提交标签以及它如何与 Git 存储库的最新版本合并

Commits on Tag and How It Merges with the Latest Version of Git Repository

好吧,我对 Git 不是很熟悉。我被要求从某人的存储库中克隆一个存储库,然后使用该标签。所以这就是我所做的:

git clone someones_repo my_new_repo
git checkout tags/bla_bla_tag -b tag_branch

所以现在我在标签版本中,而不是在 master 分支中,而是在 tag_branch。

我做了更改并想提交它们并将它们与我的主人合并,然后将我的更改提交到我们的官方存储库(我认为他们称之为黄金回购)。这是我的担忧:

  1. 这个 "master" 分支,它包含我克隆的最新版本的 repo 吗?还是包含标签的版本?
  2. 如果 master 是标签的版本,然后我将我的更改合并到它,那么如果我将这些合并的更改提交到我们的官方 repo 会发生什么?官方仓库的最新版本会成为我合并后的版本吗?如果有人试图克隆官方仓库,那么他会得到我的合并版本吗?

当您从 "tag" 创建分支时,您创建了一个指向提交的指针。该标记指向一个提交,而您的分支也就像指向该提交的指针。如果没有更多信息,则无法判断此提交是否在 master 分支上。

要获取最新的母版,您可以执行 git fetch,最新的母版将在 origin/master

例如,如果您根据标记的提交创建了一个提交,您可以通过以下方式交付它:

git checkout master
git pull -r
git cherry-pick <your commit>
git push origin master

用简单的英语就是这个意思。结帐当地的主人。更新本地主机以匹配远程主机。将您的新提交放在本地主控之上,并更新本地主控以指向此提交。将本地主机交付给远程主机。

我不确定 "turn in the changes to the main repo" 是什么意思。我希望它是一个 pull request 而不是直接推送到 golden repo

  1. 你本地分支的 master 将与那个人的 repo 的 master 相同。它可能与黄金仓库的主服务器相同,如果这两个仓库定期保持同步。

  2. 标签只是您添加到某个提交的标签,通常用于 master 中的提交。如果您的拉取请求被合并,golden repo 中的 master 将成为您的 master 版本。如果有人克隆了 golden repo,他们将获得您的请求版本。

我建议为此使用拉取请求工作流。否则丢失提交的几率非常高

已经有一些很好的答案,但您真正可以在这里使用的是图形说明。

理解分支和标签在 Git 中如何工作的关键是要认识到分支和标签 names 仅仅是辅助。他们实际上对 make 分支做的很少。 "branch" 这个词在 Git 中实际上是有歧义的。要查看更多相关信息和几个图表,请单击 What exactly do we mean by "branch"? 这描述了分支如何标记 select 特定提交,尽管不是分支 增长 的过程。 (另请注意,Pro Git 书中的第一张图片很好,但和 Jubobs 一样,我不喜欢第二张 Pro Git 图片。)

我在 Whosebug 帖子中使用了一种更简单的图表方法。以仅包含三个提交的存储库示例图为例。这三个提交中的每一个都有一个实际的哈希 ID——那些丑陋的 40 个字符缩写为 badf00dcafedad 等等的东西之一——但我只是给它们命名 one-letter,并将分支名称放在右边:

A <- B <- C   <-- master

这里 C 是我们在分支 master 上的最新提交。分支 name master 包含提交 C 的实际哈希 ID,例如 ac0ffee4cafedad5badf00d... 或其他。提交 C 本身——存储在 Git 数据库中的实际提交——在其中有提交 B 的 ID。我们说master指向CC指向BB 的 ID 为 A,因此它指向 AA 是任何人所做的第一个提交,因此它不能指向任何地方。它没有parent,用Git的术语来说;这是一个 root commit.

请注意,在这个系统中,parent 提交不知道他们有哪些 children,但是 children do 知道他们的 parent秒。要在 master 上进行新提交,Git 将一些内容写入数据库,并以新提交 object 结束。新提交——我们称它为 D——包含当前提交的 ID 作为其 parent,因此 D 指向 C。然后Git更新分支name,master,使其指向D:

A <- B <- C <- D

(正如最近有人指出的那样,这很像挖掘家谱记录:您会找到类似 "Bob Jones, born to parents Arthur and Sally" 的内容。显然 Bob 的出生记录 不能 列出25 年后他将有一个女儿,所以它没有。同样,当你提交时,Git 知道提交的 parent 是谁, 但它不知道提交是否会有 children.)

考虑到这一点,请考虑这个图形片段。我已经停止绘制内部箭头以保存 space 等,但请记住,它们总是指向 向后 (在这些图中向左):

...--D--E--F   <-- master
      \
       G--H    <-- sidebr

我们通过在 master 上进行两个新提交,即 EF,以及创建一个新分支 name[=146] =], sidebr, 指向提交 D。然后我们 git checkout sidebr 并进行两次新的提交。它们是 GH。当我们创建 G 时,提交 D 是当前的,并且是 sidebr points-to。因此 Git 将 D 作为其 parent 写入提交,并将 sidebr 更改为指向 G。现在 G 是最新的,我们使用 G 作为其 parent 进行新的提交 H,并更新 sidebr 以指向 H,并且这就是我们得到这张图的方式。

标记时间

现在是给这些图片添加标签的时候了。标签 几乎(但不完全)与分支相同。与分支名称一样,标签名称 指向 提交。关键区别1 是分支名称应该 移动:它们通过添加新提交自动增长。标签名称应该移动。

这样一来,我们还不如画他们"inside"commit diagram,而不是在右边的路上。 (或者,如果我们有颜色,我们可以为分支和标签名称使用不同的颜色——但我不能在 Whosebug 上的文本中使用颜色。)所以让我们添加一个指向提交的标签名称 E:

     tag:v0.1
        |
        v
...--D--E--F   <-- master
      \
       G--H    <-- sidebr

此标记指向提交 E。它应该总是,forever-more,指向提交 E.2 关于原始 Git 哈希 ID 有一个有趣的事实与这里非常相关:哈希 ID 是完全确定的,并且完全基于提交的实际内容(或其他 Git object)。因此,标签名称只是为其中一个糟糕的哈希 ID 提供一个 human-readable 名称。如果我们都能记住 17f9f635c101aef03874e1de1d8d0322187494b3,我们就不需要 Git 存储库中的标签 v2.6.0 来表示 Git——但是 我是 当然不会记得了。3

在任何情况下,您都可以 git checkout 通过其 ID 或任何 解析为 它的 ID 的任何提交。标签名称适用于后者,当然更容易记住。因此,鉴于上图,我们可以检查提交 E 与:

git checkout v0.1

tag: 不属于标签名称,只是我写的一些东西来说明为什么我们在这里有一个箭头)。然而,这给了我们一个 "detached HEAD",所以这就是我们 git checkout -b newbranch v0.1 的原因:分配一个新分支 name 来指向提交。现在我们需要 re-draw 稍微调整图表以提供更多空间:

     tag:v0.1
        |
        |  F   <-- master
        v /
...--D--E      <-- newbranch (HEAD)
      \
       G--H    <-- sidebr

我还添加了这个 HEAD 东西:它提醒我们现在 这个新分支上。如果我们现在进行新的提交,这将以通常的方式增加分支:

     tag:v0.1
        |
        |  F   <-- master
        v /
...--D--E--I   <-- newbranch (HEAD)
      \
       G--H    <-- sidebr

不应该移动的标签没有移动。 分支,然而,确实移动。我们可以进行任何我们喜欢的提交,并且每个提交都会使当前分支——newbranch——提前合并我们的每个新提交。


1另一个重要区别是标签名称存在于所有存储库的共享名称 space 中,而分支则不然。还有 带注释的标签 ,让您有机会附加一些未解释的数据,并且 Git 让您使用 GPG 签署 此类标签加密。但这是另一个讨论。

2可以强行移动标签,或者删除它并 re-create 它指向不同的提交。有时甚至有充分的理由这样做。您只需要确定原因特别好,因为标签实际上只是散列 ID 的 human-readable 名称,并且任何已经具有旧标签的存储库都可能认为旧的 tag-to-hash-ID 映射仍然正确,即使在您强行移动标签后也是如此。

3我是用git rev-parse v2.6.0找的。上面的哈希 ID 是带注释的标签 object 的 ID(您可以通过克隆 Git 存储库找到 Git)。实际提交是 be08dee9738eaaa0423885ed189c2b6ad8368cf0。一步找到提交 ID 有特殊的语法,使用 git rev-parse,或者您可以使用 git show v2.6.0 读取标签 object,然后 git show 标签的目标 object 查看提交,例如。

您的问题:

  1. 对于您克隆的repo,只能确定您克隆的时间是最新版本。如果你的同事在你克隆后对他的 repo 进行了一些更改,那么你的本地 repo 不是最新版本。克隆的 repo 包含您同事的 repo 中存在的标签。有一种方法可以通过以下方式检查您的本地存储库是否为最新版本:

git fetch origin git log master..origin/master

如果有输出,说明你的本地master不是最新版本,你应该使用git pull拉取修改。

  1. 如果你使用的tag在master分支上,并且你将你的修改合并到master中,官方repo的最新版本将成为你合并的新版本。之后,如果不克隆官方repo,master分支最新版本也是你合并的版本。

PS: 如果你和你的同事都在为官方仓库工作,你应该首先从官方仓库克隆。