ツール‎ > ‎

Git (frozen)

要点とチートシートをまとめる。

初期設定

初期設定は必須(ユーザグローバルなので、内容はレポジトリではなく ~/.gitconfig に入る)。

  git config --global user.name "Kiichiro NAKA"
  git config --global user.email "knaka@ayutaya.com"

確認:

  git config --global --list

ディレクトリ構成

ディレクトリ構造として見ると、Git では、現状作業ツリーの中(./.git/)に Git 管理ディレクトリがあり、その中に、レポジトリだけでなく、各種のメタ情報(リモート、ステージ)が入っている。
  • ./.git/: 管理ディレクトリ。unstaged のメタ情報、staged の内容、レポジトリ
ここまでは物理構造。

ワークツリー、ステージ、レポジトリのモデル

次いで、論理構造:
  • 作業ツリー。"unstaged"(レポジトリやステージング観点から、"not updated" とも呼ぶ)を含む
  • ステージングツリー(インデックスツリー)。"staged"("to be commited とも")を含む、全て。つまりツリー。これを差分だと考えてしまうと、reset の時などに理解がおかしなことになる。
  • そして、レポジトリ

git のエディタ

プロセスが切れるやつ(ランチャー型のエディタ。ST2 とか)はダメだ。commit や "-i" の編集ができない。
  1. $GIT_EDITOR
  2. 設定 core.editor
  3. $VISUAL
  4. $EDITOR
  5. vi

サブコマンドのエイリアス

st(atus) や ci は欲しいか。

  git config --global alias.ci "commit"

雑記

.gitignore と .git/info/exclude
  • ルートの .gitignore って、ワーク全体に効くんだな。効かせたくない場合には "/~" と書けば良い?
  • .gitignore はデータ。.git/info/exclude はメタデータで、他の開発者とは共有されない
ブランチの目的考:
  • 実験: デプロイすべきブランチとの分離
  • 新機能: 本体と部分機能(パッチ)との分離
  • 不具合の修正
コミットの相対指定:
  • HEAD
  • HEAD^, HEAD~1 (以下、= 18f822e だとする)
  • HEAD^^, HEAD~2, 18f822e^, 18f822e~1
リモートへのブランチの作成:

  git checkout -b new_branch master
  git push origin new_branch

git push --all origin をすると、ローカルの全ブランチが送られるので、怒られると思われる。

当然、bare repository にも git remote -v は効く。空の bare repository から任意の状態へは fast forward になるから、他の通常 repository を init, add, commit したものを push できる。そこからレポジトリが始まる。

コマンド

各コマンドについてのメモ。

init

init: カレントディレクトリにレポジトリを作る。init --bare --shared で、作業ツリー・ステージングツリーなしのレポジトリを、グループの書き込み権限つきで作成する。

--shared の有無による違いは? 前述のグループ権限以外では以下の差があるのだが、よく分からない。

--- bare/config
+++ bare-shared/config
@@ -4,3 +4,6 @@
        bare = true
        ignorecase = true
        precomposeunicode = false
+       sharedrepository = 1
+[receive]
+       denyNonFastforwards = true

add

add: add -i で対話モード:
  • 困ったら status
    • staged と unstaged
  • patch で、パッチのハンクごとに追加指定(add -p でも同じ)。これをやるためにステージングが存在すると言っても過言ではない(あと、ファイル単位追加もだけど)。
  • diff は、staged と unstaged の差異なので、stage しないと何も出ない

commit

commit: "-a" で追加&コミット。パス指定も可能。これは、commit 時の内容が commit される(add では、ステージした際の内容がコミットされるのとの違い)。

コミットログ考:
  • 要約
  • 詳細(文脈レスで。関連情報への URL も)
"commit" について。Git では 1 つのスナップショットを「コミット」と称し、SHA-1 の ID でユニークに識別する。SHA-1 は 7 文字もあれば大抵はユニークなので、随所で略式で利用される。

log

log: -n, since..until, そこまでの部分とのデリミタが "--" で、その後にはパスが続く。
  git log --since="2 weeks"
  git log --before="1 month"

tag

tag: git tag タグ名 対象(commit なりブランチなり)

branch

git branch:
  git branch 新ブランチ 元ブランチ(なり、タグなり commit なり)
  git branch: 一覧
  git branch -a: 無指定(ローカルブランチ)と "-r"(リモート追跡ブランチ)の双方をリスト
  git branch -m: Move

rebase

リベースで、別の…?
  git tag a_branch-1.0 a_branch
  git checkout master
  git rebase a_branch
  git branch -d a_branch # リリースにタグ、実験は成功か失敗で終われば不要になる

コミット ID 指定の箇所には、コミット名、タグ名、ブランチ名が指定できるようだ。ブランチ名を指定すると、HEAD のコミット扱いになるのかな?

archive

git archive --format=zip --prefix=foo-1.0 HEAD > /tmp/test.zip

revert

git reset と git revert の違い
  • revert: 変更をなかったことにするための変更を加える(安全)
  • reset: 変更をなかったことにする(分散環境では危険)
git revert
  • revert は、コミット名指定。即コミットされる、というよりも、レポジトリが修正対象。-n で、ステージに留め、コミット待ちになる。

reset

git reset
  • ワーク、ステージ、レポジトリの HEAD を、指定したコミット(無指定で HEAD)へ移動させるコマンド
  • 分かりやすい → git reset についてもまとめてみる - murankの日記
  • モード
    • --soft で、レポジトリの HEAD のみを指定コミットへ移動。ステージとワークには最終型が残っている(変更履歴は消えるが)。
    • --mixed で、HEAD とステージを指定コミットへ移動。ワークには最終型が残っている(変更履歴は消えるが)。
    • --hard で、HEAD, ステージ, ワークの全てを指定コミットへ移動。どこにもさっきの最終型は残っていない。
  • なので、fetch 以降に自環境で行なった作業をチャラにしたければ、git reset --hard で良い。
  • ワークにある、管理外のファイルには手はつけない。
  • ステージは、モデルとしては、決して差分を保持する場所ではなく、スナップショットを保持する場所なのだ(特に、--soft において)。

diff

  • デフォルトでは、ステージングからワークへの差分
  • コミットを指定すれば、コミットからワークへの差分。例えば、HEAD(現行ブランチの直近コミット)とか
  • --cached は、コミット(デフォルトでは HEAD)からステージングへの差分
  • --stat で、ざっくり統計が見られる。--since は使えないみたいだな。

mv

コピーは無いが、git mv はある。改名は追跡できなかったのだろうか?

branch

ブランチ作成。「git checkout -b ブランチ名」でも可:
  git branch ブランチ名
  git branch # で確認
  git checkout ブランチ名

merge

git merge ~: 現ブランチへ、~ブランチの○○から HEAD までを merge。--squash で、複数コミットを圧縮して一つにしてからマージする(圧縮コミット)。--no-commit で、マージ&ステージはするがコミットをしない。

git cherry-pick <コミット ID>: -n で、チェリーピック&ステージはするがコミットをしない。

git merge でコンフリクト。qgit mergetool で、OS X ならば opendiff が使えるらしいですよ。

rebase

普通のマージ(コミットを改変しない)

  git checkout master
  git merge bugfix

rebase による統合(コミットを改変するので、スッキリではあるが、危険。merge と rebase の差は、revert と reset の差に似ている)

  git checkout bugfix
  git rebase master
  git checkout master
  git merge bugfix


rebase は、branch base 後のコミットが、branch rebase 後の状況に合うように改変される。これが、自前のレポジトリ内だけの問題であれば良いが、改変前のコミットがリモートで共有されていると困ったことになる。squash ならばいいのかな?
  • 【状況 1】リモートの統合ブランチに対する変更箇所を、ローカルの統合ブランチへ統合する場合
    • rebase で pull
    • 統合先ブランチは自分によるローカルの修正なので、変えても平気
    • 大を小へ rebase
  • 【状況 2】ローカルにおいて、トピックブランチに対する変更箇所を統合ブランチへ統合する場合
    • no-ff merge
    • トピックブランチがトピックブランチであったことを明示したいので、残す
    • 小を大へマージ
  • 【状況 3】ローカルにおいて、統合ブランチに対する変更箇所をトピックブランチへ統合する場合
    • rebase
    • 大を小へリベース

「欠点はほんの一行でまとめることができます。公開リポジトリにプッシュしたコミットをリベースしてはいけない」「リベースはあくまでもプッシュする前のコミットをきれいにするための方法であるととらえ、リベースするのはまだ公開していないコミットのみに限定するようにしている限りはすべてがうまく進みます。」


rebase は、-i するとついでに、連結、分割、順序変更ができる。注意:
  • 他と同様、「コミット~以降」は、~を含まない
  • 表示上、時系列は上から下へ
  • squash は、「前」のコミットに統合される(「後」ではない)

blame

git blame このあらい:
  • log や diff 等の差分ではなく、結果物の各行に対して検査ができる
  • 行範囲指定 -L begin,end, -L begin,[+-]nlines
  • -M ファイル内移動・コピーの検出
  • -C ファイル間移動・コピーの検出
レポジトリ指定:
  • 速い順
    • ローカル
      • file:///path/to/repo.git/
      • /path/to/repo.git/
    • Git
      • git://example.com[:port]/path/to/repo.git/
      • git://example.com[:port]/~[user]/path/to/repo.git
      • git://exmaple.com[:port]/~[user]/path/to/repo.git/
      • 9418 番ポート
      • 匿名なので、基本的に読み取り専用
      • git core に入っている、git-daemon
      • Ubuntu でサービス化するならば git-daemon-sysvinit パッケージ
    • SSH
      • 書き込み用には、基本的にこちら
      • ssh://[user@]host.xz[:port]/path/to/repo.git/
      • [user@]host.xz:path/to/repo.git/
    • HTTP/HTTPS
      • http[s]://host.xz[:port]/path/to/repo.git/
      • WebDAV(というか BASIC 認証?)で、アカウント絞って書き込みもできるらしい?
    • その他
      • ftp[s]://host.xz[:port]/path/to/repo.git/
      • rsync://host.xz/path/to/repo.git/

remote

$ git remote [show]
$ git remote -v
origin  git@git.ayutaya.com:Foo/Bar/Baz.git (fetch)
origin  git@git.ayutaya.com:Foo/Bar/Baz.git (push)
$ git remote show origin # 通信が生じるので、以下のような例も

  # Remote branches:
  #     AppStore                         new (next fetch will store in remotes/origin)

fetch と push における、ローカルブランチ・リモートブランチ間の結びつきも表示される。これは便利。

レポジトリのエイリアス(ここでは "origin")を変えられます。

$ git remote set-url origin /home/knaka/dev/bare
$ git remote add origin git@github.com:schacon/simplegit-progit.git

その上で、必要に応じて、ブランチにアップストリームのレポジトリを指定することが必要。

git branch --set-upstream development origin/development

"refspec" は、リモートレポジトリとローカルレポジトリの結びつけ。以下で確認。

  $ git remote show レポ名 # 左記で、レポジトリ内の結びつけは出るようだ

+refs/heads/hoge:refs/remotes/origin/hoge のように、ローカルレポ、リモートレポを結びつける。"+" が付くと fast-forward チェックをしない。

メタ情報として、.git/config に入っている。

(external) submodule

git submodule で確認。Git では、ブランチの最新ではなく、個々のコミットを取ってくる。モジュールでチェックアウトし、親でコミットすれば、取ってくるコミットを変更できる。「入門」p.115~ を参照。

git clone --recursive でも良いが、手順を踏むならば、
  1. git clone → サブモジュールは空
  2. git submodule init → サブモジュール内が、Git 管理下に
  3. git submodule update → ようやく取ってくる
clone 時に取り忘れたようであれば、 git submodule update --init --recursive すること。追加は、 git submodule add レポジトリ [ディレクトリ名] で。このままでは例によって空(ポイントのみ)なので、左記コマンドでもって取得する。
Comments