Перейти к содержимому

Как перенести коммит в другую ветку git

  • автор:

Как перенести коммиты в другую ветку?

Как синхронизировать теперь ветку controller_test, приняв fix-ы из master?

Turbid ★★★★★
04.06.21 14:54:41 MSK

можно сделать rebase controller_test master , можно merge master controller_test . оба варианта имеют свои преимущества и недостатки. конкретно в случае rebase тебе нужно быть внимательным, если ты не один работаешь этой веткой. тебе придётся сделать push —force , что может быть недоступно иногда. а другим людям придётся сделать pull —force и возможно потерять свои локальные коммиты

eternal_sorrow ★★★★★
( 04.06.21 15:14:38 MSK )
Ответ на: комментарий от eternal_sorrow 04.06.21 15:14:38 MSK

Есть очень простое правило — работать с веткой только одному. Нет никаких препятствий сделать ветку только для себя. И в ней делай что хочешь. Не забывай только фетчить изменения из основной ветки и переносить свои правки на них (я имею в виду rebase). При переносе своих правок поверх правок в основной ветке да, конфликты возможны, но эти либо следствие кривой архитектуры когда два разных разработчика, решая разные проблемы, изменяют один и тот же код, либо кривая организация труда, когда два разных разработчика решают очень похожие задачи и поэтому работают над одним и тем же кодом.

anonymous
( 04.06.21 15:24:34 MSK )
Ответ на: комментарий от anonymous 04.06.21 15:24:34 MSK

Есть очень простое правило — работать с веткой только одному.

я согласен. но не все этому правилу следуют.

eternal_sorrow ★★★★★
( 04.06.21 15:26:06 MSK )
Ответ на: комментарий от eternal_sorrow 04.06.21 15:26:06 MSK

Да я просто пытался уточнить какой вариант лучше. Так то ответ у тебя исчерпывающий. Просто хотел подтолкнуть ТСа в сторону rebase, т.к. слияние (merge) может превратить лог в кашу, хоть и избавит от конфликтов, которые могут возникнуть при переносе.

Я вообще из секты линейной истории и считаю что разрешение конфликтов небольшая плата за чистый лог.

anonymous
( 04.06.21 15:31:19 MSK )
Ответ на: комментарий от eternal_sorrow 04.06.21 15:14:38 MSK

rebase controller_test master, можно merge master controller_test

а мне в какой ветке нужно быть в данный момент когда я делаю это?

Turbid ★★★★★
( 04.06.21 15:48:49 MSK ) автор топика
Ответ на: комментарий от Turbid 04.06.21 15:48:49 MSK

если явно указывать что и куда мержить/ребейзить, то неважно

eternal_sorrow ★★★★★
( 04.06.21 15:51:44 MSK )
Ответ на: комментарий от Turbid 04.06.21 15:48:49 MSK

~/dev/vc (controller_test) $ git rebase controller_test master Successfully rebased and updated refs/heads/master. ~/dev/vc (master) $ git log --graph --full-history --all --oneline * 54b9ed0 (HEAD -> master) fixed microphone position again * fa0bbea the 7th summator has been deleted * 379576b fixed microphone position * 7844ca2 (origin/controller_test, controller_test) added cyclic polling * 76dec1b added summator counting * 1e64d9e mic state refactoring * 429bb32 mic state logging refactoring * 2aeaed7 some logging refactoring * a3dbe93 added ayncio.open_connection example * 517c45e added .vscode folder to .gitingnore * 6f9bd59 some ipc and d12 tests | * c7a9e55 (tag: 0.0.2, origin/master, origin/HEAD) fixed microphone position again | * c002049 the 7th summator has been deleted | * 18b4041 fixed microphone position |/ * 7ca9b68 (tag: 0.0.1) init project in current state 

Turbid ★★★★★
( 04.06.21 15:52:18 MSK ) автор топика
Ответ на: комментарий от Turbid 04.06.21 15:52:18 MSK

eternal_sorrow ★★★★★
( 04.06.21 15:56:13 MSK )
Ответ на: комментарий от Turbid 04.06.21 15:52:18 MSK

сделай git reset c7a9e55 а потом git rebase master controller_test

eternal_sorrow ★★★★★
( 04.06.21 15:59:15 MSK )
Ответ на: комментарий от eternal_sorrow 04.06.21 15:59:15 MSK

~/dev/vc (master) $ git reset c7a9e55 Unstaged changes after reset: M .gitignore M d12/__init__.py M example.py M example2.py M ipcontroller/__init__.py ~/dev/vc (master) $ git log --graph --full-history --all --oneline * c7a9e55 (HEAD -> master, tag: 0.0.2, origin/master, origin/HEAD) fixed microphone position again * c002049 the 7th summator has been deleted * 18b4041 fixed microphone position | * 7844ca2 (origin/controller_test, controller_test) added cyclic polling | * 76dec1b added summator counting | * 1e64d9e mic state refactoring | * 429bb32 mic state logging refactoring | * 2aeaed7 some logging refactoring | * a3dbe93 added ayncio.open_connection example | * 517c45e added .vscode folder to .gitingnore | * 6f9bd59 some ipc and d12 tests |/ * 7ca9b68 (tag: 0.0.1) init project in current state ~/dev/vc (master) $ git rebase master controller_test error: cannot rebase: You have unstaged changes. error: Please commit or stash them. 

Таки пойду я читать букварь…

Turbid ★★★★★
( 04.06.21 16:03:35 MSK ) автор топика
Ответ на: комментарий от Turbid 04.06.21 16:03:35 MSK

пойди, ага. я не сказал тебе сделать reset —hard потому что возможно у тебя были незакоммиченные изменения (которые бы удалились), но очевидно, тогда мы тебе не удалось сделать rebase. так что сделай reset —hard c7a9e55 , а потом rebase, как я сказал тебе ранее.

eternal_sorrow ★★★★★
( 04.06.21 16:11:07 MSK )
Ответ на: комментарий от Turbid 04.06.21 16:03:35 MSK

Ты просто перепутал местами аргументы и из-за этого получился другой результат. Ничего страшного, бывает. Тебе нужно просто откатиться назад, на тот коммит, где все еще хорошо (c7a9e55). Откатиться можно через reset, но он изменения оставляет в виде unstaged changes, чтобы они не пропали (что вполне разумно и делает работу с git безопасной). Но эти же изменения тебе мешают сделать rebase (что тоже разумно и тоже сделано из-за соображения безопасности). Выходов тут два — либо спрятать эти изменения, чтобы не мешали через stash ( git stash ), либо вообще их удалить, если ты знаешь что они не нужны/либо присутствуют в другой ветке. Второй вариант это сделать сразу hard reset git reset —hard c7a9e55 , тогда эти изменения буду удалены сразу.

Сделай git stash и затем git rebase master controller_test

git как перенести коммит из одной ветки в другую?

Как в git перенести коммит из одной ветки в другую?

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

Такая необходимость возникла, когда я отпочковался не от ветки master, а от другой ветки (по ошибке). Затем сделал пару коммитов. Как можно перенести эти пару коммитов в новую ветку, чтобы создать новый ПР относительно мастер? Чтобы 2 независимых ПР получилось?

короткая ссылка на этот вопрос: close
спросил 8 лет назад

1 ответ

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

git cherry-pick — найдёт коммит по его хэшу и вольёт его в текущую ветку. Применятся изменения только конкретного коммита, причём изменения сразу же будут проиндексированы и будет создан новый коммит в активной ветке с таким же именем, как коммит и назывался. При наличии конфиликтных изменений, нужно будет решить конфликты, как и при «рядном» merge.

Если вам нужно перенести изменения в ветку из определённого коммита другой ветки так, чтобы коммит автоматически не создавался, то нужно использовать параметр -n (—no-commit). Пример:

git cherry-pick -n d3edb354cd3c080b28d13dcb92448de81e543297

Все поддериживаемые параметры cherry-pick:

3.6 Ветвление в Git — Перебазирование

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

Простейшее перебазирование

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

История коммитов простого разделения

Рисунок 35. История коммитов простого разделения

Как мы выяснили ранее, простейший способ выполнить слияние двух веток — это команда merge . Она осуществляет трёхстороннее слияние между двумя последними снимками сливаемых веток ( C3 и C4 ) и самого недавнего общего для этих веток родительского снимка ( C2 ), создавая новый снимок (и коммит).

Слияние разделённой истории коммитов

Рисунок 36. Слияние разделённой истории коммитов

Тем не менее есть и другой способ: вы можете взять те изменения, что были представлены в C4 , и применить их поверх C3 . В Git это называется перебазированием. С помощью команды rebase вы можете взять все коммиты из одной ветки и в том же порядке применить их к другой ветке.

В данном примере переключимся на ветку experiment и перебазируем её относительно ветки master следующим образом:

$ git checkout experiment $ git rebase master First, rewinding head to replay your work on top of it. Applying: added staged command

Это работает следующим образом: берётся общий родительский снимок двух веток (текущей, и той, поверх которой вы выполняете перебазирование), определяется дельта каждого коммита текущей ветки и сохраняется во временный файл, текущая ветка устанавливается на последний коммит ветки, поверх которой вы выполняете перебазирование, а затем по очереди применяются дельты из временных файлов.

Перебазирование изменений из `C4` поверх `C3`

Рисунок 37. Перебазирование изменений из C4 поверх C3

После этого вы можете переключиться обратно на ветку master и выполнить слияние перемоткой.

$ git checkout master $ git merge experiment

Перемотка ветки `master`

Рисунок 38. Перемотка ветки master

Теперь снимок, на который указывает C4′ абсолютно такой же, как тот, на который указывал C5 в примере с трёхсторонним слиянием. Нет абсолютно никакой разницы в конечном результате между двумя показанными примерами, но перебазирование делает историю коммитов чище. Если вы взглянете на историю перебазированной ветки, то увидите, что она выглядит абсолютно линейной: будто все операции были выполнены последовательно, даже если изначально они совершались параллельно.

Часто вы будете делать так для уверенности, что ваши коммиты могут быть бесконфликтно слиты в удалённую ветку — возможно, в проекте, куда вы пытаетесь внести вклад, но владельцем которого вы не являетесь. В этом случае вам следует работать в своей ветке и затем перебазировать вашу работу поверх origin/master , когда вы будете готовы отправить свои изменения в основной проект. Тогда владельцу проекта не придётся делать никакой лишней работы — всё решится простой перемоткой или бесконфликтным слиянием.

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

Более интересные перемещения

Также возможно сделать так, чтобы при перебазировании воспроизведение коммитов применялось к совершенно другой ветке. Для примера возьмём История разработки с тематической веткой, ответвлённой от другой тематической ветки. Вы создаёте тематическую ветку server , чтобы добавить в проект некоторую функциональность для серверной части, и делаете коммит. Затем вы выполнили ответвление, чтобы сделать изменения для клиентской части, и создали несколько коммитов. Наконец, вы вернулись на ветку server и сделали ещё несколько коммитов.

История разработки с тематической веткой, ответвлённой от другой тематической ветки

Рисунок 39. История разработки с тематической веткой, ответвлённой от другой тематической ветки

Предположим, вы решили, что хотите внести изменения клиентской части в основную линию разработки для релиза, но при этом не хотите добавлять изменения серверной части до полного тестирования. Вы можете взять изменения из ветки client , которых нет в server ( C8 и C9 ), и применить их на ветке master при помощи опции —onto команды git rebase :

$ git rebase --onto master server client

В этой команде говорится: «Переключись на ветку client , найди изменения относительно ветки server и примени их для ветки master ». Несмотря на некоторую сложность этого способа, результат впечатляет.

Перемещение тематической ветки, ответвлённой от другой тематической ветки

Рисунок 40. Перемещение тематической ветки, ответвлённой от другой тематической ветки

Теперь вы можете выполнить перемотку (fast-forward) для ветки master (см Перемотка ветки master для добавления изменений из ветки client ):

$ git checkout master $ git merge client

Перемотка ветки `master` для добавления изменений из ветки `client`

Рисунок 41. Перемотка ветки master для добавления изменений из ветки client

Представим, что вы решили добавить наработки и из ветки server . Вы можете выполнить перебазирование ветки server относительно ветки master без предварительного переключения на неё при помощи команды git rebase , которая извлечёт тематическую ветку (в данном случае server ) и применит изменения в ней к базовой ветке ( master ):

$ git rebase master server

Это повторит работу, сделанную в ветке server поверх ветки master , как показано на рисунке:

Перебазирование ветки `server` на вершину ветки `master`

Рисунок 42. Перебазирование ветки server на вершину ветки master

После чего вы сможете выполнить перемотку основной ветки ( master ):

$ git checkout master $ git merge server

Теперь вы можете удалить ветки client и server , поскольку весь ваш прогресс уже интегрирован и тематические ветки больше не нужны, а полную историю вашего рабочего процесса отражает рисунок Окончательная история коммитов:

$ git branch -d client $ git branch -d server

Окончательная история коммитов

Рисунок 43. Окончательная история коммитов

Опасности перемещения

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

Не перемещайте коммиты, уже отправленные в публичный репозиторий

Если вы будете придерживаться этого правила, всё будет хорошо. Если не будете, люди возненавидят вас, а ваши друзья и семья будут вас презирать.

Когда вы что-то перемещаете, вы отменяете существующие коммиты и создаёте новые, похожие на старые, но являющиеся другими. Если вы куда-нибудь отправляете свои коммиты и другие люди забирают их себе и в дальнейшем основывают на них свою работу, а затем вы переделываете эти коммиты командой git rebase и выкладываете их снова, то ваши коллеги будут вынуждены заново выполнять слияние для своих наработок. В итоге, когда вы в очередной раз попытаетесь включить их работу в свою, вы получите путаницу.

Давайте рассмотрим пример того, как перемещение публично доступных наработок может вызвать проблемы. Предположим, вы клонировали репозиторий с сервера и сделали какую-то работу. И ваша история коммитов выглядит так:

Клонирование репозитория и выполнение в нём какой-то работы

Рисунок 44. Клонирование репозитория и выполнение в нём какой-то работы

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

Извлекаем ещё коммиты и сливаем их со своей работой

Рисунок 45. Извлекаем ещё коммиты и сливаем их со своей работой

Затем автор коммита слияния решает вернуться назад и перебазировать свою ветку; выполнив git push —force , он перезаписывает историю на сервере. При получении изменений с сервера вы получите и новые коммиты.

Кто-то выложил перебазированные коммиты, отменяя коммиты, на которых основывалась ваша работа

Рисунок 46. Кто-то выложил перебазированные коммиты, отменяя коммиты, на которых основывалась ваша работа

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

Вы снова выполняете слияние для той же самой работы в новый коммит слияния

Рисунок 47. Вы снова выполняете слияние для той же самой работы в новый коммит слияния

Если вы посмотрите git log в этот момент, вы увидите два коммита с одинаковыми авторами, датой и сообщением, что может сбить с толку. Помимо этого, если вы отправите свою историю на удалённый сервер в таком состоянии, вы вернёте все эти перебазированные коммиты на сервер, что ещё больше всех запутает. Логично предположить, что разработчик не хочет, чтобы C4 и C6 были в истории, и именно поэтому она перебазируется в первую очередь.

Меняя базу, меняй основание

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

Оказывается, что помимо контрольной суммы коммита SHA-1, Git также вычисляет контрольную сумму отдельно для патча, входящего в этот коммит. Это контрольная сумма называется «patch-id».

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

К примеру, если в предыдущем сценарии вместо слияния в Кто-то выложил перебазированные коммиты, отменяя коммиты, на которых основывалась ваша работа мы выполним git rebase teamone/master , Git будет:

  • Определять, какая работа уникальна для вашей ветки (C2, C3, C4, C6, C7)
  • Определять, какие коммиты не были коммитами слияния (C2, C3, C4)
  • Определять, что не было перезаписано в основной ветке (только C2 и C3, поскольку C4 — это тот же патч, что и C4′)
  • Применять эти коммиты к ветке teamone/master

Перемещение в начало force-pushed перемещённой работы

Рисунок 48. Перемещение в начало force-pushed перемещённой работы

Это возможно, если C4 и C4′ фактически являются одним и тем же патчем, который был сделан вашим коллегой. В противном случае rebase не сможет определить дубликат и создаст ещё один патч, подобный C4 (который с большой вероятностью не удастся применить чисто, поскольку в нём уже присутствуют некоторые изменения).

Вы можете это упростить, применив git pull —rebase вместо обычного git pull . Или сделать это вручную с помощью git fetch , а затем git rebase teamone/master .

Если вы используете git pull и хотите использовать —rebase по умолчанию, вы можете установить соответствующее значение конфигурации pull.rebase с помощью команды git config —global pull.rebase true .

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

Если в какой-то момент вы или ваш коллега находите необходимость в этом, убедитесь, что все знают, как применять команду git pull —rebase для минимизации последствий от подобных действий.

Перемещение vs. Слияние

Теперь, когда вы увидели перемещение и слияние в действии, вы можете задаться вопросом, что из них лучше. Прежде чем ответить на этот вопрос, давайте вернёмся немного назад и поговорим о том, что означает история.

Одна из точек зрения заключается в том, что история коммитов в вашем репозитории — это запись того, что на самом деле произошло. Это исторический документ, ценный сам по себе, и его нельзя подделывать. С этой точки зрения изменение истории коммитов практически кощунственно; вы лжёте о том, что на самом деле произошло. Но что, если произошла путаница в коммитах слияния? Если это случается, репозиторий должен сохранить это для потомков.

Противоположная точка зрения заключается в том, что история коммитов — это история того, как был сделан ваш проект. Вы не публикуете первый черновик книги или инструкции по поддержке вашего программного обеспечения, так как это нуждается в тщательном редактировании. Сторонники этого лагеря считают использование инструментов rebase и filter-branch способом рассказать историю проекта наилучшим образом для будущих читателей.

Теперь к вопросу о том, что лучше — слияние или перебазирование: надеюсь, вы видите, что это не так просто. Git — мощный инструмент, позволяющий вам делать многое с вашей историей, однако каждая команда и каждый проект индивидуален. Теперь, когда вы знаете, как работают оба эти приёма, выбор — какой из них будет лучше в вашей ситуации — зависит от вас.

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

Как в Git перенести commit из одной ветки в другую?

Нередко возникает ситуация, когда срочно требуется выполнить небольшую задачу. Разработчик быстро пробегается глазами по ТЗ и старается максимально оперативно вникнуть в суть задачи. При этом можно попросту забыть создать новую ветку, и понять об ошибке уже после финального каммита.

В такой ситуации, когда возникает необходимость перенести commit из одной ветки в другую на помощь приходит команда git cherry-pick , применяющая к дереву проекта изменения, которые были внесены указанным каммитом.

Синтаксис команды git cherry-pick

Рассмотрим ситуацию на примере. Мы находились в master и сделали в него случайно коммит. Тогда чтобы вынести его в отдельную ветку и отправить на merge request нам потребуется выполнить следующие команды:

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

git log

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

3. Перейдём на найденный коммит . Другими словами переходим на то состояние ветки master , от которого нам нужно создать новую ветку.

git checkout

4. Создадим новую ветку, в которую планируется вынести неверно расположенный коммит, и перейдем на неё.

git checkout -b

5. Переносим коммит в новую ветку.

git cherry-pick

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

git cherry-pick master

Если же нужно перенести не один, а несколько подряд идущих коммитов от до , то это делается похожим образом.

git cherry-pick ..

Если же вы хотите, чтобы при переносе изменений коммит не создавался, то используйте параметр -n (—no-commit):

git cherry-pick -n d112ecf96

Как и при операции git rebase в процессе переноса коммита могут возникнуть конфликты. Как и при обычном git merge их следует разрешить, добавить изменения в индекс с помощью git add , а затем продолжить запустив git cherry-pick —continue .

6. Теперь когда коммиты перенесены в ветку , следует удалить их из ветки master . Поэтому переключаемся на ветку master

git checkout master

Смещаем указать master на коммит

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *