我在理解如何在git中使用标签和分支时遇到了一些困难。
我刚刚将我们代码的当前版本从cvs移动到git,现在我将针对特定功能处理该代码的子集。其他一些开发人员也将致力于此,但并非我们小组中的所有开发人员都会关心此功能。我应该创建分支还是标签?在什么情况下我应该使用一种与另一种?
从理论上讲:
从技术角度:
refs/tags/
命名空间中,并且可以指向标签对象(带注释的和可选的 GPG 签名标签)或直接指向提交对象(用于本地名称的较少使用的轻量级标签),或者在极少数情况下甚至指向树对象或blob 对象(例如 GPG 签名)。refs/heads/
命名空间中,并且只能指向提交对象。HEAD
指针必须指向一个分支(符号引用)或直接指向一个提交(分离的 HEAD 或未命名的分支)。refs/remotes/<remote>/
命名空间中,并遵循远程存储库中的普通分支<remote>
。另请参阅gitglossary联机帮助页:
分支
“分支”是一条活跃的发展路线。分支上的最新提交称为该分支的尖端。分支的尖端由分支头引用,随着在分支上完成额外的开发,它会向前移动。单个 git 存储库可以跟踪任意数量的分支,但您的工作树仅与其中一个关联(“当前”或“签出”分支),并且 HEAD 指向该分支。
标签
指向标签或提交对象的 ref。与头相比,提交不会更改标签。标签(不是标签对象)存储在
$GIT_DIR/refs/tags/
. [...]。标签最常用于标记提交祖先链中的特定点。标记对象
包含指向另一个对象的 ref 的对象,该对象可以像提交对象一样包含消息。它还可以包含(PGP)签名,在这种情况下,它被称为“签名标签对象”。
一个标签代表一个特定分支在某一时刻的一个版本。一个分支代表一个单独的开发线程,它可以与同一代码库上的其他开发工作同时运行。对分支的更改最终可能会合并回另一个分支以统一它们。
通常你会标记一个特定的版本,以便你可以重新创建它,例如,这是我们发给 XYZ Corp 的版本。一个分支更多的是一种策略,在继续对其进行开发的同时,对特定版本的代码进行持续更新。您将创建交付版本的分支,继续在主线上进行开发,但对代表交付版本的分支进行错误修复。最终,您会将这些错误修复合并回主线。通常你会同时使用分支和标记。您将拥有各种标签,这些标签可能适用于主线及其分支,标记您可能想要重新创建的每个分支的特定版本(例如,那些交付给客户的版本)——用于交付、错误诊断等。
它实际上比这更复杂——或者像你想要的那样复杂——但这些例子应该让你了解它们之间的区别。
如果您将存储库视为记录项目进度的书...
您可以将分支视为那些粘性书签之一:
一个全新的存储库只有其中一个(称为master
),它会自动移动到您编写的最新页面(想想commit )。但是,您可以自由创建和使用更多书签,以便标记书中的其他兴趣点,以便您快速返回它们。
此外,您始终可以将特定书签移动到书籍的其他页面(git-reset
例如,使用 );兴趣点通常会随着时间而变化。
您可以将标签视为章节标题。
它可能包含标题(想想带注释的标签)或不包含。标签与分支相似但不同,因为它标志着书中的历史兴趣点。为了保持它的历史特征,一旦你共享了一个标签(即把它推送到一个共享的遥控器上),你就不应该把它移到书中的其他地方。
来自 CVS 的您需要意识到的是,您在设置分支时不再创建目录。
不再有“粘性标签”(只能应用于一个文件)或“分支标签”。
分支和标签是 Git 中的两个不同对象,它们始终适用于所有repo。
您将不再(这次使用 SVN)必须明确地构建您的存储库:
branches
myFirstBranch
myProject
mySubDirs
mySecondBranch
...
tags
myFirstTag
myProject
mySubDirs
mySecondTag
...
这种结构源于 CVS 是修订系统而不是版本系统这一事实(请参阅源代码控制与修订控制?)。
这意味着通过 CVS 的标签和 SVN 的目录副本来模拟分支。
如果您习惯于签出标签并开始在其中工作,那么您的问题是有意义的。
您不应该这样做;)
标签应该代表不可变的内容,仅用于访问它并保证每次都获得相同的内容。
在 Git 中,修订历史是一系列提交,形成一个图表。
分支是该图的一条路径
x--x--x--x--x # one branch
\
--y----y # another branch
1.1
^
|
# a tag pointing to a commit
有关所有技术细节,请参阅Jakub Narębski 的回答,但坦率地说,在这一点上,您不需要(还)所有细节;)
要点是:标签是指向提交的简单指针,您将永远无法修改其内容。你需要一个分支。
就您而言,每个开发特定功能的开发人员:
您可以只跟踪一个“官方”中央存储库的分支,而不是直接跟踪您的同事的分支,每个人都将他/她的工作推送到该分支,以便集成和共享每个人针对该特定功能所做的工作。
树枝是用木头做的,从树干上长出来。标签由纸(木材的衍生物)制成,像圣诞装饰品一样悬挂在树上的各个地方。
您的项目就是树,您将添加到项目中的功能将在一个分支上生长。答案是分支。
看起来最好的解释方式是标签充当只读分支。您可以将分支用作标签,但您可能会无意中使用新提交对其进行更新。只要标签存在,就保证它们指向同一个提交。
我喜欢把分支想象成你要去的地方,把标签想象成你去过的地方。
一个标签感觉就像是过去某个特定重要点的书签,例如版本发布。
而分支是项目正在下降的特定路径,因此分支标记会随着您前进。完成后,您合并/删除分支(即标记)。当然,此时您可以选择标记该提交。
标签可以是有符号或无符号的;分支永远不会签名。
签名标签永远不能移动,因为它们被加密绑定(带有签名)到特定的提交。未签名的标签未绑定并且可以移动它们(但移动标签不是正常的用例)。
分支不仅可以移动到不同的提交,而且应该这样做。您应该为本地开发项目使用分支。将工作提交到“标签上”的 Git 存储库是没有意义的。
简单的答案是:
分支:当前分支指针随着每次提交到存储库而移动
但
标签:标签指向的提交不会改变,实际上标签是该提交的快照。
Git Parable解释了一个典型的DVCS 是如何创建的,以及为什么他们的创建者做了他们所做的事情。此外,您可能想看看Git for Computer Scientist;它解释了 Git 中每种类型的对象的作用,包括分支和标签。
标签用于标记版本,更具体地说,它引用分支上的时间点。分支通常用于向项目添加功能。
我们用
branches
在dev
功能开发或错误修复的环境中lightweight tags
对于test
功能分支上的环境annotated tags
对于发布/prd(主分支)在每个带注释的标签之后,所有功能分支都从主分支变基。
正如其他人所说,abranch
是一条发展路线,head
随着新提交的到来而向前发展。这是功能开发的理想选择。
Lightweight tag
固定到特定的提交,这使得创建内部版本并让 qa 团队在开发完成后测试功能非常理想。
Annotated tag
非常适合发布到生产,因为我们可以在将测试的功能分支合并到主分支(稳定)时添加正式消息和其他注释。