diff options
195 files changed, 3283 insertions, 1614 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 079645b776..dcf7d78f1d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,9 +5,23 @@ on: [push, pull_request] env: DEVELOPER: 1 +# If more than one workflow run is triggered for the very same commit hash +# (which happens when multiple branches pointing to the same commit), only +# the first one is allowed to run, the second will be kept in the "queued" +# state. This allows a successful completion of the first run to be reused +# in the second run via the `skip-if-redundant` logic in the `config` job. +# +# The only caveat is that if a workflow run is triggered for the same commit +# hash that another run is already being held, that latter run will be +# canceled. For more details about the `concurrency` attribute, see: +# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#concurrency +concurrency: + group: ${{ github.sha }} + jobs: ci-config: name: config + if: vars.CI_BRANCHES == '' || contains(vars.CI_BRANCHES, github.ref_name) runs-on: ubuntu-latest outputs: enabled: ${{ steps.check-ref.outputs.enabled }}${{ steps.skip-if-redundant.outputs.enabled }} @@ -30,10 +44,13 @@ jobs: name: check whether CI is enabled for ref run: | enabled=yes - if test -x config-repo/ci/config/allow-ref && - ! config-repo/ci/config/allow-ref '${{ github.ref }}' + if test -x config-repo/ci/config/allow-ref then - enabled=no + echo "::warning::ci/config/allow-ref is deprecated; use CI_BRANCHES instead" + if ! config-repo/ci/config/allow-ref '${{ github.ref }}' + then + enabled=no + fi fi skip_concurrent=yes diff --git a/Documentation/RelNotes/2.43.0.txt b/Documentation/RelNotes/2.43.0.txt new file mode 100644 index 0000000000..f5b426a9d9 --- /dev/null +++ b/Documentation/RelNotes/2.43.0.txt @@ -0,0 +1,198 @@ +Git v2.43 Release Notes +======================= + +Backward Compatibility Notes + + * The "--rfc" option of "git format-patch" used to be a valid way to + override an earlier "--subject-prefix=<something>" on the command + line and replace it with "[RFC PATCH]", but from this release, it + merely prefixes the string "RFC " in front of the given subject + prefix. If you are negatively affected by this change, please use + "--subject-prefix=PATCH --rfc" as a replacement. + + +UI, Workflows & Features + + * A message written in olden time prevented a branch from getting + checked out saying it is already checked out elsewhere, but these + days, we treat a branch that is being bisected or rebased just like + a branch that is checked out and protect it. Rephrase the message + to say that the branch is in use. + + * Hourly and other schedule of "git maintenance" jobs are randomly + distributed now. + + * "git cmd -h" learned to signal which options can be negated by + listing such options like "--[no-]opt". + + * The way authentication related data other than passwords (e.g. + oath token and password expiration data) are stored in libsecret + keyrings has been rethought. + + * Update two credential helpers to correctly match which credential + to erase; they dropped not the ones with stale password. + + * Git GUI updates. + + * "git format-patch" learns a way to feed cover letter description, + that (1) can be used on detached HEAD where there is no branch + description available, and (2) also can override the branch + description if there is one. + + * Use of --max-pack-size to allow multiple packfiles to be created is + now supported even when we are sending unreachable objects to cruft + packs. + + * "git format-patch --rfc --subject-prefix=<foo>" used to ignore the + "--subject-prefix" option and used "[RFC PATCH]"; now we will add + "RFC" prefix to whatever subject prefix is specified. + + * "git log --format" has been taught the %(decorate) placeholder. + + * The default log message created by "git revert", when reverting a + commit that records a revert, has been tweaked, to encourage people + describe complex "revert of revert of revert" situation better in + their own words. + + * The command-line complation support (in contrib/) learned to + complete "git commit --trailer=" for possible trailer keys. + + + * "git update-index" learns "--show-index-version" to inspect + the index format version used by the on-disk index file. + + +Performance, Internal Implementation, Development Support etc. + + * "git check-attr" has been taught to work better with sparse-index. + + * It may be tempting to leave the help text NULL for a command line + option that is either hidden or too obvious, but "git subcmd -h" + and "git subcmd --help-all" would have segfaulted if done so. Now + the help text is optional. + + * Tests that are known to pass with LSan are now marked as such. + (merge 5fafe8c95f tb/mark-more-tests-as-leak-free later to maint). + + * Flakey "git p4" tests, as well as "git svn" tests, are now skipped + in the (rather expensive) sanitizer CI job. + (merge 6ba913629f js/ci-san-skip-p4-and-svn-tests later to maint). + + * Tests with LSan from time to time seem to emit harmless message + that makes our tests unnecessarily flakey; we work it around by + filtering the uninteresting output. + (merge 370ef7e40d jk/test-lsan-denoise-output later to maint). + + * Unused parameters to functions are marked as such, and/or removed, + in order to bring us closer to -Wunused-parameter clean. + + * The code to keep track of existing packs in the repository while + repacking has been refactored. + + +Fixes since v2.42 +----------------- + + * Overly long label names used in the sequencer machinery are now + chopped to fit under filesystem limitation. + (merge ac300bda10 mp/rebase-label-length-limit later to maint). + + * Scalar updates. + (merge f9a547d3a7 ds/scalar-updates later to maint). + + * Tweak GitHub Actions CI so that pushing the same commit to multiple + branch tips at the same time will not waste building and testing + the same thing twice. + (merge 99fe06cbfd jc/ci-skip-same-commit later to maint). + + * The commit-graph verification code that detects mixture of zero and + non-zero generation numbers has been updated. + (merge db6044d762 tb/commit-graph-verify-fix later to maint). + + * "git diff -w --exit-code" with various options did not work + correctly, which is being addressed. + (merge a64f8b2595 jc/diff-exit-code-with-w-fixes later to maint). + + * transfer.unpackLimit ought to be used as a fallback, but overrode + fetch.unpackLimit and receive.unpackLimit instead. + (merge f3d33f8cfe ts/unpacklimit-config-fix later to maint). + + * The use of API between two calls to require_clean_work_tree() from + the sequencer code has been cleaned up for consistency. + (merge a9b5955e07 ob/sequencer-empty-hint-fix later to maint). + + * "git diff --no-such-option" and other corner cases around the exit + status of the "diff" command has been corrected. + (merge 5cc6b2d70b jk/diff-result-code-cleanup later to maint). + + * "git for-each-ref --sort='contents:size'" sorts the refs according + to size numerically, giving a ref that points at a blob twelve-byte + (12) long before showing a blob hundred-byte (100) long. + (merge 6d79cd8474 ks/ref-filter-sort-numerically later to maint). + + * We now limit depth of the tree objects and maximum length of + pathnames recorded in tree objects. + (merge 4d5693ba05 jk/tree-name-and-depth-limit later to maint). + + * Various fixes to the behaviour of "rebase -i" when the command got + interrupted by conflicting changes. + (merge 203573b024 pw/rebase-i-after-failure later to maint). + + * References from description of the `--patch` option in various + manual pages have been simplified and improved. + (merge 11422f23e3 so/diff-doc-for-patch-update later to maint). + + * "git grep -e A --no-or -e B" is accepted, even though the negation + of "or" did not mean anything, which has been tightened. + (merge aae8558b10 rs/grep-no-no-or later to maint). + + * The completion script (in contrib/) has been taught to treat the + "-t" option to "git checkout" and "git switch" just like the + "--track" option, to complete remote-tracking branches. + (merge 9f892830d6 js/complete-checkout-t later to maint). + + * "git diff --no-index -R <(one) <(two)" did not work correctly, + which has been corrected. + (merge 48944f214c pw/diff-no-index-from-named-pipes later to maint). + + * Update "git maintainance" timers' implementation based on systemd + timers to work with WSL. + (merge 5e8515e8e8 js/systemd-timers-wsl-fix later to maint). + + * "git diff --cached" codepath did not fill the necessary stat + information for a file when fsmonitor knows it is clean and ended + up behaving as if it is not clean, which has been corrected. + (merge 6a044a2048 js/diff-cached-fsmonitor-fix later to maint). + + * Clarify how "alias.foo = : git cmd ; aliased-command-string" should + be spelled with necessary whitespaces around punctuation marks to + work. + (merge 4333267995 pb/completion-aliases-doc later to maint). + + * HTTP Header redaction code has been adjusted for a newer version of + cURL library that shows its traces differently from earlier + versions. + (merge 0763c3a2c4 jk/redact-h2h3-headers-fix later to maint). + + * An error message given by "git send-email" when given a malformed + address did not give correct information, which has been corrected. + (merge 12288cc44e tb/send-email-extract-valid-address-error-message-fix later to maint). + + * Other code cleanup, docfix, build fix, etc. + (merge fd3ba590d8 ws/git-push-doc-grammofix later to maint). + (merge 5f33a843de ds/upload-pack-error-sequence-fix later to maint). + (merge beaa1d952b jk/function-pointer-mismatches-fix later to maint). + (merge b46d806ea5 ob/t9001-indent-fix later to maint). + (merge fdc9914c28 ja/worktree-orphan later to maint). + (merge c2cbefc510 jc/mv-d-to-d-error-message-fix later to maint). + (merge d0fc552bfc ch/t6300-verify-commit-test-cleanup later to maint). + (merge aa4b83dd5e ws/git-svn-retire-faketerm later to maint). + (merge edf80d23f1 jk/ci-retire-allow-ref later to maint). + (merge 256a94ef6c bc/more-git-var later to maint). + (merge 82af2c639c ob/sequencer-reword-error-message later to maint). + (merge 2a63c79dae rs/grep-parseopt-simplify later to maint). + (merge 078c42531e rs/name-rev-use-opt-hidden-bool later to maint). + (merge 63642d58b4 ob/sequencer-remove-dead-code later to maint). + (merge 8aae489756 ob/t3404-typofix later to maint). + (merge 58be11432e eg/config-type-path-docfix later to maint). + (merge 563f339d98 ch/clean-docfix later to maint). diff --git a/Documentation/config/core.txt b/Documentation/config/core.txt index dfbdaf00b8..0e8c2832bf 100644 --- a/Documentation/config/core.txt +++ b/Documentation/config/core.txt @@ -736,3 +736,9 @@ core.abbrev:: If set to "no", no abbreviation is made and the object names are shown in their full length. The minimum length is 4. + +core.maxTreeDepth:: + The maximum depth Git is willing to recurse while traversing a + tree (e.g., "a/b/cde/f" has a depth of 4). This is a fail-safe + to allow Git to abort cleanly, and should not generally need to + be adjusted. The default is 4096. diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt index 35a7bf86d7..9391c77e55 100644 --- a/Documentation/config/diff.txt +++ b/Documentation/config/diff.txt @@ -52,6 +52,10 @@ directories with less than 10% of the total amount of changed files, and accumulating child directory counts in the parent directories: `files,10,cumulative`. +diff.statNameWidth:: + Limit the width of the filename part in --stat output. If set, applies + to all commands generating --stat output except format-patch. + diff.statGraphWidth:: Limit the width of the graph part in --stat output. If set, applies to all commands generating --stat output except format-patch. diff --git a/Documentation/config/rebase.txt b/Documentation/config/rebase.txt index afaf6dad99..9c248accec 100644 --- a/Documentation/config/rebase.txt +++ b/Documentation/config/rebase.txt @@ -77,3 +77,9 @@ rebase.rebaseMerges:: equivalent to `--no-rebase-merges`. Passing `--rebase-merges` on the command line, with or without an argument, overrides any `rebase.rebaseMerges` configuration. + +rebase.maxLabelLength:: + When generating label names from commit subjects, truncate the names to + this length. By default, the names are truncated to a little less than + `NAME_MAX` (to allow e.g. `.lock` files to be written for the + corresponding loose refs). diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index 9f33f88771..35fae7c87c 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -22,13 +22,7 @@ ifndef::git-format-patch[] -p:: -u:: --patch:: - Generate patch (see section titled -ifdef::git-log[] -<<generate_patch_text_with_p, "Generating patch text with -p">>). -endif::git-log[] -ifndef::git-log[] -"Generating patch text with -p"). -endif::git-log[] + Generate patch (see <<generate_patch_text_with_p>>). ifdef::git-diff[] This is the default. endif::git-diff[] @@ -210,14 +204,15 @@ have to use `--diff-algorithm=default` option. part. Maximum width defaults to terminal width, or 80 columns if not connected to a terminal, and can be overridden by `<width>`. The width of the filename part can be limited by - giving another width `<name-width>` after a comma. The width - of the graph part can be limited by using - `--stat-graph-width=<width>` (affects all commands generating - a stat graph) or by setting `diff.statGraphWidth=<width>` - (does not affect `git format-patch`). - By giving a third parameter `<count>`, you can limit the - output to the first `<count>` lines, followed by `...` if - there are more. + giving another width `<name-width>` after a comma or by setting + `diff.statNameWidth=<width>`. The width of the graph part can be + limited by using `--stat-graph-width=<width>` or by setting + `diff.statGraphWidth=<width>`. Using `--stat` or + `--stat-graph-width` affects all commands generating a stat graph, + while setting `diff.statNameWidth` or `diff.statGraphWidth` + does not affect `git format-patch`. + By giving a third parameter `<count>`, you can limit the output to + the first `<count>` lines, followed by `...` if there are more. + These parameters can also be set individually with `--stat-width=<width>`, `--stat-name-width=<name-width>` and `--stat-count=<count>`. diff --git a/Documentation/fsck-msgids.txt b/Documentation/fsck-msgids.txt index 12eae8a222..09b0aecbf8 100644 --- a/Documentation/fsck-msgids.txt +++ b/Documentation/fsck-msgids.txt @@ -103,6 +103,13 @@ `hasDotgit`:: (WARN) A tree contains an entry named `.git`. +`largePathname`:: + (WARN) A tree contains an entry with a very long path name. If + the value of `fsck.largePathname` contains a colon, that value + is used as the maximum allowable length (e.g., "warn:10" would + complain about any path component of 11 or more bytes). The + default value is 4096. + `mailmapSymlink`:: (INFO) `.mailmap` is a symlink. diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt index 160d08b86b..5e1a3d5148 100644 --- a/Documentation/git-clean.txt +++ b/Documentation/git-clean.txt @@ -127,7 +127,7 @@ ask each:: quit:: - This lets you quit without do cleaning. + This lets you quit without doing any cleaning. help:: diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index 7a2bcb2f6c..b1caac887a 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -201,7 +201,7 @@ Valid `<type>`'s include: 1073741824 upon input. - 'bool-or-int': canonicalize according to either 'bool' or 'int', as described above. -- 'path': canonicalize by adding a leading `~` to the value of `$HOME` and +- 'path': canonicalize by expanding a leading `~` to the value of `$HOME` and `~user` to the home directory for the specified user. This specifier has no effect when setting the value (but you can use `git config section.variable ~/` from the command line to let your shell do the expansion.) diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt index 373b46fc0d..711823a7f4 100644 --- a/Documentation/git-format-patch.txt +++ b/Documentation/git-format-patch.txt @@ -215,11 +215,21 @@ is greater than 100 bytes, then the mode will be `message`, otherwise If `<mode>` is `none`, both the cover letter subject and body will be populated with placeholder text. +--description-file=<file>:: + Use the contents of <file> instead of the branch's description + for generating the cover letter. + --subject-prefix=<subject prefix>:: Instead of the standard '[PATCH]' prefix in the subject - line, instead use '[<subject prefix>]'. This - allows for useful naming of a patch series, and can be - combined with the `--numbered` option. + line, instead use '[<subject prefix>]'. This can be used + to name a patch series, and can be combined with the + `--numbered` option. ++ +The configuration variable `format.subjectPrefix` may also be used +to configure a subject prefix to apply to a given repository for +all patches. This is often useful on mailing lists which receive +patches for several repositories and can be used to disambiguate +the patches (with a value of e.g. "PATCH my-project"). --filename-max-length=<n>:: Instead of the standard 64 bytes, chomp the generated output @@ -229,9 +239,9 @@ populated with placeholder text. variable, or 64 if unconfigured. --rfc:: - Alias for `--subject-prefix="RFC PATCH"`. RFC means "Request For - Comments"; use this when sending an experimental patch for - discussion rather than application. + Prepends "RFC" to the subject prefix (producing "RFC PATCH" by + default). RFC means "Request For Comments"; use this when sending + an experimental patch for discussion rather than application. -v <n>:: --reroll-count=<n>:: diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt index a9995a932c..dea7eacb0f 100644 --- a/Documentation/git-pack-objects.txt +++ b/Documentation/git-pack-objects.txt @@ -116,9 +116,7 @@ unreachable object whose mtime is newer than the `--cruft-expiration`). + Incompatible with `--unpack-unreachable`, `--keep-unreachable`, `--pack-loose-unreachable`, `--stdin-packs`, as well as any other -options which imply `--revs`. Also incompatible with `--max-pack-size`; -when this option is set, the maximum pack size is not inferred from -`pack.packSizeLimit`. +options which imply `--revs`. --cruft-expiration=<approxidate>:: If specified, objects are eliminated from the cruft pack if they diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 297927d866..5b4edaf4a8 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -37,7 +37,7 @@ the default `<refspec>` by consulting `remote.*.push` configuration, and if it is not found, honors `push.default` configuration to decide what to push (See linkgit:git-config[1] for the meaning of `push.default`). -When neither the command-line nor the configuration specify what to +When neither the command-line nor the configuration specifies what to push, the default behavior is used, which corresponds to the `simple` value for `push.default`: the current branch is pushed to the corresponding upstream branch, but as a safety measure, the push is diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index f26a7591e3..6a4968f68a 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -398,7 +398,7 @@ some-command [<options>] <args>... some-command does foo and bar! -- -h,help show the help +h,help! show the help foo some nifty option --foo bar= some cool option --bar with an argument @@ -424,10 +424,10 @@ usage: some-command [<options>] <args>... some-command does foo and bar! -h, --help show the help - --foo some nifty option --foo - --bar ... some cool option --bar with an argument - --baz <arg> another cool option --baz with a named argument - --qux[=<path>] qux may take a path argument but has meaning by itself + --[no-]foo some nifty option --foo + --[no-]bar ... some cool option --bar with an argument + --[no-]baz <arg> another cool option --baz with a named argument + --[no-]qux[=<path>] qux may take a path argument but has meaning by itself An option group Header -C[...] option C with an optional argument diff --git a/Documentation/git-revert.txt b/Documentation/git-revert.txt index d2e10d3dce..cbe0208834 100644 --- a/Documentation/git-revert.txt +++ b/Documentation/git-revert.txt @@ -142,6 +142,16 @@ EXAMPLES changes. The revert only modifies the working tree and the index. +DISCUSSION +---------- + +While git creates a basic commit message automatically, it is +_strongly_ recommended to explain why the original commit is being +reverted. +In addition, repeatedly reverting reverts will result in increasingly +unwieldy subject lines, for example 'Reapply "Reapply "<original subject>""'. +Please consider rewording these to be shorter and more unique. + CONFIGURATION ------------- diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index f4bb9c5daf..1271486ae9 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -162,13 +162,19 @@ you will need to handle the situation manually. Write the resulting index out in the named on-disk format version. Supported versions are 2, 3 and 4. The current default version is 2 or 3, depending on whether extra features are used, such as - `git add -N`. + `git add -N`. With `--verbose`, also report the version the index + file uses before and after this command. + Version 4 performs a simple pathname compression that reduces index size by 30%-50% on large repositories, which results in faster load -time. Version 4 is relatively young (first released in 1.8.0 in -October 2012). Other Git implementations such as JGit and libgit2 -may not support it yet. +time. Git supports it since version 1.8.0, released in October 2012, +and support for it was added to libgit2 in 2016 and to JGit in 2020. +Older versions of this manual page called it "relatively young", but +it should be considered mature technology these days. + +--show-index-version:: + Report the index format version used by the on-disk index file. + See `--index-version` above. -z:: Only meaningful with `--stdin` or `--index-info`; paths are diff --git a/Documentation/gitformat-pack.txt b/Documentation/gitformat-pack.txt index 0c1be2dbe8..870e00f298 100644 --- a/Documentation/gitformat-pack.txt +++ b/Documentation/gitformat-pack.txt @@ -588,51 +588,17 @@ later on. It is linkgit:git-gc[1] that is typically responsible for removing expired unreachable objects. -=== Caution for mixed-version environments - -Repositories that have cruft packs in them will continue to work with any older -version of Git. Note, however, that previous versions of Git which do not -understand the `.mtimes` file will use the cruft pack's mtime as the mtime for -all of the objects in it. In other words, do not expect older (pre-cruft pack) -versions of Git to interpret or even read the contents of the `.mtimes` file. - -Note that having mixed versions of Git GC-ing the same repository can lead to -unreachable objects never being completely pruned. This can happen under the -following circumstances: - - - An older version of Git running GC explodes the contents of an existing - cruft pack loose, using the cruft pack's mtime. - - A newer version running GC collects those loose objects into a cruft pack, - where the .mtime file reflects the loose object's actual mtimes, but the - cruft pack mtime is "now". - -Repeating this process will lead to unreachable objects not getting pruned as a -result of repeatedly resetting the objects' mtimes to the present time. - -If you are GC-ing repositories in a mixed version environment, consider omitting -the `--cruft` option when using linkgit:git-repack[1] and linkgit:git-gc[1], and -setting the `gc.cruftPacks` configuration to "false" until all writers -understand cruft packs. - === Alternatives Notable alternatives to this design include: - - The location of the per-object mtime data, and - - Storing unreachable objects in multiple cruft packs. + - The location of the per-object mtime data. On the location of mtime data, a new auxiliary file tied to the pack was chosen to avoid complicating the `.idx` format. If the `.idx` format were ever to gain support for optional chunks of data, it may make sense to consolidate the `.mtimes` format into the `.idx` itself. -Storing unreachable objects among multiple cruft packs (e.g., creating a new -cruft pack during each repacking operation including only unreachable objects -which aren't already stored in an earlier cruft pack) is significantly more -complicated to construct, and so aren't pursued here. The obvious drawback to -the current implementation is that the entire cruft pack must be re-written from -scratch. - GIT --- Part of the linkgit:git[1] suite diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index 3b71334459..d38b4ab566 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -122,7 +122,9 @@ The placeholders are: - Placeholders that expand to a single literal character: '%n':: newline '%%':: a raw '%' -'%x00':: print a byte from a hex code +'%x00':: '%x' followed by two hexadecimal digits is replaced with a + byte with the hexadecimal digits' value (we will call this + "literal formatting code" in the rest of this document). - Placeholders that affect formatting of later placeholders: '%Cred':: switch color to red @@ -222,13 +224,30 @@ The placeholders are: linkgit:git-rev-list[1]) '%d':: ref names, like the --decorate option of linkgit:git-log[1] '%D':: ref names without the " (", ")" wrapping. -'%(describe[:options])':: human-readable name, like - linkgit:git-describe[1]; empty string for - undescribable commits. The `describe` string - may be followed by a colon and zero or more - comma-separated options. Descriptions can be - inconsistent when tags are added or removed at - the same time. +'%(decorate[:<options>])':: +ref names with custom decorations. The `decorate` string may be followed by a +colon and zero or more comma-separated options. Option values may contain +literal formatting codes. These must be used for commas (`%x2C`) and closing +parentheses (`%x29`), due to their role in the option syntax. ++ +** 'prefix=<value>': Shown before the list of ref names. Defaults to "{nbsp}`(`". +** 'suffix=<value>': Shown after the list of ref names. Defaults to "`)`". +** 'separator=<value>': Shown between ref names. Defaults to "`,`{nbsp}". +** 'pointer=<value>': Shown between HEAD and the branch it points to, if any. + Defaults to "{nbsp}`->`{nbsp}". +** 'tag=<value>': Shown before tag names. Defaults to "`tag:`{nbsp}". + ++ +For example, to produce decorations with no wrapping +or tag annotations, and spaces as separators: ++ +`%(decorate:prefix=,suffix=,tag=,separator= )` + +'%(describe[:<options>])':: +human-readable name, like linkgit:git-describe[1]; empty string for +undescribable commits. The `describe` string may be followed by a colon and +zero or more comma-separated options. Descriptions can be inconsistent when +tags are added or removed at the same time. + ** 'tags[=<bool-value>]': Instead of only considering annotated tags, consider lightweight tags as well. @@ -281,13 +300,11 @@ endif::git-rev-list[] '%gE':: reflog identity email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1]) '%gs':: reflog subject -'%(trailers[:options])':: display the trailers of the body as - interpreted by - linkgit:git-interpret-trailers[1]. The - `trailers` string may be followed by a colon - and zero or more comma-separated options. - If any option is provided multiple times the - last occurrence wins. +'%(trailers[:<options>])':: +display the trailers of the body as interpreted by +linkgit:git-interpret-trailers[1]. The `trailers` string may be followed by +a colon and zero or more comma-separated options. If any option is provided +multiple times, the last occurrence wins. + ** 'key=<key>': only show trailers with specified <key>. Matching is done case-insensitively and trailing colon is optional. If option is diff --git a/Documentation/scalar.txt b/Documentation/scalar.txt index f33436c7f6..361f51a647 100644 --- a/Documentation/scalar.txt +++ b/Documentation/scalar.txt @@ -8,7 +8,8 @@ scalar - A tool for managing large Git repositories SYNOPSIS -------- [verse] -scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<enlistment>] +scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] + [--[no-]src] <url> [<enlistment>] scalar list scalar register [<enlistment>] scalar unregister [<enlistment>] @@ -80,6 +81,11 @@ remote-tracking branch for the branch this option was used for the initial cloning. If the HEAD at the remote did not point at any branch when `--single-branch` clone was made, no remote-tracking branch is created. +--[no-]src:: + By default, `scalar clone` places the cloned repository within a + `<entlistment>/src` directory. Use `--no-src` to place the cloned + repository directly in the `<enlistment>` directory. + --[no-]full-clone:: A sparse-checkout is initialized by default. This behavior can be turned off via `--full-clone`. diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 2c8dae398f..9f0307d364 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v2.42.0 +DEF_VER=v2.42.GIT LF=' ' @@ -808,7 +808,6 @@ TEST_BUILTINS_OBJS += test-hash-speed.o TEST_BUILTINS_OBJS += test-hash.o TEST_BUILTINS_OBJS += test-hashmap.o TEST_BUILTINS_OBJS += test-hexdump.o -TEST_BUILTINS_OBJS += test-index-version.o TEST_BUILTINS_OBJS += test-json-writer.o TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o TEST_BUILTINS_OBJS += test-match-trees.o @@ -1 +1 @@ -Documentation/RelNotes/2.42.0.txt
\ No newline at end of file +Documentation/RelNotes/2.43.0.txt
\ No newline at end of file diff --git a/add-interactive.c b/add-interactive.c index add9a1ad43..6bf87e7ae7 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -569,7 +569,7 @@ static int get_modified_files(struct repository *r, copy_pathspec(&rev.prune_data, ps); if (s.mode == FROM_INDEX) - run_diff_index(&rev, 1); + run_diff_index(&rev, DIFF_INDEX_CACHED); else { rev.diffopt.flags.ignore_dirty_submodules = 1; run_diff_files(&rev, 0); @@ -1021,9 +1021,9 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps, return res; } -static int run_help(struct add_i_state *s, const struct pathspec *unused_ps, - struct prefix_item_list *unused_files, - struct list_and_choose_options *unused_opts) +static int run_help(struct add_i_state *s, const struct pathspec *ps UNUSED, + struct prefix_item_list *files UNUSED, + struct list_and_choose_options *opts UNUSED) { color_fprintf_ln(stdout, s->help_color, "status - %s", _("show paths with changes")); @@ -1074,7 +1074,7 @@ struct print_command_item_data { const char *color, *reset; }; -static void print_command_item(int i, int selected, +static void print_command_item(int i, int selected UNUSED, struct string_list_item *item, void *print_command_item_data) { @@ -807,35 +807,56 @@ static struct attr_stack *read_attr_from_blob(struct index_state *istate, static struct attr_stack *read_attr_from_index(struct index_state *istate, const char *path, unsigned flags) { + struct attr_stack *stack = NULL; char *buf; unsigned long size; + int sparse_dir_pos = -1; if (!istate) return NULL; /* - * The .gitattributes file only applies to files within its - * parent directory. In the case of cone-mode sparse-checkout, - * the .gitattributes file is sparse if and only if all paths - * within that directory are also sparse. Thus, don't load the - * .gitattributes file since it will not matter. - * - * In the case of a sparse index, it is critical that we don't go - * looking for a .gitattributes file, as doing so would cause the - * index to expand. + * When handling sparse-checkouts, .gitattributes files + * may reside within a sparse directory. We distinguish + * whether a path exists directly in the index or not by + * evaluating if 'pos' is negative. + * If 'pos' is negative, the path is not directly present + * in the index and is likely within a sparse directory. + * For paths not in the index, The absolute value of 'pos' + * minus 1 gives us the position where the path would be + * inserted in lexicographic order within the index. + * We then subtract another 1 from this value + * (sparse_dir_pos = -pos - 2) to find the position of the + * last index entry which is lexicographically smaller than + * the path. This would be the sparse directory containing + * the path. By identifying the sparse directory containing + * the path, we can correctly read the attributes specified + * in the .gitattributes file from the tree object of the + * sparse directory. */ - if (!path_in_cone_mode_sparse_checkout(path, istate)) - return NULL; + if (!path_in_cone_mode_sparse_checkout(path, istate)) { + int pos = index_name_pos_sparse(istate, path, strlen(path)); - buf = read_blob_data_from_index(istate, path, &size); - if (!buf) - return NULL; - if (size >= ATTR_MAX_FILE_SIZE) { - warning(_("ignoring overly large gitattributes blob '%s'"), path); - return NULL; + if (pos < 0) + sparse_dir_pos = -pos - 2; } - return read_attr_from_buf(buf, path, flags); + if (sparse_dir_pos >= 0 && + S_ISSPARSEDIR(istate->cache[sparse_dir_pos]->ce_mode) && + !strncmp(istate->cache[sparse_dir_pos]->name, path, ce_namelen(istate->cache[sparse_dir_pos]))) { + const char *relative_path = path + ce_namelen(istate->cache[sparse_dir_pos]); + stack = read_attr_from_blob(istate, &istate->cache[sparse_dir_pos]->oid, relative_path, flags); + } else { + buf = read_blob_data_from_index(istate, path, &size); + if (!buf) + return NULL; + if (size >= ATTR_MAX_FILE_SIZE) { + warning(_("ignoring overly large gitattributes blob '%s'"), path); + return NULL; + } + stack = read_attr_from_buf(buf, path, flags); + } + return stack; } static struct attr_stack *read_attr(struct index_state *istate, @@ -838,7 +838,7 @@ void die_if_checked_out(const char *branch, int ignore_current_worktree) if (is_shared_symref(worktrees[i], "HEAD", branch)) { skip_prefix(branch, "refs/heads/", &branch); - die(_("'%s' is already checked out at '%s'"), + die(_("'%s' is already used by worktree at '%s'"), branch, worktrees[i]->path); } } diff --git a/builtin/add.c b/builtin/add.c index 4b0dd798df..c27254a5cd 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -194,8 +194,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix) out = xopen(file, O_CREAT | O_WRONLY | O_TRUNC, 0666); rev.diffopt.file = xfdopen(out, "w"); rev.diffopt.close_file = 1; - if (run_diff_files(&rev, 0)) - die(_("Could not write patch")); + run_diff_files(&rev, 0); if (launch_editor(file, NULL, NULL)) die(_("editing patch failed")); @@ -232,6 +231,8 @@ static char *chmod_arg; static int ignore_removal_cb(const struct option *opt, const char *arg, int unset) { + BUG_ON_OPT_ARG(arg); + /* if we are told to ignore, we are not adding removals */ *(int *)opt->value = !unset ? 0 : 1; return 0; diff --git a/builtin/am.c b/builtin/am.c index 8bde034fae..202040b62e 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1430,7 +1430,7 @@ static void write_index_patch(const struct am_state *state) rev_info.diffopt.close_file = 1; add_pending_object(&rev_info, &tree->object, ""); diff_setup_done(&rev_info.diffopt); - run_diff_index(&rev_info, 1); + run_diff_index(&rev_info, DIFF_INDEX_CACHED); release_revisions(&rev_info); } @@ -1593,7 +1593,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa rev_info.diffopt.filter |= diff_filter_bit('M'); add_pending_oid(&rev_info, "HEAD", &our_tree, 0); diff_setup_done(&rev_info.diffopt); - run_diff_index(&rev_info, 1); + run_diff_index(&rev_info, DIFF_INDEX_CACHED); release_revisions(&rev_info); } diff --git a/builtin/branch.c b/builtin/branch.c index 08da650516..2ec190b14a 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -261,7 +261,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, const char *path; if ((path = branch_checked_out(name))) { error(_("Cannot delete branch '%s' " - "checked out at '%s'"), + "used by worktree at '%s'"), bname.buf, path); ret = 1; continue; diff --git a/builtin/check-attr.c b/builtin/check-attr.c index b22ff748c3..c1da1d184e 100644 --- a/builtin/check-attr.c +++ b/builtin/check-attr.c @@ -122,6 +122,9 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, check_attr_options, check_attr_usage, PARSE_OPT_KEEP_DASHDASH); + prepare_repo_settings(the_repository); + the_repository->settings.command_requires_full_index = 0; + if (repo_read_index(the_repository) < 0) { die("invalid cache"); } diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c index f62f13f2b5..3b68b47615 100644 --- a/builtin/checkout-index.c +++ b/builtin/checkout-index.c @@ -24,7 +24,7 @@ static int nul_term_line; static int checkout_stage; /* default to checkout stage0 */ static int ignore_skip_worktree; /* default to 0 */ -static int to_tempfile; +static int to_tempfile = -1; static char topath[4][TEMPORARY_FILENAME_LENGTH + 1]; static struct checkout state = CHECKOUT_INIT; @@ -193,15 +193,16 @@ static const char * const builtin_checkout_index_usage[] = { static int option_parse_stage(const struct option *opt, const char *arg, int unset) { + int *stage = opt->value; + BUG_ON_OPT_NEG(unset); if (!strcmp(arg, "all")) { - to_tempfile = 1; - checkout_stage = CHECKOUT_ALL; + *stage = CHECKOUT_ALL; } else { int ch = arg[0]; if ('1' <= ch && ch <= '3') - checkout_stage = arg[0] - '0'; + *stage = arg[0] - '0'; else die(_("stage should be between 1 and 3 or all")); } @@ -239,7 +240,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) N_("write the content to temporary files")), OPT_STRING(0, "prefix", &state.base_dir, N_("string"), N_("when creating files, prepend <string>")), - OPT_CALLBACK_F(0, "stage", NULL, "(1|2|3|all)", + OPT_CALLBACK_F(0, "stage", &checkout_stage, "(1|2|3|all)", N_("copy out the files from named stage"), PARSE_OPT_NONEG, option_parse_stage), OPT_END() @@ -269,6 +270,12 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix) state.base_dir = ""; state.base_dir_len = strlen(state.base_dir); + if (to_tempfile < 0) + to_tempfile = (checkout_stage == CHECKOUT_ALL); + if (!to_tempfile && checkout_stage == CHECKOUT_ALL) + die(_("options '%s' and '%s' cannot be used together"), + "--stage=all", "--no-temp"); + /* * when --prefix is specified we do not want to update cache. */ diff --git a/builtin/describe.c b/builtin/describe.c index b28a4a1f82..fb6b0508f3 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -561,9 +561,11 @@ static void describe(const char *arg, int last_one) static int option_parse_exact_match(const struct option *opt, const char *arg, int unset) { + int *val = opt->value; + BUG_ON_OPT_ARG(arg); - max_candidates = unset ? DEFAULT_CANDIDATES : 0; + *val = unset ? DEFAULT_CANDIDATES : 0; return 0; } @@ -578,7 +580,7 @@ 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_CALLBACK_F(0, "exact-match", NULL, NULL, + OPT_CALLBACK_F(0, "exact-match", &max_candidates, NULL, N_("only output exact matches"), PARSE_OPT_NOARG, option_parse_exact_match), OPT_INTEGER(0, "candidates", &max_candidates, @@ -668,7 +670,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix) struct lock_file index_lock = LOCK_INIT; struct rev_info revs; struct strvec args = STRVEC_INIT; - int fd, result; + int fd; setup_work_tree(); prepare_repo_settings(the_repository); @@ -685,9 +687,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix) strvec_pushv(&args, diff_index_args); if (setup_revisions(args.nr, args.v, &revs, NULL) != 1) BUG("malformed internal diff-index command line"); - result = run_diff_index(&revs, 0); + run_diff_index(&revs, 0); - if (!diff_result_code(&revs.diffopt, result)) + if (!diff_result_code(&revs.diffopt)) suffix = NULL; else suffix = dirty; diff --git a/builtin/diff-files.c b/builtin/diff-files.c index 50330b8dd2..f38912cd40 100644 --- a/builtin/diff-files.c +++ b/builtin/diff-files.c @@ -80,14 +80,10 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix) (rev.diffopt.output_format & DIFF_FORMAT_PATCH)) diff_merges_set_dense_combined_if_unset(&rev); - if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) { - perror("repo_read_index_preload"); - result = -1; - goto cleanup; - } - result = run_diff_files(&rev, options); - result = diff_result_code(&rev.diffopt, result); -cleanup: + if (repo_read_index_preload(the_repository, &rev.diffopt.pathspec, 0) < 0) + die_errno("repo_read_index_preload"); + run_diff_files(&rev, options); + result = diff_result_code(&rev.diffopt); release_revisions(&rev); return result; } diff --git a/builtin/diff-index.c b/builtin/diff-index.c index 9db7139b83..220f341ffa 100644 --- a/builtin/diff-index.c +++ b/builtin/diff-index.c @@ -72,8 +72,8 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix) perror("repo_read_index"); return -1; } - result = run_diff_index(&rev, option); - result = diff_result_code(&rev.diffopt, result); + run_diff_index(&rev, option); + result = diff_result_code(&rev.diffopt); release_revisions(&rev); return result; } diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index c9ba35f143..86be634286 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -232,5 +232,5 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix) diff_free(&opt->diffopt); } - return diff_result_code(&opt->diffopt, 0); + return diff_result_code(&opt->diffopt); } diff --git a/builtin/diff.c b/builtin/diff.c index b19530c996..c0f564273a 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -77,9 +77,9 @@ static void stuff_change(struct diff_options *opt, diff_queue(&diff_queued_diff, one, two); } -static int builtin_diff_b_f(struct rev_info *revs, - int argc, const char **argv UNUSED, - struct object_array_entry **blob) +static void builtin_diff_b_f(struct rev_info *revs, + int argc, const char **argv UNUSED, + struct object_array_entry **blob) { /* Blob vs file in the working tree*/ struct stat st; @@ -109,12 +109,11 @@ static int builtin_diff_b_f(struct rev_info *revs, path); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); - return 0; } -static int builtin_diff_blobs(struct rev_info *revs, - int argc, const char **argv UNUSED, - struct object_array_entry **blob) +static void builtin_diff_blobs(struct rev_info *revs, + int argc, const char **argv UNUSED, + struct object_array_entry **blob) { const unsigned mode = canon_mode(S_IFREG | 0644); @@ -134,11 +133,10 @@ static int builtin_diff_blobs(struct rev_info *revs, blob_path(blob[0]), blob_path(blob[1])); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); - return 0; } -static int builtin_diff_index(struct rev_info *revs, - int argc, const char **argv) +static void builtin_diff_index(struct rev_info *revs, + int argc, const char **argv) { unsigned int option = 0; while (1 < argc) { @@ -163,20 +161,18 @@ static int builtin_diff_index(struct rev_info *revs, setup_work_tree(); if (repo_read_index_preload(the_repository, &revs->diffopt.pathspec, 0) < 0) { - perror("repo_read_index_preload"); - return -1; + die_errno("repo_read_index_preload"); } } else if (repo_read_index(the_repository) < 0) { - perror("repo_read_cache"); - return -1; + die_errno("repo_read_cache"); } - return run_diff_index(revs, option); + run_diff_index(revs, option); } -static int builtin_diff_tree(struct rev_info *revs, - int argc, const char **argv, - struct object_array_entry *ent0, - struct object_array_entry *ent1) +static void builtin_diff_tree(struct rev_info *revs, + int argc, const char **argv, + struct object_array_entry *ent0, + struct object_array_entry *ent1) { const struct object_id *(oid[2]); struct object_id mb_oid; @@ -209,13 +205,12 @@ static int builtin_diff_tree(struct rev_info *revs, } diff_tree_oid(oid[0], oid[1], "", &revs->diffopt); log_tree_diff_flush(revs); - return 0; } -static int builtin_diff_combined(struct rev_info *revs, - int argc, const char **argv UNUSED, - struct object_array_entry *ent, - int ents, int first_non_parent) +static void builtin_diff_combined(struct rev_info *revs, + int argc, const char **argv UNUSED, + struct object_array_entry *ent, + int ents, int first_non_parent) { struct oid_array parents = OID_ARRAY_INIT; int i; @@ -236,7 +231,6 @@ static int builtin_diff_combined(struct rev_info *revs, } diff_tree_combined(&ent[first_non_parent].item->oid, &parents, revs); oid_array_clear(&parents); - return 0; } static void refresh_index_quietly(void) @@ -254,7 +248,7 @@ static void refresh_index_quietly(void) repo_update_index_if_able(the_repository, &lock_file); } -static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv) +static void builtin_diff_files(struct rev_info *revs, int argc, const char **argv) { unsigned int options = 0; @@ -269,8 +263,10 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv options |= DIFF_SILENT_ON_REMOVED; else if (!strcmp(argv[1], "-h")) usage(builtin_diff_usage); - else - return error(_("invalid option: %s"), argv[1]); + else { + error(_("invalid option: %s"), argv[1]); + usage(builtin_diff_usage); + } argv++; argc--; } @@ -287,10 +283,9 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv setup_work_tree(); if (repo_read_index_preload(the_repository, &revs->diffopt.pathspec, 0) < 0) { - perror("repo_read_index_preload"); - return -1; + die_errno("repo_read_index_preload"); } - return run_diff_files(revs, options); + run_diff_files(revs, options); } struct symdiff { @@ -404,7 +399,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) int blobs = 0, paths = 0; struct object_array_entry *blob[2]; int nongit = 0, no_index = 0; - int result = 0; + int result; struct symdiff sdiff; /* @@ -480,6 +475,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) /* Set up defaults that will apply to both no-index and regular diffs. */ rev.diffopt.stat_width = -1; + rev.diffopt.stat_name_width = -1; rev.diffopt.stat_graph_width = -1; rev.diffopt.flags.allow_external = 1; rev.diffopt.flags.allow_textconv = 1; @@ -583,17 +579,17 @@ int cmd_diff(int argc, const char **argv, const char *prefix) if (!ent.nr) { switch (blobs) { case 0: - result = builtin_diff_files(&rev, argc, argv); + builtin_diff_files(&rev, argc, argv); break; case 1: if (paths != 1) usage(builtin_diff_usage); - result = builtin_diff_b_f(&rev, argc, argv, blob); + builtin_diff_b_f(&rev, argc, argv, blob); break; case 2: if (paths) usage(builtin_diff_usage); - result = builtin_diff_blobs(&rev, argc, argv, blob); + builtin_diff_blobs(&rev, argc, argv, blob); break; default: usage(builtin_diff_usage); @@ -602,18 +598,18 @@ int cmd_diff(int argc, const char **argv, const char *prefix) else if (blobs) usage(builtin_diff_usage); else if (ent.nr == 1) - result = builtin_diff_index(&rev, argc, argv); + builtin_diff_index(&rev, argc, argv); else if (ent.nr == 2) { if (sdiff.warn) warning(_("%s...%s: multiple merge bases, using %s"), sdiff.left, sdiff.right, sdiff.base); - result = builtin_diff_tree(&rev, argc, argv, - &ent.objects[0], &ent.objects[1]); + builtin_diff_tree(&rev, argc, argv, + &ent.objects[0], &ent.objects[1]); } else - result = builtin_diff_combined(&rev, argc, argv, - ent.objects, ent.nr, - first_non_parent); - result = diff_result_code(&rev.diffopt, result); + builtin_diff_combined(&rev, argc, argv, + ent.objects, ent.nr, + first_non_parent); + result = diff_result_code(&rev.diffopt); if (1 < rev.diffopt.skip_stat_unmatch) refresh_index_quietly(); release_revisions(&rev); diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 56dc69fac1..70aff515ac 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -33,9 +33,9 @@ static const char *fast_export_usage[] = { }; static int progress; -static enum { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT; -static enum { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT; -static enum { REENCODE_ABORT, REENCODE_YES, REENCODE_NO } reencode_mode = REENCODE_ABORT; +static enum signed_tag_mode { SIGNED_TAG_ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = SIGNED_TAG_ABORT; +static enum tag_of_filtered_mode { TAG_FILTERING_ABORT, DROP, REWRITE } tag_of_filtered_mode = TAG_FILTERING_ABORT; +static enum reencode_mode { REENCODE_ABORT, REENCODE_YES, REENCODE_NO } reencode_mode = REENCODE_ABORT; static int fake_missing_tagger; static int use_done_feature; static int no_data; @@ -53,16 +53,18 @@ static struct revision_sources revision_sources; static int parse_opt_signed_tag_mode(const struct option *opt, const char *arg, int unset) { + enum signed_tag_mode *val = opt->value; + if (unset || !strcmp(arg, "abort")) - signed_tag_mode = SIGNED_TAG_ABORT; + *val = SIGNED_TAG_ABORT; else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore")) - signed_tag_mode = VERBATIM; + *val = VERBATIM; else if (!strcmp(arg, "warn")) - signed_tag_mode = WARN; + *val = WARN; else if (!strcmp(arg, "warn-strip")) - signed_tag_mode = WARN_STRIP; + *val = WARN_STRIP; else if (!strcmp(arg, "strip")) - signed_tag_mode = STRIP; + *val = STRIP; else return error("Unknown signed-tags mode: %s", arg); return 0; @@ -71,12 +73,14 @@ static int parse_opt_signed_tag_mode(const struct option *opt, static int parse_opt_tag_of_filtered_mode(const struct option *opt, const char *arg, int unset) { + enum tag_of_filtered_mode *val = opt->value; + if (unset || !strcmp(arg, "abort")) - tag_of_filtered_mode = TAG_FILTERING_ABORT; + *val = TAG_FILTERING_ABORT; else if (!strcmp(arg, "drop")) - tag_of_filtered_mode = DROP; + *val = DROP; else if (!strcmp(arg, "rewrite")) - tag_of_filtered_mode = REWRITE; + *val = REWRITE; else return error("Unknown tag-of-filtered mode: %s", arg); return 0; @@ -85,21 +89,23 @@ static int parse_opt_tag_of_filtered_mode(const struct option *opt, static int parse_opt_reencode_mode(const struct option *opt, const char *arg, int unset) { + enum reencode_mode *val = opt->value; + if (unset) { - reencode_mode = REENCODE_ABORT; + *val = REENCODE_ABORT; return 0; } switch (git_parse_maybe_bool(arg)) { case 0: - reencode_mode = REENCODE_NO; + *val = REENCODE_NO; break; case 1: - reencode_mode = REENCODE_YES; + *val = REENCODE_YES; break; default: if (!strcasecmp(arg, "abort")) - reencode_mode = REENCODE_ABORT; + *val = REENCODE_ABORT; else return error("Unknown reencoding mode: %s", arg); } diff --git a/builtin/fast-import.c b/builtin/fast-import.c index 4dbb10aff3..444f41cf8c 100644 --- a/builtin/fast-import.c +++ b/builtin/fast-import.c @@ -1102,6 +1102,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark) || (pack_size + PACK_SIZE_THRESHOLD + len) < pack_size) cycle_packfile(); + the_hash_algo->init_fn(&checkpoint.ctx); hashfile_checkpoint(pack_file, &checkpoint); offset = checkpoint.offset; diff --git a/builtin/fetch.c b/builtin/fetch.c index eed4a7cdb6..fd134ba74d 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -176,7 +176,7 @@ static int parse_refmap_arg(const struct option *opt, const char *arg, int unset * "git fetch --refmap='' origin foo" * can be used to tell the command not to store anywhere */ - refspec_append(&refmap, arg); + refspec_append(opt->value, arg); return 0; } @@ -308,7 +308,7 @@ static void clear_item(struct refname_hash_entry *item) static void add_already_queued_tags(const char *refname, - const struct object_id *old_oid, + const struct object_id *old_oid UNUSED, const struct object_id *new_oid, void *cb_data) { @@ -2204,7 +2204,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules), OPT_BOOL(0, "update-shallow", &update_shallow, N_("accept refs that update .git/shallow")), - OPT_CALLBACK_F(0, "refmap", NULL, N_("refmap"), + OPT_CALLBACK_F(0, "refmap", &refmap, 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_IPVERSION(&family), diff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c index 7e99c4d61b..5d01db5c02 100644 --- a/builtin/fsmonitor--daemon.c +++ b/builtin/fsmonitor--daemon.c @@ -129,8 +129,9 @@ struct fsmonitor_cookie_item { enum fsmonitor_cookie_item_result result; }; -static int cookies_cmp(const void *data, const struct hashmap_entry *he1, - const struct hashmap_entry *he2, const void *keydata) +static int cookies_cmp(const void *data UNUSED, + const struct hashmap_entry *he1, + const struct hashmap_entry *he2, const void *keydata) { const struct fsmonitor_cookie_item *a = container_of(he1, const struct fsmonitor_cookie_item, entry); @@ -1412,7 +1413,7 @@ done: return err; } -static int try_to_run_foreground_daemon(int detach_console) +static int try_to_run_foreground_daemon(int detach_console MAYBE_UNUSED) { /* * Technically, we don't need to probe for an existing daemon @@ -1442,7 +1443,8 @@ static int try_to_run_foreground_daemon(int detach_console) static start_bg_wait_cb bg_wait_cb; -static int bg_wait_cb(const struct child_process *cp, void *cb_data) +static int bg_wait_cb(const struct child_process *cp UNUSED, + void *cb_data UNUSED) { enum ipc_active_state s = fsmonitor_ipc__get_state(); diff --git a/builtin/gc.c b/builtin/gc.c index 5c4315f0d8..00192ae5d3 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -1403,7 +1403,7 @@ static void initialize_task_config(int schedule) strbuf_release(&config_name); } -static int task_option_parse(const struct option *opt, +static int task_option_parse(const struct option *opt UNUSED, const char *arg, int unset) { int i, num_selected = 0; @@ -1708,6 +1708,15 @@ static int get_schedule_cmd(const char **cmd, int *is_available) return 1; } +static int get_random_minute(void) +{ + /* Use a static value when under tests. */ + if (getenv("GIT_TEST_MAINT_SCHEDULER")) + return 13; + + return git_rand() % 60; +} + static int is_launchctl_available(void) { const char *cmd = "launchctl"; @@ -1820,6 +1829,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit struct strbuf plist = STRBUF_INIT, plist2 = STRBUF_INIT; struct stat st; const char *cmd = "launchctl"; + int minute = get_random_minute(); get_schedule_cmd(&cmd, NULL); preamble = "<?xml version=\"1.0\"?>\n" @@ -1845,29 +1855,30 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit case SCHEDULE_HOURLY: repeat = "<dict>\n" "<key>Hour</key><integer>%d</integer>\n" - "<key>Minute</key><integer>0</integer>\n" + "<key>Minute</key><integer>%d</integer>\n" "</dict>\n"; for (i = 1; i <= 23; i++) - strbuf_addf(&plist, repeat, i); + strbuf_addf(&plist, repeat, i, minute); break; case SCHEDULE_DAILY: repeat = "<dict>\n" "<key>Day</key><integer>%d</integer>\n" "<key>Hour</key><integer>0</integer>\n" - "<key>Minute</key><integer>0</integer>\n" + "<key>Minute</key><integer>%d</integer>\n" "</dict>\n"; for (i = 1; i <= 6; i++) - strbuf_addf(&plist, repeat, i); + strbuf_addf(&plist, repeat, i, minute); break; case SCHEDULE_WEEKLY: - strbuf_addstr(&plist, - "<dict>\n" - "<key>Day</key><integer>0</integer>\n" - "<key>Hour</key><integer>0</integer>\n" - "<key>Minute</key><integer>0</integer>\n" - "</dict>\n"); + strbuf_addf(&plist, + "<dict>\n" + "<key>Day</key><integer>0</integer>\n" + "<key>Hour</key><integer>0</integer>\n" + "<key>Minute</key><integer>%d</integer>\n" + "</dict>\n", + minute); break; default: @@ -1923,7 +1934,7 @@ static int launchctl_add_plists(void) launchctl_schedule_plist(exec_path, SCHEDULE_WEEKLY); } -static int launchctl_update_schedule(int run_maintenance, int fd) +static int launchctl_update_schedule(int run_maintenance, int fd UNUSED) { if (run_maintenance) return launchctl_add_plists(); @@ -1984,6 +1995,7 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority const char *frequency = get_frequency(schedule); char *name = schtasks_task_name(frequency); struct strbuf tfilename = STRBUF_INIT; + int minute = get_random_minute(); get_schedule_cmd(&cmd, NULL); @@ -2004,7 +2016,7 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority switch (schedule) { case SCHEDULE_HOURLY: fprintf(tfile->fp, - "<StartBoundary>2020-01-01T01:00:00</StartBoundary>\n" + "<StartBoundary>2020-01-01T01:%02d:00</StartBoundary>\n" "<Enabled>true</Enabled>\n" "<ScheduleByDay>\n" "<DaysInterval>1</DaysInterval>\n" @@ -2013,12 +2025,13 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority "<Interval>PT1H</Interval>\n" "<Duration>PT23H</Duration>\n" "<StopAtDurationEnd>false</StopAtDurationEnd>\n" - "</Repetition>\n"); + "</Repetition>\n", + minute); break; case SCHEDULE_DAILY: fprintf(tfile->fp, - "<StartBoundary>2020-01-01T00:00:00</StartBoundary>\n" + "<StartBoundary>2020-01-01T00:%02d:00</StartBoundary>\n" "<Enabled>true</Enabled>\n" "<ScheduleByWeek>\n" "<DaysOfWeek>\n" @@ -2030,19 +2043,21 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority "<Saturday />\n" "</DaysOfWeek>\n" "<WeeksInterval>1</WeeksInterval>\n" - "</ScheduleByWeek>\n"); + "</ScheduleByWeek>\n", + minute); break; case SCHEDULE_WEEKLY: fprintf(tfile->fp, - "<StartBoundary>2020-01-01T00:00:00</StartBoundary>\n" + "<StartBoundary>2020-01-01T00:%02d:00</StartBoundary>\n" "<Enabled>true</Enabled>\n" "<ScheduleByWeek>\n" "<DaysOfWeek>\n" "<Sunday />\n" "</DaysOfWeek>\n" "<WeeksInterval>1</WeeksInterval>\n" - "</ScheduleByWeek>\n"); + "</ScheduleByWeek>\n", + minute); break; default: @@ -2100,7 +2115,7 @@ static int schtasks_schedule_tasks(void) schtasks_schedule_task(exec_path, SCHEDULE_WEEKLY); } -static int schtasks_update_schedule(int run_maintenance, int fd) +static int schtasks_update_schedule(int run_maintenance, int fd UNUSED) { if (run_maintenance) return schtasks_schedule_tasks(); @@ -2159,6 +2174,7 @@ static int crontab_update_schedule(int run_maintenance, int fd) FILE *cron_list, *cron_in; struct strbuf line = STRBUF_INIT; struct tempfile *tmpedit = NULL; + int minute = get_random_minute(); get_schedule_cmd(&cmd, NULL); strvec_split(&crontab_list.args, cmd); @@ -2213,11 +2229,11 @@ static int crontab_update_schedule(int run_maintenance, int fd) "# replaced in the future by a Git command.\n\n"); strbuf_addf(&line_format, - "%%s %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%s\n", + "%%d %%s * * %%s \"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%s\n", exec_path, exec_path); - fprintf(cron_in, line_format.buf, "0", "1-23", "*", "hourly"); - fprintf(cron_in, line_format.buf, "0", "0", "1-6", "daily"); - fprintf(cron_in, line_format.buf, "0", "0", "0", "weekly"); + fprintf(cron_in, line_format.buf, minute, "1-23", "*", "hourly"); + fprintf(cron_in, line_format.buf, minute, "0", "1-6", "daily"); + fprintf(cron_in, line_format.buf, minute, "0", "0", "weekly"); strbuf_release(&line_format); fprintf(cron_in, "\n%s\n", END_LINE); @@ -2276,77 +2292,54 @@ static char *xdg_config_home_systemd(const char *filename) return xdg_config_home_for("systemd/user", filename); } -static int systemd_timer_enable_unit(int enable, - enum schedule_priority schedule) -{ - const char *cmd = "systemctl"; - struct child_process child = CHILD_PROCESS_INIT; - const char *frequency = get_frequency(schedule); - - /* - * Disabling the systemd unit while it is already disabled makes - * systemctl print an error. - * Let's ignore it since it means we already are in the expected state: - * the unit is disabled. - * - * On the other hand, enabling a systemd unit which is already enabled - * produces no error. - */ - if (!enable) - child.no_stderr = 1; - - get_schedule_cmd(&cmd, NULL); - strvec_split(&child.args, cmd); - strvec_pushl(&child.args, "--user", enable ? "enable" : "disable", - "--now", NULL); - strvec_pushf(&child.args, "git-maintenance@%s.timer", frequency); - - if (start_command(&child)) - return error(_("failed to start systemctl")); - if (finish_command(&child)) - /* - * Disabling an already disabled systemd unit makes - * systemctl fail. - * Let's ignore this failure. - * - * Enabling an enabled systemd unit doesn't fail. - */ - if (enable) - return error(_("failed to run systemctl")); - return 0; -} +#define SYSTEMD_UNIT_FORMAT "git-maintenance@%s.%s" -static int systemd_timer_delete_unit_templates(void) +static int systemd_timer_delete_timer_file(enum schedule_priority priority) { int ret = 0; - char *filename = xdg_config_home_systemd("git-maintenance@.timer"); - if (unlink(filename) && !is_missing_file_error(errno)) - ret = error_errno(_("failed to delete '%s'"), filename); - FREE_AND_NULL(filename); + const char *frequency = get_frequency(priority); + char *local_timer_name = xstrfmt(SYSTEMD_UNIT_FORMAT, frequency, "timer"); + char *filename = xdg_config_home_systemd(local_timer_name); - filename = xdg_config_home_systemd("git-maintenance@.service"); if (unlink(filename) && !is_missing_file_error(errno)) ret = error_errno(_("failed to delete '%s'"), filename); free(filename); + free(local_timer_name); return ret; } -static int systemd_timer_delete_units(void) +static int systemd_timer_delete_service_template(void) { - return systemd_timer_enable_unit(0, SCHEDULE_HOURLY) || - systemd_timer_enable_unit(0, SCHEDULE_DAILY) || - systemd_timer_enable_unit(0, SCHEDULE_WEEKLY) || - systemd_timer_delete_unit_templates(); + int ret = 0; + char *local_service_name = xstrfmt(SYSTEMD_UNIT_FORMAT, "", "service"); + char *filename = xdg_config_home_systemd(local_service_name); + if (unlink(filename) && !is_missing_file_error(errno)) + ret = error_errno(_("failed to delete '%s'"), filename); + + free(filename); + free(local_service_name); + return ret; } -static int systemd_timer_write_unit_templates(const char *exec_path) +/* + * Write the schedule information into a git-maintenance@<schedule>.timer + * file using a custom minute. This timer file cannot use the templating + * system, so we generate a specific file for each. + */ +static int systemd_timer_write_timer_file(enum schedule_priority schedule, + int minute) { + int res = -1; char *filename; FILE *file; const char *unit; + char *schedule_pattern = NULL; + const char *frequency = get_frequency(schedule); + char *local_timer_name = xstrfmt(SYSTEMD_UNIT_FORMAT, frequency, "timer"); + + filename = xdg_config_home_systemd(local_timer_name); - filename = xdg_config_home_systemd("git-maintenance@.timer"); if (safe_create_leading_directories(filename)) { error(_("failed to create directories for '%s'"), filename); goto error; @@ -2355,6 +2348,23 @@ static int systemd_timer_write_unit_templates(const char *exec_path) if (!file) goto error; + switch (schedule) { + case SCHEDULE_HOURLY: + schedule_pattern = xstrfmt("*-*-* 1..23:%02d:00", minute); + break; + + case SCHEDULE_DAILY: + schedule_pattern = xstrfmt("Tue..Sun *-*-* 0:%02d:00", minute); + break; + + case SCHEDULE_WEEKLY: + schedule_pattern = xstrfmt("Mon 0:%02d:00", minute); + break; + + default: + BUG("Unhandled schedule_priority"); + } + unit = "# This file was created and is maintained by Git.\n" "# Any edits made in this file might be replaced in the future\n" "# by a Git command.\n" @@ -2363,12 +2373,12 @@ static int systemd_timer_write_unit_templates(const char *exec_path) "Description=Optimize Git repositories data\n" "\n" "[Timer]\n" - "OnCalendar=%i\n" + "OnCalendar=%s\n" "Persistent=true\n" "\n" "[Install]\n" "WantedBy=timers.target\n"; - if (fputs(unit, file) == EOF) { + if (fprintf(file, unit, schedule_pattern) < 0) { error(_("failed to write to '%s'"), filename); fclose(file); goto error; @@ -2377,9 +2387,36 @@ static int systemd_timer_write_unit_templates(const char *exec_path) error_errno(_("failed to flush '%s'"), filename); goto error; } + + res = 0; + +error: + free(schedule_pattern); + free(local_timer_name); free(filename); + return res; +} - filename = xdg_config_home_systemd("git-maintenance@.service"); +/* + * No matter the schedule, we use the same service and can make use of the + * templating system. When installing git-maintenance@<schedule>.timer, + * systemd will notice that git-maintenance@.service exists as a template + * and will use this file and insert the <schedule> into the template at + * the position of "%i". + */ +static int systemd_timer_write_service_template(const char *exec_path) +{ + int res = -1; + char *filename; + FILE *file; + const char *unit; + char *local_service_name = xstrfmt(SYSTEMD_UNIT_FORMAT, "", "service"); + + filename = xdg_config_home_systemd(local_service_name); + if (safe_create_leading_directories(filename)) { + error(_("failed to create directories for '%s'"), filename); + goto error; + } file = fopen_or_warn(filename, "w"); if (!file) goto error; @@ -2397,7 +2434,7 @@ static int systemd_timer_write_unit_templates(const char *exec_path) "LockPersonality=yes\n" "MemoryDenyWriteExecute=yes\n" "NoNewPrivileges=yes\n" - "RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6\n" + "RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_VSOCK\n" "RestrictNamespaces=yes\n" "RestrictRealtime=yes\n" "RestrictSUIDSGID=yes\n" @@ -2412,29 +2449,114 @@ static int systemd_timer_write_unit_templates(const char *exec_path) error_errno(_("failed to flush '%s'"), filename); goto error; } + + res = 0; + +error: + free(local_service_name); free(filename); + return res; +} + +static int systemd_timer_enable_unit(int enable, + enum schedule_priority schedule, + int minute) +{ + const char *cmd = "systemctl"; + struct child_process child = CHILD_PROCESS_INIT; + const char *frequency = get_frequency(schedule); + + /* + * Disabling the systemd unit while it is already disabled makes + * systemctl print an error. + * Let's ignore it since it means we already are in the expected state: + * the unit is disabled. + * + * On the other hand, enabling a systemd unit which is already enabled + * produces no error. + */ + if (!enable) + child.no_stderr = 1; + else if (systemd_timer_write_timer_file(schedule, minute)) + return -1; + + get_schedule_cmd(&cmd, NULL); + strvec_split(&child.args, cmd); + strvec_pushl(&child.args, "--user", enable ? "enable" : "disable", + "--now", NULL); + strvec_pushf(&child.args, SYSTEMD_UNIT_FORMAT, frequency, "timer"); + + if (start_command(&child)) + return error(_("failed to start systemctl")); + if (finish_command(&child)) + /* + * Disabling an already disabled systemd unit makes + * systemctl fail. + * Let's ignore this failure. + * + * Enabling an enabled systemd unit doesn't fail. + */ + if (enable) + return error(_("failed to run systemctl")); return 0; +} + +/* + * A previous version of Git wrote the timer units as template files. + * Clean these up, if they exist. + */ +static void systemd_timer_delete_stale_timer_templates(void) +{ + char *timer_template_name = xstrfmt(SYSTEMD_UNIT_FORMAT, "", "timer"); + char *filename = xdg_config_home_systemd(timer_template_name); + + if (unlink(filename) && !is_missing_file_error(errno)) + warning(_("failed to delete '%s'"), filename); -error: free(filename); - systemd_timer_delete_unit_templates(); - return -1; + free(timer_template_name); +} + +static int systemd_timer_delete_unit_files(void) +{ + systemd_timer_delete_stale_timer_templates(); + + /* Purposefully not short-circuited to make sure all are called. */ + return systemd_timer_delete_timer_file(SCHEDULE_HOURLY) | + systemd_timer_delete_timer_file(SCHEDULE_DAILY) | + systemd_timer_delete_timer_file(SCHEDULE_WEEKLY) | + systemd_timer_delete_service_template(); +} + +static int systemd_timer_delete_units(void) +{ + int minute = get_random_minute(); + /* Purposefully not short-circuited to make sure all are called. */ + return systemd_timer_enable_unit(0, SCHEDULE_HOURLY, minute) | + systemd_timer_enable_unit(0, SCHEDULE_DAILY, minute) | + systemd_timer_enable_unit(0, SCHEDULE_WEEKLY, minute) | + systemd_timer_delete_unit_files(); } static int systemd_timer_setup_units(void) { + int minute = get_random_minute(); const char *exec_path = git_exec_path(); - int ret = systemd_timer_write_unit_templates(exec_path) || - systemd_timer_enable_unit(1, SCHEDULE_HOURLY) || - systemd_timer_enable_unit(1, SCHEDULE_DAILY) || - systemd_timer_enable_unit(1, SCHEDULE_WEEKLY); + int ret = systemd_timer_write_service_template(exec_path) || + systemd_timer_enable_unit(1, SCHEDULE_HOURLY, minute) || + systemd_timer_enable_unit(1, SCHEDULE_DAILY, minute) || + systemd_timer_enable_unit(1, SCHEDULE_WEEKLY, minute); + if (ret) systemd_timer_delete_units(); + else + systemd_timer_delete_stale_timer_templates(); + return ret; } -static int systemd_timer_update_schedule(int run_maintenance, int fd) +static int systemd_timer_update_schedule(int run_maintenance, int fd UNUSED) { if (run_maintenance) return systemd_timer_setup_units(); @@ -2606,9 +2728,12 @@ static int maintenance_start(int argc, const char **argv, const char *prefix) opts.scheduler = resolve_scheduler(opts.scheduler); validate_scheduler(opts.scheduler); + if (update_background_schedule(&opts, 1)) + die(_("failed to set up maintenance schedule")); + if (maintenance_register(ARRAY_SIZE(register_args)-1, register_args, NULL)) warning(_("failed to add repo to global config")); - return update_background_schedule(&opts, 1); + return 0; } static const char *const builtin_maintenance_stop_usage[] = { diff --git a/builtin/grep.c b/builtin/grep.c index 50e712a184..b71222330a 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -924,9 +924,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix) N_("process binary files with textconv filters")), OPT_SET_INT('r', "recursive", &opt.max_depth, N_("search in subdirectories (default)"), -1), - { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, N_("depth"), - N_("descend at most <depth> levels"), PARSE_OPT_NONEG, - NULL, 1 }, + OPT_INTEGER_F(0, "max-depth", &opt.max_depth, + N_("descend at most <n> levels"), PARSE_OPT_NONEG), OPT_GROUP(""), OPT_SET_INT('E', "extended-regexp", &opt.pattern_type_option, N_("use extended POSIX regular expressions"), @@ -990,7 +989,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) OPT_CALLBACK_F(0, "and", &opt, NULL, N_("combine patterns specified with -e"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback), - OPT_BOOL(0, "or", &dummy, ""), + OPT_BOOL_F(0, "or", &dummy, "", PARSE_OPT_NONEG), OPT_CALLBACK_F(0, "not", &opt, NULL, "", PARSE_OPT_NOARG | PARSE_OPT_NONEG, not_callback), OPT_CALLBACK_F('(', NULL, &opt, NULL, "", diff --git a/builtin/index-pack.c b/builtin/index-pack.c index 006ffdc9c5..dda94a9f46 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1166,6 +1166,7 @@ static void parse_pack_objects(unsigned char *hash) struct ofs_delta_entry *ofs_delta = ofs_deltas; struct object_id ref_delta_oid; struct stat st; + git_hash_ctx tmp_ctx; if (verbose) progress = start_progress( @@ -1202,7 +1203,9 @@ static void parse_pack_objects(unsigned char *hash) /* Check pack integrity */ flush(); - the_hash_algo->final_fn(hash, &input_ctx); + the_hash_algo->init_fn(&tmp_ctx); + the_hash_algo->clone_fn(&tmp_ctx, &input_ctx); + the_hash_algo->final_fn(hash, &tmp_ctx); if (!hasheq(fill(the_hash_algo->rawsz), hash)) die(_("pack is corrupted (SHA1 mismatch)")); use(the_hash_algo->rawsz); diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c index c5e8345265..a110e69f83 100644 --- a/builtin/interpret-trailers.c +++ b/builtin/interpret-trailers.c @@ -24,21 +24,24 @@ static enum trailer_if_exists if_exists; static enum trailer_if_missing if_missing; static int option_parse_where(const struct option *opt, - const char *arg, int unset) + const char *arg, int unset UNUSED) { - return trailer_set_where(&where, arg); + /* unset implies NULL arg, which is handled in our helper */ + return trailer_set_where(opt->value, arg); } static int option_parse_if_exists(const struct option *opt, - const char *arg, int unset) + const char *arg, int unset UNUSED) { - return trailer_set_if_exists(&if_exists, arg); + /* unset implies NULL arg, which is handled in our helper */ + return trailer_set_if_exists(opt->value, arg); } static int option_parse_if_missing(const struct option *opt, - const char *arg, int unset) + const char *arg, int unset UNUSED) { - return trailer_set_if_missing(&if_missing, arg); + /* unset implies NULL arg, which is handled in our helper */ + return trailer_set_if_missing(opt->value, arg); } static void new_trailers_clear(struct list_head *trailers) @@ -97,11 +100,11 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "in-place", &opts.in_place, N_("edit files in place")), OPT_BOOL(0, "trim-empty", &opts.trim_empty, N_("trim empty trailers")), - OPT_CALLBACK(0, "where", NULL, N_("action"), + OPT_CALLBACK(0, "where", &where, N_("action"), N_("where to place the new trailer"), option_parse_where), - OPT_CALLBACK(0, "if-exists", NULL, N_("action"), + OPT_CALLBACK(0, "if-exists", &if_exists, N_("action"), N_("action if trailer already exists"), option_parse_if_exists), - OPT_CALLBACK(0, "if-missing", NULL, N_("action"), + OPT_CALLBACK(0, "if-missing", &if_missing, N_("action"), N_("action if trailer is missing"), option_parse_if_missing), OPT_BOOL(0, "only-trailers", &opts.only_trailers, N_("output only the trailers")), diff --git a/builtin/log.c b/builtin/log.c index db3a88bfe9..80e1be1645 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -118,16 +118,19 @@ static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP; static struct string_list decorate_refs_exclude_config = STRING_LIST_INIT_NODUP; static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP; -static int clear_decorations_callback(const struct option *opt, - const char *arg, int unset) +static int clear_decorations_callback(const struct option *opt UNUSED, + const char *arg, int unset) { + BUG_ON_OPT_NEG(unset); + BUG_ON_OPT_ARG(arg); string_list_clear(&decorate_refs_include, 0); string_list_clear(&decorate_refs_exclude, 0); use_default_decoration_filter = 0; return 0; } -static int decorate_callback(const struct option *opt, const char *arg, int unset) +static int decorate_callback(const struct option *opt UNUSED, const char *arg, + int unset) { if (unset) decoration_style = 0; @@ -175,6 +178,7 @@ static void cmd_log_init_defaults(struct rev_info *rev) rev->verbose_header = 1; rev->diffopt.flags.recursive = 1; rev->diffopt.stat_width = -1; /* use full terminal width */ + rev->diffopt.stat_name_width = -1; /* respect statNameWidth config */ rev->diffopt.stat_graph_width = -1; /* respect statGraphWidth config */ rev->abbrev_commit = default_abbrev_commit; rev->show_root_diff = default_show_root; @@ -549,7 +553,7 @@ static int cmd_log_walk_no_free(struct rev_info *rev) rev->diffopt.flags.check_failed) { return 02; } - return diff_result_code(&rev->diffopt, 0); + return diff_result_code(&rev->diffopt); } static int cmd_log_walk(struct rev_info *rev) @@ -1253,7 +1257,15 @@ static void show_diffstat(struct rev_info *rev, fprintf(rev->diffopt.file, "\n"); } +static void read_desc_file(struct strbuf *buf, const char *desc_file) +{ + if (strbuf_read_file(buf, desc_file, 0) < 0) + die_errno(_("unable to read branch description file '%s'"), + desc_file); +} + static void prepare_cover_text(struct pretty_print_context *pp, + const char *description_file, const char *branch_name, struct strbuf *sb, const char *encoding, @@ -1267,7 +1279,9 @@ static void prepare_cover_text(struct pretty_print_context *pp, if (cover_from_description_mode == COVER_FROM_NONE) goto do_pp; - if (branch_name && *branch_name) + if (description_file && *description_file) + read_desc_file(&description_sb, description_file); + else if (branch_name && *branch_name) read_branch_desc(&description_sb, branch_name); if (!description_sb.len) goto do_pp; @@ -1313,6 +1327,7 @@ static void get_notes_args(struct strvec *arg, struct rev_info *rev) static void make_cover_letter(struct rev_info *rev, int use_separate_file, struct commit *origin, int nr, struct commit **list, + const char *description_file, const char *branch_name, int quiet) { @@ -1352,7 +1367,8 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, pp.rev = rev; pp.print_email_subject = 1; pp_user_info(&pp, NULL, &sb, committer, encoding); - prepare_cover_text(&pp, branch_name, &sb, encoding, need_8bit_cte); + prepare_cover_text(&pp, description_file, branch_name, &sb, + encoding, need_8bit_cte); fprintf(rev->diffopt.file, "%s\n", sb.buf); strbuf_release(&sb); @@ -1468,19 +1484,16 @@ static int subject_prefix = 0; static int subject_prefix_callback(const struct option *opt, const char *arg, int unset) { + struct strbuf *sprefix; + BUG_ON_OPT_NEG(unset); + sprefix = opt->value; subject_prefix = 1; - ((struct rev_info *)opt->value)->subject_prefix = arg; + strbuf_reset(sprefix); + strbuf_addstr(sprefix, arg); return 0; } -static int rfc_callback(const struct option *opt, const char *arg, int unset) -{ - BUG_ON_OPT_NEG(unset); - BUG_ON_OPT_ARG(arg); - return subject_prefix_callback(opt, "RFC PATCH", unset); -} - static int numbered_cmdline_opt = 0; static int numbered_callback(const struct option *opt, const char *arg, @@ -1555,7 +1568,8 @@ static int inline_callback(const struct option *opt, const char *arg, int unset) return 0; } -static int header_callback(const struct option *opt, const char *arg, int unset) +static int header_callback(const struct option *opt UNUSED, const char *arg, + int unset) { if (unset) { string_list_clear(&extra_hdr, 0); @@ -1567,24 +1581,6 @@ static int header_callback(const struct option *opt, const char *arg, int unset) return 0; } -static int to_callback(const struct option *opt, const char *arg, int unset) -{ - if (unset) - string_list_clear(&extra_to, 0); - else - string_list_append(&extra_to, arg); - return 0; -} - -static int cc_callback(const struct option *opt, const char *arg, int unset) -{ - if (unset) - string_list_clear(&extra_cc, 0); - else - string_list_append(&extra_cc, arg); - return 0; -} - static int from_callback(const struct option *opt, const char *arg, int unset) { char **from = opt->value; @@ -1893,6 +1889,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) int quiet = 0; const char *reroll_count = NULL; char *cover_from_description_arg = NULL; + char *description_file = NULL; char *branch_name = NULL; char *base_commit = NULL; struct base_tree_info bases; @@ -1907,6 +1904,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) struct strbuf rdiff_title = STRBUF_INIT; struct strbuf sprefix = STRBUF_INIT; int creation_factor = -1; + int rfc = 0; const struct option builtin_format_patch_options[] = { OPT_CALLBACK_F('n', "numbered", &numbered, NULL, @@ -1930,13 +1928,13 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) N_("mark the series as Nth re-roll")), OPT_INTEGER(0, "filename-max-length", &fmt_patch_name_max, N_("max length of output filename")), - OPT_CALLBACK_F(0, "rfc", &rev, NULL, - N_("use [RFC PATCH] instead of [PATCH]"), - PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback), + OPT_BOOL(0, "rfc", &rfc, N_("use [RFC PATCH] instead of [PATCH]")), OPT_STRING(0, "cover-from-description", &cover_from_description_arg, N_("cover-from-description-mode"), N_("generate parts of a cover letter based on a branch's description")), - OPT_CALLBACK_F(0, "subject-prefix", &rev, N_("prefix"), + OPT_FILENAME(0, "description-file", &description_file, + N_("use branch description from file")), + OPT_CALLBACK_F(0, "subject-prefix", &sprefix, N_("prefix"), N_("use [<prefix>] instead of [PATCH]"), PARSE_OPT_NONEG, subject_prefix_callback), OPT_CALLBACK_F('o', "output-directory", &output_directory, @@ -1957,8 +1955,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) OPT_GROUP(N_("Messaging")), OPT_CALLBACK(0, "add-header", NULL, N_("header"), N_("add email header"), header_callback), - OPT_CALLBACK(0, "to", NULL, N_("email"), N_("add To: header"), to_callback), - OPT_CALLBACK(0, "cc", NULL, N_("email"), N_("add Cc: header"), cc_callback), + OPT_STRING_LIST(0, "to", &extra_to, N_("email"), N_("add To: header")), + OPT_STRING_LIST(0, "cc", &extra_cc, N_("email"), N_("add Cc: header")), OPT_CALLBACK_F(0, "from", &from, N_("ident"), N_("set From address to <ident> (or committer ident if absent)"), PARSE_OPT_OPTARG, from_callback), @@ -2016,11 +2014,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) rev.max_parents = 1; rev.diffopt.flags.recursive = 1; rev.diffopt.no_free = 1; - rev.subject_prefix = fmt_patch_subject_prefix; memset(&s_r_opt, 0, sizeof(s_r_opt)); s_r_opt.def = "HEAD"; s_r_opt.revarg_opt = REVARG_COMMITTISH; + strbuf_addstr(&sprefix, fmt_patch_subject_prefix); if (format_no_prefix) diff_set_noprefix(&rev.diffopt); @@ -2048,13 +2046,16 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (cover_from_description_arg) cover_from_description_mode = parse_cover_from_description(cover_from_description_arg); + if (rfc) + strbuf_insertstr(&sprefix, 0, "RFC "); + if (reroll_count) { - strbuf_addf(&sprefix, "%s v%s", - rev.subject_prefix, reroll_count); + strbuf_addf(&sprefix, " v%s", reroll_count); rev.reroll_count = reroll_count; - rev.subject_prefix = sprefix.buf; } + rev.subject_prefix = sprefix.buf; + for (i = 0; i < extra_hdr.nr; i++) { strbuf_addstr(&buf, extra_hdr.items[i].string); strbuf_addch(&buf, '\n'); @@ -2321,7 +2322,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (thread) gen_message_id(&rev, "cover"); make_cover_letter(&rev, !!output_directory, - origin, nr, list, branch_name, quiet); + origin, nr, list, description_file, branch_name, quiet); print_bases(&bases, rev.diffopt.file); print_signature(rev.diffopt.file); total++; diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index f558db5f3b..209d2dc0d5 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -241,7 +241,8 @@ static int show_tree_long(const struct object_id *oid, struct strbuf *base, return recurse; } -static int show_tree_name_only(const struct object_id *oid, struct strbuf *base, +static int show_tree_name_only(const struct object_id *oid UNUSED, + struct strbuf *base, const char *pathname, unsigned mode, void *context) { diff --git a/builtin/merge.c b/builtin/merge.c index de68910177..fd21c0d4f4 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -79,8 +79,7 @@ static int overwrite_ignore = 1; static struct strbuf merge_msg = STRBUF_INIT; static struct strategy **use_strategies; static size_t use_strategies_nr, use_strategies_alloc; -static const char **xopts; -static size_t xopts_nr, xopts_alloc; +static struct strvec xopts = STRVEC_INIT; static const char *branch; static char *branch_mergeoptions; static int verbosity; @@ -232,7 +231,7 @@ static void append_strategy(struct strategy *s) use_strategies[use_strategies_nr++] = s; } -static int option_parse_strategy(const struct option *opt, +static int option_parse_strategy(const struct option *opt UNUSED, const char *name, int unset) { if (unset) @@ -242,29 +241,9 @@ static int option_parse_strategy(const struct option *opt, return 0; } -static int option_parse_x(const struct option *opt, - const char *arg, int unset) -{ - if (unset) - return 0; - - ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc); - xopts[xopts_nr++] = xstrdup(arg); - return 0; -} - -static int option_parse_n(const struct option *opt, - const char *arg, int unset) -{ - BUG_ON_OPT_ARG(arg); - show_diffstat = unset; - return 0; -} - static struct option builtin_merge_options[] = { - OPT_CALLBACK_F('n', NULL, NULL, NULL, - N_("do not show a diffstat at the end of the merge"), - PARSE_OPT_NOARG, option_parse_n), + OPT_SET_INT('n', NULL, &show_diffstat, + N_("do not show a diffstat at the end of the merge"), 0), OPT_BOOL(0, "stat", &show_diffstat, N_("show a diffstat at the end of the merge")), OPT_BOOL(0, "summary", &show_diffstat, N_("(synonym to --stat)")), @@ -285,10 +264,10 @@ static struct option builtin_merge_options[] = { OPT_RERERE_AUTOUPDATE(&allow_rerere_auto), OPT_BOOL(0, "verify-signatures", &verify_signatures, N_("verify that the named commit has a valid GPG signature")), - OPT_CALLBACK('s', "strategy", &use_strategies, N_("strategy"), + OPT_CALLBACK('s', "strategy", NULL, N_("strategy"), N_("merge strategy to use"), option_parse_strategy), - OPT_CALLBACK('X', "strategy-option", &xopts, N_("option=value"), - N_("option for selected merge strategy"), option_parse_x), + OPT_STRVEC('X', "strategy-option", &xopts, N_("option=value"), + N_("option for selected merge strategy")), OPT_CALLBACK('m', "message", &merge_msg, N_("message"), N_("merge commit message (for a non-fast-forward merge)"), option_parse_message), @@ -488,6 +467,7 @@ static void finish(struct commit *head_commit, struct diff_options opts; repo_diff_setup(the_repository, &opts); opts.stat_width = -1; /* use full terminal width */ + opts.stat_name_width = -1; /* respect statNameWidth config */ opts.stat_graph_width = -1; /* respect statGraphWidth config */ opts.output_format |= DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; @@ -749,9 +729,9 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, o.show_rename_progress = show_progress == -1 ? isatty(2) : show_progress; - for (x = 0; x < xopts_nr; x++) - if (parse_merge_opt(&o, xopts[x])) - die(_("unknown strategy option: -X%s"), xopts[x]); + for (x = 0; x < xopts.nr; x++) + if (parse_merge_opt(&o, xopts.v[x])) + die(_("unknown strategy option: -X%s"), xopts.v[x]); o.branch1 = head_arg; o.branch2 = merge_remote_util(remoteheads->item)->name; @@ -777,7 +757,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, return clean ? 0 : 1; } else { return try_merge_command(the_repository, - strategy, xopts_nr, xopts, + strategy, xopts.nr, xopts.v, common, head_arg, remoteheads); } } diff --git a/builtin/mv.c b/builtin/mv.c index 05e7156034..c596515ad0 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -305,7 +305,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) } if (S_ISDIR(st.st_mode) && lstat(dst, &dest_st) == 0) { - bad = _("cannot move directory over file"); + bad = _("destination already exists"); goto act_on_entry; } diff --git a/builtin/name-rev.c b/builtin/name-rev.c index c706fa3720..2dd1807c4e 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -582,12 +582,8 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "undefined", &allow_undefined, N_("allow to print `undefined` names (default)")), OPT_BOOL(0, "always", &always, N_("show abbreviated commit object as fallback")), - { - /* A Hidden OPT_BOOL */ - OPTION_SET_INT, 0, "peel-tag", &peel_tag, NULL, - N_("dereference tags in the input (internal use)"), - PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1, - }, + OPT_HIDDEN_BOOL(0, "peel-tag", &peel_tag, + N_("dereference tags in the input (internal use)")), OPT_END(), }; diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index d2a162d528..6eb9756836 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -3603,7 +3603,6 @@ static void read_cruft_objects(void) string_list_append(&discard_packs, buf.buf + 1); else string_list_append(&fresh_packs, buf.buf); - strbuf_reset(&buf); } string_list_sort(&discard_packs); @@ -3739,7 +3738,7 @@ static void show_object__ma_allow_promisor(struct object *obj, const char *name, show_object(obj, name, data); } -static int option_parse_missing_action(const struct option *opt, +static int option_parse_missing_action(const struct option *opt UNUSED, const char *arg, int unset) { assert(arg); @@ -4120,34 +4119,37 @@ 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) { + int *val = opt->value; + BUG_ON_OPT_ARG(arg); if (!unset) - progress = 0; - else if (!progress) - progress = 1; + *val = 0; + else if (!*val) + *val = 1; return 0; } static int option_parse_index_version(const struct option *opt, const char *arg, int unset) { + struct pack_idx_option *popts = opt->value; char *c; const char *val = arg; BUG_ON_OPT_NEG(unset); - pack_idx_opts.version = strtoul(val, &c, 10); - if (pack_idx_opts.version > 2) + popts->version = strtoul(val, &c, 10); + if (popts->version > 2) die(_("unsupported index version %s"), val); if (*c == ',' && c[1]) - pack_idx_opts.off32_limit = strtoul(c+1, &c, 0); - if (*c || pack_idx_opts.off32_limit & 0x80000000) + popts->off32_limit = strtoul(c+1, &c, 0); + if (*c || popts->off32_limit & 0x80000000) die(_("bad index version '%s'"), val); return 0; } -static int option_parse_unpack_unreachable(const struct option *opt, +static int option_parse_unpack_unreachable(const struct option *opt UNUSED, const char *arg, int unset) { if (unset) { @@ -4162,7 +4164,7 @@ static int option_parse_unpack_unreachable(const struct option *opt, return 0; } -static int option_parse_cruft_expiration(const struct option *opt, +static int option_parse_cruft_expiration(const struct option *opt UNUSED, const char *arg, int unset) { if (unset) { @@ -4190,7 +4192,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) LIST_OBJECTS_FILTER_INIT; struct option pack_objects_options[] = { - OPT_CALLBACK_F('q', "quiet", NULL, NULL, + OPT_CALLBACK_F('q', "quiet", &progress, NULL, N_("do not show progress meter"), PARSE_OPT_NOARG, option_parse_quiet), OPT_SET_INT(0, "progress", &progress, @@ -4200,7 +4202,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "all-progress-implied", &all_progress_implied, N_("similar to --all-progress when progress meter is shown")), - OPT_CALLBACK_F(0, "index-version", NULL, N_("<version>[,<offset>]"), + OPT_CALLBACK_F(0, "index-version", &pack_idx_opts, N_("<version>[,<offset>]"), N_("write the pack index file in the specified idx format version"), PARSE_OPT_NONEG, option_parse_index_version), OPT_MAGNITUDE(0, "max-pack-size", &pack_size_limit, @@ -4383,7 +4385,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (!HAVE_THREADS && delta_search_threads != 1) warning(_("no threads support, ignoring --threads")); - if (!pack_to_stdout && !pack_size_limit && !cruft) + if (!pack_to_stdout && !pack_size_limit) pack_size_limit = pack_size_limit_cfg; if (pack_to_stdout && pack_size_limit) die(_("--max-pack-size cannot be used to build a pack for transfer")); @@ -4415,8 +4417,6 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) die(_("cannot use internal rev list with --cruft")); if (stdin_packs) die(_("cannot use --stdin-packs with --cruft")); - if (pack_size_limit) - die(_("cannot use --max-pack-size with --cruft")); } /* diff --git a/builtin/read-tree.c b/builtin/read-tree.c index 1fec702a04..8196ca9dd8 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -49,7 +49,7 @@ static const char * const read_tree_usage[] = { NULL }; -static int index_output_cb(const struct option *opt, const char *arg, +static int index_output_cb(const struct option *opt UNUSED, const char *arg, int unset) { BUG_ON_OPT_NEG(unset); diff --git a/builtin/rebase.c b/builtin/rebase.c index 50cb85751f..ed15accec9 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -1804,6 +1804,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) /* We want color (if set), but no pager */ repo_diff_setup(the_repository, &opts); opts.stat_width = -1; /* use full terminal width */ + opts.stat_name_width = -1; /* respect statNameWidth config */ opts.stat_graph_width = -1; /* respect statGraphWidth config */ opts.output_format |= DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index fb8e1549d1..8c4f0cb90a 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -2527,10 +2527,10 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) if (cert_nonce_seed) push_cert_nonce = prepare_push_cert_nonce(service_dir, time(NULL)); - if (0 <= transfer_unpack_limit) - unpack_limit = transfer_unpack_limit; - else if (0 <= receive_unpack_limit) + if (0 <= receive_unpack_limit) unpack_limit = receive_unpack_limit; + else if (0 <= transfer_unpack_limit) + unpack_limit = transfer_unpack_limit; switch (determine_protocol_version_server()) { case protocol_v2: diff --git a/builtin/repack.c b/builtin/repack.c index 97051479e4..529e13120d 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -27,7 +27,6 @@ #define PACK_CRUFT 4 #define DELETE_PACK 1 -#define CRUFT_PACK 2 static int pack_everything; static int delta_base_offset = 1; @@ -95,14 +94,106 @@ static int repack_config(const char *var, const char *value, return git_default_config(var, value, ctx, cb); } +struct existing_packs { + struct string_list kept_packs; + struct string_list non_kept_packs; + struct string_list cruft_packs; +}; + +#define EXISTING_PACKS_INIT { \ + .kept_packs = STRING_LIST_INIT_DUP, \ + .non_kept_packs = STRING_LIST_INIT_DUP, \ + .cruft_packs = STRING_LIST_INIT_DUP, \ +} + +static int has_existing_non_kept_packs(const struct existing_packs *existing) +{ + return existing->non_kept_packs.nr || existing->cruft_packs.nr; +} + +static void pack_mark_for_deletion(struct string_list_item *item) +{ + item->util = (void*)((uintptr_t)item->util | DELETE_PACK); +} + +static int pack_is_marked_for_deletion(struct string_list_item *item) +{ + return (uintptr_t)item->util & DELETE_PACK; +} + +static void mark_packs_for_deletion_1(struct string_list *names, + struct string_list *list) +{ + struct string_list_item *item; + const int hexsz = the_hash_algo->hexsz; + + for_each_string_list_item(item, list) { + char *sha1; + size_t len = strlen(item->string); + if (len < hexsz) + continue; + sha1 = item->string + len - hexsz; + /* + * Mark this pack for deletion, which ensures that this + * pack won't be included in a MIDX (if `--write-midx` + * was given) and that we will actually delete this pack + * (if `-d` was given). + */ + if (!string_list_has_string(names, sha1)) + pack_mark_for_deletion(item); + } +} + +static void mark_packs_for_deletion(struct existing_packs *existing, + struct string_list *names) + +{ + mark_packs_for_deletion_1(names, &existing->non_kept_packs); + mark_packs_for_deletion_1(names, &existing->cruft_packs); +} + +static void remove_redundant_pack(const char *dir_name, const char *base_name) +{ + struct strbuf buf = STRBUF_INIT; + struct multi_pack_index *m = get_local_multi_pack_index(the_repository); + strbuf_addf(&buf, "%s.pack", base_name); + if (m && midx_contains_pack(m, buf.buf)) + clear_midx_file(the_repository); + strbuf_insertf(&buf, 0, "%s/", dir_name); + unlink_pack_path(buf.buf, 1); + strbuf_release(&buf); +} + +static void remove_redundant_packs_1(struct string_list *packs) +{ + struct string_list_item *item; + for_each_string_list_item(item, packs) { + if (!pack_is_marked_for_deletion(item)) + continue; + remove_redundant_pack(packdir, item->string); + } +} + +static void remove_redundant_existing_packs(struct existing_packs *existing) +{ + remove_redundant_packs_1(&existing->non_kept_packs); + remove_redundant_packs_1(&existing->cruft_packs); +} + +static void existing_packs_release(struct existing_packs *existing) +{ + string_list_clear(&existing->kept_packs, 0); + string_list_clear(&existing->non_kept_packs, 0); + string_list_clear(&existing->cruft_packs, 0); +} + /* - * Adds all packs hex strings (pack-$HASH) to either fname_nonkept_list - * or fname_kept_list based on whether each pack has a corresponding + * Adds all packs hex strings (pack-$HASH) to either packs->non_kept + * or packs->kept based on whether each pack has a corresponding * .keep file or not. Packs without a .keep file are not to be kept * if we are going to pack everything into one file. */ -static void collect_pack_filenames(struct string_list *fname_nonkept_list, - struct string_list *fname_kept_list, +static void collect_pack_filenames(struct existing_packs *existing, const struct string_list *extra_keep) { struct packed_git *p; @@ -126,28 +217,14 @@ static void collect_pack_filenames(struct string_list *fname_nonkept_list, strbuf_strip_suffix(&buf, ".pack"); 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(fname_nonkept_list, buf.buf); - if (p->is_cruft) - item->util = (void*)(uintptr_t)CRUFT_PACK; - } + string_list_append(&existing->kept_packs, buf.buf); + else if (p->is_cruft) + string_list_append(&existing->cruft_packs, buf.buf); + else + string_list_append(&existing->non_kept_packs, buf.buf); } - string_list_sort(fname_kept_list); - strbuf_release(&buf); -} - -static void remove_redundant_pack(const char *dir_name, const char *base_name) -{ - struct strbuf buf = STRBUF_INIT; - struct multi_pack_index *m = get_local_multi_pack_index(the_repository); - strbuf_addf(&buf, "%s.pack", base_name); - if (m && midx_contains_pack(m, buf.buf)) - clear_midx_file(the_repository); - strbuf_insertf(&buf, 0, "%s/", dir_name); - unlink_pack_path(buf.buf, 1); + string_list_sort(&existing->kept_packs); strbuf_release(&buf); } @@ -303,6 +380,8 @@ struct pack_geometry { struct packed_git **pack; uint32_t pack_nr, pack_alloc; uint32_t split; + + int split_factor; }; static uint32_t geometry_pack_weight(struct packed_git *p) @@ -324,17 +403,13 @@ static int geometry_cmp(const void *va, const void *vb) return 0; } -static void init_pack_geometry(struct pack_geometry **geometry_p, - struct string_list *existing_kept_packs, +static void init_pack_geometry(struct pack_geometry *geometry, + struct existing_packs *existing, const struct pack_objects_args *args) { struct packed_git *p; - struct pack_geometry *geometry; struct strbuf buf = STRBUF_INIT; - *geometry_p = xcalloc(1, sizeof(struct pack_geometry)); - geometry = *geometry_p; - for (p = get_all_packs(the_repository); p; p = p->next) { if (args->local && !p->pack_local) /* @@ -346,23 +421,24 @@ static void init_pack_geometry(struct pack_geometry **geometry_p, if (!pack_kept_objects) { /* - * Any pack that has its pack_keep bit set will appear - * in existing_kept_packs below, but this saves us from - * doing a more expensive check. + * Any pack that has its pack_keep bit set will + * appear in existing->kept_packs below, but + * this saves us from doing a more expensive + * check. */ if (p->pack_keep) continue; /* - * The pack may be kept via the --keep-pack option; - * check 'existing_kept_packs' to determine whether to - * ignore it. + * The pack may be kept via the --keep-pack + * option; check 'existing->kept_packs' to + * determine whether to ignore it. */ strbuf_reset(&buf); strbuf_addstr(&buf, pack_basename(p)); strbuf_strip_suffix(&buf, ".pack"); - if (string_list_has_string(existing_kept_packs, buf.buf)) + if (string_list_has_string(&existing->kept_packs, buf.buf)) continue; } if (p->is_cruft) @@ -380,7 +456,7 @@ static void init_pack_geometry(struct pack_geometry **geometry_p, strbuf_release(&buf); } -static void split_pack_geometry(struct pack_geometry *geometry, int factor) +static void split_pack_geometry(struct pack_geometry *geometry) { uint32_t i; uint32_t split; @@ -399,12 +475,14 @@ static void split_pack_geometry(struct pack_geometry *geometry, int factor) struct packed_git *ours = geometry->pack[i]; struct packed_git *prev = geometry->pack[i - 1]; - if (unsigned_mult_overflows(factor, geometry_pack_weight(prev))) + if (unsigned_mult_overflows(geometry->split_factor, + geometry_pack_weight(prev))) die(_("pack %s too large to consider in geometric " "progression"), prev->pack_name); - if (geometry_pack_weight(ours) < factor * geometry_pack_weight(prev)) + if (geometry_pack_weight(ours) < + geometry->split_factor * geometry_pack_weight(prev)) break; } @@ -439,10 +517,12 @@ static void split_pack_geometry(struct pack_geometry *geometry, int factor) for (i = split; i < geometry->pack_nr; i++) { struct packed_git *ours = geometry->pack[i]; - if (unsigned_mult_overflows(factor, total_size)) + if (unsigned_mult_overflows(geometry->split_factor, + total_size)) die(_("pack %s too large to roll up"), ours->pack_name); - if (geometry_pack_weight(ours) < factor * total_size) { + if (geometry_pack_weight(ours) < + geometry->split_factor * total_size) { if (unsigned_add_overflows(total_size, geometry_pack_weight(ours))) die(_("pack %s too large to roll up"), @@ -492,13 +572,38 @@ static struct packed_git *get_preferred_pack(struct pack_geometry *geometry) return NULL; } +static void geometry_remove_redundant_packs(struct pack_geometry *geometry, + struct string_list *names, + struct existing_packs *existing) +{ + struct strbuf buf = STRBUF_INIT; + uint32_t i; + + for (i = 0; i < geometry->split; i++) { + struct packed_git *p = geometry->pack[i]; + if (string_list_has_string(names, hash_to_hex(p->hash))) + continue; + + strbuf_reset(&buf); + strbuf_addstr(&buf, pack_basename(p)); + strbuf_strip_suffix(&buf, ".pack"); + + if ((p->pack_keep) || + (string_list_has_string(&existing->kept_packs, buf.buf))) + continue; + + remove_redundant_pack(packdir, buf.buf); + } + + strbuf_release(&buf); +} + static void free_pack_geometry(struct pack_geometry *geometry) { if (!geometry) return; free(geometry->pack); - free(geometry); } struct midx_snapshot_ref_data { @@ -564,18 +669,17 @@ static void midx_snapshot_refs(struct tempfile *f) } static void midx_included_packs(struct string_list *include, - struct string_list *existing_nonkept_packs, - struct string_list *existing_kept_packs, + struct existing_packs *existing, struct string_list *names, struct pack_geometry *geometry) { struct string_list_item *item; - for_each_string_list_item(item, existing_kept_packs) + for_each_string_list_item(item, &existing->kept_packs) string_list_insert(include, xstrfmt("%s.idx", item->string)); for_each_string_list_item(item, names) string_list_insert(include, xstrfmt("pack-%s.idx", item->string)); - if (geometry) { + if (geometry->split_factor) { struct strbuf buf = STRBUF_INIT; uint32_t i; for (i = geometry->split; i < geometry->pack_nr; i++) { @@ -598,24 +702,32 @@ static void midx_included_packs(struct string_list *include, string_list_insert(include, strbuf_detach(&buf, NULL)); } - - for_each_string_list_item(item, existing_nonkept_packs) { - if (!((uintptr_t)item->util & CRUFT_PACK)) { - /* - * no need to check DELETE_PACK, since we're not - * doing an ALL_INTO_ONE repack - */ - continue; - } - string_list_insert(include, xstrfmt("%s.idx", item->string)); - } } else { - for_each_string_list_item(item, existing_nonkept_packs) { - if ((uintptr_t)item->util & DELETE_PACK) + for_each_string_list_item(item, &existing->non_kept_packs) { + if (pack_is_marked_for_deletion(item)) continue; string_list_insert(include, xstrfmt("%s.idx", item->string)); } } + + for_each_string_list_item(item, &existing->cruft_packs) { + /* + * When doing a --geometric repack, there is no need to check + * for deleted packs, since we're by definition not doing an + * ALL_INTO_ONE repack (hence no packs will be deleted). + * Otherwise we must check for and exclude any packs which are + * enqueued for deletion. + * + * So we could omit the conditional below in the --geometric + * case, but doing so is unnecessary since no packs are marked + * as pending deletion (since we only call + * `mark_packs_for_deletion()` when doing an all-into-one + * repack). + */ + if (pack_is_marked_for_deletion(item)) + continue; + string_list_insert(include, xstrfmt("%s.idx", item->string)); + } } static int write_midx_included_packs(struct string_list *include, @@ -699,8 +811,7 @@ static int write_cruft_pack(const struct pack_objects_args *args, const char *pack_prefix, const char *cruft_expiration, struct string_list *names, - struct string_list *existing_packs, - struct string_list *existing_kept_packs) + struct existing_packs *existing) { struct child_process cmd = CHILD_PROCESS_INIT; struct strbuf line = STRBUF_INIT; @@ -719,7 +830,6 @@ static int write_cruft_pack(const struct pack_objects_args *args, strvec_push(&cmd.args, "--honor-pack-keep"); strvec_push(&cmd.args, "--non-empty"); - strvec_push(&cmd.args, "--max-pack-size=0"); cmd.in = -1; @@ -743,9 +853,11 @@ static int write_cruft_pack(const struct pack_objects_args *args, in = xfdopen(cmd.in, "w"); for_each_string_list_item(item, names) fprintf(in, "%s-%s.pack\n", pack_prefix, item->string); - for_each_string_list_item(item, existing_packs) + for_each_string_list_item(item, &existing->non_kept_packs) + fprintf(in, "-%s.pack\n", item->string); + for_each_string_list_item(item, &existing->cruft_packs) fprintf(in, "-%s.pack\n", item->string); - for_each_string_list_item(item, existing_kept_packs) + for_each_string_list_item(item, &existing->kept_packs) fprintf(in, "%s.pack\n", item->string); fclose(in); @@ -777,9 +889,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) struct child_process cmd = CHILD_PROCESS_INIT; struct string_list_item *item; struct string_list names = STRING_LIST_INIT_DUP; - struct string_list existing_nonkept_packs = STRING_LIST_INIT_DUP; - struct string_list existing_kept_packs = STRING_LIST_INIT_DUP; - struct pack_geometry *geometry = NULL; + struct existing_packs existing = EXISTING_PACKS_INIT; + struct pack_geometry geometry = { 0 }; struct strbuf line = STRBUF_INIT; struct tempfile *refs_snapshot = NULL; int i, ext, ret; @@ -793,7 +904,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix) struct string_list keep_pack_list = STRING_LIST_INIT_NODUP; struct pack_objects_args po_args = {NULL}; struct pack_objects_args cruft_po_args = {NULL}; - int geometric_factor = 0; int write_midx = 0; const char *cruft_expiration = NULL; const char *expire_to = NULL; @@ -842,7 +952,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) N_("repack objects in packs marked with .keep")), OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"), N_("do not repack this pack")), - OPT_INTEGER('g', "geometric", &geometric_factor, + OPT_INTEGER('g', "geometric", &geometry.split_factor, N_("find a geometric progression with factor <N>")), OPT_BOOL('m', "write-midx", &write_midx, N_("write a multi-pack index of the resulting packs")), @@ -915,14 +1025,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix) packtmp_name = xstrfmt(".tmp-%d-pack", (int)getpid()); packtmp = mkpathdup("%s/%s", packdir, packtmp_name); - collect_pack_filenames(&existing_nonkept_packs, &existing_kept_packs, - &keep_pack_list); + collect_pack_filenames(&existing, &keep_pack_list); - if (geometric_factor) { + if (geometry.split_factor) { if (pack_everything) die(_("options '%s' and '%s' cannot be used together"), "--geometric", "-A/-a"); - init_pack_geometry(&geometry, &existing_kept_packs, &po_args); - split_pack_geometry(geometry, geometric_factor); + init_pack_geometry(&geometry, &existing, &po_args); + split_pack_geometry(&geometry); } prepare_pack_objects(&cmd, &po_args, packtmp); @@ -936,7 +1045,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) strvec_pushf(&cmd.args, "--keep-pack=%s", keep_pack_list.items[i].string); strvec_push(&cmd.args, "--non-empty"); - if (!geometry) { + if (!geometry.split_factor) { /* * We need to grab all reachable objects, including those that * are reachable from reflogs and the index. @@ -965,7 +1074,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (pack_everything & ALL_INTO_ONE) { repack_promisor_objects(&po_args, &names); - if (existing_nonkept_packs.nr && delete_redundant && + if (has_existing_non_kept_packs(&existing) && + delete_redundant && !(pack_everything & PACK_CRUFT)) { for_each_string_list_item(item, &names) { strvec_pushf(&cmd.args, "--keep-pack=%s-%s.pack", @@ -983,7 +1093,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) strvec_push(&cmd.args, "--pack-loose-unreachable"); } } - } else if (geometry) { + } else if (geometry.split_factor) { strvec_push(&cmd.args, "--stdin-packs"); strvec_push(&cmd.args, "--unpacked"); } else { @@ -991,7 +1101,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) strvec_push(&cmd.args, "--incremental"); } - if (geometry) + if (geometry.split_factor) cmd.in = -1; else cmd.no_stdin = 1; @@ -1000,17 +1110,17 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (ret) goto cleanup; - if (geometry) { + if (geometry.split_factor) { FILE *in = xfdopen(cmd.in, "w"); /* * The resulting pack should contain all objects in packs that * are going to be rolled up, but exclude objects in packs which * are being left alone. */ - for (i = 0; i < geometry->split; i++) - fprintf(in, "%s\n", pack_basename(geometry->pack[i])); - for (i = geometry->split; i < geometry->pack_nr; i++) - fprintf(in, "^%s\n", pack_basename(geometry->pack[i])); + for (i = 0; i < geometry.split; i++) + fprintf(in, "%s\n", pack_basename(geometry.pack[i])); + for (i = geometry.split; i < geometry.pack_nr; i++) + fprintf(in, "^%s\n", pack_basename(geometry.pack[i])); fclose(in); } @@ -1048,14 +1158,15 @@ int cmd_repack(int argc, const char **argv, const char *prefix) cruft_po_args.depth = po_args.depth; if (!cruft_po_args.threads) cruft_po_args.threads = po_args.threads; + if (!cruft_po_args.max_pack_size) + cruft_po_args.max_pack_size = po_args.max_pack_size; cruft_po_args.local = po_args.local; cruft_po_args.quiet = po_args.quiet; ret = write_cruft_pack(&cruft_po_args, packtmp, pack_prefix, cruft_expiration, &names, - &existing_nonkept_packs, - &existing_kept_packs); + &existing); if (ret) goto cleanup; @@ -1086,8 +1197,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) pack_prefix, NULL, &names, - &existing_nonkept_packs, - &existing_kept_packs); + &existing); if (ret) goto cleanup; } @@ -1131,31 +1241,14 @@ int cmd_repack(int argc, const char **argv, const char *prefix) } /* End of pack replacement. */ - if (delete_redundant && pack_everything & ALL_INTO_ONE) { - const int hexsz = the_hash_algo->hexsz; - for_each_string_list_item(item, &existing_nonkept_packs) { - char *sha1; - size_t len = strlen(item->string); - if (len < hexsz) - continue; - sha1 = item->string + len - hexsz; - /* - * Mark this pack for deletion, which ensures that this - * pack won't be included in a MIDX (if `--write-midx` - * was given) and that we will actually delete this pack - * (if `-d` was given). - */ - if (!string_list_has_string(&names, sha1)) - item->util = (void*)(uintptr_t)((size_t)item->util | DELETE_PACK); - } - } + if (delete_redundant && pack_everything & ALL_INTO_ONE) + mark_packs_for_deletion(&existing, &names); if (write_midx) { struct string_list include = STRING_LIST_INIT_NODUP; - midx_included_packs(&include, &existing_nonkept_packs, - &existing_kept_packs, &names, geometry); + midx_included_packs(&include, &existing, &names, &geometry); - ret = write_midx_included_packs(&include, geometry, + ret = write_midx_included_packs(&include, &geometry, refs_snapshot ? get_tempfile_path(refs_snapshot) : NULL, show_progress, write_bitmaps > 0); @@ -1172,35 +1265,11 @@ int cmd_repack(int argc, const char **argv, const char *prefix) if (delete_redundant) { int opts = 0; - for_each_string_list_item(item, &existing_nonkept_packs) { - if (!((uintptr_t)item->util & DELETE_PACK)) - continue; - remove_redundant_pack(packdir, item->string); - } + remove_redundant_existing_packs(&existing); - if (geometry) { - struct strbuf buf = STRBUF_INIT; - - uint32_t i; - for (i = 0; i < geometry->split; i++) { - struct packed_git *p = geometry->pack[i]; - if (string_list_has_string(&names, - hash_to_hex(p->hash))) - continue; - - strbuf_reset(&buf); - strbuf_addstr(&buf, pack_basename(p)); - strbuf_strip_suffix(&buf, ".pack"); - - if ((p->pack_keep) || - (string_list_has_string(&existing_kept_packs, - buf.buf))) - continue; - - remove_redundant_pack(packdir, buf.buf); - } - strbuf_release(&buf); - } + if (geometry.split_factor) + geometry_remove_redundant_packs(&geometry, &names, + &existing); if (show_progress) opts |= PRUNE_PACKED_VERBOSE; prune_packed_objects(opts); @@ -1224,9 +1293,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) cleanup: string_list_clear(&names, 1); - string_list_clear(&existing_nonkept_packs, 0); - string_list_clear(&existing_kept_packs, 0); - free_pack_geometry(geometry); + existing_packs_release(&existing); + free_pack_geometry(&geometry); return ret; } diff --git a/builtin/stash.c b/builtin/stash.c index fe64cde9ce..1ad496985a 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -362,7 +362,7 @@ static int is_path_a_directory(const char *path) } static void add_diff_to_buf(struct diff_queue_struct *q, - struct diff_options *options, + struct diff_options *options UNUSED, void *data) { int i; @@ -973,7 +973,7 @@ static int show_stash(int argc, const char **argv, const char *prefix) } log_tree_diff_flush(&rev); - ret = diff_result_code(&rev.diffopt, 0); + ret = diff_result_code(&rev.diffopt); cleanup: strvec_clear(&stash_args); free_stash_info(&info); @@ -1089,7 +1089,6 @@ static int get_untracked_files(const struct pathspec *ps, int include_untracked, */ static int check_changes_tracked_files(const struct pathspec *ps) { - int result; struct rev_info rev; struct object_id dummy; int ret = 0; @@ -1111,14 +1110,14 @@ static int check_changes_tracked_files(const struct pathspec *ps) add_head_to_pending(&rev); diff_setup_done(&rev.diffopt); - result = run_diff_index(&rev, 1); - if (diff_result_code(&rev.diffopt, result)) { + run_diff_index(&rev, DIFF_INDEX_CACHED); + if (diff_result_code(&rev.diffopt)) { ret = 1; goto done; } - result = run_diff_files(&rev, 0); - if (diff_result_code(&rev.diffopt, result)) { + run_diff_files(&rev, 0); + if (diff_result_code(&rev.diffopt)) { ret = 1; goto done; } @@ -1309,10 +1308,7 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps add_pending_object(&rev, parse_object(the_repository, &info->b_commit), ""); - if (run_diff_index(&rev, 0)) { - ret = -1; - goto done; - } + run_diff_index(&rev, 0); cp_upd_index.git_cmd = 1; strvec_pushl(&cp_upd_index.args, "update-index", diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index f6871efd95..6f3bf33e61 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -629,7 +629,6 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, char *displaypath; struct strvec diff_files_args = STRVEC_INIT; struct rev_info rev = REV_INFO_INIT; - int diff_files_result; struct strbuf buf = STRBUF_INIT; const char *git_dir; struct setup_revision_opt opt = { @@ -669,9 +668,9 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, repo_init_revisions(the_repository, &rev, NULL); rev.abbrev = 0; setup_revisions(diff_files_args.nr, diff_files_args.v, &rev, &opt); - diff_files_result = run_diff_files(&rev, 0); + run_diff_files(&rev, 0); - if (!diff_result_code(&rev.diffopt, diff_files_result)) { + if (!diff_result_code(&rev.diffopt)) { print_status(flags, ' ', path, ce_oid, displaypath); } else if (!(flags & OPT_CACHED)) { @@ -1141,7 +1140,7 @@ static int compute_summary_module_list(struct object_id *head_oid, } if (diff_cmd == DIFF_INDEX) - run_diff_index(&rev, info->cached); + run_diff_index(&rev, info->cached ? DIFF_INDEX_CACHED : 0); else run_diff_files(&rev, 0); prepare_submodule_summary(info, &list); diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 32505255a0..fef7423448 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -609,6 +609,7 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix UNUSED) { int i; struct object_id oid; + git_hash_ctx tmp_ctx; disable_replace_refs(); @@ -669,7 +670,9 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix UNUSED) the_hash_algo->init_fn(&ctx); unpack_all(); the_hash_algo->update_fn(&ctx, buffer, offset); - the_hash_algo->final_oid_fn(&oid, &ctx); + the_hash_algo->init_fn(&tmp_ctx); + the_hash_algo->clone_fn(&tmp_ctx, &ctx); + the_hash_algo->final_oid_fn(&oid, &tmp_ctx); if (strict) { write_rest(); if (fsck_finish(&fsck_options)) diff --git a/builtin/update-index.c b/builtin/update-index.c index aee3cb8cbd..97617c587e 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -856,7 +856,7 @@ static int chmod_callback(const struct option *opt, return 0; } -static int resolve_undo_clear_callback(const struct option *opt, +static int resolve_undo_clear_callback(const struct option *opt UNUSED, const char *arg, int unset) { BUG_ON_OPT_NEG(unset); @@ -890,7 +890,7 @@ static int parse_new_style_cacheinfo(const char *arg, } static enum parse_opt_result cacheinfo_callback( - struct parse_opt_ctx_t *ctx, const struct option *opt, + struct parse_opt_ctx_t *ctx, const struct option *opt UNUSED, const char *arg, int unset) { struct object_id oid; @@ -1090,6 +1090,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) resolve_undo_clear_callback), OPT_INTEGER(0, "index-version", &preferred_index_format, N_("write index in this format")), + OPT_SET_INT(0, "show-index-version", &preferred_index_format, + N_("report on-disk index format version"), -1), OPT_BOOL(0, "split-index", &split_index, N_("enable or disable split index")), OPT_BOOL(0, "untracked-cache", &untracked_cache, @@ -1182,15 +1184,20 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) getline_fn = nul_term_line ? strbuf_getline_nul : strbuf_getline_lf; if (preferred_index_format) { - if (preferred_index_format < INDEX_FORMAT_LB || - INDEX_FORMAT_UB < preferred_index_format) + if (preferred_index_format < 0) { + printf(_("%d\n"), the_index.version); + } else if (preferred_index_format < INDEX_FORMAT_LB || + INDEX_FORMAT_UB < preferred_index_format) { die("index-version %d not in range: %d..%d", preferred_index_format, INDEX_FORMAT_LB, INDEX_FORMAT_UB); - - if (the_index.version != preferred_index_format) - the_index.cache_changed |= SOMETHING_CHANGED; - the_index.version = preferred_index_format; + } else { + if (the_index.version != preferred_index_format) + the_index.cache_changed |= SOMETHING_CHANGED; + report(_("index-version: was %d, set to %d"), + the_index.version, preferred_index_format); + the_index.version = preferred_index_format; + } } if (read_from_stdin) { diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 242102273e..c0c4e65e6f 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -311,8 +311,8 @@ static void report_ok(const char *command) fflush(stdout); } -static void parse_cmd_option(struct ref_transaction *transaction, - const char *next, const char *end) +static void parse_cmd_option(struct ref_transaction *transaction UNUSED, + const char *next, const char *end UNUSED) { const char *rest; if (skip_prefix(next, "no-deref", &rest) && *rest == line_termination) @@ -321,8 +321,8 @@ static void parse_cmd_option(struct ref_transaction *transaction, die("option unknown: %s", next); } -static void parse_cmd_start(struct ref_transaction *transaction, - const char *next, const char *end) +static void parse_cmd_start(struct ref_transaction *transaction UNUSED, + const char *next, const char *end UNUSED) { if (*next != line_termination) die("start: extra input: %s", next); @@ -330,7 +330,7 @@ static void parse_cmd_start(struct ref_transaction *transaction, } static void parse_cmd_prepare(struct ref_transaction *transaction, - const char *next, const char *end) + const char *next, const char *end UNUSED) { struct strbuf error = STRBUF_INIT; if (*next != line_termination) @@ -341,7 +341,7 @@ static void parse_cmd_prepare(struct ref_transaction *transaction, } static void parse_cmd_abort(struct ref_transaction *transaction, - const char *next, const char *end) + const char *next, const char *end UNUSED) { struct strbuf error = STRBUF_INIT; if (*next != line_termination) @@ -352,7 +352,7 @@ static void parse_cmd_abort(struct ref_transaction *transaction, } static void parse_cmd_commit(struct ref_transaction *transaction, - const char *next, const char *end) + const char *next, const char *end UNUSED) { struct strbuf error = STRBUF_INIT; if (*next != line_termination) diff --git a/builtin/var.c b/builtin/var.c index 74161bdf1c..8cf7dd9e2e 100644 --- a/builtin/var.c +++ b/builtin/var.c @@ -66,7 +66,7 @@ static char *git_attr_val_system(int ident_flag UNUSED) static char *git_attr_val_global(int ident_flag UNUSED) { - char *file = xstrdup(git_attr_global_file()); + char *file = xstrdup_or_null(git_attr_global_file()); if (file) { normalize_path_copy(file, file); return file; diff --git a/builtin/worktree.c b/builtin/worktree.c index 4cd01842de..62b7e26f4b 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -628,10 +628,10 @@ static void print_preparing_worktree_line(int detach, * * Returns 0 on failure and non-zero on success. */ -static int first_valid_ref(const char *refname, - const struct object_id *oid, - int flags, - void *cb_data) +static int first_valid_ref(const char *refname UNUSED, + const struct object_id *oid UNUSED, + int flags UNUSED, + void *cb_data UNUSED) { return 1; } @@ -696,7 +696,7 @@ static int can_use_remote_refs(const struct add_opts *opts) return 1; } else if (!opts->force && remote_get(NULL)) { die(_("No local or remote refs exist despite at least one remote\n" - "present, stopping; use 'add -f' to overide or fetch a remote first")); + "present, stopping; use 'add -f' to override or fetch a remote first")); } return 0; } diff --git a/bulk-checkin.c b/bulk-checkin.c index 73bff3a23d..92b9c8598b 100644 --- a/bulk-checkin.c +++ b/bulk-checkin.c @@ -268,6 +268,7 @@ static int deflate_to_pack(struct bulk_checkin_packfile *state, type, size); the_hash_algo->init_fn(&ctx); the_hash_algo->update_fn(&ctx, obuf, header_len); + the_hash_algo->init_fn(&checkpoint.ctx); /* Note: idx is non-NULL when we are writing */ if ((flags & HASH_WRITE_OBJECT) != 0) diff --git a/bundle-uri.c b/bundle-uri.c index 4b5c49b93d..8492fffd2f 100644 --- a/bundle-uri.c +++ b/bundle-uri.c @@ -20,7 +20,7 @@ static struct { { BUNDLE_HEURISTIC_CREATIONTOKEN, "creationToken" }, }; -static int compare_bundles(const void *hashmap_cmp_fn_data, +static int compare_bundles(const void *hashmap_cmp_fn_data UNUSED, const struct hashmap_entry *he1, const struct hashmap_entry *he2, const void *id) @@ -45,7 +45,7 @@ void init_bundle_list(struct bundle_list *list) } static int clear_remote_bundle_info(struct remote_bundle_info *bundle, - void *data) + void *data UNUSED) { FREE_AND_NULL(bundle->id); FREE_AND_NULL(bundle->uri); @@ -779,7 +779,7 @@ static int unbundle_all_bundles(struct repository *r, return 0; } -static int unlink_bundle(struct remote_bundle_info *info, void *data) +static int unlink_bundle(struct remote_bundle_info *info, void *data UNUSED) { if (info->file) unlink_or_warn(info->file); diff --git a/ci/config/README b/ci/config/README new file mode 100644 index 0000000000..8de3a04e32 --- /dev/null +++ b/ci/config/README @@ -0,0 +1,14 @@ +You can configure some aspects of the GitHub Actions-based CI on a +per-repository basis by setting "variables" and "secrets" from with the +GitHub web interface. These can be found at: + + https://github.com/<user>/git/settings/secrets/actions + +The following variables can be used: + + - CI_BRANCHES + + By default, CI is run when any branch is pushed. If this variable is + non-empty, then only the branches it lists will run CI. Branch names + should be separated by spaces, and should use their shortened form + (e.g., "main", not "refs/heads/main"). diff --git a/ci/config/allow-ref.sample b/ci/config/allow-ref.sample deleted file mode 100755 index af0e076f8a..0000000000 --- a/ci/config/allow-ref.sample +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -# -# Sample script for enabling/disabling GitHub Actions CI runs on -# particular refs. By default, CI is run for all branches pushed to -# GitHub. You can override this by dropping the ".sample" from the script, -# editing it, committing, and pushing the result to the "ci-config" branch of -# your repository: -# -# git checkout -b ci-config -# cp allow-ref.sample allow-ref -# $EDITOR allow-ref -# git add allow-ref -# git commit -am "implement my ci preferences" -# git push -# -# This script will then be run when any refs are pushed to that repository. It -# gets the fully qualified refname as the first argument, and should exit with -# success only for refs for which you want to run CI. - -case "$1" in -# allow one-off tests by pushing to "for-ci" or "for-ci/mybranch" -refs/heads/for-ci*) true ;; -# always build your integration branch -refs/heads/my-integration-branch) true ;; -# don't build any other branches or tags -*) false ;; -esac @@ -280,6 +280,8 @@ linux-leaks) ;; linux-asan-ubsan) export SANITIZE=address,undefined + export NO_SVN_TESTS=LetsSaveSomeTime + MAKEFLAGS="$MAKEFLAGS NO_PYTHON=YepBecauseP4FlakesTooOften" ;; esac diff --git a/commit-graph.c b/commit-graph.c index 0aa1640d15..5e8a3a5085 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -128,6 +128,16 @@ timestamp_t commit_graph_generation(const struct commit *c) return GENERATION_NUMBER_INFINITY; } +static timestamp_t commit_graph_generation_from_graph(const struct commit *c) +{ + struct commit_graph_data *data = + commit_graph_data_slab_peek(&commit_graph_data_slab, c); + + if (!data || data->graph_pos == COMMIT_NOT_FROM_GRAPH) + return GENERATION_NUMBER_INFINITY; + return data->generation; +} + static struct commit_graph_data *commit_graph_data_at(const struct commit *c) { unsigned int i, nth_slab; @@ -1568,12 +1578,14 @@ static void compute_topological_levels(struct write_commit_graph_context *ctx) stop_progress(&ctx->progress); } -static timestamp_t get_generation_from_graph_data(struct commit *c, void *data) +static timestamp_t get_generation_from_graph_data(struct commit *c, + void *data UNUSED) { return commit_graph_data_at(c)->generation; } -static void set_generation_v2(struct commit *c, timestamp_t t, void *data) +static void set_generation_v2(struct commit *c, timestamp_t t, + void *data UNUSED) { struct commit_graph_data *g = commit_graph_data_at(c); g->generation = t; @@ -1587,7 +1599,6 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx) .commits = &ctx->commits, .get_generation = get_generation_from_graph_data, .set_generation = set_generation_v2, - .data = ctx, }; if (ctx->report_progress) @@ -1616,7 +1627,7 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx) } static void set_generation_in_graph_data(struct commit *c, timestamp_t t, - void *data) + void *data UNUSED) { commit_graph_data_at(c)->generation = t; } @@ -2550,9 +2561,6 @@ static void graph_report(const char *fmt, ...) va_end(ap); } -#define GENERATION_ZERO_EXISTS 1 -#define GENERATION_NUMBER_EXISTS 2 - static int commit_graph_checksum_valid(struct commit_graph *g) { return hashfile_checksum_valid(g->data, g->data_len); @@ -2565,7 +2573,8 @@ static int verify_one_commit_graph(struct repository *r, { uint32_t i, cur_fanout_pos = 0; struct object_id prev_oid, cur_oid; - int generation_zero = 0; + struct commit *seen_gen_zero = NULL; + struct commit *seen_gen_non_zero = NULL; verify_commit_graph_error = verify_commit_graph_lite(g); if (verify_commit_graph_error) @@ -2659,7 +2668,7 @@ static int verify_one_commit_graph(struct repository *r, oid_to_hex(&graph_parents->item->object.oid), oid_to_hex(&odb_parents->item->object.oid)); - generation = commit_graph_generation(graph_parents->item); + generation = commit_graph_generation_from_graph(graph_parents->item); if (generation > max_generation) max_generation = generation; @@ -2671,16 +2680,12 @@ static int verify_one_commit_graph(struct repository *r, graph_report(_("commit-graph parent list for commit %s terminates early"), oid_to_hex(&cur_oid)); - if (!commit_graph_generation(graph_commit)) { - if (generation_zero == GENERATION_NUMBER_EXISTS) - graph_report(_("commit-graph has generation number zero for commit %s, but non-zero elsewhere"), - oid_to_hex(&cur_oid)); - generation_zero = GENERATION_ZERO_EXISTS; - } else if (generation_zero == GENERATION_ZERO_EXISTS) - graph_report(_("commit-graph has non-zero generation number for commit %s, but zero elsewhere"), - oid_to_hex(&cur_oid)); + if (commit_graph_generation_from_graph(graph_commit)) + seen_gen_non_zero = graph_commit; + else + seen_gen_zero = graph_commit; - if (generation_zero == GENERATION_ZERO_EXISTS) + if (seen_gen_zero) continue; /* @@ -2706,6 +2711,12 @@ static int verify_one_commit_graph(struct repository *r, odb_commit->date); } + if (seen_gen_zero && seen_gen_non_zero) + graph_report(_("commit-graph has both zero and non-zero " + "generations (e.g., commits '%s' and '%s')"), + oid_to_hex(&seen_gen_zero->object.oid), + oid_to_hex(&seen_gen_non_zero->object.oid)); + return verify_commit_graph_error; } diff --git a/compat/fsmonitor/fsm-health-darwin.c b/compat/fsmonitor/fsm-health-darwin.c index 5b1709d63f..c2afcbe6c8 100644 --- a/compat/fsmonitor/fsm-health-darwin.c +++ b/compat/fsmonitor/fsm-health-darwin.c @@ -4,21 +4,21 @@ #include "fsm-health.h" #include "fsmonitor--daemon.h" -int fsm_health__ctor(struct fsmonitor_daemon_state *state) +int fsm_health__ctor(struct fsmonitor_daemon_state *state UNUSED) { return 0; } -void fsm_health__dtor(struct fsmonitor_daemon_state *state) +void fsm_health__dtor(struct fsmonitor_daemon_state *state UNUSED) { return; } -void fsm_health__loop(struct fsmonitor_daemon_state *state) +void fsm_health__loop(struct fsmonitor_daemon_state *state UNUSED) { return; } -void fsm_health__stop_async(struct fsmonitor_daemon_state *state) +void fsm_health__stop_async(struct fsmonitor_daemon_state *state UNUSED) { } diff --git a/compat/fsmonitor/fsm-ipc-win32.c b/compat/fsmonitor/fsm-ipc-win32.c index 8928fa93ce..41984ea48e 100644 --- a/compat/fsmonitor/fsm-ipc-win32.c +++ b/compat/fsmonitor/fsm-ipc-win32.c @@ -6,6 +6,6 @@ const char *fsmonitor_ipc__get_path(struct repository *r) { static char *ret; if (!ret) - ret = git_pathdup("fsmonitor--daemon.ipc"); + ret = repo_git_path(r, "fsmonitor--daemon.ipc"); return ret; } diff --git a/compat/fsmonitor/fsm-listen-darwin.c b/compat/fsmonitor/fsm-listen-darwin.c index 36c7e13281..11b56d3ef1 100644 --- a/compat/fsmonitor/fsm-listen-darwin.c +++ b/compat/fsmonitor/fsm-listen-darwin.c @@ -191,12 +191,12 @@ static void my_add_path(struct fsmonitor_batch *batch, const char *path) } -static void fsevent_callback(ConstFSEventStreamRef streamRef, +static void fsevent_callback(ConstFSEventStreamRef streamRef UNUSED, void *ctx, size_t num_of_events, void *event_paths, const FSEventStreamEventFlags event_flags[], - const FSEventStreamEventId event_ids[]) + const FSEventStreamEventId event_ids[] UNUSED) { struct fsmonitor_daemon_state *state = ctx; struct fsm_listen_data *data = state->listen_data; diff --git a/compat/fsmonitor/fsm-listen-win32.c b/compat/fsmonitor/fsm-listen-win32.c index a361a7db20..90a2412284 100644 --- a/compat/fsmonitor/fsm-listen-win32.c +++ b/compat/fsmonitor/fsm-listen-win32.c @@ -289,8 +289,7 @@ void fsm_listen__stop_async(struct fsmonitor_daemon_state *state) SetEvent(state->listen_data->hListener[LISTENER_SHUTDOWN]); } -static struct one_watch *create_watch(struct fsmonitor_daemon_state *state, - const char *path) +static struct one_watch *create_watch(const char *path) { struct one_watch *watch = NULL; DWORD desired_access = FILE_LIST_DIRECTORY; @@ -361,8 +360,7 @@ static void destroy_watch(struct one_watch *watch) free(watch); } -static int start_rdcw_watch(struct fsm_listen_data *data, - struct one_watch *watch) +static int start_rdcw_watch(struct one_watch *watch) { DWORD dwNotifyFilter = FILE_NOTIFY_CHANGE_FILE_NAME | @@ -735,11 +733,11 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state) state->listen_error_code = 0; - if (start_rdcw_watch(data, data->watch_worktree) == -1) + if (start_rdcw_watch(data->watch_worktree) == -1) goto force_error_stop; if (data->watch_gitdir && - start_rdcw_watch(data, data->watch_gitdir) == -1) + start_rdcw_watch(data->watch_gitdir) == -1) goto force_error_stop; for (;;) { @@ -755,7 +753,7 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state) } if (result == -2) { /* retryable error */ - if (start_rdcw_watch(data, data->watch_worktree) == -1) + if (start_rdcw_watch(data->watch_worktree) == -1) goto force_error_stop; continue; } @@ -763,7 +761,7 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state) /* have data */ if (process_worktree_events(state) == LISTENER_SHUTDOWN) goto force_shutdown; - if (start_rdcw_watch(data, data->watch_worktree) == -1) + if (start_rdcw_watch(data->watch_worktree) == -1) goto force_error_stop; continue; } @@ -776,7 +774,7 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state) } if (result == -2) { /* retryable error */ - if (start_rdcw_watch(data, data->watch_gitdir) == -1) + if (start_rdcw_watch(data->watch_gitdir) == -1) goto force_error_stop; continue; } @@ -784,7 +782,7 @@ void fsm_listen__loop(struct fsmonitor_daemon_state *state) /* have data */ if (process_gitdir_events(state) == LISTENER_SHUTDOWN) goto force_shutdown; - if (start_rdcw_watch(data, data->watch_gitdir) == -1) + if (start_rdcw_watch(data->watch_gitdir) == -1) goto force_error_stop; continue; } @@ -821,16 +819,14 @@ int fsm_listen__ctor(struct fsmonitor_daemon_state *state) data->hEventShutdown = CreateEvent(NULL, TRUE, FALSE, NULL); - data->watch_worktree = create_watch(state, - state->path_worktree_watch.buf); + data->watch_worktree = create_watch(state->path_worktree_watch.buf); if (!data->watch_worktree) goto failed; check_for_shortnames(data->watch_worktree); if (state->nr_paths_watching > 1) { - data->watch_gitdir = create_watch(state, - state->path_gitdir_watch.buf); + data->watch_gitdir = create_watch(state->path_gitdir_watch.buf); if (!data->watch_gitdir) goto failed; } diff --git a/compat/fsmonitor/fsm-path-utils-win32.c b/compat/fsmonitor/fsm-path-utils-win32.c index c8a3e9dcdb..f4f9cc1f33 100644 --- a/compat/fsmonitor/fsm-path-utils-win32.c +++ b/compat/fsmonitor/fsm-path-utils-win32.c @@ -132,7 +132,8 @@ int fsmonitor__is_fs_remote(const char *path) /* * No-op for now. */ -int fsmonitor__get_alias(const char *path, struct alias_info *info) +int fsmonitor__get_alias(const char *path UNUSED, + struct alias_info *info UNUSED) { return 0; } @@ -140,8 +141,8 @@ int fsmonitor__get_alias(const char *path, struct alias_info *info) /* * No-op for now. */ -char *fsmonitor__resolve_alias(const char *path, - const struct alias_info *info) +char *fsmonitor__resolve_alias(const char *path UNUSED, + const struct alias_info *info UNUSED) { return NULL; } diff --git a/compat/fsmonitor/fsm-settings-win32.c b/compat/fsmonitor/fsm-settings-win32.c index b6f6744494..0f2aa321f6 100644 --- a/compat/fsmonitor/fsm-settings-win32.c +++ b/compat/fsmonitor/fsm-settings-win32.c @@ -25,7 +25,7 @@ static enum fsmonitor_reason check_vfs4git(struct repository *r) return FSMONITOR_REASON_OK; } -enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc) +enum fsmonitor_reason fsm_os__incompatible(struct repository *r, int ipc UNUSED) { enum fsmonitor_reason reason; diff --git a/compat/terminal.c b/compat/terminal.c index 83d95e8656..0afda730f2 100644 --- a/compat/terminal.c +++ b/compat/terminal.c @@ -479,10 +479,13 @@ struct escape_sequence_entry { }; static int sequence_entry_cmp(const void *hashmap_cmp_fn_data UNUSED, - const struct escape_sequence_entry *e1, - const struct escape_sequence_entry *e2, + const struct hashmap_entry *he1, + const struct hashmap_entry *he2, const void *keydata) { + const struct escape_sequence_entry + *e1 = container_of(he1, const struct escape_sequence_entry, entry), + *e2 = container_of(he2, const struct escape_sequence_entry, entry); return strcmp(e1->sequence, keydata ? keydata : e2->sequence); } @@ -496,8 +499,7 @@ static int is_known_escape_sequence(const char *sequence) struct strbuf buf = STRBUF_INIT; char *p, *eol; - hashmap_init(&sequences, (hashmap_cmp_fn)sequence_entry_cmp, - NULL, 0); + hashmap_init(&sequences, sequence_entry_cmp, NULL, 0); strvec_pushl(&cp.args, "infocmp", "-L", "-1", NULL); if (pipe_command(&cp, NULL, 0, &buf, 0, NULL, 0)) @@ -1801,6 +1801,11 @@ static int git_default_core_config(const char *var, const char *value, return 0; } + if (!strcmp(var, "core.maxtreedepth")) { + max_allowed_tree_depth = git_config_int(var, value, ctx->kvi); + return 0; + } + /* Add other config variables here and to Documentation/config.txt. */ return platform_core_config(var, value, ctx, cb); } diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 133ec92bfa..47fd664ea5 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -28,6 +28,7 @@ # completion style. For example '!f() { : git commit ; ... }; f' will # tell the completion to use commit completion. This also works with aliases # of form "!sh -c '...'". For example, "!sh -c ': git commit ; ... '". +# Be sure to add a space between the command name and the ';'. # # If you have a command that is not part of git, but you would still # like completion, you can use __git_complete: @@ -1607,7 +1608,7 @@ _git_checkout () if [ -n "$(__git_find_on_cmdline "-b -B -d --detach --orphan")" ]; then __git_complete_refs --mode="refs" - elif [ -n "$(__git_find_on_cmdline "--track")" ]; then + elif [ -n "$(__git_find_on_cmdline "-t --track")" ]; then __git_complete_refs --mode="remote-heads" else __git_complete_refs $dwim_opt --mode="refs" @@ -1677,6 +1678,11 @@ _git_clone () __git_untracked_file_modes="all no normal" +__git_trailer_tokens () +{ + __git config --name-only --get-regexp '^trailer\..*\.key$' | cut -d. -f 2- | rev | cut -d. -f2- | rev +} + _git_commit () { case "$prev" in @@ -1701,6 +1707,10 @@ _git_commit () __gitcomp "$__git_untracked_file_modes" "" "${cur##--untracked-files=}" return ;; + --trailer=*) + __gitcomp_nl "$(__git_trailer_tokens)" "" "${cur##--trailer=}" ":" + return + ;; --*) __gitcomp_builtin commit return @@ -2514,7 +2524,7 @@ _git_switch () if [ -n "$(__git_find_on_cmdline "-c -C -d --detach")" ]; then __git_complete_refs --mode="refs" - elif [ -n "$(__git_find_on_cmdline "--track")" ]; then + elif [ -n "$(__git_find_on_cmdline "-t --track")" ]; then __git_complete_refs --mode="remote-heads" else __git_complete_refs $dwim_opt --mode="heads" diff --git a/contrib/credential/libsecret/git-credential-libsecret.c b/contrib/credential/libsecret/git-credential-libsecret.c index ef681f29d5..215a81d8ba 100644 --- a/contrib/credential/libsecret/git-credential-libsecret.c +++ b/contrib/credential/libsecret/git-credential-libsecret.c @@ -39,6 +39,8 @@ struct credential { char *path; char *username; char *password; + char *password_expiry_utc; + char *oauth_refresh_token; }; #define CREDENTIAL_INIT { 0 } @@ -52,8 +54,29 @@ struct credential_operation { #define CREDENTIAL_OP_END { NULL, NULL } +static void credential_clear(struct credential *c); + /* ----------------- Secret Service functions ----------------- */ +static const SecretSchema schema = { + "org.git.Password", + /* Ignore schema name during search for backwards compatibility */ + SECRET_SCHEMA_DONT_MATCH_NAME, + { + /* + * libsecret assumes attribute values are non-confidential and + * unchanging, so we can't include oauth_refresh_token or + * password_expiry_utc. + */ + { "user", SECRET_SCHEMA_ATTRIBUTE_STRING }, + { "object", SECRET_SCHEMA_ATTRIBUTE_STRING }, + { "protocol", SECRET_SCHEMA_ATTRIBUTE_STRING }, + { "port", SECRET_SCHEMA_ATTRIBUTE_INTEGER }, + { "server", SECRET_SCHEMA_ATTRIBUTE_STRING }, + { NULL, 0 }, + } +}; + static char *make_label(struct credential *c) { if (c->port) @@ -101,7 +124,7 @@ static int keyring_get(struct credential *c) attributes = make_attr_list(c); items = secret_service_search_sync(service, - SECRET_SCHEMA_COMPAT_NETWORK, + &schema, attributes, SECRET_SEARCH_LOAD_SECRETS | SECRET_SEARCH_UNLOCK, NULL, @@ -117,6 +140,7 @@ static int keyring_get(struct credential *c) SecretItem *item; SecretValue *secret; const char *s; + gchar **parts; item = items->data; secret = secret_item_get_secret(item); @@ -130,8 +154,27 @@ static int keyring_get(struct credential *c) s = secret_value_get_text(secret); if (s) { - g_free(c->password); - c->password = g_strdup(s); + /* + * Passwords and other attributes encoded in following format: + * hunter2 + * password_expiry_utc=1684189401 + * oauth_refresh_token=xyzzy + */ + parts = g_strsplit(s, "\n", 0); + if (g_strv_length(parts) >= 1) { + g_free(c->password); + c->password = g_strdup(parts[0]); + } + for (int i = 1; i < g_strv_length(parts); i++) { + if (g_str_has_prefix(parts[i], "password_expiry_utc=")) { + g_free(c->password_expiry_utc); + c->password_expiry_utc = g_strdup(&parts[i][20]); + } else if (g_str_has_prefix(parts[i], "oauth_refresh_token=")) { + g_free(c->oauth_refresh_token); + c->oauth_refresh_token = g_strdup(&parts[i][20]); + } + } + g_strfreev(parts); } g_hash_table_unref(attributes); @@ -148,6 +191,7 @@ static int keyring_store(struct credential *c) char *label = NULL; GHashTable *attributes = NULL; GError *error = NULL; + GString *secret = NULL; /* * Sanity check that what we are storing is actually sensible. @@ -162,13 +206,23 @@ static int keyring_store(struct credential *c) label = make_label(c); attributes = make_attr_list(c); - secret_password_storev_sync(SECRET_SCHEMA_COMPAT_NETWORK, + secret = g_string_new(c->password); + if (c->password_expiry_utc) { + g_string_append_printf(secret, "\npassword_expiry_utc=%s", + c->password_expiry_utc); + } + if (c->oauth_refresh_token) { + g_string_append_printf(secret, "\noauth_refresh_token=%s", + c->oauth_refresh_token); + } + secret_password_storev_sync(&schema, attributes, NULL, label, - c->password, + secret->str, NULL, &error); + g_string_free(secret, TRUE); g_free(label); g_hash_table_unref(attributes); @@ -185,6 +239,7 @@ static int keyring_erase(struct credential *c) { GHashTable *attributes = NULL; GError *error = NULL; + struct credential existing = CREDENTIAL_INIT; /* * Sanity check that we actually have something to match @@ -197,8 +252,22 @@ static int keyring_erase(struct credential *c) if (!c->protocol && !c->host && !c->path && !c->username) return EXIT_FAILURE; + if (c->password) { + existing.host = g_strdup(c->host); + existing.path = g_strdup(c->path); + existing.port = c->port; + existing.protocol = g_strdup(c->protocol); + existing.username = g_strdup(c->username); + keyring_get(&existing); + if (existing.password && strcmp(c->password, existing.password)) { + credential_clear(&existing); + return EXIT_SUCCESS; + } + credential_clear(&existing); + } + attributes = make_attr_list(c); - secret_password_clearv_sync(SECRET_SCHEMA_COMPAT_NETWORK, + secret_password_clearv_sync(&schema, attributes, NULL, &error); @@ -238,6 +307,8 @@ static void credential_clear(struct credential *c) g_free(c->path); g_free(c->username); g_free(c->password); + g_free(c->password_expiry_utc); + g_free(c->oauth_refresh_token); credential_init(c); } @@ -284,11 +355,19 @@ static int credential_read(struct credential *c) } else if (!strcmp(key, "username")) { g_free(c->username); c->username = g_strdup(value); + } else if (!strcmp(key, "password_expiry_utc")) { + g_free(c->password_expiry_utc); + c->password_expiry_utc = g_strdup(value); } else if (!strcmp(key, "password")) { g_free(c->password); c->password = g_strdup(value); while (*value) *value++ = '\0'; + } else if (!strcmp(key, "oauth_refresh_token")) { + g_free(c->oauth_refresh_token); + c->oauth_refresh_token = g_strdup(value); + while (*value) + *value++ = '\0'; } /* * Ignore other lines; we don't know what they mean, but @@ -314,6 +393,10 @@ static void credential_write(const struct credential *c) /* only write username/password, if set */ credential_write_item(stdout, "username", c->username); credential_write_item(stdout, "password", c->password); + credential_write_item(stdout, "password_expiry_utc", + c->password_expiry_utc); + credential_write_item(stdout, "oauth_refresh_token", + c->oauth_refresh_token); } static void usage(const char *name) diff --git a/contrib/credential/wincred/git-credential-wincred.c b/contrib/credential/wincred/git-credential-wincred.c index 96f10613ae..4cd56c42e2 100644 --- a/contrib/credential/wincred/git-credential-wincred.c +++ b/contrib/credential/wincred/git-credential-wincred.c @@ -109,7 +109,18 @@ static int match_part_last(LPCWSTR *ptarget, LPCWSTR want, LPCWSTR delim) return match_part_with_last(ptarget, want, delim, 1); } -static int match_cred(const CREDENTIALW *cred) +static int match_cred_password(const CREDENTIALW *cred) { + int ret; + WCHAR *cred_password = xmalloc(cred->CredentialBlobSize); + wcsncpy_s(cred_password, cred->CredentialBlobSize, + (LPCWSTR)cred->CredentialBlob, + cred->CredentialBlobSize / sizeof(WCHAR)); + ret = !wcscmp(cred_password, password); + free(cred_password); + return ret; +} + +static int match_cred(const CREDENTIALW *cred, int match_password) { LPCWSTR target = cred->TargetName; if (wusername && wcscmp(wusername, cred->UserName ? cred->UserName : L"")) @@ -119,7 +130,8 @@ static int match_cred(const CREDENTIALW *cred) match_part(&target, protocol, L"://") && match_part_last(&target, wusername, L"@") && match_part(&target, host, L"/") && - match_part(&target, path, L""); + match_part(&target, path, L"") && + (!match_password || match_cred_password(cred)); } static void get_credential(void) @@ -134,7 +146,7 @@ static void get_credential(void) /* search for the first credential that matches username */ for (i = 0; i < num_creds; ++i) - if (match_cred(creds[i])) { + if (match_cred(creds[i], 0)) { write_item("username", creds[i]->UserName, creds[i]->UserName ? wcslen(creds[i]->UserName) : 0); write_item("password", @@ -196,7 +208,7 @@ static void erase_credential(void) return; for (i = 0; i < num_creds; ++i) { - if (match_cred(creds[i])) + if (match_cred(creds[i], password != NULL)) CredDeleteW(creds[i]->TargetName, creds[i]->Type, 0); } diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh index 7db4c45676..e0c5d3b0de 100755 --- a/contrib/subtree/git-subtree.sh +++ b/contrib/subtree/git-subtree.sh @@ -33,19 +33,19 @@ git subtree split --prefix=<prefix> [<commit>] git subtree pull --prefix=<prefix> <repository> <ref> git subtree push --prefix=<prefix> <repository> <refspec> -- -h,help show the help -q,quiet quiet -d,debug show debug messages +h,help! show the help +q,quiet! quiet +d,debug! show debug messages P,prefix= the name of the subdir to split out options for 'split' (also: 'push') annotate= add a prefix to commit message of new commits -b,branch= create a new branch from the split subtree +b,branch!= create a new branch from the split subtree ignore-joins ignore prior --rejoin commits onto= try connecting new tree to an existing one rejoin merge the new branch back into HEAD options for 'add' and 'merge' (also: 'pull', 'split --rejoin', and 'push --rejoin') squash merge subtree changes as a single commit -m,message= use the given message as the commit message for the merge commit +m,message!= use the given message as the commit message for the merge commit " indent=0 diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh index 341c169eca..49a21dd7c9 100755 --- a/contrib/subtree/t/t7900-subtree.sh +++ b/contrib/subtree/t/t7900-subtree.sh @@ -71,7 +71,7 @@ test_expect_success 'shows short help text for -h' ' test_expect_code 129 git subtree -h >out 2>err && test_must_be_empty err && grep -e "^ *or: git subtree pull" out && - grep -e --annotate out + grep -F -e "--[no-]annotate" out ' # diff --git a/credential.c b/credential.c index d664754163..18098bd35e 100644 --- a/credential.c +++ b/credential.c @@ -88,8 +88,8 @@ static int proto_is_http(const char *s) static void credential_describe(struct credential *c, struct strbuf *out); static void credential_format(struct credential *c, struct strbuf *out); -static int select_all(const struct urlmatch_item *a, - const struct urlmatch_item *b) +static int select_all(const struct urlmatch_item *a UNUSED, + const struct urlmatch_item *b UNUSED) { return 0; } diff --git a/csum-file.c b/csum-file.c index cd01713244..870748e016 100644 --- a/csum-file.c +++ b/csum-file.c @@ -207,7 +207,7 @@ int hashfile_truncate(struct hashfile *f, struct hashfile_checkpoint *checkpoint lseek(f->fd, offset, SEEK_SET) != offset) return -1; f->total = offset; - f->ctx = checkpoint->ctx; + the_hash_algo->clone_fn(&f->ctx, &checkpoint->ctx); f->offset = 0; /* hashflush() was called in checkpoint */ return 0; } diff --git a/diff-lib.c b/diff-lib.c index 6b0c6a7180..5848e4f9ca 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -36,14 +36,14 @@ * exists for ce that is a submodule -- it is a submodule that is not * checked out). Return negative for an error. */ -static int check_removed(const struct index_state *istate, const struct cache_entry *ce, struct stat *st) +static int check_removed(const struct cache_entry *ce, struct stat *st) { - assert(is_fsmonitor_refreshed(istate)); - if (!(ce->ce_flags & CE_FSMONITOR_VALID) && lstat(ce->name, st) < 0) { + if (lstat(ce->name, st) < 0) { if (!is_missing_file_error(errno)) return -1; return 1; } + if (has_symlink_leading_path(ce->name, ce_namelen(ce))) return 1; if (S_ISDIR(st->st_mode)) { @@ -96,7 +96,7 @@ static int match_stat_with_submodule(struct diff_options *diffopt, return changed; } -int run_diff_files(struct rev_info *revs, unsigned int option) +void run_diff_files(struct rev_info *revs, unsigned int option) { int entries, i; int diff_unmerged_stage = revs->max_count; @@ -149,7 +149,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option) memset(&(dpath->parent[0]), 0, sizeof(struct combine_diff_parent)*5); - changed = check_removed(istate, ce, &st); + changed = check_removed(ce, &st); if (!changed) wt_mode = ce_mode_from_stat(ce, st.st_mode); else { @@ -229,7 +229,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option) } else { struct stat st; - changed = check_removed(istate, ce, &st); + changed = check_removed(ce, &st); if (changed) { if (changed < 0) { perror(ce->name); @@ -272,7 +272,6 @@ int run_diff_files(struct rev_info *revs, unsigned int option) diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); trace_performance_since(start, "diff-files"); - return 0; } /* @@ -304,7 +303,7 @@ static int get_stat_data(const struct index_state *istate, if (!cached && !ce_uptodate(ce)) { int changed; struct stat st; - changed = check_removed(istate, ce, &st); + changed = check_removed(ce, &st); if (changed < 0) return -1; else if (changed) { @@ -606,7 +605,7 @@ void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb) free_commit_list(merge_bases); } -int run_diff_index(struct rev_info *revs, unsigned int option) +void run_diff_index(struct rev_info *revs, unsigned int option) { struct object_array_entry *ent; int cached = !!(option & DIFF_INDEX_CACHED); @@ -640,7 +639,6 @@ int run_diff_index(struct rev_info *revs, unsigned int option) diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); trace_performance_leave("diff-index"); - return 0; } int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt) @@ -682,7 +680,7 @@ int index_differs_from(struct repository *r, rev.diffopt.flags.ignore_submodules = flags->ignore_submodules; } rev.diffopt.ita_invisible_in_index = ita_invisible_in_index; - run_diff_index(&rev, 1); + run_diff_index(&rev, DIFF_INDEX_CACHED); has_changes = rev.diffopt.flags.has_changes; release_revisions(&rev); return (has_changes != 0); diff --git a/diff-no-index.c b/diff-no-index.c index 4771cf02aa..e7041b89e3 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -232,6 +232,7 @@ static int queue_diff(struct diff_options *o, if (o->flags.reverse_diff) { SWAP(mode1, mode2); SWAP(name1, name2); + SWAP(special1, special2); } d1 = noindex_filespec(name1, mode1, special1); @@ -364,7 +365,7 @@ int diff_no_index(struct rev_info *revs, * The return code for --no-index imitates diff(1): * 0 = no changes, 1 = changes, else error */ - ret = diff_result_code(&revs->diffopt, 0); + ret = diff_result_code(&revs->diffopt); out: for (i = 0; i < ARRAY_SIZE(to_free); i++) @@ -65,6 +65,7 @@ int diff_auto_refresh_index = 1; static int diff_mnemonic_prefix; static int diff_no_prefix; static int diff_relative; +static int diff_stat_name_width; static int diff_stat_graph_width; static int diff_dirstat_permille_default = 30; static struct diff_options default_diff_options; @@ -410,6 +411,10 @@ int git_diff_ui_config(const char *var, const char *value, diff_relative = git_config_bool(var, value); return 0; } + if (!strcmp(var, "diff.statnamewidth")) { + diff_stat_name_width = git_config_int(var, value, ctx->kvi); + return 0; + } if (!strcmp(var, "diff.statgraphwidth")) { diff_stat_graph_width = git_config_int(var, value, ctx->kvi); return 0; @@ -2704,12 +2709,14 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options) number_width = decimal_width(max_change) > number_width ? decimal_width(max_change) : number_width; + if (options->stat_name_width == -1) + options->stat_name_width = diff_stat_name_width; if (options->stat_graph_width == -1) options->stat_graph_width = diff_stat_graph_width; /* - * Guarantee 3/8*16==6 for the graph part - * and 5/8*16==10 for the filename part + * Guarantee 3/8*16 == 6 for the graph part + * and 5/8*16 == 10 for the filename part */ if (width < 16 + 6 + number_width) width = 16 + 6 + number_width; @@ -3563,18 +3570,21 @@ static void builtin_diff(const char *name_a, strbuf_addf(&header, "%s%snew file mode %06o%s\n", line_prefix, meta, two->mode, reset); if (xfrm_msg) strbuf_addstr(&header, xfrm_msg); + o->found_changes = 1; must_show_header = 1; } else if (lbl[1][0] == '/') { strbuf_addf(&header, "%s%sdeleted file mode %06o%s\n", line_prefix, meta, one->mode, reset); if (xfrm_msg) strbuf_addstr(&header, xfrm_msg); + o->found_changes = 1; must_show_header = 1; } else { if (one->mode != two->mode) { strbuf_addf(&header, "%s%sold mode %06o%s\n", line_prefix, meta, one->mode, reset); strbuf_addf(&header, "%s%snew mode %06o%s\n", line_prefix, meta, two->mode, reset); + o->found_changes = 1; must_show_header = 1; } if (xfrm_msg) @@ -4832,6 +4842,10 @@ void diff_setup_done(struct diff_options *options) else options->prefix_length = 0; + /* + * --name-only, --name-status, --checkdiff, and -s + * turn other output format off. + */ if (options->output_format & (DIFF_FORMAT_NAME | DIFF_FORMAT_NAME_STATUS | DIFF_FORMAT_CHECKDIFF | @@ -6206,6 +6220,8 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt) fprintf(opt->file, "%s", diff_line_prefix(opt)); write_name_quoted(name_a, opt->file, opt->line_termination); } + + opt->found_changes = 1; } static void show_file_mode_name(struct diff_options *opt, const char *newdelete, struct diff_filespec *fs) @@ -6684,6 +6700,21 @@ void diff_flush(struct diff_options *options) separator++; } + if (output_format & DIFF_FORMAT_PATCH) { + if (separator) { + emit_diff_symbol(options, DIFF_SYMBOL_SEPARATOR, NULL, 0, 0); + if (options->stat_sep) + /* attach patch instead of inline */ + emit_diff_symbol(options, DIFF_SYMBOL_STAT_SEP, + NULL, 0, 0); + } + + diff_flush_patch_all_file_pairs(options); + } + + if (output_format & DIFF_FORMAT_CALLBACK) + options->format_callback(q, options, options->format_callback_data); + if (output_format & DIFF_FORMAT_NO_OUTPUT && options->flags.exit_with_status && options->flags.diff_from_contents) { @@ -6705,21 +6736,6 @@ void diff_flush(struct diff_options *options) } } - if (output_format & DIFF_FORMAT_PATCH) { - if (separator) { - emit_diff_symbol(options, DIFF_SYMBOL_SEPARATOR, NULL, 0, 0); - if (options->stat_sep) - /* attach patch instead of inline */ - emit_diff_symbol(options, DIFF_SYMBOL_STAT_SEP, - NULL, 0, 0); - } - - diff_flush_patch_all_file_pairs(options); - } - - if (output_format & DIFF_FORMAT_CALLBACK) - options->format_callback(q, options, options->format_callback_data); - free_queue: diff_free_queue(q); DIFF_QUEUE_CLEAR(q); @@ -6973,16 +6989,14 @@ void diffcore_std(struct diff_options *options) options->found_follow = 0; } -int diff_result_code(struct diff_options *opt, int status) +int diff_result_code(struct diff_options *opt) { int result = 0; diff_warn_rename_limit("diff.renameLimit", opt->needed_rename_limit, opt->degraded_cc_to_c); - if (!opt->flags.exit_with_status && - !(opt->output_format & DIFF_FORMAT_CHECKDIFF)) - return status; + if (opt->flags.exit_with_status && opt->flags.has_changes) result |= 01; @@ -7029,6 +7043,7 @@ void compute_diffstat(struct diff_options *options, if (check_pair_status(p)) diff_flush_stat(p, options, diffstat); } + options->found_changes = !!diffstat->nr; } void diff_addremove(struct diff_options *options, @@ -637,17 +637,17 @@ void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb); #define DIFF_SILENT_ON_REMOVED 01 /* report racily-clean paths as modified */ #define DIFF_RACY_IS_MODIFIED 02 -int run_diff_files(struct rev_info *revs, unsigned int option); +void run_diff_files(struct rev_info *revs, unsigned int option); #define DIFF_INDEX_CACHED 01 #define DIFF_INDEX_MERGE_BASE 02 -int run_diff_index(struct rev_info *revs, unsigned int option); +void run_diff_index(struct rev_info *revs, unsigned int option); int do_diff_cache(const struct object_id *, struct diff_options *); int diff_flush_patch_id(struct diff_options *, struct object_id *, int); void flush_one_hunk(struct object_id *result, git_hash_ctx *ctx); -int diff_result_code(struct diff_options *, int); +int diff_result_code(struct diff_options *); int diff_no_index(struct rev_info *, int implicit_no_index, int, const char **); diff --git a/environment.c b/environment.c index f98d76f080..bb3c2a96a3 100644 --- a/environment.c +++ b/environment.c @@ -81,6 +81,7 @@ int merge_log_config = -1; int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */ unsigned long pack_size_limit_cfg; enum log_refs_config log_all_ref_updates = LOG_REFS_UNSET; +int max_allowed_tree_depth = 2048; #ifndef PROTECT_HFS_DEFAULT #define PROTECT_HFS_DEFAULT 0 diff --git a/environment.h b/environment.h index c5377473c6..e5351c9dd9 100644 --- a/environment.h +++ b/environment.h @@ -132,6 +132,7 @@ extern size_t packed_git_limit; extern size_t delta_base_cache_limit; extern unsigned long big_file_threshold; extern unsigned long pack_size_limit_cfg; +extern int max_allowed_tree_depth; /* * Accessors for the core.sharedrepository config which lazy-load the value diff --git a/fetch-pack.c b/fetch-pack.c index 65c1ff4bb4..26999e3b65 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -1911,10 +1911,10 @@ static void fetch_pack_setup(void) if (did_setup) return; fetch_pack_config(); - if (0 <= transfer_unpack_limit) - unpack_limit = transfer_unpack_limit; - else if (0 <= fetch_unpack_limit) + if (0 <= fetch_unpack_limit) unpack_limit = fetch_unpack_limit; + else if (0 <= transfer_unpack_limit) + unpack_limit = transfer_unpack_limit; did_setup = 1; } @@ -24,6 +24,8 @@ #include "credential.h" #include "help.h" +static ssize_t max_tree_entry_len = 4096; + #define STR(x) #x #define MSG_ID(id, msg_type) { STR(id), NULL, NULL, FSCK_##msg_type }, static struct { @@ -154,15 +156,29 @@ void fsck_set_msg_type(struct fsck_options *options, const char *msg_id_str, const char *msg_type_str) { int msg_id = parse_msg_id(msg_id_str); - enum fsck_msg_type msg_type = parse_msg_type(msg_type_str); + char *to_free = NULL; + enum fsck_msg_type msg_type; if (msg_id < 0) die("Unhandled message id: %s", msg_id_str); + if (msg_id == FSCK_MSG_LARGE_PATHNAME) { + const char *colon = strchr(msg_type_str, ':'); + if (colon) { + msg_type_str = to_free = + xmemdupz(msg_type_str, colon - msg_type_str); + colon++; + if (!git_parse_ssize_t(colon, &max_tree_entry_len)) + die("unable to parse max tree entry len: %s", colon); + } + } + msg_type = parse_msg_type(msg_type_str); + if (msg_type != FSCK_ERROR && msg_id_info[msg_id].msg_type == FSCK_FATAL) die("Cannot demote %s to %s", msg_id_str, msg_type_str); fsck_set_msg_type_from_ids(options, msg_id, msg_type); + free(to_free); } void fsck_set_msg_types(struct fsck_options *options, const char *values) @@ -578,6 +594,7 @@ static int fsck_tree(const struct object_id *tree_oid, int has_bad_modes = 0; int has_dup_entries = 0; int not_properly_sorted = 0; + int has_large_name = 0; struct tree_desc desc; unsigned o_mode; const char *o_name; @@ -607,6 +624,7 @@ static int fsck_tree(const struct object_id *tree_oid, has_dotdot |= !strcmp(name, ".."); has_dotgit |= is_hfs_dotgit(name) || is_ntfs_dotgit(name); has_zero_pad |= *(char *)desc.buffer == '0'; + has_large_name |= tree_entry_len(&desc.entry) > max_tree_entry_len; if (is_hfs_dotgitmodules(name) || is_ntfs_dotgitmodules(name)) { if (!S_ISLNK(mode)) @@ -749,6 +767,10 @@ static int fsck_tree(const struct object_id *tree_oid, retval += report(options, tree_oid, OBJ_TREE, FSCK_MSG_TREE_NOT_SORTED, "not properly sorted"); + if (has_large_name) + retval += report(options, tree_oid, OBJ_TREE, + FSCK_MSG_LARGE_PATHNAME, + "contains excessively large pathname"); return retval; } @@ -73,6 +73,7 @@ enum fsck_msg_type { FUNC(NULL_SHA1, WARN) \ FUNC(ZERO_PADDED_FILEMODE, WARN) \ FUNC(NUL_IN_COMMIT, WARN) \ + FUNC(LARGE_PATHNAME, WARN) \ /* infos (reported as warnings, but ignored by default) */ \ FUNC(BAD_FILEMODE, INFO) \ FUNC(GITMODULES_PARSE, INFO) \ diff --git a/fsmonitor-ipc.c b/fsmonitor-ipc.c index 88575aa54c..153918cf76 100644 --- a/fsmonitor-ipc.c +++ b/fsmonitor-ipc.c @@ -20,7 +20,7 @@ int fsmonitor_ipc__is_supported(void) return 0; } -const char *fsmonitor_ipc__get_path(struct repository *r) +const char *fsmonitor_ipc__get_path(struct repository *r UNUSED) { return NULL; } @@ -30,14 +30,14 @@ enum ipc_active_state fsmonitor_ipc__get_state(void) return IPC_STATE__OTHER_ERROR; } -int fsmonitor_ipc__send_query(const char *since_token, - struct strbuf *answer) +int fsmonitor_ipc__send_query(const char *since_token UNUSED, + struct strbuf *answer UNUSED) { return -1; } -int fsmonitor_ipc__send_command(const char *command, - struct strbuf *answer) +int fsmonitor_ipc__send_command(const char *command UNUSED, + struct strbuf *answer UNUSED) { return -1; } diff --git a/fsmonitor-settings.c b/fsmonitor-settings.c index b62acf44ae..a6a9e6bc19 100644 --- a/fsmonitor-settings.c +++ b/fsmonitor-settings.c @@ -62,7 +62,8 @@ static enum fsmonitor_reason check_remote(struct repository *r) } #endif -static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc) +static enum fsmonitor_reason check_for_incompatible(struct repository *r, + int ipc MAYBE_UNUSED) { if (!r->worktree) { /* diff --git a/git-compat-util.h b/git-compat-util.h index d32aa754ae..3e7a59b5ff 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -422,6 +422,10 @@ char *gitdirname(char *); #define PATH_MAX 4096 #endif +#ifndef NAME_MAX +#define NAME_MAX 255 +#endif + typedef uintmax_t timestamp_t; #define PRItime PRIuMAX #define parse_timestamp strtoumax diff --git a/git-gui/Makefile b/git-gui/Makefile index a0d5a4b28e..3f80435436 100644 --- a/git-gui/Makefile +++ b/git-gui/Makefile @@ -138,25 +138,10 @@ GITGUI_SCRIPT := $$0 GITGUI_RELATIVE := GITGUI_MACOSXAPP := -ifeq ($(uname_O),Cygwin) - GITGUI_SCRIPT := `cygpath --windows --absolute "$(GITGUI_SCRIPT)"` - - # Is this a Cygwin Tcl/Tk binary? If so it knows how to do - # POSIX path translation just like cygpath does and we must - # keep libdir in POSIX format so Cygwin packages of git-gui - # work no matter where the user installs them. - # - ifeq ($(shell echo 'puts [file normalize /]' | '$(TCL_PATH_SQ)'),$(shell cygpath --mixed --absolute /)) - gg_libdir_sed_in := $(gg_libdir) - else - gg_libdir_sed_in := $(shell cygpath --windows --absolute "$(gg_libdir)") - endif -else - ifeq ($(exedir),$(gg_libdir)) - GITGUI_RELATIVE := 1 - endif - gg_libdir_sed_in := $(gg_libdir) +ifeq ($(exedir),$(gg_libdir)) + GITGUI_RELATIVE := 1 endif +gg_libdir_sed_in := $(gg_libdir) ifeq ($(uname_S),Darwin) ifeq ($(shell test -d $(TKFRAMEWORK) && echo y),y) GITGUI_MACOSXAPP := YesPlease diff --git a/git-gui/README.md b/git-gui/README.md index 5ce2122fbc..b460b649a8 100644 --- a/git-gui/README.md +++ b/git-gui/README.md @@ -88,7 +88,7 @@ that you first use `git-format-patch` to generate the emails, audit them, and then send them via `git-send-email`. A pretty good guide to configuring and using `git-send-email` can be found -[here](https://www.freedesktop.org/wiki/Software/PulseAudio/HowToUseGitSendEmail/) +[here](https://www.freedesktop.org/wiki/Software/PulseAudio/HowToUseGitSendEmail/). ### Using your email client diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh index 201524c34e..3e5907a460 100755 --- a/git-gui/git-gui.sh +++ b/git-gui/git-gui.sh @@ -46,6 +46,132 @@ catch {rename send {}} ; # What an evil concept... ###################################################################### ## +## Enabling platform-specific code paths + +proc is_MacOSX {} { + if {[tk windowingsystem] eq {aqua}} { + return 1 + } + return 0 +} + +proc is_Windows {} { + if {$::tcl_platform(platform) eq {windows}} { + return 1 + } + return 0 +} + +set _iscygwin {} +proc is_Cygwin {} { + global _iscygwin + if {$_iscygwin eq {}} { + if {[string match "CYGWIN_*" $::tcl_platform(os)]} { + set _iscygwin 1 + } else { + set _iscygwin 0 + } + } + return $_iscygwin +} + +###################################################################### +## +## PATH lookup + +set _search_path {} +proc _which {what args} { + global env _search_exe _search_path + + if {$_search_path eq {}} { + if {[is_Windows]} { + set gitguidir [file dirname [info script]] + regsub -all ";" $gitguidir "\\;" gitguidir + set env(PATH) "$gitguidir;$env(PATH)" + set _search_path [split $env(PATH) {;}] + # Skip empty `PATH` elements + set _search_path [lsearch -all -inline -not -exact \ + $_search_path ""] + set _search_exe .exe + } else { + set _search_path [split $env(PATH) :] + set _search_exe {} + } + } + + if {[is_Windows] && [lsearch -exact $args -script] >= 0} { + set suffix {} + } else { + set suffix $_search_exe + } + + foreach p $_search_path { + set p [file join $p $what$suffix] + if {[file exists $p]} { + return [file normalize $p] + } + } + return {} +} + +proc sanitize_command_line {command_line from_index} { + set i $from_index + while {$i < [llength $command_line]} { + set cmd [lindex $command_line $i] + if {[llength [file split $cmd]] < 2} { + set fullpath [_which $cmd] + if {$fullpath eq ""} { + throw {NOT-FOUND} "$cmd not found in PATH" + } + lset command_line $i $fullpath + } + + # handle piped commands, e.g. `exec A | B` + for {incr i} {$i < [llength $command_line]} {incr i} { + if {[lindex $command_line $i] eq "|"} { + incr i + break + } + } + } + return $command_line +} + +# Override `exec` to avoid unsafe PATH lookup + +rename exec real_exec + +proc exec {args} { + # skip options + for {set i 0} {$i < [llength $args]} {incr i} { + set arg [lindex $args $i] + if {$arg eq "--"} { + incr i + break + } + if {[string range $arg 0 0] ne "-"} { + break + } + } + set args [sanitize_command_line $args $i] + uplevel 1 real_exec $args +} + +# Override `open` to avoid unsafe PATH lookup + +rename open real_open + +proc open {args} { + set arg0 [lindex $args 0] + if {[string range $arg0 0 0] eq "|"} { + set command_line [string trim [string range $arg0 1 end]] + lset args 0 "| [sanitize_command_line $command_line 0]" + } + uplevel 1 real_open $args +} + +###################################################################### +## ## locate our library if { [info exists ::env(GIT_GUI_LIB_DIR) ] } { @@ -163,8 +289,6 @@ set _isbare {} set _gitexec {} set _githtmldir {} set _reponame {} -set _iscygwin {} -set _search_path {} set _shellpath {@@SHELL_PATH@@} set _trace [lsearch -exact $argv --trace] @@ -211,14 +335,7 @@ proc gitexec {args} { if {[catch {set _gitexec [git --exec-path]} err]} { error "Git not installed?\n\n$err" } - if {[is_Cygwin]} { - set _gitexec [exec cygpath \ - --windows \ - --absolute \ - $_gitexec] - } else { - set _gitexec [file normalize $_gitexec] - } + set _gitexec [file normalize $_gitexec] } if {$args eq {}} { return $_gitexec @@ -233,14 +350,7 @@ proc githtmldir {args} { # Git not installed or option not yet supported return {} } - if {[is_Cygwin]} { - set _githtmldir [exec cygpath \ - --windows \ - --absolute \ - $_githtmldir] - } else { - set _githtmldir [file normalize $_githtmldir] - } + set _githtmldir [file normalize $_githtmldir] } if {$args eq {}} { return $_githtmldir @@ -252,40 +362,6 @@ proc reponame {} { return $::_reponame } -proc is_MacOSX {} { - if {[tk windowingsystem] eq {aqua}} { - return 1 - } - return 0 -} - -proc is_Windows {} { - if {$::tcl_platform(platform) eq {windows}} { - return 1 - } - return 0 -} - -proc is_Cygwin {} { - global _iscygwin - if {$_iscygwin eq {}} { - if {$::tcl_platform(platform) eq {windows}} { - if {[catch {set p [exec cygpath --windir]} err]} { - set _iscygwin 0 - } else { - set _iscygwin 1 - # Handle MSys2 which is only cygwin when MSYSTEM is MSYS. - if {[info exists ::env(MSYSTEM)] && $::env(MSYSTEM) ne "MSYS"} { - set _iscygwin 0 - } - } - } else { - set _iscygwin 0 - } - } - return $_iscygwin -} - proc is_enabled {option} { global enabled_options if {[catch {set on $enabled_options($option)}]} {return 0} @@ -448,44 +524,6 @@ proc _git_cmd {name} { return $v } -proc _which {what args} { - global env _search_exe _search_path - - if {$_search_path eq {}} { - if {[is_Cygwin] && [regexp {^(/|\.:)} $env(PATH)]} { - set _search_path [split [exec cygpath \ - --windows \ - --path \ - --absolute \ - $env(PATH)] {;}] - set _search_exe .exe - } elseif {[is_Windows]} { - set gitguidir [file dirname [info script]] - regsub -all ";" $gitguidir "\\;" gitguidir - set env(PATH) "$gitguidir;$env(PATH)" - set _search_path [split $env(PATH) {;}] - set _search_exe .exe - } else { - set _search_path [split $env(PATH) :] - set _search_exe {} - } - } - - if {[is_Windows] && [lsearch -exact $args -script] >= 0} { - set suffix {} - } else { - set suffix $_search_exe - } - - foreach p $_search_path { - set p [file join $p $what$suffix] - if {[file exists $p]} { - return [file normalize $p] - } - } - return {} -} - # Test a file for a hashbang to identify executable scripts on Windows. proc is_shellscript {filename} { if {![file exists $filename]} {return 0} @@ -623,31 +661,8 @@ proc git_write {args} { } proc githook_read {hook_name args} { - set pchook [gitdir hooks $hook_name] - lappend args 2>@1 - - # On Windows [file executable] might lie so we need to ask - # the shell if the hook is executable. Yes that's annoying. - # - if {[is_Windows]} { - upvar #0 _sh interp - if {![info exists interp]} { - set interp [_which sh] - } - if {$interp eq {}} { - error "hook execution requires sh (not in PATH)" - } - - set scr {if test -x "$1";then exec "$@";fi} - set sh_c [list $interp -c $scr $interp $pchook] - return [_open_stdout_stderr [concat $sh_c $args]] - } - - if {[file executable $pchook]} { - return [_open_stdout_stderr [concat [list $pchook] $args]] - } - - return {} + set cmd [concat git hook run --ignore-missing $hook_name -- $args 2>@1] + return [_open_stdout_stderr $cmd] } proc kill_file_process {fd} { @@ -1259,9 +1274,6 @@ if {$_gitdir eq "."} { set _gitdir [pwd] } -if {![file isdirectory $_gitdir] && [is_Cygwin]} { - catch {set _gitdir [exec cygpath --windows $_gitdir]} -} if {![file isdirectory $_gitdir]} { catch {wm withdraw .} error_popup [strcat [mc "Git directory not found:"] "\n\n$_gitdir"] @@ -1273,11 +1285,7 @@ apply_config # v1.7.0 introduced --show-toplevel to return the canonical work-tree if {[package vcompare $_git_version 1.7.0] >= 0} { - if { [is_Cygwin] } { - catch {set _gitworktree [exec cygpath --windows [git rev-parse --show-toplevel]]} - } else { - set _gitworktree [git rev-parse --show-toplevel] - } + set _gitworktree [git rev-parse --show-toplevel] } else { # try to set work tree from environment, core.worktree or use # cdup to obtain a relative path to the top of the worktree. If @@ -1502,24 +1510,8 @@ proc rescan {after {honor_trustmtime 1}} { } } -if {[is_Cygwin]} { - set is_git_info_exclude {} - proc have_info_exclude {} { - global is_git_info_exclude - - if {$is_git_info_exclude eq {}} { - if {[catch {exec test -f [gitdir info exclude]}]} { - set is_git_info_exclude 0 - } else { - set is_git_info_exclude 1 - } - } - return $is_git_info_exclude - } -} else { - proc have_info_exclude {} { - return [file readable [gitdir info exclude]] - } +proc have_info_exclude {} { + return [file readable [gitdir info exclude]] } proc rescan_stage2 {fd after} { @@ -2259,7 +2251,9 @@ proc do_git_gui {} { # Get the system-specific explorer app/command. proc get_explorer {} { - if {[is_Cygwin] || [is_Windows]} { + if {[is_Cygwin]} { + set explorer "/bin/cygstart.exe --explore" + } elseif {[is_Windows]} { set explorer "explorer.exe" } elseif {[is_MacOSX]} { set explorer "open" @@ -3053,10 +3047,6 @@ if {[is_MacOSX]} { set doc_path [githtmldir] if {$doc_path ne {}} { set doc_path [file join $doc_path index.html] - - if {[is_Cygwin]} { - set doc_path [exec cygpath --mixed $doc_path] - } } if {[file isfile $doc_path]} { @@ -4028,60 +4018,6 @@ set file_lists($ui_workdir) [list] wm title . "[appname] ([reponame]) [file normalize $_gitworktree]" focus -force $ui_comm -# -- Warn the user about environmental problems. Cygwin's Tcl -# does *not* pass its env array onto any processes it spawns. -# This means that git processes get none of our environment. -# -if {[is_Cygwin]} { - set ignored_env 0 - set suggest_user {} - set msg [mc "Possible environment issues exist. - -The following environment variables are probably -going to be ignored by any Git subprocess run -by %s: - -" [appname]] - foreach name [array names env] { - switch -regexp -- $name { - {^GIT_INDEX_FILE$} - - {^GIT_OBJECT_DIRECTORY$} - - {^GIT_ALTERNATE_OBJECT_DIRECTORIES$} - - {^GIT_DIFF_OPTS$} - - {^GIT_EXTERNAL_DIFF$} - - {^GIT_PAGER$} - - {^GIT_TRACE$} - - {^GIT_CONFIG$} - - {^GIT_(AUTHOR|COMMITTER)_DATE$} { - append msg " - $name\n" - incr ignored_env - } - {^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL)$} { - append msg " - $name\n" - incr ignored_env - set suggest_user $name - } - } - } - if {$ignored_env > 0} { - append msg [mc " -This is due to a known issue with the -Tcl binary distributed by Cygwin."] - - if {$suggest_user ne {}} { - append msg [mc " - -A good replacement for %s -is placing values for the user.name and -user.email settings into your personal -~/.gitconfig file. -" $suggest_user] - } - warn_popup $msg - } - unset ignored_env msg suggest_user name -} - # -- Only initialize complex UI if we are going to stay running. # if {[is_enabled transport]} { diff --git a/git-gui/lib/choose_repository.tcl b/git-gui/lib/choose_repository.tcl index af1fee7c75..d23abedcb3 100644 --- a/git-gui/lib/choose_repository.tcl +++ b/git-gui/lib/choose_repository.tcl @@ -174,9 +174,6 @@ constructor pick {} { -foreground blue \ -underline 1 set home $::env(HOME) - if {[is_Cygwin]} { - set home [exec cygpath --windows --absolute $home] - } set home "[file normalize $home]/" set hlen [string length $home] foreach p $sorted_recent { @@ -374,18 +371,6 @@ proc _objdir {path} { return $objdir } - if {[is_Cygwin]} { - set objdir [file join $path .git objects.lnk] - if {[file isfile $objdir]} { - return [win32_read_lnk $objdir] - } - - set objdir [file join $path objects.lnk] - if {[file isfile $objdir]} { - return [win32_read_lnk $objdir] - } - } - return {} } @@ -623,12 +608,6 @@ method _do_clone2 {} { } set giturl $origin_url - if {[is_Cygwin] && [file isdirectory $giturl]} { - set giturl [exec cygpath --unix --absolute $giturl] - if {$clone_type eq {shared}} { - set objdir [exec cygpath --unix --absolute $objdir] - } - } if {[file exists $local_path]} { error_popup [mc "Location %s already exists." $local_path] @@ -668,11 +647,7 @@ method _do_clone2 {} { fconfigure $f_cp -translation binary -encoding binary cd $objdir while {[gets $f_in line] >= 0} { - if {[is_Cygwin]} { - puts $f_cp [exec cygpath --unix --absolute $line] - } else { - puts $f_cp [file normalize $line] - } + puts $f_cp [file normalize $line] } close $f_in close $f_cp diff --git a/git-gui/lib/shortcut.tcl b/git-gui/lib/shortcut.tcl index 97d1d7aa02..674a41f5e0 100644 --- a/git-gui/lib/shortcut.tcl +++ b/git-gui/lib/shortcut.tcl @@ -27,13 +27,10 @@ proc do_windows_shortcut {} { } proc do_cygwin_shortcut {} { - global argv0 _gitworktree + global argv0 _gitworktree oguilib if {[catch { set desktop [exec cygpath \ - --windows \ - --absolute \ - --long-name \ --desktop] }]} { set desktop . @@ -48,19 +45,19 @@ proc do_cygwin_shortcut {} { set fn ${fn}.lnk } if {[catch { - set sh [exec cygpath \ - --windows \ - --absolute \ - /bin/sh.exe] - set me [exec cygpath \ - --unix \ - --absolute \ - $argv0] - win32_create_lnk $fn [list \ - $sh -c \ - "CHERE_INVOKING=1 source /etc/profile;[sq $me] &" \ - ] \ - [file normalize $_gitworktree] + set repodir [file normalize $_gitworktree] + set shargs {-c \ + "CHERE_INVOKING=1 \ + source /etc/profile; \ + git gui"} + exec /bin/mkshortcut.exe \ + --arguments $shargs \ + --desc "git-gui on $repodir" \ + --icon $oguilib/git-gui.ico \ + --name $fn \ + --show min \ + --workingdir $repodir \ + /bin/sh.exe } err]} { error_popup [strcat [mc "Cannot write shortcut:"] "\n\n$err"] } diff --git a/git-send-email.perl b/git-send-email.perl index 897cea6564..288ea1ae80 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -1166,10 +1166,10 @@ sub extract_valid_address { sub extract_valid_address_or_die { my $address = shift; - $address = extract_valid_address($address); + my $valid_address = extract_valid_address($address); die sprintf(__("error: unable to extract a valid address from: %s\n"), $address) - if !$address; - return $address; + if !$valid_address; + return $valid_address; } sub validate_address { diff --git a/git-svn.perl b/git-svn.perl index be987e316f..4e8878f035 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -297,28 +297,12 @@ my %cmd = ( {} ], ); -package FakeTerm; -sub new { - my ($class, $reason) = @_; - return bless \$reason, shift; -} -sub readline { - my $self = shift; - die "Cannot use readline on FakeTerm: $$self"; -} -package main; - my $term; sub term_init { - $term = eval { - require Term::ReadLine; - $ENV{"GIT_SVN_NOTTY"} + require Term::ReadLine; + $term = $ENV{"GIT_SVN_NOTTY"} ? new Term::ReadLine 'git-svn', \*STDIN, \*STDOUT : new Term::ReadLine 'git-svn'; - }; - if ($@) { - $term = new FakeTerm "$@: going non-interactive"; - } } my $cmd; @@ -339,7 +339,6 @@ void graph_setup_line_prefix(struct diff_options *diffopt) diffopt->output_prefix = diff_output_prefix_callback; } - struct git_graph *graph_init(struct rev_info *opt) { struct git_graph *graph = xmalloc(sizeof(struct git_graph)); @@ -17,7 +17,7 @@ static int grep_source_load(struct grep_source *gs); static int grep_source_is_binary(struct grep_source *gs, struct index_state *istate); -static void std_output(struct grep_opt *opt, const void *buf, size_t size) +static void std_output(struct grep_opt *opt UNUSED, const void *buf, size_t size) { fwrite(buf, size, 1, stdout); } @@ -452,18 +452,20 @@ static void free_pcre2_pattern(struct grep_pat *p) pcre2_general_context_free(p->pcre2_general_context); } #else /* !USE_LIBPCRE2 */ -static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt) +static void compile_pcre2_pattern(struct grep_pat *p UNUSED, + const struct grep_opt *opt UNUSED) { die("cannot use Perl-compatible regexes when not compiled with USE_LIBPCRE"); } -static int pcre2match(struct grep_pat *p, const char *line, const char *eol, - regmatch_t *match, int eflags) +static int pcre2match(struct grep_pat *p UNUSED, const char *line UNUSED, + const char *eol UNUSED, regmatch_t *match UNUSED, + int eflags UNUSED) { return 1; } -static void free_pcre2_pattern(struct grep_pat *p) +static void free_pcre2_pattern(struct grep_pat *p UNUSED) { } @@ -738,18 +738,43 @@ static int redact_sensitive_header(struct strbuf *header, size_t offset) return ret; } +static int match_curl_h2_trace(const char *line, const char **out) +{ + const char *p; + + /* + * curl prior to 8.1.0 gives us: + * + * h2h3 [<header-name>: <header-val>] + * + * Starting in 8.1.0, the first token became just "h2". + */ + if (skip_iprefix(line, "h2h3 [", out) || + skip_iprefix(line, "h2 [", out)) + return 1; + + /* + * curl 8.3.0 uses: + * [HTTP/2] [<stream-id>] [<header-name>: <header-val>] + * where <stream-id> is numeric. + */ + if (skip_iprefix(line, "[HTTP/2] [", &p)) { + while (isdigit(*p)) + p++; + if (skip_prefix(p, "] [", out)) + return 1; + } + + return 0; +} + /* Redact headers in info */ static void redact_sensitive_info_header(struct strbuf *header) { const char *sensitive_header; - /* - * curl's h2h3 prints headers in info, e.g.: - * h2h3 [<header-name>: <header-val>] - */ if (trace_curl_redact && - (skip_iprefix(header->buf, "h2h3 [", &sensitive_header) || - skip_iprefix(header->buf, "h2 [", &sensitive_header))) { + match_curl_h2_trace(header->buf, &sensitive_header)) { if (redact_sensitive_header(header, sensitive_header - header->buf)) { /* redaction ate our closing bracket */ strbuf_addch(header, ']'); diff --git a/imap-send.c b/imap-send.c index 06386e0b3b..996651e4f8 100644 --- a/imap-send.c +++ b/imap-send.c @@ -206,10 +206,14 @@ static void socket_perror(const char *func, struct imap_socket *sock, int ret) else fprintf(stderr, "%s: unexpected EOF\n", func); } + /* mark as used to appease -Wunused-parameter with NO_OPENSSL */ + (void)sock; } #ifdef NO_OPENSSL -static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify) +static int ssl_socket_connect(struct imap_socket *sock UNUSED, + int use_tls_only UNUSED, + int verify UNUSED) { fprintf(stderr, "SSL requested but SSL support not compiled in\n"); return -1; @@ -904,7 +908,9 @@ static char *cram(const char *challenge_64, const char *user, const char *pass) #else -static char *cram(const char *challenge_64, const char *user, const char *pass) +static char *cram(const char *challenge_64 UNUSED, + const char *user UNUSED, + const char *pass UNUSED) { die("If you want to use CRAM-MD5 authenticate method, " "you have to build git-imap-send with OpenSSL library."); diff --git a/list-objects.c b/list-objects.c index e60a6cd5b4..c25c72b32c 100644 --- a/list-objects.c +++ b/list-objects.c @@ -14,6 +14,7 @@ #include "packfile.h" #include "object-store-ll.h" #include "trace.h" +#include "environment.h" struct traversal_context { struct rev_info *revs; @@ -21,6 +22,7 @@ struct traversal_context { show_commit_fn show_commit; void *show_data; struct filter *filter; + int depth; }; static void show_commit(struct traversal_context *ctx, @@ -118,7 +120,9 @@ static void process_tree_contents(struct traversal_context *ctx, entry.path, oid_to_hex(&tree->object.oid)); } t->object.flags |= NOT_USER_GIVEN; + ctx->depth++; process_tree(ctx, t, base, entry.path); + ctx->depth--; } else if (S_ISGITLINK(entry.mode)) ; /* ignore gitlink */ @@ -156,6 +160,9 @@ static void process_tree(struct traversal_context *ctx, !revs->include_check_obj(&tree->object, revs->include_check_data)) return; + if (ctx->depth > max_allowed_tree_depth) + die("exceeded maximum allowed tree depth"); + failed_parse = parse_tree_gently(tree, 1); if (failed_parse) { if (revs->ignore_missing_links) @@ -349,6 +356,7 @@ static void traverse_non_commits(struct traversal_context *ctx, if (!path) path = ""; if (obj->type == OBJ_TREE) { + ctx->depth = 0; process_tree(ctx, (struct tree *)obj, base, path); continue; } diff --git a/log-tree.c b/log-tree.c index 208c69cbb7..504da6b519 100644 --- a/log-tree.c +++ b/log-tree.c @@ -303,26 +303,43 @@ static void show_name(struct strbuf *sb, const struct name_decoration *decoratio /* * The caller makes sure there is no funny color before calling. - * format_decorations_extended makes sure the same after return. + * format_decorations ensures the same after return. */ -void format_decorations_extended(struct strbuf *sb, +void format_decorations(struct strbuf *sb, const struct commit *commit, int use_color, - const char *prefix, - const char *separator, - const char *suffix) + const struct decoration_options *opts) { const struct name_decoration *decoration; const struct name_decoration *current_and_HEAD; - const char *color_commit = - diff_get_color(use_color, DIFF_COMMIT); - const char *color_reset = - decorate_get_color(use_color, DECORATION_NONE); + const char *color_commit, *color_reset; + + const char *prefix = " ("; + const char *suffix = ")"; + const char *separator = ", "; + const char *pointer = " -> "; + const char *tag = "tag: "; decoration = get_name_decoration(&commit->object); if (!decoration) return; + if (opts) { + if (opts->prefix) + prefix = opts->prefix; + if (opts->suffix) + suffix = opts->suffix; + if (opts->separator) + separator = opts->separator; + if (opts->pointer) + pointer = opts->pointer; + if (opts->tag) + tag = opts->tag; + } + + color_commit = diff_get_color(use_color, DIFF_COMMIT); + color_reset = decorate_get_color(use_color, DECORATION_NONE); + current_and_HEAD = current_pointed_by_HEAD(decoration); while (decoration) { /* @@ -331,31 +348,44 @@ void format_decorations_extended(struct strbuf *sb, * appeared, skipping the entry for current. */ if (decoration != current_and_HEAD) { - strbuf_addstr(sb, color_commit); - strbuf_addstr(sb, prefix); - strbuf_addstr(sb, color_reset); - strbuf_addstr(sb, decorate_get_color(use_color, decoration->type)); - if (decoration->type == DECORATION_REF_TAG) - strbuf_addstr(sb, "tag: "); + const char *color = + decorate_get_color(use_color, decoration->type); + + if (*prefix) { + strbuf_addstr(sb, color_commit); + strbuf_addstr(sb, prefix); + strbuf_addstr(sb, color_reset); + } + if (*tag && decoration->type == DECORATION_REF_TAG) { + strbuf_addstr(sb, color); + strbuf_addstr(sb, tag); + strbuf_addstr(sb, color_reset); + } + + strbuf_addstr(sb, color); show_name(sb, decoration); + strbuf_addstr(sb, color_reset); if (current_and_HEAD && decoration->type == DECORATION_REF_HEAD) { - strbuf_addstr(sb, " -> "); + strbuf_addstr(sb, color_commit); + strbuf_addstr(sb, pointer); strbuf_addstr(sb, color_reset); strbuf_addstr(sb, decorate_get_color(use_color, current_and_HEAD->type)); show_name(sb, current_and_HEAD); + strbuf_addstr(sb, color_reset); } - strbuf_addstr(sb, color_reset); prefix = separator; } decoration = decoration->next; } - strbuf_addstr(sb, color_commit); - strbuf_addstr(sb, suffix); - strbuf_addstr(sb, color_reset); + if (*suffix) { + strbuf_addstr(sb, color_commit); + strbuf_addstr(sb, suffix); + strbuf_addstr(sb, color_reset); + } } void show_decorations(struct rev_info *opt, struct commit *commit) @@ -370,7 +400,7 @@ void show_decorations(struct rev_info *opt, struct commit *commit) } if (!opt->show_decorations) return; - format_decorations(&sb, commit, opt->diffopt.use_color); + format_decorations(&sb, commit, opt->diffopt.use_color, NULL); fputs(sb.buf, opt->diffopt.file); strbuf_release(&sb); } diff --git a/log-tree.h b/log-tree.h index bdb6432815..41c776fea5 100644 --- a/log-tree.h +++ b/log-tree.h @@ -13,17 +13,20 @@ struct decoration_filter { struct string_list *exclude_ref_config_pattern; }; +struct decoration_options { + char *prefix; + char *suffix; + char *separator; + char *pointer; + char *tag; +}; + int parse_decorate_color_config(const char *var, const char *slot_name, const char *value); int log_tree_diff_flush(struct rev_info *); int log_tree_commit(struct rev_info *, struct commit *); void show_log(struct rev_info *opt); -void format_decorations_extended(struct strbuf *sb, const struct commit *commit, - int use_color, - const char *prefix, - const char *separator, - const char *suffix); -#define format_decorations(strbuf, commit, color) \ - format_decorations_extended((strbuf), (commit), (color), " (", ", ", ")") +void format_decorations(struct strbuf *sb, const struct commit *commit, + int use_color, const struct decoration_options *opts); void show_decorations(struct rev_info *opt, struct commit *commit); void log_write_email_headers(struct rev_info *opt, struct commit *commit, const char **extra_headers_p, diff --git a/merge-ort.c b/merge-ort.c index 8631c99700..7857ce9fbd 100644 --- a/merge-ort.c +++ b/merge-ort.c @@ -721,23 +721,6 @@ static void clear_or_reinit_internal_opts(struct merge_options_internal *opti, renames->callback_data_nr = renames->callback_data_alloc = 0; } -__attribute__((format (printf, 2, 3))) -static int err(struct merge_options *opt, const char *err, ...) -{ - va_list params; - struct strbuf sb = STRBUF_INIT; - - strbuf_addstr(&sb, "error: "); - va_start(params, err); - strbuf_vaddf(&sb, err, params); - va_end(params); - - error("%s", sb.buf); - strbuf_release(&sb); - - return -1; -} - static void format_commit(struct strbuf *sb, int indent, struct repository *repo, @@ -2122,13 +2105,12 @@ static int handle_content_merge(struct merge_options *opt, &result_buf); if ((merge_status < 0) || !result_buf.ptr) - ret = err(opt, _("Failed to execute internal merge")); + ret = error(_("failed to execute internal merge")); if (!ret && write_object_file(result_buf.ptr, result_buf.size, OBJ_BLOB, &result->oid)) - ret = err(opt, _("Unable to add %s to database"), - path); + ret = error(_("unable to add %s to database"), path); free(result_buf.ptr); if (ret) @@ -3342,10 +3324,7 @@ static int collect_renames(struct merge_options *opt, return clean; } -static int detect_and_process_renames(struct merge_options *opt, - struct tree *merge_base, - struct tree *side1, - struct tree *side2) +static int detect_and_process_renames(struct merge_options *opt) { struct diff_queue_struct combined = { 0 }; struct rename_info *renames = &opt->priv->renames; @@ -3509,8 +3488,7 @@ static int sort_dirs_next_to_their_children(const char *one, const char *two) return c1 - c2; } -static int read_oid_strbuf(struct merge_options *opt, - const struct object_id *oid, +static int read_oid_strbuf(const struct object_id *oid, struct strbuf *dst) { void *buf; @@ -3518,10 +3496,10 @@ static int read_oid_strbuf(struct merge_options *opt, unsigned long size; buf = repo_read_object_file(the_repository, oid, &type, &size); if (!buf) - return err(opt, _("cannot read object %s"), oid_to_hex(oid)); + return error(_("cannot read object %s"), oid_to_hex(oid)); if (type != OBJ_BLOB) { free(buf); - return err(opt, _("object %s is not a blob"), oid_to_hex(oid)); + return error(_("object %s is not a blob"), oid_to_hex(oid)); } strbuf_attach(dst, buf, size, size + 1); return 0; @@ -3545,8 +3523,8 @@ static int blob_unchanged(struct merge_options *opt, if (oideq(&base->oid, &side->oid)) return 1; - if (read_oid_strbuf(opt, &base->oid, &basebuf) || - read_oid_strbuf(opt, &side->oid, &sidebuf)) + if (read_oid_strbuf(&base->oid, &basebuf) || + read_oid_strbuf(&side->oid, &sidebuf)) goto error_return; /* * Note: binary | is used so that both renormalizations are @@ -4902,8 +4880,7 @@ static void merge_start(struct merge_options *opt, struct merge_result *result) trace2_region_leave("merge", "allocate/init", opt->repo); } -static void merge_check_renames_reusable(struct merge_options *opt, - struct merge_result *result, +static void merge_check_renames_reusable(struct merge_result *result, struct tree *merge_base, struct tree *side1, struct tree *side2) @@ -4973,7 +4950,7 @@ redo: * TRANSLATORS: The %s arguments are: 1) tree hash of a merge * base, and 2-3) the trees for the two trees we're merging. */ - err(opt, _("collecting merge info failed for trees %s, %s, %s"), + error(_("collecting merge info failed for trees %s, %s, %s"), oid_to_hex(&merge_base->object.oid), oid_to_hex(&side1->object.oid), oid_to_hex(&side2->object.oid)); @@ -4983,8 +4960,7 @@ redo: trace2_region_leave("merge", "collect_merge_info", opt->repo); trace2_region_enter("merge", "renames", opt->repo); - result->clean = detect_and_process_renames(opt, merge_base, - side1, side2); + result->clean = detect_and_process_renames(opt); trace2_region_leave("merge", "renames", opt->repo); if (opt->priv->renames.redo_after_renames == 2) { trace2_region_enter("merge", "reset_maps", opt->repo); @@ -5106,7 +5082,7 @@ void merge_incore_nonrecursive(struct merge_options *opt, trace2_region_enter("merge", "merge_start", opt->repo); assert(opt->ancestor != NULL); - merge_check_renames_reusable(opt, result, merge_base, side1, side2); + merge_check_renames_reusable(result, merge_base, side1, side2); merge_start(opt, result); /* * Record the trees used in this merge, so if there's a next merge in diff --git a/merge-recursive.c b/merge-recursive.c index 6a4081bb0f..0d7e57e2df 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -1383,12 +1383,12 @@ static int merge_mode_and_contents(struct merge_options *opt, extra_marker_size); if ((merge_status < 0) || !result_buf.ptr) - ret = err(opt, _("Failed to execute internal merge")); + ret = err(opt, _("failed to execute internal merge")); if (!ret && write_object_file(result_buf.ptr, result_buf.size, OBJ_BLOB, &result->blob.oid)) - ret = err(opt, _("Unable to add %s to database"), + ret = err(opt, _("unable to add %s to database"), a->path); free(result_buf.ptr); diff --git a/negotiator/noop.c b/negotiator/noop.c index 7b72937686..de39028ab7 100644 --- a/negotiator/noop.c +++ b/negotiator/noop.c @@ -3,22 +3,24 @@ #include "../commit.h" #include "../fetch-negotiator.h" -static void known_common(struct fetch_negotiator *n, struct commit *c) +static void known_common(struct fetch_negotiator *n UNUSED, + struct commit *c UNUSED) { /* do nothing */ } -static void add_tip(struct fetch_negotiator *n, struct commit *c) +static void add_tip(struct fetch_negotiator *n UNUSED, + struct commit *c UNUSED) { /* do nothing */ } -static const struct object_id *next(struct fetch_negotiator *n) +static const struct object_id *next(struct fetch_negotiator *n UNUSED) { return NULL; } -static int ack(struct fetch_negotiator *n, struct commit *c) +static int ack(struct fetch_negotiator *n UNUSED, struct commit *c UNUSED) { /* * This negotiator does not emit any commits, so there is no commit to @@ -28,7 +30,7 @@ static int ack(struct fetch_negotiator *n, struct commit *c) return 0; } -static void release(struct fetch_negotiator *n) +static void release(struct fetch_negotiator *n UNUSED) { /* nothing to release */ } diff --git a/pack-bitmap.c b/pack-bitmap.c index 6afc03d1e4..ca8319b87c 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c @@ -1101,8 +1101,9 @@ static void show_boundary_commit(struct commit *commit, void *_data) } } -static void show_boundary_object(struct object *object, - const char *name, void *data) +static void show_boundary_object(struct object *object UNUSED, + const char *name UNUSED, + void *data UNUSED) { BUG("should not be called"); } diff --git a/parse-options-cb.c b/parse-options-cb.c index a24521dee0..bdc7fae497 100644 --- a/parse-options-cb.c +++ b/parse-options-cb.c @@ -227,7 +227,9 @@ int parse_opt_strvec(const struct option *opt, const char *arg, int unset) return 0; } -int parse_opt_noop_cb(const struct option *opt, const char *arg, int unset) +int parse_opt_noop_cb(const struct option *opt UNUSED, + const char *arg UNUSED, + int unset UNUSED) { return 0; } diff --git a/parse-options.c b/parse-options.c index 60224cf8d0..e8e076c3a6 100644 --- a/parse-options.c +++ b/parse-options.c @@ -1023,14 +1023,37 @@ static int usage_argh(const struct option *opts, FILE *outfile) return utf8_fprintf(outfile, s, opts->argh ? _(opts->argh) : _("...")); } -#define USAGE_OPTS_WIDTH 24 -#define USAGE_GAP 2 +static int usage_indent(FILE *outfile) +{ + return fprintf(outfile, " "); +} + +#define USAGE_OPTS_WIDTH 26 + +static void usage_padding(FILE *outfile, size_t pos) +{ + if (pos < USAGE_OPTS_WIDTH) + fprintf(outfile, "%*s", USAGE_OPTS_WIDTH - (int)pos, ""); + else + fprintf(outfile, "\n%*s", USAGE_OPTS_WIDTH, ""); +} + +static const struct option *find_option_by_long_name(const struct option *opts, + const char *long_name) +{ + for (; opts->type != OPTION_END; opts++) { + if (opts->long_name && !strcmp(opts->long_name, long_name)) + return opts; + } + return NULL; +} static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *ctx, const char * const *usagestr, const struct option *opts, int full, int err) { + const struct option *all_opts = opts; FILE *outfile = err ? stderr : stdout; int need_newline; @@ -1111,8 +1134,8 @@ 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; + const char *positive_name = NULL; if (opts->type == OPTION_SUBCOMMAND) continue; @@ -1131,7 +1154,7 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t need_newline = 0; } - pos = fprintf(outfile, " "); + pos = usage_indent(outfile); if (opts->short_name) { if (opts->flags & PARSE_OPT_NODASH) pos += fprintf(outfile, "%c", opts->short_name); @@ -1140,8 +1163,15 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t } if (opts->long_name && opts->short_name) pos += fprintf(outfile, ", "); - if (opts->long_name) - pos += fprintf(outfile, "--%s", opts->long_name); + if (opts->long_name) { + const char *long_name = opts->long_name; + if ((opts->flags & PARSE_OPT_NONEG) || + skip_prefix(long_name, "no-", &positive_name)) + pos += fprintf(outfile, "--%s", long_name); + else + pos += fprintf(outfile, "--[no-]%s", long_name); + } + if (opts->type == OPTION_NUMBER) pos += utf8_fprintf(outfile, _("-NUM")); @@ -1149,29 +1179,31 @@ 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 + 1) - pad = -1; - else if (pos <= USAGE_OPTS_WIDTH) - pad = USAGE_OPTS_WIDTH - pos; - else { - fputc('\n', outfile); - pad = USAGE_OPTS_WIDTH; - } if (opts->type == OPTION_ALIAS) { - fprintf(outfile, "%*s", pad + USAGE_GAP, ""); + usage_padding(outfile, pos); fprintf_ln(outfile, _("alias of --%s"), (const char *)opts->value); continue; } - for (cp = _(opts->help); *cp; cp = np) { + for (cp = opts->help ? _(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; + usage_padding(outfile, pos); + fwrite(cp, 1, np - cp, outfile); + pos = 0; + } + fputc('\n', outfile); + + if (positive_name) { + if (find_option_by_long_name(all_opts, positive_name)) + continue; + pos = usage_indent(outfile); + pos += fprintf(outfile, "--%s", positive_name); + usage_padding(outfile, pos); + fprintf_ln(outfile, _("opposite of --no-%s"), + positive_name); } } fputc('\n', outfile); @@ -1252,8 +1252,8 @@ static int format_trailer_match_cb(const struct strbuf *key, void *ud) return 0; } -static struct strbuf *expand_separator(struct strbuf *sb, - const char *argval, size_t arglen) +static struct strbuf *expand_string_arg(struct strbuf *sb, + const char *argval, size_t arglen) { char *fmt = xstrndup(argval, arglen); const char *format = fmt; @@ -1301,9 +1301,9 @@ int format_set_trailers_options(struct process_trailer_options *opts, opts->filter_data = filter_list; opts->only_trailers = 1; } else if (match_placeholder_arg_value(*arg, "separator", arg, &argval, &arglen)) { - opts->separator = expand_separator(sepbuf, argval, arglen); + opts->separator = expand_string_arg(sepbuf, argval, arglen); } else if (match_placeholder_arg_value(*arg, "key_value_separator", arg, &argval, &arglen)) { - opts->key_value_separator = expand_separator(kvsepbuf, argval, arglen); + opts->key_value_separator = expand_string_arg(kvsepbuf, argval, arglen); } else if (!match_placeholder_bool_arg(*arg, "only", arg, &opts->only_trailers) && !match_placeholder_bool_arg(*arg, "unfold", arg, &opts->unfold) && !match_placeholder_bool_arg(*arg, "keyonly", arg, &opts->key_only) && @@ -1384,6 +1384,44 @@ static size_t parse_describe_args(const char *start, struct strvec *args) return arg - start; } + +static int parse_decoration_option(const char **arg, + const char *name, + char **opt) +{ + const char *argval; + size_t arglen; + + if (match_placeholder_arg_value(*arg, name, arg, &argval, &arglen)) { + struct strbuf sb = STRBUF_INIT; + + expand_string_arg(&sb, argval, arglen); + *opt = strbuf_detach(&sb, NULL); + return 1; + } + return 0; +} + +static void parse_decoration_options(const char **arg, + struct decoration_options *opts) +{ + while (parse_decoration_option(arg, "prefix", &opts->prefix) || + parse_decoration_option(arg, "suffix", &opts->suffix) || + parse_decoration_option(arg, "separator", &opts->separator) || + parse_decoration_option(arg, "pointer", &opts->pointer) || + parse_decoration_option(arg, "tag", &opts->tag)) + ; +} + +static void free_decoration_options(const struct decoration_options *opts) +{ + free(opts->prefix); + free(opts->suffix); + free(opts->separator); + free(opts->pointer); + free(opts->tag); +} + static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ const char *placeholder, void *context) @@ -1537,11 +1575,18 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ strbuf_addstr(sb, get_revision_mark(NULL, commit)); return 1; case 'd': - format_decorations(sb, commit, c->auto_color); + format_decorations(sb, commit, c->auto_color, NULL); return 1; case 'D': - format_decorations_extended(sb, commit, c->auto_color, "", ", ", ""); - return 1; + { + const struct decoration_options opts = { + .prefix = "", + .suffix = "" + }; + + format_decorations(sb, commit, c->auto_color, &opts); + return 1; + } case 'S': /* tag/branch like --source */ if (!(c->pretty_ctx->rev && c->pretty_ctx->rev->sources)) return 0; @@ -1638,6 +1683,23 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ return 2; } + if (skip_prefix(placeholder, "(decorate", &arg)) { + struct decoration_options opts = { NULL }; + size_t ret = 0; + + if (*arg == ':') { + arg++; + parse_decoration_options(&arg, &opts); + } + if (*arg == ')') { + format_decorations(sb, commit, c->auto_color, &opts); + ret = arg - placeholder + 1; + } + + free_decoration_options(&opts); + return ret; + } + /* For the rest we have to parse the commit header. */ if (!c->commit_header_parsed) { msg = c->message = diff --git a/range-diff.c b/range-diff.c index 56f6870ff9..c45b6d849c 100644 --- a/range-diff.c +++ b/range-diff.c @@ -230,16 +230,19 @@ cleanup: } static int patch_util_cmp(const void *cmp_data UNUSED, - const struct patch_util *a, - const struct patch_util *b, - const char *keydata) + const struct hashmap_entry *ha, + const struct hashmap_entry *hb, + const void *keydata) { + const struct patch_util + *a = container_of(ha, const struct patch_util, e), + *b = container_of(hb, const struct patch_util, e); return strcmp(a->diff, keydata ? keydata : b->diff); } static void find_exact_matches(struct string_list *a, struct string_list *b) { - struct hashmap map = HASHMAP_INIT((hashmap_cmp_fn)patch_util_cmp, NULL); + struct hashmap map = HASHMAP_INIT(patch_util_cmp, NULL); int i; /* First, add the patches of a to a hash map */ diff --git a/ref-filter.c b/ref-filter.c index 1bfaf20fbf..fae9f4b8ed 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -549,7 +549,8 @@ static int signature_atom_parser(struct ref_format *format UNUSED, return 0; } -static int trailers_atom_parser(struct ref_format *format, struct used_atom *atom, +static int trailers_atom_parser(struct ref_format *format UNUSED, + struct used_atom *atom, const char *arg, struct strbuf *err) { atom->u.contents.trailer_opts.no_divider = 1; @@ -582,9 +583,10 @@ static int contents_atom_parser(struct ref_format *format, struct used_atom *ato atom->u.contents.option = C_BARE; else if (!strcmp(arg, "body")) atom->u.contents.option = C_BODY; - else if (!strcmp(arg, "size")) + else if (!strcmp(arg, "size")) { + atom->type = FIELD_ULONG; atom->u.contents.option = C_LENGTH; - else if (!strcmp(arg, "signature")) + } else if (!strcmp(arg, "signature")) atom->u.contents.option = C_SIG; else if (!strcmp(arg, "subject")) atom->u.contents.option = C_SUB; @@ -690,9 +692,10 @@ static int raw_atom_parser(struct ref_format *format UNUSED, { if (!arg) atom->u.raw_data.option = RAW_BARE; - else if (!strcmp(arg, "size")) + else if (!strcmp(arg, "size")) { + atom->type = FIELD_ULONG; atom->u.raw_data.option = RAW_LENGTH; - else + } else return err_bad_arg(err, "raw", arg); return 0; } @@ -819,7 +822,7 @@ static int if_atom_parser(struct ref_format *format UNUSED, return 0; } -static int rest_atom_parser(struct ref_format *format, +static int rest_atom_parser(struct ref_format *format UNUSED, struct used_atom *atom UNUSED, const char *arg, struct strbuf *err) { @@ -828,7 +831,8 @@ static int rest_atom_parser(struct ref_format *format, return 0; } -static int ahead_behind_atom_parser(struct ref_format *format, struct used_atom *atom, +static int ahead_behind_atom_parser(struct ref_format *format, + struct used_atom *atom UNUSED, const char *arg, struct strbuf *err) { struct string_list_item *item; @@ -1857,7 +1861,8 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct exp v->s = xmemdupz(buf, buf_size); v->s_size = buf_size; } else if (atom->u.raw_data.option == RAW_LENGTH) { - v->s = xstrfmt("%"PRIuMAX, (uintmax_t)buf_size); + v->value = buf_size; + v->s = xstrfmt("%"PRIuMAX, v->value); } continue; } @@ -1883,9 +1888,10 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct exp v->s = strbuf_detach(&sb, NULL); } else if (atom->u.contents.option == C_BODY_DEP) v->s = xmemdupz(bodypos, bodylen); - else if (atom->u.contents.option == C_LENGTH) - v->s = xstrfmt("%"PRIuMAX, (uintmax_t)strlen(subpos)); - else if (atom->u.contents.option == C_BODY) + else if (atom->u.contents.option == C_LENGTH) { + v->value = strlen(subpos); + v->s = xstrfmt("%"PRIuMAX, v->value); + } else if (atom->u.contents.option == C_BODY) v->s = xmemdupz(bodypos, nonsiglen); else if (atom->u.contents.option == C_SIG) v->s = xmemdupz(sigpos, siglen); @@ -2265,6 +2271,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) v->s_size = ATOM_SIZE_UNSPECIFIED; v->handler = append_atom; + v->value = 0; v->atom = atom; if (*name == '*') { @@ -409,6 +409,7 @@ static int cmd_clone(int argc, const char **argv) { const char *branch = NULL; int full_clone = 0, single_branch = 0, show_progress = isatty(2); + int src = 1; struct option clone_options[] = { OPT_STRING('b', "branch", &branch, N_("<branch>"), N_("branch to checkout after clone")), @@ -417,10 +418,13 @@ static int cmd_clone(int argc, const char **argv) OPT_BOOL(0, "single-branch", &single_branch, N_("only download metadata for the branch that will " "be checked out")), + OPT_BOOL(0, "src", &src, + N_("create repository within 'src' directory")), OPT_END(), }; const char * const clone_usage[] = { - N_("scalar clone [<options>] [--] <repo> [<dir>]"), + N_("scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n" + "\t[--[no-]src] <url> [<enlistment>]"), NULL }; const char *url; @@ -456,7 +460,10 @@ static int cmd_clone(int argc, const char **argv) if (is_directory(enlistment)) die(_("directory '%s' exists already"), enlistment); - dir = xstrfmt("%s/src", enlistment); + if (src) + dir = xstrfmt("%s/src", enlistment); + else + dir = xstrdup(enlistment); strbuf_reset(&buf); if (branch) @@ -657,6 +664,7 @@ static int cmd_reconfigure(int argc, const char **argv) git_config(get_scalar_repos, &scalar_repos); for (i = 0; i < scalar_repos.nr; i++) { + int succeeded = 0; const char *dir = scalar_repos.items[i].string; strbuf_reset(&commondir); @@ -667,30 +675,56 @@ static int cmd_reconfigure(int argc, const char **argv) if (errno != ENOENT) { warning_errno(_("could not switch to '%s'"), dir); - res = -1; - continue; + goto loop_end; } strbuf_addstr(&buf, dir); if (remove_deleted_enlistment(&buf)) - res = error(_("could not remove stale " - "scalar.repo '%s'"), dir); - else - warning(_("removing stale scalar.repo '%s'"), + error(_("could not remove stale " + "scalar.repo '%s'"), dir); + else { + warning(_("removed stale scalar.repo '%s'"), dir); + succeeded = 1; + } strbuf_release(&buf); - } else if (discover_git_directory(&commondir, &gitdir) < 0) { - warning_errno(_("git repository gone in '%s'"), dir); - res = -1; - } else { - git_config_clear(); + goto loop_end; + } + + switch (discover_git_directory_reason(&commondir, &gitdir)) { + case GIT_DIR_INVALID_OWNERSHIP: + warning(_("repository at '%s' has different owner"), dir); + goto loop_end; + + case GIT_DIR_INVALID_GITFILE: + case GIT_DIR_INVALID_FORMAT: + warning(_("repository at '%s' has a format issue"), dir); + goto loop_end; + + case GIT_DIR_DISCOVERED: + succeeded = 1; + break; + + default: + warning(_("repository not found in '%s'"), dir); + break; + } - the_repository = &r; - r.commondir = commondir.buf; - r.gitdir = gitdir.buf; + git_config_clear(); - if (set_recommended_config(1) < 0) - res = -1; + the_repository = &r; + r.commondir = commondir.buf; + r.gitdir = gitdir.buf; + + if (set_recommended_config(1) >= 0) + succeeded = 1; + +loop_end: + if (!succeeded) { + res = -1; + warning(_("to unregister this repository from Scalar, run\n" + "\tgit config --global --unset --fixed-value scalar.repo \"%s\""), + dir); } } diff --git a/sequencer.c b/sequencer.c index 5e0c15a16b..d584cac8ed 100644 --- a/sequencer.c +++ b/sequencer.c @@ -51,6 +51,15 @@ #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" +/* + * To accommodate common filesystem limitations, where the loose refs' file + * names must not exceed `NAME_MAX`, the labels generated by `git rebase + * --rebase-merges` need to be truncated if the corresponding commit subjects + * are too long. + * Add some margin to stay clear from reaching `NAME_MAX`. + */ +#define GIT_MAX_LABEL_LENGTH ((NAME_MAX) - (LOCK_SUFFIX_LEN) - 16) + static const char sign_off_header[] = "Signed-off-by: "; static const char cherry_picked_prefix[] = "(cherry picked from commit "; @@ -139,6 +148,11 @@ static GIT_PATH_FUNC(rebase_path_amend, "rebase-merge/amend") */ static GIT_PATH_FUNC(rebase_path_stopped_sha, "rebase-merge/stopped-sha") /* + * When we stop for the user to resolve conflicts this file contains + * the patch of the commit that is being picked. + */ +static GIT_PATH_FUNC(rebase_path_patch, "rebase-merge/patch") +/* * For the post-rewrite hook, we make a list of rewritten commits and * their new sha1s. The rewritten-pending list keeps the sha1s of * commits that have been processed, but not committed yet, @@ -424,10 +438,9 @@ struct commit_message { const char *message; }; -static const char *short_commit_name(struct commit *commit) +static const char *short_commit_name(struct repository *r, struct commit *commit) { - return repo_find_unique_abbrev(the_repository, &commit->object.oid, - DEFAULT_ABBREV); + return repo_find_unique_abbrev(r, &commit->object.oid, DEFAULT_ABBREV); } static int get_message(struct commit *commit, struct commit_message *out) @@ -437,7 +450,7 @@ static int get_message(struct commit *commit, struct commit_message *out) out->message = repo_logmsg_reencode(the_repository, commit, NULL, get_commit_output_encoding()); - abbrev = short_commit_name(commit); + abbrev = short_commit_name(the_repository, commit); subject_len = find_commit_subject(out->message, &subject); @@ -2249,6 +2262,8 @@ static int do_pick_commit(struct repository *r, */ if (command == TODO_REVERT) { + const char *orig_subject; + base = commit; base_label = msg.label; next = parent; @@ -2256,6 +2271,15 @@ static int do_pick_commit(struct repository *r, if (opts->commit_use_reference) { strbuf_addstr(&msgbuf, "# *** SAY WHY WE ARE REVERTING ON THE TITLE LINE ***"); + } else if (skip_prefix(msg.subject, "Revert \"", &orig_subject) && + /* + * We don't touch pre-existing repeated reverts, because + * theoretically these can be nested arbitrarily deeply, + * thus requiring excessive complexity to deal with. + */ + !starts_with(orig_subject, "Revert \"")) { + strbuf_addstr(&msgbuf, "Reapply \""); + strbuf_addstr(&msgbuf, orig_subject); } else { strbuf_addstr(&msgbuf, "Revert \""); strbuf_addstr(&msgbuf, msg.subject); @@ -2311,7 +2335,7 @@ static int do_pick_commit(struct repository *r, const char *dest = git_path_squash_msg(r); unlink(dest); if (copy_file(dest, rebase_path_squash_msg(), 0666)) { - res = error(_("could not rename '%s' to '%s'"), + res = error(_("could not copy '%s' to '%s'"), rebase_path_squash_msg(), dest); goto leave; } @@ -2374,7 +2398,7 @@ static int do_pick_commit(struct repository *r, error(command == TODO_REVERT ? _("could not revert %s... %s") : _("could not apply %s... %s"), - short_commit_name(commit), msg.subject); + short_commit_name(r, commit), msg.subject); print_advice(r, res == 1, opts); repo_rerere(r, opts->allow_rerere_auto); goto leave; @@ -2641,7 +2665,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item, return item->commit ? 0 : -1; } -int sequencer_get_last_command(struct repository *r, enum replay_action *action) +int sequencer_get_last_command(struct repository *r UNUSED, enum replay_action *action) { const char *todo_file, *bol; struct strbuf buf = STRBUF_INIT; @@ -3163,7 +3187,8 @@ static int walk_revs_populate_todo(struct todo_list *todo_list, item->offset_in_buf = todo_list->buf.len; subject_len = find_commit_subject(commit_buffer, &subject); strbuf_addf(&todo_list->buf, "%s %s %.*s\n", command_string, - short_commit_name(commit), subject_len, subject); + short_commit_name(the_repository, commit), + subject_len, subject); repo_unuse_commit_buffer(the_repository, commit, commit_buffer); } @@ -3392,7 +3417,8 @@ give_advice: return -1; } -static int save_todo(struct todo_list *todo_list, struct replay_opts *opts) +static int save_todo(struct todo_list *todo_list, struct replay_opts *opts, + int reschedule) { struct lock_file todo_lock = LOCK_INIT; const char *todo_path = get_todo_path(opts); @@ -3402,7 +3428,7 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts) * rebase -i writes "git-rebase-todo" without the currently executing * command, appending it to "done" instead. */ - if (is_rebase_i(opts)) + if (is_rebase_i(opts) && !reschedule) next++; fd = hold_lock_file_for_update(&todo_lock, todo_path, 0); @@ -3415,7 +3441,7 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts) if (commit_lock_file(&todo_lock) < 0) return error(_("failed to finalize '%s'"), todo_path); - if (is_rebase_i(opts) && next > 0) { + if (is_rebase_i(opts) && !reschedule && next > 0) { const char *done = rebase_path_done(); int fd = open(done, O_CREAT | O_WRONLY | O_APPEND, 0666); int ret = 0; @@ -3496,18 +3522,19 @@ static int make_patch(struct repository *r, struct commit *commit, struct replay_opts *opts) { - struct strbuf buf = STRBUF_INIT; struct rev_info log_tree_opt; const char *subject; char hex[GIT_MAX_HEXSZ + 1]; int res = 0; + if (!is_rebase_i(opts)) + BUG("make_patch should only be called when rebasing"); + oid_to_hex_r(hex, &commit->object.oid); if (write_message(hex, strlen(hex), rebase_path_stopped_sha(), 1) < 0) return -1; res |= write_rebase_head(&commit->object.oid); - strbuf_addf(&buf, "%s/patch", get_dir(opts)); memset(&log_tree_opt, 0, sizeof(log_tree_opt)); repo_init_revisions(r, &log_tree_opt, NULL); log_tree_opt.abbrev = 0; @@ -3515,28 +3542,26 @@ static int make_patch(struct repository *r, log_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH; log_tree_opt.disable_stdin = 1; log_tree_opt.no_commit_id = 1; - log_tree_opt.diffopt.file = fopen(buf.buf, "w"); + log_tree_opt.diffopt.file = fopen(rebase_path_patch(), "w"); log_tree_opt.diffopt.use_color = GIT_COLOR_NEVER; if (!log_tree_opt.diffopt.file) - res |= error_errno(_("could not open '%s'"), buf.buf); + res |= error_errno(_("could not open '%s'"), + rebase_path_patch()); else { res |= log_tree_commit(&log_tree_opt, commit); fclose(log_tree_opt.diffopt.file); } - strbuf_reset(&buf); - strbuf_addf(&buf, "%s/message", get_dir(opts)); - if (!file_exists(buf.buf)) { + if (!file_exists(rebase_path_message())) { const char *encoding = get_commit_output_encoding(); const char *commit_buffer = repo_logmsg_reencode(r, commit, NULL, encoding); find_commit_subject(commit_buffer, &subject); - res |= write_message(subject, strlen(subject), buf.buf, 1); + res |= write_message(subject, strlen(subject), rebase_path_message(), 1); repo_unuse_commit_buffer(r, commit, commit_buffer); } - strbuf_release(&buf); release_revisions(&log_tree_opt); return res; @@ -3584,7 +3609,7 @@ static int error_with_patch(struct repository *r, } else if (exit_code) { if (commit) fprintf_ln(stderr, _("Could not apply %s... %.*s"), - short_commit_name(commit), subject_len, subject); + short_commit_name(r, commit), subject_len, subject); else /* * We don't have the hash of the parent so @@ -4154,6 +4179,7 @@ static int do_merge(struct repository *r, if (ret < 0) { error(_("could not even attempt to merge '%.*s'"), merge_arg_len, arg); + unlink(git_path_merge_msg(r)); goto leave_merge; } /* @@ -4641,6 +4667,68 @@ N_("Could not execute the todo command\n" " git rebase --edit-todo\n" " git rebase --continue\n"); +static int pick_one_commit(struct repository *r, + struct todo_list *todo_list, + struct replay_opts *opts, + int *check_todo, int* reschedule) +{ + int res; + struct todo_item *item = todo_list->items + todo_list->current; + const char *arg = todo_item_get_arg(todo_list, item); + if (is_rebase_i(opts)) + opts->reflog_message = reflog_message( + opts, command_to_string(item->command), NULL); + + res = do_pick_commit(r, item, opts, is_final_fixup(todo_list), + check_todo); + if (is_rebase_i(opts) && res < 0) { + /* Reschedule */ + *reschedule = 1; + return -1; + } + if (item->command == TODO_EDIT) { + struct commit *commit = item->commit; + if (!res) { + if (!opts->verbose) + term_clear_line(); + fprintf(stderr, _("Stopped at %s... %.*s\n"), + short_commit_name(r, commit), item->arg_len, arg); + } + return error_with_patch(r, commit, + arg, item->arg_len, opts, res, !res); + } + if (is_rebase_i(opts) && !res) + record_in_rewritten(&item->commit->object.oid, + peek_command(todo_list, 1)); + if (res && is_fixup(item->command)) { + if (res == 1) + intend_to_amend(); + return error_failed_squash(r, item->commit, opts, + item->arg_len, arg); + } else if (res && is_rebase_i(opts) && item->commit) { + int to_amend = 0; + struct object_id oid; + + /* + * If we are rewording and have either + * fast-forwarded already, or are about to + * create a new root commit, we want to amend, + * otherwise we do not. + */ + if (item->command == TODO_REWORD && + !repo_get_oid(r, "HEAD", &oid) && + (oideq(&item->commit->object.oid, &oid) || + (opts->have_squash_onto && + oideq(&opts->squash_onto, &oid)))) + to_amend = 1; + + return res | error_with_patch(r, item->commit, + arg, item->arg_len, opts, + res, to_amend); + } + return res; +} + static int pick_commits(struct repository *r, struct todo_list *todo_list, struct replay_opts *opts) @@ -4656,12 +4744,17 @@ static int pick_commits(struct repository *r, if (read_and_refresh_cache(r, opts)) return -1; + unlink(rebase_path_message()); + unlink(rebase_path_stopped_sha()); + unlink(rebase_path_amend()); + unlink(rebase_path_patch()); + while (todo_list->current < todo_list->nr) { struct todo_item *item = todo_list->items + todo_list->current; const char *arg = todo_item_get_arg(todo_list, item); int check_todo = 0; - if (save_todo(todo_list, opts)) + if (save_todo(todo_list, opts, reschedule)) return -1; if (is_rebase_i(opts)) { if (item->command != TODO_COMMENT) { @@ -4679,10 +4772,7 @@ static int pick_commits(struct repository *r, todo_list->total_nr, opts->verbose ? "\n" : "\r"); } - unlink(rebase_path_message()); unlink(rebase_path_author_script()); - unlink(rebase_path_stopped_sha()); - unlink(rebase_path_amend()); unlink(git_path_merge_head(r)); unlink(git_path_auto_merge(r)); delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF); @@ -4694,66 +4784,10 @@ static int pick_commits(struct repository *r, } } if (item->command <= TODO_SQUASH) { - if (is_rebase_i(opts)) - opts->reflog_message = reflog_message(opts, - command_to_string(item->command), NULL); - - res = do_pick_commit(r, item, opts, - is_final_fixup(todo_list), - &check_todo); - if (is_rebase_i(opts) && res < 0) { - /* Reschedule */ - advise(_(rescheduled_advice), - get_item_line_length(todo_list, - todo_list->current), - get_item_line(todo_list, - todo_list->current)); - todo_list->current--; - if (save_todo(todo_list, opts)) - return -1; - } - if (item->command == TODO_EDIT) { - struct commit *commit = item->commit; - if (!res) { - if (!opts->verbose) - term_clear_line(); - fprintf(stderr, - _("Stopped at %s... %.*s\n"), - short_commit_name(commit), - item->arg_len, arg); - } - return error_with_patch(r, commit, - arg, item->arg_len, opts, res, !res); - } - if (is_rebase_i(opts) && !res) - record_in_rewritten(&item->commit->object.oid, - peek_command(todo_list, 1)); - if (res && is_fixup(item->command)) { - if (res == 1) - intend_to_amend(); - return error_failed_squash(r, item->commit, opts, - item->arg_len, arg); - } else if (res && is_rebase_i(opts) && item->commit) { - int to_amend = 0; - struct object_id oid; - - /* - * If we are rewording and have either - * fast-forwarded already, or are about to - * create a new root commit, we want to amend, - * otherwise we do not. - */ - if (item->command == TODO_REWORD && - !repo_get_oid(r, "HEAD", &oid) && - (oideq(&item->commit->object.oid, &oid) || - (opts->have_squash_onto && - oideq(&opts->squash_onto, &oid)))) - to_amend = 1; - - return res | error_with_patch(r, item->commit, - arg, item->arg_len, opts, - res, to_amend); - } + res = pick_one_commit(r, todo_list, opts, &check_todo, + &reschedule); + if (!res && item->command == TODO_EDIT) + return 0; } else if (item->command == TODO_EXEC) { char *end_of_arg = (char *)(arg + item->arg_len); int saved = *end_of_arg; @@ -4801,32 +4835,25 @@ static int pick_commits(struct repository *r, get_item_line_length(todo_list, todo_list->current), get_item_line(todo_list, todo_list->current)); - todo_list->current--; - if (save_todo(todo_list, opts)) + if (save_todo(todo_list, opts, reschedule)) return -1; if (item->commit) - return error_with_patch(r, - item->commit, - arg, item->arg_len, - opts, res, 0); + write_rebase_head(&item->commit->object.oid); } else if (is_rebase_i(opts) && check_todo && !res && reread_todo_if_changed(r, todo_list, opts)) { return -1; } - todo_list->current++; if (res) return res; + + todo_list->current++; } if (is_rebase_i(opts)) { struct strbuf head_ref = STRBUF_INIT, buf = STRBUF_INIT; struct stat st; - /* Stopped in the middle, as planned? */ - if (todo_list->current < todo_list->nr) - return 0; - if (read_oneliner(&head_ref, rebase_path_head_name(), 0) && starts_with(head_ref.buf, "refs/")) { const char *msg; @@ -4969,6 +4996,11 @@ static int commit_staged_changes(struct repository *r, is_clean = !has_uncommitted_changes(r, 0); + if (!is_clean && !file_exists(rebase_path_message())) { + const char *gpg_opt = gpg_sign_opt_quoted(opts); + + return error(_(staged_changes_advice), gpg_opt, gpg_opt); + } if (file_exists(rebase_path_amend())) { struct strbuf rev = STRBUF_INIT; struct object_id head, to_amend; @@ -5352,6 +5384,7 @@ struct label_state { struct oidmap commit2label; struct hashmap labels; struct strbuf buf; + int max_label_length; }; static const char *label_oid(struct object_id *oid, const char *label, @@ -5408,6 +5441,8 @@ static const char *label_oid(struct object_id *oid, const char *label, } } else { struct strbuf *buf = &state->buf; + int label_is_utf8 = 1; /* start with this assumption */ + size_t max_len = buf->len + state->max_label_length; /* * Sanitize labels by replacing non-alpha-numeric characters @@ -5416,14 +5451,34 @@ static const char *label_oid(struct object_id *oid, const char *label, * * Note that we retain non-ASCII UTF-8 characters (identified * via the most significant bit). They should be all acceptable - * in file names. We do not validate the UTF-8 here, that's not - * the job of this function. + * in file names. + * + * As we will use the labels as names of (loose) refs, it is + * vital that the name not be longer than the maximum component + * size of the file system (`NAME_MAX`). We are careful to + * truncate the label accordingly, allowing for the `.lock` + * suffix and for the label to be UTF-8 encoded (i.e. we avoid + * truncating in the middle of a character). */ - for (; *label; label++) - if ((*label & 0x80) || isalnum(*label)) + for (; *label && buf->len + 1 < max_len; label++) + if (isalnum(*label) || + (!label_is_utf8 && (*label & 0x80))) strbuf_addch(buf, *label); + else if (*label & 0x80) { + const char *p = label; + + utf8_width(&p, NULL); + if (p) { + if (buf->len + (p - label) > max_len) + break; + strbuf_add(buf, label, p - label); + label = p - 1; + } else { + label_is_utf8 = 0; + strbuf_addch(buf, *label); + } /* avoid leading dash and double-dashes */ - else if (buf->len && buf->buf[buf->len - 1] != '-') + } else if (buf->len && buf->buf[buf->len - 1] != '-') strbuf_addch(buf, '-'); if (!buf->len) { strbuf_addstr(buf, "rev-"); @@ -5485,7 +5540,8 @@ static int make_script_with_merges(struct pretty_print_context *pp, struct string_entry *entry; struct oidset interesting = OIDSET_INIT, child_seen = OIDSET_INIT, shown = OIDSET_INIT; - struct label_state state = { OIDMAP_INIT, { NULL }, STRBUF_INIT }; + struct label_state state = + { OIDMAP_INIT, { NULL }, STRBUF_INIT, GIT_MAX_LABEL_LENGTH }; int abbr = flags & TODO_LIST_ABBREVIATE_CMDS; const char *cmd_pick = abbr ? "p" : "pick", @@ -5493,6 +5549,8 @@ static int make_script_with_merges(struct pretty_print_context *pp, *cmd_reset = abbr ? "t" : "reset", *cmd_merge = abbr ? "m" : "merge"; + git_config_get_int("rebase.maxlabellength", &state.max_label_length); + oidmap_init(&commit2todo, 0); oidmap_init(&state.commit2label, 0); hashmap_init(&state.labels, labels_cmp, NULL, 0); @@ -5529,7 +5587,7 @@ static int make_script_with_merges(struct pretty_print_context *pp, if (!is_empty && (commit->object.flags & PATCHSAME)) { if (flags & TODO_LIST_WARN_SKIPPED_CHERRY_PICKS) warning(_("skipped previously applied commit %s"), - short_commit_name(commit)); + short_commit_name(the_repository, commit)); skipped_commit = 1; continue; } @@ -5765,7 +5823,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc, if (!is_empty && (commit->object.flags & PATCHSAME)) { if (flags & TODO_LIST_WARN_SKIPPED_CHERRY_PICKS) warning(_("skipped previously applied commit %s"), - short_commit_name(commit)); + short_commit_name(r, commit)); skipped_commit = 1; continue; } @@ -5857,7 +5915,8 @@ static void todo_list_add_exec_commands(struct todo_list *todo_list, todo_list->alloc = alloc; } -static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list, +static void todo_list_to_strbuf(struct repository *r, + struct todo_list *todo_list, struct strbuf *buf, int num, unsigned flags) { struct todo_item *item; @@ -5886,7 +5945,7 @@ static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_lis /* add commit id */ if (item->commit) { const char *oid = flags & TODO_LIST_SHORTEN_IDS ? - short_commit_name(item->commit) : + short_commit_name(r, item->commit) : oid_to_hex(&item->commit->object.oid); if (item->command == TODO_FIXUP) { @@ -6194,7 +6253,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla if (checkout_onto(r, opts, onto_name, &oid, orig_head)) goto cleanup; - if (require_clean_work_tree(r, "rebase", "", 1, 1)) + if (require_clean_work_tree(r, "rebase", NULL, 1, 1)) goto cleanup; todo_list_write_total_nr(&new_todo); @@ -6245,7 +6304,7 @@ static int skip_fixupish(const char *subject, const char **p) { int todo_list_rearrange_squash(struct todo_list *todo_list) { struct hashmap subject2item; - int rearranged = 0, *next, *tail, i, nr = 0, alloc = 0; + int rearranged = 0, *next, *tail, i, nr = 0; char **subjects; struct commit_todo_item commit_todo; struct todo_item *items = NULL; @@ -6357,6 +6416,8 @@ int todo_list_rearrange_squash(struct todo_list *todo_list) } if (rearranged) { + ALLOC_ARRAY(items, todo_list->nr); + for (i = 0; i < todo_list->nr; i++) { enum todo_command command = todo_list->items[i].command; int cur = i; @@ -6369,16 +6430,15 @@ int todo_list_rearrange_squash(struct todo_list *todo_list) continue; while (cur >= 0) { - ALLOC_GROW(items, nr + 1, alloc); items[nr++] = todo_list->items[cur]; cur = next[cur]; } } + assert(nr == todo_list->nr); + todo_list->alloc = nr; FREE_AND_NULL(todo_list->items); todo_list->items = items; - todo_list->nr = nr; - todo_list->alloc = alloc; } free(next); @@ -1221,19 +1221,6 @@ static const char *allowed_bare_repo_to_string( return NULL; } -enum discovery_result { - GIT_DIR_NONE = 0, - GIT_DIR_EXPLICIT, - GIT_DIR_DISCOVERED, - GIT_DIR_BARE, - /* these are errors */ - GIT_DIR_HIT_CEILING = -1, - GIT_DIR_HIT_MOUNT_POINT = -2, - GIT_DIR_INVALID_GITFILE = -3, - GIT_DIR_INVALID_OWNERSHIP = -4, - GIT_DIR_DISALLOWED_BARE = -5, -}; - /* * We cannot decide in this function whether we are in the work tree or * not, since the config can only be read _after_ this function was called. @@ -1385,21 +1372,23 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, } } -int discover_git_directory(struct strbuf *commondir, - struct strbuf *gitdir) +enum discovery_result discover_git_directory_reason(struct strbuf *commondir, + struct strbuf *gitdir) { struct strbuf dir = STRBUF_INIT, err = STRBUF_INIT; size_t gitdir_offset = gitdir->len, cwd_len; size_t commondir_offset = commondir->len; struct repository_format candidate = REPOSITORY_FORMAT_INIT; + enum discovery_result result; if (strbuf_getcwd(&dir)) - return -1; + return GIT_DIR_CWD_FAILURE; cwd_len = dir.len; - if (setup_git_directory_gently_1(&dir, gitdir, NULL, 0) <= 0) { + result = setup_git_directory_gently_1(&dir, gitdir, NULL, 0); + if (result <= 0) { strbuf_release(&dir); - return -1; + return result; } /* @@ -1429,11 +1418,11 @@ int discover_git_directory(struct strbuf *commondir, strbuf_setlen(commondir, commondir_offset); strbuf_setlen(gitdir, gitdir_offset); clear_repository_format(&candidate); - return -1; + return GIT_DIR_INVALID_FORMAT; } clear_repository_format(&candidate); - return 0; + return result; } const char *setup_git_directory_gently(int *nongit_ok) @@ -1515,10 +1504,11 @@ const char *setup_git_directory_gently(int *nongit_ok) } *nongit_ok = 1; break; - case GIT_DIR_NONE: + case GIT_DIR_CWD_FAILURE: + case GIT_DIR_INVALID_FORMAT: /* * As a safeguard against setup_git_directory_gently_1 returning - * this value, fallthrough to BUG. Otherwise it is possible to + * these values, fallthrough to BUG. Otherwise it is possible to * set startup_info->have_repository to 1 when we did nothing to * find a repository. */ @@ -42,16 +42,45 @@ const char *resolve_gitdir_gently(const char *suspect, int *return_error_code); #define resolve_gitdir(path) resolve_gitdir_gently((path), NULL) void setup_work_tree(void); + +/* + * discover_git_directory_reason() is similar to discover_git_directory(), + * except it returns an enum value instead. It is important to note that + * a zero-valued return here is actually GIT_DIR_NONE, which is different + * from discover_git_directory. + */ +enum discovery_result { + GIT_DIR_EXPLICIT = 1, + GIT_DIR_DISCOVERED = 2, + GIT_DIR_BARE = 3, + /* these are errors */ + GIT_DIR_HIT_CEILING = -1, + GIT_DIR_HIT_MOUNT_POINT = -2, + GIT_DIR_INVALID_GITFILE = -3, + GIT_DIR_INVALID_OWNERSHIP = -4, + GIT_DIR_DISALLOWED_BARE = -5, + GIT_DIR_INVALID_FORMAT = -6, + GIT_DIR_CWD_FAILURE = -7, +}; +enum discovery_result discover_git_directory_reason(struct strbuf *commondir, + struct strbuf *gitdir); + /* * Find the commondir and gitdir of the repository that contains the current * working directory, without changing the working directory or other global * state. The result is appended to commondir and gitdir. If the discovered * gitdir does not correspond to a worktree, then 'commondir' and 'gitdir' will * both have the same result appended to the buffer. The return value is - * either 0 upon success and non-zero if no repository was found. + * either 0 upon success and -1 if no repository was found. */ -int discover_git_directory(struct strbuf *commondir, - struct strbuf *gitdir); +static inline int discover_git_directory(struct strbuf *commondir, + struct strbuf *gitdir) +{ + if (discover_git_directory_reason(commondir, gitdir) <= 0) + return -1; + return 0; +} + const char *setup_git_directory_gently(int *); const char *setup_git_directory(void); char *prefix_path(const char *prefix, int len, const char *path); diff --git a/sparse-index.c b/sparse-index.c index 1fdb07a9e6..3578feb283 100644 --- a/sparse-index.c +++ b/sparse-index.c @@ -391,7 +391,7 @@ void expand_index(struct index_state *istate, struct pattern_list *pl) strbuf_setlen(&base, 0); strbuf_add(&base, ce->name, strlen(ce->name)); - read_tree_at(istate->repo, tree, &base, &ps, + read_tree_at(istate->repo, tree, &base, 0, &ps, add_path_to_index, &ctx); /* free directory entries. full entries are re-used */ diff --git a/t/helper/test-index-version.c b/t/helper/test-index-version.c deleted file mode 100644 index f3c2dbe0a2..0000000000 --- a/t/helper/test-index-version.c +++ /dev/null @@ -1,15 +0,0 @@ -#include "test-tool.h" -#include "read-cache-ll.h" - -int cmd__index_version(int argc UNUSED, const char **argv UNUSED) -{ - struct cache_header hdr; - int version; - - memset(&hdr,0,sizeof(hdr)); - if (read(0, &hdr, sizeof(hdr)) != sizeof(hdr)) - return 0; - version = ntohl(hdr.hdr_version); - printf("%d\n", version); - return 0; -} diff --git a/t/helper/test-simple-ipc.c b/t/helper/test-simple-ipc.c index 3d1436da59..941ae7e3bc 100644 --- a/t/helper/test-simple-ipc.c +++ b/t/helper/test-simple-ipc.c @@ -278,7 +278,8 @@ static int daemon__run_server(void) static start_bg_wait_cb bg_wait_cb; -static int bg_wait_cb(const struct child_process *cp, void *cb_data) +static int bg_wait_cb(const struct child_process *cp UNUSED, + void *cb_data UNUSED) { int s = ipc_get_active_state(cl_args.path); diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c index abe8a785eb..621ac3dd10 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -38,7 +38,6 @@ static struct test_cmd cmds[] = { { "hashmap", cmd__hashmap }, { "hash-speed", cmd__hash_speed }, { "hexdump", cmd__hexdump }, - { "index-version", cmd__index_version }, { "json-writer", cmd__json_writer }, { "lazy-init-name-hash", cmd__lazy_init_name_hash }, { "match-trees", cmd__match_trees }, diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h index ea2672436c..a641c3a81d 100644 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@ -32,7 +32,6 @@ int cmd__getcwd(int argc, const char **argv); int cmd__hashmap(int argc, const char **argv); int cmd__hash_speed(int argc, const char **argv); int cmd__hexdump(int argc, const char **argv); -int cmd__index_version(int argc, const char **argv); int cmd__json_writer(int argc, const char **argv); int cmd__lazy_init_name_hash(int argc, const char **argv); int cmd__match_trees(int argc, const char **argv); diff --git a/t/helper/test-trace2.c b/t/helper/test-trace2.c index 20c7495f38..d5ca0046c8 100644 --- a/t/helper/test-trace2.c +++ b/t/helper/test-trace2.c @@ -45,7 +45,7 @@ static int get_i(int *p_value, const char *data) * [] "def_param" events for all of the "interesting" pre-defined * config settings. */ -static int ut_001return(int argc, const char **argv) +static int ut_001return(int argc UNUSED, const char **argv) { int rc; @@ -65,7 +65,7 @@ static int ut_001return(int argc, const char **argv) * [] "def_param" events for all of the "interesting" pre-defined * config settings. */ -static int ut_002exit(int argc, const char **argv) +static int ut_002exit(int argc UNUSED, const char **argv) { int rc; @@ -201,7 +201,7 @@ static int ut_006data(int argc, const char **argv) return 0; } -static int ut_007BUG(int argc, const char **argv) +static int ut_007BUG(int argc UNUSED, const char **argv UNUSED) { /* * Exercise BUG() to ensure that the message is printed to trace2. diff --git a/t/lib-credential.sh b/t/lib-credential.sh index 032b2d8fcc..15fc9a31e2 100644 --- a/t/lib-credential.sh +++ b/t/lib-credential.sh @@ -43,6 +43,8 @@ helper_test_clean() { reject $1 https example.com store-user reject $1 https example.com user1 reject $1 https example.com user2 + reject $1 https example.com user-expiry + reject $1 https example.com user-expiry-overwrite reject $1 https example.com user4 reject $1 https example.com user-distinct-pass reject $1 https example.com user-overwrite @@ -431,6 +433,81 @@ helper_test_timeout() { ' } +helper_test_password_expiry_utc() { + HELPER=$1 + + test_expect_success "helper ($HELPER) stores password_expiry_utc" ' + check approve $HELPER <<-\EOF + protocol=https + host=example.com + username=user-expiry + password=pass + password_expiry_utc=9999999999 + EOF + ' + + test_expect_success "helper ($HELPER) gets password_expiry_utc" ' + check fill $HELPER <<-\EOF + protocol=https + host=example.com + username=user-expiry + -- + protocol=https + host=example.com + username=user-expiry + password=pass + password_expiry_utc=9999999999 + -- + EOF + ' + + test_expect_success "helper ($HELPER) overwrites when password_expiry_utc changes" ' + check approve $HELPER <<-\EOF && + protocol=https + host=example.com + username=user-expiry-overwrite + password=pass1 + password_expiry_utc=9999999998 + EOF + check approve $HELPER <<-\EOF && + protocol=https + host=example.com + username=user-expiry-overwrite + password=pass2 + password_expiry_utc=9999999999 + EOF + check fill $HELPER <<-\EOF && + protocol=https + host=example.com + username=user-expiry-overwrite + -- + protocol=https + host=example.com + username=user-expiry-overwrite + password=pass2 + password_expiry_utc=9999999999 + EOF + check reject $HELPER <<-\EOF && + protocol=https + host=example.com + username=user-expiry-overwrite + password=pass2 + EOF + check fill $HELPER <<-\EOF + protocol=https + host=example.com + username=user-expiry-overwrite + -- + protocol=https + host=example.com + username=user-expiry-overwrite + password=askpass-password + -- + askpass: Password for '\''https://user-expiry-overwrite@example.com'\'': + EOF + ' +} + helper_test_oauth_refresh_token() { HELPER=$1 diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh index 4eebd9c2b5..83b83c9abb 100644 --- a/t/lib-gpg.sh +++ b/t/lib-gpg.sh @@ -45,6 +45,7 @@ test_lazy_prereq GPG ' "$TEST_DIRECTORY"/lib-gpg/keyring.gpg && gpg --homedir "${GNUPGHOME}" --import-ownertrust \ "$TEST_DIRECTORY"/lib-gpg/ownertrust && + gpg --homedir "${GNUPGHOME}" --update-trustdb && gpg --homedir "${GNUPGHOME}" </dev/null >/dev/null \ --sign -u committer@example.com ;; diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh index 7ca5b918f0..11d2dc9fe3 100644 --- a/t/lib-rebase.sh +++ b/t/lib-rebase.sh @@ -8,18 +8,21 @@ # - check that non-commit messages have a certain line count with $EXPECT_COUNT # - check the commit count in the commit message header with $EXPECT_HEADER_COUNT # - rewrite a rebase -i script as directed by $FAKE_LINES. -# $FAKE_LINES consists of a sequence of words separated by spaces. -# The following word combinations are possible: +# $FAKE_LINES consists of a sequence of words separated by spaces; +# spaces inside the words are encoded as underscores. +# The following words are possible: # -# "<lineno>" -- add a "pick" line with the SHA1 taken from the -# specified line. +# "<cmd>" -- override the command for the next line specification. Can be +# "pick", "squash", "fixup[_-(c|C)]", "edit", "reword", "drop", +# "merge[_-(c|C)_<rev>]", or "bad" for an invalid command. # -# "<cmd> <lineno>" -- add a line with the specified command -# ("pick", "squash", "fixup"|"fixup_-C"|"fixup_-c", "edit", "reword" or "drop") -# and the SHA1 taken from the specified line. +# "<lineno>" -- add a command, using the specified line as a template. +# If the command has not been overridden, the line will be copied +# verbatim, usually resulting in a "pick" line. # -# "_" -- add a space, like "fixup_-C" implies "fixup -C" and -# "exec_cmd_with_args" add an "exec cmd with args" line. +# "fakesha" -- add a command ("pick" by default), using a fake SHA1. +# +# "exec_[command...]", "break" -- add the specified command. # # "#" -- Add a comment line. # @@ -49,7 +52,7 @@ set_fake_editor () { action=\& for line in $FAKE_LINES; do case $line in - pick|p|squash|s|fixup|f|edit|e|reword|r|drop|d|label|l|reset|r|merge|m) + pick|p|squash|s|fixup|f|edit|e|reword|r|drop|d|label|l|reset|t|merge|m) action="$line";; exec_*|x_*|break|b) echo "$line" | sed 's/_/ /g' >> "$1";; @@ -64,7 +67,7 @@ set_fake_editor () { fakesha) test \& != "$action" || action=pick echo "$action XXXXXXX False commit" >> "$1" - action=pick;; + action=\&;; *) sed -n "${line}s/^[a-z][a-z]*/$action/p" < "$1".tmp >> "$1" action=\&;; diff --git a/t/perf/p2000-sparse-operations.sh b/t/perf/p2000-sparse-operations.sh index 96ed3e1d69..39e92b0841 100755 --- a/t/perf/p2000-sparse-operations.sh +++ b/t/perf/p2000-sparse-operations.sh @@ -134,5 +134,6 @@ test_perf_on_all git diff-files -- $SPARSE_CONE/a test_perf_on_all git diff-tree HEAD test_perf_on_all git diff-tree HEAD -- $SPARSE_CONE/a test_perf_on_all "git worktree add ../temp && git worktree remove ../temp" +test_perf_on_all git check-attr -a -- $SPARSE_CONE/a test_done diff --git a/t/t0007-git-var.sh b/t/t0007-git-var.sh index 8cb597f99c..ff4fd9348c 100755 --- a/t/t0007-git-var.sh +++ b/t/t0007-git-var.sh @@ -268,4 +268,13 @@ test_expect_success 'listing and asking for variables are exclusive' ' test_must_fail git var -l GIT_COMMITTER_IDENT ' +test_expect_success '`git var -l` works even without HOME' ' + ( + XDG_CONFIG_HOME= && + export XDG_CONFIG_HOME && + unset HOME && + git var -l + ) +' + test_done diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index e19a199636..a0ad6192d6 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -13,30 +13,35 @@ usage: test-tool parse-options <options> A helper function for the parse-options API. - --yes get a boolean + --[no-]yes get a boolean -D, --no-doubt begins with 'no-' + --doubt opposite of --no-doubt -B, --no-fear be brave - -b, --boolean increment by one - -4, --or4 bitwise-or boolean with ...0100 - --neg-or4 same as --no-or4 + -b, --[no-]boolean increment by one + -4, --[no-]or4 bitwise-or boolean with ...0100 + --[no-]neg-or4 same as --no-or4 - -i, --integer <n> get a integer + -i, --[no-]integer <n> + get a integer -j <n> get a integer, too -m, --magnitude <n> get a magnitude - --set23 set integer to 23 + --[no-]set23 set integer to 23 --mode1 set integer to 1 (cmdmode option) --mode2 set integer to 2 (cmdmode option) - -L, --length <str> get length of <str> - -F, --file <file> set file to <file> + -L, --[no-]length <str> + get length of <str> + -F, --[no-]file <file> + set file to <file> String options - -s, --string <string> get a string - --string2 <str> get another string - --st <st> get another string (pervert ordering) + -s, --[no-]string <string> + get a string + --[no-]string2 <str> get another string + --[no-]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 + --[no-]list <str> add str to list Magic arguments -NUM set integer to NUM @@ -45,16 +50,17 @@ Magic arguments --no-ambiguous negative ambiguity Standard options - --abbrev[=<n>] use <n> digits to display object names - -v, --verbose be verbose - -n, --dry-run dry run - -q, --quiet be quiet - --expect <string> expected output in the variable dump + --[no-]abbrev[=<n>] use <n> digits to display object names + -v, --[no-]verbose be verbose + -n, --[no-]dry-run dry run + -q, --[no-]quiet be quiet + --[no-]expect <string> + expected output in the variable dump Alias - -A, --alias-source <string> + -A, --[no-]alias-source <string> get a string - -Z, --alias-target <string> + -Z, --[no-]alias-target <string> alias of --alias-source EOF diff --git a/t/t0301-credential-cache.sh b/t/t0301-credential-cache.sh index c02a3b5969..8300faadea 100755 --- a/t/t0301-credential-cache.sh +++ b/t/t0301-credential-cache.sh @@ -29,6 +29,7 @@ test_atexit 'git credential-cache exit' # test that the daemon works with no special setup helper_test cache +helper_test_password_expiry_utc cache helper_test_oauth_refresh_token cache test_expect_success 'socket defaults to ~/.cache/git/credential/socket' ' diff --git a/t/t0303-credential-external.sh b/t/t0303-credential-external.sh index f028fd1418..095574bfc6 100755 --- a/t/t0303-credential-external.sh +++ b/t/t0303-credential-external.sh @@ -45,6 +45,8 @@ test -z "$GIT_TEST_CREDENTIAL_HELPER_SETUP" || helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER" helper_test "$GIT_TEST_CREDENTIAL_HELPER" +helper_test_password_expiry_utc "$GIT_TEST_CREDENTIAL_HELPER" +helper_test_oauth_refresh_token "$GIT_TEST_CREDENTIAL_HELPER" if test -z "$GIT_TEST_CREDENTIAL_HELPER_TIMEOUT"; then say "# skipping timeout tests (GIT_TEST_CREDENTIAL_HELPER_TIMEOUT not set)" diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh index 8a95adf4b5..2a4f35e984 100755 --- a/t/t1092-sparse-checkout-compatibility.sh +++ b/t/t1092-sparse-checkout-compatibility.sh @@ -2259,4 +2259,76 @@ test_expect_success 'worktree is not expanded' ' ensure_not_expanded worktree remove .worktrees/hotfix ' +test_expect_success 'check-attr with pathspec inside sparse definition' ' + init_repos && + + echo "a -crlf myAttr" >>.gitattributes && + run_on_all cp ../.gitattributes ./deep && + + test_all_match git check-attr -a -- deep/a && + + test_all_match git add deep/.gitattributes && + test_all_match git check-attr -a --cached -- deep/a +' + +test_expect_success 'check-attr with pathspec outside sparse definition' ' + init_repos && + + echo "a -crlf myAttr" >>.gitattributes && + run_on_sparse mkdir folder1 && + run_on_all cp ../.gitattributes ./folder1 && + run_on_all cp a folder1/a && + + test_all_match git check-attr -a -- folder1/a && + + git -C full-checkout add folder1/.gitattributes && + test_sparse_match git add --sparse folder1/.gitattributes && + test_all_match git commit -m "add .gitattributes" && + test_sparse_match git sparse-checkout reapply && + test_all_match git check-attr -a --cached -- folder1/a +' + +# NEEDSWORK: The 'diff --check' test is left as 'test_expect_failure' due +# to an underlying issue in oneway_diff() within diff-lib.c. +# 'do_oneway_diff()' is not called as expected for paths that could match +# inside of a sparse directory. Specifically, the 'ce_path_match()' function +# fails to recognize files inside a sparse directory (e.g., when 'folder1/' +# is a sparse directory, 'folder1/a' cannot be recognized). The goal is to +# proceed with 'do_oneway_diff()' if the pathspec could match inside of a +# sparse directory. +test_expect_failure 'diff --check with pathspec outside sparse definition' ' + init_repos && + + write_script edit-contents <<-\EOF && + echo "a " >"$1" + EOF + + test_all_match git config core.whitespace -trailing-space,-space-before-tab && + + echo "a whitespace=trailing-space,space-before-tab" >>.gitattributes && + run_on_all mkdir -p folder1 && + run_on_all cp ../.gitattributes ./folder1 && + test_all_match git add --sparse folder1/.gitattributes && + run_on_all ../edit-contents folder1/a && + test_all_match git add --sparse folder1/a && + + test_sparse_match git sparse-checkout reapply && + test_all_match test_must_fail git diff --check --cached -- folder1/a +' + +test_expect_success 'sparse-index is not expanded: check-attr' ' + init_repos && + + echo "a -crlf myAttr" >>.gitattributes && + mkdir ./sparse-index/folder1 && + cp ./sparse-index/a ./sparse-index/folder1/a && + cp .gitattributes ./sparse-index/deep && + cp .gitattributes ./sparse-index/folder1 && + + git -C sparse-index add deep/.gitattributes && + git -C sparse-index add --sparse folder1/.gitattributes && + ensure_not_expanded check-attr -a --cached -- deep/a && + ensure_not_expanded check-attr -a --cached -- folder1/a +' + test_done diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index 5805d47eb9..10a539158c 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -589,6 +589,16 @@ test_expect_success 'fsck notices submodule entry pointing to null sha1' ' ) ' +test_expect_success 'fsck notices excessively large tree entry name' ' + git init large-name && + ( + cd large-name && + test_commit a-long-name && + git -c fsck.largePathname=warn:10 fsck 2>out && + grep "warning.*large pathname" out + ) +' + while read name path pretty; do while read mode type; do : ${pretty:=$path} diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh index dd811b7fb4..f0737593c3 100755 --- a/t/t1502-rev-parse-parseopt.sh +++ b/t/t1502-rev-parse-parseopt.sh @@ -3,13 +3,29 @@ test_description='test git rev-parse --parseopt' . ./test-lib.sh +check_invalid_long_option () { + spec="$1" + opt="$2" + test_expect_success "test --parseopt invalid switch $opt help output for $spec" ' + { + cat <<-\EOF && + error: unknown option `'${opt#--}\'' + EOF + sed -e 1d -e \$d <"$TEST_DIRECTORY/t1502/$spec.help" + } >expect && + test_expect_code 129 git rev-parse --parseopt -- $opt \ + 2>output <"$TEST_DIRECTORY/t1502/$spec" && + test_cmp expect output + ' +} + test_expect_success 'setup optionspec' ' sed -e "s/^|//" >optionspec <<\EOF |some-command [options] <args>... | |some-command does foo and bar! |-- -|h,help show the help +|h,help! show the help | |foo some nifty option --foo |bar= some cool option --bar with an argument @@ -58,44 +74,8 @@ EOF ' test_expect_success 'test --parseopt help output' ' - sed -e "s/^|//" >expect <<\END_EXPECT && -|cat <<\EOF -|usage: some-command [options] <args>... -| -| some-command does foo and bar! -| -| -h, --help show the help -| --foo some nifty option --foo -| --bar ... some cool option --bar with an argument -| -b, --baz a short and long option -| -|An option group Header -| -C[...] option C with an optional argument -| -d, --data[=...] short and long option with an optional argument -| -|Argument hints -| -B <arg> short option required argument -| --bar2 <arg> long option required argument -| -e, --fuz <with-space> -| short and long option required argument -| -s[<some>] short option optional argument -| --long[=<data>] long option optional argument -| -g, --fluf[=<path>] short and long option optional argument -| --longest <very-long-argument-hint> -| a very long argument hint -| --pair <key=value> with an equals sign in the hint -| --aswitch help te=t contains? fl*g characters!` -| --bswitch <hint> hint has trailing tab character -| --cswitch switch has trailing tab character -| --short-hint <a> with a one symbol hint -| -|Extras -| --extra1 line above used to cause a segfault but no longer does -| -|EOF -END_EXPECT test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec && - test_cmp expect output + test_cmp "$TEST_DIRECTORY/t1502/optionspec.help" output ' test_expect_success 'test --parseopt help output no switches' ' @@ -131,7 +111,7 @@ test_expect_success 'test --parseopt help-all output hidden switches' ' | | some-command does foo and bar! | -| --hidden1 A hidden switch +| --[no-]hidden1 A hidden switch | |EOF END_EXPECT @@ -140,41 +120,12 @@ END_EXPECT ' test_expect_success 'test --parseopt invalid switch help output' ' - sed -e "s/^|//" >expect <<\END_EXPECT && -|error: unknown option `does-not-exist'\'' -|usage: some-command [options] <args>... -| -| some-command does foo and bar! -| -| -h, --help show the help -| --foo some nifty option --foo -| --bar ... some cool option --bar with an argument -| -b, --baz a short and long option -| -|An option group Header -| -C[...] option C with an optional argument -| -d, --data[=...] short and long option with an optional argument -| -|Argument hints -| -B <arg> short option required argument -| --bar2 <arg> long option required argument -| -e, --fuz <with-space> -| short and long option required argument -| -s[<some>] short option optional argument -| --long[=<data>] long option optional argument -| -g, --fluf[=<path>] short and long option optional argument -| --longest <very-long-argument-hint> -| a very long argument hint -| --pair <key=value> with an equals sign in the hint -| --aswitch help te=t contains? fl*g characters!` -| --bswitch <hint> hint has trailing tab character -| --cswitch switch has trailing tab character -| --short-hint <a> with a one symbol hint -| -|Extras -| --extra1 line above used to cause a segfault but no longer does -| -END_EXPECT + { + cat <<-\EOF && + error: unknown option `does-not-exist'\'' + EOF + sed -e 1d -e \$d <"$TEST_DIRECTORY/t1502/optionspec.help" + } >expect && test_expect_code 129 git rev-parse --parseopt -- --does-not-exist 1>/dev/null 2>output < optionspec && test_cmp expect output ' @@ -288,7 +239,7 @@ test_expect_success 'test --parseopt help output: "wrapped" options normal "or:" | [--another-option] |cmd [--yet-another-option] |-- - |h,help show the help + |h,help! show the help EOF sed -e "s/^|//" >expect <<-\END_EXPECT && @@ -322,7 +273,7 @@ test_expect_success 'test --parseopt help output: multi-line blurb after empty l |line |blurb |-- - |h,help show the help + |h,help! show the help EOF sed -e "s/^|//" >expect <<-\END_EXPECT && @@ -343,4 +294,32 @@ test_expect_success 'test --parseopt help output: multi-line blurb after empty l test_cmp expect actual ' +test_expect_success 'test --parseopt help output for optionspec-neg' ' + test_expect_code 129 git rev-parse --parseopt -- \ + -h >output <"$TEST_DIRECTORY/t1502/optionspec-neg" && + test_cmp "$TEST_DIRECTORY/t1502/optionspec-neg.help" output +' + +test_expect_success 'test --parseopt valid options for optionspec-neg' ' + cat >expect <<-\EOF && + set -- --foo --no-foo --no-bar --positive-only --no-negative -- + EOF + git rev-parse --parseopt -- <"$TEST_DIRECTORY/t1502/optionspec-neg" >output \ + --foo --no-foo --no-bar --positive-only --no-negative && + test_cmp expect output +' + +test_expect_success 'test --parseopt positivated option for optionspec-neg' ' + cat >expect <<-\EOF && + set -- --no-no-bar --no-no-bar -- + EOF + git rev-parse --parseopt -- <"$TEST_DIRECTORY/t1502/optionspec-neg" >output \ + --no-no-bar --bar && + test_cmp expect output +' + +check_invalid_long_option optionspec-neg --no-positive-only +check_invalid_long_option optionspec-neg --negative +check_invalid_long_option optionspec-neg --no-no-negative + test_done diff --git a/t/t1502/.gitattributes b/t/t1502/.gitattributes new file mode 100644 index 0000000000..562b12e16e --- /dev/null +++ b/t/t1502/.gitattributes @@ -0,0 +1 @@ +* -whitespace diff --git a/t/t1502/optionspec-neg b/t/t1502/optionspec-neg new file mode 100644 index 0000000000..392f43eb0b --- /dev/null +++ b/t/t1502/optionspec-neg @@ -0,0 +1,8 @@ +some-command [options] <args>... + +some-command does foo and bar! +-- +foo can be negated +no-bar can be positivated +positive-only! cannot be negated +no-negative! cannot be positivated diff --git a/t/t1502/optionspec-neg.help b/t/t1502/optionspec-neg.help new file mode 100644 index 0000000000..7a29f8cb03 --- /dev/null +++ b/t/t1502/optionspec-neg.help @@ -0,0 +1,12 @@ +cat <<\EOF +usage: some-command [options] <args>... + + some-command does foo and bar! + + --[no-]foo can be negated + --no-bar can be positivated + --bar opposite of --no-bar + --positive-only cannot be negated + --no-negative cannot be positivated + +EOF diff --git a/t/t1502/optionspec.help b/t/t1502/optionspec.help new file mode 100755 index 0000000000..cbdd54d41b --- /dev/null +++ b/t/t1502/optionspec.help @@ -0,0 +1,36 @@ +cat <<\EOF +usage: some-command [options] <args>... + + some-command does foo and bar! + + -h, --help show the help + --[no-]foo some nifty option --foo + --[no-]bar ... some cool option --bar with an argument + -b, --[no-]baz a short and long option + +An option group Header + -C[...] option C with an optional argument + -d, --[no-]data[=...] short and long option with an optional argument + +Argument hints + -B <arg> short option required argument + --[no-]bar2 <arg> long option required argument + -e, --[no-]fuz <with-space> + short and long option required argument + -s[<some>] short option optional argument + --[no-]long[=<data>] long option optional argument + -g, --[no-]fluf[=<path>] + short and long option optional argument + --[no-]longest <very-long-argument-hint> + a very long argument hint + --[no-]pair <key=value> + with an equals sign in the hint + --[no-]aswitch help te=t contains? fl*g characters!` + --[no-]bswitch <hint> hint has trailing tab character + --[no-]cswitch switch has trailing tab character + --[no-]short-hint <a> with a one symbol hint + +Extras + --[no-]extra1 line above used to cause a segfault but no longer does + +EOF diff --git a/t/t1600-index.sh b/t/t1600-index.sh index 9368d82f7d..62e7fd1596 100755 --- a/t/t1600-index.sh +++ b/t/t1600-index.sh @@ -118,7 +118,7 @@ test_index_version () { fi && git add a && echo $EXPECTED_OUTPUT_VERSION >expect && - test-tool index-version <.git/index >actual && + git update-index --show-index-version >actual && test_cmp expect actual ) } diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh index b4ab166369..a7b7263b35 100755 --- a/t/t1700-split-index.sh +++ b/t/t1700-split-index.sh @@ -43,7 +43,7 @@ test_expect_success 'enable split index' ' git config splitIndex.maxPercentChange 100 && git update-index --split-index && test-tool dump-split-index .git/index >actual && - indexversion=$(test-tool index-version <.git/index) && + indexversion=$(git update-index --show-index-version) && # NEEDSWORK: Stop hard-coding checksums. if test "$indexversion" = "4" diff --git a/t/t2004-checkout-cache-temp.sh b/t/t2004-checkout-cache-temp.sh index b16d69ca4a..45dd1bc858 100755 --- a/t/t2004-checkout-cache-temp.sh +++ b/t/t2004-checkout-cache-temp.sh @@ -117,6 +117,26 @@ test_expect_success 'checkout all stages/one file to temporary files' ' test $(cat $s3) = tree3path1) ' +test_expect_success '--stage=all implies --temp' ' + rm -f path* .merge_* actual && + git checkout-index --stage=all -- path1 && + test_path_is_missing path1 +' + +test_expect_success 'overriding --stage=all resets implied --temp' ' + rm -f path* .merge_* actual && + git checkout-index --stage=all --stage=2 -- path1 && + echo tree2path1 >expect && + test_cmp expect path1 +' + +test_expect_success '--stage=all --no-temp is rejected' ' + rm -f path* .merge_* actual && + test_must_fail git checkout-index --stage=all --no-temp -- path1 2>err && + grep -v "already exists" err && + grep "options .--stage=all. and .--no-temp. cannot be used together" err +' + test_expect_success 'checkout some stages/one file to temporary files' ' rm -f path* .merge_* actual && git checkout-index --stage=all --temp -- path2 >actual && diff --git a/t/t2104-update-index-skip-worktree.sh b/t/t2104-update-index-skip-worktree.sh index b8686aabd3..0bab134d71 100755 --- a/t/t2104-update-index-skip-worktree.sh +++ b/t/t2104-update-index-skip-worktree.sh @@ -39,7 +39,7 @@ test_expect_success 'setup' ' ' test_expect_success 'index is at version 2' ' - test "$(test-tool index-version < .git/index)" = 2 + test "$(git update-index --show-index-version)" = 2 ' test_expect_success 'update-index --skip-worktree' ' @@ -48,7 +48,7 @@ test_expect_success 'update-index --skip-worktree' ' ' test_expect_success 'index is at version 3 after having some skip-worktree entries' ' - test "$(test-tool index-version < .git/index)" = 3 + test "$(git update-index --show-index-version)" = 3 ' test_expect_success 'ls-files -t' ' @@ -61,7 +61,7 @@ test_expect_success 'update-index --no-skip-worktree' ' ' test_expect_success 'index version is back to 2 when there is no skip-worktree entry' ' - test "$(test-tool index-version < .git/index)" = 2 + test "$(git update-index --show-index-version)" = 2 ' test_done diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh index 89b285fa3a..22f4c92399 100755 --- a/t/t2107-update-index-basic.sh +++ b/t/t2107-update-index-basic.sh @@ -111,4 +111,35 @@ test_expect_success '--chmod=+x and chmod=-x in the same argument list' ' test_cmp expect actual ' +test_expect_success '--index-version' ' + git commit --allow-empty -m snap && + git reset --hard && + git rm -f -r --cached . && + + # The default index version is 2 --- update this test + # when you change it in the code + git update-index --show-index-version >actual && + echo 2 >expect && + test_cmp expect actual && + + # The next test wants us to be using version 2 + git update-index --index-version 2 && + + git update-index --index-version 4 --verbose >actual && + echo "index-version: was 2, set to 4" >expect && + test_cmp expect actual && + + git update-index --index-version 4 --verbose >actual && + echo "index-version: was 4, set to 4" >expect && + test_cmp expect actual && + + git update-index --index-version 2 --verbose >actual && + echo "index-version: was 4, set to 2" >expect && + test_cmp expect actual && + + # non-verbose should be silent + git update-index --index-version 4 >actual && + test_must_be_empty actual +' + test_done diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh index 051363acbb..df4aff7825 100755 --- a/t/t2400-worktree-add.sh +++ b/t/t2400-worktree-add.sh @@ -121,7 +121,8 @@ test_expect_success '"add" worktree creating new branch' ' test_expect_success 'die the same branch is already checked out' ' ( cd here && - test_must_fail git checkout newmain + test_must_fail git checkout newmain 2>actual && + grep "already used by worktree at" actual ) ' diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index daf1666df7..080e4f24a6 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -942,7 +942,19 @@ test_expect_success 'test deleting branch without config' ' test_expect_success 'deleting currently checked out branch fails' ' git worktree add -b my7 my7 && test_must_fail git -C my7 branch -d my7 && - test_must_fail git branch -d my7 && + test_must_fail git branch -d my7 2>actual && + grep "^error: Cannot delete branch .my7. used by worktree at " actual && + rm -r my7 && + git worktree prune +' + +test_expect_success 'deleting in-use branch fails' ' + git worktree add my7 && + test_commit -C my7 bt7 && + git -C my7 bisect start HEAD HEAD~2 && + test_must_fail git -C my7 branch -d my7 && + test_must_fail git branch -d my7 2>actual && + grep "^error: Cannot delete branch .my7. used by worktree at " actual && rm -r my7 && git worktree prune ' diff --git a/t/t3321-notes-stripspace.sh b/t/t3321-notes-stripspace.sh index 028d825e8f..36abdca5ee 100755 --- a/t/t3321-notes-stripspace.sh +++ b/t/t3321-notes-stripspace.sh @@ -5,6 +5,7 @@ test_description='Test commit notes with stripspace behavior' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh MULTI_LF="$LF$LF$LF" diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh index 3ce918fdb8..d3df19a51f 100755 --- a/t/t3400-rebase.sh +++ b/t/t3400-rebase.sh @@ -421,7 +421,7 @@ test_expect_success 'refuse to switch to branch checked out elsewhere' ' git checkout main && git worktree add wt && test_must_fail git -C wt rebase main main 2>err && - test_i18ngrep "already checked out" err + test_i18ngrep "already used by worktree at" err ' test_expect_success MINGW,SYMLINKS_WINDOWS 'rebase when .git/logs is a symlink' ' diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 96a56aafbe..8ea2bf1302 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -604,7 +604,8 @@ test_expect_success 'clean error after failed "exec"' ' echo "edited again" > file7 && git add file7 && test_must_fail git rebase --continue 2>error && - test_i18ngrep "you have staged changes in your working tree" error + test_i18ngrep "you have staged changes in your working tree" error && + test_i18ngrep ! "could not open.*for reading" error ' test_expect_success 'rebase a detached HEAD' ' @@ -758,7 +759,7 @@ test_expect_success 'reword' ' git show HEAD~2 | grep "C changed" ' -test_expect_success 'no uncommited changes when rewording the todo list is reloaded' ' +test_expect_success 'no uncommitted changes when rewording and the todo list is reloaded' ' git checkout E && test_when_finished "git checkout @{-1}" && ( @@ -1276,20 +1277,34 @@ test_expect_success 'todo count' ' ' test_expect_success 'rebase -i commits that overwrite untracked files (pick)' ' - git checkout --force branch2 && + git checkout --force A && git clean -f && + cat >todo <<-EOF && + exec >file2 + pick $(git rev-parse B) B + pick $(git rev-parse C) C + pick $(git rev-parse D) D + exec cat .git/rebase-merge/done >actual + EOF ( - set_fake_editor && - FAKE_LINES="edit 1 2" git rebase -i A - ) && - test_cmp_rev HEAD F && - test_path_is_missing file6 && - >file6 && - test_must_fail git rebase --continue && - test_cmp_rev HEAD F && - rm file6 && + set_replace_editor todo && + test_must_fail git rebase -i A + ) && + test_cmp_rev HEAD B && + test_cmp_rev REBASE_HEAD C && + head -n3 todo >expect && + test_cmp expect .git/rebase-merge/done && + rm file2 && + test_path_is_missing .git/rebase-merge/patch && + echo changed >file1 && + git add file1 && + test_must_fail git rebase --continue 2>err && + grep "error: you have staged changes in your working tree" err && + git reset --hard HEAD && git rebase --continue && - test_cmp_rev HEAD I + test_cmp_rev HEAD D && + tail -n3 todo >>expect && + test_cmp expect actual ' test_expect_success 'rebase -i commits that overwrite untracked files (squash)' ' @@ -1305,7 +1320,14 @@ test_expect_success 'rebase -i commits that overwrite untracked files (squash)' >file6 && test_must_fail git rebase --continue && test_cmp_rev HEAD F && + test_cmp_rev REBASE_HEAD I && rm file6 && + test_path_is_missing .git/rebase-merge/patch && + echo changed >file1 && + git add file1 && + test_must_fail git rebase --continue 2>err && + grep "error: you have staged changes in your working tree" err && + git reset --hard HEAD && git rebase --continue && test $(git cat-file commit HEAD | sed -ne \$p) = I && git reset --hard original-branch2 @@ -1323,7 +1345,14 @@ test_expect_success 'rebase -i commits that overwrite untracked files (no ff)' ' >file6 && test_must_fail git rebase --continue && test $(git cat-file commit HEAD | sed -ne \$p) = F && + test_cmp_rev REBASE_HEAD I && rm file6 && + test_path_is_missing .git/rebase-merge/patch && + echo changed >file1 && + git add file1 && + test_must_fail git rebase --continue 2>err && + grep "error: you have staged changes in your working tree" err && + git reset --hard HEAD && git rebase --continue && test $(git cat-file commit HEAD | sed -ne \$p) = I ' diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh index fb7b68990c..c4e2fcac67 100755 --- a/t/t3418-rebase-continue.sh +++ b/t/t3418-rebase-continue.sh @@ -268,6 +268,24 @@ test_expect_success 'the todo command "break" works' ' test_path_is_file execed ' +test_expect_success 'patch file is removed before break command' ' + test_when_finished "git rebase --abort" && + cat >todo <<-\EOF && + pick commit-new-file-F2-on-topic-branch + break + EOF + + ( + set_replace_editor todo && + test_must_fail git rebase -i --onto commit-new-file-F2 HEAD + ) && + test_path_is_file .git/rebase-merge/patch && + echo 22>F2 && + git add F2 && + git rebase --continue && + test_path_is_missing .git/rebase-merge/patch +' + test_expect_success '--reschedule-failed-exec' ' test_when_finished "git rebase --abort" && test_must_fail git rebase -x false --reschedule-failed-exec HEAD^ && diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh index 96ae0edf1e..59b5d6b6f2 100755 --- a/t/t3430-rebase-merges.sh +++ b/t/t3430-rebase-merges.sh @@ -128,14 +128,24 @@ test_expect_success 'generate correct todo list' ' ' test_expect_success '`reset` refuses to overwrite untracked files' ' - git checkout -b refuse-to-reset && + git checkout B && test_commit dont-overwrite-untracked && - git checkout @{-1} && - : >dont-overwrite-untracked.t && - echo "reset refs/tags/dont-overwrite-untracked" >script-from-scratch && + cat >script-from-scratch <<-EOF && + exec >dont-overwrite-untracked.t + pick $(git rev-parse B) B + reset refs/tags/dont-overwrite-untracked + pick $(git rev-parse C) C + exec cat .git/rebase-merge/done >actual + EOF test_config sequence.editor \""$PWD"/replace-editor.sh\" && - test_must_fail git rebase -ir HEAD && - git rebase --abort + test_must_fail git rebase -ir A && + test_cmp_rev HEAD B && + head -n3 script-from-scratch >expect && + test_cmp expect .git/rebase-merge/done && + rm dont-overwrite-untracked.t && + git rebase --continue && + tail -n3 script-from-scratch >>expect && + test_cmp expect actual ' test_expect_success '`reset` rejects trees' ' @@ -165,12 +175,16 @@ test_expect_success 'failed `merge -C` writes patch (may be rescheduled, too)' ' test_config sequence.editor \""$PWD"/replace-editor.sh\" && test_tick && test_must_fail git rebase -ir HEAD && + test_cmp_rev REBASE_HEAD H^0 && grep "^merge -C .* G$" .git/rebase-merge/done && grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo && - test_path_is_file .git/rebase-merge/patch && + test_path_is_missing .git/rebase-merge/patch && + echo changed >file1 && + git add file1 && + test_must_fail git rebase --continue 2>err && + grep "error: you have staged changes in your working tree" err && : fail because of merge conflict && - rm G.t .git/rebase-merge/patch && git reset --hard conflicting-G && test_must_fail git rebase --continue && ! grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo && @@ -586,4 +600,15 @@ test_expect_success 'progress shows the correct total' ' test_line_count = 14 progress ' +test_expect_success 'truncate label names' ' + commit=$(git commit-tree -p HEAD^ -p HEAD -m "0123456789 我 123" HEAD^{tree}) && + git merge --ff-only $commit && + + done="$(git rev-parse --git-path rebase-merge/done)" && + git -c rebase.maxLabelLength=14 rebase --rebase-merges -x "cp \"$done\" out" --root && + grep "label 0123456789-我$" out && + git -c rebase.maxLabelLength=13 rebase --rebase-merges -x "cp \"$done\" out" --root && + grep "label 0123456789-$" out +' + test_done diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh index e2ef619323..4158590322 100755 --- a/t/t3501-revert-cherry-pick.sh +++ b/t/t3501-revert-cherry-pick.sh @@ -176,6 +176,29 @@ test_expect_success 'advice from failed revert' ' test_cmp expected actual ' +test_expect_subject () { + echo "$1" >expect && + git log -1 --pretty=%s >actual && + test_cmp expect actual +} + +test_expect_success 'titles of fresh reverts' ' + test_commit --no-tag A file1 && + test_commit --no-tag B file1 && + git revert --no-edit HEAD && + test_expect_subject "Revert \"B\"" && + git revert --no-edit HEAD && + test_expect_subject "Reapply \"B\"" && + git revert --no-edit HEAD && + test_expect_subject "Revert \"Reapply \"B\"\"" +' + +test_expect_success 'title of legacy double revert' ' + test_commit --no-tag "Revert \"Revert \"B\"\"" file1 && + git revert --no-edit HEAD && + test_expect_subject "Revert \"Revert \"Revert \"B\"\"\"" +' + test_expect_success 'identification of reverted commit (default)' ' test_commit to-ident && test_when_finished "git reset --hard to-ident" && diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 3cf2b7a7fb..0a4ab36c3a 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -1373,7 +1373,27 @@ test_expect_success '--rfc' ' Subject: [RFC PATCH 1/1] header with . in it EOF git format-patch -n -1 --stdout --rfc >patch && - grep ^Subject: patch >actual && + grep "^Subject:" patch >actual && + test_cmp expect actual +' + +test_expect_success '--rfc does not overwrite prefix' ' + cat >expect <<-\EOF && + Subject: [RFC PATCH foobar 1/1] header with . in it + EOF + git -c format.subjectPrefix="PATCH foobar" \ + format-patch -n -1 --stdout --rfc >patch && + grep "^Subject:" patch >actual && + test_cmp expect actual +' + +test_expect_success '--rfc is argument order independent' ' + cat >expect <<-\EOF && + Subject: [RFC PATCH foobar 1/1] header with . in it + EOF + git format-patch -n -1 --stdout --rfc \ + --subject-prefix="PATCH foobar" >patch && + grep "^Subject:" patch >actual && test_cmp expect actual ' @@ -1991,6 +2011,20 @@ test_expect_success 'cover letter using branch description (6)' ' grep hello actual ' +test_expect_success 'cover letter with --description-file' ' + test_when_finished "rm -f description.txt" && + cat >description.txt <<-\EOF && + subject from file + + body from file + EOF + git checkout rebuild-1 && + git format-patch --stdout --cover-letter --cover-from-description auto \ + --description-file description.txt main >actual && + grep "^Subject: \[PATCH 0/2\] subject from file$" actual && + grep "^body from file$" actual +' + test_expect_success 'cover letter with nothing' ' git format-patch --stdout --cover-letter >actual && test_line_count = 0 actual diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh index b298f220e0..fcd2473e52 100755 --- a/t/t4015-diff-whitespace.sh +++ b/t/t4015-diff-whitespace.sh @@ -1,7 +1,7 @@ #!/bin/sh # # Copyright (c) 2006 Johannes E. Schindelin -# +# Copyright (c) 2023 Google LLC test_description='Test special whitespace in diff engine. @@ -11,6 +11,43 @@ TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-diff.sh +for opt_res in --patch --quiet -s --stat --shortstat --dirstat=lines \ + --raw! --name-only! --name-status! +do + opts=${opt_res%!} expect_failure= + test "$opts" = "$opt_res" || + expect_failure="test_expect_code 1" + + test_expect_success "status with $opts (different)" ' + echo foo >x && + git add x && + echo bar >x && + test_expect_code 1 git diff -w $opts --exit-code x + ' + + test_expect_success POSIXPERM "status with $opts (mode differs)" ' + test_when_finished "git update-index --chmod=-x x" && + echo foo >x && + git add x && + git update-index --chmod=+x x && + test_expect_code 1 git diff -w $opts --exit-code x + ' + + test_expect_success "status with $opts (removing an empty file)" ' + : >x && + git add x && + rm x && + test_expect_code 1 git diff -w $opts --exit-code -- x + ' + + test_expect_success "status with $opts (different but equivalent)" ' + echo foo >x && + git add x && + echo " foo" >x && + $expect_failure git diff -w $opts --exit-code x + ' +done + test_expect_success "Ray Lehtiniemi's example" ' cat <<-\EOF >x && do { diff --git a/t/t4017-diff-retval.sh b/t/t4017-diff-retval.sh index 5bc28ad9f0..f439f469bd 100755 --- a/t/t4017-diff-retval.sh +++ b/t/t4017-diff-retval.sh @@ -138,4 +138,9 @@ test_expect_success 'check honors conflict marker length' ' git reset --hard ' +test_expect_success 'option errors are not confused by --exit-code' ' + test_must_fail git diff --exit-code --nonsense 2>err && + grep '^usage:' err +' + test_done diff --git a/t/t4040-whitespace-status.sh b/t/t4040-whitespace-status.sh index e70e020ae9..eec3d73dc2 100755 --- a/t/t4040-whitespace-status.sh +++ b/t/t4040-whitespace-status.sh @@ -28,8 +28,7 @@ test_expect_success 'diff-tree --exit-code' ' test_expect_success 'diff-tree -b --exit-code' ' git diff -b --exit-code HEAD^ HEAD && - git diff-tree -b -p --exit-code HEAD^ HEAD && - git diff-tree -b --exit-code HEAD^ HEAD + git diff-tree -b -p --exit-code HEAD^ HEAD ' test_expect_success 'diff-index --cached --exit-code' ' diff --git a/t/t4052-stat-output.sh b/t/t4052-stat-output.sh index 3ee27e277d..beb2ec2a55 100755 --- a/t/t4052-stat-output.sh +++ b/t/t4052-stat-output.sh @@ -49,12 +49,41 @@ log -1 --stat EOF cat >expect.60 <<-'EOF' - ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 + + ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 + EOF cat >expect.6030 <<-'EOF' ...aaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 + EOF -cat >expect2.60 <<-'EOF' +while read verb expect cmd args +do + # No width limit applied when statNameWidth is ignored + case "$expect" in expect72|expect.6030) + test_expect_success "$cmd $verb statNameWidth config with long name" ' + git -c diff.statNameWidth=30 $cmd $args >output && + grep " | " output >actual && + test_cmp $expect actual + ';; + esac + # Maximum width limit still applied when statNameWidth is ignored + case "$expect" in expect.60|expect.6030) + test_expect_success "$cmd --stat=width $verb statNameWidth config with long name" ' + git -c diff.statNameWidth=30 $cmd $args --stat=60 >output && + grep " | " output >actual && + test_cmp $expect actual + ';; + esac +done <<\EOF +ignores expect72 format-patch -1 --stdout +ignores expect.60 format-patch -1 --stdout +respects expect.6030 diff HEAD^ HEAD --stat +respects expect.6030 show --stat +respects expect.6030 log -1 --stat +EOF + +cat >expect.40 <<-'EOF' + ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 + +EOF +cat >expect2.40 <<-'EOF' ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 + ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 + EOF @@ -67,16 +96,16 @@ do test_expect_success "$cmd --stat=width: a long name is given more room when the bar is short" ' git $cmd $args --stat=40 >output && grep " | " output >actual && - test_cmp $expect.60 actual + test_cmp $expect.40 actual ' test_expect_success "$cmd --stat-width=width with long name" ' git $cmd $args --stat-width=40 >output && grep " | " output >actual && - test_cmp $expect.60 actual + test_cmp $expect.40 actual ' - test_expect_success "$cmd --stat=...,name-width with long name" ' + test_expect_success "$cmd --stat=width,name-width with long name" ' git $cmd $args --stat=60,30 >output && grep " | " output >actual && test_cmp $expect.6030 actual @@ -94,7 +123,6 @@ expect show --stat expect log -1 --stat EOF - test_expect_success 'preparation for big change tests' ' >abcd && git add abcd && @@ -207,7 +235,6 @@ respects expect40 show --stat respects expect40 log -1 --stat EOF - cat >expect <<'EOF' abcd | 1000 ++++++++++++++++++++++++++ EOF diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh index 6781cc9078..5f059f65fc 100755 --- a/t/t4053-diff-no-index.sh +++ b/t/t4053-diff-no-index.sh @@ -224,6 +224,25 @@ test_expect_success "diff --no-index treats '-' as stdin" ' test_must_be_empty actual ' +test_expect_success "diff --no-index -R treats '-' as stdin" ' + cat >expect <<-EOF && + diff --git b/a/1 a/- + index $(git hash-object --stdin <a/1)..$ZERO_OID 100644 + --- b/a/1 + +++ a/- + @@ -1 +1 @@ + -1 + +x + EOF + + test_write_lines x | test_expect_code 1 \ + git -c core.abbrev=no diff --no-index -R -- - a/1 >actual && + test_cmp expect actual && + + test_write_lines 1 | git diff --no-index -R -- a/1 - >actual && + test_must_be_empty actual +' + test_expect_success 'diff --no-index refuses to diff stdin and a directory' ' test_must_fail git diff --no-index -- - a </dev/null 2>err && grep "fatal: cannot compare stdin to a directory" err diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh index dd9035aa38..16626e4fe9 100755 --- a/t/t4205-log-pretty-formats.sh +++ b/t/t4205-log-pretty-formats.sh @@ -576,6 +576,38 @@ test_expect_success 'clean log decoration' ' test_cmp expected actual1 ' +test_expect_success 'pretty format %decorate' ' + git checkout -b foo && + git commit --allow-empty -m "new commit" && + git tag bar && + git branch qux && + + echo " (HEAD -> foo, tag: bar, qux)" >expect1 && + git log --format="%(decorate)" -1 >actual1 && + test_cmp expect1 actual1 && + + echo "HEAD -> foo, tag: bar, qux" >expect2 && + git log --format="%(decorate:prefix=,suffix=)" -1 >actual2 && + test_cmp expect2 actual2 && + + echo "[ HEAD -> foo; tag: bar; qux ]" >expect3 && + git log --format="%(decorate:prefix=[ ,suffix= ],separator=%x3B )" \ + -1 >actual3 && + test_cmp expect3 actual3 && + + # Try with a typo (in "separator"), in which case the placeholder should + # not be replaced. + echo "%(decorate:prefix=[ ,suffix= ],separater=; )" >expect4 && + git log --format="%(decorate:prefix=[ ,suffix= ],separater=%x3B )" \ + -1 >actual4 && + test_cmp expect4 actual4 && + + echo "HEAD->foo bar qux" >expect5 && + git log --format="%(decorate:prefix=,suffix=,separator= ,tag=,pointer=->)" \ + -1 >actual5 && + test_cmp expect5 actual5 +' + cat >trailers <<EOF Signed-off-by: A U Thor <author@example.com> Acked-by: A U Thor <author@example.com> diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh index ded33a82e2..21986a866d 100755 --- a/t/t4207-log-decoration-colors.sh +++ b/t/t4207-log-decoration-colors.sh @@ -53,15 +53,17 @@ cmp_filtered_decorations () { # to this test since it does not contain any decoration, hence --first-parent test_expect_success 'commit decorations colored correctly' ' cat >expect <<-EOF && - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD -> \ -${c_reset}${c_branch}main${c_reset}${c_commit}, \ -${c_reset}${c_tag}tag: v1.0${c_reset}${c_commit}, \ -${c_reset}${c_tag}tag: B${c_reset}${c_commit})${c_reset} B -${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A1${c_reset}${c_commit}, \ + ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}\ +${c_commit} -> ${c_reset}${c_branch}main${c_reset}${c_commit}, \ +${c_reset}${c_tag}tag: ${c_reset}${c_tag}v1.0${c_reset}${c_commit}, \ +${c_reset}${c_tag}tag: ${c_reset}${c_tag}B${c_reset}${c_commit})${c_reset} B +${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\ +${c_tag}tag: ${c_reset}${c_tag}A1${c_reset}${c_commit}, \ ${c_reset}${c_remoteBranch}other/main${c_reset}${c_commit})${c_reset} A1 - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_stash}refs/stash${c_reset}${c_commit})${c_reset} \ -On main: Changes to A.t - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A + ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\ +${c_stash}refs/stash${c_reset}${c_commit})${c_reset} On main: Changes to A.t + ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\ +${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A EOF git log --first-parent --no-abbrev --decorate --oneline --color=always --all >actual && @@ -76,12 +78,14 @@ test_expect_success 'test coloring with replace-objects' ' git replace HEAD~1 HEAD~2 && cat >expect <<-EOF && - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD -> \ -${c_reset}${c_branch}main${c_reset}${c_commit}, \ -${c_reset}${c_tag}tag: D${c_reset}${c_commit})${c_reset} D - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: C${c_reset}${c_commit}, \ + ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}\ +${c_commit} -> ${c_reset}${c_branch}main${c_reset}${c_commit}, \ +${c_reset}${c_tag}tag: ${c_reset}${c_tag}D${c_reset}${c_commit})${c_reset} D + ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\ +${c_tag}tag: ${c_reset}${c_tag}C${c_reset}${c_commit}, \ ${c_reset}${c_grafted}replaced${c_reset}${c_commit})${c_reset} B - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A + ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\ +${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A EOF git log --first-parent --no-abbrev --decorate --oneline --color=always HEAD >actual && @@ -100,13 +104,15 @@ test_expect_success 'test coloring with grafted commit' ' git replace --graft HEAD HEAD~2 && cat >expect <<-EOF && - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD -> \ -${c_reset}${c_branch}main${c_reset}${c_commit}, \ -${c_reset}${c_tag}tag: D${c_reset}${c_commit}, \ + ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_HEAD}HEAD${c_reset}\ +${c_commit} -> ${c_reset}${c_branch}main${c_reset}${c_commit}, \ +${c_reset}${c_tag}tag: ${c_reset}${c_tag}D${c_reset}${c_commit}, \ ${c_reset}${c_grafted}replaced${c_reset}${c_commit})${c_reset} D - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: v1.0${c_reset}${c_commit}, \ -${c_reset}${c_tag}tag: B${c_reset}${c_commit})${c_reset} B - ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}${c_tag}tag: A${c_reset}${c_commit})${c_reset} A + ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\ +${c_tag}tag: ${c_reset}${c_tag}v1.0${c_reset}${c_commit}, \ +${c_reset}${c_tag}tag: ${c_reset}${c_tag}B${c_reset}${c_commit})${c_reset} B + ${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_reset}\ +${c_tag}tag: ${c_reset}${c_tag}A${c_reset}${c_commit})${c_reset} A EOF git log --first-parent --no-abbrev --decorate --oneline --color=always HEAD >actual && diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh index 4df76173a8..ba65f17dd9 100755 --- a/t/t5318-commit-graph.sh +++ b/t/t5318-commit-graph.sh @@ -450,14 +450,15 @@ GRAPH_BYTE_FANOUT2=$(($GRAPH_FANOUT_OFFSET + 4 * 255)) GRAPH_OID_LOOKUP_OFFSET=$(($GRAPH_FANOUT_OFFSET + 4 * 256)) GRAPH_BYTE_OID_LOOKUP_ORDER=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * 8)) GRAPH_BYTE_OID_LOOKUP_MISSING=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * 4 + 10)) +GRAPH_COMMIT_DATA_WIDTH=$(($HASH_LEN + 16)) GRAPH_COMMIT_DATA_OFFSET=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * $NUM_COMMITS)) GRAPH_BYTE_COMMIT_TREE=$GRAPH_COMMIT_DATA_OFFSET GRAPH_BYTE_COMMIT_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN)) GRAPH_BYTE_COMMIT_EXTRA_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 4)) GRAPH_BYTE_COMMIT_WRONG_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 3)) GRAPH_BYTE_COMMIT_GENERATION=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 11)) +GRAPH_BYTE_COMMIT_GENERATION_LAST=$(($GRAPH_BYTE_COMMIT_GENERATION + $(($NUM_COMMITS - 1)) * $GRAPH_COMMIT_DATA_WIDTH)) GRAPH_BYTE_COMMIT_DATE=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 12)) -GRAPH_COMMIT_DATA_WIDTH=$(($HASH_LEN + 16)) GRAPH_OCTOPUS_DATA_OFFSET=$(($GRAPH_COMMIT_DATA_OFFSET + \ $GRAPH_COMMIT_DATA_WIDTH * $NUM_COMMITS)) GRAPH_BYTE_OCTOPUS=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4)) @@ -596,11 +597,6 @@ test_expect_success 'detect incorrect generation number' ' "generation for commit" ' -test_expect_success 'detect incorrect generation number' ' - corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\01" \ - "commit-graph generation for commit" -' - test_expect_success 'detect incorrect commit date' ' corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATE "\01" \ "commit date" @@ -622,6 +618,16 @@ test_expect_success 'detect incorrect chunk count' ' $GRAPH_CHUNK_LOOKUP_OFFSET ' +test_expect_success 'detect mixed generation numbers (non-zero to zero)' ' + corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION_LAST "\0\0\0\0" \ + "both zero and non-zero generations" +' + +test_expect_success 'detect mixed generation numbers (zero to non-zero)' ' + corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\0\0\0\0" \ + "both zero and non-zero generations" +' + test_expect_success 'git fsck (checks commit-graph when config set to true)' ' git -C full fsck && corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \ diff --git a/t/t5329-pack-objects-cruft.sh b/t/t5329-pack-objects-cruft.sh index 45667d4999..fc5fedbe9b 100755 --- a/t/t5329-pack-objects-cruft.sh +++ b/t/t5329-pack-objects-cruft.sh @@ -573,23 +573,54 @@ test_expect_success 'cruft repack with no reachable objects' ' ) ' -test_expect_success 'cruft repack ignores --max-pack-size' ' +write_blob () { + test-tool genrandom "$@" >in && + git hash-object -w -t blob in +} + +find_pack () { + for idx in $(ls $packdir/pack-*.idx) + do + git show-index <$idx >out && + if grep -q "$1" out + then + echo $idx + fi || return 1 + done +} + +test_expect_success 'cruft repack with --max-pack-size' ' git init max-pack-size && ( cd max-pack-size && test_commit base && + # two cruft objects which exceed the maximum pack size - test-tool genrandom foo 1048576 | git hash-object --stdin -w && - test-tool genrandom bar 1048576 | git hash-object --stdin -w && + foo=$(write_blob foo 1048576) && + bar=$(write_blob bar 1048576) && + test-tool chmtime --get -1000 \ + "$objdir/$(test_oid_to_path $foo)" >foo.mtime && + test-tool chmtime --get -2000 \ + "$objdir/$(test_oid_to_path $bar)" >bar.mtime && git repack --cruft --max-pack-size=1M && find $packdir -name "*.mtimes" >cruft && - test_line_count = 1 cruft && - test-tool pack-mtimes "$(basename "$(cat cruft)")" >objects && - test_line_count = 2 objects + test_line_count = 2 cruft && + + foo_mtimes="$(basename $(find_pack $foo) .idx).mtimes" && + bar_mtimes="$(basename $(find_pack $bar) .idx).mtimes" && + test-tool pack-mtimes $foo_mtimes >foo.actual && + test-tool pack-mtimes $bar_mtimes >bar.actual && + + echo "$foo $(cat foo.mtime)" >foo.expect && + echo "$bar $(cat bar.mtime)" >bar.expect && + + test_cmp foo.expect foo.actual && + test_cmp bar.expect bar.actual && + test "$foo_mtimes" != "$bar_mtimes" ) ' -test_expect_success 'cruft repack ignores pack.packSizeLimit' ' +test_expect_success 'cruft repack with pack.packSizeLimit' ' ( cd max-pack-size && # repack everything back together to remove the existing cruft @@ -599,9 +630,12 @@ test_expect_success 'cruft repack ignores pack.packSizeLimit' ' # ensure the same post condition is met when --max-pack-size # would otherwise be inferred from the configuration find $packdir -name "*.mtimes" >cruft && - test_line_count = 1 cruft && - test-tool pack-mtimes "$(basename "$(cat cruft)")" >objects && - test_line_count = 2 objects + test_line_count = 2 cruft && + for pack in $(cat cruft) + do + test-tool pack-mtimes "$(basename $pack)" >objects && + test_line_count = 1 objects || return 1 + done ) ' diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh index 5f3ff051ca..ad7f8c6f00 100755 --- a/t/t5407-post-rewrite-hook.sh +++ b/t/t5407-post-rewrite-hook.sh @@ -17,6 +17,12 @@ test_expect_success 'setup' ' git checkout A^0 && test_commit E bar E && test_commit F foo F && + git checkout B && + git merge E && + git tag merge-E && + test_commit G G && + test_commit H H && + test_commit I I && git checkout main && test_hook --setup post-rewrite <<-EOF @@ -173,6 +179,48 @@ test_fail_interactive_rebase () { ) } +test_expect_success 'git rebase with failed pick' ' + clear_hook_input && + cat >todo <<-\EOF && + exec >bar + merge -C merge-E E + exec >G + pick G + exec >H 2>I + pick H + fixup I + EOF + + ( + set_replace_editor todo && + test_must_fail git rebase -i D D 2>err + ) && + grep "would be overwritten" err && + rm bar && + + test_must_fail git rebase --continue 2>err && + grep "would be overwritten" err && + rm G && + + test_must_fail git rebase --continue 2>err && + grep "would be overwritten" err && + rm H && + + test_must_fail git rebase --continue 2>err && + grep "would be overwritten" err && + rm I && + + git rebase --continue && + echo rebase >expected.args && + cat >expected.data <<-EOF && + $(git rev-parse merge-E) $(git rev-parse HEAD~2) + $(git rev-parse G) $(git rev-parse HEAD~1) + $(git rev-parse H) $(git rev-parse HEAD) + $(git rev-parse I) $(git rev-parse HEAD) + EOF + verify_hook_input +' + test_expect_success 'git rebase -i (unchanged)' ' git reset --hard D && clear_hook_input && diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 4f289063ce..19c36b57f4 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -1127,6 +1127,52 @@ do ' done +test_expect_success 'prepare source branch' ' + echo one >onebranch && + git checkout --orphan onebranch && + git rm --cached -r . && + git add onebranch && + git commit -m onebranch && + git rev-list --objects onebranch -- >actual && + # 3 objects should be created, at least ... + test 3 -le $(wc -l <actual) +' + +validate_store_type () { + git -C dest count-objects -v >actual && + case "$store_type" in + packed) + grep "^count: 0$" actual ;; + loose) + grep "^packs: 0$" actual ;; + esac || { + echo "store_type is $store_type" + cat actual + false + } +} + +test_unpack_limit () { + store_type=$1 + + case "$store_type" in + packed) fetch_limit=1 transfer_limit=10000 ;; + loose) fetch_limit=10000 transfer_limit=1 ;; + esac + + test_expect_success "fetch trumps transfer limit" ' + rm -fr dest && + git --bare init dest && + git -C dest config fetch.unpacklimit $fetch_limit && + git -C dest config transfer.unpacklimit $transfer_limit && + git -C dest fetch .. onebranch && + validate_store_type + ' +} + +test_unpack_limit packed +test_unpack_limit loose + setup_negotiation_tip () { SERVER="$1" URL="$2" diff --git a/t/t5546-receive-limits.sh b/t/t5546-receive-limits.sh index eed3c9d81a..9fc9ba552f 100755 --- a/t/t5546-receive-limits.sh +++ b/t/t5546-receive-limits.sh @@ -9,10 +9,26 @@ TEST_PASSES_SANITIZE_LEAK=true # When the limit is 1, `git receive-pack` will call `git index-pack`. # When the limit is 10000, `git receive-pack` will call `git unpack-objects`. +validate_store_type () { + git -C dest count-objects -v >actual && + case "$store_type" in + index) + grep "^count: 0$" actual ;; + unpack) + grep "^packs: 0$" actual ;; + esac || { + echo "store_type is $store_type" + cat actual + false; + } +} + test_pack_input_limit () { - case "$1" in - index) unpack_limit=1 ;; - unpack) unpack_limit=10000 ;; + store_type=$1 + + case "$store_type" in + index) unpack_limit=1 other_limit=10000 ;; + unpack) unpack_limit=10000 other_limit=1 ;; esac test_expect_success 'prepare destination repository' ' @@ -43,6 +59,19 @@ test_pack_input_limit () { git --git-dir=dest config receive.maxInputSize 0 && git push dest HEAD ' + + test_expect_success 'prepare destination repository (once more)' ' + rm -fr dest && + git --bare init dest + ' + + test_expect_success 'receive trumps transfer' ' + git --git-dir=dest config receive.unpacklimit "$unpack_limit" && + git --git-dir=dest config transfer.unpacklimit "$other_limit" && + git push dest HEAD && + validate_store_type + ' + } test_expect_success "create known-size (1024 bytes) commit" ' diff --git a/t/t5571-pre-push-hook.sh b/t/t5571-pre-push-hook.sh index a11b20e378..448134c4bf 100755 --- a/t/t5571-pre-push-hook.sh +++ b/t/t5571-pre-push-hook.sh @@ -4,6 +4,7 @@ test_description='check pre-push hooks' 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/t5583-push-branches.sh b/t/t5583-push-branches.sh index e7e1b6dab6..320f49c753 100755 --- a/t/t5583-push-branches.sh +++ b/t/t5583-push-branches.sh @@ -5,6 +5,7 @@ test_description='check the consisitency of behavior of --all and --branches' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh delete_refs() { diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index 5b434ab451..7b943fd34c 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -1017,16 +1017,16 @@ test_expect_success 'Verify sorts with raw' ' test_expect_success 'Verify sorts with raw:size' ' cat >expected <<-EOF && refs/myblobs/blob8 - refs/myblobs/first refs/myblobs/blob7 - refs/heads/main refs/myblobs/blob4 refs/myblobs/blob1 refs/myblobs/blob2 refs/myblobs/blob3 refs/myblobs/blob5 refs/myblobs/blob6 + refs/myblobs/first refs/mytrees/first + refs/heads/main EOF git for-each-ref --format="%(refname)" --sort=raw:size \ refs/heads/main refs/myblobs/ refs/mytrees/first >actual && @@ -1138,6 +1138,17 @@ test_expect_success 'for-each-ref --format compare with cat-file --batch' ' test_cmp expected actual ' +test_expect_success 'verify sorts with contents:size' ' + cat >expect <<-\EOF && + refs/heads/main + refs/heads/newtag + refs/heads/ambiguous + EOF + git for-each-ref --format="%(refname)" \ + --sort=contents:size refs/heads/ >actual && + test_cmp expect actual +' + test_expect_success 'set up multiple-sort tags' ' for when in 100000 200000 do @@ -1763,10 +1774,7 @@ test_expect_success GPGSSH 'setup for signature atom using ssh' ' ' test_expect_success GPG2 'bare signature atom' ' - git verify-commit first-signed 2>out.raw && - grep -Ev "checking the trustdb|PGP trust model" out.raw >out && - head -3 out >expect && - tail -1 out >>expect && + git verify-commit first-signed 2>expect && echo >>expect && git for-each-ref refs/tags/first-signed \ --format="%(signature)" >actual && diff --git a/t/t6406-merge-attr.sh b/t/t6406-merge-attr.sh index 9677180a5b..72f8c1722f 100755 --- a/t/t6406-merge-attr.sh +++ b/t/t6406-merge-attr.sh @@ -179,7 +179,8 @@ test_expect_success !WINDOWS 'custom merge driver that is killed with a signal' >./please-abort && echo "* merge=custom" >.gitattributes && - test_must_fail git merge main && + test_must_fail git merge main 2>err && + grep "^error: failed to execute internal merge" err && git ls-files -u >output && git diff --name-only HEAD >>output && test_must_be_empty output diff --git a/t/t6700-tree-depth.sh b/t/t6700-tree-depth.sh new file mode 100755 index 0000000000..e410c41234 --- /dev/null +++ b/t/t6700-tree-depth.sh @@ -0,0 +1,93 @@ +#!/bin/sh + +test_description='handling of deep trees in various commands' +. ./test-lib.sh + +# We'll test against two depths here: a small one that will let us check the +# behavior of the config setting easily, and a large one that should be +# forbidden by default. Testing the default depth will let us know whether our +# default is enough to prevent segfaults on systems that run the tests. +small_depth=50 +big_depth=4100 + +small_ok="-c core.maxtreedepth=$small_depth" +small_no="-c core.maxtreedepth=$((small_depth-1))" + +# usage: mkdeep <name> <depth> +# Create a tag <name> containing a file whose path has depth <depth>. +# +# We'll use fast-import here for two reasons: +# +# 1. It's faster than creating $big_depth tree objects. +# +# 2. As we tighten tree limits, it's more likely to allow large sizes +# than trying to stuff a deep path into the index. +mkdeep () { + { + echo "commit refs/tags/$1" && + echo "committer foo <foo@example.com> 1234 -0000" && + echo "data <<EOF" && + echo "the commit message" && + echo "EOF" && + + printf 'M 100644 inline ' && + i=0 && + while test $i -lt $2 + do + printf 'a/' + i=$((i+1)) + done && + echo "file" && + + echo "data <<EOF" && + echo "the file contents" && + echo "EOF" && + echo + } | git fast-import +} + +test_expect_success 'create small tree' ' + mkdeep small $small_depth +' + +test_expect_success 'create big tree' ' + mkdeep big $big_depth +' + +test_expect_success 'limit recursion of git-archive' ' + git $small_ok archive small >/dev/null && + test_must_fail git $small_no archive small >/dev/null +' + +test_expect_success 'default limit for git-archive fails gracefully' ' + test_must_fail git archive big >/dev/null +' + +test_expect_success 'limit recursion of ls-tree -r' ' + git $small_ok ls-tree -r small && + test_must_fail git $small_no ls-tree -r small +' + +test_expect_success 'default limit for ls-tree fails gracefully' ' + test_must_fail git ls-tree -r big >/dev/null +' + +test_expect_success 'limit recursion of rev-list --objects' ' + git $small_ok rev-list --objects small >/dev/null && + test_must_fail git $small_no rev-list --objects small >/dev/null +' + +test_expect_success 'default limit for rev-list fails gracefully' ' + test_must_fail git rev-list --objects big >/dev/null +' + +test_expect_success 'limit recursion of diff-tree -r' ' + git $small_ok diff-tree -r $EMPTY_TREE small && + test_must_fail git $small_no diff-tree -r $EMPTY_TREE small +' + +test_expect_success 'default limit for diff-tree fails gracefully' ' + test_must_fail git diff-tree -r $EMPTY_TREE big +' + +test_done diff --git a/t/t7516-commit-races.sh b/t/t7516-commit-races.sh index 2d38a16480..bb95f09810 100755 --- a/t/t7516-commit-races.sh +++ b/t/t7516-commit-races.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='git commit races' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'race to create orphan commit' ' diff --git a/t/t7527-builtin-fsmonitor.sh b/t/t7527-builtin-fsmonitor.sh index 0c241d6c14..78503158fd 100755 --- a/t/t7527-builtin-fsmonitor.sh +++ b/t/t7527-builtin-fsmonitor.sh @@ -809,6 +809,11 @@ my_match_and_clean () { status --porcelain=v2 >actual.without && test_cmp actual.with actual.without && + git -C super --no-optional-locks diff-index --name-status HEAD >actual.with && + git -C super --no-optional-locks -c core.fsmonitor=false \ + diff-index --name-status HEAD >actual.without && + test_cmp actual.with actual.without && + git -C super/dir_1/dir_2/sub reset --hard && git -C super/dir_1/dir_2/sub clean -d -f } diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh index 487e326b3f..e56f5980dc 100755 --- a/t/t7900-maintenance.sh +++ b/t/t7900-maintenance.sh @@ -744,7 +744,15 @@ test_expect_success 'start and stop Linux/systemd maintenance' ' # start registers the repo git config --get --global --fixed-value maintenance.repo "$(pwd)" && - test_systemd_analyze_verify "systemd/user/git-maintenance@.service" && + for schedule in hourly daily weekly + do + test_path_is_file "systemd/user/git-maintenance@$schedule.timer" || return 1 + done && + test_path_is_file "systemd/user/git-maintenance@.service" && + + test_systemd_analyze_verify "systemd/user/git-maintenance@hourly.service" && + test_systemd_analyze_verify "systemd/user/git-maintenance@daily.service" && + test_systemd_analyze_verify "systemd/user/git-maintenance@weekly.service" && printf -- "--user enable --now git-maintenance@%s.timer\n" hourly daily weekly >expect && test_cmp expect args && @@ -755,7 +763,10 @@ test_expect_success 'start and stop Linux/systemd maintenance' ' # stop does not unregister the repo git config --get --global --fixed-value maintenance.repo "$(pwd)" && - test_path_is_missing "systemd/user/git-maintenance@.timer" && + for schedule in hourly daily weekly + do + test_path_is_missing "systemd/user/git-maintenance@$schedule.timer" || return 1 + done && test_path_is_missing "systemd/user/git-maintenance@.service" && printf -- "--user disable --now git-maintenance@%s.timer\n" hourly daily weekly >expect && @@ -838,4 +849,17 @@ test_expect_success 'register and unregister bare repo' ' ) ' +test_expect_success 'failed schedule prevents config change' ' + git init --bare failcase && + + for scheduler in crontab launchctl schtasks systemctl + do + GIT_TEST_MAINT_SCHEDULER="$scheduler:false" && + export GIT_TEST_MAINT_SCHEDULER && + test_must_fail \ + git -C failcase maintenance start && + test_must_fail git -C failcase config maintenance.auto || return 1 + done +' + test_done diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index a60b05ad3f..263db3ad17 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -61,8 +61,8 @@ test_no_confirm () { --smtp-server="$(pwd)/fake.sendmail" \ $@ \ $patches >stdout && - ! grep "Send this email" stdout && - >no_confirm_okay + ! grep "Send this email" stdout && + >no_confirm_okay } # Exit immediately to prevent hang if a no-confirm test fails diff --git a/t/t9211-scalar-clone.sh b/t/t9211-scalar-clone.sh index 872ad1c9c2..7869f45ee6 100755 --- a/t/t9211-scalar-clone.sh +++ b/t/t9211-scalar-clone.sh @@ -180,4 +180,16 @@ test_expect_success 'scalar clone warns when background maintenance fails' ' grep "could not turn on maintenance" err ' +test_expect_success '`scalar clone --no-src`' ' + scalar clone --src "file://$(pwd)/to-clone" with-src && + scalar clone --no-src "file://$(pwd)/to-clone" without-src && + + test_path_is_dir with-src/src && + test_path_is_missing without-src/src && + + (cd with-src/src && ls ?*) >with && + (cd without-src && ls ?*) >without && + test_cmp with without +' + test_done diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 8835e16e81..47e20fb8b1 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -1622,14 +1622,22 @@ test_expect_success 'git checkout - with -d, complete only references' ' ' test_expect_success 'git switch - with --track, complete only remote branches' ' - test_completion "git switch --track " <<-\EOF + test_completion "git switch --track " <<-\EOF && + other/branch-in-other Z + other/main-in-other Z + EOF + test_completion "git switch -t " <<-\EOF other/branch-in-other Z other/main-in-other Z EOF ' test_expect_success 'git checkout - with --track, complete only remote branches' ' - test_completion "git checkout --track " <<-\EOF + test_completion "git checkout --track " <<-\EOF && + other/branch-in-other Z + other/main-in-other Z + EOF + test_completion "git checkout -t " <<-\EOF other/branch-in-other Z other/main-in-other Z EOF diff --git a/t/test-lib.sh b/t/test-lib.sh index 293caf0f20..5ea5d1d62a 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -334,6 +334,7 @@ nr_san_dir_leaks_ () { find "$TEST_RESULTS_SAN_DIR" \ -type f \ -name "$TEST_RESULTS_SAN_FILE_PFX.*" 2>/dev/null | + xargs grep -lv "Unable to get registers from thread" | wc -l } diff --git a/trace2/tr2_sysenv.c b/trace2/tr2_sysenv.c index f26ec95ab4..d3ecac2772 100644 --- a/trace2/tr2_sysenv.c +++ b/trace2/tr2_sysenv.c @@ -58,7 +58,8 @@ static struct tr2_sysenv_entry tr2_sysenv_settings[] = { /* clang-format on */ static int tr2_sysenv_cb(const char *key, const char *value, - const struct config_context *ctx UNUSED, void *d) + const struct config_context *ctx UNUSED, + void *d UNUSED) { int k; diff --git a/trace2/tr2_tgt_event.c b/trace2/tr2_tgt_event.c index 53091781ec..59910a1a4f 100644 --- a/trace2/tr2_tgt_event.c +++ b/trace2/tr2_tgt_event.c @@ -335,7 +335,7 @@ static void fn_alias_fl(const char *file, int line, const char *alias, } static void fn_child_start_fl(const char *file, int line, - uint64_t us_elapsed_absolute, + uint64_t us_elapsed_absolute UNUSED, const struct child_process *cmd) { const char *event_name = "child_start"; @@ -367,7 +367,8 @@ static void fn_child_start_fl(const char *file, int line, } static void fn_child_exit_fl(const char *file, int line, - uint64_t us_elapsed_absolute, int cid, int pid, + uint64_t us_elapsed_absolute UNUSED, + int cid, int pid, int code, uint64_t us_elapsed_child) { const char *event_name = "child_exit"; @@ -388,7 +389,8 @@ static void fn_child_exit_fl(const char *file, int line, } static void fn_child_ready_fl(const char *file, int line, - uint64_t us_elapsed_absolute, int cid, int pid, + uint64_t us_elapsed_absolute UNUSED, + int cid, int pid, const char *ready, uint64_t us_elapsed_child) { const char *event_name = "child_ready"; @@ -409,7 +411,7 @@ static void fn_child_ready_fl(const char *file, int line, } static void fn_thread_start_fl(const char *file, int line, - uint64_t us_elapsed_absolute) + uint64_t us_elapsed_absolute UNUSED) { const char *event_name = "thread_start"; struct json_writer jw = JSON_WRITER_INIT; @@ -423,7 +425,7 @@ static void fn_thread_start_fl(const char *file, int line, } static void fn_thread_exit_fl(const char *file, int line, - uint64_t us_elapsed_absolute, + uint64_t us_elapsed_absolute UNUSED, uint64_t us_elapsed_thread) { const char *event_name = "thread_exit"; @@ -439,7 +441,8 @@ static void fn_thread_exit_fl(const char *file, int line, jw_release(&jw); } -static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute, +static void fn_exec_fl(const char *file, int line, + uint64_t us_elapsed_absolute UNUSED, int exec_id, const char *exe, const char **argv) { const char *event_name = "exec"; @@ -460,8 +463,8 @@ static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute, } static void fn_exec_result_fl(const char *file, int line, - uint64_t us_elapsed_absolute, int exec_id, - int code) + uint64_t us_elapsed_absolute UNUSED, + int exec_id, int code) { const char *event_name = "exec_result"; struct json_writer jw = JSON_WRITER_INIT; @@ -511,7 +514,7 @@ static void fn_repo_fl(const char *file, int line, } static void fn_region_enter_printf_va_fl(const char *file, int line, - uint64_t us_elapsed_absolute, + uint64_t us_elapsed_absolute UNUSED, const char *category, const char *label, const struct repository *repo, @@ -538,7 +541,7 @@ static void fn_region_enter_printf_va_fl(const char *file, int line, } static void fn_region_leave_printf_va_fl( - const char *file, int line, uint64_t us_elapsed_absolute, + const char *file, int line, uint64_t us_elapsed_absolute UNUSED, uint64_t us_elapsed_region, const char *category, const char *label, const struct repository *repo, const char *fmt, va_list ap) { diff --git a/trace2/tr2_tgt_normal.c b/trace2/tr2_tgt_normal.c index d25ea13164..38d5ebddf6 100644 --- a/trace2/tr2_tgt_normal.c +++ b/trace2/tr2_tgt_normal.c @@ -86,7 +86,7 @@ static void fn_version_fl(const char *file, int line) } static void fn_start_fl(const char *file, int line, - uint64_t us_elapsed_absolute, const char **argv) + uint64_t us_elapsed_absolute UNUSED, const char **argv) { struct strbuf buf_payload = STRBUF_INIT; @@ -215,7 +215,7 @@ static void fn_alias_fl(const char *file, int line, const char *alias, } static void fn_child_start_fl(const char *file, int line, - uint64_t us_elapsed_absolute, + uint64_t us_elapsed_absolute UNUSED, const struct child_process *cmd) { struct strbuf buf_payload = STRBUF_INIT; @@ -243,7 +243,8 @@ static void fn_child_start_fl(const char *file, int line, } static void fn_child_exit_fl(const char *file, int line, - uint64_t us_elapsed_absolute, int cid, int pid, + uint64_t us_elapsed_absolute UNUSED, + int cid, int pid, int code, uint64_t us_elapsed_child) { struct strbuf buf_payload = STRBUF_INIT; @@ -256,7 +257,8 @@ static void fn_child_exit_fl(const char *file, int line, } static void fn_child_ready_fl(const char *file, int line, - uint64_t us_elapsed_absolute, int cid, int pid, + uint64_t us_elapsed_absolute UNUSED, + int cid, int pid, const char *ready, uint64_t us_elapsed_child) { struct strbuf buf_payload = STRBUF_INIT; @@ -268,7 +270,8 @@ static void fn_child_ready_fl(const char *file, int line, strbuf_release(&buf_payload); } -static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute, +static void fn_exec_fl(const char *file, int line, + uint64_t us_elapsed_absolute UNUSED, int exec_id, const char *exe, const char **argv) { struct strbuf buf_payload = STRBUF_INIT; @@ -284,8 +287,8 @@ static void fn_exec_fl(const char *file, int line, uint64_t us_elapsed_absolute, } static void fn_exec_result_fl(const char *file, int line, - uint64_t us_elapsed_absolute, int exec_id, - int code) + uint64_t us_elapsed_absolute UNUSED, + int exec_id, int code) { struct strbuf buf_payload = STRBUF_INIT; @@ -321,7 +324,8 @@ static void fn_repo_fl(const char *file, int line, } static void fn_printf_va_fl(const char *file, int line, - uint64_t us_elapsed_absolute, const char *fmt, + uint64_t us_elapsed_absolute UNUSED, + const char *fmt, va_list ap) { struct strbuf buf_payload = STRBUF_INIT; @@ -711,30 +711,35 @@ static void add_arg_item(struct list_head *arg_head, char *tok, char *val, list_add_tail(&new_item->list, arg_head); } -static void process_command_line_args(struct list_head *arg_head, - struct list_head *new_trailer_head) +static void parse_trailers_from_config(struct list_head *config_head) { struct arg_item *item; - struct strbuf tok = STRBUF_INIT; - struct strbuf val = STRBUF_INIT; - const struct conf_info *conf; struct list_head *pos; - /* - * In command-line arguments, '=' is accepted (in addition to the - * separators that are defined). - */ - char *cl_separators = xstrfmt("=%s", separators); - /* Add an arg item for each configured trailer with a command */ list_for_each(pos, &conf_head) { item = list_entry(pos, struct arg_item, list); if (item->conf.command) - add_arg_item(arg_head, + add_arg_item(config_head, xstrdup(token_from_item(item, NULL)), xstrdup(""), &item->conf, NULL); } +} + +static void parse_trailers_from_command_line_args(struct list_head *arg_head, + struct list_head *new_trailer_head) +{ + struct strbuf tok = STRBUF_INIT; + struct strbuf val = STRBUF_INIT; + const struct conf_info *conf; + struct list_head *pos; + + /* + * In command-line arguments, '=' is accepted (in addition to the + * separators that are defined). + */ + char *cl_separators = xstrfmt("=%s", separators); /* Add an arg item for each trailer on the command line */ list_for_each(pos, new_trailer_head) { @@ -961,28 +966,24 @@ static void unfold_value(struct strbuf *val) strbuf_release(&out); } -static size_t process_input_file(FILE *outfile, - const char *str, - struct list_head *head, - const struct process_trailer_options *opts) +/* + * Parse trailers in "str", populating the trailer info and "head" + * linked list structure. + */ +static void parse_trailers(struct trailer_info *info, + const char *str, + struct list_head *head, + const struct process_trailer_options *opts) { - struct trailer_info info; struct strbuf tok = STRBUF_INIT; struct strbuf val = STRBUF_INIT; size_t i; - trailer_info_get(&info, str, opts); - - /* Print lines before the trailers as is */ - if (!opts->only_trailers) - fwrite(str, 1, info.trailer_start - str, outfile); + trailer_info_get(info, str, opts); - if (!opts->only_trailers && !info.blank_line_before_trailer) - fprintf(outfile, "\n"); - - for (i = 0; i < info.trailer_nr; i++) { + for (i = 0; i < info->trailer_nr; i++) { int separator_pos; - char *trailer = info.trailers[i]; + char *trailer = info->trailers[i]; if (trailer[0] == comment_line_char) continue; separator_pos = find_separator(trailer, separators); @@ -1002,10 +1003,6 @@ static size_t process_input_file(FILE *outfile, strbuf_detach(&val, NULL)); } } - - trailer_info_release(&info); - - return info.trailer_end - str; } static void free_all(struct list_head *head) @@ -1054,6 +1051,7 @@ void process_trailers(const char *file, { LIST_HEAD(head); struct strbuf sb = STRBUF_INIT; + struct trailer_info info; size_t trailer_end; FILE *outfile = stdout; @@ -1064,18 +1062,30 @@ void process_trailers(const char *file, if (opts->in_place) outfile = create_in_place_tempfile(file); + parse_trailers(&info, sb.buf, &head, opts); + trailer_end = info.trailer_end - sb.buf; + /* Print the lines before the trailers */ - trailer_end = process_input_file(outfile, sb.buf, &head, opts); + if (!opts->only_trailers) + fwrite(sb.buf, 1, info.trailer_start - sb.buf, outfile); + + if (!opts->only_trailers && !info.blank_line_before_trailer) + fprintf(outfile, "\n"); + if (!opts->only_input) { + LIST_HEAD(config_head); LIST_HEAD(arg_head); - process_command_line_args(&arg_head, new_trailer_head); + parse_trailers_from_config(&config_head); + parse_trailers_from_command_line_args(&arg_head, new_trailer_head); + list_splice(&config_head, &arg_head); process_trailers_lists(&head, &arg_head); } print_all(outfile, &head, opts); free_all(&head); + trailer_info_release(&info); /* Print the lines after the trailers as is */ if (!opts->only_trailers) @@ -1220,14 +1230,14 @@ void trailer_iterator_init(struct trailer_iterator *iter, const char *msg) strbuf_init(&iter->key, 0); strbuf_init(&iter->val, 0); opts.no_divider = 1; - trailer_info_get(&iter->info, msg, &opts); - iter->cur = 0; + trailer_info_get(&iter->internal.info, msg, &opts); + iter->internal.cur = 0; } int trailer_iterator_advance(struct trailer_iterator *iter) { - while (iter->cur < iter->info.trailer_nr) { - char *trailer = iter->info.trailers[iter->cur++]; + while (iter->internal.cur < iter->internal.info.trailer_nr) { + char *trailer = iter->internal.info.trailers[iter->internal.cur++]; int separator_pos = find_separator(trailer, separators); if (separator_pos < 1) @@ -1245,7 +1255,7 @@ int trailer_iterator_advance(struct trailer_iterator *iter) void trailer_iterator_release(struct trailer_iterator *iter) { - trailer_info_release(&iter->info); + trailer_info_release(&iter->internal.info); strbuf_release(&iter->val); strbuf_release(&iter->key); } @@ -119,8 +119,10 @@ struct trailer_iterator { struct strbuf val; /* private */ - struct trailer_info info; - size_t cur; + struct { + struct trailer_info info; + size_t cur; + } internal; }; /* diff --git a/tree-diff.c b/tree-diff.c index 8fc159b86e..46107772d1 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -7,6 +7,7 @@ #include "hash.h" #include "tree.h" #include "tree-walk.h" +#include "environment.h" /* * Some mode bits are also used internally for computations. @@ -45,7 +46,8 @@ static struct combine_diff_path *ll_diff_tree_paths( struct combine_diff_path *p, const struct object_id *oid, const struct object_id **parents_oid, int nparent, - struct strbuf *base, struct diff_options *opt); + struct strbuf *base, struct diff_options *opt, + int depth); static void ll_diff_tree_oid(const struct object_id *old_oid, const struct object_id *new_oid, struct strbuf *base, struct diff_options *opt); @@ -196,7 +198,7 @@ static struct combine_diff_path *path_appendnew(struct combine_diff_path *last, static struct combine_diff_path *emit_path(struct combine_diff_path *p, struct strbuf *base, struct diff_options *opt, int nparent, struct tree_desc *t, struct tree_desc *tp, - int imin) + int imin, int depth) { unsigned short mode; const char *path; @@ -302,7 +304,8 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p, strbuf_add(base, path, pathlen); strbuf_addch(base, '/'); - p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt); + p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt, + depth + 1); FAST_ARRAY_FREE(parents_oid, nparent); } @@ -423,12 +426,16 @@ static inline void update_tp_entries(struct tree_desc *tp, int nparent) static struct combine_diff_path *ll_diff_tree_paths( struct combine_diff_path *p, const struct object_id *oid, const struct object_id **parents_oid, int nparent, - struct strbuf *base, struct diff_options *opt) + struct strbuf *base, struct diff_options *opt, + int depth) { struct tree_desc t, *tp; void *ttree, **tptree; int i; + if (depth > max_allowed_tree_depth) + die("exceeded maximum allowed tree depth"); + FAST_ARRAY_ALLOC(tp, nparent); FAST_ARRAY_ALLOC(tptree, nparent); @@ -522,7 +529,7 @@ static struct combine_diff_path *ll_diff_tree_paths( /* D += {δ(t,pi) if pi=p[imin]; "+a" if pi > p[imin]} */ p = emit_path(p, base, opt, nparent, - &t, tp, imin); + &t, tp, imin, depth); skip_emit_t_tp: /* t↓, ∀ pi=p[imin] pi↓ */ @@ -534,7 +541,7 @@ static struct combine_diff_path *ll_diff_tree_paths( else if (cmp < 0) { /* D += "+t" */ p = emit_path(p, base, opt, nparent, - &t, /*tp=*/NULL, -1); + &t, /*tp=*/NULL, -1, depth); /* t↓ */ update_tree_entry(&t); @@ -550,7 +557,7 @@ static struct combine_diff_path *ll_diff_tree_paths( } p = emit_path(p, base, opt, nparent, - /*t=*/NULL, tp, imin); + /*t=*/NULL, tp, imin, depth); skip_emit_tp: /* ∀ pi=p[imin] pi↓ */ @@ -572,7 +579,7 @@ struct combine_diff_path *diff_tree_paths( const struct object_id **parents_oid, int nparent, struct strbuf *base, struct diff_options *opt) { - p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt); + p = ll_diff_tree_paths(p, oid, parents_oid, nparent, base, opt, 0); /* * free pre-allocated last element, if any diff --git a/tree-walk.c b/tree-walk.c index 29ead71be1..b517792ba2 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -9,6 +9,7 @@ #include "tree.h" #include "pathspec.h" #include "json-writer.h" +#include "environment.h" static const char *get_mode(const char *str, unsigned int *modep) { @@ -441,22 +442,25 @@ int traverse_trees(struct index_state *istate, int n, struct tree_desc *t, struct traverse_info *info) { - int error = 0; - struct name_entry entry[MAX_TRAVERSE_TREES]; + int ret = 0; + struct name_entry *entry; int i; - struct tree_desc_x tx[ARRAY_SIZE(entry)]; + struct tree_desc_x *tx; struct strbuf base = STRBUF_INIT; int interesting = 1; char *traverse_path; + if (traverse_trees_cur_depth > max_allowed_tree_depth) + return error("exceeded maximum allowed tree depth"); + traverse_trees_count++; traverse_trees_cur_depth++; if (traverse_trees_cur_depth > traverse_trees_max_depth) traverse_trees_max_depth = traverse_trees_cur_depth; - if (n >= ARRAY_SIZE(entry)) - BUG("traverse_trees() called with too many trees (%d)", n); + ALLOC_ARRAY(entry, n); + ALLOC_ARRAY(tx, n); for (i = 0; i < n; i++) { tx[i].d = t[i]; @@ -539,7 +543,7 @@ int traverse_trees(struct index_state *istate, if (interesting) { trees_used = info->fn(n, mask, dirmask, entry, info); if (trees_used < 0) { - error = trees_used; + ret = trees_used; if (!info->show_all_errors) break; } @@ -551,12 +555,14 @@ int traverse_trees(struct index_state *istate, } for (i = 0; i < n; i++) free_extended_entry(tx + i); + free(tx); + free(entry); free(traverse_path); info->traverse_path = NULL; strbuf_release(&base); traverse_trees_cur_depth--; - return error; + return ret; } struct dir_state { diff --git a/tree-walk.h b/tree-walk.h index 74cdceb3fe..a6bfa3da3a 100644 --- a/tree-walk.h +++ b/tree-walk.h @@ -6,8 +6,6 @@ struct index_state; struct repository; -#define MAX_TRAVERSE_TREES 8 - /** * The tree walking API is used to traverse and inspect trees. */ @@ -10,11 +10,13 @@ #include "alloc.h" #include "tree-walk.h" #include "repository.h" +#include "environment.h" const char *tree_type = "tree"; int read_tree_at(struct repository *r, struct tree *tree, struct strbuf *base, + int depth, const struct pathspec *pathspec, read_tree_fn_t fn, void *context) { @@ -24,6 +26,9 @@ int read_tree_at(struct repository *r, int len, oldlen = base->len; enum interesting retval = entry_not_interesting; + if (depth > max_allowed_tree_depth) + return error("exceeded maximum allowed tree depth"); + if (parse_tree(tree)) return -1; @@ -74,7 +79,7 @@ int read_tree_at(struct repository *r, strbuf_add(base, entry.path, len); strbuf_addch(base, '/'); retval = read_tree_at(r, lookup_tree(r, &oid), - base, pathspec, + base, depth + 1, pathspec, fn, context); strbuf_setlen(base, oldlen); if (retval) @@ -89,7 +94,7 @@ int read_tree(struct repository *r, read_tree_fn_t fn, void *context) { struct strbuf sb = STRBUF_INIT; - int ret = read_tree_at(r, tree, &sb, pathspec, fn, context); + int ret = read_tree_at(r, tree, &sb, 0, pathspec, fn, context); strbuf_release(&sb); return ret; } @@ -44,6 +44,7 @@ typedef int (*read_tree_fn_t)(const struct object_id *, struct strbuf *, const c int read_tree_at(struct repository *r, struct tree *tree, struct strbuf *base, + int depth, const struct pathspec *pathspec, read_tree_fn_t fn, void *context); diff --git a/unpack-trees.c b/unpack-trees.c index 87517364dc..a203f9a3d7 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -864,8 +864,8 @@ static int traverse_trees_recursive(int n, unsigned long dirmask, struct unpack_trees_options *o = info->data; int i, ret, bottom; int nr_buf = 0; - struct tree_desc t[MAX_UNPACK_TREES]; - void *buf[MAX_UNPACK_TREES]; + struct tree_desc *t; + void **buf; struct traverse_info newinfo; struct name_entry *p; int nr_entries; @@ -902,6 +902,9 @@ static int traverse_trees_recursive(int n, unsigned long dirmask, newinfo.pathlen = st_add3(newinfo.pathlen, tree_entry_len(p), 1); newinfo.df_conflicts |= df_conflicts; + ALLOC_ARRAY(t, n); + ALLOC_ARRAY(buf, n); + /* * Fetch the tree from the ODB for each peer directory in the * n commits. @@ -937,6 +940,8 @@ static int traverse_trees_recursive(int n, unsigned long dirmask, for (i = 0; i < nr_buf; i++) free(buf[i]); + free(buf); + free(t); return ret; } diff --git a/unpack-trees.h b/unpack-trees.h index 9b827c307f..5867e26e17 100644 --- a/unpack-trees.h +++ b/unpack-trees.h @@ -7,7 +7,7 @@ #include "string-list.h" #include "tree-walk.h" -#define MAX_UNPACK_TREES MAX_TRAVERSE_TREES +#define MAX_UNPACK_TREES 8 struct cache_entry; struct unpack_trees_options; diff --git a/upload-pack.c b/upload-pack.c index 94751477ab..83f3d2651a 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -801,11 +801,12 @@ error: for (i = 0; i < data->want_obj.nr; i++) { struct object *o = data->want_obj.objects[i].item; if (!is_our_ref(o, data->allow_uor)) { + error("git upload-pack: not our ref %s", + oid_to_hex(&o->oid)); packet_writer_error(&data->writer, "upload-pack: not our ref %s", oid_to_hex(&o->oid)); - die("git upload-pack: not our ref %s", - oid_to_hex(&o->oid)); + exit(128); } } } diff --git a/worktree.c b/worktree.c index b8cf29e6a1..a56a6c2a3d 100644 --- a/worktree.c +++ b/worktree.c @@ -581,8 +581,10 @@ static void repair_gitfile(struct worktree *wt, strbuf_release(&dotgit); } -static void repair_noop(int iserr, const char *path, const char *msg, - void *cb_data) +static void repair_noop(int iserr UNUSED, + const char *path UNUSED, + const char *msg UNUSED, + void *cb_data UNUSED) { /* nothing */ } @@ -819,3 +819,13 @@ int csprng_bytes(void *buf, size_t len) return 0; #endif } + +uint32_t git_rand(void) +{ + uint32_t result; + + if (csprng_bytes(&result, sizeof(result)) < 0) + die(_("unable to get random bytes")); + + return result; +} @@ -139,4 +139,10 @@ void sleep_millisec(int millisec); */ int csprng_bytes(void *buf, size_t len); +/* + * Returns a random uint32_t, uniformly distributed across all possible + * values. + */ +uint32_t git_rand(void); + #endif /* WRAPPER_H */ diff --git a/wt-status.c b/wt-status.c index 5b1378965c..9f45bf6949 100644 --- a/wt-status.c +++ b/wt-status.c @@ -675,7 +675,7 @@ static void wt_status_collect_changes_index(struct wt_status *s) rev.diffopt.flags.recursive = 1; copy_pathspec(&rev.prune_data, &s->pathspec); - run_diff_index(&rev, 1); + run_diff_index(&rev, DIFF_INDEX_CACHED); release_revisions(&rev); } @@ -739,7 +739,7 @@ static void wt_status_collect_changes_initial(struct wt_status *s) ps.max_depth = -1; strbuf_add(&base, ce->name, ce->ce_namelen); - read_tree_at(istate->repo, tree, &base, &ps, + read_tree_at(istate->repo, tree, &base, 0, &ps, add_file_to_list, s); continue; } @@ -1156,7 +1156,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s) rev.diffopt.a_prefix = "c/"; rev.diffopt.b_prefix = "i/"; } /* else use prefix as per user config */ - run_diff_index(&rev, 1); + run_diff_index(&rev, DIFF_INDEX_CACHED); if (s->verbose > 1 && wt_status_check_worktree_changes(s, &dirty_submodules)) { status_printf_ln(s, c, @@ -2580,8 +2580,8 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules) } rev_info.diffopt.flags.quick = 1; diff_setup_done(&rev_info.diffopt); - result = run_diff_files(&rev_info, 0); - result = diff_result_code(&rev_info.diffopt, result); + run_diff_files(&rev_info, 0); + result = diff_result_code(&rev_info.diffopt); release_revisions(&rev_info); return result; } @@ -2614,8 +2614,8 @@ int has_uncommitted_changes(struct repository *r, } diff_setup_done(&rev_info.diffopt); - result = run_diff_index(&rev_info, 1); - result = diff_result_code(&rev_info.diffopt, result); + run_diff_index(&rev_info, DIFF_INDEX_CACHED); + result = diff_result_code(&rev_info.diffopt); release_revisions(&rev_info); return result; } @@ -2655,8 +2655,12 @@ int require_clean_work_tree(struct repository *r, } if (err) { - if (hint) + if (hint) { + if (!*hint) + BUG("empty hint passed to require_clean_work_tree();" + " use NULL instead"); error("%s", hint); + } if (!gently) exit(128); } |
