如何找到Git分支的最近父级? [英] How to find the nearest parent of a Git branch?

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

问题描述

假设我有以下具有提交树的本地存储库:

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

master 是我的是最新的稳定发布代码 develop 是我的这是下一个发布代码,而功能正在为开发 准备的新功能。



我想使用钩子在远程回购上能够执行的操作是拒绝推送功能,除非提交 f develop HEAD的直接后代。即提交树看起来像这样,因为功能已经在 d 上进行了 git rebase 了。

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

所以有可能:




  • 确定功能的父分支?

  • 确定<$ c $的父分支中的提交c> f 是的后代吗?



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

解决方案

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



Git的历史基于 DAG 提交。分支(通常是 refs)只是临时标签,指向不断增长的提交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 标签是否存在。



因此,您可能会问被推送的提交是 develop 分支的当前提示的后代吗?之类的问题,但是您不能可靠地问被推送的提交的父分支是什么?



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


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




  • 是否存在至少一个这样的提交?

  • 所有这样的提交都是单亲提交吗?


可以实现为:

  pushedrev = ... 
basename =如果开发
! baserev = $(git rev-parse --verify refs / heads / $ basename 2> / dev / null);然后
回显'$ basename'丢失,请寻求帮助!
出口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)echo必须从'$ basename'的尖端下降
退出1 ;;
,* \ *)echo一定不要合并‘$ basename’的提示(改为rebase)
出口1 ;;
,*)出口0 ;;
esac

这将涵盖您要限制的部分内容,但可能不是全部。 / p>

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

 管理员
\
o ----- J
\ / \
b | 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 的当前提示及其祖先):




  • 有两个父母有任何承诺吗?

  • 如果没有,至少有这样的承诺有




的当前提示? class = lang-sh prettyprint-override>  pushedrev = ... 
basename = develop
if! baserev = $(git rev-parse --verify refs / heads / $ basename 2> / dev / null);然后
回显'$ basename'丢失,请寻求帮助!
出口1
fi
Parents_of_commits_beyond_base = $(
git rev-list --pretty = tformat:%P $ pushedrev --not $ baserev |
grep -v'^ commit'

case $ parent
中的 $ parents_of_commits_beyond_base * \ *)echo一定不要推送合并提交(改为变基)
出口1 ;;
* $ baserev *)出口0 ;;
*)echo必须从'$ basename'的尖端下降
出口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天全站免登陆