Git: Как сделать ребаз для конкретного коммита? - PullRequest
121 голосов
/ 12 октября 2011

Я бы хотел сделать ребаз в конкретный коммит, а не в ГОЛОВУ другой ветки:

A --- B --- C          master
 \
  \-- D                topic

до

A --- B --- C          master
       \
        \-- D          topic

вместо

A --- B --- C          master
             \
              \-- D    topic

Как мне этого добиться?

Ответы [ 9 ]

75 голосов
/ 12 октября 2011

Вы можете избежать использования параметра --onto, сделав временную ветвь на коммите, который вам нравится, и затем использовать rebase в его простой форме:

git branch temp master^
git checkout topic
git rebase temp
git branch -d temp
56 голосов
/ 08 июля 2014

Вы даже можете принять прямой подход:

git checkout topic
git rebase <commitB>
44 голосов
/ 12 октября 2011

Используйте опцию "на":

git rebase --onto master^ D^ D
11 голосов
/ 13 октября 2017

Комментарий jsz выше спас меня от тонны боли, поэтому вот пошаговый получатель, основанный на нем, который я использовал, чтобы перебазировать / переместить любой коммит поверх любого другого коммита:

  1. Найти предыдущую точку ветвления ветви, которую нужно перебазировать (переместить) - назовите ее старым родителем. В приведенном выше примере это A
  2. Найдите коммит, поверх которого вы хотите переместить ветку - назовите его новым родителем. В экзамене это B
  3. Вы должны быть в своей ветке (той, которую вы двигаете):
  4. Примените свою скидку: git rebase --onto <new parent> <old parent>

В приведенном выше примере это так же просто, как:

   git checkout topic
   git rebase --onto B A
3 голосов
/ 07 марта 2019

Тема решения

Правильная команда для ответа на заданный вопрос может быть любой из следующих (при условии, что ветвь topic уже извлечена):

git rebase --onto B master
git rebase --onto master~1 master
git rebase --onto B A
git rebase --onto B C
git rebase --onto B

Если topic не отмечен, вы просто добавляете topic к команде (кроме последней) следующим образом:

git rebase --onto B master topic

В качестве альтернативы сначала проверьте ветку с помощью:

git checkout topic

Перебазировать любую строку коммитов в целевой коммит

Базовая форма нужной нам команды, взятая из документации:

git rebase --onto <Target> [<Upstream> [<Branch>]]

<Branch> является необязательным, и все, что он делает, - проверяет ветвь, указанную перед выполнением остальной части команды. Если вы уже отметили ветку, которую хотите перебазировать, то вам это не нужно. Обратите внимание, что вы должны указать <Upstream>, чтобы указать <Branch>, или git подумает, что вы указываете <Upstream>.

<Target> - это коммит, к которому мы прикрепим нашу строку коммитов. Предоставляя имя ветки, вы просто указываете главный коммит этой ветки. <Target> может быть любым коммитом, который не будет содержаться в строке перемещаемых коммитов. Например:

A --- B --- C --- D         master
      \
       \-- X --- Y --- Z    feature

Чтобы переместить всю ветвь объекта, вы не можете выбрать X, Y, Z или feature в качестве <Target>, поскольку все они являются коммитами внутри перемещаемой группы.

<Upstream> особенный, потому что это может означать две разные вещи. Если это коммит, который является предком проверенной ветви, то он служит точкой вырезания. В приведенном мною примере это будет что-то, что не является C, D или master. Все коммиты после <Upstream> до тех пор, пока не будет перемещен заголовок извлеченной ветви.

Однако, если <Upstream> не является предком, то git выполняет резервное копирование цепочки из указанного коммита до тех пор, пока if не найдет общего предка с извлеченной ветвью (и не прекратит работу, если не может его найти). В нашем случае <Upstream> из B, C, D или master приведет к тому, что коммит B будет служить точкой отсечения. <Upstream> сама по себе является необязательной командой, и если она не указана, то git просматривает родителя извлеченной ветви, что эквивалентно вводу master.

Теперь, когда git выбрал коммиты, которые он будет резать и перемещать, он применяет их для <Target>, пропуская все, что уже применено к цели.

Интересные примеры и результаты

Используя эту отправную точку:

A --- B --- C --- D --- E         master
            \
             \-- X --- Y --- Z    feature
  • git rebase --onto D A feature
    Применит коммиты B, C, X, Y, Z для фиксации D и пропустит B и C, поскольку они уже были применены.

  • git rebase --onto C X feature
    Применит коммиты Y и Z для фиксации C, эффективно удаляя коммит X

2 голосов
/ 17 января 2019

Более простое решение - git rebase <SHA1 of B> topic. Это работает независимо от того, где находится ваш HEAD.

Мы можем подтвердить это поведение из git rebase doc

<upstream> Восходящая ветка для сравнения. Может быть любым действительным commit , а не просто имя существующей ветви. По умолчанию настроено вверх по течению для текущей ветви.

<ч /> Вы можете подумать, что произойдет, если я упомяну SHA1 из topic также в приведенной выше команде?

git rebase <SHA1 of B> <SHA1 of topic>

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

2 голосов
/ 17 октября 2017

Я использовал смесь растворов, описанных выше:

$ git branch temp <specific sha1>
$ git rebase --onto temp master topic
$ git branch -d temp

Мне было намного легче читать и понимать. Принятое решение привело меня к конфликту слияний (слишком ленив, чтобы исправить вручную):

$ git rebase temp
First, rewinding head to replay your work on top of it...
Applying: <git comment>
Using index info to reconstruct a base tree...
M       pom.xml
.git/rebase-apply/patch:10: trailing whitespace.
    <some code>
.git/rebase-apply/patch:17: trailing whitespace.
        <some other code>
warning: 2 lines add whitespace errors.
Falling back to patching base and 3-way merge...
Auto-merging pom.xml
CONFLICT (content): Merge conflict in pom.xml
error: Failed to merge in the changes.
Patch failed at 0001 <git comment>
The copy of the patch that failed is found in: .git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
1 голос
/ 16 января 2019

Поскольку перебазирование является настолько фундаментальным, вот расширение Ответ Нестора Миляева .Объединяя jsz и комментариев Саймона Юга от Ответ Адама Димитрука , получается команда, которая работает на ветви topic независимо от того, ветвится ли она на ветви mastercommit A или C:

git checkout topic
git rebase --onto <commit-B> <pre-rebase-A-or-post-rebase-C-or-base-branch-name>

Обратите внимание, что требуется последний аргумент (в противном случае он перематывает вашу ветку для фиксации B).

Примеры:

# if topic branches from master commit A:
git checkout topic
git rebase --onto <commit-B> <commit-A>
# if topic branches from master commit C:
git checkout topic
git rebase --onto <commit-B> <commit-C>
# regardless of whether topic branches from master commit A or C:
git checkout topic
git rebase --onto <commit-B> master

Итак, последняя команда, которую я обычно использую.

0 голосов
/ 29 августа 2018

Есть другой способ сделать это, или если вы хотите вернуться к более чем одному коммиту.

Вот пример возврата к n количеству коммитов:

git branch topic master~n

Ради этого вопроса это также можно сделать:

git branch topic master~1

Команда отлично работает на git version 2.7.4.Не проверял ни на одной другой версии.

...