Резюме
По умолчанию 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
.