機能別のブランチ(フィーチャーブランチ)をマージする
大きなプロジェクトの開発者にとって、フィーチャーブランチ(mainを除く)上で全ての作業を行い、完成したら一度でその作業を統合するというような流れが一般的です。これは前のレッスンの内容(他のブランチからリモートにプッシュされるような状況のところが)に似ていますが、ここではもう一歩踏み込んで解説しましょう。
開発者は、mainブランチにいるときプッシュとプルしかしません -- mainは常にリモート(o/main)に追従した状態のままにします。
この作業の流れでは、私たちは二つのことを組み合わせています:
mainにフィーチャーブランチの作業を統合し、
リモートへのpushとpullを行う
git fetch
git rebase o/main side1
git rebase side1 side2
git rebase side2 side3
git rebase side3 main
git push
なぜマージではいけないのか?
新しい更新をリモートにプッシュするため、あなたがする必要があるのはリモートからの最近の変更の組み込みです。それは、リモートブランチ(例えば、o/main)にリベースかマージのどちらかをあなたがする必要があるということを意味します。
もしどっちの方法でも行うことができるなら、なぜこれまでのレッスンでは、リベースに焦点を当ててきたのでしょう?リモートへの作業で、なぜmergeを推してこなかったのでしょうか?
開発コミュニティで、マージとリベースの間でのトレードオフについては多くの議論がなされています。ここでは一般的なリベースのメリット/デメリットを紹介しましょう:
メリット:
リベースは全てが直線上にあるので、あなたのコミットツリーをとても綺麗にみせます。
デメリット:
リベースは、コミットツリーの(見ため上の)履歴を改変してしまいます。
例えば、C1コミットはC3コミットの後ににリベースすることができます。現実とは逆に、C1'の作業がまるでC3の後に行われたものであるかのように見えるようになります。
開発者によっては、履歴をそのまま保持するのを好むので、マージを選びます。他の人は(例えば私は)きれいなコミットツリーを好むのでリベースを選びます。つまるところ、好みの問題というわけですね :D
git checkout main
git pull
git merge side1
git merge side2
git merge side3
git push
リモートトラッキングブランチ
もしかしたら直近の幾つかの章で、「魔法」の様に見えた現象があるかもしれません:gitがmainブランチはo/mainに関連していることを知っていたということです。確かにこれらのブランチは似た名前を持っていて、リモートのmainブランチとローカルのmainブランチを繋ぐ論理的な意味を成すかもしれません。以下の2例がこれらブランチが明確に繋がっている事を示します:
プルの実行時は、コミットをo/main上にダウンロードし、mainブランチにそれをマージします。マージのターゲットはこの繋がりから求められます。
プッシュの実行時は、mainブランチの作業はリモートのmainブランチにプッシュされます(その後にo/mainによってローカルに反映されています)。プッシュ先の決定は、mainとo/mainの繋がりから求められます。
リモートトラッキング
端的に言えば、mainとo/mainの繋がりの正体はそれぞれのブランチの"remote traking"というプロパティです。mainブランチはo/mainに追跡するように設定されているのです。これは、mainブランチのための暗黙のプッシュ先と暗黙の取り込み先が存在することを意味します。
特にそのような設定を行うコマンドを走らせていないのに、mainブランチにこのプロパティが設定されていたことに疑問を持つかもしれません。そう、gitによってリポジトリをクローンした時、gitはこのプロパティを自動的に設定してくれるのです。
クローンしている間、gitはリモートブランチをリモートリポジトリのブランチ全てに対して作ります(o/mainのように)。その後、リモート上でアクティブなブランチを追跡するローカルブランチを作成します。多くの場合それはmainブランチになります。
gitのクローンが完了した時、一つのローカルブランチしか存在しません(なので、情報量に圧倒される事はありません)。しかし、全てのリモートのブランチを見ることもできるのです(もしあなたが十分な好奇心を持っていれば、ですが)。いわゆるwin-winの関係ですね!
クローン中に次のような出力が表示されることの説明にもなりますね:
local branch "main" set to track remote branch "o/main"
自分でトラッキング元を設定できますか?
はい、できます!o/mainを追跡するブランチを作成できますし、そのブランチはmainと同じ暗黙のプッシュ先とマージターゲットを持ちます。例えばtottallyNotMainという名前のブランチでgit pushを走らせ、リモートのmainブランチにプッシュするといったことができるということを意味しています!
このプロパティを設定するには2つの方法があります。一つ目は、リモートブランチをリファレンスとして新しいブランチをチェックアウトするというものです。例えば
git checkout -b totallyNotMain o/main
を実行する事でtotallyNotMainという名前のブランチを新しく作り、o/mainへの追跡プロパティを設定します。
二番目の方法
ブランチのリモートトラッキングを設定するもう一つの方法は、単にgit branch -uオプションを使うというものです。例えば
git branch -u o/main foo
を実行する事でfooブランチがo/mainを追跡するように設定できます。もし、fooが現在チェックアウトしているブランチだった場合、以下のように省略することができます:
git checkout -b side o/main
git commit
git pull --rebase
git push
Git pushの引数
次は git push、fetch、pull 動作の謎を追っていきましょう。一つずつ見ていきますが、各コマンドのコンセプトは非常に似ています。
まずは、git pushからです。リモートトラッキングブランチのレッスンでは、gitは現在チェックアウトされているブランチのプロパティを参照して、pushするリモートとブランチを決めるということを学びました(そのリモートは、ブランチの追跡対象でもあります)。これは git push の引数が指定されていない場合の動作ですが、git push は次の引数も取ることができます。
git push <remote> <place>
パラメータとは何でしょうか?説明の前に、まずは例をみてみましょう。
git push origin main
これを日本語訳すると、次のようになります。
リポジトリの "main" ブランチに移動し、
すべてのコミットを取得してから、
リモートの "origin" にある "main" ブランチへ移動します。
存在していないコミットをリモートのリポジトリに反映させて、
完了したら私に教えて下さい。
"place"引数にmainを指定することで、コミットがどこから来てどこへ行くのかをgitに伝えました。この"place"引数は基本的に、2つのリポジトリ間で同期する場所の名前です。
なお、git に(2つの引数を指定して)必要なことを伝えたので、現在チェックアウトされているブランチを無視することに注意してください。
引数を指定しなかった場合
このレベルではfooとmainの両方をリモートに更新してみましょう。ここではgit checkoutが無効になっているので注意してください。
注:origin/ラベルがUIに収まらないため、リモートブランチにはo/が付いていますが心配しないでください。 これについては... 通常のようにリモートの名前にoriginを使用してくださいね。