Является ли полусекретный объект пустого дерева git надежным и почему для него нет символического имени?


125

У Git есть хорошо известное или, по крайней мере, хорошо известное пустое дерево с SHA1:

4b825dc642cb6eb9a060e54bf8d69288fbee4904

(вы можете увидеть это в любом репо, даже во вновь созданном, с помощью git cat-file -tи git cat-file -p).

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

Это более полезно в качестве аргумента того git diff-tree, что делает один из примеров хуков.

Мне интересно,

  1. насколько это надежно - т.е. не будет ли в какой-нибудь будущей версии git нумерованный объект git 4b825dc642cb6eb9a060e54bf8d69288fbee4904?
  2. Почему у пустого дерева нет символического имени (или оно есть?).

(Быстрый и грязный способ создать символическое имя - это вставить SHA1, например .git/Nulltree. К сожалению, вы должны делать это для каждого репо. Кажется, лучше просто поместить магическое число в скрипты и т. Д. Я просто испытываю общее отвращение к магическим числам.)


3
просто чтобы запомнить хеш ;-) используйте SHA1 ("tree 0 \ 0") = 4b825dc642cb6eb9a060e54bf8d69288fbee4904 (\ 0 - символ NUL)
Thomas

4
@Thomas: git hash-object -t tree /dev/nullметод (из ответа VonC ниже) имеет то преимущество, что не требует жесткого кодирования SHA-1, например, в случае, если какая-то будущая версия git переключается на SHA-2. (Я не буду пытаться предсказать, когда это может произойти. :-) Было бы проще переключить Mercurial на SHA-2, поскольку они оставили для него место.)
torek

Вы, конечно, правы, но это хороший кусок «Бесполезного знания» и может быть он в любом случае пригодится кому-то еще ?!
Thomas

2
@Thomas: похоже, смена алгоритма хеширования может произойти раньше, чем ожидалось . :-)
Торек

Говоря о «какой-то будущей версии Git», я думаю, вам будет интересно мое последнее (декабрь 2017 г.) редактирование моего ответа 2012 года: stackoverflow.com/revisions/9766506/7
VonC

Ответы:


104

В этой ветке упоминается:

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

git hash-object -t tree /dev/null

Или, как предлагает Чиро Сантилли в комментариях :

printf '' | git hash-object --stdin -t tree

Или, как видно здесь , от Колина Шиммельфинга :

git hash-object -t tree --stdin < /dev/null

Так что, я думаю, безопаснее определить переменную с результатом этой команды как ваше пустое дерево sha1 (вместо того, чтобы полагаться на «общеизвестное значение»).

Примечание. Git 2.25.1 (февраль 2020 г.) предлагает в коммите 9c8a294 :

empty_tree=$(git mktree </dev/null)
# Windows:
git mktree <NUL

И добавляет:

Историческая справка: функция, теперь известная как repo_read_object_file()пустое дерево, была обучена в 346245a1bb («жестко запрограммировать объект пустого дерева», 2008-02-13, Git v1.5.5-rc0 - merge ), а теперь известная функция как oid_object_info()учили пустое дерево в c4d9986f5f (" sha1_object_info: также исследуйте cached_objectмагазин", 2011-02-07, Git v1.7.4.1).


Обратите внимание: вы увидите, что SHA1 появляется в каком-то репозитории GitHub, когда автор хочет, чтобы его первая фиксация была пустой (см. Сообщение в блоге « Как я инициализирую свои репозитории Git »):

$ GIT_AUTHOR_DATE="Thu, 01 Jan 1970 00:00:00 +0000" GIT_COMMITTER_DATE="Thu, 01 Jan 1970 00:00:00 +0000" git commit --allow-empty -m 'Initial commit'

Дам тебе:

Пустое дерево SHA1

(Видите дерево SHA1?)

Вы даже можете переназначить существующую историю поверх этой пустой фиксации (см. « Git: как вставить фиксацию первым, сдвинув все остальные? »)

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


Для этого:

git init my_new_repo
cd my_new_repo
git config user.name username
git config user.email email@com

git commit --allow-empty -m "initial empty commit"

Это сгенерирует фиксацию с SHA1, специфичным для вашего репо, имени пользователя, электронной почты, даты создания (это означает, что SHA1 самой фиксации будет каждый раз отличаться).
Но дерево, на которое ссылается этот коммит, будет 4b825dc642cb6eb9a060e54bf8d69288fbee4904пустым деревом SHA1.

git log --pretty=raw

commit 9ed4ff9ac204f20f826ddacc3f85ef7186d6cc14
tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904      <====
author VonC <vonc@laposte.net> 1381232247 +0200
committer VonC <vonc@laposte.net> 1381232247 +0200

    initial empty commit

Чтобы показать только дерево фиксации (отобразить дерево фиксации SHA1):

git show --pretty=format:%T 9ed4ff9ac204f20f826ddacc3f85ef7186d6cc14
4b825dc642cb6eb9a060e54bf8d69288fbee4904

Если эта фиксация, ссылающаяся на пустое дерево, действительно является вашей первой фиксацией, вы можете показать это пустое дерево SHA1 с помощью:

git log --pretty=format:%h --reverse | head -1 | xargs git show --pretty=format:%T
4b825dc642cb6eb9a060e54bf8d69288fbee4904

(и это работает даже в Windows с командами Gnu On Windows )


Как прокомментировано ниже , при использовании git diff <commit> HEADэто покажет весь ваш файл в текущей ветке HEAD:

git diff --name-only 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD

Примечание: это значение пустого дерева формально определено в cache.h.

#define EMPTY_TREE_SHA1_HEX \
    "4b825dc642cb6eb9a060e54bf8d69288fbee4904"

Начиная с Git 2.16 (Q1 2018), он используется в структуре, которая больше не привязана (только) к SHA1, как показано в commit eb0ccfd :

Переключите поиск пустого дерева и больших двоичных объектов на использование хеш-абстракции

Переключите использование empty_tree_oidи, empty_blob_oidчтобы использовать current_hashабстракцию, которая представляет текущий используемый алгоритм хеширования.

Дополнительные сведения см. В разделе « Почему Git не использует более современный SHA? »: Это SHA-2 , начиная с Git 2.19 (3 ​​квартал 2018 г.)


В Git 2.25 (первый квартал 2020 г.) тесты готовятся к переходу SHA-2 и включают пустое дерево.

См совершать fa26d5e , совершать cf02be8 , совершают 38ee26b , совершают 37ab8eb , совершают 0370b35 , совершают 0253e12 , совершают 45e2ef2 , совершают 79b0edc , совершают 840624f , совершают 32a6707 , совершают 440bf91 , совершают 0b408ca , совершают 2eabd38 (28 окт 2019), а также совершать 1bcef51 , совершают ecde49b (5 октября 2019 г.) Брайан М. Карлсон ( bk2204) .
(Объединено Junio ​​C Hamano - gitster- в фиксации 28014c1, 10 ноя 2019)

t/oid-info: добавить пустое дерево и пустые значения blob

Подписано: brian m. Карлсон

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

Итак, t/oid-info/hash-infoтеперь включает:

rawsz sha1:20
rawsz sha256:32

hexsz sha1:40
hexsz sha256:64

zero sha1:0000000000000000000000000000000000000000
zero sha256:0000000000000000000000000000000000000000000000000000000000000000

algo sha1:sha1
algo sha256:sha256

empty_blob sha1:e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
empty_blob sha256:473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813

empty_tree sha1:4b825dc642cb6eb9a060e54bf8d69288fbee4904
empty_tree sha256:6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321

SHA2 " 6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321" - это новое 4b825dc642cb6eb9a060e54bf8d69288fbee4904пустое дерево SHA1 " ".


@torek: Я добавил несколько примеров из лучших практик первого пустого коммита, чтобы проиллюстрировать это пустое дерево SHA1.
VonC

Ну, одна из целей - использовать хэш «пустого дерева» в качестве аргумента git diff-treeв некоторых сценариях, которые я пишу. Нет никакой гарантии, что в репо есть начальная пустая фиксация. Так что мне просто интересно, могут ли эти скрипты когда-нибудь сломаться.
torek

1
Если вы перейдете -wк git hash-object, он создаст объект в репозитории, с которым запускается, и это воссоздает пустое дерево в репозитории, с которым вы работаете, если оно когда-либо исчезнет в будущем.
javawizard 03

Если вы хотите перейти до первого коммита с помощью rebase, вы можете использовать git rebase --root
GergelyPolonkai

1
Или, если вы предпочитаете магию труб вместо магии /dev/null: printf '' | git hash-object --stdin -t tree:)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

3

Я написал сообщение в блоге с двумя разными способами поиска хеша: http://colinschimmelfing.com/blog/gits-empty-tree/

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

Двумя способами являются:

  1. Ответ выше: git hash-object -t tree --stdin < /dev/null
  2. Просто инициируйте пустое репо и затем запустите git write-treeэто новое репо - хеш будет выведен git write-tree.

Выполнение команды –-stdinдает мне fatal: Cannot open '–-stdin': No such file or directorygit 2.7.2. Однако запуск его без --stdinas в ответе VonC дает хеш-значение
sigy

Этот ответ не очень полезен сейчас, когда сообщение в блоге мертво. Следовательно, почему мы обычно не одобряем эти ответы на SO.
Филип Уайтхаус

1
@PhilipWhitehouse, сообщение в блоге не умерло, но в любом случае я включил два способа в свой ответ - я согласен, что без включения этих двух способов это был бы плохой ответ.
Шимми

3

Вот ответ, как создать фиксацию пустого дерева, даже если репозиторий еще не пуст. https://stackoverflow.com/a/14623458/9361507

Но я предпочитаю «пустой» быть тегом, а не веткой. Простой способ:

git tag empty $(git hash-object -t tree /dev/null)

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

git diff --name-only empty

Или то же самое со stat:

git diff --stat empty

Все файлы как diff:

git diff empty

Проверьте пробелы во всех файлах:

git diff --check empty

... но использование магического числа в создании вашего тега - это просто
подметание

Не правда. Я использовал тег, чтобы указать на древовидный объект. К настоящему времени это древовидное представление определено SHA-1, в будущем его можно изменить, например, на SHA-256 и так далее (с миграцией репозитория). Но тег будет таким же. :) Основная особенность тега - указывать на объект. Тег может использовать SHA-1 внутренне или что-то еще, это вопрос только внутреннего устройства Git.
Olleg 01

Я понимаю. Но если вы (или кто-либо, читающий это) (или сценарий , что еще хуже) попытаетесь применить его (вашу первую строку) в более поздний момент, он может потерпеть неудачу на новом алгоритме хеширования, где замена вашей первой строки выполненным выражением (создание этот хеш) будет успешным.
RomainValeri 01

Если вы объедините это с одним из методов автоматического создания хэша пустого дерева, вы можете защитить это в будущем (как предлагает @RomainValeri). Однако, если бы это было на мое усмотрение, у меня git rev-parseбыли бы новые флаги или ключевые слова или что-то в этом роде, чтобы создать (а) хэш пустого дерева и (б) хеш нулевой фиксации. Оба они будут полезны в сценариях и защитят от предлагаемых изменений SHA-256.
Торек

Окей, изменилось. Но это будет не «самый простой способ». :)
Olleg 01
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.