小编典典

如何找到最近的 Git 分支的父级

all

假设我有以下本地存储库,其提交树如下:

master --> a
            \
             \
      develop c --> d
               \
                \
         feature f --> g --> h

master这是我的 这是最新的稳定版本代码 ,这develop是我 的“下一个”版本代码 ,并且feature
正在准备的新功能develop

使用钩子,我希望能够拒绝推送到feature我的远程存储库,除非提交fdevelopHEAD 的直接后代。即,提交树看起来像这样,因为
feature has been git rebaseon d

master --> a
            \
             \
      develop c --> d
                     \
                      \
               feature f --> g --> h

那么是否有可能:

  • 识别 ? 的父分支feature
  • 确定父分支中的提交f是其后代?

从那里我会检查父分支的 HEAD 是什么,并查看f前任是否与父分支 HEAD 匹配,以确定是否需要重新设置该功能。


阅读 322

收藏
2022-03-11

共1个答案

小编典典

假设远程仓库有一个 develop
分支的副本(你最初的描述是在本地仓库中描述的,但听起来好像远程也有),你应该可以实现我认为你想要的,但是方法和你想象的有点不同。

Git
的历史基于提交的DAG。分支(以及一般的“efs”)只是指向不断增长的提交
DAG 中特定提交的临时标签。因此,分支之间的关系可能会随着时间而变化,但提交之间的关系不会。

    ---o---1                foo
            \
             2---3---o      bar
                  \
                   4
                    \
                     5---6  baz

看起来baz是基于(旧版本的)bar?但是如果我们删除bar呢?

    ---o---1                foo
            \
             2---3
                  \
                   4
                    \
                     5---6  baz

现在看起来baz是基于foo. 但血统baz并没有改变。我们刚刚删除了一个标签(以及由此产生的悬空提交)。如果我们在 处添加一个新标签4呢?

    ---o---1                foo
            \
             2---3
                  \
                   4        quux
                    \
                     5---6  baz

现在看起来baz是基于quux. 尽管如此,血统并没有改变,只是标签发生了变化。

然而,如果我们问“s commit 6a descendent of commit 3?”(假设36是完整的 SHA-1
提交名称),那么答案将是“测”,无论barquux标签是否存在。

因此,您可以问诸如“推送的提交是 开发 分支的当前提示的后代?”之类的问题,但您不能可靠地问“推送的提交的父分支是什么?”。

一个似乎接近您想要的最可靠的问题是:

对于所有已推送的提交的祖先(不包括 develop 的当前尖端及其祖先),将当前的 develop 尖端作为父级:

  • 至少存在一个这样的提交吗?
  • 所有这些提交都是单亲提交吗?

这可以实现为:

pushedrev=...
basename=develop
if ! baserev="$(git rev-parse --verify refs/heads/"$basename" 2>/dev/null)"; then
    echo "'$basename' is missing, call for help!"
    exit 1
fi
parents_of_children_of_base="$(
  git rev-list --pretty=tformat:%P "$pushedrev" --not "$baserev" |
  grep -F "$baserev"
)"
case ",$parents_of_children_of_base" in
    ,)     echo "must descend from tip of '$basename'"
           exit 1 ;;
    ,*\ *) echo "must not merge tip of '$basename' (rebase instead)"
           exit 1 ;;
    ,*)    exit 0 ;;
esac

这将涵盖您想要限制的一些内容,但可能不是所有内容。

作为参考,这里是一个扩展的示例历史:

    A                                   master
     \
      \                    o-----J
       \                  /       \
        \                | o---K---L
         \               |/
          C--------------D              develop
           \             |\
            F---G---H    | F'--G'--H'
                    |    |\
                    |    | o---o---o---N
                     \   \      \       \
                      \   \      o---o---P
                       \   \
                        R---S

上面的代码可以用来拒绝HandS同时接受H', J, K, or N,但它也可以接受Land P(它们涉及合并,但它们不合并
develop 的提示)。

要也拒绝Land P,您可以更改问题并询问

对于所有被推送的提交的祖先(不包括当前的 开发 技巧及其祖先):

  • 有两个父母的承诺吗?
  • 如果没有,是否至少有一个这样的提交具有 开发 其(唯一)父级的当前提示?
pushedrev=...
basename=develop
if ! baserev="$(git rev-parse --verify refs/heads/"$basename" 2>/dev/null)"; then
    echo "'$basename' is missing, call for help!"
    exit 1
fi
parents_of_commits_beyond_base="$(
  git rev-list --pretty=tformat:%P "$pushedrev" --not "$baserev" |
  grep -v '^commit '
)"
case "$parents_of_commits_beyond_base" in
    *\ *)          echo "must not push merge commits (rebase instead)"
                   exit 1 ;;
    *"$baserev"*)  exit 0 ;;
    *)             echo "must descend from tip of '$basename'"
                   exit 1 ;;
esac
2022-03-11