I did the following:

git checkout -b v1
<made some changes in the working directory, created commit c1 and push>
git checkout -b v2
<made some changes in the working directory, created commit c2 and push>
git checkout -b v3
<made some changes in the working directory, created commit c3 and push>
git checkout -b v4
<made some changes in the working directory, created commit c4 and push>

So now I have the following git tree structure:

M --- v1 --- v2 --- v3 --- v4
.
.

I made some more changes on v1 and did the following:

git checkout v1
<made some changes on working directory>
git add .
git commit --amend
git push origin v1 --force

Now when I check out on v4 I do not see the changes to commit c1 I pushed on v1... Where are the changes and how do I fix this mess?

2 Answers

3
Lasse Vågsæther Karlsen On Best Solutions

Here's what happened.

You did what you did and had this:

M --- v1 --- v2 --- v3 --- v4

Actually, this is not entirely correct, you had this:

c1 --- c2 --- c3 --- c4 --- c5
^      ^      ^      ^      ^
M      v1     v2     v3     v4

You have 5 commits, and 5 branches that each refer to their respective commit.

Then you checked out v1 and amended that commit, which in turn gave you this picture:

c1 --- c2 --- c3 --- c4 --- c5
^  \          ^      ^      ^
M   \         v2     v3     v4
     c2'
     ^
     v1

Amending a commit does not change other branches or tags or commits that still refer to the old commit. It just creates a new commit and points the branch to this one.

What you have to do is rebase v2 on top of the new v1, then v3 on top of the new v2, and finally v4 on top of the new v3.

Step by step this would be the results of rebasing v2 on top of v3:

c1 --- c2 --- c3 --- c4 --- c5
^  \                 ^      ^
M   \                v3     v4
     c2' --- c3'
     ^       ^
     v1      v2

As you can see here, c3 is kept because c4 still refers to it and thus this commit will still be a part of both v3 and v4.

Finally, after rebasing both v3 and v4 you're left with:

c1 ( --- c2 --- c3 --- c4 --- c5 )
^  \                         
M   \                         
     c2' --- c3' --- c4' --- c5'
     ^       ^       ^       ^
     v1      v2      v3      v4

Note that on disk, c2-c5 still exist, but since no branch or tag or other commits holds in the end of this line of commits, they're now not visible in the log, and they're eligible for garbage collection, which happens now and then. Once GC has executed, this is what you're left with:

c1 
^  \                         
M   \                         
     c2' --- c3' --- c4' --- c5'
     ^       ^       ^       ^
     v1      v2      v3      v4

which of course is the same as this:

c1 --- c2' --- c3' --- c4' --- c5'
^      ^       ^       ^       ^
M      v1      v2      v3      v4
1
rjmunro On

A git branch contains all the history going back to the start of the repository, not only the history to the previous branch. Amending or rebasing a branch will only affect the history of that branch. Try running gitk --all to see what is going on.

You can use a series of rebases to fix the problem:

git rebase [sha of old v1] v2 --onto v1
git rebase [sha of old v2] v3 --onto v2
git rebase [sha of old v3] v4 --onto v3

But in general you don't need to keep the v1 v2 and v3 branches as all of their data is in v4. In future, if you wish to amend a commit from earlier in the history, use git rebase -i and amend it that way.