Запомните эти элементы:
Фиксирует удержание снимков. Это не изменения , это полные копии каждого файла. (Git выполняет автоматическое c дедупликацию, поэтому тот факт, что многие коммиты повторно используют множество файлов, означает, что они действительно повторно используют файл, даже если у каждого из них есть полный снимок. у них есть один снимок этого файла, общий для всех снимков, имеющих эту единственную версию этого файла.)
Имена ветвей просто идентифицируют одну конкретную фиксацию. Каждая фиксация ссылается на предыдущую, или родительский , фиксация. 1 Фиксация, которую выбирает ветвь, по определению является последней фиксацией в этой ветке . (Это, в свою очередь, означает, что некоторые коммиты выполняются одновременно на многих или даже на каждой ветке.)
Следовательно, чтобы получить изменений , , вам необходимо выберите два коммитов и сравните их . В противном случае все, что у вас есть, - это один снимок. (Попробуйте ссылку и сравните первую пару изображений. Время на часах изменилось?)
Сами коммиты полностью доступны только для чтения. Вы никогда не можете изменить что-либо внутри любого существующего коммита. Что вам нужно Git, так это извлечь существующую фиксацию в рабочую область. Вы можете внести любые изменения в эту рабочую область, а затем использовать git add
и git commit
, чтобы сделать новый коммит. Новый коммит добавляет в репозиторий; существующие коммиты остаются без изменений.
1 A merge commit относится к более чем одной предыдущей фиксации, и по крайней мере одна фиксация в каждом репозитории - самая первая - буквально не может относится к предыдущей фиксации, поэтому это не так. Но большинство коммитов в основном имеют одного родителя, которым является предыдущий коммит. Это то, что история есть в репозитории Git: эта обратная цепочка коммитов, переходящая от фиксации к родительскому, к дедушке и дедушке и так далее. Каждый из этих коммитов имеет полный снимок всех файлов.
У меня есть ветка A, в которой я хотел бы применить изменения из ветки B.
Давайте нарисуем это, причем более ранние коммиты слева, а более поздние - справа. Мы будем использовать одиночные заглавные буквы для обозначения реальных Git commit ha sh ID, но зарезервировать A
и B
для имен веток:
I--J <-- A
/
...--G--H <-- master
\
K--L <-- B
Конечно, я не Я не знаю, как выглядят ваши ветки, но здесь у нас есть три ветки:
A
заканчивается фиксацией J
; B
заканчивается фиксацией L
; и master
заканчивается фиксацией H
.
Родителем фиксации J
является фиксация I
, родителем которого является фиксация G
. Родителем коммита L
является коммит K
, а родительским элементом K
является коммит H
, чьим родителем также является коммит G
. Это означает, что фиксация G
выполняется во всех трех ветвях; фиксация H
находится на master
и B
; и коммиты I-J
и K-L
находятся только на A
и B
соответственно.
Я бы хотел применить сумму всех изменений файла d (с master в качестве основы) из ветки B в другой файл c в ветке A (которая также имеет master в качестве базы) ...
Хорошо, возможно, мне следовало нарисовать это как:
I--J <-- A
/
...--G--H <-- master
\
K--L <-- B
Теперь ваша цель - найти изменений в каком-то файле d
, как это видно в фиксации - вероятно, H
, как определено именем master
- версии этого файла, как видно в коммите L
, идентифицируемом именем ветки B
.
Команда git diff
выполняет следующие действия:
git diff master B
покажет вам, что изменилось в все файлы в фиксации H
(найдены по имени master
) в все файлы в фиксации L
(найдены с использованием имени B
), как если бы в игре «найди разницу». Вы можете вернуть Git обратно, чтобы просто показать все, что отличается в файле d
, игнорируя все другие файлы, попросив git diff
ограничить его вывод:
git diff master B -- d
Возможно, вы захотите перенаправить вывод git diff
во временный файл:
git diff master B -- d > /tmp/the_diff
Обратите внимание, что этот файл имеет в своих инструкциях имя файла d
вместе с внесенными изменениями. Вам нужно будет заменить это имя файла , возможно, используя любой текстовый редактор в сохраненном git diff
выходе, как мы увидим через мгновение.
в другой файл c в ветке A
Помните, что имя ветки просто идентифицирует конкретную фиксацию. Чтобы получить эту фиксацию из из Git в вашу рабочую область, используйте:
git checkout A
или (в Git с 2.23):
git switch A
(эти конкретные вызовы делают то же самое; новая команда git switch
просто более удобна для пользователя, чем старая команда git checkout
, потому что многие операции git checkout
, которые незаметно перезаписывают вашу работу без запроса, были перемещены в отдельная команда, поэтому git switch
безопаснее использовать).
В вашем рабочем дереве теперь есть изменяемая копия файла c
. Теперь вам нужно изменить его, используя инструкции, которые Git создал для , как изменить копию d
, которая находится в фиксации H
, чтобы она выглядела как копия, которая находится в фиксации L
. Для этого вы сначала измените инструкции , чтобы они ссылались на файл c
вместо файла d
. Затем вы можете запустить:
git apply /tmp/the_diff
, который попытается изменить (теперь изменено, чтобы сказать «изменить файл c
», а не «изменить файл d
») на копию c
. это находится в вашем рабочем дереве.
Если все пойдет хорошо, Git применит все изменения. Если некоторые из них не применяются правильно, у вас есть различные варианты, например --reject
или --3way
.
Обратите внимание, что по умолчанию git apply
не будет обрабатывать изменения, которые нужно зафиксировать; это оставляет это вам. Использование git apply --index
изменит это.