список - текущая ветка git




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

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

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

master A - B - C - D - E

к этому?

newbranch     C - D - E
             /
master A - B 

Более простое решение с использованием git stash

Если:

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

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

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

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

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

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

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

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

цели

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

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

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

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


В общем...

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

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

Шаг 1 - Заметка, которая фиксируется у мастера, которого вы хотите на newbranch

казнить

git checkout master
git log

Обратите внимание, что хэши (скажем, 3) newbranch вас на 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

OR (на Git 1.7.2+, диапазоны использования)

git checkout newbranch
git cherry-pick 612ecb3~1..9aa1233

git cherry-pick применяет эти три фиксации к newbranch.


Переход к новой ветке

ПРЕДУПРЕЖДЕНИЕ. Этот метод работает, потому что вы создаете новую ветвь с первой командой: 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 во время rebase, чтобы избежать потери транзакций с переносом. Наличие параметра 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

1) Создайте новую ветку, которая перемещает все ваши изменения в new_branch.

git checkout -b new_branch

2) Затем вернитесь к старой ветке.

git checkout master

3) Делать git rebase

git rebase -i <short-hash-of-B-commit>

4) Затем открытый редактор содержит последнюю информацию о фиксации.

...
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

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

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


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

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, но фиксация L это сейчас ...

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

git branch newbranch 
git reset --hard #########
git checkout newbranch

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

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

Затем обе ветви могут быть сдвинуты без силы!


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

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)




branching-and-merging