mcpField
J
jj-history-investigation

jj-history-investigation

github.com/edmundmiller開発8054 回インストール今日更新
リポジトリ

jj-history-investigation について

jj-history-investigationは、複雑なJujutsuリポジトリを扱う開発者向けのスキルです。jjコマンドを使った履歴調査、コミットの分割と整理、不変性への対処、コンフリクト解決を扱います。

主な機能

  1. 01コミット履歴の検索と確認
  2. 02annotateによる行変更の追跡
  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

コミットの分割

分割するタイミング

個別にレビューするべき複数の論理的な変更が含まれている場合は、コミットを分割します。

  • ✅ 適した例:「フロントエンド+バックエンド+テスト+ドキュメントの追加」
  • ✅ 複数の目的が混在:著作権ページ+謝辞+履歴書を1つのコミットにまとめている
  • ❌ すでに目的が明確:「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."

複数のファイルを1つのコミットに分割します。

# 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 つのモノリシックなコミットを、明確な意図と適切な TODO.org の更新を含む、変更内容を絞った 6 つのコミットに分割できました。

ヒントとコツ

調査を効率化する

よく使うクエリにはエイリアスを使用します:

# 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 - 共有に向けたコミットの整理

コメント