diff options
215 files changed, 5202 insertions, 1702 deletions
diff --git a/.gitignore b/.gitignore index 3284a1e9b1..3524803da5 100644 --- a/.gitignore +++ b/.gitignore @@ -207,6 +207,7 @@ /config.mak.autogen /config.mak.append /configure +/.vscode/ /tags /TAGS /cscope* diff --git a/Documentation/.gitignore b/Documentation/.gitignore index c7096f11f1..3ef54e0adb 100644 --- a/Documentation/.gitignore +++ b/Documentation/.gitignore @@ -12,3 +12,4 @@ cmds-*.txt mergetools-*.txt manpage-base-url.xsl SubmittingPatches.txt +tmp-doc-diff/ diff --git a/Documentation/RelNotes/2.19.0.txt b/Documentation/RelNotes/2.19.0.txt index 8a0361321f..8f8da9f9c8 100644 --- a/Documentation/RelNotes/2.19.0.txt +++ b/Documentation/RelNotes/2.19.0.txt @@ -50,6 +50,29 @@ UI, Workflows & Features ancestor discovery during the "git fetch" transaction. (merge 42cc7485a2 jt/fetch-negotiator-skipping later to maint). + * A new configuration variable core.usereplacerefs has been added, + primarily to help server installations that want to ignore the + replace mechanism altogether. + + * Teach "git tag -s" etc. a few configuration variables (gpg.format + that can be set to "openpgp" or "x509", and gpg.<format>.program + that is used to specify what program to use to deal with the format) + to allow x.509 certs with CMS via "gpgsm" to be used instead of + openpgp via "gnupg". + + * Many more strings are prepared for l10n. + + * "git p4 submit" learns to ask its own pre-submit hook if it should + continue with submitting. + + * The test performed at the receiving end of "git push" to prevent + bad objects from entering repository can be customized via + receive.fsck.* configuration variables; we now have gained a + counterpart to do the same on the "git fetch" side, with + fetch.fsck.* configuration variables. + + * "git pull --rebase=interactive" learned "i" as a short-hand for + "interactive". Performance, Internal Implementation, Development Support etc. @@ -157,6 +180,46 @@ Performance, Internal Implementation, Development Support etc. * The singleton commit-graph in-core instance is made per in-core repository instance. + * "make DEVELOPER=1 DEVOPTS=pedantic" allows developers to compile + with -pedantic option, which may catch more problematic program + constructs and potential bugs. + + * Preparatory code to later add json output for telemetry data has + been added. + + * Update the way we use Coccinelle to find out-of-style code that + need to be modernised. + + * It is too easy to misuse system API functions such as strcat(); + these selected functions are now forbidden in this codebase and + will cause a compilation failure. + + * Add a script (in contrib/) to help users of VSCode work better with + our codebase. + + * The Travis CI scripts were taught to ship back the test data from + failed tests. + (merge aea8879a6a sg/travis-retrieve-trash-upon-failure later to maint). + + * The parse-options machinery learned to refrain from enclosing + placeholder string inside a "<bra" and "ket>" pair automatically + without PARSE_OPT_LITERAL_ARGHELP. Existing help text for option + arguments that are not formatted correctly have been identified and + fixed. + (merge 5f0df44cd7 rs/parse-opt-lithelp later to maint). + + * Noiseword "extern" has been removed from function decls in the + header files. + + * A few atoms like %(objecttype) and %(objectsize) in the format + specifier of "for-each-ref --format=<format>" can be filled without + getting the full contents of the object, but just with the object + header. These cases have been optimized by calling + oid_object_info() API (instead of reading and inspecting the data). + + * The end result of documentation update has been made to be + inspected more easily to help developers. + Fixes since v2.18 ----------------- @@ -329,6 +392,73 @@ Fixes since v2.18 * The lazy clone support had a few places where missing but promised objects were not correctly tolerated, which have been fixed. + * One of the "diff --color-moved" mode "dimmed_zebra" that was named + in an unusual way has been deprecated and replaced by + "dimmed-zebra". + (merge e3f2f5f9cd es/diff-color-moved-fix later to maint). + + * The wire-protocol v2 relies on the client to send "ref prefixes" to + limit the bandwidth spent on the initial ref advertisement. "git + clone" when learned to speak v2 forgot to do so, which has been + corrected. + (merge 402c47d939 bw/clone-ref-prefixes later to maint). + + * "git diff --histogram" had a bad memory usage pattern, which has + been rearranged to reduce the peak usage. + (merge 79cb2ebb92 sb/histogram-less-memory later to maint). + + * Code clean-up to use size_t/ssize_t when they are the right type. + (merge 7726d360b5 jk/size-t later to maint). + + * The wire-protocol v2 relies on the client to send "ref prefixes" to + limit the bandwidth spent on the initial ref advertisement. "git + fetch $remote branch:branch" that asks tags that point into the + history leading to the "branch" automatically followed sent to + narrow prefix and broke the tag following, which has been fixed. + (merge 2b554353a5 jt/tag-following-with-proto-v2-fix later to maint). + + * When the sparse checkout feature is in use, "git cherry-pick" and + other mergy operations lost the skip_worktree bit when a path that + is excluded from checkout requires content level merge, which is + resolved as the same as the HEAD version, without materializing the + merge result in the working tree, which made the path appear as + deleted. This has been corrected by preserving the skip_worktree + bit (and not materializing the file in the working tree). + (merge 2b75fb601c en/merge-recursive-skip-fix later to maint). + + * The "author-script" file "git rebase -i" creates got broken when + we started to move the command away from shell script, which is + getting fixed now. + (merge 5522bbac20 es/rebase-i-author-script-fix later to maint). + + * The automatic tree-matching in "git merge -s subtree" was broken 5 + years ago and nobody has noticed since then, which is now fixed. + (merge 2ec4150713 jk/merge-subtree-heuristics later to maint). + + * "git fetch $there refs/heads/s" ought to fetch the tip of the + branch 's', but when "refs/heads/refs/heads/s", i.e. a branch whose + name is "refs/heads/s" exists at the same time, fetched that one + instead by mistake. This has been corrected to honor the usual + disambiguation rules for abbreviated refnames. + (merge 60650a48c0 jt/refspec-dwim-precedence-fix later to maint). + + * Futureproofing a helper function that can easily be misused. + (merge 65bb21e77e es/want-color-fd-defensive later to maint). + + * The http-backend (used for smart-http transport) used to slurp the + whole input until EOF, without paying attention to CONTENT_LENGTH + that is supplied in the environment and instead expecting the Web + server to close the input stream. This has been fixed. + (merge eebfe40962 mk/http-backend-content-length later to maint). + + * "git merge --abort" etc. did not clean things up properly when + there were conflicted entries in the index in certain order that + are involved in D/F conflicts. This has been corrected. + (merge ad3762042a en/abort-df-conflict-fixes later to maint). + + * "git diff --indent-heuristic" had a bad corner case performance. + (merge 301ef85401 sb/indent-heuristic-optim later to maint). + * Code cleanup, docfix, build fix, etc. (merge aee9be2ebe sg/update-ref-stdin-cleanup later to maint). (merge 037714252f jc/clean-after-sanity-tests later to maint). @@ -344,3 +474,14 @@ Fixes since v2.18 (merge 6aaded5509 tb/config-default later to maint). (merge 022d2ac1f3 sb/blame-color later to maint). (merge 5a06a20e0c bp/test-drop-caches-for-windows later to maint). + (merge dd61cc1c2e jk/ui-color-always-to-auto later to maint). + (merge 1e83b9bfdd sb/trailers-docfix later to maint). + (merge ab29f1b329 sg/fast-import-dump-refs-on-checkpoint-fix later to maint). + (merge 6a8ad880f0 jn/subtree-test-fixes later to maint). + (merge ffbd51cc60 nd/pack-objects-threading-doc later to maint). + (merge e9dac7be60 es/mw-to-git-chain-fix later to maint). + (merge fe583c6c7a rs/remote-mv-leakfix later to maint). + (merge 69885ab015 en/t3031-title-fix later to maint). + (merge 8578037bed nd/config-blame-sort later to maint). + (merge 8ad169c4ba hn/config-in-code-comment later to maint). + (merge b7446fcfdf ar/t4150-am-scissors-test-fix later to maint). diff --git a/Documentation/config.txt b/Documentation/config.txt index 63365dcf3d..95ad715a44 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -924,6 +924,11 @@ gc.commitGraph:: required. Default is false. See linkgit:git-commit-graph[1] for details. +core.useReplaceRefs:: + If set to `false`, behave as if the `--no-replace-objects` + option was given on the command line. See linkgit:git[1] and + linkgit:git-replace[1] for more information. + core.sparseCheckout:: Enable "sparse checkout" feature. See section "Sparse checkout" in linkgit:git-read-tree[1] for more information. @@ -990,23 +995,28 @@ apply.whitespace:: Tells 'git apply' how to handle whitespaces, in the same way as the `--whitespace` option. See linkgit:git-apply[1]. -blame.showRoot:: - Do not treat root commits as boundaries in linkgit:git-blame[1]. - This option defaults to false. - blame.blankBoundary:: Show blank commit object name for boundary commits in linkgit:git-blame[1]. This option defaults to false. -blame.showEmail:: - Show the author email instead of author name in linkgit:git-blame[1]. - This option defaults to false. +blame.coloring:: + This determines the coloring scheme to be applied to blame + output. It can be 'repeatedLines', 'highlightRecent', + or 'none' which is the default. blame.date:: Specifies the format used to output dates in linkgit:git-blame[1]. If unset the iso format is used. For supported values, see the discussion of the `--date` option at linkgit:git-log[1]. +blame.showEmail:: + Show the author email instead of author name in linkgit:git-blame[1]. + This option defaults to false. + +blame.showRoot:: + Do not treat root commits as boundaries in linkgit:git-blame[1]. + This option defaults to false. + branch.autoSetupMerge:: Tells 'git branch' and 'git checkout' to set up new branches so that linkgit:git-pull[1] will appropriately merge from the @@ -1144,6 +1154,28 @@ color.advice:: color.advice.hint:: Use customized color for hints. +color.blame.highlightRecent:: + This can be used to color the metadata of a blame line depending + on age of the line. ++ +This setting should be set to a comma-separated list of color and date settings, +starting and ending with a color, the dates should be set from oldest to newest. +The metadata will be colored given the colors if the the line was introduced +before the given timestamp, overwriting older timestamped colors. ++ +Instead of an absolute timestamp relative timestamps work as well, e.g. +2.weeks.ago is valid to address anything older than 2 weeks. ++ +It defaults to 'blue,12 month ago,white,1 month ago,red', which colors +everything older than one year blue, recent changes between one month and +one year old are kept white, and lines introduced within the last month are +colored red. + +color.blame.repeatedLines:: + Use the customized color for the part of git-blame output that + is repeated meta information per line (such as commit id, + author name, date and timezone). Defaults to cyan. + color.branch:: A boolean to enable/disable color in the output of linkgit:git-branch[1]. May be set to `always`, @@ -1291,33 +1323,6 @@ color.status.<slot>:: status short-format), or `unmerged` (files which have unmerged changes). -color.blame.repeatedLines:: - Use the customized color for the part of git-blame output that - is repeated meta information per line (such as commit id, - author name, date and timezone). Defaults to cyan. - -color.blame.highlightRecent:: - This can be used to color the metadata of a blame line depending - on age of the line. -+ -This setting should be set to a comma-separated list of color and date settings, -starting and ending with a color, the dates should be set from oldest to newest. -The metadata will be colored given the colors if the the line was introduced -before the given timestamp, overwriting older timestamped colors. -+ -Instead of an absolute timestamp relative timestamps work as well, e.g. -2.weeks.ago is valid to address anything older than 2 weeks. -+ -It defaults to 'blue,12 month ago,white,1 month ago,red', which colors -everything older than one year blue, recent changes between one month and -one year old are kept white, and lines introduced within the last month are -colored red. - -blame.coloring:: - This determines the coloring scheme to be applied to blame - output. It can be 'repeatedLines', 'highlightRecent', - or 'none' which is the default. - color.transport:: A boolean to enable/disable color when pushes are rejected. May be set to `always`, `false` (or `never`) or `auto` (or `true`), in which @@ -1497,10 +1502,19 @@ fetch.recurseSubmodules:: fetch.fsckObjects:: If it is set to true, git-fetch-pack will check all fetched - objects. It will abort in the case of a malformed object or a - broken link. The result of an abort are only dangling objects. - Defaults to false. If not set, the value of `transfer.fsckObjects` - is used instead. + objects. See `transfer.fsckObjects` for what's + checked. Defaults to false. If not set, the value of + `transfer.fsckObjects` is used instead. + +fetch.fsck.<msg-id>:: + Acts like `fsck.<msg-id>`, but is used by + linkgit:git-fetch-pack[1] instead of linkgit:git-fsck[1]. See + the `fsck.<msg-id>` documentation for details. + +fetch.fsck.skipList:: + Acts like `fsck.skipList`, but is used by + linkgit:git-fetch-pack[1] instead of linkgit:git-fsck[1]. See + the `fsck.skipList` documentation for details. fetch.unpackLimit:: If the number of objects fetched over the Git native @@ -1536,9 +1550,12 @@ fetch.negotiationAlgorithm:: sent when negotiating the contents of the packfile to be sent by the server. Set to "skipping" to use an algorithm that skips commits in an effort to converge faster, but may result in a larger-than-necessary - packfile; any other value instructs Git to use the default algorithm + packfile; The default is "default" which instructs Git to use the default algorithm that never skips commits (unless the server has acknowledged it or one of its descendants). + Unknown values will cause 'git fetch' to error out. ++ +See also the `--negotiation-tip` option for linkgit:git-fetch[1]. format.attach:: Enable multipart/mixed attachments as the default for @@ -1639,15 +1656,42 @@ filter.<driver>.smudge:: linkgit:gitattributes[5] for details. fsck.<msg-id>:: - Allows overriding the message type (error, warn or ignore) of a - specific message ID such as `missingEmail`. -+ -For convenience, fsck prefixes the error/warning with the message ID, -e.g. "missingEmail: invalid author/committer line - missing email" means -that setting `fsck.missingEmail = ignore` will hide that issue. -+ -This feature is intended to support working with legacy repositories -which cannot be repaired without disruptive changes. + During fsck git may find issues with legacy data which + wouldn't be generated by current versions of git, and which + wouldn't be sent over the wire if `transfer.fsckObjects` was + set. This feature is intended to support working with legacy + repositories containing such data. ++ +Setting `fsck.<msg-id>` will be picked up by linkgit:git-fsck[1], but +to accept pushes of such data set `receive.fsck.<msg-id>` instead, or +to clone or fetch it set `fetch.fsck.<msg-id>`. ++ +The rest of the documentation discusses `fsck.*` for brevity, but the +same applies for the corresponding `receive.fsck.*` and +`fetch.<msg-id>.*`. variables. ++ +Unlike variables like `color.ui` and `core.editor` the +`receive.fsck.<msg-id>` and `fetch.fsck.<msg-id>` variables will not +fall back on the `fsck.<msg-id>` configuration if they aren't set. To +uniformly configure the same fsck settings in different circumstances +all three of them they must all set to the same values. ++ +When `fsck.<msg-id>` is set, errors can be switched to warnings and +vice versa by configuring the `fsck.<msg-id>` setting where the +`<msg-id>` is the fsck message ID and the value is one of `error`, +`warn` or `ignore`. For convenience, fsck prefixes the error/warning +with the message ID, e.g. "missingEmail: invalid author/committer line +- missing email" means that setting `fsck.missingEmail = ignore` will +hide that issue. ++ +In general, it is better to enumerate existing objects with problems +with `fsck.skipList`, instead of listing the kind of breakages these +problematic objects share to be ignored, as doing the latter will +allow new instances of the same breakages go unnoticed. ++ +Setting an unknown `fsck.<msg-id>` value will cause fsck to die, but +doing the same for `receive.fsck.<msg-id>` and `fetch.fsck.<msg-id>` +will only cause git to warn. fsck.skipList:: The path to a sorted list of object names (i.e. one SHA-1 per @@ -1656,6 +1700,15 @@ fsck.skipList:: should be accepted despite early commits containing errors that can be safely ignored such as invalid committer email addresses. Note: corrupt objects cannot be skipped with this setting. ++ +Like `fsck.<msg-id>` this variable has corresponding +`receive.fsck.skipList` and `fetch.fsck.skipList` variants. ++ +Unlike variables like `color.ui` and `core.editor` the +`receive.fsck.skipList` and `fetch.fsck.skipList` variables will not +fall back on the `fsck.skipList` configuration if they aren't set. To +uniformly configure the same fsck settings in different circumstances +all three of them they must all set to the same values. gc.aggressiveDepth:: The depth parameter used in the delta compression @@ -1879,6 +1932,16 @@ gpg.program:: signed, and the program is expected to send the result to its standard output. +gpg.format:: + Specifies which key format to use when signing with `--gpg-sign`. + Default is "openpgp" and another possible value is "x509". + +gpg.<format>.program:: + Use this to customize the program used for the signing format you + chose. (see `gpg.program` and `gpg.format`) `gpg.program` can still + be used as a legacy synonym for `gpg.openpgp.program`. The default + value for `gpg.x509.program` is "gpgsm". + gui.commitMsgWidth:: Defines how wide the commit message window is in the linkgit:git-gui[1]. "75" is the default. @@ -2932,32 +2995,21 @@ receive.certNonceSlop:: receive.fsckObjects:: If it is set to true, git-receive-pack will check all received - objects. It will abort in the case of a malformed object or a - broken link. The result of an abort are only dangling objects. - Defaults to false. If not set, the value of `transfer.fsckObjects` - is used instead. + objects. See `transfer.fsckObjects` for what's checked. + Defaults to false. If not set, the value of + `transfer.fsckObjects` is used instead. receive.fsck.<msg-id>:: - When `receive.fsckObjects` is set to true, errors can be switched - to warnings and vice versa by configuring the `receive.fsck.<msg-id>` - setting where the `<msg-id>` is the fsck message ID and the value - is one of `error`, `warn` or `ignore`. For convenience, fsck prefixes - the error/warning with the message ID, e.g. "missingEmail: invalid - author/committer line - missing email" means that setting - `receive.fsck.missingEmail = ignore` will hide that issue. -+ -This feature is intended to support working with legacy repositories -which would not pass pushing when `receive.fsckObjects = true`, allowing -the host to accept repositories with certain known issues but still catch -other issues. + Acts like `fsck.<msg-id>`, but is used by + linkgit:git-receive-pack[1] instead of + linkgit:git-fsck[1]. See the `fsck.<msg-id>` documentation for + details. receive.fsck.skipList:: - The path to a sorted list of object names (i.e. one SHA-1 per - line) that are known to be broken in a non-fatal way and should - be ignored. This feature is useful when an established project - should be accepted despite early commits containing errors that - can be safely ignored such as invalid committer email addresses. - Note: corrupt objects cannot be skipped with this setting. + Acts like `fsck.skipList`, but is used by + linkgit:git-receive-pack[1] instead of + linkgit:git-fsck[1]. See the `fsck.skipList` documentation for + details. receive.keepAlive:: After receiving the pack from the client, `receive-pack` may @@ -3432,6 +3484,40 @@ transfer.fsckObjects:: When `fetch.fsckObjects` or `receive.fsckObjects` are not set, the value of this variable is used instead. Defaults to false. ++ +When set, the fetch or receive will abort in the case of a malformed +object or a link to a nonexistent object. In addition, various other +issues are checked for, including legacy issues (see `fsck.<msg-id>`), +and potential security issues like the existence of a `.GIT` directory +or a malicious `.gitmodules` file (see the release notes for v2.2.1 +and v2.17.1 for details). Other sanity and security checks may be +added in future releases. ++ +On the receiving side, failing fsckObjects will make those objects +unreachable, see "QUARANTINE ENVIRONMENT" in +linkgit:git-receive-pack[1]. On the fetch side, malformed objects will +instead be left unreferenced in the repository. ++ +Due to the non-quarantine nature of the `fetch.fsckObjects` +implementation it can not be relied upon to leave the object store +clean like `receive.fsckObjects` can. ++ +As objects are unpacked they're written to the object store, so there +can be cases where malicious objects get introduced even though the +"fetch" failed, only to have a subsequent "fetch" succeed because only +new incoming objects are checked, not those that have already been +written to the object store. That difference in behavior should not be +relied upon. In the future, such objects may be quarantined for +"fetch" as well. ++ +For now, the paranoid need to find some way to emulate the quarantine +environment if they'd like the same protection as "push". E.g. in the +case of an internal mirror do the mirroring in two steps, one to fetch +the untrusted objects, and then do a second "push" (which will use the +quarantine) to another internal repo, and have internal clients +consume this pushed-to repository, or embargo internal fetches and +only allow them once a full "fsck" has run (and no new fetches have +happened in the meantime). transfer.hideRefs:: String(s) `receive-pack` and `upload-pack` use to decide which diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index f394608b42..0378cd574e 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -286,10 +286,11 @@ zebra:: are painted using either the 'color.diff.{old,new}Moved' color or 'color.diff.{old,new}MovedAlternative'. The change between the two colors indicates that a new block was detected. -dimmed_zebra:: +dimmed-zebra:: Similar to 'zebra', but additional dimming of uninteresting parts of moved code is performed. The bordering lines of two adjacent blocks are considered interesting, the rest is uninteresting. + `dimmed_zebra` is a deprecated synonym. -- --color-moved-ws=<modes>:: diff --git a/Documentation/doc-diff b/Documentation/doc-diff new file mode 100755 index 0000000000..f483fe427c --- /dev/null +++ b/Documentation/doc-diff @@ -0,0 +1,109 @@ +#!/bin/sh + +OPTIONS_SPEC="\ +doc-diff [options] <from> <to> [-- <diff-options>] +-- +j=n parallel argument to pass to make +f force rebuild; do not rely on cached results +" +SUBDIRECTORY_OK=1 +. "$(git --exec-path)/git-sh-setup" + +parallel= +force= +while test $# -gt 0 +do + case "$1" in + -j) + parallel=$2; shift ;; + -f) + force=t ;; + --) + shift; break ;; + *) + usage ;; + esac + shift +done + +if test -z "$parallel" +then + parallel=$(getconf _NPROCESSORS_ONLN 2>/dev/null) + if test $? != 0 || test -z "$parallel" + then + parallel=1 + fi +fi + +test $# -gt 1 || usage +from=$1; shift +to=$1; shift + +from_oid=$(git rev-parse --verify "$from") || exit 1 +to_oid=$(git rev-parse --verify "$to") || exit 1 + +cd_to_toplevel +tmp=Documentation/tmp-doc-diff + +if test -n "$force" +then + rm -rf "$tmp" +fi + +# We'll do both builds in a single worktree, which lets "make" reuse +# results that don't differ between the two trees. +if ! test -d "$tmp/worktree" +then + git worktree add --detach "$tmp/worktree" "$from" && + dots=$(echo "$tmp/worktree" | sed 's#[^/]*#..#g') && + ln -s "$dots/config.mak" "$tmp/worktree/config.mak" +fi + +# generate_render_makefile <srcdir> <dstdir> +generate_render_makefile () { + find "$1" -type f | + while read src + do + dst=$2/${src#$1/} + printf 'all:: %s\n' "$dst" + printf '%s: %s\n' "$dst" "$src" + printf '\t@echo >&2 " RENDER $(notdir $@)" && \\\n' + printf '\tmkdir -p $(dir $@) && \\\n' + printf '\tMANWIDTH=80 man -l $< >$@+ && \\\n' + printf '\tmv $@+ $@\n' + done +} + +# render_tree <dirname> <committish> +render_tree () { + # Skip install-man entirely if we already have an installed directory. + # We can't rely on make here, since "install-man" unconditionally + # copies the files (spending effort, but also updating timestamps that + # we then can't rely on during the render step). We use "mv" to make + # sure we don't get confused by a previous run that failed partway + # through. + if ! test -d "$tmp/installed/$1" + then + git -C "$tmp/worktree" checkout "$2" && + make -j$parallel -C "$tmp/worktree" \ + GIT_VERSION=omitted \ + SOURCE_DATE_EPOCH=0 \ + DESTDIR="$PWD/$tmp/installed/$1+" \ + install-man && + mv "$tmp/installed/$1+" "$tmp/installed/$1" + fi && + + # As with "installed" above, we skip the render if it's already been + # done. So using make here is primarily just about running in + # parallel. + if ! test -d "$tmp/rendered/$1" + then + generate_render_makefile "$tmp/installed/$1" "$tmp/rendered/$1+" | + make -j$parallel -f - && + mv "$tmp/rendered/$1+" "$tmp/rendered/$1" + fi +} + +render_tree $from_oid "$from" && +render_tree $to_oid "$to" && +git -C $tmp/rendered diff --no-index "$@" $from_oid $to_oid diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 2d09f87b4b..8bc36af4b1 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -57,6 +57,9 @@ commits reachable from any of the given commits. The argument to this option may be a glob on ref names, a ref, or the (possibly abbreviated) SHA-1 of a commit. Specifying a glob is equivalent to specifying this option multiple times, one for each matching ref name. ++ +See also the `fetch.negotiationAlgorithm` configuration variable +documented in linkgit:git-config[1]. ifndef::git-pull[] --dry-run:: diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt index 085d177d97..901faef1bf 100644 --- a/Documentation/git-for-each-ref.txt +++ b/Documentation/git-for-each-ref.txt @@ -57,7 +57,7 @@ OPTIONS `xx`; for example `%00` interpolates to `\0` (NUL), `%09` to `\t` (TAB) and `%0a` to `\n` (LF). ---color[=<when>]: +--color[=<when>]:: Respect any colors specified in the `--format` option. The `<when>` field must be one of `always`, `never`, or `auto` (if `<when>` is absent, behave as if `always` was given). diff --git a/Documentation/git-interpret-trailers.txt b/Documentation/git-interpret-trailers.txt index 9111c47a1b..b8fafb1e8b 100644 --- a/Documentation/git-interpret-trailers.txt +++ b/Documentation/git-interpret-trailers.txt @@ -88,7 +88,8 @@ OPTIONS Specify where all new trailers will be added. A setting provided with '--where' overrides all configuration variables and applies to all '--trailer' options until the next occurrence of - '--where' or '--no-where'. + '--where' or '--no-where'. Possible values are `after`, `before`, + `end` or `start`. --if-exists <action>:: --no-if-exists:: @@ -96,7 +97,8 @@ OPTIONS least one trailer with the same <token> in the message. A setting provided with '--if-exists' overrides all configuration variables and applies to all '--trailer' options until the next occurrence of - '--if-exists' or '--no-if-exists'. + '--if-exists' or '--no-if-exists'. Possible actions are `addIfDifferent`, + `addIfDifferentNeighbor`, `add`, `replace` and `doNothing`. --if-missing <action>:: --no-if-missing:: @@ -104,7 +106,8 @@ OPTIONS trailer with the same <token> in the message. A setting provided with '--if-missing' overrides all configuration variables and applies to all '--trailer' options until the next occurrence of - '--if-missing' or '--no-if-missing'. + '--if-missing' or '--no-if-missing'. Possible actions are `doNothing` + or `add`. --only-trailers:: Output only the trailers, not any other parts of the input. diff --git a/Documentation/git-p4.txt b/Documentation/git-p4.txt index f0de3b891b..41780a5aa9 100644 --- a/Documentation/git-p4.txt +++ b/Documentation/git-p4.txt @@ -374,6 +374,14 @@ These options can be used to modify 'git p4 submit' behavior. been submitted. Implies --disable-rebase. Can also be set with git-p4.disableP4Sync. Sync with origin/master still goes ahead if possible. +Hook for submit +~~~~~~~~~~~~~~~ +The `p4-pre-submit` hook is executed if it exists and is executable. +The hook takes no parameters and nothing from standard input. Exiting with +non-zero status from this script prevents `git-p4 submit` from launching. + +One usage scenario is to run unit tests in the hook. + Rebase options ~~~~~~~~~~~~~~ These options can be used to modify 'git p4 rebase' behavior. diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index 87c4288ffc..92f9c12b87 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -115,7 +115,7 @@ options for details. variable if it exists, or lexicographic order otherwise. See linkgit:git-config[1]. ---color[=<when>]: +--color[=<when>]:: Respect any colors specified in the `--format` option. The `<when>` field must be one of `always`, `never`, or `auto` (if `<when>` is absent, behave as if `always` was given). diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt index e3c283a174..959044347e 100644 --- a/Documentation/githooks.txt +++ b/Documentation/githooks.txt @@ -485,6 +485,13 @@ The exit status determines whether git will use the data from the hook to limit its search. On error, it will fall back to verifying all files and folders. +p4-pre-submit +~~~~~~~~~~~~~ + +This hook is invoked by `git-p4 submit`. It takes no parameters and nothing +from standard input. Exiting with non-zero status from this script prevent +`git-p4 submit` from launching. Run `git-p4 submit --help` for details. + GIT --- Part of the linkgit:git[1] suite diff --git a/Documentation/technical/hash-function-transition.txt b/Documentation/technical/hash-function-transition.txt index 4ab6cd1012..bc2ace2a6e 100644 --- a/Documentation/technical/hash-function-transition.txt +++ b/Documentation/technical/hash-function-transition.txt @@ -59,14 +59,11 @@ that are believed to be cryptographically secure. Goals ----- -Where NewHash is a strong 256-bit hash function to replace SHA-1 (see -"Selection of a New Hash", below): - -1. The transition to NewHash can be done one local repository at a time. +1. The transition to SHA-256 can be done one local repository at a time. a. Requiring no action by any other party. - b. A NewHash repository can communicate with SHA-1 Git servers + b. A SHA-256 repository can communicate with SHA-1 Git servers (push/fetch). - c. Users can use SHA-1 and NewHash identifiers for objects + c. Users can use SHA-1 and SHA-256 identifiers for objects interchangeably (see "Object names on the command line", below). d. New signed objects make use of a stronger hash function than SHA-1 for their security guarantees. @@ -79,7 +76,7 @@ Where NewHash is a strong 256-bit hash function to replace SHA-1 (see Non-Goals --------- -1. Add NewHash support to Git protocol. This is valuable and the +1. Add SHA-256 support to Git protocol. This is valuable and the logical next step but it is out of scope for this initial design. 2. Transparently improving the security of existing SHA-1 signed objects. @@ -87,26 +84,26 @@ Non-Goals repository. 4. Taking the opportunity to fix other bugs in Git's formats and protocols. -5. Shallow clones and fetches into a NewHash repository. (This will - change when we add NewHash support to Git protocol.) -6. Skip fetching some submodules of a project into a NewHash - repository. (This also depends on NewHash support in Git +5. Shallow clones and fetches into a SHA-256 repository. (This will + change when we add SHA-256 support to Git protocol.) +6. Skip fetching some submodules of a project into a SHA-256 + repository. (This also depends on SHA-256 support in Git protocol.) Overview -------- We introduce a new repository format extension. Repositories with this -extension enabled use NewHash instead of SHA-1 to name their objects. +extension enabled use SHA-256 instead of SHA-1 to name their objects. This affects both object names and object content --- both the names of objects and all references to other objects within an object are switched to the new hash function. -NewHash repositories cannot be read by older versions of Git. +SHA-256 repositories cannot be read by older versions of Git. -Alongside the packfile, a NewHash repository stores a bidirectional -mapping between NewHash and SHA-1 object names. The mapping is generated +Alongside the packfile, a SHA-256 repository stores a bidirectional +mapping between SHA-256 and SHA-1 object names. The mapping is generated locally and can be verified using "git fsck". Object lookups use this -mapping to allow naming objects using either their SHA-1 and NewHash names +mapping to allow naming objects using either their SHA-1 and SHA-256 names interchangeably. "git cat-file" and "git hash-object" gain options to display an object @@ -116,7 +113,7 @@ object database so that they can be named using the appropriate name (using the bidirectional hash mapping). Fetches from a SHA-1 based server convert the fetched objects into -NewHash form and record the mapping in the bidirectional mapping table +SHA-256 form and record the mapping in the bidirectional mapping table (see below for details). Pushes to a SHA-1 based server convert the objects being pushed into sha1 form so the server does not have to be aware of the hash function the client is using. @@ -125,19 +122,19 @@ Detailed Design --------------- Repository format extension ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -A NewHash repository uses repository format version `1` (see +A SHA-256 repository uses repository format version `1` (see Documentation/technical/repository-version.txt) with extensions `objectFormat` and `compatObjectFormat`: [core] repositoryFormatVersion = 1 [extensions] - objectFormat = newhash + objectFormat = sha256 compatObjectFormat = sha1 The combination of setting `core.repositoryFormatVersion=1` and populating `extensions.*` ensures that all versions of Git later than -`v0.99.9l` will die instead of trying to operate on the NewHash +`v0.99.9l` will die instead of trying to operate on the SHA-256 repository, instead producing an error message. # Between v0.99.9l and v2.7.0 @@ -155,36 +152,36 @@ repository extensions. Object names ~~~~~~~~~~~~ Objects can be named by their 40 hexadecimal digit sha1-name or 64 -hexadecimal digit newhash-name, plus names derived from those (see +hexadecimal digit sha256-name, plus names derived from those (see gitrevisions(7)). The sha1-name of an object is the SHA-1 of the concatenation of its type, length, a nul byte, and the object's sha1-content. This is the traditional <sha1> used in Git to name objects. -The newhash-name of an object is the NewHash of the concatenation of its -type, length, a nul byte, and the object's newhash-content. +The sha256-name of an object is the SHA-256 of the concatenation of its +type, length, a nul byte, and the object's sha256-content. Object format ~~~~~~~~~~~~~ The content as a byte sequence of a tag, commit, or tree object named -by sha1 and newhash differ because an object named by newhash-name refers to -other objects by their newhash-names and an object named by sha1-name +by sha1 and sha256 differ because an object named by sha256-name refers to +other objects by their sha256-names and an object named by sha1-name refers to other objects by their sha1-names. -The newhash-content of an object is the same as its sha1-content, except -that objects referenced by the object are named using their newhash-names +The sha256-content of an object is the same as its sha1-content, except +that objects referenced by the object are named using their sha256-names instead of sha1-names. Because a blob object does not refer to any -other object, its sha1-content and newhash-content are the same. +other object, its sha1-content and sha256-content are the same. -The format allows round-trip conversion between newhash-content and +The format allows round-trip conversion between sha256-content and sha1-content. Object storage ~~~~~~~~~~~~~~ Loose objects use zlib compression and packed objects use the packed format described in Documentation/technical/pack-format.txt, just like -today. The content that is compressed and stored uses newhash-content +today. The content that is compressed and stored uses sha256-content instead of sha1-content. Pack index @@ -255,10 +252,10 @@ network byte order): up to and not including the table of CRC32 values. - Zero or more NUL bytes. - The trailer consists of the following: - - A copy of the 20-byte NewHash checksum at the end of the + - A copy of the 20-byte SHA-256 checksum at the end of the corresponding packfile. - - 20-byte NewHash checksum of all of the above. + - 20-byte SHA-256 checksum of all of the above. Loose object index ~~~~~~~~~~~~~~~~~~ @@ -266,7 +263,7 @@ A new file $GIT_OBJECT_DIR/loose-object-idx contains information about all loose objects. Its format is # loose-object-idx - (newhash-name SP sha1-name LF)* + (sha256-name SP sha1-name LF)* where the object names are in hexadecimal format. The file is not sorted. @@ -292,8 +289,8 @@ To remove entries (e.g. in "git pack-refs" or "git-prune"): Translation table ~~~~~~~~~~~~~~~~~ The index files support a bidirectional mapping between sha1-names -and newhash-names. The lookup proceeds similarly to ordinary object -lookups. For example, to convert a sha1-name to a newhash-name: +and sha256-names. The lookup proceeds similarly to ordinary object +lookups. For example, to convert a sha1-name to a sha256-name: 1. Look for the object in idx files. If a match is present in the idx's sorted list of truncated sha1-names, then: @@ -301,8 +298,8 @@ lookups. For example, to convert a sha1-name to a newhash-name: name order mapping. b. Read the corresponding entry in the full sha1-name table to verify we found the right object. If it is, then - c. Read the corresponding entry in the full newhash-name table. - That is the object's newhash-name. + c. Read the corresponding entry in the full sha256-name table. + That is the object's sha256-name. 2. Check for a loose object. Read lines from loose-object-idx until we find a match. @@ -318,25 +315,25 @@ for all objects in the object store. Reading an object's sha1-content ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The sha1-content of an object can be read by converting all newhash-names -its newhash-content references to sha1-names using the translation table. +The sha1-content of an object can be read by converting all sha256-names +its sha256-content references to sha1-names using the translation table. Fetch ~~~~~ Fetching from a SHA-1 based server requires translating between SHA-1 -and NewHash based representations on the fly. +and SHA-256 based representations on the fly. SHA-1s named in the ref advertisement that are present on the client -can be translated to NewHash and looked up as local objects using the +can be translated to SHA-256 and looked up as local objects using the translation table. Negotiation proceeds as today. Any "have"s generated locally are converted to SHA-1 before being sent to the server, and SHA-1s -mentioned by the server are converted to NewHash when looking them up +mentioned by the server are converted to SHA-256 when looking them up locally. After negotiation, the server sends a packfile containing the -requested objects. We convert the packfile to NewHash format using +requested objects. We convert the packfile to SHA-256 format using the following steps: 1. index-pack: inflate each object in the packfile and compute its @@ -351,12 +348,12 @@ the following steps: (This list only contains objects reachable from the "wants". If the pack from the server contained additional extraneous objects, then they will be discarded.) -3. convert to newhash: open a new (newhash) packfile. Read the topologically +3. convert to sha256: open a new (sha256) packfile. Read the topologically sorted list just generated. For each object, inflate its - sha1-content, convert to newhash-content, and write it to the newhash - pack. Record the new sha1<->newhash mapping entry for use in the idx. + sha1-content, convert to sha256-content, and write it to the sha256 + pack. Record the new sha1<->sha256 mapping entry for use in the idx. 4. sort: reorder entries in the new pack to match the order of objects - in the pack the server generated and include blobs. Write a newhash idx + in the pack the server generated and include blobs. Write a sha256 idx file 5. clean up: remove the SHA-1 based pack file, index, and topologically sorted list obtained from the server in steps 1 @@ -388,16 +385,16 @@ send-pack. Signed Commits ~~~~~~~~~~~~~~ -We add a new field "gpgsig-newhash" to the commit object format to allow +We add a new field "gpgsig-sha256" to the commit object format to allow signing commits without relying on SHA-1. It is similar to the -existing "gpgsig" field. Its signed payload is the newhash-content of the -commit object with any "gpgsig" and "gpgsig-newhash" fields removed. +existing "gpgsig" field. Its signed payload is the sha256-content of the +commit object with any "gpgsig" and "gpgsig-sha256" fields removed. This means commits can be signed 1. using SHA-1 only, as in existing signed commit objects -2. using both SHA-1 and NewHash, by using both gpgsig-newhash and gpgsig +2. using both SHA-1 and SHA-256, by using both gpgsig-sha256 and gpgsig fields. -3. using only NewHash, by only using the gpgsig-newhash field. +3. using only SHA-256, by only using the gpgsig-sha256 field. Old versions of "git verify-commit" can verify the gpgsig signature in cases (1) and (2) without modifications and view case (3) as an @@ -405,24 +402,24 @@ ordinary unsigned commit. Signed Tags ~~~~~~~~~~~ -We add a new field "gpgsig-newhash" to the tag object format to allow +We add a new field "gpgsig-sha256" to the tag object format to allow signing tags without relying on SHA-1. Its signed payload is the -newhash-content of the tag with its gpgsig-newhash field and "-----BEGIN PGP +sha256-content of the tag with its gpgsig-sha256 field and "-----BEGIN PGP SIGNATURE-----" delimited in-body signature removed. This means tags can be signed 1. using SHA-1 only, as in existing signed tag objects -2. using both SHA-1 and NewHash, by using gpgsig-newhash and an in-body +2. using both SHA-1 and SHA-256, by using gpgsig-sha256 and an in-body signature. -3. using only NewHash, by only using the gpgsig-newhash field. +3. using only SHA-256, by only using the gpgsig-sha256 field. Mergetag embedding ~~~~~~~~~~~~~~~~~~ The mergetag field in the sha1-content of a commit contains the sha1-content of a tag that was merged by that commit. -The mergetag field in the newhash-content of the same commit contains the -newhash-content of the same tag. +The mergetag field in the sha256-content of the same commit contains the +sha256-content of the same tag. Submodules ~~~~~~~~~~ @@ -497,7 +494,7 @@ Caveats ------- Invalid objects ~~~~~~~~~~~~~~~ -The conversion from sha1-content to newhash-content retains any +The conversion from sha1-content to sha256-content retains any brokenness in the original object (e.g., tree entry modes encoded with leading 0, tree objects whose paths are not sorted correctly, and commit objects without an author or committer). This is a deliberate @@ -516,7 +513,7 @@ allow lifting this restriction. Alternates ~~~~~~~~~~ -For the same reason, a newhash repository cannot borrow objects from a +For the same reason, a sha256 repository cannot borrow objects from a sha1 repository using objects/info/alternates or $GIT_ALTERNATE_OBJECT_REPOSITORIES. @@ -524,20 +521,20 @@ git notes ~~~~~~~~~ The "git notes" tool annotates objects using their sha1-name as key. This design does not describe a way to migrate notes trees to use -newhash-names. That migration is expected to happen separately (for +sha256-names. That migration is expected to happen separately (for example using a file at the root of the notes tree to describe which hash it uses). Server-side cost ~~~~~~~~~~~~~~~~ -Until Git protocol gains NewHash support, using NewHash based storage +Until Git protocol gains SHA-256 support, using SHA-256 based storage on public-facing Git servers is strongly discouraged. Once Git -protocol gains NewHash support, NewHash based servers are likely not +protocol gains SHA-256 support, SHA-256 based servers are likely not to support SHA-1 compatibility, to avoid what may be a very expensive hash reencode during clone and to encourage peers to modernize. The design described here allows fetches by SHA-1 clients of a -personal NewHash repository because it's not much more difficult than +personal SHA-256 repository because it's not much more difficult than allowing pushes from that repository. This support needs to be guarded by a configuration option --- servers like git.kernel.org that serve a large number of clients would not be expected to bear that cost. @@ -547,7 +544,7 @@ Meaning of signatures The signed payload for signed commits and tags does not explicitly name the hash used to identify objects. If some day Git adopts a new hash function with the same length as the current SHA-1 (40 -hexadecimal digit) or NewHash (64 hexadecimal digit) objects then the +hexadecimal digit) or SHA-256 (64 hexadecimal digit) objects then the intent behind the PGP signed payload in an object signature is unclear: @@ -562,7 +559,7 @@ Does this mean Git v2.12.0 is the commit with sha1-name e7e07d5a4fcc2a203d9873968ad3e6bd4d7419d7 or the commit with new-40-digit-hash-name e7e07d5a4fcc2a203d9873968ad3e6bd4d7419d7? -Fortunately NewHash and SHA-1 have different lengths. If Git starts +Fortunately SHA-256 and SHA-1 have different lengths. If Git starts using another hash with the same length to name objects, then it will need to change the format of signed payloads using that hash to address this issue. @@ -574,24 +571,24 @@ supports four different modes of operation: 1. ("dark launch") Treat object names input by the user as SHA-1 and convert any object names written to output to SHA-1, but store - objects using NewHash. This allows users to test the code with no + objects using SHA-256. This allows users to test the code with no visible behavior change except for performance. This allows allows running even tests that assume the SHA-1 hash function, to sanity-check the behavior of the new mode. - 2. ("early transition") Allow both SHA-1 and NewHash object names in + 2. ("early transition") Allow both SHA-1 and SHA-256 object names in input. Any object names written to output use SHA-1. This allows users to continue to make use of SHA-1 to communicate with peers (e.g. by email) that have not migrated yet and prepares for mode 3. - 3. ("late transition") Allow both SHA-1 and NewHash object names in - input. Any object names written to output use NewHash. In this + 3. ("late transition") Allow both SHA-1 and SHA-256 object names in + input. Any object names written to output use SHA-256. In this mode, users are using a more secure object naming method by default. The disruption is minimal as long as most of their peers are in mode 2 or mode 3. 4. ("post-transition") Treat object names input by the user as - NewHash and write output using NewHash. This is safer than mode 3 + SHA-256 and write output using SHA-256. This is safer than mode 3 because there is less risk that input is incorrectly interpreted using the wrong hash function. @@ -601,27 +598,31 @@ The user can also explicitly specify which format to use for a particular revision specifier and for output, overriding the mode. For example: -git --output-format=sha1 log abac87a^{sha1}..f787cac^{newhash} +git --output-format=sha1 log abac87a^{sha1}..f787cac^{sha256} -Selection of a New Hash ------------------------ +Choice of Hash +-------------- In early 2005, around the time that Git was written, Xiaoyun Wang, Yiqun Lisa Yin, and Hongbo Yu announced an attack finding SHA-1 collisions in 2^69 operations. In August they published details. Luckily, no practical demonstrations of a collision in full SHA-1 were published until 10 years later, in 2017. -The hash function NewHash to replace SHA-1 should be stronger than -SHA-1 was: we would like it to be trustworthy and useful in practice -for at least 10 years. +Git v2.13.0 and later subsequently moved to a hardened SHA-1 +implementation by default that mitigates the SHAttered attack, but +SHA-1 is still believed to be weak. + +The hash to replace this hardened SHA-1 should be stronger than SHA-1 +was: we would like it to be trustworthy and useful in practice for at +least 10 years. Some other relevant properties: 1. A 256-bit hash (long enough to match common security practice; not excessively long to hurt performance and disk usage). -2. High quality implementations should be widely available (e.g. in - OpenSSL). +2. High quality implementations should be widely available (e.g., in + OpenSSL and Apple CommonCrypto). 3. The hash function's properties should match Git's needs (e.g. Git requires collision and 2nd preimage resistance and does not require @@ -630,14 +631,13 @@ Some other relevant properties: 4. As a tiebreaker, the hash should be fast to compute (fortunately many contenders are faster than SHA-1). -Some hashes under consideration are SHA-256, SHA-512/256, SHA-256x16, -K12, and BLAKE2bp-256. +We choose SHA-256. Transition plan --------------- Some initial steps can be implemented independently of one another: - adding a hash function API (vtable) -- teaching fsck to tolerate the gpgsig-newhash field +- teaching fsck to tolerate the gpgsig-sha256 field - excluding gpgsig-* from the fields copied by "git commit --amend" - annotating tests that depend on SHA-1 values with a SHA1 test prerequisite @@ -664,7 +664,7 @@ Next comes introduction of compatObjectFormat: - adding appropriate index entries when adding a new object to the object store - --output-format option -- ^{sha1} and ^{newhash} revision notation +- ^{sha1} and ^{sha256} revision notation - configuration to specify default input and output format (see "Object names on the command line" above) @@ -672,7 +672,7 @@ The next step is supporting fetches and pushes to SHA-1 repositories: - allow pushes to a repository using the compat format - generate a topologically sorted list of the SHA-1 names of fetched objects -- convert the fetched packfile to newhash format and generate an idx +- convert the fetched packfile to sha256 format and generate an idx file - re-sort to match the order of objects in the fetched packfile @@ -680,30 +680,30 @@ The infrastructure supporting fetch also allows converting an existing repository. In converted repositories and new clones, end users can gain support for the new hash function without any visible change in behavior (see "dark launch" in the "Object names on the command line" -section). In particular this allows users to verify NewHash signatures +section). In particular this allows users to verify SHA-256 signatures on objects in the repository, and it should ensure the transition code is stable in production in preparation for using it more widely. Over time projects would encourage their users to adopt the "early transition" and then "late transition" modes to take advantage of the -new, more futureproof NewHash object names. +new, more futureproof SHA-256 object names. When objectFormat and compatObjectFormat are both set, commands -generating signatures would generate both SHA-1 and NewHash signatures +generating signatures would generate both SHA-1 and SHA-256 signatures by default to support both new and old users. -In projects using NewHash heavily, users could be encouraged to adopt +In projects using SHA-256 heavily, users could be encouraged to adopt the "post-transition" mode to avoid accidentally making implicit use of SHA-1 object names. Once a critical mass of users have upgraded to a version of Git that -can verify NewHash signatures and have converted their existing +can verify SHA-256 signatures and have converted their existing repositories to support verifying them, we can add support for a -setting to generate only NewHash signatures. This is expected to be at +setting to generate only SHA-256 signatures. This is expected to be at least a year later. That is also a good moment to advertise the ability to convert -repositories to use NewHash only, stripping out all SHA-1 related +repositories to use SHA-256 only, stripping out all SHA-1 related metadata. This improves performance by eliminating translation overhead and security by avoiding the possibility of accidentally relying on the safety of SHA-1. @@ -742,16 +742,16 @@ using the old hash function. Signed objects with multiple hashes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Instead of introducing the gpgsig-newhash field in commit and tag objects -for newhash-content based signatures, an earlier version of this design -added "hash newhash <newhash-name>" fields to strengthen the existing +Instead of introducing the gpgsig-sha256 field in commit and tag objects +for sha256-content based signatures, an earlier version of this design +added "hash sha256 <sha256-name>" fields to strengthen the existing sha1-content based signatures. In other words, a single signature was used to attest to the object content using both hash functions. This had some advantages: * Using one signature instead of two speeds up the signing process. * Having one signed payload with both hashes allows the signer to - attest to the sha1-name and newhash-name referring to the same object. + attest to the sha1-name and sha256-name referring to the same object. * All users consume the same signature. Broken signatures are likely to be detected quickly using current versions of git. @@ -760,11 +760,11 @@ However, it also came with disadvantages: objects it references, even after the transition is complete and translation table is no longer needed for anything else. To support this, the design added fields such as "hash sha1 tree <sha1-name>" - and "hash sha1 parent <sha1-name>" to the newhash-content of a signed + and "hash sha1 parent <sha1-name>" to the sha256-content of a signed commit, complicating the conversion process. * Allowing signed objects without a sha1 (for after the transition is complete) complicated the design further, requiring a "nohash sha1" - field to suppress including "hash sha1" fields in the newhash-content + field to suppress including "hash sha1" fields in the sha256-content and signed payload. Lazily populated translation table @@ -772,7 +772,7 @@ Lazily populated translation table Some of the work of building the translation table could be deferred to push time, but that would significantly complicate and slow down pushes. Calculating the sha1-name at object creation time at the same time it is -being streamed to disk and having its newhash-name calculated should be +being streamed to disk and having its sha256-name calculated should be an acceptable cost. Document History @@ -814,6 +814,12 @@ Incorporated suggestions from jonathantanmy and sbeller: * avoid loose object overhead by packing more aggressively in "git gc --auto" +Later history: + + See the history of this file in git.git for the history of subsequent + edits. This document history is no longer being maintained as it + would now be superfluous to the commit log + [1] http://public-inbox.org/git/CA+55aFzJtejiCjV0e43+9oR3QuJK2PiFiLQemytoLpyJWe6P9w@mail.gmail.com/ [2] http://public-inbox.org/git/CA+55aFz+gkAsDZ24zmePQuEs1XPS9BP_s8O7Q4wQ7LV7X5-oDA@mail.gmail.com/ [3] http://public-inbox.org/git/20170306084353.nrns455dvkdsfgo5@sigill.intra.peff.net/ diff --git a/Documentation/technical/http-protocol.txt b/Documentation/technical/http-protocol.txt index 64f49d0bbb..9c5b6f0fac 100644 --- a/Documentation/technical/http-protocol.txt +++ b/Documentation/technical/http-protocol.txt @@ -338,11 +338,11 @@ server advertises capability `allow-tip-sha1-in-want` or request_end request_end = "0000" / "done" - want_list = PKT-LINE(want NUL cap_list LF) + want_list = PKT-LINE(want SP cap_list LF) *(want_pkt) want_pkt = PKT-LINE(want LF) want = "want" SP id - cap_list = *(SP capability) SP + cap_list = capability *(SP capability) have_list = *PKT-LINE("have" SP id LF) diff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt index 508a344cf1..6ac774d5f6 100644 --- a/Documentation/technical/pack-protocol.txt +++ b/Documentation/technical/pack-protocol.txt @@ -50,7 +50,8 @@ Each Extra Parameter takes the form of `<key>=<value>` or `<key>`. Servers that receive any such Extra Parameters MUST ignore all unrecognized keys. Currently, the only Extra Parameter recognized is -"version=1". +"version" with a value of '1' or '2'. See protocol-v2.txt for more +information on protocol version 2. Git Transport ------------- @@ -484,6 +484,11 @@ all:: # The DEVELOPER mode enables -Wextra with a few exceptions. By # setting this flag the exceptions are removed, and all of # -Wextra is used. +# +# pedantic: +# +# Enable -pedantic compilation. This also disables +# USE_PARENS_AROUND_GETTEXT_N to produce only relevant warnings. GIT-VERSION-FILE: FORCE @$(SHELL_PATH) ./GIT-VERSION-GEN @@ -564,7 +569,7 @@ SPATCH = spatch export TCL_PATH TCLTK_PATH SPARSE_FLAGS = -SPATCH_FLAGS = --all-includes +SPATCH_FLAGS = --all-includes --patch . @@ -709,6 +714,7 @@ TEST_BUILTINS_OBJS += test-example-decorate.o TEST_BUILTINS_OBJS += test-genrandom.o TEST_BUILTINS_OBJS += test-hashmap.o TEST_BUILTINS_OBJS += test-index-version.o +TEST_BUILTINS_OBJS += test-json-writer.o TEST_BUILTINS_OBJS += test-lazy-init-name-hash.o TEST_BUILTINS_OBJS += test-match-trees.o TEST_BUILTINS_OBJS += test-mergesort.o @@ -873,6 +879,7 @@ LIB_OBJS += hashmap.o LIB_OBJS += help.o LIB_OBJS += hex.o LIB_OBJS += ident.o +LIB_OBJS += json-writer.o LIB_OBJS += kwset.o LIB_OBJS += levenshtein.o LIB_OBJS += line-log.o @@ -2037,7 +2044,7 @@ $(BUILT_INS): git$X command-list.h: generate-cmdlist.sh command-list.txt -command-list.h: $(wildcard Documentation/git*.txt) +command-list.h: $(wildcard Documentation/git*.txt) Documentation/config.txt $(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh command-list.txt >$@+ && mv $@+ $@ SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\ @@ -2673,10 +2680,16 @@ check: command-list.h fi C_SOURCES = $(patsubst %.o,%.c,$(C_OBJ)) -%.cocci.patch: %.cocci $(C_SOURCES) +ifdef DC_SHA1_SUBMODULE +COCCI_SOURCES = $(filter-out sha1collisiondetection/%,$(C_SOURCES)) +else +COCCI_SOURCES = $(filter-out sha1dc/%,$(C_SOURCES)) +endif + +%.cocci.patch: %.cocci $(COCCI_SOURCES) @echo ' ' SPATCH $<; \ ret=0; \ - for f in $(C_SOURCES); do \ + for f in $(COCCI_SOURCES); do \ $(SPATCH) --sp-file $< $$f $(SPATCH_FLAGS) || \ { ret=$$?; break; }; \ done >$@+ 2>$@.log; \ @@ -2690,7 +2703,9 @@ C_SOURCES = $(patsubst %.o,%.c,$(C_OBJ)) then \ echo ' ' SPATCH result: $@; \ fi -coccicheck: $(patsubst %.cocci,%.cocci.patch,$(wildcard contrib/coccinelle/*.cocci)) +coccicheck: $(addsuffix .patch,$(wildcard contrib/coccinelle/*.cocci)) + +.PHONY: coccicheck ### Installation rules @@ -2902,7 +2917,10 @@ profile-clean: $(RM) $(addsuffix *.gcda,$(addprefix $(PROFILE_DIR)/, $(object_dirs))) $(RM) $(addsuffix *.gcno,$(addprefix $(PROFILE_DIR)/, $(object_dirs))) -clean: profile-clean coverage-clean +cocciclean: + $(RM) contrib/coccinelle/*.cocci.patch* + +clean: profile-clean coverage-clean cocciclean $(RM) *.res $(RM) $(OBJECTS) $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB) @@ -2914,7 +2932,6 @@ clean: profile-clean coverage-clean $(RM) -r $(GIT_TARNAME) .doc-tmp-dir $(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz $(RM) $(htmldocs).tar.gz $(manpages).tar.gz - $(RM) contrib/coccinelle/*.cocci.patch* $(MAKE) -C Documentation/ clean ifndef NO_PERL $(MAKE) -C gitweb clean @@ -2930,7 +2947,7 @@ endif $(RM) GIT-USER-AGENT GIT-PREFIX $(RM) GIT-SCRIPT-DEFINES GIT-PERL-DEFINES GIT-PERL-HEADER GIT-PYTHON-VARS -.PHONY: all install profile-clean clean strip +.PHONY: all install profile-clean cocciclean clean strip .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell .PHONY: FORCE cscope @@ -111,14 +111,14 @@ struct apply_state { int applied_after_fixing_ws; }; -extern int apply_parse_options(int argc, const char **argv, - struct apply_state *state, - int *force_apply, int *options, - const char * const *apply_usage); -extern int init_apply_state(struct apply_state *state, - const char *prefix); -extern void clear_apply_state(struct apply_state *state); -extern int check_apply_state(struct apply_state *state, int force_apply); +int apply_parse_options(int argc, const char **argv, + struct apply_state *state, + int *force_apply, int *options, + const char * const *apply_usage); +int init_apply_state(struct apply_state *state, + const char *prefix); +void clear_apply_state(struct apply_state *state); +int check_apply_state(struct apply_state *state, int force_apply); /* * Some aspects of the apply behavior are controlled by the following @@ -127,9 +127,8 @@ extern int check_apply_state(struct apply_state *state, int force_apply); #define APPLY_OPT_INACCURATE_EOF (1<<0) /* accept inaccurate eof */ #define APPLY_OPT_RECOUNT (1<<1) /* accept inaccurate line count */ -extern int apply_all_patches(struct apply_state *state, - int argc, - const char **argv, - int options); +int apply_all_patches(struct apply_state *state, + int argc, const char **argv, + int options); #endif diff --git a/archive-tar.c b/archive-tar.c index 7df8565246..0bc50f6e89 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -122,7 +122,7 @@ static int stream_blocked(const struct object_id *oid) st = open_istream(oid, &type, &sz, NULL); if (!st) - return error("cannot stream blob %s", oid_to_hex(oid)); + return error(_("cannot stream blob %s"), oid_to_hex(oid)); for (;;) { readlen = read_istream(st, buf, sizeof(buf)); if (readlen <= 0) @@ -257,7 +257,7 @@ static int write_tar_entry(struct archiver_args *args, *header.typeflag = TYPEFLAG_REG; mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask; } else { - return error("unsupported file mode: 0%o (SHA1: %s)", + return error(_("unsupported file mode: 0%o (SHA1: %s)"), mode, oid_to_hex(oid)); } if (pathlen > sizeof(header.name)) { @@ -284,7 +284,7 @@ static int write_tar_entry(struct archiver_args *args, enum object_type type; buffer = object_file_to_archive(args, path, oid, old_mode, &type, &size); if (!buffer) - return error("cannot read %s", oid_to_hex(oid)); + return error(_("cannot read %s"), oid_to_hex(oid)); } else { buffer = NULL; size = 0; @@ -455,17 +455,17 @@ static int write_tar_filter_archive(const struct archiver *ar, filter.in = -1; if (start_command(&filter) < 0) - die_errno("unable to start '%s' filter", argv[0]); + die_errno(_("unable to start '%s' filter"), argv[0]); close(1); if (dup2(filter.in, 1) < 0) - die_errno("unable to redirect descriptor"); + die_errno(_("unable to redirect descriptor")); close(filter.in); r = write_tar_archive(ar, args); close(1); if (finish_command(&filter) != 0) - die("'%s' filter reported error", argv[0]); + die(_("'%s' filter reported error"), argv[0]); strbuf_release(&cmd); return r; diff --git a/archive-zip.c b/archive-zip.c index abc556e5a7..024267ff0f 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -310,11 +310,11 @@ static int write_zip_entry(struct archiver_args *args, if (is_utf8(path)) flags |= ZIP_UTF8; else - warning("Path is not valid UTF-8: %s", path); + warning(_("path is not valid UTF-8: %s"), path); } if (pathlen > 0xffff) { - return error("path too long (%d chars, SHA1: %s): %s", + return error(_("path too long (%d chars, SHA1: %s): %s"), (int)pathlen, oid_to_hex(oid), path); } @@ -341,7 +341,7 @@ static int write_zip_entry(struct archiver_args *args, size > big_file_threshold) { stream = open_istream(oid, &type, &size, NULL); if (!stream) - return error("cannot stream blob %s", + return error(_("cannot stream blob %s"), oid_to_hex(oid)); flags |= ZIP_STREAM; out = buffer = NULL; @@ -349,7 +349,7 @@ static int write_zip_entry(struct archiver_args *args, buffer = object_file_to_archive(args, path, oid, mode, &type, &size); if (!buffer) - return error("cannot read %s", + return error(_("cannot read %s"), oid_to_hex(oid)); crc = crc32(crc, buffer, size); is_binary = entry_is_binary(path_without_prefix, @@ -358,7 +358,7 @@ static int write_zip_entry(struct archiver_args *args, } compressed_size = (method == 0) ? size : 0; } else { - return error("unsupported file mode: 0%o (SHA1: %s)", mode, + return error(_("unsupported file mode: 0%o (SHA1: %s)"), mode, oid_to_hex(oid)); } @@ -467,7 +467,7 @@ static int write_zip_entry(struct archiver_args *args, zstream.avail_in = readlen; result = git_deflate(&zstream, 0); if (result != Z_OK) - die("deflate error (%d)", result); + die(_("deflate error (%d)"), result); out_len = zstream.next_out - compressed; if (out_len > 0) { @@ -602,7 +602,7 @@ static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time) struct tm *t; if (date_overflows(*timestamp)) - die("timestamp too large for this system: %"PRItime, + die(_("timestamp too large for this system: %"PRItime), *timestamp); time = (time_t)*timestamp; t = localtime(&time); @@ -42,31 +42,31 @@ struct attr_check { struct attr_stack *stack; }; -extern struct attr_check *attr_check_alloc(void); -extern struct attr_check *attr_check_initl(const char *, ...); -extern struct attr_check *attr_check_dup(const struct attr_check *check); +struct attr_check *attr_check_alloc(void); +struct attr_check *attr_check_initl(const char *, ...); +struct attr_check *attr_check_dup(const struct attr_check *check); -extern struct attr_check_item *attr_check_append(struct attr_check *check, - const struct git_attr *attr); +struct attr_check_item *attr_check_append(struct attr_check *check, + const struct git_attr *attr); -extern void attr_check_reset(struct attr_check *check); -extern void attr_check_clear(struct attr_check *check); -extern void attr_check_free(struct attr_check *check); +void attr_check_reset(struct attr_check *check); +void attr_check_clear(struct attr_check *check); +void attr_check_free(struct attr_check *check); /* * Return the name of the attribute represented by the argument. The * return value is a pointer to a null-delimited string that is part * of the internal data structure; it should not be modified or freed. */ -extern const char *git_attr_name(const struct git_attr *); +const char *git_attr_name(const struct git_attr *); -extern int git_check_attr(const char *path, struct attr_check *check); +int git_check_attr(const char *path, struct attr_check *check); /* * Retrieve all attributes that apply to the specified path. * check holds the attributes and their values. */ -extern void git_all_attrs(const char *path, struct attr_check *check); +void git_all_attrs(const char *path, struct attr_check *check); enum git_attr_direction { GIT_ATTR_CHECKIN, @@ -76,6 +76,6 @@ enum git_attr_direction { void git_attr_set_direction(enum git_attr_direction new_direction, struct index_state *istate); -extern void attr_start(void); +void attr_start(void); #endif /* ATTR_H */ diff --git a/banned.h b/banned.h new file mode 100644 index 0000000000..28f5937035 --- /dev/null +++ b/banned.h @@ -0,0 +1,30 @@ +#ifndef BANNED_H +#define BANNED_H + +/* + * This header lists functions that have been banned from our code base, + * because they're too easy to misuse (and even if used correctly, + * complicate audits). Including this header turns them into compile-time + * errors. + */ + +#define BANNED(func) sorry_##func##_is_a_banned_function + +#undef strcpy +#define strcpy(x,y) BANNED(strcpy) +#undef strcat +#define strcat(x,y) BANNED(strcat) +#undef strncpy +#define strncpy(x,y,n) BANNED(strncpy) + +#undef sprintf +#undef vsprintf +#ifdef HAVE_VARIADIC_MACROS +#define sprintf(...) BANNED(sprintf) +#define vsprintf(...) BANNED(vsprintf) +#else +#define sprintf(buf,fmt,arg) BANNED(sprintf) +#define vsprintf(buf,fmt,arg) BANNED(sprintf) +#endif + +#endif /* BANNED_H */ @@ -159,18 +159,22 @@ static inline struct blame_origin *blame_origin_incref(struct blame_origin *o) o->refcnt++; return o; } -extern void blame_origin_decref(struct blame_origin *o); - -extern void blame_coalesce(struct blame_scoreboard *sb); -extern void blame_sort_final(struct blame_scoreboard *sb); -extern unsigned blame_entry_score(struct blame_scoreboard *sb, struct blame_entry *e); -extern void assign_blame(struct blame_scoreboard *sb, int opt); -extern const char *blame_nth_line(struct blame_scoreboard *sb, long lno); - -extern void init_scoreboard(struct blame_scoreboard *sb); -extern void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blame_origin **orig); - -extern struct blame_entry *blame_entry_prepend(struct blame_entry *head, long start, long end, struct blame_origin *o); +void blame_origin_decref(struct blame_origin *o); + +void blame_coalesce(struct blame_scoreboard *sb); +void blame_sort_final(struct blame_scoreboard *sb); +unsigned blame_entry_score(struct blame_scoreboard *sb, struct blame_entry *e); +void assign_blame(struct blame_scoreboard *sb, int opt); +const char *blame_nth_line(struct blame_scoreboard *sb, long lno); + +void init_scoreboard(struct blame_scoreboard *sb); +void setup_scoreboard(struct blame_scoreboard *sb, + const char *path, + struct blame_origin **orig); + +struct blame_entry *blame_entry_prepend(struct blame_entry *head, + long start, long end, + struct blame_origin *o); extern struct blame_origin *get_blame_suspects(struct commit *commit); diff --git a/builtin/add.c b/builtin/add.c index 8a155dd41e..ba1ff5689d 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -304,7 +304,8 @@ static struct option builtin_add_options[] = { OPT_BOOL( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")), OPT_BOOL( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")), OPT_BOOL( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")), - OPT_STRING( 0 , "chmod", &chmod_arg, N_("(+/-)x"), N_("override the executable bit of the listed files")), + OPT_STRING(0, "chmod", &chmod_arg, "(+|-)x", + N_("override the executable bit of the listed files")), OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo, N_("warn when adding an embedded repository")), OPT_END(), diff --git a/builtin/blame.c b/builtin/blame.c index 5c93d169dd..97f6ecaf37 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -410,7 +410,7 @@ static void parse_color_fields(const char *s) } if (next == EXPECT_COLOR) - die (_("must end with a color")); + die(_("must end with a color")); colorfield[colorfield_nr].hop = TIME_MAX; string_list_clear(&l, 0); diff --git a/builtin/checkout.c b/builtin/checkout.c index 516136a23a..cb6bb76312 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1198,12 +1198,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) if (opts.track != BRANCH_TRACK_UNSPECIFIED && !opts.new_branch) { const char *argv0 = argv[0]; if (!argc || !strcmp(argv0, "--")) - die (_("--track needs a branch name")); + die(_("--track needs a branch name")); skip_prefix(argv0, "refs/", &argv0); skip_prefix(argv0, "remotes/", &argv0); argv0 = strchr(argv0, '/'); if (!argv0 || !argv0[1]) - die (_("Missing branch name; try -b")); + die(_("missing branch name; try -b")); opts.new_branch = argv0 + 1; } diff --git a/builtin/clone.c b/builtin/clone.c index 9ebb5acf56..fd2c3ef090 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -897,7 +897,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix) int err = 0, complete_refs_before_fetch = 1; int submodule_progress; - struct refspec_item refspec; + struct refspec rs = REFSPEC_INIT_FETCH; + struct argv_array ref_prefixes = ARGV_ARRAY_INIT; fetch_if_missing = 0; @@ -1079,7 +1080,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (option_required_reference.nr || option_optional_reference.nr) setup_reference(); - refspec_item_init_or_die(&refspec, value.buf, REFSPEC_FETCH); + refspec_append(&rs, value.buf); strbuf_reset(&value); @@ -1136,10 +1137,18 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (transport->smart_options && !deepen && !filter_options.choice) transport->smart_options->check_self_contained_and_connected = 1; - refs = transport_get_remote_refs(transport, NULL); + + argv_array_push(&ref_prefixes, "HEAD"); + refspec_ref_prefixes(&rs, &ref_prefixes); + if (option_branch) + expand_ref_prefix(&ref_prefixes, option_branch); + if (!option_no_tags) + argv_array_push(&ref_prefixes, "refs/tags/"); + + refs = transport_get_remote_refs(transport, &ref_prefixes); if (refs) { - mapped_refs = wanted_peer_refs(refs, &refspec); + mapped_refs = wanted_peer_refs(refs, &rs.items[0]); /* * transport_get_remote_refs() may return refs with null sha-1 * in mapped_refs (see struct transport->get_refs_list @@ -1157,7 +1166,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) } if (!is_local && !complete_refs_before_fetch) - transport_fetch_refs(transport, mapped_refs, NULL); + transport_fetch_refs(transport, mapped_refs); remote_head = find_ref_by_name(refs, "HEAD"); remote_head_points_at = @@ -1199,7 +1208,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) if (is_local) clone_local(path, git_dir); else if (refs && complete_refs_before_fetch) - transport_fetch_refs(transport, mapped_refs, NULL); + transport_fetch_refs(transport, mapped_refs); update_remote_refs(refs, mapped_refs, remote_head_points_at, branch_top.buf, reflog_msg.buf, transport, @@ -1233,6 +1242,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) strbuf_release(&value); junk_mode = JUNK_LEAVE_ALL; - refspec_item_clear(&refspec); + refspec_clear(&rs); + argv_array_clear(&ref_prefixes); return err; } diff --git a/builtin/commit.c b/builtin/commit.c index 158e3f843a..213fca2d8e 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -1647,9 +1647,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix) unlink(git_path_squash_msg(the_repository)); if (commit_index_files()) - die (_("Repository has been updated, but unable to write\n" - "new_index file. Check that disk is not full and quota is\n" - "not exceeded, and then \"git reset HEAD\" to recover.")); + die(_("repository has been updated, but unable to write\n" + "new_index file. Check that disk is not full and quota is\n" + "not exceeded, and then \"git reset HEAD\" to recover.")); rerere(0); run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); diff --git a/builtin/config.c b/builtin/config.c index 2c93a289a7..97b58c4aea 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -110,7 +110,7 @@ static int option_parse_type(const struct option *opt, const char *arg, * --int' and '--type=bool * --type=int'. */ - error("only one type at a time."); + error(_("only one type at a time")); usage_builtin_config(); } *to_type = new_type; @@ -164,7 +164,11 @@ static NORETURN void usage_builtin_config(void) static void check_argc(int argc, int min, int max) { if (argc >= min && argc <= max) return; - error("wrong number of arguments"); + if (min == max) + error(_("wrong number of arguments, should be %d"), min); + else + error(_("wrong number of arguments, should be from %d to %d"), + min, max); usage_builtin_config(); } @@ -297,7 +301,7 @@ static int get_value(const char *key_, const char *regex_) key_regexp = (regex_t*)xmalloc(sizeof(regex_t)); if (regcomp(key_regexp, key, REG_EXTENDED)) { - error("invalid key pattern: %s", key_); + error(_("invalid key pattern: %s"), key_); FREE_AND_NULL(key_regexp); ret = CONFIG_INVALID_PATTERN; goto free_strings; @@ -317,7 +321,7 @@ static int get_value(const char *key_, const char *regex_) regexp = (regex_t*)xmalloc(sizeof(regex_t)); if (regcomp(regexp, regex_, REG_EXTENDED)) { - error("invalid pattern: %s", regex_); + error(_("invalid pattern: %s"), regex_); FREE_AND_NULL(regexp); ret = CONFIG_INVALID_PATTERN; goto free_strings; @@ -390,7 +394,7 @@ static char *normalize_value(const char *key, const char *value) if (type == TYPE_COLOR) { char v[COLOR_MAXLEN]; if (git_config_color(v, key, value)) - die("cannot parse color '%s'", value); + die(_("cannot parse color '%s'"), value); /* * The contents of `v` now contain an ANSI escape @@ -485,13 +489,13 @@ static int get_colorbool(const char *var, int print) static void check_write(void) { if (!given_config_source.file && !startup_info->have_repository) - die("not in a git directory"); + die(_("not in a git directory")); if (given_config_source.use_stdin) - die("writing to stdin is not supported"); + die(_("writing to stdin is not supported")); if (given_config_source.blob) - die("writing config blobs is not supported"); + die(_("writing config blobs is not supported")); } struct urlmatch_current_candidate_value { @@ -599,7 +603,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) if (use_global_config + use_system_config + use_local_config + !!given_config_source.file + !!given_config_source.blob > 1) { - error("only one config file at a time."); + error(_("only one config file at a time")); usage_builtin_config(); } @@ -626,7 +630,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) * location; error out even if XDG_CONFIG_HOME * is set and points at a sane location. */ - die("$HOME not set"); + die(_("$HOME not set")); if (access_or_warn(user_config, R_OK, 0) && xdg_config && !access_or_warn(xdg_config, R_OK, 0)) { @@ -663,12 +667,12 @@ int cmd_config(int argc, const char **argv, const char *prefix) } if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) { - error("--get-color and variable type are incoherent"); + error(_("--get-color and variable type are incoherent")); usage_builtin_config(); } if (HAS_MULTI_BITS(actions)) { - error("only one action at a time."); + error(_("only one action at a time")); usage_builtin_config(); } if (actions == 0) @@ -681,19 +685,19 @@ int cmd_config(int argc, const char **argv, const char *prefix) } if (omit_values && !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) { - error("--name-only is only applicable to --list or --get-regexp"); + error(_("--name-only is only applicable to --list or --get-regexp")); usage_builtin_config(); } if (show_origin && !(actions & (ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) { - error("--show-origin is only applicable to --get, --get-all, " - "--get-regexp, and --list."); + error(_("--show-origin is only applicable to --get, --get-all, " + "--get-regexp, and --list")); usage_builtin_config(); } if (default_value && !(actions & ACTION_GET)) { - error("--default is only applicable to --get"); + error(_("--default is only applicable to --get")); usage_builtin_config(); } @@ -706,10 +710,10 @@ int cmd_config(int argc, const char **argv, const char *prefix) &given_config_source, &config_options) < 0) { if (given_config_source.file) - die_errno("unable to read config file '%s'", + die_errno(_("unable to read config file '%s'"), given_config_source.file); else - die("error processing config file(s)"); + die(_("error processing config file(s)")); } } else if (actions == ACTION_EDIT) { @@ -717,11 +721,11 @@ int cmd_config(int argc, const char **argv, const char *prefix) check_argc(argc, 0, 0); if (!given_config_source.file && nongit) - die("not in a git directory"); + die(_("not in a git directory")); if (given_config_source.use_stdin) - die("editing stdin is not supported"); + die(_("editing stdin is not supported")); if (given_config_source.blob) - die("editing blobs is not supported"); + die(_("editing blobs is not supported")); git_config(git_default_config, NULL); config_file = given_config_source.file ? xstrdup(given_config_source.file) : @@ -822,7 +826,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) if (ret < 0) return ret; if (ret == 0) - die("No such section!"); + die(_("no such section: %s"), argv[0]); } else if (actions == ACTION_REMOVE_SECTION) { int ret; @@ -833,7 +837,7 @@ int cmd_config(int argc, const char **argv, const char *prefix) if (ret < 0) return ret; if (ret == 0) - die("No such section!"); + die(_("no such section: %s"), argv[0]); } else if (actions == ACTION_GET_COLOR) { check_argc(argc, 1, 2); diff --git a/builtin/difftool.c b/builtin/difftool.c index 3018e61d04..cdd585ca76 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -703,7 +703,7 @@ int cmd_difftool(int argc, const char **argv, const char *prefix) 1, PARSE_OPT_NONEG | PARSE_OPT_HIDDEN), OPT_BOOL(0, "symlinks", &symlinks, N_("use symlinks in dir-diff mode")), - OPT_STRING('t', "tool", &difftool_cmd, N_("<tool>"), + OPT_STRING('t', "tool", &difftool_cmd, N_("tool"), N_("use the specified diff tool")), OPT_BOOL(0, "tool-help", &tool_help, N_("print a list of diff tools that may be used with " @@ -711,7 +711,7 @@ int cmd_difftool(int argc, const char **argv, const char *prefix) 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")), - OPT_STRING('x', "extcmd", &extcmd, N_("<command>"), + OPT_STRING('x', "extcmd", &extcmd, N_("command"), N_("specify a custom command for viewing diffs")), OPT_END() }; diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 223499d7ca..9bd8a14b57 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -241,7 +241,7 @@ static void export_blob(const struct object_id *oid) } else { buf = read_object_file(oid, &type, &size); if (!buf) - die ("Could not read blob %s", oid_to_hex(oid)); + die("could not read blob %s", oid_to_hex(oid)); if (check_object_signature(oid, buf, size, type_name(type)) < 0) die("sha1 mismatch in blob %s", oid_to_hex(oid)); object = parse_object_buffer(the_repository, oid, type, @@ -255,7 +255,7 @@ static void export_blob(const struct object_id *oid) printf("blob\nmark :%"PRIu32"\ndata %lu\n", last_idnum, size); if (size && fwrite(buf, size, 1, stdout) != 1) - die_errno ("Could not write blob '%s'", oid_to_hex(oid)); + die_errno("could not write blob '%s'", oid_to_hex(oid)); printf("\n"); show_progress(); @@ -563,14 +563,14 @@ static void handle_commit(struct commit *commit, struct rev_info *rev, commit_buffer = get_commit_buffer(commit, NULL); author = strstr(commit_buffer, "\nauthor "); if (!author) - die ("Could not find author in commit %s", - oid_to_hex(&commit->object.oid)); + die("could not find author in commit %s", + oid_to_hex(&commit->object.oid)); author++; author_end = strchrnul(author, '\n'); committer = strstr(author_end, "\ncommitter "); if (!committer) - die ("Could not find committer in commit %s", - oid_to_hex(&commit->object.oid)); + die("could not find committer in commit %s", + oid_to_hex(&commit->object.oid)); committer++; committer_end = strchrnul(committer, '\n'); message = strstr(committer_end, "\n\n"); @@ -691,7 +691,7 @@ static void handle_tag(const char *name, struct tag *tag) buf = read_object_file(&tag->object.oid, &type, &size); if (!buf) - die ("Could not read tag %s", oid_to_hex(&tag->object.oid)); + die("could not read tag %s", oid_to_hex(&tag->object.oid)); message = memmem(buf, size, "\n\n", 2); if (message) { message += 2; @@ -728,18 +728,18 @@ static void handle_tag(const char *name, struct tag *tag) if (signature) switch(signed_tag_mode) { case ABORT: - die ("Encountered signed tag %s; use " - "--signed-tags=<mode> to handle it.", - oid_to_hex(&tag->object.oid)); + die("encountered signed tag %s; use " + "--signed-tags=<mode> to handle it", + oid_to_hex(&tag->object.oid)); case WARN: - warning ("Exporting signed tag %s", - oid_to_hex(&tag->object.oid)); + warning("exporting signed tag %s", + oid_to_hex(&tag->object.oid)); /* fallthru */ case VERBATIM: break; case WARN_STRIP: - warning ("Stripping signature from tag %s", - oid_to_hex(&tag->object.oid)); + warning("stripping signature from tag %s", + oid_to_hex(&tag->object.oid)); /* fallthru */ case STRIP: message_size = signature + 1 - message; @@ -753,18 +753,18 @@ static void handle_tag(const char *name, struct tag *tag) if (!tagged_mark) { switch(tag_of_filtered_mode) { case ABORT: - die ("Tag %s tags unexported object; use " - "--tag-of-filtered-object=<mode> to handle it.", - oid_to_hex(&tag->object.oid)); + die("tag %s tags unexported object; use " + "--tag-of-filtered-object=<mode> to handle it", + oid_to_hex(&tag->object.oid)); case DROP: /* Ignore this tag altogether */ free(buf); return; case REWRITE: if (tagged->type != OBJ_COMMIT) { - die ("Tag %s tags unexported %s!", - oid_to_hex(&tag->object.oid), - type_name(tagged->type)); + die("tag %s tags unexported %s!", + oid_to_hex(&tag->object.oid), + type_name(tagged->type)); } p = (struct commit *)tagged; for (;;) { @@ -775,7 +775,7 @@ static void handle_tag(const char *name, struct tag *tag) if (!(p->object.flags & TREESAME)) break; if (!p->parents) - die ("Can't find replacement commit for tag %s\n", + die("can't find replacement commit for tag %s", oid_to_hex(&tag->object.oid)); p = p->parents->item; } diff --git a/builtin/fetch.c b/builtin/fetch.c index 34d2bd123b..61bec5d213 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -942,13 +942,11 @@ static int quickfetch(struct ref *ref_map) return check_connected(iterate_ref_map, &rm, &opt); } -static int fetch_refs(struct transport *transport, struct ref *ref_map, - struct ref **updated_remote_refs) +static int fetch_refs(struct transport *transport, struct ref *ref_map) { int ret = quickfetch(ref_map); if (ret) - ret = transport_fetch_refs(transport, ref_map, - updated_remote_refs); + ret = transport_fetch_refs(transport, ref_map); if (!ret) /* * Keep the new pack's ".keep" file around to allow the caller @@ -1153,7 +1151,7 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map) transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL); transport_set_option(transport, TRANS_OPT_DEPTH, "0"); transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL); - if (!fetch_refs(transport, ref_map, NULL)) + if (!fetch_refs(transport, ref_map)) consume_refs(transport, ref_map); if (gsecondary) { @@ -1169,7 +1167,6 @@ static int do_fetch(struct transport *transport, int autotags = (transport->remote->fetch_tags == 1); int retcode = 0; const struct ref *remote_refs; - struct ref *updated_remote_refs = NULL; struct argv_array ref_prefixes = ARGV_ARRAY_INIT; if (tags == TAGS_DEFAULT) { @@ -1192,7 +1189,7 @@ static int do_fetch(struct transport *transport, refspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes); if (ref_prefixes.argc && - (tags == TAGS_SET || (tags == TAGS_DEFAULT && !rs->nr))) { + (tags == TAGS_SET || (tags == TAGS_DEFAULT))) { argv_array_push(&ref_prefixes, "refs/tags/"); } @@ -1220,24 +1217,7 @@ static int do_fetch(struct transport *transport, transport->url); } } - - if (fetch_refs(transport, ref_map, &updated_remote_refs)) { - free_refs(ref_map); - retcode = 1; - goto cleanup; - } - if (updated_remote_refs) { - /* - * Regenerate ref_map using the updated remote refs. This is - * to account for additional information which may be provided - * by the transport (e.g. shallow info). - */ - free_refs(ref_map); - ref_map = get_ref_map(transport->remote, updated_remote_refs, rs, - tags, &autotags); - free_refs(updated_remote_refs); - } - if (consume_refs(transport, ref_map)) { + if (fetch_refs(transport, ref_map) || consume_refs(transport, ref_map)) { free_refs(ref_map); retcode = 1; goto cleanup; diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index ca9206fbbe..f35ff1612b 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -628,7 +628,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out, i++; p[len] = 0; if (handle_line(p, &merge_parents)) - die ("Error in line %d: %.*s", i, len, p); + die("error in line %d: %.*s", i, len, p); } if (opts->add_title && srcs.nr) diff --git a/builtin/fsck.c b/builtin/fsck.c index c96f3f4fcc..250f5af118 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -694,7 +694,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) fetch_if_missing = 0; errors_found = 0; - check_replace_refs = 0; + read_replace_refs = 0; argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0); diff --git a/builtin/grep.c b/builtin/grep.c index 056161f0f8..ee5a1bd355 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -489,7 +489,7 @@ static int grep_cache(struct grep_opt *opt, struct repository *repo, } if (repo_read_index(repo) < 0) - die("index file corrupt"); + die(_("index file corrupt")); for (nr = 0; nr < repo->index->cache_nr; nr++) { const struct cache_entry *ce = repo->index->cache[nr]; @@ -963,7 +963,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) } if (!opt.pattern_list) - die(_("no pattern given.")); + die(_("no pattern given")); /* --only-matching has no effect with --invert. */ if (opt.invert) @@ -1093,19 +1093,19 @@ int cmd_grep(int argc, const char **argv, const char *prefix) } if (recurse_submodules && (!use_index || untracked)) - die(_("option not supported with --recurse-submodules.")); + die(_("option not supported with --recurse-submodules")); if (!show_in_pager && !opt.status_only) setup_pager(); if (!use_index && (untracked || cached)) - die(_("--cached or --untracked cannot be used with --no-index.")); + die(_("--cached or --untracked cannot be used with --no-index")); if (!use_index || untracked) { int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude; hit = grep_directory(&opt, &pathspec, use_exclude, use_index); } else if (0 <= opt_exclude) { - die(_("--[no-]exclude-standard cannot be used for tracked contents.")); + die(_("--[no-]exclude-standard cannot be used for tracked contents")); } else if (!list.nr) { if (!cached) setup_work_tree(); @@ -1113,7 +1113,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix) hit = grep_cache(&opt, the_repository, &pathspec, cached); } else { if (cached) - die(_("both --cached and trees are given.")); + die(_("both --cached and trees are given")); hit = grep_objects(&opt, &pathspec, &list); } diff --git a/builtin/index-pack.c b/builtin/index-pack.c index de311febe3..9582ead950 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -1680,7 +1680,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix) if (argc == 2 && !strcmp(argv[1], "-h")) usage(index_pack_usage); - check_replace_refs = 0; + read_replace_refs = 0; fsck_options.walk = mark_link; reset_pack_idx_option(&opts); diff --git a/builtin/init-db.c b/builtin/init-db.c index 4ecf909368..12ddda7e7b 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -73,7 +73,8 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template_path, continue; else if (S_ISLNK(st_template.st_mode)) { struct strbuf lnk = STRBUF_INIT; - if (strbuf_readlink(&lnk, template_path->buf, 0) < 0) + if (strbuf_readlink(&lnk, template_path->buf, + st_template.st_size) < 0) die_errno(_("cannot readlink '%s'"), template_path->buf); if (symlink(lnk.buf, path->buf)) die_errno(_("cannot symlink '%s' '%s'"), diff --git a/builtin/log.c b/builtin/log.c index 574595132a..e094560d9a 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -1608,14 +1608,14 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) numbered = 0; if (numbered && keep_subject) - die (_("-n and -k are mutually exclusive.")); + die(_("-n and -k are mutually exclusive")); if (keep_subject && subject_prefix) - die (_("--subject-prefix/--rfc and -k are mutually exclusive.")); + die(_("--subject-prefix/--rfc and -k are mutually exclusive")); rev.preserve_subject = keep_subject; argc = setup_revisions(argc, argv, &rev, &s_r_opt); if (argc > 1) - die (_("unrecognized argument: %s"), argv[1]); + die(_("unrecognized argument: %s"), argv[1]); if (rev.diffopt.output_format & DIFF_FORMAT_NAME) die(_("--name-only does not make sense")); diff --git a/builtin/merge.c b/builtin/merge.c index 77e1694a78..8f4a5065c2 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -725,7 +725,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common, exit(128); if (write_locked_index(&the_index, &lock, COMMIT_LOCK | SKIP_IF_UNCHANGED)) - die (_("unable to write %s"), get_index_file()); + die(_("unable to write %s"), get_index_file()); return clean ? 0 : 1; } else { return try_merge_command(strategy, xopts_nr, xopts, diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 4391504a91..c0741baa8b 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -140,7 +140,7 @@ static void *get_delta(struct object_entry *entry) buf = read_object_file(&entry->idx.oid, &type, &size); if (!buf) - die("unable to read %s", oid_to_hex(&entry->idx.oid)); + die(_("unable to read %s"), oid_to_hex(&entry->idx.oid)); base_buf = read_object_file(&DELTA(entry)->idx.oid, &type, &base_size); if (!base_buf) @@ -148,8 +148,13 @@ static void *get_delta(struct object_entry *entry) oid_to_hex(&DELTA(entry)->idx.oid)); delta_buf = diff_delta(base_buf, base_size, buf, size, &delta_size, 0); + /* + * We succesfully computed this delta once but dropped it for + * memory reasons. Something is very wrong if this time we + * recompute and create a different delta. + */ if (!delta_buf || delta_size != DELTA_SIZE(entry)) - die("delta size changed"); + BUG("delta size changed"); free(buf); free(base_buf); return delta_buf; @@ -406,7 +411,7 @@ static off_t write_reuse_object(struct hashfile *f, struct object_entry *entry, datalen = revidx[1].offset - offset; if (!pack_to_stdout && p->index_version > 1 && check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) { - error("bad packed object CRC for %s", + error(_("bad packed object CRC for %s"), oid_to_hex(&entry->idx.oid)); unuse_pack(&w_curs); return write_no_reuse_object(f, entry, limit, usable_delta); @@ -417,7 +422,7 @@ static off_t write_reuse_object(struct hashfile *f, struct object_entry *entry, if (!pack_to_stdout && p->index_version == 1 && check_pack_inflate(p, &w_curs, offset, datalen, entry_size)) { - error("corrupt packed object for %s", + error(_("corrupt packed object for %s"), oid_to_hex(&entry->idx.oid)); unuse_pack(&w_curs); return write_no_reuse_object(f, entry, limit, usable_delta); @@ -548,7 +553,7 @@ static enum write_one_status write_one(struct hashfile *f, */ recursing = (e->idx.offset == 1); if (recursing) { - warning("recursive delta detected for object %s", + warning(_("recursive delta detected for object %s"), oid_to_hex(&e->idx.oid)); return WRITE_ONE_RECURSIVE; } else if (e->idx.offset || e->preferred_base) { @@ -582,7 +587,7 @@ static enum write_one_status write_one(struct hashfile *f, /* make sure off_t is sufficiently large not to wrap */ if (signed_add_overflows(*offset, size)) - die("pack too large for current definition of off_t"); + die(_("pack too large for current definition of off_t")); *offset += size; return WRITE_ONE_WRITTEN; } @@ -748,7 +753,8 @@ static struct object_entry **compute_write_order(void) } if (wo_end != to_pack.nr_objects) - die("ordered %u objects, expected %"PRIu32, wo_end, to_pack.nr_objects); + die(_("ordered %u objects, expected %"PRIu32), + wo_end, to_pack.nr_objects); return wo; } @@ -760,15 +766,15 @@ static off_t write_reused_pack(struct hashfile *f) int fd; if (!is_pack_valid(reuse_packfile)) - die("packfile is invalid: %s", reuse_packfile->pack_name); + die(_("packfile is invalid: %s"), reuse_packfile->pack_name); fd = git_open(reuse_packfile->pack_name); if (fd < 0) - die_errno("unable to open packfile for reuse: %s", + die_errno(_("unable to open packfile for reuse: %s"), reuse_packfile->pack_name); if (lseek(fd, sizeof(struct pack_header), SEEK_SET) == -1) - die_errno("unable to seek in reused packfile"); + die_errno(_("unable to seek in reused packfile")); if (reuse_packfile_offset < 0) reuse_packfile_offset = reuse_packfile->pack_size - the_hash_algo->rawsz; @@ -779,7 +785,7 @@ static off_t write_reused_pack(struct hashfile *f) int read_pack = xread(fd, buffer, sizeof(buffer)); if (read_pack <= 0) - die_errno("unable to read from reused packfile"); + die_errno(_("unable to read from reused packfile")); if (read_pack > to_write) read_pack = to_write; @@ -882,7 +888,7 @@ static void write_pack_file(void) * to preserve this property. */ if (stat(pack_tmp_name, &st) < 0) { - warning_errno("failed to stat %s", pack_tmp_name); + warning_errno(_("failed to stat %s"), pack_tmp_name); } else if (!last_mtime) { last_mtime = st.st_mtime; } else { @@ -890,7 +896,7 @@ static void write_pack_file(void) utb.actime = st.st_atime; utb.modtime = --last_mtime; if (utime(pack_tmp_name, &utb) < 0) - warning_errno("failed utime() on %s", pack_tmp_name); + warning_errno(_("failed utime() on %s"), pack_tmp_name); } strbuf_addf(&tmpname, "%s-", base_name); @@ -935,8 +941,8 @@ static void write_pack_file(void) free(write_order); stop_progress(&progress_state); if (written != nr_result) - die("wrote %"PRIu32" objects while expecting %"PRIu32, - written, nr_result); + die(_("wrote %"PRIu32" objects while expecting %"PRIu32), + written, nr_result); } static int no_try_delta(const char *path) @@ -1480,7 +1486,7 @@ static void check_object(struct object_entry *entry) while (c & 128) { ofs += 1; if (!ofs || MSB(ofs, 7)) { - error("delta base offset overflow in pack for %s", + error(_("delta base offset overflow in pack for %s"), oid_to_hex(&entry->idx.oid)); goto give_up; } @@ -1489,7 +1495,7 @@ static void check_object(struct object_entry *entry) } ofs = entry->in_pack_offset - ofs; if (ofs <= 0 || ofs >= entry->in_pack_offset) { - error("delta base offset out of bound for %s", + error(_("delta base offset out of bound for %s"), oid_to_hex(&entry->idx.oid)); goto give_up; } @@ -1852,18 +1858,30 @@ static int delta_cacheable(unsigned long src_size, unsigned long trg_size, #ifndef NO_PTHREADS +/* Protect access to object database */ static pthread_mutex_t read_mutex; #define read_lock() pthread_mutex_lock(&read_mutex) #define read_unlock() pthread_mutex_unlock(&read_mutex) +/* Protect delta_cache_size */ static pthread_mutex_t cache_mutex; #define cache_lock() pthread_mutex_lock(&cache_mutex) #define cache_unlock() pthread_mutex_unlock(&cache_mutex) +/* + * Protect object list partitioning (e.g. struct thread_param) and + * progress_state + */ static pthread_mutex_t progress_mutex; #define progress_lock() pthread_mutex_lock(&progress_mutex) #define progress_unlock() pthread_mutex_unlock(&progress_mutex) +/* + * Access to struct object_entry is unprotected since each thread owns + * a portion of the main object list. Just don't access object entries + * ahead in the list because they can be stolen and would need + * progress_mutex for protection. + */ #else #define read_lock() (void)0 @@ -1974,10 +1992,10 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, trg->data = read_object_file(&trg_entry->idx.oid, &type, &sz); read_unlock(); if (!trg->data) - die("object %s cannot be read", + die(_("object %s cannot be read"), oid_to_hex(&trg_entry->idx.oid)); if (sz != trg_size) - die("object %s inconsistent object length (%lu vs %lu)", + die(_("object %s inconsistent object length (%lu vs %lu)"), oid_to_hex(&trg_entry->idx.oid), sz, trg_size); *mem_usage += sz; @@ -1990,7 +2008,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, if (src_entry->preferred_base) { static int warned = 0; if (!warned++) - warning("object %s cannot be read", + warning(_("object %s cannot be read"), oid_to_hex(&src_entry->idx.oid)); /* * Those objects are not included in the @@ -2000,11 +2018,11 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, */ return 0; } - die("object %s cannot be read", + die(_("object %s cannot be read"), oid_to_hex(&src_entry->idx.oid)); } if (sz != src_size) - die("object %s inconsistent object length (%lu vs %lu)", + die(_("object %s inconsistent object length (%lu vs %lu)"), oid_to_hex(&src_entry->idx.oid), sz, src_size); *mem_usage += sz; @@ -2014,7 +2032,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, if (!src->index) { static int warned = 0; if (!warned++) - warning("suboptimal pack - out of memory"); + warning(_("suboptimal pack - out of memory")); return 0; } *mem_usage += sizeof_delta_index(src->index); @@ -2245,12 +2263,19 @@ static void try_to_free_from_threads(size_t size) static try_to_free_t old_try_to_free_routine; /* + * The main object list is split into smaller lists, each is handed to + * one worker. + * * The main thread waits on the condition that (at least) one of the workers * has stopped working (which is indicated in the .working member of * struct thread_params). + * * When a work thread has completed its work, it sets .working to 0 and * signals the main thread and waits on the condition that .data_ready * becomes 1. + * + * The main thread steals half of the work from the worker that has + * most work left to hand it to the idle worker. */ struct thread_params { @@ -2341,8 +2366,8 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size, return; } if (progress > pack_to_stdout) - fprintf(stderr, "Delta compression using up to %d threads.\n", - delta_search_threads); + fprintf_ln(stderr, _("Delta compression using up to %d threads"), + delta_search_threads); p = xcalloc(delta_search_threads, sizeof(*p)); /* Partition the work amongst work threads. */ @@ -2382,7 +2407,7 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size, ret = pthread_create(&p[i].thread, NULL, threaded_find_deltas, &p[i]); if (ret) - die("unable to create thread: %s", strerror(ret)); + die(_("unable to create thread: %s"), strerror(ret)); active_threads++; } @@ -2477,7 +2502,7 @@ static void add_tag_chain(const struct object_id *oid) tag = lookup_tag(the_repository, oid); while (1) { if (!tag || parse_tag(tag) || !tag->tagged) - die("unable to pack objects reachable from tag %s", + die(_("unable to pack objects reachable from tag %s"), oid_to_hex(oid)); add_object_entry(&tag->object.oid, OBJ_TAG, NULL, 0); @@ -2543,7 +2568,7 @@ static void prepare_pack(int window, int depth) if (!entry->preferred_base) { nr_deltas++; if (oe_type(entry) < 0) - die("unable to get type of object %s", + die(_("unable to get type of object %s"), oid_to_hex(&entry->idx.oid)); } else { if (oe_type(entry) < 0) { @@ -2567,7 +2592,7 @@ static void prepare_pack(int window, int depth) ll_find_deltas(delta_list, n, window+1, depth, &nr_done); stop_progress(&progress_state); if (nr_done != nr_deltas) - die("inconsistency with delta count"); + die(_("inconsistency with delta count")); } free(delta_list); } @@ -2607,11 +2632,11 @@ static int git_pack_config(const char *k, const char *v, void *cb) if (!strcmp(k, "pack.threads")) { delta_search_threads = git_config_int(k, v); if (delta_search_threads < 0) - die("invalid number of threads specified (%d)", + die(_("invalid number of threads specified (%d)"), delta_search_threads); #ifdef NO_PTHREADS if (delta_search_threads != 1) { - warning("no threads support, ignoring %s", k); + warning(_("no threads support, ignoring %s"), k); delta_search_threads = 0; } #endif @@ -2620,7 +2645,7 @@ static int git_pack_config(const char *k, const char *v, void *cb) if (!strcmp(k, "pack.indexversion")) { pack_idx_opts.version = git_config_int(k, v); if (pack_idx_opts.version > 2) - die("bad pack.indexversion=%"PRIu32, + die(_("bad pack.indexversion=%"PRIu32), pack_idx_opts.version); return 0; } @@ -2638,7 +2663,7 @@ static void read_object_list_from_stdin(void) if (feof(stdin)) break; if (!ferror(stdin)) - die("fgets returned NULL, not EOF, not error!"); + die("BUG: fgets returned NULL, not EOF, not error!"); if (errno != EINTR) die_errno("fgets"); clearerr(stdin); @@ -2646,13 +2671,13 @@ static void read_object_list_from_stdin(void) } if (line[0] == '-') { if (get_oid_hex(line+1, &oid)) - die("expected edge object ID, got garbage:\n %s", + die(_("expected edge object ID, got garbage:\n %s"), line); add_preferred_base(&oid); continue; } if (parse_oid_hex(line, &oid, &p)) - die("expected object ID, got garbage:\n %s", line); + die(_("expected object ID, got garbage:\n %s"), line); add_preferred_base_object(p + 1); add_object_entry(&oid, OBJ_NONE, p + 1, 0); @@ -2791,7 +2816,7 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs) if (!p->pack_local || p->pack_keep || p->pack_keep_in_core) continue; if (open_pack_index(p)) - die("cannot open pack index"); + die(_("cannot open pack index")); ALLOC_GROW(in_pack.array, in_pack.nr + p->num_objects, @@ -2822,7 +2847,7 @@ static int add_loose_object(const struct object_id *oid, const char *path, enum object_type type = oid_object_info(the_repository, oid, NULL); if (type < 0) { - warning("loose object at %s could not be examined", path); + warning(_("loose object at %s could not be examined"), path); return 0; } @@ -2899,7 +2924,7 @@ static void loosen_unused_packed_objects(struct rev_info *revs) continue; if (open_pack_index(p)) - die("cannot open pack index"); + die(_("cannot open pack index")); for (i = 0; i < p->num_objects; i++) { nth_packed_object_oid(&oid, p, i); @@ -2907,7 +2932,7 @@ static void loosen_unused_packed_objects(struct rev_info *revs) !has_sha1_pack_kept_or_nonlocal(&oid) && !loosened_object_can_be_discarded(&oid, p->mtime)) if (force_object_loose(&oid, p->mtime)) - die("unable to force loose object"); + die(_("unable to force loose object")); } } } @@ -2994,17 +3019,17 @@ static void get_object_list(int ac, const char **av) use_bitmap_index = 0; continue; } - die("not a rev '%s'", line); + die(_("not a rev '%s'"), line); } if (handle_revision_arg(line, &revs, flags, REVARG_CANNOT_BE_FILENAME)) - die("bad revision '%s'", line); + die(_("bad revision '%s'"), line); } if (use_bitmap_index && !get_object_list_from_bitmap(&revs)) return; if (prepare_revision_walk(&revs)) - die("revision walk setup failed"); + die(_("revision walk setup failed")); mark_edges_uninteresting(&revs, show_edge); if (!fn_show_object) @@ -3017,9 +3042,9 @@ static void get_object_list(int ac, const char **av) revs.ignore_missing_links = 1; if (add_unseen_recent_objects_to_traversal(&revs, unpack_unreachable_expiration)) - die("unable to add recent objects"); + die(_("unable to add recent objects")); if (prepare_revision_walk(&revs)) - die("revision walk setup failed"); + die(_("revision walk setup failed")); traverse_commit_list(&revs, record_recent_commit, record_recent_object, NULL); } @@ -3110,7 +3135,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "all-progress-implied", &all_progress_implied, N_("similar to --all-progress when progress meter is shown")), - { OPTION_CALLBACK, 0, "index-version", NULL, N_("version[,offset]"), + { OPTION_CALLBACK, 0, "index-version", NULL, N_("<version>[,<offset>]"), N_("write the pack index file in the specified idx format version"), 0, option_parse_index_version }, OPT_MAGNITUDE(0, "max-pack-size", &pack_size_limit, @@ -3188,7 +3213,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (DFS_NUM_STATES > (1 << OE_DFS_STATE_BITS)) BUG("too many dfs states, increase OE_DFS_STATE_BITS"); - check_replace_refs = 0; + read_replace_refs = 0; reset_pack_idx_option(&pack_idx_opts); git_config(git_pack_config, NULL); @@ -3254,35 +3279,35 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (pack_compression_level == -1) pack_compression_level = Z_DEFAULT_COMPRESSION; else if (pack_compression_level < 0 || pack_compression_level > Z_BEST_COMPRESSION) - die("bad pack compression level %d", pack_compression_level); + die(_("bad pack compression level %d"), pack_compression_level); if (!delta_search_threads) /* --threads=0 means autodetect */ delta_search_threads = online_cpus(); #ifdef NO_PTHREADS if (delta_search_threads != 1) - warning("no threads support, ignoring --threads"); + warning(_("no threads support, ignoring --threads")); #endif if (!pack_to_stdout && !pack_size_limit) pack_size_limit = pack_size_limit_cfg; if (pack_to_stdout && pack_size_limit) - die("--max-pack-size cannot be used to build a pack for transfer."); + die(_("--max-pack-size cannot be used to build a pack for transfer")); if (pack_size_limit && pack_size_limit < 1024*1024) { - warning("minimum pack size limit is 1 MiB"); + warning(_("minimum pack size limit is 1 MiB")); pack_size_limit = 1024*1024; } if (!pack_to_stdout && thin) - die("--thin cannot be used to build an indexable pack."); + die(_("--thin cannot be used to build an indexable pack")); if (keep_unreachable && unpack_unreachable) - die("--keep-unreachable and --unpack-unreachable are incompatible."); + die(_("--keep-unreachable and --unpack-unreachable are incompatible")); if (!rev_list_all || !rev_list_reflog || !rev_list_index) unpack_unreachable_expiration = 0; if (filter_options.choice) { if (!pack_to_stdout) - die("cannot use --filter without --stdout."); + die(_("cannot use --filter without --stdout")); use_bitmap_index = 0; } @@ -3356,8 +3381,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) prepare_pack(window, depth); write_pack_file(); if (progress) - fprintf(stderr, "Total %"PRIu32" (delta %"PRIu32")," - " reused %"PRIu32" (delta %"PRIu32")\n", - written, written_delta, reused, reused_delta); + fprintf_ln(stderr, + _("Total %"PRIu32" (delta %"PRIu32")," + " reused %"PRIu32" (delta %"PRIu32")"), + written, written_delta, reused, reused_delta); return 0; } diff --git a/builtin/prune.c b/builtin/prune.c index 72b0621b76..4916a4daa2 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -118,7 +118,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix) expire = TIME_MAX; save_commit_buffer = 0; - check_replace_refs = 0; + read_replace_refs = 0; ref_paranoia = 1; init_revisions(&revs, prefix); diff --git a/builtin/pull.c b/builtin/pull.c index 4e78935392..53bc5facfd 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -48,11 +48,11 @@ static enum rebase_type parse_config_rebase(const char *key, const char *value, return REBASE_FALSE; else if (v > 0) return REBASE_TRUE; - else if (!strcmp(value, "preserve")) + else if (!strcmp(value, "preserve") || !strcmp(value, "p")) return REBASE_PRESERVE; - else if (!strcmp(value, "merges")) + else if (!strcmp(value, "merges") || !strcmp(value, "m")) return REBASE_MERGES; - else if (!strcmp(value, "interactive")) + else if (!strcmp(value, "interactive") || !strcmp(value, "i")) return REBASE_INTERACTIVE; if (fatal) diff --git a/builtin/push.c b/builtin/push.c index 9cd8e8cd56..ef4c188895 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -558,9 +558,9 @@ int cmd_push(int argc, const char **argv, const char *prefix) OPT_BIT( 0, "porcelain", &flags, N_("machine-readable output"), TRANSPORT_PUSH_PORCELAIN), OPT_BIT('f', "force", &flags, N_("force updates"), TRANSPORT_PUSH_FORCE), { OPTION_CALLBACK, - 0, CAS_OPT_NAME, &cas, N_("refname>:<expect"), + 0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"), N_("require old value of ref to be at this value"), - PARSE_OPT_OPTARG, parseopt_push_cas_option }, + PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP, parseopt_push_cas_option }, { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, "check|on-demand|no", N_("control recursive pushing of submodules"), PARSE_OPT_OPTARG, option_parse_recurse_submodules }, diff --git a/builtin/read-tree.c b/builtin/read-tree.c index ebc43eb805..fbbc98e516 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -133,7 +133,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix) N_("same as -m, but discard unmerged entries")), { OPTION_STRING, 0, "prefix", &opts.prefix, N_("<subdirectory>/"), N_("read the tree into the index under <subdirectory>/"), - PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP }, + PARSE_OPT_NONEG }, OPT_BOOL('u', NULL, &opts.update, N_("update working tree with merge result")), { OPTION_CALLBACK, 0, "exclude-per-directory", &opts, diff --git a/builtin/remote.c b/builtin/remote.c index c74ee88690..07bd51f8eb 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -566,7 +566,7 @@ static int read_remote_branches(const char *refname, strbuf_addf(&buf, "refs/remotes/%s/", rename->old_name); if (starts_with(refname, buf.buf)) { - item = string_list_append(rename->remote_branches, xstrdup(refname)); + item = string_list_append(rename->remote_branches, refname); symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING, NULL, &flag); if (symref && (flag & REF_ISSYMREF)) @@ -612,7 +612,7 @@ static int mv(int argc, const char **argv) struct remote *oldremote, *newremote; struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT, old_remote_context = STRBUF_INIT; - struct string_list remote_branches = STRING_LIST_INIT_NODUP; + struct string_list remote_branches = STRING_LIST_INIT_DUP; struct rename_info rename; int i, refspec_updated = 0; @@ -734,6 +734,7 @@ static int mv(int argc, const char **argv) if (create_symref(buf.buf, buf2.buf, buf3.buf)) die(_("creating '%s' failed"), buf.buf); } + string_list_clear(&remote_branches, 1); return 0; } diff --git a/builtin/replace.c b/builtin/replace.c index ef22d724bb..4f05791f3e 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -54,7 +54,7 @@ static int show_reference(const char *refname, const struct object_id *oid, enum object_type obj_type, repl_type; if (get_oid(refname, &object)) - return error("Failed to resolve '%s' as a valid ref.", refname); + return error(_("failed to resolve '%s' as a valid ref"), refname); obj_type = oid_object_info(the_repository, &object, NULL); @@ -83,8 +83,8 @@ static int list_replace_refs(const char *pattern, const char *format) else if (!strcmp(format, "long")) data.format = REPLACE_FORMAT_LONG; else - return error("invalid replace format '%s'\n" - "valid formats are 'short', 'medium' and 'long'\n", + return error(_("invalid replace format '%s'\n" + "valid formats are 'short', 'medium' and 'long'"), format); for_each_replace_ref(the_repository, show_reference, (void *)&data); @@ -108,7 +108,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn) for (p = argv; *p; p++) { if (get_oid(*p, &oid)) { - error("Failed to resolve '%s' as a valid ref.", *p); + error("failed to resolve '%s' as a valid ref", *p); had_error = 1; continue; } @@ -118,7 +118,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn) full_hex = ref.buf + base_len; if (read_ref(ref.buf, &oid)) { - error("replace ref '%s' not found.", full_hex); + error(_("replace ref '%s' not found"), full_hex); had_error = 1; continue; } @@ -134,7 +134,7 @@ static int delete_replace_ref(const char *name, const char *ref, { if (delete_ref(NULL, ref, oid, 0)) return 1; - printf("Deleted replace ref '%s'\n", name); + printf_ln(_("Deleted replace ref '%s'"), name); return 0; } @@ -146,12 +146,12 @@ static int check_ref_valid(struct object_id *object, strbuf_reset(ref); strbuf_addf(ref, "%s%s", git_replace_ref_base, oid_to_hex(object)); if (check_refname_format(ref->buf, 0)) - return error("'%s' is not a valid ref name.", ref->buf); + return error(_("'%s' is not a valid ref name"), ref->buf); if (read_ref(ref->buf, prev)) oidclr(prev); else if (!force) - return error("replace ref '%s' already exists", ref->buf); + return error(_("replace ref '%s' already exists"), ref->buf); return 0; } @@ -171,10 +171,10 @@ static int replace_object_oid(const char *object_ref, obj_type = oid_object_info(the_repository, object, NULL); repl_type = oid_object_info(the_repository, repl, NULL); if (!force && obj_type != repl_type) - return error("Objects must be of the same type.\n" - "'%s' points to a replaced object of type '%s'\n" - "while '%s' points to a replacement object of " - "type '%s'.", + return error(_("Objects must be of the same type.\n" + "'%s' points to a replaced object of type '%s'\n" + "while '%s' points to a replacement object of " + "type '%s'."), object_ref, type_name(obj_type), replace_ref, type_name(repl_type)); @@ -200,10 +200,10 @@ static int replace_object(const char *object_ref, const char *replace_ref, int f struct object_id object, repl; if (get_oid(object_ref, &object)) - return error("Failed to resolve '%s' as a valid ref.", + return error(_("failed to resolve '%s' as a valid ref"), object_ref); if (get_oid(replace_ref, &repl)) - return error("Failed to resolve '%s' as a valid ref.", + return error(_("failed to resolve '%s' as a valid ref"), replace_ref); return replace_object_oid(object_ref, &object, replace_ref, &repl, force); @@ -222,7 +222,7 @@ static int export_object(const struct object_id *oid, enum object_type type, fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) - return error_errno("unable to open %s for writing", filename); + return error_errno(_("unable to open %s for writing"), filename); argv_array_push(&cmd.args, "--no-replace-objects"); argv_array_push(&cmd.args, "cat-file"); @@ -235,7 +235,7 @@ static int export_object(const struct object_id *oid, enum object_type type, cmd.out = fd; if (run_command(&cmd)) - return error("cat-file reported failure"); + return error(_("cat-file reported failure")); return 0; } @@ -251,7 +251,7 @@ static int import_object(struct object_id *oid, enum object_type type, fd = open(filename, O_RDONLY); if (fd < 0) - return error_errno("unable to open %s for reading", filename); + return error_errno(_("unable to open %s for reading"), filename); if (!raw && type == OBJ_TREE) { const char *argv[] = { "mktree", NULL }; @@ -265,11 +265,11 @@ static int import_object(struct object_id *oid, enum object_type type, if (start_command(&cmd)) { close(fd); - return error("unable to spawn mktree"); + return error(_("unable to spawn mktree")); } if (strbuf_read(&result, cmd.out, 41) < 0) { - error_errno("unable to read from mktree"); + error_errno(_("unable to read from mktree")); close(fd); close(cmd.out); return -1; @@ -278,11 +278,11 @@ static int import_object(struct object_id *oid, enum object_type type, if (finish_command(&cmd)) { strbuf_release(&result); - return error("mktree reported failure"); + return error(_("mktree reported failure")); } if (get_oid_hex(result.buf, oid) < 0) { strbuf_release(&result); - return error("mktree did not return an object name"); + return error(_("mktree did not return an object name")); } strbuf_release(&result); @@ -291,12 +291,12 @@ static int import_object(struct object_id *oid, enum object_type type, int flags = HASH_FORMAT_CHECK | HASH_WRITE_OBJECT; if (fstat(fd, &st) < 0) { - error_errno("unable to fstat %s", filename); + error_errno(_("unable to fstat %s"), filename); close(fd); return -1; } if (index_fd(oid, fd, &st, type, NULL, flags) < 0) - return error("unable to write object to database"); + return error(_("unable to write object to database")); /* index_fd close()s fd for us */ } @@ -315,11 +315,11 @@ static int edit_and_replace(const char *object_ref, int force, int raw) struct strbuf ref = STRBUF_INIT; if (get_oid(object_ref, &old_oid) < 0) - return error("Not a valid object name: '%s'", object_ref); + return error(_("not a valid object name: '%s'"), object_ref); type = oid_object_info(the_repository, &old_oid, NULL); if (type < 0) - return error("unable to get object type for %s", + return error(_("unable to get object type for %s"), oid_to_hex(&old_oid)); if (check_ref_valid(&old_oid, &prev, &ref, force)) { @@ -335,7 +335,7 @@ static int edit_and_replace(const char *object_ref, int force, int raw) } if (launch_editor(tmpfile, NULL, NULL) < 0) { free(tmpfile); - return error("editing object file failed"); + return error(_("editing object file failed")); } if (import_object(&new_oid, type, raw, tmpfile)) { free(tmpfile); @@ -344,7 +344,7 @@ static int edit_and_replace(const char *object_ref, int force, int raw) free(tmpfile); if (!oidcmp(&old_oid, &new_oid)) - return error("new object is the same as the old one: '%s'", oid_to_hex(&old_oid)); + return error(_("new object is the same as the old one: '%s'"), oid_to_hex(&old_oid)); return replace_object_oid(object_ref, &old_oid, "replacement", &new_oid, force); } @@ -368,7 +368,7 @@ static int replace_parents(struct strbuf *buf, int argc, const char **argv) struct object_id oid; if (get_oid(argv[i], &oid) < 0) { strbuf_release(&new_parents); - return error(_("Not a valid object name: '%s'"), + return error(_("not a valid object name: '%s'"), argv[i]); } if (!lookup_commit_reference(the_repository, &oid)) { @@ -412,7 +412,7 @@ static int check_one_mergetag(struct commit *commit, for (i = 1; i < mergetag_data->argc; i++) { struct object_id oid; if (get_oid(mergetag_data->argv[i], &oid) < 0) - return error(_("Not a valid object name: '%s'"), + return error(_("not a valid object name: '%s'"), mergetag_data->argv[i]); if (!oidcmp(&tag->tagged->oid, &oid)) return 0; /* found */ @@ -442,7 +442,7 @@ static int create_graft(int argc, const char **argv, int force, int gentle) unsigned long size; if (get_oid(old_ref, &old_oid) < 0) - return error(_("Not a valid object name: '%s'"), old_ref); + return error(_("not a valid object name: '%s'"), old_ref); commit = lookup_commit_reference(the_repository, &old_oid); if (!commit) return error(_("could not parse %s"), old_ref); @@ -457,7 +457,7 @@ static int create_graft(int argc, const char **argv, int force, int gentle) } if (remove_signature(&buf)) { - warning(_("the original commit '%s' has a gpg signature."), old_ref); + warning(_("the original commit '%s' has a gpg signature"), old_ref); warning(_("the signature will be removed in the replacement commit!")); } @@ -476,10 +476,10 @@ static int create_graft(int argc, const char **argv, int force, int gentle) if (!oidcmp(&old_oid, &new_oid)) { if (gentle) { - warning("graft for '%s' unnecessary", oid_to_hex(&old_oid)); + warning(_("graft for '%s' unnecessary"), oid_to_hex(&old_oid)); return 0; } - return error("new commit is the same as the old one: '%s'", oid_to_hex(&old_oid)); + return error(_("new commit is the same as the old one: '%s'"), oid_to_hex(&old_oid)); } return replace_object_oid(old_ref, &old_oid, "replacement", &new_oid, force); @@ -544,7 +544,7 @@ int cmd_replace(int argc, const char **argv, const char *prefix) OPT_END() }; - check_replace_refs = 0; + read_replace_refs = 0; git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0); @@ -553,7 +553,7 @@ int cmd_replace(int argc, const char **argv, const char *prefix) cmdmode = argc ? MODE_REPLACE : MODE_LIST; if (format && cmdmode != MODE_LIST) - usage_msg_opt("--format cannot be used when not listing", + usage_msg_opt(_("--format cannot be used when not listing"), git_replace_usage, options); if (force && @@ -561,47 +561,47 @@ int cmd_replace(int argc, const char **argv, const char *prefix) cmdmode != MODE_EDIT && cmdmode != MODE_GRAFT && cmdmode != MODE_CONVERT_GRAFT_FILE) - usage_msg_opt("-f only makes sense when writing a replacement", + usage_msg_opt(_("-f only makes sense when writing a replacement"), git_replace_usage, options); if (raw && cmdmode != MODE_EDIT) - usage_msg_opt("--raw only makes sense with --edit", + usage_msg_opt(_("--raw only makes sense with --edit"), git_replace_usage, options); switch (cmdmode) { case MODE_DELETE: if (argc < 1) - usage_msg_opt("-d needs at least one argument", + usage_msg_opt(_("-d needs at least one argument"), git_replace_usage, options); return for_each_replace_name(argv, delete_replace_ref); case MODE_REPLACE: if (argc != 2) - usage_msg_opt("bad number of arguments", + usage_msg_opt(_("bad number of arguments"), git_replace_usage, options); return replace_object(argv[0], argv[1], force); case MODE_EDIT: if (argc != 1) - usage_msg_opt("-e needs exactly one argument", + usage_msg_opt(_("-e needs exactly one argument"), git_replace_usage, options); return edit_and_replace(argv[0], force, raw); case MODE_GRAFT: if (argc < 1) - usage_msg_opt("-g needs at least one argument", + usage_msg_opt(_("-g needs at least one argument"), git_replace_usage, options); return create_graft(argc, argv, force, 0); case MODE_CONVERT_GRAFT_FILE: if (argc != 0) - usage_msg_opt("--convert-graft-file takes no argument", + usage_msg_opt(_("--convert-graft-file takes no argument"), git_replace_usage, options); return !!convert_graft_file(force); case MODE_LIST: if (argc > 1) - usage_msg_opt("only one pattern can be given with -l", + usage_msg_opt(_("only one pattern can be given with -l"), git_replace_usage, options); return list_replace_refs(argv[0], format); diff --git a/builtin/rm.c b/builtin/rm.c index 65b448ef8e..f4d3f000b6 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -285,7 +285,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix) list.entry[list.nr].is_submodule = S_ISGITLINK(ce->ce_mode); if (list.entry[list.nr++].is_submodule && !is_staging_gitmodules_ok(&the_index)) - die (_("Please stage your changes to .gitmodules or stash them to proceed")); + die(_("please stage your changes to .gitmodules or stash them to proceed")); } if (pathspec.nr) { diff --git a/builtin/send-pack.c b/builtin/send-pack.c index 42fd8d1a35..724b484850 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -178,7 +178,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "stdin", &from_stdin, N_("read refs from stdin")), OPT_BOOL(0, "helper-status", &helper_status, N_("print status from remote helper")), { OPTION_CALLBACK, - 0, CAS_OPT_NAME, &cas, N_("refname>:<expect"), + 0, CAS_OPT_NAME, &cas, N_("<refname>:<expect>"), N_("require old value of ref to be at this value"), PARSE_OPT_OPTARG, parseopt_push_cas_option }, OPT_END() diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 608d6ba77b..3898a2c9c4 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -268,8 +268,9 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) N_("Suppress commit descriptions, only provides commit count")), OPT_BOOL('e', "email", &log.email, N_("Show the email address of each author")), - { OPTION_CALLBACK, 'w', NULL, &log, N_("w[,i1[,i2]]"), - N_("Linewrap output"), PARSE_OPT_OPTARG, &parse_wrap_args }, + { OPTION_CALLBACK, 'w', NULL, &log, N_("<w>[,<i1>[,<i2>]]"), + N_("Linewrap output"), PARSE_OPT_OPTARG, + &parse_wrap_args }, OPT_END(), }; diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 4b9d3c0059..363cf8509a 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -674,7 +674,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) { OPTION_CALLBACK, 'g', "reflog", &reflog_base, N_("<n>[,<base>]"), N_("show <n> most recent ref-log entries starting at " "base"), - PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP, + PARSE_OPT_OPTARG, parse_reflog_param }, OPT_END() }; diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 716408e3a9..30d9413b4b 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -514,7 +514,7 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix) int i; struct object_id oid; - check_replace_refs = 0; + read_replace_refs = 0; git_config(git_default_config, NULL); diff --git a/builtin/update-index.c b/builtin/update-index.c index f5c0b6a1d2..5aee2eaa66 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -969,9 +969,9 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) PARSE_OPT_NOARG | /* disallow --cacheinfo=<mode> form */ PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, (parse_opt_cb *) cacheinfo_callback}, - {OPTION_CALLBACK, 0, "chmod", &set_executable_bit, N_("(+/-)x"), + {OPTION_CALLBACK, 0, "chmod", &set_executable_bit, "(+|-)x", N_("override the executable bit of the listed files"), - PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, + PARSE_OPT_NONEG, chmod_callback}, {OPTION_SET_INT, 0, "assume-unchanged", &mark_valid_only, NULL, N_("mark files as \"not changing\""), diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c index decde5a3b1..42dc4da5a1 100644 --- a/builtin/upload-pack.c +++ b/builtin/upload-pack.c @@ -31,7 +31,7 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix) }; packet_trace_identity("upload-pack"); - check_replace_refs = 0; + read_replace_refs = 0; argc = parse_options(argc, argv, NULL, options, upload_pack_usage, 0); diff --git a/builtin/write-tree.c b/builtin/write-tree.c index c9d3c544e7..cdcbf8264e 100644 --- a/builtin/write-tree.c +++ b/builtin/write-tree.c @@ -24,9 +24,8 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix) struct option write_tree_options[] = { OPT_BIT(0, "missing-ok", &flags, N_("allow missing objects"), WRITE_TREE_MISSING_OK), - { OPTION_STRING, 0, "prefix", &prefix, N_("<prefix>/"), - N_("write tree object for a subdirectory <prefix>") , - PARSE_OPT_LITERAL_ARGHELP }, + OPT_STRING(0, "prefix", &prefix, N_("<prefix>/"), + N_("write tree object for a subdirectory <prefix>")), { OPTION_BIT, 0, "ignore-cache-tree", &flags, NULL, N_("only useful for debugging"), PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, NULL, diff --git a/cache-tree.h b/cache-tree.h index cfd5328cc9..9799e894f7 100644 --- a/cache-tree.h +++ b/cache-tree.h @@ -51,6 +51,6 @@ int write_index_as_tree(struct object_id *oid, struct index_state *index_state, int write_cache_as_tree(struct object_id *oid, int flags, const char *prefix); void prime_cache_tree(struct index_state *, struct tree *); -extern int cache_tree_matches_traversal(struct cache_tree *, struct name_entry *ent, struct traverse_info *info); +int cache_tree_matches_traversal(struct cache_tree *, struct name_entry *ent, struct traverse_info *info); #endif @@ -868,11 +868,9 @@ void reset_shared_repository(void); * Do replace refs need to be checked this run? This variable is * initialized to true unless --no-replace-object is used or * $GIT_NO_REPLACE_OBJECTS is set, but is set to false by some - * commands that do not want replace references to be active. As an - * optimization it is also set to false if replace references have - * been sought but there were none. + * commands that do not want replace references to be active. */ -extern int check_replace_refs; +extern int read_replace_refs; extern char *git_replace_ref_base; extern int fsync_object_files; @@ -1425,18 +1423,20 @@ extern void *read_object_with_reference(const struct object_id *oid, extern struct object *peel_to_type(const char *name, int namelen, struct object *o, enum object_type); +enum date_mode_type { + DATE_NORMAL = 0, + DATE_RELATIVE, + DATE_SHORT, + DATE_ISO8601, + DATE_ISO8601_STRICT, + DATE_RFC2822, + DATE_STRFTIME, + DATE_RAW, + DATE_UNIX +}; + struct date_mode { - enum date_mode_type { - DATE_NORMAL = 0, - DATE_RELATIVE, - DATE_SHORT, - DATE_ISO8601, - DATE_ISO8601_STRICT, - DATE_RFC2822, - DATE_STRFTIME, - DATE_RAW, - DATE_UNIX - } type; + enum date_mode_type type; const char *strftime_fmt; int local; }; diff --git a/ci/lib-travisci.sh b/ci/lib-travisci.sh index ceecc889ca..06970f7213 100755 --- a/ci/lib-travisci.sh +++ b/ci/lib-travisci.sh @@ -97,7 +97,7 @@ fi export DEVELOPER=1 export DEFAULT_TEST_TARGET=prove export GIT_PROVE_OPTS="--timer --jobs 3 --state=failed,slow,save" -export GIT_TEST_OPTS="--verbose-log -x" +export GIT_TEST_OPTS="--verbose-log -x --immediate" export GIT_TEST_CLONE_2GB=YesPlease if [ "$jobname" = linux-gcc ]; then export CC=gcc-8 diff --git a/ci/print-test-failures.sh b/ci/print-test-failures.sh index 4f261ddc01..d55460a212 100755 --- a/ci/print-test-failures.sh +++ b/ci/print-test-failures.sh @@ -8,13 +8,24 @@ # Tracing executed commands would produce too much noise in the loop below. set +x -if ! ls t/test-results/*.exit >/dev/null 2>/dev/null +cd t/ + +if ! ls test-results/*.exit >/dev/null 2>/dev/null then echo "Build job failed before the tests could have been run" exit fi -for TEST_EXIT in t/test-results/*.exit +case "$jobname" in +osx-clang|osx-gcc) + # base64 in OSX doesn't wrap its output at 76 columns by + # default, but prints a single, very long line. + base64_opts="-b 76" + ;; +esac + +combined_trash_size=0 +for TEST_EXIT in test-results/*.exit do if [ "$(cat "$TEST_EXIT")" != "0" ] then @@ -23,5 +34,45 @@ do echo "$(tput setaf 1)${TEST_OUT}...$(tput sgr0)" echo "------------------------------------------------------------------------" cat "${TEST_OUT}" + + test_name="${TEST_EXIT%.exit}" + test_name="${test_name##*/}" + trash_dir="trash directory.$test_name" + trash_tgz_b64="trash.$test_name.base64" + if [ -d "$trash_dir" ] + then + tar czp "$trash_dir" |base64 $base64_opts >"$trash_tgz_b64" + + trash_size=$(wc -c <"$trash_tgz_b64") + if [ $trash_size -gt 1048576 ] + then + # larger than 1MB + echo "$(tput setaf 1)Didn't include the trash directory of '$test_name' in the trace log, it's too big$(tput sgr0)" + continue + fi + + new_combined_trash_size=$(($combined_trash_size + $trash_size)) + if [ $new_combined_trash_size -gt 1048576 ] + then + echo "$(tput setaf 1)Didn't include the trash directory of '$test_name' in the trace log, there is plenty of trash in there already.$(tput sgr0)" + continue + fi + combined_trash_size=$new_combined_trash_size + + # DO NOT modify these two 'echo'-ed strings below + # without updating 'ci/util/extract-trash-dirs.sh' + # as well. + echo "$(tput setaf 1)Start of trash directory of '$test_name':$(tput sgr0)" + cat "$trash_tgz_b64" + echo "$(tput setaf 1)End of trash directory of '$test_name'$(tput sgr0)" + fi fi done + +if [ $combined_trash_size -gt 0 ] +then + echo "------------------------------------------------------------------------" + echo "Trash directories embedded in this log can be extracted by running:" + echo + echo " curl https://api.travis-ci.org/v3/job/$TRAVIS_JOB_ID/log.txt |./ci/util/extract-trash-dirs.sh" +fi diff --git a/ci/run-static-analysis.sh b/ci/run-static-analysis.sh index fe4ee4e06b..5688f261d0 100755 --- a/ci/run-static-analysis.sh +++ b/ci/run-static-analysis.sh @@ -5,6 +5,25 @@ . ${0%/*}/lib-travisci.sh -make coccicheck +make --jobs=2 coccicheck + +set +x + +fail= +for cocci_patch in contrib/coccinelle/*.patch +do + if test -s "$cocci_patch" + then + echo "$(tput setaf 1)Coccinelle suggests the following changes in '$cocci_patch':$(tput sgr0)" + cat "$cocci_patch" + fail=UnfortunatelyYes + fi +done + +if test -n "$fail" +then + echo "$(tput setaf 1)error: Coccinelle suggested some changes$(tput sgr0)" + exit 1 +fi save_good_tree diff --git a/ci/util/extract-trash-dirs.sh b/ci/util/extract-trash-dirs.sh new file mode 100755 index 0000000000..8e67bec21a --- /dev/null +++ b/ci/util/extract-trash-dirs.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +error () { + echo >&2 "error: $@" + exit 1 +} + +find_embedded_trash () { + while read -r line + do + case "$line" in + *Start\ of\ trash\ directory\ of\ \'t[0-9][0-9][0-9][0-9]-*\':*) + test_name="${line#*\'}" + test_name="${test_name%\'*}" + + return 0 + esac + done + + return 1 +} + +extract_embedded_trash () { + while read -r line + do + case "$line" in + *End\ of\ trash\ directory\ of\ \'$test_name\'*) + return + ;; + *) + printf '%s\n' "$line" + ;; + esac + done + + error "unexpected end of input" +} + +# Raw logs from Linux build jobs have CRLF line endings, while OSX +# build jobs mostly have CRCRLF, except an odd line every now and +# then that has CRCRCRLF. 'base64 -d' from 'coreutils' doesn't like +# CRs and complains about "invalid input", so remove all CRs at the +# end of lines. +sed -e 's/\r*$//' | \ +while find_embedded_trash +do + echo "Extracting trash directory of '$test_name'" + + extract_embedded_trash |base64 -d |tar xzp +done @@ -343,6 +343,9 @@ int want_color_fd(int fd, int var) static int want_auto[3] = { -1, -1, -1 }; + if (fd < 1 || fd >= ARRAY_SIZE(want_auto)) + BUG("file descriptor out of range: %d", fd); + if (var < 0) var = git_use_color_default; diff --git a/commit-graph.c b/commit-graph.c index b0a55ad128..0034740c26 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -80,28 +80,28 @@ struct commit_graph *load_commit_graph_one(const char *graph_file) if (graph_size < GRAPH_MIN_SIZE) { close(fd); - die("graph file %s is too small", graph_file); + die(_("graph file %s is too small"), graph_file); } graph_map = xmmap(NULL, graph_size, PROT_READ, MAP_PRIVATE, fd, 0); data = (const unsigned char *)graph_map; graph_signature = get_be32(data); if (graph_signature != GRAPH_SIGNATURE) { - error("graph signature %X does not match signature %X", + error(_("graph signature %X does not match signature %X"), graph_signature, GRAPH_SIGNATURE); goto cleanup_fail; } graph_version = *(unsigned char*)(data + 4); if (graph_version != GRAPH_VERSION) { - error("graph version %X does not match version %X", + error(_("graph version %X does not match version %X"), graph_version, GRAPH_VERSION); goto cleanup_fail; } hash_version = *(unsigned char*)(data + 5); if (hash_version != GRAPH_OID_VERSION) { - error("hash version %X does not match version %X", + error(_("hash version %X does not match version %X"), hash_version, GRAPH_OID_VERSION); goto cleanup_fail; } @@ -125,7 +125,7 @@ struct commit_graph *load_commit_graph_one(const char *graph_file) chunk_lookup += GRAPH_CHUNKLOOKUP_WIDTH; if (chunk_offset > graph_size - GIT_MAX_RAWSZ) { - error("improper chunk offset %08x%08x", (uint32_t)(chunk_offset >> 32), + error(_("improper chunk offset %08x%08x"), (uint32_t)(chunk_offset >> 32), (uint32_t)chunk_offset); goto cleanup_fail; } @@ -161,7 +161,7 @@ struct commit_graph *load_commit_graph_one(const char *graph_file) } if (chunk_repeated) { - error("chunk id %08x appears multiple times", chunk_id); + error(_("chunk id %08x appears multiple times"), chunk_id); goto cleanup_fail; } @@ -258,7 +258,7 @@ static struct commit_list **insert_parent_or_die(struct commit_graph *g, hashcpy(oid.hash, g->chunk_oid_lookup + g->hash_len * pos); c = lookup_commit(the_repository, &oid); if (!c) - die("could not find commit %s", oid_to_hex(&oid)); + die(_("could not find commit %s"), oid_to_hex(&oid)); c->graph_pos = pos; return &commit_list_insert(c, pptr)->next; } @@ -562,7 +562,7 @@ static int add_packed_commits(const struct object_id *oid, oi.typep = &type; if (packed_object_info(the_repository, pack, offset, &oi) < 0) - die("unable to get type of object %s", oid_to_hex(oid)); + die(_("unable to get type of object %s"), oid_to_hex(oid)); if (type != OBJ_COMMIT) return 0; @@ -727,9 +727,9 @@ void write_commit_graph(const char *obj_dir, strbuf_addstr(&packname, pack_indexes->items[i].string); p = add_packed_git(packname.buf, packname.len, 1); if (!p) - die("error adding pack %s", packname.buf); + die(_("error adding pack %s"), packname.buf); if (open_pack_index(p)) - die("error opening index for %s", packname.buf); + die(_("error opening index for %s"), packname.buf); for_each_object_in_pack(p, add_packed_commits, &oids); close_pack(p); } @@ -117,12 +117,12 @@ static long config_buf_ftell(struct config_source *conf) } #define MAX_INCLUDE_DEPTH 10 -static const char include_depth_advice[] = +static const char include_depth_advice[] = N_( "exceeded maximum include depth (%d) while including\n" " %s\n" "from\n" " %s\n" -"Do you have circular includes?"; +"Do you have circular includes?"); static int handle_path_include(const char *path, struct config_include_data *inc) { int ret = 0; @@ -134,7 +134,7 @@ static int handle_path_include(const char *path, struct config_include_data *inc expanded = expand_user_path(path, 0); if (!expanded) - return error("could not expand include path '%s'", path); + return error(_("could not expand include path '%s'"), path); path = expanded; /* @@ -145,7 +145,7 @@ static int handle_path_include(const char *path, struct config_include_data *inc char *slash; if (!cf || !cf->path) - return error("relative config includes must come from files"); + return error(_("relative config includes must come from files")); slash = find_last_dir_sep(cf->path); if (slash) @@ -156,7 +156,7 @@ static int handle_path_include(const char *path, struct config_include_data *inc if (!access_or_die(path, R_OK, 0)) { if (++inc->depth > MAX_INCLUDE_DEPTH) - die(include_depth_advice, MAX_INCLUDE_DEPTH, path, + die(_(include_depth_advice), MAX_INCLUDE_DEPTH, path, !cf ? "<unknown>" : cf->name ? cf->name : "the command line"); @@ -343,13 +343,13 @@ static int git_config_parse_key_1(const char *key, char **store_key, int *basele 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; } @@ -373,13 +373,13 @@ static int git_config_parse_key_1(const char *key, char **store_key, int *basele 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) @@ -415,7 +415,7 @@ int git_config_parse_parameter(const char *text, pair = strbuf_split_str(text, '=', 2); if (!pair[0]) - return error("bogus config parameter: %s", text); + return error(_("bogus config parameter: %s"), text); if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=') { strbuf_setlen(pair[0], pair[0]->len - 1); @@ -427,7 +427,7 @@ int git_config_parse_parameter(const char *text, strbuf_trim(pair[0]); if (!pair[0]->len) { strbuf_list_free(pair); - return error("bogus config parameter: %s", text); + return error(_("bogus config parameter: %s"), text); } if (git_config_parse_key(pair[0]->buf, &canonical_name, NULL)) { @@ -462,7 +462,7 @@ int git_config_from_parameters(config_fn_t fn, void *data) envw = xstrdup(env); if (sq_dequote_to_argv(envw, &argv, &nr, &alloc) < 0) { - ret = error("bogus format in " CONFIG_DATA_ENVIRONMENT); + ret = error(_("bogus format in %s"), CONFIG_DATA_ENVIRONMENT); goto out; } @@ -933,7 +933,7 @@ int git_parse_ulong(const char *value, unsigned long *ret) return 1; } -static int git_parse_ssize_t(const char *value, ssize_t *ret) +int git_parse_ssize_t(const char *value, ssize_t *ret) { intmax_t tmp; if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(ssize_t))) @@ -1166,7 +1166,7 @@ static int git_default_core_config(const char *var, const char *value) else { int abbrev = git_config_int(var, value); if (abbrev < minimum_abbrev || abbrev > 40) - return error("abbrev length out of range: %d", abbrev); + return error(_("abbrev length out of range: %d"), abbrev); default_abbrev = abbrev; } return 0; @@ -1283,7 +1283,7 @@ static int git_default_core_config(const char *var, const char *value) comment_line_char = value[0]; auto_comment_line_char = 0; } else - return error("core.commentChar should only be one character"); + return error(_("core.commentChar should only be one character")); return 0; } @@ -1353,6 +1353,11 @@ static int git_default_core_config(const char *var, const char *value) var, value); } + if (!strcmp(var, "core.usereplacerefs")) { + read_replace_refs = git_config_bool(var, value); + return 0; + } + /* Add other config variables here and to Documentation/config.txt. */ return 0; } @@ -1391,7 +1396,7 @@ static int git_default_branch_config(const char *var, const char *value) else if (!strcmp(value, "always")) autorebase = AUTOREBASE_ALWAYS; else - return error("malformed value for %s", var); + return error(_("malformed value for %s"), var); return 0; } @@ -1417,9 +1422,9 @@ static int git_default_push_config(const char *var, const char *value) else if (!strcmp(value, "current")) push_default = PUSH_DEFAULT_CURRENT; else { - error("malformed value for %s: %s", var, value); - return error("Must be one of nothing, matching, simple, " - "upstream or current."); + error(_("malformed value for %s: %s"), var, value); + return error(_("must be one of nothing, matching, simple, " + "upstream or current")); } return 0; } @@ -1598,10 +1603,10 @@ int git_config_from_blob_oid(config_fn_t fn, buf = read_object_file(oid, &type, &size); if (!buf) - return error("unable to load config blob object '%s'", name); + return error(_("unable to load config blob object '%s'"), name); if (type != OBJ_BLOB) { free(buf); - return error("reference '%s' does not point to a blob", name); + return error(_("reference '%s' does not point to a blob"), name); } ret = git_config_from_mem(fn, CONFIG_ORIGIN_BLOB, name, buf, size, @@ -1618,7 +1623,7 @@ static int git_config_from_blob_ref(config_fn_t fn, struct object_id oid; if (get_oid(name, &oid) < 0) - return error("unable to resolve config blob '%s'", name); + return error(_("unable to resolve config blob '%s'"), name); return git_config_from_blob_oid(fn, name, &oid, data); } @@ -1648,7 +1653,7 @@ unsigned long git_env_ulong(const char *k, unsigned long val) { const char *v = getenv(k); if (v && !git_parse_ulong(v, &val)) - die("failed to parse %s", k); + die(_("failed to parse %s"), k); return val; } @@ -2365,7 +2370,7 @@ static int store_aux_event(enum config_event_t type, if (type == CONFIG_EVENT_SECTION) { if (cf->var.len < 2 || cf->var.buf[cf->var.len - 1] != '.') - return error("invalid section name '%s'", cf->var.buf); + return error(_("invalid section name '%s'"), cf->var.buf); /* Is this the section we were looking for? */ store->is_keys_section = @@ -2421,7 +2426,7 @@ static int store_aux(const char *key, const char *value, void *cb) static int write_error(const char *filename) { - error("failed to write new configuration file %s", filename); + error(_("failed to write new configuration file %s"), filename); /* Same error code as "failed to rename". */ return 4; @@ -2672,7 +2677,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, */ fd = hold_lock_file_for_update(&lock, config_filename, 0); if (fd < 0) { - error_errno("could not lock config file %s", config_filename); + error_errno(_("could not lock config file %s"), config_filename); ret = CONFIG_NO_LOCK; goto out_free; } @@ -2683,7 +2688,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, in_fd = open(config_filename, O_RDONLY); if ( in_fd < 0 ) { if ( ENOENT != errno ) { - error_errno("opening %s", config_filename); + error_errno(_("opening %s"), config_filename); ret = CONFIG_INVALID_FILE; /* same as "invalid config file" */ goto out_free; } @@ -2718,7 +2723,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, store.value_regex = (regex_t*)xmalloc(sizeof(regex_t)); if (regcomp(store.value_regex, value_regex, REG_EXTENDED)) { - error("invalid pattern: %s", value_regex); + error(_("invalid pattern: %s"), value_regex); FREE_AND_NULL(store.value_regex); ret = CONFIG_INVALID_PATTERN; goto out_free; @@ -2743,7 +2748,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, if (git_config_from_file_with_options(store_aux, config_filename, &store, &opts)) { - error("invalid config file %s", config_filename); + error(_("invalid config file %s"), config_filename); ret = CONFIG_INVALID_FILE; goto out_free; } @@ -2767,7 +2772,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, if (contents == MAP_FAILED) { if (errno == ENODEV && S_ISDIR(st.st_mode)) errno = EISDIR; - error_errno("unable to mmap '%s'", config_filename); + error_errno(_("unable to mmap '%s'"), config_filename); ret = CONFIG_INVALID_FILE; contents = NULL; goto out_free; @@ -2776,7 +2781,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, in_fd = -1; if (chmod(get_lock_file_path(&lock), st.st_mode & 07777) < 0) { - error_errno("chmod on %s failed", get_lock_file_path(&lock)); + error_errno(_("chmod on %s failed"), get_lock_file_path(&lock)); ret = CONFIG_NO_WRITE; goto out_free; } @@ -2861,7 +2866,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename, } if (commit_lock_file(&lock) < 0) { - error_errno("could not write config file %s", config_filename); + error_errno(_("could not write config file %s"), config_filename); ret = CONFIG_NO_WRITE; goto out_free; } @@ -2987,7 +2992,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename memset(&store, 0, sizeof(store)); if (new_name && !section_name_is_ok(new_name)) { - ret = error("invalid section name: %s", new_name); + ret = error(_("invalid section name: %s"), new_name); goto out_no_rollback; } @@ -2996,7 +3001,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename out_fd = hold_lock_file_for_update(&lock, config_filename, 0); if (out_fd < 0) { - ret = error("could not lock config file %s", config_filename); + ret = error(_("could not lock config file %s"), config_filename); goto out; } @@ -3014,7 +3019,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename } if (chmod(get_lock_file_path(&lock), st.st_mode & 07777) < 0) { - ret = error_errno("chmod on %s failed", + ret = error_errno(_("chmod on %s failed"), get_lock_file_path(&lock)); goto out; } @@ -3111,7 +3116,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename config_file = NULL; commit_and_out: if (commit_lock_file(&lock) < 0) - ret = error_errno("could not write config file %s", + ret = error_errno(_("could not write config file %s"), config_filename); out: if (config_file) @@ -3154,7 +3159,7 @@ int git_config_copy_section(const char *old_name, const char *new_name) #undef config_error_nonbool int config_error_nonbool(const char *var) { - return error("missing value for '%s'", var); + return error(_("missing value for '%s'"), var); } int parse_config_key(const char *var, @@ -82,6 +82,7 @@ extern void git_config(config_fn_t fn, void *); extern int config_with_options(config_fn_t fn, void *, struct git_config_source *config_source, const struct config_options *opts); +extern int git_parse_ssize_t(const char *, ssize_t *); extern int git_parse_ulong(const char *, unsigned long *); extern int git_parse_maybe_bool(const char *); extern int git_config_int(const char *, const char *); @@ -188,9 +189,14 @@ struct config_set { extern void git_configset_init(struct config_set *cs); extern int git_configset_add_file(struct config_set *cs, const char *filename); -extern int git_configset_get_value(struct config_set *cs, const char *key, const char **value); extern const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key); extern void git_configset_clear(struct config_set *cs); + +/* + * These functions return 1 if not found, and 0 if found, leaving the found + * value in the 'dest' pointer. + */ +extern int git_configset_get_value(struct config_set *cs, const char *key, const char **dest); extern int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest); extern int git_configset_get_string(struct config_set *cs, const char *key, char **dest); extern int git_configset_get_int(struct config_set *cs, const char *key, int *dest); diff --git a/config.mak.dev b/config.mak.dev index 2d244ca470..9a998149d9 100644 --- a/config.mak.dev +++ b/config.mak.dev @@ -1,6 +1,11 @@ ifeq ($(filter no-error,$(DEVOPTS)),) CFLAGS += -Werror endif +ifneq ($(filter pedantic,$(DEVOPTS)),) +CFLAGS += -pedantic +# don't warn for each N_ use +CFLAGS += -DUSE_PARENS_AROUND_GETTEXT_N=0 +endif CFLAGS += -Wdeclaration-after-statement CFLAGS += -Wno-format-zero-length CFLAGS += -Wold-style-definition diff --git a/config.mak.uname b/config.mak.uname index 684fc5bf02..2be2f19811 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -528,7 +528,7 @@ ifneq (,$(findstring MINGW,$(uname_S))) COMPAT_OBJS += compat/mingw.o compat/winansi.o \ compat/win32/pthread.o compat/win32/syslog.o \ compat/win32/dirent.o - BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1 + BASIC_CFLAGS += -DWIN32 -DPROTECT_NTFS_DEFAULT=1 EXTLIBS += -lws2_32 GITLIBS += git.res PTHREAD_LIBS = @@ -58,7 +58,7 @@ static NORETURN void die_initial_contact(int unexpected) * response does not necessarily mean an ACL problem, though. */ if (unexpected) - die(_("The remote end hung up upon initial contact")); + die(_("the remote end hung up upon initial contact")); else die(_("Could not read from remote repository.\n\n" "Please make sure you have the correct access rights\n" @@ -78,7 +78,7 @@ int server_supports_v2(const char *c, int die_on_error) } if (die_on_error) - die("server doesn't support '%s'", c); + die(_("server doesn't support '%s'"), c); return 0; } @@ -100,7 +100,7 @@ int server_supports_feature(const char *c, const char *feature, } if (die_on_error) - die("server doesn't support feature '%s'", feature); + die(_("server doesn't support feature '%s'"), feature); return 0; } @@ -111,7 +111,7 @@ static void process_capabilities_v2(struct packet_reader *reader) argv_array_push(&server_capabilities_v2, reader->line); if (reader->status != PACKET_READ_FLUSH) - die("expected flush after capabilities"); + die(_("expected flush after capabilities")); } enum protocol_version discover_version(struct packet_reader *reader) @@ -230,7 +230,7 @@ static int process_dummy_ref(const char *line) static void check_no_capabilities(const char *line, int len) { if (strlen(line) != len) - warning("Ignoring capabilities after first line '%s'", + warning(_("ignoring capabilities after first line '%s'"), line + strlen(line)); } @@ -249,7 +249,7 @@ static int process_ref(const char *line, int len, struct ref ***list, if (extra_have && !strcmp(name, ".have")) { oid_array_append(extra_have, &old_oid); } else if (!strcmp(name, "capabilities^{}")) { - die("protocol error: unexpected capabilities^{}"); + die(_("protocol error: unexpected capabilities^{}")); } else if (check_ref(name, flags)) { struct ref *ref = alloc_ref(name); oidcpy(&ref->old_oid, &old_oid); @@ -270,9 +270,9 @@ static int process_shallow(const char *line, int len, return 0; if (get_oid_hex(arg, &old_oid)) - die("protocol error: expected shallow sha-1, got '%s'", arg); + die(_("protocol error: expected shallow sha-1, got '%s'"), arg); if (!shallow_points) - die("repository on the other end cannot be shallow"); + die(_("repository on the other end cannot be shallow")); oid_array_append(shallow_points, &old_oid); check_no_capabilities(line, len); return 1; @@ -307,13 +307,13 @@ struct ref **get_remote_heads(struct packet_reader *reader, case PACKET_READ_NORMAL: len = reader->pktlen; if (len > 4 && skip_prefix(reader->line, "ERR ", &arg)) - die("remote error: %s", arg); + die(_("remote error: %s"), arg); break; case PACKET_READ_FLUSH: state = EXPECTING_DONE; break; case PACKET_READ_DELIM: - die("invalid packet"); + die(_("invalid packet")); } switch (state) { @@ -333,7 +333,7 @@ struct ref **get_remote_heads(struct packet_reader *reader, case EXPECTING_SHALLOW: if (process_shallow(reader->line, len, shallow_points)) break; - die("protocol error: unexpected '%s'", reader->line); + die(_("protocol error: unexpected '%s'"), reader->line); case EXPECTING_DONE: break; } @@ -441,11 +441,11 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader, /* Process response from server */ while (packet_reader_read(reader) == PACKET_READ_NORMAL) { if (!process_ref_v2(reader->line, &list)) - die("invalid ls-refs response: %s", reader->line); + die(_("invalid ls-refs response: %s"), reader->line); } if (reader->status != PACKET_READ_FLUSH) - die("expected flush after ref listing"); + die(_("expected flush after ref listing")); return list; } @@ -544,7 +544,7 @@ static enum protocol get_protocol(const char *name) return PROTO_SSH; if (!strcmp(name, "file")) return PROTO_FILE; - die("I don't handle protocol '%s'", name); + die(_("protocol '%s' is not supported"), name); } static char *host_end(char **hoststart, int removebrackets) @@ -595,8 +595,7 @@ static void enable_keepalive(int sockfd) int ka = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)) < 0) - fprintf(stderr, "unable to set SO_KEEPALIVE on socket: %s\n", - strerror(errno)); + error_errno(_("unable to set SO_KEEPALIVE on socket")); } #ifndef NO_IPV6 @@ -636,14 +635,15 @@ static int git_tcp_connect_sock(char *host, int flags) hints.ai_protocol = IPPROTO_TCP; if (flags & CONNECT_VERBOSE) - fprintf(stderr, "Looking up %s ... ", host); + fprintf(stderr, _("Looking up %s ... "), host); gai = getaddrinfo(host, port, &hints, &ai); if (gai) - die("Unable to look up %s (port %s) (%s)", host, port, gai_strerror(gai)); + die(_("unable to look up %s (port %s) (%s)"), host, port, gai_strerror(gai)); if (flags & CONNECT_VERBOSE) - fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port); + /* TRANSLATORS: this is the end of "Looking up %s ... " */ + fprintf(stderr, _("done.\nConnecting to %s (port %s) ... "), host, port); for (ai0 = ai; ai; ai = ai->ai_next, cnt++) { sockfd = socket(ai->ai_family, @@ -665,12 +665,13 @@ static int git_tcp_connect_sock(char *host, int flags) freeaddrinfo(ai0); if (sockfd < 0) - die("unable to connect to %s:\n%s", host, error_message.buf); + die(_("unable to connect to %s:\n%s"), host, error_message.buf); enable_keepalive(sockfd); if (flags & CONNECT_VERBOSE) - fprintf(stderr, "done.\n"); + /* TRANSLATORS: this is the end of "Connecting to %s (port %s) ... " */ + fprintf_ln(stderr, _("done.")); strbuf_release(&error_message); @@ -697,22 +698,23 @@ static int git_tcp_connect_sock(char *host, int flags) get_host_and_port(&host, &port); if (flags & CONNECT_VERBOSE) - fprintf(stderr, "Looking up %s ... ", host); + fprintf(stderr, _("Looking up %s ... "), host); he = gethostbyname(host); if (!he) - die("Unable to look up %s (%s)", host, hstrerror(h_errno)); + die(_("unable to look up %s (%s)"), host, hstrerror(h_errno)); nport = strtoul(port, &ep, 10); if ( ep == port || *ep ) { /* Not numeric */ struct servent *se = getservbyname(port,"tcp"); if ( !se ) - die("Unknown port %s", port); + die(_("unknown port %s"), port); nport = se->s_port; } if (flags & CONNECT_VERBOSE) - fprintf(stderr, "done.\nConnecting to %s (port %s) ... ", host, port); + /* TRANSLATORS: this is the end of "Looking up %s ... " */ + fprintf(stderr, _("done.\nConnecting to %s (port %s) ... "), host, port); for (cnt = 0, ap = he->h_addr_list; *ap; ap++, cnt++) { memset(&sa, 0, sizeof sa); @@ -740,12 +742,13 @@ static int git_tcp_connect_sock(char *host, int flags) } if (sockfd < 0) - die("unable to connect to %s:\n%s", host, error_message.buf); + die(_("unable to connect to %s:\n%s"), host, error_message.buf); enable_keepalive(sockfd); if (flags & CONNECT_VERBOSE) - fprintf(stderr, "done.\n"); + /* TRANSLATORS: this is the end of "Connecting to %s (port %s) ... " */ + fprintf_ln(stderr, _("done.")); return sockfd; } @@ -842,9 +845,9 @@ static struct child_process *git_proxy_connect(int fd[2], char *host) get_host_and_port(&host, &port); if (looks_like_command_line_option(host)) - die("strange hostname '%s' blocked", host); + die(_("strange hostname '%s' blocked"), host); if (looks_like_command_line_option(port)) - die("strange port '%s' blocked", port); + die(_("strange port '%s' blocked"), port); proxy = xmalloc(sizeof(*proxy)); child_process_init(proxy); @@ -854,7 +857,7 @@ static struct child_process *git_proxy_connect(int fd[2], char *host) proxy->in = -1; proxy->out = -1; if (start_command(proxy)) - die("cannot start proxy %s", git_proxy_command); + die(_("cannot start proxy %s"), git_proxy_command); fd[0] = proxy->out; /* read from proxy stdout */ fd[1] = proxy->in; /* write to proxy stdin */ return proxy; @@ -921,7 +924,7 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host, path = strchr(end, separator); if (!path || !*path) - die("No path specified. See 'man git-pull' for valid url syntax"); + die(_("no path specified; see 'git help pull' for valid url syntax")); /* * null-terminate hostname and point path to ~ for URL's like this: @@ -1116,7 +1119,7 @@ static void push_ssh_options(struct argv_array *args, struct argv_array *env, case VARIANT_AUTO: BUG("VARIANT_AUTO passed to push_ssh_options"); case VARIANT_SIMPLE: - die("ssh variant 'simple' does not support -4"); + die(_("ssh variant 'simple' does not support -4")); case VARIANT_SSH: case VARIANT_PLINK: case VARIANT_PUTTY: @@ -1128,7 +1131,7 @@ static void push_ssh_options(struct argv_array *args, struct argv_array *env, case VARIANT_AUTO: BUG("VARIANT_AUTO passed to push_ssh_options"); case VARIANT_SIMPLE: - die("ssh variant 'simple' does not support -6"); + die(_("ssh variant 'simple' does not support -6")); case VARIANT_SSH: case VARIANT_PLINK: case VARIANT_PUTTY: @@ -1145,7 +1148,7 @@ static void push_ssh_options(struct argv_array *args, struct argv_array *env, case VARIANT_AUTO: BUG("VARIANT_AUTO passed to push_ssh_options"); case VARIANT_SIMPLE: - die("ssh variant 'simple' does not support setting port"); + die(_("ssh variant 'simple' does not support setting port")); case VARIANT_SSH: argv_array_push(args, "-p"); break; @@ -1168,7 +1171,7 @@ static void fill_ssh_args(struct child_process *conn, const char *ssh_host, enum ssh_variant variant; if (looks_like_command_line_option(ssh_host)) - die("strange hostname '%s' blocked", ssh_host); + die(_("strange hostname '%s' blocked"), ssh_host); ssh = get_ssh_command(); if (ssh) { @@ -1256,7 +1259,7 @@ struct child_process *git_connect(int fd[2], const char *url, child_process_init(conn); if (looks_like_command_line_option(path)) - die("strange pathname '%s' blocked", path); + die(_("strange pathname '%s' blocked"), path); strbuf_addstr(&cmd, prog); strbuf_addch(&cmd, ' '); @@ -1301,7 +1304,7 @@ struct child_process *git_connect(int fd[2], const char *url, argv_array_push(&conn->args, cmd.buf); if (start_command(conn)) - die("unable to fork"); + die(_("unable to fork")); fd[0] = conn->out; /* read from child's stdout */ fd[1] = conn->in; /* write to child's stdin */ diff --git a/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh b/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh index 22f069db48..cfbfe7ddf6 100755 --- a/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh +++ b/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh @@ -247,7 +247,7 @@ test_expect_success 'Test of resistance to modification of category on wiki for wiki_editpage Notconsidered "this page will not appear on local" false && wiki_editpage Othercategory "this page will not appear on local" false -c=Cattwo && wiki_editpage Tobeedited "this page have been modified" true -c=Catone && - wiki_delete_page Tobedeleted + wiki_delete_page Tobedeleted && git clone -c remote.origin.categories="Catone" \ mediawiki::'"$WIKI_URL"' mw_dir_14 && wiki_getallpage ref_page_14 Catone && diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh index d05c613c97..57ff4b25c1 100755 --- a/contrib/subtree/t/t7900-subtree.sh +++ b/contrib/subtree/t/t7900-subtree.sh @@ -540,26 +540,10 @@ test_expect_success 'make sure exactly the right set of files ends up in the sub git fetch .. subproj-br && git merge FETCH_HEAD && - chks="sub1 -sub2 -sub3 -sub4" && - chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\'' -$chks -TXT -) && - chkms="main-sub1 -main-sub2 -main-sub3 -main-sub4" && - chkms_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\'' -$chkms -TXT -) && - - subfiles=$(git ls-files) && - check_equal "$subfiles" "$chkms -$chks" + test_write_lines main-sub1 main-sub2 main-sub3 main-sub4 \ + sub1 sub2 sub3 sub4 >expect && + git ls-files >actual && + test_cmp expect actual ) ' @@ -606,25 +590,11 @@ test_expect_success 'make sure the subproj *only* contains commits that affect t git fetch .. subproj-br && git merge FETCH_HEAD && - chks="sub1 -sub2 -sub3 -sub4" && - chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\'' -$chks -TXT -) && - chkms="main-sub1 -main-sub2 -main-sub3 -main-sub4" && - chkms_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\'' -$chkms -TXT -) && - allchanges=$(git log --name-only --pretty=format:"" | sort | sed "/^$/d") && - check_equal "$allchanges" "$chkms -$chks" + test_write_lines main-sub1 main-sub2 main-sub3 main-sub4 \ + sub1 sub2 sub3 sub4 >expect && + git log --name-only --pretty=format:"" >log && + sort <log | sed "/^\$/ d" >actual && + test_cmp expect actual ) ' @@ -675,29 +645,16 @@ test_expect_success 'make sure exactly the right set of files ends up in the mai cd "$subtree_test_count" && git subtree pull --prefix="sub dir" ./"sub proj" master && - chkm="main1 -main2" && - chks="sub1 -sub2 -sub3 -sub4" && - chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\'' -$chks -TXT -) && - chkms="main-sub1 -main-sub2 -main-sub3 -main-sub4" && - chkms_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\'' -$chkms -TXT -) && - mainfiles=$(git ls-files) && - check_equal "$mainfiles" "$chkm -$chkms_sub -$chks_sub" -) + test_write_lines main1 main2 >chkm && + test_write_lines main-sub1 main-sub2 main-sub3 main-sub4 >chkms && + sed "s,^,sub dir/," chkms >chkms_sub && + test_write_lines sub1 sub2 sub3 sub4 >chks && + sed "s,^,sub dir/," chks >chks_sub && + + cat chkm chkms_sub chks_sub >expect && + git ls-files >actual && + test_cmp expect actual + ) ' next_test @@ -708,7 +665,7 @@ test_expect_success 'make sure each filename changed exactly once in the entire test_create_commit "$subtree_test_count/sub proj" sub1 && ( cd "$subtree_test_count" && - git config log.date relative + git config log.date relative && git fetch ./"sub proj" master && git subtree add --prefix="sub dir" FETCH_HEAD ) && @@ -748,37 +705,21 @@ test_expect_success 'make sure each filename changed exactly once in the entire cd "$subtree_test_count" && git subtree pull --prefix="sub dir" ./"sub proj" master && - chkm="main1 -main2" && - chks="sub1 -sub2 -sub3 -sub4" && - chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\'' -$chks -TXT -) && - chkms="main-sub1 -main-sub2 -main-sub3 -main-sub4" && - chkms_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\'' -$chkms -TXT -) && + test_write_lines main1 main2 >chkm && + test_write_lines sub1 sub2 sub3 sub4 >chks && + test_write_lines main-sub1 main-sub2 main-sub3 main-sub4 >chkms && + sed "s,^,sub dir/," chkms >chkms_sub && # main-sub?? and /"sub dir"/main-sub?? both change, because those are the # changes that were split into their own history. And "sub dir"/sub?? never # change, since they were *only* changed in the subtree branch. - allchanges=$(git log --name-only --pretty=format:"" | sort | sed "/^$/d") && - expected=''"$(cat <<TXT | sort -$chkms -$chkm -$chks -$chkms_sub -TXT -)"'' && - check_equal "$allchanges" "$expected" + git log --name-only --pretty=format:"" >log && + sort <log >sorted-log && + sed "/^$/ d" sorted-log >actual && + + cat chkms chkm chks chkms_sub >expect-unsorted && + sort expect-unsorted >expect && + test_cmp expect actual ) ' diff --git a/contrib/vscode/.gitattributes b/contrib/vscode/.gitattributes new file mode 100644 index 0000000000..e89f2236ef --- /dev/null +++ b/contrib/vscode/.gitattributes @@ -0,0 +1 @@ +init.sh whitespace=-indent-with-non-tab diff --git a/contrib/vscode/README.md b/contrib/vscode/README.md new file mode 100644 index 0000000000..8202d62035 --- /dev/null +++ b/contrib/vscode/README.md @@ -0,0 +1,14 @@ +Configuration for VS Code +========================= + +[VS Code](https://code.visualstudio.com/) is a lightweight but powerful source +code editor which runs on your desktop and is available for +[Windows](https://code.visualstudio.com/docs/setup/windows), +[macOS](https://code.visualstudio.com/docs/setup/mac) and +[Linux](https://code.visualstudio.com/docs/setup/linux). Among other languages, +it has [support for C/C++ via an extension](https://github.com/Microsoft/vscode-cpptools). + +To start developing Git with VS Code, simply run the Unix shell script called +`init.sh` in this directory, which creates the configuration files in +`.vscode/` that VS Code consumes. `init.sh` needs access to `make` and `gcc`, +so run the script in a Git SDK shell if you are using Windows. diff --git a/contrib/vscode/init.sh b/contrib/vscode/init.sh new file mode 100755 index 0000000000..27de94994b --- /dev/null +++ b/contrib/vscode/init.sh @@ -0,0 +1,375 @@ +#!/bin/sh + +die () { + echo "$*" >&2 + exit 1 +} + +cd "$(dirname "$0")"/../.. || +die "Could not cd to top-level directory" + +mkdir -p .vscode || +die "Could not create .vscode/" + +# General settings + +cat >.vscode/settings.json.new <<\EOF || +{ + "C_Cpp.intelliSenseEngine": "Default", + "C_Cpp.intelliSenseEngineFallback": "Disabled", + "[git-commit]": { + "editor.wordWrap": "wordWrapColumn", + "editor.wordWrapColumn": 72 + }, + "[c]": { + "editor.detectIndentation": false, + "editor.insertSpaces": false, + "editor.tabSize": 8, + "editor.wordWrap": "wordWrapColumn", + "editor.wordWrapColumn": 80, + "files.trimTrailingWhitespace": true + }, + "files.associations": { + "*.h": "c", + "*.c": "c" + }, + "cSpell.ignorePaths": [ + ], + "cSpell.words": [ + "DATAW", + "DBCACHED", + "DFCHECK", + "DTYPE", + "Hamano", + "HCAST", + "HEXSZ", + "HKEY", + "HKLM", + "IFGITLINK", + "IFINVALID", + "ISBROKEN", + "ISGITLINK", + "ISSYMREF", + "Junio", + "LPDWORD", + "LPPROC", + "LPWSTR", + "MSVCRT", + "NOARG", + "NOCOMPLETE", + "NOINHERIT", + "RENORMALIZE", + "STARTF", + "STARTUPINFOEXW", + "Schindelin", + "UCRT", + "YESNO", + "argcp", + "beginthreadex", + "committish", + "contentp", + "cpath", + "cpidx", + "ctim", + "dequote", + "envw", + "ewah", + "fdata", + "fherr", + "fhin", + "fhout", + "fragp", + "fsmonitor", + "hnsec", + "idents", + "includeif", + "interpr", + "iprog", + "isexe", + "iskeychar", + "kompare", + "mksnpath", + "mktag", + "mktree", + "mmblob", + "mmbuffer", + "mmfile", + "noenv", + "nparents", + "ntpath", + "ondisk", + "ooid", + "oplen", + "osdl", + "pnew", + "pold", + "ppinfo", + "pushf", + "pushv", + "rawsz", + "rebasing", + "reencode", + "repo", + "rerere", + "scld", + "sharedrepo", + "spawnv", + "spawnve", + "spawnvpe", + "strdup'ing", + "submodule", + "submodules", + "topath", + "topo", + "tpatch", + "unexecutable", + "unhide", + "unkc", + "unkv", + "unmark", + "unmatch", + "unsets", + "unshown", + "untracked", + "untrackedcache", + "unuse", + "upos", + "uval", + "vreportf", + "wargs", + "wargv", + "wbuffer", + "wcmd", + "wcsnicmp", + "wcstoutfdup", + "wdeltaenv", + "wdir", + "wenv", + "wenvblk", + "wenvcmp", + "wenviron", + "wenvpos", + "wenvsz", + "wfile", + "wfilename", + "wfopen", + "wfreopen", + "wfullpath", + "which'll", + "wlink", + "wmain", + "wmkdir", + "wmktemp", + "wnewpath", + "wotype", + "wpath", + "wpathname", + "wpgmptr", + "wpnew", + "wpointer", + "wpold", + "wpos", + "wputenv", + "wrmdir", + "wship", + "wtarget", + "wtemplate", + "wunlink", + "xcalloc", + "xgetcwd", + "xmallocz", + "xmemdupz", + "xmmap", + "xopts", + "xrealloc", + "xsnprintf", + "xutftowcs", + "xutftowcsn", + "xwcstoutf" + ], + "cSpell.ignoreRegExpList": [ + "\\\"(DIRC|FSMN|REUC|UNTR)\\\"", + "\\\\u[0-9a-fA-Fx]{4}\\b", + "\\b(filfre|frotz|xyzzy)\\b", + "\\bCMIT_FMT_DEFAULT\\b", + "\\bde-munge\\b", + "\\bGET_OID_DISAMBIGUATORS\\b", + "\\bHASH_RENORMALIZE\\b", + "\\bTREESAMEness\\b", + "\\bUSE_STDEV\\b", + "\\Wchar *\\*\\W*utfs\\W", + "cURL's", + "nedmalloc'ed", + "ntifs\\.h", + ], +} +EOF +die "Could not write settings.json" + +# Infer some setup-specific locations/names + +GCCPATH="$(which gcc)" +GDBPATH="$(which gdb)" +MAKECOMMAND="make -j5 DEVELOPER=1" +OSNAME= +X= +case "$(uname -s)" in +MINGW*) + GCCPATH="$(cygpath -am "$GCCPATH")" + GDBPATH="$(cygpath -am "$GDBPATH")" + MAKE_BASH="$(cygpath -am /git-cmd.exe) --command=usr\\\\bin\\\\bash.exe" + MAKECOMMAND="$MAKE_BASH -lc \\\"$MAKECOMMAND\\\"" + OSNAME=Win32 + X=.exe + ;; +Linux) + OSNAME=Linux + ;; +Darwin) + OSNAME=macOS + ;; +esac + +# Default build task + +cat >.vscode/tasks.json.new <<EOF || +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "make", + "type": "shell", + "command": "$MAKECOMMAND", + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} +EOF +die "Could not install default build task" + +# Debugger settings + +cat >.vscode/launch.json.new <<EOF || +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: + // https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "\${workspaceFolder}/git$X", + "args": [], + "stopAtEntry": false, + "cwd": "\${workspaceFolder}", + "environment": [], + "externalConsole": true, + "MIMode": "gdb", + "miDebuggerPath": "$GDBPATH", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} +EOF +die "Could not write launch configuration" + +# C/C++ extension settings + +make -f - OSNAME=$OSNAME GCCPATH="$GCCPATH" vscode-init \ + >.vscode/c_cpp_properties.json <<\EOF || +include Makefile + +vscode-init: + @mkdir -p .vscode && \ + incs= && defs= && \ + for e in $(ALL_CFLAGS) \ + '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \ + '-DGIT_LOCALE_PATH="$(localedir_relative_SQ)"' \ + '-DBINDIR="$(bindir_relative_SQ)"' \ + '-DFALLBACK_RUNTIME_PREFIX="$(prefix_SQ)"' \ + '-DDEFAULT_GIT_TEMPLATE_DIR="$(template_dir_SQ)"' \ + '-DETC_GITCONFIG="$(ETC_GITCONFIG_SQ)"' \ + '-DETC_GITATTRIBUTES="$(ETC_GITATTRIBUTES_SQ)"' \ + '-DGIT_LOCALE_PATH="$(localedir_relative_SQ)"' \ + '-DCURL_DISABLE_TYPECHECK', \ + '-DGIT_HTML_PATH="$(htmldir_relative_SQ)"' \ + '-DGIT_MAN_PATH="$(mandir_relative_SQ)"' \ + '-DGIT_INFO_PATH="$(infodir_relative_SQ)"'; do \ + case "$$e" in \ + -I.) \ + incs="$$(printf '% 16s"$${workspaceRoot}",\n%s' \ + "" "$$incs")" \ + ;; \ + -I/*) \ + incs="$$(printf '% 16s"%s",\n%s' \ + "" "$${e#-I}" "$$incs")" \ + ;; \ + -I*) \ + incs="$$(printf '% 16s"$${workspaceRoot}/%s",\n%s' \ + "" "$${e#-I}" "$$incs")" \ + ;; \ + -D*) \ + defs="$$(printf '% 16s"%s",\n%s' \ + "" "$$(echo "$${e#-D}" | sed 's/"/\\&/g')" \ + "$$defs")" \ + ;; \ + esac; \ + done && \ + echo '{' && \ + echo ' "configurations": [' && \ + echo ' {' && \ + echo ' "name": "$(OSNAME)",' && \ + echo ' "intelliSenseMode": "clang-x64",' && \ + echo ' "includePath": [' && \ + echo "$$incs" | sort | sed '$$s/,$$//' && \ + echo ' ],' && \ + echo ' "defines": [' && \ + echo "$$defs" | sort | sed '$$s/,$$//' && \ + echo ' ],' && \ + echo ' "browse": {' && \ + echo ' "limitSymbolsToIncludedHeaders": true,' && \ + echo ' "databaseFilename": "",' && \ + echo ' "path": [' && \ + echo ' "$${workspaceRoot}"' && \ + echo ' ]' && \ + echo ' },' && \ + echo ' "cStandard": "c11",' && \ + echo ' "cppStandard": "c++17",' && \ + echo ' "compilerPath": "$(GCCPATH)"' && \ + echo ' }' && \ + echo ' ],' && \ + echo ' "version": 4' && \ + echo '}' +EOF +die "Could not write settings for the C/C++ extension" + +for file in .vscode/settings.json .vscode/tasks.json .vscode/launch.json +do + if test -f $file + then + if git diff --no-index --quiet --exit-code $file $file.new + then + rm $file.new + else + printf "The file $file.new has these changes:\n\n" + git --no-pager diff --no-index $file $file.new + printf "\n\nMaybe \`mv $file.new $file\`?\n\n" + fi + else + mv $file.new $file + fi +done @@ -191,7 +191,7 @@ static enum eol output_eol(enum crlf_action crlf_action) /* fall through */ return text_eol_is_crlf() ? EOL_CRLF : EOL_LF; } - warning("Illegal crlf_action %d\n", (int)crlf_action); + warning(_("illegal crlf_action %d"), (int)crlf_action); return core_eol; } @@ -204,11 +204,11 @@ static void check_global_conv_flags_eol(const char *path, enum crlf_action crlf_ * CRLFs would not be restored by checkout */ if (conv_flags & CONV_EOL_RNDTRP_DIE) - die(_("CRLF would be replaced by LF in %s."), path); + die(_("CRLF would be replaced by LF in %s"), path); else if (conv_flags & CONV_EOL_RNDTRP_WARN) warning(_("CRLF will be replaced by LF in %s.\n" "The file will have its original line" - " endings in your working directory."), path); + " endings in your working directory"), path); } else if (old_stats->lonelf && !new_stats->lonelf ) { /* * CRLFs would be added by checkout @@ -218,7 +218,7 @@ static void check_global_conv_flags_eol(const char *path, enum crlf_action crlf_ else if (conv_flags & CONV_EOL_RNDTRP_WARN) warning(_("LF will be replaced by CRLF in %s.\n" "The file will have its original line" - " endings in your working directory."), path); + " endings in your working directory"), path); } } @@ -390,7 +390,7 @@ static int encode_to_git(const char *path, const char *src, size_t src_len, struct strbuf *buf, const char *enc, int conv_flags) { char *dst; - int dst_len; + size_t dst_len; int die_on_error = conv_flags & CONV_WRITE_OBJECT; /* @@ -453,7 +453,7 @@ static int encode_to_git(const char *path, const char *src, size_t src_len, */ if (die_on_error && check_roundtrip(enc)) { char *re_src; - int re_src_len; + size_t re_src_len; re_src = reencode_string_len(dst, dst_len, enc, default_encoding, @@ -481,7 +481,7 @@ static int encode_to_worktree(const char *path, const char *src, size_t src_len, struct strbuf *buf, const char *enc) { char *dst; - int dst_len; + size_t dst_len; /* * No encoding is specified or there is nothing to encode. @@ -493,8 +493,8 @@ static int encode_to_worktree(const char *path, const char *src, size_t src_len, dst = reencode_string_len(src, src_len, enc, default_encoding, &dst_len); if (!dst) { - error("failed to encode '%s' from %s to %s", - path, default_encoding, enc); + error(_("failed to encode '%s' from %s to %s"), + path, default_encoding, enc); return 0; } @@ -671,7 +671,8 @@ static int filter_buffer_or_fd(int in, int out, void *data) if (start_command(&child_process)) { strbuf_release(&cmd); - return error("cannot fork to run external filter '%s'", params->cmd); + return error(_("cannot fork to run external filter '%s'"), + params->cmd); } sigchain_push(SIGPIPE, SIG_IGN); @@ -690,13 +691,14 @@ static int filter_buffer_or_fd(int in, int out, void *data) if (close(child_process.in)) write_err = 1; if (write_err) - error("cannot feed the input to external filter '%s'", params->cmd); + error(_("cannot feed the input to external filter '%s'"), + params->cmd); sigchain_pop(SIGPIPE); status = finish_command(&child_process); if (status) - error("external filter '%s' failed %d", params->cmd, status); + error(_("external filter '%s' failed %d"), params->cmd, status); strbuf_release(&cmd); return (write_err || status); @@ -731,13 +733,13 @@ static int apply_single_file_filter(const char *path, const char *src, size_t le return 0; /* error was already reported */ if (strbuf_read(&nbuf, async.out, len) < 0) { - err = error("read from external filter '%s' failed", cmd); + err = error(_("read from external filter '%s' failed"), cmd); } if (close(async.out)) { - err = error("read from external filter '%s' failed", cmd); + err = error(_("read from external filter '%s' failed"), cmd); } if (finish_async(&async)) { - err = error("external filter '%s' failed", cmd); + err = error(_("external filter '%s' failed"), cmd); } if (!err) { @@ -791,7 +793,7 @@ static void handle_filter_error(const struct strbuf *filter_status, * Something went wrong with the protocol filter. * Force shutdown and restart if another blob requires filtering. */ - error("external filter '%s' failed", entry->subprocess.cmd); + error(_("external filter '%s' failed"), entry->subprocess.cmd); subprocess_stop(&subprocess_map, &entry->subprocess); free(entry); } @@ -839,7 +841,7 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len else if (wanted_capability & CAP_SMUDGE) filter_type = "smudge"; else - die("unexpected filter type"); + die(_("unexpected filter type")); sigchain_push(SIGPIPE, SIG_IGN); @@ -850,7 +852,7 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len err = strlen(path) > LARGE_PACKET_DATA_MAX - strlen("pathname=\n"); if (err) { - error("path name too long for external filter"); + error(_("path name too long for external filter")); goto done; } @@ -924,8 +926,8 @@ int async_query_available_blobs(const char *cmd, struct string_list *available_p assert(subprocess_map_initialized); entry = (struct cmd2process *)subprocess_find_entry(&subprocess_map, cmd); if (!entry) { - error("external filter '%s' is not available anymore although " - "not all paths have been filtered", cmd); + error(_("external filter '%s' is not available anymore although " + "not all paths have been filtered"), cmd); return 0; } process = &entry->subprocess.process; @@ -1396,7 +1398,7 @@ int convert_to_git(const struct index_state *istate, ret |= apply_filter(path, src, len, -1, dst, ca.drv, CAP_CLEAN, NULL); if (!ret && ca.drv && ca.drv->required) - die("%s: clean filter '%s' failed", path, ca.drv->name); + die(_("%s: clean filter '%s' failed"), path, ca.drv->name); if (ret && dst) { src = dst->buf; @@ -1430,7 +1432,7 @@ void convert_to_git_filter_fd(const struct index_state *istate, assert(ca.drv->clean || ca.drv->process); if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN, NULL)) - die("%s: clean filter '%s' failed", path, ca.drv->name); + die(_("%s: clean filter '%s' failed"), path, ca.drv->name); encode_to_git(path, dst->buf, dst->len, dst, ca.working_tree_encoding, conv_flags); crlf_to_git(istate, path, dst->buf, dst->len, dst, ca.crlf_action, conv_flags); @@ -1473,7 +1475,7 @@ static int convert_to_working_tree_internal(const char *path, const char *src, ret_filter = apply_filter( path, src, len, -1, dst, ca.drv, CAP_SMUDGE, dco); if (!ret_filter && ca.drv && ca.drv->required) - die("%s: smudge filter %s failed", path, ca.drv->name); + die(_("%s: smudge filter %s failed"), path, ca.drv->name); return ret | ret_filter; } @@ -57,35 +57,36 @@ struct delayed_checkout { extern enum eol core_eol; extern char *check_roundtrip_encoding; -extern const char *get_cached_convert_stats_ascii(const struct index_state *istate, - const char *path); -extern const char *get_wt_convert_stats_ascii(const char *path); -extern const char *get_convert_attr_ascii(const char *path); +const char *get_cached_convert_stats_ascii(const struct index_state *istate, + const char *path); +const char *get_wt_convert_stats_ascii(const char *path); +const char *get_convert_attr_ascii(const char *path); /* returns 1 if *dst was used */ -extern int convert_to_git(const struct index_state *istate, - const char *path, const char *src, size_t len, - struct strbuf *dst, int conv_flags); -extern int convert_to_working_tree(const char *path, const char *src, - size_t len, struct strbuf *dst); -extern int async_convert_to_working_tree(const char *path, const char *src, - size_t len, struct strbuf *dst, - void *dco); -extern int async_query_available_blobs(const char *cmd, struct string_list *available_paths); -extern int renormalize_buffer(const struct index_state *istate, - const char *path, const char *src, size_t len, - struct strbuf *dst); +int convert_to_git(const struct index_state *istate, + const char *path, const char *src, size_t len, + struct strbuf *dst, int conv_flags); +int convert_to_working_tree(const char *path, const char *src, + size_t len, struct strbuf *dst); +int async_convert_to_working_tree(const char *path, const char *src, + size_t len, struct strbuf *dst, + void *dco); +int async_query_available_blobs(const char *cmd, + struct string_list *available_paths); +int renormalize_buffer(const struct index_state *istate, + const char *path, const char *src, size_t len, + struct strbuf *dst); static inline int would_convert_to_git(const struct index_state *istate, const char *path) { return convert_to_git(istate, path, NULL, 0, NULL, 0); } /* Precondition: would_convert_to_git_filter_fd(path) == true */ -extern void convert_to_git_filter_fd(const struct index_state *istate, - const char *path, int fd, - struct strbuf *dst, - int conv_flags); -extern int would_convert_to_git_filter_fd(const char *path); +void convert_to_git_filter_fd(const struct index_state *istate, + const char *path, int fd, + struct strbuf *dst, + int conv_flags); +int would_convert_to_git_filter_fd(const char *path); /***************************************************************** * @@ -95,9 +96,10 @@ extern int would_convert_to_git_filter_fd(const char *path); struct stream_filter; /* opaque */ -extern struct stream_filter *get_stream_filter(const char *path, const struct object_id *); -extern void free_stream_filter(struct stream_filter *); -extern int is_null_stream_filter(struct stream_filter *); +struct stream_filter *get_stream_filter(const char *path, + const struct object_id *); +void free_stream_filter(struct stream_filter *); +int is_null_stream_filter(struct stream_filter *); /* * Use as much input up to *isize_p and fill output up to *osize_p; @@ -111,8 +113,8 @@ extern int is_null_stream_filter(struct stream_filter *); * such filters know there is no more input coming and it is time for * them to produce the remaining output based on the buffered input. */ -extern int stream_filter(struct stream_filter *, - const char *input, size_t *isize_p, - char *output, size_t *osize_p); +int stream_filter(struct stream_filter *, + const char *input, size_t *isize_p, + char *output, size_t *osize_p); #endif /* CONVERT_H */ @@ -271,10 +271,12 @@ static int parse_color_moved(const char *arg) return COLOR_MOVED_ZEBRA; else if (!strcmp(arg, "default")) return COLOR_MOVED_DEFAULT; + else if (!strcmp(arg, "dimmed-zebra")) + return COLOR_MOVED_ZEBRA_DIM; else if (!strcmp(arg, "dimmed_zebra")) return COLOR_MOVED_ZEBRA_DIM; else - return error(_("color moved setting must be one of 'no', 'default', 'blocks', 'zebra', 'dimmed_zebra', 'plain'")); + return error(_("color moved setting must be one of 'no', 'default', 'blocks', 'zebra', 'dimmed-zebra', 'plain'")); } static int parse_color_moved_ws(const char *arg) @@ -2069,8 +2071,8 @@ static void init_diff_words_data(struct emit_callback *ecbdata, if (regcomp(ecbdata->diff_words->word_regex, o->word_regex, REG_EXTENDED | REG_NEWLINE)) - die ("Invalid regular expression: %s", - o->word_regex); + die("invalid regular expression: %s", + o->word_regex); } for (i = 0; i < ARRAY_SIZE(diff_words_styles); i++) { if (o->word_diff == diff_words_styles[i].type) { @@ -258,15 +258,15 @@ const char *diff_line_prefix(struct diff_options *); extern const char mime_boundary_leader[]; -extern struct combine_diff_path *diff_tree_paths( +struct combine_diff_path *diff_tree_paths( struct combine_diff_path *p, const struct object_id *oid, const struct object_id **parents_oid, int nparent, struct strbuf *base, struct diff_options *opt); -extern int diff_tree_oid(const struct object_id *old_oid, - const struct object_id *new_oid, - const char *base, struct diff_options *opt); -extern int diff_root_tree_oid(const struct object_id *new_oid, const char *base, - struct diff_options *opt); +int diff_tree_oid(const struct object_id *old_oid, + const struct object_id *new_oid, + const char *base, struct diff_options *opt); +int diff_root_tree_oid(const struct object_id *new_oid, const char *base, + struct diff_options *opt); struct combine_diff_path { struct combine_diff_path *next; @@ -283,33 +283,33 @@ struct combine_diff_path { st_add4(sizeof(struct combine_diff_path), (l), 1, \ st_mult(sizeof(struct combine_diff_parent), (n))) -extern void show_combined_diff(struct combine_diff_path *elem, int num_parent, - int dense, struct rev_info *); +void show_combined_diff(struct combine_diff_path *elem, int num_parent, + int dense, struct rev_info *); -extern void diff_tree_combined(const struct object_id *oid, const struct oid_array *parents, int dense, struct rev_info *rev); +void diff_tree_combined(const struct object_id *oid, const struct oid_array *parents, int dense, struct rev_info *rev); -extern void diff_tree_combined_merge(const struct commit *commit, int dense, struct rev_info *rev); +void diff_tree_combined_merge(const struct commit *commit, int dense, struct rev_info *rev); void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b); -extern int diff_can_quit_early(struct diff_options *); +int diff_can_quit_early(struct diff_options *); -extern void diff_addremove(struct diff_options *, - int addremove, - unsigned mode, - const struct object_id *oid, - int oid_valid, - const char *fullpath, unsigned dirty_submodule); +void diff_addremove(struct diff_options *, + int addremove, + unsigned mode, + const struct object_id *oid, + int oid_valid, + const char *fullpath, unsigned dirty_submodule); -extern void diff_change(struct diff_options *, - unsigned mode1, unsigned mode2, - const struct object_id *old_oid, - const struct object_id *new_oid, - int old_oid_valid, int new_oid_valid, - const char *fullpath, - unsigned dirty_submodule1, unsigned dirty_submodule2); +void diff_change(struct diff_options *, + unsigned mode1, unsigned mode2, + const struct object_id *old_oid, + const struct object_id *new_oid, + int old_oid_valid, int new_oid_valid, + const char *fullpath, + unsigned dirty_submodule1, unsigned dirty_submodule2); -extern struct diff_filepair *diff_unmerge(struct diff_options *, const char *path); +struct diff_filepair *diff_unmerge(struct diff_options *, const char *path); #define DIFF_SETUP_REVERSE 1 #define DIFF_SETUP_USE_CACHE 2 @@ -319,17 +319,17 @@ extern struct diff_filepair *diff_unmerge(struct diff_options *, const char *pat * Poor man's alternative to parse-option, to allow both stuck form * (--option=value) and separate form (--option value). */ -extern int parse_long_opt(const char *opt, const char **argv, - const char **optarg); - -extern int git_diff_basic_config(const char *var, const char *value, void *cb); -extern int git_diff_heuristic_config(const char *var, const char *value, void *cb); -extern void init_diff_ui_defaults(void); -extern int git_diff_ui_config(const char *var, const char *value, void *cb); -extern void diff_setup(struct diff_options *); -extern int diff_opt_parse(struct diff_options *, const char **, int, const char *); -extern void diff_setup_done(struct diff_options *); -extern int git_config_rename(const char *var, const char *value); +int parse_long_opt(const char *opt, const char **argv, + const char **optarg); + +int git_diff_basic_config(const char *var, const char *value, void *cb); +int git_diff_heuristic_config(const char *var, const char *value, void *cb); +void init_diff_ui_defaults(void); +int git_diff_ui_config(const char *var, const char *value, void *cb); +void diff_setup(struct diff_options *); +int diff_opt_parse(struct diff_options *, const char **, int, const char *); +void diff_setup_done(struct diff_options *); +int git_config_rename(const char *var, const char *value); #define DIFF_DETECT_RENAME 1 #define DIFF_DETECT_COPY 2 @@ -347,8 +347,8 @@ extern int git_config_rename(const char *var, const char *value); #define DIFF_PICKAXE_IGNORE_CASE 32 -extern void diffcore_std(struct diff_options *); -extern void diffcore_fix_diff_index(struct diff_options *); +void diffcore_std(struct diff_options *); +void diffcore_fix_diff_index(struct diff_options *); #define COMMON_DIFF_OPTIONS_HELP \ "\ncommon diff options:\n" \ @@ -378,9 +378,9 @@ extern void diffcore_fix_diff_index(struct diff_options *); " show all files diff when -S is used and hit is found.\n" \ " -a --text treat all files as text.\n" -extern int diff_queue_is_empty(void); -extern void diff_flush(struct diff_options*); -extern void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc); +int diff_queue_is_empty(void); +void diff_flush(struct diff_options*); +void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc); /* diff-raw status letters */ #define DIFF_STATUS_ADDED 'A' @@ -402,24 +402,24 @@ extern void diff_warn_rename_limit(const char *varname, int needed, int degraded * This is different from find_unique_abbrev() in that * it stuffs the result with dots for alignment. */ -extern const char *diff_aligned_abbrev(const struct object_id *sha1, int); +const char *diff_aligned_abbrev(const struct object_id *sha1, int); /* do not report anything on removed paths */ #define DIFF_SILENT_ON_REMOVED 01 /* report racily-clean paths as modified */ #define DIFF_RACY_IS_MODIFIED 02 -extern int run_diff_files(struct rev_info *revs, unsigned int option); -extern int run_diff_index(struct rev_info *revs, int cached); +int run_diff_files(struct rev_info *revs, unsigned int option); +int run_diff_index(struct rev_info *revs, int cached); -extern int do_diff_cache(const struct object_id *, struct diff_options *); -extern int diff_flush_patch_id(struct diff_options *, struct object_id *, int); +int do_diff_cache(const struct object_id *, struct diff_options *); +int diff_flush_patch_id(struct diff_options *, struct object_id *, int); -extern int diff_result_code(struct diff_options *, int); +int diff_result_code(struct diff_options *, int); -extern void diff_no_index(struct rev_info *, int, const char **); +void diff_no_index(struct rev_info *, int, const char **); -extern int index_differs_from(const char *def, const struct diff_flags *flags, - int ita_invisible_in_index); +int index_differs_from(const char *def, const struct diff_flags *flags, + int ita_invisible_in_index); /* * Fill the contents of the filespec "df", respecting any textconv defined by @@ -432,30 +432,30 @@ extern int index_differs_from(const char *def, const struct diff_flags *flags, * struct. If it is non-NULL, then "outbuf" points to a newly allocated buffer * that should be freed by the caller. */ -extern size_t fill_textconv(struct userdiff_driver *driver, - struct diff_filespec *df, - char **outbuf); +size_t fill_textconv(struct userdiff_driver *driver, + struct diff_filespec *df, + char **outbuf); /* * Look up the userdiff driver for the given filespec, and return it if * and only if it has textconv enabled (otherwise return NULL). The result * can be passed to fill_textconv(). */ -extern struct userdiff_driver *get_textconv(struct diff_filespec *one); +struct userdiff_driver *get_textconv(struct diff_filespec *one); /* * Prepare diff_filespec and convert it using diff textconv API * if the textconv driver exists. * Return 1 if the conversion succeeds, 0 otherwise. */ -extern int textconv_object(const char *path, unsigned mode, const struct object_id *oid, int oid_valid, char **buf, unsigned long *buf_size); +int textconv_object(const char *path, unsigned mode, const struct object_id *oid, int oid_valid, char **buf, unsigned long *buf_size); -extern int parse_rename_score(const char **cp_p); +int parse_rename_score(const char **cp_p); -extern long parse_algorithm_value(const char *value); +long parse_algorithm_value(const char *value); -extern void print_stat_summary(FILE *fp, int files, - int insertions, int deletions); -extern void setup_diff_pager(struct diff_options *); +void print_stat_summary(FILE *fp, int files, + int insertions, int deletions); +void setup_diff_pager(struct diff_options *); #endif /* DIFF_H */ diff --git a/diffcore.h b/diffcore.h index a30da161da..81281a398b 100644 --- a/diffcore.h +++ b/diffcore.h @@ -50,17 +50,17 @@ struct diff_filespec { struct userdiff_driver *driver; }; -extern struct diff_filespec *alloc_filespec(const char *); -extern void free_filespec(struct diff_filespec *); -extern void fill_filespec(struct diff_filespec *, const struct object_id *, - int, unsigned short); +struct diff_filespec *alloc_filespec(const char *); +void free_filespec(struct diff_filespec *); +void fill_filespec(struct diff_filespec *, const struct object_id *, + int, unsigned short); #define CHECK_SIZE_ONLY 1 #define CHECK_BINARY 2 -extern int diff_populate_filespec(struct diff_filespec *, unsigned int); -extern void diff_free_filespec_data(struct diff_filespec *); -extern void diff_free_filespec_blob(struct diff_filespec *); -extern int diff_filespec_is_binary(struct diff_filespec *); +int diff_populate_filespec(struct diff_filespec *, unsigned int); +void diff_free_filespec_data(struct diff_filespec *); +void diff_free_filespec_blob(struct diff_filespec *); +int diff_filespec_is_binary(struct diff_filespec *); struct diff_filepair { struct diff_filespec *one; @@ -86,9 +86,9 @@ struct diff_filepair { #define DIFF_PAIR_MODE_CHANGED(p) ((p)->one->mode != (p)->two->mode) -extern void diff_free_filepair(struct diff_filepair *); +void diff_free_filepair(struct diff_filepair *); -extern int diff_unmodified_pair(struct diff_filepair *); +int diff_unmodified_pair(struct diff_filepair *); struct diff_queue_struct { struct diff_filepair **queue; @@ -102,16 +102,16 @@ struct diff_queue_struct { } while (0) extern struct diff_queue_struct diff_queued_diff; -extern struct diff_filepair *diff_queue(struct diff_queue_struct *, - struct diff_filespec *, - struct diff_filespec *); -extern void diff_q(struct diff_queue_struct *, struct diff_filepair *); +struct diff_filepair *diff_queue(struct diff_queue_struct *, + struct diff_filespec *, + struct diff_filespec *); +void diff_q(struct diff_queue_struct *, struct diff_filepair *); -extern void diffcore_break(int); -extern void diffcore_rename(struct diff_options *); -extern void diffcore_merge_broken(void); -extern void diffcore_pickaxe(struct diff_options *); -extern void diffcore_order(const char *orderfile); +void diffcore_break(int); +void diffcore_rename(struct diff_options *); +void diffcore_merge_broken(void); +void diffcore_pickaxe(struct diff_options *); +void diffcore_order(const char *orderfile); /* low-level interface to diffcore_order */ struct obj_order { @@ -138,11 +138,11 @@ void diff_debug_queue(const char *, struct diff_queue_struct *); #define diff_debug_queue(a,b) do { /* nothing */ } while (0) #endif -extern int diffcore_count_changes(struct diff_filespec *src, - struct diff_filespec *dst, - void **src_count_p, - void **dst_count_p, - unsigned long *src_copied, - unsigned long *literal_added); +int diffcore_count_changes(struct diff_filespec *src, + struct diff_filespec *dst, + void **src_count_p, + void **dst_count_p, + unsigned long *src_copied, + unsigned long *literal_added); #endif @@ -561,7 +561,7 @@ int report_path_error(const char *ps_matched, if (found_dup) continue; - error("pathspec '%s' did not match any file(s) known to git.", + error(_("pathspec '%s' did not match any file(s) known to git"), pathspec->items[num].original); errors++; } @@ -950,7 +950,7 @@ static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname, dir->unmanaged_exclude_files++; el = add_exclude_list(dir, EXC_FILE, fname); if (add_excludes(fname, "", 0, el, NULL, oid_stat) < 0) - die("cannot use %s as an exclude file", fname); + die(_("cannot use %s as an exclude file"), fname); } void add_excludes_from_file(struct dir_struct *dir, const char *fname) @@ -2231,7 +2231,7 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d return NULL; if (!ident_in_untracked(dir->untracked)) { - warning(_("Untracked cache is disabled on this system or location.")); + warning(_("untracked cache is disabled on this system or location")); return NULL; } @@ -3029,7 +3029,7 @@ static void connect_wt_gitdir_in_nested(const char *sub_worktree, return; if (repo_read_index(&subrepo) < 0) - die("index file corrupt in repo %s", subrepo.gitdir); + die(_("index file corrupt in repo %s"), subrepo.gitdir); for (i = 0; i < subrepo.index->cache_nr; i++) { const struct cache_entry *ce = subrepo.index->cache[i]; diff --git a/environment.c b/environment.c index 6cf0079389..68523b6b0e 100644 --- a/environment.c +++ b/environment.c @@ -51,7 +51,7 @@ const char *editor_program; const char *askpass_program; const char *excludes_file; enum auto_crlf auto_crlf = AUTO_CRLF_FALSE; -int check_replace_refs = 1; /* NEEDSWORK: rename to read_replace_refs */ +int read_replace_refs = 1; char *git_replace_ref_base; enum eol core_eol = EOL_UNSET; int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN; @@ -146,7 +146,7 @@ static char *expand_namespace(const char *raw_namespace) strbuf_addf(&buf, "refs/namespaces/%s", (*c)->buf); strbuf_list_free(components); if (check_refname_format(buf.buf, 0)) - die("bad git namespace path \"%s\"", raw_namespace); + die(_("bad git namespace path \"%s\""), raw_namespace); strbuf_addch(&buf, '/'); return strbuf_detach(&buf, NULL); } @@ -182,7 +182,7 @@ void setup_git_env(const char *git_dir) argv_array_clear(&to_free); if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT)) - check_replace_refs = 0; + read_replace_refs = 0; replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT); free(git_replace_ref_base); git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base @@ -328,7 +328,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); + die(_("could not set GIT_DIR to '%s'"), path); setup_git_env(path); } diff --git a/exec-cmd.c b/exec-cmd.c index 02d31ee897..4f81f44310 100644 --- a/exec-cmd.c +++ b/exec-cmd.c @@ -358,7 +358,7 @@ int execl_git_cmd(const char *cmd, ...) } va_end(param); if (MAX_ARGS <= argc) - return error("too many args to run %s", cmd); + return error(_("too many args to run %s"), cmd); argv[argc] = NULL; return execv_git_cmd(argv); diff --git a/fetch-negotiator.c b/fetch-negotiator.c index 5d283049f4..d6d685cba0 100644 --- a/fetch-negotiator.c +++ b/fetch-negotiator.c @@ -6,9 +6,15 @@ void fetch_negotiator_init(struct fetch_negotiator *negotiator, const char *algorithm) { - if (algorithm && !strcmp(algorithm, "skipping")) { - skipping_negotiator_init(negotiator); - return; + if (algorithm) { + if (!strcmp(algorithm, "skipping")) { + skipping_negotiator_init(negotiator); + return; + } else if (!strcmp(algorithm, "default")) { + /* Fall through to default initialization */ + } else { + die("unknown fetch negotiation algorithm '%s'", algorithm); + } } default_negotiator_init(negotiator); } diff --git a/fetch-object.c b/fetch-object.c index 48fe63dd6c..853624f811 100644 --- a/fetch-object.c +++ b/fetch-object.c @@ -19,7 +19,7 @@ static void fetch_refs(const char *remote_name, struct ref *ref) transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1"); transport_set_option(transport, TRANS_OPT_NO_DEPENDENTS, "1"); - transport_fetch_refs(transport, ref, NULL); + transport_fetch_refs(transport, ref); fetch_if_missing = original_fetch_if_missing; } diff --git a/fetch-pack.c b/fetch-pack.c index 5714bcbddd..88a078e9be 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -21,6 +21,7 @@ #include "object-store.h" #include "connected.h" #include "fetch-negotiator.h" +#include "fsck.h" static int transfer_unpack_limit = -1; static int fetch_unpack_limit = -1; @@ -36,6 +37,7 @@ static int server_supports_filtering; static struct lock_file shallow_lock; static const char *alternate_shallow_file; static char *negotiation_algorithm; +static struct strbuf fsck_msg_types = STRBUF_INIT; /* Remember to update object flag allocation in object.h */ #define COMPLETE (1U << 0) @@ -867,7 +869,8 @@ static int get_pack(struct fetch_pack_args *args, */ argv_array_push(&cmd.args, "--fsck-objects"); else - argv_array_push(&cmd.args, "--strict"); + argv_array_pushf(&cmd.args, "--strict%s", + fsck_msg_types.buf); } cmd.in = demux.out; @@ -1185,13 +1188,13 @@ static int process_section_header(struct packet_reader *reader, int ret; if (packet_reader_peek(reader) != PACKET_READ_NORMAL) - die("error reading section header '%s'", section); + die(_("error reading section header '%s'"), section); ret = !strcmp(reader->line, section); if (!peek) { if (!ret) - die("expected '%s', received '%s'", + die(_("expected '%s', received '%s'"), section, reader->line); packet_reader_read(reader); } @@ -1230,12 +1233,12 @@ static int process_acks(struct fetch_negotiator *negotiator, continue; } - die("unexpected acknowledgment line: '%s'", reader->line); + die(_("unexpected acknowledgment line: '%s'"), reader->line); } if (reader->status != PACKET_READ_FLUSH && reader->status != PACKET_READ_DELIM) - die("error processing acks: %d", reader->status); + die(_("error processing acks: %d"), reader->status); /* return 0 if no common, 1 if there are common, or 2 if ready */ return received_ready ? 2 : (received_ack ? 1 : 0); @@ -1272,36 +1275,37 @@ static void receive_shallow_info(struct fetch_pack_args *args, if (reader->status != PACKET_READ_FLUSH && reader->status != PACKET_READ_DELIM) - die("error processing shallow info: %d", reader->status); + die(_("error processing shallow info: %d"), reader->status); setup_alternate_shallow(&shallow_lock, &alternate_shallow_file, NULL); args->deepen = 1; } -static void receive_wanted_refs(struct packet_reader *reader, struct ref *refs) +static void receive_wanted_refs(struct packet_reader *reader, + struct ref **sought, int nr_sought) { process_section_header(reader, "wanted-refs", 0); while (packet_reader_read(reader) == PACKET_READ_NORMAL) { struct object_id oid; const char *end; - struct ref *r = NULL; + int i; if (parse_oid_hex(reader->line, &oid, &end) || *end++ != ' ') - die("expected wanted-ref, got '%s'", reader->line); + die(_("expected wanted-ref, got '%s'"), reader->line); - for (r = refs; r; r = r->next) { - if (!strcmp(end, r->name)) { - oidcpy(&r->old_oid, &oid); + for (i = 0; i < nr_sought; i++) { + if (!strcmp(end, sought[i]->name)) { + oidcpy(&sought[i]->old_oid, &oid); break; } } - if (!r) - die("unexpected wanted-ref: '%s'", reader->line); + if (i == nr_sought) + die(_("unexpected wanted-ref: '%s'"), reader->line); } if (reader->status != PACKET_READ_DELIM) - die("error processing wanted refs: %d", reader->status); + die(_("error processing wanted refs: %d"), reader->status); } enum fetch_state { @@ -1381,7 +1385,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, receive_shallow_info(args, &reader); if (process_section_header(&reader, "wanted-refs", 1)) - receive_wanted_refs(&reader, ref); + receive_wanted_refs(&reader, sought, nr_sought); /* get the pack */ process_section_header(&reader, "packfile", 0); @@ -1400,6 +1404,31 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, return ref; } +static int fetch_pack_config_cb(const char *var, const char *value, void *cb) +{ + if (strcmp(var, "fetch.fsck.skiplist") == 0) { + const char *path; + + if (git_config_pathname(&path, var, value)) + return 1; + strbuf_addf(&fsck_msg_types, "%cskiplist=%s", + fsck_msg_types.len ? ',' : '=', path); + free((char *)path); + return 0; + } + + if (skip_prefix(var, "fetch.fsck.", &var)) { + if (is_valid_msg_type(var, value)) + strbuf_addf(&fsck_msg_types, "%c%s=%s", + fsck_msg_types.len ? ',' : '=', var, value); + else + warning("Skipping unknown msg id '%s'", var); + return 0; + } + + return git_default_config(var, value, cb); +} + static void fetch_pack_config(void) { git_config_get_int("fetch.unpacklimit", &fetch_unpack_limit); @@ -1410,7 +1439,7 @@ static void fetch_pack_config(void) git_config_get_string("fetch.negotiationalgorithm", &negotiation_algorithm); - git_config(git_default_config, NULL); + git_config(fetch_pack_config_cb, NULL); } static void fetch_pack_setup(void) @@ -1448,13 +1477,12 @@ static int remove_duplicates_in_refs(struct ref **ref, int nr) } static void update_shallow(struct fetch_pack_args *args, - struct ref *refs, + struct ref **sought, int nr_sought, struct shallow_info *si) { struct oid_array ref = OID_ARRAY_INIT; int *status; int i; - struct ref *r; if (args->deepen && alternate_shallow_file) { if (*alternate_shallow_file == '\0') { /* --unshallow */ @@ -1496,8 +1524,8 @@ static void update_shallow(struct fetch_pack_args *args, remove_nonexistent_theirs_shallow(si); if (!si->nr_ours && !si->nr_theirs) return; - for (r = refs; r; r = r->next) - oid_array_append(&ref, &r->old_oid); + for (i = 0; i < nr_sought; i++) + oid_array_append(&ref, &sought[i]->old_oid); si->ref = &ref; if (args->update_shallow) { @@ -1531,12 +1559,12 @@ static void update_shallow(struct fetch_pack_args *args, * remote is also shallow, check what ref is safe to update * without updating .git/shallow */ - status = xcalloc(ref.nr, sizeof(*status)); + status = xcalloc(nr_sought, sizeof(*status)); assign_shallow_commits_to_refs(si, NULL, status); if (si->nr_ours || si->nr_theirs) { - for (r = refs, i = 0; r; r = r->next, i++) + for (i = 0; i < nr_sought; i++) if (status[i]) - r->status = REF_STATUS_REJECT_SHALLOW; + sought[i]->status = REF_STATUS_REJECT_SHALLOW; } free(status); oid_array_clear(&ref); @@ -1599,7 +1627,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args, args->connectivity_checked = 1; } - update_shallow(args, ref_cpy, &si); + update_shallow(args, sought, nr_sought, &si); cleanup: clear_shallow_info(&si); return ref_cpy; diff --git a/git-compat-util.h b/git-compat-util.h index 9a64998b24..89d37095c7 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -1239,4 +1239,10 @@ extern void unleak_memory(const void *ptr, size_t len); #define UNLEAK(var) do {} while (0) #endif +/* + * This include must come after system headers, since it introduces macros that + * replace system names. + */ +#include "banned.h" + #endif @@ -1494,7 +1494,13 @@ class P4Submit(Command, P4UserMap): optparse.make_option("--disable-p4sync", dest="disable_p4sync", action="store_true", help="Skip Perforce sync of p4/master after submit or shelve"), ] - self.description = "Submit changes from git to the perforce depot." + self.description = """Submit changes from git to the perforce depot.\n + The `p4-pre-submit` hook is executed if it exists and is executable. + The hook takes no parameters and nothing from standard input. Exiting with + non-zero status from this script prevents `git-p4 submit` from launching. + + One usage scenario is to run unit tests in the hook.""" + self.usage += " [name of git branch to submit into perforce depot]" self.origin = "" self.detectRenames = False @@ -2303,6 +2309,14 @@ class P4Submit(Command, P4UserMap): sys.exit("number of commits (%d) must match number of shelved changelist (%d)" % (len(commits), num_shelves)) + hooks_path = gitConfig("core.hooksPath") + if len(hooks_path) <= 0: + hooks_path = os.path.join(os.environ.get("GIT_DIR", ".git"), "hooks") + + hook_file = os.path.join(hooks_path, "p4-pre-submit") + if os.path.isfile(hook_file) and os.access(hook_file, os.X_OK) and subprocess.call([hook_file]) != 0: + sys.exit(1) + # # Apply the commits, one at a time. On failure, ask if should # continue to try the rest of the patches, or quit. @@ -164,7 +164,7 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) if (envchanged) *envchanged = 1; } else if (!strcmp(cmd, "--no-replace-objects")) { - check_replace_refs = 0; + read_replace_refs = 0; setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1); if (envchanged) *envchanged = 1; diff --git a/gpg-interface.c b/gpg-interface.c index 09ddfbc267..bb8ea668b3 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -7,10 +7,64 @@ #include "tempfile.h" static char *configured_signing_key; -static const char *gpg_program = "gpg"; +struct gpg_format { + const char *name; + const char *program; + const char **verify_args; + const char **sigs; +}; + +static const char *openpgp_verify_args[] = { + "--keyid-format=long", + NULL +}; +static const char *openpgp_sigs[] = { + "-----BEGIN PGP SIGNATURE-----", + "-----BEGIN PGP MESSAGE-----", + NULL +}; + +static const char *x509_verify_args[] = { + NULL +}; +static const char *x509_sigs[] = { + "-----BEGIN SIGNED MESSAGE-----", + NULL +}; -#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----" -#define PGP_MESSAGE "-----BEGIN PGP MESSAGE-----" +static struct gpg_format gpg_format[] = { + { .name = "openpgp", .program = "gpg", + .verify_args = openpgp_verify_args, + .sigs = openpgp_sigs + }, + { .name = "x509", .program = "gpgsm", + .verify_args = x509_verify_args, + .sigs = x509_sigs + }, +}; + +static struct gpg_format *use_format = &gpg_format[0]; + +static struct gpg_format *get_format_by_name(const char *str) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(gpg_format); i++) + if (!strcmp(gpg_format[i].name, str)) + return gpg_format + i; + return NULL; +} + +static struct gpg_format *get_format_by_sig(const char *sig) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(gpg_format); i++) + for (j = 0; gpg_format[i].sigs[j]; j++) + if (starts_with(sig, gpg_format[i].sigs[j])) + return gpg_format + i; + return NULL; +} void signature_check_clear(struct signature_check *sigc) { @@ -53,10 +107,11 @@ static void parse_gpg_output(struct signature_check *sigc) sigc->result = sigcheck_gpg_status[i].result; /* The trust messages are not followed by key/signer information */ if (sigc->result != 'U') { - sigc->key = xmemdupz(found, 16); + next = strchrnul(found, ' '); + sigc->key = xmemdupz(found, next - found); /* The ERRSIG message is not followed by signer information */ - if (sigc-> result != 'E') { - found += 17; + if (*next && sigc-> result != 'E') { + found = next + 1; next = strchrnul(found, '\n'); sigc->signer = xmemdupz(found, next - found); } @@ -101,12 +156,6 @@ void print_signature_buffer(const struct signature_check *sigc, unsigned flags) fputs(output, stderr); } -static int is_gpg_start(const char *line) -{ - return starts_with(line, PGP_SIGNATURE) || - starts_with(line, PGP_MESSAGE); -} - size_t parse_signature(const char *buf, size_t size) { size_t len = 0; @@ -114,7 +163,7 @@ size_t parse_signature(const char *buf, size_t size) while (len < size) { const char *eol; - if (is_gpg_start(buf + len)) + if (get_format_by_sig(buf + len)) match = len; eol = memchr(buf + len, '\n', size - len); @@ -131,6 +180,9 @@ void set_signing_key(const char *key) int git_gpg_config(const char *var, const char *value, void *cb) { + struct gpg_format *fmt = NULL; + char *fmtname = NULL; + if (!strcmp(var, "user.signingkey")) { if (!value) return config_error_nonbool(var); @@ -138,13 +190,28 @@ int git_gpg_config(const char *var, const char *value, void *cb) return 0; } - if (!strcmp(var, "gpg.program")) { + if (!strcmp(var, "gpg.format")) { if (!value) return config_error_nonbool(var); - gpg_program = xstrdup(value); + fmt = get_format_by_name(value); + if (!fmt) + return error("unsupported value for %s: %s", + var, value); + use_format = fmt; return 0; } + if (!strcmp(var, "gpg.program") || !strcmp(var, "gpg.openpgp.program")) + fmtname = "openpgp"; + + if (!strcmp(var, "gpg.x509.program")) + fmtname = "x509"; + + if (fmtname) { + fmt = get_format_by_name(fmtname); + return git_config_string(&fmt->program, var, value); + } + return 0; } @@ -163,7 +230,7 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *sig struct strbuf gpg_status = STRBUF_INIT; argv_array_pushl(&gpg.args, - gpg_program, + use_format->program, "--status-fd=2", "-bsau", signing_key, NULL); @@ -201,6 +268,7 @@ int verify_signed_buffer(const char *payload, size_t payload_size, struct strbuf *gpg_output, struct strbuf *gpg_status) { struct child_process gpg = CHILD_PROCESS_INIT; + struct gpg_format *fmt; struct tempfile *temp; int ret; struct strbuf buf = STRBUF_INIT; @@ -216,10 +284,14 @@ int verify_signed_buffer(const char *payload, size_t payload_size, return -1; } + fmt = get_format_by_sig(signature); + if (!fmt) + BUG("bad signature '%s'", signature); + + argv_array_push(&gpg.args, fmt->program); + argv_array_pushv(&gpg.args, fmt->verify_args); argv_array_pushl(&gpg.args, - gpg_program, "--status-fd=1", - "--keyid-format=long", "--verify", temp->filename.buf, "-", NULL); @@ -693,6 +693,7 @@ int cmd_version(int argc, const char **argv, const char *prefix) else printf("no commit associated with this build\n"); printf("sizeof-long: %d\n", (int)sizeof(long)); + printf("sizeof-size_t: %d\n", (int)sizeof(size_t)); /* NEEDSWORK: also save and output GIT-BUILD_OPTIONS? */ } return 0; diff --git a/http-backend.c b/http-backend.c index bd0442a805..88c38c834b 100644 --- a/http-backend.c +++ b/http-backend.c @@ -279,12 +279,18 @@ static struct rpc_service *select_service(struct strbuf *hdr, const char *name) return svc; } +static void write_to_child(int out, const unsigned char *buf, ssize_t len, const char *prog_name) +{ + if (write_in_full(out, buf, len) < 0) + die("unable to write to '%s'", prog_name); +} + /* * This is basically strbuf_read(), except that if we * hit max_request_buffer we die (we'd rather reject a * maliciously large request than chew up infinite memory). */ -static ssize_t read_request(int fd, unsigned char **out) +static ssize_t read_request_eof(int fd, unsigned char **out) { size_t len = 0, alloc = 8192; unsigned char *buf = xmalloc(alloc); @@ -321,13 +327,54 @@ static ssize_t read_request(int fd, unsigned char **out) } } -static void inflate_request(const char *prog_name, int out, int buffer_input) +static ssize_t read_request_fixed_len(int fd, ssize_t req_len, unsigned char **out) +{ + unsigned char *buf = NULL; + ssize_t cnt = 0; + + if (max_request_buffer < req_len) { + die("request was larger than our maximum size (%lu): " + "%" PRIuMAX "; try setting GIT_HTTP_MAX_REQUEST_BUFFER", + max_request_buffer, (uintmax_t)req_len); + } + + buf = xmalloc(req_len); + cnt = read_in_full(fd, buf, req_len); + if (cnt < 0) { + free(buf); + return -1; + } + *out = buf; + return cnt; +} + +static ssize_t get_content_length(void) +{ + ssize_t val = -1; + const char *str = getenv("CONTENT_LENGTH"); + + if (str && !git_parse_ssize_t(str, &val)) + die("failed to parse CONTENT_LENGTH: %s", str); + return val; +} + +static ssize_t read_request(int fd, unsigned char **out, ssize_t req_len) +{ + if (req_len < 0) + return read_request_eof(fd, out); + else + return read_request_fixed_len(fd, req_len, out); +} + +static void inflate_request(const char *prog_name, int out, int buffer_input, ssize_t req_len) { git_zstream stream; unsigned char *full_request = NULL; unsigned char in_buf[8192]; unsigned char out_buf[8192]; unsigned long cnt = 0; + int req_len_defined = req_len >= 0; + size_t req_remaining_len = req_len; memset(&stream, 0, sizeof(stream)); git_inflate_init_gzip_only(&stream); @@ -339,11 +386,18 @@ static void inflate_request(const char *prog_name, int out, int buffer_input) if (full_request) n = 0; /* nothing left to read */ else - n = read_request(0, &full_request); + n = read_request(0, &full_request, req_len); stream.next_in = full_request; } else { - n = xread(0, in_buf, sizeof(in_buf)); + ssize_t buffer_len; + if (req_len_defined && req_remaining_len <= sizeof(in_buf)) + buffer_len = req_remaining_len; + else + buffer_len = sizeof(in_buf); + n = xread(0, in_buf, buffer_len); stream.next_in = in_buf; + if (req_len_defined && n > 0) + req_remaining_len -= n; } if (n <= 0) @@ -361,9 +415,8 @@ static void inflate_request(const char *prog_name, int out, int buffer_input) die("zlib error inflating request, result %d", ret); n = stream.total_out - cnt; - if (write_in_full(out, out_buf, n) < 0) - die("%s aborted reading request", prog_name); - cnt += n; + write_to_child(out, out_buf, stream.total_out - cnt, prog_name); + cnt = stream.total_out; if (ret == Z_STREAM_END) goto done; @@ -376,18 +429,34 @@ done: free(full_request); } -static void copy_request(const char *prog_name, int out) +static void copy_request(const char *prog_name, int out, ssize_t req_len) { unsigned char *buf; - ssize_t n = read_request(0, &buf); + ssize_t n = read_request(0, &buf, req_len); if (n < 0) die_errno("error reading request body"); - if (write_in_full(out, buf, n) < 0) - die("%s aborted reading request", prog_name); + write_to_child(out, buf, n, prog_name); close(out); free(buf); } +static void pipe_fixed_length(const char *prog_name, int out, size_t req_len) +{ + unsigned char buf[8192]; + size_t remaining_len = req_len; + + while (remaining_len > 0) { + size_t chunk_length = remaining_len > sizeof(buf) ? sizeof(buf) : remaining_len; + ssize_t n = xread(0, buf, chunk_length); + if (n < 0) + die_errno("Reading request failed"); + write_to_child(out, buf, n, prog_name); + remaining_len -= n; + } + + close(out); +} + static void run_service(const char **argv, int buffer_input) { const char *encoding = getenv("HTTP_CONTENT_ENCODING"); @@ -395,6 +464,7 @@ static void run_service(const char **argv, int buffer_input) const char *host = getenv("REMOTE_ADDR"); int gzipped_request = 0; struct child_process cld = CHILD_PROCESS_INIT; + ssize_t req_len = get_content_length(); if (encoding && !strcmp(encoding, "gzip")) gzipped_request = 1; @@ -413,7 +483,7 @@ static void run_service(const char **argv, int buffer_input) "GIT_COMMITTER_EMAIL=%s@http.%s", user, host); cld.argv = argv; - if (buffer_input || gzipped_request) + if (buffer_input || gzipped_request || req_len >= 0) cld.in = -1; cld.git_cmd = 1; if (start_command(&cld)) @@ -421,9 +491,11 @@ static void run_service(const char **argv, int buffer_input) close(1); if (gzipped_request) - inflate_request(argv[0], cld.in, buffer_input); + inflate_request(argv[0], cld.in, buffer_input, req_len); else if (buffer_input) - copy_request(argv[0], cld.in); + copy_request(argv[0], cld.in, req_len); + else if (req_len >= 0) + pipe_fixed_length(argv[0], cld.in, req_len); else close(0); diff --git a/json-writer.c b/json-writer.c new file mode 100644 index 0000000000..aadb9dbddc --- /dev/null +++ b/json-writer.c @@ -0,0 +1,414 @@ +#include "cache.h" +#include "json-writer.h" + +void jw_init(struct json_writer *jw) +{ + strbuf_init(&jw->json, 0); + strbuf_init(&jw->open_stack, 0); + jw->need_comma = 0; + jw->pretty = 0; +} + +void jw_release(struct json_writer *jw) +{ + strbuf_release(&jw->json); + strbuf_release(&jw->open_stack); +} + +/* + * Append JSON-quoted version of the given string to 'out'. + */ +static void append_quoted_string(struct strbuf *out, const char *in) +{ + unsigned char c; + + strbuf_addch(out, '"'); + while ((c = *in++) != '\0') { + if (c == '"') + strbuf_addstr(out, "\\\""); + else if (c == '\\') + strbuf_addstr(out, "\\\\"); + else if (c == '\n') + strbuf_addstr(out, "\\n"); + else if (c == '\r') + strbuf_addstr(out, "\\r"); + else if (c == '\t') + strbuf_addstr(out, "\\t"); + else if (c == '\f') + strbuf_addstr(out, "\\f"); + else if (c == '\b') + strbuf_addstr(out, "\\b"); + else if (c < 0x20) + strbuf_addf(out, "\\u%04x", c); + else + strbuf_addch(out, c); + } + strbuf_addch(out, '"'); +} + +static void indent_pretty(struct json_writer *jw) +{ + int k; + + for (k = 0; k < jw->open_stack.len; k++) + strbuf_addstr(&jw->json, " "); +} + +/* + * Begin an object or array (either top-level or nested within the currently + * open object or array). + */ +static void begin(struct json_writer *jw, char ch_open, int pretty) +{ + jw->pretty = pretty; + + strbuf_addch(&jw->json, ch_open); + + strbuf_addch(&jw->open_stack, ch_open); + jw->need_comma = 0; +} + +/* + * Assert that the top of the open-stack is an object. + */ +static void assert_in_object(const struct json_writer *jw, const char *key) +{ + if (!jw->open_stack.len) + BUG("json-writer: object: missing jw_object_begin(): '%s'", key); + if (jw->open_stack.buf[jw->open_stack.len - 1] != '{') + BUG("json-writer: object: not in object: '%s'", key); +} + +/* + * Assert that the top of the open-stack is an array. + */ +static void assert_in_array(const struct json_writer *jw) +{ + if (!jw->open_stack.len) + BUG("json-writer: array: missing jw_array_begin()"); + if (jw->open_stack.buf[jw->open_stack.len - 1] != '[') + BUG("json-writer: array: not in array"); +} + +/* + * Add comma if we have already seen a member at this level. + */ +static void maybe_add_comma(struct json_writer *jw) +{ + if (jw->need_comma) + strbuf_addch(&jw->json, ','); + else + jw->need_comma = 1; +} + +static void fmt_double(struct json_writer *jw, int precision, + double value) +{ + if (precision < 0) { + strbuf_addf(&jw->json, "%f", value); + } else { + struct strbuf fmt = STRBUF_INIT; + strbuf_addf(&fmt, "%%.%df", precision); + strbuf_addf(&jw->json, fmt.buf, value); + strbuf_release(&fmt); + } +} + +static void object_common(struct json_writer *jw, const char *key) +{ + assert_in_object(jw, key); + maybe_add_comma(jw); + + if (jw->pretty) { + strbuf_addch(&jw->json, '\n'); + indent_pretty(jw); + } + + append_quoted_string(&jw->json, key); + strbuf_addch(&jw->json, ':'); + if (jw->pretty) + strbuf_addch(&jw->json, ' '); +} + +static void array_common(struct json_writer *jw) +{ + assert_in_array(jw); + maybe_add_comma(jw); + + if (jw->pretty) { + strbuf_addch(&jw->json, '\n'); + indent_pretty(jw); + } +} + +/* + * Assert that the given JSON object or JSON array has been properly + * terminated. (Has closing bracket.) + */ +static void assert_is_terminated(const struct json_writer *jw) +{ + if (jw->open_stack.len) + BUG("json-writer: object: missing jw_end(): '%s'", + jw->json.buf); +} + +void jw_object_begin(struct json_writer *jw, int pretty) +{ + begin(jw, '{', pretty); +} + +void jw_object_string(struct json_writer *jw, const char *key, const char *value) +{ + object_common(jw, key); + append_quoted_string(&jw->json, value); +} + +void jw_object_intmax(struct json_writer *jw, const char *key, intmax_t value) +{ + object_common(jw, key); + strbuf_addf(&jw->json, "%"PRIdMAX, value); +} + +void jw_object_double(struct json_writer *jw, const char *key, int precision, + double value) +{ + object_common(jw, key); + fmt_double(jw, precision, value); +} + +void jw_object_true(struct json_writer *jw, const char *key) +{ + object_common(jw, key); + strbuf_addstr(&jw->json, "true"); +} + +void jw_object_false(struct json_writer *jw, const char *key) +{ + object_common(jw, key); + strbuf_addstr(&jw->json, "false"); +} + +void jw_object_bool(struct json_writer *jw, const char *key, int value) +{ + if (value) + jw_object_true(jw, key); + else + jw_object_false(jw, key); +} + +void jw_object_null(struct json_writer *jw, const char *key) +{ + object_common(jw, key); + strbuf_addstr(&jw->json, "null"); +} + +static void increase_indent(struct strbuf *sb, + const struct json_writer *jw, + int indent) +{ + int k; + + strbuf_reset(sb); + for (k = 0; k < jw->json.len; k++) { + char ch = jw->json.buf[k]; + strbuf_addch(sb, ch); + if (ch == '\n') + strbuf_addchars(sb, ' ', indent); + } +} + +static void kill_indent(struct strbuf *sb, + const struct json_writer *jw) +{ + int k; + int eat_it = 0; + + strbuf_reset(sb); + for (k = 0; k < jw->json.len; k++) { + char ch = jw->json.buf[k]; + if (eat_it && ch == ' ') + continue; + if (ch == '\n') { + eat_it = 1; + continue; + } + eat_it = 0; + strbuf_addch(sb, ch); + } +} + +static void append_sub_jw(struct json_writer *jw, + const struct json_writer *value) +{ + /* + * If both are pretty, increase the indentation of the sub_jw + * to better fit under the super. + * + * If the super is pretty, but the sub_jw is compact, leave the + * sub_jw compact. (We don't want to parse and rebuild the sub_jw + * for this debug-ish feature.) + * + * If the super is compact, and the sub_jw is pretty, convert + * the sub_jw to compact. + * + * If both are compact, keep the sub_jw compact. + */ + if (jw->pretty && jw->open_stack.len && value->pretty) { + struct strbuf sb = STRBUF_INIT; + increase_indent(&sb, value, jw->open_stack.len * 2); + strbuf_addbuf(&jw->json, &sb); + strbuf_release(&sb); + return; + } + if (!jw->pretty && value->pretty) { + struct strbuf sb = STRBUF_INIT; + kill_indent(&sb, value); + strbuf_addbuf(&jw->json, &sb); + strbuf_release(&sb); + return; + } + + strbuf_addbuf(&jw->json, &value->json); +} + +/* + * Append existing (properly terminated) JSON sub-data (object or array) + * as-is onto the given JSON data. + */ +void jw_object_sub_jw(struct json_writer *jw, const char *key, + const struct json_writer *value) +{ + assert_is_terminated(value); + + object_common(jw, key); + append_sub_jw(jw, value); +} + +void jw_object_inline_begin_object(struct json_writer *jw, const char *key) +{ + object_common(jw, key); + + jw_object_begin(jw, jw->pretty); +} + +void jw_object_inline_begin_array(struct json_writer *jw, const char *key) +{ + object_common(jw, key); + + jw_array_begin(jw, jw->pretty); +} + +void jw_array_begin(struct json_writer *jw, int pretty) +{ + begin(jw, '[', pretty); +} + +void jw_array_string(struct json_writer *jw, const char *value) +{ + array_common(jw); + append_quoted_string(&jw->json, value); +} + +void jw_array_intmax(struct json_writer *jw, intmax_t value) +{ + array_common(jw); + strbuf_addf(&jw->json, "%"PRIdMAX, value); +} + +void jw_array_double(struct json_writer *jw, int precision, double value) +{ + array_common(jw); + fmt_double(jw, precision, value); +} + +void jw_array_true(struct json_writer *jw) +{ + array_common(jw); + strbuf_addstr(&jw->json, "true"); +} + +void jw_array_false(struct json_writer *jw) +{ + array_common(jw); + strbuf_addstr(&jw->json, "false"); +} + +void jw_array_bool(struct json_writer *jw, int value) +{ + if (value) + jw_array_true(jw); + else + jw_array_false(jw); +} + +void jw_array_null(struct json_writer *jw) +{ + array_common(jw); + strbuf_addstr(&jw->json, "null"); +} + +void jw_array_sub_jw(struct json_writer *jw, const struct json_writer *value) +{ + assert_is_terminated(value); + + array_common(jw); + append_sub_jw(jw, value); +} + +void jw_array_argc_argv(struct json_writer *jw, int argc, const char **argv) +{ + int k; + + for (k = 0; k < argc; k++) + jw_array_string(jw, argv[k]); +} + +void jw_array_argv(struct json_writer *jw, const char **argv) +{ + while (*argv) + jw_array_string(jw, *argv++); +} + +void jw_array_inline_begin_object(struct json_writer *jw) +{ + array_common(jw); + + jw_object_begin(jw, jw->pretty); +} + +void jw_array_inline_begin_array(struct json_writer *jw) +{ + array_common(jw); + + jw_array_begin(jw, jw->pretty); +} + +int jw_is_terminated(const struct json_writer *jw) +{ + return !jw->open_stack.len; +} + +void jw_end(struct json_writer *jw) +{ + char ch_open; + int len; + + if (!jw->open_stack.len) + BUG("json-writer: too many jw_end(): '%s'", jw->json.buf); + + len = jw->open_stack.len - 1; + ch_open = jw->open_stack.buf[len]; + + strbuf_setlen(&jw->open_stack, len); + jw->need_comma = 1; + + if (jw->pretty) { + strbuf_addch(&jw->json, '\n'); + indent_pretty(jw); + } + + if (ch_open == '{') + strbuf_addch(&jw->json, '}'); + else + strbuf_addch(&jw->json, ']'); +} diff --git a/json-writer.h b/json-writer.h new file mode 100644 index 0000000000..fc18acc7d9 --- /dev/null +++ b/json-writer.h @@ -0,0 +1,105 @@ +#ifndef JSON_WRITER_H +#define JSON_WRITER_H + +/* + * JSON data structures are defined at: + * [1] http://www.ietf.org/rfc/rfc7159.txt + * [2] http://json.org/ + * + * The JSON-writer API allows one to build JSON data structures using a + * simple wrapper around a "struct strbuf" buffer. It is intended as a + * simple API to build output strings; it is not intended to be a general + * object model for JSON data. In particular, it does not re-order keys + * in an object (dictionary), it does not de-dup keys in an object, and + * it does not allow lookup or parsing of JSON data. + * + * All string values (both keys and string r-values) are properly quoted + * and escaped if they contain special characters. + * + * These routines create compact JSON data (with no unnecessary whitespace, + * newlines, or indenting). If you get an unexpected response, verify + * that you're not expecting a pretty JSON string. + * + * Both "JSON objects" (aka sets of k/v pairs) and "JSON array" can be + * constructed using a 'begin append* end' model. + * + * Nested objects and arrays can either be constructed bottom up (by + * creating sub object/arrays first and appending them to the super + * object/array) -or- by building them inline in one pass. This is a + * personal style and/or data shape choice. + * + * See t/helper/test-json-writer.c for various usage examples. + * + * LIMITATIONS: + * ============ + * + * The JSON specification [1,2] defines string values as Unicode data + * and probably UTF-8 encoded. The current json-writer API does not + * enforce this and will write any string as received. However, it will + * properly quote and backslash-escape them as necessary. It is up to + * the caller to UTF-8 encode their strings *before* passing them to this + * API. This layer should not have to try to guess the encoding or locale + * of the given strings. + */ + +struct json_writer +{ + /* + * Buffer of the in-progress JSON currently being composed. + */ + struct strbuf json; + + /* + * Simple stack of the currently open array and object forms. + * This is a string of '{' and '[' characters indicating the + * currently unterminated forms. This is used to ensure the + * properly closing character is used when popping a level and + * to know when the JSON is completely closed. + */ + struct strbuf open_stack; + + unsigned int need_comma:1; + unsigned int pretty:1; +}; + +#define JSON_WRITER_INIT { STRBUF_INIT, STRBUF_INIT, 0, 0 } + +void jw_init(struct json_writer *jw); +void jw_release(struct json_writer *jw); + +void jw_object_begin(struct json_writer *jw, int pretty); +void jw_array_begin(struct json_writer *jw, int pretty); + +void jw_object_string(struct json_writer *jw, const char *key, + const char *value); +void jw_object_intmax(struct json_writer *jw, const char *key, intmax_t value); +void jw_object_double(struct json_writer *jw, const char *key, int precision, + double value); +void jw_object_true(struct json_writer *jw, const char *key); +void jw_object_false(struct json_writer *jw, const char *key); +void jw_object_bool(struct json_writer *jw, const char *key, int value); +void jw_object_null(struct json_writer *jw, const char *key); +void jw_object_sub_jw(struct json_writer *jw, const char *key, + const struct json_writer *value); + +void jw_object_inline_begin_object(struct json_writer *jw, const char *key); +void jw_object_inline_begin_array(struct json_writer *jw, const char *key); + +void jw_array_string(struct json_writer *jw, const char *value); +void jw_array_intmax(struct json_writer *jw, intmax_t value); +void jw_array_double(struct json_writer *jw, int precision, double value); +void jw_array_true(struct json_writer *jw); +void jw_array_false(struct json_writer *jw); +void jw_array_bool(struct json_writer *jw, int value); +void jw_array_null(struct json_writer *jw); +void jw_array_sub_jw(struct json_writer *jw, const struct json_writer *value); +void jw_array_argc_argv(struct json_writer *jw, int argc, const char **argv); +void jw_array_argv(struct json_writer *jw, const char **argv); + +void jw_array_inline_begin_object(struct json_writer *jw); +void jw_array_inline_begin_array(struct json_writer *jw); + +int jw_is_terminated(const struct json_writer *jw); +void jw_end(struct json_writer *jw); + +#endif /* JSON_WRITER_H */ diff --git a/line-range.h b/line-range.h index 83ba3c25e8..d3c54e45aa 100644 --- a/line-range.h +++ b/line-range.h @@ -19,11 +19,11 @@ typedef const char *(*nth_line_fn_t)(void *data, long lno); -extern int parse_range_arg(const char *arg, - nth_line_fn_t nth_line_cb, - void *cb_data, long lines, long anchor, - long *begin, long *end, - const char *path); +int parse_range_arg(const char *arg, + nth_line_fn_t nth_line_cb, + void *cb_data, long lines, long anchor, + long *begin, long *end, + const char *path); /* * Scan past a range argument that could be parsed by @@ -34,6 +34,6 @@ extern int parse_range_arg(const char *arg, * NULL in case the argument is obviously malformed. */ -extern const char *skip_range_arg(const char *arg); +const char *skip_range_arg(const char *arg); #endif /* LINE_RANGE_H */ diff --git a/log-tree.c b/log-tree.c index c0ac7af7cb..7443e5fcc7 100644 --- a/log-tree.c +++ b/log-tree.c @@ -92,7 +92,7 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid, if (starts_with(refname, git_replace_ref_base)) { struct object_id original_oid; - if (!check_replace_refs) + if (!read_replace_refs) return 0; if (get_oid_hex(refname + strlen(git_replace_ref_base), &original_oid)) { diff --git a/match-trees.c b/match-trees.c index 4cdeff53e1..37653308d3 100644 --- a/match-trees.c +++ b/match-trees.c @@ -83,34 +83,43 @@ static int score_trees(const struct object_id *hash1, const struct object_id *ha int score = 0; for (;;) { - struct name_entry e1, e2; - int got_entry_from_one = tree_entry(&one, &e1); - int got_entry_from_two = tree_entry(&two, &e2); int cmp; - if (got_entry_from_one && got_entry_from_two) - cmp = base_name_entries_compare(&e1, &e2); - else if (got_entry_from_one) + if (one.size && two.size) + cmp = base_name_entries_compare(&one.entry, &two.entry); + else if (one.size) /* two lacks this entry */ cmp = -1; - else if (got_entry_from_two) + else if (two.size) /* two has more entries */ cmp = 1; else break; - if (cmp < 0) + if (cmp < 0) { /* path1 does not appear in two */ - score += score_missing(e1.mode, e1.path); - else if (cmp > 0) + score += score_missing(one.entry.mode, one.entry.path); + update_tree_entry(&one); + } else if (cmp > 0) { /* path2 does not appear in one */ - score += score_missing(e2.mode, e2.path); - else if (oidcmp(e1.oid, e2.oid)) - /* they are different */ - score += score_differs(e1.mode, e2.mode, e1.path); - else - /* same subtree or blob */ - score += score_matches(e1.mode, e2.mode, e1.path); + score += score_missing(two.entry.mode, two.entry.path); + update_tree_entry(&two); + } else { + /* path appears in both */ + if (oidcmp(one.entry.oid, two.entry.oid)) { + /* they are different */ + score += score_differs(one.entry.mode, + two.entry.mode, + one.entry.path); + } else { + /* same subtree or blob */ + score += score_matches(one.entry.mode, + two.entry.mode, + one.entry.path); + } + update_tree_entry(&one); + update_tree_entry(&two); + } } free(one_buf); free(two_buf); diff --git a/merge-recursive.c b/merge-recursive.c index 1446e92bea..bd053c70df 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -3070,10 +3070,26 @@ static int merge_content(struct merge_options *o, if (mfi.clean && was_tracked_and_matches(o, path, &mfi.oid, mfi.mode) && !df_conflict_remains) { + int pos; + struct cache_entry *ce; + output(o, 3, _("Skipped %s (merged same as existing)"), path); if (add_cacheinfo(o, mfi.mode, &mfi.oid, path, 0, (!o->call_depth && !is_dirty), 0)) return -1; + /* + * However, add_cacheinfo() will delete the old cache entry + * and add a new one. We need to copy over any skip_worktree + * flag to avoid making the file appear as if it were + * deleted by the user. + */ + pos = index_name_pos(&o->orig_index, path, strlen(path)); + ce = o->orig_index.cache[pos]; + if (ce_skip_worktree(ce)) { + pos = index_name_pos(&the_index, path, strlen(path)); + ce = the_index.cache[pos]; + ce->ce_flags |= CE_SKIP_WORKTREE; + } return mfi.clean; } @@ -51,7 +51,7 @@ int type_from_string_gently(const char *str, ssize_t len, int gentle) if (gentle) return -1; - die("invalid object type \"%s\"", str); + die(_("invalid object type \"%s\""), str); } /* @@ -170,7 +170,7 @@ void *object_as_type(struct repository *r, struct object *obj, enum object_type } else { if (!quiet) - error("object %s is a %s, not a %s", + error(_("object %s is a %s, not a %s"), oid_to_hex(&obj->oid), type_name(obj->type), type_name(type)); return NULL; @@ -230,7 +230,7 @@ struct object *parse_object_buffer(struct repository *r, const struct object_id obj = &tag->object; } } else { - warning("object %s has unknown type id %d", oid_to_hex(oid), type); + warning(_("object %s has unknown type id %d"), oid_to_hex(oid), type); obj = NULL; } return obj; @@ -263,7 +263,7 @@ struct object *parse_object(struct repository *r, const struct object_id *oid) (!obj && has_object_file(oid) && oid_object_info(r, oid, NULL) == OBJ_BLOB)) { if (check_object_signature(repl, NULL, 0, NULL) < 0) { - error("sha1 mismatch %s", oid_to_hex(oid)); + error(_("sha1 mismatch %s"), oid_to_hex(oid)); return NULL; } parse_blob_buffer(lookup_blob(r, oid), NULL, 0); @@ -274,7 +274,7 @@ struct object *parse_object(struct repository *r, const struct object_id *oid) if (buffer) { if (check_object_signature(repl, buffer, size, type_name(type)) < 0) { free(buffer); - error("sha1 mismatch %s", oid_to_hex(repl)); + error(_("sha1 mismatch %s"), oid_to_hex(repl)); return NULL; } diff --git a/packfile.h b/packfile.h index cc7eaffe1b..fa36c473ad 100644 --- a/packfile.h +++ b/packfile.h @@ -1,12 +1,12 @@ #ifndef PACKFILE_H #define PACKFILE_H +#include "cache.h" #include "oidset.h" /* in object-store.h */ struct packed_git; struct object_info; -enum object_type; /* * Generate the filename to be used for a pack file with checksum "sha1" and diff --git a/parse-options.c b/parse-options.c index 7db84227ab..3b874a83a0 100644 --- a/parse-options.c +++ b/parse-options.c @@ -660,7 +660,8 @@ int parse_options(int argc, const char **argv, const char *prefix, static int usage_argh(const struct option *opts, FILE *outfile) { const char *s; - int literal = (opts->flags & PARSE_OPT_LITERAL_ARGHELP) || !opts->argh; + int literal = (opts->flags & PARSE_OPT_LITERAL_ARGHELP) || + !opts->argh || !!strpbrk(opts->argh, "()<>[]|"); if (opts->flags & PARSE_OPT_OPTARG) if (opts->long_name) s = literal ? "[=%s]" : "[=<%s>]"; diff --git a/pkt-line.c b/pkt-line.c index a593c08aad..04d10bbd03 100644 --- a/pkt-line.c +++ b/pkt-line.c @@ -101,7 +101,7 @@ int packet_flush_gently(int fd) { packet_trace("0000", 4, 1); if (write_in_full(fd, "0000", 4) < 0) - return error("flush packet write failed"); + return error(_("flush packet write failed")); return 0; } @@ -139,7 +139,7 @@ static void format_packet(struct strbuf *out, const char *fmt, va_list args) n = out->len - orig_len; if (n > LARGE_PACKET_MAX) - die("protocol error: impossibly long line"); + die(_("protocol error: impossibly long line")); set_packet_header(&out->buf[orig_len], n); packet_trace(out->buf + orig_len + 4, n - 4, 1); @@ -155,9 +155,9 @@ static int packet_write_fmt_1(int fd, int gently, if (write_in_full(fd, buf.buf, buf.len) < 0) { if (!gently) { check_pipe(errno); - die_errno("packet write with format failed"); + die_errno(_("packet write with format failed")); } - return error("packet write with format failed"); + return error(_("packet write with format failed")); } return 0; @@ -189,21 +189,21 @@ static int packet_write_gently(const int fd_out, const char *buf, size_t size) size_t packet_size; if (size > sizeof(packet_write_buffer) - 4) - return error("packet write failed - data exceeds max packet size"); + return error(_("packet write failed - data exceeds max packet size")); packet_trace(buf, size, 1); packet_size = size + 4; set_packet_header(packet_write_buffer, packet_size); memcpy(packet_write_buffer + 4, buf, size); if (write_in_full(fd_out, packet_write_buffer, packet_size) < 0) - return error("packet write failed"); + return error(_("packet write failed")); return 0; } void packet_write(int fd_out, const char *buf, size_t size) { if (packet_write_gently(fd_out, buf, size)) - die_errno("packet write failed"); + die_errno(_("packet write failed")); } void packet_buf_write(struct strbuf *buf, const char *fmt, ...) @@ -225,7 +225,7 @@ void packet_buf_write_len(struct strbuf *buf, const char *data, size_t len) n = buf->len - orig_len; if (n > LARGE_PACKET_MAX) - die("protocol error: impossibly long line"); + die(_("protocol error: impossibly long line")); set_packet_header(&buf->buf[orig_len], n); packet_trace(data, len, 1); @@ -288,7 +288,7 @@ static int get_packet_data(int fd, char **src_buf, size_t *src_size, } else { ret = read_in_full(fd, dst, size); if (ret < 0) - die_errno("read error"); + die_errno(_("read error")); } /* And complain if we didn't get enough bytes to satisfy the read. */ @@ -296,7 +296,7 @@ static int get_packet_data(int fd, char **src_buf, size_t *src_size, if (options & PACKET_READ_GENTLE_ON_EOF) return -1; - die("The remote end hung up unexpectedly"); + die(_("the remote end hung up unexpectedly")); } return ret; @@ -324,7 +324,7 @@ enum packet_read_status packet_read_with_status(int fd, char **src_buffer, len = packet_length(linelen); if (len < 0) { - die("protocol error: bad line length character: %.4s", linelen); + die(_("protocol error: bad line length character: %.4s"), linelen); } else if (!len) { packet_trace("0000", 4, 0); *pktlen = 0; @@ -334,12 +334,12 @@ enum packet_read_status packet_read_with_status(int fd, char **src_buffer, *pktlen = 0; return PACKET_READ_DELIM; } else if (len < 4) { - die("protocol error: bad line length %d", len); + die(_("protocol error: bad line length %d"), len); } len -= 4; if ((unsigned)len >= size) - die("protocol error: bad line length %d", len); + die(_("protocol error: bad line length %d"), len); if (get_packet_data(fd, src_buffer, src_len, buffer, len, options) < 0) { *pktlen = -1; @@ -1538,7 +1538,7 @@ void format_commit_message(const struct commit *commit, } if (output_enc) { - int outsz; + size_t outsz; char *out = reencode_string_len(sb->buf, sb->len, output_enc, utf8, &outsz); if (out) diff --git a/read-cache.c b/read-cache.c index 880849fc8a..c5fabc844a 100644 --- a/read-cache.c +++ b/read-cache.c @@ -2808,10 +2808,13 @@ out: /* * Read the index file that is potentially unmerged into given - * index_state, dropping any unmerged entries. Returns true if - * the index is unmerged. Callers who want to refuse to work - * from an unmerged state can call this and check its return value, - * instead of calling read_cache(). + * index_state, dropping any unmerged entries to stage #0 (potentially + * resulting in a path appearing as both a file and a directory in the + * index; the caller is responsible to clear out the extra entries + * before writing the index to a tree). Returns true if the index is + * unmerged. Callers who want to refuse to work from an unmerged + * state can call this and check its return value, instead of calling + * read_cache(). */ int read_index_unmerged(struct index_state *istate) { @@ -2833,7 +2836,7 @@ int read_index_unmerged(struct index_state *istate) new_ce->ce_flags = create_ce_flags(0) | CE_CONFLICTED; new_ce->ce_namelen = len; new_ce->ce_mode = ce->ce_mode; - if (add_index_entry(istate, new_ce, 0)) + if (add_index_entry(istate, new_ce, ADD_CACHE_SKIP_DFCHECK)) return error("%s: cannot drop to stage #0", new_ce->name); } diff --git a/ref-filter.c b/ref-filter.c index a8def7b3a3..0bccfceff2 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -43,6 +43,7 @@ void setup_ref_filter_porcelain_msg(void) typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type; typedef enum { COMPARE_EQUAL, COMPARE_UNEQUAL, COMPARE_NONE } cmp_status; +typedef enum { SOURCE_NONE = 0, SOURCE_OBJ, SOURCE_OTHER } info_source; struct align { align_type position; @@ -62,6 +63,17 @@ struct refname_atom { int lstrip, rstrip; }; +static struct expand_data { + struct object_id oid; + enum object_type type; + unsigned long size; + off_t disk_size; + struct object_id delta_base_oid; + void *content; + + struct object_info info; +} oi, oi_deref; + /* * An atom is a valid field atom listed below, possibly prefixed with * a "*" to denote deref_tag(). @@ -75,6 +87,7 @@ struct refname_atom { static struct used_atom { const char *name; cmp_type type; + info_source source; union { char color[COLOR_MAXLEN]; struct align align; @@ -202,6 +215,30 @@ static int remote_ref_atom_parser(const struct ref_format *format, struct used_a return 0; } +static int objecttype_atom_parser(const struct ref_format *format, struct used_atom *atom, + const char *arg, struct strbuf *err) +{ + if (arg) + return strbuf_addf_ret(err, -1, _("%%(objecttype) does not take arguments")); + if (*atom->name == '*') + oi_deref.info.typep = &oi_deref.type; + else + oi.info.typep = &oi.type; + return 0; +} + +static int objectsize_atom_parser(const struct ref_format *format, struct used_atom *atom, + const char *arg, struct strbuf *err) +{ + if (arg) + return strbuf_addf_ret(err, -1, _("%%(objectsize) does not take arguments")); + if (*atom->name == '*') + oi_deref.info.sizep = &oi_deref.size; + else + oi.info.sizep = &oi.size; + return 0; +} + static int body_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg, struct strbuf *err) { @@ -382,49 +419,50 @@ static int head_atom_parser(const struct ref_format *format, struct used_atom *a static struct { const char *name; + info_source source; cmp_type cmp_type; int (*parser)(const struct ref_format *format, struct used_atom *atom, const char *arg, struct strbuf *err); } valid_atom[] = { - { "refname" , FIELD_STR, refname_atom_parser }, - { "objecttype" }, - { "objectsize", FIELD_ULONG }, - { "objectname", FIELD_STR, objectname_atom_parser }, - { "tree" }, - { "parent" }, - { "numparent", FIELD_ULONG }, - { "object" }, - { "type" }, - { "tag" }, - { "author" }, - { "authorname" }, - { "authoremail" }, - { "authordate", FIELD_TIME }, - { "committer" }, - { "committername" }, - { "committeremail" }, - { "committerdate", FIELD_TIME }, - { "tagger" }, - { "taggername" }, - { "taggeremail" }, - { "taggerdate", FIELD_TIME }, - { "creator" }, - { "creatordate", FIELD_TIME }, - { "subject", FIELD_STR, subject_atom_parser }, - { "body", FIELD_STR, body_atom_parser }, - { "trailers", FIELD_STR, trailers_atom_parser }, - { "contents", FIELD_STR, contents_atom_parser }, - { "upstream", FIELD_STR, remote_ref_atom_parser }, - { "push", FIELD_STR, remote_ref_atom_parser }, - { "symref", FIELD_STR, refname_atom_parser }, - { "flag" }, - { "HEAD", FIELD_STR, head_atom_parser }, - { "color", FIELD_STR, color_atom_parser }, - { "align", FIELD_STR, align_atom_parser }, - { "end" }, - { "if", FIELD_STR, if_atom_parser }, - { "then" }, - { "else" }, + { "refname", SOURCE_NONE, FIELD_STR, refname_atom_parser }, + { "objecttype", SOURCE_OTHER, FIELD_STR, objecttype_atom_parser }, + { "objectsize", SOURCE_OTHER, FIELD_ULONG, objectsize_atom_parser }, + { "objectname", SOURCE_OTHER, FIELD_STR, objectname_atom_parser }, + { "tree", SOURCE_OBJ }, + { "parent", SOURCE_OBJ }, + { "numparent", SOURCE_OBJ, FIELD_ULONG }, + { "object", SOURCE_OBJ }, + { "type", SOURCE_OBJ }, + { "tag", SOURCE_OBJ }, + { "author", SOURCE_OBJ }, + { "authorname", SOURCE_OBJ }, + { "authoremail", SOURCE_OBJ }, + { "authordate", SOURCE_OBJ, FIELD_TIME }, + { "committer", SOURCE_OBJ }, + { "committername", SOURCE_OBJ }, + { "committeremail", SOURCE_OBJ }, + { "committerdate", SOURCE_OBJ, FIELD_TIME }, + { "tagger", SOURCE_OBJ }, + { "taggername", SOURCE_OBJ }, + { "taggeremail", SOURCE_OBJ }, + { "taggerdate", SOURCE_OBJ, FIELD_TIME }, + { "creator", SOURCE_OBJ }, + { "creatordate", SOURCE_OBJ, FIELD_TIME }, + { "subject", SOURCE_OBJ, FIELD_STR, subject_atom_parser }, + { "body", SOURCE_OBJ, FIELD_STR, body_atom_parser }, + { "trailers", SOURCE_OBJ, FIELD_STR, trailers_atom_parser }, + { "contents", SOURCE_OBJ, FIELD_STR, contents_atom_parser }, + { "upstream", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser }, + { "push", SOURCE_NONE, FIELD_STR, remote_ref_atom_parser }, + { "symref", SOURCE_NONE, FIELD_STR, refname_atom_parser }, + { "flag", SOURCE_NONE }, + { "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser }, + { "color", SOURCE_NONE, FIELD_STR, color_atom_parser }, + { "align", SOURCE_NONE, FIELD_STR, align_atom_parser }, + { "end", SOURCE_NONE }, + { "if", SOURCE_NONE, FIELD_STR, if_atom_parser }, + { "then", SOURCE_NONE }, + { "else", SOURCE_NONE }, }; #define REF_FORMATTING_STATE_INIT { 0, NULL } @@ -500,6 +538,13 @@ static int parse_ref_filter_atom(const struct ref_format *format, REALLOC_ARRAY(used_atom, used_atom_cnt); used_atom[at].name = xmemdupz(atom, ep - atom); used_atom[at].type = valid_atom[i].cmp_type; + used_atom[at].source = valid_atom[i].source; + if (used_atom[at].source == SOURCE_OBJ) { + if (*atom == '*') + oi_deref.info.contentp = &oi_deref.content; + else + oi.info.contentp = &oi.content; + } if (arg) { arg = used_atom[at].name + (arg - atom) + 1; if (!*arg) { @@ -795,25 +840,6 @@ int verify_ref_format(struct ref_format *format) return 0; } -/* - * Given an object name, read the object data and size, and return a - * "struct object". If the object data we are returning is also borrowed - * by the "struct object" representation, set *eaten as well---it is a - * signal from parse_object_buffer to us not to free the buffer. - */ -static void *get_obj(const struct object_id *oid, struct object **obj, unsigned long *sz, int *eaten) -{ - enum object_type type; - void *buf = read_object_file(oid, &type, sz); - - if (buf) - *obj = parse_object_buffer(the_repository, oid, type, *sz, - buf, eaten); - else - *obj = NULL; - return buf; -} - static int grab_objectname(const char *name, const struct object_id *oid, struct atom_value *v, struct used_atom *atom) { @@ -834,7 +860,7 @@ static int grab_objectname(const char *name, const struct object_id *oid, } /* See grab_values */ -static void grab_common_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) +static void grab_common_values(struct atom_value *val, int deref, struct expand_data *oi) { int i; @@ -846,13 +872,13 @@ static void grab_common_values(struct atom_value *val, int deref, struct object if (deref) name++; if (!strcmp(name, "objecttype")) - v->s = type_name(obj->type); + v->s = type_name(oi->type); else if (!strcmp(name, "objectsize")) { - v->value = sz; - v->s = xstrfmt("%lu", sz); + v->value = oi->size; + v->s = xstrfmt("%lu", oi->size); } else if (deref) - grab_objectname(name, &obj->oid, v, &used_atom[i]); + grab_objectname(name, &oi->oid, v, &used_atom[i]); } } @@ -1211,7 +1237,6 @@ static void fill_missing_values(struct atom_value *val) */ static void grab_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) { - grab_common_values(val, deref, obj, buf, sz); switch (obj->type) { case OBJ_TAG: grab_tag_values(val, deref, obj, buf, sz); @@ -1435,24 +1460,36 @@ static const char *get_refname(struct used_atom *atom, struct ref_array_item *re return show_ref(&atom->u.refname, ref->refname); } -static int get_object(struct ref_array_item *ref, const struct object_id *oid, - int deref, struct object **obj, struct strbuf *err) +static int get_object(struct ref_array_item *ref, int deref, struct object **obj, + struct expand_data *oi, struct strbuf *err) { - int eaten; - int ret = 0; - unsigned long size; - void *buf = get_obj(oid, obj, &size, &eaten); - if (!buf) - ret = strbuf_addf_ret(err, -1, _("missing object %s for %s"), - oid_to_hex(oid), ref->refname); - else if (!*obj) - ret = strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"), - oid_to_hex(oid), ref->refname); - else - grab_values(ref->value, deref, *obj, buf, size); + /* parse_object_buffer() will set eaten to 0 if free() will be needed */ + int eaten = 1; + if (oi->info.contentp) { + /* We need to know that to use parse_object_buffer properly */ + oi->info.sizep = &oi->size; + oi->info.typep = &oi->type; + } + if (oid_object_info_extended(the_repository, &oi->oid, &oi->info, + OBJECT_INFO_LOOKUP_REPLACE)) + return strbuf_addf_ret(err, -1, _("missing object %s for %s"), + oid_to_hex(&oi->oid), ref->refname); + + if (oi->info.contentp) { + *obj = parse_object_buffer(the_repository, &oi->oid, oi->type, oi->size, oi->content, &eaten); + if (!obj) { + if (!eaten) + free(oi->content); + return strbuf_addf_ret(err, -1, _("parse_object_buffer failed on %s for %s"), + oid_to_hex(&oi->oid), ref->refname); + } + grab_values(ref->value, deref, *obj, oi->content, oi->size); + } + + grab_common_values(ref->value, deref, oi); if (!eaten) - free(buf); - return ret; + free(oi->content); + return 0; } /* @@ -1462,7 +1499,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) { struct object *obj; int i; - const struct object_id *tagged; + struct object_info empty = OBJECT_INFO_INIT; ref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value)); @@ -1496,6 +1533,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) refname = get_symref(atom, ref); else if (starts_with(name, "upstream")) { const char *branch_name; + v->s = ""; /* only local branches may have an upstream */ if (!skip_prefix(ref->refname, "refs/heads/", &branch_name)) @@ -1508,6 +1546,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) continue; } else if (atom->u.remote_ref.push) { const char *branch_name; + v->s = ""; if (!skip_prefix(ref->refname, "refs/heads/", &branch_name)) continue; @@ -1548,22 +1587,26 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) continue; } else if (starts_with(name, "align")) { v->handler = align_atom_handler; + v->s = ""; continue; } else if (!strcmp(name, "end")) { v->handler = end_atom_handler; + v->s = ""; continue; } else if (starts_with(name, "if")) { const char *s; - + v->s = ""; if (skip_prefix(name, "if:", &s)) v->s = xstrdup(s); v->handler = if_atom_handler; continue; } else if (!strcmp(name, "then")) { v->handler = then_atom_handler; + v->s = ""; continue; } else if (!strcmp(name, "else")) { v->handler = else_atom_handler; + v->s = ""; continue; } else continue; @@ -1576,13 +1619,20 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) for (i = 0; i < used_atom_cnt; i++) { struct atom_value *v = &ref->value[i]; - if (v->s == NULL) - break; + if (v->s == NULL && used_atom[i].source == SOURCE_NONE) + return strbuf_addf_ret(err, -1, _("missing object %s for %s"), + oid_to_hex(&ref->objectname), ref->refname); } - if (used_atom_cnt <= i) + + if (need_tagged) + oi.info.contentp = &oi.content; + if (!memcmp(&oi.info, &empty, sizeof(empty)) && + !memcmp(&oi_deref.info, &empty, sizeof(empty))) return 0; - if (get_object(ref, &ref->objectname, 0, &obj, err)) + + oi.oid = ref->objectname; + if (get_object(ref, 0, &obj, &oi, err)) return -1; /* @@ -1596,7 +1646,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) * If it is a tag object, see if we use a value that derefs * the object, and if we do grab the object it refers to. */ - tagged = &((struct tag *)obj)->tagged->oid; + oi_deref.oid = ((struct tag *)obj)->tagged->oid; /* * NEEDSWORK: This derefs tag only once, which @@ -1604,7 +1654,7 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err) * is not consistent with what deref_tag() does * which peels the onion to the core. */ - return get_object(ref, tagged, 1, &obj, err); + return get_object(ref, 1, &obj, &oi_deref, err); } /* diff --git a/reflog-walk.c b/reflog-walk.c index 3561a8b955..3a25b27d8f 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -128,7 +128,7 @@ int add_reflog_for_walk(struct reflog_walk_info *info, enum selector_type selector = SELECTOR_NONE; if (commit->object.flags & UNINTERESTING) - die ("Cannot walk reflogs for %s", name); + die("cannot walk reflogs for %s", name); branch = xstrdup(name); if (at && at[1] == '{') { @@ -153,7 +153,7 @@ int add_reflog_for_walk(struct reflog_walk_info *info, free(branch); branch = resolve_refdup("HEAD", 0, NULL, NULL); if (!branch) - die ("No current branch"); + die("no current branch"); } reflogs = read_complete_reflog(branch); @@ -189,7 +189,7 @@ int ref_resolves_to_object(const char *refname, if (flags & REF_ISBROKEN) return 0; if (!has_sha1_file(oid->hash)) { - error("%s does not point to a valid object!", refname); + error(_("%s does not point to a valid object!"), refname); return 0; } return 1; @@ -490,16 +490,24 @@ static const char *ref_rev_parse_rules[] = { NULL }; +#define NUM_REV_PARSE_RULES (ARRAY_SIZE(ref_rev_parse_rules) - 1) + +/* + * Is it possible that the caller meant full_name with abbrev_name? + * If so return a non-zero value to signal "yes"; the magnitude of + * the returned value gives the precedence used for disambiguation. + * + * If abbrev_name cannot mean full_name, return 0. + */ int refname_match(const char *abbrev_name, const char *full_name) { const char **p; const int abbrev_name_len = strlen(abbrev_name); + const int num_rules = NUM_REV_PARSE_RULES; - for (p = ref_rev_parse_rules; *p; p++) { - if (!strcmp(full_name, mkpath(*p, abbrev_name_len, abbrev_name))) { - return 1; - } - } + for (p = ref_rev_parse_rules; *p; p++) + if (!strcmp(full_name, mkpath(*p, abbrev_name_len, abbrev_name))) + return &ref_rev_parse_rules[num_rules] - p; return 0; } @@ -568,9 +576,9 @@ int expand_ref(const char *str, int len, struct object_id *oid, char **ref) if (!warn_ambiguous_refs) break; } else if ((flag & REF_ISSYMREF) && strcmp(fullref.buf, "HEAD")) { - warning("ignoring dangling symref %s.", fullref.buf); + warning(_("ignoring dangling symref %s"), fullref.buf); } else if ((flag & REF_ISBROKEN) && strchr(fullref.buf, '/')) { - warning("ignoring broken ref %s.", fullref.buf); + warning(_("ignoring broken ref %s"), fullref.buf); } } strbuf_release(&fullref); @@ -674,7 +682,7 @@ static int write_pseudoref(const char *pseudoref, const struct object_id *oid, fd = hold_lock_file_for_update_timeout(&lock, filename, 0, get_files_ref_lock_timeout_ms()); if (fd < 0) { - strbuf_addf(err, "could not open '%s' for writing: %s", + strbuf_addf(err, _("could not open '%s' for writing: %s"), filename, strerror(errno)); goto done; } @@ -684,18 +692,18 @@ static int write_pseudoref(const char *pseudoref, const struct object_id *oid, if (read_ref(pseudoref, &actual_old_oid)) { if (!is_null_oid(old_oid)) { - strbuf_addf(err, "could not read ref '%s'", + strbuf_addf(err, _("could not read ref '%s'"), pseudoref); rollback_lock_file(&lock); goto done; } } else if (is_null_oid(old_oid)) { - strbuf_addf(err, "ref '%s' already exists", + strbuf_addf(err, _("ref '%s' already exists"), pseudoref); rollback_lock_file(&lock); goto done; } else if (oidcmp(&actual_old_oid, old_oid)) { - strbuf_addf(err, "unexpected object ID when writing '%s'", + strbuf_addf(err, _("unexpected object ID when writing '%s'"), pseudoref); rollback_lock_file(&lock); goto done; @@ -703,7 +711,7 @@ static int write_pseudoref(const char *pseudoref, const struct object_id *oid, } if (write_in_full(fd, buf.buf, buf.len) < 0) { - strbuf_addf(err, "could not write to '%s'", filename); + strbuf_addf(err, _("could not write to '%s'"), filename); rollback_lock_file(&lock); goto done; } @@ -735,9 +743,9 @@ static int delete_pseudoref(const char *pseudoref, const struct object_id *old_o return -1; } if (read_ref(pseudoref, &actual_old_oid)) - die("could not read ref '%s'", pseudoref); + die(_("could not read ref '%s'"), pseudoref); if (oidcmp(&actual_old_oid, old_oid)) { - error("unexpected object ID when deleting '%s'", + error(_("unexpected object ID when deleting '%s'"), pseudoref); rollback_lock_file(&lock); return -1; @@ -868,13 +876,13 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid, if (!is_null_oid(&cb->ooid)) { oidcpy(cb->oid, noid); if (oidcmp(&cb->ooid, noid)) - warning("Log for ref %s has gap after %s.", + warning(_("log for ref %s has gap after %s"), cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822))); } else if (cb->date == cb->at_time) oidcpy(cb->oid, noid); else if (oidcmp(noid, cb->oid)) - warning("Log for ref %s unexpectedly ended on %s.", + warning(_("log for ref %s unexpectedly ended on %s"), cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822))); oidcpy(&cb->ooid, ooid); @@ -932,7 +940,7 @@ int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, in if (flags & GET_OID_QUIETLY) exit(128); else - die("Log for %s is empty.", refname); + die(_("log for %s is empty"), refname); } if (cb.found_it) return 0; @@ -1024,7 +1032,7 @@ int ref_transaction_update(struct ref_transaction *transaction, if ((new_oid && !is_null_oid(new_oid)) ? check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) : !refname_is_safe(refname)) { - strbuf_addf(err, "refusing to update ref with bad name '%s'", + strbuf_addf(err, _("refusing to update ref with bad name '%s'"), refname); return -1; } @@ -1100,7 +1108,7 @@ int refs_update_ref(struct ref_store *refs, const char *msg, } } if (ret) { - const char *str = "update_ref failed for ref '%s': %s"; + const char *str = _("update_ref failed for ref '%s': %s"); switch (onerr) { case UPDATE_REFS_MSG_ON_ERR: @@ -1842,7 +1850,7 @@ int ref_update_reject_duplicates(struct string_list *refnames, if (!cmp) { strbuf_addf(err, - "multiple updates for ref '%s' not allowed.", + _("multiple updates for ref '%s' not allowed"), refnames->items[i].string); return 1; } else if (cmp > 0) { @@ -1970,13 +1978,13 @@ int refs_verify_refname_available(struct ref_store *refs, continue; if (!refs_read_raw_ref(refs, dirname.buf, &oid, &referent, &type)) { - strbuf_addf(err, "'%s' exists; cannot create '%s'", + strbuf_addf(err, _("'%s' exists; cannot create '%s'"), dirname.buf, refname); goto cleanup; } if (extras && string_list_has_string(extras, dirname.buf)) { - strbuf_addf(err, "cannot process '%s' and '%s' at the same time", + strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"), refname, dirname.buf); goto cleanup; } @@ -2000,7 +2008,7 @@ int refs_verify_refname_available(struct ref_store *refs, string_list_has_string(skip, iter->refname)) continue; - strbuf_addf(err, "'%s' exists; cannot create '%s'", + strbuf_addf(err, _("'%s' exists; cannot create '%s'"), iter->refname, refname); ref_iterator_abort(iter); goto cleanup; @@ -2011,7 +2019,7 @@ int refs_verify_refname_available(struct ref_store *refs, extra_refname = find_descendant_ref(dirname.buf, extras, skip); if (extra_refname) - strbuf_addf(err, "cannot process '%s' and '%s' at the same time", + strbuf_addf(err, _("cannot process '%s' and '%s' at the same time"), refname, extra_refname); else ret = 0; diff --git a/refs/files-backend.c b/refs/files-backend.c index b9eb3aabe6..1f1a98e4cb 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -363,7 +363,7 @@ stat_ref: /* Follow "normalized" - ie "refs/.." symlinks by hand */ if (S_ISLNK(st.st_mode)) { strbuf_reset(&sb_contents); - if (strbuf_readlink(&sb_contents, path, 0) < 0) { + if (strbuf_readlink(&sb_contents, path, st.st_size) < 0) { if (errno == ENOENT || errno == EINVAL) /* inconsistent with lstat; retry */ goto stat_ref; @@ -134,7 +134,7 @@ void refspec_item_init_or_die(struct refspec_item *item, const char *refspec, int fetch) { if (!refspec_item_init(item, refspec, fetch)) - die("Invalid refspec '%s'", refspec); + die(_("invalid refspec '%s'"), refspec); } void refspec_item_clear(struct refspec_item *item) @@ -1689,11 +1689,18 @@ static struct ref *get_expanded_map(const struct ref *remote_refs, static const struct ref *find_ref_by_name_abbrev(const struct ref *refs, const char *name) { const struct ref *ref; + const struct ref *best_match = NULL; + int best_score = 0; + for (ref = refs; ref; ref = ref->next) { - if (refname_match(name, ref->name)) - return ref; + int score = refname_match(name, ref->name); + + if (best_score < score) { + best_match = ref; + best_score = score; + } } - return NULL; + return best_match; } struct ref *get_remote_ref(const struct ref *remote_refs, const char *name) diff --git a/replace-object.c b/replace-object.c index 801b5c1678..4ec77ce418 100644 --- a/replace-object.c +++ b/replace-object.c @@ -17,7 +17,7 @@ static int register_replace_ref(const char *refname, if (get_oid_hex(hash, &repl_obj->original.oid)) { free(repl_obj); - warning("bad replace ref name: %s", refname); + warning(_("bad replace ref name: %s"), refname); return 0; } @@ -26,7 +26,7 @@ static int register_replace_ref(const char *refname, /* Register new object */ if (oidmap_put(the_repository->objects->replace_map, repl_obj)) - die("duplicate replace ref: %s", refname); + die(_("duplicate replace ref: %s"), refname); return 0; } @@ -51,7 +51,7 @@ static void prepare_replace_object(struct repository *r) * replacement object's name (replaced recursively, if necessary). * The return value is either oid or a pointer to a * permanently-allocated value. This function always respects replace - * references, regardless of the value of check_replace_refs. + * references, regardless of the value of read_replace_refs. */ const struct object_id *do_lookup_replace_object(struct repository *r, const struct object_id *oid) @@ -69,5 +69,5 @@ const struct object_id *do_lookup_replace_object(struct repository *r, return cur; cur = &repl_obj->replacement; } - die("replace depth too high for object %s", oid_to_hex(oid)); + die(_("replace depth too high for object %s"), oid_to_hex(oid)); } diff --git a/replace-object.h b/replace-object.h index f996de3d62..9345e105dd 100644 --- a/replace-object.h +++ b/replace-object.h @@ -26,7 +26,7 @@ extern const struct object_id *do_lookup_replace_object(struct repository *r, static inline const struct object_id *lookup_replace_object(struct repository *r, const struct object_id *oid) { - if (!check_replace_refs || + if (!read_replace_refs || (r->objects->replace_map && r->objects->replace_map->map.tablesize == 0)) return oid; diff --git a/repository.h b/repository.h index b9413be50c..58961037c4 100644 --- a/repository.h +++ b/repository.h @@ -108,19 +108,16 @@ struct set_gitdir_args { const char *alternate_db; }; -extern void repo_set_gitdir(struct repository *repo, - const char *root, - const struct set_gitdir_args *extra_args); -extern void repo_set_worktree(struct repository *repo, const char *path); -extern void repo_set_hash_algo(struct repository *repo, int algo); -extern void initialize_the_repository(void); -extern int repo_init(struct repository *r, - const char *gitdir, - const char *worktree); -extern int repo_submodule_init(struct repository *submodule, - struct repository *superproject, - const char *path); -extern void repo_clear(struct repository *repo); +void repo_set_gitdir(struct repository *repo, const char *root, + const struct set_gitdir_args *extra_args); +void repo_set_worktree(struct repository *repo, const char *path); +void repo_set_hash_algo(struct repository *repo, int algo); +void initialize_the_repository(void); +int repo_init(struct repository *r, const char *gitdir, const char *worktree); +int repo_submodule_init(struct repository *submodule, + struct repository *superproject, + const char *path); +void repo_clear(struct repository *repo); /* * Populates the repository's index from its index_file, an index struct will @@ -130,6 +127,6 @@ extern void repo_clear(struct repository *repo); * than zero if an error occured. If the repository's index has already been * populated then the number of entries will simply be returned. */ -extern int repo_read_index(struct repository *repo); +int repo_read_index(struct repository *repo); #endif /* REPOSITORY_H */ @@ -22,19 +22,19 @@ struct rerere_id { int variant; }; -extern int setup_rerere(struct string_list *, int); -extern int rerere(int); +int setup_rerere(struct string_list *, int); +int rerere(int); /* * Given the conflict ID and the name of a "file" used for replaying * the recorded resolution (e.g. "preimage", "postimage"), return the * path to that filesystem entity. With "file" specified with NULL, * return the path to the directory that houses these files. */ -extern const char *rerere_path(const struct rerere_id *, const char *file); -extern int rerere_forget(struct pathspec *); -extern int rerere_remaining(struct string_list *); -extern void rerere_clear(struct string_list *); -extern void rerere_gc(struct string_list *); +const char *rerere_path(const struct rerere_id *, const char *file); +int rerere_forget(struct pathspec *); +int rerere_remaining(struct string_list *); +void rerere_clear(struct string_list *); +void rerere_gc(struct string_list *); #define OPT_RERERE_AUTOUPDATE(v) OPT_UYN(0, "rerere-autoupdate", (v), \ N_("update the index with reused conflict resolution if possible")) diff --git a/revision.h b/revision.h index c599c34da9..51bb57d889 100644 --- a/revision.h +++ b/revision.h @@ -230,7 +230,7 @@ struct rev_info { struct revision_sources *sources; }; -extern int ref_excluded(struct string_list *, const char *path); +int ref_excluded(struct string_list *, const char *path); void clear_ref_exclusion(struct string_list **); void add_ref_exclusion(struct string_list **, const char *exclude); @@ -252,39 +252,39 @@ struct setup_revision_opt { unsigned revarg_opt; }; -extern void init_revisions(struct rev_info *revs, const char *prefix); -extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, - struct setup_revision_opt *); -extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx, - const struct option *options, - const char * const usagestr[]); +void init_revisions(struct rev_info *revs, const char *prefix); +int setup_revisions(int argc, const char **argv, struct rev_info *revs, + struct setup_revision_opt *); +void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx, + const struct option *options, + const char * const usagestr[]); #define REVARG_CANNOT_BE_FILENAME 01 #define REVARG_COMMITTISH 02 -extern int handle_revision_arg(const char *arg, struct rev_info *revs, - int flags, unsigned revarg_opt); +int handle_revision_arg(const char *arg, struct rev_info *revs, + int flags, unsigned revarg_opt); -extern void reset_revision_walk(void); -extern int prepare_revision_walk(struct rev_info *revs); -extern struct commit *get_revision(struct rev_info *revs); -extern char *get_revision_mark(const struct rev_info *revs, - const struct commit *commit); -extern void put_revision_mark(const struct rev_info *revs, - const struct commit *commit); +void reset_revision_walk(void); +int prepare_revision_walk(struct rev_info *revs); +struct commit *get_revision(struct rev_info *revs); +char *get_revision_mark(const struct rev_info *revs, + const struct commit *commit); +void put_revision_mark(const struct rev_info *revs, + const struct commit *commit); -extern void mark_parents_uninteresting(struct commit *commit); -extern void mark_tree_uninteresting(struct tree *tree); +void mark_parents_uninteresting(struct commit *commit); +void mark_tree_uninteresting(struct tree *tree); -extern void show_object_with_name(FILE *, struct object *, const char *); +void show_object_with_name(FILE *, struct object *, const char *); -extern void add_pending_object(struct rev_info *revs, - struct object *obj, const char *name); -extern void add_pending_oid(struct rev_info *revs, - const char *name, const struct object_id *oid, - unsigned int flags); +void add_pending_object(struct rev_info *revs, + struct object *obj, const char *name); +void add_pending_oid(struct rev_info *revs, + const char *name, const struct object_id *oid, + unsigned int flags); -extern void add_head_to_pending(struct rev_info *); -extern void add_reflogs_to_pending(struct rev_info *, unsigned int flags); -extern void add_index_objects_to_pending(struct rev_info *, unsigned int flags); +void add_head_to_pending(struct rev_info *); +void add_reflogs_to_pending(struct rev_info *, unsigned int flags); +void add_index_objects_to_pending(struct rev_info *, unsigned int flags); enum commit_action { commit_ignore, @@ -292,10 +292,10 @@ enum commit_action { commit_error }; -extern enum commit_action get_commit_action(struct rev_info *revs, - struct commit *commit); -extern enum commit_action simplify_commit(struct rev_info *revs, - struct commit *commit); +enum commit_action get_commit_action(struct rev_info *revs, + struct commit *commit); +enum commit_action simplify_commit(struct rev_info *revs, + struct commit *commit); enum rewrite_result { rewrite_one_ok, @@ -305,8 +305,9 @@ enum rewrite_result { typedef enum rewrite_result (*rewrite_parent_fn_t)(struct rev_info *revs, struct commit **pp); -extern int rewrite_parents(struct rev_info *revs, struct commit *commit, - rewrite_parent_fn_t rewrite_parent); +int rewrite_parents(struct rev_info *revs, + struct commit *commit, + rewrite_parent_fn_t rewrite_parent); /* * The log machinery saves the original parent list so that @@ -317,6 +318,6 @@ extern int rewrite_parents(struct rev_info *revs, struct commit *commit, * get_saved_parents() will transparently return commit->parents if * history simplification is off. */ -extern struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit); +struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit); #endif diff --git a/sequencer.c b/sequencer.c index 31038472fd..b8f274bdec 100644 --- a/sequencer.c +++ b/sequencer.c @@ -307,7 +307,7 @@ static const char *action_name(const struct replay_opts *opts) case REPLAY_INTERACTIVE_REBASE: return N_("rebase -i"); } - die(_("Unknown action: %d"), opts->action); + die(_("unknown action: %d"), opts->action); } struct commit_message { @@ -654,6 +654,7 @@ missing_author: strbuf_addch(&buf, *(message++)); else strbuf_addf(&buf, "'\\\\%c'", *(message++)); + strbuf_addch(&buf, '\''); res = write_message(buf.buf, buf.len, rebase_path_author_script(), 1); strbuf_release(&buf); return res; @@ -708,43 +709,51 @@ static const char *read_author_ident(struct strbuf *buf) const char *keys[] = { "GIT_AUTHOR_NAME=", "GIT_AUTHOR_EMAIL=", "GIT_AUTHOR_DATE=" }; - char *in, *out, *eol; - int i = 0, len; + struct strbuf out = STRBUF_INIT; + char *in, *eol; + const char *val[3]; + int i = 0; if (strbuf_read_file(buf, rebase_path_author_script(), 256) <= 0) return NULL; /* dequote values and construct ident line in-place */ - for (in = out = buf->buf; i < 3 && in - buf->buf < buf->len; i++) { + for (in = buf->buf; i < 3 && in - buf->buf < buf->len; i++) { if (!skip_prefix(in, keys[i], (const char **)&in)) { - warning("could not parse '%s' (looking for '%s'", + warning(_("could not parse '%s' (looking for '%s'"), rebase_path_author_script(), keys[i]); return NULL; } eol = strchrnul(in, '\n'); *eol = '\0'; - sq_dequote(in); - len = strlen(in); - - if (i > 0) /* separate values by spaces */ - *(out++) = ' '; - if (i == 1) /* email needs to be surrounded by <...> */ - *(out++) = '<'; - memmove(out, in, len); - out += len; - if (i == 1) /* email needs to be surrounded by <...> */ - *(out++) = '>'; + if (!sq_dequote(in)) { + warning(_("bad quoting on %s value in '%s'"), + keys[i], rebase_path_author_script()); + return NULL; + } + val[i] = in; in = eol + 1; } if (i < 3) { - warning("could not parse '%s' (looking for '%s')", + warning(_("could not parse '%s' (looking for '%s')"), rebase_path_author_script(), keys[i]); return NULL; } - buf->len = out - buf->buf; + /* validate date since fmt_ident() will die() on bad value */ + if (parse_date(val[2], &out)){ + warning(_("invalid date format '%s' in '%s'"), + val[2], rebase_path_author_script()); + strbuf_release(&out); + return NULL; + } + + strbuf_reset(&out); + strbuf_addstr(&out, fmt_ident(val[0], val[1], val[2], 0)); + strbuf_swap(buf, &out); + strbuf_release(&out); return buf->buf; } @@ -1445,7 +1454,7 @@ static const char *command_to_string(const enum todo_command command) { if (command < TODO_COMMENT) return todo_command_info[command].str; - die("Unknown command: %d", command); + die(_("unknown command: %d"), command); } static char command_to_char(const enum todo_command command) @@ -2608,15 +2617,17 @@ static int error_with_patch(struct commit *commit, if (intend_to_amend()) return -1; - fprintf(stderr, "You can amend the commit now, with\n" - "\n" - " git commit --amend %s\n" - "\n" - "Once you are satisfied with your changes, run\n" - "\n" - " git rebase --continue\n", gpg_sign_opt_quoted(opts)); + fprintf(stderr, + _("You can amend the commit now, with\n" + "\n" + " git commit --amend %s\n" + "\n" + "Once you are satisfied with your changes, run\n" + "\n" + " git rebase --continue\n"), + gpg_sign_opt_quoted(opts)); } else if (exit_code) - fprintf(stderr, "Could not apply %s... %.*s\n", + fprintf_ln(stderr, _("Could not apply %s... %.*s"), short_commit_name(commit), subject_len, subject); return exit_code; @@ -2730,7 +2741,7 @@ static int do_label(const char *name, int len) struct object_id head_oid; if (len == 1 && *name == '#') - return error("Illegal label name: '%.*s'", len, name); + return error(_("illegal label name: '%.*s'"), len, name); strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name); strbuf_addf(&msg, "rebase -i (label) '%.*s'", len, name); @@ -4244,10 +4255,9 @@ int sequencer_add_exec_commands(const char *commands) { const char *todo_file = rebase_path_todo(); struct todo_list todo_list = TODO_LIST_INIT; - struct todo_item *item; struct strbuf *buf = &todo_list.buf; size_t offset = 0, commands_len = strlen(commands); - int i, first; + int i, insert; if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0) return error(_("could not read '%s'."), todo_file); @@ -4257,19 +4267,40 @@ int sequencer_add_exec_commands(const char *commands) return error(_("unusable todo list: '%s'"), todo_file); } - first = 1; - /* insert <commands> before every pick except the first one */ - for (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) { - if (item->command == TODO_PICK && !first) { - strbuf_insert(buf, item->offset_in_buf + offset, - commands, commands_len); + /* + * Insert <commands> after every pick. Here, fixup/squash chains + * are considered part of the pick, so we insert the commands *after* + * those chains if there are any. + */ + insert = -1; + for (i = 0; i < todo_list.nr; i++) { + enum todo_command command = todo_list.items[i].command; + + if (insert >= 0) { + /* skip fixup/squash chains */ + if (command == TODO_COMMENT) + continue; + else if (is_fixup(command)) { + insert = i + 1; + continue; + } + strbuf_insert(buf, + todo_list.items[insert].offset_in_buf + + offset, commands, commands_len); offset += commands_len; + insert = -1; } - first = 0; + + if (command == TODO_PICK || command == TODO_MERGE) + insert = i + 1; } - /* append final <commands> */ - strbuf_add(buf, commands, commands_len); + /* insert or append final <commands> */ + if (insert >= 0 && insert < todo_list.nr) + strbuf_insert(buf, todo_list.items[insert].offset_in_buf + + offset, commands, commands_len); + else if (insert >= 0 || !offset) + strbuf_add(buf, commands, commands_len); i = write_message(buf->buf, buf->len, todo_file, 0); todo_list_release(&todo_list); diff --git a/sha1-file.c b/sha1-file.c index dfa8a35d68..c6ca960eb2 100644 --- a/sha1-file.c +++ b/sha1-file.c @@ -71,17 +71,17 @@ static void git_hash_sha1_final(unsigned char *hash, git_hash_ctx *ctx) static void git_hash_unknown_init(git_hash_ctx *ctx) { - die("trying to init unknown hash"); + BUG("trying to init unknown hash"); } static void git_hash_unknown_update(git_hash_ctx *ctx, const void *data, size_t len) { - die("trying to update unknown hash"); + BUG("trying to update unknown hash"); } static void git_hash_unknown_final(unsigned char *hash, git_hash_ctx *ctx) { - die("trying to finalize unknown hash"); + BUG("trying to finalize unknown hash"); } const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = { @@ -378,8 +378,8 @@ static int alt_odb_usable(struct raw_object_store *o, /* Detect cases where alternate disappeared */ if (!is_directory(path->buf)) { - error("object directory %s does not exist; " - "check .git/objects/info/alternates.", + error(_("object directory %s does not exist; " + "check .git/objects/info/alternates"), path->buf); return 0; } @@ -429,7 +429,7 @@ static int link_alt_odb_entry(struct repository *r, const char *entry, strbuf_addstr(&pathbuf, entry); if (strbuf_normalize_path(&pathbuf) < 0 && relative_base) { - error("unable to normalize alternate object path: %s", + error(_("unable to normalize alternate object path: %s"), pathbuf.buf); strbuf_release(&pathbuf); return -1; @@ -500,14 +500,14 @@ static void link_alt_odb_entries(struct repository *r, const char *alt, return; if (depth > 5) { - error("%s: ignoring alternate object stores, nesting too deep.", + error(_("%s: ignoring alternate object stores, nesting too deep"), relative_base); return; } strbuf_add_absolute_path(&objdirbuf, r->objects->objectdir); if (strbuf_normalize_path(&objdirbuf) < 0) - die("unable to normalize object directory: %s", + die(_("unable to normalize object directory: %s"), objdirbuf.buf); while (*alt) { @@ -562,7 +562,7 @@ void add_to_alternates_file(const char *reference) hold_lock_file_for_update(&lock, alts, LOCK_DIE_ON_ERROR); out = fdopen_lock_file(&lock, "w"); if (!out) - die_errno("unable to fdopen alternates lockfile"); + die_errno(_("unable to fdopen alternates lockfile")); in = fopen(alts, "r"); if (in) { @@ -580,14 +580,14 @@ void add_to_alternates_file(const char *reference) fclose(in); } else if (errno != ENOENT) - die_errno("unable to read alternates file"); + die_errno(_("unable to read alternates file")); if (found) { rollback_lock_file(&lock); } else { fprintf_or_die(out, "%s\n", reference); if (commit_lock_file(&lock)) - die_errno("unable to move new alternates file into place"); + die_errno(_("unable to move new alternates file into place")); if (the_repository->objects->alt_odb_tail) link_alt_odb_entries(the_repository, reference, '\n', NULL, 0); @@ -778,7 +778,7 @@ static void mmap_limit_check(size_t length) limit = SIZE_MAX; } if (length > limit) - die("attempting to mmap %"PRIuMAX" over limit %"PRIuMAX, + die(_("attempting to mmap %"PRIuMAX" over limit %"PRIuMAX), (uintmax_t)length, (uintmax_t)limit); } @@ -803,7 +803,7 @@ void *xmmap(void *start, size_t length, { void *ret = xmmap_gently(start, length, prot, flags, fd, offset); if (ret == MAP_FAILED) - die_errno("mmap failed"); + die_errno(_("mmap failed")); return ret; } @@ -970,7 +970,7 @@ static void *map_sha1_file_1(struct repository *r, const char *path, *size = xsize_t(st.st_size); if (!*size) { /* mmap() is forbidden on empty files */ - error("object file %s is empty", path); + error(_("object file %s is empty"), path); return NULL; } map = xmmap(NULL, *size, PROT_READ, MAP_PRIVATE, fd, 0); @@ -1090,9 +1090,9 @@ static void *unpack_sha1_rest(git_zstream *stream, void *buffer, unsigned long s } if (status < 0) - error("corrupt loose object '%s'", sha1_to_hex(sha1)); + error(_("corrupt loose object '%s'"), sha1_to_hex(sha1)); else if (stream->avail_in) - error("garbage at end of loose object '%s'", + error(_("garbage at end of loose object '%s'"), sha1_to_hex(sha1)); free(buf); return NULL; @@ -1134,7 +1134,7 @@ static int parse_sha1_header_extended(const char *hdr, struct object_info *oi, if ((flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE) && (type < 0)) type = 0; else if (type < 0) - die("invalid object type"); + die(_("invalid object type")); if (oi->typep) *oi->typep = type; @@ -1216,19 +1216,19 @@ static int sha1_loose_object_info(struct repository *r, *oi->disk_sizep = mapsize; if ((flags & OBJECT_INFO_ALLOW_UNKNOWN_TYPE)) { if (unpack_sha1_header_to_strbuf(&stream, map, mapsize, hdr, sizeof(hdr), &hdrbuf) < 0) - status = error("unable to unpack %s header with --allow-unknown-type", + status = error(_("unable to unpack %s header with --allow-unknown-type"), sha1_to_hex(sha1)); } else if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0) - status = error("unable to unpack %s header", + status = error(_("unable to unpack %s header"), sha1_to_hex(sha1)); if (status < 0) ; /* Do nothing */ else if (hdrbuf.len) { if ((status = parse_sha1_header_extended(hdrbuf.buf, oi, flags)) < 0) - status = error("unable to parse %s header with --allow-unknown-type", + status = error(_("unable to parse %s header with --allow-unknown-type"), sha1_to_hex(sha1)); } else if ((status = parse_sha1_header_extended(hdr, oi, flags)) < 0) - status = error("unable to parse %s header", sha1_to_hex(sha1)); + status = error(_("unable to parse %s header"), sha1_to_hex(sha1)); if (status >= 0 && oi->contentp) { *oi->contentp = unpack_sha1_rest(&stream, hdr, @@ -1419,19 +1419,19 @@ void *read_object_file_extended(const struct object_id *oid, return data; if (errno && errno != ENOENT) - die_errno("failed to read object %s", oid_to_hex(oid)); + die_errno(_("failed to read object %s"), oid_to_hex(oid)); /* die if we replaced an object with one that does not exist */ if (repl != oid) - die("replacement %s not found for %s", + die(_("replacement %s not found for %s"), oid_to_hex(repl), oid_to_hex(oid)); if (!stat_sha1_file(the_repository, repl->hash, &st, &path)) - die("loose object %s (stored in %s) is corrupt", + die(_("loose object %s (stored in %s) is corrupt"), oid_to_hex(repl), path); if ((p = has_packed_and_bad(repl->hash)) != NULL) - die("packed object %s (stored in %s) is corrupt", + die(_("packed object %s (stored in %s) is corrupt"), oid_to_hex(repl), p->pack_name); return NULL; @@ -1533,21 +1533,21 @@ int finalize_object_file(const char *tmpfile, const char *filename) unlink_or_warn(tmpfile); if (ret) { if (ret != EEXIST) { - return error_errno("unable to write sha1 filename %s", filename); + return error_errno(_("unable to write sha1 filename %s"), filename); } /* FIXME!!! Collision check here ? */ } out: if (adjust_shared_perm(filename)) - return error("unable to set permission to '%s'", filename); + return error(_("unable to set permission to '%s'"), filename); return 0; } static int write_buffer(int fd, const void *buf, size_t len) { if (write_in_full(fd, buf, len) < 0) - return error_errno("file write error"); + return error_errno(_("file write error")); return 0; } @@ -1566,7 +1566,7 @@ static void close_sha1_file(int fd) if (fsync_object_files) fsync_or_die(fd, "sha1 file"); if (close(fd) != 0) - die_errno("error when closing sha1 file"); + die_errno(_("error when closing sha1 file")); } /* Size of directory component, including the ending '/' */ @@ -1632,9 +1632,9 @@ static int write_loose_object(const struct object_id *oid, char *hdr, fd = create_tmpfile(&tmp_file, filename.buf); if (fd < 0) { if (errno == EACCES) - return error("insufficient permission for adding an object to repository database %s", get_object_directory()); + return error(_("insufficient permission for adding an object to repository database %s"), get_object_directory()); else - return error_errno("unable to create temporary file"); + return error_errno(_("unable to create temporary file")); } /* Set it up */ @@ -1658,21 +1658,21 @@ static int write_loose_object(const struct object_id *oid, char *hdr, ret = git_deflate(&stream, Z_FINISH); the_hash_algo->update_fn(&c, in0, stream.next_in - in0); if (write_buffer(fd, compressed, stream.next_out - compressed) < 0) - die("unable to write sha1 file"); + die(_("unable to write sha1 file")); stream.next_out = compressed; stream.avail_out = sizeof(compressed); } while (ret == Z_OK); if (ret != Z_STREAM_END) - die("unable to deflate new object %s (%d)", oid_to_hex(oid), + die(_("unable to deflate new object %s (%d)"), oid_to_hex(oid), ret); ret = git_deflate_end_gently(&stream); if (ret != Z_OK) - die("deflateEnd on object %s failed (%d)", oid_to_hex(oid), + die(_("deflateEnd on object %s failed (%d)"), oid_to_hex(oid), ret); the_hash_algo->final_fn(parano_oid.hash, &c); if (oidcmp(oid, ¶no_oid) != 0) - die("confused by unstable object source data for %s", + die(_("confused by unstable object source data for %s"), oid_to_hex(oid)); close_sha1_file(fd); @@ -1682,7 +1682,7 @@ static int write_loose_object(const struct object_id *oid, char *hdr, utb.actime = mtime; utb.modtime = mtime; if (utime(tmp_file.buf, &utb) < 0) - warning_errno("failed utime() on %s", tmp_file.buf); + warning_errno(_("failed utime() on %s"), tmp_file.buf); } return finalize_object_file(tmp_file.buf, filename.buf); @@ -1757,7 +1757,7 @@ int force_object_loose(const struct object_id *oid, time_t mtime) return 0; buf = read_object(oid->hash, &type, &len); if (!buf) - return error("cannot read sha1_file for %s", oid_to_hex(oid)); + return error(_("cannot read sha1_file for %s"), oid_to_hex(oid)); hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", type_name(type), len) + 1; ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime); free(buf); @@ -1802,7 +1802,7 @@ static void check_commit(const void *buf, size_t size) struct commit c; memset(&c, 0, sizeof(c)); if (parse_commit_buffer(the_repository, &c, buf, size, 0)) - die("corrupt commit"); + die(_("corrupt commit")); } static void check_tag(const void *buf, size_t size) @@ -1810,7 +1810,7 @@ static void check_tag(const void *buf, size_t size) struct tag t; memset(&t, 0, sizeof(t)); if (parse_tag_buffer(the_repository, &t, buf, size)) - die("corrupt tag"); + die(_("corrupt tag")); } static int index_mem(struct object_id *oid, void *buf, size_t size, @@ -1903,10 +1903,10 @@ static int index_core(struct object_id *oid, int fd, size_t size, char *buf = xmalloc(size); ssize_t read_result = read_in_full(fd, buf, size); if (read_result < 0) - ret = error_errno("read error while indexing %s", + ret = error_errno(_("read error while indexing %s"), path ? path : "<unknown>"); else if (read_result != size) - ret = error("short read while indexing %s", + ret = error(_("short read while indexing %s"), path ? path : "<unknown>"); else ret = index_mem(oid, buf, size, type, path, flags); @@ -1977,7 +1977,7 @@ int index_path(struct object_id *oid, const char *path, struct stat *st, unsigne if (fd < 0) return error_errno("open(\"%s\")", path); if (index_fd(oid, fd, st, OBJ_BLOB, path, flags) < 0) - return error("%s: failed to insert into database", + return error(_("%s: failed to insert into database"), path); break; case S_IFLNK: @@ -1986,13 +1986,13 @@ int index_path(struct object_id *oid, const char *path, struct stat *st, unsigne if (!(flags & HASH_WRITE_OBJECT)) hash_object_file(sb.buf, sb.len, blob_type, oid); else if (write_object_file(sb.buf, sb.len, blob_type, oid)) - rc = error("%s: failed to insert into database", path); + rc = error(_("%s: failed to insert into database"), path); strbuf_release(&sb); break; case S_IFDIR: return resolve_gitlink_ref(path, "HEAD", oid); default: - return error("%s: unsupported file type", path); + return error(_("%s: unsupported file type"), path); } return rc; } @@ -2016,9 +2016,9 @@ void assert_oid_type(const struct object_id *oid, enum object_type expect) { enum object_type type = oid_object_info(the_repository, oid, NULL); if (type < 0) - die("%s is not a valid object", oid_to_hex(oid)); + die(_("%s is not a valid object"), oid_to_hex(oid)); if (type != expect) - die("%s is not a valid '%s' object", oid_to_hex(oid), + die(_("%s is not a valid '%s' object"), oid_to_hex(oid), type_name(expect)); } @@ -2045,7 +2045,7 @@ int for_each_file_in_obj_subdir(unsigned int subdir_nr, dir = opendir(path->buf); if (!dir) { if (errno != ENOENT) - r = error_errno("unable to open %s", path->buf); + r = error_errno(_("unable to open %s"), path->buf); strbuf_setlen(path, origlen); return r; } @@ -2202,18 +2202,18 @@ static int check_stream_sha1(git_zstream *stream, git_inflate_end(stream); if (status != Z_STREAM_END) { - error("corrupt loose object '%s'", sha1_to_hex(expected_sha1)); + error(_("corrupt loose object '%s'"), sha1_to_hex(expected_sha1)); return -1; } if (stream->avail_in) { - error("garbage at end of loose object '%s'", + error(_("garbage at end of loose object '%s'"), sha1_to_hex(expected_sha1)); return -1; } the_hash_algo->final_fn(real_sha1, &c); if (hashcmp(expected_sha1, real_sha1)) { - error("sha1 mismatch for %s (expected %s)", path, + error(_("sha1 mismatch for %s (expected %s)"), path, sha1_to_hex(expected_sha1)); return -1; } @@ -2237,18 +2237,18 @@ int read_loose_object(const char *path, map = map_sha1_file_1(the_repository, path, NULL, &mapsize); if (!map) { - error_errno("unable to mmap %s", path); + error_errno(_("unable to mmap %s"), path); goto out; } if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0) { - error("unable to unpack header of %s", path); + error(_("unable to unpack header of %s"), path); goto out; } *type = parse_sha1_header(hdr, size); if (*type < 0) { - error("unable to parse header of %s", path); + error(_("unable to parse header of %s"), path); git_inflate_end(&stream); goto out; } @@ -2259,13 +2259,13 @@ int read_loose_object(const char *path, } else { *contents = unpack_sha1_rest(&stream, hdr, *size, expected_oid->hash); if (!*contents) { - error("unable to unpack contents of %s", path); + error(_("unable to unpack contents of %s"), path); git_inflate_end(&stream); goto out; } if (check_object_signature(expected_oid, *contents, *size, type_name(*type))) { - error("sha1 mismatch for %s (expected %s)", path, + error(_("sha1 mismatch for %s (expected %s)"), path, oid_to_hex(expected_oid)); free(*contents); goto out; diff --git a/sha1collisiondetection b/sha1collisiondetection -Subproject 19d97bf5af05312267c2e874ee6bcf584d9e968 +Subproject 232357eb2ea0397388254a4b188333a227bf5b1 diff --git a/sha1dc/sha1.c b/sha1dc/sha1.c index 25eded1399..df0630bc6d 100644 --- a/sha1dc/sha1.c +++ b/sha1dc/sha1.c @@ -93,13 +93,23 @@ #define SHA1DC_BIGENDIAN /* Not under GCC-alike or glibc or *BSD or newlib or <processor whitelist> */ +#elif (defined(_AIX)) + +/* + * Defines Big Endian on a whitelist of OSs that are known to be Big + * Endian-only. See + * https://public-inbox.org/git/93056823-2740-d072-1ebd-46b440b33d7e@felt.demon.nl/ + */ +#define SHA1DC_BIGENDIAN + +/* Not under GCC-alike or glibc or *BSD or newlib or <processor whitelist> or <os whitelist> */ #elif defined(SHA1DC_ON_INTEL_LIKE_PROCESSOR) /* * As a last resort before we do anything else we're not 100% sure * about below, we blacklist specific processors here. We could add * more, see e.g. https://wiki.debian.org/ArchitectureSpecificsMemo */ -#else /* Not under GCC-alike or glibc or *BSD or newlib or <processor whitelist> or <processor blacklist> */ +#else /* Not under GCC-alike or glibc or *BSD or newlib or <processor whitelist> or <os whitelist> or <processor blacklist> */ /* We do nothing more here for now */ /*#error "Uncomment this to see if you fall through all the detection"*/ @@ -134,7 +134,7 @@ void strbuf_ltrim(struct strbuf *sb) int strbuf_reencode(struct strbuf *sb, const char *from, const char *to) { char *out; - int len; + size_t len; if (same_encoding(from, to)) return 0; @@ -209,7 +209,7 @@ void strbuf_list_free(struct strbuf **sbs) int strbuf_cmp(const struct strbuf *a, const struct strbuf *b) { - int len = a->len < b->len ? a->len: b->len; + size_t len = a->len < b->len ? a->len: b->len; int cmp = memcmp(a->buf, b->buf, len); if (cmp) return cmp; @@ -389,7 +389,7 @@ size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src) { - int i, len = src->len; + size_t i, len = src->len; for (i = 0; i < len; i++) { if (src->buf[i] == '%') @@ -469,7 +469,7 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint) hint = 32; while (hint < STRBUF_MAXLINK) { - int len; + ssize_t len; strbuf_grow(sb, hint); len = readlink(path, sb->buf, hint); @@ -734,18 +734,18 @@ void strbuf_humanise_bytes(struct strbuf *buf, off_t bytes) { if (bytes > 1 << 30) { strbuf_addf(buf, "%u.%2.2u GiB", - (int)(bytes >> 30), - (int)(bytes & ((1 << 30) - 1)) / 10737419); + (unsigned)(bytes >> 30), + (unsigned)(bytes & ((1 << 30) - 1)) / 10737419); } else if (bytes > 1 << 20) { - int x = bytes + 5243; /* for rounding */ + unsigned x = bytes + 5243; /* for rounding */ strbuf_addf(buf, "%u.%2.2u MiB", x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20); } else if (bytes > 1 << 10) { - int x = bytes + 5; /* for rounding */ + unsigned x = bytes + 5; /* for rounding */ strbuf_addf(buf, "%u.%2.2u KiB", x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10); } else { - strbuf_addf(buf, "%u bytes", (int)bytes); + strbuf_addf(buf, "%u bytes", (unsigned)bytes); } } @@ -960,7 +960,7 @@ static size_t cleanup(char *line, size_t len) */ void strbuf_stripspace(struct strbuf *sb, int skip_comments) { - int empties = 0; + size_t empties = 0; size_t i, j, len, newlen; char *eol; diff --git a/submodule.h b/submodule.h index 4644683e6c..c98cd983e1 100644 --- a/submodule.h +++ b/submodule.h @@ -33,62 +33,62 @@ struct submodule_update_strategy { }; #define SUBMODULE_UPDATE_STRATEGY_INIT {SM_UPDATE_UNSPECIFIED, NULL} -extern int is_gitmodules_unmerged(const struct index_state *istate); -extern int is_staging_gitmodules_ok(struct index_state *istate); -extern int update_path_in_gitmodules(const char *oldpath, const char *newpath); -extern int remove_path_from_gitmodules(const char *path); -extern void stage_updated_gitmodules(struct index_state *istate); -extern void set_diffopt_flags_from_submodule_config(struct diff_options *, - const char *path); -extern int git_default_submodule_config(const char *var, const char *value, void *cb); +int is_gitmodules_unmerged(const struct index_state *istate); +int is_staging_gitmodules_ok(struct index_state *istate); +int update_path_in_gitmodules(const char *oldpath, const char *newpath); +int remove_path_from_gitmodules(const char *path); +void stage_updated_gitmodules(struct index_state *istate); +void set_diffopt_flags_from_submodule_config(struct diff_options *, + const char *path); +int git_default_submodule_config(const char *var, const char *value, void *cb); struct option; int option_parse_recurse_submodules_worktree_updater(const struct option *opt, const char *arg, int unset); -extern int is_submodule_active(struct repository *repo, const char *path); +int is_submodule_active(struct repository *repo, const char *path); /* * Determine if a submodule has been populated at a given 'path' by checking if * the <path>/.git resolves to a valid git repository. * If return_error_code is NULL, die on error. * Otherwise the return error code is the same as of resolve_gitdir_gently. */ -extern int is_submodule_populated_gently(const char *path, int *return_error_code); -extern void die_in_unpopulated_submodule(const struct index_state *istate, - const char *prefix); -extern void die_path_inside_submodule(const struct index_state *istate, - const struct pathspec *ps); -extern enum submodule_update_type parse_submodule_update_type(const char *value); -extern int parse_submodule_update_strategy(const char *value, - struct submodule_update_strategy *dst); -extern const char *submodule_strategy_to_string(const struct submodule_update_strategy *s); -extern void handle_ignore_submodules_arg(struct diff_options *, const char *); -extern void show_submodule_summary(struct diff_options *o, const char *path, - struct object_id *one, struct object_id *two, - unsigned dirty_submodule); -extern void show_submodule_inline_diff(struct diff_options *o, const char *path, - struct object_id *one, struct object_id *two, - unsigned dirty_submodule); +int is_submodule_populated_gently(const char *path, int *return_error_code); +void die_in_unpopulated_submodule(const struct index_state *istate, + const char *prefix); +void die_path_inside_submodule(const struct index_state *istate, + const struct pathspec *ps); +enum submodule_update_type parse_submodule_update_type(const char *value); +int parse_submodule_update_strategy(const char *value, + struct submodule_update_strategy *dst); +const char *submodule_strategy_to_string(const struct submodule_update_strategy *s); +void handle_ignore_submodules_arg(struct diff_options *, const char *); +void show_submodule_summary(struct diff_options *o, const char *path, + struct object_id *one, struct object_id *two, + unsigned dirty_submodule); +void show_submodule_inline_diff(struct diff_options *o, const char *path, + struct object_id *one, struct object_id *two, + unsigned dirty_submodule); /* Check if we want to update any submodule.*/ -extern int should_update_submodules(void); +int should_update_submodules(void); /* * Returns the submodule struct if the given ce entry is a submodule * and it should be updated. Returns NULL otherwise. */ -extern const struct submodule *submodule_from_ce(const struct cache_entry *ce); -extern void check_for_new_submodule_commits(struct object_id *oid); -extern int fetch_populated_submodules(struct repository *r, - const struct argv_array *options, - const char *prefix, - int command_line_option, - int default_option, - int quiet, int max_parallel_jobs); -extern unsigned is_submodule_modified(const char *path, int ignore_untracked); -extern int submodule_uses_gitfile(const char *path); +const struct submodule *submodule_from_ce(const struct cache_entry *ce); +void check_for_new_submodule_commits(struct object_id *oid); +int fetch_populated_submodules(struct repository *r, + const struct argv_array *options, + const char *prefix, + int command_line_option, + int default_option, + int quiet, int max_parallel_jobs); +unsigned is_submodule_modified(const char *path, int ignore_untracked); +int submodule_uses_gitfile(const char *path); #define SUBMODULE_REMOVAL_DIE_ON_ERROR (1<<0) #define SUBMODULE_REMOVAL_IGNORE_UNTRACKED (1<<1) #define SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED (1<<2) -extern int bad_to_remove_submodule(const char *path, unsigned flags); +int bad_to_remove_submodule(const char *path, unsigned flags); int add_submodule_odb(const char *path); @@ -96,17 +96,17 @@ int add_submodule_odb(const char *path); * Checks if there are submodule changes in a..b. If a is the null OID, * checks b and all its ancestors instead. */ -extern int submodule_touches_in_range(struct object_id *a, - struct object_id *b); -extern int find_unpushed_submodules(struct oid_array *commits, - const char *remotes_name, - struct string_list *needs_pushing); +int submodule_touches_in_range(struct object_id *a, + struct object_id *b); +int find_unpushed_submodules(struct oid_array *commits, + const char *remotes_name, + struct string_list *needs_pushing); struct refspec; -extern int push_unpushed_submodules(struct oid_array *commits, - const struct remote *remote, - const struct refspec *rs, - const struct string_list *push_options, - int dry_run); +int push_unpushed_submodules(struct oid_array *commits, + const struct remote *remote, + const struct refspec *rs, + const struct string_list *push_options, + int dry_run); /* * Given a submodule path (as in the index), return the repository * path of that submodule in 'buf'. Return -1 on error or when the @@ -116,10 +116,10 @@ int submodule_to_gitdir(struct strbuf *buf, const char *submodule); #define SUBMODULE_MOVE_HEAD_DRY_RUN (1<<0) #define SUBMODULE_MOVE_HEAD_FORCE (1<<1) -extern int submodule_move_head(const char *path, - const char *old, - const char *new_head, - unsigned flags); +int submodule_move_head(const char *path, + const char *old, + const char *new_head, + unsigned flags); void submodule_unset_core_worktree(const struct submodule *sub); @@ -128,18 +128,18 @@ void submodule_unset_core_worktree(const struct submodule *sub); * a submodule by clearing any repo-specific environment variables, but * retaining any config in the environment. */ -extern void prepare_submodule_repo_env(struct argv_array *out); +void prepare_submodule_repo_env(struct argv_array *out); #define ABSORB_GITDIR_RECURSE_SUBMODULES (1<<0) -extern void absorb_git_dir_into_superproject(const char *prefix, - const char *path, - unsigned flags); +void absorb_git_dir_into_superproject(const char *prefix, + const char *path, + unsigned flags); /* * Return the absolute path of the working tree of the superproject, which this * project is a submodule of. If this repository is not a submodule of * another repository, return NULL. */ -extern const char *get_superproject_working_tree(void); +const char *get_superproject_working_tree(void); #endif diff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c new file mode 100644 index 0000000000..37c452535f --- /dev/null +++ b/t/helper/test-json-writer.c @@ -0,0 +1,565 @@ +#include "test-tool.h" +#include "cache.h" +#include "json-writer.h" + +static const char *expect_obj1 = "{\"a\":\"abc\",\"b\":42,\"c\":true}"; +static const char *expect_obj2 = "{\"a\":-1,\"b\":2147483647,\"c\":0}"; +static const char *expect_obj3 = "{\"a\":0,\"b\":4294967295,\"c\":9223372036854775807}"; +static const char *expect_obj4 = "{\"t\":true,\"f\":false,\"n\":null}"; +static const char *expect_obj5 = "{\"abc\\tdef\":\"abc\\\\def\"}"; +static const char *expect_obj6 = "{\"a\":3.14}"; + +static const char *pretty_obj1 = ("{\n" + " \"a\": \"abc\",\n" + " \"b\": 42,\n" + " \"c\": true\n" + "}"); +static const char *pretty_obj2 = ("{\n" + " \"a\": -1,\n" + " \"b\": 2147483647,\n" + " \"c\": 0\n" + "}"); +static const char *pretty_obj3 = ("{\n" + " \"a\": 0,\n" + " \"b\": 4294967295,\n" + " \"c\": 9223372036854775807\n" + "}"); +static const char *pretty_obj4 = ("{\n" + " \"t\": true,\n" + " \"f\": false,\n" + " \"n\": null\n" + "}"); + +static struct json_writer obj1 = JSON_WRITER_INIT; +static struct json_writer obj2 = JSON_WRITER_INIT; +static struct json_writer obj3 = JSON_WRITER_INIT; +static struct json_writer obj4 = JSON_WRITER_INIT; +static struct json_writer obj5 = JSON_WRITER_INIT; +static struct json_writer obj6 = JSON_WRITER_INIT; + +static void make_obj1(int pretty) +{ + jw_object_begin(&obj1, pretty); + { + jw_object_string(&obj1, "a", "abc"); + jw_object_intmax(&obj1, "b", 42); + jw_object_true(&obj1, "c"); + } + jw_end(&obj1); +} + +static void make_obj2(int pretty) +{ + jw_object_begin(&obj2, pretty); + { + jw_object_intmax(&obj2, "a", -1); + jw_object_intmax(&obj2, "b", 0x7fffffff); + jw_object_intmax(&obj2, "c", 0); + } + jw_end(&obj2); +} + +static void make_obj3(int pretty) +{ + jw_object_begin(&obj3, pretty); + { + jw_object_intmax(&obj3, "a", 0); + jw_object_intmax(&obj3, "b", 0xffffffff); + jw_object_intmax(&obj3, "c", 0x7fffffffffffffffULL); + } + jw_end(&obj3); +} + +static void make_obj4(int pretty) +{ + jw_object_begin(&obj4, pretty); + { + jw_object_true(&obj4, "t"); + jw_object_false(&obj4, "f"); + jw_object_null(&obj4, "n"); + } + jw_end(&obj4); +} + +static void make_obj5(int pretty) +{ + jw_object_begin(&obj5, pretty); + { + jw_object_string(&obj5, "abc" "\x09" "def", "abc" "\\" "def"); + } + jw_end(&obj5); +} + +static void make_obj6(int pretty) +{ + jw_object_begin(&obj6, pretty); + { + jw_object_double(&obj6, "a", 2, 3.14159); + } + jw_end(&obj6); +} + +static const char *expect_arr1 = "[\"abc\",42,true]"; +static const char *expect_arr2 = "[-1,2147483647,0]"; +static const char *expect_arr3 = "[0,4294967295,9223372036854775807]"; +static const char *expect_arr4 = "[true,false,null]"; + +static const char *pretty_arr1 = ("[\n" + " \"abc\",\n" + " 42,\n" + " true\n" + "]"); +static const char *pretty_arr2 = ("[\n" + " -1,\n" + " 2147483647,\n" + " 0\n" + "]"); +static const char *pretty_arr3 = ("[\n" + " 0,\n" + " 4294967295,\n" + " 9223372036854775807\n" + "]"); +static const char *pretty_arr4 = ("[\n" + " true,\n" + " false,\n" + " null\n" + "]"); + +static struct json_writer arr1 = JSON_WRITER_INIT; +static struct json_writer arr2 = JSON_WRITER_INIT; +static struct json_writer arr3 = JSON_WRITER_INIT; +static struct json_writer arr4 = JSON_WRITER_INIT; + +static void make_arr1(int pretty) +{ + jw_array_begin(&arr1, pretty); + { + jw_array_string(&arr1, "abc"); + jw_array_intmax(&arr1, 42); + jw_array_true(&arr1); + } + jw_end(&arr1); +} + +static void make_arr2(int pretty) +{ + jw_array_begin(&arr2, pretty); + { + jw_array_intmax(&arr2, -1); + jw_array_intmax(&arr2, 0x7fffffff); + jw_array_intmax(&arr2, 0); + } + jw_end(&arr2); +} + +static void make_arr3(int pretty) +{ + jw_array_begin(&arr3, pretty); + { + jw_array_intmax(&arr3, 0); + jw_array_intmax(&arr3, 0xffffffff); + jw_array_intmax(&arr3, 0x7fffffffffffffffULL); + } + jw_end(&arr3); +} + +static void make_arr4(int pretty) +{ + jw_array_begin(&arr4, pretty); + { + jw_array_true(&arr4); + jw_array_false(&arr4); + jw_array_null(&arr4); + } + jw_end(&arr4); +} + +static char *expect_nest1 = + "{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}"; + +static struct json_writer nest1 = JSON_WRITER_INIT; + +static void make_nest1(int pretty) +{ + jw_object_begin(&nest1, pretty); + { + jw_object_sub_jw(&nest1, "obj1", &obj1); + jw_object_sub_jw(&nest1, "arr1", &arr1); + } + jw_end(&nest1); +} + +static char *expect_inline1 = + "{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}"; + +static char *pretty_inline1 = + ("{\n" + " \"obj1\": {\n" + " \"a\": \"abc\",\n" + " \"b\": 42,\n" + " \"c\": true\n" + " },\n" + " \"arr1\": [\n" + " \"abc\",\n" + " 42,\n" + " true\n" + " ]\n" + "}"); + +static struct json_writer inline1 = JSON_WRITER_INIT; + +static void make_inline1(int pretty) +{ + jw_object_begin(&inline1, pretty); + { + jw_object_inline_begin_object(&inline1, "obj1"); + { + jw_object_string(&inline1, "a", "abc"); + jw_object_intmax(&inline1, "b", 42); + jw_object_true(&inline1, "c"); + } + jw_end(&inline1); + jw_object_inline_begin_array(&inline1, "arr1"); + { + jw_array_string(&inline1, "abc"); + jw_array_intmax(&inline1, 42); + jw_array_true(&inline1); + } + jw_end(&inline1); + } + jw_end(&inline1); +} + +static char *expect_inline2 = + "[[1,2],[3,4],{\"a\":\"abc\"}]"; + +static char *pretty_inline2 = + ("[\n" + " [\n" + " 1,\n" + " 2\n" + " ],\n" + " [\n" + " 3,\n" + " 4\n" + " ],\n" + " {\n" + " \"a\": \"abc\"\n" + " }\n" + "]"); + +static struct json_writer inline2 = JSON_WRITER_INIT; + +static void make_inline2(int pretty) +{ + jw_array_begin(&inline2, pretty); + { + jw_array_inline_begin_array(&inline2); + { + jw_array_intmax(&inline2, 1); + jw_array_intmax(&inline2, 2); + } + jw_end(&inline2); + jw_array_inline_begin_array(&inline2); + { + jw_array_intmax(&inline2, 3); + jw_array_intmax(&inline2, 4); + } + jw_end(&inline2); + jw_array_inline_begin_object(&inline2); + { + jw_object_string(&inline2, "a", "abc"); + } + jw_end(&inline2); + } + jw_end(&inline2); +} + +/* + * When super is compact, we expect subs to be compacted (even if originally + * pretty). + */ +static const char *expect_mixed1 = + ("{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true}," + "\"arr1\":[\"abc\",42,true]}"); + +/* + * When super is pretty, a compact sub (obj1) is kept compact and a pretty + * sub (arr1) is re-indented. + */ +static const char *pretty_mixed1 = + ("{\n" + " \"obj1\": {\"a\":\"abc\",\"b\":42,\"c\":true},\n" + " \"arr1\": [\n" + " \"abc\",\n" + " 42,\n" + " true\n" + " ]\n" + "}"); + +static struct json_writer mixed1 = JSON_WRITER_INIT; + +static void make_mixed1(int pretty) +{ + jw_init(&obj1); + jw_init(&arr1); + + make_obj1(0); /* obj1 is compact */ + make_arr1(1); /* arr1 is pretty */ + + jw_object_begin(&mixed1, pretty); + { + jw_object_sub_jw(&mixed1, "obj1", &obj1); + jw_object_sub_jw(&mixed1, "arr1", &arr1); + } + jw_end(&mixed1); +} + +static void cmp(const char *test, const struct json_writer *jw, const char *exp) +{ + if (!strcmp(jw->json.buf, exp)) + return; + + printf("error[%s]: observed '%s' expected '%s'\n", + test, jw->json.buf, exp); + exit(1); +} + +#define t(v) do { make_##v(0); cmp(#v, &v, expect_##v); } while (0) +#define p(v) do { make_##v(1); cmp(#v, &v, pretty_##v); } while (0) + +/* + * Run some basic regression tests with some known patterns. + * These tests also demonstrate how to use the jw_ API. + */ +static int unit_tests(void) +{ + /* comptact (canonical) forms */ + t(obj1); + t(obj2); + t(obj3); + t(obj4); + t(obj5); + t(obj6); + + t(arr1); + t(arr2); + t(arr3); + t(arr4); + + t(nest1); + + t(inline1); + t(inline2); + + jw_init(&obj1); + jw_init(&obj2); + jw_init(&obj3); + jw_init(&obj4); + + jw_init(&arr1); + jw_init(&arr2); + jw_init(&arr3); + jw_init(&arr4); + + jw_init(&inline1); + jw_init(&inline2); + + /* pretty forms */ + p(obj1); + p(obj2); + p(obj3); + p(obj4); + + p(arr1); + p(arr2); + p(arr3); + p(arr4); + + p(inline1); + p(inline2); + + /* mixed forms */ + t(mixed1); + jw_init(&mixed1); + p(mixed1); + + return 0; +} + +static void get_s(int line_nr, char **s_in) +{ + *s_in = strtok(NULL, " "); + if (!*s_in) + die("line[%d]: expected: <s>", line_nr); +} + +static void get_i(int line_nr, intmax_t *s_in) +{ + char *s; + char *endptr; + + get_s(line_nr, &s); + + *s_in = strtol(s, &endptr, 10); + if (*endptr || errno == ERANGE) + die("line[%d]: invalid integer value", line_nr); +} + +static void get_d(int line_nr, double *s_in) +{ + char *s; + char *endptr; + + get_s(line_nr, &s); + + *s_in = strtod(s, &endptr); + if (*endptr || errno == ERANGE) + die("line[%d]: invalid float value", line_nr); +} + +static int pretty; + +#define MAX_LINE_LENGTH (64 * 1024) + +static char *get_trimmed_line(char *buf, int buf_size) +{ + int len; + + if (!fgets(buf, buf_size, stdin)) + return NULL; + + len = strlen(buf); + while (len > 0) { + char c = buf[len - 1]; + if (c == '\n' || c == '\r' || c == ' ' || c == '\t') + buf[--len] = 0; + else + break; + } + + while (*buf == ' ' || *buf == '\t') + buf++; + + return buf; +} + +static int scripted(void) +{ + struct json_writer jw = JSON_WRITER_INIT; + char buf[MAX_LINE_LENGTH]; + char *line; + int line_nr = 0; + + line = get_trimmed_line(buf, MAX_LINE_LENGTH); + if (!line) + return 0; + + if (!strcmp(line, "object")) + jw_object_begin(&jw, pretty); + else if (!strcmp(line, "array")) + jw_array_begin(&jw, pretty); + else + die("expected first line to be 'object' or 'array'"); + + while ((line = get_trimmed_line(buf, MAX_LINE_LENGTH)) != NULL) { + char *verb; + char *key; + char *s_value; + intmax_t i_value; + double d_value; + + line_nr++; + + verb = strtok(line, " "); + + if (!strcmp(verb, "end")) { + jw_end(&jw); + } + else if (!strcmp(verb, "object-string")) { + get_s(line_nr, &key); + get_s(line_nr, &s_value); + jw_object_string(&jw, key, s_value); + } + else if (!strcmp(verb, "object-int")) { + get_s(line_nr, &key); + get_i(line_nr, &i_value); + jw_object_intmax(&jw, key, i_value); + } + else if (!strcmp(verb, "object-double")) { + get_s(line_nr, &key); + get_i(line_nr, &i_value); + get_d(line_nr, &d_value); + jw_object_double(&jw, key, i_value, d_value); + } + else if (!strcmp(verb, "object-true")) { + get_s(line_nr, &key); + jw_object_true(&jw, key); + } + else if (!strcmp(verb, "object-false")) { + get_s(line_nr, &key); + jw_object_false(&jw, key); + } + else if (!strcmp(verb, "object-null")) { + get_s(line_nr, &key); + jw_object_null(&jw, key); + } + else if (!strcmp(verb, "object-object")) { + get_s(line_nr, &key); + jw_object_inline_begin_object(&jw, key); + } + else if (!strcmp(verb, "object-array")) { + get_s(line_nr, &key); + jw_object_inline_begin_array(&jw, key); + } + else if (!strcmp(verb, "array-string")) { + get_s(line_nr, &s_value); + jw_array_string(&jw, s_value); + } + else if (!strcmp(verb, "array-int")) { + get_i(line_nr, &i_value); + jw_array_intmax(&jw, i_value); + } + else if (!strcmp(verb, "array-double")) { + get_i(line_nr, &i_value); + get_d(line_nr, &d_value); + jw_array_double(&jw, i_value, d_value); + } + else if (!strcmp(verb, "array-true")) + jw_array_true(&jw); + else if (!strcmp(verb, "array-false")) + jw_array_false(&jw); + else if (!strcmp(verb, "array-null")) + jw_array_null(&jw); + else if (!strcmp(verb, "array-object")) + jw_array_inline_begin_object(&jw); + else if (!strcmp(verb, "array-array")) + jw_array_inline_begin_array(&jw); + else + die("unrecognized token: '%s'", verb); + } + + if (!jw_is_terminated(&jw)) + die("json not terminated: '%s'", jw.json.buf); + + printf("%s\n", jw.json.buf); + + strbuf_release(&jw.json); + return 0; +} + +int cmd__json_writer(int argc, const char **argv) +{ + argc--; /* skip over "json-writer" arg */ + argv++; + + if (argc > 0 && argv[0][0] == '-') { + if (!strcmp(argv[0], "-u") || !strcmp(argv[0], "--unit")) + return unit_tests(); + + if (!strcmp(argv[0], "-p") || !strcmp(argv[0], "--pretty")) + pretty = 1; + } + + return scripted(); +} diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c index dafc91c240..0edafcfd65 100644 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@ -19,6 +19,7 @@ static struct test_cmd cmds[] = { { "genrandom", cmd__genrandom }, { "hashmap", cmd__hashmap }, { "index-version", cmd__index_version }, + { "json-writer", cmd__json_writer }, { "lazy-init-name-hash", cmd__lazy_init_name_hash }, { "match-trees", cmd__match_trees }, { "mergesort", cmd__mergesort }, diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h index 80cbcf0857..e926c416ea 100644 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@ -13,6 +13,7 @@ int cmd__example_decorate(int argc, const char **argv); int cmd__genrandom(int argc, const char **argv); int cmd__hashmap(int argc, const char **argv); int cmd__index_version(int argc, const char **argv); +int cmd__json_writer(int argc, const char **argv); int cmd__lazy_init_name_hash(int argc, const char **argv); int cmd__match_trees(int argc, const char **argv); int cmd__mergesort(int argc, const char **argv); diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh index a5d3b2cbaa..3fe02876c1 100755 --- a/t/lib-gpg.sh +++ b/t/lib-gpg.sh @@ -38,7 +38,33 @@ then "$TEST_DIRECTORY"/lib-gpg/ownertrust && gpg --homedir "${GNUPGHOME}" </dev/null >/dev/null 2>&1 \ --sign -u committer@example.com && - test_set_prereq GPG + test_set_prereq GPG && + # Available key info: + # * see t/lib-gpg/gpgsm-gen-key.in + # To generate new certificate: + # * no passphrase + # gpgsm --homedir /tmp/gpghome/ \ + # -o /tmp/gpgsm.crt.user \ + # --generate-key \ + # --batch t/lib-gpg/gpgsm-gen-key.in + # To import certificate: + # gpgsm --homedir /tmp/gpghome/ \ + # --import /tmp/gpgsm.crt.user + # To export into a .p12 we can later import: + # gpgsm --homedir /tmp/gpghome/ \ + # -o t/lib-gpg/gpgsm_cert.p12 \ + # --export-secret-key-p12 "committer@example.com" + echo | gpgsm --homedir "${GNUPGHOME}" 2>/dev/null \ + --passphrase-fd 0 --pinentry-mode loopback \ + --import "$TEST_DIRECTORY"/lib-gpg/gpgsm_cert.p12 && + gpgsm --homedir "${GNUPGHOME}" 2>/dev/null -K \ + | grep fingerprint: | cut -d" " -f4 | tr -d '\n' > \ + ${GNUPGHOME}/trustlist.txt && + echo " S relax" >> ${GNUPGHOME}/trustlist.txt && + (gpgconf --kill gpg-agent >/dev/null 2>&1 || : ) && + echo hello | gpgsm --homedir "${GNUPGHOME}" >/dev/null \ + -u committer@example.com -o /dev/null --sign - 2>&1 && + test_set_prereq GPGSM ;; esac fi diff --git a/t/lib-gpg/gpgsm-gen-key.in b/t/lib-gpg/gpgsm-gen-key.in new file mode 100644 index 0000000000..a7fd87c069 --- /dev/null +++ b/t/lib-gpg/gpgsm-gen-key.in @@ -0,0 +1,8 @@ +Key-Type: RSA +Key-Length: 2048 +Key-Usage: sign +Serial: random +Name-DN: CN=C O Mitter, O=Example, SN=C O, GN=Mitter +Name-Email: committer@example.com +Not-Before: 1970-01-01 00:00:00 +Not-After: 3000-01-01 00:00:00 diff --git a/t/lib-gpg/gpgsm_cert.p12 b/t/lib-gpg/gpgsm_cert.p12 Binary files differnew file mode 100644 index 0000000000..94ffad0d31 --- /dev/null +++ b/t/lib-gpg/gpgsm_cert.p12 diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh index c03f155a35..1744cee5e9 100755 --- a/t/t0008-ignores.sh +++ b/t/t0008-ignores.sh @@ -807,10 +807,9 @@ test_expect_success 'trailing whitespace is ignored' ' cat >expect <<EOF && whitespace/untracked EOF - : >err.expect && git ls-files -o -X ignore whitespace >actual 2>err && test_cmp expect actual && - test_cmp err.expect err + test_must_be_empty err ' test_expect_success !MINGW 'quoting allows trailing whitespace' ' @@ -820,10 +819,9 @@ test_expect_success !MINGW 'quoting allows trailing whitespace' ' >whitespace/untracked && echo "whitespace/trailing\\ \\ " >ignore && echo whitespace/untracked >expect && - : >err.expect && git ls-files -o -X ignore whitespace >actual 2>err && test_cmp expect actual && - test_cmp err.expect err + test_must_be_empty err ' test_expect_success !MINGW,!CYGWIN 'correct handling of backslashes' ' @@ -845,10 +843,9 @@ test_expect_success !MINGW,!CYGWIN 'correct handling of backslashes' ' whitespace/trailing 6 \\a\\Z EOF echo whitespace/untracked >expect && - >err.expect && git ls-files -o -X ignore whitespace >actual 2>err && test_cmp expect actual && - test_cmp err.expect err + test_must_be_empty err ' test_expect_success 'info/exclude trumps core.excludesfile' ' diff --git a/t/t0019-json-writer.sh b/t/t0019-json-writer.sh new file mode 100755 index 0000000000..3b0c336b38 --- /dev/null +++ b/t/t0019-json-writer.sh @@ -0,0 +1,331 @@ +#!/bin/sh + +test_description='test json-writer JSON generation' +. ./test-lib.sh + +test_expect_success 'unit test of json-writer routines' ' + test-tool json-writer -u +' + +test_expect_success 'trivial object' ' + cat >expect <<-\EOF && + {} + EOF + cat >input <<-\EOF && + object + end + EOF + test-tool json-writer <input >actual && + test_cmp expect actual +' + +test_expect_success 'trivial array' ' + cat >expect <<-\EOF && + [] + EOF + cat >input <<-\EOF && + array + end + EOF + test-tool json-writer <input >actual && + test_cmp expect actual +' + +test_expect_success 'simple object' ' + cat >expect <<-\EOF && + {"a":"abc","b":42,"c":3.14,"d":true,"e":false,"f":null} + EOF + cat >input <<-\EOF && + object + object-string a abc + object-int b 42 + object-double c 2 3.140 + object-true d + object-false e + object-null f + end + EOF + test-tool json-writer <input >actual && + test_cmp expect actual +' + +test_expect_success 'simple array' ' + cat >expect <<-\EOF && + ["abc",42,3.14,true,false,null] + EOF + cat >input <<-\EOF && + array + array-string abc + array-int 42 + array-double 2 3.140 + array-true + array-false + array-null + end + EOF + test-tool json-writer <input >actual && + test_cmp expect actual +' + +test_expect_success 'escape quoting string' ' + cat >expect <<-\EOF && + {"a":"abc\\def"} + EOF + cat >input <<-\EOF && + object + object-string a abc\def + end + EOF + test-tool json-writer <input >actual && + test_cmp expect actual +' + +test_expect_success 'escape quoting string 2' ' + cat >expect <<-\EOF && + {"a":"abc\"def"} + EOF + cat >input <<-\EOF && + object + object-string a abc"def + end + EOF + test-tool json-writer <input >actual && + test_cmp expect actual +' + +test_expect_success 'nested inline object' ' + cat >expect <<-\EOF && + {"a":"abc","b":42,"sub1":{"c":3.14,"d":true,"sub2":{"e":false,"f":null}}} + EOF + cat >input <<-\EOF && + object + object-string a abc + object-int b 42 + object-object sub1 + object-double c 2 3.140 + object-true d + object-object sub2 + object-false e + object-null f + end + end + end + EOF + test-tool json-writer <input >actual && + test_cmp expect actual +' + +test_expect_success 'nested inline array' ' + cat >expect <<-\EOF && + ["abc",42,[3.14,true,[false,null]]] + EOF + cat >input <<-\EOF && + array + array-string abc + array-int 42 + array-array + array-double 2 3.140 + array-true + array-array + array-false + array-null + end + end + end + EOF + test-tool json-writer <input >actual && + test_cmp expect actual +' + +test_expect_success 'nested inline object and array' ' + cat >expect <<-\EOF && + {"a":"abc","b":42,"sub1":{"c":3.14,"d":true,"sub2":[false,null]}} + EOF + cat >input <<-\EOF && + object + object-string a abc + object-int b 42 + object-object sub1 + object-double c 2 3.140 + object-true d + object-array sub2 + array-false + array-null + end + end + end + EOF + test-tool json-writer <input >actual && + test_cmp expect actual +' + +test_expect_success 'nested inline object and array 2' ' + cat >expect <<-\EOF && + {"a":"abc","b":42,"sub1":{"c":3.14,"d":true,"sub2":[false,{"g":0,"h":1},null]}} + EOF + cat >input <<-\EOF && + object + object-string a abc + object-int b 42 + object-object sub1 + object-double c 2 3.140 + object-true d + object-array sub2 + array-false + array-object + object-int g 0 + object-int h 1 + end + array-null + end + end + end + EOF + test-tool json-writer <input >actual && + test_cmp expect actual +' + +test_expect_success 'pretty nested inline object and array 2' ' + sed -e "s/^|//" >expect <<-\EOF && + |{ + | "a": "abc", + | "b": 42, + | "sub1": { + | "c": 3.14, + | "d": true, + | "sub2": [ + | false, + | { + | "g": 0, + | "h": 1 + | }, + | null + | ] + | } + |} + EOF + cat >input <<-\EOF && + object + object-string a abc + object-int b 42 + object-object sub1 + object-double c 2 3.140 + object-true d + object-array sub2 + array-false + array-object + object-int g 0 + object-int h 1 + end + array-null + end + end + end + EOF + test-tool json-writer -p <input >actual && + test_cmp expect actual +' + +test_expect_success 'inline object with no members' ' + cat >expect <<-\EOF && + {"a":"abc","empty":{},"b":42} + EOF + cat >input <<-\EOF && + object + object-string a abc + object-object empty + end + object-int b 42 + end + EOF + test-tool json-writer <input >actual && + test_cmp expect actual +' + +test_expect_success 'inline array with no members' ' + cat >expect <<-\EOF && + {"a":"abc","empty":[],"b":42} + EOF + cat >input <<-\EOF && + object + object-string a abc + object-array empty + end + object-int b 42 + end + EOF + test-tool json-writer <input >actual && + test_cmp expect actual +' + +test_expect_success 'larger empty example' ' + cat >expect <<-\EOF && + {"a":"abc","empty":[{},{},{},[],{}],"b":42} + EOF + cat >input <<-\EOF && + object + object-string a abc + object-array empty + array-object + end + array-object + end + array-object + end + array-array + end + array-object + end + end + object-int b 42 + end + EOF + test-tool json-writer <input >actual && + test_cmp expect actual +' + +test_lazy_prereq PERLJSON ' + perl -MJSON -e "exit 0" +' + +# As a sanity check, ask Perl to parse our generated JSON and recursively +# dump the resulting data in sorted order. Confirm that that matches our +# expectations. +test_expect_success PERLJSON 'parse JSON using Perl' ' + cat >expect <<-\EOF && + row[0].a abc + row[0].b 42 + row[0].sub1 hash + row[0].sub1.c 3.14 + row[0].sub1.d 1 + row[0].sub1.sub2 array + row[0].sub1.sub2[0] 0 + row[0].sub1.sub2[1] hash + row[0].sub1.sub2[1].g 0 + row[0].sub1.sub2[1].h 1 + row[0].sub1.sub2[2] null + EOF + cat >input <<-\EOF && + object + object-string a abc + object-int b 42 + object-object sub1 + object-double c 2 3.140 + object-true d + object-array sub2 + array-false + array-object + object-int g 0 + object-int h 1 + end + array-null + end + end + end + EOF + test-tool json-writer <input >output.json && + perl "$TEST_DIRECTORY"/t0019/parse_json.perl <output.json >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t0019/parse_json.perl b/t/t0019/parse_json.perl new file mode 100644 index 0000000000..ca4e5bfa78 --- /dev/null +++ b/t/t0019/parse_json.perl @@ -0,0 +1,52 @@ +#!/usr/bin/perl +use strict; +use warnings; +use JSON; + +sub dump_array { + my ($label_in, $ary_ref) = @_; + my @ary = @$ary_ref; + + for ( my $i = 0; $i <= $#{ $ary_ref }; $i++ ) + { + my $label = "$label_in\[$i\]"; + dump_item($label, $ary[$i]); + } +} + +sub dump_hash { + my ($label_in, $obj_ref) = @_; + my %obj = %$obj_ref; + + foreach my $k (sort keys %obj) { + my $label = (length($label_in) > 0) ? "$label_in.$k" : "$k"; + my $value = $obj{$k}; + + dump_item($label, $value); + } +} + +sub dump_item { + my ($label_in, $value) = @_; + if (ref($value) eq 'ARRAY') { + print "$label_in array\n"; + dump_array($label_in, $value); + } elsif (ref($value) eq 'HASH') { + print "$label_in hash\n"; + dump_hash($label_in, $value); + } elsif (defined $value) { + print "$label_in $value\n"; + } else { + print "$label_in null\n"; + } +} + +my $row = 0; +while (<>) { + my $data = decode_json( $_ ); + my $label = "row[$row]"; + + dump_hash($label, $data); + $row++; +} + diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index 6a213608cc..308cd28f3b 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh @@ -583,7 +583,7 @@ test_expect_success PERL 'process filter should restart after unexpected write f git checkout --quiet --no-progress . 2>git-stderr.log && grep "smudge write error at" git-stderr.log && - grep "error: external filter" git-stderr.log && + test_i18ngrep "error: external filter" git-stderr.log && cat >expected.log <<-EOF && START diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh index bbf3e39e3d..b77948c618 100755 --- a/t/t0030-stripspace.sh +++ b/t/t0030-stripspace.sh @@ -110,31 +110,30 @@ test_expect_success \ test_expect_success \ 'only consecutive blank lines should be completely removed' ' - > expect && printf "\n" | git stripspace >actual && - test_cmp expect actual && + test_must_be_empty actual && printf "\n\n\n" | git stripspace >actual && - test_cmp expect actual && + test_must_be_empty actual && printf "$sss\n$sss\n$sss\n" | git stripspace >actual && - test_cmp expect actual && + test_must_be_empty actual && printf "$sss$sss\n$sss\n\n" | git stripspace >actual && - test_cmp expect actual && + test_must_be_empty actual && printf "\n$sss\n$sss$sss\n" | git stripspace >actual && - test_cmp expect actual && + test_must_be_empty actual && printf "$sss$sss$sss$sss\n\n\n" | git stripspace >actual && - test_cmp expect actual && + test_must_be_empty actual && printf "\n$sss$sss$sss$sss\n\n" | git stripspace >actual && - test_cmp expect actual && + test_must_be_empty actual && printf "\n\n$sss$sss$sss$sss\n" | git stripspace >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success \ diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh index 03bd31e9f2..82eaaea0f4 100755 --- a/t/t0300-credentials.sh +++ b/t/t0300-credentials.sh @@ -294,8 +294,7 @@ test_expect_success 'helpers can abort the process' ' -c credential.helper="!f() { echo quit=1; }; f" \ -c credential.helper="verbatim foo bar" \ credential fill >stdout && - >expect && - test_cmp expect stdout + test_must_be_empty stdout ' test_expect_success 'empty helper spec resets helper list' ' diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh index 0c6f48f302..ba71b159ba 100755 --- a/t/t1011-read-tree-sparse-checkout.sh +++ b/t/t1011-read-tree-sparse-checkout.sh @@ -227,12 +227,11 @@ test_expect_success 'index removal and worktree narrowing at the same time' ' ' test_expect_success 'read-tree --reset removes outside worktree' ' - >empty && echo init.t >.git/info/sparse-checkout && git checkout -f top && git reset --hard removed && git ls-files sub/added >result && - test_cmp empty result + test_must_be_empty result ' test_expect_success 'print errors when failed to update worktree' ' diff --git a/t/t1015-read-index-unmerged.sh b/t/t1015-read-index-unmerged.sh new file mode 100755 index 0000000000..55d22da32c --- /dev/null +++ b/t/t1015-read-index-unmerged.sh @@ -0,0 +1,123 @@ +#!/bin/sh + +test_description='Test various callers of read_index_unmerged' +. ./test-lib.sh + +test_expect_success 'setup modify/delete + directory/file conflict' ' + test_create_repo df_plus_modify_delete && + ( + cd df_plus_modify_delete && + + test_write_lines a b c d e f g h >letters && + git add letters && + git commit -m initial && + + git checkout -b modify && + # Throw in letters.txt for sorting order fun + # ("letters.txt" sorts between "letters" and "letters/file") + echo i >>letters && + echo "version 2" >letters.txt && + git add letters letters.txt && + git commit -m modified && + + git checkout -b delete HEAD^ && + git rm letters && + mkdir letters && + >letters/file && + echo "version 1" >letters.txt && + git add letters letters.txt && + git commit -m deleted + ) +' + +test_expect_success 'read-tree --reset cleans unmerged entries' ' + test_when_finished "git -C df_plus_modify_delete clean -f" && + test_when_finished "git -C df_plus_modify_delete reset --hard" && + ( + cd df_plus_modify_delete && + + git checkout delete^0 && + test_must_fail git merge modify && + + git read-tree --reset HEAD && + git ls-files -u >conflicts && + test_must_be_empty conflicts + ) +' + +test_expect_success 'One reset --hard cleans unmerged entries' ' + test_when_finished "git -C df_plus_modify_delete clean -f" && + test_when_finished "git -C df_plus_modify_delete reset --hard" && + ( + cd df_plus_modify_delete && + + git checkout delete^0 && + test_must_fail git merge modify && + + git reset --hard && + test_path_is_missing .git/MERGE_HEAD && + git ls-files -u >conflicts && + test_must_be_empty conflicts + ) +' + +test_expect_success 'setup directory/file conflict + simple edit/edit' ' + test_create_repo df_plus_edit_edit && + ( + cd df_plus_edit_edit && + + test_seq 1 10 >numbers && + git add numbers && + git commit -m initial && + + git checkout -b d-edit && + mkdir foo && + echo content >foo/bar && + git add foo && + echo 11 >>numbers && + git add numbers && + git commit -m "directory and edit" && + + git checkout -b f-edit d-edit^1 && + echo content >foo && + git add foo && + echo eleven >>numbers && + git add numbers && + git commit -m "file and edit" + ) +' + +test_expect_success 'git merge --abort succeeds despite D/F conflict' ' + test_when_finished "git -C df_plus_edit_edit clean -f" && + test_when_finished "git -C df_plus_edit_edit reset --hard" && + ( + cd df_plus_edit_edit && + + git checkout f-edit^0 && + test_must_fail git merge d-edit^0 && + + git merge --abort && + test_path_is_missing .git/MERGE_HEAD && + git ls-files -u >conflicts && + test_must_be_empty conflicts + ) +' + +test_expect_success 'git am --skip succeeds despite D/F conflict' ' + test_when_finished "git -C df_plus_edit_edit clean -f" && + test_when_finished "git -C df_plus_edit_edit reset --hard" && + ( + cd df_plus_edit_edit && + + git checkout f-edit^0 && + git format-patch -1 d-edit && + test_must_fail git am -3 0001*.patch && + + git am --skip && + test_path_is_missing .git/rebase-apply && + git ls-files -u >conflicts && + test_must_be_empty conflicts + ) +' + +test_done diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh index f035ee40a3..635918505d 100755 --- a/t/t1305-config-include.sh +++ b/t/t1305-config-include.sh @@ -310,7 +310,7 @@ test_expect_success 'include cycles are detected' ' cycle EOF test_must_fail git config --get-all test.value 2>stderr && - grep "exceeded maximum include depth" stderr + test_i18ngrep "exceeded maximum include depth" stderr ' test_done diff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh index 8b14ab187c..21e139a313 100755 --- a/t/t1306-xdg-files.sh +++ b/t/t1306-xdg-files.sh @@ -114,11 +114,10 @@ test_expect_success 'Exclusion in a non-XDG global ignore file' ' ' test_expect_success 'Checking XDG ignore file when HOME is unset' ' - >expected && (sane_unset HOME && git config --unset core.excludesfile && git ls-files --exclude-standard --ignored >actual) && - test_cmp expected actual + test_must_be_empty actual ' test_expect_success 'Checking attributes in the XDG attributes file' ' @@ -132,10 +131,9 @@ test_expect_success 'Checking attributes in the XDG attributes file' ' ' test_expect_success 'Checking XDG attributes when HOME is unset' ' - >expected && (sane_unset HOME && git check-attr -a f >actual) && - test_cmp expected actual + test_must_be_empty actual ' test_expect_success '$XDG_CONFIG_HOME overrides $HOME/.config/git/attributes' ' diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh index 3e00d1af01..d0a2727b85 100755 --- a/t/t1308-config-set.sh +++ b/t/t1308-config-set.sh @@ -233,7 +233,7 @@ test_expect_success 'check line errors for malformed values' ' test_expect_success 'error on modifying repo config without repo' ' nongit test_must_fail git config a.b c 2>err && - grep "not in a git directory" err + test_i18ngrep "not in a git directory" err ' cmdline_config="'foo.bar=from-cmdline'" diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index e1fd0f0ca8..7c8df20955 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -390,7 +390,7 @@ test_expect_success 'Query "master@{2005-05-26 23:33:01}" (middle of history wit test_when_finished "rm -f o e" && git rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e && test $B = $(cat o) && - test "warning: Log for ref $m has gap after $gd." = "$(cat e)" + test_i18ngrep -F "warning: log for ref $m has gap after $gd" e ' test_expect_success 'Query "master@{2005-05-26 23:38:00}" (middle of history)' ' test_when_finished "rm -f o e" && @@ -408,7 +408,7 @@ test_expect_success 'Query "master@{2005-05-28}" (past end of history)' ' test_when_finished "rm -f o e" && git rev-parse --verify "master@{2005-05-28}" >o 2>e && test $D = $(cat o) && - test "warning: Log for ref $m unexpectedly ended on $ld." = "$(cat e)" + test_i18ngrep -F "warning: log for ref $m unexpectedly ended on $ld" e ' rm -f .git/$m .git/logs/$m expect @@ -462,7 +462,7 @@ test_expect_success 'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER test_expect_success 'given old value for missing pseudoref, do not create' ' test_must_fail git update-ref PSEUDOREF $A $B 2>err && test_path_is_missing .git/PSEUDOREF && - grep "could not read ref" err + test_i18ngrep "could not read ref" err ' test_expect_success 'create pseudoref' ' @@ -483,7 +483,7 @@ test_expect_success 'overwrite pseudoref with correct old value' ' test_expect_success 'do not overwrite pseudoref with wrong old value' ' test_must_fail git update-ref PSEUDOREF $D $E 2>err && test $C = $(cat .git/PSEUDOREF) && - grep "unexpected object ID" err + test_i18ngrep "unexpected object ID" err ' test_expect_success 'delete pseudoref' ' @@ -495,7 +495,7 @@ test_expect_success 'do not delete pseudoref with wrong old value' ' git update-ref PSEUDOREF $A && test_must_fail git update-ref -d PSEUDOREF $B 2>err && test $A = $(cat .git/PSEUDOREF) && - grep "unexpected object ID" err + test_i18ngrep "unexpected object ID" err ' test_expect_success 'delete pseudoref with correct old value' ' @@ -512,7 +512,7 @@ test_expect_success 'do not overwrite pseudoref with old OID zero' ' test_when_finished git update-ref -d PSEUDOREF && test_must_fail git update-ref PSEUDOREF $B $Z 2>err && test $A = $(cat .git/PSEUDOREF) && - grep "already exists" err + test_i18ngrep "already exists" err ' # Test --stdin @@ -650,7 +650,7 @@ test_expect_success 'stdin fails with duplicate refs' ' create $a $m EOF test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed." err + test_i18ngrep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed" err ' test_expect_success 'stdin create ref works' ' @@ -1052,7 +1052,7 @@ test_expect_success 'stdin -z fails option with unknown name' ' test_expect_success 'stdin -z fails with duplicate refs' ' printf $F "create $a" "$m" "create $b" "$m" "create $a" "$m" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && - grep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed." err + test_i18ngrep "fatal: multiple updates for ref '"'"'$a'"'"' not allowed" err ' test_expect_success 'stdin -z create ref works' ' @@ -1283,7 +1283,7 @@ test_expect_success 'fails with duplicate HEAD update' ' update HEAD $B EOF test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: multiple updates for '\''HEAD'\'' (including one via its referent .refs/heads/target1.) are not allowed" err && + test_i18ngrep "fatal: multiple updates for '\''HEAD'\'' (including one via its referent .refs/heads/target1.) are not allowed" err && echo "refs/heads/target1" >expect && git symbolic-ref HEAD >actual && test_cmp expect actual && @@ -1300,7 +1300,7 @@ test_expect_success 'fails with duplicate ref update via symref' ' update refs/heads/symref2 $B EOF test_must_fail git update-ref --stdin <stdin 2>err && - grep "fatal: multiple updates for '\''refs/heads/target2'\'' (including one via symref .refs/heads/symref2.) are not allowed" err && + test_i18ngrep "fatal: multiple updates for '\''refs/heads/target2'\'' (including one via symref .refs/heads/symref2.) are not allowed" err && echo "refs/heads/target2" >expect && git symbolic-ref refs/heads/symref2 >actual && test_cmp expect actual && diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh index 30354fd26c..5d955c3bff 100755 --- a/t/t1403-show-ref.sh +++ b/t/t1403-show-ref.sh @@ -26,26 +26,22 @@ test_expect_success 'show-ref' ' git show-ref refs/tags/A >actual && test_cmp expect actual && - >expect && - test_must_fail git show-ref D >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'show-ref -q' ' - >expect && - git show-ref -q A >actual && - test_cmp expect actual && + test_must_be_empty actual && git show-ref -q tags/A >actual && - test_cmp expect actual && + test_must_be_empty actual && git show-ref -q refs/tags/A >actual && - test_cmp expect actual && + test_must_be_empty actual && test_must_fail git show-ref -q D >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'show-ref --verify' ' @@ -54,32 +50,28 @@ test_expect_success 'show-ref --verify' ' git show-ref --verify refs/tags/A >actual && test_cmp expect actual && - >expect && - test_must_fail git show-ref --verify A >actual && - test_cmp expect actual && + test_must_be_empty actual && test_must_fail git show-ref --verify tags/A >actual && - test_cmp expect actual && + test_must_be_empty actual && test_must_fail git show-ref --verify D >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'show-ref --verify -q' ' - >expect && - git show-ref --verify -q refs/tags/A >actual && - test_cmp expect actual && + test_must_be_empty actual && test_must_fail git show-ref --verify -q A >actual && - test_cmp expect actual && + test_must_be_empty actual && test_must_fail git show-ref --verify -q tags/A >actual && - test_cmp expect actual && + test_must_be_empty actual && test_must_fail git show-ref --verify -q D >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'show-ref -d' ' @@ -113,19 +105,17 @@ test_expect_success 'show-ref -d' ' git show-ref -d --verify refs/heads/master >actual && test_cmp expect actual && - >expect && - test_must_fail git show-ref -d --verify master >actual && - test_cmp expect actual && + test_must_be_empty actual && test_must_fail git show-ref -d --verify heads/master >actual && - test_cmp expect actual && + test_must_be_empty actual && test_must_fail git show-ref --verify -d A C >actual && - test_cmp expect actual && + test_must_be_empty actual && test_must_fail git show-ref --verify -d tags/A tags/C >actual && - test_cmp expect actual + test_must_be_empty actual ' @@ -178,10 +168,8 @@ test_expect_success 'show-ref --verify HEAD' ' git show-ref --verify HEAD >actual && test_cmp expect actual && - >expect && - git show-ref --verify -q HEAD >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'show-ref --verify with dangling ref' ' diff --git a/t/t1404-update-ref-errors.sh b/t/t1404-update-ref-errors.sh index 3a887b5113..2a42a589a4 100755 --- a/t/t1404-update-ref-errors.sh +++ b/t/t1404-update-ref-errors.sh @@ -27,7 +27,7 @@ test_update_rejected () { fi && printf "create $prefix/%s $C\n" $create >input && test_must_fail git update-ref --stdin <input 2>output.err && - grep -F "$error" output.err && + test_i18ngrep -F "$error" output.err && git for-each-ref $prefix >actual && test_cmp unchanged actual } @@ -103,7 +103,7 @@ df_test() { printf "%s\n" "delete $delname" "create $addname $D" fi >commands && test_must_fail git update-ref --stdin <commands 2>output.err && - test_cmp expected-err output.err && + test_i18ncmp expected-err output.err && printf "%s\n" "$C $delref" >expected-refs && git for-each-ref --format="%(objectname) %(refname)" $prefix/r >actual-refs && test_cmp expected-refs actual-refs diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index 91fd71444d..7b7602ddb4 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -372,7 +372,7 @@ test_expect_success 'rev-list --verify-objects with bad sha1' ' test_might_fail git rev-list --verify-objects refs/heads/bogus >/dev/null 2>out && cat out && - grep -q "error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff" out + test_i18ngrep -q "error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff" out ' test_expect_success 'force fsck to ignore double author' ' diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh index 349f6e10af..fa3e499641 100755 --- a/t/t1507-rev-parse-upstream.sh +++ b/t/t1507-rev-parse-upstream.sh @@ -138,8 +138,7 @@ test_expect_success 'branch -d other@{u}' ' git checkout -t -b other master && git branch -d @{u} && git for-each-ref refs/heads/master >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'checkout other@{u}' ' diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh index 26dc3f1fc0..f79dfbbdd6 100755 --- a/t/t2024-checkout-dwim.sh +++ b/t/t2024-checkout-dwim.sh @@ -24,9 +24,8 @@ test_branch_upstream () { } status_uno_is_clean () { - >status.expect && git status -uno --porcelain >status.actual && - test_cmp status.expect status.actual + test_must_be_empty status.actual } test_expect_success 'setup' ' diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh index be6e093142..166942c1bd 100755 --- a/t/t2025-worktree-add.sh +++ b/t/t2025-worktree-add.sh @@ -412,9 +412,8 @@ test_expect_success '"add" <path> <branch> dwims with checkout.defaultRemote' ' git fetch repo_upstream2 && test_must_fail git worktree add ../foo foo && git -c checkout.defaultRemote=repo_upstream worktree add ../foo foo && - >status.expect && git status -uno --porcelain >status.actual && - test_cmp status.expect status.actual + test_must_be_empty status.actual ) && ( cd foo && diff --git a/t/t2202-add-addremove.sh b/t/t2202-add-addremove.sh index 17744e8c57..9ee659098c 100755 --- a/t/t2202-add-addremove.sh +++ b/t/t2202-add-addremove.sh @@ -48,8 +48,7 @@ test_expect_success 'Just "git add" is a no-op' ' >will-not-be-added && git add && git diff-index --name-status --cached HEAD >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ' test_done diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh index 3fc484e8c3..3b47647ed5 100755 --- a/t/t3001-ls-files-others-exclude.sh +++ b/t/t3001-ls-files-others-exclude.sh @@ -210,8 +210,7 @@ test_expect_success 'subdirectory ignore (toplevel)' ' cd top && git ls-files -o --exclude-standard ) >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'subdirectory ignore (l1/l2)' ' @@ -219,8 +218,7 @@ test_expect_success 'subdirectory ignore (l1/l2)' ' cd top/l1/l2 && git ls-files -o --exclude-standard ) >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'subdirectory ignore (l1)' ' @@ -228,8 +226,7 @@ test_expect_success 'subdirectory ignore (l1)' ' cd top/l1 && git ls-files -o --exclude-standard ) >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'show/hide empty ignored directory (setup)' ' @@ -251,8 +248,7 @@ test_expect_success 'hide empty ignored directory with --no-empty-directory' ' cd top && git ls-files -o -i --exclude l1 --directory --no-empty-directory ) >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'show/hide empty ignored sub-directory (setup)' ' @@ -277,8 +273,7 @@ test_expect_success 'hide empty ignored sub-directory with --no-empty-directory' cd top && git ls-files -o -i --exclude l1 --directory --no-empty-directory ) >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'pattern matches prefix completely' ' diff --git a/t/t3005-ls-files-relative.sh b/t/t3005-ls-files-relative.sh index 377869432e..209b4c7cd8 100755 --- a/t/t3005-ls-files-relative.sh +++ b/t/t3005-ls-files-relative.sh @@ -44,13 +44,13 @@ test_expect_success 'ls-files -c' ' cd top/sub && for f in ../y* do - echo "error: pathspec $sq$f$sq did not match any file(s) known to git." + echo "error: pathspec $sq$f$sq did not match any file(s) known to git" done >expect.err && echo "Did you forget to ${sq}git add${sq}?" >>expect.err && ls ../x* >expect.out && test_must_fail git ls-files -c --error-unmatch ../[xy]* >actual.out 2>actual.err && test_cmp expect.out actual.out && - test_cmp expect.err actual.err + test_i18ncmp expect.err actual.err ) ' @@ -59,13 +59,13 @@ test_expect_success 'ls-files -o' ' cd top/sub && for f in ../x* do - echo "error: pathspec $sq$f$sq did not match any file(s) known to git." + echo "error: pathspec $sq$f$sq did not match any file(s) known to git" done >expect.err && echo "Did you forget to ${sq}git add${sq}?" >>expect.err && ls ../y* >expect.out && test_must_fail git ls-files -o --error-unmatch ../[xy]* >actual.out 2>actual.err && test_cmp expect.out actual.out && - test_cmp expect.err actual.err + test_i18ncmp expect.err actual.err ) ' diff --git a/t/t3031-merge-criscross.sh b/t/t3031-merge-criscross.sh index e59b0a32d6..3824756a02 100755 --- a/t/t3031-merge-criscross.sh +++ b/t/t3031-merge-criscross.sh @@ -88,7 +88,7 @@ test_expect_success 'setup repo with criss-cross history' ' git branch G ' -test_expect_success 'recursive merge between F and G, causes segfault' ' +test_expect_success 'recursive merge between F and G does not cause segfault' ' git merge F ' diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh index dce102130f..46aca0af10 100755 --- a/t/t3070-wildmatch.sh +++ b/t/t3070-wildmatch.sh @@ -101,8 +101,7 @@ match_with_ls_files() { match_stdout_stderr_cmp=" tr -d '\0' <actual.raw >actual && - >expect.err && - test_cmp expect.err actual.err && + test_must_be_empty actual.err && test_cmp expect actual" if test "$match_expect" = 'E' diff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh index 0ef1b6fdcc..0ea4fc4694 100755 --- a/t/t3201-branch-contains.sh +++ b/t/t3201-branch-contains.sh @@ -48,16 +48,14 @@ test_expect_success 'branch --contains master' ' test_expect_success 'branch --no-contains=master' ' git branch --no-contains=master >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'branch --no-contains master' ' git branch --no-contains master >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ' @@ -94,8 +92,7 @@ test_expect_success 'branch --contains with pattern implies --list' ' test_expect_success 'branch --no-contains with pattern implies --list' ' git branch --no-contains=master master >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ' @@ -123,8 +120,7 @@ test_expect_success 'branch --merged with pattern implies --list' ' test_expect_success 'side: branch --no-merged' ' git branch --no-merged >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ' @@ -152,8 +148,7 @@ test_expect_success 'master: branch --no-merged' ' test_expect_success 'branch --no-merged with pattern implies --list' ' git branch --no-merged=master master >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ' diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh index 724b4b9bc0..7333d7d545 100755 --- a/t/t3210-pack-refs.sh +++ b/t/t3210-pack-refs.sh @@ -186,7 +186,7 @@ test_expect_success 'notice d/f conflict with existing directory' ' test_expect_success 'existing directory reports concrete ref' ' test_must_fail git branch foo 2>stderr && - grep refs/heads/foo/bar/baz stderr + test_i18ngrep refs/heads/foo/bar/baz stderr ' test_expect_success 'notice d/f conflict with existing ref' ' diff --git a/t/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh index 9c1bf6eb3d..68436eed82 100755 --- a/t/t3310-notes-merge-manual-resolve.sh +++ b/t/t3310-notes-merge-manual-resolve.sh @@ -541,9 +541,9 @@ EOF test "$(git rev-parse refs/notes/y)" = "$(git rev-parse NOTES_MERGE_PARTIAL^1)" && test "$(git rev-parse refs/notes/m)" != "$(git rev-parse NOTES_MERGE_PARTIAL^1)" && # Mention refs/notes/m, and its current and expected value in output - grep -q "refs/notes/m" output && - grep -q "$(git rev-parse refs/notes/m)" output && - grep -q "$(git rev-parse NOTES_MERGE_PARTIAL^1)" output && + test_i18ngrep -q "refs/notes/m" output && + test_i18ngrep -q "$(git rev-parse refs/notes/m)" output && + test_i18ngrep -q "$(git rev-parse NOTES_MERGE_PARTIAL^1)" output && # Verify that other notes refs has not changed (w, x, y and z) verify_notes w && verify_notes x && diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 640fddc9c3..4c7b1ea356 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -1247,7 +1247,7 @@ rebase_setup_and_clean () { test_might_fail git branch -D $1 && test_might_fail git rebase --abort " && - git checkout -b $1 master + git checkout -b $1 ${2:-master} } test_expect_success 'drop' ' @@ -1424,4 +1424,12 @@ test_expect_success 'rebase -i --gpg-sign=<key-id> overrides commit.gpgSign' ' test_i18ngrep "$SQ-S\"S I Gner\"$SQ" err ' +test_expect_success 'valid author header after --root swap' ' + rebase_setup_and_clean author-header no-conflict-branch && + set_fake_editor && + FAKE_LINES="2 1" git rebase -i --root && + git cat-file commit HEAD^ >out && + grep "^author ..*> [0-9][0-9]* [-+][0-9][0-9][0-9][0-9]$" out +' + test_done diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh index 9e62297274..90ae613e23 100755 --- a/t/t3430-rebase-merges.sh +++ b/t/t3430-rebase-merges.sh @@ -363,4 +363,21 @@ test_expect_success 'octopus merges' ' EOF ' +test_expect_success 'with --autosquash and --exec' ' + git checkout -b with-exec H && + echo Booh >B.t && + test_tick && + git commit --fixup B B.t && + write_script show.sh <<-\EOF && + subject="$(git show -s --format=%s HEAD)" + content="$(git diff HEAD^! | tail -n 1)" + echo "$subject: $content" + EOF + test_tick && + git rebase -ir --autosquash --exec ./show.sh A >actual && + grep "B: +Booh" actual && + grep "E: +Booh" actual && + grep "G: +G" actual +' + test_done diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh index 7c5ad08626..0db166152a 100755 --- a/t/t3507-cherry-pick-conflict.sh +++ b/t/t3507-cherry-pick-conflict.sh @@ -392,4 +392,17 @@ test_expect_success 'commit --amend -s places the sign-off at the right place' ' test_cmp expect actual ' +test_expect_success 'cherry-pick preserves sparse-checkout' ' + pristine_detach initial && + test_config core.sparseCheckout true && + test_when_finished " + echo \"/*\" >.git/info/sparse-checkout + git read-tree --reset -u HEAD + rm .git/info/sparse-checkout" && + echo /unrelated >.git/info/sparse-checkout && + git read-tree --reset -u HEAD && + test_must_fail git cherry-pick -Xours picked>actual && + test_i18ngrep ! "Changes not staged for commit:" actual +' + test_done diff --git a/t/t3700-add.sh b/t/t3700-add.sh index 618750167a..37729ba258 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -188,9 +188,8 @@ test_expect_success 'git add --refresh with pathspec' ' git add foo bar baz && H=$(git rev-parse :foo) && git rm -f foo && echo "100644 $H 3 foo" | git update-index --index-info && test-tool chmtime -60 bar baz && - >expect && git add --refresh bar >actual && - test_cmp expect actual && + test_must_be_empty actual && git diff-files --name-only >actual && ! grep bar actual&& diff --git a/t/t3910-mac-os-precompose.sh b/t/t3910-mac-os-precompose.sh index 26dd5b7f78..54ce19e353 100755 --- a/t/t3910-mac-os-precompose.sh +++ b/t/t3910-mac-os-precompose.sh @@ -187,9 +187,8 @@ test_expect_failure 'handle existing decomposed filenames' ' echo content >"verbatim.$Adiarnfd" && git -c core.precomposeunicode=false add "verbatim.$Adiarnfd" && git commit -m "existing decomposed file" && - >expect && git ls-files --exclude-standard -o "verbatim*" >untracked && - test_cmp expect untracked + test_must_be_empty untracked ' # Test if the global core.precomposeunicode stops autosensing diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh index b7f25071cf..281f8fad0c 100755 --- a/t/t4010-diff-pathspec.sh +++ b/t/t4010-diff-pathspec.sh @@ -74,8 +74,7 @@ test_expect_success 'diff-tree pathspec' ' tree2=$(git write-tree) && echo "$tree2" && git diff-tree -r --name-only $tree $tree2 -- pa path1/a >current && - >expected && - test_cmp expected current + test_must_be_empty current ' test_expect_success 'diff-tree with wildcard shows dir also matches' ' diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh index 41facf7abf..35fc8b5c2a 100755 --- a/t/t4015-diff-whitespace.sh +++ b/t/t4015-diff-whitespace.sh @@ -93,21 +93,20 @@ test_expect_success 'another test, without options' ' git diff >out && test_cmp expect out && - >expect && git diff -w >out && - test_cmp expect out && + test_must_be_empty out && git diff -w -b >out && - test_cmp expect out && + test_must_be_empty out && git diff -w --ignore-space-at-eol >out && - test_cmp expect out && + test_must_be_empty out && git diff -w -b --ignore-space-at-eol >out && - test_cmp expect out && + test_must_be_empty out && git diff -w --ignore-cr-at-eol >out && - test_cmp expect out && + test_must_be_empty out && tr "Q_" "\015 " <<-\EOF >expect && diff --git a/x b/x @@ -182,8 +181,7 @@ test_expect_success 'ignore-blank-lines: only new lines' ' test_seq 5 | sed "/3/i\\ " >x && git diff --ignore-blank-lines >out && - >expect && - test_cmp expect out + test_must_be_empty out ' test_expect_success 'ignore-blank-lines: only new lines with space' ' @@ -192,8 +190,7 @@ test_expect_success 'ignore-blank-lines: only new lines with space' ' test_seq 5 | sed "/3/i\\ " >x && git diff -w --ignore-blank-lines >out && - >expect && - test_cmp expect out + test_must_be_empty out ' test_expect_success 'ignore-blank-lines: after change' ' @@ -1305,7 +1302,7 @@ test_expect_success 'detect blocks of moved code' ' ' -test_expect_success 'detect permutations inside moved code -- dimmed_zebra' ' +test_expect_success 'detect permutations inside moved code -- dimmed-zebra' ' # reuse setup from test before! test_config color.diff.oldMoved "magenta" && test_config color.diff.newMoved "cyan" && @@ -1315,7 +1312,7 @@ test_expect_success 'detect permutations inside moved code -- dimmed_zebra' ' test_config color.diff.newMovedDimmed "normal cyan" && test_config color.diff.oldMovedAlternativeDimmed "normal blue" && test_config color.diff.newMovedAlternativeDimmed "normal yellow" && - git diff HEAD --no-renames --color-moved=dimmed_zebra --color >actual.raw && + git diff HEAD --no-renames --color-moved=dimmed-zebra --color >actual.raw && grep -v "index" actual.raw | test_decode_color >actual && cat <<-\EOF >expected && <BOLD>diff --git a/lines.txt b/lines.txt<RESET> diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh index 23c0e357a7..53ac44b0f0 100755 --- a/t/t4039-diff-assume-unchanged.sh +++ b/t/t4039-diff-assume-unchanged.sh @@ -34,9 +34,8 @@ test_expect_success POSIXPERM 'find-copies-harder is not confused by mode bits' git add exec && git commit -m exec && git update-index --assume-unchanged exec && - >expect && git diff-files --find-copies-harder -- exec >actual && - test_cmp expect actual + test_must_be_empty actual ' test_done diff --git a/t/t4150-am.sh b/t/t4150-am.sh index 1ebc587f8f..01867a9898 100755 --- a/t/t4150-am.sh +++ b/t/t4150-am.sh @@ -69,13 +69,15 @@ test_expect_success 'setup: messages' ' EOF - cat >scissors-msg <<-\EOF && - Test git-am with scissors line + cat >msg-without-scissors-line <<-\EOF && + Test that git-am --scissors cuts at the scissors line This line should be included in the commit message. EOF - cat - scissors-msg >no-scissors-msg <<-\EOF && + printf "Subject: " >subject-prefix && + + cat - subject-prefix msg-without-scissors-line >msg-with-scissors-line <<-\EOF && This line should not be included in the commit message with --scissors enabled. - - >8 - - remove everything above this line - - >8 - - @@ -148,18 +150,17 @@ test_expect_success setup ' } >patch1-hg.eml && - echo scissors-file >scissors-file && - git add scissors-file && - git commit -F scissors-msg && - git tag scissors && - git format-patch --stdout scissors^ >scissors-patch.eml && + echo file >file && + git add file && + git commit -F msg-without-scissors-line && + git tag expected-for-scissors && git reset --hard HEAD^ && - echo no-scissors-file >no-scissors-file && - git add no-scissors-file && - git commit -F no-scissors-msg && - git tag no-scissors && - git format-patch --stdout no-scissors^ >no-scissors-patch.eml && + echo file >file && + git add file && + git commit -F msg-with-scissors-line && + git tag expected-for-no-scissors && + git format-patch --stdout expected-for-no-scissors^ >patch-with-scissors-line.eml && git reset --hard HEAD^ && sed -n -e "3,\$p" msg >file && @@ -416,10 +417,10 @@ test_expect_success 'am --scissors cuts the message at the scissors line' ' rm -fr .git/rebase-apply && git reset --hard && git checkout second && - git am --scissors scissors-patch.eml && + git am --scissors patch-with-scissors-line.eml && test_path_is_missing .git/rebase-apply && - git diff --exit-code scissors && - test_cmp_rev scissors HEAD + git diff --exit-code expected-for-scissors && + test_cmp_rev expected-for-scissors HEAD ' test_expect_success 'am --no-scissors overrides mailinfo.scissors' ' @@ -427,10 +428,10 @@ test_expect_success 'am --no-scissors overrides mailinfo.scissors' ' git reset --hard && git checkout second && test_config mailinfo.scissors true && - git am --no-scissors no-scissors-patch.eml && + git am --no-scissors patch-with-scissors-line.eml && test_path_is_missing .git/rebase-apply && - git diff --exit-code no-scissors && - test_cmp_rev no-scissors HEAD + git diff --exit-code expected-for-no-scissors && + test_cmp_rev expected-for-no-scissors HEAD ' test_expect_success 'setup: new author and committer' ' diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh index 8417e5a4b1..65da74c766 100755 --- a/t/t4200-rerere.sh +++ b/t/t4200-rerere.sh @@ -267,8 +267,7 @@ rerere_gc_custom_expiry_test () { git -c "gc.rerereresolved=$right_now" \ -c "gc.rerereunresolved=$right_now" rerere gc && find .git/rr-cache -type f | sort >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ' } @@ -536,9 +535,8 @@ test_expect_success 'multiple identical conflicts' ' # We resolved file1 and file2 git rerere && - >expect && git rerere remaining >actual && - test_cmp expect actual && + test_must_be_empty actual && # We must have recorded both of them count_pre_post 2 2 && @@ -548,9 +546,8 @@ test_expect_success 'multiple identical conflicts' ' test_must_fail git merge six.1 && git rerere && - >expect && git rerere remaining >actual && - test_cmp expect actual && + test_must_be_empty actual && concat_insert short 6.1 6.2 >file1.expect && concat_insert long 6.1 6.2 >file2.expect && diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 25b1f8cc73..153a506151 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -340,10 +340,9 @@ test_expect_success PCRE 'log -F -E --perl-regexp --grep=<pcre> uses PCRE' ' ' test_expect_success 'log with grep.patternType configuration' ' - >expect && git -c grep.patterntype=fixed \ log -1 --pretty=tformat:%s --grep=s.c.nd >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'log with grep.patternType configuration and command line' ' @@ -1556,12 +1555,28 @@ test_expect_success GPG 'setup signed branch' ' git commit -S -m signed_commit ' +test_expect_success GPGSM 'setup signed branch x509' ' + test_when_finished "git reset --hard && git checkout master" && + git checkout -b signed-x509 master && + echo foo >foo && + git add foo && + test_config gpg.format x509 && + test_config user.signingkey $GIT_COMMITTER_EMAIL && + git commit -S -m signed_commit +' + test_expect_success GPG 'log --graph --show-signature' ' git log --graph --show-signature -n1 signed >actual && grep "^| gpg: Signature made" actual && grep "^| gpg: Good signature" actual ' +test_expect_success GPGSM 'log --graph --show-signature x509' ' + git log --graph --show-signature -n1 signed-x509 >actual && + grep "^| gpgsm: Signature made" actual && + grep "^| gpgsm: Good signature" actual +' + test_expect_success GPG 'log --graph --show-signature for merged tag' ' test_when_finished "git reset --hard && git checkout master" && git checkout -b plain master && @@ -1581,6 +1596,27 @@ test_expect_success GPG 'log --graph --show-signature for merged tag' ' grep "^| | gpg: Good signature" actual ' +test_expect_success GPGSM 'log --graph --show-signature for merged tag x509' ' + test_when_finished "git reset --hard && git checkout master" && + test_config gpg.format x509 && + test_config user.signingkey $GIT_COMMITTER_EMAIL && + git checkout -b plain-x509 master && + echo aaa >bar && + git add bar && + git commit -m bar_commit && + git checkout -b tagged-x509 master && + echo bbb >baz && + git add baz && + git commit -m baz_commit && + git tag -s -m signed_tag_msg signed_tag_x509 && + git checkout plain-x509 && + git merge --no-ff -m msg signed_tag_x509 && + git log --graph --show-signature -n1 plain-x509 >actual && + grep "^|\\\ merged tag" actual && + grep "^| | gpgsm: Signature made" actual && + grep "^| | gpgsm: Good signature" actual +' + test_expect_success GPG '--no-show-signature overrides --show-signature' ' git log -1 --show-signature --no-show-signature signed >actual && ! grep "^gpg:" actual @@ -1625,9 +1661,8 @@ test_expect_success 'log diagnoses bogus HEAD' ' ' test_expect_success 'log does not default to HEAD when rev input is given' ' - >expect && git log --branches=does-not-exist >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'set up --source tests' ' diff --git a/t/t4210-log-i18n.sh b/t/t4210-log-i18n.sh index e585fe6129..7c519436ef 100755 --- a/t/t4210-log-i18n.sh +++ b/t/t4210-log-i18n.sh @@ -44,15 +44,13 @@ test_expect_success !MINGW 'log --grep searches in log output encoding (latin1)' ' test_expect_success !MINGW 'log --grep does not find non-reencoded values (utf8)' ' - >expect && git log --encoding=utf8 --format=%s --grep=$latin1_e >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'log --grep does not find non-reencoded values (latin1)' ' - >expect && git log --encoding=ISO-8859-1 --format=%s --grep=$utf8_e >actual && - test_cmp expect actual + test_must_be_empty actual ' test_done diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh index 2d22a17c4a..6ee4d3f2d9 100755 --- a/t/t5310-pack-bitmaps.sh +++ b/t/t5310-pack-bitmaps.sh @@ -309,9 +309,8 @@ test_expect_success 'pack reuse respects --honor-pack-keep' ' done && reusable_pack --honor-pack-keep >empty.pack && git index-pack empty.pack && - >expect && git show-index <empty.idx >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'pack reuse respects --local' ' @@ -319,17 +318,15 @@ test_expect_success 'pack reuse respects --local' ' test_when_finished "mv alt.git/objects/pack/* .git/objects/pack/" && reusable_pack --local >empty.pack && git index-pack empty.pack && - >expect && git show-index <empty.idx >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'pack reuse respects --incremental' ' reusable_pack --incremental >empty.pack && git index-pack empty.pack && - >expect && git show-index <empty.idx >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'truncated bitmap fails gracefully' ' diff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh index 4fe4ad9d61..f1708d415e 100755 --- a/t/t5313-pack-bounds-checks.sh +++ b/t/t5313-pack-bounds-checks.sh @@ -90,9 +90,8 @@ test_expect_success 'matched bogus object count' ' # Unlike above, we should notice early that the .idx is totally # bogus, and not even enumerate its contents. - >expect && git cat-file --batch-all-objects --batch-check >actual && - test_cmp expect actual && + test_must_be_empty actual && # But as before, we can do the same object-access checks. test_must_fail git cat-file blob $object && diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index fa03f56a20..8f945235e3 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -403,7 +403,7 @@ test_expect_success 'fetch creating new shallow root' ' git fetch --depth=1 --progress 2>actual && # This should fetch only the empty commit, no tree or # blob objects - grep "remote: Total 1" actual + test_i18ngrep "remote: Total 1" actual ) ' diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh index 49d3621a92..62f3569891 100755 --- a/t/t5504-fetch-receive-strict.sh +++ b/t/t5504-fetch-receive-strict.sh @@ -3,13 +3,16 @@ test_description='fetch/receive strict mode' . ./test-lib.sh -test_expect_success setup ' +test_expect_success 'setup and inject "corrupt or missing" object' ' echo hello >greetings && git add greetings && git commit -m greetings && S=$(git rev-parse :greetings | sed -e "s|^..|&/|") && X=$(echo bye | git hash-object -w --stdin | sed -e "s|^..|&/|") && + echo $S >S && + echo $X >X && + cp .git/objects/$S .git/objects/$S.back && mv -f .git/objects/$X .git/objects/$S && test_must_fail git fsck @@ -115,6 +118,13 @@ test_expect_success 'push with transfer.fsckobjects' ' test_cmp exp act ' +test_expect_success 'repair the "corrupt or missing" object' ' + mv -f .git/objects/$(cat S) .git/objects/$(cat X) && + mv .git/objects/$(cat S).back .git/objects/$(cat S) && + rm -rf .git/objects/$(cat X) && + git fsck +' + cat >bogus-commit <<EOF tree $EMPTY_TREE author Bugs Bunny 1234567890 +0000 @@ -123,6 +133,14 @@ committer Bugs Bunny <bugs@bun.ni> 1234567890 +0000 This commit object intentionally broken EOF +test_expect_success 'fsck with invalid or bogus skipList input' ' + git -c fsck.skipList=/dev/null -c fsck.missingEmail=ignore fsck && + test_must_fail git -c fsck.skipList=does-not-exist -c fsck.missingEmail=ignore fsck 2>err && + test_i18ngrep "Could not open skip list: does-not-exist" err && + test_must_fail git -c fsck.skipList=.git/config -c fsck.missingEmail=ignore fsck 2>err && + test_i18ngrep "Invalid SHA-1: \[core\]" err +' + test_expect_success 'push with receive.fsck.skipList' ' commit="$(git hash-object -t commit -w --stdin <bogus-commit)" && git push . $commit:refs/heads/bogus && @@ -130,11 +148,61 @@ test_expect_success 'push with receive.fsck.skipList' ' git init dst && git --git-dir=dst/.git config receive.fsckObjects true && test_must_fail git push --porcelain dst bogus && - git --git-dir=dst/.git config receive.fsck.skipList SKIP && echo $commit >dst/.git/SKIP && + + # receive.fsck.* does not fall back on fsck.* + git --git-dir=dst/.git config fsck.skipList SKIP && + test_must_fail git push --porcelain dst bogus && + + # Invalid and/or bogus skipList input + git --git-dir=dst/.git config receive.fsck.skipList /dev/null && + test_must_fail git push --porcelain dst bogus && + git --git-dir=dst/.git config receive.fsck.skipList does-not-exist && + test_must_fail git push --porcelain dst bogus 2>err && + test_i18ngrep "Could not open skip list: does-not-exist" err && + git --git-dir=dst/.git config receive.fsck.skipList config && + test_must_fail git push --porcelain dst bogus 2>err && + test_i18ngrep "Invalid SHA-1: \[core\]" err && + + git --git-dir=dst/.git config receive.fsck.skipList SKIP && git push --porcelain dst bogus ' +test_expect_success 'fetch with fetch.fsck.skipList' ' + commit="$(git hash-object -t commit -w --stdin <bogus-commit)" && + refspec=refs/heads/bogus:refs/heads/bogus && + git push . $commit:refs/heads/bogus && + rm -rf dst && + git init dst && + git --git-dir=dst/.git config fetch.fsckObjects true && + test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec && + git --git-dir=dst/.git config fetch.fsck.skipList /dev/null && + test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec && + echo $commit >dst/.git/SKIP && + + # fetch.fsck.* does not fall back on fsck.* + git --git-dir=dst/.git config fsck.skipList dst/.git/SKIP && + test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec && + + # Invalid and/or bogus skipList input + git --git-dir=dst/.git config fetch.fsck.skipList /dev/null && + test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec && + git --git-dir=dst/.git config fetch.fsck.skipList does-not-exist && + test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec 2>err && + test_i18ngrep "Could not open skip list: does-not-exist" err && + git --git-dir=dst/.git config fetch.fsck.skipList dst/.git/config && + test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec 2>err && + test_i18ngrep "Invalid SHA-1: \[core\]" err && + + git --git-dir=dst/.git config fetch.fsck.skipList dst/.git/SKIP && + git --git-dir=dst/.git fetch "file://$(pwd)" $refspec +' + +test_expect_success 'fsck.<unknownmsg-id> dies' ' + test_must_fail git -c fsck.whatEver=ignore fsck 2>err && + test_i18ngrep "Unhandled message id: whatever" err +' + test_expect_success 'push with receive.fsck.missingEmail=warn' ' commit="$(git hash-object -t commit -w --stdin <bogus-commit)" && git push . $commit:refs/heads/bogus && @@ -142,19 +210,58 @@ test_expect_success 'push with receive.fsck.missingEmail=warn' ' git init dst && git --git-dir=dst/.git config receive.fsckobjects true && test_must_fail git push --porcelain dst bogus && + + # receive.fsck.<msg-id> does not fall back on fsck.<msg-id> + git --git-dir=dst/.git config fsck.missingEmail warn && + test_must_fail git push --porcelain dst bogus && + + # receive.fsck.<unknownmsg-id> warns + git --git-dir=dst/.git config \ + receive.fsck.whatEver error && + git --git-dir=dst/.git config \ receive.fsck.missingEmail warn && git push --porcelain dst bogus >act 2>&1 && grep "missingEmail" act && + test_i18ngrep "Skipping unknown msg id.*whatever" act && git --git-dir=dst/.git branch -D bogus && git --git-dir=dst/.git config --add \ receive.fsck.missingEmail ignore && - git --git-dir=dst/.git config --add \ - receive.fsck.badDate warn && git push --porcelain dst bogus >act 2>&1 && ! grep "missingEmail" act ' +test_expect_success 'fetch with fetch.fsck.missingEmail=warn' ' + commit="$(git hash-object -t commit -w --stdin <bogus-commit)" && + refspec=refs/heads/bogus:refs/heads/bogus && + git push . $commit:refs/heads/bogus && + rm -rf dst && + git init dst && + git --git-dir=dst/.git config fetch.fsckobjects true && + test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec && + + # fetch.fsck.<msg-id> does not fall back on fsck.<msg-id> + git --git-dir=dst/.git config fsck.missingEmail warn && + test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" $refspec && + + # receive.fsck.<unknownmsg-id> warns + git --git-dir=dst/.git config \ + fetch.fsck.whatEver error && + + git --git-dir=dst/.git config \ + fetch.fsck.missingEmail warn && + git --git-dir=dst/.git fetch "file://$(pwd)" $refspec >act 2>&1 && + grep "missingEmail" act && + test_i18ngrep "Skipping unknown msg id.*whatever" act && + rm -rf dst && + git init dst && + git --git-dir=dst/.git config fetch.fsckobjects true && + git --git-dir=dst/.git config \ + fetch.fsck.missingEmail ignore && + git --git-dir=dst/.git fetch "file://$(pwd)" $refspec >act 2>&1 && + ! grep "missingEmail" act +' + test_expect_success \ 'receive.fsck.unterminatedHeader=warn triggers error' ' rm -rf dst && @@ -166,4 +273,15 @@ test_expect_success \ grep "Cannot demote unterminatedheader" act ' +test_expect_success \ + 'fetch.fsck.unterminatedHeader=warn triggers error' ' + rm -rf dst && + git init dst && + git --git-dir=dst/.git config fetch.fsckobjects true && + git --git-dir=dst/.git config \ + fetch.fsck.unterminatedheader warn && + test_must_fail git --git-dir=dst/.git fetch "file://$(pwd)" HEAD && + grep "Cannot demote unterminatedheader" act +' + test_done diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index 11e14a1e0f..241e6a319d 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -74,8 +74,7 @@ test_expect_success 'add another remote' ' git for-each-ref "--format=%(refname)" refs/remotes | sed -e "/^refs\/remotes\/origin\//d" \ -e "/^refs\/remotes\/second\//d" >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ) ' @@ -112,8 +111,7 @@ test_expect_success C_LOCALE_OUTPUT 'remove remote' ' check_remote_track origin master side && git for-each-ref "--format=%(refname)" refs/remotes | sed -e "/^refs\/remotes\/origin\//d" >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ) ' @@ -872,7 +870,7 @@ test_expect_success 'remote prune to cause a dangling symref' ' cd eight && test_must_fail git branch nomore origin ) 2>err && - grep "dangling symref" err + test_i18ngrep "dangling symref" err ' test_expect_success 'show empty remote' ' diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 62308be499..5e810f494e 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -535,6 +535,41 @@ test_expect_success "should be able to fetch with duplicate refspecs" ' ) ' +test_expect_success 'LHS of refspec follows ref disambiguation rules' ' + mkdir lhs-ambiguous && + ( + cd lhs-ambiguous && + git init server && + test_commit -C server unwanted && + test_commit -C server wanted && + + git init client && + + # Check a name coming after "refs" alphabetically ... + git -C server update-ref refs/heads/s wanted && + git -C server update-ref refs/heads/refs/heads/s unwanted && + git -C client fetch ../server +refs/heads/s:refs/heads/checkthis && + git -C server rev-parse wanted >expect && + git -C client rev-parse checkthis >actual && + test_cmp expect actual && + + # ... and one before. + git -C server update-ref refs/heads/q wanted && + git -C server update-ref refs/heads/refs/heads/q unwanted && + git -C client fetch ../server +refs/heads/q:refs/heads/checkthis && + git -C server rev-parse wanted >expect && + git -C client rev-parse checkthis >actual && + test_cmp expect actual && + + # Tags are preferred over branches like refs/{heads,tags}/* + git -C server update-ref refs/tags/t wanted && + git -C server update-ref refs/heads/t unwanted && + git -C client fetch ../server +t:refs/heads/checkthis && + git -C server rev-parse wanted >expect && + git -C client rev-parse checkthis >actual + ) +' + # configured prune tests set_config_tristate () { diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh index ea020040e8..bc5703ff9b 100755 --- a/t/t5512-ls-remote.sh +++ b/t/t5512-ls-remote.sh @@ -155,14 +155,12 @@ test_expect_success 'die with non-2 for wrong repository even with --exit-code' test_expect_success 'Report success even when nothing matches' ' git ls-remote other.git "refs/nsn/*" >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'Report no-match with --exit-code' ' test_expect_code 2 git ls-remote --exit-code other.git "refs/nsn/*" >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'Report match with --exit-code' ' diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh index 4b4b6673b8..0030c92e1a 100755 --- a/t/t5514-fetch-multiple.sh +++ b/t/t5514-fetch-multiple.sh @@ -152,7 +152,6 @@ test_expect_success 'git fetch --multiple (ignoring skipFetchAll)' ' ' test_expect_success 'git fetch --all --no-tags' ' - >expect && git clone one test5 && git clone test5 test6 && (cd test5 && git tag test-tag) && @@ -161,7 +160,7 @@ test_expect_success 'git fetch --all --no-tags' ' git fetch --all --no-tags && git tag >output ) && - test_cmp expect test6/output + test_must_be_empty test6/output ' test_expect_success 'git fetch --all --tags' ' diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index 68aa5f0340..5e501c8b08 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -475,10 +475,22 @@ test_expect_success 'pull.rebase=interactive' ' false EOF test_set_editor "$TRASH_DIRECTORY/fake-editor" && + test_when_finished "test_might_fail git rebase --abort" && test_must_fail git pull --rebase=interactive . copy && test "I was here" = "$(cat fake.out)" ' +test_expect_success 'pull --rebase=i' ' + write_script "$TRASH_DIRECTORY/fake-editor" <<-\EOF && + echo I was here, too >fake.out && + false + EOF + test_set_editor "$TRASH_DIRECTORY/fake-editor" && + test_when_finished "test_might_fail git rebase --abort" && + test_must_fail git pull --rebase=i . copy && + test "I was here, too" = "$(cat fake.out)" +' + test_expect_success 'pull.rebase=invalid fails' ' git reset --hard before-preserve-rebase && test_config pull.rebase invalid && diff --git a/t/t5533-push-cas.sh b/t/t5533-push-cas.sh index d38ecee217..0b0eb1d025 100755 --- a/t/t5533-push-cas.sh +++ b/t/t5533-push-cas.sh @@ -142,9 +142,8 @@ test_expect_success 'push to delete (protected, forced)' ' cd dst && git push --force --force-with-lease=master:master^ origin :master ) && - >expect && git ls-remote src refs/heads/master >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'push to delete (allowed)' ' @@ -154,9 +153,8 @@ test_expect_success 'push to delete (allowed)' ' git push --force-with-lease=master origin :master 2>err && grep deleted err ) && - >expect && git ls-remote src refs/heads/master >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'cover everything with default force-with-lease (protected)' ' diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh index 1cea758f78..030331f1c5 100755 --- a/t/t5534-push-signed.sh +++ b/t/t5534-push-signed.sh @@ -194,10 +194,12 @@ test_expect_success GPG 'fail without key and heed user.signingkey' ' EOF - unset GIT_COMMITTER_EMAIL && - git config user.email hasnokey@nowhere.com && - test_must_fail git push --signed dst noop ff +noff && - git config user.signingkey committer@example.com && + test_config user.email hasnokey@nowhere.com && + ( + sane_unset GIT_COMMITTER_EMAIL && + test_must_fail git push --signed dst noop ff +noff + ) && + test_config user.signingkey $GIT_COMMITTER_EMAIL && git push --signed dst noop ff +noff && ( @@ -218,4 +220,57 @@ test_expect_success GPG 'fail without key and heed user.signingkey' ' test_cmp expect dst/push-cert-status ' +test_expect_success GPGSM 'fail without key and heed user.signingkey x509' ' + test_config gpg.format x509 && + prepare_dst && + mkdir -p dst/.git/hooks && + git -C dst config receive.certnonceseed sekrit && + write_script dst/.git/hooks/post-receive <<-\EOF && + # discard the update list + cat >/dev/null + # record the push certificate + if test -n "${GIT_PUSH_CERT-}" + then + git cat-file blob $GIT_PUSH_CERT >../push-cert + fi && + + cat >../push-cert-status <<E_O_F + SIGNER=${GIT_PUSH_CERT_SIGNER-nobody} + KEY=${GIT_PUSH_CERT_KEY-nokey} + STATUS=${GIT_PUSH_CERT_STATUS-nostatus} + NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus} + NONCE=${GIT_PUSH_CERT_NONCE-nononce} + E_O_F + + EOF + + test_config user.email hasnokey@nowhere.com && + test_config user.signingkey "" && + ( + sane_unset GIT_COMMITTER_EMAIL && + test_must_fail git push --signed dst noop ff +noff + ) && + test_config user.signingkey $GIT_COMMITTER_EMAIL && + git push --signed dst noop ff +noff && + + ( + cat <<-\EOF && + SIGNER=/CN=C O Mitter/O=Example/SN=C O/GN=Mitter + KEY= + STATUS=G + NONCE_STATUS=OK + EOF + sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert + ) >expect.in && + key=$(cat "${GNUPGHOME}/trustlist.txt" | cut -d" " -f1 | tr -d ":") && + sed -e "s/^KEY=/KEY=${key}/" expect.in >expect && + + noop=$(git rev-parse noop) && + ff=$(git rev-parse ff) && + noff=$(git rev-parse noff) && + grep "$noop $ff refs/heads/ff" dst/push-cert && + grep "$noop $noff refs/heads/noff" dst/push-cert && + test_cmp expect dst/push-cert-status +' + test_done diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh index 3aab44bdcb..771f36f9ff 100755 --- a/t/t5551-http-fetch-smart.sh +++ b/t/t5551-http-fetch-smart.sh @@ -363,6 +363,24 @@ test_expect_success 'custom http headers' ' submodule update sub ' +test_expect_success 'using fetch command in remote-curl updates refs' ' + SERVER="$HTTPD_DOCUMENT_ROOT_PATH/twobranch" && + rm -rf "$SERVER" client && + + git init "$SERVER" && + test_commit -C "$SERVER" foo && + git -C "$SERVER" update-ref refs/heads/anotherbranch foo && + + git clone $HTTPD_URL/smart/twobranch client && + + test_commit -C "$SERVER" bar && + git -C client -c protocol.version=0 fetch && + + git -C "$SERVER" rev-parse master >expect && + git -C client rev-parse origin/master >actual && + test_cmp expect actual +' + test_expect_success 'GIT_REDACT_COOKIES redacts cookies' ' rm -rf clone && echo "Set-Cookie: Foo=1" >cookies && diff --git a/t/t5552-skipping-fetch-negotiator.sh b/t/t5552-skipping-fetch-negotiator.sh index 0a8e0e42ed..3b60bd44e0 100755 --- a/t/t5552-skipping-fetch-negotiator.sh +++ b/t/t5552-skipping-fetch-negotiator.sh @@ -47,6 +47,29 @@ test_expect_success 'commits with no parents are sent regardless of skip distanc have_not_sent c6 c4 c3 ' +test_expect_success 'unknown fetch.negotiationAlgorithm values error out' ' + rm -rf server client trace && + git init server && + test_commit -C server to_fetch && + + git init client && + test_commit -C client on_client && + git -C client checkout on_client && + + test_config -C client fetch.negotiationAlgorithm invalid && + test_must_fail git -C client fetch "$(pwd)/server" 2>err && + test_i18ngrep "unknown fetch negotiation algorithm" err && + + # Explicit "default" value + test_config -C client fetch.negotiationAlgorithm default && + git -C client -c fetch.negotiationAlgorithm=default fetch "$(pwd)/server" && + + # Implementation detail: If there is nothing to fetch, we will not error out + test_config -C client fetch.negotiationAlgorithm invalid && + git -C client fetch "$(pwd)/server" 2>err && + test_i18ngrep ! "unknown fetch negotiation algorithm" err +' + test_expect_success 'when two skips collide, favor the larger one' ' rm -rf server client trace && git init server && diff --git a/t/t5562-http-backend-content-length.sh b/t/t5562-http-backend-content-length.sh new file mode 100755 index 0000000000..43570ce120 --- /dev/null +++ b/t/t5562-http-backend-content-length.sh @@ -0,0 +1,156 @@ +#!/bin/sh + +test_description='test git-http-backend respects CONTENT_LENGTH' +. ./test-lib.sh + +test_lazy_prereq GZIP 'gzip --version' + +verify_http_result() { + # some fatal errors still produce status 200 + # so check if there is the error message + if grep 'fatal:' act.err + then + return 1 + fi + + if ! grep "Status" act.out >act + then + printf "Status: 200 OK\r\n" >act + fi + printf "Status: $1\r\n" >exp && + test_cmp exp act +} + +test_http_env() { + handler_type="$1" + request_body="$2" + shift + env \ + CONTENT_TYPE="application/x-git-$handler_type-pack-request" \ + QUERY_STRING="/repo.git/git-$handler_type-pack" \ + PATH_TRANSLATED="$PWD/.git/git-$handler_type-pack" \ + GIT_HTTP_EXPORT_ALL=TRUE \ + REQUEST_METHOD=POST \ + "$TEST_DIRECTORY"/t5562/invoke-with-content-length.pl \ + "$request_body" git http-backend >act.out 2>act.err +} + +ssize_b100dots() { + # hardcoded ((size_t) SSIZE_MAX) + 1 + case "$(build_option sizeof-size_t)" in + 8) echo 9223372036854775808;; + 4) echo 2147483648;; + *) die "Unexpected ssize_t size: $(build_option sizeof-size_t)";; + esac +} + +test_expect_success 'setup' ' + HTTP_CONTENT_ENCODING="identity" && + export HTTP_CONTENT_ENCODING && + git config http.receivepack true && + test_commit c0 && + test_commit c1 && + hash_head=$(git rev-parse HEAD) && + hash_prev=$(git rev-parse HEAD~1) && + printf "want %s" "$hash_head" | packetize >fetch_body && + printf 0000 >>fetch_body && + printf "have %s" "$hash_prev" | packetize >>fetch_body && + printf done | packetize >>fetch_body && + test_copy_bytes 10 <fetch_body >fetch_body.trunc && + hash_next=$(git commit-tree -p HEAD -m next HEAD^{tree}) && + printf "%s %s refs/heads/newbranch\\0report-status\\n" "$_z40" "$hash_next" | packetize >push_body && + printf 0000 >>push_body && + echo "$hash_next" | git pack-objects --stdout >>push_body && + test_copy_bytes 10 <push_body >push_body.trunc && + : >empty_body +' + +test_expect_success GZIP 'setup, compression related' ' + gzip -c fetch_body >fetch_body.gz && + test_copy_bytes 10 <fetch_body.gz >fetch_body.gz.trunc && + gzip -c push_body >push_body.gz && + test_copy_bytes 10 <push_body.gz >push_body.gz.trunc +' + +test_expect_success 'fetch plain' ' + test_http_env upload fetch_body && + verify_http_result "200 OK" +' + +test_expect_success 'fetch plain truncated' ' + test_http_env upload fetch_body.trunc && + ! verify_http_result "200 OK" +' + +test_expect_success 'fetch plain empty' ' + test_http_env upload empty_body && + ! verify_http_result "200 OK" +' + +test_expect_success GZIP 'fetch gzipped' ' + test_env HTTP_CONTENT_ENCODING="gzip" test_http_env upload fetch_body.gz && + verify_http_result "200 OK" +' + +test_expect_success GZIP 'fetch gzipped truncated' ' + test_env HTTP_CONTENT_ENCODING="gzip" test_http_env upload fetch_body.gz.trunc && + ! verify_http_result "200 OK" +' + +test_expect_success GZIP 'fetch gzipped empty' ' + test_env HTTP_CONTENT_ENCODING="gzip" test_http_env upload empty_body && + ! verify_http_result "200 OK" +' + +test_expect_success GZIP 'push plain' ' + test_when_finished "git branch -D newbranch" && + test_http_env receive push_body && + verify_http_result "200 OK" && + git rev-parse newbranch >act.head && + echo "$hash_next" >exp.head && + test_cmp act.head exp.head +' + +test_expect_success 'push plain truncated' ' + test_http_env receive push_body.trunc && + ! verify_http_result "200 OK" +' + +test_expect_success 'push plain empty' ' + test_http_env receive empty_body && + ! verify_http_result "200 OK" +' + +test_expect_success GZIP 'push gzipped' ' + test_when_finished "git branch -D newbranch" && + test_env HTTP_CONTENT_ENCODING="gzip" test_http_env receive push_body.gz && + verify_http_result "200 OK" && + git rev-parse newbranch >act.head && + echo "$hash_next" >exp.head && + test_cmp act.head exp.head +' + +test_expect_success GZIP 'push gzipped truncated' ' + test_env HTTP_CONTENT_ENCODING="gzip" test_http_env receive push_body.gz.trunc && + ! verify_http_result "200 OK" +' + +test_expect_success GZIP 'push gzipped empty' ' + test_env HTTP_CONTENT_ENCODING="gzip" test_http_env receive empty_body && + ! verify_http_result "200 OK" +' + +test_expect_success 'CONTENT_LENGTH overflow ssite_t' ' + NOT_FIT_IN_SSIZE=$(ssize_b100dots) && + env \ + CONTENT_TYPE=application/x-git-upload-pack-request \ + QUERY_STRING=/repo.git/git-upload-pack \ + PATH_TRANSLATED="$PWD"/.git/git-upload-pack \ + GIT_HTTP_EXPORT_ALL=TRUE \ + REQUEST_METHOD=POST \ + CONTENT_LENGTH="$NOT_FIT_IN_SSIZE" \ + git http-backend </dev/zero >/dev/null 2>err && + grep "fatal:.*CONTENT_LENGTH" err +' + +test_done diff --git a/t/t5562/invoke-with-content-length.pl b/t/t5562/invoke-with-content-length.pl new file mode 100755 index 0000000000..6c2aae7692 --- /dev/null +++ b/t/t5562/invoke-with-content-length.pl @@ -0,0 +1,37 @@ +#!/usr/bin/perl +use 5.008; +use strict; +use warnings; + +my $body_filename = $ARGV[0]; +my @command = @ARGV[1 .. $#ARGV]; + +# read data +my $body_size = -s $body_filename; +$ENV{"CONTENT_LENGTH"} = $body_size; +open(my $body_fh, "<", $body_filename) or die "Cannot open $body_filename: $!"; +my $body_data; +defined read($body_fh, $body_data, $body_size) or die "Cannot read $body_filename: $!"; +close($body_fh); + +my $exited = 0; +$SIG{"CHLD"} = sub { + $exited = 1; +}; + +# write data +my $pid = open(my $out, "|-", @command); +{ + # disable buffering at $out + my $old_selected = select; + select $out; + $| = 1; + select $old_selected; +} +print $out $body_data or die "Cannot write data: $!"; + +sleep 60; # is interrupted by SIGCHLD +if (!$exited) { + close($out); + die "Command did not exit after reading whole body"; +} diff --git a/t/t5570-git-daemon.sh b/t/t5570-git-daemon.sh index 0d4c52016b..a571f22bfd 100755 --- a/t/t5570-git-daemon.sh +++ b/t/t5570-git-daemon.sh @@ -7,9 +7,9 @@ test_description='test fetching over git protocol' start_git_daemon check_verbose_connect () { - grep -F "Looking up 127.0.0.1 ..." stderr && - grep -F "Connecting to 127.0.0.1 (port " stderr && - grep -F "done." stderr + test_i18ngrep -F "Looking up 127.0.0.1 ..." stderr && + test_i18ngrep -F "Connecting to 127.0.0.1 (port " stderr && + test_i18ngrep -F "done." stderr } test_expect_success 'setup repository' ' diff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh index fac5a73851..5582b3d5fd 100755 --- a/t/t5612-clone-refspec.sh +++ b/t/t5612-clone-refspec.sh @@ -97,8 +97,7 @@ test_expect_success 'clone with --no-tags' ' git fetch && git for-each-ref refs/tags >../actual ) && - >expect && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success '--single-branch while HEAD pointing at master' ' @@ -140,8 +139,7 @@ test_expect_success '--single-branch while HEAD pointing at master and --no-tags git fetch && git for-each-ref refs/tags >../actual ) && - >expect && - test_cmp expect actual && + test_must_be_empty actual && test_line_count = 0 actual && # get tags with --tags overrides tagOpt ( @@ -230,8 +228,7 @@ test_expect_success '--single-branch with detached' ' -e "s|/remotes/origin/|/heads/|" >../actual ) && # nothing - >expect && - test_cmp expect actual + test_must_be_empty actual ' test_done diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh index a4fe6508bd..3beeed4546 100755 --- a/t/t5702-protocol-v2.sh +++ b/t/t5702-protocol-v2.sh @@ -181,7 +181,12 @@ test_expect_success 'clone with file:// using protocol v2' ' test_cmp expect actual && # Server responded using protocol v2 - grep "clone< version 2" log + grep "clone< version 2" log && + + # Client sent ref-prefixes to filter the ref-advertisement + grep "ref-prefix HEAD" log && + grep "ref-prefix refs/heads/" log && + grep "ref-prefix refs/tags/" log ' test_expect_success 'fetch with file:// using protocol v2' ' @@ -204,6 +209,7 @@ test_expect_success 'ref advertisment is filtered during fetch using protocol v2 test_when_finished "rm -f log" && test_commit -C file_parent three && + git -C file_parent branch unwanted-branch three && GIT_TRACE_PACKET="$(pwd)/log" git -C file_child -c protocol.version=2 \ fetch origin master && @@ -212,9 +218,8 @@ test_expect_success 'ref advertisment is filtered during fetch using protocol v2 git -C file_parent log -1 --format=%s >expect && test_cmp expect actual && - ! grep "refs/tags/one" log && - ! grep "refs/tags/two" log && - ! grep "refs/tags/three" log + grep "refs/heads/master" log && + ! grep "refs/heads/unwanted-branch" log ' test_expect_success 'server-options are sent when fetching' ' @@ -359,6 +364,71 @@ test_expect_success 'default refspec is used to filter ref when fetchcing' ' grep "ref-prefix refs/tags/" log ' +test_expect_success 'fetch supports various ways of have lines' ' + rm -rf server client trace && + git init server && + test_commit -C server dwim && + TREE=$(git -C server rev-parse HEAD^{tree}) && + git -C server tag exact \ + $(git -C server commit-tree -m a "$TREE") && + git -C server tag dwim-unwanted \ + $(git -C server commit-tree -m b "$TREE") && + git -C server tag exact-unwanted \ + $(git -C server commit-tree -m c "$TREE") && + git -C server tag prefix1 \ + $(git -C server commit-tree -m d "$TREE") && + git -C server tag prefix2 \ + $(git -C server commit-tree -m e "$TREE") && + git -C server tag fetch-by-sha1 \ + $(git -C server commit-tree -m f "$TREE") && + git -C server tag completely-unrelated \ + $(git -C server commit-tree -m g "$TREE") && + + git init client && + GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \ + fetch "file://$(pwd)/server" \ + dwim \ + refs/tags/exact \ + refs/tags/prefix*:refs/tags/prefix* \ + "$(git -C server rev-parse fetch-by-sha1)" && + + # Ensure that the appropriate prefixes are sent (using a sample) + grep "fetch> ref-prefix dwim" trace && + grep "fetch> ref-prefix refs/heads/dwim" trace && + grep "fetch> ref-prefix refs/tags/prefix" trace && + + # Ensure that the correct objects are returned + git -C client cat-file -e $(git -C server rev-parse dwim) && + git -C client cat-file -e $(git -C server rev-parse exact) && + git -C client cat-file -e $(git -C server rev-parse prefix1) && + git -C client cat-file -e $(git -C server rev-parse prefix2) && + git -C client cat-file -e $(git -C server rev-parse fetch-by-sha1) && + test_must_fail git -C client cat-file -e \ + $(git -C server rev-parse dwim-unwanted) && + test_must_fail git -C client cat-file -e \ + $(git -C server rev-parse exact-unwanted) && + test_must_fail git -C client cat-file -e \ + $(git -C server rev-parse completely-unrelated) +' + +test_expect_success 'fetch supports include-tag and tag following' ' + rm -rf server client trace && + git init server && + + test_commit -C server to_fetch && + git -C server tag -a annotated_tag -m message && + + git init client && + GIT_TRACE_PACKET="$(pwd)/trace" git -C client -c protocol.version=2 \ + fetch "$(pwd)/server" to_fetch:to_fetch && + + grep "fetch> ref-prefix to_fetch" trace && + grep "fetch> ref-prefix refs/tags/" trace && + grep "fetch> include-tag" trace && + + git -C client cat-file -e $(git -C client rev-parse annotated_tag) +' + # Test protocol v2 with 'http://' transport # . "$TEST_DIRECTORY"/lib-httpd.sh diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh index ee5757966f..aaaa722cca 100755 --- a/t/t5801-remote-helpers.sh +++ b/t/t5801-remote-helpers.sh @@ -126,7 +126,7 @@ test_expect_success 'forced push' ' test_expect_success 'cloning without refspec' ' GIT_REMOTE_TESTGIT_REFSPEC="" \ git clone "testgit::${PWD}/server" local2 2>error && - grep "This remote helper should implement refspec capability" error && + test_i18ngrep "this remote helper should implement refspec capability" error && compare_refs local2 HEAD server HEAD ' @@ -134,7 +134,7 @@ test_expect_success 'pulling without refspecs' ' (cd local2 && git reset --hard && GIT_REMOTE_TESTGIT_REFSPEC="" git pull 2>../error) && - grep "This remote helper should implement refspec capability" error && + test_i18ngrep "this remote helper should implement refspec capability" error && compare_refs local2 HEAD server HEAD ' @@ -146,7 +146,7 @@ test_expect_success 'pushing without refspecs' ' GIT_REMOTE_TESTGIT_REFSPEC="" && export GIT_REMOTE_TESTGIT_REFSPEC && test_must_fail git push 2>../error) && - grep "remote-helper doesn.t support push; refspec needed" error + test_i18ngrep "remote-helper doesn.t support push; refspec needed" error ' test_expect_success 'pulling without marks' ' @@ -246,7 +246,7 @@ test_expect_success 'proper failure checks for fetching' ' (cd local && test_must_fail env GIT_REMOTE_TESTGIT_FAILURE=1 git fetch 2>error && cat error && - grep -q "Error while running fast-import" error + test_i18ngrep -q "error while running fast-import" error ) ' diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh index 969e4e9e52..fb4d295aa0 100755 --- a/t/t6000-rev-list-misc.sh +++ b/t/t6000-rev-list-misc.sh @@ -58,8 +58,7 @@ test_expect_success 'rev-list A..B and rev-list ^A B are the same' ' test_expect_success 'propagate uninteresting flag down correctly' ' git rev-list --objects ^HEAD^{tree} HEAD^{tree} >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'symleft flag bit is propagated down from tag' ' diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh index 20e3e2554a..916d9692bc 100755 --- a/t/t6009-rev-list-parent.sh +++ b/t/t6009-rev-list-parent.sh @@ -31,8 +31,7 @@ test_expect_success setup ' test_expect_success 'one is ancestor of others and should not be shown' ' git rev-list one --not four >result && - >expect && - test_cmp expect result + test_must_be_empty result ' @@ -144,8 +143,7 @@ test_expect_success 'ancestors with the same commit time' ' test_commit t$i done && git rev-list t1^! --not t$i >result && - >expect && - test_cmp expect result + test_must_be_empty result ' test_done diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh index d3453c583c..02936c2f24 100755 --- a/t/t6018-rev-list-glob.sh +++ b/t/t6018-rev-list-glob.sh @@ -256,31 +256,27 @@ test_expect_success 'rev-list accumulates multiple --exclude' ' ' test_expect_failure 'rev-list should succeed with empty output on empty stdin' ' - >expect && git rev-list --stdin <expect >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'rev-list should succeed with empty output with all refs excluded' ' - >expect && git rev-list --exclude=* --all >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'rev-list should succeed with empty output with empty --all' ' ( test_create_repo empty && cd empty && - >expect && git rev-list --all >actual && - test_cmp expect actual + test_must_be_empty actual ) ' test_expect_success 'rev-list should succeed with empty output with empty glob' ' - >expect && git rev-list --glob=does-not-match-anything >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'shortlog accepts --glob/--tags/--remotes' ' diff --git a/t/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh index dabebaee0b..beadaf6cca 100755 --- a/t/t6019-rev-list-ancestry-path.sh +++ b/t/t6019-rev-list-ancestry-path.sh @@ -95,10 +95,9 @@ test_expect_success 'rev-list --ancestry-path F...I' ' # G.t is dropped in an "-s ours" merge test_expect_success 'rev-list G..M -- G.t' ' - >expect && git rev-list --format=%s G..M -- G.t | sed -e "/^commit /d" >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'rev-list --ancestry-path G..M -- G.t' ' diff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh index 2af1beec5f..46b506b3b7 100755 --- a/t/t6020-merge-df.sh +++ b/t/t6020-merge-df.sh @@ -89,9 +89,6 @@ test_expect_success 'modify/delete + directory/file conflict' ' ' test_expect_success 'modify/delete + directory/file conflict; other way' ' - # Yes, we really need the double reset since "letters" appears as - # both a file and a directory. - git reset --hard && git reset --hard && git clean -f && git checkout modify^0 && diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh index b760c223c6..53cc9b2ffb 100755 --- a/t/t6022-merge-rename.sh +++ b/t/t6022-merge-rename.sh @@ -893,8 +893,7 @@ test_expect_success 'do not follow renames for empty files' ' git mv empty1 empty2 && git commit -m rename && test_must_fail git merge empty-base && - >expect && - test_cmp expect empty2 + test_must_be_empty empty2 ' test_done diff --git a/t/t6029-merge-subtree.sh b/t/t6029-merge-subtree.sh index 7d5bc78472..793f0c8bf3 100755 --- a/t/t6029-merge-subtree.sh +++ b/t/t6029-merge-subtree.sh @@ -29,6 +29,34 @@ test_expect_success 'subtree available and works like recursive' ' ' +test_expect_success 'setup branch sub' ' + git checkout --orphan sub && + git rm -rf . && + test_commit foo +' + +test_expect_success 'setup branch main' ' + git checkout -b main master && + git merge -s ours --no-commit --allow-unrelated-histories sub && + git read-tree --prefix=dir/ -u sub && + git commit -m "initial merge of sub into main" && + test_path_is_file dir/foo.t && + test_path_is_file hello +' + +test_expect_success 'update branch sub' ' + git checkout sub && + test_commit bar +' + +test_expect_success 'update branch main' ' + git checkout main && + git merge -s subtree sub -m "second merge of sub into main" && + test_path_is_file dir/bar.t && + test_path_is_file dir/foo.t && + test_path_is_file hello +' + test_expect_success 'setup' ' mkdir git-gui && cd git-gui && diff --git a/t/t6042-merge-rename-corner-cases.sh b/t/t6042-merge-rename-corner-cases.sh index 07dd09d985..b97aca7fa2 100755 --- a/t/t6042-merge-rename-corner-cases.sh +++ b/t/t6042-merge-rename-corner-cases.sh @@ -324,7 +324,6 @@ test_expect_success 'rename/directory conflict + content merge conflict' ' cd rename-directory-1 && git reset --hard && - git reset --hard && git clean -fdqx && git checkout left-conflict^0 && diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh index aa3e249639..86374a9c52 100755 --- a/t/t6050-replace.sh +++ b/t/t6050-replace.sh @@ -113,6 +113,12 @@ test_expect_success 'test GIT_NO_REPLACE_OBJECTS env variable' ' GIT_NO_REPLACE_OBJECTS=1 git show $HASH2 | grep "A U Thor" ' +test_expect_success 'test core.usereplacerefs config option' ' + test_config core.usereplacerefs false && + git cat-file commit $HASH2 | grep "author A U Thor" && + git show $HASH2 | grep "A U Thor" +' + cat >tag.sig <<EOF object $HASH2 type commit diff --git a/t/t6060-merge-index.sh b/t/t6060-merge-index.sh index debadbd299..ddf34f0115 100755 --- a/t/t6060-merge-index.sh +++ b/t/t6060-merge-index.sh @@ -44,8 +44,7 @@ test_expect_success 'read-tree does not resolve content merge' ' test_expect_success 'git merge-index git-merge-one-file resolves' ' git merge-index git-merge-one-file -a && git diff-files --name-only --diff-filter=U >unmerged && - >expect && - test_cmp expect unmerged && + test_must_be_empty unmerged && test_cmp expect-merged file && git cat-file blob :file >file-index && test_cmp expect-merged file-index diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index e0496da812..024f8c06f7 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -796,9 +796,8 @@ test_expect_success ':remotename and :remoteref' ' ' test_expect_success 'for-each-ref --ignore-case ignores case' ' - >expect && git for-each-ref --format="%(refname)" refs/heads/MASTER >actual && - test_cmp expect actual && + test_must_be_empty actual && echo refs/heads/master >expect && git for-each-ref --format="%(refname)" --ignore-case \ diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index d7b319e919..465eb4ea3f 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -693,9 +693,8 @@ test_expect_success \ ' test_expect_success 'The -n 100 invocation means -n --list 100, not -n100' ' - >expect && git tag -n 100 >actual && - test_cmp expect actual && + test_must_be_empty actual && git tag -m "A msg" 100 && echo "100 A msg" >expect && @@ -974,9 +973,8 @@ test_expect_success GPG 'verifying a proper tag with --format pass and format ac ' test_expect_success GPG 'verifying a forged tag with --format should fail silently' ' - >expect && test_must_fail git tag -v --format="tagname : %(tag)" "forged-tag" >actual && - test_cmp expect actual + test_must_be_empty actual ' # blank and empty messages for signed tags: @@ -1354,6 +1352,19 @@ test_expect_success GPG \ 'test_config gpg.program echo && test_must_fail git tag -s -m tail tag-gpg-failure' +# try to sign with bad user.signingkey +test_expect_success GPGSM \ + 'git tag -s fails if gpgsm is misconfigured (bad key)' \ + 'test_config user.signingkey BobTheMouse && + test_config gpg.format x509 && + test_must_fail git tag -s -m tail tag-gpg-failure' + +# try to produce invalid signature +test_expect_success GPGSM \ + 'git tag -s fails if gpgsm is misconfigured (bad signature format)' \ + 'test_config gpg.x509.program echo && + test_config gpg.format x509 && + test_must_fail git tag -s -m tail tag-gpg-failure' # try to verify without gpg: @@ -1382,9 +1393,8 @@ test_expect_success 'message in editor has initial comment: first line' ' test_expect_success \ 'message in editor has initial comment: remainder' ' # remove commented lines from the remainder -- should be empty - >rest.expect && sed -e 1d -e "/^#/d" <actual >rest.actual && - test_cmp rest.expect rest.actual + test_must_be_empty rest.actual ' get_tag_header reuse $commit commit $time >expect @@ -1466,19 +1476,18 @@ test_expect_success 'checking that first commit is in all tags (relative)' " # All the --contains tests above, but with --no-contains test_expect_success 'checking that first commit is not listed in any tag with --no-contains (hash)' " - >expected && git tag -l --no-contains $hash1 v* >actual && - test_cmp expected actual + test_must_be_empty actual " test_expect_success 'checking that first commit is in all tags (tag)' " git tag -l --no-contains v1.0 v* >actual && - test_cmp expected actual + test_must_be_empty actual " test_expect_success 'checking that first commit is in all tags (relative)' " git tag -l --no-contains HEAD~2 v* >actual && - test_cmp expected actual + test_must_be_empty actual " cat > expected <<EOF @@ -1606,9 +1615,8 @@ test_expect_success 'checking that --contains can be used in non-list mode' ' ' test_expect_success 'checking that initial commit is in all tags with --no-contains' " - >expected && git tag -l --no-contains $hash1 v* >actual && - test_cmp expected actual + test_must_be_empty actual " # mixing modes and options: @@ -1905,7 +1913,6 @@ test_expect_success 'version sort with very long prerelease suffix' ' ' test_expect_success ULIMIT_STACK_SIZE '--contains and --no-contains work in a deep repo' ' - >expect && i=1 && while test $i -lt 8000 do @@ -1920,7 +1927,7 @@ EOF" git checkout master && git tag far-far-away HEAD^ && run_with_limited_stack git tag --contains HEAD >actual && - test_cmp expect actual && + test_must_be_empty actual && run_with_limited_stack git tag --no-contains HEAD >actual && test_line_count "-gt" 10 actual ' diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh index 7541ba5edb..00e09a375c 100755 --- a/t/t7006-pager.sh +++ b/t/t7006-pager.sh @@ -626,12 +626,11 @@ test_expect_success TTY 'sub-commands of externals use their own pager' ' test_expect_success TTY 'external command pagers override sub-commands' ' sane_unset PAGER GIT_PAGER && - >expect && >actual && test_config pager.external false && test_config pager.log "sed s/^/log:/ >actual" && test_terminal git --exec-path=. external log --format=%s -1 && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'command with underscores does not complain' ' diff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh index 291a1e2b07..041e319e79 100755 --- a/t/t7030-verify-tag.sh +++ b/t/t7030-verify-tag.sh @@ -41,6 +41,13 @@ test_expect_success GPG 'create signed tags' ' git tag -uB7227189 -m eighth eighth-signed-alt ' +test_expect_success GPGSM 'create signed tags x509 ' ' + test_config gpg.format x509 && + test_config user.signingkey $GIT_COMMITTER_EMAIL && + echo 9 >file && test_tick && git commit -a -m "nineth gpgsm-signed" && + git tag -s -m nineth nineth-signed-x509 +' + test_expect_success GPG 'verify and show signatures' ' ( for tag in initial second merge fourth-signed sixth-signed seventh-signed @@ -72,6 +79,13 @@ test_expect_success GPG 'verify and show signatures' ' ) ' +test_expect_success GPGSM 'verify and show signatures x509' ' + git verify-tag nineth-signed-x509 2>actual && + grep "Good signature from" actual && + ! grep "BAD signature from" actual && + echo nineth-signed-x509 OK +' + test_expect_success GPG 'detect fudged signature' ' git cat-file tag seventh-signed >raw && sed -e "/^tag / s/seventh/7th forged/" raw >forged1 && @@ -112,6 +126,13 @@ test_expect_success GPG 'verify signatures with --raw' ' ) ' +test_expect_success GPGSM 'verify signatures with --raw x509' ' + git verify-tag --raw nineth-signed-x509 2>actual && + grep "GOODSIG" actual && + ! grep "BADSIG" actual && + echo nineth-signed-x509 OK +' + test_expect_success GPG 'verify multiple tags' ' tags="fourth-signed sixth-signed seventh-signed" && for i in $tags @@ -125,6 +146,19 @@ test_expect_success GPG 'verify multiple tags' ' test_cmp expect.stderr actual.stderr ' +test_expect_success GPGSM 'verify multiple tags x509' ' + tags="seventh-signed nineth-signed-x509" && + for i in $tags + do + git verify-tag -v --raw $i || return 1 + done >expect.stdout 2>expect.stderr.1 && + grep "^.GNUPG:." <expect.stderr.1 >expect.stderr && + git verify-tag -v --raw $tags >actual.stdout 2>actual.stderr.1 && + grep "^.GNUPG:." <actual.stderr.1 >actual.stderr && + test_cmp expect.stdout actual.stdout && + test_cmp expect.stderr actual.stderr +' + test_expect_success GPG 'verifying tag with --format' ' cat >expect <<-\EOF && tagname : fourth-signed @@ -134,9 +168,8 @@ test_expect_success GPG 'verifying tag with --format' ' ' test_expect_success GPG 'verifying a forged tag with --format should fail silently' ' - >expect && test_must_fail git verify-tag --format="tagname : %(tag)" $(cat forged1.tag) >actual-forged && - test_cmp expect actual-forged + test_must_be_empty actual-forged ' test_done diff --git a/t/t7063-status-untracked-cache.sh b/t/t7063-status-untracked-cache.sh index c61e304e97..2da57fce7b 100755 --- a/t/t7063-status-untracked-cache.sh +++ b/t/t7063-status-untracked-cache.sh @@ -26,9 +26,8 @@ avoid_racy() { } status_is_clean() { - >../status.expect && git status --porcelain >../status.actual && - test_cmp ../status.expect ../status.actual + test_must_be_empty ../status.actual } test_lazy_prereq UNTRACKED_CACHE ' @@ -666,7 +665,7 @@ test_expect_success 'test ident field is working' ' mkdir ../other_worktree && cp -R done dthree dtwo four three ../other_worktree && GIT_WORK_TREE=../other_worktree git status 2>../err && - echo "warning: Untracked cache is disabled on this system or location." >../expect && + echo "warning: untracked cache is disabled on this system or location" >../expect && test_i18ncmp ../expect ../err ' diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index 95653a08ca..97be0d968d 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -549,8 +549,7 @@ test_expect_success 'reset -N keeps removed files as intent-to-add' ' tree=$(git write-tree) && git ls-tree $tree new-file >actual && - >expect && - test_cmp expect actual && + test_must_be_empty actual && git diff --name-only >actual && echo new-file >expect && @@ -563,9 +562,8 @@ test_expect_success 'reset --mixed sets up work tree' ' cd mixed_worktree && test_commit dummy ) && - : >expect && git --git-dir=mixed_worktree/.git --work-tree=mixed_worktree reset >actual && - test_cmp expect actual + test_must_be_empty actual ' test_done diff --git a/t/t7106-reset-unborn-branch.sh b/t/t7106-reset-unborn-branch.sh index 0f95f00477..ecb85c3b82 100755 --- a/t/t7106-reset-unborn-branch.sh +++ b/t/t7106-reset-unborn-branch.sh @@ -12,9 +12,8 @@ test_expect_success 'reset' ' git add a b && git reset && - >expect && git ls-files >actual && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'reset HEAD' ' @@ -39,9 +38,8 @@ test_expect_success PERL 'reset -p' ' echo y >yes && git reset -p <yes >output && - >expect && git ls-files >actual && - test_cmp expect actual && + test_must_be_empty actual && test_i18ngrep "Unstage" output ' @@ -61,9 +59,8 @@ test_expect_success 'reset --hard' ' test_when_finished "echo a >a" && git reset --hard && - >expect && git ls-files >actual && - test_cmp expect actual && + test_must_be_empty actual && test_path_is_missing a ' diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index 2c2c97e144..2b71e62ec2 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -378,7 +378,7 @@ test_expect_success 'init should register submodule url in .git/config' ' test_failure_with_unknown_submodule () { test_must_fail git submodule $1 no-such-submodule 2>output.err && - grep "^error: .*no-such-submodule" output.err + test_i18ngrep "^error: .*no-such-submodule" output.err } test_expect_success 'init should fail with unknown submodule' ' diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh index 4e4c455502..1cd12b38c5 100755 --- a/t/t7401-submodule-summary.sh +++ b/t/t7401-submodule-summary.sh @@ -64,8 +64,7 @@ test_expect_success 'added submodule (subdirectory only)' " cd sub && git submodule summary . >../actual ) && - >expected && - test_cmp expected actual + test_must_be_empty actual " test_expect_success 'added submodule (subdirectory with explicit path)' " diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index 815b60cb59..10dc91620a 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -115,17 +115,17 @@ Submodule path '../super/submodule': checked out '$submodulesha1' EOF cat <<EOF >expect2 +Cloning into '$pwd/recursivesuper/super/merging'... +Cloning into '$pwd/recursivesuper/super/none'... +Cloning into '$pwd/recursivesuper/super/rebasing'... +Cloning into '$pwd/recursivesuper/super/submodule'... Submodule 'merging' ($pwd/merging) registered for path '../super/merging' Submodule 'none' ($pwd/none) registered for path '../super/none' Submodule 'rebasing' ($pwd/rebasing) registered for path '../super/rebasing' Submodule 'submodule' ($pwd/submodule) registered for path '../super/submodule' -Cloning into '$pwd/recursivesuper/super/merging'... done. -Cloning into '$pwd/recursivesuper/super/none'... done. -Cloning into '$pwd/recursivesuper/super/rebasing'... done. -Cloning into '$pwd/recursivesuper/super/submodule'... done. EOF @@ -137,7 +137,8 @@ test_expect_success 'submodule update --init --recursive from subdirectory' ' git submodule update --init --recursive ../super >../../actual 2>../../actual2 ) && test_i18ncmp expect actual && - test_i18ncmp expect2 actual2 + sort actual2 >actual2.sorted && + test_i18ncmp expect2 actual2.sorted ' cat <<EOF >expect2 diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh index d33a3cb331..ca4a740da0 100755 --- a/t/t7502-commit.sh +++ b/t/t7502-commit.sh @@ -393,7 +393,6 @@ EOF test_expect_success !AUTOIDENT 'do not fire editor when committer is bogus' ' >.git/result && - >expect && echo >>negative && ( @@ -403,7 +402,7 @@ test_expect_success !AUTOIDENT 'do not fire editor when committer is bogus' ' export GIT_EDITOR && test_must_fail git commit -e -m sample -a ) && - test_cmp expect .git/result + test_must_be_empty .git/result ' test_expect_success 'do not fire editor if -m <msg> was given' ' diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh index 6e2015ed9a..4e37ff8f16 100755 --- a/t/t7510-signed-commit.sh +++ b/t/t7510-signed-commit.sh @@ -227,4 +227,11 @@ test_expect_success GPG 'log.showsignature behaves like --show-signature' ' grep "gpg: Good signature" actual ' +test_expect_success GPG 'check config gpg.format values' ' + test_config gpg.format openpgp && + git commit -S --amend -m "success" && + test_config gpg.format OpEnPgP && + test_must_fail git commit -S --amend -m "fail" +' + test_done diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh index 047156e9d5..b18503de81 100755 --- a/t/t7610-mergetool.sh +++ b/t/t7610-mergetool.sh @@ -620,8 +620,7 @@ test_expect_success 'file with no base' ' git checkout -b test$test_count branch1 && test_must_fail git merge master && git mergetool --no-prompt --tool mybase -- both && - >expected && - test_cmp expected both + test_must_be_empty both ' test_expect_success 'custom commands override built-ins' ' diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh index dcaab1557b..d826e24b45 100755 --- a/t/t7810-grep.sh +++ b/t/t7810-grep.sh @@ -691,8 +691,7 @@ test_expect_success 'log grep (5)' ' test_expect_success 'log grep (6)' ' git log --author=-0700 --pretty=tformat:%s >actual && - >expect && - test_cmp expect actual + test_must_be_empty actual ' test_expect_success 'log grep (7)' ' diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index b8e919e25d..1ef1a19003 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -253,10 +253,9 @@ test_suppress_self () { mv msgtxt1 msgtxt1-$3 && sed -e '/^$/q' msgtxt1-$3 >"msghdr1-$3" && - >"expected-no-cc-$3" && (grep '^Cc:' msghdr1-$3 >"actual-no-cc-$3"; - test_cmp expected-no-cc-$3 actual-no-cc-$3) + test_must_be_empty actual-no-cc-$3) } test_suppress_self_unquoted () { diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index 9e7f96223d..40fe7e4976 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -2191,12 +2191,11 @@ test_expect_success 'R: --import-marks-if-exists' ' test_expect_success 'R: feature import-marks-if-exists' ' rm -f io.marks && - >expect && git fast-import --export-marks=io.marks <<-\EOF && feature import-marks-if-exists=not_io.marks EOF - test_cmp expect io.marks && + test_must_be_empty io.marks && blob=$(echo hi | git hash-object --stdin) && @@ -2227,13 +2226,11 @@ test_expect_success 'R: feature import-marks-if-exists' ' EOF test_cmp expect io.marks && - >expect && - git fast-import --import-marks-if-exists=not_io.marks \ --export-marks=io.marks <<-\EOF && feature import-marks-if-exists=io.marks EOF - test_cmp expect io.marks + test_must_be_empty io.marks ' test_expect_success 'R: import to output marks works without any content' ' @@ -3147,7 +3144,10 @@ background_import_then_checkpoint () { echo $! >V.pid # We don't mind if fast-import has already died by the time the test # ends. - test_when_finished "exec 8>&-; exec 9>&-; kill $(cat V.pid) || true" + test_when_finished " + exec 8>&-; exec 9>&-; + kill $(cat V.pid) && wait $(cat V.pid) + true" # Start in the background to ensure we adhere strictly to (blocking) # pipes writing sequence. We want to assume that the write below could diff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh index 4849edc4ef..729cd25770 100755 --- a/t/t9800-git-p4-basic.sh +++ b/t/t9800-git-p4-basic.sh @@ -261,6 +261,35 @@ test_expect_success 'unresolvable host in P4PORT should display error' ' ) ' +# Test following scenarios: +# - Without ".git/hooks/p4-pre-submit" , submit should continue +# - With the hook returning 0, submit should continue +# - With the hook returning 1, submit should abort +test_expect_success 'run hook p4-pre-submit before submit' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo "hello world" >hello.txt && + git add hello.txt && + git commit -m "add hello.txt" && + git config git-p4.skipSubmitEdit true && + git p4 submit --dry-run >out && + grep "Would apply" out && + mkdir -p .git/hooks && + write_script .git/hooks/p4-pre-submit <<-\EOF && + exit 0 + EOF + git p4 submit --dry-run >out && + grep "Would apply" out && + write_script .git/hooks/p4-pre-submit <<-\EOF && + exit 1 + EOF + test_must_fail git p4 submit --dry-run >errs 2>&1 && + ! grep "Would apply" errs + ) +' + test_expect_success 'submit from detached head' ' test_when_finished cleanup_git && git p4 clone --dest="$git" //depot && diff --git a/transport-helper.c b/transport-helper.c index eab7f47565..143ca008c8 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -48,7 +48,7 @@ static void sendline(struct helper_data *helper, struct strbuf *buffer) if (debug) fprintf(stderr, "Debug: Remote helper: -> %s", buffer->buf); if (write_in_full(helper->helper->in, buffer->buf, buffer->len) < 0) - die_errno("Full write to remote helper failed"); + die_errno(_("full write to remote helper failed")); } static int recvline_fh(FILE *helper, struct strbuf *buffer) @@ -77,7 +77,7 @@ static void write_constant(int fd, const char *str) if (debug) fprintf(stderr, "Debug: Remote helper: -> %s", str); if (write_in_full(fd, str, strlen(str)) < 0) - die_errno("Full write to remote helper failed"); + die_errno(_("full write to remote helper failed")); } static const char *remove_ext_force(const char *url) @@ -129,7 +129,7 @@ static struct child_process *get_helper(struct transport *transport) code = start_command(helper); if (code < 0 && errno == ENOENT) - die("Unable to find remote helper for '%s'", data->name); + die(_("unable to find remote helper for '%s'"), data->name); else if (code != 0) exit(code); @@ -145,7 +145,7 @@ static struct child_process *get_helper(struct transport *transport) */ duped = dup(helper->out); if (duped < 0) - die_errno("Can't dup helper output fd"); + die_errno(_("can't dup helper output fd")); data->out = xfdopen(duped, "r"); write_constant(helper->in, "capabilities\n"); @@ -196,13 +196,13 @@ static struct child_process *get_helper(struct transport *transport) } else if (starts_with(capname, "no-private-update")) { data->no_private_update = 1; } else if (mandatory) { - die("Unknown mandatory capability %s. This remote " - "helper probably needs newer version of Git.", + die(_("unknown mandatory capability %s; this remote " + "helper probably needs newer version of Git"), capname); } } if (!data->rs.nr && (data->import || data->bidi_import || data->export)) { - warning("This remote helper should implement refspec capability."); + warning(_("this remote helper should implement refspec capability")); } strbuf_release(&buf); if (debug) @@ -269,7 +269,7 @@ static int strbuf_set_helper_option(struct helper_data *data, else if (!strcmp(buf->buf, "unsupported")) ret = 1; else { - warning("%s unexpectedly said: '%s'", data->name, buf->buf); + warning(_("%s unexpectedly said: '%s'"), data->name, buf->buf); ret = 1; } return ret; @@ -398,7 +398,7 @@ static int fetch_with_fetch(struct transport *transport, if (starts_with(buf.buf, "lock ")) { const char *name = buf.buf + 5; if (transport->pack_lockfile) - warning("%s also locked %s", data->name, name); + warning(_("%s also locked %s"), data->name, name); else transport->pack_lockfile = xstrdup(name); } @@ -409,7 +409,7 @@ static int fetch_with_fetch(struct transport *transport, else if (!buf.len) break; else - warning("%s unexpectedly said: '%s'", data->name, buf.buf); + warning(_("%s unexpectedly said: '%s'"), data->name, buf.buf); } strbuf_release(&buf); return 0; @@ -476,7 +476,7 @@ static int fetch_with_import(struct transport *transport, get_helper(transport); if (get_importer(transport, &fastimport)) - die("Couldn't run fast-import"); + die(_("couldn't run fast-import")); for (i = 0; i < nr_heads; i++) { posn = to_fetch[i]; @@ -499,7 +499,7 @@ static int fetch_with_import(struct transport *transport, */ if (finish_command(&fastimport)) - die("Error while running fast-import"); + die(_("error while running fast-import")); /* * The fast-import stream of a remote helper that advertises @@ -528,7 +528,7 @@ static int fetch_with_import(struct transport *transport, private = xstrdup(name); if (private) { if (read_ref(private, &posn->old_oid) < 0) - die("Could not read ref %s", private); + die(_("could not read ref %s"), private); free(private); } } @@ -554,7 +554,7 @@ static int run_connect(struct transport *transport, struct strbuf *cmdbuf) */ duped = dup(helper->out); if (duped < 0) - die_errno("Can't dup helper output fd"); + die_errno(_("can't dup helper output fd")); input = xfdopen(duped, "r"); setvbuf(input, NULL, _IONBF, 0); @@ -573,8 +573,8 @@ static int run_connect(struct transport *transport, struct strbuf *cmdbuf) fprintf(stderr, "Debug: Falling back to dumb " "transport.\n"); } else { - die("Unknown response to connect: %s", - cmdbuf->buf); + die(_(_("unknown response to connect: %s")), + cmdbuf->buf); } fclose(input); @@ -595,9 +595,9 @@ static int process_connect_service(struct transport *transport, if (strcmp(name, exec)) { int r = set_helper_option(transport, "servpath", exec); if (r > 0) - warning("Setting remote service path not supported by protocol."); + warning(_("setting remote service path not supported by protocol")); else if (r < 0) - warning("Invalid remote service path."); + warning(_("invalid remote service path")); } if (data->connect) { @@ -640,10 +640,10 @@ static int connect_helper(struct transport *transport, const char *name, /* Get_helper so connect is inited. */ get_helper(transport); if (!data->connect) - die("Operation not supported by protocol."); + die(_("operation not supported by protocol")); if (!process_connect_service(transport, name, exec)) - die("Can't connect to subservice %s.", name); + die(_("can't connect to subservice %s"), name); fd[0] = data->helper->out; fd[1] = data->helper->in; @@ -651,16 +651,14 @@ static int connect_helper(struct transport *transport, const char *name, } static int fetch(struct transport *transport, - int nr_heads, struct ref **to_fetch, - struct ref **fetched_refs) + int nr_heads, struct ref **to_fetch) { struct helper_data *data = transport->data; int i, count; if (process_connect(transport, 0)) { do_take_over(transport); - return transport->vtable->fetch(transport, nr_heads, to_fetch, - fetched_refs); + return transport->vtable->fetch(transport, nr_heads, to_fetch); } count = 0; @@ -712,7 +710,7 @@ static int push_update_ref_status(struct strbuf *buf, status = REF_STATUS_REMOTE_REJECT; refname = buf->buf + 6; } else - die("expected ok/error, helper said '%s'", buf->buf); + die(_("expected ok/error, helper said '%s'"), buf->buf); msg = strchr(refname, ' '); if (msg) { @@ -765,7 +763,7 @@ static int push_update_ref_status(struct strbuf *buf, if (!*ref) *ref = find_ref_by_name(remote_refs, refname); if (!*ref) { - warning("helper reported unexpected status of %s", refname); + warning(_("helper reported unexpected status of %s"), refname); return 1; } @@ -826,20 +824,20 @@ static void set_common_push_options(struct transport *transport, { if (flags & TRANSPORT_PUSH_DRY_RUN) { if (set_helper_option(transport, "dry-run", "true") != 0) - die("helper %s does not support dry-run", name); + die(_("helper %s does not support dry-run"), name); } else if (flags & TRANSPORT_PUSH_CERT_ALWAYS) { if (set_helper_option(transport, TRANS_OPT_PUSH_CERT, "true") != 0) - die("helper %s does not support --signed", name); + die(_("helper %s does not support --signed"), name); } else if (flags & TRANSPORT_PUSH_CERT_IF_ASKED) { if (set_helper_option(transport, TRANS_OPT_PUSH_CERT, "if-asked") != 0) - die("helper %s does not support --signed=if-asked", name); + die(_("helper %s does not support --signed=if-asked"), name); } if (flags & TRANSPORT_PUSH_OPTIONS) { struct string_list_item *item; for_each_string_list_item(item, transport->push_options) if (set_helper_option(transport, "push-option", item->string) != 0) - die("helper %s does not support 'push-option'", name); + die(_("helper %s does not support 'push-option'"), name); } } @@ -931,12 +929,12 @@ static int push_refs_with_export(struct transport *transport, struct strbuf buf = STRBUF_INIT; if (!data->rs.nr) - die("remote-helper doesn't support push; refspec needed"); + die(_("remote-helper doesn't support push; refspec needed")); set_common_push_options(transport, data->name, flags); if (flags & TRANSPORT_PUSH_FORCE) { if (set_helper_option(transport, "force", "true") != 0) - warning("helper %s does not support 'force'", data->name); + warning(_("helper %s does not support 'force'"), data->name); } helper = get_helper(transport); @@ -983,12 +981,12 @@ static int push_refs_with_export(struct transport *transport, } if (get_exporter(transport, &exporter, &revlist_args)) - die("Couldn't run fast-export"); + die(_("couldn't run fast-export")); string_list_clear(&revlist_args, 1); if (finish_command(&exporter)) - die("Error while running fast-export"); + die(_("error while running fast-export")); if (push_update_refs_status(data, remote_refs, flags)) return 1; @@ -1012,8 +1010,9 @@ static int push_refs(struct transport *transport, } if (!remote_refs) { - fprintf(stderr, "No refs in common and none specified; doing nothing.\n" - "Perhaps you should specify a branch such as 'master'.\n"); + fprintf(stderr, + _("No refs in common and none specified; doing nothing.\n" + "Perhaps you should specify a branch such as 'master'.\n")); return 0; } @@ -1075,7 +1074,7 @@ static struct ref *get_refs_list(struct transport *transport, int for_push, eov = strchr(buf.buf, ' '); if (!eov) - die("Malformed response in ref list: %s", buf.buf); + die(_("malformed response in ref list: %s"), buf.buf); eon = strchr(eov + 1, ' '); *eov = '\0'; if (eon) @@ -1089,7 +1088,7 @@ static struct ref *get_refs_list(struct transport *transport, int for_push, if (has_attribute(eon + 1, "unchanged")) { (*tail)->status |= REF_STATUS_UPTODATE; if (read_ref((*tail)->name, &(*tail)->old_oid) < 0) - die(_("Could not read ref %s"), + die(_("could not read ref %s"), (*tail)->name); } } @@ -1228,7 +1227,7 @@ static int udt_do_read(struct unidirectional_transfer *t) bytes = read(t->src, t->buf + t->bufuse, BUFFERSIZE - t->bufuse); if (bytes < 0 && errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR) { - error_errno("read(%s) failed", t->src_name); + error_errno(_("read(%s) failed"), t->src_name); return -1; } else if (bytes == 0) { transfer_debug("%s EOF (with %i bytes in buffer)", @@ -1255,7 +1254,7 @@ static int udt_do_write(struct unidirectional_transfer *t) transfer_debug("%s is writable", t->dest_name); bytes = xwrite(t->dest, t->buf, t->bufuse); if (bytes < 0 && errno != EWOULDBLOCK) { - error_errno("write(%s) failed", t->dest_name); + error_errno(_("write(%s) failed"), t->dest_name); return -1; } else if (bytes > 0) { t->bufuse -= bytes; @@ -1304,11 +1303,11 @@ static int tloop_join(pthread_t thread, const char *name) void *tret; err = pthread_join(thread, &tret); if (!tret) { - error("%s thread failed", name); + error(_("%s thread failed"), name); return 1; } if (err) { - error("%s thread failed to join: %s", name, strerror(err)); + error(_("%s thread failed to join: %s"), name, strerror(err)); return 1; } return 0; @@ -1327,11 +1326,11 @@ static int tloop_spawnwait_tasks(struct bidirectional_transfer_state *s) err = pthread_create(>p_thread, NULL, udt_copy_task_routine, &s->gtp); if (err) - die("Can't start thread for copying data: %s", strerror(err)); + die(_("can't start thread for copying data: %s"), strerror(err)); err = pthread_create(&ptg_thread, NULL, udt_copy_task_routine, &s->ptg); if (err) - die("Can't start thread for copying data: %s", strerror(err)); + die(_("can't start thread for copying data: %s"), strerror(err)); ret |= tloop_join(gtp_thread, "Git to program copy"); ret |= tloop_join(ptg_thread, "Program to git copy"); @@ -1368,11 +1367,11 @@ static int tloop_join(pid_t pid, const char *name) { int tret; if (waitpid(pid, &tret, 0) < 0) { - error_errno("%s process failed to wait", name); + error_errno(_("%s process failed to wait"), name); return 1; } if (!WIFEXITED(tret) || WEXITSTATUS(tret)) { - error("%s process failed", name); + error(_("%s process failed"), name); return 1; } return 0; @@ -1390,7 +1389,7 @@ static int tloop_spawnwait_tasks(struct bidirectional_transfer_state *s) /* Fork thread #1: git to program. */ pid1 = fork(); if (pid1 < 0) - die_errno("Can't start thread for copying data"); + die_errno(_("can't start thread for copying data")); else if (pid1 == 0) { udt_kill_transfer(&s->ptg); exit(udt_copy_task_routine(&s->gtp) ? 0 : 1); @@ -1399,7 +1398,7 @@ static int tloop_spawnwait_tasks(struct bidirectional_transfer_state *s) /* Fork thread #2: program to git. */ pid2 = fork(); if (pid2 < 0) - die_errno("Can't start thread for copying data"); + die_errno(_("can't start thread for copying data")); else if (pid2 == 0) { udt_kill_transfer(&s->gtp); exit(udt_copy_task_routine(&s->ptg) ? 0 : 1); diff --git a/transport-internal.h b/transport-internal.h index eeb6c340e5..1cde6258a7 100644 --- a/transport-internal.h +++ b/transport-internal.h @@ -36,18 +36,11 @@ struct transport_vtable { * Fetch the objects for the given refs. Note that this gets * an array, and should ignore the list structure. * - * The transport *may* provide, in fetched_refs, the list of refs that - * it fetched. If the transport knows anything about the fetched refs - * that the caller does not know (for example, shallow status), it - * should provide that list of refs and include that information in the - * list. - * * If the transport did not get hashes for refs in * get_refs_list(), it should set the old_sha1 fields in the * provided refs now. **/ - int (*fetch)(struct transport *transport, int refs_nr, struct ref **refs, - struct ref **fetched_refs); + int (*fetch)(struct transport *transport, int refs_nr, struct ref **refs); /** * Push the objects and refs. Send the necessary objects, and diff --git a/transport.c b/transport.c index b64b7bcb86..06ffea2774 100644 --- a/transport.c +++ b/transport.c @@ -139,7 +139,7 @@ static struct ref *get_refs_from_bundle(struct transport *transport, close(data->fd); data->fd = read_bundle_header(transport->url, &data->header); if (data->fd < 0) - die ("Could not read bundle '%s'.", transport->url); + die(_("could not read bundle '%s'"), transport->url); for (i = 0; i < data->header.references.nr; i++) { struct ref_list_entry *e = data->header.references.list + i; struct ref *ref = alloc_ref(e->name); @@ -151,8 +151,7 @@ static struct ref *get_refs_from_bundle(struct transport *transport, } static int fetch_refs_from_bundle(struct transport *transport, - int nr_heads, struct ref **to_fetch, - struct ref **fetched_refs) + int nr_heads, struct ref **to_fetch) { struct bundle_transport_data *data = transport->data; return unbundle(&data->header, data->fd, @@ -288,8 +287,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus } static int fetch_refs_via_pack(struct transport *transport, - int nr_heads, struct ref **to_fetch, - struct ref **fetched_refs) + int nr_heads, struct ref **to_fetch) { int ret = 0; struct git_transport_data *data = transport->data; @@ -358,12 +356,8 @@ static int fetch_refs_via_pack(struct transport *transport, if (report_unmatched_refs(to_fetch, nr_heads)) ret = -1; - if (fetched_refs) - *fetched_refs = refs; - else - free_refs(refs); - free_refs(refs_tmp); + free_refs(refs); free(dest); return ret; } @@ -662,7 +656,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re switch (data->version) { case protocol_v2: - die("support for protocol v2 not implemented yet"); + die(_("support for protocol v2 not implemented yet")); break; case protocol_v1: case protocol_v0: @@ -788,7 +782,7 @@ static enum protocol_allow_config parse_protocol_config(const char *key, else if (!strcasecmp(value, "user")) return PROTOCOL_ALLOW_USER_ONLY; - die("unknown value for config '%s': %s", key, value); + die(_("unknown value for config '%s': %s"), key, value); } static enum protocol_allow_config get_protocol_config(const char *type) @@ -854,7 +848,7 @@ int is_transport_allowed(const char *type, int from_user) void transport_check_allowed(const char *type) { if (!is_transport_allowed(type, -1)) - die("transport '%s' not allowed", type); + die(_("transport '%s' not allowed"), type); } static struct transport_vtable bundle_vtable = { @@ -883,7 +877,7 @@ struct transport *transport_get(struct remote *remote, const char *url) ret->progress = isatty(2); if (!remote) - die("No remote provided to transport_get()"); + BUG("No remote provided to transport_get()"); ret->got_remote_refs = 0; ret->remote = remote; @@ -906,7 +900,7 @@ struct transport *transport_get(struct remote *remote, const char *url) if (helper) { transport_helper_init(ret, helper); } else if (starts_with(url, "rsync:")) { - die("git-over-rsync is no longer supported"); + die(_("git-over-rsync is no longer supported")); } else if (url_is_local_not_ssh(url) && is_file(url) && is_bundle(url, 1)) { struct bundle_transport_data *data = xcalloc(1, sizeof(*data)); transport_check_allowed("file"); @@ -1151,7 +1145,7 @@ int transport_push(struct transport *transport, transport->push_options, pretend)) { oid_array_clear(&commits); - die("Failed to push all needed submodules!"); + die(_("failed to push all needed submodules")); } oid_array_clear(&commits); } @@ -1223,31 +1217,19 @@ const struct ref *transport_get_remote_refs(struct transport *transport, return transport->remote_refs; } -int transport_fetch_refs(struct transport *transport, struct ref *refs, - struct ref **fetched_refs) +int transport_fetch_refs(struct transport *transport, struct ref *refs) { int rc; int nr_heads = 0, nr_alloc = 0, nr_refs = 0; struct ref **heads = NULL; - struct ref *nop_head = NULL, **nop_tail = &nop_head; struct ref *rm; for (rm = refs; rm; rm = rm->next) { nr_refs++; if (rm->peer_ref && !is_null_oid(&rm->old_oid) && - !oidcmp(&rm->peer_ref->old_oid, &rm->old_oid)) { - /* - * These need to be reported as fetched, but we don't - * actually need to fetch them. - */ - if (fetched_refs) { - struct ref *nop_ref = copy_ref(rm); - *nop_tail = nop_ref; - nop_tail = &nop_ref->next; - } + !oidcmp(&rm->peer_ref->old_oid, &rm->old_oid)) continue; - } ALLOC_GROW(heads, nr_heads + 1, nr_alloc); heads[nr_heads++] = rm; } @@ -1265,11 +1247,7 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs, heads[nr_heads++] = rm; } - rc = transport->vtable->fetch(transport, nr_heads, heads, fetched_refs); - if (fetched_refs && nop_head) { - *nop_tail = *fetched_refs; - *fetched_refs = nop_head; - } + rc = transport->vtable->fetch(transport, nr_heads, heads); free(heads); return rc; @@ -1289,7 +1267,7 @@ int transport_connect(struct transport *transport, const char *name, if (transport->vtable->connect) return transport->vtable->connect(transport, name, exec, fd); else - die("Operation not supported by protocol"); + die(_("operation not supported by protocol")); } int transport_disconnect(struct transport *transport) @@ -1371,7 +1349,7 @@ static void read_alternate_refs(const char *path, if (get_oid_hex(line.buf, &oid) || line.buf[GIT_SHA1_HEXSZ] != ' ') { - warning("invalid line while parsing alternate refs: %s", + warning(_("invalid line while parsing alternate refs: %s"), line.buf); break; } diff --git a/transport.h b/transport.h index 113530ea54..01e717c29e 100644 --- a/transport.h +++ b/transport.h @@ -239,8 +239,7 @@ int transport_push(struct transport *connection, const struct ref *transport_get_remote_refs(struct transport *transport, const struct argv_array *ref_prefixes); -int transport_fetch_refs(struct transport *transport, struct ref *refs, - struct ref **fetched_refs); +int transport_fetch_refs(struct transport *transport, struct ref *refs); void transport_unlock_pack(struct transport *transport); int transport_disconnect(struct transport *transport); char *transport_anonymize_url(const char *url); @@ -470,14 +470,14 @@ int utf8_fprintf(FILE *stream, const char *format, ...) #else typedef char * iconv_ibp; #endif -char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv, int *outsz_p) +char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv, size_t *outsz_p) { size_t outsz, outalloc; char *out, *outpos; iconv_ibp cp; outsz = insz; - outalloc = outsz + 1; /* for terminating NUL */ + outalloc = st_add(outsz, 1); /* for terminating NUL */ out = xmalloc(outalloc); outpos = out; cp = (iconv_ibp)in; @@ -497,7 +497,7 @@ char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv, int *outs * converting the rest. */ sofar = outpos - out; - outalloc = sofar + insz * 2 + 32; + outalloc = st_add3(sofar, st_mult(insz, 2), 32); out = xrealloc(out, outalloc); outpos = out + sofar; outsz = outalloc - sofar - 1; @@ -534,9 +534,9 @@ static const char *fallback_encoding(const char *name) return name; } -char *reencode_string_len(const char *in, int insz, +char *reencode_string_len(const char *in, size_t insz, const char *out_encoding, const char *in_encoding, - int *outsz) + size_t *outsz) { iconv_t conv; char *out; @@ -25,14 +25,14 @@ void strbuf_utf8_replace(struct strbuf *sb, int pos, int width, #ifndef NO_ICONV char *reencode_string_iconv(const char *in, size_t insz, - iconv_t conv, int *outsz); -char *reencode_string_len(const char *in, int insz, + iconv_t conv, size_t *outsz); +char *reencode_string_len(const char *in, size_t insz, const char *out_encoding, const char *in_encoding, - int *outsz); + size_t *outsz); #else -static inline char *reencode_string_len(const char *a, int b, - const char *c, const char *d, int *e) +static inline char *reencode_string_len(const char *a, size_t b, + const char *c, const char *d, size_t *e) { if (e) *e = 0; return NULL; } #endif diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c index 3e8aff92bc..1f1f4a3c78 100644 --- a/xdiff/xdiffi.c +++ b/xdiff/xdiffi.c @@ -575,6 +575,11 @@ static void measure_split(const xdfile_t *xdf, long split, #define INDENT_WEIGHT 60 /* + * How far do we slide a hunk at most? + */ +#define INDENT_HEURISTIC_MAX_SLIDING 100 + +/* * Compute a badness score for the hypothetical split whose measurements are * stored in m. The weight factors were determined empirically using the tools and * corpus described in @@ -886,7 +891,12 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { long shift, best_shift = -1; struct split_score best_score; - for (shift = earliest_end; shift <= g.end; shift++) { + shift = earliest_end; + if (g.end - groupsize - 1 > shift) + shift = g.end - groupsize - 1; + if (g.end - INDENT_HEURISTIC_MAX_SLIDING > shift) + shift = g.end - INDENT_HEURISTIC_MAX_SLIDING; + for (; shift <= g.end; shift++) { struct split_measurement m; struct split_score score = {0, 0}; diff --git a/xdiff/xhistogram.c b/xdiff/xhistogram.c index 73210cb6f3..ec85f5992b 100644 --- a/xdiff/xhistogram.c +++ b/xdiff/xhistogram.c @@ -233,54 +233,31 @@ static int try_lcs(struct histindex *index, struct region *lcs, int b_ptr, return b_next; } -static int find_lcs(struct histindex *index, struct region *lcs, - int line1, int count1, int line2, int count2) { - int b_ptr; - - if (scanA(index, line1, count1)) - return -1; - - index->cnt = index->max_chain_length + 1; - - for (b_ptr = line2; b_ptr <= LINE_END(2); ) - b_ptr = try_lcs(index, lcs, b_ptr, line1, count1, line2, count2); - - return index->has_common && index->max_chain_length < index->cnt; -} - -static int fall_back_to_classic_diff(struct histindex *index, +static int fall_back_to_classic_diff(xpparam_t const *xpp, xdfenv_t *env, int line1, int count1, int line2, int count2) { - xpparam_t xpp; - xpp.flags = index->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK; + xpparam_t xpparam; + xpparam.flags = xpp->flags & ~XDF_DIFF_ALGORITHM_MASK; - return xdl_fall_back_diff(index->env, &xpp, + return xdl_fall_back_diff(env, &xpparam, line1, count1, line2, count2); } -static int histogram_diff(xpparam_t const *xpp, xdfenv_t *env, - int line1, int count1, int line2, int count2) +static inline void free_index(struct histindex *index) { - struct histindex index; - struct region lcs; - int sz; - int result = -1; - - if (count1 <= 0 && count2 <= 0) - return 0; - - if (LINE_END(1) >= MAX_PTR) - return -1; + xdl_free(index->records); + xdl_free(index->line_map); + xdl_free(index->next_ptrs); + xdl_cha_free(&index->rcha); +} - if (!count1) { - while(count2--) - env->xdf2.rchg[line2++ - 1] = 1; - return 0; - } else if (!count2) { - while(count1--) - env->xdf1.rchg[line1++ - 1] = 1; - return 0; - } +static int find_lcs(xpparam_t const *xpp, xdfenv_t *env, + struct region *lcs, + int line1, int count1, int line2, int count2) +{ + int b_ptr; + int sz, ret = -1; + struct histindex index; memset(&index, 0, sizeof(index)); @@ -318,9 +295,55 @@ static int histogram_diff(xpparam_t const *xpp, xdfenv_t *env, index.ptr_shift = line1; index.max_chain_length = 64; + if (scanA(&index, line1, count1)) + goto cleanup; + + index.cnt = index.max_chain_length + 1; + + for (b_ptr = line2; b_ptr <= LINE_END(2); ) + b_ptr = try_lcs(&index, lcs, b_ptr, line1, count1, line2, count2); + + if (index.has_common && index.max_chain_length < index.cnt) + ret = 1; + else + ret = 0; + +cleanup: + free_index(&index); + return ret; +} + +static int histogram_diff(xpparam_t const *xpp, xdfenv_t *env, + int line1, int count1, int line2, int count2) +{ + struct region lcs; + int lcs_found; + int result; +redo: + result = -1; + + if (count1 <= 0 && count2 <= 0) + return 0; + + if (LINE_END(1) >= MAX_PTR) + return -1; + + if (!count1) { + while(count2--) + env->xdf2.rchg[line2++ - 1] = 1; + return 0; + } else if (!count2) { + while(count1--) + env->xdf1.rchg[line1++ - 1] = 1; + return 0; + } + memset(&lcs, 0, sizeof(lcs)); - if (find_lcs(&index, &lcs, line1, count1, line2, count2)) - result = fall_back_to_classic_diff(&index, line1, count1, line2, count2); + lcs_found = find_lcs(xpp, env, &lcs, line1, count1, line2, count2); + if (lcs_found < 0) + goto out; + else if (lcs_found) + result = fall_back_to_classic_diff(xpp, env, line1, count1, line2, count2); else { if (lcs.begin1 == 0 && lcs.begin2 == 0) { while (count1--) @@ -333,21 +356,21 @@ static int histogram_diff(xpparam_t const *xpp, xdfenv_t *env, line1, lcs.begin1 - line1, line2, lcs.begin2 - line2); if (result) - goto cleanup; - result = histogram_diff(xpp, env, - lcs.end1 + 1, LINE_END(1) - lcs.end1, - lcs.end2 + 1, LINE_END(2) - lcs.end2); - if (result) - goto cleanup; + goto out; + /* + * result = histogram_diff(xpp, env, + * lcs.end1 + 1, LINE_END(1) - lcs.end1, + * lcs.end2 + 1, LINE_END(2) - lcs.end2); + * but let's optimize tail recursion ourself: + */ + count1 = LINE_END(1) - lcs.end1; + line1 = lcs.end1 + 1; + count2 = LINE_END(2) - lcs.end2; + line2 = lcs.end2 + 1; + goto redo; } } - -cleanup: - xdl_free(index.records); - xdl_free(index.line_map); - xdl_free(index.next_ptrs); - xdl_cha_free(&index.rcha); - +out: return result; } |