git-read-tree
Имя
git-read-tree — Читает информацию о дереве в индекс
Синтаксис
git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>)
[-u | -i]] [--index-output=<file>] [--no-sparse-checkout]
(--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]]) Описание
Считывает информацию о дереве, указанном <tree-ish>, в индекс, но не обновляет фактически никакие файлы, которые он "кэширует". (см.: git-checkout-index[1])
По желанию, он может объединить дерево в индекс, выполнить слияние по типу «быстрый сдвиг» (т.е. двухстороннее), или трёхстороннее слияние с помощью флага -m. При использовании с -m, флаг -u заставляет его также обновить файлы в рабочей области результатом слияния.
Только тривиальные слияния выполняются git read-tree самостоятельно. Только конфликтные пути останутся в неслитом состоянии, когда git read-tree возвращается.
Параметры
- -m
-
Выполнить слияние, а не просто чтение. Команда откажется от запуска, если в вашем файле индекса есть неслитые записи, что указывает на то, что вы не завершили предыдущее начатое слияние.
- --reset
-
То же, что и -m, за исключением того, что неслитые записи отбрасываются вместо отказа. При использовании с
-u, обновления, приводящие к потере изменений в рабочей области или неотслеживаемых файлов или каталогов, не прервут операцию. - -u
-
После успешного слияния обновить файлы в рабочей области результатом слияния.
- -i
-
Обычно для слияния требуется, чтобы файл индекса, а также файлы в рабочей области были обновлены с текущей коммитом, чтобы не потерять локальные изменения. Этот флаг отключает проверку с рабочей областью и предназначен для использования при создании слияния деревьев, которые не напрямую связаны с текущим состоянием рабочей области, в временный файл индекса.
- -n
- --dry-run
-
Проверить, вызовет ли команда ошибку, без обновления индекса или файлов в рабочей области.
- -v
-
Показать процесс проверки файлов.
- --trivial
-
Ограничить трёхстороннее слияние
git read-treeтем, что оно произойдёт только в том случае, если не требуется слияние на уровне файлов, вместо разрешения слияний для тривиальных случаев и оставления конфликтующих файлов неразрешенными в индексе. - --aggressive
-
Обычно трёхстороннее слияние
git read-treeразрешает слияние для действительно тривиальных случаев и оставляет другие случаи неразрешенными в индексе, так что утилиты могут реализовывать разные стратегии слияния. Этот флаг заставляет команду разрешать несколько больше случаев внутри:-
когда одна сторона удаляет путь, а другая сторона оставляет путь без изменений. Решение — удалить этот путь.
-
когда обе стороны удаляют путь. Решение — удалить этот путь.
-
когда обе стороны добавляют путь идентично. Решение — добавить этот путь.
-
- --prefix=<prefix>
-
Сохранить текущее содержимое индекса и прочитать содержимое указанного tree-ish в каталоге по адресу
<prefix>. Команда откажется от перезаписи записей, которые уже существовали в исходном файле индекса. - --index-output=<file>
-
Вместо записи результатов в
$GIT_INDEX_FILE, записать полученный индекс в указанный файл. Во время работы команды исходный файл индекса блокируется стандартным механизмом. Файл должен позволять переименовать его (2) в временный файл, созданный рядом с обычным файлом индекса; обычно это означает, что он должен находиться в той же файловой системе, что и сам файл индекса, и у вас должны быть права записи в каталогах, где расположены файл индекса и файл вывода индекса. - --[no-]recurse-submodules
-
Использование --recurse-submodules обновит содержимое всех активных подмодулей в соответствии с коммитом, записанным в суперпроекте, вызвав read-tree рекурсивно, также установив HEAD подмодулей в этом коммите.
- --no-sparse-checkout
-
Отключить поддержку разреженного checkout, даже если
core.sparseCheckoutистинно. - --empty
-
Вместо считывания объекта(ов) дерева в индекс, просто очистить его.
- -q
- --quiet
-
Без вывода сообщений.
- <tree-ish#>
-
Идентификатор объекта(ов) дерева, который нужно прочитать/слить.
Слияние
Если указано -m, git read-tree может выполнить 3 вида слияния: слияние одного дерева, если указано только одно дерево; быстрое слияние с двумя деревьями; или слияние трёх путей, если указано три или более деревьев.
Слияние одного дерева
Если указано только одно дерево, git read-tree работает так, как если бы пользователь не указал -m, за исключением того, что если в исходном индексе есть запись для заданного пути и содержимое пути совпадает с содержимым читаемого дерева, используется информация о состоянии из индекса. (Другими словами, информация о состоянии из индекса имеет приоритет над информацией о состоянии из сливаемого дерева).
Это означает, что если вы выполните git read-tree -m <newtree> за которым следует git checkout-index -f -u -a, git checkout-index проверяет только действительно изменённые данные.
Это используется для предотвращения ненужных ложных срабатываний, когда git diff-files выполняется после git read-tree.
Слияние двух деревьев
Обычно это вызывается как git read-tree -m $H $M, где $H — коммит головы текущего репозитория, а $M — голова внешнего дерева, которое просто находится впереди $H (т.е. мы находимся в ситуации быстрого слияния).
Когда указываются два дерева, пользователь сообщает git read-tree следующее:
-
Текущий индекс и рабочая директория получены из $H, но с момента $H пользователь мог внести локальные изменения.
-
Пользователь хочет выполнить быстрое слияние до $M.
В этом случае команда git read-tree -m $H $M гарантирует, что при этом слиянии не потеряются локальные изменения. Вот правила "переноса", где "I" обозначает индекс, "чисто" означает, что индекс и рабочая директория совпадают, а "существует"/"нет" относится к наличию пути в указанном коммите:
I H M Result
-------------------------------------------------------
0 nothing nothing nothing (does not happen)
1 nothing nothing exists use M
2 nothing exists nothing remove path from index
3 nothing exists exists, use M if "initial checkout",
H == M keep index otherwise
exists, fail
H != M
clean I==H I==M
------------------
4 yes N/A N/A nothing nothing keep index
5 no N/A N/A nothing nothing keep index
6 yes N/A yes nothing exists keep index
7 no N/A yes nothing exists keep index
8 yes N/A no nothing exists fail
9 no N/A no nothing exists fail
10 yes yes N/A exists nothing remove path from index
11 no yes N/A exists nothing fail
12 yes no N/A exists nothing fail
13 no no N/A exists nothing fail
clean (H==M)
------
14 yes exists exists keep index
15 no exists exists keep index
clean I==H I==M (H!=M)
------------------
16 yes no no exists exists fail
17 no no no exists exists fail
18 yes no yes exists exists keep index
19 no no yes exists exists keep index
20 yes yes no exists exists use M
21 no yes no exists exists fail Во всех случаях "сохранение индекса" запись в индексе остаётся такой же, как и в исходном файле индекса. Если запись не обновлена, git read-tree сохраняет копию в рабочей директории неизменной при работе со флагом -u.
Когда эта форма git read-tree завершается успешно, вы можете увидеть, какие из ваших "локальных изменений" были перенесены, выполнив git diff-index --cached $M. Обратите внимание, что это не обязательно совпадает с тем, что git diff-index --cached $H вывело бы до такого слияния двух деревьев. Это из-за случаев 18 и 19 — если у вас уже были изменения в $M (например, вы получили их по электронной почте в виде патча), git diff-index
--cached $H сообщило бы вам об изменении до этого слияния, но оно не будет отображено в выводе git diff-index --cached $M после слияния двух деревьев.
Случай 3 немного сложнее и требует объяснения. Результат из этого правила логически должен заключаться в удалении пути, если пользователь поставил на отметку удаление пути, а затем переключился на новую ветку. Однако это помешает первоначальному выполнению проверки, поэтому правило модифицировано для использования M (нового дерева) только в том случае, если содержимое индекса пустое. В противном случае удаление пути сохраняется, пока $H и $M одинаковы.
Слияние трёх путей
В каждой записи "индекса" есть два бита состояния "этапа". Стадия 0 — обычная, и именно она видна при обычном использовании.
Однако, когда вы выполняете git read-tree с тремя деревьями, "стадия" начинается с 1.
Это означает, что вы можете выполнить
$ git read-tree -m <tree1> <tree2> <tree3>
и в итоге получите индекс со всеми записями <tree1> на "этапе 1", со всеми записями <tree2> на "этапе 2" и со всеми записями <tree3> на "этапе 3". При выполнении слияния другой ветки в текущую ветку мы используем общее предковое дерево как <tree1>, текущую вершину ветки как <tree2> и вершину другой ветки как <tree3>.
Кроме того, у git read-tree есть специальная логика, которая гласит: если вы видите файл, который полностью соответствует в следующих состояниях, он "восстанавливается" до "стадии 0":
-
стадия 2 и 3 одинаковы; возьмите одну из них (нет разницы — одинаковая работа была выполнена в ветке на этапе 2 и в их ветке на этапе 3)
-
стадия 1 и стадия 2 одинаковы, а стадия 3 отличается; возьмите стадию 3 (наша ветка на стадии 2 ничего не делала с момента предка на стадии 1, в то время как их ветка на стадии 3 работала над этим)
-
стадия 1 и стадия 3 одинаковы, а стадия 2 отличается; возьмите стадию 2 (мы что-то сделали, а они — нет)
Команда git write-tree отказывается записывать бессмысленное дерево, и она будет жаловаться на неслитые записи, если она увидит хотя бы одну запись, которая не находится на стадии 0.
Хорошо, это всё звучит как набор совершенно бессмысленных правил, но на самом деле это именно то, что вам нужно для быстрого слияния. Различные стадии представляют "результирующее дерево" (стадия 0, также известная как "слитое"), исходное дерево (стадия 1, также известная как "исходное") и два дерева, которые вы пытаетесь слить (соответственно стадии 2 и 3).
Порядок стадий 1, 2 и 3 (и, следовательно, порядок трёх аргументов командной строки <tree-ish>) важен, когда вы начинаете слияние трёх путей с файлом индекса, который уже заполнен. Вот набросок того, как работает алгоритм:
-
если файл существует в идентичном формате во всех трёх деревьях, он автоматически переходит в состояние "слито"
git read-tree. -
файл, который имеет
anyразличие в трёх деревьях, останется отдельными записями в индексе. За "политикой по обработке данных" остаётся решение о том, как удалить стадии, отличные от 0, и вставить объединённую версию. -
файл индекса сохраняет и восстанавливает всю эту информацию, поэтому вы можете выполнять слияние постепенно. Но до тех пор, пока он содержит записи на стадиях 1/2/3 (т.е. "неслитые записи"), вы не можете записать результат. Теперь алгоритм слияния становится действительно простым:
-
вы просматриваете индекс в порядке и пропускаете все записи на стадии 0, так как они уже обработаны.
-
если вы найдёте "стадию 1", но не найдёте соответствующих "стадий 2" или "3", вы знаете, что она была удалена из обоих деревьев (она существовала только в исходном дереве), и вы удаляете эту запись.
-
если вы найдёте соответствующие "стадии 2" и "3", вы удалите одну из них и превратите другую в запись "стадия 0". Удалите соответствующую запись "стадия 1", если она существует. .. все обычные тривиальные правила ..
-
Обычно вы используете git merge-index с предоставленными git merge-one-file для выполнения этого последнего шага. Сценарий обновляет файлы в рабочей директории по мере слияния каждого пути и в конце успешного слияния.
Когда вы начинаете слияние трёх путей с уже заполненным файлом индекса, предполагается, что он представляет состояние файлов в вашей рабочей директории, и вы даже можете иметь файлы с внесёнными изменениями, не зафиксированными в файле индекса. Далее предполагается, что это состояние "получено" из дерева на стадии 2. Слияние трёх путей откажется от работы, если обнаружит запись в исходном файле индекса, не соответствующую стадии 2.
Это делается для предотвращения потери ваших изменений в процессе работы, а также смешивания случайных изменений в несвязанном коммите слияния. В качестве примера, предположим, что вы начинаете с последнего внесённого в ваш репозиторий коммита:
$ JC=`git rev-parse --verify "HEAD^0"` $ git checkout-index -f -u -a $JC
Вы делаете случайные правки, не выполняя git update-index. И затем вы замечаете, что вершина вашего "удаленного" дерева продвинулась с момента вашего извлечения данных от него:
$ git fetch git://.... linus $ LT=`git rev-parse FETCH_HEAD`
Ваша рабочая директория всё ещё основана на вашем HEAD ($JC), но у вас есть некоторые изменения с тех пор. Слияние трёх путей гарантирует, что вы не добавляли или не изменяли записи индекса с момента $JC, и если этого не произошло, то выполняет правильные действия. Таким образом, при следующей последовательности:
$ git read-tree -m -u `git merge-base $JC $LT` $JC $LT $ git merge-index git-merge-one-file -a $ echo "Merge with Linus" | \ git commit-tree `git write-tree` -p $JC -p $LT
то тем, что вы будете коммитить, будет чистое слияние между $JC и $LT без ваших изменений в процессе работы, и ваша рабочая директория будет обновлена до результата слияния.
Однако, если у вас есть локальные изменения в рабочей директории, которые будут перезаписаны этим слиянием, git read-tree откажется от выполнения, чтобы предотвратить потерю ваших изменений.
Другими словами, не нужно беспокоиться о том, что существует только в рабочей директории. Когда у вас есть локальные изменения в части проекта, которая не участвует в слиянии, ваши изменения не влияют на слияние и сохраняются. Когда они влияют, слияние даже не начинается (git read-tree громко жалуется и завершается неудачно, без внесения каких-либо изменений). В таком случае вы можете просто продолжить то, что вы делали, и когда ваша рабочая директория будет готова (т.е. вы завершите свои текущие изменения), попробуйте выполнить слияние снова.
Разреженный вывод
Примечание: возможности пропуска рабочей области в git-update-index[1] и read-tree появились до введения git-sparse-checkout[1]. Пользователям рекомендуется использовать команду sparse-checkout вместо этих вспомогательных команд для задач, связанных с sparse-checkout/пропуском рабочей области. Однако информация ниже может быть полезной для пользователей, пытающихся понять стиль шаблонов, используемых в режиме неконуса команды sparse-checkout.
"Разреженный вывод" позволяет заполнять рабочую область выборочно. Он использует бит skip-worktree (см. git-update-index[1]), чтобы указать Git, стоит ли рассматривать файл в рабочей области.
git read-tree и другие команды, основанные на слиянии (git merge, git checkout…) могут помочь в поддержании бита skip-worktree и обновлении рабочей области. $GIT_DIR/info/sparse-checkout используется для определения ссылки на битовую карту skip-worktree. Когда git read-tree необходимо обновить рабочую область, он сбрасывает бит skip-worktree в индексе на основе этого файла, который использует тот же синтаксис, что и файлы .gitignore. Если запись соответствует шаблону в этом файле или запись соответствует файлу, присутствующему в рабочей области, то бит skip-worktree не будет установлен для этой записи. В противном случае, бит skip-worktree будет установлен.
Затем сравнивается новое значение skip-worktree со старым. Если skip-worktree переключается с установленного в не установленное, соответствующий файл будет добавлен обратно. Если он переключается с не установленного в установленное, этот файл будет удалён.
В то время как $GIT_DIR/info/sparse-checkout обычно используется для указания файлов, которые должны быть включены, вы также можете указать файлы, которые должны быть not включены, используя обратные шаблоны. Например, чтобы удалить файл unwanted:
/* !unwanted
Ещё одна тонкость – полное заполнение рабочей области, когда вы больше не хотите использовать разреженный вывод. Вы не можете просто отключить "разреженный вывод", потому что биты skip-worktree всё ещё находятся в индексе, и ваша рабочая область всё ещё заполняется выборочно. Вам следует повторно заполнить рабочую область содержимым файла $GIT_DIR/info/sparse-checkout следующим образом:
/*
Затем вы можете отключить разреженный вывод. Поддержка разреженного вывода в git read-tree и аналогичных командах отключена по умолчанию. Вам нужно включить core.sparseCheckout для поддержки разреженного вывода.
См. также
read-tree
© 2005–2026 Linus Torvalds and others
Licensed under the GNU General Public License version 2.
https://git-scm.com/docs/git-read-tree