Основная ветвь и «происхождение / мастер» разошлись, как «отклонить» ветки?

965

Каким-то образом мой мастер и моя ветвь происхождения / мастера разошлись.
Я на самом деле не хочу, чтобы они расходились.

Как я могу просмотреть эти различия и «объединить» их?

Фрэнк
источник
2
что вы имеете в виду, расходясь? ты перебазируешь своего хозяина после его нажатия?
Хазен
13
Я получаю сообщение о том, что «ваша ветка и« origin / master »разошлись, # и имеют 1 и 1 разные коммиты соответственно».
Франк
Я обновил свой ответ, чтобы отразить это «разошедшееся» предупреждающее сообщение.
VonC
Принятый ответ на другой вопрос также может быть полезен при разрешении некоторых случаев, когда это может вступить в игру (например, вы пытаетесь переместить своего мастера, но его уже подтолкнули): stackoverflow.com/questions/2862590/…
Линд
8
Объяснение в этом блоге помогло мне бесконечно больше, чем любой ответ ниже: sebgoo.blogspot.com/2012/02/…

Ответы:

1014

Вы можете просмотреть различия с:

git log HEAD..origin/master

перед тем, потянув его (принести + слияние) (см «Как ты мерзавец всегда вытащить из конкретной отрасли?» )


Когда у вас есть сообщение вроде:

«Ваша ветка и« origin / master »разошлись, # и имеют 1 и 1 разных коммитов соответственно».

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

... o ---- o ---- A ---- B  origin/master (upstream work)
                   \
                    C  master (your work)

Вы основали коммит C на коммите A, потому что это была последняя работа, которую вы получили из апстрима в то время.

Однако, прежде чем вы попытались вернуться к исходной точке, кто-то еще подтолкнул коммит B.
История разработки разошлась по отдельным путям.

Затем вы можете объединить или перебазировать. Смотрите Pro Git: Git Branching - Rebasing для деталей.

Объединить

Используйте команду git merge:

$ git merge origin/master

Это говорит Git интегрировать изменения origin/masterв вашу работу и создавать коммит слияния.
Граф истории теперь выглядит так:

... o ---- o ---- A ---- B  origin/master (upstream work)
                   \      \
                    C ---- M  master (your work)

У нового слияния, commit M, есть два родителя, каждый из которых представляет один путь развития, который привел к содержанию, сохраненному в этом коммите.

Обратите внимание, что история позади M теперь нелинейна.

Rebase

Используйте команду git rebase:

$ git rebase origin/master

Это заставляет Git воспроизводить коммит C (вашу работу) так, как если бы вы основывали его на коммите B, а не на A. Пользователи
CVS и Subversion регулярно обновляют свои локальные изменения в верхней части исходной работы, когда они обновляются перед коммитом.
Git просто добавляет явное разделение между шагами commit и rebase.

Граф истории теперь выглядит так:

... o ---- o ---- A ---- B  origin/master (upstream work)
                          \
                           C'  master (your work)

Commit C '- это новый коммит, созданный командой git rebase.
Он отличается от C двумя способами:

  1. У него другая история: B вместо A.
  2. Его содержание учитывает изменения как в B, так и в C; это то же самое, что и M из примера слияния.

Обратите внимание, что история C 'все еще линейна.
Мы выбрали (на данный момент), чтобы разрешить только линейную историю в cmake.org/cmake.git.
Этот подход сохраняет рабочий процесс на основе CVS, который использовался ранее, и может облегчить переход.
Попытка вставить C 'в наш репозиторий сработает (при условии, что у вас есть разрешения, и никто не выдвигал их во время перебазировки).

Команда git pull обеспечивает сокращенный способ извлечения из источника и перебазирования локальной работы над ним:

$ git pull --rebase

Это объединяет вышеупомянутые шаги извлечения и перебазирования в одну команду.

VonC
источник
5
Я нашел это, когда искал ту же проблему. Можете ли вы объяснить, почему «git reset --hard HEAD» не устранил проблему?
Нес
12
@Neth: потому что речь идет не о поэтапных модификациях (то есть о модификациях, присутствующих в индексе, но еще не зафиксированных), а о локальных фиксациях (что отличается от коммитов, представленных на удаленном компьютере). git reset --hard HEADудалит только локальные индексированные не зафиксированные изменения и ничего не сделает для приведения в соответствие различий между локальным и удаленным коммитами . Только слияние или перебазирование объединят два набора коммитов (локальный и удаленный).
VonC
4
Вау, спасибо за этот потрясающий ответ. Мы случайно сделали «git pull» без «--rebase», и «git rebase origin / master» было просто исправлением!
mrooney
3
Как насчет - я просто хочу игнорировать / сбрасывать свои локальные изменения и находиться в моей локальной ветке, где находится пульт? Другими словами, я хочу masterуказать Bна ваш пример.
CygnusX1
23
@ CygnusX1, который будет, git reset --hard origin/masterкак указано в ответе чуть ниже: stackoverflow.com/a/8476004/6309
VonC
726

У меня было это, и я не уверен, что вызвало это, даже после прочтения вышеупомянутых ответов. Мое решение было сделать

git reset --hard origin/master

Затем это просто сбрасывает мою (локальную) копию мастера (которая, как я полагаю, облажалась) в правильную точку, представленную (удаленным) источником / мастером.

ВНИМАНИЕ : Вы потеряете все изменения, которые еще не были переданы origin/master.

skiphoppy
источник
21
да, это немного похоже на вариант манекенов, но если реальной опасности нет и вы здесь для быстрого решения - это работает (во всяком случае для меня)
PandaWood
7
Это должно быть в главной ветке раньше («git checkout master»).
голубоглазый
Это случилось со мной однажды, когда я получил последнее от источника, затем кто-то еще сделал принудительный толчок к источнику, что привело к отмене предыдущего коммита в источнике. Таким образом, в моем локальном филиале был зафиксирован обратный коммит, и когда я попытался извлечь последний из источника, он говорил, что мне нужно слить. В этом случае имеет смысл сбросить --hard origin / (branch), потому что проблема уже была исправлена ​​в origin.
Лэндон Поч
96
Вы, вероятно, должны предупредить пользователей, что это заставит их потерять все изменения, которые еще не были переданы в источник
Pedro Loureiro
6
@PedroLoureiro Коммиты действительно потеряны, вы все равно можете найти коммиты git reflogили увидеть их в gitk --all. Но все же, конечно, хард ресет это совсем не то, что ребейс.
Себкремер
52
git pull --rebase origin/master 

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

Редактировать: извлекает коммиты из источника / мастера и применяет ваши изменения к недавно извлеченной истории веток.

asitmoharna
источник
89
упомяните,
1
Если проблем нет, вы должны получить свой мастер, содержащий все исходные / основные изменения, плюс все ваши локальные коммиты будут воспроизведены поверх него. Кажется, хорошо для меня.
Филипп Классен
7
За исключением случаев, когда есть реальные различия, и это оставляет вас в прерванной перезагрузке.
Неизвестный
Это приводит к ошибке: ошибка: не удалось объединить изменения. Не удалось
исправить
32

Я оказался в этой ситуации, когда попытался перебазировать ветку, которая отслеживала удаленную ветку, и пытался перебазировать ее на master. В этом сценарии, если вы попытаетесь перебазировать, вы, скорее всего, обнаружите, что ваша ветвь разошлась, и это может создать беспорядок, который не для git nubees!

Допустим, вы находитесь на ветке my_remote_tracking_branch, которая была разветвлена ​​от master

$ git status

# На ветке my_remote_tracking_branch

нечего коммитить (рабочий каталог чистый)

И теперь вы пытаетесь сделать ребаз из мастера как:

мастер git rebase

Остановись сейчас и избавь себя от неприятностей! Вместо этого используйте слияние как:

Git Merge Master

Да, в вашей ветке вы получите дополнительные коммиты. Но если вы не хотите «не расходящихся» веток, это будет гораздо более плавный рабочий процесс, чем перебазирование. Смотрите этот блог для более подробного объяснения.

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

Теперь, если вы читаете это, потому что вы уже находитесь в «дивергированном» сценарии из-за такой перебазировки, вы можете вернуться к последнему коммиту из источника (то есть в нерасходившемся состоянии), используя:

git reset --hard origin / my_remote_tracking_branch

paneer_tikka
источник
5
rebaseПрактическое правило - использовать, если ветка, которую вы перебираете, не была опубликована (и использовалась другими людьми). В противном случае используйте merge. Если вы перебазируете уже опубликованные (и использованные) ветки, вам нужно согласовать заговор, чтобы переписать историю всех разработчиков, которые использовали вашу ветку.
Микко Ранталайнен
1
К сожалению, я не читал это сообщение, прежде чем делать git rebase master...
Виталий Исаев
Если я выполняю git rebase master в ветке 'foobar', то технически foobar отклоняется от origin / foobar, пока я не сделаю git push -f, верно?
Отпуск
1
git reset --hard origin/my_remote_tracking_branchэто то, что действительно работало
roothahn
24

В моем случае вот что я сделал, чтобы вызвать расхождение сообщения: я сделал, git pushно потом сделал, git commit --amendчтобы добавить что-то к сообщению фиксации. Затем я также сделал еще один коммит.

Так что в моем случае это просто означало, что origin / master устарел. Поскольку я знал, что никто не трогал происхождение / мастера, исправление было тривиальным: git push -f (где -fозначает силу)

Даррен Кук
источник
8
+1, git push -fчтобы перезаписать изменения, ранее зафиксированные и отправленные на источник. Я также уверен, что никто больше не трогал хранилище.
Захаридл
4
Очень рискованная команда. Пожалуйста, напишите краткую информацию о факторе риска команды.
J4cK
1
@ Trickster: я уже описал риск: «как я знал, никто больше не касался происхождения / мастера». Я считаю, что в этом случае это не рискованная команда.
Даррен Кук
1
Если кто-то фиксирует мастер, а затем один человек запускает команду git push -f, то это команда высокого риска
J4cK
11

В моем случае я отправил изменения в origin/masterи затем понял, что не должен был делать это :-( Это было осложнено тем, что локальные изменения были в поддереве. Поэтому я вернулся к последнему хорошему коммиту перед «плохим» локальным изменения (используя SourceTree), а затем я получил «сообщение расхождения».

После локального исправления беспорядка (подробности здесь не важны) я захотел «отодвинуть назад» удаленную origin/masterветку, чтобы она снова синхронизировалась с локальной master. Решение в моем случае было:

git push origin master -f

Обратите внимание на -fпереключатель (сила). Это удалило «плохие изменения», которые были выдвинуты origin/masterпо ошибке, и теперь локальные и удаленные ветви синхронизированы.

Помните, что это потенциально разрушительная операция, поэтому выполняйте ее только в том случае, если вы на 100% уверены в том, что «возврат» удаленного мастера во времени в порядке.

Ларикс Децидуа
источник
Всегда полезно, но, конечно, не отвечает на вопрос.
Тибо Д.
1
@ThibaultD. даже если это не так, это именно то, что я искал.
Нил Чоудхури
Я получаю You are not allowed to force push code to a protected branch on this project.. Я пытаюсь подтолкнуть к своей вилке.
BanAnanas
Мне пришлось снять защиту на gitlab repo stackoverflow.com/questions/32246503/…
BanAnanas
5

Я знаю, что здесь есть много ответов, но я думаю, что git reset --soft HEAD~1заслуживает некоторого внимания, потому что это позволяет вам сохранять изменения в последнем локальном (не выдвинутом) коммите при решении расхожденного состояния. Я думаю, что это более универсальное решение, чем использование rebase, так как локальный коммит можно просмотреть и даже перенести в другую ветку.

Ключ используется --softвместо грубого --hard. Если существует более 1 коммита, вариация HEAD~xдолжна работать. Итак, вот все шаги, которые решили мою ситуацию (у меня был 1 локальный коммит и 8 коммитов в удаленном):

1) git reset --soft HEAD~1 отменить локальный коммит. Для следующих шагов я использовал интерфейс в SourceTree, но я думаю, что следующие команды также должны работать:

2) git stash прятать изменения от 1). Теперь все изменения безопасны и расхождений больше нет.

3) git pull получить удаленные изменения.

4) git stash pop или git stash applyприменить последние сохраненные изменения с последующим новым коммитом, если требуется. Этот шаг не является обязательным, наряду с 2) , когда вы хотите отменить изменения в локальной фиксации. Также, когда вы хотите зафиксировать другую ветку, этот шаг следует выполнить после переключения на нужную.

Бьянка Даничук
источник
На самом деле, в наши дни pull --rebaseвсе равно будет автоматически прятаться. stackoverflow.com/a/30209750/6309
VonC
4

Чтобы увидеть различия:

git difftool --dir-diff master origin/master

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

С Джонсон
источник
1
Интересное использование git-dir: +1
VonC
3

В моем случае это было вызвано невыполнением моего разрешения конфликта.

Проблема была вызвана запуском git pullкоманды. Изменения в происхождении привели к конфликтам с моим локальным репо, которые я решил. Однако я их не совершал. Решение на этом этапе - зафиксировать изменения ( git commitразрешенный файл).

Если вы также изменили некоторые файлы после разрешения конфликта, git statusкоманда покажет локальные изменения как локальные изменения без этапов, а разрешение слияния - как локальные этапные изменения. Эту git commitпроблему можно решить, зафиксировав сначала изменения из слияния , затем добавив и зафиксировав необработанные изменения, как обычно (например, с помощью git commit -a).

Мохаммед Реза Эсмаилзаде
источник
0

У меня было то же самое сообщение, когда я пытался редактировать последнее сообщение о коммите, уже переданного коммита, используя: git commit --amend -m "New message" Когда я выдвигал изменения, git push --force-with-lease repo_name branch_name не было никаких проблем.

LoveForDroid
источник
0

Я столкнулся с этой проблемой, когда создал ветку на основе

git checkout -b a

а затем я установил восходящий поток ветви А к исходной ветви В

git branch -u origin/B

Тогда я получил сообщение об ошибке выше.

Один из способов решить эту проблему для меня был,

  • Удалить ветку
  • Создать новую ветку b
git checkout -b b origin/B
Yanqing
источник