diff options
74 files changed, 2108 insertions, 668 deletions
diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml new file mode 100644 index 0000000000..e5532d381b --- /dev/null +++ b/.github/workflows/coverity.yml @@ -0,0 +1,163 @@ +name: Coverity + +# This GitHub workflow automates submitting builds to Coverity Scan. To enable it, +# set the repository variable `ENABLE_COVERITY_SCAN_FOR_BRANCHES` (for details, see +# https://docs.github.com/en/actions/learn-github-actions/variables) to a JSON +# string array containing the names of the branches for which the workflow should be +# run, e.g. `["main", "next"]`. +# +# In addition, two repository secrets must be set (for details how to add secrets, see +# https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions): +# `COVERITY_SCAN_EMAIL` and `COVERITY_SCAN_TOKEN`. The former specifies the +# email to which the Coverity reports should be sent and the latter can be +# obtained from the Project Settings tab of the Coverity project). +# +# The workflow runs on `ubuntu-latest` by default. This can be overridden by setting +# the repository variable `ENABLE_COVERITY_SCAN_ON_OS` to a JSON string array specifying +# the operating systems, e.g. `["ubuntu-latest", "windows-latest"]`. +# +# By default, the builds are submitted to the Coverity project `git`. To override this, +# set the repository variable `COVERITY_PROJECT`. + +on: + push: + +defaults: + run: + shell: bash + +jobs: + coverity: + if: contains(fromJSON(vars.ENABLE_COVERITY_SCAN_FOR_BRANCHES || '[""]'), github.ref_name) + strategy: + matrix: + os: ${{ fromJSON(vars.ENABLE_COVERITY_SCAN_ON_OS || '["ubuntu-latest"]') }} + runs-on: ${{ matrix.os }} + env: + COVERITY_PROJECT: ${{ vars.COVERITY_PROJECT || 'git' }} + COVERITY_LANGUAGE: cxx + COVERITY_PLATFORM: overridden-below + steps: + - uses: actions/checkout@v3 + - name: install minimal Git for Windows SDK + if: contains(matrix.os, 'windows') + uses: git-for-windows/setup-git-for-windows-sdk@v1 + - run: ci/install-dependencies.sh + if: contains(matrix.os, 'ubuntu') || contains(matrix.os, 'macos') + env: + runs_on_pool: ${{ matrix.os }} + + # The Coverity site says the tool is usually updated twice yearly, so the + # MD5 of download can be used to determine whether there's been an update. + - name: get the Coverity Build Tool hash + id: lookup + run: | + case "${{ matrix.os }}" in + *windows*) + COVERITY_PLATFORM=win64 + COVERITY_TOOL_FILENAME=cov-analysis.zip + MAKEFLAGS=-j$(nproc) + ;; + *macos*) + COVERITY_PLATFORM=macOSX + COVERITY_TOOL_FILENAME=cov-analysis.dmg + MAKEFLAGS=-j$(sysctl -n hw.physicalcpu) + ;; + *ubuntu*) + COVERITY_PLATFORM=linux64 + COVERITY_TOOL_FILENAME=cov-analysis.tgz + MAKEFLAGS=-j$(nproc) + ;; + *) + echo '::error::unhandled OS ${{ matrix.os }}' >&2 + exit 1 + ;; + esac + echo "COVERITY_PLATFORM=$COVERITY_PLATFORM" >>$GITHUB_ENV + echo "COVERITY_TOOL_FILENAME=$COVERITY_TOOL_FILENAME" >>$GITHUB_ENV + echo "MAKEFLAGS=$MAKEFLAGS" >>$GITHUB_ENV + MD5=$(curl https://scan.coverity.com/download/$COVERITY_LANGUAGE/$COVERITY_PLATFORM \ + --fail \ + --form token='${{ secrets.COVERITY_SCAN_TOKEN }}' \ + --form project="$COVERITY_PROJECT" \ + --form md5=1) + case $? in + 0) ;; # okay + 22) # 40x, i.e. access denied + echo "::error::incorrect token or project?" >&2 + exit 1 + ;; + *) # other error + echo "::error::Failed to retrieve MD5" >&2 + exit 1 + ;; + esac + echo "hash=$MD5" >>$GITHUB_OUTPUT + + # Try to cache the tool to avoid downloading 1GB+ on every run. + # A cache miss will add ~30s to create, but a cache hit will save minutes. + - name: restore the Coverity Build Tool + id: cache + uses: actions/cache/restore@v3 + with: + path: ${{ runner.temp }}/cov-analysis + key: cov-build-${{ env.COVERITY_LANGUAGE }}-${{ env.COVERITY_PLATFORM }}-${{ steps.lookup.outputs.hash }} + - name: download the Coverity Build Tool (${{ env.COVERITY_LANGUAGE }} / ${{ env.COVERITY_PLATFORM}}) + if: steps.cache.outputs.cache-hit != 'true' + run: | + curl https://scan.coverity.com/download/$COVERITY_LANGUAGE/$COVERITY_PLATFORM \ + --fail --no-progress-meter \ + --output $RUNNER_TEMP/$COVERITY_TOOL_FILENAME \ + --form token='${{ secrets.COVERITY_SCAN_TOKEN }}' \ + --form project="$COVERITY_PROJECT" + - name: extract the Coverity Build Tool + if: steps.cache.outputs.cache-hit != 'true' + run: | + case "$COVERITY_TOOL_FILENAME" in + *.tgz) + mkdir $RUNNER_TEMP/cov-analysis && + tar -xzf $RUNNER_TEMP/$COVERITY_TOOL_FILENAME --strip 1 -C $RUNNER_TEMP/cov-analysis + ;; + *.dmg) + cd $RUNNER_TEMP && + attach="$(hdiutil attach $COVERITY_TOOL_FILENAME)" && + volume="$(echo "$attach" | cut -f 3 | grep /Volumes/)" && + mkdir cov-analysis && + cd cov-analysis && + sh "$volume"/cov-analysis-macosx-*.sh && + ls -l && + hdiutil detach "$volume" + ;; + *.zip) + cd $RUNNER_TEMP && + mkdir cov-analysis-tmp && + unzip -d cov-analysis-tmp $COVERITY_TOOL_FILENAME && + mv cov-analysis-tmp/* cov-analysis + ;; + *) + echo "::error::unhandled archive type: $COVERITY_TOOL_FILENAME" >&2 + exit 1 + ;; + esac + - name: cache the Coverity Build Tool + if: steps.cache.outputs.cache-hit != 'true' + uses: actions/cache/save@v3 + with: + path: ${{ runner.temp }}/cov-analysis + key: cov-build-${{ env.COVERITY_LANGUAGE }}-${{ env.COVERITY_PLATFORM }}-${{ steps.lookup.outputs.hash }} + - name: build with cov-build + run: | + export PATH="$RUNNER_TEMP/cov-analysis/bin:$PATH" && + cov-configure --gcc && + cov-build --dir cov-int make + - name: package the build + run: tar -czvf cov-int.tgz cov-int + - name: submit the build to Coverity Scan + run: | + curl \ + --fail \ + --form token='${{ secrets.COVERITY_SCAN_TOKEN }}' \ + --form email='${{ secrets.COVERITY_SCAN_EMAIL }}' \ + --form file=@cov-int.tgz \ + --form version='${{ github.sha }}' \ + "https://scan.coverity.com/builds?project=$COVERITY_PROJECT" @@ -59,9 +59,9 @@ David Reiss <dreiss@facebook.com> <dreiss@dreiss-vmware.(none)> David S. Miller <davem@davemloft.net> David Turner <novalis@novalis.org> <dturner@twopensource.com> David Turner <novalis@novalis.org> <dturner@twosigma.com> -Derrick Stolee <derrickstolee@github.com> <stolee@gmail.com> -Derrick Stolee <derrickstolee@github.com> Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com> -Derrick Stolee <derrickstolee@github.com> <dstolee@microsoft.com> +Derrick Stolee <stolee@gmail.com> <derrickstolee@github.com> +Derrick Stolee <stolee@gmail.com> Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com> +Derrick Stolee <stolee@gmail.com> <dstolee@microsoft.com> Deskin Miller <deskinm@umich.edu> Đoàn Trần Công Danh <congdanhqx@gmail.com> Doan Tran Cong Danh Dirk Süsserott <newsletter@dirk.my1.cc> diff --git a/Documentation/RelNotes/2.43.0.txt b/Documentation/RelNotes/2.43.0.txt index 7f46b10ed9..a6ed554886 100644 --- a/Documentation/RelNotes/2.43.0.txt +++ b/Documentation/RelNotes/2.43.0.txt @@ -10,6 +10,14 @@ Backward Compatibility Notes prefix. If you are negatively affected by this change, please use "--subject-prefix=PATCH --rfc" as a replacement. + * "git rev-list --stdin" learned to take non-revisions (like "--not") + recently from the standard input, but the way such a "--not" was + handled was quite confusing, which has been rethought. The updated + rule is that "--not" given from the command line only affects revs + given from the command line that comes but not revs read from the + standard input, and "--not" read from the standard input affects + revs given from the stanrdard input and not revs given from the + command line. UI, Workflows & Features @@ -72,6 +80,12 @@ UI, Workflows & Features completed. The parsing code for the alias as been loosened to allow ';' without an extra space before it. + * "git for-each-ref" and friends learned to apply mailmap to + authorname and other fields. + + * "git repack" machinery learns to pay attention to the "--filter=" + option. + Performance, Internal Implementation, Development Support etc. @@ -100,6 +114,13 @@ Performance, Internal Implementation, Development Support etc. * The code to keep track of existing packs in the repository while repacking has been refactored. + * The "streaming" interface used for bulk-checkin codepath has been + narrowed to take only blob objects for now, with no real loss of + functionality. + + * GitHub CI workflow has learned to trigger Coverity check. + (merge 3349520e1a js/ci-coverity later to maint). + Fixes since v2.42 ----------------- @@ -198,6 +219,10 @@ Fixes since v2.42 but now they do. (merge 5bdedac3c7 jc/unresolve-removal later to maint). + * The display width table for unicode characters has been updated for + Unicode 15.1 + (merge 872976c37e bb/unicode-width-table-15 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). @@ -218,3 +243,5 @@ Fixes since v2.42 (merge 563f339d98 ch/clean-docfix later to maint). (merge 4fbe83fcd9 hy/doc-show-is-like-log-not-diff-tree later to maint). (merge 43abaaf008 ob/am-msgfix later to maint). + (merge c2c349a15c xz/commit-title-soft-limit-doc later to maint). + (merge f4cbb32c27 rs/parse-opt-ctx-cleanup later to maint). diff --git a/Documentation/config/gc.txt b/Documentation/config/gc.txt index ca47eb2008..466466d6cc 100644 --- a/Documentation/config/gc.txt +++ b/Documentation/config/gc.txt @@ -145,6 +145,22 @@ Multiple hooks are supported, but all must exit successfully, else the operation (either generating a cruft pack or unpacking unreachable objects) will be halted. +gc.repackFilter:: + When repacking, use the specified filter to move certain + objects into a separate packfile. See the + `--filter=<filter-spec>` option of linkgit:git-repack[1]. + +gc.repackFilterTo:: + When repacking and using a filter, see `gc.repackFilter`, the + specified location will be used to create the packfile + containing the filtered out objects. **WARNING:** The + specified location should be accessible, using for example the + Git alternates mechanism, otherwise the repo could be + considered corrupt by Git as it migh not be able to access the + objects in that packfile. See the `--filter-to=<dir>` option + of linkgit:git-repack[1] and the `objects/info/alternates` + section of linkgit:gitrepository-layout[5]. + gc.rerereResolved:: Records of conflicted merge you resolved earlier are kept for this many days when 'git rerere gc' is run. diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 225c6c9f2e..a6cef5d820 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -541,7 +541,7 @@ DISCUSSION ---------- Though not required, it's a good idea to begin the commit message -with a single short (less than 50 character) line summarizing the +with a single short (no more than 50 characters) line summarizing the change, followed by a blank line and then a more thorough description. The text up to the first blank line in a commit message is treated as the commit title, and that title is used throughout Git. diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 11b2bc3121..e86d5700dd 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -303,7 +303,11 @@ Fields that have name-email-date tuple as its value (`author`, and `date` to extract the named component. For email fields (`authoremail`, `committeremail` and `taggeremail`), `:trim` can be appended to get the email without angle brackets, and `:localpart` to get the part before the `@` symbol -out of the trimmed email. +out of the trimmed email. In addition to these, the `:mailmap` option and the +corresponding `:mailmap,trim` and `:mailmap,localpart` can be used (order does +not matter) to get values of the name and email according to the .mailmap file +or according to the file set in the mailmap.file or mailmap.blob configuration +variable (see linkgit:gitmailmap[5]). The raw data in an object is `raw`. diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt index 55d8961466..418265f044 100644 --- a/Documentation/git-interpret-trailers.txt +++ b/Documentation/git-interpret-trailers.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git interpret-trailers' [--in-place] [--trim-empty] - [(--trailer <token>[(=|:)<value>])...] + [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...] [--parse] [<file>...] DESCRIPTION @@ -31,10 +31,15 @@ the last two lines starting with "Signed-off-by" are trailers. This command reads commit messages from either the <file> arguments or the standard input if no <file> is specified. -If `--parse` is specified, the output consists of the parsed trailers. -Otherwise, this command applies the arguments passed using the -`--trailer` option, if any, to each input file. The result is emitted on the -standard output. +If `--parse` is specified, the output consists of the parsed trailers +coming from the input, without influencing them with any command line +options or configuration variables. + +Otherwise, this command applies `trailer.*` configuration variables +(which could potentially add new trailers, as well as reposition them), +as well as any command line arguments that can override configuration +variables (such as `--trailer=...` which could also add new trailers), +to each input file. The result is emitted on the standard output. This command can also operate on the output of linkgit:git-format-patch[1], which is more elaborate than a plain commit message. Namely, such output @@ -48,22 +53,32 @@ are applied to each input and the way any existing trailer in the input is changed. They also make it possible to automatically add some trailers. -By default, a '<token>=<value>' or '<token>:<value>' argument given +By default, a '<key>=<value>' or '<key>:<value>' argument given using `--trailer` will be appended after the existing trailers only if -the last trailer has a different (<token>, <value>) pair (or if there -is no existing trailer). The <token> and <value> parts will be trimmed +the last trailer has a different (<key>, <value>) pair (or if there +is no existing trailer). The <key> and <value> parts will be trimmed to remove starting and trailing whitespace, and the resulting trimmed -<token> and <value> will appear in the output like this: +<key> and <value> will appear in the output like this: ------------------------------------------------ -token: value +key: value ------------------------------------------------ -This means that the trimmed <token> and <value> will be separated by -`': '` (one colon followed by one space). For convenience, the <token> can be a -shortened string key (e.g., "sign") instead of the full string which should -appear before the separator on the output (e.g., "Signed-off-by"). This can be -configured using the 'trailer.<token>.key' configuration variable. +This means that the trimmed <key> and <value> will be separated by +`': '` (one colon followed by one space). + +For convenience, a <keyAlias> can be configured to make using `--trailer` +shorter to type on the command line. This can be configured using the +'trailer.<keyAlias>.key' configuration variable. The <keyAlias> must be a prefix +of the full <key> string, although case sensitivity does not matter. For +example, if you have + +------------------------------------------------ +trailer.sign.key "Signed-off-by: " +------------------------------------------------ + +in your configuration, you only need to specify `--trailer="sign: foo"` +on the command line instead of `--trailer="Signed-off-by: foo"`. By default the new trailer will appear at the end of all the existing trailers. If there is no existing trailer, the new trailer will appear @@ -80,14 +95,14 @@ non-whitespace lines before a line that starts with '---' (followed by a space or the end of the line). When reading trailers, there can be no whitespace before or inside the -<token>, but any number of regular space and tab characters are allowed -between the <token> and the separator. There can be whitespaces before, +<key>, but any number of regular space and tab characters are allowed +between the <key> and the separator. There can be whitespaces before, inside or after the <value>. The <value> may be split over multiple lines with each subsequent line starting with at least one whitespace, like the "folding" in RFC 822. Example: ------------------------------------------------ -token: This is a very long value, with spaces and +key: This is a very long value, with spaces and newlines in it. ------------------------------------------------ @@ -104,35 +119,44 @@ OPTIONS the whole trailer will be removed from the output. This applies to existing trailers as well as new trailers. ---trailer <token>[(=|:)<value>]:: - Specify a (<token>, <value>) pair that should be applied as a +--trailer <key>[(=|:)<value>]:: + Specify a (<key>, <value>) pair that should be applied as a trailer to the inputs. See the description of this command. --where <placement>:: --no-where:: Specify where all new trailers will be added. A setting - provided with '--where' overrides all configuration variables + provided with '--where' overrides the `trailer.where` and any + applicable `trailer.<keyAlias>.where` configuration variables and applies to all '--trailer' options until the next occurrence of - '--where' or '--no-where'. Possible values are `after`, `before`, - `end` or `start`. + '--where' or '--no-where'. Upon encountering '--no-where', clear the + effect of any previous use of '--where', such that the relevant configuration + variables are no longer overridden. Possible placements are `after`, + `before`, `end` or `start`. --if-exists <action>:: --no-if-exists:: Specify what action will be performed when there is already at - least one trailer with the same <token> in the input. A setting - provided with '--if-exists' overrides all configuration variables + least one trailer with the same <key> in the input. A setting + provided with '--if-exists' overrides the `trailer.ifExists` and any + applicable `trailer.<keyAlias>.ifExists` configuration variables and applies to all '--trailer' options until the next occurrence of - '--if-exists' or '--no-if-exists'. Possible actions are `addIfDifferent`, + '--if-exists' or '--no-if-exists'. Upon encountering '--no-if-exists, clear the + effect of any previous use of '--if-exists, such that the relevant configuration + variables are no longer overridden. Possible actions are `addIfDifferent`, `addIfDifferentNeighbor`, `add`, `replace` and `doNothing`. --if-missing <action>:: --no-if-missing:: Specify what action will be performed when there is no other - trailer with the same <token> in the input. A setting - provided with '--if-missing' overrides all configuration variables + trailer with the same <key> in the input. A setting + provided with '--if-missing' overrides the `trailer.ifMissing` and any + applicable `trailer.<keyAlias>.ifMissing` configuration variables and applies to all '--trailer' options until the next occurrence of - '--if-missing' or '--no-if-missing'. Possible actions are `doNothing` + '--if-missing' or '--no-if-missing'. Upon encountering '--no-if-missing, + clear the effect of any previous use of '--if-missing, such that the relevant + configuration variables are no longer overridden. Possible actions are `doNothing` or `add`. --only-trailers:: @@ -140,16 +164,19 @@ OPTIONS --only-input:: Output only trailers that exist in the input; do not add any - from the command-line or by following configured `trailer.*` - rules. + from the command-line or by applying `trailer.*` configuration + variables. --unfold:: - Remove any whitespace-continuation in trailers, so that each - trailer appears on a line by itself with its full content. + If a trailer has a value that runs over multiple lines (aka "folded"), + reformat the value into a single line. --parse:: A convenience alias for `--only-trailers --only-input - --unfold`. + --unfold`. This makes it easier to only see the trailers coming from the + input without influencing them with any command line options or + configuration variables, while also making the output machine-friendly with + --unfold. --no-divider:: Do not treat `---` as the end of the commit message. Use this @@ -170,11 +197,11 @@ used when another separator is not specified in the config for this trailer. + For example, if the value for this option is "%=$", then only lines -using the format '<token><sep><value>' with <sep> containing '%', '=' +using the format '<key><sep><value>' with <sep> containing '%', '=' or '$' and then spaces will be considered trailers. And '%' will be the default separator used, so by default trailers will appear like: -'<token>% <value>' (one percent sign and one space will appear between -the token and the value). +'<key>% <value>' (one percent sign and one space will appear between +the key and the value). trailer.where:: This option tells where a new trailer will be added. @@ -188,41 +215,41 @@ If it is `start`, then each new trailer will appear at the start, instead of the end, of the existing trailers. + If it is `after`, then each new trailer will appear just after the -last trailer with the same <token>. +last trailer with the same <key>. + If it is `before`, then each new trailer will appear just before the -first trailer with the same <token>. +first trailer with the same <key>. trailer.ifexists:: This option makes it possible to choose what action will be performed when there is already at least one trailer with the - same <token> in the input. + same <key> in the input. + The valid values for this option are: `addIfDifferentNeighbor` (this is the default), `addIfDifferent`, `add`, `replace` or `doNothing`. + With `addIfDifferentNeighbor`, a new trailer will be added only if no -trailer with the same (<token>, <value>) pair is above or below the line +trailer with the same (<key>, <value>) pair is above or below the line where the new trailer will be added. + With `addIfDifferent`, a new trailer will be added only if no trailer -with the same (<token>, <value>) pair is already in the input. +with the same (<key>, <value>) pair is already in the input. + With `add`, a new trailer will be added, even if some trailers with -the same (<token>, <value>) pair are already in the input. +the same (<key>, <value>) pair are already in the input. + -With `replace`, an existing trailer with the same <token> will be +With `replace`, an existing trailer with the same <key> will be deleted and the new trailer will be added. The deleted trailer will be -the closest one (with the same <token>) to the place where the new one +the closest one (with the same <key>) to the place where the new one will be added. + With `doNothing`, nothing will be done; that is no new trailer will be -added if there is already one with the same <token> in the input. +added if there is already one with the same <key> in the input. trailer.ifmissing:: This option makes it possible to choose what action will be performed when there is not yet any trailer with the same - <token> in the input. + <key> in the input. + The valid values for this option are: `add` (this is the default) and `doNothing`. @@ -231,34 +258,40 @@ With `add`, a new trailer will be added. + With `doNothing`, nothing will be done. -trailer.<token>.key:: - This `key` will be used instead of <token> in the trailer. At - the end of this key, a separator can appear and then some - space characters. By default the only valid separator is ':', - but this can be changed using the `trailer.separators` config - variable. +trailer.<keyAlias>.key:: + Defines a <keyAlias> for the <key>. The <keyAlias> must be a + prefix (case does not matter) of the <key>. For example, in `git + config trailer.ack.key "Acked-by"` the "Acked-by" is the <key> and + the "ack" is the <keyAlias>. This configuration allows the shorter + `--trailer "ack:..."` invocation on the command line using the "ack" + <keyAlias> instead of the longer `--trailer "Acked-by:..."`. ++ +At the end of the <key>, a separator can appear and then some +space characters. By default the only valid separator is ':', +but this can be changed using the `trailer.separators` config +variable. + -If there is a separator, then the key will be used instead of both the -<token> and the default separator when adding the trailer. +If there is a separator in the key, then it overrides the default +separator when adding the trailer. -trailer.<token>.where:: +trailer.<keyAlias>.where:: This option takes the same values as the 'trailer.where' configuration variable and it overrides what is specified by - that option for trailers with the specified <token>. + that option for trailers with the specified <keyAlias>. -trailer.<token>.ifexists:: +trailer.<keyAlias>.ifexists:: This option takes the same values as the 'trailer.ifexists' configuration variable and it overrides what is specified by - that option for trailers with the specified <token>. + that option for trailers with the specified <keyAlias>. -trailer.<token>.ifmissing:: +trailer.<keyAlias>.ifmissing:: This option takes the same values as the 'trailer.ifmissing' configuration variable and it overrides what is specified by - that option for trailers with the specified <token>. + that option for trailers with the specified <keyAlias>. -trailer.<token>.command:: - Deprecated in favor of 'trailer.<token>.cmd'. - This option behaves in the same way as 'trailer.<token>.cmd', except +trailer.<keyAlias>.command:: + Deprecated in favor of 'trailer.<keyAlias>.cmd'. + This option behaves in the same way as 'trailer.<keyAlias>.cmd', except that it doesn't pass anything as argument to the specified command. Instead the first occurrence of substring $ARG is replaced by the <value> that would be passed as argument. @@ -266,29 +299,29 @@ trailer.<token>.command:: Note that $ARG in the user's command is only replaced once and that the original way of replacing $ARG is not safe. + -When both 'trailer.<token>.cmd' and 'trailer.<token>.command' are given -for the same <token>, 'trailer.<token>.cmd' is used and -'trailer.<token>.command' is ignored. +When both 'trailer.<keyAlias>.cmd' and 'trailer.<keyAlias>.command' are given +for the same <keyAlias>, 'trailer.<keyAlias>.cmd' is used and +'trailer.<keyAlias>.command' is ignored. -trailer.<token>.cmd:: +trailer.<keyAlias>.cmd:: This option can be used to specify a shell command that will be called - once to automatically add a trailer with the specified <token>, and then - called each time a '--trailer <token>=<value>' argument is specified to + once to automatically add a trailer with the specified <keyAlias>, and then + called each time a '--trailer <keyAlias>=<value>' argument is specified to modify the <value> of the trailer that this option would produce. + When the specified command is first called to add a trailer -with the specified <token>, the behavior is as if a special -'--trailer <token>=<value>' argument was added at the beginning +with the specified <keyAlias>, the behavior is as if a special +'--trailer <keyAlias>=<value>' argument was added at the beginning of the "git interpret-trailers" command, where <value> is taken to be the standard output of the command with any leading and trailing whitespace trimmed off. + -If some '--trailer <token>=<value>' arguments are also passed +If some '--trailer <keyAlias>=<value>' arguments are also passed on the command line, the command is called again once for each -of these arguments with the same <token>. And the <value> part +of these arguments with the same <keyAlias>. And the <value> part of these arguments, if any, will be passed to the command as its first argument. This way the command can produce a <value> computed -from the <value> passed in the '--trailer <token>=<value>' argument. +from the <value> passed in the '--trailer <keyAlias>=<value>' argument. EXAMPLES -------- diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt index dea7eacb0f..e32404c6aa 100644 --- a/Documentation/git-pack-objects.txt +++ b/Documentation/git-pack-objects.txt @@ -296,8 +296,8 @@ So does `git bundle` (see linkgit:git-bundle[1]) when it creates a bundle. nevertheless. --filter=<filter-spec>:: - Requires `--stdout`. Omits certain objects (usually blobs) from - the resulting packfile. See linkgit:git-rev-list[1] for valid + Omits certain objects (usually blobs) from the resulting + packfile. See linkgit:git-rev-list[1] for valid `<filter-spec>` forms. --no-filter:: diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt index 4017157949..8545a32667 100644 --- a/Documentation/git-repack.txt +++ b/Documentation/git-repack.txt @@ -143,6 +143,29 @@ depth is 4095. a larger and slower repository; see the discussion in `pack.packSizeLimit`. +--filter=<filter-spec>:: + Remove objects matching the filter specification from the + resulting packfile and put them into a separate packfile. Note + that objects used in the working directory are not filtered + out. So for the split to fully work, it's best to perform it + in a bare repo and to use the `-a` and `-d` options along with + this option. Also `--no-write-bitmap-index` (or the + `repack.writebitmaps` config option set to `false`) should be + used otherwise writing bitmap index will fail, as it supposes + a single packfile containing all the objects. See + linkgit:git-rev-list[1] for valid `<filter-spec>` forms. + +--filter-to=<dir>:: + Write the pack containing filtered out objects to the + directory `<dir>`. Only useful with `--filter`. This can be + used for putting the pack on a separate object directory that + is accessed through the Git alternates mechanism. **WARNING:** + If the packfile containing the filtered out objects is not + accessible, the repo can become corrupt as it might not be + possible to access the objects in that packfile. See the + `objects` and `objects/info/alternates` sections of + linkgit:gitrepository-layout[5]. + -b:: --write-bitmap-index:: Write a reachability bitmap index as part of the repack. This diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt index b27d127b5e..48f46eb204 100644 --- a/Documentation/git-status.txt +++ b/Documentation/git-status.txt @@ -246,10 +246,9 @@ U U unmerged, both modified Submodules have more state and instead report - M the submodule has a different HEAD than - recorded in the index - m the submodule has modified content - ? the submodule has untracked files +* 'M' = the submodule has a different HEAD than recorded in the index +* 'm' = the submodule has modified content +* '?' = the submodule has untracked files since modified content or untracked files in a submodule cannot be added via `git add` in the superproject to prepare a commit. diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index a4a0cb93b2..66d71d1b95 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -151,6 +151,10 @@ endif::git-log[] --not:: Reverses the meaning of the '{caret}' prefix (or lack thereof) for all following revision specifiers, up to the next `--not`. + When used on the command line before --stdin, the revisions passed + through stdin will not be affected by it. Conversely, when passed + via standard input, the revisions passed on the command line will + not be affected by it. --all:: Pretend as if all the refs in `refs/`, along with `HEAD`, are @@ -240,7 +244,9 @@ endif::git-rev-list[] them from standard input as well. This accepts commits and pseudo-options like `--all` and `--glob=`. When a `--` separator is seen, the following input is treated as paths and used to - limit the result. + limit the result. Flags like `--not` which are read via standard input + are only respected for arguments passed in the same way and will not + influence any subsequent command line arguments. ifdef::git-rev-list[] --quiet:: @@ -800,6 +800,7 @@ TEST_BUILTINS_OBJS += test-dump-untracked-cache.o TEST_BUILTINS_OBJS += test-env-helper.o TEST_BUILTINS_OBJS += test-example-decorate.o TEST_BUILTINS_OBJS += test-fast-rebase.o +TEST_BUILTINS_OBJS += test-find-pack.o TEST_BUILTINS_OBJS += test-fsmonitor-client.o TEST_BUILTINS_OBJS += test-genrandom.o TEST_BUILTINS_OBJS += test-genzeros.o @@ -1039,6 +1040,7 @@ LIB_OBJS += hash-lookup.o LIB_OBJS += hashmap.o LIB_OBJS += help.o LIB_OBJS += hex.o +LIB_OBJS += hex-ll.o LIB_OBJS += hook.o LIB_OBJS += ident.o LIB_OBJS += json-writer.o @@ -1089,6 +1091,7 @@ LIB_OBJS += pack-write.o LIB_OBJS += packfile.o LIB_OBJS += pager.o LIB_OBJS += parallel-checkout.o +LIB_OBJS += parse.o LIB_OBJS += parse-options-cb.o LIB_OBJS += parse-options.o LIB_OBJS += patch-delta.o @@ -7,7 +7,7 @@ */ #include "git-compat-util.h" -#include "config.h" +#include "parse.h" #include "environment.h" #include "exec-cmd.h" #include "attr.h" diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c index c527a8369e..45d035af60 100644 --- a/builtin/commit-graph.c +++ b/builtin/commit-graph.c @@ -69,10 +69,12 @@ static int graph_verify(int argc, const char **argv, const char *prefix) struct commit_graph *graph = NULL; struct object_directory *odb = NULL; char *graph_name; - int open_ok; + char *chain_name; + enum { OPENED_NONE, OPENED_GRAPH, OPENED_CHAIN } opened = OPENED_NONE; int fd; struct stat st; int flags = 0; + int incomplete_chain = 0; int ret; static struct option builtin_commit_graph_verify_options[] = { @@ -102,24 +104,39 @@ static int graph_verify(int argc, const char **argv, const char *prefix) odb = find_odb(the_repository, opts.obj_dir); graph_name = get_commit_graph_filename(odb); - open_ok = open_commit_graph(graph_name, &fd, &st); - if (!open_ok && errno != ENOENT) + chain_name = get_commit_graph_chain_filename(odb); + if (open_commit_graph(graph_name, &fd, &st)) + opened = OPENED_GRAPH; + else if (errno != ENOENT) die_errno(_("Could not open commit-graph '%s'"), graph_name); + else if (open_commit_graph_chain(chain_name, &fd, &st)) + opened = OPENED_CHAIN; + else if (errno != ENOENT) + die_errno(_("could not open commit-graph chain '%s'"), chain_name); FREE_AND_NULL(graph_name); + FREE_AND_NULL(chain_name); FREE_AND_NULL(options); - if (open_ok) + if (opened == OPENED_NONE) + return 0; + else if (opened == OPENED_GRAPH) graph = load_commit_graph_one_fd_st(the_repository, fd, &st, odb); else - graph = read_commit_graph_one(the_repository, odb); + graph = load_commit_graph_chain_fd_st(the_repository, fd, &st, + &incomplete_chain); - /* Return failure if open_ok predicted success */ if (!graph) - return !!open_ok; + return 1; ret = verify_commit_graph(the_repository, graph, flags); free_commit_graph(graph); + + if (incomplete_chain) { + error("one or more commit-graph chain files could not be loaded"); + ret |= 1; + } + return ret; } diff --git a/builtin/diff.c b/builtin/diff.c index c0f564273a..55e7d21755 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -474,9 +474,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix) repo_init_revisions(the_repository, &rev, 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; + init_diffstat_widths(&rev.diffopt); rev.diffopt.flags.allow_external = 1; rev.diffopt.flags.allow_textconv = 1; diff --git a/builtin/gc.c b/builtin/gc.c index 00192ae5d3..68ca8d45bf 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -61,6 +61,8 @@ static timestamp_t gc_log_expire_time; static const char *gc_log_expire = "1.day.ago"; static const char *prune_expire = "2.weeks.ago"; static const char *prune_worktrees_expire = "3.months.ago"; +static char *repack_filter; +static char *repack_filter_to; static unsigned long big_pack_threshold; static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE; @@ -170,6 +172,9 @@ static void gc_config(void) git_config_get_ulong("gc.bigpackthreshold", &big_pack_threshold); git_config_get_ulong("pack.deltacachesize", &max_delta_cache_size); + git_config_get_string("gc.repackfilter", &repack_filter); + git_config_get_string("gc.repackfilterto", &repack_filter_to); + git_config(git_default_config, NULL); } @@ -355,6 +360,11 @@ static void add_repack_all_option(struct string_list *keep_pack) if (keep_pack) for_each_string_list(keep_pack, keep_one_pack, NULL); + + if (repack_filter && *repack_filter) + strvec_pushf(&repack, "--filter=%s", repack_filter); + if (repack_filter_to && *repack_filter_to) + strvec_pushf(&repack, "--filter-to=%s", repack_filter_to); } static void add_repack_incremental_option(void) diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c index a110e69f83..033bd1556c 100644 --- a/builtin/interpret-trailers.c +++ b/builtin/interpret-trailers.c @@ -14,7 +14,7 @@ static const char * const git_interpret_trailers_usage[] = { N_("git interpret-trailers [--in-place] [--trim-empty]\n" - " [(--trailer <token>[(=|:)<value>])...]\n" + " [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n" " [--parse] [<file>...]"), NULL }; @@ -100,7 +100,7 @@ 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", &where, N_("action"), + OPT_CALLBACK(0, "where", &where, N_("placement"), N_("where to place the new trailer"), option_parse_where), OPT_CALLBACK(0, "if-exists", &if_exists, N_("action"), N_("action if trailer already exists"), option_parse_if_exists), @@ -108,11 +108,11 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix) N_("action if trailer is missing"), option_parse_if_missing), OPT_BOOL(0, "only-trailers", &opts.only_trailers, N_("output only the trailers")), - OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply config rules")), - OPT_BOOL(0, "unfold", &opts.unfold, N_("join whitespace-continued values")), - OPT_CALLBACK_F(0, "parse", &opts, NULL, N_("set parsing options"), + OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply trailer.* configuration variables")), + OPT_BOOL(0, "unfold", &opts.unfold, N_("reformat multiline trailer values as single-line values")), + OPT_CALLBACK_F(0, "parse", &opts, NULL, N_("alias for --only-trailers --only-input --unfold"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_parse), - OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat --- specially")), + OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat \"---\" as the end of input")), OPT_CALLBACK(0, "trailer", &trailers, N_("trailer"), N_("trailer(s) to add"), option_parse_trailer), OPT_END() diff --git a/builtin/log.c b/builtin/log.c index 80e1be1645..ba775d7b5c 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -176,17 +176,15 @@ static void cmd_log_init_defaults(struct rev_info *rev) if (default_follow) rev->diffopt.flags.default_follow_renames = 1; rev->verbose_header = 1; + init_diffstat_widths(&rev->diffopt); 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->diffopt.flags.allow_textconv = 1; rev->abbrev_commit = default_abbrev_commit; rev->show_root_diff = default_show_root; rev->subject_prefix = fmt_patch_subject_prefix; rev->patch_name_max = fmt_patch_name_max; rev->show_signature = default_show_signature; rev->encode_email_headers = default_encode_email_headers; - rev->diffopt.flags.allow_textconv = 1; if (default_date_mode) parse_date_format(default_date_mode, &rev->date_mode); diff --git a/builtin/merge.c b/builtin/merge.c index ac4816c14e..d748d46e13 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -466,9 +466,7 @@ static void finish(struct commit *head_commit, if (new_head && show_diffstat) { 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 */ + init_diffstat_widths(&opts); opts.output_format |= DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; opts.detect_rename = DIFF_DETECT_RENAME; diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 6eb9756836..89a8b5a976 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -4402,12 +4402,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (!rev_list_all || !rev_list_reflog || !rev_list_index) unpack_unreachable_expiration = 0; - if (filter_options.choice) { - if (!pack_to_stdout) - die(_("cannot use --filter without --stdout")); - if (stdin_packs) - die(_("cannot use --filter with --stdin-packs")); - } + if (stdin_packs && filter_options.choice) + die(_("cannot use --filter with --stdin-packs")); if (stdin_packs && use_internal_rev_list) die(_("cannot use internal rev list with --stdin-packs")); diff --git a/builtin/rebase.c b/builtin/rebase.c index ed15accec9..f990811614 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -1803,9 +1803,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 */ + init_diffstat_widths(&opts); opts.output_format |= DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; opts.detect_rename = DIFF_DETECT_RENAME; diff --git a/builtin/repack.c b/builtin/repack.c index 529e13120d..db9277081d 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -21,6 +21,7 @@ #include "pack.h" #include "pack-bitmap.h" #include "refs.h" +#include "list-objects-filter-options.h" #define ALL_INTO_ONE 1 #define LOOSEN_UNREACHABLE 2 @@ -56,6 +57,7 @@ struct pack_objects_args { int no_reuse_object; int quiet; int local; + struct list_objects_filter_options filter_options; }; static int repack_config(const char *var, const char *value, @@ -806,6 +808,86 @@ static void remove_redundant_bitmaps(struct string_list *include, strbuf_release(&path); } +static int finish_pack_objects_cmd(struct child_process *cmd, + struct string_list *names, + int local) +{ + FILE *out; + struct strbuf line = STRBUF_INIT; + + out = xfdopen(cmd->out, "r"); + while (strbuf_getline_lf(&line, out) != EOF) { + struct string_list_item *item; + + if (line.len != the_hash_algo->hexsz) + die(_("repack: Expecting full hex object ID lines only " + "from pack-objects.")); + /* + * Avoid putting packs written outside of the repository in the + * list of names. + */ + if (local) { + item = string_list_append(names, line.buf); + item->util = populate_pack_exts(line.buf); + } + } + fclose(out); + + strbuf_release(&line); + + return finish_command(cmd); +} + +static int write_filtered_pack(const struct pack_objects_args *args, + const char *destination, + const char *pack_prefix, + struct existing_packs *existing, + struct string_list *names) +{ + struct child_process cmd = CHILD_PROCESS_INIT; + struct string_list_item *item; + FILE *in; + int ret; + const char *caret; + const char *scratch; + int local = skip_prefix(destination, packdir, &scratch); + + prepare_pack_objects(&cmd, args, destination); + + strvec_push(&cmd.args, "--stdin-packs"); + + if (!pack_kept_objects) + strvec_push(&cmd.args, "--honor-pack-keep"); + for_each_string_list_item(item, &existing->kept_packs) + strvec_pushf(&cmd.args, "--keep-pack=%s", item->string); + + cmd.in = -1; + + ret = start_command(&cmd); + if (ret) + return ret; + + /* + * Here 'names' contains only the pack(s) that were just + * written, which is exactly the packs we want to keep. Also + * 'existing_kept_packs' already contains the packs in + * 'keep_pack_list'. + */ + 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->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); + caret = pack_kept_objects ? "" : "^"; + for_each_string_list_item(item, &existing->kept_packs) + fprintf(in, "%s%s.pack\n", caret, item->string); + fclose(in); + + return finish_pack_objects_cmd(&cmd, names, local); +} + static int write_cruft_pack(const struct pack_objects_args *args, const char *destination, const char *pack_prefix, @@ -814,9 +896,8 @@ static int write_cruft_pack(const struct pack_objects_args *args, struct existing_packs *existing) { struct child_process cmd = CHILD_PROCESS_INIT; - struct strbuf line = STRBUF_INIT; struct string_list_item *item; - FILE *in, *out; + FILE *in; int ret; const char *scratch; int local = skip_prefix(destination, packdir, &scratch); @@ -861,27 +942,18 @@ static int write_cruft_pack(const struct pack_objects_args *args, fprintf(in, "%s.pack\n", item->string); fclose(in); - out = xfdopen(cmd.out, "r"); - while (strbuf_getline_lf(&line, out) != EOF) { - struct string_list_item *item; - - if (line.len != the_hash_algo->hexsz) - die(_("repack: Expecting full hex object ID lines only " - "from pack-objects.")); - /* - * avoid putting packs written outside of the repository in the - * list of names - */ - if (local) { - item = string_list_append(names, line.buf); - item->util = populate_pack_exts(line.buf); - } - } - fclose(out); - - strbuf_release(&line); + return finish_pack_objects_cmd(&cmd, names, local); +} - return finish_command(&cmd); +static const char *find_pack_prefix(const char *packdir, const char *packtmp) +{ + const char *pack_prefix; + if (!skip_prefix(packtmp, packdir, &pack_prefix)) + die(_("pack prefix %s does not begin with objdir %s"), + packtmp, packdir); + if (*pack_prefix == '/') + pack_prefix++; + return pack_prefix; } int cmd_repack(int argc, const char **argv, const char *prefix) @@ -891,10 +963,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix) struct string_list names = STRING_LIST_INIT_DUP; 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; - FILE *out; int show_progress; /* variables to be filled by option parsing */ @@ -907,6 +977,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) int write_midx = 0; const char *cruft_expiration = NULL; const char *expire_to = NULL; + const char *filter_to = NULL; struct option builtin_repack_options[] = { OPT_BIT('a', NULL, &pack_everything, @@ -948,6 +1019,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) N_("limits the maximum number of threads")), OPT_STRING(0, "max-pack-size", &po_args.max_pack_size, N_("bytes"), N_("maximum size of each packfile")), + OPT_PARSE_LIST_OBJECTS_FILTER(&po_args.filter_options), OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects, N_("repack objects in packs marked with .keep")), OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"), @@ -958,9 +1030,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix) N_("write a multi-pack index of the resulting packs")), OPT_STRING(0, "expire-to", &expire_to, N_("dir"), N_("pack prefix to store a pack containing pruned objects")), + OPT_STRING(0, "filter-to", &filter_to, N_("dir"), + N_("pack prefix to store a pack containing filtered out objects")), OPT_END() }; + list_objects_filter_init(&po_args.filter_options); + git_config(repack_config, &cruft_po_args); argc = parse_options(argc, argv, prefix, builtin_repack_options, @@ -1101,6 +1177,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix) strvec_push(&cmd.args, "--incremental"); } + if (po_args.filter_options.choice) + strvec_pushf(&cmd.args, "--filter=%s", + expand_list_objects_filter_spec(&po_args.filter_options)); + else if (filter_to) + die(_("option '%s' can only be used along with '%s'"), "--filter-to", "--filter"); + if (geometry.split_factor) cmd.in = -1; else @@ -1124,18 +1206,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) fclose(in); } - out = xfdopen(cmd.out, "r"); - while (strbuf_getline_lf(&line, out) != EOF) { - struct string_list_item *item; - - if (line.len != the_hash_algo->hexsz) - die(_("repack: Expecting full hex object ID lines only from pack-objects.")); - item = string_list_append(&names, line.buf); - item->util = populate_pack_exts(item->string); - } - strbuf_release(&line); - fclose(out); - ret = finish_command(&cmd); + ret = finish_pack_objects_cmd(&cmd, &names, 1); if (ret) goto cleanup; @@ -1143,12 +1214,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) printf_ln(_("Nothing new to pack.")); if (pack_everything & PACK_CRUFT) { - const char *pack_prefix; - if (!skip_prefix(packtmp, packdir, &pack_prefix)) - die(_("pack prefix %s does not begin with objdir %s"), - packtmp, packdir); - if (*pack_prefix == '/') - pack_prefix++; + const char *pack_prefix = find_pack_prefix(packdir, packtmp); if (!cruft_po_args.window) cruft_po_args.window = po_args.window; @@ -1203,6 +1269,19 @@ int cmd_repack(int argc, const char **argv, const char *prefix) } } + if (po_args.filter_options.choice) { + if (!filter_to) + filter_to = packtmp; + + ret = write_filtered_pack(&po_args, + filter_to, + find_pack_prefix(packdir, packtmp), + &existing, + &names); + if (ret) + goto cleanup; + } + string_list_sort(&names); close_object_store(the_repository->objects); @@ -1295,6 +1374,7 @@ cleanup: string_list_clear(&names, 1); existing_packs_release(&existing); free_pack_geometry(&geometry); + list_objects_filter_release(&po_args.filter_options); return ret; } diff --git a/bulk-checkin.c b/bulk-checkin.c index 92b9c8598b..6ce62999e5 100644 --- a/bulk-checkin.c +++ b/bulk-checkin.c @@ -155,10 +155,10 @@ static int already_written(struct bulk_checkin_packfile *state, struct object_id * status before calling us just in case we ask it to call us again * with a new pack. */ -static int stream_to_pack(struct bulk_checkin_packfile *state, - git_hash_ctx *ctx, off_t *already_hashed_to, - int fd, size_t size, enum object_type type, - const char *path, unsigned flags) +static int stream_blob_to_pack(struct bulk_checkin_packfile *state, + git_hash_ctx *ctx, off_t *already_hashed_to, + int fd, size_t size, const char *path, + unsigned flags) { git_zstream s; unsigned char ibuf[16384]; @@ -170,7 +170,7 @@ static int stream_to_pack(struct bulk_checkin_packfile *state, git_deflate_init(&s, pack_compression_level); - hdrlen = encode_in_pack_object_header(obuf, sizeof(obuf), type, size); + hdrlen = encode_in_pack_object_header(obuf, sizeof(obuf), OBJ_BLOB, size); s.next_out = obuf + hdrlen; s.avail_out = sizeof(obuf) - hdrlen; @@ -247,11 +247,10 @@ static void prepare_to_stream(struct bulk_checkin_packfile *state, die_errno("unable to write pack header"); } -static int deflate_to_pack(struct bulk_checkin_packfile *state, - struct object_id *result_oid, - int fd, size_t size, - enum object_type type, const char *path, - unsigned flags) +static int deflate_blob_to_pack(struct bulk_checkin_packfile *state, + struct object_id *result_oid, + int fd, size_t size, + const char *path, unsigned flags) { off_t seekback, already_hashed_to; git_hash_ctx ctx; @@ -265,7 +264,7 @@ static int deflate_to_pack(struct bulk_checkin_packfile *state, return error("cannot find the current offset"); header_len = format_object_header((char *)obuf, sizeof(obuf), - type, size); + OBJ_BLOB, size); the_hash_algo->init_fn(&ctx); the_hash_algo->update_fn(&ctx, obuf, header_len); the_hash_algo->init_fn(&checkpoint.ctx); @@ -283,8 +282,8 @@ static int deflate_to_pack(struct bulk_checkin_packfile *state, idx->offset = state->offset; crc32_begin(state->f); } - if (!stream_to_pack(state, &ctx, &already_hashed_to, - fd, size, type, path, flags)) + if (!stream_blob_to_pack(state, &ctx, &already_hashed_to, + fd, size, path, flags)) break; /* * Writing this object to the current pack will make @@ -351,12 +350,12 @@ void fsync_loose_object_bulk_checkin(int fd, const char *filename) } } -int index_bulk_checkin(struct object_id *oid, - int fd, size_t size, enum object_type type, - const char *path, unsigned flags) +int index_blob_bulk_checkin(struct object_id *oid, + int fd, size_t size, + const char *path, unsigned flags) { - int status = deflate_to_pack(&bulk_checkin_packfile, oid, fd, size, type, - path, flags); + int status = deflate_blob_to_pack(&bulk_checkin_packfile, oid, fd, size, + path, flags); if (!odb_transaction_nesting) flush_bulk_checkin_packfile(&bulk_checkin_packfile); return status; diff --git a/bulk-checkin.h b/bulk-checkin.h index 48fe9a6e91..aa7286a7b3 100644 --- a/bulk-checkin.h +++ b/bulk-checkin.h @@ -9,9 +9,9 @@ void prepare_loose_object_bulk_checkin(void); void fsync_loose_object_bulk_checkin(int fd, const char *filename); -int index_bulk_checkin(struct object_id *oid, - int fd, size_t size, enum object_type type, - const char *path, unsigned flags); +int index_blob_bulk_checkin(struct object_id *oid, + int fd, size_t size, + const char *path, unsigned flags); /* * Tell the object database to optimize for adding @@ -3,7 +3,7 @@ #include "color.h" #include "editor.h" #include "gettext.h" -#include "hex.h" +#include "hex-ll.h" #include "pager.h" #include "strbuf.h" diff --git a/commit-graph.c b/commit-graph.c index e4d09da090..fd2f700b2e 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -473,6 +473,31 @@ static struct commit_graph *load_commit_graph_v1(struct repository *r, return g; } +/* + * returns 1 if and only if all graphs in the chain have + * corrected commit dates stored in the generation_data chunk. + */ +static int validate_mixed_generation_chain(struct commit_graph *g) +{ + int read_generation_data = 1; + struct commit_graph *p = g; + + while (read_generation_data && p) { + read_generation_data = p->read_generation_data; + p = p->base_graph; + } + + if (read_generation_data) + return 1; + + while (g) { + g->read_generation_data = 0; + g = g->base_graph; + } + + return 0; +} + static int add_graph_to_chain(struct commit_graph *g, struct commit_graph *chain, struct object_id *oids, @@ -513,31 +538,41 @@ static int add_graph_to_chain(struct commit_graph *g, return 1; } -static struct commit_graph *load_commit_graph_chain(struct repository *r, - struct object_directory *odb) +int open_commit_graph_chain(const char *chain_file, + int *fd, struct stat *st) +{ + *fd = git_open(chain_file); + if (*fd < 0) + return 0; + if (fstat(*fd, st)) { + close(*fd); + return 0; + } + if (st->st_size < the_hash_algo->hexsz) { + close(*fd); + if (!st->st_size) { + /* treat empty files the same as missing */ + errno = ENOENT; + } else { + warning("commit-graph chain file too small"); + errno = EINVAL; + } + return 0; + } + return 1; +} + +struct commit_graph *load_commit_graph_chain_fd_st(struct repository *r, + int fd, struct stat *st, + int *incomplete_chain) { struct commit_graph *graph_chain = NULL; struct strbuf line = STRBUF_INIT; - struct stat st; struct object_id *oids; int i = 0, valid = 1, count; - char *chain_name = get_commit_graph_chain_filename(odb); - FILE *fp; - int stat_res; - - fp = fopen(chain_name, "r"); - stat_res = stat(chain_name, &st); - free(chain_name); + FILE *fp = xfdopen(fd, "r"); - if (!fp) - return NULL; - if (stat_res || - st.st_size <= the_hash_algo->hexsz) { - fclose(fp); - return NULL; - } - - count = st.st_size / (the_hash_algo->hexsz + 1); + count = st->st_size / (the_hash_algo->hexsz + 1); CALLOC_ARRAY(oids, count); prepare_alt_odb(r); @@ -580,36 +615,32 @@ static struct commit_graph *load_commit_graph_chain(struct repository *r, } } + validate_mixed_generation_chain(graph_chain); + free(oids); fclose(fp); strbuf_release(&line); + *incomplete_chain = !valid; return graph_chain; } -/* - * returns 1 if and only if all graphs in the chain have - * corrected commit dates stored in the generation_data chunk. - */ -static int validate_mixed_generation_chain(struct commit_graph *g) +static struct commit_graph *load_commit_graph_chain(struct repository *r, + struct object_directory *odb) { - int read_generation_data = 1; - struct commit_graph *p = g; - - while (read_generation_data && p) { - read_generation_data = p->read_generation_data; - p = p->base_graph; - } - - if (read_generation_data) - return 1; + char *chain_file = get_commit_graph_chain_filename(odb); + struct stat st; + int fd; + struct commit_graph *g = NULL; - while (g) { - g->read_generation_data = 0; - g = g->base_graph; + if (open_commit_graph_chain(chain_file, &fd, &st)) { + int incomplete; + /* ownership of fd is taken over by load function */ + g = load_commit_graph_chain_fd_st(r, fd, &st, &incomplete); } - return 0; + free(chain_file); + return g; } struct commit_graph *read_commit_graph_one(struct repository *r, @@ -620,8 +651,6 @@ struct commit_graph *read_commit_graph_one(struct repository *r, if (!g) g = load_commit_graph_chain(r, odb); - validate_mixed_generation_chain(g); - return g; } diff --git a/commit-graph.h b/commit-graph.h index 5e534f0fcc..20ada7e891 100644 --- a/commit-graph.h +++ b/commit-graph.h @@ -26,6 +26,7 @@ struct string_list; char *get_commit_graph_filename(struct object_directory *odb); char *get_commit_graph_chain_filename(struct object_directory *odb); int open_commit_graph(const char *graph_file, int *fd, struct stat *st); +int open_commit_graph_chain(const char *chain_file, int *fd, struct stat *st); /* * Given a commit struct, try to fill the commit struct info, including: @@ -105,6 +106,9 @@ struct commit_graph { struct commit_graph *load_commit_graph_one_fd_st(struct repository *r, int fd, struct stat *st, struct object_directory *odb); +struct commit_graph *load_commit_graph_chain_fd_st(struct repository *r, + int fd, struct stat *st, + int *incomplete_chain); struct commit_graph *read_commit_graph_one(struct repository *r, struct object_directory *odb); @@ -11,6 +11,7 @@ #include "date.h" #include "branch.h" #include "config.h" +#include "parse.h" #include "convert.h" #include "environment.h" #include "gettext.h" @@ -1165,129 +1166,6 @@ static int git_parse_source(struct config_source *cs, config_fn_t fn, return error_return; } -static uintmax_t get_unit_factor(const char *end) -{ - if (!*end) - return 1; - else if (!strcasecmp(end, "k")) - return 1024; - else if (!strcasecmp(end, "m")) - return 1024 * 1024; - else if (!strcasecmp(end, "g")) - return 1024 * 1024 * 1024; - return 0; -} - -static int git_parse_signed(const char *value, intmax_t *ret, intmax_t max) -{ - if (value && *value) { - char *end; - intmax_t val; - intmax_t factor; - - if (max < 0) - BUG("max must be a positive integer"); - - errno = 0; - val = strtoimax(value, &end, 0); - if (errno == ERANGE) - return 0; - if (end == value) { - errno = EINVAL; - return 0; - } - factor = get_unit_factor(end); - if (!factor) { - errno = EINVAL; - return 0; - } - if ((val < 0 && -max / factor > val) || - (val > 0 && max / factor < val)) { - errno = ERANGE; - return 0; - } - val *= factor; - *ret = val; - return 1; - } - errno = EINVAL; - return 0; -} - -static int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max) -{ - if (value && *value) { - char *end; - uintmax_t val; - uintmax_t factor; - - /* negative values would be accepted by strtoumax */ - if (strchr(value, '-')) { - errno = EINVAL; - return 0; - } - errno = 0; - val = strtoumax(value, &end, 0); - if (errno == ERANGE) - return 0; - if (end == value) { - errno = EINVAL; - return 0; - } - factor = get_unit_factor(end); - if (!factor) { - errno = EINVAL; - return 0; - } - if (unsigned_mult_overflows(factor, val) || - factor * val > max) { - errno = ERANGE; - return 0; - } - val *= factor; - *ret = val; - return 1; - } - errno = EINVAL; - return 0; -} - -int git_parse_int(const char *value, int *ret) -{ - intmax_t tmp; - if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int))) - return 0; - *ret = tmp; - return 1; -} - -static int git_parse_int64(const char *value, int64_t *ret) -{ - intmax_t tmp; - if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int64_t))) - return 0; - *ret = tmp; - return 1; -} - -int git_parse_ulong(const char *value, unsigned long *ret) -{ - uintmax_t tmp; - if (!git_parse_unsigned(value, &tmp, maximum_unsigned_value_of_type(long))) - return 0; - *ret = tmp; - return 1; -} - -int git_parse_ssize_t(const char *value, ssize_t *ret) -{ - intmax_t tmp; - if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(ssize_t))) - return 0; - *ret = tmp; - return 1; -} - NORETURN static void die_bad_number(const char *name, const char *value, const struct key_value_info *kvi) @@ -1363,23 +1241,6 @@ ssize_t git_config_ssize_t(const char *name, const char *value, return ret; } -static int git_parse_maybe_bool_text(const char *value) -{ - if (!value) - return 1; - if (!*value) - return 0; - if (!strcasecmp(value, "true") - || !strcasecmp(value, "yes") - || !strcasecmp(value, "on")) - return 1; - if (!strcasecmp(value, "false") - || !strcasecmp(value, "no") - || !strcasecmp(value, "off")) - return 0; - return -1; -} - static const struct fsync_component_name { const char *name; enum fsync_component component_bits; @@ -1454,16 +1315,6 @@ next_name: return (current & ~negative) | positive; } -int git_parse_maybe_bool(const char *value) -{ - int v = git_parse_maybe_bool_text(value); - if (0 <= v) - return v; - if (git_parse_int(value, &v)) - return !!v; - return -1; -} - int git_config_bool_or_int(const char *name, const char *value, const struct key_value_info *kvi, int *is_bool) { @@ -2131,28 +1982,6 @@ void git_global_config(char **user_out, char **xdg_out) *xdg_out = xdg_config; } -/* - * Parse environment variable 'k' as a boolean (in various - * possible spellings); if missing, use the default value 'def'. - */ -int git_env_bool(const char *k, int def) -{ - const char *v = getenv(k); - return v ? git_config_bool(k, v) : def; -} - -/* - * Parse environment variable 'k' as ulong with possibly a unit - * suffix; if missing, use the default value 'val'. - */ -unsigned long git_env_ulong(const char *k, unsigned long val) -{ - const char *v = getenv(k); - if (v && !git_parse_ulong(v, &val)) - die(_("failed to parse %s"), k); - return val; -} - int git_config_system(void) { return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0); @@ -4,7 +4,7 @@ #include "hashmap.h" #include "string-list.h" #include "repository.h" - +#include "parse.h" /** * The config API gives callers a way to access Git configuration files @@ -243,16 +243,6 @@ int config_with_options(config_fn_t fn, void *, * The following helper functions aid in parsing string values */ -int git_parse_ssize_t(const char *, ssize_t *); -int git_parse_ulong(const char *, unsigned long *); -int git_parse_int(const char *value, int *ret); - -/** - * Same as `git_config_bool`, except that it returns -1 on error rather - * than dying. - */ -int git_parse_maybe_bool(const char *); - /** * Parse the string to an integer, including unit factors. Dies on error; * otherwise, returns the parsed result. @@ -385,8 +375,6 @@ int git_config_rename_section(const char *, const char *); int git_config_rename_section_in_file(const char *, const char *, const char *); int git_config_copy_section(const char *, const char *); int git_config_copy_section_in_file(const char *, const char *, const char *); -int git_env_bool(const char *, int); -unsigned long git_env_ulong(const char *, unsigned long); int git_config_system(void); int config_error_nonbool(const char *); #if defined(__GNUC__) @@ -6936,6 +6936,13 @@ void diff_queued_diff_prefetch(void *repository) oid_array_clear(&to_fetch); } +void init_diffstat_widths(struct diff_options *options) +{ + options->stat_width = -1; /* use full terminal width */ + options->stat_name_width = -1; /* respect diff.statNameWidth config */ + options->stat_graph_width = -1; /* respect diff.statGraphWidth config */ +} + void diffcore_std(struct diff_options *options) { int output_formats_to_prefetch = DIFF_FORMAT_DIFFSTAT | @@ -573,6 +573,7 @@ int git_config_rename(const char *var, const char *value); #define DIFF_PICKAXE_IGNORE_CASE 32 +void init_diffstat_widths(struct diff_options *); void diffcore_std(struct diff_options *); void diffcore_fix_diff_index(void); @@ -581,3 +581,8 @@ void unlink_entry(const struct cache_entry *ce, const char *super_prefix) return; schedule_dir_for_removal(ce->name, ce_namelen(ce)); } + +int remove_or_warn(unsigned int mode, const char *file) +{ + return S_ISGITLINK(mode) ? rmdir_or_warn(file) : unlink_or_warn(file); +} @@ -62,4 +62,10 @@ int fstat_checkout_output(int fd, const struct checkout *state, struct stat *st) void update_ce_after_write(const struct checkout *state, struct cache_entry *ce, struct stat *st); +/* + * Calls the correct function out of {unlink,rmdir}_or_warn based on + * the supplied file mode. + */ +int remove_or_warn(unsigned int mode, const char *path); + #endif /* ENTRY_H */ diff --git a/hex-ll.c b/hex-ll.c new file mode 100644 index 0000000000..4d7ece1de5 --- /dev/null +++ b/hex-ll.c @@ -0,0 +1,49 @@ +#include "git-compat-util.h" +#include "hex-ll.h" + +const signed char hexval_table[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, /* 00-07 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 08-0f */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 10-17 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 18-1f */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 20-27 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 28-2f */ + 0, 1, 2, 3, 4, 5, 6, 7, /* 30-37 */ + 8, 9, -1, -1, -1, -1, -1, -1, /* 38-3f */ + -1, 10, 11, 12, 13, 14, 15, -1, /* 40-47 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 48-4f */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 50-57 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 58-5f */ + -1, 10, 11, 12, 13, 14, 15, -1, /* 60-67 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 68-67 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 70-77 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 78-7f */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 80-87 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 88-8f */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 90-97 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* 98-9f */ + -1, -1, -1, -1, -1, -1, -1, -1, /* a0-a7 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* a8-af */ + -1, -1, -1, -1, -1, -1, -1, -1, /* b0-b7 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* b8-bf */ + -1, -1, -1, -1, -1, -1, -1, -1, /* c0-c7 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* c8-cf */ + -1, -1, -1, -1, -1, -1, -1, -1, /* d0-d7 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* d8-df */ + -1, -1, -1, -1, -1, -1, -1, -1, /* e0-e7 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* e8-ef */ + -1, -1, -1, -1, -1, -1, -1, -1, /* f0-f7 */ + -1, -1, -1, -1, -1, -1, -1, -1, /* f8-ff */ +}; + +int hex_to_bytes(unsigned char *binary, const char *hex, size_t len) +{ + for (; len; len--, hex += 2) { + unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]); + + if (val & ~0xff) + return -1; + *binary++ = val; + } + return 0; +} diff --git a/hex-ll.h b/hex-ll.h new file mode 100644 index 0000000000..a381fa8556 --- /dev/null +++ b/hex-ll.h @@ -0,0 +1,27 @@ +#ifndef HEX_LL_H +#define HEX_LL_H + +extern const signed char hexval_table[256]; +static inline unsigned int hexval(unsigned char c) +{ + return hexval_table[c]; +} + +/* + * Convert two consecutive hexadecimal digits into a char. Return a + * negative value on error. Don't run over the end of short strings. + */ +static inline int hex2chr(const char *s) +{ + unsigned int val = hexval(s[0]); + return (val & ~0xf) ? val : (val << 4) | hexval(s[1]); +} + +/* + * Read `len` pairs of hexadecimal digits from `hex` and write the + * values to `binary` as `len` bytes. Return 0 on success, or -1 if + * the input does not consist of hex digits). + */ +int hex_to_bytes(unsigned char *binary, const char *hex, size_t len); + +#endif @@ -2,53 +2,6 @@ #include "hash.h" #include "hex.h" -const signed char hexval_table[256] = { - -1, -1, -1, -1, -1, -1, -1, -1, /* 00-07 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 08-0f */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 10-17 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 18-1f */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 20-27 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 28-2f */ - 0, 1, 2, 3, 4, 5, 6, 7, /* 30-37 */ - 8, 9, -1, -1, -1, -1, -1, -1, /* 38-3f */ - -1, 10, 11, 12, 13, 14, 15, -1, /* 40-47 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 48-4f */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 50-57 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 58-5f */ - -1, 10, 11, 12, 13, 14, 15, -1, /* 60-67 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 68-67 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 70-77 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 78-7f */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 80-87 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 88-8f */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 90-97 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* 98-9f */ - -1, -1, -1, -1, -1, -1, -1, -1, /* a0-a7 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* a8-af */ - -1, -1, -1, -1, -1, -1, -1, -1, /* b0-b7 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* b8-bf */ - -1, -1, -1, -1, -1, -1, -1, -1, /* c0-c7 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* c8-cf */ - -1, -1, -1, -1, -1, -1, -1, -1, /* d0-d7 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* d8-df */ - -1, -1, -1, -1, -1, -1, -1, -1, /* e0-e7 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* e8-ef */ - -1, -1, -1, -1, -1, -1, -1, -1, /* f0-f7 */ - -1, -1, -1, -1, -1, -1, -1, -1, /* f8-ff */ -}; - -int hex_to_bytes(unsigned char *binary, const char *hex, size_t len) -{ - for (; len; len--, hex += 2) { - unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]); - - if (val & ~0xff) - return -1; - *binary++ = val; - } - return 0; -} - static int get_hash_hex_algop(const char *hex, unsigned char *hash, const struct git_hash_algo *algop) { @@ -2,22 +2,7 @@ #define HEX_H #include "hash-ll.h" - -extern const signed char hexval_table[256]; -static inline unsigned int hexval(unsigned char c) -{ - return hexval_table[c]; -} - -/* - * Convert two consecutive hexadecimal digits into a char. Return a - * negative value on error. Don't run over the end of short strings. - */ -static inline int hex2chr(const char *s) -{ - unsigned int val = hexval(s[0]); - return (val & ~0xf) ? val : (val << 4) | hexval(s[1]); -} +#include "hex-ll.h" /* * Try to read a hash (specified by the_hash_algo) in hexadecimal @@ -35,13 +20,6 @@ int get_oid_hex(const char *hex, struct object_id *oid); int get_oid_hex_algop(const char *hex, struct object_id *oid, const struct git_hash_algo *algop); /* - * Read `len` pairs of hexadecimal digits from `hex` and write the - * values to `binary` as `len` bytes. Return 0 on success, or -1 if - * the input does not consist of hex digits). - */ -int hex_to_bytes(unsigned char *binary, const char *hex, size_t len); - -/* * Convert a binary hash in "unsigned char []" or an object name in * "struct object_id *" to its hex equivalent. The `_r` variant is reentrant, * and writes the NUL-terminated output to the buffer `out`, which must be at diff --git a/mailinfo.c b/mailinfo.c index 931505363c..a07d2da16d 100644 --- a/mailinfo.c +++ b/mailinfo.c @@ -1,7 +1,7 @@ #include "git-compat-util.h" #include "config.h" #include "gettext.h" -#include "hex.h" +#include "hex-ll.h" #include "utf8.h" #include "strbuf.h" #include "mailinfo.h" diff --git a/object-file.c b/object-file.c index 7dc0c4bfbb..7c7afe5793 100644 --- a/object-file.c +++ b/object-file.c @@ -2446,11 +2446,11 @@ static int index_core(struct index_state *istate, * binary blobs, they generally do not want to get any conversion, and * callers should avoid this code path when filters are requested. */ -static int index_stream(struct object_id *oid, int fd, size_t size, - enum object_type type, const char *path, - unsigned flags) +static int index_blob_stream(struct object_id *oid, int fd, size_t size, + const char *path, + unsigned flags) { - return index_bulk_checkin(oid, fd, size, type, path, flags); + return index_blob_bulk_checkin(oid, fd, size, path, flags); } int index_fd(struct index_state *istate, struct object_id *oid, @@ -2472,8 +2472,8 @@ int index_fd(struct index_state *istate, struct object_id *oid, ret = index_core(istate, oid, fd, xsize_t(st->st_size), type, path, flags); else - ret = index_stream(oid, fd, xsize_t(st->st_size), type, path, - flags); + ret = index_blob_stream(oid, fd, xsize_t(st->st_size), path, + flags); close(fd); return ret; } diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c index f6757c3cbf..f4ecdf8b0e 100644 --- a/pack-bitmap-write.c +++ b/pack-bitmap-write.c @@ -413,15 +413,19 @@ static int fill_bitmap_commit(struct bb_commit *ent, if (old_bitmap && mapping) { struct ewah_bitmap *old = bitmap_for_commit(old_bitmap, c); + struct bitmap *remapped = bitmap_new(); /* * If this commit has an old bitmap, then translate that * bitmap and add its bits to this one. No need to walk * parents or the tree for this commit. */ - if (old && !rebuild_bitmap(mapping, old, ent->bitmap)) { + if (old && !rebuild_bitmap(mapping, old, remapped)) { + bitmap_or(ent->bitmap, remapped); + bitmap_free(remapped); reused_bitmaps_nr++; continue; } + bitmap_free(remapped); } /* diff --git a/pack-objects.c b/pack-objects.c index 1b8052bece..f403ca6986 100644 --- a/pack-objects.c +++ b/pack-objects.c @@ -3,7 +3,7 @@ #include "pack.h" #include "pack-objects.h" #include "packfile.h" -#include "config.h" +#include "parse.h" static uint32_t locate_object_entry_hash(struct packing_data *pdata, const struct object_id *oid, diff --git a/pack-revindex.c b/pack-revindex.c index 7fffcad912..a01a2a4640 100644 --- a/pack-revindex.c +++ b/pack-revindex.c @@ -6,7 +6,7 @@ #include "packfile.h" #include "strbuf.h" #include "trace2.h" -#include "config.h" +#include "parse.h" #include "midx.h" #include "csum-file.h" diff --git a/parse-options.c b/parse-options.c index e8e076c3a6..093eaf2db8 100644 --- a/parse-options.c +++ b/parse-options.c @@ -1,11 +1,12 @@ #include "git-compat-util.h" #include "parse-options.h" #include "abspath.h" -#include "config.h" +#include "parse.h" #include "commit.h" #include "color.h" #include "gettext.h" #include "strbuf.h" +#include "string-list.h" #include "utf8.h" static int disallow_abbreviated_options; diff --git a/parse-options.h b/parse-options.h index 57a7fe9d91..4a66ec3bf5 100644 --- a/parse-options.h +++ b/parse-options.h @@ -459,7 +459,6 @@ struct parse_opt_ctx_t { unsigned has_subcommands; const char *prefix; const char **alias_groups; /* must be in groups of 3 elements! */ - struct option *updated_options; }; void parse_options_start(struct parse_opt_ctx_t *ctx, diff --git a/parse.c b/parse.c new file mode 100644 index 0000000000..42d691a0fb --- /dev/null +++ b/parse.c @@ -0,0 +1,182 @@ +#include "git-compat-util.h" +#include "gettext.h" +#include "parse.h" + +static uintmax_t get_unit_factor(const char *end) +{ + if (!*end) + return 1; + else if (!strcasecmp(end, "k")) + return 1024; + else if (!strcasecmp(end, "m")) + return 1024 * 1024; + else if (!strcasecmp(end, "g")) + return 1024 * 1024 * 1024; + return 0; +} + +int git_parse_signed(const char *value, intmax_t *ret, intmax_t max) +{ + if (value && *value) { + char *end; + intmax_t val; + intmax_t factor; + + if (max < 0) + BUG("max must be a positive integer"); + + errno = 0; + val = strtoimax(value, &end, 0); + if (errno == ERANGE) + return 0; + if (end == value) { + errno = EINVAL; + return 0; + } + factor = get_unit_factor(end); + if (!factor) { + errno = EINVAL; + return 0; + } + if ((val < 0 && -max / factor > val) || + (val > 0 && max / factor < val)) { + errno = ERANGE; + return 0; + } + val *= factor; + *ret = val; + return 1; + } + errno = EINVAL; + return 0; +} + +static int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max) +{ + if (value && *value) { + char *end; + uintmax_t val; + uintmax_t factor; + + /* negative values would be accepted by strtoumax */ + if (strchr(value, '-')) { + errno = EINVAL; + return 0; + } + errno = 0; + val = strtoumax(value, &end, 0); + if (errno == ERANGE) + return 0; + if (end == value) { + errno = EINVAL; + return 0; + } + factor = get_unit_factor(end); + if (!factor) { + errno = EINVAL; + return 0; + } + if (unsigned_mult_overflows(factor, val) || + factor * val > max) { + errno = ERANGE; + return 0; + } + val *= factor; + *ret = val; + return 1; + } + errno = EINVAL; + return 0; +} + +int git_parse_int(const char *value, int *ret) +{ + intmax_t tmp; + if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int))) + return 0; + *ret = tmp; + return 1; +} + +int git_parse_int64(const char *value, int64_t *ret) +{ + intmax_t tmp; + if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int64_t))) + return 0; + *ret = tmp; + return 1; +} + +int git_parse_ulong(const char *value, unsigned long *ret) +{ + uintmax_t tmp; + if (!git_parse_unsigned(value, &tmp, maximum_unsigned_value_of_type(long))) + return 0; + *ret = tmp; + return 1; +} + +int git_parse_ssize_t(const char *value, ssize_t *ret) +{ + intmax_t tmp; + if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(ssize_t))) + return 0; + *ret = tmp; + return 1; +} + +int git_parse_maybe_bool_text(const char *value) +{ + if (!value) + return 1; + if (!*value) + return 0; + if (!strcasecmp(value, "true") + || !strcasecmp(value, "yes") + || !strcasecmp(value, "on")) + return 1; + if (!strcasecmp(value, "false") + || !strcasecmp(value, "no") + || !strcasecmp(value, "off")) + return 0; + return -1; +} + +int git_parse_maybe_bool(const char *value) +{ + int v = git_parse_maybe_bool_text(value); + if (0 <= v) + return v; + if (git_parse_int(value, &v)) + return !!v; + return -1; +} + +/* + * Parse environment variable 'k' as a boolean (in various + * possible spellings); if missing, use the default value 'def'. + */ +int git_env_bool(const char *k, int def) +{ + const char *v = getenv(k); + int val; + if (!v) + return def; + val = git_parse_maybe_bool(v); + if (val < 0) + die(_("bad boolean environment value '%s' for '%s'"), + v, k); + return val; +} + +/* + * Parse environment variable 'k' as ulong with possibly a unit + * suffix; if missing, use the default value 'val'. + */ +unsigned long git_env_ulong(const char *k, unsigned long val) +{ + const char *v = getenv(k); + if (v && !git_parse_ulong(v, &val)) + die(_("failed to parse %s"), k); + return val; +} diff --git a/parse.h b/parse.h new file mode 100644 index 0000000000..07d2193d69 --- /dev/null +++ b/parse.h @@ -0,0 +1,20 @@ +#ifndef PARSE_H +#define PARSE_H + +int git_parse_signed(const char *value, intmax_t *ret, intmax_t max); +int git_parse_ssize_t(const char *, ssize_t *); +int git_parse_ulong(const char *, unsigned long *); +int git_parse_int(const char *value, int *ret); +int git_parse_int64(const char *value, int64_t *ret); + +/** + * Same as `git_config_bool`, except that it returns -1 on error rather + * than dying. + */ +int git_parse_maybe_bool(const char *); +int git_parse_maybe_bool_text(const char *value); + +int git_env_bool(const char *, int); +unsigned long git_env_ulong(const char *, unsigned long); + +#endif /* PARSE_H */ diff --git a/pathspec.c b/pathspec.c index 3a3a5724c4..7f88f1c02b 100644 --- a/pathspec.c +++ b/pathspec.c @@ -1,6 +1,6 @@ #include "git-compat-util.h" #include "abspath.h" -#include "config.h" +#include "parse.h" #include "dir.h" #include "environment.h" #include "gettext.h" diff --git a/preload-index.c b/preload-index.c index e44530c80c..63fd35d64b 100644 --- a/preload-index.c +++ b/preload-index.c @@ -7,7 +7,7 @@ #include "environment.h" #include "fsmonitor.h" #include "gettext.h" -#include "config.h" +#include "parse.h" #include "preload-index.h" #include "progress.h" #include "read-cache.h" diff --git a/progress.c b/progress.c index f695798aca..c83cb60bf1 100644 --- a/progress.c +++ b/progress.c @@ -17,7 +17,7 @@ #include "trace.h" #include "trace2.h" #include "utf8.h" -#include "config.h" +#include "parse.h" #define TP_IDX_MAX 8 @@ -1,5 +1,5 @@ #include "git-compat-util.h" -#include "config.h" +#include "parse.h" #include "environment.h" #include "run-command.h" #include "strbuf.h" @@ -1,6 +1,6 @@ #include "git-compat-util.h" #include "rebase.h" -#include "config.h" +#include "parse.h" #include "gettext.h" /* diff --git a/ref-filter.c b/ref-filter.c index fae9f4b8ed..e4d3510e28 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -13,6 +13,8 @@ #include "oid-array.h" #include "repository.h" #include "commit.h" +#include "mailmap.h" +#include "ident.h" #include "remote.h" #include "color.h" #include "tag.h" @@ -215,8 +217,16 @@ static struct used_atom { struct { enum { O_SIZE, O_SIZE_DISK } option; } objectsize; - struct email_option { - enum { EO_RAW, EO_TRIM, EO_LOCALPART } option; + struct { + enum { N_RAW, N_MAILMAP } option; + } name_option; + struct { + enum { + EO_RAW = 0, + EO_TRIM = 1<<0, + EO_LOCALPART = 1<<1, + EO_MAILMAP = 1<<2, + } option; } email_option; struct { enum { S_BARE, S_GRADE, S_SIGNER, S_KEY, @@ -720,21 +730,55 @@ static int oid_atom_parser(struct ref_format *format UNUSED, return 0; } -static int person_email_atom_parser(struct ref_format *format UNUSED, - struct used_atom *atom, - const char *arg, struct strbuf *err) +static int person_name_atom_parser(struct ref_format *format UNUSED, + struct used_atom *atom, + const char *arg, struct strbuf *err) { if (!arg) - atom->u.email_option.option = EO_RAW; - else if (!strcmp(arg, "trim")) - atom->u.email_option.option = EO_TRIM; - else if (!strcmp(arg, "localpart")) - atom->u.email_option.option = EO_LOCALPART; + atom->u.name_option.option = N_RAW; + else if (!strcmp(arg, "mailmap")) + atom->u.name_option.option = N_MAILMAP; else return err_bad_arg(err, atom->name, arg); return 0; } +static int email_atom_option_parser(struct used_atom *atom, + const char **arg, struct strbuf *err) +{ + if (!*arg) + return EO_RAW; + if (skip_prefix(*arg, "trim", arg)) + return EO_TRIM; + if (skip_prefix(*arg, "localpart", arg)) + return EO_LOCALPART; + if (skip_prefix(*arg, "mailmap", arg)) + return EO_MAILMAP; + return -1; +} + +static int person_email_atom_parser(struct ref_format *format UNUSED, + struct used_atom *atom, + const char *arg, struct strbuf *err) +{ + for (;;) { + int opt = email_atom_option_parser(atom, &arg, err); + const char *bad_arg = arg; + + if (opt < 0) + return err_bad_arg(err, atom->name, bad_arg); + atom->u.email_option.option |= opt; + + if (!arg || !*arg) + break; + if (*arg == ',') + arg++; + else + return err_bad_arg(err, atom->name, bad_arg); + } + return 0; +} + static int refname_atom_parser(struct ref_format *format UNUSED, struct used_atom *atom, const char *arg, struct strbuf *err) @@ -877,15 +921,15 @@ static struct { [ATOM_TYPE] = { "type", SOURCE_OBJ }, [ATOM_TAG] = { "tag", SOURCE_OBJ }, [ATOM_AUTHOR] = { "author", SOURCE_OBJ }, - [ATOM_AUTHORNAME] = { "authorname", SOURCE_OBJ }, + [ATOM_AUTHORNAME] = { "authorname", SOURCE_OBJ, FIELD_STR, person_name_atom_parser }, [ATOM_AUTHOREMAIL] = { "authoremail", SOURCE_OBJ, FIELD_STR, person_email_atom_parser }, [ATOM_AUTHORDATE] = { "authordate", SOURCE_OBJ, FIELD_TIME }, [ATOM_COMMITTER] = { "committer", SOURCE_OBJ }, - [ATOM_COMMITTERNAME] = { "committername", SOURCE_OBJ }, + [ATOM_COMMITTERNAME] = { "committername", SOURCE_OBJ, FIELD_STR, person_name_atom_parser }, [ATOM_COMMITTEREMAIL] = { "committeremail", SOURCE_OBJ, FIELD_STR, person_email_atom_parser }, [ATOM_COMMITTERDATE] = { "committerdate", SOURCE_OBJ, FIELD_TIME }, [ATOM_TAGGER] = { "tagger", SOURCE_OBJ }, - [ATOM_TAGGERNAME] = { "taggername", SOURCE_OBJ }, + [ATOM_TAGGERNAME] = { "taggername", SOURCE_OBJ, FIELD_STR, person_name_atom_parser }, [ATOM_TAGGEREMAIL] = { "taggeremail", SOURCE_OBJ, FIELD_STR, person_email_atom_parser }, [ATOM_TAGGERDATE] = { "taggerdate", SOURCE_OBJ, FIELD_TIME }, [ATOM_CREATOR] = { "creator", SOURCE_OBJ }, @@ -1486,32 +1530,49 @@ static const char *copy_name(const char *buf) return xstrdup(""); } +static const char *find_end_of_email(const char *email, int opt) +{ + const char *eoemail; + + if (opt & EO_LOCALPART) { + eoemail = strchr(email, '@'); + if (eoemail) + return eoemail; + return strchr(email, '>'); + } + + if (opt & EO_TRIM) + return strchr(email, '>'); + + /* + * The option here is either the raw email option or the raw + * mailmap option (that is EO_RAW or EO_MAILMAP). In such cases, + * we directly grab the whole email including the closing + * angle brackets. + * + * If EO_MAILMAP was set with any other option (that is either + * EO_TRIM or EO_LOCALPART), we already grab the end of email + * above. + */ + eoemail = strchr(email, '>'); + if (eoemail) + eoemail++; + return eoemail; +} + static const char *copy_email(const char *buf, struct used_atom *atom) { const char *email = strchr(buf, '<'); const char *eoemail; + int opt = atom->u.email_option.option; + if (!email) return xstrdup(""); - switch (atom->u.email_option.option) { - case EO_RAW: - eoemail = strchr(email, '>'); - if (eoemail) - eoemail++; - break; - case EO_TRIM: - email++; - eoemail = strchr(email, '>'); - break; - case EO_LOCALPART: + + if (opt & (EO_LOCALPART | EO_TRIM)) email++; - eoemail = strchr(email, '@'); - if (!eoemail) - eoemail = strchr(email, '>'); - break; - default: - BUG("unknown email option"); - } + eoemail = find_end_of_email(email, opt); if (!eoemail) return xstrdup(""); return xmemdupz(email, eoemail - email); @@ -1572,16 +1633,23 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam v->value = 0; } +static struct string_list mailmap = STRING_LIST_INIT_NODUP; + /* See grab_values */ static void grab_person(const char *who, struct atom_value *val, int deref, void *buf) { int i; int wholen = strlen(who); const char *wholine = NULL; + const char *headers[] = { "author ", "committer ", + "tagger ", NULL }; for (i = 0; i < used_atom_cnt; i++) { - const char *name = used_atom[i].name; + struct used_atom *atom = &used_atom[i]; + const char *name = atom->name; struct atom_value *v = &val[i]; + struct strbuf mailmap_buf = STRBUF_INIT; + if (!!deref != (*name == '*')) continue; if (deref) @@ -1589,22 +1657,36 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void if (strncmp(who, name, wholen)) continue; if (name[wholen] != 0 && - strcmp(name + wholen, "name") && + !starts_with(name + wholen, "name") && !starts_with(name + wholen, "email") && !starts_with(name + wholen, "date")) continue; - if (!wholine) + + if ((starts_with(name + wholen, "name") && + (atom->u.name_option.option == N_MAILMAP)) || + (starts_with(name + wholen, "email") && + (atom->u.email_option.option & EO_MAILMAP))) { + if (!mailmap.items) + read_mailmap(&mailmap); + strbuf_addstr(&mailmap_buf, buf); + apply_mailmap_to_header(&mailmap_buf, headers, &mailmap); + wholine = find_wholine(who, wholen, mailmap_buf.buf); + } else { wholine = find_wholine(who, wholen, buf); + } + if (!wholine) return; /* no point looking for it */ if (name[wholen] == 0) v->s = copy_line(wholine); - else if (!strcmp(name + wholen, "name")) + else if (starts_with(name + wholen, "name")) v->s = copy_name(wholine); else if (starts_with(name + wholen, "email")) v->s = copy_email(wholine, &used_atom[i]); else if (starts_with(name + wholen, "date")) grab_date(wholine, v, name); + + strbuf_release(&mailmap_buf); } /* diff --git a/revision.c b/revision.c index 49d385257a..e789834dd1 100644 --- a/revision.c +++ b/revision.c @@ -2790,13 +2790,13 @@ static int handle_revision_pseudo_opt(struct rev_info *revs, } static void read_revisions_from_stdin(struct rev_info *revs, - struct strvec *prune, - int *flags) + struct strvec *prune) { struct strbuf sb; int seen_dashdash = 0; int seen_end_of_options = 0; int save_warning; + int flags = 0; save_warning = warn_on_object_refname_ambiguity; warn_on_object_refname_ambiguity = 0; @@ -2819,13 +2819,13 @@ static void read_revisions_from_stdin(struct rev_info *revs, continue; } - if (handle_revision_pseudo_opt(revs, argv, flags) > 0) + if (handle_revision_pseudo_opt(revs, argv, &flags) > 0) continue; die(_("invalid option '%s' in --stdin mode"), sb.buf); } - if (handle_revision_arg(sb.buf, revs, 0, + if (handle_revision_arg(sb.buf, revs, flags, REVARG_CANNOT_BE_FILENAME)) die("bad revision '%s'", sb.buf); } @@ -2908,7 +2908,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s } if (revs->read_from_stdin++) die("--stdin given twice?"); - read_revisions_from_stdin(revs, &prune_data, &flags); + read_revisions_from_stdin(revs, &prune_data); continue; } @@ -1,6 +1,6 @@ #include "git-compat-util.h" #include "gettext.h" -#include "hex.h" +#include "hex-ll.h" #include "strbuf.h" #include "string-list.h" #include "utf8.h" diff --git a/t/helper/test-env-helper.c b/t/helper/test-env-helper.c index 66c88b8ff3..1c486888a4 100644 --- a/t/helper/test-env-helper.c +++ b/t/helper/test-env-helper.c @@ -1,5 +1,5 @@ #include "test-tool.h" -#include "config.h" +#include "parse.h" #include "parse-options.h" static char const * const env__helper_usage[] = { diff --git a/t/helper/test-find-pack.c b/t/helper/test-find-pack.c new file mode 100644 index 0000000000..e8bd793e58 --- /dev/null +++ b/t/helper/test-find-pack.c @@ -0,0 +1,50 @@ +#include "test-tool.h" +#include "object-name.h" +#include "object-store.h" +#include "packfile.h" +#include "parse-options.h" +#include "setup.h" + +/* + * Display the path(s), one per line, of the packfile(s) containing + * the given object. + * + * If '--check-count <n>' is passed, then error out if the number of + * packfiles containing the object is not <n>. + */ + +static const char *find_pack_usage[] = { + "test-tool find-pack [--check-count <n>] <object>", + NULL +}; + +int cmd__find_pack(int argc, const char **argv) +{ + struct object_id oid; + struct packed_git *p; + int count = -1, actual_count = 0; + const char *prefix = setup_git_directory(); + + struct option options[] = { + OPT_INTEGER('c', "check-count", &count, "expected number of packs"), + OPT_END(), + }; + + argc = parse_options(argc, argv, prefix, options, find_pack_usage, 0); + if (argc != 1) + usage(find_pack_usage[0]); + + if (repo_get_oid(the_repository, argv[0], &oid)) + die("cannot parse %s as an object name", argv[0]); + + for (p = get_all_packs(the_repository); p; p = p->next) + if (find_pack_entry_one(oid.hash, p)) { + printf("%s\n", p->pack_name); + actual_count++; + } + + if (count > -1 && count != actual_count) + die("bad packfile count %d instead of %d", actual_count, count); + + return 0; +} diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c index 621ac3dd10..9010ac6de7 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -31,6 +31,7 @@ static struct test_cmd cmds[] = { { "env-helper", cmd__env_helper }, { "example-decorate", cmd__example_decorate }, { "fast-rebase", cmd__fast_rebase }, + { "find-pack", cmd__find_pack }, { "fsmonitor-client", cmd__fsmonitor_client }, { "genrandom", cmd__genrandom }, { "genzeros", cmd__genzeros }, diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h index a641c3a81d..f134f96b97 100644 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@ -25,6 +25,7 @@ int cmd__dump_reftable(int argc, const char **argv); int cmd__env_helper(int argc, const char **argv); int cmd__example_decorate(int argc, const char **argv); int cmd__fast_rebase(int argc, const char **argv); +int cmd__find_pack(int argc, const char **argv); int cmd__fsmonitor_client(int argc, const char **argv); int cmd__genrandom(int argc, const char **argv); int cmd__genzeros(int argc, const char **argv); diff --git a/t/t0081-find-pack.sh b/t/t0081-find-pack.sh new file mode 100755 index 0000000000..67b11216a3 --- /dev/null +++ b/t/t0081-find-pack.sh @@ -0,0 +1,82 @@ +#!/bin/sh + +test_description='test `test-tool find-pack`' + +TEST_PASSES_SANITIZE_LEAK=true +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit one && + test_commit two && + test_commit three && + test_commit four && + test_commit five +' + +test_expect_success 'repack everything into a single packfile' ' + git repack -a -d --no-write-bitmap-index && + + head_commit_pack=$(test-tool find-pack HEAD) && + head_tree_pack=$(test-tool find-pack HEAD^{tree}) && + one_pack=$(test-tool find-pack HEAD:one.t) && + three_pack=$(test-tool find-pack HEAD:three.t) && + old_commit_pack=$(test-tool find-pack HEAD~4) && + + test-tool find-pack --check-count 1 HEAD && + test-tool find-pack --check-count=1 HEAD^{tree} && + ! test-tool find-pack --check-count=0 HEAD:one.t && + ! test-tool find-pack -c 2 HEAD:one.t && + test-tool find-pack -c 1 HEAD:three.t && + + # Packfile exists at the right path + case "$head_commit_pack" in + ".git/objects/pack/pack-"*".pack") true ;; + *) false ;; + esac && + test -f "$head_commit_pack" && + + # Everything is in the same pack + test "$head_commit_pack" = "$head_tree_pack" && + test "$head_commit_pack" = "$one_pack" && + test "$head_commit_pack" = "$three_pack" && + test "$head_commit_pack" = "$old_commit_pack" +' + +test_expect_success 'add more packfiles' ' + git rev-parse HEAD^{tree} HEAD:two.t HEAD:four.t >objects && + git pack-objects .git/objects/pack/mypackname1 >packhash1 <objects && + + git rev-parse HEAD~ HEAD~^{tree} HEAD:five.t >objects && + git pack-objects .git/objects/pack/mypackname2 >packhash2 <objects && + + head_commit_pack=$(test-tool find-pack HEAD) && + + # HEAD^{tree} is in 2 packfiles + test-tool find-pack HEAD^{tree} >head_tree_packs && + grep "$head_commit_pack" head_tree_packs && + grep mypackname1 head_tree_packs && + ! grep mypackname2 head_tree_packs && + test-tool find-pack --check-count 2 HEAD^{tree} && + ! test-tool find-pack --check-count 1 HEAD^{tree} && + + # HEAD:five.t is also in 2 packfiles + test-tool find-pack HEAD:five.t >five_packs && + grep "$head_commit_pack" five_packs && + ! grep mypackname1 five_packs && + grep mypackname2 five_packs && + test-tool find-pack -c 2 HEAD:five.t && + ! test-tool find-pack --check-count=0 HEAD:five.t +' + +test_expect_success 'add more commits (as loose objects)' ' + test_commit six && + test_commit seven && + + test -z "$(test-tool find-pack HEAD)" && + test -z "$(test-tool find-pack HEAD:six.t)" && + test-tool find-pack --check-count 0 HEAD && + test-tool find-pack -c 0 HEAD:six.t && + ! test-tool find-pack -c 1 HEAD:seven.t +' + +test_done diff --git a/t/t4052-stat-output.sh b/t/t4052-stat-output.sh index beb2ec2a55..7badd72488 100755 --- a/t/t4052-stat-output.sh +++ b/t/t4052-stat-output.sh @@ -12,7 +12,7 @@ TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh . "$TEST_DIRECTORY"/lib-terminal.sh -# 120 character name +# 120-character name name=aaaaaaaaaa name=$name$name$name$name$name$name$name$name$name$name$name$name test_expect_success 'preparation' ' @@ -58,7 +58,7 @@ 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" ' + test_expect_success "$cmd $verb diff.statNameWidth with long name" ' git -c diff.statNameWidth=30 $cmd $args >output && grep " | " output >actual && test_cmp $expect actual @@ -66,7 +66,7 @@ do 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" ' + test_expect_success "$cmd --stat=width $verb diff.statNameWidth with long name" ' git -c diff.statNameWidth=30 $cmd $args --stat=60 >output && grep " | " output >actual && test_cmp $expect actual @@ -111,7 +111,7 @@ do test_cmp $expect.6030 actual ' - test_expect_success "$cmd --stat-name-width with long name" ' + test_expect_success "$cmd --stat-name-width=width with long name" ' git $cmd $args --stat-name-width=30 >output && grep " | " output >actual && test_cmp $expect.6030 actual @@ -123,7 +123,7 @@ expect show --stat expect log -1 --stat EOF -test_expect_success 'preparation for big change tests' ' +test_expect_success 'preparation for big-change tests' ' >abcd && git add abcd && git commit -m message && @@ -139,7 +139,7 @@ cat >expect72 <<'EOF' abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ EOF -test_expect_success "format-patch --cover-letter ignores COLUMNS (big change)" ' +test_expect_success "format-patch --cover-letter ignores COLUMNS with big change" ' COLUMNS=200 git format-patch -1 --stdout --cover-letter >output && grep " | " output >actual && test_cmp expect72 actual @@ -159,7 +159,7 @@ cat >expect200-graph <<'EOF' EOF while read verb expect cmd args do - test_expect_success "$cmd $verb COLUMNS (big change)" ' + test_expect_success "$cmd $verb COLUMNS with big change" ' COLUMNS=200 git $cmd $args >output && grep " | " output >actual && test_cmp "$expect" actual @@ -167,7 +167,7 @@ do case "$cmd" in diff|show) continue;; esac - test_expect_success "$cmd --graph $verb COLUMNS (big change)" ' + test_expect_success "$cmd --graph $verb COLUMNS with big change" ' COLUMNS=200 git $cmd $args --graph >output && grep " | " output >actual && test_cmp "$expect-graph" actual @@ -187,7 +187,7 @@ cat >expect40-graph <<'EOF' EOF while read verb expect cmd args do - test_expect_success "$cmd $verb not enough COLUMNS (big change)" ' + test_expect_success "$cmd $verb not enough COLUMNS with big change" ' COLUMNS=40 git $cmd $args >output && grep " | " output >actual && test_cmp "$expect" actual @@ -195,7 +195,7 @@ do case "$cmd" in diff|show) continue;; esac - test_expect_success "$cmd --graph $verb not enough COLUMNS (big change)" ' + test_expect_success "$cmd --graph $verb not enough COLUMNS with big change" ' COLUMNS=40 git $cmd $args --graph >output && grep " | " output >actual && test_cmp "$expect-graph" actual @@ -215,7 +215,7 @@ cat >expect40-graph <<'EOF' EOF while read verb expect cmd args do - test_expect_success "$cmd $verb statGraphWidth config" ' + test_expect_success "$cmd $verb diff.statGraphWidth" ' git -c diff.statGraphWidth=26 $cmd $args >output && grep " | " output >actual && test_cmp "$expect" actual @@ -223,7 +223,7 @@ do case "$cmd" in diff|show) continue;; esac - test_expect_success "$cmd --graph $verb statGraphWidth config" ' + test_expect_success "$cmd --graph $verb diff.statGraphWidth" ' git -c diff.statGraphWidth=26 $cmd $args --graph >output && grep " | " output >actual && test_cmp "$expect-graph" actual @@ -255,7 +255,7 @@ do test_cmp expect actual ' - test_expect_success "$cmd --stat-graph-width with big change" ' + test_expect_success "$cmd --stat-graph-width=width with big change" ' git $cmd $args --stat-graph-width=26 >output && grep " | " output >actual && test_cmp expect actual @@ -269,7 +269,7 @@ do test_cmp expect-graph actual ' - test_expect_success "$cmd --stat-graph-width --graph with big change" ' + test_expect_success "$cmd --stat-graph-width=width --graph with big change" ' git $cmd $args --stat-graph-width=26 --graph >output && grep " | " output >actual && test_cmp expect-graph actual @@ -281,7 +281,7 @@ show --stat log -1 --stat EOF -test_expect_success 'preparation for long filename tests' ' +test_expect_success 'preparation for long-name tests' ' cp abcd aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa && git add aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa && git commit -m message @@ -329,7 +329,7 @@ cat >expect200-graph <<'EOF' EOF while read verb expect cmd args do - test_expect_success "$cmd $verb COLUMNS (long filename)" ' + test_expect_success "$cmd $verb COLUMNS with long name" ' COLUMNS=200 git $cmd $args >output && grep " | " output >actual && test_cmp "$expect" actual @@ -337,7 +337,7 @@ do case "$cmd" in diff|show) continue;; esac - test_expect_success "$cmd --graph $verb COLUMNS (long filename)" ' + test_expect_success "$cmd --graph $verb COLUMNS with long name" ' COLUMNS=200 git $cmd $args --graph >output && grep " | " output >actual && test_cmp "$expect-graph" actual @@ -358,7 +358,7 @@ EOF while read verb expect cmd args do test_expect_success COLUMNS_CAN_BE_1 \ - "$cmd $verb prefix greater than COLUMNS (big change)" ' + "$cmd $verb prefix greater than COLUMNS with big change" ' COLUMNS=1 git $cmd $args >output && grep " | " output >actual && test_cmp "$expect" actual @@ -367,7 +367,7 @@ do case "$cmd" in diff|show) continue;; esac test_expect_success COLUMNS_CAN_BE_1 \ - "$cmd --graph $verb prefix greater than COLUMNS (big change)" ' + "$cmd --graph $verb prefix greater than COLUMNS with big change" ' COLUMNS=1 git $cmd $args --graph >output && grep " | " output >actual && test_cmp "$expect-graph" actual @@ -382,8 +382,14 @@ EOF cat >expect <<'EOF' abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ EOF -test_expect_success 'merge --stat respects COLUMNS (big change)' ' - git checkout -b branch HEAD^^ && +test_expect_success 'merge --stat respects diff.statGraphWidth with big change' ' + git checkout -b branch1 HEAD^^ && + git -c diff.statGraphWidth=26 merge --stat --no-ff main^ >output && + grep " | " output >actual && + test_cmp expect40 actual +' +test_expect_success 'merge --stat respects COLUMNS with big change' ' + git checkout -b branch2 HEAD^^ && COLUMNS=100 git merge --stat --no-ff main^ >output && grep " | " output >actual && test_cmp expect actual @@ -392,7 +398,17 @@ test_expect_success 'merge --stat respects COLUMNS (big change)' ' cat >expect <<'EOF' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++ EOF -test_expect_success 'merge --stat respects COLUMNS (long filename)' ' +cat >expect.30 <<'EOF' + ...aaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++++++++++++++++++++++ +EOF +test_expect_success 'merge --stat respects diff.statNameWidth with long name' ' + git switch branch1 && + git -c diff.statNameWidth=30 merge --stat --no-ff main >output && + grep " | " output >actual && + test_cmp expect.30 actual +' +test_expect_success 'merge --stat respects COLUMNS with long name' ' + git switch branch2 && COLUMNS=100 git merge --stat --no-ff main >output && grep " | " output >actual && test_cmp expect actual diff --git a/t/t5317-pack-objects-filter-objects.sh b/t/t5317-pack-objects-filter-objects.sh index b26d476c64..2ff3eef9a3 100755 --- a/t/t5317-pack-objects-filter-objects.sh +++ b/t/t5317-pack-objects-filter-objects.sh @@ -53,6 +53,14 @@ test_expect_success 'verify blob:none packfile has no blobs' ' ! grep blob verify_result ' +test_expect_success 'verify blob:none packfile without --stdout' ' + git -C r1 pack-objects --revs --filter=blob:none mypackname >packhash <<-EOF && + HEAD + EOF + git -C r1 verify-pack -v "mypackname-$(cat packhash).pack" >verify_result && + ! grep blob verify_result +' + test_expect_success 'verify normal and blob:none packfiles have same commits/trees' ' git -C r1 verify-pack -v ../all.pack >verify_result && grep -E "commit|tree" verify_result | diff --git a/t/t5324-split-commit-graph.sh b/t/t5324-split-commit-graph.sh index 52e8a3e619..8a9720dcb0 100755 --- a/t/t5324-split-commit-graph.sh +++ b/t/t5324-split-commit-graph.sh @@ -287,6 +287,32 @@ test_expect_success 'verify hashes along chain, even in shallow' ' ) ' +test_expect_success 'verify notices chain slice which is bogus (base)' ' + git clone --no-hardlinks . verify-chain-bogus-base && + ( + cd verify-chain-bogus-base && + git commit-graph verify && + base_file=$graphdir/graph-$(sed -n 1p $graphdir/commit-graph-chain).graph && + echo "garbage" >$base_file && + test_must_fail git commit-graph verify 2>test_err && + grep -v "^+" test_err >err && + grep "commit-graph file is too small" err + ) +' + +test_expect_success 'verify notices chain slice which is bogus (tip)' ' + git clone --no-hardlinks . verify-chain-bogus-tip && + ( + cd verify-chain-bogus-tip && + git commit-graph verify && + tip_file=$graphdir/graph-$(sed -n 2p $graphdir/commit-graph-chain).graph && + echo "garbage" >$tip_file && + test_must_fail git commit-graph verify 2>test_err && + grep -v "^+" test_err >err && + grep "commit-graph file is too small" err + ) +' + test_expect_success 'verify --shallow does not check base contents' ' git clone --no-hardlinks . verify-shallow && ( @@ -308,27 +334,54 @@ test_expect_success 'warn on base graph chunk incorrect' ' git commit-graph verify && base_file=$graphdir/graph-$(tail -n 1 $graphdir/commit-graph-chain).graph && corrupt_file "$base_file" $(test_oid base) "\01" && - git commit-graph verify --shallow 2>test_err && + test_must_fail git commit-graph verify --shallow 2>test_err && grep -v "^+" test_err >err && test_i18ngrep "commit-graph chain does not match" err ) ' -test_expect_success 'verify after commit-graph-chain corruption' ' - git clone --no-hardlinks . verify-chain && +test_expect_success 'verify after commit-graph-chain corruption (base)' ' + git clone --no-hardlinks . verify-chain-base && + ( + cd verify-chain-base && + corrupt_file "$graphdir/commit-graph-chain" 30 "G" && + test_must_fail git commit-graph verify 2>test_err && + grep -v "^+" test_err >err && + test_i18ngrep "invalid commit-graph chain" err && + corrupt_file "$graphdir/commit-graph-chain" 30 "A" && + test_must_fail git commit-graph verify 2>test_err && + grep -v "^+" test_err >err && + test_i18ngrep "unable to find all commit-graph files" err + ) +' + +test_expect_success 'verify after commit-graph-chain corruption (tip)' ' + git clone --no-hardlinks . verify-chain-tip && ( - cd verify-chain && - corrupt_file "$graphdir/commit-graph-chain" 60 "G" && - git commit-graph verify 2>test_err && + cd verify-chain-tip && + corrupt_file "$graphdir/commit-graph-chain" 70 "G" && + test_must_fail git commit-graph verify 2>test_err && grep -v "^+" test_err >err && test_i18ngrep "invalid commit-graph chain" err && - corrupt_file "$graphdir/commit-graph-chain" 60 "A" && - git commit-graph verify 2>test_err && + corrupt_file "$graphdir/commit-graph-chain" 70 "A" && + test_must_fail git commit-graph verify 2>test_err && grep -v "^+" test_err >err && test_i18ngrep "unable to find all commit-graph files" err ) ' +test_expect_success 'verify notices too-short chain file' ' + git clone --no-hardlinks . verify-chain-short && + ( + cd verify-chain-short && + git commit-graph verify && + echo "garbage" >$graphdir/commit-graph-chain && + test_must_fail git commit-graph verify 2>test_err && + grep -v "^+" test_err >err && + grep "commit-graph chain file too small" err + ) +' + test_expect_success 'verify across alternates' ' git clone --no-hardlinks . verify-alt && ( diff --git a/t/t6017-rev-list-stdin.sh b/t/t6017-rev-list-stdin.sh index a57f1ae2ba..4821b90e74 100755 --- a/t/t6017-rev-list-stdin.sh +++ b/t/t6017-rev-list-stdin.sh @@ -68,6 +68,7 @@ check --glob=refs/heads check --glob=refs/heads -- check --glob=refs/heads -- file-1 check --end-of-options -dashed-branch +check --all --not refs/heads/main test_expect_success 'not only --stdin' ' cat >expect <<-EOF && @@ -127,4 +128,24 @@ test_expect_success 'unknown option without --end-of-options' ' test_cmp expect error ' +test_expect_success '--not on command line does not influence revisions read via --stdin' ' + cat >input <<-EOF && + refs/heads/main + EOF + git rev-list refs/heads/main >expect && + + git rev-list refs/heads/main --not --stdin <input >actual && + test_cmp expect actual +' + +test_expect_success '--not via stdin does not influence revisions from command line' ' + cat >input <<-EOF && + --not + EOF + git rev-list refs/heads/main >expect && + + git rev-list refs/heads/main --stdin refs/heads/main <input >actual && + test_cmp expect actual +' + test_done diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index 7b943fd34c..00a060df0b 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -25,6 +25,13 @@ test_expect_success setup ' disklen sha1:138 disklen sha256:154 EOF + + # setup .mailmap + cat >.mailmap <<-EOF && + A Thor <athor@example.com> A U Thor <author@example.com> + C Mitter <cmitter@example.com> C O Mitter <committer@example.com> + EOF + setdate_and_increment && echo "Using $datestamp" > one && git add one && @@ -41,25 +48,29 @@ test_expect_success setup ' git config push.default current ' -test_atom() { +test_atom () { case "$1" in head) ref=refs/heads/main ;; tag) ref=refs/tags/testtag ;; sym) ref=refs/heads/sym ;; *) ref=$1 ;; esac + format=$2 + test_do=test_expect_${4:-success} + printf '%s\n' "$3" >expected - test_expect_${4:-success} $PREREQ "basic atom: $1 $2" " - git for-each-ref --format='%($2)' $ref >actual && + $test_do $PREREQ "basic atom: $ref $format" ' + git for-each-ref --format="%($format)" "$ref" >actual && sanitize_pgp <actual >actual.clean && test_cmp expected actual.clean - " + ' + # Automatically test "contents:size" atom after testing "contents" - if test "$2" = "contents" + if test "$format" = "contents" then # for commit leg, $3 is changed there expect=$(printf '%s' "$3" | wc -c) - test_expect_${4:-success} $PREREQ "basic atom: $1 contents:size" ' + $test_do $PREREQ "basic atom: $ref contents:size" ' type=$(git cat-file -t "$ref") && case $type in tag) @@ -141,15 +152,31 @@ test_atom head '*objectname' '' test_atom head '*objecttype' '' test_atom head author 'A U Thor <author@example.com> 1151968724 +0200' test_atom head authorname 'A U Thor' +test_atom head authorname:mailmap 'A Thor' test_atom head authoremail '<author@example.com>' test_atom head authoremail:trim 'author@example.com' test_atom head authoremail:localpart 'author' +test_atom head authoremail:trim,localpart 'author' +test_atom head authoremail:mailmap '<athor@example.com>' +test_atom head authoremail:mailmap,trim 'athor@example.com' +test_atom head authoremail:trim,mailmap 'athor@example.com' +test_atom head authoremail:mailmap,localpart 'athor' +test_atom head authoremail:localpart,mailmap 'athor' +test_atom head authoremail:mailmap,trim,localpart,mailmap,trim 'athor' test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200' test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200' test_atom head committername 'C O Mitter' +test_atom head committername:mailmap 'C Mitter' test_atom head committeremail '<committer@example.com>' test_atom head committeremail:trim 'committer@example.com' test_atom head committeremail:localpart 'committer' +test_atom head committeremail:localpart,trim 'committer' +test_atom head committeremail:mailmap '<cmitter@example.com>' +test_atom head committeremail:mailmap,trim 'cmitter@example.com' +test_atom head committeremail:trim,mailmap 'cmitter@example.com' +test_atom head committeremail:mailmap,localpart 'cmitter' +test_atom head committeremail:localpart,mailmap 'cmitter' +test_atom head committeremail:trim,mailmap,trim,trim,localpart 'cmitter' test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200' test_atom head tag '' test_atom head tagger '' @@ -199,22 +226,46 @@ test_atom tag '*objectname' $(git rev-parse refs/tags/testtag^{}) test_atom tag '*objecttype' 'commit' test_atom tag author '' test_atom tag authorname '' +test_atom tag authorname:mailmap '' test_atom tag authoremail '' test_atom tag authoremail:trim '' test_atom tag authoremail:localpart '' +test_atom tag authoremail:trim,localpart '' +test_atom tag authoremail:mailmap '' +test_atom tag authoremail:mailmap,trim '' +test_atom tag authoremail:trim,mailmap '' +test_atom tag authoremail:mailmap,localpart '' +test_atom tag authoremail:localpart,mailmap '' +test_atom tag authoremail:mailmap,trim,localpart,mailmap,trim '' test_atom tag authordate '' test_atom tag committer '' test_atom tag committername '' +test_atom tag committername:mailmap '' test_atom tag committeremail '' test_atom tag committeremail:trim '' test_atom tag committeremail:localpart '' +test_atom tag committeremail:localpart,trim '' +test_atom tag committeremail:mailmap '' +test_atom tag committeremail:mailmap,trim '' +test_atom tag committeremail:trim,mailmap '' +test_atom tag committeremail:mailmap,localpart '' +test_atom tag committeremail:localpart,mailmap '' +test_atom tag committeremail:trim,mailmap,trim,trim,localpart '' test_atom tag committerdate '' test_atom tag tag 'testtag' test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200' test_atom tag taggername 'C O Mitter' +test_atom tag taggername:mailmap 'C Mitter' test_atom tag taggeremail '<committer@example.com>' test_atom tag taggeremail:trim 'committer@example.com' test_atom tag taggeremail:localpart 'committer' +test_atom tag taggeremail:trim,localpart 'committer' +test_atom tag taggeremail:mailmap '<cmitter@example.com>' +test_atom tag taggeremail:mailmap,trim 'cmitter@example.com' +test_atom tag taggeremail:trim,mailmap 'cmitter@example.com' +test_atom tag taggeremail:mailmap,localpart 'cmitter' +test_atom tag taggeremail:localpart,mailmap 'cmitter' +test_atom tag taggeremail:trim,mailmap,trim,localpart,localpart 'cmitter' test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200' test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200' test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200' @@ -267,6 +318,66 @@ test_expect_success 'arguments to %(objectname:short=) must be positive integers test_must_fail git for-each-ref --format="%(objectname:short=foo)" ' +test_bad_atom () { + case "$1" in + head) ref=refs/heads/main ;; + tag) ref=refs/tags/testtag ;; + sym) ref=refs/heads/sym ;; + *) ref=$1 ;; + esac + format=$2 + test_do=test_expect_${4:-success} + + printf '%s\n' "$3" >expect + $test_do $PREREQ "err basic atom: $ref $format" ' + test_must_fail git for-each-ref \ + --format="%($format)" "$ref" 2>error && + test_cmp expect error + ' +} + +test_bad_atom head 'authoremail:foo' \ + 'fatal: unrecognized %(authoremail) argument: foo' + +test_bad_atom head 'authoremail:mailmap,trim,bar' \ + 'fatal: unrecognized %(authoremail) argument: bar' + +test_bad_atom head 'authoremail:trim,' \ + 'fatal: unrecognized %(authoremail) argument: ' + +test_bad_atom head 'authoremail:mailmaptrim' \ + 'fatal: unrecognized %(authoremail) argument: trim' + +test_bad_atom head 'committeremail: ' \ + 'fatal: unrecognized %(committeremail) argument: ' + +test_bad_atom head 'committeremail: trim,foo' \ + 'fatal: unrecognized %(committeremail) argument: trim,foo' + +test_bad_atom head 'committeremail:mailmap,localpart ' \ + 'fatal: unrecognized %(committeremail) argument: ' + +test_bad_atom head 'committeremail:trim_localpart' \ + 'fatal: unrecognized %(committeremail) argument: _localpart' + +test_bad_atom head 'committeremail:localpart,,,trim' \ + 'fatal: unrecognized %(committeremail) argument: ,,trim' + +test_bad_atom tag 'taggeremail:mailmap,trim, foo ' \ + 'fatal: unrecognized %(taggeremail) argument: foo ' + +test_bad_atom tag 'taggeremail:trim,localpart,' \ + 'fatal: unrecognized %(taggeremail) argument: ' + +test_bad_atom tag 'taggeremail:mailmap;localpart trim' \ + 'fatal: unrecognized %(taggeremail) argument: ;localpart trim' + +test_bad_atom tag 'taggeremail:localpart trim' \ + 'fatal: unrecognized %(taggeremail) argument: trim' + +test_bad_atom tag 'taggeremail:mailmap,mailmap,trim,qux,localpart,trim' \ + 'fatal: unrecognized %(taggeremail) argument: qux,localpart,trim' + test_date () { f=$1 && committer_date=$2 && diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh index 69509d0c11..e412cf8daf 100755 --- a/t/t6500-gc.sh +++ b/t/t6500-gc.sh @@ -202,6 +202,30 @@ test_expect_success 'one of gc.reflogExpire{Unreachable,}=never does not skip "e grep -E "^trace: (built-in|exec|run_command): git reflog expire --" trace.out ' +test_expect_success 'gc.repackFilter launches repack with a filter' ' + git clone --no-local --bare . bare.git && + + git -C bare.git -c gc.cruftPacks=false gc && + test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack && + + GIT_TRACE=$(pwd)/trace.out git -C bare.git -c gc.repackFilter=blob:none \ + -c repack.writeBitmaps=false -c gc.cruftPacks=false gc && + test_stdout_line_count = 2 ls bare.git/objects/pack/*.pack && + grep -E "^trace: (built-in|exec|run_command): git repack .* --filter=blob:none ?.*" trace.out +' + +test_expect_success 'gc.repackFilterTo store filtered out objects' ' + test_when_finished "rm -rf bare.git filtered.git" && + + git init --bare filtered.git && + git -C bare.git -c gc.repackFilter=blob:none \ + -c gc.repackFilterTo=../filtered.git/objects/pack/pack \ + -c repack.writeBitmaps=false -c gc.cruftPacks=false gc && + + test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack && + test_stdout_line_count = 1 ls filtered.git/objects/pack/*.pack +' + prepare_cruft_history () { test_commit base && diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh index 97f10905d2..832aff0616 100755 --- a/t/t7513-interpret-trailers.sh +++ b/t/t7513-interpret-trailers.sh @@ -489,7 +489,7 @@ test_expect_success 'multiline field treated as atomic for neighbor check' ' ' test_expect_success 'with config setup' ' - git config trailer.ack.key "Acked-by: " && + test_config trailer.ack.key "Acked-by: " && cat >expected <<-\EOF && Acked-by: Peff @@ -503,8 +503,8 @@ test_expect_success 'with config setup' ' ' test_expect_success 'with config setup and ":=" as separators' ' - git config trailer.separators ":=" && - git config trailer.ack.key "Acked-by= " && + test_config trailer.separators ":=" && + test_config trailer.ack.key "Acked-by= " && cat >expected <<-\EOF && Acked-by= Peff @@ -518,7 +518,7 @@ test_expect_success 'with config setup and ":=" as separators' ' ' test_expect_success 'with config setup and "%" as separators' ' - git config trailer.separators "%" && + test_config trailer.separators "%" && cat >expected <<-\EOF && bug% 42 @@ -532,6 +532,7 @@ test_expect_success 'with config setup and "%" as separators' ' ' test_expect_success 'with "%" as separators and a message with trailers' ' + test_config trailer.separators "%" && cat >special_message <<-\EOF && Special Message @@ -553,8 +554,8 @@ test_expect_success 'with "%" as separators and a message with trailers' ' ' test_expect_success 'with config setup and ":=#" as separators' ' - git config trailer.separators ":=#" && - git config trailer.bug.key "Bug #" && + test_config trailer.separators ":=#" && + test_config trailer.bug.key "Bug #" && cat >expected <<-\EOF && Bug #42 @@ -581,6 +582,8 @@ test_expect_success 'with basic patch' ' ' test_expect_success 'with commit complex message as argument' ' + test_config trailer.separators ":=" && + test_config trailer.ack.key "Acked-by= " && cat complex_message_body complex_message_trailers >complex_message && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && @@ -594,6 +597,8 @@ test_expect_success 'with commit complex message as argument' ' ' test_expect_success 'with 2 files arguments' ' + test_config trailer.separators ":=" && + test_config trailer.ack.key "Acked-by= " && cat basic_message >>expected && echo >>expected && cat basic_patch >>expected && @@ -677,6 +682,9 @@ test_expect_success 'with message that has an old style conflict block' ' ' test_expect_success 'with commit complex message and trailer args' ' + test_config trailer.separators ":=#" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.bug.key "Bug #" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Fixes: Z @@ -692,6 +700,9 @@ test_expect_success 'with commit complex message and trailer args' ' ' test_expect_success 'with complex patch, args and --trim-empty' ' + test_config trailer.separators ":=#" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.bug.key "Bug #" && cat complex_message >complex_patch && cat basic_patch >>complex_patch && cat complex_message_body >expected && @@ -746,7 +757,10 @@ test_expect_success POSIXPERM,SANITY "in-place editing doesn't clobber original ' test_expect_success 'using "where = before"' ' - git config trailer.bug.where "before" && + test_config trailer.separators ":=#" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -762,7 +776,9 @@ test_expect_success 'using "where = before"' ' ' test_expect_success 'overriding configuration with "--where after"' ' - git config trailer.ack.where "before" && + test_config trailer.separators ":=" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "before" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Fixes: Z @@ -776,7 +792,12 @@ test_expect_success 'overriding configuration with "--where after"' ' test_cmp expected actual ' -test_expect_success 'using "where = before" with "--no-where"' ' +test_expect_success 'using "--where after" with "--no-where"' ' + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "before" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -791,8 +812,59 @@ test_expect_success 'using "where = before" with "--no-where"' ' test_cmp expected actual ' +# Check whether using "--no-where" clears out only the "--where after", such +# that we still use the configuration in trailer.where (which is different from +# the hardcoded default (in WHERE_END) assuming the absence of .gitconfig). +# Here, the "start" setting of trailer.where is respected, so the new "Acked-by" +# and "Bug" trailers are placed at the beginning, and not at the end which is +# the harcoded default. +test_expect_success 'using "--where after" with "--no-where" defaults to configuration' ' + test_config trailer.ack.key "Acked-by= " && + test_config trailer.bug.key "Bug #" && + test_config trailer.separators ":=#" && + test_config trailer.where "start" && + cat complex_message_body >expected && + sed -e "s/ Z\$/ /" >>expected <<-\EOF && + Bug #42 + Acked-by= Peff + Fixes: Z + Acked-by= Z + Reviewed-by: Z + Signed-off-by: Z + EOF + git interpret-trailers --where after --no-where --trailer "ack: Peff" \ + --trailer "bug: 42" complex_message >actual && + test_cmp expected actual +' + +# The "--where after" will only get respected for the trailer that came +# immediately after it. For the next trailer (Bug #42), we default to using the +# hardcoded WHERE_END because we don't have any "trailer.where" or +# "trailer.bug.where" configured. +test_expect_success 'using "--no-where" defaults to harcoded default if nothing configured' ' + test_config trailer.ack.key "Acked-by= " && + test_config trailer.bug.key "Bug #" && + test_config trailer.separators ":=#" && + cat complex_message_body >expected && + sed -e "s/ Z\$/ /" >>expected <<-\EOF && + Fixes: Z + Acked-by= Z + Acked-by= Peff + Reviewed-by: Z + Signed-off-by: Z + Bug #42 + EOF + git interpret-trailers --where after --trailer "ack: Peff" --no-where \ + --trailer "bug: 42" complex_message >actual && + test_cmp expected actual +' + test_expect_success 'using "where = after"' ' - git config trailer.ack.where "after" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -808,8 +880,11 @@ test_expect_success 'using "where = after"' ' ' test_expect_success 'using "where = end"' ' - git config trailer.review.key "Reviewed-by" && - git config trailer.review.where "end" && + test_config trailer.review.key "Reviewed-by" && + test_config trailer.review.where "end" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.separators ":=" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Fixes: Z @@ -827,8 +902,11 @@ test_expect_success 'using "where = end"' ' ' test_expect_success 'using "where = start"' ' - git config trailer.review.key "Reviewed-by" && - git config trailer.review.where "start" && + test_config trailer.review.key "Reviewed-by" && + test_config trailer.review.where "start" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.separators ":=" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Reviewed-by: Johannes @@ -846,8 +924,13 @@ test_expect_success 'using "where = start"' ' ' test_expect_success 'using "where = before" for a token in the middle of the message' ' - git config trailer.review.key "Reviewed-by:" && - git config trailer.review.where "before" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.review.where "before" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -864,6 +947,12 @@ test_expect_success 'using "where = before" for a token in the middle of the mes ' test_expect_success 'using "where = before" and --trim-empty' ' + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && cat >>expected <<-\EOF && Bug #46 @@ -878,6 +967,13 @@ test_expect_success 'using "where = before" and --trim-empty' ' ' test_expect_success 'the default is "ifExists = addIfDifferentNeighbor"' ' + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.review.where "before" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -896,7 +992,13 @@ test_expect_success 'the default is "ifExists = addIfDifferentNeighbor"' ' ' test_expect_success 'default "ifExists" is now "addIfDifferent"' ' - git config trailer.ifexists "addIfDifferent" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -914,8 +1016,14 @@ test_expect_success 'default "ifExists" is now "addIfDifferent"' ' ' test_expect_success 'using "ifExists = addIfDifferent" with "where = end"' ' - git config trailer.ack.ifExists "addIfDifferent" && - git config trailer.ack.where "end" && + test_config trailer.ack.ifExists "addIfDifferent" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "end" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -932,8 +1040,14 @@ test_expect_success 'using "ifExists = addIfDifferent" with "where = end"' ' ' test_expect_success 'using "ifExists = addIfDifferent" with "where = before"' ' - git config trailer.ack.ifExists "addIfDifferent" && - git config trailer.ack.where "before" && + test_config trailer.ack.ifExists "addIfDifferent" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "before" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -950,8 +1064,14 @@ test_expect_success 'using "ifExists = addIfDifferent" with "where = before"' ' ' test_expect_success 'using "ifExists = addIfDifferentNeighbor" with "where = end"' ' - git config trailer.ack.ifExists "addIfDifferentNeighbor" && - git config trailer.ack.where "end" && + test_config trailer.ack.ifExists "addIfDifferentNeighbor" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "end" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -973,8 +1093,14 @@ test_expect_success 'using "ifExists = addIfDifferentNeighbor" with "where = end ' test_expect_success 'using "ifExists = addIfDifferentNeighbor" with "where = after"' ' - git config trailer.ack.ifExists "addIfDifferentNeighbor" && - git config trailer.ack.where "after" && + test_config trailer.ack.ifExists "addIfDifferentNeighbor" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -995,7 +1121,11 @@ test_expect_success 'using "ifExists = addIfDifferentNeighbor" with "where = af ' test_expect_success 'using "ifExists = addIfDifferentNeighbor" and --trim-empty' ' - git config trailer.ack.ifExists "addIfDifferentNeighbor" && + test_config trailer.ack.ifExists "addIfDifferentNeighbor" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && cat >>expected <<-\EOF && Bug #42 @@ -1011,8 +1141,14 @@ test_expect_success 'using "ifExists = addIfDifferentNeighbor" and --trim-empty' ' test_expect_success 'using "ifExists = add" with "where = end"' ' - git config trailer.ack.ifExists "add" && - git config trailer.ack.where "end" && + test_config trailer.ack.ifExists "add" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "end" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -1036,8 +1172,14 @@ test_expect_success 'using "ifExists = add" with "where = end"' ' ' test_expect_success 'using "ifExists = add" with "where = after"' ' - git config trailer.ack.ifExists "add" && - git config trailer.ack.where "after" && + test_config trailer.ack.ifExists "add" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -1058,8 +1200,15 @@ test_expect_success 'using "ifExists = add" with "where = after"' ' ' test_expect_success 'overriding configuration with "--if-exists replace"' ' - git config trailer.fix.key "Fixes: " && - git config trailer.fix.ifExists "add" && + test_config trailer.fix.key "Fixes: " && + test_config trailer.fix.ifExists "add" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.review.where "before" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -1074,9 +1223,66 @@ test_expect_success 'overriding configuration with "--if-exists replace"' ' test_cmp expected actual ' +# "trailer.ifexists" is set to "doNothing", so using "--no-if-exists" defaults +# to this "doNothing" behavior. So the "Fixes: 53" trailer does not get added. +test_expect_success 'using "--if-exists replace" with "--no-if-exists" defaults to configuration' ' + test_config trailer.ifexists "doNothing" && + cat complex_message_body >expected && + sed -e "s/ Z\$/ /" >>expected <<-\EOF && + Fixes: Z + Acked-by: Z + Reviewed-by: Z + Signed-off-by: Z + EOF + git interpret-trailers --if-exists replace --no-if-exists --trailer "Fixes: 53" \ + <complex_message >actual && + test_cmp expected actual +' + +# No "ifexists" configuration is set, so using "--no-if-exists" makes it default +# to addIfDifferentNeighbor. Because we do have a different neighbor "Fixes: 53" +# (because it got added by overriding with "--if-exists replace" earlier in the +# arguments list), we add "Signed-off-by: addme". +test_expect_success 'using "--no-if-exists" defaults to hardcoded default if nothing configured' ' + cat complex_message_body >expected && + sed -e "s/ Z\$/ /" >>expected <<-\EOF && + Acked-by: Z + Reviewed-by: Z + Signed-off-by: Z + Fixes: 53 + Signed-off-by: addme + EOF + git interpret-trailers --if-exists replace --trailer "Fixes: 53" --no-if-exists \ + --trailer "Signed-off-by: addme" <complex_message >actual && + test_cmp expected actual +' + +# The second "Fixes: 53" trailer is discarded, because the "--no-if-exists" here +# makes us default to addIfDifferentNeighbor, and we already added the "Fixes: +# 53" trailer earlier in the argument list. +test_expect_success 'using "--no-if-exists" defaults to hardcoded default if nothing configured (no addition)' ' + cat complex_message_body >expected && + sed -e "s/ Z\$/ /" >>expected <<-\EOF && + Acked-by: Z + Reviewed-by: Z + Signed-off-by: Z + Fixes: 53 + EOF + git interpret-trailers --if-exists replace --trailer "Fixes: 53" --no-if-exists \ + --trailer "Fixes: 53" <complex_message >actual && + test_cmp expected actual +' + test_expect_success 'using "ifExists = replace"' ' - git config trailer.fix.key "Fixes: " && - git config trailer.fix.ifExists "replace" && + test_config trailer.fix.key "Fixes: " && + test_config trailer.fix.ifExists "replace" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -1095,7 +1301,16 @@ test_expect_success 'using "ifExists = replace"' ' ' test_expect_success 'using "ifExists = replace" with "where = after"' ' - git config trailer.fix.where "after" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.fix.key "Fixes: " && + test_config trailer.fix.ifExists "replace" && + test_config trailer.fix.where "after" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -1114,7 +1329,15 @@ test_expect_success 'using "ifExists = replace" with "where = after"' ' ' test_expect_success 'using "ifExists = doNothing"' ' - git config trailer.fix.ifExists "doNothing" && + test_config trailer.fix.ifExists "doNothing" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.fix.key "Fixes: " && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -1133,8 +1356,17 @@ test_expect_success 'using "ifExists = doNothing"' ' ' test_expect_success 'the default is "ifMissing = add"' ' - git config trailer.cc.key "Cc: " && - git config trailer.cc.where "before" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.cc.key "Cc: " && + test_config trailer.cc.where "before" && + test_config trailer.fix.key "Fixes: " && + test_config trailer.fix.ifExists "doNothing" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -1154,7 +1386,14 @@ test_expect_success 'the default is "ifMissing = add"' ' ' test_expect_success 'overriding configuration with "--if-missing doNothing"' ' - git config trailer.ifmissing "add" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.fix.key "Fixes: " && + test_config trailer.fix.ifExists "doNothing" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.ifmissing "add" && + test_config trailer.separators ":=" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Fixes: Z @@ -1173,7 +1412,13 @@ test_expect_success 'overriding configuration with "--if-missing doNothing"' ' ' test_expect_success 'when default "ifMissing" is "doNothing"' ' - git config trailer.ifmissing "doNothing" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.fix.ifExists "doNothing" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.ifmissing "doNothing" && + test_config trailer.separators ":=" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Fixes: Z @@ -1187,14 +1432,21 @@ test_expect_success 'when default "ifMissing" is "doNothing"' ' --trailer "cc=Linus" --trailer "ack: Junio" \ --trailer "fix=22" --trailer "bug: 42" --trailer "ack: Peff" \ <complex_message >actual && - test_cmp expected actual && - git config trailer.ifmissing "add" + test_cmp expected actual ' test_expect_success 'using "ifMissing = add" with "where = end"' ' - git config trailer.cc.key "Cc: " && - git config trailer.cc.where "end" && - git config trailer.cc.ifMissing "add" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.cc.key "Cc: " && + test_config trailer.cc.ifMissing "add" && + test_config trailer.cc.where "end" && + test_config trailer.fix.ifExists "doNothing" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -1214,9 +1466,17 @@ test_expect_success 'using "ifMissing = add" with "where = end"' ' ' test_expect_success 'using "ifMissing = add" with "where = before"' ' - git config trailer.cc.key "Cc: " && - git config trailer.cc.where "before" && - git config trailer.cc.ifMissing "add" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.cc.key "Cc: " && + test_config trailer.cc.ifMissing "add" && + test_config trailer.cc.where "before" && + test_config trailer.fix.ifExists "doNothing" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Cc: Linus @@ -1236,7 +1496,15 @@ test_expect_success 'using "ifMissing = add" with "where = before"' ' ' test_expect_success 'using "ifMissing = doNothing"' ' - git config trailer.cc.ifMissing "doNothing" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.cc.ifMissing "doNothing" && + test_config trailer.fix.ifExists "doNothing" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -1254,9 +1522,50 @@ test_expect_success 'using "ifMissing = doNothing"' ' test_cmp expected actual ' +# Ignore the "IgnoredTrailer" because of "--if-missing doNothing", but also +# ignore the "StillIgnoredTrailer" because we set "trailer.ifMissing" to +# "doNothing" in configuration. +test_expect_success 'using "--no-if-missing" defaults to configuration' ' + test_config trailer.ifMissing "doNothing" && + cat complex_message_body >expected && + sed -e "s/ Z\$/ /" >>expected <<-\EOF && + Fixes: Z + Acked-by: Z + Reviewed-by: Z + Signed-off-by: Z + EOF + git interpret-trailers --if-missing doNothing --trailer "IgnoredTrailer: ignoreme" --no-if-missing \ + --trailer "StillIgnoredTrailer: ignoreme" <complex_message >actual && + test_cmp expected actual +' + +# Add the "AddedTrailer" because the "--no-if-missing" clears the "--if-missing +# doNothing" from earlier in the argument list. +test_expect_success 'using "--no-if-missing" defaults to hardcoded default if nothing configured' ' + cat complex_message_body >expected && + sed -e "s/ Z\$/ /" >>expected <<-\EOF && + Fixes: Z + Acked-by: Z + Reviewed-by: Z + Signed-off-by: Z + AddedTrailer: addme + EOF + git interpret-trailers --if-missing doNothing --trailer "IgnoredTrailer: ignoreme" --no-if-missing \ + --trailer "AddedTrailer: addme" <complex_message >actual && + test_cmp expected actual +' + test_expect_success 'default "where" is now "after"' ' git config trailer.where "after" && - git config --unset trailer.ack.where && + test_config trailer.ack.ifExists "add" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.ack.where "after" && + test_config trailer.bug.key "Bug #" && + test_config trailer.bug.where "before" && + test_config trailer.fix.ifExists "doNothing" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=#" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Bug #42 @@ -1280,10 +1589,15 @@ test_expect_success 'default "where" is now "after"' ' ' test_expect_success 'with simple command' ' - git config trailer.sign.key "Signed-off-by: " && - git config trailer.sign.where "after" && - git config trailer.sign.ifExists "addIfDifferentNeighbor" && - git config trailer.sign.command "echo \"A U Thor <author@example.com>\"" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.fix.ifExists "doNothing" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.sign.command "echo \"A U Thor <author@example.com>\"" && + test_config trailer.sign.key "Signed-off-by: " && + test_config trailer.sign.ifExists "addIfDifferentNeighbor" && + test_config trailer.sign.where "after" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Fixes: Z @@ -1298,8 +1612,14 @@ test_expect_success 'with simple command' ' ' test_expect_success 'with command using committer information' ' - git config trailer.sign.ifExists "addIfDifferent" && - git config trailer.sign.command "echo \"\$GIT_COMMITTER_NAME <\$GIT_COMMITTER_EMAIL>\"" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.fix.ifExists "doNothing" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.sign.command "echo \"\$GIT_COMMITTER_NAME <\$GIT_COMMITTER_EMAIL>\"" && + test_config trailer.sign.key "Signed-off-by: " && + test_config trailer.sign.ifExists "addIfDifferent" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Fixes: Z @@ -1314,10 +1634,15 @@ test_expect_success 'with command using committer information' ' ' test_expect_success 'with command using author information' ' - git config trailer.sign.key "Signed-off-by: " && - git config trailer.sign.where "after" && - git config trailer.sign.ifExists "addIfDifferentNeighbor" && - git config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.fix.ifExists "doNothing" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" && + test_config trailer.sign.key "Signed-off-by: " && + test_config trailer.sign.ifExists "addIfDifferentNeighbor" && + test_config trailer.sign.where "after" && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-\EOF && Fixes: Z @@ -1338,12 +1663,19 @@ test_expect_success 'setup a commit' ' ' test_expect_success 'cmd takes precedence over command' ' - test_when_finished "git config --unset trailer.fix.cmd" && - git config trailer.fix.ifExists "replace" && - git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \ - --abbrev-commit --abbrev=14 \"\$1\" || true" && - git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \ + test_config trailer.ack.key "Acked-by= " && + test_config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \ --abbrev-commit --abbrev=14 \$ARG" && + test_config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \ + --abbrev-commit --abbrev=14 \"\$1\" || true" && + test_config trailer.fix.key "Fixes: " && + test_config trailer.fix.ifExists "replace" && + test_config trailer.fix.where "after" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" && + test_config trailer.sign.key "Signed-off-by: " && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=" && FIXED=$(git log -1 --oneline --format="%h (%aN)" --abbrev-commit --abbrev=14 HEAD) && cat complex_message_body >expected2 && sed -e "s/ Z\$/ /" >>expected2 <<-EOF && @@ -1359,8 +1691,16 @@ test_expect_success 'cmd takes precedence over command' ' ' test_expect_success 'with command using $ARG' ' - git config trailer.fix.ifExists "replace" && - git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" && + test_config trailer.fix.key "Fixes: " && + test_config trailer.fix.ifExists "replace" && + test_config trailer.fix.where "after" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" && + test_config trailer.sign.key "Signed-off-by: " && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=" && FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-EOF && @@ -1376,8 +1716,16 @@ test_expect_success 'with command using $ARG' ' ' test_expect_success 'with failing command using $ARG' ' - git config trailer.fix.ifExists "replace" && - git config trailer.fix.command "false \$ARG" && + test_config trailer.ack.key "Acked-by= " && + test_config trailer.fix.command "false \$ARG" && + test_config trailer.fix.key "Fixes: " && + test_config trailer.fix.ifExists "replace" && + test_config trailer.fix.where "after" && + test_config trailer.review.key "Reviewed-by:" && + test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" && + test_config trailer.sign.key "Signed-off-by: " && + test_config trailer.ifexists "addIfDifferent" && + test_config trailer.separators ":=" && cat complex_message_body >expected && sed -e "s/ Z\$/ /" >>expected <<-EOF && Fixes: Z @@ -1392,7 +1740,9 @@ test_expect_success 'with failing command using $ARG' ' ' test_expect_success 'with empty tokens' ' - git config --unset trailer.fix.command && + test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" && + test_config trailer.sign.key "Signed-off-by: " && + test_config trailer.ifexists "addIfDifferent" && cat >expected <<-EOF && Signed-off-by: A U Thor <author@example.com> @@ -1403,7 +1753,8 @@ test_expect_success 'with empty tokens' ' ' test_expect_success 'with command but no key' ' - git config --unset trailer.sign.key && + test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" && + test_config trailer.ifexists "addIfDifferent" && cat >expected <<-EOF && sign: A U Thor <author@example.com> @@ -1414,7 +1765,9 @@ test_expect_success 'with command but no key' ' ' test_expect_success 'with no command and no key' ' - git config --unset trailer.review.key && + test_config trailer.review.where "before" && + test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" && + test_config trailer.ifexists "addIfDifferent" && cat >expected <<-EOF && review: Junio @@ -1426,6 +1779,8 @@ test_expect_success 'with no command and no key' ' ' test_expect_success 'with cut line' ' + test_config trailer.review.where "before" && + test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" && cat >expected <<-\EOF && my subject @@ -1443,7 +1798,8 @@ test_expect_success 'with cut line' ' ' test_expect_success 'only trailers' ' - git config trailer.sign.command "echo config-value" && + test_config trailer.sign.command "echo config-value" && + test_config trailer.ifexists "addIfDifferent" && cat >expected <<-\EOF && existing: existing-value sign: config-value @@ -1462,7 +1818,7 @@ test_expect_success 'only trailers' ' ' test_expect_success 'only-trailers omits non-trailer in middle of block' ' - git config trailer.sign.command "echo config-value" && + test_config trailer.sign.command "echo config-value" && cat >expected <<-\EOF && Signed-off-by: nobody <nobody@nowhere> Signed-off-by: somebody <somebody@somewhere> @@ -1482,7 +1838,7 @@ test_expect_success 'only-trailers omits non-trailer in middle of block' ' ' test_expect_success 'only input' ' - git config trailer.sign.command "echo config-value" && + test_config trailer.sign.command "echo config-value" && cat >expected <<-\EOF && existing: existing-value EOF diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh index 27b66807cd..48e92aa6f7 100755 --- a/t/t7700-repack.sh +++ b/t/t7700-repack.sh @@ -327,6 +327,203 @@ test_expect_success 'auto-bitmaps do not complain if unavailable' ' test_must_be_empty actual ' +test_expect_success 'repacking with a filter works' ' + git -C bare.git repack -a -d && + test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack && + git -C bare.git -c repack.writebitmaps=false repack -a -d --filter=blob:none && + test_stdout_line_count = 2 ls bare.git/objects/pack/*.pack && + commit_pack=$(test-tool -C bare.git find-pack -c 1 HEAD) && + blob_pack=$(test-tool -C bare.git find-pack -c 1 HEAD:file1) && + test "$commit_pack" != "$blob_pack" && + tree_pack=$(test-tool -C bare.git find-pack -c 1 HEAD^{tree}) && + test "$tree_pack" = "$commit_pack" && + blob_pack2=$(test-tool -C bare.git find-pack -c 1 HEAD:file2) && + test "$blob_pack2" = "$blob_pack" +' + +test_expect_success '--filter fails with --write-bitmap-index' ' + test_must_fail \ + env GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0 \ + git -C bare.git repack -a -d --write-bitmap-index --filter=blob:none +' + +test_expect_success 'repacking with two filters works' ' + git init two-filters && + ( + cd two-filters && + mkdir subdir && + test_commit foo && + test_commit subdir_bar subdir/bar && + test_commit subdir_baz subdir/baz + ) && + git clone --no-local --bare two-filters two-filters.git && + ( + cd two-filters.git && + test_stdout_line_count = 1 ls objects/pack/*.pack && + git -c repack.writebitmaps=false repack -a -d \ + --filter=blob:none --filter=tree:1 && + test_stdout_line_count = 2 ls objects/pack/*.pack && + commit_pack=$(test-tool find-pack -c 1 HEAD) && + blob_pack=$(test-tool find-pack -c 1 HEAD:foo.t) && + root_tree_pack=$(test-tool find-pack -c 1 HEAD^{tree}) && + subdir_tree_hash=$(git ls-tree --object-only HEAD -- subdir) && + subdir_tree_pack=$(test-tool find-pack -c 1 "$subdir_tree_hash") && + + # Root tree and subdir tree are not in the same packfiles + test "$commit_pack" != "$blob_pack" && + test "$commit_pack" = "$root_tree_pack" && + test "$blob_pack" = "$subdir_tree_pack" + ) +' + +prepare_for_keep_packs () { + git init keep-packs && + ( + cd keep-packs && + test_commit foo && + test_commit bar + ) && + git clone --no-local --bare keep-packs keep-packs.git && + ( + cd keep-packs.git && + + # Create two packs + # The first pack will contain all of the objects except one blob + git rev-list --objects --all >objs && + grep -v "bar.t" objs | git pack-objects pack && + # The second pack will contain the excluded object and be kept + packid=$(grep "bar.t" objs | git pack-objects pack) && + >pack-$packid.keep && + + # Replace the existing pack with the 2 new ones + rm -f objects/pack/pack* && + mv pack-* objects/pack/ + ) +} + +test_expect_success '--filter works with .keep packs' ' + prepare_for_keep_packs && + ( + cd keep-packs.git && + + foo_pack=$(test-tool find-pack -c 1 HEAD:foo.t) && + bar_pack=$(test-tool find-pack -c 1 HEAD:bar.t) && + head_pack=$(test-tool find-pack -c 1 HEAD) && + + test "$foo_pack" != "$bar_pack" && + test "$foo_pack" = "$head_pack" && + + git -c repack.writebitmaps=false repack -a -d --filter=blob:none && + + foo_pack_1=$(test-tool find-pack -c 1 HEAD:foo.t) && + bar_pack_1=$(test-tool find-pack -c 1 HEAD:bar.t) && + head_pack_1=$(test-tool find-pack -c 1 HEAD) && + + # Object bar is still only in the old .keep pack + test "$foo_pack_1" != "$foo_pack" && + test "$bar_pack_1" = "$bar_pack" && + test "$head_pack_1" != "$head_pack" && + + test "$foo_pack_1" != "$bar_pack_1" && + test "$foo_pack_1" != "$head_pack_1" && + test "$bar_pack_1" != "$head_pack_1" + ) +' + +test_expect_success '--filter works with --pack-kept-objects and .keep packs' ' + rm -rf keep-packs keep-packs.git && + prepare_for_keep_packs && + ( + cd keep-packs.git && + + foo_pack=$(test-tool find-pack -c 1 HEAD:foo.t) && + bar_pack=$(test-tool find-pack -c 1 HEAD:bar.t) && + head_pack=$(test-tool find-pack -c 1 HEAD) && + + test "$foo_pack" != "$bar_pack" && + test "$foo_pack" = "$head_pack" && + + git -c repack.writebitmaps=false repack -a -d --filter=blob:none \ + --pack-kept-objects && + + foo_pack_1=$(test-tool find-pack -c 1 HEAD:foo.t) && + test-tool find-pack -c 2 HEAD:bar.t >bar_pack_1 && + head_pack_1=$(test-tool find-pack -c 1 HEAD) && + + test "$foo_pack_1" != "$foo_pack" && + test "$foo_pack_1" != "$bar_pack" && + test "$head_pack_1" != "$head_pack" && + + # Object bar is in both the old .keep pack and the new + # pack that contained the filtered out objects + grep "$bar_pack" bar_pack_1 && + grep "$foo_pack_1" bar_pack_1 && + test "$foo_pack_1" != "$head_pack_1" + ) +' + +test_expect_success '--filter-to stores filtered out objects' ' + git -C bare.git repack -a -d && + test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack && + + git init --bare filtered.git && + git -C bare.git -c repack.writebitmaps=false repack -a -d \ + --filter=blob:none \ + --filter-to=../filtered.git/objects/pack/pack && + test_stdout_line_count = 1 ls bare.git/objects/pack/pack-*.pack && + test_stdout_line_count = 1 ls filtered.git/objects/pack/pack-*.pack && + + commit_pack=$(test-tool -C bare.git find-pack -c 1 HEAD) && + blob_pack=$(test-tool -C bare.git find-pack -c 0 HEAD:file1) && + blob_hash=$(git -C bare.git rev-parse HEAD:file1) && + test -n "$blob_hash" && + blob_pack=$(test-tool -C filtered.git find-pack -c 1 $blob_hash) && + + echo $(pwd)/filtered.git/objects >bare.git/objects/info/alternates && + blob_pack=$(test-tool -C bare.git find-pack -c 1 HEAD:file1) && + blob_content=$(git -C bare.git show $blob_hash) && + test "$blob_content" = "content1" +' + +test_expect_success '--filter works with --max-pack-size' ' + rm -rf filtered.git && + git init --bare filtered.git && + git init max-pack-size && + ( + cd max-pack-size && + test_commit base && + # two blobs which exceed the maximum pack size + test-tool genrandom foo 1048576 >foo && + git hash-object -w foo && + test-tool genrandom bar 1048576 >bar && + git hash-object -w bar && + git add foo bar && + git commit -m "adding foo and bar" + ) && + git clone --no-local --bare max-pack-size max-pack-size.git && + ( + cd max-pack-size.git && + git -c repack.writebitmaps=false repack -a -d --filter=blob:none \ + --max-pack-size=1M \ + --filter-to=../filtered.git/objects/pack/pack && + echo $(cd .. && pwd)/filtered.git/objects >objects/info/alternates && + + # Check that the 3 blobs are in different packfiles in filtered.git + test_stdout_line_count = 3 ls ../filtered.git/objects/pack/pack-*.pack && + test_stdout_line_count = 1 ls objects/pack/pack-*.pack && + foo_pack=$(test-tool find-pack -c 1 HEAD:foo) && + bar_pack=$(test-tool find-pack -c 1 HEAD:bar) && + base_pack=$(test-tool find-pack -c 1 HEAD:base.t) && + test "$foo_pack" != "$bar_pack" && + test "$foo_pack" != "$base_pack" && + test "$bar_pack" != "$base_pack" && + for pack in "$foo_pack" "$bar_pack" "$base_pack" + do + case "$foo_pack" in */filtered.git/objects/pack/*) true ;; *) return 1 ;; esac + done + ) +' + objdir=.git/objects midx=$objdir/pack/multi-pack-index diff --git a/unicode-width.h b/unicode-width.h index e15fb0455b..be5bf8c4f2 100644 --- a/unicode-width.h +++ b/unicode-width.h @@ -396,14 +396,13 @@ static const struct interval double_width[] = { { 0x2E80, 0x2E99 }, { 0x2E9B, 0x2EF3 }, { 0x2F00, 0x2FD5 }, -{ 0x2FF0, 0x2FFB }, -{ 0x3000, 0x303E }, +{ 0x2FF0, 0x303E }, { 0x3041, 0x3096 }, { 0x3099, 0x30FF }, { 0x3105, 0x312F }, { 0x3131, 0x318E }, { 0x3190, 0x31E3 }, -{ 0x31F0, 0x321E }, +{ 0x31EF, 0x321E }, { 0x3220, 0x3247 }, { 0x3250, 0x4DBF }, { 0x4E00, 0xA48C }, diff --git a/unpack-trees.c b/unpack-trees.c index a203f9a3d7..c2b20b80d5 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -2,7 +2,7 @@ #include "advice.h" #include "strvec.h" #include "repository.h" -#include "config.h" +#include "parse.h" #include "dir.h" #include "environment.h" #include "gettext.h" @@ -1,5 +1,5 @@ #include "git-compat-util.h" -#include "hex.h" +#include "hex-ll.h" #include "strbuf.h" #include "url.h" diff --git a/urlmatch.c b/urlmatch.c index 1c45f23adf..1d0254abac 100644 --- a/urlmatch.c +++ b/urlmatch.c @@ -1,6 +1,6 @@ #include "git-compat-util.h" #include "gettext.h" -#include "hex.h" +#include "hex-ll.h" #include "strbuf.h" #include "urlmatch.h" @@ -3,9 +3,8 @@ */ #include "git-compat-util.h" #include "abspath.h" -#include "config.h" +#include "parse.h" #include "gettext.h" -#include "object.h" #include "repository.h" #include "strbuf.h" #include "trace2.h" @@ -632,11 +631,6 @@ int rmdir_or_warn(const char *file) return warn_if_unremovable("rmdir", file, rmdir(file)); } -int remove_or_warn(unsigned int mode, const char *file) -{ - return S_ISGITLINK(mode) ? rmdir_or_warn(file) : unlink_or_warn(file); -} - static int access_error_is_ok(int err, unsigned flag) { return (is_missing_file_error(err) || @@ -106,11 +106,6 @@ int unlink_or_msg(const char *file, struct strbuf *err); * not exist. */ int rmdir_or_warn(const char *path); -/* - * Calls the correct function out of {unlink,rmdir}_or_warn based on - * the supplied file mode. - */ -int remove_or_warn(unsigned int mode, const char *path); /* * Call access(2), but warn for any error except "missing file" diff --git a/write-or-die.c b/write-or-die.c index d8355c0c3e..42a2dc73cd 100644 --- a/write-or-die.c +++ b/write-or-die.c @@ -1,5 +1,5 @@ #include "git-compat-util.h" -#include "config.h" +#include "parse.h" #include "run-command.h" #include "write-or-die.h" |
