我定期从git收到如下信息:
Your branch is behind the tracked remote branch 'local-master/master'
by 3 commits, and can be fast-forwarded.
我希望能够在shell脚本中编写可以执行以下操作的命令:
如何判断我当前的分支是否可以从正在跟踪的远程分支快进?
如何判断我的分支“背后”有多少提交?
如何通过一个提交快进,以便例如,我的本地分支将从“后面的3次提交”变为“后面的2次提交”?< / p>
(对于那些有兴趣的人,我正在尝试组合一个高质量的git / darcs镜像。)
答案 0 :(得分:10)
你提到你正在为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
git rev-list ^HEAD upstream | wc -l
这并不要求HEAD可以快进到上游(它只计算HEAD在上游的距离,而不是HEAD后面的上游距离)。
一般而言,快速前进的历史可能不是线性的。在下面的历史DAG中, master 可以快进到上游,但A和B都是来自 master 的“一个提交转发”通往上游的方式。
---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的父级并在处理合并提交之前在该分支上前进?
答案 1 :(得分:9)
如果当前提交是远程分支头的祖先,则远程分支可以快速转发到本地分支。换句话说,如果远程分支的“一分支历史”包含当前提交(因为如果确实如此,则确定新提交已提交到“当前提交”)
这是一种确定远程分支是否可以快速转发的安全方法:
# Convert reference names to commit IDs
current_commit=$(git rev-parse HEAD)
remote_commit=$(git rev-parse remote_name/remote_branch_name)
# Call git log so that it prints only commit IDs
log=$(git log --topo-order --format='%H' $remote_commit | grep $current_commit)
# Check the existence of the current commit in the log
if [ ! -z "$log" ]
then echo 'Remote branch can be fast-forwarded!'
fi
请注意,调用git log时没有--all参数(列出所有分支),因此当前提交不可能在“side branch”上并且仍然在输出上打印。
当前提交之前的提交数等于$ current_commit之前$ log中的行数。
如果您只想快进一个提交,则取当前提交之前的行(例如,使用grep -B 1),并将本地分支重置为此提交。
更新:您可以使用git log commit1..commit2
来确定快速转发提交的数量:
if [ ! -z "$log" ]
then
# print the number of commits ahead of the current commit
ff_commits=$(git log --topo-order --format='%H' \
$current_commit..$remote_commit | wc -l)
echo "Number of fast-forwarding commits: $ff_commits"
# fast-forward only one commit
if [ $ff_commits -gt 1 ]
then
next_commit=$(git log --topo-order --format='%H' \
$current_commit..$remote_commit | tail -1)
git reset --hard $next_commit
fi
fi
当然,如果将第一个调用的结果保存到文件中,则可以使用一个git log调用来执行此操作。
答案 2 :(得分:7)
这可能不是最优雅的,但它有效:
$ git fetch $ git status | sed -n 2p # Your branch is behind 'origin/master' by 23 commits, and can be fast-forwarded. $ git reset origin/master~22 > /dev/null $ git status | sed -n 2p # Your branch is behind 'origin/master' by 22 commits, and can be fast-forwarded.