Git ребаз с объединенными коммитами - PullRequest
0 голосов
/ 05 июля 2018

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

Мой статус git-репо теперь примерно такой:

      C---D---E  feature-branch
     /
A---B---C---D---X---F  master
                |
         Reverts C and D

Я работал над feature-branch, а коммиты C & D были случайно объединены для освоения (сложная история). Другие, работающие в репо, отменили эти коммиты, так как это нарушает сборку.

Теперь, после коммита E, я хочу перебазировать ветку Feature с текущим мастером. Но проблема в том, что, поскольку коммиты C & D уже присутствуют в мастере, эти коммиты не включаются при перебазировании.

со страницы руководства пользователя git:

Если вышестоящая ветвь уже содержит внесенные вами изменения (например, потому что вы отправили по почте исправление, которое было применено в восходящем потоке), то эта фиксация будет пропущена. Например, при запуске git rebase master в следующей истории (в которой A 'и A вводят одинаковый набор изменений, но имеют разную информацию о коммитере):

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

приведет к:

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

Как я могу включить коммиты C & D (возможно, с новым SHA) сейчас, так как изменения возвращаются обратно коммитом X?

Ответы [ 3 ]

0 голосов
/ 05 июля 2018

В ветви функций выполнить,

git rebase master

, который будет перебазирован на master и приведет к ожидаемому результату, игнорируя C D. (A---B---C---D---X---F--E в Feature-Branch)

Затем выполните git reset --soft <sha of F>, чтобы временно избавиться от коммита E.

Далее git stash изменения коммита сброса (т.е. E)

Затем в том же feature-branch выполните следующее,

git cherry-pick <sha of C>
git cherry-pick <sha of D>

Наконец git stash pop и сделайте git commit, чтобы вы вернули свой E.

Это будет делать то, что вы ожидали.

Я проверил на своей машине, и ниже приведен результат, полученный git log --online в feature-branch

ae4b6bb E commited
eee0a6a D commited
3141961 C commited
a44aa27 F Commited
265da4c Commited X
9e2729d D commited
84a3a9b C commited
8a543ca B commited
4e4a8ca A commited
0 голосов
/ 05 июля 2018

tl; dr: Вы можете делать что хотите с помощью команды, подобной

git rebase --onto master feature-branch~3 feature-branch

(где feautre-branch~3 работает в примере, но в общем случае это будет любое выражение, которое разрешает коммит B - например, хеш коммита для B и т. Д.)

Чтобы узнать, почему это работает, читайте дальше ...


Во-первых, мы должны немного уточнить вашу диаграмму. Вы сказали

Я работал над веткой возможностей, и коммиты C & D были случайно объединены с мастером (сложная история). Другие, работающие в репо, отменили эти коммиты, поскольку это нарушает сборку.

(выделение добавлено). Теперь слияние может выполнять одно из двух [1], но дублирование коммитов C и D не входит в их число. Поведение по умолчанию будет выглядеть так:

A -- B -- C -- D -- !CD -- F <-(master)
                \
                 E <-(feature-branch)

Поскольку master, по-видимому, находилось в B во время случайного слияния, поведение по умолчанию было бы "перемоткой вперед" master к существующему коммиту D. Тогда feature-branch рука не создается до тех пор, пока D.

(я также внес некоторые изменения в обозначения. !CD указывает на объединение, которое отменило C и D (без необходимости в аннотации). И я считаю, что этот способ рисования линий более читабелен. Важным моментом является то, как C и D представлены ...)

Другая возможность, если вы использовали опцию --no-ff (или если master отклонился от feature-branch до слияния), выглядела бы больше как

       C - D -- E <--(feature-branch)
      /     \
A -- B ----- M -- !CD -- F <--(master)

Опять же merge не будет дублировать C или D; вместо этого он создаст «коммит слияния» M, который включает в себя изменения из C и D в историю master (и делает C и D «достижимыми» из master).

В обоих случаях «база слияния» между feature-branch и master после слияния равна D. Таким образом, вам нужно решить две проблемы: проблему «дубликата патча», описанную в документации; и база слияния находится в точке, исключающей ваши коммиты - потому что rebase даже не будет рассматривать копирование коммитов, которые уже доступны из апстрима. [2]

Часто полезно решить только проблему "слияния". В конце мы увидим, почему вы не обязаны это делать, но для полноты вот как вы это сделаете:

Сначала найдите выражение, которое разрешается для фиксации B (например, в фиксации для B или feature-branch~3 в приведенных выше примерах). Используйте это в команде как

git rebase -f feature-branch~3 feature-branch

Это скопирует коммиты C, D и E, так что у вас будет

                 E
                /
A -- B -- C -- D -- !CD -- F <-(master)
      \          
       C' -- D' -- E' <-(feature-branch)

(где E технически все еще существует, но недоступен, поэтому в конечном итоге может быть уничтожен gc). Конечно, это предполагает «ускоренную перемотку вперед»; если бы у вас был коммит слияния, он выглядел бы иначе, но результат был бы таким же.

И в результате вы можете без проблем объединить feature-branch в master. Но если вы хотите от rebase feature-branch до master (при подготовке к ускоренной перемотке вперед), вам все равно придется решить исходную проблему, которую вы указали: C' и D' будут пропущены как дубликаты C и D.

Вы хотите помешать проверке дубликатов идентификатора патча; и хотя кажется, что для этого нет реальной возможности, вы можете сделать это, не давая git знать, где искать дубликаты. Вместо указания master в качестве восходящего потока, передайте commit B в качестве восходящего; а затем перебазировать --onto master.

git rebase --onto master feature-branch~3 feature-branch

Не только git не имеет смысла учитывать C и D, когда вы даете эту команду, но и потому, что вы больше не используете master в качестве восходящего потока, master -to- feature-branch База слияния больше не имеет значения. Вы явно говорите, что B - ваш восходящий поток, так что это решает обе проблемы одновременно, поэтому, как я уже отмечал выше, вам, в конце концов, не придется беспокоиться о команде rebase -f.


[1] На самом деле, есть, по крайней мере, третье, что он может сделать. Если бы вы указали опцию --squash, то она не осуществила бы реальное слияние, а вместо этого создала бы один новый коммит CD в master. По сути, это то же самое, что и коммит слияния, который обычно создается, за исключением того, что он не включает D в качестве родителя.

       C - D -- E <--(feature-branch)
      /     
A -- B ----- CD -- !CD -- F <--(master)

Это полезно для некоторых конкретных ситуаций, но в целом я не рекомендую его, так как git «теряет учет» того факта, что C и D уже «учтены» в master. В вашей ситуации это может сработать хорошо - вот почему я знаю, что вы не сделали этого таким образом и не включили эту возможность в основной текст ответа. Но в большинстве случаев это затрудняет будущие взаимодействия между ветвями.


[2] Эти проблемы кажутся похожими, но различаются. В одном случае C уже уже само по себе достижимо от восходящего потока, к которому вы переходите; в другом случае это не так, но другой коммит, который вносит те же изменения .

0 голосов
/ 05 июля 2018

Для этого вы можете использовать git cherry-pick . В вашем конкретном примере команда будет:

in master# git cherry-pick topic~3..topic
...