Git push отклонен после перебазировки ветки

Хорошо, я думал, что это был простой сценарий мерзавца, что я пропускаю?

у меня есть master филиал и feature ветка. Я работаю над master некоторые на feature, а затем еще немного о master, Я получаю что-то вроде этого (лексикографический порядок подразумевает порядок коммитов):

A--B--C------F--G  (master)
       \    
        D--E  (feature)

У меня нет проблем с git push origin master держать пульт master обновлено, ни с git push origin feature (когда на feature) поддерживать удаленное резервное копирование для моего feature Работа. До сих пор у нас все хорошо.

Но теперь я хочу перебазировать feature на вершине F--G совершает на мастера, поэтому я git checkout feature а также git rebase master, Все еще хорош. Теперь у нас есть:

A--B--C------F--G  (master)
                 \
                  D'--E'  (feature)

Проблема: в тот момент, когда я хочу сделать резервную копию, новый ребазед feature разветвленный с git push origin feature толчок отклоняется, поскольку дерево изменилось из-за перебазирования. Это может быть решено только с git push --force origin feature,

Я ненавижу использовать --force не будучи уверенным, что мне это нужно. Так мне это нужно? Обязательно ли перебазировка подразумевает, что следующий push должно быть --force FUL?

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

14 ответов

Решение

Проблема в том, что git push Предполагается, что удаленная ветвь может быть быстро перенаправлена ​​в вашу локальную ветвь, то есть, что вся разница между локальной и удаленной ветвями заключается в том, что в конце есть несколько новых коммитов:

Z--X--R         <- origin/some-branch (can be fast-forwarded to Y commit)
       \        
        T--Y    <- some-branch

Когда вы выполняете git rebase коммиты D и E применяются к новой базе, и создаются новые коммиты. Это означает, что после перебазирования у вас есть что-то вроде этого:

A--B--C------F--G--D'--E'   <- feature-branch
       \  
        D--E                <- origin/feature-branch

В этой ситуации удаленная ветвь не может быть быстро перенаправлена ​​на локальную. Хотя теоретически локальная ветвь может быть объединена с удаленной (очевидно, в этом случае она вам не нужна), но как git push выполняет только быстрое слияние, выбрасывание и ошибку.

И что --force Опция просто игнорирует состояние удаленной ветви и устанавливает его в коммит, который вы вводите в него. Так git push --force origin feature-branch просто переопределяет origin/feature-branch с местными feature-branch,

На мой взгляд, перебазирование веток master и принудительно отправлять их обратно в удаленное хранилище - это нормально, если вы единственный, кто работает в этой ветке.

Вместо использования -f или --force разработчики должны использовать

--force-with-lease

Зачем? Потому что он проверяет удаленную ветку на наличие изменений, что является хорошей идеей. Давайте представим, что Джеймс и Лиза работают над одной и той же веткой функций, а Лиза выдвинула коммит. Джеймс теперь перебазирует свою местную ветку и отклоняется при попытке толкнуть. Конечно, Джеймс думает, что это происходит из-за перебазирования и использует --force, и переписал бы все изменения Лизы. Если бы Джеймс использовал --force-with-аренды, он получил бы предупреждение о том, что кто-то другой совершил коммиты. Я не понимаю, почему кто-то будет использовать --force вместо --force-with-lease при нажатии после ребазинга.

Я бы использовал вместо "checkout -b", и это легче понять.

git checkout myFeature
git rebase master
git push origin --delete myFeature
git push origin myFeature

когда вы удаляете, вы не можете нажать на выходящую ветку с другим идентификатором SHA. Я удаляю только удаленную ветку в этом случае.

Одно из решений этой проблемы - сделать то, что делает сценарий слияния msysGit: после слияния слить в старый заголовок feature с -s ours, В итоге вы получите граф фиксации:

A--B--C------F--G (master)
       \         \
        \         D'--E' (feature)
         \           /
          \       --
           \    /
            D--E (old-feature)

... и ваш толчок feature будет перемотка вперед.

Другими словами, вы можете сделать:

git checkout feature
git branch old-feature
git rebase master
git merge -s ours old-feature
git push origin feature

(Не проверено, но я думаю, что это правильно...)

Другие ответили на ваш вопрос. Если вы перебазируете ветку, вам нужно будет принудительно нажать на эту ветку.

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

В целом, rebase хорошо работает для местного филиала. Удаленное управление филиалами лучше всего работает с явными слияниями (--no-ff).

Мы также избегаем слияния мастера в ветку функций. Вместо этого мы перебазируем на master, но с новым именем ветви (например, добавив суффикс версии). Это позволяет избежать проблемы перебазирования в общем хранилище.

Может быть, а может и нет, что в этой ветке только один разработчик, который сейчас (после ребазинга) не соответствует источнику / функции.

В качестве такового я бы предложил использовать следующую последовательность:

git rebase master
git checkout -b feature_branch_2
git push origin feature_branch_2

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

Мой способ избежать принудительного нажатия - создать новую ветку и продолжить работу над этой новой веткой и после некоторой стабильности удалить старую ветку, которая была перебазирована:

  • Перебазирование проверенной ветки локально
  • Ветвление из перебазированной ветки в новую ветку
  • Раздвигая эту ветку как новую ветку к удаленной. и удаление старой ветки на удаленной

Что не так с git merge master на feature ветка? Это сохранит работу, которую вы имели, сохранив ее отдельно от основной ветки.

A--B--C------F--G
       \         \
        D--E------H

Изменить: Ах, извините, не прочитал вашу проблему. Вам понадобится сила, когда вы выполнили rebase, Все команды, которые изменяют историю, будут нуждаться в --force аргумент. Это отказоустойчивый, чтобы предотвратить потерю работы (старый D а также E будет потеряно).

Итак, вы выполнили git rebase что заставило дерево выглядеть (хотя частично скрыто как D а также E больше не в именованной ветви):

A--B--C------F--G
       \         \
        D--E      D'--E'

Итак, при попытке подтолкнуть ваш новый feature филиал (с D' а также E' в нем), вы бы потеряли D а также E,

Следующие работы для меня:

git push -f origin branch_name

и это не удаляет любой мой код.

Но, если вы хотите избежать этого, вы можете сделать следующее:

git checkout master
git pull --rebase
git checkout -b new_branch_name

тогда вы можете выбрать все свои коммиты в новую ветку.git cherry-pick COMMIT IDа затем нажмите вашу новую ветку.

Для меня работает следующие простые шаги:

1. git checkout myFeature
2. git rebase master
3. git push --force-with-lease
4. git branch -f master HEAD
5. git checkout master
6. git pull

После всего вышеперечисленного, мы также можем удалить ветку myFeature, выполнив следующую команду:

git push origin --delete myFeature

Получение новых изменений в главной и функциональной ветке rebase поверх последней главной

git checkout master
git pull
git checkout feature
git pull --rebase origin master
git push origin feature

Поскольку ОП понимает проблему, просто ищет более хорошее решение...

Как насчет этого как практики?

  • Вступайте в настоящую ветку разработки функций (где вы никогда не перебираете и не форсируете, чтобы ваши коллеги-разработчики не ненавидели вас). Здесь регулярно берут эти изменения с основного слияния. Да, история сложнее, но жизнь легка, и никто не вмешивается в его работу.

  • Иметь вторую ветку разработки функций, где один член команды функций регулярно выталкивает все функции, делает их фактически перебазированными, действительно форсированными. Так что почти чисто основано на довольно недавнем генеральном коммите. По завершении функции нажмите эту ветку сверху мастера.

Для этого метода уже может быть имя шаблона.

Сначала извините, это не ответ Обычно это должен быть ответ, но я подумал, что было бы важно подчеркнуть, что ответы не выглядят как правильный способ использования перебазирования (некоторые из них являются допустимым обходным путем, но не так элегантны, как мы можно было ожидать от git).

  1. "git force" - опасно, может переопределить работу других
  2. Создание новой ветки — потребуется синхронизация с другими, кто работает над этой функциональной веткой, упустите смысл использования git.
  3. воссоздание ветки (после ее удаления с помощью или без однострочной магической команды), те же проблемы, что и в № 2.
  4. «git merge» — rebase был разработан, чтобы избежать/предотвратить коммиты слияния в журнале и поддерживать чистоту и согласованность журнала git.

К сожалению, у меня нет ответа (пока). Среди вышеперечисленных «git merge» — единственный, который безопасен для командной работы.


[Редактировать № 1] В соответствии с этим замечательным документом Atlassian rebase следует использовать только для вашей локальной работы.

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


[Редактировать № 2] Согласно этому прекрасному объяснению , это ожидаемое поведение. Это означает, что вам может потребоваться повторно объединить вещи, которые уже были объединены в исходном коммите. Если torec прав и если я прав, то я голосую за старый добрый поток извлечения (выборки/слияния).

[Edit #3] Все вышеперечисленное может быть правильным в зависимости от сценария. Если вы работаете над функциональной веткой с другом, не нажимайте --force, а извлекайте и объединяйте origin/feature1. Если вы работаете над веткой в ​​одиночку, вы можете нажать --force и получить красивый лог.

Я бы сделал как показано ниже

      rebase feature
git checkout -b feature2 origin/feature
git push -u origin feature2:feature2
Delete the old remote branch feature
git push -u origin feature:feature

Теперь пульт будет иметь функцию (переустановленную на последний мастер) и функцию 2(со старой головкой мастера). Это позволит вам позже сравнить, если вы допустили ошибки при разрешении конфликтов.

Другие вопросы по тегам