替代方案
你提到正在为Git和Darcs制作某种类型的镜像。你可以考虑使用git fast-import和git fast-export命令,以查看它们是否提供了更好的方法来管理需要提取/提供的数据,而无需在历史记录中拖着一个工作树。
如何确定分支能否向其上游分支进行快进合并
这个问题有两个部分。首先,您必须知道或确定哪个分支是当前分支的“上游”分支。然后,一旦您知道如何引用上游分支,您就可以检查是否可以进行快进合并。
查找分支的上游分支
Git 1.7.0有一种方便的方法来查询分支跟踪的分支(即其“上游”分支)。@{upstream}
对象规范语法可用作分支指定器。作为一个裸名,它引用当前已检出的分支的上游分支。作为后缀,可以用于查找当前没有检出的分支的上游分支。
对于早于1.7.0版本的Git,您将不得不自己解析分支配置选项(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
找到“落后提交”的数量
git rev-list ^HEAD upstream | wc -l
这并不要求HEAD可以快速前进到upstream(它只计算HEAD落后于upstream的距离,而不是upstream落后于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将如下图所示(拓扑结构与之前相同,只有master标签已移动):
---o---o--A--o--o--o--o--o master
| \
| o upstream
\ /
B---o---o---o---o
如果你“向前移动一次提交”,那么你将转到合并。这也将使从B开始的所有提交“带入”(从master可达)。如果您认为“向前移动一次提交”只会向历史DAG中添加一个提交,则此步骤将违反该假设。
在这种情况下,您可能需要仔细考虑您真正想要做什么。把额外的提交拖进来是可以的,或者应该有一些机制让您“返回”到B的父提交,并在处理合并提交之前在该分支上向前移动吗?
git-merge-base
的,谢谢。大部分都是我想避免的“陷阱和问题”。我认为git-fast-export
在根本上与darcs的业务方式不一致,事实上,获取准确信息的唯一方法是逐步浏览历史记录。我希望我使用仓库的方式能确保我避免这些问题。+1 - Norman Ramsey