替代方法
您提到您正在为 Git 和 Darcs 开发某种镜像。您可以查看git fast-import和git fast-export命令来查看它们是否提供了更好的方法来管理您需要提取/提供的数据,而不是在历史中拖动工作树。
如何判断一个分支是否可以快进到其上游分支
这有两个部分。首先,您必须知道或确定哪个分支是当前分支的“上游”。然后,一旦您知道如何引用上游,您就可以检查快进的能力。
寻找分支的上游
Git 1.7.0 有一种方便的方法来查询一个分支跟踪哪个分支(它的“上游”分支)。@{upstream}
对象规范语法可以用作分支说明符。作为一个简单的名称,它指的是当前签出的分支的上游分支。作为后缀,它可用于查找当前未签出的分支的上游分支。
对于 1.7.0 之前的 Gits,您必须自己解析分支配置选项(branch.name.remote
和branch.name.merge
)。或者,如果您有一个标准的命名约定,您可以使用它来确定上游分支的名称。
在这个答案中,我将写upstream
参考当前分支上游的分支尖端的提交。
检查快进的能力
当且仅当 A 是 B 的祖先时,提交 A 处的分支可以快进到提交 B。
gyim 展示了一种检查这种情况的方法(列出所有可从 B 访问的提交并在列表中检查 A)。检查这种情况的一种更简单的方法可能是检查 A 是否是 A 和 B 的合并基础。
can_ff() {
a="$(git rev-parse "$1")" &&
test "$(git merge-base "$a" "$2")" = "$a"
}
if can_ff HEAD local-master/master; then
echo can ff to local-master/master
else
echo CAN NOT ff to local-master/master
fi
查找“背后提交”的数量</h2>
git rev-list ^HEAD upstream | wc -l
这并不要求 HEAD 可以快进到上游(它只计算 HEAD 落后于上游多远,而不是上游落后于 HEAD 多远)。
通过一次提交向前推进
一般来说,可快进的历史可能不是线性的。在下面的历史 DAG 中,master可以快进到upstream ,但是 A 和 B 都是从master到upstream的“一个提交” 。
---o---o master
|\
| A--o--o--o--o--o--o upstream
\ /
B---o---o---o---o
您可以跟踪一侧,就好像它是线性历史一样,但仅限于合并提交的直接祖先。
修订遍历命令有一个--first-parent
选项,可以很容易地只跟踪导致合并提交的第一个父级的提交。将此与git reset结合使用,您可以有效地“向前,一次提交”拖动一个分支。
git reset --hard "$(git rev-list --first-parent --topo-order --reverse ^HEAD upstream | head -1)"
在对另一个答案的评论中,您表达了对git reset的恐惧。如果您担心损坏某些分支,那么您可以使用临时分支或使用分离的 HEAD 作为未命名分支。只要您的工作树是干净的并且您不介意移动树枝(或分离的 HEAD),git reset --hard
就不会破坏任何东西。如果您仍然担心,您应该认真考虑使用git fast-export,您根本不必接触工作树。
跟随不同的父母会更加困难。您可能必须编写自己的历史漫游器,以便您可以就每次合并的“哪个方向”向它提供建议。
当您向前移动到距离合并不远的点时,DAG 将如下所示(拓扑与之前相同,只是移动了主标签):
---o---o--A--o--o--o--o--o master
| \
| o upstream
\ /
B---o---o---o---o
此时,如果您“向前提交一次”,您将进入合并。这也将“引入”(使master可以访问)从 B 到合并提交的所有提交。如果您假设“向前提交一次”只会向历史 DAG 添加一次提交,那么这一步将违反该假设。
您可能需要仔细考虑在这种情况下您真正想要做什么。像这样拖入额外的提交是可以的,或者在处理合并提交之前是否应该有一些机制可以“返回”到 B 的父级并在该分支上前进?