В каких случаях "git pull" может быть вредным?
у меня есть коллега, который утверждает, что git pull
вредно, и расстраивается, когда кто-то использует его.
на git pull
команда кажется каноническим способом обновления локального репозитория. Делает использование git pull
создать проблемы? Какие проблемы это создает? Есть ли лучший способ обновить репозиторий Git?
4 ответов
резюме
по умолчанию git pull
создает коммиты слияния, которые добавляют шум и сложность в историю кода. Кроме того, pull
легко не думать о том, как изменения могут повлиять на входящие изменения.
The 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
command и почему это было создан.) Коммиты слияния, созданные git pull
не передавайте полезную семантику другим-они просто говорят, что кто-то еще случайно нажал на репозиторий, прежде чем вы закончили с вашими изменениями. Почему эти коммиты слияния, если они не имеют смысла для других и могут быть опасны?
можно настроить git pull
перебазировать вместо слияния, но это также имеет проблемы (обсуждается позже). Вместо git pull
должен быть настроен только вперед поглощает.
повторное введение повторных коммитов
Предположим, кто-то переставляет ветку и силой толкает ее. Как правило, этого не должно происходить, но иногда это необходимо (например, удалить файл журнала 50GiB, который был случайно введен и нажат). Слияние сделано git pull
объединит новую версию восходящей ветви в старую версию, которая все еще существует в вашем локальном репозитории. Если вы нажмете результат, вилы тангажа и факелы начнут приходить ваше путь.
некоторые могут утверждать,что реальная проблема-обновления force. Да, обычно рекомендуется избегать силовых толчков, когда это возможно, но иногда они неизбежны. Разработчики должны быть готовы иметь дело с обновлениями силу, потому что они иногда случаются. Это означает не слепое слияние в старых коммитах через обычный git pull
.
Сюрприз Изменения Рабочего Каталога
нет способа предсказать, что рабочий каталог или индекс будет выглядеть до git pull
сделано. Могут возникнуть конфликты слияния, которые вам нужно разрешить, прежде чем вы сможете сделать что-либо еще, он может ввести файл журнала 50GiB в ваш рабочий каталог, потому что кто-то случайно толкнул его, он может переименовать каталог, в котором вы работаете, и т. д.
git remote update -p
(или git fetch --all -p
) позволяет просматривать коммиты других людей, прежде чем вы решите объединить или перебазировать, что позволяет сформировать план перед принятием мер.
сложности Обзор чужих коммитов
Предположим, вы находитесь в середине внесения некоторых изменений, и кто-то еще хочет, чтобы вы рассмотрели некоторые коммиты, которые они просто подтолкнули. git pull
операция слияния (или rebase) изменяет рабочий каталог и индекс, что означает, что ваш рабочий каталог и индекс должны быть чистыми.
вы могли бы использовать 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
), ни один ребаз-тянуть (git pull
С branch.<branch>.rebase
значение true
) или слияние-pull (по умолчанию git pull
поведение), за которым последует перебазирование. Это потому что git rebase
устраняет слияния (он линеаризирует DAG) без . Операция rebase-pull не может быть настроена для сохранения слияний, а операция merge-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
не обрезает ветви удаленного отслеживания, соответствующие ветвям, которые были удалены из удаленного репозитория. Например, если кто-то удаляет ветку 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
.
мой ответ, вытащил из обсуждения, что на HackerNews:
Я чувствую соблазн просто ответить на вопрос, используя закон Betteridge заголовков: почему git pull
считаются вредными? Это не так.
- нелинейности не являются изначально плохими. Если они представляют реальную историю, они в порядке.
- случайное повторное введение коммитов rebased upstream является результатом неправильного переписывания истории вверх по течению. Вы невозможно переписать историю, когда история реплицируется по нескольким репозиториям.
- изменение рабочего каталога является ожидаемым результатом; спорной полезности, а именно перед лицом поведения hg/monotone/darcs / other_dvcs_predating_git, но опять же не внутренне плохо.
- пауза для обзора работы других необходима для слияния и снова является ожидаемым поведением на git pull. Если вы не хотите объединяться, вы должны использовать git fetch. Опять же, это идиосинкразия git in сравнение с предыдущими популярными dvcs, но это ожидаемое поведение и не внутренне плохо.
- что делает его трудно перебазировать против удаленной ветви хорошо. Не переписывайте историю без крайней необходимости. Я не могу ни за что на свете понять это преследование (поддельной) линейной истории
- не очищать ветви хорошо. Каждый РЕПО знает, что он хочет провести. ГИТ понятия не имеет об отношениях хозяина и раба.
Это не считается вредным, если вы правильно используете Git. Я вижу, как это влияет на вас негативно, учитывая ваш вариант использования, но вы можете избежать проблем, просто не изменяя общую историю.
принятый ответ утверждает
операция rebase-pull не может быть настроена для сохранения слияний
но Git 1.8.5, который постдатирует этот ответ, вы можете сделать
git pull --rebase=preserve
или
git config --global pull.rebase preserve
или
git config branch.<name>.rebase preserve
на docs сказать
, когда
preserve,
и передать--preserve-merges
вместе с "git rebase", чтобы локально зафиксированные коммиты слияния не были сглажены запустив "git pull".
это предыдущее обсуждение имеет более подробную информацию и диаграммы: git pull --rebase --сохранить-слияния. Это также объясняет, почему git pull --rebase=preserve
- это не то же самое как git pull --rebase --preserve-merges
, что не делает правильную вещь.
это другое предыдущее обсуждение объясняет, что на самом деле делает вариант Reserve-merges rebase и как он намного сложнее, чем обычный rebase: что именно делает git " rebase --preserve-merges" делать (и почему?)