diff --git a/Documentation/RelNotes/2.54.0.adoc b/Documentation/RelNotes/2.54.0.adoc index 30ec959e7e5bf2..3fa25e06f2d1d6 100644 --- a/Documentation/RelNotes/2.54.0.adoc +++ b/Documentation/RelNotes/2.54.0.adoc @@ -124,6 +124,9 @@ UI, Workflows & Features * Handling of signed commits and tags in fast-import has been made more configurable. + * "git config list" is the official way to spell "git config -l" and + "git config --list". Use it to update the documentation. + Performance, Internal Implementation, Development Support etc. -------------------------------------------------------------- @@ -316,6 +319,14 @@ Performance, Internal Implementation, Development Support etc. log/diff machinery is being reworked a bit to make the feature compatible with more diff options, like -S/G. + * Further work to adjust the codebase for C23 that changes functions + like strchr() that discarded constness when they return a pointer into + a const string to preserve constness. + + * "git rev-list --maximal-only" has been optimized by borrowing the + logic used by "git show-branch --independent", which computes the + same kind of information much more efficiently. + Fixes since v2.53 ----------------- diff --git a/Documentation/git-var.adoc b/Documentation/git-var.adoc index b606c2d649979f..697c10adedcca6 100644 --- a/Documentation/git-var.adoc +++ b/Documentation/git-var.adoc @@ -22,7 +22,7 @@ OPTIONS Display the logical variables. In addition, all the variables of the Git configuration file .git/config are listed as well. (However, the configuration variables listing functionality - is deprecated in favor of `git config -l`.) + is deprecated in favor of `git config list`.) EXAMPLES -------- diff --git a/Documentation/gitcvs-migration.adoc b/Documentation/gitcvs-migration.adoc index 1cd1283d0f817d..905d08cd5f991c 100644 --- a/Documentation/gitcvs-migration.adoc +++ b/Documentation/gitcvs-migration.adoc @@ -49,8 +49,7 @@ them first before running git pull. ================================ The 'pull' command knows where to get updates from because of certain configuration variables that were set by the first 'git clone' -command; see `git config -l` and the linkgit:git-config[1] man -page for details. +command; see the subcommand `list` in linkgit:git-config[1] for details. ================================ You can update the shared repository with your changes by first committing diff --git a/Documentation/gitprotocol-v2.adoc b/Documentation/gitprotocol-v2.adoc index f985cb4c474953..ac5102b64f99ec 100644 --- a/Documentation/gitprotocol-v2.adoc +++ b/Documentation/gitprotocol-v2.adoc @@ -659,7 +659,7 @@ use by the client, MUST indicate prerequisites (in any) with standard applicable. + The advertised URI may alternatively contain a plaintext file that `git -config --list` would accept (with the `--file` option). The key-value +config list` would accept (with the `--file` option). The key-value pairs in this list are in the `bundle.*` namespace (see linkgit:git-config[1]). diff --git a/Documentation/gittutorial.adoc b/Documentation/gittutorial.adoc index f89ad30cf652b4..519b8d8be2cc46 100644 --- a/Documentation/gittutorial.adoc +++ b/Documentation/gittutorial.adoc @@ -432,7 +432,7 @@ bob$ git config --get remote.origin.url ------------------------------------- (The complete configuration created by `git clone` is visible using -`git config -l`, and the linkgit:git-config[1] man page +`git config list`, and the linkgit:git-config[1] man page explains the meaning of each option.) Git also keeps a pristine copy of Alice's `master` branch under the diff --git a/Documentation/technical/api-trace2.adoc b/Documentation/technical/api-trace2.adoc index cf493dae03f3ac..918e517c2e6edb 100644 --- a/Documentation/technical/api-trace2.adoc +++ b/Documentation/technical/api-trace2.adoc @@ -1253,7 +1253,7 @@ it. $ git config --system color.ui never $ git config --global color.ui always $ git config --local color.ui auto -$ git config --list --show-scope | grep 'color.ui' +$ git config list --show-scope | grep 'color.ui' system color.ui=never global color.ui=always local color.ui=auto diff --git a/Documentation/user-manual.adoc b/Documentation/user-manual.adoc index 64009baf370231..5ec65cebe29dd7 100644 --- a/Documentation/user-manual.adoc +++ b/Documentation/user-manual.adoc @@ -2865,7 +2865,7 @@ stored in Git configuration variables, which you can see using linkgit:git-config[1]: ------------------------------------------------- -$ git config -l +$ git config list core.repositoryformatversion=0 core.filemode=true core.logallrefupdates=true diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index dada55884a0b06..878aa7f0ed9eb5 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -1029,8 +1029,8 @@ static int read_proc_receive_report(struct packet_reader *reader, for (;;) { struct object_id old_oid, new_oid; - const char *head; - const char *refname; + char *head; + char *refname; char *p; enum packet_read_status status; @@ -1054,7 +1054,8 @@ static int read_proc_receive_report(struct packet_reader *reader, } *p++ = '\0'; if (!strcmp(head, "option")) { - const char *key, *val; + char *key; + const char *val; if (!hint || !(report || new_report)) { if (!once++) diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 854d82ece368b3..8f63003709242e 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -25,6 +25,7 @@ #include "oidset.h" #include "oidmap.h" #include "packfile.h" +#include "commit-reach.h" #include "quote.h" #include "strbuf.h" @@ -633,6 +634,61 @@ static int try_bitmap_disk_usage(struct rev_info *revs, return 0; } +/* + * If revs->maximal_only is set and no other walk modifiers are provided, + * run a faster computation to filter the independent commits and prepare + * them for output. Set revs->no_walk to prevent later walking. + * + * If this algorithm doesn't apply, then no changes are made to revs. + */ +static void prepare_maximal_independent(struct rev_info *revs) +{ + struct commit_list *c; + + if (!revs->maximal_only) + return; + + for (c = revs->commits; c; c = c->next) { + if (c->item->object.flags & UNINTERESTING) + return; + } + + if (revs->limited || + revs->topo_order || + revs->first_parent_only || + revs->reverse || + revs->max_count >= 0 || + revs->skip_count >= 0 || + revs->min_age != (timestamp_t)-1 || + revs->max_age != (timestamp_t)-1 || + revs->min_parents > 0 || + revs->max_parents >= 0 || + revs->prune_data.nr || + revs->count || + revs->left_right || + revs->boundary || + revs->tag_objects || + revs->tree_objects || + revs->blob_objects || + revs->filter.choice || + revs->reflog_info || + revs->diff || + revs->grep_filter.pattern_list || + revs->grep_filter.header_list || + revs->verbose_header || + revs->print_parents || + revs->edge_hint || + revs->unpacked || + revs->no_kept_objects || + revs->line_level_traverse) + return; + + reduce_heads_replace(&revs->commits); + + /* Modify 'revs' to only output this commit list. */ + revs->no_walk = 1; +} + int cmd_rev_list(int argc, const char **argv, const char *prefix, @@ -875,6 +931,9 @@ int cmd_rev_list(int argc, if (prepare_revision_walk(&revs)) die("revision walk setup failed"); + + prepare_maximal_independent(&revs); + if (revs.tree_objects) mark_edges_uninteresting(&revs, show_edge, 0); diff --git a/convert.c b/convert.c index a34ec6ecdc057e..eae36c8a5936f4 100644 --- a/convert.c +++ b/convert.c @@ -1168,7 +1168,8 @@ static int ident_to_worktree(const char *src, size_t len, struct strbuf *buf, int ident) { struct object_id oid; - char *to_free = NULL, *dollar, *spc; + char *to_free = NULL; + const char *dollar, *spc; int cnt; if (!ident) diff --git a/git-compat-util.h b/git-compat-util.h index 4b4ea2498f13ef..ae1bdc90a4cd6a 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -335,11 +335,7 @@ static inline int is_path_owned_by_current_uid(const char *path, #endif #ifndef find_last_dir_sep -static inline char *git_find_last_dir_sep(const char *path) -{ - return strrchr(path, '/'); -} -#define find_last_dir_sep git_find_last_dir_sep +#define find_last_dir_sep(path) strrchr((path), '/') #endif #ifndef has_dir_sep @@ -467,6 +463,21 @@ void set_warn_routine(report_fn routine); report_fn get_warn_routine(void); void set_die_is_recursing_routine(int (*routine)(void)); +/* + * Check that an out-parameter is "at least as const as" a matching + * in-parameter. For example, skip_prefix() will return "out" that is a subset + * of "str". So: + * + * const str, const out: ok + * non-const str, const out: ok + * non-const str, non-const out: ok + * const str, non-const out: compile error + * + * See the skip_prefix macro below for an example of use. + */ +#define CONST_OUTPARAM(in, out) \ + ((const char **)(0 ? ((*(out) = (in)),(out)) : (out))) + /* * If the string "str" begins with the string found in "prefix", return true. * The "out" parameter is set to "str + strlen(prefix)" (i.e., to the point in @@ -483,8 +494,10 @@ void set_die_is_recursing_routine(int (*routine)(void)); * [skip prefix if present, otherwise use whole string] * skip_prefix(name, "refs/heads/", &name); */ -static inline bool skip_prefix(const char *str, const char *prefix, - const char **out) +#define skip_prefix(str, prefix, out) \ + skip_prefix_impl((str), (prefix), CONST_OUTPARAM((str), (out))) +static inline bool skip_prefix_impl(const char *str, const char *prefix, + const char **out) { do { if (!*prefix) { @@ -889,8 +902,10 @@ static inline size_t xsize_t(off_t len) * is done via tolower(), so it is strictly ASCII (no multi-byte characters or * locale-specific conversions). */ -static inline bool skip_iprefix(const char *str, const char *prefix, - const char **out) +#define skip_iprefix(str, prefix, out) \ + skip_iprefix_impl((str), (prefix), CONST_OUTPARAM((str), (out))) +static inline bool skip_iprefix_impl(const char *str, const char *prefix, + const char **out) { do { if (!*prefix) { diff --git a/hex.c b/hex.c index 865a232167553d..bc756722ca623b 100644 --- a/hex.c +++ b/hex.c @@ -54,9 +54,9 @@ int get_oid_hex(const char *hex, struct object_id *oid) return get_oid_hex_algop(hex, oid, the_hash_algo); } -int parse_oid_hex_algop(const char *hex, struct object_id *oid, - const char **end, - const struct git_hash_algo *algop) +int parse_oid_hex_algop_impl(const char *hex, struct object_id *oid, + const char **end, + const struct git_hash_algo *algop) { int ret = get_oid_hex_algop(hex, oid, algop); if (!ret) diff --git a/hex.h b/hex.h index e9ccb54065c0bc..1e9a65d83a4f6b 100644 --- a/hex.h +++ b/hex.h @@ -40,8 +40,10 @@ char *oid_to_hex(const struct object_id *oid); /* same static buffer */ * other invalid character. end is only updated on success; otherwise, it is * unmodified. */ -int parse_oid_hex_algop(const char *hex, struct object_id *oid, const char **end, - const struct git_hash_algo *algo); +#define parse_oid_hex_algop(hex, oid, end, algo) \ + parse_oid_hex_algop_impl((hex), (oid), CONST_OUTPARAM((hex), (end)), (algo)) +int parse_oid_hex_algop_impl(const char *hex, struct object_id *oid, const char **end, + const struct git_hash_algo *algo); /* * These functions work like get_oid_hex and parse_oid_hex, but they will parse diff --git a/http-push.c b/http-push.c index 06c3acbb5d9a21..d143fe28455623 100644 --- a/http-push.c +++ b/http-push.c @@ -99,7 +99,7 @@ static struct object_list *objects; struct repo { char *url; - char *path; + const char *path; int path_len; int has_info_refs; int can_update_info_refs; diff --git a/http.c b/http.c index d8d016891b7974..67c9c6fc60673d 100644 --- a/http.c +++ b/http.c @@ -748,7 +748,7 @@ static int has_proxy_cert_password(void) static int redact_sensitive_header(struct strbuf *header, size_t offset) { int ret = 0; - const char *sensitive_header; + char *sensitive_header; if (trace_curl_redact && (skip_iprefix(header->buf + offset, "Authorization:", &sensitive_header) || @@ -765,7 +765,7 @@ static int redact_sensitive_header(struct strbuf *header, size_t offset) } else if (trace_curl_redact && skip_iprefix(header->buf + offset, "Cookie:", &sensitive_header)) { struct strbuf redacted_header = STRBUF_INIT; - const char *cookie; + char *cookie; while (isspace(*sensitive_header)) sensitive_header++; diff --git a/pager.c b/pager.c index 5531fff50eb73f..35b210e0484f90 100644 --- a/pager.c +++ b/pager.c @@ -108,10 +108,11 @@ const char *git_pager(struct repository *r, int stdout_is_tty) static void setup_pager_env(struct strvec *env) { - const char **argv; + char **argv; int i; char *pager_env = xstrdup(PAGER_ENV); - int n = split_cmdline(pager_env, &argv); + /* split_cmdline splits in place, so we know the result is writable */ + int n = split_cmdline(pager_env, (const char ***)&argv); if (n < 0) die("malformed build-time PAGER_ENV: %s", diff --git a/pkt-line.h b/pkt-line.h index 3b33cc64f34dcc..e6cf85e34ee3c4 100644 --- a/pkt-line.h +++ b/pkt-line.h @@ -184,7 +184,7 @@ struct packet_reader { int pktlen; /* the last line read */ - const char *line; + char *line; /* indicates if a line has been peeked */ int line_peeked; diff --git a/pseudo-merge.c b/pseudo-merge.c index a2d5bd85f95ebf..ff18b6c364245e 100644 --- a/pseudo-merge.c +++ b/pseudo-merge.c @@ -638,14 +638,21 @@ static int pseudo_merge_commit_cmp(const void *va, const void *vb) return 0; } -static struct pseudo_merge_commit *find_pseudo_merge(const struct pseudo_merge_map *pm, - uint32_t pos) +static int find_pseudo_merge(const struct pseudo_merge_map *pm, uint32_t pos, + struct pseudo_merge_commit *out) { + const unsigned char *at; + if (!pm->commits_nr) - return NULL; + return 0; - return bsearch(&pos, pm->commits, pm->commits_nr, - PSEUDO_MERGE_COMMIT_RAWSZ, pseudo_merge_commit_cmp); + at = bsearch(&pos, pm->commits, pm->commits_nr, + PSEUDO_MERGE_COMMIT_RAWSZ, pseudo_merge_commit_cmp); + if (!at) + return 0; + + read_pseudo_merge_commit_at(out, at); + return 1; } int apply_pseudo_merges_for_commit(const struct pseudo_merge_map *pm, @@ -653,16 +660,15 @@ int apply_pseudo_merges_for_commit(const struct pseudo_merge_map *pm, struct commit *commit, uint32_t commit_pos) { struct pseudo_merge *merge; - struct pseudo_merge_commit *merge_commit; + struct pseudo_merge_commit merge_commit; int ret = 0; - merge_commit = find_pseudo_merge(pm, commit_pos); - if (!merge_commit) + if (!find_pseudo_merge(pm, commit_pos, &merge_commit)) return 0; - if (merge_commit->pseudo_merge_ofs & ((uint64_t)1<<63)) { + if (merge_commit.pseudo_merge_ofs & ((uint64_t)1<<63)) { struct pseudo_merge_commit_ext ext = { 0 }; - off_t ofs = merge_commit->pseudo_merge_ofs & ~((uint64_t)1<<63); + off_t ofs = merge_commit.pseudo_merge_ofs & ~((uint64_t)1<<63); uint32_t i; if (pseudo_merge_ext_at(pm, &ext, ofs) < -1) { @@ -673,11 +679,11 @@ int apply_pseudo_merges_for_commit(const struct pseudo_merge_map *pm, } for (i = 0; i < ext.nr; i++) { - if (nth_pseudo_merge_ext(pm, &ext, merge_commit, i) < 0) + if (nth_pseudo_merge_ext(pm, &ext, &merge_commit, i) < 0) return ret; merge = pseudo_merge_at(pm, &commit->object.oid, - merge_commit->pseudo_merge_ofs); + merge_commit.pseudo_merge_ofs); if (!merge) return ret; @@ -687,7 +693,7 @@ int apply_pseudo_merges_for_commit(const struct pseudo_merge_map *pm, } } else { merge = pseudo_merge_at(pm, &commit->object.oid, - merge_commit->pseudo_merge_ofs); + merge_commit.pseudo_merge_ofs); if (!merge) return ret; diff --git a/range-diff.c b/range-diff.c index 2712a9a107ab06..8e2dd2eb193eb9 100644 --- a/range-diff.c +++ b/range-diff.c @@ -88,7 +88,7 @@ static int read_patches(const char *range, struct string_list *list, line = contents.buf; size = contents.len; for (; size > 0; size -= len, line += len) { - const char *p; + char *p; char *eol; eol = memchr(line, '\n', size); diff --git a/refs/files-backend.c b/refs/files-backend.c index 0537a72b2af9e0..b3b0c25f84e503 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -2190,7 +2190,7 @@ static int show_one_reflog_ent(struct files_ref_store *refs, char *email_end, *message; timestamp_t timestamp; int tz; - const char *p = sb->buf; + char *p = sb->buf; /* old SP new SP name SP time TAB msg LF */ if (!sb->len || sb->buf[sb->len - 1] != '\n' || diff --git a/run-command.c b/run-command.c index 574d5c40f0a1a5..c146a56532a139 100644 --- a/run-command.c +++ b/run-command.c @@ -604,11 +604,11 @@ static void trace_add_env(struct strbuf *dst, const char *const *deltaenv) /* Last one wins, see run-command.c:prep_childenv() for context */ for (e = deltaenv; e && *e; e++) { struct strbuf key = STRBUF_INIT; - char *equals = strchr(*e, '='); + const char *equals = strchr(*e, '='); if (equals) { strbuf_add(&key, *e, equals - *e); - string_list_insert(&envs, key.buf)->util = equals + 1; + string_list_insert(&envs, key.buf)->util = (void *)(equals + 1); } else { string_list_insert(&envs, *e)->util = NULL; } diff --git a/send-pack.c b/send-pack.c index 07ecfae4de92a2..b4361d5610dc91 100644 --- a/send-pack.c +++ b/send-pack.c @@ -175,8 +175,8 @@ static int receive_status(struct repository *r, ret = receive_unpack_status(reader); while (1) { struct object_id old_oid, new_oid; - const char *head; - const char *refname; + char *head; + char *refname; char *p; if (packet_reader_read(reader) != PACKET_READ_NORMAL) break; @@ -190,7 +190,8 @@ static int receive_status(struct repository *r, *p++ = '\0'; if (!strcmp(head, "option")) { - const char *key, *val; + char *key; + const char *val; if (!hint || !(report || new_report)) { if (!once++) diff --git a/t/perf/p6011-rev-list-maximal.sh b/t/perf/p6011-rev-list-maximal.sh new file mode 100755 index 00000000000000..e868e83ff847b3 --- /dev/null +++ b/t/perf/p6011-rev-list-maximal.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +test_description='Test --maximal-only and --independent options' + +. ./perf-lib.sh + +test_perf_default_repo + +test_expect_success 'setup' ' + git for-each-ref --format="%(*objecttype) %(objecttype) %(objectname)" \ + "refs/heads/*" "refs/tags/*" | + sed -n -e "s/^commit commit //p" -e "s/^ commit //p" | + head -n 50 >commits && + git commit-graph write --reachable +' + +test_perf 'merge-base --independent' ' + git merge-base --independent $(cat commits) >/dev/null +' + +test_perf 'rev-list --maximal-only' ' + git rev-list --maximal-only $(cat commits) >/dev/null +' + +test_perf 'rev-list --maximal-only --since' ' + git rev-list --maximal-only --since=2000-01-01 $(cat commits) >/dev/null +' + +test_done diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh index d0a2a866100d56..a95ba576fa24b2 100755 --- a/t/t6000-rev-list-misc.sh +++ b/t/t6000-rev-list-misc.sh @@ -263,4 +263,35 @@ test_expect_success 'rev-list --boundary incompatible with --maximal-only' ' test_grep "cannot be used together" err ' +test_expect_success 'rev-list --maximal-only and --pretty' ' + test_when_finished rm -rf repo && + + git init repo && + test_commit -C repo 1 && + oid1=$(git -C repo rev-parse HEAD) && + test_commit -C repo 2 && + oid2=$(git -C repo rev-parse HEAD) && + git -C repo checkout --detach HEAD~1 && + test_commit -C repo 3 && + oid3=$(git -C repo rev-parse HEAD) && + + cat >expect <<-EOF && + commit $oid3 + $oid3 + commit $oid2 + $oid2 + EOF + + git -C repo rev-list --pretty="%H" --maximal-only $oid1 $oid2 $oid3 >out && + test_cmp expect out && + + cat >expect <<-EOF && + $oid3 + $oid2 + EOF + + git -C repo log --pretty="%H" --maximal-only $oid1 $oid2 $oid3 >out && + test_cmp expect out +' + test_done diff --git a/t/t6600-test-reach.sh b/t/t6600-test-reach.sh index 2613075894282d..dc0421ed2f3726 100755 --- a/t/t6600-test-reach.sh +++ b/t/t6600-test-reach.sh @@ -837,4 +837,49 @@ test_expect_success 'rev-list --maximal-only (range)' ' --first-parent --exclude-first-parent-only ' +test_expect_success 'rev-list --maximal-only matches merge-base --independent' ' + # Mix of independent and dependent + git merge-base --independent \ + refs/heads/commit-5-2 \ + refs/heads/commit-3-2 \ + refs/heads/commit-2-5 >expect && + sort expect >expect.sorted && + git rev-list --maximal-only \ + refs/heads/commit-5-2 \ + refs/heads/commit-3-2 \ + refs/heads/commit-2-5 >actual && + sort actual >actual.sorted && + test_cmp expect.sorted actual.sorted && + + # All independent commits. + git merge-base --independent \ + refs/heads/commit-5-2 \ + refs/heads/commit-4-3 \ + refs/heads/commit-3-4 \ + refs/heads/commit-2-5 >expect && + sort expect >expect.sorted && + git rev-list --maximal-only \ + refs/heads/commit-5-2 \ + refs/heads/commit-4-3 \ + refs/heads/commit-3-4 \ + refs/heads/commit-2-5 >actual && + sort actual >actual.sorted && + test_cmp expect.sorted actual.sorted && + + # Only one independent. + git merge-base --independent \ + refs/heads/commit-1-1 \ + refs/heads/commit-4-2 \ + refs/heads/commit-4-4 \ + refs/heads/commit-8-4 >expect && + sort expect >expect.sorted && + git rev-list --maximal-only \ + refs/heads/commit-1-1 \ + refs/heads/commit-4-2 \ + refs/heads/commit-4-4 \ + refs/heads/commit-8-4 >actual && + sort actual >actual.sorted && + test_cmp expect.sorted actual.sorted +' + test_done diff --git a/transport-helper.c b/transport-helper.c index 570d7c6439569a..4e5d1d914fb12a 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -783,7 +783,8 @@ static int push_update_ref_status(struct strbuf *buf, if (starts_with(buf->buf, "option ")) { struct object_id old_oid, new_oid; - const char *key, *val; + char *key; + const char *val; char *p; if (!state->hint || !(state->report || state->new_report))