GIT 源码阅读之 update-cache

来源:互联网 发布:python中文乱码解决sys 编辑:程序博客网 时间:2024/05/16 14:08

说明

本文所有代码基于 GIT COMMIT e83c5163316f89bfbde7d9ab23ca2e25604af290 版本的源码,在 Ubuntu 16.04 上编译运行,部分代码有所改动。

update-cache

update-cache 是 GIT 最初版本中用于向缓存目录增加追踪文件的命令,该命令不能用于将目录添加至缓存,其使用方法为 update-cache <filename ...> 。主函数如下所示:

int main(int argc, char **argv){        int i, newfd, entries;        entries = read_cache();        if (entries < 0) {                perror("cache corrupted");                return -1;        }        newfd = open(".dircache/index.lock", O_RDWR | O_CREAT | O_EXCL, 0600);        if (newfd < 0) {                perror("unable to create new cachefile");                return -1;        }        for (i = 1; i < argc; i++) {                char *path = argv[i];                if (!verify_path(path)) {                        fprintf(stderr, "Ignoring path %s\n", argv[i]);                        continue;                }                if (add_file_to_cache(path)) {                        fprintf(stderr, "Unable to add %s to database\n", path);                        goto out;                }        }        if (!write_cache(newfd, active_cache, active_nr) && !rename(".dircache/index.lock", ".dircache/index"))                return 0;    out:        unlink(".dircache/index.lock");}

程序运行时,首先会通过 read_cache() 函数加载存储在 .dircache/index 中的文件信息。接着程序会在 .dircache 目录下新建 index.lock 文件用于保存当前文件的缓存信息。然后通过 add_file_to_cache() 函数将命令行中指定的文件添加到缓存对象中。最后通过 write_cache() 函数将缓存信息写入到 .dircache/index.lock 文件中,并重命名为 .dircache/index 完成添加文件到缓存中的功能。
接下来我们看看 read_cache() 函数,该函数负责将存储在 .dircache/index 中的文件信息载入内存并保存至全局变量 active_cache 数组中。因此,我们需要先了解 .dircache/index 的文件结果。 .dircache/index 在文件头部会存放文件实例的信息 cache_header ,其定义如下:

struct cache_header {        unsigned int signature;        unsigned int version;        unsigned int entries;        unsigned char sha1[20];};
  • signature 签名标识,固定为 0x44495243 ,即 DIRC 的十六进制形式。
  • version 版本标识。
  • entries 文件总个数。
  • sha1 校验信息,index 文件中去除 sha1 自身的所有数据的 SHA1 值。

read_cache() 函数源码如下所示:

int read_cache(void){        int fd, i;        struct stat st;        unsigned long size, offset;        void *map;        struct cache_header *hdr;        errno = EBUSY; /* 有什么用?*/        if (active_cache)                return error("more than on cachefile");        errno = ENOENT;        sha1_file_directory = getenv(DB_ENVIRONMENT);        if (!sha1_file_directory)                sha1_file_directory = DEFAULT_DB_ENVIRONMENT;        if (access(sha1_file_directory, X_OK) < 0)                return error("no access to SHA1 file directory");        fd = open(".dircache/index", O_RDONLY);        if (fd < 0)                return (errno == ENOENT) ? 0 : error("open failed");        map = (void *)-1;        if (!fstat(fd, &st)) {                map = NULL;                size = st.st_size;                errno = EINVAL;                if (size > sizeof(struct cache_header))                        map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);        }        close(fd);        if (-1 == (int)(long)map)                return error("mmap failed");        hdr = map;        if (verify_hdr(hdr, size) < 0)                goto unmap;        active_nr = hdr->entries;        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++) {                struct cache_entry *ce = map + offset;                offset = offset + ce_size(ce);                active_cache[i] = ce;        }        return active_nr;    unmap:        munmap(map, size);        errno = EINVAL;        return error("verify header failed");}

该函数中需要设置 errno 的值,关于这些值有什么作用我个人并不是很清楚。 read_cache() 函数首先会检测是否已经载入过 .dircache/index 到 active_cache 中,同时需要判断能否访问 .dircache/objects 目录。接着,该函数通过 mmap() 函数将 .dircache/index 映射到内存中并通过 verify_hdr() 校验信息的正确性。最后将 .dircache/index 文件实例依次载入到 active_cache 数组中。
add_file_to_cache() 函数负责将文件添加到缓存 active_cache 数组中,其函数定义如下:

static int add_file_to_cache(char *path){        int size, namelen;        struct cache_entry *ce;        struct stat st;        int fd;        fd = open(path, O_RDONLY);        if (fd < 0) {                if (errno == ENOENT)                        return remove_file_from_cache(path);                return -1;        }        if (fstat(fd, &st) < 0) {                close(fd);                return -1;        }        namelen = strlen(path);        size = cache_entry_size(namelen);        ce = malloc(size);        memset(ce, 0, size);        memcpy(ce->name, path, namelen);        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;        if (index_fd(path, namelen, ce, fd, &st) < 0)                return -1;        return add_cache_entry(ce);}

该函数接收一个文件路径参数,若该文件不存在,它会试图将 active_cache 中相同路径的文件信息移除。否则它将创建并填充 cache_entry 结构,同时将文件进行压缩并存储到压缩后的 SHA1 值对应的文件中(备注:在压缩前加入了 blob 字符串,size 为文件大小)。最后,通过将新建的 cache_entry 信息加入到 active_cache 中(根据路径名进行快速排序)。

write_cache() 函数负责将内存中 active_cache 的内容写入到磁盘文件中,其定义如下:

static int write_cache(int newfd, struct cache_entry **cache, int entries){        SHA_CTX c;        struct cache_header hdr;        int i;        hdr.signature = CACHE_SIGNATURE;        hdr.version = 1;        hdr.entries = entries;        SHA1_Init(&c);        SHA1_Update(&c, &hdr, offsetof(struct cache_header, sha1));        for (i = 0; i < entries; i++) {                struct cache_entry *ce = cache[i];                int size = ce_size(ce);                SHA1_Update(&c, ce, size);        }        SHA1_Final(hdr.sha1, &c);        if (write(newfd, &hdr, sizeof(hdr)) != sizeof(hdr))                return -1;        for (i = 0; i < entries; i++) {                struct cache_entry *ce = cache[i];                int size = ce_size(ce);                if (write(newfd, ce, size) != size)                        return -1;        }        return 0;}

该函数对 cache_header 中除 sha1 字段以及 active_cache 所有的 cache_entry 计算 SHA1 值并保存至 cache_headersha1 字段中。接着将头信息写入文件,最后遍历 active_cache 并写入所有的 cache_entry 信息。