30 天精通 Git 版本控管

 主页   资讯   文章   代码   电子书 

第 26 天:多人在同一个远端储存库中进行版控

一个人用的版本控管,只能算是当作原始码历史备份工具,在大多数的情况下,版本控管机制都是设计给多人共同使用的,尤其是 Git 这套分散式版本控管系统,更是设计给成千上万人都能顺利使用的版本控管工具。不过,在多人使用的情境下,通常多多少少也会带来一些副作用,多跟少的问题。在 Git 版控中,多人同时进行版控的策略有好几种,今天将介绍大家共用一个远端储存库的使用方式与问题解决方法。

建立多人使用的远端储存库与工作目录

我们先假设所有人只会共用一个「远端储存库」,由于大家会用 git clone 指令把远端储存库给複製回来,所以每个人都会有一份拥有完整历史的版本库。

为了简化讲解,我先在本地先建立一个「共用储存库」,把它当成「远端储存库」来用,其用法跟你在用 GitHub 的时候一模一样,不但观念一样,指令操作也都完全相同。我们先用以下指令建立一个共用储存物,并位于 C:/myproject.git 资料夹下:

c:
cd \
mkdir myproject.git
cd myproject.git
git init --bare

再来我们假设有两位开发人员准备开发一个新专案 myproject,分别是 User1User2 这两位。

首先,User1 先利用 git clone C:/myproject.git User1WD 建立一个工作目录,并在工作目录下建立一个初始版本,并推送到 origin 远端储存库。其指令如下:

c:
cd \
git clone C:/myproject.git User1WD
cd User1WD
echo a > a.txt
git add .
git commit -m "Add a.txt"
git push origin master

现在我们的远端储存库 C:/myproject.git 已经有了一个初始版本,并拥有一个 a.txt 档案。

接著,User2 利用 git clone C:/myproject.git User2WD 建立另一个属于 User2 自己的工作目录,预设会自动建立好 origin 远端储存库的设定。其指令如下:

c:
cd \
git clone C:/myproject.git User2WD
cd User2WD

现在我们已经准备好一个「多人」(两人) 使用的版控环境,并共用一个远端储存库。

远端储存库的基本开发流程

现在 User1User2 拥有完全相同的储存库,版本也都完全一样,都只有一个。

现在 User1 先声夺人,抢先建立了版本,而且也将变更推送到 C:/myproject.git 远端储存库:

C:\User1WD>echo b > b.txt

C:\User1WD>git add .

C:\User1WD>git commit -m "Add b.txt"
[master 7bcbc05] Add b.txt
 1 file changed, 1 insertion(+)
 create mode 100644 b.txt

C:\User1WD>git push origin master
Counting objects: 4, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 267 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To C:/myproject.git
   f4f7df9..7bcbc05  master -> master

这时 User2 的工作目录有两个分支,一个是本地的 master 分支,另一个是 origin/master 本地追踪分支。但是 User2 现在的 origin/master 并没有得到远端储存库的最新版,而且 User2 并不知道 User1 已经将他手边的版本推送到远端储存库了,所以还是继续自己的开发作业,也在他自己的工作目录中建立了一个版本。但在准备将版本推送到远端储存库时,发现了一个问题,因为他的推送作业被远端储存库拒绝了!原因就出在存在于远端储存库的初始版本之后,已经拥有了一个新版本,他不允许另外一个人建立一个多重的版本历史,所以拒绝你将本地版本推送上去。

C:\User2WD>echo c > c.txt

C:\User2WD>git add .

C:\User2WD>git commit -m "Add c.txt"
[master dbebba3] Add c.txt
 1 file changed, 1 insertion(+)
 create mode 100644 c.txt

C:\User2WD>git push origin master
To C:/myproject.git
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to 'C:/myproject.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first merge the remote changes (e.g.,
hint: 'git pull') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

image

遇到这种问题请不要紧张,Git 很擅长处裡这种状况。你 (User2) 现在要做的事,就是先把远端储存库中的新物件取回,如下指令:

C:\User2WD>git fetch
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From C:/myproject
   f4f7df9..7bcbc05  master     -> origin/master

image

这时我们可以看到 User2WDorigin/master 这个本地追踪分支的的版本线图,已经移动了一个版本,这代表你已经成功改变了 origin/master 的参照位址到最新的 Add b.txt 这个版本。

现在你要做的则是把 origin/master 版本的变更「合併」回自己的 master 本地分支:

C:\User2WD>git merge origin/master
Merge made by the 'recursive' strategy.
 b.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 b.txt

image

这样你就可以将远端储存库中 master 远端分支的所有版本套用到自己的 master 分支上,也代表你现在可以尝试把本地修改过的变更版本推送到远端储存库了。

C:\User2WD>git push origin master
Counting objects: 7, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 566 bytes | 0 bytes/s, done.
Total 5 (delta 0), reused 0 (delta 0)
To C:/myproject.git
   7bcbc05..32ef41c  master -> master

image

如果这个时候 User2 又再度做出变更,而且 User1 也不知道原来 User2 也送出了一些变更到远端储存库 (在分散式的版本控管系统中,这种状况很常见,毕竟大家并没有坐在同一间办公室),而又建立了一个版本,当然他也无法成功的把变更推送上去。

C:\User1WD>echo d > d.txt

C:\User1WD>git add .

C:\User1WD>git commit -m "Add d.txt"
[master 57ea603] Add d.txt
 1 file changed, 1 insertion(+)
 create mode 100644 d.txt

此时 User1 该做的事,其实跟刚刚 User2 做的事一模一样,也是要先用 git fetch 取回远端储存库中的最新版,然后再用 git merge origin/master 合併回自己的 master 本地分支,最后再用 git push 推送进远端储存库。不过,这次我们改用 git pull 指令帮我们一次做到 git fetchgit merge origin/master 这个动作,这动作相对的会简单很多。

C:\User1WD>git pull
Merge made by the 'recursive' strategy.
 c.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 c.txt

image

最后,我们用 git push origin master 把版本给推送到远端储存库:

C:\User1WD>git push origin master
Counting objects: 7, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 484 bytes | 0 bytes/s, done.
Total 5 (delta 2), reused 0 (delta 0)
To C:/myproject.git
   32ef41c..1ae28db  master -> master

image

就这样不断周而复始,完成多人协同作业的步骤。

今日小结

无法避免的,在执行 git merge origin/mastergit pull 的过程中,还是很有可能会出现合併衝突的现象,遇到这种情形你还是必须手动处裡并协调解决衝突,但这已经是多人使用 Git 版本控管中最简单的使用方式。

如果你今天发生了衝突状况,而又不知道如何解决,因为版本尚未被成功合併,所以你可以执行以下指令「重置」到目前的 HEAD 版本:

git reset --hard HEAD

如果你今天成功的合併了,但又想反悔这次的合併动作,那麽你还是可以执行以下指令「重置」到合併前的版本状态,也就是重置到 ORIG_HEAD 这个版本:

git reset --hard ORIG_HEAD

我重新整理一下本日学到的 Git 指令与参数:

  • git init --bare
  • git clone [repo_url] [dir]
  • git add .
  • git commit -m "message"
  • git push origin master
  • git fetch
  • git merge origin/master
  • git pull
  • git reset --hard HEAD
  • git reset --hard ORIG_HEAD