🧚

【Git】便利だけど忘れがちなコマンド

2021/08/07に公開

git addを分割する

❯ git add -p <ファイル名>

git addを分割するには、pオプションを指定します。更に細かく分割したい場合はStage this hunk [y,n,q,a,d,/,j,J,g,e,?]?と聞かれている状態の時にsを選択します。

ただし、変更した行が連続する場合、sが使えません。その時は、eを選択して直接編集します。eを押すとエディタが立ち上がり、diffの編集画面になります。

  • s: split the current hunk into smaller hunks
  • e: manually edit the current hunk

コンフリクトした時、片側の変更だけ適用する

今いるブランチに合わせる場合

❯ git checkout --ours <ファイル名>

mergeしようとしているブランチに合わせる場合

❯ git checkout --theirs <ファイル名>

--ours--theirsを実行後、addしてcommitします。

他ブランチのコミットを反映する

git cherry-pick <コミットID>

ファイルやフォルダを別のブランチから持ってくる

cherry-pickはコミット単位に対して、checkoutはファイルやフォルダそのものを持ってきます。

別のブランチからファイルを持ってくる前に確認する場合

❯ git show <ブランチ名>:<ファイル名>

別のブランチからファイルを持ってくる場合

❯ git checkout <ブランチ名> -- <ファイル名>

別のブランチからフォルダを持ってくる場合

❯ git checkout <ブランチ名> <フォルダ名>

addを取り消す

以下、3つのコマンドはすべて同義です。引数にドットを指定すると、現在のディレクトリ配下を対象とした全てのファイルが指定されます。現在のディレクトリ配下を意識せず、全てのファイルを指定したいのであればドットは不要です。

ちなみにHEADとは、簡単に言えば自分が今いる位置のことです。Gitのv1.8.5からは、HEADのエイリアスとして@が用意されています。

❯ git reset
❯ git reset HEAD
❯ git reset @

git reset --hardを取り消す

手順(1)

履歴を確認します(fugaとhogeのコミット履歴があります)

❯ git log --oneline
4e6ec8ce fuga
5fb49374 hoge

直前のコミットを取り消します(fugaが消えます)

❯ git reset --hard HEAD\^
❯ git log --oneline
5fb49374 hoge

reflogで履歴を確認して、git reset --hard HEAD\^を取り消します(fugaを消したのを取り消します)

❯ git reflog
5fb49374 HEAD@{0}: reset: moving to HEAD\^
4e6ec8ce HEAD@{1}: commit: fuga
5fb49374 HEAD@{2}: commit: hoge

❯ git reset --hard HEAD@{1} // たいてい1つ前

改めて履歴を確認すると、最初の状態に戻っています。

❯ git log --oneline
4e6ec8ce fuga
5fb49374 hoge

手順(2)

先ほどのgit reset --hard HEAD@{1}以外でも取り消せます。

❯ git reset --hard ORIG_HEAD

reflogとは

reflogとはHEADの動き、つまり自分自身の行動履歴で、具体的には以下が履歴として残ります。

  • 新規コミット(commit、merge、pull、revert等)
  • ブランチの切り替え(checkout)
  • 履歴の書き換え(reset等)

使い所は、先ほどのように誤ってgit reset --hardをしてしまい、それを戻したいときなどにreflogを使います。

Gitリポジトリ内をgrepする

❯ git grep <検索キーワード>
❯ git grep hoge
src/App.vue:import hoge from './components/hoge.vue'
src/App.vue:    hoge,
src/components/hoge.vue:  name: 'hoge',

どのコミットでバグが入ったか探す

blameを使うと、いつ誰のコミットでその行が変更されたのかを調べられます。

❯ git blame src/components/hoge.vue
eba3de4d src/components/hoge.vue (chida 2021-03-17 11:56:37 +0900  1) <template>
eba3de4d src/components/hoge.vue (tanaka 2021-03-20 12:37:30 +0900  2)   <div>hoge</div>
eba3de4d src/components/hoge.vue (chida 2021-03-17 11:56:37 +0900  3) </template>

Pull Request打ち消す

Pull Request打ち消すには、Revertを使います。Revertとは、コミットの内容を打ち消すためのコミットです。既にリモートにコミットをして、git resetなどで取り消せない時に使います。

Pull Requestではなく、通常のコミットをRevertする場合は次の通りです。

❯ git revert <コミットID>

Pull RequestをRevertする場合は、まずgit log --graphMergeを探します。(graphはこの記事用に履歴を見やすくするため、整形しています)

❯ git log --graph --pretty='format:%C(yellow)%h%Creset %s'
*   4e9a80f Merge pull request #3 from chida0909/foo
|\  
| * c972faa fourth-commit
| * 753f7de third-commit
|/  
* 41348ea second-commit
* dd2e15e first-commit

4e9a80fのログでMerge pull requestが作成されたことが分かりました。そのため4e9a80fをrevertして、c972faaと fourth-commit753f7de third-commitのコミット内容を取り消します。

❯ git revert -m 1 4e9a80f

この-m--mainlineの略で引数に番号を指定します。1はマージされた側のブランチで、2がマージする側のブランチになるため、多くの場合は1を指定します。

Revertを実行後、もう一度履歴を確認すると、Revertのコミットが作成されていることが分かります。c972faaと fourth-commit753f7de third-commitのコミット履歴自体は残っていますが、作業したファイルの内容は消えています。

❯ git log --graph --pretty='format:%C(yellow)%h%Creset %s'
* 26281fb Revert "Merge pull request #3 from chida0909/foo"
*   4e9a80f Merge pull request #3 from chida0909/foo
|\  
| * c972faa fourth-commit
| * 753f7de third-commit
|/  
* 41348ea second-commit
* dd2e15e first-commit

ちなみにコマンドではなく、GitHub上でも行うことが出来ます。
https://docs.github.com/ja/github/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/reverting-a-pull-request

昔のコミットに戻る

git checkout <コミットID>

特定ファイルを昔のコミットに戻す

❯ git checkout <コミットID> <ファイルパス>

直前のコミットメッセージを変更する

もう一度コミットし直すほどでもない修正をした時に、直前のコミットを修正できます。コミット履歴が無駄に増えません。

git commit --amend

git commit --amendを実行すると、GNU nanoエディタが立ち上がるので、コミットメッセージを入力します。Ctrl + oを押して入力を保存します。保存するファイル名を聞かれるので、そのままEnterを押します。最後にCtrl + xを押してエディタからExitします。

HEAD^ と HEAD~の違い

  • キャレット(^): マージコミットがあり、2番目以降の親を指定する場合に使う
  • チルダ(~):1番目の親を指定する場合に使う

次の例で表すと、一番左側の一直線に伸びているグラフが1番目の親で、1番目に対してマージしたグラフが2番目の親です。

❯ git log --graph --pretty='format:%C(yellow)%h%Creset %s'
*   4e9a80f Merge pull request #3 from chida/hoge
|\
| * c972faa fourth-commit
| * 753f7de third-commit
|/
* 41348ea second-commit
* dd2e15e first-commit

git show HEAD\^2で2番目の親の1つ前の履歴を確認します。

❯ git show HEAD\^2
commit c972faa6499bbfebc3687fba6ea530faaaf634c6

git show HEAD~2で1番目の親の2つ前の履歴を確認します。

❯ git show HEAD~2
commit dd2e15e00093e2b959319aa4f983f6b005a40ce3

数値を指定しないと「1」と同義になります。キャレット(^)の場合、1番目の親の1つ前の履歴を確認します。チルダ(~)の場合も、1番目の親の1つ前の履歴を確認します。

❯ git show HEAD\^
commit 41348ea9b757b296ce826e2ae4203d04f53babd5

❯ git show HEAD~
commit 41348ea9b757b296ce826e2ae4203d04f53babd5

ちなみに、コミットIDを指定しても同じことが出来ます。

❯ git show c972faa
commit c972faa6499bbfebc3687fba6ea530faaaf634c6

ローカルのブランチを掃除する

リモート側で存在しないブランチをローカルのリモート追跡ブランチから削除する

❯ git fetch --prune

マージ済のブランチを全て削除

コマンドでdevelop, master, mainを指定しているので、それ以外のマージ済みのローカルのブランチが全て削除されます。

❯ git branch --merged|egrep -v '\*|develop|master|main'|xargs git branch -d

指定したブランチ以外を全て削除

マージ済みかどうかに関わらず、ローカルからdevelop, master, main以外のブランチを全て削除します。

❯ git branch |egrep -v '\*|develop|master|main'|xargs git branch -D

git log --graphを良い感じに整形する

記事の公開用にgit log --graphで、必要な内容だけに絞って出力していました。

❯ git log --graph --pretty='format:%C(yellow)%h%Creset %s'
*   4e9a80f Merge pull request #3 from chida0909/foo
|\
| * c972faa fourth-commit
| * 753f7de third-commit
|/
* 41348ea second-commit
* dd2e15e first-commit
  • --pretty: ログをデフォルトの書式以外で出力する
  • %C(yellow): 色の指定
  • %h: コミットのハッシュ(短縮版)
  • %Creset: 色の指定の終わりを表す。上記の場合、%hだけがyellowになる
  • %s: コミットメッセージ

https://qiita.com/shizen-shin/items/6950f5bc7a3e696f21a3#--prettyformatのよく使うオプション

単純なログを見る

単純なログの確認はgit log --onelineでいけますが、更に簡略化したハッシュ値のないログを確認する場合は、shortlogを使います。

❯ git shortlog
chida (2):
      feat: hogeを追加
      fix: fugaを修正

空コミット

❯ git commit --allow-empty -m "first commit"

キャレット(^)の自動エスケープ

zshを使っているとキャレット(^)を入力する度にエスケープしないといけません。毎回手動で入力するのは面倒なので自動でエスケープします。

❯ mkdir ~/.functions
❯ git clone https://github.com/knu/zsh-git-escape-magic
❯ mv zsh-git-escape-magic/git-escape-magic ~/.functions/git-escape-magic
❯ vi ~/.zshrc

以下を.zshrcに貼り付けます。

# git環境下でキャレット(^)の自動エスケープ
fpath=(~/.functions ${fpath})
autoload -Uz git-escape-magic
git-escape-magic
❯ source ~/.zshrc

これでgitコマンド入力時にキャレット(^)を入力すると、自動でバックスラッシュが入ります。

Discussion