Nor*_*sey 17 git git-pull fast-forward
我定期从git获取看起来像这样的消息:
Your branch is behind the tracked remote branch 'local-master/master'
by 3 commits, and can be fast-forwarded.
Run Code Online (Sandbox Code Playgroud)
我希望能够在shell脚本中编写命令,可以执行以下操作:
如何判断我当前的分支是否可以从它正在跟踪的远程分支快进?
如何判断我的分支"后面"有多少提交?
如何通过一次提交快进,以便例如,我的本地分支将从"后面的3次提交"变为"后面的2次提交"?
(对于那些感兴趣的人,我正在尝试组合一个高质量的git/darcs镜像.)
Chr*_*sen 11
你提到你正在为Git和Darcs制作某种镜像.而不是通过历史拖着工作树,你可能反而看git的快速导入和Git的快速出口命令,看看他们是否提供了一个更好的办法来管理你需要提取/提供的数据.
这有两个部分.首先,您必须知道或确定哪个分支是当前分支的"上游".然后,一旦您知道如何引用上游,就会检查快进的能力.
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
Run Code Online (Sandbox Code Playgroud)
git rev-list ^HEAD upstream | wc -l
Run Code Online (Sandbox Code Playgroud)
这并不要求HEAD可以快进到上游(它只计算HEAD在上游后面的距离,而不是HEAD后面的上游距离).
通常,快速前进的历史可能不是线性的.在下面的历史DAG中,主人可以快进到上游,但A和B都是从主人那里向上游的 "一次前进" .
---o---o master
|\
| A--o--o--o--o--o--o upstream
\ /
B---o---o---o---o
Run Code Online (Sandbox Code Playgroud)
您可以将一侧视为线性历史记录,但仅限于合并提交的直接祖先.
版本行走命令有一个--first-parent选项,可以很容易地只跟踪导致第一个合并提交父项的提交.将此与git reset相结合,您可以有效地拖动分支"前进,一次一个提交".
git reset --hard "$(git rev-list --first-parent --topo-order --reverse ^HEAD upstream | head -1)"
Run Code Online (Sandbox Code Playgroud)
在对另一个答案的评论中,你表达了对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
Run Code Online (Sandbox Code Playgroud)
此时,如果您"前进一次提交",您将转到合并.这也将"引入"(从主服务器可以访问)从B到提交的所有提交.如果您假设"前进一个提交"只会向历史DAG添加一个提交,则此步骤将违反该假设.
在这种情况下,您可能需要仔细考虑您真正想要做的事情.它是确定只需将额外提交这样,还是应该有一些机制,"要回"到B的家长和该分支前进处理该合并提交过吗?
gyi*_*yim 10
如果当前提交是远程分支头的祖先,则远程分支可以快速转发到本地分支.换句话说,如果远程分支的"一分支历史"包含当前提交(因为如果确实如此,则确定新提交已提交到"当前提交")
因此,一种确定远程分支是否可以快速转发的安全方法:
# 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
Run Code Online (Sandbox Code Playgroud)
请注意,调用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
Run Code Online (Sandbox Code Playgroud)
当然,如果将第一个调用的结果保存到文件中,则可以使用一个git log调用执行此操作.
这可能不是最优雅的,但它有效:
$ 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.