Это немного длинно, потому что вы решительно хотите понять, что происходит, поэтому я собираюсь предоставить больше информации, чем просто прямой ответ на ваш вопрос. Но если вы ничего больше не отнимаете от этого: проверьте ваше локальное состояние перед нажатием . (И вторая секунда: будьте более скептичны в отношении силовых толчков.)
Люди привыкли думать, что "rebase" == "нужно принудительно толкать", и эти два в некотором роде связаны; но это не просто акт перебазирования, который создает необходимость форсировать толчок. Это удаление коммитов из истории ветки (скажем, BranchX) , а затем это только ветвь X, которую нужно принудительно нажать .
Итак, помня об этом, давайте пройдемся по вашему рабочему процессу - сначала так, как он должен работать, а затем так, как это случилось с этой ошибкой. Для начала, ваш репо может выглядеть как
... O <--(origin/v1.0)(v1.0)
\
.. M -- x <--(origin/v2.0)(v2.0)
\
... M -- x <--(origin/master)(master)
Здесь ...
означает «некоторая история, которая нас не особо волнует», x
означает «коммит, но не тот, на который я собираюсь специально ссылаться по имени в этом обсуждении», M
означает « коммит слияния, но не тот, на который я собираюсь специально ссылаться по имени в этом обсуждении ". Другие буквы означают коммиты, на которые я мог бы ссылаться по имени. Если бы я мог ссылаться на слияние по имени, я назову это что-то вроде M1
. Затем /
, \
и --
показывают отношения родитель-потомок между коммитами (более новый коммит справа), а имя в паренах - это ссылка (например, ветвь) со стрелкой, которая показывает текущий коммит ссылки. .
В дополнение к локальным филиалам я показал ссылки на удаленное отслеживание, т. Е. Понимание вашего репо того, где находятся филиалы на пульте.
Итак ...
Предполагаемое поведение
1) Ветка от v1.0
... O <--(origin/v1.0)(v1.0)(feature_branch)
\
.. M -- x <--(origin/v2.0)(v2.0)
\
... M -- x <--(origin/master)(master)
Здесь мы только что создали новую ссылку, которая указывает на тот же коммит, что и ветка версии.
2) Внести некоторые изменения
A <--(feature_branch)
/
... O <--(origin/v1.0)(v1.0)
\
.. M -- x <--(origin/v2.0)(v2.0)
\
... M -- x <--(origin/master)(master)
3) git rebase origin/v1.0
Этот шаг кажется немного пессимистичным. Есть ли у вас частые, одновременные изменения в старых версиях вашего продукта? Если нет, то я рассмотрю это только в качестве шага обработки исключений для случаев, когда на самом деле появляются новые изменения в v1.0
. Приведенный выше график не изменится, но если предположить, что произошли промежуточные изменения
A <--(feature_branch)
/
| B <--(origin/v1.0)
|/
... O <--(v1.0)
\
.. M -- x <--(origin/v2.0)(v2.0)
\
... M -- x <--(origin/master)(master)
тогда этот шаг даст вам
A' <--(feature_branch)
/
B <--(origin/v1.0)
/
| A
|/
... O <--(v1.0)
\
.. M -- x <--(origin/v2.0)(v2.0)
\
... M -- x <--(origin/master)(master)
Обратите внимание, что feature_branch
"перемещен", так что A
был удален из его истории, в то время как B
и новый коммит (A'
- копия A
) с исправлениями была добавлена к его истории. история.
Я все еще показываю A
на картинке, потому что он все еще существует, хотя на данный момент ничто не ссылается на него. (Но об этом я еще скоро расскажу ...) Это усиливает важный момент, который часто неправильно понимают: rebase
не «двигался» A
. Он создал новый коммит A'
, который отличается от A
. Вот почему я говорю, что A
был удален из истории feature_branch
.
В любом случае, все другие рефери сохраняют всю историю, которую они уже имели.
4) git push -f origin feature_branch
Это немного сбивает с толку, потому что вы не показываете, что вы ранее нажали feature_branch
. Если вы этого не сделали, тогда флаг -f
не нужен - потому что, даже если вы удалили коммиты из локальной истории feature_branch
, пульт дистанционного управления об этом ничего не знает.
То есть уточнение того, что я сказал выше - принудительное нажатие необходимо только при удалении ссылки из истории, из которой вы удалили коммит , который был частью истории пульта для этой ветви .
Итак, давайте предположим, что перед тем, как вас обмануть, у было нажато feature_branch
, и диаграмма действительно выглядит как
A' <--(feature_branch)
/
B <--(origin/v1.0)
/
| A <--(origin/feature_branch)
|/
... O <--(v1.0)
\
.. M -- x <--(origin/v2.0)(v2.0)
\
... M -- x <--(origin/master)(master)
(Это реальная причина, по которой я сохранил A
на диаграмме.) Теперь вы не сможете нажать feature_branch
без флага -f
, потому что нажатие удалит A
с пульта Понимание истории feature_branch
.
Но сейчас самое время упомянуть ... основываясь на моих комментариях по поводу шага 3, вам следует опасаться рабочего процесса, в котором принудительное нажатие является обычным шагом. Так же, как пульт ДУ знает A
как часть feature_branch
и должен быть уведомлен, что история была отредактирована, если у любого другого разработчика есть fetch
ed или pull
ed feature-branch
, то принудительное нажатие сделает их репо в сломанном состоянии. Им придется восстанавливаться, особенно если они внесли дополнительные изменения в feature-branch
; и если они сделают это неправильно, это может отменить ребаз.
Тем не менее, картинка после push
будет
A' <--(feature_branch)(origin/feature_branch)
/
B <--(origin/v1.0)
/
... O <--(v1.0)
\
.. M -- x <--(origin/v2.0)(v2.0)
\
... M -- x <--(origin/master)(master)
(На этот раз я удалил A
, потому что мы уже беспокоились об этом. Он все еще там, все еще доступен в reflog, но в конечном итоге gc
уничтожит его, если вы не предпримете шаги, чтобы воскресить его.)
5) ускоренное слияние feature_branch
в v1.0
Предположительно, вы также хотите нажать v1.0
после ускоренной перемотки вперед. Поскольку это быстрая перемотка вперед (даже для пульта дистанционного управления), принудительное нажатие не требуется; то есть каждый коммит, который удаленный когда-либо видел как часть v1.0
, все еще является частью v1.0
.
... O -- B -- A' <--(v1.0)(origin/v1.0)(feature_branch)(origin/feature_branch)
\
.. M -- x <--(origin/v2.0)(v2.0)
\
... M -- x <--(origin/master)(master)
5 и 6) объединить вперед
Это просто, и снова толчки не нужно заставлять.
... O ----- B ---- A' <--(v1.0)(origin/v1.0)(feature_branch)(origin/feature_branch)
\ \
.. M -- x ------- M <--(or-igin/v2.0)(v2.0)
\ \
... M -- x -- M <--(origin/master)(master)
Хорошо. Теперь, насколько я понимаю, проблема возникла, когда вы создали функцию из master
. На данный момент я собираюсь добавить отдельные имена к паре x
коммитов и удалить некоторые ссылки, о которых мы не будем говорить
... O ----- B ---- A' <--(v1.0)(origin/v1.0)
\ \
.. M -- V ------- M <--(or-igin/v2.0)(v2.0)
\ \
... M -- W -- M <--(origin/master)(master)
так что после шагов 1 и 2 у вас есть
... O ----- B ---- A' <--(v1.0)(origin/v1.0)
\ \
.. M -- V ------- M <--(or-igin/v2.0)(v2.0)
\ \
... M -- W -- M <--(origin/master)(master)
\
C -- D <--(feature2)
Но затем вы начали работать через описанный выше рабочий процесс, как если бы это была v1.0
функция. Так что для шага 3
git rebase origin/v1.0
И, как вы знаете, это будет проблемой. Все в истории текущей ветки, а не в истории origni/v1.0
, рассматривается как «необходимость копирования».
V' -- W' -- C' -- D' <--(feature)
/
... O ----- B ---- A' <--(v1.0)(origin/v1.0)
\ \
.. M -- V ------- M <--(or-igin/v2.0)(v2.0)
\ \
... M -- W -- M <--(origin/master)(master)
\
C -- D
Коммиты слияния игнорируются (поведение по умолчанию rebase
; в любом случае они не должны вносить четких изменений, хотя разрешение конфликтов и / или "злые слияния" могут нарушить это предположение). Но V
и W
не игнорируются. Как всегда, обратите внимание, что V
и W
остаются, а истории каждой ветви , за исключением текущей ветви, которую вы перебазировали, остаются без изменений.
Как и в приведенном выше рабочем процессе, теперь вы можете нажать feature
. И, как указано выше, если бы у вас когда-либо было push
ed feature
до ребазинга, вам теперь пришлось бы принудительно толкнуть его ... , но ваш типичный рабочий процесс обманул вас в ожидании, что в любом случае , поэтому, пока он поднимает красный флаг, он не будет.
В любом случае, вы можете видеть, что v1.0
с радостью перенесется на feature
(потому что история feature
в любом случае включает всю историю v1.0
), и это означает, что v1.0
будет толкать без силы.
Так вот как все пошло не так, но что делать дальше?
Мой первый совет - чувствовать себя менее комфортно при случайных толчках. На первый взгляд, поскольку принудительное нажатие и переписывание истории (например, rebase
) каким-то образом связаны, это может звучать как причина использовать слияния вместо rebase ... но это не поможет. Если ваша ветка была закодирована с master
... O ----- B ---- A' <--(v1.0)(origin/v1.0)
\ \
.. M -- V ------- M <--(or-igin/v2.0)(v2.0)
\ \
... M -- W -- M <--(origin/master)(master)
\
C -- D <--(feature2)
но потом ошибочно думаю, что вам нужно перейти на v1.0
, слияние будет работать так же тихо, а результат будет таким же неправильным.
-------------------------------- M <--(v1.0)
/ /
... O ----- B ---- A' <--(origin/v1.0) |
\ \ |
.. M -- V ------- M <--(origin/v2.0)(v2.0) |
\ \ |
... M -- W -- M <--(origin/master)(master) |
\ /
C ---------------------- D <--(feature2)
Это все еще включает все v2.0
и master
изменения в v1.0
.
Так что вы можете сделать?
Вы можете построить свой рабочий процесс на оптимистическом предположении, что v1.0
не получит конфликтующие изменения. В этом случае вы бы
1) создать ветку из v1.0
2) внести изменения
3) перемотка вперед v1.0
на feature
(git merge --ff-only feature
)
4) попытка толкнуть v1.0
без силы
Теперь, если вы попытаетесь включить ваши изменения в неправильную ветку, есть вероятность, что слияние не удастся (из-за --ff-only
). Это поможет, только если ветви действительно разошлись; но это, по крайней мере, не хуже, чем статус-кво.
Если вы идете в правильную ветку, и если шаг 4 успешен, то все готово. Если шаг 4 завершается неудачно и выдает ошибку об изменениях без ускоренной пересылки, то (поскольку это скорее исключение, чем ваш предполагаемый шаг), он намекает на то, что вам следует проверить и убедиться, , почему произошел сбой. Если все выглядит хорошо, то в следующий раз вы можете
git pull --rebase
Это сокращение для извлечения удаленных изменений и изменения локальных изменений поверх них. Это считается «потенциально опасной» операцией в соответствии с документацией, но то же самое вы делаете; и, по крайней мере, это создает некую структуру, так что, пока вы правильно слились, это будет делать то, что вы хотите.
Тогда вам всегда следует по привычке быстро взглянуть на локальный результат, прежде чем нажимать - потому что проблемы всегда легче устранить, прежде чем их подтолкнуть. Посмотрите на журнал и, если что-то выглядит странно, изучите его. Вернитесь к требованию / истории / карточке / тому, что вам было сказано, и подтвердите, к какой ветви вы добавляете это. Может быть, визуализировать общее состояние репо с помощью таких инструментов, как gitk
.
Итог, git
очень гибкий и очень мощный, поэтому, если вы явно скажете ему сделать что-то не то, он, вероятно, сделает это. Это означает, что вы должны знать, что сказать, чтобы сделать это. Хорошей новостью является то, что обычно не 1213 * слишком трудно исправить после ошибок. Проще всего до того, как ошибка будет push
ed, но более или менее всегда есть выход.