从标签创建Git分支 - 内部原理

6
在git中,假设我有一个分支master和一个标签0.0.1,如果我执行以下操作:
git checkout 0.0.1
git branch -b random-fix
# some changes...
git checkout master
git merge random-fix

从标签创建的分支,在内部会创建一个对于主分支或者标签所标记的提交的引用,就像标签本身是某种分支一样?
我之所以问这个问题,是因为当我切换到标签时,我会处于“分离头指针”状态,对吧?知道这一点让我想到了从标签创建分支。

1
不喜欢链接,但这个解释得很好:https://dev59.com/r2035IYBdhLWcg3wK8wm#5582368 - Luke Hutton
2
标签只是指向提交的指针。当你从标签分支时,它会从标签指向的提交分支。分支也只是指向提交的指针。Git 实际上并不真正跟踪分支,例如,尝试确定一个提交来自哪个分支可能很困难,并且取决于经过了多长时间,有时是不可能的。 - aet
明白了,基本上,由于标签是分支中提交的指针,所以你实际上是从分支中的某个点进行分支,对吧?(知道标签不可移动/没有头部) - caarlos0
1
@caarlos0 不是的,标签是指向提交的指针,而不是指向“分支中的提交”的指针。提交不在分支中。分支只是指向单个提交的名称。当您创建分支时,它仅指向那个单个提交,并且该提交向后指向其父项。但是,该提交并不是以某种特定方式与该分支“相关联”的,该提交可能在许多分支上; 每个指向可以追溯到该提交的历史记录的提交的分支。 - user229044
2个回答

7

标签参考提交

标签是对提交的引用。具体来说,轻量级标签只是使用用户定义的名称指向给定提交的引用。例如:

$ git log
commit e0e92bc337b246696aec5c214507321c7526c1e9
Author: John Doe <john.doe@example.com>
Date:   Thu Sep 26 14:38:36 2013 -0400

    Empty initial commit.

$ git tag v0.0.1

$ cat .git/refs/tags/v0.0.1 
e0e92bc337b246696aec5c214507321c7526c1e9

实际的SHA-1在两种情况下都是相同的。换句话说,标签只是指向提交的指针。
将标签用作提交标识符
您可以像使用提交标识符一样从标签分支。例如,以下两个命令实际上是相同的:
git checkout -b new_branch v0.0.1 git checkout -b new_branch e0e92bc337b246696aec5c214507321c7526c1e9
对于 Git 来说,新分支的起点是否列为标签、SHA-1 或其他形式的修订选择都没有区别。

5

(这可能应该是一个注释,但太太太大了,需要很好的格式化 :-) ...另外,如果你跟随一些链接,我似乎处于古怪的心情...)

理解这一点的关键 - 我认为你已经明白了,但让我们把它放在SO文章中,以便清楚地告诉下一个读者 - 就是这样:

所有git refs最终都会命名(指向)单个提交。

这适用于标签(无论是轻量级还是注释型),分支,“远程分支”,git“笔记”引用,“stash”等。它们都命名一个提交,并且仅限于此。 (好吧,不完全如此:从技术上讲,标签可以命名存储库中的任何对象。实际上,这就是注释型标签的工作方式:有一个轻量级标签,它命名了一个存储库对象,该对象是注释标签,然后注释标签命名了一个提交对象。这也是HEAD的工作原理:它通常命名另一个ref,然后该ref再命名提交。因此,有时您必须剥离洋葱的几层才能到达提交。命名blob或tree对象是可能的,但通常没有实际这样做。)

(提交的“真实名称”当然是SHA-1值。)

一个分支引用名称变得“特殊”的原因同样简单:
分支引用是一个名称,当添加新的提交时自动移动到新的分支尖端。
具体来说,形式为refs/heads/branchname的引用指向某个提交(根据定义,因为名称指向提交)。当您“在”该分支上并执行一些添加新提交的git操作,例如git commit或git merge或git cherry-pick时,git将新提交插入存储库,然后将名称重新指向新提交。这就是它所要做的!
我们所谓的“分支”是从末尾开始形成的——即名称指向的提交,并向后工作,使用每个提交的父级。如果提交只有一个父级,则它是“分支”上的普通提交。如果有两个或更多,则为“合并”,您可以跟踪所有父级以查找合并了什么。如果根本没有父级,则为根提交(例如新存储库中的初始提交——实际上可以有多个根;例如git“注释”)。
如果您在一个提交上放置了七个分支标签,那么您现在有七个“分支”的名称。当然,将其缩减为一个名称(不那么混乱)会更好,但是 Git 不会关心这一点。(Git 只关心如果您将其缩减到零个名称。现在该分支仍然存在,但很难找到,并且可以进行垃圾 收集。)

既然我们在这个话题上,让我们也注意一下 "remote 分支"。 (我从来没有完全满意过“远程分支”的名称,但我没有更好的名称,所以让我们先定义它。) “远程分支”是一个形式为 refs/remotes/rname/bname本地引用,其中rname是远程的名称(例如origin),bname是远程的分支名称,即,在该远程登录并查看那个分支后面的部分refs/heads/。 你不能“进入”远程分支——如果你git checkout origin/master,git会给你一个"分离的 HEAD" ——但这些分支名称自动更新的:当你使用git fetch从远程获取新提交时,你也会获取新的分支端点。 换句话说,你不需要移动名称到新的分支端点,而是让其他人(在远程上)来做这件事,然后你一次性从他们那里获取最新版本,当你从他们那里获取时。


1要处于“分支”状态,HEAD引用必须是“间接”的引用,类似于ref: refs/heads/master。当HEAD变成“分离头指针”时,它包含原始的SHA-1值。您仍然可以添加提交;它们被添加到一个无标签的分支中。HEAD中的引用会防止它们被垃圾回收。

2或者更多,如果有标签存在。假设没有标签。

3没有第三个脚注。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接