私はGitが大好きで、いつでもGitを使っています。私は時々、何かについて深く調べてみたり、ドキュメントを一通り読んでみたり、設定を見直してみたりするのですが、今回はGitについてそれをやってみました。私の書いた4番目の技術スタックの改善に関する記事にようこそ!
Gitのすべて
私がコーディングを始めたのは、ただのファイルシステム上でコピーしていたあの辛い日々、そしてチェックアウトに排他的ロックが必要だったVisual SourceSafeを使っていた時でした。それでもその時、ソース管理のコンセプトは私にとって素晴らしいものに思えましたし、家でコーディングする時にはそういったものにアクセスできたらな、と思っていました。
その後カリフォルニアポリテクニック州立大学(Cal Poly)で、グループプロジェクトに関わる中でConcurrent Versions System (CVS)に出会いました。限られたグループプロジェクトしかなかったので、あまり得意になったわけではありませんでした。
マイクロソフトで働いている間、プロダクトが出荷できるものか確認するためにVisual Studioの開発者でない人間が丸々1週間を使ってドッグフーディングすると言う「App Week」と呼ばれる期間中、ソース管理のためにTeam Foundation Serverを使いました。しかし、その間に個人的なプログラミングの全てはSubversionと共にありました。無料で、しかも簡単にローカルで動かせたからです。ローカルでの変更の全てを記録するのに使っていました。
それが変わったのは、2010年の秋のことでした。サイドプロジェクトを開発するためにRuby on Railsを学習し、私が読んだその総合的なチュートリアルは、Herokuと新しいバージョン管理システムである Git を取り上げていました。Gitを使えば、ローカルにホストしているように扱いつつも、同時に他の人ともやり取りできるという点で、素晴らしいものでした。排他的ロックもないし、オフラインでも生産性が落ちないし、賢いマージをしてくれる。私はGitに夢中になりました。
それからGitはまさに人気を得ています。オープンソースにとっては事実上の標準です。主要などのクラウドソースコードホスティングサービスにもサポートされています。専用のソース管理ツールからコードエディターまで、多くのGUIツールにもサポートされています。
Gitは、知っておくべき、そしてちゃんと知っておくべきものなのです。
グローバル設定
知っているかどうかに関わらず、あなたはもうグローバルなGitの設定を使っています。それは、ホームディレクトリの .gitconfig
ファイルです。ほとんどの .gitconfig
には、よくある「Gitを始める」チュートリアルで作られた通り、あなたの名前とメールアドレスが書かれているでしょう。しかし、このファイルにはとてもたくさんの設定が使えるのです!
私の グローバルな .gitconfig
の中身はコメント付きで全てgistに置いてあります。直接このファイルを見てみてもよいですが、ここではもう少し詳しくそれぞれのセクションについて取り上げてみたいと思います。
エイリアス
.gitconfig
を特別仕様にするのになくてはならないのが、独自のコマンドを作れてしまう [alias]
セクションです。何か機能が不足していると思いますか?ここに追加してしまいましょう!いまいち使いたいように動いてくれない機能がある?ここに自分専用バージョンを追加しましょう!
prune = fetch --prune
- 他の人がメインリポジトリーにブランチをプッシュするプロジェクトで仕事している時は、たくさんの色々なローカルブランチに遭遇することになります。 pruneは、リモートで削除済みのローカルブランチを全て削除します。ここに追加するのは、私がいつもこれを忘れてしまうからです。undo = reset --soft HEAD^
- コミットで間違いをしてしまった時には、このコマンドがコミット前の状態を復元してくれます。ただし、通常私はこのような場合、既にしてしまったコミットをamend(修正)します。これはコミットメッセージを残してくれるからです。stash-all = stash save --include-untracked
- Stashは、他のブランチで作業中にも関わらず誰かがあちこちのブランチをチェックアウトしてくれと依頼してくるような時にとても便利です。このコマンドを使うと、stashした時、まだgit add
でトラックしていない新しいファイルも一緒にstashしてくれます。glog = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'
- デフォルトのgit log
コマンドで表示されるコミット履歴はスペースの効率が悪く、重要な情報に焦点が当たっていません。このコマンドのカラフルでグラフィカルな表示は、ブランチが複雑な時などにもパースしやすくなっています。
マージ
ff = only
は、すべてのマージがfast-forwardでない限りエラーを出してくれるようにするものです。マージコミットを作らず、2つの履歴をひとつにすることもなく、コミットからコミットへスムーズに進んで行くようにします。
これでどうやって仕事を進めるのか疑問に思った人もいるでしょう!その答えは、コミットツリーの分岐を持ってきて違う場所に付け替え、新しい「ベース」を作ってくれる git rebase
です。これは非常に便利なツールです。
これが、 master
とコンフリクトが起きた時にプルリクエストを更新する場合の私のやり方です。これはつまり、他の人が変更を加えた未来の世界に、自分のコミット全部を適用するチャンスを得たということです。最新の master
を自分のブランチにマージする時の「他人の変更を自分の変更にマージする」という比喩よりもこちらの方が私はいいと考えています。
このオプションは素晴らしくて、これでうっかりマージコミットを作ってしまうということがなくなります。マージコミットを意図的に作りたいなら、 git merge --ff
を明示的に指定して強制的に作ることが可能です。
conflictstyle = diff3
は、マージのコンフリクトが発生した時に少し多めの情報が得られます。通常は、「左から」の変更と「右から」の変更という2つのセクションが表示されます。しかしこのオプションを使うと、「左」と「右」が変更しようとする前の元々の状態を表示する3番目のセクションが表示されます。
コミット
gpgSign = true
は、全てのコミットがGPGキーで署名されるようにしてくれます。 .gitconfig
ファイルの中にあるユーザー情報は何の証明もされておらず、あなたのものに見えるコミットが、他の誰かのプルリクエストに現れると行ったことが簡単に起こり得てしまうので、このような設定をするのはよい考えです。
私は一度、アカウントやマシンの準備に時間がかかりすぎるという理由で、他の人の認証情報を使わなくてはならないことがありました。私のプルリクエストは他の人のアカウントから作られたのですが、そこに含まれるコミットの全ては、私の本名が書かれていたというわけです。パブリックなGPGキーをGitHubアカウントに登録することで、コミットがどこからきたものなのかに対する疑問を消し去ることができます。署名されたコミットには、小さな"Verified"バッジが付きます。
注意 :
- 複数のGPGキーを持っている場合は、
user.signingKey
オプションでどれを使うかを指定できます。 - GUIアプリケーションを使っているときには、コミットに署名するのにこのオプション群を使う必要はありません。アプリケーションのオプションを調べてみましょう。
gpg-agent
で、パスフレーズを入力する時間を節約できるので、作業がちょっと楽になります。使いましょう!
プッシュ
default = simple
は、もう既に設定済みのオプションかもしれません。これで、ローカルブランチをリモートの同名のブランチにプッシュするのが簡単になります。
followTags = true
もとても簡単です。新しいタグを --follow-tags
を付けて手動でプッシュする代わりに、 git push
すると一緒にローカルのタグを送ってしまえるのです。あなたはどうか分かりませんが、私はローカルで作ったタグがあったら、プッシュする時に全部含まれてて欲しいんです。
ステータス
showUntrackedFiles = all
普通、新しいディレクトリーを追加したけれどもまだ git add
していない時には、 git status
は単にディレクトリー名だけを表示するはずです。たくさんのファイルを含む新しいディレクトリを作ったのに1行しか表示されなくて、私はこれで失敗したことが何度かあります。このオプションは、このような新しいディレクトリーは以下のファイル全てを git status
コマンドで表示するようにします。
注意 : 非常に大きいディレクトリーの場合、これによって動作が遅くなる可能性があります。
転送
fsckobjects = true
は、変更の受信あるいは送信の際に追加のチェックを行いたいということをGitに伝えるためのものです。どうして追加チェックが必要なのでしょうか?データの破損を、あとではなく近いうちに警告されたいと思うのではないでしょうか?
注意 : これにより転送が少し遅くなる可能性があります。
diffツール : icdiff
ビルトインの git diff
コマンドに加えて、Gitではdiffをビジュアライズしてくれる外部ツールを使えるようになっています。以下の一連の設定は、リポジトリー内の2つの状態の違いを表示する icdiff
を設定するものです。
[diff]
tool = icdiff
[difftool]
prompt = false
[difftool "icdiff"]
cmd = /usr/local/bin/icdiff --line-numbers $LOCAL $REMOTE
これで、普通通り git difftool master branch
で使えます。
icdiff
は、カラフルでGitHub風の分割型のdiffを、コンソール上に再現しようとするのが面白いところです。通常のチャンクベースのdiffの表示よりも、ちょっと読みやすいでしょう。
注意 :
- icdiff
のインストールがちょっと難しいかもしれません。ラッキーなことに、簡単な回避策があります。
- git diff
は残したままにしておきましょう。 icdiff
は、 /dev/null
との比較は扱えないようです。例えば、新しいファイルをステージした後に git difftool --cached
してみましょう。
ボーナス : もっとリビジョンを!
いつも git checkout master
実行してますよね?このコマンドでは、 master
はリビジョンの一例であり、特に master
ブランチの最新のコミットを指す完結表現です。よくあるリビジョン表現があります。
# ブランチのチェックアウト
git checkout branchname
# リモートブランチのチェックアウト
git checkout remotes/origin/branchname
# 特定のコミットのチェックアウト
git checkout 158e4ef8409a7f115250309e1234567a44341404
# 現在のブランチの最新コミットのチェックアウト
git checkout HEAD
しかしここで、リビジョンを指定する完全な表現があることに気づきます。それぞれの記号は、上の例の目的語部分に対応します。
# ^ は「最初の親コミット」なので、これはmasterブランチの2番目に新しいコミット
git checkout master^
# これが2つ以上の親を持つマージコミットなら、これは2番目の親を取得
git checkout master^2
# 3回 ^ を繰り返すのと同じ。「最初の親」を3回さかのぼる
git checkout master~3
# 1日前の最初のコミット
git checkout master@{yesterday}
# 5分前の最初のコミット
git checkout master@{5.minutes.ago}
サポートされているリビジョンのフォーマット全部の一覧はここにあります https://git-scm.com/docs/gitrevisions なんと広範囲に渡るのかと驚きました!
多くのGitコマンドでリビジョンが使えることを覚えておきましょう。例えば git log master@{10.days.ago}..master
を試してみましょう。
使ってみよう!
私の .gitconfig
から始めるか、あるいはそれを自分のカスタマイズの手がかりに使ってみましょう。私がやったようにディーブダイブすることもできます。コマンドリストやオプション一覧はとてもためになります。
Gitが最大限便利になるようにしましょう。Gitをゆるく使うと、辛いバグや現実のマージコンフリクトの余地を残してしまいます。さあ調べてみましょう!
その他の参考になる資料 :
- 多くの設定オプションはプロジェクトごとに適用するものです。プロジェクト特定の設定ファイルは、同じフォーマットを使いましょう:
project-dir/.git/config
git log
の全てのオプションの最新情報を追いましょう https://git-scm.com/docs/git-log#_options- 色々なGitのリビジョン表記を調べてみましょう https://git-scm.com/docs/gitrevisions
- コミットの署名について調べましょう https://mikegerwitz.com/papers/git-horror-story
- 趣味と実益を兼ねてエイリアスをカスタマイズしましょう https://hackernoon.com/lesser-known-git-commands-151a1918a60
- リポジトリの破損についての詳しくは