Git 远程仓库

内容纲要

查看【Git】专题可浏览更多内容

在使用版本控制系统时,大部分的操作都在「本地仓库」进行,但当需要与团队协作进行数据共享时,就需要「远程仓库」了。

克隆一个远程仓库

Git | 入门 中介绍到,如何获取一个远程仓库,答案是使用克隆,如:

# git clone <url>
git clone https://example.com/git-repo.git

查看远程仓库信息

当克隆一个远程仓库的同时,Git 会自动记录其链接。默认使用「origin」来标识所克隆的原始仓库,「origin」就像是分支中的「master」或「main」,这是一个约定俗成的名称。

然后我们来看一下远程仓库信息:

git remote -v
origin  https://example.com/git-repo.git (fetch)
origin  https://example.com/git-repo.git (push)

每个远程仓库包含两行:

  • fetch:用来进行抓取的 URL;
  • push:用来把本地仓库中的数据推送到远程仓库的 URL;

很多情况下这两个 URL 都是相同的,但出于安全或性能等方面的考虑,可以对抓取和推送的设置不同的 URL 地址。

此外可以对一个本地仓库设置很多个远程链接,这是没有数量限制的。

连接本地仓库与远程仓库

如果已经有了一个本地仓库,想要与某个远程仓库连接呢?

# git remote add [<选项>] <名称> <地址>
git remote add backup https://backup.com/git-repo.git

上述命令表示增加一个远程仓库名称为「backup」,地址为「https://backup.com/git-repo.git

这里的「backup」相当于使用克隆命令后 Git 默认创建的「origin」。

再次使用命令

git remote -v
backup  https://backup.com/git-repo.git (fetch)
backup  https://backup.com/git-repo.git (push)
origin  https://example.com/git-repo.git (fetch)
origin  https://example.com/git-repo.git (push)

除了「origin」现在还多了刚才添加的「backup」

如果你当前的仓库是在本地使用 git init 命令创建而不是克隆来的,那么将没有 origin,这是正常的

本地与远程仓库的工作流程

首先要知道本地与远程的数据同步并不是一个实时的流程,那么就需要:

  1. 在需要的时候使用 git fetch 抓取远程仓库的数据,以了解团队的其他人也没有做什么新的改动;
  2. 如果没有,而你有新的改动要推送上去就可以使用 git push 命令推送上去;
  3. 如果有新的改动,就可以先 git fetch 使远程仓库的数据抓取到远程跟踪分支,然后 git merge 到本地仓库的分支上,当然还有一个组合命令 git pull 可以同时做到这些;

❓ 等等,什么是「远程跟踪分支」?

远程跟踪分支

当使用命令查看所有分支时:

git branch -a
* master
  remotes/origin/master

其中的 remotes/origin/master 就是远程跟踪分支,它是远程分支的本地只读副本,处于本地分支与远程分支之间,也就是说流程应该是这样的:
本地分支 <=> 远程跟踪分支 <=> 远程分支

在使用克隆获取远程仓库时 Git 会自动建立好追踪关系,使用命令:

git branch -vv
* master 834365f [origin/master] Add Title

如上示例就可以看到本地的 master 跟踪远程跟踪分支也就是 origin/master,也就是说 origin/master 是本地分支 master 的上游分支。

除了 git branch -vv 还可以使用命令查看:

git remote show origin
* 远程 origin
  获取地址:[email protected]:Example/Markdown.git
  推送地址:[email protected]:Example/Markdown.git
  HEAD 分支:master
  远程分支:
    master 已跟踪
  为 'git pull' 配置的本地分支:
    local  与远程 remote 合并
    master 与远程 master 合并
  为 'git push' 配置的本地引用:
    master 推送至 master (最新)

如果不是通过克隆而是手动添加的远程仓库,那么在推送时就会遇到这样的错误:

致命错误:当前分支 master 没有对应的上游分支。
为推送当前分支并建立与远程上游的跟踪,使用

    git push --set-upstream origin master

为了让没有追踪上游的分支自动配置,参见 'git help config' 中的 push.autoSetupRemote。

这就是本地仓库的当前分支没有设置上游的远程追踪分支,此时可以使用 -u 推送并进行跟踪:

# 也可以 git push --set-upstream origin master
git push -u origin master
枚举对象中: 4, 完成.
对象计数中: 100% (4/4), 完成.
使用 8 个线程进行压缩
压缩对象中: 100% (2/2), 完成.
写入对象中: 100% (3/3), 274 字节 | 274.00 KiB/s, 完成.
总共 3(差异 0),复用 0(差异 0),包复用 0
remote:
remote: Create a pull request for 'master' on GitHub by visiting:
remote:      https://github.com/Example/Markdown/pull/new/master
remote:
To github.com:Example/Markdown.git
 * [new branch]      master -> master
分支 'master' 设置为跟踪 'origin/master'。

除了在推送时指定,也可以在创建分支时指定追踪某个远程追踪分支,以名为 remote 的远程跟踪分支为例:

git branch --track local origin/remote
分支 'local' 设置为跟踪 'origin/remote'。

那么如果是将一条已经存在的分支设置追踪某个远程跟踪分支呢?以名为 remote 的远程跟踪分支为例:

git branch --set-upstream-to=origin/remote

跟踪分支主要是为了让我们得知本地分支和远程分支的状态,即:

  • 如果本地分支上提交了一些改动,且没有发布它和推送到远程仓库中。相对于这些提交来说的本地分支就「领先 (ahead)」于那些它所对应的远程分支。
  • 如果团队的其他开发人员提交并且发布了一些改动到远程仓库中,这时远程仓库就拥有了那些你还没有下载到本地仓库的提交。你的本地仓库就「落后 (behind)」于它所关联的远程仓库。

举个例子,在追踪远程分支后,在本地分支上对一个文件进行改动并提交,此时查看状态:

git status
位于分支 dev
您的分支领先 'origin/dev' 共 1 个提交。
  (使用 "git push" 来发布您的本地提交)

无文件要提交,干净的工作区

可以看到提示「您的分支领先 ‘origin/dev’ 共 1 个提交。」

抓取远程仓库数据

在连接到远程仓库后,还需要对远程仓库进行数据更新,因为与远程仓库仅仅是建立了连接关系,还没有进行任何数据交换。

Git 会在你的本地仓库中保存远程数据的信息(例如分支,提交等等)。但是它并不是实时地连接到的远程仓库上的:

git branch -a
* master
  remotes/origin/master

如果远程仓库创建了一个新分支如 feature,这时使用 git branch -a 是看不到的,但可以使用命令:

git ls-remote
From [email protected]:Example/Markdown.git
834365f4351ec5b902a11504c31580cf6e70e458    HEAD
55fd276ceae56769a8d9c9fa57144301a9c5c974    refs/heads/feature
834365f4351ec5b902a11504c31580cf6e70e458    refs/heads/master

这样就能看到远程的分支和提交 ID,然后抓取一下远程仓库的数据:

git fetch origin

再使用 git branch -a 就可以看到了

推送改动至远程仓库

# git push <远程仓库名> <远程仓库上的分支>
git push origin master

如果已经建立了跟踪关系,就可以简写成 git push

拉取远程仓库的改动

# git pull <远程仓库名> <远程仓库上的分支>
git pull origin master

git pull 命令将会从远程分支下载所有的新的提交到你的本地副本中来。

它实际上就是「抓取 (fetch)」命令和「合并 (merge)」命令的组合 (下载数据并整合到本地副本)。

git push 命令一样,如果你本地的 HEAD 分支还没有创建任何一个「跟踪」链接,你就必须告诉 Git,你要从哪一个远程仓库上的哪一分支中抓取数据(例如 git pull origin master),如果已经存在了一个链接,就可以简写成 git pull

整合的目标并不基于存在什么样的跟踪链接,它总是会被整合到你的本地 HEAD 分支中,也就是你的工作副本。

调和分离的分支

在新版本 Git 中,当出现偏离的分支时会遇到这样的提示:

提示:您有偏离的分支,需要指定如何调和它们。您可以在执行下一次
提示:pull 操作之前执行下面一条命令来抑制本消息:
提示:
提示:  git config pull.rebase false  # 合并
提示:  git config pull.rebase true   # 变基
提示:  git config pull.ff only       # 仅快进
提示:
提示:您可以将 "git config" 替换为 "git config --global" 以便为所有仓库设置
提示:缺省的配置项。您也可以在每次执行 pull 命令时添加 --rebase、--no-rebase,
提示:或者 --ff-only 参数覆盖缺省设置。

这就需要为分支的合并方式作出设置:

# 合并,先尝试 fast-forward,如果快速合并失败则使用正常合并
git config pull.rebase false

# 变基
git config pull.rebase true

# 仅快进 fast-forward
git config pull.ff only

或是每次执行使命时带上选项:

git pull [--rebase | --no-rebase | --ff-only]

删除远程分支

如果一个主题分支的工作已经完成了,需要删除本地分支以及解除与远程分支的关联:

# -d, --delete
git branch -d dev

# -r, --remotes 作用于远程跟踪分支
git branch -dr origin/dev

如果需要删除远程仓库上的分支,例如删除 dev 分支:

# git push <remote> --delete <branch>
git push origin -d dev

⚠️ 注意:对于远程仓库的操作都需要谨慎以免出错。


小结

  • 使用 git clone 命令可用于克隆一个远程仓库上的项目;
  • 使用 git remote 命令可用于管理远程仓库,选项 add 用于添加远程仓库、set-url 用于修改远程仓库 URL、rm 用于删除远程仓库设置,git remote show origin 用于显示远程仓库相详细信息 (获取、推送地址、远程分支等);
  • 使用 git branch -a 查看本地与远程的分支,-v-vv 可以查看本地与远程跟踪分支的跟踪关系、提交信息等,除了 git branch 命令还可以使用 git ls-remote
  • 远程跟踪分支是远程分支在本地的只读副本,克隆时会自动设置跟踪关系,如果本地分支没有跟踪关系可以在推送时使用 git push -u 指定为哪个远程服务器哪条分支,也可在创建本地分支时使用 git branch --track 指定,或是指定一条已经存在的本地分支:git branch --set-upstream-to=
  • 本地与远程的数据并不是实时同步,所以在需要时可以使用 git fetch 获取远程仓库的数据;
  • 需要推送本地数据到远程仓库时使用 git push 命令;
  • 拉取远程仓库改动到本地仓库时使用 git pull 命令,它是 git fetch 获取数据到远程跟踪分支 + git merge 从远程跟踪分支这个本地只读副本合并到本地仓库数据的组合命令;