使用rebase的Git工作流

栏目: 编程工具 · 发布时间: 6年前

内容简介:Git是目前最为强大的代码版本管理工具,被开源社区和各大公司所广泛使用。使用Git进行团队协作开发是很便利的一件事情,但是在多人协作的过程中,我们也会面临如何运用好Git的问题。这种情况下,就出现了各种各样的风险总是与利益并存的,使用

Git是目前最为强大的代码版本管理工具,被开源社区和各大公司所广泛使用。使用Git进行团队协作开发是很便利的一件事情,但是在多人协作的过程中,我们也会面临如何运用好Git的问题。这种情况下,就出现了各种各样的 Git Workflow ,而本文将介绍一种基于 rebase 的工作流,这种工作流也是目前开源社区所比较推崇的做法,了解了这种工作流之后可以更好地优化对git的使用、对代码的管理

一、Rebase和Squash

1、Rebase是什么,为什么使用Rebase

rebase 是能够将我们对代码的更改从一个分支集成到另一个分支中的git命令之一(另一个命令是 Merge )。使用 rebase 的一个风险在于,它会改写 commit 历史,如果操作不当那么会是一种破坏性的操作。

风险总是与利益并存的,使用 rebase 也好处良多,体现在于:

  • 能够保持提交记录的清爽,不带来额外的提交历史(而使用 merge 会生成一条 mergecommit
  • 能够保持commit线的线性,保持成一条直线,从而能够容易地看出代码是如何推进的

2、Squash是什么,为什么使用Squash

rebase 可以运行在 “交互(interactive)” 模式下,交互模式下的 rebase 操作允许我们将多次 commit 进行压缩,从而合并成更少的、甚至单一的一次提交。

之所以这么做,是因为在我们有很多 commit 时,在最终合并到 master 分支时,这些 commit 就都会进入 mastercommit 历史中,那么这会导致 master 中的提交历史不那么可读。

举个例子,假设我们为某个 feature 进行了多次 commit ,每次 commit 都只是做了一点点的更新,那么commit历史看起来会像是这样:

e94d2fb (HEAD -> feat-china) feat: Add Shenzhen
81d2258 feat: Add Guangzhou
1525479 feat: Add Shanghai
e8021a2 feat: Add Beijing
40d9fc0 feat: Add headline
ce9657d (upstream/master, origin/master, origin/HEAD, master) feat: i18n (#1)
ab15f4f feat(i18n): Add support for Japanese
a39b290 feat: Add new headline
4774815 Initial commit

我们之所以频繁地提交 commit ,是为了能够及时地存档,以便能够在需要的时候方便地回滚代码。但是为了修复特定问题、开发某个特性的多次小提交,其实是仅仅在当前开发上下文中有意义的,可能我们为此提交了十几个 commit ,但是对于其他同事而言,他们可能就只想知道我们这次做了什么事情。那么通过 Squash 这个操作,我们就能够将这些小 commit 进行合并,组成一个有意义的 commit 。以下是用了 Squash 后的结果:

c27766b (Head -> feat-china) feat: show first-tier cities of China
4774815 (master) Initial commit

二、基本法则

在这套工作流中,有一些重要的法则,即:

  • 在fork的仓库上拉分支 每个贡献者都应该 fork 代码仓库并且在该 fork 出的仓库上进行开发。通过 fork 仓库,就可以拥有独立的工作空间,从而可以放心大胆地进行开发、修改等,对于多人开发而言,还无需事先获得分支权限也能继续开发( Tips: 其实并不建议多人在同一个分支上进行开发,因为这样子很容易导致合并冲突,如果某个 feature 需要不止一个人去开发,相比只拉一个 feature 分支,把大的 feature 拆分为小的可独立工作单元其实是一种更好的实践)
  • 禁止对一个多人协作的分支进行rebase 这一点 非常重要 ,贡献者只能对自己 fork 仓库的分支进行 rebase ,这是因为 rebase 会改写 commit 历史,是比较危险的一种操作,只有对自己 fork 仓库的分支进行 rebase ,才不会有任何问题

三、工作角色

在这套工作流中,涉及两个角色:

  • Maintainer(维护者) :维护者拥有仓库的写权限,他们可对 Pull Request 进行 Review 来决定通过或者拒绝,并且能创建 Git Tag 用于发布
  • Contributor(贡献者) :贡献者拥有仓库的读和 fork 权限,可以查看和创建 issue ,也可以提交 Pull Request 。贡献者也负责解决合并冲突,此外贡献者仅能推送分支到自己 fork 的仓库里

四、准备工作

在开始这套工作流之前,我们需要先做一些起始步骤。首先,需要先 fork 项目仓库(通常原项目仓库惯称为 upstream ,上游仓库);然后,在本地 clone 这个 fork 后的仓库(与此对应的远程仓库惯称为 origin );之后,在本地的 remote 记录中添加这个 upstream 仓库,具体步骤如下:

  1. Fork 上游仓库,如在 Github 中可以点击右上角的“Fork”按钮:
    使用rebase的Git工作流
  2. 在本地 clone 这个 fork 后的仓库,如:
$ git clone [email protected]:RuphiLau/playgit.git
$ cd playgit
  1. 添加上游仓库
$ git remote add upstream [email protected]:trialground/playgit.git
  1. 确认信息:通过 git remote -v 确认我们是否成功进行了配置,正常而言需要显示两组记录(一组 origin ,一组 upstream ),如:
origin [email protected]:RuphiLau/playgit.git (fetch)
origin [email protected]:RuphiLau/playgit.git (push)
upstream [email protected]:trialground/playgit.git (fetch)
upstream [email protected]:trialground/playgit.git (push)

五、工作流

好了,说了这么多前置知识,总算应该开始进入正题了。在工作流中,建议所提交的代码是关联一个 user story(用户故事) 或者一个 issue 的,可以和一些外部系统相关联(如 JIRAVersionOne ),本套工作流的步骤总结如下:

  • 第一步: fetch 上游仓库的更新
  • 第二步:upstream/master 分支和本地 master 合并
  • 第三步: 在本地新建分支
  • 第四步: 写代码、提交代码
  • 第五步: 再次 fetch 上游仓库的更新(以同步在拉取分支之后 upstream/master 中更新的内容)
  • 第六步: 对分支基于 upstream/master 进行 rebasesquash ,并且解决合并冲突(如果有)
  • 第七步: 推送分支
  • 第八步: 发起一个 Pull Request ,进行 Code ReviewCode Review 通过后,合并到 master ,此后可以删除本地分支

以下是具体的细节:

1、 fetch 上游仓库的更新

我们开发时,应当基于最新版本的代码库进行开发。国际惯例,我们一般将原始被fork的那个仓库成为 upstream 仓库(上游仓库),通过 fetch 命令,我们可以将 upstream 中的内容获取下来存在本地,即执行:

$ git fetch upstream

示例流程图:

ORIGIN:
 master: A->B

UPSTREAM:
 master: A->B->C

LOCAL:
          master: A->B
   origin/master: A->B
 upstream/master: A->B->C   # 获取结果

2、将 upstream/master 分支和本地 master 合并

一般情况下,我们都是先拉取远程分支,然后基于此创建本地新分支,这样子就能够拥有最新的代码。不过在创建本地新分支前,我们可以先执行以下命令:

$ git checkout master
$ git merge upstream/master

这样子将会执行一个 快速合并(Fast-Forward) 来让 masterupstream/master 指向同一个提交,示例图:

ORIGIN:
 master: A->B

UPSTREAM:
 master: A->B->C

LOCAL:
          master: A->B->C   # 新增了C
   origin/master: A->B
 upstream/master: A->B->C

3、创建本地新分支

现在 master 分支是最新的了,因此我们可以基于此来拉新分支:

$ git checkout -b feat-xyz

示例图:

ORIGIN:
 master: A->B

UPSTREAM:
 master: A->B->C

LOCAL:
          master: A->B->C
   origin/master: A->B
 upstream/master: A->B->C
        feat-xyz: A->B->C   # 新增了 feat-xyz 分支

4、编写代码、提交代码

这一步就是开发的主要步骤了,开发过程中我们可以时不时地提交 commit (当然也要确保 commit 是有意义的),示例图:

ORIGIN:
 master: A->B

UPSTREAM:
 master: A->B->C

LOCAL:
          master: A->B->C
   origin/master: A->B
 upstream/master: A->B->C
        feat-xyz: A->B->C->D->E->F   # 新增了 D、E、F 三次 commit

5、再次拉取代码

编码结束后,在发起一个 Pull Request 合并上游仓库的 master 分支之前,我们需要抓取一些 upstream 里的新提交记录(这是因为对于原仓库而言,我们不是唯一的一个开发者),示例图:

ORIGIN:
 master: A->B

UPSTREAM:
 master: A->B->C->C2->C3   # 其他人新提交了 C2、C3 两次 commit

LOCAL:
          master: A->B->C
   origin/master: A->B
 upstream/master: A->B->C
        feat-xyz: A->B->C->D->E->F

为了保持和 upstream/master 的同步,我们需要使用 git fetch

$ git fetch upstream

拉取之后,示例图:

LOCAL:
          master: A->B->C
   origin/master: A->B
 upstream/master: A->B->C->C2->C3   # 拉取结果
        feat-xyz: A->B->C->D->E->F

6、Rebase和Squash

rebase 会改变原 commit 所基于的分支(简称 变基 ),并且创建带着新 SHA-1 哈希的新 commit (与此同时会保留原有提交信息)。而 squash 会将多个 commit 压缩为一个新的 commit (也可以是多个 commit )并且带上新的 SHA-1 哈希值。通常情况下,我们会想要对目标合并分支进行 rebase ,当最终创建 Pull Request 的时候,这个 rebasesquash 后的分支就会从 origin 仓库的分支进入到 upstream 仓库的master分支里,所以我们需要 rebase upstream/master ,为了执行 squash 操作,我们需要运行交互模式的 rebase ,如下:

$ git rebase --interactive upstream/master

这将会打开默认的编辑器,然后呈现将被 rebasecommit 列表,如:

pick 40d9fc0 feat: Add headline
pick e8021a2 feat: Add Beijing
pick 1525479 feat: Add Shanghai
pick 81d2258 feat: Add Guangzhou
pick e94d2fb feat: Add Shenzhen

# Rebase ce9657d..e94d2fb onto ce9657d (5 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

开头即为 commit 列表,而 # 中的内容则是对一些操作的解释。 commit 列表中列出了从旧到新的每次 commit ,然后我们可以使用说明里的命令选择对这些 commit 执行怎样的一个操作,当我们操作完了后,可以保存文件然后退出编辑器。这之后, rebase 就会基于所选择的命令进行处理。

rebase 过程中,可能会有合并冲突(比如你和 upstream/master 里都修改了同个文件的同一部分),那么这时候,需要手动解决冲突,步骤如下:

  • 通过 git status 查看哪些文件发生了冲突
  • 手动解决冲突
  • 运行 git add 来暂存文件
  • 运行 git rebase --continue 来继续合并过程(不需要用 git commit 来解决合并冲突)

rebase 之后的示例图如下:

ORIGIN:
 master: A->B

UPSTREAM:
 master: A->B->C->C2->C3

LOCAL:
          master: A->B->C
   origin/master: A->B
 upstream/master: A->B->C->C2->C3
        feat-xyz: A->B->C->C2->C3->D->E->F   # Rebase结果

如果我们还执行了 squash 操作,那么这多次的 commit 会被压缩, git 会提示我们输入 commit message 来作为这次压缩后的 commit message ,所以我们通常需要让这条 message 能够概括多次 commit 的内容,示例图:

LOCAL:
          master: A->B->C
   origin/master: A->B
 upstream/master: A->B->C->C2->C3
        feat-xyz: A->B->C->C2->C3->DEF   # Squash结果

7、推送分支

为了创建一个 Pull Request ,我们需要将分支推送到 origin 仓库,可以运行:

$ git push --set-upstream origin feat-china

如果你已经推送过你的分支了,并且想要更新它,那么以上的命令会执行失败。这是因此 rebase 改写了 commit 历史,所以不再有一个公共的 commit ,因此需要使用 --force 选项来告诉 git 放弃并覆盖远程分支,即:

$ git push --force origin feat-china

Tips:以上也是为什么我们要在 fork 分支上独立写代码的主要原因

8、发起一个Pull Request

到这里,是时候对上游仓库的 master 分支发起一个来自 origin 仓库 feat-china 分支的 PR 了,通常在我们 push 之后,在 github 中可以看到如下图所示:

使用rebase的Git工作流

这时候点击 “Compare && pull request” 便可进入发起 PR 的流程

ORIGIN:
   master: A->B
 feat-xyz: A->B->C->C2->C3->DEF  ----
                                    | Pull Request
UPSTREAM:                           | 
 master: A->B->C->C2->C3        <----

进入 PR 创建页面后,我们可以填写对这个 PR 的描述信息,然后点击 Create pull request 便可成功发起 PR
使用rebase的Git工作流

我们可以通过填写 Reviewers 邀请同事进行 Code Review (与此同时,还可以通过 CI 处理一些流程,比如校验代码格式、判断是否已经 Review 通过,必须完成这项步骤才能合并代码):

使用rebase的Git工作流

CI (如果有)通过和 Code Review 通过后,我们便可以点击 Squash and merge 或者 Rebase and merge 来合并代码到 master (这里我们推荐 Squash and merge ,可以压缩多次 commit 为一次):

使用rebase的Git工作流

此后,维护者接受PR,代码就会被自动合并到 master 分支里,并且关闭该 PR 。然后,我们也可以做一些后期清理工作:删除本地分支和远程分支(如 feat-chinaorigin/feat-china ),可以执行如下命令:

$ git branch -D feat-china
$ git push origin --delete feat-china

最终效果为:

ORIGIN:
   master: A->B

UPSTREAM:
 master: A->B->C->C2->C3->DEF

LOCAL:
          master: A->B->C
   origin/master: A->B
 upstream/master: A->B->C->C2->C3->DEF

六、成果

现在,让我们来看一下这个工作流带来的成果,以下是以上步骤完成后,所呈现出的一个 commit 历史:

使用rebase的Git工作流

怎么样,是不是又干净又清爽呢?I do believe that you'll enjoy it !

参考资料


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Four

The Four

Scott Galloway / Portfolio / 2017-10-3 / USD 28.00

NEW YORK TIMES BESTSELLER USA TODAY BESTSELLER Amazon, Apple, Facebook, and Google are the four most influential companies on the planet. Just about everyone thinks they know how they got there.......一起来看看 《The Four》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

MD5 加密
MD5 加密

MD5 加密工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具