--- /dev/null
+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"
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>
* "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.
+
+ * "git repack" learned "--max-cruft-size" to prevent cruft packs from
+ growing without bounds.
+
+ * "git merge-tree" learned to take strategy backend specific options
+ via the "-X" option, like "git merge" does.
+
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).
+
+ * Test coverage for trailers has been improved.
+
+ * The code to iterate over loose references have been optimized to
+ reduce the number of lstat() system calls.
+ (merge 2cdb796101 vd/loose-ref-iteration-optimization later to maint).
+
Fixes since v2.42
-----------------
Unicode 15.1
(merge 872976c37e bb/unicode-width-table-15 later to maint).
+ * Update mailmap entry for Derrick.
+ (merge 6e5457d8c7 ds/mailmap-entry-update later to maint).
+
+ * In .gitmodules files, submodules are keyed by their names, and the
+ path to the submodule whose name is $name is specified by the
+ submodule.$name.path variable. There were a few codepaths that
+ mixed the name and path up when consulting the submodule database,
+ which have been corrected. It took long for these bugs to be found
+ as the name of a submodule initially is the same as its path, and
+ the problem does not surface until it is moved to a different path,
+ which apparently happens very rarely.
+
+ * "git diff --merge-base X other args..." insisted that X must be a
+ commit and errored out when given an annotated tag that peels to a
+ commit, but we only need it to be a committish. This has been
+ corrected.
+ (merge 4adceb5a29 ar/diff-index-merge-base-fix later to maint).
+
+ * Fix "git merge-tree" to stop segfaulting when the --attr-source
+ option is used.
+ (merge e95bafc52f jc/merge-ort-attr-index-fix later to maint).
+
+ * Unlike "git log --pretty=%D", "git log --pretty="%(decorate)" did
+ not auto-initialize the decoration subsystem, which has been
+ corrected.
+
* Other code cleanup, docfix, build fix, etc.
(merge fd3ba590d8 ws/git-push-doc-grammofix later to maint).
(merge 5f33a843de ds/upload-pack-error-sequence-fix later to maint).
(merge 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).
+ (merge badf2fe1c3 jk/decoration-and-other-leak-fixes later to maint).
+ (merge cebfaaa333 sn/cat-file-doc-update later to maint).
+ (merge 8b3aa36f5a ps/rewritten-is-per-worktree-doc later to maint).
linkgit:git-repack[1]) instead of as loose objects. The default
is `true`.
+gc.maxCruftSize::
+ Limit the size of new cruft packs when repacking. When
+ specified in addition to `--max-cruft-size`, the command line
+ option takes priority. See the `--max-cruft-size` option of
+ linkgit:git-repack[1].
+
gc.pruneExpire::
When 'git gc' is run, it will call 'prune --expire 2.weeks.ago'
(and 'repack --cruft --cruft-expiration 2.weeks.ago' if using
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.
NAME
----
-git-cat-file - Provide content or type and size information for repository objects
-
+git-cat-file - Provide contents or details of repository objects
SYNOPSIS
--------
'git cat-file' <type> <object>
'git cat-file' (-e | -p) <object>
'git cat-file' (-t | -s) [--allow-unknown-type] <object>
+'git cat-file' (--textconv | --filters)
+ [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]
'git cat-file' (--batch | --batch-check | --batch-command) [--batch-all-objects]
[--buffer] [--follow-symlinks] [--unordered]
[--textconv | --filters] [-Z]
-'git cat-file' (--textconv | --filters)
- [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]
DESCRIPTION
-----------
-In its first form, the command provides the content or the type of an object in
-the repository. The type is required unless `-t` or `-p` is used to find the
-object type, or `-s` is used to find the object size, or `--textconv` or
-`--filters` is used (which imply type "blob").
-
-In the second form, a list of objects (separated by linefeeds) is provided on
-stdin, and the SHA-1, type, and size of each object is printed on stdout. The
-output format can be overridden using the optional `<format>` argument. If
-either `--textconv` or `--filters` was specified, the input is expected to
-list the object names followed by the path name, separated by a single
-whitespace, so that the appropriate drivers can be determined.
+Output the contents or other properties such as size, type or delta
+information of one or more objects.
+
+This command can operate in two modes, depending on whether an option
+from the `--batch` family is specified.
+
+In non-batch mode, the command provides information on an object
+named on the command line.
+
+In batch mode, arguments are read from standard input.
OPTIONS
-------
-e::
Exit with zero status if `<object>` exists and is a valid
- object. If `<object>` is of an invalid format exit with non-zero and
- emits an error on stderr.
+ object. If `<object>` is of an invalid format, exit with non-zero
+ status and emit an error on stderr.
-p::
Pretty-print the contents of `<object>` based on its type.
cruft pack instead of storing them as loose objects. `--cruft`
is on by default.
+--max-cruft-size=<n>::
+ When packing unreachable objects into a cruft pack, limit the
+ size of new cruft packs to be at most `<n>` bytes. Overrides any
+ value specified via the `gc.maxCruftSize` configuration. See
+ the `--max-cruft-size` option of linkgit:git-repack[1] for
+ more.
+
--prune=<date>::
Prune loose objects older than date (default is 2 weeks ago,
overridable by the config variable `gc.pruneExpire`).
--------
[verse]
'git interpret-trailers' [--in-place] [--trim-empty]
- [(--trailer <token>[(=|:)<value>])...]
+ [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]
[--parse] [<file>...]
DESCRIPTION
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
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
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.
------------------------------------------------
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::
--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
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.
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`.
+
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.
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
--------
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::
immediately instead of waiting for the next `git gc` invocation.
Only useful with `--cruft -d`.
+--max-cruft-size=<n>::
+ Repack cruft objects into packs as large as `<n>` bytes before
+ creating new packs. As long as there are enough cruft packs
+ smaller than `<n>`, repacking will cause a new cruft pack to
+ be created containing objects from any combined cruft packs,
+ along with any new unreachable objects. Cruft packs larger than
+ `<n>` will not be modified. When the new cruft pack is larger
+ than `<n>` bytes, it will be split into multiple packs, all of
+ which are guaranteed to be at most `<n>` bytes in size. Only
+ useful with `--cruft -d`.
+
--expire-to=<dir>::
Write a cruft pack containing pruned objects (if any) to the
directory `<dir>`. This option is useful for keeping a copy of
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
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.
In general, all pseudo refs are per-worktree and all refs starting with
`refs/` are shared. Pseudo refs are ones like `HEAD` which are directly
under `$GIT_DIR` instead of inside `$GIT_DIR/refs`. There are exceptions,
-however: refs inside `refs/bisect` and `refs/worktree` are not shared.
+however: refs inside `refs/bisect`, `refs/worktree` and `refs/rewritten` are
+not shared.
Refs that are per-worktree can still be accessed from another worktree via
two special paths, `main-worktree` and `worktrees`. The former gives
`/path/other/test-next/.git/HEAD` or `/path/main/.git/HEAD`) while `git
rev-parse --git-path refs/heads/master` uses
`$GIT_COMMON_DIR` and returns `/path/main/.git/refs/heads/master`,
-since refs are shared across all worktrees, except `refs/bisect` and
-`refs/worktree`.
+since refs are shared across all worktrees, except `refs/bisect`,
+`refs/worktree` and `refs/rewritten`.
See linkgit:gitrepository-layout[5] for more information. The rule of
thumb is do not make any assumption about whether a path belongs to
them to the index, and commit, all in one step.
A note on commit messages: Though not required, it's a good idea to
-begin the commit message with a single short (less than 50 character)
-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
+begin the commit message 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. For example, linkgit:git-format-patch[1] turns a
commit into email, and it uses the title on the Subject line and the
rest of the commit in the body.
=== Creating good commit messages
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
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
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
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
*/
#include "git-compat-util.h"
-#include "config.h"
+#include "parse.h"
#include "environment.h"
#include "exec-cmd.h"
#include "attr.h"
N_("git cat-file <type> <object>"),
N_("git cat-file (-e | -p) <object>"),
N_("git cat-file (-t | -s) [--allow-unknown-type] <object>"),
+ N_("git cat-file (--textconv | --filters)\n"
+ " [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"),
N_("git cat-file (--batch | --batch-check | --batch-command) [--batch-all-objects]\n"
" [--buffer] [--follow-symlinks] [--unordered]\n"
" [--textconv | --filters] [-Z]"),
- N_("git cat-file (--textconv | --filters)\n"
- " [<rev>:<path|tree-ish> | --path=<path|tree-ish> <rev>]"),
NULL
};
const struct option options[] = {
FREE_AND_NULL(options);
string_list_clear(&pack_indexes, 0);
strbuf_release(&buf);
+ oidset_clear(&commits);
return result;
}
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;
static int pack_refs = 1;
static int prune_reflogs = 1;
static int cruft_packs = 1;
+static unsigned long max_cruft_size;
static int aggressive_depth = 50;
static int aggressive_window = 250;
static int gc_auto_threshold = 6700;
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;
git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
git_config_get_bool("gc.autodetach", &detach_auto);
git_config_get_bool("gc.cruftpacks", &cruft_packs);
+ git_config_get_ulong("gc.maxcruftsize", &max_cruft_size);
git_config_get_expiry("gc.pruneexpire", &prune_expire);
git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire);
git_config_get_expiry("gc.logexpiry", &gc_log_expire);
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);
}
strvec_push(&repack, "--cruft");
if (prune_expire)
strvec_pushf(&repack, "--cruft-expiration=%s", prune_expire);
+ if (max_cruft_size)
+ strvec_pushf(&repack, "--max-cruft-size=%lu",
+ max_cruft_size);
} else {
strvec_push(&repack, "-A");
if (prune_expire)
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)
N_("prune unreferenced objects"),
PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
OPT_BOOL(0, "cruft", &cruft_packs, N_("pack unreferenced objects separately")),
+ OPT_MAGNITUDE(0, "max-cruft-size", &max_cruft_size,
+ N_("with --cruft, limit the size of new cruft packs")),
OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
OPT_BOOL_F(0, "auto", &auto_gc, N_("enable auto-gc mode"),
PARSE_OPT_NOCOMPLETE),
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
};
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),
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()
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);
#include "quote.h"
#include "tree.h"
#include "config.h"
+#include "strvec.h"
static int line_termination = '\n';
int show_messages;
int name_only;
int use_stdin;
+ struct merge_options merge_options;
};
static int real_merge(struct merge_tree_options *o,
{
struct commit *parent1, *parent2;
struct commit_list *merge_bases = NULL;
- struct merge_options opt;
struct merge_result result = { 0 };
int show_messages = o->show_messages;
+ struct merge_options opt;
+ copy_merge_options(&opt, &o->merge_options);
parent1 = get_merge_parent(branch1);
if (!parent1)
help_unknown_ref(branch1, "merge-tree",
help_unknown_ref(branch2, "merge-tree",
_("not something we can merge"));
- init_merge_options(&opt, the_repository);
-
opt.show_rename_progress = 0;
opt.branch1 = branch1;
if (o->use_stdin)
putchar(line_termination);
merge_finalize(&opt, &result);
+ clear_merge_options(&opt);
return !result.clean; /* result.clean < 0 handled above */
}
int cmd_merge_tree(int argc, const char **argv, const char *prefix)
{
struct merge_tree_options o = { .show_messages = -1 };
+ struct strvec xopts = STRVEC_INIT;
int expected_remaining_argc;
int original_argc;
const char *merge_base = NULL;
&merge_base,
N_("commit"),
N_("specify a merge-base for the merge")),
+ OPT_STRVEC('X', "strategy-option", &xopts, N_("option=value"),
+ N_("option for selected merge strategy")),
OPT_END()
};
+ /* Init merge options */
+ init_merge_options(&o.merge_options, the_repository);
+
/* Parse arguments */
original_argc = argc - 1; /* ignoring argv[0] */
argc = parse_options(argc, argv, prefix, mt_options,
merge_tree_usage, PARSE_OPT_STOP_AT_NON_OPTION);
+ if (xopts.nr && o.mode == MODE_TRIVIAL)
+ die(_("--trivial-merge is incompatible with all other options"));
+ for (int x = 0; x < xopts.nr; x++)
+ if (parse_merge_opt(&o.merge_options, xopts.v[x]))
+ die(_("unknown strategy option: -X%s"), xopts.v[x]);
+
/* Handle --stdin */
if (o.use_stdin) {
struct strbuf buf = STRBUF_INIT;
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;
for (j = remoteheads; j; j = j->next) {
struct commit_list *common_one;
+ struct commit *common_item;
/*
* Here we *have* to calculate the individual
common_one = repo_get_merge_bases(the_repository,
head_commit,
j->item);
- if (!oideq(&common_one->item->object.oid, &j->item->object.oid)) {
+ common_item = common_one->item;
+ free_commit_list(common_one);
+ if (!oideq(&common_item->object.oid, &j->item->object.oid)) {
up_to_date = 0;
break;
}
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"));
/* 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;
#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
#define PACK_CRUFT 4
#define DELETE_PACK 1
+#define RETAIN_PACK 2
static int pack_everything;
static int delta_base_offset = 1;
const char *window_memory;
const char *depth;
const char *threads;
- const char *max_pack_size;
+ unsigned long max_pack_size;
int no_reuse_delta;
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,
item->util = (void*)((uintptr_t)item->util | DELETE_PACK);
}
+static void pack_unmark_for_deletion(struct string_list_item *item)
+{
+ item->util = (void*)((uintptr_t)item->util & ~DELETE_PACK);
+}
+
static int pack_is_marked_for_deletion(struct string_list_item *item)
{
return (uintptr_t)item->util & DELETE_PACK;
}
+static void pack_mark_retained(struct string_list_item *item)
+{
+ item->util = (void*)((uintptr_t)item->util | RETAIN_PACK);
+}
+
+static int pack_is_retained(struct string_list_item *item)
+{
+ return (uintptr_t)item->util & RETAIN_PACK;
+}
+
static void mark_packs_for_deletion_1(struct string_list *names,
struct string_list *list)
{
if (len < hexsz)
continue;
sha1 = item->string + len - hexsz;
- /*
- * Mark this pack for deletion, which ensures that this
- * pack won't be included in a MIDX (if `--write-midx`
- * was given) and that we will actually delete this pack
- * (if `-d` was given).
- */
- if (!string_list_has_string(names, sha1))
+
+ if (pack_is_retained(item)) {
+ pack_unmark_for_deletion(item);
+ } else if (!string_list_has_string(names, sha1)) {
+ /*
+ * Mark this pack for deletion, which ensures
+ * that this pack won't be included in a MIDX
+ * (if `--write-midx` was given) and that we
+ * will actually delete this pack (if `-d` was
+ * given).
+ */
pack_mark_for_deletion(item);
+ }
}
}
+static void retain_cruft_pack(struct existing_packs *existing,
+ struct packed_git *cruft)
+{
+ struct strbuf buf = STRBUF_INIT;
+ struct string_list_item *item;
+
+ strbuf_addstr(&buf, pack_basename(cruft));
+ strbuf_strip_suffix(&buf, ".pack");
+
+ item = string_list_lookup(&existing->cruft_packs, buf.buf);
+ if (!item)
+ BUG("could not find cruft pack '%s'", pack_basename(cruft));
+
+ pack_mark_retained(item);
+ strbuf_release(&buf);
+}
+
static void mark_packs_for_deletion(struct existing_packs *existing,
struct string_list *names)
}
string_list_sort(&existing->kept_packs);
+ string_list_sort(&existing->non_kept_packs);
+ string_list_sort(&existing->cruft_packs);
strbuf_release(&buf);
}
if (args->threads)
strvec_pushf(&cmd->args, "--threads=%s", args->threads);
if (args->max_pack_size)
- strvec_pushf(&cmd->args, "--max-pack-size=%s", args->max_pack_size);
+ strvec_pushf(&cmd->args, "--max-pack-size=%lu", args->max_pack_size);
if (args->no_reuse_delta)
strvec_pushf(&cmd->args, "--no-reuse-delta");
if (args->no_reuse_object)
return data;
}
+static int has_pack_ext(const struct generated_pack_data *data,
+ const char *ext)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(exts); i++) {
+ if (strcmp(exts[i].name, ext))
+ continue;
+ return !!data->tempfiles[i];
+ }
+ BUG("unknown pack extension: '%s'", ext);
+}
+
static void repack_promisor_objects(const struct pack_objects_args *args,
struct string_list *names)
{
static int write_midx_included_packs(struct string_list *include,
struct pack_geometry *geometry,
+ struct string_list *names,
const char *refs_snapshot,
int show_progress, int write_bitmaps)
{
if (preferred)
strvec_pushf(&cmd.args, "--preferred-pack=%s",
pack_basename(preferred));
+ else if (names->nr) {
+ /* The largest pack was repacked, meaning that either
+ * one or two packs exist depending on whether the
+ * repository has a cruft pack or not.
+ *
+ * Select the non-cruft one as preferred to encourage
+ * pack-reuse among packs containing reachable objects
+ * over unreachable ones.
+ *
+ * (Note we could write multiple packs here if
+ * `--max-pack-size` was given, but any one of them
+ * will suffice, so pick the first one.)
+ */
+ for_each_string_list_item(item, names) {
+ struct generated_pack_data *data = item->util;
+ if (has_pack_ext(data, ".mtimes"))
+ continue;
+
+ strvec_pushf(&cmd.args, "--preferred-pack=pack-%s.pack",
+ item->string);
+ break;
+ }
+ } else {
+ /*
+ * No packs were kept, and no packs were written. The
+ * only thing remaining are .keep packs (unless
+ * --pack-kept-objects was given).
+ *
+ * Set the `--preferred-pack` arbitrarily here.
+ */
+ ;
+ }
if (refs_snapshot)
strvec_pushf(&cmd.args, "--refs-snapshot=%s", refs_snapshot);
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 existing_cruft_pack_cmp(const void *va, const void *vb)
+{
+ struct packed_git *a = *(struct packed_git **)va;
+ struct packed_git *b = *(struct packed_git **)vb;
+
+ if (a->pack_size < b->pack_size)
+ return -1;
+ if (a->pack_size > b->pack_size)
+ return 1;
+ return 0;
+}
+
+static void collapse_small_cruft_packs(FILE *in, size_t max_size,
+ struct existing_packs *existing)
+{
+ struct packed_git **existing_cruft, *p;
+ struct strbuf buf = STRBUF_INIT;
+ size_t total_size = 0;
+ size_t existing_cruft_nr = 0;
+ size_t i;
+
+ ALLOC_ARRAY(existing_cruft, existing->cruft_packs.nr);
+
+ for (p = get_all_packs(the_repository); p; p = p->next) {
+ if (!(p->is_cruft && p->pack_local))
+ continue;
+
+ strbuf_reset(&buf);
+ strbuf_addstr(&buf, pack_basename(p));
+ strbuf_strip_suffix(&buf, ".pack");
+
+ if (!string_list_has_string(&existing->cruft_packs, buf.buf))
+ continue;
+
+ if (existing_cruft_nr >= existing->cruft_packs.nr)
+ BUG("too many cruft packs (found %"PRIuMAX", but knew "
+ "of %"PRIuMAX")",
+ (uintmax_t)existing_cruft_nr + 1,
+ (uintmax_t)existing->cruft_packs.nr);
+ existing_cruft[existing_cruft_nr++] = p;
+ }
+
+ QSORT(existing_cruft, existing_cruft_nr, existing_cruft_pack_cmp);
+
+ for (i = 0; i < existing_cruft_nr; i++) {
+ size_t proposed;
+
+ p = existing_cruft[i];
+ proposed = st_add(total_size, p->pack_size);
+
+ if (proposed <= max_size) {
+ total_size = proposed;
+ fprintf(in, "-%s\n", pack_basename(p));
+ } else {
+ retain_cruft_pack(existing, p);
+ fprintf(in, "%s\n", pack_basename(p));
+ }
+ }
+
+ for (i = 0; i < existing->non_kept_packs.nr; i++)
+ fprintf(in, "-%s.pack\n",
+ existing->non_kept_packs.items[i].string);
+
+ strbuf_release(&buf);
+ free(existing_cruft);
+}
+
static int write_cruft_pack(const struct pack_objects_args *args,
const char *destination,
const char *pack_prefix,
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);
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);
+ if (args->max_pack_size && !cruft_expiration) {
+ collapse_small_cruft_packs(in, args->max_pack_size, existing);
+ } else {
+ for_each_string_list_item(item, &existing->non_kept_packs)
+ fprintf(in, "-%s.pack\n", item->string);
+ for_each_string_list_item(item, &existing->cruft_packs)
+ fprintf(in, "-%s.pack\n", item->string);
+ }
for_each_string_list_item(item, &existing->kept_packs)
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)
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 */
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,
PACK_CRUFT),
OPT_STRING(0, "cruft-expiration", &cruft_expiration, N_("approxidate"),
N_("with --cruft, expire objects older than this")),
+ OPT_MAGNITUDE(0, "max-cruft-size", &cruft_po_args.max_pack_size,
+ N_("with --cruft, limit the size of new cruft packs")),
OPT_BOOL('d', NULL, &delete_redundant,
N_("remove redundant packs, and run git-prune-packed")),
OPT_BOOL('f', NULL, &po_args.no_reuse_delta,
N_("limits the maximum delta depth")),
OPT_STRING(0, "threads", &po_args.threads, N_("n"),
N_("limits the maximum number of threads")),
- OPT_STRING(0, "max-pack-size", &po_args.max_pack_size, N_("bytes"),
+ OPT_MAGNITUDE(0, "max-pack-size", &po_args.max_pack_size,
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"),
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,
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
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;
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;
}
}
+ 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);
struct string_list include = STRING_LIST_INIT_NODUP;
midx_included_packs(&include, &existing, &names, &geometry);
- ret = write_midx_included_packs(&include, &geometry,
+ ret = write_midx_included_packs(&include, &geometry, &names,
refs_snapshot ? get_tempfile_path(refs_snapshot) : NULL,
show_progress, write_bitmaps > 0);
string_list_clear(&names, 1);
existing_packs_release(&existing);
free_pack_geometry(&geometry);
+ list_objects_filter_release(&po_args.filter_options);
return ret;
}
static int module_set_url(int argc, const char **argv, const char *prefix)
{
- int quiet = 0;
+ int quiet = 0, ret;
const char *newurl;
const char *path;
char *config_name;
N_("git submodule set-url [--quiet] <path> <newurl>"),
NULL
};
+ const struct submodule *sub;
argc = parse_options(argc, argv, prefix, options, usage, 0);
if (argc != 2 || !(path = argv[0]) || !(newurl = argv[1]))
usage_with_options(usage, options);
- config_name = xstrfmt("submodule.%s.url", path);
+ sub = submodule_from_path(the_repository, null_oid(), path);
- config_set_in_gitmodules_file_gently(config_name, newurl);
- sync_submodule(path, prefix, NULL, quiet ? OPT_QUIET : 0);
+ if (!sub)
+ die(_("no submodule mapping found in .gitmodules for path '%s'"),
+ path);
- free(config_name);
+ config_name = xstrfmt("submodule.%s.url", sub->name);
+ ret = config_set_in_gitmodules_file_gently(config_name, newurl);
- return 0;
+ if (!ret) {
+ repo_read_gitmodules(the_repository, 0);
+ sync_submodule(sub->path, prefix, NULL, quiet ? OPT_QUIET : 0);
+ }
+
+ free(config_name);
+ return !!ret;
}
static int module_set_branch(int argc, const char **argv, const char *prefix)
N_("git submodule set-branch [-q|--quiet] (-b|--branch) <branch> <path>"),
NULL
};
+ const struct submodule *sub;
argc = parse_options(argc, argv, prefix, options, usage, 0);
if (argc != 1 || !(path = argv[0]))
usage_with_options(usage, options);
- config_name = xstrfmt("submodule.%s.branch", path);
+ sub = submodule_from_path(the_repository, null_oid(), path);
+
+ if (!sub)
+ die(_("no submodule mapping found in .gitmodules for path '%s'"),
+ path);
+
+ config_name = xstrfmt("submodule.%s.branch", sub->name);
ret = config_set_in_gitmodules_file_gently(config_name, opt_branch);
free(config_name);
* 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];
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;
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;
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);
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
}
}
-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;
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
#include "color.h"
#include "editor.h"
#include "gettext.h"
-#include "hex.h"
+#include "hex-ll.h"
#include "pager.h"
#include "strbuf.h"
cur_g = cur_g->base_graph;
}
- g->base_graph = chain;
-
if (chain) {
if (unsigned_add_overflows(chain->num_commits,
chain->num_commits_in_base)) {
g->num_commits_in_base = chain->num_commits + chain->num_commits_in_base;
}
+ g->base_graph = chain;
+
return 1;
}
if (add_graph_to_chain(g, graph_chain, oids, i)) {
graph_chain = g;
valid = 1;
+ } else {
+ free_commit_graph(g);
}
break;
return NULL;
}
-static void close_commit_graph_one(struct commit_graph *g)
-{
- if (!g)
- return;
-
- clear_commit_graph_data_slab(&commit_graph_data_slab);
- close_commit_graph_one(g->base_graph);
- free_commit_graph(g);
-}
-
void close_commit_graph(struct raw_object_store *o)
{
- close_commit_graph_one(o->commit_graph);
+ clear_commit_graph_data_slab(&commit_graph_data_slab);
+ free_commit_graph(o->commit_graph);
o->commit_graph = NULL;
}
free(graph_name);
}
+ free(ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1]);
ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1] = xstrdup(hash_to_hex(file_hash));
final_graph_name = get_split_graph_filename(ctx->odb,
ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1]);
+ free(ctx->commit_graph_filenames_after[ctx->num_commit_graphs_after - 1]);
ctx->commit_graph_filenames_after[ctx->num_commit_graphs_after - 1] = final_graph_name;
result = rename(ctx->graph_name, final_graph_name);
cleanup:
free(ctx->graph_name);
+ free(ctx->base_graph_name);
free(ctx->commits.list);
oid_array_clear(&ctx->oids);
clear_topo_level_slab(&topo_levels);
void free_commit_graph(struct commit_graph *g)
{
- if (!g)
- return;
- if (g->data) {
- munmap((void *)g->data, g->data_len);
- g->data = NULL;
+ while (g) {
+ struct commit_graph *next = g->base_graph;
+
+ if (g->data)
+ munmap((void *)g->data, g->data_len);
+ free(g->filename);
+ free(g->bloom_filter_settings);
+ free(g);
+
+ g = next;
}
- free(g->filename);
- free(g->bloom_filter_settings);
- free(g);
}
void disable_commit_graph(struct repository *r)
for (k = bases; k; k = k->next)
end = k;
}
+ free_commit_list(ret);
ret = new_commits;
}
return ret;
#include "date.h"
#include "branch.h"
#include "config.h"
+#include "parse.h"
#include "convert.h"
#include "environment.h"
#include "gettext.h"
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)
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;
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)
{
*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);
#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
* 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.
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__)
int cmd_main(int argc, const char **argv)
{
int listen_port = 0;
- struct string_list listen_addr = STRING_LIST_INIT_NODUP;
+ struct string_list listen_addr = STRING_LIST_INIT_DUP;
int serve_mode = 0, inetd_mode = 0;
const char *pid_file = NULL, *user_name = NULL, *group_name = NULL;
int detach = 0;
struct credentials *cred = NULL;
int i;
+ int ret;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
const char *v;
if (skip_prefix(arg, "--listen=", &v)) {
- string_list_append(&listen_addr, xstrdup_tolower(v));
+ string_list_append_nodup(&listen_addr, xstrdup_tolower(v));
continue;
}
if (skip_prefix(arg, "--port=", &v)) {
die_errno("failed to redirect stderr to /dev/null");
}
- if (inetd_mode || serve_mode)
- return execute();
+ if (inetd_mode || serve_mode) {
+ ret = execute();
+ } else {
+ if (detach) {
+ if (daemonize())
+ die("--detach not supported on this platform");
+ }
- if (detach) {
- if (daemonize())
- die("--detach not supported on this platform");
- }
+ if (pid_file)
+ write_file(pid_file, "%"PRIuMAX, (uintmax_t) getpid());
- if (pid_file)
- write_file(pid_file, "%"PRIuMAX, (uintmax_t) getpid());
+ /* prepare argv for serving-processes */
+ strvec_push(&cld_argv, argv[0]); /* git-daemon */
+ strvec_push(&cld_argv, "--serve");
+ for (i = 1; i < argc; ++i)
+ strvec_push(&cld_argv, argv[i]);
- /* prepare argv for serving-processes */
- strvec_push(&cld_argv, argv[0]); /* git-daemon */
- strvec_push(&cld_argv, "--serve");
- for (i = 1; i < argc; ++i)
- strvec_push(&cld_argv, argv[i]);
+ ret = serve(&listen_addr, listen_port, cred);
+ }
- return serve(&listen_addr, listen_port, cred);
+ string_list_clear(&listen_addr, 0);
+ return ret;
}
j = 0;
}
}
+
+void clear_decoration(struct decoration *n, void (*free_cb)(void *))
+{
+ if (free_cb) {
+ unsigned int i;
+ for (i = 0; i < n->size; i++) {
+ void *d = n->entries[i].decoration;
+ if (d)
+ free_cb(d);
+ }
+ }
+
+ FREE_AND_NULL(n->entries);
+ n->size = n->nr = 0;
+}
*/
void *lookup_decoration(struct decoration *n, const struct object *obj);
+/*
+ * Clear all decoration entries, releasing any memory used by the structure.
+ * If free_cb is not NULL, it is called for every decoration value currently
+ * stored.
+ *
+ * After clearing, the decoration struct can be used again. The "name" field is
+ * retained.
+ */
+void clear_decoration(struct decoration *n, void (*free_cb)(void *));
+
#endif
return 0;
}
-/*
- * Get the d_type of a dirent. If the d_type is unknown, derive it from
- * stat.st_mode.
- *
- * Note that 'path' is assumed to have a trailing slash. It is also modified
- * in-place during the execution of the function, but is then reverted to its
- * original value before returning.
- */
-static unsigned char get_dtype(struct dirent *e, struct strbuf *path)
-{
- struct stat st;
- unsigned char dtype = DTYPE(e);
- size_t base_path_len;
-
- if (dtype != DT_UNKNOWN)
- return dtype;
-
- /* d_type unknown in dirent, try to fall back on lstat results */
- base_path_len = path->len;
- strbuf_addstr(path, e->d_name);
- if (lstat(path->buf, &st))
- goto cleanup;
-
- /* determine d_type from st_mode */
- if (S_ISREG(st.st_mode))
- dtype = DT_REG;
- else if (S_ISDIR(st.st_mode))
- dtype = DT_DIR;
- else if (S_ISLNK(st.st_mode))
- dtype = DT_LNK;
-
-cleanup:
- strbuf_setlen(path, base_path_len);
- return dtype;
-}
-
static int count_files(struct strbuf *path)
{
DIR *dir = opendir(path->buf);
return 0;
while ((e = readdir_skip_dot_and_dotdot(dir)) != NULL)
- if (get_dtype(e, path) == DT_REG)
+ if (get_dtype(e, path, 0) == DT_REG)
count++;
closedir(dir);
base_path_len = count_path.len;
while ((e = readdir_skip_dot_and_dotdot(dir)) != NULL)
- if (get_dtype(e, &count_path) == DT_DIR &&
+ if (get_dtype(e, &count_path, 0) == DT_DIR &&
strlen(e->d_name) == 2 &&
!hex_to_bytes(&c, e->d_name, 1)) {
strbuf_setlen(&count_path, base_path_len);
strbuf_add_absolute_path(&abspath, at_root ? "." : path);
strbuf_addch(&abspath, '/');
- dtype = get_dtype(e, &abspath);
+ dtype = get_dtype(e, &abspath, 0);
strbuf_setlen(&buf, len);
strbuf_addstr(&buf, e->d_name);
struct object *obj = revs->pending.objects[i].item;
if (obj->flags)
die(_("--merge-base does not work with ranges"));
- if (obj->type != OBJ_COMMIT)
- die(_("--merge-base only works with commits"));
}
/*
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 |
#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);
return DT_UNKNOWN;
}
+unsigned char get_dtype(struct dirent *e, struct strbuf *path,
+ int follow_symlink)
+{
+ struct stat st;
+ unsigned char dtype = DTYPE(e);
+ size_t base_path_len;
+
+ if (dtype != DT_UNKNOWN && !(follow_symlink && dtype == DT_LNK))
+ return dtype;
+
+ /*
+ * d_type unknown or unfollowed symlink, try to fall back on [l]stat
+ * results. If [l]stat fails, explicitly set DT_UNKNOWN.
+ */
+ base_path_len = path->len;
+ strbuf_addstr(path, e->d_name);
+ if ((follow_symlink && stat(path->buf, &st)) ||
+ (!follow_symlink && lstat(path->buf, &st)))
+ goto cleanup;
+
+ /* determine d_type from st_mode */
+ if (S_ISREG(st.st_mode))
+ dtype = DT_REG;
+ else if (S_ISDIR(st.st_mode))
+ dtype = DT_DIR;
+ else if (S_ISLNK(st.st_mode))
+ dtype = DT_LNK;
+
+cleanup:
+ strbuf_setlen(path, base_path_len);
+ return dtype;
+}
+
static int resolve_dtype(int dtype, struct index_state *istate,
const char *path, int len)
{
struct dirent *readdir_skip_dot_and_dotdot(DIR *dirp);
+/*
+ * Get the d_type of a dirent. If the d_type is unknown, derive it from
+ * stat.st_mode using the path to the dirent's containing directory (path) and
+ * the name of the dirent itself.
+ *
+ * If 'follow_symlink' is 1, this function will attempt to follow DT_LNK types
+ * using 'stat'. Links are *not* followed recursively, so a symlink pointing
+ * to another symlink will still resolve to 'DT_LNK'.
+ *
+ * Note that 'path' is assumed to have a trailing slash. It is also modified
+ * in-place during the execution of the function, but is then reverted to its
+ * original value before returning.
+ */
+unsigned char get_dtype(struct dirent *e, struct strbuf *path,
+ int follow_symlink);
+
/*Count the number of slashes for string s*/
int count_slashes(const char *s);
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);
+}
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 */
--- /dev/null
+#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;
+}
--- /dev/null
+#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
#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)
{
#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
/* Like get_oid_hex, but for an arbitrary hash algorithm. */
int get_oid_hex_algop(const char *hex, struct object_id *oid, const struct git_hash_algo *algop);
-/*
- * 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,
return 0;
}
+
+static void free_void_line_log_data(void *data)
+{
+ free_line_log_data(data);
+}
+
+void line_log_free(struct rev_info *rev)
+{
+ clear_decoration(&rev->line_log_data, free_void_line_log_data);
+}
int line_log_print(struct rev_info *rev, struct commit *commit);
+void line_log_free(struct rev_info *rev);
+
#endif /* LINE_LOG_H */
#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"
struct index_state *attr_index = &opt->priv->attr_index;
struct cache_entry *ce;
+ attr_index->repo = opt->repo;
attr_index->initialized = 1;
if (!opt->renormalize)
opt->buffer_output = 0;
}
+/*
+ * For now, members of merge_options do not need deep copying, but
+ * it may change in the future, in which case we would need to update
+ * this, and also make a matching change to clear_merge_options() to
+ * release the resources held by a copied instance.
+ */
+void copy_merge_options(struct merge_options *dst, struct merge_options *src)
+{
+ *dst = *src;
+}
+
+void clear_merge_options(struct merge_options *opt UNUSED)
+{
+ ; /* no-op as our copy is shallow right now */
+}
+
int parse_merge_opt(struct merge_options *opt, const char *s)
{
const char *arg;
void init_merge_options(struct merge_options *opt, struct repository *repo);
+void copy_merge_options(struct merge_options *dst, struct merge_options *src);
+void clear_merge_options(struct merge_options *opt);
+
/* parse the option in s and update the relevant field of opt */
int parse_merge_opt(struct merge_options *opt, const char *s);
* 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,
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;
}
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);
}
/*
#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,
#include "packfile.h"
#include "strbuf.h"
#include "trace2.h"
-#include "config.h"
+#include "parse.h"
#include "midx.h"
#include "csum-file.h"
#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;
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,
--- /dev/null
+#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;
+}
--- /dev/null
+#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 */
#include "git-compat-util.h"
#include "abspath.h"
-#include "config.h"
+#include "parse.h"
#include "dir.h"
#include "environment.h"
#include "gettext.h"
- Do not use non-ASCII characters in the subject of a commit.
- The length of commit subject (first line of the commit log) should
- be less than 50 characters, and the length of other lines of the
+ be no more than 50 characters, and the length of other lines of the
commit log should be no more than 72 characters.
- Add "Signed-off-by" trailer to your commit log, like other commits
#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"
case 'D':
w->decorate = 1;
break;
+ case '(':
+ if (starts_with(fmt + 1, "decorate"))
+ w->decorate = 1;
+ break;
}
}
}
#include "trace.h"
#include "trace2.h"
#include "utf8.h"
-#include "config.h"
+#include "parse.h"
#define TP_IDX_MAX 8
#include "git-compat-util.h"
-#include "config.h"
+#include "parse.h"
#include "environment.h"
#include "run-command.h"
#include "strbuf.h"
#include "git-compat-util.h"
#include "rebase.h"
-#include "config.h"
+#include "parse.h"
#include "gettext.h"
/*
int dirnamelen = strlen(dirname);
struct strbuf refname;
struct strbuf path = STRBUF_INIT;
- size_t path_baselen;
files_ref_path(refs, &path, dirname);
- path_baselen = path.len;
d = opendir(path.buf);
if (!d) {
while ((de = readdir(d)) != NULL) {
struct object_id oid;
- struct stat st;
int flag;
+ unsigned char dtype;
if (de->d_name[0] == '.')
continue;
if (ends_with(de->d_name, ".lock"))
continue;
strbuf_addstr(&refname, de->d_name);
- strbuf_addstr(&path, de->d_name);
- if (stat(path.buf, &st) < 0) {
- ; /* silently ignore */
- } else if (S_ISDIR(st.st_mode)) {
+
+ dtype = get_dtype(de, &path, 1);
+ if (dtype == DT_DIR) {
strbuf_addch(&refname, '/');
add_entry_to_dir(dir,
create_dir_entry(dir->cache, refname.buf,
refname.len));
- } else {
+ } else if (dtype == DT_REG) {
if (!refs_resolve_ref_unsafe(&refs->base,
refname.buf,
RESOLVE_REF_READING,
create_ref_entry(refname.buf, &oid, flag));
}
strbuf_setlen(&refname, dirnamelen);
- strbuf_setlen(&path, path_baselen);
}
strbuf_release(&refname);
strbuf_release(&path);
if (level->prefix_state == PREFIX_WITHIN_DIR) {
entry_prefix_state = overlaps_prefix(entry->name, iter->prefix);
- if (entry_prefix_state == PREFIX_EXCLUDES_DIR)
+ if (entry_prefix_state == PREFIX_EXCLUDES_DIR ||
+ (entry_prefix_state == PREFIX_WITHIN_DIR && !(entry->flag & REF_DIR)))
continue;
} else {
entry_prefix_state = level->prefix_state;
static void release_revisions_topo_walk_info(struct topo_walk_info *info);
+static void free_void_commit_list(void *list)
+{
+ free_commit_list(list);
+}
+
void release_revisions(struct rev_info *revs)
{
free_commit_list(revs->commits);
diff_free(&revs->pruning);
reflog_walk_info_release(revs->reflog_info);
release_revisions_topo_walk_info(revs->topo_walk_info);
+ clear_decoration(&revs->children, free_void_commit_list);
+ clear_decoration(&revs->merge_simplification, free);
+ clear_decoration(&revs->treesame, free);
+ line_log_free(revs);
}
static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
#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"
#include "test-tool.h"
-#include "config.h"
+#include "parse.h"
#include "parse-options.h"
static char const * const env__helper_usage[] = {
if (objects_noticed != 2)
BUG("should have 2 objects");
+ clear_decoration(&n, NULL);
+
return 0;
}
--- /dev/null
+#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;
+}
{ "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 },
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);
--- /dev/null
+#!/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
test_cmp expect actual
'
+test_expect_success 'rev-parse --bisect includes bad, excludes good' '
+ test_commit_bulk 6 &&
+
+ git update-ref refs/bisect/bad-1 HEAD~1 &&
+ git update-ref refs/bisect/b HEAD~2 &&
+ git update-ref refs/bisect/bad-3 HEAD~3 &&
+ git update-ref refs/bisect/good-3 HEAD~3 &&
+ git update-ref refs/bisect/bad-4 HEAD~4 &&
+ git update-ref refs/bisect/go HEAD~4 &&
+
+ # Note: refs/bisect/b and refs/bisect/go should be ignored because they
+ # do not match the refs/bisect/bad or refs/bisect/good prefixes.
+ cat >expect <<-EOF &&
+ refs/bisect/bad-1
+ refs/bisect/bad-3
+ refs/bisect/bad-4
+ ^refs/bisect/good-3
+ EOF
+
+ git rev-parse --symbolic-full-name --bisect >actual &&
+ test_cmp expect actual
+'
+
test_done
. ./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' '
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
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
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
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 &&
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
echo c >c &&
git add c &&
git commit -m C &&
- git tag commit-C &&
+ git tag -m commit-C commit-C &&
git merge -m D main &&
git tag commit-D &&
git checkout main &&
test_cmp expect actual
'
+ test_expect_success "$cmd --merge-base with annotated tag" '
+ git checkout main &&
+ git $cmd commit-C >expect &&
+ git $cmd --merge-base commit-C >actual &&
+ test_cmp expect actual
+ '
+
test_expect_success "$cmd --merge-base with one commit and unstaged changes" '
git checkout main &&
test_when_finished git reset --hard &&
test_expect_success "$cmd --merge-base with non-commit" '
git checkout main &&
test_must_fail git $cmd --merge-base main^{tree} 2>err &&
- test_i18ngrep "fatal: --merge-base only works with commits" err
+ test_i18ngrep "is a tree, not a commit" err
'
test_expect_success "$cmd --merge-base with no merge bases and one commit" '
test_expect_success "$cmd --merge-base commit and non-commit" '
test_must_fail git $cmd --merge-base br2 main^{tree} 2>err &&
- test_i18ngrep "fatal: --merge-base only works with commits" err
+ test_i18ngrep "is a tree, not a commit" err
'
test_expect_success "$cmd --merge-base with no merge bases and two commits" '
git log --format="%(decorate:prefix=,suffix=)" -1 >actual2 &&
test_cmp expect2 actual2 &&
- echo "[ HEAD -> foo; tag: bar; qux ]" >expect3 &&
- git log --format="%(decorate:prefix=[ ,suffix= ],separator=%x3B )" \
- -1 >actual3 &&
+ echo "[ bar; qux; foo ]" >expect3 &&
+ git log --format="%(decorate:prefix=[ ,suffix= ],separator=%x3B ,tag=)" \
+ --decorate-refs=refs/ -1 >actual3 &&
test_cmp expect3 actual3 &&
# Try with a typo (in "separator"), in which case the placeholder should
test_cmp expect actual
'
+test_expect_success 'setup more commits for %S with --bisect' '
+ test_commit four &&
+ test_commit five &&
+
+ head1=$(git rev-parse --verify HEAD~0) &&
+ head2=$(git rev-parse --verify HEAD~1) &&
+ head3=$(git rev-parse --verify HEAD~2) &&
+ head4=$(git rev-parse --verify HEAD~3)
+'
+
+test_expect_success '%S with --bisect labels commits with refs/bisect/bad ref' '
+ git update-ref refs/bisect/bad-$head1 $head1 &&
+ git update-ref refs/bisect/go $head1 &&
+ git update-ref refs/bisect/bad-$head2 $head2 &&
+ git update-ref refs/bisect/b $head3 &&
+ git update-ref refs/bisect/bad-$head4 $head4 &&
+ git update-ref refs/bisect/good-$head4 $head4 &&
+
+ # We expect to see the range of commits betwee refs/bisect/good-$head4
+ # and refs/bisect/bad-$head1. The "source" ref is the nearest bisect ref
+ # from which the commit is reachable.
+ cat >expect <<-EOF &&
+ $head1 refs/bisect/bad-$head1
+ $head2 refs/bisect/bad-$head2
+ $head3 refs/bisect/bad-$head2
+ EOF
+ git log --bisect --format="%H %S" >actual &&
+ test_cmp expect actual
+'
+
test_expect_success 'log --pretty=reference' '
git log --pretty="tformat:%h (%s, %as)" >expect &&
git log --pretty=reference >actual &&
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-log-graph.sh
test_description='git log --graph of skewed merges'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-log-graph.sh
test_description='git log with filter options limiting the output'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup test' '
test_cmp expected actual
'
+test_expect_success '3-way merge with --attr-source' '
+ test_when_finished rm -rf 3-way &&
+ git init 3-way &&
+ (
+ cd 3-way &&
+ test_commit initial file1 foo &&
+ base=$(git rev-parse HEAD) &&
+ git checkout -b brancha &&
+ echo bar >>file1 &&
+ git commit -am "adding bar" &&
+ source=$(git rev-parse HEAD) &&
+ git checkout @{-1} &&
+ git checkout -b branchb &&
+ echo baz >>file1 &&
+ git commit -am "adding baz" &&
+ merge=$(git rev-parse HEAD) &&
+ git checkout -b gitattributes &&
+ test_commit "gitattributes" .gitattributes "file1 merge=union" &&
+ git checkout @{-1} &&
+ tree=$(git --attr-source=gitattributes merge-tree --write-tree \
+ --merge-base "$base" --end-of-options "$source" "$merge") &&
+ test_write_lines foo bar baz >expect &&
+ git cat-file -p "$tree:file1" >actual &&
+ test_cmp expect actual
+ )
+'
+
test_expect_success 'file change A, B (same)' '
git reset --hard initial &&
test_commit "change-a-b-same-A" "initial-file" "AAA" &&
git branch side1 &&
git branch side2 &&
git branch side3 &&
+ git branch side4 &&
git checkout side1 &&
test_write_lines 1 2 3 4 5 6 >numbers &&
test_tick &&
git commit -m rename-numbers &&
+ git checkout side4 &&
+ test_write_lines 0 1 2 3 4 5 >numbers &&
+ echo yo >greeting &&
+ git add numbers greeting &&
+ test_tick &&
+ git commit -m other-content-modifications &&
+
git switch --orphan unrelated &&
>something-else &&
git add something-else &&
test_cmp expect actual
'
+test_expect_success 'Auto resolve conflicts by "ours" strategy option' '
+ git checkout side1^0 &&
+
+ # make sure merge conflict exists
+ test_must_fail git merge side4 &&
+ git merge --abort &&
+
+ git merge -X ours side4 &&
+ git rev-parse HEAD^{tree} >expected &&
+
+ git merge-tree -X ours side1 side4 >actual &&
+
+ test_cmp expected actual
+'
+
test_expect_success 'Barf on misspelled option, with exit code other than 0 or 1' '
# Mis-spell with single "s" instead of double "s"
test_expect_code 129 git merge-tree --write-tree --mesages FOOBAR side1 side2 2>expect &&
! 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 |
#!/bin/sh
test_description='split commit graph'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-chunk.sh
#!/bin/sh
test_description='commit graph with 64-bit timestamps'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
if ! test_have_prereq TIME_IS_64BIT || ! test_have_prereq TIME_T_IS_64BIT
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
#!/bin/sh
test_description='test disabling of git-over-tcp in clone/fetch'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY/lib-proto-disable.sh"
. "$TEST_DIRECTORY/lib-git-daemon.sh"
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check_revlist () {
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-merge.sh
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
t3033_reset () {
GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB=1
export GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-merge.sh
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 &&
)
'
+cruft_max_size_opts="git repack -d -l --cruft --cruft-expiration=2.weeks.ago"
+
+test_expect_success 'setup for --max-cruft-size tests' '
+ git init cruft--max-size &&
+ (
+ cd cruft--max-size &&
+ prepare_cruft_history
+ )
+'
+
+test_expect_success '--max-cruft-size sets appropriate repack options' '
+ GIT_TRACE2_EVENT=$(pwd)/trace2.txt git -C cruft--max-size \
+ gc --cruft --max-cruft-size=1M &&
+ test_subcommand $cruft_max_size_opts --max-cruft-size=1048576 <trace2.txt
+'
+
+test_expect_success 'gc.maxCruftSize sets appropriate repack options' '
+ GIT_TRACE2_EVENT=$(pwd)/trace2.txt \
+ git -C cruft--max-size -c gc.maxCruftSize=2M gc --cruft &&
+ test_subcommand $cruft_max_size_opts --max-cruft-size=2097152 <trace2.txt &&
+
+ GIT_TRACE2_EVENT=$(pwd)/trace2.txt \
+ git -C cruft--max-size -c gc.maxCruftSize=2M gc --cruft \
+ --max-cruft-size=3M &&
+ test_subcommand $cruft_max_size_opts --max-cruft-size=3145728 <trace2.txt
+'
+
run_and_wait_for_auto_gc () {
# We read stdout from gc for the side effect of waiting until the
# background gc process exits, closing its fd 9. Furthermore, the
#!/bin/sh
test_description='handling of deep trees in various commands'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# We'll test against two depths here: a small one that will let us check the
TEST_PASSES_SANITIZE_LEAK=true
TEST_NO_CREATE_REPO=1
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
. ./test-lib.sh
test_expect_success 'setup' '
git checkout -b topic &&
echo b >a &&
git add . &&
- git commit -mb
+ git commit -mb &&
+ git checkout main
) &&
mkdir super &&
(cd super &&
git init &&
git submodule add ../submodule &&
- git commit -m "add submodule"
+ git submodule add --name thename ../submodule thepath &&
+ git commit -m "add submodules"
)
'
test_expect_success 'ensure submodule branch is unset' '
(cd super &&
- ! grep branch .gitmodules
+ test_cmp_config "" -f .gitmodules --default "" submodule.submodule.branch
)
'
test_expect_success 'test submodule set-branch --branch' '
(cd super &&
git submodule set-branch --branch topic submodule &&
- grep "branch = topic" .gitmodules &&
+ test_cmp_config topic -f .gitmodules submodule.submodule.branch &&
git submodule update --remote &&
cat <<-\EOF >expect &&
b
'
test_expect_success 'test submodule set-branch --default' '
- test_commit -C submodule c &&
(cd super &&
git submodule set-branch --default submodule &&
- ! grep branch .gitmodules &&
+ test_cmp_config "" -f .gitmodules --default "" submodule.submodule.branch &&
git submodule update --remote &&
cat <<-\EOF >expect &&
- c
+ a
EOF
git -C submodule show -s --pretty=%s >actual &&
test_cmp expect actual
'
test_expect_success 'test submodule set-branch -b' '
- test_commit -C submodule b &&
(cd super &&
git submodule set-branch -b topic submodule &&
- grep "branch = topic" .gitmodules &&
+ test_cmp_config topic -f .gitmodules submodule.submodule.branch &&
git submodule update --remote &&
cat <<-\EOF >expect &&
b
'
test_expect_success 'test submodule set-branch -d' '
- test_commit -C submodule d &&
(cd super &&
git submodule set-branch -d submodule &&
- ! grep branch .gitmodules &&
+ test_cmp_config "" -f .gitmodules --default "" submodule.submodule.branch &&
git submodule update --remote &&
cat <<-\EOF >expect &&
- d
+ a
EOF
git -C submodule show -s --pretty=%s >actual &&
test_cmp expect actual
)
'
+test_expect_success 'test submodule set-branch --branch with named submodule' '
+ (cd super &&
+ git submodule set-branch --branch topic thepath &&
+ test_cmp_config topic -f .gitmodules submodule.thename.branch &&
+ test_cmp_config "" -f .gitmodules --default "" submodule.thepath.branch &&
+ git submodule update --remote &&
+ cat <<-\EOF >expect &&
+ b
+ EOF
+ git -C thepath show -s --pretty=%s >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'test submodule set-branch --default with named submodule' '
+ (cd super &&
+ git submodule set-branch --default thepath &&
+ test_cmp_config "" -f .gitmodules --default "" submodule.thename.branch &&
+ git submodule update --remote &&
+ cat <<-\EOF >expect &&
+ a
+ EOF
+ git -C thepath show -s --pretty=%s >actual &&
+ test_cmp expect actual
+ )
+'
+
test_done
git add file &&
git commit -ma
) &&
+ mkdir namedsubmodule &&
+ (
+ cd namedsubmodule &&
+ git init &&
+ echo 1 >file &&
+ git add file &&
+ git commit -m1
+ ) &&
mkdir super &&
(
cd super &&
git init &&
git submodule add ../submodule &&
- git commit -m "add submodule"
+ git submodule add --name thename ../namedsubmodule thepath &&
+ git commit -m "add submodules"
)
'
test_expect_success 'test submodule set-url' '
- # add a commit and move the submodule (change the url)
+ # add commits and move the submodules (change the urls)
(
cd submodule &&
echo b >>file &&
) &&
mv submodule newsubmodule &&
+ (
+ cd namedsubmodule &&
+ echo 2 >>file &&
+ git add file &&
+ git commit -m2
+ ) &&
+ mv namedsubmodule newnamedsubmodule &&
+
git -C newsubmodule show >expect &&
+ git -C newnamedsubmodule show >>expect &&
(
cd super &&
test_must_fail git submodule update --remote &&
git submodule set-url submodule ../newsubmodule &&
- grep -F "url = ../newsubmodule" .gitmodules &&
+ test_cmp_config ../newsubmodule -f .gitmodules submodule.submodule.url &&
+ git submodule set-url thepath ../newnamedsubmodule &&
+ test_cmp_config ../newnamedsubmodule -f .gitmodules submodule.thename.url &&
+ test_cmp_config "" -f .gitmodules --default "" submodule.thepath.url &&
git submodule update --remote
) &&
git -C super/submodule show >actual &&
+ git -C super/thepath show >>actual &&
test_cmp expect actual
'
'
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
'
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
'
test_expect_success 'with config setup and "%" as separators' '
- git config trailer.separators "%" &&
+ test_config trailer.separators "%" &&
cat >expected <<-\EOF &&
bug% 42
'
test_expect_success 'with "%" as separators and a message with trailers' '
+ test_config trailer.separators "%" &&
cat >special_message <<-\EOF &&
Special Message
'
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
'
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 &&
'
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 &&
'
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
'
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 &&
'
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
'
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
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
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
'
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
'
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
'
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
'
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
'
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
'
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
'
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
'
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
'
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
'
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
'
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
'
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
'
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
'
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
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
'
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
'
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
'
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
'
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
'
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
--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
'
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
'
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
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
'
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
'
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
'
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
'
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 &&
'
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 &&
'
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
'
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>
'
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>
'
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
'
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
'
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
'
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>
'
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
Testing octopus merge with more than 25 refs.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'setup' '
Testing octopus merge when reducing parents to independent branches.'
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
# 0 - 1
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'Ensure we restore original state if no merge strategy handles it' '
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
check_oneline() {
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
test_server_info_missing
'
-test_expect_success '--expire-to stores pruned objects (now)' '
- git init expire-to-now &&
- (
- cd expire-to-now &&
-
- git branch -M main &&
-
- test_commit base &&
-
- git checkout -b cruft &&
- test_commit --no-tag cruft &&
-
- git rev-list --objects --no-object-names main..cruft >moved.raw &&
- sort moved.raw >moved.want &&
-
- git rev-list --all --objects --no-object-names >expect.raw &&
- sort expect.raw >expect &&
-
- git checkout main &&
- git branch -D cruft &&
- git reflog expire --all --expire=all &&
-
- git init --bare expired.git &&
- git repack -d \
- --cruft --cruft-expiration="now" \
- --expire-to="expired.git/objects/pack/pack" &&
-
- expired="$(ls expired.git/objects/pack/pack-*.idx)" &&
- test_path_is_file "${expired%.idx}.mtimes" &&
-
- # Since the `--cruft-expiration` is "now", the effective
- # behavior is to move _all_ unreachable objects out to
- # the location in `--expire-to`.
- git show-index <$expired >expired.raw &&
- cut -d" " -f2 expired.raw | sort >expired.objects &&
- git rev-list --all --objects --no-object-names \
- >remaining.objects &&
-
- # ...in other words, the combined contents of this
- # repository and expired.git should be the same as the
- # set of objects we started with.
- cat expired.objects remaining.objects | sort >actual &&
- test_cmp expect actual &&
-
- # The "moved" objects (i.e., those in expired.git)
- # should be the same as the cruft objects which were
- # expired in the previous step.
- test_cmp moved.want expired.objects
- )
-'
-
-test_expect_success '--expire-to stores pruned objects (5.minutes.ago)' '
- git init expire-to-5.minutes.ago &&
- (
- cd expire-to-5.minutes.ago &&
-
- git branch -M main &&
-
- test_commit base &&
-
- # Create two classes of unreachable objects, one which
- # is older than 5 minutes (stale), and another which is
- # newer (recent).
- for kind in stale recent
- do
- git checkout -b $kind main &&
- test_commit --no-tag $kind || return 1
- done &&
-
- git rev-list --objects --no-object-names main..stale >in &&
- stale="$(git pack-objects $objdir/pack/pack <in)" &&
- mtime="$(test-tool chmtime --get =-600 $objdir/pack/pack-$stale.pack)" &&
-
- # expect holds the set of objects we expect to find in
- # this repository after repacking
- git rev-list --objects --no-object-names recent >expect.raw &&
- sort expect.raw >expect &&
-
- # moved.want holds the set of objects we expect to find
- # in expired.git
- git rev-list --objects --no-object-names main..stale >out &&
- sort out >moved.want &&
-
- git checkout main &&
- git branch -D stale recent &&
- git reflog expire --all --expire=all &&
- git prune-packed &&
-
- git init --bare expired.git &&
- git repack -d \
- --cruft --cruft-expiration=5.minutes.ago \
- --expire-to="expired.git/objects/pack/pack" &&
-
- # Some of the remaining objects in this repository are
- # unreachable, so use `cat-file --batch-all-objects`
- # instead of `rev-list` to get their names
- git cat-file --batch-all-objects --batch-check="%(objectname)" \
- >remaining.objects &&
- sort remaining.objects >actual &&
- test_cmp expect actual &&
-
- (
- cd expired.git &&
-
- expired="$(ls objects/pack/pack-*.mtimes)" &&
- test-tool pack-mtimes $(basename $expired) >out &&
- cut -d" " -f1 out | sort >../moved.got &&
-
- # Ensure that there are as many objects with the
- # expected mtime as were moved to expired.git.
- #
- # In other words, ensure that the recorded
- # mtimes of any moved objects was written
- # correctly.
- grep " $mtime$" out >matching &&
- test_line_count = $(wc -l <../moved.want) matching
- ) &&
- test_cmp moved.want moved.got
- )
-'
-
test_done
--- /dev/null
+#!/bin/sh
+
+test_description='git repack works correctly'
+
+. ./test-lib.sh
+
+objdir=.git/objects
+packdir=$objdir/pack
+
+test_expect_success '--expire-to stores pruned objects (now)' '
+ git init expire-to-now &&
+ (
+ cd expire-to-now &&
+
+ git branch -M main &&
+
+ test_commit base &&
+
+ git checkout -b cruft &&
+ test_commit --no-tag cruft &&
+
+ git rev-list --objects --no-object-names main..cruft >moved.raw &&
+ sort moved.raw >moved.want &&
+
+ git rev-list --all --objects --no-object-names >expect.raw &&
+ sort expect.raw >expect &&
+
+ git checkout main &&
+ git branch -D cruft &&
+ git reflog expire --all --expire=all &&
+
+ git init --bare expired.git &&
+ git repack -d \
+ --cruft --cruft-expiration="now" \
+ --expire-to="expired.git/objects/pack/pack" &&
+
+ expired="$(ls expired.git/objects/pack/pack-*.idx)" &&
+ test_path_is_file "${expired%.idx}.mtimes" &&
+
+ # Since the `--cruft-expiration` is "now", the effective
+ # behavior is to move _all_ unreachable objects out to
+ # the location in `--expire-to`.
+ git show-index <$expired >expired.raw &&
+ cut -d" " -f2 expired.raw | sort >expired.objects &&
+ git rev-list --all --objects --no-object-names \
+ >remaining.objects &&
+
+ # ...in other words, the combined contents of this
+ # repository and expired.git should be the same as the
+ # set of objects we started with.
+ cat expired.objects remaining.objects | sort >actual &&
+ test_cmp expect actual &&
+
+ # The "moved" objects (i.e., those in expired.git)
+ # should be the same as the cruft objects which were
+ # expired in the previous step.
+ test_cmp moved.want expired.objects
+ )
+'
+
+test_expect_success '--expire-to stores pruned objects (5.minutes.ago)' '
+ git init expire-to-5.minutes.ago &&
+ (
+ cd expire-to-5.minutes.ago &&
+
+ git branch -M main &&
+
+ test_commit base &&
+
+ # Create two classes of unreachable objects, one which
+ # is older than 5 minutes (stale), and another which is
+ # newer (recent).
+ for kind in stale recent
+ do
+ git checkout -b $kind main &&
+ test_commit --no-tag $kind || return 1
+ done &&
+
+ git rev-list --objects --no-object-names main..stale >in &&
+ stale="$(git pack-objects $objdir/pack/pack <in)" &&
+ mtime="$(test-tool chmtime --get =-600 $objdir/pack/pack-$stale.pack)" &&
+
+ # expect holds the set of objects we expect to find in
+ # this repository after repacking
+ git rev-list --objects --no-object-names recent >expect.raw &&
+ sort expect.raw >expect &&
+
+ # moved.want holds the set of objects we expect to find
+ # in expired.git
+ git rev-list --objects --no-object-names main..stale >out &&
+ sort out >moved.want &&
+
+ git checkout main &&
+ git branch -D stale recent &&
+ git reflog expire --all --expire=all &&
+ git prune-packed &&
+
+ git init --bare expired.git &&
+ git repack -d \
+ --cruft --cruft-expiration=5.minutes.ago \
+ --expire-to="expired.git/objects/pack/pack" &&
+
+ # Some of the remaining objects in this repository are
+ # unreachable, so use `cat-file --batch-all-objects`
+ # instead of `rev-list` to get their names
+ git cat-file --batch-all-objects --batch-check="%(objectname)" \
+ >remaining.objects &&
+ sort remaining.objects >actual &&
+ test_cmp expect actual &&
+
+ (
+ cd expired.git &&
+
+ expired="$(ls objects/pack/pack-*.mtimes)" &&
+ test-tool pack-mtimes $(basename $expired) >out &&
+ cut -d" " -f1 out | sort >../moved.got &&
+
+ # Ensure that there are as many objects with the
+ # expected mtime as were moved to expired.git.
+ #
+ # In other words, ensure that the recorded
+ # mtimes of any moved objects was written
+ # correctly.
+ grep " $mtime$" out >matching &&
+ test_line_count = $(wc -l <../moved.want) matching
+ ) &&
+ test_cmp moved.want moved.got
+ )
+'
+
+generate_random_blob() {
+ test-tool genrandom "$@" >blob &&
+ git hash-object -w -t blob blob &&
+ rm blob
+}
+
+pack_random_blob () {
+ generate_random_blob "$@" &&
+ git repack -d -q >/dev/null
+}
+
+generate_cruft_pack () {
+ pack_random_blob "$@" >/dev/null &&
+
+ ls $packdir/pack-*.pack | xargs -n 1 basename >in &&
+ pack="$(git pack-objects --cruft $packdir/pack <in)" &&
+ git prune-packed &&
+
+ echo "$packdir/pack-$pack.mtimes"
+}
+
+test_expect_success '--max-cruft-size creates new packs when above threshold' '
+ git init max-cruft-size-large &&
+ (
+ cd max-cruft-size-large &&
+ test_commit base &&
+
+ foo="$(pack_random_blob foo $((1*1024*1024)))" &&
+ git repack --cruft -d &&
+ cruft_foo="$(ls $packdir/pack-*.mtimes)" &&
+
+ bar="$(pack_random_blob bar $((1*1024*1024)))" &&
+ git repack --cruft -d --max-cruft-size=1M &&
+ cruft_bar="$(ls $packdir/pack-*.mtimes | grep -v $cruft_foo)" &&
+
+ test-tool pack-mtimes $(basename "$cruft_foo") >foo.objects &&
+ test-tool pack-mtimes $(basename "$cruft_bar") >bar.objects &&
+
+ grep "^$foo" foo.objects &&
+ test_line_count = 1 foo.objects &&
+ grep "^$bar" bar.objects &&
+ test_line_count = 1 bar.objects
+ )
+'
+
+test_expect_success '--max-cruft-size combines existing packs when below threshold' '
+ git init max-cruft-size-small &&
+ (
+ cd max-cruft-size-small &&
+ test_commit base &&
+
+ foo="$(pack_random_blob foo $((1*1024*1024)))" &&
+ git repack --cruft -d &&
+
+ bar="$(pack_random_blob bar $((1*1024*1024)))" &&
+ git repack --cruft -d --max-cruft-size=10M &&
+
+ cruft=$(ls $packdir/pack-*.mtimes) &&
+ test-tool pack-mtimes $(basename "$cruft") >cruft.objects &&
+
+ grep "^$foo" cruft.objects &&
+ grep "^$bar" cruft.objects &&
+ test_line_count = 2 cruft.objects
+ )
+'
+
+test_expect_success '--max-cruft-size combines smaller packs first' '
+ git init max-cruft-size-consume-small &&
+ (
+ cd max-cruft-size-consume-small &&
+
+ test_commit base &&
+ git repack -ad &&
+
+ cruft_foo="$(generate_cruft_pack foo 524288)" && # 0.5 MiB
+ cruft_bar="$(generate_cruft_pack bar 524288)" && # 0.5 MiB
+ cruft_baz="$(generate_cruft_pack baz 1048576)" && # 1.0 MiB
+ cruft_quux="$(generate_cruft_pack quux 1572864)" && # 1.5 MiB
+
+ test-tool pack-mtimes "$(basename $cruft_foo)" >expect.raw &&
+ test-tool pack-mtimes "$(basename $cruft_bar)" >>expect.raw &&
+ sort expect.raw >expect.objects &&
+
+ # repacking with `--max-cruft-size=2M` should combine
+ # both 0.5 MiB packs together, instead of, say, one of
+ # the 0.5 MiB packs with the 1.0 MiB pack
+ ls $packdir/pack-*.mtimes | sort >cruft.before &&
+ git repack -d --cruft --max-cruft-size=2M &&
+ ls $packdir/pack-*.mtimes | sort >cruft.after &&
+
+ comm -13 cruft.before cruft.after >cruft.new &&
+ comm -23 cruft.before cruft.after >cruft.removed &&
+
+ test_line_count = 1 cruft.new &&
+ test_line_count = 2 cruft.removed &&
+
+ # the two smaller packs should be rolled up first
+ printf "%s\n" $cruft_foo $cruft_bar | sort >expect.removed &&
+ test_cmp expect.removed cruft.removed &&
+
+ # ...and contain the set of objects rolled up
+ test-tool pack-mtimes "$(basename $(cat cruft.new))" >actual.raw &&
+ sort actual.raw >actual.objects &&
+
+ test_cmp expect.objects actual.objects
+ )
+'
+
+test_expect_success 'setup --max-cruft-size with freshened objects' '
+ git init max-cruft-size-freshen &&
+ (
+ cd max-cruft-size-freshen &&
+
+ test_commit base &&
+ git repack -ad &&
+
+ foo="$(generate_random_blob foo 64)" &&
+ test-tool chmtime --get -10000 \
+ "$objdir/$(test_oid_to_path "$foo")" >foo.mtime &&
+
+ git repack --cruft -d &&
+
+ cruft="$(ls $packdir/pack-*.mtimes)" &&
+ test-tool pack-mtimes "$(basename $cruft)" >actual &&
+ echo "$foo $(cat foo.mtime)" >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success '--max-cruft-size with freshened objects (loose)' '
+ (
+ cd max-cruft-size-freshen &&
+
+ # regenerate the object, setting its mtime to be more recent
+ foo="$(generate_random_blob foo 64)" &&
+ test-tool chmtime --get -100 \
+ "$objdir/$(test_oid_to_path "$foo")" >foo.mtime &&
+
+ git repack --cruft -d &&
+
+ cruft="$(ls $packdir/pack-*.mtimes)" &&
+ test-tool pack-mtimes "$(basename $cruft)" >actual &&
+ echo "$foo $(cat foo.mtime)" >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success '--max-cruft-size with freshened objects (packed)' '
+ (
+ cd max-cruft-size-freshen &&
+
+ # regenerate the object and store it in a packfile,
+ # setting its mtime to be more recent
+ #
+ # store it alongside another cruft object so that we
+ # do not create an identical copy of the existing
+ # cruft pack (which contains $foo).
+ foo="$(generate_random_blob foo 64)" &&
+ bar="$(generate_random_blob bar 64)" &&
+ foo_pack="$(printf "%s\n" $foo $bar | git pack-objects $packdir/pack)" &&
+ git prune-packed &&
+
+ test-tool chmtime --get -10 \
+ "$packdir/pack-$foo_pack.pack" >foo.mtime &&
+
+ git repack --cruft -d &&
+
+ cruft="$(ls $packdir/pack-*.mtimes)" &&
+ test-tool pack-mtimes "$(basename $cruft)" >actual &&
+ echo "$foo $(cat foo.mtime)" >expect.raw &&
+ echo "$bar $(cat foo.mtime)" >>expect.raw &&
+ sort expect.raw >expect &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success '--max-cruft-size with pruning' '
+ git init max-cruft-size-prune &&
+ (
+ cd max-cruft-size-prune &&
+
+ test_commit base &&
+ foo="$(generate_random_blob foo $((1024*1024)))" &&
+ bar="$(generate_random_blob bar $((1024*1024)))" &&
+ baz="$(generate_random_blob baz $((1024*1024)))" &&
+
+ test-tool chmtime -10000 "$objdir/$(test_oid_to_path "$foo")" &&
+
+ git repack -d --cruft --max-cruft-size=1M &&
+
+ # backdate the mtimes of all cruft packs to validate
+ # that they were rewritten as a result of pruning
+ ls $packdir/pack-*.mtimes | sort >cruft.before &&
+ for cruft in $(cat cruft.before)
+ do
+ mtime="$(test-tool chmtime --get -10000 "$cruft")" &&
+ echo $cruft $mtime >>mtimes || return 1
+ done &&
+
+ # repack (and prune) with a --max-cruft-size to ensure
+ # that we appropriately split the resulting set of packs
+ git repack -d --cruft --max-cruft-size=1M \
+ --cruft-expiration=10.seconds.ago &&
+ ls $packdir/pack-*.mtimes | sort >cruft.after &&
+
+ for cruft in $(cat cruft.after)
+ do
+ old_mtime="$(grep $cruft mtimes | cut -d" " -f2)" &&
+ new_mtime="$(test-tool chmtime --get $cruft)" &&
+ test $old_mtime -lt $new_mtime || return 1
+ done &&
+
+ test_line_count = 3 cruft.before &&
+ test_line_count = 2 cruft.after &&
+ test_must_fail git cat-file -e $foo &&
+ git cat-file -e $bar &&
+ git cat-file -e $baz
+ )
+'
+
+test_expect_success '--max-cruft-size ignores non-local packs' '
+ repo="max-cruft-size-non-local" &&
+ git init $repo &&
+ (
+ cd $repo &&
+ test_commit base &&
+ generate_random_blob foo 64 &&
+ git repack --cruft -d
+ ) &&
+
+ git clone --reference=$repo $repo $repo-alt &&
+ (
+ cd $repo-alt &&
+
+ test_commit other &&
+ generate_random_blob bar 64 &&
+
+ # ensure that we do not attempt to pick up packs from
+ # the non-alternated repository, which would result in a
+ # crash
+ git repack --cruft --max-cruft-size=1M -d
+ )
+'
+
+test_expect_success 'reachable packs are preferred over cruft ones' '
+ repo="cruft-preferred-packs" &&
+ git init "$repo" &&
+ (
+ cd "$repo" &&
+
+ # This test needs to exercise careful control over when a MIDX
+ # is and is not written. Unset the corresponding TEST variable
+ # accordingly.
+ sane_unset GIT_TEST_MULTI_PACK_INDEX &&
+
+ test_commit base &&
+ test_commit --no-tag cruft &&
+
+ non_cruft="$(echo base | git pack-objects --revs $packdir/pack)" &&
+ # Write a cruft pack which both (a) sorts ahead of the non-cruft
+ # pack in lexical order, and (b) has an older mtime to appease
+ # the MIDX preferred pack selection routine.
+ cruft="$(echo pack-$non_cruft.pack | git pack-objects --cruft $packdir/pack-A)" &&
+ test-tool chmtime -1000 $packdir/pack-A-$cruft.pack &&
+
+ test_commit other &&
+ git repack -d &&
+
+ git repack --geometric 2 -d --write-midx --write-bitmap-index &&
+
+ # After repacking, there are two packs left: one reachable one
+ # (which is the result of combining both of the existing two
+ # non-cruft packs), and one cruft pack.
+ find .git/objects/pack -type f -name "*.pack" >packs &&
+ test_line_count = 2 packs &&
+
+ # Make sure that the pack we just wrote is marked as preferred,
+ # not the cruft one.
+ pack="$(test-tool read-midx --preferred-pack $objdir)" &&
+ test_path_is_missing "$packdir/$(basename "$pack" ".idx").mtimes"
+ )
+'
+
+test_done
#!/bin/sh
test_description='check that example code compiles and runs'
+
+TEST_PASSES_SANITIZE_LEAK=true
. ./test-lib.sh
test_expect_success 'decorate' '
#include "advice.h"
#include "strvec.h"
#include "repository.h"
-#include "config.h"
+#include "parse.h"
#include "dir.h"
#include "environment.h"
#include "gettext.h"
#include "git-compat-util.h"
-#include "hex.h"
+#include "hex-ll.h"
#include "strbuf.h"
#include "url.h"
#include "git-compat-util.h"
#include "gettext.h"
-#include "hex.h"
+#include "hex-ll.h"
#include "strbuf.h"
#include "urlmatch.h"
*/
#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"
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) ||
* 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"
#include "git-compat-util.h"
-#include "config.h"
+#include "parse.h"
#include "run-command.h"
#include "write-or-die.h"