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

1036

Я разветвил ветку из репозитория в 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
netawater
источник
Если вы пытаетесь сделать это в отношении github, эта статья поможет вам в этом. markosullivan.ca/how-to-handle-a-pull-request-from-github
johndpope

Ответы:

1177

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

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

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

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

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

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

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

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

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

VonC
источник
1
@ openid000: «более мелкозернистые ветви»: именно это и предлагал bdonlan в своем ответе.
VonC
8
Примечание: «git rebase» также меняет SHA-1. Смотрите также "git rebase против git merge ( stackoverflow.com/questions/804115/git-rebase-vs-git-merge ) и" git workflow "( stackoverflow.com/questions/457927/… ) для случаев, когда" git rebase "является законным.
VonC
1
Между «мелкозернистыми ветками», «cherry-pick» и «rebase» у вас будут все возможности для управления кодом в ветках с помощью git.
VonC
@VonC "мелкозернистые ветви" да. У меня было две ветви, и я сделал обновления до определенного модуля визуализации в одной ветви. Этот модуль не зависел от всего другого кода, поэтому cherry-pick было чертовски удобно применять эти изменения и к другой ветке
Cheeku
1
Но обратите внимание, что при слиянии оба коммита C и C преобладают в истории коммитов.
Рахул Шах
740

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

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

bdonlan
источник
68
+1 за ваш предыдущий пост по сбору вишни ( stackoverflow.com/questions/880957/… ). Я взял на себя смелость скопировать его выдержку в своем ответе выше.
VonC
4
Возможно git cherry-pick d42cили git cherry-pick d42c3 будет работать. Git умный ;)
guneysus
2
фатальный: плохая ревизия
Евгений Найда
3
Я только что выполнил эту команду, и она, кажется, сработала, но когда я делаю git status, он говорит «ничего не коммитить, рабочее дерево чистое». Когда я обновляю страницу Commits на моей веб-странице bitbucket, она не отображается. Но это появляется, когда я выполняю git log. И я вижу модифицированный код. Может кто-нибудь объяснить, если я должен сделать какие-либо дополнительные шаги?
Рэй
1
К сожалению, это не отвечает на вопрос: на самом деле это не создает слияние. Там нет предков, указывающих на d42c389f. Может быть, OP не заботился о создании слияния как такового, но разница иногда имеет значение.
LarsH
29

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

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

Где Y <commit-id> = <master> ветка коммитов - несколько коммитов

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

Шаг 1:

git checkout -b local origin/new

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

Шаг 2:

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

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

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

git merge --help

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

git cherry-pick <commit-id>
Spyder
источник
Вы изменили определение Yв своем 3-м предложении? «У меня есть новая ветвь, указывающая на Y» против «Теперь скажем для ветки Y», звучит так, как будто Y когда-то был коммитом, а затем стал веткой
Purefan
как я могу сделать это с определенной точки фиксации ветви, которую я хочу слить
Kulbhushan Singh
3

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

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

Пример кода:

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

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

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

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

nrkkalyan
источник
3

Ведущие ответы описывают, как применить изменения от конкретного коммита к текущей ветви. Если это то, что вы подразумеваете под «как объединить», тогда просто используйте cherry-pick, как они предлагают.

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

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

Вместо cherry-pick вы можете сделать фактический 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.

LarsH
источник