Як відновити / повторно синхронізувати після того, як хтось надсилає перебазу або скидання до опублікованої гілки?


88

Ми всі чули , що один ніколи не повинен перебазуватися опублікував роботу, що це небезпечно, і т.д. Тим НЕ менше, я не бачив ніяких рецептів , розміщених на тому , як впоратися з ситуацією в разі перебазуватися буде опубліковано.

Тепер зауважте, що це насправді можливо, лише якщо сховище клонується лише відомою (і бажано невеликою) групою людей, щоб той, хто натискає на перебазу або скидання, міг повідомити всіх інших, що їм потрібно буде звернути увагу наступного разу отримати (!).

Одне очевидне рішення, яке я бачив, спрацює, якщо у вас немає місцевих комітетів, fooі він перебазується:

git fetch
git checkout foo
git reset --hard origin/foo

Це просто відкине місцевий штат fooна користь його історії відповідно до віддаленого сховища.

Але як можна впоратись із ситуацією, якщо в цій галузі відбулися суттєві місцеві зміни?


+1 для простого рецепту випадку. Це ідеально підходить для особистої синхронізації між машинами, особливо якщо вони мають різні ОС. Це те, про що слід згадати у посібнику.
Філіп Оуклі

Ідеальним рецептом особистої синхронізації є git pull --rebase && git push. Якщо ви працюєте masterлише над цим , то це майже безпомилково зробить для вас правильну справу, навіть якщо ви перебазували і натискали на інший кінець.
Арістотель Пагальціс

Оскільки я синхронізую та розробляю між ПК та машинами Linux, я виявляю, що використання нової гілки для кожного перебазування / оновлення працює добре. Я також використовую варіант git reset --hard @{upstream}тепер, коли я знаю, що магічне заклинання refspec для "забудь, що я мав / мав, використовуй те, що я отримав з пульта". Див. Мій останній коментар до stackoverflow.com/a/15284176/717355
Філіп Оклі

Ви зможете за допомогою Git2.0 знайти старе походження вашої гілки (до того, як гілка вище була переписана на a push -f): див. Мою відповідь нижче
VonC

Відповіді:


75

Повернення синхронізації після перенаправленої бази даних у більшості випадків насправді не таке складне.

git checkout foo
git branch old-foo origin/foo # BEFORE fetching!!
git fetch
git rebase --onto origin/foo old-foo foo
git branch -D old-foo

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

Переоцінка - це як насильство: якщо це не вирішить вашу проблему, вам просто потрібно більше цього. ☺

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

Так само ви вирішуєте ситуацію, коли ви забули зробити закладку перед завантаженням. Нічого не втрачено - вам просто потрібно перевірити перезапис віддаленого відділення:

git reflog show origin/foo | awk '
    PRINT_NEXT==1 { print $1; exit }
    /fetch: forced-update/ { PRINT_NEXT=1 }'

Це надрукує ідентифікатор origin/fooкоміту, на який вказував перед останньою вибіркою, яка змінила його історію.

Тоді можна просто

git rebase --onto origin/foo $commit foo

11
Коротке зауваження: Я думаю, це досить інтуїтивно, але якщо ви погано знаєте awk ... цей однокласник просто переглядає вихідні git reflog show origin/fooдані першого рядка, кажучи "fetch: прымусове оновлення"; це те, що записує git, коли завантаження змушує віддалену гілку робити що завгодно, але не вперед. (Ви можете зробити це теж вручну - примусове оновлення - це, мабуть, остання річ.)
Cascabel

2
Це ніщо інше як насильство. Насильство іноді буває веселим
Іоло

5
@iolo Правда, переоцінка - це завжди весело.
Dan Bechard,

1
Як і насильство, майже завжди уникайте переоцінки. Але майте уявлення як.
Боб Штейн,

2
Ну, уникайте натискання на перебазу, де це буде порушено інших.
Арістотель Пагальціс

11

Я б сказав, що відновлення з попереднього розділу перебазування на git-rebase керівницькій сторінці охоплює майже все це.

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


4
Так, це так. Але хоча я тепер розумію, що там сказано, я б цього не знав раніше, до того, як з’ясувати це самостійно. І рецепту кулінарних книг немає (можливо, це правильно в такій документації). Я також зазначу, що називати "важкий випадок" жорстким - FUD. Я вважаю, що переписаною історією тривіально можна керувати в масштабах більшості власних розробок. Забобонний спосіб, з яким завжди розглядають цю тему, мене дратує.
Арістотель Пагальціс

4
@ Арістотель: Ви маєте рацію, що це дуже керовано, враховуючи те, що всі розробники знають, як користуватися git, і що ви можете ефективно спілкуватися з усіма розробниками. У ідеальному світі це був би кінець історії. Але багато проектів там досить великі, що перебазування вище за течією справді є страшною річчю. (І тоді є такі місця, як моє робоче місце, де більшість розробників навіть не чули про перебазу.) Я думаю, що "забобони" - це лише спосіб надати найбезпечніші та найзагальніші поради. Ніхто не хоче бути тим, хто спричиняє катастрофу в чужому репо.
Cascabel

2
Так, я розумію мотив. І я з цим повністю згоден. Але існує різниця у світі між "не намагайся цього, якщо ти не розумієш наслідків" і "ти ніколи не повинен цього робити, бо це зло", і лише це я сприймаю. Завжди краще наставляти, ніж вселяти страх.
Арістотель Пагальціс

@ Арістотель: Погоджено. Я намагаюся схилятися до кінця "переконайся, що ти знаєш, що робиш", але особливо в Інтернеті, я намагаюся надати йому достатньо ваги, щоб випадковий відвідувач Google зробив це на замітку. Ви маєте рацію, багато з цього, мабуть, слід пом'якшити.
Cascabel

11

Починаючи з мерзотником 1,9 / 2,0 Q1 2014 року, ви не повинні будете відзначати свій попередній філія походження , перш ніж перебазування його на переписаною вгору по течії гілки, як описано в Аристотель Pagaltzis «s відповідь :
Див здійснювати 07d406b і здійснювати d96855f :

Після роботи над topicгілкою, створеною за допомогою git checkout -b topic origin/master, історія віддаленого відстеження гілки, origin/masterможливо, була перемотана і перебудована, що призвело до історії такої форми:

                   o---B1
                  /
  ---o---o---B2--o---o---o---B (origin/master)
          \
           B3
            \
             Derived (topic)

де origin/masterвикористовується , щоб вказати на фіксацій B3, B2, B1і тепер він вказує на B, і ваша topicгілка була розпочата на вершині його назад , коли origin/masterбув B3.

Цей режим використовує рефлог origin/masterдля пошуку B3в якості точки розгалуження, щоб topicможна було перебазувати поверх оновленогоorigin/master за допомогою:

$ fork_point=$(git merge-base --fork-point origin/master topic)
$ git rebase --onto origin/master $fork_point topic

Ось чому git merge-baseкоманда має нову опцію:

--fork-point::

Знайдіть точку, в якій гілка (або будь-яка історія, до якої веде <commit>) відгалужується від іншої гілки (або будь-якого посилання) <ref>.
Це не просто шукає спільного предка двох комітів<ref><commit><ref> , але також бере до уваги переробку даних, щоб побачити, чи історія, що веде до розщеплення з попереднього втілення гілки .


Команда " git pull --rebase" обчислює точку розгалуження гілки, що перебазується, за допомогою записів перезапису baseгілки (зазвичай гілки віддаленого відстеження), на якій базувалася робота гілки, щоб справитись із випадком, коли "база" гілка була перемотана і відновлена.

Наприклад, якщо історія виглядала так: де:

  • поточний наконечник " base" гілки знаходиться в B, але раніше завантаження зауважив, що його наконечник був B3і тоді B2і потім, B1 перш ніж дійти до поточного коміту,
  • гілка, що перебазується поверх останньої "бази", базується на коміті B3,

він намагається знайти B3, перейшовши через вихід « git rev-list --reflog base» (тобто B, B1, B2, B3) до тих пір , поки не знайде фіксації , що є предком поточного наконечника « Derived (topic)».

Внутрішньо, ми get_merge_bases_many()можемо обчислити це одним рухом.
Ми хотіли б базу злиття між Derivedі фіктивним комітом злиття, який вийшов би шляхом об'єднання всіх історичних підказок " base (origin/master)".
Коли такий коміт існує, ми повинні отримати єдиний результат, який точно відповідає одному із записів перезапису " base".


Git 2.1 (Q3 2014) додасть, зробить цю функцію більш надійною до цього: див. Коміт 1e0dacd , Джон Кепінг ( johnkeeping)

правильно обробляти сценарій, де ми маємо таку топологію:

    C --- D --- E  <- dev
   /
  B  <- master@{1}
 /
o --- B' --- C* --- D*  <- master

де:

  • B'є виправленою версією, Bяка не є ідентичною виправленням B;
  • C*і D*ідентичні патчам Cі, Dвідповідно, і конфліктують у текстовому відношенні, якщо застосовуються в неправильному порядку;
  • Eзалежить від тексту D.

Правильний результат в git rebase master devтому , що Bідентифікується як вилочні точка devі master, таким чином , що C, D, Eє коммітов які повинні бути перетвореними на master; але Cі Dє ідентичними патчам з C*і D*і тому можна скинути, так що кінцевий результат:

o --- B' --- C* --- D* --- E  <- dev

Якщо точка розгалуження не ідентифікована, тоді вибір Bна гілці, що містить, B'призводить до конфлікту, а якщо ідентичні фіксації комітів невірно визначені, тоді вибір Cна гілці, що містить D(або еквівалентно D*), призводить до конфлікту.


Режим " --fork-point" " git rebase" регресував, коли команда була переписана на C ще в епоху 2.20, що було виправлено за допомогою Git 2.27 (Q2 2020).

Див. Коміт f08132f (09 грудня 2019 р.) Хуніо С Хамано ( gitster) .
(Об’єднано Junio ​​C Hamano - gitster- у комітеті fb4175b , 27 березня 2020 р.)

rebase: --fork-pointвиправлення регресії

Підписав: Алекс Торок
[jc: оновив виправлення та використовував тести Алекса]
Підписав: Хуніо С Хамано

" git rebase --fork-point master" раніше працював нормально, як "" він внутрішньо називав " git merge-base --fork-point", що знав, як обробляти коротке refname і вводити його до повного refname перед викликом базової get_fork_point()функції.

Це більше не відповідає дійсності після того, як команда була переписана на C, оскільки її внутрішній виклик, зроблений безпосередньо на get_fork_point(), не зменшує короткий опис.

Перемістіть аргумент "dwim refname до повної імені ref", що використовується в "git merge-base", до базової get_fork_point()функції, щоб інший виклик функції у реалізації "git rebase" поводився так само, щоб виправити ця регресія.


1
Зверніть увагу , що мерзотник поштовх --force тепер (мерзотник 1.8.5) зробити більш розсудливо: stackoverflow.com/a/18505634/6309
VonC
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.