Lite Git (V) - Remote
前言
本专栏名为Lite Git。主要想与Pro Git对应,后者为Git官方指南,有兴趣,或者想了解更多细节的同学,请移步官网下载PDF版。
本专栏主要为了让初出茅庐的同学更快、更合理地掌握Git的基本运用;
同时,本专栏也会介绍一下作为Android开发人员关心的:repo的运用;
本篇是该专栏的第五篇,主要介绍Git的远端与本地的概念,属于比较重要的一节,刚开始了解Git的同学强烈建议了解一下;
远端(Remote)仓库
何为远端仓库?
顾名思义,是相对于本地仓库而言的:远端仓库指本地仓库设置的,可进行进度同步的目标仓库;
本地仓库与远端仓库的同步通过git pull/git fetch
,git push
完成,示意图如下:
这里延用Working Directory
的概念作为本地仓库,因为本地仓库并不包含本地工作区和暂存区;
这是非常重要的一点,理解了这里,才可以理解为什么git fetch
与git push
都不涉及工作区、暂存区的修改了;(git pull
本质上是git fetch
与其他指令的组合,因此会操作暂存区、工作区。但其git fetch
部分也是不涉及工作区、暂存区的修改,特此注明)
远端仓库的查看
在包含本地仓库的目录下,使用git remote
或git remote -v
命令即可查看;
还是以demo_client
仓库为例:
# 显示当前仓库设定的所有远端仓库
ryan ~/git_demo/demo_client (master) $ git remote
origin
# 显示当前仓库设定的所有远端仓库,及其路径
ryan ~/git_demo/demo_client (master) $ git remote -v
origin /home/ryan/git_demo/demo_bare (fetch)
origin /home/ryan/git_demo/demo_bare (push)
这里再强调一点,所谓远端,是相对于本地的概念,并不一定是本机以外的路径,比如此例就是本机内另外一个路径的裸仓库为远端。
此外,我们可以通过git remote add
与git remote remove
指令对当前本地仓库设定的远端仓库进行修改;
# 显示当前仓库设定的所有远端仓库,及其路径
ryan ~/git_demo/demo_client (master) $ git remote -v
origin /home/ryan/git_demo/demo_bare (fetch)
origin /home/ryan/git_demo/demo_bare (push)
# 删除名为origin的远端仓库引用
ryan ~/git_demo/demo_client (master) $ git remote remove origin
# 再次查看已设定的远端仓库,内容为空
ryan ~/git_demo/demo_client (master) $ git remote -v
ryan ~/git_demo/demo_client (master) $ git remote
# 添加名为new_origin 的远端仓库引用,地址为/home/ryan/git_demo/demo_bare
ryan ~/git_demo/demo_client (master) $ git remote add new_origin /home/ryan/git_demo/demo_bare
# 再次查看已设定的远端仓库,出现new_origin,且对应路径为设定的路径
ryan ~/git_demo/demo_client (master) $ git remote
new_origin
ryan ~/git_demo/demo_client (master) $ git remote -v
new_origin /home/ryan/git_demo/demo_bare (fetch)
new_origin /home/ryan/git_demo/demo_bare (push)
或许有人有疑问,如果在git remote add
时,我输入了一个不合法的路径,会发生什么,答案是,什么报错都不会出现:
# 添加一个名为wrong_origin 的远端仓库引用,指向http://www.baidu.com,后者显然不是一个git仓库
ryan ~/git_demo/demo_client (master) $ git remote add wrong_origin http://www.baidu.com
# 再次查看已设定的远端仓库,发现可以添加成功
ryan ~/git_demo/demo_client (master) $ git remote
new_origin
wrong_origin
ryan ~/git_demo/demo_client (master) $ git remote -v
new_origin /home/ryan/git_demo/demo_bare (fetch)
new_origin /home/ryan/git_demo/demo_bare (push)
wrong_origin http://www.baidu.com (fetch)
wrong_origin http://www.baidu.com (push)
因此,为了校验我们添加的远端仓库是否可及(是否是合法的git仓库,且权限足够),我们需要在git remote add
以后,执行git fetch
来检验:
# 拉取名为new_origin的远端仓库的进度,可以正常拉去,并发现有dev与master两个分支
ryan ~/git_demo/demo_client (master) $ git fetch new_origin
From /home/ryan/git_demo/demo_bare
* [new branch] dev -> new_origin/dev
* [new branch] master -> new_origin/master
# 拉取名为wrong_origin的远端仓库,发现未能在对应路径上找到git仓库,即表明路径不合法
ryan ~/git_demo/demo_client (master) $ git fetch wrong_origin
fatal: repository 'http://www.baidu.com/' not found
# 故将其删除
ryan ~/git_demo/demo_client (master) $ git remote remove wrong_origin
此外,通过上面一个例子,我们也不难得出一个结论:本地仓库与远端仓库的关系设定是弱绑定的,添加时甚至不会进行合法性校验。并且,在设定好远端仓库后,两者并不会立即同步,需要手动调用git fetch
/git pull
/git push
完成同步;
远端仓库的进度
既然远端仓库与本地仓库的进度不会实时同步,那么我们如何来确定两者的进度差异呢?答案仍然是git status
与git log
这部分对初学者来说比较难,为了方便理解,我们引入一个之前有展示,但是没有介绍的功能:git配置文件中的别名
所谓别名,就是为了简化而将一整串命令通过一个词来代替的功能,例如我们常用的git status
,通过如下设置以后,我就可以通过git st
这一剪短的指令来完成git status
的调用;
同时,我们引入一个带树状结构的git log
命令变体,这对于我们后续理解git的树状结构有很大的帮助:
# 文件路径:~/.gitconfig
...
[alias]
st = status
...
lg = log --pretty=format:\"%C(yellow)%h %C(red)%d %C(white)%s %C(blue)%an/%C(blue)%cn %C(magenta)%ad/%C(magenta)%cd%C(reset)\" --graph --color=always --date=iso
...
...
设置完后我们验证一下:
# git status与git st结果一致
ryan ~/git_demo/demo_client (master) $ git status
On branch master
nothing to commit, working tree clean
ryan ~/git_demo/demo_client (master) $ git st
On branch master
nothing to commit, working tree clean
# git lg通过每行的*来显示树状结构,由于当前仓库没有分叉,因此没有体现,后面遇到会再提及一下
ryan ~/git_demo/demo_client (master) $ git lg
* 7a0a4ff (HEAD -> master) [demo_client]update FileB Ryan_ZHENG/Ryan_ZHENG 2021-10-28 13:23:30 +0800/2021-10-28 13:23:30 +0800
* 91a45d2 (new_origin/master) [demo_client]add FileA FileB FileC FileD FileE Ryan_ZHENG/Ryan_ZHENG 2021-10-28 10:57:51 +0800/2021-10-28 10:57:51 +0800
* 0b64004 [demo_client]delete FileA Ryan_ZHENG/Ryan_ZHENG 2021-10-27 14:19:08 +0800/2021-10-27 14:19:08 +0800
* 0dbaef0 [demo_client]add FileA Ryan_ZHENG/Ryan_ZHENG 2021-10-27 13:58:48 +0800/2021-10-27 13:58:48 +0800
上面例子中,我们发现git lg
的结果中有这么几个字段:
(HEAD -> master)
表示当前本地仓库的HEAD指向master
分支;(new_origin/master)
表示当前设定的名为new_origin
的远端仓库引用,截至目前的进度在91a45d2
这笔提交上;
这里是初学者不容易理解的地方:
(new_origin/master)
这种[远端仓库名/分支名]的结构,表示远端仓库的这个分支,在当前状态下的本地仓库记录的进度节点;
注意:这里强调当前状态下的本地仓库记录的进度节点 ,是因为上面讲的”本地仓库与远端仓库的同步不是实施的,因此从本地仓库通过git lg
查看的,只能是从上次git fetch
/git pull
后同步的节点,不代表当前远端仓库的真实节点;master
这种纯[分支名]的标记,表示本地仓库的这个分支的进度节点;HEAD -> master
这种[HEAD -> 分支名]的结构,表示当前HEAD指向的本地分支名;
理解了上面的信息,我们来看这个现象:
HEAD指向的master分支,即本地当前所处的分支,是领先于远端new_origin的master分支的;
这是因为上一节我们分别在demo_client
和demo_client_2
仓库对FileB
进行了不同的修改后,通过git commit
提交进了本地仓库,但demo_client_2
将提交推送到了远端仓库的dev
分支,而demo_client
未执行git push
来将其推送至远端仓库;那么我们现在来进行提交:
# 由于我们修改了远端仓库的引用,因此这里不再是origin,而是新的远端仓库的名称new_origin
# 由于我们推送的仍是master分支的进度,那么第二个参数不变
ryan ~/git_demo/demo_client (master) $ git push new_origin master
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 6 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 301 bytes | 301.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
To /home/ryan/git_demo/demo_bare
91a45d2..7a0a4ff master -> master
# 查看当前分支的提交历史树,发现HEAD->master与new_origin/master完成了同步;
ryan ~/git_demo/demo_client (master) $ git lg
* 7a0a4ff (HEAD -> master, new_origin/master) [demo_client]update FileB Ryan_ZHENG/Ryan_ZHENG 2021-10-28 13:23:30 +0800/2021-10-28 13:23:30 +0800
* 91a45d2 [demo_client]add FileA FileB FileC FileD FileE Ryan_ZHENG/Ryan_ZHENG 2021-10-28 10:57:51 +0800/2021-10-28 10:57:51 +0800
* 0b64004 [demo_client]delete FileA Ryan_ZHENG/Ryan_ZHENG 2021-10-27 14:19:08 +0800/2021-10-27 14:19:08 +0800
* 0dbaef0 [demo_client]add FileA Ryan_ZHENG/Ryan_ZHENG 2021-10-27 13:58:48 +0800/2021-10-27 13:58:48 +0800
本地仓库与远端仓库引用的绑定
也许你在其他地方见过,git pull
与git push
不带任何参数就完成了远端仓库与本地仓库的同步,而此时如果你在demo_client
仓库下输入git pull
,会发现如下报错:
ryan ~/git_demo/demo_client (master) $ git pull
There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.
git pull <remote> <branch>
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream-to=new_origin/<branch> master
原因是我们在创建新的名为new_origin
的远端仓库时,并未指定本地的任何分支与之对应,因此无参的git pull
不知道该同步哪个分支的进度。
按照提示,我们要么使用git pull <remote> <branch>
这种完整指令,要么将本地一个分支的上游设置为远端的对应分支;
比如我们希望本地仓库的master分支上游为new_origin的master分支(即master分支进度默认与new_origin/master同步)
# 设置本地master分支自动追踪远端仓库new_origin的master分支
ryan ~/git_demo/demo_client (master) $ git branch --set-upstream-to=new_origin/master master
Branch 'master' set up to track remote branch 'master' from 'new_origin'.
# 再次执行git pull,无报错
ryan ~/git_demo/demo_client (master) $ git pull
Already up to date.
# git push同理,无报错
ryan ~/git_demo/demo_client (master) $ git push
Everything up-to-date
由于此处本地仓库的master
分支已经与远端分支new_origin/master
进度一致,因此此处没有任何同步的行为,直接显示up to date
总结
远端仓库与本地仓库之间的关系具有如下特点:
- 远端仓库的概念是相对于本地仓库的,并不一定存在于本机磁盘以外;
git remote -v
可以查看当前本地仓库设定的远端仓库引用信息;git remote add
/git remote remove
可以添加、删除远端仓库引用,但git remote add
指令并不会验证其添加的远端仓库引用是否合法,需要手动调用git fetch
验证;- 并非实时同步,需要手动调用
git fetch
/git pull
/git push
完成同步; - 通过
git branch --set-upstream-to=<remote>/<remote_branch> <local_branch>
可以为本地分支与远端分支建立追踪关系,使后续git pull
与git push
命令无需追加额外参数;(学习过程中建议使用完整指令,以加深理解)