↓Перейти к содержанию
  1. Git: руководства и команды/

Git хеши: SHA-1, SHA-256 и как Git идентифицирует объекты

·4 минуты·

В Git каждый объект — коммит, файл, папка, тег — идентифицируется уникальным хешем. Это основа архитектуры Git: хеш является одновременно именем объекта и проверкой его целостности. Невозможно изменить содержимое объекта без изменения его хеша.

Что такое хеш в Git #

Хеш — это результат криптографической функции, которая превращает данные любого размера в строку фиксированной длины:

SHA-1: 40 шестнадцатеричных символов
Пример: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0

SHA-256: 64 шестнадцатеричных символа (в новых репозиториях)
Пример: a1b2c3d4e5f6...64 символа...

Хеш:

  • Детерминированный — одни и те же данные дают один и тот же хеш
  • Уникальный — разные данные дают разные хеши (коллизии практически невозможны)
  • Однонаправленный — из хеша нельзя получить исходные данные

Типы объектов Git #

Git хранит четыре типа объектов:

blob — содержимое файла:

# Вычислить хеш содержимого файла
git hash-object README.md
# a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0

# Просмотреть содержимое объекта
git cat-file -p a1b2c3d4
# # My Project
# This is the README file

tree — структура папки (аналог директории):

# Посмотреть дерево текущего коммита
git ls-tree HEAD
# 100644 blob a1b2c3d4  README.md
# 100644 blob e5f6a7b8  package.json
# 040000 tree c9d0e1f2  src/

# Дерево подпапки
git ls-tree HEAD src/
# 100644 blob f3a4b5c6  index.js
# 100644 blob d7e8f9a0  utils.js

commit — коммит с метаданными:

# Просмотреть объект коммита
git cat-file -p HEAD
# tree c9d0e1f2a3b4c5d6e7f8a9b0a1b2c3d4e5f6a7b8
# parent a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0
# author John Doe <[email protected]> 1700000000 +0300
# committer John Doe <[email protected]> 1700000000 +0300
#
# feat: add user authentication

tag — аннотированный тег:

# Просмотреть объект тега
git cat-file -p v1.0
# object a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0
# type commit
# tag v1.0
# tagger John Doe <[email protected]> 1700000000 +0300
#
# Release version 1.0

Как Git вычисляет хеш #

Git вычисляет SHA-1 от строки вида <type> <size>\0<content>:

# Для blob (содержимое файла):
# sha1("blob 12\0Hello World\n")

# Для коммита:
# sha1("commit <size>\0tree <hash>\nauthor ...\n...")

# Проверить вручную (bash)
echo -n "blob 12\0Hello World\n" | sha1sum
# (технически немного другой формат)

# Git способ:
echo "Hello World" | git hash-object --stdin
# Возвращает SHA-1 хеш этого содержимого

Сокращённые хеши #

Хеш в 40 символов неудобен для ввода. Git поддерживает сокращённые версии:

# Полный хеш
git show a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0

# Сокращённый (7+ символов, уникальность гарантируется)
git show a1b2c3d

# git log выводит сокращённые хеши
git log --oneline
# a1b2c3d feat: add authentication
# e5f6a7b fix: resolve login bug
# c9d0e1f initial commit

# Минимальная длина сокращения
git log --oneline --abbrev=4  # 4 символа (рискованно)
git log --oneline --abbrev=12 # 12 символов (надёжно)

Обычно 7 символов достаточно для небольших репозиториев. В больших проектах (ядро Linux) используют 12-15 символов.

Хеш как гарантия целостности #

Хеши связывают коммиты в цепочку:

Commit C: hash = SHA1(tree, parent=B, message)
Commit B: hash = SHA1(tree, parent=A, message)
Commit A: hash = SHA1(tree, parent=null, message)

Изменение любого коммита меняет его хеш. Поскольку следующий коммит содержит хеш предыдущего — меняются все следующие хеши тоже. Это делает историю Git неизменяемой без перезаписи всех последующих коммитов.

SHA-256: будущее Git #

Git 2.29+ поддерживает SHA-256 как альтернативу SHA-1:

# Создать репозиторий с SHA-256
git init --object-format=sha256 my-project

# Проверить формат существующего репозитория
git rev-parse --show-object-format
# sha1  (или sha256 для нового формата)

SHA-256 используется, так как SHA-1 теоретически уязвим к коллизионным атакам (SHAttered атака 2017 года). Для большинства задач SHA-1 всё ещё достаточно безопасен, но переход на SHA-256 — правильное направление.

Практические команды для работы с хешами #

# Получить хеш HEAD (последнего коммита)
git rev-parse HEAD
# a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0

# Получить сокращённый хеш HEAD
git rev-parse --short HEAD
# a1b2c3d

# Хеш конкретной ветки
git rev-parse main
git rev-parse origin/main

# Хеш тега
git rev-parse v1.0

# Тип объекта по хешу
git cat-file -t a1b2c3d4
# commit (или blob, tree, tag)

# Размер объекта
git cat-file -s a1b2c3d4
# 256 (байт)

# Создать blob объект из файла
git hash-object -w myfile.txt
# Создаёт объект в .git/objects/ и возвращает хеш

Расположение объектов в .git #

.git/objects/
├── a1/
│   └── b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0  ← хранится здесь
├── e5/
│   └── f6a7b8...
├── pack/
│   ├── pack-abc123.idx   ← индекс packfile
│   └── pack-abc123.pack  ← сжатые объекты
└── info/

Первые 2 символа хеша — имя папки, оставшиеся 38 — имя файла. Git периодически упаковывает объекты в packfiles (git gc).

Часто задаваемые вопросы #

Могут ли два разных файла иметь одинаковый хеш? Теоретически да (коллизия), но вероятность ничтожно мала для SHA-1 (1 из 2^80) и ещё меньше для SHA-256. На практике не происходит.

Что значит “хеш коммита” в сообщениях об ошибках? Это уникальный идентификатор коммита — строка из 40 (SHA-1) или 64 (SHA-256) шестнадцатеричных символов. По хешу можно найти любой коммит через git show <hash>.

Почему изменение коммита меняет все последующие хеши? Хеш коммита вычисляется из его содержимого + хеша родительского коммита. Если родитель изменился, его хеш изменился → дочерний коммит тоже пересчитывается с новым хешем родителя.

Что такое short hash и когда он недостаточен? Short hash — первые 7+ символов полного хеша. Достаточен когда уникален в репозитории. В очень больших репозиториях (Linux kernel) иногда нужно 12 символов. git log --abbrev-commit автоматически выбирает минимально достаточную длину.

Заключение #

Хеши — основа архитектуры Git. Каждый объект (blob, tree, commit, tag) идентифицируется SHA-1 или SHA-256 хешем. Связанность через хеши делает историю Git неизменяемой. Для просмотра объектов используйте git cat-file, для вычисления хешей — git hash-object. Подробнее об устройстве Git изнутри — объекты Git.