git中commit文件格式


一、git內部objects文件的格式

在執行git cat-file命令的時候,可以看到提示文件的類型可以有四種:blob、tree、commit和tag。其中的blob和tree是比較直觀的概念,也是比較常用的概念,tag相對更加簡單,而這個commit是通常被忽視的一種類型。但是這種類型恰好又是整個版本控制結構中最為關鍵和基礎的一個概念。在git的說明中,對於這種類型文件的格式說明並不太多,所以還是簡單看下commit類型文件的格式是怎樣的的,它是如何處理擴展性、兼容性問題的(添加一個新的字段之后如何處理)?
tsecer@harry: git cat-file
usage: git cat-file (-t [--allow-unknown-type] | -s [--allow-unknown-type] | -e | -p | <type> | --textconv | --filters) [--path=<path>] <object>
or: git cat-file (--batch | --batch-check) [--follow-symlinks] [--textconv | --filters]

<type> can be one of: blob, tree, commit, tag

二、git內部objects(.git/objects/文件夾下文件)格式

從代碼上看,它包含了cat-file中枚舉的常見類型:
git-master\cache.h
/*
* Values in this enum (except those outside the 3 bit range) are part
* of pack file format. See Documentation/technical/pack-format.txt
* for more information.
*/
enum object_type {
OBJ_BAD = -1,
OBJ_NONE = 0,
OBJ_COMMIT = 1,
OBJ_TREE = 2,
OBJ_BLOB = 3,
OBJ_TAG = 4,
/* 5 for future expansion */
OBJ_OFS_DELTA = 6,
OBJ_REF_DELTA = 7,
OBJ_ANY,
OBJ_MAX
};

三、文件如何識別自己格式

也就是在文件的最開始以字符串的形式保存了文件類型。這里的loose是相對於pack格式,pack格式使用的不是字符串而是二進制格式。但是,也是在文件的最開始保存了文件的格式信息。
git-master\sha1-file.c
int unpack_loose_header(git_zstream *stream,
unsigned char *map, unsigned long mapsize,
void *buffer, unsigned long bufsiz)
{
int status = unpack_loose_short_header(stream, map, mapsize,
buffer, bufsiz);

if (status < Z_OK)
return status;

/* Make sure we have the terminating NUL */
if (!memchr(buffer, '\0', stream->next_out - (unsigned char *)buffer))
return -1;
return 0;
}

從網上找到的展示文件內容的命令可以看到
tsecer@harry: python -c "import zlib,sys;sys.stdout.write(zlib.decompress(sys.stdin.read()))" < .git/objects/d4/bf73b80b80f1fa62b397066854ce812fe9b338
commit 184tree 2cbe7cd574af6dec02c3ac609cb8845746307f73
author tsecer <tsecer@harry> 1640590880 +0800
committer tsecer <tsecer@harry> 1640590880 +0800

tsecer add commit
tsecer@harry:

四、從字符串到枚舉類型的轉換

將字符串格式內容轉換為內部枚舉類型格式的代碼為
git-master\object.c
static const char *object_type_strings[] = {
NULL, /* OBJ_NONE = 0 */
"commit", /* OBJ_COMMIT = 1 */
"tree", /* OBJ_TREE = 2 */
"blob", /* OBJ_BLOB = 3 */
"tag", /* OBJ_TAG = 4 */
};

const char *type_name(unsigned int type)
{
if (type >= ARRAY_SIZE(object_type_strings))
return NULL;
return object_type_strings[type];
}

int type_from_string_gently(const char *str, ssize_t len, int gentle)
{
int i;

if (len < 0)
len = strlen(str);

for (i = 1; i < ARRAY_SIZE(object_type_strings); i++)
if (!strncmp(str, object_type_strings[i], len) &&
object_type_strings[i][len] == '\0')
return i;

if (gentle)
return -1;

die(_("invalid object type \"%s\""), str);
}

五、commit文件的格式

從這種解析來看,它更像是一種json文本的描述格式。開始的tree屬性是必須的,然后有可選的parent字段,之后可選的提交者信息,也主要是文本格式保存。
git-master\commit.c
int parse_commit_buffer(struct repository *r, struct commit *item, const void *buffer, unsigned long size, int check_graph)
{
……
if (tail <= bufptr + tree_entry_len + 1 || memcmp(bufptr, "tree ", 5) ||
bufptr[tree_entry_len] != '\n')
return error("bogus commit object %s", oid_to_hex(&item->object.oid));
……
//父節點可能有多個
while (bufptr + parent_entry_len < tail && !memcmp(bufptr, "parent ", 7)) {
……
}
……
item->date = parse_commit_date(bufptr, tail);
……
}

static timestamp_t parse_commit_date(const char *buf, const char *tail)
{
const char *dateptr;

if (buf + 6 >= tail)
return 0;
if (memcmp(buf, "author", 6))
return 0;
while (buf < tail && *buf++ != '\n')
/* nada */;
if (buf + 9 >= tail)
return 0;
if (memcmp(buf, "committer", 9))
return 0;
while (buf < tail && *buf++ != '>')
/* nada */;
if (buf >= tail)
return 0;
dateptr = buf;
while (buf < tail && *buf++ != '\n')
/* nada */;
if (buf >= tail)
return 0;
/* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
return parse_timestamp(dateptr, NULL, 10);
}

六、順便看下tree文件的格式

git-master\tree-walk.c文件描述了文件格式。可以看到,這里並沒有保存文件的修改時間之類的參數,但是有文件是否可執行之類的mode字段。這也意味着,當使用git checkout文件的時候,文件的修改時間不會改變,使用make之類依賴文件時間戳的構件系統也不會導致依賴丟失。
static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned long size, struct strbuf *err)
{
const char *path;
unsigned int mode, len;
const unsigned hashsz = the_hash_algo->rawsz;

if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {
strbuf_addstr(err, _("too-short tree object"));
return -1;
}

path = get_mode(buf, &mode);
if (!path) {
strbuf_addstr(err, _("malformed mode in tree entry"));
return -1;
}
if (!*path) {
strbuf_addstr(err, _("empty filename in tree entry"));
return -1;
}
len = strlen(path) + 1;

/* Initialize the descriptor entry */
desc->entry.path = path;
desc->entry.mode = canon_mode(mode);
desc->entry.pathlen = len - 1;
hashcpy(desc->entry.oid.hash, (const unsigned char *)path + len);

return 0;
}

七、git文件格式描述

網上關於該文件的描述,的確是linus的風格——更偏向使用文本格式描述。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM