mcpField
J
jj-history-investigation

jj-history-investigation

github.com/edmundmiller开发8054 次安装今天更新
代码仓库

关于 jj-history-investigation

jj-history-investigation 面向处理复杂 Jujutsu 仓库的开发者,涵盖历史调查、拆分与清理提交、处理不可变性以及解决冲突,并使用 jj 命令完成这些操作。

主要功能

  1. 01搜索并检查提交历史
  2. 02用 annotate 追踪行变更
  3. 03将提交拆分为聚焦的改动
  4. 04在保护措施下改写不可变历史
  5. 05清理冗余提交并解决冲突

适用场景

  1. 01查明某项改动何时及何处引入
  2. 02拆分大型提交以便审查
  3. 03在变基或拆分后清理历史

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/edmundmiller/dotfiles --skill jj-history-investigation

安装后在 Claude Code 中输入/jj-history-investigation确认已加载。

详细介绍

Jujutsu(jj)历史调查与操作

概述

处理复杂的 jj 仓库时,你可能需要调查历史提交、将单体更改拆分为多个专注的提交,或清理杂乱的历史记录。本技能介绍高效技巧,用于:

  • 调查提交历史:查找更改的时间和原因
  • 使用 jj annotate:追踪特定行的添加者和添加时间
  • 拆分提交:将大型提交拆分为多个专注且便于审查的部分
  • 处理不可变性:重写共享历史时覆盖保护措施
  • 清理冗余内容:移除重复或空提交
  • 解决冲突:修复变基后的合并冲突

核心命令

命令用途示例
jj log查看提交历史jj log -r 'ancestors(@, 10)'
jj show显示提交详情jj show abc123
jj file annotate逐行追踪更改jj file annotate src/main.tex
jj edit开始编辑提交jj edit abc123 --ignore-immutable
jj split将提交拆分为多个部分jj split --ignore-immutable path/to/file
jj abandon从历史记录中移除提交jj abandon abc123
jj rebase将提交移至新的基底jj rebase -b main -d new-base

调查技巧

1. 查找更改时间

已知文件发生了更改,但不知道时间时,可使用以下方法:

检查近期提交消息:

# Search for commits mentioning "copyright"
jj log -r 'description(copyright)' --no-graph

# Output:
# trsozpwy feat(frontmatter): Add post-defense required sections
# (shows commits with "copyright" in the message)

查看提交详情:

# Show full commit with statistics
jj show trsozpwy --stat

# Show just the files changed
jj show trsozpwy --summary

检查特定文件的历史记录:

# See who/when each line was added
jj file annotate src/main.tex | grep "copyrightpage"

# Output:
# trsozpwy edmund.m 2025-11-09 00:13:05   49: \copyrightpage{2025}
2. 理解提交谱系

检查基于某个提交构建的内容:

# View descendants of commit abc123
jj log -r 'abc123::@' --limit 20

# Check ancestors of current commit
jj log -r 'ancestors(@, 10)'

查找分支之间的关系:

# See where main and feature branches diverged
jj log -r 'ancestors(main) & ancestors(feature)'

# Check if commit is ancestor of main
jj log -r 'abc123::main'
3. 提取特定提交信息

获取提交元数据:

# Show commit ID, author, date
jj show abc123 | head -10

# Get just the change ID
jj log -r abc123 --no-graph -T 'change_id'

检查文件更改:

# See what changed in a commit
jj diff -r abc123

# See specific file diff
jj diff -r abc123 src/main.tex

拆分提交

何时拆分

当一个提交包含多个应分别审查的逻辑更改时,应拆分该提交:

  • ✅ 适合拆分:“添加前端 + 后端 + 测试 + 文档”
  • ✅ 涉及多种关注点:在一个提交中包含版权页 + 致谢 + 简历
  • ❌ 已经聚焦:“修复 README 中的拼写错误”
基本拆分工作流

1. 检查提交是否不可变:

jj edit abc123

# If error: "Commit abc123 is immutable"
# Then use --ignore-immutable flag

2. 编辑提交:

jj edit abc123 --ignore-immutable

# Working copy now at abc123
# File system shows state at that commit

3. 按文件/目录拆分:

# Split out specific files
jj split --ignore-immutable src/main.tex

# jj will prompt for commit messages for:
# - Selected changes (what you specified)
# - Remaining changes (everything else)

4. 逐步拆分:

# After first split, you're at "remaining changes"
# Split again to extract more pieces
jj split --ignore-immutable src/chapters/appendix.tex

5. 更新提交消息:

# Update the commit you just split
jj describe @- -m "feat(frontmatter): Enable copyright page"

# Continue until all pieces are separated
高级拆分技巧

在同一提交中拆分并更新 TODO:

# 1. Split out the main change
jj split --ignore-immutable src/main.tex

# 2. In the split commit, also update TODO.org
# Edit TODO.org to mark item as DONE
jj describe -m "feat(frontmatter): Enable copyright page

Enable copyright on page ii as recommended by UTD.
Mark as complete in TODO.org."

将多个文件拆分到一个提交中:

# Save final state
cp src/main.tex /tmp/main.tex.final

# Restore to parent
jj restore --from @- src/main.tex

# Apply changes incrementally
# Make change 1, commit
# Make change 2, commit
# etc.

处理不可变性

提交为何会变为不可变

出现以下情况时,提交会变为不可变:

  • 它们有后代(基于其构建的其他提交)
  • 它们位于共享历史中(已推送到远程)
  • 它们在配置中被显式标记
覆盖不可变性

适用情形:

  • ✅ 仅本地历史记录(未推送)
  • ✅ 你拥有所有后代提交
  • ✅ 不会影响协作者

覆盖方法:

# Edit immutable commit
jj edit abc123 --ignore-immutable

# Split immutable commit
jj split --ignore-immutable src/main.tex

# Note: This will rewrite ALL descendant commits

将会发生什么:

# Before split:
# abc123 (monolithic commit)
#   ↓
# def456 (descendant 1)
#   ↓
# ghi789 (descendant 2)

# After split:
# abc123 (piece 1)
#   ↓
# xyz111 (piece 2)
#   ↓
# def456' (rewritten descendant 1)
#   ↓
# ghi789' (rewritten descendant 2)

清理历史记录

识别冗余提交

查找空提交:

# Show commit stats
jj show abc123 --stat

# If output shows "0 files changed", it's empty

查找冲突提交:

# Check for conflicts
jj log -r 'all()' | grep conflict

# Output:
# ×  abc123 (conflict) feat: Add something
弃置提交

移除冗余提交:

# Abandon single commit
jj abandon abc123

# Abandon multiple commits
jj abandon abc123 def456 ghi789

# What happens:
# - Commit is removed from history
# - Descendants are rebased onto parent
# - Changes are preserved in descendants

自动弃置空提交:

# After rebasing, jj may create empty commits
# Abandon them to clean up
jj log -r 'all()' --summary | grep "(empty)"
jj abandon empty-commit-id
拆分后清理

常见模式:

# 1. Split monolithic commit
jj edit old-commit --ignore-immutable
jj split src/file1.txt
jj split src/file2.txt
# (repeat for each logical piece)

# 2. Check for redundant descendants
jj log -r 'old-commit::@' --limit 30

# 3. Abandon redundant commits
jj abandon redundant-commit-1 redundant-commit-2

# 4. Verify history
jj log -r 'old-commit::@' --no-graph

解决冲突

变基/拆分后

1. 找出冲突:

jj status

# Output:
# Warning: There are unresolved conflicts at these paths:
# TODO.org    2-sided conflict

2. 创建提交以解决冲突:

# Create new commit on top of conflict
jj new conflicted-commit-id

3. 阅读冲突标记:

cat TODO.org

# Output:
# <<<<<<< Conflict 1 of 1
# +++++++ Contents of side #1
# ** DONE Add something
# %%%%%%% Changes from base to side #2
# ** TODO Add something
# >>>>>>> Conflict 1 of 1 ends

4. 选择解决方案:

# Edit file to pick side #1, side #2, or merge both
# Remove conflict markers

# Or use editor with conflict resolution
jj resolve TODO.org

5. 压缩解决冲突的提交:

# Merge resolution into parent
jj squash

# This resolves the conflict in the parent commit

验证历史记录

重大变更后

1. 检查提交的沿袭关系:

# View commits from base to current
jj log -r 'base-commit::@' --limit 50

# Check all commits are present
jj log -r 'commit1 | commit2 | commit3' --no-graph

2. 验证每个提交都聚焦明确:

# For each commit in your split:
jj show commit-id --stat

# Should show:
# - Small number of files changed
# - Logical grouping of changes
# - Clear commit message

3. 检查冲突:

# List all commits with conflicts
jj log -r 'all()' | grep conflict

# Should be empty after cleanup

4. 验证书签:

# Check where main/feature branches point
jj log -r 'bookmarks()'

# Ensure they're at correct positions
历史记录健康检查清单

拆分/变基/清理后:

  • jj status 中没有冲突标记
  • jj log 中没有 (conflict) 标签
  • 没有空的 (empty) 提交(除非有意为之)
  • 提交消息清晰且具有描述性
  • 每个提交都包含聚焦明确的变更
  • 书签(main、feature 等)指向正确
  • TODO.org 或类似文件保持一致

真实示例

场景: 历史提交 trsozpwy 混杂了 6 项不同变更。需要拆分以供审查。

调查阶段:

# 1. Find the commit
jj log -r 'description(post-defense)' --no-graph
# Found: trsozpwy

# 2. Check what's in it
jj show trsozpwy --stat
# Shows: 10 files changed

# 3. Check if immutable
jj edit trsozpwy
# Error: Commit is immutable (16 descendants)

# 4. Verify we can override
jj log -r 'trsozpwy::@' --limit 20
# Shows descendant commits, all owned by us

拆分阶段:

# 1. Override immutability
jj edit trsozpwy --ignore-immutable

# 2. Split out Python scripts
jj split --ignore-immutable src/figures/modules/
jj describe @- -m "feat(ch3): Add core overlap Venn diagram scripts"

# 3. Split out appendix fix
jj split --ignore-immutable src/chapters/appendix-*.tex
jj describe @- -m "fix(appendix): Remove 'A' label from solo appendix"

# 4. Split frontmatter pieces incrementally
# (Copyright page)
# Edit src/main.tex to add only copyright
jj commit -m "feat(frontmatter): Enable copyright page (2025)"

# (Acknowledgments)
# Edit src/main.tex to add acknowledgments
jj commit -m "feat(frontmatter): Add acknowledgments section"

# (And so on for biographical sketch, CV)

清理阶段:

# 1. Check for redundant descendants
jj log -r 'trsozpwy::@' | grep "Mark post-defense"
# Found: kqkm (redundant - duplicates our split work)

# 2. Abandon redundant commits
jj abandon kqkm lkto qqkq srls

# 3. Rebase main branch onto splits
jj rebase -b main -d last-split-commit

# 4. Resolve conflicts
jj new conflicted-commit
# Edit TODO.org to pick correct side
jj squash

验证阶段:

# 1. Check final structure
jj log -r 'first-split::@' --limit 30

# 2. Verify each split commit
jj show trso --stat  # Core overlap scripts
jj show nlnr --stat  # Appendix fix
jj show zspm --stat  # Copyright page
jj show mrpk --stat  # Acknowledgments
jj show wqrn --stat  # Biographical sketch
jj show ynom --stat  # CV

# 3. Confirm no conflicts
jj status
# (should be clean)

# 4. Check bookmarks
jj log -r 'bookmarks()'
# main and Cleanup should be on correct commits

结果: 成功将 1 个单体提交拆分为 6 个聚焦明确的提交,每个提交意图清晰,并正确更新了 TODO.org。

提示与技巧

加快调查速度

为常用查询使用别名:

# In ~/.jjconfig.toml
[aliases]
recent = ["log", "-r", "ancestors(@, 20)", "--no-graph"]
conflicts = ["log", "-r", "conflicts()"]
empty = ["log", "-r", "empty()"]

高效搜索提交消息:

# Case-insensitive search
jj log -r 'description("(?i)copyright")'

# Multiple terms
jj log -r 'description(frontmatter) & description(copyright)'
避免常见陷阱
  1. 不要未经调查就拆分:务必先运行 jj show,了解提交中包含哪些内容
  2. 拆分前保存状态:进行重大历史记录改写前,先执行 cp important-file /tmp/
  3. 逐步拆分:不要试图一次性拆分所有内容;每次处理一个逻辑部分
  4. 立即更新 TODO:在完成工作的同一个提交中标记 TODO 项
  5. 每一步后都进行验证:拆分/变基操作期间,频繁检查 jj log
不应拆分的情况
  • 提交已推送,并与他人共享
  • 变更确实不可拆分(例如“修复拼写错误及其测试”)
  • 拆分会让历史记录更难理解
  • 提交年代久远且很少被查看

参考资料

相关技能

  • jj:jj-workflow - jj 基本操作与工作流
  • jj:commit-messages - 编写优秀的提交消息
  • jj:commit-curation - 整理提交以便共享

评论

支持 Markdown