05 常用指令
1. git reflog¶
git reflog 是 Git 中一个非常强大的工具,全称是 reference log(引用日志),用于记录本地仓库中引用(如分支、HEAD 等)的更新历史。它就像一个“时间机器”,可以帮助开发者追踪和恢复那些可能已经“丢失”的提交或操作,尤其是在误操作(如错误删除分支或重置提交)后。
什么是 Git Reflog?¶
git reflog 记录了本地仓库中引用(如 HEAD、分支或标签)的每次更新,包括:
- 提交(git commit)
- 分支切换(git checkout 或 git switch)
- 重置(git reset)
- 变基(git rebase)
- 合并(git merge)
- 创建或删除分支(git branch)
这些记录存储在本地仓库的 .git/logs/refs/heads/ 和 .git/logs/HEAD 文件中,仅存在于本地,不会被推送到远程仓库(如 git push、git fetch 或 git clone)。因此,git reflog 是一个纯粹的本地工具,提供了恢复误操作的“安全网”。
与 git log 不同,git log 显示的是提交历史(基于提交的祖先关系),而 git reflog 记录的是引用的移动历史(包括 HEAD 和分支的每次变更),因此它能捕获一些在 git log 中不可见的操作,比如被重置或删除的提交。
基本用法¶
运行以下命令可以查看 git reflog 的默认输出:
这将显示 HEAD 的引用日志,输出格式如下:
a1b2c3d HEAD@{0}: commit: 添加了新功能
d4e5f6g HEAD@{1}: reset: moving to HEAD^
h7i8j9k HEAD@{2}: checkout: moving from main to feature-branch
l0m1n2o HEAD@{3}: commit: 修复了 bug
- a 1 b 2 c 3 d:提交的 SHA-1 哈希值(前 7 位)。
- HEAD@{0}:表示这是 HEAD 的最新状态,数字递增表示更早的状态(如
HEAD@{1}、HEAD@{2})。 - commit/reset/checkout:操作类型。
- 描述:操作的详细信息,例如提交信息或分支切换。
如果想查看特定分支的引用日志,可以指定分支名称:
例如: 这会显示main 分支的引用日志。
子命令¶
git reflog 支持多个子命令,用于管理引用日志或执行特定操作。以下是常用的子命令:
- show(默认)
- 显示指定引用的日志,默认显示
HEAD的日志。 - 语法:
git reflog show <ref> - 示例:
-
备注:
git reflog默认运行git reflog show,等同于git log -g --abbrev-commit --pretty=oneline。 -
expire
- 用于清理过旧或不可达的引用日志条目,以优化存储空间。
- 语法:
git reflog expire [--expire=<time>] [--expire-unreachable=<time>] [--all] - 示例:
- 默认情况下,引用日志保留 90 天,不可达条目保留 30 天(由配置
gc.reflogExpire和gc.reflogExpireUnreachable控制)。 -
注意:此命令可能导致数据丢失,建议使用
--dry-run选项预览将被删除的条目: -
delete
- 删除指定的引用日志条目。
- 语法:
git reflog delete <ref>@{<specifier>} - 示例:
-
注意:删除引用日志可能导致无法恢复某些提交,需谨慎操作。
-
exists
- 检查某个引用是否有关联的引用日志。
- 语法:
git reflog exists <ref> - 示例:
- 如果引用日志存在,返回状态码 0;否则返回非 0。
常见使用场景¶
以下是 git reflog 的几种典型应用场景,帮助开发者解决实际问题:
1. 恢复误删除的分支¶
假设你不小心删除了一个分支(例如 feature-branch),可以通过 git reflog 找回:
a1b2c3d HEAD@{0}: reset: moving to main
d4e5f6g HEAD@{1}: checkout: moving from feature-branch to main
h7i8j9k HEAD@{2}: commit: 添加了新功能 (feature-branch)
找到 feature-branch 最后一次提交的哈希值(例如 h7i8j9k),然后恢复分支:
h7i8j9k 提交的 feature-branch 分支。
2. 恢复被重置的提交¶
如果你执行了 git reset --hard 并丢失了一些提交,可以通过 git reflog 找回:
a1b2c3d HEAD@{0}: reset: moving to HEAD^
d4e5f6g HEAD@{1}: commit: 添加了新功能
h7i8j9k HEAD@{2}: commit: 修复了 bug
找到丢失的提交(例如 d4e5f6g),然后重置回去:
3. 修复错误的变基¶
如果 git rebase 导致分支状态异常,可以用 git reflog 找到变基前的提交:
找到变基前的提交(例如 d4e5f6g),然后重置:
4. 查看 stash 历史¶
git reflog 也可以查看 git stash 的历史:
可以用以下命令恢复某个 stash:
5. 限制输出条目¶
如果引用日志很长,可以用 --max-count 或 -n 限制输出条目数:
6. 查看特定时间范围¶
可以用时间修饰符查看特定时间段的引用日志:
这会显示过去一周的引用日志。注意事项¶
- 本地性:
git reflog仅记录本地操作,不会同步到远程仓库。如果你在其他机器上克隆仓库,引用日志不会跟随。 - 日志清理:引用日志默认保留 90 天(可达条目)和 30 天(不可达条目)。Git 会通过
git gc自动清理过旧条目。如果需要永久保留,需调整配置: - 数据丢失风险:使用
git reflog expire或git reflog delete时要小心,可能导致无法恢复某些提交。 - 中文乱码问题:在某些环境下(如 Windows),
git reflog输出可能出现中文乱码。可以通过设置 Git 的编码解决:
与 git log 的区别¶
| 特性 | git log |
git reflog |
|---|---|---|
| 记录内容 | 提交历史(基于祖先关系) | 引用更新历史(HEAD 和分支的移动) |
| 范围 | 公共记录,随仓库同步到远程 | 本地记录,不随仓库同步 |
| 用途 | 查看提交历史、查找特定提交 | 恢复丢失提交、追踪引用变更 |
| 输出格式 | 详细的提交信息(作者、日期、消息等) | 简洁的引用变更记录(哈希、操作、时间) |
2. git reset:撤销更改或回退版本¶
定义¶
git reset 用于撤销暂存区的更改或回退到指定的历史版本,调整 HEAD 指针和文件状态。
主要模式¶
git reset 有三种模式,影响工作区、暂存区和本地仓库的程度不同:
- --soft:
- 仅移动 HEAD 指针到指定提交,保留暂存区和工作区的更改。
- 用途:撤销提交,但保留更改以重新提交。
- --mixed(默认):
- 移动 HEAD 指针并重置暂存区到指定提交,工作区文件不变。
- 用途:撤销暂存区内容,将更改放回工作区。
- --hard:
- 移动 HEAD 指针,重置暂存区和工作区到指定提交。
- 用途:彻底回退到某版本,丢弃后续更改(谨慎使用)。
常用命令¶
- 撤销暂存(从暂存区移回工作区):
- 回退到指定提交:
<commit-hash>可通过git log获取。
示例¶
- 撤销暂存:
- 修改
index.html并暂存: - 撤销暂存:
- 回退提交:
- 查看提交历史:
- 回退到指定提交,保留更改:
- 彻底回退:
注意事项¶
- 危险操作:
--hard会永久丢失未提交的更改,建议先备份。 - 远程仓库:若已推送(
git push),回退后需谨慎使用git push --force(通知团队)。 - HEAD 指针:
HEAD^表示上一个提交。HEAD~n表示前 n 个提交。
3. git diff:查看文件差异¶
定义¶
git diff 用于比较文件在不同工作区域之间的差异,帮助检查修改内容。
主要用法¶
- 工作区 vs 暂存区:
- 显示工作区中已修改但未暂存的更改。
- 暂存区 vs 本地仓库:
- 显示暂存区与上次提交(HEAD)的差异。
- 工作区 vs 本地仓库:
- 显示工作区与上次提交的差异(包括已暂存和未暂存)。
- 比较特定文件:
- 比较两个提交:
示例¶
- 检查工作区修改:
- 修改
index.html: - 查看差异:
- 检查暂存区差异:
- 暂存修改:
注意事项¶
- 输出格式:绿色(
+)表示添加,红色(-)表示删除。 - 空输出:若无差异,
git diff无输出。 - 可视化工具:可用
git difftool配合工具(如 VS Code、Meld)查看差异。
4. git rm:从 Git 管理中移除文件¶
定义¶
git rm 用于从工作区和暂存区删除文件,并记录删除操作以提交。
主要用法¶
- 删除文件并暂存删除操作:
- 删除工作区的文件并将其移除标记添加到暂存区。
- 仅从 Git 跟踪中移除(保留工作区文件):
- 停止跟踪文件,但保留工作区中的文件。
示例¶
- 删除文件:
- 删除
oldfile.txt: - 停止跟踪但保留文件:
- 停止跟踪
config.local: - 文件仍保留在工作区。
注意事项¶
- 提交确认:
git rm仅修改暂存区,需git commit确认删除。 - 恢复删除:
- 若未提交,可用
git reset <file>撤销暂存,git checkout -- <file>恢复文件。 - 若已提交,需回退提交(见
git reset)。 .gitignore:移除跟踪后,建议将文件添加到.gitignore避免再次跟踪。
5. .gitignore:忽略文件¶
定义¶
.gitignore 是一个文本文件,定义 Git 忽略的文件或目录模式,避免将无关文件(如临时文件、日志、依赖)纳入版本控制。
创建与配置¶
- 创建
.gitignore: - 添加忽略规则:
- 每行一个模式,支持通配符。
- 示例内容:
常用规则¶
- 通配符:
*:匹配任意字符(除路径分隔符/),如*.log忽略所有.log文件。**:匹配任意层级目录,如**/temp/忽略所有temp文件夹。!:排除忽略,如!important.log强制跟踪。- 目录:以
/结尾,如node_modules/。 - 注释:以
#开头。
示例¶
- 忽略常见文件:
- 检查忽略效果:
- 创建
test.log文件,运行:
注意事项¶
- 生效范围:
.gitignore只对未跟踪文件生效,已跟踪文件需用git rm --cached。 - 全局
.gitignore: - 创建全局忽略文件:
- 模板资源:参考 GitHub 的 .gitignore 模板 获取语言/框架特定规则。
综合示例¶
以下是一个结合所有命令的场景:
-
初始化并创建文件:
-
配置
.gitignore: -
添加和修改文件:
-
查看差异:
-
撤销暂存:
-
删除文件:
-
停止跟踪但保留文件:
最佳实践¶
- 频繁检查差异:用
git diff确认修改内容。 - 谨慎使用
git reset --hard:确保备份重要更改。 - 规范
.gitignore:项目初期配置,定期检查。 - 提交前确认:用
git status和git diff --cached确保提交内容正确。 - 移除后更新
.gitignore:用git rm --cached配合.gitignore清理已跟踪的无关文件。
常见问题¶
.gitignore不生效:- 检查文件是否已跟踪(用
git rm --cached移除)。 - 确保模式语法正确(如目录用
/结尾)。 - 误删文件:
- 未提交:用
git checkout -- <file>恢复。 - 已提交:用
git reset或git checkout <commit> -- <file>。 git diff无输出:- 确认是否有修改或暂存内容。
- 检查命令参数(如
git diff --cached)。