
3.4 Git与远程仓库
正是由于远程仓库(GitHub)的存在,Git才能具有分布式特质。在Git中,一个本地仓库可以同时绑定一个以上的远程仓库。对远程仓库而言,除了提供对外的网络服务之外,其本质上只是本地仓库的一个副本,本节将基于GitHub创建远程仓库,并实现与本地仓库的协同工作。
3.4.1 远程仓库的管理
在GitHub上创建Git仓库是一件很简单的事情,限于篇幅这里就不展开讲解了,假设我们在GitHub上有一个远程仓库,它的地址为git@github.com:wangwenjun/git_section.git。
有两种做法可以让本地仓库和远程仓库建立绑定关系,第一种做法是使用clone命令获取远程仓库的所有数据对象,包括分支、Tag等,这样的话,本地也会有一个与远程仓库一模一样的仓库。今后所有的提交操作都将在本地直接进行,然后定期将本地仓库提交的变更推送至远程仓库。第二种做法是为本地仓库增加远程仓库的配置,然后将本地仓库中已提交的所有变更全部推送至远程仓库。下面通过示例代码分别演示这两种做法。
(1)使用git clone命令
示例代码如下:
> git clone git@github.com:wangwenjun/git_section.git Cloning into 'git_section'... The authenticity of host 'github.com (13.250.177.223)' can't be established. RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added 'github.com,13.250.177.223' (RSA) to the list of known hosts. warning: You appear to have cloned an empty repository. Checking connectivity... done. > cd git_section/ > ls -a #发现多了一个“.git”目录,通过clone命令即可创建一个与远程仓库一模一样的本地仓库,并建立绑定。 . .. .git
(2)手动添加远程仓库
示例代码如下:
# 首先在本地工作目录中执行git init命令。 > git init # 手动添加远程仓库,其中,origin是远程仓库名。 > git remote add origin git@github.com:wangwenjun/git_section.git # 还可以添加多个远程仓库,其中,https是远程仓库名。 > git remote add https https://github.com/wangwenjun/git_section.git # 在本地列出所有的远程仓库。 > git remote -v https https://github.com/wangwenjun/git_section.git (fetch) https https://github.com/wangwenjun/git_section.git (push) origin git@github.com:wangwenjun/git_section.git (fetch) origin git@github.com:wangwenjun/git_section.git (push) # 当然,我们也可以删除某个已经添加的远程仓库。 > git remote remove https > git remote -v origin git@github.com:wangwenjun/git_section.git (fetch) origin git@github.com:wangwenjun/git_section.git (push)
本地仓库与某个远程仓库建立了绑定关系之后,就可以将本地仓库中的变更提交推送至远程仓库了,由于远程仓库的存在,即使因本地磁盘损坏而导致本地仓库中的所有数据丢失也不用担心,因为远程仓库中存储了一份与本地仓库一样的数据备份,只需要重新clone一次即可。
3.4.2 远程仓库的操作
为本地仓库添加远程仓库后,本地已提交的变更就可以推送至远程仓库中了,其他开发人员也可以从远程仓库中将变更拉取至本地,如图3-13所示。

图3-13 本地仓库与远程仓库的协同工作
下面的示例代码演示了如何将本地已提交的变更推送至远程仓库,以及如何从远程仓库拉取最新的变更等命令。
# 程序员 A将本地提交的变更推送到远程仓库的操作步骤如下: > echo "Example">README.md > git add README.md > git commit -m "initial commit" # 将本地变更推送至远程仓库,“--set-upstream”命令可以简写为“-u”。 # origin是配置在本地中的唯一一个表示远程仓库的名字,master是远程仓库的分支。 > git push --set-upstream origin master Counting objects: 3, done. Writing objects: 100% (3/3), 221 bytes | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) To git@github.com:wangwenjun/git_section.git * [new branch] master -> master Branch master set up to track remote branch master from origin. # 程序员A在本地创建一个新的分支,并在本地进行一些提交。 > git checkout -b dev > echo "test">test.txt > git add test.txt > git commit -m "add the new file test.txt" #将本地的dev分支推送至远程仓库 > git push -u origin dev Counting objects: 3, done. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 289 bytes | 0 bytes/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/wangwenjun/git_section/pull/new/dev remote: To git@github.com:wangwenjun/git_section.git * [new branch] dev -> dev Branch dev set up to track remote branch dev from origin. # 目前,本地仓库和远程仓库分别拥有两个分支。 > git branch --all --list * dev master remotes/origin/dev remotes/origin/master
在上面的示例代码中,程序员A将本地提交的变更推送至远程仓库,并且创建了dev分支,现在,程序员B就可以通过远程仓库拉取到程序员A推送的提交了。
# 程序员 B从远程仓库拉取变更的操作步骤如下: > git branch --list --all #空无一物。 > git log --oneline #也是空无一物。 # 获取(fetch)远程仓库的提交,但是不会直接应用于本地,执行了fetch命令后,远程仓库的分支、tag等信息会同步到本地仓库(不会直接创建这些分支,需要通过checkout命令创建)。 > git fetch origin remote: Enumerating objects: 6, done. remote: Counting objects: 100% (6/6), done. remote: Compressing objects: 100% (3/3), done. remote: Total 6 (delta 0), reused 6 (delta 0), pack-reused 0 Unpacking objects: 100% (6/6), done. From github.com:wangwenjun/git_section * [new branch] master -> origin/master * [new branch] dev -> origin/dev > git branch --list --all remotes/origin/dev remotes/origin/master # 如果想要直接拉取并应用远程仓库中的提交,则可以使用pull命令;如果本地仓库中还没有对应的分支,则需要先执行checkout操作。 > git checkout -b master 'origin/master' > git checkout -b dev 'origin/dev' # 然后就可以在某个分支中进行拉取操作了。 > git pull origin master
这里需要特别说明的是,在团队协作时,push和pull命令是会经常用到的两个命令,每次在对本地仓库进行变更之前,最好先执行pull操作,拉取远程仓库最新的提交;在完成了本地提交之后,应尽快将提交push至远程仓库,这样做的好处是,能够最大程度地避免因多个人修改同一个文件而引起冲突。
3.4.3 本地仓库与远程仓库的其他协同操作
在了解了本地仓库与远程仓库如何进行交互之后,下面就来讲解如何删除远程仓库的分支和标签(Tag),以及如何回退(reset)已推送至远程仓库的提交等操作。
(1)本地仓库Tag与远程仓库Tag间的协同操作
下面的示例代码展示了如何将本地创建的Tag推送至远程仓库,如何将远程仓库的Tag拉取至本地,如何删除本地Tag并将Tag的删除动作同步至远程仓库。
# 在本地仓库中创建 Tag。 > git tag 'v0.0.1' ed7caa7 # 将本地仓库中的Tag推送至远程仓库。 > git push origin v0.0.1(只推送一个tag。) > git push origin --tags(将本地仓库的所有Tag推送至远程仓库。) Total 0 (delta 0), reused 0 (delta 0) To git@github.com:wangwenjun/git_section.git * [new tag] v0.0.1 -> v0.0.1 #将远程仓库中的Tag拉取至本地仓库。 > git tag -l #看不到v0.0.1。 # 执行拉取远程Tag的操作。 > git pull origin tag 'v0.0.1' From github.com:wangwenjun/git_section * [new tag] v0.0.1 -> v0.0.1 Already up-to-date. > git show v0.0.1 commit ed7caa74e8734612eee9006d334df24d6e752861 Author: Alex Wang <alex@wangwenjun.com> Date: Sat Feb 20 19:28:19 2021 -0800 initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..12a719a --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Example # 删除本地仓库的Tag。 > git tag -d v0.0.1 # 将对本地仓库Tag的删除推送至远程仓库origin。 > git push origin :refs/tags/v0.0.1 # 与上面的命令等价。 > git push origin --delete v0.0.1 # 这样一来,远程仓库的v0.0.1 Tag也会被删除。
(2)本地仓库分支与远程仓库分支间的协同操作
下面的示例代码展示了如何将本地创建的分支推送至远程仓库,如何从远程仓库拉取分支至本地仓库,如何删除本地分支并将分支的删除动作同步至远程仓库。
# 在本地创建新的分支 test。 > git checkout -b test #将变更提交至本地仓库。 > touch a && git add a && git commit -m "add new file a" # 推送(push)本地变更和新的分支test。 > git push --set-upstream origin test Counting objects: 3, done. Compressing objects: 100% (2/2), done. Writing objects: 100% (3/3), 304 bytes | 0 bytes/s, done. Total 3 (delta 0), reused 0 (delta 0) remote: remote: Create a pull request for 'test' on GitHub by visiting: remote: https://github.com/wangwenjun/git_section/pull/new/test remote: To git@github.com:wangwenjun/git_section.git * [new branch] test -> test Branch test set up to track remote branch test from origin. # 查看远程仓库的分支。 > git branch -r origin/dev origin/master origin/test # 删除本地分支。 > git branch -d test # 删除远程仓库的分支。 > git push origin --delete test To git@github.com:wangwenjun/git_section.git - [deleted] test # 再次查看远程仓库的分支。 > git branch -r origin/dev origin/master
(3)回退远程仓库的提交
如果不小心把错误的变更提交到了本地仓库,并且推送到了远程仓库,那么是否可以借助于reset命令回退到某个正确的版本呢?答案是可以的,下面的示例代码展示了如何使用reset命令删除推送至远程仓库的提交。
# 将错误的变更提交到本地仓库。 > echo "error"> error.txt && git add error.txt && git commit -m "error commit" # 将错误的提交推送至远程仓库。 > git push -u origin dev > git log --oneline 8822b3a error commit 3eb4cc6 add the new file test.txt ed7caa7 initial commit # 由于8822b3a 是一次错误的提交,因此现在要让HEAD指向3eb4cc6。 > git reset --hard origin/dev^ HEAD is now at 3eb4cc6 add the new file test.txt # 使用“--force”参数强制push至远程仓库。 > git push --force origin dev Total 0 (delta 0), reused 0 (delta 0) To git@github.com:wangwenjun/git_section.git + 8822b3a...3eb4cc6 dev -> dev (forced update) > git log --graph --decorate --pretty=oneline --abbrev-commit * 3eb4cc6 (HEAD -> dev, origin/dev) add the new file test.txt * ed7caa7 (tag: v0.0.1, origin/master, master) initial commit
经过上面的操作,本地仓库和远程仓库中都删除了8822b3a提交,但是通常情况下,我们不建议使用reset命令进行undo(回退)操作,虽然reset命令能够减少历史提交记录,但是这种做法毕竟是有一定风险性的(很容易出现数据丢失的问题)。这里还是推荐大家基于最新的提交进行修改,比如,新增数据文件、修改数据文件,或者删除某个文件,然后再次提交,这样的话所有的操作都会记录在案,即使出现了操作失误的情况,也可以基于历史提交记录进行恢复。