Подтвердить что ты не робот

Я не могу понять поведение git rebase --onto

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

У меня есть ветки A и B которые расходятся с одним commit

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

Я хочу перебазировать ветку B на последней A (и получить коммит на ветке B)

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

Нет проблем, если я сделаю:

checkout B
rebase A

Но если я сделаю:

checkout B
rebase --onto B A

Это не работает вообще, ничего не происходит. Я не понимаю, почему эти два поведения отличаются.

Gp-клиент Phpstorm использует второй синтаксис, и поэтому мне кажется, что он полностью нарушен, поэтому я спрашиваю об этой проблеме синтаксиса.

4b9b3361

Ответ 1

TL;DR

Правильный синтаксис для rebase B поверх A с помощью git rebase --onto в вашем случае:

git checkout B
git rebase --onto A B^

или rebase B поверх A, начиная с commit, являющегося родителем 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 --onto с двумя аргументами

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, или rebase HEAD поверх B, где старый родитель был E.

Хирург: git rebase --onto с тремя аргументами

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, что означает:

Восстановите диапазон коммитов, чей родительский элемент D до H поверх F.

Синтаксис git rebase --onto с диапазоном коммитов тогда становится git rebase --onto <newparent> <oldparent> <until>. Трюк здесь помнят, что фиксация, на которую ссылается <until>, включена в диапазон и станет новой HEAD после завершения переадресации.

Ответ 2

Это все, что вам нужно знать, чтобы понять --onto:

git rebase --onto <newparent> <oldparent>

Вы переключаете родителя на фиксацию, но вы не предоставляете sha коммита, а только его текущий (старый) родительский.

Ответ 3

Короче говоря, учитывая:

      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. Обратите внимание, что диапазон является эксклюзивным для левой руки. Он эксклюзивен, потому что проще указать 1-й коммит, набрав, например, branch чтобы позволить git найти 1-й дивертированный коммит из branch т.е. D который приводит к H

OP чехол

    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 пропускается, потому что она уже применена к цели --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). Что интересно, когда вторым аргументом является хеш коммита, а перебазирование имени ветки все еще работает, но ветка не перемещается, поэтому вы попадаете в "отсоединенную головку" вместо того, чтобы быть извлеченным в перемещенную ветвь.

Пропустить первичные дивергентные коммиты.

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".

Избегайте явных проверок.

По умолчанию аргумент HEAD или current_branch берется из того места, где вы находитесь. Именно поэтому большинство людей обращаются к ветке, которую они хотят перебазировать. Но когда 2-й аргумент rebase задан явно, вам не нужно проверять перед 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.

Ответ 4

Проще говоря, 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

В этом случае вы скажете git для переустановки коммитов от topicA до topicB поверх master.

Ответ 5

Для onto вам понадобятся две дополнительные ветки. С помощью этой команды вы можете применять коммиты из branchB, основанные на branchA, на другую ветвь, например. master. В приведенном ниже примере branchB основан на branchA, и вы хотите применить изменения branchB на master без применения изменений branchA.

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

с помощью команд:

checkout master
rebase --onto branchA branchB

вы получите следующую иерархию фиксации.

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

Ответ 6

В другом случае трудно понять 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).
(Merged by Junio C Hamano -- [TG412] -- in commit 640f9cd, 30 Sep 2019)

rebase: ускоренная перемотка вперед --onto в других случаях

Раньше, когда у нас был следующий график,

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

запуск "git rebase --onto master... master side" приведет к тому, что D будет всегда перебазирован, несмотря ни на что.

На этом этапе прочитайте "В чем различия между двойной точкой '..' и тройной точкой" ... "в диапазонах фиксации различий в Git?"

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

Здесь: "master..." относится к master...HEAD, то есть B: HEAD является боковым HEAD (в настоящее время проверено): вы переходите на B.
Что ты ребазируешь? Любой коммит не в master и доступный из ветки side: есть только один коммит, соответствующий этому описанию: D... который уже находится над B!

Опять же, до Git 2.24 такой rebase --onto приводил к тому, что D всегда перебазировался, несмотря ни на что.

Однако желаемое поведение заключается в том, что rebase должен заметить, что это быстро пересылаемое, и сделать это вместо этого.

Это похоже на 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 HEAD: это только G, но не сам F.
В этом случае ускоренная перемотка вперед включит F в перебазированную ветку, что неправильно.

С помощью дополнительной логики мы обнаруживаем, что восходящая и основная база слияния это F. Так как на F нет, это означает, что мы не отказываемся от полного набора фиксирует из master..topic.
Так как мы исключаем некоторые коммиты, ускоренная перемотка вперед не может быть выполнена, поэтому мы правильно возвращаем 0.

Добавьте "-f" в тестовые случаи, которые не были выполнены в результате этого изменения, потому что они не ожидали быстрой перемотки вперед, так что перебазировка была вынуждена.