From 67ac1e1d57e45899a4dfd900a0249f48507584b5 Mon Sep 17 00:00:00 2001 From: Jonathan Nieder Date: Fri, 10 Dec 2010 18:51:44 -0600 Subject: cherry-pick/revert: add support for -X/--strategy-option For example, this would allow cherry-picking or reverting patches from a piece of history with a different end-of-line style, like so: $ git revert -Xrenormalize old-problematic-commit Currently that is possible with manual use of merge-recursive but the cherry-pick/revert porcelain does not expose the functionality. While at it, document the existing support for --strategy. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- builtin/merge.c | 6 ++++-- builtin/revert.c | 29 ++++++++++++++++++++++------- 2 files changed, 26 insertions(+), 9 deletions(-) (limited to 'builtin') diff --git a/builtin/merge.c b/builtin/merge.c index 42fff387e6..9403747b6a 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -582,7 +582,8 @@ static void write_tree_trivial(unsigned char *sha1) die("git write-tree failed to write a tree"); } -int try_merge_command(const char *strategy, struct commit_list *common, +int try_merge_command(const char *strategy, size_t xopts_nr, + const char **xopts, struct commit_list *common, const char *head_arg, struct commit_list *remotes) { const char **args; @@ -680,7 +681,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, rollback_lock_file(lock); return clean ? 0 : 1; } else { - return try_merge_command(strategy, common, head_arg, remoteheads); + return try_merge_command(strategy, xopts_nr, xopts, + common, head_arg, remoteheads); } } diff --git a/builtin/revert.c b/builtin/revert.c index bb6e9e83b7..dc1b702edc 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -44,7 +44,11 @@ static const char **commit_argv; static int allow_rerere_auto; static const char *me; + +/* Merge strategy. */ static const char *strategy; +static const char **xopts; +static size_t xopts_nr, xopts_alloc; #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" @@ -55,6 +59,17 @@ static const char * const *revert_or_cherry_pick_usage(void) return action == REVERT ? revert_usage : cherry_pick_usage; } +static int option_parse_x(const struct option *opt, + const char *arg, int unset) +{ + if (unset) + return 0; + + ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc); + xopts[xopts_nr++] = xstrdup(arg); + return 0; +} + static void parse_args(int argc, const char **argv) { const char * const * usage_str = revert_or_cherry_pick_usage(); @@ -67,6 +82,8 @@ static void parse_args(int argc, const char **argv) OPT_INTEGER('m', "mainline", &mainline, "parent number"), OPT_RERERE_AUTOUPDATE(&allow_rerere_auto), OPT_STRING(0, "strategy", &strategy, "strategy", "merge strategy"), + OPT_CALLBACK('X', "strategy-option", &xopts, "option", + "option for merge strategy", option_parse_x), OPT_END(), OPT_END(), OPT_END(), @@ -311,18 +328,13 @@ static int do_recursive_merge(struct commit *base, struct commit *next, struct merge_options o; struct tree *result, *next_tree, *base_tree, *head_tree; int clean, index_fd; + const char **xopt; static struct lock_file index_lock; index_fd = hold_locked_index(&index_lock, 1); read_cache(); - /* - * NEEDSWORK: cherry-picking between branches with - * different end-of-line normalization is a pain; - * plumb in an option to set o.renormalize? - * (or better: arbitrary -X options) - */ init_merge_options(&o); o.ancestor = base ? base_label : "(empty tree)"; o.branch1 = "HEAD"; @@ -332,6 +344,9 @@ static int do_recursive_merge(struct commit *base, struct commit *next, next_tree = next ? next->tree : empty_tree(); base_tree = base ? base->tree : empty_tree(); + for (xopt = xopts; xopt != xopts + xopts_nr; xopt++) + parse_merge_opt(&o, *xopt); + clean = merge_trees(&o, head_tree, next_tree, base_tree, &result); @@ -503,7 +518,7 @@ static int do_pick_commit(void) commit_list_insert(base, &common); commit_list_insert(next, &remotes); - res = try_merge_command(strategy, common, + res = try_merge_command(strategy, xopts_nr, xopts, common, sha1_to_hex(head), remotes); free_commit_list(common); free_commit_list(remotes); -- cgit v1.3 From 66f136252fe1998e2c1381c913795b5f56b6dc8c Mon Sep 17 00:00:00 2001 From: Nguyễn Thái Ngọc Duy Date: Wed, 15 Dec 2010 22:02:38 +0700 Subject: Convert struct diff_options to use struct pathspec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/diff-files.c | 2 +- builtin/diff.c | 4 ++-- builtin/log.c | 2 +- diff-lib.c | 2 +- diff-no-index.c | 4 ++-- diff.h | 4 +--- revision.c | 6 +----- tree-diff.c | 48 +++++++++++++----------------------------------- 8 files changed, 22 insertions(+), 50 deletions(-) (limited to 'builtin') diff --git a/builtin/diff-files.c b/builtin/diff-files.c index 951c7c8994..46085f862f 100644 --- a/builtin/diff-files.c +++ b/builtin/diff-files.c @@ -61,7 +61,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) (rev.diffopt.output_format & DIFF_FORMAT_PATCH)) rev.combine_merges = rev.dense_combined_merges = 1; - if (read_cache_preload(rev.diffopt.paths) < 0) { + if (read_cache_preload(rev.diffopt.pathspec.raw) < 0) { perror("read_cache_preload"); return -1; } diff --git a/builtin/diff.c b/builtin/diff.c index 945e7583a8..a08c3240d5 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -135,7 +135,7 @@ static int builtin_diff_index(struct rev_info *revs, revs->max_count != -1 || revs->min_age != -1 || revs->max_age != -1) usage(builtin_diff_usage); - if (read_cache_preload(revs->diffopt.paths) < 0) { + if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) { perror("read_cache_preload"); return -1; } @@ -237,7 +237,7 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv revs->combine_merges = revs->dense_combined_merges = 1; setup_work_tree(); - if (read_cache_preload(revs->diffopt.paths) < 0) { + if (read_cache_preload(revs->diffopt.pathspec.raw) < 0) { perror("read_cache_preload"); return -1; } diff --git a/builtin/log.c b/builtin/log.c index d8c6c28d2f..f5ed690c43 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -89,7 +89,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix, rev->always_show_header = 0; if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) { rev->always_show_header = 0; - if (rev->diffopt.nr_paths != 1) + if (rev->diffopt.pathspec.nr != 1) usage("git logs can only follow renames on one pathname at a time"); } for (i = 1; i < argc; i++) { diff --git a/diff-lib.c b/diff-lib.c index 392ce2bef0..3b809f2e0d 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -501,7 +501,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt) active_nr = dst - active_cache; init_revisions(&revs, NULL); - revs.prune_data = opt->paths; + revs.prune_data = opt->pathspec.raw; tree = parse_tree_indirect(tree_sha1); if (!tree) die("bad tree object %s", sha1_to_hex(tree_sha1)); diff --git a/diff-no-index.c b/diff-no-index.c index e48ab92ba1..3a36144687 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -260,8 +260,8 @@ void diff_no_index(struct rev_info *revs, if (diff_setup_done(&revs->diffopt) < 0) die("diff_setup_done failed"); - if (queue_diff(&revs->diffopt, revs->diffopt.paths[0], - revs->diffopt.paths[1])) + if (queue_diff(&revs->diffopt, revs->diffopt.pathspec.raw[0], + revs->diffopt.pathspec.raw[1])) exit(1); diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/"); diffcore_std(&revs->diffopt); diff --git a/diff.h b/diff.h index 0083d92438..310bd6b283 100644 --- a/diff.h +++ b/diff.h @@ -133,9 +133,7 @@ struct diff_options { FILE *file; int close_file; - int nr_paths; - const char **paths; - int *pathlens; + struct pathspec pathspec; change_fn_t change; add_remove_fn_t add_remove; diff_format_fn_t format_callback; diff --git a/revision.c b/revision.c index 7b9eaefae4..0c511aa44b 100644 --- a/revision.c +++ b/revision.c @@ -553,11 +553,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs) left_first = left_count < right_count; init_patch_ids(&ids); - if (revs->diffopt.nr_paths) { - ids.diffopts.nr_paths = revs->diffopt.nr_paths; - ids.diffopts.paths = revs->diffopt.paths; - ids.diffopts.pathlens = revs->diffopt.pathlens; - } + ids.diffopts.pathspec = revs->diffopt.pathspec; /* Compute patch-ids for one side */ for (p = list; p; p = p->next) { diff --git a/tree-diff.c b/tree-diff.c index 12c9a88884..f6b4aa4caa 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -102,16 +102,17 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int int pathlen; int never_interesting = -1; - if (!opt->nr_paths) + if (!opt->pathspec.nr) return 2; sha1 = tree_entry_extract(desc, &path, &mode); pathlen = tree_entry_len(path, sha1); - for (i = 0; i < opt->nr_paths; i++) { - const char *match = opt->paths[i]; - int matchlen = opt->pathlens[i]; + for (i = 0; i < opt->pathspec.nr; i++) { + const struct pathspec_item *item = opt->pathspec.items+i; + const char *match = item->match; + int matchlen = item->len; int m = -1; /* signals that we haven't called strncmp() */ if (baselen >= matchlen) { @@ -286,7 +287,7 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, stru if (DIFF_OPT_TST(opt, QUICK) && DIFF_OPT_TST(opt, HAS_CHANGES)) break; - if (opt->nr_paths) { + if (opt->pathspec.nr) { if (!all_t1_interesting) skip_uninteresting(t1, base, baselen, opt, &all_t1_interesting); @@ -349,7 +350,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co DIFF_OPT_SET(&diff_opts, RECURSIVE); DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER); diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; - diff_opts.single_follow = opt->paths[0]; + diff_opts.single_follow = opt->pathspec.raw[0]; diff_opts.break_opt = opt->break_opt; paths[0] = NULL; diff_tree_setup_paths(paths, &diff_opts); @@ -369,15 +370,16 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co * diff_queued_diff, we will also use that as the path in * the future! */ - if ((p->status == 'R' || p->status == 'C') && !strcmp(p->two->path, opt->paths[0])) { + if ((p->status == 'R' || p->status == 'C') && + !strcmp(p->two->path, opt->pathspec.raw[0])) { /* Switch the file-pairs around */ q->queue[i] = choice; choice = p; /* Update the path we use from now on.. */ diff_tree_release_paths(opt); - opt->paths[0] = xstrdup(p->one->path); - diff_tree_setup_paths(opt->paths, opt); + opt->pathspec.raw[0] = xstrdup(p->one->path); + diff_tree_setup_paths(opt->pathspec.raw, opt); /* * The caller expects us to return a set of vanilla @@ -452,36 +454,12 @@ int diff_root_tree_sha1(const unsigned char *new, const char *base, struct diff_ return retval; } -static int count_paths(const char **paths) -{ - int i = 0; - while (*paths++) - i++; - return i; -} - void diff_tree_release_paths(struct diff_options *opt) { - free(opt->pathlens); + free_pathspec(&opt->pathspec); } void diff_tree_setup_paths(const char **p, struct diff_options *opt) { - opt->nr_paths = 0; - opt->pathlens = NULL; - opt->paths = NULL; - - if (p) { - int i; - - opt->paths = p; - opt->nr_paths = count_paths(p); - if (opt->nr_paths == 0) { - opt->pathlens = NULL; - return; - } - opt->pathlens = xmalloc(opt->nr_paths * sizeof(int)); - for (i=0; i < opt->nr_paths; i++) - opt->pathlens[i] = strlen(p[i]); - } + init_pathspec(&opt->pathspec, p); } -- cgit v1.3 From afe069d16618190a6f7e84ef8451970e274aedb4 Mon Sep 17 00:00:00 2001 From: Nguyễn Thái Ngọc Duy Date: Fri, 17 Dec 2010 19:43:06 +0700 Subject: struct rev_info: convert prune_data to struct pathspec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/add.c | 2 +- builtin/diff.c | 12 ++++-------- builtin/fast-export.c | 2 +- diff-lib.c | 6 +++--- revision.c | 15 ++++++++------- revision.h | 2 +- wt-status.c | 4 ++-- 7 files changed, 20 insertions(+), 23 deletions(-) (limited to 'builtin') diff --git a/builtin/add.c b/builtin/add.c index 12b964e642..5f817ad77f 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -86,7 +86,7 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags) struct rev_info rev; init_revisions(&rev, prefix); setup_revisions(0, NULL, &rev, NULL); - rev.prune_data = pathspec; + init_pathspec(&rev.prune_data, pathspec); rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = update_callback; data.flags = flags; diff --git a/builtin/diff.c b/builtin/diff.c index a08c3240d5..d12de8f392 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -371,14 +371,10 @@ int cmd_diff(int argc, const char **argv, const char *prefix) } die("unhandled object '%s' given.", name); } - if (rev.prune_data) { - const char **pathspec = rev.prune_data; - while (*pathspec) { - if (!path) - path = *pathspec; - paths++; - pathspec++; - } + if (rev.prune_data.nr) { + if (!path) + path = rev.prune_data.items[0].match; + paths += rev.prune_data.nr; } /* diff --git a/builtin/fast-export.c b/builtin/fast-export.c index c8fd46b872..ba57457cc5 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -651,7 +651,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix) if (import_filename) import_marks(import_filename); - if (import_filename && revs.prune_data) + if (import_filename && revs.prune_data.nr) full_tree = 1; get_tags_and_duplicates(&revs.pending, &extra_refs); diff --git a/diff-lib.c b/diff-lib.c index 3b809f2e0d..2251f3df68 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -106,7 +106,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option) DIFF_OPT_TST(&revs->diffopt, HAS_CHANGES)) break; - if (!ce_path_match(ce, revs->prune_data)) + if (!ce_path_match(ce, revs->prune_data.raw)) continue; if (ce_stage(ce)) { @@ -427,7 +427,7 @@ static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o) if (tree == o->df_conflict_entry) tree = NULL; - if (ce_path_match(idx ? idx : tree, revs->prune_data)) + if (ce_path_match(idx ? idx : tree, revs->prune_data.raw)) do_oneway_diff(o, idx, tree); return 0; @@ -501,7 +501,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt) active_nr = dst - active_cache; init_revisions(&revs, NULL); - revs.prune_data = opt->pathspec.raw; + init_pathspec(&revs.prune_data, opt->pathspec.raw); tree = parse_tree_indirect(tree_sha1); if (!tree) die("bad tree object %s", sha1_to_hex(tree_sha1)); diff --git a/revision.c b/revision.c index 0c511aa44b..bf87e01e45 100644 --- a/revision.c +++ b/revision.c @@ -323,7 +323,7 @@ static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct * tagged commit by specifying both --simplify-by-decoration * and pathspec. */ - if (!revs->prune_data) + if (!revs->prune_data.nr) return REV_TREE_SAME; } @@ -969,7 +969,7 @@ static void prepare_show_merge(struct rev_info *revs) struct cache_entry *ce = active_cache[i]; if (!ce_stage(ce)) continue; - if (ce_path_match(ce, revs->prune_data)) { + if (ce_path_match(ce, revs->prune_data.raw)) { prune_num++; prune = xrealloc(prune, sizeof(*prune) * prune_num); prune[prune_num-2] = ce->name; @@ -979,7 +979,8 @@ static void prepare_show_merge(struct rev_info *revs) ce_same_name(ce, active_cache[i+1])) i++; } - revs->prune_data = prune; + free_pathspec(&revs->prune_data); + init_pathspec(&revs->prune_data, prune); revs->limited = 1; } @@ -1616,7 +1617,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s } if (prune_data) - revs->prune_data = get_pathspec(revs->prefix, prune_data); + init_pathspec(&revs->prune_data, get_pathspec(revs->prefix, prune_data)); if (revs->def == NULL) revs->def = opt ? opt->def : NULL; @@ -1647,13 +1648,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s if (revs->topo_order) revs->limited = 1; - if (revs->prune_data) { - diff_tree_setup_paths(revs->prune_data, &revs->pruning); + if (revs->prune_data.nr) { + diff_tree_setup_paths(revs->prune_data.raw, &revs->pruning); /* Can't prune commits with rename following: the paths change.. */ if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES)) revs->prune = 1; if (!revs->full_diff) - diff_tree_setup_paths(revs->prune_data, &revs->diffopt); + diff_tree_setup_paths(revs->prune_data.raw, &revs->diffopt); } if (revs->combine_merges) revs->ignore_merges = 0; diff --git a/revision.h b/revision.h index 05659c64ac..82509dd1d9 100644 --- a/revision.h +++ b/revision.h @@ -34,7 +34,7 @@ struct rev_info { /* Basic information */ const char *prefix; const char *def; - void *prune_data; + struct pathspec prune_data; unsigned int early_output; /* Traversal flags */ diff --git a/wt-status.c b/wt-status.c index 123582b6cb..1ea330ed17 100644 --- a/wt-status.c +++ b/wt-status.c @@ -323,7 +323,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s) } rev.diffopt.format_callback = wt_status_collect_changed_cb; rev.diffopt.format_callback_data = s; - rev.prune_data = s->pathspec; + init_pathspec(&rev.prune_data, s->pathspec); run_diff_files(&rev, 0); } @@ -348,7 +348,7 @@ static void wt_status_collect_changes_index(struct wt_status *s) rev.diffopt.detect_rename = 1; rev.diffopt.rename_limit = 200; rev.diffopt.break_opt = 0; - rev.prune_data = s->pathspec; + init_pathspec(&rev.prune_data, s->pathspec); run_diff_index(&rev, 1); } -- cgit v1.3 From eb9cb55b944796374402ab4e2639300dc9b0b409 Mon Sep 17 00:00:00 2001 From: Nguyễn Thái Ngọc Duy Date: Fri, 17 Dec 2010 19:43:07 +0700 Subject: Convert ce_path_match() to use struct pathspec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/update-index.c | 8 ++++++-- cache.h | 2 +- diff-lib.c | 4 ++-- preload-index.c | 5 ++++- read-cache.c | 7 ++++--- revision.c | 2 +- wt-status.c | 5 ++++- 7 files changed, 22 insertions(+), 11 deletions(-) (limited to 'builtin') diff --git a/builtin/update-index.c b/builtin/update-index.c index 56baf27fb7..d7850c6309 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -546,7 +546,10 @@ static int do_reupdate(int ac, const char **av, */ int pos; int has_head = 1; - const char **pathspec = get_pathspec(prefix, av + 1); + const char **paths = get_pathspec(prefix, av + 1); + struct pathspec pathspec; + + init_pathspec(&pathspec, paths); if (read_ref("HEAD", head_sha1)) /* If there is no HEAD, that means it is an initial @@ -559,7 +562,7 @@ static int do_reupdate(int ac, const char **av, struct cache_entry *old = NULL; int save_nr; - if (ce_stage(ce) || !ce_path_match(ce, pathspec)) + if (ce_stage(ce) || !ce_path_match(ce, &pathspec)) continue; if (has_head) old = read_one_ent(NULL, head_sha1, @@ -578,6 +581,7 @@ static int do_reupdate(int ac, const char **av, if (save_nr != active_nr) goto redo; } + free_pathspec(&pathspec); return 0; } diff --git a/cache.h b/cache.h index 800efa2328..4beb2dc6ff 100644 --- a/cache.h +++ b/cache.h @@ -515,7 +515,7 @@ struct pathspec { extern int init_pathspec(struct pathspec *, const char **); extern void free_pathspec(struct pathspec *); -extern int ce_path_match(const struct cache_entry *ce, const char **pathspec); +extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec); extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path); extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object); extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); diff --git a/diff-lib.c b/diff-lib.c index 2251f3df68..1e22992cb1 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -106,7 +106,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option) DIFF_OPT_TST(&revs->diffopt, HAS_CHANGES)) break; - if (!ce_path_match(ce, revs->prune_data.raw)) + if (!ce_path_match(ce, &revs->prune_data)) continue; if (ce_stage(ce)) { @@ -427,7 +427,7 @@ static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o) if (tree == o->df_conflict_entry) tree = NULL; - if (ce_path_match(idx ? idx : tree, revs->prune_data.raw)) + if (ce_path_match(idx ? idx : tree, &revs->prune_data)) do_oneway_diff(o, idx, tree); return 0; diff --git a/preload-index.c b/preload-index.c index e3d0bda31a..49cb08df96 100644 --- a/preload-index.c +++ b/preload-index.c @@ -35,7 +35,9 @@ static void *preload_thread(void *_data) struct index_state *index = p->index; struct cache_entry **cep = index->cache + p->offset; struct cache_def cache; + struct pathspec pathspec; + init_pathspec(&pathspec, p->pathspec); memset(&cache, 0, sizeof(cache)); nr = p->nr; if (nr + p->offset > index->cache_nr) @@ -51,7 +53,7 @@ static void *preload_thread(void *_data) continue; if (ce_uptodate(ce)) continue; - if (!ce_path_match(ce, p->pathspec)) + if (!ce_path_match(ce, &pathspec)) continue; if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce))) continue; @@ -61,6 +63,7 @@ static void *preload_thread(void *_data) continue; ce_mark_uptodate(ce); } while (--nr > 0); + free_pathspec(&pathspec); return NULL; } diff --git a/read-cache.c b/read-cache.c index 4f2e890b01..8b2d537f02 100644 --- a/read-cache.c +++ b/read-cache.c @@ -706,17 +706,18 @@ int ce_same_name(struct cache_entry *a, struct cache_entry *b) return ce_namelen(b) == len && !memcmp(a->name, b->name, len); } -int ce_path_match(const struct cache_entry *ce, const char **pathspec) +int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec) { const char *match, *name; + const char **ps = pathspec->raw; int len; - if (!pathspec) + if (!pathspec->nr) return 1; len = ce_namelen(ce); name = ce->name; - while ((match = *pathspec++) != NULL) { + while ((match = *ps++) != NULL) { int matchlen = strlen(match); if (matchlen > len) continue; diff --git a/revision.c b/revision.c index bf87e01e45..86d2470489 100644 --- a/revision.c +++ b/revision.c @@ -969,7 +969,7 @@ static void prepare_show_merge(struct rev_info *revs) struct cache_entry *ce = active_cache[i]; if (!ce_stage(ce)) continue; - if (ce_path_match(ce, revs->prune_data.raw)) { + if (ce_path_match(ce, &revs->prune_data)) { prune_num++; prune = xrealloc(prune, sizeof(*prune) * prune_num); prune[prune_num-2] = ce->name; diff --git a/wt-status.c b/wt-status.c index 1ea330ed17..a82b11d341 100644 --- a/wt-status.c +++ b/wt-status.c @@ -354,14 +354,16 @@ static void wt_status_collect_changes_index(struct wt_status *s) static void wt_status_collect_changes_initial(struct wt_status *s) { + struct pathspec pathspec; int i; + init_pathspec(&pathspec, s->pathspec); for (i = 0; i < active_nr; i++) { struct string_list_item *it; struct wt_status_change_data *d; struct cache_entry *ce = active_cache[i]; - if (!ce_path_match(ce, s->pathspec)) + if (!ce_path_match(ce, &pathspec)) continue; it = string_list_insert(&s->change, ce->name); d = it->util; @@ -376,6 +378,7 @@ static void wt_status_collect_changes_initial(struct wt_status *s) else d->index_status = DIFF_STATUS_ADDED; } + free_pathspec(&pathspec); } static void wt_status_collect_untracked(struct wt_status *s) -- cgit v1.3 From f34bbc15ce0749a3e9f749f0fef6c486e1482719 Mon Sep 17 00:00:00 2001 From: Nguyễn Thái Ngọc Duy Date: Wed, 15 Dec 2010 22:02:51 +0700 Subject: grep: convert to use struct pathspec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/grep.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'builtin') diff --git a/builtin/grep.c b/builtin/grep.c index fdf7131efd..ad9ec4a299 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -581,7 +581,7 @@ static void run_pager(struct grep_opt *opt, const char *prefix) free(argv); } -static int grep_cache(struct grep_opt *opt, const char **paths, int cached) +static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int cached) { int hit = 0; int nr; @@ -591,7 +591,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached) struct cache_entry *ce = active_cache[nr]; if (!S_ISREG(ce->ce_mode)) continue; - if (!pathspec_matches(paths, ce->name, opt->max_depth)) + if (!pathspec_matches(pathspec->raw, ce->name, opt->max_depth)) continue; /* * If CE_VALID is on, we assume worktree file and its cache entry @@ -618,7 +618,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached) return hit; } -static int grep_tree(struct grep_opt *opt, const char **paths, +static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, struct tree_desc *tree, const char *tree_name, const char *base) { @@ -652,7 +652,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths, strbuf_addch(&pathbuf, '/'); down = pathbuf.buf + tn_len; - if (!pathspec_matches(paths, down, opt->max_depth)) + if (!pathspec_matches(pathspec->raw, down, opt->max_depth)) ; else if (S_ISREG(entry.mode)) hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len); @@ -667,7 +667,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths, die("unable to read tree (%s)", sha1_to_hex(entry.sha1)); init_tree_desc(&sub, data, size); - hit |= grep_tree(opt, paths, &sub, tree_name, down); + hit |= grep_tree(opt, pathspec, &sub, tree_name, down); free(data); } if (hit && opt->status_only) @@ -677,7 +677,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths, return hit; } -static int grep_object(struct grep_opt *opt, const char **paths, +static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, struct object *obj, const char *name) { if (obj->type == OBJ_BLOB) @@ -692,14 +692,14 @@ static int grep_object(struct grep_opt *opt, const char **paths, if (!data) die("unable to read tree (%s)", sha1_to_hex(obj->sha1)); init_tree_desc(&tree, data, size); - hit = grep_tree(opt, paths, &tree, name, ""); + hit = grep_tree(opt, pathspec, &tree, name, ""); free(data); return hit; } die("unable to grep from object of type %s", typename(obj->type)); } -static int grep_objects(struct grep_opt *opt, const char **paths, +static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec, const struct object_array *list) { unsigned int i; @@ -709,7 +709,7 @@ static int grep_objects(struct grep_opt *opt, const char **paths, for (i = 0; i < nr; i++) { struct object *real_obj; real_obj = deref_tag(list->objects[i].item, NULL, 0); - if (grep_object(opt, paths, real_obj, list->objects[i].name)) { + if (grep_object(opt, pathspec, real_obj, list->objects[i].name)) { hit = 1; if (opt->status_only) break; @@ -718,7 +718,7 @@ static int grep_objects(struct grep_opt *opt, const char **paths, return hit; } -static int grep_directory(struct grep_opt *opt, const char **paths) +static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec) { struct dir_struct dir; int i, hit = 0; @@ -726,7 +726,7 @@ static int grep_directory(struct grep_opt *opt, const char **paths) memset(&dir, 0, sizeof(dir)); setup_standard_excludes(&dir); - fill_directory(&dir, paths); + fill_directory(&dir, pathspec->raw); for (i = 0; i < dir.nr; i++) { hit |= grep_file(opt, dir.entries[i]->name); if (hit && opt->status_only) @@ -832,6 +832,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) struct grep_opt opt; struct object_array list = OBJECT_ARRAY_INIT; const char **paths = NULL; + struct pathspec pathspec; struct string_list path_list = STRING_LIST_INIT_NODUP; int i; int dummy; @@ -1059,6 +1060,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) paths[0] = prefix; paths[1] = NULL; } + init_pathspec(&pathspec, paths); if (show_in_pager && (cached || list.nr)) die("--open-files-in-pager only works on the worktree"); @@ -1089,16 +1091,16 @@ int cmd_grep(int argc, const char **argv, const char *prefix) die("--cached cannot be used with --no-index."); if (list.nr) die("--no-index cannot be used with revs."); - hit = grep_directory(&opt, paths); + hit = grep_directory(&opt, &pathspec); } else if (!list.nr) { if (!cached) setup_work_tree(); - hit = grep_cache(&opt, paths, cached); + hit = grep_cache(&opt, &pathspec, cached); } else { if (cached) die("both --cached and trees are given."); - hit = grep_objects(&opt, paths, &list); + hit = grep_objects(&opt, &pathspec, &list); } if (use_threads) -- cgit v1.3 From 2ed2437a143391c9e882318874905c964f313977 Mon Sep 17 00:00:00 2001 From: Nguyễn Thái Ngọc Duy Date: Wed, 15 Dec 2010 22:02:52 +0700 Subject: grep: use match_pathspec_depth() for cache/worktree grepping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/grep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'builtin') diff --git a/builtin/grep.c b/builtin/grep.c index ad9ec4a299..16a2fde947 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -591,7 +591,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int struct cache_entry *ce = active_cache[nr]; if (!S_ISREG(ce->ce_mode)) continue; - if (!pathspec_matches(pathspec->raw, ce->name, opt->max_depth)) + if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL)) continue; /* * If CE_VALID is on, we assume worktree file and its cache entry -- cgit v1.3 From e5e062b6dcdbbc338a0501b97a35e2e5efa56075 Mon Sep 17 00:00:00 2001 From: Nguyễn Thái Ngọc Duy Date: Fri, 17 Dec 2010 19:44:25 +0700 Subject: grep: use writable strbuf from caller for grep_tree() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/grep.c | 51 ++++++++++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 27 deletions(-) (limited to 'builtin') diff --git a/builtin/grep.c b/builtin/grep.c index 16a2fde947..c9622d6aa9 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -619,43 +619,29 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int } static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, - struct tree_desc *tree, - const char *tree_name, const char *base) + struct tree_desc *tree, struct strbuf *base, int tn_len) { - int len; int hit = 0; struct name_entry entry; - char *down; - int tn_len = strlen(tree_name); - struct strbuf pathbuf; - - strbuf_init(&pathbuf, PATH_MAX + tn_len); - - if (tn_len) { - strbuf_add(&pathbuf, tree_name, tn_len); - strbuf_addch(&pathbuf, ':'); - tn_len = pathbuf.len; - } - strbuf_addstr(&pathbuf, base); - len = pathbuf.len; + int old_baselen = base->len; while (tree_entry(tree, &entry)) { int te_len = tree_entry_len(entry.path, entry.sha1); - pathbuf.len = len; - strbuf_add(&pathbuf, entry.path, te_len); + + strbuf_add(base, entry.path, te_len); if (S_ISDIR(entry.mode)) /* Match "abc/" against pathspec to * decide if we want to descend into "abc" * directory. */ - strbuf_addch(&pathbuf, '/'); + strbuf_addch(base, '/'); - down = pathbuf.buf + tn_len; - if (!pathspec_matches(pathspec->raw, down, opt->max_depth)) + if (!pathspec_matches(pathspec->raw, base->buf + tn_len, opt->max_depth)) ; - else if (S_ISREG(entry.mode)) - hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len); + else if (S_ISREG(entry.mode)) { + hit |= grep_sha1(opt, entry.sha1, base->buf, tn_len); + } else if (S_ISDIR(entry.mode)) { enum object_type type; struct tree_desc sub; @@ -667,13 +653,14 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, die("unable to read tree (%s)", sha1_to_hex(entry.sha1)); init_tree_desc(&sub, data, size); - hit |= grep_tree(opt, pathspec, &sub, tree_name, down); + hit |= grep_tree(opt, pathspec, &sub, base, tn_len); free(data); } + strbuf_setlen(base, old_baselen); + if (hit && opt->status_only) break; } - strbuf_release(&pathbuf); return hit; } @@ -686,13 +673,23 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, struct tree_desc tree; void *data; unsigned long size; - int hit; + struct strbuf base; + int hit, len; + data = read_object_with_reference(obj->sha1, tree_type, &size, NULL); if (!data) die("unable to read tree (%s)", sha1_to_hex(obj->sha1)); + + len = name ? strlen(name) : 0; + strbuf_init(&base, PATH_MAX + len + 1); + if (len) { + strbuf_add(&base, name, len); + strbuf_addch(&base, ':'); + } init_tree_desc(&tree, data, size); - hit = grep_tree(opt, pathspec, &tree, name, ""); + hit = grep_tree(opt, pathspec, &tree, &base, base.len); + strbuf_release(&base); free(data); return hit; } -- cgit v1.3 From 1376e50723228fc21b7183fe86d71ee484a70dd7 Mon Sep 17 00:00:00 2001 From: Nguyễn Thái Ngọc Duy Date: Fri, 17 Dec 2010 19:45:33 +0700 Subject: grep: drop pathspec_matches() in favor of tree_entry_interesting() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/grep.c | 125 +++++++-------------------------------------------------- tree-diff.c | 4 +- tree-walk.c | 24 ++++++----- tree-walk.h | 2 +- 4 files changed, 30 insertions(+), 125 deletions(-) (limited to 'builtin') diff --git a/builtin/grep.c b/builtin/grep.c index c9622d6aa9..c3af8760cc 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -329,106 +329,6 @@ static int grep_config(const char *var, const char *value, void *cb) return 0; } -/* - * Return non-zero if max_depth is negative or path has no more then max_depth - * slashes. - */ -static int accept_subdir(const char *path, int max_depth) -{ - if (max_depth < 0) - return 1; - - while ((path = strchr(path, '/')) != NULL) { - max_depth--; - if (max_depth < 0) - return 0; - path++; - } - return 1; -} - -/* - * Return non-zero if name is a subdirectory of match and is not too deep. - */ -static int is_subdir(const char *name, int namelen, - const char *match, int matchlen, int max_depth) -{ - if (matchlen > namelen || strncmp(name, match, matchlen)) - return 0; - - if (name[matchlen] == '\0') /* exact match */ - return 1; - - if (!matchlen || match[matchlen-1] == '/' || name[matchlen] == '/') - return accept_subdir(name + matchlen + 1, max_depth); - - return 0; -} - -/* - * git grep pathspecs are somewhat different from diff-tree pathspecs; - * pathname wildcards are allowed. - */ -static int pathspec_matches(const char **paths, const char *name, int max_depth) -{ - int namelen, i; - if (!paths || !*paths) - return accept_subdir(name, max_depth); - namelen = strlen(name); - for (i = 0; paths[i]; i++) { - const char *match = paths[i]; - int matchlen = strlen(match); - const char *cp, *meta; - - if (is_subdir(name, namelen, match, matchlen, max_depth)) - return 1; - if (!fnmatch(match, name, 0)) - return 1; - if (name[namelen-1] != '/') - continue; - - /* We are being asked if the directory ("name") is worth - * descending into. - * - * Find the longest leading directory name that does - * not have metacharacter in the pathspec; the name - * we are looking at must overlap with that directory. - */ - for (cp = match, meta = NULL; cp - match < matchlen; cp++) { - char ch = *cp; - if (ch == '*' || ch == '[' || ch == '?') { - meta = cp; - break; - } - } - if (!meta) - meta = cp; /* fully literal */ - - if (namelen <= meta - match) { - /* Looking at "Documentation/" and - * the pattern says "Documentation/howto/", or - * "Documentation/diff*.txt". The name we - * have should match prefix. - */ - if (!memcmp(match, name, namelen)) - return 1; - continue; - } - - if (meta - match < namelen) { - /* Looking at "Documentation/howto/" and - * the pattern says "Documentation/h*"; - * match up to "Do.../h"; this avoids descending - * into "Documentation/technical/". - */ - if (!memcmp(match, name, meta - match)) - return 1; - continue; - } - } - return 0; -} - static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size) { void *data; @@ -621,25 +521,24 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, struct tree_desc *tree, struct strbuf *base, int tn_len) { - int hit = 0; + int hit = 0, matched = 0; struct name_entry entry; int old_baselen = base->len; while (tree_entry(tree, &entry)) { int te_len = tree_entry_len(entry.path, entry.sha1); - strbuf_add(base, entry.path, te_len); + if (matched != 2) { + matched = tree_entry_interesting(&entry, base, tn_len, pathspec); + if (matched == -1) + break; /* no more matches */ + if (!matched) + continue; + } - if (S_ISDIR(entry.mode)) - /* Match "abc/" against pathspec to - * decide if we want to descend into "abc" - * directory. - */ - strbuf_addch(base, '/'); + strbuf_add(base, entry.path, te_len); - if (!pathspec_matches(pathspec->raw, base->buf + tn_len, opt->max_depth)) - ; - else if (S_ISREG(entry.mode)) { + if (S_ISREG(entry.mode)) { hit |= grep_sha1(opt, entry.sha1, base->buf, tn_len); } else if (S_ISDIR(entry.mode)) { @@ -652,6 +551,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, if (!data) die("unable to read tree (%s)", sha1_to_hex(entry.sha1)); + + strbuf_addch(base, '/'); init_tree_desc(&sub, data, size); hit |= grep_tree(opt, pathspec, &sub, base, tn_len); free(data); @@ -1058,6 +959,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) paths[1] = NULL; } init_pathspec(&pathspec, paths); + pathspec.max_depth = opt.max_depth; + pathspec.recursive = 1; if (show_in_pager && (cached || list.nr)) die("--open-files-in-pager only works on the worktree"); diff --git a/tree-diff.c b/tree-diff.c index 03dc5c8e70..3954281f50 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -72,7 +72,7 @@ static void show_tree(struct diff_options *opt, const char *prefix, if (all_interesting) show = 1; else { - show = tree_entry_interesting(&desc->entry, base, + show = tree_entry_interesting(&desc->entry, base, 0, &opt->pathspec); if (show == 2) all_interesting = 1; @@ -124,7 +124,7 @@ static void skip_uninteresting(struct tree_desc *t, struct strbuf *base, struct diff_options *opt, int *all_interesting) { while (t->size) { - int show = tree_entry_interesting(&t->entry, base, &opt->pathspec); + int show = tree_entry_interesting(&t->entry, base, 0, &opt->pathspec); if (show == 2) *all_interesting = 1; if (!show) { diff --git a/tree-walk.c b/tree-walk.c index 7596716cf0..322becc3b4 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -544,7 +544,8 @@ static int match_dir_prefix(const char *base, int baselen, /* * Is a tree entry interesting given the pathspec we have? * - * Pre-condition: baselen == 0 || base[baselen-1] == '/' + * Pre-condition: either baselen == base_offset (i.e. empty path) + * or base[baselen-1] == '/' (i.e. with trailing slash). * * Return: * - 2 for "yes, and all subsequent entries will be" @@ -553,44 +554,45 @@ static int match_dir_prefix(const char *base, int baselen, * - negative for "no, and no subsequent entries will be either" */ int tree_entry_interesting(const struct name_entry *entry, - struct strbuf *base, + struct strbuf *base, int base_offset, const struct pathspec *ps) { int i; - int pathlen, baselen = base->len; + int pathlen, baselen = base->len - base_offset; int never_interesting = ps->has_wildcard ? 0 : -1; if (!ps->nr) { if (!ps->recursive || ps->max_depth == -1) return 2; - return !!within_depth(base->buf, baselen, + return !!within_depth(base->buf + base_offset, baselen, !!S_ISDIR(entry->mode), ps->max_depth); } pathlen = tree_entry_len(entry->path, entry->sha1); - for (i = ps->nr-1; i >= 0; i--) { + for (i = ps->nr - 1; i >= 0; i--) { const struct pathspec_item *item = ps->items+i; const char *match = item->match; + const char *base_str = base->buf + base_offset; int matchlen = item->len; if (baselen >= matchlen) { /* If it doesn't match, move along... */ - if (!match_dir_prefix(base->buf, baselen, match, matchlen)) + if (!match_dir_prefix(base_str, baselen, match, matchlen)) goto match_wildcards; if (!ps->recursive || ps->max_depth == -1) return 2; - return !!within_depth(base->buf + matchlen + 1, + return !!within_depth(base_str + matchlen + 1, baselen - matchlen - 1, !!S_ISDIR(entry->mode), ps->max_depth); } /* Does the base match? */ - if (!strncmp(base->buf, match, baselen)) { + if (!strncmp(base_str, match, baselen)) { if (match_entry(entry, pathlen, match + baselen, matchlen - baselen, &never_interesting)) @@ -622,11 +624,11 @@ match_wildcards: strbuf_add(base, entry->path, pathlen); - if (!fnmatch(match, base->buf, 0)) { - strbuf_setlen(base, baselen); + if (!fnmatch(match, base->buf + base_offset, 0)) { + strbuf_setlen(base, base_offset + baselen); return 1; } - strbuf_setlen(base, baselen); + strbuf_setlen(base, base_offset + baselen); /* * Match all directories. We'll try to match files diff --git a/tree-walk.h b/tree-walk.h index 6589ee27e4..39524b7dba 100644 --- a/tree-walk.h +++ b/tree-walk.h @@ -60,6 +60,6 @@ static inline int traverse_path_len(const struct traverse_info *info, const stru return info->pathlen + tree_entry_len(n->path, n->sha1); } -extern int tree_entry_interesting(const struct name_entry *, struct strbuf *, const struct pathspec *ps); +extern int tree_entry_interesting(const struct name_entry *, struct strbuf *, int, const struct pathspec *ps); #endif -- cgit v1.3 From c879daa23729547fb28aa7e8783c5e4e619a9e7c Mon Sep 17 00:00:00 2001 From: Nguyễn Thái Ngọc Duy Date: Sat, 5 Feb 2011 17:52:21 +0700 Subject: Make hash-object more robust against malformed objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commits, trees and tags have structure. Don't let users feed git with malformed ones. Sooner or later git will die() when encountering them. Note that this patch does not check semantics. A tree that points to non-existent objects is perfectly OK (and should be so, users may choose to add commit first, then its associated tree for example). Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- builtin/hash-object.c | 2 +- cache.h | 2 +- read-cache.c | 2 +- sha1_file.c | 54 +++++++++++++++++++++++++++++++++++++++++++------- t/t1007-hash-object.sh | 13 ++++++++++++ 5 files changed, 63 insertions(+), 10 deletions(-) (limited to 'builtin') diff --git a/builtin/hash-object.c b/builtin/hash-object.c index 080af1a01b..c90acddcb2 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -15,7 +15,7 @@ static void hash_fd(int fd, const char *type, int write_object, const char *path struct stat st; unsigned char sha1[20]; if (fstat(fd, &st) < 0 || - index_fd(sha1, fd, &st, write_object, type_from_string(type), path)) + index_fd(sha1, fd, &st, write_object, type_from_string(type), path, 1)) die(write_object ? "Unable to add %s to database" : "Unable to hash %s", path); diff --git a/cache.h b/cache.h index d83d68c859..9186a56be7 100644 --- a/cache.h +++ b/cache.h @@ -501,7 +501,7 @@ extern int ie_match_stat(const struct index_state *, struct cache_entry *, struc extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int); extern int ce_path_match(const struct cache_entry *ce, const char **pathspec); -extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path); +extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path, int format_check); extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object); extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); diff --git a/read-cache.c b/read-cache.c index 4f2e890b01..fbc12f3c3e 100644 --- a/read-cache.c +++ b/read-cache.c @@ -92,7 +92,7 @@ static int ce_compare_data(struct cache_entry *ce, struct stat *st) if (fd >= 0) { unsigned char sha1[20]; - if (!index_fd(sha1, fd, st, 0, OBJ_BLOB, ce->name)) + if (!index_fd(sha1, fd, st, 0, OBJ_BLOB, ce->name, 0)) match = hashcmp(sha1, ce->sha1); /* index_fd() closed the file descriptor already */ } diff --git a/sha1_file.c b/sha1_file.c index d86a8db69a..58ca85835b 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -13,6 +13,7 @@ #include "commit.h" #include "tag.h" #include "tree.h" +#include "tree-walk.h" #include "refs.h" #include "pack-revindex.h" #include "sha1-lookup.h" @@ -2471,8 +2472,37 @@ int has_sha1_file(const unsigned char *sha1) return has_loose_object(sha1); } +static void check_tree(const void *buf, size_t size) +{ + struct tree_desc desc; + struct name_entry entry; + + init_tree_desc(&desc, buf, size); + while (tree_entry(&desc, &entry)) + /* do nothing + * tree_entry() will die() on malformed entries */ + ; +} + +static void check_commit(const void *buf, size_t size) +{ + struct commit c; + memset(&c, 0, sizeof(c)); + if (parse_commit_buffer(&c, buf, size)) + die("corrupt commit"); +} + +static void check_tag(const void *buf, size_t size) +{ + struct tag t; + memset(&t, 0, sizeof(t)); + if (parse_tag_buffer(&t, buf, size)) + die("corrupt tag"); +} + static int index_mem(unsigned char *sha1, void *buf, size_t size, - int write_object, enum object_type type, const char *path) + int write_object, enum object_type type, + const char *path, int format_check) { int ret, re_allocated = 0; @@ -2490,6 +2520,14 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size, re_allocated = 1; } } + if (format_check) { + if (type == OBJ_TREE) + check_tree(buf, size); + if (type == OBJ_COMMIT) + check_commit(buf, size); + if (type == OBJ_TAG) + check_tag(buf, size); + } if (write_object) ret = write_sha1_file(buf, size, typename(type), sha1); @@ -2503,7 +2541,7 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size, #define SMALL_FILE_SIZE (32*1024) int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, - enum object_type type, const char *path) + enum object_type type, const char *path, int format_check) { int ret; size_t size = xsize_t(st->st_size); @@ -2512,23 +2550,25 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, struct strbuf sbuf = STRBUF_INIT; if (strbuf_read(&sbuf, fd, 4096) >= 0) ret = index_mem(sha1, sbuf.buf, sbuf.len, write_object, - type, path); + type, path, format_check); else ret = -1; strbuf_release(&sbuf); } else if (!size) { - ret = index_mem(sha1, NULL, size, write_object, type, path); + ret = index_mem(sha1, NULL, size, write_object, type, path, + format_check); } else if (size <= SMALL_FILE_SIZE) { char *buf = xmalloc(size); if (size == read_in_full(fd, buf, size)) ret = index_mem(sha1, buf, size, write_object, type, - path); + path, format_check); else ret = error("short read %s", strerror(errno)); free(buf); } else { void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); - ret = index_mem(sha1, buf, size, write_object, type, path); + ret = index_mem(sha1, buf, size, write_object, type, path, + format_check); munmap(buf, size); } close(fd); @@ -2546,7 +2586,7 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write if (fd < 0) return error("open(\"%s\"): %s", path, strerror(errno)); - if (index_fd(sha1, fd, st, write_object, OBJ_BLOB, path) < 0) + if (index_fd(sha1, fd, st, write_object, OBJ_BLOB, path, 0) < 0) return error("%s: failed to insert into database", path); break; diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh index dd32432d62..6d52b824b1 100755 --- a/t/t1007-hash-object.sh +++ b/t/t1007-hash-object.sh @@ -188,4 +188,17 @@ for args in "-w --stdin-paths" "--stdin-paths -w"; do pop_repo done +test_expect_success 'corrupt tree' ' + echo abc >malformed-tree + test_must_fail git hash-object -t tree malformed-tree +' + +test_expect_success 'corrupt commit' ' + test_must_fail git hash-object -t commit --stdin Date: Tue, 8 Feb 2011 04:29:09 -0600 Subject: checkout: split off a function to peel away branchname arg The code to parse and consume the tree name and "--" in commands such as "git checkout @{-1} -- '*.c'" is intimidatingly long. Split it out into a separate function and make it easier to skip on first reading by making the data it uses and produces more explicit. No functional change intended. Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- builtin/checkout.c | 229 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 131 insertions(+), 98 deletions(-) (limited to 'builtin') diff --git a/builtin/checkout.c b/builtin/checkout.c index 953abdd0fa..0e7a6a3952 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -675,11 +675,123 @@ static const char *unique_tracking_name(const char *name) return NULL; } +static int parse_branchname_arg(int argc, const char **argv, + int dwim_new_local_branch_ok, + struct branch_info *new, + struct tree **source_tree, + unsigned char rev[20], + const char **new_branch) +{ + int argcount = 0; + unsigned char branch_rev[20]; + const char *arg; + int has_dash_dash; + + /* + * case 1: git checkout -- [] + * + * must be a valid tree, everything after the '--' must be + * a path. + * + * case 2: git checkout -- [] + * + * everything after the '--' must be paths. + * + * case 3: git checkout [] + * + * With no paths, if is a commit, that is to + * switch to the branch or detach HEAD at it. As a special case, + * if is A...B (missing A or B means HEAD but you can + * omit at most one side), and if there is a unique merge base + * between A and B, A...B names that merge base. + * + * With no paths, if is _not_ a commit, no -t nor -b + * was given, and there is a tracking branch whose name is + * in one and only one remote, then this is a short-hand + * to fork local from that remote tracking branch. + * + * Otherwise shall not be ambiguous. + * - If it's *only* a reference, treat it like case (1). + * - If it's only a path, treat it like case (2). + * - else: fail. + * + */ + if (!argc) + return 0; + + if (!strcmp(argv[0], "--")) /* case (2) */ + return 1; + + arg = argv[0]; + has_dash_dash = (argc > 1) && !strcmp(argv[1], "--"); + + if (!strcmp(arg, "-")) + arg = "@{-1}"; + + if (get_sha1_mb(arg, rev)) { + if (has_dash_dash) /* case (1) */ + die("invalid reference: %s", arg); + if (dwim_new_local_branch_ok && + !check_filename(NULL, arg) && + argc == 1) { + const char *remote = unique_tracking_name(arg); + if (!remote || get_sha1(remote, rev)) + return argcount; + *new_branch = arg; + arg = remote; + /* DWIMmed to create local branch */ + } else { + return argcount; + } + } + + /* we can't end up being in (2) anymore, eat the argument */ + argcount++; + argv++; + argc--; + + new->name = arg; + setup_branch_path(new); + + if (check_ref_format(new->path) == CHECK_REF_FORMAT_OK && + resolve_ref(new->path, branch_rev, 1, NULL)) + hashcpy(rev, branch_rev); + else + new->path = NULL; /* not an existing branch */ + + new->commit = lookup_commit_reference_gently(rev, 1); + if (!new->commit) { + /* not a commit */ + *source_tree = parse_tree_indirect(rev); + } else { + parse_commit(new->commit); + *source_tree = new->commit->tree; + } + + if (!*source_tree) /* case (1): want a tree */ + die("reference is not a tree: %s", arg); + if (!has_dash_dash) {/* case (3 -> 1) */ + /* + * Do not complain the most common case + * git checkout branch + * even if there happen to be a file called 'branch'; + * it would be extremely annoying. + */ + if (argc) + verify_non_filename(NULL, arg); + } else { + argcount++; + argv++; + argc--; + } + + return argcount; +} + int cmd_checkout(int argc, const char **argv, const char *prefix) { struct checkout_opts opts; - unsigned char rev[20], branch_rev[20]; - const char *arg; + unsigned char rev[20]; struct branch_info new; struct tree *source_tree = NULL; char *conflict_style = NULL; @@ -709,7 +821,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, OPT_END(), }; - int has_dash_dash; memset(&opts, 0, sizeof(opts)); memset(&new, 0, sizeof(new)); @@ -766,108 +877,30 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) die("git checkout: -f and -m are incompatible"); /* - * case 1: git checkout -- [] - * - * must be a valid tree, everything after the '--' must be - * a path. - * - * case 2: git checkout -- [] - * - * everything after the '--' must be paths. + * Extract branch name from command line arguments, so + * all that is left is pathspecs. * - * case 3: git checkout [] - * - * With no paths, if is a commit, that is to - * switch to the branch or detach HEAD at it. As a special case, - * if is A...B (missing A or B means HEAD but you can - * omit at most one side), and if there is a unique merge base - * between A and B, A...B names that merge base. - * - * With no paths, if is _not_ a commit, no -t nor -b - * was given, and there is a tracking branch whose name is - * in one and only one remote, then this is a short-hand - * to fork local from that remote tracking branch. + * Handle * - * Otherwise shall not be ambiguous. - * - If it's *only* a reference, treat it like case (1). - * - If it's only a path, treat it like case (2). - * - else: fail. + * 1) git checkout -- [] + * 2) git checkout -- [] + * 3) git checkout [] * + * including "last branch" syntax and DWIM-ery for names of + * remote branches, erroring out for invalid or ambiguous cases. */ if (argc) { - if (!strcmp(argv[0], "--")) { /* case (2) */ - argv++; - argc--; - goto no_reference; - } - - arg = argv[0]; - has_dash_dash = (argc > 1) && !strcmp(argv[1], "--"); - - if (!strcmp(arg, "-")) - arg = "@{-1}"; - - if (get_sha1_mb(arg, rev)) { - if (has_dash_dash) /* case (1) */ - die("invalid reference: %s", arg); - if (!patch_mode && - dwim_new_local_branch && - opts.track == BRANCH_TRACK_UNSPECIFIED && - !opts.new_branch && - !check_filename(NULL, arg) && - argc == 1) { - const char *remote = unique_tracking_name(arg); - if (!remote || get_sha1(remote, rev)) - goto no_reference; - opts.new_branch = arg; - arg = remote; - /* DWIMmed to create local branch */ - } - else - goto no_reference; - } - - /* we can't end up being in (2) anymore, eat the argument */ - argv++; - argc--; - - new.name = arg; - setup_branch_path(&new); - - if (check_ref_format(new.path) == CHECK_REF_FORMAT_OK && - resolve_ref(new.path, branch_rev, 1, NULL)) - hashcpy(rev, branch_rev); - else - new.path = NULL; /* not an existing branch */ - - if (!(new.commit = lookup_commit_reference_gently(rev, 1))) { - /* not a commit */ - source_tree = parse_tree_indirect(rev); - } else { - parse_commit(new.commit); - source_tree = new.commit->tree; - } - - if (!source_tree) /* case (1): want a tree */ - die("reference is not a tree: %s", arg); - if (!has_dash_dash) {/* case (3 -> 1) */ - /* - * Do not complain the most common case - * git checkout branch - * even if there happen to be a file called 'branch'; - * it would be extremely annoying. - */ - if (argc) - verify_non_filename(NULL, arg); - } - else { - argv++; - argc--; - } + int dwim_ok = + !patch_mode && + dwim_new_local_branch && + opts.track == BRANCH_TRACK_UNSPECIFIED && + !opts.new_branch; + int n = parse_branchname_arg(argc, argv, dwim_ok, + &new, &source_tree, rev, &opts.new_branch); + argv += n; + argc -= n; } -no_reference: - if (opts.track == BRANCH_TRACK_UNSPECIFIED) opts.track = git_branch_track; -- cgit v1.3 From 32669671c7746888aa1e3832907deb7fc8405061 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 8 Feb 2011 04:32:49 -0600 Subject: checkout: introduce --detach synonym for "git checkout foo^{commit}" For example, one might use this when making a temporary merge to test that two topics work well together. Patch by Junio, with tests from Jeff King. [jn: with some extra checks for bogus commandline usage] Suggested-by: Jeff King Signed-off-by: Junio C Hamano Signed-off-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- Documentation/git-checkout.txt | 13 +++++- builtin/checkout.c | 25 ++++++++--- t/t2020-checkout-detach.sh | 95 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 7 deletions(-) create mode 100755 t/t2020-checkout-detach.sh (limited to 'builtin') diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 22d36114df..d162117e71 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -9,6 +9,7 @@ SYNOPSIS -------- [verse] 'git checkout' [-q] [-f] [-m] [] +'git checkout' [-q] [-f] [-m] [--detach] [] 'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] ] [] 'git checkout' [-f|--ours|--theirs|-m|--conflict=