找到Git分支的父分支 [英] Find the parent branch of a Git branch

查看:558
本文介绍了找到Git分支的父分支的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有以下本地存储库,其中包含一个提交树:

  master  - > a 
\
\
开发c - > d
\
\
特征f - > g - > h

master 是我的 this是最新的稳定版本代码 develop 是my 这是'下一个'版本代码功能是为开发 准备的新功能。

我想使用钩子在远程仓库上执行操作,除非commit f ,否则将推送到 feature 被拒绝。 c $ c>是 develop HEAD的直接后裔。即提交树看起来像这样,因为 d
上的特性已被 git rebase

  master  - > a 
\
\
开发c - > d
\
\
特征f - > g - > h

所以有可能:


  • 识别特性的父分支

  • 识别父分支中的提交 f 是后裔?



从那里我会检查父分支的HEAD,并查看 f predecessor是否与父分支HEAD匹配,以确定是否需要重新分配功能。

解决方案

假设远程存储库有一个 develop 分支的副本(您的初始描述将其描述在本地存储库中,但它听起来像它也存在于远程中) ,你应该能够实现我认为你想要的,但是这种方法与你所设想的有所不同。



Git的历史基于 DAG 的提交。分支(和通常的参考)只是指向不断增长的提交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 。但是,祖先并没有改变,只有标签发生了变化。



然而,如果我们问是否提交 6 提交的后代 3 ?(假设 3 6 是完整的SHA-1提交名称),那么答案将是是,无论 bar quux 标签是否存在。



因此,您可以提出一些问题,比如推送的提交是当前开发的后代分支?,但你不能可靠地问推送提交的父分支是什么?。

一个最可靠的问题似乎接近于什么你需要的是:

lockquote

对于所有推送提交的祖先(不包括当前的 develop 和其祖先) ,将当前提示的开发当作父级:


  • 至少有一个这样的提交存在吗? / li>
  • 都是这样的提交singl e-parent提交?


可以实现为:

  pushrev = ... 
basename = develop
if! baserev =$(git rev-parse --verify refs / heads /$ basename2> / dev / null);然后
echo''$ basename'缺失,请求帮助!
exit 1
fi
parents_of_children_of_base =$(
git rev-list --pretty = tformat:%P$ pushrev--not$ baserev|
grep -F$ baserev

case,
中的$ parents_of_children_of_base)echo必须从'$ basename'''
exit 1 ;;
,* \ *)echo不能合并'$ basename'的提示(改为反转)
exit 1 ;;
,*)exit 0 ;;
esac

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

作为参考,下面是一个扩展的示例历史记录:

  
\
\ o ----- J
\ / \
\ | o --- K --- L
\ | /
C -------------- D开发
\ | \
F --- G --- H | F' - G' - H'
| | \
| | o --- o --- o --- N
\ \ \\
\\ o --- o --- P
\ \
R --- S

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



也可以拒绝 L P ,您可以更改问题并请求


对于所有推送提交的祖先(不包括当前的 develop 和其祖先) p>


  • 有两个父母是否有任何提交?如果不是,那么至少有一个这样的提交有目前的小费开发其(仅)父级?

>



  pushrev = ... 
basename = develop
if! baserev =$(git rev-parse --verify refs / heads /$ basename2> / dev / null);然后
echo''$ basename'缺失,请求帮助!
exit 1
fi
parents_of_commits_beyond_base =$(
git rev-list --pretty = tformat:%P$ pushrev--not$ baserev|
grep -v'^ commit'

case$ parents_of_commits_beyond_basein
* \ *)echomust not push merge commit(rebase instead)
1号出口;;
*$ baserev*)exit 0 ;;
*)echo必须从'$ basename''
exit 1;
esac


Let's say I have the following local repository with a commit tree like this:

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

master is my this is the latest stable release code, develop is my this is the 'next' release code, and feature is a new feature being prepared for develop.

What I want to be able to do on my remote repo using hooks, is for pushes to feature to be refused unless commit f is a direct descendant of develop HEAD. i.e. the commit tree looks like this because feature has been git rebase on d.

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

So is it possible to:

  • Identify the parent branch of feature?
  • Identify the commit in parent branch which f is a descendant of?

From there I would check what HEAD of parent branch is, and see if f predecessor matches the parent branch HEAD, to determine if the feature needs to be rebased.

解决方案

Assuming that the remote repository has a copy of the develop branch (your initial description describes it in a local repository, but it sounds like it also exists in the remote), you should be able to achieve what I think you want, but the approach is a bit different from what you have envisioned.

Git’s history is based on a DAG of commits. Branches (and "refs" in general) are just transient labels that point to specific commits in the continually growing commit DAG. As such, the relationship between branches can vary over time, but the relationship between commits does not.

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

It looks like baz is based on (an old version of) bar? But what if we delete bar?

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

Now it looks like baz is based on foo. But the ancestry of baz did not change, we just removed a label (and the resulting dangling commit). And what if we add a new label at 4?

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

Now it looks like baz is based on quux. Still, the ancestry did not change, only the labels changed.

If, however, we were asking "is commit 6 a descendent of commit 3?" (assuming 3 and 6 are full SHA-1 commit names), then the answer would be "yes", whether the bar and quux labels are present or not.

So, you could ask questions like "is the pushed commit a descendent of the current tip of the develop branch?", but you can not reliably ask "what is the parent branch of the pushed commit?".

A mostly reliable question that seems to get close to what you want is:

For all the pushed commit’s ancestors (excluding the current tip of develop and its ancestors), that have the current tip of develop as a parent:

  • does at least one such commit exist?
  • are all such commits single-parent commits?

Which could be implemented as:

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

This will cover some of what you want restricted, but maybe not everything.

For reference, here is an extended example history:

    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

The above code could be used to reject Hand S while accepting H', J, K, or N, but it would also accept L and P (they involve merges, but they do not merge the tip of develop).

To also reject L and P, you can change the question and ask

For all the pushed commit’s ancestors (excluding the current tip of develop and its ancestors):

  • are there any commits with two parents?
  • if not, does at least one such commit have the current tip of develop its (only) parent?

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

这篇关于找到Git分支的父分支的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆