基于rebase的Git工作流

2024-04-02 23:58
文章标签 工作 git rebase

本文主要是介绍基于rebase的Git工作流,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

参考:https://www.phpmianshi.com/?id=124

使用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历史看起来就会杂乱无章。

比如我们的commit历史是这样的:

commit a
commit b
commit c

我们可以利用:

git rebase -i HEAD~3

把三个commit 合并成一个 commit 

 

我们之所以频繁地提交commit,是为了能够及时地存档,以便能够在需要的时候方便地回滚代码。但是为了修复特定问题、开发某个特性的多次小提交,其实是仅仅在当前开发上下文中有意义的,可能我们为此提交了十几个commit,但是对于其他同事而言,他们可能就只想知道我们这次做了什么事情。那么通过Squash这个操作,我们就能够将这些小commit进行合并,组成一个有意义的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上游仓库,如在Gitlab中可以点击右上角的“Fork”按钮,选择自己的空间,确定

  2. 在本地clone这个fork后的仓库,如:

$ git clone git@git.phpmianshi.com:yang/app.git
  1. 添加上游仓库

$ git remote add upstream git@git.phpmianshi.com:php/app.git
  1. 确认信息:通过git remote -v确认我们是否成功进行了配置,正常而言需要显示两组记录(一组origin,一组upstream),如:

origin git@phpmianshi.com:yang/app.git (fetch)
origin git@phpmianshi.com:yang/app.git (push)
upstream git@phpmianshi.com:php/app.git (fetch)
upstream git@phpmianshi.com:php/app.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

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

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

$ git checkout master
$ git merge upstream/master

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

3、创建本地新分支

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

$ git checkout -b feat-xyz

4、编写代码、提交代码

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

5、再次拉取代码

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

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

$ git fetch upstream

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来解决合并冲突)

git会提示我们输入commit message来作为这次压缩后的commit message,所以我们通常需要让这条message能够概括多次commit的内容

7、推送分支

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

$ git push --set-upstream origin search

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

$ git push origin HEAD:search -f
Enumerating objects: 92, done.
Counting objects: 100% (92/92), done.
Delta compression using up to 8 threads
Compressing objects: 100% (59/59), done.
Writing objects: 100% (74/74), 12.02 KiB | 947.00 KiB/s, done.
Total 74 (delta 58), reused 25 (delta 15), pack-reused 0
remote:
remote: To create a merge request for search, visit:
remote:   https://git.phpmianshi.com.com/yang/app/merge_requests/new?merge_request%5Bsource_branch%5D=search
remote:
To git.phpmianshi.com:yang/app.git* [new branch]        HEAD -> search

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

8、发起一个Pull Request

到这里,是时候对上游仓库的master分支发起一个来自origin仓库search分支的PR了,可以直接复制上一步的输出结果中的url:

https://git.phpmianshi.com.com/yang/app/merge_requests/new?merge_request%5Bsource_branch%5D=search

 

就可以直接打开gitlab进行merge了,这时候点击“Compare && pull request”便可进入发起PR的流程

 

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

 

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

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

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

$ git branch -D search
$ git push origin --delete search

 

到这里基本就结束了,欢迎留言沟通其他的git workflow 。谢谢

这篇关于基于rebase的Git工作流的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/871405

相关文章

git使用的说明总结

Git使用说明 下载安装(下载地址) macOS: Git - Downloading macOS Windows: Git - Downloading Windows Linux/Unix: Git (git-scm.com) 创建新仓库 本地创建新仓库:创建新文件夹,进入文件夹目录,执行指令 git init ,用以创建新的git 克隆仓库 执行指令用以创建一个本地仓库的

git ssh key相关

step1、进入.ssh文件夹   (windows下 下载git客户端)   cd ~/.ssh(windows mkdir ~/.ssh) step2、配置name和email git config --global user.name "你的名称"git config --global user.email "你的邮箱" step3、生成key ssh-keygen

工作常用指令与快捷键

Git提交代码 git fetch  git add .  git commit -m “desc”  git pull  git push Git查看当前分支 git symbolic-ref --short -q HEAD Git创建新的分支并切换 git checkout -b XXXXXXXXXXXXXX git push origin XXXXXXXXXXXXXX

查看提交历史 —— Git 学习笔记 11

查看提交历史 查看提交历史 不带任何选项的git log-p选项--stat 选项--pretty=oneline选项--pretty=format选项git log常用选项列表参考资料 在提交了若干更新,又或者克隆了某个项目之后,你也许想回顾下提交历史。 完成这个任务最简单而又有效的 工具是 git log 命令。 接下来的例子会用一个用于演示的 simplegit

记录每次更新到仓库 —— Git 学习笔记 10

记录每次更新到仓库 文章目录 文件的状态三个区域检查当前文件状态跟踪新文件取消跟踪(un-tracking)文件重新跟踪(re-tracking)文件暂存已修改文件忽略某些文件查看已暂存和未暂存的修改提交更新跳过暂存区删除文件移动文件参考资料 咱们接着很多天以前的 取得Git仓库 这篇文章继续说。 文件的状态 不管是通过哪种方法,现在我们已经有了一个仓库,并从这个仓

忽略某些文件 —— Git 学习笔记 05

忽略某些文件 忽略某些文件 通过.gitignore文件其他规则源如何选择规则源参考资料 对于某些文件,我们不希望把它们纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。通常它们都是些自动生成的文件,比如日志文件、编译过程中创建的临时文件等。 通过.gitignore文件 假设我们要忽略 lib.a 文件,那我们可以在 lib.a 所在目录下创建一个名为 .gi

取得 Git 仓库 —— Git 学习笔记 04

取得 Git 仓库 —— Git 学习笔记 04 我认为, Git 的学习分为两大块:一是工作区、索引、本地版本库之间的交互;二是本地版本库和远程版本库之间的交互。第一块是基础,第二块是难点。 下面,我们就围绕着第一部分内容来学习,先不考虑远程仓库,只考虑本地仓库。 怎样取得项目的 Git 仓库? 有两种取得 Git 项目仓库的方法。第一种是在本地创建一个新的仓库,第二种是把其他地方的某个

Git 的特点—— Git 学习笔记 02

文章目录 Git 简史Git 的特点直接记录快照,而非差异比较近乎所有操作都是本地执行保证完整性一般只添加数据 参考资料 Git 简史 众所周知,Linux 内核开源项目有着为数众多的参与者。这么多人在世界各地为 Linux 编写代码,那Linux 的代码是如何管理的呢?事实是在 2002 年以前,世界各地的开发者把源代码通过 diff 的方式发给 Linus,然后由 Linus

集中式版本控制与分布式版本控制——Git 学习笔记01

什么是版本控制 如果你用 Microsoft Word 写过东西,那你八成会有这样的经历: 想删除一段文字,又怕将来这段文字有用,怎么办呢?有一个办法,先把当前文件“另存为”一个文件,然后继续改,改到某个程度,再“另存为”一个文件。就这样改着、存着……最后你的 Word 文档变成了这样: 过了几天,你想找回被删除的文字,但是已经记不清保存在哪个文件了,只能挨个去找。真麻烦,眼睛都花了。看

嵌入式方向的毕业生,找工作很迷茫

一个应届硕士生的问题: 虽然我明白想成为技术大牛需要日积月累的磨练,但我总感觉自己学习方法或者哪些方面有问题,时间一天天过去,自己也每天不停学习,但总感觉自己没有想象中那样进步,总感觉找不到一个很清晰的学习规划……眼看 9 月份就要参加秋招了,我想毕业了去大城市磨练几年,涨涨见识,拓开眼界多学点东西。但是感觉自己的实力还是很不够,内心慌得不行,总怕浪费了这人生唯一的校招机会,当然我也明白,毕业