RefTreeBatch.java

  1. /*
  2.  * Copyright (C) 2016, Google Inc.
  3.  * and other copyright owners as documented in the project's IP log.
  4.  *
  5.  * This program and the accompanying materials are made available
  6.  * under the terms of the Eclipse Distribution License v1.0 which
  7.  * accompanies this distribution, is reproduced below, and is
  8.  * available at http://www.eclipse.org/org/documents/edl-v10.php
  9.  *
  10.  * All rights reserved.
  11.  *
  12.  * Redistribution and use in source and binary forms, with or
  13.  * without modification, are permitted provided that the following
  14.  * conditions are met:
  15.  *
  16.  * - Redistributions of source code must retain the above copyright
  17.  *   notice, this list of conditions and the following disclaimer.
  18.  *
  19.  * - Redistributions in binary form must reproduce the above
  20.  *   copyright notice, this list of conditions and the following
  21.  *   disclaimer in the documentation and/or other materials provided
  22.  *   with the distribution.
  23.  *
  24.  * - Neither the name of the Eclipse Foundation, Inc. nor the
  25.  *   names of its contributors may be used to endorse or promote
  26.  *   products derived from this software without specific prior
  27.  *   written permission.
  28.  *
  29.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30.  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38.  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41.  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42.  */

  43. package org.eclipse.jgit.internal.storage.reftree;

  44. import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
  45. import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
  46. import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
  47. import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_NONFASTFORWARD;
  48. import static org.eclipse.jgit.transport.ReceiveCommand.Result.REJECTED_OTHER_REASON;
  49. import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE;
  50. import static org.eclipse.jgit.transport.ReceiveCommand.Type.UPDATE_NONFASTFORWARD;

  51. import java.io.IOException;
  52. import java.text.MessageFormat;
  53. import java.util.ArrayList;
  54. import java.util.List;

  55. import org.eclipse.jgit.annotations.Nullable;
  56. import org.eclipse.jgit.internal.JGitText;
  57. import org.eclipse.jgit.lib.BatchRefUpdate;
  58. import org.eclipse.jgit.lib.CommitBuilder;
  59. import org.eclipse.jgit.lib.NullProgressMonitor;
  60. import org.eclipse.jgit.lib.ObjectId;
  61. import org.eclipse.jgit.lib.ObjectInserter;
  62. import org.eclipse.jgit.lib.ObjectReader;
  63. import org.eclipse.jgit.lib.PersonIdent;
  64. import org.eclipse.jgit.lib.ProgressMonitor;
  65. import org.eclipse.jgit.lib.Ref;
  66. import org.eclipse.jgit.lib.Repository;
  67. import org.eclipse.jgit.revwalk.RevCommit;
  68. import org.eclipse.jgit.revwalk.RevWalk;
  69. import org.eclipse.jgit.transport.ReceiveCommand;

  70. /** Batch update a {@link RefTreeDatabase}. */
  71. class RefTreeBatch extends BatchRefUpdate {
  72.     private final RefTreeDatabase refdb;
  73.     private Ref src;
  74.     private ObjectId parentCommitId;
  75.     private ObjectId parentTreeId;
  76.     private RefTree tree;
  77.     private PersonIdent author;
  78.     private ObjectId newCommitId;

  79.     RefTreeBatch(RefTreeDatabase refdb) {
  80.         super(refdb);
  81.         this.refdb = refdb;
  82.     }

  83.     /** {@inheritDoc} */
  84.     @Override
  85.     public void execute(RevWalk rw, ProgressMonitor monitor)
  86.             throws IOException {
  87.         List<Command> todo = new ArrayList<>(getCommands().size());
  88.         for (ReceiveCommand c : getCommands()) {
  89.             if (!isAllowNonFastForwards()) {
  90.                 if (c.getType() == UPDATE) {
  91.                     c.updateType(rw);
  92.                 }
  93.                 if (c.getType() == UPDATE_NONFASTFORWARD) {
  94.                     c.setResult(REJECTED_NONFASTFORWARD);
  95.                     if (isAtomic()) {
  96.                         ReceiveCommand.abort(getCommands());
  97.                         return;
  98.                     } else {
  99.                         continue;
  100.                     }
  101.                 }
  102.             }
  103.             todo.add(new Command(rw, c));
  104.         }
  105.         init(rw);
  106.         execute(rw, todo);
  107.     }

  108.     void init(RevWalk rw) throws IOException {
  109.         src = refdb.getBootstrap().exactRef(refdb.getTxnCommitted());
  110.         if (src != null && src.getObjectId() != null) {
  111.             RevCommit c = rw.parseCommit(src.getObjectId());
  112.             parentCommitId = c;
  113.             parentTreeId = c.getTree();
  114.             tree = RefTree.read(rw.getObjectReader(), c.getTree());
  115.         } else {
  116.             parentCommitId = ObjectId.zeroId();
  117.             parentTreeId = new ObjectInserter.Formatter()
  118.                     .idFor(OBJ_TREE, new byte[] {});
  119.             tree = RefTree.newEmptyTree();
  120.         }
  121.     }

  122.     @Nullable
  123.     Ref exactRef(ObjectReader reader, String name) throws IOException {
  124.         return tree.exactRef(reader, name);
  125.     }

  126.     /**
  127.      * Execute an update from {@link RefTreeUpdate} or {@link RefTreeRename}.
  128.      *
  129.      * @param rw
  130.      *            current RevWalk handling the update or rename.
  131.      * @param todo
  132.      *            commands to execute. Must never be a bootstrap reference name.
  133.      * @throws IOException
  134.      *             the storage system is unable to read or write data.
  135.      */
  136.     void execute(RevWalk rw, List<Command> todo) throws IOException {
  137.         for (Command c : todo) {
  138.             if (c.getResult() != NOT_ATTEMPTED) {
  139.                 Command.abort(todo, null);
  140.                 return;
  141.             }
  142.             if (refdb.conflictsWithBootstrap(c.getRefName())) {
  143.                 c.setResult(REJECTED_OTHER_REASON, MessageFormat
  144.                         .format(JGitText.get().invalidRefName, c.getRefName()));
  145.                 Command.abort(todo, null);
  146.                 return;
  147.             }
  148.         }

  149.         if (apply(todo) && newCommitId != null) {
  150.             commit(rw, todo);
  151.         }
  152.     }

  153.     private boolean apply(List<Command> todo) throws IOException {
  154.         if (!tree.apply(todo)) {
  155.             // apply set rejection information on commands.
  156.             return false;
  157.         }

  158.         Repository repo = refdb.getRepository();
  159.         try (ObjectInserter ins = repo.newObjectInserter()) {
  160.             CommitBuilder b = new CommitBuilder();
  161.             b.setTreeId(tree.writeTree(ins));
  162.             if (parentTreeId.equals(b.getTreeId())) {
  163.                 for (Command c : todo) {
  164.                     c.setResult(OK);
  165.                 }
  166.                 return true;
  167.             }
  168.             if (!parentCommitId.equals(ObjectId.zeroId())) {
  169.                 b.setParentId(parentCommitId);
  170.             }

  171.             author = getRefLogIdent();
  172.             if (author == null) {
  173.                 author = new PersonIdent(repo);
  174.             }
  175.             b.setAuthor(author);
  176.             b.setCommitter(author);
  177.             b.setMessage(getRefLogMessage());
  178.             newCommitId = ins.insert(b);
  179.             ins.flush();
  180.         }
  181.         return true;
  182.     }

  183.     private void commit(RevWalk rw, List<Command> todo) throws IOException {
  184.         ReceiveCommand commit = new ReceiveCommand(
  185.                 parentCommitId, newCommitId,
  186.                 refdb.getTxnCommitted());
  187.         updateBootstrap(rw, commit);

  188.         if (commit.getResult() == OK) {
  189.             for (Command c : todo) {
  190.                 c.setResult(OK);
  191.             }
  192.         } else {
  193.             Command.abort(todo, commit.getResult().name());
  194.         }
  195.     }

  196.     private void updateBootstrap(RevWalk rw, ReceiveCommand commit)
  197.             throws IOException {
  198.         BatchRefUpdate u = refdb.getBootstrap().newBatchUpdate();
  199.         u.setAllowNonFastForwards(true);
  200.         u.setPushCertificate(getPushCertificate());
  201.         if (isRefLogDisabled()) {
  202.             u.disableRefLog();
  203.         } else {
  204.             u.setRefLogIdent(author);
  205.             u.setRefLogMessage(getRefLogMessage(), false);
  206.         }
  207.         u.addCommand(commit);
  208.         u.execute(rw, NullProgressMonitor.INSTANCE);
  209.     }
  210. }