2021.05.08_pyhack
https://gyazo.com/a20ed03aaed3fbadcfb2328360caa598
エンジニアのためのGitの教科書(上級編)を読んでみました。
手を動かしながら本を読む時間がなかなかとれないので、pyhackの時間が有効に使えました。
.gitディレクトリ以下の構造(初級編)
構造とその中身に関して普段意識しない箇所の要点を書いておく
code:shell
$ tree .git
.git/
|-- HEAD
|-- config
|-- description
|-- hooks
| |-- applypatch-msg.sample
| |-- commit-msg.sample
| |-- fsmonitor-watchman.sample
| |-- post-update.sample
| |-- pre-applypatch.sample
| |-- pre-commit.sample
| |-- pre-merge-commit.sample
| |-- pre-push.sample
| |-- pre-rebase.sample
| |-- pre-receive.sample
| |-- prepare-commit-msg.sample
| |-- push-to-checkout.sample
| `-- update.sample
|-- info
| `-- exclude
|-- objects
| |-- info
| `-- pack
`-- refs
|-- heads
`-- tags
最初からgit-hookのサンプルがscaffoldで生成されるんですね。".sample"をファイル名から削除するとhookは有効になるとのこと。
excludeはリポジトリに対しての除外ファイル。ディレクトリ単位の除外ファイルパターンは.gitignoreに書く
objectsディレクトリは、3種類のobjectを扱う
blobオブジェクト: ファイルデータそのもの
treeオブジェクト: blobオブジェクトのIDへのリンク、1階層分のディレクトリの情報とファイルパス
commitオブジェクト: 変更時のメタデータ
objectの種類の確認は、git cat-file -t オブジェクトのファイル名(ハッシュ値)
code:shell
$ git cat-file -t 12799ccbe7ce445b11b7bd4833bcc2c2ce1b48b7
blob
$ git cat-file -t 353ddaea2db9dbdd6b190c5f5c8736c9caae257a
commit
objectsディレクトリのobjectデータ名(SHA1hash値)の先頭二文字はディレクトリ
code:shell
$ tree .git
|-- objects
| |-- 12
| | `-- 799ccbe7ce445b11b7bd4833bcc2c2ce1b48b7
| |-- 35
| | `-- 3ddaea2db9dbdd6b190c5f5c8736c9caae257a
| |-- 7e
| | `-- 8ea00534a9e105715e50819ff46d5b669256e6
| |-- info
| `-- pack
実際のHash値は、12799ccbe7ce445b11b7bd4833bcc2c2ce1b48b7, 353ddaea2db9dbdd6b190c5f5c8736c9caae257a, 7e8ea00534a9e105715e50819ff46d5b669256e6
Treeオブジェクト内のモードはUnixのファイルタイプとパーミッション
table: Treeオブジェクト内部のモード
モード 説明
100644 100(ファイル) + 644(実行できないrwrr)
100755 100(ファイル) + 755(実行可能rwxwxwx)
040000 ディレクトリ
120000 シンボリックリンク
160000 GitLink(git submodule)
code:shell
$ git cat-file -p d2693a0f14d4fb870d6294a509acde33fa6da82f
100644 blob 12799ccbe7ce445b11b7bd4833bcc2c2ce1b48b7 a.txt
100644 blob 67be85f1274474029aad8a75b823592324305aa4 b.txt
gitの仕組み(中級編)
bareリポジトリ
index(ワークディレクトリ)のないgitリポジトリ
code:shell
$ mkdir git-advanced-remote && cd git-advanced-remote
$ git init --bare
$ cd ../ && tree git-advanced-remote
./git-advanced-remote
|-- HEAD
|-- config
|-- description
|-- hooks
|-- info
|-- objects
...
git pushの管理情報
localリポジトリからの--set-upstreamオプションによる追跡
code:shell
$ git remote add origin ../git-advanced-remote
$ git push --set-upstream origin master
localリポジトリのrefsにremoteリポジトリが増える
code:shell
.git/
|-- COMMIT_EDITMSG
|-- HEAD
|-- config
|-- description
|-- hooks
|-- index
|-- info
| `-- exclude
|-- logs
| |-- HEAD
| `-- refs
| |-- heads
| | `-- master
| `-- remotes
| `-- origin
| `-- master
|-- objects
`-- refs
|-- heads
| `-- master
|-- remotes
| `-- origin
| `-- master
`-- tags
git tag
commitオブジェクトを参照する「タグオブジェクト」
code:shell
$ git tag -a v0.1 -m "proto1"
$ tree .git
...略...
`-- refs
|-- heads
| `-- master
|-- remotes
| `-- origin
| `-- master
`-- tags
`-- v0.1
$ cat .git/refs/tags/v0.1
61797217576d721e92a695ab88ab010809648884
$ git cat-file -t 61797217576d721e92a695ab88ab010809648884
tag
$ git cat-file -p 61797217576d721e92a695ab88ab010809648884
object 76856ea4a32c289a71f1113a0520d50a7460149c
type commit
tag v0.1
tagger example <ex@example.com> 1620458288 +0900
proto1
$ git log decorate=full
commit 76856ea4a32c289a71f1113a0520d50a7460149c (HEAD -> refs/heads/master, tag: refs/tags/v0.1, refs/remotes/origin/master)
Author: example <ex@example.com>
Date: Sat May 8 15:31:53 2021 +0900
2nd
git branch
コミットオブジェクトのハッシュ値やコミットIDの参照。(作成された時と最新のコミットIDのペア)
code:shell
$ git branch develop
$ cat .git/logs/refs/heads/develop
0000000000000000000000000000000000000000 76856ea4a32c289a71f1113a0520d50a7460149c ex <ex@example.com> 1620458845 +0900 branch: Created from master
$ cat .git/logs/refs/heads/master
0000000000000000000000000000000000000000 353ddaea2db9dbdd6b190c5f5c8736c9caae257a ex <ex@example.com> 1620452391 +0900 commit (initial): first
353ddaea2db9dbdd6b190c5f5c8736c9caae257a 76856ea4a32c289a71f1113a0520d50a7460149c ex <ex@example.com> 1620455513 +0900 commit: 2nd
git mv
データに変更のないファイル名(ファイルパス)の変更。blobオブジェクトは作成されず、コミットオブジェクト作成され、treeオブジェクトの更新がされる。
code:shell
$ git mv a.txt mv.txt
$ git status
On branch develop
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: a.txt -> mv.txt
$ git log
commit 4c12421558e3d3b1a3830093671e5894253d4f59 (HEAD -> develop)
Author: ex <ex@example.com>
Date: Sat May 8 16:42:49 2021 +0900
movefile a to mv
$ git cat-file -p 4c12421558e3d3b1a3830093671e5894253d4f59
tree 9378852cd4dd7d615b9c4a701e794982b0be42d5
parent 76856ea4a32c289a71f1113a0520d50a7460149c
author ex <ex@example.com> 1620459769 +0900
committer ex <ex@example.com> 1620459769 +0900
movefile a to mv
$ git cat-file -t 4c12421558e3d3b1a3830093671e5894253d4f59
commit
$ git cat-file -t 9378852cd4dd7d615b9c4a701e794982b0be42d5
tree
$ git cat-file -p 9378852cd4dd7d615b9c4a701e794982b0be42d5
100644 blob 67be85f1274474029aad8a75b823592324305aa4 b.txt
100644 blob 12799ccbe7ce445b11b7bd4833bcc2c2ce1b48b7 mv.txt
git commit履歴更新(上書き)の挙動
ファイル更新後のblobオブジェクトには更新後のデータしか格納されていない。Treeオブジェクトで更新後のHash値を参照しているだけ。
ファイル更新前のデータはそのまま保持されている。
code:shell
$ echo "bad and good" > b.txt
$ git add b.txt && git commit -m "add more words"
$ git log
commit 004ac31faca6563103e7aa7b8b5ca39693db2983 (HEAD -> develop)
...
$ git cat-file -t 004ac31faca6563103e7aa7b8b5ca39693db2983
commit
$ git cat-file -p 004ac31faca6563103e7aa7b8b5ca39693db2983
tree edc9ffc2e868609e1cafab2161f8cef2491adf86
parent 4c12421558e3d3b1a3830093671e5894253d4f59
...
$ git cat-file -p edc9ffc2e868609e1cafab2161f8cef2491adf86
100644 blob f0605ccc6f963cae38fb93a54046783c0185e29a b.txt
100644 blob 12799ccbe7ce445b11b7bd4833bcc2c2ce1b48b7 mv.txt
$ git cat-file -p f0605ccc6f963cae38fb93a54046783c0185e29a
bad and good
git merge
masterとdevelopのブランチ差分コミット
code:shell
$ git rev-list --pretty="%H %s" master...develop
commit 004ac31faca6563103e7aa7b8b5ca39693db2983
004ac31faca6563103e7aa7b8b5ca39693db2983 add more words
commit 4c12421558e3d3b1a3830093671e5894253d4f59
4c12421558e3d3b1a3830093671e5894253d4f59 movefile a to mv
merge(FirstForward): FirstForwardマージでは、参照するコミットIDがマージ元のIDに移動しているだけ。
code:shell
$ git checkout master
$ git merge develop
Updating 76856ea..004ac31
Fast-forward
b.txt | 2 +-
a.txt => mv.txt | 0
2 files changed, 1 insertion(+), 1 deletion(-)
rename a.txt => mv.txt (100%)
$ cat .git/refs/heads/master
004ac31faca6563103e7aa7b8b5ca39693db2983
merge(Recursive):
git log graphの通り、ブランチのHEADが参照しているコミットIDを1つにまとめて新たなコミットIDがマージされるブランチに作成されています。
code:shell
$ git checkout -b topic 4c12421558e3d3b1a3830093671e5894253d4f59
Switched to a new branch 'topic'
$ git branch
develop
master
* topic
$ echo "new type" > c.txt && git add c.txt
$ git commit -m "add new file"
1 file changed, 1 insertion(+)
create mode 100644 c.txt
$ git log
commit 12267105e67bcd3f9147ba3ed11be611d46f4f48 (HEAD -> topic)
$ git cat-file -p 12267105e67bcd3f9147ba3ed11be611d46f4f48
tree a850d92f42c6d7e8b6f6b86ec4f25ff217f578b1
parent 4c12421558e3d3b1a3830093671e5894253d4f59
$ git checkout master
$ git merge topic
$ git log --graph --pretty="%H %s"
* d5c9c6e1635874e41f19c78616a68a6c772b693b Merge branch 'topic'
|\
| * 12267105e67bcd3f9147ba3ed11be611d46f4f48 add new file
* | 004ac31faca6563103e7aa7b8b5ca39693db2983 add more words
|/
* 4c12421558e3d3b1a3830093671e5894253d4f59 movefile a to mv
* 76856ea4a32c289a71f1113a0520d50a7460149c 2nd
* 353ddaea2db9dbdd6b190c5f5c8736c9caae257a first
$ git cat-file -p d5c9c6e1635874e41f19c78616a68a6c772b693b <----マージコミット。それぞれのブランチのHEADのコミットIDがparentとして参照されている。
tree 1f96477c485f1236d3411a5d30b6446df21a889b
parent 004ac31faca6563103e7aa7b8b5ca39693db2983
parent 12267105e67bcd3f9147ba3ed11be611d46f4f48
$ git cat-file -p 1f96477c485f1236d3411a5d30b6446df21a889b <----それぞれのブランチの差分のblobオブジェクトがこのtreeオブジェクトに追加されている。
100644 blob f0605ccc6f963cae38fb93a54046783c0185e29a b.txt
100644 blob 9c362f9da6f2d7b9e33287a9f0acec3adf53b3c0 c.txt
git rebase
topicブランチから派生したrebase_topicブランチをmasterブランチから派生したrebase_mainに対してrebaseします。
rebaseブランチにrebase_topic分が更新commitされます。
code:shell
$ git branch rebase_main 004ac31faca6563103e7aa7b8b5ca39693db2983
$ git branch rebase_topic 12267105e67bcd3f9147ba3ed11be611d46f4f48
$ git checkout rebase_topic
$ git branch
develop
master
rebase_main
* rebase_topic
topic
$ git log --graph --pretty="%H %s"
* 12267105e67bcd3f9147ba3ed11be611d46f4f48 add new file
* 4c12421558e3d3b1a3830093671e5894253d4f59 movefile a to mv
* 76856ea4a32c289a71f1113a0520d50a7460149c 2nd
* 353ddaea2db9dbdd6b190c5f5c8736c9caae257a first
$ git rebase rebase_main
Successfully rebased and updated refs/heads/rebase_topic.
$ git log --graph --pretty="%H %s"
* bfd08226aa0fecffbbe5d7f805621408f9eec513 add new file <------ rebase_topic分がrebase_mainの上にくっつく
* 004ac31faca6563103e7aa7b8b5ca39693db2983 add more words
* 4c12421558e3d3b1a3830093671e5894253d4f59 movefile a to mv
* 76856ea4a32c289a71f1113a0520d50a7460149c 2nd
* 353ddaea2db9dbdd6b190c5f5c8736c9caae257a first
次いで、rebase_mainにrebase_topicをマージしてみます。rebase_main分がすでに上にくっついて履歴が一本化されたため、fast-forwardできます。
code:shell
$ git checkout rebase_main
$ git merge rebase_topic
Updating 004ac31..bfd0822
Fast-forward
c.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 c.txt
本日は時間の都合で中級編の内容までを手を動かしました。
上級編の内容は今後(次回?)にやる予定。