Как переместить определенные коммиты на основе другой ветки в git?

381

Ситуация:

  • мастер в X
  • quickfix1 в X + 2 коммитов

Такой что:

o-o-X (master HEAD)
     \
      q1a--q1b (quickfix1 HEAD)

Затем я начал работать с quickfix2, но случайно взял в качестве источника ветку quickfix1, а не мастер. Теперь quickfix2 находится на X + 2 коммитов + 2 соответствующих коммитов.

o-o-X (master HEAD)
     \
      q1a--q1b (quickfix1 HEAD)
              \
               q2a--q2b (quickfix2 HEAD)

Теперь я хочу иметь ветку с quickfix2, но без двух коммитов, которые относятся к quickfix1.

      q2a'--q2b' (quickfix2 HEAD)
     /
o-o-X (master HEAD)
     \ 
      q1a--q1b (quickfix1 HEAD)

Я пытался создать патч из определенной ревизии в quickfix2, но патч не сохраняет историю коммитов. Есть ли способ сохранить мою историю коммитов, но иметь ветку без изменений в quickfix1?

Алекс Ярмула
источник
8
@Kevin Этот вопрос касается только перемещения коммитов из одной ветки в другую, в этом есть дополнительное требование не включать коммиты quickfix1. (Обратите внимание также на разницу в ответах.)
Скотт Уэлдон

Ответы:

372

Это классический случай rebase --onto:

 # let's go to current master (X, where quickfix2 should begin)
 git checkout master

 # replay every commit *after* quickfix1 up to quickfix2 HEAD.
 git rebase --onto master quickfix1 quickfix2 

Так что вы должны идти от

o-o-X (master HEAD)
     \ 
      q1a--q1b (quickfix1 HEAD)
              \
               q2a--q2b (quickfix2 HEAD)

чтобы:

      q2a'--q2b' (new quickfix2 HEAD)
     /
o-o-X (master HEAD)
     \ 
      q1a--q1b (quickfix1 HEAD)

Лучше всего это делать на чистом рабочем дереве.
Смотритеgit config --global rebase.autostash true , особенно после Git 2.10 .

VonC
источник
24
Помните, что эти шаги изменят историю quickfix2, поэтому, если вы уже поделились веткой, вместо этого используйте cherry-picking (см. Следующие ответы).
Макс Черняк
Только для записей: с журналом SmartGit просто перетащите q2aна Xи выберите Rebase 2 commits из вариантов появившегося диалога.
Томас С.
1
@ThomasS. Интересно. Это хорошая реализация GUI git rebase --onto.
VonC
1
Я должен признать, я делаю глупые вещи, такие как фиксация на неправильной ветке чаще, чем я действительно должен, графический интерфейс просмотра журнала SmartGit спасал меня много раз в одной и той же ситуации.
WORMSS
1
@Cosine Согласен. Я отредактировал свой ответ, добавив ссылку на rebase.autostashконфигурацию: это позволит избежать потери выполняемой работы в рабочем дереве при выполнении ребазирования.
VonC
155

Вы можете использовать, git cherry-pickчтобы просто выбрать коммит, который вы хотите скопировать.

Вероятно, лучший способ - создать ветку из master, а затем использовать в этой ветке git cherry-pick2 коммита из quickfix2, которые вы хотите.

DJ.
источник
Это также лучший вариант, если вы хотите переместить только один коммит. Спасибо.
Алекс
142

Самое простое, что вы можете сделать, это выбрать диапазон вишни. Он делает то же самое, что и rebase --ontoдля глаз:

git cherry-pick quickfix1..quickfix2
Christoph
источник
6
кроме того, он не теряет исходные коммиты, IIUC, поэтому кажется предпочтительным для таких игр, как я;) или rebase --ontoтакже сохраняет оригинальные изменения?
akavel
6
как rebaseи cherry-pickдать вам новые ключи SHA. Это потому, что каждый коммит является уникальным снимком репозитория.
Кристоф
6
@Akavel имел в виду, что cherry-pick сохранит первоначальные коммиты в своей ветке, что верно
Mr_and_Mrs_D
4
Для чего бы это ни стоило, я попытался cherry-pickдиапазон, как в этом ответе, и это смутило мой репо. Я должен был сделать отдельные cherry-picks для каждого коммита. (И, может быть, само собой разумеется, но в случае, если кто-то борется, вы должны cherry-pickв хронологическом порядке, что ваши коммиты были применены.)
Карменизм
3
git checkoutимеет решающее значение здесь. какая у тебя ГОЛОВА :)?
Славомир Ленарт
28

Я считаю, что это:

git checkout master
git checkout -b good_quickfix2
git cherry-pick quickfix2^
git cherry-pick quickfix2
Мэтью Флэшен
источник
3
cherry-pickработает с хешами коммитов, поэтому, если вы просто хотите взять коммит из какого-то места и поместить его в другое место, это путь. Просто убедитесь, что вы делаете checkout <branch>правильную ветку в первую очередь.
Джон Лейдгрен
-1
// on your branch that holds the commit you want to pass
$ git log
// copy the commit hash found
$ git checkout [branch that will copy the commit]
$ git reset --hard [hash of the commit you want to copy from the other branch]
// remove the [brackets]

Другие более полезные команды здесь с объяснением: Git Guide

суслик
источник