GIT 源码阅读之 update-cache

说明

本文所有代码基于 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 信息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值