Шаг самый последний коммит(ы) на новую ветку с Git
Я хотел бы переместить последние несколько коммитов, которые я совершил для master, в новую ветвь и вернуть master до того, как эти коммиты были сделаны. К сожалению, мой Git-fu еще недостаточно силен, любая помощь?
т. е. как я могу уйти от этого
master A - B - C - D - E
для этого?
newbranch C - D - E
/
master A - B
11 ответов:
переход на новую ветку
предупреждение: этот метод работает, потому что вы создаете новую ветку с первой команды:
git branch newbranch
. Если вы хотите переместить коммиты в существующий филиал вам необходимо объединить изменения в существующую ветку перед выполнениемgit reset --hard HEAD~3
(см. переход к существующей ветке ниже). если вы не объедините свои изменения в первую очередь, они будут потерянный.если нет других обстоятельств, это можно легко сделать путем ветвления и отката назад.
# Note: Any changes not committed will be lost. git branch newbranch # Create a new branch, saving the desired commits git reset --hard HEAD~3 # Move master back by 3 commits (GONE from master) git checkout newbranch # Go to the new branch that still has the desired commits
но убедитесь, сколько коммитов вернуться. Кроме того, вы можете вместо
HEAD~3
, просто укажите хэш фиксации (или ссылку типа origin / master) вы хотите "вернуться к" о мастер (/current) ветвь, например:git reset --hard a1b2c3d4
*1 Вы только" потерять " коммиты из главной ветви, но не волнуйтесь, у вас будут эти коммиты в newbranch!
предупреждение: С git версии 2.0 и выше, если вы позднее
git rebase
новая ветвь на оригинале (master
) ветка, вам может понадобиться явный--no-fork-point
опция во время перебазирования, чтобы избежать потери перенесенных коммитов. Имеяbranch.autosetuprebase always
set делает это более вероятным. Смотрите ответ Джона Меллора для подробности.переход к существующей ветке
если вы хотите переместить свои коммиты в существующий филиал, это будет выглядеть так:
git checkout existingbranch git merge master git checkout master git reset --hard HEAD~3 # Go back 3 commits. You *will* lose uncommitted work. git checkout existingbranch
для тех, кто задается вопросом, почему это работает (как я был сначала):
вы хотите вернуться к C и переместить D и E в новую ветвь. Вот как это выглядит на первый взгляд:
A-B-C-D-E (HEAD) ↑ master
после
git branch newBranch
:newBranch ↓ A-B-C-D-E (HEAD) ↑ master
после
git reset --hard HEAD~2
:newBranch ↓ A-B-C-D-E (HEAD) ↑ master
так как ветвь-это просто указатель,мастер указывает на последний коммит. Когда вы сделали newBranch, вы просто сделали новый указатель на последний коммит. Затем с помощью
git reset
вы передвинул мастер указатель назад два коммита. Но так как вы не двигались newBranch, он по-прежнему указывает на коммит поначалу.
В Целом...
метод, выставленный Сикорой, является лучшим вариантом в этом случае. Но иногда это не самый простой и не общий метод. Для общего метода используйте git cherry-pick:
для достижения того, что OP хочет, его 2-шаговый процесс:
Шаг 1-Обратите внимание, какие коммиты от мастера вы хотите на
newbranch
выполнить
git checkout master git log
обратите внимание на хэши (скажем, 3) коммитов, которые вы хотите на
newbranch
. Вот И Я будем использовать:
С фиксацией:9aa1233
Д фиксации:453ac3d
Е совершения:612ecb3
Примечание: вы можете использовать первые семь символов или весь хэш фиксации
Шаг 2 - Положите их на
newbranch
git checkout newbranch git cherry-pick 612ecb3 git cherry-pick 453ac3d git cherry-pick 9aa1233
или (на Git 1.7.2+, используйте диапазоны)
git checkout newbranch git cherry-pick 612ecb3~1..9aa1233
git cherry-pick применяет эти три коммита к newbranch.
еще один способ сделать это, используя только 2 команды. Также сохраняет ваше текущее рабочее дерево нетронутым.
git checkout -b newbranch # switch to a new branch git branch -f master HEAD~3 # make master point to some older commit
старая версия - прежде чем я узнал о
git branch -f
git checkout -b newbranch # switch to a new branch git push . +HEAD~3:master # make master point to some older commit
будучи в состоянии
push
до.
- это хороший трюк, чтобы знать.
большинство предыдущих ответов опасно неправильно!
не делайте этого:
git branch -t newbranch git reset --hard HEAD~3 git checkout newbranch
в следующий раз, когда вы запускаете
git rebase
(илиgit pull --rebase
) эти 3 коммита будут молча отброшены изnewbranch
! (см. пояснение ниже)вместо этого:
git reset --keep HEAD~3 git checkout -t -b newbranch git cherry-pick ..HEAD@{2}
- сначала он отбрасывает 3 последних коммита (
--keep
как--hard
, но безопаснее, так как терпит неудачу, а не выбрасывает незафиксированные изменения.)- затем он разветвляется
newbranch. Поскольку они больше не ссылаются на ветке, он делает это с помощью Git это reflog:newbranch
.HEAD@{2}
это фиксация, чтоHEAD
используется для обозначения 2 операций назад, т. е. до нас 1. провереноnewbranch
и 2. используетсяgit reset
чтобы отменить 3 коммита.предупреждение: reflog включена по умолчанию, но если вы вручную отключите его (например, используя" голый " репозиторий git), вы не сможете вернуть 3 коммита после запуска
git reset --keep HEAD~3
.альтернатива, которая не полагается на reflog-это:
# newbranch will omit the 3 most recent commits. git checkout -b newbranch HEAD~3 git branch --set-upstream-to=oldbranch # Cherry-picks the extra commits from oldbranch. git cherry-pick ..oldbranch # Discards the 3 most recent commits from oldbranch. git branch --force oldbranch oldbranch~3
(если вы предпочитаете, вы можете написать
@{-1}
- ранее проверенная ветка-вместоoldbranch
).
техническое описание
С чего бы
git rebase
сбросить 3 совершает после того, как в первом примере? Это потому чтоgit rebase
без аргументы включают по умолчанию, который использует местные reflog, чтобы попытаться быть устойчива к вышестоящим филиал насильно подтолкнули.предположим, что вы разветвили origin / master, когда он содержал коммиты M1, M2, M3, а затем сделали три коммита:
M1--M2--M3 <-- origin/master \ T1--T2--T3 <-- topic
но потом кто-то переписывает историю силой-толкая origin/master, чтобы удалить M2:
M1--M3' <-- origin/master \ M2--M3--T1--T2--T3 <-- topic
через локальную reflog,
git rebase
можно увидеть, что вы раздвоились от более раннего воплощение ветви origin / master, и, следовательно, коммиты M2 и M3 на самом деле не являются частью вашей ветви темы. Поэтому разумно предположить, что поскольку M2 был удален из восходящей ветви, вы больше не хотите, чтобы он был в вашей ветви темы, как только ветвь темы будет перебазирована:M1--M3' <-- origin/master \ T1'--T2'--T3' <-- topic (rebased)
такое поведение имеет смысл, и, как правило, правильно делать при перебазировании.
Итак, причина, по которой следующие команды терпят неудачу:
git branch -t newbranch git reset --hard HEAD~3 git checkout newbranch
is потому что они оставляют reflog в неправильном состоянии. ГИТ видит
newbranch
как разветвление восходящей ветви в ревизии, которая включает в себя 3 коммита, тоreset --hard
переписывает историю восходящего потока, чтобы удалить коммиты, и поэтому в следующий раз вы запуститеgit rebase
он отбрасывает их, как и любой другой коммит, который был удален из апстрима.но в данном конкретном случае мы хотим, чтобы эти 3 обязуется быть рассмотрены в рамках темы ветки. Чтобы достичь этого, нам нужно раскошелиться вверх по течению в более ранней версии, которая не включает 3 фиксации. Вот что мне предложил решения, поэтому они оба покидают reflog в правильном состоянии.
для получения более подробной информации см. Определение
--fork-point
на git rebase и git merge-base docs.
Это не" двигает " их в техническом смысле, но имеет тот же эффект:
A--B--C (branch-foo) \ ^-- I wanted them here! \ D--E--F--G (branch-bar) ^--^--^-- Opps wrong branch! While on branch-bar: $ git reset --hard D # remember the SHAs for E, F, G (or E and G for a range) A--B--C (branch-foo) \ \ D-(E--F--G) detached ^-- (branch-bar) Switch to branch-foo $ git cherry-pick E..G A--B--C--E'--F'--G' (branch-foo) \ E--F--G detached (This can be ignored) \ / D--H--I (branch-bar) Now you won't need to worry about the detached branch because it is basically like they are in the trash can waiting for the day it gets garbage collected. Eventually some time in the far future it will look like: A--B--C--E'--F'--G'--L--M--N--... (branch-foo) \ \ D--H--I--J--K--.... (branch-bar)
сделать это без переписывания истории (т. е. если вы уже толкнул совершает):
git checkout master git revert <commitID(s)> git checkout -b new-branch git cherry-pick <commitID(s)>
обе ветви могут быть нажаты без силы!
гораздо более простое решение с помощью git stash
если:
- ваша основная цель-откат
master
и- вы хотите сохранить изменения, но не особенно заботитесь об отдельных коммитах, и
- вы еще не нажали, и
- вы хотите, чтобы это было легко и не сложно с ветвями temp и другими головными болями
тогда следующее намного проще (начиная с ветки
master
что имеет три ошибочных коммита):git reset HEAD~3 git stash git checkout newbranch git stash pop
что это делает, по номеру строки
- отменяет последние три коммита (и их сообщения) к
master
, но оставляет все файлы нетронутыми- прячет все изменения рабочего файла, делая
master
рабочее дерево равно головке~3 состояния- переключается на существующую ветку
newbranch
- применяет сохраненные изменения к рабочему каталогу и очищает тайник
теперь вы можете использовать
git add
иgit commit
как обычно. Все новые коммиты будут добавлены вnewbranch
.чего это не делает
- он не оставляет случайных временных ветвей, загромождающих ваше дерево
- он не сохраняет ошибочные коммиты и сообщения фиксации, поэтому вам нужно будет добавить новое сообщение фиксации к этому новому фиксации
цели
ФП заявленная цель состояла в том, чтобы" вернуть мастера до того, как эти коммиты были сделаны", не теряя изменений, и это решение делает это.
я делаю это по крайней мере один раз в неделю, когда я случайно делаю новые коммиты
master
вместоdevelop
. Обычно у меня есть только один коммит для отката в этом случае с помощьюgit reset HEAD^
on line 1-это более простой способ отката только одного коммита.не делайте этого, если вы нажали изменения мастера вверх по течению
кто-то другие, возможно, потянули эти изменения. Если вы только переписываете свой локальный мастер, нет никакого влияния, когда он продвигается вверх по течению, но нажатие переписанной истории на сотрудников может вызвать головные боли.
была просто такая ситуация:
Branch one: A B C D E F J L M \ (Merge) Branch two: G I K N
Я исполнила:
git branch newbranch git reset --hard HEAD~8 git checkout newbranch
Я ожидал, что commit я буду главой, но commit L-это сейчас...
чтобы быть уверенным, чтобы приземлиться на правильном месте в истории его легче работать с хэшем фиксации
git branch newbranch git reset --hard ######### git checkout newbranch
1) создать новую ветку, которая перемещает все ваши изменения в new_branch.
git checkout -b new_branch
2) затем вернитесь к старой ветке.
git checkout master
3) Сделайте git rebase
git rebase -i <short-hash-of-B-commit>
4) тогда открытый редактор содержит последние 3 фиксации информации.
... pick <C's hash> C pick <D's hash> D pick <E's hash> E ...
5) Изменение
pick
доdrop
во всех этих 3 совершает. Затем сохраните и закройте редактор.... drop <C's hash> C drop <D's hash> D drop <E's hash> E ...
6) Теперь последние 3 коммита удаляются из текущей ветви (
master
). Теперь нажимаем ветка сильно, с+
знак перед названием ветки.git push origin +master
вы можете сделать это всего 3 простых шага, которые я использовал.
1) Создайте новую ветку, в которой вы хотите зафиксировать последнее обновление.
git branch <branch name>
2) Найдите недавний идентификатор фиксации для фиксации в новой ветке.
git log
3) скопируйте этот идентификатор фиксации обратите внимание, что самый последний список фиксации происходит сверху. так что вы можете найти свой совершал. вы также найдете это через сообщение.
git cherry-pick d34bcef232f6c...
вы также можете предоставить некоторые звонил фиксации id.
git cherry-pick d34bcef...86d2aec
теперь ваша работа выполнена. Если вы выбрали правильный идентификатор и правильную ветку, то вы добьетесь успеха. Поэтому, прежде чем сделать это, будьте осторожны. иначе может возникнуть другая проблема.
теперь вы можете нажать ваш код
git push