avatar

veedrin

Git 专题 13 —— remote

git是分布式版本管理工具,它没有中央仓库。但多人协作时,我们依然需要一个集散地,让协作成员之间统一往集散地推送和拉取更新。否则,点对点的沟通,效率会很低。

所以就引出了git中远端仓库的概念。

概念

我们之前所有的操作都是在本地仓库完成的,和本地仓库对应的是远端仓库。那么本地有若干分支,远端仓库是不是也有对应的若干分支呢?

当然。

我们探讨一个问题,在离线状态下,git是不是无从知道远端仓库的任何状态?

我让网络下线,查询从github克隆下来的本地仓库的状态,结果它告诉我本地仓库的master分支是up to date with 'origin/master'

$ git status

On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

实际上,git的分支有三种:

  • 本地分支,我们可以通过<branch>写法访问它。
  • 远端分支,我们可以通过<remote branch>写法访问它。
  • 远端分支引用,我们可以通过<remote/branch>写法访问它。实际上它也是本地分支,只不过我们无法操作它,只有git的网络操作才可以更新它。离线状态下,git给的状态就是本地分支和远端分支引用的比较结果。

git官方把我所说的远端分支引用称为远端分支。知道谁是谁就行了,名字不重要🤔

我是马蹄疾

我们看一下本地的远端分支引用。

.git/
.git/refs/
.git/refs/remotes/
.git/refs/remotes/origin/
.git/refs/remotes/origin/HEAD
.git/refs/remotes/origin/master

默认的远端仓库名就叫origin。它也有master分支指针,也有HEAD指针。

拉取

如果远端仓库有新的提交或者新的分支,我们需要运行git fetch命令来拉取更新。

$ git fetch

remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From github.com:veedrin/git
   3893459..0f80eeb  master     -> origin/master

这个命令是git fetch origin的缩写。因为origin是远端仓库的默认名称,所以可以省略。如果有手动添加的远端仓库,那就必须指定远端仓库的名称了。

这个命令做了什么呢?

它会把新的提交和新的分支拉取到本地,然后更新本地的远端分支引用到最新的提交。

git fetch仅仅是将远端的更新拉取下来,同步本地的远端分支引用,不会对本地分支有任何影响。我们需要手动执行合并操作才能更新本地分支。

$ git merge origin/master

On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean

当然,有一个更简单的操作。

$ git pull

remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
Unpacking objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
From github.com:veedrin/git
   4fbd1d4..d9785d7  master     -> origin/master
Updating 4fbd1d4..d9785d7
Fast-forward
 README.md | 2 ++
 1 file changed, 2 insertions(+)

git pull就是git fetchgit merge的一键操作。

推送

推送到远端的命令是git push <remote-name> <remote-branch-name>

$ git push origin master

Counting objects: 3, done.
Writing objects: 100% (3/3), 261 bytes | 261.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com:veedrin/git.git
   3eaa1ae..2bd3c9d  master -> master

如果当前分支对远端分支设置了追踪的话,也可以省略分支名。

$ git push

Counting objects: 3, done.
Writing objects: 100% (3/3), 261 bytes | 261.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To github.com:veedrin/git.git
   3eaa1ae..2bd3c9d  master -> master

有时候本地分支和远端分支同时有新的提交,直接push是不行的。

$ git push

To github.com:veedrin/git.git
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to 'git@github.com:veedrin/git.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 integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

有两种方式解决。

第一是先把远端的更新拉下来,有冲突则解决冲突,没冲突则再推送。

第二是强推。有时候我们就是想覆盖远端对吧,也不是不行,但是必须十分谨慎。而且不要在公共分支上强制推送。

$ git push -f

Counting objects: 24, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (8/8), done.
Writing objects: 100% (24/24), 3.72 KiB | 1.24 MiB/s, done.
Total 24 (delta 0), reused 3 (delta 0)
To github.com:veedrin/git.git
 + 54d741b...2db10e0 master -> master (forced update)

实际开发时我们会建很多特性分支,推送到远端,通过测试后再合入主分支。使用git push <remote-name> <remote-branch-name>每次都要指定远端分支名,如果会有多次推送,我们可以在推送时设置本地分支追踪远端分支,这样下次就可以直接推送了。

也可以简写成git push -u <remote-name> <remote-branch-name>

$ git push --set-upstream origin dev

Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 255 bytes | 255.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: 
remote: Create a pull request for 'dev' on GitHub by visiting:
remote:      https://github.com/veedrin/git/pull/new/dev
remote: 
To github.com:veedrin/git.git
 * [new branch]      dev -> dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.

然后我们在.git/config文件中能看到多了一条配置。

[branch "dev"]
    remote = origin
    merge = refs/heads/dev

查看

查看远端仓库的命令是git remote

$ git remote

origin

-v参数可以查看更为详细的信息,-v--verbose的缩写。

$ git remote -v

origin  git@github.com:veedrin/git.git (fetch)
origin  git@github.com:veedrin/git.git (push)

查看某个远端仓库的信息,可以使用命令git remote show <remote-name>

$ git remote show origin

* remote origin
  Fetch URL: git@github.com:veedrin/git-1.git
  Push  URL: git@github.com:veedrin/git-1.git
  HEAD branch: master
  Remote branches:
    dev    tracked
    master tracked
  Local branches configured for 'git pull':
    dev    merges with remote dev
    master merges with remote master
  Local refs configured for 'git push':
    master pushes to master (up to date)

添加

添加新的远端仓库,使用git remote add <shortname> <url>命令。

$ git remote add horseshoe https://github.com/veedrin/horseshoe

然后本地就多了一个远端仓库。

$ git remote

horseshoe
origin

除了添加远端仓库,我们还可以添加本地分支对远端分支的追踪。

$ git checkout -b dev origin/dev

Branch 'dev' set up to track remote branch 'dev' from 'origin'.
Switched to a new branch 'dev'

创建dev分支的同时,也设置了对远端分支dev的追踪,这样下次推送的时候就不需要指定了。

当然,远端分支引用必须得存在才行。

$ git checkout -b dev origin/dev

fatal: 'origin/dev' is not a commit and a branch 'dev' cannot be created from it

git也提供了快捷方式。

$ git checkout --track origin/dev

Branch 'dev' set up to track remote branch 'dev' from 'origin'.
Switched to a new branch 'dev'

重命名

有时候你想修改远端仓库的简写名。比如你将女朋友的名字命名为远端仓库的简写名,然后你们分手了。这真是一个令人悲伤(欣喜)的故事。

$ git remote rename nvpengyou gaoyuanyuan

查看远端仓库列表。

$ git remote

gaoyuanyuan
origin

删除

一般来说,一个git项目有一个远端仓库就行了,其余的大多是临时性的。所以总有一天要删除它。

$ git remote rm horseshoe

查看远端仓库列表。

$ git remote

origin