让我们假设我有以下本地存储库和一个像这样的提交树:

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

Master是我的,这是最新的稳定发布代码,develop是我的,这是“下一个”发布代码,feature是一个正在准备开发的新功能。

使用钩子,我希望能够拒绝推送功能到我的远程存储库,除非commit f是develop HEAD的直接后代。也就是说,提交树看起来是这样的,因为feature已经基于d。

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

那么是否有可能:

识别特征的父分支? 确定父分支中的提交f是哪个分支的后代?

从那里,我将检查父分支的HEAD是什么,并查看f前任是否匹配父分支HEAD,以确定该特性是否需要重基。


当前回答

在我的例子中,“feature”分支的“父”是一个分支,我在“feature”分支的拉请求中显式地将其标记为“base”。它可以通过GitHub CLI发现:

gh pr list --head "feature" --json baseRefName --jq '.[].baseRefName'

其他回答

转到父级

您可以直接执行命令

git parent

如果您添加Joe Chrysler的答案作为Git别名,就可以找到分支的父分支。它将简化使用。

打开位于“~/”的gitconfig文件。使用任何文本编辑器(适用于Linux)。而对于Windows的“。gitconfig路径通常位于C:\users\your-user\.gitconfig。

vim  ~/.gitconfig

在文件中添加如下alias命令:

[alias]
    parent = "!git show-branch | grep '*' | grep -v \"$(git rev-parse --abbrev-ref HEAD)\" | head -n1 | sed 's/.*\\[\\(.*\\)\\].*/\\1/' | sed 's/[\\^~].*//' #"

保存并退出编辑器。

执行命令git parent。

就是这样!

对于您的整体问题,我有一个解决方案(确定feature是否源自develop的提示),但使用您概述的方法它不起作用。

你可以使用git branch——contains列出所有来自develop提示的分支,然后使用grep确保feature在其中。

git branch --contains develop | grep "^ *feature$"

如果是其中之一,则将“feature”打印到标准输出,并返回代码为0。否则,它将不打印任何东西,并有一个返回代码1。

基于git show-branch -a加上一些过滤器的解决方案有一个缺点:git可能会考虑存在时间较短的分支的分支名。

如果你有一些你关心的潜在父母,你可以问自己类似的问题(可能也是OP想知道的):

从所有分支的特定子集,哪个是最近的父git分支?

为了简化起见,我将“一个git分支”表示HEAD(即当前分支)。

让我们假设我们有以下分支:

HEAD
important/a
important/b
spam/a
spam/b

基于git show-branch -a +过滤器的解决方案,可能会给出最近的HEAD父节点是spam/a,但我们不关心这个。

如果我们想知道important/a和important/b中哪一个是HEAD的最近父节点,我们可以运行下面的代码:

for b in $(git branch -a -l "important/*" | sed -E -e "s/\*//"); do
    d1=$(git rev-list --first-parent ^${b} HEAD | wc -l);
    d2=$(git rev-list --first-parent ^HEAD ${b} | wc -l);
    echo "${b} ${d1} ${d2}";
done \
| sort -n -k2 -k3 \
| head -n1 \
| awk '{print $1}';

它的作用:

1.) $(git branch -a -l "important/*" | sed -E -E "s/\*//"):打印带有某些模式("important/*")的所有分支列表。(如果你碰巧在一个重要的/*分支上,git分支将包含一个*来指示你当前的分支。命令替换$()将把它展开到当前目录的内容中。sed从git分支的输出中删除*。)

2.) d=$(git revl -list——first-parent ^${b} HEAD | wc -l);:对于每个分支($b),计算从HEAD到$b中最近的提交的距离($d1)(类似于计算从一点到直线的距离)。在这里,您可能需要以不同的方式考虑距离:您可能不想使用——first-parent,或者可能需要从分支尖端到尖端的距离("${b}"…HEAD),…

2.2) d2=$(git rev-list——first-parent ^HEAD ${b} | wc -l);:对于每个分支($b),计算从分支顶端到HEAD中最近的提交的距离($d2)。我们将使用这个距离在距离$d1相等的两个分支之间进行选择。

3.) echo "${b} ${d1} ${d2}";:打印每个分支的名称,后面跟着距离,以便稍后对它们进行排序(首先是$d1,然后是$d2)。

4.) | sort -n -k2 -k3:对之前的结果进行排序,因此我们得到一个排序(按距离)的所有分支的列表,后面跟着它们的距离(两个)。

5) | head -n1:上一步的第一个结果将是距离较小的分支,即最近的父分支。所以丢弃所有其他分支。

6.) | awk '{print $1}';:我们只关心分支的名称,而不关心距离,因此提取第一个字段,这是父节点的名称。在这里!:)

我并不是说这是解决问题的好方法,但这似乎对我来说确实有效:

git branch --contains $(cat .git/ORIG_HEAD)

问题是,隐藏一个文件是窥探Git的内部工作,所以这并不一定是向前兼容(或向后兼容)。

Mark Reed的解决方案基本上是正确的。但是,请注意,提交行不仅应该包含星号,而且应该以星号开头!否则,包含星号的提交消息也包含在匹配的行中。所以它应该是:

git show-branch——| awk - f '[]^~[]' '/^\*/ && !/'"$ current_branch”/{打印2美元;退出}'

或者是更长的版本:

git show-branch -a           |
  awk '^\*'                  | # we want only lines that contain an asterisk
  awk -v "$current_branch"   | # but also don't contain the current branch
  head -n1                   | # and only the first such line
  sed 's/.*\[\(.*\)\].*/\1/' | # really, just the part of the line between []
  sed 's/[\^~].*//'            # and with any relative refs (^, ~n) removed`