]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'ps/completion-with-reftable-fix'
authorJunio C Hamano <gitster@pobox.com>
Fri, 26 Jan 2024 16:54:46 +0000 (08:54 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 26 Jan 2024 16:54:46 +0000 (08:54 -0800)
Completion update to prepare for reftable

* ps/completion-with-reftable-fix:
  completion: treat dangling symrefs as existing pseudorefs
  completion: silence pseudoref existence check
  completion: improve existence check for pseudo-refs
  t9902: verify that completion does not print anything
  completion: discover repo path in `__git_pseudoref_exists ()`

77 files changed:
.gitlab-ci.yml
Documentation/RelNotes/2.44.0.txt
Documentation/config/advice.txt
Documentation/config/extensions.txt
Documentation/config/fetch.txt
Documentation/fetch-options.txt
Documentation/git-bisect.txt
Documentation/git-branch.txt
Documentation/git-clone.txt
Documentation/git-init.txt
Documentation/git-rebase.txt
Documentation/git-rev-parse.txt
Documentation/git.txt
Documentation/ref-storage-format.txt [new file with mode: 0644]
Documentation/rev-list-options.txt
Documentation/technical/repository-version.txt
Makefile
advice.c
advice.h
builtin/branch.c
builtin/clone.c
builtin/fetch.c
builtin/index-pack.c
builtin/init-db.c
builtin/rev-parse.c
builtin/send-pack.c
builtin/worktree.c
ci/install-docker-dependencies.sh
commit-graph.c
compat/mingw.c
contrib/completion/git-prompt.sh
git-p4.py
gitweb/gitweb.perl
http-push.c
refs.c
refs.h
refs/debug.c
refs/files-backend.c
refs/packed-backend.c
refs/refs-internal.h
reftable/block_test.c
reftable/merged.c
reftable/merged_test.c
reftable/readwrite_test.c
reftable/record.c
reftable/record_test.c
reftable/reftable-record.h
reftable/stack.c
reftable/stack_test.c
reftable/writer.c
repository.c
repository.h
setup.c
setup.h
sideband.c
strvec.h
t/README
t/helper/test-ctype.c [deleted file]
t/helper/test-tool.c
t/helper/test-tool.h
t/t0001-init.sh
t/t0070-fundamental.sh
t/t1401-symbolic-ref.sh
t/t1500-rev-parse.sh
t/t3200-branch.sh
t/t4129-apply-samemode.sh
t/t5510-fetch.sh
t/t5514-fetch-multiple.sh
t/t5541-http-push-smart.sh
t/t5601-clone.sh
t/t9500-gitweb-standalone-no-errors.sh
t/test-lib-functions.sh
t/test-lib.sh
t/unit-tests/t-ctype.c [new file with mode: 0644]
transport.c
worktree.c
worktree.h

index cd98bcb18aa38aca55f577466f7d44f06c50177f..793243421cd09ee3d16737bb064461456e41b5d8 100644 (file)
@@ -51,3 +51,13 @@ test:
     paths:
       - t/failed-test-artifacts
     when: on_failure
+
+static-analysis:
+  image: ubuntu:22.04
+  variables:
+    jobname: StaticAnalysis
+  before_script:
+    - ./ci/install-docker-dependencies.sh
+  script:
+    - ./ci/run-static-analysis.sh
+    - ./ci/check-directional-formatting.bash
index 020692cda37fc0fc7f09471303ebaa63c9a4d98f..4dda977fc3d361b17e1dc5647eef6faf82a9d7d1 100644 (file)
@@ -38,6 +38,14 @@ UI, Workflows & Features
    useful in magic pathspec, e.g., ":(attr:builtin_objectmode=100755)"
    to limit to executables.
 
+ * "git fetch" learned to pay attention to "fetch.all" configuration
+   variable, which pretends as if "--all" was passed from the command
+   line when no remote parameter was given.
+
+ * In addition to (rather cryptic) Security Identifiers, show username
+   and domain in the error message when we barf on mismatch between
+   the Git directory and the current user on Windows.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -167,6 +175,12 @@ Fixes since v2.43
    which has been corrected.
    (merge 556e68032f cp/git-flush-is-an-env-bool later to maint).
 
+ * Clearing in-core repository (happens during e.g., "git fetch
+   --recurse-submodules" with commit graph enabled) made in-core
+   commit object in an inconsistent state by discarding the necessary
+   data from commit-graph too early, which has been corrected.
+   (merge d70f554cdf jk/commit-graph-slab-clear-fix later to maint).
+
  * Other code cleanup, docfix, build fix, etc.
    (merge 50f1abcff6 js/packfile-h-typofix later to maint).
    (merge cbf498eb53 jb/reflog-expire-delete-dry-run-options later to maint).
@@ -195,3 +209,8 @@ Fixes since v2.43
    (merge 54d8a2531b jk/t1006-cat-file-objectsize-disk later to maint).
    (merge 7033d5479b jx/sideband-chomp-newline-fix later to maint).
    (merge 9cd30af991 ms/rebase-insnformat-doc-fix later to maint).
+   (merge 03bcc93769 cp/sideband-array-index-comment-fix later to maint).
+   (merge 993d38a066 jk/index-pack-lsan-false-positive-fix later to maint).
+   (merge 25aec06326 ib/rebase-reschedule-doc later to maint).
+   (merge 5aea3955bc rj/clarify-branch-doc-m later to maint).
+   (merge 9cce3be2df bk/bisect-doc-fix later to maint).
index 4d7e5d875998fe1c3aa20c0bb5efdcf9fb6fad91..25c091752445a820f9ef9849ab3c37673da42bd7 100644 (file)
@@ -4,27 +4,59 @@ advice.*::
        can tell Git that you do not need help by setting these to 'false':
 +
 --
+       addEmbeddedRepo::
+               Advice on what to do when you've accidentally added one
+               git repo inside of another.
+       addEmptyPathspec::
+               Advice shown if a user runs the add command without providing
+               the pathspec parameter.
+       addIgnoredFile::
+               Advice shown if a user attempts to add an ignored file to
+               the index.
+       amWorkDir::
+               Advice that shows the location of the patch file when
+               linkgit:git-am[1] fails to apply it.
        ambiguousFetchRefspec::
                Advice shown when a fetch refspec for multiple remotes maps to
                the same remote-tracking branch namespace and causes branch
                tracking set-up to fail.
+       checkoutAmbiguousRemoteBranchName::
+               Advice shown when the argument to
+               linkgit:git-checkout[1] and linkgit:git-switch[1]
+               ambiguously resolves to a
+               remote tracking branch on more than one remote in
+               situations where an unambiguous argument would have
+               otherwise caused a remote-tracking branch to be
+               checked out. See the `checkout.defaultRemote`
+               configuration variable for how to set a given remote
+               to be used by default in some situations where this
+               advice would be printed.
+       commitBeforeMerge::
+               Advice shown when linkgit:git-merge[1] refuses to
+               merge to avoid overwriting local changes.
+       detachedHead::
+               Advice shown when you used
+               linkgit:git-switch[1] or linkgit:git-checkout[1]
+               to move to the detached HEAD state, to instruct how to
+               create a local branch after the fact.
+       diverging::
+               Advice shown when a fast-forward is not possible.
        fetchShowForcedUpdates::
                Advice shown when linkgit:git-fetch[1] takes a long time
                to calculate forced updates after ref updates, or to warn
                that the check is disabled.
-       pushUpdateRejected::
-               Set this variable to 'false' if you want to disable
-               'pushNonFFCurrent', 'pushNonFFMatching', 'pushAlreadyExists',
-               'pushFetchFirst', 'pushNeedsForce', and 'pushRefNeedsUpdate'
-               simultaneously.
-       pushNonFFCurrent::
-               Advice shown when linkgit:git-push[1] fails due to a
-               non-fast-forward update to the current branch.
-       pushNonFFMatching::
-               Advice shown when you ran linkgit:git-push[1] and pushed
-               'matching refs' explicitly (i.e. you used ':', or
-               specified a refspec that isn't your current branch) and
-               it resulted in a non-fast-forward error.
+       forceDeleteBranch::
+               Advice shown when a user tries to delete a not fully merged
+               branch without the force option set.
+       ignoredHook::
+               Advice shown if a hook is ignored because the hook is not
+               set as executable.
+       implicitIdentity::
+               Advice on how to set your identity configuration when
+               your information is guessed from the system username and
+               domain name.
+       nestedTag::
+               Advice shown if a user attempts to recursively tag a tag object.
        pushAlreadyExists::
                Shown when linkgit:git-push[1] rejects an update that
                does not qualify for fast-forwarding (e.g., a tag.)
@@ -37,6 +69,18 @@ advice.*::
                tries to overwrite a remote ref that points at an
                object that is not a commit-ish, or make the remote
                ref point at an object that is not a commit-ish.
+       pushNonFFCurrent::
+               Advice shown when linkgit:git-push[1] fails due to a
+               non-fast-forward update to the current branch.
+       pushNonFFMatching::
+               Advice shown when you ran linkgit:git-push[1] and pushed
+               'matching refs' explicitly (i.e. you used ':', or
+               specified a refspec that isn't your current branch) and
+               it resulted in a non-fast-forward error.
+       pushRefNeedsUpdate::
+               Shown when linkgit:git-push[1] rejects a forced update of
+               a branch when its remote-tracking ref has updates that we
+               do not have locally.
        pushUnqualifiedRefname::
                Shown when linkgit:git-push[1] gives up trying to
                guess based on the source and destination refs what
@@ -44,10 +88,23 @@ advice.*::
                we can still suggest that the user push to either
                refs/heads/* or refs/tags/* based on the type of the
                source object.
-       pushRefNeedsUpdate::
-               Shown when linkgit:git-push[1] rejects a forced update of
-               a branch when its remote-tracking ref has updates that we
-               do not have locally.
+       pushUpdateRejected::
+               Set this variable to 'false' if you want to disable
+               'pushNonFFCurrent', 'pushNonFFMatching', 'pushAlreadyExists',
+               'pushFetchFirst', 'pushNeedsForce', and 'pushRefNeedsUpdate'
+               simultaneously.
+       resetNoRefresh::
+               Advice to consider using the `--no-refresh` option to
+               linkgit:git-reset[1] when the command takes more than 2 seconds
+               to refresh the index after reset.
+       resolveConflict::
+               Advice shown by various commands when conflicts
+               prevent the operation from being performed.
+       rmHints::
+               In case of failure in the output of linkgit:git-rm[1],
+               show directions on how to proceed from the current state.
+       sequencerInUse::
+               Advice shown when a sequencer command is already in progress.
        skippedCherryPicks::
                Shown when linkgit:git-rebase[1] skips a commit that has already
                been cherry-picked onto the upstream branch.
@@ -68,76 +125,22 @@ advice.*::
                Advise to consider using the `-u` option to linkgit:git-status[1]
                when the command takes more than 2 seconds to enumerate untracked
                files.
-       commitBeforeMerge::
-               Advice shown when linkgit:git-merge[1] refuses to
-               merge to avoid overwriting local changes.
-       resetNoRefresh::
-               Advice to consider using the `--no-refresh` option to
-               linkgit:git-reset[1] when the command takes more than 2 seconds
-               to refresh the index after reset.
-       resolveConflict::
-               Advice shown by various commands when conflicts
-               prevent the operation from being performed.
-       sequencerInUse::
-               Advice shown when a sequencer command is already in progress.
-       implicitIdentity::
-               Advice on how to set your identity configuration when
-               your information is guessed from the system username and
-               domain name.
-       detachedHead::
-               Advice shown when you used
-               linkgit:git-switch[1] or linkgit:git-checkout[1]
-               to move to the detached HEAD state, to instruct how to
-               create a local branch after the fact.
-       suggestDetachingHead::
-               Advice shown when linkgit:git-switch[1] refuses to detach HEAD
-               without the explicit `--detach` option.
-       checkoutAmbiguousRemoteBranchName::
-               Advice shown when the argument to
-               linkgit:git-checkout[1] and linkgit:git-switch[1]
-               ambiguously resolves to a
-               remote tracking branch on more than one remote in
-               situations where an unambiguous argument would have
-               otherwise caused a remote-tracking branch to be
-               checked out. See the `checkout.defaultRemote`
-               configuration variable for how to set a given remote
-               to be used by default in some situations where this
-               advice would be printed.
-       amWorkDir::
-               Advice that shows the location of the patch file when
-               linkgit:git-am[1] fails to apply it.
-       rmHints::
-               In case of failure in the output of linkgit:git-rm[1],
-               show directions on how to proceed from the current state.
-       addEmbeddedRepo::
-               Advice on what to do when you've accidentally added one
-               git repo inside of another.
-       ignoredHook::
-               Advice shown if a hook is ignored because the hook is not
-               set as executable.
-       waitingForEditor::
-               Print a message to the terminal whenever Git is waiting for
-               editor input from the user.
-       nestedTag::
-               Advice shown if a user attempts to recursively tag a tag object.
        submoduleAlternateErrorStrategyDie::
                Advice shown when a submodule.alternateErrorStrategy option
                configured to "die" causes a fatal error.
        submodulesNotUpdated::
                Advice shown when a user runs a submodule command that fails
                because `git submodule update --init` was not run.
-       addIgnoredFile::
-               Advice shown if a user attempts to add an ignored file to
-               the index.
-       addEmptyPathspec::
-               Advice shown if a user runs the add command without providing
-               the pathspec parameter.
+       suggestDetachingHead::
+               Advice shown when linkgit:git-switch[1] refuses to detach HEAD
+               without the explicit `--detach` option.
        updateSparsePath::
                Advice shown when either linkgit:git-add[1] or linkgit:git-rm[1]
                is asked to update index entries outside the current sparse
                checkout.
-       diverging::
-               Advice shown when a fast-forward is not possible.
+       waitingForEditor::
+               Print a message to the terminal whenever Git is waiting for
+               editor input from the user.
        worktreeAddOrphan::
                Advice shown when a user tries to create a worktree from an
                invalid reference, to instruct how to create a new unborn
index bccaec7a963679f8262d7c8d056fa4429b9586d1..66db0e15da7db82819de941626b7abc5983862c5 100644 (file)
@@ -7,6 +7,17 @@ Note that this setting should only be set by linkgit:git-init[1] or
 linkgit:git-clone[1].  Trying to change it after initialization will not
 work and will produce hard-to-diagnose issues.
 
+extensions.refStorage::
+       Specify the ref storage format to use. The acceptable values are:
++
+include::../ref-storage-format.txt[]
++
+It is an error to specify this key unless `core.repositoryFormatVersion` is 1.
++
+Note that this setting should only be set by linkgit:git-init[1] or
+linkgit:git-clone[1]. Trying to change it after initialization will not
+work and will produce hard-to-diagnose issues.
+
 extensions.worktreeConfig::
        If enabled, then worktrees will load config settings from the
        `$GIT_DIR/config.worktree` file in addition to the
index aea5b97477b64ef664b6b35faad78b2eb618db9a..d7dc461bd16ad782933a40e060c523b063cb1aca 100644 (file)
@@ -50,6 +50,12 @@ fetch.pruneTags::
        refs. See also `remote.<name>.pruneTags` and the PRUNING
        section of linkgit:git-fetch[1].
 
+fetch.all::
+       If true, fetch will attempt to update all available remotes.
+       This behavior can be overridden by passing `--no-all` or by
+       explicitly specifying one or more remote(s) to fetch from.
+       Defaults to false.
+
 fetch.output::
        Control how ref update status is printed. Valid values are
        `full` and `compact`. Default value is `full`. See the
index a1d6633a4f15b719a33615ca0ce2805ec2fa0b3e..54ebb4452e997f8c62c9dd2421948086249102bd 100644 (file)
@@ -1,5 +1,6 @@
---all::
-       Fetch all remotes.
+--[no-]all::
+       Fetch all remotes. This overrides the configuration variable
+       `fetch.all`.
 
 -a::
 --append::
index aa02e462244ee0ad2836412cb2351fc43e9c43ad..8e01f1d6189d523fc4c5a196c64349117ea389fe 100644 (file)
@@ -17,7 +17,7 @@ The command takes various subcommands, and different options depending
 on the subcommand:
 
  git bisect start [--term-(new|bad)=<term-new> --term-(old|good)=<term-old>]
-                 [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]
+                 [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]
  git bisect (bad|new|<term-new>) [<rev>]
  git bisect (good|old|<term-old>) [<rev>...]
  git bisect terms [--term-good | --term-bad]
@@ -299,7 +299,7 @@ Cutting down bisection by giving more parameters to bisect start
 
 You can further cut down the number of trials, if you know what part of
 the tree is involved in the problem you are tracking down, by specifying
-path parameters when issuing the `bisect start` command:
+pathspec parameters when issuing the `bisect start` command:
 
 ------------
 $ git bisect start -- arch/i386 include/asm-i386
index 4395aa935438aaac47cfe41f5db7a1eadafe8a25..0b08442932354fcdcbb321f0c45b80a3a5bc4fdf 100644 (file)
@@ -312,7 +312,8 @@ superproject's "origin/main", but tracks the submodule's "origin/main".
        option is omitted, the current HEAD will be used instead.
 
 <oldbranch>::
-       The name of an existing branch to rename.
+       The name of an existing branch.  If this option is omitted,
+       the name of the current branch will be used instead.
 
 <newbranch>::
        The new name for an existing branch. The same restrictions as for
index c37c4a37f7412c0b712d14067c2d7c7d975933c5..6e43eb9c205371548655c5abae5e59bb963c959a 100644 (file)
@@ -311,6 +311,12 @@ or `--mirror` is given)
        The result is Git repository can be separated from working
        tree.
 
+--ref-format=<ref-format::
+
+Specify the given ref storage format for the repository. The valid values are:
++
+include::ref-storage-format.txt[]
+
 -j <n>::
 --jobs <n>::
        The number of submodules fetched at the same time.
index 6f0d2973bf444a510ae4989dee9aa1e360eee247..e8dc645bb59a8664865c9eed00698ee4374ea4a3 100644 (file)
@@ -11,6 +11,7 @@ SYNOPSIS
 [verse]
 'git init' [-q | --quiet] [--bare] [--template=<template-directory>]
          [--separate-git-dir <git-dir>] [--object-format=<format>]
+         [--ref-format=<format>]
          [-b <branch-name> | --initial-branch=<branch-name>]
          [--shared[=<permissions>]] [<directory>]
 
@@ -57,6 +58,12 @@ values are 'sha1' and (if enabled) 'sha256'.  'sha1' is the default.
 +
 include::object-format-disclaimer.txt[]
 
+--ref-format=<format>::
+
+Specify the given ref storage format for the repository. The valid values are:
++
+include::ref-storage-format.txt[]
+
 --template=<template-directory>::
 
 Specify the directory from which templates will be used.  (See the "TEMPLATE
index 25516c45d8b8959f6f9d9687b9a5642509f31c17..8a8d32161b4455feead4bd43e251e43041a24dd0 100644 (file)
@@ -626,13 +626,16 @@ See also INCOMPATIBLE OPTIONS below.
        Automatically reschedule `exec` commands that failed. This only makes
        sense in interactive mode (or when an `--exec` option was provided).
 +
-Even though this option applies once a rebase is started, it's set for
-the whole rebase at the start based on either the
-`rebase.rescheduleFailedExec` configuration (see linkgit:git-config[1]
-or "CONFIGURATION" below) or whether this option is
-provided. Otherwise an explicit `--no-reschedule-failed-exec` at the
-start would be overridden by the presence of
-`rebase.rescheduleFailedExec=true` configuration.
+This option applies once a rebase is started. It is preserved for the whole
+rebase based on, in order, the command line option provided to the initial `git
+rebase`, the `rebase.rescheduleFailedExec` configuration (see
+linkgit:git-config[1] or "CONFIGURATION" below), or it defaults to false.
++
+Recording this option for the whole rebase is a convenience feature. Otherwise
+an explicit `--no-reschedule-failed-exec` at the start would be overridden by
+the presence of a `rebase.rescheduleFailedExec=true` configuration when `git
+rebase --continue` is invoked. Currently, you cannot pass
+`--[no-]reschedule-failed-exec` to `git rebase --continue`.
 
 --update-refs::
 --no-update-refs::
index 912fab9f5e00b6e286b1d80a8164d76224408682..546faf9017723581ea8e13bea6f5f90b00372403 100644 (file)
@@ -307,6 +307,9 @@ The following options are unaffected by `--path-format`:
        input, multiple algorithms may be printed, space-separated.
        If not specified, the default is "storage".
 
+--show-ref-format::
+       Show the reference storage format used for the repository.
+
 
 Other Options
 ~~~~~~~~~~~~~
index 962887f1907d1c18ce74bf36adb2d31abd76a940..da4e8d1303a07c85964acfbd564ed4c521140cbb 100644 (file)
@@ -556,6 +556,11 @@ double-quotes and respecting backslash escapes. E.g., the value
        is always used. The default is "sha1".
        See `--object-format` in linkgit:git-init[1].
 
+`GIT_DEFAULT_REF_FORMAT`::
+       If this variable is set, the default reference backend format for new
+       repositories will be set to this value. The default is "files".
+       See `--ref-format` in linkgit:git-init[1].
+
 Git Commits
 ~~~~~~~~~~~
 `GIT_AUTHOR_NAME`::
diff --git a/Documentation/ref-storage-format.txt b/Documentation/ref-storage-format.txt
new file mode 100644 (file)
index 0000000..1a65cac
--- /dev/null
@@ -0,0 +1 @@
+* `files` for loose files with packed-refs. This is the default.
index 2bf239ff0309aa0e45436c06788f49c094bfac8f..a583b52c612aece1f7d1fd24086c0f7961eecceb 100644 (file)
@@ -947,10 +947,10 @@ ifdef::git-rev-list[]
 +
 The form '--filter=blob:none' omits all blobs.
 +
-The form '--filter=blob:limit=<n>[kmg]' omits blobs larger than n bytes
-or units.  n may be zero.  The suffixes k, m, and g can be used to name
-units in KiB, MiB, or GiB.  For example, 'blob:limit=1k' is the same
-as 'blob:limit=1024'.
+The form '--filter=blob:limit=<n>[kmg]' omits blobs of size at least n
+bytes or units.  n may be zero.  The suffixes k, m, and g can be used
+to name units in KiB, MiB, or GiB.  For example, 'blob:limit=1k'
+is the same as 'blob:limit=1024'.
 +
 The form '--filter=object:type=(tag|commit|tree|blob)' omits all objects
 which are not of the requested type.
index 045a76756fcf47401dc61cb55f37df1deff480d7..27be3741e6040ed9f9915f504793b4ad978d40ad 100644 (file)
@@ -100,3 +100,8 @@ If set, by default "git config" reads from both "config" and
 multiple working directory mode, "config" file is shared while
 "config.worktree" is per-working directory (i.e., it's in
 GIT_COMMON_DIR/worktrees/<id>/config.worktree)
+
+==== `refStorage`
+
+Specifies the file format for the ref database. The only valid value
+is `files` (loose references with a packed-refs file).
index 15990ff3122eb61676e4bc39477804e77d04367b..1a62e4875947f89b55902c0f49841d3dc347ada7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -792,7 +792,6 @@ TEST_BUILTINS_OBJS += test-chmtime.o
 TEST_BUILTINS_OBJS += test-config.o
 TEST_BUILTINS_OBJS += test-crontab.o
 TEST_BUILTINS_OBJS += test-csprng.o
-TEST_BUILTINS_OBJS += test-ctype.o
 TEST_BUILTINS_OBJS += test-date.o
 TEST_BUILTINS_OBJS += test-delta.o
 TEST_BUILTINS_OBJS += test-dir-iterator.o
@@ -1342,6 +1341,7 @@ THIRD_PARTY_SOURCES += sha1dc/%
 UNIT_TEST_PROGRAMS += t-basic
 UNIT_TEST_PROGRAMS += t-mem-pool
 UNIT_TEST_PROGRAMS += t-strbuf
+UNIT_TEST_PROGRAMS += t-ctype
 UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS))
 UNIT_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(UNIT_TEST_PROGRAMS))
 UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o
index 50c79443ba749fc56437806c21a20755c1988714..f6e4c2f302e9e8686ae515446b7bbd9a5827081f 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -40,14 +40,14 @@ static struct {
        [ADVICE_ADD_EMBEDDED_REPO]                      = { "addEmbeddedRepo", 1 },
        [ADVICE_ADD_EMPTY_PATHSPEC]                     = { "addEmptyPathspec", 1 },
        [ADVICE_ADD_IGNORED_FILE]                       = { "addIgnoredFile", 1 },
-       [ADVICE_AM_WORK_DIR]                            = { "amWorkDir", 1 },
        [ADVICE_AMBIGUOUS_FETCH_REFSPEC]                = { "ambiguousFetchRefspec", 1 },
+       [ADVICE_AM_WORK_DIR]                            = { "amWorkDir", 1 },
        [ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME]  = { "checkoutAmbiguousRemoteBranchName", 1 },
        [ADVICE_COMMIT_BEFORE_MERGE]                    = { "commitBeforeMerge", 1 },
        [ADVICE_DETACHED_HEAD]                          = { "detachedHead", 1 },
-       [ADVICE_SUGGEST_DETACHING_HEAD]                 = { "suggestDetachingHead", 1 },
        [ADVICE_DIVERGING]                              = { "diverging", 1 },
        [ADVICE_FETCH_SHOW_FORCED_UPDATES]              = { "fetchShowForcedUpdates", 1 },
+       [ADVICE_FORCE_DELETE_BRANCH]                    = { "forceDeleteBranch", 1 },
        [ADVICE_GRAFT_FILE_DEPRECATED]                  = { "graftFileDeprecated", 1 },
        [ADVICE_IGNORED_HOOK]                           = { "ignoredHook", 1 },
        [ADVICE_IMPLICIT_IDENTITY]                      = { "implicitIdentity", 1 },
@@ -56,15 +56,12 @@ static struct {
        [ADVICE_PUSH_ALREADY_EXISTS]                    = { "pushAlreadyExists", 1 },
        [ADVICE_PUSH_FETCH_FIRST]                       = { "pushFetchFirst", 1 },
        [ADVICE_PUSH_NEEDS_FORCE]                       = { "pushNeedsForce", 1 },
-       [ADVICE_PUSH_REF_NEEDS_UPDATE]                  = { "pushRefNeedsUpdate", 1 },
-
-       /* make this an alias for backward compatibility */
-       [ADVICE_PUSH_UPDATE_REJECTED_ALIAS]             = { "pushNonFastForward", 1 },
-
        [ADVICE_PUSH_NON_FF_CURRENT]                    = { "pushNonFFCurrent", 1 },
        [ADVICE_PUSH_NON_FF_MATCHING]                   = { "pushNonFFMatching", 1 },
+       [ADVICE_PUSH_REF_NEEDS_UPDATE]                  = { "pushRefNeedsUpdate", 1 },
        [ADVICE_PUSH_UNQUALIFIED_REF_NAME]              = { "pushUnqualifiedRefName", 1 },
        [ADVICE_PUSH_UPDATE_REJECTED]                   = { "pushUpdateRejected", 1 },
+       [ADVICE_PUSH_UPDATE_REJECTED_ALIAS]             = { "pushNonFastForward", 1 }, /* backwards compatibility */
        [ADVICE_RESET_NO_REFRESH_WARNING]               = { "resetNoRefresh", 1 },
        [ADVICE_RESOLVE_CONFLICT]                       = { "resolveConflict", 1 },
        [ADVICE_RM_HINTS]                               = { "rmHints", 1 },
@@ -74,8 +71,9 @@ static struct {
        [ADVICE_STATUS_AHEAD_BEHIND_WARNING]            = { "statusAheadBehindWarning", 1 },
        [ADVICE_STATUS_HINTS]                           = { "statusHints", 1 },
        [ADVICE_STATUS_U_OPTION]                        = { "statusUoption", 1 },
-       [ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE] = { "submoduleAlternateErrorStrategyDie", 1 },
        [ADVICE_SUBMODULES_NOT_UPDATED]                 = { "submodulesNotUpdated", 1 },
+       [ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE] = { "submoduleAlternateErrorStrategyDie", 1 },
+       [ADVICE_SUGGEST_DETACHING_HEAD]                 = { "suggestDetachingHead", 1 },
        [ADVICE_UPDATE_SPARSE_PATH]                     = { "updateSparsePath", 1 },
        [ADVICE_WAITING_FOR_EDITOR]                     = { "waitingForEditor", 1 },
        [ADVICE_WORKTREE_ADD_ORPHAN]                    = { "worktreeAddOrphan", 1 },
index 2affbe142616de0d329c9aaaaee9ca355fb73adf..9d4f49ae38bcfe3b0bdf9999cf9d66ee6a95739e 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -10,18 +10,18 @@ struct string_list;
  * Add the new config variable to Documentation/config/advice.txt.
  * Call advise_if_enabled to print your advice.
  */
- enum advice_type {
+enum advice_type {
        ADVICE_ADD_EMBEDDED_REPO,
        ADVICE_ADD_EMPTY_PATHSPEC,
        ADVICE_ADD_IGNORED_FILE,
-       ADVICE_AM_WORK_DIR,
        ADVICE_AMBIGUOUS_FETCH_REFSPEC,
+       ADVICE_AM_WORK_DIR,
        ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME,
        ADVICE_COMMIT_BEFORE_MERGE,
        ADVICE_DETACHED_HEAD,
        ADVICE_DIVERGING,
-       ADVICE_SUGGEST_DETACHING_HEAD,
        ADVICE_FETCH_SHOW_FORCED_UPDATES,
+       ADVICE_FORCE_DELETE_BRANCH,
        ADVICE_GRAFT_FILE_DEPRECATED,
        ADVICE_IGNORED_HOOK,
        ADVICE_IMPLICIT_IDENTITY,
@@ -32,23 +32,24 @@ struct string_list;
        ADVICE_PUSH_NEEDS_FORCE,
        ADVICE_PUSH_NON_FF_CURRENT,
        ADVICE_PUSH_NON_FF_MATCHING,
+       ADVICE_PUSH_REF_NEEDS_UPDATE,
        ADVICE_PUSH_UNQUALIFIED_REF_NAME,
-       ADVICE_PUSH_UPDATE_REJECTED_ALIAS,
        ADVICE_PUSH_UPDATE_REJECTED,
-       ADVICE_PUSH_REF_NEEDS_UPDATE,
+       ADVICE_PUSH_UPDATE_REJECTED_ALIAS,
        ADVICE_RESET_NO_REFRESH_WARNING,
        ADVICE_RESOLVE_CONFLICT,
        ADVICE_RM_HINTS,
        ADVICE_SEQUENCER_IN_USE,
        ADVICE_SET_UPSTREAM_FAILURE,
+       ADVICE_SKIPPED_CHERRY_PICKS,
        ADVICE_STATUS_AHEAD_BEHIND_WARNING,
        ADVICE_STATUS_HINTS,
        ADVICE_STATUS_U_OPTION,
-       ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE,
        ADVICE_SUBMODULES_NOT_UPDATED,
+       ADVICE_SUBMODULE_ALTERNATE_ERROR_STRATEGY_DIE,
+       ADVICE_SUGGEST_DETACHING_HEAD,
        ADVICE_UPDATE_SPARSE_PATH,
        ADVICE_WAITING_FOR_EDITOR,
-       ADVICE_SKIPPED_CHERRY_PICKS,
        ADVICE_WORKTREE_ADD_ORPHAN,
 };
 
index 0a32d1b6c851d4cf07bb02be9376d61a8d38e5d3..cfb63cce5fb9dff64106907947d0df25a2c25489 100644 (file)
@@ -24,6 +24,7 @@
 #include "ref-filter.h"
 #include "worktree.h"
 #include "help.h"
+#include "advice.h"
 #include "commit-reach.h"
 
 static const char * const builtin_branch_usage[] = {
@@ -190,9 +191,10 @@ static int check_branch_commit(const char *branchname, const char *refname,
                return -1;
        }
        if (!force && !branch_merged(kinds, branchname, rev, head_rev)) {
-               error(_("the branch '%s' is not fully merged.\n"
-                     "If you are sure you want to delete it, "
-                     "run 'git branch -D %s'"), branchname, branchname);
+               error(_("the branch '%s' is not fully merged"), branchname);
+               advise_if_enabled(ADVICE_FORCE_DELETE_BRANCH,
+                                 _("If you are sure you want to delete it, "
+                                 "run 'git branch -D %s'"), branchname);
                return -1;
        }
        return 0;
index 0605fa79aa2fd53433f633b8b8e592098766a43f..bad1b70ce8255156cf4745aca5e120371d39642d 100644 (file)
@@ -71,6 +71,7 @@ static char *remote_name = NULL;
 static char *option_branch = NULL;
 static struct string_list option_not = STRING_LIST_INIT_NODUP;
 static const char *real_git_dir;
+static const char *ref_format;
 static char *option_upload_pack = "git-upload-pack";
 static int option_verbosity;
 static int option_progress = -1;
@@ -156,6 +157,8 @@ static struct option builtin_clone_options[] = {
                    N_("any cloned submodules will be shallow")),
        OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
                   N_("separate git dir from working tree")),
+       OPT_STRING(0, "ref-format", &ref_format, N_("format"),
+                  N_("specify the reference format to use")),
        OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
                        N_("set config inside the new repository")),
        OPT_STRING_LIST(0, "server-option", &server_options,
@@ -931,6 +934,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        int submodule_progress;
        int filter_submodules = 0;
        int hash_algo;
+       unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
        const int do_not_override_repo_unix_permissions = -1;
 
        struct transport_ls_refs_options transport_ls_refs_options =
@@ -956,6 +960,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (option_single_branch == -1)
                option_single_branch = deepen ? 1 : 0;
 
+       if (ref_format) {
+               ref_storage_format = ref_storage_format_by_name(ref_format);
+               if (ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
+                       die(_("unknown ref storage format '%s'"), ref_format);
+       }
+
        if (option_mirror)
                option_bare = 1;
 
@@ -1106,7 +1116,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
         * repository, and reference backends may persist that information into
         * their on-disk data structures.
         */
-       init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, NULL,
+       init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN,
+               ref_storage_format, NULL,
                do_not_override_repo_unix_permissions, INIT_DB_QUIET | INIT_DB_SKIP_REFDB);
 
        if (real_git_dir) {
@@ -1289,9 +1300,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
         * ours to the same thing.
         */
        hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
-       initialize_repository_version(hash_algo, 1);
+       initialize_repository_version(hash_algo, the_repository->ref_storage_format, 1);
        repo_set_hash_algo(the_repository, hash_algo);
-       create_reference_database(NULL, 1);
+       create_reference_database(the_repository->ref_storage_format, NULL, 1);
 
        /*
         * Before fetching from the remote, download and install bundle
index 119f1a72ac6605b65937af3b7cbc651b4c695c0d..3aedfd1bb6361c6bbfd651970f9e9767d0595734 100644 (file)
@@ -100,6 +100,7 @@ static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP;
 
 struct fetch_config {
        enum display_format display_format;
+       int all;
        int prune;
        int prune_tags;
        int show_forced_updates;
@@ -113,6 +114,11 @@ static int git_fetch_config(const char *k, const char *v,
 {
        struct fetch_config *fetch_config = cb;
 
+       if (!strcmp(k, "fetch.all")) {
+               fetch_config->all = git_config_bool(k, v);
+               return 0;
+       }
+
        if (!strcmp(k, "fetch.prune")) {
                fetch_config->prune = git_config_bool(k, v);
                return 0;
@@ -2130,7 +2136,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        const char *bundle_uri;
        struct string_list list = STRING_LIST_INIT_DUP;
        struct remote *remote = NULL;
-       int all = 0, multiple = 0;
+       int all = -1, multiple = 0;
        int result = 0;
        int prune_tags_ok = 1;
        int enable_auto_gc = 1;
@@ -2335,11 +2341,20 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
            fetch_bundle_uri(the_repository, bundle_uri, NULL))
                warning(_("failed to fetch bundles from '%s'"), bundle_uri);
 
+       if (all < 0) {
+               /*
+                * no --[no-]all given;
+                * only use config option if no remote was explicitly specified
+                */
+               all = (!argc) ? config.all : 0;
+       }
+
        if (all) {
                if (argc == 1)
                        die(_("fetch --all does not take a repository argument"));
                else if (argc > 1)
                        die(_("fetch --all does not make sense with refspecs"));
+
                (void) for_each_remote(get_one_remote_for_fetch, &list);
 
                /* do not do fetch_multiple() of one */
index 0841b6940a3ddf5c71beba29d092881b3fd8b044..1ea87e01f2905ed70e3a17a847278ec2b7b68ec3 100644 (file)
@@ -1255,6 +1255,7 @@ static void resolve_deltas(void)
        base_cache_limit = delta_base_cache_limit * nr_threads;
        if (nr_threads > 1 || getenv("GIT_FORCE_THREADS")) {
                init_thread();
+               work_lock();
                for (i = 0; i < nr_threads; i++) {
                        int ret = pthread_create(&thread_data[i].thread, NULL,
                                                 threaded_second_pass, thread_data + i);
@@ -1262,6 +1263,7 @@ static void resolve_deltas(void)
                                die(_("unable to create thread: %s"),
                                    strerror(ret));
                }
+               work_unlock();
                for (i = 0; i < nr_threads; i++)
                        pthread_join(thread_data[i].thread, NULL);
                cleanup_thread();
index b89814a6f87da30fc1721ecef02637f61600cd32..0170469b849e5e5ad52cf8586f4540088b0edd61 100644 (file)
@@ -10,6 +10,8 @@
 #include "object-file.h"
 #include "parse-options.h"
 #include "path.h"
+#include "refs.h"
+#include "repository.h"
 #include "setup.h"
 #include "strbuf.h"
 
@@ -56,6 +58,7 @@ static int shared_callback(const struct option *opt, const char *arg, int unset)
 static const char *const init_db_usage[] = {
        N_("git init [-q | --quiet] [--bare] [--template=<template-directory>]\n"
           "         [--separate-git-dir <git-dir>] [--object-format=<format>]\n"
+          "         [--ref-format=<format>]\n"
           "         [-b <branch-name> | --initial-branch=<branch-name>]\n"
           "         [--shared[=<permissions>]] [<directory>]"),
        NULL
@@ -75,8 +78,10 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
        const char *template_dir = NULL;
        unsigned int flags = 0;
        const char *object_format = NULL;
+       const char *ref_format = NULL;
        const char *initial_branch = NULL;
        int hash_algo = GIT_HASH_UNKNOWN;
+       unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
        int init_shared_repository = -1;
        const struct option init_db_options[] = {
                OPT_STRING(0, "template", &template_dir, N_("template-directory"),
@@ -94,6 +99,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                           N_("override the name of the initial branch")),
                OPT_STRING(0, "object-format", &object_format, N_("hash"),
                           N_("specify the hash algorithm to use")),
+               OPT_STRING(0, "ref-format", &ref_format, N_("format"),
+                          N_("specify the reference format to use")),
                OPT_END()
        };
 
@@ -157,6 +164,12 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                        die(_("unknown hash algorithm '%s'"), object_format);
        }
 
+       if (ref_format) {
+               ref_storage_format = ref_storage_format_by_name(ref_format);
+               if (ref_storage_format == REF_STORAGE_FORMAT_UNKNOWN)
+                       die(_("unknown ref storage format '%s'"), ref_format);
+       }
+
        if (init_shared_repository != -1)
                set_shared_repository(init_shared_repository);
 
@@ -235,5 +248,6 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
 
        flags |= INIT_DB_EXIST_OK;
        return init_db(git_dir, real_git_dir, template_dir, hash_algo,
-                      initial_branch, init_shared_repository, flags);
+                      ref_storage_format, initial_branch,
+                      init_shared_repository, flags);
 }
index 917f12244059a67b74b43c80ec59b8503b61700f..d08987646a0a533ad81480ae4667b577a335f9f7 100644 (file)
@@ -1062,6 +1062,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                puts(the_hash_algo->name);
                                continue;
                        }
+                       if (!strcmp(arg, "--show-ref-format")) {
+                               puts(ref_storage_format_to_name(the_repository->ref_storage_format));
+                               continue;
+                       }
                        if (!strcmp(arg, "--end-of-options")) {
                                seen_end_of_options = 1;
                                if (filter & (DO_FLAGS | DO_REVS))
index b7183be9709fd40a54162685825166945935e522..3df9eaad092babdbbfc47f1c5441fc43a4ccd182 100644 (file)
@@ -333,6 +333,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
        }
 
        if (!ret && !transport_refs_pushed(remote_refs))
+               /* stable plumbing output; do not modify or localize */
                fprintf(stderr, "Everything up-to-date\n");
 
        return ret;
index cac83a94197dd60054d5266de8cc1b5186ea2b35..6d7da11746a25ade659a208db13fe8c0bcd4e36c 100644 (file)
@@ -416,7 +416,6 @@ static int add_worktree(const char *path, const char *refname,
        struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
        struct strbuf sb = STRBUF_INIT, realpath = STRBUF_INIT;
        const char *name;
-       struct child_process cp = CHILD_PROCESS_INIT;
        struct strvec child_env = STRVEC_INIT;
        unsigned int counter = 0;
        int len, ret;
@@ -424,7 +423,8 @@ static int add_worktree(const char *path, const char *refname,
        struct commit *commit = NULL;
        int is_branch = 0;
        struct strbuf sb_name = STRBUF_INIT;
-       struct worktree **worktrees;
+       struct worktree **worktrees, *wt = NULL;
+       struct ref_store *wt_refs;
 
        worktrees = get_worktrees();
        check_candidate_path(path, opts->force, worktrees, "add");
@@ -495,20 +495,32 @@ static int add_worktree(const char *path, const char *refname,
        strbuf_realpath(&realpath, get_git_common_dir(), 1);
        write_file(sb_git.buf, "gitdir: %s/worktrees/%s",
                   realpath.buf, name);
-       /*
-        * This is to keep resolve_ref() happy. We need a valid HEAD
-        * or is_git_directory() will reject the directory. Any value which
-        * looks like an object ID will do since it will be immediately
-        * replaced by the symbolic-ref or update-ref invocation in the new
-        * worktree.
-        */
-       strbuf_reset(&sb);
-       strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
-       write_file(sb.buf, "%s", oid_to_hex(null_oid()));
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
        write_file(sb.buf, "../..");
 
+       /*
+        * Set up the ref store of the worktree and create the HEAD reference.
+        */
+       wt = get_linked_worktree(name, 1);
+       if (!wt) {
+               ret = error(_("could not find created worktree '%s'"), name);
+               goto done;
+       }
+       wt_refs = get_worktree_ref_store(wt);
+
+       ret = refs_init_db(wt_refs, REFS_INIT_DB_IS_WORKTREE, &sb);
+       if (ret)
+               goto done;
+
+       if (!is_branch && commit)
+               ret = refs_update_ref(wt_refs, NULL, "HEAD", &commit->object.oid,
+                                     NULL, 0, UPDATE_REFS_MSG_ON_ERR);
+       else
+               ret = refs_create_symref(wt_refs, "HEAD", symref.buf, NULL);
+       if (ret)
+               goto done;
+
        /*
         * If the current worktree has sparse-checkout enabled, then copy
         * the sparse-checkout patterns from the current worktree.
@@ -526,22 +538,6 @@ static int add_worktree(const char *path, const char *refname,
 
        strvec_pushf(&child_env, "%s=%s", GIT_DIR_ENVIRONMENT, sb_git.buf);
        strvec_pushf(&child_env, "%s=%s", GIT_WORK_TREE_ENVIRONMENT, path);
-       cp.git_cmd = 1;
-
-       if (!is_branch && commit) {
-               strvec_pushl(&cp.args, "update-ref", "HEAD",
-                            oid_to_hex(&commit->object.oid), NULL);
-       } else {
-               strvec_pushl(&cp.args, "symbolic-ref", "HEAD",
-                            symref.buf, NULL);
-               if (opts->quiet)
-                       strvec_push(&cp.args, "--quiet");
-       }
-
-       strvec_pushv(&cp.env, child_env.v);
-       ret = run_command(&cp);
-       if (ret)
-               goto done;
 
        if (opts->orphan &&
            (ret = make_worktree_orphan(refname, opts, &child_env)))
@@ -587,6 +583,7 @@ done:
        strbuf_release(&sb_git);
        strbuf_release(&sb_name);
        strbuf_release(&realpath);
+       free_worktree(wt);
        return ret;
 }
 
index 48c43f0f90774e65fcc8b388c6a52444fde50506..eb2c9e1eca73d3beb19ad0d20087f50af13fc62a 100755 (executable)
@@ -21,7 +21,7 @@ linux-musl)
                apache2 apache2-http2 apache2-proxy apache2-ssl apache2-webdav apr-util-dbd_sqlite3 \
                bash cvs gnupg perl-cgi perl-dbd-sqlite >/dev/null
        ;;
-linux-*)
+linux-*|StaticAnalysis)
        # Required so that apt doesn't wait for user input on certain packages.
        export DEBIAN_FRONTEND=noninteractive
 
@@ -31,6 +31,11 @@ linux-*)
                perl-modules liberror-perl libauthen-sasl-perl libemail-valid-perl \
                libdbd-sqlite3-perl libio-socket-ssl-perl libnet-smtp-ssl-perl ${CC_PACKAGE:-${CC:-gcc}} \
                apache2 cvs cvsps gnupg libcgi-pm-perl subversion
+
+       if test "$jobname" = StaticAnalysis
+       then
+               apt install -q -y coccinelle
+       fi
        ;;
 pedantic)
        dnf -yq update >/dev/null &&
index bba316913cc9c270e061092000c605014dc97904..45417d7412202e54d21de0ae49941536eb2c82b4 100644 (file)
@@ -806,6 +806,9 @@ struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r)
 
 void close_commit_graph(struct raw_object_store *o)
 {
+       if (!o->commit_graph)
+               return;
+
        clear_commit_graph_data_slab(&commit_graph_data_slab);
        free_commit_graph(o->commit_graph);
        o->commit_graph = NULL;
@@ -2616,19 +2619,16 @@ cleanup:
        oid_array_clear(&ctx->oids);
        clear_topo_level_slab(&topo_levels);
 
-       if (ctx->commit_graph_filenames_after) {
-               for (i = 0; i < ctx->num_commit_graphs_after; i++) {
-                       free(ctx->commit_graph_filenames_after[i]);
-                       free(ctx->commit_graph_hash_after[i]);
-               }
-
-               for (i = 0; i < ctx->num_commit_graphs_before; i++)
-                       free(ctx->commit_graph_filenames_before[i]);
+       for (i = 0; i < ctx->num_commit_graphs_before; i++)
+               free(ctx->commit_graph_filenames_before[i]);
+       free(ctx->commit_graph_filenames_before);
 
-               free(ctx->commit_graph_filenames_after);
-               free(ctx->commit_graph_filenames_before);
-               free(ctx->commit_graph_hash_after);
+       for (i = 0; i < ctx->num_commit_graphs_after; i++) {
+               free(ctx->commit_graph_filenames_after[i]);
+               free(ctx->commit_graph_hash_after[i]);
        }
+       free(ctx->commit_graph_filenames_after);
+       free(ctx->commit_graph_hash_after);
 
        free(ctx);
 
index 42053c1f656bd8b73696b751f30177ce9e544d59..238a84ddbaa1b4c7563f6950eca469949d77fa68 100644 (file)
@@ -2684,6 +2684,30 @@ static PSID get_current_user_sid(void)
        return result;
 }
 
+static BOOL user_sid_to_user_name(PSID sid, LPSTR *str)
+{
+       SID_NAME_USE pe_use;
+       DWORD len_user = 0, len_domain = 0;
+       BOOL translate_sid_to_user;
+
+       /*
+        * returns only FALSE, because the string pointers are NULL
+        */
+       LookupAccountSidA(NULL, sid, NULL, &len_user, NULL, &len_domain,
+                         &pe_use);
+       /*
+        * Alloc needed space of the strings
+        */
+       ALLOC_ARRAY((*str), (size_t)len_domain + (size_t)len_user);
+       translate_sid_to_user = LookupAccountSidA(NULL, sid,
+           (*str) + len_domain, &len_user, *str, &len_domain, &pe_use);
+       if (!translate_sid_to_user)
+               FREE_AND_NULL(*str);
+       else
+               (*str)[len_domain] = '/';
+       return translate_sid_to_user;
+}
+
 static int acls_supported(const char *path)
 {
        size_t offset = offset_1st_component(path);
@@ -2765,27 +2789,47 @@ int is_path_owned_by_current_sid(const char *path, struct strbuf *report)
                        strbuf_addf(report, "'%s' is on a file system that does "
                                    "not record ownership\n", path);
                } else if (report) {
-                       LPSTR str1, str2, to_free1 = NULL, to_free2 = NULL;
+                       LPSTR str1, str2, str3, str4, to_free1 = NULL,
+                           to_free3 = NULL, to_local_free2 = NULL,
+                           to_local_free4 = NULL;
 
-                       if (ConvertSidToStringSidA(sid, &str1))
+                       if (user_sid_to_user_name(sid, &str1))
                                to_free1 = str1;
                        else
                                str1 = "(inconvertible)";
-
-                       if (!current_user_sid)
-                               str2 = "(none)";
-                       else if (!IsValidSid(current_user_sid))
-                               str2 = "(invalid)";
-                       else if (ConvertSidToStringSidA(current_user_sid, &str2))
-                               to_free2 = str2;
+                       if (ConvertSidToStringSidA(sid, &str2))
+                               to_local_free2 = str2;
                        else
                                str2 = "(inconvertible)";
+
+                       if (!current_user_sid) {
+                               str3 = "(none)";
+                               str4 = "(none)";
+                       }
+                       else if (!IsValidSid(current_user_sid)) {
+                               str3 = "(invalid)";
+                               str4 = "(invalid)";
+                       } else {
+                               if (user_sid_to_user_name(current_user_sid,
+                                                         &str3))
+                                       to_free3 = str3;
+                               else
+                                       str3 = "(inconvertible)";
+                               if (ConvertSidToStringSidA(current_user_sid,
+                                                          &str4))
+                                       to_local_free4 = str4;
+                               else
+                                       str4 = "(inconvertible)";
+                       }
                        strbuf_addf(report,
                                    "'%s' is owned by:\n"
-                                   "\t'%s'\nbut the current user is:\n"
-                                   "\t'%s'\n", path, str1, str2);
-                       LocalFree(to_free1);
-                       LocalFree(to_free2);
+                                   "\t%s (%s)\nbut the current user is:\n"
+                                   "\t%s (%s)\n",
+                                   path, str1, str2, str3, str4);
+                       free(to_free1);
+                       LocalFree(to_local_free2);
+                       free(to_free3);
+                       LocalFree(to_local_free4);
                }
        }
 
index 2c030050aea1c67472570a9d86d69d55e31e7714..71f179cba3fbda3bc93de461649cf75bb08c6653 100644 (file)
@@ -408,7 +408,7 @@ __git_ps1 ()
 
        local repo_info rev_parse_exit_code
        repo_info="$(git rev-parse --git-dir --is-inside-git-dir \
-               --is-bare-repository --is-inside-work-tree \
+               --is-bare-repository --is-inside-work-tree --show-ref-format \
                --short HEAD 2>/dev/null)"
        rev_parse_exit_code="$?"
 
@@ -421,6 +421,8 @@ __git_ps1 ()
                short_sha="${repo_info##*$'\n'}"
                repo_info="${repo_info%$'\n'*}"
        fi
+       local ref_format="${repo_info##*$'\n'}"
+       repo_info="${repo_info%$'\n'*}"
        local inside_worktree="${repo_info##*$'\n'}"
        repo_info="${repo_info%$'\n'*}"
        local bare_repo="${repo_info##*$'\n'}"
@@ -479,12 +481,25 @@ __git_ps1 ()
                        b="$(git symbolic-ref HEAD 2>/dev/null)"
                else
                        local head=""
-                       if ! __git_eread "$g/HEAD" head; then
-                               return $exit
-                       fi
-                       # is it a symbolic ref?
-                       b="${head#ref: }"
-                       if [ "$head" = "$b" ]; then
+
+                       case "$ref_format" in
+                       files)
+                               if ! __git_eread "$g/HEAD" head; then
+                                       return $exit
+                               fi
+
+                               if [[ $head == "ref: "* ]]; then
+                                       head="${head#ref: }"
+                               else
+                                       head=""
+                               fi
+                               ;;
+                       *)
+                               head="$(git symbolic-ref HEAD 2>/dev/null)"
+                               ;;
+                       esac
+
+                       if test -z "$head"; then
                                detached=yes
                                b="$(
                                case "${GIT_PS1_DESCRIBE_STYLE-}" in
@@ -502,6 +517,8 @@ __git_ps1 ()
 
                                b="$short_sha..."
                                b="($b)"
+                       else
+                               b="$head"
                        fi
                fi
        fi
index 0eb3bb4c47dcf12a9f1d98e1b76a3681197ac52e..3ea1c405e55ac9e0081f4b5c544dbc97d04fe507 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -4251,7 +4251,8 @@ class P4Sync(Command, P4UserMap):
         if self.tempBranches != []:
             for branch in self.tempBranches:
                 read_pipe(["git", "update-ref", "-d", branch])
-            os.rmdir(os.path.join(os.environ.get("GIT_DIR", ".git"), self.tempBranchLocation))
+            if len(read_pipe(["git", "for-each-ref", self.tempBranchLocation])) > 0:
+                   die("There are unexpected temporary branches")
 
         # Create a symbolic ref p4/HEAD pointing to p4/<branch> to allow
         # a convenient shortcut refname "p4".
index fc6d5dd522bf4f4c9869e4e14ea5a07409155784..ccd14e0e30c14ef1da31d09cf6949e70b75cf5e0 100755 (executable)
@@ -728,9 +728,11 @@ our $per_request_config = 1;
 sub read_config_file {
        my $filename = shift;
        return unless defined $filename;
-       # die if there are errors parsing config file
        if (-e $filename) {
                do $filename;
+               # die if there is a problem accessing the file
+               die $! if $!;
+               # die if there are errors parsing config file
                die $@ if $@;
                return 1;
        }
index b4d0b2a6aa381f4edb1bd890de4a4be1d10ab8fb..12d111374107a7a071ac90d035c5172af2b63c79 100644 (file)
@@ -1851,6 +1851,7 @@ int cmd_main(int argc, const char **argv)
 
                if (oideq(&ref->old_oid, &ref->peer_ref->new_oid)) {
                        if (push_verbosely)
+                               /* stable plumbing output; do not modify or localize */
                                fprintf(stderr, "'%s': up-to-date\n", ref->name);
                        if (helper_status)
                                printf("ok %s up to date\n", ref->name);
@@ -1871,6 +1872,7 @@ int cmd_main(int argc, const char **argv)
                                 * commits at the remote end and likely
                                 * we were not up to date to begin with.
                                 */
+                               /* stable plumbing output; do not modify or localize */
                                error("remote '%s' is not an ancestor of\n"
                                      "local '%s'.\n"
                                      "Maybe you are not up-to-date and "
diff --git a/refs.c b/refs.c
index 2f58a3460af27372454317096c795ebc8d4c79a2..c4a1a8c8c1082fe9a4dc2ab121b59dc08f7d8440 100644 (file)
--- a/refs.c
+++ b/refs.c
 /*
  * List of all available backends
  */
-static struct ref_storage_be *refs_backends = &refs_be_files;
+static const struct ref_storage_be *refs_backends[] = {
+       [REF_STORAGE_FORMAT_FILES] = &refs_be_files,
+};
 
-static struct ref_storage_be *find_ref_storage_backend(const char *name)
+static const struct ref_storage_be *find_ref_storage_backend(unsigned int ref_storage_format)
 {
-       struct ref_storage_be *be;
-       for (be = refs_backends; be; be = be->next)
-               if (!strcmp(be->name, name))
-                       return be;
+       if (ref_storage_format < ARRAY_SIZE(refs_backends))
+               return refs_backends[ref_storage_format];
        return NULL;
 }
 
+unsigned int ref_storage_format_by_name(const char *name)
+{
+       for (unsigned int i = 0; i < ARRAY_SIZE(refs_backends); i++)
+               if (refs_backends[i] && !strcmp(refs_backends[i]->name, name))
+                       return i;
+       return REF_STORAGE_FORMAT_UNKNOWN;
+}
+
+const char *ref_storage_format_to_name(unsigned int ref_storage_format)
+{
+       const struct ref_storage_be *be = find_ref_storage_backend(ref_storage_format);
+       if (!be)
+               return "unknown";
+       return be->name;
+}
+
 /*
  * How to handle various characters in refnames:
  * 0: An acceptable character for refs
@@ -1981,11 +1997,9 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
 }
 
 /* backend functions */
-int refs_init_db(struct strbuf *err)
+int refs_init_db(struct ref_store *refs, int flags, struct strbuf *err)
 {
-       struct ref_store *refs = get_main_ref_store(the_repository);
-
-       return refs->be->init_db(refs, err);
+       return refs->be->init_db(refs, flags, err);
 }
 
 const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
@@ -2082,12 +2096,12 @@ static struct ref_store *ref_store_init(struct repository *repo,
                                        const char *gitdir,
                                        unsigned int flags)
 {
-       const char *be_name = "files";
-       struct ref_storage_be *be = find_ref_storage_backend(be_name);
+       const struct ref_storage_be *be;
        struct ref_store *refs;
 
+       be = find_ref_storage_backend(repo->ref_storage_format);
        if (!be)
-               BUG("reference backend %s is unknown", be_name);
+               BUG("reference backend is unknown");
 
        refs = be->init(repo, gitdir, flags);
        return refs;
diff --git a/refs.h b/refs.h
index ff113bb12a2512c8f2caf9f0bd8e1e471490a5c0..303c5fac4d08b9798fafb6d4c667e9a94bf91e72 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -11,6 +11,9 @@ struct string_list;
 struct string_list_item;
 struct worktree;
 
+unsigned int ref_storage_format_by_name(const char *name);
+const char *ref_storage_format_to_name(unsigned int ref_storage_format);
+
 /*
  * Resolve a reference, recursively following symbolic refererences.
  *
@@ -123,7 +126,9 @@ int should_autocreate_reflog(const char *refname);
 
 int is_branch(const char *refname);
 
-int refs_init_db(struct strbuf *err);
+#define REFS_INIT_DB_IS_WORKTREE (1 << 0)
+
+int refs_init_db(struct ref_store *refs, int flags, struct strbuf *err);
 
 /*
  * Return the peeled value of the oid currently being iterated via
index 83b7a0ba655a50e83430da8ad3d80885cc68bd99..634681ca44e39b86129b2bbd305aa4c1771c3328 100644 (file)
@@ -33,10 +33,10 @@ struct ref_store *maybe_debug_wrap_ref_store(const char *gitdir, struct ref_stor
        return (struct ref_store *)res;
 }
 
-static int debug_init_db(struct ref_store *refs, struct strbuf *err)
+static int debug_init_db(struct ref_store *refs, int flags, struct strbuf *err)
 {
        struct debug_ref_store *drefs = (struct debug_ref_store *)refs;
-       int res = drefs->refs->be->init_db(drefs->refs, err);
+       int res = drefs->refs->be->init_db(drefs->refs, flags, err);
        trace_printf_key(&trace_refs, "init_db: %d\n", res);
        return res;
 }
@@ -426,7 +426,6 @@ static int debug_reflog_expire(struct ref_store *ref_store, const char *refname,
 }
 
 struct ref_storage_be refs_be_debug = {
-       .next = NULL,
        .name = "debug",
        .init = NULL,
        .init_db = debug_init_db,
index 6734f2a309ebad096e1c4df994f17d9af6c10445..75dcc21ecb5ab83cadd37a899a252618a0c66df0 100644 (file)
@@ -3218,28 +3218,52 @@ static int files_reflog_expire(struct ref_store *ref_store,
        return -1;
 }
 
-static int files_init_db(struct ref_store *ref_store, struct strbuf *err UNUSED)
+static int files_init_db(struct ref_store *ref_store,
+                        int flags,
+                        struct strbuf *err UNUSED)
 {
        struct files_ref_store *refs =
                files_downcast(ref_store, REF_STORE_WRITE, "init_db");
        struct strbuf sb = STRBUF_INIT;
 
        /*
-        * Create .git/refs/{heads,tags}
+        * We need to create a "refs" dir in any case so that older versions of
+        * Git can tell that this is a repository. This serves two main purposes:
+        *
+        * - Clients will know to stop walking the parent-directory chain when
+        *   detecting the Git repository. Otherwise they may end up detecting
+        *   a Git repository in a parent directory instead.
+        *
+        * - Instead of failing to detect a repository with unknown reference
+        *   format altogether, old clients will print an error saying that
+        *   they do not understand the reference format extension.
         */
-       files_ref_path(refs, &sb, "refs/heads");
+       strbuf_addf(&sb, "%s/refs", ref_store->gitdir);
        safe_create_dir(sb.buf, 1);
+       adjust_shared_perm(sb.buf);
 
-       strbuf_reset(&sb);
-       files_ref_path(refs, &sb, "refs/tags");
-       safe_create_dir(sb.buf, 1);
+       /*
+        * There is no need to create directories for common refs when creating
+        * a worktree ref store.
+        */
+       if (!(flags & REFS_INIT_DB_IS_WORKTREE)) {
+               /*
+                * Create .git/refs/{heads,tags}
+                */
+               strbuf_reset(&sb);
+               files_ref_path(refs, &sb, "refs/heads");
+               safe_create_dir(sb.buf, 1);
+
+               strbuf_reset(&sb);
+               files_ref_path(refs, &sb, "refs/tags");
+               safe_create_dir(sb.buf, 1);
+       }
 
        strbuf_release(&sb);
        return 0;
 }
 
 struct ref_storage_be refs_be_files = {
-       .next = NULL,
        .name = "files",
        .init = files_ref_store_create,
        .init_db = files_init_db,
index e46906e612285a9412099e703a3715ca882eba75..a499a91c7e0ac94e45a7c28264647802522b65c4 100644 (file)
@@ -1245,6 +1245,7 @@ static const char PACKED_REFS_HEADER[] =
        "# pack-refs with: peeled fully-peeled sorted \n";
 
 static int packed_init_db(struct ref_store *ref_store UNUSED,
+                         int flags UNUSED,
                          struct strbuf *err UNUSED)
 {
        /* Nothing to do. */
@@ -1704,7 +1705,6 @@ static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_s
 }
 
 struct ref_storage_be refs_be_packed = {
-       .next = NULL,
        .name = "packed",
        .init = packed_ref_store_create,
        .init_db = packed_init_db,
index 4af83bf9a5e4d400939dfb61af28995aef33d6cc..82219829b011d12fcb4a27577baa41225ff812f7 100644 (file)
@@ -529,7 +529,9 @@ typedef struct ref_store *ref_store_init_fn(struct repository *repo,
                                            const char *gitdir,
                                            unsigned int flags);
 
-typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);
+typedef int ref_init_db_fn(struct ref_store *refs,
+                          int flags,
+                          struct strbuf *err);
 
 typedef int ref_transaction_prepare_fn(struct ref_store *refs,
                                       struct ref_transaction *transaction,
@@ -663,7 +665,6 @@ typedef int read_symbolic_ref_fn(struct ref_store *ref_store, const char *refnam
                                 struct strbuf *referent);
 
 struct ref_storage_be {
-       struct ref_storage_be *next;
        const char *name;
        ref_store_init_fn *init;
        ref_init_db_fn *init_db;
index c00bbc8aed50f02d21ed06c046bed3c5d4c2f17f..dedb05c7d8c0560898b03aba93043c7efa46aeea 100644 (file)
@@ -49,13 +49,11 @@ static void test_block_read_write(void)
 
        for (i = 0; i < N; i++) {
                char name[100];
-               uint8_t hash[GIT_SHA1_RAWSZ];
                snprintf(name, sizeof(name), "branch%02d", i);
-               memset(hash, i, sizeof(hash));
 
                rec.u.ref.refname = name;
                rec.u.ref.value_type = REFTABLE_REF_VAL1;
-               rec.u.ref.value.val1 = hash;
+               memset(rec.u.ref.value.val1, i, GIT_SHA1_RAWSZ);
 
                names[i] = xstrdup(name);
                n = block_writer_add(&bw, &rec);
index 574394092d1c2241e794cda58564382a1e8b42dc..c258ce953e81d7df5774a03250b056280beccbf3 100644 (file)
@@ -123,12 +123,12 @@ static int merged_iter_next_entry(struct merged_iter *mi,
                reftable_record_release(&top.rec);
        }
 
-       reftable_record_copy_from(rec, &entry.rec, hash_size(mi->hash_id));
+       reftable_record_release(rec);
+       *rec = entry.rec;
 
 done:
-       reftable_record_release(&entry.rec);
-       strbuf_release(&mi->entry_key);
-       strbuf_release(&mi->key);
+       if (err)
+               reftable_record_release(&entry.rec);
        return err;
 }
 
index 0d6e0d4bf572f46683a1a9a3c6e1c128ed51f928..46908f738f770f4be920a65df85b96178ddbc70d 100644 (file)
@@ -122,13 +122,11 @@ static void readers_destroy(struct reftable_reader **readers, size_t n)
 
 static void test_merged_between(void)
 {
-       uint8_t hash1[GIT_SHA1_RAWSZ] = { 1, 2, 3, 0 };
-
        struct reftable_ref_record r1[] = { {
                .refname = "b",
                .update_index = 1,
                .value_type = REFTABLE_REF_VAL1,
-               .value.val1 = hash1,
+               .value.val1 = { 1, 2, 3, 0 },
        } };
        struct reftable_ref_record r2[] = { {
                .refname = "a",
@@ -164,26 +162,24 @@ static void test_merged_between(void)
 
 static void test_merged(void)
 {
-       uint8_t hash1[GIT_SHA1_RAWSZ] = { 1 };
-       uint8_t hash2[GIT_SHA1_RAWSZ] = { 2 };
        struct reftable_ref_record r1[] = {
                {
                        .refname = "a",
                        .update_index = 1,
                        .value_type = REFTABLE_REF_VAL1,
-                       .value.val1 = hash1,
+                       .value.val1 = { 1 },
                },
                {
                        .refname = "b",
                        .update_index = 1,
                        .value_type = REFTABLE_REF_VAL1,
-                       .value.val1 = hash1,
+                       .value.val1 = { 1 },
                },
                {
                        .refname = "c",
                        .update_index = 1,
                        .value_type = REFTABLE_REF_VAL1,
-                       .value.val1 = hash1,
+                       .value.val1 = { 1 },
                }
        };
        struct reftable_ref_record r2[] = { {
@@ -196,13 +192,13 @@ static void test_merged(void)
                        .refname = "c",
                        .update_index = 3,
                        .value_type = REFTABLE_REF_VAL1,
-                       .value.val1 = hash2,
+                       .value.val1 = { 2 },
                },
                {
                        .refname = "d",
                        .update_index = 3,
                        .value_type = REFTABLE_REF_VAL1,
-                       .value.val1 = hash1,
+                       .value.val1 = { 1 },
                },
        };
 
index 79cd4e4c28abc4dbbad43f219b11fe4c0b28e5c3..b8a32240164d6a5c29789fb1730a630127f085d5 100644 (file)
@@ -59,18 +59,15 @@ static void write_table(char ***names, struct strbuf *buf, int N,
        *names = reftable_calloc(sizeof(char *) * (N + 1));
        reftable_writer_set_limits(w, update_index, update_index);
        for (i = 0; i < N; i++) {
-               uint8_t hash[GIT_SHA256_RAWSZ] = { 0 };
                char name[100];
                int n;
 
-               set_test_hash(hash, i);
-
                snprintf(name, sizeof(name), "refs/heads/branch%02d", i);
 
                ref.refname = name;
                ref.update_index = update_index;
                ref.value_type = REFTABLE_REF_VAL1;
-               ref.value.val1 = hash;
+               set_test_hash(ref.value.val1, i);
                (*names)[i] = xstrdup(name);
 
                n = reftable_writer_add_ref(w, &ref);
@@ -549,8 +546,6 @@ static void test_table_refs_for(int indexed)
                uint8_t hash[GIT_SHA1_RAWSZ];
                char fill[51] = { 0 };
                char name[100];
-               uint8_t hash1[GIT_SHA1_RAWSZ];
-               uint8_t hash2[GIT_SHA1_RAWSZ];
                struct reftable_ref_record ref = { NULL };
 
                memset(hash, i, sizeof(hash));
@@ -560,11 +555,9 @@ static void test_table_refs_for(int indexed)
                name[40] = 0;
                ref.refname = name;
 
-               set_test_hash(hash1, i / 4);
-               set_test_hash(hash2, 3 + i / 4);
                ref.value_type = REFTABLE_REF_VAL2;
-               ref.value.val2.value = hash1;
-               ref.value.val2.target_value = hash2;
+               set_test_hash(ref.value.val2.value, i / 4);
+               set_test_hash(ref.value.val2.target_value, 3 + i / 4);
 
                /* 80 bytes / entry, so 3 entries per block. Yields 17
                 */
@@ -572,8 +565,8 @@ static void test_table_refs_for(int indexed)
                n = reftable_writer_add_ref(w, &ref);
                EXPECT(n == 0);
 
-               if (!memcmp(hash1, want_hash, GIT_SHA1_RAWSZ) ||
-                   !memcmp(hash2, want_hash, GIT_SHA1_RAWSZ)) {
+               if (!memcmp(ref.value.val2.value, want_hash, GIT_SHA1_RAWSZ) ||
+                   !memcmp(ref.value.val2.target_value, want_hash, GIT_SHA1_RAWSZ)) {
                        want_names[want_names_len++] = xstrdup(name);
                }
        }
@@ -674,11 +667,10 @@ static void test_write_object_id_min_length(void)
        struct strbuf buf = STRBUF_INIT;
        struct reftable_writer *w =
                reftable_new_writer(&strbuf_add_void, &buf, &opts);
-       uint8_t hash[GIT_SHA1_RAWSZ] = {42};
        struct reftable_ref_record ref = {
                .update_index = 1,
                .value_type = REFTABLE_REF_VAL1,
-               .value.val1 = hash,
+               .value.val1 = {42},
        };
        int err;
        int i;
@@ -710,11 +702,10 @@ static void test_write_object_id_length(void)
        struct strbuf buf = STRBUF_INIT;
        struct reftable_writer *w =
                reftable_new_writer(&strbuf_add_void, &buf, &opts);
-       uint8_t hash[GIT_SHA1_RAWSZ] = {42};
        struct reftable_ref_record ref = {
                .update_index = 1,
                .value_type = REFTABLE_REF_VAL1,
-               .value.val1 = hash,
+               .value.val1 = {42},
        };
        int err;
        int i;
@@ -797,6 +788,84 @@ static void test_write_key_order(void)
        strbuf_release(&buf);
 }
 
+static void test_write_multiple_indices(void)
+{
+       struct reftable_write_options opts = {
+               .block_size = 100,
+       };
+       struct strbuf writer_buf = STRBUF_INIT, buf = STRBUF_INIT;
+       struct reftable_block_source source = { 0 };
+       struct reftable_iterator it = { 0 };
+       const struct reftable_stats *stats;
+       struct reftable_writer *writer;
+       struct reftable_reader *reader;
+       int err, i;
+
+       writer = reftable_new_writer(&strbuf_add_void, &writer_buf, &opts);
+       reftable_writer_set_limits(writer, 1, 1);
+       for (i = 0; i < 100; i++) {
+               struct reftable_ref_record ref = {
+                       .update_index = 1,
+                       .value_type = REFTABLE_REF_VAL1,
+                       .value.val1 = {i},
+               };
+
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "refs/heads/%04d", i);
+               ref.refname = buf.buf,
+
+               err = reftable_writer_add_ref(writer, &ref);
+               EXPECT_ERR(err);
+       }
+
+       for (i = 0; i < 100; i++) {
+               unsigned char hash[GIT_SHA1_RAWSZ] = {i};
+               struct reftable_log_record log = {
+                       .update_index = 1,
+                       .value_type = REFTABLE_LOG_UPDATE,
+                       .value.update = {
+                               .old_hash = hash,
+                               .new_hash = hash,
+                       },
+               };
+
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "refs/heads/%04d", i);
+               log.refname = buf.buf,
+
+               err = reftable_writer_add_log(writer, &log);
+               EXPECT_ERR(err);
+       }
+
+       reftable_writer_close(writer);
+
+       /*
+        * The written data should be sufficiently large to result in indices
+        * for each of the block types.
+        */
+       stats = reftable_writer_stats(writer);
+       EXPECT(stats->ref_stats.index_offset > 0);
+       EXPECT(stats->obj_stats.index_offset > 0);
+       EXPECT(stats->log_stats.index_offset > 0);
+
+       block_source_from_strbuf(&source, &writer_buf);
+       err = reftable_new_reader(&reader, &source, "filename");
+       EXPECT_ERR(err);
+
+       /*
+        * Seeking the log uses the log index now. In case there is any
+        * confusion regarding indices we would notice here.
+        */
+       err = reftable_reader_seek_log(reader, &it, "");
+       EXPECT_ERR(err);
+
+       reftable_iterator_destroy(&it);
+       reftable_writer_free(writer);
+       reftable_reader_free(reader);
+       strbuf_release(&writer_buf);
+       strbuf_release(&buf);
+}
+
 static void test_corrupt_table_empty(void)
 {
        struct strbuf buf = STRBUF_INIT;
@@ -846,5 +915,6 @@ int readwrite_test_main(int argc, const char *argv[])
        RUN_TEST(test_log_overflow);
        RUN_TEST(test_write_object_id_length);
        RUN_TEST(test_write_object_id_min_length);
+       RUN_TEST(test_write_multiple_indices);
        return 0;
 }
index fbaa1fbef56c52fed8391c7bcef2efd035eb588b..5c3fbb7b2a1e95ad418066df0aa96235116f5329 100644 (file)
@@ -76,7 +76,7 @@ int reftable_is_block_type(uint8_t typ)
        return 0;
 }
 
-uint8_t *reftable_ref_record_val1(const struct reftable_ref_record *rec)
+const unsigned char *reftable_ref_record_val1(const struct reftable_ref_record *rec)
 {
        switch (rec->value_type) {
        case REFTABLE_REF_VAL1:
@@ -88,7 +88,7 @@ uint8_t *reftable_ref_record_val1(const struct reftable_ref_record *rec)
        }
 }
 
-uint8_t *reftable_ref_record_val2(const struct reftable_ref_record *rec)
+const unsigned char *reftable_ref_record_val2(const struct reftable_ref_record *rec)
 {
        switch (rec->value_type) {
        case REFTABLE_REF_VAL2:
@@ -219,13 +219,10 @@ static void reftable_ref_record_copy_from(void *rec, const void *src_rec,
        case REFTABLE_REF_DELETION:
                break;
        case REFTABLE_REF_VAL1:
-               ref->value.val1 = reftable_malloc(hash_size);
                memcpy(ref->value.val1, src->value.val1, hash_size);
                break;
        case REFTABLE_REF_VAL2:
-               ref->value.val2.value = reftable_malloc(hash_size);
                memcpy(ref->value.val2.value, src->value.val2.value, hash_size);
-               ref->value.val2.target_value = reftable_malloc(hash_size);
                memcpy(ref->value.val2.target_value,
                       src->value.val2.target_value, hash_size);
                break;
@@ -242,7 +239,7 @@ static char hexdigit(int c)
        return 'a' + (c - 10);
 }
 
-static void hex_format(char *dest, uint8_t *src, int hash_size)
+static void hex_format(char *dest, const unsigned char *src, int hash_size)
 {
        assert(hash_size > 0);
        if (src) {
@@ -299,11 +296,8 @@ void reftable_ref_record_release(struct reftable_ref_record *ref)
                reftable_free(ref->value.symref);
                break;
        case REFTABLE_REF_VAL2:
-               reftable_free(ref->value.val2.target_value);
-               reftable_free(ref->value.val2.value);
                break;
        case REFTABLE_REF_VAL1:
-               reftable_free(ref->value.val1);
                break;
        case REFTABLE_REF_DELETION:
                break;
@@ -394,7 +388,6 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,
                        return -1;
                }
 
-               r->value.val1 = reftable_malloc(hash_size);
                memcpy(r->value.val1, in.buf, hash_size);
                string_view_consume(&in, hash_size);
                break;
@@ -404,11 +397,9 @@ static int reftable_ref_record_decode(void *rec, struct strbuf key,
                        return -1;
                }
 
-               r->value.val2.value = reftable_malloc(hash_size);
                memcpy(r->value.val2.value, in.buf, hash_size);
                string_view_consume(&in, hash_size);
 
-               r->value.val2.target_value = reftable_malloc(hash_size);
                memcpy(r->value.val2.target_value, in.buf, hash_size);
                string_view_consume(&in, hash_size);
                break;
@@ -1164,7 +1155,7 @@ int reftable_record_equal(struct reftable_record *a, struct reftable_record *b,
                reftable_record_data(a), reftable_record_data(b), hash_size);
 }
 
-static int hash_equal(uint8_t *a, uint8_t *b, int hash_size)
+static int hash_equal(const unsigned char *a, const unsigned char *b, int hash_size)
 {
        if (a && b)
                return !memcmp(a, b, hash_size);
index 70ae78feca4f11cdb50f4fdcb08d90118bf487df..2876db7d2708aa36d861181775de74c966862c2d 100644 (file)
@@ -119,15 +119,10 @@ static void test_reftable_ref_record_roundtrip(void)
                case REFTABLE_REF_DELETION:
                        break;
                case REFTABLE_REF_VAL1:
-                       in.u.ref.value.val1 = reftable_malloc(GIT_SHA1_RAWSZ);
                        set_hash(in.u.ref.value.val1, 1);
                        break;
                case REFTABLE_REF_VAL2:
-                       in.u.ref.value.val2.value =
-                               reftable_malloc(GIT_SHA1_RAWSZ);
                        set_hash(in.u.ref.value.val2.value, 1);
-                       in.u.ref.value.val2.target_value =
-                               reftable_malloc(GIT_SHA1_RAWSZ);
                        set_hash(in.u.ref.value.val2.target_value, 2);
                        break;
                case REFTABLE_REF_SYMREF:
index 67104f8fbfecd7984127d29dd1ae3fa9bb263394..bb6e99acd3151ec75a7a2dc60e930064b7322e60 100644 (file)
@@ -9,6 +9,7 @@ https://developers.google.com/open-source/licenses/bsd
 #ifndef REFTABLE_RECORD_H
 #define REFTABLE_RECORD_H
 
+#include "hash-ll.h"
 #include <stdint.h>
 
 /*
@@ -38,10 +39,10 @@ struct reftable_ref_record {
 #define REFTABLE_NR_REF_VALUETYPES 4
        } value_type;
        union {
-               uint8_t *val1; /* malloced hash. */
+               unsigned char val1[GIT_MAX_RAWSZ];
                struct {
-                       uint8_t *value; /* first value, malloced hash  */
-                       uint8_t *target_value; /* second value, malloced hash */
+                       unsigned char value[GIT_MAX_RAWSZ]; /* first hash  */
+                       unsigned char target_value[GIT_MAX_RAWSZ]; /* second hash */
                } val2;
                char *symref; /* referent, malloced 0-terminated string */
        } value;
@@ -49,11 +50,11 @@ struct reftable_ref_record {
 
 /* Returns the first hash, or NULL if `rec` is not of type
  * REFTABLE_REF_VAL1 or REFTABLE_REF_VAL2. */
-uint8_t *reftable_ref_record_val1(const struct reftable_ref_record *rec);
+const unsigned char *reftable_ref_record_val1(const struct reftable_ref_record *rec);
 
 /* Returns the second hash, or NULL if `rec` is not of type
  * REFTABLE_REF_VAL2. */
-uint8_t *reftable_ref_record_val2(const struct reftable_ref_record *rec);
+const unsigned char *reftable_ref_record_val2(const struct reftable_ref_record *rec);
 
 /* returns whether 'ref' represents a deletion */
 int reftable_ref_record_is_deletion(const struct reftable_ref_record *ref);
index 16bab82063d2b3e228abe05b3a8a26ffd310767c..7ffeb3ee1074bc59aa094fabbbce24d1f3cfaf9a 100644 (file)
@@ -425,9 +425,6 @@ int reftable_stack_add(struct reftable_stack *st,
                return err;
        }
 
-       if (!st->disable_auto_compact)
-               return reftable_stack_auto_compact(st);
-
        return 0;
 }
 
@@ -801,18 +798,16 @@ static int stack_write_compact(struct reftable_stack *st,
                        err = 0;
                        break;
                }
-               if (err < 0) {
-                       break;
-               }
+               if (err < 0)
+                       goto done;
 
                if (first == 0 && reftable_ref_record_is_deletion(&ref)) {
                        continue;
                }
 
                err = reftable_writer_add_ref(wr, &ref);
-               if (err < 0) {
-                       break;
-               }
+               if (err < 0)
+                       goto done;
                entries++;
        }
        reftable_iterator_destroy(&it);
@@ -827,9 +822,8 @@ static int stack_write_compact(struct reftable_stack *st,
                        err = 0;
                        break;
                }
-               if (err < 0) {
-                       break;
-               }
+               if (err < 0)
+                       goto done;
                if (first == 0 && reftable_log_record_is_deletion(&log)) {
                        continue;
                }
@@ -845,9 +839,8 @@ static int stack_write_compact(struct reftable_stack *st,
                }
 
                err = reftable_writer_add_log(wr, &log);
-               if (err < 0) {
-                       break;
-               }
+               if (err < 0)
+                       goto done;
                entries++;
        }
 
index 82280c2fd586e2464561765575127051dd77c15b..289e902146470035e4dd0109af6b8436e97d40d4 100644 (file)
@@ -462,7 +462,6 @@ static void test_reftable_stack_add(void)
                refs[i].refname = xstrdup(buf);
                refs[i].update_index = i + 1;
                refs[i].value_type = REFTABLE_REF_VAL1;
-               refs[i].value.val1 = reftable_malloc(GIT_SHA1_RAWSZ);
                set_test_hash(refs[i].value.val1, i);
 
                logs[i].refname = xstrdup(buf);
@@ -599,7 +598,6 @@ static void test_reftable_stack_tombstone(void)
                refs[i].update_index = i + 1;
                if (i % 2 == 0) {
                        refs[i].value_type = REFTABLE_REF_VAL1;
-                       refs[i].value.val1 = reftable_malloc(GIT_SHA1_RAWSZ);
                        set_test_hash(refs[i].value.val1, i);
                }
 
index 2e322a5683d081eea60fc85dfc4d792ce89b7a93..ee4590e20f84dd442fde4f7506e27e568f9ad509 100644 (file)
@@ -432,12 +432,12 @@ static int writer_finish_section(struct reftable_writer *w)
                reftable_free(idx);
        }
 
-       writer_clear_index(w);
-
        err = writer_flush_block(w);
        if (err < 0)
                return err;
 
+       writer_clear_index(w);
+
        bstats = writer_reftable_block_stats(w, typ);
        bstats->index_blocks = w->stats.idx_stats.blocks - before_blocks;
        bstats->index_offset = index_start;
index a7679ceeaa45ce7a20094f9a29a82987290ad354..d7d24d416ae4373c9283ec6bd5171b7cfdd1e759 100644 (file)
@@ -104,6 +104,11 @@ void repo_set_hash_algo(struct repository *repo, int hash_algo)
        repo->hash_algo = &hash_algos[hash_algo];
 }
 
+void repo_set_ref_storage_format(struct repository *repo, unsigned int format)
+{
+       repo->ref_storage_format = format;
+}
+
 /*
  * Attempt to resolve and set the provided 'gitdir' for repository 'repo'.
  * Return 0 upon success and a non-zero value upon failure.
@@ -184,6 +189,7 @@ int repo_init(struct repository *repo,
                goto error;
 
        repo_set_hash_algo(repo, format.hash_algo);
+       repo_set_ref_storage_format(repo, format.ref_storage_format);
        repo->repository_format_worktree_config = format.worktree_config;
 
        /* take ownership of format.partial_clone */
index 5f18486f6465c44b44abb2ded74bcdda14ba7bd9..f5269b3730c602a79adbde479f4622f124156b60 100644 (file)
@@ -24,6 +24,9 @@ enum fetch_negotiation_setting {
        FETCH_NEGOTIATION_NOOP,
 };
 
+#define REF_STORAGE_FORMAT_UNKNOWN 0
+#define REF_STORAGE_FORMAT_FILES   1
+
 struct repo_settings {
        int initialized;
 
@@ -160,6 +163,9 @@ struct repository {
        /* Repository's current hash algorithm, as serialized on disk. */
        const struct git_hash_algo *hash_algo;
 
+       /* Repository's reference storage format, as serialized on disk. */
+       unsigned int ref_storage_format;
+
        /* A unique-id for tracing purposes. */
        int trace2_repo_id;
 
@@ -199,6 +205,7 @@ void repo_set_gitdir(struct repository *repo, const char *root,
                     const struct set_gitdir_args *extra_args);
 void repo_set_worktree(struct repository *repo, const char *path);
 void repo_set_hash_algo(struct repository *repo, int algo);
+void repo_set_ref_storage_format(struct repository *repo, unsigned int format);
 void initialize_the_repository(void);
 RESULT_MUST_BE_USED
 int repo_init(struct repository *r, const char *gitdir, const char *worktree);
diff --git a/setup.c b/setup.c
index 0161ce747dfd041a67494433b25fee1e5d0ab9f8..f4ad53d5454847951f2c55d02e9418484fdadac2 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -591,6 +591,17 @@ static enum extension_result handle_extension(const char *var,
                                     "extensions.objectformat", value);
                data->hash_algo = format;
                return EXTENSION_OK;
+       } else if (!strcmp(ext, "refstorage")) {
+               unsigned int format;
+
+               if (!value)
+                       return config_error_nonbool(var);
+               format = ref_storage_format_by_name(value);
+               if (format == REF_STORAGE_FORMAT_UNKNOWN)
+                       return error(_("invalid value for '%s': '%s'"),
+                                    "extensions.refstorage", value);
+               data->ref_storage_format = format;
+               return EXTENSION_OK;
        }
        return EXTENSION_UNKNOWN;
 }
@@ -1565,6 +1576,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
                }
                if (startup_info->have_repository) {
                        repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
+                       repo_set_ref_storage_format(the_repository,
+                                                   repo_fmt.ref_storage_format);
                        the_repository->repository_format_worktree_config =
                                repo_fmt.worktree_config;
                        /* take ownership of repo_fmt.partial_clone */
@@ -1658,6 +1671,8 @@ void check_repository_format(struct repository_format *fmt)
        check_repository_format_gently(get_git_dir(), fmt, NULL);
        startup_info->have_repository = 1;
        repo_set_hash_algo(the_repository, fmt->hash_algo);
+       repo_set_ref_storage_format(the_repository,
+                                   fmt->ref_storage_format);
        the_repository->repository_format_worktree_config =
                fmt->worktree_config;
        the_repository->repository_format_partial_clone =
@@ -1866,12 +1881,15 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)
        return 1;
 }
 
-void initialize_repository_version(int hash_algo, int reinit)
+void initialize_repository_version(int hash_algo,
+                                  unsigned int ref_storage_format,
+                                  int reinit)
 {
        char repo_version_string[10];
        int repo_version = GIT_REPO_VERSION;
 
-       if (hash_algo != GIT_HASH_SHA1)
+       if (hash_algo != GIT_HASH_SHA1 ||
+           ref_storage_format != REF_STORAGE_FORMAT_FILES)
                repo_version = GIT_REPO_VERSION_READ;
 
        /* This forces creation of new config file */
@@ -1884,6 +1902,10 @@ void initialize_repository_version(int hash_algo, int reinit)
                               hash_algos[hash_algo].name);
        else if (reinit)
                git_config_set_gently("extensions.objectformat", NULL);
+
+       if (ref_storage_format != REF_STORAGE_FORMAT_FILES)
+               git_config_set("extensions.refstorage",
+                              ref_storage_format_to_name(ref_storage_format));
 }
 
 static int is_reinit(void)
@@ -1898,27 +1920,14 @@ static int is_reinit(void)
        return ret;
 }
 
-void create_reference_database(const char *initial_branch, int quiet)
+void create_reference_database(unsigned int ref_storage_format,
+                              const char *initial_branch, int quiet)
 {
        struct strbuf err = STRBUF_INIT;
        int reinit = is_reinit();
 
-       /*
-        * We need to create a "refs" dir in any case so that older versions of
-        * Git can tell that this is a repository. This serves two main purposes:
-        *
-        * - Clients will know to stop walking the parent-directory chain when
-        *   detecting the Git repository. Otherwise they may end up detecting
-        *   a Git repository in a parent directory instead.
-        *
-        * - Instead of failing to detect a repository with unknown reference
-        *   format altogether, old clients will print an error saying that
-        *   they do not understand the reference format extension.
-        */
-       safe_create_dir(git_path("refs"), 1);
-       adjust_shared_perm(git_path("refs"));
-
-       if (refs_init_db(&err))
+       repo_set_ref_storage_format(the_repository, ref_storage_format);
+       if (refs_init_db(get_main_ref_store(the_repository), 0, &err))
                die("failed to set up refs db: %s", err.buf);
 
        /*
@@ -2023,7 +2032,7 @@ static int create_default_files(const char *template_path,
                adjust_shared_perm(get_git_dir());
        }
 
-       initialize_repository_version(fmt->hash_algo, 0);
+       initialize_repository_version(fmt->hash_algo, fmt->ref_storage_format, 0);
 
        /* Check filemode trustability */
        path = git_path_buf(&buf, "config");
@@ -2136,8 +2145,29 @@ static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash
        }
 }
 
+static void validate_ref_storage_format(struct repository_format *repo_fmt,
+                                       unsigned int format)
+{
+       const char *name = getenv("GIT_DEFAULT_REF_FORMAT");
+
+       if (repo_fmt->version >= 0 &&
+           format != REF_STORAGE_FORMAT_UNKNOWN &&
+           format != repo_fmt->ref_storage_format) {
+               die(_("attempt to reinitialize repository with different reference storage format"));
+       } else if (format != REF_STORAGE_FORMAT_UNKNOWN) {
+               repo_fmt->ref_storage_format = format;
+       } else if (name) {
+               format = ref_storage_format_by_name(name);
+               if (format == REF_STORAGE_FORMAT_UNKNOWN)
+                       die(_("unknown ref storage format '%s'"), name);
+               repo_fmt->ref_storage_format = format;
+       }
+}
+
 int init_db(const char *git_dir, const char *real_git_dir,
-           const char *template_dir, int hash, const char *initial_branch,
+           const char *template_dir, int hash,
+           unsigned int ref_storage_format,
+           const char *initial_branch,
            int init_shared_repository, unsigned int flags)
 {
        int reinit;
@@ -2180,13 +2210,22 @@ int init_db(const char *git_dir, const char *real_git_dir,
        check_repository_format(&repo_fmt);
 
        validate_hash_algorithm(&repo_fmt, hash);
+       validate_ref_storage_format(&repo_fmt, ref_storage_format);
 
        reinit = create_default_files(template_dir, original_git_dir,
                                      &repo_fmt, prev_bare_repository,
                                      init_shared_repository);
 
+       /*
+        * Now that we have set up both the hash algorithm and the ref storage
+        * format we can update the repository's settings accordingly.
+        */
+       repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
+       repo_set_ref_storage_format(the_repository, repo_fmt.ref_storage_format);
+
        if (!(flags & INIT_DB_SKIP_REFDB))
-               create_reference_database(initial_branch, flags & INIT_DB_QUIET);
+               create_reference_database(repo_fmt.ref_storage_format,
+                                         initial_branch, flags & INIT_DB_QUIET);
        create_object_directory();
 
        if (get_shared_repository()) {
diff --git a/setup.h b/setup.h
index 3f0f17c351cbc54348ef1abe6b6e74a54d98ec56..3599aec93c5ac0b72aafaf3cdcb030831cc53e3b 100644 (file)
--- a/setup.h
+++ b/setup.h
@@ -115,6 +115,7 @@ struct repository_format {
        int worktree_config;
        int is_bare;
        int hash_algo;
+       unsigned int ref_storage_format;
        int sparse_index;
        char *work_tree;
        struct string_list unknown_extensions;
@@ -131,6 +132,7 @@ struct repository_format {
        .version = -1, \
        .is_bare = -1, \
        .hash_algo = GIT_HASH_SHA1, \
+       .ref_storage_format = REF_STORAGE_FORMAT_FILES, \
        .unknown_extensions = STRING_LIST_INIT_DUP, \
        .v1_only_extensions = STRING_LIST_INIT_DUP, \
 }
@@ -175,10 +177,14 @@ void check_repository_format(struct repository_format *fmt);
 
 int init_db(const char *git_dir, const char *real_git_dir,
            const char *template_dir, int hash_algo,
+           unsigned int ref_storage_format,
            const char *initial_branch, int init_shared_repository,
            unsigned int flags);
-void initialize_repository_version(int hash_algo, int reinit);
-void create_reference_database(const char *initial_branch, int quiet);
+void initialize_repository_version(int hash_algo,
+                                  unsigned int ref_storage_format,
+                                  int reinit);
+void create_reference_database(unsigned int ref_storage_format,
+                              const char *initial_branch, int quiet);
 
 /*
  * NOTE NOTE NOTE!!
index 6cbfd391c47fb531740eb4b963a11b77f8abe29d..266a67342be7245ae3c1dfae5a1a5ad6647f95df 100644 (file)
@@ -69,7 +69,10 @@ void list_config_color_sideband_slots(struct string_list *list, const char *pref
  * of the line. This should be called for a single line only, which is
  * passed as the first N characters of the SRC array.
  *
- * NEEDSWORK: use "size_t n" instead for clarity.
+ * It is fine to use "int n" here instead of "size_t n" as all calls to this
+ * function pass an 'int' parameter. Additionally, the buffer involved in
+ * storing these 'int' values takes input from a packet via the pkt-line
+ * interface, which is capable of transferring only 64kB at a time.
  */
 static void maybe_colorize_sideband(struct strbuf *dest, const char *src, int n)
 {
index 9f55c8766ba9de77437275b8796f8e348fe35e03..4715d3e51f873e7b6618a80ebc79f2d2b4ebaab7 100644 (file)
--- a/strvec.h
+++ b/strvec.h
@@ -4,8 +4,8 @@
 /**
  * The strvec API allows one to dynamically build and store
  * NULL-terminated arrays of strings. A strvec maintains the invariant that the
- * `items` member always points to a non-NULL array, and that the array is
- * always NULL-terminated at the element pointed to by `items[nr]`. This
+ * `v` member always points to a non-NULL array, and that the array is
+ * always NULL-terminated at the element pointed to by `v[nr]`. This
  * makes the result suitable for passing to functions expecting to receive
  * argv from main().
  *
@@ -22,7 +22,7 @@ extern const char *empty_strvec[];
 
 /**
  * A single array. This should be initialized by assignment from
- * `STRVEC_INIT`, or by calling `strvec_init`. The `items`
+ * `STRVEC_INIT`, or by calling `strvec_init`. The `v`
  * member contains the actual array; the `nr` member contains the
  * number of elements in the array, not including the terminating
  * NULL.
@@ -80,7 +80,7 @@ void strvec_split(struct strvec *, const char *);
 void strvec_clear(struct strvec *);
 
 /**
- * Disconnect the `items` member from the `strvec` struct and
+ * Disconnect the `v` member from the `strvec` struct and
  * return it. The caller is responsible for freeing the memory used
  * by the array, and by the strings it references. After detaching,
  * the `strvec` is in a reinitialized state and can be pushed
index 36463d074259e3805e6de6d41d30bc3632b369f5..621d3b8c095441a8a8985b7f12363e26f8ab4d98 100644 (file)
--- a/t/README
+++ b/t/README
@@ -479,6 +479,9 @@ GIT_TEST_DEFAULT_HASH=<hash-algo> specifies which hash algorithm to
 use in the test scripts. Recognized values for <hash-algo> are "sha1"
 and "sha256".
 
+GIT_TEST_DEFAULT_REF_FORMAT=<format> specifies which ref storage format
+to use in the test scripts. Recognized values for <format> are "files".
+
 GIT_TEST_NO_WRITE_REV_INDEX=<boolean>, when true disables the
 'pack.writeReverseIndex' setting.
 
diff --git a/t/helper/test-ctype.c b/t/helper/test-ctype.c
deleted file mode 100644 (file)
index e5659df..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "test-tool.h"
-
-static int rc;
-
-static void report_error(const char *class, int ch)
-{
-       printf("%s classifies char %d (0x%02x) wrongly\n", class, ch, ch);
-       rc = 1;
-}
-
-static int is_in(const char *s, int ch)
-{
-       /*
-        * We can't find NUL using strchr. Accept it as the first
-        * character in the spec -- there are no empty classes.
-        */
-       if (ch == '\0')
-               return ch == *s;
-       if (*s == '\0')
-               s++;
-       return !!strchr(s, ch);
-}
-
-#define TEST_CLASS(t,s) {                      \
-       int i;                                  \
-       for (i = 0; i < 256; i++) {             \
-               if (is_in(s, i) != t(i))        \
-                       report_error(#t, i);    \
-       }                                       \
-       if (t(EOF))                             \
-               report_error(#t, EOF);          \
-}
-
-#define DIGIT "0123456789"
-#define LOWER "abcdefghijklmnopqrstuvwxyz"
-#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-#define PUNCT "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
-#define ASCII \
-       "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
-       "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
-       "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" \
-       "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" \
-       "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" \
-       "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" \
-       "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" \
-       "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
-#define CNTRL \
-       "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
-       "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
-       "\x7f"
-
-int cmd__ctype(int argc UNUSED, const char **argv UNUSED)
-{
-       TEST_CLASS(isdigit, DIGIT);
-       TEST_CLASS(isspace, " \n\r\t");
-       TEST_CLASS(isalpha, LOWER UPPER);
-       TEST_CLASS(isalnum, LOWER UPPER DIGIT);
-       TEST_CLASS(is_glob_special, "*?[\\");
-       TEST_CLASS(is_regex_special, "$()*+.?[\\^{|");
-       TEST_CLASS(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~");
-       TEST_CLASS(isascii, ASCII);
-       TEST_CLASS(islower, LOWER);
-       TEST_CLASS(isupper, UPPER);
-       TEST_CLASS(iscntrl, CNTRL);
-       TEST_CLASS(ispunct, PUNCT);
-       TEST_CLASS(isxdigit, DIGIT "abcdefABCDEF");
-       TEST_CLASS(isprint, LOWER UPPER DIGIT PUNCT " ");
-
-       return rc;
-}
index 37ba996539b72f66316722cc73094d44aef6e61c..33b9501c2112a758c1f662890d31fcbce93b854f 100644 (file)
@@ -19,7 +19,6 @@ static struct test_cmd cmds[] = {
        { "config", cmd__config },
        { "crontab", cmd__crontab },
        { "csprng", cmd__csprng },
-       { "ctype", cmd__ctype },
        { "date", cmd__date },
        { "delta", cmd__delta },
        { "dir-iterator", cmd__dir_iterator },
index 8a1a7c63daa4dabf1b92d35420f2b2f5c36b136c..b72f07ded9946b207b0c20da9886d7842cc5ac5c 100644 (file)
@@ -12,7 +12,6 @@ int cmd__chmtime(int argc, const char **argv);
 int cmd__config(int argc, const char **argv);
 int cmd__crontab(int argc, const char **argv);
 int cmd__csprng(int argc, const char **argv);
-int cmd__ctype(int argc, const char **argv);
 int cmd__date(int argc, const char **argv);
 int cmd__delta(int argc, const char **argv);
 int cmd__dir_iterator(int argc, const char **argv);
index 2b78e3be4795096aa3d9eeb5f7b5c3dc3a24f717..b131d665db40e9858d4a3f9c13ab71eb77d6cc5f 100755 (executable)
@@ -532,6 +532,76 @@ test_expect_success 'init rejects attempts to initialize with different hash' '
        test_must_fail git -C sha256 init --object-format=sha1
 '
 
+test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage is not allowed with repo version 0' '
+       test_when_finished "rm -rf refstorage" &&
+       git init refstorage &&
+       git -C refstorage config extensions.refStorage files &&
+       test_must_fail git -C refstorage rev-parse 2>err &&
+       grep "repo version is 0, but v1-only extension found" err
+'
+
+test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with files backend' '
+       test_when_finished "rm -rf refstorage" &&
+       git init refstorage &&
+       git -C refstorage config core.repositoryformatversion 1 &&
+       git -C refstorage config extensions.refStorage files &&
+       test_commit -C refstorage A &&
+       git -C refstorage rev-parse --verify HEAD
+'
+
+test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with unknown backend' '
+       test_when_finished "rm -rf refstorage" &&
+       git init refstorage &&
+       git -C refstorage config core.repositoryformatversion 1 &&
+       git -C refstorage config extensions.refStorage garbage &&
+       test_must_fail git -C refstorage rev-parse 2>err &&
+       grep "invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}garbage${SQ}" err
+'
+
+test_expect_success DEFAULT_REPO_FORMAT 'init with GIT_DEFAULT_REF_FORMAT=files' '
+       test_when_finished "rm -rf refformat" &&
+       GIT_DEFAULT_REF_FORMAT=files git init refformat &&
+       echo 0 >expect &&
+       git -C refformat config core.repositoryformatversion >actual &&
+       test_cmp expect actual &&
+       test_must_fail git -C refformat config extensions.refstorage
+'
+
+test_expect_success 'init with GIT_DEFAULT_REF_FORMAT=garbage' '
+       test_when_finished "rm -rf refformat" &&
+       cat >expect <<-EOF &&
+       fatal: unknown ref storage format ${SQ}garbage${SQ}
+       EOF
+       test_must_fail env GIT_DEFAULT_REF_FORMAT=garbage git init refformat 2>err &&
+       test_cmp expect err
+'
+
+test_expect_success 'init with --ref-format=files' '
+       test_when_finished "rm -rf refformat" &&
+       git init --ref-format=files refformat &&
+       echo files >expect &&
+       git -C refformat rev-parse --show-ref-format >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 're-init with same format' '
+       test_when_finished "rm -rf refformat" &&
+       git init --ref-format=files refformat &&
+       git init --ref-format=files refformat &&
+       echo files >expect &&
+       git -C refformat rev-parse --show-ref-format >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'init with --ref-format=garbage' '
+       test_when_finished "rm -rf refformat" &&
+       cat >expect <<-EOF &&
+       fatal: unknown ref storage format ${SQ}garbage${SQ}
+       EOF
+       test_must_fail git init --ref-format=garbage refformat 2>err &&
+       test_cmp expect err
+'
+
 test_expect_success MINGW 'core.hidedotfiles = false' '
        git config --global core.hidedotfiles false &&
        rm -rf newdir &&
index f18f9284a5bf3961a3f5373f887b808e55cd9b75..0ecec2ba71116959fd2efceac7fc46683d1a3e91 100755 (executable)
@@ -9,10 +9,6 @@ Verify wrappers and compatibility functions.
 TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
-test_expect_success 'character classes (isspace, isalpha etc.)' '
-       test-tool ctype
-'
-
 test_expect_success 'mktemp to nonexistent directory prints filename' '
        test_must_fail test-tool mktemp doesnotexist/testXXXXXX 2>err &&
        grep "doesnotexist/test" err
index 3241d3591799415e423e4f6155e4111de778a964..5c60d6f812dc20c1c86f5125b5feabb4ce1df9ce 100755 (executable)
@@ -106,9 +106,8 @@ test_expect_success LONG_REF 'we can parse long symbolic ref' '
 '
 
 test_expect_success 'symbolic-ref reports failure in exit code' '
-       test_when_finished "rm -f .git/HEAD.lock" &&
-       >.git/HEAD.lock &&
-       test_must_fail git symbolic-ref HEAD refs/heads/whatever
+       # Create d/f conflict to simulate failure.
+       test_must_fail git symbolic-ref refs/heads refs/heads/foo
 '
 
 test_expect_success 'symbolic-ref writes reflog entry' '
index 3f9e7f62e458c4db06a686700a15c5e08f47fad9..a669e592f1d95ca7e0dc575697342326a7a6654f 100755 (executable)
@@ -208,6 +208,23 @@ test_expect_success 'rev-parse --show-object-format in repo' '
        grep "unknown mode for --show-object-format: squeamish-ossifrage" err
 '
 
+test_expect_success 'rev-parse --show-ref-format' '
+       test_detect_ref_format >expect &&
+       git rev-parse --show-ref-format >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rev-parse --show-ref-format with invalid storage' '
+       test_when_finished "rm -rf repo" &&
+       git init repo &&
+       (
+               cd repo &&
+               git config extensions.refstorage broken &&
+               test_must_fail git rev-parse --show-ref-format 2>err &&
+               grep "error: invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}broken${SQ}" err
+       )
+'
+
 test_expect_success '--show-toplevel from subdir of working tree' '
        pwd >expect &&
        git -C sub/dir rev-parse --show-toplevel >actual &&
index 6a316f081ee2267ebd0c545220b4de3e6e527101..de7d3014e4fb914012db343d9efd85a2818677b4 100755 (executable)
@@ -519,7 +519,7 @@ EOF
 
 mv .git/config .git/config-saved
 
-test_expect_success SHA1 'git branch -m q q2 without config should succeed' '
+test_expect_success DEFAULT_REPO_FORMAT 'git branch -m q q2 without config should succeed' '
        git branch -m q q2 &&
        git branch -m q2 q
 '
index e7a7295f1b687d1ed916e80e431e78c4c67657b7..2775bfadd8619b2a5e2440209c46c0658205ddcd 100755 (executable)
@@ -41,7 +41,8 @@ test_expect_success FILEMODE 'same mode (index only)' '
        chmod +x file &&
        git add file &&
        git apply --cached patch-0.txt &&
-       git ls-files -s file | grep "^100755"
+       git ls-files -s file >ls-files-output &&
+       test_grep "^100755" ls-files-output
 '
 
 test_expect_success FILEMODE 'mode update (no index)' '
@@ -60,7 +61,8 @@ test_expect_success FILEMODE 'mode update (with index)' '
 test_expect_success FILEMODE 'mode update (index only)' '
        git reset --hard &&
        git apply --cached patch-1.txt &&
-       git ls-files -s file | grep "^100755"
+       git ls-files -s file >ls-files-output &&
+       test_grep "^100755" ls-files-output
 '
 
 test_expect_success FILEMODE 'empty mode is rejected' '
index 79592a3b0addcbee8015ee0ddf843c9ccedf6935..33d34d5ae9e953e29f6dba8badb16922072cd76c 100755 (executable)
@@ -803,7 +803,8 @@ test_expect_success 'fetch.writeCommitGraph with submodules' '
                cd super-clone &&
                rm -rf .git/objects/info &&
                git -c fetch.writeCommitGraph=true fetch origin &&
-               test_path_is_file .git/objects/info/commit-graphs/commit-graph-chain
+               test_path_is_file .git/objects/info/commit-graphs/commit-graph-chain &&
+               git -c fetch.writeCommitGraph=true fetch --recurse-submodules origin
        )
 '
 
index a95841dc368329286a798982ffac30b7321e02df..25772c85c5a9a89bf247cfa41cf9d8b127572cd8 100755 (executable)
@@ -24,6 +24,15 @@ setup_repository () {
        )
 }
 
+setup_test_clone () {
+       test_dir="$1" &&
+       git clone one "$test_dir" &&
+       for r in one two three
+       do
+               git -C "$test_dir" remote add "$r" "../$r" || return 1
+       done
+}
+
 test_expect_success setup '
        setup_repository one &&
        setup_repository two &&
@@ -209,4 +218,156 @@ test_expect_success 'git fetch --multiple --jobs=0 picks a default' '
         git fetch --multiple --jobs=0)
 '
 
+create_fetch_all_expect () {
+       cat >expect <<-\EOF
+         one/main
+         one/side
+         origin/HEAD -> origin/main
+         origin/main
+         origin/side
+         three/another
+         three/main
+         three/side
+         two/another
+         two/main
+         two/side
+       EOF
+}
+
+for fetch_all in true false
+do
+       test_expect_success "git fetch --all (works with fetch.all = $fetch_all)" '
+               test_dir="test_fetch_all_$fetch_all" &&
+               setup_test_clone "$test_dir" &&
+               (
+                       cd "$test_dir" &&
+                       git config fetch.all $fetch_all &&
+                       git fetch --all &&
+                       create_fetch_all_expect &&
+                       git branch -r >actual &&
+                       test_cmp expect actual
+               )
+       '
+done
+
+test_expect_success 'git fetch (fetch all remotes with fetch.all = true)' '
+       setup_test_clone test9 &&
+       (
+               cd test9 &&
+               git config fetch.all true &&
+               git fetch &&
+               git branch -r >actual &&
+               create_fetch_all_expect &&
+               test_cmp expect actual
+       )
+'
+
+create_fetch_one_expect () {
+       cat >expect <<-\EOF
+         one/main
+         one/side
+         origin/HEAD -> origin/main
+         origin/main
+         origin/side
+       EOF
+}
+
+test_expect_success 'git fetch one (explicit remote overrides fetch.all)' '
+       setup_test_clone test10 &&
+       (
+               cd test10 &&
+               git config fetch.all true &&
+               git fetch one &&
+               create_fetch_one_expect &&
+               git branch -r >actual &&
+               test_cmp expect actual
+       )
+'
+
+create_fetch_two_as_origin_expect () {
+       cat >expect <<-\EOF
+         origin/HEAD -> origin/main
+         origin/another
+         origin/main
+         origin/side
+       EOF
+}
+
+test_expect_success 'git config fetch.all false (fetch only default remote)' '
+       setup_test_clone test11 &&
+       (
+               cd test11 &&
+               git config fetch.all false &&
+               git remote set-url origin ../two &&
+               git fetch &&
+               create_fetch_two_as_origin_expect &&
+               git branch -r >actual &&
+               test_cmp expect actual
+       )
+'
+
+for fetch_all in true false
+do
+       test_expect_success "git fetch --no-all (fetch only default remote with fetch.all = $fetch_all)" '
+               test_dir="test_no_all_fetch_all_$fetch_all" &&
+               setup_test_clone "$test_dir" &&
+               (
+                       cd "$test_dir" &&
+                       git config fetch.all $fetch_all &&
+                       git remote set-url origin ../two &&
+                       git fetch --no-all &&
+                       create_fetch_two_as_origin_expect &&
+                       git branch -r >actual &&
+                       test_cmp expect actual
+               )
+       '
+done
+
+test_expect_success 'git fetch --no-all (fetch only default remote without fetch.all)' '
+       setup_test_clone test12 &&
+       (
+               cd test12 &&
+               git config --unset-all fetch.all || true &&
+               git remote set-url origin ../two &&
+               git fetch --no-all &&
+               create_fetch_two_as_origin_expect &&
+               git branch -r >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'git fetch --all --no-all (fetch only default remote)' '
+       setup_test_clone test13 &&
+       (
+               cd test13 &&
+               git remote set-url origin ../two &&
+               git fetch --all --no-all &&
+               create_fetch_two_as_origin_expect &&
+               git branch -r >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'git fetch --no-all one (fetch only explicit remote)' '
+       setup_test_clone test14 &&
+       (
+               cd test14 &&
+               git fetch --no-all one &&
+               create_fetch_one_expect &&
+               git branch -r >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'git fetch --no-all --all (fetch all remotes)' '
+       setup_test_clone test15 &&
+       (
+               cd test15 &&
+               git fetch --no-all --all &&
+               create_fetch_all_expect &&
+               git branch -r >actual &&
+               test_cmp expect actual
+       )
+'
+
 test_done
index df758e187dfdc2fc869ad5cb747d895a145d5e91..71428f3d5c760a9bdca7524e15cb9747e92cfd17 100755 (executable)
@@ -232,8 +232,9 @@ test_expect_success 'push --atomic fails on server-side errors' '
        test_config -C "$d" http.receivepack true &&
        up="$HTTPD_URL"/smart/atomic-branches.git &&
 
-       # break ref updates for other on the remote site
-       mkdir "$d/refs/heads/other.lock" &&
+       # Create d/f conflict to break ref updates for other on the remote site.
+       git -C "$d" update-ref -d refs/heads/other &&
+       git -C "$d" update-ref refs/heads/other/conflict HEAD &&
 
        # add the new commit to other
        git branch -f other collateral &&
@@ -241,18 +242,9 @@ test_expect_success 'push --atomic fails on server-side errors' '
        # --atomic should cause entire push to be rejected
        test_must_fail git push --atomic "$up" atomic other 2>output  &&
 
-       # the new branch should not have been created upstream
-       test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
-
-       # upstream should still reflect atomic2, the last thing we pushed
-       # successfully
-       git rev-parse atomic2 >expected &&
-       # ...to other.
-       git -C "$d" rev-parse refs/heads/other >actual &&
-       test_cmp expected actual &&
-
-       # the new branch should not have been created upstream
+       # The atomic and other branches should not be created upstream.
        test_must_fail git -C "$d" show-ref --verify refs/heads/atomic &&
+       test_must_fail git -C "$d" show-ref --verify refs/heads/other &&
 
        # the failed refs should be indicated to the user
        grep "^ ! .*rejected.* other -> other .*atomic transaction failed" output &&
index 47eae641f018055f2200ef6e2581d8ac60364cdb..fb1b9c686db24a30f9dbd2f6f38f29bfc4624676 100755 (executable)
@@ -157,6 +157,23 @@ test_expect_success 'clone --mirror does not repeat tags' '
 
 '
 
+test_expect_success 'clone with files ref format' '
+       test_when_finished "rm -rf ref-storage" &&
+       git clone --ref-format=files --mirror src ref-storage &&
+       echo files >expect &&
+       git -C ref-storage rev-parse --show-ref-format >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone with garbage ref format' '
+       cat >expect <<-EOF &&
+       fatal: unknown ref storage format ${SQ}garbage${SQ}
+       EOF
+       test_must_fail git clone --ref-format=garbage --mirror src ref-storage 2>err &&
+       test_cmp expect err &&
+       test_path_is_missing ref-storage
+'
+
 test_expect_success 'clone to destination with trailing /' '
 
        git clone src target-1/ &&
index 0333065d4d60a0113cb5ac1de391a4a997d2eaa8..7679780fb87b4f14e9c0f5a22708b1e8925a4d53 100755 (executable)
@@ -627,6 +627,7 @@ test_expect_success \
 test_expect_success 'setup' '
        version=$(git config core.repositoryformatversion) &&
        algo=$(test_might_fail git config extensions.objectformat) &&
+       refstorage=$(test_might_fail git config extensions.refstorage) &&
        cat >.git/config <<-\EOF &&
        # testing noval and alternate separator
        [gitweb]
@@ -637,6 +638,10 @@ test_expect_success 'setup' '
        if test -n "$algo"
        then
                git config extensions.objectformat "$algo"
+       fi &&
+       if test -n "$refstorage"
+       then
+               git config extensions.refstorage "$refstorage"
        fi
 '
 
index 1213b8398bb60289bd6eb05d1261cf7f44d522fa..b5eaf7fdc1186d4420388c355e794bfdba783f2f 100644 (file)
@@ -1659,6 +1659,11 @@ test_detect_hash () {
        test_hash_algo="${GIT_TEST_DEFAULT_HASH:-sha1}"
 }
 
+# Detect the hash algorithm in use.
+test_detect_ref_format () {
+       echo "${GIT_TEST_DEFAULT_REF_FORMAT:-files}"
+}
+
 # Load common hash metadata and common placeholder object IDs for use with
 # test_oid.
 test_oid_init () {
index 876b99562a323ee351d4afe0fe2afbca34fb46bc..fc93aa57e6116d5602b17a63f41bd4b8746289e2 100644 (file)
@@ -542,6 +542,8 @@ export EDITOR
 
 GIT_DEFAULT_HASH="${GIT_TEST_DEFAULT_HASH:-sha1}"
 export GIT_DEFAULT_HASH
+GIT_DEFAULT_REF_FORMAT="${GIT_TEST_DEFAULT_REF_FORMAT:-files}"
+export GIT_DEFAULT_REF_FORMAT
 GIT_TEST_MERGE_ALGORITHM="${GIT_TEST_MERGE_ALGORITHM:-ort}"
 export GIT_TEST_MERGE_ALGORITHM
 
@@ -1745,7 +1747,14 @@ parisc* | hppa*)
        ;;
 esac
 
-test_set_prereq REFFILES
+case "$GIT_DEFAULT_REF_FORMAT" in
+files)
+       test_set_prereq REFFILES;;
+*)
+       echo 2>&1 "error: unknown ref format $GIT_DEFAULT_REF_FORMAT"
+       exit 1
+       ;;
+esac
 
 ( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1
 test -z "$NO_CURL" && test_set_prereq LIBCURL
@@ -1936,6 +1945,10 @@ test_lazy_prereq SHA1 '
        esac
 '
 
+test_lazy_prereq DEFAULT_REPO_FORMAT '
+       test_have_prereq SHA1,REFFILES
+'
+
 # Ensure that no test accidentally triggers a Git command
 # that runs the actual maintenance scheduler, affecting a user's
 # system permanently.
diff --git a/t/unit-tests/t-ctype.c b/t/unit-tests/t-ctype.c
new file mode 100644 (file)
index 0000000..f315489
--- /dev/null
@@ -0,0 +1,80 @@
+#include "test-lib.h"
+
+static int is_in(const char *s, int ch)
+{
+       /*
+        * We can't find NUL using strchr. Accept it as the first
+        * character in the spec -- there are no empty classes.
+        */
+       if (ch == '\0')
+               return ch == *s;
+       if (*s == '\0')
+               s++;
+       return !!strchr(s, ch);
+}
+
+/* Macro to test a character type */
+#define TEST_CTYPE_FUNC(func, string) \
+static void test_ctype_##func(void) { \
+       for (int i = 0; i < 256; i++) { \
+               if (!check_int(func(i), ==, is_in(string, i))) \
+                       test_msg("       i: 0x%02x", i); \
+       } \
+       if (!check(!func(EOF))) \
+                       test_msg("      i: 0x%02x (EOF)", EOF); \
+}
+
+#define TEST_CHAR_CLASS(class) TEST(test_ctype_##class(), #class " works")
+
+#define DIGIT "0123456789"
+#define LOWER "abcdefghijklmnopqrstuvwxyz"
+#define UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define PUNCT "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
+#define ASCII \
+       "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
+       "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
+       "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" \
+       "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f" \
+       "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" \
+       "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f" \
+       "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f" \
+       "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
+#define CNTRL \
+       "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" \
+       "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" \
+       "\x7f"
+
+TEST_CTYPE_FUNC(isdigit, DIGIT)
+TEST_CTYPE_FUNC(isspace, " \n\r\t")
+TEST_CTYPE_FUNC(isalpha, LOWER UPPER)
+TEST_CTYPE_FUNC(isalnum, LOWER UPPER DIGIT)
+TEST_CTYPE_FUNC(is_glob_special, "*?[\\")
+TEST_CTYPE_FUNC(is_regex_special, "$()*+.?[\\^{|")
+TEST_CTYPE_FUNC(is_pathspec_magic, "!\"#%&',-/:;<=>@_`~")
+TEST_CTYPE_FUNC(isascii, ASCII)
+TEST_CTYPE_FUNC(islower, LOWER)
+TEST_CTYPE_FUNC(isupper, UPPER)
+TEST_CTYPE_FUNC(iscntrl, CNTRL)
+TEST_CTYPE_FUNC(ispunct, PUNCT)
+TEST_CTYPE_FUNC(isxdigit, DIGIT "abcdefABCDEF")
+TEST_CTYPE_FUNC(isprint, LOWER UPPER DIGIT PUNCT " ")
+
+int cmd_main(int argc, const char **argv) {
+       /* Run all character type tests */
+       TEST_CHAR_CLASS(isspace);
+       TEST_CHAR_CLASS(isdigit);
+       TEST_CHAR_CLASS(isalpha);
+       TEST_CHAR_CLASS(isalnum);
+       TEST_CHAR_CLASS(is_glob_special);
+       TEST_CHAR_CLASS(is_regex_special);
+       TEST_CHAR_CLASS(is_pathspec_magic);
+       TEST_CHAR_CLASS(isascii);
+       TEST_CHAR_CLASS(islower);
+       TEST_CHAR_CLASS(isupper);
+       TEST_CHAR_CLASS(iscntrl);
+       TEST_CHAR_CLASS(ispunct);
+       TEST_CHAR_CLASS(isxdigit);
+       TEST_CHAR_CLASS(isprint);
+
+       return test_done();
+}
index bd7899e9bf5550b2b333dbbcf85a5b98c7813ad2..df518ead70c387dd354dd69a5ee4af8a3ab0d560 100644 (file)
@@ -1467,6 +1467,7 @@ int transport_push(struct repository *r,
        if (porcelain && !push_ret)
                puts("Done");
        else if (!quiet && !ret && !transport_refs_pushed(remote_refs))
+               /* stable plumbing output; do not modify or localize */
                fprintf(stderr, "Everything up-to-date\n");
 
 done:
index 1399d452accaa3d0bdadd759c95fa7fd704f0a95..b02a05a74a341157fa8dff7da22936127bebf18e 100644 (file)
 #include "wt-status.h"
 #include "config.h"
 
+void free_worktree(struct worktree *worktree)
+{
+       if (!worktree)
+               return;
+       free(worktree->path);
+       free(worktree->id);
+       free(worktree->head_ref);
+       free(worktree->lock_reason);
+       free(worktree->prune_reason);
+       free(worktree);
+}
+
 void free_worktrees(struct worktree **worktrees)
 {
        int i = 0;
-
-       for (i = 0; worktrees[i]; i++) {
-               free(worktrees[i]->path);
-               free(worktrees[i]->id);
-               free(worktrees[i]->head_ref);
-               free(worktrees[i]->lock_reason);
-               free(worktrees[i]->prune_reason);
-               free(worktrees[i]);
-       }
+       for (i = 0; worktrees[i]; i++)
+               free_worktree(worktrees[i]);
        free (worktrees);
 }
 
@@ -51,7 +56,7 @@ static void add_head_info(struct worktree *wt)
 /**
  * get the main worktree
  */
-static struct worktree *get_main_worktree(void)
+static struct worktree *get_main_worktree(int skip_reading_head)
 {
        struct worktree *worktree = NULL;
        struct strbuf worktree_path = STRBUF_INIT;
@@ -70,11 +75,13 @@ static struct worktree *get_main_worktree(void)
         */
        worktree->is_bare = (is_bare_repository_cfg == 1) ||
                is_bare_repository();
-       add_head_info(worktree);
+       if (!skip_reading_head)
+               add_head_info(worktree);
        return worktree;
 }
 
-static struct worktree *get_linked_worktree(const char *id)
+struct worktree *get_linked_worktree(const char *id,
+                                    int skip_reading_head)
 {
        struct worktree *worktree = NULL;
        struct strbuf path = STRBUF_INIT;
@@ -93,7 +100,8 @@ static struct worktree *get_linked_worktree(const char *id)
        CALLOC_ARRAY(worktree, 1);
        worktree->path = strbuf_detach(&worktree_path, NULL);
        worktree->id = xstrdup(id);
-       add_head_info(worktree);
+       if (!skip_reading_head)
+               add_head_info(worktree);
 
 done:
        strbuf_release(&path);
@@ -118,7 +126,14 @@ static void mark_current_worktree(struct worktree **worktrees)
        free(git_dir);
 }
 
-struct worktree **get_worktrees(void)
+/*
+ * NEEDSWORK: This function exists so that we can look up metadata of a
+ * worktree without trying to access any of its internals like the refdb. It
+ * would be preferable to instead have a corruption-tolerant function for
+ * retrieving worktree metadata that could be used when the worktree is known
+ * to not be in a healthy state, e.g. when creating or repairing it.
+ */
+static struct worktree **get_worktrees_internal(int skip_reading_head)
 {
        struct worktree **list = NULL;
        struct strbuf path = STRBUF_INIT;
@@ -128,7 +143,7 @@ struct worktree **get_worktrees(void)
 
        ALLOC_ARRAY(list, alloc);
 
-       list[counter++] = get_main_worktree();
+       list[counter++] = get_main_worktree(skip_reading_head);
 
        strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
        dir = opendir(path.buf);
@@ -137,7 +152,7 @@ struct worktree **get_worktrees(void)
                while ((d = readdir_skip_dot_and_dotdot(dir)) != NULL) {
                        struct worktree *linked = NULL;
 
-                       if ((linked = get_linked_worktree(d->d_name))) {
+                       if ((linked = get_linked_worktree(d->d_name, skip_reading_head))) {
                                ALLOC_GROW(list, counter + 1, alloc);
                                list[counter++] = linked;
                        }
@@ -151,6 +166,11 @@ struct worktree **get_worktrees(void)
        return list;
 }
 
+struct worktree **get_worktrees(void)
+{
+       return get_worktrees_internal(0);
+}
+
 const char *get_worktree_git_dir(const struct worktree *wt)
 {
        if (!wt)
@@ -591,7 +611,7 @@ static void repair_noop(int iserr UNUSED,
 
 void repair_worktrees(worktree_repair_fn fn, void *cb_data)
 {
-       struct worktree **worktrees = get_worktrees();
+       struct worktree **worktrees = get_worktrees_internal(1);
        struct worktree **wt = worktrees + 1; /* +1 skips main worktree */
 
        if (!fn)
index ce45b66de9e82d4d619b6f74e0e77c8bd5ab3f53..f14784a2ff871c69e5b5e28466439201721407b2 100644 (file)
@@ -57,6 +57,13 @@ struct worktree *find_worktree(struct worktree **list,
                               const char *prefix,
                               const char *arg);
 
+/*
+ * Look up the worktree corresponding to `id`, or NULL of no such worktree
+ * exists.
+ */
+struct worktree *get_linked_worktree(const char *id,
+                                    int skip_reading_head);
+
 /*
  * Return the worktree corresponding to `path`, or NULL if no such worktree
  * exists.
@@ -134,6 +141,11 @@ void repair_worktrees(worktree_repair_fn, void *cb_data);
  */
 void repair_worktree_at_path(const char *, worktree_repair_fn, void *cb_data);
 
+/*
+ * Free up the memory for a worktree.
+ */
+void free_worktree(struct worktree *);
+
 /*
  * Free up the memory for worktree(s)
  */