git
/
tree.c
222 строки · 5.2 Кб
1#define USE_THE_REPOSITORY_VARIABLE2
3#include "git-compat-util.h"4#include "hex.h"5#include "tree.h"6#include "object-name.h"7#include "object-store-ll.h"8#include "commit.h"9#include "alloc.h"10#include "tree-walk.h"11#include "repository.h"12#include "environment.h"13
14const char *tree_type = "tree";15
16int read_tree_at(struct repository *r,17struct tree *tree, struct strbuf *base,18int depth,19const struct pathspec *pathspec,20read_tree_fn_t fn, void *context)21{
22struct tree_desc desc;23struct name_entry entry;24struct object_id oid;25int len, oldlen = base->len;26enum interesting retval = entry_not_interesting;27
28if (depth > max_allowed_tree_depth)29return error("exceeded maximum allowed tree depth");30
31if (parse_tree(tree))32return -1;33
34init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);35
36while (tree_entry(&desc, &entry)) {37if (retval != all_entries_interesting) {38retval = tree_entry_interesting(r->index, &entry,39base, pathspec);40if (retval == all_entries_not_interesting)41break;42if (retval == entry_not_interesting)43continue;44}45
46switch (fn(&entry.oid, base,47entry.path, entry.mode, context)) {48case 0:49continue;50case READ_TREE_RECURSIVE:51break;52default:53return -1;54}55
56if (S_ISDIR(entry.mode))57oidcpy(&oid, &entry.oid);58else if (S_ISGITLINK(entry.mode)) {59struct commit *commit;60
61commit = lookup_commit(r, &entry.oid);62if (!commit)63die("Commit %s in submodule path %s%s not found",64oid_to_hex(&entry.oid),65base->buf, entry.path);66
67if (repo_parse_commit(r, commit))68die("Invalid commit %s in submodule path %s%s",69oid_to_hex(&entry.oid),70base->buf, entry.path);71
72oidcpy(&oid, get_commit_tree_oid(commit));73}74else75continue;76
77len = tree_entry_len(&entry);78strbuf_add(base, entry.path, len);79strbuf_addch(base, '/');80retval = read_tree_at(r, lookup_tree(r, &oid),81base, depth + 1, pathspec,82fn, context);83strbuf_setlen(base, oldlen);84if (retval)85return -1;86}87return 0;88}
89
90int read_tree(struct repository *r,91struct tree *tree,92const struct pathspec *pathspec,93read_tree_fn_t fn, void *context)94{
95struct strbuf sb = STRBUF_INIT;96int ret = read_tree_at(r, tree, &sb, 0, pathspec, fn, context);97strbuf_release(&sb);98return ret;99}
100
101int base_name_compare(const char *name1, size_t len1, int mode1,102const char *name2, size_t len2, int mode2)103{
104unsigned char c1, c2;105size_t len = len1 < len2 ? len1 : len2;106int cmp;107
108cmp = memcmp(name1, name2, len);109if (cmp)110return cmp;111c1 = name1[len];112c2 = name2[len];113if (!c1 && S_ISDIR(mode1))114c1 = '/';115if (!c2 && S_ISDIR(mode2))116c2 = '/';117return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;118}
119
120/*
121* df_name_compare() is identical to base_name_compare(), except it
122* compares conflicting directory/file entries as equal. Note that
123* while a directory name compares as equal to a regular file, they
124* then individually compare _differently_ to a filename that has
125* a dot after the basename (because '\0' < '.' < '/').
126*
127* This is used by routines that want to traverse the git namespace
128* but then handle conflicting entries together when possible.
129*/
130int df_name_compare(const char *name1, size_t len1, int mode1,131const char *name2, size_t len2, int mode2)132{
133unsigned char c1, c2;134size_t len = len1 < len2 ? len1 : len2;135int cmp;136
137cmp = memcmp(name1, name2, len);138if (cmp)139return cmp;140/* Directories and files compare equal (same length, same name) */141if (len1 == len2)142return 0;143c1 = name1[len];144if (!c1 && S_ISDIR(mode1))145c1 = '/';146c2 = name2[len];147if (!c2 && S_ISDIR(mode2))148c2 = '/';149if (c1 == '/' && !c2)150return 0;151if (c2 == '/' && !c1)152return 0;153return c1 - c2;154}
155
156int name_compare(const char *name1, size_t len1, const char *name2, size_t len2)157{
158size_t min_len = (len1 < len2) ? len1 : len2;159int cmp = memcmp(name1, name2, min_len);160if (cmp)161return cmp;162if (len1 < len2)163return -1;164if (len1 > len2)165return 1;166return 0;167}
168
169struct tree *lookup_tree(struct repository *r, const struct object_id *oid)170{
171struct object *obj = lookup_object(r, oid);172if (!obj)173return create_object(r, oid, alloc_tree_node(r));174return object_as_type(obj, OBJ_TREE, 0);175}
176
177int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size)178{
179if (item->object.parsed)180return 0;181item->object.parsed = 1;182item->buffer = buffer;183item->size = size;184
185return 0;186}
187
188int parse_tree_gently(struct tree *item, int quiet_on_missing)189{
190enum object_type type;191void *buffer;192unsigned long size;193
194if (item->object.parsed)195return 0;196buffer = repo_read_object_file(the_repository, &item->object.oid,197&type, &size);198if (!buffer)199return quiet_on_missing ? -1 :200error("Could not read %s",201oid_to_hex(&item->object.oid));202if (type != OBJ_TREE) {203free(buffer);204return error("Object %s not a tree",205oid_to_hex(&item->object.oid));206}207return parse_tree_buffer(item, buffer, size);208}
209
210void free_tree_buffer(struct tree *tree)211{
212FREE_AND_NULL(tree->buffer);213tree->size = 0;214tree->object.parsed = 0;215}
216
217struct tree *parse_tree_indirect(const struct object_id *oid)218{
219struct repository *r = the_repository;220struct object *obj = parse_object(r, oid);221return (struct tree *)repo_peel_to_type(r, NULL, 0, obj, OBJ_TREE);222}
223