Bug: 346350 Change-Id: I119766a00bc52a810c51cffaa19207cb8555ca22 Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>tags/v3.0.2.201309041250-rc2
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); | ||||
} | } | ||||
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 |
* >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; |
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: |