縛りプレイで勉強するGitの基礎

縛りプレイをすることで、Gitの基礎であるハッシュ(sha-1)を意識してみる。


縛りプレイ とは

1. SMプレイのうち亀甲縛りなど縄を使うプレイ(性行為)のこと。昭和から存在する言葉。
2. ゲームをプレイする際、本来ゲーム側からは設定されていない制限(縛り)を自ら科す事によって、より難易度の高いゲームをプレイする事。

記事のタイトルの意味は 2. の方です。

概要

縛りの条件は下記の通り。

つまり、リモート(origin含む)、ブランチ(master含む)、タグの使用禁止です。

関連して、下記のコマンド/操作も使用禁止です。

  • git clone (origin が作成されるため)
  • git checkout -b (-b オプションはブランチ作られるので禁止、それ以外はOK)
  • git pull (動作が複雑なので)
  • .git/ の直接編集(邪道なので)

つまり、常にdetached HEADで開発し、sha-1ハッシュをノートにメモしながら開発することになります。
これにより、「Gitの基礎であるハッシュ(sha-1)がどういうものか?」というのが分かります。*1

この記事には、cloneとか、pullみたいな上級者用のコマンドは登場しません。

Gitリポジトリの準備

まずはgitリポジトリを用意する必要があります。

$ mkdir git_proj
$ cd git_proj
$ git init

masterなんてブランチは不要なので、削除します。ただ、すぐに削除が出来ないため、空のコミットを作成し、その後にmasterブランチを削除します。

$ git commit --allow-empty -m "first commit"
$ git log -n 1
(表示されたsha-1の値をノートにメモする)
$ git checkout <メモしたsha-1>
$ git branch -d master

これで準備が出来ました。
実際に上記の手順で作業したのが下記の画像です。

ファイルの追加/コミットの手順

基本的に普通のadd/commitと同じです。

新しいファイルを作成して、追加した場合のコマンドなどを記載してみました。

$ echo "Hello, Git World" > README.md
$ git add README.md
$ git commit -m "add README"

コミット毎にノートにsha-1の値をメモしておいた方が良いです。(ブランチが使えないので)

$ git log
commit 421bcacadf8581e8cc459ea1254ee3990af967af
Author: sinsoku <sinsoku.listy@gmail.com>
Date:   Wed Jul 18 00:07:49 2012 +0900

    add README

commit 62f92f671d0e83d8a75fabd1dd46f64aa7b36dc6
Author: sinsoku <sinsoku.listy@gmail.com>
Date:   Tue Jul 17 23:59:08 2012 +0900

    first commit
sinsoku@sinsoku-vm:~/Projects/sample/git_proj(git:(421bcac...))
$ 

ファイルを変更したコミットを作ってみる。
markdown形式にREADME.mdを変更してみました。

$ echo "# Hello, Git World" > README.md
$ git add README.md
$ git commit -m "markdown形式に直した"

ログを確認してみる。

$ git log -n 1
commit 1a8905309eac98743b3b2facd30db992ff0d5b9e
Author: sinsoku <sinsoku.listy@gmail.com>
Date:   Wed Jul 18 00:15:44 2012 +0900

    markdown形式に直した

私の環境では、このような出力になりました。
sha-1の値は環境により異なりますが、大体の表示は同じになると思います。

ブランチ関連

branchコマンドを使わなくても、sha-1をcheckoutしてコミットをする事で自然にログが分岐します。

まず「add README」のsha-1の値をcheckoutしてみます。
(この値は各人で異なるので、自分の環境の値を使う)

$ git checkout 421bcacadf8581e8cc459ea1254ee3990af967af
Warning: you are leaving 3 commits behind, not connected to
any of your branches:

  1a89053 markdown形式に直した
  421bcac add README
  62f92f6 first commit

If you want to keep them by creating a new branch, this may be a good time
to do so with:

 git branch new_branch_name 1a8905309eac98743b3b2facd30db992ff0d5b9e

HEAD is now at 421bcac... add README
sinsoku@sinsoku-vm:~/Projects/sample/git_proj(git:(421bcac...))
$ 

警告が出ていますが、気にせずに作業します。
README.md は作られているので、Licenseのファイルでも作成してみましょう。

$ touch LGPL-2.1
$ git add LGPL-2.1
$ git commit -m "add License"

ログを確認しましょう。

$ git log

commit 1aeb413b6d839d85074cc0e3f5fee9f1dfe16252
Author: sinsoku <sinsoku.listy@gmail.com>
Date:   Wed Jul 18 00:29:18 2012 +0900

    add License

commit 421bcacadf8581e8cc459ea1254ee3990af967af
Author: sinsoku <sinsoku.listy@gmail.com>
Date:   Wed Jul 18 00:07:49 2012 +0900

    add README

commit 62f92f671d0e83d8a75fabd1dd46f64aa7b36dc6
Author: sinsoku <sinsoku.listy@gmail.com>
Date:   Tue Jul 17 23:59:08 2012 +0900

    first commit
sinsoku@sinsoku-vm:~/Projects/sample/git_proj(git:(1aeb413...))
$ 

「add License」のコミットは「add README」の次に作成されました。
現在の状態の詳細をlogのオプションを使って表示してみます。

$ git log --oneline --graph --decorate 1a8905309eac98743b3b2facd30db992ff0d5b9e 1aeb413b6d839d85074cc0e3f5fee9f1dfe16252
* 1aeb413 (HEAD) add License
| * 1a89053 markdown形式に直した
|/  
* 421bcac add README
* 62f92f6 first commit
sinsoku@sinsoku-vm:~/Projects/sample/git_proj(git:(1aeb413...))
$

上記のようにブランチが出来ています。
では、これをマージします。

$ git merge 1a8905309eac98743b3b2facd30db992ff0d5b9e

Merge made by the 'recursive' strategy.
 README.md |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
sinsoku@sinsoku-vm:~/Projects/sample/git_proj(git:(9e97a14...))
$ 

グラフ表示でログを確認してみます。

$ git log --oneline --graph --decorate
*   9e97a14 (HEAD) Merge commit '1a8905309eac98743b3b2facd30db992ff0d5b9e' into HEAD
|\  
| * 1a89053 markdown形式に直した
* | 1aeb413 add License
|/  
* 421bcac add README
* 62f92f6 first commit
sinsoku@sinsoku-vm:~/Projects/sample/git_proj(git:(9e97a14...))
$ 

ちゃんとマージされています。

リモートにブランチをpush

remoteコマンドやbranchコマンドが使えないため、で直接指定します。

$ git push git@github.com:sinsoku/git_proj.git 9e97a14:refs/heads/develop

これで、リモートリポジトリにdevelopブランチが作成されます。

リモートにタグをpush

これはブランチとほとんど同じです。

$ git push git@github.com:sinsoku/git_proj.git 9e97a14:refs/tags/v1.0.0

リモートの変更を取り込む

clone, pullが使えず、branchも使えないため、fetchを使います。
取り込んだ変更個所の指定は常にsha-1を使います。

例として、gitのリポジトリの変更を取り込んでみます。

$ mkdir git
$ cd git
$ git init
$ git fetch git://github.com/git/git.git

github上で確認すると、タグ v1.7.11.2 のハッシュタグは 8d141a1d562abb31f27f599dbf6e10a6c06ed73e のようです。

$ git log 8d141a1d562abb31f27f599dbf6e10a6c06ed73e
commit 8d141a1d562abb31f27f599dbf6e10a6c06ed73e
Author: Junio C Hamano <gitster@pobox.com>
Date:   Wed Jul 11 12:55:38 2012 -0700

    Git 1.7.11.2
    
    Signed-off-by: Junio C Hamano <gitster@pobox.com>

commit b700086d840ceaee339f9f031be72b34e237f84f
Merge: 2e1e8ef b31272f
Author: Junio C Hamano <gitster@pobox.com>
Date:   Wed Jul 11 12:58:28 2012 -0700

    Merge branch 'jc/maint-blame-unique-abbrev' into maint
    
    "git blame" did not try to make sure that the abbreviated commit
    object names in its output are unique.
    
    * jc/maint-blame-unique-abbrev:
      blame: compute abbreviation width that ensures uniqueness

上記のように、ログが表示されています(=変更が取得できています)。

参考書籍

入門Git

入門Git

Gitポケットリファレンス

Gitポケットリファレンス

後日談

この記事の話を隣の人に話したら「努力の方向性が間違っている」と言われました(´・ω・)

*1:たぶん