diff options
119 files changed, 1700 insertions, 1131 deletions
diff --git a/.github/workflows/l10n.yml b/.github/workflows/l10n.yml new file mode 100644 index 0000000000..27f72f0ff3 --- /dev/null +++ b/.github/workflows/l10n.yml @@ -0,0 +1,105 @@ +name: git-l10n + +on: [push, pull_request_target] + +jobs: + git-po-helper: + if: >- + endsWith(github.repository, '/git-po') || + contains(github.head_ref, 'l10n') || + contains(github.ref, 'l10n') + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - name: Setup base and head objects + id: setup-tips + run: | + if test "${{ github.event_name }}" = "pull_request_target" + then + base=${{ github.event.pull_request.base.sha }} + head=${{ github.event.pull_request.head.sha }} + else + base=${{ github.event.before }} + head=${{ github.event.after }} + fi + echo "::set-output name=base::$base" + echo "::set-output name=head::$head" + - name: Run partial clone + run: | + git -c init.defaultBranch=master init --bare . + git remote add \ + --mirror=fetch \ + origin \ + https://github.com/${{ github.repository }} + # Fetch tips that may be unreachable from github.ref: + # - For a forced push, "$base" may be unreachable. + # - For a "pull_request_target" event, "$head" may be unreachable. + args= + for commit in \ + ${{ steps.setup-tips.outputs.base }} \ + ${{ steps.setup-tips.outputs.head }} + do + case $commit in + *[^0]*) + args="$args $commit" + ;; + *) + # Should not fetch ZERO-OID. + ;; + esac + done + git -c protocol.version=2 fetch \ + --progress \ + --no-tags \ + --no-write-fetch-head \ + --filter=blob:none \ + origin \ + ${{ github.ref }} \ + $args + - uses: actions/setup-go@v2 + with: + go-version: '>=1.16' + - name: Install git-po-helper + run: go install github.com/git-l10n/git-po-helper@main + - name: Install other dependencies + run: | + sudo apt-get update -q && + sudo apt-get install -q -y gettext + - name: Run git-po-helper + id: check-commits + run: | + exit_code=0 + git-po-helper check-commits \ + --github-action-event="${{ github.event_name }}" -- \ + ${{ steps.setup-tips.outputs.base }}..${{ steps.setup-tips.outputs.head }} \ + >git-po-helper.out 2>&1 || exit_code=$? + if test $exit_code -ne 0 || grep -q WARNING git-po-helper.out + then + # Remove ANSI colors which are proper for console logs but not + # proper for PR comment. + echo "COMMENT_BODY<<EOF" >>$GITHUB_ENV + perl -pe 's/\e\[[0-9;]*m//g; s/\bEOF$//g' git-po-helper.out >>$GITHUB_ENV + echo "EOF" >>$GITHUB_ENV + fi + cat git-po-helper.out + exit $exit_code + - name: Create comment in pull request for report + uses: mshick/add-pr-comment@v1 + if: >- + always() && + github.event_name == 'pull_request_target' && + env.COMMENT_BODY != '' + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + repo-token-user-login: 'github-actions[bot]' + message: > + ${{ steps.check-commits.outcome == 'failure' && 'Errors and warnings' || 'Warnings' }} + found by [git-po-helper](https://github.com/git-l10n/git-po-helper#readme) in workflow + [#${{ github.run_number }}](${{ env.GITHUB_SERVER_URL }}/${{ github.repository }}/actions/runs/${{ github.run_id }}): + + ``` + + ${{ env.COMMENT_BODY }} + + ``` diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b053b01c66..4728168478 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -232,6 +232,9 @@ jobs: - jobname: linux-gcc-default cc: gcc pool: ubuntu-latest + - jobname: linux-leaks + cc: gcc + pool: ubuntu-latest env: CC: ${{matrix.vector.cc}} jobname: ${{matrix.vector.jobname}} diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt index 015cf24631..b20bc8e914 100644 --- a/Documentation/MyFirstContribution.txt +++ b/Documentation/MyFirstContribution.txt @@ -1029,22 +1029,42 @@ kidding - be patient!) [[v2-git-send-email]] === Sending v2 -Skip ahead to <<reviewing,Responding to Reviews>> for information on how to -handle comments from reviewers. Continue this section when your topic branch is -shaped the way you want it to look for your patchset v2. +This section will focus on how to send a v2 of your patchset. To learn what +should go into v2, skip ahead to <<reviewing,Responding to Reviews>> for +information on how to handle comments from reviewers. + +We'll reuse our `psuh` topic branch for v2. Before we make any changes, we'll +mark the tip of our v1 branch for easy reference: -When you're ready with the next iteration of your patch, the process is fairly -similar. +---- +$ git checkout psuh +$ git branch psuh-v1 +---- -First, generate your v2 patches again: +Refine your patch series by using `git rebase -i` to adjust commits based upon +reviewer comments. Once the patch series is ready for submission, generate your +patches again, but with some new flags: ---- -$ git format-patch -v2 --cover-letter -o psuh/ master..psuh +$ git format-patch -v2 --cover-letter -o psuh/ --range-diff master..psuh-v1 master.. ---- -This will add your v2 patches, all named like `v2-000n-my-commit-subject.patch`, -to the `psuh/` directory. You may notice that they are sitting alongside the v1 -patches; that's fine, but be careful when you are ready to send them. +The `--range-diff master..psuh-v1` parameter tells `format-patch` to include a +range-diff between `psuh-v1` and `psuh` in the cover letter (see +linkgit:git-range-diff[1]). This helps tell reviewers about the differences +between your v1 and v2 patches. + +The `-v2` parameter tells `format-patch` to output your patches +as version "2". For instance, you may notice that your v2 patches are +all named like `v2-000n-my-commit-subject.patch`. `-v2` will also format +your patches by prefixing them with "[PATCH v2]" instead of "[PATCH]", +and your range-diff will be prefaced with "Range-diff against v1". + +Afer you run this command, `format-patch` will output the patches to the `psuh/` +directory, alongside the v1 patches. Using a single directory makes it easy to +refer to the old v1 patches while proofreading the v2 patches, but you will need +to be careful to send out only the v2 patches. We will use a pattern like +"psuh/v2-*.patch" (not "psuh/*.patch", which would match v1 and v2 patches). Edit your cover letter again. Now is a good time to mention what's different between your last version and now, if it's something significant. You do not @@ -1082,7 +1102,7 @@ to the command: ---- $ git send-email --to=target@example.com --in-reply-to="<foo.12345.author@example.com>" - psuh/v2* + psuh/v2-*.patch ---- [[single-patch]] diff --git a/Documentation/RelNotes/2.34.0.txt b/Documentation/RelNotes/2.34.0.txt index fa8ecedb9e..9d583ead04 100644 --- a/Documentation/RelNotes/2.34.0.txt +++ b/Documentation/RelNotes/2.34.0.txt @@ -56,6 +56,12 @@ UI, Workflows & Features * The error in "git help no-such-git-command" is handled better. + * The unicode character width table (used for output alignment) has + been updated. + + * The ref iteration code used to optionally allow dangling refs to be + shown, which has been tightened up. + Performance, Internal Implementation, Development Support etc. @@ -130,6 +136,11 @@ Performance, Internal Implementation, Development Support etc. * An oddball OPTION_ARGUMENT feature has been removed from the parse-options API. + * The mergesort implementation used to sort linked list has been + optimized. + + * Remove external declaration of functions that no longer exist. + Fixes since v2.33 ----------------- @@ -267,6 +278,36 @@ Fixes since v2.33 * Correct nr and alloc members of strvec struct to be of type size_t. (merge 8d133a4653 jk/strvec-typefix later to maint). + * "git stash", where the tentative change involves changing a + directory to a file (or vice versa), was confused, which has been + corrected. + (merge bee8691f19 en/stash-df-fix later to maint). + + * "git clone" from a repository whose HEAD is unborn into a bare + repository didn't follow the branch name the other side used, which + is corrected. + (merge 6b58df54cf jk/clone-unborn-head-in-bare later to maint). + + * "git cvsserver" had a long-standing bug in its authentication code, + which has finally been corrected (it is unclear and is a separate + question if anybody is seriously using it, though). + (merge 4b81f690f6 cb/cvsserver later to maint). + + * "git difftool --dir-diff" mishandled symbolic links. + (merge 5bafb3576a da/difftool-dir-diff-symlink-fix later to maint). + + * Sensitive data in the HTTP trace were supposed to be redacted, but + we failed to do so in HTTP/2 requests. + (merge b66c77a64e jk/http-redact-fix later to maint). + + * "make clean" has been updated to remove leftover .depend/ + directories, even when it is not told to use them to compute header + dependencies. + (merge f0a74bcb03 ab/make-clean-depend-dirs later to maint). + + * Protocol v0 clients can get stuck parsing a malformed feature line. + (merge 44d2aec6e8 ah/connect-parse-feature-v0-fix later to maint). + * Other code cleanup, docfix, build fix, etc. (merge 1d9c8daef8 ab/bundle-doc later to maint). (merge 81483fe613 en/merge-strategy-docs later to maint). @@ -303,3 +344,10 @@ Fixes since v2.33 (merge 66c0c44df6 cb/plug-leaks-in-alloca-emu-users later to maint). (merge afb32e8101 kz/revindex-comment-fix later to maint). (merge ae578de926 po/git-config-doc-mentions-help-c later to maint). + (merge 187fc8b8b6 cb/unicode-14 later to maint). + (merge 3584cff71c en/typofixes later to maint). + (merge f188160be9 ab/bundle-remove-verbose-option later to maint). + (merge 8c6b4332b4 rs/close-pack-leakfix later to maint). + (merge 51b04c05b7 bs/difftool-msg-tweak later to maint). + (merge dd20e4a6db ab/make-compdb-fix later to maint). + (merge 6ffb990dc4 os/status-docfix later to maint). diff --git a/Documentation/config/pack.txt b/Documentation/config/pack.txt index 763f7af7c4..ad7f73a1ea 100644 --- a/Documentation/config/pack.txt +++ b/Documentation/config/pack.txt @@ -159,6 +159,10 @@ pack.writeBitmapHashCache:: between an older, bitmapped pack and objects that have been pushed since the last gc). The downside is that it consumes 4 bytes per object of disk space. Defaults to true. ++ +When writing a multi-pack reachability bitmap, no new namehashes are +computed; instead, any namehashes stored in an existing bitmap are +permuted into their appropriate location when writing a new bitmap. pack.writeReverseIndex:: When true, git will write a corresponding .rev file (see: diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt index f2e4a47ebe..4dc57ed254 100644 --- a/Documentation/git-cvsserver.txt +++ b/Documentation/git-cvsserver.txt @@ -99,7 +99,7 @@ looks like ------ -Only anonymous access is provided by pserve by default. To commit you +Only anonymous access is provided by pserver by default. To commit you will have to create pserver accounts, simply add a gitcvs.authdb setting in the config file of the repositories you want the cvsserver to allow writes to, for example: @@ -114,21 +114,20 @@ The format of these files is username followed by the encrypted password, for example: ------ - myuser:$1Oyx5r9mdGZ2 - myuser:$1$BA)@$vbnMJMDym7tA32AamXrm./ + myuser:sqkNi8zPf01HI + myuser:$1$9K7FzU28$VfF6EoPYCJEYcVQwATgOP/ + myuser:$5$.NqmNH1vwfzGpV8B$znZIcumu1tNLATgV2l6e1/mY8RzhUDHMOaVOeL1cxV3 ------ You can use the 'htpasswd' facility that comes with Apache to make these -files, but Apache's MD5 crypt method differs from the one used by most C -library's crypt() function, so don't use the -m option. +files, but only with the -d option (or -B if your system suports it). -Alternatively you can produce the password with perl's crypt() operator: ------ - perl -e 'my ($user, $pass) = @ARGV; printf "%s:%s\n", $user, crypt($user, $pass)' $USER password ------ +Preferably use the system specific utility that manages password hash +creation in your platform (e.g. mkpasswd in Linux, encrypt in OpenBSD or +pwhash in NetBSD) and paste it in the right location. Then provide your password via the pserver method, for example: ------ - cvs -d:pserver:someuser:somepassword <at> server/path/repo.git co <HEAD_name> + cvs -d:pserver:someuser:somepassword@server:/path/repo.git co <HEAD_name> ------ No special setup is needed for SSH access, other than having Git tools in the PATH. If you have clients that do not accept the CVS_SERVER @@ -138,7 +137,7 @@ Note: Newer CVS versions (>= 1.12.11) also support specifying CVS_SERVER directly in CVSROOT like ------ -cvs -d ":ext;CVS_SERVER=git cvsserver:user@server/path/repo.git" co <HEAD_name> + cvs -d ":ext;CVS_SERVER=git cvsserver:user@server/path/repo.git" co <HEAD_name> ------ This has the advantage that it will be saved in your 'CVS/Root' files and you don't need to worry about always setting the correct environment @@ -186,8 +185,8 @@ allowing access over SSH. + -- ------ - export CVSROOT=:ext:user@server:/var/git/project.git - export CVS_SERVER="git cvsserver" + export CVSROOT=:ext:user@server:/var/git/project.git + export CVS_SERVER="git cvsserver" ------ -- 4. For SSH clients that will make commits, make sure their server-side @@ -203,7 +202,7 @@ allowing access over SSH. `project-master` directory: + ------ - cvs co -d project-master master + cvs co -d project-master master ------ [[dbbackend]] diff --git a/Documentation/git-multi-pack-index.txt b/Documentation/git-multi-pack-index.txt index a9df3dbd32..3b0b55cd75 100644 --- a/Documentation/git-multi-pack-index.txt +++ b/Documentation/git-multi-pack-index.txt @@ -9,8 +9,7 @@ git-multi-pack-index - Write and verify multi-pack-indexes SYNOPSIS -------- [verse] -'git multi-pack-index' [--object-dir=<dir>] [--[no-]progress] - [--preferred-pack=<pack>] [--[no-]bitmap] <subcommand> +'git multi-pack-index' [--object-dir=<dir>] [--[no-]bitmap] <sub-command> DESCRIPTION ----------- @@ -28,7 +27,8 @@ OPTIONS --[no-]progress:: Turn progress on/off explicitly. If neither is specified, progress is - shown if standard error is connected to a terminal. + shown if standard error is connected to a terminal. Supported by + sub-commands `write`, `verify`, `expire`, and `repack. The following subcommands are available: diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt index 506345cb0e..c116dbf4bb 100644 --- a/Documentation/git-rebase.txt +++ b/Documentation/git-rebase.txt @@ -446,7 +446,8 @@ When --fork-point is active, 'fork_point' will be used instead of ends up being empty, the <upstream> will be used as a fallback. + If <upstream> is given on the command line, then the default is -`--no-fork-point`, otherwise the default is `--fork-point`. +`--no-fork-point`, otherwise the default is `--fork-point`. See also +`rebase.forkpoint` in linkgit:git-config[1]. + If your branch was based on <upstream> but <upstream> was rewound and your branch contains commits which were dropped, this option can be used diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt index 83f38e3198..c33a3d8d53 100644 --- a/Documentation/git-status.txt +++ b/Documentation/git-status.txt @@ -363,7 +363,7 @@ Field Meaning Unmerged entries have the following format; the first character is a "u" to distinguish from ordinary changed entries. - u <xy> <sub> <m1> <m2> <m3> <mW> <h1> <h2> <h3> <path> + u <XY> <sub> <m1> <m2> <m3> <mW> <h1> <h2> <h3> <path> .... Field Meaning diff --git a/Documentation/git.txt b/Documentation/git.txt index abace9eac2..d63c65e67d 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -867,15 +867,16 @@ for full details. end user, to be recorded in the body of the reflog. `GIT_REF_PARANOIA`:: - If set to `1`, include broken or badly named refs when iterating - over lists of refs. In a normal, non-corrupted repository, this - does nothing. However, enabling it may help git to detect and - abort some operations in the presence of broken refs. Git sets - this variable automatically when performing destructive - operations like linkgit:git-prune[1]. You should not need to set - it yourself unless you want to be paranoid about making sure - an operation has touched every ref (e.g., because you are - cloning a repository to make a backup). + If set to `0`, ignore broken or badly named refs when iterating + over lists of refs. Normally Git will try to include any such + refs, which may cause some operations to fail. This is usually + preferable, as potentially destructive operations (e.g., + linkgit:git-prune[1]) are better off aborting rather than + ignoring broken refs (and thus considering the history they + point to as not worth saving). The default value is `1` (i.e., + be paranoid about detecting and aborting all operations). You + should not normally need to set this to `0`, but it may be + useful when trying to salvage data from a corrupted repository. `GIT_ALLOW_PROTOCOL`:: If set to a colon-separated list of protocols, behave as if diff --git a/Documentation/technical/multi-pack-index.txt b/Documentation/technical/multi-pack-index.txt index 1a73c3ee20..86f40f2490 100644 --- a/Documentation/technical/multi-pack-index.txt +++ b/Documentation/technical/multi-pack-index.txt @@ -36,7 +36,9 @@ Design Details directory of an alternate. It refers only to packfiles in that same directory. -- The core.multiPackIndex config setting must be on to consume MIDX files. +- The core.multiPackIndex config setting must be on (which is the + default) to consume MIDX files. Setting it to `false` prevents + Git from reading a MIDX file, even if one exists. - The file format includes parameters for the object ID hash function, so a future change of hash algorithm does not require diff --git a/Documentation/technical/protocol-v2.txt b/Documentation/technical/protocol-v2.txt index 59b86fcf97..21e8258ccf 100644 --- a/Documentation/technical/protocol-v2.txt +++ b/Documentation/technical/protocol-v2.txt @@ -199,7 +199,11 @@ ls-refs takes in the following arguments: Show peeled tags. ref-prefix <prefix> When specified, only references having a prefix matching one of - the provided prefixes are displayed. + the provided prefixes are displayed. Multiple instances may be + given, in which case references matching any prefix will be + shown. Note that this is purely for optimization; a server MAY + show refs not matching the prefix if it chooses, and clients + should filter the result themselves. If the 'unborn' feature is advertised the following argument can be included in the client's request. @@ -1214,6 +1214,9 @@ PTHREAD_CFLAGS = SPARSE_FLAGS ?= SP_EXTRA_FLAGS = -Wno-universal-initializer +# For informing GIT-BUILD-OPTIONS of the SANITIZE=leak target +SANITIZE_LEAK = + # For the 'coccicheck' target; setting SPATCH_BATCH_SIZE higher will # usually result in less CPU usage at the cost of higher peak memory. # Setting it to 0 will feed all files in a single spatch invocation. @@ -1258,6 +1261,7 @@ BASIC_CFLAGS += -DSHA1DC_FORCE_ALIGNED_ACCESS endif ifneq ($(filter leak,$(SANITIZERS)),) BASIC_CFLAGS += -DSUPPRESS_ANNOTATED_LEAKS +SANITIZE_LEAK = YesCompiledWithIt endif ifneq ($(filter address,$(SANITIZERS)),) NO_REGEX = NeededForASAN @@ -1278,6 +1282,7 @@ endif ifeq ($(COMPUTE_HEADER_DEPENDENCIES),auto) dep_check = $(shell $(CC) $(ALL_CFLAGS) \ + -Wno-pedantic \ -c -MF /dev/null -MQ /dev/null -MMD -MP \ -x c /dev/null -o /dev/null 2>&1; \ echo $$?) @@ -1303,6 +1308,7 @@ endif ifeq ($(GENERATE_COMPILATION_DATABASE),yes) compdb_check = $(shell $(CC) $(ALL_CFLAGS) \ + -Wno-pedantic \ -c -MJ /dev/null \ -x c /dev/null -o /dev/null 2>&1; \ echo $$?) @@ -2459,7 +2465,6 @@ dep_args = -MF $(dep_file) -MQ $@ -MMD -MP endif ifneq ($(COMPUTE_HEADER_DEPENDENCIES),yes) -dep_dirs = missing_dep_dirs = dep_args = endif @@ -2796,6 +2801,7 @@ GIT-BUILD-OPTIONS: FORCE @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+ @echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+ @echo DC_SHA1=\''$(subst ','\'',$(subst ','\'',$(DC_SHA1)))'\' >>$@+ + @echo SANITIZE_LEAK=\''$(subst ','\'',$(subst ','\'',$(SANITIZE_LEAK)))'\' >>$@+ @echo X=\'$(X)\' >>$@+ ifdef TEST_OUTPUT_DIRECTORY @echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+ @@ -225,7 +225,6 @@ int cmd_submodule__helper(int argc, const char **argv, const char *prefix); int cmd_switch(int argc, const char **argv, const char *prefix); int cmd_symbolic_ref(int argc, const char **argv, const char *prefix); int cmd_tag(int argc, const char **argv, const char *prefix); -int cmd_tar_tree(int argc, const char **argv, const char *prefix); int cmd_unpack_file(int argc, const char **argv, const char *prefix); int cmd_unpack_objects(int argc, const char **argv, const char *prefix); int cmd_update_index(int argc, const char **argv, const char *prefix); diff --git a/builtin/branch.c b/builtin/branch.c index 03c7b7253a..0b7ed82654 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -427,7 +427,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin memset(&array, 0, sizeof(array)); - filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN); + filter_refs(&array, filter, filter->kind); if (filter->verbose) maxwidth = calc_maxwidth(&array, strlen(remote_prefix)); diff --git a/builtin/bundle.c b/builtin/bundle.c index 91975def2d..5a85d7cd0f 100644 --- a/builtin/bundle.c +++ b/builtin/bundle.c @@ -39,8 +39,6 @@ static const char * const builtin_bundle_unbundle_usage[] = { NULL }; -static int verbose; - static int parse_options_cmd_bundle(int argc, const char **argv, const char* prefix, @@ -197,7 +195,6 @@ cleanup: int cmd_bundle(int argc, const char **argv, const char *prefix) { struct option options[] = { - OPT__VERBOSE(&verbose, N_("be verbose; must be placed before a subcommand")), OPT_END() }; int result; diff --git a/builtin/clone.c b/builtin/clone.c index ff1d3d447a..c9d4ca2664 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -1229,6 +1229,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix) our_head_points_at = remote_head_points_at; } else { + const char *branch; + char *ref; + if (option_branch) die(_("Remote branch %s not found in upstream %s"), option_branch, remote_name); @@ -1239,24 +1242,22 @@ int cmd_clone(int argc, const char **argv, const char *prefix) remote_head_points_at = NULL; remote_head = NULL; option_no_checkout = 1; - if (!option_bare) { - const char *branch; - char *ref; - - if (transport_ls_refs_options.unborn_head_target && - skip_prefix(transport_ls_refs_options.unborn_head_target, - "refs/heads/", &branch)) { - ref = transport_ls_refs_options.unborn_head_target; - transport_ls_refs_options.unborn_head_target = NULL; - create_symref("HEAD", ref, reflog_msg.buf); - } else { - branch = git_default_branch_name(0); - ref = xstrfmt("refs/heads/%s", branch); - } - install_branch_config(0, branch, remote_name, ref); - free(ref); + if (transport_ls_refs_options.unborn_head_target && + skip_prefix(transport_ls_refs_options.unborn_head_target, + "refs/heads/", &branch)) { + ref = transport_ls_refs_options.unborn_head_target; + transport_ls_refs_options.unborn_head_target = NULL; + create_symref("HEAD", ref, reflog_msg.buf); + } else { + branch = git_default_branch_name(0); + ref = xstrfmt("refs/heads/%s", branch); } + + if (!option_bare) + install_branch_config(0, branch, remote_name, ref); + + free(ref); } write_refspec_config(src_ref_prefix, our_head_points_at, diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c index 0386f5c775..3c3de3a156 100644 --- a/builtin/commit-graph.c +++ b/builtin/commit-graph.c @@ -50,8 +50,6 @@ static struct option common_opts[] = { OPT_STRING(0, "object-dir", &opts.obj_dir, N_("dir"), N_("the object directory to store the graph")), - OPT_BOOL(0, "progress", &opts.progress, - N_("force progress reporting")), OPT_END() }; @@ -73,6 +71,8 @@ static int graph_verify(int argc, const char **argv) static struct option builtin_commit_graph_verify_options[] = { OPT_BOOL(0, "shallow", &opts.shallow, N_("if the commit-graph is split, only verify the tip file")), + OPT_BOOL(0, "progress", &opts.progress, + N_("force progress reporting")), OPT_END(), }; struct option *options = add_common_options(builtin_commit_graph_verify_options); @@ -224,6 +224,8 @@ static int graph_write(int argc, const char **argv) OPT_CALLBACK_F(0, "max-new-filters", &write_opts.max_new_filters, NULL, N_("maximum number of changed-path Bloom filters to compute"), 0, write_option_max_new_filters), + OPT_BOOL(0, "progress", &opts.progress, + N_("force progress reporting")), OPT_END(), }; struct option *options = add_common_options(builtin_commit_graph_write_options); diff --git a/builtin/difftool.c b/builtin/difftool.c index bb9fe7245a..210da03908 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -405,9 +405,9 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, const char *src_path, *dst_path; if (starts_with(info.buf, "::")) - die(N_("combined diff formats('-c' and '--cc') are " + die(N_("combined diff formats ('-c' and '--cc') are " "not supported in\n" - "directory diff mode('-d' and '--dir-diff').")); + "directory diff mode ('-d' and '--dir-diff').")); if (parse_index_info(info.buf, &lmode, &rmode, &loid, &roid, &status)) @@ -557,11 +557,13 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, if (*entry->left) { add_path(&ldir, ldir_len, entry->path); ensure_leading_directories(ldir.buf); + unlink(ldir.buf); write_file(ldir.buf, "%s", entry->left); } if (*entry->right) { add_path(&rdir, rdir_len, entry->path); ensure_leading_directories(rdir.buf); + unlink(rdir.buf); write_file(rdir.buf, "%s", entry->right); } } @@ -706,7 +708,7 @@ int cmd_difftool(int argc, const char **argv, const char *prefix) "`--tool`")), OPT_BOOL(0, "trust-exit-code", &trust_exit_code, N_("make 'git-difftool' exit when an invoked diff " - "tool returns a non - zero exit code")), + "tool returns a non-zero exit code")), OPT_STRING('x', "extcmd", &extcmd, N_("command"), N_("specify a custom command for viewing diffs")), OPT_BOOL(0, "no-index", &no_index, N_("passed to `diff`")), diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 89cb6307d4..642b4b888f 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -77,7 +77,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix) filter.name_patterns = argv; filter.match_as_path = 1; - filter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN); + filter_refs(&array, &filter, FILTER_REFS_ALL); ref_array_sort(sorting, &array); if (!maxcount || array.nr < maxcount) diff --git a/builtin/grep.c b/builtin/grep.c index 51278b01fa..8af5249a7b 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -433,17 +433,14 @@ static int grep_submodule(struct grep_opt *opt, { struct repository *subrepo; struct repository *superproject = opt->repo; - const struct submodule *sub; struct grep_opt subopt; int hit = 0; - sub = submodule_from_path(superproject, null_oid(), path); - if (!is_submodule_active(superproject, path)) return 0; subrepo = xmalloc(sizeof(*subrepo)); - if (repo_submodule_init(subrepo, superproject, sub)) { + if (repo_submodule_init(subrepo, superproject, path, null_oid())) { free(subrepo); return 0; } diff --git a/builtin/ls-files.c b/builtin/ls-files.c index 29a26ad8ae..a2000ed6bf 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -209,10 +209,8 @@ static void show_submodule(struct repository *superproject, struct dir_struct *dir, const char *path) { struct repository subrepo; - const struct submodule *sub = submodule_from_path(superproject, - null_oid(), path); - if (repo_submodule_init(&subrepo, superproject, sub)) + if (repo_submodule_init(&subrepo, superproject, path, null_oid())) return; if (repo_read_index(&subrepo) < 0) @@ -614,7 +612,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) struct option builtin_ls_files_options[] = { /* Think twice before adding "--nul" synonym to this */ OPT_SET_INT('z', NULL, &line_terminator, - N_("paths are separated with NUL character"), '\0'), + N_("separate paths with the NUL character"), '\0'), OPT_BOOL('t', NULL, &show_tag, N_("identify the file status with tags")), OPT_BOOL('v', NULL, &show_valid_bit, @@ -651,7 +649,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix) N_("skip files matching pattern"), PARSE_OPT_NONEG, option_parse_exclude), OPT_CALLBACK_F('X', "exclude-from", &dir, N_("file"), - N_("exclude patterns are read from <file>"), + N_("read exclude patterns from <file>"), PARSE_OPT_NONEG, option_parse_exclude_from), OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, N_("file"), N_("read additional per-directory exclude patterns in <file>")), diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c index 66de6efd41..6426bbdace 100644 --- a/builtin/multi-pack-index.c +++ b/builtin/multi-pack-index.c @@ -52,7 +52,6 @@ static struct opts_multi_pack_index { static struct option common_opts[] = { OPT_FILENAME(0, "object-dir", &opts.object_dir, N_("object directory containing set of packfile and pack-index pairs")), - OPT_BIT(0, "progress", &opts.flags, N_("force progress reporting"), MIDX_PROGRESS), OPT_END(), }; @@ -61,6 +60,23 @@ static struct option *add_common_options(struct option *prev) return parse_options_concat(common_opts, prev); } +static int git_multi_pack_index_write_config(const char *var, const char *value, + void *cb) +{ + if (!strcmp(var, "pack.writebitmaphashcache")) { + if (git_config_bool(var, value)) + opts.flags |= MIDX_WRITE_BITMAP_HASH_CACHE; + else + opts.flags &= ~MIDX_WRITE_BITMAP_HASH_CACHE; + } + + /* + * We should never make a fall-back call to 'git_default_config', since + * this was already called in 'cmd_multi_pack_index()'. + */ + return 0; +} + static int cmd_multi_pack_index_write(int argc, const char **argv) { struct option *options; @@ -70,13 +86,21 @@ static int cmd_multi_pack_index_write(int argc, const char **argv) N_("pack for reuse when computing a multi-pack bitmap")), OPT_BIT(0, "bitmap", &opts.flags, N_("write multi-pack bitmap"), MIDX_WRITE_BITMAP | MIDX_WRITE_REV_INDEX), + OPT_BIT(0, "progress", &opts.flags, + N_("force progress reporting"), MIDX_PROGRESS), OPT_END(), }; + opts.flags |= MIDX_WRITE_BITMAP_HASH_CACHE; + + git_config(git_multi_pack_index_write_config, NULL); + options = add_common_options(builtin_multi_pack_index_write_options); trace2_cmd_mode(argv[0]); + if (isatty(2)) + opts.flags |= MIDX_PROGRESS; argc = parse_options(argc, argv, NULL, options, builtin_multi_pack_index_write_usage, PARSE_OPT_KEEP_UNKNOWN); @@ -92,10 +116,18 @@ static int cmd_multi_pack_index_write(int argc, const char **argv) static int cmd_multi_pack_index_verify(int argc, const char **argv) { - struct option *options = common_opts; + struct option *options; + static struct option builtin_multi_pack_index_verify_options[] = { + OPT_BIT(0, "progress", &opts.flags, + N_("force progress reporting"), MIDX_PROGRESS), + OPT_END(), + }; + options = add_common_options(builtin_multi_pack_index_verify_options); trace2_cmd_mode(argv[0]); + if (isatty(2)) + opts.flags |= MIDX_PROGRESS; argc = parse_options(argc, argv, NULL, options, builtin_multi_pack_index_verify_usage, PARSE_OPT_KEEP_UNKNOWN); @@ -108,10 +140,18 @@ static int cmd_multi_pack_index_verify(int argc, const char **argv) static int cmd_multi_pack_index_expire(int argc, const char **argv) { - struct option *options = common_opts; + struct option *options; + static struct option builtin_multi_pack_index_expire_options[] = { + OPT_BIT(0, "progress", &opts.flags, + N_("force progress reporting"), MIDX_PROGRESS), + OPT_END(), + }; + options = add_common_options(builtin_multi_pack_index_expire_options); trace2_cmd_mode(argv[0]); + if (isatty(2)) + opts.flags |= MIDX_PROGRESS; argc = parse_options(argc, argv, NULL, options, builtin_multi_pack_index_expire_usage, PARSE_OPT_KEEP_UNKNOWN); @@ -128,6 +168,8 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv) static struct option builtin_multi_pack_index_repack_options[] = { OPT_MAGNITUDE(0, "batch-size", &opts.batch_size, N_("during repack, collect pack-files of smaller size into a batch that is larger than this size")), + OPT_BIT(0, "progress", &opts.flags, + N_("force progress reporting"), MIDX_PROGRESS), OPT_END(), }; @@ -135,6 +177,8 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv) trace2_cmd_mode(argv[0]); + if (isatty(2)) + opts.flags |= MIDX_PROGRESS; argc = parse_options(argc, argv, NULL, options, builtin_multi_pack_index_repack_usage, @@ -156,8 +200,6 @@ int cmd_multi_pack_index(int argc, const char **argv, git_config(git_default_config, NULL); - if (isatty(2)) - opts.flags |= MIDX_PROGRESS; argc = parse_options(argc, argv, prefix, builtin_multi_pack_index_options, builtin_multi_pack_index_usage, diff --git a/builtin/prune.c b/builtin/prune.c index 02c6ab7cba..485c9a3c56 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -143,7 +143,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix) expire = TIME_MAX; save_commit_buffer = 0; read_replace_refs = 0; - ref_paranoia = 1; repo_init_revisions(the_repository, &revs, prefix); argc = parse_options(argc, argv, prefix, options, prune_usage, 0); diff --git a/builtin/rebase.c b/builtin/rebase.c index 6c2463037f..8c6393f6d7 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -765,17 +765,6 @@ static int finish_rebase(struct rebase_options *opts) return ret; } -static struct commit *peel_committish(const char *name) -{ - struct object *obj; - struct object_id oid; - - if (get_oid(name, &oid)) - return NULL; - obj = parse_object(the_repository, &oid); - return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT); -} - static void add_var(struct strbuf *buf, const char *name, const char *value) { if (!value) @@ -1580,7 +1569,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) die(_("could not move back to %s"), oid_to_hex(&options.orig_head)); remove_branch_state(the_repository, 0); - ret = !!finish_rebase(&options); + ret = finish_rebase(&options); goto cleanup; } case ACTION_QUIT: { @@ -1589,11 +1578,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) struct replay_opts replay = REPLAY_OPTS_INIT; replay.action = REPLAY_INTERACTIVE_REBASE; - ret = !!sequencer_remove_state(&replay); + ret = sequencer_remove_state(&replay); } else { strbuf_reset(&buf); strbuf_addstr(&buf, options.state_dir); - ret = !!remove_dir_recursively(&buf, 0); + ret = remove_dir_recursively(&buf, 0); if (ret) error(_("could not remove '%s'"), options.state_dir); @@ -1851,7 +1840,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (!strcmp(options.upstream_name, "-")) options.upstream_name = "@{-1}"; } - options.upstream = peel_committish(options.upstream_name); + options.upstream = + lookup_commit_reference_by_name(options.upstream_name); if (!options.upstream) die(_("invalid upstream '%s'"), options.upstream_name); options.upstream_arg = options.upstream_name; @@ -1894,7 +1884,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.onto = lookup_commit_or_die(&merge_base, options.onto_name); } else { - options.onto = peel_committish(options.onto_name); + options.onto = + lookup_commit_reference_by_name(options.onto_name); if (!options.onto) die(_("Does not point to a valid commit '%s'"), options.onto_name); @@ -1919,13 +1910,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) die_if_checked_out(buf.buf, 1); options.head_name = xstrdup(buf.buf); /* If not is it a valid ref (branch or commit)? */ - } else if (!get_oid(branch_name, &options.orig_head) && - lookup_commit_reference(the_repository, - &options.orig_head)) + } else { + struct commit *commit = + lookup_commit_reference_by_name(branch_name); + if (!commit) + die(_("no such branch/commit '%s'"), + branch_name); + oidcpy(&options.orig_head, &commit->object.oid); options.head_name = NULL; - else - die(_("no such branch/commit '%s'"), - branch_name); + } } else if (argc == 0) { /* Do not need to switch branches, we are already on it. */ options.head_name = @@ -1965,7 +1958,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) if (require_clean_work_tree(the_repository, "rebase", _("Please commit or stash them."), 1, 1)) { - ret = 1; + ret = -1; goto cleanup; } @@ -2000,7 +1993,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) RESET_HEAD_RUN_POST_CHECKOUT_HOOK, NULL, buf.buf, DEFAULT_REFLOG_ACTION) < 0) { - ret = !!error(_("could not switch to " + ret = error(_("could not switch to " "%s"), options.switch_to); goto cleanup; @@ -2015,7 +2008,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) else printf(_("Current branch %s is up to date.\n"), branch_name); - ret = !!finish_rebase(&options); + ret = finish_rebase(&options); goto cleanup; } else if (!(options.flags & REBASE_NO_QUIET)) ; /* be quiet */ @@ -2093,7 +2086,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) RESET_HEAD_REFS_ONLY, "HEAD", msg.buf, DEFAULT_REFLOG_ACTION); strbuf_release(&msg); - ret = !!finish_rebase(&options); + ret = finish_rebase(&options); goto cleanup; } @@ -2107,7 +2100,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix) options.revisions = revisions.buf; run_rebase: - ret = !!run_specific_rebase(&options, action); + ret = run_specific_rebase(&options, action); cleanup: strbuf_release(&buf); @@ -2118,5 +2111,5 @@ cleanup: free(options.strategy); strbuf_release(&options.git_format_patch_opt); free(squash_onto_name); - return ret; + return !!ret; } diff --git a/builtin/reflog.c b/builtin/reflog.c index 09541d1c80..bd4c669918 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -629,8 +629,9 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) free_worktrees(worktrees); for (i = 0; i < collected.nr; i++) { struct collected_reflog *e = collected.e[i]; + set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog); - status |= reflog_expire(e->reflog, &e->oid, flags, + status |= reflog_expire(e->reflog, flags, reflog_expiry_prepare, should_expire_reflog_ent, reflog_expiry_cleanup, @@ -642,13 +643,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) for (; i < argc; i++) { char *ref; - struct object_id oid; - if (!dwim_log(argv[i], strlen(argv[i]), &oid, &ref)) { + if (!dwim_log(argv[i], strlen(argv[i]), NULL, &ref)) { status |= error(_("%s points nowhere!"), argv[i]); continue; } set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref); - status |= reflog_expire(ref, &oid, flags, + status |= reflog_expire(ref, flags, reflog_expiry_prepare, should_expire_reflog_ent, reflog_expiry_cleanup, @@ -700,7 +700,6 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) for ( ; i < argc; i++) { const char *spec = strstr(argv[i], "@{"); - struct object_id oid; char *ep, *ref; int recno; @@ -709,7 +708,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) continue; } - if (!dwim_log(argv[i], spec - argv[i], &oid, &ref)) { + if (!dwim_log(argv[i], spec - argv[i], NULL, &ref)) { status |= error(_("no reflog for '%s'"), argv[i]); continue; } @@ -724,7 +723,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix) cb.cmd.expire_total = 0; } - status |= reflog_expire(ref, &oid, flags, + status |= reflog_expire(ref, flags, reflog_expiry_prepare, should_expire_reflog_ent, reflog_expiry_cleanup, diff --git a/builtin/repack.c b/builtin/repack.c index c1a209013b..cb9f4bfed3 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -586,15 +586,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix) strvec_pushf(&cmd.args, "--unpack-unreachable=%s", unpack_unreachable); - strvec_push(&cmd.env_array, "GIT_REF_PARANOIA=1"); } else if (pack_everything & LOOSEN_UNREACHABLE) { strvec_push(&cmd.args, "--unpack-unreachable"); } else if (keep_unreachable) { strvec_push(&cmd.args, "--keep-unreachable"); strvec_push(&cmd.args, "--pack-loose-unreachable"); - } else { - strvec_push(&cmd.env_array, "GIT_REF_PARANOIA=1"); } } } else if (geometry) { diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 22c4e1a4ff..8480a59f57 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -863,8 +863,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--bisect")) { - for_each_fullref_in("refs/bisect/bad", show_reference, NULL, 0); - for_each_fullref_in("refs/bisect/good", anti_reference, NULL, 0); + for_each_fullref_in("refs/bisect/bad", show_reference, NULL); + for_each_fullref_in("refs/bisect/good", anti_reference, NULL); continue; } if (opt_with_value(arg, "--branches", &arg)) { diff --git a/builtin/stash.c b/builtin/stash.c index 8f42360ca9..5512f4942c 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -313,6 +313,17 @@ static int reset_head(void) return run_command(&cp); } +static int is_path_a_directory(const char *path) +{ + /* + * This function differs from abspath.c:is_directory() in that + * here we use lstat() instead of stat(); we do not want to + * follow symbolic links here. + */ + struct stat st; + return (!lstat(path, &st) && S_ISDIR(st.st_mode)); +} + static void add_diff_to_buf(struct diff_queue_struct *q, struct diff_options *options, void *data) @@ -320,6 +331,9 @@ static void add_diff_to_buf(struct diff_queue_struct *q, int i; for (i = 0; i < q->nr; i++) { + if (is_path_a_directory(q->queue[i]->one->path)) + continue; + strbuf_addstr(data, q->queue[i]->one->path); /* NUL-terminate: will be fed to update-index -z */ @@ -521,9 +535,6 @@ static int do_apply_stash(const char *prefix, struct stash_info *info, } } - if (info->has_u && restore_untracked(&info->u_tree)) - return error(_("could not restore untracked files from stash")); - init_merge_options(&o, the_repository); o.branch1 = "Updated upstream"; @@ -558,6 +569,9 @@ static int do_apply_stash(const char *prefix, struct stash_info *info, unstage_changes_unless_new(&c_tree); } + if (info->has_u && restore_untracked(&info->u_tree)) + return error(_("could not restore untracked files from stash")); + if (!quiet) { struct child_process cp = CHILD_PROCESS_INIT; diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 57f0992515..d574d650af 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -2767,7 +2767,6 @@ static int push_check(int argc, const char **argv, const char *prefix) static int ensure_core_worktree(int argc, const char **argv, const char *prefix) { - const struct submodule *sub; const char *path; const char *cw; struct repository subrepo; @@ -2777,11 +2776,7 @@ static int ensure_core_worktree(int argc, const char **argv, const char *prefix) path = argv[1]; - sub = submodule_from_path(the_repository, null_oid(), path); - if (!sub) - BUG("We could get the submodule handle before?"); - - if (repo_submodule_init(&subrepo, the_repository, sub)) + if (repo_submodule_init(&subrepo, the_repository, path, null_oid())) die(_("could not get a repository handle for submodule '%s'"), path); if (!repo_config_get_string_tmp(&subrepo, "core.worktree", &cw)) { @@ -995,14 +995,6 @@ extern int core_apply_sparse_checkout; extern int core_sparse_checkout_cone; /* - * Include broken refs in all ref iterations, which will - * generally choke dangerous operations rather than letting - * them silently proceed without taking the broken ref into - * account. - */ -extern int ref_paranoia; - -/* * Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value). */ int use_optional_locks(void); @@ -1210,49 +1202,6 @@ enum scld_error safe_create_leading_directories(char *path); enum scld_error safe_create_leading_directories_const(const char *path); enum scld_error safe_create_leading_directories_no_share(char *path); -/* - * Callback function for raceproof_create_file(). This function is - * expected to do something that makes dirname(path) permanent despite - * the fact that other processes might be cleaning up empty - * directories at the same time. Usually it will create a file named - * path, but alternatively it could create another file in that - * directory, or even chdir() into that directory. The function should - * return 0 if the action was completed successfully. On error, it - * should return a nonzero result and set errno. - * raceproof_create_file() treats two errno values specially: - * - * - ENOENT -- dirname(path) does not exist. In this case, - * raceproof_create_file() tries creating dirname(path) - * (and any parent directories, if necessary) and calls - * the function again. - * - * - EISDIR -- the file already exists and is a directory. In this - * case, raceproof_create_file() removes the directory if - * it is empty (and recursively any empty directories that - * it contains) and calls the function again. - * - * Any other errno causes raceproof_create_file() to fail with the - * callback's return value and errno. - * - * Obviously, this function should be OK with being called again if it - * fails with ENOENT or EISDIR. In other scenarios it will not be - * called again. - */ -typedef int create_file_fn(const char *path, void *cb); - -/* - * Create a file in dirname(path) by calling fn, creating leading - * directories if necessary. Retry a few times in case we are racing - * with another process that is trying to clean up the directory that - * contains path. See the documentation for create_file_fn for more - * details. - * - * Return the value and set the errno that resulted from the most - * recent call of fn. fn is always called at least once, and will be - * called more than once if it returns ENOENT or EISDIR. - */ -int raceproof_create_file(const char *path, create_file_fn fn, void *cb); - int mkdir_in_gitdir(const char *path); char *interpolate_path(const char *path, int real_home); /* NEEDSWORK: remove this synonym once in-flight topics have migrated */ @@ -1727,13 +1676,6 @@ int update_server_info(int); const char *get_log_output_encoding(void); const char *get_commit_output_encoding(void); -/* - * This is a hack for test programs like test-dump-untracked-cache to - * ensure that they do not modify the untracked cache when reading it. - * Do not use it otherwise! - */ -extern int ignore_untracked_cache_config; - int committer_ident_sufficiently_given(void); int author_ident_sufficiently_given(void); diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh index 5772081b6e..1d0e48f451 100755 --- a/ci/install-dependencies.sh +++ b/ci/install-dependencies.sh @@ -12,7 +12,7 @@ UBUNTU_COMMON_PKGS="make libssl-dev libcurl4-openssl-dev libexpat-dev libemail-valid-perl libio-socket-ssl-perl libnet-smtp-ssl-perl" case "$jobname" in -linux-clang|linux-gcc) +linux-clang|linux-gcc|linux-leaks) sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test" sudo apt-get -q update sudo apt-get -q -y install language-pack-is libsvn-perl apache2 \ @@ -183,7 +183,7 @@ export GIT_TEST_CLONE_2GB=true export SKIP_DASHED_BUILT_INS=YesPlease case "$jobname" in -linux-clang|linux-gcc) +linux-clang|linux-gcc|linux-leaks) if [ "$jobname" = linux-gcc ] then export CC=gcc-8 @@ -233,4 +233,11 @@ linux-musl) ;; esac +case "$jobname" in +linux-leaks) + export SANITIZE=leak + export GIT_TEST_PASSING_SANITIZE_LEAK=true + ;; +esac + MAKEFLAGS="$MAKEFLAGS CC=${CC:-cc}" diff --git a/compat/win32/lazyload.h b/compat/win32/lazyload.h index d2056cdadf..2b3637135f 100644 --- a/compat/win32/lazyload.h +++ b/compat/win32/lazyload.h @@ -15,10 +15,12 @@ * source, target); */ +typedef void (*FARVOIDPROC)(void); + struct proc_addr { const char *const dll; const char *const function; - FARPROC pfunction; + FARVOIDPROC pfunction; unsigned initialized : 1; }; @@ -26,7 +28,8 @@ struct proc_addr { #define DECLARE_PROC_ADDR(dll, rettype, function, ...) \ static struct proc_addr proc_addr_##function = \ { #dll, #function, NULL, 0 }; \ - static rettype (WINAPI *function)(__VA_ARGS__) + typedef rettype (WINAPI *proc_type_##function)(__VA_ARGS__); \ + static proc_type_##function function /* * Loads a function from a DLL (once-only). @@ -35,9 +38,9 @@ struct proc_addr { * This function is not thread-safe. */ #define INIT_PROC_ADDR(function) \ - (function = get_proc_addr(&proc_addr_##function)) + (function = (proc_type_##function)get_proc_addr(&proc_addr_##function)) -static inline FARPROC get_proc_addr(struct proc_addr *proc) +static inline FARVOIDPROC get_proc_addr(struct proc_addr *proc) { /* only do this once */ if (!proc->initialized) { @@ -46,7 +49,8 @@ static inline FARPROC get_proc_addr(struct proc_addr *proc) hnd = LoadLibraryExA(proc->dll, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); if (hnd) - proc->pfunction = GetProcAddress(hnd, proc->function); + proc->pfunction = (FARVOIDPROC)GetProcAddress(hnd, + proc->function); } /* set ENOSYS if DLL or function was not found */ if (!proc->pfunction) @@ -425,7 +425,7 @@ static inline int iskeychar(int c) * baselen - pointer to size_t which will hold the length of the * section + subsection part, can be NULL */ -static int git_config_parse_key_1(const char *key, char **store_key, size_t *baselen_, int quiet) +int git_config_parse_key(const char *key, char **store_key, size_t *baselen_) { size_t i, baselen; int dot; @@ -437,14 +437,12 @@ static int git_config_parse_key_1(const char *key, char **store_key, size_t *bas */ if (last_dot == NULL || last_dot == key) { - if (!quiet) - error(_("key does not contain a section: %s"), key); + error(_("key does not contain a section: %s"), key); return -CONFIG_NO_SECTION_OR_NAME; } if (!last_dot[1]) { - if (!quiet) - error(_("key does not contain variable name: %s"), key); + error(_("key does not contain variable name: %s"), key); return -CONFIG_NO_SECTION_OR_NAME; } @@ -455,8 +453,7 @@ static int git_config_parse_key_1(const char *key, char **store_key, size_t *bas /* * Validate the key and while at it, lower case it for matching. */ - if (store_key) - *store_key = xmallocz(strlen(key)); + *store_key = xmallocz(strlen(key)); dot = 0; for (i = 0; key[i]; i++) { @@ -467,39 +464,24 @@ static int git_config_parse_key_1(const char *key, char **store_key, size_t *bas if (!dot || i > baselen) { if (!iskeychar(c) || (i == baselen + 1 && !isalpha(c))) { - if (!quiet) - error(_("invalid key: %s"), key); + error(_("invalid key: %s"), key); goto out_free_ret_1; } c = tolower(c); } else if (c == '\n') { - if (!quiet) - error(_("invalid key (newline): %s"), key); + error(_("invalid key (newline): %s"), key); goto out_free_ret_1; } - if (store_key) - (*store_key)[i] = c; + (*store_key)[i] = c; } return 0; out_free_ret_1: - if (store_key) { - FREE_AND_NULL(*store_key); - } + FREE_AND_NULL(*store_key); return -CONFIG_INVALID_KEY; } -int git_config_parse_key(const char *key, char **store_key, size_t *baselen) -{ - return git_config_parse_key_1(key, store_key, baselen, 0); -} - -int git_config_key_is_valid(const char *key) -{ - return !git_config_parse_key_1(key, NULL, NULL, 1); -} - static int config_parse_pair(const char *key, const char *value, config_fn_t fn, void *data) { @@ -259,7 +259,6 @@ int git_config_set_gently(const char *, const char *); void git_config_set(const char *, const char *); int git_config_parse_key(const char *, char **, size_t *); -int git_config_key_is_valid(const char *key); /* * The following macros specify flag bits that alter the behavior @@ -609,7 +608,6 @@ int git_config_get_maybe_bool(const char *key, int *dest); int git_config_get_pathname(const char *key, const char **dest); int git_config_get_index_threads(int *dest); -int git_config_get_untracked_cache(void); int git_config_get_split_index(void); int git_config_get_max_percent_split_change(void); int git_config_get_fsmonitor(void); diff --git a/config.mak.dev b/config.mak.dev index c080ac0231..7673fed114 100644 --- a/config.mak.dev +++ b/config.mak.dev @@ -6,13 +6,17 @@ ifeq ($(filter no-error,$(DEVOPTS)),) DEVELOPER_CFLAGS += -Werror SPARSE_FLAGS += -Wsparse-error endif + DEVELOPER_CFLAGS += -Wall ifeq ($(filter no-pedantic,$(DEVOPTS)),) DEVELOPER_CFLAGS += -pedantic +ifneq (($or $(filter gcc5,$(COMPILER_FEATURES)),$(filter clang4,$(COMPILER_FEATURES))),) DEVELOPER_CFLAGS += -Wpedantic -ifneq ($(filter gcc5,$(COMPILER_FEATURES)),) +ifneq ($(filter gcc10,$(COMPILER_FEATURES)),) +ifeq ($(uname_S),MINGW) DEVELOPER_CFLAGS += -Wno-pedantic-ms-format -DEVELOPER_CFLAGS += -Wno-incompatible-pointer-types +endif +endif endif endif DEVELOPER_CFLAGS += -Wdeclaration-after-statement diff --git a/config.mak.uname b/config.mak.uname index 76516aaa9a..2b178bad58 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -11,6 +11,10 @@ uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not') uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not') uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not') +ifneq ($(findstring MINGW,$(uname_S)),) + uname_S := MINGW +endif + ifdef MSVC # avoid the MingW and Cygwin configuration sections uname_S := Windows @@ -588,7 +592,7 @@ ifeq ($(uname_S),NONSTOP_KERNEL) SANE_TOOL_PATH = /usr/coreutils/bin:/usr/local/bin SHELL_PATH = /usr/coreutils/bin/bash endif -ifneq (,$(findstring MINGW,$(uname_S))) +ifeq ($(uname_S),MINGW) pathsep = ; HAVE_ALLOCA_H = YesPlease NO_PREAD = YesPlease @@ -557,6 +557,8 @@ const char *parse_feature_value(const char *feature_list, const char *feature, i if (!*value || isspace(*value)) { if (lenp) *lenp = 0; + if (offset) + *offset = found + len - feature_list; return value; } /* feature with a value (e.g., "agent=git/1.2.3") */ diff --git a/environment.c b/environment.c index b4ba4fa22d..9da7f3c1a1 100644 --- a/environment.c +++ b/environment.c @@ -31,7 +31,6 @@ int prefer_symlink_refs; int is_bare_repository_cfg = -1; /* unspecified */ int warn_ambiguous_refs = 1; int warn_on_object_refname_ambiguity = 1; -int ref_paranoia = -1; int repository_format_precious_objects; int repository_format_worktree_config; const char *git_commit_encoding; @@ -95,13 +94,6 @@ int auto_comment_line_char; /* Parallel index stat data preload? */ int core_preload_index = 1; -/* - * This is a hack for test programs like test-dump-untracked-cache to - * ensure that they do not modify the untracked cache when reading it. - * Do not use it otherwise! - */ -int ignore_untracked_cache_config; - /* This is set by setup_git_dir_gently() and/or git_default_config() */ char *git_work_tree_cfg; @@ -329,8 +321,7 @@ char *get_graft_file(struct repository *r) static void set_git_dir_1(const char *path) { - if (setenv(GIT_DIR_ENVIRONMENT, path, 1)) - die(_("could not set GIT_DIR to '%s'"), path); + xsetenv(GIT_DIR_ENVIRONMENT, path, 1); setup_git_env(path); } diff --git a/fetch-negotiator.c b/fetch-negotiator.c index 57ed5784e1..273390229f 100644 --- a/fetch-negotiator.c +++ b/fetch-negotiator.c @@ -19,7 +19,6 @@ void fetch_negotiator_init(struct repository *r, return; case FETCH_NEGOTIATION_DEFAULT: - default: default_negotiator_init(negotiator); return; } diff --git a/git-compat-util.h b/git-compat-util.h index 7c99eef661..141bb86351 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -878,6 +878,8 @@ void *xmemdupz(const void *data, size_t len); char *xstrndup(const char *str, size_t len); void *xrealloc(void *ptr, size_t size); void *xcalloc(size_t nmemb, size_t size); +void xsetenv(const char *name, const char *value, int overwrite); +void xunsetenv(const char *name); void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); const char *mmap_os_err(void); void *xmmap_gently(void *start, size_t length, int prot, int flags, int fd, off_t offset); diff --git a/git-curl-compat.h b/git-curl-compat.h index a308bdb3b9..56a83b6bbd 100644 --- a/git-curl-compat.h +++ b/git-curl-compat.h @@ -67,10 +67,11 @@ /** * CURLOPT_PINNEDPUBLICKEY was added in 7.39.0, released in November - * 2014. + * 2014. CURLE_SSL_PINNEDPUBKEYNOTMATCH was added in that same version. */ #if LIBCURL_VERSION_NUM >= 0x072c00 #define GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY 1 +#define GIT_CURL_HAVE_CURLE_SSL_PINNEDPUBKEYNOTMATCH 1 #endif /** diff --git a/git-cvsserver.perl b/git-cvsserver.perl index ed035f32c2..64319bed43 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -222,10 +222,11 @@ if ($state->{method} eq 'pserver') { open my $passwd, "<", $authdb or die $!; while (<$passwd>) { if (m{^\Q$user\E:(.*)}) { - if (crypt($user, descramble($password)) eq $1) { + my $hash = crypt(descramble($password), $1); + if (defined $hash and $hash eq $1) { $auth_ok = 1; } - }; + } } close $passwd; @@ -867,7 +867,7 @@ void free_grep_patterns(struct grep_opt *opt) free_pattern_expr(opt->pattern_expression); } -static char *end_of_line(char *cp, unsigned long *left) +static const char *end_of_line(const char *cp, unsigned long *left) { unsigned long l = *left; while (l && *cp != '\n') { @@ -908,7 +908,8 @@ static void show_name(struct grep_opt *opt, const char *name) opt->output(opt, opt->null_following_name ? "\0" : "\n", 1); } -static int patmatch(struct grep_pat *p, char *line, char *eol, +static int patmatch(struct grep_pat *p, + const char *line, const char *eol, regmatch_t *match, int eflags) { int hit; @@ -922,20 +923,16 @@ static int patmatch(struct grep_pat *p, char *line, char *eol, return hit; } -static int strip_timestamp(char *bol, char **eol_p) +static void strip_timestamp(const char *bol, const char **eol_p) { - char *eol = *eol_p; - int ch; + const char *eol = *eol_p; while (bol < --eol) { if (*eol != '>') continue; *eol_p = ++eol; - ch = *eol; - *eol = '\0'; - return ch; + break; } - return 0; } static struct { @@ -947,12 +944,12 @@ static struct { { "reflog ", 7 }, }; -static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, +static int match_one_pattern(struct grep_pat *p, + const char *bol, const char *eol, enum grep_context ctx, regmatch_t *pmatch, int eflags) { int hit = 0; - int saved_ch = 0; const char *start = bol; if ((p->token != GREP_PATTERN) && @@ -971,7 +968,7 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, switch (p->field) { case GREP_HEADER_AUTHOR: case GREP_HEADER_COMMITTER: - saved_ch = strip_timestamp(bol, &eol); + strip_timestamp(bol, &eol); break; default: break; @@ -1021,8 +1018,6 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, goto again; } } - if (p->token == GREP_PATTERN_HEAD && saved_ch) - *eol = saved_ch; if (hit) { pmatch[0].rm_so += bol - start; pmatch[0].rm_eo += bol - start; @@ -1030,8 +1025,9 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol, return hit; } -static int match_expr_eval(struct grep_opt *opt, struct grep_expr *x, char *bol, - char *eol, enum grep_context ctx, ssize_t *col, +static int match_expr_eval(struct grep_opt *opt, struct grep_expr *x, + const char *bol, const char *eol, + enum grep_context ctx, ssize_t *col, ssize_t *icol, int collect_hits) { int h = 0; @@ -1098,7 +1094,8 @@ static int match_expr_eval(struct grep_opt *opt, struct grep_expr *x, char *bol, return h; } -static int match_expr(struct grep_opt *opt, char *bol, char *eol, +static int match_expr(struct grep_opt *opt, + const char *bol, const char *eol, enum grep_context ctx, ssize_t *col, ssize_t *icol, int collect_hits) { @@ -1106,7 +1103,8 @@ static int match_expr(struct grep_opt *opt, char *bol, char *eol, return match_expr_eval(opt, x, bol, eol, ctx, col, icol, collect_hits); } -static int match_line(struct grep_opt *opt, char *bol, char *eol, +static int match_line(struct grep_opt *opt, + const char *bol, const char *eol, ssize_t *col, ssize_t *icol, enum grep_context ctx, int collect_hits) { @@ -1138,7 +1136,8 @@ static int match_line(struct grep_opt *opt, char *bol, char *eol, return hit; } -static int match_next_pattern(struct grep_pat *p, char *bol, char *eol, +static int match_next_pattern(struct grep_pat *p, + const char *bol, const char *eol, enum grep_context ctx, regmatch_t *pmatch, int eflags) { @@ -1159,7 +1158,8 @@ static int match_next_pattern(struct grep_pat *p, char *bol, char *eol, return 1; } -static int next_match(struct grep_opt *opt, char *bol, char *eol, +static int next_match(struct grep_opt *opt, + const char *bol, const char *eol, enum grep_context ctx, regmatch_t *pmatch, int eflags) { struct grep_pat *p; @@ -1215,7 +1215,8 @@ static void show_line_header(struct grep_opt *opt, const char *name, } } -static void show_line(struct grep_opt *opt, char *bol, char *eol, +static void show_line(struct grep_opt *opt, + const char *bol, const char *eol, const char *name, unsigned lno, ssize_t cno, char sign) { int rest = eol - bol; @@ -1246,7 +1247,6 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol, if (opt->color || opt->only_matching) { regmatch_t match; enum grep_context ctx = GREP_CONTEXT_BODY; - int ch = *eol; int eflags = 0; if (opt->color) { @@ -1261,7 +1261,6 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol, else if (sign == '=') line_color = opt->colors[GREP_COLOR_FUNCTION]; } - *eol = '\0'; while (next_match(opt, bol, eol, ctx, &match, eflags)) { if (match.rm_so == match.rm_eo) break; @@ -1279,7 +1278,6 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol, rest -= match.rm_eo; eflags = REG_NOTBOL; } - *eol = ch; } if (!opt->only_matching) { output_color(opt, bol, rest, line_color); @@ -1307,7 +1305,8 @@ static inline void grep_attr_unlock(void) pthread_mutex_unlock(&grep_attr_mutex); } -static int match_funcname(struct grep_opt *opt, struct grep_source *gs, char *bol, char *eol) +static int match_funcname(struct grep_opt *opt, struct grep_source *gs, + const char *bol, const char *eol) { xdemitconf_t *xecfg = opt->priv; if (xecfg && !xecfg->find_func) { @@ -1334,10 +1333,10 @@ static int match_funcname(struct grep_opt *opt, struct grep_source *gs, char *bo } static void show_funcname_line(struct grep_opt *opt, struct grep_source *gs, - char *bol, unsigned lno) + const char *bol, unsigned lno) { while (bol > gs->buf) { - char *eol = --bol; + const char *eol = --bol; while (bol > gs->buf && bol[-1] != '\n') bol--; @@ -1356,7 +1355,7 @@ static void show_funcname_line(struct grep_opt *opt, struct grep_source *gs, static int is_empty_line(const char *bol, const char *eol); static void show_pre_context(struct grep_opt *opt, struct grep_source *gs, - char *bol, char *end, unsigned lno) + const char *bol, const char *end, unsigned lno) { unsigned cur = lno, from = 1, funcname_lno = 0, orig_from; int funcname_needed = !!opt->funcname, comment_needed = 0; @@ -1376,8 +1375,8 @@ static void show_pre_context(struct grep_opt *opt, struct grep_source *gs, /* Rewind. */ while (bol > gs->buf && cur > from) { - char *next_bol = bol; - char *eol = --bol; + const char *next_bol = bol; + const char *eol = --bol; while (bol > gs->buf && bol[-1] != '\n') bol--; @@ -1408,7 +1407,7 @@ static void show_pre_context(struct grep_opt *opt, struct grep_source *gs, /* Back forward. */ while (cur < lno) { - char *eol = bol, sign = (cur == funcname_lno) ? '=' : '-'; + const char *eol = bol, sign = (cur == funcname_lno) ? '=' : '-'; while (*eol != '\n') eol++; @@ -1436,12 +1435,12 @@ static int should_lookahead(struct grep_opt *opt) static int look_ahead(struct grep_opt *opt, unsigned long *left_p, unsigned *lno_p, - char **bol_p) + const char **bol_p) { unsigned lno = *lno_p; - char *bol = *bol_p; + const char *bol = *bol_p; struct grep_pat *p; - char *sp, *last_bol; + const char *sp, *last_bol; regoff_t earliest = -1; for (p = opt->pattern_list; p; p = p->next) { @@ -1543,8 +1542,8 @@ static int is_empty_line(const char *bol, const char *eol) static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int collect_hits) { - char *bol; - char *peek_bol = NULL; + const char *bol; + const char *peek_bol = NULL; unsigned long left; unsigned lno = 1; unsigned last_hit = 0; @@ -1626,7 +1625,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle bol = gs->buf; left = gs->size; while (left) { - char *eol, ch; + const char *eol; int hit; ssize_t cno; ssize_t col = -1, icol = -1; @@ -1647,14 +1646,11 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle && look_ahead(opt, &left, &lno, &bol)) break; eol = end_of_line(bol, &left); - ch = *eol; - *eol = 0; if ((ctx == GREP_CONTEXT_HEAD) && (eol == bol)) ctx = GREP_CONTEXT_BODY; hit = match_line(opt, bol, eol, &col, &icol, ctx, collect_hits); - *eol = ch; if (collect_hits) goto next_line; @@ -1713,7 +1709,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle } if (show_function && (!peek_bol || peek_bol < bol)) { unsigned long peek_left = left; - char *peek_eol = eol; + const char *peek_eol = eol; /* * Trailing empty lines are not interesting. @@ -1825,7 +1821,8 @@ int grep_source(struct grep_opt *opt, struct grep_source *gs) return grep_source_1(opt, gs, 0); } -static void grep_source_init_buf(struct grep_source *gs, char *buf, +static void grep_source_init_buf(struct grep_source *gs, + const char *buf, unsigned long size) { gs->type = GREP_SOURCE_BUF; @@ -1837,7 +1834,7 @@ static void grep_source_init_buf(struct grep_source *gs, char *buf, gs->identifier = NULL; } -int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size) +int grep_buffer(struct grep_opt *opt, const char *buf, unsigned long size) { struct grep_source gs; int r; @@ -1889,7 +1886,9 @@ void grep_source_clear_data(struct grep_source *gs) switch (gs->type) { case GREP_SOURCE_FILE: case GREP_SOURCE_OID: - FREE_AND_NULL(gs->buf); + /* these types own the buffer */ + free((char *)gs->buf); + gs->buf = NULL; gs->size = 0; break; case GREP_SOURCE_BUF: @@ -189,7 +189,7 @@ void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *orig void append_header_grep_pattern(struct grep_opt *, enum grep_header_field, const char *); void compile_grep_patterns(struct grep_opt *opt); void free_grep_patterns(struct grep_opt *opt); -int grep_buffer(struct grep_opt *opt, char *buf, unsigned long size); +int grep_buffer(struct grep_opt *opt, const char *buf, unsigned long size); struct grep_source { char *name; @@ -202,7 +202,7 @@ struct grep_source { void *identifier; struct repository *repo; /* if GREP_SOURCE_OID */ - char *buf; + const char *buf; unsigned long size; char *path; /* for attribute lookups */ @@ -223,7 +223,6 @@ void grep_source_load_driver(struct grep_source *gs, int grep_source(struct grep_opt *opt, struct grep_source *gs); struct grep_opt *grep_opt_dup(const struct grep_opt *opt); -int grep_threads_ok(const struct grep_opt *opt); /* * Mutex used around access to the attributes machinery if @@ -551,8 +551,8 @@ static void redact_sensitive_header(struct strbuf *header) const char *sensitive_header; if (trace_curl_redact && - (skip_prefix(header->buf, "Authorization:", &sensitive_header) || - skip_prefix(header->buf, "Proxy-Authorization:", &sensitive_header))) { + (skip_iprefix(header->buf, "Authorization:", &sensitive_header) || + skip_iprefix(header->buf, "Proxy-Authorization:", &sensitive_header))) { /* The first token is the type, which is OK to log */ while (isspace(*sensitive_header)) sensitive_header++; @@ -562,7 +562,7 @@ static void redact_sensitive_header(struct strbuf *header) strbuf_setlen(header, sensitive_header - header->buf); strbuf_addstr(header, " <redacted>"); } else if (trace_curl_redact && - skip_prefix(header->buf, "Cookie:", &sensitive_header)) { + skip_iprefix(header->buf, "Cookie:", &sensitive_header)) { struct strbuf redacted_header = STRBUF_INIT; const char *cookie; @@ -1489,6 +1489,10 @@ static int handle_curl_result(struct slot_results *results) */ credential_reject(&cert_auth); return HTTP_NOAUTH; +#ifdef GIT_CURL_HAVE_CURLE_SSL_PINNEDPUBKEYNOTMATCH + } else if (results->curl_result == CURLE_SSL_PINNEDPUBKEYNOTMATCH) { + return HTTP_NOMATCHPUBLICKEY; +#endif } else if (missing_target(results)) return HTTP_MISSING_TARGET; else if (results->http_code == 401) { @@ -154,6 +154,7 @@ struct http_get_options { #define HTTP_START_FAILED 3 #define HTTP_REAUTH 4 #define HTTP_NOAUTH 5 +#define HTTP_NOMATCHPUBLICKEY 6 /* * Requests a URL and stores the result in a strbuf. diff --git a/log-tree.h b/log-tree.h index 1e8c91dbf2..e7e4641cf8 100644 --- a/log-tree.h +++ b/log-tree.h @@ -14,10 +14,8 @@ struct decoration_filter { }; int parse_decorate_color_config(const char *var, const char *slot_name, const char *value); -void init_log_tree_opt(struct rev_info *); int log_tree_diff_flush(struct rev_info *); int log_tree_commit(struct rev_info *, struct commit *); -int log_tree_opt_parse(struct rev_info *, const char **, int); void show_log(struct rev_info *opt); void format_decorations_extended(struct strbuf *sb, const struct commit *commit, int use_color, @@ -41,6 +41,12 @@ static void ensure_config_read(void) } /* + * If we see this many or more "ref-prefix" lines from the client, we consider + * it "too many" and will avoid using the prefix feature entirely. + */ +#define TOO_MANY_PREFIXES 65536 + +/* * Check if one of the prefixes is a prefix of the ref. * If no prefixes were provided, all refs match. */ @@ -158,20 +164,32 @@ int ls_refs(struct repository *r, struct packet_reader *request) data.peel = 1; else if (!strcmp("symrefs", arg)) data.symrefs = 1; - else if (skip_prefix(arg, "ref-prefix ", &out)) - strvec_push(&data.prefixes, out); + else if (skip_prefix(arg, "ref-prefix ", &out)) { + if (data.prefixes.nr < TOO_MANY_PREFIXES) + strvec_push(&data.prefixes, out); + } else if (!strcmp("unborn", arg)) data.unborn = allow_unborn; + else + die(_("unexpected line: '%s'"), arg); } if (request->status != PACKET_READ_FLUSH) die(_("expected flush after ls-refs arguments")); + /* + * If we saw too many prefixes, we must avoid using them at all; as + * soon as we have any prefix, they are meant to form a comprehensive + * list. + */ + if (data.prefixes.nr >= TOO_MANY_PREFIXES) + strvec_clear(&data.prefixes); + send_possibly_unborn_head(&data); if (!data.prefixes.nr) strvec_push(&data.prefixes, ""); for_each_fullref_in_prefixes(get_git_namespace(), data.prefixes.v, - send_ref, &data, 0); + send_ref, &data); packet_fflush(stdout); strvec_clear(&data.prefixes); strbuf_release(&data.buf); diff --git a/merge-ort.c b/merge-ort.c index 35aa979c3a..ab42c5d847 100644 --- a/merge-ort.c +++ b/merge-ort.c @@ -32,6 +32,7 @@ #include "promisor-remote.h" #include "revision.h" #include "strmap.h" +#include "submodule-config.h" #include "submodule.h" #include "tree.h" #include "unpack-trees.h" @@ -1511,7 +1512,6 @@ static int find_first_merges(struct repository *repo, xsnprintf(merged_revision, sizeof(merged_revision), "^%s", oid_to_hex(&a->object.oid)); repo_init_revisions(repo, &revs, NULL); - rev_opts.submodule = path; /* FIXME: can't handle linked worktrees in submodules yet */ revs.single_worktree = path != NULL; setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts); @@ -1521,7 +1521,7 @@ static int find_first_merges(struct repository *repo, die("revision walk setup failed"); while ((commit = get_revision(&revs)) != NULL) { struct object *o = &(commit->object); - if (in_merge_bases(b, commit)) + if (repo_in_merge_bases(repo, b, commit)) add_object_array(o, NULL, &merges); } reset_revision_walk(); @@ -1536,7 +1536,7 @@ static int find_first_merges(struct repository *repo, contains_another = 0; for (j = 0; j < merges.nr; j++) { struct commit *m2 = (struct commit *) merges.objects[j].item; - if (i != j && in_merge_bases(m2, m1)) { + if (i != j && repo_in_merge_bases(repo, m2, m1)) { contains_another = 1; break; } @@ -1557,10 +1557,12 @@ static int merge_submodule(struct merge_options *opt, const struct object_id *b, struct object_id *result) { + struct repository subrepo; + struct strbuf sb = STRBUF_INIT; + int ret = 0; struct commit *commit_o, *commit_a, *commit_b; int parent_count; struct object_array merges; - struct strbuf sb = STRBUF_INIT; int i; int search = !opt->priv->call_depth; @@ -1576,6 +1578,10 @@ static int merge_submodule(struct merge_options *opt, if (is_null_oid(b)) return 0; + /* + * NEEDSWORK: Remove this when all submodule object accesses are + * through explicitly specified repositores. + */ if (add_submodule_odb(path)) { path_msg(opt, path, 0, _("Failed to merge submodule %s (not checked out)"), @@ -1583,39 +1589,48 @@ static int merge_submodule(struct merge_options *opt, return 0; } - if (!(commit_o = lookup_commit_reference(opt->repo, o)) || - !(commit_a = lookup_commit_reference(opt->repo, a)) || - !(commit_b = lookup_commit_reference(opt->repo, b))) { + if (repo_submodule_init(&subrepo, opt->repo, path, null_oid())) { + path_msg(opt, path, 0, + _("Failed to merge submodule %s (not checked out)"), + path); + return 0; + } + + if (!(commit_o = lookup_commit_reference(&subrepo, o)) || + !(commit_a = lookup_commit_reference(&subrepo, a)) || + !(commit_b = lookup_commit_reference(&subrepo, b))) { path_msg(opt, path, 0, _("Failed to merge submodule %s (commits not present)"), path); - return 0; + goto cleanup; } /* check whether both changes are forward */ - if (!in_merge_bases(commit_o, commit_a) || - !in_merge_bases(commit_o, commit_b)) { + if (!repo_in_merge_bases(&subrepo, commit_o, commit_a) || + !repo_in_merge_bases(&subrepo, commit_o, commit_b)) { path_msg(opt, path, 0, _("Failed to merge submodule %s " "(commits don't follow merge-base)"), path); - return 0; + goto cleanup; } /* Case #1: a is contained in b or vice versa */ - if (in_merge_bases(commit_a, commit_b)) { + if (repo_in_merge_bases(&subrepo, commit_a, commit_b)) { oidcpy(result, b); path_msg(opt, path, 1, _("Note: Fast-forwarding submodule %s to %s"), path, oid_to_hex(b)); - return 1; + ret = 1; + goto cleanup; } - if (in_merge_bases(commit_b, commit_a)) { + if (repo_in_merge_bases(&subrepo, commit_b, commit_a)) { oidcpy(result, a); path_msg(opt, path, 1, _("Note: Fast-forwarding submodule %s to %s"), path, oid_to_hex(a)); - return 1; + ret = 1; + goto cleanup; } /* @@ -1627,10 +1642,10 @@ static int merge_submodule(struct merge_options *opt, /* Skip the search if makes no sense to the calling context. */ if (!search) - return 0; + goto cleanup; /* find commit which merges them */ - parent_count = find_first_merges(opt->repo, path, commit_a, commit_b, + parent_count = find_first_merges(&subrepo, path, commit_a, commit_b, &merges); switch (parent_count) { case 0: @@ -1664,7 +1679,9 @@ static int merge_submodule(struct merge_options *opt, } object_array_clear(&merges); - return 0; +cleanup: + repo_clear(&subrepo); + return ret; } static void initialize_attr_index(struct merge_options *opt) @@ -4096,7 +4113,7 @@ static int record_conflicted_index_entries(struct merge_options *opt) state.istate = index; original_cache_nr = index->cache_nr; - /* Put every entry from paths into plist, then sort */ + /* Append every entry from conflicted into index, then sort */ strmap_for_each_entry(&opt->priv->conflicted, &iter, e) { const char *path = e->key; struct conflict_info *ci = e->value; diff --git a/merge-recursive.c b/merge-recursive.c index e594d4c3fa..5a2d8a60c0 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -24,6 +24,7 @@ #include "repository.h" #include "revision.h" #include "string-list.h" +#include "submodule-config.h" #include "submodule.h" #include "tag.h" #include "tree-walk.h" @@ -1110,7 +1111,6 @@ static int find_first_merges(struct repository *repo, xsnprintf(merged_revision, sizeof(merged_revision), "^%s", oid_to_hex(&a->object.oid)); repo_init_revisions(repo, &revs, NULL); - rev_opts.submodule = path; /* FIXME: can't handle linked worktrees in submodules yet */ revs.single_worktree = path != NULL; setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts); @@ -1120,7 +1120,7 @@ static int find_first_merges(struct repository *repo, die("revision walk setup failed"); while ((commit = get_revision(&revs)) != NULL) { struct object *o = &(commit->object); - if (in_merge_bases(b, commit)) + if (repo_in_merge_bases(repo, b, commit)) add_object_array(o, NULL, &merges); } reset_revision_walk(); @@ -1135,7 +1135,7 @@ static int find_first_merges(struct repository *repo, contains_another = 0; for (j = 0; j < merges.nr; j++) { struct commit *m2 = (struct commit *) merges.objects[j].item; - if (i != j && in_merge_bases(m2, m1)) { + if (i != j && repo_in_merge_bases(repo, m2, m1)) { contains_another = 1; break; } @@ -1171,6 +1171,8 @@ static int merge_submodule(struct merge_options *opt, const struct object_id *base, const struct object_id *a, const struct object_id *b) { + struct repository subrepo; + int ret = 0; struct commit *commit_base, *commit_a, *commit_b; int parent_count; struct object_array merges; @@ -1194,27 +1196,36 @@ static int merge_submodule(struct merge_options *opt, if (is_null_oid(b)) return 0; + /* + * NEEDSWORK: Remove this when all submodule object accesses are + * through explicitly specified repositores. + */ if (add_submodule_odb(path)) { output(opt, 1, _("Failed to merge submodule %s (not checked out)"), path); return 0; } - if (!(commit_base = lookup_commit_reference(opt->repo, base)) || - !(commit_a = lookup_commit_reference(opt->repo, a)) || - !(commit_b = lookup_commit_reference(opt->repo, b))) { - output(opt, 1, _("Failed to merge submodule %s (commits not present)"), path); + if (repo_submodule_init(&subrepo, opt->repo, path, null_oid())) { + output(opt, 1, _("Failed to merge submodule %s (not checked out)"), path); return 0; } + if (!(commit_base = lookup_commit_reference(&subrepo, base)) || + !(commit_a = lookup_commit_reference(&subrepo, a)) || + !(commit_b = lookup_commit_reference(&subrepo, b))) { + output(opt, 1, _("Failed to merge submodule %s (commits not present)"), path); + goto cleanup; + } + /* check whether both changes are forward */ - if (!in_merge_bases(commit_base, commit_a) || - !in_merge_bases(commit_base, commit_b)) { + if (!repo_in_merge_bases(&subrepo, commit_base, commit_a) || + !repo_in_merge_bases(&subrepo, commit_base, commit_b)) { output(opt, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path); - return 0; + goto cleanup; } /* Case #1: a is contained in b or vice versa */ - if (in_merge_bases(commit_a, commit_b)) { + if (repo_in_merge_bases(&subrepo, commit_a, commit_b)) { oidcpy(result, b); if (show(opt, 3)) { output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path); @@ -1224,9 +1235,10 @@ static int merge_submodule(struct merge_options *opt, else ; /* no output */ - return 1; + ret = 1; + goto cleanup; } - if (in_merge_bases(commit_b, commit_a)) { + if (repo_in_merge_bases(&subrepo, commit_b, commit_a)) { oidcpy(result, a); if (show(opt, 3)) { output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path); @@ -1236,7 +1248,8 @@ static int merge_submodule(struct merge_options *opt, else ; /* no output */ - return 1; + ret = 1; + goto cleanup; } /* @@ -1248,10 +1261,10 @@ static int merge_submodule(struct merge_options *opt, /* Skip the search if makes no sense to the calling context. */ if (!search) - return 0; + goto cleanup; /* find commit which merges them */ - parent_count = find_first_merges(opt->repo, &merges, path, + parent_count = find_first_merges(&subrepo, &merges, path, commit_a, commit_b); switch (parent_count) { case 0: @@ -1278,7 +1291,9 @@ static int merge_submodule(struct merge_options *opt, } object_array_clear(&merges); - return 0; +cleanup: + repo_clear(&subrepo); + return ret; } static int merge_mode_and_contents(struct merge_options *opt, @@ -993,9 +993,13 @@ static int write_midx_bitmap(char *midx_name, unsigned char *midx_hash, struct pack_idx_entry **index; struct commit **commits = NULL; uint32_t i, commits_nr; + uint16_t options = 0; char *bitmap_name = xstrfmt("%s-%s.bitmap", midx_name, hash_to_hex(midx_hash)); int ret; + if (flags & MIDX_WRITE_BITMAP_HASH_CACHE) + options |= BITMAP_OPT_HASH_CACHE; + prepare_midx_packing_data(&pdata, ctx); commits = find_commits_for_midx_bitmap(&commits_nr, ctx); @@ -1034,7 +1038,7 @@ static int write_midx_bitmap(char *midx_name, unsigned char *midx_hash, goto cleanup; bitmap_writer_set_checksum(midx_hash); - bitmap_writer_finish(index, pdata.nr_objects, bitmap_name, 0); + bitmap_writer_finish(index, pdata.nr_objects, bitmap_name, options); cleanup: free(index); @@ -44,6 +44,7 @@ struct multi_pack_index { #define MIDX_PROGRESS (1 << 0) #define MIDX_WRITE_REV_INDEX (1 << 1) #define MIDX_WRITE_BITMAP (1 << 2) +#define MIDX_WRITE_BITMAP_HASH_CACHE (1 << 3) const unsigned char *get_midx_checksum(struct multi_pack_index *m); char *get_midx_filename(const char *object_dir); diff --git a/object-file.c b/object-file.c index be4f94ecf3..112d9b4bad 100644 --- a/object-file.c +++ b/object-file.c @@ -415,74 +415,6 @@ enum scld_error safe_create_leading_directories_const(const char *path) return result; } -int raceproof_create_file(const char *path, create_file_fn fn, void *cb) -{ - /* - * The number of times we will try to remove empty directories - * in the way of path. This is only 1 because if another - * process is racily creating directories that conflict with - * us, we don't want to fight against them. - */ - int remove_directories_remaining = 1; - - /* - * The number of times that we will try to create the - * directories containing path. We are willing to attempt this - * more than once, because another process could be trying to - * clean up empty directories at the same time as we are - * trying to create them. - */ - int create_directories_remaining = 3; - - /* A scratch copy of path, filled lazily if we need it: */ - struct strbuf path_copy = STRBUF_INIT; - - int ret, save_errno; - - /* Sanity check: */ - assert(*path); - -retry_fn: - ret = fn(path, cb); - save_errno = errno; - if (!ret) - goto out; - - if (errno == EISDIR && remove_directories_remaining-- > 0) { - /* - * A directory is in the way. Maybe it is empty; try - * to remove it: - */ - if (!path_copy.len) - strbuf_addstr(&path_copy, path); - - if (!remove_dir_recursively(&path_copy, REMOVE_DIR_EMPTY_ONLY)) - goto retry_fn; - } else if (errno == ENOENT && create_directories_remaining-- > 0) { - /* - * Maybe the containing directory didn't exist, or - * maybe it was just deleted by a process that is - * racing with us to clean up empty directories. Try - * to create it: - */ - enum scld_error scld_result; - - if (!path_copy.len) - strbuf_addstr(&path_copy, path); - - do { - scld_result = safe_create_leading_directories(path_copy.buf); - if (scld_result == SCLD_OK) - goto retry_fn; - } while (scld_result == SCLD_VANISHED && create_directories_remaining-- > 0); - } - -out: - strbuf_release(&path_copy); - errno = save_errno; - return ret; -} - static void fill_loose_path(struct strbuf *buf, const struct object_id *oid) { int i; diff --git a/pack-bitmap.c b/pack-bitmap.c index 8504110a4d..33a3732992 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c @@ -1742,6 +1742,33 @@ int test_bitmap_commits(struct repository *r) return 0; } +int test_bitmap_hashes(struct repository *r) +{ + struct bitmap_index *bitmap_git = prepare_bitmap_git(r); + struct object_id oid; + uint32_t i, index_pos; + + if (!bitmap_git->hashes) + goto cleanup; + + for (i = 0; i < bitmap_num_objects(bitmap_git); i++) { + if (bitmap_is_midx(bitmap_git)) + index_pos = pack_pos_to_midx(bitmap_git->midx, i); + else + index_pos = pack_pos_to_index(bitmap_git->pack, i); + + nth_bitmap_object_oid(bitmap_git, &oid, index_pos); + + printf("%s %"PRIu32"\n", + oid_to_hex(&oid), get_be32(bitmap_git->hashes + index_pos)); + } + +cleanup: + free_bitmap_index(bitmap_git); + + return 0; +} + int rebuild_bitmap(const uint32_t *reposition, struct ewah_bitmap *source, struct bitmap *dest) @@ -1791,18 +1818,20 @@ uint32_t *create_bitmap_mapping(struct bitmap_index *bitmap_git, for (i = 0; i < num_objects; ++i) { struct object_id oid; struct object_entry *oe; + uint32_t index_pos; if (bitmap_is_midx(bitmap_git)) - nth_midxed_object_oid(&oid, - bitmap_git->midx, - pack_pos_to_midx(bitmap_git->midx, i)); + index_pos = pack_pos_to_midx(bitmap_git->midx, i); else - nth_packed_object_id(&oid, bitmap_git->pack, - pack_pos_to_index(bitmap_git->pack, i)); + index_pos = pack_pos_to_index(bitmap_git->pack, i); + nth_bitmap_object_oid(bitmap_git, &oid, index_pos); oe = packlist_find(mapping, &oid); - if (oe) + if (oe) { reposition[i] = oe_in_pack_pos(mapping, oe) + 1; + if (bitmap_git->hashes && !oe->hash) + oe->hash = get_be32(bitmap_git->hashes + index_pos); + } } return reposition; diff --git a/pack-bitmap.h b/pack-bitmap.h index 469090bad2..ed46d27077 100644 --- a/pack-bitmap.h +++ b/pack-bitmap.h @@ -52,6 +52,7 @@ void traverse_bitmap_commit_list(struct bitmap_index *, show_reachable_fn show_reachable); void test_bitmap_walk(struct rev_info *revs); int test_bitmap_commits(struct repository *r); +int test_bitmap_hashes(struct repository *r); struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs, struct list_objects_filter_options *filter, int filter_provided_objects); diff --git a/packfile.c b/packfile.c index 0e92bd7bd2..89402cfc69 100644 --- a/packfile.c +++ b/packfile.c @@ -339,6 +339,7 @@ void close_pack(struct packed_git *p) close_pack_fd(p); close_pack_index(p); close_pack_revindex(p); + oidset_clear(&p->bad_objects); } void close_object_store(struct raw_object_store *o) diff --git a/read-cache.c b/read-cache.c index f5d4385c40..a78b88a41b 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1944,13 +1944,22 @@ static void tweak_untracked_cache(struct index_state *istate) prepare_repo_settings(r); - if (r->settings.core_untracked_cache == UNTRACKED_CACHE_REMOVE) { + switch (r->settings.core_untracked_cache) { + case UNTRACKED_CACHE_REMOVE: remove_untracked_cache(istate); - return; - } - - if (r->settings.core_untracked_cache == UNTRACKED_CACHE_WRITE) + break; + case UNTRACKED_CACHE_WRITE: add_untracked_cache(istate); + break; + case UNTRACKED_CACHE_KEEP: + /* + * Either an explicit "core.untrackedCache=keep", the + * default if "core.untrackedCache" isn't configured, + * or a fallback on an unknown "core.untrackedCache" + * value. + */ + break; + } } static void tweak_split_index(struct index_state *istate) @@ -2391,9 +2400,21 @@ int read_index_from(struct index_state *istate, const char *path, base_path = xstrfmt("%s/sharedindex.%s", gitdir, base_oid_hex); trace2_region_enter_printf("index", "shared/do_read_index", the_repository, "%s", base_path); - ret = do_read_index(split_index->base, base_path, 1); + ret = do_read_index(split_index->base, base_path, 0); trace2_region_leave_printf("index", "shared/do_read_index", the_repository, "%s", base_path); + if (!ret) { + char *path_copy = xstrdup(path); + const char *base_path2 = xstrfmt("%s/sharedindex.%s", + dirname(path_copy), + base_oid_hex); + free(path_copy); + trace2_region_enter_printf("index", "shared/do_read_index", + the_repository, "%s", base_path2); + ret = do_read_index(split_index->base, base_path2, 1); + trace2_region_leave_printf("index", "shared/do_read_index", + the_repository, "%s", base_path2); + } if (!oideq(&split_index->base_oid, &split_index->base->oid)) die(_("broken index, expect %s in %s, got %s"), base_oid_hex, base_path, @@ -2812,11 +2833,8 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, } } - if (!istate->version) { + if (!istate->version) istate->version = get_index_format_default(the_repository); - if (git_env_bool("GIT_TEST_SPLIT_INDEX", 0)) - init_split_index(istate); - } /* demote version 3 to version 2 when the latter suffices */ if (istate->version == 3 || istate->version == 2) @@ -3243,7 +3261,7 @@ static int too_many_not_shared_entries(struct index_state *istate) int write_locked_index(struct index_state *istate, struct lock_file *lock, unsigned flags) { - int new_shared_index, ret; + int new_shared_index, ret, test_split_index_env; struct split_index *si = istate->split_index; if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0)) @@ -3258,7 +3276,10 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock, if (istate->fsmonitor_last_update) fill_fsmonitor_bitmap(istate); - if (!si || alternate_index_output || + test_split_index_env = git_env_bool("GIT_TEST_SPLIT_INDEX", 0); + + if ((!si && !test_split_index_env) || + alternate_index_output || (istate->cache_changed & ~EXTMASK)) { if (si) oidclr(&si->base_oid); @@ -3266,10 +3287,15 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock, goto out; } - if (git_env_bool("GIT_TEST_SPLIT_INDEX", 0)) { - int v = si->base_oid.hash[0]; - if ((v & 15) < 6) + if (test_split_index_env) { + if (!si) { + si = init_split_index(istate); istate->cache_changed |= SPLIT_INDEX_ORDERED; + } else { + int v = si->base_oid.hash[0]; + if ((v & 15) < 6) + istate->cache_changed |= SPLIT_INDEX_ORDERED; + } } if (too_many_not_shared_entries(istate)) istate->cache_changed |= SPLIT_INDEX_ORDERED; diff --git a/ref-filter.c b/ref-filter.c index eee4f9b17c..add429be79 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -2100,8 +2100,7 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname) */ static int for_each_fullref_in_pattern(struct ref_filter *filter, each_ref_fn cb, - void *cb_data, - int broken) + void *cb_data) { if (!filter->match_as_path) { /* @@ -2109,7 +2108,7 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter, * prefixes like "refs/heads/" etc. are stripped off, * so we have to look at everything: */ - return for_each_fullref_in("", cb, cb_data, broken); + return for_each_fullref_in("", cb, cb_data); } if (filter->ignore_case) { @@ -2118,16 +2117,16 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter, * so just return everything and let the caller * sort it out. */ - return for_each_fullref_in("", cb, cb_data, broken); + return for_each_fullref_in("", cb, cb_data); } if (!filter->name_patterns[0]) { /* no patterns; we have to look at everything */ - return for_each_fullref_in("", cb, cb_data, broken); + return for_each_fullref_in("", cb, cb_data); } return for_each_fullref_in_prefixes(NULL, filter->name_patterns, - cb, cb_data, broken); + cb, cb_data); } /* @@ -2405,13 +2404,10 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int { struct ref_filter_cbdata ref_cbdata; int ret = 0; - unsigned int broken = 0; ref_cbdata.array = array; ref_cbdata.filter = filter; - if (type & FILTER_REFS_INCLUDE_BROKEN) - broken = 1; filter->kind = type & FILTER_REFS_KIND_MASK; init_contains_cache(&ref_cbdata.contains_cache); @@ -2428,13 +2424,13 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int * of filter_ref_kind(). */ if (filter->kind == FILTER_REFS_BRANCHES) - ret = for_each_fullref_in("refs/heads/", ref_filter_handler, &ref_cbdata, broken); + ret = for_each_fullref_in("refs/heads/", ref_filter_handler, &ref_cbdata); else if (filter->kind == FILTER_REFS_REMOTES) - ret = for_each_fullref_in("refs/remotes/", ref_filter_handler, &ref_cbdata, broken); + ret = for_each_fullref_in("refs/remotes/", ref_filter_handler, &ref_cbdata); else if (filter->kind == FILTER_REFS_TAGS) - ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata, broken); + ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata); else if (filter->kind & FILTER_REFS_ALL) - ret = for_each_fullref_in_pattern(filter, ref_filter_handler, &ref_cbdata, broken); + ret = for_each_fullref_in_pattern(filter, ref_filter_handler, &ref_cbdata); if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD)) head_ref(ref_filter_handler, &ref_cbdata); } diff --git a/ref-filter.h b/ref-filter.h index c15dee8d6b..b636f4389d 100644 --- a/ref-filter.h +++ b/ref-filter.h @@ -13,7 +13,6 @@ #define QUOTE_PYTHON 4 #define QUOTE_TCL 8 -#define FILTER_REFS_INCLUDE_BROKEN 0x0001 #define FILTER_REFS_TAGS 0x0002 #define FILTER_REFS_BRANCHES 0x0004 #define FILTER_REFS_REMOTES 0x0008 diff --git a/reflog-walk.c b/reflog-walk.c index e9cd328369..8ac4b284b6 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -158,10 +158,9 @@ int add_reflog_for_walk(struct reflog_walk_info *info, } reflogs = read_complete_reflog(branch); if (!reflogs || reflogs->nr == 0) { - struct object_id oid; char *b; int ret = dwim_log(branch, strlen(branch), - &oid, &b); + NULL, &b); if (ret > 1) free(b); else if (ret == 1) { @@ -33,11 +33,6 @@ static struct ref_storage_be *find_ref_storage_backend(const char *name) return NULL; } -int ref_storage_backend_exists(const char *name) -{ - return find_ref_storage_backend(name) != NULL; -} - /* * How to handle various characters in refnames: * 0: An acceptable character for refs @@ -698,7 +693,7 @@ int repo_dwim_log(struct repository *r, const char *str, int len, strbuf_addf(&path, *p, len, str); ref = refs_resolve_ref_unsafe(refs, path.buf, RESOLVE_REF_READING, - &hash, NULL); + oid ? &hash : NULL, NULL); if (!ref) continue; if (refs_reflog_exists(refs, path.buf)) @@ -710,7 +705,8 @@ int repo_dwim_log(struct repository *r, const char *str, int len, continue; if (!logs_found++) { *log = xstrdup(it); - oidcpy(oid, &hash); + if (oid) + oidcpy(oid, &hash); } if (!warn_ambiguous_refs) break; @@ -1413,14 +1409,21 @@ int head_ref(each_ref_fn fn, void *cb_data) struct ref_iterator *refs_ref_iterator_begin( struct ref_store *refs, - const char *prefix, int trim, int flags) + const char *prefix, int trim, + enum do_for_each_ref_flags flags) { struct ref_iterator *iter; - if (ref_paranoia < 0) - ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0); - if (ref_paranoia) - flags |= DO_FOR_EACH_INCLUDE_BROKEN; + if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) { + static int ref_paranoia = -1; + + if (ref_paranoia < 0) + ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 1); + if (ref_paranoia) { + flags |= DO_FOR_EACH_INCLUDE_BROKEN; + flags |= DO_FOR_EACH_OMIT_DANGLING_SYMREFS; + } + } iter = refs->be->iterator_begin(refs, prefix, flags); @@ -1479,7 +1482,8 @@ static int do_for_each_ref_helper(struct repository *r, } static int do_for_each_ref(struct ref_store *refs, const char *prefix, - each_ref_fn fn, int trim, int flags, void *cb_data) + each_ref_fn fn, int trim, + enum do_for_each_ref_flags flags, void *cb_data) { struct ref_iterator *iter; struct do_for_each_ref_help hp = { fn, cb_data }; @@ -1514,25 +1518,16 @@ int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data) return refs_for_each_ref_in(get_main_ref_store(the_repository), prefix, fn, cb_data); } -int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken) +int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data) { - unsigned int flag = 0; - - if (broken) - flag = DO_FOR_EACH_INCLUDE_BROKEN; return do_for_each_ref(get_main_ref_store(the_repository), - prefix, fn, 0, flag, cb_data); + prefix, fn, 0, 0, cb_data); } int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix, - each_ref_fn fn, void *cb_data, - unsigned int broken) + each_ref_fn fn, void *cb_data) { - unsigned int flag = 0; - - if (broken) - flag = DO_FOR_EACH_INCLUDE_BROKEN; - return do_for_each_ref(refs, prefix, fn, 0, flag, cb_data); + return do_for_each_ref(refs, prefix, fn, 0, 0, cb_data); } int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data) @@ -1624,8 +1619,7 @@ static void find_longest_prefixes(struct string_list *out, int for_each_fullref_in_prefixes(const char *namespace, const char **patterns, - each_ref_fn fn, void *cb_data, - unsigned int broken) + each_ref_fn fn, void *cb_data) { struct string_list prefixes = STRING_LIST_INIT_DUP; struct string_list_item *prefix; @@ -1640,7 +1634,7 @@ int for_each_fullref_in_prefixes(const char *namespace, for_each_string_list_item(prefix, &prefixes) { strbuf_addstr(&buf, prefix->string); - ret = for_each_fullref_in(buf.buf, fn, cb_data, broken); + ret = for_each_fullref_in(buf.buf, fn, cb_data); if (ret) break; strbuf_setlen(&buf, namespace_len); @@ -1681,7 +1675,7 @@ int refs_read_raw_ref(struct ref_store *ref_store, } return ref_store->be->read_raw_ref(ref_store, refname, oid, referent, - type); + type, &errno); } /* This function needs to return a meaningful errno on failure */ @@ -2370,19 +2364,19 @@ int delete_reflog(const char *refname) } int refs_reflog_expire(struct ref_store *refs, - const char *refname, const struct object_id *oid, + const char *refname, unsigned int flags, reflog_expiry_prepare_fn prepare_fn, reflog_expiry_should_prune_fn should_prune_fn, reflog_expiry_cleanup_fn cleanup_fn, void *policy_cb_data) { - return refs->be->reflog_expire(refs, refname, oid, flags, + return refs->be->reflog_expire(refs, refname, flags, prepare_fn, should_prune_fn, cleanup_fn, policy_cb_data); } -int reflog_expire(const char *refname, const struct object_id *oid, +int reflog_expire(const char *refname, unsigned int flags, reflog_expiry_prepare_fn prepare_fn, reflog_expiry_should_prune_fn should_prune_fn, @@ -2390,7 +2384,7 @@ int reflog_expire(const char *refname, const struct object_id *oid, void *policy_cb_data) { return refs_reflog_expire(get_main_ref_store(the_repository), - refname, oid, flags, + refname, flags, prepare_fn, should_prune_fn, cleanup_fn, policy_cb_data); } @@ -342,10 +342,8 @@ int for_each_ref(each_ref_fn fn, void *cb_data); int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data); int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix, - each_ref_fn fn, void *cb_data, - unsigned int broken); -int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, - unsigned int broken); + each_ref_fn fn, void *cb_data); +int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data); /** * iterate all refs in "patterns" by partitioning patterns into disjoint sets @@ -354,8 +352,7 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, * callers should be prepared to ignore references that they did not ask for. */ int for_each_fullref_in_prefixes(const char *namespace, const char **patterns, - each_ref_fn fn, void *cb_data, - unsigned int broken); + each_ref_fn fn, void *cb_data); /** * iterate refs from the respective area. */ @@ -796,7 +793,7 @@ enum expire_reflog_flags { * expiration policy that is desired. * * reflog_expiry_prepare_fn -- Called once after the reference is - * locked. + * locked. Called with the OID of the locked reference. * * reflog_expiry_should_prune_fn -- Called once for each entry in the * existing reflog. It should return true iff that entry should be @@ -816,28 +813,25 @@ typedef int reflog_expiry_should_prune_fn(struct object_id *ooid, typedef void reflog_expiry_cleanup_fn(void *cb_data); /* - * Expire reflog entries for the specified reference. oid is the old - * value of the reference. flags is a combination of the constants in + * Expire reflog entries for the specified reference. + * flags is a combination of the constants in * enum expire_reflog_flags. The three function pointers are described * above. On success, return zero. */ int refs_reflog_expire(struct ref_store *refs, const char *refname, - const struct object_id *oid, unsigned int flags, reflog_expiry_prepare_fn prepare_fn, reflog_expiry_should_prune_fn should_prune_fn, reflog_expiry_cleanup_fn cleanup_fn, void *policy_cb_data); -int reflog_expire(const char *refname, const struct object_id *oid, +int reflog_expire(const char *refname, unsigned int flags, reflog_expiry_prepare_fn prepare_fn, reflog_expiry_should_prune_fn should_prune_fn, reflog_expiry_cleanup_fn cleanup_fn, void *policy_cb_data); -int ref_storage_backend_exists(const char *name); - struct ref_store *get_main_ref_store(struct repository *r); /** diff --git a/refs/debug.c b/refs/debug.c index 1a7a9e11cf..8667c64023 100644 --- a/refs/debug.c +++ b/refs/debug.c @@ -239,15 +239,14 @@ debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix, static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname, struct object_id *oid, struct strbuf *referent, - unsigned int *type) + unsigned int *type, int *failure_errno) { struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; int res = 0; oidcpy(oid, null_oid()); - errno = 0; res = drefs->refs->be->read_raw_ref(drefs->refs, refname, oid, referent, - type); + type, failure_errno); if (res == 0) { trace_printf_key(&trace_refs, "read_raw_ref: %s: %s (=> %s) type %x: %d\n", @@ -255,7 +254,7 @@ static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname, } else { trace_printf_key(&trace_refs, "read_raw_ref: %s: %d (errno %d)\n", refname, - res, errno); + res, *failure_errno); } return res; } @@ -365,8 +364,8 @@ struct debug_reflog_expiry_should_prune { }; static void debug_reflog_expiry_prepare(const char *refname, - const struct object_id *oid, - void *cb_data) + const struct object_id *oid, + void *cb_data) { struct debug_reflog_expiry_should_prune *prune = cb_data; trace_printf_key(&trace_refs, "reflog_expire_prepare: %s\n", refname); @@ -392,7 +391,7 @@ static void debug_reflog_expiry_cleanup(void *cb_data) } static int debug_reflog_expire(struct ref_store *ref_store, const char *refname, - const struct object_id *oid, unsigned int flags, + unsigned int flags, reflog_expiry_prepare_fn prepare_fn, reflog_expiry_should_prune_fn should_prune_fn, reflog_expiry_cleanup_fn cleanup_fn, @@ -405,7 +404,7 @@ static int debug_reflog_expire(struct ref_store *ref_store, const char *refname, .should_prune = should_prune_fn, .cb_data = policy_cb_data, }; - int res = drefs->refs->be->reflog_expire(drefs->refs, refname, oid, + int res = drefs->refs->be->reflog_expire(drefs->refs, refname, flags, &debug_reflog_expiry_prepare, &debug_reflog_expiry_should_prune_fn, &debug_reflog_expiry_cleanup, diff --git a/refs/files-backend.c b/refs/files-backend.c index 74c0385873..6a6ead0b99 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -227,7 +227,7 @@ static void add_per_worktree_entries_to_dir(struct ref_dir *dir, const char *dir pos = search_ref_dir(dir, prefix, prefix_len); if (pos >= 0) continue; - child_entry = create_dir_entry(dir->cache, prefix, prefix_len, 1); + child_entry = create_dir_entry(dir->cache, prefix, prefix_len); add_entry_to_dir(dir, child_entry); } } @@ -278,7 +278,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, strbuf_addch(&refname, '/'); add_entry_to_dir(dir, create_dir_entry(dir->cache, refname.buf, - refname.len, 1)); + refname.len)); } else { if (!refs_resolve_ref_unsafe(&refs->base, refname.buf, @@ -336,14 +336,14 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs) * lazily): */ add_entry_to_dir(get_ref_dir(refs->loose->root), - create_dir_entry(refs->loose, "refs/", 5, 1)); + create_dir_entry(refs->loose, "refs/", 5)); } return refs->loose; } -static int files_read_raw_ref(struct ref_store *ref_store, - const char *refname, struct object_id *oid, - struct strbuf *referent, unsigned int *type) +static int files_read_raw_ref(struct ref_store *ref_store, const char *refname, + struct object_id *oid, struct strbuf *referent, + unsigned int *type, int *failure_errno) { struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_READ, "read_raw_ref"); @@ -354,7 +354,6 @@ static int files_read_raw_ref(struct ref_store *ref_store, struct stat st; int fd; int ret = -1; - int save_errno; int remaining_retries = 3; *type = 0; @@ -459,10 +458,9 @@ stat_ref: ret = parse_loose_ref_contents(buf, oid, referent, type); out: - save_errno = errno; + *failure_errno = errno; strbuf_release(&sb_path); strbuf_release(&sb_contents); - errno = save_errno; return ret; } @@ -531,7 +529,6 @@ static void unlock_ref(struct ref_lock *lock) static int lock_raw_ref(struct files_ref_store *refs, const char *refname, int mustexist, const struct string_list *extras, - const struct string_list *skip, struct ref_lock **lock_p, struct strbuf *referent, unsigned int *type, @@ -541,6 +538,7 @@ static int lock_raw_ref(struct files_ref_store *refs, struct strbuf ref_file = STRBUF_INIT; int attempts_remaining = 3; int ret = TRANSACTION_GENERIC_ERROR; + int failure_errno; assert(err); files_assert_main_repository(refs, "lock_raw_ref"); @@ -568,7 +566,7 @@ retry: * reason to expect this error to be transitory. */ if (refs_verify_refname_available(&refs->base, refname, - extras, skip, err)) { + extras, NULL, err)) { if (mustexist) { /* * To the user the relevant error is @@ -611,7 +609,9 @@ retry: if (hold_lock_file_for_update_timeout( &lock->lk, ref_file.buf, LOCK_NO_DEREF, get_files_ref_lock_timeout_ms()) < 0) { - if (errno == ENOENT && --attempts_remaining > 0) { + int myerr = errno; + errno = 0; + if (myerr == ENOENT && --attempts_remaining > 0) { /* * Maybe somebody just deleted one of the * directories leading to ref_file. Try @@ -619,7 +619,7 @@ retry: */ goto retry; } else { - unable_to_lock_message(ref_file.buf, errno, err); + unable_to_lock_message(ref_file.buf, myerr, err); goto error_return; } } @@ -629,9 +629,9 @@ retry: * fear that its value will change. */ - if (files_read_raw_ref(&refs->base, refname, - &lock->old_oid, referent, type)) { - if (errno == ENOENT) { + if (files_read_raw_ref(&refs->base, refname, &lock->old_oid, referent, + type, &failure_errno)) { + if (failure_errno == ENOENT) { if (mustexist) { /* Garden variety missing reference. */ strbuf_addf(err, "unable to resolve reference '%s'", @@ -655,7 +655,7 @@ retry: * reference named "refs/foo/bar/baz". */ } - } else if (errno == EISDIR) { + } else if (failure_errno == EISDIR) { /* * There is a directory in the way. It might have * contained references that have been deleted. If @@ -673,7 +673,7 @@ retry: REMOVE_DIR_EMPTY_ONLY)) { if (refs_verify_refname_available( &refs->base, refname, - extras, skip, err)) { + extras, NULL, err)) { /* * The error message set by * verify_refname_available() is OK. @@ -693,13 +693,13 @@ retry: goto error_return; } } - } else if (errno == EINVAL && (*type & REF_ISBROKEN)) { + } else if (failure_errno == EINVAL && (*type & REF_ISBROKEN)) { strbuf_addf(err, "unable to resolve reference '%s': " "reference broken", refname); goto error_return; } else { strbuf_addf(err, "unable to resolve reference '%s': %s", - refname, strerror(errno)); + refname, strerror(failure_errno)); goto error_return; } @@ -710,7 +710,7 @@ retry: */ if (refs_verify_refname_available( refs->packed_ref_store, refname, - extras, skip, err)) + extras, NULL, err)) goto error_return; } @@ -744,6 +744,11 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator) ref_type(iter->iter0->refname) != REF_TYPE_PER_WORKTREE) continue; + if ((iter->flags & DO_FOR_EACH_OMIT_DANGLING_SYMREFS) && + (iter->iter0->flags & REF_ISSYMREF) && + (iter->iter0->flags & REF_ISBROKEN)) + continue; + if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) && !ref_resolves_to_object(iter->iter0->refname, iter->iter0->oid, @@ -854,39 +859,112 @@ static struct ref_iterator *files_ref_iterator_begin( } /* - * Verify that the reference locked by lock has the value old_oid - * (unless it is NULL). Fail if the reference doesn't exist and - * mustexist is set. Return 0 on success. On error, write an error - * message to err, set errno, and return a negative value. + * Callback function for raceproof_create_file(). This function is + * expected to do something that makes dirname(path) permanent despite + * the fact that other processes might be cleaning up empty + * directories at the same time. Usually it will create a file named + * path, but alternatively it could create another file in that + * directory, or even chdir() into that directory. The function should + * return 0 if the action was completed successfully. On error, it + * should return a nonzero result and set errno. + * raceproof_create_file() treats two errno values specially: + * + * - ENOENT -- dirname(path) does not exist. In this case, + * raceproof_create_file() tries creating dirname(path) + * (and any parent directories, if necessary) and calls + * the function again. + * + * - EISDIR -- the file already exists and is a directory. In this + * case, raceproof_create_file() removes the directory if + * it is empty (and recursively any empty directories that + * it contains) and calls the function again. + * + * Any other errno causes raceproof_create_file() to fail with the + * callback's return value and errno. + * + * Obviously, this function should be OK with being called again if it + * fails with ENOENT or EISDIR. In other scenarios it will not be + * called again. + */ +typedef int create_file_fn(const char *path, void *cb); + +/* + * Create a file in dirname(path) by calling fn, creating leading + * directories if necessary. Retry a few times in case we are racing + * with another process that is trying to clean up the directory that + * contains path. See the documentation for create_file_fn for more + * details. + * + * Return the value and set the errno that resulted from the most + * recent call of fn. fn is always called at least once, and will be + * called more than once if it returns ENOENT or EISDIR. */ -static int verify_lock(struct ref_store *ref_store, struct ref_lock *lock, - const struct object_id *old_oid, int mustexist, - struct strbuf *err) +static int raceproof_create_file(const char *path, create_file_fn fn, void *cb) { - assert(err); + /* + * The number of times we will try to remove empty directories + * in the way of path. This is only 1 because if another + * process is racily creating directories that conflict with + * us, we don't want to fight against them. + */ + int remove_directories_remaining = 1; - if (refs_read_ref_full(ref_store, lock->ref_name, - mustexist ? RESOLVE_REF_READING : 0, - &lock->old_oid, NULL)) { - if (old_oid) { - int save_errno = errno; - strbuf_addf(err, "can't verify ref '%s'", lock->ref_name); - errno = save_errno; - return -1; - } else { - oidclr(&lock->old_oid); - return 0; - } - } - if (old_oid && !oideq(&lock->old_oid, old_oid)) { - strbuf_addf(err, "ref '%s' is at %s but expected %s", - lock->ref_name, - oid_to_hex(&lock->old_oid), - oid_to_hex(old_oid)); - errno = EBUSY; - return -1; + /* + * The number of times that we will try to create the + * directories containing path. We are willing to attempt this + * more than once, because another process could be trying to + * clean up empty directories at the same time as we are + * trying to create them. + */ + int create_directories_remaining = 3; + + /* A scratch copy of path, filled lazily if we need it: */ + struct strbuf path_copy = STRBUF_INIT; + + int ret, save_errno; + + /* Sanity check: */ + assert(*path); + +retry_fn: + ret = fn(path, cb); + save_errno = errno; + if (!ret) + goto out; + + if (errno == EISDIR && remove_directories_remaining-- > 0) { + /* + * A directory is in the way. Maybe it is empty; try + * to remove it: + */ + if (!path_copy.len) + strbuf_addstr(&path_copy, path); + + if (!remove_dir_recursively(&path_copy, REMOVE_DIR_EMPTY_ONLY)) + goto retry_fn; + } else if (errno == ENOENT && create_directories_remaining-- > 0) { + /* + * Maybe the containing directory didn't exist, or + * maybe it was just deleted by a process that is + * racing with us to clean up empty directories. Try + * to create it: + */ + enum scld_error scld_result; + + if (!path_copy.len) + strbuf_addstr(&path_copy, path); + + do { + scld_result = safe_create_leading_directories(path_copy.buf); + if (scld_result == SCLD_OK) + goto retry_fn; + } while (scld_result == SCLD_VANISHED && create_directories_remaining-- > 0); } - return 0; + +out: + strbuf_release(&path_copy); + errno = save_errno; + return ret; } static int remove_empty_directories(struct strbuf *path) @@ -910,64 +988,27 @@ static int create_reflock(const char *path, void *cb) /* * Locks a ref returning the lock on success and NULL on failure. - * On failure errno is set to something meaningful. */ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs, - const char *refname, - const struct object_id *old_oid, - const struct string_list *extras, - const struct string_list *skip, - unsigned int flags, int *type, + const char *refname, int *type, struct strbuf *err) { struct strbuf ref_file = STRBUF_INIT; struct ref_lock *lock; - int last_errno = 0; - int mustexist = (old_oid && !is_null_oid(old_oid)); - int resolve_flags = RESOLVE_REF_NO_RECURSE; - int resolved; files_assert_main_repository(refs, "lock_ref_oid_basic"); assert(err); CALLOC_ARRAY(lock, 1); - if (mustexist) - resolve_flags |= RESOLVE_REF_READING; - if (flags & REF_DELETING) - resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME; - files_ref_path(refs, &ref_file, refname); - resolved = !!refs_resolve_ref_unsafe(&refs->base, - refname, resolve_flags, - &lock->old_oid, type); - if (!resolved && errno == EISDIR) { - /* - * we are trying to lock foo but we used to - * have foo/bar which now does not exist; - * it is normal for the empty directory 'foo' - * to remain. - */ - if (remove_empty_directories(&ref_file)) { - last_errno = errno; - if (!refs_verify_refname_available( - &refs->base, - refname, extras, skip, err)) - strbuf_addf(err, "there are still refs under '%s'", - refname); - goto error_return; - } - resolved = !!refs_resolve_ref_unsafe(&refs->base, - refname, resolve_flags, - &lock->old_oid, type); - } - if (!resolved) { - last_errno = errno; - if (last_errno != ENOTDIR || - !refs_verify_refname_available(&refs->base, refname, - extras, skip, err)) + if (!refs_resolve_ref_unsafe(&refs->base, refname, + RESOLVE_REF_NO_RECURSE, + &lock->old_oid, type)) { + if (!refs_verify_refname_available(&refs->base, refname, + NULL, NULL, err)) strbuf_addf(err, "unable to resolve reference '%s': %s", - refname, strerror(last_errno)); + refname, strerror(errno)); goto error_return; } @@ -980,23 +1021,20 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs, */ if (is_null_oid(&lock->old_oid) && refs_verify_refname_available(refs->packed_ref_store, refname, - extras, skip, err)) { - last_errno = ENOTDIR; + NULL, NULL, err)) goto error_return; - } lock->ref_name = xstrdup(refname); if (raceproof_create_file(ref_file.buf, create_reflock, &lock->lk)) { - last_errno = errno; unable_to_lock_message(ref_file.buf, errno, err); goto error_return; } - if (verify_lock(&refs->base, lock, old_oid, mustexist, err)) { - last_errno = errno; - goto error_return; - } + if (refs_read_ref_full(&refs->base, lock->ref_name, + 0, + &lock->old_oid, NULL)) + oidclr(&lock->old_oid); goto out; error_return: @@ -1005,7 +1043,6 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs, out: strbuf_release(&ref_file); - errno = last_errno; return lock; } @@ -1415,8 +1452,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store, logmoved = log; - lock = lock_ref_oid_basic(refs, newrefname, NULL, NULL, NULL, - REF_NO_DEREF, NULL, &err); + lock = lock_ref_oid_basic(refs, newrefname, NULL, &err); if (!lock) { if (copy) error("unable to copy '%s' to '%s': %s", oldrefname, newrefname, err.buf); @@ -1438,8 +1474,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store, goto out; rollback: - lock = lock_ref_oid_basic(refs, oldrefname, NULL, NULL, NULL, - REF_NO_DEREF, NULL, &err); + lock = lock_ref_oid_basic(refs, oldrefname, NULL, &err); if (!lock) { error("unable to lock %s for rollback: %s", oldrefname, err.buf); strbuf_release(&err); @@ -1846,9 +1881,7 @@ static int files_create_symref(struct ref_store *ref_store, struct ref_lock *lock; int ret; - lock = lock_ref_oid_basic(refs, refname, NULL, - NULL, NULL, REF_NO_DEREF, NULL, - &err); + lock = lock_ref_oid_basic(refs, refname, NULL, &err); if (!lock) { error("%s", err.buf); strbuf_release(&err); @@ -2416,7 +2449,7 @@ static int lock_ref_for_update(struct files_ref_store *refs, } ret = lock_raw_ref(refs, update->refname, mustexist, - affected_refnames, NULL, + affected_refnames, &lock, &referent, &update->type, err); if (ret) { @@ -3037,7 +3070,7 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid, } static int files_reflog_expire(struct ref_store *ref_store, - const char *refname, const struct object_id *oid, + const char *refname, unsigned int flags, reflog_expiry_prepare_fn prepare_fn, reflog_expiry_should_prune_fn should_prune_fn, @@ -3054,6 +3087,7 @@ static int files_reflog_expire(struct ref_store *ref_store, int status = 0; int type; struct strbuf err = STRBUF_INIT; + const struct object_id *oid; memset(&cb, 0, sizeof(cb)); cb.flags = flags; @@ -3065,14 +3099,26 @@ static int files_reflog_expire(struct ref_store *ref_store, * reference itself, plus we might need to update the * reference if --updateref was specified: */ - lock = lock_ref_oid_basic(refs, refname, oid, - NULL, NULL, REF_NO_DEREF, - &type, &err); + lock = lock_ref_oid_basic(refs, refname, &type, &err); if (!lock) { error("cannot lock ref '%s': %s", refname, err.buf); strbuf_release(&err); return -1; } + oid = &lock->old_oid; + + /* + * When refs are deleted, their reflog is deleted before the + * ref itself is deleted. This is because there is no separate + * lock for reflog; instead we take a lock on the ref with + * lock_ref_oid_basic(). + * + * If a race happens and the reflog doesn't exist after we've + * acquired the lock that's OK. We've got nothing more to do; + * We were asked to delete the reflog, but someone else + * deleted it! The caller doesn't care that we deleted it, + * just that it is deleted. So we can return successfully. + */ if (!refs_reflog_exists(ref_store, refname)) { unlock_ref(lock); return 0; diff --git a/refs/packed-backend.c b/refs/packed-backend.c index f8aa97d799..47247a1491 100644 --- a/refs/packed-backend.c +++ b/refs/packed-backend.c @@ -724,9 +724,9 @@ static struct snapshot *get_snapshot(struct packed_ref_store *refs) return refs->snapshot; } -static int packed_read_raw_ref(struct ref_store *ref_store, - const char *refname, struct object_id *oid, - struct strbuf *referent, unsigned int *type) +static int packed_read_raw_ref(struct ref_store *ref_store, const char *refname, + struct object_id *oid, struct strbuf *referent, + unsigned int *type, int *failure_errno) { struct packed_ref_store *refs = packed_downcast(ref_store, REF_STORE_READ, "read_raw_ref"); @@ -739,7 +739,7 @@ static int packed_read_raw_ref(struct ref_store *ref_store, if (!rec) { /* refname is not a packed reference. */ - errno = ENOENT; + *failure_errno = ENOENT; return -1; } @@ -1600,6 +1600,7 @@ static int packed_for_each_reflog_ent(struct ref_store *ref_store, const char *refname, each_reflog_ent_fn fn, void *cb_data) { + BUG("packed reference store does not support reflogs"); return 0; } @@ -1608,12 +1609,14 @@ static int packed_for_each_reflog_ent_reverse(struct ref_store *ref_store, each_reflog_ent_fn fn, void *cb_data) { + BUG("packed reference store does not support reflogs"); return 0; } static int packed_reflog_exists(struct ref_store *ref_store, const char *refname) { + BUG("packed reference store does not support reflogs"); return 0; } @@ -1627,17 +1630,19 @@ static int packed_create_reflog(struct ref_store *ref_store, static int packed_delete_reflog(struct ref_store *ref_store, const char *refname) { + BUG("packed reference store does not support reflogs"); return 0; } static int packed_reflog_expire(struct ref_store *ref_store, - const char *refname, const struct object_id *oid, + const char *refname, unsigned int flags, reflog_expiry_prepare_fn prepare_fn, reflog_expiry_should_prune_fn should_prune_fn, reflog_expiry_cleanup_fn cleanup_fn, void *policy_cb_data) { + BUG("packed reference store does not support reflogs"); return 0; } diff --git a/refs/ref-cache.c b/refs/ref-cache.c index 49d732f6db..a5ad8a39fb 100644 --- a/refs/ref-cache.c +++ b/refs/ref-cache.c @@ -49,7 +49,7 @@ struct ref_cache *create_ref_cache(struct ref_store *refs, ret->ref_store = refs; ret->fill_ref_dir = fill_ref_dir; - ret->root = create_dir_entry(ret, "", 0, 1); + ret->root = create_dir_entry(ret, "", 0); return ret; } @@ -86,14 +86,13 @@ static void clear_ref_dir(struct ref_dir *dir) } struct ref_entry *create_dir_entry(struct ref_cache *cache, - const char *dirname, size_t len, - int incomplete) + const char *dirname, size_t len) { struct ref_entry *direntry; FLEX_ALLOC_MEM(direntry, name, dirname, len); direntry->u.subdir.cache = cache; - direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0); + direntry->flag = REF_DIR | REF_INCOMPLETE; return direntry; } @@ -144,30 +143,19 @@ int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len) /* * Search for a directory entry directly within dir (without * recursing). Sort dir if necessary. subdirname must be a directory - * name (i.e., end in '/'). If mkdir is set, then create the - * directory if it is missing; otherwise, return NULL if the desired + * name (i.e., end in '/'). Returns NULL if the desired * directory cannot be found. dir must already be complete. */ static struct ref_dir *search_for_subdir(struct ref_dir *dir, - const char *subdirname, size_t len, - int mkdir) + const char *subdirname, size_t len) { int entry_index = search_ref_dir(dir, subdirname, len); struct ref_entry *entry; - if (entry_index == -1) { - if (!mkdir) - return NULL; - /* - * Since dir is complete, the absence of a subdir - * means that the subdir really doesn't exist; - * therefore, create an empty record for it but mark - * the record complete. - */ - entry = create_dir_entry(dir->cache, subdirname, len, 0); - add_entry_to_dir(dir, entry); - } else { - entry = dir->entries[entry_index]; - } + + if (entry_index == -1) + return NULL; + + entry = dir->entries[entry_index]; return get_ref_dir(entry); } @@ -176,18 +164,17 @@ static struct ref_dir *search_for_subdir(struct ref_dir *dir, * tree that should hold refname. If refname is a directory name * (i.e., it ends in '/'), then return that ref_dir itself. dir must * represent the top-level directory and must already be complete. - * Sort ref_dirs and recurse into subdirectories as necessary. If - * mkdir is set, then create any missing directories; otherwise, + * Sort ref_dirs and recurse into subdirectories as necessary. Will * return NULL if the desired directory cannot be found. */ static struct ref_dir *find_containing_dir(struct ref_dir *dir, - const char *refname, int mkdir) + const char *refname) { const char *slash; for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) { size_t dirnamelen = slash - refname + 1; struct ref_dir *subdir; - subdir = search_for_subdir(dir, refname, dirnamelen, mkdir); + subdir = search_for_subdir(dir, refname, dirnamelen); if (!subdir) { dir = NULL; break; @@ -202,7 +189,7 @@ struct ref_entry *find_ref_entry(struct ref_dir *dir, const char *refname) { int entry_index; struct ref_entry *entry; - dir = find_containing_dir(dir, refname, 0); + dir = find_containing_dir(dir, refname); if (!dir) return NULL; entry_index = search_ref_dir(dir, refname, strlen(refname)); @@ -212,50 +199,6 @@ struct ref_entry *find_ref_entry(struct ref_dir *dir, const char *refname) return (entry->flag & REF_DIR) ? NULL : entry; } -int remove_entry_from_dir(struct ref_dir *dir, const char *refname) -{ - int refname_len = strlen(refname); - int entry_index; - struct ref_entry *entry; - int is_dir = refname[refname_len - 1] == '/'; - if (is_dir) { - /* - * refname represents a reference directory. Remove - * the trailing slash; otherwise we will get the - * directory *representing* refname rather than the - * one *containing* it. - */ - char *dirname = xmemdupz(refname, refname_len - 1); - dir = find_containing_dir(dir, dirname, 0); - free(dirname); - } else { - dir = find_containing_dir(dir, refname, 0); - } - if (!dir) - return -1; - entry_index = search_ref_dir(dir, refname, refname_len); - if (entry_index == -1) - return -1; - entry = dir->entries[entry_index]; - - MOVE_ARRAY(&dir->entries[entry_index], - &dir->entries[entry_index + 1], dir->nr - entry_index - 1); - dir->nr--; - if (dir->sorted > entry_index) - dir->sorted--; - free_ref_entry(entry); - return dir->nr; -} - -int add_ref_entry(struct ref_dir *dir, struct ref_entry *ref) -{ - dir = find_containing_dir(dir, ref->name, 1); - if (!dir) - return -1; - add_entry_to_dir(dir, ref); - return 0; -} - /* * Emit a warning and return true iff ref1 and ref2 have the same name * and the same oid. Die if they have the same name but different @@ -522,7 +465,7 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache, dir = get_ref_dir(cache->root); if (prefix && *prefix) - dir = find_containing_dir(dir, prefix, 0); + dir = find_containing_dir(dir, prefix); if (!dir) /* There's nothing to iterate over. */ return empty_ref_iterator_begin(); diff --git a/refs/ref-cache.h b/refs/ref-cache.h index 3bfb89d2b3..5c042ae718 100644 --- a/refs/ref-cache.h +++ b/refs/ref-cache.h @@ -169,8 +169,7 @@ struct ref_dir *get_ref_dir(struct ref_entry *entry); * "refs/heads/") or "" for the top-level directory. */ struct ref_entry *create_dir_entry(struct ref_cache *cache, - const char *dirname, size_t len, - int incomplete); + const char *dirname, size_t len); struct ref_entry *create_ref_entry(const char *refname, const struct object_id *oid, int flag); @@ -200,29 +199,6 @@ void free_ref_cache(struct ref_cache *cache); void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry); /* - * Remove the entry with the given name from dir, recursing into - * subdirectories as necessary. If refname is the name of a directory - * (i.e., ends with '/'), then remove the directory and its contents. - * If the removal was successful, return the number of entries - * remaining in the directory entry that contained the deleted entry. - * If the name was not found, return -1. Please note that this - * function only deletes the entry from the cache; it does not delete - * it from the filesystem or ensure that other cache entries (which - * might be symbolic references to the removed entry) are updated. - * Nor does it remove any containing dir entries that might be made - * empty by the removal. dir must represent the top-level directory - * and must already be complete. - */ -int remove_entry_from_dir(struct ref_dir *dir, const char *refname); - -/* - * Add a ref_entry to the ref_dir (unsorted), recursing into - * subdirectories as necessary. dir must represent the top-level - * directory. Return 0 on success. - */ -int add_ref_entry(struct ref_dir *dir, struct ref_entry *ref); - -/* * Find the value entry with the given name in dir, sorting ref_dirs * and recursing into subdirectories as necessary. If the name is not * found or it corresponds to a directory entry, return NULL. diff --git a/refs/refs-internal.h b/refs/refs-internal.h index 3155708345..72746407fc 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -245,8 +245,36 @@ int refs_rename_ref_available(struct ref_store *refs, /* We allow "recursive" symbolic refs. Only within reason, though */ #define SYMREF_MAXDEPTH 5 -/* Include broken references in a do_for_each_ref*() iteration: */ -#define DO_FOR_EACH_INCLUDE_BROKEN 0x01 +/* + * These flags are passed to refs_ref_iterator_begin() (and do_for_each_ref(), + * which feeds it). + */ +enum do_for_each_ref_flags { + /* + * Include broken references in a do_for_each_ref*() iteration, which + * would normally be omitted. This includes both refs that point to + * missing objects (a true repository corruption), ones with illegal + * names (which we prefer not to expose to callers), as well as + * dangling symbolic refs (i.e., those that point to a non-existent + * ref; this is not a corruption, but as they have no valid oid, we + * omit them from normal iteration results). + */ + DO_FOR_EACH_INCLUDE_BROKEN = (1 << 0), + + /* + * Only include per-worktree refs in a do_for_each_ref*() iteration. + * Normally this will be used with a files ref_store, since that's + * where all reference backends will presumably store their + * per-worktree refs. + */ + DO_FOR_EACH_PER_WORKTREE_ONLY = (1 << 1), + + /* + * Omit dangling symrefs from output; this only has an effect with + * INCLUDE_BROKEN, since they are otherwise not included at all. + */ + DO_FOR_EACH_OMIT_DANGLING_SYMREFS = (1 << 2), +}; /* * Reference iterators @@ -349,16 +377,12 @@ int is_empty_ref_iterator(struct ref_iterator *ref_iterator); * Return an iterator that goes over each reference in `refs` for * which the refname begins with prefix. If trim is non-zero, then * trim that many characters off the beginning of each refname. - * The output is ordered by refname. The following flags are supported: - * - * DO_FOR_EACH_INCLUDE_BROKEN: include broken references in - * the iteration. - * - * DO_FOR_EACH_PER_WORKTREE_ONLY: only produce REF_TYPE_PER_WORKTREE refs. + * The output is ordered by refname. */ struct ref_iterator *refs_ref_iterator_begin( struct ref_store *refs, - const char *prefix, int trim, int flags); + const char *prefix, int trim, + enum do_for_each_ref_flags flags); /* * A callback function used to instruct merge_ref_iterator how to @@ -446,10 +470,8 @@ void base_ref_iterator_free(struct ref_iterator *iter); /* * backend-specific implementation of ref_iterator_advance. For symrefs, the * function should set REF_ISSYMREF, and it should also dereference the symref - * to provide the OID referent. If DO_FOR_EACH_INCLUDE_BROKEN is set, symrefs - * with non-existent referents and refs pointing to non-existent object names - * should also be returned. If DO_FOR_EACH_PER_WORKTREE_ONLY, only - * REF_TYPE_PER_WORKTREE refs should be returned. + * to provide the OID referent. It should respect do_for_each_ref_flags + * that were passed to refs_ref_iterator_begin(). */ typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator); @@ -498,14 +520,6 @@ int do_for_each_repo_ref_iterator(struct repository *r, struct ref_iterator *iter, each_repo_ref_fn fn, void *cb_data); -/* - * Only include per-worktree refs in a do_for_each_ref*() iteration. - * Normally this will be used with a files ref_store, since that's - * where all reference backends will presumably store their - * per-worktree refs. - */ -#define DO_FOR_EACH_PER_WORKTREE_ONLY 0x02 - struct ref_store; /* refs backends */ @@ -593,7 +607,7 @@ typedef int create_reflog_fn(struct ref_store *ref_store, const char *refname, int force_create, struct strbuf *err); typedef int delete_reflog_fn(struct ref_store *ref_store, const char *refname); typedef int reflog_expire_fn(struct ref_store *ref_store, - const char *refname, const struct object_id *oid, + const char *refname, unsigned int flags, reflog_expiry_prepare_fn prepare_fn, reflog_expiry_should_prune_fn should_prune_fn, @@ -620,11 +634,15 @@ typedef int reflog_expire_fn(struct ref_store *ref_store, * properly-formatted or even safe reference name. NEITHER INPUT NOR * OUTPUT REFERENCE NAMES ARE VALIDATED WITHIN THIS FUNCTION. * - * Return 0 on success. If the ref doesn't exist, set errno to ENOENT - * and return -1. If the ref exists but is neither a symbolic ref nor - * an object ID, it is broken; set REF_ISBROKEN in type, set errno to - * EINVAL, and return -1. If there is another error reading the ref, - * set errno appropriately and return -1. + * Return 0 on success, or -1 on failure. If the ref exists but is neither a + * symbolic ref nor an object ID, it is broken. In this case set REF_ISBROKEN in + * type, and return -1 (failure_errno should not be ENOENT) + * + * failure_errno provides errno codes that are interpreted beyond error + * reporting. The following error codes have special meaning: + * * ENOENT: the ref doesn't exist + * * EISDIR: ref name is a directory + * * ENOTDIR: ref prefix is not a directory * * Backend-specific flags might be set in type as well, regardless of * outcome. @@ -638,9 +656,9 @@ typedef int reflog_expire_fn(struct ref_store *ref_store, * - in all other cases, referent will be untouched, and therefore * refname will still be valid and unchanged. */ -typedef int read_raw_ref_fn(struct ref_store *ref_store, - const char *refname, struct object_id *oid, - struct strbuf *referent, unsigned int *type); +typedef int read_raw_ref_fn(struct ref_store *ref_store, const char *refname, + struct object_id *oid, struct strbuf *referent, + unsigned int *type, int *failure_errno); struct ref_storage_be { struct ref_storage_be *next; diff --git a/remote-curl.c b/remote-curl.c index 598cff7cde..5975103b96 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -499,6 +499,10 @@ static struct discovery *discover_refs(const char *service, int for_push) show_http_message(&type, &charset, &buffer); die(_("Authentication failed for '%s'"), transport_anonymize_url(url.buf)); + case HTTP_NOMATCHPUBLICKEY: + show_http_message(&type, &charset, &buffer); + die(_("unable to access '%s' with http.pinnedPubkey configuration: %s"), + transport_anonymize_url(url.buf), curl_errorstr); default: show_http_message(&type, &charset, &buffer); die(_("unable to access '%s': %s"), @@ -1478,8 +1482,8 @@ int cmd_main(int argc, const char **argv) options.verbosity = 1; options.progress = !!isatty(2); options.thin = 1; - string_list_init(&options.deepen_not, 1); - string_list_init(&options.push_options, 1); + string_list_init_dup(&options.deepen_not); + string_list_init_dup(&options.push_options); /* * Just report "remote-curl" here (folding all the various aliases diff --git a/repo-settings.c b/repo-settings.c index 0cfe8b787d..b93e91a212 100644 --- a/repo-settings.c +++ b/repo-settings.c @@ -3,40 +3,77 @@ #include "repository.h" #include "midx.h" -#define UPDATE_DEFAULT_BOOL(s,v) do { if (s == -1) { s = v; } } while(0) +static void repo_cfg_bool(struct repository *r, const char *key, int *dest, + int def) +{ + if (repo_config_get_bool(r, key, dest)) + *dest = def; +} void prepare_repo_settings(struct repository *r) { + int experimental; int value; char *strval; + int manyfiles; - if (r->settings.initialized) + if (r->settings.initialized++) return; /* Defaults */ - memset(&r->settings, -1, sizeof(r->settings)); + r->settings.index_version = -1; + r->settings.core_untracked_cache = UNTRACKED_CACHE_KEEP; + r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_DEFAULT; + + /* Booleans config or default, cascades to other settings */ + repo_cfg_bool(r, "feature.manyfiles", &manyfiles, 0); + repo_cfg_bool(r, "feature.experimental", &experimental, 0); + + /* Defaults modified by feature.* */ + if (experimental) { + r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_SKIPPING; + } + if (manyfiles) { + r->settings.index_version = 4; + r->settings.core_untracked_cache = UNTRACKED_CACHE_WRITE; + } - if (!repo_config_get_bool(r, "core.commitgraph", &value)) - r->settings.core_commit_graph = value; - if (!repo_config_get_bool(r, "commitgraph.readchangedpaths", &value)) - r->settings.commit_graph_read_changed_paths = value; - if (!repo_config_get_bool(r, "gc.writecommitgraph", &value)) - r->settings.gc_write_commit_graph = value; - UPDATE_DEFAULT_BOOL(r->settings.core_commit_graph, 1); - UPDATE_DEFAULT_BOOL(r->settings.commit_graph_read_changed_paths, 1); - UPDATE_DEFAULT_BOOL(r->settings.gc_write_commit_graph, 1); + /* Boolean config or default, does not cascade (simple) */ + repo_cfg_bool(r, "core.commitgraph", &r->settings.core_commit_graph, 1); + repo_cfg_bool(r, "commitgraph.readchangedpaths", &r->settings.commit_graph_read_changed_paths, 1); + repo_cfg_bool(r, "gc.writecommitgraph", &r->settings.gc_write_commit_graph, 1); + repo_cfg_bool(r, "fetch.writecommitgraph", &r->settings.fetch_write_commit_graph, 0); + repo_cfg_bool(r, "pack.usesparse", &r->settings.pack_use_sparse, 1); + repo_cfg_bool(r, "core.multipackindex", &r->settings.core_multi_pack_index, 1); + repo_cfg_bool(r, "index.sparse", &r->settings.sparse_index, 0); + /* + * The GIT_TEST_MULTI_PACK_INDEX variable is special in that + * either it *or* the config sets + * r->settings.core_multi_pack_index if true. We don't take + * the environment variable if it exists (even if false) over + * any config, as in most other cases. + */ + if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0)) + r->settings.core_multi_pack_index = 1; + + /* + * Non-boolean config + */ if (!repo_config_get_int(r, "index.version", &value)) r->settings.index_version = value; - if (!repo_config_get_maybe_bool(r, "core.untrackedcache", &value)) { - if (value == 0) - r->settings.core_untracked_cache = UNTRACKED_CACHE_REMOVE; - else - r->settings.core_untracked_cache = UNTRACKED_CACHE_WRITE; - } else if (!repo_config_get_string(r, "core.untrackedcache", &strval)) { - if (!strcasecmp(strval, "keep")) - r->settings.core_untracked_cache = UNTRACKED_CACHE_KEEP; + if (!repo_config_get_string(r, "core.untrackedcache", &strval)) { + int v = git_parse_maybe_bool(strval); + + /* + * If it's set to "keep", or some other non-boolean + * value then "v < 0". Then we do nothing and keep it + * at the default of UNTRACKED_CACHE_KEEP. + */ + if (v >= 0) + r->settings.core_untracked_cache = v ? + UNTRACKED_CACHE_WRITE : UNTRACKED_CACHE_REMOVE; free(strval); } @@ -45,39 +82,8 @@ void prepare_repo_settings(struct repository *r) r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_SKIPPING; else if (!strcasecmp(strval, "noop")) r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_NOOP; - else - r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_DEFAULT; } - if (!repo_config_get_bool(r, "pack.usesparse", &value)) - r->settings.pack_use_sparse = value; - UPDATE_DEFAULT_BOOL(r->settings.pack_use_sparse, 1); - - value = git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0); - if (value || !repo_config_get_bool(r, "core.multipackindex", &value)) - r->settings.core_multi_pack_index = value; - UPDATE_DEFAULT_BOOL(r->settings.core_multi_pack_index, 1); - - if (!repo_config_get_bool(r, "feature.manyfiles", &value) && value) { - UPDATE_DEFAULT_BOOL(r->settings.index_version, 4); - UPDATE_DEFAULT_BOOL(r->settings.core_untracked_cache, UNTRACKED_CACHE_WRITE); - } - - if (!repo_config_get_bool(r, "fetch.writecommitgraph", &value)) - r->settings.fetch_write_commit_graph = value; - UPDATE_DEFAULT_BOOL(r->settings.fetch_write_commit_graph, 0); - - if (!repo_config_get_bool(r, "feature.experimental", &value) && value) - UPDATE_DEFAULT_BOOL(r->settings.fetch_negotiation_algorithm, FETCH_NEGOTIATION_SKIPPING); - - /* Hack for test programs like test-dump-untracked-cache */ - if (ignore_untracked_cache_config) - r->settings.core_untracked_cache = UNTRACKED_CACHE_KEEP; - else - UPDATE_DEFAULT_BOOL(r->settings.core_untracked_cache, UNTRACKED_CACHE_KEEP); - - UPDATE_DEFAULT_BOOL(r->settings.fetch_negotiation_algorithm, FETCH_NEGOTIATION_DEFAULT); - /* * This setting guards all index reads to require a full index * over a sparse index. After suitable guards are placed in the @@ -85,11 +91,4 @@ void prepare_repo_settings(struct repository *r) * removed. */ r->settings.command_requires_full_index = 1; - - /* - * Initialize this as off. - */ - r->settings.sparse_index = 0; - if (!repo_config_get_bool(r, "index.sparse", &value) && value) - r->settings.sparse_index = 1; } diff --git a/repository.c b/repository.c index 710a3b4bf8..c5b90ba93e 100644 --- a/repository.c +++ b/repository.c @@ -190,19 +190,15 @@ error: int repo_submodule_init(struct repository *subrepo, struct repository *superproject, - const struct submodule *sub) + const char *path, + const struct object_id *treeish_name) { struct strbuf gitdir = STRBUF_INIT; struct strbuf worktree = STRBUF_INIT; int ret = 0; - if (!sub) { - ret = -1; - goto out; - } - - strbuf_repo_worktree_path(&gitdir, superproject, "%s/.git", sub->path); - strbuf_repo_worktree_path(&worktree, superproject, "%s", sub->path); + strbuf_repo_worktree_path(&gitdir, superproject, "%s/.git", path); + strbuf_repo_worktree_path(&worktree, superproject, "%s", path); if (repo_init(subrepo, gitdir.buf, worktree.buf)) { /* @@ -212,6 +208,13 @@ int repo_submodule_init(struct repository *subrepo, * in the superproject's 'modules' directory. In this case the * submodule would not have a worktree. */ + const struct submodule *sub = + submodule_from_path(superproject, treeish_name, path); + if (!sub) { + ret = -1; + goto out; + } + strbuf_reset(&gitdir); submodule_name_to_gitdir(&gitdir, superproject, sub->name); @@ -224,7 +227,7 @@ int repo_submodule_init(struct repository *subrepo, subrepo->submodule_prefix = xstrfmt("%s%s/", superproject->submodule_prefix ? superproject->submodule_prefix : - "", sub->path); + "", path); out: strbuf_release(&gitdir); diff --git a/repository.h b/repository.h index 3740c93bc0..a057653981 100644 --- a/repository.h +++ b/repository.h @@ -13,18 +13,15 @@ struct submodule_cache; struct promisor_remote_config; enum untracked_cache_setting { - UNTRACKED_CACHE_UNSET = -1, - UNTRACKED_CACHE_REMOVE = 0, - UNTRACKED_CACHE_KEEP = 1, - UNTRACKED_CACHE_WRITE = 2 + UNTRACKED_CACHE_KEEP, + UNTRACKED_CACHE_REMOVE, + UNTRACKED_CACHE_WRITE, }; enum fetch_negotiation_setting { - FETCH_NEGOTIATION_UNSET = -1, - FETCH_NEGOTIATION_NONE = 0, - FETCH_NEGOTIATION_DEFAULT = 1, - FETCH_NEGOTIATION_SKIPPING = 2, - FETCH_NEGOTIATION_NOOP = 3, + FETCH_NEGOTIATION_DEFAULT, + FETCH_NEGOTIATION_SKIPPING, + FETCH_NEGOTIATION_NOOP, }; struct repo_settings { @@ -34,6 +31,8 @@ struct repo_settings { int commit_graph_read_changed_paths; int gc_write_commit_graph; int fetch_write_commit_graph; + int command_requires_full_index; + int sparse_index; int index_version; enum untracked_cache_setting core_untracked_cache; @@ -42,9 +41,6 @@ struct repo_settings { enum fetch_negotiation_setting fetch_negotiation_algorithm; int core_multi_pack_index; - - unsigned command_requires_full_index:1, - sparse_index:1; }; struct repository { @@ -172,15 +168,18 @@ void initialize_the_repository(void); int repo_init(struct repository *r, const char *gitdir, const char *worktree); /* - * Initialize the repository 'subrepo' as the submodule given by the - * struct submodule 'sub' in parent repository 'superproject'. - * Return 0 upon success and a non-zero value upon failure, which may happen - * if the submodule is not found, or 'sub' is NULL. + * Initialize the repository 'subrepo' as the submodule at the given path. If + * the submodule's gitdir cannot be found at <path>/.git, this function calls + * submodule_from_path() to try to find it. treeish_name is only used if + * submodule_from_path() needs to be called; see its documentation for more + * information. + * Return 0 upon success and a non-zero value upon failure. */ -struct submodule; +struct object_id; int repo_submodule_init(struct repository *subrepo, struct repository *superproject, - const struct submodule *sub); + const char *path, + const struct object_id *treeish_name); void repo_clear(struct repository *repo); /* diff --git a/revision.c b/revision.c index 73e5004d60..ab7c135804 100644 --- a/revision.c +++ b/revision.c @@ -2548,7 +2548,7 @@ static int for_each_bisect_ref(struct ref_store *refs, each_ref_fn fn, struct strbuf bisect_refs = STRBUF_INIT; int status; strbuf_addf(&bisect_refs, "refs/bisect/%s", term); - status = refs_for_each_fullref_in(refs, bisect_refs.buf, fn, cb_data, 0); + status = refs_for_each_fullref_in(refs, bisect_refs.buf, fn, cb_data); strbuf_release(&bisect_refs); return status; } @@ -2563,8 +2563,7 @@ static int for_each_good_bisect_ref(struct ref_store *refs, each_ref_fn fn, void return for_each_bisect_ref(refs, fn, cb_data, term_good); } -static int handle_revision_pseudo_opt(const char *submodule, - struct rev_info *revs, +static int handle_revision_pseudo_opt(struct rev_info *revs, const char **argv, int *flags) { const char *arg = argv[0]; @@ -2572,7 +2571,7 @@ static int handle_revision_pseudo_opt(const char *submodule, struct ref_store *refs; int argcount; - if (submodule) { + if (revs->repo != the_repository) { /* * We need some something like get_submodule_worktrees() * before we can go through all worktrees of a submodule, @@ -2581,9 +2580,8 @@ static int handle_revision_pseudo_opt(const char *submodule, */ if (!revs->single_worktree) BUG("--single-worktree cannot be used together with submodule"); - refs = get_submodule_ref_store(submodule); - } else - refs = get_main_ref_store(revs->repo); + } + refs = get_main_ref_store(revs->repo); /* * NOTE! @@ -2707,12 +2705,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s { int i, flags, left, seen_dashdash, revarg_opt; struct strvec prune_data = STRVEC_INIT; - const char *submodule = NULL; int seen_end_of_options = 0; - if (opt) - submodule = opt->submodule; - /* First, search for "--" */ if (opt && opt->assume_dashdash) { seen_dashdash = 1; @@ -2741,7 +2735,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s if (!seen_end_of_options && *arg == '-') { int opts; - opts = handle_revision_pseudo_opt(submodule, + opts = handle_revision_pseudo_opt( revs, argv + i, &flags); if (opts > 0) { diff --git a/revision.h b/revision.h index 0c65a760ee..5578bb4720 100644 --- a/revision.h +++ b/revision.h @@ -336,7 +336,6 @@ extern volatile show_early_output_fn_t show_early_output; struct setup_revision_opt { const char *def; void (*tweak)(struct rev_info *, struct setup_revision_opt *); - const char *submodule; /* TODO: drop this and use rev_info->repo */ unsigned int assume_dashdash:1, allow_exclude_promisor_objects:1; unsigned revarg_opt; diff --git a/sequencer.c b/sequencer.c index 614d56f5e2..64b1f2e681 100644 --- a/sequencer.c +++ b/sequencer.c @@ -2693,7 +2693,6 @@ static int read_populate_todo(struct repository *r, struct todo_list *todo_list, struct replay_opts *opts) { - struct stat st; const char *todo_file = get_todo_path(opts); int res; @@ -2701,11 +2700,6 @@ static int read_populate_todo(struct repository *r, if (strbuf_read_file_or_whine(&todo_list->buf, todo_file) < 0) return -1; - res = stat(todo_file, &st); - if (res) - return error(_("could not stat '%s'"), todo_file); - fill_stat_data(&todo_list->stat, &st); - res = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list); if (res) { if (is_rebase_i(opts)) @@ -4284,6 +4278,30 @@ static int stopped_at_head(struct repository *r) } +static int reread_todo_if_changed(struct repository *r, + struct todo_list *todo_list, + struct replay_opts *opts) +{ + int offset; + struct strbuf buf = STRBUF_INIT; + + if (strbuf_read_file_or_whine(&buf, get_todo_path(opts)) < 0) + return -1; + offset = get_item_line_offset(todo_list, todo_list->current + 1); + if (buf.len != todo_list->buf.len - offset || + memcmp(buf.buf, todo_list->buf.buf + offset, buf.len)) { + /* Reread the todo file if it has changed. */ + todo_list_release(todo_list); + if (read_populate_todo(r, todo_list, opts)) + return -1; /* message was printed */ + /* `current` will be incremented on return */ + todo_list->current = -1; + } + strbuf_release(&buf); + + return 0; +} + static const char rescheduled_advice[] = N_("Could not execute the todo command\n" "\n" @@ -4462,20 +4480,9 @@ static int pick_commits(struct repository *r, item->commit, arg, item->arg_len, opts, res, 0); - } else if (is_rebase_i(opts) && check_todo && !res) { - struct stat st; - - if (stat(get_todo_path(opts), &st)) { - res = error_errno(_("could not stat '%s'"), - get_todo_path(opts)); - } else if (match_stat_data(&todo_list->stat, &st)) { - /* Reread the todo file if it has changed. */ - todo_list_release(todo_list); - if (read_populate_todo(r, todo_list, opts)) - res = -1; /* message was printed */ - /* `current` will be incremented below */ - todo_list->current = -1; - } + } else if (is_rebase_i(opts) && check_todo && !res && + reread_todo_if_changed(r, todo_list, opts)) { + return -1; } todo_list->current++; diff --git a/sequencer.h b/sequencer.h index 259d4eb4a8..d8a1853b29 100644 --- a/sequencer.h +++ b/sequencer.h @@ -116,7 +116,6 @@ struct todo_list { struct todo_item *items; int nr, alloc, current; int done_nr, total_nr; - struct stat_data stat; }; #define TODO_LIST_INIT { \ @@ -10,6 +10,7 @@ #include "upload-pack.h" static int advertise_sid = -1; +static int client_hash_algo = GIT_HASH_SHA1; static int always_advertise(struct repository *r, struct strbuf *value) @@ -33,6 +34,17 @@ static int object_format_advertise(struct repository *r, return 1; } +static void object_format_receive(struct repository *r, + const char *algo_name) +{ + if (!algo_name) + die("object-format capability requires an argument"); + + client_hash_algo = hash_algo_by_name(algo_name); + if (client_hash_algo == GIT_HASH_UNKNOWN) + die("unknown object format '%s'", algo_name); +} + static int session_id_advertise(struct repository *r, struct strbuf *value) { if (advertise_sid == -1 && @@ -45,6 +57,14 @@ static int session_id_advertise(struct repository *r, struct strbuf *value) return 1; } +static void session_id_receive(struct repository *r, + const char *client_sid) +{ + if (!client_sid) + client_sid = ""; + trace2_data_string("transfer", NULL, "client-sid", client_sid); +} + struct protocol_capability { /* * The name of the capability. The server uses this name when @@ -70,6 +90,16 @@ struct protocol_capability { * This field should be NULL for capabilities which are not commands. */ int (*command)(struct repository *r, struct packet_reader *request); + + /* + * Function called when a client requests the capability as a + * non-command. This may be NULL if the capability does nothing. + * + * For a capability of the form "foo=bar", the value string points to + * the content after the "=" (i.e., "bar"). For simple capabilities + * (just "foo"), it is NULL. + */ + void (*receive)(struct repository *r, const char *value); }; static struct protocol_capability capabilities[] = { @@ -94,10 +124,12 @@ static struct protocol_capability capabilities[] = { { .name = "object-format", .advertise = object_format_advertise, + .receive = object_format_receive, }, { .name = "session-id", .advertise = session_id_advertise, + .receive = session_id_receive, }, { .name = "object-info", @@ -139,7 +171,7 @@ void protocol_v2_advertise_capabilities(void) strbuf_release(&value); } -static struct protocol_capability *get_capability(const char *key) +static struct protocol_capability *get_capability(const char *key, const char **value) { int i; @@ -149,31 +181,46 @@ static struct protocol_capability *get_capability(const char *key) for (i = 0; i < ARRAY_SIZE(capabilities); i++) { struct protocol_capability *c = &capabilities[i]; const char *out; - if (skip_prefix(key, c->name, &out) && (!*out || *out == '=')) + if (!skip_prefix(key, c->name, &out)) + continue; + if (!*out) { + *value = NULL; return c; + } + if (*out++ == '=') { + *value = out; + return c; + } } return NULL; } -static int is_valid_capability(const char *key) +static int receive_client_capability(const char *key) { - const struct protocol_capability *c = get_capability(key); + const char *value; + const struct protocol_capability *c = get_capability(key, &value); - return c && c->advertise(the_repository, NULL); + if (!c || c->command || !c->advertise(the_repository, NULL)) + return 0; + + if (c->receive) + c->receive(the_repository, value); + return 1; } -static int is_command(const char *key, struct protocol_capability **command) +static int parse_command(const char *key, struct protocol_capability **command) { const char *out; if (skip_prefix(key, "command=", &out)) { - struct protocol_capability *cmd = get_capability(out); + const char *value; + struct protocol_capability *cmd = get_capability(out, &value); if (*command) die("command '%s' requested after already requesting command '%s'", out, (*command)->name); - if (!cmd || !cmd->advertise(the_repository, NULL) || !cmd->command) + if (!cmd || !cmd->advertise(the_repository, NULL) || !cmd->command || value) die("invalid command '%s'", out); *command = cmd; @@ -183,42 +230,6 @@ static int is_command(const char *key, struct protocol_capability **command) return 0; } -static int has_capability(const struct strvec *keys, const char *capability, - const char **value) -{ - int i; - for (i = 0; i < keys->nr; i++) { - const char *out; - if (skip_prefix(keys->v[i], capability, &out) && - (!*out || *out == '=')) { - if (value) { - if (*out == '=') - out++; - *value = out; - } - return 1; - } - } - - return 0; -} - -static void check_algorithm(struct repository *r, struct strvec *keys) -{ - int client = GIT_HASH_SHA1, server = hash_algo_by_ptr(r->hash_algo); - const char *algo_name; - - if (has_capability(keys, "object-format", &algo_name)) { - client = hash_algo_by_name(algo_name); - if (client == GIT_HASH_UNKNOWN) - die("unknown object format '%s'", algo_name); - } - - if (client != server) - die("mismatched object format: server %s; client %s\n", - r->hash_algo->name, hash_algos[client].name); -} - enum request_state { PROCESS_REQUEST_KEYS, PROCESS_REQUEST_DONE, @@ -228,9 +239,8 @@ static int process_request(void) { enum request_state state = PROCESS_REQUEST_KEYS; struct packet_reader reader; - struct strvec keys = STRVEC_INIT; + int seen_capability_or_command = 0; struct protocol_capability *command = NULL; - const char *client_sid; packet_reader_init(&reader, 0, NULL, 0, PACKET_READ_CHOMP_NEWLINE | @@ -250,10 +260,9 @@ static int process_request(void) case PACKET_READ_EOF: BUG("Should have already died when seeing EOF"); case PACKET_READ_NORMAL: - /* collect request; a sequence of keys and values */ - if (is_command(reader.line, &command) || - is_valid_capability(reader.line)) - strvec_push(&keys, reader.line); + if (parse_command(reader.line, &command) || + receive_client_capability(reader.line)) + seen_capability_or_command = 1; else die("unknown capability '%s'", reader.line); @@ -265,7 +274,7 @@ static int process_request(void) * If no command and no keys were given then the client * wanted to terminate the connection. */ - if (!keys.nr) + if (!seen_capability_or_command) return 1; /* @@ -292,14 +301,13 @@ static int process_request(void) if (!command) die("no command requested"); - check_algorithm(the_repository, &keys); - - if (has_capability(&keys, "session-id", &client_sid)) - trace2_data_string("transfer", NULL, "client-sid", client_sid); + if (client_hash_algo != hash_algo_by_ptr(the_repository->hash_algo)) + die("mismatched object format: server %s; client %s\n", + the_repository->hash_algo->name, + hash_algos[client_hash_algo].name); command->command(the_repository, &reader); - strvec_clear(&keys); return 0; } diff --git a/string-list.c b/string-list.c index 43576ad126..549fc416d6 100644 --- a/string-list.c +++ b/string-list.c @@ -13,14 +13,6 @@ void string_list_init_dup(struct string_list *list) memcpy(list, &blank, sizeof(*list)); } -void string_list_init(struct string_list *list, int strdup_strings) -{ - if (strdup_strings) - string_list_init_dup(list); - else - string_list_init_nodup(list); -} - /* if there is no exact match, point to the index where the entry could be * inserted */ static int get_entry_index(const struct string_list *list, const char *string, diff --git a/string-list.h b/string-list.h index 0d6b469239..267d6e5769 100644 --- a/string-list.h +++ b/string-list.h @@ -104,11 +104,6 @@ struct string_list { void string_list_init_nodup(struct string_list *list); void string_list_init_dup(struct string_list *list); -/** - * TODO remove: For compatibility with any in-flight older API users - */ -void string_list_init(struct string_list *list, int strdup_strings); - /** Callback function type for for_each_string_list */ typedef int (*string_list_each_func_t)(struct string_list_item *, void *); diff --git a/submodule.c b/submodule.c index 03cf36707a..96487517f9 100644 --- a/submodule.c +++ b/submodule.c @@ -525,9 +525,6 @@ static void prepare_submodule_repo_env_in_gitdir(struct strvec *out) /* * Initialize a repository struct for a submodule based on the provided 'path'. * - * Unlike repo_submodule_init, this tolerates submodules not present - * in .gitmodules. This function exists only to preserve historical behavior, - * * Returns the repository struct on success, * NULL when the submodule is not present. */ @@ -1423,24 +1420,13 @@ static void fetch_task_release(struct fetch_task *p) } static struct repository *get_submodule_repo_for(struct repository *r, - const struct submodule *sub) + const char *path) { struct repository *ret = xmalloc(sizeof(*ret)); - if (repo_submodule_init(ret, r, sub)) { - /* - * No entry in .gitmodules? Technically not a submodule, - * but historically we supported repositories that happen to be - * in-place where a gitlink is. Keep supporting them. - */ - struct strbuf gitdir = STRBUF_INIT; - strbuf_repo_worktree_path(&gitdir, r, "%s/.git", sub->path); - if (repo_init(ret, gitdir.buf, NULL)) { - strbuf_release(&gitdir); - free(ret); - return NULL; - } - strbuf_release(&gitdir); + if (repo_submodule_init(ret, r, path, null_oid())) { + free(ret); + return NULL; } return ret; @@ -1482,7 +1468,7 @@ static int get_next_submodule(struct child_process *cp, continue; } - task->repo = get_submodule_repo_for(spf->r, task->sub); + task->repo = get_submodule_repo_for(spf->r, task->sub->path); if (task->repo) { struct strbuf submodule_prefix = STRBUF_INIT; child_process_init(cp); @@ -366,6 +366,13 @@ excluded as so much relies on it, but this might change in the future. GIT_TEST_SPLIT_INDEX=<boolean> forces split-index mode on the whole test suite. Accept any boolean values that are accepted by git-config. +GIT_TEST_PASSING_SANITIZE_LEAK=<boolean> when compiled with +SANITIZE=leak will run only those tests that have whitelisted +themselves as passing with no memory leaks. Tests can be whitelisted +by setting "TEST_PASSES_SANITIZE_LEAK=true" before sourcing +"test-lib.sh" itself at the top of the test script. This test mode is +used by the "linux-leaks" CI target. + GIT_TEST_PROTOCOL_VERSION=<n>, when set, makes 'protocol.version' default to n. diff --git a/t/helper/test-bitmap.c b/t/helper/test-bitmap.c index 134a1e9d76..ff35f5999b 100644 --- a/t/helper/test-bitmap.c +++ b/t/helper/test-bitmap.c @@ -7,6 +7,11 @@ static int bitmap_list_commits(void) return test_bitmap_commits(the_repository); } +static int bitmap_dump_hashes(void) +{ + return test_bitmap_hashes(the_repository); +} + int cmd__bitmap(int argc, const char **argv) { setup_git_directory(); @@ -16,9 +21,12 @@ int cmd__bitmap(int argc, const char **argv) if (!strcmp(argv[1], "list-commits")) return bitmap_list_commits(); + if (!strcmp(argv[1], "dump-hashes")) + return bitmap_dump_hashes(); usage: - usage("\ttest-tool bitmap list-commits"); + usage("\ttest-tool bitmap list-commits\n" + "\ttest-tool bitmap dump-hashes"); return -1; } diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c index cf0f2c7228..99010614f6 100644 --- a/t/helper/test-dump-untracked-cache.c +++ b/t/helper/test-dump-untracked-cache.c @@ -45,8 +45,10 @@ int cmd__dump_untracked_cache(int ac, const char **av) struct untracked_cache *uc; struct strbuf base = STRBUF_INIT; - /* Hack to avoid modifying the untracked cache when we read it */ - ignore_untracked_cache_config = 1; + /* Set core.untrackedCache=keep before setup_git_directory() */ + xsetenv("GIT_CONFIG_COUNT", "1", 1); + xsetenv("GIT_CONFIG_KEY_0", "core.untrackedCache", 1); + xsetenv("GIT_CONFIG_VALUE_0", "keep", 1); setup_git_directory(); if (read_cache() < 0) diff --git a/t/helper/test-submodule-nested-repo-config.c b/t/helper/test-submodule-nested-repo-config.c index e3f11ff5a7..dc1c14bde3 100644 --- a/t/helper/test-submodule-nested-repo-config.c +++ b/t/helper/test-submodule-nested-repo-config.c @@ -11,15 +11,13 @@ static void die_usage(const char **argv, const char *msg) int cmd__submodule_nested_repo_config(int argc, const char **argv) { struct repository subrepo; - const struct submodule *sub; if (argc < 3) die_usage(argv, "Wrong number of arguments."); setup_git_directory(); - sub = submodule_from_path(the_repository, null_oid(), argv[1]); - if (repo_submodule_init(&subrepo, the_repository, sub)) { + if (repo_submodule_init(&subrepo, the_repository, argv[1], null_oid())) { die_usage(argv, "Submodule not found."); } diff --git a/t/perf/p5326-multi-pack-bitmaps.sh b/t/perf/p5326-multi-pack-bitmaps.sh index 5845109ac7..f2fa228f16 100755 --- a/t/perf/p5326-multi-pack-bitmaps.sh +++ b/t/perf/p5326-multi-pack-bitmaps.sh @@ -6,15 +6,24 @@ test_description='Tests performance using midx bitmaps' test_perf_large_repo -test_expect_success 'enable multi-pack index' ' - git config core.multiPackIndex true +# we need to create the tag up front such that it is covered by the repack and +# thus by generated bitmaps. +test_expect_success 'create tags' ' + git tag --message="tag pointing to HEAD" perf-tag HEAD +' + +test_expect_success 'start with bitmapped pack' ' + git repack -adb ' test_perf 'setup multi-pack index' ' - git repack -ad && git multi-pack-index write --bitmap ' +test_expect_success 'drop pack bitmap' ' + rm -f .git/objects/pack/pack-*.bitmap +' + test_full_bitmap test_expect_success 'create partial bitmap state' ' diff --git a/t/perf/run b/t/perf/run index d19dec258a..55219aa405 100755 --- a/t/perf/run +++ b/t/perf/run @@ -74,7 +74,7 @@ set_git_test_installed () { mydir=$1 mydir_abs=$(cd $mydir && pwd) - mydir_abs_wrappers="$mydir_abs_wrappers/bin-wrappers" + mydir_abs_wrappers="$mydir_abs/bin-wrappers" if test -d "$mydir_abs_wrappers" then GIT_TEST_INSTALLED=$mydir_abs_wrappers diff --git a/t/t0004-unwritable.sh b/t/t0004-unwritable.sh index e3137d638e..37d68ef03b 100755 --- a/t/t0004-unwritable.sh +++ b/t/t0004-unwritable.sh @@ -2,6 +2,7 @@ test_description='detect unwritable repository and fail correctly' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success setup ' @@ -21,7 +22,7 @@ test_expect_success POSIXPERM,SANITY 'write-tree should notice unwritable reposi test_must_fail git write-tree ' -test_expect_success POSIXPERM,SANITY 'commit should notice unwritable repository' ' +test_expect_success POSIXPERM,SANITY,!SANITIZE_LEAK 'commit should notice unwritable repository' ' test_when_finished "chmod 775 .git/objects .git/objects/??" && chmod a-w .git/objects .git/objects/?? && test_must_fail git commit -m second diff --git a/t/t0011-hashmap.sh b/t/t0011-hashmap.sh index 5343ffd3f9..e094975b13 100755 --- a/t/t0011-hashmap.sh +++ b/t/t0011-hashmap.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='test hashmap and string hash functions' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_hashmap() { diff --git a/t/t0016-oidmap.sh b/t/t0016-oidmap.sh index 31f8276ba8..0faef1f4f1 100755 --- a/t/t0016-oidmap.sh +++ b/t/t0016-oidmap.sh @@ -1,6 +1,8 @@ #!/bin/sh test_description='test oidmap' + +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # This purposefully is very similar to t0011-hashmap.sh diff --git a/t/t0017-env-helper.sh b/t/t0017-env-helper.sh index 4a159f99e4..2e42fba956 100755 --- a/t/t0017-env-helper.sh +++ b/t/t0017-env-helper.sh @@ -2,6 +2,7 @@ test_description='test env--helper' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh diff --git a/t/t0018-advice.sh b/t/t0018-advice.sh index 39e5e4b34f..c13057a4ca 100755 --- a/t/t0018-advice.sh +++ b/t/t0018-advice.sh @@ -2,6 +2,7 @@ test_description='Test advise_if_enabled functionality' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_expect_success 'advice should be printed when config variable is unset' ' diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh index 0c24a0f9a3..ae1ca380c1 100755 --- a/t/t0030-stripspace.sh +++ b/t/t0030-stripspace.sh @@ -5,6 +5,7 @@ test_description='git stripspace' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh t40='A quick brown fox jumps over the lazy do' diff --git a/t/t0063-string-list.sh b/t/t0063-string-list.sh index c6ee9f66b1..46d4839194 100755 --- a/t/t0063-string-list.sh +++ b/t/t0063-string-list.sh @@ -5,6 +5,7 @@ test_description='Test string list functionality' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh test_split () { diff --git a/t/t0091-bugreport.sh b/t/t0091-bugreport.sh index 526304ff95..eeedbfa919 100755 --- a/t/t0091-bugreport.sh +++ b/t/t0091-bugreport.sh @@ -2,6 +2,7 @@ test_description='git bugreport' +TEST_PASSES_SANITIZE_LEAK=true . ./test-lib.sh # Headers "[System Info]" will be followed by a non-empty line if we put some diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh index 71236981e6..8675ad5154 100755 --- a/t/t1091-sparse-checkout-builtin.sh +++ b/t/t1091-sparse-checkout-builtin.sh @@ -206,16 +206,21 @@ test_expect_success 'sparse-checkout disable' ' ' test_expect_success 'sparse-index enabled and disabled' ' - git -C repo sparse-checkout init --cone --sparse-index && - test_cmp_config -C repo true index.sparse && - test-tool -C repo read-cache --table >cache && - grep " tree " cache && - - git -C repo sparse-checkout disable && - test-tool -C repo read-cache --table >cache && - ! grep " tree " cache && - git -C repo config --list >config && - ! grep index.sparse config + ( + sane_unset GIT_TEST_SPLIT_INDEX && + git -C repo update-index --no-split-index && + + git -C repo sparse-checkout init --cone --sparse-index && + test_cmp_config -C repo true index.sparse && + test-tool -C repo read-cache --table >cache && + grep " tree " cache && + + git -C repo sparse-checkout disable && + test-tool -C repo read-cache --table >cache && + ! grep " tree " cache && + git -C repo config --list >config && + ! grep index.sparse config + ) ' test_expect_success 'cone mode: init and set' ' diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh index b1839e0877..fa3aeb80f2 100755 --- a/t/t1430-bad-ref-name.sh +++ b/t/t1430-bad-ref-name.sh @@ -170,7 +170,7 @@ test_expect_success 'for-each-ref emits warnings for broken names' ' ! grep -e "badname" output && ! grep -e "broken\.\.\.symref" output && test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.ref" error && - test_i18ngrep "ignoring broken ref refs/heads/badname" error && + test_i18ngrep ! "ignoring broken ref refs/heads/badname" error && test_i18ngrep "ignoring ref with broken name refs/heads/broken\.\.\.symref" error ' diff --git a/t/t1600-index.sh b/t/t1600-index.sh index c9b9e718b8..46329c488b 100755 --- a/t/t1600-index.sh +++ b/t/t1600-index.sh @@ -4,6 +4,8 @@ test_description='index file specific tests' . ./test-lib.sh +sane_unset GIT_TEST_SPLIT_INDEX + test_expect_success 'setup' ' echo 1 >a ' @@ -13,7 +15,8 @@ test_expect_success 'bogus GIT_INDEX_VERSION issues warning' ' rm -f .git/index && GIT_INDEX_VERSION=2bogus && export GIT_INDEX_VERSION && - git add a 2>&1 | sed "s/[0-9]//" >actual.err && + git add a 2>err && + sed "s/[0-9]//" err >actual.err && sed -e "s/ Z$/ /" <<-\EOF >expect.err && warning: GIT_INDEX_VERSION set, but the value is invalid. Using version Z @@ -27,7 +30,8 @@ test_expect_success 'out of bounds GIT_INDEX_VERSION issues warning' ' rm -f .git/index && GIT_INDEX_VERSION=1 && export GIT_INDEX_VERSION && - git add a 2>&1 | sed "s/[0-9]//" >actual.err && + git add a 2>err && + sed "s/[0-9]//" err >actual.err && sed -e "s/ Z$/ /" <<-\EOF >expect.err && warning: GIT_INDEX_VERSION set, but the value is invalid. Using version Z @@ -50,7 +54,8 @@ test_expect_success 'out of bounds index.version issues warning' ' sane_unset GIT_INDEX_VERSION && rm -f .git/index && git config --add index.version 1 && - git add a 2>&1 | sed "s/[0-9]//" >actual.err && + git add a 2>err && + sed "s/[0-9]//" err >actual.err && sed -e "s/ Z$/ /" <<-\EOF >expect.err && warning: index.version set, but the value is invalid. Using version Z @@ -79,7 +84,7 @@ test_index_version () { else unset GIT_INDEX_VERSION fi && - git add a 2>&1 && + git add a && echo $EXPECTED_OUTPUT_VERSION >expect && test-tool index-version <.git/index >actual && test_cmp expect actual diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh index 986baa612e..decd2527ed 100755 --- a/t/t1700-split-index.sh +++ b/t/t1700-split-index.sh @@ -510,4 +510,38 @@ test_expect_success 'do not refresh null base index' ' ) ' +test_expect_success 'reading split index at alternate location' ' + git init reading-alternate-location && + ( + cd reading-alternate-location && + >file-in-alternate && + git update-index --split-index --add file-in-alternate + ) && + echo file-in-alternate >expect && + + # Should be able to find the shared index both right next to + # the specified split index file ... + GIT_INDEX_FILE=./reading-alternate-location/.git/index \ + git ls-files --cached >actual && + test_cmp expect actual && + + # ... and, for backwards compatibility, in the current GIT_DIR + # as well. + mv -v ./reading-alternate-location/.git/sharedindex.* .git && + GIT_INDEX_FILE=./reading-alternate-location/.git/index \ + git ls-files --cached >actual && + test_cmp expect actual +' + +test_expect_success 'GIT_TEST_SPLIT_INDEX works' ' + git init git-test-split-index && + ( + cd git-test-split-index && + >file && + GIT_TEST_SPLIT_INDEX=1 git update-index --add file && + ls -l .git/sharedindex.* >actual && + test_line_count = 1 actual + ) +' + test_done diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh index 7c381fbc89..ebbaed147a 100755 --- a/t/t3407-rebase-abort.sh +++ b/t/t3407-rebase-abort.sh @@ -7,77 +7,77 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME . ./test-lib.sh -### Test that we handle space characters properly -work_dir="$(pwd)/test dir" - test_expect_success setup ' - mkdir -p "$work_dir" && - cd "$work_dir" && - git init && - echo a > a && - git add a && - git commit -m a && + test_commit a a a && git branch to-rebase && - echo b > a && - git commit -a -m b && - echo c > a && - git commit -a -m c && + test_commit --annotate b a b && + test_commit --annotate c a c && git checkout to-rebase && - echo d > a && - git commit -a -m "merge should fail on this" && - echo e > a && - git commit -a -m "merge should fail on this, too" && - git branch pre-rebase + test_commit "merge should fail on this" a d d && + test_commit --annotate "merge should fail on this, too" a e pre-rebase ' +# Check that HEAD is equal to "pre-rebase" and the current branch is +# "to-rebase" +check_head() { + test_cmp_rev HEAD pre-rebase^{commit} && + test "$(git symbolic-ref HEAD)" = refs/heads/to-rebase +} + testrebase() { type=$1 - dotest=$2 + state_dir=$2 test_expect_success "rebase$type --abort" ' - cd "$work_dir" && # Clean up the state from the previous one git reset --hard pre-rebase && test_must_fail git rebase$type main && - test_path_is_dir "$dotest" && + test_path_is_dir "$state_dir" && git rebase --abort && - test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) && - test ! -d "$dotest" + check_head && + test_path_is_missing "$state_dir" ' test_expect_success "rebase$type --abort after --skip" ' - cd "$work_dir" && # Clean up the state from the previous one git reset --hard pre-rebase && test_must_fail git rebase$type main && - test_path_is_dir "$dotest" && + test_path_is_dir "$state_dir" && test_must_fail git rebase --skip && - test $(git rev-parse HEAD) = $(git rev-parse main) && + test_cmp_rev HEAD main && git rebase --abort && - test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) && - test ! -d "$dotest" + check_head && + test_path_is_missing "$state_dir" ' test_expect_success "rebase$type --abort after --continue" ' - cd "$work_dir" && # Clean up the state from the previous one git reset --hard pre-rebase && test_must_fail git rebase$type main && - test_path_is_dir "$dotest" && + test_path_is_dir "$state_dir" && echo c > a && echo d >> a && git add a && test_must_fail git rebase --continue && - test $(git rev-parse HEAD) != $(git rev-parse main) && + test_cmp_rev ! HEAD main && + git rebase --abort && + check_head && + test_path_is_missing "$state_dir" + ' + + test_expect_success "rebase$type --abort when checking out a tag" ' + test_when_finished "git symbolic-ref HEAD refs/heads/to-rebase" && + git reset --hard a -- && + test_must_fail git rebase$type --onto b c pre-rebase && + test_cmp_rev HEAD b^{commit} && git rebase --abort && - test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) && - test ! -d "$dotest" + test_cmp_rev HEAD pre-rebase^{commit} && + ! git symbolic-ref HEAD ' test_expect_success "rebase$type --abort does not update reflog" ' - cd "$work_dir" && # Clean up the state from the previous one git reset --hard pre-rebase && git reflog show to-rebase > reflog_before && @@ -89,7 +89,6 @@ testrebase() { ' test_expect_success 'rebase --abort can not be used with other options' ' - cd "$work_dir" && # Clean up the state from the previous one git reset --hard pre-rebase && test_must_fail git rebase$type main && @@ -97,33 +96,21 @@ testrebase() { test_must_fail git rebase --abort -v && git rebase --abort ' + + test_expect_success "rebase$type --quit" ' + test_when_finished "git symbolic-ref HEAD refs/heads/to-rebase" && + # Clean up the state from the previous one + git reset --hard pre-rebase && + test_must_fail git rebase$type main && + test_path_is_dir $state_dir && + head_before=$(git rev-parse HEAD) && + git rebase --quit && + test_cmp_rev HEAD $head_before && + test_path_is_missing .git/rebase-apply + ' } testrebase " --apply" .git/rebase-apply testrebase " --merge" .git/rebase-merge -test_expect_success 'rebase --apply --quit' ' - cd "$work_dir" && - # Clean up the state from the previous one - git reset --hard pre-rebase && - test_must_fail git rebase --apply main && - test_path_is_dir .git/rebase-apply && - head_before=$(git rev-parse HEAD) && - git rebase --quit && - test $(git rev-parse HEAD) = $head_before && - test ! -d .git/rebase-apply -' - -test_expect_success 'rebase --merge --quit' ' - cd "$work_dir" && - # Clean up the state from the previous one - git reset --hard pre-rebase && - test_must_fail git rebase --merge main && - test_path_is_dir .git/rebase-merge && - head_before=$(git rev-parse HEAD) && - git rebase --quit && - test $(git rev-parse HEAD) = $head_before && - test ! -d .git/rebase-merge -' - test_done diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index 873aa56e35..f0a82be9de 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -1307,4 +1307,62 @@ test_expect_success 'stash -c stash.useBuiltin=false warning ' ' test_must_be_empty err ' +test_expect_success 'git stash succeeds despite directory/file change' ' + test_create_repo directory_file_switch_v1 && + ( + cd directory_file_switch_v1 && + test_commit init && + + test_write_lines this file has some words >filler && + git add filler && + git commit -m filler && + + git rm filler && + mkdir filler && + echo contents >filler/file && + git stash push + ) +' + +test_expect_success 'git stash can pop file -> directory saved changes' ' + test_create_repo directory_file_switch_v2 && + ( + cd directory_file_switch_v2 && + test_commit init && + + test_write_lines this file has some words >filler && + git add filler && + git commit -m filler && + + git rm filler && + mkdir filler && + echo contents >filler/file && + cp filler/file expect && + git stash push --include-untracked && + git stash apply --index && + test_cmp expect filler/file + ) +' + +test_expect_success 'git stash can pop directory -> file saved changes' ' + test_create_repo directory_file_switch_v3 && + ( + cd directory_file_switch_v3 && + test_commit init && + + mkdir filler && + test_write_lines some words >filler/file1 && + test_write_lines and stuff >filler/file2 && + git add filler && + git commit -m filler && + + git rm -rf filler && + echo contents >filler && + cp filler expect && + git stash push --include-untracked && + git stash apply --index && + test_cmp expect filler + ) +' + test_done diff --git a/t/t5312-prune-corruption.sh b/t/t5312-prune-corruption.sh index 11423b3cb2..ea889c088a 100755 --- a/t/t5312-prune-corruption.sh +++ b/t/t5312-prune-corruption.sh @@ -7,6 +7,9 @@ if we see, for example, a ref with a bogus name, it is OK either to bail out or to proceed using it as a reachable tip, but it is _not_ OK to proceed as if it did not exist. Otherwise we might silently delete objects that cannot be recovered. + +Note that we do assert command failure in these cases, because that is +what currently happens. If that changes, these tests should be revisited. ' GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME @@ -18,39 +21,58 @@ test_expect_success 'disable reflogs' ' git reflog expire --expire=all --all ' +create_bogus_ref () { + test_when_finished 'rm -f .git/refs/heads/bogus..name' && + echo $bogus >.git/refs/heads/bogus..name +} + test_expect_success 'create history reachable only from a bogus-named ref' ' test_tick && git commit --allow-empty -m main && base=$(git rev-parse HEAD) && test_tick && git commit --allow-empty -m bogus && bogus=$(git rev-parse HEAD) && git cat-file commit $bogus >saved && - echo $bogus >.git/refs/heads/bogus..name && git reset --hard HEAD^ ' test_expect_success 'pruning does not drop bogus object' ' test_when_finished "git hash-object -w -t commit saved" && - test_might_fail git prune --expire=now && - verbose git cat-file -e $bogus + create_bogus_ref && + test_must_fail git prune --expire=now && + git cat-file -e $bogus ' test_expect_success 'put bogus object into pack' ' git tag reachable $bogus && git repack -ad && git tag -d reachable && - verbose git cat-file -e $bogus + git cat-file -e $bogus +' + +test_expect_success 'non-destructive repack bails on bogus ref' ' + create_bogus_ref && + test_must_fail git repack -adk ' +test_expect_success 'GIT_REF_PARANOIA=0 overrides safety' ' + create_bogus_ref && + GIT_REF_PARANOIA=0 git repack -adk +' + + test_expect_success 'destructive repack keeps packed object' ' - test_might_fail git repack -Ad --unpack-unreachable=now && - verbose git cat-file -e $bogus && - test_might_fail git repack -ad && - verbose git cat-file -e $bogus + create_bogus_ref && + test_must_fail git repack -Ad --unpack-unreachable=now && + git cat-file -e $bogus && + test_must_fail git repack -ad && + git cat-file -e $bogus ' -# subsequent tests will have different corruptions -test_expect_success 'clean up bogus ref' ' - rm .git/refs/heads/bogus..name +test_expect_success 'destructive repack not confused by dangling symref' ' + test_when_finished "git symbolic-ref -d refs/heads/dangling" && + git symbolic-ref refs/heads/dangling refs/heads/does-not-exist && + git repack -ad && + test_must_fail git cat-file -e $bogus ' # We create two new objects here, "one" and "two". Our @@ -77,8 +99,8 @@ test_expect_success 'create history with missing tip commit' ' test_expect_success 'pruning with a corrupted tip does not drop history' ' test_when_finished "git hash-object -w -t commit saved" && - test_might_fail git prune --expire=now && - verbose git cat-file -e $recoverable + test_must_fail git prune --expire=now && + git cat-file -e $recoverable ' test_expect_success 'pack-refs does not silently delete broken loose ref' ' diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh index bb04f0f23b..bd17f308b3 100755 --- a/t/t5319-multi-pack-index.sh +++ b/t/t5319-multi-pack-index.sh @@ -174,12 +174,12 @@ test_expect_success 'write progress off for redirected stderr' ' ' test_expect_success 'write force progress on for stderr' ' - GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --progress write 2>err && + GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir write --progress 2>err && test_file_not_empty err ' test_expect_success 'write with the --no-progress option' ' - GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --no-progress write 2>err && + GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir write --no-progress 2>err && test_line_count = 0 err ' @@ -474,12 +474,12 @@ test_expect_success 'repack progress off for redirected stderr' ' ' test_expect_success 'repack force progress on for stderr' ' - GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --progress repack 2>err && + GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir repack --progress 2>err && test_file_not_empty err ' test_expect_success 'repack with the --no-progress option' ' - GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir --no-progress repack 2>err && + GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir repack --no-progress 2>err && test_line_count = 0 err ' @@ -672,7 +672,7 @@ test_expect_success 'expire progress off for redirected stderr' ' test_expect_success 'expire force progress on for stderr' ' ( cd dup && - GIT_PROGRESS_DELAY=0 git multi-pack-index --progress expire 2>err && + GIT_PROGRESS_DELAY=0 git multi-pack-index expire --progress 2>err && test_file_not_empty err ) ' @@ -680,7 +680,7 @@ test_expect_success 'expire force progress on for stderr' ' test_expect_success 'expire with the --no-progress option' ' ( cd dup && - GIT_PROGRESS_DELAY=0 git multi-pack-index --no-progress expire 2>err && + GIT_PROGRESS_DELAY=0 git multi-pack-index expire --no-progress 2>err && test_line_count = 0 err ) ' diff --git a/t/t5326-multi-pack-bitmaps.sh b/t/t5326-multi-pack-bitmaps.sh index 4ad7c2c969..ec4aa89f63 100755 --- a/t/t5326-multi-pack-bitmaps.sh +++ b/t/t5326-multi-pack-bitmaps.sh @@ -283,4 +283,34 @@ test_expect_success 'pack.preferBitmapTips' ' ) ' +test_expect_success 'hash-cache values are propagated from pack bitmaps' ' + rm -fr repo && + git init repo && + test_when_finished "rm -fr repo" && + ( + cd repo && + + test_commit base && + test_commit base2 && + git repack -adb && + + test-tool bitmap dump-hashes >pack.raw && + test_file_not_empty pack.raw && + sort pack.raw >pack.hashes && + + test_commit new && + git repack && + git multi-pack-index write --bitmap && + + test-tool bitmap dump-hashes >midx.raw && + sort midx.raw >midx.hashes && + + # ensure that every namehash in the pack bitmap can be found in + # the midx bitmap (i.e., that there are no oid-namehash pairs + # unique to the pack bitmap). + comm -23 pack.hashes midx.hashes >dropped.hashes && + test_must_be_empty dropped.hashes + ) +' + test_done diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 4db8edd9c8..8212ca56dc 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -662,10 +662,10 @@ test_expect_success 'push does not update local refs on failure' ' test_expect_success 'allow deleting an invalid remote ref' ' - mk_test testrepo heads/main && + mk_test testrepo heads/branch && rm -f testrepo/.git/objects/??/* && - git push testrepo :refs/heads/main && - (cd testrepo && test_must_fail git rev-parse --verify refs/heads/main) + git push testrepo :refs/heads/branch && + (cd testrepo && test_must_fail git rev-parse --verify refs/heads/branch) ' @@ -706,25 +706,26 @@ test_expect_success 'pushing valid refs triggers post-receive and post-update ho ' test_expect_success 'deleting dangling ref triggers hooks with correct args' ' - mk_test_with_hooks testrepo heads/main && + mk_test_with_hooks testrepo heads/branch && + orig=$(git -C testrepo rev-parse refs/heads/branch) && rm -f testrepo/.git/objects/??/* && - git push testrepo :refs/heads/main && + git push testrepo :refs/heads/branch && ( cd testrepo/.git && cat >pre-receive.expect <<-EOF && - $ZERO_OID $ZERO_OID refs/heads/main + $orig $ZERO_OID refs/heads/branch EOF cat >update.expect <<-EOF && - refs/heads/main $ZERO_OID $ZERO_OID + refs/heads/branch $orig $ZERO_OID EOF cat >post-receive.expect <<-EOF && - $ZERO_OID $ZERO_OID refs/heads/main + $orig $ZERO_OID refs/heads/branch EOF cat >post-update.expect <<-EOF && - refs/heads/main + refs/heads/branch EOF test_cmp pre-receive.expect pre-receive.actual && diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh index cffc47a8e3..f92c79c132 100755 --- a/t/t5551-http-fetch-smart.sh +++ b/t/t5551-http-fetch-smart.sh @@ -196,8 +196,8 @@ test_expect_success 'GIT_TRACE_CURL redacts auth details' ' # Ensure that there is no "Basic" followed by a base64 string, but that # the auth details are redacted - ! grep "Authorization: Basic [0-9a-zA-Z+/]" trace && - grep "Authorization: Basic <redacted>" trace + ! grep -i "Authorization: Basic [0-9a-zA-Z+/]" trace && + grep -i "Authorization: Basic <redacted>" trace ' test_expect_success 'GIT_CURL_VERBOSE redacts auth details' ' @@ -208,8 +208,8 @@ test_expect_success 'GIT_CURL_VERBOSE redacts auth details' ' # Ensure that there is no "Basic" followed by a base64 string, but that # the auth details are redacted - ! grep "Authorization: Basic [0-9a-zA-Z+/]" trace && - grep "Authorization: Basic <redacted>" trace + ! grep -i "Authorization: Basic [0-9a-zA-Z+/]" trace && + grep -i "Authorization: Basic <redacted>" trace ' test_expect_success 'GIT_TRACE_CURL does not redact auth details if GIT_TRACE_REDACT=0' ' @@ -219,7 +219,7 @@ test_expect_success 'GIT_TRACE_CURL does not redact auth details if GIT_TRACE_RE git clone --bare "$HTTPD_URL/auth/smart/repo.git" redact-auth && expect_askpass both user@host && - grep "Authorization: Basic [0-9a-zA-Z+/]" trace + grep -i "Authorization: Basic [0-9a-zA-Z+/]" trace ' test_expect_success 'disable dumb http on server' ' @@ -474,10 +474,10 @@ test_expect_success 'cookies are redacted by default' ' GIT_TRACE_CURL=true \ git -c "http.cookieFile=$(pwd)/cookies" clone \ $HTTPD_URL/smart/repo.git clone 2>err && - grep "Cookie:.*Foo=<redacted>" err && - grep "Cookie:.*Bar=<redacted>" err && - ! grep "Cookie:.*Foo=1" err && - ! grep "Cookie:.*Bar=2" err + grep -i "Cookie:.*Foo=<redacted>" err && + grep -i "Cookie:.*Bar=<redacted>" err && + ! grep -i "Cookie:.*Foo=1" err && + ! grep -i "Cookie:.*Bar=2" err ' test_expect_success 'empty values of cookies are also redacted' ' @@ -486,7 +486,7 @@ test_expect_success 'empty values of cookies are also redacted' ' GIT_TRACE_CURL=true \ git -c "http.cookieFile=$(pwd)/cookies" clone \ $HTTPD_URL/smart/repo.git clone 2>err && - grep "Cookie:.*Foo=<redacted>" err + grep -i "Cookie:.*Foo=<redacted>" err ' test_expect_success 'GIT_TRACE_REDACT=0 disables cookie redaction' ' @@ -496,8 +496,8 @@ test_expect_success 'GIT_TRACE_REDACT=0 disables cookie redaction' ' GIT_TRACE_REDACT=0 GIT_TRACE_CURL=true \ git -c "http.cookieFile=$(pwd)/cookies" clone \ $HTTPD_URL/smart/repo.git clone 2>err && - grep "Cookie:.*Foo=1" err && - grep "Cookie:.*Bar=2" err + grep -i "Cookie:.*Foo=1" err && + grep -i "Cookie:.*Bar=2" err ' test_expect_success 'GIT_TRACE_CURL_NO_DATA prevents data from being traced' ' diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh index 5bf10261d3..34b3df4027 100755 --- a/t/t5600-clone-fail-cleanup.sh +++ b/t/t5600-clone-fail-cleanup.sh @@ -35,7 +35,9 @@ test_expect_success 'create a repo to clone' ' ' test_expect_success 'create objects in repo for later corruption' ' - test_commit -C foo file + test_commit -C foo file && + git -C foo checkout --detach && + test_commit -C foo detached ' # source repository given to git clone should be relative to the diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh index 930721f053..aa1827d841 100755 --- a/t/t5701-git-serve.sh +++ b/t/t5701-git-serve.sh @@ -72,6 +72,37 @@ test_expect_success 'request invalid command' ' test_i18ngrep "invalid command" err ' +test_expect_success 'request capability as command' ' + test-tool pkt-line pack >in <<-EOF && + command=agent + object-format=$(test_oid algo) + 0000 + EOF + test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in && + grep invalid.command.*agent err +' + +test_expect_success 'request command as capability' ' + test-tool pkt-line pack >in <<-EOF && + command=ls-refs + object-format=$(test_oid algo) + fetch + 0000 + EOF + test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in && + grep unknown.capability err +' + +test_expect_success 'requested command is command=value' ' + test-tool pkt-line pack >in <<-EOF && + command=ls-refs=whatever + object-format=$(test_oid algo) + 0000 + EOF + test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in && + grep invalid.command.*ls-refs=whatever err +' + test_expect_success 'wrong object-format' ' test-tool pkt-line pack >in <<-EOF && command=fetch @@ -116,6 +147,19 @@ test_expect_success 'basics of ls-refs' ' test_cmp expect actual ' +test_expect_success 'ls-refs complains about unknown options' ' + test-tool pkt-line pack >in <<-EOF && + command=ls-refs + object-format=$(test_oid algo) + 0001 + no-such-arg + 0000 + EOF + + test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in && + grep unexpected.line.*no-such-arg err +' + test_expect_success 'basic ref-prefixes' ' test-tool pkt-line pack >in <<-EOF && command=ls-refs @@ -158,6 +202,37 @@ test_expect_success 'refs/heads prefix' ' test_cmp expect actual ' +test_expect_success 'ignore very large set of prefixes' ' + # generate a large number of ref-prefixes that we expect + # to match nothing; the value here exceeds TOO_MANY_PREFIXES + # from ls-refs.c. + { + echo command=ls-refs && + echo object-format=$(test_oid algo) && + echo 0001 && + perl -le "print \"ref-prefix refs/heads/\$_\" for (1..65536)" && + echo 0000 + } | + test-tool pkt-line pack >in && + + # and then confirm that we see unmatched prefixes anyway (i.e., + # that the prefix was not applied). + cat >expect <<-EOF && + $(git rev-parse HEAD) HEAD + $(git rev-parse refs/heads/dev) refs/heads/dev + $(git rev-parse refs/heads/main) refs/heads/main + $(git rev-parse refs/heads/release) refs/heads/release + $(git rev-parse refs/tags/annotated-tag) refs/tags/annotated-tag + $(git rev-parse refs/tags/one) refs/tags/one + $(git rev-parse refs/tags/two) refs/tags/two + 0000 + EOF + + test-tool serve-v2 --stateless-rpc <in >out && + test-tool pkt-line unpack <out >actual && + test_cmp expect actual +' + test_expect_success 'peel parameter' ' test-tool pkt-line pack >in <<-EOF && command=ls-refs diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh index d3687b1a2e..d527cf6c49 100755 --- a/t/t5702-protocol-v2.sh +++ b/t/t5702-protocol-v2.sh @@ -237,6 +237,19 @@ test_expect_success '...but not if explicitly forbidden by config' ' ! grep "refs/heads/mydefaultbranch" file_empty_child/.git/HEAD ' +test_expect_success 'bare clone propagates empty default branch' ' + test_when_finished "rm -rf file_empty_parent file_empty_child.git" && + + GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \ + git -c init.defaultBranch=mydefaultbranch init file_empty_parent && + + GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \ + git -c init.defaultBranch=main -c protocol.version=2 \ + clone --bare \ + "file://$(pwd)/file_empty_parent" file_empty_child.git && + grep "refs/heads/mydefaultbranch" file_empty_child.git/HEAD +' + test_expect_success 'fetch with file:// using protocol v2' ' test_when_finished "rm -f log" && diff --git a/t/t5704-protocol-violations.sh b/t/t5704-protocol-violations.sh index 5c941949b9..bc393d7c31 100755 --- a/t/t5704-protocol-violations.sh +++ b/t/t5704-protocol-violations.sh @@ -32,4 +32,19 @@ test_expect_success 'extra delim packet in v2 fetch args' ' test_i18ngrep "expected flush after fetch arguments" err ' +test_expect_success 'bogus symref in v0 capabilities' ' + test_commit foo && + oid=$(git rev-parse HEAD) && + dst=refs/heads/foo && + { + printf "%s HEAD\0symref object-format=%s symref=HEAD:%s\n" \ + "$oid" "$GIT_DEFAULT_HASH" "$dst" | + test-tool pkt-line pack-raw-stdin && + printf "0000" + } >input && + git ls-remote --symref --upload-pack="cat input; read junk;:" . >actual && + printf "ref: %s\tHEAD\n%s\tHEAD\n" "$dst" "$oid" >expect && + test_cmp expect actual +' + test_done diff --git a/t/t7519-status-fsmonitor.sh b/t/t7519-status-fsmonitor.sh index f1463197b9..f488d930df 100755 --- a/t/t7519-status-fsmonitor.sh +++ b/t/t7519-status-fsmonitor.sh @@ -399,41 +399,45 @@ check_sparse_index_behavior () { } test_expect_success 'status succeeds with sparse index' ' - git clone . full && - git clone --sparse . sparse && - git -C sparse sparse-checkout init --cone --sparse-index && - git -C sparse sparse-checkout set dir1 dir2 && + ( + sane_unset GIT_TEST_SPLIT_INDEX && - write_script .git/hooks/fsmonitor-test <<-\EOF && - printf "last_update_token\0" - EOF - git -C full config core.fsmonitor ../.git/hooks/fsmonitor-test && - git -C sparse config core.fsmonitor ../.git/hooks/fsmonitor-test && - check_sparse_index_behavior ! && + git clone . full && + git clone --sparse . sparse && + git -C sparse sparse-checkout init --cone --sparse-index && + git -C sparse sparse-checkout set dir1 dir2 && - write_script .git/hooks/fsmonitor-test <<-\EOF && - printf "last_update_token\0" - printf "dir1/modified\0" - EOF - check_sparse_index_behavior ! && - - git -C sparse sparse-checkout add dir1a && + write_script .git/hooks/fsmonitor-test <<-\EOF && + printf "last_update_token\0" + EOF + git -C full config core.fsmonitor ../.git/hooks/fsmonitor-test && + git -C sparse config core.fsmonitor ../.git/hooks/fsmonitor-test && + check_sparse_index_behavior ! && - for repo in full sparse - do - cp -r $repo/dir1 $repo/dir1a && - git -C $repo add dir1a && - git -C $repo commit -m "add dir1a" || return 1 - done && - git -C sparse sparse-checkout set dir1 dir2 && - - # This one modifies outside the sparse-checkout definition - # and hence we expect to expand the sparse-index. - write_script .git/hooks/fsmonitor-test <<-\EOF && - printf "last_update_token\0" - printf "dir1a/modified\0" - EOF - check_sparse_index_behavior + write_script .git/hooks/fsmonitor-test <<-\EOF && + printf "last_update_token\0" + printf "dir1/modified\0" + EOF + check_sparse_index_behavior ! && + + git -C sparse sparse-checkout add dir1a && + + for repo in full sparse + do + cp -r $repo/dir1 $repo/dir1a && + git -C $repo add dir1a && + git -C $repo commit -m "add dir1a" || return 1 + done && + git -C sparse sparse-checkout set dir1 dir2 && + + # This one modifies outside the sparse-checkout definition + # and hence we expect to expand the sparse-index. + write_script .git/hooks/fsmonitor-test <<-\EOF && + printf "last_update_token\0" + printf "dir1a/modified\0" + EOF + check_sparse_index_behavior + ) ' test_done diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh index a173f564bc..528e0dabf0 100755 --- a/t/t7800-difftool.sh +++ b/t/t7800-difftool.sh @@ -674,7 +674,6 @@ test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' ' rm c && ln -s d c && cat >expect <<-EOF && - b c c @@ -710,7 +709,6 @@ test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' ' # Deleted symlinks rm -f c && cat >expect <<-EOF && - b c EOF @@ -723,6 +721,71 @@ test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' ' test_cmp expect actual ' +test_expect_success SYMLINKS 'difftool --dir-diff writes symlinks as raw text' ' + # Start out on a branch called "branch-init". + git init -b branch-init symlink-files && + ( + cd symlink-files && + # This test ensures that symlinks are written as raw text. + # The "cat" tools output link and file contents. + git config difftool.cat-left-link.cmd "cat \"\$LOCAL/link\"" && + git config difftool.cat-left-a.cmd "cat \"\$LOCAL/file-a\"" && + git config difftool.cat-right-link.cmd "cat \"\$REMOTE/link\"" && + git config difftool.cat-right-b.cmd "cat \"\$REMOTE/file-b\"" && + + # Record the empty initial state so that we can come back here + # later and not have to consider the any cases where difftool + # will create symlinks back into the worktree. + test_tick && + git commit --allow-empty -m init && + + # Create a file called "file-a" with a symlink pointing to it. + git switch -c branch-a && + echo a >file-a && + ln -s file-a link && + git add file-a link && + test_tick && + git commit -m link-to-file-a && + + # Create a file called "file-b" and point the symlink to it. + git switch -c branch-b && + echo b >file-b && + rm link && + ln -s file-b link && + git add file-b link && + git rm file-a && + test_tick && + git commit -m link-to-file-b && + + # Checkout the initial branch so that the --symlinks behavior is + # not activated. The two directories should be completely + # independent with no symlinks pointing back here. + git switch branch-init && + + # The left link must be "file-a" and "file-a" must contain "a". + echo file-a >expect && + git difftool --symlinks --dir-diff --tool cat-left-link \ + branch-a branch-b >actual && + test_cmp expect actual && + + echo a >expect && + git difftool --symlinks --dir-diff --tool cat-left-a \ + branch-a branch-b >actual && + test_cmp expect actual && + + # The right link must be "file-b" and "file-b" must contain "b". + echo file-b >expect && + git difftool --symlinks --dir-diff --tool cat-right-link \ + branch-a branch-b >actual && + test_cmp expect actual && + + echo b >expect && + git difftool --symlinks --dir-diff --tool cat-right-b \ + branch-a branch-b >actual && + test_cmp expect actual + ) +' + test_expect_success 'add -N and difftool -d' ' test_when_finished git reset --hard && diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh index 36a4218745..9b9f11a8e7 100755 --- a/t/t7900-maintenance.sh +++ b/t/t7900-maintenance.sh @@ -21,8 +21,7 @@ test_xmllint () { } test_lazy_prereq SYSTEMD_ANALYZE ' - systemd-analyze --help >out && - grep verify out + systemd-analyze verify /lib/systemd/system/basic.target ' test_systemd_analyze_verify () { @@ -277,7 +276,7 @@ test_expect_success 'incremental-repack task' ' # Delete refs that have not been repacked in these packs. git for-each-ref --format="delete %(refname)" \ - refs/prefetch refs/tags >refs && + refs/prefetch refs/tags refs/remotes >refs && git update-ref --stdin <refs && # Replace the object directory with this pack layout. @@ -286,6 +285,10 @@ test_expect_success 'incremental-repack task' ' ls $packDir/*.pack >packs-before && test_line_count = 3 packs-before && + # make sure we do not have any broken refs that were + # missed in the deletion above + git for-each-ref && + # the job repacks the two into a new pack, but does not # delete the old ones. git maintenance run --task=incremental-repack && diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh index 2d29d486ee..17f988edd2 100755 --- a/t/t9400-git-cvsserver-server.sh +++ b/t/t9400-git-cvsserver-server.sh @@ -36,6 +36,13 @@ CVSWORK="$PWD/cvswork" CVS_SERVER=git-cvsserver export CVSROOT CVS_SERVER +if perl -e 'exit(1) if not defined crypt("", "cv")' +then + PWDHASH='lac2ItudM3.KM' +else + PWDHASH='$2b$10$t8fGvE/a9eLmfOLzsZme2uOa2QtoMYwIxq9wZA6aBKtF1Yb7FJIzi' +fi + rm -rf "$CVSWORK" "$SERVERDIR" test_expect_success 'setup' ' git config push.default matching && @@ -54,7 +61,7 @@ test_expect_success 'setup' ' GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true && GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" && GIT_DIR="$SERVERDIR" git config gitcvs.authdb "$SERVERDIR/auth.db" && - echo cvsuser:cvGVEarMLnhlA > "$SERVERDIR/auth.db" + echo "cvsuser:$PWDHASH" >"$SERVERDIR/auth.db" ' # note that cvs doesn't accept absolute pathnames diff --git a/t/test-lib.sh b/t/test-lib.sh index aa1ad8180e..8361b5c1c5 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -1381,6 +1381,26 @@ then test_done fi +# skip non-whitelisted tests when compiled with SANITIZE=leak +if test -n "$SANITIZE_LEAK" +then + if test_bool_env GIT_TEST_PASSING_SANITIZE_LEAK false + then + # We need to see it in "git env--helper" (via + # test_bool_env) + export TEST_PASSES_SANITIZE_LEAK + + if ! test_bool_env TEST_PASSES_SANITIZE_LEAK false + then + skip_all="skipping $this_test under GIT_TEST_PASSING_SANITIZE_LEAK=true" + test_done + fi + fi +elif test_bool_env GIT_TEST_PASSING_SANITIZE_LEAK false +then + error "GIT_TEST_PASSING_SANITIZE_LEAK=true has no effect except when compiled with SANITIZE=leak" +fi + # Last-minute variable setup USER_HOME="$HOME" HOME="$TRASH_DIRECTORY" @@ -1534,6 +1554,7 @@ test -z "$NO_PYTHON" && test_set_prereq PYTHON test -n "$USE_LIBPCRE2" && test_set_prereq PCRE test -n "$USE_LIBPCRE2" && test_set_prereq LIBPCRE2 test -z "$NO_GETTEXT" && test_set_prereq GETTEXT +test -n "$SANITIZE_LEAK" && test_set_prereq SANITIZE_LEAK if test -z "$GIT_TEST_CHECK_CACHE_TREE" then @@ -350,7 +350,7 @@ void trace2_def_repo_fl(const char *file, int line, struct repository *repo); * being started, such as "read_recursive" or "do_read_index". * * The `repo` field, if set, will be used to get the "repo-id", so that - * recursive oerations can be attributed to the correct repository. + * recursive operations can be attributed to the correct repository. */ void trace2_region_enter_fl(const char *file, int line, const char *category, const char *label, const struct repository *repo, ...); diff --git a/unicode-width.h b/unicode-width.h index b50e686bae..97c851b27d 100644 --- a/unicode-width.h +++ b/unicode-width.h @@ -26,7 +26,9 @@ static const struct interval zero_width[] = { { 0x0825, 0x0827 }, { 0x0829, 0x082D }, { 0x0859, 0x085B }, -{ 0x08D3, 0x0902 }, +{ 0x0890, 0x0891 }, +{ 0x0898, 0x089F }, +{ 0x08CA, 0x0902 }, { 0x093A, 0x093A }, { 0x093C, 0x093C }, { 0x0941, 0x0948 }, @@ -66,6 +68,7 @@ static const struct interval zero_width[] = { { 0x0BCD, 0x0BCD }, { 0x0C00, 0x0C00 }, { 0x0C04, 0x0C04 }, +{ 0x0C3C, 0x0C3C }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, @@ -116,7 +119,7 @@ static const struct interval zero_width[] = { { 0x1160, 0x11FF }, { 0x135D, 0x135F }, { 0x1712, 0x1714 }, -{ 0x1732, 0x1734 }, +{ 0x1732, 0x1733 }, { 0x1752, 0x1753 }, { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, @@ -124,7 +127,7 @@ static const struct interval zero_width[] = { { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, -{ 0x180B, 0x180E }, +{ 0x180B, 0x180F }, { 0x1885, 0x1886 }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, @@ -140,7 +143,7 @@ static const struct interval zero_width[] = { { 0x1A65, 0x1A6C }, { 0x1A73, 0x1A7C }, { 0x1A7F, 0x1A7F }, -{ 0x1AB0, 0x1AC0 }, +{ 0x1AB0, 0x1ACE }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, { 0x1B36, 0x1B3A }, @@ -163,8 +166,7 @@ static const struct interval zero_width[] = { { 0x1CED, 0x1CED }, { 0x1CF4, 0x1CF4 }, { 0x1CF8, 0x1CF9 }, -{ 0x1DC0, 0x1DF9 }, -{ 0x1DFB, 0x1DFF }, +{ 0x1DC0, 0x1DFF }, { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2064 }, @@ -227,12 +229,16 @@ static const struct interval zero_width[] = { { 0x10D24, 0x10D27 }, { 0x10EAB, 0x10EAC }, { 0x10F46, 0x10F50 }, +{ 0x10F82, 0x10F85 }, { 0x11001, 0x11001 }, { 0x11038, 0x11046 }, +{ 0x11070, 0x11070 }, +{ 0x11073, 0x11074 }, { 0x1107F, 0x11081 }, { 0x110B3, 0x110B6 }, { 0x110B9, 0x110BA }, { 0x110BD, 0x110BD }, +{ 0x110C2, 0x110C2 }, { 0x110CD, 0x110CD }, { 0x11100, 0x11102 }, { 0x11127, 0x1112B }, @@ -315,6 +321,8 @@ static const struct interval zero_width[] = { { 0x16FE4, 0x16FE4 }, { 0x1BC9D, 0x1BC9E }, { 0x1BCA0, 0x1BCA3 }, +{ 0x1CF00, 0x1CF2D }, +{ 0x1CF30, 0x1CF46 }, { 0x1D167, 0x1D169 }, { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, @@ -332,6 +340,7 @@ static const struct interval zero_width[] = { { 0x1E023, 0x1E024 }, { 0x1E026, 0x1E02A }, { 0x1E130, 0x1E136 }, +{ 0x1E2AE, 0x1E2AE }, { 0x1E2EC, 0x1E2EF }, { 0x1E8D0, 0x1E8D6 }, { 0x1E944, 0x1E94A }, @@ -404,7 +413,10 @@ static const struct interval double_width[] = { { 0x17000, 0x187F7 }, { 0x18800, 0x18CD5 }, { 0x18D00, 0x18D08 }, -{ 0x1B000, 0x1B11E }, +{ 0x1AFF0, 0x1AFF3 }, +{ 0x1AFF5, 0x1AFFB }, +{ 0x1AFFD, 0x1AFFE }, +{ 0x1B000, 0x1B122 }, { 0x1B150, 0x1B152 }, { 0x1B164, 0x1B167 }, { 0x1B170, 0x1B2FB }, @@ -439,21 +451,23 @@ static const struct interval double_width[] = { { 0x1F6CC, 0x1F6CC }, { 0x1F6D0, 0x1F6D2 }, { 0x1F6D5, 0x1F6D7 }, +{ 0x1F6DD, 0x1F6DF }, { 0x1F6EB, 0x1F6EC }, { 0x1F6F4, 0x1F6FC }, { 0x1F7E0, 0x1F7EB }, +{ 0x1F7F0, 0x1F7F0 }, { 0x1F90C, 0x1F93A }, { 0x1F93C, 0x1F945 }, -{ 0x1F947, 0x1F978 }, -{ 0x1F97A, 0x1F9CB }, -{ 0x1F9CD, 0x1F9FF }, +{ 0x1F947, 0x1F9FF }, { 0x1FA70, 0x1FA74 }, -{ 0x1FA78, 0x1FA7A }, +{ 0x1FA78, 0x1FA7C }, { 0x1FA80, 0x1FA86 }, -{ 0x1FA90, 0x1FAA8 }, -{ 0x1FAB0, 0x1FAB6 }, -{ 0x1FAC0, 0x1FAC2 }, -{ 0x1FAD0, 0x1FAD6 }, +{ 0x1FA90, 0x1FAAC }, +{ 0x1FAB0, 0x1FABA }, +{ 0x1FAC0, 0x1FAC5 }, +{ 0x1FAD0, 0x1FAD9 }, +{ 0x1FAE0, 0x1FAE7 }, +{ 0x1FAF0, 0x1FAF6 }, { 0x20000, 0x2FFFD }, { 0x30000, 0x3FFFD } }; @@ -145,6 +145,18 @@ void *xcalloc(size_t nmemb, size_t size) return ret; } +void xsetenv(const char *name, const char *value, int overwrite) +{ + if (setenv(name, value, overwrite)) + die_errno(_("could not setenv '%s'"), name ? name : "(null)"); +} + +void xunsetenv(const char *name) +{ + if (!unsetenv(name)) + die_errno(_("could not unsetenv '%s'"), name ? name : "(null)"); +} + /* * Limit size of IO chunks, because huge chunks only cause pain. OS X * 64-bit is buggy, returning EINVAL if len >= INT_MAX; and even in |