Как объединить конкретный коммит в Git

Я разветвил ветку из репозитория в GitHub и совершил что-то конкретное для меня. Теперь я обнаружил, что оригинальный репозиторий имеет хорошую функцию, которая была на HEAD,

Я хочу объединить его только без предыдущих коммитов. Что я должен делать? Я знаю, как объединить все коммиты:

git branch -b a-good-feature
git pull repository master
git checkout master
git merge a-good-feature
git commit -a
git push

11 ответов

Решение

' git cherry-pick 'должен быть ваш ответ здесь.

Примените изменения, внесенные существующим коммитом.

Не забудьте прочитать bdonlan о последствиях вишни в этом посте:
"Вытащить все коммиты из ветви, нажать указанные коммиты в другую", где:

A-----B------C
 \
  \
   D

будет выглядеть так:

A-----B------C
 \
  \
   D-----C'

Проблема с этим коммитом в том, что git считает коммиты включающими всю историю до них

Где C'имеет другое SHA-1 Я БЫ.
Аналогично, выбор вишни из одной ветви в другую в основном включает в себя создание патча, а затем его применение, что также приводит к потере истории.

Это изменение идентификаторов коммитов нарушает функциональность git слияния между прочим (хотя, если использовать его редко, есть эвристики, которые распишутся об этом).
Что еще более важно, он игнорирует функциональные зависимости - если C фактически использовал функцию, определенную в B, вы никогда не узнаете.

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

Пример: git cherry-pick d42c389f

Допустим, вы хотите объединить коммит e27af03 от ветки X к мастеру.

git checkout master
git cherry-pick e27af03
git push

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

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

Вот ссылка на блоги Рэймонда Чена по этой теме: https://devblogs.microsoft.com/oldnewthing/20180312-00/?p=98215

Единственная проблема, с которой я столкнулся с блогом Раймонда, заключалась в том, что он не предоставил полный рабочий пример. Так что я попытаюсь предоставить один здесь.

Вопрос выше спрашивает, как объединить только фиксацию, на которую указывает HEAD в ветке a-good-feature, в master.

Вот как это будет сделано:

  1. Найти общий предок между мастер и а-хорошо-художественные отраслями.
  2. Создайте новую ветку от этого предка, мы назовем эту новую ветку patch.
  3. Cherry выбирает один или несколько коммитов в эту новую ветку патча.
  4. Слияние ветви заплаты в обамастере иа-хорошо-художественных отрасли.
  5. Мастер филиал теперь будет содержать коммиты, и как мастер и а-хорошо-функцию отрасль также будет иметь новый общий предок, который будет решать любые проблемы в будущем, если дальнейшее сращивание выполняется позже.

Вот пример этих команд:

git checkout master...a-good-feature  [checkout the common ancestor]
git checkout -b patch
git cherry-pick a-good-feature  [this is not only the branch name, but also the commit we want]
git checkout master
git merge patch
git checkout a-good-feature
git merge -s ours patch

Возможно, стоит отметить, что последняя строка, которая слилась с веткой a-good-feature, использовала стратегию слияния "-s ours". Причина этого в том, что нам просто нужно создать фиксацию в ветке a-good-feature, которая указывает на нового общего предка, и, поскольку код уже находится в этой ветке, мы хотим убедиться, что нет никаких шансов конфликта слияния. Это становится более важным, если объединяемые фиксации не самые свежие.

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

Давайте попробуем взять пример и понять:

У меня есть ветвь, скажем, master, указывающая на X , и у меня есть новая ветвь, указывающая на Y .

Где Y = ветка коммитов - несколько коммитов

Теперь скажите, что для Y-ветви я должен закрыть фиксацию между главной веткой и новой веткой. Ниже приведена процедура, которой мы можем следовать:

Шаг 1:

git checkout -b local origin/new

где local это название филиала. Любое имя может быть дано.

Шаг 2:

  git merge origin/master --no-ff --stat -v --log=300

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

Для получения дополнительной информации и параметров о слиянии Git, пожалуйста, обратитесь к:

git merge --help

Также, если вам нужно объединить определенный коммит, вы можете использовать:

git cherry-pick <commit-id>

Если вы зафиксировали изменения в главной ветке. Теперь вы хотите переместить тот же коммит в ветку выпуска. Проверьте идентификатор фиксации (например:xyzabc123) для фиксации.

Теперь попробуйте следующие команды

      git checkout release-branch
git cherry-pick xyzabc123
git push origin release-branch

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

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

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

Вместо выбора вишни вы можете сделать настоящий git merge --no-commit, а затем вручную настройте индекс, чтобы удалить ненужные изменения.

Предположим, вы на ветке A и вы хотите объединить фиксацию на кончике ветки B:

git checkout A
git merge --no-commit B

Теперь вы настроены для создания коммита с двумя родителями, текущий совет коммитит A а также B. Однако у вас может быть применено больше изменений, чем вы хотите, включая изменения из более ранних коммитов в ветви B. Вам нужно отменить эти нежелательные изменения, а затем зафиксировать.

(Может быть простой способ вернуть состояние рабочего каталога и индекса к состоянию до слияния, чтобы у вас был чистый лист, на котором можно было бы выбрать коммит, который вы хотели в первую очередь. Но Я не знаю, как добиться этого с чистого листа. git checkout HEAD а также git reset HEAD оба удалят состояние слияния, что противоречит цели этого метода.)

Так что вручную отмените нежелательные изменения. Например, вы могли

git revert --no-commit 012ea56

за каждую нежелательную фиксацию 012ea56.

Когда вы закончите настраивать вещи, создайте коммит:

git commit -m "Merge in commit 823749a from B which tweaked the timeout code"

Теперь у вас есть только то изменение, которое вы хотели, а дерево предков показывает, что вы технически слились с B.

Нам придется использовать git cherry-pick <commit-number>

Сценарий: я нахожусь в ветке под названием release, и я хочу добавить только несколько изменений из основной ветки в ветку release.

Шаг 1: проверьте ветку, в которую вы хотите добавить изменения

git checkout release

Шаг 2: получите номер фиксации изменений, которые вы хотите добавить

например

git cherry-pick 634af7b56ec

Шаг 3: git push

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

В моем случае у нас была аналогичная потребность в компакт-диске CI. Мы использовали git flow с ветками development и master. Разработчики могут объединять эти изменения напрямую для разработки или с помощью запроса на вытягивание из ветки функции. Однако для освоения мы автоматически объединяем только стабильные коммиты из ветки разработки через Jenkins.

В этом случае сбор вишни - не лучший вариант. Однако мы создаем локальную ветвь из идентификатора фиксации, затем объединяем эту локальную ветку с мастером и выполняем mvn clean verify(мы используем maven). В случае успеха выпустите артефакт производственной версии в nexus, используя плагин выпуска maven с опцией localCheckout = true и pushChanges = false. Наконец, когда все будет удачно, нажмите изменения и отметьте источник.

Пример фрагмента кода:

Предполагая, что вы на мастере, если это сделано вручную. Однако на jenkins, когда вы проверяете репо, вы будете в ветке по умолчанию (мастер, если настроен).

git pull  // Just to pull any changes.
git branch local-<commitd-id> <commit-id>  // Create a branch from the given commit-id
git merge local-<commit-id>  // Merge that local branch to master.
mvn clean verify   // Verify if the code is build able
mvn <any args> release:clean release:prepare release:perform // Release artifacts
git push origin/master  // Push the local changes performed above to origin.
git push origin <tag>  // Push the tag to origin

Это даст вам полный контроль с бесстрашным слиянием или адом конфликта.

Не стесняйтесь посоветовать, если есть лучший вариант.

Если вы новичок в Cherrypick, попробуйте настольную версию GitHub, когда вы выбираете вариант Cherry-Pick, GitHub попросит вас выбрать ветку, которую вы хотите отправить в коммит.

1)

2)

Я предложу ответ. То есть вручную изменить и зафиксировать целевую ветку с помощью экспорта, а для использования такого инструмента, как черепаха, экспорт может быть действительно эффективным способом добиться этого. Я знаю, что это довольно хорошо работает в SVN, и до сих пор в некоторых тестах от git был такой же успех, пока они на 100% идентичны, не должно быть никакого разрешения конфликтов от будущего слияния ветвей функций. Может ли простой подход действительно соответствовать всем требованиям? Возможно, стоит попробовать.

Позвольте мне показать пример:
c: / git / MyProject_Master / ModuleA /
c: / git / MyProject_FeatureA / ModuleA /

Предположим, мы хотим, чтобы все файлы из ветки FeatureA ModuleA находились в главной ветке. Предположим на мгновение, что это огромный проект и есть другие модули, и мы знаем по опыту, что ModuleA не имеет зависимостей, которые могли бы вызвать компилятор или функциональную проблему из-за новых изменений ветки функций. Выберите папку ModuleA и выберите Экспорт из черепахи. Затем выберите ModuleA мастера, в который его нужно экспортировать. Наконец, сделайте проверку различий в файлах для каждого файла, чтобы просмотреть изменения, используя инструмент сравнения, убедитесь, что все вызовы по-прежнему совместимы. Убедитесь, что он компилируется, тщательно протестируйте. Зафиксируйте, нажмите. Это решение проверено временем и эффективно для svn, но я еще не тестировал его в git, поэтому, если есть какие-либо недостатки, сообщите мне.

Другие вопросы по тегам