如何让你的 Git 历史保持“干净”?!

提示您,本文原题为 -- 如何让你的 Git 历史保持“干净”?!

消除 Git 历史的最佳手段有哪些?


如何让你的 Git 历史保持“干净”?!

如何让你的 Git 历史保持“干净”?!// //

作者 | Manuel Sidler

译者 | 谭开朗 , 责编 | 屠敏

使用Git已有几年的时间了 , 我必须承认 , 干净的提交历史并不总是那么重要 。 我们从《回到未来》中知道 , 改写历史可能会造成某些糟糕的后果 。 我只能假设 , 这就是为什么这是一个可怕的话题 。 在这篇文章中 , 我想分享一些基本的命令 , 这些命令将帮助你保持Git历史的整洁 , 并消除《回到未来》中的糟糕后果 。

要事第一

本文中的所有命令都将生成新的提交哈希码 , 从而将其与原始分支区别开来 。 这意味着 , 你必须使用git push -force或git push -f强制覆盖远程上的历史记录 。 这又意味着:永远不要更改共享分支上的git历史记录 。 如果远程分支有新的提交 , 则有一个更安全的命令 , 它甚至可以拒绝强制推送:git push –force-with-lease 。

场景1:在最后一次提交中添加内容

每个人都有过这样的经历:将改动添加到暂存区 , 提交并等待构建 。 但不幸的是 , 构建失败了 。 哦 , 你忘了添加文件x 。 接下来会发生什么?将文件x添加到暂存区 , 并提交一条类似于“愚蠢的我忘了添加文件x”的内容 。 请不要这么做!让我来介绍第一个命令:

gitcommit -amend

该命令将暂存区的改动添加到最近一次提交 , 并支持更改最近一次的提交内容 。 使用以下简单的命令也能达到相同的效果:

git commit–amend -m“ newmessage”

如果不想修改最后的提交信息 , 可以添加no-edit参数:

gitcommit --amend -- no-edit

所以不要再犯“愚蠢的我忘了…”的错误了!

Git的交互式rebase

在接下来的几个场景中 , 我们将使用交互式git rebase 。 这个工具能帮助修改历史上更早的更改 。 只需使用以下命令启动一个交互式rebase:

git rebase –i < base>

其中<base>表示要重写历史的节点 。

例如 , 重写最后三次提交:

gitrebase -i HEAD~ 3

另一个例子 , 重写历史到一个特定的提交:

gitrebase -i 63a2356

该命令显示了配置编辑器的提交和选项:

pick bcfd87e add sql s fordatabase

pick 9fb0b9c prevent users fromchanging 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

让我们进入第一个rebase场景 。

场景2:向历史记录中的任意提交添加内容

就像在场景1中 , 我们忘记在提交中添加内容 。 但是 , 与其添加到最后一次提交中 , 不如将改动修改为历史上更远的一次提交 。 对于这种情况 , 交互式rebase为我们提供了编辑选项 。

我们从交互式rebase命令开始 , 然后在我们想要更改的提交上设置编辑命令:

pick bcfd87e addsql s fordatabase

edit 9fb0b9c prevent users fromchanging their email

pick e0b46b9 cleanup web config

交互式rebase停止此提交 , 我们可以添加更改:

Stopped at 9fb0b9c... prevent users from changing their email

You can amend the commitnow, with

git commit--amend

Once you aresatisfied withyour changes, run

git rebase --continue

完成后 , 只需将更改添加到暂存区 , 并使用amend命令提交:

git commit--amend

几乎完成了!现在我们要continue rebase:

git rebase – continue

场景3:合并提交

尽量多做合并 。 这意味着功能分支中的git历史记录通常是这样的:

9edf77a more review findings

67b5e01 review findings

940778d enable users to changetheir name

dc6b0db enableuserstochangetheir name

dfdd77d wip

这在特性开发期间可能很有用 , 但并不适用于整个git存储库历史 。 因此将所有提交合并为一个 。 有两种方式:squash和fixup 。 这两个命令都是将提交合并到前一个命令中 。 唯一的区别是 , squash支持编辑新的提交内容 , 而fixup不支持 。

我们来squash一些提交 。 我们像之前一样启动交互式rebase , 并设置适当的选项:

pick dfdd77d wip

squash dc6b0db enable users to changetheir name

squash 940778d enableuserstochangetheir 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 changetheir name

# This is the commit message #3:

enableuserstochangetheir 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 changetheir 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 changetheir name

我们可以使用fixup命令执行同样的操作 。 在下面的场景中 , 提交消息很完美 , 我想把提交合并到:

pick dc6b0db enable users to changetheir name

fixup dfdd77d wip

fixup 940778d enableuserstochangetheir name

fixup 67b5e01 review findings

fixup 9edf77a more review findings

# Rebase 63a2356..9edf77a onto 63a2356 (5 commands)

结果和上面squash的例子一样:

b8e76d1 enable users to changetheir name

场景4:分割提交

我们常常把两个或两个以上不同的主题合并在一起:

7080968add sql s fordatabase

172db2b prevent user fromchanging their email andcleanup web config

在这种情况下 , 将web配置的清理分离出来会更干净 , 对吧?此时 , 我们可以再次利用edit命令 。 使用互动rebase可做到这一点:

pick 7080968add sql s fordatabase

edit 172db2b prevent user fromchanging their email andcleanup web config

# Rebase dfef724..172db2b onto dfef724 (2 commands)

正如我们所期望的 , Git在提交时停止了 。 现在我们可以把更改带回工作区域:

git resetHEAD~

接下来很简单 , 只需为更改创建两个提交:

git add [files]

git commit-m "prevent user from changing their email"

git add[files]

git commit-m "cleanup web config"

别忘了继续rebase:

git rebase -- continue

结果如下:

9fb0b9c prevent user fromchanging their email

dfef724 cleanup web config

7080968addsql s fordatabase

多整洁!

场景5:重新排序提交

如果合并两个或多个提交 , 但它们的顺序不对怎么办?

09f43c9 validate user inputs

62490ed importuser data

c531f57 validate user inputs

只需在rebase编辑器中重新排序提交 , 然后再使用fixup或squash合并它们 。

pick 62490ed importuser data

pick c531f57 validate user inputs

fixup 09f43c9 validate user inputs

# Rebase 6c70ff2. .09f43c9 onto 6c70ff2 ( 3commands)

场景6:更改提交消息

不满意特定的提交内容?可以使用reword命令随时更改它:

reword c531f57 validate user inputs

pick 62490ed importuser data

# Rebase 6c70ff2..09f43c9 onto 6c70ff2 (3 commands)

Git将在此时停止提交 , 我们可以选择更改消息:

validateuser 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 s for database

drop9fb0b9c prevent usersfromchanging their email

pick e0b46b9 cleanupweb config

# Rebase dfef724..e0b46b9 onto dfef724 (3 commands)

在这里 , 我们知道了保持Git历史记录整洁的重要性 。 假设我们将不同的特性放入一个提交中 , 或者将一个特性拆分为多个(无意义的)提交 , 那么还原要复杂得多 。

当事情出现问题时

在重写Git历史时 , 不要担心会破坏某些东西 。

首先 , 我们总是可以使用git rebase--abort中止交互式rebase会话 。

这个命令将停止rebase会话并恢复所有更改 。

其次 , 即使已经完成了一个rebase并把它搞砸了 , 我们也可以还原它 。 幸运的是 , Git跟踪执行所有命令 , 我们可以使用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 andcleanup 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 andcleanup web config

bcfd87e HEAD@{51}: commit (amend): add sql s fordatabase

7080968HEAD@{52}: rebase -i: fast-forward

dfef724 (master) HEAD@{53}: rebase -i (start): checkout master

172db2b (feature/ my) HEAD@{54}: checkout: moving from feature/ myto feature/my2

172db2b (feature/ my) HEAD@{55}: commit: prevent user from changing their email andcleanup web config

7080968HEAD@{56}: commit: add sql s fordatabase

dfef724 (master) HEAD@{57}: checkout: moving from master to feature/ my

dfef724 (master) HEAD@{58}: commit (initial): init

我们可以找到交互式rebase会话之前的最后一个操作 , 并将其恢复状态 。 例如:

gitresthardHEAD@{18}

所以 , 请战胜恐惧 , 并保持Git历史的干净吧!

原文:https://dev.to/manuelsidler/keep-your-git-history-clean-101-k7