Usually you simply try to avoid this, but of course sometimes you can't. A simple alternative is if the later tasks might alter the architecture of the first task, then you could work on them together as one larger feature and merge them together in a single Code Review. Or even break it up into 3 smaller merges after you're done with all 3 tasks. But assuming neither of those are good options for you, let's proceed with your scenario as asked.
The general algorithm goes something like this:
- Base
task2
off of task1
, and base task3
off of task2
.
- Monitor
task2
, task1
, and main
and rewrite higher numbered branches when anything changes.
#1 is simple, and #2 is usually what people stress out about, particularly when the branches are worked on by different people, or when any branch other than main
is shared by multiple people. The reality though, is that "rewriting" a branch is relatively simple to do; it's communicating the change properly and instructing others what to do about it that's more difficult. In your case that may not be a problem at all if you're the one working on all 3 tasks, because you can rebase (rewrite) all 3 branches whenever you feel like it without messing up anyone else. As an added bonus, Git version 2.38 added a new option called rebase --update-refs which enables you to quickly rewrite all of the branches in one command.
Here's an example of how it could work with the new --update-refs
option.
Sneak Preview: You're going to make all of your changes on the task3
branch!
Let's assume each task branch is 2 commits ahead of the previous task branch:
A(main)-B-C(task1)-D-E(task2)-F-G(task3)
During the code review of task1
, someone recommends a change. Since you are the owner of all three task branches, you can do all of your changes to any of them while having task3
checked out. So make your fix on task3
in commit X
:
A(main)-B-C(task1)-D-E(task2)-F-G-X(task3)
Now you are going to do an interactive rebase (-i
) in conjunction with the new option (--update-refs
):
git rebase -i --update-refs main task3 # You can remove "task3" if it's already checked out
You'll be presented with a file in an editor showing the commits in reverse order, so X
will be at the bottom. You'll also see the commands that will update the other branches for you, for example something like:
pick 655be8a Commit B
pick 5a07e2f Commit C
update-ref refs/heads/task1
pick 40564e0 Commit D
pick 2706e8c Commit E
update-ref refs/heads/task2
pick 43b4c59 Commit F
pick 4945a1b Commit G
pick c09f14a Fix Commit B # Note this is Commit X
Now move commit X
up to somewhere between commit B
and the update-ref
command for task1
. Perhaps you want to add it as an additional commit, or squash it into another commit if it's just a minor tweak:
pick 655be8a Commit B
fixup c09f14a Fix Commit B # Note this is Commit X
pick 5a07e2f Commit C
update-ref refs/heads/task1
pick 40564e0 Commit D
pick 2706e8c Commit E
update-ref refs/heads/task2
pick 43b4c59 Commit F
pick 4945a1b Commit G
Now save the file and close it, and the rebase will begin. When it's finished it will have re-written all 3 branches, and then you can force push out all of the branches that are being reviewed. Here's a related question that talks about pushing multiple branches after using rebase --update-refs
.
Note: This answer assumed you were the only one working on all 3 branches. If that's not the case, you'll need to let others know that you force-pushed these branches so they can either delete their local copy and re-check it out, or do a hard reset to the latest version, or if they have unshared commits they can rebase their changes onto the latest version.