Git перенос файла из одной ветки в другую

Обновлено: 04.07.2024

Однако после завершения слияния я понимаю, что один файл был испорчен слиянием (кто-то другой сделал автоформат, gah), и было бы проще перейти на новую версию в другой ветке, и затем снова вставьте мое однострочное изменение после переноса в мою ветку.

Так какой же самый простой способ сделать это в git?

Запустите это из ветки, где вы хотите, чтобы файл в конечном итоге:

Некоторые заметки (из комментариев):

  • Используя хэш коммита, вы можете извлекать файлы из любого коммита
  • Это работает для файлов и каталогов
  • перезаписывает файл myfile.txt и mydir
  • Подстановочные знаки не работают, но относительные пути работают
  • Можно указать несколько путей
Вероятно, очевидно, но вам нужно использовать полное имя файла . Подстановочные знаки не работают! хотя .. это также хороший способ: git show commit_id: путь / к / файлу> путь / к / файлу удаленный: git checkout origin / otherbranch myfile.txt Использование подстановочных знаков работает, вам просто нужно обернуть их, '' чтобы они не интерпретировались оболочкой.

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

Цель «git show» - вывести данные на терминал в читаемом формате, который не гарантирует точное совпадение с содержимым файла. То же самое, что лучше скопировать текстовый документ целиком, а не копировать и вставлять его содержимое в другой документ. Я просто хотел посмотреть его, чтобы сравнить содержимое с текущей веткой (проверьте часть кода). Я хотел бы использовать vim с этим, хотя . для подсветки синтаксиса и т. Д. Чтобы сравнить содержимое, прежде чем делать заказ, git diff <other branch> <path to file> работает хорошо. @Gonen: Начиная с git версии 2.21.0, на странице руководства «git show» написано: «Для простых BLOB-объектов он показывает простое содержимое». Я не уверен, означает ли это, что мы всегда хороши. Я с некоторой осторожностью

Как насчет использования команды checkout:

Примечание слияние (2-я команда) не может работать, если файл не существует в обеих ветвях Гектометр У меня нет diffstat. Это конкретная версия инструмента сравнения, потому что я никогда не слышал об этом (и стоит ли переходить на него?): D git diff поддерживает --stat аргумент, который в основном делает то же самое, что и diffstat. Я должен был проверить обе ветви локально, чтобы заставить это работать.

1) Убедитесь, что вы находитесь в филиале, где вам нужна копия файла. Например: я хочу файл подветви в мастер, так что вы должны оформить заказ или должен быть в мастере git checkout master

2) Теперь извлеките конкретный файл, который вы хотите из дочерней ветки в master,

Здесь sub_branch означает, где у вас есть этот файл, а затем имя файла, который нужно скопировать.

Следуя ответу madlep, вы также можете просто скопировать один каталог из другой ветви с помощью каталога blob.

Что касается вопроса ОП, если вы изменили там только один файл, это будет нормально работать ^ _ ^

Обратите внимание, что обе ветви должны быть сначала правильно вытянуты или использованы origin/other-branch для ссылки на ветку репо. Основы, но укусил меня. (ответ отличный - редактирование не требуется)

Я бы использовал git restore (доступно с git 2.23)

git restore --source otherbranch path/to/myfile.txt

Почему это лучше, чем другие варианты?

git checkout otherbranch -- path/to/myfile.txt - Он копирует файл в рабочий каталог, а также в промежуточную область (аналогично тому, как если бы вы копировали этот файл вручную и выполняли git add на нем). git restore не касается области постановки (если не указано по --staged желанию).

git show otherbranch:path/to/myfile.txt > path/to/myfile.txt использует стандартное перенаправление оболочки. Если вы используете Powershell, возможно, проблема с заключением текста или вы можете получить поврежденный файл, если он двоичный . С git restore изменением файлов все делается git исполняемым файлом.

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

git restore --source otherbranch path/to

или с, git restore --overlay --source otherbranch path/to если вы хотите избежать удаления файлов. Например, если файлов меньше, otherbranch чем в текущем рабочем каталоге (и эти файлы отслеживаются ) без --overlay опции, они git restore будут удалены. Но это хорошее поведение по умолчанию, вы, скорее всего, хотите, чтобы состояние каталога было таким же, как в otherbranch , а не "то же самое, но с дополнительными файлами в моей текущей ветке"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Если вы попали в такую ситуацию, у 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, поэтому мне нужно перенести файлы из главной ветви в другую, чтобы иметь резервную копию на случай, если что-то пойдет не так, и затем продолжить работу над основной веткой. Как мне это сделать в git bash?

2 ответа

Git не отправляет файлы; Git нажимает коммиты . Коммиты содержат файлы, поэтому эффект аналогичен, но важно помнить об этом: в репозитории либо есть коммит (в котором затем есть снимок каждого файла), либо нет передайте (и тогда у него нет тех снимков).

Это посылает им - Git over origin - любые ваши коммиты, которых у вас нет (но они нужны). Затем он отправляет им вежливый запрос: Установите ветку с именем some-new-branch-name , чтобы она запоминала коммит, который я вызываю master .

(Ваш Git отправляет им необработанный хеш-идентификатор этого коммита, а не имя master . Вы можете просмотреть этот необработанный хеш-идентификатор, если хотите, с помощью:

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

После того, как вы сделаете выше, если вы запустите:

Вы увидите, что теперь у вас есть новое имя для удаленного отслеживания origin/some-new-branch-name . Это копия вашего Git-а - их источника - имя ветки Git some-new-branch-name . Если вы запускаете:

Вы увидите, что у вас не только есть это новое имя, но оно идентифицирует тот же коммит как ваше собственное master .

Все это немного глупо и / или излишне, если вы не уверены, что то, что вы делаете, действительно разрушит ваш репозиторий здесь. Причина в том, что когда-то сделанные коммиты являются постоянными - ну, в основном постоянными - и только для чтения (полностью только для чтения: ничто в любом коммите может когда-либо быть измененным!). Сами коммиты действительно находятся по хеш-идентификатору. Имя ветки типа master просто служит отправной точкой : ваш Git преобразует ваше имя master в большой уродливый хэш-идентификатор и использует его для поиска последнего коммит. Этот последний коммит имеет большой некрасивый хеш-идентификатор своего предыдущего или родительского коммита, поэтому с момента последнего коммита ваш Git может работать в обратном направлении на один шаг. Родительский коммит хранит другой родительский хеш-идентификатор, так что от родителя ваш Git может перейти на шаг назад. Этот коммит имеет еще один родительский идентификатор:

При наличии любого хеш-идентификатора коммита, например, для коммита H , Git может прочитать коммит и получить его родительский идентификатор. Затем он может прочитать G и получить другой родительский идентификатор, который он может использовать для чтения F , и так далее. Вот что такое ветвь: Это всего лишь серия коммитов, соединенных назад с помощью хеш-идентификаторов, причем ветвь name указывает на последний в серии . Если H является последним коммитом, серия выглядит следующим образом:

name master позволяет вашему Git найти коммит H . Это позволяет вашему Git найти G , затем F и так далее.

Когда вы добавляете новый коммит в master , Git действительно выписывает новый коммит (с новым произвольно выглядящим хеш-идентификатором), у которого parent - коммит H , Затем Git записывает хэш-идентификатор нового коммита - назовем его I - в имя master , и теперь у вас есть:

Когда вы создаете новое имя , например some-new-branch-name , Git просто создает это имя, указывая на какой-то существующий коммит. По умолчанию, фиксацией нового имени является фиксация current :

Это также объясняет, что такое Git HEAD . Если у вас более одного имени ветки, как Git узнает, какое имя обновить , когда вы делаете новый коммит? Ответ таков: Git присоединяет специальное имя HEAD к одному из имен веток. Это имя будет обновляться при создании новых коммитов. Таким образом, мы действительно должны нарисовать это как:

В зависимости от того, какое имя ветви вы выбрали с помощью git checkout . Текущий коммит (реальный идентификатор хэша) I ; текущее имя ветви - это любое имя HEAD , к которому присоединено.

Если вы беспокоитесь о файлах, просто создайте новое имя ветки с помощью git checkout -b и, если необходимо, зафиксируйте (или зафиксируйте сначала, если вы хотите, чтобы изменения были сначала добавлены в другую ветку, если это более уместно). Ваше предыдущее имя ветки теперь запоминает хеш-идентификатор его последнего коммита, а ваше новое имя ветки запоминает хеш-код любого нового коммитов, которые вы сделали:

(если у вас есть два коммита, которые есть только в новой ветке).

(Обратите внимание, что коммиты через I находятся в обеих ветвях.)

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

Создание новой ветки локально

Для создания новой ветки просто используйте:

Или в этом случае мы можем использовать

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

Или в этом случае мы будем использовать

После этого нам нужно совершить изменения

Чтобы добавить все новые файлы (точка заменяет все файлы, которые в данный момент не добавлены для фиксации)

И сделать коммит

Затем мы идем в мастер ветку

А затем просто вставьте эти изменения в Git репо

Загрузка новых файлов из главной ветки в резервную копию

Если вы хотите иметь новые файлы в качестве базы в резервной копии филиала, вы можете использовать


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

Перенос изменений (если вы еще не сделали этого)

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

функция проверки git

git checkout -b tempfeature git checkout feature git merge tempfeature

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

git stash git switch функция git stash применить

Перемещение коммитов (если вы уже совершили)

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

git сбросить HEAD

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

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

Запустите git log, чтобы найти идентификатор фиксации, которую вы хотите отменить:

Затем проверьте ветку функций, предполагая, что ваши изменения были зафиксированы, и запустите cherry-pick:

функция переключателя git git cherry-pick

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

Читайте также: