pyてよn日記

一寸先は闇が人生

Git チートシート

git を使う際に勉強したことを雑に更新していく.大雑把な理解なので間違っている情報が記載されていたらご指摘お願いします.更新は不定期です.

心掛け

  • git add * は使わない.
  • 出来る限りターミナル,コマンドラインgit addgit commit は行わない(やむを得ない場合は仕方ない).SourceTree などの GUI 環境で編集内容を確認しながらコミットを行う.(そうは言ってもコマンドは勉強しておく必要は当然ある)
  • 空白行などに注意して綺麗な diff を心掛ける.diff は必ず GUI 等で確認すること.
  • フォーマッタによるフォーマットと機能追加を一つのコミットで行わない.diff が汚くなる.
  • commit の粒度に気を配る(今の所「一つの機能ごと,かつ正常に動く」を目標にしている.一つの機能の粒度はチームによりけり...).

git の基礎知識

master と origin

  • origin: リポジトリの場所(URL)の別名(default).origin 以外の名前も付けれるが慣例として origin が用いられる.
  • master: ブランチの名前

git pull origin master は,origin という名前のリポジトリの master ブランチから,git pull しろと命令している.


コミット関連

コミット:git commit

「コミット」=「その時点におけるソースコード編集履歴をメッセージ付きで残す」イメージ.

$ git commit -m 'commit message'

コミットの取り消し:git reset

  • 直前のコミットを取消
    • オプション
      • 注意--hard:コミットを取り消した上で,コミット時のソースコードの変更内容は消えてしまう(つまり一つ前のコミットまで全てのファイルの変更内容が戻る).
      • --soft:コミットを取り消すが,コミット時の変更内容はそのまま残る.コミットメッセージやコミットの内容を修正したい時はこちらを実行すると良い.
# 変更内容が全て元に戻ってしまう注意が必要
$ git reset --hard HEAD^

# 変更内容は保存されたまま,'soft' にコミットを取り消す
$ git reset --soft HEAD^
  • コミットの取消:HEAD^HEAD~{n}@^,commit hash

    • (git > v1.8.5,「HEAD」の alias として「@」が実装された)
    • 1 つ前(直前)のコミット
      • HEAD^ = HEAD~{1} = HEAD~(これだけ特殊) = @^
    • 2 つ前のコミット
      • HEAD^^ = HEAD~{2} = @^^
    • 3 つ前のコミット
      • HEAD^^^ = HEAD~{3} = @^^^
    • ...

HEAD^HEAD~{n}@^ の代わりにコミットのハッシュ値を指定しても良い.

$ git reset --hard HEAD^^
$ git reset --soft HEAD~{2}
$ git reset --soft @^^
$ git reset --soft [commit-hash]

コミットを打ち消し(コミットを取り消した履歴を残す):git revert

作業ブランチを,指定したコミット時点の状態まで戻し,コミットを行う.現時点までのコミットを消去するのではなく,逆向きのコミットを行うことで「コミットを打ち消した」という履歴を残す.

git revert [commit-hash]

直前のコミットの上書き

直前のコミットを上書きするには以下を実行.

$ git commit --amend
  • git commit --amend の使いどころ
    • コミットメッセージを変更したいとき
    • git rebase に失敗した時,コンフリクトを避けるためにコミットを上書きするとき

git cherry-pick:他ブランチの特定のコミットをカレントブランチに反映する

git cherry-pick により,他ブランチの特定のコミットを指定し,そのコミットをカレントブランチに反映することができる.コミットはコミットハッシュ等で指定する(後述).

使い所

  • リリース用のブランチにパッチ的な用途で使える
  • 「あのブランチの内容,こっちに持ってきたくね?」

cherry-pick の挙動

git cherry-pick を実行した時の挙動には注意が必要.

  • コンフリクトがない場合
    • 自動でコミットが実行される
      • 指定したコミットのコミットメッセージのまま,カレントブランチにコミットが反映される
      • コミットメッセージを直したかったら git --amend
  • コンフリクトがある場合
    • コミットは実行されない
    • コンフリクトがあるファイルには,お馴染みの「>>>> HEAD」で編集内容が追加される.つまり,コンフリクトを修正しないとコミットできない(大体これ?).

他ブランチの特定の 1 つのコミットを反映する

  • template
$ git cherry-pick [commit hash]

他ブランチの特定のコミットを範囲指定して反映する

  • template

指定したブランチ branch-name の最新のコミットからさかのぼり,number 番目までのコミットをカレントブランチに反映する.

$ git cherry-pick [branch-name]~[number]..[branch-name]
  • ex) develop ブランチの最新のコミットからさかのぼり,5 番目までのコミットをカレントブランチに反映する.
# git cherry-pick [branch-name]~[number]..[branch-name]
$ git cherry-pick develop~5..develop

ブランチ関連

既存のブランチの確認:git branch

$ git branch

-r オプションを付けるだけ.

$ git branch -r

リモートリポジトリのブランチの情報を取得する:git fetch

リモートリポジトリから,ブランチ(とタグ)の情報をローカルリポジトリに取得するためのコマンドである.git fetch は,チーム開発などで他のメンバーがリモートリポジトリを更新した際に,その更新をローカルリポジトリに反映させる場合などに用いる.

  • 注意点
    • 1 つ注意すべき点は,git fetch コマンドはリモートリポジトリの情報をローカルリポジトリに引き出すだけだということ.

git fetch を実行した際,リモートリポジトリでの変更内容がローカルリポジトリソースコードに勝手にマージされたり,作業中の内容を書き換えたりすることはない.従って,必要に応じて自分でマージをする必要がある.

つまり,もしリモートリポジトリの変更をローカルリポジトリに反映させたい場合,「fetch -> merge」という手順を必ず踏まなければならない(pull はこれらを合わせたようなものだが,ここではややこしくなるため触れない).

例えば,下記はリモートリポジトリ origin から,ブランチ master の全てのコミット履歴と参照(=ブランチ・タグ)を取得するコマンドである.取得した履歴に関連するタグがあれば,同時に取得する.

全てのリモートリポジトリの情報を取得する

1 つのローカルリポジトリにつき,複数のリモートリポジトリを登録することができるが,この複数のリモートリポジトリの情報を一挙に取得することができる.

  • template
$ git fetch --all

特定のリモートリポジトリの情報を取得する

  • template
$ git fetch [remote-repository]
  • ex) リモートリポジトリ origin の情報を取得する

単一のリモートリポジトリのみを扱う場合,origin という名前が用いられることが多い(リモートリポジトリを clone したときには,リモートリポジトリ名に origin という名前が自動的に付けられる).

$ git fetch origin

特定のリモートリポジトリの特定のブランチの情報を取得する

  • template
$ git fetch [remote-repository] [branch-name]
  • ex) リモートリポジトリ originmaster ブランチの情報を取得する
$ git fetch origin master

(最近 master というブランチ名 main になるというのが話題になってますね(2020/06/18))

ブランチの切り替え

基本

$ git checkout [branch-name]

ブランチの作成 & 切り替え

以下のコマンドで,一気にブランチの作成 -> 切り替えが行える.

$ git checkout -b [new-branch]

1-liner:リモートのブランチを pull & ブランチの作成 & 切り替え

下記のコマンドにより,新規ブランチの作成,リモートのブランチの pull,ブランチの切り替えを同時に行える.

$ git checkout -b [new-branch-name] origin/[remote-branch-name]

ブランチ名の変更

ローカルリポジトリのブランチ名の変更

ローカルリポジトリのブランチ名を変更する.ブランチ名のミス,例えば PR の issue 番号が間違っていたときなどに使える.

  • template
$ git branch -m [古いブランチ名] [新しいブランチ名]
$ git branch -m [新しいブランチ名]  # 現在のブランチの名前を変更

現在の作業中のローカルのブランチ名を変更する場合は [古いブランチ名] を省略できる.

  • ex1) ローカルリポジトリissues/9 というブランチ名を issues/11 というブランチ名に変更する
# 例えば,develop ブランチで作業中
$ git branch -m issues/9 issues/11

issues/9 上で作業している場合,issues/9 を省略できる.

# issues/9 ブランチで作業中
$ git branch -m issues/11
  • ex2
$ git branch  # 現在のブランチの確認
  develop
* feature/add-filters
$ git branch -m feature/add-filter  # 単数形に変更
$ git branch
  develop
* feature/add-filter  # 変更されたことを確認

リモートのブランチ名の変更

ローカルでブランチ名を変更し、新しいブランチ名のブランチを新規ブランチとして push する。このタイミングでは、古いブランチ名と新しいブランチ名の 2 つのブランチがリモートリポジトリに存在することになる。

最後にリモートの古いブランチ名のブランチを削除する。リモートのブランチを削除する作業なので、変更を消してしまわないように慎重に。

# ローカルのブランチ名を変更
git branch -m issues/9-fix-error-handling issues/9-hotfix-error-handling-on-receiving-signal

# 新しいブランチ名でリモートに新規ブランチとして push
git push origin issues/9-hotfix-error-handling-on-receiving-signal

# リモートの古いブランチ名のブランチを削除
git push origin :issues/9-fix-error-handling

ブランチの消去

ローカルブランチの消去

  • template

git branch -D を行う際は十分注意すること(自己責任で実行してください).

# soft(変更内容が merge されていない場合エラーが出る)
$ git branch -d [branch-name]
# hard(強制的に消去)
$ git branch -D [branch-name]
  • ex
$ git checkout master
$ git branch -d [branch-name]

# もしそのブランチがマージされていないとエラーが出る
error: The branch '[branch-name]' is not fully merged.
If you are sure you want to delete it, run 'git branch -D my-branch'.

# -D でオプションで強制的に消去できる
$ git branch -D [branch-name]

リモートリポジトリのブランチの消去

  • template
# 短くて良さげ
$ git push origin :[branch-name]
# 明示的に的に delete
$ git push --delete origin [branch-name]
  • ex) issues/9 というブランチを消去する
$ git push origin :issues/9
$ git push --delete origin issues/9

既存のブランチをコピーする

ローカルのブランチ -> ローカルのブランチへコピー

  • template
$ git branch
$ git checkout -b [コピー先の新しいブランチ] [コピー元のブランチ]

リモートのブランチ -> ローカルのブランチへコピーする

ローカルで開発するとき,リモートリポジトリの既存のブランチ上で作業したい場合がある.それを行う方法は以下 4 つ.一番楽なのは 3 つ目だが,仕組みを理解するためにも順を追って説明する(新規ブランチを作成する場合はここでは紹介しないが,これよりも簡単).

  • branch + checkout
  • checkout -b
  • (推奨)checkout -t
branch + checkout
  • template

git branch [branch-name] origin/[branch-name] で,リモートリポジトリ origin/[branch-name]origin はリモートリポジトリの名前)というブランチをローカルリポジトリ[branch-name] というブランチにコピーする(ブランチの新規作成 & 内容のコピー),という処理を行っている.

$ git branch [branch-name] origin/[branch-name]
$ git checkout [branch-name]
  • ex) ローカル環境でリモートリポジトリの issues/9 というブランチ上で作業をしたい
$ git branch issues/9 origin/issues/9
$ git checkout issues/9
checkout -b
  • template
$ git checkout -b [branch-name] origin/[branch-name]
  • ex) ローカル環境でリモートリポジトリの issues/9 というブランチ上で作業をしたい
$ git checkout -b issues/9 origin/issues/9
(推奨)checkout -t

これが一番楽.

  • template

ほとんどの場合,リモートリポジトリのブランチ名をそのままローカルリポジトリのブランチ名に使うため,このコマンドならそれが少ないタイプ量で実現できる.

git checkout -t origin/[branch-name]
fetch + checkout
# リモートブランチ一覧から指定の探す
git branch -r

# fetch
git fetch origin [branch-name]

# checkout
git checkout [branch-name]

作業中 / 新規ブランチの push

既に作業中のブランチに対する push も,ローカルで新しく作成したブランチの push も同じコマンドで行える

$ git push origin [branch-name]
$ git push origin [new-branch-name]

git stash:作業の一時保存

一時退避

git stash は,現在のブランチでの作業を(コミットせずに)一時保存して他のブランチで作業できるようにするためのコマンド.git stash を実行すると変更内容がスタックに保存される.

実際にやってみると分かるが,git commit しないまま git checkout でブランチを切り替えようとするとエラーが出てブランチを切り替えることができない.

git stash

(後で単語調べる用)現在進行中の作業(WIP)をスタッシュ(安全な隠し場所)に入れて、git checkout を実行できるようにする.

スタックのリスト

まず,積まれているスタックを git stash list で確認する.

$ git stash list

stash@{0}: WIP on branch1: ...
stash@{1}: WIP on branch2: ...
...
stash@{3}: WIP on master: ...

スタックから特定の退避内容を復元

特定のブランチをスタックに積まれているものから復元する.

git stash pop stash@{[num]}

スタックから特定の退避内容を消去

git stash drop stash@{[num]}

スタックを全消去

git stash clear

main ブランチの変更を作業ブランチに反映させる

  • よくある状況
    • 複数のブランチで作業してて、とある作業ブランチを main にマージしたときに、その変更を別の作業ブランチに反映させたいとき

merge

不必要なマージコミットが発生するのでできれば merge はしたくない。状況によるのでなんとも言えないが、個人で作業する場合は rebase の方が良い(基本的に作業ブランチは個人で作業することの方が多いはず。ただし、複数人で作業しているときに rebase をすると、作業ブランチのこれまでのコミット ID が全て更新されるので場合によっては force push が必要になる可能性がある)。

rebase

まず、main ブランチを最新の状態にする。

git switch main
git pull

rebase コマンドを使用して、作業ブランチで main ブランチの作業を適用する。

git rebase main
コンフリクトが発生した場合

Git が指示する通りにそれらを解決し、rebase を続ける。コンフリクトを解決した後、以下のコマンドで rebase を続けることができる。

git add .
git rebase --continue

コンフリクトが解決できなそうなとき、以下のコマンドで中止して、元の状態に戻すことができる。

git rebase --abort

.gitignore 関連

一度追跡したファイル,ディレクトリを追跡対象から外す

一度追跡してしまったファイル,ディレクトリは,.gitignore に追加しても追跡対象から外すことはできない.もしこれらを追跡対象から外したければ,git rm --cached コマンドで一度 git の管理下から外し,.gitignore に追加すれば良い.(git rm--cached オプションを付けないとファイル,ディレクトリごと消えてしまうため注意

  • ファイルを追跡対象から外す

teminal

git rm --cached [filename]

.gitignore

filename

ディレクトリごと外したい場合は,ディレクトリ下全てのファイルを外す必要があるため,再帰オプション(recursive option)-r を付ける必要がある.

terminal

git rm --chached -r [dirname]

.gitignore

dirname/

注意点は 2 点

  • git rm に必ず --cached オプションを付けて実行すること.付けないとファイル,ディレクトリごと消える.
  • git rm で管理下から外した直後には,外したファイル群は再度 git add で追跡対象に加えることができてしまう.そのため必ず,git rm -> .gitignore を編集 -> git add という順序を辿り,外したファイル群を再度追跡対象に加えてしまうことがないように気をつける.

参考

pdf

  • Pro git

progit-ja.github.io

辞書的に使っている資料.まぐ(@maguroguma0712)さんに教えていただいた.

ブランチ関連

.gitignore 関連

自分が書いた git に関する記事

pyteyon.hatenablog.com