# 第 26 天：多人在同一个远端仓库中进行版控

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

## 建立多人使用的远端仓库与工作目录

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

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

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

再来我们假设有两位开发人员准备开发一个新项目 `myproject`，分別是 `User1` 与 `User2` 这两位。

首先，`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
```

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

## 远端仓库的基本开发流程

现在 `User1` 与 `User2` 拥有完全相同的仓库，版本也都完全一样，都只有一个。

现在 `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](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-97e3df29f396e7dccc46fc638caae798eadebb9b%2F01.png?alt=media)

遇到这种问题请不要紧张，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](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-236f2bb3eb0875a1bffcfd0c5b61b40697ea8877%2F02.png?alt=media)

这时我们可以看到 `User2WD` 中 `origin/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](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-32a8ff50355c0fa83169a4cd7bd39d3546e3b3fc%2F03.png?alt=media)

这样你就可以将远端仓库中 `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](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-734950c7501f17ce15c00ca21f1f83a9f3bc02ab%2F04.png?alt=media)

如果这个时候 `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 fetch` 与 `git 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](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-ad26629861ac271e78ac72a11706c6fc4a2b5621%2F05.png?alt=media)

最后，我们用 `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](https://1991450022-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj2h2C95aFkfa08k9hVd3%2Fuploads%2Fgit-blob-25ca6b35b797b60ff32896dfd38e08de2ce253d4%2F06.png?alt=media)

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

## 今日小结

无法避免的，在执行 `git merge origin/master` 或 `git 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

***

* [HOME](https://kerryhuangs-organization.gitbook.io/kerry-de-bi-ji-ben/git/30-tian-jing-tong-git-ban-ben-kong-guan)
* [回目录](https://kerryhuangs-organization.gitbook.io/kerry-de-bi-ji-ben/git/30-tian-jing-tong-git-ban-ben-kong-guan/zh-cn)
* [前一天：使用 GitHub 远端仓库 - 观念篇](https://kerryhuangs-organization.gitbook.io/kerry-de-bi-ji-ben/git/30-tian-jing-tong-git-ban-ben-kong-guan/zh-cn/25)
* [下一天：通过分支在同一个远端仓库中进行版控](https://kerryhuangs-organization.gitbook.io/kerry-de-bi-ji-ben/git/30-tian-jing-tong-git-ban-ben-kong-guan/zh-cn/27)

***
