5、Git基础操作(二):git log、git diff、git show、git rm、git mv,以及文件重命名与删除的追踪

上一节我们聊了怎么把文件扔进仓库,这一节咱们来点更实在的——怎么查看历史、对比差异、删除和移动文件。说白了,就是让你能「回头看」和「动手改」。我刚开始用 Git 的时候,最怕的就是删错文件或者改完找不到原来的版本。后来发现,只要把这几个命令玩熟了,心里就踏实多了。

5.1 查看提交历史:git log

git log 是查看提交历史的利器。你每次 git commit 之后,都会生成一条记录,git log 就是把这些记录按时间倒序列出来。

git log

默认输出是这样的:

commit 1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0
Author: Your Name <your.email@example.com>
Date:   Mon Jan 15 10:30:00 2024 +0800

    修复了登录页面的样式问题

我个人习惯加几个参数,让输出更清爽:

  • git log --oneline:每条记录只显示一行,包含短 commit hash 和提交信息。
  • git log --graph:用 ASCII 图形显示分支合并历史,特别适合看多人协作的提交图。
  • git log -p:显示每次提交的具体改动内容(diff)。
  • git log --author="张三":只看某个人的提交。

举个例子,你想看最近 3 次提交的详细改动:

git log -p -3

我在项目中遇到过一个问题:同事说「我明明提交了,怎么找不到?」后来发现他用了 git commit --amend 修改了上一次提交,导致原来的 commit 被替换了。嗯,这里要注意,git log 默认只看当前分支的历史,如果你切换了分支或者做了 rebase,有些提交可能就「消失」了——其实还在,只是不在当前分支的日志里。

5.2 对比差异:git diff

git diff 是用来比较文件差异的。说白了,就是看看你改了哪些地方,还没提交。

常用场景:

  • git diff:比较工作区和暂存区的差异(还没 git add 的内容)。
  • git diff --staged:比较暂存区和最近一次提交的差异(已经 git add 但还没 git commit 的内容)。
  • git diff HEAD:比较工作区和最近一次提交的差异(包含已暂存和未暂存的所有改动)。
  • git diff <commit1> <commit2>:比较两个提交之间的差异。

举个例子,你改了一个文件,想看看改了啥:

git diff src/main.js

输出会显示哪些行被删了(前面有 -),哪些行被加了(前面有 +)。

我曾经有一次改完代码,觉得没问题就提交了,结果上线后出 bug。回头用 git diff 对比才发现,我不小心删了一行关键的初始化代码。所以我现在改完代码,提交前一定会跑一遍 git diff,确认改动都是预期的。

小技巧: 如果你觉得默认的 diff 输出太「原始」,可以试试 git diff --word-diff,它会按单词粒度显示差异,而不是整行。看长文本改动时特别有用。

5.3 查看某次提交的详情:git show

git show 用来查看某次提交的详细信息,包括提交信息和具体的文件改动。它比 git log -p 更聚焦,适合只看某一次提交。

git show 1a2b3c4d

如果不指定 commit hash,默认显示最近一次提交(HEAD)。

你也可以只看某个文件在这次提交中的改动:

git show 1a2b3c4d -- src/main.js

我个人习惯用 git show --stat 先看概览,看看这次提交改了多少文件、多少行,然后再决定要不要看具体内容。

5.4 删除文件:git rm

在 Git 里删除文件,不是直接按 Delete 键就完事了。你需要告诉 Git:「这个文件我不想要了,请从版本控制里移除。」

git rm 做了两件事:

  1. 从工作区删除文件。
  2. 把这次删除操作记录到暂存区。

基本用法:

git rm old_file.txt

执行后,再 git commit,这个文件就从仓库里彻底消失了。

如果你只是从工作区删了文件,忘了 git rm,Git 会提示你:

Changes not staged for commit:
    deleted:    old_file.txt

这时候你可以用 git rm old_file.txt 或者 git add old_file.txt 来让 Git 知道这个删除操作。

注意: git rm 会直接删除工作区的文件,不可恢复(除非从 Git 历史里找回)。如果你只是想从暂存区移除,但保留工作区的文件,用 git rm --cached

我曾经有一次手快,想删一个临时文件,结果 git rm 把整个目录都删了……还好有 Git 历史,用 git checkout HEAD -- <文件路径> 恢复了。嗯,从那以后我删文件之前都会先 git status 确认一下。

5.5 移动或重命名文件:git mv

重命名文件在 Git 里其实是一个「删除旧文件 + 创建新文件」的组合操作。但 Git 很聪明,它能自动识别出这是重命名,而不是删了一个又加了一个。

git mv 是最直接的方式:

git mv old_name.txt new_name.txt

这条命令等价于:

mv old_name.txt new_name.txt
git rm old_name.txt
git add new_name.txt

git mv 一步到位,省事。

你可能会问:「如果我用系统自带的 mv 命令,Git 能识别吗?」答案是能。Git 会通过文件内容相似度来判断是不是重命名。你试试:

mv old_name.txt new_name.txt
git status

你会看到类似这样的输出:

renamed:    old_name.txt -> new_name.txt

但如果文件内容改动太大,Git 可能就认不出来了,会显示成 deletednew file 两条记录。

核心要点: Git 不关心文件名,它关心的是文件内容。重命名只是给内容换了个标签。所以即使你改了文件名,只要内容没变,Git 就能追踪到历史。

5.6 文件重命名与删除的追踪原理

Git 追踪文件的方式,跟其他版本控制工具不太一样。它不记录「文件 A 改成了文件 B」,而是记录「这个 blob 对象(文件内容)现在叫这个名字了」。

举个例子:

  • 你有一个文件 a.txt,内容为 hello,commit 之后,Git 把内容存成一个 blob,hash 是 abc123
  • 你把 a.txt 重命名为 b.txt,内容不变,再次 commit。
  • Git 发现:b.txt 的内容 hash 也是 abc123,跟之前 a.txt 一样。于是它自动推断出这是重命名。

这就是为什么 Git 能追踪重命名——它靠的是内容匹配,而不是文件名匹配。

删除也是同理。你删了文件,Git 只是把那个 blob 从当前分支的引用里移除。但 blob 本身还在对象库里,只要你知道 hash,随时可以恢复。

下面这张图展示了文件从创建到删除或重命名的完整生命周期:

Git 文件生命周期 工作区 你正在编辑的文件 暂存区 git add 后的文件 本地仓库 git commit 后的记录 git add git commit 删除文件 git rm 或手动删除 暂存删除 git rm 自动暂存 提交删除 git commit 后文件消失 重命名文件 git mv 或 mv + git add 暂存重命名 git mv 自动暂存 提交重命名 git commit 后记录改名 正常流程 删除流程 重命名流程

从图中可以看到,无论是删除还是重命名,最终都要经过 git commit 才能生效。而且 Git 在重命名时,内部其实做了「删除旧文件 + 添加新文件」两步,只是展示给你看的时候合并成了「renamed」。

你想想看,如果你在项目里把 user_controller.js 改成了 userController.js,Git 能自动识别出这是重命名,而不是删了一个文件又新建了一个。这样你在查看历史时,就能看到这个文件完整的变更记录,不会断掉。

好了,这一节的内容就到这里。这几个命令虽然基础,但用好了能帮你省不少事。尤其是 git diffgit log,我几乎每天都要用上几十次。下次你改完代码,提交之前,记得先 git diff 看一眼,确认没改错地方。


公众号:蓝海资料掘金营,微信deep3321