新しいことにはウェルカム

技術 | 電子工作 | ガジェット | ゲーム のメモ書き

理屈で覚えて楽をする。Gitの使い方メモ

Gitは難しいです…。対処療法的なチートシート作ればやっていけるかなと思ったのですがダメでした…。

何かトラブルがあると、どうしていいか分からす、お手上げになってしまいます。

同じことをするのにも、コマンドをまたいで書き方が複数あったりするのですが、かえってそのコマンド本来の用途が分からなくなって混乱してしまいます。

そこで、九九を覚えなくても足し算だけで掛け算ができるように、多少手数は多くなっても、基本的なコマンドだけ覚えておけば、後は理屈で乗り切れるようなGitの使い方を、メモしておこうと思います。

勘違いしてたけど重要だったこと

下記の重要な点で勘違いしていて、それがネックでハマっていました。反省を踏まえ、それらの勘違いポイントを中心にまとめていこうと思います。

  • Indexはスナップショット
  • コミットは消えていない
  • ブランチはどこにでも移動できる
  • コンフリクトは印がついている

Gitの基本的な作業の流れ

あるコミットの状態からスタートして、次のコミットするまでの流れは、下記のようになります。

  1. まず、最新のコミットにいる
  2. いくつかのファイルを編集する
  3. 編集したファイルをadd <file>で「Index」に登録する
  4. commitで「Index」に登録されたものを、コミットとして残す

この一連の流れを繰り返していくことにより、更新をコミット履歴として記録していきます。

Indexはスナップショット

自分が勝手に勘違いしていたことなのですが、上記のフローだけを見て、Indexに登録されるのは編集のあったファイルのみだと思い込んでいました。

しかし実際は、Indexはリポジトリ全体のスナップショットで、add <file>をすると、Indexのスナップショットの中の該当ファイルが、編集したファイルに変更されます。

そしてコミットすると、Indexのスナップショットが、新しいコミットとして登録されます。

つまり、Indexもコミットも、リポジトリ全体のスナップショットで、Indexは次のコミットの組み立て工場のような役目になります。

Gitの3つの構成メンバー

Gitは下記の3つの状態の操作で成り立っています。

  • 現在起点となるコミットを示す「HEAD」
  • 現在のディレクトリを示す「Working」
  • 登録するコミットになる予定の状態を示す「Index」

理屈で覚えて楽をする。Gitの使い方メモ

  • 「Commit」は便器上図に記載しましたが、コミットした時点で次の「HEAD」になるので、「HEAD」と同じものを指しています
  • 「Working」はその名の通り、今作業しているディレクトリの状態が「Working」です

差分ではなくスナップショット

全ての状態は、差分ではなく、スナップショットとして表現されています。

(Gitのドキュメントに書いてあるのですが、その意味がよく分かっていませんでした…)

下記を例に、3つの状態変化を見てみます。

  • HEADがコミット「commit_01」にいるとして、「commit_01」には「test_01.txt」「test_02.txt」「test_03.txt」の3つのファイルが登録されていたとします
  • 「test_01.txt」を編集して「test_01.txt(v1)」になり、それをコミットして「commit_02」を作ったとします

スタート

理屈で覚えて楽をする。Gitの使い方メモ

HEAD・Working・Index全てが同じ状態です。

checkout <branch>でブランチを移動した時、ディレクトリのファイルも移動したブランチのものに書き換わるのですが、まさにその時「WorkingがHEADの状態に書き換わる」が起こっています。

そして、表から見ることはできないのですが、Indexも同様に、HEADの状態に書き換わっています。

(前述のとおり、Indexは更新ファイルのみが登録され、最初は空っぽなのだと勘違いしていました…)

ファイル変更

理屈で覚えて楽をする。Gitの使い方メモ

手元のディレクトリのファイルを編集したので、Workingだけが更新されています。

git add

理屈で覚えて楽をする。Gitの使い方メモ

add <file>で、更新したWorkingファイルをIndexに追加します。

Indexは全体を示すスナップショットなので、例えば編集されていないファイル「test_02.txt」をadd test_02.txtしても、状態が変わらないので結果としてIndexの変化はありません。

コミット

理屈で覚えて楽をする。Gitの使い方メモ

コミットすると、Indexのスナップショットで、新しいコミットが作成されます。

そして、HEADの参照先が、その新しいコミットに切り替わります。

以上が、Gitが、あるコミットの状態からスタートして、次のコミットするまでの流れです。

注意

コミットしても、Workingは、新しいHEADと同じ状態に書き換わりませんcheckout <branch>の時と異なるので注意が必要です。

なので、Indexの更新後、コミットする前に、再度「test_01.txt(v1)」を編集して「test_01.txt(v2)」にしたとして、その状態でコミットすると、下記のように、Workingは編集されたままになります。

理屈で覚えて楽をする。Gitの使い方メモ

Git順方向まとめ

一旦ここまでの操作を整理します。

add

  • add <path>は、Workingの指定したパスの状態を、Indexに適用するコマンドです
  • addはファイルの追加だけでなく、Workingのファイルが削除されてた場合、Indexからの削除も行ってくれます
  • パスはファイル直指定だけでなく、ディレクトリや「*」「.」で指定して再帰的に行うこともできます
    • WorkingとIndexを同じ状態にするには、add *で行えます

rm

  • rm <path>は、Workingからファイルを削除し、かつ、Indexからも削除するコマンドです
  • Indexからのみ削除するには、rm --cached <path>とします
    • Workingにファイルを残しつつ、リポジトリから除外(Untracked)する時に使います
  • Workingからのみ削除するのは、シェルの/bin/rm <path>のことなので、Gitコマンドはありません

commit

  • commitは、Indexをコミットとして登録し、HEADを登録したコミットに移動するコマンドです

なので、ファイル編集からコミットまでの順方向で、代用不可で覚える必要のあるコマンドは下記の3つになります。

  • add <path>
  • rm --cached <path>
  • commit

git rm <path>は、「bin/rm <path>+git add <path>」または、「bin/rm <path>+git rm --cached <path>」で代用可能です

コミットは消えていない

Gitに対して、下手にコミットやマージをして、元に戻せなくなってしまうんじゃないかという、恐怖心がありました。

デフォルトの設定では、コミットログは現在いるブランチのみ表示となっています。

実はその、現ブランチのコミットしか見せないという親切設定が、逆に恐怖心を生む原因の一つなのかなと思っています。

例)全体像が見えない

例えばブランチ「b1」にいる時の、デフォルトのログ表示範囲は下記になります。

git log --oneline --graph
* 9852770 (HEAD -> b1) .
* b3da73b .
* cd58e8e .

全てのブランチを表示するようにすると、下記のようになります。

git log --oneline --graph --all
* 9852770 (HEAD -> b1) .
* b3da73b .
| * d818e15 (b2) .
| * 0ed10e8 .
|/
* cd58e8e .

全体図が見えて、今自分がいる場所が把握できれば、不安は少なくなります!

例)コミットが消える

rebase <branch>という、今いるブランチを、指定したブランチの後ろに持っていくコマンドがあります。

前述の例で、ブランチ「b1」を、ブランチ「b2」の後ろに持っていった結果は下記になります。

git branch b2
* a1791c1 (HEAD -> b1) .
* 9266f2a .
* d818e15 (b2) .
* 0ed10e8 .
* cd58e8e .

ブランチ「b1」が、ブランチ「b2」の後ろに移動しています。

と同時に、ブランチ「b1」がいた経路のコミットが消えてしまっています!!

しかしこれは、デフォルトの設定が見えなくしているだけで、「--reflog」オプションをつけると見ることができます。

git log --oneline --graph --all --reflog
* a1791c1 (HEAD -> b1) .
* 9266f2a .
* d818e15 (b2) .
* 0ed10e8 .
| * 9852770 .
| * b3da73b .
|/
* cd58e8e .

なので、間違えてrebaseして元に戻したいといった時に、元のコミットが消えてしまったと慌てる必要はなく、ブランチ「b1」を元のコミットに移動しなおせば済みます。

詳しくは後述しますが、reset --hard 9852770で、今いるブランチ「b1」を元のコミットに移動できるので、移動した結果は下記の通りになります。

git log --oneline --graph --all --reflog
* a1791c1 .
* 9266f2a .
* d818e15 (b2) .
* 0ed10e8 .
| * 9852770 (HEAD -> b1) .
| * b3da73b .
|/
* cd58e8e .

無事元に戻っています!

面白いことに、rebaseした時にできたコミット「9266f2a」「a1791c1」も残っています。

実は、Gitは全てのコミットが残っています!!

なので、どんなコミットにでも戻すことができるので、ブランチの移動方法さえ知っていれば、慌てず恐れずコミットができるのです。

注意点

Gitは、どのブランチにも属さないコミットを、不要なコミットとして削除します。例えば、前の例の残ったコミット「9266f2a」「a1791c1」などです。

ただし、コミットの保持期間はデフォルトで90日なので、すぐに削除されるような心配はありません。

ブランチ

ブランチとは?

  • ブランチとは、コミットの位置を示す参照(ポインター)です
  • ブランチ1つにつき、1つのコミットの場所を指します
  • HEADは、今いるコミットを示す、特殊なブランチとも言えます

checkout

  • checkoutとは、HEADを指定したブランチ・コミットに移動するコマンドです
  • checkoutすると、HEADを指定したブランチ・コミットに移動して、「Working」「Index」をHEADと同じに書き換えます
  • 移動先をブランチで指定した場合と、コミットで指定した場合で、挙動が異なるので注意が必要です

移動先をブランチで指定した場合

  1. HEADを指定したブランチに移動します
  2. 「Working」と「Index」をHEADと同じ内容に書き換えます
  3. HEADとブランチが紐づけられます
  4. コミットすると、HEADだけでなく、ブランチも新しいコミットに移動します

移動先をコミットで指定した場合

  1. HEADを指定したコミットに移動します
  2. 「Working」と「Index」をHEADと同じ内容に書き換えます
  3. HEADとブランチの紐づけが外れます
  4. コミットすると、HEADだけが新しいコミットに移動します

例)ブランチ「b2」に移動して、そこで何かコミットした場合

移動前

* 9852770 (HEAD -> b1) .
* b3da73b .
| * d818e15 (b2) .
| * 0ed10e8 .
|/
* cd58e8e .

移動後

git checkout b2
* 9852770 (b1) .
* b3da73b .
| * d818e15 (HEAD -> b2) .
| * 0ed10e8 .
|/
* cd58e8e .

「HEAD -> b2」と、HEADがブランチに紐付いています。

コミット後

* 971d696 (HEAD -> b2) .
* d818e15 .
* 0ed10e8 .
| * 9852770 (b1) .
| * b3da73b .
|/
* cd58e8e .

HEADがブランチ「b2」に紐付いているので、コミットするとHEADとb2共に、新しいコミットに移動しています。

例)コミットに移動して、そこで何かコミットした場合

移動前

* 9852770 (HEAD -> b1) .
* b3da73b .
| * d818e15 (b2) .
| * 0ed10e8 .
|/
* cd58e8e .

移動後

git checkout d818e15
* 9852770 (b1) .
* b3da73b .
| * d818e15 (HEAD, b2) .
| * 0ed10e8 .
|/
* cd58e8e .

HEADは「b2」と同じ位置にいますが、「HEAD, b2」と、HEADはブランチには紐付いておらず、独立してその場にいます

コミット後

* f6525a1 (HEAD) .
* d818e15 (b2) .
* 0ed10e8 .
| * 9852770 (b1) .
| * b3da73b .
|/
* cd58e8e .

HEADがブランチに紐付いていないので、コミットしても「b2」の位置はそのままで、HEADのみが新しいコミット位置に移動しています。

reset

resetcheckout同様、HEADを指定したブランチ・コミットに移動するコマンドですが、checkoutと挙動が大きく異なっています。

resetの基本

  • resetとは、HEADを指定したブランチ・コミットに移動するコマンドです
  • checkoutと異なり、移動前にHEADがブランチと紐付いていた場合、紐付いたブランチも移動先に移動します
  • HEADがブランチと紐付いていなかった場合、HEADのみが移動先に移動します

resetのオプション別挙動

resetにはオプション「--soft」「(なし=デフォルト)」「--hard」があり、どれも超重要です。

オプション

  • 「--soft」
    • HEADのみ移動し、「Working」「Index」は書き換えません
  • 「(なし=デフォルト)」
    • HEADが移動し、「Index」をHEADで書き換えます
  • 「--hard」
    • HEADが移動し、「Working」「Index」をHEADで書き換えます

resetcheckoutに似ていますが

  • resetはブランチを移動させる
  • checkoutはブランチを切り替える

という点が大きく異なります。

reset・checkoutでパスを指定した場合

resetcheckoutは共に、引数にパスを渡せますが、渡さなかった時とは別の挙動になるので、覚えておく必要があります。

resetでパスを指定した場合

resetはオプションが「(なし=デフォルト)」の時だけ、引数にパスを指定することができます。

その場合、パスを指定しなかった場合と違い、HEAD・ブランチの移動は行われず、指定したパスの「Index」の書き換えのみが行われます。

「--soft」は何もしないのと同じなのでコマンドがありません。 「--hard」は後述のcheckoutでパスを指定した時と同じ挙動なので、これもコマンドがありません。

checkoutでパスを指定した場合

checkoutでパスを指定した場合、reset同様、HEADの移動は行われず、指定したパスの「Working」「Index」の書き換えのみが行われます。

resetcheckoutでのパス指定は、他のブランチ・コミットからファイルを持ってきたい時などに使われます。

checkoutの注意点

checkoutは、「Working」と「Index」の内容が、HEADと違うとエラーが出て、移動できません。

これは、「Working」と「Index」の内容がHEADと違うということは、何かしらの変更が行われているとうことなので、移動によって変更が失われないよう、安全装置としてcheckoutが実行できない仕様になっています。

resetの用途

resetは原始的なコマンドですが、組み合わせで様々なことができます。

ただ、resetの仕様を覚えておけば、1つ1つ覚える必要はありません。

例)今の編集を破棄して最初に戻す

方針

「Working」と「Index」を、最初の状態「HEAD」と同じにします。

git reset --hard HEAD

例)コミットを取り消す

方針

1つ前のコミットにブランチを移動させ、「Working」「Index」をHEADと同じにします。

git reset --hard HEAD^

例)途中のコミットをまとめる

方針

今の「Index」を保持したまま、遡りたい所に移動して、コミットします。

git reset --soft <遡りたいCommit>
git commit

reset・checkoutまとめ

resetcheckoutは重要なコマンドで、オプション・パスが付いた時の挙動全て、覚えておく必要があります。

コマンド 備考 Working書換 Index書換
reset --soft <br/cm> ブランチ Move なし なし
reset <br/cm> ブランチ Move なし あり
reset --hard <br/cm> ブランチ Move あり あり
checkout <br/cm> ブランチ Switch あり あり
reset <br/cm> <path> HEAD移動なし・ファイルのみ なし あり
checkout <br/cm> <path> HEAD移動なし・ファイルのみ あり あり

Gitは目的作業毎にコマンドを覚えるのではなく、 目標の状態にするには、「HEAD」「Working」「Index」「ブランチ」「コミット」をどういう値にしてコミットすればいいかを考え、resetcheckoutを組み合わせてその値に持って行きコミットしていく、パズルの様なものとも言えます。

resetcheckoutを使えば、任意のブランチを任意のコミットに移動させることが可能です!

失敗コミットも含め、全てのコミットは残っているので、前述の全てのコミット表示と合わせて使えば、どこからでもやり直すことができるので安心です。

mergeのコンフリクトは印がついている

mergeとは?

mergeとは、HEADのコミットに、別のコミットの状態を取り込んで、マージされた新しいコミットを作るコマンドです。

git merge <branch|commit>

merge挙動

  • 取り込んだ際、コンフリクトが起こらなければ、取り込んで新しくできた状態でコミットをしてくれます
  • 取り込んだ際、コンフリクトが起これば、下記を行います
    • コンフリクトしたファイルを、マージ未解決ファイルとして「Index」に登録
    • コンフリクトしたファイルに、競合した内容を追記
    • 更新されていて、かつ、競合しなかったファイルは「Index」に登録

コンフリクト時の対応

マージしてコンフリクトすると、status -sで状態を見た時に、「Index」のコンフリクトしたファイルに、マージ未解決マークが付いて赤くなります。

git status -s
UU test_01.txt
UD test_02.txt
R  test_00.txt -> test_03.txt
A  test_05.txt

見方

  • 左から順に「Index」「Working」の状態を表しています
  • 表示意味
    • 「U」:マージ未解決
    • 「A」:ファイル追加
    • 「D」:ファイル削除
    • 「M」:ファイル更新
    • 「R」:ファイルリネーム
    • 「?」:Untrackedファイル(これも赤文字ですがマージ未解決ではありません)

コンフリクトを解消してコミットする場合

「Index」にマージ未解決ファイル(赤文字ファイル)がある限り、コミットができません。

なので、addでマージ未解決マークを全て更新していき、全てのマージ未解決マークを消せて、晴れてコミットができます。

マージできたファイルは既に「Index」に登録されているので、マージ未解決マークのファイルのみaddするでOKです。

(初めのころ、Gitはどうしてコンフリクトを解消したかを知っているのか謎だったのですが、「Index」に書かれてました…。合理的だなぁ)

マージをあきらめて元に戻す場合

コンフリクトすると、HEADはマージ前のコミット位置のままで、「Working」「Index」が中途半端にマージされた状態で、コンフリクトが解消されるのを待っています。

なので、マージをあきらめて元に戻すには、「Working」「Index」をマージ前の状態、つまり、HEADと同じに書き換えると戻ります。

git reset --hard HEAD

リモートリポジトリ・リモートブランチ

Gitは個人でローカルで使うだけでなく、サーバーにリポジトリを置いて、複数の人でそのリポジトリを更新していくという使い方もできます。

リモートリポジトリ更新フロー

サーバーの内容を更新するのは下記の様なフローになります。

  1. まずはじめに、サーバーのリポジトリをローカルにコピーしてくる(もしくはローカルリポジトリをサーバーにアップ)
  2. ローカルで任意に更新・コミット
  3. ローカルでの更新内容を、サーバーにアップ

2、3を繰り返して、サーバーを更新していきます。

リモートリポジトリ更新対象

ローカルでの更新内容全てが、サーバーにアップされるかというと、そうではありません。

リモートリポジトリには、任意のブランチが登録されていて、それをリモートブランチと呼び、 そのリモートブランチを更新していくことにより、サーバーのリポジトリが更新されていきます。

リモートリポジトリのブランチが、ローカルリポジトリのどこにあるかは、logの「remote/branch」表示で分かります。

* 51f2962 (HEAD -> dev) .
* fca6cc4 (origin/dev) .
| * c8bca3d (master) .
|/
* ccdf7f8 (origin/master, origin/HEAD) Initial commit

リモートリポジトリには「master」「dev」ブランチがあって、ローカルのリポジトリでいうところの「origin/master」「origin/dev」の位置まで進んでいます。

リモートブランチの最新状態取得

リモートリポジトリは、他の人も更新するので、ローカルに持ってきた後に、コミットが追加されてブランチの位置が移動されることがあります。

なので、fetchで、ローカルをリモートの最新状態にします。

リモート更新

ローカルの任意のコミットで、リモートリポジトリのブランチを更新することができます。

しかし、リモートリポジトリは、他の人も使うものなので、安易に任意のコミットから更新すると大変危険です。

なので、通常は「追跡ブランチ」を設定して、その追跡ブランチからリモートを更新するようにしてミスを防ぎます。

リモート追跡ブランチ・追跡ブランチ

リモート追跡ブランチ

リモート追跡ブランチとは、リモートのブランチが、今ローカルのどのコミットにあるのかを示すブランチで、「remote/branch」と表示されます。

あくまでリモートのブランチがどこかを示している印なので、fetchで更新しない限り移動しません。

追跡ブランチ

ローカルブランチは、任意のリモート追跡ブランチと紐づけることができます。

リモート追跡ブランチとの紐づけ設定がされているローカルブランチを、「追跡ブランチ」と呼びます。

確認方法

下記コマンドで、どのブランチが、どこのリモートの追跡ブランチになっているかが分かります。

git branch --all -vv
  b1                    51f2962 .
  b2                    51f2962 .
  dev                   51f2962 [origin/dev: ahead 1] .
* master                c8bca3d [origin/master: ahead 1] .
  remotes/origin/HEAD   -> origin/master
  remotes/origin/dev    fca6cc4 .
  remotes/origin/master ccdf7f8 Initial commit
  • 全てのブランチが表示されます
  • リモートブランチは「remotes/[remote]/[branch]」と表示されます
  • 追跡ブランチは「[[remote]/[branch]]」と表示され、どのリモートブランチと紐付いているかが表示されます

追跡ブランチを使うメリット

  • HEADが追跡ブランチにいる状態でpushすると、そのブランチが指しているリモートブランチに対して、コミットがpushされます。
  • HEADが追跡ブランチにいる状態でmergeすると、そのブランチが指しているリモートブランチのコミットがmergeされます。

ただし、ブランチ名がローカルとリモートで同じでないとダメなので注意が必要です。

ブランチ名を揃えられない場合は、仕方ないので

  • push <remote> <local_branch>:<remote_branch>

で直接pushします。

revert

下記記事の「ほんとうは怖いリベース」にある通り、リモートブランチでは、一度行ったコミットを取り消してはいけません。

https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E6%A9%9F%E8%83%BD-%E3%83%AA%E3%83%99%E3%83%BC%E3%82%B9

なので、既にアップしてしまったコミットを取り消すには、それを打ち消すコミットをしてアップすることにより、間接的にコミット取り消しを行います。

その、打ち消しコミットを作るのがrevertです。打ち消したいコミットを指定して使います。

git revert <commit>

コンフリクトが発生した場合の対処方法は、マージの時と同じです。

マージのrevert

打ち消したいコミットが、マージコミットだった場合は、どちらのコミットを残すかを「-m」オプションで指定します。

git revert -m <1|2> <commit>

打ち消したいマージコミットをlog <commit>で表示した際、「Merge:」に2つのコミットが表示されるので、残したいコミットを1番目・2番目で、「-m」オプションで指定します。

commit 68723fcb7803e9b16cc359a29db641cd65b3b1bd
Merge: 96b9a7f cd6e138

コンフリクトが発生した場合の対処方法は、マージの時と同じです。

マージのrevertのrevert

revertは、間接的にコミットを取り消すだけで、本当に過去のコミットをなかったことにしているわけではあり ません。

つまり、マージのrevertを行っても、マージはされているので、マージのrevertをした後で、もう一度revertしたマージをし直したい場合は、マージをし直すのではなく、「マージのrevertのrevert」を行います。(ややこしい…)

作業の一時退避

作業中の状態を一時保存して、別の作業をしたい時があります。

そんな時は、stashを使います。

一時退避

git stash

とすると、「Working」「Index」の状態が別の場所に保存され、「Working」「Index」がHEADと同じ状態になります。

stashは、どこでも・何度でも行え、保存はスタックしていきます。

stash一覧表示

これまで行ったstash一覧は、下記で見ることができます。

git stash list
stash@{0}: WIP on b2: 96b9a7f .
stash@{1}: WIP on b1: 8a96143 .

stash場所確認

コミットログを見ると、stashをした場所からコミットが伸びているので、どこで行ったstashなのかが分かります。stashした後でブランチを移動しても大丈夫です。

*   7e3e31d (refs/stash) WIP on b2: 96b9a7f .
|\
| * 53f5622 index on b2: 96b9a7f .
|/
* 96b9a7f (HEAD -> b2) .
| *   cb4b54f WIP on b1: 8a96143 .
| |\
| | * c64b84e index on b1: 8a96143 .
| |/
| * 8a96143 (b1) .
| * bb5c8e8 .
| * cd6e138 .
|/
* 51f2962 .

stash復元

保存したものを元に戻すには、stashをした所にcheckoutして、そこで

git stash apply stash@{x} --index

とすると、「Working」「Index」の状態が復元されます。(「x」はstash listで表示される値)

stash自体は1コミットに過ぎないので、任意のコミットとして使うこともできます。

stash削除

スタックしたstashを削除は下記で行います。

git stash drop stash@{x}

最後に

長くなりましたが、以上が、Gitのざっくりとした使い方メモになります。

最後に自分用にGitチートシートを記します。

Gitチートシート

# Index
git add <file> # Index登録
git add * # Index全て登録
git rm --cached <file> # Index削除

# コミット
git commit -m "<comment>"

# ブランチ
git branch <branch> # 作成
git branch -d <branch> # 削除
git branch -D <branch> # 強制削除

# reset・checkout
## ファイル指定なし
git reset --soft <branch|commit> # Working->X  Index->X
git reset <branch|commit> # Working->X  Index->O
git reset --hard <branch|commit> # Working->O  Index->O
git checkout <branch|commit> # Working->O  Index->O
## ファイル指定あり
git reset <branch|commit> <file> # Working->X  Index->O
git checkout <branch|commit> <file> # Working->O  Index->O

# マージ
git merge <branch|commit>

# リモート
git push <remote> <local_branch>:<remote_branch> # リモートブランチ作成
git push <remote> --delete <branch> # リモートブランチ削除
git branch -u <remote/branch> <branch> # 追跡ブランチ紐づけ
git branch --unset-upstream <branch> # 追跡ブランチ解除
git fetch # リモートブランチ最新取得(該当ブランチ)
git fetch <remote> # リモートブランチ最新取得(全ブランチ)
git push # push(該当ブランチ)
git push <remote> <local_branch>:<remote_branch> # push(フル指定)

# リバート
git revert <commit> # コミット打ち消し
git revert -m x <commit> # マージコミット打ち消し(mはlog <commit>で確認)

# 確認
git status -s # 簡易状態表示
git log --oneline --graph --all --reflog # 全ログ表示
git log -p # ログdiff表示
git log --stat # コミットログファイル名表示
git diff # HEAD <-> Working 間比較
git diff --cached # HEAD <-> Index 間比較
git diff <branch|commit> <branch|commit> # コミット間比較
git branch --all -vv # ブランチ一覧・紐づけ確認

# 一時退避
git stash # 一時退避
git stash list # 一覧表示
git stash apply stash@{x} --index # 復帰
git stash drop stash@{x} # 削除

# ブランチに属さないコミットを全て削除
git reflog expire --expire=now --all && git gc --aggressive --prune=now

感想

覚えるコマンドを少なくしようと思ったのですが、随分長くなってしまいました…。

結局、いっぱい覚えなきゃダメってことですね…。

Gitは

  • 全コミットの俯瞰
  • HEADとWorking・Indexとの比較
  • reset・checkoutを駆使した、HEAD・ブランチ・Working・Indexの書き換え

がキモなのかなぁと感じました。

コミットは必ず残り、後から削除も修正もできないので、失敗して過去のコミットが壊れるようなことはなくてむしろ安心です。

resetは取り上げられることが少ないのですが、一番重要なコマンドじゃないかと思ってます。むしろreset使わずに、どうやってコミットの交通整理するんだろう?

参考記事

https://git-scm.com/book/ja/v2 https://gist.github.com/ktx2207/3167fa69531bdd6b44f1 https://qiita.com/misopeso/items/ede49b661cc7ad30528a https://qiita.com/Iyutaka/items/83264e4377bc398c1463