Let's imagine I have this git graph:
(fig 1)
* 9e5d9b3 - (HEAD -> branch10) Commit 10
* 3e0a94e - (branch9) Commit 9
* 8ff2ab4 - (branch8) Commit 8
* 2a4b9ec - (branch7) Commit 7
* f075e86 - (branch6) Commit 6
* 2960e5a - (branch5) Commit 5
* 61bf766 - (branch4) Commit 4
* adb8403 - (branch3) Commit 3
* 1539f24 - (branch2) Commit 2
* fa81942 - (branch1) init
Now, imagine a messed up tree of the repository.
Basically, on this kind of tree, every branch has a new commit, but there is zero conflict (like, for example, it's only about adding new files).
(sorry, hard to read, but definitely what I might end up with sometimes)
(fig 2)
* b6336ed - (HEAD -> branch10) New file on branch10
* 9e5d9b3 - Commit 10
| * 26cc916 - (branch2) New file on branch2
| | * c0a3bdb - (branch3) New file on branch3
| | | * 147b2b3 - (branch4) New file on branch4
| | | | * f8d8009 - (branch5) New file on branch5
| | | | | * 707f527 - (branch6) New file on branch6
| | | | | | * b43c03a - (branch7) New file on branch7
| | | | | | | * 859801b - (branch8) New file on branch8
| | | | | | | | * 731e7a3 - (branch9) New file on branch9
| |_|_|_|_|_|_|/
|/| | | | | | |
* | | | | | | | 3e0a94e - Commit 9
| |_|_|_|_|_|/
|/| | | | | |
* | | | | | | 8ff2ab4 - Commit 8
| |_|_|_|_|/
|/| | | | |
* | | | | | 2a4b9ec - Commit 7
| |_|_|_|/
|/| | | |
* | | | | f075e86 - Commit 6
| |_|_|/
|/| | |
* | | | 2960e5a - Commit 5
| |_|/
|/| |
* | | 61bf766 - Commit 4
| |/
|/|
* | adb8403 - Commit 3
|/
* 1539f24 - Commit 2
* fa81942 - (branch1) init
I need to loop over all these branches by their order, and rebase everything.
I do something like this:
for i in {2..10}
do
prev=$((i-1))
git switch "branch${i}"
GIT_EDITOR=true git rebase "branch${prev}"
done
And then, I end up with a beautiful tree of everything rebased onto each previous branch:
(fig 3)
* cf3f06f - (HEAD -> branch10) New file on branch10
* 46bf70b - Commit 10
* b4b33b7 - (branch9) New file on branch9
* 25d40f0 - Commit 9
* a776a3e - (branch8) New file on branch8
* 2b8a707 - Commit 8
* 318010a - (branch7) New file on branch7
* 1dddc07 - Commit 7
* f26fe68 - (branch6) New file on branch6
* fba3235 - Commit 6
* ce4d5db - (branch5) New file on branch5
* b00a618 - Commit 5
* 2ba7ac1 - (branch4) New file on branch4
* 59d1d1d - Commit 4
* 105f4fa - (branch3) New file on branch3
* 7e1e4d0 - Commit 3
* ab12590 - (branch2) New file on branch2
* 3a23697 - Commit 2
* 6c7d585 - (branch1) init
Whenever I update one branch anywhere in the git tree, and the changes have conflicts, and I re-run the "rebase everything" command, I can end up with strange trees like that, especially when conflicts are solved in a way that I have to "rewrite" some things based on new information:
(fig 4)
* 4ec68c0 - (HEAD -> branch10) New file on branch10
* 126ef9b - Commit 10
* 95273fb - (branch9) New file on branch9
* b053b3b - Commit 9
* 9af7554 - (branch8) New file on branch8
* 9c9dbf4 - Commit 8
* a9656bb - (branch7) New file on branch7
* 71c8535 - Commit 7
* 7e763e0 - (branch6) New file on branch6
* 430a784 - Commit 6
* 8f224b6 - (branch5) New file on branch5
* 84c4439 - Commit 5
* a932c79 - Commit 4
* b521576 - Commit 3
* 4fd71bb - Commit 2
* 8eb2d76 - (branch4) New file on branch4
* e09c8bd - Commit 4
* a060b2c - (branch3) New file on branch3
* 1153c70 - Commit 3
* ceb580a - (branch2) New file on branch2
* 46b6902 - Commit 2
* 6c7d585 - (branch1) init
See the list of "Commit X" in branch5
: they correspond to a rebase on a previous branch where everytime I had to manually fix something, and it created a new commit in the tree.
What I'd like is to keep the "working case" where remnants of old commits references are squashed together with the first commit of the branch.
So in here, I'd like the tree to stay exactly the same as in fig 3
.
The only "workaround" I currently have is the following:
git switch branch5
git rebase --interactive branch4
# The "Interactive rebase" opens, and I do this:
# > Change "pick" into "squash" for commits that reference commits from previous branches, in this case, a932c79, b521576 and 4fd71bb
# > Save & exit
# Then the "Commit" editor opens, and I do this:
# > Remove the commit lines from the "old commits"
# > Save & exit
# Then sometimes, in case of conflicts:
git rebase --continue
# And if no conflicts, rebase is done
Once I run this workaround, I have to re-run the "rebase everything" command, and usually, the branches "after the problematic one" will suffer the exact same thing, so I have to do it by hand to ALL upcoming branches.
So, how can I make sure to run a proper chain-rebase like this, resolve conflicts manually if I need (I already have some code that "waits until conflict resolution is done" so it can run git rebase --continue
by itself), and end up with only the commits from the specific branches?
What I thought about (and haven't tried yet):
- Using
git reset && git cherry-pick
at some point, but the point is, this is quite annoying because I need to fetch the list of all commits that differ between the base and the current head, do it, check for conflicts manually like I'm doing now. Not impossible, but quite tricky too. - I also thought about using
git merge
all over the place, but the graph will be totally unreadable then, and debugging this will be a definite PITA.
(Oh, and side-note: I don't care about commit hashes, it's meant to be exactly like that)
What are your recommendations?
Any tips on fixing the rebase
process?
Should I change and use reset & pick
?
Should I give up and use git merge
, and say goodbye to the nice tree?
I just tested with merge
-ing everything when having conflicts.
Conflicts are still extremely annoying to handle, but there's less of them: maximum one per branch, compared to rebasing where there can be multiple commits impacted by the conflicting change.
And it turns out it's as ugly as I expected.
Here's an example with the "new file" thing from before, and afterwards, I introduced a conflicting commit in branch5
.
* 4be3807 - (HEAD -> branch10) Merge branch 'branch9' into branch10
|\
| * b7c9304 - (branch9) Merge branch 'branch8' into branch9
| |\
| | * f1a5d51 - (branch8) Merge branch 'branch7' into branch8
| | |\
| | | * 3cd0392 - (branch7) Merge branch 'branch6' into branch7
| | | |\
| | | | * 6aca8c5 - (branch6) Merge branch 'branch5' into branch6
| | | | |\
| | | | | * cfbb51d - (branch5) conflict with branch 5
* | | | | | 2965de0 - Merge branch 'branch9' into branch10
|\| | | | |
| * | | | | dd861d1 - Merge branch 'branch8' into branch9
| |\| | | |
| | * | | | c3cf079 - Merge branch 'branch7' into branch8
| | |\| | |
| | | * | | a432a77 - Merge branch 'branch6' into branch7
| | | |\| |
| | | | * | f8e6478 - Merge branch 'branch5' into branch6
| | | | |\|
| | | | | * 0e5a170 - Merge branch 'branch4' into branch5
| | | | | |\
| | | | | | * acc8d0b - (branch4) Merge branch 'branch3' into branch4
| | | | | | |\
| | | | | | | * 4a08149 - (branch3) Merge branch 'branch2' into branch3
| | | | | | | |\
| | | | | | | | * 8dde879 - (branch2) New file on branch2
| | | | | | | * | efad1d7 - New file on branch3
| | | | | | * | | 84e093b - New file on branch4
| | | | | * | | | 94291f6 - New file on branch5
| | | | * | | | | 2563af8 - New file on branch6
| | | * | | | | | 3ff40bf - New file on branch7
| | * | | | | | | fd5874d - New file on branch8
| * | | | | | | | f27bbe0 - New file on branch9
* | | | | | | | | afe9778 - New file on branch10
* | | | | | | | | e2f29df - Commit 10
|/ / / / / / / /
* / / / / / / / 3c37942 - Commit 9
|/ / / / / / /
* / / / / / / 9410c9d - Commit 8
|/ / / / / /
* / / / / / 1c45e66 - Commit 7
|/ / / / /
* / / / / 8a85594 - Commit 6
|/ / / /
* / / / 07083ff - Commit 5
|/ / /
* / / f0e50ea - Commit 4
|/ /
* / e6e5545 - Commit 3
|/
* 926b51f - Commit 2
* 332af2f - (branch1) init