为何?还有怎样才能保持你的 Git 提交历史清晰?

文章转发自专业的Laravel开发者社区,原始连接:learnku.com/laravel/t/1…css

提交是 Git 仓库的重要组成部分之一,不只于此,提交信息  贯穿于 Git 仓库的整个生命周期。随着项目/仓库的发展(新特性的增长、Bugs 的修复、架构的重构),提交信息可让咱们看到改了什么地方以及是如何改动的。所以,这些信息以一种简短、精确的方式反映着潜在的变化是很是重要的。html

为何有意义的提交历史很重要

Git 提交消息就像是你在你接触过的代码上面留下的指纹。任何你今天提交的代码,一年之后当你看到这段代码的变化时,你将感谢本身当时留下的清晰、有意义的提交信息,同时它还将使你的开发变得更为容易。当提交被基于上下文独立时,由某个提交引入的 Bug 将会更快被找到,而且更容易恢复到致使错误提交前的代码。前端

当你工做在一个大的项目中时,咱们常常更新、新增或者移动文件。确保在这种状况下维持提交消息将很棘手,尤为是在开发周期跨越数天,数周甚至数月的状况下。所以,为了简化维护简洁提交历史记录的工做,这篇文章将使用开发人员在处理 Git 仓库时可能遇到的一些常见状况。laravel

在咱们深刻讨论以前让咱们快速的了解一下,在咱们假设的 Ruby 应用程序中,典型的开发工做流是什么样子的。git

注意: 这篇文章假定您了解基本的 Git 知识 、分支的工做方式、如何在阶段中添加未提交的分支更改以及如何提交更改。若是你不了解这些流程, 咱们的文档 是一个很好的起点。shell

平常开发中的一天

如今,咱们正在开发一个小型的 Ruby on Rails 项目。咱们须要在首页添加一个导航视图,这涉及到更新和添加几个文件。下面是整个流程的逐步细分:后端

  • 你首先从更新某个文件开始来开发新功能;咱们叫它为application_controller.rb
  • 这个功能也须要你更新一个视图文件:index.html.haml
  • 你添加了一个局部视图,它会在首页中用到:_navigation.html.haml
  • 页面样式也须要更新,让它做用到咱们的视图上:styles.css.scss
  • 新功能已经准备好全部指望的修改,该更新测试文件了;要更新的文件以下:
    • application_controller_spec.rb
    • navigation_spec.rb
  • 测试文件更新完毕并如期经过,如今是时候提交全部更改了!

由于全部的文件分属于架构的不一样区域,因此咱们对这些更改彼此隔离进行提交,确保每次提交表明一个特定的上下文,并按顺序提交。我一般喜欢从后端 -> 前段的顺序。首先提交大多数之后端为中心的更改,接着是中间层,最后是在提交列表中之前端为中心的更改。安全

  1. application_controller.rb & application_controller_spec.rbAdd routes for navigation
  2. _navigation.html.haml & navigation_spec.rbPage Navigation View
  3. index.html.hamlRender navigation partial
  4. styles.css.scssAdd styles for navigation

如今咱们已经提交了更改,咱们使用分支来建立 merge 请求。一旦建立了 merge 请求,一般会在更改合并进 master分支以前由原仓库的拥有者进行审核。如今咱们来了解一下,在代码审核期间咱们可能会面临的不一样状况。bash

状况1:我须要修改最近的提交

想象一下,审阅者查看了styles.css.scss文件并建议进行更改。在这种情形下,进行更改很是简单,由于对样式的更改是你分支上 最近一次 提交的一部分。下面是咱们怎样处理此次更改的步骤:架构

  • 你能够直接在你的分支上对styles.css.scss文件作必要的更改。
  • 一旦你完成了这些更改,请将这些改变添加到暂存区;运行git add styles.css.scss
  • 当这些改变被暂存以后,咱们须要将这些更改 添加 到上次的提交中;运行 git commit --amend
    • 命令分解:在这里,咱们要求git commit命令 修改 暂存区中的最近一次提交。
  • 这将会用你已定义的 Git 文本编辑器打开最近一次提交,而且提交消息是 Add styles for navigation
  • 由于咱们只更新了 CSS 声明,因此无需修改提交消息。此时,你只须要保存并退出 Git 为你打开的文本编辑器,全部的更改将会反映在本次提交中。

因为你修改了现有的提交,因此必须使用git push --force-with-lease <remote_name> <branch_name>将这些更改 强制推送 到你的远程仓库。这个命令将会使用咱们在本次仓库更新后的提交,覆盖远程仓库上消息为Add styles for navigation的那次提交。

在强制推送分支时须要注意的一件事是,若是你和其余同事工做于同一分支,当其余人尝试向一个刚被强制推送过的远程分支正常推送他们的更改时,可能遇到一些麻烦。因此,请明智地使用强制推送。你能够在 这里 了解有关 Git 强制推送的更多选项。

状况 2:我须要更正一个特定的提交

在以前的状况下,因为咱们只能修改最后一次提交,因此这种修复很是简单,可是想象一下,若是审稿人建议改变_navigation.html.haml。 在这种状况下,由于它是第二次从顶部提交,因此要想改变它不会像第一种状况那样直接。让咱们仍是来看看如何处理这个问题吧:

每当在分支中进行提交时,它就被惟一的SHA1哈希字符串标识。把它看做是一个惟一的ID,它将一个提交与另外一个提交分开。经过运行git log,您能够查看分支中的全部提交以及它们的SHA1哈希。经过它,你会看到一个看起来有点相似下面这样的输出,其中最近的提交在顶部;

commit aa0a35a867ed2094da60042062e8f3d6000e3952 (HEAD -> add-page-navigation)
Author: Kushal Pandya <kushal@gitlab.com>
Date: Wed May 2 15:24:02 2018 +0530

    Add styles for navigation

commit c22a3fa0c5cdc175f2b8232b9704079d27c619d0
Author: Kushal Pandya <kushal@gitlab.com>
Date: Wed May 2 08:42:52 2018 +0000

    Render navigation partial

commit 4155df1cdc7be01c98b0773497ff65c22ba1549f
Author: Kushal Pandya <kushal@gitlab.com>
Date: Wed May 2 08:42:51 2018 +0000

    Page Navigation View

commit 8d74af102941aa0b51e1a35b8ad731284e4b5a20
Author: Kushal Pandya <kushal@gitlab.com>
Date: Wed May 2 08:12:20 2018 +0000

    Add routes for navigation

复制代码

这就是 Git ReBase 命令发挥做用的地方。每当咱们但愿编辑 git ReBase 的特定提交时,咱们须要先将咱们的分支从新定位为在咱们但愿编辑的提交以前,将头移到右边。在咱们用例中,咱们须要更改读取的提交  Page Navigation View

这里请注意,正确的提交哈希是咱们想要修改提交的前一个提交哈希;拷贝那个哈希,而后执行下面的步骤:

  • 变基(rebase)分支,移动到目标提交的前一次提交;运行git rebase -i 8d74af102941aa0b51e1a35b8ad731284e4b5a20
    • 命令分解:这里咱们运行rebase命令的 交互 模式,并提供了要变基的提交哈希。
  • 这将在 Git 交互模式运行 rebase 命令,并打开你的文本编辑器,里面显示了正在变基的提交(8d74af)以后 的全部提交。它看起来就像这样:
pick 4155df1cdc7 Page Navigation View
pick c22a3fa0c5c Render navigation partial
pick aa0a35a867e Add styles for navigation

# 在 8d74af10294 上变基 8d74af10294..aa0a35a867e 范围(3次提交)
#
# 命令:
# p, pick = 保留本次提交
# r, reword = 保留本次提交,但要修改提交消息
# e, edit = 保留本次提交,但暂停下来进行修改(不仅修改提交消息)
# s, squash = 保留本次提交,但合并到前一次提交
# f, fixup = 与“squash”相似,但丢弃本次提交消息
# x, exec = 运行 shell 命令(本行的剩余内容)
# d, drop = 删除本次提交
#
# 这些行能够重排顺序,并自顶向底依次执行。
#
# 若是你删除一行,那次提交将会丢失。
#
# 若是你删除全部行,本次变基(rebase)将会停止。
#
# 注意空提交已注释掉。

复制代码

注意每一个提交前面都有一个单词pick,下面的注释是咱们可能用到的关键字。由于咱们想要 编辑 某此提交(4155df),因此须要把pick 4155df1cdc7 Page Navigation View改为edit 4155df1cdc7 Page Navigation View。保存修改,而后退出编辑器。

如今你的分支就重置到了所作的修改包含_navigation.html.haml的时刻。打开文件根据审核反馈执行须要的修改。一旦修改完毕,经过运行git add _navigation.html.haml命令进行暂存。

既然咱们暂存了这些改变,如今是时候把 HEAD 分支移回咱们的原始提交了(包含咱们添加的最新修改),运行git rebase --continue,这将会在终端打开你的默认编辑器而后显示在变基(rebase)期间的提交消息;Page Navigation View。若是但愿的话,你能够改变这个提交消息,可是咱们如今让它保持现状,保存并退出编辑器。此刻,Git 会从新播放你所编辑的提交以后的全部提交,而且如今的HEAD分支回溯到了咱们原始的顶部提交。

因为咱们再次修改了远程仓库中已存在的一次提交,因此须要使用git push --force-with-lease <remote_name> <branch_name>强制推送分支。

场景3:我须要添加、删除或者合并 commit

一个很常见的场景就是为了修复以前提交的内容,你已经 commit 了几回。如今咱们来尽量地减小 commit 的次数并把它们和原来的提交合并起来。

你须要作的就是就像你在其余场景同样启动交互式 rebase。

pick 4155df1cdc7 Page Navigation View
pick c22a3fa0c5c Render navigation partial
pick aa0a35a867e Add styles for navigation
pick 62e858a322 Fix a typo
pick 5c25eb48c8 Ops another fix
pick 7f0718efe9 Fix 2
pick f0ffc19ef7 Argh Another fix!

复制代码

假设你如今想要把这些提交记录都合并到 c22a3fa0c5c Render navigation partial。你只须要作到:

  1. 将 fixes 向上移动直到它们位于你但愿保留的最后的提交下面
  2. 把每个 fix 的 pick 改成 squash 或者 fixup

注意: squash 保留了描述中的提交注释。 fixup 不会保留提交的注释而只保留原始注释。

你会获得这样的结果:

pick 4155df1cdc7 Page Navigation View
pick c22a3fa0c5c Render navigation partial
fixup 62e858a322 Fix a typo
fixup 5c25eb48c8 Ops another fix
fixup 7f0718efe9 Fix 2
fixup f0ffc19ef7 Argh Another fix!
pick aa0a35a867e Add styles for navigation

复制代码

保存更改,退出编辑,你就完成了!这是产生的历史:

pick 4155df1cdc7 Page Navigation View
pick 96373c0bcf Render navigation partial
pick aa0a35a867e Add styles for navigation

复制代码

和之前同样,你要作的就是 git push --force-with-lease <remote_name> <branch_name>,改变就生效了。

若是你想要彻底地删除一个提交,把 squash 或者 fixup 换成 drop 或者干脆删掉那一行。

避免冲突

为了不冲突,请确保你移动到的目标没有编辑到同一个文件。

pick 4155df1cdc7 Page Navigation View
pick c22a3fa0c5c Render navigation partial
fixup 62e858a322 Fix a typo                 # this changes styles.css
fixup 5c25eb48c8 Ops another fix            # this changes image/logo.svg
fixup 7f0718efe9 Fix 2                      # this changes styles.css
fixup f0ffc19ef7 Argh Another fix!          # this changes styles.css
pick aa0a35a867e Add styles for navigation  # this changes index.html (no conflict)

复制代码

技巧: 快速 fixup

若是你清楚知道你将 fixup 哪个提交,你不须要浪费时间构思一个临时的名称,如: "Fix 1", "Fix 2", ..., "Fix 42"。你可使用如下的方法:

第一步:开始使用 --fixup

在你 stage 而且修复了你想要的内容时,使用如下命令:

git commit --fixup c22a3fa0c5c
复制代码

(注意这个 hash 信息对应 c22a3fa0c5c Render navigation partial)

以上命令会生成一个 commit:: fixup! Render navigation partial.

第二步:召唤你的好朋友 --autosquash

简单的交互性 rebase,如下命令会让 git 在正确的位置里设置 fixup:

git rebase -i 4155df1cdc7 --autosquash

如今你的历史是:

pick 4155df1cdc7 Page Navigation View
pick c22a3fa0c5c Render navigation partial
fixup 62e858a322 Fix a typo
fixup 5c25eb48c8 Ops another fix
fixup 7f0718efe9 Fix 2
fixup f0ffc19ef7 Argh Another fix!
pick aa0a35a867e Add styles for navigation

复制代码

你还可使用 git rebase --autosquash 命令来直接跳过 review 阶段,可是通常建议除非你感受特别安全,不然仍是少用为妙,由于你没机会 review 到具体变动。

场景 4: 个人提交信息太乱了,我须要从新开始

若是你在开发一个大功能,有时候你在分支里(如:add-page-navigation)留下不少 commit ,而你不喜欢让这些 commit 进入主分支,接下来我教你一个方法:

  • 在开始以前,请确保你的分支已是最新的而且不会与  master 分支冲突;
  • 当你 checkout 在 add-page-navigation 分支下时,可使用 git rebase master 或者 git merge master 命令来保持与 master 的更新;
  • 接下来使用命令建立补丁文件  git diff master add-page-navigation > ~/add_page_navigation.patch
    • 命令分解:咱们使用 Git 的 diff 功能,对 master 分支和 add-page-navigation 分支作了一个 diff 操做,而且将结果输出到  ~/add_page_navigation.patch 文件中。
  • 文件路径你能够随意设置;
  • 命令成功执行后没报错的话,补丁文件就算建立成功了;
  • 如今咱们运行 git checkout master 切换到  master 分支上;
  • 使用命令 git branch -D add-page-navigation 删除本地分支 add-page-navigation (咱们已经有补丁文件了,不要惧怕);
  • 接下来使用命令 git checkout -b add-page-navigation 建立一个新的分支;
  • 此时咱们在全新的 add-page-navigation 分支上,此分支没有任何更改;
  • 最后,使用命令 git apply ~/add_page_navigation.patch 来应用补丁里的修改;
  • 全部的修改都会应用上,而且会以 未提交 的状态显示着;
  • 此时你能够选择将全部更改一次提交,或者单个文件提交,随意操做便可。

就跟以前遇到的场景同样,咱们修改了整个分支,你须要作一个 force push 了。

总结

尽管咱们已经介绍了使用 Git 进行平常工做流程中出现的大多数常见的状况,但重写 Git 历史是一个巨大的话题,而且当你在熟悉上述提示时,你能够在 Git 官方文档 学习围绕该主题的更高级概念。祝你愉快的学习 Git。

相关文章
相关标签/搜索