Reputation: 595
I am rebasing one branch feature
on another branch master
. During the rebase some files are auto-merged in a way that causes compilation errors when the rebase is complete. I fix these errors on the now-rebased feature
branch and thus have a set if changed files.
Ideally I would like the commit log to only hold commits from master
, then commits from feature
(and no other commits), but the changes have to go somewhere.
What is your "best practice" of handling such changes resulting from rebasing? Do you put them in a separate commit on feature
? Squash them into an existing commit on feature
(I'm going to force push anyway)?
Or is there a way to avoid this problem altogether? E.g., is there a way that I can stop the rebasing after the last commit from feature
is applied, but before git registers the rebasing as completed? If there is I could build and fix at this time and then do a final git rebase --continue
Upvotes: 0
Views: 5476
Reputation: 487893
Remember that rebase works by copying commits. The original commits remain unchanged. For instance, suppose you have the following series of commits:
...--G--H--K--L <-- master
\
I--J <-- feature
Here, Alice made commits I
and J
one after the other atop commit H
(each of these letters stands in for some big ugly Git hash ID). Around the same time (shortly before and/or afterward), Bob made commits K
and L
atop master.
Someone—the best case is usually that this "someone" is Alice herself—is now given the job of coming up with some new and improved commits, which we will call I'
and J'
, that are a great deal like commits I
and J
but which:
K
-and-L
;K
and L
; andAlice can run:
git checkout feature
git rebase master
and Git will do its best to make new commits I'
and J'
on its own:
I'-J' <-- feature
/
...--G--H--K--L <-- master
\
I--J [abandoned]
If Git thinks it's going badly, Git will stop and demand that Alice fix any merge conflicts that Git has detected during this copying process, before Git moves the branch name feature
to locate commit J'
. But Git is not very smart, and if Git thinks everything is going smoothly, Git will just run right on ahead and finish the rebase, producing these copies that don't actually work.
If that's the case, it's Alice's job (or whoever is doing the rebase) to control Git. She can use git rebase -i
and replace one or all pick
commands with edit
commands instead, for instance. Git will then stop after each cherry-pick—remember that rebase's commit copying is a series of git cherry-pick
operations—and allow Alice to fix up the commit. She can build and test the system, locate any bugs, make some edits, and run git add
and git commit --amend
as necessary to repair them, replacing, e.g.:
I' <-- HEAD
/
...--G--H--K--L <-- master
\
I--J <-- feature
with:
I' [abandoned]
/
| I" <-- HEAD
|/
...--G--H--K--L <-- master
\
I--J <-- feature
and then run git rebase --continue
to have Git go on to cherry-pick J
to make J'
and then stop again due to another edit
:
I' [abandoned]
/
| I"-J' <-- HEAD
|/
...--G--H--K--L <-- master
\
I--J <-- feature
Should it be necessary, Alice can now use git commit --amend
as before (along with the pre-commit steps) to replace J'
with J"
.
The final result is, presumably a working series of replacement commits. When the rebase is complete, Git moves the name feature
to point to the last of the copied commits:
I' [abandoned]
/
| I"-J' <-- feature
|/
...--G--H--K--L <-- master
\
I--J [abandoned]
Removing the abandoned (and invisible) commits from this picture, we now have a completely working updated feature
. The fact that the hash IDs of new copies I"
(modified from I'
to make it work) and J'
(which worked right off the bat after all) differ from those of the original I
and J
is equally invisible since no human ever notices these hash IDs.
(Note, however, that Git notices the hash IDs—in fact, those are what Git really cares about—so it's important that Alice and everyone else be careful not to reintroduce the original I
and J
commits. Only Alice has any temporary commits made during the rebase-and-edit procedure, so only Alice has to be careful not to reintroduce those, and it's unlikely that Alice will do so accidentally. But if commits I
and J
were given out to other Git users, and those other Git users have them now, those other Git users could easily reintroduce I
and J
accidentally.)
If you're Alice, you can:
git rebase -i
to pick specific commits to modify.You can also make "fixup" commits with git commit --fixup
, then use git rebase -i --autosquash
to have Git helpfully update the interactive rebase command sheet. This is a more advanced way to achieve the same result as above.
(Note that it's easy to miss a bad intermediate commit this way, if the final commit somehow fixes it up.)
If you have automated tests that will find these problems—this can be as simple as a failed compilation, perhaps—consider adding -x command
to a rebase. This uses the interactive machinery—you can use git rebase -i
here, in other words—to run a command after each pick
(or pick-and-fixup etc; see the documentation for details). If the supplied command fails, the rebase will stop automatically. If not, it will go on to the next pick
.
Having good automated tests is extremely helpful here.
If the tests are very slow, consider doing the full rebase followed by a single test, and if that fails, using git bisect
to find the failure point.
Upvotes: 3