Gitは便利なバージョン管理ツールです。この分野では向かうところ敵なしでしょう。あらゆる日常的な作業をGitのコマンドの繰り返しで簡単に行うことができます。しかし、基本以上のことが必要になる場面もたくさんあります。そんなわけで、Gitスキルを次のレベルに引き上げる高度なGitコマンドを知っておくと便利です。
また、普段はあまり使うことのない様々なGitの概念も存在します。例えば、変更をコミットするのとは対照的なstash、その他にもrebaseなどがあります。これらを学べば、あなたのチームにおける価値がさらに高まるかもしれません。
今回の記事では、一歩進んだコマンドをいくつかご紹介します。ただし使いこなすには、特定のスキルやコンセプトの理解などが必要です。
推奨される前提条件
Gitの基本を学び、git delete
のような中級コマンドは理解できるかもしれませんが、高度な操作を行えるようにもっと多くの選択肢があると便利です。
バージョン管理全般が初めての場合は、レパートリーを増やす前に基本的なコマンドを日常的に使ってみることをお勧めします。
簡単にまとめると、理解しなければならない点は以下の通りです。
- コミット、ブランチ、プルリクエストなどGitの中核となる概念
git add
、git merge
、git commit
やgit 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ウィンドウが開き、コミットを選択して編集することができます。
画面上部にはコミットの一覧が表示され、下部にはコマンドの選択とその他の関連情報が表示されます。デフォルトのオプションはpick
で、何も変更せずに使いたいコミットとして選択します。
rebaseに役立つコマンドは他にもたくさんあります。たとえば、コミットをrewordしたり、複数のコミットをまとめたりすることができます。変更を加えたりコマンドを使用したりするには、ターミナルのエディタを使用します。デフォルトはVimであることが多いので、このエディタを知っておくのが賢明です。
最後に、変更を保存してリモートブランチにプッシュします。
2. revert、reset、unstage
Gitは、変更の取り消しに関して「悪名高い」ものです。一般的には難しい処理で、エラーを吐きがちです。しかし、ワーキングディレクトリやステージングエリアに加えた変更を元に戻したい場合は、いくつかのGitコマンドを使うことができます。
git status
を実行すると、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 --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という拡張子を削除する必要があります。
ただし、一度に実行できるのは一種類のフックだけです。そのため、ファイル名はサンプルのスクリプトをもとに使いたいフックの種類に対応させましょう。pre-commitやupdateなどです。
Gitフックの作成
Gitフックを作成するには、.git/hooksサブディレクトリに拡張子なしの実行スクリプトを作成する必要があります。このスクリプトは、hooksフォルダに追加しておけばそのまま実行できます。
実行形式であれば、スクリプト言語はどれでもかまいません。RubyかPythonをお勧めしますが、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
コマンドによって、変更についての概要が出力されます。コミットやマージを確認する出力とよく似ています。
これを細かなブランチやハッシュなどにまで掘り下げることができます。たとえば、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
は、どのファイルが違うかだけをチェックし、コンテキストは除外します。
特に「diff」が長い場合は、出力を解析するのが難しいかもしれません。このような場合は、--color-words
オプションを使用します。
全体として、git diff
は他のコマンドと同じくらい強力です。特に、特定のオプションを呼び出してどのような差分を返すかを絞り込むことができます。
11. 個々のコミットの適用
あるブランチの特定のコミットを、そのブランチをマージせずに別のブランチに適用したいこともあるかもしれません。Gitでは、git cherry-pick
を使ってこれを行うことができます。慎重に使うべきですが、git cherry-pick
を使えばいくつかの状況で効率的に作業を進められます。
そのひとつが、main
やtrunk
にマージしない古い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コマンドの数々は、あなたの日々の仕事の一部になることができそうでしょうか?以下のコメント欄でお聞かせください。
コメントを残す