update-catch.c 作用类似于以后的 git add f1 f2 ...
1. 从.dircache/index文件中读取所有的已存储的文件对应的catch_entry信息到内存列表中
2. 对于新文件:
I. 将文件内容压缩
II. 对压缩数据取SHA1值作为文件名
III. 将压缩数据写入到文件
IV. 添加新catch_entry到内存列表
3. 对于更新文件:
I. 将文件内容压缩
II. 对压缩数据取SHA1值作为文件名
III. 将压缩数据写入到文件
IV. 更新catch_entry到内存列表
4. 对于删除文件:
I. 从内存列表中删除catch_entry
5. 将内存列表信息写入到.dircache/index.lock文件,重命名为.dircache/index
首先介绍几个关键的数据结构和宏定义
cache_entry: 用于保存每个源数据文件信息
cache_header: 用于保存索引信息
cache_entry_size:
ce_size:
struct cache_time {
unsigned int sec;
unsigned int nsec;
};
struct cache_entry {
struct cache_time ctime;
struct cache_time mtime;
unsigned int st_dev;
unsigned int st_ino;
unsigned int st_mode;
unsigned int st_uid;
unsigned int st_gid;
unsigned int st_size;
unsigned char sha1[20];
unsigned short namelen;
unsigned char name[0];
};
#define CACHE_SIGNATURE 0x44495243
struct cache_header {
unsigned int signature;
unsigned int version;
unsigned int entries;
unsigned char sha1[20];
};
#define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)
#define ce_size(ce) cache_entry_size((ce)->namelen)
int main(int argc, char **argv)
{
entries = read_cache(); // 从.dircache/index文件读取已保存的catch_entry信息,载入到内存
fd = open(".dircache/index", O_RDONLY);
fstat(fd, &st);
size = st.st_size;
map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); // 映射文件内容到内存
hdr = map;
verify_hdr(hdr, size); // 校验cache_header中字段signature/version/sha1
// 首先校验cache_header中signature字段和version字段
// 然后取文件中cache_header前三个字段和所有cache_entry数据的hash值
// 最后将此hash值与cache_header中sha1字段校验
SHA1_Update(&c, hdr, offsetof(struct cache_header, sha1));
SHA1_Update(&c, hdr+1, size - sizeof(*hdr));
memcmp(sha1, hdr->sha1, 20);
active_nr = hdr->entries; // 将.dircache/index中保存的cache_entry个数读入内存
active_alloc = alloc_nr(active_nr);
active_cache = calloc(active_alloc, sizeof(struct cache_entry *));
offset = sizeof(*hdr);
for (i = 0; i < hdr->entries; i++) // 将每个cache_entry地址值读入指针数组
struct cache_entry *ce = map + offset;
offset = offset + ce_size(ce);
active_cache[i] = ce;
return active_nr;
newfd = open(".dircache/index.lock", O_RDWR | O_CREAT | O_EXCL, 0600);
for (i = 1 ; i < argc; i++)
char *path = argv[i];
// 处理每一个源数据文件,创建并写入压缩数据到对应的sha1文件
// 然后将catch_entry信息插入到catch_entry列表中
add_file_to_cache(path)
fd = open(path, O_RDONLY);
if (errno == ENOENT)
// 如果源文件的修改是被删除,则从cache_entry列表中删除
return remove_file_from_cache(path);
// 根据文件路径名查找在cache_entry列表中的索引位置
// cache_entry列表按照路径名升序排列,通过二分法查找
int pos = cache_name_pos(path, strlen(path));
active_nr--;
// 将cache_entry列表中从待删除到最后的所有catch_entry向前移动一个位置
memmove(active_cache + pos, active_cache + pos + 1,
(active_nr - pos - 1) * sizeof(struct cache_entry *));
fstat(fd, &st);
namelen = strlen(path);
size = cache_entry_size(namelen); // 分配catch_entry空间,填充除sha1外其它字段
ce = malloc(size);
ce->ctime.sec = st.st_ctime;
ce->ctime.nsec = st.st_ctim.tv_nsec;
ce->mtime.sec = st.st_mtime;
ce->mtime.nsec = st.st_mtim.tv_nsec;
ce->st_dev = st.st_dev;
ce->st_ino = st.st_ino;
ce->st_mode = st.st_mode;
ce->st_uid = st.st_uid;
ce->st_gid = st.st_gid;
ce->st_size = st.st_size;
ce->namelen = namelen;
memcpy(ce->name, path, namelen);
// 填充catch_entry中sha1字段,然后创建并将压缩数据写入sha1文件
index_fd(path, namelen, ce, fd, &st);
void *in = mmap(NULL, st->st_size, PROT_READ, MAP_PRIVATE, fd, 0);
stream.next_in = metadata;
// 将字符串 "blob %lu/0" 压缩
stream.avail_in = 1+sprintf(metadata, "blob %lu", st_size);
stream.next_out = out;
stream.avail_out = max_out_bytes;
stream.next_in = in; // 将源数据文件内容压缩
stream.avail_in = st->st_size;
SHA1_Init(&c);
SHA1_Update(&c, out, stream.total_out);
SHA1_Final(ce->sha1, &c); // 将两部分压缩内容取hash值,填充sha1字段
// 创建并将压缩数据写入sha1文件
write_sha1_buffer(ce->sha1, out, stream.total_out);
// 根据hash值生成.dircache/objects/xx/xx...(38位)文件名
char *filename = sha1_file_name(sha1);
fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
write(fd, buf, size);
close(fd);
add_cache_entry(ce); // 将新建catch_entry加入到列表中
// 二分法查找catch_entry将要插入的位置
pos = cache_name_pos(ce->name, ce->namelen);
if (pos < 0) // 如果源文件对应的catch_entry已存在,则用新的catch_entry替换
active_cache[-pos-1] = ce;
return;
active_nr++; // catch_entry有效个数+1
// 将catch_entry列表中从待插入位置到最后的数据全部后移一个位置
memmove(active_cache + pos + 1, active_cache + pos,
(active_nr - pos - 1) * sizeof(ce));
active_cache[pos] = ce; // 将新catch_entry插入到列表中
// 将catch_entry列表信息写入到.dircache/index.lock文件
write_cache(newfd, active_cache, active_nr);
hdr.signature = CACHE_SIGNATURE; //0x44495243
hdr.version = 1;
hdr.entries = active_nr; // 填充cache_header前三个字段
SHA1_Init(&c);
// cache_header前三个字段取hash值
SHA1_Update(&c, &hdr, offsetof(struct cache_header, sha1));
for (i = 0; i < entries; i++) // catch_entry列表中所有catch_entry数据取hash值
struct cache_entry *ce = cache[i];
int size = ce_size(ce);
SHA1_Update(&c, ce, size);
SHA1_Final(hdr.sha1, &c); // 将两部分数据的hash值填充到cache_header的sha1字段
write(newfd, &hdr, sizeof(hdr)); // 将cache_header数据写入文件
for (i = 0; i < active_nr; i++) // 将所有cache_entry信息写入文件
struct cache_entry *ce = cache[i];
int size = ce_size(ce);
write(newfd, ce, size);
rename(".dircache/index.lock", ".dircache/index") // 重命名文件为.dircache/index
}