1. ホーム
  2. ギット

[解決済み】Gitのワークフローとrebaseとmergeの質問

2022-03-23 12:46:30

質問

私は今、もう一人の開発者とともにあるプロジェクトで数ヶ月間Gitを使っています。私は数年にわたる SVN だから、私はこの関係に多くの荷物を持ってきたのだと思います。

Gitはブランチやマージに優れていると聞きますが、今のところ、私にはそれがわかりません。確かにブランチは非常にシンプルですが、マージをしようとすると、すべてが地獄のようになります。SVNで慣れてはいるのですが、下位のバージョン管理システムを別のものと交換したようなものです。

私のパートナーは、私の問題は、やみくもにマージをしたいからであり、多くの状況でマージの代わりにリベースを使うべきだと教えてくれました。例えば、彼が作ったワークフローはこんな感じです。

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature
git checkout master
git merge my_new_feature

基本的には、機能ブランチを作成し、常に master からそのブランチにリベースし、ブランチから master にマージします。重要なのは、ブランチは常にローカルのままであるということです。

以下は、私が始めたワークフローです。

clone remote repository
create my_new_feature branch on remote repository
git checkout -b --track my_new_feature origin/my_new_feature
..work, commit, push to origin/my_new_feature
git merge master (to get some changes that my partner added)
..work, commit, push to origin/my_new_feature
git merge master
..finish my_new_feature, push to origin/my_new_feature
git checkout master
git merge my_new_feature
delete remote branch
delete local branch

本質的な違いは2つある(と思う)。リベースの代わりに常にマージを使うこと、そして機能ブランチ (と機能ブランチのコミット) をリモートリポジトリにプッシュすることです。

リモートブランチを使う理由は、作業中に自分の作業のバックアップを取りたいからです。私たちのリポジトリは自動的にバックアップされ、何か問題が発生しても復元することができます。私のラップトップはそうではありませんし、それほど徹底していません。ですから、私のノートパソコンに、どこか他の場所にミラーリングされていないコードがあるのは嫌なのです。

rebaseではなくmergeにした理由は、mergeは標準機能、rebaseは高度な機能のように思われるからです。私の直感では、私がやろうとしていることは高度な設定ではないので、リベースは不要なはずです。新しいPragmatic Programming book on Gitを熟読しましたが、彼らはmergeを広範囲にカバーしており、rebaseについてはほとんど触れていませんでした。

とにかく、私は最近のブランチで自分のワークフローに従っていたのですが、それをmasterにマージしようとしたら、すべてが地獄に落ちました。どうでもいいようなことが大量にコンフリクトしていたのです。そのコンフリクトは、私にとって意味不明なものでした。最終的にはリモートマスターに強制的にプッシュすることになりました。ローカルマスターはすべてのコンフリクトを解決しましたが、リモートマスターはまだ満足していませんでした。

このような場合、どのようなワークフローが正しいのでしょうか?Gitはブランチやマージをとても簡単にできるはずですが、私にはそれがわかりません。

更新 2011-04-15

これは非常に人気のある質問のようなので、最初に質問してから2年間の経験を更新しようと思いました。

少なくとも私たちの場合は、当初のワークフローが正しいことが判明しました。言い換えれば、これが私たちがやっていることであり、うまくいっていることなのです。

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge my_new_feature

実は、私たちのワークフローは少し違っていて、次のようなことをすることが多いのです。 スクワッシュマージ を、生マージではなく ( 注:これには賛否両論があります。 ) これにより、機能ブランチ全体を master 上の単一のコミットにすることができます。そして、その機能ブランチを削除します。こうすることで、ブランチでのコミットが少々乱雑でも master でのコミットを論理的に構成することができます。というわけで、このようにします。

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge --squash my_new_feature
git commit -m "added my_new_feature"
git branch -D my_new_feature

スクワッシュ・マージ論争 - 何人かのコメントで指摘されているように、squash マージはあなたの feature ブランチの履歴をすべて捨ててしまいます。その名のとおり、すべてのコミットをひとつにまとめてしまうのです。小さな機能であれば、これはひとつのパッケージに凝縮されるので理にかなっています。しかし、大規模な機能の場合は、特に個々のコミットがすでにアトミックである場合には、この方法はあまり良いアイデアとは言えません。結局は個人の好みになります。

GithubとBitbucket(その他?)のプルリクエスト - merge/rebase と Pull Requests の関係を知りたい方のために補足すると、master にマージする準備ができるまで、上記のすべてのステップを実行することをおすすめします。git で手動でマージする代わりに、PR を受け入れるだけです。しかし、Pull Request コミュニティでは (私が知る限り) Squash や Fast Forward を使わないのが一般的なマージのやり方です。具体的には、このように動作します。

clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git push # May need to force push
...submit PR, wait for a review, make any changes requested for the PR
git rebase master
git push # Will probably need to force push (-f), due to previous rebases from master
...accept the PR, most likely also deleting the feature branch in the process
git checkout master
git branch -d my_new_feature
git remote prune origin

私はGitが大好きになり、もうSVNには戻りたくないと思っています。もしあなたが苦労しているのなら、ただそれを続けることです。やがてトンネルの先に光が見えてくるでしょう。

解決するには?

コンフリクトとは、同じコンテンツが並行して進化していることを意味します。つまり、マージ中に「地獄に落ちる」ようなことがあれば、それは同じファイルのセットで大量の進化を遂げたことを意味します。

では、なぜリベースがマージより優れているかというと、次のような理由があるからです。

  • ローカルのコミット履歴をマスターのものに書き換える(そして自分の作業を再適用し、そのときに衝突を解決する)。
  • 最終的なマージは、マスターのすべてのコミット履歴に加え、自分の変更点のみを再適用することになるので、確かに "fast forward" なマージとなります。

この場合の正しいワークフロー(共通のファイルセットに対するエボリューション)は、以下の通りであることを確認しました。 最初にリベース、次にマージ .

しかし、これはつまり、(バックアップのために) 自分のローカルブランチをプッシュした場合、そのブランチを他の人がプルしてはいけない (少なくとも使ってはいけない) ということです (連続したリベースでコミット履歴が書き換えられてしまうからです)。


そのトピック(リベース→マージのワークフロー)について。 バーラポント が、コメントで2つの興味深い投稿に触れています。 ランディフェイ・ドットコム :

<ブロッククオート

このテクニックを使えば、あなたの仕事は常に最新のパッチのように公開ブランチの上に置かれることになります。 HEAD .

(類似技法 bazaarに存在する )