找到Git分支的父分支 [英] Find the parent branch of a Git branch
问题描述
master - > a
\
\
开发c - > d
\
\
特征f - > g - > h
master
是我的 this是最新的稳定版本代码, develop
是my 这是'下一个'版本代码,功能
是为开发
准备的新功能。
我想使用钩子在远程仓库上执行操作,除非commit f $>,否则将推送到
feature
被拒绝。 c $ c>是 develop
HEAD的直接后裔。即提交树看起来像这样,因为
上的特性已被 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 和其祖先) ,将当前提示的开发当作父级:
可以实现为:
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 H
and 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屋!