git的write-tree命令处理逻辑

本文深入解析Git中write-tree命令的工作原理,详细介绍了如何从缓存区创建快照,包括内存分配、信息写入及SHA1校验过程。

git的write-tree命令的执行入口main(write-tree.c):

30 #define ORIG_OFFSET (40)        /* Enough space to add the header of "tree <size>\0" */
31 
32 int main(int argc, char **argv)
33 {
34         unsigned long size, offset, val;
35         int i, entries = read_cache();
36         char *buffer;
37 
38         if (entries <= 0) {
39                 fprintf(stderr, "No file-cache to create a tree of\n");
40                 exit(1);
41         }
42 
43         /* Guess at an initial size */
44         size = entries * 40 + 400;
45         buffer = malloc(size);
46         offset = ORIG_OFFSET;
47 
48         for (i = 0; i < entries; i++) {
49                 struct cache_entry *ce = active_cache[i];
50                 if (check_valid_sha1(ce->sha1) < 0)
51                         exit(1);
52                 if (offset + ce->namelen + 60 > size) {
53                         size = alloc_nr(offset + ce->namelen + 60);
54                         buffer = realloc(buffer, size);
55                 }
56                 offset += sprintf(buffer + offset, "%o %s", ce->st_mode, ce->name);
57                 buffer[offset++] = 0;
58                 memcpy(buffer + offset, ce->sha1, 20);
59                 offset += 20;
60         }
61 
62         i = prepend_integer(buffer, offset - ORIG_OFFSET, ORIG_OFFSET);
63         i -= 5;
64         memcpy(buffer+i, "tree ", 5);
65 
66         buffer += i;
67         offset -= i;
68 
69         write_sha1_file(buffer, offset);
70         return 0;
71 }

git的write-tree命令把cache区域中当前信息保存下来, 相当于快照功能.Line35:41获取当前cache区域的信息,如果cache区域为空,视为错误,直接返回. Line43:46动态分配一个预估的内存空间buffer,用来保存cache相关信息.Line48:60循环遍历active_cache中元素,将元素相关信息写入分配的地址空间,对于每个cache_entry对象,保存了文件模式,文件名和文件内容的sha1.Line50调用函数check_valid_sha1校验cache_entry对象对应的文件,该函数的实现为(write-tree.c):

 8 static int check_valid_sha1(unsigned char *sha1)
 9 {
10         char *filename = sha1_file_name(sha1);
11         int ret;
12 
13         /* If we were anal, we'd check that the sha1 of the contents actually matches */
14         ret = access(filename, R_OK);
15         if (ret)
16                 perror(filename);
17         return ret;
18 }

Line10调用函数sha1_file_name获得该sha1对应的文件名.Line14通过系统调用access来测试文件是否可读.严格来讲,应该读取文件内容,校验文件内容的sha1.

回到函数main,Line52:55如果之前分配的动态内存空间不足,不能再容纳cache_entry对象信息,则进行动态空间扩容.Line56:59将cache_entry对象的文件模式,文件名以及文件内容的sha1写入buffer.注意动态内存buffer头部预留了ORIG_OFFSET字节用来存储元数据.元数据包括类型字符串(这里是tree)和写入的cache信息大小.Line62调用函数prepend_integer将cache信息大小写入buffer.该函数的实现为(write-tree.c):

20 static int prepend_integer(char *buffer, unsigned val, int i)
21 {
22         buffer[--i] = '\0';
23         do {
24                 buffer[--i] = '0' + (val % 10);
25                 val /= 10;
26         } while (val);
27         return i;
28 }

这个函数需要注意的两点是:val值是以字符的形式写入buffer的,写入buffer时,是从后往前写入的.该函数返回buffer头部空闲字节的长度.

回到函数main,Line63:64将buffer头部空闲字节长度减少5,用来写入类型字符串.同样是写在buffer预留空间的尾部.Line66:67将buffer指针指向有效数据的起始位置,同时更新有效数据的长度.Line69调用函数write_sha1_file将cache快照信息写入文件对象.

def srctree_hash_files_(d, srcdir=None): import shutil import subprocess import tempfile s_dir = srcdir or d.getVar('WORKONSRC') git_dir = None try: git_dir = os.path.join(s_dir, subprocess.check_output(['git', '-C', s_dir, 'rev-parse', '--git-dir'], stderr=subprocess.DEVNULL).decode("utf-8").rstrip()) except subprocess.CalledProcessError: pass ret = " " if git_dir is not None: oe_hash_file = os.path.join(git_dir, 'oe-devtool-tree-sha1') with tempfile.NamedTemporaryFile(prefix='oe-devtool-index') as tmp_index: # Clone index shutil.copyfile(os.path.join(git_dir, 'index'), tmp_index.name) # Update our custom index env = os.environ.copy() env['GIT_INDEX_FILE'] = tmp_index.name subprocess.check_output(['git', 'add', '-A', '.'], cwd=s_dir, env=env) sha1 = subprocess.check_output(['git', 'write-tree'], cwd=s_dir, env=env).decode("utf-8") with open(oe_hash_file, 'w') as fobj: fobj.write(sha1) ret = oe_hash_file + ':True' else: ret = s_dir + '/*:True' return ret def srctree_configure_hash_files_(d): """ Get the list of files that should trigger do_configure to re-execute, based on the value of CONFIGURE_FILES """ in_files = (d.getVar('CONFIGURE_FILES') or '').split() out_items = [] search_files = [] for entry in in_files: if entry.startswith('/'): out_items.append('%s:%s' % (entry, os.path.exists(entry))) else: search_files.append(entry) if search_files: s_dir = d.getVar('WORKONSRC') for root, _, files in os.walk(s_dir): for f in files: if f in search_files: out_items.append('%s:True' % os.path.join(root, f)) return ' '.join(out_items)
09-17
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值