进阶Git:学会使用更强大命令

进阶Git:学会使用更强大命令

Git是一个功能强大的版本控制工具,在这一领域具有一定的垄断地位。对于大多数日常工作而言,Git的命令重复性使其简单易用。然而,在很多情况下,你需要的不仅仅是基本的操作。因此,有很多高级 Git 命令可以让你的 Git 技能更上一层楼。

这也会让你了解到很多平时接触不到的 Git 概念。例如,相对于提交改动的 “存储”(stashing),”重定向”(rebasing),以及将文件添加到暂存区(staging area)。一旦你学会了这些,你可能会发现自己对团队的价值会更大。

在这篇博文中,介绍了你想知道的不同的强大Git命令。然而,要使用这些命令,你需要掌握一些技能、概念和信息。让我们先来了解一下。

  1. 推荐先决条件
  2. 进阶Git:你想知道的12个强大命令

虽然您可能已经掌握了Git的基础知识,并能理解一些中级命令,比如 git delete,但您还需要更多的工具箱来处理高级命令。

我们在此分享的技巧并不适合Git初学者,所以如果您对版本控制还很陌生,我们建议您先花些时间日常使用基本命令,然后再考虑添加更多命令。

简而言之,以下是您必须了解的几个方面:

  • Git的核心概念,如提交、分支、拉取请求等。
  • 熟悉 Git 的基本命令,如 git addgit mergegit commitgit push
  • 对于高级 Git 任务,您需要能够浏览终端。

有在团队环境中使用 Git 的经验会更有帮助,因为很多命令都会对共享代码库产生重大影响。例如,您可能只在负责的岗位上使用其中的某些命令。这意味着你需要小心谨慎,并了解何时使用这些命令。

进阶Git:你想知道的12个强大命令

本文接下来将介绍12个不同的高级Git命令,它们将带你超越基础,成为Git向导。让我们从您可能使用较多的命令开始。

1. 重定向

首先,重定向(rebasing)是一个强大的 Git 命令,它可以替代拉取请求。它可以将分支分歧后的所有新提交拉入一个新分支,并将这些改动放在基本分支的顶端。如果出于某种原因,您想在不创建合并提交的情况下纳入另一个分支的变更,这将非常有用。

重定向的一个常见用例是,当您在特性分支上工作时,希望在不创建合并提交的情况下纳入来自主分支的变更。这有助于保持项目历史的整洁,不过运行时间会更长,而且合并时可能会出现更多错误。

使用 git rebase 命令来重置分支。下面是一个例子,我们将一个分支重定向到另一个分支上:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git checkout foo-branch
git rebase main
git checkout foo-branch git rebase main
git checkout foo-branch
git rebase main

您还可以列出您将要发布的提交,并为您提供事先编辑的机会。因此,你可以压制提交、编辑提交信息等等。你可以使用 --interactive 或 --i 标志来执行 “interactive rebase”。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git rebase --interactive <other-branch-name>
git rebase --interactive <other-branch-name>
git rebase --interactive <other-branch-name>

说到这一点,将提交合并为一个提交来整理提交历史是rebasing的一个常见用例。这可以让你的提交历史更容易阅读和理解。

在rebase过程中,您可以在标记后加上您希望压扁的提交次数,这样就可以将提交合并为一个提交:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git rebase -i HEAD~3
git rebase -i HEAD~3
git rebase -i HEAD~3

这将打开一个交互式的rebase窗口,就像提交窗口一样,在这里您可以选择和编辑您想要删除的提交:

在终端运行 git rebase

在终端运行 git rebase。

屏幕上方是提交列表,下方是命令选项,以及一些其他相关信息。默认选项是 pick 一个提交,将其作为您要使用的提交,不做任何改动。

不过,还有很多其他命令可以帮助你浏览rebase。例如,你可以重写一个提交,或者将多个提交合并在一起。要进行修改和使用这些命令,你需要使用终端的编辑器。默认的编辑器通常是Vim,这意味着你需要熟悉该编辑器

最后一步是保存修改,然后推送到远程分支。

2. 回退、重置和解除缓存

Git在撤销修改方面是出了名的。一般来说,这个过程很困难,而且容易出错。不过,如果你想撤销对工作目录或暂存区域的改动,有一些 Git 命令可以帮到你。

事实上,Git会在运行 git status 时告诉您如何解除文件缓存:

Git 会在状态输出中告诉您如何解除文件缓存

Git 会在状态输出中告诉您如何解除文件缓存。

因此,您可以轻松地从当前分支移除提交,并将其发送到其他分支:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git reset HEAD <commit>
git reset HEAD <commit>
git reset HEAD <commit>

这样做的效果是将分支向后移动一个提交,就像移除最后一个已知提交一样。您甚至可能需要将分支重置为原始状态。在这种情况下,可以使用 git reset --hard origin/main 来重置远程起源。不过请注意,这些改动将永远消失。

虽然 git checkout 命令是您会经常用到的基本命令,但您也可以用它在提交前解除文件的缓存:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git checkout -- <filename>
git checkout -- <filename>
git checkout -- <filename>

注意破折号和文件名占位符之间的空格。在这里,命令将取消您指定的文件并丢弃工作目录中的更改。请注意,这将删除您对文件所做的任何本地更改。因此,请反复确认您不需要这些未保存的改动。

您也可以使用 git revert 来还原提交的改动。不过,这并不是回滚修改,而是在撤销前一个提交的基础上创建一个新的提交。

这里的主要区别是,命令不会移动任何引用指针到新提交,但会保留旧提交。当你想在不从提交历史中删除改动的情况下撤销改动时,这很有用。

该命令期望收到一个引用,并且可以直接还原最新的提交:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git revert HEAD
git revert HEAD
git revert HEAD

不过,您可以在更大的范围内恢复、修改文件和提交。本 Git 高级命令列表的下两个条目将介绍它们。

3. 将文件恢复到默认状态

在 Git 中,你可以使用一条新命令: git restore ,轻松地将文件恢复到默认状态。事实上,在大多数情况下,您应该把它当作 git reset 的替代品,因为它提供了更强大的功能。例如,使用 git restore --staged <filename> 和使用 git reset HEAD 可以获得相同的结果。

该命令还能做更多–您还能将文件恢复到默认状态。如果您也运行 git status ,就能看到如何做到这一点:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git restore <filename>
git restore <filename>
git restore <filename>

这将从工作目录中移除修改,就像什么都没发生过一样。和 git checkout -- <filename> 一样,您需要确保您不需要任何本地变更,因为它们将永远消失。

4. 修改提交

很多时候,当您推送了一个提交,却发现忘了包含一些重要的内容。有了Git,你可以很容易地修改提交,把遗漏的改动包括进去。

这需要一个特定的过程:

  • 首先,在项目需要的文件中进行修改。
  • 使用 git add 将它们作为典型的暂存文件。
  • 在暂存区域使用不同的命令重新提交这些改动:
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git commit --amend
git commit --amend
git commit --amend

这将使用您的暂存区域用新提交修改原始提交。因此,请确保您不需要旧版本的提交,因为它将丢失。我们也建议您在本地提交时使用 --amend 标志,而不是远程提交,原因与我们在本篇文章中提到的类似。

您也可以使用 git commit --amend 只编辑提交信息,命令如下:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git commit --amend -m "New commit message"
git commit --amend -m "New commit message"
git commit --amend -m "New commit message"

5. Git 日志

使用 Git 的日志可以帮助您了解仓库的历史。不过,我们不会把 git log 命令列为高级命令。相反,您可以使用各种选项来过滤输出,以满足您的需要:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git log
git log --decorate
git log --stat
git log git log --decorate git log --stat
git log
git log --decorate
git log --stat

例如,装饰日志条目会打印出所有显示提交的 ref 名称。 --stat 选项显示一个提交的插入和删除:

在终端运行 git log -stat 命令

在终端运行 git log -stat 命令。

您还可以使用其他选项来定制日志的输出–称为 “提交限制”。例如,使用以下命令

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git log --author=<author-name>
git log --grep=<pattern-string>
git log --author=<author-name> git log --grep=<pattern-string>
git log --author=<author-name>
git log --grep=<pattern-string>

在这里,您可以通过特定的作者姓名或文本模式过滤日志。事实上,您可以结合多个选项和标志来生成特定用途的日志。例如

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git log --oneline --author=<author-name> --since=<date> feature-temp
git log --oneline --author=<author-name> --since=<date> feature-temp
git log --oneline --author=<author-name> --since=<date> feature-temp

搜索自指定日期以来,某个作者在 feature-temp 分支中的所有提交,然后用单行条目打印出来。注意 <date> 参数也可以是字符串:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
--since=”Two weeks ago”
--since=”Two weeks ago”
--since=”Two weeks ago”

此外,如果您想搜索特定文件而不是分支,可以运行以下命令:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git log --oneline --author=bartonfink --since=”5 days ago” -- readme.rm
git log --oneline --author=bartonfink --since=”5 days ago” -- readme.rm
git log --oneline --author=bartonfink --since=”5 days ago” -- readme.rm

这组示例只是略微介绍了您可以用日志做的事情,但根据您的搜索条件,您可以在日志中找到精确的提交。

6. Git钩子

您有时可能会使用宏或其他自动化脚本来帮助运行代码。Git也以钩子的形式提供了此类功能。这些脚本会在某些事件(如提交或推送)发生时自动运行。还有很多方法可以使用钩子来强制代码格式化、运行测试等等。

钩子有两种类型:客户端和服务器端:

  • 客户端钩子基于提交和合并等本地操作触发。
  • 服务器端钩子会根据网络操作触发。例如,当仓库收到推送的提交时。

Git 会在您运行 git init 时为您的 repo 添加几个钩子示例。不过,您需要移除 .sample 扩展名才能使用它们:

显示 Git 在启动时安装的钩子示例

MacOS 中的一个文件夹,显示 Git 在启动时安装的钩子示例。

需要注意的是,一次只能运行一种钩子类型,不过也可以同时使用多个脚本。因此,文件名应根据示例脚本对应钩子类型:预提交(pre-commit)、更新(update)等。

创建 Git 钩子

要创建一个 Git 挂钩,需要在 .git/hooks 子目录下创建一个可执行脚本(不带扩展名)。只要把它添加到hooks文件夹,它就能运行。

您可以使用任何脚本语言,只要它能作为可执行文件运行即可。我们建议您使用RubyPython,但您也可以使用Bash、Perl等其他语言。你所需要做的就是改变你的解释器的路径,而不是默认的Bash:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#!/usr/bin/env python
#!/usr/bin/env python
#!/usr/bin/env python

在这里,你可以像正常一样编写代码。例如,这里是一个 Python 的 prepare-commit 脚本,它提示用户写好提交信息:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#!/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!")
#!/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!")
#!/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/<hook-name> 以确保可以执行。

总的来说,钩子是自动化重复任务和在团队中执行最佳实践的有力工具。

7. 提交引用

在 Git 中,您可以通过 SHA-1 加密哈希值来识别提交。虽然也可以通过完整的哈希值来引用提交,但这样做既繁琐又容易出错:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
bc7623b7a94ed3d8feaffaf7580df3eca4f5f5ca
bc7623b7a94ed3d8feaffaf7580df3eca4f5f5ca
bc7623b7a94ed3d8feaffaf7580df3eca4f5f5ca

Git 提供了几种更简短、更易记的提交名。例如,可以使用分支或标签名。例如,我们可以使用一个名为 “develop” 的分支。下面是一个例子,我们引用这个分支上的最新提交:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git diff develop..HEAD
git diff develop..HEAD
git diff develop..HEAD

这显示了 “develop” 分支上的最新提交( HEAD )与当前提交之间的差异。

您也可以通过提交历史中的相对位置来引用提交。例如,你可以用 HEAD~2 来表示当前提交之前的两次提交:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git diff HEAD~2..HEAD
git diff HEAD~2..HEAD
git diff HEAD~2..HEAD

Git还提供了其他几种引用提交的方式,比如用”@”符号引用当前分支,或者用”^”符号引用提交的父分支。通过使用这些速记符号,您可以在处理提交时节省时间并避免错误。

8. 存储

在通常情况下,您会认为没有办法在不提交的情况下存储您对文件所做的更改。暂存就是临时存储的方法。当你需要切换分支或处理不同的任务,但又不想提交修改时,这种方法非常有用。

例如,如果你需要切换分支来处理流程中的某些事情,你可以把修改保存在当前分支,然后签出另一个分支。从那里,您可以在另一个分支上工作,然后提交并推送这些变更。然后,您就可以在原始分支上签出并检索您的工作。

存储变更有两种方法:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git stash
git stash
git stash

这将把您的改动存储到一个新的储藏库中,并把您的工作目录还原到上一次 HEAD 提交的状态(即您做新改动之前的状态)。您可以用 git stash list 列出改动,并用 git stash show 查看。后一个命令也可以接受任何 git diff 接受的格式。

在这里,您可以切换分支或处理其他任务。当您想取回您的改动时,运行下面的命令:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git stash apply
git stash apply
git stash apply

这将把上次隐藏的更改应用到您的工作目录中。不过要注意的是,如果您对文件改动过大,仍然可能会遇到冲突。毕竟, git stash 只是暂时解决问题的办法。

您也可以有多个储藏库,您可以使用下面的命令指定应用哪个储藏库:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git stash apply stash@{n}
git stash apply stash@{n}
git stash apply stash@{n}

占位符 {n} 是一个整数, stash@{0} 代表最新的 stash。Git 官方文档中还有一些其他的 git stash 例子。

9. Bisecting

我们敢打赌,每个人都会遇到 bug 或问题,却不知道从哪里开始查找。在这种情况下,”bisecting” 可以帮助你快速找出引入问题的提交。

简而言之,该命令通过搜索您的提交来找出bug。一旦找到问题提交,它就会返回给你。这个命令的强大之处在于它的子命令。

要使用bisecting,首先需要运行 git bisect start 命令。Git 会将您带到项目历史中的第一次提交。

从这里开始,您需要使用相关命令指出该提交是好是坏:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git bisect good
git bisect bad
git bisect good git bisect bad
git bisect good
git bisect bad

然后,Git 会将您转到下一个提交,以测试其 “质量”。注意,您也可以用 “旧的” 替换 “好的”,用 “新的 “替换 “坏的”,以符合您的特定用例(但不能混用术语)。

从这里开始,您可以继续将每个提交标记为 “好” 或 “坏”,直到找到引入错误的提交。不过,您不必遍查每个提交–您可以指定确切的标识符来帮助缩小搜索范围并缩短搜索时间:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git bisect bad feature-test
git bisect bad feature-test
git bisect bad feature-test

这里使用的是分支名称,但也可以是使用整数、哈希引用等的具体修订版本。无论如何,一旦您找到错误,您可以运行以下命令之一来返回最新代码:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git bisect reset
git bisect reset
git bisect reset

与本列表中的所有 Git 高级命令一样,还有很多内容需要消化,Git 文档将是必不可少的读物。

10. 比较分支

我们在 “提交引用” 条目中提到了使用 git diff 。现在,是时候详细了解一下了。您经常会发现自己的多个分支包含了不同的改动。Git 允许您使用 git diff 命令比较两个分支之间的差异。事实上,您可以通过多种方式使用它,通常与其他命令一起使用,来调查和分析一个 repo。

基本的 git diff 命令会输出变化概览。它看起来很像提交合并调查的输出:

显示 git diff 请求的输出

显示 git diff 请求的输出。

不过,您还可以深入到精确的分支、哈希值等等。例如,要比较两个分支,您可以运行 git diff branch1..branch2 ,然后替换占位符:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git diff feature-branch pre-prod
git diff feature-branch pre-prod
git diff feature-branch pre-prod

您还可以比较当前分支与其他分支之间的差异:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git diff HEAD..pre-prod
git diff HEAD..pre-prod
git diff HEAD..pre-prod

请注意,这里使用两个点将返回两个分支顶端之间的差值。相比之下,三个点将返回两个分支的共同祖先之间的差异,并以此进行测试。

git log 一样,您也可以清理输出并细化其返回的内容。例如,git diff --name-only branch1..branch2 将只检查哪些文件不同,而省略上下文:

在终端运行 git diff -name-only 命令

在终端运行 git diff -name-only 命令。

您可能会发现输出很难解析,尤其是当 “diff” 很长的时候。在这种情况下,您可以使用 --color-words 选项:

运行 git diff -color-words 命令并在终端中查看输出

运行 git diff -color-words 命令并在终端中查看输出。

总的来说, git diff 和其他命令一样强大,尤其是当您调用某些选项并细化返回的差异时。

11. 应用单个提交

有时,您可能想在不合并两个分支的情况下,将某个特定的提交从一个分支应用到另一个分支。Git 提供了 git cherry-pick 功能。您应该小心使用,但您会发现 git cherry-pick 在一些情况下可以帮到您。

一种情况是,您有一些过时的特性分支没有合并到 main 或 trunk 中。您可以使用命令组合(比如 git log )把相关的旧提交挖掘出来,重新应用到其他地方。

使用 git log 查找某个提交的引用。从那里,确保您在您想cherry pick的分支上。例如,假设您想把提交 xxxxxaaaa 挑选到分支 ” trunk “。首先,签出您的分支…

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git checkout trunk
git checkout trunk
git checkout trunk

……然后选择您的提交:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git cherry-pick xxxxxaaaaaa
git cherry-pick xxxxxaaaaaa
git cherry-pick xxxxxaaaaaa

在很多情况下,您的提交信息可能会过时。为了解决这个问题,你可以在命令中加入 --edit 选项。这将让你在剔除之前提供一个新的提交信息。

12. 给 “git add” 加点料

最后一个高级Git命令是 git add 。不,这不是个错别字–这个最基本的 Git 命令有着惊人的力量。

举例来说,如果您发现自己在缓存区添加单个文件,您可以使用下面的命令:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git add -p
git add -p
git add -p

通过 -p 选项,您可以在交互的基础上对修改进行分期。 你可以查看你对每个文件所做的修改,然后选择哪些需要暂存。这可以节省你大量的时间,帮助你避免不需要的修改。

虽然您可能知道可以对单个文件进行暂存,但您也可以指定一个目录。例如,暂存 ” new-feature“目录下的所有文件:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git add new-feature
git add new-feature
git add new-feature

您甚至可能想在不执行完整过程的情况下查看 git add 的结果。您可以选择

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git add --dry-run
git add -n
git add --dry-run git add -n
git add --dry-run
git add -n

运行后,Git会显示是添加还是忽略这些文件。说到忽略的文件,你也可以把它们添加到暂存区:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
git add --force
git add -f
git add --force git add -f
git add --force
git add -f

向暂存区域添加文件可能比简单的二进制 “添加或不添加” 更复杂。因此,Git最核心的命令之一可以涵盖无数种可能的情况。

小结

一旦掌握了基本的 Git 命令,您就拥有了执行项目常规版本控制任务所需的 80%的知识。然而,最后的20%才是高级Git命令大显身手的地方。

重新排序、分段、恢复文件等命令和技术都能让您快速摆脱困境。更重要的是,你可以为团队和项目提供更大的价值,帮助简化工作流程,提高工作效率,全面提升开发者的能力。

这些高级Git命令会成为你日常工作的一部分吗?请在下面的评论区告诉我们!

评论留言