是否有可能无法从除master之外的任何分支创建分支?
以下是我的工作方式:
今天早上我有一个惊喜,当我将一个票务分支合并为主人时,很多其他东西被合并了:显然我是从preprod创建了票证分支。
那么,有没有办法避免这种情况?我需要确保我创建的分支仅从master创建。
答案 0 :(得分:3)
根据要求,答案是“不”。分支 - 更确切地说,分支标签 -in git是一些短暂的东西:它们随意出入,任何人都可以创建一个附加到任何地方的新标签在存储库中的任何地方提交 。例如,如果你有这个:
A - B - C - D <-- main
\
E - F - G <-- sub
\
H <-- two
作为您的(整个)存储库,其中每个单个字母代表一个提交,并且有三个命名分支(main
,sub
和two
),我(或您)可以这样做:
$ git branch foo main~1
现在有一个新的分支标签foo
指向提交C
:
D <-- main
/
A - B - C <-- foo
\
E - F - G <-- sub
\
H <-- two
请注意,提交图完全相同,我只是将D
提升到一个新行,以便更容易看到新标签foo
指向的位置。
此处还需要注意的是,分支标签(如果存在)会附加到特定的提交,而不是(另一个)标签。 (有一个间接标签这样的东西,但通常只适用于HEAD
,如果你试图将它用作“分支”,它就不会真正起作用。)什么使本地分支标签成为“分支” “当你进行新的提交时,git会移动 这个 你。
我认为Zeeker is suggesting in a comment在您即将进行新提交时(在实际执行之前,即在pre-commit
挂钩中)要检查:
写钩子有点棘手。如果您有使用shell脚本(sh / bash)的经验,那么这会有所帮助。您可以使用任何您喜欢的语言,但最容易从shell脚本运行git命令。在任何情况下,您都会进入所有git“plumbing”命令,因为它们意味着要从脚本中使用 - 事实上,许多git命令只是调用管道命令的shell脚本。 (例如,git stash
和git rebase
的变体都只是脚本。)
这是一个完全未经测试的大纲。我们从一些样板开始......
#! /bin/sh
#
# Are we on a branch, or do we have a detached HEAD?
# If detached HEAD, just exit 0 = ok to commit. Otherwise
# set $branch to the short name of the branch.
branch=$(git symbolic-ref -q --abbrev-ref HEAD) || exit 0
现在我们检查新提交的分支,即新提交要移动哪个标签,如果允许的话(退出0)。 (退出1,显示有关禁止提交的原因的消息,以防止提交。)您可能希望修改此部分,例如,检查ticket- *作为名称。对于其余部分,我只需要验证该名称既不是master
也不是preprod
。我还需要比较master
和preprod
的SHA-1,因为如果它们相同,则规则“必须是master
的后代而不是preprod
的后代”是几乎不可能满足(master
的所有后代也是preprod
的后代(根据定义)。
case "$branch" in
master) exit 0;; # branch master, just allow (or deny=exit 1)
preprod) exit 0;; # branch preprod, just allow
esac # all others: fall through to next checks
# Must be a ticket branch.
# If there are no branches named preprod and master, skip all this.
# Otherwise, get their SHA-1 values.
master=$(git rev-parse -q --verify preprod) || exit 0
preprod=$(git rev-parse -q --verify master) || exit 0
# Ensure that the new commit, once made, will not be a descendant
# of branch preprod, and will be a descendent of master. That is,
# if we add a new commit node to the graph, ancestors(new) will
# NOT include the commit labeled preprod but WILL include the
# commit labeled master.
#
# Note that if preprod and master point to the *same* commit,
# this condition can never be satisfied. In this particular
# case (preprod == master) just allow the commit. (We're not
# committing *on* master or preprod -- we checked that above --
# so this commit will not move either of *those* two labels.)
[ $master = $preprod ] && exit 0
最后,我们执行规则:
# A new commit's immediate parent will be whatever the SHA-1 of HEAD
# is, so we simply check whether HEAD satisfies our conditions.
if git merge-base --is-ancestor $preprod HEAD; then
echo "ERROR: new commit will be a descendent of" 1>&2
echo " branch preprod (commit $preprod)" 1>&2
exit 0
fi
if ! git merge-base --is-ancestor $master HEAD; then
echo "ERROR: new commit will not be a descendent of" 1>&2
echo " branch master (commit $master)" 1>&2
exit 0
fi
上述检查中有一些漏洞。最明显的是当master
和preprod
都指向相同的提交时:
... - o - o - o <-- master, preprod, branch3
\
o - o <-- branch4
如果您在branch3
(HEAD
为branch3
)并添加新提交,则会获得:
o <-- HEAD=branch3
/
... - o - o - o <-- master, preprod
\
o - o <-- branch4
这就是你必须考虑如何处理这个问题:所有这些操作,在git中,修改提交图,并拖动当前分支标签(其中包含HEAD
)和它们。 (在“分离的HEAD”情况下,HEAD
直接指向提交,而不是指向分支标签,而分支标签又指向提交。您可以在此时或任何时候添加新标签,并使用HEAD
使git checkout -b
指向它。)
不太明显但更大的是洞knittl mentioned in a comment:git rebase
会将一系列提交复制到新系列,然后移动标签;并且git reset
命令按指示移动标签(同时还根据参数更新索引和工作树)。