git commit命令: 信息修改、删除、取消、推送、选项
这篇文章将涵盖几乎所有与git commit
命令有关的内容。我们将介绍如何编辑、删除和中止提交信息,推送后中止,以及其他有用的选项。
1. 仔细看看git commit命令
在用git init命令创建了一个新的 repo,并用git add命令暂存了需要修改的内容之后,就该提交了。
git commit
命令负责将所有当前被暂存的变更登记为当前 repo 的新历史。
既然这是Git系统的一个核心部分,那么就值得更具体一点了。
让我们一步一步地详细了解一下吧
- 当你输入
git commit
命令时,Git 会检查在 repo 中是否有任何阶段性的修改。如果没有阶段性变化,它将打印以下信息。
$ git commit
On branch main
nothing to commit, working tree clean
-
在确认变更的存在后,Git 会创建一个新的提交对象,并为该提交分配一个 SHA-1 哈希值。正如我们在git add post中看到的,这个对象负责存储与单个提交相关的所有数据。
-
元数据值,如作者、提交者和提交消息,都被添加到提交对象中。
-
提交对象还存储了对一个树形对象的引用,该对象代表了提交时的文件系统。树对象指向Blob对象,Blob对象持有实际的文件内容。 这样一来,提交对象就有了一个已知的当前状态的快照。
-
当前工作分支中的最新提交被存储为新提交对象的父提交值。这样可以确保新创建的提交在当前分支上被注册。
-
最后,之前指向最新提交的
HEAD
被改为指向新添加的提交,使其成为当前分支的最后一个提交。
这样一来,所有对阶段性状态的改变都被记录为该 repo 历史的一部分。这是个简单的命令,但里面有很多东西要做。
接下来,我们来看看如何修改已经提交的提交信息。
2. 如何编辑一个提交信息
Git是一个非常重视提交的系统,一旦提交了,就会有很多人关注。 对于已经完成推送的提交,更是如此。 这是因为一次提交会破坏与许多团队成员的同步。
尽管如此,我们都是人,都会犯错。 如果你在提交信息中犯了一个错,你可以迅速修复它并通知你的团队。让我们来看看如何做到这一点。
首先,编辑当前分支中最后一次提交的命令。
$ git commit --amend -m "new commit message"
让我们看看实际的执行结果
提交信息 2.md created
已经改为 2.md created!!
。
但这里有一点你需要知道。
同一位置的两个提交的哈希值是不同的。
在修改之前,它是7d16880
,而在修改之后,它是ff55e3d
。
虽然它看起来像一个修复,但Git在内部删除了旧的提交,并注册了一个新的提交,其中有相同的修改和新的信息。
这有可能导致与使用旧提交开始新工作的团队成员产生冲突。 为了避免这种情况,如果你改变了提交信息,你应该按照新提交的方式通知团队。
2.1. 如果你已经提交了一个现有的提交,怎么办?
如果你用--amend
选项在本地编辑一个现有的提交,而该提交在编辑之前已经被推送了,那么本地 repo 和远程 repo 之间会有差异。
在这种情况下,你将无法在当时推送本地的版本。这样做的目的是为了防止远程 repo 提前搞乱你的提交。
在这种情况下,你会得到一个类似下面的错误信息。
$ git push origin main
! [rejected] your-branch -> your-branch (non-fast-forward)
error: failed to push some refs to 'your-remote-repository-url'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
有两种方法来解决这种情况。
2.2. 强制推送解决方案
第一种方法是覆盖远程 repo 的拒绝,强制当前本地 repo 推送。I 如果你正在运行一个单独的项目,或者你的团队中的每个人都知道当前的问题,这可能很容易。
它只需要一个命令
$ git push -f
或
$ git push --force
然而,在所有其他情况下,例如当一个新的提交被注册到一个远程 repo,或者当一个团队成员正在处理一个现有的提交时,它有可能弄乱提交历史,这可能是相当烦人的。
所以只有在真正安全的情况下才使用这种方法。
2.3. 拉取和合并的解决方法
第二种方法在某种程度上更安全。它涉及到从远程 repo 拉取现有的提交历史,然后将其与你的本地提交历史合并或重新发布。然后你可以推回,就像普通的合并一样。
这种方法是安全的,但它确实需要一些额外的工作。如果你还在协作,第二种方法才是最合适的。
关于pull、merge和rebase命令的更多信息,见相应的文章。
3. 如何撤销一个提交
下面介绍如何撤销一个提交,这将使现有的提交消失,但现有提交中登记的修改会回来,并被修改和解除缓存。
命令如下
$ git reset HEAD~1
让我们看看它的运行情况
首先我们用新文本替换了2.md
的内容,然后是add
,然后是commit
。
完成提交后,我用git status
命令检查了一下,没有更多的改动(nothing to commit
)。
然后我运行了git reset HEAD~1
来撤销提交,2.md
文件中的改动恢复到了未缓存状态。(unstaged changes after reset
)
你也可以在撤销提交后检查的状态中看到,2.md
文件处于未暂存状态。
最后,运行 git log{:shell}:
,你会看到我们上面记录的 1ac689b
提交已经消失。
这就是如何在不丢失所有修改的情况下撤销一个提交。
4.如何删除一个提交
下面是删除一个提交的方法,它可以删除现有的提交以及针对它注册的修改。如果不需要这些修改,可以用它来代替第3节中的取消方法。
命令如下
$ git reset --hard HEAD~1
让我们看看它的运行情况
如果你用git status
检查,目前没有任何阶段性变化。使用 git add
命令将其分阶段,然后完成提交。如果你检查日志,你会发现新哈希值c2072b8
的提交已经被注册了。
现在发出 git reset ---hard HEAD~1
来删除最后一次提交。没有修改回来,看历史记录,c2072b8
的提交被成功删除。
5. 撤销推送完成的提交
我们之前在第2.1节中看到过这个问题,但要用git reset
命令撤消或删除你推送到远程 repo 的提交并不容易。
这是因为本地 repo 和远程 repo 的提交历史是不同的,而远程 repo 会拒绝推送。
解决这个问题的一个方法是第2.2节中的强制推送方法,但我提到不推荐这样做。 取而代之的是,Git 提供了一个命令来明确撤销旧的提交并注册一个新的。
这些命令是
$ git revert HEAD --no-edit
这条命令创建了一个新的提交,恢复了上一次提交的所有修改。这可以由开发者手动完成,但开发者也是人,他们有可能会漏掉某一行。 因此,我们建议使用上述命令来代劳。
我们来看看实际的执行情况
这是当前新提交的状态。
让我们注册一个新的提交,明确地恢复我们刚刚注册的那个。
一个新的提交已经创建,并且自动写了一条消息,说我们已经恢复了一个现有的提交。如果你没有指定 --no-edit
选项、
它将打开一个纯文本编辑器,比如`vi',你可以在那里写出你想要的提交信息。
6. 使开发更容易的其他选项
到目前为止,git commit
命令的-m
和--amend
选项已经在示例代码中有所涉及。这里有一些其他选项可以考虑。
6.1. 不使用git add -u命令的-a选项
git commit -a
选项会自动分阶段提交所有修改,而不使用git add
命令。
注意,这不包括处于未跟踪状态的文件。像这样使用它
$ git commit -a -m "commit without add command"
6.2. 针对空提交的 --allow-empty 选项
有时你想留下一堆没有新改动的提交。这个选项允许你在没有改动的情况下留下提交。
像这样使用它
$ git commit --allow-empty -m "No changes"
6.3. 使用 --no-verify 选项来覆盖 pre-commit 和 commit-msg 钩子
Git 钩子是事件触发器,当某一事件发生时执行定义的脚本。当一个新的提交被注册时,"pre-commit钩 "和 "commit-msg钩 "被触发。
--no-verify
选项是一种忽略这些钩子并创建新提交的方法。使用方法如下
$ git commit --no-verify -m "This Commit ignore hooks"
7. 总结
在这篇文章中,我们几乎涵盖了关于git commit
命令的所有知识。
我相信,知道如何提交与知道如何修改和删除提交同样重要。
我将就本篇文章中涉及的git reset
和git revert
命令写出更深入的文章。
