tag其实就是跟commit id一样的东西,只是我们对这次commit id做了特殊标识。
而通过commit id就可以找回当时的代码,不需要保留相应分支。
产品是按照Relese1.1.1,Relese1.1.2,…等版本进行不断地功能完善和发布的。
假如,产品已经发布到了Relese1.1.2,有一家客户使用的是我们Relese1.1.1的版本。
这时候,在客户现场发现了一个bug,我们需要在Relese1.1.1上修复bug,并创建tag Relese1.1.1.1
假如这个修复bug的分支,不需要merge到现有的release分支上(比如,该功能已经被删除了)
那么,我们基于Relese1.1.1创建并写了修复bug代码的这个分支,是不是可以删除?
我们的开发环境有两个分支:master和release
master作为新功能的开发分支,release作为稳定版本的发布分支。
每次输出稳定版本之后,我们会基于release分支创建一个tag,比如Relese1.1.2。
release分支从来没有删除过,所以我一直以为release是不可以删掉的。
直到有一天,另一个开发说git上的分支太多了,是不是可以删掉。我检查了git上的branch,发现都是项目打补丁的branch
【以“不需要merge回release”举例。release分支当前的版本1.1.2,客户现场版本1.1.1】
git checkout release-1.1.1
切到tag为release-1.1.1的位置,拿到版本1.1.1的代码git checkout -b branchA
用tag为release-1.1.1的代码新建一个分支branchA注意,这里是可以删掉branchA的。
正如我们拿版本1.1.1的代码是基于tag release-1.1.1,根本没有用到分支release一样。下次我们要基于1.1.1.1开发的时候,直接根据tag release-1.1.1.1拉代码即可。
问题是,删掉该tag基于的分支,还能拉到这个tag对应的代码吗?
可以。
我做了实验:
基于master新建了branchA,打了tag a
基于tag a新建了branchB,修改了一些代码,打了tag b
删除branchA,tag a,branchB
然后,
git checkout b
依然可以拉到tag b对应的代码。
tag是跟commit id类似的东西,指向某次提交,是个时间点,所以本来我认为一定要有对应的分支才有意义呀。
可是好像tag,commit id是跟branch类似的东西。它们的区别就是:tag 对应某次commit, 是一个点,是不可移动的。
branch 对应一系列commit,是很多点连成的一根线。
就像另一篇文章里说的:
两者的区别决定了使用方式,改动代码用 branch ,不改动只查看用 tag。
tag 和 branch 的相互配合使用,有时候起到非常方便的效果。
例如:已经发布了 v1.0 v2.0 v3.0 三个版本,这个时候,我突然想不改现有代码的前提下,在 v2.0 的基础上加个新功能,作为 v4.0 发布。就可以检出 v2.0 的代码作为一个 branch ,然后作为开发分支。
其实这样才是合理的。不然我们要是打很多不需要merge回主分支的补丁的话,那git上的分支就越来越多了,这是不太合理的。
其实,commit id的使用也是跟tag一样:删掉branchA之后,branchA上的提交ba2365a,可以通过git checkout ba2365a找回来。
只不过,我们不去记commit id,当删去分支并关掉当前命令窗口后,我们找不到commit id了而已
tag其实就是跟commit id一样的东西,只是我们对这次commit id做了特殊标识。而通过commit id就可以找回当时的代码,不需要保留相应分支。
Git 中的 HEAD 可以理解为一个指针,一般它指向当前工作目录所在分支的最新提交。
如果使用的是 git checkout
,即切换到指定的某一次提交,HEAD 就会处于 detached 状态(游离状态)。
e.g. 切换到某一个tag之后
HEAD的游离状态可以让我们很方便地在历史版本之间互相切换,但是它的弊端就是:在这个基础上的提交会新开一个匿名分支。也就是说我们的提交是无法可见保存的,一旦切到别的分支,游离状态以后的提交就不可追溯了。
所以,在HEAD处于游离状态之后,要新建一个分支保存游离状态后的提交
e.g.