FORMA

Git 查看历史、撤销回退与冲突解决

排错是日常开发中绕不开的一环:查看历史定位问题、选择正确的方式撤销或回退、以及在合并/拉取/变基时解决冲突。本文汇总这三部分内容。

一、查看提交日志(git log

git log 用于查看项目的提交历史,支持多种过滤和显示格式。

基本用法

bash
git log                     # 完整日志(按时间倒序)
git log --oneline           # 简洁模式:一行显示 commit hash + 提交说明
git log --graph --oneline   # 图形化分支结构,非常直观

限制显示条数与时间范围

bash
git log -n 3                  # 只显示最近 3 条提交
git log --since="2025-01-01"  # 显示指定日期之后的提交
git log --until="2025-03-01"  # 显示指定日期之前的提交
git log --since="2 weeks ago" # 支持相对时间

按作者/内容筛选

bash
git log --author="Zhang"    # 作者名称包含 "Zhang" 的提交
git log --committer="Li"    # 提交者名称包含 "Li" 的提交
git log --grep="fix"        # 提交说明中包含 "fix" 的提交

其他常用选项

bash
git log --stat              # 显示每个提交修改的文件统计(+/- 行数)
git log -p                  # 显示每个提交的具体代码差异(patch)

二、查看差异(git diff

git diff 用于比较不同区域之间的内容差异。

比较范围命令
工作区 vs 暂存区(未暂存的改动)git diff
暂存区 vs HEAD(即将提交的内容)git diff --staged(等价 --cached
工作区 vs HEAD(已暂存+未暂存的全部改动)git diff HEAD
两个 commit 之间git diff commit1 commit2
两个分支之间git diff branch1 branch2
只看文件名列表git diff --name-only / git diff --stat

综合示例

bash
# 查看最近 5 条提交的图形化日志
git log --oneline --graph -5

# 查看工作区有哪些改动(还未 add)
git diff

# 查看已暂存的改动
git diff --staged

# 查看当前分支与 main 分支的差异
git diff main

# 查看某个文件在两个 commit 之间的变化
git diff abc123 def456 -- src/index.js

三、撤销提交与回退

1. 修正上一次提交(git commit --amend

适用于:提交信息写错了,或者漏掉了几个文件。

bash
# 修改最近一次提交的说明
git commit --amend -m "新的提交说明"

# 补充漏掉的文件(先 git add),不修改说明
git add forgotten-file.txt
git commit --amend --no-edit
  • --amend 实际上会创建一个新的提交(新的 commit hash),替换掉上一次提交。
  • 注意:如果已经推送到远程,--amend 后需要 git push --force(谨慎),否则会与远程历史冲突。不建议对公共分支(如 main)使用 force push

2. 回退版本(git reset

将当前分支的 HEAD 指针移动到指定的提交,并可选地影响暂存区和工作区。

模式命令影响
--softgit reset --soft HEAD~1撤销 commit,保留修改在暂存区git status 显示为 “Changes to be committed”)
--mixed(默认)git reset --mixed HEAD~1撤销 commit 和 add,保留修改在工作区git status 显示为 “Changes not staged”)
--hardgit reset --hard HEAD~1完全丢弃修改(工作区、暂存区都恢复到目标提交的状态)—— 危险
  • HEAD~1 表示前一个提交,也可以写具体的 commit hash(如 git reset --soft abc123)。
  • --hard丢失所有未提交的改动,请确保不需要这些改动或已备份。

场景示例

  • 想重新编辑上一次提交的内容(撤销 commit 后重新 add、commit):用 --soft
  • 想撤销 commit 且取消暂存,但继续编辑:用 --mixed
  • 想彻底丢弃最近几次错误的提交:用 --hard(确认后使用)。

3. 查看历史操作记录(git reflog)——找回丢失的 commit

当你执行 git reset --hard 误删了提交,或者不小心丢弃了分支,reflog 是救命稻草。

bash
git reflog

它会列出所有 HEAD 的移动记录(包括 commit、reset、merge 等)。找到目标 commit 的 hash,然后:

bash
git reset --hard <commit-hash>
  • 注意reflog 只在本地存在,通常保留 90 天。如果你已经进行了 git gc 或操作时间太长,可能无法恢复。

4. 反向撤销(git revert)——安全地撤销已推送的提交

git revert 会创建一个新的提交,其内容正好抵消指定的提交所做的修改。这是无风险的撤销方式,因为不会重写历史。

bash
git revert <commit-hash>
  • 执行后会打开编辑器让你输入新提交的说明,也可以直接 git revert --no-edit
  • 如果需要撤销多个提交,可以指定范围:git revert HEAD~3..HEAD
  • 适用场景:已经推送到远程分支(尤其是公共分支),希望撤销某个提交但又不影响其他人的历史。

5. 撤销方式对比

命令是否改写历史是否适合已推送分支风险
git commit --amend是(替换最后一次提交)否(需 force push)中(可能影响协作者)
git reset(soft/mixed/hard)是(移动 HEAD,丢弃后续提交)否(需 force push)高(hard 模式会丢失数据)
git revert否(生成新提交来抵消)(推荐)

安全建议

  • 对于尚未推送的本地提交,可以使用 resetamend
  • 对于已经推送到共享仓库的提交,应优先使用 git revert,避免改写公共历史导致团队混乱。
  • 使用 git reset --hard 前,务必确认没有未保存的工作,或用 git stash 暂存。

四、冲突解决

1. 冲突产生情况

冲突通常发生在以下场景:

  • 合并分支时:两个分支修改了同一文件的同一区域,Git 无法自动决定保留哪个版本。
  • git pull:远程分支和本地分支都有新提交,且修改了相同区域(本质也是合并冲突)。
  • git rebase:变基过程中如果遇到提交冲突也会暂停。

2. 解决步骤

  1. 查看冲突文件
    bash
    git status
    

    会列出 both modified 的文件,这些就是存在冲突的文件。
  2. 编辑冲突文件
    打开冲突文件,你会看到类似下面的标记:
    text
    <<<<<<< HEAD
    当前分支的内容
    =======
    被合并分支的内容(如 feature-branch)
    >>>>>>> feature-branch
    
    • <<<<<<< HEAD======= 之间的代码是当前分支(通常是 HEAD)的版本。
    • =======>>>>>>> branch 之间的代码是另一个分支的版本。

    手动保留你需要的内容(可以是其中一边,也可以是两边组合),然后删除所有冲突标记(<<<<<<<=======>>>>>>>)。
  3. 保存文件
    保存你对冲突文件的修改。
  4. 标记为已解决
    bash
    git add <冲突文>
    

    (可以一次添加多个文件)
  5. 完成合并(提交)
    • 如果是 git merge 产生的冲突:
      bash
      git commit                      # 会生成默认的合并提交信息
      # 或
      git merge --continue
      
    • 如果是 git rebase 产生的冲突:解决后执行 git add <文件>,然后 git rebase --continue
    • 如果想放弃本次合并:git merge --abortgit rebase --abort

3. 示例流程

bash
# 假设合并时出现冲突
git merge feature-branch
# Auto-merging index.js
# CONFLICT (content): Merge conflict in index.js

# 1. 查看冲突文件
git status
# both modified: index.js

# 2. 编辑 index.js,删除冲突标记,保留正确内容

# 3. 添加已解决的文件
git add index.js

# 4. 完成合并
git commit -m "Merge feature-branch: resolve conflict in index.js"

解决冲突的关键是仔细对比两边的代码,确保最终结果逻辑正确。如果不确定,可以咨询相关开发者。

五、注意事项

  • git reset --hardgit push --forcegit rebase(对已推送提交)都属于高风险操作,操作前确认改动已备份或团队已知悉。
  • 冲突解决完成后,务必仔细检查代码逻辑,不能只是简单删除冲突标记,还要保证语义正确。
  • git reflog 是本地误操作的最后一道保险,但有效期有限(默认约 90 天),不能作为长期备份手段。

参考文献

资料说明
Pro Git官方书
Git 文档命令
运维导读学习路径