Bladeren bron

Add support for rebase interactive 'reword' command

'reword' command is used to change commit message of any
commit in git history.

Bug: 394575
Change-Id: Ic974e76dfd923fd6f0cb8f07d1a6fbecd9abbf31
Signed-off-by: Dariusz Luksza <dariusz@luksza.org>
Signed-off-by: Chris Aniszczyk <zx@twitter.com>
tags/v2.2.0.201212191850-r
Dariusz Luksza 11 jaren geleden
bovenliggende
commit
84fb2b59d1

+ 58
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java Bestand weergeven

import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.List; import java.util.List;


import org.eclipse.jgit.api.MergeResult.MergeStatus; import org.eclipse.jgit.api.MergeResult.MergeStatus;
import org.eclipse.jgit.api.RebaseCommand.Action; import org.eclipse.jgit.api.RebaseCommand.Action;
import org.eclipse.jgit.api.RebaseCommand.InteractiveHandler;
import org.eclipse.jgit.api.RebaseCommand.Operation; import org.eclipse.jgit.api.RebaseCommand.Operation;
import org.eclipse.jgit.api.RebaseCommand.Step; import org.eclipse.jgit.api.RebaseCommand.Step;
import org.eclipse.jgit.api.RebaseResult.Status; import org.eclipse.jgit.api.RebaseResult.Status;
assertEquals("2222222", steps.get(1).commit.name()); assertEquals("2222222", steps.get(1).commit.name());
} }


@Test
public void testParseRewordCommand() throws Exception {
String todo = "pick 1111111 Commit 1\n"
+ "reword 2222222 Commit 2\n";
write(getTodoFile(), todo);

RebaseCommand rebaseCommand = git.rebase();
List<Step> steps = rebaseCommand.loadSteps();

assertEquals(2, steps.size());
assertEquals("1111111", steps.get(0).commit.name());
assertEquals("2222222", steps.get(1).commit.name());
assertEquals(Action.REWORD, steps.get(1).action);
}

@Test
public void testRebaseInteractiveReword() throws Exception {
// create file1 on master
writeTrashFile(FILE1, FILE1);
git.add().addFilepattern(FILE1).call();
git.commit().setMessage("Add file1").call();
assertTrue(new File(db.getWorkTree(), FILE1).exists());

// create file2 on master
writeTrashFile("file2", "file2");
git.add().addFilepattern("file2").call();
git.commit().setMessage("Add file2").call();
assertTrue(new File(db.getWorkTree(), "file2").exists());

// update FILE1 on master
writeTrashFile(FILE1, "blah");
git.add().addFilepattern(FILE1).call();
git.commit().setMessage("updated file1 on master").call();

writeTrashFile("file2", "more change");
git.add().addFilepattern("file2").call();
git.commit().setMessage("update file2 on side").call();

RebaseResult res = git.rebase().setUpstream("HEAD~2")
.runInteractively(new InteractiveHandler() {
public void prepareSteps(List<Step> steps) {
steps.get(0).action = Action.REWORD;
}
public String modifyCommitMessage(String commit) {
return "rewritten commit message";
}
}).call();
assertTrue(new File(db.getWorkTree(), "file2").exists());
checkFile(new File(db.getWorkTree(), "file2"), "more change");
assertEquals(Status.OK, res.getStatus());
Iterator<RevCommit> logIterator = git.log().all().call().iterator();
logIterator.next(); // skip first commit;
String actualCommitMag = logIterator.next().getShortMessage();
assertEquals("rewritten commit message", actualCommitMag);
}

private File getTodoFile() { private File getTodoFile() {
File todoFile = new File(db.getDirectory(), File todoFile = new File(db.getDirectory(),
"rebase-merge/git-rebase-todo"); "rebase-merge/git-rebase-todo");

+ 129
- 7
org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java Bestand weergeven



private final File rebaseDir; private final File rebaseDir;


private InteractiveHandler interactiveHandler;

/** /**
* @param repo * @param repo
*/ */
ObjectReader or = repo.newObjectReader(); ObjectReader or = repo.newObjectReader();


List<Step> steps = loadSteps(); List<Step> steps = loadSteps();
if (isInteractive()) {
interactiveHandler.prepareSteps(steps);
BufferedWriter fw = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(new File(
rebaseDir, GIT_REBASE_TODO)),
Constants.CHARACTER_ENCODING));
fw.newLine();
try {
StringBuilder sb = new StringBuilder();
for (Step step : steps) {
sb.setLength(0);
sb.append(step.action.token);
sb.append(" ");
sb.append(step.commit.name());
sb.append(" ");
sb.append(new String(step.shortMessage,
Constants.CHARACTER_ENCODING).trim());
fw.write(sb.toString());
fw.newLine();
}
} finally {
fw.close();
}
}
for (Step step : steps) { for (Step step : steps) {
popSteps(1); popSteps(1);
Collection<ObjectId> ids = or.resolve(step.commit); Collection<ObjectId> ids = or.resolve(step.commit);
newHead = cherryPickResult.getNewHead(); newHead = cherryPickResult.getNewHead();
} }
} }
switch (step.action) {
case PICK:
continue; // continue rebase process on pick command
case REWORD:
String oldMessage = commitToPick.getFullMessage();
String newMessage = interactiveHandler
.modifyCommitMessage(oldMessage);
newHead = new Git(repo).commit().setMessage(newMessage)
.setAmend(true).call();
continue;
}
} finally { } finally {
monitor.endTask(); monitor.endTask();
} }
String popCandidate = br.readLine(); String popCandidate = br.readLine();
if (popCandidate == null) if (popCandidate == null)
break; break;
if (popCandidate.length() == 0)
continue;
if (popCandidate.charAt(0) == '#') if (popCandidate.charAt(0) == '#')
continue; continue;
int spaceIndex = popCandidate.indexOf(' '); int spaceIndex = popCandidate.indexOf(' ');
RevCommit headCommit = walk.lookupCommit(headId); RevCommit headCommit = walk.lookupCommit(headId);
RevCommit upstream = walk.lookupCommit(upstreamCommit.getId()); RevCommit upstream = walk.lookupCommit(upstreamCommit.getId());


if (walk.isMergedInto(upstream, headCommit))
if (!isInteractive() && walk.isMergedInto(upstream, headCommit))
return RebaseResult.UP_TO_DATE_RESULT; return RebaseResult.UP_TO_DATE_RESULT;
else if (walk.isMergedInto(headCommit, upstream)) {
else if (!isInteractive() && walk.isMergedInto(headCommit, upstream)) {
// head is already merged into upstream, fast-foward // head is already merged into upstream, fast-foward
monitor.beginTask(MessageFormat.format( monitor.beginTask(MessageFormat.format(
JGitText.get().resettingHead, JGitText.get().resettingHead,
return null; return null;
} }


private boolean isInteractive() {
return interactiveHandler != null;
}

/** /**
* checks if we can fast-forward and returns the new head if it is possible * checks if we can fast-forward and returns the new head if it is possible
* *
return this; return this;
} }


static enum Action {
PICK("pick"); // later add SQUASH, EDIT, etc.
/**
* Enables interactive rebase
*
* @param handler
* @return this
*/
public RebaseCommand runInteractively(InteractiveHandler handler) {
this.interactiveHandler = handler;
return this;
}

/**
* Allows configure rebase interactive process and modify commit message
*/
public interface InteractiveHandler {
/**
* Given list of {@code steps} should be modified according to user
* rebase configuration
* @param steps
* initial configuration of rebase interactive
*/
void prepareSteps(List<Step> steps);

/**
* Used for editing commit message on REWORD
*
* @param commit
* @return new commit message
*/
String modifyCommitMessage(String commit);
}

/**
* Describes rebase actions
*/
public static enum Action {
/** Use commit */
PICK("pick", "p"),
/** Use commit, but edit the commit message */
REWORD("reword", "r"); // later add SQUASH, EDIT, etc.


private final String token; private final String token;


private Action(String token) {
private final String shortToken;

private Action(String token, String shortToken) {
this.token = token; this.token = token;
this.shortToken = shortToken;
} }


/**
* @return full action token name
*/
public String toToken() { public String toToken() {
return this.token; return this.token;
} }
} }


static Action parse(String token) { static Action parse(String token) {
if (token.equals("pick") || token.equals("p"))
if (token.equals(PICK.token) || token.equals(PICK.shortToken))
return PICK; return PICK;
if (token.equals(REWORD.token) || token.equals(REWORD.shortToken))
return REWORD;
throw new JGitInternalException(MessageFormat.format( throw new JGitInternalException(MessageFormat.format(
JGitText.get().unknownOrUnsupportedCommand, token, JGitText.get().unknownOrUnsupportedCommand, token,
PICK.toToken())); PICK.toToken()));
} }
} }


static class Step {
/**
* Describes single rebase step
*/
public static class Step {
Action action; Action action;


AbbreviatedObjectId commit; AbbreviatedObjectId commit;
this.action = action; this.action = action;
} }


/**
* @return rebase action type
*/
public Action getAction() {
return action;
}

/**
* @param action
*/
public void setAction(Action action) {
this.action = action;
}

/**
* @return abbreviated commit SHA-1 of commit that action will be
* performed on
*/
public AbbreviatedObjectId getCommit() {
return commit;
}

/**
* @return short message commit of commit that action will be performed
* on
*/
public byte[] getShortMessage() {
return shortMessage;
}

@Override @Override
public String toString() { public String toString() {
return "Step[" + action + ", " return "Step[" + action + ", "

Laden…
Annuleren
Opslaan