Бывает, что такое надо сделать программно. Мне нужно было для модификации .gitmodules.
И так, на входе у нас:
- remote — сервер, куда мы хотим запушить результат.
- branch — некая ветка, причем мы на ней не стоим!
- filename_src — имя физического файла, источника данных.
- filename — имя файла, как его будет знать GIT.
- commit_message — текст сообщения для коммита.
- author — автор в формате "name <email>.
Последовательность действий:
# получаем текущий хеш ветки (далее $commit_hash)
$git show-ref --hash refs/heads/$branch
# — получаем хеш дерева
$ git cat-file -p $commit_hash | grep '^tree '
# очищаем индекс
$ git reset
# читаем дерево в индекс
$ git read-tree $tree_hash
# создаем объект на основе содержимого файла в локальном кеше GIT, выводит его хеш (далее $file_hash)
$ git hash-object -w $filename_src
# обновляем дерево в индексе
$ git update-index --add --cacheinfo "100644,$file_hash,$filename"
# создаем объект в локальном кеше GIT — новое дерево из индекса, выводит его хеш (далее $new_tree_hash)
$ git write-tree
# коммитер всегда из берется из конфига "~/.gitconfig", если не установить GIT_AUTHOR_NAME, то и автор будет из конфига
$ GIT_AUTHOR_NAME=$author git commit-tree -p $commit_hash -m "$commit_message" $new_tree_hash
# чистим за собой индекс
$ git reset
# обновляем ветку
$ git branch --no-track -f $branch $new_commit_hash
# пушим ветку
$ git push $remote $branch
Если на любом этапе, от read-tree до commit-tree включительно, произошла ошибка, надо почистить индекс.