git 高级操作

概述

本文介绍使用 git 版本控制系统过程中所涉的一部分高级操作命令。

查看提交记录

命令 git log 可以查看全部提交记录信息,而借助于命令 git show ,我们可以查看单个提交记录、区间提交记录的具体信息。

单个提交记录

对于提交记录而言,它们使用 <commit-id> 进行标识 (<commit-id> 实际为 SHA-1 散列值),因此我们可使用命令 git show <conmmit-id> 查看指定提交记录的具体信息。由于 <commit-id> 往往很长且难于记忆,所以 Git 提供快捷方式:允许使用不少于 4 个字符的 SHA-1 散列值代替 <commit-id>,如果该散列值仅能对应到一个 <commit-id>,则查询成功,否则查询失败。针对图例,等价命令可以是:git show f58af6ceb89e5e00git show f58a

借助于命令 git log --abbrev-commit --oneline ,Git 可为提交记录显示简短而唯一的 SHA-1 散列值,默认截取 <commit-id> 前 7 个字符。

除上面这种查看方式外,Git 仍提供其他多种方式拥有查看特定分支的提交信息:

  • 如果特定提交记录为某分支的顶端提交,则可使用该分支名表示此提交记录,针对上图例中提交,我们可使用等价命令 git show 250

  • 使用工具工作之时,Git 会在本机本地的后台保存一个引用日志 (reflog),它用于记录 HEAD 和各分支所指向的提交记录。命令 git reflog 可用于查看这些信息:

    那么 HEAD 和各分支对提交记录就存在一一对应关系,使用命令 git show HEAD@{2}/250@{2} 可用于查看 HEAD 或分支 250 所指向的倒数第 2 个提交记录。

  • 借助于特殊字符 ^<num>,我们可以查看特定提交记录的第 <num> 个父提交记录信息。例如:命令 git show f58a^ 用于查看 f58a 所示提交记录的第 1 个父提交记录信息;命令 git show f58a^2 用于查看 f58a 所示提交记录的第 2 个父提交记录信息。

    <num> 个父提交记录仅对于分支合并所产生的提交记录有用。对于该记录而言,它的第 1 个父提交记录位于合并分支之上,第 2 个父提交记录位于被合并分支之上。

  • 借助于特殊字符 ~<num>,我们可以查看特定提交记录的祖父级提交记录信息,例如:命令 git show f58a~/f582~1 与命令 git show f58a^ 具有相同作用,均用于查看 f58a 所示提交记录的父提交记录信息;命令 git show f58a~2 用于查看 f58a 所示提交记录的父提交记录的父提交记录信息。

    默认均指代第 1 父提交记录。

区间提交记录

区间提交记录可用于解决问题 “某分支还有哪些提交记录尚未合并至特定分支?”。它共有三种语法:

  • 双点

    命令 git log <branchA>..<branchB> 用于查看在分支 <branchB> 中且不在分支 <branchA> 中的提交记录。

  • 多点

    为弥补双点命令不能作用于多个分支,多点命令被提出。它使用 <branchA> 表示在分支 <branchA> 、使用 ^<branchA>/--not <branchA> 表示不在分支 <branchA> ,从而提供多分支支持。

    命令 git log <branchA> <branchB> ^<branchC> 或命令 git log <branchA> <branchB> --not <branchC> 用于查看在分支 <branchA> 或在分支 <branchB> 且不在分支 <branchC> 中的提交记录。

  • 三点

    命令 git log <branchA>...<branchB> --left-right 用于查看被两分支之一包含且不被两分支同时包含的提交记录。借助于参数 --left-right,输出结果将标识哪些提交记录仅出现于哪个分支。

交互式暂存

交互式暂存 Git 命令可用于将项目文件的特定部分组合为一次提交。换言之,如果我们修改大量文件后,希望将这些改动拆分为若干提交而非一次提交,使用交互式暂存命令将会很容易做到这一点。

使用命令 git add -i/--interactive 后,我们将进入交互式终端模式。该模式界面比较特殊,它显示了已暂存文件区和未暂存文件区,同时下方列出可执行的命令。

如果希望列出交互式暂存状态,可在 What now> 提示符后键入 1sstatus

如果希望暂存文件,可在 What now> 提示符后键入 2uupdate,随后选择文件对应数字以待暂存此文件 (数字前有 * 表示文件处于待暂存状态),如果某次待暂存文件集为空,则表示暂存文件工作完成。

如果希望取消暂存文件,可在 What now> 提示符后键入 3rrevert,随后操作与暂存文件过程类似。

如果希望添加未追踪未经,可在 What now> 提示符后键入 4aadd untracked

如果希望暂存指定文件的特定部分,可在 What now> 提示符后键入 5ppatch,随后选择待部分暂存文件对应数字,接下来 Git 会根据修改部分一一询问是否暂存。

如果希望查看已暂存文件 (指代真实位于暂存区的文件) 与暂存文件 (指代 staged 列表中文件)的区别,可在 What now> 提示符后键入 6ddiff,随后选择待查看文件对应数字即可。

贮藏与清理

当对某分支的若干文件进行部分改动、且想要切换到另一个分支完成其他事宜时,按照 Git 使用规程,我们必须提交此次修改,然后才能完成分支切换,否则报如下错误。如果不想单独为此创建一次提交,我们可使用命令 git stash 加以解决。

命令 git stash 或命令 git stash push 用于将工作目录中未完成的修改 (包括已暂存和未暂存的已追踪文件) 保存至 Git 栈中,同时你可在任何分支中应用这些修改。

命令 git stash list 用于查看所有保存至 Git 栈中的修改列表。

命令 git stash apply [--index] [stash@{<num>}] 用于将修改 stash@{<num>} 应用于当前分支。如果不使用参数 --index,则将所有改动恢复至工作区;如果使用参数 --index ,则将暂存区改动恢复至暂存区、工作区改动恢复至工作区。如果不添加参数 stash@{<num>} ,默认应用栈顶修改。,需要注意的是:如果当前分支相应内容已经修改,可能会发生合并冲突。

命令 git stash drop [stash@{<num>}] 用于从 Git 栈中删除指定修改 stash@{<num>}

命令 git stash pop 用于应用栈顶修改并从 Git 栈中删除之。

命令 git stash 存在若干变种,我们简单介绍一二:

  • git stash --keep-index

    该命令不仅将工作目录中未完成的修改保存至 Git 栈中,同时将已暂存文件仍然保留在暂存区中。

  • git stash --include-untracked

    该命令不仅将工作目录中未完成的修改保存至 Git 栈中,也会将未追踪文件保存至 Git 栈中。

  • git stash branch <new branchname>

    创建新分支,并将栈顶修改应用至该分支,随后丢弃栈顶修改。

如果希望删除工作目录中未追踪文件或文件夹,可使用命令 git clean -d -f (-f 表示强制删除)。为防止错删,可先行执行命令 git clean -d --dry-run ,它会列出将要删除的文件和文件夹。

搜索

Git 提供两个工具用于快速从其数据库中搜索指定内容。

命令 git grep <searchContent> 用于从工作目录中搜索指定内容,同时显示该内容所在文件名。该命令存在若干参数可以选择:

  • -n

    用于显示搜索内容在相应文件中的行号。

  • -c/--count

    搜索结果的统计信息,包括指定内容出现在哪些文件,指定内容在这些文件中出现的次数。

  • -p/--show-function

命令 git log -S <searchContent> 用于从提交记录中搜索指定内容。

重置

重置操作可用于任意切换仓库区中的历史版本,它具体通过命令 git reset 实现。

在具体说明命令 git rest 之前,简单回顾 Git 仓库:它由三部分组成 —— 工作区、暂存区、仓库区。借助于命令 git add,工作区文件可转至暂存区;借助于命令 git commit,暂存区文件转至仓库区。仓库区由一系列提交记录所组成,这些记录构成一个有向无环图。所谓分支便是一个指向提交记录的指针,HEAD 指针则指向当前分支。

命令 git reset <commit-id> 主要有三个版本:

  • git reset --soft <commit-id>

    该命令仅会调整当前分支使其指向特定提交记录,从而切换仓库区中当前版本为某特定历史版本。

  • git reset [--mixed] <commit-id>

    命令 git reset 默认使用参数 --mixed,除 git reset --soft <commit-id> 所完成的工作外,该命令会使用仓库区中 <commit-id> 所示提交记录的快照更新暂存区。

  • git reset --hard <commit-id>

    git reset --mixed <commit-id> 所完成的工作外,该命令会使用暂存区中内容更新工作区。

除使用 <commit-id> 执行重置外,还可根据文件或文件夹路径进行重置。由于此时可能涉及多个提交记录,因此仓库区中内容不会发生变化 (也即 git reset --soft 所指代过程不会发生) 。具体涉及如下命令:

  • git reset [--mixed] <commit-id> <file>

    此命令使用 <commit-id> 所指代提交记录更新暂存区中的文件 <file>

    如果命令中 <commit-id> 具体为 HEAD,则其等价于取消对文件 <file> 的暂存。

  • git reset --hard <commit-id> <file>

    此命令使用 <commit-id> 所指代提交记录更新暂存区中的文件 <file>,同时使用暂存区中文件更新工作区。