Я не можу зрозуміти поведінку git rebase --onto


169

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

У мене є Aі Bгілка, яка розходиться з однієюcommit

---COMMIT--- (A)
\
 --- (B)

Я хочу відновити Bгілку на останній термін A(і мати фіксацію на Bгілці)

---COMMIT--- (A)
         \
          --- (B)

Немає проблем, якщо я роблю:

checkout B
rebase A

Але якщо я:

checkout B
rebase --onto B A

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

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


Відповіді:


412

тл; д-р

Правильний синтаксис перебазуватися Bна вершині Aвикористання git rebase --ontoв вашому випадку:

git checkout B
git rebase --onto A B^

або перезавантажити Bзверху, Aпочинаючи з комітету, який є батьківськимB посиланням на B^або B~1.

Якщо вас цікавить різниця між git rebase <branch>і git rebase --onto <branch>читайте далі.

Швидкий: git rebase

git rebase <branch>збирається перебазуватися сук , в даний час вже перевірили, на який посилається HEAD, на вершині останнього коммітов , який доступний з , <branch>але НЕ з HEAD.
Це найпоширеніший випадок звільнення, і, мабуть, той, який вимагає менше планування фронту.

          Before                           After
    A---B---C---F---G (branch)        A---B---C---F---G (branch)
             \                                         \
              D---E (HEAD)                              D---E (HEAD)

У цьому прикладі Fі Gє коміти, до яких можна дістатися, branchале не з них HEAD. Висловлення git rebase branchбуде приймати D, тобто першу фіксацію після точки розгалуження, і перебазувати його (тобто змінити його батько ) на вершині останнього Комміт досяжна з , branchале не від HEAD, тобто G.

Точність: git rebase - до 2 аргументів

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

Наприклад, давайте уявимо, що нам потрібно перезавантажуватись HEADсаме на початку, Fпочинаючи з цього E. Ми зацікавлені лише в Fтому, щоб зайнятися нашою робочою галуззю, в той же час ми не хочемо зберігати Dїї, оскільки вона містить деякі несумісні зміни.

          Before                           After
    A---B---C---F---G (branch)        A---B---C---F---G (branch)
             \                                     \
              D---E---H---I (HEAD)                  E---H---I (HEAD)

У цьому випадку ми б сказали git rebase --onto F D. Це означає:

Обґрунтуйте доступ, який можна здійснити, HEADчий з батьків є Dповерх F.

Іншими словами, змінити батька в Eвід Dдо F. Синтаксис git rebase --ontoє тоді git rebase --onto <newparent> <oldparent>.

Ще один сценарій, коли це стане в нагоді, - це коли ви хочете швидко видалити деякі комісії з поточної гілки без необхідності робити інтерактивну базу даних :

          Before                       After
    A---B---C---E---F (HEAD)        A---B---F (HEAD)

У цьому прикладі для того, щоб видалити Cта Eз послідовності, яку ви б сказали git rebase --onto B E, або перезавантажити HEADповерх того, Bде був старий батьків E.

The Surgeon: git rebase - до 3 аргументів

git rebase --ontoможна досягти на крок далі з точки зору точності. Насправді це дозволяє перезавантажити довільний діапазон комітетів поверх іншого.

Ось приклад:

          Before                                     After
    A---B---C---F---G (branch)                A---B---C---F---G (branch)
             \                                             \
              D---E---H---I (HEAD)                          E---H (HEAD)

У цьому випадку ми хочемо відновити точний діапазон E---Hна вершині F, ігноруючи, куди HEADзараз вказується. Ми можемо це зробити, сказавши git rebase --onto F D H, що означає:

Rebase діапазон коммітов , чий батько Dдо Hзверху F.

Потім стає синтаксисом git rebase --ontoз діапазоном комітівgit rebase --onto <newparent> <oldparent> <until> . Хитрість тут пам'ятати , що фіксація посилається <until>буде включений в діапазон і стане новим HEADпісля перебазуватися завершення.


1
Гарна відповідь. Лише невелике доповнення до загального випадку: <oldparent>Назва розбивається, якщо дві частини діапазону знаходяться на різних гілках. Як правило, це: "Включіть усі комісії, до яких можна дістатися, <until>але виключіть усі комісії, до яких можна дістатися <oldparent>."
musiKk

50
git rebase --onto <newparent> <oldparent>найкраще пояснення - до поведінки, яку я бачив!
ронкот

4
Дякую! Я якось боровся з --ontoваріантом, але це зробило його кришталево зрозумілим! Я навіть не розумію, як я не міг цього раніше зрозуміти: D Спасибі за відмінний "підручник" :-)
grongor

3
Хоча ця відповідь є відмінною, я вважаю, що вона не охоплює всіх можливих випадків. Остання форма синтаксису також може бути використана для вираження більш тонкого типу ребазу. Виходячи з прикладу в Pro Git (2-е видання), D не обов'язково повинен бути родоначальником Х. Натомість, D і H також можуть зв'язатись із спільним предком - у цьому випадку Git з'ясує їхнього загального предка і переграти з цього предка на H на F.
Pastafarian

1
Це було корисно. Сторінка man зовсім не пояснює аргументи.
a544jh

61

Це все, що потрібно знати, щоб зрозуміти --onto:

git rebase --onto <newparent> <oldparent>

Ви перемикаєте батьків на комутацію, але ви не надаєте шафи комітів, а лише його поточний (старий) батьків.


4
Короткий і легкий. Насправді, усвідомлення того, що я маю надати батьківську фіксацію тієї, яку я хочу відновити замість цього, зайняв у мене найдовший час.
Антоніоссс

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

2
Для зауваження: Вам потрібно бути в гілці або додати назву гілки як відновлення 3-го параметра git --onto <newparent> <oldparent> <feature-branch>
Jason

1
Ця відповідь дивовижна, прямо до того, як потрібно в цій темі
Джон Кальвінер

13

Незабаром, якщо:

      Before rebase                             After rebase
A---B---C---F---G (branch)                A---B---C---F---G (branch)
         \                                         \   \
          D---E---H---I (HEAD)                      \   E'---H' (HEAD)
                                                     \
                                                      D---E---H---I

git rebase --onto F D H

Що це те саме, що (оскільки --ontoбере один аргумент):

git rebase D H --onto F

Означає, що база даних здійснює діапазон (D, H] на верхній частині F. Зауважте, що діапазон є виключним зліва. Це ексклюзивно, тому що простіше визначити першу комісію, ввівши, наприклад, branchщоб gitзнайти першу розбіжну комітку, branchтобто, до Dякої призводить H.

Справа ОП

    o---o (A)
     \
      o (B)(HEAD)

git checkout B
git rebase --onto B A

Можна змінити на одну команду:

git rebase --onto B A B

Тут виглядає помилка - розміщення, Bяке означає "переміщення деяких комітетів, які ведуть до гілки Bвгорі B". Питання в тому, що таке "деякі зобов'язання". Якщо ви додасте -iпрапор, ви побачите, що це один коміт, на який вказує HEAD. Комісія пропускається, оскільки вона вже застосована до --ontoцілі, Bі тому нічого не відбувається.

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

Подальше пояснення та застосовне використання git rebase <upstream> <branch> --onto <newbase>.

git rebase за замовчуванням.

git rebase master

Розгортається на:

git rebase --onto master master HEAD
git rebase --onto master master current_branch

Автоматичний замовлення після ребати.

При використанні стандартним способом, наприклад:

git checkout branch
git rebase master

Ви не помітите, що після того, як база даних gitпереміститься branchдо останньої перезавантаженої фіксації та робить git checkout branch(див. git reflogІсторію). Що цікаво, коли другий аргумент - це зробити хеш, а замість імені гілки все ще працює, але немає гілки для переміщення, тож ви опинитесь у "відокремленій HEAD", а не перевіряєтесь на переміщену гілку.

Опустіть первинні розбіжні коміти.

masterУ --ontoузятий з 1 - го git rebaseаргументу.

                   git rebase master
                              /    \
         git rebase --onto master master

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

git rebase --onto master HEAD~
git rebase --onto master HEAD~ HEAD  # Expanded.

Буде чи перебазуватися одну фіксацію зазначених вище HEADдо masterі в кінцевому підсумку в «окремих КЕРІВНИКА».

Уникайте явних кас.

Типово HEADабо current_branchаргумент контекстно взято з місця, де ви знаходитесь. Ось чому більшість людей отримують заявку на відділення, які вони хочуть відновити. Але коли аргумент другого rebase надається явно, вам не доведеться перевіряти його перед повторною передачею, щоб передати її неявно.

(branch) $ git rebase master
(branch) $ git rebase master branch  # Expanded.
(branch) $ git rebase master $(git rev-parse --abbrev-ref HEAD)  # Kind of what git does.

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

(master) $ git rebase master branch
(branch) $ # Rebased. Notice checkout.

8

Простіше кажучи, git rebase --ontoвибирає діапазон комітетів і перебазує їх на фіксацію, задану як параметр.

Прочитайте сторінки чоловіка git rebase, шукайте "на". Приклади дуже хороші:

example of --onto option is to rebase part of a branch. If we have the following situation:

                                   H---I---J topicB
                                  /
                         E---F---G  topicA
                        /
           A---B---C---D  master

   then the command

       git rebase --onto master topicA topicB

   would result in:

                        H'--I'--J'  topicB
                       /
                       | E---F---G  topicA
                       |/
           A---B---C---D  master

В цьому випадку ви говорите мерзотник перебазуватися здійснюють від topicAдо topicBу верхній частині master.


8

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

git rebase master

і результат:

Before                              After
A---B---C---F---G (master)          A---B---C---F---G (master)
         \                                           \
          D---E (HEAD next-feature)                   D'---E' (HEAD next-feature)

git rebase --ontoбільше точності. Це дозволяє нам вибирати конкретну команду, де ми хочемо почати, а також там, де ми хочемо закінчити. Як тут:

git rebase --onto F D

і результат:

Before                                    After
A---B---C---F---G (branch)                A---B---C---F---G (branch)
         \                                             \
          D---E---H---I (HEAD my-branch)                E'---H'---I' (HEAD my-branch)

Для отримання більш детальної інформації рекомендую ознайомитись із моєю власною статтею про git rebase - до огляду


@Makyen Звичайно, я буду мати це на увазі в майбутньому :)
womanonrails

Отже, ми можемо читати git rebase --onto F Dяк встановлену дитину батька D як F , чи не так?
Prihex

2

Для ontoвас потрібні дві додаткові гілки. За допомогою цієї команди ви можете застосувати комітети branchB, які базуються branchAна іншій гілці, наприклад master. У наведеному нижче прикладі branchBна основі branchAі ви хочете застосувати зміни branchBна masterбез застосування змін branchA.

o---o (master)
     \
      o---o---o---o (branchA)
                   \
                    o---o (branchB)

за допомогою команд:

checkout branchB
rebase --onto master branchA 

ви отримаєте наступну ієрархію фіксування.

      o'---o' (branchB)
     /
o---o (master)
     \
      o---o---o---o (branchA)

1
Чи можете ви поясніть, будь ласка, ще трохи, якщо ми хочемо перейти на головний майстер, як це стає поточною гілкою? Якби ви зробили rebase --onto branchA branchBце, щоб покласти всю головну гілку на голову відділенняA?
Полімераза

8
не повинно бути цього checkout branchB: rebase --onto master branchA?
goldenratio

4
Чому це було скасовано? це не робить те, що каже, що робить.
De Novo

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

0

Є ще один випадок, коли git rebase --ontoважко зрозуміти: коли ви переставляєте на комітку, що є результатом симетричного перемикача різниці (три крапки ' ...')

Git 2.24 (Q4 2019) робить кращу роботу з управління цією справою:

Див. Фіксувати 414d924 , здійснити 4effc5b , зробити c0efb4c , скоїти 2b318aa (27 серпня 2019 р.) Та здійснити 793ac7e , здійснити 359eceb (25 серпня 2019 р.) Від Дентона Лю ( Denton-L) .
Допомогли: Ерік Саншайн ( sunshineco) , Хуніо С Хамано ( gitster) , Швар Арнфьорр Б'ярмасон ( avar) та Йоганнес Шинделін ( dscho) .
Див. Команду 6330209 , здійснити c9efc21 (27 серпня 2019 р.) Та 4336d36 (25 серпня 2019 р.) Виконувати Ævar Arnfjörð Bjarmason ( avar).
Допомогли: Ерік Саншайн ( sunshineco) , Хуніо С Хамано ( gitster) , Швар Арнфьорр Б'ярмасон ( avar) та Йоганнес Шинделін ( dscho) .
(Об'єднав Хуніо С Хамано - gitster- у комітеті 640f9cd , 30 вересня 2019 р.)

rebase: перемотка вперед --ontoу більшості випадків

Раніше, коли у нас був такий графік,

A---B---C (master)
     \
      D (side)

біг ' git rebase --onto master... master side' призведе до Dтого, що завжди буде переоцінено, незважаючи ні на що.

У цей момент прочитайте " Які відмінності між" двократкою " .." і "потрійною точкою" ..."в діапазонах комірок Git diff? "

https://sphinx.mythic-beasts.com/~mark/git-diff-help.png

Тут: " master..." посилається на master...HEAD, а саме B: HEAD - це сторона HEAD (наразі перевірена): ви переходите на B.
Що ви відпускаєте? Будь-яка фіксація не головна, а доступна з sideгілки: є лише одна фіксація, що відповідає цьому опису: D... яка вже знаходиться на вершині B!

Знову ж таки, перед Git 2.24, такий rebase --ontoрезультат призведе до Dтого, що він завжди буде переоформлений, незважаючи ні на що.

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

Це схоже на rebase --onto B AОП, який нічого не зробив.

Додайте виявлення, щоб can_fast_forwardцей випадок міг бути виявлений і буде здійснено швидке перемотування вперед.
Перш за все, перепишіть функцію для використання gotos, що спрощує логіку.
Далі, починаючи з

options.upstream &&
!oidcmp(&options.upstream->object.oid, &options.onto->object.oid)

Умови були усунені cmd_rebase, ми знову вводимо заміну в can_fast_forward.
Зокрема, перевірка основ об'єднання upstreamта headвиправлення несправного випадку в t3416.

Скорочений графік для t3416 такий:

        F---G topic
       /
  A---B---C---D---E master

і невдала команда була

git rebase --onto master...topic F topic

Раніше Git бачив, що існує одна база злиття ( C, результат master...topic), і злиття і на те саме, щоб воно неправильно повернуло 1, що вказує на те, що ми можемо перейти вперед. Це призведе до того, що графік на основі базису буде " ABCFG", коли ми очікували " ABCG".

Засіб rebase --onto C F topicозначає будь-яке вчинення після F , яке може бути досягнуте topicГОЛОВОЮ: це Gлише, а не Fсама.
Швидке переадресація в цьому випадку буде включати Fв перезавантажену гілку, що є неправильним.

За допомогою додаткової логіки ми виявляємо, що є база злиття вгору за течією та головою F. Оскільки це не так F, це означає, що ми не відпускаємо повний набір комісій master..topic.
Оскільки ми виключаємо деякі зобов’язання, перемотка вперед не може бути виконана, і тому ми повертаємо 0.

Додайте ' -f' до тестових випадків, які не вдалися в результаті цієї зміни, оскільки вони не очікували швидкого перемотування вперед, щоб резабагація вимушена.

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