В каких случаях "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" делать (и почему?)