Defer object flushing when merging

Mergers in the most recent JGit know how to read previously-inserted
objects back, so a series of merges does not require aggressively
flushing the objects. Instead, flush objects in MergeOp just before
updating the relevant refs.

Change-Id: Ia85c5114e86739b4a480bea65f3413fe47cf572d
This commit is contained in:
Dave Borowitz
2013-06-10 16:45:48 -07:00
parent 587c7a235d
commit 5dea1c0fc0
5 changed files with 120 additions and 130 deletions

View File

@@ -113,16 +113,15 @@ public class CherryPickChange {
Project.NameKey project = change.getProject();
IdentifiedUser identifiedUser = (IdentifiedUser) currentUser.get();
final Repository git;
try {
git = gitManager.openRepository(project);
} catch (RepositoryNotFoundException e) {
throw new NoSuchChangeException(change.getId(), e);
}
Repository git = null;
ObjectInserter oi = null;
RevWalk revWalk = null;
try {
RevWalk revWalk = new RevWalk(git);
try {
git = gitManager.openRepository(project);
oi = git.newObjectInserter();
revWalk = new RevWalk(oi.newReader());
Ref destRef = git.getRef(destinationBranch);
if (destRef == null) {
throw new InvalidChangeOperationException("Branch "
@@ -146,17 +145,11 @@ public class CherryPickChange {
ChangeIdUtil.insertId(message, computedChangeId).trim() + '\n';
RevCommit cherryPickCommit;
ObjectInserter oi = git.newObjectInserter();
try {
ProjectState projectState = refControl.getProjectControl().getProjectState();
cherryPickCommit =
mergeUtilFactory.create(projectState).createCherryPickFromCommit(git, oi, mergeTip,
commitToCherryPick, committerIdent, commitMessage, revWalk);
} catch (MergeIdenticalTreeException | MergeConflictException e) {
throw new MergeException("Cherry pick failed: " + e.getMessage());
} finally {
oi.release();
}
oi.flush();
Change.Key changeKey;
final List<String> idList = cherryPickCommit.getFooterLines(
@@ -189,13 +182,22 @@ public class CherryPickChange {
patch.getId(), destRef, cherryPickCommit, refControl,
identifiedUser, change.getTopic());
}
} catch (MergeIdenticalTreeException | MergeConflictException e) {
throw new MergeException("Cherry pick failed: " + e.getMessage());
} catch (RepositoryNotFoundException e) {
throw new NoSuchChangeException(change.getId(), e);
} finally {
if (revWalk != null) {
revWalk.release();
}
} finally {
if (oi != null) {
oi.release();
}
if (git != null) {
git.close();
}
}
}
private Change.Id insertPatchSet(Repository git, RevWalk revWalk, Change change,
RevCommit cherryPickCommit, RefControl refControl,

View File

@@ -23,6 +23,7 @@ import com.google.gerrit.server.project.ChangeControl;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -50,12 +51,11 @@ public class CodeReviewCommit extends RevCommit {
}).nullsFirst();
public static RevWalk newRevWalk(Repository repo) {
return new RevWalk(repo) {
@Override
protected RevCommit createCommit(AnyObjectId id) {
return new CodeReviewCommit(id);
return new CodeReviewRevWalk(repo);
}
};
public static RevWalk newRevWalk(ObjectReader reader) {
return new CodeReviewRevWalk(reader);
}
static CodeReviewCommit revisionGone(ChangeControl ctl) {
@@ -85,6 +85,21 @@ public class CodeReviewCommit extends RevCommit {
return r;
}
private static class CodeReviewRevWalk extends RevWalk {
private CodeReviewRevWalk(Repository repo) {
super(repo);
}
private CodeReviewRevWalk(ObjectReader reader) {
super(reader);
}
@Override
protected RevCommit createCommit(AnyObjectId id) {
return new CodeReviewCommit(id);
}
}
/**
* Unique key of the PatchSet entity from the code review system.
* <p>

View File

@@ -430,13 +430,11 @@ public class MergeOp {
String m = "Error opening repository \"" + name.get() + '"';
throw new MergeException(m, err);
}
rw = CodeReviewCommit.newRevWalk(repo);
inserter = repo.newObjectInserter();
rw = CodeReviewCommit.newRevWalk(inserter.newReader());
rw.sort(RevSort.TOPO);
rw.sort(RevSort.COMMIT_TIME_DESC, true);
canMergeFlag = rw.newFlag("CAN_MERGE");
inserter = repo.newObjectInserter();
}
private RefUpdate openBranch()
@@ -674,6 +672,11 @@ public class MergeOp {
+ destProject.getProject().getName(), e);
}
}
try {
inserter.flush();
} catch (IOException e) {
throw new MergeException("Cannot flush merge results", e);
}
branchUpdate.setRefLogIdent(refLogIdent);
branchUpdate.setForceUpdate(false);

View File

@@ -43,7 +43,6 @@ import org.eclipse.jgit.errors.LargeObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.NoMergeBaseException;
import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
@@ -67,7 +66,6 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
@@ -79,6 +77,12 @@ import java.util.List;
import java.util.Set;
import java.util.TimeZone;
/**
* Utilities for various kinds of merges and cherry-picks.
* <p>
* <b>Note:</b> Unless otherwise noted, the methods in this class do not flush
* the {@link ObjectInserter}s passed in after performing a merge.
*/
public class MergeUtil {
private static final Logger log = LoggerFactory.getLogger(MergeUtil.class);
private static final String R_HEADS_MASTER =
@@ -177,7 +181,7 @@ public class MergeUtil {
final ThreeWayMerger m = newThreeWayMerger(repo, inserter);
m.setBase(originalCommit.getParent(0));
if (m.merge(mergeTip, originalCommit)) {
if (m.merge(false, mergeTip, originalCommit)) {
ObjectId tree = m.getResultTreeId();
if (tree.equals(mergeTip.getTree())) {
throw new MergeIdenticalTreeException("identical tree");
@@ -189,7 +193,7 @@ public class MergeUtil {
mergeCommit.setAuthor(originalCommit.getAuthorIdent());
mergeCommit.setCommitter(cherryPickCommitterIdent);
mergeCommit.setMessage(commitMsg);
return rw.parseCommit(commit(inserter, mergeCommit));
return rw.parseCommit(inserter.insert(mergeCommit));
} else {
throw new MergeConflictException("merge conflict");
}
@@ -393,7 +397,7 @@ public class MergeUtil {
ThreeWayMerger m = newThreeWayMerger(repo, createDryRunInserter(repo));
try {
return m.merge(new AnyObjectId[] {mergeTip, toMerge});
return m.merge(false, mergeTip, toMerge);
} catch (LargeObjectException e) {
log.warn("Cannot merge due to LargeObjectException: " + toMerge.name());
return false;
@@ -442,7 +446,7 @@ public class MergeUtil {
try {
ThreeWayMerger m = newThreeWayMerger(repo, createDryRunInserter(repo));
m.setBase(toMerge.getParent(0));
return m.merge(mergeTip, toMerge);
return m.merge(false, mergeTip, toMerge);
} catch (IOException e) {
throw new MergeException("Cannot merge " + toMerge.name(), e);
}
@@ -494,7 +498,7 @@ public class MergeUtil {
throws MergeException {
final ThreeWayMerger m = newThreeWayMerger(repo, inserter);
try {
if (m.merge(new AnyObjectId[] {mergeTip, n})) {
if (m.merge(false, mergeTip, n)) {
return writeMergeCommit(myIdent, rw, inserter, canMergeFlag, destBranch,
mergeTip, m.getResultTreeId(), n);
} else {
@@ -582,7 +586,7 @@ public class MergeUtil {
mergeCommit.setMessage(msgbuf.toString());
CodeReviewCommit mergeResult =
(CodeReviewCommit) rw.parseCommit(commit(inserter, mergeCommit));
(CodeReviewCommit) rw.parseCommit(inserter.insert(mergeCommit));
mergeResult.setControl(n.getControl());
return mergeResult;
}
@@ -656,31 +660,10 @@ public class MergeUtil {
Merger m = strategy.newMerger(repo, true);
checkArgument(m instanceof ThreeWayMerger,
"merge strategy %s does not support three-way merging", strategyName);
m.setObjectInserter(new ObjectInserter.Filter() {
@Override
protected ObjectInserter delegate() {
return inserter;
}
@Override
public void flush() {
}
@Override
public void release() {
}
});
m.setObjectInserter(inserter);
return (ThreeWayMerger) m;
}
public ObjectId commit(final ObjectInserter inserter,
final CommitBuilder mergeCommit) throws IOException,
UnsupportedEncodingException {
ObjectId id = inserter.insert(mergeCommit);
inserter.flush();
return id;
}
public PatchSetApproval markCleanMerges(final RevWalk rw,
final RevFlag canMergeFlag, final CodeReviewCommit mergeTip,
final Set<RevCommit> alreadyAccepted) throws MergeException {

View File

@@ -275,24 +275,11 @@ public class PatchListLoader extends CacheLoader<PatchListKey, PatchList> {
try {
DirCache dc = DirCache.newInCore();
m.setDirCache(dc);
m.setObjectInserter(new ObjectInserter.Filter() {
@Override
protected ObjectInserter delegate() {
return ins;
}
@Override
public void flush() {
}
@Override
public void release() {
}
});
m.setObjectInserter(ins);
boolean couldMerge;
try {
couldMerge = m.merge(b.getParents());
couldMerge = m.merge(false, b.getParents());
} catch (IOException e) {
// It is not safe to continue further down in this method as throwing
// an exception most likely means that the merge tree was not created