Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit/api')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java109
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java46
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java20
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java6
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java162
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java285
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java15
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java13
9 files changed, 551 insertions, 118 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
index 7922f9e729..ceba89d166 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java
@@ -9,6 +9,8 @@
*/
package org.eclipse.jgit.api;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH;
+
import java.io.IOException;
import java.text.MessageFormat;
import java.util.LinkedList;
@@ -28,6 +30,7 @@ import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.CommitConfig;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
@@ -124,7 +127,7 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> {
final RevCommit srcParent = getParentCommit(srcCommit, revWalk);
String ourName = calculateOurName(headRef);
- String cherryPickName = srcCommit.getId().abbreviate(7).name()
+ String cherryPickName = srcCommit.getId().abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH).name()
+ " " + srcCommit.getShortMessage(); //$NON-NLS-1$
Merger merger = strategy.newMerger(repo);
@@ -181,9 +184,13 @@ public class CherryPickCommand extends GitCommand<CherryPickResult> {
String message;
if (unmergedPaths != null) {
+ CommitConfig cfg = repo.getConfig()
+ .get(CommitConfig.KEY);
+ message = srcCommit.getFullMessage();
+ char commentChar = cfg.getCommentChar(message);
message = new MergeMessageFormatter()
- .formatWithConflicts(srcCommit.getFullMessage(),
- unmergedPaths);
+ .formatWithConflicts(message, unmergedPaths,
+ commentChar);
} else {
message = srcCommit.getFullMessage();
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
index 37f1d482aa..3b3baf5a12 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CommitCommand.java
@@ -19,6 +19,7 @@ import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
+import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.api.errors.AbortedByHookException;
import org.eclipse.jgit.api.errors.CanceledException;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
@@ -46,6 +47,8 @@ import org.eclipse.jgit.hooks.PostCommitHook;
import org.eclipse.jgit.hooks.PreCommitHook;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.CommitConfig;
+import org.eclipse.jgit.lib.CommitConfig.CleanupMode;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.GpgConfig;
@@ -133,6 +136,12 @@ public class CommitCommand extends GitCommand<RevCommit> {
private CredentialsProvider credentialsProvider;
+ private @NonNull CleanupMode cleanupMode = CleanupMode.VERBATIM;
+
+ private boolean cleanDefaultIsStrip = true;
+
+ private Character commentChar;
+
/**
* Constructor for CommitCommand
*
@@ -200,7 +209,7 @@ public class CommitCommand extends GitCommand<RevCommit> {
throw new WrongRepositoryStateException(
JGitText.get().commitAmendOnInitialNotPossible);
- if (headId != null)
+ if (headId != null) {
if (amend) {
RevCommit previousCommit = rw.parseCommit(headId);
for (RevCommit p : previousCommit.getParents())
@@ -210,7 +219,7 @@ public class CommitCommand extends GitCommand<RevCommit> {
} else {
parents.add(0, headId);
}
-
+ }
if (!noVerify) {
message = Hooks
.commitMsg(repo,
@@ -219,6 +228,33 @@ public class CommitCommand extends GitCommand<RevCommit> {
.setCommitMessage(message).call();
}
+ CommitConfig config = null;
+ if (CleanupMode.DEFAULT.equals(cleanupMode)) {
+ config = repo.getConfig().get(CommitConfig.KEY);
+ cleanupMode = config.resolve(cleanupMode, cleanDefaultIsStrip);
+ }
+ char comments = (char) 0;
+ if (CleanupMode.STRIP.equals(cleanupMode)
+ || CleanupMode.SCISSORS.equals(cleanupMode)) {
+ if (commentChar == null) {
+ if (config == null) {
+ config = repo.getConfig().get(CommitConfig.KEY);
+ }
+ if (config.isAutoCommentChar()) {
+ // We're supposed to pick a character that isn't used,
+ // but then cleaning up won't remove any lines. So don't
+ // bother.
+ comments = (char) 0;
+ cleanupMode = CleanupMode.WHITESPACE;
+ } else {
+ comments = config.getCommentChar();
+ }
+ } else {
+ comments = commentChar.charValue();
+ }
+ }
+ message = CommitConfig.cleanText(message, cleanupMode, comments);
+
RevCommit revCommit;
DirCache index = repo.lockDirCache();
try (ObjectInserter odi = repo.newObjectInserter()) {
@@ -287,8 +323,14 @@ public class CommitCommand extends GitCommand<RevCommit> {
private void sign(CommitBuilder commit) throws ServiceUnavailableException,
CanceledException, UnsupportedSigningFormatException {
if (gpgSigner == null) {
- throw new ServiceUnavailableException(
- JGitText.get().signingServiceUnavailable);
+ gpgSigner = GpgSigner.getDefault();
+ if (gpgSigner == null) {
+ throw new ServiceUnavailableException(
+ JGitText.get().signingServiceUnavailable);
+ }
+ }
+ if (signingKey == null) {
+ signingKey = gpgConfig.getSigningKey();
}
if (gpgSigner instanceof GpgObjectSigner) {
((GpgObjectSigner) gpgSigner).signObject(commit,
@@ -623,12 +665,6 @@ public class CommitCommand extends GitCommand<RevCommit> {
signCommit = gpgConfig.isSignCommits() ? Boolean.TRUE
: Boolean.FALSE;
}
- if (signingKey == null) {
- signingKey = gpgConfig.getSigningKey();
- }
- if (gpgSigner == null) {
- gpgSigner = GpgSigner.getDefault();
- }
}
private boolean isMergeDuringRebase(RepositoryState state) {
@@ -658,6 +694,57 @@ public class CommitCommand extends GitCommand<RevCommit> {
}
/**
+ * Sets the {@link CleanupMode} to apply to the commit message. If not
+ * called, {@link CommitCommand} applies {@link CleanupMode#VERBATIM}.
+ *
+ * @param mode
+ * {@link CleanupMode} to set
+ * @return {@code this}
+ * @since 6.1
+ */
+ public CommitCommand setCleanupMode(@NonNull CleanupMode mode) {
+ checkCallable();
+ this.cleanupMode = mode;
+ return this;
+ }
+
+ /**
+ * Sets the default clean mode if {@link #setCleanupMode(CleanupMode)
+ * setCleanupMode(CleanupMode.DEFAULT)} is set and git config
+ * {@code commit.cleanup = default} or is not set.
+ *
+ * @param strip
+ * if {@code true}, default to {@link CleanupMode#STRIP};
+ * otherwise default to {@link CleanupMode#WHITESPACE}
+ * @return {@code this}
+ * @since 6.1
+ */
+ public CommitCommand setDefaultClean(boolean strip) {
+ checkCallable();
+ this.cleanDefaultIsStrip = strip;
+ return this;
+ }
+
+ /**
+ * Sets the comment character to apply when cleaning a commit message. If
+ * {@code null} (the default) and the {@link #setCleanupMode(CleanupMode)
+ * clean-up mode} is {@link CleanupMode#STRIP} or
+ * {@link CleanupMode#SCISSORS}, the value of git config
+ * {@code core.commentChar} will be used.
+ *
+ * @param commentChar
+ * the comment character, or {@code null} to use the value from
+ * the git config
+ * @return {@code this}
+ * @since 6.1
+ */
+ public CommitCommand setCommentCharacter(Character commentChar) {
+ checkCallable();
+ this.commentChar = commentChar;
+ return this;
+ }
+
+ /**
* Set whether to allow to create an empty commit
*
* @param allowEmpty
@@ -806,7 +893,7 @@ public class CommitCommand extends GitCommand<RevCommit> {
* command line.
*
* @param amend
- * whether to ammend the tip of the current branch
+ * whether to amend the tip of the current branch
* @return {@code this}
*/
public CommitCommand setAmend(boolean amend) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
index 1e524fadab..805a886392 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/DescribeCommand.java
@@ -11,6 +11,7 @@ package org.eclipse.jgit.api;
import static org.eclipse.jgit.lib.Constants.R_REFS;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
+import static org.eclipse.jgit.lib.TypedConfigGetter.UNSET_INT;
import java.io.IOException;
import java.text.MessageFormat;
@@ -33,6 +34,7 @@ import org.eclipse.jgit.errors.InvalidPatternException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.fnmatch.FileNameMatcher;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.AbbrevConfig;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
@@ -89,6 +91,11 @@ public class DescribeCommand extends GitCommand<String> {
private boolean always;
/**
+ * The prefix length to use when abbreviating a commit hash.
+ */
+ private int abbrev = UNSET_INT;
+
+ /**
* Constructor for DescribeCommand.
*
* @param repo
@@ -205,12 +212,33 @@ public class DescribeCommand extends GitCommand<String> {
return this;
}
+ /**
+ * Sets the prefix length to use when abbreviating an object SHA-1.
+ *
+ * @param abbrev
+ * minimum length of the abbreviated string. Must be in the range
+ * [{@value AbbrevConfig#MIN_ABBREV},
+ * {@value Constants#OBJECT_ID_STRING_LENGTH}].
+ * @return {@code this}
+ * @since 6.1
+ */
+ public DescribeCommand setAbbrev(int abbrev) {
+ if (abbrev == 0) {
+ this.abbrev = 0;
+ } else {
+ this.abbrev = AbbrevConfig.capAbbrev(abbrev);
+ }
+ return this;
+ }
+
private String longDescription(Ref tag, int depth, ObjectId tip)
throws IOException {
- return String.format(
- "%s-%d-g%s", formatRefName(tag.getName()), //$NON-NLS-1$
- Integer.valueOf(depth), w.getObjectReader().abbreviate(tip)
- .name());
+ if (abbrev == 0) {
+ return formatRefName(tag.getName());
+ }
+ return String.format("%s-%d-g%s", formatRefName(tag.getName()), //$NON-NLS-1$
+ Integer.valueOf(depth),
+ w.getObjectReader().abbreviate(tip, abbrev).name());
}
/**
@@ -302,6 +330,9 @@ public class DescribeCommand extends GitCommand<String> {
if (target == null) {
setTarget(Constants.HEAD);
}
+ if (abbrev == UNSET_INT) {
+ abbrev = AbbrevConfig.parseFromConfig(repo).get();
+ }
Collection<Ref> tagList = repo.getRefDatabase()
.getRefsByPrefix(useAll ? R_REFS : R_TAGS);
@@ -413,7 +444,12 @@ public class DescribeCommand extends GitCommand<String> {
// if all the nodes are dominated by all the tags, the walk stops
if (candidates.isEmpty()) {
- return always ? w.getObjectReader().abbreviate(target).name() : null;
+ return always
+ ? w.getObjectReader()
+ .abbreviate(target,
+ AbbrevConfig.capAbbrev(abbrev))
+ .name()
+ : null;
}
Candidate best = Collections.min(candidates,
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java
index 0c691062f9..c3415581ef 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/LsRemoteCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011, 2020 Christoph Brill <egore911@egore911.de> and others
+ * Copyright (C) 2011, 2022 Christoph Brill <egore911@egore911.de> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -9,6 +9,7 @@
*/
package org.eclipse.jgit.api;
+import java.io.IOException;
import java.net.URISyntaxException;
import java.text.MessageFormat;
import java.util.ArrayList;
@@ -20,8 +21,8 @@ import java.util.Map;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.NotSupportedException;
-import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
@@ -30,6 +31,8 @@ import org.eclipse.jgit.transport.FetchConnection;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.Transport;
import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.transport.UrlConfig;
+import org.eclipse.jgit.util.SystemReader;
/**
* The ls-remote command
@@ -153,7 +156,7 @@ public class LsRemoteCommand extends
try (Transport transport = repo != null
? Transport.open(repo, remote)
- : Transport.open(new URIish(remote))) {
+ : Transport.open(new URIish(translate(remote)))) {
transport.setOptionUploadPack(uploadPack);
configure(transport);
Collection<RefSpec> refSpecs = new ArrayList<>(1);
@@ -185,11 +188,16 @@ public class LsRemoteCommand extends
throw new JGitInternalException(
JGitText.get().exceptionCaughtDuringExecutionOfLsRemoteCommand,
e);
- } catch (TransportException e) {
+ } catch (IOException | ConfigInvalidException e) {
throw new org.eclipse.jgit.api.errors.TransportException(
- e.getMessage(),
- e);
+ e.getMessage(), e);
}
}
+ private String translate(String uri)
+ throws IOException, ConfigInvalidException {
+ UrlConfig urls = new UrlConfig(
+ SystemReader.getInstance().getUserConfig());
+ return urls.replace(uri);
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
index ef56d802c8..ed4a5342b3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/MergeCommand.java
@@ -34,6 +34,7 @@ import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.CommitConfig;
import org.eclipse.jgit.lib.Config.ConfigEnum;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
@@ -404,8 +405,11 @@ public class MergeCommand extends GitCommand<MergeResult> {
MergeStatus.FAILED, mergeStrategy, lowLevelResults,
failingPaths, null);
}
+ CommitConfig cfg = repo.getConfig().get(CommitConfig.KEY);
+ char commentChar = cfg.getCommentChar(message);
String mergeMessageWithConflicts = new MergeMessageFormatter()
- .formatWithConflicts(mergeMessage, unmergedPaths);
+ .formatWithConflicts(mergeMessage, unmergedPaths,
+ commentChar);
repo.writeMergeCommitMsg(mergeMessageWithConflicts);
return new MergeResult(null, merger.getBaseCommitId(),
new ObjectId[] { headCommit.getId(),
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java
index aa5a63499c..08353dfdfa 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> and others
+ * Copyright (C) 2010, 2022 Chris Aniszczyk <caniszczyk@gmail.com> and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -21,7 +21,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.eclipse.jgit.api.errors.DetachedHeadException;
import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.InvalidRefNameException;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.errors.NotSupportedException;
@@ -29,11 +31,16 @@ import org.eclipse.jgit.errors.TooLargeObjectInPackException;
import org.eclipse.jgit.errors.TooLargePackException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.BranchConfig;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.PushConfig;
+import org.eclipse.jgit.transport.PushConfig.PushDefault;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RefLeaseSpec;
import org.eclipse.jgit.transport.RefSpec;
@@ -52,7 +59,7 @@ import org.eclipse.jgit.transport.Transport;
public class PushCommand extends
TransportCommand<PushCommand, Iterable<PushResult>> {
- private String remote = Constants.DEFAULT_REMOTE_NAME;
+ private String remote;
private final List<RefSpec> refSpecs;
@@ -71,6 +78,10 @@ public class PushCommand extends
private List<String> pushOptions;
+ // Legacy behavior as default. Use setPushDefault(null) to determine the
+ // value from the git config.
+ private PushDefault pushDefault = PushDefault.CURRENT;
+
/**
* <p>
* Constructor for PushCommand.
@@ -98,19 +109,20 @@ public class PushCommand extends
InvalidRemoteException,
org.eclipse.jgit.api.errors.TransportException {
checkCallable();
+ setCallable(false);
ArrayList<PushResult> pushResults = new ArrayList<>(3);
try {
+ Config config = repo.getConfig();
+ remote = determineRemote(config, remote);
if (refSpecs.isEmpty()) {
- RemoteConfig config = new RemoteConfig(repo.getConfig(),
+ RemoteConfig rc = new RemoteConfig(config,
getRemote());
- refSpecs.addAll(config.getPushRefSpecs());
- }
- if (refSpecs.isEmpty()) {
- Ref head = repo.exactRef(Constants.HEAD);
- if (head != null && head.isSymbolic())
- refSpecs.add(new RefSpec(head.getLeaf().getName()));
+ refSpecs.addAll(rc.getPushRefSpecs());
+ if (refSpecs.isEmpty()) {
+ determineDefaultRefSpecs(config);
+ }
}
if (force) {
@@ -118,8 +130,8 @@ public class PushCommand extends
refSpecs.set(i, refSpecs.get(i).setForceUpdate(true));
}
- final List<Transport> transports;
- transports = Transport.openAll(repo, remote, Transport.Operation.PUSH);
+ List<Transport> transports = Transport.openAll(repo, remote,
+ Transport.Operation.PUSH);
for (@SuppressWarnings("resource") // Explicitly closed in finally
final Transport transport : transports) {
transport.setPushThin(thin);
@@ -171,6 +183,102 @@ public class PushCommand extends
return pushResults;
}
+ private String determineRemote(Config config, String remoteName)
+ throws IOException {
+ if (remoteName != null) {
+ return remoteName;
+ }
+ Ref head = repo.exactRef(Constants.HEAD);
+ String effectiveRemote = null;
+ BranchConfig branchCfg = null;
+ if (head != null && head.isSymbolic()) {
+ String currentBranch = head.getLeaf().getName();
+ branchCfg = new BranchConfig(config,
+ Repository.shortenRefName(currentBranch));
+ effectiveRemote = branchCfg.getPushRemote();
+ }
+ if (effectiveRemote == null) {
+ effectiveRemote = config.getString(
+ ConfigConstants.CONFIG_REMOTE_SECTION, null,
+ ConfigConstants.CONFIG_KEY_PUSH_DEFAULT);
+ if (effectiveRemote == null && branchCfg != null) {
+ effectiveRemote = branchCfg.getRemote();
+ }
+ }
+ if (effectiveRemote == null) {
+ effectiveRemote = Constants.DEFAULT_REMOTE_NAME;
+ }
+ return effectiveRemote;
+ }
+
+ private String getCurrentBranch()
+ throws IOException, DetachedHeadException {
+ Ref head = repo.exactRef(Constants.HEAD);
+ if (head != null && head.isSymbolic()) {
+ return head.getLeaf().getName();
+ }
+ throw new DetachedHeadException();
+ }
+
+ private void determineDefaultRefSpecs(Config config)
+ throws IOException, GitAPIException {
+ if (pushDefault == null) {
+ pushDefault = config.get(PushConfig::new).getPushDefault();
+ }
+ switch (pushDefault) {
+ case CURRENT:
+ refSpecs.add(new RefSpec(getCurrentBranch()));
+ break;
+ case MATCHING:
+ refSpecs.add(new RefSpec(":")); //$NON-NLS-1$
+ break;
+ case NOTHING:
+ throw new InvalidRefNameException(
+ JGitText.get().pushDefaultNothing);
+ case SIMPLE:
+ case UPSTREAM:
+ String currentBranch = getCurrentBranch();
+ BranchConfig branchCfg = new BranchConfig(config,
+ Repository.shortenRefName(currentBranch));
+ String fetchRemote = branchCfg.getRemote();
+ if (fetchRemote == null) {
+ fetchRemote = Constants.DEFAULT_REMOTE_NAME;
+ }
+ boolean isTriangular = !fetchRemote.equals(remote);
+ if (isTriangular) {
+ if (PushDefault.UPSTREAM.equals(pushDefault)) {
+ throw new InvalidRefNameException(MessageFormat.format(
+ JGitText.get().pushDefaultTriangularUpstream,
+ remote, fetchRemote));
+ }
+ // Strange, but consistent with C git: "simple" doesn't even
+ // check whether there is a configured upstream, and if so, that
+ // it is equal to the local branch name. It just becomes
+ // "current".
+ refSpecs.add(new RefSpec(currentBranch));
+ } else {
+ String trackedBranch = branchCfg.getMerge();
+ if (branchCfg.isRemoteLocal() || trackedBranch == null
+ || !trackedBranch.startsWith(Constants.R_HEADS)) {
+ throw new InvalidRefNameException(MessageFormat.format(
+ JGitText.get().pushDefaultNoUpstream,
+ currentBranch));
+ }
+ if (PushDefault.SIMPLE.equals(pushDefault)
+ && !trackedBranch.equals(currentBranch)) {
+ throw new InvalidRefNameException(MessageFormat.format(
+ JGitText.get().pushDefaultSimple, currentBranch,
+ trackedBranch));
+ }
+ refSpecs.add(new RefSpec(currentBranch + ':' + trackedBranch));
+ }
+ break;
+ default:
+ throw new InvalidRefNameException(MessageFormat
+ .format(JGitText.get().pushDefaultUnknown, pushDefault));
+ }
+ }
+
/**
* The remote (uri or name) used for the push operation. If no remote is
* set, the default value of <code>Constants.DEFAULT_REMOTE_NAME</code> will
@@ -336,9 +444,37 @@ public class PushCommand extends
}
/**
+ * Retrieves the {@link PushDefault} currently set.
+ *
+ * @return the {@link PushDefault}, or {@code null} if not set
+ * @since 6.1
+ */
+ public PushDefault getPushDefault() {
+ return pushDefault;
+ }
+
+ /**
+ * Sets an explicit {@link PushDefault}. The default used if this is not
+ * called is {@link PushDefault#CURRENT} for compatibility reasons with
+ * earlier JGit versions.
+ *
+ * @param pushDefault
+ * {@link PushDefault} to set; if {@code null} the value defined
+ * in the git config will be used.
+ *
+ * @return {@code this}
+ * @since 6.1
+ */
+ public PushCommand setPushDefault(PushDefault pushDefault) {
+ checkCallable();
+ this.pushDefault = pushDefault;
+ return this;
+ }
+
+ /**
* Push all branches under refs/heads/*.
*
- * @return {code this}
+ * @return {@code this}
*/
public PushCommand setPushAll() {
refSpecs.add(Transport.REFSPEC_PUSH_ALL);
@@ -348,7 +484,7 @@ public class PushCommand extends
/**
* Push all tags under refs/tags/*.
*
- * @return {code this}
+ * @return {@code this}
*/
public PushCommand setPushTags() {
refSpecs.add(Transport.REFSPEC_TAGS);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
index a26ffc2e66..4e0d9d78c3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java
@@ -29,6 +29,7 @@ import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.api.RebaseResult.Status;
import org.eclipse.jgit.api.ResetCommand.ResetType;
import org.eclipse.jgit.api.errors.CheckoutConflictException;
@@ -52,6 +53,8 @@ import org.eclipse.jgit.errors.RevisionSyntaxException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.CommitConfig;
+import org.eclipse.jgit.lib.CommitConfig.CleanupMode;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
@@ -205,6 +208,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
private InteractiveHandler interactiveHandler;
+ private CommitConfig commitConfig;
+
private boolean stopAfterInitialization = false;
private RevCommit newHead;
@@ -246,6 +251,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
lastStepWasForward = false;
checkCallable();
checkParameters();
+ commitConfig = repo.getConfig().get(CommitConfig.KEY);
try {
switch (operation) {
case ABORT:
@@ -441,11 +447,17 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
return null; // continue rebase process on pick command
case REWORD:
String oldMessage = commitToPick.getFullMessage();
- String newMessage = interactiveHandler
- .modifyCommitMessage(oldMessage);
+ CleanupMode mode = commitConfig.resolve(CleanupMode.DEFAULT, true);
+ boolean[] doChangeId = { false };
+ String newMessage = editCommitMessage(doChangeId, oldMessage, mode,
+ commitConfig.getCommentChar(oldMessage));
try (Git git = new Git(repo)) {
- newHead = git.commit().setMessage(newMessage).setAmend(true)
- .setNoVerify(true).call();
+ newHead = git.commit()
+ .setMessage(newMessage)
+ .setAmend(true)
+ .setNoVerify(true)
+ .setInsertChangeId(doChangeId[0])
+ .call();
}
return null;
case EDIT:
@@ -460,17 +472,49 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
resetSoftToParent();
List<RebaseTodoLine> steps = repo.readRebaseTodo(
rebaseState.getPath(GIT_REBASE_TODO), false);
- RebaseTodoLine nextStep = steps.isEmpty() ? null : steps.get(0);
+ boolean isLast = steps.isEmpty();
+ if (!isLast) {
+ switch (steps.get(0).getAction()) {
+ case FIXUP:
+ case SQUASH:
+ break;
+ default:
+ isLast = true;
+ break;
+ }
+ }
File messageFixupFile = rebaseState.getFile(MESSAGE_FIXUP);
File messageSquashFile = rebaseState.getFile(MESSAGE_SQUASH);
- if (isSquash && messageFixupFile.exists())
+ if (isSquash && messageFixupFile.exists()) {
messageFixupFile.delete();
- newHead = doSquashFixup(isSquash, commitToPick, nextStep,
+ }
+ newHead = doSquashFixup(isSquash, commitToPick, isLast,
messageFixupFile, messageSquashFile);
}
return null;
}
+ private String editCommitMessage(boolean[] doChangeId, String message,
+ @NonNull CleanupMode mode, char commentChar) {
+ String newMessage;
+ CommitConfig.CleanupMode cleanup;
+ if (interactiveHandler instanceof InteractiveHandler2) {
+ InteractiveHandler2.ModifyResult modification = ((InteractiveHandler2) interactiveHandler)
+ .editCommitMessage(message, mode, commentChar);
+ newMessage = modification.getMessage();
+ cleanup = modification.getCleanupMode();
+ if (CleanupMode.DEFAULT.equals(cleanup)) {
+ cleanup = mode;
+ }
+ doChangeId[0] = modification.shouldAddChangeId();
+ } else {
+ newMessage = interactiveHandler.modifyCommitMessage(message);
+ cleanup = CommitConfig.CleanupMode.STRIP;
+ doChangeId[0] = false;
+ }
+ return CommitConfig.cleanText(newMessage, cleanup, commentChar);
+ }
+
private RebaseResult cherryPickCommit(RevCommit commitToPick)
throws IOException, GitAPIException, NoMessageException,
UnmergedPathsException, ConcurrentRefUpdateException,
@@ -707,7 +751,7 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
}
private RevCommit doSquashFixup(boolean isSquash, RevCommit commitToPick,
- RebaseTodoLine nextStep, File messageFixup, File messageSquash)
+ boolean isLast, File messageFixup, File messageSquash)
throws IOException, GitAPIException {
if (!messageSquash.exists()) {
@@ -717,24 +761,20 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
initializeSquashFixupFile(MESSAGE_SQUASH,
previousCommit.getFullMessage());
- if (!isSquash)
- initializeSquashFixupFile(MESSAGE_FIXUP,
- previousCommit.getFullMessage());
+ if (!isSquash) {
+ rebaseState.createFile(MESSAGE_FIXUP,
+ previousCommit.getFullMessage());
+ }
}
- String currSquashMessage = rebaseState
- .readFile(MESSAGE_SQUASH);
+ String currSquashMessage = rebaseState.readFile(MESSAGE_SQUASH);
int count = parseSquashFixupSequenceCount(currSquashMessage) + 1;
String content = composeSquashMessage(isSquash,
commitToPick, currSquashMessage, count);
rebaseState.createFile(MESSAGE_SQUASH, content);
- if (messageFixup.exists())
- rebaseState.createFile(MESSAGE_FIXUP, content);
- return squashIntoPrevious(
- !messageFixup.exists(),
- nextStep);
+ return squashIntoPrevious(!messageFixup.exists(), isLast);
}
private void resetSoftToParent() throws IOException,
@@ -756,26 +796,31 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
}
private RevCommit squashIntoPrevious(boolean sequenceContainsSquash,
- RebaseTodoLine nextStep)
+ boolean isLast)
throws IOException, GitAPIException {
RevCommit retNewHead;
- String commitMessage = rebaseState
- .readFile(MESSAGE_SQUASH);
-
+ String commitMessage;
+ if (!isLast || sequenceContainsSquash) {
+ commitMessage = rebaseState.readFile(MESSAGE_SQUASH);
+ } else {
+ commitMessage = rebaseState.readFile(MESSAGE_FIXUP);
+ }
try (Git git = new Git(repo)) {
- if (nextStep == null || ((nextStep.getAction() != Action.FIXUP)
- && (nextStep.getAction() != Action.SQUASH))) {
- // this is the last step in this sequence
+ if (isLast) {
+ boolean[] doChangeId = { false };
if (sequenceContainsSquash) {
- commitMessage = interactiveHandler
- .modifyCommitMessage(commitMessage);
+ char commentChar = commitMessage.charAt(0);
+ commitMessage = editCommitMessage(doChangeId, commitMessage,
+ CleanupMode.STRIP, commentChar);
}
retNewHead = git.commit()
- .setMessage(stripCommentLines(commitMessage))
- .setAmend(true).setNoVerify(true).call();
+ .setMessage(commitMessage)
+ .setAmend(true)
+ .setNoVerify(true)
+ .setInsertChangeId(doChangeId[0])
+ .call();
rebaseState.getFile(MESSAGE_SQUASH).delete();
rebaseState.getFile(MESSAGE_FIXUP).delete();
-
} else {
// Next step is either Squash or Fixup
retNewHead = git.commit().setMessage(commitMessage)
@@ -785,46 +830,61 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
return retNewHead;
}
- private static String stripCommentLines(String commitMessage) {
- StringBuilder result = new StringBuilder();
- for (String line : commitMessage.split("\n")) { //$NON-NLS-1$
- if (!line.trim().startsWith("#")) //$NON-NLS-1$
- result.append(line).append("\n"); //$NON-NLS-1$
- }
- if (!commitMessage.endsWith("\n")) { //$NON-NLS-1$
- int bufferSize = result.length();
- if (bufferSize > 0 && result.charAt(bufferSize - 1) == '\n') {
- result.deleteCharAt(bufferSize - 1);
- }
- }
- return result.toString();
- }
-
@SuppressWarnings("nls")
- private static String composeSquashMessage(boolean isSquash,
+ private String composeSquashMessage(boolean isSquash,
RevCommit commitToPick, String currSquashMessage, int count) {
StringBuilder sb = new StringBuilder();
String ordinal = getOrdinal(count);
- sb.setLength(0);
- sb.append("# This is a combination of ").append(count)
- .append(" commits.\n");
- // Add the previous message without header (i.e first line)
- sb.append(currSquashMessage
- .substring(currSquashMessage.indexOf('\n') + 1));
- sb.append("\n");
- if (isSquash) {
- sb.append("# This is the ").append(count).append(ordinal)
- .append(" commit message:\n");
- sb.append(commitToPick.getFullMessage());
+ // currSquashMessage is always non-empty here, and the first character
+ // is the comment character used so far.
+ char commentChar = currSquashMessage.charAt(0);
+ String newMessage = commitToPick.getFullMessage();
+ if (!isSquash) {
+ sb.append(commentChar).append(" This is a combination of ")
+ .append(count).append(" commits.\n");
+ // Add the previous message without header (i.e first line)
+ sb.append(currSquashMessage
+ .substring(currSquashMessage.indexOf('\n') + 1));
+ sb.append('\n');
+ sb.append(commentChar).append(" The ").append(count).append(ordinal)
+ .append(" commit message will be skipped:\n")
+ .append(commentChar).append(' ');
+ sb.append(newMessage.replaceAll("([\n\r])",
+ "$1" + commentChar + ' '));
} else {
- sb.append("# The ").append(count).append(ordinal)
- .append(" commit message will be skipped:\n# ");
- sb.append(commitToPick.getFullMessage().replaceAll("([\n\r])",
- "$1# "));
+ String currentMessage = currSquashMessage;
+ if (commitConfig.isAutoCommentChar()) {
+ // Figure out a new comment character taking into account the
+ // new message
+ String cleaned = CommitConfig.cleanText(currentMessage,
+ CommitConfig.CleanupMode.STRIP, commentChar) + '\n'
+ + newMessage;
+ char newCommentChar = commitConfig.getCommentChar(cleaned);
+ if (newCommentChar != commentChar) {
+ currentMessage = replaceCommentChar(currentMessage,
+ commentChar, newCommentChar);
+ commentChar = newCommentChar;
+ }
+ }
+ sb.append(commentChar).append(" This is a combination of ")
+ .append(count).append(" commits.\n");
+ // Add the previous message without header (i.e first line)
+ sb.append(
+ currentMessage.substring(currentMessage.indexOf('\n') + 1));
+ sb.append('\n');
+ sb.append(commentChar).append(" This is the ").append(count)
+ .append(ordinal).append(" commit message:\n");
+ sb.append(newMessage);
}
return sb.toString();
}
+ private String replaceCommentChar(String message, char oldChar,
+ char newChar) {
+ // (?m) - Switch on multi-line matching; \h - horizontal whitespace
+ return message.replaceAll("(?m)^(\\h*)" + oldChar, "$1" + newChar); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
private static String getOrdinal(int count) {
switch (count % 10) {
case 1:
@@ -858,10 +918,11 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
private void initializeSquashFixupFile(String messageFile,
String fullMessage) throws IOException {
- rebaseState
- .createFile(
- messageFile,
- "# This is a combination of 1 commits.\n# The first commit's message is:\n" + fullMessage); //$NON-NLS-1$);
+ char commentChar = commitConfig.getCommentChar(fullMessage);
+ rebaseState.createFile(messageFile,
+ commentChar + " This is a combination of 1 commits.\n" //$NON-NLS-1$
+ + commentChar + " The first commit's message is:\n" //$NON-NLS-1$
+ + fullMessage);
}
private String getOurCommitName() {
@@ -1625,26 +1686,106 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
}
/**
- * Allows configure rebase interactive process and modify commit message
+ * Allows to configure the interactive rebase process steps and to modify
+ * commit messages.
*/
public interface InteractiveHandler {
+
/**
- * Given list of {@code steps} should be modified according to user
- * rebase configuration
+ * Callback API to modify the initial list of interactive rebase steps.
+ *
* @param steps
- * initial configuration of rebase interactive
+ * initial configuration of interactive rebase
*/
void prepareSteps(List<RebaseTodoLine> steps);
/**
- * Used for editing commit message on REWORD
+ * Used for editing commit message on REWORD or SQUASH.
*
- * @param commit
+ * @param message
+ * existing commit message
* @return new commit message
*/
- String modifyCommitMessage(String commit);
+ String modifyCommitMessage(String message);
}
+ /**
+ * Extends {@link InteractiveHandler} with an enhanced callback for editing
+ * commit messages.
+ *
+ * @since 6.1
+ */
+ public interface InteractiveHandler2 extends InteractiveHandler {
+
+ /**
+ * Callback API for editing a commit message on REWORD or SQUASH.
+ * <p>
+ * The callback gets the comment character currently set, and the
+ * clean-up mode. It can use this information when presenting the
+ * message to the user, and it also has the possibility to clean the
+ * message itself (in which case the returned {@link ModifyResult}
+ * should have {@link CleanupMode#VERBATIM} set lest JGit cleans the
+ * message again). It can also override the initial clean-up mode by
+ * returning clean-up mode other than {@link CleanupMode#DEFAULT}. If it
+ * does return {@code DEFAULT}, the passed-in {@code mode} will be
+ * applied.
+ * </p>
+ *
+ * @param message
+ * existing commit message
+ * @param mode
+ * {@link CleanupMode} currently set
+ * @param commentChar
+ * comment character used
+ * @return a {@link ModifyResult}
+ */
+ @NonNull
+ ModifyResult editCommitMessage(@NonNull String message,
+ @NonNull CleanupMode mode, char commentChar);
+
+ @Override
+ default String modifyCommitMessage(String message) {
+ // Should actually not be called; but do something reasonable anyway
+ ModifyResult result = editCommitMessage(
+ message == null ? "" : message, CleanupMode.STRIP, //$NON-NLS-1$
+ '#');
+ return result.getMessage();
+ }
+
+ /**
+ * Describes the result of editing a commit message: the new message,
+ * and how it should be cleaned.
+ */
+ interface ModifyResult {
+
+ /**
+ * Retrieves the new commit message.
+ *
+ * @return the message
+ */
+ @NonNull
+ String getMessage();
+
+ /**
+ * Tells how the message returned by {@link #getMessage()} should be
+ * cleaned.
+ *
+ * @return the {@link CleanupMode}
+ */
+ @NonNull
+ CleanupMode getCleanupMode();
+
+ /**
+ * Tells whether a Gerrit Change-Id should be computed and added to
+ * the commit message, as with
+ * {@link CommitCommand#setInsertChangeId(boolean)}.
+ *
+ * @return {@code true} if a Change-Id should be handled,
+ * {@code false} otherwise
+ */
+ boolean shouldAddChangeId();
+ }
+ }
PersonIdent parseAuthor(byte[] raw) {
if (raw.length == 0)
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
index 22ef4d0a32..513f579b67 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RevertCommand.java
@@ -9,6 +9,8 @@
*/
package org.eclipse.jgit.api;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH;
+
import java.io.IOException;
import java.text.MessageFormat;
import java.util.LinkedList;
@@ -28,6 +30,7 @@ import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.CommitConfig;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
@@ -128,8 +131,9 @@ public class RevertCommand extends GitCommand<RevCommit> {
revWalk.parseHeaders(srcParent);
String ourName = calculateOurName(headRef);
- String revertName = srcCommit.getId().abbreviate(7).name()
- + " " + srcCommit.getShortMessage(); //$NON-NLS-1$
+ String revertName = srcCommit.getId()
+ .abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH).name() + " " //$NON-NLS-1$
+ + srcCommit.getShortMessage();
ResolveMerger merger = (ResolveMerger) strategy.newMerger(repo);
merger.setWorkingTreeIterator(new FileTreeIterator(repo));
@@ -182,9 +186,12 @@ public class RevertCommand extends GitCommand<RevCommit> {
MergeStatus.CONFLICTING, strategy,
merger.getMergeResults(), failingPaths, null);
if (!merger.failed() && !unmergedPaths.isEmpty()) {
+ CommitConfig config = repo.getConfig()
+ .get(CommitConfig.KEY);
+ char commentChar = config.getCommentChar(newMessage);
String message = new MergeMessageFormatter()
- .formatWithConflicts(newMessage,
- merger.getUnmergedPaths());
+ .formatWithConflicts(newMessage,
+ merger.getUnmergedPaths(), commentChar);
repo.writeRevertHead(srcCommit.getId());
repo.writeMergeCommitMsg(message);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
index 35fd8992b6..f7a1f4eff8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/StashCreateCommand.java
@@ -9,6 +9,8 @@
*/
package org.eclipse.jgit.api;
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_ABBREV_STRING_LENGTH;
+
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -302,7 +304,8 @@ public class StashCreateCommand extends GitCommand<RevCommit> {
builder.setParentId(headCommit);
builder.setTreeId(cache.writeTree(inserter));
builder.setMessage(MessageFormat.format(indexMessage, branch,
- headCommit.abbreviate(7).name(),
+ headCommit.abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH)
+ .name(),
headCommit.getShortMessage()));
ObjectId indexCommit = inserter.insert(builder);
@@ -319,7 +322,10 @@ public class StashCreateCommand extends GitCommand<RevCommit> {
builder.setParentIds(new ObjectId[0]);
builder.setTreeId(untrackedDirCache.writeTree(inserter));
builder.setMessage(MessageFormat.format(MSG_UNTRACKED,
- branch, headCommit.abbreviate(7).name(),
+ branch,
+ headCommit
+ .abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH)
+ .name(),
headCommit.getShortMessage()));
untrackedCommit = inserter.insert(builder);
}
@@ -339,7 +345,8 @@ public class StashCreateCommand extends GitCommand<RevCommit> {
builder.addParentId(untrackedCommit);
builder.setMessage(MessageFormat.format(
workingDirectoryMessage, branch,
- headCommit.abbreviate(7).name(),
+ headCommit.abbreviate(OBJECT_ID_ABBREV_STRING_LENGTH)
+ .name(),
headCommit.getShortMessage()));
builder.setTreeId(cache.writeTree(inserter));
commitId = inserter.insert(builder);

Back to the top