Каким образом git сохраняет изменившуюся строку при коммите?

1,00
р.
Помогите, пожалуйста, разобраться! При замене части одной строки с последующим коммитом, gitk, gitg отображают нечто похожее на:
-Длинная строка версии 1 +Длинная строка версии 2
А сам git работает со строками, или данный вывод только для удобства пользователя? Как же тогда с бинарными данными? То есть можно записать несколько короче, что строка изменилась в таком-то месте.


Ответ
Есть две важных особенности Git:
Git сохраняет содержимое файлов при выполнении git add, а не git commit Git не имеет такого объекта как дельты/патчи/изменения файлов, он работает со снимками (snapshots) файлов, которые были добавлены в индекс из рабочей области.
Однако на уровне хранилища дельты используются, об этом в параграфе 3.2.

А сам git работает со строками, или данный вывод только для удобства пользователя?
Git сравнивает целые файлы, но показывает только те строки, которые изменились. То, что вы видите, выполняя git diff ... - это результат работы инструмента diff. (спасибо Котик_хочет_кушать за ссылку).
1. Как файлы добавляются в индекс (stage)
Команда git add (и git commit -a, которая неявно вызывает git add) создает объекты двух типов и связывает их в направленный ациклический граф:
Листья (blob) — объекты, соответствующие файлам проекта.
Можно считать, что они являются бинарными архивами этих файлов. В обычном режиме для одного файла создается один объект типа лист. Для текстовых файлов возможно исключение: git add --patch Буквально переводится как «капля» или «шарик». Термин заимствован из баз данных, где BLOB это Binary Large OBject.
Деревья (tree) — объекты, соответствующие директориям проекта.
Каждое дерево содержит другие деревья и/или листья. Для корневой директории создается единственный корневой объект–дерево. Дерево не может быть пустым, т.е. ничего не содержать. Поэтому в Git нельзя добавить пустую директорию. Не путайте с веткой — в Git это совершенно иная концепция.

2. Как создается коммит
Все коммиты также хранятся в виде направленного ациклического графа. Команда git commit только формирует объект коммита из уже готовых данных, которые содержатся в индексе. Вот как она это делает:
Создается корневой объект типа commit, который получает ссылку на корневой объект-дерево, хранящийся в индексе. После создания графа к коммиту добавляется метаинформация (автор, дата и т.п.). Коммит получает ссылку на родительский коммит и таким образом включается в граф коммитов. Ветка (которая является просто плавающим указателем) перемещается на вновь созданный коммит. Индекс очищается и ждет новой команды git add.
3. Как сохраняются изменения файлов в истории коммитов
3.1. На концептуальном уровне: цельные снимки
Повторю, что с точки зрения пользователя, коммиты представляются как цельные снимки, а не дельты/патчи относительно друг друга.
На этой иллюстрации: с пунктирным контуром — переиспользуемые версии файлов из прошлого коммита, с целым контуром — новые версии файлов, которые сохраняются в листьях и включаются в дерево нового коммита.
Впрочем, это плохая иллюстрация. Со временем я добавлю хорошую. Напомните мне, если я забуду.

Подробнее в Pro Git (на английском, на русском).
3.2. На функциональном уровне: сжатие через дельты
(Источник, авторы Jan Hudec и VonC, спасибо Eugene Ryabtsev за ссылку).
Git использует дельты, но только для хранения данных в своем внутреннем формате. Более того, это реализовано гораздо эффективнее, чем в любой другой VCS. Git хранит дельты листьев. При сохранении нового листа Git выбирает другие, наиболее близкие к нему, генерирует дельты и выбирает наименьшую. Таким образом он может использовать листья от похожих файлов или более ранних версий этого же, которые ближе по содержанию. Внутренний параметр "pack window" отвечает за баланс между производительностью и эффективностью сжатия дельт. Значение по умолчанию (10) обычно дает удоволетворительные результаты. Но для того, чтобы сэкономить место или ускорить передачу репозитория по сети, можно использовать git gc --aggressive. Эта команда работает со значением pack window = 250 она выполняется очень долго, но обеспечивает более высокое сжатие данных.
Подробнее о сжатии дельт: Matthew McCullough, Git Compression of Blobs and Packfiles.