我们在Linux常用工具中学会了 git 的简单使用,下面我们进一步学习使用 git.
版本控制器:为了能够更⽅便我们管理一些不同版本的⽂件,便有了版本控制器。
所谓的版本控制器,就是能让你了解到⼀个⽂件的历史,以及它的发展过程的系统。通俗的讲就是⼀个可以记录⼯程的每⼀次改动和版本迭代的⼀个管理系统,同时也⽅便多⼈协同作业。⽬前最主流的版本控制器就是 Git.
在Linux中,如果我们的平台是 centos,安装 git 相当简单,以 centos7.6 为例,只需要执行 sudo yum -y install git
,查看 git 安装的版本:git --version
.
如果我们的平台是 ubuntu,安装 git 的指令为: sudo apt-get install git -y
;查看 git 安装的版本:git --version
.
注意:使用 git 工具无论是 centos 还是 ubuntu 平台,都是一样的。
一、Git 本地仓库
1. 本地仓库的创建
仓库是进⾏版本控制的⼀个⽂件⽬录。我们要想对⽂件进⾏版本控制,就必须先创建⼀个仓库出来。
创建⼀个 Git 本地仓库对应的命令为 git init
,注意命令要在⽂件⽬录下执⾏,例如:
如上就在一个目录下建好了一个本地仓库,我们使用 ls -la
查看这个目录下的仓库:
我们发现,当前⽬录下多了⼀个 .git 的隐藏⽂件, .git ⽬录是 Git 来跟踪管理仓库的,我们不要⼿动修改这个⽬录⾥⾯的⽂件,不然改乱了,就把 Git 仓库给破坏了。
2. 配置 Git
当安装 Git 后⾸先要做的事情是设置你的用户名称 和 e-mail 地址,这是⾮常重要的。
我们在 Linux 常用工具中也有所介绍,其配置指令为:
git config [--global] user.name "Your Name" git config [--global] user.email "email@example.com"
其中 –global 是⼀个可选项。如果使⽤了该选项,表⽰这台机器上所有的 Git 仓库都会使⽤这个配置。我们也推荐加上该选项;如果你希望在不同仓库中使⽤不同的 name 或 e-mail ,可以不要 –global 选项,但要注意的是,执⾏命令时必须要在仓库⾥。
如果我们想修改配置,直接执行上面的指令即可,新增的配置会覆盖之前的配置。
我们配置好 Git 后,可以查看当前的配置:git config -l
;
如果我们想删除配置,删除配置的指令如下:
git config [--global] --unset user.name git config [--global] --unset user.email
注意,如果我们在配置 Git 时加上了 –global 选项,在删除配置时也应该要加上 –global 选项。
3. 工作区、暂存区、版本库
首先我们先认识一下工作区、暂存区和版本库的概念:
- 工作区:是在电脑上你要写代码或⽂件的⽬录
- 暂存区:英⽂叫 stage 或 index。⼀般存放在 .git ⽬录下的 index ⽂件(.git/index)中,我们把暂存区有时也叫作索引(index)
- 版本库:⼜名仓库,英⽂名 repository 。⼯作区有⼀个隐藏⽬录 .git ,它不算⼯作区,⽽是 Git 的版本库。这个版本库⾥⾯的所有⽂件都可以被 Git 管理起来,每个⽂件的修改、删除,Git 都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“ 还原 ”。
其中,三者之间的关系如下图:
- 图中左侧为⼯作区,右侧为版本库。Git 的版本库⾥存了很多东西,其中最重要的就是暂存区
- 在创建 Git 版本库时,Git 会为我们⾃动创建⼀个唯⼀的 master 分⽀,以及指向 master 的⼀个指针叫 HEAD。(分⽀和HEAD的概念后面再介绍)
- 当对⼯作区修改(或新增)的⽂件执⾏
git add
命令时,暂存区⽬录树的⽂件索引会被更新 - 当执⾏提交操作
git commit
时,master 分⽀会做相应的更新,可以简单理解为暂存区的⽬录树才会被真正写到版本库中。
由上述描述我们便能得知:通过新建或粘贴进⽬录的⽂件,并不能称之为向仓库中新增⽂件,⽽只是在⼯作区新增了⽂件。必须要通过使⽤ git add
和 git commit
命令才能将⽂件添加到仓库中进⾏管理。
4. 添加文件
在包含 .git 的⽬录下新建⼀个 test ⽂件,我们可以使⽤ git add
命令可以将⽂件添加到暂存区,其使用如下:
- 添加⼀个或多个⽂件到暂存区:
git add [file1] [file2] ...
- 添加指定⽬录到暂存区,包括子目录:
git add [dir]
- 添加当前⽬录下的所有⽂件改动到暂存区:
git add .
再使⽤ git commit
命令将暂存区内容添加到本地仓库中,其使用如下:
- 提交暂存区全部内容到本地仓库中:
git commit -m "message"
- 提交暂存区的指定⽂件到仓库区:
git commit [file1] [file2] ... -m "message"
注意 git commit 后⾯的 -m 选项,要跟上描述本次提交的 message,由用户自己完成,这部分内容绝对不能省略,并要好好描述,是⽤来记录提交细节的。
例如:
git commit
命令执⾏成功后会告诉我们,1个⽂件被改动(就是我们新添加的 test ⽂件),插入了一⾏内容(test 中有一⾏内容)。
我们可以使⽤ git log
命令,来查看下历史提交记录,例如:
该命令显⽰从最近到最远的提交⽇志,并且可以看到我们 commit 时的⽇志消息。如果嫌输出信息太多,不好看的,可以加上 --pretty=oneline
参数:
需要说明的是,我们看到的⼀⼤串类似 7aad62bad5848c12ef8818df7e3c9cfb9c01fa17 的是每次提交的 commit id (版本号),Git 的 commit id 不是1,2,3……递增的数字,⽽是⼀个 SHA1 计算出来的⼀个非常大的数字,用十六进制表示。
5. 查看 .git 文件
我们可以使用 tree .git/
指令查看自己的 .git/ 的目录结构,由于目录结构太长了,我们就截取重要的部分讲解。
- index 就是我们的暂存区,add 后的内容都是添加到这⾥的:
- HEAD 就是我们的默认指向 master 分⽀的指针
⽽默认的 master 分⽀,其实就是:
打印的 7aad62bad5848c12ef8818df7e3c9cfb9c01fa17 就是当前保存的最新的 commit id
- objects 为 Git 的对象库,里面包含了创建的各种版本库对象及内容。当执⾏ git add 命令时,暂存区的⽬录树被更新,同时⼯作区修改(或新增)的⽂件内容被写⼊到对象库中的⼀个新的对象中,就位于 “.git/objects” ⽬录下,让我们来看看这些对象有何⽤处:
查找 object 时要将 commit id 分成两部分,其前两位是⽂件夹名称,后38位是⽂件名称。找到这个⽂件之后,⼀般不能直接看到⾥⾯是什么,该类⽂件是经过 sha (安全哈希算法)加密过的⽂件,但是我们可以使⽤ git cat-file -p + commit id
命令来查看版本库对象的内容:
以上就是我们最近⼀次的提交。
总结,在本地的 git 仓库中,有几个文件或者目录很特殊:
- index: 暂存区, git add 后会更新该内容。
- HEAD: 默认指向 master 分⽀的⼀个指针。
- refs/heads/master: ⽂件⾥保存当前 master 分⽀的最新 commit id 。
- objects: 包含了创建的各种版本库对象及内容,可以简单理解为放了 git 维护的所有修改。
6. 修改文件
Git 跟踪并管理的是修改,而非文件。什么是修改?比如我们新增了一行,这就是⼀个修改,删除了一行,也是⼀个修改,更改了某些字符,也是⼀个修改,删了⼀些⼜加了⼀些,也是⼀个修改,甚⾄创建⼀个新⽂件,也算⼀个修改。我们下面对 test 文件进行一次修改:
此时,仓库中的 test 和我们⼯作区的 test是不同的,如何查看当前仓库的状态呢? git status
命令用于查看在我们上次提交之后是否有对⽂件进⾏再次修改,例如:
上面的结果告诉我们,test 被修改过了,但还没有完成添加与提交。
我们先对文件进行添加,再查看仓库的状态:
如上图就说明 test 已经添加到暂存区了,但时还没有完成提交,随后我们提交即可。
最后,git diff [file]
命令可以⽤来显示暂存区和⼯作区⽂件的差异;也可以使用 git diff HEAD -- [file]
命令来查看版本库和⼯作区文件的区别。
例如我们再对 test 进行修改,修改后对比暂存区和工作区文件的差异:
如上结果,在最后一个 hello world!! 前面有一个 ’ - ’ 号,说明是减少了这一行,结果的确是这样的。
7. 版本回退
Git 能够管理⽂件的历史版本,这也是版本控制器重要的能⼒。如果有⼀天我们发现之前的⼯作做的出现了很大的问题,需要在某个特定的历史版本重新开始,这个时候,就需要版本回退的功能了。
执⾏ git reset
命令⽤于回退版本,可以指定退回某⼀次提交的版本。要解释⼀下 “回退” 本质是要将版本库中的内容进行回退,⼯作区或暂存区是否回退由命令参数决定:
git reset 命令语法格式为: git reset [--soft | --mixed | --hard] [HEAD] --mixed 为默认选项,使⽤时可以不⽤带该参数。该参数将暂存区的内容退回为指定提交版本内容,⼯作区⽂件保持不变。 --soft 参数对于⼯作区和暂存区的内容都不变,只是将版本库回退到某个指定版本。 --hard 参数将暂存区与⼯作区都退回到指定版本。切记⼯作区有未提交的代码时不要⽤这个命令,因为⼯作区会回滚,你没有提交的代码就再也找不回了,所以使⽤该参数前⼀定要慎重。 HEAD 说明: ◦ 可直接写成 commit id,表⽰指定退回的版本 ◦ HEAD 表⽰当前版本 ◦ HEAD^ 上⼀个版本 ◦ HEAD^^ 上上⼀个版本 ◦ ...以此类推
例如,我们重新对 test 文件写入内容:
然后对这里的内容进行添加和提交:
如上我们版本库中最新的提交是 “test reset”,此时我们将它进行版本回退,将暂存区和版本库中的内容回退到上一个版本:
如上结果,仓库中提醒我们需要进行添加和提交操作,说明我们已经回退到上一个版本;除此之外,上面回退版本操作中,我们还可以使用 git reset + commit id
进行指定版本的回退;假设我们从最新的版本直接回退到最初的commit test的版本,如下:
当前是最新版本:
回退到最初版本,复制 最初的commit test 的 commit id 使用即可:
当前我们的暂存区和版本库都已经回到 最初的commit test 的版本,但是工作区并没有回去,因为我们没有带选项,默认的选项 --mixed
只会回退暂存区和版本库中的内容;工作区中的内容依然如下:
但现在如果我后悔了,想再回到 test reset 的那一个版本怎么办?我们可以继续使⽤ git reset 命令回退版本,但我们必须要拿到test reset的 commit id 去指定回退的版本。
但我们看到了 git log 并不能打印出上一次最新的 commit id ,运气好的话我们可以从终端上去找之前的记录,运⽓不好的话 commit id 已经被我们搞丢了。
但是 Git 还提供了⼀个 git reflog
命令能补救⼀下,该命令⽤来记录本地的每⼀次命令,如下:
我们观察到,这里的 commit id 只有前面的一部分,没错,Git 版本回退的时候,也可以使⽤部分 commit id 来代表目标版本,我们找到 test reset 的版本后,进行回退:
此时又回到了 test reset 的版本。
Git 的版本回退速度⾮常快,因为 Git 在内部有个指向当前分⽀(此处是master)的 HEAD 指针, refs/heads/master ⽂件⾥保存当前 master 分⽀的最新 commit id 。当我们在回退版本的时候,Git 仅仅是给 refs/heads/master 中存储⼀个特定的版本,可以简单理解成如下图:
8. 撤销修改
如果我们在我们的⼯作区写了很长时间代码,越写越写不下去,觉得自己写的实在是不好,想恢复到上⼀个版本;当然我们可以直接删除在工作区新增的代码,但是这样效率极低所以我们可以使用 git 指令完成;
我们可以使用 git checkout -- [file]
命令让⼯作区的文件回到最近⼀次 add 或 commit 时的状态。 要注意 git checkout -- [file]
命令中的 --
很重要,切记不要省略,⼀旦省略,该命令就变为其他意思了,后面我们再介绍。
下面我们分几种情况讨论:
- (1)对于工作区的代码,还没有 add
我们的 test 文件中有如下的句子:
下面我们在 test 文件中新增内容:
下面我们可以直接使用指令 git checkout -- test
即可将文件内容恢复到上一个 add 之前版本:
- (2)已经 add,但没有 commit
让我们来回忆⼀下学过的 git reset
回退命令,该命令如果使⽤ --mixed
参数,可以将暂存区的内容退回为指定的版本内容,但工作区文件保持不变。那我们就可以回退暂存区的内容了,例如:
add 之后我们将暂存区的内容回退,执行 git reset HEAD test
指令:
我们发现,暂存区的内容回退了,但是工作区的内容还没有回退,此时我们已经回到情况一了,所以执行 git checkout -- test
即可:
- (3)已经 add,也已经 commit
我们可以 git reset --hard HEAD^
回退到上⼀个版本,不过,这是有条件的,就是你还没有把自己的本地版本库推送到远程仓库。
如下已经 add 和 commit:
将工作区、暂存区、版本库中的内容全部回到上一版本:
9. 删除文件
在 Git 中,删除也是⼀个修改操作,如下,如果要删除 file3 ⽂件,怎么搞呢?
如果直接执行 rm file3
,此时,工作区和版本库就不⼀致了,要删⽂件,目前除了要删⼯作区的⽂件,还要清除版本库的文件。
⼀般⾛到这⾥,有两种可能:
- 确实要从版本库中删除该文件
- 不小心删错了
对第⼆种情况,很明显误删,需要使⽤ git 来进行恢复,很简单,我们刚学过(删除也是修改):git checkout -- file3
即可;
对于第⼀种情况,很明显是没有删完,我们只删除了⼯作区的⽂件。这时就需要使用 git rm
将文件从暂存区和工作区中删除,并且 commit :
二、分支管理
1. 理解分支
分⽀就好像科幻电影里面的平⾏宇宙,当你正在学习语文的时候,另⼀个你正在另⼀个平行宇宙里学习数学。如果两个平行宇宙互不干扰,那对现在的你也没啥影响。不过,在某个时间点,两个平行宇宙合并了,结果,你既学会了语文又学会了数学!
在版本回退里,我们已经知道,每次提交,Git 都把它们串成⼀条时间线,这条时间线就可以理解为是⼀个分支。截止到目前,只有⼀条时间线,在 Git ⾥,这个分支叫主分支,即 master 分支。
再来理解⼀下 HEAD,HEAD 严格来说不是指向提交,而是指向master,master 才是指向提交的,所以,HEAD 指向的就是当前分支。
每次提交,master 分支都会向前移动⼀步,这样,随着我们不断提交,master 分支的线也越来越⻓,而 HEAD 只要⼀直指向 master 分支即可指向当前分支。
通过查看当前的版本库,我们也能看出当前 master 分支指向的最新提交的 commit id:
2. 创建分支
Git ⽀持我们查看或创建其他分支,对应的命令为:git branch
;在这⾥我们来创建第⼀个自己的分支 dev ,对应的命令为:git branch dev
;创建好后我们再看看我们的分支:
当我们创建新的分支后,Git 新建了⼀个分支叫 dev, master 前面的 * 表示当前 HEAD 指向的分支是 master 分支。另外,可以通过目录结构发现,新的 dev 分支:
发现目前 dev 和 master 指向同⼀个修改。目前的状态如下图:
3. 切换分支
如何切换到 dev 分支下进行开发呢?使用 git checkout
命令即可完成切换,如下:
我们发现 HEAD 已经指向了 dev,就表示我们已经成功的切换到了 dev 上。
我们现在在 dev 分支上对 test 文件进行一次修改并提交:
如上我们对文件添加了一行内容,接下来我们进行添加和提交:
然后我们切换回 master 分支上查看 test 文件:
此时我们发现,我们在 master 分支上的 test 并没有新增的那一行内容;我们来看看 dev 分支和 master 分支指向,发现两者指向的 commit id 是不⼀样:
因为我们是在 dev 分支上提交的,而 master 分支此刻的提交点并没有变,此时的状态如图如下:
当切换到 master 分支时,HEAD 就指向了 master,所以看不到提交了。
4. 合并分支
为了在 master 主分支上能看到最新的提交,就需要将 dev 分支合并到 master 分支,此时合并分支需要用到指令:git merge
,如下:
此时如果我们在 dev 分支上,我们先需要切换到 master 分支上再进行合并:
合并分支:
git merge
命令用于合并指定分支到当前分支。合并后,master 就能看到 dev 分支提交的内容了。此时的状态如图如下:
此时的合并模式是 Fast-forward 模式, 代表 “快进模式” ,也就是直接把 master 指向 dev 的当前提交,所以合并速度非常快。当然,也不是每次合并都能 Fast-forward,我们后面会介绍其他的合并模式。
5. 删除分支
合并完成后,dev 分支对于我们来说就没用了, 那么 dev 分支就可以被删除掉,注意如果当前正处于某分支下,就不能删除当前分支;而可以在其他分支下删除那个分支,删除分支的指令为 git branch -d + 分支名称
,如下:
因为创建、合并和删除分支非常快,所以 Git 推荐我们使用分支完成某个任务,合并后再删掉分⽀,这和直接在 master 分支上工作效果是⼀样的,但过程更安全。
6. 合并冲突
在实际分支合并的时候,并不是想合并就能合并成功的,有时候可能会遇到代码冲突的问题。
首先,我们先创建⼀个新的分支 dev1 ,并切换至目标分支,我们可以使⽤ git checkout -b dev1
,一步完成创建并切换的动作,如下:
我们在 dev1 分支上修改 test 文件并进行添加提交,如下:
此时我们切换回 master 分支上,观察 test 中的内容,并没有改变,这是正常的现象,我们前面也已经解释过:
如今我们在 master 分支上的 test 文件进行修改,并进行添加提交:
现在 master 分支和 dev1 分支各自都分别有新的提交,变成了如下图状态:
这种情况下,Git 只能试图把各自的修改合并起来,但这种合并就可能会有冲突,如下所示:
上面提示我们合并有冲突,我们查看仓库此时的状态:
发现 test 文件有冲突后,可以直接查看⽂件内容,注意, Git 会⽤ <<<<<<<,=======,>>>>>>>
来标记出不同分支的冲突内容,如下图:
此时我们必须要手动调整冲突代码,并需要再次提交修正后的结果,要么保留 master 分支上的内容,要么保留 dev1 分支上的内容;再次提交很重要,切勿忘记,如下:
到这里冲突就解决完成,此时的状态变成了下图:
用带参数的 git log 也可以看到分支的合并情况,例如指令:git log --graph --pretty=oneline --abbrev-commit
,如下所示:
此时我们就可以把 dev1 分支删除了。
7. 分支管理策略
通常合并分支时,如果可能,Git 会采用 Fast forward 模式,我们上面也简单介绍过这个模式,就是直接将 dev 分支合并到 master 分支上,在这种 Fast forward 模式下,删除分支后,查看分支历史时,会丢掉分支信息,看不出来最新提交到底是 merge 进来的还是正常提交的。
但在合并冲突部分,我们也看到通过解决冲突问题,会再进⾏⼀次新的提交,得到的最终状态为:
那么这就不是 Fast forward 模式了,这样的好处是,从分支历史上就可以看出分支信息。例如我们现在已经删除了在合并冲突部分创建的 dev 分支,但依旧能看到 master 其实是由其他分支合并得到:
Git 也推荐我们强制禁用 Fast forward 模式,那么就会在 merge 时生成⼀个新的 commit ,这样,从分支历史上就可以看出分支信息。
下面我们使用 --no-ff
方式的 git merge
实践一下禁用 Fast forward 模式 进行合并。首先,创建新的分支 dev ,并切换至新的分支,在新的分支上修改文件并添加提交:
然后切回 master 分支开始合并:
此时我们执行的指令为:git merge --no-ff -m "test git merge --no-ff" dev
;注意 --no-ff
参数,表示禁用 Fast forward 模式。禁用 Fast forward 模式 后合并会创建⼀个新的 commit ,所以加上 -m
参数,把描述写进去。
此时我们可以查看合并的历史情况:
可以看到,不使用 Fast forward 模式,merge 后就像这样:
所以在合并分支时,加上 --no-ff
参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,⽽ fast forward 合并就看不出来曾经做过合并。
8. bug 分支
假如我们现在正在 dev 分支上进行开发,开发到⼀半,突然发现 master 分支上面有 bug,需要解决。在 Git 中,每个 bug 都可以通过⼀个新的临时分支来修复,修复后,合并分支,然后将临时分支
删除。
假设我们现在主分支的 test 如下:
我们现在要去 dev 分支上开发:
突然发现 master 分支上的 test 中少写了一个 hello, world!,即出现了 bug,此时我们切换到 master 分支上查看 test 中的内容:
我们看到,我们在 dev 分支上的 test 没有添加并提交时,修改 test 文件会影响 master 分支上的 test 文件,此时我们不想在 master 分支的 test 中看到 dev 分支上的 test 修改的内容,所以,Git 提供了 git stash
命令,可以将当前的⼯作区信息进⾏储藏,被储藏的内容可以在将来某个时间恢复出来,如下,在 dev 分支上使用该命令:
我们用 git status
查看⼯作区,就是干净的(除非有没有被 Git 管理的⽂件),因此可以放心地创建分支来修复bug。
储藏 dev ⼯作区之后,由于我们要基于 master 分支修复 bug,所以需要切回 master 分支,再新建临时分支来修复 bug,如下:
此时我们已经创建并进入 fix_bug 分支,我们开始修 bug,由于我们少写了一个 hello, world!,所以我们加上后添加并提交:
然后再切换回 master 分支进行合并,最后删除 fix_bug 分支:
此时,bug 的修复⼯作已经做完了,我们还要继续回到 dev 分支进行开发。切换回 dev 分支:
此时我们的工作区还是干净的,我们需要恢复我们开发的代码,使用 git stash list
可以查看我们存储区:
工作现场还在,Git 把 stash 内容存在某个地方了,但是需要恢复⼀下,如何恢复现场呢?我们可以使用 git stash pop
命令,恢复的同时会把 stash 也删了,如下:
如上,我们开发时的代码就找回来了,此时我们继续可以开发;但是修复 bug 的内容,并没有在 dev 上显示,此时的状态图为:
我们的最终目的是要让 master 合并 dev 分支的,那么正常情况下我们切回 master 分支直接合并即可,但这样其实是有⼀定风险的。是因为在合并分支时可能会有冲突,而代码冲突需要我们⼿动解决(在 master 上解决)。我们无法保证对于冲突问题可以正确地⼀次性解决掉,解决的过程中难免手误出错,导致错误的代码被合并到 master 上。如果在 master 分支上直接合并 dev 分支,此时的状态为:
此时我们有另外一种解决方案,在 dev 分支上合并 master ,再让 master 去合并 dev ,这样做的目的是有冲突可以在 dev 分支解决并进行测试,而不影响 master 。此时的状态为:
下面我们开始代码演示,先在 dev 上合并 master:
发现冲突,然后解决冲突:
然后添加并提交:
切换回 master,进行合并 dev:
此时合并完成,删除 dev 分支即可。
9. 强制删除临时分支
如果我们在日常开发中在一个开发分支上开发代码,突然被叫停了,需要把这个分支删除,如下为新的分支上开发的代码:
并进行添加提交:
如果切换回 master 分支上按照常规方式删除,会出现以下问题:
我们可以使用指令:git branch -D temp
强制删除:
三、远程仓库
创建一个仓库我们在 Linux常用工具(下) 中已经介绍过,这里不再做介绍。
1. 克隆远程仓库
克隆/下载远端仓库到本地,需要使⽤ git clone
命令,后⾯跟上我们的远端仓库的链接,远端仓库的链接可以从仓库中找到:选择 “克隆/下载” 获取远程仓库链接:
SSH 协议和 HTTPS 协议是 Git 最常使⽤的两种数据传输协议。SSH 协议使⽤了公钥加密和公钥登陆机制,体现了其实⽤性和安全性,使⽤此协议需要将我们的公钥放上服务器,由 Git 服务器进⾏管理。使
⽤ HTTPS 方式时,没有要求,可以直接克隆下来。
HTTPS 的方式我们之前也介绍过,直接将仓库地址复制到 git clone
后即可,这里着重介绍 SSH 的方式。
如果我们按照上面的方式克隆,由于我们没有添加公钥到远端库中,服务器拒绝了我们的 clone 链接:
需要我们设置一下:
- 创建 SSH Key。在用户主⽬录下,看看有没有 .ssh 目录,如果有,再看看这个目录下有没有 id_rsa 和 id_rsa.pub 这两个文件,如果已经有了,可直接跳到下⼀步。如果没有,需要创建 SSH Key:
像这种情况,在 .ssh 里没有 id_rsa 和 id_rsa.pub,我们就要直接创建 SSH Key:
此时就需要我们输入指令:ssh-keygen -t rsa -C "你的邮箱"
,然后一路按回车即可:
随后我们的 .ssh 目录下就有这两个文件了,其中这两个就是 SSH Key 的秘钥对, id_rsa 是私钥,不能泄露出去, id_rsa.pub 是公钥,可以放心告诉任何人:
- 添加自己的公钥到远端仓库
我们回到自己的远程仓库找到设置:
点击 ssh公钥 选项:
进入后我们先随意给公钥命名,然后复制公钥到以下地方:
其中复制公钥,我们 cat 一下公钥的文件即可,然后复制公钥:
我们做好上面的工作后,点击确认即可,随后输入自己账号的密码即可。
随后我们就可以创建 SSH 仓库了,新建一个目录然后 git clone + 仓库地址
即可。
如果有多个人协作开发,GitHub/Gitee 允许添加多个公钥,只要把每个人的电脑上的 Key 都添加到 GitHub/Gitee,就可以在每台电脑上往GitHub/Gitee 上提交推送了。
当我们从远程仓库克隆后,实际上 Git 会自动把本地的 master 分支和远程的 master 分支对应起来,并且,远程仓库的默认名称是 origin 。在本地我们可以使⽤ git remote
命令,来查看远程库的信息,如:
或者,用 git remote -v
显示更详细的信息:
上面显示了可以抓取和推送的 origin 的地址。如果没有推送权限,就看不到 push 的地址。
2. 向远程仓库推送
本地已经 clone 成功远程仓库后,我们便可以向仓库中提交内容,例如新增一些文件后提交上去:
到这里我们已经将内容提交至本地仓库中,如何将本地仓库的内容推送至远程仓库呢,需要使用 git push
命令,该命令用于将本地的分支版本上传到远程并合并,命令格式如下:
git push <远程主机名> <本地分支名>:<远程分支名> #如果本地分支名与远程分支名相同,则可以省略冒号: git push <远程主机名> <本地分支名>
此时我们要将本地的 master 分支推送到 origin 主机的 master 分支,则可以执行:git push origin master
:
这里可以直接使用 git push
,因为我们在克隆的时候本地的 master 分支和远端的 master 分支已经对应起来。
由于我们使用的是 HTTPS 协议,所以每次 push 都要输入用户名和密码,如果是使用 SSH 协议则不需要。
如下,三个文件都被推送至远程仓库了:
3. 拉取远程仓库
假设我们在远程仓库中修改 file1 文件,如下,点击编辑进行修改:
如下,我们修改了 file1:
此时,远程仓库是要领先于本地仓库⼀个版本,为了使本地仓库保持最新的版本,我们需要拉取远端代码,并合并到本地。Git 提供了 git pull
命令,该命令用于从远程获取代码并合并本地的版本。格式如下:
git pull <远程主机名> <远程分支名>:<本地分支名> # 如果远程分支是与当前分支合并,则冒号后⾯的部分可以省略。 git pull <远程主机名> <远程分支名>
我们使用一下:
这里可以直接使用 git pull
,因为我们在克隆的时候本地的 master 分支和远端的 master 分支已经对应起来。
4. 配置 Git
(1)忽略特殊文件
在⽇常开发中,我们有些文件不想或者不应该提交到远端,比如保存了数据库密码的配置⽂件,那怎么让 Git 知道呢?在 Git ⼯作区的根目录下创建⼀个特殊的 .gitignore
⽂件,然后把要忽略的⽂件名填进去,Git 就会自动忽略这些文件了。
不需要从头写 .gitignore ⽂件,gitee 在创建仓库时就可以为我们生成,不过需要我们主动勾选⼀下:
如果当时没有选择这个选择,在⼯作区创建⼀个也是可以的。无论哪种方式,最终都可以得到⼀个完整的 .gitignore 文件,例如我们想忽略以 .so 和 .ini 结尾所有⽂件, .gitignore 的内容如下:
*.ini *.so
在 .gitignore 文件中也可以指定某个确定的文件。最后⼀步就是把 .gitignore 也提交到远端,就完成了。
(2)给命令配置别名
在我们使用 Git 期间,有些命令敲的时候着实让⼈头疼(太长了),幸运的是,git 支持对命令进行简化!
例如,将 git status
简化为 git st
,对应的命令为:
git config --global alias.st status
效果如下:
5. 标签管理
(1)创建标签
标签 tag ,可以简单的理解为是对某次 commit 的⼀个标识,相当于起了⼀个别名。例如,在项⽬发布某个版本的时候,针对最后⼀次 commit 起⼀个 v1.0 这样的标签来标识里程碑的意义。
这有什么用呢?相较于难以记住的 commit id , tag 很好的解决这个问题,因为 tag ⼀定要给⼀个让人容易记住,且有意义的名字。当我们需要回退到某个重要版本时,直接使用标签就能很快定位到。
在 Git 中打标签非常简单,首先,切换到需要打标签的分支上:
然后,使用命令 git tag [tag name]
就可以打⼀个新标签:
可以用命令 git tag
查看所有标签:
默认标签是打在最新提交的 commit 上的。那如何在指定的 commit 上打标签呢?⽅法是找到历史提交的 commit id,然后打上就可以了,示例如下:
先查看历史提交的记录:
再对某一次提交打标签,将它的 commit id 跟在 git tag [tag name]
后即可;假设我们给 fix bug 的那一次提交打标签,并起标签名为 v0.9:
如上,标签就已经打好了;注意,标签不是按时间顺序列出,而是按字母排序的。我们可以用 git show [tag name]
查看标签信息:
最后,Git 还提供可以创建带有说明的标签,⽤ -a
指定标签名,-m
指定说明文字,格式为:
git tag -a [tag name] -m "XXX" [commit id]
例如:
再查看标签,就可以看见我们的指定说明文字了:
(2)操作标签
如果标签打错了,也可以删除,指令为:
git tag -d v1.0
如下所示:
其实我们的远程仓库也有标签,因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。
如果要推送某个标签到远程,使用命令: git push origin <tag name>
即可,如下:
此时我们查看远程仓库中的标签,确实推送到了远程:
当然,如果我们的本地有很多标签,也可以⼀次性的全部推送到远端,其指令为:git push origin --tags
;这里就不做演示了。
如果标签已经推送到远程,要删除远程标签就麻烦⼀点,先从本地删除:
然后,从远程删除,删除命令也是 push,指令为:git push origin :[tag name]
,如下:
我们查看远程仓库的情况:
远程仓库的标签也确实删除了。