# Git 变基(rebase)、ssh 免密登录、Issue 任务协作
TIP
从本节内容开始,我们来补充几个我们在未来的 Git 项目中可能会用得到的一些功能:
- Git 变基
- ssh 免密登录
- Issue 任务协作
# 一、变基
TIP
在 Git 中整合来自不同分支的修改主要有两种方法:merge 和 rebase。
在本节中我们将学习什么是 “变基”,怎样使用 “变基”,并将展示该操作的惊艳之处,以及指出在何种情况下你应避免使用它。
# 1、什么是变基
TIP
rebase(变基)翻译过来就是改变基底的意思,你可以理解为通过改变某个分支的基底从而实现分支的合并。
以下是一个项目的 Git 提交历史记录,假设现在我们要把
dev
分支合并到master
分
# 1.1、merge 合并分支
如果采用 merge 来合并,我们会执行以下命令来合并
git checkout master # 切换到master分支
git merge dev # 将dev分支合并到master分支
合并后的提交记录如下图一:
# 1.2、rebase 合并分支
TIP
如果采用 rebase 来合并分支,会先执行以下命令
git checkout dev # 切换到dev分支
git rebase master # 以master分支最后一次的提交作为dev分支的基底来实现变基
注:
上面的 git rebase master
命令内部相当于做了以下几件事
- 首先会找到两个分支(即当前分支 dev、变基操作的目标基底分支 master)的最近共同祖先 V2
- 然后找到 dev 分支 V2 之后的每一次提交(D1,D2)相对于目标基底 master 分支 V4 的差异,并存为临时文件。
- 然后以 V4 为基底,把 D1 与 V4 的差异与 V4 合并,生成新的版本
'D1
,此时'D1
会指向 V4 - 然后以 V4 为基底,把 D2 与 V4 的差异与 V4 合并,生成新的版本
'D2
,此时'D2
会指向'D1
此时提交记录如下图二:
我们的目标是把 dev 合并到 master 分支,所以此时还没有完成合并,我们还需要执行以下命令,才能完成合并
git checkout master # 切换到master分支
git merge dev # 将dev合并到master
因为此时的
'D1
与'D2
相当于是从 V4 分支分出来的,所以dev
分支合并到master
分支,属于快速合并,直接移动master
指针指向'D2
即可。
最终合并后的提交记录如下图三:
从前面的图一可以看出,原来
dev
分支是从V2
分叉出来的,相当于 dev 分支的基底是V2
。
从上面图三可以看出,现在 dev 分支变成了从V4
分叉出来的,相当于dev
分支的基底是 V4。(上面的'D1
相当于是D1
与V4
合并后生成的,'D2
相当于是D1
与V4
合并生成的)。
注意
图三里面的'D2
版本内容与图一里面V5
内容是一模一样的。这两种整合方法(merge 与 rebase)的最终结果没有任何区别,但是变基使得提交历史更加整洁。
merge 与 rebase 的区别
变基是将一系列提交按照原有次序依次应用到另一分支上,而合并是把最终结果合在一起。
# 2、实操:变基的基本操作
TIP
我们来实操上面提到的 rebase 实现分支合并的方式,并了解其背后实现合并的原理。
首先我们需要创建一个
Git
项目,并完成如下图所示的提交记录:
# 2.1、创建 Git 项目
执行以下命令创建 git 项目
mkdir web
cd .\web\
git init
V1 版本
在web
目录下新建index.html
,内容如下:
<body>
<div class="master">master---V1</div>
</body>
然后执行以下命令,然后在 master 分支生成第一个版本 V1
git add .
git commit -m 'V1'
V2 版本
修改index.html
文件,内容如下,然后提交生成第二个版本 V2
<div class="master">master---V1</div>
<div class="master">master---V2</div>
D1 版本
在 V2 版本基础上创建 dev 分支,并切换到 dev 分支,然后新建login.html
文件,内容如下:
<div class="login">dev---D1</div>
最后提交生成第三个版本 D1
D2 版本
修改login.html
文件内容如下,然后提交生成第四个版本 D2
<div class="login">dev---D1</div>
<div class="login">dev---D2</div>
最后 dev 分支的提交记录
V3 版本
切换到 master 分支,然后新建register.html
文件,内容如下。
<div class="register">master---V3</div>
然后提交生成第 5 个版本 V3
V4 版本
修改register.html
文件,内容如下
<div class="register">master---V3</div>
<div class="register">master---V4</div>
然后提交生成第 6 个版本 V4
最后 master 分支的提交记录
# 2.2、rebase 实现分支合并
接下来,我们执行以下命令,利用 rebase 方式实现变基,将 dev 分支变基为以 master 分支当前最新提交为基底。
git checkout dev # 切换到dev分支
git rebase master # 将dev分支变基为以master分支当前最新提交为基底
以上命令执行后,我们可以执行git log --oneline --graph
来查看提交记录,具体如下:
注意观察,变基后的 D1 与 D2 不再是原来的 D1 与 D2,我们看到他们的版本号已经不一样了。
注意:
观察工作区内容变动,此时register.html
文件内容也出现在 dev 分支的当前提交中
接下来,我们需要执行以下命令切换到master
分支,然后来将dev
分支合并到master
分支
git checkout master # 切换到master分支
git merge dev # 将dev分支合并到master分支
以上命令执行后,master 分支提交记录如下:
# 2.3、变基时遇到冲突
TIP
变基时如果遇到冲突,需要先解决冲突,然后再执行git add
,最后再执行git rebase --continue
来继续完成变基操作。
如果想要中止变基操作,可以执行
git rebase --abort
命令
# 3、变基:多次提交合并为一次
TIP
如果我们在本地开发时,因为某种原因提交次数过多,造成中间产生很多不必要的提交。如果我们想把这些提交合并成一次提交,就可以利用变基来实现。
下图为某个项目的提交记录:
现在我想把 v3、v4、v5、v6、v7 五条提交记录合并成一条记录,如下图所示:
我就可以执行下命令
git checkout dev # 切换到dev分支
# -i 表示 列出即将重写的提交列表。 让用户在重写前编辑该列表
git rebase -i 2945168 # 2945168为v2的版本号
# 以下命令与上面 git rebase -i 是相同的效果
git rebase -i HEAD~5 # 从当前提交向前找5个
以上命令执行后,会在 VSCode 编辑器打开一个文件,如下图:
你可以编辑版本号前面的字段,来告诉 Git 你想要做的操作。版本号前面字段只能是以下几种形式中的一种
命令 | 缩写 | 含义 |
---|---|---|
pick | p | 保留该 commit |
reword | r | 保留该 commit,但需要修改该 commit 的注释 |
edit | e | 保留该 commit, 但我要停下来修改该提交(不仅仅修改注释) |
squash | s | 将该 commit 合并到前一个 commit |
fixup | f | 将该 commit 合并到前一个 commit,但不要保留该提交的注释信息 |
exec | e | 执行 shell 命令(不常用) |
drop | d | 丢弃该 commit |
我这们里是希望将 V3、V4、V5、V6、V7 这 5 次提交合并成一次提交,并要修改本次提交的提交记录。
所以我们把文件修改成如下:
然后保存,并关闭该文件。关闭后会打开如下文件,让你为合并后的V3'
写一个提交描述。
填写描述后保存,然后关闭这个文件,又会打开一个文件,这里会显示之前每个版本的提交描述,你在这里如果还需要修改新生 V3 版本的提交记录,就可以再这里再修改,如果不需要就直接关闭就好。
关闭后,就会提示变基合并成功,如下图所示:
此时,我们可以执行git log --oneline
命令来查看当前的提交记录,发现只有三条了,如下图所示:
# 4、变基的风险
TIP
呃,奇妙的变基也并非完美无缺,要用它得遵守一条准则:
如果提交存在于你本地的仓库之外,而别人可能基于这些提交进行开发,那么不要执行变基。
如果你遵循这条金科玉律,就不会出差错。 否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你。
变基操作的实质是丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交。 如果你已经将提交推送至某个仓库,而其他人也已经从该仓库拉取提交并进行了后续工作,此时,如果你用 git rebase
命令重新整理了提交并再次推送,你的同伴因此将不得不再次将他们手头的工作与你的提交进行整合,如果接下来你还要拉取并整合他们修改过的提交,事情就会变得一团糟。
# 5、变基 VS 合并
TIP
至此,你已在实战中学习了变基和合并的用法,你一定会想问,到底哪种方式更好。 在回答这个问题之前,让我们退后一步,想讨论一下提交历史到底意味着什么。
有一种观点认为,仓库的提交历史即是 记录实际发生过什么。 它是针对历史的文档,本身就有价值,不能乱改。 从这个角度看来,改变提交历史是一种亵渎,你使用 谎言 掩盖了实际发生过的事情。 如果由合并产生的提交历史是一团糟怎么办? 既然事实就是如此,那么这些痕迹就应该被保留下来,让后人能够查阅。
另一种观点则正好相反,他们认为提交历史是 项目过程中发生的事。 没人会出版一本书的第一版草稿,软件维护手册也是需要反复修订才能方便使用。 持这一观点的人会使用 rebase
及 filter-branch
等工具来编写故事,怎么方便后来的读者就怎么写。
现在,让我们回到之前的问题上来,到底合并还是变基好?希望你能明白,这并没有一个简单的答案。 Git 是一个非常强大的工具,它允许你对提交历史做许多事情,但每个团队、每个项目对此的需求并不相同。 既然你已经分别学习了两者的用法,相信你能够根据实际情况作出明智的选择。
总的原则是
只对尚未推送或分享给别人的本地修改执行变基操作清理历史, 从不对已推送至别处的提交执行变基操作,这样,你才能享受到两种方式带来的便利。
- 以上内容来自 Git 官方文档 - 变基 VS 合并 (opens new window)
- 关于变基更详细的教程可以参考:Git 官方文档 - Git 分支 - 变基 (opens new window)
# 二、ssh 免密登录
TIP
现在 Git 会自动帮我们管理凭存,只要我们在电脑上通过git push
命令向远程仓库推送代码时输入过一次用户名和密码,Git 就会帮我们把用户名和密码保存起来。
后面我们只要是在这台电脑上执行git push
命令向远程仓库推送代码时,就不需要再输入用户名和密码了。
Git 会把我们输入的用户名和密码保存在 控制面板 -> 用户账户 -> 凭据管理器(管理 windows 凭据) 中。
在早期,Git 并不会帮我们自动管理用户凭据,最常见的免密登录方式就是ssh
免密登录
注:
ssh 实现免密登录需要执行以下三步,即可:
- 生成公钥与私钥
- 在 Gitee 中添加公钥
- 在本地 Git 项目中添加远程仓库 ssh 地址
# 1、生成公钥与私钥
首先在命令行中执行以下命令,即可以在我们的电脑上生成一对公钥和私钥
ssh-keygen -t rsa # 指定以rsa 加密码算法生成密钥 ssh-keygen默认使用rsa密钥,所以不加-t rsa也行
以上命令执行后,会提示我们输入公私钥的生成路径,直接按回车,则直接使用默认名字和路径
接着提示,要我们输入密码短语,如果写了,则在 push 项目时,每次都要输入这里设置的密码。如果不想每次操作时都要输入密码,则可以不填,直接回车。回车后要求再次输入密码,如果前面没有输,就直接回车,如果输了,就再输入一次。
注意:
上面如果你设置了密码短语,在设置时没有任何的输入提示。
这样就在C:\Users\EDY/.ssh
路径下生成了一对公私钥,文件名为id_rsa
中保存的是私钥,id_rsa.pub
中保存的是公钥。
你现在可以用记事本打开id_rsa.pub
文件,或者直接执行以下命令在命令行中打开文件
cat C:\Users\EDY/.ssh/id_rsa.pub
# 2、在 Gitee 中添加公钥
按下图所示步骤在 Gitee 账户中添加公钥
# 3、本地 Git 项目添加远程仓库
找到 Git 项目远程仓库的ssh
地址,如下图:
然后在 Git 项目中做如下配置
git remote add origin git@gitee.com:qxin905/test.git
后面我们执行以下命令推送代码到远程仓库时,就不需要再输入密码了
git push origin dev
不过在第一次推送时,会有如下提示:
输入 yes 后,SSH 配置成功,此时你还会在.ssh
文件夹下看到生成了一个新的known_hosts
文件。
但上次推送是失败的,所以接下来,你需要再执行git push origin dev
就可以直接推送了。
# 三、Issue 任务协作
TIP
深入浅出 Issue 任务协作 与 实践
# 1、什么是 Issue
TIP
我们或许可以将 Issue 定义为问题和待办清单、Bug 列表、讨论版等等,便于大家更好地理解 Issue 的含义和功能。
Issue 作为团队协作中一种可以为我们大大提高效率的方式,可以使我们更方便的对整个仓库进行跟踪、增强和排错。在一个公开的仓库来说,任何人都可以使用 Issue,仓库的所有者和其他任何人都可以向该仓库提 Issue。
Gitee 官方的建议中,项目相关的技术问题、缺陷报告、建议等信息都可以通过 Issue 进行发布。
接下来我将带你了解在仓库中 Issue 如何灵活应用,期待它可以对你的开发协作有一个好的帮助!
# 2、Issue 教程
TIP
Issue 教程的详细内容具体参考 Gitee 官方教程 - Issue 任务协作 (opens new window)
# 四、总结
TIP
总结 Git 从入门到企业项目实践应用的必备核心技能
# 1、Git 中常出现的基本概念
概念 | 描述 |
---|---|
仓库(Repository) | Git 的核心概念,用于存储版本控制的文件和历史记录。 |
提交(Commit) | 对仓库中的文件进行的更改进行记录,包含更改的内容、提交者、提交时间等信息。 |
版本(Version) | 指代某个提交的标识符,通常使用十六进制哈希值表示。 |
分支(Branch) | 用于在不同的开发线路上进行开发,可以创建、合并和管理分支。 |
标签(Tag) | 用于标记仓库中的特定版本,可以用于标识主要版本、发布版本等。 |
工作区(Working Area) | 用户在本地计算机上直接编辑的文件和目录区 |
暂存区(Staging Area) | 用于临时存储用户准备提交的文件状态,等待用户进行提交操作。 |
HEAD | 指向当前活跃分支的指针,用于标识当前分支的当前提交 |
合并(Merge) | 将两个分支的更改合并到一起的操作,通常用于解决冲突和保持代码线的同步。 |
拉取(Pull) | 从远程仓库获取最新的更改并将其合并到本地仓库的操作。 |
推送(Push) | 将本地仓库的更改推送到远程仓库的操作。 |
克隆(Clone) | 复制整个远程仓库到本地计算机的操作,以便在本地进行版本控制。 |
冲突(Conflict) | 当两个或多个提交对同一文件进行了更改时发生,需要手动解决冲突。 |
跟踪(Tracking) | 用于跟踪和链接其他版本库中的更改,允许用户在本地仓库中查看和管理来自远程仓库的提交。 |
变基(Rebase) | 将一个分支的更改应用到另一个分支的操作,通常用于整合不同分支上的更改并保持线性开发历史。 |
master | 仓库的master 分支,默认的主分支,初始化仓库就有了 |
origin/master | 表示远程仓库(origin )的master 分支 |
origin/HEAD | 表示远程仓库(origin )的最新提交的位置,一般情况等于origin/master |
# 2、配置文件
TIP
在安装 Git 时需要全局配置用户名和邮箱信息,主要是为了我们在记录版本信息的时候,记录下来这是谁提交的,通过什么方式可以找到他 (仅配置一次)
命令 | 描述 |
---|---|
git config --global user.name 用户名 | 全局配置用户名 (常用) |
git config --global user.email 邮箱 | 全局配置邮箱(常用) |
git config user.name 用户名 | 只配置该项目的用户名 (特殊情况下用) |
git config user.email 邮箱 | 只配置该项目的邮箱(特殊情况下用) |
git config --system user.name 用户名 | 系统上每一个仓库的通用配置 (用户名)一般不用 |
git config --system user.emali 邮箱 | 系统上每一个仓库的通用配置(邮箱)一般不用 |
# 3、初始化 Git
TIP
如果我们希望用 Git 来帮我们管理当前项目,我们就需要在当前项目中初始化 Git
命令 | 描述 |
---|---|
git init | 在项目中初始化 Git |
# 4、克隆项目
TIP
如果我们首次是通过 clone 方式来获取远程项目到本地,则不需要使用 git init
来初始化 Git
命令 | 描述 |
---|---|
git clone 仓库地址 | 克隆远程仓库项目到本地 |
# 5、生成版本
命令 | 描述 |
---|---|
git status | 检查当前文件状态 |
git add <file1> <file2> | 将工作区的指定内容添加到暂存区 |
git add . | 将工作区的有变动内容全部,添加到暂存区 |
git commit -m '版本说明' | 将暂存区内容提交到本地仓库,生成版本 |
git log | 查看提交记录 |
git log --oneline --graph | 以一行、图形化的方式查看提交记录 |
git reflog | 可以查看所有提交记录,包括被“丢弃”的版本 |
git add 说明
- 使用
git add
后,工作区和暂存区的内容是一致的 - 工作区和暂存区的内容不一致的时候,需要通过
git add
,让它们内容一致
git commit 说明
git commit
后暂存区和本地版本库的内容是一致的- 暂存区和本地版本库内容不一致的时候,需要通过
git commit
让它们内容一致
# 6、撤消操作
命令 | 描述 |
---|---|
git commit -m '提交描述' --amend | 撤消提交操作 |
git restore --staged <file> | 将文件从暂存取撤消 |
git restore --staged . | 撤消暂存取的所有文件 |
git restore <file> | 撤消对工作区某个文件的修改 |
git restore . | 撤消对工作区所有文件的修改 |
注意事项:
- 撤消提交操作,本质上是重新提交生成一个新版本,替换(覆盖)之前的版本
- 将文件从暂存区撤消,并不会影响工作区内容的变化
- 撤消对工作区文件的修改(工作区内容会丢失),使文件内容与暂存区内容保持一次
# 7、重置版本
TIP
- 只重置本地仓库
- 重置本地仓库、暂存区
- 重置本地仓库,暂存区、工作区
命令 | 描述 |
---|---|
git reset --soft 版本号 | 仅重置本地仓库 |
git reset --mixed 版本号 | 重置本地仓库、暂存区 (默认行为) |
git reset --hard 版本号 | 重置本地仓库,暂存区、工作区 |
--hard
很危险,会覆盖正在开发的代码
# 8、分支操作
命令 | 描述 |
---|---|
git branch | 查看分支 |
git branch -v | 查看分支,展示的信息多一些 |
git branch 分支名 | 在当前分支的节点上创建新的分支 |
git checkout 分支名 | 切换分支,兼容性好 |
git switch 分支名 | 切换分支,v2.23.0 版及以后可用 |
git merge 要合并的分支名 | 将指定的分支合并到当前分支 |
git branch -d 分支名 | 删除分支 |
注意
git merge 要合并的分支名
,在合并分支时,先切换到最终要合并的分支,再合并
合并冲突
在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,这种情况 就需要人为的解决冲突,解决后再提交一次。
# 9、.gitignore 忽略文件
TIP
Git 管理的是文件,空目录会自动被 Git 忽略掉
# 注释
test.html # 忽略指定文件,不让Git管理
test.* # 忽略所有文件名是test的文件,不管后缀是什么
*.tmp # 忽略所有后缀是.tmp的文件,不管文件名是什么
!test.tmp # 表示取反,不忽略test.tmp文件,让Git管理该文件
node_modules/ # 忽略node_modules 目录下的所有文件
# 10、远程仓库的操作
TIP
- 创建和删除远程仓库
- 推送本地仓库到远程仓库
git push
- 克隆远程仓库到本地
git clone
- 拉取远程仓库的最新代码到本地
git pull
命令 | 描述 |
---|---|
git remote add 别名(常用 origin) 远程仓库地址 | 配置别名 |
git remote -v | 查看配置的别名 |
git push 别名/远程仓库地址 要推送的分支 | 推送本地版本到远程仓库 |
git remote -v | 查看配置的别名 |
git clone 远程仓库地址 | 没有本地仓库的时候,用 git clone |
git pull 别名 /远程仓库地址 分支名 | 已有本地库,更新时,用 pull |
注意事项
- 别名只针对当前项目,如果 git 管理了很多项目,需要为每个项目配置别名
- 将本地仓库推送到远程仓库是需要权限的 (加入团队就有权限,不加入就没有权限)
# 11、tag 标签常用命令
tag 标签有:轻量标签(lightweight)与 附注标签(annotated)两种类型
命令 | 描述 |
---|---|
git tag -a 标签名 -m 标签说明 | 为当前分支当前提交创建【附注标签】 |
git tag 标签名 | 为当前分支当前提交创建【轻量标签】 |
git tag 标签名 版本号 | 给指定提交版本创建一个【轻量标签】 |
git push 远程仓库别名 标签名 | 推送标签到远程仓库 |
git push 远程仓库别名 --tags | 把所有不在远程仓库服务器上的标签全部传送到那里。 |
git tag -d 标签名 | 删除本地标签名 |
git push 远程仓库别名 --deleted 标签名 | 删除远程仓库标签名 |
git tag | 查看所有标签列表 |
git tag -l 标签名称筛选字符串 | 根据标签名筛选字符串筛选符合要求的标签 |
git show 标签名 | 查看标签的信息(轻量标签和附注标签的信息是不一样的) |
# 12、Git 工作流
以下三种 Git 工作流,具体参考前面的文档
- 单人异地开发工作流
- 团队内多人协作开发工作流
- 跨团队协作开发工作流(为开源贡献自己的源码)
# 12.1、工作流注意事项
- 【主分支】master 分支作为主分支,用来发布稳定版本的代码
- 【开发分支】 每个开发者需要在自己独立的分支上进行开发,开发完再提交审核和测试,没有问题才可以合并到 master 分支。
- 【分支命名】:分支命应使用英文单词,一个分支用来开发一个具体的功能,开发完合并到 master 分支后,即可删除。
- 【分支保护】:主分支和代码审核分支,或某些特定功能的分支,一定要设置分支保护,只有特定的人才能操作这些分支。
- 【分支提交】:不要频繁的创建提交,而应该在完成某个具体的功能后再提交,提交时的描交描述要详细的描述本次提交的内容,主要包括:开发哪些功能、修改了哪些内容等。
# 12.2、开发好习惯
- 每天开始工作的时候,都应该从远程仓库拉取最新的代码
- 每天工作完都应该把代码推送到远程仓库,每次推送前需要先拉取合并,再推送。
大厂最新技术学习分享群
微信扫一扫进群,获取资料
X