Git 组织:更好的 Git 流程
想象一下:你被派去调查一起生产事故,经过一番调查后,你找到了包含破坏性代码的提交。你决定撤销这项更改:
$ git revert 1a2b3c
不幸的是,这样做会引入一个新的 bug!原来,在那个旧的“损坏”提交中隐藏着应用程序其他部分依赖的一些代码,当你还原这些代码行时,网站又一次陷入了崩溃状态。🙃 哎呀!
如何避免这种情况?要回答这个问题,我们首先需要研究这些类型的提交是如何产生的。
常见的 Git 流程
让我们看一下构建新功能时的常见 git 流程:
- 从 创建一个新分支
main
。 - 创建提交以保存您的工作并修复您在过程中发现的错误。
- 当功能完成后,发出拉取请求。
main
PR 获得批准后,合并分支。
您可能觉得这个流程很熟悉,这没关系。我们很多人都是这样学习使用 Git 的。然而,这种方法存在两个问题。第一个问题我们已经讨论过了,那就是,如果您只是简单地进行提交,某些提交可能包含不完整的工作。这使得恢复操作相当危险。
第二个问题是,它会让审查拉取请求变得非常繁琐。例如,如果你被要求审查一个最近的 PR,其中作者声明除了添加新功能外,他们还修复了一个不相关的错误。该 PR 包含数十个文件的更改。单独查看每个提交并不能确定哪些更改与错误修复有关,哪些更改与新功能有关。此外,你还会注意到一些更改似乎与 PR 描述中的任何内容都无关。显然,这不会是一个快速的审查。
现在,虽然每次提交都只与一个变更紧密相关固然很好,但在开发过程中,这却是一项艰巨的任务。修改和重写只是流程的一部分。我们的工作很少如此线性,我们的 git 提交也往往反映了这一点。
那么,我们如何才能保证我们的 git 历史记录整洁、易于查看,同时又能接受开发过程中一些无关紧要的性质呢?通过稍微修改这个基本的 git 流程,我们可以创建一个更好的流程来实现这一点。
改进的 Git Flow
以下方法受我的同事 Dan Wendorf 启发,他的 git 流程通常围绕一个核心原则:先做工作,后清理提交。这种流程的好处在于,它将工程 工作与提交的编写分离开来。最终,我们将得到一系列按逻辑分组的提交,每个提交都与代码中的一个主要更改相关,从而清理我们的 git 历史记录,并为更快的 PR 审核铺平道路。
我们可以将其分为以下三个步骤。
步骤 1:进行更改
第一步与之前并无太大区别。首先创建一个新分支,然后开始进行修改。暂时不必太在意编写描述性提交信息,因为这些信息不会包含在最终的 PR 中。现在,一个简单的“工作进行中”或“WIP”信息就可以了,或者一些能帮助你记住提交内容的信息,例如“WIP:开始构建新模型”。这些提交的目的是确保你不会丢失工作成果,并在工作过程中提供一些通用的指导。
$ git checkout -b my-feature-branch
...make changes...
$ git commit -m"WIP"
...make more changes...
$ git commit -m"WIP"
...make even more changes...
$ git commit -m"WIP"
在此步骤中,可以保留代码库的缺陷状态,或者提交一些尚未完成的功能。这些功能稍后都会被清理。
第 2 步:重置
完成更改后,就该准备执行“git clean up”了。为此,我们将运行以下命令:
$ git reset origin/main
如果不添加任何额外参数,git reset
则不会更改工作树,因此您的代码不会更改——您所做的所有工作仍会保留在那里。但由于您已重置为较旧的提交,因此git status
将显示自开始构建功能以来所做的所有更改。看起来您完成了所有工作,但之前从未进行过任何“WIP”提交。
$ git reset origin/main
Unstaged changes after reset:
M src/components/Footer/Footer.tsx
M src/components/Nav/Nav.css
M src/components/Nav/Nav.tsx
M src/components/Posts/Post.tsx
M src/components/Posts/PostList.tsx
$ git status
On branch feature-branch
Your branch is behind 'origin/feature-branch' by 3 commits, and can be fast-forwarded.
(use "git pull" to update your local branch)
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: src/components/Footer/Footer.tsx
modified: src/components/Nav/Nav.css
modified: src/components/Nav/Nav.tsx
modified: src/components/Posts/Post.tsx
modified: src/components/Posts/PostList.tsx
如果你在这里遇到问题,别担心——你随时可以恢复你最初的提交!你所做的每个提交都会保留在你的.git
文件夹中,即使过了一段时间reset
。即使它们看起来像是消失了,但它们仍然在那里,隐藏着。
如果您想回到某个未发生任何损坏的提交,git reflog
它会显示您在本地仓库中引用的每个提交的时间线,即使跨分支也是如此。运行git reflog
找到您想要返回的提交,然后运行git reset <commit-sha>
。此命令会将当前分支的 HEAD 指向该提交,然后您就可以重新开始工作了!
从这里开始,我们准备开始做出新的承诺。
步骤 3:创建新的、逻辑分组的提交
现在,查看所有已更改的文件。有哪些文件可以按逻辑分组?例如,所有与特定模型相关的依赖项更新或变更。文件分组没有“正确”的方法,因此请自行判断。将这些文件添加到暂存区,并提交描述这些更改的提交。
$ git add src/components/Nav/Nav.css
$ git add src/components/Nav/Nav.tsx
$ git commit -m"Added new styles to navigation"
$ git add src/components/Posts/Post.tsx
$ git add src/components/Posts/PostList.tsx
$ git commit -m"Updated author images on posts"
$ git add src/components/Footer/Footer.tsx
$ git commit -m"Fixed responsive bug in footer"
如果您没有更改很多文件,则可能不需要多次提交,但我们通常可以通过将更改拆分为人类可读、易于遵循的提交,使我们的拉取请求更容易审查。
如果同一个文件包含多个需要单独分组的更改,该怎么办?可以使用( 或)暂存文件的部分内容。一些代码编辑器还提供了暂存一系列更改(而非整个文件)的方法。git add --patch
git add -p
在此步骤中,请注意不要让代码库处于损坏状态。记住,我们清理提交的一个重要原因是,如果我们想要还原更改,就不会出现任何问题。在进行其中一个新提交后,您可以查看git stash
其余未暂存的更改,并测试一切是否仍然正常工作。如果您意识到应该在该提交中包含另一个文件,您可以git stash pop
恢复其他更改和git add
丢失的文件,然后执行git commit --amend
。此命令将用具有相同描述的新提交替换上一个提交,其中包含旧提交和您刚刚所做的更改。
最终结果
将工作拆分成逻辑分组的提交后,您就可以创建拉取请求了!最终结果是一组更改,您的同事可以以可管理的块为单位,一次审核一个提交。
这种 git 流的好处是,它允许典型开发的流动性,同时还提供一些维护存储库历史记录所急需的顺序。
文章来源:https://dev.to/render/git-organized-a-better-git-flow-56go