我有一个包含许多提交的开发分支。这些提交包括“真实”更改(例如添加功能)以及临时更改(例如在一次提交中添加测试代码,然后在以后的提交中将其删除)。
这个分支的真正变化正在逐渐添加到 master 中。每次添加后,我都会在新的 master 上重新建立开发分支。随着每个周期,开发分支和主分支之间的差异越来越小。然而,rebased 开发分支包含所有原始(现在 rebased)提交。其中一些提交现在的净效果为零。
简单的例子:
DEV_BRANCH
Commit ...
Commit D: Remove all test code from f.cpp
Commit ...
Commit C: Add new feature to f.cpp
Commit ...
Commit B: Add more test code to f.cpp
Commit A: Add some test code to f.cpp
Commit ...
MASTER
Commit X: Add new feature to f.cpp
Commit ...
此时,从提交 C
到 f.cpp 的更改已经作为提交 X
在 master 中,并提交 A
+B
+D
组合时对 f.cpp 没有变化。换句话说,对于文件 f.cpp,分支和主分支之间的差异不显示任何内容。
(实际上,提交 A、B、C 和 D 也可能包含对其他文件的更改。)
有没有什么方法可以在 rebase 期间或其他情况下自动简化开发分支中的提交?
在上面的简单示例中,是否可以删除提交C
(更改已经 merge 到master因此现在是“空的”)并且还提交A
、B
和 D
(组合时没有变化)自动?
在更复杂的情况下,当对 f.cpp 的提交也修改其他文件时,是否可以从开发分支中的提交中自动删除对文件 f.cpp 的更改(以简化它们)但保留提交如果它们包含对其他文件的更改?
换句话说,如果将开发分支中所有提交中的所有更改应用到一个文件导致文件与主文件中的文件相同,是否可以从开发分支提交中删除对该文件的更改? (我确实意识到,如果对其他文件的更改需要与对没有实际影响的文件同步完成,这可能会导致副作用。但是,这在我的场景中不是问题,因为我不需要 cherry - 从开发分支中选择任何中间状态。)
最佳答案
我认为针对您的特定用例有几个相当简单的解决方案,这些解决方案首先考虑了如何生成欺骗。
解决方案 1:对于分散分支上的少量提交
假设您在 dev
中以 git rebase --interactive
模式显示了以下一组提交:
pick bf45b13 Add feature X
pick b1f790f Cleanup feature X
pick 40b299a Add feature Y
现在您决定将 40b299a
挑选到 master
中。
问题是当你 rebase dev
时新的提交将是空的。我建议为将来的 rebase 做 git rebase -i master
。然后,您可以删除您从选择中挑选的行,只留下您想要的提交。
因为你想“自动地”执行此操作,你可以制作自己的 git 脚本来同时执行 cherry-pick 和 rebase,从 dev
中删除正确的提交,同时将其添加到 master。
如果你调用下面的脚本,比如 git-transfer
,并将它添加到你的 PATH
某处,你可以调用它作为 git transfer dev master commit [...]
:
#!/bin/bash
usage() {
echo "Usage: git transfer from-branch to-branch commits [...]"
if [ -n "${1}" ]
then
echo
for line; do echo "${line}"; done
fi
exit 1
}
FROM="$(git rev-parse --abbrev-ref "${1}")"
[ -z "${FROM}" ] && usage 'from-branch must be a valid branch name' || shift
TO="$(git rev-parse --abbrev-ref "${1}")"
[ -z "${TO}" ] && usage 'to-branch must be a valid branch name' || shift
ORIGINAL="$(git rev-parse --abbrev-ref HEAD)"
if [ "${ORIGINAL}" == "${TO}" ]
then
echo "Already on branch ${TO}"
else
echo "Switching from ${ORIGINAL} to ${TO}"
git checkout "${TO}" || exit
fi
while [ $# -gt 0 ]
do
for COMMIT in "$(git rev-parse "${1}" | grep '^[^^]')"
do
echo "Moving ${COMMIT} to ${TO}"
git cherry-pick "${COMMIT}"
echo "Removing ${COMMIT} from ${FROM}"
EDITOR="sed -i '/^pick $(git rev-parse --short ${COMMIT})/ d'" git rebase -i "${TO}" "${FROM}"
done
shift
done
if [ "${ORIGINAL}" != "${TO}" ]
then
echo "Switching back to ${ORIGINAL} from ${TO}"
git checkout "${ORIGINAL}"
fi
该脚本接受将被重新定位的“from”分支的名称、将被挑选到的“to”分支的名称,以及提交列表、提交范围等。它基于大量关于本文提出的想法 question和 answer (还有这个 answer 用于循环)。
此解决方案适用于少量提交(最有可能一次提交一次)以及分歧很大的分支。
解决方案 2:对于单个时间线上的大量提交
在您描述的用例中,您定期将 dev
rebase 到 master
,因此 dev
始终有效地领先于 master。下次,不要将单个提交提交到 master,而是执行以下操作:
- 开始交互式 rebase
- 将你想要的提交移到列表的开头
- 完成 rebase
- 将 master 移动到您想要的最后一个提交。
这种方法将使您完全避免任何重复,并且可能是您可以做的最简单的事情。请注意,虽然 dev
会不断 rebase ,但 master
只会通过这种方法快速前进。
关于git - 如何自动删除取消自己的提交?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38220368/