有时候我需要将一个将多个文件接触的提交分解为单独的提交。
例如,假设我有一个涉及文件foo
和bar
的提交,并且我想分成两个提交,其中一个仅涉及foo
,而其中一个仅涉及bar
。假设我要拆分的提交是提示提交。
例如,假设这是我的存储库的历史记录
commit a78143999ebc8fb1d696832ed5ef2654a3325bb4
Author: Gregory Nisbet <>
Date: Fri Oct 25 23:12:55 2019 -0700
add both foo and bar
commit 760208789956815d471ccd1ce9b642b32299cbb3
Author: Gregory Nisbet <>
Date: Fri Oct 25 23:12:25 2019 -0700
empty foo and bar
我想将其更改为更多类似的内容。我在上面添加新提交的地方,仅涉及foo
。在此特定示例中,第二个提交仅触摸bar
。理想情况下,我希望能够通过使用foo和bar定位原始提交并提供放置新提交的路径来执行这种拆分。
commit 938b52c57511f1150ca0f1958458ece069205631
Author: Gregory Nisbet <>
Date: Fri Oct 25 23:18:14 2019 -0700
add foo only
commit d9c09b249f105ffd65f35392b26ac6a08f7effc2
Author: Gregory Nisbet <>
Date: Fri Oct 25 23:12:55 2019 -0700
add bar only
commit 760208789956815d471ccd1ce9b642b32299cbb3
Author: Gregory Nisbet <>
Date: Fri Oct 25 23:12:25 2019 -0700
empty foo and bar
我试图编写一个脚本,通过产生两个还原并将第一个还原修改为原始提交来执行这种拆分行为。
该脚本很丑陋,但在我尝试过的少数情况下确实可以运行,并且具有正确的API。
我感觉到必须有一种更直接的方法来实现我要尝试的工作。
#!/bin/bash
while getopts "m:" opt; do
case "$opt" in
m)
message="$OPTARG" ;;
esac
done
shift "$((OPTIND-1))"
if test -z "$message"; then
1>&2 printf '%s\n' 'message cannot be empty'
exit 20
fi
branch="$(python -c 'import uuid; print(str(uuid.uuid4()))')"
if 1>/dev/null 2>/dev/null git diff --exit-code; then
: "do nothing"
else
1>&2 printf '%s\n' 'working directory not clear'
exit 30
fi
hash="$(git rev-parse --verify HEAD)"
# revert initial commit, write revert to index
git revert --no-commit -- "$hash" || exit 10
# remove paths specified on command line from index
git reset HEAD "$@" || { git revert --abort; exit 20; }
git checkout -- "$@" || { git revert --abort; exit 30; }
# commit partial revert
git commit --allow-empty-message --no-edit || exit 40
# revert the just-committed partial revert, keep
# the new double revert in the index
git revert --no-commit HEAD || exit 50
# checkout an unused branch
git checkout -b "$branch" || exit 60
# commit double revert
git commit --allow-empty-message --no-edit || exit 70
# go back to previous branch
git checkout - || exit 80
# uncommit the latest change (the first revert)
git reset --soft HEAD^ || exit 90
# amend first revert onto previous commit
git commit --amend --no-edit || exit 10
# go to scratch branch
git checkout "$branch" || exit 20
# revert latest change in scratch branch, keep it in the index
git reset --soft HEAD^ || exit 30
# go to previous branch
git checkout - || exit 40
# use our commit message to create a new commit with that message
git commit --no-edit --message="$message" || exit 50
# delete temporary branch
git branch -D "$branch" || exit 60
使用上述脚本,我可以仅使用gitsplit -m 'second commit' foo
定位提示提交中路径的一部分。
$ git init
Initialized empty Git repository in /tmp/gitdir/.git/
$ echo foo > foo
$ echo bar > bar
$ git add foo bar
$ git commit -m 'initial commit'
[master (root-commit) 4472c3c] initial commit
2 files changed, 2 insertions(+)
create mode 100644 bar
create mode 100644 foo
$ gitsplit -m 'second commit' foo
Unstaged changes after reset:
D foo
[master 9a5d357] Revert "initial commit"
1 file changed, 1 deletion(-)
delete mode 100644 bar
A bar
Switched to a new branch 'b70d1c24-aca5-4676-a287-d35dbe14f790'
[b70d1c24-aca5-4676-a287-d35dbe14f790 abb559a]
1 file changed, 1 insertion(+)
create mode 100644 bar
Switched to branch 'master'
[master 5d95965] initial commit
Date: Fri Oct 25 23:36:26 2019 -0700
1 file changed, 1 insertion(+)
create mode 100644 foo
Switched to branch 'b70d1c24-aca5-4676-a287-d35dbe14f790'
A bar
Switched to branch 'master'
[master 2f64de7] second commit
1 file changed, 1 insertion(+)
create mode 100644 bar
Deleted branch b70d1c24-aca5-4676-a287-d35dbe14f790 (was 9a5d357).
$ git --no-pager show --format=short HEAD
commit 2f64de7dc9df58323db21b676251df27c46b924f
Author: Gregory Nisbet <>
second commit
diff --git a/bar b/bar
new file mode 100644
index 0000000..5716ca5
--- /dev/null
+++ b/bar
@@ -0,0 +1 @@
+bar
$ git --no-pager show --format=short HEAD^
commit 5d9596507023a4af4dd7536e49e97359b35632d6
Author: Gregory Nisbet <>
initial commit
diff --git a/foo b/foo
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/foo
@@ -0,0 +1 @@
+foo