Support rebasing independent branches
With completely independent branches, there is no merge base. In this case, the list of commits must include the root commit of the branch to be rebased. Bug: 581832 Change-Id: I0f5bdf179d5b07ff09f1a274d61c7a0b1c0011c6 Signed-off-by: Thomas Wolf <twolf@apache.org>
This commit is contained in:
parent
8bc13fb79d
commit
8c0c96e0a7
|
@ -76,6 +76,10 @@ public class RebaseCommandTest extends RepositoryTestCase {
|
|||
|
||||
private static final String FILE1 = "file1";
|
||||
|
||||
private static final String FILE2 = "file2";
|
||||
|
||||
private static final String FILE3 = "file3";
|
||||
|
||||
protected Git git;
|
||||
|
||||
@Override
|
||||
|
@ -190,6 +194,143 @@ public void testFastForwardWithMultipleCommits() throws Exception {
|
|||
topicLog.get(0).getComment());
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebase a single root commit onto an independent branch.
|
||||
*
|
||||
* <pre>
|
||||
* A (master)
|
||||
*
|
||||
* B - C (orphan)
|
||||
* </pre>
|
||||
*
|
||||
* to
|
||||
*
|
||||
* <pre>
|
||||
* A
|
||||
*
|
||||
* B - C (orphan) - A' (master)
|
||||
* </pre>
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testRebaseRootCommit() throws Exception {
|
||||
writeTrashFile(FILE1, FILE1);
|
||||
writeTrashFile(FILE2, FILE2);
|
||||
git.add().addFilepattern(FILE1).addFilepattern(FILE2).call();
|
||||
RevCommit first = git.commit().setMessage("Add files").call();
|
||||
File file1 = new File(db.getWorkTree(), FILE1);
|
||||
File file2 = new File(db.getWorkTree(), FILE2);
|
||||
assertTrue(file1.exists());
|
||||
assertTrue(file2.exists());
|
||||
// Create an independent branch
|
||||
git.checkout().setOrphan(true).setName("orphan").call();
|
||||
git.rm().addFilepattern(FILE1).addFilepattern(FILE2).call();
|
||||
assertFalse(file1.exists());
|
||||
assertFalse(file2.exists());
|
||||
writeTrashFile(FILE1, "something else");
|
||||
git.add().addFilepattern(FILE1).call();
|
||||
RevCommit orphanBase = git.commit().setMessage("Orphan base").call();
|
||||
writeTrashFile(FILE1, FILE1);
|
||||
git.add().addFilepattern(FILE1).call();
|
||||
RevCommit orphanTop = git.commit().setMessage("Same file1").call();
|
||||
checkoutBranch("refs/heads/master");
|
||||
assertEquals(first.getId(), db.resolve("HEAD"));
|
||||
RebaseResult res = git.rebase().setUpstream("refs/heads/orphan").call();
|
||||
assertEquals(Status.OK, res.getStatus());
|
||||
Iterable<RevCommit> log = git.log().add(db.resolve("HEAD")).call();
|
||||
ObjectId[] ids = { orphanTop.getId(), orphanBase.getId() };
|
||||
int nOfCommits = 0;
|
||||
for (RevCommit c : log) {
|
||||
nOfCommits++;
|
||||
if (nOfCommits == 1) {
|
||||
assertEquals("Add files", c.getFullMessage());
|
||||
} else {
|
||||
assertEquals(ids[nOfCommits - 2], c.getId());
|
||||
}
|
||||
}
|
||||
assertEquals(3, nOfCommits);
|
||||
assertTrue(file1.exists());
|
||||
checkFile(file1, FILE1);
|
||||
assertTrue(file2.exists());
|
||||
checkFile(file2, FILE2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebase a branch onto an independent branch.
|
||||
*
|
||||
* <pre>
|
||||
* A - B (master)
|
||||
*
|
||||
* C - D (orphan)
|
||||
* </pre>
|
||||
*
|
||||
* to
|
||||
*
|
||||
* <pre>
|
||||
* A - B
|
||||
*
|
||||
* C - D (orphan) - A' - B' (master)
|
||||
* </pre>
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testRebaseNoMergeBase() throws Exception {
|
||||
writeTrashFile(FILE1, FILE1);
|
||||
writeTrashFile(FILE2, FILE2);
|
||||
git.add().addFilepattern(FILE1).addFilepattern(FILE2).call();
|
||||
git.commit().setMessage("Add files").call();
|
||||
writeTrashFile(FILE3, FILE3);
|
||||
git.add().addFilepattern(FILE3).call();
|
||||
RevCommit first = git.commit().setMessage("File3").call();
|
||||
File file1 = new File(db.getWorkTree(), FILE1);
|
||||
File file2 = new File(db.getWorkTree(), FILE2);
|
||||
File file3 = new File(db.getWorkTree(), FILE3);
|
||||
assertTrue(file1.exists());
|
||||
assertTrue(file2.exists());
|
||||
assertTrue(file3.exists());
|
||||
// Create an independent branch
|
||||
git.checkout().setOrphan(true).setName("orphan").call();
|
||||
git.rm()
|
||||
.addFilepattern(FILE1)
|
||||
.addFilepattern(FILE2)
|
||||
.addFilepattern(FILE3)
|
||||
.call();
|
||||
assertFalse(file1.exists());
|
||||
assertFalse(file2.exists());
|
||||
assertFalse(file3.exists());
|
||||
writeTrashFile(FILE1, "something else");
|
||||
git.add().addFilepattern(FILE1).call();
|
||||
RevCommit orphanBase = git.commit().setMessage("Orphan base").call();
|
||||
writeTrashFile(FILE1, FILE1);
|
||||
git.add().addFilepattern(FILE1).call();
|
||||
RevCommit orphanTop = git.commit().setMessage("Same file1").call();
|
||||
checkoutBranch("refs/heads/master");
|
||||
assertEquals(first.getId(), db.resolve("HEAD"));
|
||||
RebaseResult res = git.rebase().setUpstream("refs/heads/orphan").call();
|
||||
assertEquals(Status.OK, res.getStatus());
|
||||
Iterable<RevCommit> log = git.log().add(db.resolve("HEAD")).call();
|
||||
String[] msgs = { "File3", "Add files" };
|
||||
ObjectId[] ids = { orphanTop.getId(), orphanBase.getId() };
|
||||
int nOfCommits = 0;
|
||||
for (RevCommit c : log) {
|
||||
nOfCommits++;
|
||||
if (nOfCommits <= msgs.length) {
|
||||
assertEquals(msgs[nOfCommits - 1], c.getFullMessage());
|
||||
} else {
|
||||
assertEquals(ids[nOfCommits - msgs.length - 1], c.getId());
|
||||
}
|
||||
}
|
||||
assertEquals(4, nOfCommits);
|
||||
assertTrue(file1.exists());
|
||||
checkFile(file1, FILE1);
|
||||
assertTrue(file2.exists());
|
||||
checkFile(file2, FILE2);
|
||||
assertTrue(file3.exists());
|
||||
checkFile(file3, FILE3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the following commits and then attempt to rebase topic onto
|
||||
* master. This will serialize the branches.
|
||||
|
|
|
@ -1217,7 +1217,7 @@ private List<RevCommit> calculatePickList(RevCommit headCommit)
|
|||
Iterator<RevCommit> commitsToUse = r.iterator();
|
||||
while (commitsToUse.hasNext()) {
|
||||
RevCommit commit = commitsToUse.next();
|
||||
if (preserveMerges || commit.getParentCount() == 1) {
|
||||
if (preserveMerges || commit.getParentCount() <= 1) {
|
||||
cherryPickList.add(commit);
|
||||
}
|
||||
}
|
||||
|
@ -1234,23 +1234,31 @@ private List<RevCommit> calculatePickList(RevCommit headCommit)
|
|||
walk.markStart(upstreamCommit);
|
||||
walk.markStart(headCommit);
|
||||
RevCommit base;
|
||||
while ((base = walk.next()) != null)
|
||||
while ((base = walk.next()) != null) {
|
||||
RebaseState.createFile(rewrittenDir, base.getName(),
|
||||
upstreamCommit.getName());
|
||||
|
||||
}
|
||||
Iterator<RevCommit> iterator = cherryPickList.iterator();
|
||||
pickLoop: while(iterator.hasNext()){
|
||||
RevCommit commit = iterator.next();
|
||||
for (int i = 0; i < commit.getParentCount(); i++) {
|
||||
boolean parentRewritten = new File(rewrittenDir, commit
|
||||
.getParent(i).getName()).exists();
|
||||
if (parentRewritten) {
|
||||
new File(rewrittenDir, commit.getName()).createNewFile();
|
||||
continue pickLoop;
|
||||
int nOfParents = commit.getParentCount();
|
||||
if (nOfParents == 0) {
|
||||
// Must be the very first commit in the cherryPickList. We
|
||||
// have independent branches.
|
||||
new File(rewrittenDir, commit.getName()).createNewFile();
|
||||
} else {
|
||||
for (int i = 0; i < nOfParents; i++) {
|
||||
boolean parentRewritten = new File(rewrittenDir,
|
||||
commit.getParent(i).getName()).exists();
|
||||
if (parentRewritten) {
|
||||
new File(rewrittenDir, commit.getName())
|
||||
.createNewFile();
|
||||
continue pickLoop;
|
||||
}
|
||||
}
|
||||
// commit is only merged in, needs not be rewritten
|
||||
iterator.remove();
|
||||
}
|
||||
// commit is only merged in, needs not be rewritten
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
return cherryPickList;
|
||||
|
|
Loading…
Reference in New Issue