AQ Tech Blog

【今日から使える】地味に便利なGitコマンド

作成者: hironori.yamada|2024年10月09日

 

はじめに

デジタルイノベーション部IoT/AIソリューション2課の山田です。
普段の業務ではバックエンドの開発とインフラの構築を担当しており、作業をするうえでは様々なツールを使用していますが、その中でも欠かせないツールの一つとしてGitがあります。
Gitを日常的に使用しているエンジニアの方々は多くいらっしゃるかと思いますが、意外と知られていないコマンドや使い方がたくさんある奥の深いツールであるとも言えます。

今回は自分が普段使用しているGitコマンドの中で、普段の業務に役に立っていながらもあまり知名度の高くなさそうな使い方をいくつか紹介していきます。

期間を絞ってclone

git clone コマンドはコミット数や日付を指定することができます

長期間運用されているプロジェクトやOSSのリポジトリをcloneする際は、Gitのcommit数も多いためcloneが完了するまで結構な時間がかかることがあります。
そこで git clone コマンドにオプションを付与することでcommit数を指定してcloneすることができます。

 

—-depth 数を指定

$ git clone --depth 1 git@github.com:my-project/example.git

—-depth [数値]を付与することで指定した分のcommitだけ最新から遡ってcloneを実行します。

とりあえずローカルで動作確認をしたい時や現状のコードを確認したい場合なら —-depth 1 で十分です。爆速でcloneが完了します。

実際にどれくらいの差があるのか試しにTypeScriptのリポジトリをcloneしてみて--depth 1を付けない場合と付けた場合でかかった時間を比較してみましょう。

オプション無しではcloneに6分50秒かかっているのに対し、--depth 1オプションを付けた場合は18秒でcloneが完了しています。

clone後にやっぱり過去の履歴が欲しいという場合もあるかと思います。その場合はgit fetchで履歴を取得します。

$ git fetch --depth 50

上記のように git fetch --depth [数値]をつけて指定した分のコミットを取得できます。

$ git fetch --unshallow

さらに全履歴を取得したい場合は git fetch --unshallowをつけて実行すると全てのコミットを取得できます。

 

--shallow-since 日時を指定

$ git clone --shallow-since="last year" git@github.com:my-project/example.git

--shallow-since ”日付や時間”で日時を指定してcloneすることも可能です。↑の例の場合は最新1年間分のコミットを取得します。

日付部分は”2024/09/01””last Friday at noon”などでもOKです。

$ git fetch --shallow-since="5 years ago"

同様にgit fetchでも可能です。

ブランチ間のショートカット

ブランチに関連するコマンドで-(ハイフン)を使うとショートカットができます。 (結構便利…!🙌)

 

ブランチの切り替え

$ git switch develop               // 最初はdevelopブランチにいる
$ git switch feature/add-file // feature/add-fileブランチに移動
$ git switch - // developブランチに戻る

↑の例だとgit switch -でfeature/add-fileブランチから1つ前にいたdevelopブランチに戻ることができます。ブランチ名が長いときなどは特に便利です。

ちなみにgit switch @{-3}というコマンドで3回前のブランチに戻ることができます。

 

直前をマージ

mergeコマンドでも-を使用できます。

$ git switch develop               // 最初はdevelopブランチにいる
$ git switch feature/add-file // feature/add-fileブランチに移動
$ git merge - // 直前のブランチをマージ

↑の例だとdevelopブランチの内容をfeature/add-fileブランチにmergeすることができます。

 

ディレクトリ移動でも

これはGitコマンドとは関係ないのですが、ディレクトリ移動でも-を使用することができます。

$ cd ~/project/dir                 // 最初はdirにいる
$ cd ~/project/app/functions/hoge // hogeディレクトリに移動
$ cd - // -で移動
$ pwd
~/project/dir // dirに戻っている

↑のように直前にいた場所へ移動することができます。

グローバルな.gitignore

プロジェクトを問わず必ずGitで追跡したくないファイルはグローバルに設定しておくと便利です。

まずは通常の.gitignoreを作成するようにリスト化してファイルに保存します。ファイル名や場所は自由でOKです。

$ vi ~/.gitignore_global

.DS_Store // 追跡したくない
.vscode/ // ファイルやディレクトリを
.idea/ // リスト化する

次にgit config --global core.execludesFile "ファイル名"というコマンドを使用します。

$ git config --global core.execludesFile "~/.gitignore_global"

↑コマンドにて先程のリストを記述したファイルをグローバルなgitignoreとして登録することで一括でGit追跡を回避することができます。

これでうっかりPCやエディタの環境設定ファイルなどをGitの追跡に含めてしまうことを避けることができますね。

エイリアスの設定

よく使用するGitコマンドにエイリアスを付けてコマンドを短くすることができます。

git config --global alias.[エイリアスコマンド] "エイリアスに登録したいコマンド" というようにコマンドを実行するとエイリアスを作成することができます。

$ git config --global alias.cm "commit -m"

↑の例はgit commit -m git cm で打てるようにする場合のコマンドです。

上記を実行することでPCユーザーのホームディレクトリにある.gitconfig ファイルに以下のようにエイリアスが登録されます。

[alias]
cm = commit -m

同様の形式で記述すれば.gitconfigを直接編集してエイリアスを追加することも可能です。

$ git cm "コミットメッセージを書く"    // エイリアス「cm」で「commit -m」を実行できる

さらにGitコマンドではないコマンドもgitで始まるコマンドとしてエイリアスに登録することができます。

.gitconfigのエイリアス部分にてコマンド部分に!をつけることで通常のコマンドを登録することができます。

[alias]
ignore-l = !cat .gitignore
ignore-gl = !cat ~/.gitignore_global

↑の例ではカレントディレクトリの.gitignoreとグローバルに設定した.gitignoreを確認できるコマンドを登録してみました。
以下が実行結果になります。

$ git ignore-l
node_modules/
npm-debug.log*
.DS_Store

通常のコマンドをGitコマンドのようにgit で始まるコマンドとして登録できるので、Git関連のワンライナーで作成した複雑なコマンドなどをまとめておくのに良いかもしれません。

自分の場合は以下のように長いワンライナーのエイリアスを登録したりしています。

不具合の原因となったコミットを素早く特定

コミットを重ねる中で気付かないうちにどこかのタイミングで不具合が発生してしまったというケースにおいて、原因となったコミット地点を特定する際にはgit bisectコマンドが役に立ちます。

git bisect コマンド

$ git bisect start
$ git bisect good
$ git bisect bad
$ git bisect reset

bisectコマンドには上記の4種類があります。

使い方としてはstartでbisectモードに入り、goodbadによって順番に各コミットを判別していき、最終的に不具合の原因となったコミットを特定します。そしてresetでモードを終了するという流れになります。

上記の説明だけではわかりにくいと思うので実際の使用例を見てみましょう。

 

git bisectコマンドの流れ

ここでは1ヶ月前には正常に動いていたシステムが、どこかのコミットタイミングで不具合が起きたことを想定して説明していきます。
1ヶ月前の時期と現在の間での期間でどのコミットが不具合の原因かを特定してみましょう。

下記は1から10に行くに従って古くなるコミットの一覧を表しています

- コミット01 ← いまココ
- コミット02
- コミット03
- コミット04
- コミット05
- コミット06
- コミット07
- コミット08
- コミット09
- コミット10 ← 1ヶ月前

まずはbisectモードをスタートさせます。

$ git bisect start

ここから各コミットに対してgoodかbadの判別を入力しコミットを分別していきます。

現在のコミット01の時点では不具合が起きている状態であり、bad判定にするために以下のbisectコマンドを入力します。

$ git bisect bad

現在の状態を入力したら、自分の記憶をもとに正常に動いていた時のコミットに移動します。

ここでは1ヶ月前は正常に動いていたという想定なので、その時点のコミットに移動します。

$ git checkout "@{last month}"     // checkoutで日付を指定して移動
- コミット01 bad
- コミット02
- コミット03
- コミット04
- コミット05
- コミット06
- コミット07
- コミット08
- コミット09
- コミット10 ← 1ヶ月前 ← いまココに移動した

移動したらシステムの動作を確認し、正常に動いているのを確認できたらこの時点のコミットをgood判定にします。

$ git bisect good
- コミット01 bad
- コミット02
- コミット03
- コミット04
- コミット05
- コミット06
- コミット07
- コミット08
- コミット09
- コミット10 good ← 1ヶ月前 ← いまココを判定した

goodを入力するとbisectモードが自動的に中間地点のコミットへ移動してくれます。

- コミット01 bad
- コミット02
- コミット03
- コミット04
- コミット05 ← いまココに移動した
- コミット06
- コミット07
- コミット08
- コミット09
- コミット10 good ← 1ヶ月前

移動したら再度システムの動作確認をし、正常か不具合が起きているかによって判別を行います。

ここではこの時点で不具合が起きていたとし、bad判定を入力します。

$ git bisect bad
- コミット01 bad
- コミット02
- コミット03
- コミット04
- コミット05 bad ← いまココを判定した
- コミット06
- コミット07
- コミット08
- コミット09
- コミット10 good ← 1ヶ月前

判定を入力すると同じように、bisectモードが自動的にgoodとbadの中間地点コミットに移動してくれます。

- コミット01 bad
- コミット02
- コミット03
- コミット04
- コミット05 bad
- コミット06
- コミット07 ← いまココに移動した
- コミット08
- コミット09
- コミット10 good ← 1ヶ月前

移動したら同様に動作確認を行いgoodかbadの判定を入力します。
このような流れで原因のコミットが特定できるまで繰り返し行っていきます。

コミット07では動作に問題が無かったのでgoodという判定を入力します。

$ git bisect good
- コミット01 bad
- コミット02
- コミット03
- コミット04
- コミット05 bad
- コミット06
- コミット07 good ← いまココを判定した
- コミット08
- コミット09
- コミット10 good ← 1ヶ月前

そうするとこの時点で不具合が起きた原因となるコミットが特定できました。

- コミット01 bad
- コミット02
- コミット03
- コミット04
- コミット05 bad
- コミット06 ← ココが不具合の原因となったコミット💡
- コミット07 good
- コミット08
- コミット09
- コミット10 good ← 1ヶ月前

bisectモードは特定が完了すると最後の判定を入力した時点で原因となったコミットの情報を表示してくれます。

$ git bisect good                  // goodの判定を入力後↓の原因となったコミット情報が表示される

◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯ is the first bad commit
commit xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Author: hogetarou <hogehoge@fuga.com>
Date: Mon Sep 1 12:12:00 2000 +0000

コミット06

src/path/example.ts | 1 +
1 file changed, 1 insertion(+)

原因のコミットが特定完了したら最後に終了コマンドを打ってbisectモードを終了します。

$ git bisect reset                 // bisectモードを終了しコミット01の時点に戻る

終了後は一番最初の最新のコミットの位置に作業状態が戻ります。
その後は原因のコミットに移動などして不具合を修復をしていくことになるかと思います。

このようにgoodなコミットとbadなコミットに仕分けし二分探索で原因となるコミットを特定していきます。
500個のコミットがある場合はlog2(500)=9なので最短で9回、1000個のコミットの場合は10回の仕分けで特定することができるので効率的に調査を行うことができます。
チーム開発や途中から引き継いだプロジェクトにて自分で把握しきれないコードや、コミットが含まれている際の調査に威力を発揮したりします。

まとめ

以上、今日から使える地味に便利なGitコマンドの紹介でした。
ショートカットキーと同じようにちょっとしたコマンドも使う場面が多かったりすると、作業効率を大きく上げてくれることがあります。
もし参考になりましたら、ぜひ普段の業務にも取り入れてみてください!