O objeto de árvore vazia semi-secreta do git é confiável e por que não existe um nome simbólico para ele?

125

O Git tem uma árvore vazia conhecida ou pelo menos uma espécie conhecida, cujo SHA1 é:

4b825dc642cb6eb9a060e54bf8d69288fbee4904

(você pode ver isso em qualquer repositório, mesmo um recém-criado, com git cat-file -te git cat-file -p).

Se você trabalha duro e é muito cuidadoso, pode usar essa árvore vazia para armazenar um diretório que não possui arquivos (consulte a resposta em Como adiciono um diretório vazio a um repositório git ), embora não seja realmente uma boa idéia.

É mais útil como um argumento git diff-tree, como um dos ganchos de amostra.

O que eu quero saber é,

  1. Quão confiável é isso - ou seja, alguma versão futura do git não terá um objeto git numerado 4b825dc642cb6eb9a060e54bf8d69288fbee4904?
  2. Por que não existe um nome simbólico para a árvore vazia (ou existe um?).

(Uma maneira rápida e suja de criar um nome simbólico é colocar o SHA1, por exemplo .git/Nulltree. Infelizmente, você deve fazer isso para todos os repositórios. Parece melhor colocar o número mágico nos scripts, etc. Eu tenho uma aversão geral para números mágicos.)

torek
fonte
3
só para lembrar o hash SHA1 ;-) uso ( "árvore 0 \ 0") = 4b825dc642cb6eb9a060e54bf8d69288fbee4904 (\ 0 é o caráter NUL)
Thomas
4
@ Thomas: o git hash-object -t tree /dev/nullmétodo (da resposta de VonC abaixo) tem a vantagem de não codificar o SHA-1, caso alguma versão futura do git mude para o SHA-2, por exemplo. (Eu não estou indo para tentar prever quando isso pode acontecer :-) Seria mais fácil para alternar Mercurial para SHA-2, desde que eles deixaram espaço para isso..)
Torek
da causa, você está certo, mas é um bom pedaço de "Conhecimento Inútil" e pode, em qualquer caso, ser útil para mais alguém ?!
Thomas
2
@ Thomas: parece que a mudança do algoritmo de hash pode acontecer mais cedo do que o esperado . :-)
torek 26/03
Falando em "alguma versão futura do Git", acho que você estará interessado na minha edição mais recente (dezembro de 2017) da minha resposta de 2012: stackoverflow.com/revisions/9766506/7
VonC

Respostas:

104

Este tópico menciona:

Se você não se lembra da árvore vazia sha1, sempre pode derivá-la com:

git hash-object -t tree /dev/null

Ou, como Ciro Santilli propõe nos comentários :

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

Ou, como visto aqui , de Colin Schimmelfing :

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

Portanto, acho que é mais seguro definir uma variável com o resultado desse comando como sua árvore sha1 vazia (em vez de confiar em um "valor conhecido").

Nota: O Git 2.25.1 (fevereiro de 2020) propõe em commit 9c8a294 :

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

E acrescenta:

Como uma nota histórica, a função agora conhecida como repo_read_object_file()foi ensinada a árvore vazia em 346245a1bb ("codifique o objeto da árvore vazia", ​​13-02-2008, Git v1.5.5-rc0 - mesclagem ) e a função agora conhecida como oid_object_info()foi ensinado a árvore vazia em c4d9986f5f ("sha1_object_info : examine cached_objectstore too", 07-02-2011, Git v1.7.4.1).


Observe que você verá que o SHA1 aparece em algum repositório do GitHub quando o autor deseja que seu primeiro commit fique vazio (consulte a publicação no blog " repositório Como inicializar meus repositórios 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'

Darei à você:

Árvore vazia SHA1

(Veja a árvore SHA1?)

Você pode até refazer o histórico existente sobre esse commit vazio (consulte " git: como inserir um commit como o primeiro, alterando todos os outros? ")

Nos dois casos, você não depende do valor exato de SHA1 dessa árvore vazia.
Você simplesmente segue uma prática recomendada, inicializando seu repositório com um primeiro commit vazio .


Fazer isso:

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"

Isso gerará uma confirmação com um SHA1 específico para seu repositório, nome de usuário, email, data de criação (o que significa que o SHA1 do próprio envio será diferente a cada vez).
Mas a árvore referenciada por esse commit será4b825dc642cb6eb9a060e54bf8d69288fbee4904 a árvore vazia SHA1.

git log --pretty=raw

commit 9ed4ff9ac204f20f826ddacc3f85ef7186d6cc14
tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904      <====
author VonC <[email protected]> 1381232247 +0200
committer VonC <[email protected]> 1381232247 +0200

    initial empty commit

Para mostrar apenas a árvore de uma confirmação (exiba a árvore de confirmação SHA1):

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

Se esse commit, referenciando uma árvore vazia, for realmente o seu primeiro commit, você poderá mostrar esse SHA1 da árvore vazia com:

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

(e isso ainda funciona no Windows, com o Gnu On Windows comandos do )


Como comentado abaixo , usando git diff <commit> HEAD, isso mostrará todo o seu arquivo no ramo atual HEAD:

git diff --name-only 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD

Nota: esse valor vazio da árvore é formalmente definido em cache.h.

#define EMPTY_TREE_SHA1_HEX \
    "4b825dc642cb6eb9a060e54bf8d69288fbee4904"

Desde o Git 2.16 (primeiro trimestre de 2018), ele é usado em uma estrutura que não está mais vinculada ao (apenas) SHA1, como visto em commit eb0ccfd :

Alterne as pesquisas em árvore e blob vazias para usar a abstração de hash

Alterne os usos de empty_tree_oide empty_blob_oidpara usar a current_hashabstração que representa o algoritmo de hash atual em uso.

Veja mais em " Por que o Git não usa SHA mais moderno? ": É SHA-2 , desde o Git 2.19 (terceiro trimestre de 2018)


Com o Git 2.25 (primeiro trimestre de 2020), os testes estão se preparando para uma transição SHA-2 e envolvem a árvore vazia.

Veja cometer fa26d5e , cometer cf02be8 , cometer 38ee26b , cometer 37ab8eb , cometer 0370b35 , cometer 0253e12 , cometer 45e2ef2 , cometer 79b0edc , cometer 840624f , cometer 32a6707 , cometer 440bf91 , cometer 0b408ca , cometer 2eabd38 (28 de outubro de 2019), e comprometer 1bcef51 , cometer ecde49b (05 Out 2019) por brian m. Carlson ( bk2204) .
(Mesclado por Junio ​​C Hamano - gitster- no commit 28014c1, 10 nov 2019)

t/oid-info: adicionar árvore vazia e valores de blob vazios

Assinado por: brian m. Carlson

Eventualmente, o testsuite aprenderá a executar usando um algoritmo que não seja o SHA-1. Em preparação para isso, ensine à test_oidfamília de funções como procurar o blob vazio e os valores da árvore vazia para que possam ser usados.

Então t/oid-info/hash-infoagora inclui:

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

O SHA2 " 6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321" é a nova 4b825dc642cb6eb9a060e54bf8d69288fbee4904árvore vazia do SHA1 " ".

VonC
fonte
@torek: adicionei alguns exemplos à primeira prática recomendada de commit vazio para ilustrar a árvore vazia SHA1.
VonC 19/03/12
Bem, um dos objetivos é usar o hash "árvore vazia" como argumento git diff-treeem alguns scripts que estou escrevendo. Não há garantia de que haja um commit vazio inicial no repositório. Então, eu estou me perguntando se esses scripts podem acabar quebrando algum dia.
Torek 19/03/12
1
Se você passar -wpara git hash-object, ele vai criar o objeto no repositório é executado contra, e que iria recriar a árvore vazia no repositório você está correndo contra foram para nunca ir longe no futuro.
javawizard
Se você quer ir antes do primeiro commit usando rebase, você pode usar git rebase --root
GergelyPolonkai
1
Ou se você prefere a magia dos canos, em vez da mágica de /dev/null: printf '' | git hash-object --stdin -t tree:)
Ciro Santilli
3

Eu escrevi um post de blog com duas maneiras diferentes de encontrar o hash: http://colinschimmelfing.com/blog/gits-empty-tree/

Se alguma vez mudar por algum motivo, você pode usar as duas maneiras abaixo para encontrá-lo. No entanto, eu me sentiria bastante confiante usando o hash nos aliases .bashrc etc., e não acho que isso mude tão cedo. No mínimo, provavelmente seria um grande lançamento do git.

As duas maneiras são:

  1. A resposta acima: git hash-object -t tree --stdin < /dev/null
  2. Simplesmente iniciando um repositório vazio e executando git write-treenesse novo repositório - o hash será gerado pelo git write-tree.
schimmy
fonte
A execução do comando com –-stdinme fornece o fatal: Cannot open '–-stdin': No such file or directorygit 2.7.2. No entanto, executá-lo sem --stdincomo na resposta do VonC dá o valor de hash
sigy
Esta resposta não é muito útil agora que o post do blog está morto. Por isso, geralmente não aprovamos essas respostas no SO.
Philip Whitehouse
1
@PhilipWhitehouse, a postagem do blog não está morta, mas em qualquer caso, incluí as duas maneiras na minha resposta - concordo que, sem incluir essas duas maneiras, não seria uma boa resposta.
schimmy
3

Aqui está a resposta sobre como criar confirmação de árvore vazia, mesmo no caso em que o repositório ainda não estiver vazio. https://stackoverflow.com/a/14623458/9361507

Mas eu prefiro que "vazio" seja tag, mas não um ramo. Maneira simples é:

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

Porque a tag pode apontar diretamente para a árvore, sem confirmação. Agora, para obter todos os arquivos na árvore de trabalho:

git diff --name-only empty

Ou o mesmo com stat:

git diff --stat empty

Todos os arquivos como diff:

git diff empty

Verifique os espaços em branco em todos os arquivos:

git diff --check empty
Olleg
fonte
... mas usar o número mágico na criação de sua tag é apenas escovar debaixo do tapete a própria questão ( não usar o número mágico SHA-1) #
9139 RomainValeri
Não é verdade. Eu usei a tag para apontar para o objeto tree-ish. No momento, esse tree-ish é definido pelo SHA-1; no futuro, ele pode ser alterado, por exemplo, para SHA-256 e assim por diante (com a migração do repositório). Mas a tag será a mesma. :) O principal recurso de uma tag é apontar para o objeto. Uma tag pode usar o SHA-1 internamente ou qualquer outra coisa; é apenas uma questão de componentes internos do Git.
Olleg
Entendi. Mas se você (ou alguém que estiver lendo isso) (ou um script , ainda pior) tentar aplicá-lo (sua primeira linha) posteriormente, poderá falhar em um novo algoritmo de hash, ao substituir sua primeira linha por uma expressão executada (produzindo esse hash) continuaria tendo sucesso.
RomainValeri 01/03/19
Se você combinar isso com um dos métodos para gerar o hash da árvore vazia automaticamente, poderá fazer uma prova futura (como sugere @RomainValeri). No entanto, se dependesse de mim, git rev-parseteria novos sinalizadores ou palavras-chave ou algo nesse sentido, para produzir (a) o hash da árvore vazia e (b) o hash de confirmação nula. Ambos seriam úteis em scripts e protegeriam contra as alterações propostas no SHA-256.
Torek 01/03/19
Okey, mudou. Mas isso não será "uma maneira mais simples". :)
Olleg 01/03/19