Skip to content

Instantly share code, notes, and snippets.

@fortune
Last active August 19, 2022 10:42
Show Gist options
  • Save fortune/81f0d0668ad1480c7225810939ffe130 to your computer and use it in GitHub Desktop.
Save fortune/81f0d0668ad1480c7225810939ffe130 to your computer and use it in GitHub Desktop.
My Git Cheet Sheet

Git のチートシート

すでに Git はインストール済みとする。

Git の設定

まず、システム上のすべてのリポジトリに適用される設定のデフォルト値を定義する。

$ git config --global user.name "Kazuyoshi TOMITA"
$ git config --global user.email "*****@gmail.com"
$ git config --global color.ui "auto"
$ git config --global core.excludesfile "/home/fortune/.gitignore"

core.excludesfile で指定したファイルには、すべてのリポジトリで無視すべきファイルのパターンを記述する。Mac OS なら .DS_STORE を記述するだろう。

設定情報は、ホームディレクトリの .gitconfig ファイルに記録される。設定情報を確認するには、このファイルを直接見る他に

$ git config --global --list

とする。ここでの設定値はグローバルだが、リポジトリごとにローカルな設定をすることもできる。

ローカルに Git リポジトリを作成する

リポジトリを作成したいプロジェクト myproject があるなら、

$ mkdir myproject
$ cd myproject
$ git init
$ ls -la
drwxr-xr-x  3 kazu  admin   96 Nov  8 10:13 .
drwxr-xr-x  5 kazu  admin  160 Nov  8 10:10 ..
drwxr-xr-x  9 kazu  admin  288 Nov  8 10:13 .git

.git ディレクトリがリポジトリとなり、リポジトリのあらゆるメタデータが格納される。myproject は空でもいいし、すでにプロジェクトのファイルがあってもよい。

リポジトリにローカルな設定をする

リポジトリにローカルな設定は、.git/config ファイルに記述されている。git init でリポジトリを作成した時点でいくつかの値がデフォルトで設定されており、

$ git config --local --list

で確認できる。このリポジトリが会社の仕事で使うリポジトリで、user.email のグローバルな設定値が個人用アドレスになっているのなら、ローカルに会社用アドレスで設定したいだろう。その場合はこうする。

$ git config --local user.email "tomita@***.jp

このリポジトリでのみ無視したいファイルは、myproject/.gitignore ファイルに記述する。また、自分の環境でのみ生成されてしまうような無視すべきファイルは、.git/info/exclude ファイルに記述する。

作業ツリーとステージングエリアとリポジトリ

作業ツリーとはリポジトリの現在の見た目のこと。ここでは myproject ディレクトリをルートとするツリーのこと。この作業ツリーを編集、つまりファイルの作成、更新等をしていく。

ステージングエリアとは、作業ツリーにおける変更の差分をバッファする場所のこと。ステージングエリアの内容をリポジトリへとコミットする。一つ一つのコミットは連なっていき、コミットを追跡することができる。

ステージングエリアへの追加

$ git add newfile modifiedfile       # 新規作成されたファイル newfile と、前にステージングあるいはコミット後に更新したファイル modifiedfile の更新分をステージング
$ git add .    # カレントディレクトリ以下の全更新分(新規作成ファイルも含む)をステージング

somefile を更新し、それをステージングし、その後でさらに somefile を更新してステージングすると、前のステージング内容は上書きされる。

リポジトリへのコミット

$ git commit -m "Create an empty index.html"                 # ステージングエリアの内容をコミット
$ git commit -m "コミットログ" -a                                # 作業ツリーの変更分をステージングし、同時にコミット。ただし、新規作成ファイルは除く。
$ git commit -m "コミットログ" somefile_1 sub_dir/somefile_2     # 指定したファイルをステージングし、同時にコミット

-m オプションで指定するログは、大文字で開始し、最後のピリオドは省略し、50文字以内にするのが慣習になっている。-m を指定しなければ、エディタが起動するので、そこでログメッセージを書く。1行目の後に空行を入れ、それ以降の行は72文字以内にするのが慣習である。こうすることで、git log コマンドでログが見やすくなる。

作業ツリーとステージングエリアの状態を見る

$ git status

ステージングエリアに何が追加されたか? 作業ツリー内のどのファイルが更新されたか? 作業ツリー内に新規作成されたファイルがあるか? を確認できる。なお、Git はディレクトリは追跡しないので、空のディレクトリは無視される。

差分を確認する

$ git diff             # ステージングエリアに対する作業ツリーの差分
$ git diff --cached    # 現在のブランチの HEAD(直近のコミット)に対するステージングエリアの差分
$ git diff HEAD        # 現在のブランチの HEAD に対する作業ツリーの差分
$ git diff HEAD^       # 現在のブランチの HEAD の1つ前のコミットと作業ツリーとの差分

コミットの一覧を表示する

$ git log                   # HEAD から順に各コミットのハッシュ値、作成者、日時、ログメッセージを表示する
$ git log --oneline         # 各コミットごとに、そのハッシュ値とログメッセージの最初の行を含む1行を表示する
$ git log -n3               # HEAD から3つのコミットだけ表示する
$ git log -n3 --oneline

ファイルの削除

Commit 済みの somefile というファイルがあるとする。これを削除してコミットするにはこうする。

$ rm somefile
$ git add somefile            # または git rm somefile
$ git commit -m "Remove somefile"

もしくはこうする。

$ git rm somefile           # rm somefile と git add somefile を一括して実行する。
$ git commit -m "Remove somefile"

ファイルの移動

コミット済みの somefile_1 を somefile_2 へ移動する。まずは面倒くさい方法から。git status で確認しながらすすめる。

$ mv somefile_1 somefile2
$ git status
On branch master
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	deleted:    somefile_1

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	somefile_2

no changes added to commit (use "git add" and/or "git commit -a")

$ git add somefile_1 somefile_2
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	renamed:    somefile_1 -> somefile_2
 
$ git commit -m "Rename somefile_1 to somefile_2"

git mv コマンドが上の操作のショートカットだ。

$ git mv somefile_1 somefile_2
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	renamed:    somefile_1 -> somefile_2

$ git commit -m "Rename somefile_1 to somefile_2"

ブランチ

ブランチとは、コミットが連なったシーケンスの末端(直近のコミット)を指すポインタのこと。

branch_1

左図は最初のコミットA をつくった後の状態の master ブランチ。右図は、コミット A の後に B, C をコミットした後の master ブランチ。

$ git branch
* master

このようにリポジトリ内のブランチを表示できる。カレントブランチには * マークがつく。

直近のコミットの修正

リポジトリが下図のような状態だとする。

branch_9

コミット C の後で些細な間違いやコミットログのみ修正したいようなとき、新たなコミットを追加するのではなく、最新のコミット C を修正して置き換えたい。そのようなとき、--amend オプションを使う。

$ git commit --amend -m "New comment"   # 新しいログをつけて直近のコミットを修正する
$ git commit --amend --no-edit          # ログメッセージを変更せずに直近のコミットを修正する
$ git commit --amend -c HEAD            # 直近のログメッセージが入力されているエディタを開く
$ git commit --amend -c HEAD^           # 直近の1つ前のコミットのログメッセージを使ってエディタを開く

上のいずれかのコマンドを実行後、リポジトリは次の状態になる。

branch_10

コミットのハッシュ値は変化する。

ブランチの作成とチェックアウト

ブランチを新たに作成するには、git branch コマンドを使う。今、master ブランチにいるとして

$ git branch alternate master       # カレントブランチが作成元なので master は省略できる。

とすると、次の図のように alternate ブランチができる。

branch_2

いま、master ブランチにいるので、作業ツリーも当然 master ブランチに対応している。これを別のブランチ alternate に変更するには、alternate をチェックアウトする。

$ git checkout alternate

チェックアウトすると、作業ツリーは alternate に切り替わる。ブランチの作成とチェックアウトを同時に実行するには、今 master ブランチにいるとして

$ git checkout -b alternate master      # カレントブランチが作成元なので master は省略できる。

とする。

ファイルの復旧

作業ツリー内のファイルを誤って削除してしまったり、ファイルへの変更を取り消したい場合、git checkout コマンドを利用できる。たとえば、作業ツリーにある index.html ファイルを間違って削除してしまったり、間違った変更をしてしまい元に戻せなくなったとする。こういう場合、次のようにする。

$ git checkout -- index.html

こうすると、作業ツリーの index.html がステージングエリアでのバージョンの index.html に戻すことにより復旧できる。しかし、index.html を削除し、それをステージングしてしまった後だとうまくいかない。

$ git rm index.html
$ git checkout -- index.html    # うまくいかない
error: pathspec 'index.html' did not match any file(s) known to git

失敗する理由は、git rm コマンドにより、ファイルの削除と同時にその変更がステージングされてしまっているからだ。この場合、ステージングでのバージョンに戻そうとしてもうまくいかなくなる。こういうときは、git restore --staged コマンドを使って index.html ファイルの削除のステージングを解除してから git checkout すればよい。

$ git status    # index.html の削除がステージングされていることを確認
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	deleted:    index.html

$ git restore --staged index.html

$ git status      # 削除のステージングが解除されたことを確認。
On branch master
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	deleted:    index.html

no changes added to commit (use "git add" and/or "git commit -a")

$ git checkout -- index.html

これで index.html は元に戻る。

変更を巻き戻す

branch_9

コミット C を取り消して B に戻したいとする。これには git revert コマンドを使う。

$ git revert --no-edit HEAD     # --no-edit により、Revert したことを示すログメッセージを自動でつけてくれる。

こうすると次の状態になる。

branch_11

コミット B' は B の C に対する差分をコミットしたもの。その結果 B' と B は内容が同じになる。

複数のコミットを巻き戻す場合、その都度 git revert すると、その度に新たなコミットが追加されてしまうので、-n オプションを使ってステージング(と作業ツリーに)だけ実行し、一つにまとめたほうがよい。最初の図で B と C の両方を取り消す場合には次のようにする。

$ git revert -n HEAD
$ git revert -n HEAD^
$ git commit --no-edit

この結果、リポジトリは次の状態になる。

branch_12

ここでは、まず、B の C に対する差分をステージングし、次に A の B に対する差分をステージングし、最後にコミットし A' を作っている。その結果、A と A' の内容は等しくなる。

変更のリセット

変更を巻き戻すのではなく、コミットそのものを消してしまいたいときは git reset コマンドを使う。たとえば、下のようなリポジトリに置いて、コミット B と C を取り消したいとする。

branch_9

この場合、次のようにする。

$ git reset HEAD^^    # 直近のコミットの2つ前のコミットまでリセットする。

リポジトリは下の状態になる。

branch_13

インデックスは reset 後のコミットが表すツリー構造と一致するように変更される。作業ツリーはそのままだ。

$ git reset --soft HEAD^^

とすると、インデックスも作業ツリーも変更されない。インデックスも作業ツリーも reset 後のコミットに合致するように変更するなら、

$ git reset --hard HEAD^^

とする。こうすると、コミット A をコミットした直後の状態と同じになる。

ブランチのマージ(Fast-forward マージ)

リポジトリの状態が

branch_2

だとして、いま、alternate ブランチにいるとする。このとき、D, E コミットをしたとすると、次の図のようになる。

branch_3

ここで、master ブランチへ alternate ブランチの変更をマージするには、master をチェックアウトして git merge コマンドを使う。

$ git checkout master
$ git merge alternate

実行後の状態は次の図のようになる。

branch_4

ここでは master のブランチポインターが前方へ移動しただけなので、Fast-forward マージと呼ぶ。

リポジトリのクローン

共同作業をする場合、協力者は元のリポジトリの複製を作る必要がある。このために git clone を使う。現実的ではないが、 同じホストにオリジナルのリポジトリがあるとして、それの作業ツリーのルートが Cats だとしよう。別のディレクトリ Alice で次のようにする。

$ git clone ../Cats .

こうすると、カレントディレクトリに Cats リポジトリの複製がつくられ、master ブランチが checkout された状態になる。 git clone で作成されたリポジトリには、リモートリポジトリへのポインタとして、元のリポジトリの場所が remote.origin.url 変数に 設定されている。これは、git config --list --local で見ることができる。

remote.origin.url=/Users/kazu/Documents/samples/Git_intro/Alice/../Cats

リモートリポジトリの名前として origin がデフォルトで使われる。

クローンとして作成したリポジトリの作業ツリーでファイルを変更し、それをコミットしたとする。そのとき、git config --local コマンドで user.name, user.email を別の値に設定しておく。この後、元のリポジトリでこの変更を受け入れるには、

$ cd ../Cats
$ git remote add remote-alice ../Alice   # リポジトリのローカル設定に remote.remote-alice.url=../Alice がセットされ、remote-alice がリモートリポジトリの名前になる。
$ git pull remote-alice master   # remote-alice という名前のリモートリポジトリの master ブランチの変更を取得する。

とする。

共有リポジトリを作成する

共同作業をするには、複数の協力者がもつローカルなリポジトリからリモートリポジトリとして共有されるリポジトリを 1つだけ作成する。このリポジトリでは変更作業がおこなわれることはないため、作業ツリーがない。これを Bare リポジトリ という。

Bare リポジトリは次のように作成する。リポジトリ名は、Shared.git とする。Bare リポジトリのディレクトリは、.git で 終わる名前にするのが慣習になっている。

$ mkdir Shared.git
$ cd Shared.git
$ git init --bare

次は、自分自身のリポジトリの内容で共有リポジトリの内容を初期化する。このために、自分のリポジトリに戻って、 共有リポジトリへと Push する。

$ cd ../Cats
$ git remote add origin ../Shared.git  # origin をリモートリポジトリ ../Shared.git への短縮名として使えるように設定
$ git push origin master   # origin リモートリポジトリへ master ブランチをプッシュする

git push コマンドには、リモートリポジトリ名と、対象となるブランチ名を指定する必要がある。ただし、--set-upstream オプションを つければ、次からは指定する必要がなくなる。

これ以降は、共有リポジトリを git clone すればいい。

共同作業

自分のローカルリポジトリで作業し、変更内容を git commit していく。そして、リモートリポジトリと同期するために git pushgit pull していく。git pull とは、リモートリポジトリの内容を取得して、それを自分のブランチにマージすることだ。 git pull する前に自分の作業ツリーの内容は Commit してクリーンな状態にしておく方がよい。ダーティな状態で Pull しても 同じファイルを変更していなければ問題ないが、そうでない場合、警告が出て、中断するので、 git diff origin -- 問題のファイル名 として変更内容を確認し、git stash して自分の変更を退避してから git pull する。その後、git stash pop して退避内容をポップすると、変更がマージされる。その上で、git commit し、git push すればよい。

演習 - 共有リポジトリを使用して共同作業を行う

よりよい方法は、master ブランチでは作業をせず、特定機能の追加用、またはデバッグ用のブランチを作って、そこで作業する。そこでの作業が完了したら、 master ブランチへとマージし(マージの前に git pullmaster ブランチを最新の状態にする)、リモートリポジトリへとプッシュする。

ブランチのマージ

Fast-forward では済まない場合もある。リポジトリの状態が下図のようになっており、master ブランチにいるとする。

branch_5

ここで、alternate ブランチをマージするために

$ git merge alternate --no-edit

とすると、リポジトリは次のような状態になる。

branch_6

新しいコミットは H であり、git merge したときに --no-edit オプションをつけたので、Merge branch 'alternate' というログメッセージが自動で つけられている。このとき、master ブランチで git log コマンドを実行すると、D も E も履歴に表示される。もし、A, B, C, F, G, D, E という時間順で コミットされたのなら、git log は新しい順に表示されるので、H, E, D, G, F, C, B, A の順で表示される。ただし、コミット H の前のコミットは、G であり、 HEAD^ はコミット G を指している。

コミット D, E を履歴として残したくないなら圧縮コミットを使う。

$ git merge --squash alternate

これを実行すると D, E をマージした変更分がステージングエリアに追加されて終了する。後は、コミットすれば上の図のような状態になるが、git log しても D, E は表示されなくなる。

マージの競合

git merge コマンドで競合が発生すると、競合を起こしたファイルに競合箇所が指摘されてマージが停止する。たとえば、さっきの例で masteralternate ブランチでも同じ index.html の同じ箇所を編集してしまっていたため、自動マージに失敗したとする。この場合、こうなる。

$ git merge alternate --no-edit

Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

競合を起こした index.html には、競合を起こしている箇所に特殊な書式が設定されている。これをエディタで編集して 手動で競合を解消する。その上で

$ git add index.html     # 競合が解消したことを git に伝える
$ git commit -m "Resolved conflict"

とすれば、自動マージが成功した場合と同じブランチの状態になる。もしくは、git merge --abortgit reset --hard HEAD とすれば マージを実行する前の状態にもどる。

マージの競合を解決する

ブランチの削除

$ git branch -d alternate     # alternate ブランチがマージされていないと失敗する
$ git branch -D alternate     # alternate ブランチを強制的に削除する

ブランチの Rename

$ git branch -m alternate new        # new ブランチがすでにあると失敗する
$ git branch -M alternate new        # new ブランチがすでにあるとそれを上書きしてしまう!

バージョン間の差分を見る

git diff コマンドは、バージョン間の差分を取るのにも使える。比較対象は同じブランチでなくていい。

$ git diff 18f822e       # コミット名 18f822e に対する作業ツリーの差分を表示。両者は同じブランチでなくていい。
$ git diff 1.0 HEAD      # タグ 1.0 に対する直近のコミットの差分を表示
$ git diff --stat 1.0    # タグ 1.0 に対する作業ツリーの差分の統計情報を表示

ファイルの各行のコミット情報を見る

ファイルの行ごとに、コミット名、作成者、コミット日時を確認するには git blame コマンドを使う。

$ git blame hello.html
$ git blame -L 12,13 hello.html   # 12行目と13行目に対してのみ表示する。
$ git blame -L 12,+2 hello.html   # 上と同じ
$ git blame -L 12,-2 hello.html   # 11行目と12行目に対してのみ表示する。
$ git blame -L 12,13 18f822e^^ -- hello.html   # 18f822e の2つ前のコミットにおける hello.html の12行目と13行目に対してのみ表示する。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment