Store commits under refs/cache-automerge/, instead of trees

Storing an (automerge) commit objects under refs/cache-automerge/
is just a small overhead compared to storing the automerge trees.
A benefit of storing commits is that we can use them as the
input for the blame annotations computation which is implemented
in a follow-up change.

Any existing tree, stored under refs/cache-automerge/, is replaced with
a commit (pointing to that tree) when it gets accessed first time.

Change-Id: I9c8b5addedf734e0bb500c176701d7f45e202a76
This commit is contained in:
Saša Živkov
2016-04-01 16:54:11 +02:00
committed by Dave Borowitz
parent c28ebd8d72
commit 9d5a0d3154
3 changed files with 55 additions and 12 deletions

View File

@@ -373,7 +373,8 @@ public class AllChangesIndexer
walk.parseBody(a); walk.parseBody(a);
return walk.parseTree(a.getTree()); return walk.parseTree(a.getTree());
case 2: case 2:
return autoMerger.merge(repo, walk, b, mergeStrategy); RevCommit am = autoMerger.merge(repo, walk, b, mergeStrategy);
return am == null ? null : am.getTree();
default: default:
return null; return null;
} }

View File

@@ -17,15 +17,18 @@ package com.google.gerrit.server.patch;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.gerrit.reviewdb.client.RefNames; import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.inject.Inject; import com.google.inject.Inject;
import org.eclipse.jgit.diff.Sequence; import org.eclipse.jgit.diff.Sequence;
import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder; import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
@@ -34,7 +37,7 @@ import org.eclipse.jgit.merge.MergeResult;
import org.eclipse.jgit.merge.ResolveMerger; import org.eclipse.jgit.merge.ResolveMerger;
import org.eclipse.jgit.merge.ThreeWayMergeStrategy; import org.eclipse.jgit.merge.ThreeWayMergeStrategy;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.util.TemporaryBuffer; import org.eclipse.jgit.util.TemporaryBuffer;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -48,11 +51,20 @@ import java.util.Map;
public class AutoMerger { public class AutoMerger {
private static final Logger log = LoggerFactory.getLogger(AutoMerger.class); private static final Logger log = LoggerFactory.getLogger(AutoMerger.class);
private final PersonIdent gerritIdent;
@Inject @Inject
AutoMerger() { AutoMerger(@GerritPersonIdent PersonIdent gerritIdent) {
this.gerritIdent = gerritIdent;
} }
public RevTree merge(Repository repo, RevWalk rw, RevCommit merge, /**
* Perform an auto-merge of the parents of the given merge commit.
*
* @return auto-merge commit or {@code null} if an auto-merge commit
* couldn't be created. Headers of the returned RevCommit are parsed.
*/
public RevCommit merge(Repository repo, RevWalk rw, RevCommit merge,
ThreeWayMergeStrategy mergeStrategy) throws IOException { ThreeWayMergeStrategy mergeStrategy) throws IOException {
rw.parseHeaders(merge); rw.parseHeaders(merge);
String hash = merge.name(); String hash = merge.name();
@@ -62,7 +74,11 @@ public class AutoMerger {
+ hash.substring(2); + hash.substring(2);
Ref ref = repo.getRefDatabase().exactRef(refName); Ref ref = repo.getRefDatabase().exactRef(refName);
if (ref != null && ref.getObjectId() != null) { if (ref != null && ref.getObjectId() != null) {
return rw.parseTree(ref.getObjectId()); RevObject obj = rw.parseAny(ref.getObjectId());
if (obj instanceof RevCommit) {
return (RevCommit) obj;
}
return commit(repo, rw, refName, obj, merge);
} }
ResolveMerger m = (ResolveMerger) mergeStrategy.newMerger(repo, true); ResolveMerger m = (ResolveMerger) mergeStrategy.newMerger(repo, true);
@@ -178,12 +194,38 @@ public class AutoMerger {
} }
ins.flush(); ins.flush();
RefUpdate update = repo.updateRef(refName); return commit(repo, rw, refName, treeId, merge);
update.setNewObjectId(treeId);
update.disableRefLog();
update.forceUpdate();
return rw.lookupTree(treeId);
} }
} }
private RevCommit commit(Repository repo, RevWalk rw, String refName,
ObjectId tree, RevCommit merge) throws IOException {
rw.parseHeaders(merge);
// For maximum stability, choose a single ident using the committer time of
// the input commit, using the server name and timezone.
PersonIdent ident = new PersonIdent(
gerritIdent,
merge.getCommitterIdent().getWhen(),
gerritIdent.getTimeZone());
CommitBuilder cb = new CommitBuilder();
cb.setAuthor(ident);
cb.setCommitter(ident);
cb.setTreeId(tree);
cb.setMessage("Auto-merge of " + merge.name() + '\n');
for (RevCommit p : merge.getParents()) {
cb.addParentId(p);
}
ObjectId commitId;
try (ObjectInserter ins = repo.newObjectInserter()) {
commitId = ins.insert(cb);
ins.flush();
}
RefUpdate ru = repo.updateRef(refName);
ru.setNewObjectId(commitId);
ru.disableRefLog();
ru.forceUpdate();
return rw.parseCommit(commitId);
}
} }

View File

@@ -34,7 +34,7 @@ import java.io.ObjectOutputStream;
import java.io.Serializable; import java.io.Serializable;
public class PatchListKey implements Serializable { public class PatchListKey implements Serializable {
static final long serialVersionUID = 19L; static final long serialVersionUID = 20L;
public static final BiMap<Whitespace, Character> WHITESPACE_TYPES = ImmutableBiMap.of( public static final BiMap<Whitespace, Character> WHITESPACE_TYPES = ImmutableBiMap.of(
Whitespace.IGNORE_NONE, 'N', Whitespace.IGNORE_NONE, 'N',