I know how to manually split a commit using git rebase -i
, but how can I automatically split every commit in a branch by file?
For instance, commit A
modified 3 files, f1, f2 and f3. After the split, there are 3 commits A-f1, A-f2 and A-f3.
I want to do this to make a major rewriting easier as I will only have to squash some small commits.
The Script
The following script splits HEAD
by file:
#!/bin/bash
set -e
LF=$'\n'
SHA=$(git rev-parse --short HEAD)
MSG=$(git show -s --format=%B HEAD)
set -f; IFS=$'\n'
FILES=($(git diff-tree --no-commit-id --name-only -r HEAD))
set +f; unset IFS
git reset HEAD^
for f in "${FILES[@]}"; do
git add "$f"
git commit -m "$SHA $f$LF$LF$MSG"
done
The generated commit messages are of the form:
<original SHA> <file name>
<original commit message>
Usage
The following assumes that you can run above script as git-split
.
If you want to split all commits in a range by file, use it like this:
git rebase --interactive --exec git-split <branch>
If you want to split a single commit during an interactive rebase, use it like this:
p Commit to split
x git-split
Any improvements to the script are welcome.
For every commit, you would need
first to list all files in that commit
git diff-tree --no-commit-id --name-only -r <SHA1>
then for each file, extract that file
git show <SHA1>:/path/within/repo/to/file
Do that in a working tree of a dedicated branch, and for every file extracted, add and commit.
Then you can reset your current branch by that new one built commit-file by commit-file.