# Git 变基(rebase)、ssh 免密登录、Issue 任务协作

TIP

从本节内容开始,我们来补充几个我们在未来的 Git 项目中可能会用得到的一些功能:

  • Git 变基
  • ssh 免密登录
  • Issue 任务协作

# 一、变基

TIP

在 Git 中整合来自不同分支的修改主要有两种方法:merge 和 rebase。

在本节中我们将学习什么是 “变基”,怎样使用 “变基”,并将展示该操作的惊艳之处,以及指出在何种情况下你应避免使用它。

# 1、什么是变基

TIP

rebase(变基)翻译过来就是改变基底的意思,你可以理解为通过改变某个分支的基底从而实现分支的合并。

以下是一个项目的 Git 提交历史记录,假设现在我们要把dev分支合并到master

image-20231207184035736

# 1.1、merge 合并分支

如果采用 merge 来合并,我们会执行以下命令来合并

git checkout master  # 切换到master分支
git merge dev  # 将dev分支合并到master分支

合并后的提交记录如下图一

image-20231207184627789

# 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

此时提交记录如下图二

image-20231207192406467

我们的目标是把 dev 合并到 master 分支,所以此时还没有完成合并,我们还需要执行以下命令,才能完成合并

git checkout master  # 切换到master分支
git merge dev # 将dev合并到master

因为此时的'D1'D2相当于是从 V4 分支分出来的,所以dev分支合并到master分支,属于快速合并,直接移动master指针指向'D2即可。

最终合并后的提交记录如下图三

image-20231207202916081

从前面的图一可以看出,原来dev分支是从V2分叉出来的,相当于 dev 分支的基底是V2

从上面图三可以看出,现在 dev 分支变成了从V4 分叉出来的,相当于dev分支的基底是 V4。(上面的'D1相当于是D1V4合并后生成的,'D2相当于是D1V4合并生成的)。

注意

图三里面的'D2版本内容与图一里面V5内容是一模一样的。这两种整合方法(merge 与 rebase)的最终结果没有任何区别,但是变基使得提交历史更加整洁。

merge 与 rebase 的区别

变基是将一系列提交按照原有次序依次应用到另一分支上,而合并是把最终结果合在一起。

# 2、实操:变基的基本操作

TIP

我们来实操上面提到的 rebase 实现分支合并的方式,并了解其背后实现合并的原理。

首先我们需要创建一个Git项目,并完成如下图所示的提交记录:

image-20231207184035736

# 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 分支的提交记录

image-20231207201812741

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 分支的提交记录

image-20231207202228959

# 2.2、rebase 实现分支合并

接下来,我们执行以下命令,利用 rebase 方式实现变基,将 dev 分支变基为以 master 分支当前最新提交为基底。

git checkout dev # 切换到dev分支
git rebase master # 将dev分支变基为以master分支当前最新提交为基底

以上命令执行后,我们可以执行git log --oneline --graph来查看提交记录,具体如下:

image-20231207203729651

注意观察,变基后的 D1 与 D2 不再是原来的 D1 与 D2,我们看到他们的版本号已经不一样了。

image-20231207192406467

注意:

观察工作区内容变动,此时register.html文件内容也出现在 dev 分支的当前提交中

接下来,我们需要执行以下命令切换到master分支,然后来将dev分支合并到master分支

git checkout  master  # 切换到master分支
git merge dev  # 将dev分支合并到master分支

以上命令执行后,master 分支提交记录如下:

image-20231207204222164

image-20231207202916081

# 2.3、变基时遇到冲突

TIP

变基时如果遇到冲突,需要先解决冲突,然后再执行git add,最后再执行git rebase --continue来继续完成变基操作。

如果想要中止变基操作,可以执行git rebase --abort命令

# 3、变基:多次提交合并为一次

TIP

如果我们在本地开发时,因为某种原因提交次数过多,造成中间产生很多不必要的提交。如果我们想把这些提交合并成一次提交,就可以利用变基来实现。

下图为某个项目的提交记录:

image-20231208011120468

现在我想把 v3、v4、v5、v6、v7 五条提交记录合并成一条记录,如下图所示:

image-20231208013944292

我就可以执行下命令

git checkout dev  # 切换到dev分支

# -i  表示 列出即将重写的提交列表。 让用户在重写前编辑该列表
git rebase -i  2945168 #  2945168为v2的版本号

#  以下命令与上面 git rebase -i  是相同的效果
 git rebase -i HEAD~5  # 从当前提交向前找5个

以上命令执行后,会在 VSCode 编辑器打开一个文件,如下图:

image-20231208012056772

你可以编辑版本号前面的字段,来告诉 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 次提交合并成一次提交,并要修改本次提交的提交记录。

所以我们把文件修改成如下:

image-20231208012617998

然后保存,并关闭该文件。关闭后会打开如下文件,让你为合并后的V3'写一个提交描述。

image-20231208012822654

填写描述后保存,然后关闭这个文件,又会打开一个文件,这里会显示之前每个版本的提交描述,你在这里如果还需要修改新生 V3 版本的提交记录,就可以再这里再修改,如果不需要就直接关闭就好。

image-20231208013115874

关闭后,就会提示变基合并成功,如下图所示:

image-20231208013230528

此时,我们可以执行git log --oneline命令来查看当前的提交记录,发现只有三条了,如下图所示:

image-20231208013342208

# 4、变基的风险

TIP

呃,奇妙的变基也并非完美无缺,要用它得遵守一条准则:

如果提交存在于你本地的仓库之外,而别人可能基于这些提交进行开发,那么不要执行变基。

如果你遵循这条金科玉律,就不会出差错。 否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你。

变基操作的实质是丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交。 如果你已经将提交推送至某个仓库,而其他人也已经从该仓库拉取提交并进行了后续工作,此时,如果你用 git rebase 命令重新整理了提交并再次推送,你的同伴因此将不得不再次将他们手头的工作与你的提交进行整合,如果接下来你还要拉取并整合他们修改过的提交,事情就会变得一团糟。

以上内容来自 Git 官方文档 - 变基的风险 (opens new window)

# 5、变基 VS 合并

TIP

至此,你已在实战中学习了变基和合并的用法,你一定会想问,到底哪种方式更好。 在回答这个问题之前,让我们退后一步,想讨论一下提交历史到底意味着什么。

有一种观点认为,仓库的提交历史即是 记录实际发生过什么。 它是针对历史的文档,本身就有价值,不能乱改。 从这个角度看来,改变提交历史是一种亵渎,你使用 谎言 掩盖了实际发生过的事情。 如果由合并产生的提交历史是一团糟怎么办? 既然事实就是如此,那么这些痕迹就应该被保留下来,让后人能够查阅。

另一种观点则正好相反,他们认为提交历史是 项目过程中发生的事。 没人会出版一本书的第一版草稿,软件维护手册也是需要反复修订才能方便使用。 持这一观点的人会使用 rebasefilter-branch 等工具来编写故事,怎么方便后来的读者就怎么写。

现在,让我们回到之前的问题上来,到底合并还是变基好?希望你能明白,这并没有一个简单的答案。 Git 是一个非常强大的工具,它允许你对提交历史做许多事情,但每个团队、每个项目对此的需求并不相同。 既然你已经分别学习了两者的用法,相信你能够根据实际情况作出明智的选择。

总的原则是

只对尚未推送或分享给别人的本地修改执行变基操作清理历史, 从不对已推送至别处的提交执行变基操作,这样,你才能享受到两种方式带来的便利。

# 二、ssh 免密登录

TIP

现在 Git 会自动帮我们管理凭存,只要我们在电脑上通过git push命令向远程仓库推送代码时输入过一次用户名和密码,Git 就会帮我们把用户名和密码保存起来。

后面我们只要是在这台电脑上执行git push命令向远程仓库推送代码时,就不需要再输入用户名和密码了。

Git 会把我们输入的用户名和密码保存在 控制面板 -> 用户账户 -> 凭据管理器(管理 windows 凭据) 中。

image-20231208030616615

image-20231208030638601

image-20231208030706176

在早期,Git 并不会帮我们自动管理用户凭据,最常见的免密登录方式就是ssh免密登录

注:

ssh 实现免密登录需要执行以下三步,即可:

  • 生成公钥与私钥
  • 在 Gitee 中添加公钥
  • 在本地 Git 项目中添加远程仓库 ssh 地址

# 1、生成公钥与私钥

首先在命令行中执行以下命令,即可以在我们的电脑上生成一对公钥和私钥

ssh-keygen -t rsa   # 指定以rsa 加密码算法生成密钥  ssh-keygen默认使用rsa密钥,所以不加-t rsa也行

以上命令执行后,会提示我们输入公私钥的生成路径,直接按回车,则直接使用默认名字和路径

image-20231209014725158

接着提示,要我们输入密码短语,如果写了,则在 push 项目时,每次都要输入这里设置的密码。如果不想每次操作时都要输入密码,则可以不填,直接回车。回车后要求再次输入密码,如果前面没有输,就直接回车,如果输了,就再输入一次。

image-20231209021122049

注意:

上面如果你设置了密码短语,在设置时没有任何的输入提示。

这样就在C:\Users\EDY/.ssh路径下生成了一对公私钥,文件名为id_rsa中保存的是私钥,id_rsa.pub中保存的是公钥。

image-20231209014046213

你现在可以用记事本打开id_rsa.pub文件,或者直接执行以下命令在命令行中打开文件

cat C:\Users\EDY/.ssh/id_rsa.pub

image-20231208025800600

# 2、在 Gitee 中添加公钥

按下图所示步骤在 Gitee 账户中添加公钥

image-20231208031602175

image-20231208031134227

# 3、本地 Git 项目添加远程仓库

找到 Git 项目远程仓库的ssh地址,如下图:

image-20231208031830070

然后在 Git 项目中做如下配置

git remote add origin git@gitee.com:qxin905/test.git

后面我们执行以下命令推送代码到远程仓库时,就不需要再输入密码了

git push origin dev

不过在第一次推送时,会有如下提示:

image-20231209023522664

输入 yes 后,SSH 配置成功,此时你还会在.ssh文件夹下看到生成了一个新的known_hosts文件。

image-20231209014243787

但上次推送是失败的,所以接下来,你需要再执行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、开发好习惯

  • 每天开始工作的时候,都应该从远程仓库拉取最新的代码
  • 每天工作完都应该把代码推送到远程仓库,每次推送前需要先拉取合并,再推送。
上次更新时间: 12/12/2023, 1:30:52 AM

大厂最新技术学习分享群

大厂最新技术学习分享群

微信扫一扫进群,获取资料

X