CommitBuilder.java

  1. /*
  2.  * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
  3.  * Copyright (C) 2006, 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
  4.  * Copyright (C) 2006, 2020, Shawn O. Pearce <spearce@spearce.org> and others
  5.  *
  6.  * This program and the accompanying materials are made available under the
  7.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  8.  * https://www.eclipse.org/org/documents/edl-v10.php.
  9.  *
  10.  * SPDX-License-Identifier: BSD-3-Clause
  11.  */

  12. package org.eclipse.jgit.lib;

  13. import static java.nio.charset.StandardCharsets.UTF_8;

  14. import java.io.ByteArrayOutputStream;
  15. import java.io.IOException;
  16. import java.io.OutputStreamWriter;
  17. import java.io.UnsupportedEncodingException;
  18. import java.nio.charset.Charset;
  19. import java.util.List;

  20. import org.eclipse.jgit.util.References;

  21. /**
  22.  * Mutable builder to construct a commit recording the state of a project.
  23.  *
  24.  * Applications should use this object when they need to manually construct a
  25.  * commit and want precise control over its fields. For a higher level interface
  26.  * see {@link org.eclipse.jgit.api.CommitCommand}.
  27.  *
  28.  * To read a commit object, construct a {@link org.eclipse.jgit.revwalk.RevWalk}
  29.  * and obtain a {@link org.eclipse.jgit.revwalk.RevCommit} instance by calling
  30.  * {@link org.eclipse.jgit.revwalk.RevWalk#parseCommit(AnyObjectId)}.
  31.  */
  32. public class CommitBuilder extends ObjectBuilder {
  33.     private static final ObjectId[] EMPTY_OBJECTID_LIST = new ObjectId[0];

  34.     private static final byte[] htree = Constants.encodeASCII("tree"); //$NON-NLS-1$

  35.     private static final byte[] hparent = Constants.encodeASCII("parent"); //$NON-NLS-1$

  36.     private static final byte[] hauthor = Constants.encodeASCII("author"); //$NON-NLS-1$

  37.     private static final byte[] hcommitter = Constants.encodeASCII("committer"); //$NON-NLS-1$

  38.     private static final byte[] hgpgsig = Constants.encodeASCII("gpgsig"); //$NON-NLS-1$

  39.     private ObjectId treeId;

  40.     private ObjectId[] parentIds;

  41.     private PersonIdent committer;

  42.     /**
  43.      * Initialize an empty commit.
  44.      */
  45.     public CommitBuilder() {
  46.         parentIds = EMPTY_OBJECTID_LIST;
  47.     }

  48.     /**
  49.      * Get id of the root tree listing this commit's snapshot.
  50.      *
  51.      * @return id of the root tree listing this commit's snapshot.
  52.      */
  53.     public ObjectId getTreeId() {
  54.         return treeId;
  55.     }

  56.     /**
  57.      * Set the tree id for this commit object.
  58.      *
  59.      * @param id
  60.      *            the tree identity.
  61.      */
  62.     public void setTreeId(AnyObjectId id) {
  63.         treeId = id.copy();
  64.     }

  65.     /**
  66.      * Get the author of this commit (who wrote it).
  67.      *
  68.      * @return the author of this commit (who wrote it).
  69.      */
  70.     @Override
  71.     public PersonIdent getAuthor() {
  72.         return super.getAuthor();
  73.     }

  74.     /**
  75.      * Set the author (name, email address, and date) of who wrote the commit.
  76.      *
  77.      * @param newAuthor
  78.      *            the new author. Should not be null.
  79.      */
  80.     @Override
  81.     public void setAuthor(PersonIdent newAuthor) {
  82.         super.setAuthor(newAuthor);
  83.     }

  84.     /**
  85.      * Get the committer and commit time for this object.
  86.      *
  87.      * @return the committer and commit time for this object.
  88.      */
  89.     public PersonIdent getCommitter() {
  90.         return committer;
  91.     }

  92.     /**
  93.      * Set the committer and commit time for this object.
  94.      *
  95.      * @param newCommitter
  96.      *            the committer information. Should not be null.
  97.      */
  98.     public void setCommitter(PersonIdent newCommitter) {
  99.         committer = newCommitter;
  100.     }

  101.     /**
  102.      * Get the ancestors of this commit.
  103.      *
  104.      * @return the ancestors of this commit. Never null.
  105.      */
  106.     public ObjectId[] getParentIds() {
  107.         return parentIds;
  108.     }

  109.     /**
  110.      * Set the parent of this commit.
  111.      *
  112.      * @param newParent
  113.      *            the single parent for the commit.
  114.      */
  115.     public void setParentId(AnyObjectId newParent) {
  116.         parentIds = new ObjectId[] { newParent.copy() };
  117.     }

  118.     /**
  119.      * Set the parents of this commit.
  120.      *
  121.      * @param parent1
  122.      *            the first parent of this commit. Typically this is the current
  123.      *            value of the {@code HEAD} reference and is thus the current
  124.      *            branch's position in history.
  125.      * @param parent2
  126.      *            the second parent of this merge commit. Usually this is the
  127.      *            branch being merged into the current branch.
  128.      */
  129.     public void setParentIds(AnyObjectId parent1, AnyObjectId parent2) {
  130.         parentIds = new ObjectId[] { parent1.copy(), parent2.copy() };
  131.     }

  132.     /**
  133.      * Set the parents of this commit.
  134.      *
  135.      * @param newParents
  136.      *            the entire list of parents for this commit.
  137.      */
  138.     public void setParentIds(ObjectId... newParents) {
  139.         parentIds = new ObjectId[newParents.length];
  140.         for (int i = 0; i < newParents.length; i++)
  141.             parentIds[i] = newParents[i].copy();
  142.     }

  143.     /**
  144.      * Set the parents of this commit.
  145.      *
  146.      * @param newParents
  147.      *            the entire list of parents for this commit.
  148.      */
  149.     public void setParentIds(List<? extends AnyObjectId> newParents) {
  150.         parentIds = new ObjectId[newParents.size()];
  151.         for (int i = 0; i < newParents.size(); i++)
  152.             parentIds[i] = newParents.get(i).copy();
  153.     }

  154.     /**
  155.      * Add a parent onto the end of the parent list.
  156.      *
  157.      * @param additionalParent
  158.      *            new parent to add onto the end of the current parent list.
  159.      */
  160.     public void addParentId(AnyObjectId additionalParent) {
  161.         if (parentIds.length == 0) {
  162.             setParentId(additionalParent);
  163.         } else {
  164.             ObjectId[] newParents = new ObjectId[parentIds.length + 1];
  165.             System.arraycopy(parentIds, 0, newParents, 0, parentIds.length);
  166.             newParents[parentIds.length] = additionalParent.copy();
  167.             parentIds = newParents;
  168.         }
  169.     }

  170.     /**
  171.      * Set the encoding for the commit information.
  172.      *
  173.      * @param encodingName
  174.      *            the encoding name. See
  175.      *            {@link java.nio.charset.Charset#forName(String)}.
  176.      * @deprecated use {@link #setEncoding(Charset)} instead.
  177.      */
  178.     @Deprecated
  179.     public void setEncoding(String encodingName) {
  180.         setEncoding(Charset.forName(encodingName));
  181.     }

  182.     @Override
  183.     public byte[] build() throws UnsupportedEncodingException {
  184.         ByteArrayOutputStream os = new ByteArrayOutputStream();
  185.         OutputStreamWriter w = new OutputStreamWriter(os, getEncoding());
  186.         try {
  187.             os.write(htree);
  188.             os.write(' ');
  189.             getTreeId().copyTo(os);
  190.             os.write('\n');

  191.             for (ObjectId p : getParentIds()) {
  192.                 os.write(hparent);
  193.                 os.write(' ');
  194.                 p.copyTo(os);
  195.                 os.write('\n');
  196.             }

  197.             os.write(hauthor);
  198.             os.write(' ');
  199.             w.write(getAuthor().toExternalString());
  200.             w.flush();
  201.             os.write('\n');

  202.             os.write(hcommitter);
  203.             os.write(' ');
  204.             w.write(getCommitter().toExternalString());
  205.             w.flush();
  206.             os.write('\n');

  207.             GpgSignature signature = getGpgSignature();
  208.             if (signature != null) {
  209.                 os.write(hgpgsig);
  210.                 os.write(' ');
  211.                 writeMultiLineHeader(signature.toExternalString(), os,
  212.                         true);
  213.                 os.write('\n');
  214.             }

  215.             writeEncoding(getEncoding(), os);

  216.             os.write('\n');

  217.             if (getMessage() != null) {
  218.                 w.write(getMessage());
  219.                 w.flush();
  220.             }
  221.         } catch (IOException err) {
  222.             // This should never occur, the only way to get it above is
  223.             // for the ByteArrayOutputStream to throw, but it doesn't.
  224.             //
  225.             throw new RuntimeException(err);
  226.         }
  227.         return os.toByteArray();
  228.     }

  229.     /**
  230.      * Format this builder's state as a commit object.
  231.      *
  232.      * @return this object in the canonical commit format, suitable for storage
  233.      *         in a repository.
  234.      * @throws java.io.UnsupportedEncodingException
  235.      *             the encoding specified by {@link #getEncoding()} is not
  236.      *             supported by this Java runtime.
  237.      */
  238.     public byte[] toByteArray() throws UnsupportedEncodingException {
  239.         return build();
  240.     }

  241.     /** {@inheritDoc} */
  242.     @SuppressWarnings("nls")
  243.     @Override
  244.     public String toString() {
  245.         StringBuilder r = new StringBuilder();
  246.         r.append("Commit");
  247.         r.append("={\n");

  248.         r.append("tree ");
  249.         r.append(treeId != null ? treeId.name() : "NOT_SET");
  250.         r.append("\n");

  251.         for (ObjectId p : parentIds) {
  252.             r.append("parent ");
  253.             r.append(p.name());
  254.             r.append("\n");
  255.         }

  256.         r.append("author ");
  257.         r.append(getAuthor() != null ? getAuthor().toString() : "NOT_SET");
  258.         r.append("\n");

  259.         r.append("committer ");
  260.         r.append(committer != null ? committer.toString() : "NOT_SET");
  261.         r.append("\n");

  262.         r.append("gpgSignature ");
  263.         GpgSignature signature = getGpgSignature();
  264.         r.append(signature != null ? signature.toString()
  265.                 : "NOT_SET");
  266.         r.append("\n");

  267.         Charset encoding = getEncoding();
  268.         if (!References.isSameObject(encoding, UTF_8)) {
  269.             r.append("encoding ");
  270.             r.append(encoding.name());
  271.             r.append("\n");
  272.         }

  273.         r.append("\n");
  274.         r.append(getMessage() != null ? getMessage() : "");
  275.         r.append("}");
  276.         return r.toString();
  277.     }
  278. }