]> source.dussan.org Git - jgit.git/commitdiff
Add support for rebase interactive 'reword' command 79/7779/6
authorDariusz Luksza <dariusz@luksza.org>
Wed, 3 Oct 2012 07:59:30 +0000 (09:59 +0200)
committerChris Aniszczyk <zx@twitter.com>
Mon, 19 Nov 2012 15:40:53 +0000 (09:40 -0600)
'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>
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/RebaseCommandTest.java
org.eclipse.jgit/src/org/eclipse/jgit/api/RebaseCommand.java

index 81730b93aa384013d5653074a37a50d8673ac2bf..07ce7606b185ba49b94995eec2f96277ce985f0a 100644 (file)
@@ -53,10 +53,12 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.util.Iterator;
 import java.util.List;
 
 import org.eclipse.jgit.api.MergeResult.MergeStatus;
 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.Step;
 import org.eclipse.jgit.api.RebaseResult.Status;
@@ -1523,6 +1525,62 @@ public class RebaseCommandTest extends RepositoryTestCase {
                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() {
                File todoFile = new File(db.getDirectory(),
                                "rebase-merge/git-rebase-todo");
index 6f87349e5b88cbd13b734159ae8696b61299fa7c..ece861f7f55c22fde63c5f2011d70d06a6c3fa3f 100644 (file)
@@ -177,6 +177,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
 
        private final File rebaseDir;
 
+       private InteractiveHandler interactiveHandler;
+
        /**
         * @param repo
         */
@@ -254,6 +256,30 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
                        ObjectReader or = repo.newObjectReader();
 
                        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) {
                                popSteps(1);
                                Collection<ObjectId> ids = or.resolve(step.commit);
@@ -295,6 +321,17 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
                                                        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 {
                                        monitor.endTask();
                                }
@@ -492,6 +529,8 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
                                String popCandidate = br.readLine();
                                if (popCandidate == null)
                                        break;
+                               if (popCandidate.length() == 0)
+                                       continue;
                                if (popCandidate.charAt(0) == '#')
                                        continue;
                                int spaceIndex = popCandidate.indexOf(' ');
@@ -564,9 +603,9 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
                RevCommit headCommit = walk.lookupCommit(headId);
                RevCommit upstream = walk.lookupCommit(upstreamCommit.getId());
 
-               if (walk.isMergedInto(upstream, headCommit))
+               if (!isInteractive() && walk.isMergedInto(upstream, headCommit))
                        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
                        monitor.beginTask(MessageFormat.format(
                                        JGitText.get().resettingHead,
@@ -647,6 +686,10 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
                return null;
        }
 
+       private boolean isInteractive() {
+               return interactiveHandler != null;
+       }
+
        /**
         * checks if we can fast-forward and returns the new head if it is possible
         *
@@ -988,15 +1031,59 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
                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 Action(String token) {
+               private final String shortToken;
+
+               private Action(String token, String shortToken) {
                        this.token = token;
+                       this.shortToken = shortToken;
                }
 
+               /**
+                * @return full action token name
+                */
                public String toToken() {
                        return this.token;
                }
@@ -1007,15 +1094,20 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
                }
 
                static Action parse(String token) {
-                       if (token.equals("pick") || token.equals("p"))
+                       if (token.equals(PICK.token) || token.equals(PICK.shortToken))
                                return PICK;
+                       if (token.equals(REWORD.token) || token.equals(REWORD.shortToken))
+                               return REWORD;
                        throw new JGitInternalException(MessageFormat.format(
                                        JGitText.get().unknownOrUnsupportedCommand, token,
                                        PICK.toToken()));
                }
        }
 
-       static class Step {
+       /**
+        * Describes single rebase step
+        */
+       public static class Step {
                Action action;
 
                AbbreviatedObjectId commit;
@@ -1026,6 +1118,36 @@ public class RebaseCommand extends GitCommand<RebaseResult> {
                        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
                public String toString() {
                        return "Step[" + action + ", "