If I have a branch of single-parent commits; how can I pick a contiguous region of commits from this branch, and add the squash of them to another branch? I do not want to change the original branch (and assume each commit has only one parent).

Here's an example: I want to pick C, D, and E, squash them, and place them onto Z. Then pick F and G, squash them, and place them onto the head of Z. We start with the tree below:

A-----B------C------D------E------F------G------H
 \
  \
   Z

And obtain this result:

A-----B------C------D------E------F------G------H
 \
  \
   Z------Y------X

Where:

Y is the squashed commit of C, D, and E

X is the squashed commit of F and G.

I want to avoid the intermediate result below:

A-----B------C------D------E------F------G------H
 \
  \
   Z------C------D------E------F------G

Is there a direct way to do this in JGit? I have been looking at MergeCommand, CherryPickCommand, and RebaseCommand, but I am not sure how to do this while avoiding the intermediate result. I cannot find many JGit examples either. Any suggestions would be helpful.

有帮助吗?

解决方案

I found a solution using the CherryPickCommand and RebaseCommand. Using the example above, I switch to branch at Z.

A-----B------C------D------E------F------G------H
 \
  \
   Z

Then, I cherry pick the RevCommits C, D, and E using .include(RevCommit) for each commit. Then I use .call() to add the commits onto my new branch, resulting in the diagram below:

A-----B------C------D------E------F------G------H
 \
  \
   Z------C------D------E

Next, I squash C, D, and E into a single commit with RebaseCommand. First, I set the upstream commi with .setUpstream("HEAD~3") to move back 3 commits. Then I run the interactive handler using a callback function shown below:

runInteractively(new InteractiveHandler() {
                    @Override
                    public String modifyCommitMessage(String commit)
                    {
                        return rebaseMessage;
                    }
                    @Override
                    public void prepareSteps(List<RebaseTodoLine> steps)
                    {
                        try
                        {
                            steps.get(0).setAction(RebaseTodoLine.Action.PICK);
                            for (int j = 1; j < steps.size(); j++)
                            {
                                RebaseTodoLine step = steps.get(j); 
                                step.setAction(RebaseTodoLine.Action.SQUASH);
                            }
                        }
                        catch (IllegalTodoFileModification e)
                        {
                            System.out.println("Cannot prepare step: " + e);
                            e.printStackTrace();
                        }
                    }
                });

This effectively gives instructions to the interactive command line prompt that you see in normal git rebase operations. Now, my repo looks as follows:

A-----B------C------D------E------F------G------H
 \
  \
   Z------Y

Where:

Y is the squashed commit of C, D, and E

And that's it!

To finish the example, I would do the same for commits F and G.

If there is a direct method to add and squash the commits in one move, I would be very interested. For now, this works and avoids duplication of the entire branch.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top