【问题标题】:git checkout <branch> or <commit>git checkout <branch> 或 <commit>
【发布时间】:2018-12-20 01:49:44
【问题描述】:

有什么方法可以获取和检查提供的参数,而不关心它是分支名称还是提交哈希?

git fetch
git checkout origin/<branch> or origin/<commit>

怎么样? git checkout origin/&lt;commit&gt; 说没有这样的分支。一个简单的git checkout &lt;argument&gt; 是不可能的,因为它不会像git checkout origin/&lt;argument&gt; 那样检查远程分支。

【问题讨论】:

  • 好吧,你可以总是默认签出提交。毕竟,origin/some_branch 也有一个提交的头部。
  • 这是一个较大项目的一部分,用户可以提供分支名称或提交 ID 作为参数。
  • 为什么在工作树中获取分支与获取提交哈希有如此大的不同? git fetch &amp;&amp; git checkout &lt;commit-hash&gt; 就足够了,但是对于一个分支,你也必须合并它:git fetch &amp;&amp; git checkout &lt;branch&gt; &amp;&amp; git merge origin/&lt;branch&gt;.

标签: git


【解决方案1】:

您可以先获取 所有 个远程分支(自动包括具有您所需提交的分支)到您的本地:

git fetch -a

然后简单地通过其哈希检查提交:

git checkout &lt;commit-hash&gt;

首先需要fetch,否则您的本地人可能知道也可能不知道远程上的提交哈希。如果没有fetch,直接执行checkout会报错commit-hash无效。

【讨论】:

  • 唉,我无法确定提供的参数是提交哈希还是分支名称。
  • git fetch -a 表示从所有远程获取。例如,如果您有多个远程名称并且想要git fetch origin &amp;&amp; git fetch upstream,则只需要-a
【解决方案2】:

你问题的前提是错误的:

一个简单的git checkout &lt;argument&gt; 是不可能的,因为它不会像git checkout origin/&lt;argument&gt; 那样检查远程分支。

了解关于 Git 的几个相互关联的事情很重要:

  1. 总是——嗯,几乎总是——当前提交,您可以使用单词 HEAD 来查找。
  2. 当前分支并不总是存在的,但如果有,它就是一个分支名称,即一个全名格式为refs/heads/<em>name</em> 的引用。同一个词—HEAD,全部大写,找到该分支名称。如果没有当前分支名称,Git 将其称为分离的 HEAD
  3. 远程跟踪名称,例如origin/master,不是分支名称。它的完整形式以refs/remotes/ 开头,而不是refs/heads/
  4. 如果您告诉git checkout 签出一个提交,但用 branch 名称以外的其他名称来标识它,Git 将——如果签出成功,即——生成 detached第 2 点中描述的 HEAD 状态。(您也可以使用git checkout --detach 生成具有分支名称的相同状态。)

上面第 4 点的结果是 git checkout origin/<em>name</em> 导致分离的 HEAD,与 git checkout <em>hash-ID</em> 的方式相同。

这意味着你的脚本可以只使用git checkout &lt;argument&gt;,因为它会做同样的事情——产生一个分离的HEAD——如果参数是一个哈希ID或者它是一个远程跟踪名称,比如origin/develop

但是,请注意,如果我们将此语句更改为:

简单的git checkout &lt;argument&gt; 是不合适的,因为它不会像git checkout &lt;argument minus the leading origin/ part&gt; 那样首先基于现有的远程跟踪名称创建然后签出本地分支。

我们得到了一个真实的陈述:git checkout develop创建一个名为 develop 的新(本地)分支使用名称 origin/develop(当然,前提是,那个本地的develop 还不存在)。但是,在这里只允许&lt;argument&gt; 并让用户提供develop 作为名称并没有明显的问题:

#! /bin/sh
git fetch && git checkout "$@"

例如。

旁注

这里的第 1 点和第 2 点有一个有趣的结果,即询问 目前HEAD 的值是多少实际上是在询问两个不同中的一个问题:

  • HEAD 是否附加到分支?如果有,是哪个分支?
  • 当前提交的哈希 ID 是多少?

git symbolic-ref HEAD 命令只回答第一个问题; git rev-parse HEAD 主要回答第二个,但也可以被告知也回答第一个。


在上面的第 1 点中,几乎出于特定原因。想象一下,您刚刚创建了一个新的、完全空的存储库。 此存储库中没有提交,那么当前提交是哪个提交?

这种情况对于 Git 来说是有问题的。你在一个分支上,即master不存在。 Git 将此称为 孤立分支尚未创建的分支(取决于 Git 的哪个部分正在执行调用)。 Git 处理这个问题的方式是将分支的 name 存储到 .git/HEAD 中,而不是在参考数据库中实际创建 分支本身。当您进行新提交时,that 会自行创建分支,现在问题已解决:您在分支上,它标识了刚刚进行的一个新提交,即当前提交,所以HEAD 命名当前提交当前分支。

(Git 可以根据需要重新创建这种稍微有点苦恼的情况,使用git checkout --orphan,它将新分支的名称写入HEAD,而不实际创建新分支。)

【讨论】:

  • 问题是我们不希望用户提供origin/前缀,如origin/some-branch,而只是写some-branch隐含origin/。
  • 在这种情况下,你就属于第二类,前提是调整后:使用提供的参数。
  • 简单的git fetch &amp;&amp; git checkout &lt;branch-or-commit&gt;branch 没有 origin/ 前缀)的问题在于,在commit 的情况下它可以工作 - 磁盘上的代码是获取的代码,但是如果是branch,磁盘上的代码仍然保持不变,需要像git merge 这样的进一步步骤。好吧,我想要的只是为提供的分支和提交提供相同数量的步骤,因为无法区分它们。
  • 所以,听起来您希望您的脚本: (1) 运行 git fetch; (2) 运行git checkout &lt;branch-name&gt;; (3) 依赖上游设置运行git merge --no-ffgit merge --ff-onlygit merge。 (进行哪种合并,何时以及为什么,取决于您想要什么样的结果。)最终,看起来您提出了错误的问题,可能是过度简化了实际问题。见minimal reproducible example
  • 这对分支名称和提交哈希都有效吗?嗯,让我试试,谢谢。
【解决方案3】:

这个巧妙的技巧似乎正是我想要的:

git show-ref --head --sha | grep -q ^argument

如果参数是提交哈希,它的退出代码将为 0,否则为 1。取自这里https://stackoverflow.com/a/29707340/3116571

【讨论】:

  • 首先,找出参数是否是提交哈希:git show-ref --head --hash | grep -q ^argument(除非我们创建与提交哈希相同的分支名称,这不太可能)。所以我可以运行git fetch &amp;&amp; git checkout $argument,然后如果它是一个提交哈希,不要做任何其他事情,否则做git merge origin/$argument。怎么样?
猜你喜欢
  • 2012-04-17
  • 2014-09-11
  • 2017-02-15
  • 2021-04-14
  • 2011-10-01
  • 2011-12-20
  • 2020-03-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多