From: Junio C Hamano Date: Sat, 4 Sep 2010 05:23:49 +0000 (-0700) Subject: Merge branch 'dg/local-mod-error-messages' X-Git-Tag: v1.7.3-rc0~5 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/c208e05bd9880028c980fafbc5dda3d17a603ac1?hp=-c Merge branch 'dg/local-mod-error-messages' * dg/local-mod-error-messages: t7609-merge-co-error-msgs: test non-fast forward case too. Move "show_all_errors = 1" to setup_unpack_trees_porcelain() setup_unpack_trees_porcelain: take the whole options struct as parameter Move set_porcelain_error_msgs to unpack-trees.c and rename it Conflicts: merge-recursive.c --- c208e05bd9880028c980fafbc5dda3d17a603ac1 diff --combined builtin/checkout.c index ff5ac1e0ff,f3dfb7b804..aae80c34cf --- a/builtin/checkout.c +++ b/builtin/checkout.c @@@ -32,11 -32,7 +32,11 @@@ struct checkout_opts int writeout_stage; int writeout_error; + /* not set by parse_options */ + int branch_exists; + const char *new_branch; + const char *new_branch_force; const char *new_orphan_branch; int new_branch_log; enum branch_track track; @@@ -154,10 -150,6 +154,10 @@@ static int checkout_merged(int pos, str read_mmblob(&ours, active_cache[pos+1]->sha1); read_mmblob(&theirs, active_cache[pos+2]->sha1); + /* + * NEEDSWORK: re-create conflicts from merges with + * merge.renormalize set, too + */ status = ll_merge(&result_buf, path, &ancestor, "base", &ours, "ours", &theirs, "theirs", 0); free(ancestor.ptr); @@@ -287,6 -279,7 +287,6 @@@ static void show_local_changes(struct o struct rev_info rev; /* I think we want full paths, even if we're in a subdirectory. */ init_revisions(&rev, NULL); - rev.abbrev = 0; rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS; if (diff_setup_done(&rev.diffopt) < 0) die("diff_setup_done failed"); @@@ -380,7 -373,7 +380,7 @@@ static int merge_working_tree(struct ch topts.src_index = &the_index; topts.dst_index = &the_index; - set_porcelain_error_msgs(topts.msgs, "checkout"); + setup_unpack_trees_porcelain(&topts, "checkout"); refresh_cache(REFRESH_QUIET); @@@ -399,7 -392,6 +399,6 @@@ topts.dir = xcalloc(1, sizeof(*topts.dir)); topts.dir->flags |= DIR_SHOW_IGNORED; topts.dir->exclude_per_dir = ".gitignore"; - topts.show_all_errors = 1; tree = parse_tree_indirect(old->commit ? old->commit->object.sha1 : (unsigned char *)EMPTY_TREE_SHA1_BIN); @@@ -441,13 -433,6 +440,13 @@@ */ add_files_to_cache(NULL, NULL, 0); + /* + * NEEDSWORK: carrying over local changes + * when branches have different end-of-line + * normalization (or clean+smudge rules) is + * a pain; plumb in an option to set + * o.renormalize? + */ init_merge_options(&o); o.verbosity = 0; work = write_tree_from_memory(&o); @@@ -526,8 -511,7 +525,8 @@@ static void update_refs_for_switch(stru } } else - create_branch(old->name, opts->new_branch, new->name, 0, + create_branch(old->name, opts->new_branch, new->name, + opts->new_branch_force ? 1 : 0, opts->new_branch_log, opts->track); new->name = opts->new_branch; setup_branch_path(new); @@@ -545,12 -529,9 +544,12 @@@ if (old->path && !strcmp(new->path, old->path)) fprintf(stderr, "Already on '%s'\n", new->name); - else + else if (opts->new_branch) fprintf(stderr, "Switched to%s branch '%s'\n", - opts->new_branch ? " a new" : "", + opts->branch_exists ? " and reset" : " a new", + new->name); + else + fprintf(stderr, "Switched to branch '%s'\n", new->name); } if (old->path && old->name) { @@@ -676,10 -657,7 +675,10 @@@ int cmd_checkout(int argc, const char * int dwim_new_local_branch = 1; struct option options[] = { OPT__QUIET(&opts.quiet), - OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"), + OPT_STRING('b', NULL, &opts.new_branch, "branch", + "create and checkout a new branch"), + OPT_STRING('B', NULL, &opts.new_branch_force, "branch", + "create/reset and checkout a branch"), OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"), OPT_SET_INT('t', "track", &opts.track, "track", BRANCH_TRACK_EXPLICIT), @@@ -710,14 -688,6 +709,14 @@@ argc = parse_options(argc, argv, prefix, options, checkout_usage, PARSE_OPT_KEEP_DASHDASH); + /* we can assume from now on new_branch = !new_branch_force */ + if (opts.new_branch && opts.new_branch_force) + die("-B cannot be used with -b"); + + /* copy -B over to -b, so that we can just check the latter */ + if (opts.new_branch_force) + opts.new_branch = opts.new_branch_force; + if (patch_mode && (opts.track > 0 || opts.new_branch || opts.new_branch_log || opts.merge || opts.force)) die ("--patch is incompatible with all other options"); @@@ -739,7 -709,7 +738,7 @@@ if (opts.new_orphan_branch) { if (opts.new_branch) - die("--orphan and -b are mutually exclusive"); + die("--orphan and -b|-B are mutually exclusive"); if (opts.track > 0) die("--orphan cannot be used with -t"); opts.new_branch = opts.new_orphan_branch; @@@ -888,12 -858,8 +887,12 @@@ no_reference if (strbuf_check_branch_ref(&buf, opts.new_branch)) die("git checkout: we do not like '%s' as a branch name.", opts.new_branch); - if (!get_sha1(buf.buf, rev)) - die("git checkout: branch %s already exists", opts.new_branch); + if (!get_sha1(buf.buf, rev)) { + opts.branch_exists = 1; + if (!opts.new_branch_force) + die("git checkout: branch %s already exists", + opts.new_branch); + } strbuf_release(&buf); } diff --combined builtin/merge.c index 4e4ec898e6,32b4d9d022..5f65c0c8a6 --- a/builtin/merge.c +++ b/builtin/merge.c @@@ -54,7 -54,6 +54,7 @@@ static size_t use_strategies_nr, use_st static const char **xopts; static size_t xopts_nr, xopts_alloc; static const char *branch; +static int option_renormalize; static int verbosity; static int allow_rerere_auto; @@@ -132,7 -131,6 +132,7 @@@ static struct strategy *get_strategy(co ret = xcalloc(1, sizeof(struct strategy)); ret->name = xstrdup(name); + ret->attr = NO_TRIVIAL; return ret; } @@@ -439,7 -437,7 +439,7 @@@ static void merge_name(const char *remo strbuf_addstr(&truname, "refs/heads/"); strbuf_addstr(&truname, remote); strbuf_setlen(&truname, truname.len - len); - if (resolve_ref(truname.buf, buf_sha, 0, NULL)) { + if (resolve_ref(truname.buf, buf_sha, 1, NULL)) { strbuf_addf(msg, "%s\t\tbranch '%s'%s of .\n", sha1_to_hex(remote_head->sha1), @@@ -488,8 -486,7 +488,8 @@@ static int git_merge_config(const char buf = xstrdup(v); argc = split_cmdline(buf, &argv); if (argc < 0) - die("Bad branch.%s.mergeoptions string", branch); + die("Bad branch.%s.mergeoptions string: %s", branch, + split_cmdline_strerror(argc)); argv = xrealloc(argv, sizeof(*argv) * (argc + 2)); memmove(argv + 1, argv, sizeof(*argv) * (argc + 1)); argc++; @@@ -506,8 -503,6 +506,8 @@@ return git_config_string(&pull_octopus, k, v); else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary")) option_log = git_config_bool(k, v); + else if (!strcmp(k, "merge.renormalize")) + option_renormalize = git_config_bool(k, v); return git_diff_ui_config(k, v, cb); } @@@ -629,11 -624,6 +629,11 @@@ static int try_merge_strategy(const cha if (!strcmp(strategy, "subtree")) o.subtree_shift = ""; + o.renormalize = option_renormalize; + + /* + * NEEDSWORK: merge with table in builtin/merge-recursive + */ for (x = 0; x < xopts_nr; x++) { if (!strcmp(xopts[x], "ours")) o.recursive_variant = MERGE_RECURSIVE_OURS; @@@ -643,10 -633,6 +643,10 @@@ o.subtree_shift = ""; else if (!prefixcmp(xopts[x], "subtree=")) o.subtree_shift = xopts[x]+8; + else if (!strcmp(xopts[x], "renormalize")) + o.renormalize = 1; + else if (!strcmp(xopts[x], "no-renormalize")) + o.renormalize = 0; else die("Unknown option for merge-recursive: -X%s", xopts[x]); } @@@ -718,8 -704,7 +718,7 @@@ int checkout_fast_forward(const unsigne opts.verbose_update = 1; opts.merge = 1; opts.fn = twoway_merge; - opts.show_all_errors = 1; - set_porcelain_error_msgs(opts.msgs, "merge"); + setup_unpack_trees_porcelain(&opts, "merge"); trees[nr_trees] = parse_tree_indirect(head); if (!trees[nr_trees++]) @@@ -831,7 -816,7 +830,7 @@@ static int finish_automerge(struct comm return 0; } -static int suggest_conflicts(void) +static int suggest_conflicts(int renormalizing) { FILE *fp; int pos; @@@ -1316,5 -1301,5 +1315,5 @@@ int cmd_merge(int argc, const char **ar "stopped before committing as requested\n"); return 0; } else - return suggest_conflicts(); + return suggest_conflicts(option_renormalize); } diff --combined merge-recursive.c index aadd48c4fc,084f54b88c..20e1779428 --- a/merge-recursive.c +++ b/merge-recursive.c @@@ -20,7 -20,6 +20,7 @@@ #include "attr.h" #include "merge-recursive.h" #include "dir.h" +#include "submodule.h" static struct tree *shift_tree_object(struct tree *one, struct tree *two, const char *subtree_shift) @@@ -137,10 -136,16 +137,10 @@@ static void output_commit_title(struct if (parse_commit(commit) != 0) printf("(bad commit)\n"); else { - const char *s; - int len; - for (s = commit->buffer; *s; s++) - if (*s == '\n' && s[1] == '\n') { - s += 2; - break; - } - for (len = 0; s[len] && '\n' != s[len]; len++) - ; /* do nothing */ - printf("%.*s\n", len, s); + const char *title; + int len = find_commit_subject(commit->buffer, &title); + if (len) + printf("%.*s\n", len, title); } } } @@@ -180,7 -185,7 +180,7 @@@ static int git_merge_trees(int index_on opts.fn = threeway_merge; opts.src_index = &the_index; opts.dst_index = &the_index; - set_porcelain_error_msgs(opts.msgs, "merge"); + setup_unpack_trees_porcelain(&opts, "merge"); init_tree_desc_from_tree(t+0, common); init_tree_desc_from_tree(t+1, head); @@@ -520,15 -525,13 +520,15 @@@ static void update_file_flags(struct me void *buf; unsigned long size; - if (S_ISGITLINK(mode)) + if (S_ISGITLINK(mode)) { /* * We may later decide to recursively descend into * the submodule directory and update its index * and/or work tree, but we do not do that now. */ + update_wd = 0; goto update_index; + } buf = read_sha1_file(sha, &type, &size); if (!buf) @@@ -644,9 -647,7 +644,9 @@@ static int merge_3way(struct merge_opti merge_status = ll_merge(result_buf, a->path, &orig, base_name, &src1, name1, &src2, name2, - (!!o->call_depth) | (favor << 1)); + ((o->call_depth ? LL_OPT_VIRTUAL_ANCESTOR : 0) | + (o->renormalize ? LL_OPT_RENORMALIZE : 0) | + create_ll_flag(favor))); free(name1); free(name2); @@@ -715,8 -716,8 +715,8 @@@ static struct merge_file_info merge_fil free(result_buf.ptr); result.clean = (merge_status == 0); } else if (S_ISGITLINK(a->mode)) { - result.clean = 0; - hashcpy(result.sha, a->sha1); + result.clean = merge_submodule(result.sha, one->path, one->sha1, + a->sha1, b->sha1); } else if (S_ISLNK(a->mode)) { hashcpy(result.sha, a->sha1); @@@ -805,8 -806,7 +805,8 @@@ static int process_renames(struct merge struct string_list *b_renames) { int clean_merge = 1, i, j; - struct string_list a_by_dst = {NULL, 0, 0, 0}, b_by_dst = {NULL, 0, 0, 0}; + struct string_list a_by_dst = STRING_LIST_INIT_NODUP; + struct string_list b_by_dst = STRING_LIST_INIT_NODUP; const struct rename *sre; for (i = 0; i < a_renames->nr; i++) { @@@ -1019,22 -1019,14 +1019,22 @@@ if (mfi.clean && sha_eq(mfi.sha, ren1->pair->two->sha1) && - mfi.mode == ren1->pair->two->mode) + mfi.mode == ren1->pair->two->mode) { /* - * This messaged is part of + * This message is part of * t6022 test. If you change * it update the test too. */ output(o, 3, "Skipped %s (merged same as existing)", ren1_dst); - else { + + /* There may be higher stage entries left + * in the index (e.g. due to a D/F + * conflict) that need to be resolved. + */ + if (!ren1->dst_entry->stages[2].mode != + !ren1->dst_entry->stages[3].mode) + ren1->dst_entry->processed = 0; + } else { if (mfi.merge || !mfi.clean) output(o, 1, "Renaming %s => %s", ren1_src, ren1_dst); if (mfi.merge) @@@ -1064,53 -1056,6 +1064,53 @@@ static unsigned char *stage_sha(const u return (is_null_sha1(sha) || mode == 0) ? NULL: (unsigned char *)sha; } +static int read_sha1_strbuf(const unsigned char *sha1, struct strbuf *dst) +{ + void *buf; + enum object_type type; + unsigned long size; + buf = read_sha1_file(sha1, &type, &size); + if (!buf) + return error("cannot read object %s", sha1_to_hex(sha1)); + if (type != OBJ_BLOB) { + free(buf); + return error("object %s is not a blob", sha1_to_hex(sha1)); + } + strbuf_attach(dst, buf, size, size + 1); + return 0; +} + +static int blob_unchanged(const unsigned char *o_sha, + const unsigned char *a_sha, + int renormalize, const char *path) +{ + struct strbuf o = STRBUF_INIT; + struct strbuf a = STRBUF_INIT; + int ret = 0; /* assume changed for safety */ + + if (sha_eq(o_sha, a_sha)) + return 1; + if (!renormalize) + return 0; + + assert(o_sha && a_sha); + if (read_sha1_strbuf(o_sha, &o) || read_sha1_strbuf(a_sha, &a)) + goto error_return; + /* + * Note: binary | is used so that both renormalizations are + * performed. Comparison can be skipped if both files are + * unchanged since their sha1s have already been compared. + */ + if (renormalize_buffer(path, o.buf, o.len, &o) | + renormalize_buffer(path, a.buf, o.len, &a)) + ret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len)); + +error_return: + strbuf_release(&o); + strbuf_release(&a); + return ret; +} + /* Per entry merge function */ static int process_entry(struct merge_options *o, const char *path, struct stage_data *entry) @@@ -1120,7 -1065,6 +1120,7 @@@ print_index_entry("\tpath: ", entry); */ int clean_merge = 1; + int normalize = o->renormalize; unsigned o_mode = entry->stages[1].mode; unsigned a_mode = entry->stages[2].mode; unsigned b_mode = entry->stages[3].mode; @@@ -1128,12 -1072,11 +1128,12 @@@ unsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode); unsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode); + entry->processed = 1; if (o_sha && (!a_sha || !b_sha)) { /* Case A: Deleted in one */ if ((!a_sha && !b_sha) || - (sha_eq(a_sha, o_sha) && !b_sha) || - (!a_sha && sha_eq(b_sha, o_sha))) { + (!b_sha && blob_unchanged(o_sha, a_sha, normalize, path)) || + (!a_sha && blob_unchanged(o_sha, b_sha, normalize, path))) { /* Deleted in both or deleted in one and * unchanged in the other */ if (a_sha) @@@ -1161,28 -1104,33 +1161,28 @@@ } else if ((!o_sha && a_sha && !b_sha) || (!o_sha && !a_sha && b_sha)) { /* Case B: Added in one. */ - const char *add_branch; - const char *other_branch; unsigned mode; const unsigned char *sha; - const char *conf; if (a_sha) { - add_branch = o->branch1; - other_branch = o->branch2; mode = a_mode; sha = a_sha; - conf = "file/directory"; } else { - add_branch = o->branch2; - other_branch = o->branch1; mode = b_mode; sha = b_sha; - conf = "directory/file"; } if (string_list_has_string(&o->current_directory_set, path)) { - const char *new_path = unique_path(o, path, add_branch); - clean_merge = 0; - output(o, 1, "CONFLICT (%s): There is a directory with name %s in %s. " - "Adding %s as %s", - conf, path, other_branch, path, new_path); - remove_file(o, 0, path, 0); - update_file(o, 0, sha, mode, new_path); + /* Handle D->F conflicts after all subfiles */ + entry->processed = 0; + /* But get any file out of the way now, so conflicted + * entries below the directory of the same name can + * be put in the working directory. + */ + if (a_sha) + output(o, 2, "Removing %s", path); + /* do not touch working file if it did not exist */ + remove_file(o, 0, path, !a_sha); + return 1; /* Assume clean till processed */ } else { output(o, 2, "Adding %s", path); update_file(o, 1, sha, mode, path); @@@ -1230,108 -1178,6 +1230,64 @@@ return clean_merge; } +/* + * Per entry merge function for D/F conflicts, to be called only after + * all files below dir have been processed. We do this because in the + * cases we can cleanly resolve D/F conflicts, process_entry() can clean + * out all the files below the directory for us. + */ +static int process_df_entry(struct merge_options *o, + const char *path, struct stage_data *entry) +{ + int clean_merge = 1; + unsigned o_mode = entry->stages[1].mode; + unsigned a_mode = entry->stages[2].mode; + unsigned b_mode = entry->stages[3].mode; + unsigned char *o_sha = stage_sha(entry->stages[1].sha, o_mode); + unsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode); + unsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode); + const char *add_branch; + const char *other_branch; + unsigned mode; + const unsigned char *sha; + const char *conf; + struct stat st; + + /* We currently only handle D->F cases */ + assert((!o_sha && a_sha && !b_sha) || + (!o_sha && !a_sha && b_sha)); + + entry->processed = 1; + + if (a_sha) { + add_branch = o->branch1; + other_branch = o->branch2; + mode = a_mode; + sha = a_sha; + conf = "file/directory"; + } else { + add_branch = o->branch2; + other_branch = o->branch1; + mode = b_mode; + sha = b_sha; + conf = "directory/file"; + } + if (lstat(path, &st) == 0 && S_ISDIR(st.st_mode)) { + const char *new_path = unique_path(o, path, add_branch); + clean_merge = 0; + output(o, 1, "CONFLICT (%s): There is a directory with name %s in %s. " + "Adding %s as %s", + conf, path, other_branch, path, new_path); + remove_file(o, 0, path, 0); + update_file(o, 0, sha, mode, new_path); + } else { + output(o, 2, "Adding %s", path); + update_file(o, 1, sha, mode, path); + } + + return clean_merge; +} + - void set_porcelain_error_msgs(const char **msgs, const char *cmd) - { - const char *msg; - char *tmp; - const char *cmd2 = strcmp(cmd, "checkout") ? cmd : "switch branches"; - if (advice_commit_before_merge) - msg = "Your local changes to the following files would be overwritten by %s:\n%%s" - "Please, commit your changes or stash them before you can %s."; - else - msg = "Your local changes to the following files would be overwritten by %s:\n%%s"; - tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 2); - sprintf(tmp, msg, cmd, cmd2); - msgs[ERROR_WOULD_OVERWRITE] = tmp; - msgs[ERROR_NOT_UPTODATE_FILE] = tmp; - - msgs[ERROR_NOT_UPTODATE_DIR] = - "Updating the following directories would lose untracked files in it:\n%s"; - - if (advice_commit_before_merge) - msg = "The following untracked working tree files would be %s by %s:\n%%s" - "Please move or remove them before you can %s."; - else - msg = "The following untracked working tree files would be %s by %s:\n%%s"; - tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("removed") + strlen(cmd2) - 4); - sprintf(tmp, msg, "removed", cmd, cmd2); - msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = tmp; - tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("overwritten") + strlen(cmd2) - 4); - sprintf(tmp, msg, "overwritten", cmd, cmd2); - msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = tmp; - - /* - * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we - * cannot easily display it as a list. - */ - msgs[ERROR_BIND_OVERLAP] = "Entry '%s' overlaps with '%s'. Cannot bind."; - - msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] = - "Cannot update sparse checkout: the following entries are not up-to-date:\n%s"; - msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] = - "The following Working tree files would be overwritten by sparse checkout update:\n%s"; - msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] = - "The following Working tree files would be removed by sparse checkout update:\n%s"; - } - int merge_trees(struct merge_options *o, struct tree *head, struct tree *merge, @@@ -1381,13 -1227,6 +1337,13 @@@ && !process_entry(o, path, e)) clean = 0; } + for (i = 0; i < entries->nr; i++) { + const char *path = entries->items[i].string; + struct stage_data *e = entries->items[i].util; + if (!e->processed + && !process_df_entry(o, path, e)) + clean = 0; + } string_list_clear(re_merge, 0); string_list_clear(re_head, 0); @@@ -1575,7 -1414,6 +1531,7 @@@ void init_merge_options(struct merge_op o->buffer_output = 1; o->diff_rename_limit = -1; o->merge_rename_limit = -1; + o->renormalize = 0; git_config(merge_recursive_config, o); if (getenv("GIT_MERGE_VERBOSITY")) o->verbosity = diff --combined merge-recursive.h index 196f053106,f79917cef4..34492dbd6e --- a/merge-recursive.h +++ b/merge-recursive.h @@@ -14,7 -14,6 +14,7 @@@ struct merge_options } recursive_variant; const char *subtree_shift; unsigned buffer_output : 1; + unsigned renormalize : 1; int verbosity; int diff_rename_limit; int merge_rename_limit; @@@ -24,12 -23,6 +24,6 @@@ struct string_list current_directory_set; }; - /* - * Sets the list of user-friendly error messages to be used by the - * command "cmd" (either merge or checkout) - */ - void set_porcelain_error_msgs(const char **msgs, const char *cmd); - /* merge_trees() but with recursive ancestor consolidation */ int merge_recursive(struct merge_options *o, struct commit *h1, diff --combined unpack-trees.c index 3c7a7c9cde,d0c5d1c4d5..803445aa7b --- a/unpack-trees.c +++ b/unpack-trees.c @@@ -14,7 -14,7 +14,7 @@@ * read-tree. Non-scripted Porcelain is not required to use these messages * and in fact are encouraged to reword them to better suit their particular * situation better. See how "git checkout" and "git merge" replaces - * them using set_porcelain_error_msgs(), for example. + * them using setup_unpack_trees_porcelain(), for example. */ const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = { /* ERROR_WOULD_OVERWRITE */ @@@ -50,6 -50,54 +50,54 @@@ ? ((o)->msgs[(type)]) \ : (unpack_plumbing_errors[(type)]) ) + void setup_unpack_trees_porcelain(struct unpack_trees_options *opts, + const char *cmd) + { + const char **msgs = opts->msgs; + const char *msg; + char *tmp; + const char *cmd2 = strcmp(cmd, "checkout") ? cmd : "switch branches"; + if (advice_commit_before_merge) + msg = "Your local changes to the following files would be overwritten by %s:\n%%s" + "Please, commit your changes or stash them before you can %s."; + else + msg = "Your local changes to the following files would be overwritten by %s:\n%%s"; + tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 2); + sprintf(tmp, msg, cmd, cmd2); + msgs[ERROR_WOULD_OVERWRITE] = tmp; + msgs[ERROR_NOT_UPTODATE_FILE] = tmp; + + msgs[ERROR_NOT_UPTODATE_DIR] = + "Updating the following directories would lose untracked files in it:\n%s"; + + if (advice_commit_before_merge) + msg = "The following untracked working tree files would be %s by %s:\n%%s" + "Please move or remove them before you can %s."; + else + msg = "The following untracked working tree files would be %s by %s:\n%%s"; + tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("removed") + strlen(cmd2) - 4); + sprintf(tmp, msg, "removed", cmd, cmd2); + msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = tmp; + tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("overwritten") + strlen(cmd2) - 4); + sprintf(tmp, msg, "overwritten", cmd, cmd2); + msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = tmp; + + /* + * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we + * cannot easily display it as a list. + */ + msgs[ERROR_BIND_OVERLAP] = "Entry '%s' overlaps with '%s'. Cannot bind."; + + msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] = + "Cannot update sparse checkout: the following entries are not up-to-date:\n%s"; + msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] = + "The following Working tree files would be overwritten by sparse checkout update:\n%s"; + msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] = + "The following Working tree files would be removed by sparse checkout update:\n%s"; + + opts->show_all_errors = 1; + } + static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce, unsigned int set, unsigned int clear) { @@@ -58,9 -106,6 +106,9 @@@ clear |= CE_HASHED | CE_UNHASHED; + if (set & CE_REMOVE) + set |= CE_WT_REMOVE; + memcpy(new, ce, size); new->next = NULL; new->ce_flags = (new->ce_flags & ~clear) | set; @@@ -77,12 -122,6 +125,6 @@@ static int add_rejected_path(struct unp const char *path) { struct rejected_paths_list *newentry; - int porcelain = o && (o)->msgs[e]; - /* - * simply display the given error message if in plumbing mode - */ - if (!porcelain) - o->show_all_errors = 0; if (!o->show_all_errors) return error(ERRORMSG(o, e), path); @@@ -159,7 -198,7 +201,7 @@@ static int check_updates(struct unpack_ if (o->update && o->verbose_update) { for (total = cnt = 0; cnt < index->cache_nr; cnt++) { struct cache_entry *ce = index->cache[cnt]; - if (ce->ce_flags & (CE_UPDATE | CE_REMOVE | CE_WT_REMOVE)) + if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE)) total++; } @@@ -179,6 -218,12 +221,6 @@@ unlink_entry(ce); continue; } - - if (ce->ce_flags & CE_REMOVE) { - display_progress(progress, ++cnt); - if (o->update) - unlink_entry(ce); - } } remove_marked_cache_entries(&o->result); remove_scheduled_dirs(); @@@ -207,6 -252,9 +249,6 @@@ static int will_have_skip_worktree(cons { const char *basename; - if (ce_stage(ce)) - return 0; - basename = strrchr(ce->name, '/'); basename = basename ? basename+1 : ce->name; return excluded_from_list(ce->name, ce_namelen(ce), basename, NULL, o->el) <= 0; @@@ -216,36 -264,19 +258,36 @@@ static int apply_sparse_checkout(struc { int was_skip_worktree = ce_skip_worktree(ce); - if (will_have_skip_worktree(ce, o)) + if (!ce_stage(ce) && will_have_skip_worktree(ce, o)) ce->ce_flags |= CE_SKIP_WORKTREE; else ce->ce_flags &= ~CE_SKIP_WORKTREE; /* - * We only care about files getting into the checkout area - * If merge strategies want to remove some, go ahead, this - * flag will be removed eventually in unpack_trees() if it's - * outside checkout area. + * if (!was_skip_worktree && !ce_skip_worktree()) { + * This is perfectly normal. Move on; + * } */ - if (ce->ce_flags & CE_REMOVE) - return 0; + + /* + * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout + * area as a result of ce_skip_worktree() shortcuts in + * verify_absent() and verify_uptodate(). + * Make sure they don't modify worktree if they are already + * outside checkout area + */ + if (was_skip_worktree && ce_skip_worktree(ce)) { + ce->ce_flags &= ~CE_UPDATE; + + /* + * By default, when CE_REMOVE is on, CE_WT_REMOVE is also + * on to get that file removed from both index and worktree. + * If that file is already outside worktree area, don't + * bother remove it. + */ + if (ce->ce_flags & CE_REMOVE) + ce->ce_flags &= ~CE_WT_REMOVE; + } if (!was_skip_worktree && ce_skip_worktree(ce)) { /* @@@ -412,7 -443,6 +454,7 @@@ static int traverse_trees_recursive(in { int i, ret, bottom; struct tree_desc t[MAX_UNPACK_TREES]; + void *buf[MAX_UNPACK_TREES]; struct traverse_info newinfo; struct name_entry *p; @@@ -430,16 -460,12 +472,16 @@@ const unsigned char *sha1 = NULL; if (dirmask & 1) sha1 = names[i].sha1; - fill_tree_descriptor(t+i, sha1); + buf[i] = fill_tree_descriptor(t+i, sha1); } bottom = switch_cache_bottom(&newinfo); ret = traverse_trees(n, t, &newinfo); restore_cache_bottom(&newinfo, bottom); + + for (i = 0; i < n; i++) + free(buf[i]); + return ret; } @@@ -887,7 -913,14 +929,7 @@@ int unpack_trees(unsigned len, struct t ret = -1; goto done; } - /* - * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout - * area as a result of ce_skip_worktree() shortcuts in - * verify_absent() and verify_uptodate(). Clear them. - */ - if (ce_skip_worktree(ce)) - ce->ce_flags &= ~(CE_UPDATE | CE_REMOVE); - else + if (!ce_skip_worktree(ce)) empty_worktree = 0; } @@@ -1186,8 -1219,6 +1228,8 @@@ static int merged_entry(struct cache_en if (!old) { if (verify_absent(merge, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) return -1; + if (!o->skip_sparse_checkout && will_have_skip_worktree(merge, o)) + update |= CE_SKIP_WORKTREE; invalidate_ce_path(merge, o); } else if (!(old->ce_flags & CE_CONFLICTED)) { /*