保持 Git 历史记录的整洁——101
封面照片由JESHOOTS.COM提供,来自Unsplash
我使用 Git 已经好几年了,说实话,保持提交历史的整洁对我来说并非一直那么重要。正如我们在《回到未来》中看到的那样,改写历史可能会带来一些糟糕的后果。我只能猜测,这就是为什么它如此令人恐惧的原因。在这篇文章中,我想分享一些基本命令,它们将帮助你保持 Git 提交历史的整洁,并消除你对《回到未来》的恐惧 ;-)
首先,
本文中的所有命令都会生成新的提交哈希值,因此会使你的分支与远程分支产生分歧。这意味着你必须使用 `git push`git push --force或 ` git push` 强制推送来覆盖远程git push -f
分支的历史记录。这反过来意味着:永远不要更改共享分支上的 Git 历史记录。
有一个更安全的选项,即使强制推送也会在远程分支包含比本地分支更新的提交时被拒绝:git push --force-with-lease
场景 1:在上次提交中添加一些内容
每个人都遇到过这种情况。你把修改添加到暂存区,提交,然后等待构建成功。不幸的是,构建失败了。当然,你忘了添加文件 x。接下来会发生什么?把文件 x 添加到暂存区,提交并附上类似“我真笨,忘了添加文件 x”这样的信息……等等!让我来介绍一下我们的第一个命令:git commit --amend
这个命令会将你在暂存区的修改添加到最后一次提交中,并允许你修改最后一次提交的信息。你也可以用一个命令完成所有操作:git commit --amend -m "new message"
如果你不想修改最后一次提交的信息,只需添加`--no-edit`参数:git commit --amend --no-edit
这样就不会再出现“我真笨,忘了添加文件 x”之类的错误提交了!
Git 交互式变基
接下来的几个场景中,我们将使用交互式 Git 变基。这个工具可以帮助我们修改更早的历史记录。只需使用以下命令启动交互式变基:其中 ` --rebase - background git rebase -i <base>
...<base>git rebase -i HEAD~3git rebase -i 63a2356
pick bcfd87e add sql scripts for database
pick 9fb0b9c prevent users from changing their email
pick e0b46b9 cleanup web config
# Rebase dfef724..e0b46b9 onto dfef724 (3 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
#
# 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
让我们直接进入第一个变基场景。
场景 2:向历史记录中的任意提交添加内容
就像场景 1 中那样,你忘记向提交中添加一些内容。但你不想将其添加到最近的提交中,而是想将该更改修改到历史记录中更早的提交中。对于这种情况,交互式变基提供了编辑选项。
我们首先使用交互式变基命令(见上文),然后将编辑命令设置为要更改的提交:
pick bcfd87e add sql scripts for database
edit 9fb0b9c prevent users from changing their email
pick e0b46b9 cleanup web config
交互式变基现在会停止于此提交,您可以添加您的更改:
Stopped at 9fb0b9c... prevent users from changing their email
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
完成后,只需将更改添加到暂存区,然后使用 amend 命令提交即可:git commit --amend
快完成了!现在我们需要继续执行交互式变基:git rebase --continue
场景 3:合并提交
我尽量频繁地提交代码。这意味着我在特性分支中的 Git 历史记录通常看起来像这样:
9edf77a more review findings
67b5e01 review findings
940778d enable users to change their name
dc6b0db enable users to change their name
dfdd77d wip
这在功能开发过程中可能很有用,但对于整个 Git 仓库的历史记录来说并不适用。因此,我希望将所有提交合并为一个。有两种方法可以做到这一点:squash 和 fixup。这两个命令都会将一个提交合并到前一个提交中。唯一的区别在于,squash 允许你添加新的提交消息,而 fixup 会丢弃该消息。
那么,让我们来合并一些提交。我们像之前一样启动交互式变基,并设置相应的选项:
pick dfdd77d wip
squash dc6b0db enable users to change their name
squash 940778d enable users to change their name
squash 67b5e01 review findings
squash 9edf77a more review findings
# Rebase 63a2356..9edf77a onto 63a2356 (5 commands)
Git 现在提供包含所有消息的概览:
# This is a combination of 5 commits.
# This is the 1st commit message:
wip
# This is the commit message #2:
enable users to change their name
# This is the commit message #3:
enable users to change their name
# This is the commit message #4:
review findings
# This is the commit message #5:
more review findings
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
我们删除所有多余的行,只保留最终想要表达的信息:
enable users to change their name
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
瞧,我们已经将所有提交合并成一个了:
cb88cf6 enable users to change their name
我们也可以使用 fixup 命令执行相同的操作。在下面的场景中,我对提交信息很满意,我想将我的提交合并到该提交信息中:
pick dc6b0db enable users to change their name
fixup dfdd77d wip
fixup 940778d enable users to change their name
fixup 67b5e01 review findings
fixup 9edf77a more review findings
# Rebase 63a2356..9edf77a onto 63a2356 (5 commands)
结果与上面壁球的例子相同:
b8e76d1 enable users to change their name
场景 4:拆分提交
我们有时会将两个或多个不同的主题合并成一个主题:
7080968 add sql scripts for database
172db2b prevent user from changing their email and cleanup web config
在这种情况下,将 web.config 的清理工作单独进行会更清晰,不是吗?此时我们可以再次使用 edit 命令。让我们启动交互式 rebase 会话并执行此操作:
pick 7080968 add sql scripts for database
edit 172db2b prevent user from changing their email and cleanup web config
# Rebase dfef724..172db2b onto dfef724 (2 commands)
正如你所料,Git 会在我们的提交处停止。现在我们可以将更改恢复到工作区:git reset HEAD~
剩下的步骤很简单,只需为我们的更改创建两个提交:git add [files]git commit -m "prevent user from changing their email"git add [files]git commit -m "cleanup web config"
别忘了继续执行变基操作:git rebase --continue
让我们看看结果:
9fb0b9c prevent user from changing their email
dfef724 cleanup web config
7080968 add sql scripts for database
整洁的!
场景 5:重新排序提交
如果要合并两个或多个提交,但它们的顺序不对,该怎么办?
09f43c9 validate user inputs
62490ed import user data
c531f57 validate user inputs
您只需在变基编辑器中重新排列提交顺序即可。之后,您可以使用 fixup 或 squash 选项来合并它们。
pick 62490ed import user data
pick c531f57 validate user inputs
fixup 09f43c9 validate user inputs
# Rebase 6c70ff2..09f43c9 onto 6c70ff2 (3 commands)
场景 6:更改提交消息
对某个提交信息不满意?借助 reword 命令,您可以随时更改它:
reword c531f57 validate user inputs
pick 62490ed import user data
# Rebase 6c70ff2..09f43c9 onto 6c70ff2 (3 commands)
Git 将在此提交处停止,您可以选择更改此消息:
validate user inputs
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
场景 7:删除提交
需要完全撤销某个特定的提交吗?对于这种情况,我们可以使用 drop 命令:
pick bcfd87e add sql scripts for database
drop 9fb0b9c prevent users from changing their email
pick e0b46b9 cleanup web config
# Rebase dfef724..e0b46b9 onto dfef724 (3 commands)
至少在这里,你就能明白为什么保持 Git 历史记录的整洁如此重要了。想象一下,你把不同的功能放在一个提交里,或者把一个功能拆分成多个(毫无意义的)提交。那么,回滚操作就会变得复杂得多。
当事情出错时
重写 Git 历史记录时,不必担心会破坏任何东西。
首先,你可以随时使用 `git rebase` 命令中止交互式变基会话。git rebase --abort
该命令会停止变基会话并撤销所有已做的更改。
其次,即使你已经完成了变基操作,但不知何故搞砸了,你也可以撤销它。幸运的是,Git 会跟踪你执行的所有命令。你可以使用 `git log` 命令打开此日志。git reflog
e0b46b9 (HEAD -> feature/my2, feature/my3) HEAD@{34}: rebase -i (finish): returning to refs/heads/feature/my2
e0b46b9 (HEAD -> feature/my2, feature/my3) HEAD@{35}: commit: cleanup web config
9fb0b9c HEAD@{36}: commit: prevent users from changing their email
bcfd87e HEAD@{37}: reset: moving to HEAD~
3892bc7 HEAD@{38}: rebase -i: fast-forward
bcfd87e HEAD@{39}: rebase -i (start): checkout master
3892bc7 HEAD@{40}: rebase -i (finish): returning to refs/heads/feature/my2
3892bc7 HEAD@{41}: commit (amend): prevent user from changing their email and cleanup web config
39262b5 HEAD@{42}: rebase -i: fast-forward
bcfd87e HEAD@{43}: rebase -i (start): checkout master
39262b5 HEAD@{44}: rebase -i (abort): updating HEAD
39262b5 HEAD@{45}: rebase -i (abort): updating HEAD
bcfd87e HEAD@{46}: reset: moving to HEAD~
39262b5 HEAD@{47}: rebase -i: fast-forward
bcfd87e HEAD@{48}: rebase -i (start): checkout master
39262b5 HEAD@{49}: rebase -i (finish): returning to refs/heads/feature/my2
39262b5 HEAD@{50}: rebase -i (pick): prevent user from changing their email and cleanup web config
bcfd87e HEAD@{51}: commit (amend): add sql scripts for database
7080968 HEAD@{52}: rebase -i: fast-forward
dfef724 (master) HEAD@{53}: rebase -i (start): checkout master
172db2b (feature/my) HEAD@{54}: checkout: moving from feature/my to feature/my2
172db2b (feature/my) HEAD@{55}: commit: prevent user from changing their email and cleanup web config
7080968 HEAD@{56}: commit: add sql scripts for database
dfef724 (master) HEAD@{57}: checkout: moving from master to feature/my
dfef724 (master) HEAD@{58}: commit (initial): init
现在您可以搜索在开始交互式变基会话之前的最后一个操作,并恢复到该状态。例如:git reset --hard HEAD@{18}
所以,让我们克服恐惧,保持 Git 历史记录的整洁吧!:-)
文章来源:https://dev.to/manuelsidler/keep-your-git-history-clean-101-k7