git cherry-pick говорит: «… 38c74d - это слияние, но опция -m не была предоставлена»

519

Я сделал некоторые изменения в своей основной ветке и хочу внести их в апстрим. когда я выбираю следующие коммиты, я застреваю на fd9f578, где git говорит:

$ git cherry-pick fd9f578
fatal: Commit fd9f57850f6b94b7906e5bbe51a0d75bf638c74d is a merge but no -m option was given.

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

Это коммиты, которые я хочу представить.

e7d4cff added some comments...
23e6d2a moved static strings...
44cc65a incorporated test ...
40b83d5 whoops delete whitspace...
24f8a50 implemented global.c...
43651c3 cleaned up ...
068b2fe cleaned up version.c ...
fd9f578 Merge branch 'master' of ssh://extgit/git/sessions_common
4172caa cleaned up comments in sessions.c ...
Wufoo
источник

Ответы:

607

Метод вишневого пика работает, используя diff, который представляет набор изменений (различие между рабочим деревом в этой точке и рабочим деревом его родителя), и применяя его к текущей ветке.

Итак, если коммит имеет двух или более родителей, он также представляет два или более различий - какой из них следует применить?

Вы пытаетесь выбрать вишню fd9f578, которая была слиянием с двумя родителями. Таким образом, вы должны указать команде cherry-pick, какую из них нужно рассчитать, используя -mопцию. Например, git cherry-pick -m 1 fd9f578использовать родителя 1 в качестве базы.

Я не могу сказать наверняка для вашей конкретной ситуации, но обычно рекомендуется использовать git mergeвместо git cherry-pick. Когда вы выбираете коммит слияния, он объединяет все изменения, сделанные в родительском элементе, который вы не указали, -mв этот коммит . Вы теряете всю их историю и собираете вместе все их различия. Ваш звонок.

Borealid
источник
3
@wufoo Вы, вероятно, также должны узнать об этом git rebase- это как слияние, но вместо объединения двух ветвей он пересаживает одну, чтобы сидеть над другой.
Бореалид
92
как узнать номер родителя?
Anentropic
66
@Anentropic 1 - это «первый родитель», 2 - «второй родитель» и так далее. Это тот порядок, в котором они перечислены в коммите (как просмотрено git showи тому подобное).
Бореалид
2
@lkraav Вы могли бы просто сделать, git reset --hard HEAD@{1}чтобы вернуть свой недостающий коммит. git resetне ограничивается движением «назад» в истории. git checkout -b mybranch HEAD@{1}также будет работать.
Бореалид
4
ВНИМАНИЕ: git mergeможет иметь непредвиденные последствия. Эта команда добавит все другие (более старые) коммиты, которые существуют в родительской ветви. Обычно люди выбирают вишню, потому что они не хотят других коммитов. Убедитесь, что вы дважды проверили, что вносите только те изменения, которые хотите!
Кей V
52

-m означает родительский номер.

Из Git Doc:

Обычно вы не можете выбрать слияние, потому что вы не знаете, какую сторону слияния следует считать основной линией. Эта опция указывает родительский номер (начиная с 1) основной линии и позволяет cherry-pick воспроизводить изменения относительно указанного родителя.

Например, если ваше дерево коммитов выглядит так:

- A - D - E - F -   master
   \     /
    B - C           branch one

затем git cherry-pick Eсоздаст проблему, с которой вы столкнулись.

git cherry-pick E -m 1означает использование D-E, в то время как git cherry-pick E -m 2означает использование B-C-E.

gavincook
источник
32

Ответ @ Бореалида верный, но предположим, что вы не заботитесь о сохранении точной истории слияния ветки и просто хотите выбрать ее линеаризованную версию. Вот простой и безопасный способ сделать это:

Начальное состояние: вы находитесь на ветке X, и вы хотите выбрать коммиты Y..Z.

  1. git checkout -b tempZ Z
  2. git rebase Y
  3. git checkout -b newX X
  4. git cherry-pick Y..tempZ
  5. (необязательный) git branch -D tempZ

Для этого нужно создать ветку, tempZоснованную на Zистории, но с Yлинеаризованной историей , а затем выбрать ее на копию Xколла newX. (Безопаснее делать это в новой ветви, а не мутировать X.) Конечно, на шаге 4 могут возникнуть конфликты, которые вам придется разрешать обычным способом ( cherry-pickработает очень похоже rebaseна это). Наконец, он удаляет временную tempZветку.

Если на шаге 2 выдается сообщение «Текущая ветвь tempZ актуальна», то Y..Zона уже была линейной, поэтому просто проигнорируйте это сообщение и продолжайте с шага 3 и далее.

Затем просмотрите newXи посмотрите, сделал ли это то, что вы хотели.

(Примечание: это не то же самое, что и в простом git rebase Xветвлении Z, потому что это никак не зависит от отношений между Xи Y; могут быть коммиты между общим предком и тем, Yчто вы не хотели.)

Дайра Хопвуд
источник
1
git rebase YговоритCurrent branch tempZ is up to date
Basilevs
Я думаю, это означает, что это Y..Zбыло уже линейно. Таким образом, вы можете проигнорировать это сообщение и перейти к шагам 3 и 4.
Дайра Хопвуд
1
Интересная идея, мне пришлось нарисовать ее на бумаге, чтобы полностью оценить происходящее = D
Крис
2
Brilliant. git cherry-pick для всего диапазона жаловался либо на отсутствие опции -m, либо на то, что она была предоставлена. Ваше решение было золотым. (Одно предложение: удалить ветку tempZ после)
Otheus
1
это потрясающе! Я боролся с вишневым выбором, и только это имело смысл. У меня были небольшие проблемы с выбранными буквами (некоторые ветви и коммиты являются заглавными буквами, а некоторые - строчными)
pcarvalho
19

Упростить. Вишневый пик коммитов. Не выбирай слияния.

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

Вы пытаетесь выбрать вишню fd9f578, которая была слиянием с двумя родителями.

Вместо выбора вишни слияния, самая простая вещь - вишня выбрать коммит (ы), который вы на самом деле хотите из каждой ветви в слиянии.

Поскольку вы уже слились, вероятно, все ваши желаемые коммиты есть в вашем списке. Выбирайте их напрямую, и вам не нужно связываться с коммитом слияния.

объяснение

Метод вишневого выбора работает, используя diff, который представляет набор изменений (разница между рабочим деревом в этой точке и рабочим деревом его родителя), и применяя набор изменений к вашей текущей ветви.

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

альтернативы

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

  1. (Более сложный и неясный; также отбрасывает историю) вы можете указать, к какому из родителей следует обратиться.

    • Используйте -mопцию, чтобы сделать это. Например, git cherry-pick -m 1 fd9f578будет использоваться первый родитель в списке в качестве слияния.

    • Также учтите, что когда вы выбираете коммит слияния, он объединяет все изменения, сделанные в родительском элементе, для которого вы не указали, -mв этот коммит . Вы теряете всю их историю и собираете вместе все их различия. Ваш звонок.

  2. (Проще и знакомее; сохраняет историю) вы можете использовать git mergeвместо git cherry-pick.

    • Как обычно git merge, он попытается применить все коммиты, существующие в ветке, которую вы объединяете, и перечислить их по отдельности в ваш журнал git.
Кей V
источник
2

Упрощение метода @Daira Hopwood для выбора одного коммита. Не нужно никаких временных веток.

В случае с автором:

  • Z находится в розыске (fd9f578)
  • Y это совершить перед этим
  • X текущая рабочая ветвь

затем сделайте:

git checkout Z   # move HEAD to wanted commit
git reset Y      # have Z as changes in working tree
git stash        # save Z in stash
git checkout X   # return to working branch
git stash pop    # apply Z to current branch
git commit -a    # do commit
ephemerr
источник
2
Это, конечно, теряет метаданные, связанные с исходным коммитом. Я думаю, это вопрос мнения, проще ли это. Я использую его иногда, когда хочу потерять метаданные и сохранить только общие изменения кода. Обратите внимание, что это работает, даже если Y не является непосредственным родителем Z (в этом случае изменения будут отменены).
Дайра Хопвуд