]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'jk/chunk-bounds'
authorJunio C Hamano <gitster@pobox.com>
Mon, 23 Oct 2023 20:56:36 +0000 (13:56 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 23 Oct 2023 20:56:36 +0000 (13:56 -0700)
The codepaths that read "chunk" formatted files have been corrected
to pay attention to the chunk size and notice broken files.

* jk/chunk-bounds: (21 commits)
  t5319: make corrupted large-offset test more robust
  chunk-format: drop pair_chunk_unsafe()
  commit-graph: detect out-of-order BIDX offsets
  commit-graph: check bounds when accessing BIDX chunk
  commit-graph: check bounds when accessing BDAT chunk
  commit-graph: bounds-check generation overflow chunk
  commit-graph: check size of generations chunk
  commit-graph: bounds-check base graphs chunk
  commit-graph: detect out-of-bounds extra-edges pointers
  commit-graph: check size of commit data chunk
  midx: check size of revindex chunk
  midx: bounds-check large offset chunk
  midx: check size of object offset chunk
  midx: enforce chunk alignment on reading
  midx: check size of pack names chunk
  commit-graph: check consistency of fanout table
  midx: check size of oid lookup chunk
  commit-graph: check size of oid fanout chunk
  midx: stop ignoring malformed oid fanout chunk
  t: add library for munging chunk-format files
  ...

116 files changed:
.github/workflows/coverity.yml [new file with mode: 0644]
.mailmap
Documentation/RelNotes/2.43.0.txt
Documentation/config/gc.txt
Documentation/git-cat-file.txt
Documentation/git-gc.txt
Documentation/git-interpret-trailers.txt
Documentation/git-pack-objects.txt
Documentation/git-repack.txt
Documentation/git-status.txt
Documentation/git-worktree.txt
Documentation/gittutorial.txt
Documentation/user-manual.txt
Makefile
attr.c
builtin/cat-file.c
builtin/commit-graph.c
builtin/diff.c
builtin/gc.c
builtin/interpret-trailers.c
builtin/log.c
builtin/merge-tree.c
builtin/merge.c
builtin/pack-objects.c
builtin/rebase.c
builtin/repack.c
builtin/submodule--helper.c
bulk-checkin.c
bulk-checkin.h
color.c
commit-graph.c
commit-reach.c
config.c
config.h
daemon.c
decorate.c
decorate.h
diagnose.c
diff-lib.c
diff.c
diff.h
dir.c
dir.h
entry.c
entry.h
hex-ll.c [new file with mode: 0644]
hex-ll.h [new file with mode: 0644]
hex.c
hex.h
line-log.c
line-log.h
mailinfo.c
merge-ort.c
merge-recursive.c
merge-recursive.h
object-file.c
pack-bitmap-write.c
pack-objects.c
pack-revindex.c
parse-options.c
parse-options.h
parse.c [new file with mode: 0644]
parse.h [new file with mode: 0644]
pathspec.c
po/README.md
preload-index.c
pretty.c
progress.c
prompt.c
rebase.c
refs/files-backend.c
refs/ref-cache.c
revision.c
strbuf.c
t/helper/test-env-helper.c
t/helper/test-example-decorate.c
t/helper/test-find-pack.c [new file with mode: 0644]
t/helper/test-tool.c
t/helper/test-tool.h
t/t0081-find-pack.sh [new file with mode: 0755]
t/t1500-rev-parse.sh
t/t4052-stat-output.sh
t/t4068-diff-symmetric-merge-base.sh
t/t4205-log-pretty-formats.sh
t/t4214-log-graph-octopus.sh
t/t4215-log-skewed-merges.sh
t/t4217-log-limit.sh
t/t4300-merge-tree.sh
t/t4301-merge-tree-write-tree.sh
t/t5317-pack-objects-filter-objects.sh
t/t5324-split-commit-graph.sh
t/t5328-commit-graph-64bit-time.sh
t/t5521-pull-options.sh
t/t5811-proto-disable-git.sh
t/t6009-rev-list-parent.sh
t/t6416-recursive-corner-cases.sh
t/t6433-merge-toplevel.sh
t/t6437-submodule-merge.sh
t/t6500-gc.sh
t/t6700-tree-depth.sh
t/t7419-submodule-set-branch.sh
t/t7420-submodule-set-url.sh
t/t7513-interpret-trailers.sh
t/t7602-merge-octopus-many.sh
t/t7603-merge-reduce-heads.sh
t/t7607-merge-state.sh
t/t7608-merge-messages.sh
t/t7700-repack.sh
t/t7704-repack-cruft.sh [new file with mode: 0755]
t/t9004-example.sh
unpack-trees.c
url.c
urlmatch.c
wrapper.c
wrapper.h
write-or-die.c

diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml
new file mode 100644 (file)
index 0000000..e5532d3
--- /dev/null
@@ -0,0 +1,163 @@
+name: Coverity
+
+# This GitHub workflow automates submitting builds to Coverity Scan. To enable it,
+# set the repository variable `ENABLE_COVERITY_SCAN_FOR_BRANCHES` (for details, see
+# https://docs.github.com/en/actions/learn-github-actions/variables) to a JSON
+# string array containing the names of the branches for which the workflow should be
+# run, e.g. `["main", "next"]`.
+#
+# In addition, two repository secrets must be set (for details how to add secrets, see
+# https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions):
+# `COVERITY_SCAN_EMAIL` and `COVERITY_SCAN_TOKEN`. The former specifies the
+# email to which the Coverity reports should be sent and the latter can be
+# obtained from the Project Settings tab of the Coverity project).
+#
+# The workflow runs on `ubuntu-latest` by default. This can be overridden by setting
+# the repository variable `ENABLE_COVERITY_SCAN_ON_OS` to a JSON string array specifying
+# the operating systems, e.g. `["ubuntu-latest", "windows-latest"]`.
+#
+# By default, the builds are submitted to the Coverity project `git`. To override this,
+# set the repository variable `COVERITY_PROJECT`.
+
+on:
+  push:
+
+defaults:
+  run:
+    shell: bash
+
+jobs:
+  coverity:
+    if: contains(fromJSON(vars.ENABLE_COVERITY_SCAN_FOR_BRANCHES || '[""]'), github.ref_name)
+    strategy:
+      matrix:
+        os: ${{ fromJSON(vars.ENABLE_COVERITY_SCAN_ON_OS || '["ubuntu-latest"]') }}
+    runs-on: ${{ matrix.os }}
+    env:
+      COVERITY_PROJECT: ${{ vars.COVERITY_PROJECT || 'git' }}
+      COVERITY_LANGUAGE: cxx
+      COVERITY_PLATFORM: overridden-below
+    steps:
+      - uses: actions/checkout@v3
+      - name: install minimal Git for Windows SDK
+        if: contains(matrix.os, 'windows')
+        uses: git-for-windows/setup-git-for-windows-sdk@v1
+      - run: ci/install-dependencies.sh
+        if: contains(matrix.os, 'ubuntu') || contains(matrix.os, 'macos')
+        env:
+          runs_on_pool: ${{ matrix.os }}
+
+      # The Coverity site says the tool is usually updated twice yearly, so the
+      # MD5 of download can be used to determine whether there's been an update.
+      - name: get the Coverity Build Tool hash
+        id: lookup
+        run: |
+          case "${{ matrix.os }}" in
+          *windows*)
+            COVERITY_PLATFORM=win64
+            COVERITY_TOOL_FILENAME=cov-analysis.zip
+            MAKEFLAGS=-j$(nproc)
+            ;;
+          *macos*)
+            COVERITY_PLATFORM=macOSX
+            COVERITY_TOOL_FILENAME=cov-analysis.dmg
+            MAKEFLAGS=-j$(sysctl -n hw.physicalcpu)
+            ;;
+          *ubuntu*)
+            COVERITY_PLATFORM=linux64
+            COVERITY_TOOL_FILENAME=cov-analysis.tgz
+            MAKEFLAGS=-j$(nproc)
+            ;;
+          *)
+            echo '::error::unhandled OS ${{ matrix.os }}' >&2
+            exit 1
+            ;;
+          esac
+          echo "COVERITY_PLATFORM=$COVERITY_PLATFORM" >>$GITHUB_ENV
+          echo "COVERITY_TOOL_FILENAME=$COVERITY_TOOL_FILENAME" >>$GITHUB_ENV
+          echo "MAKEFLAGS=$MAKEFLAGS" >>$GITHUB_ENV
+          MD5=$(curl https://scan.coverity.com/download/$COVERITY_LANGUAGE/$COVERITY_PLATFORM \
+                   --fail \
+                   --form token='${{ secrets.COVERITY_SCAN_TOKEN }}' \
+                   --form project="$COVERITY_PROJECT" \
+                   --form md5=1)
+          case $? in
+          0) ;; # okay
+          22) # 40x, i.e. access denied
+            echo "::error::incorrect token or project?" >&2
+            exit 1
+            ;;
+          *) # other error
+            echo "::error::Failed to retrieve MD5" >&2
+            exit 1
+            ;;
+          esac
+          echo "hash=$MD5" >>$GITHUB_OUTPUT
+
+      # Try to cache the tool to avoid downloading 1GB+ on every run.
+      # A cache miss will add ~30s to create, but a cache hit will save minutes.
+      - name: restore the Coverity Build Tool
+        id: cache
+        uses: actions/cache/restore@v3
+        with:
+          path: ${{ runner.temp }}/cov-analysis
+          key: cov-build-${{ env.COVERITY_LANGUAGE }}-${{ env.COVERITY_PLATFORM }}-${{ steps.lookup.outputs.hash }}
+      - name: download the Coverity Build Tool (${{ env.COVERITY_LANGUAGE }} / ${{ env.COVERITY_PLATFORM}})
+        if: steps.cache.outputs.cache-hit != 'true'
+        run: |
+          curl https://scan.coverity.com/download/$COVERITY_LANGUAGE/$COVERITY_PLATFORM \
+            --fail --no-progress-meter \
+            --output $RUNNER_TEMP/$COVERITY_TOOL_FILENAME \
+            --form token='${{ secrets.COVERITY_SCAN_TOKEN }}' \
+            --form project="$COVERITY_PROJECT"
+      - name: extract the Coverity Build Tool
+        if: steps.cache.outputs.cache-hit != 'true'
+        run: |
+          case "$COVERITY_TOOL_FILENAME" in
+          *.tgz)
+            mkdir $RUNNER_TEMP/cov-analysis &&
+            tar -xzf $RUNNER_TEMP/$COVERITY_TOOL_FILENAME --strip 1 -C $RUNNER_TEMP/cov-analysis
+            ;;
+          *.dmg)
+            cd $RUNNER_TEMP &&
+            attach="$(hdiutil attach $COVERITY_TOOL_FILENAME)" &&
+            volume="$(echo "$attach" | cut -f 3 | grep /Volumes/)" &&
+            mkdir cov-analysis &&
+            cd cov-analysis &&
+            sh "$volume"/cov-analysis-macosx-*.sh &&
+            ls -l &&
+            hdiutil detach "$volume"
+            ;;
+          *.zip)
+            cd $RUNNER_TEMP &&
+            mkdir cov-analysis-tmp &&
+            unzip -d cov-analysis-tmp $COVERITY_TOOL_FILENAME &&
+            mv cov-analysis-tmp/* cov-analysis
+            ;;
+          *)
+            echo "::error::unhandled archive type: $COVERITY_TOOL_FILENAME" >&2
+            exit 1
+            ;;
+          esac
+      - name: cache the Coverity Build Tool
+        if: steps.cache.outputs.cache-hit != 'true'
+        uses: actions/cache/save@v3
+        with:
+          path: ${{ runner.temp }}/cov-analysis
+          key: cov-build-${{ env.COVERITY_LANGUAGE }}-${{ env.COVERITY_PLATFORM }}-${{ steps.lookup.outputs.hash }}
+      - name: build with cov-build
+        run: |
+          export PATH="$RUNNER_TEMP/cov-analysis/bin:$PATH" &&
+          cov-configure --gcc &&
+          cov-build --dir cov-int make
+      - name: package the build
+        run: tar -czvf cov-int.tgz cov-int
+      - name: submit the build to Coverity Scan
+        run: |
+          curl \
+            --fail \
+            --form token='${{ secrets.COVERITY_SCAN_TOKEN }}' \
+            --form email='${{ secrets.COVERITY_SCAN_EMAIL }}' \
+            --form file=@cov-int.tgz \
+            --form version='${{ github.sha }}' \
+            "https://scan.coverity.com/builds?project=$COVERITY_PROJECT"
index dc31d70b8c1e98b14a4e76d5fa0e35cfd0bccc34..82129be449f94b9872087717a03f1e3d5e2fac1a 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -59,9 +59,9 @@ David Reiss <dreiss@facebook.com> <dreiss@dreiss-vmware.(none)>
 David S. Miller <davem@davemloft.net>
 David Turner <novalis@novalis.org> <dturner@twopensource.com>
 David Turner <novalis@novalis.org> <dturner@twosigma.com>
-Derrick Stolee <derrickstolee@github.com> <stolee@gmail.com>
-Derrick Stolee <derrickstolee@github.com> Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com>
-Derrick Stolee <derrickstolee@github.com> <dstolee@microsoft.com>
+Derrick Stolee <stolee@gmail.com> <derrickstolee@github.com>
+Derrick Stolee <stolee@gmail.com> Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com>
+Derrick Stolee <stolee@gmail.com> <dstolee@microsoft.com>
 Deskin Miller <deskinm@umich.edu>
 Đoàn Trần Công Danh <congdanhqx@gmail.com> Doan Tran Cong Danh
 Dirk Süsserott <newsletter@dirk.my1.cc>
index 494e13e827ea7dacf6b2194a9b2ef5b9bd38fe7e..a1a2da191c0d02b3d58a6ea96cf942666bd00649 100644 (file)
@@ -83,6 +83,15 @@ UI, Workflows & Features
  * "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.
 
@@ -111,6 +120,19 @@ 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
 -----------------
@@ -213,6 +235,32 @@ 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).
@@ -234,3 +282,7 @@ Fixes since v2.42
    (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).
index ca47eb200882a251ddf7ad644151bb6e896b242f..c6e3acc99df5bf077808100b517852d9eb1b74df 100644 (file)
@@ -86,6 +86,12 @@ gc.cruftPacks::
        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
@@ -145,6 +151,22 @@ Multiple hooks are supported, but all must exit successfully, else the
 operation (either generating a cruft pack or unpacking unreachable
 objects) will be halted.
 
+gc.repackFilter::
+       When repacking, use the specified filter to move certain
+       objects into a separate packfile.  See the
+       `--filter=<filter-spec>` option of linkgit:git-repack[1].
+
+gc.repackFilterTo::
+       When repacking and using a filter, see `gc.repackFilter`, the
+       specified location will be used to create the packfile
+       containing the filtered out objects. **WARNING:** The
+       specified location should be accessible, using for example the
+       Git alternates mechanism, otherwise the repo could be
+       considered corrupt by Git as it migh not be able to access the
+       objects in that packfile. See the `--filter-to=<dir>` option
+       of linkgit:git-repack[1] and the `objects/info/alternates`
+       section of linkgit:gitrepository-layout[5].
+
 gc.rerereResolved::
        Records of conflicted merge you resolved earlier are
        kept for this many days when 'git rerere gc' is run.
index 0e4936d182632fb3362376787efd7d633bd623d2..bd95a6c10a7d0b1ee16b619c4b546c8d9c0b0e13 100644 (file)
@@ -3,8 +3,7 @@ git-cat-file(1)
 
 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
 --------
@@ -12,25 +11,24 @@ 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
 -------
@@ -51,8 +49,8 @@ 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.
index 90806fd26aa4ac0d1fef93d25384af3799818896..b5561c458a101c32a51ccca8599d4898e166b174 100644 (file)
@@ -59,6 +59,13 @@ be performed as well.
        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`).
index 55d89614661c5c46c8dd69fb149cfec70d72fc85..418265f044d65000e9ae22235b9801644f9c2170 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git interpret-trailers' [--in-place] [--trim-empty]
-                       [(--trailer <token>[(=|:)<value>])...]
+                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]
                        [--parse] [<file>...]
 
 DESCRIPTION
@@ -31,10 +31,15 @@ the last two lines starting with "Signed-off-by" are trailers.
 
 This command reads commit messages from either the
 <file> arguments or the standard input if no <file> is specified.
-If `--parse` is specified, the output consists of the parsed trailers.
-Otherwise, this command applies the arguments passed using the
-`--trailer` option, if any, to each input file. The result is emitted on the
-standard output.
+If `--parse` is specified, the output consists of the parsed trailers
+coming from the input, without influencing them with any command line
+options or configuration variables.
+
+Otherwise, this command applies `trailer.*` configuration variables
+(which could potentially add new trailers, as well as reposition them),
+as well as any command line arguments that can override configuration
+variables (such as `--trailer=...` which could also add new trailers),
+to each input file. The result is emitted on the standard output.
 
 This command can also operate on the output of linkgit:git-format-patch[1],
 which is more elaborate than a plain commit message. Namely, such output
@@ -48,22 +53,32 @@ are applied to each input and the way any existing trailer in
 the input is changed. They also make it possible to
 automatically add some trailers.
 
-By default, a '<token>=<value>' or '<token>:<value>' argument given
+By default, a '<key>=<value>' or '<key>:<value>' argument given
 using `--trailer` will be appended after the existing trailers only if
-the last trailer has a different (<token>, <value>) pair (or if there
-is no existing trailer). The <token> and <value> parts will be trimmed
+the last trailer has a different (<key>, <value>) pair (or if there
+is no existing trailer). The <key> and <value> parts will be trimmed
 to remove starting and trailing whitespace, and the resulting trimmed
-<token> and <value> will appear in the output like this:
+<key> and <value> will appear in the output like this:
 
 ------------------------------------------------
-token: value
+key: value
 ------------------------------------------------
 
-This means that the trimmed <token> and <value> will be separated by
-`': '` (one colon followed by one space). For convenience, the <token> can be a
-shortened string key (e.g., "sign") instead of the full string which should
-appear before the separator on the output (e.g., "Signed-off-by"). This can be
-configured using the 'trailer.<token>.key' configuration variable.
+This means that the trimmed <key> and <value> will be separated by
+`': '` (one colon followed by one space).
+
+For convenience, a <keyAlias> can be configured to make using `--trailer`
+shorter to type on the command line. This can be configured using the
+'trailer.<keyAlias>.key' configuration variable. The <keyAlias> must be a prefix
+of the full <key> string, although case sensitivity does not matter. For
+example, if you have
+
+------------------------------------------------
+trailer.sign.key "Signed-off-by: "
+------------------------------------------------
+
+in your configuration, you only need to specify `--trailer="sign: foo"`
+on the command line instead of `--trailer="Signed-off-by: foo"`.
 
 By default the new trailer will appear at the end of all the existing
 trailers. If there is no existing trailer, the new trailer will appear
@@ -80,14 +95,14 @@ non-whitespace lines before a line that starts with '---' (followed by a
 space or the end of the line).
 
 When reading trailers, there can be no whitespace before or inside the
-<token>, but any number of regular space and tab characters are allowed
-between the <token> and the separator. There can be whitespaces before,
+<key>, but any number of regular space and tab characters are allowed
+between the <key> and the separator. There can be whitespaces before,
 inside or after the <value>. The <value> may be split over multiple lines
 with each subsequent line starting with at least one whitespace, like
 the "folding" in RFC 822. Example:
 
 ------------------------------------------------
-token: This is a very long value, with spaces and
+key: This is a very long value, with spaces and
   newlines in it.
 ------------------------------------------------
 
@@ -104,35 +119,44 @@ OPTIONS
        the whole trailer will be removed from the output.
        This applies to existing trailers as well as new trailers.
 
---trailer <token>[(=|:)<value>]::
-       Specify a (<token>, <value>) pair that should be applied as a
+--trailer <key>[(=|:)<value>]::
+       Specify a (<key>, <value>) pair that should be applied as a
        trailer to the inputs. See the description of this
        command.
 
 --where <placement>::
 --no-where::
        Specify where all new trailers will be added.  A setting
-       provided with '--where' overrides all configuration variables
+       provided with '--where' overrides the `trailer.where` and any
+       applicable `trailer.<keyAlias>.where` configuration variables
        and applies to all '--trailer' options until the next occurrence of
-       '--where' or '--no-where'. Possible values are `after`, `before`,
-       `end` or `start`.
+       '--where' or '--no-where'. Upon encountering '--no-where', clear the
+       effect of any previous use of '--where', such that the relevant configuration
+       variables are no longer overridden. Possible placements are `after`,
+       `before`, `end` or `start`.
 
 --if-exists <action>::
 --no-if-exists::
        Specify what action will be performed when there is already at
-       least one trailer with the same <token> in the input.  A setting
-       provided with '--if-exists' overrides all configuration variables
+       least one trailer with the same <key> in the input.  A setting
+       provided with '--if-exists' overrides the `trailer.ifExists` and any
+       applicable `trailer.<keyAlias>.ifExists` configuration variables
        and applies to all '--trailer' options until the next occurrence of
-       '--if-exists' or '--no-if-exists'. Possible actions are `addIfDifferent`,
+       '--if-exists' or '--no-if-exists'. Upon encountering '--no-if-exists, clear the
+       effect of any previous use of '--if-exists, such that the relevant configuration
+       variables are no longer overridden. Possible actions are `addIfDifferent`,
        `addIfDifferentNeighbor`, `add`, `replace` and `doNothing`.
 
 --if-missing <action>::
 --no-if-missing::
        Specify what action will be performed when there is no other
-       trailer with the same <token> in the input.  A setting
-       provided with '--if-missing' overrides all configuration variables
+       trailer with the same <key> in the input.  A setting
+       provided with '--if-missing' overrides the `trailer.ifMissing` and any
+       applicable `trailer.<keyAlias>.ifMissing` configuration variables
        and applies to all '--trailer' options until the next occurrence of
-       '--if-missing' or '--no-if-missing'. Possible actions are `doNothing`
+       '--if-missing' or '--no-if-missing'. Upon encountering '--no-if-missing,
+       clear the effect of any previous use of '--if-missing, such that the relevant
+       configuration variables are no longer overridden. Possible actions are `doNothing`
        or `add`.
 
 --only-trailers::
@@ -140,16 +164,19 @@ OPTIONS
 
 --only-input::
        Output only trailers that exist in the input; do not add any
-       from the command-line or by following configured `trailer.*`
-       rules.
+       from the command-line or by applying `trailer.*` configuration
+       variables.
 
 --unfold::
-       Remove any whitespace-continuation in trailers, so that each
-       trailer appears on a line by itself with its full content.
+       If a trailer has a value that runs over multiple lines (aka "folded"),
+       reformat the value into a single line.
 
 --parse::
        A convenience alias for `--only-trailers --only-input
-       --unfold`.
+       --unfold`. This makes it easier to only see the trailers coming from the
+       input without influencing them with any command line options or
+       configuration variables, while also making the output machine-friendly with
+       --unfold.
 
 --no-divider::
        Do not treat `---` as the end of the commit message. Use this
@@ -170,11 +197,11 @@ used when another separator is not specified in the config for this
 trailer.
 +
 For example, if the value for this option is "%=$", then only lines
-using the format '<token><sep><value>' with <sep> containing '%', '='
+using the format '<key><sep><value>' with <sep> containing '%', '='
 or '$' and then spaces will be considered trailers. And '%' will be
 the default separator used, so by default trailers will appear like:
-'<token>% <value>' (one percent sign and one space will appear between
-the token and the value).
+'<key>% <value>' (one percent sign and one space will appear between
+the key and the value).
 
 trailer.where::
        This option tells where a new trailer will be added.
@@ -188,41 +215,41 @@ If it is `start`, then each new trailer will appear at the start,
 instead of the end, of the existing trailers.
 +
 If it is `after`, then each new trailer will appear just after the
-last trailer with the same <token>.
+last trailer with the same <key>.
 +
 If it is `before`, then each new trailer will appear just before the
-first trailer with the same <token>.
+first trailer with the same <key>.
 
 trailer.ifexists::
        This option makes it possible to choose what action will be
        performed when there is already at least one trailer with the
-       same <token> in the input.
+       same <key> in the input.
 +
 The valid values for this option are: `addIfDifferentNeighbor` (this
 is the default), `addIfDifferent`, `add`, `replace` or `doNothing`.
 +
 With `addIfDifferentNeighbor`, a new trailer will be added only if no
-trailer with the same (<token>, <value>) pair is above or below the line
+trailer with the same (<key>, <value>) pair is above or below the line
 where the new trailer will be added.
 +
 With `addIfDifferent`, a new trailer will be added only if no trailer
-with the same (<token>, <value>) pair is already in the input.
+with the same (<key>, <value>) pair is already in the input.
 +
 With `add`, a new trailer will be added, even if some trailers with
-the same (<token>, <value>) pair are already in the input.
+the same (<key>, <value>) pair are already in the input.
 +
-With `replace`, an existing trailer with the same <token> will be
+With `replace`, an existing trailer with the same <key> will be
 deleted and the new trailer will be added. The deleted trailer will be
-the closest one (with the same <token>) to the place where the new one
+the closest one (with the same <key>) to the place where the new one
 will be added.
 +
 With `doNothing`, nothing will be done; that is no new trailer will be
-added if there is already one with the same <token> in the input.
+added if there is already one with the same <key> in the input.
 
 trailer.ifmissing::
        This option makes it possible to choose what action will be
        performed when there is not yet any trailer with the same
-       <token> in the input.
+       <key> in the input.
 +
 The valid values for this option are: `add` (this is the default) and
 `doNothing`.
@@ -231,34 +258,40 @@ With `add`, a new trailer will be added.
 +
 With `doNothing`, nothing will be done.
 
-trailer.<token>.key::
-       This `key` will be used instead of <token> in the trailer. At
-       the end of this key, a separator can appear and then some
-       space characters. By default the only valid separator is ':',
-       but this can be changed using the `trailer.separators` config
-       variable.
+trailer.<keyAlias>.key::
+       Defines a <keyAlias> for the <key>. The <keyAlias> must be a
+       prefix (case does not matter) of the <key>. For example, in `git
+       config trailer.ack.key "Acked-by"` the "Acked-by" is the <key> and
+       the "ack" is the <keyAlias>. This configuration allows the shorter
+       `--trailer "ack:..."` invocation on the command line using the "ack"
+       <keyAlias> instead of the longer `--trailer "Acked-by:..."`.
++
+At the end of the <key>, a separator can appear and then some
+space characters. By default the only valid separator is ':',
+but this can be changed using the `trailer.separators` config
+variable.
 +
-If there is a separator, then the key will be used instead of both the
-<token> and the default separator when adding the trailer.
+If there is a separator in the key, then it overrides the default
+separator when adding the trailer.
 
-trailer.<token>.where::
+trailer.<keyAlias>.where::
        This option takes the same values as the 'trailer.where'
        configuration variable and it overrides what is specified by
-       that option for trailers with the specified <token>.
+       that option for trailers with the specified <keyAlias>.
 
-trailer.<token>.ifexists::
+trailer.<keyAlias>.ifexists::
        This option takes the same values as the 'trailer.ifexists'
        configuration variable and it overrides what is specified by
-       that option for trailers with the specified <token>.
+       that option for trailers with the specified <keyAlias>.
 
-trailer.<token>.ifmissing::
+trailer.<keyAlias>.ifmissing::
        This option takes the same values as the 'trailer.ifmissing'
        configuration variable and it overrides what is specified by
-       that option for trailers with the specified <token>.
+       that option for trailers with the specified <keyAlias>.
 
-trailer.<token>.command::
-       Deprecated in favor of 'trailer.<token>.cmd'.
-       This option behaves in the same way as 'trailer.<token>.cmd', except
+trailer.<keyAlias>.command::
+       Deprecated in favor of 'trailer.<keyAlias>.cmd'.
+       This option behaves in the same way as 'trailer.<keyAlias>.cmd', except
        that it doesn't pass anything as argument to the specified command.
        Instead the first occurrence of substring $ARG is replaced by the
        <value> that would be passed as argument.
@@ -266,29 +299,29 @@ trailer.<token>.command::
 Note that $ARG in the user's command is
 only replaced once and that the original way of replacing $ARG is not safe.
 +
-When both 'trailer.<token>.cmd' and 'trailer.<token>.command' are given
-for the same <token>, 'trailer.<token>.cmd' is used and
-'trailer.<token>.command' is ignored.
+When both 'trailer.<keyAlias>.cmd' and 'trailer.<keyAlias>.command' are given
+for the same <keyAlias>, 'trailer.<keyAlias>.cmd' is used and
+'trailer.<keyAlias>.command' is ignored.
 
-trailer.<token>.cmd::
+trailer.<keyAlias>.cmd::
        This option can be used to specify a shell command that will be called
-       once to automatically add a trailer with the specified <token>, and then
-       called each time a '--trailer <token>=<value>' argument is specified to
+       once to automatically add a trailer with the specified <keyAlias>, and then
+       called each time a '--trailer <keyAlias>=<value>' argument is specified to
        modify the <value> of the trailer that this option would produce.
 +
 When the specified command is first called to add a trailer
-with the specified <token>, the behavior is as if a special
-'--trailer <token>=<value>' argument was added at the beginning
+with the specified <keyAlias>, the behavior is as if a special
+'--trailer <keyAlias>=<value>' argument was added at the beginning
 of the "git interpret-trailers" command, where <value>
 is taken to be the standard output of the command with any
 leading and trailing whitespace trimmed off.
 +
-If some '--trailer <token>=<value>' arguments are also passed
+If some '--trailer <keyAlias>=<value>' arguments are also passed
 on the command line, the command is called again once for each
-of these arguments with the same <token>. And the <value> part
+of these arguments with the same <keyAlias>. And the <value> part
 of these arguments, if any, will be passed to the command as its
 first argument. This way the command can produce a <value> computed
-from the <value> passed in the '--trailer <token>=<value>' argument.
+from the <value> passed in the '--trailer <keyAlias>=<value>' argument.
 
 EXAMPLES
 --------
index dea7eacb0fff24de821d58c4759d35cb39a446bf..e32404c6aaee30f39f3f128839171601999bccb3 100644 (file)
@@ -296,8 +296,8 @@ So does `git bundle` (see linkgit:git-bundle[1]) when it creates a bundle.
        nevertheless.
 
 --filter=<filter-spec>::
-       Requires `--stdout`.  Omits certain objects (usually blobs) from
-       the resulting packfile.  See linkgit:git-rev-list[1] for valid
+       Omits certain objects (usually blobs) from the resulting
+       packfile.  See linkgit:git-rev-list[1] for valid
        `<filter-spec>` forms.
 
 --no-filter::
index 4017157949e6d764a619f870c0eed538d3e99f64..893b8a2fea4319526d1f4dbb3e05f00fec9c2485 100644 (file)
@@ -74,6 +74,17 @@ to the new separate pack will be written.
        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
@@ -143,6 +154,29 @@ depth is 4095.
        a larger and slower repository; see the discussion in
        `pack.packSizeLimit`.
 
+--filter=<filter-spec>::
+       Remove objects matching the filter specification from the
+       resulting packfile and put them into a separate packfile. Note
+       that objects used in the working directory are not filtered
+       out. So for the split to fully work, it's best to perform it
+       in a bare repo and to use the `-a` and `-d` options along with
+       this option.  Also `--no-write-bitmap-index` (or the
+       `repack.writebitmaps` config option set to `false`) should be
+       used otherwise writing bitmap index will fail, as it supposes
+       a single packfile containing all the objects. See
+       linkgit:git-rev-list[1] for valid `<filter-spec>` forms.
+
+--filter-to=<dir>::
+       Write the pack containing filtered out objects to the
+       directory `<dir>`. Only useful with `--filter`. This can be
+       used for putting the pack on a separate object directory that
+       is accessed through the Git alternates mechanism. **WARNING:**
+       If the packfile containing the filtered out objects is not
+       accessible, the repo can become corrupt as it might not be
+       possible to access the objects in that packfile. See the
+       `objects` and `objects/info/alternates` sections of
+       linkgit:gitrepository-layout[5].
+
 -b::
 --write-bitmap-index::
        Write a reachability bitmap index as part of the repack. This
index b27d127b5e24f2be3c1ff303cbc1beef6d9126d9..48f46eb2047375716ff9f7f30c5a247511a3901f 100644 (file)
@@ -246,10 +246,9 @@ U           U    unmerged, both modified
 
 Submodules have more state and instead report
 
-               M    the submodule has a different HEAD than
-                    recorded in the index
-               m    the submodule has modified content
-               ?    the submodule has untracked files
+* 'M' = the submodule has a different HEAD than recorded in the index
+* 'm' = the submodule has modified content
+* '?' = the submodule has untracked files
 
 since modified content or untracked files in a submodule cannot be added
 via `git add` in the superproject to prepare a commit.
index a4fbf5e8386d7d4ae58230b17b534a90461ff421..93d76f5d665a57a8bdca565add921cbd8b76bc37 100644 (file)
@@ -286,7 +286,8 @@ rules and how to access refs of one worktree from another.
 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
@@ -363,8 +364,8 @@ linked worktree `git rev-parse --git-path HEAD` returns
 `/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
index c7cadd8aaf1a0e6606d2b559b674526bc687849a..4759408788070baba26b94e91dde74a9e6c6dda5 100644 (file)
@@ -137,10 +137,10 @@ which will automatically notice any modified (but not new) files, add
 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.
index 4281396093d7c92d22ccf2a8f7dd7c98400445e2..d8dbe6b56d42a693461d7deae9ac76e214f3d1e4 100644 (file)
@@ -1122,7 +1122,7 @@ choosing "Stage Hunk For Commit").
 === 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
index 003e63b792e39731e3baa771f3ff669917f98838..9c6a2f125f82229a3275ca72fb44a38e9402e74a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -800,6 +800,7 @@ TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
 TEST_BUILTINS_OBJS += test-env-helper.o
 TEST_BUILTINS_OBJS += test-example-decorate.o
 TEST_BUILTINS_OBJS += test-fast-rebase.o
+TEST_BUILTINS_OBJS += test-find-pack.o
 TEST_BUILTINS_OBJS += test-fsmonitor-client.o
 TEST_BUILTINS_OBJS += test-genrandom.o
 TEST_BUILTINS_OBJS += test-genzeros.o
@@ -1039,6 +1040,7 @@ LIB_OBJS += hash-lookup.o
 LIB_OBJS += hashmap.o
 LIB_OBJS += help.o
 LIB_OBJS += hex.o
+LIB_OBJS += hex-ll.o
 LIB_OBJS += hook.o
 LIB_OBJS += ident.o
 LIB_OBJS += json-writer.o
@@ -1089,6 +1091,7 @@ LIB_OBJS += pack-write.o
 LIB_OBJS += packfile.o
 LIB_OBJS += pager.o
 LIB_OBJS += parallel-checkout.o
+LIB_OBJS += parse.o
 LIB_OBJS += parse-options-cb.o
 LIB_OBJS += parse-options.o
 LIB_OBJS += patch-delta.o
diff --git a/attr.c b/attr.c
index 71c84fbcf865ebeb160b775fe5ca106a89cd4471..3c0b4fb3d9d1704c58cbaf287944a6afe517ef98 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -7,7 +7,7 @@
  */
 
 #include "git-compat-util.h"
-#include "config.h"
+#include "parse.h"
 #include "environment.h"
 #include "exec-cmd.h"
 #include "attr.h"
index 694c8538df2ff8200e214351e5e857530d2a1191..ea8ad601ecc0b7ee2d7eb9c3da2bb70bcfc55cf8 100644 (file)
@@ -922,11 +922,11 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
                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[] = {
index f5e66e9969e6ea96c31b94718ea9fb72367fe134..45d035af6007a2a64833015dbe58300493a55f80 100644 (file)
@@ -328,6 +328,7 @@ cleanup:
        FREE_AND_NULL(options);
        string_list_clear(&pack_indexes, 0);
        strbuf_release(&buf);
+       oidset_clear(&commits);
        return result;
 }
 
index c0f564273afcf00e709af0e2a15b3ef5490078ca..55e7d21755a09c9754d45fea8689cbcbd32939dd 100644 (file)
@@ -474,9 +474,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        repo_init_revisions(the_repository, &rev, prefix);
 
        /* Set up defaults that will apply to both no-index and regular diffs. */
-       rev.diffopt.stat_width = -1;
-       rev.diffopt.stat_name_width = -1;
-       rev.diffopt.stat_graph_width = -1;
+       init_diffstat_widths(&rev.diffopt);
        rev.diffopt.flags.allow_external = 1;
        rev.diffopt.flags.allow_textconv = 1;
 
index 00192ae5d32162229dee52cde1a77a6846c75443..7c11d5ebef052a46cd06b411960fb1c2b3d6ac08 100644 (file)
@@ -52,6 +52,7 @@ static const char * const builtin_gc_usage[] = {
 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;
@@ -61,6 +62,8 @@ static timestamp_t gc_log_expire_time;
 static const char *gc_log_expire = "1.day.ago";
 static const char *prune_expire = "2.weeks.ago";
 static const char *prune_worktrees_expire = "3.months.ago";
+static char *repack_filter;
+static char *repack_filter_to;
 static unsigned long big_pack_threshold;
 static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE;
 
@@ -163,6 +166,7 @@ static void gc_config(void)
        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);
@@ -170,6 +174,9 @@ static void gc_config(void)
        git_config_get_ulong("gc.bigpackthreshold", &big_pack_threshold);
        git_config_get_ulong("pack.deltacachesize", &max_delta_cache_size);
 
+       git_config_get_string("gc.repackfilter", &repack_filter);
+       git_config_get_string("gc.repackfilterto", &repack_filter_to);
+
        git_config(git_default_config, NULL);
 }
 
@@ -347,6 +354,9 @@ static void add_repack_all_option(struct string_list *keep_pack)
                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)
@@ -355,6 +365,11 @@ static void add_repack_all_option(struct string_list *keep_pack)
 
        if (keep_pack)
                for_each_string_list(keep_pack, keep_one_pack, NULL);
+
+       if (repack_filter && *repack_filter)
+               strvec_pushf(&repack, "--filter=%s", repack_filter);
+       if (repack_filter_to && *repack_filter_to)
+               strvec_pushf(&repack, "--filter-to=%s", repack_filter_to);
 }
 
 static void add_repack_incremental_option(void)
@@ -575,6 +590,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                        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),
index a110e69f831f5a911f069784ab708ba092dc105d..033bd1556cf9f8d6e56c2730a3e868500388144e 100644 (file)
@@ -14,7 +14,7 @@
 
 static const char * const git_interpret_trailers_usage[] = {
        N_("git interpret-trailers [--in-place] [--trim-empty]\n"
-          "                       [(--trailer <token>[(=|:)<value>])...]\n"
+          "                       [(--trailer (<key>|<keyAlias>)[(=|:)<value>])...]\n"
           "                       [--parse] [<file>...]"),
        NULL
 };
@@ -100,7 +100,7 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "in-place", &opts.in_place, N_("edit files in place")),
                OPT_BOOL(0, "trim-empty", &opts.trim_empty, N_("trim empty trailers")),
 
-               OPT_CALLBACK(0, "where", &where, N_("action"),
+               OPT_CALLBACK(0, "where", &where, N_("placement"),
                             N_("where to place the new trailer"), option_parse_where),
                OPT_CALLBACK(0, "if-exists", &if_exists, N_("action"),
                             N_("action if trailer already exists"), option_parse_if_exists),
@@ -108,11 +108,11 @@ int cmd_interpret_trailers(int argc, const char **argv, const char *prefix)
                             N_("action if trailer is missing"), option_parse_if_missing),
 
                OPT_BOOL(0, "only-trailers", &opts.only_trailers, N_("output only the trailers")),
-               OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply config rules")),
-               OPT_BOOL(0, "unfold", &opts.unfold, N_("join whitespace-continued values")),
-               OPT_CALLBACK_F(0, "parse", &opts, NULL, N_("set parsing options"),
+               OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply trailer.* configuration variables")),
+               OPT_BOOL(0, "unfold", &opts.unfold, N_("reformat multiline trailer values as single-line values")),
+               OPT_CALLBACK_F(0, "parse", &opts, NULL, N_("alias for --only-trailers --only-input --unfold"),
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_parse),
-               OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat --- specially")),
+               OPT_BOOL(0, "no-divider", &opts.no_divider, N_("do not treat \"---\" as the end of input")),
                OPT_CALLBACK(0, "trailer", &trailers, N_("trailer"),
                                N_("trailer(s) to add"), option_parse_trailer),
                OPT_END()
index 80e1be1645f04c06e540081d1ecf95f78d7dd4b8..ba775d7b5cf886374bd94c9f2e54d5695a3dbaff 100644 (file)
@@ -176,17 +176,15 @@ static void cmd_log_init_defaults(struct rev_info *rev)
        if (default_follow)
                rev->diffopt.flags.default_follow_renames = 1;
        rev->verbose_header = 1;
+       init_diffstat_widths(&rev->diffopt);
        rev->diffopt.flags.recursive = 1;
-       rev->diffopt.stat_width = -1; /* use full terminal width */
-       rev->diffopt.stat_name_width = -1; /* respect statNameWidth config */
-       rev->diffopt.stat_graph_width = -1; /* respect statGraphWidth config */
+       rev->diffopt.flags.allow_textconv = 1;
        rev->abbrev_commit = default_abbrev_commit;
        rev->show_root_diff = default_show_root;
        rev->subject_prefix = fmt_patch_subject_prefix;
        rev->patch_name_max = fmt_patch_name_max;
        rev->show_signature = default_show_signature;
        rev->encode_email_headers = default_encode_email_headers;
-       rev->diffopt.flags.allow_textconv = 1;
 
        if (default_date_mode)
                parse_date_format(default_date_mode, &rev->date_mode);
index 0de42aecf4babf5d1cae19aa81e7d46676c636d1..a35e0452d6672594da29623e8934256f37784549 100644 (file)
@@ -18,6 +18,7 @@
 #include "quote.h"
 #include "tree.h"
 #include "config.h"
+#include "strvec.h"
 
 static int line_termination = '\n';
 
@@ -414,6 +415,7 @@ struct merge_tree_options {
        int show_messages;
        int name_only;
        int use_stdin;
+       struct merge_options merge_options;
 };
 
 static int real_merge(struct merge_tree_options *o,
@@ -423,10 +425,11 @@ 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",
@@ -437,8 +440,6 @@ static int real_merge(struct merge_tree_options *o,
                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;
@@ -507,12 +508,14 @@ static int real_merge(struct merge_tree_options *o,
        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;
@@ -548,14 +551,25 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
                           &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;
index fd21c0d4f4b88ee2c26b7ba4b1206c20357c17ea..d748d46e13517afd6908b1f7297bf448557b1a86 100644 (file)
@@ -466,9 +466,7 @@ static void finish(struct commit *head_commit,
        if (new_head && show_diffstat) {
                struct diff_options opts;
                repo_diff_setup(the_repository, &opts);
-               opts.stat_width = -1; /* use full terminal width */
-               opts.stat_name_width = -1; /* respect statNameWidth config */
-               opts.stat_graph_width = -1; /* respect statGraphWidth config */
+               init_diffstat_widths(&opts);
                opts.output_format |=
                        DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
                opts.detect_rename = DIFF_DETECT_RENAME;
@@ -1634,6 +1632,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
                for (j = remoteheads; j; j = j->next) {
                        struct commit_list *common_one;
+                       struct commit *common_item;
 
                        /*
                         * Here we *have* to calculate the individual
@@ -1643,7 +1642,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        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;
                        }
index 6eb975683647b065852fba7d7d32595341180a46..89a8b5a9768e42da3edaaddb63a663936ef70576 100644 (file)
@@ -4402,12 +4402,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        if (!rev_list_all || !rev_list_reflog || !rev_list_index)
                unpack_unreachable_expiration = 0;
 
-       if (filter_options.choice) {
-               if (!pack_to_stdout)
-                       die(_("cannot use --filter without --stdout"));
-               if (stdin_packs)
-                       die(_("cannot use --filter with --stdin-packs"));
-       }
+       if (stdin_packs && filter_options.choice)
+               die(_("cannot use --filter with --stdin-packs"));
 
        if (stdin_packs && use_internal_rev_list)
                die(_("cannot use internal rev list with --stdin-packs"));
index ed15accec92d3b93b647580acbbbd69946465d51..f990811614bf84102a1bb61a08fcf4abc93b82a9 100644 (file)
@@ -1803,9 +1803,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
                /* We want color (if set), but no pager */
                repo_diff_setup(the_repository, &opts);
-               opts.stat_width = -1; /* use full terminal width */
-               opts.stat_name_width = -1; /* respect statNameWidth config */
-               opts.stat_graph_width = -1; /* respect statGraphWidth config */
+               init_diffstat_widths(&opts);
                opts.output_format |=
                        DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
                opts.detect_rename = DIFF_DETECT_RENAME;
index 529e13120d5a9ad1b14f01241220cbade275b855..edaee4dbec7b01624b5d272a7c402c49f46dba5d 100644 (file)
 #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;
@@ -51,11 +53,12 @@ struct pack_objects_args {
        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,
@@ -116,11 +119,26 @@ static void pack_mark_for_deletion(struct string_list_item *item)
        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)
 {
@@ -133,17 +151,39 @@ static void mark_packs_for_deletion_1(struct string_list *names,
                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)
 
@@ -225,6 +265,8 @@ static void collect_pack_filenames(struct existing_packs *existing,
        }
 
        string_list_sort(&existing->kept_packs);
+       string_list_sort(&existing->non_kept_packs);
+       string_list_sort(&existing->cruft_packs);
        strbuf_release(&buf);
 }
 
@@ -242,7 +284,7 @@ static void prepare_pack_objects(struct child_process *cmd,
        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)
@@ -315,6 +357,18 @@ static struct generated_pack_data *populate_pack_exts(const char *name)
        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)
 {
@@ -732,6 +786,7 @@ static void midx_included_packs(struct string_list *include,
 
 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)
 {
@@ -761,6 +816,38 @@ static int write_midx_included_packs(struct string_list *include,
        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);
@@ -806,6 +893,153 @@ static void remove_redundant_bitmaps(struct string_list *include,
        strbuf_release(&path);
 }
 
+static int finish_pack_objects_cmd(struct child_process *cmd,
+                                  struct string_list *names,
+                                  int local)
+{
+       FILE *out;
+       struct strbuf line = STRBUF_INIT;
+
+       out = xfdopen(cmd->out, "r");
+       while (strbuf_getline_lf(&line, out) != EOF) {
+               struct string_list_item *item;
+
+               if (line.len != the_hash_algo->hexsz)
+                       die(_("repack: Expecting full hex object ID lines only "
+                             "from pack-objects."));
+               /*
+                * Avoid putting packs written outside of the repository in the
+                * list of names.
+                */
+               if (local) {
+                       item = string_list_append(names, line.buf);
+                       item->util = populate_pack_exts(line.buf);
+               }
+       }
+       fclose(out);
+
+       strbuf_release(&line);
+
+       return finish_command(cmd);
+}
+
+static int write_filtered_pack(const struct pack_objects_args *args,
+                              const char *destination,
+                              const char *pack_prefix,
+                              struct existing_packs *existing,
+                              struct string_list *names)
+{
+       struct child_process cmd = CHILD_PROCESS_INIT;
+       struct string_list_item *item;
+       FILE *in;
+       int ret;
+       const char *caret;
+       const char *scratch;
+       int local = skip_prefix(destination, packdir, &scratch);
+
+       prepare_pack_objects(&cmd, args, destination);
+
+       strvec_push(&cmd.args, "--stdin-packs");
+
+       if (!pack_kept_objects)
+               strvec_push(&cmd.args, "--honor-pack-keep");
+       for_each_string_list_item(item, &existing->kept_packs)
+               strvec_pushf(&cmd.args, "--keep-pack=%s", item->string);
+
+       cmd.in = -1;
+
+       ret = start_command(&cmd);
+       if (ret)
+               return ret;
+
+       /*
+        * Here 'names' contains only the pack(s) that were just
+        * written, which is exactly the packs we want to keep. Also
+        * 'existing_kept_packs' already contains the packs in
+        * 'keep_pack_list'.
+        */
+       in = xfdopen(cmd.in, "w");
+       for_each_string_list_item(item, names)
+               fprintf(in, "^%s-%s.pack\n", pack_prefix, item->string);
+       for_each_string_list_item(item, &existing->non_kept_packs)
+               fprintf(in, "%s.pack\n", item->string);
+       for_each_string_list_item(item, &existing->cruft_packs)
+               fprintf(in, "%s.pack\n", item->string);
+       caret = pack_kept_objects ? "" : "^";
+       for_each_string_list_item(item, &existing->kept_packs)
+               fprintf(in, "%s%s.pack\n", caret, item->string);
+       fclose(in);
+
+       return finish_pack_objects_cmd(&cmd, names, local);
+}
+
+static int 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,
@@ -814,9 +1048,8 @@ static int write_cruft_pack(const struct pack_objects_args *args,
                            struct existing_packs *existing)
 {
        struct child_process cmd = CHILD_PROCESS_INIT;
-       struct strbuf line = STRBUF_INIT;
        struct string_list_item *item;
-       FILE *in, *out;
+       FILE *in;
        int ret;
        const char *scratch;
        int local = skip_prefix(destination, packdir, &scratch);
@@ -853,35 +1086,30 @@ static int write_cruft_pack(const struct pack_objects_args *args,
        in = xfdopen(cmd.in, "w");
        for_each_string_list_item(item, names)
                fprintf(in, "%s-%s.pack\n", pack_prefix, item->string);
-       for_each_string_list_item(item, &existing->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)
@@ -891,10 +1119,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        struct string_list names = STRING_LIST_INIT_DUP;
        struct existing_packs existing = EXISTING_PACKS_INIT;
        struct pack_geometry geometry = { 0 };
-       struct strbuf line = STRBUF_INIT;
        struct tempfile *refs_snapshot = NULL;
        int i, ext, ret;
-       FILE *out;
        int show_progress;
 
        /* variables to be filled by option parsing */
@@ -907,6 +1133,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        int write_midx = 0;
        const char *cruft_expiration = NULL;
        const char *expire_to = NULL;
+       const char *filter_to = NULL;
 
        struct option builtin_repack_options[] = {
                OPT_BIT('a', NULL, &pack_everything,
@@ -919,6 +1146,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                                   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,
@@ -946,8 +1175,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                                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"),
@@ -958,9 +1188,13 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                           N_("write a multi-pack index of the resulting packs")),
                OPT_STRING(0, "expire-to", &expire_to, N_("dir"),
                           N_("pack prefix to store a pack containing pruned objects")),
+               OPT_STRING(0, "filter-to", &filter_to, N_("dir"),
+                          N_("pack prefix to store a pack containing filtered out objects")),
                OPT_END()
        };
 
+       list_objects_filter_init(&po_args.filter_options);
+
        git_config(repack_config, &cruft_po_args);
 
        argc = parse_options(argc, argv, prefix, builtin_repack_options,
@@ -1101,6 +1335,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                strvec_push(&cmd.args, "--incremental");
        }
 
+       if (po_args.filter_options.choice)
+               strvec_pushf(&cmd.args, "--filter=%s",
+                            expand_list_objects_filter_spec(&po_args.filter_options));
+       else if (filter_to)
+               die(_("option '%s' can only be used along with '%s'"), "--filter-to", "--filter");
+
        if (geometry.split_factor)
                cmd.in = -1;
        else
@@ -1124,18 +1364,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                fclose(in);
        }
 
-       out = xfdopen(cmd.out, "r");
-       while (strbuf_getline_lf(&line, out) != EOF) {
-               struct string_list_item *item;
-
-               if (line.len != the_hash_algo->hexsz)
-                       die(_("repack: Expecting full hex object ID lines only from pack-objects."));
-               item = string_list_append(&names, line.buf);
-               item->util = populate_pack_exts(item->string);
-       }
-       strbuf_release(&line);
-       fclose(out);
-       ret = finish_command(&cmd);
+       ret = finish_pack_objects_cmd(&cmd, &names, 1);
        if (ret)
                goto cleanup;
 
@@ -1143,12 +1372,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                printf_ln(_("Nothing new to pack."));
 
        if (pack_everything & PACK_CRUFT) {
-               const char *pack_prefix;
-               if (!skip_prefix(packtmp, packdir, &pack_prefix))
-                       die(_("pack prefix %s does not begin with objdir %s"),
-                           packtmp, packdir);
-               if (*pack_prefix == '/')
-                       pack_prefix++;
+               const char *pack_prefix = find_pack_prefix(packdir, packtmp);
 
                if (!cruft_po_args.window)
                        cruft_po_args.window = po_args.window;
@@ -1203,6 +1427,19 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                }
        }
 
+       if (po_args.filter_options.choice) {
+               if (!filter_to)
+                       filter_to = packtmp;
+
+               ret = write_filtered_pack(&po_args,
+                                         filter_to,
+                                         find_pack_prefix(packdir, packtmp),
+                                         &existing,
+                                         &names);
+               if (ret)
+                       goto cleanup;
+       }
+
        string_list_sort(&names);
 
        close_object_store(the_repository->objects);
@@ -1248,7 +1485,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
                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);
 
@@ -1295,6 +1532,7 @@ cleanup:
        string_list_clear(&names, 1);
        existing_packs_release(&existing);
        free_pack_geometry(&geometry);
+       list_objects_filter_release(&po_args.filter_options);
 
        return ret;
 }
index 6f3bf33e615b3162c9109c1884113497ef130809..cce46450abe95ea6a24d28026a1686cb930cb75a 100644 (file)
@@ -2889,7 +2889,7 @@ cleanup:
 
 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;
@@ -2901,20 +2901,29 @@ static int module_set_url(int argc, const char **argv, const char *prefix)
                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)
@@ -2941,6 +2950,7 @@ 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);
 
@@ -2953,7 +2963,13 @@ static int module_set_branch(int argc, const char **argv, const char *prefix)
        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);
index 92b9c8598b46f1fa654adadd07adef601c314a64..6ce62999e58523e20a686801446daaf0d924e9be 100644 (file)
@@ -155,10 +155,10 @@ static int already_written(struct bulk_checkin_packfile *state, struct object_id
  * status before calling us just in case we ask it to call us again
  * with a new pack.
  */
-static int stream_to_pack(struct bulk_checkin_packfile *state,
-                         git_hash_ctx *ctx, off_t *already_hashed_to,
-                         int fd, size_t size, enum object_type type,
-                         const char *path, unsigned flags)
+static int stream_blob_to_pack(struct bulk_checkin_packfile *state,
+                              git_hash_ctx *ctx, off_t *already_hashed_to,
+                              int fd, size_t size, const char *path,
+                              unsigned flags)
 {
        git_zstream s;
        unsigned char ibuf[16384];
@@ -170,7 +170,7 @@ static int stream_to_pack(struct bulk_checkin_packfile *state,
 
        git_deflate_init(&s, pack_compression_level);
 
-       hdrlen = encode_in_pack_object_header(obuf, sizeof(obuf), type, size);
+       hdrlen = encode_in_pack_object_header(obuf, sizeof(obuf), OBJ_BLOB, size);
        s.next_out = obuf + hdrlen;
        s.avail_out = sizeof(obuf) - hdrlen;
 
@@ -247,11 +247,10 @@ static void prepare_to_stream(struct bulk_checkin_packfile *state,
                die_errno("unable to write pack header");
 }
 
-static int deflate_to_pack(struct bulk_checkin_packfile *state,
-                          struct object_id *result_oid,
-                          int fd, size_t size,
-                          enum object_type type, const char *path,
-                          unsigned flags)
+static int deflate_blob_to_pack(struct bulk_checkin_packfile *state,
+                               struct object_id *result_oid,
+                               int fd, size_t size,
+                               const char *path, unsigned flags)
 {
        off_t seekback, already_hashed_to;
        git_hash_ctx ctx;
@@ -265,7 +264,7 @@ static int deflate_to_pack(struct bulk_checkin_packfile *state,
                return error("cannot find the current offset");
 
        header_len = format_object_header((char *)obuf, sizeof(obuf),
-                                         type, size);
+                                         OBJ_BLOB, size);
        the_hash_algo->init_fn(&ctx);
        the_hash_algo->update_fn(&ctx, obuf, header_len);
        the_hash_algo->init_fn(&checkpoint.ctx);
@@ -283,8 +282,8 @@ static int deflate_to_pack(struct bulk_checkin_packfile *state,
                        idx->offset = state->offset;
                        crc32_begin(state->f);
                }
-               if (!stream_to_pack(state, &ctx, &already_hashed_to,
-                                   fd, size, type, path, flags))
+               if (!stream_blob_to_pack(state, &ctx, &already_hashed_to,
+                                        fd, size, path, flags))
                        break;
                /*
                 * Writing this object to the current pack will make
@@ -351,12 +350,12 @@ void fsync_loose_object_bulk_checkin(int fd, const char *filename)
        }
 }
 
-int index_bulk_checkin(struct object_id *oid,
-                      int fd, size_t size, enum object_type type,
-                      const char *path, unsigned flags)
+int index_blob_bulk_checkin(struct object_id *oid,
+                           int fd, size_t size,
+                           const char *path, unsigned flags)
 {
-       int status = deflate_to_pack(&bulk_checkin_packfile, oid, fd, size, type,
-                                    path, flags);
+       int status = deflate_blob_to_pack(&bulk_checkin_packfile, oid, fd, size,
+                                         path, flags);
        if (!odb_transaction_nesting)
                flush_bulk_checkin_packfile(&bulk_checkin_packfile);
        return status;
index 48fe9a6e9171e77aba4f2a3c7ab1edd79e9291b8..aa7286a7b3e127bbd4ee14cf99e3fd21f46df445 100644 (file)
@@ -9,9 +9,9 @@
 void prepare_loose_object_bulk_checkin(void);
 void fsync_loose_object_bulk_checkin(int fd, const char *filename);
 
-int index_bulk_checkin(struct object_id *oid,
-                      int fd, size_t size, enum object_type type,
-                      const char *path, unsigned flags);
+int index_blob_bulk_checkin(struct object_id *oid,
+                           int fd, size_t size,
+                           const char *path, unsigned flags);
 
 /*
  * Tell the object database to optimize for adding
diff --git a/color.c b/color.c
index b24b19566b99eb498859ed315b74cabcf6df37c1..f663c06ac4eddc7b8085739318ddfc99a3ee7583 100644 (file)
--- a/color.c
+++ b/color.c
@@ -3,7 +3,7 @@
 #include "color.h"
 #include "editor.h"
 #include "gettext.h"
-#include "hex.h"
+#include "hex-ll.h"
 #include "pager.h"
 #include "strbuf.h"
 
index 1f334987b50b3bce3d2a457d9e61616a62be5699..c2b782af3b649fbf3deea7d90c959e10f951a003 100644 (file)
@@ -598,8 +598,6 @@ static int add_graph_to_chain(struct commit_graph *g,
                cur_g = cur_g->base_graph;
        }
 
-       g->base_graph = chain;
-
        if (chain) {
                if (unsigned_add_overflows(chain->num_commits,
                                           chain->num_commits_in_base)) {
@@ -610,6 +608,8 @@ static int add_graph_to_chain(struct commit_graph *g,
                g->num_commits_in_base = chain->num_commits + chain->num_commits_in_base;
        }
 
+       g->base_graph = chain;
+
        return 1;
 }
 
@@ -676,6 +676,8 @@ struct commit_graph *load_commit_graph_chain_fd_st(struct repository *r,
                                if (add_graph_to_chain(g, graph_chain, oids, i)) {
                                        graph_chain = g;
                                        valid = 1;
+                               } else {
+                                       free_commit_graph(g);
                                }
 
                                break;
@@ -827,19 +829,10 @@ struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r)
        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;
 }
 
@@ -2186,9 +2179,11 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
                        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);
@@ -2637,6 +2632,7 @@ int write_commit_graph(struct object_directory *odb,
 
 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);
@@ -2867,15 +2863,17 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
 
 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)
index 4b7c233fd468f9ad2a60abf5b9202f5c4fbad3c4..a868a575ea1cf8e7ccd331610b9d309a065ad135 100644 (file)
@@ -173,6 +173,7 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in)
                        for (k = bases; k; k = k->next)
                                end = k;
                }
+               free_commit_list(ret);
                ret = new_commits;
        }
        return ret;
index 2ea39295cd6ee8060cab8b4822250bbc020d6d0a..f9a1cca4e8af6b14e51b6993027f7cae3b8d31bb 100644 (file)
--- a/config.c
+++ b/config.c
@@ -11,6 +11,7 @@
 #include "date.h"
 #include "branch.h"
 #include "config.h"
+#include "parse.h"
 #include "convert.h"
 #include "environment.h"
 #include "gettext.h"
@@ -1165,129 +1166,6 @@ static int git_parse_source(struct config_source *cs, config_fn_t fn,
        return error_return;
 }
 
-static uintmax_t get_unit_factor(const char *end)
-{
-       if (!*end)
-               return 1;
-       else if (!strcasecmp(end, "k"))
-               return 1024;
-       else if (!strcasecmp(end, "m"))
-               return 1024 * 1024;
-       else if (!strcasecmp(end, "g"))
-               return 1024 * 1024 * 1024;
-       return 0;
-}
-
-static int git_parse_signed(const char *value, intmax_t *ret, intmax_t max)
-{
-       if (value && *value) {
-               char *end;
-               intmax_t val;
-               intmax_t factor;
-
-               if (max < 0)
-                       BUG("max must be a positive integer");
-
-               errno = 0;
-               val = strtoimax(value, &end, 0);
-               if (errno == ERANGE)
-                       return 0;
-               if (end == value) {
-                       errno = EINVAL;
-                       return 0;
-               }
-               factor = get_unit_factor(end);
-               if (!factor) {
-                       errno = EINVAL;
-                       return 0;
-               }
-               if ((val < 0 && -max / factor > val) ||
-                   (val > 0 && max / factor < val)) {
-                       errno = ERANGE;
-                       return 0;
-               }
-               val *= factor;
-               *ret = val;
-               return 1;
-       }
-       errno = EINVAL;
-       return 0;
-}
-
-static int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max)
-{
-       if (value && *value) {
-               char *end;
-               uintmax_t val;
-               uintmax_t factor;
-
-               /* negative values would be accepted by strtoumax */
-               if (strchr(value, '-')) {
-                       errno = EINVAL;
-                       return 0;
-               }
-               errno = 0;
-               val = strtoumax(value, &end, 0);
-               if (errno == ERANGE)
-                       return 0;
-               if (end == value) {
-                       errno = EINVAL;
-                       return 0;
-               }
-               factor = get_unit_factor(end);
-               if (!factor) {
-                       errno = EINVAL;
-                       return 0;
-               }
-               if (unsigned_mult_overflows(factor, val) ||
-                   factor * val > max) {
-                       errno = ERANGE;
-                       return 0;
-               }
-               val *= factor;
-               *ret = val;
-               return 1;
-       }
-       errno = EINVAL;
-       return 0;
-}
-
-int git_parse_int(const char *value, int *ret)
-{
-       intmax_t tmp;
-       if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int)))
-               return 0;
-       *ret = tmp;
-       return 1;
-}
-
-static int git_parse_int64(const char *value, int64_t *ret)
-{
-       intmax_t tmp;
-       if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int64_t)))
-               return 0;
-       *ret = tmp;
-       return 1;
-}
-
-int git_parse_ulong(const char *value, unsigned long *ret)
-{
-       uintmax_t tmp;
-       if (!git_parse_unsigned(value, &tmp, maximum_unsigned_value_of_type(long)))
-               return 0;
-       *ret = tmp;
-       return 1;
-}
-
-int git_parse_ssize_t(const char *value, ssize_t *ret)
-{
-       intmax_t tmp;
-       if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(ssize_t)))
-               return 0;
-       *ret = tmp;
-       return 1;
-}
-
 NORETURN
 static void die_bad_number(const char *name, const char *value,
                           const struct key_value_info *kvi)
@@ -1363,23 +1241,6 @@ ssize_t git_config_ssize_t(const char *name, const char *value,
        return ret;
 }
 
-static int git_parse_maybe_bool_text(const char *value)
-{
-       if (!value)
-               return 1;
-       if (!*value)
-               return 0;
-       if (!strcasecmp(value, "true")
-           || !strcasecmp(value, "yes")
-           || !strcasecmp(value, "on"))
-               return 1;
-       if (!strcasecmp(value, "false")
-           || !strcasecmp(value, "no")
-           || !strcasecmp(value, "off"))
-               return 0;
-       return -1;
-}
-
 static const struct fsync_component_name {
        const char *name;
        enum fsync_component component_bits;
@@ -1454,16 +1315,6 @@ next_name:
        return (current & ~negative) | positive;
 }
 
-int git_parse_maybe_bool(const char *value)
-{
-       int v = git_parse_maybe_bool_text(value);
-       if (0 <= v)
-               return v;
-       if (git_parse_int(value, &v))
-               return !!v;
-       return -1;
-}
-
 int git_config_bool_or_int(const char *name, const char *value,
                           const struct key_value_info *kvi, int *is_bool)
 {
@@ -2131,28 +1982,6 @@ void git_global_config(char **user_out, char **xdg_out)
        *xdg_out = xdg_config;
 }
 
-/*
- * Parse environment variable 'k' as a boolean (in various
- * possible spellings); if missing, use the default value 'def'.
- */
-int git_env_bool(const char *k, int def)
-{
-       const char *v = getenv(k);
-       return v ? git_config_bool(k, v) : def;
-}
-
-/*
- * Parse environment variable 'k' as ulong with possibly a unit
- * suffix; if missing, use the default value 'val'.
- */
-unsigned long git_env_ulong(const char *k, unsigned long val)
-{
-       const char *v = getenv(k);
-       if (v && !git_parse_ulong(v, &val))
-               die(_("failed to parse %s"), k);
-       return val;
-}
-
 int git_config_system(void)
 {
        return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
index 6332d749047764aff33000d01fc78f75ddfe03b1..14f881ecfaf6b264b6d120205ab509f2ee5930f2 100644 (file)
--- a/config.h
+++ b/config.h
@@ -4,7 +4,7 @@
 #include "hashmap.h"
 #include "string-list.h"
 #include "repository.h"
-
+#include "parse.h"
 
 /**
  * The config API gives callers a way to access Git configuration files
@@ -243,16 +243,6 @@ int config_with_options(config_fn_t fn, void *,
  * The following helper functions aid in parsing string values
  */
 
-int git_parse_ssize_t(const char *, ssize_t *);
-int git_parse_ulong(const char *, unsigned long *);
-int git_parse_int(const char *value, int *ret);
-
-/**
- * Same as `git_config_bool`, except that it returns -1 on error rather
- * than dying.
- */
-int git_parse_maybe_bool(const char *);
-
 /**
  * Parse the string to an integer, including unit factors. Dies on error;
  * otherwise, returns the parsed result.
@@ -385,8 +375,6 @@ int git_config_rename_section(const char *, const char *);
 int git_config_rename_section_in_file(const char *, const char *, const char *);
 int git_config_copy_section(const char *, const char *);
 int git_config_copy_section_in_file(const char *, const char *, const char *);
-int git_env_bool(const char *, int);
-unsigned long git_env_ulong(const char *, unsigned long);
 int git_config_system(void);
 int config_error_nonbool(const char *);
 #if defined(__GNUC__)
index f5e597114b671fc79cd0acf63c63e5ac427af8bf..17d331b2f38465e295c019e534eff371ef3e6e49 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -1243,19 +1243,20 @@ static int serve(struct string_list *listen_addr, int listen_port,
 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)) {
@@ -1437,22 +1438,26 @@ int cmd_main(int argc, const char **argv)
                        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;
 }
index a5c43c0c1461761bb808b4d6b833222b181843a6..69aeb142b45e9fee08d61dace73f6dc2bce1c2d0 100644 (file)
@@ -81,3 +81,18 @@ void *lookup_decoration(struct decoration *n, const struct object *obj)
                        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;
+}
index ee43dee1f008882094ca9c85f6b28b86ec88fdf6..cdeb17c9df2eb680d85e73e8ed2f48b52f44569f 100644 (file)
@@ -58,4 +58,14 @@ void *add_decoration(struct decoration *n, const struct object *obj, void *decor
  */
 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
index 8430064000bcba96506e4b9c99c9efe60ba072f4..4d096c857f1e669a44bc2e43375889ce2952083c 100644 (file)
@@ -71,42 +71,6 @@ static int dir_file_stats(struct object_directory *object_dir, void *data)
        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);
@@ -117,7 +81,7 @@ static int count_files(struct strbuf *path)
                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);
@@ -146,7 +110,7 @@ static void loose_objs_stats(struct strbuf *buf, const char *path)
        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);
@@ -191,7 +155,7 @@ static int add_directory_to_archiver(struct strvec *archiver_args,
 
                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);
index 5848e4f9ca294052b0e06c7e4d038ebd462ade92..0e9ec4f68afb01f23d9b9a0142d2ffb2cd1cc79b 100644 (file)
@@ -571,8 +571,6 @@ void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb)
                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"));
        }
 
        /*
diff --git a/diff.c b/diff.c
index 353e3b2cc92304a9ed4c342958ef497f724e200f..2c602df10a372c7e99402ca5a2dc9cf7aa673c09 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -6936,6 +6936,13 @@ void diff_queued_diff_prefetch(void *repository)
        oid_array_clear(&to_fetch);
 }
 
+void init_diffstat_widths(struct diff_options *options)
+{
+       options->stat_width = -1;        /* use full terminal width */
+       options->stat_name_width = -1;   /* respect diff.statNameWidth config */
+       options->stat_graph_width = -1;  /* respect diff.statGraphWidth config */
+}
+
 void diffcore_std(struct diff_options *options)
 {
        int output_formats_to_prefetch = DIFF_FORMAT_DIFFSTAT |
diff --git a/diff.h b/diff.h
index caf1528bf077cda0be89882b5ef856fe34dd3999..66bd8aeb2936fbe9d6610f7dc1202e31e1450ebb 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -573,6 +573,7 @@ int git_config_rename(const char *var, const char *value);
 
 #define DIFF_PICKAXE_IGNORE_CASE       32
 
+void init_diffstat_widths(struct diff_options *);
 void diffcore_std(struct diff_options *);
 void diffcore_fix_diff_index(void);
 
diff --git a/dir.c b/dir.c
index 8486e4d56ff50cafd14aa3ef00ee89c069d87269..16fdb03f2a51c9505b3cc9da2d9a908f3e4ae119 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -2235,6 +2235,39 @@ static int get_index_dtype(struct index_state *istate,
        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)
 {
diff --git a/dir.h b/dir.h
index ad06682fd54b3e7ae47d6f8a8d80048da0324f01..98aa85fcc0ee357a2df50014008c3e5ec12acb25 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -363,6 +363,22 @@ struct dir_struct {
 
 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);
 
diff --git a/entry.c b/entry.c
index 43767f9043c0cbb97ec3257fd78b0a985e5a8f77..076e97eb89ce15e312b6ff08eebd3c1389245997 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -581,3 +581,8 @@ void unlink_entry(const struct cache_entry *ce, const char *super_prefix)
                return;
        schedule_dir_for_removal(ce->name, ce_namelen(ce));
 }
+
+int remove_or_warn(unsigned int mode, const char *file)
+{
+       return S_ISGITLINK(mode) ? rmdir_or_warn(file) : unlink_or_warn(file);
+}
diff --git a/entry.h b/entry.h
index 7329f918a97ee3f80aeeaff07e4236143fcf3cbf..ca3ed35bc08654ee47bc6c9331c1fad2804bfd32 100644 (file)
--- a/entry.h
+++ b/entry.h
@@ -62,4 +62,10 @@ int fstat_checkout_output(int fd, const struct checkout *state, struct stat *st)
 void update_ce_after_write(const struct checkout *state, struct cache_entry *ce,
                           struct stat *st);
 
+/*
+ * Calls the correct function out of {unlink,rmdir}_or_warn based on
+ * the supplied file mode.
+ */
+int remove_or_warn(unsigned int mode, const char *path);
+
 #endif /* ENTRY_H */
diff --git a/hex-ll.c b/hex-ll.c
new file mode 100644 (file)
index 0000000..4d7ece1
--- /dev/null
+++ b/hex-ll.c
@@ -0,0 +1,49 @@
+#include "git-compat-util.h"
+#include "hex-ll.h"
+
+const signed char hexval_table[256] = {
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 00-07 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 08-0f */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 10-17 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 18-1f */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 20-27 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 28-2f */
+         0,  1,  2,  3,  4,  5,  6,  7,                /* 30-37 */
+         8,  9, -1, -1, -1, -1, -1, -1,                /* 38-3f */
+        -1, 10, 11, 12, 13, 14, 15, -1,                /* 40-47 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 48-4f */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 50-57 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 58-5f */
+        -1, 10, 11, 12, 13, 14, 15, -1,                /* 60-67 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 68-67 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 70-77 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 78-7f */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 80-87 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 88-8f */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 90-97 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* 98-9f */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* a0-a7 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* a8-af */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* b0-b7 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* b8-bf */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* c0-c7 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* c8-cf */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* d0-d7 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* d8-df */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* e0-e7 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* e8-ef */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* f0-f7 */
+        -1, -1, -1, -1, -1, -1, -1, -1,                /* f8-ff */
+};
+
+int hex_to_bytes(unsigned char *binary, const char *hex, size_t len)
+{
+       for (; len; len--, hex += 2) {
+               unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]);
+
+               if (val & ~0xff)
+                       return -1;
+               *binary++ = val;
+       }
+       return 0;
+}
diff --git a/hex-ll.h b/hex-ll.h
new file mode 100644 (file)
index 0000000..a381fa8
--- /dev/null
+++ b/hex-ll.h
@@ -0,0 +1,27 @@
+#ifndef HEX_LL_H
+#define HEX_LL_H
+
+extern const signed char hexval_table[256];
+static inline unsigned int hexval(unsigned char c)
+{
+       return hexval_table[c];
+}
+
+/*
+ * Convert two consecutive hexadecimal digits into a char.  Return a
+ * negative value on error.  Don't run over the end of short strings.
+ */
+static inline int hex2chr(const char *s)
+{
+       unsigned int val = hexval(s[0]);
+       return (val & ~0xf) ? val : (val << 4) | hexval(s[1]);
+}
+
+/*
+ * Read `len` pairs of hexadecimal digits from `hex` and write the
+ * values to `binary` as `len` bytes. Return 0 on success, or -1 if
+ * the input does not consist of hex digits).
+ */
+int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
+
+#endif
diff --git a/hex.c b/hex.c
index 01f17fe5c906e6f8dd225b7c07bfb725a54a48a8..d42262bdca71a8703ce191393f0217f0c589a75e 100644 (file)
--- a/hex.c
+++ b/hex.c
@@ -2,53 +2,6 @@
 #include "hash.h"
 #include "hex.h"
 
-const signed char hexval_table[256] = {
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 00-07 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 08-0f */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 10-17 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 18-1f */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 20-27 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 28-2f */
-         0,  1,  2,  3,  4,  5,  6,  7,                /* 30-37 */
-         8,  9, -1, -1, -1, -1, -1, -1,                /* 38-3f */
-        -1, 10, 11, 12, 13, 14, 15, -1,                /* 40-47 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 48-4f */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 50-57 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 58-5f */
-        -1, 10, 11, 12, 13, 14, 15, -1,                /* 60-67 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 68-67 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 70-77 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 78-7f */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 80-87 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 88-8f */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 90-97 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* 98-9f */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* a0-a7 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* a8-af */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* b0-b7 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* b8-bf */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* c0-c7 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* c8-cf */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* d0-d7 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* d8-df */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* e0-e7 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* e8-ef */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* f0-f7 */
-        -1, -1, -1, -1, -1, -1, -1, -1,                /* f8-ff */
-};
-
-int hex_to_bytes(unsigned char *binary, const char *hex, size_t len)
-{
-       for (; len; len--, hex += 2) {
-               unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]);
-
-               if (val & ~0xff)
-                       return -1;
-               *binary++ = val;
-       }
-       return 0;
-}
-
 static int get_hash_hex_algop(const char *hex, unsigned char *hash,
                              const struct git_hash_algo *algop)
 {
diff --git a/hex.h b/hex.h
index 87abf6660214151a50f45197572b6749c10944cc..e0b83f776f1a8cc62ab1694be4faf579051396b4 100644 (file)
--- a/hex.h
+++ b/hex.h
@@ -2,22 +2,7 @@
 #define HEX_H
 
 #include "hash-ll.h"
-
-extern const signed char hexval_table[256];
-static inline unsigned int hexval(unsigned char c)
-{
-       return hexval_table[c];
-}
-
-/*
- * Convert two consecutive hexadecimal digits into a char.  Return a
- * negative value on error.  Don't run over the end of short strings.
- */
-static inline int hex2chr(const char *s)
-{
-       unsigned int val = hexval(s[0]);
-       return (val & ~0xf) ? val : (val << 4) | hexval(s[1]);
-}
+#include "hex-ll.h"
 
 /*
  * Try to read a hash (specified by the_hash_algo) in hexadecimal
@@ -34,13 +19,6 @@ int get_oid_hex(const char *hex, struct object_id *oid);
 /* Like get_oid_hex, but for an arbitrary hash algorithm. */
 int get_oid_hex_algop(const char *hex, struct object_id *oid, const struct git_hash_algo *algop);
 
-/*
- * 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,
index 790ab732127d04df57b330f06b43785532fd68b9..24a1ecb67799c23570ef485bc382b4bbdf2779d2 100644 (file)
@@ -1327,3 +1327,13 @@ int line_log_filter(struct rev_info *rev)
 
        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);
+}
index adff361b1bc93905641341df88a7cfcbfb6c118d..4291da8d79235d1f477742a11a5fdb67a4346e2e 100644 (file)
@@ -60,4 +60,6 @@ int line_log_process_ranges_arbitrary_commit(struct rev_info *rev,
 
 int line_log_print(struct rev_info *rev, struct commit *commit);
 
+void line_log_free(struct rev_info *rev);
+
 #endif /* LINE_LOG_H */
index 931505363cde08b0475f200e9c60fd3c034413b7..a07d2da16dedb2779a9797cfa185ec29d9321491 100644 (file)
@@ -1,7 +1,7 @@
 #include "git-compat-util.h"
 #include "config.h"
 #include "gettext.h"
-#include "hex.h"
+#include "hex-ll.h"
 #include "utf8.h"
 #include "strbuf.h"
 #include "mailinfo.h"
index 7857ce9fbd18e5575ae998c710a8b6c4e5f67427..36537256613b5d18aff7b4b0d6e45ca7b921d146 100644 (file)
@@ -1902,6 +1902,7 @@ static void initialize_attr_index(struct merge_options *opt)
        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)
index 0d7e57e2dfcb3dc5b306729ddffbac70021085a4..e3beb0801b115691d84b5da3403bc52abcb97336 100644 (file)
@@ -3912,6 +3912,22 @@ void init_merge_options(struct merge_options *opt,
                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;
index b88000e3c25277d07d20b7ba29755b9670cd28ff..3d3b3e3c295deb0dc8470958d01f8ed1e6ef0611 100644 (file)
@@ -55,6 +55,9 @@ struct merge_options {
 
 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);
 
index 7dc0c4bfbba87d1a9f42e3274764f7e07cc74c52..7c7afe5793641c06b10dee6f1f6735a1a476c7d9 100644 (file)
@@ -2446,11 +2446,11 @@ static int index_core(struct index_state *istate,
  * binary blobs, they generally do not want to get any conversion, and
  * callers should avoid this code path when filters are requested.
  */
-static int index_stream(struct object_id *oid, int fd, size_t size,
-                       enum object_type type, const char *path,
-                       unsigned flags)
+static int index_blob_stream(struct object_id *oid, int fd, size_t size,
+                            const char *path,
+                            unsigned flags)
 {
-       return index_bulk_checkin(oid, fd, size, type, path, flags);
+       return index_blob_bulk_checkin(oid, fd, size, path, flags);
 }
 
 int index_fd(struct index_state *istate, struct object_id *oid,
@@ -2472,8 +2472,8 @@ int index_fd(struct index_state *istate, struct object_id *oid,
                ret = index_core(istate, oid, fd, xsize_t(st->st_size),
                                 type, path, flags);
        else
-               ret = index_stream(oid, fd, xsize_t(st->st_size), type, path,
-                                  flags);
+               ret = index_blob_stream(oid, fd, xsize_t(st->st_size), path,
+                                       flags);
        close(fd);
        return ret;
 }
index f6757c3cbf2019b7cf41b3d82e0861138c92c075..f4ecdf8b0e3581cb6d92d0b88832c37adf749dc1 100644 (file)
@@ -413,15 +413,19 @@ static int fill_bitmap_commit(struct bb_commit *ent,
 
                if (old_bitmap && mapping) {
                        struct ewah_bitmap *old = bitmap_for_commit(old_bitmap, c);
+                       struct bitmap *remapped = bitmap_new();
                        /*
                         * If this commit has an old bitmap, then translate that
                         * bitmap and add its bits to this one. No need to walk
                         * parents or the tree for this commit.
                         */
-                       if (old && !rebuild_bitmap(mapping, old, ent->bitmap)) {
+                       if (old && !rebuild_bitmap(mapping, old, remapped)) {
+                               bitmap_or(ent->bitmap, remapped);
+                               bitmap_free(remapped);
                                reused_bitmaps_nr++;
                                continue;
                        }
+                       bitmap_free(remapped);
                }
 
                /*
index 1b8052bececc1f9b0a2ef53f04d70334c1b57314..f403ca6986a9d4c68715c1e1b24462d462fb8aa9 100644 (file)
@@ -3,7 +3,7 @@
 #include "pack.h"
 #include "pack-objects.h"
 #include "packfile.h"
-#include "config.h"
+#include "parse.h"
 
 static uint32_t locate_object_entry_hash(struct packing_data *pdata,
                                         const struct object_id *oid,
index 6d8fd3645a7350ee30e0a4740c5b4aca1c610a79..acf1dd9786cd3c2a0cb410804721488b6d1d92b4 100644 (file)
@@ -6,7 +6,7 @@
 #include "packfile.h"
 #include "strbuf.h"
 #include "trace2.h"
-#include "config.h"
+#include "parse.h"
 #include "midx.h"
 #include "csum-file.h"
 
index e8e076c3a6d9cf30149d833d9ca4f3278aa045ae..093eaf2db80f82d86260d929bb514f3d84c39efa 100644 (file)
@@ -1,11 +1,12 @@
 #include "git-compat-util.h"
 #include "parse-options.h"
 #include "abspath.h"
-#include "config.h"
+#include "parse.h"
 #include "commit.h"
 #include "color.h"
 #include "gettext.h"
 #include "strbuf.h"
+#include "string-list.h"
 #include "utf8.h"
 
 static int disallow_abbreviated_options;
index 57a7fe9d91a1690e59e05838897a8d474aca5de1..4a66ec3bf5f4bfcae08297f4799bdd51222b3b81 100644 (file)
@@ -459,7 +459,6 @@ struct parse_opt_ctx_t {
        unsigned has_subcommands;
        const char *prefix;
        const char **alias_groups; /* must be in groups of 3 elements! */
-       struct option *updated_options;
 };
 
 void parse_options_start(struct parse_opt_ctx_t *ctx,
diff --git a/parse.c b/parse.c
new file mode 100644 (file)
index 0000000..42d691a
--- /dev/null
+++ b/parse.c
@@ -0,0 +1,182 @@
+#include "git-compat-util.h"
+#include "gettext.h"
+#include "parse.h"
+
+static uintmax_t get_unit_factor(const char *end)
+{
+       if (!*end)
+               return 1;
+       else if (!strcasecmp(end, "k"))
+               return 1024;
+       else if (!strcasecmp(end, "m"))
+               return 1024 * 1024;
+       else if (!strcasecmp(end, "g"))
+               return 1024 * 1024 * 1024;
+       return 0;
+}
+
+int git_parse_signed(const char *value, intmax_t *ret, intmax_t max)
+{
+       if (value && *value) {
+               char *end;
+               intmax_t val;
+               intmax_t factor;
+
+               if (max < 0)
+                       BUG("max must be a positive integer");
+
+               errno = 0;
+               val = strtoimax(value, &end, 0);
+               if (errno == ERANGE)
+                       return 0;
+               if (end == value) {
+                       errno = EINVAL;
+                       return 0;
+               }
+               factor = get_unit_factor(end);
+               if (!factor) {
+                       errno = EINVAL;
+                       return 0;
+               }
+               if ((val < 0 && -max / factor > val) ||
+                   (val > 0 && max / factor < val)) {
+                       errno = ERANGE;
+                       return 0;
+               }
+               val *= factor;
+               *ret = val;
+               return 1;
+       }
+       errno = EINVAL;
+       return 0;
+}
+
+static int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max)
+{
+       if (value && *value) {
+               char *end;
+               uintmax_t val;
+               uintmax_t factor;
+
+               /* negative values would be accepted by strtoumax */
+               if (strchr(value, '-')) {
+                       errno = EINVAL;
+                       return 0;
+               }
+               errno = 0;
+               val = strtoumax(value, &end, 0);
+               if (errno == ERANGE)
+                       return 0;
+               if (end == value) {
+                       errno = EINVAL;
+                       return 0;
+               }
+               factor = get_unit_factor(end);
+               if (!factor) {
+                       errno = EINVAL;
+                       return 0;
+               }
+               if (unsigned_mult_overflows(factor, val) ||
+                   factor * val > max) {
+                       errno = ERANGE;
+                       return 0;
+               }
+               val *= factor;
+               *ret = val;
+               return 1;
+       }
+       errno = EINVAL;
+       return 0;
+}
+
+int git_parse_int(const char *value, int *ret)
+{
+       intmax_t tmp;
+       if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int)))
+               return 0;
+       *ret = tmp;
+       return 1;
+}
+
+int git_parse_int64(const char *value, int64_t *ret)
+{
+       intmax_t tmp;
+       if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(int64_t)))
+               return 0;
+       *ret = tmp;
+       return 1;
+}
+
+int git_parse_ulong(const char *value, unsigned long *ret)
+{
+       uintmax_t tmp;
+       if (!git_parse_unsigned(value, &tmp, maximum_unsigned_value_of_type(long)))
+               return 0;
+       *ret = tmp;
+       return 1;
+}
+
+int git_parse_ssize_t(const char *value, ssize_t *ret)
+{
+       intmax_t tmp;
+       if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(ssize_t)))
+               return 0;
+       *ret = tmp;
+       return 1;
+}
+
+int git_parse_maybe_bool_text(const char *value)
+{
+       if (!value)
+               return 1;
+       if (!*value)
+               return 0;
+       if (!strcasecmp(value, "true")
+           || !strcasecmp(value, "yes")
+           || !strcasecmp(value, "on"))
+               return 1;
+       if (!strcasecmp(value, "false")
+           || !strcasecmp(value, "no")
+           || !strcasecmp(value, "off"))
+               return 0;
+       return -1;
+}
+
+int git_parse_maybe_bool(const char *value)
+{
+       int v = git_parse_maybe_bool_text(value);
+       if (0 <= v)
+               return v;
+       if (git_parse_int(value, &v))
+               return !!v;
+       return -1;
+}
+
+/*
+ * Parse environment variable 'k' as a boolean (in various
+ * possible spellings); if missing, use the default value 'def'.
+ */
+int git_env_bool(const char *k, int def)
+{
+       const char *v = getenv(k);
+       int val;
+       if (!v)
+               return def;
+       val = git_parse_maybe_bool(v);
+       if (val < 0)
+               die(_("bad boolean environment value '%s' for '%s'"),
+                   v, k);
+       return val;
+}
+
+/*
+ * Parse environment variable 'k' as ulong with possibly a unit
+ * suffix; if missing, use the default value 'val'.
+ */
+unsigned long git_env_ulong(const char *k, unsigned long val)
+{
+       const char *v = getenv(k);
+       if (v && !git_parse_ulong(v, &val))
+               die(_("failed to parse %s"), k);
+       return val;
+}
diff --git a/parse.h b/parse.h
new file mode 100644 (file)
index 0000000..07d2193
--- /dev/null
+++ b/parse.h
@@ -0,0 +1,20 @@
+#ifndef PARSE_H
+#define PARSE_H
+
+int git_parse_signed(const char *value, intmax_t *ret, intmax_t max);
+int git_parse_ssize_t(const char *, ssize_t *);
+int git_parse_ulong(const char *, unsigned long *);
+int git_parse_int(const char *value, int *ret);
+int git_parse_int64(const char *value, int64_t *ret);
+
+/**
+ * Same as `git_config_bool`, except that it returns -1 on error rather
+ * than dying.
+ */
+int git_parse_maybe_bool(const char *);
+int git_parse_maybe_bool_text(const char *value);
+
+int git_env_bool(const char *, int);
+unsigned long git_env_ulong(const char *, unsigned long);
+
+#endif /* PARSE_H */
index 3a3a5724c44bcb7b53d10ddce6f0f9f9f397512b..7f88f1c02bbf3b89406ea54f04d3f99a954f02bf 100644 (file)
@@ -1,6 +1,6 @@
 #include "git-compat-util.h"
 #include "abspath.h"
-#include "config.h"
+#include "parse.h"
 #include "dir.h"
 #include "environment.h"
 #include "gettext.h"
index 3e4f897d935918092f3dfd1a05637b7176bd9fe1..ec08aa24add4e3da7c02b990ba1851665825c2b0 100644 (file)
@@ -412,7 +412,7 @@ There are some conventions that l10n contributors must follow:
 - 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
index e44530c80cf51edfdff846ed476a16c9bae0047a..63fd35d64b141f6f1488c40c4fb34b1fe1e53ae0 100644 (file)
@@ -7,7 +7,7 @@
 #include "environment.h"
 #include "fsmonitor.h"
 #include "gettext.h"
-#include "config.h"
+#include "parse.h"
 #include "preload-index.h"
 #include "progress.h"
 #include "read-cache.h"
index 7f3abb676c2e1707f3d897f1d82a774b1e1868f3..cf964b060cd128e2bc271c07ed8bb8b4c28bfe29 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1961,6 +1961,10 @@ void userformat_find_requirements(const char *fmt, struct userformat_want *w)
                case 'D':
                        w->decorate = 1;
                        break;
+               case '(':
+                       if (starts_with(fmt + 1, "decorate"))
+                               w->decorate = 1;
+                       break;
                }
        }
 }
index f695798acac72bd703dfacc59f83054fd761b619..c83cb60bf17eb490ccc3abc97a1b6f6679196e07 100644 (file)
@@ -17,7 +17,7 @@
 #include "trace.h"
 #include "trace2.h"
 #include "utf8.h"
-#include "config.h"
+#include "parse.h"
 
 #define TP_IDX_MAX      8
 
index 3baa33f63d87a675947bafcdfe3a798b02410de2..8935fe4dfb9414f101bf603aaf866ac6a1f6e027 100644 (file)
--- a/prompt.c
+++ b/prompt.c
@@ -1,5 +1,5 @@
 #include "git-compat-util.h"
-#include "config.h"
+#include "parse.h"
 #include "environment.h"
 #include "run-command.h"
 #include "strbuf.h"
index 17a570f1ff97fab8e789d2960ae54894b1c42a80..69a1822da34ce7b5877c7aace0a770ef51c655fd 100644 (file)
--- a/rebase.c
+++ b/rebase.c
@@ -1,6 +1,6 @@
 #include "git-compat-util.h"
 #include "rebase.h"
-#include "config.h"
+#include "parse.h"
 #include "gettext.h"
 
 /*
index 341354182bbf10937dffc9de2c56645027f5cfcd..db5c0c7a724d57ad67250cb0be996ae5cfbc0372 100644 (file)
@@ -246,10 +246,8 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
        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) {
@@ -262,23 +260,22 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
 
        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,
@@ -308,7 +305,6 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
                                         create_ref_entry(refname.buf, &oid, flag));
                }
                strbuf_setlen(&refname, dirnamelen);
-               strbuf_setlen(&path, path_baselen);
        }
        strbuf_release(&refname);
        strbuf_release(&path);
index 2294c4564fba3e171334314a5623928c5f27c14a..6e3b725245c3e8df1ec666cc23ab51b718be1d1f 100644 (file)
@@ -412,7 +412,8 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
 
                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;
index e789834dd14b17ed42c5ca55bf94abf0b14313b5..219dc76716fb0611ae8e7eb3dc10522d8779b213 100644 (file)
@@ -3083,6 +3083,11 @@ static void release_revisions_mailmap(struct string_list *mailmap)
 
 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);
@@ -3100,6 +3105,10 @@ void release_revisions(struct rev_info *revs)
        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)
index 4c9ac6dc5e3cd34fd059ca3a20f8e98bd08b2d4a..7827178d8e5e374582357f48a14a77bb15d98320 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -1,6 +1,6 @@
 #include "git-compat-util.h"
 #include "gettext.h"
-#include "hex.h"
+#include "hex-ll.h"
 #include "strbuf.h"
 #include "string-list.h"
 #include "utf8.h"
index 66c88b8ff3d311573db482035aa143a0c1ad136f..1c486888a42d0da490f6b9a4d90941b09eda754b 100644 (file)
@@ -1,5 +1,5 @@
 #include "test-tool.h"
-#include "config.h"
+#include "parse.h"
 #include "parse-options.h"
 
 static char const * const env__helper_usage[] = {
index 2ed910adaa3da3b5db771679ccb1eefb0a8da2f5..8f59f6be4cff6c53da68ebb4ea7201e71793cbe1 100644 (file)
@@ -72,5 +72,7 @@ int cmd__example_decorate(int argc UNUSED, const char **argv UNUSED)
        if (objects_noticed != 2)
                BUG("should have 2 objects");
 
+       clear_decoration(&n, NULL);
+
        return 0;
 }
diff --git a/t/helper/test-find-pack.c b/t/helper/test-find-pack.c
new file mode 100644 (file)
index 0000000..e8bd793
--- /dev/null
@@ -0,0 +1,50 @@
+#include "test-tool.h"
+#include "object-name.h"
+#include "object-store.h"
+#include "packfile.h"
+#include "parse-options.h"
+#include "setup.h"
+
+/*
+ * Display the path(s), one per line, of the packfile(s) containing
+ * the given object.
+ *
+ * If '--check-count <n>' is passed, then error out if the number of
+ * packfiles containing the object is not <n>.
+ */
+
+static const char *find_pack_usage[] = {
+       "test-tool find-pack [--check-count <n>] <object>",
+       NULL
+};
+
+int cmd__find_pack(int argc, const char **argv)
+{
+       struct object_id oid;
+       struct packed_git *p;
+       int count = -1, actual_count = 0;
+       const char *prefix = setup_git_directory();
+
+       struct option options[] = {
+               OPT_INTEGER('c', "check-count", &count, "expected number of packs"),
+               OPT_END(),
+       };
+
+       argc = parse_options(argc, argv, prefix, options, find_pack_usage, 0);
+       if (argc != 1)
+               usage(find_pack_usage[0]);
+
+       if (repo_get_oid(the_repository, argv[0], &oid))
+               die("cannot parse %s as an object name", argv[0]);
+
+       for (p = get_all_packs(the_repository); p; p = p->next)
+               if (find_pack_entry_one(oid.hash, p)) {
+                       printf("%s\n", p->pack_name);
+                       actual_count++;
+               }
+
+       if (count > -1 && count != actual_count)
+               die("bad packfile count %d instead of %d", actual_count, count);
+
+       return 0;
+}
index 621ac3dd107b077e090c9217d68ab1523f9e4a12..9010ac6de70bdc3cfe63766326078f228c5c6365 100644 (file)
@@ -31,6 +31,7 @@ static struct test_cmd cmds[] = {
        { "env-helper", cmd__env_helper },
        { "example-decorate", cmd__example_decorate },
        { "fast-rebase", cmd__fast_rebase },
+       { "find-pack", cmd__find_pack },
        { "fsmonitor-client", cmd__fsmonitor_client },
        { "genrandom", cmd__genrandom },
        { "genzeros", cmd__genzeros },
index a641c3a81d7d84118e8069050245871ebbb93fbb..f134f96b979a576d7bece39ac743f6690a376b95 100644 (file)
@@ -25,6 +25,7 @@ int cmd__dump_reftable(int argc, const char **argv);
 int cmd__env_helper(int argc, const char **argv);
 int cmd__example_decorate(int argc, const char **argv);
 int cmd__fast_rebase(int argc, const char **argv);
+int cmd__find_pack(int argc, const char **argv);
 int cmd__fsmonitor_client(int argc, const char **argv);
 int cmd__genrandom(int argc, const char **argv);
 int cmd__genzeros(int argc, const char **argv);
diff --git a/t/t0081-find-pack.sh b/t/t0081-find-pack.sh
new file mode 100755 (executable)
index 0000000..67b1121
--- /dev/null
@@ -0,0 +1,82 @@
+#!/bin/sh
+
+test_description='test `test-tool find-pack`'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_commit one &&
+       test_commit two &&
+       test_commit three &&
+       test_commit four &&
+       test_commit five
+'
+
+test_expect_success 'repack everything into a single packfile' '
+       git repack -a -d --no-write-bitmap-index &&
+
+       head_commit_pack=$(test-tool find-pack HEAD) &&
+       head_tree_pack=$(test-tool find-pack HEAD^{tree}) &&
+       one_pack=$(test-tool find-pack HEAD:one.t) &&
+       three_pack=$(test-tool find-pack HEAD:three.t) &&
+       old_commit_pack=$(test-tool find-pack HEAD~4) &&
+
+       test-tool find-pack --check-count 1 HEAD &&
+       test-tool find-pack --check-count=1 HEAD^{tree} &&
+       ! test-tool find-pack --check-count=0 HEAD:one.t &&
+       ! test-tool find-pack -c 2 HEAD:one.t &&
+       test-tool find-pack -c 1 HEAD:three.t &&
+
+       # Packfile exists at the right path
+       case "$head_commit_pack" in
+               ".git/objects/pack/pack-"*".pack") true ;;
+               *) false ;;
+       esac &&
+       test -f "$head_commit_pack" &&
+
+       # Everything is in the same pack
+       test "$head_commit_pack" = "$head_tree_pack" &&
+       test "$head_commit_pack" = "$one_pack" &&
+       test "$head_commit_pack" = "$three_pack" &&
+       test "$head_commit_pack" = "$old_commit_pack"
+'
+
+test_expect_success 'add more packfiles' '
+       git rev-parse HEAD^{tree} HEAD:two.t HEAD:four.t >objects &&
+       git pack-objects .git/objects/pack/mypackname1 >packhash1 <objects &&
+
+       git rev-parse HEAD~ HEAD~^{tree} HEAD:five.t >objects &&
+       git pack-objects .git/objects/pack/mypackname2 >packhash2 <objects &&
+
+       head_commit_pack=$(test-tool find-pack HEAD) &&
+
+       # HEAD^{tree} is in 2 packfiles
+       test-tool find-pack HEAD^{tree} >head_tree_packs &&
+       grep "$head_commit_pack" head_tree_packs &&
+       grep mypackname1 head_tree_packs &&
+       ! grep mypackname2 head_tree_packs &&
+       test-tool find-pack --check-count 2 HEAD^{tree} &&
+       ! test-tool find-pack --check-count 1 HEAD^{tree} &&
+
+       # HEAD:five.t is also in 2 packfiles
+       test-tool find-pack HEAD:five.t >five_packs &&
+       grep "$head_commit_pack" five_packs &&
+       ! grep mypackname1 five_packs &&
+       grep mypackname2 five_packs &&
+       test-tool find-pack -c 2 HEAD:five.t &&
+       ! test-tool find-pack --check-count=0 HEAD:five.t
+'
+
+test_expect_success 'add more commits (as loose objects)' '
+       test_commit six &&
+       test_commit seven &&
+
+       test -z "$(test-tool find-pack HEAD)" &&
+       test -z "$(test-tool find-pack HEAD:six.t)" &&
+       test-tool find-pack --check-count 0 HEAD &&
+       test-tool find-pack -c 0 HEAD:six.t &&
+       ! test-tool find-pack -c 1 HEAD:seven.t
+'
+
+test_done
index 37ee5091b5caa7c087663cbb51f701084227c414..3f9e7f62e458c4db06a686700a15c5e08f47fad9 100755 (executable)
@@ -264,4 +264,27 @@ test_expect_success 'rev-parse --since= unsqueezed ordering' '
        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
index beb2ec2a55c05b697a68232c305792f718b5c966..7badd72488d664ff776e1004f620df1fbc774cdc 100755 (executable)
@@ -12,7 +12,7 @@ TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
-# 120 character name
+# 120-character name
 name=aaaaaaaaaa
 name=$name$name$name$name$name$name$name$name$name$name$name$name
 test_expect_success 'preparation' '
@@ -58,7 +58,7 @@ while read verb expect cmd args
 do
        # No width limit applied when statNameWidth is ignored
        case "$expect" in expect72|expect.6030)
-               test_expect_success "$cmd $verb statNameWidth config with long name" '
+               test_expect_success "$cmd $verb diff.statNameWidth with long name" '
                        git -c diff.statNameWidth=30 $cmd $args >output &&
                        grep " | " output >actual &&
                        test_cmp $expect actual
@@ -66,7 +66,7 @@ do
        esac
        # Maximum width limit still applied when statNameWidth is ignored
        case "$expect" in expect.60|expect.6030)
-               test_expect_success "$cmd --stat=width $verb statNameWidth config with long name" '
+               test_expect_success "$cmd --stat=width $verb diff.statNameWidth with long name" '
                        git -c diff.statNameWidth=30 $cmd $args --stat=60 >output &&
                        grep " | " output >actual &&
                        test_cmp $expect actual
@@ -111,7 +111,7 @@ do
                test_cmp $expect.6030 actual
        '
 
-       test_expect_success "$cmd --stat-name-width with long name" '
+       test_expect_success "$cmd --stat-name-width=width with long name" '
                git $cmd $args --stat-name-width=30 >output &&
                grep " | " output >actual &&
                test_cmp $expect.6030 actual
@@ -123,7 +123,7 @@ expect show --stat
 expect log -1 --stat
 EOF
 
-test_expect_success 'preparation for big change tests' '
+test_expect_success 'preparation for big-change tests' '
        >abcd &&
        git add abcd &&
        git commit -m message &&
@@ -139,7 +139,7 @@ cat >expect72 <<'EOF'
  abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 EOF
-test_expect_success "format-patch --cover-letter ignores COLUMNS (big change)" '
+test_expect_success "format-patch --cover-letter ignores COLUMNS with big change" '
        COLUMNS=200 git format-patch -1 --stdout --cover-letter >output &&
        grep " | " output >actual &&
        test_cmp expect72 actual
@@ -159,7 +159,7 @@ cat >expect200-graph <<'EOF'
 EOF
 while read verb expect cmd args
 do
-       test_expect_success "$cmd $verb COLUMNS (big change)" '
+       test_expect_success "$cmd $verb COLUMNS with big change" '
                COLUMNS=200 git $cmd $args >output &&
                grep " | " output >actual &&
                test_cmp "$expect" actual
@@ -167,7 +167,7 @@ do
 
        case "$cmd" in diff|show) continue;; esac
 
-       test_expect_success "$cmd --graph $verb COLUMNS (big change)" '
+       test_expect_success "$cmd --graph $verb COLUMNS with big change" '
                COLUMNS=200 git $cmd $args --graph >output &&
                grep " | " output >actual &&
                test_cmp "$expect-graph" actual
@@ -187,7 +187,7 @@ cat >expect40-graph <<'EOF'
 EOF
 while read verb expect cmd args
 do
-       test_expect_success "$cmd $verb not enough COLUMNS (big change)" '
+       test_expect_success "$cmd $verb not enough COLUMNS with big change" '
                COLUMNS=40 git $cmd $args >output &&
                grep " | " output >actual &&
                test_cmp "$expect" actual
@@ -195,7 +195,7 @@ do
 
        case "$cmd" in diff|show) continue;; esac
 
-       test_expect_success "$cmd --graph $verb not enough COLUMNS (big change)" '
+       test_expect_success "$cmd --graph $verb not enough COLUMNS with big change" '
                COLUMNS=40 git $cmd $args --graph >output &&
                grep " | " output >actual &&
                test_cmp "$expect-graph" actual
@@ -215,7 +215,7 @@ cat >expect40-graph <<'EOF'
 EOF
 while read verb expect cmd args
 do
-       test_expect_success "$cmd $verb statGraphWidth config" '
+       test_expect_success "$cmd $verb diff.statGraphWidth" '
                git -c diff.statGraphWidth=26 $cmd $args >output &&
                grep " | " output >actual &&
                test_cmp "$expect" actual
@@ -223,7 +223,7 @@ do
 
        case "$cmd" in diff|show) continue;; esac
 
-       test_expect_success "$cmd --graph $verb statGraphWidth config" '
+       test_expect_success "$cmd --graph $verb diff.statGraphWidth" '
                git -c diff.statGraphWidth=26 $cmd $args --graph >output &&
                grep " | " output >actual &&
                test_cmp "$expect-graph" actual
@@ -255,7 +255,7 @@ do
                test_cmp expect actual
        '
 
-       test_expect_success "$cmd --stat-graph-width with big change" '
+       test_expect_success "$cmd --stat-graph-width=width with big change" '
                git $cmd $args --stat-graph-width=26 >output &&
                grep " | " output >actual &&
                test_cmp expect actual
@@ -269,7 +269,7 @@ do
                test_cmp expect-graph actual
        '
 
-       test_expect_success "$cmd --stat-graph-width --graph with big change" '
+       test_expect_success "$cmd --stat-graph-width=width --graph with big change" '
                git $cmd $args --stat-graph-width=26 --graph >output &&
                grep " | " output >actual &&
                test_cmp expect-graph actual
@@ -281,7 +281,7 @@ show --stat
 log -1 --stat
 EOF
 
-test_expect_success 'preparation for long filename tests' '
+test_expect_success 'preparation for long-name tests' '
        cp abcd aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
        git add aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
        git commit -m message
@@ -329,7 +329,7 @@ cat >expect200-graph <<'EOF'
 EOF
 while read verb expect cmd args
 do
-       test_expect_success "$cmd $verb COLUMNS (long filename)" '
+       test_expect_success "$cmd $verb COLUMNS with long name" '
                COLUMNS=200 git $cmd $args >output &&
                grep " | " output >actual &&
                test_cmp "$expect" actual
@@ -337,7 +337,7 @@ do
 
        case "$cmd" in diff|show) continue;; esac
 
-       test_expect_success "$cmd --graph $verb COLUMNS (long filename)" '
+       test_expect_success "$cmd --graph $verb COLUMNS with long name" '
                COLUMNS=200 git $cmd $args --graph >output &&
                grep " | " output >actual &&
                test_cmp "$expect-graph" actual
@@ -358,7 +358,7 @@ EOF
 while read verb expect cmd args
 do
        test_expect_success COLUMNS_CAN_BE_1 \
-               "$cmd $verb prefix greater than COLUMNS (big change)" '
+               "$cmd $verb prefix greater than COLUMNS with big change" '
                COLUMNS=1 git $cmd $args >output &&
                grep " | " output >actual &&
                test_cmp "$expect" actual
@@ -367,7 +367,7 @@ do
        case "$cmd" in diff|show) continue;; esac
 
        test_expect_success COLUMNS_CAN_BE_1 \
-               "$cmd --graph $verb prefix greater than COLUMNS (big change)" '
+               "$cmd --graph $verb prefix greater than COLUMNS with big change" '
                COLUMNS=1 git $cmd $args --graph >output &&
                grep " | " output >actual &&
                test_cmp "$expect-graph" actual
@@ -382,8 +382,14 @@ EOF
 cat >expect <<'EOF'
  abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 EOF
-test_expect_success 'merge --stat respects COLUMNS (big change)' '
-       git checkout -b branch HEAD^^ &&
+test_expect_success 'merge --stat respects diff.statGraphWidth with big change' '
+       git checkout -b branch1 HEAD^^ &&
+       git -c diff.statGraphWidth=26 merge --stat --no-ff main^ >output &&
+       grep " | " output >actual &&
+       test_cmp expect40 actual
+'
+test_expect_success 'merge --stat respects COLUMNS with big change' '
+       git checkout -b branch2 HEAD^^ &&
        COLUMNS=100 git merge --stat --no-ff main^ >output &&
        grep " | " output >actual &&
        test_cmp expect actual
@@ -392,7 +398,17 @@ test_expect_success 'merge --stat respects COLUMNS (big change)' '
 cat >expect <<'EOF'
  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++
 EOF
-test_expect_success 'merge --stat respects COLUMNS (long filename)' '
+cat >expect.30 <<'EOF'
+ ...aaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++++++++++++++++++++++
+EOF
+test_expect_success 'merge --stat respects diff.statNameWidth with long name' '
+       git switch branch1 &&
+       git -c diff.statNameWidth=30 merge --stat --no-ff main >output &&
+       grep " | " output >actual &&
+       test_cmp expect.30 actual
+'
+test_expect_success 'merge --stat respects COLUMNS with long name' '
+       git switch branch2 &&
        COLUMNS=100 git merge --stat --no-ff main >output &&
        grep " | " output >actual &&
        test_cmp expect actual
index 2d650d8f1032ca7c113fa0f48c72719fb08ab245..541642650f533816ae8ea6490141893cbc111ce4 100755 (executable)
@@ -34,7 +34,7 @@ test_expect_success setup '
        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 &&
@@ -109,6 +109,13 @@ do
                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 &&
@@ -143,7 +150,7 @@ do
        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" '
@@ -169,7 +176,7 @@ do
 
        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" '
index 16626e4fe96f48098ecca8c64e8a33929e169f9d..e3d655e6b8b5565d619298b16da010ee671667a2 100755 (executable)
@@ -590,9 +590,9 @@ test_expect_success 'pretty format %decorate' '
        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
@@ -956,6 +956,36 @@ test_expect_success '%S in git log --format works with other placeholders (part
        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 &&
index f70c46bbbfa2c8fc9cc5ca1b620c9a80f29e382e..79055978690962fc3b58cd754a9383a88148f0f9 100755 (executable)
@@ -5,6 +5,7 @@ test_description='git log --graph of skewed left octopus merge.'
 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
 
index 28d0779a8c599ee9eab9b0b31afe1a57bb558c28..b877ac723516dc710fd42c17b65877d795d874ff 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git log --graph of skewed merges'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-log-graph.sh
 
index 6e01e2629c1b158464ab29e4086fb4eeea16bbaa..613f0710e90511c6419e843eec3cb4d11aafc437 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git log with filter options limiting the output'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup test' '
index 57c4f26e4613a97644ad5822b1f6a0c4572a33a4..9c197260d5bbf54d3ad578c27f54e278d88ab97b 100755 (executable)
@@ -86,6 +86,33 @@ EXPECTED
        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" &&
index 250f721795b5bd2ecba3090b7e181c50673540aa..b2c8a43fce311570e358d77a3b062228adcbbf68 100755 (executable)
@@ -22,6 +22,7 @@ test_expect_success setup '
        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 &&
@@ -46,6 +47,13 @@ test_expect_success setup '
        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 &&
@@ -97,6 +105,21 @@ test_expect_success 'Content merge and a few conflicts' '
        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 &&
index b26d476c646fdb63a9569d98bc9d2792dd6f3426..2ff3eef9a3b8cad7f95253c79283d3ac542802fd 100755 (executable)
@@ -53,6 +53,14 @@ test_expect_success 'verify blob:none packfile has no blobs' '
        ! grep blob verify_result
 '
 
+test_expect_success 'verify blob:none packfile without --stdout' '
+       git -C r1 pack-objects --revs --filter=blob:none mypackname >packhash <<-EOF &&
+       HEAD
+       EOF
+       git -C r1 verify-pack -v "mypackname-$(cat packhash).pack" >verify_result &&
+       ! grep blob verify_result
+'
+
 test_expect_success 'verify normal and blob:none packfiles have same commits/trees' '
        git -C r1 verify-pack -v ../all.pack >verify_result &&
        grep -E "commit|tree" verify_result |
index 3c8482d073d13913c818881360db39aaf7516aec..97eb6d2e72bac5c3936965c269ff9bfa56d87568 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='split commit graph'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-chunk.sh
 
index e5ff3e07adedad915115c7c04b4abba562780403..fc6a242b56d88686036ff019003352ee806ef8ce 100755 (executable)
@@ -1,6 +1,8 @@
 #!/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
index 264de29c35c11cbe09d31d57629aabaae0423b35..079b2f2536e6974c90b152226d8ea2189016a6f6 100755 (executable)
@@ -5,6 +5,7 @@ test_description='pull options'
 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' '
index 8ac6b2a1d0a286cac120c751e5e3038e465dc074..ed773e7432694b7ccc05a4d09084f238c9ff4b42 100755 (executable)
@@ -1,6 +1,8 @@
 #!/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"
index 5a67bbc760fdc8c2b53dc7721a144e033bf8e934..ced40157ed68c077c17cb4758d1ebbc529e67d9c 100755 (executable)
@@ -5,6 +5,7 @@ test_description='ancestor culling and limiting by parent number'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 check_revlist () {
index 17b54d625d0e468047377c378faae25c50633c9b..5f414abc89267d3f3729366d67f8b7d6aec52c37 100755 (executable)
@@ -5,6 +5,7 @@ test_description='recursive merge corner cases involving criss-cross merges'
 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
 
index b16031465f34b20600cadd51c2f620ec64ae0e54..2b42f095dcba5b228a60cb6ce2ea5678e611c893 100755 (executable)
@@ -5,6 +5,7 @@ test_description='"git merge" top-level frontend'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 t3033_reset () {
index c9a86f2e947e4a2943438cc5adcaaeab39a465d9..daa507862c65ea73d39c555ed2c316503094be49 100755 (executable)
@@ -8,6 +8,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 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
 
index 69509d0c11db655a12fc6ed20215ed25976c46eb..04acf22d930421cf206556c54b1acf0a22b644d4 100755 (executable)
@@ -202,6 +202,30 @@ test_expect_success 'one of gc.reflogExpire{Unreachable,}=never does not skip "e
        grep -E "^trace: (built-in|exec|run_command): git reflog expire --" trace.out
 '
 
+test_expect_success 'gc.repackFilter launches repack with a filter' '
+       git clone --no-local --bare . bare.git &&
+
+       git -C bare.git -c gc.cruftPacks=false gc &&
+       test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&
+
+       GIT_TRACE=$(pwd)/trace.out git -C bare.git -c gc.repackFilter=blob:none \
+               -c repack.writeBitmaps=false -c gc.cruftPacks=false gc &&
+       test_stdout_line_count = 2 ls bare.git/objects/pack/*.pack &&
+       grep -E "^trace: (built-in|exec|run_command): git repack .* --filter=blob:none ?.*" trace.out
+'
+
+test_expect_success 'gc.repackFilterTo store filtered out objects' '
+       test_when_finished "rm -rf bare.git filtered.git" &&
+
+       git init --bare filtered.git &&
+       git -C bare.git -c gc.repackFilter=blob:none \
+               -c gc.repackFilterTo=../filtered.git/objects/pack/pack \
+               -c repack.writeBitmaps=false -c gc.cruftPacks=false gc &&
+
+       test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&
+       test_stdout_line_count = 1 ls filtered.git/objects/pack/*.pack
+'
+
 prepare_cruft_history () {
        test_commit base &&
 
@@ -303,6 +327,33 @@ test_expect_success 'gc.bigPackThreshold ignores cruft packs' '
        )
 '
 
+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
index e410c41234c7d36069c27a69598e02a3f354322f..9e70a7c763a210c6b1d855e2cc5dbb2ca403394a 100755 (executable)
@@ -1,6 +1,8 @@
 #!/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
index 232065504cbfdcba60d3f5f69ffb4536b5ae99e8..a5d1bc5c54ae095f79bf9b3ee420ba004bb18a88 100755 (executable)
@@ -11,6 +11,10 @@ as expected.
 
 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' '
@@ -27,26 +31,28 @@ test_expect_success 'submodule config cache 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
@@ -57,13 +63,12 @@ test_expect_success 'test submodule set-branch --branch' '
 '
 
 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
@@ -71,10 +76,9 @@ test_expect_success 'test submodule set-branch --default' '
 '
 
 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
@@ -85,17 +89,43 @@ test_expect_success 'test submodule set-branch -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
index d6bf62b3ac670fc0be8167150cdf79cbd9000a45..bf7f15ee7973958970dfe0d1b34cea9d25e635a9 100755 (executable)
@@ -25,17 +25,26 @@ test_expect_success 'submodule config cache setup' '
                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 &&
@@ -44,15 +53,28 @@ test_expect_success 'test submodule set-url' '
        ) &&
        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
 '
 
index 97f10905d23fd3077aa9dd253fa079eb9c5be73d..832aff0616736025cb78d2b35e3823594b543b1d 100755 (executable)
@@ -489,7 +489,7 @@ test_expect_success 'multiline field treated as atomic for neighbor check' '
 '
 
 test_expect_success 'with config setup' '
-       git config trailer.ack.key "Acked-by: " &&
+       test_config trailer.ack.key "Acked-by: " &&
        cat >expected <<-\EOF &&
 
                Acked-by: Peff
@@ -503,8 +503,8 @@ test_expect_success 'with config setup' '
 '
 
 test_expect_success 'with config setup and ":=" as separators' '
-       git config trailer.separators ":=" &&
-       git config trailer.ack.key "Acked-by= " &&
+       test_config trailer.separators ":=" &&
+       test_config trailer.ack.key "Acked-by= " &&
        cat >expected <<-\EOF &&
 
                Acked-by= Peff
@@ -518,7 +518,7 @@ test_expect_success 'with config setup and ":=" as separators' '
 '
 
 test_expect_success 'with config setup and "%" as separators' '
-       git config trailer.separators "%" &&
+       test_config trailer.separators "%" &&
        cat >expected <<-\EOF &&
 
                bug% 42
@@ -532,6 +532,7 @@ test_expect_success 'with config setup and "%" as separators' '
 '
 
 test_expect_success 'with "%" as separators and a message with trailers' '
+       test_config trailer.separators "%" &&
        cat >special_message <<-\EOF &&
                Special Message
 
@@ -553,8 +554,8 @@ test_expect_success 'with "%" as separators and a message with trailers' '
 '
 
 test_expect_success 'with config setup and ":=#" as separators' '
-       git config trailer.separators ":=#" &&
-       git config trailer.bug.key "Bug #" &&
+       test_config trailer.separators ":=#" &&
+       test_config trailer.bug.key "Bug #" &&
        cat >expected <<-\EOF &&
 
                Bug #42
@@ -581,6 +582,8 @@ test_expect_success 'with basic patch' '
 '
 
 test_expect_success 'with commit complex message as argument' '
+       test_config trailer.separators ":=" &&
+       test_config trailer.ack.key "Acked-by= " &&
        cat complex_message_body complex_message_trailers >complex_message &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
@@ -594,6 +597,8 @@ test_expect_success 'with commit complex message as argument' '
 '
 
 test_expect_success 'with 2 files arguments' '
+       test_config trailer.separators ":=" &&
+       test_config trailer.ack.key "Acked-by= " &&
        cat basic_message >>expected &&
        echo >>expected &&
        cat basic_patch >>expected &&
@@ -677,6 +682,9 @@ test_expect_success 'with message that has an old style conflict block' '
 '
 
 test_expect_success 'with commit complex message and trailer args' '
+       test_config trailer.separators ":=#" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.bug.key "Bug #" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Fixes: Z
@@ -692,6 +700,9 @@ test_expect_success 'with commit complex message and trailer args' '
 '
 
 test_expect_success 'with complex patch, args and --trim-empty' '
+       test_config trailer.separators ":=#" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.bug.key "Bug #" &&
        cat complex_message >complex_patch &&
        cat basic_patch >>complex_patch &&
        cat complex_message_body >expected &&
@@ -746,7 +757,10 @@ test_expect_success POSIXPERM,SANITY "in-place editing doesn't clobber original
 '
 
 test_expect_success 'using "where = before"' '
-       git config trailer.bug.where "before" &&
+       test_config trailer.separators ":=#" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -762,7 +776,9 @@ test_expect_success 'using "where = before"' '
 '
 
 test_expect_success 'overriding configuration with "--where after"' '
-       git config trailer.ack.where "before" &&
+       test_config trailer.separators ":=" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "before" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Fixes: Z
@@ -776,7 +792,12 @@ test_expect_success 'overriding configuration with "--where after"' '
        test_cmp expected actual
 '
 
-test_expect_success 'using "where = before" with "--no-where"' '
+test_expect_success 'using "--where after" with "--no-where"' '
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "before" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -791,8 +812,59 @@ test_expect_success 'using "where = before" with "--no-where"' '
        test_cmp expected actual
 '
 
+# Check whether using "--no-where" clears out only the "--where after", such
+# that we still use the configuration in trailer.where (which is different from
+# the hardcoded default (in WHERE_END) assuming the absence of .gitconfig).
+# Here, the "start" setting of trailer.where is respected, so the new "Acked-by"
+# and "Bug" trailers are placed at the beginning, and not at the end which is
+# the harcoded default.
+test_expect_success 'using "--where after" with "--no-where" defaults to configuration' '
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.separators ":=#" &&
+       test_config trailer.where "start" &&
+       cat complex_message_body >expected &&
+       sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+               Bug #42
+               Acked-by= Peff
+               Fixes: Z
+               Acked-by= Z
+               Reviewed-by: Z
+               Signed-off-by: Z
+       EOF
+       git interpret-trailers --where after --no-where --trailer "ack: Peff" \
+               --trailer "bug: 42" complex_message >actual &&
+       test_cmp expected actual
+'
+
+# The "--where after" will only get respected for the trailer that came
+# immediately after it. For the next trailer (Bug #42), we default to using the
+# hardcoded WHERE_END because we don't have any "trailer.where" or
+# "trailer.bug.where" configured.
+test_expect_success 'using "--no-where" defaults to harcoded default if nothing configured' '
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.separators ":=#" &&
+       cat complex_message_body >expected &&
+       sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+               Fixes: Z
+               Acked-by= Z
+               Acked-by= Peff
+               Reviewed-by: Z
+               Signed-off-by: Z
+               Bug #42
+       EOF
+       git interpret-trailers --where after --trailer "ack: Peff" --no-where \
+               --trailer "bug: 42" complex_message >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'using "where = after"' '
-       git config trailer.ack.where "after" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -808,8 +880,11 @@ test_expect_success 'using "where = after"' '
 '
 
 test_expect_success 'using "where = end"' '
-       git config trailer.review.key "Reviewed-by" &&
-       git config trailer.review.where "end" &&
+       test_config trailer.review.key "Reviewed-by" &&
+       test_config trailer.review.where "end" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.separators ":=" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Fixes: Z
@@ -827,8 +902,11 @@ test_expect_success 'using "where = end"' '
 '
 
 test_expect_success 'using "where = start"' '
-       git config trailer.review.key "Reviewed-by" &&
-       git config trailer.review.where "start" &&
+       test_config trailer.review.key "Reviewed-by" &&
+       test_config trailer.review.where "start" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.separators ":=" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Reviewed-by: Johannes
@@ -846,8 +924,13 @@ test_expect_success 'using "where = start"' '
 '
 
 test_expect_success 'using "where = before" for a token in the middle of the message' '
-       git config trailer.review.key "Reviewed-by:" &&
-       git config trailer.review.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.review.where "before" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -864,6 +947,12 @@ test_expect_success 'using "where = before" for a token in the middle of the mes
 '
 
 test_expect_success 'using "where = before" and --trim-empty' '
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        cat >>expected <<-\EOF &&
                Bug #46
@@ -878,6 +967,13 @@ test_expect_success 'using "where = before" and --trim-empty' '
 '
 
 test_expect_success 'the default is "ifExists = addIfDifferentNeighbor"' '
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.review.where "before" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -896,7 +992,13 @@ test_expect_success 'the default is "ifExists = addIfDifferentNeighbor"' '
 '
 
 test_expect_success 'default "ifExists" is now "addIfDifferent"' '
-       git config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -914,8 +1016,14 @@ test_expect_success 'default "ifExists" is now "addIfDifferent"' '
 '
 
 test_expect_success 'using "ifExists = addIfDifferent" with "where = end"' '
-       git config trailer.ack.ifExists "addIfDifferent" &&
-       git config trailer.ack.where "end" &&
+       test_config trailer.ack.ifExists "addIfDifferent" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "end" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -932,8 +1040,14 @@ test_expect_success 'using "ifExists = addIfDifferent" with "where = end"' '
 '
 
 test_expect_success 'using "ifExists = addIfDifferent" with "where = before"' '
-       git config trailer.ack.ifExists "addIfDifferent" &&
-       git config trailer.ack.where "before" &&
+       test_config trailer.ack.ifExists "addIfDifferent" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "before" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -950,8 +1064,14 @@ test_expect_success 'using "ifExists = addIfDifferent" with "where = before"' '
 '
 
 test_expect_success 'using "ifExists = addIfDifferentNeighbor" with "where = end"' '
-       git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
-       git config trailer.ack.where "end" &&
+       test_config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "end" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -973,8 +1093,14 @@ test_expect_success 'using "ifExists = addIfDifferentNeighbor" with "where = end
 '
 
 test_expect_success 'using "ifExists = addIfDifferentNeighbor"  with "where = after"' '
-       git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
-       git config trailer.ack.where "after" &&
+       test_config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -995,7 +1121,11 @@ test_expect_success 'using "ifExists = addIfDifferentNeighbor"  with "where = af
 '
 
 test_expect_success 'using "ifExists = addIfDifferentNeighbor" and --trim-empty' '
-       git config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+       test_config trailer.ack.ifExists "addIfDifferentNeighbor" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        cat >>expected <<-\EOF &&
                Bug #42
@@ -1011,8 +1141,14 @@ test_expect_success 'using "ifExists = addIfDifferentNeighbor" and --trim-empty'
 '
 
 test_expect_success 'using "ifExists = add" with "where = end"' '
-       git config trailer.ack.ifExists "add" &&
-       git config trailer.ack.where "end" &&
+       test_config trailer.ack.ifExists "add" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "end" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1036,8 +1172,14 @@ test_expect_success 'using "ifExists = add" with "where = end"' '
 '
 
 test_expect_success 'using "ifExists = add" with "where = after"' '
-       git config trailer.ack.ifExists "add" &&
-       git config trailer.ack.where "after" &&
+       test_config trailer.ack.ifExists "add" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1058,8 +1200,15 @@ test_expect_success 'using "ifExists = add" with "where = after"' '
 '
 
 test_expect_success 'overriding configuration with "--if-exists replace"' '
-       git config trailer.fix.key "Fixes: " &&
-       git config trailer.fix.ifExists "add" &&
+       test_config trailer.fix.key "Fixes: " &&
+       test_config trailer.fix.ifExists "add" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.review.where "before" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1074,9 +1223,66 @@ test_expect_success 'overriding configuration with "--if-exists replace"' '
        test_cmp expected actual
 '
 
+# "trailer.ifexists" is set to "doNothing", so using "--no-if-exists" defaults
+# to this "doNothing" behavior. So the "Fixes: 53" trailer does not get added.
+test_expect_success 'using "--if-exists replace" with "--no-if-exists" defaults to configuration' '
+       test_config trailer.ifexists "doNothing" &&
+       cat complex_message_body >expected &&
+       sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+               Fixes: Z
+               Acked-by: Z
+               Reviewed-by: Z
+               Signed-off-by: Z
+       EOF
+       git interpret-trailers --if-exists replace --no-if-exists --trailer "Fixes: 53" \
+               <complex_message >actual &&
+       test_cmp expected actual
+'
+
+# No "ifexists" configuration is set, so using "--no-if-exists" makes it default
+# to addIfDifferentNeighbor. Because we do have a different neighbor "Fixes: 53"
+# (because it got added by overriding with "--if-exists replace" earlier in the
+# arguments list), we add "Signed-off-by: addme".
+test_expect_success 'using "--no-if-exists" defaults to hardcoded default if nothing configured' '
+       cat complex_message_body >expected &&
+       sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+               Acked-by: Z
+               Reviewed-by: Z
+               Signed-off-by: Z
+               Fixes: 53
+               Signed-off-by: addme
+       EOF
+       git interpret-trailers --if-exists replace --trailer "Fixes: 53" --no-if-exists \
+               --trailer "Signed-off-by: addme" <complex_message >actual &&
+       test_cmp expected actual
+'
+
+# The second "Fixes: 53" trailer is discarded, because the "--no-if-exists" here
+# makes us default to addIfDifferentNeighbor, and we already added the "Fixes:
+# 53" trailer earlier in the argument list.
+test_expect_success 'using "--no-if-exists" defaults to hardcoded default if nothing configured (no addition)' '
+       cat complex_message_body >expected &&
+       sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+               Acked-by: Z
+               Reviewed-by: Z
+               Signed-off-by: Z
+               Fixes: 53
+       EOF
+       git interpret-trailers --if-exists replace --trailer "Fixes: 53" --no-if-exists \
+               --trailer "Fixes: 53" <complex_message >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'using "ifExists = replace"' '
-       git config trailer.fix.key "Fixes: " &&
-       git config trailer.fix.ifExists "replace" &&
+       test_config trailer.fix.key "Fixes: " &&
+       test_config trailer.fix.ifExists "replace" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1095,7 +1301,16 @@ test_expect_success 'using "ifExists = replace"' '
 '
 
 test_expect_success 'using "ifExists = replace" with "where = after"' '
-       git config trailer.fix.where "after" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.fix.key "Fixes: " &&
+       test_config trailer.fix.ifExists "replace" &&
+       test_config trailer.fix.where "after" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1114,7 +1329,15 @@ test_expect_success 'using "ifExists = replace" with "where = after"' '
 '
 
 test_expect_success 'using "ifExists = doNothing"' '
-       git config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.fix.key "Fixes: " &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1133,8 +1356,17 @@ test_expect_success 'using "ifExists = doNothing"' '
 '
 
 test_expect_success 'the default is "ifMissing = add"' '
-       git config trailer.cc.key "Cc: " &&
-       git config trailer.cc.where "before" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.cc.key "Cc: " &&
+       test_config trailer.cc.where "before" &&
+       test_config trailer.fix.key "Fixes: " &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1154,7 +1386,14 @@ test_expect_success 'the default is "ifMissing = add"' '
 '
 
 test_expect_success 'overriding configuration with "--if-missing doNothing"' '
-       git config trailer.ifmissing "add" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.fix.key "Fixes: " &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.ifmissing "add" &&
+       test_config trailer.separators ":=" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Fixes: Z
@@ -1173,7 +1412,13 @@ test_expect_success 'overriding configuration with "--if-missing doNothing"' '
 '
 
 test_expect_success 'when default "ifMissing" is "doNothing"' '
-       git config trailer.ifmissing "doNothing" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.ifmissing "doNothing" &&
+       test_config trailer.separators ":=" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Fixes: Z
@@ -1187,14 +1432,21 @@ test_expect_success 'when default "ifMissing" is "doNothing"' '
                --trailer "cc=Linus" --trailer "ack: Junio" \
                --trailer "fix=22" --trailer "bug: 42" --trailer "ack: Peff" \
                <complex_message >actual &&
-       test_cmp expected actual &&
-       git config trailer.ifmissing "add"
+       test_cmp expected actual
 '
 
 test_expect_success 'using "ifMissing = add" with "where = end"' '
-       git config trailer.cc.key "Cc: " &&
-       git config trailer.cc.where "end" &&
-       git config trailer.cc.ifMissing "add" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.cc.key "Cc: " &&
+       test_config trailer.cc.ifMissing "add" &&
+       test_config trailer.cc.where "end" &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1214,9 +1466,17 @@ test_expect_success 'using "ifMissing = add" with "where = end"' '
 '
 
 test_expect_success 'using "ifMissing = add" with "where = before"' '
-       git config trailer.cc.key "Cc: " &&
-       git config trailer.cc.where "before" &&
-       git config trailer.cc.ifMissing "add" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.cc.key "Cc: " &&
+       test_config trailer.cc.ifMissing "add" &&
+       test_config trailer.cc.where "before" &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Cc: Linus
@@ -1236,7 +1496,15 @@ test_expect_success 'using "ifMissing = add" with "where = before"' '
 '
 
 test_expect_success 'using "ifMissing = doNothing"' '
-       git config trailer.cc.ifMissing "doNothing" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.cc.ifMissing "doNothing" &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1254,9 +1522,50 @@ test_expect_success 'using "ifMissing = doNothing"' '
        test_cmp expected actual
 '
 
+# Ignore the "IgnoredTrailer" because of "--if-missing doNothing", but also
+# ignore the "StillIgnoredTrailer" because we set "trailer.ifMissing" to
+# "doNothing" in configuration.
+test_expect_success 'using "--no-if-missing" defaults to configuration' '
+       test_config trailer.ifMissing "doNothing" &&
+       cat complex_message_body >expected &&
+       sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+                       Fixes: Z
+                       Acked-by: Z
+                       Reviewed-by: Z
+                       Signed-off-by: Z
+       EOF
+       git interpret-trailers --if-missing doNothing --trailer "IgnoredTrailer: ignoreme" --no-if-missing \
+                       --trailer "StillIgnoredTrailer: ignoreme" <complex_message >actual &&
+       test_cmp expected actual
+'
+
+# Add the "AddedTrailer" because the "--no-if-missing" clears the "--if-missing
+# doNothing" from earlier in the argument list.
+test_expect_success 'using "--no-if-missing" defaults to hardcoded default if nothing configured' '
+       cat complex_message_body >expected &&
+       sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
+                       Fixes: Z
+                       Acked-by: Z
+                       Reviewed-by: Z
+                       Signed-off-by: Z
+                       AddedTrailer: addme
+       EOF
+       git interpret-trailers --if-missing doNothing --trailer "IgnoredTrailer: ignoreme" --no-if-missing \
+                       --trailer "AddedTrailer: addme" <complex_message >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'default "where" is now "after"' '
        git config trailer.where "after" &&
-       git config --unset trailer.ack.where &&
+       test_config trailer.ack.ifExists "add" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.ack.where "after" &&
+       test_config trailer.bug.key "Bug #" &&
+       test_config trailer.bug.where "before" &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=#" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Bug #42
@@ -1280,10 +1589,15 @@ test_expect_success 'default "where" is now "after"' '
 '
 
 test_expect_success 'with simple command' '
-       git config trailer.sign.key "Signed-off-by: " &&
-       git config trailer.sign.where "after" &&
-       git config trailer.sign.ifExists "addIfDifferentNeighbor" &&
-       git config trailer.sign.command "echo \"A U Thor <author@example.com>\"" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.sign.command "echo \"A U Thor <author@example.com>\"" &&
+       test_config trailer.sign.key "Signed-off-by: " &&
+       test_config trailer.sign.ifExists "addIfDifferentNeighbor" &&
+       test_config trailer.sign.where "after" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Fixes: Z
@@ -1298,8 +1612,14 @@ test_expect_success 'with simple command' '
 '
 
 test_expect_success 'with command using committer information' '
-       git config trailer.sign.ifExists "addIfDifferent" &&
-       git config trailer.sign.command "echo \"\$GIT_COMMITTER_NAME <\$GIT_COMMITTER_EMAIL>\"" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.sign.command "echo \"\$GIT_COMMITTER_NAME <\$GIT_COMMITTER_EMAIL>\"" &&
+       test_config trailer.sign.key "Signed-off-by: " &&
+       test_config trailer.sign.ifExists "addIfDifferent" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Fixes: Z
@@ -1314,10 +1634,15 @@ test_expect_success 'with command using committer information' '
 '
 
 test_expect_success 'with command using author information' '
-       git config trailer.sign.key "Signed-off-by: " &&
-       git config trailer.sign.where "after" &&
-       git config trailer.sign.ifExists "addIfDifferentNeighbor" &&
-       git config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.fix.ifExists "doNothing" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+       test_config trailer.sign.key "Signed-off-by: " &&
+       test_config trailer.sign.ifExists "addIfDifferentNeighbor" &&
+       test_config trailer.sign.where "after" &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
                Fixes: Z
@@ -1338,12 +1663,19 @@ test_expect_success 'setup a commit' '
 '
 
 test_expect_success 'cmd takes precedence over command' '
-       test_when_finished "git config --unset trailer.fix.cmd" &&
-       git config trailer.fix.ifExists "replace" &&
-       git config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
-       --abbrev-commit --abbrev=14 \"\$1\" || true" &&
-       git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" \
                --abbrev-commit --abbrev=14 \$ARG" &&
+       test_config trailer.fix.cmd "test -n \"\$1\" && git log -1 --oneline --format=\"%h (%aN)\" \
+       --abbrev-commit --abbrev=14 \"\$1\" || true" &&
+       test_config trailer.fix.key "Fixes: " &&
+       test_config trailer.fix.ifExists "replace" &&
+       test_config trailer.fix.where "after" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+       test_config trailer.sign.key "Signed-off-by: " &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=" &&
        FIXED=$(git log -1 --oneline --format="%h (%aN)" --abbrev-commit --abbrev=14 HEAD) &&
        cat complex_message_body >expected2 &&
        sed -e "s/ Z\$/ /" >>expected2 <<-EOF &&
@@ -1359,8 +1691,16 @@ test_expect_success 'cmd takes precedence over command' '
 '
 
 test_expect_success 'with command using $ARG' '
-       git config trailer.fix.ifExists "replace" &&
-       git config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.fix.command "git log -1 --oneline --format=\"%h (%s)\" --abbrev-commit --abbrev=14 \$ARG" &&
+       test_config trailer.fix.key "Fixes: " &&
+       test_config trailer.fix.ifExists "replace" &&
+       test_config trailer.fix.where "after" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+       test_config trailer.sign.key "Signed-off-by: " &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=" &&
        FIXED=$(git log -1 --oneline --format="%h (%s)" --abbrev-commit --abbrev=14 HEAD) &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-EOF &&
@@ -1376,8 +1716,16 @@ test_expect_success 'with command using $ARG' '
 '
 
 test_expect_success 'with failing command using $ARG' '
-       git config trailer.fix.ifExists "replace" &&
-       git config trailer.fix.command "false \$ARG" &&
+       test_config trailer.ack.key "Acked-by= " &&
+       test_config trailer.fix.command "false \$ARG" &&
+       test_config trailer.fix.key "Fixes: " &&
+       test_config trailer.fix.ifExists "replace" &&
+       test_config trailer.fix.where "after" &&
+       test_config trailer.review.key "Reviewed-by:" &&
+       test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+       test_config trailer.sign.key "Signed-off-by: " &&
+       test_config trailer.ifexists "addIfDifferent" &&
+       test_config trailer.separators ":=" &&
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-EOF &&
                Fixes: Z
@@ -1392,7 +1740,9 @@ test_expect_success 'with failing command using $ARG' '
 '
 
 test_expect_success 'with empty tokens' '
-       git config --unset trailer.fix.command &&
+       test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+       test_config trailer.sign.key "Signed-off-by: " &&
+       test_config trailer.ifexists "addIfDifferent" &&
        cat >expected <<-EOF &&
 
                Signed-off-by: A U Thor <author@example.com>
@@ -1403,7 +1753,8 @@ test_expect_success 'with empty tokens' '
 '
 
 test_expect_success 'with command but no key' '
-       git config --unset trailer.sign.key &&
+       test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+       test_config trailer.ifexists "addIfDifferent" &&
        cat >expected <<-EOF &&
 
                sign: A U Thor <author@example.com>
@@ -1414,7 +1765,9 @@ test_expect_success 'with command but no key' '
 '
 
 test_expect_success 'with no command and no key' '
-       git config --unset trailer.review.key &&
+       test_config trailer.review.where "before" &&
+       test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
+       test_config trailer.ifexists "addIfDifferent" &&
        cat >expected <<-EOF &&
 
                review: Junio
@@ -1426,6 +1779,8 @@ test_expect_success 'with no command and no key' '
 '
 
 test_expect_success 'with cut line' '
+       test_config trailer.review.where "before" &&
+       test_config trailer.sign.command "echo \"\$GIT_AUTHOR_NAME <\$GIT_AUTHOR_EMAIL>\"" &&
        cat >expected <<-\EOF &&
                my subject
 
@@ -1443,7 +1798,8 @@ test_expect_success 'with cut line' '
 '
 
 test_expect_success 'only trailers' '
-       git config trailer.sign.command "echo config-value" &&
+       test_config trailer.sign.command "echo config-value" &&
+       test_config trailer.ifexists "addIfDifferent" &&
        cat >expected <<-\EOF &&
                existing: existing-value
                sign: config-value
@@ -1462,7 +1818,7 @@ test_expect_success 'only trailers' '
 '
 
 test_expect_success 'only-trailers omits non-trailer in middle of block' '
-       git config trailer.sign.command "echo config-value" &&
+       test_config trailer.sign.command "echo config-value" &&
        cat >expected <<-\EOF &&
                Signed-off-by: nobody <nobody@nowhere>
                Signed-off-by: somebody <somebody@somewhere>
@@ -1482,7 +1838,7 @@ test_expect_success 'only-trailers omits non-trailer in middle of block' '
 '
 
 test_expect_success 'only input' '
-       git config trailer.sign.command "echo config-value" &&
+       test_config trailer.sign.command "echo config-value" &&
        cat >expected <<-\EOF &&
                existing: existing-value
        EOF
index ff085b086cc38f36a180e22ab02bbea12a29cc0c..3669d33bd5a15fc19fbfd2cbea14507c8e273642 100755 (executable)
@@ -4,6 +4,7 @@ test_description='git merge
 
 Testing octopus merge with more than 25 refs.'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
index 4887ca705b330e8cbf6f25595cbcadfdfd67f9f2..0e85b21ec82cc64e7d1e519c269a9bdcb189e01b 100755 (executable)
@@ -4,6 +4,7 @@ test_description='git merge
 
 Testing octopus merge when reducing parents to independent branches.'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # 0 - 1
index 89a62ac53b3d6012d13b3b1ce0dfa97d494060f2..9001674f2ea2201836613e4909d9442062f609f4 100755 (executable)
@@ -4,6 +4,7 @@ test_description="Test that merge state is as expected after failed merge"
 
 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' '
index 0b908ab2e7128052a0513e740b54ba308b9feadc..2179938c437e47552eccb7af85fa3e4669ccc1bf 100755 (executable)
@@ -4,6 +4,7 @@ test_description='test auto-generated merge messages'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 check_oneline() {
index 27b66807cd82cbb93c4985905a8de7945cc17cbc..d2975e6c93a07480e8da4e89bb1bd48faf397cba 100755 (executable)
@@ -327,6 +327,203 @@ test_expect_success 'auto-bitmaps do not complain if unavailable' '
        test_must_be_empty actual
 '
 
+test_expect_success 'repacking with a filter works' '
+       git -C bare.git repack -a -d &&
+       test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&
+       git -C bare.git -c repack.writebitmaps=false repack -a -d --filter=blob:none &&
+       test_stdout_line_count = 2 ls bare.git/objects/pack/*.pack &&
+       commit_pack=$(test-tool -C bare.git find-pack -c 1 HEAD) &&
+       blob_pack=$(test-tool -C bare.git find-pack -c 1 HEAD:file1) &&
+       test "$commit_pack" != "$blob_pack" &&
+       tree_pack=$(test-tool -C bare.git find-pack -c 1 HEAD^{tree}) &&
+       test "$tree_pack" = "$commit_pack" &&
+       blob_pack2=$(test-tool -C bare.git find-pack -c 1 HEAD:file2) &&
+       test "$blob_pack2" = "$blob_pack"
+'
+
+test_expect_success '--filter fails with --write-bitmap-index' '
+       test_must_fail \
+               env GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0 \
+               git -C bare.git repack -a -d --write-bitmap-index --filter=blob:none
+'
+
+test_expect_success 'repacking with two filters works' '
+       git init two-filters &&
+       (
+               cd two-filters &&
+               mkdir subdir &&
+               test_commit foo &&
+               test_commit subdir_bar subdir/bar &&
+               test_commit subdir_baz subdir/baz
+       ) &&
+       git clone --no-local --bare two-filters two-filters.git &&
+       (
+               cd two-filters.git &&
+               test_stdout_line_count = 1 ls objects/pack/*.pack &&
+               git -c repack.writebitmaps=false repack -a -d \
+                       --filter=blob:none --filter=tree:1 &&
+               test_stdout_line_count = 2 ls objects/pack/*.pack &&
+               commit_pack=$(test-tool find-pack -c 1 HEAD) &&
+               blob_pack=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+               root_tree_pack=$(test-tool find-pack -c 1 HEAD^{tree}) &&
+               subdir_tree_hash=$(git ls-tree --object-only HEAD -- subdir) &&
+               subdir_tree_pack=$(test-tool find-pack -c 1 "$subdir_tree_hash") &&
+
+               # Root tree and subdir tree are not in the same packfiles
+               test "$commit_pack" != "$blob_pack" &&
+               test "$commit_pack" = "$root_tree_pack" &&
+               test "$blob_pack" = "$subdir_tree_pack"
+       )
+'
+
+prepare_for_keep_packs () {
+       git init keep-packs &&
+       (
+               cd keep-packs &&
+               test_commit foo &&
+               test_commit bar
+       ) &&
+       git clone --no-local --bare keep-packs keep-packs.git &&
+       (
+               cd keep-packs.git &&
+
+               # Create two packs
+               # The first pack will contain all of the objects except one blob
+               git rev-list --objects --all >objs &&
+               grep -v "bar.t" objs | git pack-objects pack &&
+               # The second pack will contain the excluded object and be kept
+               packid=$(grep "bar.t" objs | git pack-objects pack) &&
+               >pack-$packid.keep &&
+
+               # Replace the existing pack with the 2 new ones
+               rm -f objects/pack/pack* &&
+               mv pack-* objects/pack/
+       )
+}
+
+test_expect_success '--filter works with .keep packs' '
+       prepare_for_keep_packs &&
+       (
+               cd keep-packs.git &&
+
+               foo_pack=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+               bar_pack=$(test-tool find-pack -c 1 HEAD:bar.t) &&
+               head_pack=$(test-tool find-pack -c 1 HEAD) &&
+
+               test "$foo_pack" != "$bar_pack" &&
+               test "$foo_pack" = "$head_pack" &&
+
+               git -c repack.writebitmaps=false repack -a -d --filter=blob:none &&
+
+               foo_pack_1=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+               bar_pack_1=$(test-tool find-pack -c 1 HEAD:bar.t) &&
+               head_pack_1=$(test-tool find-pack -c 1 HEAD) &&
+
+               # Object bar is still only in the old .keep pack
+               test "$foo_pack_1" != "$foo_pack" &&
+               test "$bar_pack_1" = "$bar_pack" &&
+               test "$head_pack_1" != "$head_pack" &&
+
+               test "$foo_pack_1" != "$bar_pack_1" &&
+               test "$foo_pack_1" != "$head_pack_1" &&
+               test "$bar_pack_1" != "$head_pack_1"
+       )
+'
+
+test_expect_success '--filter works with --pack-kept-objects and .keep packs' '
+       rm -rf keep-packs keep-packs.git &&
+       prepare_for_keep_packs &&
+       (
+               cd keep-packs.git &&
+
+               foo_pack=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+               bar_pack=$(test-tool find-pack -c 1 HEAD:bar.t) &&
+               head_pack=$(test-tool find-pack -c 1 HEAD) &&
+
+               test "$foo_pack" != "$bar_pack" &&
+               test "$foo_pack" = "$head_pack" &&
+
+               git -c repack.writebitmaps=false repack -a -d --filter=blob:none \
+                       --pack-kept-objects &&
+
+               foo_pack_1=$(test-tool find-pack -c 1 HEAD:foo.t) &&
+               test-tool find-pack -c 2 HEAD:bar.t >bar_pack_1 &&
+               head_pack_1=$(test-tool find-pack -c 1 HEAD) &&
+
+               test "$foo_pack_1" != "$foo_pack" &&
+               test "$foo_pack_1" != "$bar_pack" &&
+               test "$head_pack_1" != "$head_pack" &&
+
+               # Object bar is in both the old .keep pack and the new
+               # pack that contained the filtered out objects
+               grep "$bar_pack" bar_pack_1 &&
+               grep "$foo_pack_1" bar_pack_1 &&
+               test "$foo_pack_1" != "$head_pack_1"
+       )
+'
+
+test_expect_success '--filter-to stores filtered out objects' '
+       git -C bare.git repack -a -d &&
+       test_stdout_line_count = 1 ls bare.git/objects/pack/*.pack &&
+
+       git init --bare filtered.git &&
+       git -C bare.git -c repack.writebitmaps=false repack -a -d \
+               --filter=blob:none \
+               --filter-to=../filtered.git/objects/pack/pack &&
+       test_stdout_line_count = 1 ls bare.git/objects/pack/pack-*.pack &&
+       test_stdout_line_count = 1 ls filtered.git/objects/pack/pack-*.pack &&
+
+       commit_pack=$(test-tool -C bare.git find-pack -c 1 HEAD) &&
+       blob_pack=$(test-tool -C bare.git find-pack -c 0 HEAD:file1) &&
+       blob_hash=$(git -C bare.git rev-parse HEAD:file1) &&
+       test -n "$blob_hash" &&
+       blob_pack=$(test-tool -C filtered.git find-pack -c 1 $blob_hash) &&
+
+       echo $(pwd)/filtered.git/objects >bare.git/objects/info/alternates &&
+       blob_pack=$(test-tool -C bare.git find-pack -c 1 HEAD:file1) &&
+       blob_content=$(git -C bare.git show $blob_hash) &&
+       test "$blob_content" = "content1"
+'
+
+test_expect_success '--filter works with --max-pack-size' '
+       rm -rf filtered.git &&
+       git init --bare filtered.git &&
+       git init max-pack-size &&
+       (
+               cd max-pack-size &&
+               test_commit base &&
+               # two blobs which exceed the maximum pack size
+               test-tool genrandom foo 1048576 >foo &&
+               git hash-object -w foo &&
+               test-tool genrandom bar 1048576 >bar &&
+               git hash-object -w bar &&
+               git add foo bar &&
+               git commit -m "adding foo and bar"
+       ) &&
+       git clone --no-local --bare max-pack-size max-pack-size.git &&
+       (
+               cd max-pack-size.git &&
+               git -c repack.writebitmaps=false repack -a -d --filter=blob:none \
+                       --max-pack-size=1M \
+                       --filter-to=../filtered.git/objects/pack/pack &&
+               echo $(cd .. && pwd)/filtered.git/objects >objects/info/alternates &&
+
+               # Check that the 3 blobs are in different packfiles in filtered.git
+               test_stdout_line_count = 3 ls ../filtered.git/objects/pack/pack-*.pack &&
+               test_stdout_line_count = 1 ls objects/pack/pack-*.pack &&
+               foo_pack=$(test-tool find-pack -c 1 HEAD:foo) &&
+               bar_pack=$(test-tool find-pack -c 1 HEAD:bar) &&
+               base_pack=$(test-tool find-pack -c 1 HEAD:base.t) &&
+               test "$foo_pack" != "$bar_pack" &&
+               test "$foo_pack" != "$base_pack" &&
+               test "$bar_pack" != "$base_pack" &&
+               for pack in "$foo_pack" "$bar_pack" "$base_pack"
+               do
+                       case "$foo_pack" in */filtered.git/objects/pack/*) true ;; *) return 1 ;; esac
+               done
+       )
+'
+
 objdir=.git/objects
 midx=$objdir/pack/multi-pack-index
 
@@ -633,125 +830,4 @@ test_expect_success '-n overrides repack.updateServerInfo=true' '
        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
diff --git a/t/t7704-repack-cruft.sh b/t/t7704-repack-cruft.sh
new file mode 100755 (executable)
index 0000000..be3735d
--- /dev/null
@@ -0,0 +1,414 @@
+#!/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
index 7e8894a4a70648fd12d3ab4425f1beac2c3e4641..590aab0304c679b6559df0e840ddce6dacc684af 100755 (executable)
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='check that example code compiles and runs'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'decorate' '
index a203f9a3d7a6ac8233326365fa4d38a06e8e85af..c2b20b80d5a448b7b5c76d2de4ac9358fa596b69 100644 (file)
@@ -2,7 +2,7 @@
 #include "advice.h"
 #include "strvec.h"
 #include "repository.h"
-#include "config.h"
+#include "parse.h"
 #include "dir.h"
 #include "environment.h"
 #include "gettext.h"
diff --git a/url.c b/url.c
index 2e1a9f6feec96b055a9415748eb721937461c07f..282b12495ae7d4af9db51a76017af6065b924d3a 100644 (file)
--- a/url.c
+++ b/url.c
@@ -1,5 +1,5 @@
 #include "git-compat-util.h"
-#include "hex.h"
+#include "hex-ll.h"
 #include "strbuf.h"
 #include "url.h"
 
index 1c45f23adf2c2fe770c147e9bbf0d5a557fcf688..1d0254abacbc3f393062351bc55ccd9bef32051a 100644 (file)
@@ -1,6 +1,6 @@
 #include "git-compat-util.h"
 #include "gettext.h"
-#include "hex.h"
+#include "hex-ll.h"
 #include "strbuf.h"
 #include "urlmatch.h"
 
index 48065c4f53391147fd3dfb57206c9b48688c6b5c..7da15a56da614ce82ca1d6a5c6e2277b444f1063 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -3,9 +3,8 @@
  */
 #include "git-compat-util.h"
 #include "abspath.h"
-#include "config.h"
+#include "parse.h"
 #include "gettext.h"
-#include "object.h"
 #include "repository.h"
 #include "strbuf.h"
 #include "trace2.h"
@@ -632,11 +631,6 @@ int rmdir_or_warn(const char *file)
        return warn_if_unremovable("rmdir", file, rmdir(file));
 }
 
-int remove_or_warn(unsigned int mode, const char *file)
-{
-       return S_ISGITLINK(mode) ? rmdir_or_warn(file) : unlink_or_warn(file);
-}
-
 static int access_error_is_ok(int err, unsigned flag)
 {
        return (is_missing_file_error(err) ||
index 79c7321bb3700d0ea231b4be16fc01e48f3d452d..1b2b047ea069272052b13371c31ca06924231cc9 100644 (file)
--- a/wrapper.h
+++ b/wrapper.h
@@ -106,11 +106,6 @@ int unlink_or_msg(const char *file, struct strbuf *err);
  * not exist.
  */
 int rmdir_or_warn(const char *path);
-/*
- * Calls the correct function out of {unlink,rmdir}_or_warn based on
- * the supplied file mode.
- */
-int remove_or_warn(unsigned int mode, const char *path);
 
 /*
  * Call access(2), but warn for any error except "missing file"
index d8355c0c3e36830f8b9dbd9af696ed3917860c9e..42a2dc73cd3f18638445780e7fa9aa9b67ba871c 100644 (file)
@@ -1,5 +1,5 @@
 #include "git-compat-util.h"
-#include "config.h"
+#include "parse.h"
 #include "run-command.h"
 #include "write-or-die.h"