У яких випадках `git pull` може бути шкідливим?


409

У мене є колега, який стверджує, що git pullце шкідливо, і засмучується, коли хтось ним користується.

git pullЗдається, що ця команда є канонічним способом оновлення локального сховища. Чи використання git pullпроблем створює проблеми? Які проблеми це створює? Чи є кращий спосіб оновити сховище git?



8
Або ви можете просто git pull --rebaseвстановити цю стратегію за замовчуванням для нових гілок git config branch.autosetuprebase
knoopx

4
knoopx має право, додаючи --rebaseпрапор для git pullсинхронізації локального з віддаленим, а потім відтворює ваші локальні зміни поверх оновлених локальних. Потім, коли ви натискаєте все, що ви робите, додаєте свої нові зобов’язання до кінця пульта. Досить просто.
Хіт Ліллі

4
Дякую @BenMcCormick. Я вже робив це, але дискусія щодо обґрунтованості питання, мабуть, ведеться в цих коментарях нижче питання. І я думаю, що задавати питання, щоб створити платформу для представлення вашої особистої думки як фактично, це не те, чим насправді є питання Q & A.
mcv

4
@ RichardHansen, це просто здається способом обдурити точкову систему, особливо якщо ваша відповідь має таку різку різницю в тону і такий короткий проміжок часу. Використовуючи вашу модель запитань та запитань, ми всі могли просто задавати питання та відповідати на них самостійно, використовуючи попередні знання. У цей момент вам слід просто розглянути можливість написання публікації в блозі, як це в багато разів доречніше. Питання та відповіді спеціально шукають знання інших людей. Повідомлення в блозі демонструє своє.
Джош Браун

Відповіді:


546

Підсумок

За замовчуванням 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створює комісію злиття.

Немає нічого поганого в об'єднанні, але вони можуть бути небезпечними і до них слід ставитися з повагою:

  • Об'єднання об'єднань по суті важко вивчити. Щоб зрозуміти, що таке злиття, потрібно зрозуміти відмінності для всіх батьків. Звичайна різниця не передає цю багатовимірну інформацію добре. На відміну від цього, ряд нормальних комісій легко переглядати.
  • Вирішення конфлікту об'єднань є складним, і помилки часто залишаються невизначеними протягом тривалого часу, оскільки об'єднання об'єктів важко переглядати.
  • Злиття можуть спокійно витіснити ефекти регулярних комітетів. Код більше не є сумою поступових скоєнь, що призводить до непорозумінь щодо того, що насправді змінилося.
  • Об'єднання об'єднань може порушити деякі схеми безперервної інтеграції (наприклад, автоматично будувати лише перший вихідний шлях відповідно до прийнятої конвенції, згідно з якою другі батьки вказують на незавершене виконання роботи).

Звичайно, є час і місце для злиття, але розуміння того, коли злиття слід і не слід використовувати, може покращити корисність вашого сховища.

Зауважте, що мета 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 pullвведений комітет злиття . Це загальноприйнята досить того, що Git має деякі параметри конфігурації , щоб зменшити ці два кроки за один крок, розповівши git pullвиконати перебазування замість злиття (див branch.<branch>.rebase, branch.autosetuprebaseі pull.rebaseваріанти).

На жаль, якщо у вас є незапущена команда злиття, яку ви хочете зберегти (наприклад, фіксація, що об'єднує відгалужену гілку функції master), ні перетягування бази даних ( git pullз branch.<branch>.rebaseвстановленим на true), ні витягнення злиття ( git pullповедінка за замовчуванням ), за яким слідує a Rebase буде працювати. Це тому, що git rebaseусуває злиття (це лінеаризує DAG) без --preserve-mergesопції. Операція витягування бази даних не може бути налаштована для збереження злиття, і злиття, яке слідує за нею, git rebase -p @{u}не усуне злиття, викликане злиттям. Оновлення: 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.


6
@brianz: git remote update -pеквівалентно git fetch --all -p. Я звик вводити текст, git remote update -pтому що колись fetchне було -pможливості. Щодо провідних !, дивіться опис alias.*в git help config. У ньому сказано: "Якщо розширення псевдоніму встановлено префіксом оклику, воно буде розглядатися як команда оболонки".
Річард Хансен

13
Git 2.0 додає pull.ffконфігурацію, яка, як видається, досягає того самого, без псевдонімів.
Денні Томас

51
Деякі причини, які звучать як "тягнення, можуть спричинити проблеми, коли інші роблять божевільні речі". Ні, це божевільні речі, такі як звільнення комісії з репо за течією, що викликає проблеми. База даних IMO безпечна лише тоді, коли ви робите це локально на комітеті, який ще не було натиснуто. Як, наприклад, коли ви натягуєте перед тим, як натиснути, повторне використання локальних комісій допомагає зберігати історію лінійною (хоча лінійна історія не така вже й велика угода). І все-таки це git upзвучить як цікава альтернатива.
mcv

16
Більшість ваших пунктів пов’язані з тим, що ви робите щось не так: ви намагаєтесь переглянути код у власній робочій галузі . Це не гарна ідея, просто створіть нову гілку, витягніть --rebase = збережіть, а потім киньте цю гілку (або об'єднайте її, якщо хочете).
funkaster

5
Точка @ funkaster тут має багато сенсу, особливо це стосується: "Складність перегляду комітетів інших людей". Більшість користувачів Git - це не оглядний потік, це те, чого я ніколи не бачив, і ніде не рекомендував, і це є причиною всієї непотрібної додаткової роботи, описаної під заголовком git pull.
Бен Регенспан

195

Моя відповідь, витягнута з дискусії, що виникла на HackerNews:

Я відчуваю спокусу просто відповісти на питання, використовуючи Закон про Betteridge of Headlines: Чому це git pullвважається шкідливим? Це не так.

  • Нелінійності не є по суті поганими. Якщо вони представляють фактичну історію, вони в порядку.
  • Випадкове повторне введення комітетів, що перезавантажуються вгору за течією, є результатом неправильного переписування історії вище за течією. Ви не можете переписати історію, коли історія копіюється протягом декількох репост.
  • Зміна робочого каталогу - очікуваний результат; спірної корисності, а саме в умовах поведінки hg / monotone / darcs / other_dvcs_predating_git, але знову ж таки не по суті погано.
  • Призупинення для перегляду роботи інших людей потрібно для злиття, і це знову очікувана поведінка під час руху git. Якщо ви не хочете зливатися, вам слід скористатися git fetch. Знову ж таки, це ідіосинкразія git порівняно з попередніми популярними dvcs, але це очікувана поведінка і не по суті погано.
  • Це важко перезавантажити відновлення віддаленої гілки. Не переписуйте історію, якщо вам абсолютно не потрібно. Я не можу за все життя зрозуміти це прагнення до (підробленої) лінійної історії
  • Не прибирати гілки - це добре. Кожен репо знає, що хоче провести. Гіт не має поняття відносин між господарем і рабом.

13
Я згоден. У цьому немає нічого по суті шкідливого git pull. Однак це може суперечити деяким шкідливим практикам, як, наприклад, бажати переписати історію більше, ніж суворо потрібно. Але git є гнучким, тому, якщо ви хочете використовувати його по-іншому, будь-ласка, зробіть це. Але це тому, що ви (ну, @Richard Hansen) хочете зробити щось незвичне в git, а не тому git pull, що шкідливо.
mcv

28
Не можу погодитися більше. Люди виступають за git rebaseта вважають git pullшкідливим? Дійсно?
Віктор Мороз

10
Було б добре бачити, як хтось створює графік з мораллю як вашу вісь і класифікує команди git як добрі, погані чи десь посередні. Ця діаграма відрізнятиметься від розробників, хоча вона б багато говорила про використання git.
michaelt

5
Моя проблема git pullбез --rebaseопції - це напрямок злиття, який він створює. Якщо ви подивитесь на різницю, всі зміни цього злиття тепер належать людині, яка витягнула, а не людині, яка внесла зміни. Мені подобається робочий процес, де злиття зарезервоване для двох окремих гілок (A -> B), так що фіксація злиття зрозуміла, що було введено, а повторне звільнення зарезервоване для отримання оновлених даних у тій самій гілці (віддалений A -> локальний A )
Крейг Кочіс

4
То з чого ви отримуєте знання, якщо хтось здійснив тяг лише за кілька секунд перед кимось іншим чи навпаки? Я думаю, що це просто шум і просто затьмарює дійсно релевантну історію. Це навіть зменшує цінність історії. Гарна історія повинна бути а) чистою та б) насправді мати важливу історію.
Девід Онгаро

26

Це не вважається шкідливим, якщо ви правильно використовуєте Git. Я бачу, як це негативно впливає на вас, враховуючи випадки використання, але ви можете уникнути проблем, просто не змінюючи спільну історію.


10
Щоб детальніше зупинитися на цьому: Якщо кожен працює над власною гілкою (що, на мою думку, є правильним способом використання git), git pullце не є жодним питанням. Відгалуження в git дешево.
AlexQueue

18

Прийнята відповідь стверджує

Операція витягування бази даних не може бути налаштована для збереження злиття

але щодо Git 1.8.5 , який розміщує цю відповідь, ви можете зробити

git pull --rebase=preserve

або

git config --global pull.rebase preserve

або

git config branch.<name>.rebase preserve

У документах говорять

Після цього preserve,перейдіть --preserve-mergesдо "git rebase", щоб локально здійснені об'єднання об'єднань не були згладжені запуском "git pull".

Це попереднє обговорення має більш детальну інформацію та діаграми: git pull --rebase - збереження-злиття . Це також пояснює, чому git pull --rebase=preserveце не те саме git pull --rebase --preserve-merges, що не робить правильно.

Ця інша попередня дискусія пояснює, що насправді робить варіант збереження-злиття ребазу та наскільки це набагато складніше, ніж звичайний ребаз: Що саме робить "rebase - консервація-злиття" git (і чому?)


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.