From: Junio C Hamano Date: Wed, 15 Nov 2017 03:14:36 +0000 (+0900) Subject: Merge branch 'sb/bisect-run-empty' X-Git-Tag: v2.16.0-rc0~119 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/5066a008bb6a810f908c020de02a646cf3c92f34?hp=fecd2dd36e6210c0211c812f1be2a07c71c6dcdc Merge branch 'sb/bisect-run-empty' "git bisect run" that did not specify any command to run used to go ahead and treated all commits to be tested as 'good'. This has been corrected by making the command error out. * sb/bisect-run-empty: bisect run: die if no command is given --- diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index 6c42abf070..4a1417bdcd 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -23,7 +23,7 @@ on the subcommand: git bisect terms [--term-good | --term-bad] git bisect skip [(|)...] git bisect reset [] - git bisect visualize + git bisect (visualize|view) git bisect replay git bisect log git bisect run ... @@ -193,24 +193,23 @@ git bisect start --term-new fixed --term-old broken Then, use `git bisect ` and `git bisect ` instead of `git bisect good` and `git bisect bad` to mark commits. -Bisect visualize -~~~~~~~~~~~~~~~~ +Bisect visualize/view +~~~~~~~~~~~~~~~~~~~~~ To see the currently remaining suspects in 'gitk', issue the following -command during the bisection process: +command during the bisection process (the subcommand `view` can be used +as an alternative to `visualize`): ------------ $ git bisect visualize ------------ -`view` may also be used as a synonym for `visualize`. - If the `DISPLAY` environment variable is not set, 'git log' is used instead. You can also give command-line options such as `-p` and `--stat`. ------------ -$ git bisect view --stat +$ git bisect visualize --stat ------------ Bisect log and bisect replay diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 1d420e4cde..dffa14a795 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -145,18 +145,25 @@ upstream:: (behind), "<>" (ahead and behind), or "=" (in sync). `:track` also prints "[gone]" whenever unknown upstream ref is encountered. Append `:track,nobracket` to show tracking - information without brackets (i.e "ahead N, behind M"). Has - no effect if the ref does not have tracking information - associated with it. All the options apart from `nobracket` - are mutually exclusive, but if used together the last option - is selected. + information without brackets (i.e "ahead N, behind M"). ++ +For any remote-tracking branch `%(upstream)`, `%(upstream:remotename)` +and `%(upstream:remoteref)` refer to the name of the remote and the +name of the tracked remote ref, respectively. In other words, the +remote-tracking branch can be updated explicitly and individually by +using the refspec `%(upstream:remoteref):%(upstream)` to fetch from +`%(upstream:remotename)`. ++ +Has no effect if the ref does not have tracking information associated +with it. All the options apart from `nobracket` are mutually exclusive, +but if used together the last option is selected. push:: The name of a local ref which represents the `@{push}` location for the displayed ref. Respects `:short`, `:lstrip`, - `:rstrip`, `:track`, and `:trackshort` options as `upstream` - does. Produces an empty string if no `@{push}` ref is - configured. + `:rstrip`, `:track`, `:trackshort`, `:remotename`, and `:remoteref` + options as `upstream` does. Produces an empty string if no `@{push}` + ref is configured. HEAD:: '*' if HEAD matches current ref (the checked out branch), ' ' diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt index 9f3a78a36c..fc282e0a92 100644 --- a/Documentation/git-status.txt +++ b/Documentation/git-status.txt @@ -97,8 +97,27 @@ configuration variable documented in linkgit:git-config[1]. (and suppresses the output of submodule summaries when the config option `status.submoduleSummary` is set). ---ignored:: +--ignored[=]:: Show ignored files as well. ++ +The mode parameter is used to specify the handling of ignored files. +It is optional: it defaults to 'traditional'. ++ +The possible options are: ++ + - 'traditional' - Shows ignored files and directories, unless + --untracked-files=all is specifed, in which case + individual files in ignored directories are + displayed. + - 'no' - Show no ignored files. + - 'matching' - Shows ignored files and directories matching an + ignore pattern. ++ +When 'matching' mode is specified, paths that explicity match an +ignored pattern are shown. If a directory matches an ignore pattern, +then it is shown, but not paths contained in the ignored directory. If +a directory does not match an ignore pattern, but all contents are +ignored, then the directory is not shown, but all contents are shown. -z:: Terminate entries with NUL, instead of LF. This implies diff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt index 6c77b4920c..7fae00f44f 100644 --- a/Documentation/technical/api-directory-listing.txt +++ b/Documentation/technical/api-directory-listing.txt @@ -22,16 +22,20 @@ The notable options are: `flags`:: - A bit-field of options (the `*IGNORED*` flags are mutually exclusive): + A bit-field of options: `DIR_SHOW_IGNORED`::: - Return just ignored files in `entries[]`, not untracked files. + Return just ignored files in `entries[]`, not untracked + files. This flag is mutually exclusive with + `DIR_SHOW_IGNORED_TOO`. `DIR_SHOW_IGNORED_TOO`::: - Similar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]` - in addition to untracked files in `entries[]`. + Similar to `DIR_SHOW_IGNORED`, but return ignored files in + `ignored[]` in addition to untracked files in + `entries[]`. This flag is mutually exclusive with + `DIR_SHOW_IGNORED`. `DIR_KEEP_UNTRACKED_CONTENTS`::: @@ -39,6 +43,21 @@ The notable options are: untracked contents of untracked directories are also returned in `entries[]`. +`DIR_SHOW_IGNORED_TOO_MODE_MATCHING`::: + + Only has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if + this is set, returns ignored files and directories that match + an exclude pattern. If a directory matches an exclude pattern, + then the directory is returned and the contained paths are + not. A directory that does not match an exclude pattern will + not be returned even if all of its contents are ignored. In + this case, the contents are returned as individual entries. ++ +If this is set, files and directories that explicity match an ignore +pattern are reported. Implicity ignored directories (directories that +do not match an ignore pattern, but whose contents are all ignored) +are not reported, instead all of the contents are reported. + `DIR_COLLECT_IGNORED`::: Special mode for git-add. Return ignored files in `ignored[]` and diff --git a/bisect.c b/bisect.c index fda2c4a186..0fca17c02b 100644 --- a/bisect.c +++ b/bisect.c @@ -226,10 +226,11 @@ static struct commit_list *best_bisection_sorted(struct commit_list *list, int n add_name_decoration(DECORATION_NONE, buf.buf, obj); p->item = array[i].commit; - p = p->next; + if (i < cnt - 1) + p = p->next; } - if (p) - p->next = NULL; + free_commit_list(p->next); + p->next = NULL; strbuf_release(&buf); free(array); return list; @@ -360,28 +361,29 @@ static struct commit_list *do_find_bisection(struct commit_list *list, return best_bisection_sorted(list, nr); } -struct commit_list *find_bisection(struct commit_list *list, - int *reaches, int *all, - int find_all) +void find_bisection(struct commit_list **commit_list, int *reaches, + int *all, int find_all) { int nr, on_list; - struct commit_list *p, *best, *next, *last; + struct commit_list *list, *p, *best, *next, *last; int *weights; - show_list("bisection 2 entry", 0, 0, list); + show_list("bisection 2 entry", 0, 0, *commit_list); /* * Count the number of total and tree-changing items on the * list, while reversing the list. */ - for (nr = on_list = 0, last = NULL, p = list; + for (nr = on_list = 0, last = NULL, p = *commit_list; p; p = next) { unsigned flags = p->item->object.flags; next = p->next; - if (flags & UNINTERESTING) + if (flags & UNINTERESTING) { + free(p); continue; + } p->next = last; last = p; if (!(flags & TREESAME)) @@ -397,12 +399,16 @@ struct commit_list *find_bisection(struct commit_list *list, /* Do the real work of finding bisection commit. */ best = do_find_bisection(list, nr, weights, find_all); if (best) { - if (!find_all) + if (!find_all) { + list->item = best->item; + free_commit_list(list->next); + best = list; best->next = NULL; + } *reaches = weight(best); } free(weights); - return best; + *commit_list = best; } static int register_ref(const char *refname, const struct object_id *oid, @@ -960,8 +966,7 @@ int bisect_next_all(const char *prefix, int no_checkout) bisect_common(&revs); - revs.commits = find_bisection(revs.commits, &reaches, &all, - !!skipped_revs.nr); + find_bisection(&revs.commits, &reaches, &all, !!skipped_revs.nr); revs.commits = managed_skipped(revs.commits, &tried); if (!revs.commits) { @@ -1068,7 +1073,7 @@ int bisect_clean_state(void) struct string_list refs_for_removal = STRING_LIST_INIT_NODUP; for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal); string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD")); - result = delete_refs("bisect: remove", &refs_for_removal, REF_NODEREF); + result = delete_refs("bisect: remove", &refs_for_removal, REF_NO_DEREF); refs_for_removal.strdup_strings = 1; string_list_clear(&refs_for_removal, 0); unlink_or_warn(git_path_bisect_expected_rev()); diff --git a/bisect.h b/bisect.h index 0ae63d4616..a5d9248a47 100644 --- a/bisect.h +++ b/bisect.h @@ -1,9 +1,15 @@ #ifndef BISECT_H #define BISECT_H -extern struct commit_list *find_bisection(struct commit_list *list, - int *reaches, int *all, - int find_all); +/* + * Find bisection. If something is found, `reaches` will be the number of + * commits that the best commit reaches. `all` will be the count of + * non-SAMETREE commits. If nothing is found, `list` will be NULL. + * Otherwise, it will be either all non-SAMETREE commits or the single + * best commit, as chosen by `find_all`. + */ +extern void find_bisection(struct commit_list **list, int *reaches, int *all, + int find_all); extern struct commit_list *filter_skipped(struct commit_list *list, struct commit_list **tried, diff --git a/builtin/am.c b/builtin/am.c index 92c4853505..02853b3e05 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -2148,7 +2148,7 @@ static void am_abort(struct am_state *state) has_curr_head ? &curr_head : NULL, 0, UPDATE_REFS_DIE_ON_ERR); else if (curr_branch) - delete_ref(NULL, curr_branch, NULL, REF_NODEREF); + delete_ref(NULL, curr_branch, NULL, REF_NO_DEREF); free(curr_branch); am_destroy(state); diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c index 35d2105f94..4b5fadcbe1 100644 --- a/builtin/bisect--helper.c +++ b/builtin/bisect--helper.c @@ -46,7 +46,7 @@ static int check_term_format(const char *term, const char *orig_term) return error(_("'%s' is not a valid term"), term); if (one_of(term, "help", "start", "skip", "next", "reset", - "visualize", "replay", "log", "run", "terms", NULL)) + "visualize", "view", "replay", "log", "run", "terms", NULL)) return error(_("can't use the builtin command '%s' as a term"), term); /* diff --git a/builtin/branch.c b/builtin/branch.c index b1ed649300..33fd5fcfd1 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -258,7 +258,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, } if (delete_ref(NULL, name, is_null_oid(&oid) ? NULL : &oid, - REF_NODEREF)) { + REF_NO_DEREF)) { error(remote_branch ? _("Error deleting remote-tracking branch '%s'") : _("Error deleting branch '%s'"), diff --git a/builtin/checkout.c b/builtin/checkout.c index 6c2b4cd419..7d8bcc3833 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -663,7 +663,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts, /* Nothing to do. */ } else if (opts->force_detach || !new->path) { /* No longer on any branch. */ update_ref(msg.buf, "HEAD", &new->commit->object.oid, NULL, - REF_NODEREF, UPDATE_REFS_DIE_ON_ERR); + REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR); if (!opts->quiet) { if (old->path && advice_detached_head && !opts->force_detach) diff --git a/builtin/clone.c b/builtin/clone.c index cf6eddc9c5..b22845738a 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -689,7 +689,7 @@ static void update_head(const struct ref *our, const struct ref *remote, } else if (our) { struct commit *c = lookup_commit_reference(&our->old_oid); /* --branch specifies a non-branch (i.e. tags), detach HEAD */ - update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NODEREF, + update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR); } else if (remote) { /* @@ -697,7 +697,7 @@ static void update_head(const struct ref *our, const struct ref *remote, * HEAD points to a branch but we don't know which one. * Detach HEAD in all these cases. */ - update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NODEREF, + update_ref(msg, "HEAD", &remote->old_oid, NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR); } } diff --git a/builtin/commit.c b/builtin/commit.c index 605ea8c0e9..8a87701414 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -118,7 +118,7 @@ static int edit_flag = -1; /* unspecified */ static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship; static int config_commit_verbose = -1; /* unspecified */ static int no_post_rewrite, allow_empty_message; -static char *untracked_files_arg, *force_date, *ignore_submodule_arg; +static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg; static char *sign_commit; /* @@ -139,7 +139,7 @@ static const char *cleanup_arg; static enum commit_whence whence; static int sequencer_in_use; static int use_editor = 1, include_status = 1; -static int show_ignored_in_status, have_option_m; +static int have_option_m; static struct strbuf message = STRBUF_INIT; static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED; @@ -1076,6 +1076,19 @@ static const char *find_author_by_nickname(const char *name) die(_("--author '%s' is not 'Name ' and matches no existing author"), name); } +static void handle_ignored_arg(struct wt_status *s) +{ + if (!ignored_arg) + ; /* default already initialized */ + else if (!strcmp(ignored_arg, "traditional")) + s->show_ignored_mode = SHOW_TRADITIONAL_IGNORED; + else if (!strcmp(ignored_arg, "no")) + s->show_ignored_mode = SHOW_NO_IGNORED; + else if (!strcmp(ignored_arg, "matching")) + s->show_ignored_mode = SHOW_MATCHING_IGNORED; + else + die(_("Invalid ignored mode '%s'"), ignored_arg); +} static void handle_untracked_files_arg(struct wt_status *s) { @@ -1364,8 +1377,10 @@ int cmd_status(int argc, const char **argv, const char *prefix) N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, - OPT_BOOL(0, "ignored", &show_ignored_in_status, - N_("show ignored files")), + { OPTION_STRING, 0, "ignored", &ignored_arg, + N_("mode"), + N_("show ignored files, optional modes: traditional, matching, no. (Default: traditional)"), + PARSE_OPT_OPTARG, NULL, (intptr_t)"traditional" }, { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, N_("when"), N_("ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, @@ -1384,8 +1399,12 @@ int cmd_status(int argc, const char **argv, const char *prefix) finalize_deferred_config(&s); handle_untracked_files_arg(&s); - if (show_ignored_in_status) - s.show_ignored_files = 1; + handle_ignored_arg(&s); + + if (s.show_ignored_mode == SHOW_MATCHING_IGNORED && + s.show_untracked_files == SHOW_NO_UNTRACKED_FILES) + die(_("Unsupported combination of ignored and untracked-files arguments")); + parse_pathspec(&s.pathspec, 0, PATHSPEC_PREFER_FULL, prefix, argv); @@ -1731,7 +1750,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) allow_fast_forward = 0; } if (allow_fast_forward) - parents = reduce_heads(parents); + reduce_heads_replace(&parents); } else { if (!reflog_msg) reflog_msg = (whence == FROM_CHERRY_PICK) diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index b69f7d3be2..22034f87e7 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -571,7 +571,7 @@ static void find_merge_parents(struct merge_parents *result, head_commit = lookup_commit(head); if (head_commit) commit_list_insert(head_commit, &parents); - parents = reduce_heads(parents); + reduce_heads_replace(&parents); while (parents) { struct commit *cmit = pop_commit(&parents); diff --git a/builtin/merge-base.c b/builtin/merge-base.c index e99f5405ce..3b7600150b 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -9,20 +9,20 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all) { - struct commit_list *result; + struct commit_list *result, *r; result = get_merge_bases_many_dirty(rev[0], rev_nr - 1, rev + 1); if (!result) return 1; - while (result) { - printf("%s\n", oid_to_hex(&result->item->object.oid)); + for (r = result; r; r = r->next) { + printf("%s\n", oid_to_hex(&r->item->object.oid)); if (!show_all) - return 0; - result = result->next; + break; } + free_commit_list(result); return 0; } @@ -51,45 +51,47 @@ static struct commit *get_commit_reference(const char *arg) static int handle_independent(int count, const char **args) { - struct commit_list *revs = NULL; - struct commit_list *result; + struct commit_list *revs = NULL, *rev; int i; for (i = count - 1; i >= 0; i--) commit_list_insert(get_commit_reference(args[i]), &revs); - result = reduce_heads(revs); - if (!result) + reduce_heads_replace(&revs); + + if (!revs) return 1; - while (result) { - printf("%s\n", oid_to_hex(&result->item->object.oid)); - result = result->next; - } + for (rev = revs; rev; rev = rev->next) + printf("%s\n", oid_to_hex(&rev->item->object.oid)); + + free_commit_list(revs); return 0; } static int handle_octopus(int count, const char **args, int show_all) { struct commit_list *revs = NULL; - struct commit_list *result; + struct commit_list *result, *rev; int i; for (i = count - 1; i >= 0; i--) commit_list_insert(get_commit_reference(args[i]), &revs); - result = reduce_heads(get_octopus_merge_bases(revs)); + result = get_octopus_merge_bases(revs); + free_commit_list(revs); + reduce_heads_replace(&result); if (!result) return 1; - while (result) { - printf("%s\n", oid_to_hex(&result->item->object.oid)); + for (rev = result; rev; rev = rev->next) { + printf("%s\n", oid_to_hex(&rev->item->object.oid)); if (!show_all) - return 0; - result = result->next; + break; } + free_commit_list(result); return 0; } diff --git a/builtin/merge.c b/builtin/merge.c index 6071dbfe34..612dd7bfb6 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -998,6 +998,7 @@ static struct commit_list *reduce_parents(struct commit *head_commit, /* Find what parents to record by checking independent ones. */ parents = reduce_heads(remoteheads); + free_commit_list(remoteheads); remoteheads = NULL; remotes = &remoteheads; diff --git a/builtin/notes.c b/builtin/notes.c index 12afdf1907..d7754db143 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -686,7 +686,7 @@ static int merge_abort(struct notes_merge_options *o) if (delete_ref(NULL, "NOTES_MERGE_PARTIAL", NULL, 0)) ret += error(_("failed to delete ref NOTES_MERGE_PARTIAL")); - if (delete_ref(NULL, "NOTES_MERGE_REF", NULL, REF_NODEREF)) + if (delete_ref(NULL, "NOTES_MERGE_REF", NULL, REF_NO_DEREF)) ret += error(_("failed to delete ref NOTES_MERGE_REF")); if (notes_merge_abort(o)) ret += error(_("failed to remove 'git notes merge' worktree")); diff --git a/builtin/pull.c b/builtin/pull.c index a28f0ffadd..f7e2c4f2ec 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -751,12 +751,15 @@ static int get_octopus_merge_base(struct object_id *merge_base, if (!is_null_oid(fork_point)) commit_list_insert(lookup_commit_reference(fork_point), &revs); - result = reduce_heads(get_octopus_merge_bases(revs)); + result = get_octopus_merge_bases(revs); free_commit_list(revs); + reduce_heads_replace(&result); + if (!result) return 1; oidcpy(merge_base, &result->item->object.oid); + free_commit_list(result); return 0; } diff --git a/builtin/remote.c b/builtin/remote.c index a04ea50e40..d95bf904c3 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -693,7 +693,7 @@ static int mv(int argc, const char **argv) read_ref_full(item->string, RESOLVE_REF_READING, &oid, &flag); if (!(flag & REF_ISSYMREF)) continue; - if (delete_ref(NULL, item->string, NULL, REF_NODEREF)) + if (delete_ref(NULL, item->string, NULL, REF_NO_DEREF)) die(_("deleting '%s' failed"), item->string); } for (i = 0; i < remote_branches.nr; i++) { @@ -788,7 +788,7 @@ static int rm(int argc, const char **argv) strbuf_release(&buf); if (!result) - result = delete_refs("remote: remove", &branches, REF_NODEREF); + result = delete_refs("remote: remove", &branches, REF_NO_DEREF); string_list_clear(&branches, 0); if (skipped.nr) { @@ -1255,7 +1255,7 @@ static int set_head(int argc, const char **argv) head_name = xstrdup(states.heads.items[0].string); free_remote_ref_states(&states); } else if (opt_d && !opt_a && argc == 1) { - if (delete_ref(NULL, buf.buf, NULL, REF_NODEREF)) + if (delete_ref(NULL, buf.buf, NULL, REF_NO_DEREF)) result |= error(_("Could not delete %s"), buf.buf); } else usage_with_options(builtin_remote_sethead_usage, options); diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 8034d2eff2..4032eb3811 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -397,8 +397,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) if (bisect_list) { int reaches = reaches, all = all; - revs.commits = find_bisection(revs.commits, &reaches, &all, - bisect_find_all); + find_bisection(&revs.commits, &reaches, &all, bisect_find_all); if (bisect_show_vars) return show_bisect_vars(&info, reaches, all); diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c index 17aabaa679..80237f0df1 100644 --- a/builtin/symbolic-ref.c +++ b/builtin/symbolic-ref.c @@ -58,7 +58,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) die("Cannot delete %s, not a symbolic ref", argv[0]); if (!strcmp(argv[0], "HEAD")) die("deleting '%s' is not allowed", argv[0]); - return delete_ref(NULL, argv[0], NULL, REF_NODEREF); + return delete_ref(NULL, argv[0], NULL, REF_NO_DEREF); } switch (argc) { diff --git a/builtin/update-ref.c b/builtin/update-ref.c index cf1552b478..4b4714b3fd 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -312,7 +312,7 @@ static const char *parse_cmd_verify(struct ref_transaction *transaction, static const char *parse_cmd_option(struct strbuf *input, const char *next) { if (!strncmp(next, "no-deref", 8) && next[8] == line_termination) - update_flags |= REF_NODEREF; + update_flags |= REF_NO_DEREF; else die("option unknown: %s", next); return next + 8; @@ -427,7 +427,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) } if (no_deref) - flags = REF_NODEREF; + flags = REF_NO_DEREF; if (delete) /* * For purposes of backwards compatibility, we treat diff --git a/commit.c b/commit.c index 1e0e633790..cab8d4455b 100644 --- a/commit.c +++ b/commit.c @@ -1090,6 +1090,13 @@ struct commit_list *reduce_heads(struct commit_list *heads) return result; } +void reduce_heads_replace(struct commit_list **heads) +{ + struct commit_list *result = reduce_heads(*heads); + free_commit_list(*heads); + *heads = result; +} + static const char gpg_sig_header[] = "gpgsig"; static const int gpg_sig_header_len = sizeof(gpg_sig_header) - 1; diff --git a/commit.h b/commit.h index 6d769590f2..99a3fea68d 100644 --- a/commit.h +++ b/commit.h @@ -313,7 +313,23 @@ extern int interactive_add(int argc, const char **argv, const char *prefix, int extern int run_add_interactive(const char *revision, const char *patch_mode, const struct pathspec *pathspec); -struct commit_list *reduce_heads(struct commit_list *heads); +/* + * Takes a list of commits and returns a new list where those + * have been removed that can be reached from other commits in + * the list. It is useful for, e.g., reducing the commits + * randomly thrown at the git-merge command and removing + * redundant commits that the user shouldn't have given to it. + * + * This function destroys the STALE bit of the commit objects' + * flags. + */ +extern struct commit_list *reduce_heads(struct commit_list *heads); + +/* + * Like `reduce_heads()`, except it replaces the list. Use this + * instead of `foo = reduce_heads(foo);` to avoid memory leaks. + */ +extern void reduce_heads_replace(struct commit_list **heads); struct commit_extra_header { struct commit_extra_header *next; diff --git a/compat/obstack.c b/compat/obstack.c index e276ccd7b3..4d1d95beeb 100644 --- a/compat/obstack.c +++ b/compat/obstack.c @@ -14,9 +14,8 @@ Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ + License along with the GNU C Library; if not, see + . */ #include "git-compat-util.h" #include diff --git a/compat/obstack.h b/compat/obstack.h index ceb4bdbcdd..6bc24b7644 100644 --- a/compat/obstack.h +++ b/compat/obstack.h @@ -14,9 +14,8 @@ Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ + License along with the GNU C Library; if not, see + . */ /* Summary: diff --git a/compat/poll/poll.c b/compat/poll/poll.c index ae03b74a6f..7ed3fbbea1 100644 --- a/compat/poll/poll.c +++ b/compat/poll/poll.c @@ -16,8 +16,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + with this program; if not, see . */ /* Tell gcc not to warn about the (nfd < 0) tests, below. */ #if (__GNUC__ == 4 && 3 <= __GNUC_MINOR__) || 4 < __GNUC__ diff --git a/compat/poll/poll.h b/compat/poll/poll.h index b7aa59d973..cd1995292a 100644 --- a/compat/poll/poll.h +++ b/compat/poll/poll.h @@ -16,8 +16,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + with this program; if not, see . */ #ifndef _GL_POLL_H #define _GL_POLL_H diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c index d8bde06f1a..51cd60baa3 100644 --- a/compat/regex/regcomp.c +++ b/compat/regex/regcomp.c @@ -14,9 +14,8 @@ Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA. */ + License along with the GNU C Library; if not, see + . */ static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern, size_t length, reg_syntax_t syntax); diff --git a/compat/regex/regex.c b/compat/regex/regex.c index 5cb23e5d59..f3e03a9eab 100644 --- a/compat/regex/regex.c +++ b/compat/regex/regex.c @@ -14,9 +14,8 @@ Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA. */ + License along with the GNU C Library; if not, see + . */ #ifdef HAVE_CONFIG_H #include "config.h" diff --git a/compat/regex/regex.h b/compat/regex/regex.h index 61c9683872..4d81358a83 100644 --- a/compat/regex/regex.h +++ b/compat/regex/regex.h @@ -18,9 +18,8 @@ Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA. */ + License along with the GNU C Library; if not, see + . */ #ifndef _REGEX_H #define _REGEX_H 1 diff --git a/compat/regex/regex_internal.c b/compat/regex/regex_internal.c index 98342b8316..59bf151336 100644 --- a/compat/regex/regex_internal.c +++ b/compat/regex/regex_internal.c @@ -14,9 +14,8 @@ Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA. */ + License along with the GNU C Library; if not, see + . */ static void re_string_construct_common (const char *str, int len, re_string_t *pstr, diff --git a/compat/regex/regex_internal.h b/compat/regex/regex_internal.h index 4184d7f5a6..3ee8aae59d 100644 --- a/compat/regex/regex_internal.h +++ b/compat/regex/regex_internal.h @@ -14,9 +14,8 @@ Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ + License along with the GNU C Library; if not, see + . */ #ifndef _REGEX_INTERNAL_H #define _REGEX_INTERNAL_H 1 diff --git a/compat/regex/regexec.c b/compat/regex/regexec.c index 6f2b48a78b..1b5d89fd5e 100644 --- a/compat/regex/regexec.c +++ b/compat/regex/regexec.c @@ -14,9 +14,8 @@ Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA. */ + License along with the GNU C Library; if not, see + . */ static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags, int n) internal_function; diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index fdd984d34a..f07f16b28f 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -111,8 +111,7 @@ __git () # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# along with this program; if not, see . # # The latest version of this software can be obtained here: # diff --git a/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c b/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c index 2a317fca44..d389bfadce 100644 --- a/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c +++ b/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c @@ -13,8 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see . */ /* diff --git a/contrib/credential/libsecret/git-credential-libsecret.c b/contrib/credential/libsecret/git-credential-libsecret.c index b4750c9ee8..e6598b6383 100644 --- a/contrib/credential/libsecret/git-credential-libsecret.c +++ b/contrib/credential/libsecret/git-credential-libsecret.c @@ -14,8 +14,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see . */ /* diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el index e671f6c1c6..510e0f7103 100644 --- a/contrib/emacs/git-blame.el +++ b/contrib/emacs/git-blame.el @@ -25,9 +25,8 @@ ;; PURPOSE. See the GNU General Public License for more details. ;; You should have received a copy of the GNU General Public -;; License along with this program; if not, write to the Free -;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, -;; MA 02111-1307 USA +;; License along with this program; if not, see +;; . ;; http://www.fsf.org/copyleft/gpl.html diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 5ffc506f6d..97919f2d73 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -15,9 +15,8 @@ ;; PURPOSE. See the GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public -;; License along with this program; if not, write to the Free -;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, -;; MA 02111-1307 USA +;; License along with this program; if not, see +;; . ;;; Commentary: diff --git a/contrib/fast-import/import-directories.perl b/contrib/fast-import/import-directories.perl index 4dec1f18e4..a16f79cfdc 100755 --- a/contrib/fast-import/import-directories.perl +++ b/contrib/fast-import/import-directories.perl @@ -14,8 +14,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# along with this program; if not, see . # # ------------------------------------------------------------------------ diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index 60dec86d37..de3f81667e 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -15,8 +15,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program; if not, see . """ import os, os.path, sys diff --git a/contrib/mw-to-git/Git/Mediawiki.pm b/contrib/mw-to-git/Git/Mediawiki.pm index d13c4dfa7d..917d9e2d32 100644 --- a/contrib/mw-to-git/Git/Mediawiki.pm +++ b/contrib/mw-to-git/Git/Mediawiki.pm @@ -2,6 +2,7 @@ package Git::Mediawiki; use 5.008; use strict; +use POSIX; use Git; BEGIN { @@ -52,7 +53,7 @@ sub smudge_filename { $filename =~ s/ /_/g; # Decode forbidden characters encoded in clean_filename $filename =~ s/_%_([0-9a-fA-F][0-9a-fA-F])/sprintf('%c', hex($1))/ge; - return $filename; + return substr($filename, 0, NAME_MAX-length('.mw')); } sub connect_maybe { diff --git a/contrib/mw-to-git/git-remote-mediawiki.perl b/contrib/mw-to-git/git-remote-mediawiki.perl index e7f857c1a2..af9cbc9d0f 100755 --- a/contrib/mw-to-git/git-remote-mediawiki.perl +++ b/contrib/mw-to-git/git-remote-mediawiki.perl @@ -63,6 +63,11 @@ my @tracked_categories = split(/[ \n]/, run_git("config --get-all remote.${remotename}.categories")); chomp(@tracked_categories); +# Just like @tracked_categories, but for MediaWiki namespaces. +my @tracked_namespaces = split(/[ \n]/, run_git("config --get-all remote.${remotename}.namespaces")); +for (@tracked_namespaces) { s/_/ /g; } +chomp(@tracked_namespaces); + # Import media files on pull my $import_media = run_git("config --get --bool remote.${remotename}.mediaimport"); chomp($import_media); @@ -256,6 +261,32 @@ sub get_mw_tracked_categories { return; } +sub get_mw_tracked_namespaces { + my $pages = shift; + foreach my $local_namespace (sort @tracked_namespaces) { + my $namespace_id; + if ($local_namespace eq "(Main)") { + $namespace_id = 0; + } else { + $namespace_id = get_mw_namespace_id($local_namespace); + } + # virtual namespaces don't support allpages + next if !defined($namespace_id) || $namespace_id < 0; + my $mw_pages = $mediawiki->list( { + action => 'query', + list => 'allpages', + apnamespace => $namespace_id, + aplimit => 'max' } ) + || die $mediawiki->{error}->{code} . ': ' + . $mediawiki->{error}->{details} . "\n"; + print {*STDERR} "$#{$mw_pages} found in namespace $local_namespace ($namespace_id)\n"; + foreach my $page (@{$mw_pages}) { + $pages->{$page->{title}} = $page; + } + } + return; +} + sub get_mw_all_pages { my $pages = shift; # No user-provided list, get the list of pages from the API. @@ -319,6 +350,10 @@ sub get_mw_pages { $user_defined = 1; get_mw_tracked_categories(\%pages); } + if (@tracked_namespaces) { + $user_defined = 1; + get_mw_tracked_namespaces(\%pages); + } if (!$user_defined) { get_mw_all_pages(\%pages); } @@ -1308,7 +1343,8 @@ sub get_mw_namespace_id { my $id; if (!defined $ns) { - print {*STDERR} "No such namespace ${name} on MediaWiki.\n"; + my @namespaces = map { s/ /_/g; $_; } sort keys %namespace_id; + print {*STDERR} "No such namespace ${name} on MediaWiki, known namespaces: @namespaces\n"; $ns = {is_namespace => 0}; $namespace_id{$name} = $ns; } diff --git a/dir.c b/dir.c index fc2bdc79fc..047a950f55 100644 --- a/dir.c +++ b/dir.c @@ -1389,6 +1389,30 @@ static enum path_treatment treat_directory(struct dir_struct *dir, case index_nonexistent: if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES) break; + if (exclude && + (dir->flags & DIR_SHOW_IGNORED_TOO) && + (dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING)) { + + /* + * This is an excluded directory and we are + * showing ignored paths that match an exclude + * pattern. (e.g. show directory as ignored + * only if it matches an exclude pattern). + * This path will either be 'path_excluded` + * (if we are showing empty directories or if + * the directory is not empty), or will be + * 'path_none' (empty directory, and we are + * not showing empty directories). + */ + if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES)) + return path_excluded; + + if (read_directory_recursive(dir, istate, dirname, len, + untracked, 1, 1, pathspec) == path_excluded) + return path_excluded; + + return path_none; + } if (!(dir->flags & DIR_NO_GITLINKS)) { struct object_id oid; if (resolve_gitlink_ref(dirname, "HEAD", &oid) == 0) @@ -1561,6 +1585,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir, { int exclude; int has_path_in_index = !!index_file_exists(istate, path->buf, path->len, ignore_case); + enum path_treatment path_treatment; if (dtype == DT_UNKNOWN) dtype = get_dtype(de, istate, path->buf, path->len); @@ -1607,8 +1632,23 @@ static enum path_treatment treat_one_path(struct dir_struct *dir, return path_none; case DT_DIR: strbuf_addch(path, '/'); - return treat_directory(dir, istate, untracked, path->buf, path->len, - baselen, exclude, pathspec); + path_treatment = treat_directory(dir, istate, untracked, + path->buf, path->len, + baselen, exclude, pathspec); + /* + * If 1) we only want to return directories that + * match an exclude pattern and 2) this directory does + * not match an exclude pattern but all of its + * contents are excluded, then indicate that we should + * recurse into this directory (instead of marking the + * directory itself as an ignored path). + */ + if (!exclude && + path_treatment == path_excluded && + (dir->flags & DIR_SHOW_IGNORED_TOO) && + (dir->flags & DIR_SHOW_IGNORED_TOO_MODE_MATCHING)) + return path_recurse; + return path_treatment; case DT_REG: case DT_LNK: return exclude ? path_excluded : path_untracked; diff --git a/dir.h b/dir.h index e3717055d1..57b0943dae 100644 --- a/dir.h +++ b/dir.h @@ -152,7 +152,8 @@ struct dir_struct { DIR_COLLECT_IGNORED = 1<<4, DIR_SHOW_IGNORED_TOO = 1<<5, DIR_COLLECT_KILLED_ONLY = 1<<6, - DIR_KEEP_UNTRACKED_CONTENTS = 1<<7 + DIR_KEEP_UNTRACKED_CONTENTS = 1<<7, + DIR_SHOW_IGNORED_TOO_MODE_MATCHING = 1<<8 } flags; struct dir_entry **entries; struct dir_entry **ignored; diff --git a/ewah/bitmap.c b/ewah/bitmap.c index 7103ceefbf..756bdd050e 100644 --- a/ewah/bitmap.c +++ b/ewah/bitmap.c @@ -14,8 +14,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * along with this program; if not, see . */ #include "cache.h" #include "ewok.h" diff --git a/ewah/ewah_bitmap.c b/ewah/ewah_bitmap.c index 06c479f70a..b9fdda1d3d 100644 --- a/ewah/ewah_bitmap.c +++ b/ewah/ewah_bitmap.c @@ -14,8 +14,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * along with this program; if not, see . */ #include "git-compat-util.h" #include "ewok.h" diff --git a/ewah/ewah_io.c b/ewah/ewah_io.c index f73210973f..bed1994551 100644 --- a/ewah/ewah_io.c +++ b/ewah/ewah_io.c @@ -14,8 +14,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * along with this program; if not, see . */ #include "git-compat-util.h" #include "ewok.h" diff --git a/ewah/ewah_rlw.c b/ewah/ewah_rlw.c index c723f1aefd..b9643b7d0f 100644 --- a/ewah/ewah_rlw.c +++ b/ewah/ewah_rlw.c @@ -14,8 +14,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * along with this program; if not, see . */ #include "git-compat-util.h" #include "ewok.h" diff --git a/ewah/ewok.h b/ewah/ewok.h index 269a1a8706..dc43d05b64 100644 --- a/ewah/ewok.h +++ b/ewah/ewok.h @@ -14,8 +14,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * along with this program; if not, see . */ #ifndef __EWOK_BITMAP_H__ #define __EWOK_BITMAP_H__ diff --git a/ewah/ewok_rlw.h b/ewah/ewok_rlw.h index 63efdf9698..bb3c6ff7e0 100644 --- a/ewah/ewok_rlw.h +++ b/ewah/ewok_rlw.h @@ -14,8 +14,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * along with this program; if not, see . */ #ifndef __EWOK_RLW_H__ #define __EWOK_RLW_H__ diff --git a/git-bisect.sh b/git-bisect.sh index a69e436563..54cbfecc5a 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -1,6 +1,6 @@ #!/bin/sh -USAGE='[help|start|bad|good|new|old|terms|skip|next|reset|visualize|replay|log|run]' +USAGE='[help|start|bad|good|new|old|terms|skip|next|reset|visualize|view|replay|log|run]' LONG_USAGE='git bisect help print this long help message. git bisect start [--term-{old,good}= --term-{new,bad}=] @@ -20,7 +20,7 @@ git bisect next find next bisection to test and check it out. git bisect reset [] finish bisection search and go back to commit. -git bisect visualize +git bisect (visualize|view) show bisect status in gitk. git bisect replay replay bisection log. diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh index 5bc21b878d..ed24aa9d2f 100755 --- a/git-gui/git-gui.sh +++ b/git-gui/git-gui.sh @@ -24,8 +24,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA}] +along with this program; if not, see .}] ###################################################################### ## diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 2563dc52da..437815669f 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -722,7 +722,7 @@ collapse_todo_ids() { git rebase--helper --shorten-ids } -# Add commands after a pick or after a squash/fixup serie +# Add commands after a pick or after a squash/fixup series # in the todo list. add_exec_commands () { { diff --git a/grep.c b/grep.c index ce6a48e634..d0b9b6cdfa 100644 --- a/grep.c +++ b/grep.c @@ -387,7 +387,7 @@ static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt) if (!p->pcre1_regexp) compile_regexp_failed(p, error); - p->pcre1_extra_info = pcre_study(p->pcre1_regexp, PCRE_STUDY_JIT_COMPILE, &error); + p->pcre1_extra_info = pcre_study(p->pcre1_regexp, GIT_PCRE_STUDY_JIT_COMPILE, &error); if (!p->pcre1_extra_info && error) die("%s", error); diff --git a/grep.h b/grep.h index 52aecfab6e..399381c908 100644 --- a/grep.h +++ b/grep.h @@ -7,11 +7,12 @@ #if PCRE_MAJOR >= 8 && PCRE_MINOR >= 32 #ifndef NO_LIBPCRE1_JIT #define GIT_PCRE1_USE_JIT +#define GIT_PCRE_STUDY_JIT_COMPILE PCRE_STUDY_JIT_COMPILE #endif #endif #endif -#ifndef PCRE_STUDY_JIT_COMPILE -#define PCRE_STUDY_JIT_COMPILE 0 +#ifndef GIT_PCRE_STUDY_JIT_COMPILE +#define GIT_PCRE_STUDY_JIT_COMPILE 0 #endif #if PCRE_MAJOR <= 8 && PCRE_MINOR < 20 typedef int pcre_jit_stack; diff --git a/imap-send.c b/imap-send.c index 8c785f3ca2..54e6a80fd6 100644 --- a/imap-send.c +++ b/imap-send.c @@ -18,8 +18,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see . */ #include "cache.h" @@ -684,7 +683,7 @@ static int parse_response_code(struct imap_store *ctx, struct imap_cmd_cb *cb, struct imap *imap = ctx->imap; char *arg, *p; - if (*s != '[') + if (!s || *s != '[') return RESP_OK; /* no response code */ s++; if (!(p = strchr(s, ']'))) { @@ -693,6 +692,10 @@ static int parse_response_code(struct imap_store *ctx, struct imap_cmd_cb *cb, } *p++ = 0; arg = next_arg(&s); + if (!arg) { + fprintf(stderr, "IMAP error: empty response code\n"); + return RESP_BAD; + } if (!strcmp("UIDVALIDITY", arg)) { if (!(arg = next_arg(&s)) || !(ctx->uidvalidity = atoi(arg))) { fprintf(stderr, "IMAP error: malformed UIDVALIDITY status\n"); @@ -725,7 +728,8 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd) { struct imap *imap = ctx->imap; struct imap_cmd *cmdp, **pcmdp; - char *cmd, *arg, *arg1; + char *cmd; + const char *arg, *arg1; int n, resp, resp2, tag; for (;;) { @@ -733,6 +737,10 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd) return RESP_BAD; arg = next_arg(&cmd); + if (!arg) { + fprintf(stderr, "IMAP error: empty response\n"); + return RESP_BAD; + } if (*arg == '*') { arg = next_arg(&cmd); if (!arg) { @@ -807,6 +815,8 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd) if (cmdp->cb.cont || cmdp->cb.data) imap->literal_pending = 0; arg = next_arg(&cmd); + if (!arg) + arg = ""; if (!strcmp("OK", arg)) resp = DRV_OK; else { diff --git a/kwset.c b/kwset.c index e6236a0359..4fb6455aca 100644 --- a/kwset.c +++ b/kwset.c @@ -18,9 +18,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with this program; if not, see . */ /* Written August 1989 by Mike Haertel. The author may be reached (Email) at the address mike@ai.mit.edu, diff --git a/kwset.h b/kwset.h index 61a134f256..583f6268ef 100644 --- a/kwset.h +++ b/kwset.h @@ -17,9 +17,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with this program; if not, see . */ /* Written August 1989 by Mike Haertel. The author may be reached (Email) at the address mike@ai.mit.edu, diff --git a/merge-recursive.c b/merge-recursive.c index 2ca8444c65..b48b15a6fd 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -2201,6 +2201,7 @@ static void merge_recursive_config(struct merge_options *o) void init_merge_options(struct merge_options *o) { + const char *merge_verbosity; memset(o, 0, sizeof(struct merge_options)); o->verbosity = 2; o->buffer_output = 1; @@ -2209,9 +2210,9 @@ void init_merge_options(struct merge_options *o) o->renormalize = 0; o->detect_rename = 1; merge_recursive_config(o); - if (getenv("GIT_MERGE_VERBOSITY")) - o->verbosity = - strtol(getenv("GIT_MERGE_VERBOSITY"), NULL, 10); + merge_verbosity = getenv("GIT_MERGE_VERBOSITY"); + if (merge_verbosity) + o->verbosity = strtol(merge_verbosity, NULL, 10); if (o->verbosity >= 5) o->buffer_output = 0; strbuf_init(&o->obuf, 0); diff --git a/perl/Git/Packet.pm b/perl/Git/Packet.pm new file mode 100644 index 0000000000..255b28c098 --- /dev/null +++ b/perl/Git/Packet.pm @@ -0,0 +1,168 @@ +package Git::Packet; +use 5.008; +use strict; +use warnings; +BEGIN { + require Exporter; + if ($] < 5.008003) { + *import = \&Exporter::import; + } else { + # Exporter 5.57 which supports this invocation was + # released with perl 5.8.3 + Exporter->import('import'); + } +} + +our @EXPORT = qw( + packet_compare_lists + packet_bin_read + packet_txt_read + packet_required_key_val_read + packet_bin_write + packet_txt_write + packet_flush + packet_initialize + packet_read_capabilities + packet_read_and_check_capabilities + packet_check_and_write_capabilities + ); +our @EXPORT_OK = @EXPORT; + +sub packet_compare_lists { + my ($expect, @result) = @_; + my $ix; + if (scalar @$expect != scalar @result) { + return undef; + } + for ($ix = 0; $ix < $#result; $ix++) { + if ($expect->[$ix] ne $result[$ix]) { + return undef; + } + } + return 1; +} + +sub packet_bin_read { + my $buffer; + my $bytes_read = read STDIN, $buffer, 4; + if ( $bytes_read == 0 ) { + # EOF - Git stopped talking to us! + return ( -1, "" ); + } elsif ( $bytes_read != 4 ) { + die "invalid packet: '$buffer'"; + } + my $pkt_size = hex($buffer); + if ( $pkt_size == 0 ) { + return ( 1, "" ); + } elsif ( $pkt_size > 4 ) { + my $content_size = $pkt_size - 4; + $bytes_read = read STDIN, $buffer, $content_size; + if ( $bytes_read != $content_size ) { + die "invalid packet ($content_size bytes expected; $bytes_read bytes read)"; + } + return ( 0, $buffer ); + } else { + die "invalid packet size: $pkt_size"; + } +} + +sub remove_final_lf_or_die { + my $buf = shift; + unless ( $buf =~ s/\n$// ) { + die "A non-binary line MUST be terminated by an LF.\n" + . "Received: '$buf'"; + } + return $buf; +} + +sub packet_txt_read { + my ( $res, $buf ) = packet_bin_read(); + unless ( $res == -1 or $buf eq '' ) { + $buf = remove_final_lf_or_die($buf); + } + return ( $res, $buf ); +} + +sub packet_required_key_val_read { + my ( $key ) = @_; + my ( $res, $buf ) = packet_txt_read(); + unless ( $res == -1 or ( $buf =~ s/^$key=// and $buf ne '' ) ) { + die "bad $key: '$buf'"; + } + return ( $res, $buf ); +} + +sub packet_bin_write { + my $buf = shift; + print STDOUT sprintf( "%04x", length($buf) + 4 ); + print STDOUT $buf; + STDOUT->flush(); +} + +sub packet_txt_write { + packet_bin_write( $_[0] . "\n" ); +} + +sub packet_flush { + print STDOUT sprintf( "%04x", 0 ); + STDOUT->flush(); +} + +sub packet_initialize { + my ($name, $version) = @_; + + packet_compare_lists([0, $name . "-client"], packet_txt_read()) || + die "bad initialize"; + packet_compare_lists([0, "version=" . $version], packet_txt_read()) || + die "bad version"; + packet_compare_lists([1, ""], packet_bin_read()) || + die "bad version end"; + + packet_txt_write( $name . "-server" ); + packet_txt_write( "version=" . $version ); + packet_flush(); +} + +sub packet_read_capabilities { + my @cap; + while (1) { + my ( $res, $buf ) = packet_bin_read(); + if ( $res == -1 ) { + die "unexpected EOF when reading capabilities"; + } + return ( $res, @cap ) if ( $res != 0 ); + $buf = remove_final_lf_or_die($buf); + unless ( $buf =~ s/capability=// ) { + die "bad capability buf: '$buf'"; + } + push @cap, $buf; + } +} + +# Read remote capabilities and check them against capabilities we require +sub packet_read_and_check_capabilities { + my @required_caps = @_; + my ($res, @remote_caps) = packet_read_capabilities(); + my %remote_caps = map { $_ => 1 } @remote_caps; + foreach (@required_caps) { + unless (exists($remote_caps{$_})) { + die "required '$_' capability not available from remote" ; + } + } + return %remote_caps; +} + +# Check our capabilities we want to advertise against the remote ones +# and then advertise our capabilities +sub packet_check_and_write_capabilities { + my ($remote_caps, @our_caps) = @_; + foreach (@our_caps) { + unless (exists($remote_caps->{$_})) { + die "our capability '$_' is not available from remote" + } + packet_txt_write( "capability=" . $_ ); + } + packet_flush(); +} + +1; diff --git a/perl/Makefile b/perl/Makefile index 15d96fcc7a..f657de20e3 100644 --- a/perl/Makefile +++ b/perl/Makefile @@ -30,6 +30,7 @@ instdir_SQ = $(subst ','\'',$(prefix)/lib) modules += Git modules += Git/I18N modules += Git/IndexInfo +modules += Git/Packet modules += Git/SVN modules += Git/SVN/Memoize/YAML modules += Git/SVN/Fetcher diff --git a/ref-filter.c b/ref-filter.c index e728b15b3a..3f9161707e 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -76,9 +76,11 @@ static struct used_atom { char color[COLOR_MAXLEN]; struct align align; struct { - enum { RR_REF, RR_TRACK, RR_TRACKSHORT } option; + enum { + RR_REF, RR_TRACK, RR_TRACKSHORT, RR_REMOTE_NAME, RR_REMOTE_REF + } option; struct refname_atom refname; - unsigned int nobracket : 1; + unsigned int nobracket : 1, push : 1, push_remote : 1; } remote_ref; struct { enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option; @@ -138,6 +140,9 @@ static void remote_ref_atom_parser(const struct ref_format *format, struct used_ struct string_list params = STRING_LIST_INIT_DUP; int i; + if (!strcmp(atom->name, "push") || starts_with(atom->name, "push:")) + atom->u.remote_ref.push = 1; + if (!arg) { atom->u.remote_ref.option = RR_REF; refname_atom_parser_internal(&atom->u.remote_ref.refname, @@ -157,7 +162,13 @@ static void remote_ref_atom_parser(const struct ref_format *format, struct used_ atom->u.remote_ref.option = RR_TRACKSHORT; else if (!strcmp(s, "nobracket")) atom->u.remote_ref.nobracket = 1; - else { + else if (!strcmp(s, "remotename")) { + atom->u.remote_ref.option = RR_REMOTE_NAME; + atom->u.remote_ref.push_remote = 1; + } else if (!strcmp(s, "remoteref")) { + atom->u.remote_ref.option = RR_REMOTE_REF; + atom->u.remote_ref.push_remote = 1; + } else { atom->u.remote_ref.option = RR_REF; refname_atom_parser_internal(&atom->u.remote_ref.refname, arg, atom->name); @@ -1268,6 +1279,25 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname, *s = ">"; else *s = "<>"; + } else if (atom->u.remote_ref.option == RR_REMOTE_NAME) { + int explicit; + const char *remote = atom->u.remote_ref.push ? + pushremote_for_branch(branch, &explicit) : + remote_for_branch(branch, &explicit); + if (explicit) + *s = xstrdup(remote); + else + *s = ""; + } else if (atom->u.remote_ref.option == RR_REMOTE_REF) { + int explicit; + const char *merge; + + merge = remote_ref_for_branch(branch, atom->u.remote_ref.push, + &explicit); + if (explicit) + *s = xstrdup(merge); + else + *s = ""; } else die("BUG: unhandled RR_* enum"); } @@ -1377,16 +1407,20 @@ static void populate_value(struct ref_array_item *ref) if (refname) fill_remote_ref_details(atom, refname, branch, &v->s); continue; - } else if (starts_with(name, "push")) { + } else if (atom->u.remote_ref.push) { const char *branch_name; if (!skip_prefix(ref->refname, "refs/heads/", &branch_name)) continue; branch = branch_get(branch_name); - refname = branch_get_push(branch, NULL); - if (!refname) - continue; + if (atom->u.remote_ref.push_remote) + refname = NULL; + else { + refname = branch_get_push(branch, NULL); + if (!refname) + continue; + } fill_remote_ref_details(atom, refname, branch, &v->s); continue; } else if (starts_with(name, "color:")) { diff --git a/refs.c b/refs.c index 62a7621025..339d4318ee 100644 --- a/refs.c +++ b/refs.c @@ -770,7 +770,7 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid, if (cb->cutoff_cnt) *cb->cutoff_cnt = cb->reccnt - 1; /* - * we have not yet updated cb->[n|o]sha1 so they still + * we have not yet updated cb->[n|o]oid so they still * hold the values for the previous record. */ if (!is_null_oid(&cb->ooid)) { @@ -906,9 +906,6 @@ struct ref_update *ref_transaction_add_update( if (transaction->state != REF_TRANSACTION_OPEN) die("BUG: update called for transaction that is not open"); - if ((flags & REF_ISPRUNING) && !(flags & REF_NODEREF)) - die("BUG: REF_ISPRUNING set without REF_NODEREF"); - FLEX_ALLOC_STR(update, refname, refname); ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc); transaction->updates[transaction->nr++] = update; @@ -940,7 +937,8 @@ int ref_transaction_update(struct ref_transaction *transaction, return -1; } - flags &= REF_TRANSACTION_UPDATE_ALLOWED_FLAGS; + if (flags & ~REF_TRANSACTION_UPDATE_ALLOWED_FLAGS) + BUG("illegal flags 0x%x passed to ref_transaction_update()", flags); flags |= (new_oid ? REF_HAVE_NEW : 0) | (old_oid ? REF_HAVE_OLD : 0); diff --git a/refs.h b/refs.h index 15fd419c7d..18582a408c 100644 --- a/refs.h +++ b/refs.h @@ -126,7 +126,7 @@ int peel_ref(const char *refname, struct object_id *oid); /** * Resolve refname in the nested "gitlink" repository in the specified * submodule (which must be non-NULL). If the resolution is - * successful, return 0 and set sha1 to the name of the object; + * successful, return 0 and set oid to the name of the object; * otherwise, return a non-zero value. */ int resolve_gitlink_ref(const char *submodule, const char *refname, @@ -260,7 +260,7 @@ struct ref_transaction; /* * The signature for the callback function for the for_each_*() - * functions below. The memory pointed to by the refname and sha1 + * functions below. The memory pointed to by the refname and oid * arguments is only guaranteed to be valid for the duration of a * single callback invocation. */ @@ -335,24 +335,6 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, */ int refs_pack_refs(struct ref_store *refs, unsigned int flags); -/* - * Flags controlling ref_transaction_update(), ref_transaction_create(), etc. - * REF_NODEREF: act on the ref directly, instead of dereferencing - * symbolic references. - * - * Other flags are reserved for internal use. - */ -#define REF_NODEREF 0x01 -#define REF_FORCE_CREATE_REFLOG 0x40 - -/* - * Flags that can be passed in to ref_transaction_update - */ -#define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS \ - REF_ISPRUNING | \ - REF_FORCE_CREATE_REFLOG | \ - REF_NODEREF - /* * Setup reflog before using. Fill in err and return -1 on failure. */ @@ -372,7 +354,7 @@ int reflog_exists(const char *refname); /* * Delete the specified reference. If old_oid is non-NULL, then - * verify that the current value of the reference is old_sha1 before + * verify that the current value of the reference is old_oid before * deleting it. If old_oid is NULL, delete the reference if it * exists, regardless of its old value. It is an error for old_oid to * be null_oid. msg and flags are passed through to @@ -480,22 +462,23 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err); * * refname -- the name of the reference to be affected. * - * new_sha1 -- the SHA-1 that should be set to be the new value of - * the reference. Some functions allow this parameter to be + * new_oid -- the object ID that should be set to be the new value + * of the reference. Some functions allow this parameter to be * NULL, meaning that the reference is not changed, or - * null_sha1, meaning that the reference should be deleted. A + * null_oid, meaning that the reference should be deleted. A * copy of this value is made in the transaction. * - * old_sha1 -- the SHA-1 value that the reference must have before + * old_oid -- the object ID that the reference must have before * the update. Some functions allow this parameter to be NULL, * meaning that the old value of the reference is not checked, - * or null_sha1, meaning that the reference must not exist + * or null_oid, meaning that the reference must not exist * before the update. A copy of this value is made in the * transaction. * * flags -- flags affecting the update, passed to - * update_ref_lock(). Can be REF_NODEREF, which means that - * symbolic references should not be followed. + * update_ref_lock(). Possible flags: REF_NO_DEREF, + * REF_FORCE_CREATE_REFLOG. See those constants for more + * information. * * msg -- a message describing the change (for the reflog). * @@ -511,11 +494,37 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err); */ /* - * Add a reference update to transaction. new_oid is the value that - * the reference should have after the update, or null_oid if it - * should be deleted. If new_oid is NULL, then the reference is not - * changed at all. old_oid is the value that the reference must have - * before the update, or null_oid if it must not have existed + * The following flags can be passed to ref_transaction_update() etc. + * Internally, they are stored in `ref_update::flags`, along with some + * internal flags. + */ + +/* + * Act on the ref directly; i.e., without dereferencing symbolic refs. + * If this flag is not specified, then symbolic references are + * dereferenced and the update is applied to the referent. + */ +#define REF_NO_DEREF (1 << 0) + +/* + * Force the creation of a reflog for this reference, even if it + * didn't previously have a reflog. + */ +#define REF_FORCE_CREATE_REFLOG (1 << 1) + +/* + * Bitmask of all of the flags that are allowed to be passed in to + * ref_transaction_update() and friends: + */ +#define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS \ + (REF_NO_DEREF | REF_FORCE_CREATE_REFLOG) + +/* + * Add a reference update to transaction. `new_oid` is the value that + * the reference should have after the update, or `null_oid` if it + * should be deleted. If `new_oid` is NULL, then the reference is not + * changed at all. `old_oid` is the value that the reference must have + * before the update, or `null_oid` if it must not have existed * beforehand. The old value is checked after the lock is taken to * prevent races. If the old value doesn't agree with old_oid, the * whole transaction fails. If old_oid is NULL, then the previous @@ -624,7 +633,7 @@ int ref_transaction_abort(struct ref_transaction *transaction, * It is a bug to call this function when there might be other * processes accessing the repository or if there are existing * references that might conflict with the ones being created. All - * old_sha1 values must either be absent or NULL_SHA1. + * old_oid values must either be absent or null_oid. */ int initial_ref_transaction_commit(struct ref_transaction *transaction, struct strbuf *err); diff --git a/refs/files-backend.c b/refs/files-backend.c index 2bd54e11ae..f75d960e19 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -10,6 +10,51 @@ #include "../object.h" #include "../dir.h" +/* + * This backend uses the following flags in `ref_update::flags` for + * internal bookkeeping purposes. Their numerical values must not + * conflict with REF_NO_DEREF, REF_FORCE_CREATE_REFLOG, REF_HAVE_NEW, + * REF_HAVE_OLD, or REF_IS_PRUNING, which are also stored in + * `ref_update::flags`. + */ + +/* + * Used as a flag in ref_update::flags when a loose ref is being + * pruned. This flag must only be used when REF_NO_DEREF is set. + */ +#define REF_IS_PRUNING (1 << 4) + +/* + * Flag passed to lock_ref_sha1_basic() telling it to tolerate broken + * refs (i.e., because the reference is about to be deleted anyway). + */ +#define REF_DELETING (1 << 5) + +/* + * Used as a flag in ref_update::flags when the lockfile needs to be + * committed. + */ +#define REF_NEEDS_COMMIT (1 << 6) + +/* + * Used as a flag in ref_update::flags when we want to log a ref + * update but not actually perform it. This is used when a symbolic + * ref update is split up. + */ +#define REF_LOG_ONLY (1 << 7) + +/* + * Used as a flag in ref_update::flags when the ref_update was via an + * update to HEAD. + */ +#define REF_UPDATE_VIA_HEAD (1 << 8) + +/* + * Used as a flag in ref_update::flags when the loose reference has + * been deleted. + */ +#define REF_DELETED_LOOSE (1 << 9) + struct ref_lock { char *ref_name; struct lock_file lk; @@ -195,7 +240,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, } else if (is_null_oid(&oid)) { /* * It is so astronomically unlikely - * that NULL_SHA1 is the SHA-1 of an + * that null_oid is the OID of an * actual object that we consider its * appearance in a loose reference * file to be repo corruption @@ -428,7 +473,7 @@ static void unlock_ref(struct ref_lock *lock) * are passed to refs_verify_refname_available() for this check. * * If mustexist is not set and the reference is not found or is - * broken, lock the reference anyway but clear sha1. + * broken, lock the reference anyway but clear old_oid. * * Return 0 on success. On failure, write an error message to err and * return TRANSACTION_NAME_CONFLICT or TRANSACTION_GENERIC_ERROR. @@ -989,22 +1034,29 @@ static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r) { struct ref_transaction *transaction; struct strbuf err = STRBUF_INIT; + int ret = -1; if (check_refname_format(r->name, 0)) return; transaction = ref_store_transaction_begin(&refs->base, &err); - if (!transaction || - ref_transaction_delete(transaction, r->name, &r->oid, - REF_ISPRUNING | REF_NODEREF, NULL, &err) || - ref_transaction_commit(transaction, &err)) { - ref_transaction_free(transaction); + if (!transaction) + goto cleanup; + ref_transaction_add_update( + transaction, r->name, + REF_NO_DEREF | REF_HAVE_NEW | REF_HAVE_OLD | REF_IS_PRUNING, + &null_oid, &r->oid, NULL); + if (ref_transaction_commit(transaction, &err)) + goto cleanup; + + ret = 0; + +cleanup: + if (ret) error("%s", err.buf); - strbuf_release(&err); - return; - } - ref_transaction_free(transaction); strbuf_release(&err); + ref_transaction_free(transaction); + return; } /* @@ -1081,7 +1133,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags) */ if (ref_transaction_update(transaction, iter->refname, iter->oid, NULL, - REF_NODEREF, NULL, &err)) + REF_NO_DEREF, NULL, &err)) die("failure preparing to create packed reference %s: %s", iter->refname, err.buf); @@ -1284,7 +1336,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store, } if (!copy && refs_delete_ref(&refs->base, logmsg, oldrefname, - &orig_oid, REF_NODEREF)) { + &orig_oid, REF_NO_DEREF)) { error("unable to delete old %s", oldrefname); goto rollback; } @@ -1300,7 +1352,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE, &oid, NULL) && refs_delete_ref(&refs->base, NULL, newrefname, - NULL, REF_NODEREF)) { + NULL, REF_NO_DEREF)) { if (errno == EISDIR) { struct strbuf path = STRBUF_INIT; int result; @@ -1325,7 +1377,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store, logmoved = log; lock = lock_ref_oid_basic(refs, newrefname, NULL, NULL, NULL, - REF_NODEREF, NULL, &err); + REF_NO_DEREF, NULL, &err); if (!lock) { if (copy) error("unable to copy '%s' to '%s': %s", oldrefname, newrefname, err.buf); @@ -1348,7 +1400,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store, rollback: lock = lock_ref_oid_basic(refs, oldrefname, NULL, NULL, NULL, - REF_NODEREF, NULL, &err); + REF_NO_DEREF, NULL, &err); if (!lock) { error("unable to lock %s for rollback: %s", oldrefname, err.buf); strbuf_release(&err); @@ -1596,9 +1648,8 @@ static int files_log_ref_write(struct files_ref_store *refs, } /* - * Write sha1 into the open lockfile, then close the lockfile. On - * errors, rollback the lockfile, fill in *err and - * return -1. + * Write oid into the open lockfile, then close the lockfile. On + * errors, rollback the lockfile, fill in *err and return -1. */ static int write_ref_to_lockfile(struct ref_lock *lock, const struct object_id *oid, struct strbuf *err) @@ -1764,7 +1815,7 @@ static int files_create_symref(struct ref_store *ref_store, int ret; lock = lock_ref_oid_basic(refs, refname, NULL, - NULL, NULL, REF_NODEREF, NULL, + NULL, NULL, REF_NO_DEREF, NULL, &err); if (!lock) { error("%s", err.buf); @@ -2125,7 +2176,7 @@ static int split_head_update(struct ref_update *update, struct ref_update *new_update; if ((update->flags & REF_LOG_ONLY) || - (update->flags & REF_ISPRUNING) || + (update->flags & REF_IS_PRUNING) || (update->flags & REF_UPDATE_VIA_HEAD)) return 0; @@ -2148,7 +2199,7 @@ static int split_head_update(struct ref_update *update, new_update = ref_transaction_add_update( transaction, "HEAD", - update->flags | REF_LOG_ONLY | REF_NODEREF, + update->flags | REF_LOG_ONLY | REF_NO_DEREF, &update->new_oid, &update->old_oid, update->msg); @@ -2167,8 +2218,8 @@ static int split_head_update(struct ref_update *update, /* * update is for a symref that points at referent and doesn't have - * REF_NODEREF set. Split it into two updates: - * - The original update, but with REF_LOG_ONLY and REF_NODEREF set + * REF_NO_DEREF set. Split it into two updates: + * - The original update, but with REF_LOG_ONLY and REF_NO_DEREF set * - A new, separate update for the referent reference * Note that the new update will itself be subject to splitting when * the iteration gets to it. @@ -2220,10 +2271,10 @@ static int split_symref_update(struct files_ref_store *refs, /* * Change the symbolic ref update to log only. Also, it - * doesn't need to check its old SHA-1 value, as that will be + * doesn't need to check its old OID value, as that will be * done when new_update is processed. */ - update->flags |= REF_LOG_ONLY | REF_NODEREF; + update->flags |= REF_LOG_ONLY | REF_NO_DEREF; update->flags &= ~REF_HAVE_OLD; /* @@ -2289,10 +2340,10 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid, * Prepare for carrying out update: * - Lock the reference referred to by update. * - Read the reference under lock. - * - Check that its old SHA-1 value (if specified) is correct, and in + * - Check that its old OID value (if specified) is correct, and in * any case record it in update->lock->old_oid for later use when * writing the reflog. - * - If it is a symref update without REF_NODEREF, split it up into a + * - If it is a symref update without REF_NO_DEREF, split it up into a * REF_LOG_ONLY update of the symref and add a separate update for * the referent to transaction. * - If it is an update of head_ref, add a corresponding REF_LOG_ONLY @@ -2340,11 +2391,11 @@ static int lock_ref_for_update(struct files_ref_store *refs, update->backend_data = lock; if (update->type & REF_ISSYMREF) { - if (update->flags & REF_NODEREF) { + if (update->flags & REF_NO_DEREF) { /* * We won't be reading the referent as part of * the transaction, so we have to read it here - * to record and possibly check old_sha1: + * to record and possibly check old_oid: */ if (refs_read_ref_full(&refs->base, referent.buf, 0, @@ -2364,7 +2415,7 @@ static int lock_ref_for_update(struct files_ref_store *refs, /* * Create a new update for the reference this * symref is pointing at. Also, we will record - * and verify old_sha1 for this update as part + * and verify old_oid for this update as part * of processing the split-off update, so we * don't have to do it here. */ @@ -2384,7 +2435,7 @@ static int lock_ref_for_update(struct files_ref_store *refs, /* * If this update is happening indirectly because of a - * symref update, record the old SHA-1 in the parent + * symref update, record the old OID in the parent * update: */ for (parent_update = update->parent_update; @@ -2511,13 +2562,18 @@ static int files_transaction_prepare(struct ref_store *ref_store, * transaction. (If we end up splitting up any updates using * split_symref_update() or split_head_update(), those * functions will check that the new updates don't have the - * same refname as any existing ones.) + * same refname as any existing ones.) Also fail if any of the + * updates use REF_IS_PRUNING without REF_NO_DEREF. */ for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; struct string_list_item *item = string_list_append(&affected_refnames, update->refname); + if ((update->flags & REF_IS_PRUNING) && + !(update->flags & REF_NO_DEREF)) + BUG("REF_IS_PRUNING set without REF_NO_DEREF"); + /* * We store a pointer to update in item->util, but at * the moment we never use the value of this field @@ -2575,7 +2631,7 @@ static int files_transaction_prepare(struct ref_store *ref_store, if (update->flags & REF_DELETING && !(update->flags & REF_LOG_ONLY) && - !(update->flags & REF_ISPRUNING)) { + !(update->flags & REF_IS_PRUNING)) { /* * This reference has to be deleted from * packed-refs if it exists there. @@ -2594,8 +2650,8 @@ static int files_transaction_prepare(struct ref_store *ref_store, ref_transaction_add_update( packed_transaction, update->refname, - update->flags & ~REF_HAVE_OLD, - &update->new_oid, &update->old_oid, + REF_HAVE_NEW | REF_NO_DEREF, + &update->new_oid, NULL, NULL); } } @@ -2606,7 +2662,23 @@ static int files_transaction_prepare(struct ref_store *ref_store, goto cleanup; } backend_data->packed_refs_locked = 1; - ret = ref_transaction_prepare(packed_transaction, err); + + if (is_packed_transaction_needed(refs->packed_ref_store, + packed_transaction)) { + ret = ref_transaction_prepare(packed_transaction, err); + } else { + /* + * We can skip rewriting the `packed-refs` + * file. But we do need to leave it locked, so + * that somebody else doesn't pack a reference + * that we are trying to delete. + */ + if (ref_transaction_abort(packed_transaction, err)) { + ret = TRANSACTION_GENERIC_ERROR; + goto cleanup; + } + backend_data->packed_transaction = NULL; + } } cleanup: @@ -2692,7 +2764,7 @@ static int files_transaction_finish(struct ref_store *ref_store, struct ref_update *update = transaction->updates[i]; if (update->flags & REF_DELETING && !(update->flags & REF_LOG_ONLY) && - !(update->flags & REF_ISPRUNING)) { + !(update->flags & REF_IS_PRUNING)) { strbuf_reset(&sb); files_reflog_path(refs, &sb, update->refname); if (!unlink_or_warn(sb.buf)) @@ -2938,7 +3010,7 @@ static int files_reflog_expire(struct ref_store *ref_store, * reference if --updateref was specified: */ lock = lock_ref_oid_basic(refs, refname, oid, - NULL, NULL, REF_NODEREF, + NULL, NULL, REF_NO_DEREF, &type, &err); if (!lock) { error("cannot lock ref '%s': %s", refname, err.buf); diff --git a/refs/packed-backend.c b/refs/packed-backend.c index 74f1dea0f4..023243fd5f 100644 --- a/refs/packed-backend.c +++ b/refs/packed-backend.c @@ -744,7 +744,7 @@ static int packed_read_raw_ref(struct ref_store *ref_store, /* * This value is set in `base.flags` if the peeled value of the * current reference is known. In that case, `peeled` contains the - * correct peeled value for the reference, which might be `null_sha1` + * correct peeled value for the reference, which might be `null_oid` * if the reference is not a tag or if it is broken. */ #define REF_KNOWS_PEELED 0x40 @@ -961,11 +961,11 @@ static struct ref_iterator *packed_ref_iterator_begin( * by the failing call to `fprintf()`. */ static int write_packed_entry(FILE *fh, const char *refname, - const unsigned char *sha1, - const unsigned char *peeled) + const struct object_id *oid, + const struct object_id *peeled) { - if (fprintf(fh, "%s %s\n", sha1_to_hex(sha1), refname) < 0 || - (peeled && fprintf(fh, "^%s\n", sha1_to_hex(peeled)) < 0)) + if (fprintf(fh, "%s %s\n", oid_to_hex(oid), refname) < 0 || + (peeled && fprintf(fh, "^%s\n", oid_to_hex(peeled)) < 0)) return -1; return 0; @@ -1203,8 +1203,8 @@ static int write_with_updates(struct packed_ref_store *refs, int peel_error = ref_iterator_peel(iter, &peeled); if (write_packed_entry(out, iter->refname, - iter->oid->hash, - peel_error ? NULL : peeled.hash)) + iter->oid, + peel_error ? NULL : &peeled)) goto write_error; if ((ok = ref_iterator_advance(iter)) != ITER_OK) @@ -1224,8 +1224,8 @@ static int write_with_updates(struct packed_ref_store *refs, &peeled); if (write_packed_entry(out, update->refname, - update->new_oid.hash, - peel_error ? NULL : peeled.hash)) + &update->new_oid, + peel_error ? NULL : &peeled)) goto write_error; i++; @@ -1261,6 +1261,100 @@ static int write_with_updates(struct packed_ref_store *refs, return -1; } +int is_packed_transaction_needed(struct ref_store *ref_store, + struct ref_transaction *transaction) +{ + struct packed_ref_store *refs = packed_downcast( + ref_store, + REF_STORE_READ, + "is_packed_transaction_needed"); + struct strbuf referent = STRBUF_INIT; + size_t i; + int ret; + + if (!is_lock_file_locked(&refs->lock)) + BUG("is_packed_transaction_needed() called while unlocked"); + + /* + * We're only going to bother returning false for the common, + * trivial case that references are only being deleted, their + * old values are not being checked, and the old `packed-refs` + * file doesn't contain any of those reference(s). This gives + * false positives for some other cases that could + * theoretically be optimized away: + * + * 1. It could be that the old value is being verified without + * setting a new value. In this case, we could verify the + * old value here and skip the update if it agrees. If it + * disagrees, we could either let the update go through + * (the actual commit would re-detect and report the + * problem), or come up with a way of reporting such an + * error to *our* caller. + * + * 2. It could be that a new value is being set, but that it + * is identical to the current packed value of the + * reference. + * + * Neither of these cases will come up in the current code, + * because the only caller of this function passes to it a + * transaction that only includes `delete` updates with no + * `old_id`. Even if that ever changes, false positives only + * cause an optimization to be missed; they do not affect + * correctness. + */ + + /* + * Start with the cheap checks that don't require old + * reference values to be read: + */ + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; + + if (update->flags & REF_HAVE_OLD) + /* Have to check the old value -> needed. */ + return 1; + + if ((update->flags & REF_HAVE_NEW) && !is_null_oid(&update->new_oid)) + /* Have to set a new value -> needed. */ + return 1; + } + + /* + * The transaction isn't checking any old values nor is it + * setting any nonzero new values, so it still might be able + * to be skipped. Now do the more expensive check: the update + * is needed if any of the updates is a delete, and the old + * `packed-refs` file contains a value for that reference. + */ + ret = 0; + for (i = 0; i < transaction->nr; i++) { + struct ref_update *update = transaction->updates[i]; + unsigned int type; + struct object_id oid; + + if (!(update->flags & REF_HAVE_NEW)) + /* + * This reference isn't being deleted -> not + * needed. + */ + continue; + + if (!refs_read_raw_ref(ref_store, update->refname, + &oid, &referent, &type) || + errno != ENOENT) { + /* + * We have to actually delete that reference + * -> this transaction is needed. + */ + ret = 1; + break; + } + } + + strbuf_release(&referent); + return ret; +} + struct packed_transaction_backend_data { /* True iff the transaction owns the packed-refs lock. */ int own_lock; diff --git a/refs/packed-backend.h b/refs/packed-backend.h index 61687e408a..640245d3b9 100644 --- a/refs/packed-backend.h +++ b/refs/packed-backend.h @@ -23,4 +23,13 @@ int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err) void packed_refs_unlock(struct ref_store *ref_store); int packed_refs_is_locked(struct ref_store *ref_store); +/* + * Return true if `transaction` really needs to be carried out against + * the specified packed_ref_store, or false if it can be skipped + * (i.e., because it is an obvious NOOP). `ref_store` must be locked + * before calling this function. + */ +int is_packed_transaction_needed(struct ref_store *ref_store, + struct ref_transaction *transaction); + #endif /* REFS_PACKED_BACKEND_H */ diff --git a/refs/ref-cache.c b/refs/ref-cache.c index 043eb83748..82c1cf90a7 100644 --- a/refs/ref-cache.c +++ b/refs/ref-cache.c @@ -260,8 +260,8 @@ int add_ref_entry(struct ref_dir *dir, struct ref_entry *ref) /* * Emit a warning and return true iff ref1 and ref2 have the same name - * and the same sha1. Die if they have the same name but different - * sha1s. + * and the same oid. Die if they have the same name but different + * oids. */ static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2) { diff --git a/refs/refs-internal.h b/refs/refs-internal.h index b0f3e300c7..dd834314bd 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -8,58 +8,22 @@ */ /* - * Flag passed to lock_ref_sha1_basic() telling it to tolerate broken - * refs (i.e., because the reference is about to be deleted anyway). + * The following flags can appear in `ref_update::flags`. Their + * numerical values must not conflict with those of REF_NO_DEREF and + * REF_FORCE_CREATE_REFLOG, which are also stored in + * `ref_update::flags`. */ -#define REF_DELETING 0x02 /* - * Used as a flag in ref_update::flags when a loose ref is being - * pruned. This flag must only be used when REF_NODEREF is set. + * The reference should be updated to new_oid. */ -#define REF_ISPRUNING 0x04 +#define REF_HAVE_NEW (1 << 2) /* - * Used as a flag in ref_update::flags when the reference should be - * updated to new_sha1. + * The current reference's value should be checked to make sure that + * it agrees with old_oid. */ -#define REF_HAVE_NEW 0x08 - -/* - * Used as a flag in ref_update::flags when old_sha1 should be - * checked. - */ -#define REF_HAVE_OLD 0x10 - -/* - * Used as a flag in ref_update::flags when the lockfile needs to be - * committed. - */ -#define REF_NEEDS_COMMIT 0x20 - -/* - * 0x40 is REF_FORCE_CREATE_REFLOG, so skip it if you're adding a - * value to ref_update::flags - */ - -/* - * Used as a flag in ref_update::flags when we want to log a ref - * update but not actually perform it. This is used when a symbolic - * ref update is split up. - */ -#define REF_LOG_ONLY 0x80 - -/* - * Internal flag, meaning that the containing ref_update was via an - * update to HEAD. - */ -#define REF_UPDATE_VIA_HEAD 0x100 - -/* - * Used as a flag in ref_update::flags when the loose reference has - * been deleted. - */ -#define REF_DELETED_LOOSE 0x200 +#define REF_HAVE_OLD (1 << 3) /* * Return the length of time to retry acquiring a loose reference lock @@ -122,7 +86,7 @@ enum peel_status { * tag recursively until a non-tag is found. If successful, store the * result to oid and return PEEL_PEELED. If the object is not a tag * or is not valid, return PEEL_NON_TAG or PEEL_INVALID, respectively, - * and leave sha1 unchanged. + * and leave oid unchanged. */ enum peel_status peel_object(const struct object_id *name, struct object_id *oid); @@ -134,30 +98,29 @@ enum peel_status peel_object(const struct object_id *name, struct object_id *oid int copy_reflog_msg(char *buf, const char *msg); /** - * Information needed for a single ref update. Set new_sha1 to the new - * value or to null_sha1 to delete the ref. To check the old value - * while the ref is locked, set (flags & REF_HAVE_OLD) and set - * old_sha1 to the old value, or to null_sha1 to ensure the ref does - * not exist before update. + * Information needed for a single ref update. Set new_oid to the new + * value or to null_oid to delete the ref. To check the old value + * while the ref is locked, set (flags & REF_HAVE_OLD) and set old_oid + * to the old value, or to null_oid to ensure the ref does not exist + * before update. */ struct ref_update { - /* - * If (flags & REF_HAVE_NEW), set the reference to this value: + * If (flags & REF_HAVE_NEW), set the reference to this value + * (or delete it, if `new_oid` is `null_oid`). */ struct object_id new_oid; /* * If (flags & REF_HAVE_OLD), check that the reference - * previously had this value: + * previously had this value (or didn't previously exist, if + * `old_oid` is `null_oid`). */ struct object_id old_oid; /* - * One or more of REF_HAVE_NEW, REF_HAVE_OLD, REF_NODEREF, - * REF_DELETING, REF_ISPRUNING, REF_LOG_ONLY, - * REF_UPDATE_VIA_HEAD, REF_NEEDS_COMMIT, and - * REF_DELETED_LOOSE: + * One or more of REF_NO_DEREF, REF_FORCE_CREATE_REFLOG, + * REF_HAVE_NEW, REF_HAVE_OLD, or backend-specific flags. */ unsigned int flags; @@ -195,7 +158,7 @@ int ref_update_reject_duplicates(struct string_list *refnames, /* * Add a ref_update with the specified properties to transaction, and * return a pointer to the new object. This function does not verify - * that refname is well-formed. new_sha1 and old_sha1 are only + * that refname is well-formed. new_oid and old_oid are only * dereferenced if the REF_HAVE_NEW and REF_HAVE_OLD bits, * respectively, are set in flags. */ diff --git a/remote.c b/remote.c index 685e776a65..4e93753e19 100644 --- a/remote.c +++ b/remote.c @@ -675,6 +675,36 @@ const char *pushremote_for_branch(struct branch *branch, int *explicit) return remote_for_branch(branch, explicit); } +const char *remote_ref_for_branch(struct branch *branch, int for_push, + int *explicit) +{ + if (branch) { + if (!for_push) { + if (branch->merge_nr) { + if (explicit) + *explicit = 1; + return branch->merge_name[0]; + } + } else { + const char *dst, *remote_name = + pushremote_for_branch(branch, NULL); + struct remote *remote = remote_get(remote_name); + + if (remote && remote->push_refspec_nr && + (dst = apply_refspecs(remote->push, + remote->push_refspec_nr, + branch->refname))) { + if (explicit) + *explicit = 1; + return dst; + } + } + } + if (explicit) + *explicit = 0; + return ""; +} + static struct remote *remote_get_1(const char *name, const char *(*get_default)(struct branch *, int *)) { diff --git a/remote.h b/remote.h index 2ecf4c8c74..1f6611be21 100644 --- a/remote.h +++ b/remote.h @@ -223,6 +223,8 @@ struct branch { struct branch *branch_get(const char *name); const char *remote_for_branch(struct branch *branch, int *explicit); const char *pushremote_for_branch(struct branch *branch, int *explicit); +const char *remote_ref_for_branch(struct branch *branch, int for_push, + int *explicit); int branch_has_merge_config(struct branch *branch); int branch_merge_matches(struct branch *, int n, const char *); diff --git a/sequencer.c b/sequencer.c index 6d027b06c8..19dd575ed9 100644 --- a/sequencer.c +++ b/sequencer.c @@ -1117,11 +1117,11 @@ static int do_pick_commit(enum todo_command command, struct commit *commit, */ if (command == TODO_PICK && !opts->no_commit && (res == 0 || res == 1) && update_ref(NULL, "CHERRY_PICK_HEAD", &commit->object.oid, NULL, - REF_NODEREF, UPDATE_REFS_MSG_ON_ERR)) + REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) res = -1; if (command == TODO_REVERT && ((opts->no_commit && res == 0) || res == 1) && update_ref(NULL, "REVERT_HEAD", &commit->object.oid, NULL, - REF_NODEREF, UPDATE_REFS_MSG_ON_ERR)) + REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) res = -1; if (res) { @@ -2130,7 +2130,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts) msg = reflog_message(opts, "finish", "%s onto %s", head_ref.buf, buf.buf); if (update_ref(msg, head_ref.buf, &head, &orig, - REF_NODEREF, UPDATE_REFS_MSG_ON_ERR)) { + REF_NO_DEREF, UPDATE_REFS_MSG_ON_ERR)) { res = error(_("could not update %s"), head_ref.buf); goto cleanup_head_ref; @@ -2670,6 +2670,19 @@ int check_todo_list(void) return res; } +static int rewrite_file(const char *path, const char *buf, size_t len) +{ + int rc = 0; + int fd = open(path, O_WRONLY | O_TRUNC); + if (fd < 0) + return error_errno(_("could not open '%s' for writing"), path); + if (write_in_full(fd, buf, len) < 0) + rc = error_errno(_("could not write to '%s'"), path); + if (close(fd) && !rc) + rc = error_errno(_("could not close '%s'"), path); + return rc; +} + /* skip picking commits whose parents are unchanged */ int skip_unnecessary_picks(void) { @@ -2742,29 +2755,11 @@ int skip_unnecessary_picks(void) } close(fd); - fd = open(rebase_path_todo(), O_WRONLY, 0666); - if (fd < 0) { - error_errno(_("could not open '%s' for writing"), - rebase_path_todo()); - todo_list_release(&todo_list); - return -1; - } - if (write_in_full(fd, todo_list.buf.buf + offset, - todo_list.buf.len - offset) < 0) { - error_errno(_("could not write to '%s'"), - rebase_path_todo()); - close(fd); - todo_list_release(&todo_list); - return -1; - } - if (ftruncate(fd, todo_list.buf.len - offset) < 0) { - error_errno(_("could not truncate '%s'"), - rebase_path_todo()); + if (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset, + todo_list.buf.len - offset) < 0) { todo_list_release(&todo_list); - close(fd); return -1; } - close(fd); todo_list.current = i; if (is_fixup(peek_command(&todo_list, 0))) @@ -2949,15 +2944,7 @@ int rearrange_squash(void) } } - fd = open(todo_file, O_WRONLY); - if (fd < 0) - res = error_errno(_("could not open '%s'"), todo_file); - else if (write(fd, buf.buf, buf.len) < 0) - res = error_errno(_("could not write to '%s'"), todo_file); - else if (ftruncate(fd, buf.len) < 0) - res = error_errno(_("could not truncate '%s'"), - todo_file); - close(fd); + res = rewrite_file(todo_file, buf.buf, buf.len); strbuf_release(&buf); } diff --git a/sh-i18n--envsubst.c b/sh-i18n--envsubst.c index c3a2b5ad17..09c6b445b9 100644 --- a/sh-i18n--envsubst.c +++ b/sh-i18n--envsubst.c @@ -30,8 +30,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + along with this program; if not, see . */ /* closeout.c - close standard output and standard error Copyright (C) 1998-2007 Free Software Foundation, Inc. @@ -47,8 +46,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + along with this program; if not, see . */ #include #include diff --git a/sha1_file.c b/sha1_file.c index d708981376..8a7c6b7eba 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -404,6 +404,9 @@ static void link_alt_odb_entries(const char *alt, int sep, struct strbuf objdirbuf = STRBUF_INIT; struct strbuf entry = STRBUF_INIT; + if (!alt || !*alt) + return; + if (depth > 5) { error("%s: ignoring alternate object stores, nesting too deep.", relative_base); @@ -604,7 +607,6 @@ void prepare_alt_odb(void) return; alt = getenv(ALTERNATE_DB_ENVIRONMENT); - if (!alt) alt = ""; alt_odb_tail = &alt_odb_list; link_alt_odb_entries(alt, PATH_SEP, NULL, 0); diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh index 2d26f86800..bb94c23209 100755 --- a/t/lib-submodule-update.sh +++ b/t/lib-submodule-update.sh @@ -306,9 +306,9 @@ test_submodule_content () { # to protect the history! # -# Test that submodule contents are currently not updated when switching -# between commits that change a submodule. -test_submodule_switch () { +# Internal function; use test_submodule_switch() or +# test_submodule_forced_switch() instead. +test_submodule_switch_common() { command="$1" ######################### Appearing submodule ######################### # Switching to a commit letting a submodule appear creates empty dir ... @@ -332,7 +332,7 @@ test_submodule_switch () { test_submodule_content sub1 origin/add_sub1 ) ' - # ... and doesn't care if it already exists ... + # ... and doesn't care if it already exists. test_expect_$RESULT "$command: added submodule leaves existing empty directory alone" ' prolog && reset_work_tree_to no_submodule && @@ -347,19 +347,6 @@ test_submodule_switch () { test_submodule_content sub1 origin/add_sub1 ) ' - # ... unless there is an untracked file in its place. - test_expect_success "$command: added submodule doesn't remove untracked unignored file with same name" ' - prolog && - reset_work_tree_to no_submodule && - ( - cd submodule_update && - git branch -t add_sub1 origin/add_sub1 && - >sub1 && - test_must_fail $command add_sub1 && - test_superproject_content origin/no_submodule && - test_must_be_empty sub1 - ) - ' # Replacing a tracked file with a submodule produces an empty # directory ... test_expect_$RESULT "$command: replace tracked file with submodule creates empty directory" ' @@ -441,6 +428,11 @@ test_submodule_switch () { # submodule files with the newly checked out ones in the # directory of the same name while it shouldn't. RESULT="failure" + elif test "$KNOWN_FAILURE_FORCED_SWITCH_TESTS" = 1 + then + # All existing tests that use test_submodule_forced_switch() + # require this. + RESULT="failure" else RESULT="success" fi @@ -522,7 +514,6 @@ test_submodule_switch () { test_submodule_content sub1 origin/modify_sub1 ) ' - # Updating a submodule to an invalid sha1 doesn't update the # submodule's work tree, subsequent update will fail test_expect_$RESULT "$command: modified submodule does not update submodule work tree to invalid commit" ' @@ -555,42 +546,51 @@ test_submodule_switch () { ' } -# Test that submodule contents are currently not updated when switching -# between commits that change a submodule, but throwing away local changes in -# the superproject is allowed. -test_submodule_forced_switch () { +# Declares and invokes several tests that, in various situations, checks that +# the provided transition function: +# - succeeds in updating the worktree and index of a superproject to a target +# commit, or fails atomically (depending on the test situation) +# - if succeeds, the contents of submodule directories are unchanged +# - if succeeds, once "git submodule update" is invoked, the contents of +# submodule directories are updated +# +# Use as follows: +# +# my_func () { +# target=$1 +# # Do something here that updates the worktree and index to match target, +# # but not any submodule directories. +# } +# test_submodule_switch "my_func" +test_submodule_switch () { command="$1" - ######################### Appearing submodule ######################### - # Switching to a commit letting a submodule appear creates empty dir ... - test_expect_success "$command: added submodule creates empty directory" ' - prolog && - reset_work_tree_to no_submodule && - ( - cd submodule_update && - git branch -t add_sub1 origin/add_sub1 && - $command add_sub1 && - test_superproject_content origin/add_sub1 && - test_dir_is_empty sub1 && - git submodule update --init --recursive && - test_submodule_content sub1 origin/add_sub1 - ) - ' - # ... and doesn't care if it already exists ... - test_expect_success "$command: added submodule leaves existing empty directory alone" ' + test_submodule_switch_common "$command" + + # An empty directory does not prevent the creation of a submodule of + # the same name, but a file does. + test_expect_success "$command: added submodule doesn't remove untracked unignored file with same name" ' prolog && reset_work_tree_to no_submodule && ( cd submodule_update && git branch -t add_sub1 origin/add_sub1 && - mkdir sub1 && - $command add_sub1 && - test_superproject_content origin/add_sub1 && - test_dir_is_empty sub1 && - git submodule update --init --recursive && - test_submodule_content sub1 origin/add_sub1 + >sub1 && + test_must_fail $command add_sub1 && + test_superproject_content origin/no_submodule && + test_must_be_empty sub1 ) ' - # ... unless there is an untracked file in its place. +} + +# Same as test_submodule_switch(), except that throwing away local changes in +# the superproject is allowed. +test_submodule_forced_switch () { + command="$1" + KNOWN_FAILURE_FORCED_SWITCH_TESTS=1 + test_submodule_switch_common "$command" + + # When forced, a file in the superproject does not prevent creating a + # submodule of the same name. test_expect_success "$command: added submodule does remove untracked unignored file with same name when forced" ' prolog && reset_work_tree_to no_submodule && @@ -603,165 +603,6 @@ test_submodule_forced_switch () { test_dir_is_empty sub1 ) ' - # Replacing a tracked file with a submodule produces an empty - # directory ... - test_expect_success "$command: replace tracked file with submodule creates empty directory" ' - prolog && - reset_work_tree_to replace_sub1_with_file && - ( - cd submodule_update && - git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 && - $command replace_file_with_sub1 && - test_superproject_content origin/replace_file_with_sub1 && - test_dir_is_empty sub1 && - git submodule update --init --recursive && - test_submodule_content sub1 origin/replace_file_with_sub1 - ) - ' - # ... as does removing a directory with tracked files with a - # submodule. - test_expect_success "$command: replace directory with submodule" ' - prolog && - reset_work_tree_to replace_sub1_with_directory && - ( - cd submodule_update && - git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 && - $command replace_directory_with_sub1 && - test_superproject_content origin/replace_directory_with_sub1 && - test_dir_is_empty sub1 && - git submodule update --init --recursive && - test_submodule_content sub1 origin/replace_directory_with_sub1 - ) - ' - - ######################## Disappearing submodule ####################### - # Removing a submodule doesn't remove its work tree ... - test_expect_success "$command: removed submodule leaves submodule directory and its contents in place" ' - prolog && - reset_work_tree_to add_sub1 && - ( - cd submodule_update && - git branch -t remove_sub1 origin/remove_sub1 && - $command remove_sub1 && - test_superproject_content origin/remove_sub1 && - test_submodule_content sub1 origin/add_sub1 - ) - ' - # ... especially when it contains a .git directory. - test_expect_success "$command: removed submodule leaves submodule containing a .git directory alone" ' - prolog && - reset_work_tree_to add_sub1 && - ( - cd submodule_update && - git branch -t remove_sub1 origin/remove_sub1 && - replace_gitfile_with_git_dir sub1 && - $command remove_sub1 && - test_superproject_content origin/remove_sub1 && - test_git_directory_is_unchanged sub1 && - test_submodule_content sub1 origin/add_sub1 - ) - ' - # Replacing a submodule with files in a directory must fail as the - # submodule work tree isn't removed ... - test_expect_failure "$command: replace submodule with a directory must fail" ' - prolog && - reset_work_tree_to add_sub1 && - ( - cd submodule_update && - git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory && - test_must_fail $command replace_sub1_with_directory && - test_superproject_content origin/add_sub1 && - test_submodule_content sub1 origin/add_sub1 - ) - ' - # ... especially when it contains a .git directory. - test_expect_failure "$command: replace submodule containing a .git directory with a directory must fail" ' - prolog && - reset_work_tree_to add_sub1 && - ( - cd submodule_update && - git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory && - replace_gitfile_with_git_dir sub1 && - test_must_fail $command replace_sub1_with_directory && - test_superproject_content origin/add_sub1 && - test_git_directory_is_unchanged sub1 && - test_submodule_content sub1 origin/add_sub1 - ) - ' - # Replacing it with a file must fail as it could throw away any local - # work tree changes ... - test_expect_failure "$command: replace submodule with a file must fail" ' - prolog && - reset_work_tree_to add_sub1 && - ( - cd submodule_update && - git branch -t replace_sub1_with_file origin/replace_sub1_with_file && - test_must_fail $command replace_sub1_with_file && - test_superproject_content origin/add_sub1 && - test_submodule_content sub1 origin/add_sub1 - ) - ' - # ... or even destroy unpushed parts of submodule history if that - # still uses a .git directory. - test_expect_failure "$command: replace submodule containing a .git directory with a file must fail" ' - prolog && - reset_work_tree_to add_sub1 && - ( - cd submodule_update && - git branch -t replace_sub1_with_file origin/replace_sub1_with_file && - replace_gitfile_with_git_dir sub1 && - test_must_fail $command replace_sub1_with_file && - test_superproject_content origin/add_sub1 && - test_git_directory_is_unchanged sub1 && - test_submodule_content sub1 origin/add_sub1 - ) - ' - - ########################## Modified submodule ######################### - # Updating a submodule sha1 doesn't update the submodule's work tree - test_expect_success "$command: modified submodule does not update submodule work tree" ' - prolog && - reset_work_tree_to add_sub1 && - ( - cd submodule_update && - git branch -t modify_sub1 origin/modify_sub1 && - $command modify_sub1 && - test_superproject_content origin/modify_sub1 && - test_submodule_content sub1 origin/add_sub1 && - git submodule update && - test_submodule_content sub1 origin/modify_sub1 - ) - ' - # Updating a submodule to an invalid sha1 doesn't update the - # submodule's work tree, subsequent update will fail - test_expect_success "$command: modified submodule does not update submodule work tree to invalid commit" ' - prolog && - reset_work_tree_to add_sub1 && - ( - cd submodule_update && - git branch -t invalid_sub1 origin/invalid_sub1 && - $command invalid_sub1 && - test_superproject_content origin/invalid_sub1 && - test_submodule_content sub1 origin/add_sub1 && - test_must_fail git submodule update && - test_submodule_content sub1 origin/add_sub1 - ) - ' - # Updating a submodule from an invalid sha1 doesn't update the - # submodule's work tree, subsequent update will succeed - test_expect_success "$command: modified submodule does not update submodule work tree from invalid commit" ' - prolog && - reset_work_tree_to invalid_sub1 && - ( - cd submodule_update && - git branch -t valid_sub1 origin/valid_sub1 && - $command valid_sub1 && - test_superproject_content origin/valid_sub1 && - test_dir_is_empty sub1 && - git submodule update --init --recursive && - test_submodule_content sub1 origin/valid_sub1 - ) - ' } # Test that submodule contents are correctly updated when switching diff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl index ad685d92f8..6fd7fa476b 100644 --- a/t/t0021/rot13-filter.pl +++ b/t/t0021/rot13-filter.pl @@ -30,9 +30,12 @@ # to the "list_available_blobs" response. # +use 5.008; +use lib (split(/:/, $ENV{GITPERLLIB})); use strict; use warnings; use IO::File; +use Git::Packet; my $MAX_PACKET_CONTENT_SIZE = 65516; my $log_file = shift @ARGV; @@ -55,89 +58,30 @@ sub rot13 { return $str; } -sub packet_bin_read { - my $buffer; - my $bytes_read = read STDIN, $buffer, 4; - if ( $bytes_read == 0 ) { - # EOF - Git stopped talking to us! - print $debug "STOP\n"; - exit(); - } - elsif ( $bytes_read != 4 ) { - die "invalid packet: '$buffer'"; - } - my $pkt_size = hex($buffer); - if ( $pkt_size == 0 ) { - return ( 1, "" ); - } - elsif ( $pkt_size > 4 ) { - my $content_size = $pkt_size - 4; - $bytes_read = read STDIN, $buffer, $content_size; - if ( $bytes_read != $content_size ) { - die "invalid packet ($content_size bytes expected; $bytes_read bytes read)"; - } - return ( 0, $buffer ); - } - else { - die "invalid packet size: $pkt_size"; - } -} - -sub packet_txt_read { - my ( $res, $buf ) = packet_bin_read(); - unless ( $buf eq '' or $buf =~ s/\n$// ) { - die "A non-binary line MUST be terminated by an LF."; - } - return ( $res, $buf ); -} - -sub packet_bin_write { - my $buf = shift; - print STDOUT sprintf( "%04x", length($buf) + 4 ); - print STDOUT $buf; - STDOUT->flush(); -} - -sub packet_txt_write { - packet_bin_write( $_[0] . "\n" ); -} - -sub packet_flush { - print STDOUT sprintf( "%04x", 0 ); - STDOUT->flush(); -} - print $debug "START\n"; $debug->flush(); -( packet_txt_read() eq ( 0, "git-filter-client" ) ) || die "bad initialize"; -( packet_txt_read() eq ( 0, "version=2" ) ) || die "bad version"; -( packet_bin_read() eq ( 1, "" ) ) || die "bad version end"; +packet_initialize("git-filter", 2); -packet_txt_write("git-filter-server"); -packet_txt_write("version=2"); -packet_flush(); +my %remote_caps = packet_read_and_check_capabilities("clean", "smudge", "delay"); +packet_check_and_write_capabilities(\%remote_caps, @capabilities); -( packet_txt_read() eq ( 0, "capability=clean" ) ) || die "bad capability"; -( packet_txt_read() eq ( 0, "capability=smudge" ) ) || die "bad capability"; -( packet_txt_read() eq ( 0, "capability=delay" ) ) || die "bad capability"; -( packet_bin_read() eq ( 1, "" ) ) || die "bad capability end"; - -foreach (@capabilities) { - packet_txt_write( "capability=" . $_ ); -} -packet_flush(); print $debug "init handshake complete\n"; $debug->flush(); while (1) { - my ( $command ) = packet_txt_read() =~ /^command=(.+)$/; + my ( $res, $command ) = packet_required_key_val_read("command"); + if ( $res == -1 ) { + print $debug "STOP\n"; + exit(); + } print $debug "IN: $command"; $debug->flush(); if ( $command eq "list_available_blobs" ) { # Flush - packet_bin_read(); + packet_compare_lists([1, ""], packet_bin_read()) || + die "bad list_available_blobs end"; foreach my $pathname ( sort keys %DELAY ) { if ( $DELAY{$pathname}{"requested"} >= 1 ) { @@ -161,16 +105,14 @@ sub packet_flush { $debug->flush(); packet_txt_write("status=success"); packet_flush(); - } - else { - my ( $pathname ) = packet_txt_read() =~ /^pathname=(.+)$/; + } else { + my ( $res, $pathname ) = packet_required_key_val_read("pathname"); + if ( $res == -1 ) { + die "unexpected EOF while expecting pathname"; + } print $debug " $pathname"; $debug->flush(); - if ( $pathname eq "" ) { - die "bad pathname '$pathname'"; - } - # Read until flush my ( $done, $buffer ) = packet_txt_read(); while ( $buffer ne '' ) { @@ -184,6 +126,9 @@ sub packet_flush { ( $done, $buffer ) = packet_txt_read(); } + if ( $done == -1 ) { + die "unexpected EOF after pathname '$pathname'"; + } my $input = ""; { @@ -194,6 +139,9 @@ sub packet_flush { ( $done, $buffer ) = packet_bin_read(); $input .= $buffer; } + if ( $done == -1 ) { + die "unexpected EOF while reading input for '$pathname'"; + } print $debug " " . length($input) . " [OK] -- "; $debug->flush(); } @@ -201,17 +149,13 @@ sub packet_flush { my $output; if ( exists $DELAY{$pathname} and exists $DELAY{$pathname}{"output"} ) { $output = $DELAY{$pathname}{"output"} - } - elsif ( $pathname eq "error.r" or $pathname eq "abort.r" ) { + } elsif ( $pathname eq "error.r" or $pathname eq "abort.r" ) { $output = ""; - } - elsif ( $command eq "clean" and grep( /^clean$/, @capabilities ) ) { + } elsif ( $command eq "clean" and grep( /^clean$/, @capabilities ) ) { $output = rot13($input); - } - elsif ( $command eq "smudge" and grep( /^smudge$/, @capabilities ) ) { + } elsif ( $command eq "smudge" and grep( /^smudge$/, @capabilities ) ) { $output = rot13($input); - } - else { + } else { die "bad command '$command'"; } @@ -220,25 +164,21 @@ sub packet_flush { $debug->flush(); packet_txt_write("status=error"); packet_flush(); - } - elsif ( $pathname eq "abort.r" ) { + } elsif ( $pathname eq "abort.r" ) { print $debug "[ABORT]\n"; $debug->flush(); packet_txt_write("status=abort"); packet_flush(); - } - elsif ( $command eq "smudge" and + } elsif ( $command eq "smudge" and exists $DELAY{$pathname} and - $DELAY{$pathname}{"requested"} == 1 - ) { + $DELAY{$pathname}{"requested"} == 1 ) { print $debug "[DELAYED]\n"; $debug->flush(); packet_txt_write("status=delayed"); packet_flush(); $DELAY{$pathname}{"requested"} = 2; $DELAY{$pathname}{"output"} = $output; - } - else { + } else { packet_txt_write("status=success"); packet_flush(); @@ -258,8 +198,7 @@ sub packet_flush { print $debug "."; if ( length($output) > $MAX_PACKET_CONTENT_SIZE ) { $output = substr( $output, $MAX_PACKET_CONTENT_SIZE ); - } - else { + } else { $output = ""; } } diff --git a/t/t1409-avoid-packing-refs.sh b/t/t1409-avoid-packing-refs.sh new file mode 100755 index 0000000000..e5cb8a252d --- /dev/null +++ b/t/t1409-avoid-packing-refs.sh @@ -0,0 +1,118 @@ +#!/bin/sh + +test_description='avoid rewriting packed-refs unnecessarily' + +. ./test-lib.sh + +# Add an identifying mark to the packed-refs file header line. This +# shouldn't upset readers, and it should be omitted if the file is +# ever rewritten. +mark_packed_refs () { + sed -e "s/^\(#.*\)/\1 t1409 /" <.git/packed-refs >.git/packed-refs.new && + mv .git/packed-refs.new .git/packed-refs +} + +# Verify that the packed-refs file is still marked. +check_packed_refs_marked () { + grep -q '^#.* t1409 ' .git/packed-refs +} + +test_expect_success 'setup' ' + git commit --allow-empty -m "Commit A" && + A=$(git rev-parse HEAD) && + git commit --allow-empty -m "Commit B" && + B=$(git rev-parse HEAD) && + git commit --allow-empty -m "Commit C" && + C=$(git rev-parse HEAD) +' + +test_expect_success 'do not create packed-refs file gratuitously' ' + test_must_fail test -f .git/packed-refs && + git update-ref refs/heads/foo $A && + test_must_fail test -f .git/packed-refs && + git update-ref refs/heads/foo $B && + test_must_fail test -f .git/packed-refs && + git update-ref refs/heads/foo $C $B && + test_must_fail test -f .git/packed-refs && + git update-ref -d refs/heads/foo && + test_must_fail test -f .git/packed-refs +' + +test_expect_success 'check that marking the packed-refs file works' ' + git for-each-ref >expected && + git pack-refs --all && + mark_packed_refs && + check_packed_refs_marked && + git for-each-ref >actual && + test_cmp expected actual && + git pack-refs --all && + test_must_fail check_packed_refs_marked && + git for-each-ref >actual2 && + test_cmp expected actual2 +' + +test_expect_success 'leave packed-refs untouched on update of packed' ' + git update-ref refs/heads/packed-update $A && + git pack-refs --all && + mark_packed_refs && + git update-ref refs/heads/packed-update $B && + check_packed_refs_marked +' + +test_expect_success 'leave packed-refs untouched on checked update of packed' ' + git update-ref refs/heads/packed-checked-update $A && + git pack-refs --all && + mark_packed_refs && + git update-ref refs/heads/packed-checked-update $B $A && + check_packed_refs_marked +' + +test_expect_success 'leave packed-refs untouched on verify of packed' ' + git update-ref refs/heads/packed-verify $A && + git pack-refs --all && + mark_packed_refs && + echo "verify refs/heads/packed-verify $A" | git update-ref --stdin && + check_packed_refs_marked +' + +test_expect_success 'touch packed-refs on delete of packed' ' + git update-ref refs/heads/packed-delete $A && + git pack-refs --all && + mark_packed_refs && + git update-ref -d refs/heads/packed-delete && + test_must_fail check_packed_refs_marked +' + +test_expect_success 'leave packed-refs untouched on update of loose' ' + git pack-refs --all && + git update-ref refs/heads/loose-update $A && + mark_packed_refs && + git update-ref refs/heads/loose-update $B && + check_packed_refs_marked +' + +test_expect_success 'leave packed-refs untouched on checked update of loose' ' + git pack-refs --all && + git update-ref refs/heads/loose-checked-update $A && + mark_packed_refs && + git update-ref refs/heads/loose-checked-update $B $A && + check_packed_refs_marked +' + +test_expect_success 'leave packed-refs untouched on verify of loose' ' + git pack-refs --all && + git update-ref refs/heads/loose-verify $A && + mark_packed_refs && + echo "verify refs/heads/loose-verify $A" | git update-ref --stdin && + check_packed_refs_marked +' + +test_expect_success 'leave packed-refs untouched on delete of loose' ' + git pack-refs --all && + git update-ref refs/heads/loose-delete $A && + mark_packed_refs && + git update-ref -d refs/heads/loose-delete && + check_packed_refs_marked +' + +test_done diff --git a/t/t3426-rebase-submodule.sh b/t/t3426-rebase-submodule.sh index ebf4f5e4b2..a2bba04ba9 100755 --- a/t/t3426-rebase-submodule.sh +++ b/t/t3426-rebase-submodule.sh @@ -40,4 +40,21 @@ git_rebase_interactive () { test_submodule_switch "git_rebase_interactive" +test_expect_success 'rebase interactive ignores modified submodules' ' + test_when_finished "rm -rf super sub" && + git init sub && + git -C sub commit --allow-empty -m "Initial commit" && + git init super && + git -C super submodule add ../sub && + git -C super config submodule.sub.ignore dirty && + >super/foo && + git -C super add foo && + git -C super commit -m "Initial commit" && + test_commit -C super a && + test_commit -C super b && + test_commit -C super/sub c && + set_fake_editor && + git -C super rebase -i HEAD^^ +' + test_done diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index 81c6059a2d..46f15169f5 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -688,7 +688,7 @@ test_expect_success 'checking out a commit after submodule removal needs manual git submodule update && git checkout -q HEAD^ && git checkout -q master 2>actual && - test_i18ngrep "^warning: unable to rmdir submod:" actual && + test_i18ngrep "^warning: unable to rmdir '\''submod'\'':" actual && git status -s submod >actual && echo "?? submod/" >expected && test_cmp expected actual && diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh index 9df054bf05..da10478f59 100755 --- a/t/t4201-shortlog.sh +++ b/t/t4201-shortlog.sh @@ -9,6 +9,7 @@ test_description='git shortlog . ./test-lib.sh test_expect_success 'setup' ' + test_tick && echo 1 >a1 && git add a1 && tree=$(git write-tree) && @@ -59,7 +60,7 @@ fuzz() { file=$1 && sed " s/$_x40/OBJECT_NAME/g - s/$_x05/OBJID/g + s/$_x35/OBJID/g s/^ \{6\}[CTa].*/ SUBJECT/g s/^ \{8\}[^ ].*/ CONTINUATION/g " <"$file" >"$file.fuzzy" && @@ -81,7 +82,7 @@ test_expect_success 'pretty format' ' test_expect_success '--abbrev' ' sed s/SUBJECT/OBJID/ expect.template >expect && - git shortlog --format="%h" --abbrev=5 HEAD >log && + git shortlog --format="%h" --abbrev=35 HEAD >log && fuzz log >log.predictable && test_cmp expect log.predictable ' diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index 3aa534933e..c128dfc579 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -766,4 +766,36 @@ test_expect_success 'Verify usage of %(symref:rstrip) atom' ' test_cmp expected actual ' +test_expect_success ':remotename and :remoteref' ' + git init remote-tests && + ( + cd remote-tests && + test_commit initial && + git remote add from fifth.coffee:blub && + git config branch.master.remote from && + git config branch.master.merge refs/heads/stable && + git remote add to southridge.audio:repo && + git config remote.to.push "refs/heads/*:refs/heads/pushed/*" && + git config branch.master.pushRemote to && + for pair in "%(upstream)=refs/remotes/from/stable" \ + "%(upstream:remotename)=from" \ + "%(upstream:remoteref)=refs/heads/stable" \ + "%(push)=refs/remotes/to/pushed/master" \ + "%(push:remotename)=to" \ + "%(push:remoteref)=refs/heads/pushed/master" + do + echo "${pair#*=}" >expect && + git for-each-ref --format="${pair%=*}" \ + refs/heads/master >actual && + test_cmp expect actual + done && + git branch push-simple && + git config branch.push-simple.pushRemote from && + actual="$(git for-each-ref \ + --format="%(push:remotename),%(push:remoteref)" \ + refs/heads/push-simple)" && + test from, = "$actual" + ) +' + test_done diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index f5929c46f3..6e5031f56f 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -452,7 +452,7 @@ test_expect_success 'checking out a commit before submodule moved needs manual u git mv sub sub2 && git commit -m "moved sub to sub2" && git checkout -q HEAD^ 2>actual && - test_i18ngrep "^warning: unable to rmdir sub2:" actual && + test_i18ngrep "^warning: unable to rmdir '\''sub2'\'':" actual && git status -s sub2 >actual && echo "?? sub2/" >expected && test_cmp expected actual && diff --git a/t/t7521-ignored-mode.sh b/t/t7521-ignored-mode.sh new file mode 100755 index 0000000000..91790943c3 --- /dev/null +++ b/t/t7521-ignored-mode.sh @@ -0,0 +1,233 @@ +#!/bin/sh + +test_description='git status ignored modes' + +. ./test-lib.sh + +test_expect_success 'setup initial commit and ignore file' ' + cat >.gitignore <<-\EOF && + *.ign + ignored_dir/ + !*.unignore + EOF + git add . && + git commit -m "Initial commit" +' + +test_expect_success 'Verify behavior of status on directories with ignored files' ' + test_when_finished "git clean -fdx" && + cat >expect <<-\EOF && + ? expect + ? output + ! dir/ignored/ignored_1.ign + ! dir/ignored/ignored_2.ign + ! ignored/ignored_1.ign + ! ignored/ignored_2.ign + EOF + + mkdir -p ignored dir/ignored && + touch ignored/ignored_1.ign ignored/ignored_2.ign \ + dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign && + + git status --porcelain=v2 --ignored=matching --untracked-files=all >output && + test_i18ncmp expect output +' + +test_expect_success 'Verify status behavior on directory with tracked & ignored files' ' + test_when_finished "git clean -fdx && git reset HEAD~1 --hard" && + cat >expect <<-\EOF && + ? expect + ? output + ! dir/tracked_ignored/ignored_1.ign + ! dir/tracked_ignored/ignored_2.ign + ! tracked_ignored/ignored_1.ign + ! tracked_ignored/ignored_2.ign + EOF + + mkdir -p tracked_ignored dir/tracked_ignored && + touch tracked_ignored/tracked_1 tracked_ignored/tracked_2 \ + tracked_ignored/ignored_1.ign tracked_ignored/ignored_2.ign \ + dir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 \ + dir/tracked_ignored/ignored_1.ign dir/tracked_ignored/ignored_2.ign && + + git add tracked_ignored/tracked_1 tracked_ignored/tracked_2 \ + dir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 && + git commit -m "commit tracked files" && + + git status --porcelain=v2 --ignored=matching --untracked-files=all >output && + test_i18ncmp expect output +' + +test_expect_success 'Verify status behavior on directory with untracked and ignored files' ' + test_when_finished "git clean -fdx" && + cat >expect <<-\EOF && + ? dir/untracked_ignored/untracked_1 + ? dir/untracked_ignored/untracked_2 + ? expect + ? output + ? untracked_ignored/untracked_1 + ? untracked_ignored/untracked_2 + ! dir/untracked_ignored/ignored_1.ign + ! dir/untracked_ignored/ignored_2.ign + ! untracked_ignored/ignored_1.ign + ! untracked_ignored/ignored_2.ign + EOF + + mkdir -p untracked_ignored dir/untracked_ignored && + touch untracked_ignored/untracked_1 untracked_ignored/untracked_2 \ + untracked_ignored/ignored_1.ign untracked_ignored/ignored_2.ign \ + dir/untracked_ignored/untracked_1 dir/untracked_ignored/untracked_2 \ + dir/untracked_ignored/ignored_1.ign dir/untracked_ignored/ignored_2.ign && + + git status --porcelain=v2 --ignored=matching --untracked-files=all >output && + test_i18ncmp expect output +' + +test_expect_success 'Verify status matching ignored files on ignored directory' ' + test_when_finished "git clean -fdx" && + cat >expect <<-\EOF && + ? expect + ? output + ! ignored_dir/ + EOF + + mkdir ignored_dir && + touch ignored_dir/ignored_1 ignored_dir/ignored_2 \ + ignored_dir/ignored_1.ign ignored_dir/ignored_2.ign && + + git status --porcelain=v2 --ignored=matching --untracked-files=all >output && + test_i18ncmp expect output +' + +test_expect_success 'Verify status behavior on ignored directory containing tracked file' ' + test_when_finished "git clean -fdx && git reset HEAD~1 --hard" && + cat >expect <<-\EOF && + ? expect + ? output + ! ignored_dir/ignored_1 + ! ignored_dir/ignored_1.ign + ! ignored_dir/ignored_2 + ! ignored_dir/ignored_2.ign + EOF + + mkdir ignored_dir && + touch ignored_dir/ignored_1 ignored_dir/ignored_2 \ + ignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \ + ignored_dir/tracked && + git add -f ignored_dir/tracked && + git commit -m "Force add file in ignored directory" && + git status --porcelain=v2 --ignored=matching --untracked-files=all >output && + test_i18ncmp expect output +' + +test_expect_success 'Verify matching ignored files with --untracked-files=normal' ' + test_when_finished "git clean -fdx" && + cat >expect <<-\EOF && + ? expect + ? output + ? untracked_dir/ + ! ignored_dir/ + ! ignored_files/ignored_1.ign + ! ignored_files/ignored_2.ign + EOF + + mkdir ignored_dir ignored_files untracked_dir && + touch ignored_dir/ignored_1 ignored_dir/ignored_2 \ + ignored_files/ignored_1.ign ignored_files/ignored_2.ign \ + untracked_dir/untracked && + git status --porcelain=v2 --ignored=matching --untracked-files=normal >output && + test_i18ncmp expect output +' + +test_expect_success 'Verify matching ignored files with --untracked-files=normal' ' + test_when_finished "git clean -fdx" && + cat >expect <<-\EOF && + ? expect + ? output + ? untracked_dir/ + ! ignored_dir/ + ! ignored_files/ignored_1.ign + ! ignored_files/ignored_2.ign + EOF + + mkdir ignored_dir ignored_files untracked_dir && + touch ignored_dir/ignored_1 ignored_dir/ignored_2 \ + ignored_files/ignored_1.ign ignored_files/ignored_2.ign \ + untracked_dir/untracked && + git status --porcelain=v2 --ignored=matching --untracked-files=normal >output && + test_i18ncmp expect output +' + +test_expect_success 'Verify status behavior on ignored directory containing tracked file' ' + test_when_finished "git clean -fdx && git reset HEAD~1 --hard" && + cat >expect <<-\EOF && + ? expect + ? output + ! ignored_dir/ignored_1 + ! ignored_dir/ignored_1.ign + ! ignored_dir/ignored_2 + ! ignored_dir/ignored_2.ign + EOF + + mkdir ignored_dir && + touch ignored_dir/ignored_1 ignored_dir/ignored_2 \ + ignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \ + ignored_dir/tracked && + git add -f ignored_dir/tracked && + git commit -m "Force add file in ignored directory" && + git status --porcelain=v2 --ignored=matching --untracked-files=normal >output && + test_i18ncmp expect output +' + +test_expect_success 'Verify behavior of status with --ignored=no' ' + test_when_finished "git clean -fdx" && + cat >expect <<-\EOF && + ? expect + ? output + EOF + + mkdir -p ignored dir/ignored && + touch ignored/ignored_1.ign ignored/ignored_2.ign \ + dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign && + + git status --porcelain=v2 --ignored=no --untracked-files=all >output && + test_i18ncmp expect output +' + +test_expect_success 'Verify behavior of status with --ignored=traditional and --untracked-files=all' ' + test_when_finished "git clean -fdx" && + cat >expect <<-\EOF && + ? expect + ? output + ! dir/ignored/ignored_1.ign + ! dir/ignored/ignored_2.ign + ! ignored/ignored_1.ign + ! ignored/ignored_2.ign + EOF + + mkdir -p ignored dir/ignored && + touch ignored/ignored_1.ign ignored/ignored_2.ign \ + dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign && + + git status --porcelain=v2 --ignored=traditional --untracked-files=all >output && + test_i18ncmp expect output +' + +test_expect_success 'Verify behavior of status with --ignored=traditional and --untracked-files=normal' ' + test_when_finished "git clean -fdx" && + cat >expect <<-\EOF && + ? expect + ? output + ! dir/ + ! ignored/ + EOF + + mkdir -p ignored dir/ignored && + touch ignored/ignored_1.ign ignored/ignored_2.ign \ + dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign && + + git status --porcelain=v2 --ignored=traditional --untracked-files=normal >output && + test_i18ncmp expect output +' + +test_done diff --git a/t/t9114-git-svn-dcommit-merge.sh b/t/t9114-git-svn-dcommit-merge.sh index a3d388228a..50bca62def 100755 --- a/t/t9114-git-svn-dcommit-merge.sh +++ b/t/t9114-git-svn-dcommit-merge.sh @@ -27,9 +27,7 @@ cat << EOF # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, -# MA 02111-1307 USA +# along with this program; if not, see . # EOF } diff --git a/t/test-lib.sh b/t/test-lib.sh index 9b61f16f7a..116bd6a70c 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -175,9 +175,10 @@ esac # Convenience # -# A regexp to match 5 and 40 hexdigits +# A regexp to match 5, 35 and 40 hexdigits _x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' -_x40="$_x05$_x05$_x05$_x05$_x05$_x05$_x05$_x05" +_x35="$_x05$_x05$_x05$_x05$_x05$_x05$_x05" +_x40="$_x35$_x05" # Zero SHA-1 _z40=0000000000000000000000000000000000000000 @@ -193,7 +194,7 @@ LF=' # when case-folding filenames u200c=$(printf '\342\200\214') -export _x05 _x40 _z40 LF u200c EMPTY_TREE EMPTY_BLOB +export _x05 _x35 _x40 _z40 LF u200c EMPTY_TREE EMPTY_BLOB # Each test should start with something like this, after copyright notices: # diff --git a/trace.c b/trace.c index 7508aea028..cb1293ed33 100644 --- a/trace.c +++ b/trace.c @@ -18,8 +18,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see . */ #include "cache.h" diff --git a/wrapper.c b/wrapper.c index 61aba0b5c1..d20356a776 100644 --- a/wrapper.c +++ b/wrapper.c @@ -569,7 +569,7 @@ static int warn_if_unremovable(const char *op, const char *file, int rc) if (!rc || errno == ENOENT) return 0; err = errno; - warning_errno("unable to %s %s", op, file); + warning_errno("unable to %s '%s'", op, file); errno = err; return rc; } @@ -583,7 +583,7 @@ int unlink_or_msg(const char *file, struct strbuf *err) if (!rc || errno == ENOENT) return 0; - strbuf_addf(err, "unable to unlink %s: %s", + strbuf_addf(err, "unable to unlink '%s': %s", file, strerror(errno)); return -1; } @@ -653,9 +653,9 @@ void write_file_buf(const char *path, const char *buf, size_t len) { int fd = xopen(path, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (write_in_full(fd, buf, len) < 0) - die_errno(_("could not write to %s"), path); + die_errno(_("could not write to '%s'"), path); if (close(fd)) - die_errno(_("could not close %s"), path); + die_errno(_("could not close '%s'"), path); } void write_file(const char *path, const char *fmt, ...) diff --git a/wt-status.c b/wt-status.c index ed3271c3f3..ef26f07446 100644 --- a/wt-status.c +++ b/wt-status.c @@ -658,10 +658,15 @@ static void wt_status_collect_untracked(struct wt_status *s) if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES) dir.flags |= DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES; - if (s->show_ignored_files) + if (s->show_ignored_mode) { dir.flags |= DIR_SHOW_IGNORED_TOO; - else + + if (s->show_ignored_mode == SHOW_MATCHING_IGNORED) + dir.flags |= DIR_SHOW_IGNORED_TOO_MODE_MATCHING; + } else { dir.untracked = the_index.untracked; + } + setup_standard_excludes(&dir); fill_directory(&dir, &the_index, &s->pathspec); @@ -1619,7 +1624,7 @@ static void wt_longstatus_print(struct wt_status *s) } if (s->show_untracked_files) { wt_longstatus_print_other(s, &s->untracked, _("Untracked files"), "add"); - if (s->show_ignored_files) + if (s->show_ignored_mode) wt_longstatus_print_other(s, &s->ignored, _("Ignored files"), "add -f"); if (advice_status_u_option && 2000 < s->untracked_in_ms) { status_printf_ln(s, GIT_COLOR_NORMAL, "%s", ""); @@ -2262,8 +2267,10 @@ int has_unstaged_changes(int ignore_submodules) int result; init_revisions(&rev_info, NULL); - if (ignore_submodules) + if (ignore_submodules) { rev_info.diffopt.flags.ignore_submodules = 1; + rev_info.diffopt.flags.override_submodule_config = 1; + } rev_info.diffopt.flags.quick = 1; diff_setup_done(&rev_info.diffopt); result = run_diff_files(&rev_info, 0); diff --git a/wt-status.h b/wt-status.h index 64f4d33ea1..fe27b465e2 100644 --- a/wt-status.h +++ b/wt-status.h @@ -27,6 +27,12 @@ enum untracked_status_type { SHOW_ALL_UNTRACKED_FILES }; +enum show_ignored_type { + SHOW_NO_IGNORED, + SHOW_TRADITIONAL_IGNORED, + SHOW_MATCHING_IGNORED, +}; + /* from where does this commit originate */ enum commit_whence { FROM_COMMIT, /* normal */ @@ -70,7 +76,7 @@ struct wt_status { int display_comment_prefix; int relative_paths; int submodule_summary; - int show_ignored_files; + enum show_ignored_type show_ignored_mode; enum untracked_status_type show_untracked_files; const char *ignore_submodule_arg; char color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN]; diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h index b090ad8eac..785ecb0899 100644 --- a/xdiff/xdiff.h +++ b/xdiff/xdiff.h @@ -13,8 +13,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with this library; if not, see + * . * * Davide Libenzi * diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c index 93a65680a1..0de1ef463b 100644 --- a/xdiff/xdiffi.c +++ b/xdiff/xdiffi.c @@ -13,8 +13,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with this library; if not, see + * . * * Davide Libenzi * diff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h index 8b81206c9a..8f1c7c8b04 100644 --- a/xdiff/xdiffi.h +++ b/xdiff/xdiffi.h @@ -13,8 +13,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with this library; if not, see + * . * * Davide Libenzi * diff --git a/xdiff/xemit.c b/xdiff/xemit.c index 8c88dbde38..6881445e4a 100644 --- a/xdiff/xemit.c +++ b/xdiff/xemit.c @@ -13,8 +13,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with this library; if not, see + * . * * Davide Libenzi * diff --git a/xdiff/xemit.h b/xdiff/xemit.h index d29710770c..1b9887e670 100644 --- a/xdiff/xemit.h +++ b/xdiff/xemit.h @@ -13,8 +13,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with this library; if not, see + * . * * Davide Libenzi * diff --git a/xdiff/xinclude.h b/xdiff/xinclude.h index 526ccb344d..f35c4485df 100644 --- a/xdiff/xinclude.h +++ b/xdiff/xinclude.h @@ -13,8 +13,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with this library; if not, see + * . * * Davide Libenzi * diff --git a/xdiff/xmacros.h b/xdiff/xmacros.h index 165a895a93..2809a28ca9 100644 --- a/xdiff/xmacros.h +++ b/xdiff/xmacros.h @@ -13,8 +13,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with this library; if not, see + * . * * Davide Libenzi * diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c index f338ad6c75..1659edb453 100644 --- a/xdiff/xmerge.c +++ b/xdiff/xmerge.c @@ -13,8 +13,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with this library; if not, see + * . * * Davide Libenzi * diff --git a/xdiff/xpatience.c b/xdiff/xpatience.c index 9f91702de7..a44e776328 100644 --- a/xdiff/xpatience.c +++ b/xdiff/xpatience.c @@ -13,8 +13,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with this library; if not, see + * . * * Davide Libenzi * diff --git a/xdiff/xprepare.c b/xdiff/xprepare.c index 13b55aba74..abeb8fb84e 100644 --- a/xdiff/xprepare.c +++ b/xdiff/xprepare.c @@ -13,8 +13,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with this library; if not, see + * . * * Davide Libenzi * diff --git a/xdiff/xprepare.h b/xdiff/xprepare.h index 8fb06a5374..947d9fc1bb 100644 --- a/xdiff/xprepare.h +++ b/xdiff/xprepare.h @@ -13,8 +13,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with this library; if not, see + * . * * Davide Libenzi * diff --git a/xdiff/xtypes.h b/xdiff/xtypes.h index 2511aef8d8..8442bd436e 100644 --- a/xdiff/xtypes.h +++ b/xdiff/xtypes.h @@ -13,8 +13,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with this library; if not, see + * . * * Davide Libenzi * diff --git a/xdiff/xutils.c b/xdiff/xutils.c index 04d7b32e4e..088001db4f 100644 --- a/xdiff/xutils.c +++ b/xdiff/xutils.c @@ -13,8 +13,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with this library; if not, see + * . * * Davide Libenzi * diff --git a/xdiff/xutils.h b/xdiff/xutils.h index 4646ce5752..fba7bae03c 100644 --- a/xdiff/xutils.h +++ b/xdiff/xutils.h @@ -13,8 +13,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with this library; if not, see + * . * * Davide Libenzi *