Резюме
По умолчанию git pullсоздает коммиты слияния, которые добавляют шум и сложность в историю кода. Кроме того, pullпозволяет легко не думать о том, как на ваши изменения могут повлиять входящие изменения.
Эта git pullкоманда безопасна, если она выполняет только быстрое слияние. Если git pullсконфигурировано только для ускоренного слияния, а ускоренное слияние невозможно, то Git завершит работу с ошибкой. Это даст вам возможность изучить входящие коммиты, подумать о том, как они могут повлиять на ваши локальные коммиты, и выбрать оптимальный порядок действий (слияние, перебазирование, сброс и т. Д.).
С Git 2.0 и новее вы можете запустить:
git config --global pull.ff only
изменить поведение по умолчанию только на ускоренную перемотку вперед. В версиях Git между 1.6.6 и 1.9.x вам придётся набирать привычку:
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потому что:
- он работает со всеми (не древними) версиями Git,
- он выбирает все ветки вверх по течению (не только ветку, над которой вы сейчас работаете), и
- он очищает старые
origin/*ветви, которые больше не существуют вверх по течению.
Проблемы с git pull
git pullНеплохо, если его правильно использовать. Несколько недавних изменений в Git упростили git pullправильное использование , но, к сожалению, поведение равнины по умолчанию git pullимеет несколько проблем:
- это вносит ненужные нелинейности в историю
- это позволяет легко случайно ввести коммиты, которые были преднамеренно перебазированы вверх по течению
- он изменяет ваш рабочий каталог непредсказуемым образом
- пауза в том, что вы делаете для проверки чужой работы, раздражает
git pull
- это затрудняет корректную перезагрузку на удаленную ветку
- он не очищает ветки, которые были удалены в удаленном репо
Эти проблемы более подробно описаны ниже.
Нелинейная история
По умолчанию git pullкоманда эквивалентна выполнению с git fetchпоследующим git merge @{u}. Если в локальном репозитории есть невыдвинутые коммиты, часть слияния git pullсоздает коммит слияния.
В коммитах слияния нет ничего плохого по своей сути, но они могут быть опасными и должны рассматриваться с уважением:
- Коммиты слияния по сути сложны для изучения. Чтобы понять, что делает слияние, вы должны понимать различия между всеми родителями. Обычный diff не очень хорошо передает эту многомерную информацию. Напротив, ряд нормальных коммитов легко просмотреть.
- Урегулирование конфликта слиянием сложно, и ошибки часто остаются незамеченными в течение длительного времени, потому что коммиты слияния трудно рассмотреть.
- Слияния могут спокойно заменить эффекты регулярных коммитов. Код больше не является суммой добавочных коммитов, что приводит к недопониманию того, что на самом деле изменилось.
- Коммиты слияния могут нарушить некоторые схемы непрерывной интеграции (например, автоматически построить только путь первого родителя в соответствии с принятым соглашением, что вторые родители указывают на незавершенные работы в процессе).
Конечно, есть время и место для слияний, но понимание того, когда слияния следует и не следует использовать, может повысить полезность вашего хранилища.
Обратите внимание, что цель Git состоит в том, чтобы упростить совместное использование и использование эволюции кодовой базы, а не точно записывать историю точно в том виде, в каком она была развернута. (Если вы не согласны, рассмотрите rebaseкоманду и то, почему она была создана.) Созданные коммиты слияния git pullне передают полезную семантику другим - они просто говорят, что кто-то случайно отправил репозиторий до того, как вы сделали свои изменения. Зачем эти коммиты слияния, если они не имеют смысла для других и могут быть опасны?
Можно настроить git pullперебазирование вместо слияния, но это также имеет проблемы (обсуждается позже). Вместо этого, git pullдолжен быть настроен только для быстрого слияния.
Реинтродукция перебазированных комитетов
Предположим, кто-то перебрасывает ветку и силой ее толкает. Как правило, этого не должно происходить, но иногда это необходимо (например, удалить файл журнала размером 50 ГБ, который был случайно обработан и отправлен). Сделанное слияние git pullобъединит новую версию восходящей ветви со старой версией, которая все еще существует в вашем локальном хранилище. Если вы нажмете результат, вилы и факелы начнут появляться на вашем пути.
Некоторые могут утверждать, что настоящая проблема заключается в принудительном обновлении. Да, обычно желательно избегать толчков, когда это возможно, но иногда они неизбежны. Разработчики должны быть готовы справиться с принудительными обновлениями, потому что они иногда случаются. Это означает, что нельзя слепо объединять старые коммиты с помощью обычных git pull.
Изменения в рабочем каталоге Surprise
Нет способа предсказать, как будет выглядеть рабочий каталог или индекс, пока не git pullбудет сделано. Могут возникнуть конфликты слияния, которые вам нужно разрешить, прежде чем вы сможете что-либо сделать, это может привести к появлению файла журнала 50 ГБ в вашем рабочем каталоге, потому что кто-то случайно его нажал, он может переименовать каталог, в котором вы работаете, и т. Д.
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выполнить перебазирования вместо слияния (см 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 не может быть сконфигурирована для сохранения слияний, и pull-merge, сопровождаемый git rebase -p @{u}not, не устранит слияние, вызванное merge-pull. Обновление: добавлен Git v1.8.5 git pull --rebase=preserveи git config pull.rebase preserve. Это git pullнужно делать git rebase --preserve-mergesпосле получения вышестоящих коммитов. (Спасибо фанкастеру за хедз-ап!)
Очистка удаленных веток
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 2.0 и новее вы можете настроить git pullпо умолчанию только ускоренное слияние:
git config --global pull.ff only
Это приводит к тому , git pullчтобы действовать как git pull --ff-only, но он все еще не извлечь все добывающие фиксации или очистить старые origin/*ветви , так что я до сих пор предпочитаю git up.