锋言锋语

NferZhuang的自留地

git remote详解

本文通过实例一步步讲解git的remote相关操作。

注意,以下所有操作都是在mac的命令行下,操作的相对路径都是用户根目录。

创建仓库

$ mkdir -p test
$ mkdir -p test/server
$ cd test/server
$ git init --bare a.git

首先,我们创建了test目录,后续所有的操作都是在这个目录下进行的;紧接着我们创建server子目录,用于存放git的服务端内容。

执行完git init --bare a.git后,会在test/server目录下生成一个名字叫a.git的文件夹,这个文件夹就是存放git仓库的服务端。通过ls命令,可以看到服务器的目录结构和客户端的目录结构完全不同:

$ ls a.git
HEAD        config      description hooks       info        objects     refs

克隆仓库

$ cd ~/test
$ mkdir -p client
$ cd client
$ git clone ~/test/server/a.git

回到test目录,创建client子目录,用于存放git的客户端内容。

执行完git clone ~/test/server/a.git后,会在test/client目录下生成一个名字叫a的文件夹,注意,这个文件夹是没有.git后缀的。

远程仓库

$ git remote -v
origin  /Users/nferzhuang/test/server/a.git (fetch)
origin  /Users/nferzhuang/test/server/a.git (push)

远程仓库名字origin

远程仓库名字“origin”在Git中并没有任何特别的含义,“origin”是当你运行git clone时默认的远程仓库名字。如果你运行git clone -o booyah,那么远程仓库名字就是booyah。 — git-scm

fetch or push

git 2.5版本后,把远程仓库的fetch和push配置区分开来,主要是为了简化三角工作流,效果如下图:

三角工作流

这样就可以方便的从一个仓库拉取代码,修改后,再推送到另一个仓库中。不过一般使用中,fetch和push配置都是一致的。

如果你使用不到配置不同的fetch和push,则在使用git remote的时候不加-v参数:

$ git remote
origin

分支

$ git branch -a

显示并没有任何分支,因为这是一个空仓库,在clone的时候就给出了提醒:

Cloning into 'a'...
warning: You appear to have cloned an empty repository.

提交修改

$ touch a.txt
$ git add . && git ci -m "first commit"
$ echo "test" >> a.txt
$ git add . && git ci -m "update"
$ echo "hello world" >> a.txt
$ git add . && git ci -m "hello world"

注意,这个时候使用git branch -a看到是只有本地分支:

$ git branch -a
* master

执行git push之后,会自动创建远程分支:

$ git push
Counting objects: 9, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (9/9), 665 bytes | 0 bytes/s, done.
Total 9 (delta 0), reused 0 (delta 0)
To /Users/nferzhuang/test/server/a.git
 * [new branch]      master -> master

$ git branch -a
* master
  remotes/origin/master

查看分支映射关系

$ git branch -vv
* master 48d090f [origin/master] hello world

可以看到本地分支master和远程仓库的master的分支是一一映射的关系。

创建仓库b

按照仓库a的操作,对仓库b执行上述所有命令:

$ cd ~/test/server
$ git init --bare b.git

$ cd ~/test/client
$ git clone ~/test/server/b.git
$ cd b

$ touch b.txt
$ git add . && git ci -m "first commit"
$ echo "test b" >> b.txt
$ git add . && git ci -m "update b"
$ echo "hello world b" >> b.txt
$ git add . && git ci -m "hello world b"
$ git push

多远程仓库操作

在某个业务场景下,需要同时使用两个远程仓库,日常拉取和提交代码是在一个仓库中进行,发布版本的时候需要同步代码到另外一个仓库进行发布。

首先,添加远程仓库(把仓库a作为日常使用仓库,把仓库b作为发布仓库):

$ git remote add b ~/test/server/b.git
$ git remote
b
origin

可以看到,这个时候已经有多个远程仓库了,分别是origin和b(注意,这里的b可以使用任意名称,业务需要暂定为b)。

那么如何提交代码到b远程仓库呢?

$ git push b
To /Users/nferzhuang/test/server/b.git
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to '/Users/nferzhuang/test/server/b.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally.

推送代码被拒绝了,因为仓库b本身就有一些提交,实际上是会产生冲突。其实我在上一步创建仓库并做了一些提交,也是为了使场景复杂,覆盖更多的使用场景。

多仓库方案一

最简单的操作就是直接使用git push -f来强制覆盖提交:

$ git push -f b
...
To /Users/nferzhuang/test/server/b.git
 + b134e4c...48d090f master -> master (forced update)

可以看到,远程仓库被强制更新了(forced update)。

但是,实际场景中,git push -f的权限要求更高,一般是不允许进行这种危险操作的。毕竟如果出现了问题,只能看其他同事是否有原始的本地仓库进行还原。

而且有的时候,可能发布仓库上还有一些改动是日常使用仓库上没有的,需要先合并到日常使用仓库。如果是较少的一些改动可以手动人工合并,如果改动较多则就操作比较复杂了。

因此,简单暴力的git push -f在很多时候下并不是最优方案。

多仓库方案二

创建一个分支和发布仓库进行映射,合并版本后再进行提交。

$ git fetch -p b
...
From /Users/nfer/test/server/b
 * [new branch]      master     -> b/master

$ git branch -a
* master
  remotes/b/master
  remotes/origin/master

拉取远程仓库b的信息后,可以看到远程仓库b中有一个master分支,但本地仓库中并没有一个分支和其对应。下面就创建一个b分支来追踪b/master远程分支:

$ git checkout -b b b/master
Branch b set up to track remote branch master from b.
Switched to a new branch 'b'

$ git branch -vv
* b      0c4424a [b/master] hello world b
  master 3be0867 [origin/master] hello world

可以看到本地有两个分支masterb,分别和远程分支origin/masterb/master一一映射。

合并代码,并进行提交

$ git merge --no-ff master
fatal: refusing to merge unrelated histories

合并代码出错,我们这里测试的时候是用的两个独立的仓库,本身没有任何关联。实际项目中,请尽量避免这种情况,更多的场景是“本是同根生,发展各不同”。这里为了演示我们先暴力的进行合并并提交:

$ git merge --no-ff master --allow-unrelated-histories
Merge made by the 'recursive' strategy.
 a.txt | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 a.txt

$ git push b HEAD:master
...
To /Users/nfer/test/server/b.git
   704315d..79496f7  HEAD -> master

注意,这里推送代码的时候,一定不要执行git push b b命令,这个操作实际上会把当前分支的改动推动到b远程仓库的b分支。

$ git push b b
Total 0 (delta 0), reused 0 (delta 0)
To /Users/nfer/test/server/b.git
 * [new branch]      b -> b
$ git br -r
  remotes/b/b
  remotes/b/master
  remotes/origin/master

删除远程分支的命令如下:

$ git push -d b b
To /Users/nfer/test/server/b.git
 - [deleted]         b
$ git br -r
  b/master
  origin/master
文 / nfer
LEAVE A REPLY

loading