Gitは便利なバージョン管理ツールです。この分野では向かうところ敵なしでしょう。あらゆる日常的な作業をGitのコマンドの繰り返しで簡単に行うことができます。しかし、基本以上のことが必要になる場面もたくさんあります。そんなわけで、Gitスキルを次のレベルに引き上げる高度なGitコマンドを知っておくと便利です。

また、普段はあまり使うことのない様々なGitの概念も存在します。例えば、変更をコミットするのとは対照的なstash、その他にもrebaseなどがあります。これらを学べば、あなたのチームにおける価値がさらに高まるかもしれません。

今回の記事では、一歩進んだコマンドをいくつかご紹介します。ただし使いこなすには、特定のスキルやコンセプトの理解などが必要です。

推奨される前提条件

Gitの基本を学び、git deleteのような中級コマンドは理解できるかもしれませんが、高度な操作を行えるようにもっと多くの選択肢があると便利です。

バージョン管理全般が初めての場合は、レパートリーを増やす前に基本的なコマンドを日常的に使ってみることをお勧めします。

簡単にまとめると、理解しなければならない点は以下の通りです。

  • コミット、ブランチ、プルリクエストなどGitの中核となる概念
  • git addgit mergegit commitgit pushなどの基本的なGitコマンド
  • 高度なGitタスクの土台であるターミナルの操作

また、今回ご紹介するコマンドの多くは、共有されているコードベースに大きな影響を与える可能性があるため、チームでの共同環境でGitを使った実務経験があると理想です。例えば、責任のある立場で特定のコマンドを利用するということもあり得ます。つまり、どのようなときにこれらのコマンドを使うのか、注意深く理解する必要があります。

Git上級者向けの便利なコマンド12選

続いては、基本を越えてあなたのGitのマスターを後押しする12の高度なGitコマンドを取り上げます。その中でもよく使うであろうコマンドから始めましょう。

1. rebase/リベース

rebaseはプルリクエストに代わる強力なGitコマンドです。ブランチの分岐後に行った新しいコミットをすべて新しいブランチにプルし、その変更をベースブランチの最後に整理することができます。これは、何らかの理由でマージコミットを作成せずに別のブランチからの変更を取り込みたい場合に便利です。

rebaseのよくある使用例としては、フィーチャーブランチで作業しているときにメインブランチからの変更をマージコミットなしで取り込みたい場合などがあります。rebaseはプロジェクトの履歴をきれいに保つのに有用ですが、実行に時間がかかったりマージ中にエラーが発生したりする可能性があります。

ブランチをrebaseするには、git rebaseコマンドを使います。あるブランチを別のブランチにrebaseする例を示します。

git checkout foo-branch

git rebase main

リリース予定のコミットをリストアップして、事前に編集する機会を確保することもできます。このように、コミットをつぶしたり、コミットメッセージを編集したりできます。--interactiveまたは--iフラグを使うと、「対話的rebase」を実行できます。

git rebase --interactive <other-branch-name>

コミット履歴を整理するためにコミットをひとつにまとめることも、rebaseの一般的な使用例です。これにより、コミット履歴を読みやすく理解しやすくすることができます。

rebase中にフラグに続けてつぶしたいコミット数を書くと、コミットがひとつにまとまります。

git rebase -i HEAD~3

コミットウィンドウのような対話的なrebaseウィンドウが開き、コミットを選択して編集することができます。

ターミナルでgit rebaseを実行する
ターミナルでgit rebaseを実行する

画面上部にはコミットの一覧が表示され、下部にはコマンドの選択とその他の関連情報が表示されます。デフォルトのオプションはpickで、何も変更せずに使いたいコミットとして選択します。

rebaseに役立つコマンドは他にもたくさんあります。たとえば、コミットをrewordしたり、複数のコミットをまとめたりすることができます。変更を加えたりコマンドを使用したりするには、ターミナルのエディタを使用します。デフォルトはVimであることが多いので、このエディタを知っておくのが賢明です。

最後に、変更を保存してリモートブランチにプッシュします。

2. revert、reset、unstage

Gitは、変更の取り消しに関して「悪名高い」ものです。一般的には難しい処理で、エラーを吐きがちです。しかし、ワーキングディレクトリやステージングエリアに加えた変更を元に戻したい場合は、いくつかのGitコマンドを使うことができます。

git statusを実行すると、Gitでファイルのステージを解除する(ワーキングディレクトリに戻す)方法が確認できます。

Gitのステータス出力でファイルのステージ解除の方法がわかる
Gitのステータス出力でファイルのステージ解除の方法がわかる

このように、現在のブランチからコミットを削除して別の場所に送ることも簡単にできます。

git reset HEAD <commit>

これは、あたかも直近のコミットを削除したかのように、ブランチをひとつ前のコミットに戻す効果があります。ブランチを元の状態に戻したいというケースがあるはずです。この場合、リモートオリジンに対してresetすることができます。たとえばgit reset --hard origin/mainを使用します。しかし、この変更が永久に消えてしまうことに注意しましょう。

git checkoutコマンドはよく使うものであり、基本的なものと考えられていますが、コミットの前にファイルをアンステージする(ワーキングディレクトリに戻す)ためにも使うことができます。

git checkout -- <filename>

ダッシュとファイル名のプレースホルダーの間のスペースに注意してください。このコマンドにより指定したファイルのステージ解除を経て、作業ディレクトリの変更が破棄されます。このコマンドは、ファイルに加えたローカルな変更をすべて削除することになりますので十分に注意してください。未保存の変更が不要であることを二重三重に確認しましょう。

また、git revertの変更も使用できます。しかし、これは変更をロールバックするのではなく、前のコミット内の変更を元に戻す新しいコミットを作成します。

ここでの大きな違いは、新しいコミットへ参照ポインタを移動せず、古いコミットを保持することです。これは、コミット履歴から削除せずに変更を取り消したい場合に便利です。

このコマンドは参照を受け取ることを想定しており、最新のコミットをシンプルに元に戻す効果があります。

git revert HEAD

しかし、ファイルやコミットを元に戻したり変更したりできるのは、実際にはもっと広範囲にわたります。次の2つの項目で、これについてご紹介します。

3. ファイルをデフォルトの状態に戻す

Gitでは、(比較的新しい)コマンドgit restoreを使ってファイルをデフォルトの状態に簡単に復元することができます。実際のところ、これはgit resetに取って代わるものだと考えるべきでしょう。たとえば、git restore --staged <filename>を使ってgit reset HEADと同じ結果を得ることができます。

このコマンドで、さらに多くのことが可能になります。例えば、複数のファイルをデフォルトの状態に戻すこともできます。git status を実行してもその方法を確認可能です。

git restore <filename>

このコマンドは作業ディレクトリから変更内容を削除し、何事もなかったかのようにします。git checkout -- <filename>と同様、ローカルでの変更内容が完全に消えてしまうため、それが不要であることを確認しておきましょう。

4. コミットの修正

コミットをプッシュした後で、重要なことを忘れていたことに気づくことがあるかもしれません。Gitでは、コミットを簡単に修正して足りない変更を適用することができます。

これは特定のプロセスで実行します。

  • プロジェクトに必要なファイルすべてに変更を加える
  • git addを使い通常のやり方でステージングに送る
  • ステージングエリアの変更を、別のコマンドを使ってコミットする
git commit --amend

これにより、元のコミットが、ステージングエリアを使った新しいコミットで修正されます。そのため、古いバージョンのコミットが不要であることを確認するのをお忘れなく。また、--amendフラグは(この記事全体で説明しているのと同じ理由で)リモートコミットではなくローカルコミットで使用することをおすすめします。

git commit --amendを使ってコミットメッセージのみを編集することもできます。

git commit --amend -m "New commit message"

5. Gitログ

Gitログは、リポジトリの過去の記録を理解するのに有用です。しかし、git logコマンドは特に高度なものではありません。これをそのまま使う代わりに、さまざまなオプションを組み合わせて出力をフィルタリングすることができます。

git log

git log --decorate

git log --stat

例えば、ログエントリーを装飾すると、表示されているすべてのコミットの参照名が表示されます。--statでコミットの挿入と削除を表示できます。

ターミナルでgit log --statコマンドを実行する
ターミナルでgit log –statコマンドを実行する

他のオプションを使ってログの出力をカスタマイズすることもできます。たとえば、次のようなコマンドです。

git log --author=<author-name>

git log --grep=<pattern-string>

ここでは、特定の作者名やテキストパターンでログをフィルタリングしています。実際には、複数のオプションとフラグを組み合わせて、特定の目的にあったログを生成することができます。例えば、次のコマンドを考えてみましょう。

git log --oneline --author=<author-name> --since=<date> feature-temp

このコマンドは、指定した日付以降に特定の作者がfeature-tempブランチで行ったすべてのコミットを検索し、一行で表示します。<date>パラメータには文字列を指定することもできます。

--since=”Two weeks ago”

また、ブランチではなく特定のファイルを検索したい場合は、次のようにします。

git log --oneline --author=bartonfink --since=”5 days ago” -- readme.rm

この例は、ログでできることのほんの一部ですが、検索条件に基づいて正確に特定のコミットを見つけることは十分可能です。

6. Gitフック

コードの実行を補助するために、マクロやその他の自動スクリプトを使うことがあるはずです。Gitにも、フックという形でこのような機能があります。そのスクリプトは、コミットやプッシュなどの特定のイベントに反応し自動で実行されます。また、フックを使ってコードの書式を強制したりテストを実行したりする方法もたくさんあります。

フックには、クライアントサイドとサーバーサイドの2種類があります。

  • クライアントサイドフックは、コミットやマージなどのローカルアクションに基づいて実行されます。
  • サーバーサイドフックは、ネットワーク操作によって実行されます。リポジトリがプッシュされたコミットを受け取ったときなど、さまざまな例が考えられます。

git initを実行すると、Gitによりリポジトリにいくつかのサンプルフックが登録されます。しかし、これを実際に使うには.sampleという拡張子を削除する必要があります。

macOSのフォルダ(Git開始時にインストールされるサンプルフック)
macOSのフォルダ(Git開始時にインストールされるサンプルフック)

ただし、一度に実行できるのは一種類のフックだけです。そのため、ファイル名はサンプルのスクリプトをもとに使いたいフックの種類に対応させましょう。pre-commitupdateなどです。

Gitフックの作成

Gitフックを作成するには、.git/hooksサブディレクトリに拡張子なしの実行スクリプトを作成する必要があります。このスクリプトは、hooksフォルダに追加しておけばそのまま実行できます。

実行形式であれば、スクリプト言語はどれでもかまいません。RubyPythonをお勧めしますが、Bash、Perl、その他多くの言語が使えます。ここですべきは、インタプリタへのパスをデフォルトのBashから変更することだけです。

#!/usr/bin/env python

ここから、通常通りにコードを記述します。たとえば、prepare-commitというPythonのスクリプトなどです。優れたコミットメッセージを書くように促すことができます。

#!/usr/bin/env python

import sys, os

path_commit_msg = sys.argv[1]

with open(commit_msg_filepath, 'w') as f:

f.write("# You’ll need to provide a better commit message than that, buddy!")

常に必要というわけではありませんが、確実に実行できるようにコマンドラインからchmod +x .git/hooks/<hook-name>を実行することをお勧めします。

全体として、フックは繰り返しの作業を自動化し、チーム内でベストプラクティスを実施する便利な手段になります。

7. コミットの参照

Gitでは、SHA-1ハッシュ化アルゴリズムによってコミットを識別します。完全なハッシュでコミットを参照することも可能ですが、面倒でエラーが発生しがちです。

bc7623b7a94ed3d8feaffaf7580df3eca4f5f5ca

代わりに、Gitにはコミットをより短く覚えやすい名前で参照する方法がいくつか用意されています。たとえば、ブランチ名やタグ名を使うことができます。たとえば、「develop」というブランチを考えてみましょう。このブランチの最新のコミットを指す例を示します。

git diff develop..HEAD

これは「develop」ブランチの最新のコミット(HEAD)と現在のコミットの違いを示すものです。

コミット履歴内での相対的な位置を使ってコミットを参照することもできます。たとえば、HEAD~2という省略記法を使うと、現在のコミットより前の2つのコミットを参照することができます。

git diff HEAD~2..HEAD

Gitには、他にもコミットを参照する方法がいくつか用意されています。「@」で現在のブランチを参照したり、「^」でコミットの親を参照したりすることができます。これらの省略記法を使うことで、コミットを操作する時間を節約したりミスを防いだりできます。

8. stash/スタッシュ

一般的な状況で、ファイルに加えた変更をコミットせずに保存する方法はないと考えている方は多いはず。しかしstashを使って、一時的に保存することができます。コミットすることなくブランチを切り替えたり別の作業をしたりする時に便利な選択肢です。

たとえば、作業の途中でブランチを切り替える時にstashが使えます。既存のブランチで作業の内容を保存し、別のブランチにチェックアウトします。別のブランチで作業し、その変更をコミットしてプッシュします。その後、チェックアウトして元のブランチで作業した内容を取り出すことができます。

変更内容を保存するには、2つの方法があります。

git stash

新しく変更を保存し、作業ディレクトリを最後のHEADコミット(変更を加える前の状態)に戻します。git stash listを使って変更点をリストアップし、git stash showを使ってそれを確認することができます。後者のコマンドは、git diffが受け付けるどのようなフォーマットにも対応しています。

ここからブランチを切り替えたり、別の作業をすることができます。変更内容を取り出したいときは、以下のコマンドを実行してください。

git stash apply

これで、最後にstashした変更が作業ディレクトリに適用されます。ただし、ファイルを変更しすぎるとコンフリクトが起こる可能性があるため注意してください。結局のところ、git stashは目下の問題に対する一時的な策ということです。

複数の場所にstashすることもでき、どれを利用するかは以下のようにして指定することができます。

git stash apply stash@{n}

プレースホルダー{n}は整数をとり、stash@{0}は最新の場所を表します。公式のGitドキュメントには、他にもgit stashの例がいくつか掲載されています。

9. bisect/二等分

バグや問題に遭遇したとき、どこから調べればいいのか見当がつかなかったという経験は誰にでもあるはず。そんなときにbisectを使えば、問題のコミットをすばやく特定することができます。

簡単に説明すると、このコマンドはコミットを探索しバグを探し出すものです。問題のあるコミットを見つけ返します。このコマンドの真価は、そのサブコマンドにあります。

bisectを使うには、まずgit bisect startコマンドを使います。すると、プロジェクト履歴の中でも最初のコミットが表示されます。

そこからコマンドを使ってコミットの良し悪しを指定します。

git bisect good

git bisect bad

その後、次のコミットに移動してその「質」のテストを行います。「Good」を「old」に「bad」を「new」に置き換えるといったこともできます。

ここから、バグのあるコミットを見つけるまで、各コミットに「良い」「悪い」のマークをつけていきます。しかし、すべてのコミットを確認する必要はありません。識別子を指定することで、検索結果を絞り込んだり短縮したりすることができます。

git bisect bad feature-test

上の例ではブランチ名を使用していますが、整数を使用した特定のリビジョンであったり、ハッシュ参照であったり、その中身はさまざまです。いずれにしても、バグを見つけたら、以下のコマンドを実行して最新のコードに戻ることができます。

git bisect reset

この記事でご紹介している他の高度なGitコマンドと同様に、これについてもGit公式ドキュメントで詳細を確認可能です。

10. 複数ブランチの比較

コミットの参照について、git diffを使う方法はご説明したとおりです。続いては、これを詳しく見ていきましょう。いくつもの変更を含む複数のブランチが存在することが、よくあるはずです。Gitでは、git diffコマンドを使ってふたつのブランチの差分を比較することができます。リポジトリを調査したり分析したりするために、多くの場合他のコマンドと組み合わせてこれを使うことができます。

基本的なgit diffコマンドによって、変更についての概要が出力されます。コミットやマージを確認する出力とよく似ています。

git diffリクエストの出力結果
git diffリクエストの出力結果

これを細かなブランチやハッシュなどにまで掘り下げることができます。たとえば、2つのブランチを比較するにはgit diff branch1..branch2コマンドを実行します。実際にはプレースホルダの部分を置き換えてください。

git diff feature-branch pre-prod

現在のブランチと別のブランチの差分を比較することもできます。

git diff HEAD..pre-prod

ここで2つドットを使うと、ブランチの最新のコミットの差が返されます。一方、3つドットを使うと、両方のブランチの共通祖先の差を返し、それをテストに使うことができます。

git logのように、出力をクリーンアップし、返すものを絞り込むこともできます。たとえば、git diff --name-only branch1..branch2は、どのファイルが違うかだけをチェックし、コンテキストは除外します。

ターミナルでgit diff --name-onlyコマンドを実行したところ
ターミナルでgit diff –name-onlyコマンドを実行したところ

特に「diff」が長い場合は、出力を解析するのが難しいかもしれません。このような場合は、--color-wordsオプションを使用します。

git diff --color-wordsコマンドを実行し、その出力をターミナルで見る
git diff –color-wordsコマンドを実行し、その出力をターミナルで見る

全体として、git diffは他のコマンドと同じくらい強力です。特に、特定のオプションを呼び出してどのような差分を返すかを絞り込むことができます。

11. 個々のコミットの適用

あるブランチの特定のコミットを、そのブランチをマージせずに別のブランチに適用したいこともあるかもしれません。Gitでは、git cherry-pickを使ってこれを行うことができます。慎重に使うべきですが、git cherry-pickを使えばいくつかの状況で効率的に作業を進められます。

そのひとつが、maintrunkにマージしない古いfeatureブランチがある場合です。git logなどのコマンドを組み合わせて、古い関連コミットを取り出して別の場所に再適用できます。

コミットのリファレンスを見つけるにはgit logを使用します。そこから、cherry-pickしたいブランチにいることを確認します。たとえば、コミットxxxxxaaaaaaをブランチ「trunk」にcherry-pickしたいとします。まず、自分のブランチからチェックアウトします。

git checkout trunk

その後コミットをcherry-pickします。

git cherry-pick xxxxxaaaaaa

コミットメッセージは、おそらく多くの場合古いものになるでしょう。これを解決するには、--editオプションをコマンドに渡します。これにより、cherry-pickの前に新しいコミットメッセージを指定することができます。

12. git addの強化

最後の高度なGitコマンドとして、git addをご紹介します。非常に基本的なGitコマンドですが、驚くべき性能が隠されています。

たとえば、ステージングエリアに個々のファイルを追加する場合は次のようにします。

git add -p

-pオプションを使うと、細かなステージングエリアへの追加が可能になります。各ファイルに加えた変更を確認し、ステージングするファイルを選ぶことができます。多くの時間を節約し、不要な変更のステージングエリアへの追加を避けるのに有効です。

個々のファイルをステージングできることはご存知かもしれませんが、ディレクトリを指定することもできます。たとえば「new-feature」ディレクトリ内のすべてのファイルをステージングエリアに追加する方法は以下のとおりです。

git add new-feature

また、git addの結果を、そのプロセスを完全に実行することなく確認したいとします。これを実現する方法もあります。

git add --dry-run

git add -n

これにより、ファイルが追加されるのか無視されるのかがわかります。無視されたファイルについては、それをステージングエリアに追加する方法もあります。

git add --force

git add -f

ステージングエリアにファイルを追加するのは、単純な「追加するかしないか」という判断よりも複雑になり得ます。このように、Gitの基本的なコマンドを使って、無数の条件をカバーすることができます。

まとめ

便利なGitコマンドを身につければ、プロジェクトの通常のバージョン管理作業に必要なものの80%は揃ったことになります。最後の20パーセントは、高度なGitコマンドが輝く場所です。

rebase、bisect、ファイルの復元などのコマンドやテクニックは、多くの人を窮地から救うことでしょう。これを活用することで、チームやプロジェクトにより大きな価値を届け、ワークフローを合理化し、生産性を高め、開発者としての成長が加速するはずです。

今回ご紹介したGitコマンドの数々は、あなたの日々の仕事の一部になることができそうでしょうか?以下のコメント欄でお聞かせください。

Jeremy Holcombe Kinsta

Kinstaのコンテンツ&マーケティングエディター、WordPress開発者、コンテンツライター。WordPress以外の趣味は、ビーチでのんびりすること、ゴルフ、映画。高身長が特徴。