私はgit pull
が有害であると主張し、誰かがそれを使用するたびに怒る同僚を持っています。
git pull
コマンドはあなたのローカルリポジトリを更新するための標準的な方法のようです。 git pull
を使用すると問題が発生しますか?それはどんな問題を引き起こしますか? gitリポジトリを更新するもっと良い方法はありますか?
デフォルトでは、git pull
はマージコミットを作成し、コード履歴にノイズと複雑さを追加します。さらに、pull
を使用すると、変更が受信した変更によってどのように影響を受ける可能性があるかについて考えるのが簡単になります。
git pull
コマンドは早送りマージのみを実行する限り安全です。 git pull
が早送りマージのみを行うように設定されていて、早送りマージが不可能な場合、Gitはエラーで終了します。これはあなたに入ってくるコミットを研究し、それらがあなたのローカルコミットにどのように影響するかについて考え、そして最良の行動方針(マージ、リベース、リセットなど)を決定する機会をあなたに与えるでしょう。
Git 2.0以降では、次のものを実行できます。
git config --global pull.ff only
デフォルトの動作を早送りのみに変更します。 1.6.6と1.9.xの間のGitバージョンでは、タイピングの習慣を身に付ける必要があります。
git pull --ff-only
しかし、Gitのすべてのバージョンで、git up
エイリアスを次のように設定することをお勧めします。
git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
git up
の代わりにgit pull
を使用する。私はgit pull --ff-only
よりもこのエイリアスが好きです。
Origin/*
ブランチを削除します。git pull
に関する問題git pull
は、正しく使用されていれば悪くありません。最近のGitへのいくつかの変更はgit pull
を適切に使うことをより簡単にしました、しかし残念ながらプレーンなgit pull
のデフォルトの振る舞いにはいくつかの問題があります:
git pull
で煩わしいですこれらの問題は以下により詳細に説明される。
デフォルトでは、git pull
コマンドは、git fetch
に続けてgit merge @{u}
を実行するのと同じです。ローカルリポジトリにプッシュされていないコミットがある場合、git pull
のマージ部分はマージコミットを作成します。
マージコミットについて本質的に悪いことは何もありませんが、それらは危険になる可能性があるため、注意して扱う必要があります。
もちろんマージには時間と場所がありますが、マージをいつ使うべきか、使うべきでないかを理解することでリポジトリの有用性を高めることができます。
Gitの目的は、展開されたとおりに履歴を正確に記録するのではなく、コードベースの進化を共有および消費することを容易にすることです。 (同意しない場合は、rebase
コマンドと作成された理由を検討してください。)git pull
によって作成されたマージコミットは、他の人に有用なセマンティクスを伝えません。あなたの変更と共に。他の人にとって意味がなく、危険になる可能性があるのに、なぜそれらのマージをコミットするのでしょうか。
マージの代わりにリベースするようにgit pull
を設定することは可能ですが、これには問題があります(後述)。代わりに、git pull
は早送りマージのみを行うように設定されるべきです。
誰かがブランチをリベースして強制的にプッシュしたとします。これは通常起こるべきではありませんが、時には必要です(たとえば、誤ってコミットされプッシュされた50GiBのログファイルを削除するなど)。 git pull
によって行われたマージは上流のブランチの新しいバージョンをあなたのローカルリポジトリにまだ存在する古いバージョンにマージします。あなたが結果をプッシュするならば、ピッチフォークとトーチはあなたの方法を来始めます。
本当の問題は強制的な更新であると主張する人もいるかもしれません。はい、可能な限り強制プッシュを避けることをお勧めしますが、避けられないこともあります。開発者は強制的な更新に対処する準備をしなければなりません。なぜならそれらは時々起こるからです。これは、通常のgit pull
を介して古いコミットを盲目的にマージしないことを意味します。
git pull
が完了するまで、作業ディレクトリまたはインデックスがどのように見えるかを予測する方法はありません。他の操作を実行する前にマージ競合が発生する可能性があります。作業ディレクトリに50GiBのログファイルが誤ってプッシュされたために作業ディレクトリに名前が変更されたりする可能性があります。
git remote update -p
(またはgit fetch --all -p
)を使用すると、マージまたはリベースする前に他の人のコミットを確認して、行動を起こす前に計画を立てることができます。
あなたが何らかの変更を加えている最中で、他の誰かがあなたがそれらがちょうどプッシュしたいくつかのコミットを見直すことを望んでいると仮定しましょう。 git pull
のマージ(またはリベース)操作は作業ディレクトリとインデックスを変更します。つまり、作業ディレクトリとインデックスはクリーンでなければなりません。
git stash
を使用してからgit pull
を使用することもできますが、確認が終わったらどうしますか?元の場所に戻るには、git pull
によって作成されたマージを元に戻して、隠し場所を適用する必要があります。
git remote update -p
(またはgit fetch --all -p
)は作業ディレクトリまたはインデックスを変更しないため、段階的または段階的な変更を行った場合でも、いつでも実行しても安全です。作業中のコミットを隠したり完了したりすることを心配せずに、自分が行っていることを一時停止して他の人のコミットを確認することができます。 git pull
はあなたにその柔軟性を与えません。
Gitの一般的な使用パターンは、最新の変更を反映させるためにgit pull
を実行し、続いてgit rebase @{u}
が導入したマージコミットを排除するためのgit pull
を実行することです。 Gitがマージの代わりにリベースを実行するようにgit pull
に指示することによってこれらの2つのステップを単一のステップに減らすためのいくつかの設定オプションを持っていることは十分に一般的です(branch.<branch>.rebase
、branch.autosetuprebase
、およびpull.rebase
オプションを参照)。
残念ながら、保存したくないプッシュされたマージコミット(例えば、プッシュされた機能ブランチをmaster
にマージするコミット)がある場合、rebase-pull(git pull
を含むbranch.<branch>.rebase
をtrue
に設定)もマージプル(デフォルトのgit pull
の振る舞い)とそれに続くリベースがうまくいきます。これは、git rebase
オプションが指定されていない場合、--preserve-merges
がマージを排除する(DAGを線形化する)ためです。 rebase-pull操作はマージを保持するように設定することはできません、そしてgit rebase -p @{u}
が後に続くマージ - プルはマージ - プルによって引き起こされたマージを排除しません。 更新:Git v1.8.5にgit pull --rebase=preserve
とgit config pull.rebase preserve
が追加されました。これらは、上流のコミットを取得した後にgit pull
にgit rebase --preserve-merges
をさせます。 (ヘッドアップをしてくれた funkaster に感謝!)
git pull
は、リモートリポジトリから削除されたブランチに対応するリモートトラッキングブランチをPruneしません。たとえば、誰かがリモートリポジトリからブランチfoo
を削除しても、Origin/foo
が表示されます。
これにより、ユーザーはまだアクティブになっていると思われるために、誤って強制終了されたブランチを復活させることになります。
git up
の代わりにgit pull
を使うgit pull
の代わりに、次のgit up
エイリアスを作成して使用することをお勧めします。
git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
このエイリアスはすべてのアップストリームブランチからすべての最新のコミットをダウンロードし(デッドブランチを枝刈りして)、ローカルブランチをアップストリームブランチの最新のコミットに早送りしようとします。成功した場合、ローカルコミットは行われなかったので、マージ競合の危険性はありませんでした。ローカルの(プッシュされていない)コミットがある場合、早送りは失敗します。そのため、アクションを実行する前にアップストリームのコミットを確認する機会があります。
これはまだあなたの作業ディレクトリを予測不可能な方法で変更しますが、それはローカルの変更がない場合に限られます。 git pull
とは異なり、git up
はマージの競合を解決することを期待するプロンプトにあなたを落とすことは決してありません。
git pull --ff-only --all -p
以下は、上記のgit up
エイリアスの代替です。
git config --global alias.up 'pull --ff-only --all -p'
このバージョンのgit up
は、以前のgit up
エイリアスと同じ動作をします。
fetch
に渡される-p
引数)に依存しており、Gitの将来のバージョンでは変更される可能性があります。Git 2.0以降では、デフォルトで早送りマージのみを行うようにgit pull
を設定できます。
git config --global pull.ff only
これはgit pull
をgit pull --ff-only
のように動作させますが、それでも上流のコミットをすべて取得したり古いOrigin/*
ブランチを一掃したりするわけではないので、私はまだgit up
を好みます。
私の答えは、HackerNewsの arose という議論から引き出されました。
私は、Betteridgeの見出しの法則を使用して質問に答えるだけの誘惑に駆られます。なぜgit pull
は有害と見なされるのですか?そうではありません。
Gitを正しく使っていても害はありません。ユースケースを考えると、それがあなたに悪影響を及ぼしていることがわかりますが、共有履歴を変更しないことで問題を回避できます。
受け入れられた答えは主張する
Rebase-pull操作はマージを保持するように設定できません
しかし、 Git 1.8.5 の時点では、その答えを投稿して、あなたがすることができます
git pull --rebase=preserve
または
git config --global pull.rebase preserve
または
git config branch.<name>.rebase preserve
のドキュメント は言う
preserve,
が 'git rebase'にも--preserve-merges
を渡すと、ローカルでコミットされたマージコミットは 'git pull'を実行してもフラット化されません。
この前の議論はより詳細な情報と図を持っています: git pull --rebase --preserve-merges 。また、git pull --rebase=preserve
がgit pull --rebase --preserve-merges
と同じではない理由も説明されています。
この他の前の議論では、リベースのpreserve-mergeマージ版が実際に何をしているのか、そして通常のリベースよりもはるかに複雑であることを説明しています。 する(そしてなぜ?)
古いgitリポジトリgit upにアクセスすると、提案されているエイリアスが異なります。 https://github.com/aanand/git-up
git config --global alias.up 'pull --rebase --autostash'
これは私に最適です。