0

Not a duplicate!

All the vanilla rebase examples an fancy diagrams I see show how to rebase a branch onto the TOP of another branch/commit. Is it possible, let's say, to move branch A onto a concrete intermediate place on branch B?

        O--O--O Branch B             O--0--0--O--O Branch B
       /                            /  
O--O--O--0--0   Branch A     O--O--O

EDIT: I am not looking for rebasing into a arbitrary commit, but rebasing in the middle of a branch, connecting both ends

Whimusical
  • 6,401
  • 11
  • 62
  • 105
  • of course it is, you can checkout a commit, make a branch there and then rebase – Dunno Jan 25 '18 at 13:37
  • 1
    Possible duplicate of [Git: How to rebase to a specific commit?](https://stackoverflow.com/questions/7744049/git-how-to-rebase-to-a-specific-commit) – Dunno Jan 25 '18 at 13:38
  • @Dunno it's not the same case. In the example of your link, my case would be equivalent to place D between B and C – Whimusical Jan 25 '18 at 13:40
  • You can rebase on top of any commit. – axiac Jan 25 '18 at 13:40
  • @axiac I know, but what I want is not a "rebase on top of a particular commit" but a "rebase between 2 commits of another branch" – Whimusical Jan 25 '18 at 13:41
  • There is nothing between two commits. – axiac Jan 25 '18 at 13:52
  • @axiac - What does that even mean? There's nothing between two books on my bookshelf, until I put another book in between them. – Mark Adelsberger Jan 25 '18 at 14:14
  • @MarkAdelsberger is each book on your shelf tied on the previous one? – axiac Jan 25 '18 at 14:18
  • @axiac - Oh, so you mean to say that you *can't* put anything between two adjacent commits. Well that's not what you said, and once we're talking about history rewrites (e.g. rebasing) it's not practically true anyway. – Mark Adelsberger Jan 25 '18 at 14:22
  • @MarkAdelsberger "rebasing" means changing the base. The question is poorly formulated and asks how to not rebase on top of a new commit but on top of whatever is between two commits. I understand what the OP really asks only after I carefully investigated the drawings. – axiac Jan 25 '18 at 14:29
  • @axiac - Well, sorry you didn't find it clear. I did, but that's not what we were discussing. What I found unclear was your statement about there being "nothing between two commits", which is why I asked about it. – Mark Adelsberger Jan 25 '18 at 14:45

4 Answers4

3

You're asking to take the commits from one branch and place them in the middle of another branch. (This phrasing doesn't really line up with how git "thinks" about the relationship of branches and commits, but I think what you mean is clear, so ok...)

FROM:

x -- x -- A -- B -- C -- F -- G <--(branch_1)
      \
       D -- E <--(branch_2)

TO:

x -- x -- A -- B -- C -- D' -- E' -- F' -- G' <--(branch_1)

(It's not clear where you want the branch_2 ref to end up - or if you'd just want to delete it - but getting the commits right is the bigger issue, and you can work out the refs afterward.)

Important side note: In this diagram commits D onward are replaced with commits D' onward; this is because a commit cannot be "moved" or changed in any way, so what we do is create new commits. This means that commits D, E, F, and G (the original versions) are removed from the histories of your branches, which can cause issues if the branches have been pushed. Consult the git rebase documentation (https://git-scm.com/docs/git-rebase) for more information about the problems that can result and how to fix those problems; be aware that if these issues are left unaddressed, it is likely that another developer will unintentionally undo your work.

That said, there are multiple ways to go about it; generally you'll need two rebase operations.

One of the more straightforward ways to think about it would be, just do the rebase normally and then do an interactive rebase to change the commit order.

If you want something more scriptable, you could start with

git rebase --onto branch_1~2 branch_1 branch_2

Here the --onto option specifies a new base other than the default (the default being "the tip of the upstream"). Because I'm inserting the commits in this example after the parent of the parent of branch_1, I can use branch_1~2 or branch_1^^ as the new base expression. The commits to be rewritten are specified in the usual way (in this case "reachable from branch_2 but not reachable from branch_1). So you'd have

x -- x -- A -- B -- C -- F -- G <--(branch_1)
                     \
                      D' -- E' <--(branch_2)

and now you cold simply rebase branch_1 to the end of branch_2 in the normal way.

git rebase branch_2 branch_1

yielding

x -- x -- A -- B -- C -- D' -- E' -- F' -- G' <--(branch_1)
                                ^(branch_2)
Mark Adelsberger
  • 42,148
  • 4
  • 35
  • 52
3

Your question is ambiguous, fortunately the drawing sheds some light about what you want.

If I understand it correctly, this is the input status of the repo

        M--N--P   <-- Branch B
       /
O--O--X--Y--Z     <-- Branch A

and this is what you want to achieve. I used letters to denote the commits.

        M--Y--Z--N--P   <-- Branch B
       /
O--O--X

First you have to create a new branch (C) that points to M to keep it visible until the end of the operation

git branch C B~2

Replace B~2 with the actual number of commits you have from N to B (including both) or use absolute references (existing branches or commit hashes).

The following command moves N-P on top of Z. It moves all the commits that are in B but are not in C on top of A

# Move N-P on top of Z (A points to Z, B points to P, C points to M)
git rebase --onto A C B

The last step is to move all commits that are in B but are not in C on top of C:

# Move Y-Z-N-P on top of M (B still points to P, A still points to Z)
git rebase C B

The repo now look like this:

        +-- Branch C
        v
        M--Y'-Z'-N'-P'  <-- Branch B
       /
O--O--X--Y--Z           <-- Branch A

Branch A still points to the original Z commit; also the original Y commit is still in place. The current branch is B.

Commits Y', Z',N' and P' are copies of Y, Z, N and P. They differ from the originals only on the commit date and parent; the are no code changes.

The branches A and C can be removed now. (git branch -D A C).

Read more about git rebase.

axiac
  • 68,258
  • 9
  • 99
  • 134
0

Yes it is possible, you need to first

git checkout <commit>

Then rebase your branch and then push to origin.

Mike Tung
  • 4,735
  • 1
  • 17
  • 24
  • Thanks for the answer! Is this gonna show like the second picture? Am I gonna loose the last Branch B commits or something? – Whimusical Jan 25 '18 at 13:38
  • In your picture it would be two rebases, one from A to B so you get the figure to the right (top part) then a second one from B to A to make the connect – Mike Tung Jan 25 '18 at 13:40
  • I am not still sure this is exactly my case, sorry. Looks like everybody just assumed I want the branch sticked to the other, not integrated – Whimusical Jan 25 '18 at 13:47
  • More fundamental than whether this is the operation Whimusical specified, it doesn't appear to be correct. What commit is checked out when you do a rebase does not determine the new base for the rewritten commits in the way this would suggest. (It does provide a default for what should be the last rewritten commit, but I almost never use that fact.) – Mark Adelsberger Jan 25 '18 at 14:13
0

I would first rebase normally branch A onto branch B. Then I would use git rebase -i HEAD~n (where n is number of commits for interactive edit) and make rearrangement of the commits (by simply changing order of lines in editor).