Ответ 1
Слияния не происходят при нажатии, они происходят на git merge
(ну и pull
, но это действительно просто выборка + слияние, следовательно, слияние происходит при слиянии).
Скорее всего, вы сделали что-то вроде этого:
<get copy from origin/develop>
<wait around for remote origin/develop to acquire new commits>
git checkout develop # get onto (local) develop branch
<edit>
git commit -m message # create some new commit(s)
git push origin develop # attempt to push -- but this fails!
git pull
Это последний шаг, который создает фиксацию слияния (M
выше), потому что pull
означает fetch
(получите все эти новые коммиты, которые теперь находятся в origin/develop
), затем merge
(возьмите свой локальный develop
и объедините свой фиксат с теми, которые только что были извлечены).
Если у вас нет git push
ed этого нового результата, тогда удаленное репо не имеет ни одной из ваших локальных коммитов, те, которые вы отметили C*
и M
. В этом случае вы в хорошей форме! (Вы можете проверить, запустив git fetch
снова, чтобы убедиться, что ваше местное репо origin/develop
соответствует тому, которое находится на пульте дистанционного управления, затем выполните git log origin/develop
, чтобы узнать, что в нем.)
Это может помочь вспомнить, что здесь есть два совершенно разных git repos: yours, с вашими вещами; и тот, который вы вызываете origin
здесь. (Позвольте называть этот компьютер X
.) Если вы должны были войти в X
, у него есть своя отдельная история фиксации и имена ветвей и т.д. Там вы смените каталог на репо, запустите git log -1 develop
и посмотрите, что на кончике этой ветки.
Теперь, если вы выйдете из X
и вернетесь на свой собственный компьютер, вы можете запустить git log -1 origin/develop
. Если это то же, что вы видели на X
, тогда get fetch
не имеет ничего, чтобы обновить, потому что то, что git fetch
делает, фактически (но более эффективно), входит в систему X
и смотрит, что в develop
там. Все, что X
имеет, что у вас нет в origin/develop
, fetch
, приносит и добавляет к origin/develop
. Теперь вы синхронизированы с X
. X
не имеет ваших вещей, но у вас их есть.
Если вы затем сделаете дополнительный шаг для выполнения merge
(включая тот, который подразумевается под pull
), git будет, если это необходимо, выполнить слияние с фиксацией... но все это в вашем репо, на кончике ветки (все еще develop
в этом случае). Если и до тех пор, пока вы не нажмете фиксацию слияния на X
(или кто-то из X
вытащит ваши коммиты от вас, но пусть игнорирует это сейчас:-)), X
не будет иметь его.
В любом случае, пока пульт (X
здесь) не имеет вашего слияния, вы золотые. Поскольку у них этого нет, никто не делает этого. Вы можете сделать rebase
вашего ветки develop
, чтобы поместить фиксацию (C*
) поверх origin/develop
. Это избавит от фиксации слияния (M
), а затем вы можете нажать простую перемотку вперед до origin/develop
.
Если X
выполняет транзакцию слиянием, т.е. если вы нажали после pull
ed и получили это слияние - тогда вы застряли (в некоторой степени), потому что предположительно другие люди имеют доступ к X
и теперь используют вашу транзакцию слиянием. Возможно откат репо на X
, подобно тому, как вы можете сделать это в своем собственном репо с git reset
и git rebase
и т.д., Но, как правило, это плохая идея.
Теперь предположим, что вы на самом деле нажали на другое репо (на машине
X
), но вы абсолютно уверены, что никто еще не видел ваших изменений, и вы определенно перейдете к reset им, а не возвратите их (возврат равен "размахиванию" и оставлению записи отвращения позади, что позволяет всем остальным восстанавливаться после этого легко, но также позволяет им видеть вашу ошибку:-)). 1Вот трюк: вам сначала нужно заставить машинный X-репо сказать, что "кончик ветки devel
- это фиксация C7", где C7 находится в вашей собственной диаграмме ранее, просто перечитана так, что я могу назвать каждый имеет разные значения:
--------------------------- C*--- M
--- C4 --- C5 --- C6 --- C7 ---- /
Итак, как вы можете это сделать? Ну, один из способов - войти в X
, 2cd
в репо (даже если он --bare
) и использовать git update-ref
там. Пусть говорят, что SHA1 для C7 фактически 50db850
(как показано "git log" ). Тогда вы можете сделать это:
localhost$ ssh X
X$ cd /path/to/repo.git
X$ git update-ref refs/heads/develop 50db850
Но если вы не можете войти в X
или даже просто не хотите, вы можете сделать то же самое с git push -f
. 3 (у этого есть другие преимущества: в в частности, ваш ретранслятор git будет знать, что origin/develop
был перемотан, как только push -f
завершается успешно.) Просто создайте локальный ветки, указывающий на правильную фиксацию: 4
localhost$ git branch resetter 50db850
localhost$ git log resetter # make sure it looks right
...
localhost$ git push -f origin resetter:develop
Total 0 (delta 0), reused 0 (delta 0)
To ssh://[redacted]
+ 21011c9...50db850 resetter -> develop (forced update)
localhost$ git branch -d resetter # we don't need it anymore
Как только вы это сделаете, машина X вернется в состояние, в котором вы ее хотели, и вы можете продолжить, как будто вы никогда не толкали слияние, которое вам не понравилось.
Обратите внимание, что когда вы делаете push -f
, если кто-то еще совершил новые коммиты поверх M
, они также станут невидимыми (технически они все еще там, вместе с вашей фиксацией слияния, но они "потеряли" в значении lost+found
git fsck --lost-found
, и через несколько месяцев они действительно исчезнут навсегда). 5
Снова и очень важно: этот "откат общего репо" - большая боль для других пользователей этого общего репо, поэтому будьте уверены, что все в порядке, прежде чем вы это сделаете.
1 Этот вид тривиального слияния даже не нуждается в возврате. Там нет ничего принципиально неправильного в том, чтобы покинуть слияние там. Если вы имеете дело с более серьезным ошибочным слиянием, тем не менее, есть еще один недостаток в возврате слияния, помимо записи вашего "oops": он заставляет "переделывать" изменения позже немного сложнее, тем более что "слияние" по назначению "увидит более раннее слияние и подумает: хорошо, мне не нужно повторно объединять эти изменения. Затем вам нужно" вернуть обратно".
Я думаю, что правильный урок убрания здесь: look (git log
), прежде чем нажимать, чтобы убедиться, что то, что вы собираетесь нажать, - это то, что вы намереваетесь нажать.
2 Или проще и проще: git ls-remote
. Это использует код протокола выборки, чтобы узнать, что такое пульт. Но он скрывает тему, которую я собираюсь здесь, а именно: удаленный репо, как и ваш!
3 В предстоящих выпусках git версии 2 есть новые "функции безопасности", которые будут использоваться с git push -f
. Но они еще не вышли, так что это вам не поможет. Я отредактирую это позже, когда они будут, чтобы отметить их. До тех пор будьте очень осторожны: вы мчатесь против кого-либо еще, пытающегося направить новый материал в общий репозиторий, на данный момент.
4 Вы можете даже сделать это с помощью сырого идентификатора SHA-1. Название ветки легче вводить правильно несколько раз подряд.
5 Сохранение и истечение осуществляется через git "reflogs". Общий сервер может не регистрировать все обновления ref; если нет, только частные репозитории, у которых уже есть новые коммиты, сохранят их. Самое короткое истечение срока действия по умолчанию - 30 дней, т.е. Около одного месяца, поскольку оно больше не может быть достигнуто с кончика ответвления. Но имейте в виду, что это не забавная сумасшедшая схватка, которая заставит всех искать через свои репозитории "потерянные" коммиты после того, как принудительные "теряют работу" у общего репозитория.