あなたはこんなアスキーアートを見たことがあるかもしれません。
git-logやgit-replayやgit-shortlogのman page の「Commit Limiting」に出てくるコミット・グラフを表した図です。
.-A---M---N---O---P---Q
/ / / / / /
I B C D E Y
\ / / / / /
`-------------' X
この図と同じリポジトリを作成します。
スクリプト
AIのgrok君に頼んでおおまか作成してもらったスクリプトです。
適当に掘ったディレクトリ内で以下スクリプトを実行します。
使い方
mkdir repos
cd repos
# ここに git-hist-simple-sample.sh を置く
mkdir repo
cd repo
bash ../git-hist-simple-sample.sh
スクリプト本体
git-hist-simple-sample.sh
#!/usr/bin/env bash
set -euo pipefail
DOT_GIT=".git"
if [ -d "$DOT_GIT" ]; then
echo "エラー: $DOT_GIT が存在するため、スクリプトを終了します。"
echo "空のフォルダを用意してください。"
exit 1
fi
git init --initial-branch=main
# コミット作成ヘルパー
commit() {
echo "commit" $@
local msg="$1"
shift
while [[ $# -gt 0 ]]; do
echo -n "$2" > "$1"
git add "$1"
shift 2
done
git commit -m "$msg"
}
echo "公式ドキュメントの --ancestry-path 説明図を正確に再現中..."
# 1. 最初のコミット I(空ツリーからの初回コミット → !TREESAME)
commit "I" foo "asdf" quux "quux"
I=$(git rev-parse HEAD)
# 2. A(foo を "foo" に変更)
commit "A" foo "foo"
A=$(git rev-parse HEAD)
# 3. B(A と同じ変更 → A と TREESAME)
git checkout -b side-B HEAD~1 # I に戻す
commit "B" foo "foo"
B=$(git rev-parse HEAD)
# 4. C(foo を変更しない → 親 I と TREESAME)
git checkout -b side-C $I
git commit --allow-empty -m C # 変更なし → TREESAME to I
C=$(git rev-parse HEAD)
# 5. D(foo を "baz" に変更)
# git checkout -q -b side-D $I
git checkout -b side-D $I
commit "D" foo "baz"
D=$(git rev-parse HEAD)
# 6. E(quux を "xyzzy" に変更)
# git checkout -q -b side-E $I
git checkout -b side-E $I
commit "E" quux "xyzzy"
E=$(git rev-parse HEAD)
# 7. X(新ファイル side を追加)
# git checkout -q -b side-X $I
git checkout --orphan side-X
git rm -rf . > /dev/null 2>&1
commit "X" side "first"
X=$(git rev-parse HEAD)
# 8. Y(X に対して TREESAME )
# git checkout -q -b side-Y side-X
git checkout -b side-Y side-X
git commit --allow-empty -m Y # 変更なし → TREESAME to X
Y=$(git rev-parse HEAD)
# メインラインに戻って順番にマージしていく(これが図の斜め線)
git checkout main
# M: A と B をマージ(trivial merge → M は両親に対して TREESAME)
echo "M:"
git merge side-B -m "M"
M=$(git rev-parse HEAD)
# N: M と C をマージ(C は変更なしだが、N で foo を "foobar" に変更)
echo "N:"
git merge --no-commit --no-ff side-C
echo -n "foobar" > foo
git add foo
git commit -m N
N=$(git rev-parse HEAD)
# O: N と D をマージ → foo が "foobarbaz" になる
# || ture は、コンフリクト発生すると exit code 1 を返すので無視する
echo "O:"
git merge --no-commit --no-ff side-D || true
echo -n "foobarbaz" > foo
git add foo
git commit -m O
O=$(git rev-parse HEAD)
# P: O と E をマージ → quux が "quux xyzzy" になる
# git merge -q side-E -m "P"
echo "P:"
git merge --no-commit --no-ff side-E || true
echo -n "quux xyzzy" > quux
git add quux
git commit -m P
P=$(git rev-parse HEAD)
# Q: P と Y をマージ → 新ファイル side が追加される
# git merge -q side-Y -m "Q"
echo "Q:"
git merge --allow-unrelated-histories -m "Q" side-Y
Q=$(git rev-parse HEAD)
echo "完全に再現完了!"
echo "場所: $(pwd)"
echo
echo "重要なコミット(短縮ハッシュ)"
echo " I : $(git rev-parse --short $I)"
echo " A : $(git rev-parse --short $A)"
echo " B : $(git rev-parse --short $B)"
echo " M : $(git rev-parse --short $M)"
echo " N : $(git rev-parse --short $N)"
echo " O : $(git rev-parse --short $O)"
echo " P : $(git rev-parse --short $P)"
echo " Q : $(git rev-parse --short $Q)"
echo " X : $(git rev-parse --short $X)"
echo " Y : $(git rev-parse --short $Y)"
echo
echo "これで試してください(公式ドキュメントと全く同じ結果になります)"
echo
echo " # 全体図"
echo " git log --graph --oneline --decorate --all"
echo
echo " # 普通の範囲指定(大量に出る)"
echo " git log --oneline I..Q"
echo
echo " # --ancestry-path を使うと、ドキュメント通りの結果!"
echo " git log --graph --oneline --decorate --ancestry-path I..Q"
echo " # → 表示されるのは M → N → O → P → Q の5コミットだけ!"
echo
echo " # さらに --simplify-merges も試すと"
echo " git log --graph --oneline --simplify-merges --ancestry-path I..Q"
echo " # → N-O-P-Q の4コミットだけになり、まさにドキュメントの斜線部分になります"
実行例
全体表示
git log --graph --oneline --decorate --all
* b068f04 (HEAD -> master) Q
|\
| * 73adbb0 (side-Y) Y
| * f86655f (side-X) X
* 876db2f P
|\
| * 979c03b (side-E) E
* | 41aae2c O
|\ \
| * | e3df52e (side-D) D
| |/
* | 6691af7 N
|\ \
| * | fb91665 (side-C) C
| |/
* | 99ccdf4 M
|\ \
| * | d792583 (side-B) B
| |/
* / a34e8f5 A
|/
* 0fc8da2 I
デフォルトモード
git log --graph --oneline -- foo
* 41aae2c O
|\
| * e3df52e D
* | 6691af7 N
|\|
* | a34e8f5 A
|/
* 0fc8da2 I
親の書き換え無し(--grah無し)--full-history
git log --pretty="format:%s" --full-history -- foo
Q
P
O
N
D
A
B
I
親の書き換えあり(--grahあり)--full-history
git log --graph --oneline --full-history -- foo
* b068f04 Q
* 876db2f P
|\
* \ 41aae2c O
|\ \
| * | e3df52e D
| |/
* | 6691af7 N
|\|
* | 99ccdf4 M
|\ \
| * | d792583 B
| |/
* / a34e8f5 A
|/
* 0fc8da2 I
--simplify-merges
git log --graph --oneline --simplify-merges -- foo
* 41aae2c O
|\
| * e3df52e D
* | 6691af7 N
* | 99ccdf4 M
|\ \
| * | d792583 B
| |/
* / a34e8f5 A
|/
* 0fc8da2 I