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

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,以确定该特性是否需要重基。


当前回答

这是我的PowerShell版本:

function Get-GHAParentBranch {
    [CmdletBinding()]
    param(
        $Name = (git branch --show-current)
    )
    git show-branch |
      Select-String '^[^\[]*\*' |
      Select-String -NotMatch -Pattern "\[$([Regex]::Escape($Name)).*?\]" |
      Select-Object -First 1 |
      Foreach-Object {$PSItem -replace '^.+?\[(.+)\].+$','$1'}
}

其他回答

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

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

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

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

请记住,正如“Git:查找一个提交来自哪个分支”中所描述的那样,你不能轻易地确定提交是在哪个分支进行的(分支可以重命名、移动、删除……),即使Git branch——contains <commit>是一个开始。

你可以从commit返回到commit,直到git分支——contains <commit>没有列出feature分支和development分支, 比较提交SHA1到/refs/heads/develop

如果两个提交id匹配,就可以继续了(这意味着特性分支的起源在develop的HEAD)。

当我做了像develop→release-v1.0.0→feature-foo这样的事情时,这对我来说并不管用。它会一直往回发展。请注意,其中涉及到一个基数调整,我不确定这是否加剧了我的问题……

下面给出了正确的提交哈希值:

git log --decorate \
  | grep 'commit' \
  | grep 'origin/' \
  | head -n 2 \
  | tail -n 1 \
  | awk '{ print $2 }' \
  | tr -d "\n"

Use:

vbc=$(git rev-parse --abbrev-ref HEAD)
vbc_col=$(( $(git show-branch | grep '^[^\[]*\*' | head -1 | cut -d* -f1 | wc -c) - 1 )) 
swimming_lane_start_row=$(( $(git show-branch | grep -n "^[\-]*$" | cut -d: -f1) + 1 )) 
git show-branch | tail -n +$swimming_lane_start_row | grep -v "^[^\[]*\[$vbc" | grep "^.\{$vbc_col\}[^ ]" | head -n1 | sed 's/.*\[\(.*\)\].*/\1/' | sed 's/[\^~].*//'

它达到了与Mark Reed的答案相同的目的,但它使用了一种更安全的方法,在许多情况下都不会表现不当:

父分支的最后一次提交是一个merge,使列显示为-,而不是* 提交消息包含一个分支名称 提交消息包含*

Joe Chrysler的命令行魔法可以简化。下面是Joe的逻辑——为了简洁起见,我在两个版本中都引入了一个名为cur_branch的参数来代替命令替换' git rev-parse——abbrev-ref HEAD ';可以像这样初始化:

cur_branch=$(git rev-parse --abbrev-ref HEAD)

然后,这是Joe的管道:

git show-branch -a           |
  grep '\*'                  | # we want only lines that contain an asterisk
  grep -v "$cur_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

我们可以在一个相对简单的awk命令中完成与所有这五个单独的命令过滤器相同的事情:

git show-branch -a |
  awk -F'[]^~[]' '/\*/ && !/'"$cur_branch"'/ {print $2;exit}'

具体情况是这样的:

-F'[]^~[]'

将行分割为以]、^、~和[为字符的字段。

/\*/

找出包含星号的行

&& !/'"$cur_branch"'/

...但不是当前的分支名称

{ print $2;

当您找到这样的一行时,打印它的第二个字段(即字段分隔符第一次和第二次出现之间的部分)。对于简单的分支名称,这将是括号之间的内容;对于具有相对跳转的引用,它将只是没有修饰符的名称。因此,我们的字段分隔符集处理了两个sed命令的意图。

  exit }

然后立即退出。这意味着它只处理第一个匹配的行,所以我们不需要通过head -n 1来输出。