远程仓库
远程仓库
之前的所有演示都基于本地仓库的,git同样也支持远程仓库,如果想要与他人进行协作开发,可以将项目保存在一个中央服务器上,每一个人将本地仓库的修改推送到远程仓库上,其他人拉取远程仓库的修改,这样一来就可以同步他人的修改。对于远程仓库而言,对于公司而言,都会有自己的内网代码托管服务器,对于个人开发者而言,可以选择自己搭建一个代码托管服务器,又或者是选择第三方托管商。如果你有精力折腾的话可以自己搭,不过我推荐选择第三方的托管商,这样可以将更多精力专注于项目开发上,而且能让更多人发现你的优秀项目。
第三方托管
自建托管网站就是自己搭建的,第三方代码托管网站就是第三方搭建的,他们通过提供优质的代码托管服务,来吸引各式各样的开发人员与开源项目,时至今日,很多托管商基本上都不在局限于代码托管的功能。使用第三方托管商提供的平台,可以让开发者更专注于项目开发,而有些第三方托管商会将自己的项目开源,以供进行私有化部署,并为此提供配套的企业级服务。做的比较好的第三方托管商有以下几个
- Github
- GitLab
- BiteBucket
- Gitee
- sourceforge
- Coding
其中,GitHub是使用最普及的,可以说,干程序员这行就没有不知道GitHub的,本文将选择Github来作为远程仓库进行讲述。
Git代理
在本文开始讲解怎么进行远程仓库的操作之前,有一个相当重要的东西需要解决,那就是网络问题。在国内,Github是无法正常访问的,正常访问Github网站以及它提供的代码托管服务都会相当的缓慢,慢到只有几KB/s,在这种情况下,只能通过魔法上网来解决。
首先你需要自己付费购买代理服务,一般代理商都会给你提供相应的代理工具,比如我使用的代理工具是Clash for windows,它的本地代理端口是7890
,并且同时支持http和socks5协议
在知晓了代理端口以后,就可以给Git bash 配置代理了
# http
$ git config --gloabl http.proxy http://127.0.0.1:7890
$ git config --gloabl https.proxy http://127.0.0.1:7890
# socks5
$ git config --global http.proxy socks5://127.0.0.1:7890
$ git config --global https.proxy socks5://127.0.0.1:7890
上面的是全局设置,你可以只为特定的域名设置代理
git config --global http.https://github.com.proxy http://127.0.0.1:7890
git config --global http.https://github.com.proxy socks5://127.0.0.1:7890
代理设置完毕后,再使用远程托管服务就会流畅许多。
克隆仓库
在GitHub上有着成千上万的开源仓库,如果你想要获取一个开源仓库的源代码,最好的方式就是克隆仓库,比如Go这门编程语言的开源仓库,事实上这是镜像仓库,源仓库在谷歌。
通过Code按钮可以获取该仓库的url
然后在本地找一个你觉得合适的位置来放置该项目,随后执行命令
$ git clone https://github.com/golang/go.git
Go源代码的大小有500MB左右,在将代码克隆到本地以后,你就可以开始独自研究,修改,并编译这些源代码了。
$ ls
CONTRIBUTING.md PATENTS SECURITY.md codereview.cfg go.env misc/ test/
LICENSE README.md api/ doc/ lib/ src/
实际上git clone
的url参数也可以是本地仓库,例如
$ git clone /home/bob/project/git-learn/
git在将仓库克隆到本地时或者检出远程分支时,会自动创建跟踪分支,跟踪分支是与远程分支有着直接关系的本地分支,比如远程分支叫origin/main
,那么本地的跟踪分支就与之同名叫main
,先查看下分支情况
$ git branch --all
* main
remotes/origin/HEAD -> origin/main
remotes/origin/main
remotes/origin/op
可以看到这里有四个分支,首先main
属于跟踪分支,origin/main
属于远程跟踪分支,它是对于远程仓库中的分支的引用。我们后续在工作区的修改都是基于跟踪分支,远程跟踪分支是不可写的,git会在每一次fetch时更新远程跟踪分支。通过给git branch
命令加上-vv
参数,可以查看本地所有的跟踪分支。
$ git branch -vv
* main f5602b9 [origin/main] Revert "revert example"
可以看到git只为main分支自动创建了跟踪分支。假设远程仓库初始状态如下
将代码克隆到本地后,本地仓库的状态如下图,在最开始时两个分支都指向的同一个提交。
当你在本地做了一些修改并提交,发现远程仓库上有新提交,并使用git fetch
抓取了修改后,于是两个分支各自指向了不同的提交。
这时,为了同步修改,你需要将远程跟踪分支与本地跟踪分支使用git merge
合并,于是两个分支又指向了同一个提交。
最终你将提交通过git push
推送到了远程仓库,而此时远程仓库的状态就如下图。
这基本上就是一般远程仓库的工作流程。
关联仓库
在本地已有仓库的情况下,可以通过git remote
命令将其与远程仓库关联,已知远程仓库的URL为
https://github.com/246859/git-example.git
那么执行git remote add <name> <url>
来将其关联
$ git remote add github https://github.com/246859/git-example.git
通过git remote -v
来查看本地仓库与之关联的远程仓库
$ git remote -v
github https://github.com/246859/git-example.git (fetch)
github https://github.com/246859/git-example.git (push)
仓库关联成功以后通过show
子命令来查看细节
$ git remote show github
* remote github
Fetch URL: https://github.com/246859/git-example.git
Push URL: https://github.com/246859/git-example.git
HEAD branch: main
Remote branches:
main tracked
noop tracked
op tracked
Local refs configured for 'git push':
main pushes to main (up to date)
op pushes to op (up to date)
如果后续不再需要了可以删除掉
$ git remote remove github
通过git remote rename
来修改关联名称
$ git remote rename github gitea
或者使用git remote set-url
来更新url
$ git remote set-url schema://host/repo
一个本地仓库也可以多同时关联多个仓库
$ git remote add gitea https://gitea.com/246859/example.git
$ git remote -v
gitea https://gitea.com/246859/example.git (fetch)
gitea https://gitea.com/246859/example.git (push)
github https://github.com/246859/git-example.git (fetch)
github https://github.com/246859/git-example.git (push)
实际上gitea这个url并不存在,只是我随便编的,git在关联远程仓库时并不会去尝试抓取它,除非加上-f
参数,因为url不存在,抓取的结果自然会失败。
$ git remote add -f gitea https://gitea.com/246859/example.git
Updating gitea
remote: Not found.
fatal: repository 'https://gitea.com/246859/example.git/' not found
error: Could not fetch gitea
拉取修改
在本地仓库与远程仓库刚关联时,仓库内的代码多半是不一致的,为了同步,首先需要拉取远程仓库的修改。
$ git fetch github
remote: Enumerating objects: 28, done.
remote: Counting objects: 100% (28/28), done.
remote: Compressing objects: 100% (19/19), done.
remote: Total 28 (delta 6), reused 27 (delta 5), pack-reused 0
Unpacking objects: 100% (28/28), 2.34 KiB | 14.00 KiB/s, done.
From https://github.com/246859/git-example
* [new branch] main -> github/main
* [new tag] v1.0.0 -> v1.0.0
* [new tag] v1.0.1 -> v1.0.1
* [new tag] v1.0.3 -> v1.0.3
然后查看本地分支就会发现多出来了一个分支remotes/github/main
$ git branch -a
conflict
feature_V3
feature_v2
jkl
* main
op
test
v1
v2
remotes/github/main
该分支就是远程仓库上的分支,git fetch
命令就是将远程仓库上的修改抓取到了本地的remotes/github/main
分支上,但实际上我们的工作分支是main
分支,所以我们需要改将其合并
$ git merge github/main
如果想抓取所有远程分支的修改,可以带上--all
参数。
$ git fetch --all
提示
如果提示fatal: refusing to merge unrelated histories
,可以加上--allow-unrelated-histories
参数,之所以发生这个问题是因为两个仓库的历史不相关,是独立的。
跟踪分支
在抓取修改后,git并不会创建跟踪分支,在这种情况下,需要手动创建一个分支,然后将指定的远程分支设置为其上游分支
$ git checkout -b <branch>
$ git branch -u <remote>/<branch>
或者使用更简洁但具有同样效果的命令
$ git checkout -b <branchname> <remote>/<branch>
以及加上--track
参数来自动创建同名的本地跟踪分支
$ git checkout --track <remote>/<branch>
或者你也可以只带分支名,当git发现有与之同名的远程分支就会自动跟踪
$ git checkout <branch>
当不再需要跟踪分支时,可以直接通过如下来撤销该分支的上游
$ git branch --unset-upstream <branch>
拉取合并
每一次抓取修改后都需要手动合并或许有点麻烦,为此git提供了git pull
命令来一次性完成这个步骤。格式是如下
$ git pull <remote> <remote-branch>:<local-branch>
如果要合并的本地分支就是当前分支,则可以省略冒号以及后面的参数,例如
$ git pull github main
From https://github.com/246859/git-example
* branch main -> FETCH_HEAD
Already up to date.
同样的,它也支持 --allow-unrelated-histories
参数,以及所有git fetch
支持的参数。
推送修改
当你在本地完成了修改,并提交到了本地仓库时,如果想要将提交推送到远程仓库,就需要用到git push
命令。
$ git push <remote>
该命令执行时,默认会推送当前分支的提交,如果当前分支在远程仓库上并不存在,远程仓库就会自动创建该分支,git也在控制台中输出了整个创建的过程。
$ git push github
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
remote:
remote: Create a pull request for 'op' on GitHub by visiting:
remote: https://github.com/246859/git-example/pull/new/op
remote:
To https://github.com/246859/git-example.git
* [new branch] op -> op
或者你也可以推送指定分支以及指定远程分支的名称
$ git push github op:noop
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
remote:
remote: Create a pull request for 'noop' on GitHub by visiting:
remote: https://github.com/246859/git-example/pull/new/noop
remote:
To https://github.com/246859/git-example.git
* [new branch] op -> noop
如果想要删除远程分支,只需要加上一个--delete
参数即可,例如
$ git push github --delete noop
To https://github.com/246859/git-example.git
- [deleted] noop
SSH
在与远程仓库进行交互的时候,默认使用的是HTTP方式,它的缺点很明显,就是每一次都要手动输入账号密码,为此,使用SSH协议来替代HTTP会更好。
接下来要在本地创建ssh密钥对,打开gitbash,执行如下命令,过程中会要求输入一些信息,根据自己情况来定。
$ ssh-keygen -t rsa -b 4096
Generating public/private rsa key pair.
Enter file in which to save the key (/c/Users/Stranger/.ssh/id_rsa):
/c/Users/Stranger/.ssh/id_rsa already exists.
Overwrite (y/n)? y
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /c/Users/Stranger/.ssh/id_rsa
Your public key has been saved in /c/Users/Stranger/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:bbkk3VPOsowFTn9FQYeDl1aP3Ib+BpdC1x9vaAsFOQA Stranger@LAPTOP-9VDMJGFL
The key's randomart image is:
+---[RSA 4096]----+
| E....o.*=|
| +o*=+|
| o =*+*|
| = =.*.+=|
| S B B.O.=|
| + = B.* |
| o o . o|
| . |
| |
+----[SHA256]-----+
默认情况下,它会生成在~/.ssh/
目录下,git也是默认从这里去读取你的密钥文件。id.rsa
是私钥文件,不可以泄露,否则这个密钥对就没有安全意义了。id.rsa.pub
是公钥文件,这是需要向外部暴露的。来到github的setting中,添加新的SSH Keys。
将公钥文件的内容复制到输入框中,再点击按钮添加公钥。完事后执行如下命令测试下
$ ssh -T git@github.com
Hi 246859! You've successfully authenticated, but GitHub does not provide shell access.
可以看到成功通过SSH认证了,再通过SSH方式克隆一个远程仓库试一试。
$ git clone git@github.com:246859/git-example.git
Cloning into 'git-example'...
remote: Enumerating objects: 28, done.
remote: Counting objects: 100% (28/28), done.
remote: Compressing objects: 100% (19/19), done.
remote: Total 28 (delta 6), reused 27 (delta 5), pack-reused 0
Receiving objects: 100% (28/28), done.
Resolving deltas: 100% (6/6), done.
可以看到成功了,在github密钥管理界面,也能看到密钥的使用情况。
如此便配置好了通过SSH方式使用git。