In a game project I am working on (small team of about 10), we have the common Git workflow of having a development branch (the main branch) and then several feature branches, which once completed and pass code review, get merged back to the development branch.
One issue we are facing though, is how to proceed with the next feature (that depends on the last feature) when the last feature hasn't yet passed code review and so is not merged back into development.
For example.
Lets say I have 3 tasks to do. Where each task builds on the code of the previous task (they can't be worked on independently)
To keep it simple, the tasks might be something like :
[Task1] - Add player code [Task2] - Add player code extensions (needs code from Task1) [Task3] - Add more player code bits (needs code from Task2)
So I :
Create a feature branch from development for [Task1] Work on [Task1] until completion Submit a pull request for [Task1] (lets say it takes a few days for code review)
Now I need to work on [Task2], but [Task2] builds on what I did in [Task1]. I can't create my feature branch from development as the [Task1] code has not been merged yet (as it's still in code review)
Should I :
a) Create the [Task2] feature branch from development and merge the [Task1] feature branch into it
or
b) Create the [Task2] feature branch from the [Task1] feature branch
Whilst the above might be manageable, it all gets really messy when you now have to work on [Task3], yet [Task1] is still being code reviewed.
Not to mention that if [Task1] has code review changes, they now have to be merged to [Task2] and then to [Task3] etc
What are my options in this scenario?
CodePudding user response:
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 oftask1
, and basetask3
off oftask2
. - Monitor
task2
,task1
, andmain
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.