TL; DR
Помните, что git revert
действительно означает отменить изменение , не , чтобы вернуться, т.е. восстановить, какую-то конкретную версию.Можно достичь revert to / restore , но команды, которые это делают, git checkout
и git read-tree
(обе они слегка хитры по разным причинам).См. Как вернуть Git-репозиторий к предыдущему коммиту? и Откат к старому Git-коммиту в публичном репо (в частности ответ jthill ).
Единственная действительно сложная часть здесь - «отменить слияние».Это равносильно использованию git revert -m 1
в коммите слияния, который достаточно прост для запуска;но это означает, что впоследствии вы не сможете повторно объединить, так как Git совершенно уверен (и правильно), что вы уже объединили всю эту работу и что правильный результат на месте (что это, вы просто отменилиэто позже).Чтобы вернуть все обратно, вы можете отменить возврат.
Возврат создает новый коммит, который, как и каждый коммит, просто добавляет новый коммит в текущую ветку.Вы делаете это в своем собственном хранилище, как всегда.Остальная часть работы заключается в том, чтобы просто отправить новый коммит в какой-нибудь другой Git-репозиторий, добавив его в коллекцию этого другого Git как новый совет одной из их ветвей.
Длинный (подробно)
Во-первых, давайте вернемся немного назад, потому что фраза remote branch ничего не значит, или, скорее, означает слишком много разных вещей для слишком многихразные люди - в любом случае это приводит к сбою связи.
Git имеет ответвлений , но даже слово "ответвление" неоднозначно (см. Что именно мы делаемимеется ввиду под "веткой"? ).Я считаю, что лучше быть конкретным: у нас есть названия филиалов , такие как master
и staging
и, по вашему предложению, cleaning
.Git также имеет имена для удаленного отслеживания или имена для удаленного отслеживания как origin/staging
, но, как ни странно, Git сохраняет эти имена локально , т. Е. В вашем собственномхранилище (только).Ваш Git использует your origin/staging
, чтобы запомнить, что ваш Git видел на их Git, когда ваш Git последний раз разговаривал со своим Git и спросил их что-то о их staging
.
Следовательно, корень проблемы здесь в том, что ваш репозиторий Git ваш , но есть второй репозиторий Git, который не ваш,и в конечном итоге вы хотели бы сделать что-нибудь в этом другом хранилище Git.Ограничение здесь состоит в том, что ваш Git позволяет вам делать это только в вашем хранилище, после чего вы в конечном итоге запустите git push
.Шаг git push
переведет некоторые коммиты или коммиты из вашего Git-репозитория в их Git-репозиторий.В настоящее время вы можете попросить их Git установить их имя - их staging
- и они либо скажут Да, я установилчто (ваш Git теперь обновит ваш origin/staging
, чтобы запомнить это), или Нет, я отказываюсь устанавливать это по следующей причине: _____ (укажите причину здесь) .
Следовательно, то, что вы собираетесь делать в вашем репозитории, настроено на соответствующие шаги так, чтобы их Git, в их репозитории, принял коммитвы git push
и обновите их staging
.Имейте это в виду, когда мы пройдем эти шаги.Есть несколько способов сделать это, но вот те, которые я бы использовал.
Выполнить git fetch
.Эта команда всегда безопасна для выполнения.Вы можете дать ему имя одного конкретного пульта, если у вас их больше одного, но у большинства людей есть только один с именем origin
, и если у вас есть только один, его не нужно называть.
(Название пульта - origin
- в основном является кратким способом написания URL-адреса, который ваш компьютер должен использовать для доступа к другому хранилищу Git.)
Thisgit fetch
может ничего не делать или может обновлять некоторые ваши имена для удаленного отслеживания (origin/*
).git fetch
вызвал другой Git, получил от него список всех его веток и хеш-коммитов, которые идут вместе с ними, а затем перенес их на любой коммит, который у вас нет.Ваш origin/staging
теперь помнит их staging
.Теперь вы можете добавить новый коммит к этому.
Теперь, когда ваш origin/staging
синхронизирован с другим Git staging
, создайте или обновите имя локальной ветви какнеобходимо.Лучшее имя для использования здесь - staging
, но вы можете использовать cleaning
, если хотите.Поскольку этот шаг создать или обновить , он имеет подэтапы:
Если это «создать», вы можете использовать git checkout -b staging origin/staging
для создания нового staging
у которого вверх по течению origin/staging
.
Вы можете сократить его до git checkout staging
, который - поскольку у вас нет своего staging
- будет искать по всем вашим origin/*
именам (и любымдругие имена удаленного отслеживания, если у вас есть более одного удаленного), чтобы найти, какие из них соответствуют.Затем он действует так, как будто вы использовали более длинную команду.(Имея только один пульт, единственное имя, которое может соответствовать, это origin/staging
. Если у вас есть и origin
, и xyzzy
в качестве пультов, вы можете использовать и origin/staging
, и xyzzy/staging
; тогда вам понадобится большекоманда.)
Или, если это «обновление», у вас уже будет staging
, для которого уже установлено origin/staging
в качестве восходящего потока, потому что вы делали это раньше.В этом случае просто запустите:
git checkout staging
git merge --ff-only origin/staging
, чтобы синхронизировать вашу постановку с origin/staging
.Если слияние ускоренной перемотки завершится неудачно, у вас есть некоторые коммиты, которых нет, и вам понадобится что-то более сложное, но мы будем предполагать, что это успешно.
Вы можете сократить эти командынемного, но я оставлю их изложенными здесь.Обратите внимание, что первая команда - это такая же , как у короткой версии для первого случая выше, и вы можете определить, какая из них произошла по выводу git checkout staging
.(Я оставлю детали для других вопросов или упражнений.)
Мы можем нарисовать картину того, что у вас есть сейчас, в вашем собственном хранилище, иэто выглядит примерно так:
...--o--o--o---M <-- staging (HEAD), origin/staging
\ /
o--o <-- feature/whatever
Каждый раунд o
представляет коммит.M
представляет коммит слияния, результат которого вам не нравится, но который также присутствует в другом Git на origin
в их имени staging
, поэтому ваш собственный Git имеетимя origin/staging
, указывающее на фиксацию M
.
Теперь вы хотите создать фиксацию, которая отменяет неверную фиксацию.Скорее всего, здесь будет использоваться git revert
, но помните, что возврат означает отменить или возврат , а не переключение на старую версию .Вы сообщаете Git, какой коммит отменить, а Git отменяет его, выясняя, что вы сделали, и поступая противоположным образом.
Например, если коммит, который вы говорите для возврата, говорит «удалить файл README», изменениебудет включать «восстановить файл README в том виде, в каком он был при удалении».Если в коммите, который вы говорите для возврата, написано «добавить эту строку в Documentation / doc.txt», изменение будет включать «удалить эту строку из Documentation / doc.txt».Если в коммите, который вы говорите «отменить», написано «изменить привет на прощание» в каком-то третьем файле, изменение, которое произойдет для возврата, - это изменить «до свидания» на «привет» в этом третьем файле, в той же строке (с некоторой магиейстрока, если она была перемещена).
Это означает, что git revert
может отменить любой коммит, даже если это не последний коммит.Однако для этого он должен сравнить этот коммит со своим непосредственным родителем .Если коммит, который вы пытаетесь вернуть, является коммитом merge , он имеет более одного родителя , и вам нужно будет указать, какой родительский Git следует использовать.
правильный родитель для использования не всегда сразу очевиден.Однако для большинства слияний это просто «родительский номер 1».Это потому, что Git делает особый акцент на первом родителе слияния: это коммит, который был HEAD
, когда вы запустили git merge
.Так что это все, что принесло слияние, чего еще не было.
Когда git revert
завершается успешно, он делает новый коммит, который отменяет эффект слияния:
W <-- staging (HEAD)
/
...--o--o--o---M <-- origin/staging
\ /
o--o <-- feature/whatever
Здесь W
представляет этот новый коммит: он M
перевернут вверх дном.Все, что вам нужно сделать сейчас, это запустить git push origin staging
, чтобы отправить свой новый коммит W
другому Git:
git push origin staging
: это вызывает этот другой Git и предлагает егоcommit W
- это каждый наш коммит, которого нет;у них есть M
и все ранее (слева), но не W
.
Пока нет особых ограничений, они примут этот новый коммит и изменят свои staging
для указания нового коммита W
.Ваш Git запомнит это изменение:
W <-- staging (HEAD), origin/staging
/
...--o--o--o---M
\ /
o--o <-- feature/whatever
(Нет необходимости продолжать рисовать W
на отдельной строке, но я использую здесь копирование-вставку, чтобы сохранить форму такой же.)
Как видите, все готово.Вы и они оба согласны, что вы и их staging
должны оба указать на фиксацию W
, которая приводит к отмене фиксации M
.Теперь можно безопасно удалить собственное имя staging
, если вам нравится:
git checkout <something-else>
git branch -d staging
, что приводит к:
...--o--o--o---M--W <-- origin/staging
\ /
o--o <-- feature/whatever