Переместите последние фиксации (ов) в новую ветку с помощью Git

Я хотел бы переместить последние несколько коммитов, которые я совершил, чтобы овладеть новой веткой и вернуть мастера обратно до того, как эти коммиты были сделаны. К сожалению, мой Git -fu еще недостаточно силен, любая помощь?

т.е. Как я могу перейти от этого

master A - B - C - D - E

к этому?

newbranch     C - D - E
             /
master A - B 
4319
27 окт. '09 в 6:07
источник поделиться
13 ответов

Переезд в новую ветку

ВНИМАНИЕ: Этот метод работает, потому что вы создаете новую ветку с помощью первой команды: 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), которую вы хотите "вернуть обратно" в ветке master (/current), например:

git reset --hard a1b2c3d4

* 1 Вы будете только "потерять" совершает от основной ветки, но не волнуйтесь, вы будете иметь те коммиты в newbranch!

ВНИМАНИЕ: В Git версии 2.0 и новее, если вы позже git rebase новую ветку на оригинальную (master) ветку, вам может понадобиться явная опция --no-fork-point во время перебазировки, чтобы избежать потери перенесенных коммитов. Наличие branch.autosetuprebase always установленного значения branch.autosetuprebase always делает это более вероятным. Смотрите Джон Меллор ответ для деталей.

Переезд в существующую ветку

Если вы хотите переместить ваши коммиты в существующую ветку, это будет выглядеть так:

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
5496
27 окт. '09 в 6:15
источник

Для тех, кто задается вопросом, почему он работает (как я был вначале):

Вы хотите вернуться на 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, он все же указывает на фиксацию, которую она изначально сделала.

930
23 июля '11 в 1:37
источник

В общем...

В этом случае наилучшим вариантом является метод, открытый sykora. Но иногда это не самый простой и не общий метод. Для общего метода используйте git cherry-pick:

Чтобы достичь желаемого OP, это двухэтапный процесс:

Шаг 1 - примечание, которое фиксирует требуемый мастер на newbranch

Выполнить

git checkout master
git log

Обратите внимание, что хэши (скажем, 3) обязывают вас на newbranch. Здесь я буду использовать:
C commit: 9aa1233
D commit: 453ac3d
E commit: 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.

392
07 февр. '12 в 19:58
источник

Еще один способ сделать это, используя всего 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 к . хороший трюк, чтобы знать.

288
26 марта '14 в 11:13
источник

Большинство предыдущих ответов опасно ошибочны!

НЕ делайте этого:

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.
  • Затем он вишневый выбирает, что эти 3 берет обратно на newbranch. Поскольку они больше не ссылаются на ветку, это делает это с помощью git reflog: 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 без аргументов по умолчанию использует параметр --fork-point, который использует локальный рефлок, чтобы попытаться быть надежным против того, чтобы восходящая ветка была принудительно нажата.

Предположим, что вы разветвлялись от источника/хозяина, когда он содержал коммиты 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

Используя ваш локальный рефлок, 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

заключается в том, что они оставляют reflog в неправильном состоянии. git видит newbranch как разветвленную ветвь вверх по ревизии, которая включает в себя 3 коммита, затем reset --hard перезаписывает историю восходящего потока, чтобы удалить коммиты, и поэтому в следующий раз при запуске git rebase он отбрасывает их как любой другой фиксатор, который был удален из восходящего потока.

Но в этом конкретном случае мы хотим, чтобы эти 3 фиксации рассматривались как часть ветки темы. Чтобы достичь этого, нам нужно отбросить вверх по течению при более раннем пересмотре, который не включает 3 коммита. Это то, что мои предлагаемые решения делают, поэтому они оба оставляют reflog в правильном состоянии.

Подробнее см. определение --fork-point в git rebase и git merge- базы.

263
07 апр. '16 в 1:38
источник

Гораздо проще решение с использованием git stash

Тогда следующее намного проще (начиная с ветки master которой есть три ошибочных коммита):

git reset HEAD~3
git stash
git checkout newbranch
git stash pop

Когда использовать это?

  • Если вашей основной целью является откат master
  • Вы хотите сохранить изменения файла
  • Вас не волнуют сообщения о ошибочных коммитах
  • Вы еще не нажали
  • Вы хотите, чтобы это было легко запомнить
  • Вам не нужны такие сложности, как временные/новые ветки, поиск и копирование хэшей коммитов и другие головные боли

Что это делает, по номеру строки

  1. Отменяет последние три коммита (и их сообщения) для master, но оставляет все рабочие файлы нетронутыми
  2. Сохраняет все изменения рабочего файла, делая master рабочее дерево точно равным состоянию HEAD ~ 3
  3. Переключение на существующую ветку newbranch
  4. Применяет сохраненные изменения к вашему рабочему каталогу и очищает тайник

Теперь вы можете использовать git add и git commit как обычно. Все новые коммиты будут добавлены в newbranch.

Что это не делает

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

цели

ОП заявил, что цель состояла в том, чтобы "вернуть мастера до того, как эти коммиты были сделаны", не теряя изменений, и это решение делает это.

Я делаю это, по крайней мере, один раз в неделю, когда я делаю новые коммиты для master вместо develop. Обычно у меня есть только один коммит для отката, и в этом случае использование git reset HEAD^ в строке 1 - это более простой способ отката только одного коммита.

Не делайте этого, если вы выдвинули основные изменения вверх по течению

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

64
01 мая '18 в 10:50
источник

Это не "перемещает" их в техническом смысле, но имеет такой же эффект:

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)
29
19 окт. '13 в 17:12
источник

Чтобы сделать это без перезаписи истории (т.е. если вы уже нажали на коммиты):

git checkout master
git revert <commitID(s)>
git checkout -b new-branch
git cherry-pick <commitID(s)>

Обе ветки могут быть нажаты без усилий!

23
21 янв. '16 в 19:10
источник

Если бы такая ситуация:

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

Я ожидал, что фиксация будет HEAD, но commit L это сейчас...

Чтобы приземлиться в нужном месте в истории, его легче работать с хешем фиксации

git branch newbranch 
git reset --hard #########
git checkout newbranch
13
20 сент. '13 в 13:17
источник

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 hash> C
pick <D hash> D
pick <E hash> E
...

5) Измените pick чтобы drop все эти 3 коммита. Затем сохраните и закройте редактор.

...
drop <C hash> C
drop <D hash> D
drop <E hash> E
...

6) Теперь последние 3 коммита удалены из текущей ветки (master). Теперь принудительно нажмите на ветку, со знаком + перед именем ветки.

git push origin +master
2
01 июня '18 в 8:40
источник

Как я могу пойти от этого

A - B - C - D - E 
                |
                master

к этому?

A - B - C - D - E 
    |           |
    master      newbranch

С двумя командами

  • git branch -m master newbranch

дающий

A - B - C - D - E 
                |
                newbranch

а также

  • Git Branch Master B

дающий

A - B - C - D - E
    |           |
    master      newbranch
1
16 мая '19 в 15:00
источник

Если вам просто нужно переместить все ваши невыполненные коммиты в новую ветку, то вам просто нужно,

  1. создать новую ветку из текущей: git branch new-branch-name

  2. нажмите вашу новую ветку: git push origin new-branch-name

  3. вернуть вашу старую (текущую) ветвь в последнее git reset --hard origin/old-branch-name/стабильное состояние: git reset --hard origin/old-branch-name

Некоторые люди также имеют другие upstreams а не origin, они должны использовать соответствующие upstream

0
17 июня '19 в 10:38
источник

Вы можете сделать это всего за 3 простых шага, которые я использовал.

1) Создайте новую ветку, где вы хотите зафиксировать последние обновления.

git branch <branch name>

2) Найти недавний коммит-идентификатор для коммита на новой ветке.

git log

3) Скопируйте это примечание id коммита, что Список последних коммитов находится сверху. так что вы можете найти свой коммит. Вы также найдете это через сообщение.

git cherry-pick d34bcef232f6c...

Вы также можете указать некоторый диапазон идентификатора коммита.

git cherry-pick d34bcef...86d2aec

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

Теперь вы можете нажать свой код

git push

0
25 мая '18 в 17:14
источник

Посмотрите другие вопросы по меткам или Задайте вопрос