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,确认改动都是预期的。
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 做了两件事:
- 从工作区删除文件。
- 把这次删除操作记录到暂存区。
基本用法:
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 可能就认不出来了,会显示成 deleted 和 new file 两条记录。
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 commit 才能生效。而且 Git 在重命名时,内部其实做了「删除旧文件 + 添加新文件」两步,只是展示给你看的时候合并成了「renamed」。
你想想看,如果你在项目里把 user_controller.js 改成了 userController.js,Git 能自动识别出这是重命名,而不是删了一个文件又新建了一个。这样你在查看历史时,就能看到这个文件完整的变更记录,不会断掉。
好了,这一节的内容就到这里。这几个命令虽然基础,但用好了能帮你省不少事。尤其是 git diff 和 git log,我几乎每天都要用上几十次。下次你改完代码,提交之前,记得先 git diff 看一眼,确认没改错地方。