X-Git-Url: https://git.octo.it/?p=git.git;a=blobdiff_plain;f=builtin-read-tree.c;h=04506da892c1bfb198c706e166efe8a2bf6cf1be;hp=da0731ca0e3e22dd8cbb9fbde9201c66de406b4a;hb=HEAD;hpb=097dc3d8c32f4b85bf9701d5e1de98999ac25c1c diff --git a/builtin-read-tree.c b/builtin-read-tree.c index da0731ca..04506da8 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -9,6 +9,8 @@ #include "object.h" #include "tree.h" +#include "tree-walk.h" +#include "cache-tree.h" #include #include #include "builtin.h" @@ -22,13 +24,24 @@ static int trivial_merges_only = 0; static int aggressive = 0; static int verbose_update = 0; static volatile int progress_update = 0; +static const char *prefix = NULL; static int head_idx = -1; static int merge_size = 0; static struct object_list *trees = NULL; -static struct cache_entry df_conflict_entry = { +static struct cache_entry df_conflict_entry = { +}; + +struct tree_entry_list { + struct tree_entry_list *next; + unsigned directory : 1; + unsigned executable : 1; + unsigned symlink : 1; + unsigned int mode; + const char *name; + const unsigned char *sha1; }; static struct tree_entry_list df_conflict_list = { @@ -38,6 +51,34 @@ static struct tree_entry_list df_conflict_list = { typedef int (*merge_fn_t)(struct cache_entry **src); +static struct tree_entry_list *create_tree_entry_list(struct tree *tree) +{ + struct tree_desc desc; + struct name_entry one; + struct tree_entry_list *ret = NULL; + struct tree_entry_list **list_p = &ret; + + desc.buf = tree->buffer; + desc.size = tree->size; + + while (tree_entry(&desc, &one)) { + struct tree_entry_list *entry; + + entry = xmalloc(sizeof(struct tree_entry_list)); + entry->name = one.path; + entry->sha1 = one.sha1; + entry->mode = one.mode; + entry->directory = S_ISDIR(one.mode) != 0; + entry->executable = (one.mode & S_IXUSR) != 0; + entry->symlink = S_ISLNK(one.mode) != 0; + entry->next = NULL; + + *list_p = entry; + list_p = &entry->next; + } + return ret; +} + static int entcmp(const char *name1, int dir1, const char *name2, int dir2) { int len1 = strlen(name1); @@ -336,6 +377,7 @@ static void check_updates(struct cache_entry **src, int nr) fprintf(stderr, "%4u%% (%u/%u) done\r", percent, cnt, total); last_percent = percent; + progress_update = 0; } } } @@ -371,7 +413,8 @@ static int unpack_trees(merge_fn_t fn) posns[i] = create_tree_entry_list((struct tree *) posn->item); posn = posn->next; } - if (unpack_trees_rec(posns, len, "", fn, &indpos)) + if (unpack_trees_rec(posns, len, prefix ? prefix : "", + fn, &indpos)) return -1; } @@ -428,6 +471,12 @@ static void verify_uptodate(struct cache_entry *ce) die("Entry '%s' not uptodate. Cannot merge.", ce->name); } +static void invalidate_ce_path(struct cache_entry *ce) +{ + if (ce) + cache_tree_invalidate_path(active_cache_tree, ce->name); +} + /* * We do not want to remove or overwrite a working tree file that * is not tracked. @@ -458,10 +507,13 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old) *merge = *old; } else { verify_uptodate(old); + invalidate_ce_path(old); } } - else + else { verify_absent(merge->name, "overwritten"); + invalidate_ce_path(merge); + } merge->ce_flags &= ~htons(CE_STAGEMASK); add_cache_entry(merge, ADD_CACHE_OK_TO_ADD); @@ -476,6 +528,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old) verify_absent(ce->name, "removed"); ce->ce_mode = 0; add_cache_entry(ce, ADD_CACHE_OK_TO_ADD); + invalidate_ce_path(ce); return 1; } @@ -711,6 +764,28 @@ static int twoway_merge(struct cache_entry **src) } /* + * Bind merge. + * + * Keep the index entries at stage0, collapse stage1 but make sure + * stage0 does not have anything there. + */ +static int bind_merge(struct cache_entry **src) +{ + struct cache_entry *old = src[0]; + struct cache_entry *a = src[1]; + + if (merge_size != 1) + return error("Cannot do a bind merge of %d trees\n", + merge_size); + if (a && old) + die("Entry '%s' overlaps. Cannot bind.", a->name); + if (!a) + return keep_entry(old); + else + return merged_entry(a, NULL); +} + +/* * One-way merge. * * The rule is: @@ -741,29 +816,68 @@ static int oneway_merge(struct cache_entry **src) static int read_cache_unmerged(void) { - int i, deleted; + int i; struct cache_entry **dst; + struct cache_entry *last = NULL; read_cache(); dst = active_cache; - deleted = 0; for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; if (ce_stage(ce)) { - deleted++; - continue; + if (last && !strcmp(ce->name, last->name)) + continue; + invalidate_ce_path(ce); + last = ce; + ce->ce_mode = 0; + ce->ce_flags &= ~htons(CE_STAGEMASK); } - if (deleted) - *dst = ce; - dst++; + *dst++ = ce; } - active_nr -= deleted; - return deleted; + active_nr = dst - active_cache; + return !!last; } -static const char read_tree_usage[] = "git-read-tree ( | -m [--aggressive] [-u | -i] [ []])"; +static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree) +{ + struct tree_desc desc; + struct name_entry entry; + int cnt; + + memcpy(it->sha1, tree->object.sha1, 20); + desc.buf = tree->buffer; + desc.size = tree->size; + cnt = 0; + while (tree_entry(&desc, &entry)) { + if (!S_ISDIR(entry.mode)) + cnt++; + else { + struct cache_tree_sub *sub; + struct tree *subtree = lookup_tree(entry.sha1); + if (!subtree->object.parsed) + parse_tree(subtree); + sub = cache_tree_sub(it, entry.path); + sub->cache_tree = cache_tree(); + prime_cache_tree_rec(sub->cache_tree, subtree); + cnt += sub->cache_tree->entry_count; + } + } + it->entry_count = cnt; +} -static struct cache_file cache_file; +static void prime_cache_tree(void) +{ + struct tree *tree = (struct tree *)trees->item; + if (!tree) + return; + active_cache_tree = cache_tree(); + prime_cache_tree_rec(active_cache_tree, tree); + +} + +static const char read_tree_usage[] = "git-read-tree ( | [[-m [--aggressive] | --reset | --prefix=] [-u | -i]] [ []])"; + +static struct lock_file lock_file; int cmd_read_tree(int argc, const char **argv, char **envp) { @@ -774,9 +888,9 @@ int cmd_read_tree(int argc, const char **argv, char **envp) setup_git_directory(); git_config(git_default_config); - newfd = hold_index_file_for_update(&cache_file, get_index_file()); + newfd = hold_lock_file_for_update(&lock_file, get_index_file()); if (newfd < 0) - die("unable to create new cachefile"); + die("unable to create new index file"); git_config(git_default_config); @@ -806,9 +920,27 @@ int cmd_read_tree(int argc, const char **argv, char **envp) continue; } - /* This differs from "-m" in that we'll silently ignore unmerged entries */ + /* "--prefix=/" means keep the current index + * entries and put the entries from the tree under the + * given subdirectory. + */ + if (!strncmp(arg, "--prefix=", 9)) { + if (stage || merge || prefix) + usage(read_tree_usage); + prefix = arg + 9; + merge = 1; + stage = 1; + if (read_cache_unmerged()) + die("you need to resolve your current index first"); + continue; + } + + /* This differs from "-m" in that we'll silently ignore + * unmerged entries and overwrite working tree files that + * correspond to them. + */ if (!strcmp(arg, "--reset")) { - if (stage || merge) + if (stage || merge || prefix) usage(read_tree_usage); reset = 1; merge = 1; @@ -829,7 +961,7 @@ int cmd_read_tree(int argc, const char **argv, char **envp) /* "-m" stands for "merge", meaning we start in stage 1 */ if (!strcmp(arg, "-m")) { - if (stage || merge) + if (stage || merge || prefix) usage(read_tree_usage); if (read_cache_unmerged()) die("you need to resolve your current index first"); @@ -851,21 +983,39 @@ int cmd_read_tree(int argc, const char **argv, char **envp) if ((update||index_only) && !merge) usage(read_tree_usage); + if (prefix) { + int pfxlen = strlen(prefix); + int pos; + if (prefix[pfxlen-1] != '/') + die("prefix must end with /"); + if (stage != 2) + die("binding merge takes only one tree"); + pos = cache_name_pos(prefix, pfxlen); + if (0 <= pos) + die("corrupt index file"); + pos = -pos-1; + if (pos < active_nr && + !strncmp(active_cache[pos]->name, prefix, pfxlen)) + die("subdirectory '%s' already exists.", prefix); + pos = cache_name_pos(prefix, pfxlen-1); + if (0 <= pos) + die("file '%.*s' already exists.", pfxlen-1, prefix); + } + if (merge) { if (stage < 2) die("just how do you expect me to merge %d trees?", stage-1); switch (stage - 1) { case 1: - fn = oneway_merge; + fn = prefix ? bind_merge : oneway_merge; break; case 2: fn = twoway_merge; break; case 3: - fn = threeway_merge; - break; default: fn = threeway_merge; + cache_tree_free(&active_cache_tree); break; } @@ -876,8 +1026,20 @@ int cmd_read_tree(int argc, const char **argv, char **envp) } unpack_trees(fn); + + /* + * When reading only one tree (either the most basic form, + * "-m ent" or "--reset ent" form), we can obtain a fully + * valid cache-tree because the index must match exactly + * what came from the tree. + */ + if (trees && trees->item && !prefix && (!merge || (stage == 2))) { + cache_tree_free(&active_cache_tree); + prime_cache_tree(); + } + if (write_cache(newfd, active_cache, active_nr) || - commit_index_file(&cache_file)) + commit_lock_file(&lock_file)) die("unable to write new index file"); return 0; }