我想编写一个bash函数,将一个标签/分支检出为具有该名称的分支。有办法吗?
我尝试了以下操作,但是它仅适用于标签,不适用于分支。
git checkout -b {1} {2}
这是我看到的:
$ git checkout -b v1.0 v1.0 # works
$ git checkout -b release release # fails
[ fatal: Cannot update paths and switch to branch 'release' at the same time. ]
$ git checkout -b release origin/release # works if i prefix origin
$ git checkout release # works if i don't give anything
我可以将其合并为一个命令吗?
答案 0 :(得分:2)
$ git checkout -b release release
这意味着:在release
指定的提交处创建一个新分支release
。因为必须有一个名为release
的分支才能起作用,所以无法创建它,并且命令失败。
摘自 git-checkout 文档:
git checkout -b|-B <new_branch> [<start point>]
指定 -b 会导致创建新分支,就像调用 git-branch(1)然后将其检出一样。 […]
如果给出了 -B ,则创建
<new_branch>
(如果不存在);否则,将被重置。这是等价交易$ git branch -f <branch> [<start point>] $ git checkout <branch>
也就是说,除非“ git checkout”成功,否则不会重置/创建分支。
因此,您可以尝试使用-B
而不是-b
。
答案 1 :(得分:1)
使用mkrieger1's answer中描述的方法-B
是可行的,但是总的来说,这不是一个好主意。如果我们创建一个分支名称来遮盖标签名称,则会导致问题。
在这种情况下,首先检查各种错误情况可能是最明智的选择。如果存在这些错误情况,请停止并寻求他人的帮助。仅在一切正常的情况下进行。在这里,当分支名称已经作为分支名称存在并且您可以切换到它时,或者当没有分支名称但是有一个已知的起点使用时,就会发生一切正常的情况一些 other 名称,您可以创建分支并切换到该分支。
换句话说,您可能应该允许git checkout existing-branch
和git checkout -b new-branch start-point
。在这里, existing-branch
应该要求该名称为分支名称。同样, new-branch
变体应要求 start-point
是一个不同于的名称 new-branch
,并且该 new-branch
不是现有的有效标签或其他名称。允许/要求这些需要一些if / else样式逻辑。
假设您有一些短名称字符串 S ,其中 S 可能是release
或dev
或v1.2
或其他名称可能是这样(我们可以假设 S 不是原始哈希ID,尽管我们可以实际对其进行测试)。有了这个 S 和一个存储库,至少要针对这个特定问题,要考虑两种情况:
git rev-parse S
产生一些哈希ID。 S 可能是现有名称。它可以是分支名称,因此 S 是refs/heads/S
的缩写,也可以是标签名,因此 S 是refs/tags/S
的缩写:
$ git rev-parse stash-exp
8dbdf339cd2e757143d9f222f662edd8ef745ea8
因此,stash-exp
可能是分支或标记。
或者,git rev-parse S
不会产生一些哈希ID,但是会失败:
$ git rev-parse gronk
gronk
fatal: ambiguous argument 'gronk': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
git rev-parse
的这种特殊用法可以将名称本身打印到stdout,并将上面的fatal
消息打印到stderr(您可以通过将stdout和stderr重定向到两个不同的文件来进行自我测试)。添加--verify
会将其更改为仅抱怨(并退出非零=失败):
$ git rev-parse --verify gronk && echo ok || echo failed
fatal: Needed a single revision
failed
git rev-parse --verify stash-exp
继续工作时(退出0 =成功):
$ git rev-parse --verify stash-exp && echo ok || echo failed
8dbdf339cd2e757143d9f222f662edd8ef745ea8
ok
请注意,我们可以改为git rev-parse
告诉我们引用的全名,否则将失败:
$ git rev-parse --verify --symbolic-full-name stash-exp
refs/heads/stash-exp
$ git rev-parse --verify --symbolic-full-name gronk
fatal: Needed a single revision
由此可见,stash-exp
实际上是一个分支名称。同时v2.1.0
是标签名:
$ git rev-parse --verify --symbolic-full-name v2.1.0
refs/tags/v2.1.0
在这里值得注意的是,在更多情况下git rev-parse
可以将名称转换为哈希ID,并且并非所有这些都是符号引用。 The gitrevisions documentation包含名称可用的完整列表,包括诸如相对操作之类的内容:HEAD~3
,master^2~2
等。使用--symbolic-full-name
可以让git rev-parse
告诉我们完整名称,然后可以将其与我们关心的一个或多个模式匹配:
# check whether $name is a branch name
hash=$(git rev-parse --verify "$name" 2>/dev/null) || {
echo "I do not recognize $name at all" 1>&2
exit 1
}
fullname=$(git rev-parse --verify --symbolic-full-name "$name" 2>/dev/null) || {
echo "I can translate $name to $hash but it is not any branch or tag name" 1>&2
exit 1
}
case "$fullname" in
refs/heads/*) ;; # ok - it is a branch name
*) echo "$name is really $fullname and that is not a branch" 1>&2
exit 1;;
esac
因此,以上代码片段验证了$name
是否已设置为现有分支名称。它计算当前保存在$hash
中的技巧提交哈希ID及其全名,由于rev-parse规则,其全名将为refs/heads/$name
,并保存在$fullname
中
好吧,要注意的一个原因是git checkout
在分支名称和标记名称之间的行为有所不同:
$ git checkout stash-exp
Switched to branch 'stash-exp'
由于stash-exp
是分支,因此我们在其上。
$ git checkout v2.1.0
Note: checking out 'v2.1.0'.
You are in 'detached HEAD' state. ... [massive snip]
由于v2.1.0
是不是分支,因此我们现在位于 no 分支。取而代之的是,Git切换到了这种分离式HEAD模式。
$ git checkout master
Previous HEAD position was 6c4ab27f23 Git 2.1
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
现在我们回到master
,它是 的一个分支。因此,是否给git checkout
指定分支名称很重要,这可能很重要。
让我们现在使用git checkout -b
创建一个分支名称,该名称是 same 短名称 S 作为我们的标签名称v2.1.0
:
$ git checkout -b v2.1.0 v2.1.0
Switched to a new branch 'v2.1.0'
$ git rev-parse v2.1.0
warning: refname 'v2.1.0' is ambiguous.
7452b4b5786778d5d87f5c90a94fab8936502e20
嗯,这个含糊的东西是新的,不是吗?让我们创建一个新的虚拟提交:
$ git commit --allow-empty -m dummy
[v2.1.0 83429187cf] dummy
$ git show | sed 's/@/ /'
commit 83429187cfe0ff9055453b8c2284deabb21139aa (HEAD -> v2.1.0)
Author: Chris Torek <chris.torek@gmail.com>
Date: Sat Feb 23 11:44:29 2019 -0800
dummy
$
所以我们分支的最重要的提交就是这个新的提交83429187...
。
但是:
$ git show v2.1.0 | sed -e 's/@/ /' -e 10q
warning: refname 'v2.1.0' is ambiguous.
tag v2.1.0
Tagger: Junio C Hamano <gitster pobox.com>
Date: Fri Aug 15 15:09:28 2014 -0700
Git 2.1
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
iQIcBAABAgAGBQJT7oUZAAoJELC16IaWr+bLD3UP/iqk7c+1BdEjIUks3JS8eUu7
V/sU1dS2K/8ZeeQa9aeqmAxt/9aqeF6DNtN9AcAO5bf2WeGYfKkTdxsb4eWAaw+W
$
名称v2.1.0
有时表示标签,有时又表示分支。这就是这个“模棱两可”的警告。如果您回到我先前链接的the gitrevisions documentation,您会看到解析符号名的过程分为六步S 为哈希ID。使用 tag 名称的步骤位于使用 branch 名称的步骤之前。
这实际上意味着,很大一部分Git都倾向于使用标签而不是分支。例外情况是知道或假设其参数为< em>分支名称,例如git checkout
和git branch
。但这反过来又意味着,如果您创建了含糊不清的名称情况,就会惹上麻烦。
让我们切换回master
并删除这个不太好的分支名称:
$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
$ git branch -D v2.1.0
Deleted branch v2.1.0 (was 83429187cf).
git checkout -B release release
不是一个好计划的原因要使git checkout -B release release
工作,release
必须是提交的有效符号名。也就是说,git rev-parse release
必须有效(实际上,git rev-parse release^{commit}
必须有效,尽管我们不需要在这里详细介绍该细节)。
如果 S (代表release
或stash-exp
或其他名称)是有效的符号名称,则一种可能是 S 是现有分支名称。但是,在这种情况下,git checkout -B S S
的操作是将 S 设置为 S 中已经存在的值。这是无害的,但也没有意义: S 已经是一个分支名称,我们可以只运行git checkout S
。
如果这是一个有效的符号名称,因此它可以成为一个分支名称,但是现在不是一个分支名称,则可能是一个标签名称。在这种情况下,git checkout -B S S
将创建 S 作为新的分支名称。我们也可以在这里使用git checkout -b S S
,因为 S 还不是 分支名称,正如上面v2.1.0
所见,小写字母-b
选项有效。但这会导致警告:refname'v2.1.0'是不明确的情况:现在我们既有分支又有标签。如果我们认为这是一个坏主意-并且至少 I 如此认为-那么我们根本就不应该这样做。
这里的结论是,我们应该使用带有{em>现有分支名称的git checkout
,将切换为,否则我们应该使用{{ 1}},具有 new 分支名称,该分支名称尚未以其他任何形式使用,因此git checkout -b new-branch start-point
表示我不知道该名称 。 git rev-parse --verify
告诉Git使用什么提交来启动新分支,由于必须将其解析为实际的提交哈希,因此它不能与新的分支名称相同:我们肯定知道新名称根本不会解析为任何东西。