Git是一个功能强大的版本控制工具,在这一领域具有一定的垄断地位。对于大多数日常工作而言,Git的命令重复性使其简单易用。然而,在很多情况下,你需要的不仅仅是基本的操作。因此,有很多高级 Git 命令可以让你的 Git 技能更上一层楼。
这也会让你了解到很多平时接触不到的 Git 概念。例如,相对于提交改动的 “存储”(stashing),”重定向”(rebasing),以及将文件添加到暂存区(staging area)。一旦你学会了这些,你可能会发现自己对团队的价值会更大。
在这篇博文中,介绍了你想知道的不同的强大Git命令。然而,要使用这些命令,你需要掌握一些技能、概念和信息。让我们先来了解一下。
虽然您可能已经掌握了Git的基础知识,并能理解一些中级命令,比如 git delete
,但您还需要更多的工具箱来处理高级命令。
我们在此分享的技巧并不适合Git初学者,所以如果您对版本控制还很陌生,我们建议您先花些时间日常使用基本命令,然后再考虑添加更多命令。
简而言之,以下是您必须了解的几个方面:
git add
、 git merge
、 git commit
和 git push
。有在团队环境中使用 Git 的经验会更有帮助,因为很多命令都会对共享代码库产生重大影响。例如,您可能只在负责的岗位上使用其中的某些命令。这意味着你需要小心谨慎,并了解何时使用这些命令。
本文接下来将介绍12个不同的高级Git命令,它们将带你超越基础,成为Git向导。让我们从您可能使用较多的命令开始。
首先,重定向(rebasing)是一个强大的 Git 命令,它可以替代拉取请求。它可以将分支分歧后的所有新提交拉入一个新分支,并将这些改动放在基本分支的顶端。如果出于某种原因,您想在不创建合并提交的情况下纳入另一个分支的变更,这将非常有用。
重定向的一个常见用例是,当您在特性分支上工作时,希望在不创建合并提交的情况下纳入来自主分支的变更。这有助于保持项目历史的整洁,不过运行时间会更长,而且合并时可能会出现更多错误。
使用 git rebase
命令来重置分支。下面是一个例子,我们将一个分支重定向到另一个分支上:
git checkout foo-branch
git rebase main
您还可以列出您将要发布的提交,并为您提供事先编辑的机会。因此,你可以压制提交、编辑提交信息等等。你可以使用 --interactive
或 --i
标志来执行 “interactive rebase”。
git rebase --interactive
说到这一点,将提交合并为一个提交来整理提交历史是rebasing的一个常见用例。这可以让你的提交历史更容易阅读和理解。
在rebase过程中,您可以在标记后加上您希望压扁的提交次数,这样就可以将提交合并为一个提交:
git rebase -i HEAD~3
这将打开一个交互式的rebase窗口,就像提交窗口一样,在这里您可以选择和编辑您想要删除的提交:
在终端运行 git rebase。
屏幕上方是提交列表,下方是命令选项,以及一些其他相关信息。默认选项是 pick
一个提交,将其作为您要使用的提交,不做任何改动。
不过,还有很多其他命令可以帮助你浏览rebase。例如,你可以重写一个提交,或者将多个提交合并在一起。要进行修改和使用这些命令,你需要使用终端的编辑器。默认的编辑器通常是Vim,这意味着你需要熟悉该编辑器。
最后一步是保存修改,然后推送到远程分支。
Git在撤销修改方面是出了名的。一般来说,这个过程很困难,而且容易出错。不过,如果你想撤销对工作目录或暂存区域的改动,有一些 Git 命令可以帮到你。
事实上,Git会在运行 git status
时告诉您如何解除文件缓存:
Git 会在状态输出中告诉您如何解除文件缓存。
因此,您可以轻松地从当前分支移除提交,并将其发送到其他分支:
git reset HEAD
这样做的效果是将分支向后移动一个提交,就像移除最后一个已知提交一样。您甚至可能需要将分支重置为原始状态。在这种情况下,可以使用 git reset --hard origin/main
来重置远程起源。不过请注意,这些改动将永远消失。
虽然 git checkout
命令是您会经常用到的基本命令,但您也可以用它在提交前解除文件的缓存:
git checkout --
注意破折号和文件名占位符之间的空格。在这里,命令将取消您指定的文件并丢弃工作目录中的更改。请注意,这将删除您对文件所做的任何本地更改。因此,请反复确认您不需要这些未保存的改动。
您也可以使用 git revert
来还原提交的改动。不过,这并不是回滚修改,而是在撤销前一个提交的基础上创建一个新的提交。
这里的主要区别是,命令不会移动任何引用指针到新提交,但会保留旧提交。当你想在不从提交历史中删除改动的情况下撤销改动时,这很有用。
该命令期望收到一个引用,并且可以直接还原最新的提交:
git revert HEAD
不过,您可以在更大的范围内恢复、修改文件和提交。本 Git 高级命令列表的下两个条目将介绍它们。
在 Git 中,你可以使用一条新命令: git restore
,轻松地将文件恢复到默认状态。事实上,在大多数情况下,您应该把它当作 git reset
的替代品,因为它提供了更强大的功能。例如,使用 git restore --staged
和使用 git reset HEAD
可以获得相同的结果。
该命令还能做更多–您还能将文件恢复到默认状态。如果您也运行 git status
,就能看到如何做到这一点:
git restore
这将从工作目录中移除修改,就像什么都没发生过一样。和 git checkout --
一样,您需要确保您不需要任何本地变更,因为它们将永远消失。
很多时候,当您推送了一个提交,却发现忘了包含一些重要的内容。有了Git,你可以很容易地修改提交,把遗漏的改动包括进去。
这需要一个特定的过程:
git add
将它们作为典型的暂存文件。git commit --amend
这将使用您的暂存区域用新提交修改原始提交。因此,请确保您不需要旧版本的提交,因为它将丢失。我们也建议您在本地提交时使用 --amend
标志,而不是远程提交,原因与我们在本篇文章中提到的类似。
您也可以使用 git commit --amend
只编辑提交信息,命令如下:
git commit --amend -m "New commit message"
使用 Git 的日志可以帮助您了解仓库的历史。不过,我们不会把 git log
命令列为高级命令。相反,您可以使用各种选项来过滤输出,以满足您的需要:
git log
git log --decorate
git log --stat
例如,装饰日志条目会打印出所有显示提交的 ref 名称。 --stat
选项显示一个提交的插入和删除:
在终端运行 git log -stat 命令。
您还可以使用其他选项来定制日志的输出–称为 “提交限制”。例如,使用以下命令
git log --author=
git log --grep=
在这里,您可以通过特定的作者姓名或文本模式过滤日志。事实上,您可以结合多个选项和标志来生成特定用途的日志。例如
git log --oneline --author= --since= feature-temp
搜索自指定日期以来,某个作者在 feature-temp 分支中的所有提交,然后用单行条目打印出来。注意
参数也可以是字符串:
--since=”Two weeks ago”
此外,如果您想搜索特定文件而不是分支,可以运行以下命令:
git log --oneline --author=bartonfink --since=”5 days ago” -- readme.rm
这组示例只是略微介绍了您可以用日志做的事情,但根据您的搜索条件,您可以在日志中找到精确的提交。
您有时可能会使用宏或其他自动化脚本来帮助运行代码。Git也以钩子的形式提供了此类功能。这些脚本会在某些事件(如提交或推送)发生时自动运行。还有很多方法可以使用钩子来强制代码格式化、运行测试等等。
钩子有两种类型:客户端和服务器端:
Git 会在您运行 git init
时为您的 repo 添加几个钩子示例。不过,您需要移除 .sample 扩展名才能使用它们:
MacOS 中的一个文件夹,显示 Git 在启动时安装的钩子示例。
需要注意的是,一次只能运行一种钩子类型,不过也可以同时使用多个脚本。因此,文件名应根据示例脚本对应钩子类型:预提交(pre-commit)、更新(update)等。
创建 Git 钩子
要创建一个 Git 挂钩,需要在 .git/hooks 子目录下创建一个可执行脚本(不带扩展名)。只要把它添加到hooks文件夹,它就能运行。
您可以使用任何脚本语言,只要它能作为可执行文件运行即可。我们建议您使用Ruby或Python,但您也可以使用Bash、Perl等其他语言。你所需要做的就是改变你的解释器的路径,而不是默认的Bash:
#!/usr/bin/env python
在这里,你可以像正常一样编写代码。例如,这里是一个 Python 的 prepare-commit
脚本,它提示用户写好提交信息:
#!/usr/bin/env python
import sys, os
path_commit_msg = sys.argv[1]
with open(commit_msg_filepath, 'w') as f:
f.write("# You’ll need to provide a better commit message than that, buddy!")
我们建议您在命令行中运行 chmod +x .git/hooks/
以确保可以执行。
总的来说,钩子是自动化重复任务和在团队中执行最佳实践的有力工具。
在 Git 中,您可以通过 SHA-1 加密哈希值来识别提交。虽然也可以通过完整的哈希值来引用提交,但这样做既繁琐又容易出错:
bc7623b7a94ed3d8feaffaf7580df3eca4f5f5ca
Git 提供了几种更简短、更易记的提交名。例如,可以使用分支或标签名。例如,我们可以使用一个名为 “develop” 的分支。下面是一个例子,我们引用这个分支上的最新提交:
git diff develop..HEAD
这显示了 “develop” 分支上的最新提交( HEAD
)与当前提交之间的差异。
您也可以通过提交历史中的相对位置来引用提交。例如,你可以用 HEAD~2
来表示当前提交之前的两次提交:
git diff HEAD~2..HEAD
Git还提供了其他几种引用提交的方式,比如用”@”符号引用当前分支,或者用”^”符号引用提交的父分支。通过使用这些速记符号,您可以在处理提交时节省时间并避免错误。
在通常情况下,您会认为没有办法在不提交的情况下存储您对文件所做的更改。暂存就是临时存储的方法。当你需要切换分支或处理不同的任务,但又不想提交修改时,这种方法非常有用。
例如,如果你需要切换分支来处理流程中的某些事情,你可以把修改保存在当前分支,然后签出另一个分支。从那里,您可以在另一个分支上工作,然后提交并推送这些变更。然后,您就可以在原始分支上签出并检索您的工作。
存储变更有两种方法:
git stash
这将把您的改动存储到一个新的储藏库中,并把您的工作目录还原到上一次 HEAD 提交的状态(即您做新改动之前的状态)。您可以用 git stash list
列出改动,并用 git stash show
查看。后一个命令也可以接受任何 git diff
接受的格式。
在这里,您可以切换分支或处理其他任务。当您想取回您的改动时,运行下面的命令:
git stash apply
这将把上次隐藏的更改应用到您的工作目录中。不过要注意的是,如果您对文件改动过大,仍然可能会遇到冲突。毕竟, git stash
只是暂时解决问题的办法。
您也可以有多个储藏库,您可以使用下面的命令指定应用哪个储藏库:
git stash apply stash@{n}
占位符 {n}
是一个整数, stash@{0}
代表最新的 stash。Git 官方文档中还有一些其他的 git stash
例子。
我们敢打赌,每个人都会遇到 bug 或问题,却不知道从哪里开始查找。在这种情况下,”bisecting” 可以帮助你快速找出引入问题的提交。
简而言之,该命令通过搜索您的提交来找出bug。一旦找到问题提交,它就会返回给你。这个命令的强大之处在于它的子命令。
要使用bisecting,首先需要运行 git bisect start
命令。Git 会将您带到项目历史中的第一次提交。
从这里开始,您需要使用相关命令指出该提交是好是坏:
git bisect good
git bisect bad
然后,Git 会将您转到下一个提交,以测试其 “质量”。注意,您也可以用 “旧的” 替换 “好的”,用 “新的 “替换 “坏的”,以符合您的特定用例(但不能混用术语)。
从这里开始,您可以继续将每个提交标记为 “好” 或 “坏”,直到找到引入错误的提交。不过,您不必遍查每个提交–您可以指定确切的标识符来帮助缩小搜索范围并缩短搜索时间:
git bisect bad feature-test
这里使用的是分支名称,但也可以是使用整数、哈希引用等的具体修订版本。无论如何,一旦您找到错误,您可以运行以下命令之一来返回最新代码:
git bisect reset
与本列表中的所有 Git 高级命令一样,还有很多内容需要消化,Git 文档将是必不可少的读物。
我们在 “提交引用” 条目中提到了使用 git diff
。现在,是时候详细了解一下了。您经常会发现自己的多个分支包含了不同的改动。Git 允许您使用 git diff
命令比较两个分支之间的差异。事实上,您可以通过多种方式使用它,通常与其他命令一起使用,来调查和分析一个 repo。
基本的 git diff
命令会输出变化概览。它看起来很像提交合并调查的输出:
显示 git diff 请求的输出。
不过,您还可以深入到精确的分支、哈希值等等。例如,要比较两个分支,您可以运行 git diff branch1..branch2
,然后替换占位符:
git diff feature-branch pre-prod
您还可以比较当前分支与其他分支之间的差异:
git diff HEAD..pre-prod
请注意,这里使用两个点将返回两个分支顶端之间的差值。相比之下,三个点将返回两个分支的共同祖先之间的差异,并以此进行测试。
和 git log
一样,您也可以清理输出并细化其返回的内容。例如,git diff --name-only branch1..branch2
将只检查哪些文件不同,而省略上下文:
在终端运行 git diff -name-only 命令。
您可能会发现输出很难解析,尤其是当 “diff” 很长的时候。在这种情况下,您可以使用 --color-words
选项:
运行 git diff -color-words 命令并在终端中查看输出。
总的来说, git diff
和其他命令一样强大,尤其是当您调用某些选项并细化返回的差异时。
有时,您可能想在不合并两个分支的情况下,将某个特定的提交从一个分支应用到另一个分支。Git 提供了 git cherry-pick
功能。您应该小心使用,但您会发现 git cherry-pick
在一些情况下可以帮到您。
一种情况是,您有一些过时的特性分支没有合并到 main
或 trunk
中。您可以使用命令组合(比如 git log
)把相关的旧提交挖掘出来,重新应用到其他地方。
使用 git log 查找某个提交的引用。从那里,确保您在您想cherry pick的分支上。例如,假设您想把提交 xxxxxaaaa
挑选到分支 ” trunk
“。首先,签出您的分支…
git checkout trunk
……然后选择您的提交:
git cherry-pick xxxxxaaaaaa
在很多情况下,您的提交信息可能会过时。为了解决这个问题,你可以在命令中加入 --edit
选项。这将让你在剔除之前提供一个新的提交信息。
最后一个高级Git命令是 git add
。不,这不是个错别字–这个最基本的 Git 命令有着惊人的力量。
举例来说,如果您发现自己在缓存区添加单个文件,您可以使用下面的命令:
git add -p
通过 -p
选项,您可以在交互的基础上对修改进行分期。 你可以查看你对每个文件所做的修改,然后选择哪些需要暂存。这可以节省你大量的时间,帮助你避免不需要的修改。
虽然您可能知道可以对单个文件进行暂存,但您也可以指定一个目录。例如,暂存 ” new-feature
“目录下的所有文件:
git add new-feature
您甚至可能想在不执行完整过程的情况下查看 git add
的结果。您可以选择
git add --dry-run
git add -n
运行后,Git会显示是添加还是忽略这些文件。说到忽略的文件,你也可以把它们添加到暂存区:
git add --force
git add -f
向暂存区域添加文件可能比简单的二进制 “添加或不添加” 更复杂。因此,Git最核心的命令之一可以涵盖无数种可能的情况。
一旦掌握了基本的 Git 命令,您就拥有了执行项目常规版本控制任务所需的 80%的知识。然而,最后的20%才是高级Git命令大显身手的地方。
重新排序、分段、恢复文件等命令和技术都能让您快速摆脱困境。更重要的是,你可以为团队和项目提供更大的价值,帮助简化工作流程,提高工作效率,全面提升开发者的能力。
这些高级Git命令会成为你日常工作的一部分吗?请在下面的评论区告诉我们!