From 99d981ce35d66b89cdd5d539513bf0ec070b7b34 Mon Sep 17 00:00:00 2001 From: Robin Rosenberg Date: Mon, 6 May 2013 01:39:25 +0200 Subject: [PATCH] Update reflog like C Git during rebase (non-interactive) Bug: 346350 Change-Id: I119766a00bc52a810c51cffaa19207cb8555ca22 Signed-off-by: Chris Aniszczyk --- .../jgit/junit/RepositoryTestCase.java | 1 + .../eclipse/jgit/api/RebaseCommandTest.java | 92 ++++++++++++++++++- .../eclipse/jgit/api/CherryPickCommand.java | 22 ++++- .../org/eclipse/jgit/api/RebaseCommand.java | 31 +++++-- 4 files changed, 131 insertions(+), 15 deletions(-) diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java index 397fb6151..1f1962a7b 100644 --- a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java +++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java @@ -415,6 +415,7 @@ public abstract class RepositoryTestCase extends LocalDiskRepositoryTestCase { walk.release(); // update the HEAD RefUpdate refUpdate = db.updateRef(Constants.HEAD); + refUpdate.setRefLogMessage("checkout: moving to " + branchName, false); refUpdate.link(branchName); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java index 9aa13caf9..ef5a1eaa0 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java @@ -74,6 +74,7 @@ import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.ReflogEntry; import org.eclipse.jgit.lib.RepositoryState; import org.eclipse.jgit.merge.MergeStrategy; import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason; @@ -107,6 +108,8 @@ public class RebaseCommandTest extends RepositoryTestCase { // update the HEAD RefUpdate refUpdate = db.updateRef(Constants.HEAD, true); refUpdate.setNewObjectId(commit); + refUpdate.setRefLogMessage("checkout: moving to " + head.getName(), + false); refUpdate.forceUpdate(); } @@ -123,7 +126,7 @@ public class RebaseCommandTest extends RepositoryTestCase { // create file2 on master File file2 = writeTrashFile("file2", "file2"); git.add().addFilepattern("file2").call(); - git.commit().setMessage("Add file2").call(); + RevCommit second = git.commit().setMessage("Add file2").call(); assertTrue(new File(db.getWorkTree(), "file2").exists()); checkoutBranch("refs/heads/topic"); @@ -133,6 +136,22 @@ public class RebaseCommandTest extends RepositoryTestCase { assertTrue(new File(db.getWorkTree(), "file2").exists()); checkFile(file2, "file2"); assertEquals(Status.FAST_FORWARD, res.getStatus()); + + List headLog = db.getReflogReader(Constants.HEAD) + .getReverseEntries(); + List topicLog = db.getReflogReader("refs/heads/topic") + .getReverseEntries(); + List masterLog = db.getReflogReader("refs/heads/master") + .getReverseEntries(); + assertEquals("rebase finished: returning to refs/heads/topic", headLog + .get(0).getComment()); + assertEquals("checkout: moving from topic to " + second.getName(), + headLog.get(1).getComment()); + assertEquals(2, masterLog.size()); + assertEquals(2, topicLog.size()); + assertEquals( + "rebase finished: refs/heads/topic onto " + second.getName(), + topicLog.get(0).getComment()); } @Test @@ -153,7 +172,8 @@ public class RebaseCommandTest extends RepositoryTestCase { // write a second commit writeTrashFile("file2", "file2 new content"); git.add().addFilepattern("file2").call(); - git.commit().setMessage("Change content of file2").call(); + RevCommit second = git.commit().setMessage("Change content of file2") + .call(); checkoutBranch("refs/heads/topic"); assertFalse(new File(db.getWorkTree(), "file2").exists()); @@ -162,6 +182,22 @@ public class RebaseCommandTest extends RepositoryTestCase { assertTrue(new File(db.getWorkTree(), "file2").exists()); checkFile(file2, "file2 new content"); assertEquals(Status.FAST_FORWARD, res.getStatus()); + + List headLog = db.getReflogReader(Constants.HEAD) + .getReverseEntries(); + List topicLog = db.getReflogReader("refs/heads/topic") + .getReverseEntries(); + List masterLog = db.getReflogReader("refs/heads/master") + .getReverseEntries(); + assertEquals("rebase finished: returning to refs/heads/topic", headLog + .get(0).getComment()); + assertEquals("checkout: moving from topic to " + second.getName(), + headLog.get(1).getComment()); + assertEquals(3, masterLog.size()); + assertEquals(2, topicLog.size()); + assertEquals( + "rebase finished: refs/heads/topic onto " + second.getName(), + topicLog.get(0).getComment()); } /** @@ -242,6 +278,29 @@ public class RebaseCommandTest extends RepositoryTestCase { assertDerivedFrom(rw.next(), c); assertEquals(b, rw.next()); assertEquals(a, rw.next()); + + List headLog = db.getReflogReader(Constants.HEAD) + .getReverseEntries(); + List sideLog = db.getReflogReader("refs/heads/side") + .getReverseEntries(); + List topicLog = db.getReflogReader("refs/heads/topic") + .getReverseEntries(); + List masterLog = db.getReflogReader("refs/heads/master") + .getReverseEntries(); + assertEquals("rebase finished: returning to refs/heads/topic", headLog + .get(0).getComment()); + assertEquals("rebase: update file2 on side", headLog.get(1) + .getComment()); + assertEquals("rebase: Add file2", headLog.get(2).getComment()); + assertEquals("rebase: update file3 on topic", headLog.get(3) + .getComment()); + assertEquals("checkout: moving from topic to " + b.getName(), headLog + .get(4).getComment()); + assertEquals(2, masterLog.size()); + assertEquals(2, sideLog.size()); + assertEquals(5, topicLog.size()); + assertEquals("rebase finished: refs/heads/topic onto " + b.getName(), + topicLog.get(0).getComment()); } static void assertDerivedFrom(RevCommit derived, RevCommit original) { @@ -261,6 +320,11 @@ public class RebaseCommandTest extends RepositoryTestCase { RebaseResult result = git.rebase().setUpstream(parent).call(); assertEquals(Status.UP_TO_DATE, result.getStatus()); + + assertEquals(2, db.getReflogReader(Constants.HEAD).getReverseEntries() + .size()); + assertEquals(2, db.getReflogReader("refs/heads/master") + .getReverseEntries().size()); } @Test @@ -274,6 +338,11 @@ public class RebaseCommandTest extends RepositoryTestCase { RebaseResult res = git.rebase().setUpstream(first).call(); assertEquals(Status.UP_TO_DATE, res.getStatus()); + + assertEquals(1, db.getReflogReader(Constants.HEAD).getReverseEntries() + .size()); + assertEquals(1, db.getReflogReader("refs/heads/master") + .getReverseEntries().size()); } @Test @@ -328,6 +397,18 @@ public class RebaseCommandTest extends RepositoryTestCase { assertEquals(lastMasterChange, new RevWalk(db).parseCommit( db.resolve(Constants.HEAD)).getParent(0)); assertEquals(origHead, db.readOrigHead()); + List headLog = db.getReflogReader(Constants.HEAD) + .getReverseEntries(); + List topicLog = db.getReflogReader("refs/heads/topic") + .getReverseEntries(); + List masterLog = db.getReflogReader("refs/heads/master") + .getReverseEntries(); + assertEquals(2, masterLog.size()); + assertEquals(3, topicLog.size()); + assertEquals("rebase finished: refs/heads/topic onto " + + lastMasterChange.getName(), topicLog.get(0).getComment()); + assertEquals("rebase finished: returning to refs/heads/topic", headLog + .get(0).getComment()); } @Test @@ -366,6 +447,13 @@ public class RebaseCommandTest extends RepositoryTestCase { assertEquals(lastMasterChange, new RevWalk(db).parseCommit( db.resolve(Constants.HEAD)).getParent(0)); + List headLog = db.getReflogReader(Constants.HEAD) + .getReverseEntries(); + assertEquals(8, headLog.size()); + assertEquals("rebase: change file1 in topic", headLog.get(0) + .getComment()); + assertEquals("checkout: moving from " + topicCommit.getName() + " to " + + lastMasterChange.getName(), headLog.get(1).getComment()); } @Test 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 2ebff14f9..1b8441170 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java @@ -82,6 +82,8 @@ import org.eclipse.jgit.treewalk.FileTreeIterator; * >Git documentation about cherry-pick */ public class CherryPickCommand extends GitCommand { + private String reflogPrefix = "cherry-pick:"; //$NON-NLS-1$ + private List commits = new LinkedList(); private String ourCommitName = null; @@ -166,9 +168,8 @@ public class CherryPickCommand extends GitCommand { dco.checkout(); newHead = new Git(getRepository()).commit() .setMessage(srcCommit.getFullMessage()) - .setReflogComment( - "cherry-pick: " //$NON-NLS-1$ - + srcCommit.getShortMessage()) + .setReflogComment(reflogPrefix + " " //$NON-NLS-1$ + + srcCommit.getShortMessage()) .setAuthor(srcCommit.getAuthorIdent()).call(); cherryPickedRefs.add(src); } else { @@ -242,6 +243,21 @@ public class CherryPickCommand extends GitCommand { return this; } + /** + * Set the prefix to use in the reflog. + *

+ * This is primarily needed for implementing rebase in terms of + * cherry-picking + * + * @param prefix + * including ":" + * @return {@code this} + */ + public CherryPickCommand setReflogPrefix(final String prefix) { + this.reflogPrefix = prefix; + return this; + } + private String calculateOurName(Ref headRef) { if (ourCommitName != null) return ourCommitName; 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 911a4e629..592a01287 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java @@ -319,7 +319,8 @@ public class RebaseCommand extends GitCommand { String ourCommitName = getOurCommitName(); CherryPickResult cherryPickResult = new Git(repo) .cherryPick().include(commitToPick) - .setOurCommitName(ourCommitName).call(); + .setOurCommitName(ourCommitName) + .setReflogPrefix("rebase:").call(); //$NON-NLS-1$ switch (cherryPickResult.getStatus()) { case FAILED: if (operation == Operation.BEGIN) @@ -353,7 +354,7 @@ public class RebaseCommand extends GitCommand { } if (newHead != null) { String headName = rebaseState.readFile(HEAD_NAME); - updateHead(headName, newHead); + updateHead(headName, newHead, upstreamCommit); FileUtils.delete(rebaseState.getDir(), FileUtils.RECURSIVE); if (lastStepWasForward) return RebaseResult.FAST_FORWARD_RESULT; @@ -370,18 +371,20 @@ public class RebaseCommand extends GitCommand { private String getOurCommitName() { // If onto is different from upstream, this should say "onto", but // RebaseCommand doesn't support a different "onto" at the moment. - String ourCommitName = "Upstream, based on " + String ourCommitName = "Upstream, based on " //$NON-NLS-1$ + Repository.shortenRefName(upstreamCommitName); return ourCommitName; } - private void updateHead(String headName, RevCommit newHead) + private void updateHead(String headName, RevCommit newHead, RevCommit onto) throws IOException { // point the previous head (if any) to the new commit if (headName.startsWith(Constants.R_REFS)) { RefUpdate rup = repo.updateRef(headName); rup.setNewObjectId(newHead); + rup.setRefLogMessage("rebase finished: " + headName + " onto " //$NON-NLS-1$ + + onto.getName(), false); Result res = rup.forceUpdate(); switch (res) { case FAST_FORWARD: @@ -392,6 +395,8 @@ public class RebaseCommand extends GitCommand { throw new JGitInternalException("Updating HEAD failed"); } rup = repo.updateRef(Constants.HEAD); + rup.setRefLogMessage("rebase finished: returning to " + headName, //$NON-NLS-1$ + false); res = rup.link(headName); switch (res) { case FAST_FORWARD: @@ -614,7 +619,7 @@ public class RebaseCommand extends GitCommand { if (head.isSymbolic()) headName = head.getTarget().getName(); else - headName = "detached HEAD"; + headName = head.getObjectId().getName(); ObjectId headId = head.getObjectId(); if (headId == null) throw new RefNotFoundException(MessageFormat.format( @@ -629,10 +634,10 @@ public class RebaseCommand extends GitCommand { monitor.beginTask(MessageFormat.format( JGitText.get().resettingHead, upstreamCommit.getShortMessage()), ProgressMonitor.UNKNOWN); - checkoutCommit(upstreamCommit); + checkoutCommit(headName, upstreamCommit); monitor.endTask(); - updateHead(headName, upstreamCommit); + updateHead(headName, upstreamCommit, upstream); return RebaseResult.FAST_FORWARD_RESULT; } @@ -691,7 +696,7 @@ public class RebaseCommand extends GitCommand { upstreamCommit.getShortMessage()), ProgressMonitor.UNKNOWN); boolean checkoutOk = false; try { - checkoutOk = checkoutCommit(upstreamCommit); + checkoutOk = checkoutCommit(headName, upstreamCommit); } finally { if (!checkoutOk) FileUtils.delete(rebaseState.getDir(), FileUtils.RECURSIVE); @@ -732,7 +737,7 @@ public class RebaseCommand extends GitCommand { if (head.isSymbolic()) headName = head.getTarget().getName(); else - headName = "detached HEAD"; + headName = head.getObjectId().getName(); return tryFastForward(headName, headCommit, newCommit); } @@ -843,6 +848,7 @@ public class RebaseCommand extends GitCommand { // update the HEAD RefUpdate refUpdate = repo.updateRef(Constants.HEAD, false); + refUpdate.setRefLogMessage("rebase: aborting", false); //$NON-NLS-1$ Result res = refUpdate.link(headName); switch (res) { case FAST_FORWARD: @@ -864,7 +870,8 @@ public class RebaseCommand extends GitCommand { } } - private boolean checkoutCommit(RevCommit commit) throws IOException, + private boolean checkoutCommit(String headName, RevCommit commit) + throws IOException, CheckoutConflictException { try { RevCommit head = walk.parseCommit(repo.resolve(Constants.HEAD)); @@ -880,6 +887,10 @@ public class RebaseCommand extends GitCommand { RefUpdate refUpdate = repo.updateRef(Constants.HEAD, true); refUpdate.setExpectedOldObjectId(head); refUpdate.setNewObjectId(commit); + refUpdate.setRefLogMessage( + "checkout: moving from " //$NON-NLS-1$ + + Repository.shortenRefName(headName) + + " to " + commit.getName(), false); //$NON-NLS-1$ Result res = refUpdate.forceUpdate(); switch (res) { case FAST_FORWARD: