代码之家  ›  专栏  ›  技术社区  ›  Norman Ramsey

如何以编程方式快速转发单个Git提交?

  •  16
  • Norman Ramsey  · 技术社区  · 14 年前

    我定期从Git收到如下消息:

    Your branch is behind the tracked remote branch 'local-master/master' 
    by 3 commits, and can be fast-forwarded.
    

    我希望能够在shell脚本中编写可以执行以下操作的命令:

    1. 如何判断当前分支是否可以从跟踪的远程分支快速转发?

    2. 我怎么知道我的分支背后有多少承诺?

    3. 我怎样才能快进 提交,例如,我的本地分支将从“落后3个提交”变为“落后2个提交”?

    (对于那些感兴趣的人,我正在尝试组装一个高质量的Git/Darcs镜像。)

    3 回复  |  直到 14 年前
        1
  •  9
  •   gyim    14 年前

    如果当前提交是远程分支头的祖先,则可以将远程分支快速转发到本地分支。换句话说,如果远程分支的“一个分支历史记录”包含当前提交(因为如果包含当前提交,则确保将新提交提交“提交到”当前提交)

    因此,确定远程分支是否可以快速转发的一种安全方法是:

    # 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日志时没有--all参数(它将列出所有分支),因此当前提交不可能位于“侧分支”上,并且仍然打印在输出上。

    当前提交之前的提交数等于$current_提交之前$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日志调用来完成此操作。

        2
  •  10
  •   Chris Johnsen    14 年前

    替代方法

    你提到你正在为吉特和达尔克制作某种镜子。您可以查看 git fast-import git fast-export 命令查看它们是否提供了更好的方法来管理您需要提取/提供的数据。

    如何判断一个分支机构是否可以快速前进到其上游分支机构

    这有两个部分。首先,您必须知道或确定哪个分支是当前分支的上游分支。然后,一旦你知道了如何指向上游,你就检查了快速前进的能力。

    为分支找到上游

    Git 1.7.0提供了一种方便的方法来查询分支跟踪的分支(其__上游__分支)。这个 @{upstream} 对象规范语法可以用作分支说明符。作为裸名称,它指的是当前签出的分支的上游分支。作为后缀,它可以用于查找当前未签出的分支的上游分支。

    对于1.7.0之前的Gits,您必须自己分析分支配置选项。( branch.name.remote branch.name.merge )或者,如果您有一个标准的命名约定,您可以使用它来确定上游分支的名称。

    在这个答案里我会写 upstream 引用当前分支上游分支尖端的提交。

    检查快速前进的能力

    如果且仅当a是b的祖先,那么commit a的分支可以快速转发到commit 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
    

    这不需要头部能快速向前 上游 (它只计算上游有多远的水头,而不是上游有多远的水头)。

    一步一步前进

    一般来说,快速前进的历史可能不是线性的。在下面的历史数据中, 主人 可以快进到 上游 ,但A和B都是一个从 主人 在去的路上 上游 .

    ---o---o                      master
           |\
           | A--o--o--o--o--o--o  upstream
            \                 /
             B---o---o---o---o
    

    您可以像线性历史一样沿着一边走,但只能到达合并提交的直接祖先。

    修订行走命令具有 --first-parent 选项,使只执行导致合并提交的第一个父级的提交变得容易。把这个和 Git复位 您可以有效地向前拖动一个分支,一次提交一个分支。

    git reset --hard "$(git rev-list --first-parent --topo-order --reverse ^HEAD upstream | head -1)"
    

    在对另一个答案的评论中,你表达了对 Git复位 . 如果你担心某个分支会被腐蚀,那么你可以使用一个临时分支或者使用一个分离的头作为一个未命名的分支。只要你的工作树是干净的,你不介意移动树枝(或分离的头)。 git reset --hard 不会丢弃任何东西。如果你仍然担心,你应该认真研究使用 快速出口 在那里你根本不需要触摸工作的树。

    跟随不同的父母会更困难。您可能需要编写自己的历史助行器,以便为它提供关于您希望为每个合并选择哪个方向的建议。

    当你向前移动到一个离合并点很近的点时,DAG会像这样(拓扑结构和以前一样,它只是 主人 已移动的标签):

    ---o---o--A--o--o--o--o--o    master
           |                  \
           |                   o  upstream
            \                 /
             B---o---o---o---o
    

    此时,如果您_156;向前移动一个commit_157;,您将移动到合并。这也将引入____(使可从 主人 )从B到合并提交的所有提交。如果您假定_156;向前移动一个commit_157;将只向历史DAG添加一个commit,那么此步骤将违反该假设。

    在这种情况下,您可能需要仔细考虑您真正想要做什么。像这样拖入额外的提交是可以的,还是应该有一些机制让__返回_到B的父级,并在处理合并提交之前在该分支上向前移动?

        3
  •  7
  •   William Pursell    14 年前

    这可能不是最优雅的,但它很管用:

    $ 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.