如果在公司内部只有一个共用的 Git 远端储存库,大家都有存取权限的情况下,可能会遇到一些协同作业上的问题,那就是不同人彼此之间的程式码互相干扰的情况。例如你在团队开发的过程中,被指派负责开发新功能,但同时间其他同事负责修正目前上线的程式错误,如果两人共用同一个分支 (例如 master 分支),那麽在版控的过程中就很容易发生衝突的情况,这时你应该要善加利用分支将不同用途的原始码分别进行版本管理。
master
我在 GitHub 上建立了一个 sandbox-multi-branch 专案,并直接在 GitHub 上建立一个 Initial commit 版本,用做本篇文章的远端储存库。但这次我改用 git@github.com:doggy8088/sandbox-multi-branch.git 这个网址,透过 SSH 通讯协定来存取我的远端储存库,好让我可以不用每次执行远端储存库的操作时都要输入帐号密码。
sandbox-multi-branch
git@github.com:doggy8088/sandbox-multi-branch.git
我们执行以下指令将专案複製回来:
C:\>git clone git@github.com:doggy8088/sandbox-multi-branch.git Cloning into 'sandbox-multi-branch'... Receiving objecg objects: 3, done.Receiving objects: 33% (1/3) Receiving objects: 100%0), reused 0 (delta 0)ts: 66% (2/3) Receiving objects: 100% (3/3), done. C:\>cd sandbox-multi-branch C:\sandbox-multi-branch>git log commit 6eeee883275e3d5e0281767aca4f456d952fa682 Author: Will 保哥 <xxx@gmail.com> Date: Sun Oct 27 07:13:50 2013 -0700 Initial commit C:\sandbox-multi-branch>git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/master
此时我们的 .git\config 内容如下:
.git\config
[core] repositoryformatversion = 0 filemode = false bare = false logallrefupdates = true symlinks = false ignorecase = true hideDotFiles = dotGitOnly [remote "origin"] url = git@github.com:doggy8088/sandbox-multi-branch.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "master"] remote = origin merge = refs/heads/master
我们知道在建立好一个新的 Git 储存库时,预设都会有一个 master 分支。在实务上,这个分支通常用来当作目前系统的「稳定版本」,也就是这个版本必须是乾淨且高品质的原始码版本。所以,我们会要求所有人都不要用这个分支来建立任何版本,真正要建立版本时,一定会透过「合併」的方式来进行操作,以确保版本能够更容易被追踪。
进入开发阶段时,我们通常会再从 master 分支建立起另一个 develop 分支,用来作为「开发分支」,也就是所有人都会在这个分支上进行开发,但这个时候或许会产生一些衝突的情形,因为大家都在同一个分支上进行版本控管。不过这种用法跟以往我们用 Subversion 的时候是比较类似的,所以使用上的观念通常差不多。本文稍后就会介绍一些分支开发的练习,但我们现在就可以先建立一个 develop 分支起来。
develop
C:\sandbox-multi-branch>git branch * master C:\sandbox-multi-branch>git checkout -b develop Switched to a new branch 'develop' C:\sandbox-multi-branch>git branch * develop master
开发过程中,有时候我们也会因为需求变更,而被指派开发一些新功能,但这些新功能可能变动性还很大,甚至只是想进行 PoC 验证而开发的小功能。这些小功能只是测试用途,你不想因为开发这些测试功能而影响到大家的开发作业,所以这时我们会选择再建立起一个「新功能分支」,专门用来存放这些新增功能的程式码版本。这个测试用的「功能分支」,通常会建立在 develop 之上,所以我们会再从 develop 分支来建立另一个分支。但这个分支的名称,实务上通常会取名为 feature/[branch_name],例如:feature/aspnet_identity。
feature/[branch_name]
feature/aspnet_identity
C:\sandbox-multi-branch>git branch * develop master C:\sandbox-multi-branch>git checkout -b feature/aspnet_identity Switched to a new branch 'feature/aspnet_identity' C:\sandbox-multi-branch>git branch develop * feature/aspnet_identity master
如果你发现在开发的过程中,「正式机」 (生产环境) 的系统出现了一个严重错误,但在「开发分支」裡又包含一些尚未完成的功能,这时你可能会从 master 分支紧急建立一个「修正分支」,通常的命名为 hotfix/[branch_name],例如:hotfix/bugs_in_membership。
hotfix/[branch_name]
hotfix/bugs_in_membership
C:\sandbox-multi-branch>git branch develop * feature/aspnet_identity master C:\sandbox-multi-branch>git checkout master Switched to branch 'master' C:\sandbox-multi-branch>git branch develop feature/aspnet_identity * master C:\sandbox-multi-branch>git checkout -b hotfix/bugs_in_membership Switched to a new branch 'hotfix/bugs_in_membership' C:\sandbox-multi-branch>git branch develop feature/aspnet_identity * hotfix/bugs_in_membership master
如果你发现目前的 master 分支趋于稳定版本,那麽你可能会想替目前的 master 分支建立起一个「标籤物件」或称「标示标籤」(annotated tag),那麽你可以先切换到 master 分支后输入 git tag 1.0.0-beta1 -a -m "V1.0.0-beta1 created" 即可建立一个名为 1.0.0-beta 的标示标籤,并透过 -m 赋予标籤一个说明讯息。
git tag 1.0.0-beta1 -a -m "V1.0.0-beta1 created"
1.0.0-beta
-m
C:\sandbox-multi-branch>git branch develop feature/aspnet_identity * hotfix/bugs_in_membership master C:\sandbox-multi-branch>git checkout master Switched to branch 'master' C:\sandbox-multi-branch>git tag 1.0.0-beta1 -a -m "V1.0.0-beta1 created" C:\sandbox-multi-branch>git tag 1.0.0-beta1
以上就是使用 Git 的过程中常见的命名规则与版控流程。
目前为止我们建立了好几个分支与标籤,用 SourceTree 来看,目前还看不出分支的版本线图,毕竟我们还没有建立任何版本,但该有的分支已经被成功建立,如下图示:
不过,这些分支都仅储存在本地储存库中,团队中所有其他人都无法得到你建立的这些分支,如果要将这些分支的参照名称推送到远端储存库,可以使用 git push --all 这个指令。
git push --all
C:\sandbox-multi-branch>git push --all Total 0 (delta 0), reused 0 (delta 0) To git@github.com:doggy8088/sandbox-multi-branch.git * [new branch] develop -> develop * [new branch] feature/aspnet_identity -> feature/aspnet_identity * [new branch] hotfix/bugs_in_membership -> hotfix/bugs_in_membership
不过如果只下达 --all 参数是不够的,可能还要加上 --tags 参数,才能将标示标籤也一併推送到远端储存库。
--all
--tags
C:\sandbox-multi-branch>git push --tags Counting objects: 1, done. Writing objects: 100% (1/1), 159 bytes | 0 bytes/s, done. Total 1 (delta 0), reused 0 (delta 0) To git@github.com:doggy8088/sandbox-multi-branch.git * [new tag] 1.0.0-beta1 -> 1.0.0-beta1
这个时候,所有物件与参照名称都已经储存在远端储存库了。我们连到 GitHub 就能看到这些物件已经可以被浏览到:
如果切换到 Tags 页籤的话,也可以看到标籤物件也被送上来了:
这个时候大家就能够透过 git fetch --all --tags 将所有物件取回,包含所有物件参照与标籤参照。
git fetch --all --tags
我们建立起另一个工作目录,模拟其他使用者取回资料的情况:
C:\>git clone git@github.com:doggy8088/sandbox-multi-branch.git sandbox-multi-branch-user2 Cloning into 'sandbox-multi-branch-user2'... remote: Counting objects: 4, done. remote: Compressing objects: 100% (2/2), done. remote: Total 4 (delta 0), reused 1 (delta 0) Receiving objects: 100% (4/4), done. C:\>cd sandbox-multi-branch-user2 C:\sandbox-multi-branch-user2>git fetch --all --tags Fetching origin
取回物件后,用 SourceTree 查看储存库的状态如下:
现在开始,团队所有成员都拥有了预先定义好的「Git 储存库范本」,大家就能各就各位,开发自己需要开发的功能。或许会有两个人在 develop 分支上进行开发,或许会有一个人被指派 hotfix/bugs_in_membership 分支进行修复任务,诸如此类的,等分支完成开发后,再将变更推送到远端储存库裡。
眼尖的你可能会发现,这个 User2 的本地分支只有 master 而已,跟我们原本建立的那个工作目录有些不一样。之前在【第 25 天:使用 GitHub 远端储存库 - 观念篇】文章中不是提到说「把这些「本地追踪分支」视为是一种「唯读」的分支」吗?没有本地分支要怎样进行呢?
User2
关于这一点,各位也不用担心,Git 早就帮我们想好了。假设你现在被赋予的任务是去开发 hotfix/bugs_in_membership 分支,并负责把变更错误修正,你可以直接执行 git checkout hotfix/bugs_in_membership 将这个「本地追踪分支」给取出 (checkout)。
git checkout hotfix/bugs_in_membership
C:\sandbox-multi-branch-user2>git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/develop remotes/origin/feature/aspnet_identity remotes/origin/hotfix/bugs_in_membership remotes/origin/master C:\sandbox-multi-branch-user2>git checkout hotfix/bugs_in_membership Branch hotfix/bugs_in_membership set up to track remote branch hotfix/bugs_in_me mbership from origin. Switched to a new branch 'hotfix/bugs_in_membership' C:\sandbox-multi-branch-user2>git branch -a * hotfix/bugs_in_membership master remotes/origin/HEAD -> origin/master remotes/origin/develop remotes/origin/feature/aspnet_identity remotes/origin/hotfix/bugs_in_membership remotes/origin/master
在取出 hotfix/bugs_in_membership 这个「本地追踪分支」后,Git 会动帮你建立起一个同名的「本地分支」,所以你根本不用担心有没有本地分支的情形。
这时我们模拟在 hotfix/bugs_in_membership 这个「本地分支」建立一个版本:
C:\sandbox-multi-branch-user2>git status # On branch hotfix/bugs_in_membership nothing to commit, working directory clean C:\sandbox-multi-branch-user2>echo %date% %time% > a.txt C:\sandbox-multi-branch-user2>git add . C:\sandbox-multi-branch-user2>git commit -m "Add a.txt" [hotfix/bugs_in_membership 250e907] Add a.txt 1 file changed, 1 insertion(+) create mode 100644 a.txt
目前的版本线图如下:
接著如果你想将变更推送到远端,只要下达 git push origin hotfix/bugs_in_membership 即可将变更推送回去:
git push origin hotfix/bugs_in_membership
C:\sandbox-multi-branch-user2>git push origin hotfix/bugs_in_membership Counting objects: 4, done. Delta compression using up to 8 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 294 bytes | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To git@github.com:doggy8088/sandbox-multi-branch.git 6eeee88..250e907 hotfix/bugs_in_membership -> hotfix/bugs_in_membership
不过目前为止,你所推送回去的,只有 hotfix/bugs_in_membership 这个分支的版本而已,你并没有将变更「合併」回 master 分支。这样操作所代表的意思是,你将变更放上远端储存库,目的是为了将变更可以让其他人看到,也可以取回继续修改,就跟昨天【第 26 天:多人在同一个远端储存库中进行版控】文章中讲的版控流程一样。
如果你想合併回去,可以先切换至 master 分支,再去合併 hotfix/bugs_in_membership 分支的变更,最后在推送到远端储存库。如下指令范例:
C:\sandbox-multi-branch-user2>git branch -a * hotfix/bugs_in_membership master remotes/origin/HEAD -> origin/master remotes/origin/develop remotes/origin/feature/aspnet_identity remotes/origin/hotfix/bugs_in_membership remotes/origin/master C:\sandbox-multi-branch-user2>git checkout master Switched to branch 'master' C:\sandbox-multi-branch-user2>git merge hotfix/bugs_in_membership Updating 6eeee88..250e907 Fast-forward a.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 a.txt C:\sandbox-multi-branch-user2>git push Total 0 (delta 0), reused 0 (delta 0) To git@github.com:doggy8088/sandbox-multi-branch.git 6eeee88..250e907 master -> master
如此一来,master 的分支内容就成功被更新,并且推送到远端储存库了。
想当然尔,其他的分支也都是用类似的方式运作著。
今天更进一步介绍了 Git 更接近实务面的版控流程,让大家透过不同的分支进行开发,彼此之间做一个有效区隔,然后还能在同一个远端储存库中进行版控,同时享受了集中式版本控管的特性,以及分散式版本控管的弹性,非常优秀不是吗! ^_^
我重新整理一下本日学到的 Git 指令与参数:
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8