diff options
111 files changed, 1776 insertions, 518 deletions
diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt index 56130e4bec..62d11a5cd7 100644 --- a/Documentation/MyFirstContribution.txt +++ b/Documentation/MyFirstContribution.txt @@ -1256,6 +1256,38 @@ index 88f126184c..38da593a60 100644 [[now-what]] == My Patch Got Emailed - Now What? +Please give reviewers enough time to process your initial patch before +sending an updated version. That is, resist the temptation to send a new +version immediately, because others may have already started reviewing +your initial version. + +While waiting for review comments, you may find mistakes in your initial +patch, or perhaps realize a different and better way to achieve the goal +of the patch. In this case you may communicate your findings to other +reviewers as follows: + + - If the mistakes you found are minor, send a reply to your patch as if + you were a reviewer and mention that you will fix them in an + updated version. + + - On the other hand, if you think you want to change the course so + drastically that reviews on the initial patch would be a waste of + time (for everyone involved), retract the patch immediately with + a reply like "I am working on a much better approach, so please + ignore this patch and wait for the updated version." + +Now, the above is a good practice if you sent your initial patch +prematurely without polish. But a better approach of course is to avoid +sending your patch prematurely in the first place. + +Please be considerate of the time needed by reviewers to examine each +new version of your patch. Rather than seeing the initial version right +now (followed by several "oops, I like this version better than the +previous one" patches over 2 days), reviewers would strongly prefer if a +single polished version came 2 days later instead, and that version with +fewer mistakes were the only one they would need to review. + + [[reviewing]] === Responding to Reviews diff --git a/Documentation/RelNotes/2.42.0.txt b/Documentation/RelNotes/2.42.0.txt index fac88307fa..0d31aed9f9 100644 --- a/Documentation/RelNotes/2.42.0.txt +++ b/Documentation/RelNotes/2.42.0.txt @@ -26,6 +26,18 @@ UI, Workflows & Features were regular files, to allow "git diff <(process) <(substitution)" some shells support. + * Help newbies by suggesting that there are cases where force-pushing + is a valid and sensible thing to update a branch at a remote + repository, rather than reconciling with merge/rebase. + + * "git blame --contents=file" has been taught to work in a bare + repository. + + * "git branch -f X" to repoint the branch X said that X was "checked + out" in another worktree, even when branch X was not and instead + being bisected or rebased. The message was reworded to say the + branch was "in use". + Performance, Internal Implementation, Development Support etc. @@ -57,6 +69,29 @@ Performance, Internal Implementation, Development Support etc. * "imap-send" codepaths got cleaned up to get rid of unused parameters. + * Enumerating refs in the packed-refs file, while excluding refs that + match certain patterns, has been optimized. + + * Mark-up unused parameters in the code so that we can eventually + enable -Wunused-parameter by default. + + * Instead of inventing a custom counter variables for debugging, + use existing trace2 facility in the fsync customization codepath. + + * "git branch --list --format=<format>" and friends are taught + a new "%(describe)" placeholder. + + * Clarify how to choose the starting point for a new topic in + developer guidance document. + + * The implementation of "get_sha1_hex()" that reads a hexadecimal + string that spells a full object name has been extended to cope + with any hash function used in the repository, but the "sha1" in + its name survived. Rename it to get_hash_hex(), a name that is + more consistent within its friends like get_hash_hex_algop(). + + * Command line parser fix, and a small parse-options API update. + Fixes since v2.41 ----------------- @@ -161,7 +196,7 @@ Fixes since v2.41 been corrected. (merge a096a889f4 jk/cherry-pick-revert-status later to maint). - * A few places failed to differenciate the case where the index is + * A few places failed to differentiate the case where the index is truly empty (nothing added) and we haven't yet read from the on-disk index file, which have been corrected. (merge 2ee045eea1 js/empty-index-fixes later to maint). @@ -179,6 +214,44 @@ Fixes since v2.41 which has been corrected. (merge f4a8fde057 jc/pathspec-match-with-common-prefix later to maint). + * "git fsck --no-progress" still spewed noise from the commit-graph + subsystem, which has been corrected. + (merge 9281cd07f0 tb/fsck-no-progress later to maint). + + * Various offset computation in the code that accesses the packfiles + and other data in the object layer has been hardened against + arithmetic overflow, especially on 32-bit systems. + (merge 9a25cad7e0 tb/object-access-overflow-protection later to maint). + + * Names of MinGW header files are spelled in mixed case in some + source files, but the build host can be using case sensitive + filesystem with header files with their name spelled in all + lowercase. + (merge 4a53d0d0bc mh/mingw-case-sensitive-build later to maint). + + * Update message mark-up for i18n in "git bundle". + (merge bbb6acd998 dk/bundle-i18n-more later to maint). + + * "git tag --list --points-at X" showed tags that directly refers to + object X, but did not list a tag that points at such a tag, which + has been corrected. + + * "./configure --with-expat=no" did not work as a way to refuse use + of the expat library on a system with the library installed, which + has been corrected. + (merge fb8f7269c2 ah/autoconf-fixes later to maint). + + * When the user edits "rebase -i" todo file so that it starts with a + "fixup", which would make it invalid, the command truncated the + rest of the file before giving an error and returning the control + back to the user. Stop truncating to make it easier to correct + such a malformed todo file. + (merge 9645a087c2 ah/sequencer-rewrite-todo-fix later to maint). + + * Rewrite the description of giving a custom command to the + submodule.<name>.update configuration variable. + (merge 7cebc5bd78 pv/doc-submodule-update-settings later to maint). + * Other code cleanup, docfix, build fix, etc. (merge 51f9d2e563 sa/doc-ls-remote later to maint). (merge c6d26a9dda jk/format-patch-message-id-unleak later to maint). @@ -201,3 +274,15 @@ Fixes since v2.41 (merge 1876a5ae15 ks/t4205-test-describe-with-abbrev-fix later to maint). (merge 6e6a529b57 jk/fsck-indices-in-worktrees later to maint). (merge 3e81b896f7 rs/packet-length-simplify later to maint). + (merge 4c9cb51fe7 mh/doc-credential-helpers later to maint). + (merge 3437f549dd jr/gitignore-doc-example-markup later to maint). + (merge 947ebd62a0 jc/am-parseopt-fix later to maint). + (merge e12cb98e1e jc/branch-parseopt-fix later to maint). + (merge d6f598e443 jc/gitignore-doc-pattern-markup later to maint). + (merge a2dad4868b jc/transport-parseopt-fix later to maint). + (merge 68cbb20e73 jc/parse-options-show-branch later to maint). + (merge 3821eb6c3d jc/parse-options-reset later to maint). + (merge c48af99a3e bb/trace2-comment-fix later to maint). + (merge c95ae3ff9c rs/describe-parseopt-fix later to maint). + (merge 36f76d2a25 rs/pack-objects-parseopt-fix later to maint). + (merge 30c8c55cbf jc/tree-walk-drop-base-offset later to maint). diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index b218e27357..973d7a81d4 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -3,45 +3,101 @@ Submitting Patches == Guidelines -Here are some guidelines for people who want to contribute their code to this -software. There is also a link:MyFirstContribution.html[step-by-step tutorial] +Here are some guidelines for contributing back to this +project. There is also a link:MyFirstContribution.html[step-by-step tutorial] available which covers many of these same guidelines. -[[base-branch]] -=== Decide what to base your work on. - -In general, always base your work on the oldest branch that your -change is relevant to. - -* A bugfix should be based on `maint` in general. If the bug is not - present in `maint`, base it on `master`. For a bug that's not yet - in `master`, find the topic that introduces the regression, and - base your work on the tip of the topic. - -* A new feature should be based on `master` in general. If the new - feature depends on other topics that are in `next`, but not in - `master`, fork a branch from the tip of `master`, merge these topics - to the branch, and work on that branch. You can remind yourself of - how you prepared the base with `git log --first-parent master..`. - -* Corrections and enhancements to a topic not yet in `master` should - be based on the tip of that topic. If the topic has not been merged - to `next`, it's alright to add a note to squash minor corrections - into the series. - -* In the exceptional case that a new feature depends on several topics - not in `master`, start working on `next` or `seen` privately and - send out patches only for discussion. Once your new feature starts - to stabilize, you would have to rebase it (see the "depends on other - topics" above). - -* Some parts of the system have dedicated maintainers with their own - repositories (see the section "Subsystems" below). Changes to - these parts should be based on their trees. - -To find the tip of a topic branch, run `git log --first-parent -master..seen` and look for the merge commit. The second parent of this -commit is the tip of the topic branch. +[[choose-starting-point]] +=== Choose a starting point. + +As a preliminary step, you must first choose a starting point for your +work. Typically this means choosing a branch, although technically +speaking it is actually a particular commit (typically the HEAD, or tip, +of the branch). + +There are several important branches to be aware of. Namely, there are +four integration branches as discussed in linkgit:gitworkflows[7]: + +* maint +* master +* next +* seen + +The branches lower on the list are typically descendants of the ones +that come before it. For example, `maint` is an "older" branch than +`master` because `master` usually has patches (commits) on top of +`maint`. + +There are also "topic" branches, which contain work from other +contributors. Topic branches are created by the Git maintainer (in +their fork) to organize the current set of incoming contributions on +the mailing list, and are itemized in the regular "What's cooking in +git.git" announcements. To find the tip of a topic branch, run `git log +--first-parent master..seen` and look for the merge commit. The second +parent of this commit is the tip of the topic branch. + +There is one guiding principle for choosing the right starting point: in +general, always base your work on the oldest integration branch that +your change is relevant to (see "Merge upwards" in +linkgit:gitworkflows[7]). What this principle means is that for the +vast majority of cases, the starting point for new work should be the +latest HEAD commit of `maint` or `master` based on the following cases: + +* If you are fixing bugs in the released version, use `maint` as the + starting point (which may mean you have to fix things without using + new API features on the cutting edge that recently appeared in + `master` but were not available in the released version). + +* Otherwise (such as if you are adding new features) use `master`. + + +NOTE: In exceptional cases, a bug that was introduced in an old +version may have to be fixed for users of releases that are much older +than the recent releases. `git describe --contains X` may describe +`X` as `v2.30.0-rc2-gXXXXXX` for the commit `X` that introduced the +bug, and the bug may be so high-impact that we may need to issue a new +maintenance release for Git 2.30.x series, when "Git 2.41.0" is the +current release. In such a case, you may want to use the tip of the +maintenance branch for the 2.30.x series, which may be available in the +`maint-2.30` branch in https://github.com/gitster/git[the maintainer's +"broken out" repo]. + +This also means that `next` or `seen` are inappropriate starting points +for your work, if you want your work to have a realistic chance of +graduating to `master`. They are simply not designed to be used as a +base for new work; they are only there to make sure that topics in +flight work well together. This is why both `next` and `seen` are +frequently re-integrated with incoming patches on the mailing list and +force-pushed to replace previous versions of themselves. A topic that is +literally built on top of `next` cannot be merged to `master` without +dragging in all the other topics in `next`, some of which may not be +ready. + +For example, if you are making tree-wide changes, while somebody else is +also making their own tree-wide changes, your work may have severe +overlap with the other person's work. This situation may tempt you to +use `next` as your starting point (because it would have the other +person's work included in it), but doing so would mean you'll not only +depend on the other person's work, but all the other random things from +other contributors that are already integrated into `next`. And as soon +as `next` is updated with a new version, all of your work will need to +be rebased anyway in order for them to be cleanly applied by the +maintainer. + +Under truly exceptional circumstances where you absolutely must depend +on a select few topic branches that are already in `next` but not in +`master`, you may want to create your own custom base-branch by forking +`master` and merging the required topic branches to it. You could then +work on top of this base-branch. But keep in mind that this base-branch +would only be known privately to you. So when you are ready to send +your patches to the list, be sure to communicate how you created it in +your cover letter. This critical piece of information would allow +others to recreate your base-branch on their end in order for them to +try out your work. + +Finally, note that some parts of the system have dedicated maintainers +with their own separate source code repositories (see the section +"Subsystems" below). [[separate-commits]] === Make separate commits for logically separate changes. @@ -317,10 +373,13 @@ Please make sure your patch does not add commented out debugging code, or include any extra files which do not relate to what your patch is trying to achieve. Make sure to review your patch after generating it, to ensure accuracy. Before -sending out, please make sure it cleanly applies to the base you -have chosen in the "Decide what to base your work on" section, -and unless it targets the `master` branch (which is the default), -mark your patches as such. +sending out, please make sure it cleanly applies to the starting point you +have chosen in the "Choose a starting point" section. + +NOTE: From the perspective of those reviewing your patch, the `master` +branch is the default expected starting point. So if you have chosen a +different starting point, please communicate this choice in your cover +letter. [[send-patches]] diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 2e0318770b..11b2bc3121 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -14,6 +14,7 @@ SYNOPSIS [--points-at=<object>] [--merged[=<object>]] [--no-merged[=<object>]] [--contains[=<object>]] [--no-contains[=<object>]] + [--exclude=<pattern> ...] DESCRIPTION ----------- @@ -102,6 +103,11 @@ OPTIONS Do not print a newline after formatted refs where the format expands to the empty string. +--exclude=<pattern>:: + If one or more patterns are given, only refs which do not match + any excluded pattern(s) are shown. Matching is done using the + same rules as `<pattern>` above. + FIELD NAMES ----------- @@ -258,6 +264,29 @@ ahead-behind:<committish>:: commits ahead and behind, respectively, when comparing the output ref to the `<committish>` specified in the format. +describe[:options]:: + A human-readable name, like linkgit:git-describe[1]; + empty string for undescribable commits. The `describe` string may + be followed by a colon and one or more comma-separated options. ++ +-- +tags=<bool-value>;; + Instead of only considering annotated tags, consider + lightweight tags as well; see the corresponding option in + linkgit:git-describe[1] for details. +abbrev=<number>;; + Use at least <number> hexadecimal digits; see the corresponding + option in linkgit:git-describe[1] for details. +match=<pattern>;; + Only consider tags matching the given `glob(7)` pattern, + excluding the "refs/tags/" prefix; see the corresponding option + in linkgit:git-describe[1] for details. +exclude=<pattern>;; + Do not consider tags matching the given `glob(7)` pattern, + excluding the "refs/tags/" prefix; see the corresponding option + in linkgit:git-describe[1] for details. +-- + In addition to the above, for commit and tag objects, the header field names (`tree`, `parent`, `object`, `type`, and `tag`) can be used to specify the value in the header field. diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt index 4d3ab6b9f9..695730609a 100644 --- a/Documentation/git-submodule.txt +++ b/Documentation/git-submodule.txt @@ -95,7 +95,7 @@ too (and can also report changes to a submodule's work tree). init [--] [<path>...]:: Initialize the submodules recorded in the index (which were added and committed elsewhere) by setting `submodule.$name.url` - in .git/config. It uses the same setting from `.gitmodules` as + in `.git/config`, using the same setting from `.gitmodules` as a template. If the URL is relative, it will be resolved using the default remote. If there is no default remote, the current repository will be assumed to be upstream. @@ -105,9 +105,12 @@ If no path is specified and submodule.active has been configured, submodules configured to be active will be initialized, otherwise all submodules are initialized. + -When present, it will also copy the value of `submodule.$name.update`. -This command does not alter existing information in .git/config. -You can then customize the submodule clone URLs in .git/config +It will also copy the value of `submodule.$name.update`, if present in +the `.gitmodules` file, to `.git/config`, but (1) this command does not +alter existing information in `.git/config`, and (2) `submodule.$name.update` +that is set to a custom command is *not* copied for security reasons. ++ +You can then customize the submodule clone URLs in `.git/config` for your local setup and proceed to `git submodule update`; you can also just use `git submodule update --init` without the explicit 'init' step if you do not intend to customize @@ -143,6 +146,8 @@ the submodules. The "updating" can be done in several ways depending on command line options and the value of `submodule.<name>.update` configuration variable. The command line option takes precedence over the configuration variable. If neither is given, a 'checkout' is performed. +(note: what is in `.gitmodules` file is irrelevant at this point; +see `git submodule init` above for how `.gitmodules` is used). The 'update' procedures supported both from the command line as well as through the `submodule.<name>.update` configuration are: @@ -160,16 +165,18 @@ checked out in the submodule. merge;; the commit recorded in the superproject will be merged into the current branch in the submodule. -The following 'update' procedures are only available via the -`submodule.<name>.update` configuration variable: +The following update procedures have additional limitations: - custom command;; arbitrary shell command that takes a single - argument (the sha1 of the commit recorded in the - superproject) is executed. When `submodule.<name>.update` - is set to '!command', the remainder after the exclamation mark - is the custom command. + custom command;; mechanism for running arbitrary commands with the + commit ID as an argument. Specifically, if the + `submodule.<name>.update` configuration variable is set to + `!custom command`, the object name of the commit recorded in the + superproject for the submodule is appended to the `custom command` + string and executed. Note that this mechanism is not supported in + the `.gitmodules` file or on the command line. - none;; the submodule is not updated. + none;; the submodule is not updated. This update procedure is not + allowed on the command line. If the submodule is not yet initialized, and you just want to use the setting as stored in `.gitmodules`, you can automatically initialize the diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt index 4c17f2356c..5e0964ef41 100644 --- a/Documentation/gitignore.txt +++ b/Documentation/gitignore.txt @@ -88,7 +88,7 @@ PATTERN FORMAT Put a backslash ("`\`") in front of the first "`!`" for patterns that begin with a literal "`!`", for example, "`\!important!.txt`". - - The slash '/' is used as the directory separator. Separators may + - The slash "`/`" is used as the directory separator. Separators may occur at the beginning, middle or end of the `.gitignore` search pattern. - If there is a separator at the beginning or middle (or both) of the @@ -174,10 +174,10 @@ EXAMPLES is not relevant if there is already a middle slash in the pattern. - - The pattern "foo/*", matches "foo/test.json" - (a regular file), "foo/bar" (a directory), but it does not match - "foo/bar/hello.c" (a regular file), as the asterisk in the - pattern does not match "bar/hello.c" which has a slash in it. + - The pattern `foo/*`, matches `foo/test.json` + (a regular file), `foo/bar` (a directory), but it does not match + `foo/bar/hello.c` (a regular file), as the asterisk in the + pattern does not match `bar/hello.c` which has a slash in it. -------------------------------------------------------------- $ git status diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt index dcee09b500..d9bec8b187 100644 --- a/Documentation/gitmodules.txt +++ b/Documentation/gitmodules.txt @@ -43,9 +43,9 @@ submodule.<name>.update:: command in the superproject. This is only used by `git submodule init` to initialize the configuration variable of the same name. Allowed values here are 'checkout', 'rebase', - 'merge' or 'none'. See description of 'update' command in - linkgit:git-submodule[1] for their meaning. For security - reasons, the '!command' form is not accepted here. + 'merge' or 'none', but not '!command' (for security reasons). + See the description of the 'update' command in + linkgit:git-submodule[1] for more details. submodule.<name>.branch:: A remote branch name for tracking updates in the upstream submodule. diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 07e72c28b8..7313f8aa3e 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.41.GIT +DEF_VER=v2.42.0-rc0 LF=' ' @@ -2806,7 +2806,9 @@ void setup_scoreboard(struct blame_scoreboard *sb, parent_oid = &head_oid; } - setup_work_tree(); + if (!sb->contents_from) + setup_work_tree(); + sb->final = fake_working_tree_commit(sb->repo, &sb->revs->diffopt, sb->path, sb->contents_from, @@ -471,7 +471,7 @@ int validate_new_branchname(const char *name, struct strbuf *ref, int force) if ((path = branch_checked_out(ref->buf))) die(_("cannot force update the branch '%s' " - "checked out at '%s'"), + "used by worktree at '%s'"), ref->buf + strlen("refs/heads/"), path); return 1; diff --git a/builtin/am.c b/builtin/am.c index dcb89439b1..8bde034fae 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -786,7 +786,7 @@ static int split_mail_conv(mail_conv_fn fn, struct am_state *state, * A split_mail_conv() callback that converts an StGit patch to an RFC2822 * message suitable for parsing with git-mailinfo. */ -static int stgit_patch_to_mail(FILE *out, FILE *in, int keep_cr) +static int stgit_patch_to_mail(FILE *out, FILE *in, int keep_cr UNUSED) { struct strbuf sb = STRBUF_INIT; int subject_printed = 0; @@ -869,7 +869,7 @@ static int split_mail_stgit_series(struct am_state *state, const char **paths, * A split_patches_conv() callback that converts a mercurial patch to a RFC2822 * message suitable for parsing with git-mailinfo. */ -static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr) +static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr UNUSED) { struct strbuf sb = STRBUF_INIT; int rc = 0; @@ -2347,12 +2347,9 @@ int cmd_am(int argc, const char **argv, const char *prefix) N_("pass -b flag to git-mailinfo"), KEEP_NON_PATCH), OPT_BOOL('m', "message-id", &state.message_id, N_("pass -m flag to git-mailinfo")), - OPT_SET_INT_F(0, "keep-cr", &keep_cr, - N_("pass --keep-cr flag to git-mailsplit for mbox format"), - 1, PARSE_OPT_NONEG), - OPT_SET_INT_F(0, "no-keep-cr", &keep_cr, - N_("do not pass --keep-cr flag to git-mailsplit independent of am.keepcr"), - 0, PARSE_OPT_NONEG), + OPT_SET_INT(0, "keep-cr", &keep_cr, + N_("pass --keep-cr flag to git-mailsplit for mbox format"), + 1), OPT_BOOL('c', "scissors", &state.scissors, N_("strip everything before a scissors line")), OPT_CALLBACK_F(0, "quoted-cr", &state.quoted_cr, N_("action"), diff --git a/builtin/branch.c b/builtin/branch.c index a27bc0a3df..08da650516 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -701,7 +701,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) int reflog = 0, quiet = 0, icase = 0, force = 0, recurse_submodules_explicit = 0; enum branch_track track; - struct ref_filter filter; + struct ref_filter filter = REF_FILTER_INIT; static struct ref_sorting *sorting; struct string_list sorting_options = STRING_LIST_INIT_DUP; struct ref_format format = REF_FORMAT_INIT; @@ -720,8 +720,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")), OPT_BOOL(0, "unset-upstream", &unset_upstream, N_("unset the upstream info")), OPT__COLOR(&branch_use_color, N_("use colored output")), - OPT_SET_INT('r', "remotes", &filter.kind, N_("act on remote-tracking branches"), - FILTER_REFS_REMOTES), + OPT_SET_INT_F('r', "remotes", &filter.kind, N_("act on remote-tracking branches"), + FILTER_REFS_REMOTES, + PARSE_OPT_NONEG), OPT_CONTAINS(&filter.with_commit, N_("print only branches that contain the commit")), OPT_NO_CONTAINS(&filter.no_commit, N_("print only branches that don't contain the commit")), OPT_WITH(&filter.with_commit, N_("print only branches that contain the commit")), @@ -729,8 +730,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT__ABBREV(&filter.abbrev), OPT_GROUP(N_("Specific git-branch actions:")), - OPT_SET_INT('a', "all", &filter.kind, N_("list both remote-tracking and local branches"), - FILTER_REFS_REMOTES | FILTER_REFS_BRANCHES), + OPT_SET_INT_F('a', "all", &filter.kind, N_("list both remote-tracking and local branches"), + FILTER_REFS_REMOTES | FILTER_REFS_BRANCHES, + PARSE_OPT_NONEG), OPT_BIT('d', "delete", &delete, N_("delete fully merged branch"), 1), OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2), OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1), @@ -759,7 +761,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix) setup_ref_filter_porcelain_msg(); - memset(&filter, 0, sizeof(filter)); filter.kind = FILTER_REFS_BRANCHES; filter.abbrev = -1; @@ -855,6 +856,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) print_columns(&output, colopts, NULL); string_list_clear(&output, 0); ref_sorting_release(sorting); + ref_filter_clear(&filter); return 0; } else if (edit_description) { const char *branch_name; diff --git a/builtin/checkout.c b/builtin/checkout.c index 2ff9625644..f53612f468 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -916,7 +916,7 @@ static void report_tracking(struct branch_info *new_branch_info) struct strbuf sb = STRBUF_INIT; struct branch *branch = branch_get(new_branch_info->name); - if (!format_tracking_info(branch, &sb, AHEAD_BEHIND_FULL)) + if (!format_tracking_info(branch, &sb, AHEAD_BEHIND_FULL, 1)) return; fputs(sb.buf, stdout); strbuf_release(&sb); diff --git a/builtin/clone.c b/builtin/clone.c index c65378b3d2..c6357af949 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -161,10 +161,7 @@ static struct option builtin_clone_options[] = { N_("set config inside the new repository")), OPT_STRING_LIST(0, "server-option", &server_options, N_("server-specific"), N_("option to transmit")), - OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"), - TRANSPORT_FAMILY_IPV4), - OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"), - TRANSPORT_FAMILY_IPV6), + OPT_IPVERSION(&family), OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options), OPT_BOOL(0, "also-filter-submodules", &option_filter_submodules, N_("apply partial clone filters to submodules")), diff --git a/builtin/count-objects.c b/builtin/count-objects.c index 97cdfb0ac5..2d4bb5e8d0 100644 --- a/builtin/count-objects.c +++ b/builtin/count-objects.c @@ -82,7 +82,7 @@ static int count_cruft(const char *basename UNUSED, const char *path, return 0; } -static int print_alternate(struct object_directory *odb, void *data) +static int print_alternate(struct object_directory *odb, void *data UNUSED) { printf("alternate: "); quote_c_style(odb->path, NULL, stdout, 0); diff --git a/builtin/describe.c b/builtin/describe.c index 7ce23e1b8e..b28a4a1f82 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -25,6 +25,7 @@ #include "wildmatch.h" #define MAX_TAGS (FLAG_BITS - 1) +#define DEFAULT_CANDIDATES 10 define_commit_slab(commit_names, struct commit_name *); @@ -41,7 +42,7 @@ static int tags; /* Allow lightweight tags */ static int longformat; static int first_parent; static int abbrev = -1; /* unspecified */ -static int max_candidates = 10; +static int max_candidates = DEFAULT_CANDIDATES; static struct hashmap names; static int have_util; static struct string_list patterns = STRING_LIST_INIT_NODUP; @@ -557,6 +558,15 @@ static void describe(const char *arg, int last_one) strbuf_release(&sb); } +static int option_parse_exact_match(const struct option *opt, const char *arg, + int unset) +{ + BUG_ON_OPT_ARG(arg); + + max_candidates = unset ? DEFAULT_CANDIDATES : 0; + return 0; +} + int cmd_describe(int argc, const char **argv, const char *prefix) { int contains = 0; @@ -568,8 +578,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "long", &longformat, N_("always use long format")), OPT_BOOL(0, "first-parent", &first_parent, N_("only follow first parent")), OPT__ABBREV(&abbrev), - OPT_SET_INT(0, "exact-match", &max_candidates, - N_("only output exact matches"), 0), + OPT_CALLBACK_F(0, "exact-match", NULL, NULL, + N_("only output exact matches"), + PARSE_OPT_NOARG, option_parse_exact_match), OPT_INTEGER(0, "candidates", &max_candidates, N_("consider <n> most recent tags (default: 10)")), OPT_STRING_LIST(0, "match", &patterns, N_("pattern"), diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index d62caa6c8b..c9ba35f143 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -99,7 +99,7 @@ static const char diff_tree_usage[] = " --root include the initial commit as diff against /dev/null\n" COMMON_DIFF_OPTIONS_HELP; -static void diff_tree_tweak_rev(struct rev_info *rev, struct setup_revision_opt *opt) +static void diff_tree_tweak_rev(struct rev_info *rev) { if (!rev->diffopt.output_format) { if (rev->dense_combined_merges) diff --git a/builtin/fetch.c b/builtin/fetch.c index a01ecad574..eed4a7cdb6 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -2207,10 +2207,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) OPT_CALLBACK_F(0, "refmap", NULL, N_("refmap"), N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg), OPT_STRING_LIST('o', "server-option", &server_options, N_("server-specific"), N_("option to transmit")), - OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"), - TRANSPORT_FAMILY_IPV4), - OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"), - TRANSPORT_FAMILY_IPV6), + OPT_IPVERSION(&family), OPT_STRING_LIST(0, "negotiation-tip", &negotiation_tip, N_("revision"), N_("report that we have only objects reachable from this object")), OPT_BOOL(0, "negotiate-only", &negotiate_only, diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 15409337f8..350bfa6e81 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -24,7 +24,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) struct string_list sorting_options = STRING_LIST_INIT_DUP; int maxcount = 0, icase = 0, omit_empty = 0; struct ref_array array; - struct ref_filter filter; + struct ref_filter filter = REF_FILTER_INIT; struct ref_format format = REF_FORMAT_INIT; struct strbuf output = STRBUF_INIT; struct strbuf err = STRBUF_INIT; @@ -47,6 +47,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")), OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")), OPT__COLOR(&format.use_color, N_("respect format colors")), + OPT_REF_FILTER_EXCLUDE(&filter), OPT_REF_SORT(&sorting_options), OPT_CALLBACK(0, "points-at", &filter.points_at, N_("object"), N_("print only refs which points at the given object"), @@ -61,7 +62,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) }; memset(&array, 0, sizeof(array)); - memset(&filter, 0, sizeof(filter)); format.format = "%(objectname) %(objecttype)\t%(refname)"; @@ -121,8 +121,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) strbuf_release(&err); strbuf_release(&output); ref_array_clear(&array); - free_commit_list(filter.with_commit); - free_commit_list(filter.no_commit); + ref_filter_clear(&filter); ref_sorting_release(sorting); strvec_clear(&vec); return 0; diff --git a/builtin/fsck.c b/builtin/fsck.c index 768bebe268..c1d0290026 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -92,11 +92,11 @@ static int objerror(struct object *obj, const char *err) return -1; } -static int fsck_error_func(struct fsck_options *o, +static int fsck_error_func(struct fsck_options *o UNUSED, const struct object_id *oid, enum object_type object_type, enum fsck_msg_type msg_type, - enum fsck_msg_id msg_id, + enum fsck_msg_id msg_id UNUSED, const char *message) { switch (msg_type) { @@ -121,7 +121,7 @@ static int fsck_error_func(struct fsck_options *o, static struct object_array pending; static int mark_object(struct object *obj, enum object_type type, - void *data, struct fsck_options *options) + void *data, struct fsck_options *options UNUSED) { struct object *parent = data; @@ -206,8 +206,8 @@ static int traverse_reachable(void) return !!result; } -static int mark_used(struct object *obj, enum object_type object_type, - void *data, struct fsck_options *options) +static int mark_used(struct object *obj, int type UNUSED, + void *data UNUSED, struct fsck_options *options UNUSED) { if (!obj) return 1; diff --git a/builtin/grep.c b/builtin/grep.c index ce866523e2..50e712a184 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -643,7 +643,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, strbuf_addstr(&name, base->buf + tn_len); match = tree_entry_interesting(repo->index, &entry, &name, - 0, pathspec); + pathspec); strbuf_setlen(&name, name_base_len); if (match == all_entries_not_interesting) diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 3da879d138..006ffdc9c5 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -221,7 +221,8 @@ static void cleanup_thread(void) } static int mark_link(struct object *obj, enum object_type type, - void *data, struct fsck_options *options) + void *data UNUSED, + struct fsck_options *options UNUSED) { if (!obj) return -1; diff --git a/builtin/log.c b/builtin/log.c index 1b119eaf0b..db3a88bfe9 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -718,8 +718,7 @@ static int show_tree_object(const struct object_id *oid UNUSED, return 0; } -static void show_setup_revisions_tweak(struct rev_info *rev, - struct setup_revision_opt *opt) +static void show_setup_revisions_tweak(struct rev_info *rev) { if (rev->first_parent_only) diff_merges_default_to_first_parent(rev); @@ -862,8 +861,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix) return cmd_log_deinit(cmd_log_walk(&rev), &rev); } -static void log_setup_revisions_tweak(struct rev_info *rev, - struct setup_revision_opt *opt) +static void log_setup_revisions_tweak(struct rev_info *rev) { if (rev->diffopt.flags.default_follow_renames && diff_check_follow_pathspec(&rev->prune_data, 0)) diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 53073d64cb..f558db5f3b 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -343,7 +343,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) struct object_id oid; struct tree *tree; int i, full_tree = 0; - int chomp_prefix = prefix && *prefix; + int full_name = !prefix || !*prefix; read_tree_fn_t fn = NULL; enum ls_tree_cmdmode cmdmode = MODE_DEFAULT; int null_termination = 0; @@ -365,8 +365,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) MODE_NAME_STATUS), OPT_CMDMODE(0, "object-only", &cmdmode, N_("list only objects"), MODE_OBJECT_ONLY), - OPT_SET_INT(0, "full-name", &chomp_prefix, - N_("use full path names"), 0), + OPT_BOOL(0, "full-name", &full_name, N_("use full path names")), OPT_BOOL(0, "full-tree", &full_tree, N_("list entire tree; not just current directory " "(implies --full-name)")), @@ -387,7 +386,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix) if (full_tree) prefix = NULL; - options.prefix = chomp_prefix ? prefix : NULL; + options.prefix = full_name ? NULL : prefix; /* * We wanted to detect conflicts between --name-only and diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 6f7db436d2..0de42aecf4 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -324,7 +324,9 @@ static void unresolved(const struct traverse_info *info, struct name_entry n[3]) * The successful merge rules are the same as for the three-way merge * in git-read-tree. */ -static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *info) +static int threeway_callback(int n UNUSED, unsigned long mask, + unsigned long dirmask UNUSED, + struct name_entry *entry, struct traverse_info *info) { /* Same in both? */ if (same_entry(entry+1, entry+2) || both_empty(entry+1, entry+2)) { diff --git a/builtin/mktag.c b/builtin/mktag.c index 43e2766db4..d8e0b5afc0 100644 --- a/builtin/mktag.c +++ b/builtin/mktag.c @@ -18,11 +18,11 @@ static int option_strict = 1; static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT; -static int mktag_fsck_error_func(struct fsck_options *o, - const struct object_id *oid, - enum object_type object_type, +static int mktag_fsck_error_func(struct fsck_options *o UNUSED, + const struct object_id *oid UNUSED, + enum object_type object_type UNUSED, enum fsck_msg_type msg_type, - enum fsck_msg_id msg_id, + enum fsck_msg_id msg_id UNUSED, const char *message) { switch (msg_type) { diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 06b33d49e9..d2a162d528 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -4117,6 +4117,18 @@ static void add_extra_kept_packs(const struct string_list *names) } } +static int option_parse_quiet(const struct option *opt, const char *arg, + int unset) +{ + BUG_ON_OPT_ARG(arg); + + if (!unset) + progress = 0; + else if (!progress) + progress = 1; + return 0; +} + static int option_parse_index_version(const struct option *opt, const char *arg, int unset) { @@ -4178,8 +4190,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) LIST_OBJECTS_FILTER_INIT; struct option pack_objects_options[] = { - OPT_SET_INT('q', "quiet", &progress, - N_("do not show progress meter"), 0), + OPT_CALLBACK_F('q', "quiet", NULL, NULL, + N_("do not show progress meter"), + PARSE_OPT_NOARG, option_parse_quiet), OPT_SET_INT(0, "progress", &progress, N_("show progress meter"), 1), OPT_SET_INT(0, "all-progress", &progress, @@ -4255,8 +4268,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) N_("ignore this pack")), OPT_INTEGER(0, "compression", &pack_compression_level, N_("pack compression level")), - OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents, - N_("do not hide commits by grafts"), 0), + OPT_BOOL(0, "keep-true-parents", &grafts_keep_true_parents, + N_("do not hide commits by grafts")), OPT_BOOL(0, "use-bitmap-index", &use_bitmap_index, N_("use a bitmap index if available to speed up counting objects")), OPT_SET_INT(0, "write-bitmap-index", &write_bitmap_index, diff --git a/builtin/push.c b/builtin/push.c index 82603a5570..2e708383c2 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -301,21 +301,21 @@ static void setup_default_push_refspecs(int *flags, struct remote *remote) static const char message_advice_pull_before_push[] = N_("Updates were rejected because the tip of your current branch is behind\n" - "its remote counterpart. Integrate the remote changes (e.g.\n" - "'git pull ...') before pushing again.\n" + "its remote counterpart. If you want to integrate the remote changes,\n" + "use 'git pull' before pushing again.\n" "See the 'Note about fast-forwards' in 'git push --help' for details."); static const char message_advice_checkout_pull_push[] = N_("Updates were rejected because a pushed branch tip is behind its remote\n" - "counterpart. Check out this branch and integrate the remote changes\n" - "(e.g. 'git pull ...') before pushing again.\n" + "counterpart. If you want to integrate the remote changes, use 'git pull'\n" + "before pushing again.\n" "See the 'Note about fast-forwards' in 'git push --help' for details."); static const char message_advice_ref_fetch_first[] = - N_("Updates were rejected because the remote contains work that you do\n" - "not have locally. This is usually caused by another repository pushing\n" - "to the same ref. You may want to first integrate the remote changes\n" - "(e.g., 'git pull ...') before pushing again.\n" + N_("Updates were rejected because the remote contains work that you do not\n" + "have locally. This is usually caused by another repository pushing to\n" + "the same ref. If you want to integrate the remote changes, use\n" + "'git pull' before pushing again.\n" "See the 'Note about fast-forwards' in 'git push --help' for details."); static const char message_advice_ref_already_exists[] = @@ -327,10 +327,10 @@ static const char message_advice_ref_needs_force[] = "without using the '--force' option.\n"); static const char message_advice_ref_needs_update[] = - N_("Updates were rejected because the tip of the remote-tracking\n" - "branch has been updated since the last checkout. You may want\n" - "to integrate those changes locally (e.g., 'git pull ...')\n" - "before forcing an update.\n"); + N_("Updates were rejected because the tip of the remote-tracking branch has\n" + "been updated since the last checkout. If you want to integrate the\n" + "remote changes, use 'git pull' before pushing again.\n" + "See the 'Note about fast-forwards' in 'git push --help' for details."); static void advise_pull_before_push(void) { @@ -627,10 +627,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) PARSE_OPT_OPTARG, option_parse_push_signed), OPT_BIT(0, "atomic", &flags, N_("request atomic transaction on remote side"), TRANSPORT_PUSH_ATOMIC), OPT_STRING_LIST('o', "push-option", &push_options_cmdline, N_("server-specific"), N_("option to transmit")), - OPT_SET_INT('4', "ipv4", &family, N_("use IPv4 addresses only"), - TRANSPORT_FAMILY_IPV4), - OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"), - TRANSPORT_FAMILY_IPV6), + OPT_IPVERSION(&family), OPT_END() }; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index a7fe8c4d9a..fb8e1549d1 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -90,7 +90,7 @@ static struct object_id push_cert_oid; static struct signature_check sigcheck; static const char *push_cert_nonce; static const char *cert_nonce_seed; -static struct string_list hidden_refs = STRING_LIST_INIT_DUP; +static struct strvec hidden_refs = STRVEC_INIT; static const char *NONCE_UNSOLICITED = "UNSOLICITED"; static const char *NONCE_BAD = "BAD"; @@ -338,7 +338,9 @@ static void write_head_info(void) { static struct oidset seen = OIDSET_INIT; - for_each_ref(show_ref_cb, &seen); + refs_for_each_fullref_in(get_main_ref_store(the_repository), "", + hidden_refs_to_excludes(&hidden_refs), + show_ref_cb, &seen); for_each_alternate_ref(show_one_alternate_ref, &seen); oidset_clear(&seen); if (!sent_capabilities) @@ -2620,7 +2622,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) packet_flush(1); oid_array_clear(&shallow); oid_array_clear(&ref); - string_list_clear(&hidden_refs, 0); + strvec_clear(&hidden_refs); free((void *)push_cert_nonce); return 0; } diff --git a/builtin/remote.c b/builtin/remote.c index 479a5191d4..d91bbe728d 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -168,10 +168,9 @@ static int add(int argc, const char **argv, const char *prefix) struct option options[] = { OPT_BOOL('f', "fetch", &fetch, N_("fetch the remote branches")), OPT_SET_INT(0, "tags", &fetch_tags, - N_("import all tags and associated objects when fetching"), + N_("import all tags and associated objects when fetching\n" + "or do not fetch any tag at all (--no-tags)"), TAGS_SET), - OPT_SET_INT(0, NULL, &fetch_tags, - N_("or do not fetch any tag at all (--no-tags)"), TAGS_UNSET), OPT_STRING_LIST('t', "track", &track, N_("branch"), N_("branch(es) to track")), OPT_STRING('m', "master", &master, N_("branch"), N_("master branch")), diff --git a/builtin/repack.c b/builtin/repack.c index f913e9a8a2..aea5ca9d44 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -105,46 +105,38 @@ static void collect_pack_filenames(struct string_list *fname_nonkept_list, struct string_list *fname_kept_list, const struct string_list *extra_keep) { - DIR *dir; - struct dirent *e; - char *fname; + struct packed_git *p; struct strbuf buf = STRBUF_INIT; - if (!(dir = opendir(packdir))) - return; - - while ((e = readdir(dir)) != NULL) { - size_t len; + for (p = get_all_packs(the_repository); p; p = p->next) { int i; + const char *base; - if (!strip_suffix(e->d_name, ".idx", &len)) + if (!p->pack_local) continue; - strbuf_reset(&buf); - strbuf_add(&buf, e->d_name, len); - strbuf_addstr(&buf, ".pack"); + base = pack_basename(p); for (i = 0; i < extra_keep->nr; i++) - if (!fspathcmp(buf.buf, extra_keep->items[i].string)) + if (!fspathcmp(base, extra_keep->items[i].string)) break; - fname = xmemdupz(e->d_name, len); + strbuf_reset(&buf); + strbuf_addstr(&buf, base); + strbuf_strip_suffix(&buf, ".pack"); - if ((extra_keep->nr > 0 && i < extra_keep->nr) || - (file_exists(mkpath("%s/%s.keep", packdir, fname)))) { - string_list_append_nodup(fname_kept_list, fname); - } else { + if ((extra_keep->nr > 0 && i < extra_keep->nr) || p->pack_keep) + string_list_append(fname_kept_list, buf.buf); + else { struct string_list_item *item; - item = string_list_append_nodup(fname_nonkept_list, - fname); - if (file_exists(mkpath("%s/%s.mtimes", packdir, fname))) + item = string_list_append(fname_nonkept_list, buf.buf); + if (p->is_cruft) item->util = (void*)(uintptr_t)CRUFT_PACK; } } - closedir(dir); - strbuf_release(&buf); string_list_sort(fname_kept_list); + strbuf_release(&buf); } static void remove_redundant_pack(const char *dir_name, const char *base_name) diff --git a/builtin/replace.c b/builtin/replace.c index 9ceaa25233..da59600ad2 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -49,7 +49,7 @@ struct show_data { static int show_reference(struct repository *r, const char *refname, const struct object_id *oid, - int flag, void *cb_data) + int flag UNUSED, void *cb_data) { struct show_data *data = cb_data; @@ -409,7 +409,7 @@ struct check_mergetag_data { const char **argv; }; -static int check_one_mergetag(struct commit *commit, +static int check_one_mergetag(struct commit *commit UNUSED, struct commit_extra_header *extra, void *data) { diff --git a/builtin/reset.c b/builtin/reset.c index 7f18dc03b8..4b018d20e3 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -338,18 +338,25 @@ int cmd_reset(int argc, const char **argv, const char *prefix) OPT__QUIET(&quiet, N_("be quiet, only report errors")), OPT_BOOL(0, "no-refresh", &no_refresh, N_("skip refreshing the index after reset")), - OPT_SET_INT(0, "mixed", &reset_type, - N_("reset HEAD and index"), MIXED), - OPT_SET_INT(0, "soft", &reset_type, N_("reset only HEAD"), SOFT), - OPT_SET_INT(0, "hard", &reset_type, - N_("reset HEAD, index and working tree"), HARD), - OPT_SET_INT(0, "merge", &reset_type, - N_("reset HEAD, index and working tree"), MERGE), - OPT_SET_INT(0, "keep", &reset_type, - N_("reset HEAD but keep local changes"), KEEP), + OPT_SET_INT_F(0, "mixed", &reset_type, + N_("reset HEAD and index"), + MIXED, PARSE_OPT_NONEG), + OPT_SET_INT_F(0, "soft", &reset_type, + N_("reset only HEAD"), + SOFT, PARSE_OPT_NONEG), + OPT_SET_INT_F(0, "hard", &reset_type, + N_("reset HEAD, index and working tree"), + HARD, PARSE_OPT_NONEG), + OPT_SET_INT_F(0, "merge", &reset_type, + N_("reset HEAD, index and working tree"), + MERGE, PARSE_OPT_NONEG), + OPT_SET_INT_F(0, "keep", &reset_type, + N_("reset HEAD but keep local changes"), + KEEP, PARSE_OPT_NONEG), OPT_CALLBACK_F(0, "recurse-submodules", NULL, - "reset", "control recursive updating of submodules", - PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater), + "reset", "control recursive updating of submodules", + PARSE_OPT_OPTARG, + option_parse_recurse_submodules_worktree_updater), OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that removed paths will be added later")), diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 434646b074..fde8861ca4 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -225,7 +225,7 @@ static int anti_reference(const char *refname, const struct object_id *oid, return 0; } -static int show_abbrev(const struct object_id *oid, void *cb_data) +static int show_abbrev(const struct object_id *oid, void *cb_data UNUSED) { show_rev(NORMAL, oid, NULL); return 0; diff --git a/builtin/show-branch.c b/builtin/show-branch.c index a86b3c7677..b01ec761d2 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -649,7 +649,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) int with_current_branch = 0; int head_at = -1; int topics = 0; - int dense = 1; + int sparse = 0; const char *reflog_base = NULL; struct option builtin_show_branch_options[] = { OPT_BOOL('a', "all", &all_heads, @@ -671,17 +671,17 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) N_("show possible merge bases")), OPT_BOOL(0, "independent", &independent, N_("show refs unreachable from any other ref")), - OPT_SET_INT(0, "topo-order", &sort_order, - N_("show commits in topological order"), - REV_SORT_IN_GRAPH_ORDER), + OPT_SET_INT_F(0, "topo-order", &sort_order, + N_("show commits in topological order"), + REV_SORT_IN_GRAPH_ORDER, PARSE_OPT_NONEG), OPT_BOOL(0, "topics", &topics, N_("show only commits not on the first branch")), - OPT_SET_INT(0, "sparse", &dense, - N_("show merges reachable from only one tip"), 0), - OPT_SET_INT(0, "date-order", &sort_order, - N_("topologically sort, maintaining date order " - "where possible"), - REV_SORT_BY_COMMIT_DATE), + OPT_SET_INT(0, "sparse", &sparse, + N_("show merges reachable from only one tip"), 1), + OPT_SET_INT_F(0, "date-order", &sort_order, + N_("topologically sort, maintaining date order " + "where possible"), + REV_SORT_BY_COMMIT_DATE, PARSE_OPT_NONEG), OPT_CALLBACK_F('g', "reflog", &reflog_base, N_("<n>[,<base>]"), N_("show <n> most recent ref-log entries starting at " "base"), @@ -940,7 +940,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) !is_merge_point && (this_flag & (1u << REV_SHIFT))) continue; - if (dense && is_merge && + if (!sparse && is_merge && omit_in_dense(commit, rev, num_rev)) continue; for (i = 0; i < num_rev; i++) { diff --git a/builtin/tag.c b/builtin/tag.c index 7d34af416c..3918eacbb5 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -121,7 +121,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn, return had_error; } -static int collect_tags(const char *name, const char *ref, +static int collect_tags(const char *name UNUSED, const char *ref, const struct object_id *oid, void *cb_data) { struct string_list *ref_list = cb_data; @@ -155,7 +155,7 @@ static int delete_tags(const char **argv) return result; } -static int verify_tag(const char *name, const char *ref, +static int verify_tag(const char *name, const char *ref UNUSED, const struct object_id *oid, void *cb_data) { int flags; @@ -445,7 +445,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) struct msg_arg msg = { .buf = STRBUF_INIT }; struct ref_transaction *transaction; struct strbuf err = STRBUF_INIT; - struct ref_filter filter; + struct ref_filter filter = REF_FILTER_INIT; struct ref_sorting *sorting; struct string_list sorting_options = STRING_LIST_INIT_DUP; struct ref_format format = REF_FORMAT_INIT; @@ -504,7 +504,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix) git_config(git_tag_config, &sorting_options); memset(&opt, 0, sizeof(opt)); - memset(&filter, 0, sizeof(filter)); filter.lines = -1; opt.sign = -1; @@ -660,6 +659,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) cleanup: ref_sorting_release(sorting); + ref_filter_clear(&filter); strbuf_release(&buf); strbuf_release(&ref); strbuf_release(&reflog_msg); diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 84b68304ed..32505255a0 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -215,7 +215,8 @@ static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf) * Verify its reachability and validity recursively and write it out. */ static int check_object(struct object *obj, enum object_type type, - void *data, struct fsck_options *options) + void *data UNUSED, + struct fsck_options *options UNUSED) { struct obj_buffer *obj_buf; diff --git a/builtin/worktree.c b/builtin/worktree.c index 2ce39b593c..4cd01842de 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -53,14 +53,14 @@ "(branch with no commits) for this repository, you can do so\n" \ "using the --orphan flag:\n" \ "\n" \ - " git worktree add --orphan -b %s %s\n") + " git worktree add --orphan -b %s %s\n") #define WORKTREE_ADD_ORPHAN_NO_DASH_B_HINT_TEXT \ _("If you meant to create a worktree containing a new orphan branch\n" \ "(branch with no commits) for this repository, you can do so\n" \ "using the --orphan flag:\n" \ "\n" \ - " git worktree add --orphan %s\n") + " git worktree add --orphan %s\n") static const char * const git_worktree_usage[] = { BUILTIN_WORKTREE_ADD_USAGE, @@ -271,10 +271,10 @@ int verify_bundle(struct repository *r, list_refs(r, 0, NULL); } - printf_ln("The bundle uses this hash algorithm: %s", + printf_ln(_("The bundle uses this hash algorithm: %s"), header->hash_algo->name); if (header->filter.choice) - printf_ln("The bundle uses this filter: %s", + printf_ln(_("The bundle uses this filter: %s"), list_objects_filter_spec(&header->filter)); } cleanup: diff --git a/commit-graph.c b/commit-graph.c index efc697e437..0aa1640d15 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -480,7 +480,7 @@ static int add_graph_to_chain(struct commit_graph *g, if (!cur_g || !oideq(&oids[n], &cur_g->oid) || - !hasheq(oids[n].hash, g->chunk_base_graphs + g->hash_len * n)) { + !hasheq(oids[n].hash, g->chunk_base_graphs + st_mult(g->hash_len, n))) { warning(_("commit-graph chain does not match")); return 0; } @@ -490,8 +490,15 @@ static int add_graph_to_chain(struct commit_graph *g, g->base_graph = chain; - if (chain) + if (chain) { + if (unsigned_add_overflows(chain->num_commits, + chain->num_commits_in_base)) { + warning(_("commit count in base graph too high: %"PRIuMAX), + (uintmax_t)chain->num_commits_in_base); + return 0; + } g->num_commits_in_base = chain->num_commits + chain->num_commits_in_base; + } return 1; } @@ -745,7 +752,7 @@ static void load_oid_from_graph(struct commit_graph *g, lex_index = pos - g->num_commits_in_base; - oidread(oid, g->chunk_oid_lookup + g->hash_len * lex_index); + oidread(oid, g->chunk_oid_lookup + st_mult(g->hash_len, lex_index)); } static struct commit_list **insert_parent_or_die(struct repository *r, @@ -781,7 +788,7 @@ static void fill_commit_graph_info(struct commit *item, struct commit_graph *g, die(_("invalid commit position. commit-graph is likely corrupt")); lex_index = pos - g->num_commits_in_base; - commit_data = g->chunk_commit_data + GRAPH_DATA_WIDTH * lex_index; + commit_data = g->chunk_commit_data + st_mult(GRAPH_DATA_WIDTH, lex_index); graph_data = commit_graph_data_at(item); graph_data->graph_pos = pos; @@ -791,14 +798,14 @@ static void fill_commit_graph_info(struct commit *item, struct commit_graph *g, item->date = (timestamp_t)((date_high << 32) | date_low); if (g->read_generation_data) { - offset = (timestamp_t)get_be32(g->chunk_generation_data + sizeof(uint32_t) * lex_index); + offset = (timestamp_t)get_be32(g->chunk_generation_data + st_mult(sizeof(uint32_t), lex_index)); if (offset & CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW) { if (!g->chunk_generation_data_overflow) die(_("commit-graph requires overflow generation data but has none")); offset_pos = offset ^ CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW; - graph_data->generation = item->date + get_be64(g->chunk_generation_data_overflow + 8 * offset_pos); + graph_data->generation = item->date + get_be64(g->chunk_generation_data_overflow + st_mult(8, offset_pos)); } else graph_data->generation = item->date + offset; } else @@ -829,7 +836,7 @@ static int fill_commit_in_graph(struct repository *r, fill_commit_graph_info(item, g, pos); lex_index = pos - g->num_commits_in_base; - commit_data = g->chunk_commit_data + (g->hash_len + 16) * lex_index; + commit_data = g->chunk_commit_data + st_mult(g->hash_len + 16, lex_index); item->object.parsed = 1; @@ -851,7 +858,7 @@ static int fill_commit_in_graph(struct repository *r, } parent_data_ptr = (uint32_t*)(g->chunk_extra_edges + - 4 * (uint64_t)(edge_value & GRAPH_EDGE_LAST_MASK)); + st_mult(4, edge_value & GRAPH_EDGE_LAST_MASK)); do { edge_value = get_be32(parent_data_ptr); pptr = insert_parent_or_die(r, g, @@ -971,7 +978,7 @@ static struct tree *load_tree_for_commit(struct repository *r, g = g->base_graph; commit_data = g->chunk_commit_data + - GRAPH_DATA_WIDTH * (graph_pos - g->num_commits_in_base); + st_mult(GRAPH_DATA_WIDTH, graph_pos - g->num_commits_in_base); oidread(&oid, commit_data); set_commit_tree(c, lookup_tree(r, &oid)); @@ -1951,35 +1958,35 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx) add_chunk(cf, GRAPH_CHUNKID_OIDFANOUT, GRAPH_FANOUT_SIZE, write_graph_chunk_fanout); - add_chunk(cf, GRAPH_CHUNKID_OIDLOOKUP, hashsz * ctx->commits.nr, + add_chunk(cf, GRAPH_CHUNKID_OIDLOOKUP, st_mult(hashsz, ctx->commits.nr), write_graph_chunk_oids); - add_chunk(cf, GRAPH_CHUNKID_DATA, (hashsz + 16) * ctx->commits.nr, + add_chunk(cf, GRAPH_CHUNKID_DATA, st_mult(hashsz + 16, ctx->commits.nr), write_graph_chunk_data); if (ctx->write_generation_data) add_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA, - sizeof(uint32_t) * ctx->commits.nr, + st_mult(sizeof(uint32_t), ctx->commits.nr), write_graph_chunk_generation_data); if (ctx->num_generation_data_overflows) add_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW, - sizeof(timestamp_t) * ctx->num_generation_data_overflows, + st_mult(sizeof(timestamp_t), ctx->num_generation_data_overflows), write_graph_chunk_generation_data_overflow); if (ctx->num_extra_edges) add_chunk(cf, GRAPH_CHUNKID_EXTRAEDGES, - 4 * ctx->num_extra_edges, + st_mult(4, ctx->num_extra_edges), write_graph_chunk_extra_edges); if (ctx->changed_paths) { add_chunk(cf, GRAPH_CHUNKID_BLOOMINDEXES, - sizeof(uint32_t) * ctx->commits.nr, + st_mult(sizeof(uint32_t), ctx->commits.nr), write_graph_chunk_bloom_indexes); add_chunk(cf, GRAPH_CHUNKID_BLOOMDATA, - sizeof(uint32_t) * 3 - + ctx->total_bloom_filter_data_size, + st_add(sizeof(uint32_t) * 3, + ctx->total_bloom_filter_data_size), write_graph_chunk_bloom_data); } if (ctx->num_commit_graphs_after > 1) add_chunk(cf, GRAPH_CHUNKID_BASE, - hashsz * (ctx->num_commit_graphs_after - 1), + st_mult(hashsz, ctx->num_commit_graphs_after - 1), write_graph_chunk_base); hashwrite_be32(f, GRAPH_SIGNATURE); @@ -1997,7 +2004,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx) get_num_chunks(cf)); ctx->progress = start_delayed_progress( progress_title.buf, - get_num_chunks(cf) * ctx->commits.nr); + st_mult(get_num_chunks(cf), ctx->commits.nr)); } write_chunkfile(cf, ctx); @@ -2103,11 +2110,16 @@ static void split_graph_merge_strategy(struct write_commit_graph_context *ctx) if (flags != COMMIT_GRAPH_SPLIT_MERGE_PROHIBITED && flags != COMMIT_GRAPH_SPLIT_REPLACE) { - while (g && (g->num_commits <= size_mult * num_commits || + while (g && (g->num_commits <= st_mult(size_mult, num_commits) || (max_commits && num_commits > max_commits))) { if (g->odb != ctx->odb) break; + if (unsigned_add_overflows(num_commits, g->num_commits)) + die(_("cannot merge graphs with %"PRIuMAX", " + "%"PRIuMAX" commits"), + (uintmax_t)num_commits, + (uintmax_t)g->num_commits); num_commits += g->num_commits; g = g->base_graph; @@ -2165,6 +2177,11 @@ static void merge_commit_graph(struct write_commit_graph_context *ctx, uint32_t i; uint32_t offset = g->num_commits_in_base; + if (unsigned_add_overflows(ctx->commits.nr, g->num_commits)) + die(_("cannot merge graph %s, too many commits: %"PRIuMAX), + oid_to_hex(&g->oid), + (uintmax_t)st_add(ctx->commits.nr, g->num_commits)); + ALLOC_GROW(ctx->commits.list, ctx->commits.nr + g->num_commits, ctx->commits.alloc); for (i = 0; i < g->num_commits; i++) { @@ -2435,7 +2452,7 @@ int write_commit_graph(struct object_directory *odb, struct commit_graph *g = ctx->r->objects->commit_graph; for (i = 0; i < g->num_commits; i++) { struct object_id oid; - oidread(&oid, g->chunk_oid_lookup + g->hash_len * i); + oidread(&oid, g->chunk_oid_lookup + st_mult(g->hash_len, i)); oid_array_append(&ctx->oids, &oid); } } @@ -2562,7 +2579,7 @@ static int verify_one_commit_graph(struct repository *r, for (i = 0; i < g->num_commits; i++) { struct commit *graph_commit; - oidread(&cur_oid, g->chunk_oid_lookup + g->hash_len * i); + oidread(&cur_oid, g->chunk_oid_lookup + st_mult(g->hash_len, i)); if (i && oidcmp(&prev_oid, &cur_oid) >= 0) graph_report(_("commit-graph has incorrect OID order: %s then %s"), @@ -2606,7 +2623,7 @@ static int verify_one_commit_graph(struct repository *r, timestamp_t generation; display_progress(progress, ++(*seen)); - oidread(&cur_oid, g->chunk_oid_lookup + g->hash_len * i); + oidread(&cur_oid, g->chunk_oid_lookup + st_mult(g->hash_len, i)); graph_commit = lookup_commit(r, &cur_oid); odb_commit = (struct commit *)create_object(r, &cur_oid, alloc_commit_node(r)); @@ -516,7 +516,7 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b * The clone is shallow if nr_parent < 0, and we must * not traverse its real parents even when we unhide them. */ - if (graft && (graft->nr_parent < 0 || grafts_replace_parents)) + if (graft && (graft->nr_parent < 0 || !grafts_keep_true_parents)) continue; new_parent = lookup_commit(r, &parent); if (!new_parent) diff --git a/compat/win32/trace2_win32_process_info.c b/compat/win32/trace2_win32_process_info.c index a2b1506f9c..3ef0936f6f 100644 --- a/compat/win32/trace2_win32_process_info.c +++ b/compat/win32/trace2_win32_process_info.c @@ -3,8 +3,8 @@ #include "../../repository.h" #include "../../trace2.h" #include "lazyload.h" -#include <Psapi.h> -#include <tlHelp32.h> +#include <psapi.h> +#include <tlhelp32.h> /* * An arbitrarily chosen value to limit the size of the ancestor diff --git a/configure.ac b/configure.ac index 38ff86678a..276593cd9d 100644 --- a/configure.ac +++ b/configure.ac @@ -546,6 +546,8 @@ fi # git-http-push are not built, and you cannot use http:// and https:// # transports. +if test -z "$NO_CURL"; then + GIT_STASH_FLAGS($CURLDIR) AC_CHECK_LIB([curl], [curl_global_init], @@ -554,6 +556,8 @@ AC_CHECK_LIB([curl], [curl_global_init], GIT_UNSTASH_FLAGS($CURLDIR) +fi + GIT_CONF_SUBST([NO_CURL]) if test -z "$NO_CURL"; then @@ -581,6 +585,8 @@ fi # Define NO_EXPAT if you do not have expat installed. git-http-push is # not built, and you cannot push using http:// and https:// transports. +if test -z "$NO_EXPAT"; then + GIT_STASH_FLAGS($EXPATDIR) AC_CHECK_LIB([expat], [XML_ParserCreate], @@ -589,6 +595,8 @@ AC_CHECK_LIB([expat], [XML_ParserCreate], GIT_UNSTASH_FLAGS($EXPATDIR) +fi + GIT_CONF_SUBST([NO_EXPAT]) # @@ -636,7 +644,6 @@ LIBS="$old_LIBS" GIT_UNSTASH_FLAGS($ICONVDIR) GIT_CONF_SUBST([NEEDS_LIBICONV]) -GIT_CONF_SUBST([NO_ICONV]) if test -n "$NO_ICONV"; then NEEDS_LIBICONV= @@ -644,6 +651,8 @@ fi fi +GIT_CONF_SUBST([NO_ICONV]) + # # Define NO_DEFLATE_BOUND if deflateBound is missing from zlib. diff --git a/environment.c b/environment.c index a0d1d070d1..f98d76f080 100644 --- a/environment.c +++ b/environment.c @@ -73,7 +73,7 @@ enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED; #endif enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE; char *notes_ref_name; -int grafts_replace_parents = 1; +int grafts_keep_true_parents; int core_apply_sparse_checkout; int core_sparse_checkout_cone; int sparse_expect_files_outside_of_patterns; diff --git a/environment.h b/environment.h index 611aa0ffed..c5377473c6 100644 --- a/environment.h +++ b/environment.h @@ -193,7 +193,7 @@ extern enum object_creation_mode object_creation_mode; extern char *notes_ref_name; -extern int grafts_replace_parents; +extern int grafts_keep_true_parents; extern int repository_format_precious_objects; @@ -1309,9 +1309,9 @@ int fsck_buffer(const struct object_id *oid, enum object_type type, int fsck_error_function(struct fsck_options *o, const struct object_id *oid, - enum object_type object_type, + enum object_type object_type UNUSED, enum fsck_msg_type msg_type, - enum fsck_msg_id msg_id, + enum fsck_msg_id msg_id UNUSED, const char *message) { if (msg_type == FSCK_WARN) { @@ -63,7 +63,7 @@ static int get_hash_hex_algop(const char *hex, unsigned char *hash, return 0; } -int get_sha1_hex(const char *hex, unsigned char *sha1) +int get_hash_hex(const char *hex, unsigned char *sha1) { return get_hash_hex_algop(hex, sha1, the_hash_algo); } @@ -20,14 +20,16 @@ static inline int hex2chr(const char *s) } /* - * Try to read a SHA1 in hexadecimal format from the 40 characters - * starting at hex. Write the 20-byte result to sha1 in binary form. + * Try to read a hash (specified by the_hash_algo) in hexadecimal + * format from the 40 (or whatever length the hash algorithm uses) + * characters starting at hex. Write the 20-byte (or the length of + * the hash) result to hash in binary form. * Return 0 on success. Reading stops if a NUL is encountered in the * input, so it is safe to pass this function an arbitrary * null-terminated string. */ -int get_sha1_hex(const char *hex, unsigned char *sha1); -int get_oid_hex(const char *hex, struct object_id *sha1); +int get_hash_hex(const char *hex, unsigned char *hash); +int get_oid_hex(const char *hex, struct object_id *oid); /* Like get_oid_hex, but for an arbitrary hash algorithm. */ int get_oid_hex_algop(const char *hex, struct object_id *oid, const struct git_hash_algo *algop); diff --git a/http-backend.c b/http-backend.c index e24399ed10..ff07b87e64 100644 --- a/http-backend.c +++ b/http-backend.c @@ -558,7 +558,7 @@ static void get_info_refs(struct strbuf *hdr, char *arg UNUSED) } else { select_getanyfile(hdr); - for_each_namespaced_ref(show_text_ref, &buf); + for_each_namespaced_ref(NULL, show_text_ref, &buf); send_strbuf(hdr, "text/plain", &buf); } strbuf_release(&buf); diff --git a/http-push.c b/http-push.c index 9ab2383d2b..a704f490fd 100644 --- a/http-push.c +++ b/http-push.c @@ -783,7 +783,7 @@ static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed) static void one_remote_ref(const char *refname); static void -xml_start_tag(void *userData, const char *name, const char **atts) +xml_start_tag(void *userData, const char *name, const char **atts UNUSED) { struct xml_ctx *ctx = (struct xml_ctx *)userData; const char *c = strchr(name, ':'); @@ -196,7 +196,7 @@ static inline int is_hdr_continuation(const char *ptr, const size_t size) return size && (*ptr == ' ' || *ptr == '\t'); } -static size_t fwrite_wwwauth(char *ptr, size_t eltsize, size_t nmemb, void *p) +static size_t fwrite_wwwauth(char *ptr, size_t eltsize, size_t nmemb, void *p UNUSED) { size_t size = eltsize * nmemb; struct strvec *values = &http_auth.wwwauth_headers; @@ -295,7 +295,8 @@ exit: return size; } -size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf) +size_t fwrite_null(char *ptr UNUSED, size_t eltsize UNUSED, size_t nmemb, + void *data UNUSED) { return nmemb; } @@ -821,7 +822,9 @@ static void curl_dump_info(char *data, size_t size) strbuf_release(&buf); } -static int curl_trace(CURL *handle, curl_infotype type, char *data, size_t size, void *userp) +static int curl_trace(CURL *handle UNUSED, curl_infotype type, + char *data, size_t size, + void *userp UNUSED) { const char *text; enum { NO_FILTER = 0, DO_FILTER = 1 }; diff --git a/list-objects.c b/list-objects.c index 672a4cd529..e60a6cd5b4 100644 --- a/list-objects.c +++ b/list-objects.c @@ -102,7 +102,7 @@ static void process_tree_contents(struct traversal_context *ctx, while (tree_entry(&desc, &entry)) { if (match != all_entries_interesting) { match = tree_entry_interesting(ctx->revs->repo->index, - &entry, base, 0, + &entry, base, &ctx->revs->diffopt.pathspec); if (match == all_entries_not_interesting) break; @@ -72,7 +72,7 @@ struct ls_refs_data { unsigned symrefs; struct strvec prefixes; struct strbuf buf; - struct string_list hidden_refs; + struct strvec hidden_refs; unsigned unborn : 1; }; @@ -156,7 +156,7 @@ int ls_refs(struct repository *r, struct packet_reader *request) memset(&data, 0, sizeof(data)); strvec_init(&data.prefixes); strbuf_init(&data.buf, 0); - string_list_init_dup(&data.hidden_refs); + strvec_init(&data.hidden_refs); git_config(ls_refs_config, &data); @@ -194,11 +194,12 @@ int ls_refs(struct repository *r, struct packet_reader *request) strvec_push(&data.prefixes, ""); refs_for_each_fullref_in_prefixes(get_main_ref_store(r), get_git_namespace(), data.prefixes.v, + hidden_refs_to_excludes(&data.hidden_refs), send_ref, &data); packet_fflush(stdout); strvec_clear(&data.prefixes); strbuf_release(&data.buf); - string_list_clear(&data.hidden_refs, 0); + strvec_clear(&data.hidden_refs); return 0; } @@ -253,7 +253,7 @@ struct object_id *nth_midxed_object_oid(struct object_id *oid, if (n >= m->num_objects) return NULL; - oidread(oid, m->chunk_oid_lookup + m->hash_len * n); + oidread(oid, m->chunk_oid_lookup + st_mult(m->hash_len, n)); return oid; } @@ -270,7 +270,8 @@ off_t nth_midxed_offset(struct multi_pack_index *m, uint32_t pos) die(_("multi-pack-index stores a 64-bit offset, but off_t is too small")); offset32 ^= MIDX_LARGE_OFFSET_NEEDED; - return get_be64(m->chunk_large_offsets + sizeof(uint64_t) * offset32); + return get_be64(m->chunk_large_offsets + + st_mult(sizeof(uint64_t), offset32)); } return offset32; @@ -444,14 +445,14 @@ static int idx_or_pack_name_cmp(const void *_va, const void *_vb) struct write_midx_context { struct pack_info *info; - uint32_t nr; - uint32_t alloc; + size_t nr; + size_t alloc; struct multi_pack_index *m; struct progress *progress; unsigned pack_paths_checked; struct pack_midx_entry *entries; - uint32_t entries_nr; + size_t entries_nr; uint32_t *pack_perm; uint32_t *pack_order; @@ -583,12 +584,14 @@ static void fill_pack_entry(uint32_t pack_int_id, struct midx_fanout { struct pack_midx_entry *entries; - uint32_t nr; - uint32_t alloc; + size_t nr, alloc; }; -static void midx_fanout_grow(struct midx_fanout *fanout, uint32_t nr) +static void midx_fanout_grow(struct midx_fanout *fanout, size_t nr) { + if (nr < fanout->nr) + BUG("negative growth in midx_fanout_grow() (%"PRIuMAX" < %"PRIuMAX")", + (uintmax_t)nr, (uintmax_t)fanout->nr); ALLOC_GROW(fanout->entries, nr, fanout->alloc); } @@ -667,17 +670,18 @@ static void midx_fanout_add_pack_fanout(struct midx_fanout *fanout, static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m, struct pack_info *info, uint32_t nr_packs, - uint32_t *nr_objects, + size_t *nr_objects, int preferred_pack) { uint32_t cur_fanout, cur_pack, cur_object; - uint32_t alloc_objects, total_objects = 0; + size_t alloc_objects, total_objects = 0; struct midx_fanout fanout = { 0 }; struct pack_midx_entry *deduplicated_entries = NULL; uint32_t start_pack = m ? m->num_packs : 0; for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++) - total_objects += info[cur_pack].p->num_objects; + total_objects = st_add(total_objects, + info[cur_pack].p->num_objects); /* * As we de-duplicate by fanout value, we expect the fanout @@ -720,7 +724,8 @@ static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m, &fanout.entries[cur_object].oid)) continue; - ALLOC_GROW(deduplicated_entries, *nr_objects + 1, alloc_objects); + ALLOC_GROW(deduplicated_entries, st_add(*nr_objects, 1), + alloc_objects); memcpy(&deduplicated_entries[*nr_objects], &fanout.entries[cur_object], sizeof(struct pack_midx_entry)); @@ -1495,21 +1500,22 @@ static int write_midx_internal(const char *object_dir, add_chunk(cf, MIDX_CHUNKID_OIDFANOUT, MIDX_CHUNK_FANOUT_SIZE, write_midx_oid_fanout); add_chunk(cf, MIDX_CHUNKID_OIDLOOKUP, - (size_t)ctx.entries_nr * the_hash_algo->rawsz, + st_mult(ctx.entries_nr, the_hash_algo->rawsz), write_midx_oid_lookup); add_chunk(cf, MIDX_CHUNKID_OBJECTOFFSETS, - (size_t)ctx.entries_nr * MIDX_CHUNK_OFFSET_WIDTH, + st_mult(ctx.entries_nr, MIDX_CHUNK_OFFSET_WIDTH), write_midx_object_offsets); if (ctx.large_offsets_needed) add_chunk(cf, MIDX_CHUNKID_LARGEOFFSETS, - (size_t)ctx.num_large_offsets * MIDX_CHUNK_LARGE_OFFSET_WIDTH, + st_mult(ctx.num_large_offsets, + MIDX_CHUNK_LARGE_OFFSET_WIDTH), write_midx_large_offsets); if (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP)) { ctx.pack_order = midx_pack_order(&ctx); add_chunk(cf, MIDX_CHUNKID_REVINDEX, - ctx.entries_nr * sizeof(uint32_t), + st_mult(ctx.entries_nr, sizeof(uint32_t)), write_midx_revindex); } @@ -1987,8 +1993,8 @@ static int fill_included_packs_batch(struct repository *r, if (open_pack_index(p) || !p->num_objects) continue; - expected_size = (size_t)(p->pack_size - * pack_info[i].referenced_objects); + expected_size = st_mult(p->pack_size, + pack_info[i].referenced_objects); expected_size /= p->num_objects; if (expected_size >= batch_size) diff --git a/object-file.c b/object-file.c index 5ebe1b00c5..7dc0c4bfbb 100644 --- a/object-file.c +++ b/object-file.c @@ -2306,11 +2306,11 @@ int repo_has_object_file(struct repository *r, * report the minimal fsck error here, and rely on the caller to * give more context. */ -static int hash_format_check_report(struct fsck_options *opts, - const struct object_id *oid, - enum object_type object_type, - enum fsck_msg_type msg_type, - enum fsck_msg_id msg_id, +static int hash_format_check_report(struct fsck_options *opts UNUSED, + const struct object_id *oid UNUSED, + enum object_type object_type UNUSED, + enum fsck_msg_type msg_type UNUSED, + enum fsck_msg_id msg_id UNUSED, const char *message) { error(_("object fails fsck: %s"), message); diff --git a/object-store-ll.h b/object-store-ll.h index e8f22cdb1b..26a3895c82 100644 --- a/object-store-ll.h +++ b/object-store-ll.h @@ -106,7 +106,7 @@ struct packed_git { const void *index_data; size_t index_size; uint32_t num_objects; - uint32_t crc_offset; + size_t crc_offset; struct oidset bad_objects; int index_version; time_t mtime; diff --git a/pack-bitmap.c b/pack-bitmap.c index 01fbc0a657..6afc03d1e4 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c @@ -1293,7 +1293,7 @@ static void show_extended_objects(struct bitmap_index *bitmap_git, for (i = 0; i < eindex->count; ++i) { struct object *obj; - if (!bitmap_get(objects, bitmap_num_objects(bitmap_git) + i)) + if (!bitmap_get(objects, st_add(bitmap_num_objects(bitmap_git), i))) continue; obj = eindex->objects[i]; @@ -1472,7 +1472,7 @@ static void filter_bitmap_exclude_type(struct bitmap_index *bitmap_git, * them individually. */ for (i = 0; i < eindex->count; i++) { - uint32_t pos = i + bitmap_num_objects(bitmap_git); + size_t pos = st_add(i, bitmap_num_objects(bitmap_git)); if (eindex->objects[i]->type == type && bitmap_get(to_filter, pos) && !bitmap_get(tips, pos)) @@ -1563,7 +1563,7 @@ static void filter_bitmap_blob_limit(struct bitmap_index *bitmap_git, } for (i = 0; i < eindex->count; i++) { - uint32_t pos = i + bitmap_num_objects(bitmap_git); + size_t pos = st_add(i, bitmap_num_objects(bitmap_git)); if (eindex->objects[i]->type == OBJ_BLOB && bitmap_get(to_filter, pos) && !bitmap_get(tips, pos) && @@ -2037,7 +2037,8 @@ static uint32_t count_object_type(struct bitmap_index *bitmap_git, for (i = 0; i < eindex->count; ++i) { if (eindex->objects[i]->type == type && - bitmap_get(objects, bitmap_num_objects(bitmap_git) + i)) + bitmap_get(objects, + st_add(bitmap_num_objects(bitmap_git), i))) count++; } @@ -2451,7 +2452,8 @@ static off_t get_disk_usage_for_extended(struct bitmap_index *bitmap_git) for (i = 0; i < eindex->count; i++) { struct object *obj = eindex->objects[i]; - if (!bitmap_get(result, bitmap_num_objects(bitmap_git) + i)) + if (!bitmap_get(result, + st_add(bitmap_num_objects(bitmap_git), i))) continue; if (oid_object_info_extended(the_repository, &obj->oid, &oi, 0) < 0) diff --git a/packfile.c b/packfile.c index 030b7ec7a8..9cc0a2e37a 100644 --- a/packfile.c +++ b/packfile.c @@ -184,7 +184,7 @@ int load_idx(const char *path, const unsigned int hashsz, void *idx_map, */ (sizeof(off_t) <= 4)) return error("pack too large for current definition of off_t in %s", path); - p->crc_offset = 8 + 4 * 256 + nr * hashsz; + p->crc_offset = st_add(8 + 4 * 256, st_mult(nr, hashsz)); } p->index_version = version; @@ -751,7 +751,7 @@ struct packed_git *add_packed_git(const char *path, size_t path_len, int local) p->pack_local = local; p->mtime = st.st_mtime; if (path_len < the_hash_algo->hexsz || - get_sha1_hex(path + path_len - the_hash_algo->hexsz, p->hash)) + get_hash_hex(path + path_len - the_hash_algo->hexsz, p->hash)) hashclr(p->hash); return p; } @@ -1918,10 +1918,10 @@ int nth_packed_object_id(struct object_id *oid, return -1; index += 4 * 256; if (p->index_version == 1) { - oidread(oid, index + (hashsz + 4) * n + 4); + oidread(oid, index + st_add(st_mult(hashsz + 4, n), 4)); } else { index += 8; - oidread(oid, index + hashsz * n); + oidread(oid, index + st_mult(hashsz, n)); } return 0; } @@ -1946,14 +1946,15 @@ off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n) const unsigned int hashsz = the_hash_algo->rawsz; index += 4 * 256; if (p->index_version == 1) { - return ntohl(*((uint32_t *)(index + (hashsz + 4) * (size_t)n))); + return ntohl(*((uint32_t *)(index + st_mult(hashsz + 4, n)))); } else { uint32_t off; - index += 8 + (size_t)p->num_objects * (hashsz + 4); - off = ntohl(*((uint32_t *)(index + 4 * n))); + index += st_add(8, st_mult(p->num_objects, hashsz + 4)); + off = ntohl(*((uint32_t *)(index + st_mult(4, n)))); if (!(off & 0x80000000)) return off; - index += (size_t)p->num_objects * 4 + (off & 0x7fffffff) * 8; + index += st_add(st_mult(p->num_objects, 4), + st_mult(off & 0x7fffffff, 8)); check_pack_index_ptr(p, index); return get_be64(index); } diff --git a/parse-options.c b/parse-options.c index f8a155ee13..87c9fae634 100644 --- a/parse-options.c +++ b/parse-options.c @@ -1109,6 +1109,7 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t for (; opts->type != OPTION_END; opts++) { size_t pos; int pad; + const char *cp, *np; if (opts->type == OPTION_SUBCOMMAND) continue; @@ -1145,7 +1146,9 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t !(opts->flags & PARSE_OPT_NOARG)) pos += usage_argh(opts, outfile); - if (pos <= USAGE_OPTS_WIDTH) + if (pos == USAGE_OPTS_WIDTH + 1) + pad = -1; + else if (pos <= USAGE_OPTS_WIDTH) pad = USAGE_OPTS_WIDTH - pos; else { fputc('\n', outfile); @@ -1157,7 +1160,16 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t (const char *)opts->value); continue; } - fprintf(outfile, "%*s%s\n", pad + USAGE_GAP, "", _(opts->help)); + + for (cp = _(opts->help); *cp; cp = np) { + np = strchrnul(cp, '\n'); + fprintf(outfile, + "%*s%.*s\n", pad + USAGE_GAP, "", + (int)(np - cp), cp); + if (*np) + np++; + pad = USAGE_OPTS_WIDTH; + } } fputc('\n', outfile); diff --git a/parse-options.h b/parse-options.h index 8e48efe524..57a7fe9d91 100644 --- a/parse-options.h +++ b/parse-options.h @@ -581,4 +581,10 @@ int parse_opt_tracking_mode(const struct option *, const char *, int); #define OPT_PATHSPEC_FILE_NUL(v) OPT_BOOL(0, "pathspec-file-nul", v, N_("with --pathspec-from-file, pathspec elements are separated with NUL character")) #define OPT_AUTOSTASH(v) OPT_BOOL(0, "autostash", v, N_("automatically stash/stash pop before and after")) +#define OPT_IPVERSION(v) \ + OPT_SET_INT_F('4', "ipv4", (v), N_("use IPv4 addresses only"), \ + TRANSPORT_FAMILY_IPV4, PARSE_OPT_NONEG), \ + OPT_SET_INT_F('6', "ipv6", (v), N_("use IPv6 addresses only"), \ + TRANSPORT_FAMILY_IPV6, PARSE_OPT_NONEG) + #endif diff --git a/ref-filter.c b/ref-filter.c index 60919f375f..1bfaf20fbf 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1,9 +1,11 @@ #include "git-compat-util.h" #include "environment.h" #include "gettext.h" +#include "config.h" #include "gpg-interface.h" #include "hex.h" #include "parse-options.h" +#include "run-command.h" #include "refs.h" #include "wildmatch.h" #include "object-name.h" @@ -145,6 +147,7 @@ enum atom_type { ATOM_TAGGERDATE, ATOM_CREATOR, ATOM_CREATORDATE, + ATOM_DESCRIBE, ATOM_SUBJECT, ATOM_BODY, ATOM_TRAILERS, @@ -219,6 +222,7 @@ static struct used_atom { enum { S_BARE, S_GRADE, S_SIGNER, S_KEY, S_FINGERPRINT, S_PRI_KEY_FP, S_TRUST_LEVEL } option; } signature; + const char **describe_args; struct refname_atom refname; char *head; } u; @@ -255,6 +259,110 @@ static int err_bad_arg(struct strbuf *sb, const char *name, const char *arg) return -1; } +/* + * Parse option of name "candidate" in the option string "to_parse" of + * the form + * + * "candidate1[=val1],candidate2[=val2],candidate3[=val3],..." + * + * The remaining part of "to_parse" is stored in "end" (if we are + * parsing the last candidate, then this is NULL) and the value of + * the candidate is stored in "valuestart" and its length in "valuelen", + * that is the portion after "=". Since it is possible for a "candidate" + * to not have a value, in such cases, "valuestart" is set to point to + * NULL and "valuelen" to 0. + * + * The function returns 1 on success. It returns 0 if we don't find + * "candidate" in "to_parse" or we find "candidate" but it is followed + * by more chars (for example, "candidatefoo"), that is, we don't find + * an exact match. + * + * This function only does the above for one "candidate" at a time. So + * it has to be called each time trying to parse a "candidate" in the + * option string "to_parse". + */ +static int match_atom_arg_value(const char *to_parse, const char *candidate, + const char **end, const char **valuestart, + size_t *valuelen) +{ + const char *atom; + + if (!skip_prefix(to_parse, candidate, &atom)) + return 0; /* definitely not "candidate" */ + + if (*atom == '=') { + /* we just saw "candidate=" */ + *valuestart = atom + 1; + atom = strchrnul(*valuestart, ','); + *valuelen = atom - *valuestart; + } else if (*atom != ',' && *atom != '\0') { + /* key begins with "candidate" but has more chars */ + return 0; + } else { + /* just "candidate" without "=val" */ + *valuestart = NULL; + *valuelen = 0; + } + + /* atom points at either the ',' or NUL after this key[=val] */ + if (*atom == ',') + atom++; + else if (*atom) + BUG("Why is *atom not NULL yet?"); + + *end = atom; + return 1; +} + +/* + * Parse boolean option of name "candidate" in the option list "to_parse" + * of the form + * + * "candidate1[=bool1],candidate2[=bool2],candidate3[=bool3],..." + * + * The remaining part of "to_parse" is stored in "end" (if we are parsing + * the last candidate, then this is NULL) and the value (if given) is + * parsed and stored in "val", so "val" always points to either 0 or 1. + * If the value is not given, then "val" is set to point to 1. + * + * The boolean value is parsed using "git_parse_maybe_bool()", so the + * accepted values are + * + * to set true - "1", "yes", "true" + * to set false - "0", "no", "false" + * + * This function returns 1 on success. It returns 0 when we don't find + * an exact match for "candidate" or when the boolean value given is + * not valid. + */ +static int match_atom_bool_arg(const char *to_parse, const char *candidate, + const char **end, int *val) +{ + const char *argval; + char *strval; + size_t arglen; + int v; + + if (!match_atom_arg_value(to_parse, candidate, end, &argval, &arglen)) + return 0; + + if (!argval) { + *val = 1; + return 1; + } + + strval = xstrndup(argval, arglen); + v = git_parse_maybe_bool(strval); + free(strval); + + if (v == -1) + return 0; + + *val = v; + + return 1; +} + static int color_atom_parser(struct ref_format *format, struct used_atom *atom, const char *color_value, struct strbuf *err) { @@ -495,6 +603,87 @@ static int contents_atom_parser(struct ref_format *format, struct used_atom *ato return 0; } +static int describe_atom_option_parser(struct strvec *args, const char **arg, + struct strbuf *err) +{ + const char *argval; + size_t arglen = 0; + int optval = 0; + + if (match_atom_bool_arg(*arg, "tags", arg, &optval)) { + if (!optval) + strvec_push(args, "--no-tags"); + else + strvec_push(args, "--tags"); + return 1; + } + + if (match_atom_arg_value(*arg, "abbrev", arg, &argval, &arglen)) { + char *endptr; + + if (!arglen) + return strbuf_addf_ret(err, -1, + _("argument expected for %s"), + "describe:abbrev"); + if (strtol(argval, &endptr, 10) < 0) + return strbuf_addf_ret(err, -1, + _("positive value expected %s=%s"), + "describe:abbrev", argval); + if (endptr - argval != arglen) + return strbuf_addf_ret(err, -1, + _("cannot fully parse %s=%s"), + "describe:abbrev", argval); + + strvec_pushf(args, "--abbrev=%.*s", (int)arglen, argval); + return 1; + } + + if (match_atom_arg_value(*arg, "match", arg, &argval, &arglen)) { + if (!arglen) + return strbuf_addf_ret(err, -1, + _("value expected %s="), + "describe:match"); + + strvec_pushf(args, "--match=%.*s", (int)arglen, argval); + return 1; + } + + if (match_atom_arg_value(*arg, "exclude", arg, &argval, &arglen)) { + if (!arglen) + return strbuf_addf_ret(err, -1, + _("value expected %s="), + "describe:exclude"); + + strvec_pushf(args, "--exclude=%.*s", (int)arglen, argval); + return 1; + } + + return 0; +} + +static int describe_atom_parser(struct ref_format *format UNUSED, + struct used_atom *atom, + const char *arg, struct strbuf *err) +{ + struct strvec args = STRVEC_INIT; + + for (;;) { + int found = 0; + const char *bad_arg = arg; + + if (!arg || !*arg) + break; + + found = describe_atom_option_parser(&args, &arg, err); + if (found < 0) + return found; + if (!found) + return err_bad_arg(err, "describe", bad_arg); + } + atom->u.describe_args = strvec_detach(&args); + return 0; +} + static int raw_atom_parser(struct ref_format *format UNUSED, struct used_atom *atom, const char *arg, struct strbuf *err) @@ -697,6 +886,7 @@ static struct { [ATOM_TAGGERDATE] = { "taggerdate", SOURCE_OBJ, FIELD_TIME }, [ATOM_CREATOR] = { "creator", SOURCE_OBJ }, [ATOM_CREATORDATE] = { "creatordate", SOURCE_OBJ, FIELD_TIME }, + [ATOM_DESCRIBE] = { "describe", SOURCE_OBJ, FIELD_STR, describe_atom_parser }, [ATOM_SUBJECT] = { "subject", SOURCE_OBJ, FIELD_STR, subject_atom_parser }, [ATOM_BODY] = { "body", SOURCE_OBJ, FIELD_STR, body_atom_parser }, [ATOM_TRAILERS] = { "trailers", SOURCE_OBJ, FIELD_STR, trailers_atom_parser }, @@ -1603,6 +1793,44 @@ static void append_lines(struct strbuf *out, const char *buf, unsigned long size } } +static void grab_describe_values(struct atom_value *val, int deref, + struct object *obj) +{ + struct commit *commit = (struct commit *)obj; + int i; + + for (i = 0; i < used_atom_cnt; i++) { + struct used_atom *atom = &used_atom[i]; + enum atom_type type = atom->atom_type; + const char *name = atom->name; + struct atom_value *v = &val[i]; + + struct child_process cmd = CHILD_PROCESS_INIT; + struct strbuf out = STRBUF_INIT; + struct strbuf err = STRBUF_INIT; + + if (type != ATOM_DESCRIBE) + continue; + + if (!!deref != (*name == '*')) + continue; + + cmd.git_cmd = 1; + strvec_push(&cmd.args, "describe"); + strvec_pushv(&cmd.args, atom->u.describe_args); + strvec_push(&cmd.args, oid_to_hex(&commit->object.oid)); + if (pipe_command(&cmd, NULL, 0, &out, 0, &err, 0) < 0) { + error(_("failed to run 'describe'")); + v->s = xstrdup(""); + continue; + } + strbuf_rtrim(&out); + v->s = strbuf_detach(&out, NULL); + + strbuf_release(&err); + } +} + /* See grab_values */ static void grab_sub_body_contents(struct atom_value *val, int deref, struct expand_data *data) { @@ -1712,6 +1940,7 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s grab_tag_values(val, deref, obj); grab_sub_body_contents(val, deref, data); grab_person("tagger", val, deref, buf); + grab_describe_values(val, deref, obj); break; case OBJ_COMMIT: grab_commit_values(val, deref, obj); @@ -1719,6 +1948,7 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, s grab_person("author", val, deref, buf); grab_person("committer", val, deref, buf); grab_signature(val, deref, obj); + grab_describe_values(val, deref, obj); break; case OBJ_TREE: /* grab_tree_values(val, deref, obj, buf, sz); */ @@ -2225,12 +2455,12 @@ static int get_ref_atom_value(struct ref_array_item *ref, int atom, * matches a pattern "refs/heads/mas") or a wildcard (e.g. the same ref * matches "refs/heads/mas*", too). */ -static int match_pattern(const struct ref_filter *filter, const char *refname) +static int match_pattern(const char **patterns, const char *refname, + int ignore_case) { - const char **patterns = filter->name_patterns; unsigned flags = 0; - if (filter->ignore_case) + if (ignore_case) flags |= WM_CASEFOLD; /* @@ -2255,13 +2485,13 @@ static int match_pattern(const struct ref_filter *filter, const char *refname) * matches a pattern "refs/heads/" but not "refs/heads/m") or a * wildcard (e.g. the same ref matches "refs/heads/m*", too). */ -static int match_name_as_path(const struct ref_filter *filter, const char *refname) +static int match_name_as_path(const char **pattern, const char *refname, + int ignore_case) { - const char **pattern = filter->name_patterns; int namelen = strlen(refname); unsigned flags = WM_PATHNAME; - if (filter->ignore_case) + if (ignore_case) flags |= WM_CASEFOLD; for (; *pattern; pattern++) { @@ -2286,8 +2516,20 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname) if (!*filter->name_patterns) return 1; /* No pattern always matches */ if (filter->match_as_path) - return match_name_as_path(filter, refname); - return match_pattern(filter, refname); + return match_name_as_path(filter->name_patterns, refname, + filter->ignore_case); + return match_pattern(filter->name_patterns, refname, + filter->ignore_case); +} + +static int filter_exclude_match(struct ref_filter *filter, const char *refname) +{ + if (!filter->exclude.nr) + return 0; + if (filter->match_as_path) + return match_name_as_path(filter->exclude.v, refname, + filter->ignore_case); + return match_pattern(filter->exclude.v, refname, filter->ignore_case); } /* @@ -2319,43 +2561,53 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter, if (!filter->name_patterns[0]) { /* no patterns; we have to look at everything */ - return for_each_fullref_in("", cb, cb_data); + return refs_for_each_fullref_in(get_main_ref_store(the_repository), + "", filter->exclude.v, cb, cb_data); } return refs_for_each_fullref_in_prefixes(get_main_ref_store(the_repository), NULL, filter->name_patterns, + filter->exclude.v, cb, cb_data); } /* * Given a ref (oid, refname), check if the ref belongs to the array * of oids. If the given ref is a tag, check if the given tag points - * at one of the oids in the given oid array. + * at one of the oids in the given oid array. Returns non-zero if a + * match is found. + * * NEEDSWORK: - * 1. Only a single level of indirection is obtained, we might want to - * change this to account for multiple levels (e.g. annotated tags - * pointing to annotated tags pointing to a commit.) - * 2. As the refs are cached we might know what refname peels to without + * As the refs are cached we might know what refname peels to without * the need to parse the object via parse_object(). peel_ref() might be a * more efficient alternative to obtain the pointee. */ -static const struct object_id *match_points_at(struct oid_array *points_at, - const struct object_id *oid, - const char *refname) +static int match_points_at(struct oid_array *points_at, + const struct object_id *oid, + const char *refname) { - const struct object_id *tagged_oid = NULL; struct object *obj; if (oid_array_lookup(points_at, oid) >= 0) - return oid; - obj = parse_object(the_repository, oid); + return 1; + obj = parse_object_with_flags(the_repository, oid, + PARSE_OBJECT_SKIP_HASH_CHECK); + while (obj && obj->type == OBJ_TAG) { + struct tag *tag = (struct tag *)obj; + + if (parse_tag(tag) < 0) { + obj = NULL; + break; + } + + if (oid_array_lookup(points_at, get_tagged_oid(tag)) >= 0) + return 1; + + obj = tag->tagged; + } if (!obj) die(_("malformed object at '%s'"), refname); - if (obj->type == OBJ_TAG) - tagged_oid = get_tagged_oid((struct tag *)obj); - if (tagged_oid && oid_array_lookup(points_at, tagged_oid) >= 0) - return tagged_oid; - return NULL; + return 0; } /* @@ -2457,6 +2709,9 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid, if (!filter_pattern_match(filter, refname)) return 0; + if (filter_exclude_match(filter, refname)) + return 0; + if (filter->points_at.nr && !match_points_at(&filter->points_at, oid, refname)) return 0; @@ -2539,13 +2794,13 @@ void ref_array_clear(struct ref_array *array) #define EXCLUDE_REACHED 0 #define INCLUDE_REACHED 1 static void reach_filter(struct ref_array *array, - struct commit_list *check_reachable, + struct commit_list **check_reachable, int include_reached) { int i, old_nr; struct commit **to_clear; - if (!check_reachable) + if (!*check_reachable) return; CALLOC_ARRAY(to_clear, array->nr); @@ -2555,7 +2810,7 @@ static void reach_filter(struct ref_array *array, } tips_reachable_from_bases(the_repository, - check_reachable, + *check_reachable, to_clear, array->nr, UNINTERESTING); @@ -2576,8 +2831,8 @@ static void reach_filter(struct ref_array *array, clear_commit_marks_many(old_nr, to_clear, ALL_REV_FLAGS); - while (check_reachable) { - struct commit *merge_commit = pop_commit(&check_reachable); + while (*check_reachable) { + struct commit *merge_commit = pop_commit(check_reachable); clear_commit_marks(merge_commit, ALL_REV_FLAGS); } @@ -2674,8 +2929,8 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int clear_contains_cache(&ref_cbdata.no_contains_cache); /* Filters that need revision walking */ - reach_filter(array, filter->reachable_from, INCLUDE_REACHED); - reach_filter(array, filter->unreachable_from, EXCLUDE_REACHED); + reach_filter(array, &filter->reachable_from, INCLUDE_REACHED); + reach_filter(array, &filter->unreachable_from, EXCLUDE_REACHED); save_commit_buffer = save_commit_buffer_orig; return ret; @@ -2987,3 +3242,20 @@ int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset) return 0; } + +void ref_filter_init(struct ref_filter *filter) +{ + struct ref_filter blank = REF_FILTER_INIT; + memcpy(filter, &blank, sizeof(blank)); +} + +void ref_filter_clear(struct ref_filter *filter) +{ + strvec_clear(&filter->exclude); + oid_array_clear(&filter->points_at); + free_commit_list(filter->with_commit); + free_commit_list(filter->no_commit); + free_commit_list(filter->reachable_from); + free_commit_list(filter->unreachable_from); + ref_filter_init(filter); +} diff --git a/ref-filter.h b/ref-filter.h index 430701cfb7..1524bc463a 100644 --- a/ref-filter.h +++ b/ref-filter.h @@ -6,6 +6,7 @@ #include "refs.h" #include "commit.h" #include "string-list.h" +#include "strvec.h" /* Quoting styles */ #define QUOTE_NONE 0 @@ -59,6 +60,7 @@ struct ref_array { struct ref_filter { const char **name_patterns; + struct strvec exclude; struct oid_array points_at; struct commit_list *with_commit; struct commit_list *no_commit; @@ -92,6 +94,10 @@ struct ref_format { struct string_list bases; }; +#define REF_FILTER_INIT { \ + .points_at = OID_ARRAY_INIT, \ + .exclude = STRVEC_INIT, \ +} #define REF_FORMAT_INIT { \ .use_color = -1, \ .bases = STRING_LIST_INIT_DUP, \ @@ -109,6 +115,9 @@ struct ref_format { #define OPT_REF_SORT(var) \ OPT_STRING_LIST(0, "sort", (var), \ N_("key"), N_("field name to sort on")) +#define OPT_REF_FILTER_EXCLUDE(var) \ + OPT_STRVEC(0, "exclude", &(var)->exclude, \ + N_("pattern"), N_("exclude refs which match pattern")) /* * API for filtering a set of refs. Based on the type of refs the user @@ -167,4 +176,7 @@ void filter_ahead_behind(struct repository *r, struct ref_format *format, struct ref_array *array); +void ref_filter_init(struct ref_filter *filter); +void ref_filter_clear(struct ref_filter *filter); + #endif /* REF_FILTER_H */ @@ -375,8 +375,8 @@ char *resolve_refdup(const char *refname, int resolve_flags, oid, flags); } -/* The argument to filter_refs */ -struct ref_filter { +/* The argument to for_each_filter_refs */ +struct for_each_ref_filter { const char *pattern; const char *prefix; each_ref_fn *fn; @@ -409,10 +409,11 @@ int ref_exists(const char *refname) return refs_ref_exists(get_main_ref_store(the_repository), refname); } -static int filter_refs(const char *refname, const struct object_id *oid, - int flags, void *data) +static int for_each_filter_refs(const char *refname, + const struct object_id *oid, + int flags, void *data) { - struct ref_filter *filter = (struct ref_filter *)data; + struct for_each_ref_filter *filter = data; if (wildmatch(filter->pattern, refname, 0)) return 0; @@ -569,7 +570,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, const char *prefix, void *cb_data) { struct strbuf real_pattern = STRBUF_INIT; - struct ref_filter filter; + struct for_each_ref_filter filter; int ret; if (!prefix && !starts_with(pattern, "refs/")) @@ -589,7 +590,7 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, filter.prefix = prefix; filter.fn = fn; filter.cb_data = cb_data; - ret = for_each_ref(filter_refs, &filter); + ret = for_each_ref(for_each_filter_refs, &filter); strbuf_release(&real_pattern); return ret; @@ -1426,7 +1427,7 @@ char *shorten_unambiguous_ref(const char *refname, int strict) } int parse_hide_refs_config(const char *var, const char *value, const char *section, - struct string_list *hide_refs) + struct strvec *hide_refs) { const char *key; if (!strcmp("transfer.hiderefs", var) || @@ -1437,22 +1438,23 @@ int parse_hide_refs_config(const char *var, const char *value, const char *secti if (!value) return config_error_nonbool(var); - ref = xstrdup(value); + + /* drop const to remove trailing '/' characters */ + ref = (char *)strvec_push(hide_refs, value); len = strlen(ref); while (len && ref[len - 1] == '/') ref[--len] = '\0'; - string_list_append_nodup(hide_refs, ref); } return 0; } int ref_is_hidden(const char *refname, const char *refname_full, - const struct string_list *hide_refs) + const struct strvec *hide_refs) { int i; for (i = hide_refs->nr - 1; i >= 0; i--) { - const char *match = hide_refs->items[i].string; + const char *match = hide_refs->v[i]; const char *subject; int neg = 0; const char *p; @@ -1478,6 +1480,30 @@ int ref_is_hidden(const char *refname, const char *refname_full, return 0; } +const char **hidden_refs_to_excludes(const struct strvec *hide_refs) +{ + const char **pattern; + for (pattern = hide_refs->v; *pattern; pattern++) { + /* + * We can't feed any excludes from hidden refs config + * sections, since later rules may override previous + * ones. For example, with rules "refs/foo" and + * "!refs/foo/bar", we should show "refs/foo/bar" (and + * everything underneath it), but the earlier exclusion + * would cause us to skip all of "refs/foo". We + * likewise don't implement the namespace stripping + * required for '^' rules. + * + * Both are possible to do, but complicated, so avoid + * populating the jump list at all if we see either of + * these patterns. + */ + if (**pattern == '!' || **pattern == '^') + return NULL; + } + return hide_refs->v; +} + const char *find_descendant_ref(const char *dirname, const struct string_list *extras, const struct string_list *skip) @@ -1525,7 +1551,9 @@ int head_ref(each_ref_fn fn, void *cb_data) struct ref_iterator *refs_ref_iterator_begin( struct ref_store *refs, - const char *prefix, int trim, + const char *prefix, + const char **exclude_patterns, + int trim, enum do_for_each_ref_flags flags) { struct ref_iterator *iter; @@ -1541,8 +1569,7 @@ struct ref_iterator *refs_ref_iterator_begin( } } - iter = refs->be->iterator_begin(refs, prefix, flags); - + iter = refs->be->iterator_begin(refs, prefix, exclude_patterns, flags); /* * `iterator_begin()` already takes care of prefix, but we * might need to do some trimming: @@ -1576,7 +1603,7 @@ static int do_for_each_repo_ref(struct repository *r, const char *prefix, if (!refs) return 0; - iter = refs_ref_iterator_begin(refs, prefix, trim, flags); + iter = refs_ref_iterator_begin(refs, prefix, NULL, trim, flags); return do_for_each_repo_ref_iterator(r, iter, fn, cb_data); } @@ -1586,7 +1613,7 @@ struct do_for_each_ref_help { void *cb_data; }; -static int do_for_each_ref_helper(struct repository *r, +static int do_for_each_ref_helper(struct repository *r UNUSED, const char *refname, const struct object_id *oid, int flags, @@ -1598,6 +1625,7 @@ static int do_for_each_ref_helper(struct repository *r, } static int do_for_each_ref(struct ref_store *refs, const char *prefix, + const char **exclude_patterns, each_ref_fn fn, int trim, enum do_for_each_ref_flags flags, void *cb_data) { @@ -1607,7 +1635,8 @@ static int do_for_each_ref(struct ref_store *refs, const char *prefix, if (!refs) return 0; - iter = refs_ref_iterator_begin(refs, prefix, trim, flags); + iter = refs_ref_iterator_begin(refs, prefix, exclude_patterns, trim, + flags); return do_for_each_repo_ref_iterator(the_repository, iter, do_for_each_ref_helper, &hp); @@ -1615,7 +1644,7 @@ static int do_for_each_ref(struct ref_store *refs, const char *prefix, int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data) { - return do_for_each_ref(refs, "", fn, 0, 0, cb_data); + return do_for_each_ref(refs, "", NULL, fn, 0, 0, cb_data); } int for_each_ref(each_ref_fn fn, void *cb_data) @@ -1626,7 +1655,7 @@ int for_each_ref(each_ref_fn fn, void *cb_data) int refs_for_each_ref_in(struct ref_store *refs, const char *prefix, each_ref_fn fn, void *cb_data) { - return do_for_each_ref(refs, prefix, fn, strlen(prefix), 0, cb_data); + return do_for_each_ref(refs, prefix, NULL, fn, strlen(prefix), 0, cb_data); } int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data) @@ -1637,13 +1666,14 @@ int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data) int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data) { return do_for_each_ref(get_main_ref_store(the_repository), - prefix, fn, 0, 0, cb_data); + prefix, NULL, fn, 0, 0, cb_data); } int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix, + const char **exclude_patterns, each_ref_fn fn, void *cb_data) { - return do_for_each_ref(refs, prefix, fn, 0, 0, cb_data); + return do_for_each_ref(refs, prefix, exclude_patterns, fn, 0, 0, cb_data); } int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data) @@ -1654,20 +1684,21 @@ int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_dat DO_FOR_EACH_INCLUDE_BROKEN, cb_data); } -int for_each_namespaced_ref(each_ref_fn fn, void *cb_data) +int for_each_namespaced_ref(const char **exclude_patterns, + each_ref_fn fn, void *cb_data) { struct strbuf buf = STRBUF_INIT; int ret; strbuf_addf(&buf, "%srefs/", get_git_namespace()); ret = do_for_each_ref(get_main_ref_store(the_repository), - buf.buf, fn, 0, 0, cb_data); + buf.buf, exclude_patterns, fn, 0, 0, cb_data); strbuf_release(&buf); return ret; } int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data) { - return do_for_each_ref(refs, "", fn, 0, + return do_for_each_ref(refs, "", NULL, fn, 0, DO_FOR_EACH_INCLUDE_BROKEN, cb_data); } @@ -1737,6 +1768,7 @@ static void find_longest_prefixes(struct string_list *out, int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store, const char *namespace, const char **patterns, + const char **exclude_patterns, each_ref_fn fn, void *cb_data) { struct string_list prefixes = STRING_LIST_INIT_DUP; @@ -1752,7 +1784,8 @@ int refs_for_each_fullref_in_prefixes(struct ref_store *ref_store, for_each_string_list_item(prefix, &prefixes) { strbuf_addstr(&buf, prefix->string); - ret = refs_for_each_fullref_in(ref_store, buf.buf, fn, cb_data); + ret = refs_for_each_fullref_in(ref_store, buf.buf, + exclude_patterns, fn, cb_data); if (ret) break; strbuf_setlen(&buf, namespace_len); @@ -2407,7 +2440,7 @@ int refs_verify_refname_available(struct ref_store *refs, strbuf_addstr(&dirname, refname + dirname.len); strbuf_addch(&dirname, '/'); - iter = refs_ref_iterator_begin(refs, dirname.buf, 0, + iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0, DO_FOR_EACH_INCLUDE_BROKEN); while ((ok = ref_iterator_advance(iter)) == ITER_OK) { if (skip && @@ -343,7 +343,12 @@ int for_each_ref(each_ref_fn fn, void *cb_data); */ int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data); +/* + * references matching any pattern in "exclude_patterns" are omitted from the + * result set on a best-effort basis. + */ int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix, + const char **exclude_patterns, each_ref_fn fn, void *cb_data); int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data); @@ -351,10 +356,15 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data); * iterate all refs in "patterns" by partitioning patterns into disjoint sets * and iterating the longest-common prefix of each set. * + * references matching any pattern in "exclude_patterns" are omitted from the + * result set on a best-effort basis. + * * callers should be prepared to ignore references that they did not ask for. */ int refs_for_each_fullref_in_prefixes(struct ref_store *refs, - const char *namespace, const char **patterns, + const char *namespace, + const char **patterns, + const char **exclude_patterns, each_ref_fn fn, void *cb_data); /** @@ -372,7 +382,12 @@ int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, const char *prefix, void *cb_data); int head_ref_namespaced(each_ref_fn fn, void *cb_data); -int for_each_namespaced_ref(each_ref_fn fn, void *cb_data); +/* + * references matching any pattern in "exclude_patterns" are omitted from the + * result set on a best-effort basis. + */ +int for_each_namespaced_ref(const char **exclude_patterns, + each_ref_fn fn, void *cb_data); /* can be used to learn about broken ref and symref */ int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data); @@ -810,7 +825,7 @@ int update_ref(const char *msg, const char *refname, unsigned int flags, enum action_on_err onerr); int parse_hide_refs_config(const char *var, const char *value, const char *, - struct string_list *); + struct strvec *); /* * Check whether a ref is hidden. If no namespace is set, both the first and @@ -820,7 +835,13 @@ int parse_hide_refs_config(const char *var, const char *value, const char *, * the ref is outside that namespace, the first parameter is NULL. The second * parameter always points to the full ref name. */ -int ref_is_hidden(const char *, const char *, const struct string_list *); +int ref_is_hidden(const char *, const char *, const struct strvec *); + +/* + * Returns an array of patterns to use as excluded_patterns, if none of the + * hidden references use the token '!' or '^'. + */ +const char **hidden_refs_to_excludes(const struct strvec *hide_refs); /* Is this a per-worktree ref living in the refs/ namespace? */ int is_per_worktree_ref(const char *refname); diff --git a/refs/debug.c b/refs/debug.c index c0fa707a1d..b7ffc4ce67 100644 --- a/refs/debug.c +++ b/refs/debug.c @@ -229,11 +229,12 @@ static struct ref_iterator_vtable debug_ref_iterator_vtable = { static struct ref_iterator * debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix, - unsigned int flags) + const char **exclude_patterns, unsigned int flags) { struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; struct ref_iterator *res = - drefs->refs->be->iterator_begin(drefs->refs, prefix, flags); + drefs->refs->be->iterator_begin(drefs->refs, prefix, + exclude_patterns, flags); struct debug_ref_iterator *diter = xcalloc(1, sizeof(*diter)); base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable, 1); diter->iter = res; diff --git a/refs/files-backend.c b/refs/files-backend.c index 09b4954f21..341354182b 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -832,7 +832,8 @@ static struct ref_iterator_vtable files_ref_iterator_vtable = { static struct ref_iterator *files_ref_iterator_begin( struct ref_store *ref_store, - const char *prefix, unsigned int flags) + const char *prefix, const char **exclude_patterns, + unsigned int flags) { struct files_ref_store *refs; struct ref_iterator *loose_iter, *packed_iter, *overlay_iter; @@ -877,7 +878,7 @@ static struct ref_iterator *files_ref_iterator_begin( * the packed and loose references. */ packed_iter = refs_ref_iterator_begin( - refs->packed_ref_store, prefix, 0, + refs->packed_ref_store, prefix, exclude_patterns, 0, DO_FOR_EACH_INCLUDE_BROKEN); overlay_iter = overlay_ref_iterator_begin(loose_iter, packed_iter); diff --git a/refs/packed-backend.c b/refs/packed-backend.c index 27bd6339ff..59c78d7941 100644 --- a/refs/packed-backend.c +++ b/refs/packed-backend.c @@ -13,6 +13,7 @@ #include "../statinfo.h" #include "../wrapper.h" #include "../write-or-die.h" +#include "../trace2.h" enum mmap_strategy { /* @@ -304,7 +305,8 @@ static int cmp_packed_ref_records(const void *v1, const void *v2) * Compare a snapshot record at `rec` to the specified NUL-terminated * refname. */ -static int cmp_record_to_refname(const char *rec, const char *refname) +static int cmp_record_to_refname(const char *rec, const char *refname, + int start) { const char *r1 = rec + the_hash_algo->hexsz + 1; const char *r2 = refname; @@ -313,7 +315,7 @@ static int cmp_record_to_refname(const char *rec, const char *refname) if (*r1 == '\n') return *r2 ? -1 : 0; if (!*r2) - return 1; + return start ? 1 : -1; if (*r1 != *r2) return (unsigned char)*r1 < (unsigned char)*r2 ? -1 : +1; r1++; @@ -528,22 +530,9 @@ static int load_contents(struct snapshot *snapshot) return 1; } -/* - * Find the place in `snapshot->buf` where the start of the record for - * `refname` starts. If `mustexist` is true and the reference doesn't - * exist, then return NULL. If `mustexist` is false and the reference - * doesn't exist, then return the point where that reference would be - * inserted, or `snapshot->eof` (which might be NULL) if it would be - * inserted at the end of the file. In the latter mode, `refname` - * doesn't have to be a proper reference name; for example, one could - * search for "refs/replace/" to find the start of any replace - * references. - * - * The record is sought using a binary search, so `snapshot->buf` must - * be sorted. - */ -static const char *find_reference_location(struct snapshot *snapshot, - const char *refname, int mustexist) +static const char *find_reference_location_1(struct snapshot *snapshot, + const char *refname, int mustexist, + int start) { /* * This is not *quite* a garden-variety binary search, because @@ -573,7 +562,7 @@ static const char *find_reference_location(struct snapshot *snapshot, mid = lo + (hi - lo) / 2; rec = find_start_of_record(lo, mid); - cmp = cmp_record_to_refname(rec, refname); + cmp = cmp_record_to_refname(rec, refname, start); if (cmp < 0) { lo = find_end_of_record(mid, hi); } else if (cmp > 0) { @@ -590,6 +579,41 @@ static const char *find_reference_location(struct snapshot *snapshot, } /* + * Find the place in `snapshot->buf` where the start of the record for + * `refname` starts. If `mustexist` is true and the reference doesn't + * exist, then return NULL. If `mustexist` is false and the reference + * doesn't exist, then return the point where that reference would be + * inserted, or `snapshot->eof` (which might be NULL) if it would be + * inserted at the end of the file. In the latter mode, `refname` + * doesn't have to be a proper reference name; for example, one could + * search for "refs/replace/" to find the start of any replace + * references. + * + * The record is sought using a binary search, so `snapshot->buf` must + * be sorted. + */ +static const char *find_reference_location(struct snapshot *snapshot, + const char *refname, int mustexist) +{ + return find_reference_location_1(snapshot, refname, mustexist, 1); +} + +/* + * Find the place in `snapshot->buf` after the end of the record for + * `refname`. In other words, find the location of first thing *after* + * `refname`. + * + * Other semantics are identical to the ones in + * `find_reference_location()`. + */ +static const char *find_reference_location_end(struct snapshot *snapshot, + const char *refname, + int mustexist) +{ + return find_reference_location_1(snapshot, refname, mustexist, 0); +} + +/* * Create a newly-allocated `snapshot` of the `packed-refs` file in * its current state and return it. The return value will already have * its reference count incremented. @@ -780,6 +804,13 @@ struct packed_ref_iterator { /* The end of the part of the buffer that will be iterated over: */ const char *eof; + struct jump_list_entry { + const char *start; + const char *end; + } *jump; + size_t jump_nr, jump_alloc; + size_t jump_cur; + /* Scratch space for current values: */ struct object_id oid, peeled; struct strbuf refname_buf; @@ -797,14 +828,36 @@ struct packed_ref_iterator { */ static int next_record(struct packed_ref_iterator *iter) { - const char *p = iter->pos, *eol; + const char *p, *eol; strbuf_reset(&iter->refname_buf); + /* + * If iter->pos is contained within a skipped region, jump past + * it. + * + * Note that each skipped region is considered at most once, + * since they are ordered based on their starting position. + */ + while (iter->jump_cur < iter->jump_nr) { + struct jump_list_entry *curr = &iter->jump[iter->jump_cur]; + if (iter->pos < curr->start) + break; /* not to the next jump yet */ + + iter->jump_cur++; + if (iter->pos < curr->end) { + iter->pos = curr->end; + trace2_counter_add(TRACE2_COUNTER_ID_PACKED_REFS_JUMPS, 1); + /* jumps are coalesced, so only one jump is necessary */ + break; + } + } + if (iter->pos == iter->eof) return ITER_DONE; iter->base.flags = REF_ISPACKED; + p = iter->pos; if (iter->eof - p < the_hash_algo->hexsz + 2 || parse_oid_hex(p, &iter->oid, &p) || @@ -912,6 +965,7 @@ static int packed_ref_iterator_abort(struct ref_iterator *ref_iterator) int ok = ITER_DONE; strbuf_release(&iter->refname_buf); + free(iter->jump); release_snapshot(iter->snapshot); base_ref_iterator_free(ref_iterator); return ok; @@ -923,9 +977,112 @@ static struct ref_iterator_vtable packed_ref_iterator_vtable = { .abort = packed_ref_iterator_abort }; +static int jump_list_entry_cmp(const void *va, const void *vb) +{ + const struct jump_list_entry *a = va; + const struct jump_list_entry *b = vb; + + if (a->start < b->start) + return -1; + if (a->start > b->start) + return 1; + return 0; +} + +static int has_glob_special(const char *str) +{ + const char *p; + for (p = str; *p; p++) { + if (is_glob_special(*p)) + return 1; + } + return 0; +} + +static void populate_excluded_jump_list(struct packed_ref_iterator *iter, + struct snapshot *snapshot, + const char **excluded_patterns) +{ + size_t i, j; + const char **pattern; + struct jump_list_entry *last_disjoint; + + if (!excluded_patterns) + return; + + for (pattern = excluded_patterns; *pattern; pattern++) { + struct jump_list_entry *e; + const char *start, *end; + + /* + * We can't feed any excludes with globs in them to the + * refs machinery. It only understands prefix matching. + * We likewise can't even feed the string leading up to + * the first meta-character, as something like "foo[a]" + * should not exclude "foobar" (but the prefix "foo" + * would match that and mark it for exclusion). + */ + if (has_glob_special(*pattern)) + continue; + + start = find_reference_location(snapshot, *pattern, 0); + end = find_reference_location_end(snapshot, *pattern, 0); + + if (start == end) + continue; /* nothing to jump over */ + + ALLOC_GROW(iter->jump, iter->jump_nr + 1, iter->jump_alloc); + + e = &iter->jump[iter->jump_nr++]; + e->start = start; + e->end = end; + } + + if (!iter->jump_nr) { + /* + * Every entry in exclude_patterns has a meta-character, + * nothing to do here. + */ + return; + } + + QSORT(iter->jump, iter->jump_nr, jump_list_entry_cmp); + + /* + * As an optimization, merge adjacent entries in the jump list + * to jump forwards as far as possible when entering a skipped + * region. + * + * For example, if we have two skipped regions: + * + * [[A, B], [B, C]] + * + * we want to combine that into a single entry jumping from A to + * C. + */ + last_disjoint = iter->jump; + + for (i = 1, j = 1; i < iter->jump_nr; i++) { + struct jump_list_entry *ours = &iter->jump[i]; + if (ours->start <= last_disjoint->end) { + /* overlapping regions extend the previous one */ + last_disjoint->end = last_disjoint->end > ours->end + ? last_disjoint->end : ours->end; + } else { + /* otherwise, insert a new region */ + iter->jump[j++] = *ours; + last_disjoint = ours; + } + } + + iter->jump_nr = j; + iter->jump_cur = 0; +} + static struct ref_iterator *packed_ref_iterator_begin( struct ref_store *ref_store, - const char *prefix, unsigned int flags) + const char *prefix, const char **exclude_patterns, + unsigned int flags) { struct packed_ref_store *refs; struct snapshot *snapshot; @@ -957,6 +1114,9 @@ static struct ref_iterator *packed_ref_iterator_begin( ref_iterator = &iter->base; base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable, 1); + if (exclude_patterns) + populate_excluded_jump_list(iter, snapshot, exclude_patterns); + iter->snapshot = snapshot; acquire_snapshot(snapshot); @@ -1150,7 +1310,7 @@ static int write_with_updates(struct packed_ref_store *refs, * list of refs is exhausted, set iter to NULL. When the list * of updates is exhausted, leave i set to updates->nr. */ - iter = packed_ref_iterator_begin(&refs->base, "", + iter = packed_ref_iterator_begin(&refs->base, "", NULL, DO_FOR_EACH_INCLUDE_BROKEN); if ((ok = ref_iterator_advance(iter)) != ITER_OK) iter = NULL; diff --git a/refs/refs-internal.h b/refs/refs-internal.h index f72b7be894..9db8aec4da 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -367,8 +367,8 @@ int is_empty_ref_iterator(struct ref_iterator *ref_iterator); */ struct ref_iterator *refs_ref_iterator_begin( struct ref_store *refs, - const char *prefix, int trim, - enum do_for_each_ref_flags flags); + const char *prefix, const char **exclude_patterns, + int trim, enum do_for_each_ref_flags flags); /* * A callback function used to instruct merge_ref_iterator how to @@ -571,7 +571,8 @@ typedef int copy_ref_fn(struct ref_store *ref_store, */ typedef struct ref_iterator *ref_iterator_begin_fn( struct ref_store *ref_store, - const char *prefix, unsigned int flags); + const char *prefix, const char **exclude_patterns, + unsigned int flags); /* reflog functions */ @@ -2259,7 +2259,8 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs, * Return true when there is anything to report, otherwise false. */ int format_tracking_info(struct branch *branch, struct strbuf *sb, - enum ahead_behind_flags abf) + enum ahead_behind_flags abf, + int show_divergence_advice) { int ours, theirs, sti; const char *full_base; @@ -2322,9 +2323,10 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb, "respectively.\n", ours + theirs), base, ours, theirs); - if (advice_enabled(ADVICE_STATUS_HINTS)) + if (show_divergence_advice && + advice_enabled(ADVICE_STATUS_HINTS)) strbuf_addstr(sb, - _(" (use \"git pull\" to merge the remote branch into yours)\n")); + _(" (use \"git pull\" if you want to integrate the remote branch with yours)\n")); } free(base); return 1; @@ -380,7 +380,8 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs, const char **upstream_name, int for_push, enum ahead_behind_flags abf); int format_tracking_info(struct branch *branch, struct strbuf *sb, - enum ahead_behind_flags abf); + enum ahead_behind_flags abf, + int show_divergence_advice); struct ref *get_local_heads(void); /* @@ -204,7 +204,7 @@ static void read_rr(struct repository *r, struct string_list *rr) const unsigned hexsz = the_hash_algo->hexsz; /* There has to be the hash, tab, path and then NUL */ - if (buf.len < hexsz + 2 || get_sha1_hex(buf.buf, hash)) + if (buf.len < hexsz + 2 || get_hash_hex(buf.buf, hash)) die(_("corrupt MERGE_RR")); if (buf.buf[hexsz] != '.') { diff --git a/revision.c b/revision.c index 0976d41404..2f4c53ea20 100644 --- a/revision.c +++ b/revision.c @@ -1560,7 +1560,7 @@ void init_ref_exclusions(struct ref_exclusions *exclusions) void clear_ref_exclusions(struct ref_exclusions *exclusions) { string_list_clear(&exclusions->excluded_refs, 0); - string_list_clear(&exclusions->hidden_refs, 0); + strvec_clear(&exclusions->hidden_refs); exclusions->hidden_refs_configured = 0; } @@ -2641,7 +2641,7 @@ static int for_each_bisect_ref(struct ref_store *refs, each_ref_fn fn, struct strbuf bisect_refs = STRBUF_INIT; int status; strbuf_addf(&bisect_refs, "refs/bisect/%s", term); - status = refs_for_each_fullref_in(refs, bisect_refs.buf, fn, cb_data); + status = refs_for_each_fullref_in(refs, bisect_refs.buf, NULL, fn, cb_data); strbuf_release(&bisect_refs); return status; } @@ -2970,7 +2970,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s if (!revs->def) revs->def = opt ? opt->def : NULL; if (opt && opt->tweak) - opt->tweak(revs, opt); + opt->tweak(revs); if (revs->show_merge) prepare_show_merge(revs); if (revs->def && !revs->pending.nr && !revs->rev_input_given) { diff --git a/revision.h b/revision.h index 25776af381..82ab400139 100644 --- a/revision.h +++ b/revision.h @@ -10,6 +10,7 @@ #include "decorate.h" #include "ident.h" #include "list-objects-filter-options.h" +#include "strvec.h" /** * The revision walking API offers functions to build a list of revisions @@ -95,7 +96,7 @@ struct ref_exclusions { * Hidden refs is a list of patterns that is to be hidden via * `ref_is_hidden()`. */ - struct string_list hidden_refs; + struct strvec hidden_refs; /* * Indicates whether hidden refs have been configured. This is to @@ -110,7 +111,7 @@ struct ref_exclusions { */ #define REF_EXCLUSIONS_INIT { \ .excluded_refs = STRING_LIST_INIT_DUP, \ - .hidden_refs = STRING_LIST_INIT_DUP, \ + .hidden_refs = STRVEC_INIT, \ } struct oidset; @@ -428,7 +429,7 @@ void repo_init_revisions(struct repository *r, */ struct setup_revision_opt { const char *def; - void (*tweak)(struct rev_info *, struct setup_revision_opt *); + void (*tweak)(struct rev_info *); unsigned int assume_dashdash:1, allow_exclude_promisor_objects:1, free_removed_argv_elements:1; diff --git a/sequencer.c b/sequencer.c index cc9821ece2..adc9cfb4df 100644 --- a/sequencer.c +++ b/sequencer.c @@ -2702,7 +2702,7 @@ int todo_list_parse_insn_buffer(struct repository *r, char *buf, if (fixup_okay) ; /* do nothing */ else if (is_fixup(item->command)) - return error(_("cannot '%s' without a previous commit"), + res = error(_("cannot '%s' without a previous commit"), command_to_string(item->command)); else if (!is_noop(item->command)) fixup_okay = 1; @@ -936,31 +936,19 @@ void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm, * of seconds. */ while (strbuf_expand_step(&munged_fmt, &fmt)) { - switch (*fmt) { - case '%': + if (skip_prefix(fmt, "%", &fmt)) strbuf_addstr(&munged_fmt, "%%"); - fmt++; - break; - case 's': + else if (skip_prefix(fmt, "s", &fmt)) strbuf_addf(&munged_fmt, "%"PRItime, (timestamp_t)tm_to_time_t(tm) - 3600 * (tz_offset / 100) - 60 * (tz_offset % 100)); - fmt++; - break; - case 'z': + else if (skip_prefix(fmt, "z", &fmt)) strbuf_addf(&munged_fmt, "%+05d", tz_offset); - fmt++; - break; - case 'Z': - if (suppress_tz_name) { - fmt++; - break; - } - /* FALLTHROUGH */ - default: + else if (suppress_tz_name && skip_prefix(fmt, "Z", &fmt)) + ; /* nothing */ + else strbuf_addch(&munged_fmt, '%'); - } } fmt = munged_fmt.buf; diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh index 2ef70235b1..5e21e84f38 100644 --- a/t/annotate-tests.sh +++ b/t/annotate-tests.sh @@ -83,6 +83,15 @@ test_expect_success 'blame with --contents' ' check_count --contents=file A 2 ' +test_expect_success 'blame with --contents in a bare repo' ' + git clone --bare . bare-contents.git && + ( + cd bare-contents.git && + echo "1A quick brown fox jumps over the" >contents && + check_count --contents=contents A 1 + ) +' + test_expect_success 'blame with --contents changed' ' echo "1A quick brown fox jumps over the" >contents && echo "another lazy dog" >>contents && diff --git a/t/helper/test-dump-split-index.c b/t/helper/test-dump-split-index.c index 5cf0b26dca..f29d18ef94 100644 --- a/t/helper/test-dump-split-index.c +++ b/t/helper/test-dump-split-index.c @@ -7,7 +7,7 @@ #include "split-index.h" #include "ewah/ewok.h" -static void show_bit(size_t pos, void *data) +static void show_bit(size_t pos, void *data UNUSED) { printf(" %d", (int)pos); } diff --git a/t/helper/test-oid-array.c b/t/helper/test-oid-array.c index eef68833b7..aafe398ef0 100644 --- a/t/helper/test-oid-array.c +++ b/t/helper/test-oid-array.c @@ -4,7 +4,7 @@ #include "setup.h" #include "strbuf.h" -static int print_oid(const struct object_id *oid, void *data) +static int print_oid(const struct object_id *oid, void *data UNUSED) { puts(oid_to_hex(oid)); return 0; diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c index 00fa281a9c..a4f6e24b0c 100644 --- a/t/helper/test-parse-options.c +++ b/t/helper/test-parse-options.c @@ -133,6 +133,8 @@ int cmd__parse_options(int argc, const char **argv) OPT_STRING(0, "st", &string, "st", "get another string (pervert ordering)"), OPT_STRING('o', NULL, &string, "str", "get another string"), OPT_NOOP_NOARG(0, "obsolete"), + OPT_SET_INT_F(0, "longhelp", &integer, "help text of this entry\n" + "spans multiple lines", 0, PARSE_OPT_NONEG), OPT_STRING_LIST(0, "list", &list, "str", "add str to list"), OPT_GROUP("Magic arguments"), OPT_NUMBER_CALLBACK(&integer, "set integer to NUM", diff --git a/t/helper/test-reach.c b/t/helper/test-reach.c index 119f4908cf..3e173399a0 100644 --- a/t/helper/test-reach.c +++ b/t/helper/test-reach.c @@ -138,7 +138,7 @@ int cmd__reach(int ac, const char **av) printf("%s(X,_,_,0,0):%d\n", av[1], can_all_from_reach_with_flag(&X_obj, 2, 4, 0, 0)); } else if (!strcmp(av[1], "commit_contains")) { - struct ref_filter filter; + struct ref_filter filter = REF_FILTER_INIT; struct contains_cache cache; init_contains_cache(&cache); diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c index fb18831ec2..48552e6a9e 100644 --- a/t/helper/test-ref-store.c +++ b/t/helper/test-ref-store.c @@ -186,6 +186,15 @@ static int cmd_for_each_ref(struct ref_store *refs, const char **argv) return refs_for_each_ref_in(refs, prefix, each_ref, NULL); } +static int cmd_for_each_ref__exclude(struct ref_store *refs, const char **argv) +{ + const char *prefix = notnull(*argv++, "prefix"); + const char **exclude_patterns = argv; + + return refs_for_each_fullref_in(refs, prefix, exclude_patterns, each_ref, + NULL); +} + static int cmd_resolve_ref(struct ref_store *refs, const char **argv) { struct object_id oid = *null_oid(); @@ -268,11 +277,6 @@ static int cmd_delete_reflog(struct ref_store *refs, const char **argv) return refs_delete_reflog(refs, refname); } -static int cmd_reflog_expire(struct ref_store *refs, const char **argv) -{ - die("not supported yet"); -} - static int cmd_delete_ref(struct ref_store *refs, const char **argv) { const char *msg = notnull(*argv++, "msg"); @@ -318,6 +322,7 @@ static struct command commands[] = { { "delete-refs", cmd_delete_refs }, { "rename-ref", cmd_rename_ref }, { "for-each-ref", cmd_for_each_ref }, + { "for-each-ref--exclude", cmd_for_each_ref__exclude }, { "resolve-ref", cmd_resolve_ref }, { "verify-ref", cmd_verify_ref }, { "for-each-reflog", cmd_for_each_reflog }, @@ -326,7 +331,6 @@ static struct command commands[] = { { "reflog-exists", cmd_reflog_exists }, { "create-reflog", cmd_create_reflog }, { "delete-reflog", cmd_delete_reflog }, - { "reflog-expire", cmd_reflog_expire }, /* * backend transaction functions can't be tested separately */ diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index 7d7ecfd571..e19a199636 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -30,11 +30,12 @@ usage: test-tool parse-options <options> -F, --file <file> set file to <file> String options - -s, --string <string> - get a string + -s, --string <string> get a string --string2 <str> get another string --st <st> get another string (pervert ordering) -o <str> get another string + --longhelp help text of this entry + spans multiple lines --list <str> add str to list Magic arguments diff --git a/t/t0041-usage.sh b/t/t0041-usage.sh index c4fc34eb18..9ea974b0c6 100755 --- a/t/t0041-usage.sh +++ b/t/t0041-usage.sh @@ -5,6 +5,7 @@ test_description='Test commands behavior when given invalid argument value' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'setup ' ' diff --git a/t/t1419-exclude-refs.sh b/t/t1419-exclude-refs.sh new file mode 100755 index 0000000000..5d8c86b657 --- /dev/null +++ b/t/t1419-exclude-refs.sh @@ -0,0 +1,122 @@ +#!/bin/sh + +test_description='test exclude_patterns functionality in main ref store' + +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME + +TEST_PASSES_SANITIZE_LEAK=true +. ./test-lib.sh + +for_each_ref__exclude () { + GIT_TRACE2_PERF=1 test-tool ref-store main \ + for-each-ref--exclude "$@" >actual.raw + cut -d ' ' -f 2 actual.raw +} + +for_each_ref () { + git for-each-ref --format='%(refname)' "$@" +} + +assert_jumps () { + local nr="$1" + local trace="$2" + + grep -q "name:jumps_made value:$nr$" $trace +} + +assert_no_jumps () { + ! assert_jumps ".*" "$1" +} + +test_expect_success 'setup' ' + test_commit --no-tag base && + base="$(git rev-parse HEAD)" && + + for name in foo bar baz quux + do + for i in 1 2 3 + do + echo "create refs/heads/$name/$i $base" || return 1 + done || return 1 + done >in && + echo "delete refs/heads/main" >>in && + + git update-ref --stdin <in && + git pack-refs --all +' + +test_expect_success 'excluded region in middle' ' + for_each_ref__exclude refs/heads refs/heads/foo >actual 2>perf && + for_each_ref refs/heads/bar refs/heads/baz refs/heads/quux >expect && + + test_cmp expect actual && + assert_jumps 1 perf +' + +test_expect_success 'excluded region at beginning' ' + for_each_ref__exclude refs/heads refs/heads/bar >actual 2>perf && + for_each_ref refs/heads/baz refs/heads/foo refs/heads/quux >expect && + + test_cmp expect actual && + assert_jumps 1 perf +' + +test_expect_success 'excluded region at end' ' + for_each_ref__exclude refs/heads refs/heads/quux >actual 2>perf && + for_each_ref refs/heads/foo refs/heads/bar refs/heads/baz >expect && + + test_cmp expect actual && + assert_jumps 1 perf +' + +test_expect_success 'disjoint excluded regions' ' + for_each_ref__exclude refs/heads refs/heads/bar refs/heads/quux >actual 2>perf && + for_each_ref refs/heads/baz refs/heads/foo >expect && + + test_cmp expect actual && + assert_jumps 2 perf +' + +test_expect_success 'adjacent, non-overlapping excluded regions' ' + for_each_ref__exclude refs/heads refs/heads/bar refs/heads/baz >actual 2>perf && + for_each_ref refs/heads/foo refs/heads/quux >expect && + + test_cmp expect actual && + assert_jumps 1 perf +' + +test_expect_success 'overlapping excluded regions' ' + for_each_ref__exclude refs/heads refs/heads/ba refs/heads/baz >actual 2>perf && + for_each_ref refs/heads/foo refs/heads/quux >expect && + + test_cmp expect actual && + assert_jumps 1 perf +' + +test_expect_success 'several overlapping excluded regions' ' + for_each_ref__exclude refs/heads \ + refs/heads/bar refs/heads/baz refs/heads/foo >actual 2>perf && + for_each_ref refs/heads/quux >expect && + + test_cmp expect actual && + assert_jumps 1 perf +' + +test_expect_success 'non-matching excluded section' ' + for_each_ref__exclude refs/heads refs/heads/does/not/exist >actual 2>perf && + for_each_ref >expect && + + test_cmp expect actual && + assert_no_jumps perf +' + +test_expect_success 'meta-characters are discarded' ' + for_each_ref__exclude refs/heads "refs/heads/ba*" >actual 2>perf && + for_each_ref >expect && + + test_cmp expect actual && + assert_no_jumps perf +' + +test_done diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh index 0ac468e69e..051363acbb 100755 --- a/t/t2400-worktree-add.sh +++ b/t/t2400-worktree-add.sh @@ -417,9 +417,9 @@ test_wt_add_orphan_hint () { grep "hint: If you meant to create a worktree containing a new orphan branch" actual && if [ $use_branch -eq 1 ] then - grep -E "^hint:\s+git worktree add --orphan -b \S+ \S+\s*$" actual + grep -E "^hint: +git worktree add --orphan -b [^ ]+ [^ ]+$" actual else - grep -E "^hint:\s+git worktree add --orphan \S+\s*$" actual + grep -E "^hint: +git worktree add --orphan [^ ]+$" actual fi ' @@ -709,8 +709,8 @@ test_dwim_orphan () { local info_text="No possible source branch, inferring '--orphan'" && local fetch_error_text="fatal: No local or remote refs exist despite at least one remote" && local orphan_hint="hint: If you meant to create a worktree containing a new orphan branch" && - local invalid_ref_regex="^fatal: invalid reference:\s\+.*" && - local bad_combo_regex="^fatal: '[a-z-]\+' and '[a-z-]\+' cannot be used together" && + local invalid_ref_regex="^fatal: invalid reference: " && + local bad_combo_regex="^fatal: '[-a-z]*' and '[-a-z]*' cannot be used together" && local git_ns="repo" && local dashc_args="-C $git_ns" && @@ -995,11 +995,11 @@ test_dwim_orphan () { grep "$invalid_ref_regex" actual && ! grep "$orphan_hint" actual else - headpath=$(git $dashc_args rev-parse --sq --path-format=absolute --git-path HEAD) && + headpath=$(git $dashc_args rev-parse --path-format=absolute --git-path HEAD) && headcontents=$(cat "$headpath") && grep "HEAD points to an invalid (or orphaned) reference" actual && - grep "HEAD path:\s*.$headpath." actual && - grep "HEAD contents:\s*.$headcontents." actual && + grep "HEAD path: .$headpath." actual && + grep "HEAD contents: .$headcontents." actual && grep "$orphan_hint" actual && ! grep "$info_text" actual fi && diff --git a/t/t2407-worktree-heads.sh b/t/t2407-worktree-heads.sh index 019a40df2c..469443d8ae 100755 --- a/t/t2407-worktree-heads.sh +++ b/t/t2407-worktree-heads.sh @@ -58,7 +58,7 @@ test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in bisect' ' git -C wt-4 bisect good wt-1 && test_must_fail git branch -f wt-4 HEAD 2>err && - grep "cannot force update the branch '\''wt-4'\'' checked out at.*wt-4" err + grep "cannot force update the branch '\''wt-4'\'' used by worktree at.*wt-4" err ' test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (apply)' ' @@ -68,7 +68,7 @@ test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (app test_must_fail git -C wt-2 rebase --apply conflict-2 && test_must_fail git branch -f wt-2 HEAD 2>err && - grep "cannot force update the branch '\''wt-2'\'' checked out at.*wt-2" err + grep "cannot force update the branch '\''wt-2'\'' used by worktree at.*wt-2" err ' test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (merge)' ' @@ -78,7 +78,7 @@ test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase (mer test_must_fail git -C wt-2 rebase conflict-2 && test_must_fail git branch -f wt-2 HEAD 2>err && - grep "cannot force update the branch '\''wt-2'\'' checked out at.*wt-2" err + grep "cannot force update the branch '\''wt-2'\'' used by worktree at.*wt-2" err ' test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase with --update-refs' ' @@ -90,7 +90,7 @@ test_expect_success !SANITIZE_LEAK 'refuse to overwrite: worktree in rebase with for i in 3 4 do test_must_fail git branch -f can-be-updated HEAD 2>err && - grep "cannot force update the branch '\''can-be-updated'\'' checked out at.*wt-3" err || + grep "cannot force update the branch '\''can-be-updated'\'' used by worktree at.*wt-3" err || return 1 done ' @@ -150,7 +150,7 @@ test_expect_success 'refuse to overwrite when in error states' ' for i in 1 2 do test_must_fail git branch -f fake-$i HEAD 2>err && - grep "cannot force update the branch '\''fake-$i'\'' checked out at" err || + grep "cannot force update the branch '\''fake-$i'\'' used by worktree at" err || return 1 done ' diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh index 217006d1bf..5af2dac0e4 100755 --- a/t/t3101-ls-tree-dirname.sh +++ b/t/t3101-ls-tree-dirname.sh @@ -154,6 +154,14 @@ EOF test_output ' +test_expect_success 'ls-tree --no-full-name' ' + git -C path0 ls-tree --no-full-name $tree a >current && + cat >expected <<-EOF && + 040000 tree X a + EOF + test_output +' + test_expect_success 'ls-tree --full-tree' ' ( cd path1/b/c && diff --git a/t/t3202-show-branch.sh b/t/t3202-show-branch.sh index be20ebe1d5..b17f388f56 100755 --- a/t/t3202-show-branch.sh +++ b/t/t3202-show-branch.sh @@ -119,6 +119,22 @@ test_expect_success 'show branch --remotes' ' test_must_be_empty actual.out ' +test_expect_success 'show-branch --sparse' ' + test_when_finished "git checkout branch10 && git branch -D branchA" && + git checkout -b branchA branch10 && + git merge -s ours -m "merge 1 and 10 to make A" branch1 && + git commit --allow-empty -m "another" && + + git show-branch --sparse >out && + grep "merge 1 and 10 to make A" out && + + git show-branch >out && + ! grep "merge 1 and 10 to make A" out && + + git show-branch --no-sparse >out && + ! grep "merge 1 and 10 to make A" out +' + test_expect_success 'setup show branch --list' ' sed "s/^> //" >expect <<-\EOF > [branch1] branch1 @@ -197,6 +213,15 @@ done <<\EOF --reflog --current EOF +# unnegatable options +for opt in topo-order date-order reflog +do + test_expect_success "show-branch --no-$opt (should fail)" ' + test_must_fail git show-branch --no-$opt 2>err && + grep "unknown option .no-$opt." err + ' +done + test_expect_success 'error descriptions on non-existent branch' ' cat >expect <<-EOF && error: No branch named '\''non-existent'\'.' diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index 93f8295339..758963b189 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -55,9 +55,17 @@ cat >expect <<'EOF' EOF test_expect_success 'git branch -r shows remote branches' ' git branch -r >actual && + test_cmp expect actual && + + git branch --remotes >actual && test_cmp expect actual ' +test_expect_success 'git branch --no-remotes is rejected' ' + test_must_fail git branch --no-remotes 2>err && + grep "unknown option .no-remotes." err +' + cat >expect <<'EOF' branch-one branch-two @@ -68,9 +76,17 @@ cat >expect <<'EOF' EOF test_expect_success 'git branch -a shows local and remote branches' ' git branch -a >actual && + test_cmp expect actual && + + git branch --all >actual && test_cmp expect actual ' +test_expect_success 'git branch --no-all is rejected' ' + test_must_fail git branch --no-all 2>err && + grep "unknown option .no-all." err +' + cat >expect <<'EOF' two one diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh index 79b0640c00..e9e03ca4b5 100755 --- a/t/t3402-rebase-merge.sh +++ b/t/t3402-rebase-merge.sh @@ -8,6 +8,7 @@ test_description='git rebase --merge test' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh T="A quick brown fox diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index ff0afad63e..96a56aafbe 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -1596,6 +1596,32 @@ test_expect_success 'static check of bad command' ' test C = $(git cat-file commit HEAD^ | sed -ne \$p) ' +test_expect_success 'the first command cannot be a fixup' ' + rebase_setup_and_clean fixup-first && + + cat >orig <<-EOF && + fixup $(git log -1 --format="%h %s" B) + pick $(git log -1 --format="%h %s" C) + EOF + + ( + set_replace_editor orig && + test_must_fail git rebase -i A 2>actual + ) && + grep "cannot .fixup. without a previous commit" actual && + grep "You can fix this with .git rebase --edit-todo.." actual && + # verify that the todo list has not been truncated + grep -v "^#" .git/rebase-merge/git-rebase-todo >actual && + test_cmp orig actual && + + test_must_fail git rebase --edit-todo 2>actual && + grep "cannot .fixup. without a previous commit" actual && + grep "You can fix this with .git rebase --edit-todo.." actual && + # verify that the todo list has not been truncated + grep -v "^#" .git/rebase-merge/git-rebase-todo >actual && + test_cmp orig actual +' + test_expect_success 'tabs and spaces are accepted in the todolist' ' rebase_setup_and_clean indented-comment && write_script add-indent.sh <<-\EOF && diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh index d524d4057d..7afc883ec3 100755 --- a/t/t4002-diff-basic.sh +++ b/t/t4002-diff-basic.sh @@ -403,7 +403,7 @@ test_expect_success 'diff-tree -r B A == diff-tree -r -R A B' ' git diff-tree -r -R $tree_A $tree_B >.test-b && cmp -s .test-a .test-b' -test_expect_success'diff can read from stdin' ' +test_expect_success 'diff can read from stdin' ' test_must_fail git diff --no-index -- MN - < NN | grep -v "^index" | sed "s#/-#/NN#" >.test-a && test_must_fail git diff --no-index -- MN NN | diff --git a/t/t5351-unpack-large-objects.sh b/t/t5351-unpack-large-objects.sh index 8c8af99b84..43cbcd5d49 100755 --- a/t/t5351-unpack-large-objects.sh +++ b/t/t5351-unpack-large-objects.sh @@ -55,7 +55,7 @@ check_fsync_events () { cat >expect && sed -n \ - -e '/^{"event":"data",.*"category":"fsync",/ { + -e '/^{"event":"counter",.*"category":"fsync",/ { s/.*"category":"fsync",//; s/}$//; p; @@ -78,8 +78,8 @@ test_expect_success 'unpack big object in stream (core.fsyncmethod=batch)' ' flush_count=1 fi && check_fsync_events trace2.txt <<-EOF && - "key":"fsync/writeout-only","value":"6" - "key":"fsync/hardware-flush","value":"$flush_count" + "name":"writeout-only","count":6 + "name":"hardware-flush","count":$flush_count EOF test_dir_is_empty dest.git/objects/pack && diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 19ebefa5ac..87163d7745 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -120,6 +120,17 @@ test_expect_success setup ' ' +for cmd in push fetch +do + for opt in ipv4 ipv6 + do + test_expect_success "reject 'git $cmd --no-$opt'" ' + test_must_fail git $cmd --no-$opt 2>err && + grep "unknown option .no-$opt" err + ' + done +done + test_expect_success 'fetch without wildcard' ' mk_empty testrepo && ( diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index c9afcef201..0a5c487540 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh @@ -85,6 +85,7 @@ check_describe e-1-gHASH --tags HEAD^^ check_describe c-2-gHASH --tags HEAD^^2 check_describe B --tags HEAD^^2^ check_describe e --tags HEAD^^^ +check_describe e --tags --exact-match HEAD^^^ check_describe heads/main --all HEAD check_describe tags/c-6-gHASH --all HEAD^ @@ -96,6 +97,13 @@ check_describe A-3-gHASH --long HEAD^^2 check_describe c-7-gHASH --tags check_describe e-3-gHASH --first-parent --tags +check_describe c-7-gHASH --tags --no-exact-match HEAD +check_describe e-3-gHASH --first-parent --tags --no-exact-match HEAD + +test_expect_success '--exact-match failure' ' + test_must_fail git describe --exact-match HEAD 2>err +' + test_expect_success 'describe --contains defaults to HEAD without commit-ish' ' echo "A^0" >expect && git checkout A && diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index 6e6ec852b5..5b434ab451 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -449,6 +449,41 @@ test_expect_success 'exercise glob patterns with prefixes' ' ' cat >expected <<\EOF +refs/tags/bar +refs/tags/baz +refs/tags/testtag +EOF + +test_expect_success 'exercise patterns with prefix exclusions' ' + for tag in foo/one foo/two foo/three bar baz + do + git tag "$tag" || return 1 + done && + test_when_finished "git tag -d foo/one foo/two foo/three bar baz" && + git for-each-ref --format="%(refname)" \ + refs/tags/ --exclude=refs/tags/foo >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +refs/tags/bar +refs/tags/baz +refs/tags/foo/one +refs/tags/testtag +EOF + +test_expect_success 'exercise patterns with pattern exclusions' ' + for tag in foo/one foo/two foo/three bar baz + do + git tag "$tag" || return 1 + done && + test_when_finished "git tag -d foo/one foo/two foo/three bar baz" && + git for-each-ref --format="%(refname)" \ + refs/tags/ --exclude="refs/tags/foo/t*" >actual && + test_cmp expected actual +' + +cat >expected <<\EOF 'refs/heads/main' 'refs/remotes/origin/main' 'refs/tags/testtag' @@ -562,6 +597,144 @@ test_expect_success 'color.ui=always does not override tty check' ' test_cmp expected.bare actual ' +test_expect_success 'setup for describe atom tests' ' + git init -b master describe-repo && + ( + cd describe-repo && + + test_commit --no-tag one && + git tag tagone && + + test_commit --no-tag two && + git tag -a -m "tag two" tagtwo + ) +' + +test_expect_success 'describe atom vs git describe' ' + ( + cd describe-repo && + + git for-each-ref --format="%(objectname)" \ + refs/tags/ >obj && + while read hash + do + if desc=$(git describe $hash) + then + : >expect-contains-good + else + : >expect-contains-bad + fi && + echo "$hash $desc" || return 1 + done <obj >expect && + test_path_exists expect-contains-good && + test_path_exists expect-contains-bad && + + git for-each-ref --format="%(objectname) %(describe)" \ + refs/tags/ >actual 2>err && + test_cmp expect actual && + test_must_be_empty err + ) +' + +test_expect_success 'describe:tags vs describe --tags' ' + ( + cd describe-repo && + git describe --tags >expect && + git for-each-ref --format="%(describe:tags)" \ + refs/heads/master >actual && + test_cmp expect actual + ) +' + +test_expect_success 'describe:abbrev=... vs describe --abbrev=...' ' + ( + cd describe-repo && + + # Case 1: We have commits between HEAD and the most + # recent tag reachable from it + test_commit --no-tag file && + git describe --abbrev=14 >expect && + git for-each-ref --format="%(describe:abbrev=14)" \ + refs/heads/master >actual && + test_cmp expect actual && + + # Make sure the hash used is atleast 14 digits long + sed -e "s/^.*-g\([0-9a-f]*\)$/\1/" <actual >hexpart && + test 15 -le $(wc -c <hexpart) && + + # Case 2: We have a tag at HEAD, describe directly gives + # the name of the tag + git tag -a -m tagged tagname && + git describe --abbrev=14 >expect && + git for-each-ref --format="%(describe:abbrev=14)" \ + refs/heads/master >actual && + test_cmp expect actual && + test tagname = $(cat actual) + ) +' + +test_expect_success 'describe:match=... vs describe --match ...' ' + ( + cd describe-repo && + git tag -a -m "tag foo" tag-foo && + git describe --match "*-foo" >expect && + git for-each-ref --format="%(describe:match="*-foo")" \ + refs/heads/master >actual && + test_cmp expect actual + ) +' + +test_expect_success 'describe:exclude:... vs describe --exclude ...' ' + ( + cd describe-repo && + git tag -a -m "tag bar" tag-bar && + git describe --exclude "*-bar" >expect && + git for-each-ref --format="%(describe:exclude="*-bar")" \ + refs/heads/master >actual && + test_cmp expect actual + ) +' + +test_expect_success 'deref with describe atom' ' + ( + cd describe-repo && + cat >expect <<-\EOF && + + tagname + tagname + tagname + + tagtwo + EOF + git for-each-ref --format="%(*describe)" >actual && + test_cmp expect actual + ) +' + +test_expect_success 'err on bad describe atom arg' ' + ( + cd describe-repo && + + # The bad arg is the only arg passed to describe atom + cat >expect <<-\EOF && + fatal: unrecognized %(describe) argument: baz + EOF + test_must_fail git for-each-ref --format="%(describe:baz)" \ + refs/heads/master 2>actual && + test_cmp expect actual && + + # The bad arg is in the middle of the option string + # passed to the describe atom + cat >expect <<-\EOF && + fatal: unrecognized %(describe) argument: qux=1,abbrev=14 + EOF + test_must_fail git for-each-ref \ + --format="%(describe:tags,qux=1,abbrev=14)" \ + ref/heads/master 2>actual && + test_cmp expect actual + ) +' + cat >expected <<\EOF heads/main tags/main @@ -1584,7 +1757,8 @@ test_expect_success GPGSSH 'setup for signature atom using ssh' ' test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" && echo "8" >file && test_tick && - git commit -a -S -m "file: 8" && + git add file && + git commit -S -m "file: 8" && git tag eighth-signed-ssh ' diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh index 1ce5f490e9..af223e44d6 100755 --- a/t/t6302-for-each-ref-filter.sh +++ b/t/t6302-for-each-ref-filter.sh @@ -45,6 +45,8 @@ test_expect_success 'check signed tags with --points-at' ' sed -e "s/Z$//" >expect <<-\EOF && refs/heads/side Z refs/tags/annotated-tag four + refs/tags/doubly-annotated-tag An annotated tag + refs/tags/doubly-signed-tag A signed tag refs/tags/four Z refs/tags/signed-tag four EOF diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index 22477f3a31..4287863ae6 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -71,6 +71,16 @@ check_changes () { done | test_cmp .cat_expect - } +# no negated form for various type of resets +for opt in soft mixed hard merge keep +do + test_expect_success "no 'git reset --no-$opt'" ' + test_when_finished "rm -f err" && + test_must_fail git reset --no-$opt 2>err && + grep "error: unknown option .no-$opt." err + ' +done + test_expect_success 'reset --hard message' ' hex=$(git log -1 --format="%h") && git reset --hard >.actual && diff --git a/t/t7508-status.sh b/t/t7508-status.sh index 36567708f5..6928fd89f5 100755 --- a/t/t7508-status.sh +++ b/t/t7508-status.sh @@ -92,7 +92,7 @@ test_expect_success 'status --column' ' # On branch main # Your branch and '\''upstream'\'' have diverged, # and have 1 and 2 different commits each, respectively. -# (use "git pull" to merge the remote branch into yours) +# (use "git pull" if you want to integrate the remote branch with yours) # # Changes to be committed: # (use "git restore --staged <file>..." to unstage) @@ -123,7 +123,7 @@ cat >expect <<\EOF # On branch main # Your branch and 'upstream' have diverged, # and have 1 and 2 different commits each, respectively. -# (use "git pull" to merge the remote branch into yours) +# (use "git pull" if you want to integrate the remote branch with yours) # # Changes to be committed: # (use "git restore --staged <file>..." to unstage) @@ -270,7 +270,7 @@ test_expect_success 'status with gitignore' ' On branch main Your branch and '\''upstream'\'' have diverged, and have 1 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) + (use "git pull" if you want to integrate the remote branch with yours) Changes to be committed: (use "git restore --staged <file>..." to unstage) @@ -335,7 +335,7 @@ test_expect_success 'status with gitignore (nothing untracked)' ' On branch main Your branch and '\''upstream'\'' have diverged, and have 1 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) + (use "git pull" if you want to integrate the remote branch with yours) Changes to be committed: (use "git restore --staged <file>..." to unstage) @@ -405,7 +405,7 @@ test_expect_success 'status -uno' ' On branch main Your branch and '\''upstream'\'' have diverged, and have 1 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) + (use "git pull" if you want to integrate the remote branch with yours) Changes to be committed: (use "git restore --staged <file>..." to unstage) @@ -467,7 +467,7 @@ test_expect_success 'status -unormal' ' On branch main Your branch and '\''upstream'\'' have diverged, and have 1 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) + (use "git pull" if you want to integrate the remote branch with yours) Changes to be committed: (use "git restore --staged <file>..." to unstage) @@ -522,7 +522,7 @@ test_expect_success 'status -uall' ' On branch main Your branch and '\''upstream'\'' have diverged, and have 1 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) + (use "git pull" if you want to integrate the remote branch with yours) Changes to be committed: (use "git restore --staged <file>..." to unstage) @@ -582,7 +582,7 @@ test_expect_success 'status with relative paths' ' On branch main Your branch and '\''upstream'\'' have diverged, and have 1 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) + (use "git pull" if you want to integrate the remote branch with yours) Changes to be committed: (use "git restore --staged <file>..." to unstage) @@ -650,7 +650,7 @@ test_expect_success TTY 'status with color.ui' ' On branch <GREEN>main<RESET> Your branch and '\''upstream'\'' have diverged, and have 1 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) + (use "git pull" if you want to integrate the remote branch with yours) Changes to be committed: (use "git restore --staged <file>..." to unstage) @@ -773,7 +773,7 @@ test_expect_success 'status without relative paths' ' On branch main Your branch and '\''upstream'\'' have diverged, and have 1 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) + (use "git pull" if you want to integrate the remote branch with yours) Changes to be committed: (use "git restore --staged <file>..." to unstage) @@ -847,7 +847,6 @@ test_expect_success 'dry-run of partial commit excluding new file in index' ' On branch main Your branch and '\''upstream'\'' have diverged, and have 1 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) Changes to be committed: (use "git restore --staged <file>..." to unstage) @@ -901,7 +900,7 @@ test_expect_success 'status submodule summary is disabled by default' ' On branch main Your branch and '\''upstream'\'' have diverged, and have 1 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) + (use "git pull" if you want to integrate the remote branch with yours) Changes to be committed: (use "git restore --staged <file>..." to unstage) @@ -958,7 +957,7 @@ test_expect_success 'status submodule summary' ' On branch main Your branch and '\''upstream'\'' have diverged, and have 1 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) + (use "git pull" if you want to integrate the remote branch with yours) Changes to be committed: (use "git restore --staged <file>..." to unstage) @@ -1013,11 +1012,11 @@ test_expect_success 'status -s submodule summary' ' ' test_expect_success 'status submodule summary (clean submodule): commit' ' - cat >expect <<EOF && + cat >expect-status <<EOF && On branch main Your branch and '\''upstream'\'' have diverged, and have 2 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) + (use "git pull" if you want to integrate the remote branch with yours) Changes not staged for commit: (use "git add <file>..." to update what will be committed) @@ -1033,12 +1032,13 @@ Untracked files: no changes added to commit (use "git add" and/or "git commit -a") EOF + sed "/git pull/d" expect-status > expect-commit && git commit -m "commit submodule" && git config status.submodulesummary 10 && test_must_fail git commit --dry-run >output && - test_cmp expect output && + test_cmp expect-commit output && git status >output && - test_cmp expect output + test_cmp expect-status output ' cat >expect <<EOF @@ -1065,7 +1065,6 @@ test_expect_success 'commit --dry-run submodule summary (--amend)' ' On branch main Your branch and '\''upstream'\'' have diverged, and have 2 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) Changes to be committed: (use "git restore --source=HEAD^1 --staged <file>..." to unstage) @@ -1117,7 +1116,7 @@ test_expect_success '--ignore-submodules=untracked suppresses submodules with un On branch main Your branch and '\''upstream'\'' have diverged, and have 2 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) + (use "git pull" if you want to integrate the remote branch with yours) Changes to be committed: (use "git restore --staged <file>..." to unstage) @@ -1226,7 +1225,7 @@ test_expect_success "--ignore-submodules=untracked doesn't suppress submodules w On branch main Your branch and '\''upstream'\'' have diverged, and have 2 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) + (use "git pull" if you want to integrate the remote branch with yours) Changes to be committed: (use "git restore --staged <file>..." to unstage) @@ -1283,7 +1282,7 @@ test_expect_success "--ignore-submodules=untracked doesn't suppress submodule su On branch main Your branch and '\''upstream'\'' have diverged, and have 2 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) + (use "git pull" if you want to integrate the remote branch with yours) Changes to be committed: (use "git restore --staged <file>..." to unstage) @@ -1364,7 +1363,7 @@ cat > expect << EOF ; On branch main ; Your branch and 'upstream' have diverged, ; and have 2 and 2 different commits each, respectively. -; (use "git pull" to merge the remote branch into yours) +; (use "git pull" if you want to integrate the remote branch with yours) ; ; Changes to be committed: ; (use "git restore --staged <file>..." to unstage) @@ -1412,7 +1411,7 @@ test_expect_success "--ignore-submodules=all suppresses submodule summary" ' On branch main Your branch and '\''upstream'\'' have diverged, and have 2 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) + (use "git pull" if you want to integrate the remote branch with yours) Changes not staged for commit: (use "git add <file>..." to update what will be committed) @@ -1438,7 +1437,7 @@ test_expect_success '.gitmodules ignore=all suppresses unstaged submodule summar On branch main Your branch and '\''upstream'\'' have diverged, and have 2 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) + (use "git pull" if you want to integrate the remote branch with yours) Changes to be committed: (use "git restore --staged <file>..." to unstage) @@ -1558,7 +1557,6 @@ test_expect_success 'git commit --dry-run will show a staged but ignored submodu On branch main Your branch and '\''upstream'\'' have diverged, and have 2 and 2 different commits each, respectively. - (use "git pull" to merge the remote branch into yours) Changes to be committed: (use "git restore --staged <file>..." to unstage) diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh index af79266c58..27b66807cd 100755 --- a/t/t7700-repack.sh +++ b/t/t7700-repack.sh @@ -213,7 +213,7 @@ test_expect_success 'repack --keep-pack' ' test_create_repo keep-pack && ( cd keep-pack && - # avoid producing difference packs to delta/base choices + # avoid producing different packs due to delta/base choices git config pack.window 0 && P1=$(commit_and_pack 1) && P2=$(commit_and_pack 2) && @@ -239,6 +239,10 @@ test_expect_success 'repack --keep-pack' ' mv "$from" "$to" || return 1 done && + # A .idx file without a .pack should not stop us from + # repacking what we can. + touch .git/objects/pack/pack-does-not-exist.idx && + git repack --cruft -d --keep-pack $P1 --keep-pack $P4 && ls .git/objects/pack/*.pack >newer-counts && @@ -247,6 +251,36 @@ test_expect_success 'repack --keep-pack' ' ) ' +test_expect_success 'repacking fails when missing .pack actually means missing objects' ' + test_create_repo idx-without-pack && + ( + cd idx-without-pack && + + # Avoid producing different packs due to delta/base choices + git config pack.window 0 && + P1=$(commit_and_pack 1) && + P2=$(commit_and_pack 2) && + P3=$(commit_and_pack 3) && + P4=$(commit_and_pack 4) && + ls .git/objects/pack/*.pack >old-counts && + test_line_count = 4 old-counts && + + # Remove one .pack file + rm .git/objects/pack/$P2 && + + ls .git/objects/pack/*.pack >before-pack-dir && + + test_must_fail git fsck && + test_must_fail git repack --cruft -d 2>err && + grep "bad object" err && + + # Before failing, the repack did not modify the + # pack directory. + ls .git/objects/pack/*.pack >after-pack-dir && + test_cmp before-pack-dir after-pack-dir + ) +' + test_expect_success 'bitmaps are created by default in bare repos' ' git clone --bare .git bare.git && rm -f bare.git/objects/pack/*.bitmap && @@ -276,7 +276,6 @@ void trace2_cmd_exit_fl(const char *file, int line, int code) if (!trace2_enabled) return; - trace_git_fsync_stats(); trace2_collect_process_info(TRACE2_PROCESS_INFO_EXIT); tr2main_exit_code = code; @@ -541,7 +541,7 @@ void trace2_timer_stop(enum trace2_timer_id tid); * elsewhere as array indexes). * * Any values added to this enum be also be added to the - * `tr2_counter_metadata[]` in `trace2/tr2_tr2_ctr.c`. + * `tr2_counter_metadata[]` in `trace2/tr2_ctr.c`. */ enum trace2_counter_id { /* @@ -552,6 +552,12 @@ enum trace2_counter_id { TRACE2_COUNTER_ID_TEST1 = 0, /* emits summary event only */ TRACE2_COUNTER_ID_TEST2, /* emits summary and thread events */ + TRACE2_COUNTER_ID_PACKED_REFS_JUMPS, /* counts number of jumps */ + + /* counts number of fsyncs */ + TRACE2_COUNTER_ID_FSYNC_WRITEOUT_ONLY, + TRACE2_COUNTER_ID_FSYNC_HARDWARE_FLUSH, + /* Add additional counter definitions before here. */ TRACE2_NUMBER_OF_COUNTERS }; diff --git a/trace2/tr2_ctr.c b/trace2/tr2_ctr.c index b342d3b1a3..87cf9034fb 100644 --- a/trace2/tr2_ctr.c +++ b/trace2/tr2_ctr.c @@ -27,6 +27,21 @@ static struct tr2_counter_metadata tr2_counter_metadata[TRACE2_NUMBER_OF_COUNTER .name = "test2", .want_per_thread_events = 1, }, + [TRACE2_COUNTER_ID_PACKED_REFS_JUMPS] = { + .category = "packed-refs", + .name = "jumps_made", + .want_per_thread_events = 0, + }, + [TRACE2_COUNTER_ID_FSYNC_WRITEOUT_ONLY] = { + .category = "fsync", + .name = "writeout-only", + .want_per_thread_events = 0, + }, + [TRACE2_COUNTER_ID_FSYNC_HARDWARE_FLUSH] = { + .category = "fsync", + .name = "hardware-flush", + .want_per_thread_events = 0, + }, /* Add additional metadata before here. */ }; diff --git a/tree-diff.c b/tree-diff.c index 966946848a..8fc159b86e 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -317,7 +317,7 @@ static void skip_uninteresting(struct tree_desc *t, struct strbuf *base, while (t->size) { match = tree_entry_interesting(opt->repo->index, &t->entry, - base, 0, &opt->pathspec); + base, &opt->pathspec); if (match) { if (match == all_entries_not_interesting) t->size = 0; diff --git a/tree-walk.c b/tree-walk.c index 6c07913f3f..29ead71be1 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -434,7 +434,7 @@ static inline int prune_traversal(struct index_state *istate, if (still_interesting < 0) return still_interesting; return tree_entry_interesting(istate, e, base, - 0, info->pathspec); + info->pathspec); } int traverse_trees(struct index_state *istate, @@ -1015,17 +1015,17 @@ static int match_wildcard_base(const struct pathspec_item *item, /* * Is a tree entry interesting given the pathspec we have? * - * Pre-condition: either baselen == base_offset (i.e. empty path) + * Pre-condition: either baselen == 0 (i.e. empty path) * or base[baselen-1] == '/' (i.e. with trailing slash). */ static enum interesting do_match(struct index_state *istate, const struct name_entry *entry, - struct strbuf *base, int base_offset, + struct strbuf *base, const struct pathspec *ps, int exclude) { int i; - int pathlen, baselen = base->len - base_offset; + int pathlen, baselen = base->len; enum interesting never_interesting = ps->has_wildcard ? entry_not_interesting : all_entries_not_interesting; @@ -1043,7 +1043,7 @@ static enum interesting do_match(struct index_state *istate, !(ps->magic & PATHSPEC_MAXDEPTH) || ps->max_depth == -1) return all_entries_interesting; - return within_depth(base->buf + base_offset, baselen, + return within_depth(base->buf, baselen, !!S_ISDIR(entry->mode), ps->max_depth) ? entry_interesting : entry_not_interesting; @@ -1054,7 +1054,7 @@ static enum interesting do_match(struct index_state *istate, for (i = ps->nr - 1; i >= 0; i--) { const struct pathspec_item *item = ps->items+i; const char *match = item->match; - const char *base_str = base->buf + base_offset; + const char *base_str = base->buf; int matchlen = item->len, matched = 0; if ((!exclude && item->magic & PATHSPEC_EXCLUDE) || @@ -1147,9 +1147,9 @@ match_wildcards: strbuf_add(base, entry->path, pathlen); - if (!git_fnmatch(item, match, base->buf + base_offset, + if (!git_fnmatch(item, match, base->buf, item->nowildcard_len)) { - strbuf_setlen(base, base_offset + baselen); + strbuf_setlen(base, baselen); goto interesting; } @@ -1161,13 +1161,13 @@ match_wildcards: * be performed in the submodule itself. */ if (ps->recurse_submodules && S_ISGITLINK(entry->mode) && - !ps_strncmp(item, match, base->buf + base_offset, + !ps_strncmp(item, match, base->buf, item->nowildcard_len)) { - strbuf_setlen(base, base_offset + baselen); + strbuf_setlen(base, baselen); goto interesting; } - strbuf_setlen(base, base_offset + baselen); + strbuf_setlen(base, baselen); /* * Match all directories. We'll try to match files @@ -1203,9 +1203,9 @@ interesting: return entry_interesting; strbuf_add(base, entry->path, pathlen); - ret = match_pathspec_attrs(istate, base->buf + base_offset, - base->len - base_offset, item); - strbuf_setlen(base, base_offset + baselen); + ret = match_pathspec_attrs(istate, base->buf, + base->len, item); + strbuf_setlen(base, baselen); if (!ret) continue; } @@ -1217,16 +1217,16 @@ interesting: /* * Is a tree entry interesting given the pathspec we have? * - * Pre-condition: either baselen == base_offset (i.e. empty path) + * Pre-condition: either baselen == 0 (i.e. empty path) * or base[baselen-1] == '/' (i.e. with trailing slash). */ enum interesting tree_entry_interesting(struct index_state *istate, const struct name_entry *entry, - struct strbuf *base, int base_offset, + struct strbuf *base, const struct pathspec *ps) { enum interesting positive, negative; - positive = do_match(istate, entry, base, base_offset, ps, 0); + positive = do_match(istate, entry, base, ps, 0); /* * case | entry | positive | negative | result @@ -1263,7 +1263,7 @@ enum interesting tree_entry_interesting(struct index_state *istate, positive <= entry_not_interesting) /* #1, #2, #11, #12 */ return positive; - negative = do_match(istate, entry, base, base_offset, ps, 1); + negative = do_match(istate, entry, base, ps, 1); /* #8, #18 */ if (positive == all_entries_interesting && diff --git a/tree-walk.h b/tree-walk.h index 01a9d8eb44..74cdceb3fe 100644 --- a/tree-walk.h +++ b/tree-walk.h @@ -224,7 +224,7 @@ enum interesting { enum interesting tree_entry_interesting(struct index_state *istate, const struct name_entry *, - struct strbuf *, int, + struct strbuf *, const struct pathspec *ps); #endif @@ -32,7 +32,7 @@ int read_tree_at(struct repository *r, while (tree_entry(&desc, &entry)) { if (retval != all_entries_interesting) { retval = tree_entry_interesting(r->index, &entry, - base, 0, pathspec); + base, pathspec); if (retval == all_entries_not_interesting) break; if (retval == entry_not_interesting) diff --git a/upload-pack.c b/upload-pack.c index 0970392b41..94751477ab 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -68,7 +68,7 @@ struct upload_pack_data { struct object_array have_obj; struct oid_array haves; /* v2 only */ struct string_list wanted_refs; /* v2 only */ - struct string_list hidden_refs; + struct strvec hidden_refs; struct object_array shallows; struct string_list deepen_not; @@ -126,7 +126,7 @@ static void upload_pack_data_init(struct upload_pack_data *data) { struct string_list symref = STRING_LIST_INIT_DUP; struct string_list wanted_refs = STRING_LIST_INIT_DUP; - struct string_list hidden_refs = STRING_LIST_INIT_DUP; + struct strvec hidden_refs = STRVEC_INIT; struct object_array want_obj = OBJECT_ARRAY_INIT; struct object_array have_obj = OBJECT_ARRAY_INIT; struct oid_array haves = OID_ARRAY_INIT; @@ -161,7 +161,7 @@ static void upload_pack_data_clear(struct upload_pack_data *data) { string_list_clear(&data->symref, 1); string_list_clear(&data->wanted_refs, 1); - string_list_clear(&data->hidden_refs, 0); + strvec_clear(&data->hidden_refs); object_array_clear(&data->want_obj); object_array_clear(&data->have_obj); oid_array_clear(&data->haves); @@ -601,11 +601,36 @@ static int get_common_commits(struct upload_pack_data *data, } } +static int allow_hidden_refs(enum allow_uor allow_uor) +{ + if ((allow_uor & ALLOW_ANY_SHA1) == ALLOW_ANY_SHA1) + return 1; + return !(allow_uor & (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1)); +} + +static void for_each_namespaced_ref_1(each_ref_fn fn, + struct upload_pack_data *data) +{ + const char **excludes = NULL; + /* + * If `data->allow_uor` allows fetching hidden refs, we need to + * mark all references (including hidden ones), to check in + * `is_our_ref()` below. + * + * Otherwise, we only care about whether each reference's object + * has the OUR_REF bit set or not, so do not need to visit + * hidden references. + */ + if (allow_hidden_refs(data->allow_uor)) + excludes = hidden_refs_to_excludes(&data->hidden_refs); + + for_each_namespaced_ref(excludes, fn, data); +} + + static int is_our_ref(struct object *o, enum allow_uor allow_uor) { - int allow_hidden_ref = (allow_uor & - (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1)); - return o->flags & ((allow_hidden_ref ? HIDDEN_REF : 0) | OUR_REF); + return o->flags & ((allow_hidden_refs(allow_uor) ? 0 : HIDDEN_REF) | OUR_REF); } /* @@ -854,7 +879,7 @@ static void deepen(struct upload_pack_data *data, int depth) * marked with OUR_REF. */ head_ref_namespaced(check_ref, data); - for_each_namespaced_ref(check_ref, data); + for_each_namespaced_ref_1(check_ref, data); get_reachable_list(data, &reachable_shallows); result = get_shallow_commits(&reachable_shallows, @@ -1169,7 +1194,7 @@ static void receive_needs(struct upload_pack_data *data, /* return non-zero if the ref is hidden, otherwise 0 */ static int mark_our_ref(const char *refname, const char *refname_full, - const struct object_id *oid, const struct string_list *hidden_refs) + const struct object_id *oid, const struct strvec *hidden_refs) { struct object *o = lookup_unknown_object(the_repository, oid); @@ -1391,7 +1416,7 @@ void upload_pack(const int advertise_refs, const int stateless_rpc, if (advertise_refs) data.no_done = 1; head_ref_namespaced(send_ref, &data); - for_each_namespaced_ref(send_ref, &data); + for_each_namespaced_ref_1(send_ref, &data); if (!data.sent_capabilities) { const char *refname = "capabilities^{}"; write_v0_ref(&data, refname, refname, null_oid()); @@ -1405,7 +1430,7 @@ void upload_pack(const int advertise_refs, const int stateless_rpc, packet_flush(1); } else { head_ref_namespaced(check_ref, &data); - for_each_namespaced_ref(check_ref, &data); + for_each_namespaced_ref_1(check_ref, &data); } if (!advertise_refs) { @@ -1470,7 +1495,7 @@ static int parse_want(struct packet_writer *writer, const char *line, static int parse_want_ref(struct packet_writer *writer, const char *line, struct string_list *wanted_refs, - struct string_list *hidden_refs, + struct strvec *hidden_refs, struct object_array *want_obj) { const char *refname_nons; @@ -10,13 +10,10 @@ #include "strbuf.h" #include "trace2.h" -static intmax_t count_fsync_writeout_only; -static intmax_t count_fsync_hardware_flush; - #ifdef HAVE_RTLGENRANDOM /* This is required to get access to RtlGenRandom. */ #define SystemFunction036 NTAPI SystemFunction036 -#include <NTSecAPI.h> +#include <ntsecapi.h> #undef SystemFunction036 #endif @@ -551,7 +548,7 @@ int git_fsync(int fd, enum fsync_action action) { switch (action) { case FSYNC_WRITEOUT_ONLY: - count_fsync_writeout_only += 1; + trace2_counter_add(TRACE2_COUNTER_ID_FSYNC_WRITEOUT_ONLY, 1); #ifdef __APPLE__ /* @@ -583,7 +580,7 @@ int git_fsync(int fd, enum fsync_action action) return -1; case FSYNC_HARDWARE_FLUSH: - count_fsync_hardware_flush += 1; + trace2_counter_add(TRACE2_COUNTER_ID_FSYNC_HARDWARE_FLUSH, 1); /* * On macOS, a special fcntl is required to really flush the @@ -600,18 +597,6 @@ int git_fsync(int fd, enum fsync_action action) } } -static void log_trace_fsync_if(const char *key, intmax_t value) -{ - if (value) - trace2_data_intmax("fsync", the_repository, key, value); -} - -void trace_git_fsync_stats(void) -{ - log_trace_fsync_if("fsync/writeout-only", count_fsync_writeout_only); - log_trace_fsync_if("fsync/hardware-flush", count_fsync_hardware_flush); -} - static int warn_if_unremovable(const char *op, const char *file, int rc) { int err; @@ -88,11 +88,6 @@ enum fsync_action { int git_fsync(int fd, enum fsync_action action); /* - * Writes out trace statistics for fsync using the trace2 API. - */ -void trace_git_fsync_stats(void); - -/* * Preserves errno, prints a message, but gives no warning for ENOENT. * Returns 0 on success, which includes trying to unlink an object that does * not exist. diff --git a/wt-status.c b/wt-status.c index 8a1a4fb1f0..5b1378965c 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1186,7 +1186,8 @@ static void wt_longstatus_print_tracking(struct wt_status *s) t_begin = getnanotime(); - if (!format_tracking_info(branch, &sb, s->ahead_behind_flags)) + if (!format_tracking_info(branch, &sb, s->ahead_behind_flags, + !s->commit_template)) return; if (advice_enabled(ADVICE_STATUS_AHEAD_BEHIND_WARNING) && |
