Browse Source

Update reflog like C Git during rebase (non-interactive)

Bug: 346350
Change-Id: I119766a00bc52a810c51cffaa19207cb8555ca22
Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
tags/v3.0.2.201309041250-rc2
Robin Rosenberg 11 years ago
parent
commit
99d981ce35

+ 1
- 0
org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/RepositoryTestCase.java View File

walk.release(); walk.release();
// update the HEAD // update the HEAD
RefUpdate refUpdate = db.updateRef(Constants.HEAD); RefUpdate refUpdate = db.updateRef(Constants.HEAD);
refUpdate.setRefLogMessage("checkout: moving to " + branchName, false);
refUpdate.link(branchName); refUpdate.link(branchName);
} }



+ 90
- 2
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java View File

import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.ReflogEntry;
import org.eclipse.jgit.lib.RepositoryState; import org.eclipse.jgit.lib.RepositoryState;
import org.eclipse.jgit.merge.MergeStrategy; import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason; import org.eclipse.jgit.merge.ResolveMerger.MergeFailureReason;
// update the HEAD // update the HEAD
RefUpdate refUpdate = db.updateRef(Constants.HEAD, true); RefUpdate refUpdate = db.updateRef(Constants.HEAD, true);
refUpdate.setNewObjectId(commit); refUpdate.setNewObjectId(commit);
refUpdate.setRefLogMessage("checkout: moving to " + head.getName(),
false);
refUpdate.forceUpdate(); refUpdate.forceUpdate();
} }


// create file2 on master // create file2 on master
File file2 = writeTrashFile("file2", "file2"); File file2 = writeTrashFile("file2", "file2");
git.add().addFilepattern("file2").call(); 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()); assertTrue(new File(db.getWorkTree(), "file2").exists());


checkoutBranch("refs/heads/topic"); checkoutBranch("refs/heads/topic");
assertTrue(new File(db.getWorkTree(), "file2").exists()); assertTrue(new File(db.getWorkTree(), "file2").exists());
checkFile(file2, "file2"); checkFile(file2, "file2");
assertEquals(Status.FAST_FORWARD, res.getStatus()); assertEquals(Status.FAST_FORWARD, res.getStatus());

List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
.getReverseEntries();
List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
.getReverseEntries();
List<ReflogEntry> 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 @Test
// write a second commit // write a second commit
writeTrashFile("file2", "file2 new content"); writeTrashFile("file2", "file2 new content");
git.add().addFilepattern("file2").call(); 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"); checkoutBranch("refs/heads/topic");
assertFalse(new File(db.getWorkTree(), "file2").exists()); assertFalse(new File(db.getWorkTree(), "file2").exists());
assertTrue(new File(db.getWorkTree(), "file2").exists()); assertTrue(new File(db.getWorkTree(), "file2").exists());
checkFile(file2, "file2 new content"); checkFile(file2, "file2 new content");
assertEquals(Status.FAST_FORWARD, res.getStatus()); assertEquals(Status.FAST_FORWARD, res.getStatus());

List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
.getReverseEntries();
List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
.getReverseEntries();
List<ReflogEntry> 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());
} }


/** /**
assertDerivedFrom(rw.next(), c); assertDerivedFrom(rw.next(), c);
assertEquals(b, rw.next()); assertEquals(b, rw.next());
assertEquals(a, rw.next()); assertEquals(a, rw.next());

List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
.getReverseEntries();
List<ReflogEntry> sideLog = db.getReflogReader("refs/heads/side")
.getReverseEntries();
List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
.getReverseEntries();
List<ReflogEntry> 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) { static void assertDerivedFrom(RevCommit derived, RevCommit original) {


RebaseResult result = git.rebase().setUpstream(parent).call(); RebaseResult result = git.rebase().setUpstream(parent).call();
assertEquals(Status.UP_TO_DATE, result.getStatus()); 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 @Test


RebaseResult res = git.rebase().setUpstream(first).call(); RebaseResult res = git.rebase().setUpstream(first).call();
assertEquals(Status.UP_TO_DATE, res.getStatus()); 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 @Test
assertEquals(lastMasterChange, new RevWalk(db).parseCommit( assertEquals(lastMasterChange, new RevWalk(db).parseCommit(
db.resolve(Constants.HEAD)).getParent(0)); db.resolve(Constants.HEAD)).getParent(0));
assertEquals(origHead, db.readOrigHead()); assertEquals(origHead, db.readOrigHead());
List<ReflogEntry> headLog = db.getReflogReader(Constants.HEAD)
.getReverseEntries();
List<ReflogEntry> topicLog = db.getReflogReader("refs/heads/topic")
.getReverseEntries();
List<ReflogEntry> 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 @Test
assertEquals(lastMasterChange, new RevWalk(db).parseCommit( assertEquals(lastMasterChange, new RevWalk(db).parseCommit(
db.resolve(Constants.HEAD)).getParent(0)); db.resolve(Constants.HEAD)).getParent(0));


List<ReflogEntry> 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 @Test

+ 19
- 3
org.eclipse.jgit/src/org/eclipse/jgit/api/CherryPickCommand.java View File

* >Git documentation about cherry-pick</a> * >Git documentation about cherry-pick</a>
*/ */
public class CherryPickCommand extends GitCommand<CherryPickResult> { public class CherryPickCommand extends GitCommand<CherryPickResult> {
private String reflogPrefix = "cherry-pick:"; //$NON-NLS-1$

private List<Ref> commits = new LinkedList<Ref>(); private List<Ref> commits = new LinkedList<Ref>();


private String ourCommitName = null; private String ourCommitName = null;
dco.checkout(); dco.checkout();
newHead = new Git(getRepository()).commit() newHead = new Git(getRepository()).commit()
.setMessage(srcCommit.getFullMessage()) .setMessage(srcCommit.getFullMessage())
.setReflogComment(
"cherry-pick: " //$NON-NLS-1$
+ srcCommit.getShortMessage())
.setReflogComment(reflogPrefix + " " //$NON-NLS-1$
+ srcCommit.getShortMessage())
.setAuthor(srcCommit.getAuthorIdent()).call(); .setAuthor(srcCommit.getAuthorIdent()).call();
cherryPickedRefs.add(src); cherryPickedRefs.add(src);
} else { } else {
return this; return this;
} }


/**
* Set the prefix to use in the reflog.
* <p>
* 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) { private String calculateOurName(Ref headRef) {
if (ourCommitName != null) if (ourCommitName != null)
return ourCommitName; return ourCommitName;

+ 21
- 10
org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java View File

String ourCommitName = getOurCommitName(); String ourCommitName = getOurCommitName();
CherryPickResult cherryPickResult = new Git(repo) CherryPickResult cherryPickResult = new Git(repo)
.cherryPick().include(commitToPick) .cherryPick().include(commitToPick)
.setOurCommitName(ourCommitName).call();
.setOurCommitName(ourCommitName)
.setReflogPrefix("rebase:").call(); //$NON-NLS-1$
switch (cherryPickResult.getStatus()) { switch (cherryPickResult.getStatus()) {
case FAILED: case FAILED:
if (operation == Operation.BEGIN) if (operation == Operation.BEGIN)
} }
if (newHead != null) { if (newHead != null) {
String headName = rebaseState.readFile(HEAD_NAME); String headName = rebaseState.readFile(HEAD_NAME);
updateHead(headName, newHead);
updateHead(headName, newHead, upstreamCommit);
FileUtils.delete(rebaseState.getDir(), FileUtils.RECURSIVE); FileUtils.delete(rebaseState.getDir(), FileUtils.RECURSIVE);
if (lastStepWasForward) if (lastStepWasForward)
return RebaseResult.FAST_FORWARD_RESULT; return RebaseResult.FAST_FORWARD_RESULT;
private String getOurCommitName() { private String getOurCommitName() {
// If onto is different from upstream, this should say "onto", but // If onto is different from upstream, this should say "onto", but
// RebaseCommand doesn't support a different "onto" at the moment. // 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); + Repository.shortenRefName(upstreamCommitName);
return ourCommitName; return ourCommitName;
} }


private void updateHead(String headName, RevCommit newHead)
private void updateHead(String headName, RevCommit newHead, RevCommit onto)
throws IOException { throws IOException {
// point the previous head (if any) to the new commit // point the previous head (if any) to the new commit


if (headName.startsWith(Constants.R_REFS)) { if (headName.startsWith(Constants.R_REFS)) {
RefUpdate rup = repo.updateRef(headName); RefUpdate rup = repo.updateRef(headName);
rup.setNewObjectId(newHead); rup.setNewObjectId(newHead);
rup.setRefLogMessage("rebase finished: " + headName + " onto " //$NON-NLS-1$
+ onto.getName(), false);
Result res = rup.forceUpdate(); Result res = rup.forceUpdate();
switch (res) { switch (res) {
case FAST_FORWARD: case FAST_FORWARD:
throw new JGitInternalException("Updating HEAD failed"); throw new JGitInternalException("Updating HEAD failed");
} }
rup = repo.updateRef(Constants.HEAD); rup = repo.updateRef(Constants.HEAD);
rup.setRefLogMessage("rebase finished: returning to " + headName, //$NON-NLS-1$
false);
res = rup.link(headName); res = rup.link(headName);
switch (res) { switch (res) {
case FAST_FORWARD: case FAST_FORWARD:
if (head.isSymbolic()) if (head.isSymbolic())
headName = head.getTarget().getName(); headName = head.getTarget().getName();
else else
headName = "detached HEAD";
headName = head.getObjectId().getName();
ObjectId headId = head.getObjectId(); ObjectId headId = head.getObjectId();
if (headId == null) if (headId == null)
throw new RefNotFoundException(MessageFormat.format( throw new RefNotFoundException(MessageFormat.format(
monitor.beginTask(MessageFormat.format( monitor.beginTask(MessageFormat.format(
JGitText.get().resettingHead, JGitText.get().resettingHead,
upstreamCommit.getShortMessage()), ProgressMonitor.UNKNOWN); upstreamCommit.getShortMessage()), ProgressMonitor.UNKNOWN);
checkoutCommit(upstreamCommit);
checkoutCommit(headName, upstreamCommit);
monitor.endTask(); monitor.endTask();


updateHead(headName, upstreamCommit);
updateHead(headName, upstreamCommit, upstream);
return RebaseResult.FAST_FORWARD_RESULT; return RebaseResult.FAST_FORWARD_RESULT;
} }


upstreamCommit.getShortMessage()), ProgressMonitor.UNKNOWN); upstreamCommit.getShortMessage()), ProgressMonitor.UNKNOWN);
boolean checkoutOk = false; boolean checkoutOk = false;
try { try {
checkoutOk = checkoutCommit(upstreamCommit);
checkoutOk = checkoutCommit(headName, upstreamCommit);
} finally { } finally {
if (!checkoutOk) if (!checkoutOk)
FileUtils.delete(rebaseState.getDir(), FileUtils.RECURSIVE); FileUtils.delete(rebaseState.getDir(), FileUtils.RECURSIVE);
if (head.isSymbolic()) if (head.isSymbolic())
headName = head.getTarget().getName(); headName = head.getTarget().getName();
else else
headName = "detached HEAD";
headName = head.getObjectId().getName();
return tryFastForward(headName, headCommit, newCommit); return tryFastForward(headName, headCommit, newCommit);
} }




// update the HEAD // update the HEAD
RefUpdate refUpdate = repo.updateRef(Constants.HEAD, false); RefUpdate refUpdate = repo.updateRef(Constants.HEAD, false);
refUpdate.setRefLogMessage("rebase: aborting", false); //$NON-NLS-1$
Result res = refUpdate.link(headName); Result res = refUpdate.link(headName);
switch (res) { switch (res) {
case FAST_FORWARD: case FAST_FORWARD:
} }
} }


private boolean checkoutCommit(RevCommit commit) throws IOException,
private boolean checkoutCommit(String headName, RevCommit commit)
throws IOException,
CheckoutConflictException { CheckoutConflictException {
try { try {
RevCommit head = walk.parseCommit(repo.resolve(Constants.HEAD)); RevCommit head = walk.parseCommit(repo.resolve(Constants.HEAD));
RefUpdate refUpdate = repo.updateRef(Constants.HEAD, true); RefUpdate refUpdate = repo.updateRef(Constants.HEAD, true);
refUpdate.setExpectedOldObjectId(head); refUpdate.setExpectedOldObjectId(head);
refUpdate.setNewObjectId(commit); 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(); Result res = refUpdate.forceUpdate();
switch (res) { switch (res) {
case FAST_FORWARD: case FAST_FORWARD:

Loading…
Cancel
Save