Шаг самый последний коммит(ы) на новую ветку с Git


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

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

master A - B - C - D - E

для этого?

newbranch     C - D - E
             /
master A - B 
11 3860

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.
  • 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 без аргументы включают по умолчанию, который использует местные 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

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

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

теперь вы можете использовать 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