写在前面
Git 的官网上有很详细的使用教程(当然有翻译版本),具体地址是 https://git-scm.com/book/zh/v2。唯一不足就是,很多讲解并没有实机演示。但是,毫无疑问,官网资料是最全面的!如果有任何疑问,可以去官网看看!
协议
Git 通常也会有个远程仓库。用来协调各个参与者的工作!这与上一代集中式版本控制系统的作用基本类似。一个远程仓库通常只是一个裸仓库(bare repository),即一个没有当前工作目录的仓库。 因为该仓库仅仅作为合作媒介,不需要从磁盘检查快照;存放的只有 Git 的资料。 简单的说,裸存仓库就是你工程目录内的 .git 子目录内容,不包含其他资料。
Git 本地仓库与远程仓库之间的交互需要遵循一定的协议。Git 可以使用四种主要的协议来传输资料:本地协议(Local),HTTP 协议,SSH(Secure Shell)协议及 Git 协议。
本地协议(Local)
本地协议主要是针对版本库就是硬盘内的另一个目录的情况。常见于团队每一个成员都对一个共享的文件系统(例如一个挂载的 NFS)拥有访问权。使用本地协议,就可以从本地版本库克隆(clone)、推送(push)以及拉取(pull)代码。这与操作其他远程仓库一样!
如果在 URL 开头明确的指定 file://,那么 Git 的行为会略有不同。 如果仅是指定路径,Git 会尝试使用硬链接(hard link)或直接复制所需要的文件。 如果指定 file://,Git 会触发平时用于网路传输资料的进程。如下:
git clone /opt/git/project.git
# 区别于
git clone file:///opt/git/project.git
我们可以使用本地协议来模拟学习 Git。如下:
- 建立一个目录,用来存放 Git 仓库(一个目录作为远程仓库,一个目录作为本地仓库),然后在远程仓库中,创建一个裸存仓库。如下图:
在 Git 中,裸存仓库通常是 名 + .git 的形式! - 在本地仓库,我们就可以直接克隆我们的远程仓库了,如下图:
Git 会给出提示,我们正在克隆一个裸存仓库!注意,我们克隆仓库时,没有指定路径(含名称),因此默认名称就是远程仓库的名字。 - 在本地仓库添加一个文件,并提交,如下图:
- 将本地仓库推送到远程仓库,如下图:
成功推送之后,在远程仓库中会有对应的记录。如上图所示的远程仓库的 objects 目录。 - 此时,我们就可以再建立一个 Local2,也来克隆我们的本地远程仓库,验证一下有没有 Local 推送的 a.txt 文件,如下图:
其实,使用远程协议的流程与本地协议是一样的!
HTTP 协议
根据 Git 官网的描述,Git 通过 HTTP 通信有两种模式。 在 Git 1.6.6 版本之前只有一个方式可用,十分简单并且通常是只读模式的;Git 1.6.6 版本引入了一种新的、更智能的协议,让 Git 可以像通过 SSH 那样智能的协商和传输数据。这两种模式分别被称为 哑(Dumb) HTTP 协议 和 智能(Smart) HTTP 协议。
哑 HTTP 协议里 web 服务器仅把裸版本库当作普通文件来对待,提供文件服务。智能(Smart) HTTP 协议在服务端运行了两组进程,分别负责上传[客户端的进程连接到服务端进程]和下载数据[客户端的进程连接服务端],它们可以读取本地数据,理解客户端有什么和需要什么,并为它生成合适的包文件。
每次推送,Git 服务器会询问用户名与密码。 默认情况下它会在终端中提示服务器是否允许你进行推送。如果不想在每一次推送时都输入用户名与密码,你可以设置一个 “credential cache”。 最简单的方式就是将其保存在内存中几分钟,可以简单地运行 git config --global credential.helper cache
来设置它。在 Windows 系统中,在输入过用户名密码之后,会被系统给保存下来!我们可以如下图所示的位置进行修改:
由于我们有搭建服务器,因此这个不好验证。但是 Git 官网有比较详细的介绍这两种协议的具体工作流程:https://git-scm.com/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-%E4%BC%A0%E8%BE%93%E5%8D%8F%E8%AE%AE
SSH(Secure Shell)协议
架设 Git 服务器时常用 SSH 协议作为传输协议。 因为大多数环境下服务器已经支持通过 SSH 访问 —— 即使没有也很容易架设。 SSH 协议也是一个验证授权的网络协议;并且,因为其普遍性,架设和使用都很容易。
通过 SSH 协议克隆版本库,你可以指定一个 ssh:// 的 URL:$ git clone ssh://user@server/project.git
或者使用一个简短的 scp 式的写法:$ git clone user@server:project.git
。你也可以不指定用户,Git 会使用当前登录的用户名。
现有的很多平台(例如 Github)都是以 SSH 秘钥的方式进行通信的!因此如果我们要以 SSH 的方式访问远程仓库,则根据 SSH 协议,必须要生成我们 Key。Git 客户端默认是带有 OpenSSH 的,配置方式是使用命令ssh-keygen -t rsa -C "youremail@example.com"
,如下:
Git 协议
这是包含在 Git 里的一个特殊的守护进程,它监听在一个特定的端口(9418),类似于 SSH 服务,但是访问无需任何授权。 要让版本库支持 Git 协议,需要先创建一个 git-daemon-export-ok 文件 —— 它是 Git 协议守护进程为这个版本库提供服务的必要条件 —— 但是除此之外没有任何安全措施。 要么谁都可以克隆这个版本库,要么谁也不能。 这意味着,通常不能通过 Git 协议推送。 由于没有授权机制,一旦你开放推送操作,意味着网络上知道这个项目 URL 的人都可以向项目推送数据。
Git 协议是现存最快的传输协议。如果你在提供一个有很大访问量的公共项目,或者一个不需要对读操作进行授权的庞大项目,架设一个Git守护进程来供应仓库是个不错的选择。它使用与 SSH 协议相同的数据传输机制,但省去了加密和授权的开销。
Git 协议消极的一面是缺少授权机制。用 Git 协议作为访问项目的唯一方法通常是不可取的。一般的做法是,同时提供 SSH 接口,让几个开发者拥有推送(写)权限,其他人通过 git:// 拥有只读权限。Git 协议可能也是最难架设的协议。它要求有单独的守护进程,需要定制,需要设定 xinetd 或类似的程序,而这些工作就没那么轻松了。该协议还要求防火墙开放 9418 端口,而企业级防火墙一般不允许对这个非标准端口的访问。大型企业级防火墙通常会封锁这个少见的端口。
关联远程仓库
Git 通常也会有个远程仓库。使用 git init
方式创建的本地仓库只能本地使用,其没有与任何远程仓库关联!远程仓库的增、删、改、查都是通过 git remote
命令来实现。
在 Git 中,会为关联的远程仓库创建一个本地名称,默认不显示指定时为 origin,这样我们在与远程仓库通信时,直接使用本地名称就可以了!例如,我们要将代码推送到以上关联的远程仓库,直接使用 git push origin
就可以了。git remote
命令的格式如下:
git remote [-v | --verbose]
git remote add [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=<fetch|push>] <name> <url>
git remote rename <old> <new>
git remote remove <name>
git remote set-head <name> (-a | --auto | -d | --delete | <branch>)
git remote set-branches [--add] <name> <branch>…
git remote get-url [--push] [--all] <name>
git remote set-url [--push] <name> <newurl> [<oldurl>]
git remote set-url --add [--push] <name> <newurl>
git remote set-url --delete [--push] <name> <url>
git remote [-v | --verbose] show [-n] <name>…
git remote prune [-n | --dry-run] <name>…
git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)…]
以下是一些常用的命令参数:
-v、--verbose
:用于列出关联的远程仓库,格式为:本地名称 + 远程仓库地址。该参数必须紧跟在 git remote 之后。如下图:
git remote add
:命令格式:git remote add [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=<fetch|push>] <name> <url>
。关联一个远程仓库 <url>,本地名称为 <name>。
git remote rename
:命令格式:git remote rename <old> <new>
。将名为 <old> 的远程仓库名重命名为 <new>。git remote rm
:命令格式:git remote remove <name>
。其中,参数--rm
是--remove
的简写。删除关联的名为 <name> 的远程仓库的关联信息。
与远程仓库相关的配置,均位于 工作目录下/.git/config
文件中,这是个文本文件,我们也可以直接打开改文件进行修改。
还有一点就是,一个本地仓库可以关联多个远程仓库! 如下图所示是关联了两个远程仓库之后的情况。
如上所示,建议在关联远程仓库的时候,指定一个本地名称,这样我们就可以很容易的知道关联情况。这个在我们往远程仓库提交代码是很有用处!
关联远程分支(upstream)
有一种情况,我们建立的远程仓库并不是空的(里面有文件),在使用以上命令关联本地仓库和远程仓库之后,我们还并不能将本地修改提交到远程仓库!第一点是因为,由于远程仓库本身有内容,我们的本地仓库与远程仓库并不是同步的!我们必须要将本地仓库与远程仓库同步之后,才可以正常提交代码。第二点是因为,多分支的问题,我们必须要关联分支!如下图所示:
从上图中我们不难看出两者的区别!远程仓库不空时,具体操作步骤如下:
# git初始化
git init
# 关联远程仓库
git remote add --origin Github_xxx 远程仓库地址
# 获取远程仓库 master 分支上的内容
git pull Github_xxx master
# 将本地仓库分支设置为远程仓库的 master 分支
git branch --set-upstream-to=Github_xxx/master master
# 将全部文件加入 git 版本管理。 . 的意思是将当前文件夹下的全部文件放到版本管理中
git add .
# 提交文件 使用 -m 编写注释
git commit -m "注释"
# 推送到远程分支
git push
当然,如果远程仓库本身是空的(没有任何文件),这步骤如下:
#git初始化
git init
#关联远程仓库
git remote add --origin Github_xxx 远程仓库地址
#将全部文件加入git版本管理 .的意思是将当前文件夹下的全部文件放到版本管理中
git add .
#提交文件 使用-m 编写注释
git commit -m "注释"
#推送到远程分支
git push
克隆远程仓库
如果本地仓库是直接使用 git clone
命令克隆自一个指定的远程仓库,则默认本地仓库就是和克隆的仓库关联的!我们可以使用 git remote
命令来修改关联信息。例如,如果在克隆时,使用的命令是git clone 远程仓库地址
而不是 git clone --origin 本地名字
,那么,默认使用 origin 来表示我们关联的远程仓库,我们可以使用 git remote rename
来更改这个名字!
在我们想要保存远程仓库的文件夹中打开 Bash 命令框,执行 git clone https://github.com/ZCShou/N_W_Z_1.git
命令即可(注意将以上仓库地址换为自己的)。如下图:
该命令将远程存储库克隆到新创建的目录中,并为本地存储库中的每个分支创建远程分支关联,并创建和检出从远程存储库当前活动的分支派生出来的初始分支。
该命令的完整格式如下:
git clone [--template=<template_directory>]
[-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
[-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
[--dissociate] [--separate-git-dir <git dir>]
[--depth <depth>] [--[no-]single-branch] [--no-tags]
[--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules]
[--[no-]remote-submodules] [--jobs <n>] [--] <repository>
[<directory>]
该命令常用参数如下:
-o <name>、--origin <name>
:指定远程仓库的本地名称为 <name>,而不使用默认的 origin。-b <name>、--branch <name>
:用于克隆名为 <name> 的远程分支,而不是以 HEAD 创建分支。在非空的仓库中执行该命令,表示将指定的分支同步到当前仓库!<repository>
:要克隆的(可能是远程的)存储库。<directory>
:指定保存克隆的仓库的本地目录名称。 如果没有明确给出目录,则使用远程库名字在当前目录。只有当目录为空时,才允许克隆到现有目录。
克隆之后,默认会使用 git fetch
将更新所有远程跟踪分支,并且没有参数的 git pull
将另外将远程主分支合并到当前主分支中(如果有的话)。其中也会有个 .git 的文件夹!
使用以上命令之后,本地仓库就会和以上指定的远程仓库管理关联。在 Git 中,会为关联的远程仓库创建一个本地名称,不显示指定时默认为 origin。我们可以在以上命令中使用参数 --origin 本地名称
来指定本地名称(git clone --origin NWZ_xxx https://github.com/ZCShou/N_W_Z_1.git
)。
远程仓库名字 “origin” 与分支名字 “master” 一样,在 Git 中并没有任何特别的含义一样。 同时 “master” 是当你运行 git init 时默认的起始分支名字,原因仅仅是它的广泛使用,“origin” 是当你运行 git clone 时默认的远程仓库名字。 如果你运行 git clone -o booyah,那么你默认的远程分支名字将会是 booyah/master。
git fetch 与 git pull
一旦远程主机的版本库有了更新,需要先将这些更新取回本地,否则,在将本地提交到远程仓库时会报错。这时就要用到 git fetch
或者 git pull
命令。
该命令用来从一个或多个其他存储库中获取分支和/或标记(统称为“refs”),以及完成其历史记录所需的对象。 该操作会更新远程跟踪分支。行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。
必须注意 git fetch
命令会将数据拉取到你的本地仓库——它并不会自动合并或修改你当前的工作。 当准备好时你必须手动将其合并入你的工作。而运行 git pull
通常会从最初克隆的服务器上抓取数据并自动尝试合并到当前所在的分支。简单来说,git pull = git fetch + git merge
git pull 命令:
格式:$ git pull <远程主机名> <远程分支名>:<本地分支名>
举例:$ git pull Docs master:master # 将远程仓库Docs的master分支克隆到本地master分支
# 如果与当前本地分支,则:后可以省略:
$ git push Docs master # 取回Docs/master分支,合并到当前本地分支
# 在某些场合,Git会自动在本地分支与远程分支之间,建立一种追踪关系(tracking)。
# 比如,在git clone的时候,所有本地分支默认与远程主机的同名分支,建立追踪关系,也就是说,本地的master分支自动”追踪”Docs/master分支。
$ git branch --set-upstream master Docs/next
# 如果当前分支与远程分支存在追踪关系,git pull就可以省略远程分支名。
$ git pull origin
# 如果当前分支只有一个追踪分支,连远程主机名都可以省略。
$ git pull
git fetch 命令:
# 将某个远程主机的更新(所有分支),全部取回本地
$ git fetch <远程主机名>
# 如果只想取回特定分支的更新,可以指定分支名
$ git fetch <远程主机名> <分支名>
# 注意:fetch后,内容并没有真正保存,修改手动合并
$ git merge origin/master
# 也可以在它的基础上,使用git checkout命令创建一个新的分支
# 所取回的更新,在本地主机上要用 ”远程主机名/分支名” 的形式读取。比如Docs主机的master,就要用Docsmaster读取。
$ git checkout -b newBrach Docs/master
git push
该命令用来将本地仓库(分支)同步到远程仓库(分支)。该命令将使用本地引用更新远程引用,同时发送完成给定引用所必需的对象。
git push -u Docs master #这里的Docs 就是上面 起的别名; -u 表示:以后操作,默认的仓库为Docs,默认关联的分支为master
- 1
使用上面的-u 以后,后续很多操作都可以简化,如下:
git push 命令:
格式:$ git push <远程主机名> <本地分支名>:<远程分支名>
举例:$ git push Docs master:master # 将本地master分支推送到远程仓库Docs的master分支
# 如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支。例如:
$ git push Docs :master
# 等同于
$ git push Docs --delete master
# 如果当前分支与远程分支之间存在追踪关系,则本地分支和远程分支都可以省略。( 即使用 git push -u 关联的那个)
$ git push Docs
# 如果当前分支只有一个追踪分支,那么主机名都可以省略。
$ git push
代码托管平台
代码托管平台主要用来辅助项目开发,同时协调项目程序的开发工作。一般的代码托管平台除了提供基本的源代码版本控制之外,还通常会集成一系列项目辅助开发工具。
Github
Github 应该是大家最熟悉的一个代码托管平台。目前应该是最大的开源软件托管平台。针对于个人以及企业,Github 有不同的套餐,具体如下:
如上图列出了不同类型的用户的限制条件。最初,Github 的免费用户仅可以创建公开仓库,被微软在 2018 年 6 月4 日收购之后,现在可以创建无限的公开或者私有仓库。
登录自己的账号,在账号下面就可以创建我们自己的仓库了。
Github 的仓库支持通过 HTTPS 和 SSH 协议来克隆仓库。打开仓库的主页,就会发下如下:
在使用 SSH 方式的时候,我们必须要将 SSH 的公钥放到 Github 的账户下。至于如何生成 SSH 的公钥,参见之前的博文即可。具体如下:
特别说明一下,就是创建组织,上图左侧列表中的 Organizations 选项下,我们就可以创建组织,创建组织之后,我们就可以直接在组织中,通过 Github 账户名来添加组织成员。
GitLab
使用方式与 Github 差不多。https://about.gitlab.com
Bitbucket
使用方式与 Github 差不多。Github 中的组织这里被称为 Team。
码云
码云是开源中国旗下的 Git 代码托管平台,依托国内的程序员较多基于平台中发布项目。目前是独立平台运营的。
阿里云Code
https://code.aliyun.com
Azure DevOps
https://dev.azure.com
https://docs.microsoft.com/en-us/azure/devops/?view=azure-devops
参考
- https://www.aneasystone.com/archives/2018/12/build-your-own-git-server.html
- https://gotgit.readthedocs.io/en/latest/03-git-harmony/010-protocol.html
- https://cloud.tencent.com/developer/news/319910
- https://segmentfault.com/a/1190000016959596
- https://cloud.tencent.com/developer/article/1347791
- https://www.cnblogs.com/wyl090512/p/9887795.html
- https://git-scm.com/book/zh/v2/%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E7%9A%84-Git-%E7%94%9F%E6%88%90-SSH-%E5%85%AC%E9%92%A5