Reputation: 1023
After amending some commit messages in a submodule I figured I should also update the parent repositories' commits to repair the broken references.
So I did an interactive rebase on the parent repository. I wanted to amend two commits in the parent repository that refer to two other commits in the submodule, like this:
---A---B---C---D--- parent | | v v ---a---b--- sub
Therefore I do git rebase -i A^
and choose to edit A and B:
edit A ... edit B ... pick C ... pick D ...
Now I am at commit A. To restore the correct submodule commit ID I first check the relevant submodule commit out and then amend the commit in the parent repository.
$ cd sub/
$ git checkout a
$ cd ..
$ git add sub
$ git commit --amend
Seems good, so I continue to the next one:
$ git rebase --continue
Now it complains it could not merge. Here my translation from a localized message:
Error merging submodule sub (Commits do not follow any merge base) automatic merge of sub CONFLICT (submodule): Merge conflict in sub error: Could not apply B (B's commit message) Resolve all conflicts manually, mark them as resolved with "git add/rm ", then run "git rebase --continue". You can instead skip this commit: run "git rebase --skip". To abort and get back to the state before "git rebase", run "git rebase --abort". Could not apply B (B's commit message)
Fair enough. The second commit has lost its connection to the first one because of the changed submodule ID in the first one. git status
says changed by both: sub
. Unswervingly, I try to amend once again:
$ cd sub/
$ git checkout b
$ cd ..
$ git add sub
$ git commit --amend
Unfortunately this does not do what I want. The reason is that the erroneous git rebase --continue
somehow ate commit B completely. B's changes were put in the staging area but the commit is gone. Therefore the second git commit --amend
will amend A and put all changes from B into A! Now I guess I could create a new commit at that point, replicating B. But to get it exactly as B would be rather complicated.
How do I amend submodule references correctly? Why and under which circumstances does rebasing destroy commits like that?
Upvotes: 0
Views: 132
Reputation: 488453
TL;DR: Do the use git commit
without --amend
git rebase --continue
, which will do the git commit
without --amend
.
Let's take your original diagram, minus the connections to the submodule (since they're flimsy and get in the way :-) ):
...--o--A--B--C--D <-- branch
You run git rebase -i A^
and change A
to edit
(and so on). Rebase cherry-picks A
to a new copy A'
:
...--o--A--B--C--D <-- branch
\
A' <-- HEAD
You make your update, git add
, and git commit --amend
to make new A"
, leaving A'
behind as abandoned:
A" <-- HEAD
/
...--o--A--B--C--D <-- branch
\
A'
Now, regardless of what you put in for instructions for B
, Git tries to copy B
but fails. So the rebase stops at:
A" <-- HEAD
/
...--o--A--B--C--D <-- branch
\
A'
with some uncommitted changes in the index and work-tree. There is no commit B'
yet. There are two things you do can do now. One is to make it yourself:
<fix the submodule>
git add sub
git commit
This creates B'
:
A"-B' <-- HEAD
/
...--o--A--B--C--D <-- branch
\
A'
and now you can git rebase --continue
to have Git go on to the next part of the instructions (hence you don't need to ask to edit B
originally, you can just go on to copy C
to C'
now: if Git now stops to let you edit B'
you can just tell it to continue).
The other—since git commit
now requires that you type in a commit message—is to set everything up, and instead of git commit
, run git rebase --continue
to make Git make B'
:
<fix the submodule>
git add sub
git rebase --continue
The --continue
step notices that all conflicts are fixed (or complains and stops if they're not), and then does the git commit
and goes on, exactly as if you'd made the commit yourself.
You need edit
as an instruction to make rebase stop and let you --amend
a cherry-pick that will succeed. You don't need edit
for a commit where the cherry-pick will fail, as Git will stop without having made the commit in the first place.
It's hard to know in advance—in some cases, it might as well be impossible—to know which cherry-picks are going to succeed and which are going to fail. So you wind up having to pay a lot of attention to the exact messages that come out just before git rebase
stops to let you do something: did it stop because the cherry-pick failed, or did it stop because you told it to stop after the cherry-pick succeeded? In other words, is there a commit that you should --amend
now, or should you (have Git) make a new commit now?
(I get tripped up by this myself. I find it pretty annoying.)
Upvotes: 1