一覧SE-03-2 · Gitの変更管理

Gitのブランチ・タグ・HEAD

ブランチ切替えと履歴の追加を、HEAD・ブランチ・タグ・親コミットの関係から説明します。

説明

Gitのブランチは、あるコミットを指す名前です。通常の作業ではHEADが現在のブランチを指し、コミットを追加するとそのブランチの参照先が進みます。ほかのブランチやタグが同時に進むわけではありません。

alphaという1行を持つ初期コミットから始めます。featureブランチとv1タグを作り、featureだけにbeta、gammaを追加します。ファイルの内容、コミットID、親への参照を使って、どの名前がどこを指すか確認します。

ブランチ切替えと履歴の追加を、HEAD・ブランチ・タグ・親コミットの関係から説明します。

1HEADが現在のブランチを指す

初期コミットの短いIDと、HEADの参照先を表示します。

git log -1直近コミットを表示する --format="%h %s" git symbolic-ref HEADHEADが指す参照名を表示する

HEADはrefs/heads/mainを指しています。mainが指すコミットが、現在の作業の基準になります。短いIDは完全なオブジェクトIDの省略表記です。

2同じコミットへ別の名前を付ける

featureブランチと軽量タグv1を作ります。まだファイルを変更していません。

git branch featureブランチ名を作る git tag v1軽量タグを作る git show-ref --heads --tagsブランチとタグの参照先を表示する

feature・main・v1が同じIDを指します。ここで作った軽量タグはコミットへ直接名前を付ける参照で、注釈付きタグとは構造が異なります。

3featureでコミットを追加

featureへ切り替え、betaを追加して記録します。

git switch feature現在のブランチを切り替える printf 'alpha\nbeta\n' > note.txt git add note.txt git commit -m 'Add beta'featureへコミットを追加する git log --oneline --all --decorate各参照と名前を含めて履歴を表示する

HEADとfeatureが新しいコミットに進み、mainとv1は初期コミットに残ります。ブランチを作っただけでファイルの複製が増えるのではなく、参照が分かれた後で履歴が伸びています。

4現在・親・各名前のIDを照合

五つの参照を指定順に解決します。HEAD^は、現在のコミットの第1親を表します。

git rev-parse参照名を完全なIDへ解決する HEAD HEAD^現在のコミットの第1親 main v1 feature

1行目のHEADと5行目のfeatureが同じID、2〜4行目の親・main・v1が同じIDです。新しいコミットは、直前のコミットを親として記録しています。

5mainへ戻ると内容も戻る

作業状態が記録済みなので、mainへ切り替えます。

git switch mainmainのコミットへ作業を切り替える cat note.txt切替え後のファイルを読む git symbolic-ref HEAD

ファイルはalphaだけになり、HEADはmainを指します。betaのコミットが削除されたわけではなく、別のコミットを基準に作業ファイルが展開されています。

6切り替えずに別ブランチを読む

mainにいるまま、feature側のファイルを参照します。

git show feature:note.txtfeature内のファイルを読む git symbolic-ref HEAD現在のブランチが変わっていないことを確認する

表示内容はalphaとbetaですが、HEADはmainのままです。git showは対象の履歴を読む操作で、作業ブランチを切り替えません。

7さらに親子関係を一段伸ばす

featureへ戻り、gammaを追加します。logに各コミットの短いIDと親IDを表示します。

git switch feature printf 'alpha\nbeta\ngamma\n' > note.txt git add note.txt git commit -m 'Add gamma'次の変更を記録する git log feature --format='%h parent=%p %s'コミットID・親ID・件名を表示する

gammaの親はbeta、betaの親は初期コミットです。初期コミットには親がないためparent=の後が空です。通常のコミットはこの親参照で履歴をつなぎます。

8タグは追加コミットに追随しない

v1が保持する内容とIDを確認します。

git show v1:note.txtタグが指す内容を読む git rev-parse v1 mainタグとmainのIDを比較する

v1はalphaだけの初期コミットを指したままで、mainとも同じIDです。タグは通常、新しいコミットを作っても自動では移動しません。明示的な付け替えは可能なので、絶対に変更できない名前ではありません。

9差分と履歴の範囲を使い分ける

mainとfeatureのファイル差分、mainにないfeature側のコミットを順に表示します。

git diff main..feature二つの参照先の内容を比較する -- note.txt git log main..featuremainに含まれないfeature側の履歴を表示する --oneline

diffでは両端の内容を比較し、betaとgammaの追加が出ます。logのmain..featureはfeatureから到達でき、mainから到達できないコミットの集合です。同じ二点表記でも、diffとlogで示す対象は異なります。

10コミットに保存された親参照

コミットオブジェクトの内容を直接読みます。treeはファイル構成、parentは親コミット、authorとcommitterは作成・記録の情報です。

git cat-file -p HEADコミットオブジェクトを読む git show HEAD^:note.txt親コミット内のファイルを読む

parentの完全なIDがあり、その親のファイルにはalphaとbetaが入っています。コミットは差分だけの箱ではなく、ファイル構成を示すtreeと親参照などを持つオブジェクトです。

まとめ

HEADは通常、現在のブランチを指します。コミットを追加すると、そのブランチの参照先が進みます。

ブランチ切替えは基準と作業ファイルを切り替えます。git showなら切り替えずに別の履歴を読めます。

親参照で履歴がつながり、タグやほかのブランチは自動では同じ先へ進みません。

操作履歴と解説を振り返れます。

Git / シェル

1
git log -1 --format="%h %s" git symbolic-ref HEAD
dfaf585 Initial files
refs/heads/main
2
git branch feature git tag v1 git show-ref --heads --tags
dfaf585ee68990808a5f4c486d0007de53bee54d refs/heads/feature
dfaf585ee68990808a5f4c486d0007de53bee54d refs/heads/main
dfaf585ee68990808a5f4c486d0007de53bee54d refs/tags/v1
3
git switch feature printf 'alpha\nbeta\n' > note.txt git add note.txt git commit -m 'Add beta' git log --oneline --all --decorate
Switched to branch 'feature'
[feature 5f4534f] Add beta
 1 file changed, 1 insertion(+)
5f4534f (HEAD -> feature) Add beta
dfaf585 (tag: v1, main) Initial files
4
git rev-parse HEAD HEAD^ main v1 feature
5f4534f7a93dba890e3a4be2e8d272d88de79ea7
dfaf585ee68990808a5f4c486d0007de53bee54d
dfaf585ee68990808a5f4c486d0007de53bee54d
dfaf585ee68990808a5f4c486d0007de53bee54d
5f4534f7a93dba890e3a4be2e8d272d88de79ea7
5
git switch main cat note.txt git symbolic-ref HEAD
Switched to branch 'main'
alpha
refs/heads/main
6
git show feature:note.txt git symbolic-ref HEAD
alpha
beta
refs/heads/main
7
git switch feature printf 'alpha\nbeta\ngamma\n' > note.txt git add note.txt git commit -m 'Add gamma' git log feature --format='%h parent=%p %s'
Switched to branch 'feature'
[feature c9e8e19] Add gamma
 1 file changed, 1 insertion(+)
c9e8e19 parent=5f4534f Add gamma
5f4534f parent=dfaf585 Add beta
dfaf585 parent= Initial files
8
git show v1:note.txt git rev-parse v1 main
alpha
dfaf585ee68990808a5f4c486d0007de53bee54d
dfaf585ee68990808a5f4c486d0007de53bee54d
9
git diff main..feature -- note.txt git log main..feature --oneline
diff --git a/note.txt b/note.txt
index 4a58007..85c3040 100644
--- a/note.txt
+++ b/note.txt
@@ -1 +1,3 @@
 alpha
+beta
+gamma
c9e8e19 Add gamma
5f4534f Add beta
10
git cat-file -p HEAD git show HEAD^:note.txt
tree f6af0574449653e84bc2964063c166fea24ac722
parent 5f4534f7a93dba890e3a4be2e8d272d88de79ea7
author Learner <learner@example.test> 1793491200 +0900
committer Learner <learner@example.test> 1793491200 +0900

Add gamma
alpha
beta

演習完了