I had lines like this to change:
service.audit(Message.delete()[…] [long line]
I had to add withMessage(…)
to them where that was missing. At the
same time I formatted the places where the lines were long:
service.audit(Message.
.delete()
.withMessage([…])
.withUser(…)
.withPayload(…)
[…]
Now I want to split this commit into two:
But if I do a split in the standard way, say with:
withMessage(…)
from the staged changes/index,
leaving just the formatting changesI get merge conflicts since I did changes in the same places.
We’re not gonna use git-rebase(1) and/or git-cherry-pick(1) since these turn commits into patches in order to make new commits. That’s why you get merge conflicts.
Instead you want to make use of the fact that commits are snapshots.
More concretely:
hash
)withMessage(…)
lineshash
and the parent set to the current commit (the first
commit in the split; HEAD
)#!/usr/bin/env bash
hash=$(git rev-parse @)
# Safely do `git reset`
# See: https://stackoverflow.com/a/2659808/1725151
git diff-index --quiet --cached HEAD -- \
|| { printf "staged changes: refusing to check out"; exit 1; }
git diff-files --quiet \
|| { printf "unstaged changes: refusing to check out"; exit 1; }
git reset --soft @~1
# Manipulate the staged changes
# Give a shell prompt to pause and allow that
printf "Stage your changes and then type ‘exit’\n"
sh
# You can edit the commit message with an interactive rebase afterwards
git commit -msplit
# Add back the original commit
git restore -SW -s "$hash" -- .
git commit -C"$hash"
In this case it helps to keep the state of the starting point and then move one step backwards. Since you have the desired end-state already the only manual work is to remove things from what will become the first commit of the two.
Contrast with a rebase session where you go back N commits, change to the state you want back at that point, and then deal with merge conflicts (if overlapping) as you apply the changes forwards.
It’s natural that the commit messages will change after a split. We leave that for later:
git commit -msplit
)Since we can already change the commit messages easily in a rebase session: there are no possible conflicts any more so we can just focus on that part.
This script splits the current commit you are on. What if you want to do a split N commits back?
edit
)git rebase --continue
(Thanks to j6t)