Как выбрать несколько коммитов


876

У меня есть две ветви. Commit aявляется главой одного, а другие имеют b, c, d, eи fна вершине a. Я хочу двигаться c, d, eи fк первой ветви без фиксации b. Использование вишни забрать его легко: фотографии первой ветви вишневого выбрать один за другим cдо fи перебазироваться вторую ветвь на первой. Но есть ли способ черри все c- fв одной команде?

Вот визуальное описание сценария (спасибо JJD ):

введите описание изображения здесь


3
перебаз, который вы упоминаете, на самом деле не имеет отношения к вопросу, не так ли? (Я понимаю, что вы, возможно, захотите bосновываться fпозже, но это не имеет ничего общего с сбором вишни.)
Superole

Ответы:


1283

В Git 1.7.2 появилась возможность вишни выбирать диапазон коммитов. Из примечаний к выпуску :

git cherry-pickнаучился выбирать диапазон коммитов (например, cherry-pick A..Bи cherry-pick --stdin) git revert; однако они не поддерживают более приятный контроль секвенирования rebase [-i].

Чтобы выбрать все коммиты из Aкоммита B(где Aон старше B), выполните:

git cherry-pick A^..B

Если вы хотите игнорировать саму A, запустите:

git cherry-pick A..B

(Благодарность достается Дамиану, Дж. Б. Рейнсберге и Сшауфу в комментариях)


249
В форме "Cherry-pick A..B" A должно быть старше B. Если они неправильного порядка, команда молча провалится.
Дамиан

294
Кроме того, это будет не вишня А, а, скорее, все после А до В. включительно.
Дж. Б. Райнсбергер,

455
Чтобы включить просто введитеgit cherry-pick A^..B
kiritsuku

17
Если у вас git 1.7.1 или более ранняя версия и вы не можете обновить ее, вы можете довольно быстро выбрать их по порядку, запустив git cherry-pick f~3затем git cherry-pick f~2и т. Д. До git cherry-pick f(нажатие стрелки вверх возвращает предыдущую команду, поэтому я могу быстро изменить номер и запустить это должно быть похоже на большинство консолей).
Дэвид Мейсон

19
Может быть полезно знать, что этот синтаксис работает и с именами ветвей. git cherry-pick master..somebranchвыберет все коммиты на некоторой ветви начиная с master (при условии, что он уже перебазирован на master), и примените их к текущей ветке.
Тор Клингберг

104

Самый простой способ сделать это с ontoопцией rebase. Предположим, что ветвь, в которой заканчивается текущий a, называется mybranch, и это ветвь, на которую вы хотите перейти c- f.

# checkout mybranch
git checkout mybranch

# reset it to f (currently includes a)
git reset --hard f

# rebase every commit after b and transplant it onto a
git rebase --onto a b

1
Спасибо! Не могли бы вы добавить git checkout secondbranch && git rebase mybranchдля полного ответа
салить

1
Этот ответ очень помог мне понять, какой коммит есть в этом сценарии. И: вы также можете использовать rebaseинтерактивный режим. Спасибо, @Charles!
Оливер

1
Прелесть этого подхода в том, что вы можете использовать --interactiveдля удаления некоторых коммитов из последовательности или переупорядочить их до «выбора вишни». +1
Майкл Мерикель

Это гениальная команда, немного сложная, чтобы обернуть голову, но она творит чудеса.
Валерио

Дикий, что вы должны дать коммит, который вы не хотите перебазировать, как один из аргументов ( bв этом примере), но да, это сработало для меня.
asontu

85

Или запрашиваемый однострочный

git rebase --onto a b f

5
Если только для краткости, это лучший ответ.
Нейт Чендлер

9
Проголосовано, но оставит вас в отключенном состоянии HEAD, если f - коммит (в отличие от ветки) - вам нужно отредактировать, чтобы добавить ветку, как
показано

68

Вы можете использовать последовательную комбинацию git rebaseи, git branchчтобы применить группу коммитов к другой ветви. Как уже сообщал wolfc, первая команда фактически копирует коммиты. Тем не менее, изменение не будет видно, пока вы не добавите имя ветви в самый верхний коммит группы.

Пожалуйста, откройте картинку в новой вкладке ...

Workflow

Чтобы суммировать команды в текстовом виде:

  1. Открытый gitk как самостоятельный процесс , с помощью команды: gitk --all &.
  2. Беги git rebase --onto a b f.
  3. Нажмите F5в гитке . Ничего не меняется Но не HEADотмечен.
  4. Запустить git branch selection
  5. Нажмите F5в Gitk . Появляется новая ветка с ее коммитами.

Это должно прояснить вещи:

  • Фиксация a- это новое корневое назначение группы.
  • Фиксация b- это фиксация перед первым коммитом группы (эксклюзив).
  • Фиксация fявляется последним коммитом группы (включительно).

После этого вы можете использовать git checkout feature && git reset --hard bдля удаления коммитов cдо fиз featureветви.

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


2
Если mybranch (коммиты a..f) больше не нужны, это можно упростить до: git rebase --onto a b mybranchи, кстати, - какая программа делает эти классные картинки git?
Mr_and_Mrs_D

2
@Mr_and_Mrs_D Спасибо за ваш комментарий. Я думаю, что использовал cacoo.com, чтобы нарисовать картинки.
JJD

48

Чтобы применить комментарии JB Rainsberger и sschaef, чтобы конкретно ответить на вопрос ... Чтобы использовать диапазон выбора вишни в этом примере:

git checkout a
git cherry-pick b..f

или

git checkout a
git cherry-pick c^..f

3
Я использую git 2.7.0.windows.1и заметил, что когда я пытаюсь выбрать диапазон git cherry-pick --continue | --abort | --quitкоммитов cherry, все в порядке, но git нигде не говорит вам, что вы должны сделать, прежде чем попытаться снова сделать коммит / cherry-pick. Так что, если вы выбираете диапазон коммитов, вам нужно будет запускать git cherry-pick --continueкаждый раз, когда вы будете готовы (разрешать конфликты или тому подобное) с коммитом из данного диапазона.
kuskmen

Я сделал то же самое, но со смертельным исходом: не могу найти «a..b»
Амит Карник

Я не знаю, где я ошибаюсь, но когда я делаю 'git cherry-pick c ^ .. f' на моей стороне, это включает в себя коммит f, но не коммит c. Но поскольку я читаю везде, он должен определять c и f как включающие. Или я не прав?
Самуил

@ Самуил, да, это правильно. ^После того , как с на самом деле означает «фиксация , прежде чем с» , который является б в данном случае. Вот почему c^..fэто синоним b..f. Попробуйте выполнить, git log c^..fи вы должны увидеть коммиты от c до f, точно так же, как если бы вы это делалиgit log b..f
Энди

41

Если у вас есть выборочные ревизии для объединения, скажем, A, C, F, J из коммитов A, B, C, D, E, F, G, H, I, J, просто используйте команду ниже:

мерзкая вишня ACFJ


1
красиво и просто
раскрутка

21
git rev-list --reverse b..f | xargs -n 1 git cherry-pick

2
Прекрасно работает, если нет конфликтов, в противном случае «ребаз на» может быть проще, так как вам не нужно будет выяснять, где он остановился, и повторно применить остальные патчи.
Руслан Кабалин

6
Пожалуйста, добавьте комментарии, объясняющие, что это делает
Mr_and_Mrs_D

7
поскольку никто не объяснил ... git rev-list печатает все ревизии из ветви b в f (в ​​обратном порядке), так что, когда каждая строка (хеш коммита) передается по порядку, она выбирает каждую из них в текущем git HEAD. то естьgit cherry-pick {hash of c}; git cherry-pick {hash of d}; ...
coderatchet

8

Чтобы выбрать вишню от идентификатора коммита до кончика ветви, вы можете использовать:

git cherry-pick commit_id^..branch_name


Это уже часть ответа stackoverflow.com/a/31640427/96823
тиг

1
Этот ответ на самом деле другой и был полезен для меня. Он указывает имя ветки, а не окончательный коммит SHA.
Subtletree

7

Другой вариант, о котором стоит упомянуть, это то, что если вам нужны последние nкоммиты из ветви, ~может быть полезен синтаксис:

git cherry-pick some-branch~4..some-branch

В этом случае приведенная выше команда выберет последние 4 коммита из вызываемой ветви some-branch(хотя вы также можете использовать хеш коммита вместо имени ветви)


1
ОЧЕНЬ полезный ответ, спасибо! :)
Яцек Дзюрдзиковски

3

На самом деле, самый простой способ сделать это может быть:

  1. запишите базу слияния между двумя ветвями: MERGE_BASE=$(git merge-base branch-a branch-b)
  2. перемотка вперед или перебазирование старой ветви на новую.
  3. перебазировать полученную ветвь на себя, начиная с базы слияния с шага 1, и вручную удалить коммиты, которые не нужны:

    git rebase ${SAVED_MERGE_BASE} -i
    

    В качестве альтернативы, если есть только несколько новых коммитов, пропустите шаг 1 и просто используйте

    git rebase HEAD^^^^^^^ -i
    

    на первом этапе, используя достаточно, ^чтобы пройти мимо базы слияния.

Вы увидите нечто подобное в интерактивной перебазе:

pick 3139276 commit a
pick c1b421d commit b
pick 7204ee5 commit c
pick 6ae9419 commit d
pick 0152077 commit e
pick 2656623 commit f

Затем удалите строки b (и любые другие, которые вы хотите)



0

Вот скрипт, который позволит вам выбрать несколько коммитов подряд, просто сообщив скрипту, какие исходные и целевые ветви для выбора вишни и количество коммитов:

https://gist.github.com/nickboldt/99ac1dc4eb4c9ff003a1effef2eb2d81

Чтобы выбрать вишню из вашей ветки к мастеру (в качестве источника используется текущая ветка):

./gcpl.sh -m

Чтобы выбрать последние 5 коммитов из вашей ветки 6.19.x, выполните следующие действия:

./gcpl.sh -c 5 -s 6.19.x -t master

Зачем нужен этот скрипт, если вы можете сделать это непосредственно с помощью Git? ...
code_dredd

Меньше печатания, и мой скрипт автоматически выбирает коммиты для вас, поэтому вам не нужно выбирать каждый из них по его SHA. Во всяком случае, YMMV.
Никболдт
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.