Що саме робить "rebase - збереження-злиття" git (і чому?)


355

Документаціяrebase Git для команди досить коротка:

--preserve-merges
    Instead of ignoring merges, try to recreate them.

This uses the --interactive machinery internally, but combining it
with the --interactive option explicitly is generally not a good idea
unless you know what you are doing (see BUGS below).

То що насправді відбувається, коли ви користуєтесь --preserve-merges? Чим вона відрізняється від поведінки за замовчуванням (без цього прапора)? Що означає «відтворити» злиття тощо.


20
Попередження: починаючи з Git 2.18 (Q2 2018, 5 років пізніше), git --rebase-mergesврешті-решт замінить старе git --preserve-merges. Дивіться мою відповідь нижче
VonC

Відповіді:


464

Як і у звичайній базі даних git, git --preserve-mergesспершу ідентифікує список комітетів, зроблених в одній частині графа фіксації, а потім повторює ці коміти над іншою частиною. Відмінності із --preserve-mergesзанепокоєнням, які комісії обрані для повторного відтворення та як це відтворення працює для об'єднань комітетів.

Щоб бути більш чіткими щодо основних відмінностей між нормальною базою даних і збереженням злиття:

  • Збереження, що зберігає злиття, готове відтворити (деякі) об'єднання об'єднань, тоді як звичайне відновлення повністю ігнорує об'єднання об'єднань.
  • Оскільки він готовий повторно виконувати об'єднання об'єднань, оновлення, що зберігає злиття, повинно визначити, що означає повторне об'єднання об'єднань, та усунути деякі зайві зморшки
    • Концептуально найцікавіша частина - це, мабуть, вибір того, яким має бути батьки нового злиття.
    • Повторне відтворення об'єднань також вимагає чітко перевірити окремі коміти ( git checkout <desired first parent>), тоді як звичайна база даних про це не повинна турбуватися.
  • Збереження, що зберігає злиття, розглядає більш дрібний набір комітетів для відтворення:
    • Зокрема, він буде розглядати лише повторення комітетів, здійснених з моменту останньої бази злиття (тобто, за останній час, коли обидві гілки розійшлися), тоді як звичайна база даних може відтворювати комітети, повернувшись в перший раз, коли дві гілки розійшлися.
    • Якщо бути тимчасовим і неясним, я вважаю, що це врешті-решт засіб для екранізації відтворення "старих комісій", які вже були "включені" в об'єднання об'єднань.

Спершу я спробую описати "достатньо точно" те, що --preserve-mergesробить rebase , а потім будуть приклади. Можна, звичайно, почати з прикладів, якщо це здається кориснішим.

Алгоритм у "Короткій"

Якщо ви дійсно хочете потрапити в бур’яни, завантажте джерело git та вивчіть файл git-rebase--interactive.sh. (Rebase не є частиною ядра C Git, а навпаки, написаний у bash. І за лаштунками він ділить кодом з "інтерактивним ребазом".)

Але тут я намалюю те, що, на мою думку, є суттю цього. Для того, щоб зменшити кількість речей, про які варто подумати, я взяв кілька свобод. (наприклад, я не намагаюсь зі 100% точністю визначити точний порядок, в якому проводяться обчислення, і ігнорувати деякі менш центральні, здавалося б, теми, наприклад, що робити з комісіями, які вже були вибрані між гілками).

По-перше, зауважте, що база даних, що не зберігає об'єднання, досить проста. Це більш-менш:

Find all commits on B but not on A ("git log A..B")
Reset B to A ("git reset --hard A") 
Replay all those commits onto B one at a time in order.

База даних --preserve-mergesпорівняно складна. Ось так просто, як мені вдалося це зробити, не втрачаючи речей, які здаються досить важливими:

Find the commits to replay:
  First find the merge-base(s) of A and B (i.e. the most recent common ancestor(s))
    This (these) merge base(s) will serve as a root/boundary for the rebase.
    In particular, we'll take its (their) descendants and replay them on top of new parents
  Now we can define C, the set of commits to replay. In particular, it's those commits:
    1) reachable from B but not A (as in a normal rebase), and ALSO
    2) descendants of the merge base(s)
  If we ignore cherry-picks and other cleverness preserve-merges does, it's more or less:
    git log A..B --not $(git merge-base --all A B)
Replay the commits:
  Create a branch B_new, on which to replay our commits.
  Switch to B_new (i.e. "git checkout B_new")
  Proceeding parents-before-children (--topo-order), replay each commit c in C on top of B_new:
    If it's a non-merge commit, cherry-pick as usual (i.e. "git cherry-pick c")
    Otherwise it's a merge commit, and we'll construct an "equivalent" merge commit c':
      To create a merge commit, its parents must exist and we must know what they are.
      So first, figure out which parents to use for c', by reference to the parents of c:
        For each parent p_i in parents_of(c):
          If p_i is one of the merge bases mentioned above:
            # p_i is one of the "boundary commits" that we no longer want to use as parents
            For the new commit's ith parent (p_i'), use the HEAD of B_new.
          Else if p_i is one of the commits being rewritten (i.e. if p_i is in R):
            # Note: Because we're moving parents-before-children, a rewritten version
            # of p_i must already exist. So reuse it:
            For the new commit's ith parent (p_i'), use the rewritten version of p_i.
          Otherwise:
            # p_i is one of the commits that's *not* slated for rewrite. So don't rewrite it
            For the new commit's ith parent (p_i'), use p_i, i.e. the old commit's ith parent.
      Second, actually create the new commit c':
        Go to p_1'. (i.e. "git checkout p_1'", p_1' being the "first parent" we want for our new commit)
        Merge in the other parent(s):
          For a typical two-parent merge, it's just "git merge p_2'".
          For an octopus merge, it's "git merge p_2' p_3' p_4' ...".
        Switch (i.e. "git reset") B_new to the current commit (i.e. HEAD), if it's not already there
  Change the label B to apply to this new branch, rather than the old one. (i.e. "git reset --hard B")

База даних з --onto Cаргументом має бути дуже схожою. Тільки замість того, щоб починати відтворення коміксів у HEAD B, замість цього ви починаєте виконувати відтворення в HEAD of C. (І використовуйте C_new замість B_new.)

Приклад 1

Наприклад, візьміть графік фіксації

  B---C <-- master
 /                     
A-------D------E----m----H <-- topic
         \         /
          F-------G

m - об'єднання з батьками E та G.

Припустимо, ми переосмислили тему (H) на вершині master (C), використовуючи звичайну базу даних, що не зберігає об'єднання. (Наприклад, тема оформлення замовлення; майстер перезавантаження ). У цьому випадку git вибрав би такі коміти для повторного відтворення:

  • вибрати D
  • вибрати E
  • вибрати F
  • вибрати G
  • вибрати H

а потім оновіть графік фіксації так:

  B---C <-- master
 /     \                
A       D'---E'---F'---G'---H' <-- topic

(D '- повторюваний еквівалент D тощо).

Зауважте, що комісія об'єднання m не обрана для відтворення.

Якби ми замість цього зробили --preserve-mergesребауз H на вершині C. (Наприклад, тема оформлення замовлення; rebase - зберегти-зливає майстер .) У цьому новому випадку git вибрав би такі коміти для повторного відтворення:

  • вибрати D
  • вибрати E
  • виберіть F (на D "у гілці" підтеми ")
  • виберіть G (на F "у гілці" підтеми ")
  • виберіть гілку "Злиття" підтеми в тему
  • вибрати H

Тепер м був обраний для відтворення. Також зауважте, що батьки злиття E і G були вибрані для включення перед здійсненням об'єднання m.

Ось отриманий графік фіксації:

 B---C <-- master
/     \                
A      D'-----E'----m'----H' <-- topic
        \          / 
         F'-------G'

Знову ж таки, D '- це вишня (тобто відтворена) версія D. Те саме для E' тощо. І E, і G (батьки злиття m) були відтворені як E 'і G', щоб служити батьками m '(після відновлення історія дерев залишається тією ж).

Приклад 2

На відміну від звичайного ребазування, збереження злиття може створити кілька дітей верхньої частини потоку.

Наприклад, врахуйте:

  B---C <-- master
 /                     
A-------D------E---m----H <-- topic
 \                 |
  ------- F-----G--/ 

Якщо ми перезавантажимо H (тему) поверх C (головний), то комітети, вибрані для відновлення, це:

  • вибрати D
  • вибрати E
  • вибрати F
  • вибрати G
  • забрати м
  • вибрати H

І результат такий:

  B---C  <-- master
 /    | \                
A     |  D'----E'---m'----H' <-- topic
       \            |
         F'----G'---/

Приклад 3

У вищенаведених прикладах як злиття, так і два його батьки відтворюються комітетами, а не початковими батьками, які мають оригінальні комісії злиття. Однак, в інших знижках повторне об’єднання об'єднання може закінчитися з батьками, які вже були в графіку фіксації перед об'єднанням.

Наприклад, врахуйте:

  B--C---D <-- master
 /    \                
A---E--m------F <-- topic

Якщо ми перезавантажимо тему на головну (зберігши злиття), то зобов’язання щодо відтворення будуть

  • вибрати об'єднати комісію m
  • вибрати F

Переписаний графік фіксації виглядатиме так:

                     B--C--D <-- master
                    /       \             
                   A-----E---m'--F'; <-- topic

Тут відтворена комісія злиття m 'отримує батьків, які існували раніше у графіку фіксування, а саме D (HEAD master) та E (один з батьків початкового читання m merge).

Приклад 4

Збереження, що зберігає злиття, може заплутатися у певних випадках "порожнього фіксування". Принаймні, це стосується лише старих версій git (наприклад, 1.7.8.)

Візьміть цей графік фіксації:

                   A--------B-----C-----m2---D <-- master
                    \        \         /
                      E--- F--\--G----/
                            \  \
                             ---m1--H <--topic

Зауважте, що обидва фікси m1 і m2 повинні включати всі зміни від B і F.

Якщо ми спробуємо зробити git rebase --preserve-mergesH (тему) на D (master), то для повторного вибору вибираються наступні коміти:

  • вибрати m1
  • вибрати H

Зауважте, що зміни (B, F), об'єднані в m1, повинні бути вже включені в D. (Ці зміни повинні бути вже включені в m2, тому що m2 зливаються разом у дітей B і F.) Тому, концептуально, відтворення m1 поверх D, мабуть, повинен бути або неоперативним, або створювати порожній коміт (тобто такий, де різниця між послідовними редакціями порожня).

Натомість git може відхилити спробу повторного відтворення m1 поверх D. Ви можете отримати помилку на зразок:

error: Commit 90caf85 is a merge but no -m option was given.
fatal: cherry-pick failed

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


6
Я помітив, що git rebase --preserve-mergesце набагато повільніше, ніж rebaseбез --preserve-merges. Це побічний ефект пошуку правильних зобов'язань? Чи можна щось зробити, щоб пришвидшити це? (До речі ... дякую за дуже детальну відповідь!)
Девід Алан Хелл

7
Це здається, що ви завжди повинні використовувати - зберегти-зливати. Інакше є потенціал втратити історію, тобто злиття здійснює.
ДарВар

19
@DarVar Ви завжди втрачаєте історію на ребазі, тому що ви стверджуєте, що вони змінюються там, де вони зроблені в іншій кодовій базі, ніж у нас.
Хронічний

5
Це все ще "попередня відповідь"?
Ендрю Грімм

5
@Chronial Звичайно, ви маєте рацію, що релізинг завжди включає історію втрати, але, можливо, DarVar натякав на те, що ви не тільки втрачаєте історію, але і змінюєте базу коду. Вирішення конфлікту містить інформацію, яка втрачається всіма можливими способами відновлення бази даних. Ви завжди повинні це переробити. Чи дійсно немає можливості дозволити git повторити вирішення конфлікту? Чому не можна назбирати вишневий комітет злиття?
Nils_M

94

Git 2.18 (Q2 2018) значно покращить --preserve-mergeваріант, додавши нову опцію.

" git rebase" навчився " --rebase-merges" пересаджувати всю топологію графіка фіксації в інше місце .

(Примітка: Git 2.22, Q2 2019, насправді застаріло --preserve-merge , а Git 2.25, Q1 2020, припиняє рекламувати його на git rebase --helpвиході " " )

Див здійснюють 25cff9f , здійснюють 7543f6f , здійснюють 1131ec9 , здійснюють 7ccdf65 , здійснюють 537e7d6 , здійснюють a9be29c , здійснюють 8f6aed7 , здійснюють 1644c73 , здійснюють d1e8b01 , здійснюють 4c68e7d , здійснюють 9055e40 , здійснюють cb5206e , здійснюють a01c2a5 , здійснюють 2f6b1d1 , здійснюють bf5c057 (25 апр 2018) від Johannes Schindelin ( dscho) .
Див. Комісію f431d73 (25 квітня 2018 р.) Від Стефана Беллера ( stefanbeller) .
Див. Коміт 2429335 (25 квітня 2018) від Phillip Wood ( phillipwood) .
(Об’єднав Хуніо С Хамано - gitster- у комітеті 2c18e6a , 23 травня 2018 р.)

pull: прийняти --rebase-mergesвідтворити топологію галузі

Подібно до preserveрежиму просто передачі --preserve-merges опції rebaseкоманді, mergesрежим просто передає --rebase-mergesопцію.

Це дозволить користувачам зручно переосмислити нетривіальні топології фіксації під час витягування нових комісій, не згладжуючи їх.


git rebaseНа цій сторінці man є повний розділ, присвячений оновленню історії злиттям .

Витяг:

Є законні причини, через які розробник може захотіти відтворити об'єднання об'єднань: зберігати структуру гілки (або "здійснювати топологію") під час роботи над декількома взаємопов'язаними гілками.

У наступному прикладі розробник працює на тематичній гілці, яка перетворює фактори визначення кнопок, і на іншій гілці теми, яка використовує цей рефакторинг для реалізації кнопки "Повідомити про помилку".
Вихід git log --graph --format=%s -5може виглядати так:

*   Merge branch 'report-a-bug'
|\
| * Add the feedback button
* | Merge branch 'refactor-button'
|\ \
| |/
| * Use the Button class for all buttons
| * Extract a generic Button class from the DownloadButton one

Розробник може захотіти відновити ці зобов'язання на новіші master , зберігаючи топологію гілки, наприклад, коли очікується, що перша галузь теми буде інтегрована в masterнабагато раніше, ніж друга, скажімо, для вирішення конфліктів злиття зі змінами DownloadButtonкласу, який склав це в master.

Ця база даних може бути виконана за допомогою --rebase-mergesпараметра.


Див. Команду 1644c73 для невеликого прикладу:

rebase-helper --make-script: введіть прапор для відновлення злиття

Секвенсер щойно вивчив нові команди, призначені для відтворення структури гілок ( схожих за духом --preserve-merges, але з істотно менш розбитим дизайном ).

Давайте дозволяють rebase--helperгенерувати списки TODO використання цих команд, що запускаються з допомогою нової --rebase-mergesопції.
Для такої топології фіксації (де HEAD вказує на C):

- A - B - C (HEAD)
    \   /
      D

згенерований список тодо виглядатиме так:

# branch D
pick 0123 A
label branch-point
pick 1234 D
label D

reset branch-point
pick 2345 B
merge -C 3456 D # C

У чому різниця --preserve-merge?
Коміт 8f6aed7 пояснює:

Колись цей розробник думав: чи не було б приємно, якби, скажімо, патчі Git для Windows поверх ядра Git могли бути представлені у вигляді зарості гілок і переобладнані поверх ядра Git, щоб підтримувати вишневий набір накладних серій?

Оригінальна спроба відповісти на це: git rebase --preserve-merges.

Однак цей експеримент ніколи не задумувався як інтерактивний варіант, і він підтримувався лише підсиленням, git rebase --interactiveтому що виконання цієї команди виглядало вже дуже-дуже звично: її розробив той самий чоловік, який створив --preserve-merges: ваш справді.

І під «твоєю справді» автор посилається на себе: Йоганнес Шинделін ( dscho) , який є основною причиною (з кількома іншими героями - Ханнесом, Штеффен, Себастьяном, ...), що у нас є Git For Windows (навіть якщо ще в той день - 2009 - це було непросто ).
Він працює в Microsoft з вересня 2015 року , що має сенс враховувати, що Microsoft зараз активно використовує Git і потребує його послуг.
Ця тенденція почалася фактично у 2013 році з TFS . З того часу Microsoft управляє найбільшим сховищем Git на планеті ! І з жовтня 2018 року Microsoft придбала GitHub .

Ви можете побачити Йоганнеса, який виступає у цьому відео для Git Merge 2018 у квітні 2018 року.

Через деякий час інший розробник (я дивлюся на тебе, Андреас! ;-)) вирішив, що було б хорошою ідеєю дозволити --preserve-mergesпоєднувати з --interactive(із застереженнями!) Та підтримку Git (ну тимчасовий утримувач Git під час відсутності Джуніо, тобто) погодився, і саме тоді гламур --preserve-mergesдизайну почав розпадатися досить швидко і неслухняно.

Тут Джонатан говорить про Андреаса Шваба із Суза.
Деякі їх обговорення ви можете побачити ще у 2012 році .

Причина? У --preserve-mergesрежимі батьки об'єднання об'єднань (або з цього приводу будь-якого комітету ) не були вказані прямо, але мали на увазі ім'я комітету, передане pickкоманді .

Це унеможливило, наприклад, переупорядкування комісій .
Не кажучи вже про переміщення комісій між гілками або, заборони божество, розділити гілки теми на дві частини.

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

П'ять років потому, коли стало справді безперебійним мати одну непросту, велику серію патчів hodge-podge з частково пов'язаними, частково не пов’язаними між собою патчами в Git для Windows, які час від часу перероблялися на основні теги Git (заробляючи незаслужений гнів розробника із злощасної git-remote-hgсерії, яка спочатку застаріла з Git для конкурентного підходу Windows, а лише пізніше її відмовилися без технічного обслуговування), була справді непереборною, народилися " Ножиці садових ножиць " : сценарій, підписка на вершині інтерактивної бази даних, що спочатку визначило б топологію гілки патчів, які підлягають перебазуванню, створить псевдотодовий список для подальшого редагування, перетворить результат у справжній список тодо (використовуючи важке використанняexec команда "реалізувати" відсутні команди команд списку todo) і, нарешті, відтворити серію патчів поверх нової базової комісії.

(Скрипт ножиць Git garden посилається на цей патч у фіксації 9055e40 )

Це було в 2013 році.
І, щоб розробити дизайн та реалізувати його як сценарій поза деревом, знадобилося близько трьох тижнів. Потрібно сказати, що на реалізацію знадобилося досить багато років, щоб стабілізуватись, в той час як сама конструкція виявилася здоровою.

За допомогою цього пластиру сама доброта садових ножиць Git приходить до git rebase -iсебе .
При передачі цього --rebase-mergesпараметра буде створено список todo, який можна легко зрозуміти, і де очевидно, як упорядкувати комісії .
Нові гілки можна ввести, вставляючи labelкоманди та викликаючи merge <label>.
І як тільки цей режим стане стабільним і загальновизнаним, ми можемо знехтувати помилку дизайну, яка була--preserve-merges .


Git 2.19 (Q3 2018) покращує новий --rebase-mergesваріант, змушуючи його працювати --exec.

" --exec" Варіант до " git rebase --rebase-merges" розміщував команди exec у неправильних місцях, що було виправлено.

Див. Комісію 1ace63b (09 серпня 2018 р.) Та фіксування f0880f7 (06 серпня 2018 р.) Йоганнеса Шинделіна ( dscho) .
(Об’єднав Хуніо С Хамано - gitster- в комітеті 750eb11 , 20 серпня 2018)

rebase --exec: змусити його працювати --rebase-merges

Ідея --exec- додавати execдзвінок після кожного pick.

З моменту введення fixup!/ с quash!фіксацій, ця ідея була розширена , щоб застосувати до «забрати, можливо супроводжуваного FixUp / сквош ланцюга», тобто Exec НЕ буде вставлений між а pickі будь-який з його відповідних fixupабо squashліній.

Поточна реалізація використовує капость для досягнення цієї мети: вона передбачає , що тільки вибрати / Fixup / сквош команди, а потім вставляє в execлінію перед будь-яким , pickале першим, і приєднує остаточну.

За допомогою списків todo, що генеруються git rebase --rebase-merges, ця проста реалізація показує свої проблеми: вона створює абсолютно неправильну річ, коли є label, resetі mergeкоманди.

Давайте змінимо реалізацію, щоб зробити саме те, що ми хочемо: шукаємо pickлінії, пропускаємо будь-які ланцюги фіксації / сквош, а потім вставляємо exec рядок . Натріть, промийте, повторіть.

Примітка: ми намагаємось вставити перед рядками коментарів, коли це можливо, оскільки порожні комірки представлені коментованими рядками вибору (і ми хочемо вставити попередній рядок виконання вибору перед таким рядком, а не після цього).

Перебуваючи в ньому, також додайте execрядки після mergeкоманд, оскільки вони за духом схожі на pickкоманди: вони додають нові коміти.


Git 2.22 (Q2 2019) фіксує використання refs / переписаної / ієрархії для зберігання проміжних станів ребаза, що по суті робить ієрархію на робочому дереві.

Див. Комісію b9317d5 , вчиняйте 90d31ff , фіксуйте 09e6564 (07 березня 2019 р.) Nguyễn Thái Ngọc Duy ( pclouds) .
(Об'єднано Хуніо С Хамано - gitster- в комісії 917f2cd , 09 квітня 2019 р.)

Переконайтеся, що refs / переписаний / є кожним робочим деревом

a9be29c (секвенсор: зробити refs, згенерований labelкомандою worktree-local, 2018-04-25, Git 2.19) додає refs/rewritten/як опорний простір для worktree.
На жаль (мені погано) є кілька місць, які потребують оновлення, щоб переконатися, що це дійсно на кожному рівні роботи.

- add_per_worktree_entries_to_dir()оновлюється, щоб переконатися, що перелік посилань переглядає per-worktree refs/rewritten/замість per-repo.

  • common_list[]оновлюється, щоб git_path()повернути правильне розташування. Сюди входить " rev-parse --git-path".

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


З Git 2.24 (Q4 2019), " git rebase --rebase-merges" навчився керувати різними стратегіями злиття та передавати їм певні стратегічні варіанти.

Див. Коміт 476998d (04 вересня 2019 року) від Іллі Ньюрена ( newren) .
Див здійснюють e1fac53 , здійснює a63f990 , здійснює 5dcdd74 , здійснює e145d99 , здійснює 4e6023b , здійснює f67336d , здійснює a9c7107 , здійснюють b8c6f24 , здійснює d51b771 , здійснює c248d32 , здійснює 8c1e240 , здійснює 5efed0e , здійснює 68b54f6 , здійснює 2e7bbac , здійснює 6180b20 , здійснюють d5b581f (31 Липень 2019) відЙоганнес Шинделін ( dscho) .
(Об’єднав Хуніо С Хамано - gitster- у комітеті 917a319 , 18 вересня 2019 р.)


З Git 2.25 (Q1 2020) логіка, яка використовується для розбиття локальних і глобальних репозиторіїв робочого дерева, виправлена, щоб полегшити збереження-об'єднання.

Див. Комісію f45f88b , фіксування c72fc40 , фіксування 8a64881 , фіксування 7cb8c92 , фіксування e536b1f (21 жовтня 2019 р.) SZEDER Gábor ( szeder) .
(Об’єднав Хуніо С Хамано - gitster- в комісії db806d7 , 10 листопада 2019 р.)

path.c: не викликайте matchфункцію без значення вtrie_find()

Вихід із системи: SZEDER Gábor

'logs / refs' не є діючим деревом, але специфічний b9317d55a3 (переконайтеся, що refs / rewritten / is per-worktree, 2019-03-07, v2.22.0-rc0) ' git rev-parse --git-path' повертає помилковий шлях якщо замикає « /» присутній:

$ git -C WT/ rev-parse --git-path logs/refs --git-path logs/refs/
/home/szeder/src/git/.git/logs/refs
/home/szeder/src/git/.git/worktrees/WT/logs/refs/

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

Як це буває, b9317d55a3 викликала помилку, яка є такою ж старою, як і trieсама реалізація, додана в 4e09cf2acf (" path: оптимізувати загальну перевірку режиму", 2015-08-31, Git v2.7.0-rc0 - злиття, вказане в пакеті №2 ).

  • Відповідно до опису коментаря trie_find(), він повинен викликати задану функцію відповідності "fn" лише за "/ -or- \ 0-припиненим префіксом ключа, для якого трие містить значення".
    Це неправда: є три місця, де trie_find () викликає функцію відповідності, але в одному з них відсутня перевірка існування значення.

  • b9317d55a3 додав два нові ключі до trie:

    • ' logs/refs/rewritten', і
    • ' logs/refs/worktree', поруч із уже існуючим ' logs/refs/bisect'.
      Це призвело до появи trieвузла з контуром ' logs/refs/', який раніше не існував і якому не додано значення.
      Запит на ' logs/refs/' знаходить цей вузол, а потім потрапляє на той виклик matchфункції, який не перевіряє існування значення, і, таким чином, викликає matchфункцію NULLяк значення.
  • Коли matchфункція check_common()викликається NULLзначенням, вона повертає 0, що вказує на те, що запитуваний шлях не належить до загального каталогу, в кінцевому підсумку в результаті відображається помилковий шлях.

Додайте відсутню умову, щоб trie_find()вона ніколи не викликала функції збігу з неіснуючим значенням.

check_common() тоді більше не доведеться перевіряти, чи отримав воно значення, яке не має значення NULL, тому видаліть цю умову.

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

AFAICT, єдиною іншою клавішею, що призводить до виклику функції відповідності зі NULLзначенням, є ' co' (через клавіші ' common' і ' config').

Однак, оскільки вони не знаходяться в каталозі, що належить до загальної директорії, очікується результат, що відповідає дереву.


3
Я думаю, це має бути головною відповіддю, --preserve-mergesнасправді "не збереже" злиття, як хочеш, це дуже наївно. Це дозволяє зберегти злиття комітетів та їхніх батьківських зв'язків, надаючи вам гнучкість інтерактивної бази даних. Ця нова особливість є приголомшливою, якби не ця добре написана відповідь ТА, я б не знав!
egucciar

@egucciar Дякую І це не єдина функція Git 2.18 ( stackoverflow.com/search?q=user%3A6309+%22git+2.18%22 ), а також Git 2.19 ( stackoverflow.com/search?q=user%3A6309+%22git+2.19% 22 )
VonC

1
Дуже корисно , якщо ви намагаєтеся перемістити скибки фіксацій навколо , як у цьому Q / A, stackoverflow.com/questions/45059039 / ...
okovko

1
О, це справді те, що я шукав з певного часу! У мене було вручну вирішити такі випадки, коли слід створити фіктивну комісію, яка приєднується до всіх злиттям.
карник

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