私には、それが有害であると主張し、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
コマンドは、runningの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には、マージの代わりにリベースを実行するようにgit pull
指示することで、これら2つのステップを1つのステップに減らすためのいくつかの構成オプションがあることは十分に一般的です( 、、、およびオプションを参照)。git pull
branch.<branch>.rebase
branch.autosetuprebase
pull.rebase
残念ながら、保持したいプッシュされていないマージコミットがある場合(たとえば、プッシュされた機能ブランチをにマージするコミットmaster
)、rebase-pull(git pull
にbranch.<branch>.rebase
設定されているtrue
)もmerge-pull(デフォルトのgit pull
動作)の後にリベースは機能します。これは、オプションgit rebase
なしでマージを排除する(DAGを線形化する)ためです。--preserve-merges
rebase-pull操作は、マージを保持するように構成できません。また、merge-pullの後に、merge-pullgit rebase -p @{u}
によって引き起こされたマージを削除することはできません。 更新: Gitv1.8.5が追加されましgit pull --rebase=preserve
たgit config pull.rebase preserve
。これらは、アップストリームコミットをフェッチした後git pull
に実行します。git rebase --preserve-merges
(ヘッズアップをしてくれたfunkasterに感謝します!)
git pull
リモートリポジトリから削除されたブランチに対応するリモートトラッキングブランチをプルーニングしません。たとえば、誰かが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
-p
文書化されていない機能(に渡される引数)に依存しておりfetch
、Gitの将来のバージョンで変更される可能性がありますGit 2.0以降ではgit pull
、デフォルトで早送りマージのみを実行するように構成できます。
git config --global pull.ff only
これによりgit pull
、のように動作しますが、それでもすべてのアップストリームコミットをフェッチしたり、古いブランチをgit pull --ff-only
クリーンアップしたりするわけではないので、私はまだ好みです。origin/*
git up
私の答えは、HackerNewsで起こった議論から引き出されたものです。
Betteridge Law of Headlinesを使用して質問に答えたくなります:なぜgit pull
有害であると見なされるのですか?そうではありません。
Gitを正しく使用していれば、有害とは見なされません。ユースケースを考えると、それがどのように悪影響を与えるかはわかりますが、共有履歴を変更しないだけで問題を回避できます。
受け入れられた答えの主張
リベースプル操作は、マージを保持するように構成できません
しかし、その回答よりも後の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
て、ローカルでコミットされたマージコミットが「gitpull」を実行してもフラット化されないようにする場合。
この前の説明には、より詳細な情報と図があります:git pull--rebase--preserve-merges。また、なぜgit pull --rebase=preserve
がと同じではないのかを説明しgit pull --rebase --preserve-merges
ます。これは正しいことをしません。
この他の以前の議論では、rebaseのpreserve-mergesバリアントが実際に何をするのか、そしてそれが通常のrebaseよりもはるかに複雑である方法を説明しています:gitの「rebase--preserve-merges」は正確に何をしますか(そしてその理由は?)
古いgitリポジトリに移動すると、エイリアスが異なります。 https://github.com/aanand/git-up
git config --global alias.up 'pull --rebase --autostash'
これは私にとって完璧に機能します。