git add fileの挙動を追ってみた。
https://github.com/git/git/blob/v1.8.4/builtin/add.c#L445
そこで頭を切り替えて別のアプローチをとりました。
「addしたら結局はzlibで圧縮されるわけだから、deflateまわりのコードにブレークポイントをしかければ引っかかるんじゃないか」と考え、 git grep deflateで検索したらgit_deflate_initというそれっぽい関数が見つかったのでそこにブレークポイントを仕掛けてみました。
break zlib.c:160
そしたらビンゴ!このgit_deflate_initは addするときのzlib圧縮処理の一部であることがわかりました。
git add fileのスタックトレース
スタックトレースはこんな感じです。#0 git_deflate_init (strm=0x7fffffffc8a0, level=1) at zlib.c:163
#1 write_loose_object (sha1=0x7fffffffca30 "\316\001\066%\003\v\250\333\251\006\367V\226\177\236\234\243\224FJ", hdr=0x7fffffffca10 "blob 6", hdrlen=7, buf\
=0x7d3af0, len=6, mtime=0) at sha1_file.c:2892
#2 write_sha1_file (buf=0x7d3af0, len=6, type=<value optimized out>, returnsha1=0x7d3550 "\316\001\066%\003\v\250\333\251\006\367V\226\177\236\234\243\224F\
J") at sha1_file.c:2954
#3 index_mem (sha1=0x7d3550 "\316\001\066%\003\v\250\333\251\006\367V\226\177\236\234\243\224FJ", buf=0x7d3af0, size=6, type=<value optimized out>, path=<v\
alue optimized out>, flags=<value optimized out>) at sha1_file.c:3060
#4 index_core (sha1=0x7d3550 "\316\001\066%\003\v\250\333\251\006\367V\226\177\236\234\243\224FJ", fd=7, st=<value optimized out>, type=OBJ_BLOB, path=0x7d\
44b4 "hello.txt", flags=1) at sha1_file.c:3095
#5 index_fd (sha1=0x7d3550 "\316\001\066%\003\v\250\333\251\006\367V\226\177\236\234\243\224FJ", fd=7, st=<value optimized out>, type=OBJ_BLOB, path=0x7d44\
b4 "hello.txt", flags=1) at sha1_file.c:3139
#6 index_path (sha1=0x7d3550 "\316\001\066%\003\v\250\333\251\006\367V\226\177\236\234\243\224FJ", path=0x7d44b4 "hello.txt", st=<value optimized out>, fla\
gs=1) at sha1_file.c:3157
#7 add_to_index (istate=0x7d19e0, path=0x7d44b4 "hello.txt", st=0x7fffffffcd40, flags=8) at read-cache.c:665
#8 add_file_to_index (istate=0x7d19e0, path=0x7d44b4 "hello.txt", flags=8) at read-cache.c:694
#9 add_files (argc=<value optimized out>, argv=<value optimized out>, prefix=0x0) at builtin/add.c:397
#10 cmd_add (argc=<value optimized out>, argv=<value optimized out>, prefix=0x0) at builtin/add.c:581
#11 run_builtin (argc=2, argv=0x7fffffffe920) at git.c:303
#12 handle_internal_command (argc=2, argv=0x7fffffffe920) at git.c:466
#13 run_argv (argc=2, av=<value optimized out>) at git.c:512
#14 main (argc=2, av=<value optimized out>) at git.c:595
簡単に解説
簡単に解説すると、zlib圧縮して保存する処理はsha_fil1.cのwrite_loose_object関数をみればだいたいわかります。- 一時ファイルを作る
- オブジェクトのヘッダをzlib圧縮
- オブジェクトのデータ部をzlib圧縮
- 一時ファイルに書き込む
- 完了したら一時ファイルを.git/objects/xx/xxxxxx にリネーム
という感じですね。
static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
const void *buf, unsigned long len, time_t mtime)
{
int fd, ret;
unsigned char compressed[4096];
git_zstream stream;
git_SHA_CTX c;
unsigned char parano_sha1[20];
char *filename;
static char tmp_file[PATH_MAX];
filename = sha1_file_name(sha1);
fd = create_tmpfile(tmp_file, sizeof(tmp_file), filename);
if (fd < 0) {
if (errno == EACCES)
return error("insufficient permission for adding an object to repository database %s", get_object_directory());
else
return error("unable to create temporary file: %s", strerror(errno));
}
/* Set it up */
memset(&stream, 0, sizeof(stream));
git_deflate_init(&stream, zlib_compression_level);
stream.next_out = compressed;
stream.avail_out = sizeof(compressed);
git_SHA1_Init(&c);
/* First header.. */
stream.next_in = (unsigned char *)hdr;
stream.avail_in = hdrlen;
while (git_deflate(&stream, 0) == Z_OK)
; /* nothing */
git_SHA1_Update(&c, hdr, hdrlen);
/* Then the data itself.. */
stream.next_in = (void *)buf;
stream.avail_in = len;
do {
unsigned char *in0 = stream.next_in;
ret = git_deflate(&stream, Z_FINISH);
git_SHA1_Update(&c, in0, stream.next_in - in0);
if (write_buffer(fd, compressed, stream.next_out - compressed) < 0)
die("unable to write sha1 file");
stream.next_out = compressed;
stream.avail_out = sizeof(compressed);
} while (ret == Z_OK);
if (ret != Z_STREAM_END)
die("unable to deflate new object %s (%d)", sha1_to_hex(sha1), ret);
ret = git_deflate_end_gently(&stream);
if (ret != Z_OK)
die("deflateEnd on object %s failed (%d)", sha1_to_hex(sha1), ret);
git_SHA1_Final(parano_sha1, &c);
if (hashcmp(sha1, parano_sha1) != 0)
die("confused by unstable object source data for %s", sha1_to_hex(sha1));
close_sha1_file(fd);
if (mtime) {
struct utimbuf utb;
utb.actime = mtime;
utb.modtime = mtime;
if (utime(tmp_file, &utb) < 0)
warning("failed utime() on %s: %s",
tmp_file, strerror(errno));
}
return move_temp_to_file(tmp_file, filename);
}
カテゴリ:
Git