diff options
322 files changed, 9597 insertions, 7427 deletions
diff --git a/.editorconfig b/.editorconfig index 6e4eaa8e95..82e121a417 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,7 +4,11 @@ insert_final_newline = true # The settings for C (*.c and *.h) files are mirrored in .clang-format. Keep # them in sync. -[{*.{c,h,sh,bash,perl,pl,pm,txt,adoc},config.mak.*,Makefile,templates/hooks/*.sample}] +[{*.{c,h,sh,bash,perl,pl,pm,txt,adoc},config.mak.*,Makefile}] +indent_style = tab +tab_width = 8 + +[templates/hooks/*.sample] indent_style = tab tab_width = 8 diff --git a/Documentation/RelNotes/2.54.0.adoc b/Documentation/RelNotes/2.54.0.adoc index d7c67cbb07..c692dddb4a 100644 --- a/Documentation/RelNotes/2.54.0.adoc +++ b/Documentation/RelNotes/2.54.0.adoc @@ -88,6 +88,39 @@ UI, Workflows & Features * "git repo structure" command learns to report maximum values on various aspects of objects it inspects. + * "git rebase" learns "--trailer" command to drive the + interpret-trailers machinery. + + * "git fast-import" learned to optionally replace signature on + commits whose signatures get invalidated due to replaying by + signing afresh. + + * "git history" learned the "split" subcommand. + + * The reference-transaction hook was taught to be triggered before + taking locks on references in the "preparing" phase. + + * "git apply" now reports the name of the input file along with the + line number when it encounters a corrupt patch, and correctly + resets the line counter when processing multiple patch files. + + * The HTTP transport learned to react to "429 Too Many Requests". + + * "git repo info -h" and "git repo structure -h" limit their help output + to the part that is specific to the subcommand. + + * "git format-patch --cover-letter" learns to use a simpler format + instead of the traditional shortlog format to list its commits with + a new --commit-list-format option and format.commitListFormat + configuration variable. + + * `git backfill` learned to accept revision and pathspec arguments. + + * "git replay" (experimental) learns, in addition to "pick" and + "replay", a new operating mode "revert". + + * git replay now supports replaying down to the root commit. + Performance, Internal Implementation, Development Support etc. -------------------------------------------------------------- @@ -101,7 +134,7 @@ Performance, Internal Implementation, Development Support etc. * Improve set-up time of a perf test. - * ISO C23 redefines strchr and friends that tradiotionally took + * ISO C23 redefines strchr and friends that traditionally took a const pointer and returned a non-const pointer derived from it to preserve constness (i.e., if you ask for a substring in a const string, you get a const pointer to the substring). Update code @@ -193,6 +226,80 @@ Performance, Internal Implementation, Development Support etc. * Reduce dependence on the global the_hash_algo and the_repository variables of wt-status code path. + * The way combined list-object filter options are parsed has been + revamped. + + * Editorconfig filename patterns were specified incorrectly, making + many source files inside subdirectories unaffected, which has been + corrected. + + * The run_command() API lost its implicit dependency on the singleton + `the_repository` instance. + + * The unit test helper function was taught to use backslash + + mnemonic notation for certain control characters like "\t", instead + of octal notation like "\011". + + * Adjust test-lint to allow "sed -E" to use ERE in the patterns. + + * Clar (unit testing framework) update from the upstream. + + * Reduce system overhead "git upload-pack" spends on relaying "git + pack-objects" output to the "git fetch" running on the other end of + the connection. + + * Add a coccinelle rule to break the build when "struct strbuf" gets + passed by value. + + * Further work on incremental repacking using MIDX/bitmap + + * The logic to count objects has been cleaned up. + + * Tweak the build infrastructure by moving tools around. + + * Uses of prio_queue as a LIFO stack of commits have been written + with commit_stack. + + * The cleanup of remaining bitmaps in "ahead_behind()" has been + simplified. + + * split-index.c has been updated to not use the global the_repository + and the_hash_algo variables. + + * The unsigned integer that is used as an bitset to specify the kind + of branches interpret_branch_name() function has been changed to + use a dedicated enum type. + + * Various updates to contrib/diff-highlight, including documentation + updates, test improvements, and color configuration handling. + + * Code paths that loop over another array to push each element into a + strvec have been rewritten to use strvec_pushv() instead. + + * In case homebrew breaks REG_ENHANCED again, leave a in-code comment + to suggest use of our replacement regex as a workaround. + + * MinGW build updates. + + * The way dash 0.5.13 handles non-ASCII contents in here-doc + is buggy and breaks our existing tests, which unfortunately + have been rewritten to avoid triggering the bug. + + * Object name handling (disambiguation and abbreviation) has been + refactored to be backend-generic, moving logic into the respective + object database backends. + + * pack-objects's --stdin-packs=follow mode learns to handle + excluded-but-open packs. + + * A few code paths that spawned child processes for network + connection weren't wait(2)ing for their children and letting "init" + reap them instead; they have been tightened. + + * Adjust the codebase for C23 that changes functions like strchr() + that discarded constness when they return a pointer into a const + string to preserve constness. + Fixes since v2.53 ----------------- @@ -323,6 +430,54 @@ Fixes since v2.53 * Plug a few leaks where mmap'ed memory regions are not unmapped. (merge a8a69bbb64 jk/unleak-mmap later to maint). + * A test now uses the symbolic constant $ZERO_OID instead of 40 "0" to + work better with SHA-256 as well as SHA-1. + (merge 30310f3cc4 ss/t3200-test-zero-oid later to maint). + + * Instead of hardcoded 'origin', use the configured default remote + when fetching from submodules. + (merge 3b5fb32da8 ng/submodule-default-remote later to maint). + + * The code in "git help" that shows configuration items in sorted + order was awkwardly organized and prone to bugs. + + * "imap-send" used to use functions whose use is going to be removed + with OpenSSL 4.0; rewrite them using public API that has been + available since OpenSSL 1.1 since 2016 or so. + (merge 6392a0b75d bb/imap-send-openssl-4.0-prep later to maint). + + * Fix an example in the user-manual. + (merge 5514f14617 gj/user-manual-fix-grep-example later to maint). + + * The final clean-up phase of the diff output could turn the result of + histogram diff algorithm suboptimal, which has been corrected. + (merge e417277ae9 yc/histogram-hunk-shift-fix later to maint). + + * "git diff -U<num>" was too lenient in its command line parsing and + took an empty string as a valid <num>. + (merge 4f6a803aba ty/doc-diff-u-wo-number later to maint). + + * The handling of the incomplete lines at the end by "git + diff-highlight" has been fixed. + + * merge-file --object-id used to trigger a BUG when run in a linked + worktree, which has been fixed. + (merge 57246b7c62 mr/merge-file-object-id-worktree-fix later to maint). + + * "git apply -p<n>" parses <n> more carefully now. + (merge d05d84c5f5 mf/apply-p-no-atoi later to maint). + + * A test to run a .bat file with whitespaces in the name with arguments + with whitespaces in them was flaky in that sometimes it got killed + before it produced expected side effects, which has been rewritten to + make it more robust. + (merge 3ad4921838 jk/t0061-bat-test-update later to maint). + + * "git ls-remote '+refs/tags/*:refs/tags/*' https://..." run outside a + repository would dereference a NULL while trying to see if the given + refspec is a single-object refspec, which has been corrected. + (merge 4e5dc601dd kj/refspec-parsing-outside-repository later to maint). + * Other code cleanup, docfix, build fix, etc. (merge d79fff4a11 jk/remote-tracking-ref-leakfix later to maint). (merge 7a747f972d dd/t5403-modernise later to maint). @@ -362,3 +517,18 @@ Fixes since v2.53 (merge 63c00a677b ss/t9123-setup-inside-test-expect-success later to maint). (merge beca0ca4be os/doc-git-custom-commands later to maint). (merge 4c223571be ty/patch-ids-document-lazy-eval later to maint). + (merge 476365ac85 jc/doc-wholesale-replace-before-next later to maint). + (merge 35f220b639 ss/submodule--helper-use-xmalloc later to maint). + (merge 02cbae61df cf/constness-fixes later to maint). + (merge 69efd53c81 ms/t7605-test-path-is-helpers later to maint). + (merge d39cef3a1a ss/t0410-delete-object-cleanup later to maint). + (merge 2f05039717 rj/pack-refs-tests-path-is-helpers later to maint). + (merge 2594747ad1 jk/transport-color-leakfix later to maint). + (merge 48430e44ac mf/t0008-cleanup later to maint). + (merge fc8a4f15e7 gi/doc-boolean-config-typofix later to maint). + (merge 37182267a0 kh/doc-interpret-trailers-1 later to maint). + (merge f64c50e768 jc/rerere-modern-strbuf-handling later to maint). + (merge 699248d89e th/t8003-unhide-git-failures later to maint). + (merge d8e34f971b za/t2000-modernise later to maint). + (merge 849988bc74 th/t6101-unhide-git-failures later to maint). + (merge 0f0ce07625 sp/doc-gitignore-oowt later to maint). diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 359f5fb74e..d570184ec8 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -56,6 +56,18 @@ feedback before sending a new version, rather than sending an updated series immediately after receiving a review. This helps collect broader input and avoids unnecessary churn from many rapid iterations. +. These early update iterations are expected to be full replacements, + not incremental updates on top of what you posted already. If you + are correcting mistakes you made in the previous iteration that a + reviewer noticed and pointed out in their review, you _fix_ that + mistake by rewriting your history (e.g., by using "git rebase -i") + to pretend that you never made the mistake in the first place. In + other words, this is a chance to pretend to be a perfect developer, + and you are expected to take advantage of that. In the larger + picture, nobody is interested in your earlier mistakes. Just + present a logical progression made by a perfect developer who makes + no mistakes while working on the topic. + . Polish, refine, and re-send your patches to the list and to the people who spent their time to improve your patch. Go back to step (2). diff --git a/Documentation/config/format.adoc b/Documentation/config/format.adoc index ab0710e86a..dbd186290b 100644 --- a/Documentation/config/format.adoc +++ b/Documentation/config/format.adoc @@ -101,6 +101,11 @@ format.coverLetter:: generate a cover-letter only when there's more than one patch. Default is false. +format.commitListFormat:: + When the `--cover-letter-format` option is not given, `format-patch` + uses the value of this variable to decide how to format the entry of + each commit. Defaults to `shortlog`. + format.outputDirectory:: Set a custom directory to store the resulting files instead of the current working directory. All directory components will be created. diff --git a/Documentation/config/hook.adoc b/Documentation/config/hook.adoc index 64e845a260..9e78f26439 100644 --- a/Documentation/config/hook.adoc +++ b/Documentation/config/hook.adoc @@ -1,23 +1,23 @@ -hook.<name>.command:: - The command to execute for `hook.<name>`. `<name>` is a unique - "friendly" name that identifies this hook. (The hook events that - trigger the command are configured with `hook.<name>.event`.) The - value can be an executable path or a shell oneliner. If more than - one value is specified for the same `<name>`, only the last value - parsed is used. See linkgit:git-hook[1]. +hook.<friendly-name>.command:: + The command to execute for `hook.<friendly-name>`. `<friendly-name>` + is a unique name that identifies this hook. The hook events that + trigger the command are configured with `hook.<friendly-name>.event`. + The value can be an executable path or a shell oneliner. If more than + one value is specified for the same `<friendly-name>`, only the last + value parsed is used. See linkgit:git-hook[1]. -hook.<name>.event:: - The hook events that trigger `hook.<name>`. The value is the name - of a hook event, like "pre-commit" or "update". (See +hook.<friendly-name>.event:: + The hook events that trigger `hook.<friendly-name>`. The value is the + name of a hook event, like "pre-commit" or "update". (See linkgit:githooks[5] for a complete list of hook events.) On the - specified event, the associated `hook.<name>.command` is executed. - This is a multi-valued key. To run `hook.<name>` on multiple + specified event, the associated `hook.<friendly-name>.command` is executed. + This is a multi-valued key. To run `hook.<friendly-name>` on multiple events, specify the key more than once. An empty value resets the list of events, clearing any previously defined events for - `hook.<name>`. See linkgit:git-hook[1]. + `hook.<friendly-name>`. See linkgit:git-hook[1]. -hook.<name>.enabled:: - Whether the hook `hook.<name>` is enabled. Defaults to `true`. +hook.<friendly-name>.enabled:: + Whether the hook `hook.<friendly-name>` is enabled. Defaults to `true`. Set to `false` to disable the hook without removing its configuration. This is particularly useful when a hook is defined in a system or global config file and needs to be disabled for a diff --git a/Documentation/config/http.adoc b/Documentation/config/http.adoc index 9da5c298cc..849c89f36c 100644 --- a/Documentation/config/http.adoc +++ b/Documentation/config/http.adoc @@ -315,6 +315,32 @@ http.keepAliveCount:: unset, curl's default value is used. Can be overridden by the `GIT_HTTP_KEEPALIVE_COUNT` environment variable. +http.retryAfter:: + Default wait time in seconds before retrying when a server returns + HTTP 429 (Too Many Requests) without a Retry-After header. + Defaults to 0 (retry immediately). When a Retry-After header is + present, its value takes precedence over this setting; however, + automatic use of the server-provided `Retry-After` header requires + libcurl 7.66.0 or later. On older versions, configure this setting + manually to control the retry delay. Can be overridden by the + `GIT_HTTP_RETRY_AFTER` environment variable. + See also `http.maxRetries` and `http.maxRetryTime`. + +http.maxRetries:: + Maximum number of times to retry after receiving HTTP 429 (Too Many + Requests) responses. Set to 0 (the default) to disable retries. + Can be overridden by the `GIT_HTTP_MAX_RETRIES` environment variable. + See also `http.retryAfter` and `http.maxRetryTime`. + +http.maxRetryTime:: + Maximum time in seconds to wait for a single retry attempt when + handling HTTP 429 (Too Many Requests) responses. If the server + requests a delay (via Retry-After header) or if `http.retryAfter` + is configured with a value that exceeds this maximum, Git will fail + immediately rather than waiting. Default is 300 seconds (5 minutes). + Can be overridden by the `GIT_HTTP_MAX_RETRY_TIME` environment + variable. See also `http.retryAfter` and `http.maxRetries`. + http.noEPSV:: A boolean which disables using of EPSV ftp command by curl. This can be helpful with some "poor" ftp servers which don't diff --git a/Documentation/config/trailer.adoc b/Documentation/config/trailer.adoc index 60bc221c88..1bc70192d3 100644 --- a/Documentation/config/trailer.adoc +++ b/Documentation/config/trailer.adoc @@ -1,21 +1,21 @@ -trailer.separators:: +`trailer.separators`:: This option tells which characters are recognized as trailer - separators. By default only ':' is recognized as a trailer - separator, except that '=' is always accepted on the command + separators. By default only `:` is recognized as a trailer + separator, except that `=` is always accepted on the command line for compatibility with other git commands. + The first character given by this option will be the default character used when another separator is not specified in the config for this trailer. + -For example, if the value for this option is "%=$", then only lines -using the format '<key><sep><value>' with <sep> containing '%', '=' -or '$' and then spaces will be considered trailers. And '%' will be +For example, if the value for this option is `%=$`, then only lines +using the format _<key><sep><value>_ with _<sep>_ containing `%`, `=` +or `$` and then spaces will be considered trailers. And `%` will be the default separator used, so by default trailers will appear like: -'<key>% <value>' (one percent sign and one space will appear between +`<key>% <value>` (one percent sign and one space will appear between the key and the value). -trailer.where:: +`trailer.where`:: This option tells where a new trailer will be added. + This can be `end`, which is the default, `start`, `after` or `before`. @@ -27,41 +27,41 @@ If it is `start`, then each new trailer will appear at the start, instead of the end, of the existing trailers. + If it is `after`, then each new trailer will appear just after the -last trailer with the same <key>. +last trailer with the same _<key>_. + If it is `before`, then each new trailer will appear just before the -first trailer with the same <key>. +first trailer with the same _<key>_. -trailer.ifexists:: +`trailer.ifexists`:: This option makes it possible to choose what action will be performed when there is already at least one trailer with the - same <key> in the input. + same _<key>_ in the input. + The valid values for this option are: `addIfDifferentNeighbor` (this is the default), `addIfDifferent`, `add`, `replace` or `doNothing`. + With `addIfDifferentNeighbor`, a new trailer will be added only if no -trailer with the same (<key>, <value>) pair is above or below the line +trailer with the same (_<key>_, _<value>_) pair is above or below the line where the new trailer will be added. + With `addIfDifferent`, a new trailer will be added only if no trailer -with the same (<key>, <value>) pair is already in the input. +with the same (_<key>_, _<value>_) pair is already in the input. + With `add`, a new trailer will be added, even if some trailers with -the same (<key>, <value>) pair are already in the input. +the same (_<key>_, _<value>_) pair are already in the input. + -With `replace`, an existing trailer with the same <key> will be +With `replace`, an existing trailer with the same _<key>_ will be deleted and the new trailer will be added. The deleted trailer will be -the closest one (with the same <key>) to the place where the new one +the closest one (with the same _<key>_) to the place where the new one will be added. + With `doNothing`, nothing will be done; that is no new trailer will be -added if there is already one with the same <key> in the input. +added if there is already one with the same _<key>_ in the input. -trailer.ifmissing:: +`trailer.ifmissing`:: This option makes it possible to choose what action will be performed when there is not yet any trailer with the same - <key> in the input. + _<key>_ in the input. + The valid values for this option are: `add` (this is the default) and `doNothing`. @@ -70,67 +70,68 @@ With `add`, a new trailer will be added. + With `doNothing`, nothing will be done. -trailer.<keyAlias>.key:: - Defines a <keyAlias> for the <key>. The <keyAlias> must be a - prefix (case does not matter) of the <key>. For example, in `git - config trailer.ack.key "Acked-by"` the "Acked-by" is the <key> and - the "ack" is the <keyAlias>. This configuration allows the shorter +`trailer.<key-alias>.key`:: + Defines a _<key-alias>_ for the _<key>_. The _<key-alias>_ must be a + prefix (case does not matter) of the _<key>_. For example, in `git + config trailer.ack.key "Acked-by"` the `Acked-by` is the _<key>_ and + the `ack` is the _<key-alias>_. This configuration allows the shorter `--trailer "ack:..."` invocation on the command line using the "ack" - <keyAlias> instead of the longer `--trailer "Acked-by:..."`. + `<key-alias>` instead of the longer `--trailer "Acked-by:..."`. + -At the end of the <key>, a separator can appear and then some -space characters. By default the only valid separator is ':', +At the end of the _<key>_, a separator can appear and then some +space characters. By default the only valid separator is `:`, but this can be changed using the `trailer.separators` config variable. + If there is a separator in the key, then it overrides the default separator when adding the trailer. -trailer.<keyAlias>.where:: - This option takes the same values as the 'trailer.where' +`trailer.<key-alias>.where`:: + This option takes the same values as the `trailer.where` configuration variable and it overrides what is specified by - that option for trailers with the specified <keyAlias>. + that option for trailers with the specified _<key-alias>_. -trailer.<keyAlias>.ifexists:: - This option takes the same values as the 'trailer.ifexists' +`trailer.<key-alias>.ifexists`:: + This option takes the same values as the `trailer.ifexists` configuration variable and it overrides what is specified by - that option for trailers with the specified <keyAlias>. + that option for trailers with the specified _<key-alias>_. -trailer.<keyAlias>.ifmissing:: - This option takes the same values as the 'trailer.ifmissing' +`trailer.<key-alias>.ifmissing`:: + This option takes the same values as the `trailer.ifmissing` configuration variable and it overrides what is specified by - that option for trailers with the specified <keyAlias>. + that option for trailers with the specified _<key-alias>_. -trailer.<keyAlias>.command:: - Deprecated in favor of 'trailer.<keyAlias>.cmd'. - This option behaves in the same way as 'trailer.<keyAlias>.cmd', except +`trailer.<key-alias>.command`:: + Deprecated in favor of `trailer.<key-alias>.cmd`. + This option behaves in the same way as `trailer.<key-alias>.cmd`, except that it doesn't pass anything as argument to the specified command. - Instead the first occurrence of substring $ARG is replaced by the - <value> that would be passed as argument. + Instead the first occurrence of substring `$ARG` is replaced by the + _<value>_ that would be passed as argument. + -Note that $ARG in the user's command is -only replaced once and that the original way of replacing $ARG is not safe. +Note that `$ARG` in the user's command is +only replaced once and that the original way of replacing `$ARG` is not safe. + -When both 'trailer.<keyAlias>.cmd' and 'trailer.<keyAlias>.command' are given -for the same <keyAlias>, 'trailer.<keyAlias>.cmd' is used and -'trailer.<keyAlias>.command' is ignored. +When both `trailer.<key-alias>.cmd` and `trailer.<key-alias>.command` are given +for the same _<key-alias>_, `trailer.<key-alias>.cmd` is used and +`trailer.<key-alias>.command` is ignored. -trailer.<keyAlias>.cmd:: +`trailer.<key-alias>.cmd`:: This option can be used to specify a shell command that will be called - once to automatically add a trailer with the specified <keyAlias>, and then - called each time a '--trailer <keyAlias>=<value>' argument is specified to - modify the <value> of the trailer that this option would produce. + once to automatically add a trailer with the specified _<key-alias>_, and then + called each time a `--trailer <key-alias>=<value>` argument is specified to + modify the _<value>_ of the trailer that this option would produce. + When the specified command is first called to add a trailer -with the specified <keyAlias>, the behavior is as if a special -'--trailer <keyAlias>=<value>' argument was added at the beginning -of the "git interpret-trailers" command, where <value> -is taken to be the standard output of the command with any -leading and trailing whitespace trimmed off. +with the specified _<key-alias>_, the behavior is as if a special +`--trailer <key-alias>=<value>` argument was added at the beginning +of linkgit:git-interpret-trailers[1], where _<value>_ is taken to be the +standard output of the command with any leading and trailing whitespace +trimmed off. + -If some '--trailer <keyAlias>=<value>' arguments are also passed +If some `--trailer <key-alias>=<value>` arguments are also passed on the command line, the command is called again once for each -of these arguments with the same <keyAlias>. And the <value> part +of these arguments with the same _<key-alias>_. And the _<value>_ part of these arguments, if any, will be passed to the command as its -first argument. This way the command can produce a <value> computed -from the <value> passed in the '--trailer <keyAlias>=<value>' argument. +first argument. This way the command can produce a _<value>_ computed +from the _<value>_ passed in the `--trailer <key-alias>=<value>` +argument. diff --git a/Documentation/diff-context-options.adoc b/Documentation/diff-context-options.adoc index e161260358..b9ace2aa4b 100644 --- a/Documentation/diff-context-options.adoc +++ b/Documentation/diff-context-options.adoc @@ -1,7 +1,9 @@ `-U<n>`:: `--unified=<n>`:: - Generate diffs with _<n>_ lines of context. Defaults to `diff.context` - or 3 if the config option is unset. + Generate diffs with _<n>_ lines of context. The number of context + lines defaults to `diff.context` or 3 if the configuration variable + is unset. (`-U` without `<n>` is silently accepted as a synonym for + `-p` due to a historical accident). `--inter-hunk-context=<n>`:: Show the context between diff hunks, up to the specified _<number>_ diff --git a/Documentation/diff-options.adoc b/Documentation/diff-options.adoc index fcfcdf0286..8a63b5e164 100644 --- a/Documentation/diff-options.adoc +++ b/Documentation/diff-options.adoc @@ -127,8 +127,10 @@ endif::git-log[] `-U<n>`:: `--unified=<n>`:: - Generate diffs with _<n>_ lines of context instead of - the usual three. + Generate diffs with _<n>_ lines of context. The number of context + lines defaults to `diff.context` or 3 if the configuration variable + is unset. (`-U` without `<n>` is silently accepted as a synonym for + `-p` due to a historical accident). ifndef::git-format-patch[] Implies `--patch`. endif::git-format-patch[] diff --git a/Documentation/git-backfill.adoc b/Documentation/git-backfill.adoc index b8394dcf22..246ab417c2 100644 --- a/Documentation/git-backfill.adoc +++ b/Documentation/git-backfill.adoc @@ -63,9 +63,12 @@ OPTIONS current sparse-checkout. If the sparse-checkout feature is enabled, then `--sparse` is assumed and can be disabled with `--no-sparse`. +You may also specify the commit limiting options from linkgit:git-rev-list[1]. + SEE ALSO -------- -linkgit:git-clone[1]. +linkgit:git-clone[1], +linkgit:git-rev-list[1] GIT --- diff --git a/Documentation/git-config.adoc b/Documentation/git-config.adoc index 5300dd4c51..00545b2054 100644 --- a/Documentation/git-config.adoc +++ b/Documentation/git-config.adoc @@ -221,7 +221,7 @@ Use `--no-value` to unset _<pattern>_. + Valid `<type>`'s include: + -- 'bool': canonicalize values `true`, `yes`,`on`, and positive +- 'bool': canonicalize values `true`, `yes`, `on`, and positive numbers as "true", and values `false`, `no`, `off` and `0` as "false". - 'int': canonicalize values as simple decimal numbers. An optional suffix of diff --git a/Documentation/git-fast-import.adoc b/Documentation/git-fast-import.adoc index 479c4081da..b3f42d4637 100644 --- a/Documentation/git-fast-import.adoc +++ b/Documentation/git-fast-import.adoc @@ -86,6 +86,10 @@ already trusted to run their own code. * `strip-if-invalid` will check signatures and, if they are invalid, will strip them and display a warning. The validation is performed in the same way as linkgit:git-verify-commit[1] does it. +* `sign-if-invalid[=<keyid>]`, similar to `strip-if-invalid`, verifies + commit signatures and replaces invalid signatures with newly created ones. + Valid signatures are left unchanged. If `<keyid>` is provided, that key is + used for signing; otherwise the configured default signing key is used. Options for Frontends ~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/git-format-patch.adoc b/Documentation/git-format-patch.adoc index 36146006fa..5662382450 100644 --- a/Documentation/git-format-patch.adoc +++ b/Documentation/git-format-patch.adoc @@ -24,6 +24,7 @@ SYNOPSIS [(--reroll-count|-v) <n>] [--to=<email>] [--cc=<email>] [--[no-]cover-letter] [--quiet] + [--commit-list-format=<format-spec>] [--[no-]encode-email-headers] [--no-notes | --notes[=<ref>]] [--interdiff=<previous>] @@ -318,9 +319,21 @@ feeding the result to `git send-email`. --cover-letter:: --no-cover-letter:: - In addition to the patches, generate a cover letter file - containing the branch description, shortlog and the overall diffstat. You can - fill in a description in the file before sending it out. + In addition to the patches, generate a cover letter file containing the + branch description, commit list and the overall diffstat. You can fill + in a description in the file before sending it out. + +--commit-list-format=<format-spec>:: + Specify the format in which to generate the commit list of the patch + series. The accepted values for format-spec are `shortlog`, `modern` or + a format-string prefixed with `log:`. E.g. `log: %s (%an)`. + `modern` is the same as `log:%w(72)[%(count)/%(total)] %s`. + The `log:` prefix can be omitted if the format-string has a `%` in it + (expecting that it is part of `%<placeholder>`). + Defaults to the `format.commitListFormat` configuration variable, if + set, or `shortlog`. + This option given from the command-line implies the use of + `--cover-letter` unless `--no-cover-letter` is given. --encode-email-headers:: --no-encode-email-headers:: @@ -453,6 +466,7 @@ with configuration variables. signOff = true outputDirectory = <directory> coverLetter = auto + commitListFormat = shortlog coverFromDescription = auto ------------ diff --git a/Documentation/git-history.adoc b/Documentation/git-history.adoc index cc019de697..24dc907033 100644 --- a/Documentation/git-history.adoc +++ b/Documentation/git-history.adoc @@ -9,6 +9,7 @@ SYNOPSIS -------- [synopsis] git history reword <commit> [--dry-run] [--update-refs=(branches|head)] +git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--] [<pathspec>...] DESCRIPTION ----------- @@ -57,6 +58,26 @@ The following commands are available to rewrite history in different ways: details of this commit remain unchanged. This command will spawn an editor with the current message of that commit. +`split <commit> [--] [<pathspec>...]`:: + Interactively split up <commit> into two commits by choosing + hunks introduced by it that will be moved into the new split-out + commit. These hunks will then be written into a new commit that + becomes the parent of the previous commit. The original commit + stays intact, except that its parent will be the newly split-out + commit. ++ +The commit messages of the split-up commits will be asked for by launching +the configured editor. Authorship of the commit will be the same as for the +original commit. ++ +If passed, _<pathspec>_ can be used to limit which changes shall be split out +of the original commit. Files not matching any of the pathspecs will remain +part of the original commit. For more details, see the 'pathspec' entry in +linkgit:gitglossary[7]. ++ +It is invalid to select either all or no hunks, as that would lead to +one of the commits becoming empty. + OPTIONS ------- @@ -72,6 +93,47 @@ OPTIONS descendants of the original commit will be rewritten. With `head`, only the current `HEAD` reference will be rewritten. Defaults to `branches`. +EXAMPLES +-------- + +Split a commit +~~~~~~~~~~~~~~ + +---------- +$ git log --stat --oneline +3f81232 (HEAD -> main) original + bar | 1 + + foo | 1 + + 2 files changed, 2 insertions(+) + +$ git history split HEAD +diff --git a/bar b/bar +new file mode 100644 +index 0000000..5716ca5 +--- /dev/null ++++ b/bar +@@ -0,0 +1 @@ ++bar +(1/1) Stage addition [y,n,q,a,d,p,?]? y + +diff --git a/foo b/foo +new file mode 100644 +index 0000000..257cc56 +--- /dev/null ++++ b/foo +@@ -0,0 +1 @@ ++foo +(1/1) Stage addition [y,n,q,a,d,p,?]? n + +$ git log --stat --oneline +7cebe64 (HEAD -> main) original + foo | 1 + + 1 file changed, 1 insertion(+) +d1582f3 split-out commit + bar | 1 + + 1 file changed, 1 insertion(+) +---------- + GIT --- Part of the linkgit:git[1] suite diff --git a/Documentation/git-hook.adoc b/Documentation/git-hook.adoc index 12d2701b52..318c637bd8 100644 --- a/Documentation/git-hook.adoc +++ b/Documentation/git-hook.adoc @@ -8,8 +8,8 @@ git-hook - Run git hooks SYNOPSIS -------- [verse] -'git hook' run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>] -'git hook' list [-z] <hook-name> +'git hook' run [--allow-unknown-hook-name] [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>] +'git hook' list [--allow-unknown-hook-name] [-z] [--show-scope] <hook-name> DESCRIPTION ----------- @@ -44,7 +44,7 @@ event`), and then `~/bin/spellchecker` will have a chance to check your commit message (during the `commit-msg` hook event). Commands are run in the order Git encounters their associated -`hook.<name>.event` configs during the configuration parse (see +`hook.<friendly-name>.event` configs during the configuration parse (see linkgit:git-config[1]). Although multiple `hook.linter.event` configs can be added, only one `hook.linter.command` event is valid - Git uses "last-one-wins" to determine which command to run. @@ -76,10 +76,10 @@ first start `~/bin/linter --cpp20` and second start `~/bin/leak-detector`. It would evaluate the output of each when deciding whether to proceed with the commit. -For a full list of hook events which you can set your `hook.<name>.event` to, +For a full list of hook events which you can set your `hook.<friendly-name>.event` to, and how hooks are invoked during those events, see linkgit:githooks[5]. -Git will ignore any `hook.<name>.event` that specifies an event it doesn't +Git will ignore any `hook.<friendly-name>.event` that specifies an event it doesn't recognize. This is intended so that tools which wrap Git can use the hook infrastructure to run their own hooks; see "WRAPPERS" for more guidance. @@ -113,7 +113,7 @@ Any positional arguments to the hook should be passed after a mandatory `--` (or `--end-of-options`, see linkgit:gitcli[7]). See linkgit:githooks[5] for arguments hooks might expect (if any). -list [-z]:: +list [-z] [--show-scope]:: Print a list of hooks which will be run on `<hook-name>` event. If no hooks are configured for that event, print a warning and return 1. Use `-z` to terminate output lines with NUL instead of newlines. @@ -121,6 +121,13 @@ list [-z]:: OPTIONS ------- +--allow-unknown-hook-name:: + By default `git hook run` and `git hook list` will bail out when + `<hook-name>` is not a hook event known to Git (see linkgit:githooks[5] + for the list of known hooks). This is meant to help catch typos + such as `prereceive` when `pre-receive` was intended. Pass this + flag to allow unknown hook names. + --to-stdin:: For "run"; specify a file which will be streamed into the hook's stdin. The hook will receive the entire file from @@ -134,6 +141,12 @@ OPTIONS -z:: Terminate "list" output lines with NUL instead of newlines. +--show-scope:: + For "list"; prefix each configured hook's friendly name with a + tab-separated config scope (e.g. `local`, `global`, `system`), + mirroring the output style of `git config --show-scope`. Traditional + hooks from the hookdir are unaffected. + WRAPPERS -------- @@ -153,7 +166,7 @@ Then, in your 'mywrapper' tool, you can invoke any users' configured hooks by running: ---- -git hook run mywrapper-start-tests \ +git hook run --allow-unknown-hook-name mywrapper-start-tests \ # providing something to stdin --stdin some-tempfile-123 \ # execute hooks in serial diff --git a/Documentation/git-interpret-trailers.adoc b/Documentation/git-interpret-trailers.adoc index fd335fe772..77b4f63b05 100644 --- a/Documentation/git-interpret-trailers.adoc +++ b/Documentation/git-interpret-trailers.adoc @@ -7,14 +7,14 @@ git-interpret-trailers - Add or parse structured information in commit messages SYNOPSIS -------- -[verse] -'git interpret-trailers' [--in-place] [--trim-empty] +[synopsis] +git interpret-trailers [--in-place] [--trim-empty] [(--trailer (<key>|<key-alias>)[(=|:)<value>])...] [--parse] [<file>...] DESCRIPTION ----------- -Add or parse 'trailer' lines that look similar to RFC 822 e-mail +Add or parse _trailer_ lines that look similar to RFC 822 e-mail headers, at the end of the otherwise free-form part of a commit message. For example, in the following commit message @@ -27,23 +27,24 @@ Signed-off-by: Alice <alice@example.com> Signed-off-by: Bob <bob@example.com> ------------------------------------------------ -the last two lines starting with "Signed-off-by" are trailers. +the last two lines starting with `Signed-off-by` are trailers. This command reads commit messages from either the -<file> arguments or the standard input if no <file> is specified. +_<file>_ arguments or the standard input if no _<file>_ is specified. If `--parse` is specified, the output consists of the parsed trailers coming from the input, without influencing them with any command line options or configuration variables. -Otherwise, this command applies `trailer.*` configuration variables -(which could potentially add new trailers, as well as reposition them), -as well as any command line arguments that can override configuration -variables (such as `--trailer=...` which could also add new trailers), -to each input file. The result is emitted on the standard output. +Otherwise, this command applies `trailer.<key-alias>` configuration +variables (which could potentially add new trailers, as well as +reposition them), as well as any command line arguments that can +override configuration variables (such as `--trailer=...` which could +also add new trailers), to each input file. The result is emitted on the +standard output. This command can also operate on the output of linkgit:git-format-patch[1], which is more elaborate than a plain commit message. Namely, such output -includes a commit message (as above), a "---" divider line, and a patch part. +includes a commit message (as above), a `---` divider line, and a patch part. For these inputs, the divider and patch parts are not modified by this command and are emitted as is on the output, unless `--no-divider` is specified. @@ -53,24 +54,24 @@ are applied to each input and the way any existing trailer in the input is changed. They also make it possible to automatically add some trailers. -By default, a '<key>=<value>' or '<key>:<value>' argument given +By default, a `<key>=<value>` or `<key>:<value>` argument given using `--trailer` will be appended after the existing trailers only if -the last trailer has a different (<key>, <value>) pair (or if there -is no existing trailer). The <key> and <value> parts will be trimmed +the last trailer has a different (_<key>_, _<value>_) pair (or if there +is no existing trailer). The _<key>_ and _<value>_ parts will be trimmed to remove starting and trailing whitespace, and the resulting trimmed -<key> and <value> will appear in the output like this: +_<key>_ and _<value>_ will appear in the output like this: ------------------------------------------------ key: value ------------------------------------------------ -This means that the trimmed <key> and <value> will be separated by -`': '` (one colon followed by one space). +This means that the trimmed _<key>_ and _<value>_ will be separated by +"`:`{nbsp}" (one colon followed by one space). -For convenience, a <key-alias> can be configured to make using `--trailer` +For convenience, a _<key-alias>_ can be configured to make using `--trailer` shorter to type on the command line. This can be configured using the -'trailer.<key-alias>.key' configuration variable. The <keyAlias> must be a prefix -of the full <key> string, although case sensitivity does not matter. For +`trailer.<key-alias>.key` configuration variable. The _<key-alias>_ must be a prefix +of the full _<key>_ string, although case sensitivity does not matter. For example, if you have ------------------------------------------------ @@ -91,13 +92,13 @@ least one Git-generated or user-configured trailer and consists of at least 25% trailers. The group must be preceded by one or more empty (or whitespace-only) lines. The group must either be at the end of the input or be the last -non-whitespace lines before a line that starts with '---' (followed by a +non-whitespace lines before a line that starts with `---` (followed by a space or the end of the line). When reading trailers, there can be no whitespace before or inside the -<key>, but any number of regular space and tab characters are allowed -between the <key> and the separator. There can be whitespaces before, -inside or after the <value>. The <value> may be split over multiple lines +_<key>_, but any number of regular space and tab characters are allowed +between the _<key>_ and the separator. There can be whitespaces before, +inside or after the _<value>_. The _<value>_ may be split over multiple lines with each subsequent line starting with at least one whitespace, like the "folding" in RFC 822. Example: @@ -111,77 +112,97 @@ rules for RFC 822 headers. For example they do not follow the encoding rule. OPTIONS ------- ---in-place:: - Edit the files in place. +`--in-place`:: +`--no-in-place`:: + Edit the files in place. The default is `--no-in-place`. ---trim-empty:: - If the <value> part of any trailer contains only whitespace, +`--trim-empty`:: +`--no-trim-empty`:: + If the _<value>_ part of any trailer contains only whitespace, the whole trailer will be removed from the output. This applies to existing trailers as well as new trailers. ++ +The default is `--no-trim-empty`. ---trailer <key>[(=|:)<value>]:: - Specify a (<key>, <value>) pair that should be applied as a - trailer to the inputs. See the description of this - command. +`--trailer=<key>[(=|:)<value>]`:: +`--no-trailer`:: + Specify a (_<key>_, _<value>_) pair that should be applied as a + trailer to the inputs. See the description of this command. Can + be given multiple times. ++ +Use `--no-trailer` to reset the list. ---where <placement>:: ---no-where:: +`--where=<placement>`:: +`--no-where`:: Specify where all new trailers will be added. A setting - provided with '--where' overrides the `trailer.where` and any - applicable `trailer.<keyAlias>.where` configuration variables - and applies to all '--trailer' options until the next occurrence of - '--where' or '--no-where'. Upon encountering '--no-where', clear the - effect of any previous use of '--where', such that the relevant configuration - variables are no longer overridden. Possible placements are `after`, + provided with `--where` overrides the `trailer.where` and any + applicable `trailer.<key-alias>.where` configuration variables + and applies to all `--trailer` options until the next occurrence of + `--where` or `--no-where`. Possible placements are `after`, `before`, `end` or `start`. ++ +Use `--no-where` to clear the effect of any previous use of `--where`, +such that the relevant configuration variables are no longer overridden. ---if-exists <action>:: ---no-if-exists:: +`--if-exists=<action>`:: +`--no-if-exists`:: Specify what action will be performed when there is already at - least one trailer with the same <key> in the input. A setting - provided with '--if-exists' overrides the `trailer.ifExists` and any - applicable `trailer.<keyAlias>.ifExists` configuration variables - and applies to all '--trailer' options until the next occurrence of - '--if-exists' or '--no-if-exists'. Upon encountering '--no-if-exists', clear the - effect of any previous use of '--if-exists', such that the relevant configuration - variables are no longer overridden. Possible actions are `addIfDifferent`, + least one trailer with the same _<key>_ in the input. A setting + provided with `--if-exists` overrides the `trailer.ifExists` and any + applicable `trailer.<key-alias>.ifExists` configuration variables + and applies to all `--trailer` options until the next occurrence of + `--if-exists` or `--no-if-exists`. Possible actions are `addIfDifferent`, `addIfDifferentNeighbor`, `add`, `replace` and `doNothing`. ++ +Use `--no-if-exists` to clear the effect of any previous use of +`--if-exists`, such that the relevant configuration variables are no +longer overridden. ---if-missing <action>:: ---no-if-missing:: +`--if-missing=<action>`:: +`--no-if-missing`:: Specify what action will be performed when there is no other - trailer with the same <key> in the input. A setting - provided with '--if-missing' overrides the `trailer.ifMissing` and any - applicable `trailer.<keyAlias>.ifMissing` configuration variables - and applies to all '--trailer' options until the next occurrence of - '--if-missing' or '--no-if-missing'. Upon encountering '--no-if-missing', - clear the effect of any previous use of '--if-missing', such that the relevant - configuration variables are no longer overridden. Possible actions are `doNothing` - or `add`. + trailer with the same _<key>_ in the input. A setting + provided with `--if-missing` overrides the `trailer.ifMissing` and any + applicable `trailer.<key-alias>.ifMissing` configuration variables + and applies to all `--trailer` options until the next occurrence of + `--if-missing` or `--no-if-missing`. Possible actions are + `doNothing` or `add`. ++ +Use `--no-if-missing` to clear the effect of any previous use of +`--if-missing`, such that the relevant configuration variables are no +longer overridden. ---only-trailers:: - Output only the trailers, not any other parts of the input. +`--only-trailers`:: +`--no-only-trailers`:: + Output only the trailers, not any other parts of the + input. The default is `--no-only-trailers`. ---only-input:: +`--only-input`:: +`--no-only-input`:: Output only trailers that exist in the input; do not add any - from the command-line or by applying `trailer.*` configuration - variables. + from the command-line or by applying `trailer.<key-alias>` configuration + variables. The default is `--no-only-input`. ---unfold:: +`--unfold`:: +`--no-unfold`:: If a trailer has a value that runs over multiple lines (aka "folded"), - reformat the value into a single line. + reformat the value into a single line. The default is `--no-unfold`. ---parse:: +`--parse`:: A convenience alias for `--only-trailers --only-input --unfold`. This makes it easier to only see the trailers coming from the input without influencing them with any command line options or configuration variables, while also making the output machine-friendly with - --unfold. + `--unfold`. ++ +There is no convenience alias to negate this alias. ---no-divider:: - Do not treat `---` as the end of the commit message. Use this - when you know your input contains just the commit message itself - (and not an email or the output of `git format-patch`). +`--divider`:: +`--no-divider`:: + Treat `---` as the end of the commit message. This is the default. + Use `--no-divider` when you know your input contains just the + commit message itself (and not an email or the output of + linkgit:git-format-patch[1]). CONFIGURATION VARIABLES ----------------------- @@ -193,7 +214,7 @@ include::config/trailer.adoc[] EXAMPLES -------- -* Configure a 'sign' trailer with a 'Signed-off-by' key, and then +* Configure a `sign` trailer with a `Signed-off-by` key, and then add two of these trailers to a commit message file: + ------------ @@ -230,8 +251,8 @@ Signed-off-by: Bob <bob@example.com> Acked-by: Alice <alice@example.com> ------------ -* Extract the last commit as a patch, and add a 'Cc' and a - 'Reviewed-by' trailer to it: +* Extract the last commit as a patch, and add a `Cc` and a + `Reviewed-by` trailer to it: + ------------ $ git format-patch -1 @@ -239,9 +260,9 @@ $ git format-patch -1 $ git interpret-trailers --trailer 'Cc: Alice <alice@example.com>' --trailer 'Reviewed-by: Bob <bob@example.com>' 0001-foo.patch >0001-bar.patch ------------ -* Configure a 'sign' trailer with a command to automatically add a - 'Signed-off-by: ' with the author information only if there is no - 'Signed-off-by: ' already, and show how it works: +* Configure a `sign` trailer with a command to automatically add a + "`Signed-off-by:`{nbsp}" with the author information only if there is no + "`Signed-off-by:`{nbsp}" already, and show how it works: + ------------ $ cat msg1.txt @@ -272,7 +293,7 @@ body text Signed-off-by: Alice <alice@example.com> ------------ -* Configure a 'fix' trailer with a key that contains a '#' and no +* Configure a `fix` trailer with a key that contains a `#` and no space after this character, and show how it works: + ------------ @@ -284,7 +305,7 @@ subject Fix #42 ------------ -* Configure a 'help' trailer with a cmd use a script `glog-find-author` +* Configure a `help` trailer with a cmd use a script `glog-find-author` which search specified author identity from git log in git repository and show how it works: + @@ -308,7 +329,7 @@ Helped-by: Junio C Hamano <gitster@pobox.com> Helped-by: Christian Couder <christian.couder@gmail.com> ------------ -* Configure a 'ref' trailer with a cmd use a script `glog-grep` +* Configure a `ref` trailer with a cmd use a script `glog-grep` to grep last relevant commit from git log in the git repository and show how it works: + @@ -331,7 +352,7 @@ body text Reference-to: 8bc9a0c769 (Add copyright notices., 2005-04-07) ------------ -* Configure a 'see' trailer with a command to show the subject of a +* Configure a `see` trailer with a command to show the subject of a commit that is related, and show how it works: + ------------ @@ -359,8 +380,8 @@ See-also: fe3187489d69c4 (subject of related commit) * Configure a commit template with some trailers with empty values (using sed to show and keep the trailing spaces at the end of the trailers), then configure a commit-msg hook that uses - 'git interpret-trailers' to remove trailers with empty values and - to add a 'git-version' trailer: + git-interpret-trailers(1) to remove trailers with empty values and to + add a `git-version` trailer: + ------------ $ cat temp.txt diff --git a/Documentation/git-multi-pack-index.adoc b/Documentation/git-multi-pack-index.adoc index 2f642697e9..6125683014 100644 --- a/Documentation/git-multi-pack-index.adoc +++ b/Documentation/git-multi-pack-index.adoc @@ -9,7 +9,14 @@ git-multi-pack-index - Write and verify multi-pack-indexes SYNOPSIS -------- [verse] -'git multi-pack-index' [--object-dir=<dir>] [--[no-]bitmap] <sub-command> +'git multi-pack-index' [<options>] write [--preferred-pack=<pack>] + [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs] + [--refs-snapshot=<path>] +'git multi-pack-index' [<options>] compact [--[no-]incremental] + [--[no-]bitmap] <from> <to> +'git multi-pack-index' [<options>] verify +'git multi-pack-index' [<options>] expire +'git multi-pack-index' [<options>] repack [--batch-size=<size>] DESCRIPTION ----------- @@ -18,6 +25,8 @@ Write or verify a multi-pack-index (MIDX) file. OPTIONS ------- +The following command-line options are applicable to all sub-commands: + --object-dir=<dir>:: Use given directory for the location of Git objects. We check `<dir>/packs/multi-pack-index` for the current MIDX file, and @@ -73,7 +82,21 @@ marker). Write an incremental MIDX file containing only objects and packs not present in an existing MIDX layer. Migrates non-incremental MIDXs to incremental ones when - necessary. Incompatible with `--bitmap`. + necessary. +-- + +compact:: + Write a new MIDX layer containing only objects and packs present + in the range `<from>` to `<to>`, where both arguments are + checksums of existing layers in the MIDX chain. ++ +-- + --incremental:: + Write the result to a MIDX chain instead of writing a + stand-alone MIDX. + + --[no-]bitmap:: + Control whether or not a multi-pack bitmap is written. -- verify:: diff --git a/Documentation/git-pack-objects.adoc b/Documentation/git-pack-objects.adoc index 71b9682485..b78175fbe1 100644 --- a/Documentation/git-pack-objects.adoc +++ b/Documentation/git-pack-objects.adoc @@ -94,13 +94,24 @@ base-name:: included packs (those not beginning with `^`), excluding any objects listed in the excluded packs (beginning with `^`). + -When `mode` is "follow", objects from packs not listed on stdin receive -special treatment. Objects within unlisted packs will be included if -those objects are (1) reachable from the included packs, and (2) not -found in any excluded packs. This mode is useful, for example, to -resurrect once-unreachable objects found in cruft packs to generate -packs which are closed under reachability up to the boundary set by the -excluded packs. +When `mode` is "follow" packs may additionally be prefixed with `!`, +indicating that they are excluded but not necessarily closed under +reachability. In addition to objects in included packs, the resulting +pack may include additional objects based on the following: ++ +-- +* If any packs are marked with `!`, then objects reachable from such + packs or included ones via objects outside of excluded-closed packs + will be included. In this case, all `^` packs are treated as closed + under reachability. +* Otherwise (if there are no `!` packs), objects within unlisted packs + will be included if those objects are (1) reachable from the + included packs, and (2) not found in any excluded packs. +-- ++ +This mode is useful, for example, to resurrect once-unreachable +objects found in cruft packs to generate packs which are closed under +reachability up to the boundary set by the excluded packs. + Incompatible with `--revs`, or options that imply `--revs` (such as `--all`), with the exception of `--unpacked`, which is compatible. diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc index e177808004..f6c22d1598 100644 --- a/Documentation/git-rebase.adoc +++ b/Documentation/git-rebase.adoc @@ -497,6 +497,13 @@ See also INCOMPATIBLE OPTIONS below. + See also INCOMPATIBLE OPTIONS below. +--trailer=<trailer>:: + Append the given trailer to every rebased commit message, processed + via linkgit:git-interpret-trailers[1]. This option implies + `--force-rebase`. ++ +See also INCOMPATIBLE OPTIONS below. + -i:: --interactive:: Make a list of the commits which are about to be rebased. Let the @@ -653,6 +660,7 @@ are incompatible with the following options: * --[no-]reapply-cherry-picks when used without --keep-base * --update-refs * --root when used without --onto + * --trailer In addition, the following pairs of options are incompatible: diff --git a/Documentation/git-replay.adoc b/Documentation/git-replay.adoc index 8d696ce3ab..997097e420 100644 --- a/Documentation/git-replay.adoc +++ b/Documentation/git-replay.adoc @@ -9,7 +9,7 @@ git-replay - EXPERIMENTAL: Replay commits on a new base, works with bare repos t SYNOPSIS -------- [verse] -(EXPERIMENTAL!) 'git replay' ([--contained] --onto <newbase> | --advance <branch>) [--ref-action[=<mode>]] <revision-range> +(EXPERIMENTAL!) 'git replay' ([--contained] --onto <newbase> | --advance <branch> | --revert <branch>) [--ref-action[=<mode>]] <revision-range> DESCRIPTION ----------- @@ -42,6 +42,25 @@ The history is replayed on top of the <branch> and <branch> is updated to point at the tip of the resulting history. This is different from `--onto`, which uses the target only as a starting point without updating it. +--revert <branch>:: + Starting point at which to create the reverted commits; must be a + branch name. ++ +When `--revert` is specified, the commits in the revision range are reverted +(their changes are undone) and the reverted commits are created on top of +<branch>. The <branch> is then updated to point at the new commits. This is +the same as running `git revert <revision-range>` but does not update the +working tree. ++ +The commit messages follow `git revert` conventions: they are prefixed with +"Revert" and include "This reverts commit <hash>." When reverting a commit +whose message starts with "Revert", the new message uses "Reapply" instead. +Unlike cherry-pick which preserves the original author, revert commits use +the current user as the author, matching the behavior of `git revert`. ++ +This option is mutually exclusive with `--onto` and `--advance`. It is also +incompatible with `--contained` (which is a modifier for `--onto` only). + --contained:: Update all branches that point at commits in <revision-range>. Requires `--onto`. @@ -60,10 +79,11 @@ The default mode can be configured via the `replay.refAction` configuration vari <revision-range>:: Range of commits to replay; see "Specifying Ranges" in - linkgit:git-rev-parse[1]. In `--advance <branch>` mode, the - range should have a single tip, so that it's clear to which tip the - advanced <branch> should point. Any commits in the range whose - changes are already present in the branch the commits are being + linkgit:git-rev-parse[1]. In `--advance <branch>` or + `--revert <branch>` mode, the range should have a single tip, + so that it's clear to which tip the advanced or reverted + <branch> should point. Any commits in the range whose changes + are already present in the branch the commits are being replayed onto will be dropped. :git-replay: 1 @@ -84,9 +104,10 @@ When using `--ref-action=print`, the output is usable as input to update refs/heads/branch3 ${NEW_branch3_HASH} ${OLD_branch3_HASH} where the number of refs updated depends on the arguments passed and -the shape of the history being replayed. When using `--advance`, the -number of refs updated is always one, but for `--onto`, it can be one -or more (rebasing multiple branches simultaneously is supported). +the shape of the history being replayed. When using `--advance` or +`--revert`, the number of refs updated is always one, but for `--onto`, +it can be one or more (rebasing multiple branches simultaneously is +supported). There is no stderr output on conflicts; see the <<exit-status,EXIT STATUS>> section below. @@ -152,6 +173,21 @@ all commits they have since `base`, playing them on top of `origin/main`. These three branches may have commits on top of `base` that they have in common, but that does not need to be the case. +To revert commits on a branch: + +------------ +$ git replay --revert main topic~2..topic +------------ + +This reverts the last two commits from `topic`, creating revert commits on +top of `main`, and updates `main` to point at the result. This is useful when +commits from `topic` were previously merged or cherry-picked into `main` and +need to be undone. + +NOTE: For reverting an entire merge request as a single commit (rather than +commit-by-commit), consider using `git merge-tree --merge-base $TIP HEAD $BASE` +which can avoid unnecessary merge conflicts. + GIT --- Part of the linkgit:git[1] suite diff --git a/Documentation/git-stash.adoc b/Documentation/git-stash.adoc index 235d57ddd8..b05c990ecd 100644 --- a/Documentation/git-stash.adoc +++ b/Documentation/git-stash.adoc @@ -14,10 +14,10 @@ git stash drop [-q | --quiet] [<stash>] git stash pop [--index] [-q | --quiet] [<stash>] git stash apply [--index] [-q | --quiet] [<stash>] git stash branch <branchname> [<stash>] -git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet] +git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet] [-u | --include-untracked] [-a | --all] [(-m | --message) <message>] [--pathspec-from-file=<file> [--pathspec-file-nul]] - [--] [<pathspec>...]] + [--] [<pathspec>...] git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet] [-u | --include-untracked] [-a | --all] [<message>] git stash clear @@ -60,10 +60,8 @@ COMMANDS the description along with the stashed state. + For quickly making a snapshot, you can omit "push". In this mode, -non-option arguments are not allowed to prevent a misspelled -subcommand from making an unwanted stash entry. The two exceptions to this -are `stash -p` which acts as alias for `stash push -p` and pathspec elements, -which are allowed after a double hyphen `--` for disambiguation. +pathspec elements are only allowed after a double hyphen `--` +to prevent a misspelled subcommand from making an unwanted stash entry. `save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-u | --include-untracked] [-a | --all] [-q | --quiet] [<message>]`:: diff --git a/Documentation/gitformat-pack.adoc b/Documentation/gitformat-pack.adoc index 1b4db4aa61..3416edceab 100644 --- a/Documentation/gitformat-pack.adoc +++ b/Documentation/gitformat-pack.adoc @@ -374,7 +374,9 @@ HEADER: The signature is: {'M', 'I', 'D', 'X'} 1-byte version number: - Git only writes or recognizes version 1. + Git writes the version specified by the "midx.version" + configuration option, which defaults to 2. It recognizes + both versions 1 and 2. 1-byte Object Id Version We infer the length of object IDs (OIDs) from this value: @@ -413,7 +415,9 @@ CHUNK DATA: strings. There is no extra padding between the filenames, and they are listed in lexicographic order. The chunk itself is padded at the end with between 0 and 3 NUL bytes to make the - chunk size a multiple of 4 bytes. + chunk size a multiple of 4 bytes. Version 1 MIDXs are required to + list their packs in lexicographic order, but version 2 MIDXs may + list their packs in any arbitrary order. Bitmapped Packfiles (ID: {'B', 'T', 'M', 'P'}) Stores a table of two 4-byte unsigned integers in network order. diff --git a/Documentation/githooks.adoc b/Documentation/githooks.adoc index 056553788d..ed045940d1 100644 --- a/Documentation/githooks.adoc +++ b/Documentation/githooks.adoc @@ -484,13 +484,16 @@ reference-transaction ~~~~~~~~~~~~~~~~~~~~~ This hook is invoked by any Git command that performs reference -updates. It executes whenever a reference transaction is prepared, -committed or aborted and may thus get called multiple times. The hook -also supports symbolic reference updates. +updates. It executes whenever a reference transaction is preparing, +prepared, committed or aborted and may thus get called multiple times. +The hook also supports symbolic reference updates. The hook takes exactly one argument, which is the current state the given reference transaction is in: + - "preparing": All reference updates have been queued to the + transaction but references are not yet locked on disk. + - "prepared": All reference updates have been queued to the transaction and references were locked on disk. @@ -511,16 +514,18 @@ ref and `<ref-name>` is the full name of the ref. When force updating the reference regardless of its current value or when the reference is to be created anew, `<old-value>` is the all-zeroes object name. To distinguish these cases, you can inspect the current value of -`<ref-name>` via `git rev-parse`. +`<ref-name>` via `git rev-parse`. During the "preparing" state, symbolic +references are not resolved: `<ref-name>` will reflect the symbolic reference +itself rather than the object it points to. For symbolic reference updates the `<old_value>` and `<new-value>` fields could denote references instead of objects. A reference will be denoted with a 'ref:' prefix, like `ref:<ref-target>`. The exit status of the hook is ignored for any state except for the -"prepared" state. In the "prepared" state, a non-zero exit status will -cause the transaction to be aborted. The hook will not be called with -"aborted" state in that case. +"preparing" and "prepared" states. In these states, a non-zero exit +status will cause the transaction to be aborted. The hook will not be +called with "aborted" state in that case. push-to-checkout ~~~~~~~~~~~~~~~~ diff --git a/Documentation/gitignore.adoc b/Documentation/gitignore.adoc index 9fccab4ae8..a3d24e5c34 100644 --- a/Documentation/gitignore.adoc +++ b/Documentation/gitignore.adoc @@ -96,6 +96,11 @@ PATTERN FORMAT particular `.gitignore` file itself. Otherwise the pattern may also match at any level below the `.gitignore` level. + - Patterns read from exclude sources that are outside the working tree, + such as $GIT_DIR/info/exclude and core.excludesFile, are treated as if + they are specified at the root of the working tree, i.e. a leading "/" + in such patterns anchors the match at the root of the repository. + - If there is a separator at the end of the pattern then the pattern will only match directories, otherwise the pattern can match both files and directories. diff --git a/Documentation/pretty-formats.adoc b/Documentation/pretty-formats.adoc index 5405e57a60..2ae0eb11a9 100644 --- a/Documentation/pretty-formats.adoc +++ b/Documentation/pretty-formats.adoc @@ -253,6 +253,10 @@ The placeholders are: linkgit:git-rev-list[1]) +%d+:: ref names, like the --decorate option of linkgit:git-log[1] +%D+:: ref names without the " (", ")" wrapping. ++%(count)+:: the number of a patch within a patch series. Used only in + `--commit-list-format` in `format-patch` ++%(total)+:: the total number of patches in a patch series. Used only in + `--commit-list-format` in `format-patch` ++%(decorate++`[:<option>,...]`++)++:: ref names with custom decorations. The `decorate` string may be followed by a colon and zero or more comma-separated options. Option values may contain diff --git a/Documentation/user-manual.adoc b/Documentation/user-manual.adoc index 7696987117..64009baf37 100644 --- a/Documentation/user-manual.adoc +++ b/Documentation/user-manual.adoc @@ -4466,7 +4466,7 @@ $ git show # most recent commit $ git diff v2.6.15..v2.6.16 # diff between two tagged versions $ git diff v2.6.15..HEAD # diff with current head $ git grep "foo()" # search working directory for "foo()" -$ git grep v2.6.15 "foo()" # search old tree for "foo()" +$ git grep "foo()" v2.6.15 # search old tree for "foo()" $ git show v2.6.15:a.txt # look at old version of a.txt ----------------------------------------------- diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 44240e07b8..92ea811ae6 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,6 +1,6 @@ #!/bin/sh -DEF_VER=v2.53.GIT +DEF_VER=v2.54.0-rc0 LF=' ' @@ -1005,8 +1005,8 @@ SPATCH_TEST_FLAGS = # COMPUTE_HEADER_DEPENDENCIES=no this will be unset too. SPATCH_USE_O_DEPENDENCIES = YesPlease -# Set SPATCH_CONCAT_COCCI to concatenate the contrib/cocci/*.cocci -# files into a single contrib/cocci/ALL.cocci before running +# Set SPATCH_CONCAT_COCCI to concatenate the tools/coccinelle/*.cocci +# files into a single tools/coccinelle/ALL.cocci before running # "coccicheck". # # Pros: @@ -1025,7 +1025,7 @@ SPATCH_USE_O_DEPENDENCIES = YesPlease # generate a specific patch, e.g. this will always use strbuf.cocci, # not ALL.cocci: # -# make contrib/coccinelle/strbuf.cocci.patch +# make tools/coccinelle/strbuf.cocci.patch SPATCH_CONCAT_COCCI = YesPlease # Rebuild 'coccicheck' if $(SPATCH), its flags etc. change @@ -1066,11 +1066,13 @@ SOURCES_CMD = ( \ '*.sh' \ ':!*[tp][0-9][0-9][0-9][0-9]*' \ ':!contrib' \ + ':!tools' \ 2>/dev/null || \ $(FIND) . \ \( -name .git -type d -prune \) \ -o \( -name '[tp][0-9][0-9][0-9][0-9]*' -prune \) \ -o \( -name contrib -type d -prune \) \ + -o \( -name tools -type d -prune \) \ -o \( -name build -type d -prune \) \ -o \( -name .build -type d -prune \) \ -o \( -name 'trash*' -type d -prune \) \ @@ -2027,6 +2029,10 @@ ifdef NO_PREAD COMPAT_CFLAGS += -DNO_PREAD COMPAT_OBJS += compat/pread.o endif +ifdef NO_WRITEV + COMPAT_CFLAGS += -DNO_WRITEV + COMPAT_OBJS += compat/writev.o +endif ifdef NO_FAST_WORKING_DIRECTORY BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY endif @@ -2669,6 +2675,7 @@ git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS) help.sp help.s help.o: command-list.h builtin/bugreport.sp builtin/bugreport.s builtin/bugreport.o: hook-list.h +builtin/hook.sp builtin/hook.s builtin/hook.o: hook-list.h builtin/help.sp builtin/help.s builtin/help.o: config-list.h GIT-PREFIX builtin/help.sp builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \ @@ -2693,21 +2700,21 @@ $(BUILT_INS): git$X ln -s $< $@ 2>/dev/null || \ cp $< $@ -config-list.h: generate-configlist.sh +config-list.h: tools/generate-configlist.sh @mkdir -p .depend - $(QUIET_GEN)$(SHELL_PATH) ./generate-configlist.sh . $@ .depend/config-list.h.d + $(QUIET_GEN)$(SHELL_PATH) ./tools/generate-configlist.sh . $@ .depend/config-list.h.d -include .depend/config-list.h.d -command-list.h: generate-cmdlist.sh command-list.txt +command-list.h: tools/generate-cmdlist.sh command-list.txt command-list.h: $(wildcard Documentation/git*.adoc) - $(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh \ + $(QUIET_GEN)$(SHELL_PATH) ./tools/generate-cmdlist.sh \ $(patsubst %,--exclude-program %,$(EXCLUDED_PROGRAMS)) \ . $@ -hook-list.h: generate-hooklist.sh Documentation/githooks.adoc - $(QUIET_GEN)$(SHELL_PATH) ./generate-hooklist.sh . $@ +hook-list.h: tools/generate-hooklist.sh Documentation/githooks.adoc + $(QUIET_GEN)$(SHELL_PATH) ./tools/generate-hooklist.sh . $@ SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):\ $(localedir_SQ):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\ @@ -2720,8 +2727,8 @@ GIT-SCRIPT-DEFINES: FORCE echo "$$FLAGS" >$@; \ fi -$(SCRIPT_SH_GEN) $(SCRIPT_LIB) : % : %.sh generate-script.sh GIT-BUILD-OPTIONS GIT-SCRIPT-DEFINES - $(QUIET_GEN)./generate-script.sh "$<" "$@+" ./GIT-BUILD-OPTIONS && \ +$(SCRIPT_SH_GEN) $(SCRIPT_LIB) : % : %.sh tools/generate-script.sh GIT-BUILD-OPTIONS GIT-SCRIPT-DEFINES + $(QUIET_GEN)./tools/generate-script.sh "$<" "$@+" ./GIT-BUILD-OPTIONS && \ mv $@+ $@ git.rc: git.rc.in GIT-VERSION-GEN GIT-VERSION-FILE @@ -2761,8 +2768,8 @@ endif PERL_DEFINES += $(gitexecdir) $(perllibdir) $(localedir) -$(SCRIPT_PERL_GEN): % : %.perl generate-perl.sh GIT-PERL-DEFINES GIT-PERL-HEADER GIT-VERSION-FILE - $(QUIET_GEN)$(SHELL_PATH) generate-perl.sh ./GIT-BUILD-OPTIONS ./GIT-VERSION-FILE GIT-PERL-HEADER "$<" "$@+" && \ +$(SCRIPT_PERL_GEN): % : %.perl tools/generate-perl.sh GIT-PERL-DEFINES GIT-PERL-HEADER GIT-VERSION-FILE + $(QUIET_GEN)$(SHELL_PATH) tools/generate-perl.sh ./GIT-BUILD-OPTIONS ./GIT-VERSION-FILE GIT-PERL-HEADER "$<" "$@+" && \ mv $@+ $@ PERL_DEFINES := $(subst $(space),:,$(PERL_DEFINES)) @@ -2790,8 +2797,8 @@ GIT-PERL-HEADER: $(PERL_HEADER_TEMPLATE) GIT-PERL-DEFINES Makefile perllibdir: @echo '$(perllibdir_SQ)' -git-instaweb: git-instaweb.sh generate-script.sh GIT-BUILD-OPTIONS GIT-SCRIPT-DEFINES - $(QUIET_GEN)./generate-script.sh "$<" "$@+" ./GIT-BUILD-OPTIONS && \ +git-instaweb: git-instaweb.sh tools/generate-script.sh GIT-BUILD-OPTIONS GIT-SCRIPT-DEFINES + $(QUIET_GEN)./tools/generate-script.sh "$<" "$@+" ./GIT-BUILD-OPTIONS && \ chmod +x $@+ && \ mv $@+ $@ else # NO_PERL @@ -2808,9 +2815,9 @@ endif # NO_PERL $(SCRIPT_PYTHON_GEN): GIT-BUILD-OPTIONS ifndef NO_PYTHON -$(SCRIPT_PYTHON_GEN): generate-python.sh +$(SCRIPT_PYTHON_GEN): tools/generate-python.sh $(SCRIPT_PYTHON_GEN): % : %.py - $(QUIET_GEN)$(SHELL_PATH) generate-python.sh ./GIT-BUILD-OPTIONS "$<" "$@" + $(QUIET_GEN)$(SHELL_PATH) tools/generate-python.sh ./GIT-BUILD-OPTIONS "$<" "$@" else # NO_PYTHON $(SCRIPT_PYTHON_GEN): % : unimplemented.sh $(QUIET_GEN) \ @@ -3230,9 +3237,9 @@ endif NO_PERL_CPAN_FALLBACKS_SQ = $(subst ','\'',$(NO_PERL_CPAN_FALLBACKS)) endif -perl/build/lib/%.pm: perl/%.pm generate-perl.sh GIT-BUILD-OPTIONS GIT-VERSION-FILE GIT-PERL-DEFINES +perl/build/lib/%.pm: perl/%.pm tools/generate-perl.sh GIT-BUILD-OPTIONS GIT-VERSION-FILE GIT-PERL-DEFINES $(call mkdir_p_parent_template) - $(QUIET_GEN)$(SHELL_PATH) generate-perl.sh ./GIT-BUILD-OPTIONS ./GIT-VERSION-FILE GIT-PERL-HEADER "$<" "$@" + $(QUIET_GEN)$(SHELL_PATH) tools/generate-perl.sh ./GIT-BUILD-OPTIONS ./GIT-VERSION-FILE GIT-PERL-HEADER "$<" "$@" perl/build/man/man3/Git.3pm: perl/Git.pm $(call mkdir_p_parent_template) @@ -3461,15 +3468,15 @@ check: exit 1; \ fi -COCCI_GEN_ALL = .build/contrib/coccinelle/ALL.cocci -COCCI_GLOB = $(wildcard contrib/coccinelle/*.cocci) +COCCI_GEN_ALL = .build/tools/coccinelle/ALL.cocci +COCCI_GLOB = $(wildcard tools/coccinelle/*.cocci) COCCI_RULES_TRACKED = $(COCCI_GLOB:%=.build/%) COCCI_RULES_TRACKED_NO_PENDING = $(filter-out %.pending.cocci,$(COCCI_RULES_TRACKED)) COCCI_RULES = COCCI_RULES += $(COCCI_GEN_ALL) COCCI_RULES += $(COCCI_RULES_TRACKED) COCCI_NAMES = -COCCI_NAMES += $(COCCI_RULES:.build/contrib/coccinelle/%.cocci=%) +COCCI_NAMES += $(COCCI_RULES:.build/tools/coccinelle/%.cocci=%) COCCICHECK_PENDING = $(filter %.pending.cocci,$(COCCI_RULES)) COCCICHECK = $(filter-out $(COCCICHECK_PENDING),$(COCCI_RULES)) @@ -3484,20 +3491,20 @@ COCCICHECK_PATCHES_PENDING_INTREE = $(COCCICHECK_PATCHES_PENDING:.build/%=%) # on $(MAKECMDGOALS) that match these $(COCCI_RULES) COCCI_RULES_GLOB = COCCI_RULES_GLOB += cocci% -COCCI_RULES_GLOB += .build/contrib/coccinelle/% +COCCI_RULES_GLOB += .build/tools/coccinelle/% COCCI_RULES_GLOB += $(COCCICHECK_PATCHES) COCCI_RULES_GLOB += $(COCCICHEC_PATCHES_PENDING) COCCI_RULES_GLOB += $(COCCICHECK_PATCHES_INTREE) COCCI_RULES_GLOB += $(COCCICHECK_PATCHES_PENDING_INTREE) COCCI_GOALS = $(filter $(COCCI_RULES_GLOB),$(MAKECMDGOALS)) -COCCI_TEST_RES = $(wildcard contrib/coccinelle/tests/*.res) +COCCI_TEST_RES = $(wildcard tools/coccinelle/tests/*.res) $(COCCI_RULES_TRACKED): .build/% : % $(call mkdir_p_parent_template) $(QUIET_CP)cp $< $@ -.build/contrib/coccinelle/FOUND_H_SOURCES: $(FOUND_H_SOURCES) +.build/tools/coccinelle/FOUND_H_SOURCES: $(FOUND_H_SOURCES) $(call mkdir_p_parent_template) $(QUIET_GEN) >$@ @@ -3511,12 +3518,12 @@ endif define cocci-rule ## Rule for .build/$(1).patch/$(2); Params: -# $(1) = e.g. ".build/contrib/coccinelle/free.cocci" +# $(1) = e.g. ".build/tools/coccinelle/free.cocci" # $(2) = e.g. "grep.c" # $(3) = e.g. "grep.o" -COCCI_$(1:.build/contrib/coccinelle/%.cocci=%) += $(1).d/$(2).patch +COCCI_$(1:.build/tools/coccinelle/%.cocci=%) += $(1).d/$(2).patch $(1).d/$(2).patch: GIT-SPATCH-DEFINES -$(1).d/$(2).patch: $(if $(and $(SPATCH_USE_O_DEPENDENCIES),$(wildcard $(3))),$(3),.build/contrib/coccinelle/FOUND_H_SOURCES) +$(1).d/$(2).patch: $(if $(and $(SPATCH_USE_O_DEPENDENCIES),$(wildcard $(3))),$(3),.build/tools/coccinelle/FOUND_H_SOURCES) $(1).d/$(2).patch: $(1) $(1).d/$(2).patch: $(1).d/%.patch : % $$(call mkdir_p_parent_template) @@ -3542,13 +3549,13 @@ endif define spatch-rule -.build/contrib/coccinelle/$(1).cocci.patch: $$(COCCI_$(1)) +.build/tools/coccinelle/$(1).cocci.patch: $$(COCCI_$(1)) $$(QUIET_SPATCH_CAT)cat $$^ >$$@ && \ if test -s $$@; \ then \ echo ' ' SPATCH result: $$@; \ fi -contrib/coccinelle/$(1).cocci.patch: .build/contrib/coccinelle/$(1).cocci.patch +tools/coccinelle/$(1).cocci.patch: .build/tools/coccinelle/$(1).cocci.patch $$(QUIET_CP)cp $$< $$@ endef @@ -3562,9 +3569,9 @@ $(COCCI_TEST_RES_GEN): GIT-SPATCH-DEFINES $(COCCI_TEST_RES_GEN): .build/%.res : %.c $(COCCI_TEST_RES_GEN): .build/%.res : %.res ifdef SPATCH_CONCAT_COCCI -$(COCCI_TEST_RES_GEN): .build/contrib/coccinelle/tests/%.res : $(COCCI_GEN_ALL) +$(COCCI_TEST_RES_GEN): .build/tools/coccinelle/tests/%.res : $(COCCI_GEN_ALL) else -$(COCCI_TEST_RES_GEN): .build/contrib/coccinelle/tests/%.res : contrib/coccinelle/%.cocci +$(COCCI_TEST_RES_GEN): .build/tools/coccinelle/tests/%.res : tools/coccinelle/%.cocci endif $(call mkdir_p_parent_template) $(QUIET_SPATCH_TEST)$(SPATCH) $(SPATCH_TEST_FLAGS) \ @@ -3580,14 +3587,14 @@ coccicheck-test: $(COCCI_TEST_RES_GEN) coccicheck: coccicheck-test ifdef SPATCH_CONCAT_COCCI -COCCICHECK_PATCH_MUST_BE_EMPTY_FILES = contrib/coccinelle/ALL.cocci.patch +COCCICHECK_PATCH_MUST_BE_EMPTY_FILES = tools/coccinelle/ALL.cocci.patch else COCCICHECK_PATCH_MUST_BE_EMPTY_FILES = $(COCCICHECK_PATCHES_INTREE) endif coccicheck: $(COCCICHECK_PATCH_MUST_BE_EMPTY_FILES) ! grep ^ $(COCCICHECK_PATCH_MUST_BE_EMPTY_FILES) /dev/null -# See contrib/coccinelle/README +# See tools/coccinelle/README coccicheck-pending: coccicheck-test coccicheck-pending: $(COCCICHECK_PATCHES_PENDING_INTREE) @@ -3861,8 +3868,8 @@ profile-clean: cocciclean: $(RM) GIT-SPATCH-DEFINES - $(RM) -r .build/contrib/coccinelle - $(RM) contrib/coccinelle/*.cocci.patch + $(RM) -r .build/tools/coccinelle + $(RM) tools/coccinelle/*.cocci.patch clean: profile-clean coverage-clean cocciclean $(RM) -r .build $(UNIT_TEST_BIN) @@ -3940,7 +3947,7 @@ check-docs:: ### Make sure built-ins do not have dups and listed in git.c # check-builtins:: - ./check-builtins.sh + ./tools/check-builtins.sh ### Test suite coverage testing # diff --git a/add-interactive.c b/add-interactive.c index 1580639682..3cf8a1dbf8 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -3,7 +3,6 @@ #include "git-compat-util.h" #include "add-interactive.h" #include "color.h" -#include "config.h" #include "diffcore.h" #include "gettext.h" #include "hash.h" @@ -20,120 +19,18 @@ #include "prompt.h" #include "tree.h" -static void init_color(struct repository *r, enum git_colorbool use_color, - const char *section_and_slot, char *dst, - const char *default_color) -{ - char *key = xstrfmt("color.%s", section_and_slot); - const char *value; - - if (!want_color(use_color)) - dst[0] = '\0'; - else if (repo_config_get_value(r, key, &value) || - color_parse(value, dst)) - strlcpy(dst, default_color, COLOR_MAXLEN); - - free(key); -} - -static enum git_colorbool check_color_config(struct repository *r, const char *var) -{ - const char *value; - enum git_colorbool ret; - - if (repo_config_get_value(r, var, &value)) - ret = GIT_COLOR_UNKNOWN; - else - ret = git_config_colorbool(var, value); - - /* - * Do not rely on want_color() to fall back to color.ui for us. It uses - * the value parsed by git_color_config(), which may not have been - * called by the main command. - */ - if (ret == GIT_COLOR_UNKNOWN && - !repo_config_get_value(r, "color.ui", &value)) - ret = git_config_colorbool("color.ui", value); - - return ret; -} - void init_add_i_state(struct add_i_state *s, struct repository *r, - struct add_p_opt *add_p_opt) + struct interactive_options *opts) { s->r = r; - s->context = -1; - s->interhunkcontext = -1; - s->auto_advance = add_p_opt->auto_advance; - - s->use_color_interactive = check_color_config(r, "color.interactive"); - - init_color(r, s->use_color_interactive, "interactive.header", - s->header_color, GIT_COLOR_BOLD); - init_color(r, s->use_color_interactive, "interactive.help", - s->help_color, GIT_COLOR_BOLD_RED); - init_color(r, s->use_color_interactive, "interactive.prompt", - s->prompt_color, GIT_COLOR_BOLD_BLUE); - init_color(r, s->use_color_interactive, "interactive.error", - s->error_color, GIT_COLOR_BOLD_RED); - strlcpy(s->reset_color_interactive, - want_color(s->use_color_interactive) ? GIT_COLOR_RESET : "", COLOR_MAXLEN); - - s->use_color_diff = check_color_config(r, "color.diff"); - - init_color(r, s->use_color_diff, "diff.frag", s->fraginfo_color, - diff_get_color(s->use_color_diff, DIFF_FRAGINFO)); - init_color(r, s->use_color_diff, "diff.context", s->context_color, - "fall back"); - if (!strcmp(s->context_color, "fall back")) - init_color(r, s->use_color_diff, "diff.plain", - s->context_color, - diff_get_color(s->use_color_diff, DIFF_CONTEXT)); - init_color(r, s->use_color_diff, "diff.old", s->file_old_color, - diff_get_color(s->use_color_diff, DIFF_FILE_OLD)); - init_color(r, s->use_color_diff, "diff.new", s->file_new_color, - diff_get_color(s->use_color_diff, DIFF_FILE_NEW)); - strlcpy(s->reset_color_diff, - want_color(s->use_color_diff) ? GIT_COLOR_RESET : "", COLOR_MAXLEN); - - FREE_AND_NULL(s->interactive_diff_filter); - repo_config_get_string(r, "interactive.difffilter", - &s->interactive_diff_filter); - - FREE_AND_NULL(s->interactive_diff_algorithm); - repo_config_get_string(r, "diff.algorithm", - &s->interactive_diff_algorithm); - - if (!repo_config_get_int(r, "diff.context", &s->context)) - if (s->context < 0) - die(_("%s cannot be negative"), "diff.context"); - if (!repo_config_get_int(r, "diff.interHunkContext", &s->interhunkcontext)) - if (s->interhunkcontext < 0) - die(_("%s cannot be negative"), "diff.interHunkContext"); - - repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); - if (s->use_single_key) - setbuf(stdin, NULL); - - if (add_p_opt->context != -1) { - if (add_p_opt->context < 0) - die(_("%s cannot be negative"), "--unified"); - s->context = add_p_opt->context; - } - if (add_p_opt->interhunkcontext != -1) { - if (add_p_opt->interhunkcontext < 0) - die(_("%s cannot be negative"), "--inter-hunk-context"); - s->interhunkcontext = add_p_opt->interhunkcontext; - } + interactive_config_init(&s->cfg, r, opts); } void clear_add_i_state(struct add_i_state *s) { - FREE_AND_NULL(s->interactive_diff_filter); - FREE_AND_NULL(s->interactive_diff_algorithm); + interactive_config_clear(&s->cfg); memset(s, 0, sizeof(*s)); - s->use_color_interactive = GIT_COLOR_UNKNOWN; - s->use_color_diff = GIT_COLOR_UNKNOWN; + interactive_config_clear(&s->cfg); } /* @@ -287,7 +184,7 @@ static void list(struct add_i_state *s, struct string_list *list, int *selected, return; if (opts->header) - color_fprintf_ln(stdout, s->header_color, + color_fprintf_ln(stdout, s->cfg.header_color, "%s", opts->header); for (i = 0; i < list->nr; i++) { @@ -355,7 +252,7 @@ static ssize_t list_and_choose(struct add_i_state *s, list(s, &items->items, items->selected, &opts->list_opts); - color_fprintf(stdout, s->prompt_color, "%s", opts->prompt); + color_fprintf(stdout, s->cfg.prompt_color, "%s", opts->prompt); fputs(singleton ? "> " : ">> ", stdout); fflush(stdout); @@ -433,7 +330,7 @@ static ssize_t list_and_choose(struct add_i_state *s, if (from < 0 || from >= items->items.nr || (singleton && from + 1 != to)) { - color_fprintf_ln(stderr, s->error_color, + color_fprintf_ln(stderr, s->cfg.error_color, _("Huh (%s)?"), p); break; } else if (singleton) { @@ -993,7 +890,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps, free(files->items.items[i].string); } else if (item->index.unmerged || item->worktree.unmerged) { - color_fprintf_ln(stderr, s->error_color, + color_fprintf_ln(stderr, s->cfg.error_color, _("ignoring unmerged: %s"), files->items.items[i].string); free(item); @@ -1015,10 +912,10 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps, opts->prompt = N_("Patch update"); count = list_and_choose(s, files, opts); if (count > 0) { - struct add_p_opt add_p_opt = { - .context = s->context, - .interhunkcontext = s->interhunkcontext, - .auto_advance = s->auto_advance + struct interactive_options opts = { + .context = s->cfg.context, + .interhunkcontext = s->cfg.interhunkcontext, + .auto_advance = s->cfg.auto_advance, }; struct strvec args = STRVEC_INIT; struct pathspec ps_selected = { 0 }; @@ -1030,7 +927,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps, parse_pathspec(&ps_selected, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, PATHSPEC_LITERAL_PATH, "", args.v); - res = run_add_p(s->r, ADD_P_ADD, &add_p_opt, NULL, &ps_selected); + res = run_add_p(s->r, ADD_P_ADD, &opts, NULL, &ps_selected, 0); strvec_clear(&args); clear_pathspec(&ps_selected); } @@ -1066,10 +963,10 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps, struct child_process cmd = CHILD_PROCESS_INIT; strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", NULL); - if (s->context != -1) - strvec_pushf(&cmd.args, "--unified=%i", s->context); - if (s->interhunkcontext != -1) - strvec_pushf(&cmd.args, "--inter-hunk-context=%i", s->interhunkcontext); + if (s->cfg.context != -1) + strvec_pushf(&cmd.args, "--unified=%i", s->cfg.context); + if (s->cfg.interhunkcontext != -1) + strvec_pushf(&cmd.args, "--inter-hunk-context=%i", s->cfg.interhunkcontext); strvec_pushl(&cmd.args, oid_to_hex(!is_initial ? &oid : s->r->hash_algo->empty_tree), "--", NULL); for (i = 0; i < files->items.nr; i++) @@ -1087,17 +984,17 @@ static int run_help(struct add_i_state *s, const struct pathspec *ps UNUSED, struct prefix_item_list *files UNUSED, struct list_and_choose_options *opts UNUSED) { - color_fprintf_ln(stdout, s->help_color, "status - %s", + color_fprintf_ln(stdout, s->cfg.help_color, "status - %s", _("show paths with changes")); - color_fprintf_ln(stdout, s->help_color, "update - %s", + color_fprintf_ln(stdout, s->cfg.help_color, "update - %s", _("add working tree state to the staged set of changes")); - color_fprintf_ln(stdout, s->help_color, "revert - %s", + color_fprintf_ln(stdout, s->cfg.help_color, "revert - %s", _("revert staged set of changes back to the HEAD version")); - color_fprintf_ln(stdout, s->help_color, "patch - %s", + color_fprintf_ln(stdout, s->cfg.help_color, "patch - %s", _("pick hunks and update selectively")); - color_fprintf_ln(stdout, s->help_color, "diff - %s", + color_fprintf_ln(stdout, s->cfg.help_color, "diff - %s", _("view diff between HEAD and index")); - color_fprintf_ln(stdout, s->help_color, "add untracked - %s", + color_fprintf_ln(stdout, s->cfg.help_color, "add untracked - %s", _("add contents of untracked files to the staged set of changes")); return 0; @@ -1105,21 +1002,21 @@ static int run_help(struct add_i_state *s, const struct pathspec *ps UNUSED, static void choose_prompt_help(struct add_i_state *s) { - color_fprintf_ln(stdout, s->help_color, "%s", + color_fprintf_ln(stdout, s->cfg.help_color, "%s", _("Prompt help:")); - color_fprintf_ln(stdout, s->help_color, "1 - %s", + color_fprintf_ln(stdout, s->cfg.help_color, "1 - %s", _("select a single item")); - color_fprintf_ln(stdout, s->help_color, "3-5 - %s", + color_fprintf_ln(stdout, s->cfg.help_color, "3-5 - %s", _("select a range of items")); - color_fprintf_ln(stdout, s->help_color, "2-3,6-9 - %s", + color_fprintf_ln(stdout, s->cfg.help_color, "2-3,6-9 - %s", _("select multiple ranges")); - color_fprintf_ln(stdout, s->help_color, "foo - %s", + color_fprintf_ln(stdout, s->cfg.help_color, "foo - %s", _("select item based on unique prefix")); - color_fprintf_ln(stdout, s->help_color, "-... - %s", + color_fprintf_ln(stdout, s->cfg.help_color, "-... - %s", _("unselect specified items")); - color_fprintf_ln(stdout, s->help_color, "* - %s", + color_fprintf_ln(stdout, s->cfg.help_color, "* - %s", _("choose all items")); - color_fprintf_ln(stdout, s->help_color, " - %s", + color_fprintf_ln(stdout, s->cfg.help_color, " - %s", _("(empty) finish selecting")); } @@ -1154,7 +1051,7 @@ static void print_command_item(int i, int selected UNUSED, static void command_prompt_help(struct add_i_state *s) { - const char *help_color = s->help_color; + const char *help_color = s->cfg.help_color; color_fprintf_ln(stdout, help_color, "%s", _("Prompt help:")); color_fprintf_ln(stdout, help_color, "1 - %s", _("select a numbered item")); @@ -1165,7 +1062,7 @@ static void command_prompt_help(struct add_i_state *s) } int run_add_i(struct repository *r, const struct pathspec *ps, - struct add_p_opt *add_p_opt) + struct interactive_options *interactive_opts) { struct add_i_state s = { NULL }; struct print_command_item_data data = { "[", "]" }; @@ -1208,15 +1105,15 @@ int run_add_i(struct repository *r, const struct pathspec *ps, ->util = util; } - init_add_i_state(&s, r, add_p_opt); + init_add_i_state(&s, r, interactive_opts); /* * When color was asked for, use the prompt color for * highlighting, otherwise use square brackets. */ - if (want_color(s.use_color_interactive)) { - data.color = s.prompt_color; - data.reset = s.reset_color_interactive; + if (want_color(s.cfg.use_color_interactive)) { + data.color = s.cfg.prompt_color; + data.reset = s.cfg.reset_color_interactive; } print_file_item_data.color = data.color; print_file_item_data.reset = data.reset; diff --git a/add-interactive.h b/add-interactive.h index 7843397775..eefa2edc7c 100644 --- a/add-interactive.h +++ b/add-interactive.h @@ -1,57 +1,21 @@ #ifndef ADD_INTERACTIVE_H #define ADD_INTERACTIVE_H -#include "color.h" +#include "add-patch.h" -struct add_p_opt { - int context; - int interhunkcontext; - int auto_advance; -}; - -#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1, .auto_advance = 1 } +struct pathspec; +struct repository; struct add_i_state { struct repository *r; - enum git_colorbool use_color_interactive; - enum git_colorbool use_color_diff; - char header_color[COLOR_MAXLEN]; - char help_color[COLOR_MAXLEN]; - char prompt_color[COLOR_MAXLEN]; - char error_color[COLOR_MAXLEN]; - char reset_color_interactive[COLOR_MAXLEN]; - - char fraginfo_color[COLOR_MAXLEN]; - char context_color[COLOR_MAXLEN]; - char file_old_color[COLOR_MAXLEN]; - char file_new_color[COLOR_MAXLEN]; - char reset_color_diff[COLOR_MAXLEN]; - - int use_single_key; - char *interactive_diff_filter, *interactive_diff_algorithm; - int context, interhunkcontext; - int auto_advance; + struct interactive_config cfg; }; void init_add_i_state(struct add_i_state *s, struct repository *r, - struct add_p_opt *add_p_opt); + struct interactive_options *opts); void clear_add_i_state(struct add_i_state *s); -struct repository; -struct pathspec; int run_add_i(struct repository *r, const struct pathspec *ps, - struct add_p_opt *add_p_opt); - -enum add_p_mode { - ADD_P_ADD, - ADD_P_STASH, - ADD_P_RESET, - ADD_P_CHECKOUT, - ADD_P_WORKTREE, -}; - -int run_add_p(struct repository *r, enum add_p_mode mode, - struct add_p_opt *o, const char *revision, - const struct pathspec *ps); + struct interactive_options *opts); #endif diff --git a/add-patch.c b/add-patch.c index 30df920723..f27edcbe8d 100644 --- a/add-patch.c +++ b/add-patch.c @@ -2,11 +2,15 @@ #define DISABLE_SIGN_COMPARE_WARNINGS #include "git-compat-util.h" -#include "add-interactive.h" +#include "add-patch.h" #include "advice.h" +#include "commit.h" +#include "config.h" +#include "diff.h" #include "editor.h" #include "environment.h" #include "gettext.h" +#include "hex.h" #include "object-name.h" #include "pager.h" #include "read-cache-ll.h" @@ -260,7 +264,10 @@ struct hunk { }; struct add_p_state { - struct add_i_state s; + struct repository *r; + struct index_state *index; + const char *index_file; + struct interactive_config cfg; struct strbuf answer, buf; /* parsed diff */ @@ -278,6 +285,123 @@ struct add_p_state { const char *revision; }; +static void init_color(struct repository *r, + enum git_colorbool use_color, + const char *section_and_slot, char *dst, + const char *default_color) +{ + char *key = xstrfmt("color.%s", section_and_slot); + const char *value; + + if (!want_color(use_color)) + dst[0] = '\0'; + else if (repo_config_get_value(r, key, &value) || + color_parse(value, dst)) + strlcpy(dst, default_color, COLOR_MAXLEN); + + free(key); +} + +static enum git_colorbool check_color_config(struct repository *r, const char *var) +{ + const char *value; + enum git_colorbool ret; + + if (repo_config_get_value(r, var, &value)) + ret = GIT_COLOR_UNKNOWN; + else + ret = git_config_colorbool(var, value); + + /* + * Do not rely on want_color() to fall back to color.ui for us. It uses + * the value parsed by git_color_config(), which may not have been + * called by the main command. + */ + if (ret == GIT_COLOR_UNKNOWN && + !repo_config_get_value(r, "color.ui", &value)) + ret = git_config_colorbool("color.ui", value); + + return ret; +} + +void interactive_config_init(struct interactive_config *cfg, + struct repository *r, + struct interactive_options *opts) +{ + cfg->context = -1; + cfg->interhunkcontext = -1; + cfg->auto_advance = opts->auto_advance; + + cfg->use_color_interactive = check_color_config(r, "color.interactive"); + + init_color(r, cfg->use_color_interactive, "interactive.header", + cfg->header_color, GIT_COLOR_BOLD); + init_color(r, cfg->use_color_interactive, "interactive.help", + cfg->help_color, GIT_COLOR_BOLD_RED); + init_color(r, cfg->use_color_interactive, "interactive.prompt", + cfg->prompt_color, GIT_COLOR_BOLD_BLUE); + init_color(r, cfg->use_color_interactive, "interactive.error", + cfg->error_color, GIT_COLOR_BOLD_RED); + strlcpy(cfg->reset_color_interactive, + want_color(cfg->use_color_interactive) ? GIT_COLOR_RESET : "", COLOR_MAXLEN); + + cfg->use_color_diff = check_color_config(r, "color.diff"); + + init_color(r, cfg->use_color_diff, "diff.frag", cfg->fraginfo_color, + diff_get_color(cfg->use_color_diff, DIFF_FRAGINFO)); + init_color(r, cfg->use_color_diff, "diff.context", cfg->context_color, + "fall back"); + if (!strcmp(cfg->context_color, "fall back")) + init_color(r, cfg->use_color_diff, "diff.plain", + cfg->context_color, + diff_get_color(cfg->use_color_diff, DIFF_CONTEXT)); + init_color(r, cfg->use_color_diff, "diff.old", cfg->file_old_color, + diff_get_color(cfg->use_color_diff, DIFF_FILE_OLD)); + init_color(r, cfg->use_color_diff, "diff.new", cfg->file_new_color, + diff_get_color(cfg->use_color_diff, DIFF_FILE_NEW)); + strlcpy(cfg->reset_color_diff, + want_color(cfg->use_color_diff) ? GIT_COLOR_RESET : "", COLOR_MAXLEN); + + FREE_AND_NULL(cfg->interactive_diff_filter); + repo_config_get_string(r, "interactive.difffilter", + &cfg->interactive_diff_filter); + + FREE_AND_NULL(cfg->interactive_diff_algorithm); + repo_config_get_string(r, "diff.algorithm", + &cfg->interactive_diff_algorithm); + + if (!repo_config_get_int(r, "diff.context", &cfg->context)) + if (cfg->context < 0) + die(_("%s cannot be negative"), "diff.context"); + if (!repo_config_get_int(r, "diff.interHunkContext", &cfg->interhunkcontext)) + if (cfg->interhunkcontext < 0) + die(_("%s cannot be negative"), "diff.interHunkContext"); + + repo_config_get_bool(r, "interactive.singlekey", &cfg->use_single_key); + if (cfg->use_single_key) + setbuf(stdin, NULL); + + if (opts->context != -1) { + if (opts->context < 0) + die(_("%s cannot be negative"), "--unified"); + cfg->context = opts->context; + } + if (opts->interhunkcontext != -1) { + if (opts->interhunkcontext < 0) + die(_("%s cannot be negative"), "--inter-hunk-context"); + cfg->interhunkcontext = opts->interhunkcontext; + } +} + +void interactive_config_clear(struct interactive_config *cfg) +{ + FREE_AND_NULL(cfg->interactive_diff_filter); + FREE_AND_NULL(cfg->interactive_diff_algorithm); + memset(cfg, 0, sizeof(*cfg)); + cfg->use_color_interactive = GIT_COLOR_UNKNOWN; + cfg->use_color_diff = GIT_COLOR_UNKNOWN; +} + static void add_p_state_clear(struct add_p_state *s) { size_t i; @@ -289,7 +413,7 @@ static void add_p_state_clear(struct add_p_state *s) for (i = 0; i < s->file_diff_nr; i++) free(s->file_diff[i].hunk); free(s->file_diff); - clear_add_i_state(&s->s); + interactive_config_clear(&s->cfg); } __attribute__((format (printf, 2, 3))) @@ -298,9 +422,9 @@ static void err(struct add_p_state *s, const char *fmt, ...) va_list args; va_start(args, fmt); - fputs(s->s.error_color, stdout); + fputs(s->cfg.error_color, stdout); vprintf(fmt, args); - puts(s->s.reset_color_interactive); + puts(s->cfg.reset_color_interactive); va_end(args); } @@ -318,7 +442,7 @@ static void setup_child_process(struct add_p_state *s, cp->git_cmd = 1; strvec_pushf(&cp->env, - INDEX_ENVIRONMENT "=%s", s->s.r->index_file); + INDEX_ENVIRONMENT "=%s", s->index_file); } static int parse_range(const char **p, @@ -423,19 +547,19 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) int res; strvec_pushv(&args, s->mode->diff_cmd); - if (s->s.context != -1) - strvec_pushf(&args, "--unified=%i", s->s.context); - if (s->s.interhunkcontext != -1) - strvec_pushf(&args, "--inter-hunk-context=%i", s->s.interhunkcontext); - if (s->s.interactive_diff_algorithm) - strvec_pushf(&args, "--diff-algorithm=%s", s->s.interactive_diff_algorithm); + if (s->cfg.context != -1) + strvec_pushf(&args, "--unified=%i", s->cfg.context); + if (s->cfg.interhunkcontext != -1) + strvec_pushf(&args, "--inter-hunk-context=%i", s->cfg.interhunkcontext); + if (s->cfg.interactive_diff_algorithm) + strvec_pushf(&args, "--diff-algorithm=%s", s->cfg.interactive_diff_algorithm); if (s->revision) { struct object_id oid; strvec_push(&args, /* could be on an unborn branch */ !strcmp("HEAD", s->revision) && - repo_get_oid(s->s.r, "HEAD", &oid) ? - empty_tree_oid_hex(s->s.r->hash_algo) : s->revision); + repo_get_oid(s->r, "HEAD", &oid) ? + empty_tree_oid_hex(s->r->hash_algo) : s->revision); } color_arg_index = args.nr; /* Use `--no-color` explicitly, just in case `diff.color = always`. */ @@ -457,9 +581,9 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) } strbuf_complete_line(plain); - if (want_color_fd(1, s->s.use_color_diff)) { + if (want_color_fd(1, s->cfg.use_color_diff)) { struct child_process colored_cp = CHILD_PROCESS_INIT; - const char *diff_filter = s->s.interactive_diff_filter; + const char *diff_filter = s->cfg.interactive_diff_filter; setup_child_process(s, &colored_cp, NULL); xsnprintf((char *)args.v[color_arg_index], 8, "--color"); @@ -692,7 +816,7 @@ static void render_hunk(struct add_p_state *s, struct hunk *hunk, hunk->colored_end - hunk->colored_start); return; } else { - strbuf_addstr(out, s->s.fraginfo_color); + strbuf_addstr(out, s->cfg.fraginfo_color); p = s->colored.buf + header->colored_extra_start; len = header->colored_extra_end - header->colored_extra_start; @@ -714,7 +838,7 @@ static void render_hunk(struct add_p_state *s, struct hunk *hunk, if (len) strbuf_add(out, p, len); else if (colored) - strbuf_addf(out, "%s\n", s->s.reset_color_diff); + strbuf_addf(out, "%s\n", s->cfg.reset_color_diff); else strbuf_addch(out, '\n'); } @@ -1103,12 +1227,12 @@ static void recolor_hunk(struct add_p_state *s, struct hunk *hunk) strbuf_addstr(&s->colored, plain[current] == '-' ? - s->s.file_old_color : + s->cfg.file_old_color : plain[current] == '+' ? - s->s.file_new_color : - s->s.context_color); + s->cfg.file_new_color : + s->cfg.context_color); strbuf_add(&s->colored, plain + current, eol - current); - strbuf_addstr(&s->colored, s->s.reset_color_diff); + strbuf_addstr(&s->colored, s->cfg.reset_color_diff); if (next > eol) strbuf_add(&s->colored, plain + eol, next - eol); current = next; @@ -1147,7 +1271,7 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk) "removed, then the edit is\n" "aborted and the hunk is left unchanged.\n")); - if (strbuf_edit_interactively(s->s.r, &s->buf, + if (strbuf_edit_interactively(s->r, &s->buf, "addp-hunk-edit.diff", NULL) < 0) return -1; @@ -1237,7 +1361,7 @@ static int run_apply_check(struct add_p_state *s, static int read_single_character(struct add_p_state *s) { - if (s->s.use_single_key) { + if (s->cfg.use_single_key) { int res = read_key_without_echo(&s->answer); printf("%s\n", res == EOF ? "" : s->answer.buf); return res; @@ -1251,7 +1375,7 @@ static int read_single_character(struct add_p_state *s) static int prompt_yesno(struct add_p_state *s, const char *prompt) { for (;;) { - color_fprintf(stdout, s->s.prompt_color, "%s", _(prompt)); + color_fprintf(stdout, s->cfg.prompt_color, "%s", _(prompt)); fflush(stdout); if (read_single_character(s) == EOF) return -1; @@ -1439,7 +1563,7 @@ static void apply_patch(struct add_p_state *s, struct file_diff *file_diff) strbuf_reset(&s->buf); reassemble_patch(s, file_diff, 0, &s->buf); - discard_index(s->s.r->index); + discard_index(s->index); if (s->mode->apply_for_checkout) apply_for_checkout(s, &s->buf, s->mode->is_reverse); @@ -1450,9 +1574,11 @@ static void apply_patch(struct add_p_state *s, struct file_diff *file_diff) NULL, 0, NULL, 0)) error(_("'git apply' failed")); } - if (repo_read_index(s->s.r) >= 0) - repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0, - 1, NULL, NULL, NULL); + if (read_index_from(s->index, s->index_file, s->r->gitdir) >= 0 && + s->index == s->r->index) { + repo_refresh_and_write_index(s->r, REFRESH_QUIET, 0, + 1, NULL, NULL, NULL); + } } } @@ -1478,7 +1604,9 @@ static bool get_first_undecided(const struct file_diff *file_diff, size_t *idx) return false; } -static size_t patch_update_file(struct add_p_state *s, size_t idx) +static size_t patch_update_file(struct add_p_state *s, + size_t idx, + unsigned flags) { size_t hunk_index = 0; ssize_t i, undecided_previous, undecided_next, rendered_hunk_index = -1; @@ -1540,7 +1668,7 @@ static size_t patch_update_file(struct add_p_state *s, size_t idx) /* Everything decided? */ if (undecided_previous < 0 && undecided_next < 0 && hunk->use != UNDECIDED_HUNK) { - if (!s->s.auto_advance) + if (!s->cfg.auto_advance) all_decided = 1; else { patch_update_resp++; @@ -1551,7 +1679,7 @@ static size_t patch_update_file(struct add_p_state *s, size_t idx) if (file_diff->hunk_nr) { if (rendered_hunk_index != hunk_index) { if (use_pager) { - setup_pager(s->s.r); + setup_pager(s->r); sigchain_push(SIGPIPE, SIG_IGN); } render_hunk(s, hunk, 0, colored, &s->buf); @@ -1589,16 +1717,17 @@ static size_t patch_update_file(struct add_p_state *s, size_t idx) permitted |= ALLOW_SPLIT; strbuf_addstr(&s->buf, ",s"); } - if (hunk_index + 1 > file_diff->mode_change && + if (!(flags & ADD_P_DISALLOW_EDIT) && + hunk_index + 1 > file_diff->mode_change && !file_diff->deleted) { permitted |= ALLOW_EDIT; strbuf_addstr(&s->buf, ",e"); } - if (!s->s.auto_advance && s->file_diff_nr > 1) { + if (!s->cfg.auto_advance && s->file_diff_nr > 1) { permitted |= ALLOW_GOTO_NEXT_FILE; strbuf_addstr(&s->buf, ",>"); } - if (!s->s.auto_advance && s->file_diff_nr > 1) { + if (!s->cfg.auto_advance && s->file_diff_nr > 1) { permitted |= ALLOW_GOTO_PREVIOUS_FILE; strbuf_addstr(&s->buf, ",<"); } @@ -1613,7 +1742,7 @@ static size_t patch_update_file(struct add_p_state *s, size_t idx) else prompt_mode_type = PROMPT_HUNK; - printf("%s(%"PRIuMAX"/%"PRIuMAX") ", s->s.prompt_color, + printf("%s(%"PRIuMAX"/%"PRIuMAX") ", s->cfg.prompt_color, (uintmax_t)hunk_index + 1, (uintmax_t)(file_diff->hunk_nr ? file_diff->hunk_nr @@ -1626,8 +1755,8 @@ static size_t patch_update_file(struct add_p_state *s, size_t idx) } printf(_(s->mode->prompt_mode[prompt_mode_type]), hunk_use_decision, s->buf.buf); - if (*s->s.reset_color_interactive) - fputs(s->s.reset_color_interactive, stdout); + if (*s->cfg.reset_color_interactive) + fputs(s->cfg.reset_color_interactive, stdout); fflush(stdout); if (read_single_character(s) == EOF) { patch_update_resp = s->file_diff_nr; @@ -1678,7 +1807,7 @@ soft_increment: } else if (ch == 'q') { patch_update_resp = s->file_diff_nr; break; - } else if (!s->s.auto_advance && s->answer.buf[0] == '>') { + } else if (!s->cfg.auto_advance && s->answer.buf[0] == '>') { if (permitted & ALLOW_GOTO_NEXT_FILE) { if (patch_update_resp == s->file_diff_nr - 1) patch_update_resp = 0; @@ -1689,7 +1818,7 @@ soft_increment: err(s, _("No next file")); continue; } - } else if (!s->s.auto_advance && s->answer.buf[0] == '<') { + } else if (!s->cfg.auto_advance && s->answer.buf[0] == '<') { if (permitted & ALLOW_GOTO_PREVIOUS_FILE) { if (patch_update_resp == 0) patch_update_resp = s->file_diff_nr - 1; @@ -1812,7 +1941,7 @@ soft_increment: err(s, _("Sorry, cannot split this hunk")); } else if (!split_hunk(s, file_diff, hunk - file_diff->hunk)) { - color_fprintf_ln(stdout, s->s.header_color, + color_fprintf_ln(stdout, s->cfg.header_color, _("Split into %d hunks."), (int)splittable_into); rendered_hunk_index = -1; @@ -1830,7 +1959,7 @@ soft_increment: } else if (s->answer.buf[0] == '?') { const char *p = _(help_patch_remainder), *eol = p; - color_fprintf(stdout, s->s.help_color, "%s", + color_fprintf(stdout, s->cfg.help_color, "%s", _(s->mode->help_patch_text)); /* @@ -1854,13 +1983,13 @@ soft_increment: if (file_diff->hunk[i].use == SKIP_HUNK) skipped += 1; } - color_fprintf_ln(stdout, s->s.help_color, _(p), + color_fprintf_ln(stdout, s->cfg.help_color, _(p), total, used, skipped); } if (*p != '?' && !strchr(s->buf.buf, *p)) continue; - color_fprintf_ln(stdout, s->s.help_color, + color_fprintf_ln(stdout, s->cfg.help_color, "%.*s", (int)(eol - p), p); } } else { @@ -1869,23 +1998,62 @@ soft_increment: } } - if (s->s.auto_advance) + if (s->cfg.auto_advance) apply_patch(s, file_diff); putchar('\n'); return patch_update_resp; } +static int run_add_p_common(struct add_p_state *state, + const struct pathspec *ps, + unsigned flags) +{ + size_t binary_count = 0; + size_t i; + + if (parse_diff(state, ps) < 0) + return -1; + + for (i = 0; i < state->file_diff_nr;) { + if (state->file_diff[i].binary && !state->file_diff[i].hunk_nr) { + binary_count++; + i++; + continue; + } + if ((i = patch_update_file(state, i, flags)) == state->file_diff_nr) + break; + } + + if (!state->cfg.auto_advance) + for (i = 0; i < state->file_diff_nr; i++) + apply_patch(state, state->file_diff + i); + + if (state->file_diff_nr == 0) + err(state, _("No changes.")); + else if (binary_count == state->file_diff_nr) + err(state, _("Only binary files changed.")); + + return 0; +} + int run_add_p(struct repository *r, enum add_p_mode mode, - struct add_p_opt *o, const char *revision, - const struct pathspec *ps) + struct interactive_options *opts, const char *revision, + const struct pathspec *ps, + unsigned flags) { struct add_p_state s = { - { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT + .r = r, + .index = r->index, + .index_file = r->index_file, + .answer = STRBUF_INIT, + .buf = STRBUF_INIT, + .plain = STRBUF_INIT, + .colored = STRBUF_INIT, }; - size_t i, binary_count = 0; + int ret; - init_add_i_state(&s.s, r, o); + interactive_config_init(&s.cfg, r, opts); if (mode == ADD_P_STASH) s.mode = &patch_mode_stash; @@ -1916,30 +2084,91 @@ int run_add_p(struct repository *r, enum add_p_mode mode, if (repo_read_index(r) < 0 || (!s.mode->index_only && repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1, - NULL, NULL, NULL) < 0) || - parse_diff(&s, ps) < 0) { - add_p_state_clear(&s); - return -1; + NULL, NULL, NULL) < 0)) { + ret = -1; + goto out; } - for (i = 0; i < s.file_diff_nr;) { - if (s.file_diff[i].binary && !s.file_diff[i].hunk_nr) { - binary_count++; - i++; - continue; - } - if ((i = patch_update_file(&s, i)) == s.file_diff_nr) - break; - } - if (!s.s.auto_advance) - for (i = 0; i < s.file_diff_nr; i++) - apply_patch(&s, s.file_diff + i); + ret = run_add_p_common(&s, ps, flags); + if (ret < 0) + goto out; - if (s.file_diff_nr == 0) - err(&s, _("No changes.")); - else if (binary_count == s.file_diff_nr) - err(&s, _("Only binary files changed.")); + ret = 0; +out: add_p_state_clear(&s); - return 0; + return ret; +} + +int run_add_p_index(struct repository *r, + struct index_state *index, + const char *index_file, + struct interactive_options *opts, + const char *revision, + const struct pathspec *ps, + unsigned flags) +{ + struct patch_mode mode = { + .apply_args = { "--cached", NULL }, + .apply_check_args = { "--cached", NULL }, + .prompt_mode = { + N_("Stage mode change [y,n,q,a,d%s,?]? "), + N_("Stage deletion [y,n,q,a,d%s,?]? "), + N_("Stage addition [y,n,q,a,d%s,?]? "), + N_("Stage this hunk [y,n,q,a,d%s,?]? ") + }, + .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk " + "will immediately be marked for staging."), + .help_patch_text = + N_("y - stage this hunk\n" + "n - do not stage this hunk\n" + "q - quit; do not stage this hunk or any of the remaining " + "ones\n" + "a - stage this hunk and all later hunks in the file\n" + "d - do not stage this hunk or any of the later hunks in " + "the file\n"), + .index_only = 1, + }; + struct add_p_state s = { + .r = r, + .index = index, + .index_file = index_file, + .answer = STRBUF_INIT, + .buf = STRBUF_INIT, + .plain = STRBUF_INIT, + .colored = STRBUF_INIT, + .mode = &mode, + .revision = revision, + }; + char parent_tree_oid[GIT_MAX_HEXSZ + 1]; + struct commit *commit; + int ret; + + interactive_config_init(&s.cfg, r, opts); + + commit = lookup_commit_reference_by_name(revision); + if (!commit) { + err(&s, _("Revision does not refer to a commit")); + ret = -1; + goto out; + } + + if (commit->parents) + oid_to_hex_r(parent_tree_oid, get_commit_tree_oid(commit->parents->item)); + else + oid_to_hex_r(parent_tree_oid, r->hash_algo->empty_tree); + + mode.diff_cmd[0] = "diff-tree"; + mode.diff_cmd[1] = "-r"; + mode.diff_cmd[2] = parent_tree_oid; + + ret = run_add_p_common(&s, ps, flags); + if (ret < 0) + goto out; + + ret = 0; + +out: + add_p_state_clear(&s); + return ret; } diff --git a/add-patch.h b/add-patch.h new file mode 100644 index 0000000000..fb6d975b68 --- /dev/null +++ b/add-patch.h @@ -0,0 +1,74 @@ +#ifndef ADD_PATCH_H +#define ADD_PATCH_H + +#include "color.h" + +struct index_state; +struct pathspec; +struct repository; + +struct interactive_options { + int context; + int interhunkcontext; + int auto_advance; +}; + +#define INTERACTIVE_OPTIONS_INIT { \ + .context = -1, \ + .interhunkcontext = -1, \ + .auto_advance = 1, \ +} + +struct interactive_config { + enum git_colorbool use_color_interactive; + enum git_colorbool use_color_diff; + char header_color[COLOR_MAXLEN]; + char help_color[COLOR_MAXLEN]; + char prompt_color[COLOR_MAXLEN]; + char error_color[COLOR_MAXLEN]; + char reset_color_interactive[COLOR_MAXLEN]; + + char fraginfo_color[COLOR_MAXLEN]; + char context_color[COLOR_MAXLEN]; + char file_old_color[COLOR_MAXLEN]; + char file_new_color[COLOR_MAXLEN]; + char reset_color_diff[COLOR_MAXLEN]; + + int use_single_key; + char *interactive_diff_filter, *interactive_diff_algorithm; + int context, interhunkcontext; + int auto_advance; +}; + +void interactive_config_init(struct interactive_config *cfg, + struct repository *r, + struct interactive_options *opts); +void interactive_config_clear(struct interactive_config *cfg); + +enum add_p_mode { + ADD_P_ADD, + ADD_P_STASH, + ADD_P_RESET, + ADD_P_CHECKOUT, + ADD_P_WORKTREE, +}; + +enum add_p_flags { + /* Disallow "editing" hunks. */ + ADD_P_DISALLOW_EDIT = (1 << 0), +}; + +int run_add_p(struct repository *r, enum add_p_mode mode, + struct interactive_options *opts, const char *revision, + const struct pathspec *ps, + unsigned flags); + +int run_add_p_index(struct repository *r, + struct index_state *index, + const char *index_file, + struct interactive_options *opts, + const char *revision, + const struct pathspec *ps, + unsigned flags); + +#endif @@ -42,6 +42,7 @@ struct gitdiff_data { struct strbuf *root; + const char *patch_input_file; int linenr; int p_value; }; @@ -900,7 +901,8 @@ static int parse_traditional_patch(struct apply_state *state, } } if (!name) - return error(_("unable to find filename in patch at line %d"), state->linenr); + return error(_("unable to find filename in patch at %s:%d"), + state->patch_input_file, state->linenr); return 0; } @@ -937,20 +939,35 @@ static int gitdiff_verify_name(struct gitdiff_data *state, if (*name) { char *another; - if (isnull) + if (isnull) { + if (state->patch_input_file) + return error(_("git apply: bad git-diff - expected /dev/null, got %s at %s:%d"), + *name, state->patch_input_file, state->linenr); return error(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), *name, state->linenr); + } another = find_name(state->root, line, NULL, state->p_value, TERM_TAB); if (!another || strcmp(another, *name)) { free(another); + if (state->patch_input_file) + return error((side == DIFF_NEW_NAME) ? + _("git apply: bad git-diff - inconsistent new filename at %s:%d") : + _("git apply: bad git-diff - inconsistent old filename at %s:%d"), + state->patch_input_file, state->linenr); return error((side == DIFF_NEW_NAME) ? - _("git apply: bad git-diff - inconsistent new filename on line %d") : - _("git apply: bad git-diff - inconsistent old filename on line %d"), state->linenr); + _("git apply: bad git-diff - inconsistent new filename on line %d") : + _("git apply: bad git-diff - inconsistent old filename on line %d"), + state->linenr); } free(another); } else { - if (!is_dev_null(line)) - return error(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr); + if (!is_dev_null(line)) { + if (state->patch_input_file) + return error(_("git apply: bad git-diff - expected /dev/null at %s:%d"), + state->patch_input_file, state->linenr); + return error(_("git apply: bad git-diff - expected /dev/null on line %d"), + state->linenr); + } } return 0; @@ -974,12 +991,19 @@ static int gitdiff_newname(struct gitdiff_data *state, DIFF_NEW_NAME); } -static int parse_mode_line(const char *line, int linenr, unsigned int *mode) +static int parse_mode_line(const char *line, + const char *patch_input_file, + int linenr, + unsigned int *mode) { char *end; *mode = strtoul(line, &end, 8); - if (end == line || !isspace(*end)) + if (end == line || !isspace(*end)) { + if (patch_input_file) + return error(_("invalid mode at %s:%d: %s"), + patch_input_file, linenr, line); return error(_("invalid mode on line %d: %s"), linenr, line); + } *mode = canon_mode(*mode); return 0; } @@ -988,14 +1012,16 @@ static int gitdiff_oldmode(struct gitdiff_data *state, const char *line, struct patch *patch) { - return parse_mode_line(line, state->linenr, &patch->old_mode); + return parse_mode_line(line, state->patch_input_file, state->linenr, + &patch->old_mode); } static int gitdiff_newmode(struct gitdiff_data *state, const char *line, struct patch *patch) { - return parse_mode_line(line, state->linenr, &patch->new_mode); + return parse_mode_line(line, state->patch_input_file, state->linenr, + &patch->new_mode); } static int gitdiff_delete(struct gitdiff_data *state, @@ -1314,6 +1340,7 @@ static int check_header_line(int linenr, struct patch *patch) } int parse_git_diff_header(struct strbuf *root, + const char *patch_input_file, int *linenr, int p_value, const char *line, @@ -1345,6 +1372,7 @@ int parse_git_diff_header(struct strbuf *root, size -= len; (*linenr)++; parse_hdr_state.root = root; + parse_hdr_state.patch_input_file = patch_input_file; parse_hdr_state.linenr = *linenr; parse_hdr_state.p_value = p_value; @@ -1382,6 +1410,7 @@ int parse_git_diff_header(struct strbuf *root, int res; if (len < oplen || memcmp(p->str, line, oplen)) continue; + parse_hdr_state.linenr = *linenr; res = p->fn(&parse_hdr_state, line + oplen, patch); if (res < 0) return -1; @@ -1396,12 +1425,20 @@ int parse_git_diff_header(struct strbuf *root, done: if (!patch->old_name && !patch->new_name) { if (!patch->def_name) { - error(Q_("git diff header lacks filename information when removing " - "%d leading pathname component (line %d)", - "git diff header lacks filename information when removing " - "%d leading pathname components (line %d)", - parse_hdr_state.p_value), - parse_hdr_state.p_value, *linenr); + if (patch_input_file) + error(Q_("git diff header lacks filename information when removing " + "%d leading pathname component at %s:%d", + "git diff header lacks filename information when removing " + "%d leading pathname components at %s:%d", + parse_hdr_state.p_value), + parse_hdr_state.p_value, patch_input_file, *linenr); + else + error(Q_("git diff header lacks filename information when removing " + "%d leading pathname component (line %d)", + "git diff header lacks filename information when removing " + "%d leading pathname components (line %d)", + parse_hdr_state.p_value), + parse_hdr_state.p_value, *linenr); return -128; } patch->old_name = xstrdup(patch->def_name); @@ -1409,8 +1446,12 @@ done: } if ((!patch->new_name && !patch->is_delete) || (!patch->old_name && !patch->is_new)) { - error(_("git diff header lacks filename information " - "(line %d)"), *linenr); + if (patch_input_file) + error(_("git diff header lacks filename information at %s:%d"), + patch_input_file, *linenr); + else + error(_("git diff header lacks filename information (line %d)"), + *linenr); return -128; } patch->is_toplevel_relative = 1; @@ -1577,8 +1618,9 @@ static int find_header(struct apply_state *state, struct fragment dummy; if (parse_fragment_header(line, len, &dummy) < 0) continue; - error(_("patch fragment without header at line %d: %.*s"), - state->linenr, (int)len-1, line); + error(_("patch fragment without header at %s:%d: %.*s"), + state->patch_input_file, state->linenr, + (int)len-1, line); return -128; } @@ -1590,7 +1632,9 @@ static int find_header(struct apply_state *state, * or mode change, so we handle that specially */ if (!memcmp("diff --git ", line, 11)) { - int git_hdr_len = parse_git_diff_header(&state->root, &state->linenr, + int git_hdr_len = parse_git_diff_header(&state->root, + state->patch_input_file, + &state->linenr, state->p_value, line, len, size, patch); if (git_hdr_len < 0) @@ -1796,8 +1840,16 @@ static int parse_fragment(struct apply_state *state, trailing++; check_old_for_crlf(patch, line, len); if (!state->apply_in_reverse && - state->ws_error_action == correct_ws_error) - check_whitespace(state, line, len, patch->ws_rule); + state->ws_error_action == correct_ws_error) { + const char *test_line = line; + int test_len = len; + if (*line == '\n') { + test_line = " \n"; + test_len = 2; + } + check_whitespace(state, test_line, test_len, + patch->ws_rule); + } break; case '-': if (!state->apply_in_reverse) @@ -1875,7 +1927,8 @@ static int parse_single_patch(struct apply_state *state, len = parse_fragment(state, line, size, patch, fragment); if (len <= 0) { free(fragment); - return error(_("corrupt patch at line %d"), state->linenr); + return error(_("corrupt patch at %s:%d"), + state->patch_input_file, state->linenr); } fragment->patch = line; fragment->size = len; @@ -2065,8 +2118,8 @@ static struct fragment *parse_binary_hunk(struct apply_state *state, corrupt: free(data); *status_p = -1; - error(_("corrupt binary patch at line %d: %.*s"), - state->linenr-1, llen-1, buffer); + error(_("corrupt binary patch at %s:%d: %.*s"), + state->patch_input_file, state->linenr-1, llen-1, buffer); return NULL; } @@ -2102,7 +2155,8 @@ static int parse_binary(struct apply_state *state, forward = parse_binary_hunk(state, &buffer, &size, &status, &used); if (!forward && !status) /* there has to be one hunk (forward hunk) */ - return error(_("unrecognized binary patch at line %d"), state->linenr-1); + return error(_("unrecognized binary patch at %s:%d"), + state->patch_input_file, state->linenr-1); if (status) /* otherwise we already gave an error message */ return status; @@ -2264,7 +2318,8 @@ static int parse_chunk(struct apply_state *state, char *buffer, unsigned long si */ if ((state->apply || state->check) && (!patch->is_binary && !metadata_changes(patch))) { - error(_("patch with only garbage at line %d"), state->linenr); + error(_("patch with only garbage at %s:%d"), + state->patch_input_file, state->linenr); return -128; } } @@ -4825,6 +4880,7 @@ static int apply_patch(struct apply_state *state, int flush_attributes = 0; state->patch_input_file = filename; + state->linenr = 1; if (read_patch_file(&buf, fd) < 0) return -128; offset = 0; @@ -4981,7 +5037,8 @@ static int apply_option_parse_p(const struct option *opt, BUG_ON_OPT_NEG(unset); - state->p_value = atoi(arg); + if (strtol_i(arg, 10, &state->p_value) < 0 || state->p_value < 0) + die(_("option -p expects a non-negative integer, got '%s'"), arg); state->p_value_known = 1; return 0; } @@ -167,6 +167,7 @@ int check_apply_state(struct apply_state *state, int force_apply); * Returns -1 on failure, the length of the parsed header otherwise. */ int parse_git_diff_header(struct strbuf *root, + const char *patch_input_file, int *linenr, int p_value, const char *line, @@ -501,7 +501,7 @@ struct bloom_filter *get_or_compute_bloom_filter(struct repository *r, struct hashmap_iter iter; for (i = 0; i < diff_queued_diff.nr; i++) { - const char *path = diff_queued_diff.queue[i]->two->path; + char *path = diff_queued_diff.queue[i]->two->path; /* * Add each leading directory of the changed file, i.e. for @@ -523,7 +523,7 @@ struct bloom_filter *get_or_compute_bloom_filter(struct repository *r, free(e); if (!last_slash) - last_slash = (char*)path; + last_slash = path; *last_slash = '\0'; } while (*path); diff --git a/builtin/add.c b/builtin/add.c index 0ee21692c2..7737ab878b 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -31,7 +31,7 @@ static const char * const builtin_add_usage[] = { NULL }; static int patch_interactive, add_interactive, edit_interactive; -static struct add_p_opt add_p_opt = ADD_P_OPT_INIT; +static struct interactive_options interactive_opts = INTERACTIVE_OPTIONS_INIT; static int take_worktree_changes; static int add_renormalize; static int pathspec_file_nul; @@ -160,7 +160,7 @@ static int refresh(struct repository *repo, int verbose, const struct pathspec * int interactive_add(struct repository *repo, const char **argv, const char *prefix, - int patch, struct add_p_opt *add_p_opt) + int patch, struct interactive_options *interactive_opts) { struct pathspec pathspec; int ret; @@ -172,9 +172,9 @@ int interactive_add(struct repository *repo, prefix, argv); if (patch) - ret = !!run_add_p(repo, ADD_P_ADD, add_p_opt, NULL, &pathspec); + ret = !!run_add_p(repo, ADD_P_ADD, interactive_opts, NULL, &pathspec, 0); else - ret = !!run_add_i(repo, &pathspec, add_p_opt); + ret = !!run_add_i(repo, &pathspec, interactive_opts); clear_pathspec(&pathspec); return ret; @@ -256,10 +256,10 @@ static struct option builtin_add_options[] = { OPT_GROUP(""), OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")), OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")), - OPT_BOOL(0, "auto-advance", &add_p_opt.auto_advance, + OPT_BOOL(0, "auto-advance", &interactive_opts.auto_advance, N_("auto advance to the next file when selecting hunks interactively")), - OPT_DIFF_UNIFIED(&add_p_opt.context), - OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), + OPT_DIFF_UNIFIED(&interactive_opts.context), + OPT_DIFF_INTERHUNK_CONTEXT(&interactive_opts.interhunkcontext), OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")), OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0), OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")), @@ -402,9 +402,9 @@ int cmd_add(int argc, prepare_repo_settings(repo); repo->settings.command_requires_full_index = 0; - if (add_p_opt.context < -1) + if (interactive_opts.context < -1) die(_("'%s' cannot be negative"), "--unified"); - if (add_p_opt.interhunkcontext < -1) + if (interactive_opts.interhunkcontext < -1) die(_("'%s' cannot be negative"), "--inter-hunk-context"); if (patch_interactive) @@ -414,13 +414,13 @@ int cmd_add(int argc, die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch"); if (pathspec_from_file) die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch"); - exit(interactive_add(repo, argv + 1, prefix, patch_interactive, &add_p_opt)); + exit(interactive_add(repo, argv + 1, prefix, patch_interactive, &interactive_opts)); } else { - if (add_p_opt.context != -1) + if (interactive_opts.context != -1) die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch"); - if (add_p_opt.interhunkcontext != -1) + if (interactive_opts.interhunkcontext != -1) die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch"); - if (!add_p_opt.auto_advance) + if (!interactive_opts.auto_advance) die(_("the option '%s' requires '%s'"), "--no-auto-advance", "--interactive/--patch"); } diff --git a/builtin/am.c b/builtin/am.c index e0c767e223..fe6e087eee 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -1188,7 +1188,7 @@ static void am_append_signoff(struct am_state *state) { struct strbuf sb = STRBUF_INIT; - strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len); + strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len + 1); append_signoff(&sb, 0, 0); state->msg = strbuf_detach(&sb, &state->msg_len); } @@ -1937,7 +1937,7 @@ next: */ if (!state->rebasing) { am_destroy(state); - run_auto_maintenance(state->quiet); + run_auto_maintenance(the_repository, state->quiet); } } diff --git a/builtin/backfill.c b/builtin/backfill.c index e9a33e81be..2c5ce56fb7 100644 --- a/builtin/backfill.c +++ b/builtin/backfill.c @@ -35,6 +35,7 @@ struct backfill_context { struct oid_array current_batch; size_t min_batch_size; int sparse; + struct rev_info revs; }; static void backfill_context_clear(struct backfill_context *ctx) @@ -79,7 +80,6 @@ static int fill_missing_blobs(const char *path UNUSED, static int do_backfill(struct backfill_context *ctx) { - struct rev_info revs; struct path_walk_info info = PATH_WALK_INFO_INIT; int ret; @@ -91,13 +91,14 @@ static int do_backfill(struct backfill_context *ctx) } } - repo_init_revisions(ctx->repo, &revs, ""); - handle_revision_arg("HEAD", &revs, 0, 0); + /* Walk from HEAD if otherwise unspecified. */ + if (!ctx->revs.pending.nr) + add_head_to_pending(&ctx->revs); info.blobs = 1; info.tags = info.commits = info.trees = 0; - info.revs = &revs; + info.revs = &ctx->revs; info.path_fn = fill_missing_blobs; info.path_fn_data = ctx; @@ -108,7 +109,6 @@ static int do_backfill(struct backfill_context *ctx) download_batch(ctx); path_walk_info_clear(&info); - release_revisions(&revs); return ret; } @@ -120,6 +120,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit .current_batch = OID_ARRAY_INIT, .min_batch_size = 50000, .sparse = 0, + .revs = REV_INFO_INIT, }; struct option options[] = { OPT_UNSIGNED(0, "min-batch-size", &ctx.min_batch_size, @@ -134,7 +135,15 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit builtin_backfill_usage, options); argc = parse_options(argc, argv, prefix, options, builtin_backfill_usage, - 0); + PARSE_OPT_KEEP_UNKNOWN_OPT | + PARSE_OPT_KEEP_ARGV0 | + PARSE_OPT_KEEP_DASHDASH); + + repo_init_revisions(repo, &ctx.revs, prefix); + argc = setup_revisions(argc, argv, &ctx.revs, NULL); + + if (argc > 1) + die(_("unrecognized argument: %s"), argv[1]); repo_config(repo, git_default_config, NULL); @@ -143,5 +152,6 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit result = do_backfill(&ctx); backfill_context_clear(&ctx); + release_revisions(&ctx.revs); return result; } diff --git a/builtin/branch.c b/builtin/branch.c index a1a43380d0..1572a4f9ef 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -228,7 +228,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, int ret = 0; int remote_branch = 0; struct strbuf bname = STRBUF_INIT; - unsigned allowed_interpret; + enum interpret_branch_kind allowed_interpret; struct string_list refs_to_delete = STRING_LIST_INIT_DUP; struct string_list_item *item; int branch_name_pos; diff --git a/builtin/cat-file.c b/builtin/cat-file.c index b6f12f41d6..cd13a3a89f 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -848,6 +848,9 @@ static void batch_each_object(struct batch_options *opt, .callback = callback, .payload = _payload, }; + struct odb_for_each_object_options opts = { + .flags = flags, + }; struct bitmap_index *bitmap = NULL; struct odb_source *source; @@ -860,7 +863,7 @@ static void batch_each_object(struct batch_options *opt, odb_prepare_alternates(the_repository->objects); for (source = the_repository->objects->sources; source; source = source->next) { int ret = odb_source_loose_for_each_object(source, NULL, batch_one_object_oi, - &payload, flags); + &payload, &opts); if (ret) break; } @@ -884,7 +887,7 @@ static void batch_each_object(struct batch_options *opt, for (source = the_repository->objects->sources; source; source = source->next) { struct odb_source_files *files = odb_source_files_downcast(source); int ret = packfile_store_for_each_object(files->packed, &oi, - batch_one_object_oi, &payload, flags); + batch_one_object_oi, &payload, &opts); if (ret) break; } diff --git a/builtin/checkout.c b/builtin/checkout.c index 1d1667fa4c..e031e61886 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -532,7 +532,7 @@ static int checkout_paths(const struct checkout_opts *opts, if (opts->patch_mode) { enum add_p_mode patch_mode; - struct add_p_opt add_p_opt = { + struct interactive_options interactive_opts = { .context = opts->patch_context, .interhunkcontext = opts->patch_interhunk_context, .auto_advance = opts->auto_advance @@ -562,8 +562,8 @@ static int checkout_paths(const struct checkout_opts *opts, else BUG("either flag must have been set, worktree=%d, index=%d", opts->checkout_worktree, opts->checkout_index); - return !!run_add_p(the_repository, patch_mode, &add_p_opt, - rev, &opts->pathspec); + return !!run_add_p(the_repository, patch_mode, &interactive_opts, + rev, &opts->pathspec, 0); } repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); @@ -891,7 +891,8 @@ static int merge_working_tree(const struct checkout_opts *opts, 0, 0); init_ui_merge_options(&o, the_repository); o.verbosity = 0; - work = write_in_core_index_as_tree(the_repository); + work = write_in_core_index_as_tree(the_repository, + the_repository->index); ret = reset_tree(new_tree, opts, 1, diff --git a/builtin/commit.c b/builtin/commit.c index 844bdcc728..a3e52ac9ca 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -123,7 +123,7 @@ static const char *edit_message, *use_message; static char *fixup_message, *fixup_commit, *squash_message; static const char *fixup_prefix; static int all, also, interactive, patch_interactive, only, amend, signoff; -static struct add_p_opt add_p_opt = ADD_P_OPT_INIT; +static struct interactive_options interactive_opts = INTERACTIVE_OPTIONS_INIT; static int edit_flag = -1; /* unspecified */ static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship; static int config_commit_verbose = -1; /* unspecified */ @@ -357,9 +357,9 @@ static const char *prepare_index(const char **argv, const char *prefix, const char *ret; char *path = NULL; - if (add_p_opt.context < -1) + if (interactive_opts.context < -1) die(_("'%s' cannot be negative"), "--unified"); - if (add_p_opt.interhunkcontext < -1) + if (interactive_opts.interhunkcontext < -1) die(_("'%s' cannot be negative"), "--inter-hunk-context"); if (is_status) @@ -408,7 +408,7 @@ static const char *prepare_index(const char **argv, const char *prefix, old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); - if (interactive_add(the_repository, argv, prefix, patch_interactive, &add_p_opt) != 0) + if (interactive_add(the_repository, argv, prefix, patch_interactive, &interactive_opts) != 0) die(_("interactive add failed")); the_repository->index_file = old_repo_index_file; @@ -433,9 +433,9 @@ static const char *prepare_index(const char **argv, const char *prefix, ret = get_lock_file_path(&index_lock); goto out; } else { - if (add_p_opt.context != -1) + if (interactive_opts.context != -1) die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch"); - if (add_p_opt.interhunkcontext != -1) + if (interactive_opts.interhunkcontext != -1) die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch"); } @@ -1720,7 +1720,8 @@ int cmd_commit(int argc, OPT_STRING(0, "fixup", &fixup_message, N_("[(amend|reword):]commit"), N_("use autosquash formatted message to fixup or amend/reword specified commit")), OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")), OPT_BOOL(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")), - OPT_PASSTHRU_ARGV(0, "trailer", &trailer_args, N_("trailer"), N_("add custom trailer(s)"), PARSE_OPT_NONEG), + OPT_STRVEC(0, "trailer", &trailer_args, N_("trailer"), + N_("add custom trailer(s)")), OPT_BOOL('s', "signoff", &signoff, N_("add a Signed-off-by trailer")), OPT_FILENAME('t', "template", &template_file, N_("use specified template file")), OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")), @@ -1743,8 +1744,8 @@ int cmd_commit(int argc, OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")), OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")), OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")), - OPT_DIFF_UNIFIED(&add_p_opt.context), - OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), + OPT_DIFF_UNIFIED(&interactive_opts.context), + OPT_DIFF_INTERHUNK_CONTEXT(&interactive_opts.interhunkcontext), OPT_BOOL('o', "only", &only, N_("commit only specified files")), OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")), OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")), @@ -1820,6 +1821,9 @@ int cmd_commit(int argc, argc = parse_and_validate_options(argc, argv, builtin_commit_options, builtin_commit_usage, prefix, current_head, &s); + if (trailer_args.nr) + trailer_config_init(); + if (verbose == -1) verbose = (config_commit_verbose < 0) ? 0 : config_commit_verbose; @@ -1958,7 +1962,7 @@ int cmd_commit(int argc, git_test_write_commit_graph_or_die(the_repository->objects->sources); repo_rerere(the_repository, 0); - run_auto_maintenance(quiet); + run_auto_maintenance(the_repository, quiet); run_commit_hook(use_editor, repo_get_index_file(the_repository), NULL, "post-commit", NULL); if (amend && !no_post_rewrite) { diff --git a/builtin/config.c b/builtin/config.c index 7c4857be62..cf4ba0f7cc 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -838,6 +838,7 @@ static int get_urlmatch(const struct config_location_options *opts, const char *var, const char *url) { int ret; + char *section; char *section_tail; struct config_display_options display_opts = *_display_opts; struct string_list_item *item; @@ -851,8 +852,8 @@ static int get_urlmatch(const struct config_location_options *opts, if (!url_normalize(url, &config.url)) die("%s", config.url.err); - config.section = xstrdup_tolower(var); - section_tail = strchr(config.section, '.'); + config.section = section = xstrdup_tolower(var); + section_tail = strchr(section, '.'); if (section_tail) { *section_tail = '\0'; config.key = section_tail + 1; @@ -886,7 +887,7 @@ static int get_urlmatch(const struct config_location_options *opts, string_list_clear(&values, 1); free(config.url.url); - free((void *)config.section); + free(section); return ret; } diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 0c5d2386d8..13621b0d6a 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -64,7 +64,7 @@ static int parse_opt_sign_mode(const struct option *opt, if (unset) return 0; - if (parse_sign_mode(arg, val)) + if (parse_sign_mode(arg, val, NULL)) return error(_("unknown %s mode: %s"), opt->long_name, arg); return 0; @@ -825,6 +825,9 @@ static void handle_commit(struct commit *commit, struct rev_info *rev, case SIGN_STRIP_IF_INVALID: die(_("'strip-if-invalid' is not a valid mode for " "git fast-export with --signed-commits=<mode>")); + case SIGN_SIGN_IF_INVALID: + die(_("'sign-if-invalid' is not a valid mode for " + "git fast-export with --signed-commits=<mode>")); default: BUG("invalid signed_commit_mode value %d", signed_commit_mode); } @@ -970,6 +973,9 @@ static void handle_tag(const char *name, struct tag *tag) case SIGN_STRIP_IF_INVALID: die(_("'strip-if-invalid' is not a valid mode for " "git fast-export with --signed-tags=<mode>")); + case SIGN_SIGN_IF_INVALID: + die(_("'sign-if-invalid' is not a valid mode for " + "git fast-export with --signed-tags=<mode>")); default: BUG("invalid signed_commit_mode value %d", signed_commit_mode); } diff --git a/builtin/fast-import.c b/builtin/fast-import.c index a41f95191e..570fd048d7 100644 --- a/builtin/fast-import.c +++ b/builtin/fast-import.c @@ -190,6 +190,7 @@ static const char *global_prefix; static enum sign_mode signed_tag_mode = SIGN_VERBATIM; static enum sign_mode signed_commit_mode = SIGN_VERBATIM; +static const char *signed_commit_keyid; /* Memory pools */ static struct mem_pool fi_mem_pool = { @@ -2840,10 +2841,46 @@ static void finalize_commit_buffer(struct strbuf *new_data, strbuf_addbuf(new_data, msg); } -static void handle_strip_if_invalid(struct strbuf *new_data, - struct signature_data *sig_sha1, - struct signature_data *sig_sha256, - struct strbuf *msg) +static void warn_invalid_signature(struct signature_check *check, + const char *msg, enum sign_mode mode) +{ + const char *signer = check->signer ? check->signer : _("unknown"); + const char *subject; + int subject_len = find_commit_subject(msg, &subject); + + switch (mode) { + case SIGN_STRIP_IF_INVALID: + if (subject_len > 100) + warning(_("stripping invalid signature for commit '%.100s...'\n" + " allegedly by %s"), subject, signer); + else if (subject_len > 0) + warning(_("stripping invalid signature for commit '%.*s'\n" + " allegedly by %s"), subject_len, subject, signer); + else + warning(_("stripping invalid signature for commit\n" + " allegedly by %s"), signer); + break; + case SIGN_SIGN_IF_INVALID: + if (subject_len > 100) + warning(_("replacing invalid signature for commit '%.100s...'\n" + " allegedly by %s"), subject, signer); + else if (subject_len > 0) + warning(_("replacing invalid signature for commit '%.*s'\n" + " allegedly by %s"), subject_len, subject, signer); + else + warning(_("replacing invalid signature for commit\n" + " allegedly by %s"), signer); + break; + default: + BUG("unsupported signing mode"); + } +} + +static void handle_signature_if_invalid(struct strbuf *new_data, + struct signature_data *sig_sha1, + struct signature_data *sig_sha256, + struct strbuf *msg, + enum sign_mode mode) { struct strbuf tmp_buf = STRBUF_INIT; struct signature_check signature_check = { 0 }; @@ -2855,20 +2892,34 @@ static void handle_strip_if_invalid(struct strbuf *new_data, ret = verify_commit_buffer(tmp_buf.buf, tmp_buf.len, &signature_check); if (ret) { - const char *signer = signature_check.signer ? - signature_check.signer : _("unknown"); - const char *subject; - int subject_len = find_commit_subject(msg->buf, &subject); + warn_invalid_signature(&signature_check, msg->buf, mode); - if (subject_len > 100) - warning(_("stripping invalid signature for commit '%.100s...'\n" - " allegedly by %s"), subject, signer); - else if (subject_len > 0) - warning(_("stripping invalid signature for commit '%.*s'\n" - " allegedly by %s"), subject_len, subject, signer); - else - warning(_("stripping invalid signature for commit\n" - " allegedly by %s"), signer); + if (mode == SIGN_SIGN_IF_INVALID) { + struct strbuf signature = STRBUF_INIT; + struct strbuf payload = STRBUF_INIT; + + /* + * NEEDSWORK: To properly support interoperability mode + * when signing commit signatures, the commit buffer + * must be provided in both the repository and + * compatibility object formats. As currently + * implemented, only the repository object format is + * considered meaning compatibility signatures cannot be + * generated. Thus, attempting to sign commit signatures + * in interoperability mode is currently unsupported. + */ + if (the_repository->compat_hash_algo) + die(_("signing commits in interoperability mode is unsupported")); + + strbuf_addstr(&payload, signature_check.payload); + if (sign_buffer(&payload, &signature, signed_commit_keyid, + SIGN_BUFFER_USE_DEFAULT_KEY)) + die(_("failed to sign commit object")); + add_header_signature(new_data, &signature, the_hash_algo); + + strbuf_release(&signature); + strbuf_release(&payload); + } finalize_commit_buffer(new_data, NULL, NULL, msg); } else { @@ -2931,6 +2982,7 @@ static void parse_new_commit(const char *arg) /* fallthru */ case SIGN_VERBATIM: case SIGN_STRIP_IF_INVALID: + case SIGN_SIGN_IF_INVALID: import_one_signature(&sig_sha1, &sig_sha256, v); break; @@ -3015,9 +3067,11 @@ static void parse_new_commit(const char *arg) "encoding %s\n", encoding); - if (signed_commit_mode == SIGN_STRIP_IF_INVALID && + if ((signed_commit_mode == SIGN_STRIP_IF_INVALID || + signed_commit_mode == SIGN_SIGN_IF_INVALID) && (sig_sha1.hash_algo || sig_sha256.hash_algo)) - handle_strip_if_invalid(&new_data, &sig_sha1, &sig_sha256, &msg); + handle_signature_if_invalid(&new_data, &sig_sha1, &sig_sha256, + &msg, signed_commit_mode); else finalize_commit_buffer(&new_data, &sig_sha1, &sig_sha256, &msg); @@ -3064,6 +3118,9 @@ static void handle_tag_signature(struct strbuf *msg, const char *name) case SIGN_STRIP_IF_INVALID: die(_("'strip-if-invalid' is not a valid mode for " "git fast-import with --signed-tags=<mode>")); + case SIGN_SIGN_IF_INVALID: + die(_("'sign-if-invalid' is not a valid mode for " + "git fast-import with --signed-tags=<mode>")); default: BUG("invalid signed_tag_mode value %d from tag '%s'", signed_tag_mode, name); @@ -3250,7 +3307,7 @@ static void cat_blob(struct object_entry *oe, struct object_id *oid) cat_blob_write("\n", 1); if (oe && oe->pack_id == pack_id) { last_blob.offset = oe->idx.offset; - strbuf_attach(&last_blob.data, buf, size, size); + strbuf_attach(&last_blob.data, buf, size, size + 1); last_blob.depth = oe->depth; } else free(buf); @@ -3653,10 +3710,10 @@ static int parse_one_option(const char *option) } else if (skip_prefix(option, "export-pack-edges=", &option)) { option_export_pack_edges(option); } else if (skip_prefix(option, "signed-commits=", &option)) { - if (parse_sign_mode(option, &signed_commit_mode)) + if (parse_sign_mode(option, &signed_commit_mode, &signed_commit_keyid)) usagef(_("unknown --signed-commits mode '%s'"), option); } else if (skip_prefix(option, "signed-tags=", &option)) { - if (parse_sign_mode(option, &signed_tag_mode)) + if (parse_sign_mode(option, &signed_tag_mode, NULL)) usagef(_("unknown --signed-tags mode '%s'"), option); } else if (!strcmp(option, "quiet")) { show_stats = 0; diff --git a/builtin/fetch.c b/builtin/fetch.c index 8a36cf67b5..4795b2a13c 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -2873,7 +2873,7 @@ int cmd_fetch(int argc, if (opt_val != 0) git_config_push_parameter("maintenance.incremental-repack.auto=-1"); } - run_auto_maintenance(verbosity < 0); + run_auto_maintenance(the_repository, verbosity < 0); } cleanup: diff --git a/builtin/fsck.c b/builtin/fsck.c index 9bab32effe..99696604b8 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -1,4 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" #include "gettext.h" #include "hex.h" @@ -42,8 +41,8 @@ static int check_full = 1; static int connectivity_only; static int check_strict; static int keep_cache_objects; -static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT; -static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT; +static struct fsck_options fsck_walk_options; +static struct fsck_options fsck_obj_options; static int errors_found; static int write_lost_and_found; static int verbose; @@ -66,14 +65,14 @@ static const char *describe_object(const struct object_id *oid) return fsck_describe_object(&fsck_walk_options, oid); } -static const char *printable_type(const struct object_id *oid, +static const char *printable_type(struct repository *repo, + const struct object_id *oid, enum object_type type) { const char *ret; if (type == OBJ_NONE) - type = odb_read_object_info(the_repository->objects, - oid, NULL); + type = odb_read_object_info(repo->objects, oid, NULL); ret = type_name(type); if (!ret) @@ -82,17 +81,17 @@ static const char *printable_type(const struct object_id *oid, return ret; } -static int objerror(struct object *obj, const char *err) +static int objerror(struct repository *repo, struct object *obj, const char *err) { errors_found |= ERROR_OBJECT; /* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */ fprintf_ln(stderr, _("error in %s %s: %s"), - printable_type(&obj->oid, obj->type), + printable_type(repo, &obj->oid, obj->type), describe_object(&obj->oid), err); return -1; } -static int fsck_objects_error_func(struct fsck_options *o UNUSED, +static int fsck_objects_error_func(struct fsck_options *o, void *fsck_report, enum fsck_msg_type msg_type, enum fsck_msg_id msg_id UNUSED, @@ -106,13 +105,13 @@ static int fsck_objects_error_func(struct fsck_options *o UNUSED, case FSCK_WARN: /* TRANSLATORS: e.g. warning in tree 01bfda: <more explanation> */ fprintf_ln(stderr, _("warning in %s %s: %s"), - printable_type(oid, object_type), + printable_type(o->repo, oid, object_type), describe_object(oid), message); return 0; case FSCK_ERROR: /* TRANSLATORS: e.g. error in tree 01bfda: <more explanation> */ fprintf_ln(stderr, _("error in %s %s: %s"), - printable_type(oid, object_type), + printable_type(o->repo, oid, object_type), describe_object(oid), message); return 1; default: @@ -124,7 +123,7 @@ static int fsck_objects_error_func(struct fsck_options *o UNUSED, static struct object_array pending; static int mark_object(struct object *obj, enum object_type type, - void *data, struct fsck_options *options UNUSED) + void *data, struct fsck_options *options) { struct object *parent = data; @@ -136,7 +135,7 @@ static int mark_object(struct object *obj, enum object_type type, if (!obj) { /* ... these references to parent->fld are safe here */ printf_ln(_("broken link from %7s %s"), - printable_type(&parent->oid, parent->type), + printable_type(options->repo, &parent->oid, parent->type), describe_object(&parent->oid)); printf_ln(_("broken link from %7s %s"), (type == OBJ_ANY ? _("unknown") : type_name(type)), @@ -147,13 +146,13 @@ static int mark_object(struct object *obj, enum object_type type, if (type != OBJ_ANY && obj->type != type) /* ... and the reference to parent is safe here */ - objerror(parent, _("wrong object type in link")); + objerror(options->repo, parent, _("wrong object type in link")); if (obj->flags & REACHABLE) return 0; obj->flags |= REACHABLE; - if (is_promisor_object(the_repository, &obj->oid)) + if (is_promisor_object(options->repo, &obj->oid)) /* * Further recursion does not need to be performed on this * object since it is a promisor object (so it does not need to @@ -162,13 +161,13 @@ static int mark_object(struct object *obj, enum object_type type, return 0; if (!(obj->flags & HAS_OBJ)) { - if (parent && !odb_has_object(the_repository->objects, &obj->oid, + if (parent && !odb_has_object(options->repo->objects, &obj->oid, HAS_OBJECT_RECHECK_PACKED)) { printf_ln(_("broken link from %7s %s\n" " to %7s %s"), - printable_type(&parent->oid, parent->type), + printable_type(options->repo, &parent->oid, parent->type), describe_object(&parent->oid), - printable_type(&obj->oid, obj->type), + printable_type(options->repo, &obj->oid, obj->type), describe_object(&obj->oid)); errors_found |= ERROR_REACHABLE; } @@ -181,7 +180,7 @@ static int mark_object(struct object *obj, enum object_type type, static void mark_object_reachable(struct object *obj) { - mark_object(obj, OBJ_ANY, NULL, NULL); + mark_object(obj, OBJ_ANY, NULL, &fsck_walk_options); } static int traverse_one_object(struct object *obj) @@ -195,13 +194,13 @@ static int traverse_one_object(struct object *obj) return result; } -static int traverse_reachable(void) +static int traverse_reachable(struct repository *repo) { struct progress *progress = NULL; unsigned int nr = 0; int result = 0; if (show_progress) - progress = start_delayed_progress(the_repository, + progress = start_delayed_progress(repo, _("Checking connectivity"), 0); while (pending.nr) { result |= traverse_one_object(object_array_pop(&pending)); @@ -222,10 +221,11 @@ static int mark_used(struct object *obj, enum object_type type UNUSED, static int mark_unreachable_referents(const struct object_id *oid, struct object_info *oi UNUSED, - void *data UNUSED) + void *data) { - struct fsck_options options = FSCK_OPTIONS_DEFAULT; - struct object *obj = lookup_object(the_repository, oid); + struct repository *repo = data; + struct fsck_options options; + struct object *obj = lookup_object(data, oid); if (!obj || !(obj->flags & HAS_OBJ)) return 0; /* not part of our original set */ @@ -237,12 +237,13 @@ static int mark_unreachable_referents(const struct object_id *oid, * (and we want to avoid parsing blobs). */ if (obj->type == OBJ_NONE) { - enum object_type type = odb_read_object_info(the_repository->objects, + enum object_type type = odb_read_object_info(repo->objects, &obj->oid, NULL); if (type > 0) object_as_type(obj, type, 0); } + fsck_options_init(&options, repo, FSCK_OPTIONS_DEFAULT); options.walk = mark_used; fsck_walk(obj, NULL, &options); if (obj->type == OBJ_TREE) @@ -254,7 +255,7 @@ static int mark_unreachable_referents(const struct object_id *oid, /* * Check a single reachable object */ -static void check_reachable_object(struct object *obj) +static void check_reachable_object(struct repository *repo, struct object *obj) { /* * We obviously want the object to be parsed, @@ -262,12 +263,12 @@ static void check_reachable_object(struct object *obj) * do a full fsck */ if (!(obj->flags & HAS_OBJ)) { - if (is_promisor_object(the_repository, &obj->oid)) + if (is_promisor_object(repo, &obj->oid)) return; - if (has_object_pack(the_repository, &obj->oid)) + if (has_object_pack(repo, &obj->oid)) return; /* it is in pack - forget about it */ printf_ln(_("missing %s %s"), - printable_type(&obj->oid, obj->type), + printable_type(repo, &obj->oid, obj->type), describe_object(&obj->oid)); errors_found |= ERROR_REACHABLE; return; @@ -277,7 +278,7 @@ static void check_reachable_object(struct object *obj) /* * Check a single unreachable object */ -static void check_unreachable_object(struct object *obj) +static void check_unreachable_object(struct repository *repo, struct object *obj) { /* * Missing unreachable object? Ignore it. It's not like @@ -294,7 +295,7 @@ static void check_unreachable_object(struct object *obj) */ if (show_unreachable) { printf_ln(_("unreachable %s %s"), - printable_type(&obj->oid, obj->type), + printable_type(repo, &obj->oid, obj->type), describe_object(&obj->oid)); return; } @@ -314,22 +315,22 @@ static void check_unreachable_object(struct object *obj) if (!(obj->flags & USED)) { if (show_dangling) printf_ln(_("dangling %s %s"), - printable_type(&obj->oid, obj->type), + printable_type(repo, &obj->oid, obj->type), describe_object(&obj->oid)); if (write_lost_and_found) { - char *filename = repo_git_path(the_repository, "lost-found/%s/%s", + char *filename = repo_git_path(repo, "lost-found/%s/%s", obj->type == OBJ_COMMIT ? "commit" : "other", describe_object(&obj->oid)); FILE *f; - if (safe_create_leading_directories_const(the_repository, filename)) { + if (safe_create_leading_directories_const(repo, filename)) { error(_("could not create lost-found")); free(filename); return; } f = xfopen(filename, "w"); if (obj->type == OBJ_BLOB) { - if (odb_stream_blob_to_fd(the_repository->objects, fileno(f), + if (odb_stream_blob_to_fd(repo->objects, fileno(f), &obj->oid, NULL, 1)) die_errno(_("could not write '%s'"), filename); } else @@ -349,23 +350,23 @@ static void check_unreachable_object(struct object *obj) */ } -static void check_object(struct object *obj) +static void check_object(struct repository *repo, struct object *obj) { if (verbose) fprintf_ln(stderr, _("Checking %s"), describe_object(&obj->oid)); if (obj->flags & REACHABLE) - check_reachable_object(obj); + check_reachable_object(repo, obj); else - check_unreachable_object(obj); + check_unreachable_object(repo, obj); } -static void check_connectivity(void) +static void check_connectivity(struct repository *repo) { int i, max; /* Traverse the pending reachable objects */ - traverse_reachable(); + traverse_reachable(repo); /* * With --connectivity-only, we won't have actually opened and marked @@ -383,24 +384,25 @@ static void check_connectivity(void) * and ignore any that weren't present in our earlier * traversal. */ - odb_for_each_object(the_repository->objects, NULL, - mark_unreachable_referents, NULL, 0); + odb_for_each_object(repo->objects, NULL, + mark_unreachable_referents, repo, 0); } /* Look up all the requirements, warn about missing objects.. */ - max = get_max_object_index(the_repository); + max = get_max_object_index(repo); if (verbose) fprintf_ln(stderr, _("Checking connectivity (%d objects)"), max); for (i = 0; i < max; i++) { - struct object *obj = get_indexed_object(the_repository, i); + struct object *obj = get_indexed_object(repo, i); if (obj) - check_object(obj); + check_object(repo, obj); } } -static int fsck_obj(struct object *obj, void *buffer, unsigned long size) +static int fsck_obj(struct repository *repo, + struct object *obj, void *buffer, unsigned long size) { int err; @@ -410,11 +412,11 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size) if (verbose) fprintf_ln(stderr, _("Checking %s %s"), - printable_type(&obj->oid, obj->type), + printable_type(repo, &obj->oid, obj->type), describe_object(&obj->oid)); if (fsck_walk(obj, NULL, &fsck_obj_options)) - objerror(obj, _("broken links")); + objerror(repo, obj, _("broken links")); err = fsck_object(obj, buffer, size, &fsck_obj_options); if (err) goto out; @@ -432,7 +434,7 @@ static int fsck_obj(struct object *obj, void *buffer, unsigned long size) if (show_tags && tag->tagged) { printf_ln(_("tagged %s %s (%s) in %s"), - printable_type(&tag->tagged->oid, tag->tagged->type), + printable_type(repo, &tag->tagged->oid, tag->tagged->type), describe_object(&tag->tagged->oid), tag->tag, describe_object(&tag->object.oid)); @@ -446,15 +448,16 @@ out: } static int fsck_obj_buffer(const struct object_id *oid, enum object_type type, - unsigned long size, void *buffer, int *eaten) + unsigned long size, void *buffer, int *eaten, void *cb_data) { + struct repository *repo = cb_data; + struct object *obj; + /* * Note, buffer may be NULL if type is OBJ_BLOB. See * verify_packfile(), data_valid variable for details. */ - struct object *obj; - obj = parse_object_buffer(the_repository, oid, type, size, buffer, - eaten); + obj = parse_object_buffer(repo, oid, type, size, buffer, eaten); if (!obj) { errors_found |= ERROR_OBJECT; return error(_("%s: object corrupt or missing"), @@ -462,18 +465,19 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type, } obj->flags &= ~(REACHABLE | SEEN); obj->flags |= HAS_OBJ; - return fsck_obj(obj, buffer, size); + return fsck_obj(repo, obj, buffer, size); } static int default_refs; -static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, - timestamp_t timestamp) +static void fsck_handle_reflog_oid(struct repository *repo, + const char *refname, struct object_id *oid, + timestamp_t timestamp) { struct object *obj; if (!is_null_oid(oid)) { - obj = lookup_object(the_repository, oid); + obj = lookup_object(repo, oid); if (obj && (obj->flags & HAS_OBJ)) { if (timestamp) fsck_put_object_name(&fsck_walk_options, oid, @@ -481,7 +485,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, refname, timestamp); obj->flags |= USED; mark_object_reachable(obj); - } else if (!is_promisor_object(the_repository, oid)) { + } else if (!is_promisor_object(repo, oid)) { error(_("%s: invalid reflog entry %s"), refname, oid_to_hex(oid)); errors_found |= ERROR_REACHABLE; @@ -493,8 +497,10 @@ static int fsck_handle_reflog_ent(const char *refname, struct object_id *ooid, struct object_id *noid, const char *email UNUSED, timestamp_t timestamp, int tz UNUSED, - const char *message UNUSED, void *cb_data UNUSED) + const char *message UNUSED, void *cb_data) { + struct repository *repo = cb_data; + if (now && timestamp > now) return 0; @@ -502,19 +508,20 @@ static int fsck_handle_reflog_ent(const char *refname, fprintf_ln(stderr, _("Checking reflog %s->%s"), oid_to_hex(ooid), oid_to_hex(noid)); - fsck_handle_reflog_oid(refname, ooid, 0); - fsck_handle_reflog_oid(refname, noid, timestamp); + fsck_handle_reflog_oid(repo, refname, ooid, 0); + fsck_handle_reflog_oid(repo, refname, noid, timestamp); return 0; } static int fsck_handle_reflog(const char *logname, void *cb_data) { struct strbuf refname = STRBUF_INIT; + struct worktree *wt = cb_data; - strbuf_worktree_ref(cb_data, &refname, logname); - refs_for_each_reflog_ent(get_main_ref_store(the_repository), + strbuf_worktree_ref(wt, &refname, logname); + refs_for_each_reflog_ent(get_main_ref_store(wt->repo), refname.buf, fsck_handle_reflog_ent, - NULL); + wt->repo); strbuf_release(&refname); return 0; } @@ -532,14 +539,20 @@ struct snapshot { /* TODO: Consider also snapshotting the index of each worktree. */ }; +struct snapshot_ref_data { + struct repository *repo; + struct snapshot *snap; +}; + static int snapshot_ref(const struct reference *ref, void *cb_data) { - struct snapshot *snap = cb_data; + struct snapshot_ref_data *data = cb_data; + struct snapshot *snap = data->snap; struct object *obj; - obj = parse_object(the_repository, ref->oid); + obj = parse_object(data->repo, ref->oid); if (!obj) { - if (is_promisor_object(the_repository, ref->oid)) { + if (is_promisor_object(data->repo, ref->oid)) { /* * Increment default_refs anyway, because this is a * valid ref. @@ -567,11 +580,12 @@ static int snapshot_ref(const struct reference *ref, void *cb_data) return 0; } -static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED) +static int fsck_handle_ref(const struct reference *ref, void *cb_data) { + struct repository *repo = cb_data; struct object *obj; - obj = parse_object(the_repository, ref->oid); + obj = parse_object(repo, ref->oid); obj->flags |= USED; fsck_put_object_name(&fsck_walk_options, ref->oid, "%s", ref->name); @@ -580,11 +594,16 @@ static int fsck_handle_ref(const struct reference *ref, void *cb_data UNUSED) return 0; } -static void snapshot_refs(struct snapshot *snap, int argc, const char **argv) +static void snapshot_refs(struct repository *repo, + struct snapshot *snap, int argc, const char **argv) { struct refs_for_each_ref_options opts = { .flags = REFS_FOR_EACH_INCLUDE_BROKEN, }; + struct snapshot_ref_data data = { + .repo = repo, + .snap = snap, + }; struct worktree **worktrees, **p; const char *head_points_at; struct object_id head_oid; @@ -592,13 +611,13 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv) for (int i = 0; i < argc; i++) { const char *arg = argv[i]; struct object_id oid; - if (!repo_get_oid(the_repository, arg, &oid)) { + if (!repo_get_oid(repo, arg, &oid)) { struct reference ref = { .name = arg, .oid = &oid, }; - snapshot_ref(&ref, snap); + snapshot_ref(&ref, &data); continue; } error(_("invalid parameter: expected sha1, got '%s'"), arg); @@ -610,8 +629,8 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv) return; } - refs_for_each_ref_ext(get_main_ref_store(the_repository), - snapshot_ref, snap, &opts); + refs_for_each_ref_ext(get_main_ref_store(repo), + snapshot_ref, &data, &opts); worktrees = get_worktrees(); for (p = worktrees; *p; p++) { @@ -620,7 +639,7 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv) strbuf_worktree_ref(wt, &refname, "HEAD"); - head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), + head_points_at = refs_resolve_ref_unsafe(get_main_ref_store(repo), refname.buf, 0, &head_oid, NULL); if (head_points_at && !is_null_oid(&head_oid)) { @@ -629,7 +648,7 @@ static void snapshot_refs(struct snapshot *snap, int argc, const char **argv) .oid = &head_oid, }; - snapshot_ref(&ref, snap); + snapshot_ref(&ref, &data); } strbuf_release(&refname); @@ -653,7 +672,7 @@ static void free_snapshot_refs(struct snapshot *snap) free(snap->ref); } -static void process_refs(struct snapshot *snap) +static void process_refs(struct repository *repo, struct snapshot *snap) { struct worktree **worktrees, **p; @@ -662,7 +681,7 @@ static void process_refs(struct snapshot *snap) .name = snap->ref[i].refname, .oid = &snap->ref[i].oid, }; - fsck_handle_ref(&ref, NULL); + fsck_handle_ref(&ref, repo); } if (include_reflogs) { @@ -694,27 +713,28 @@ static void process_refs(struct snapshot *snap) } } -struct for_each_loose_cb -{ +struct for_each_loose_cb { + struct repository *repo; struct progress *progress; }; static int fsck_loose(const struct object_id *oid, const char *path, - void *data UNUSED) + void *cb_data) { + struct for_each_loose_cb *data = cb_data; struct object *obj; enum object_type type = OBJ_NONE; unsigned long size; void *contents = NULL; int eaten; struct object_info oi = OBJECT_INFO_INIT; - struct object_id real_oid = *null_oid(the_hash_algo); + struct object_id real_oid = *null_oid(data->repo->hash_algo); int err = 0; oi.sizep = &size; oi.typep = &type; - if (read_loose_object(the_repository, path, oid, &real_oid, &contents, &oi) < 0) { + if (read_loose_object(data->repo, path, oid, &real_oid, &contents, &oi) < 0) { if (contents && !oideq(&real_oid, oid)) err = error(_("%s: hash-path mismatch, found at: %s"), oid_to_hex(&real_oid), path); @@ -731,7 +751,7 @@ static int fsck_loose(const struct object_id *oid, const char *path, if (!contents && type != OBJ_BLOB) BUG("read_loose_object streamed a non-blob"); - obj = parse_object_buffer(the_repository, oid, type, size, + obj = parse_object_buffer(data->repo, oid, type, size, contents, &eaten); if (!obj) { @@ -745,7 +765,7 @@ static int fsck_loose(const struct object_id *oid, const char *path, obj->flags &= ~(REACHABLE | SEEN); obj->flags |= HAS_OBJ; - if (fsck_obj(obj, contents, size)) + if (fsck_obj(data->repo, obj, contents, size)) errors_found |= ERROR_OBJECT; if (!eaten) @@ -769,10 +789,11 @@ static int fsck_subdir(unsigned int nr, const char *path UNUSED, void *data) return 0; } -static void fsck_source(struct odb_source *source) +static void fsck_source(struct repository *repo, struct odb_source *source) { struct progress *progress = NULL; struct for_each_loose_cb cb_data = { + .repo = source->odb->repo, .progress = progress, }; @@ -780,7 +801,7 @@ static void fsck_source(struct odb_source *source) fprintf_ln(stderr, _("Checking object directory")); if (show_progress) - progress = start_progress(the_repository, + progress = start_progress(repo, _("Checking object directories"), 256); for_each_loose_file_in_source(source, fsck_loose, @@ -789,7 +810,7 @@ static void fsck_source(struct odb_source *source) stop_progress(&progress); } -static int fsck_cache_tree(struct cache_tree *it, const char *index_path) +static int fsck_cache_tree(struct repository *repo, struct cache_tree *it, const char *index_path) { int i; int err = 0; @@ -798,7 +819,7 @@ static int fsck_cache_tree(struct cache_tree *it, const char *index_path) fprintf_ln(stderr, _("Checking cache tree of %s"), index_path); if (0 <= it->entry_count) { - struct object *obj = parse_object(the_repository, &it->oid); + struct object *obj = parse_object(repo, &it->oid); if (!obj) { error(_("%s: invalid sha1 pointer in cache-tree of %s"), oid_to_hex(&it->oid), index_path); @@ -809,10 +830,10 @@ static int fsck_cache_tree(struct cache_tree *it, const char *index_path) fsck_put_object_name(&fsck_walk_options, &it->oid, ":"); mark_object_reachable(obj); if (obj->type != OBJ_TREE) - err |= objerror(obj, _("non-tree in cache-tree")); + err |= objerror(repo, obj, _("non-tree in cache-tree")); } for (i = 0; i < it->subtree_nr; i++) - err |= fsck_cache_tree(it->down[i]->cache_tree, index_path); + err |= fsck_cache_tree(repo, it->down[i]->cache_tree, index_path); return err; } @@ -838,7 +859,7 @@ static int fsck_resolve_undo(struct index_state *istate, if (!ru->mode[i] || !S_ISREG(ru->mode[i])) continue; - obj = parse_object(the_repository, &ru->oid[i]); + obj = parse_object(istate->repo, &ru->oid[i]); if (!obj) { error(_("%s: invalid sha1 pointer in resolve-undo of %s"), oid_to_hex(&ru->oid[i]), @@ -870,7 +891,7 @@ static void fsck_index(struct index_state *istate, const char *index_path, mode = istate->cache[i]->ce_mode; if (S_ISGITLINK(mode)) continue; - blob = lookup_blob(the_repository, + blob = lookup_blob(istate->repo, &istate->cache[i]->oid); if (!blob) continue; @@ -883,15 +904,16 @@ static void fsck_index(struct index_state *istate, const char *index_path, mark_object_reachable(obj); } if (istate->cache_tree) - fsck_cache_tree(istate->cache_tree, index_path); + fsck_cache_tree(istate->repo, istate->cache_tree, index_path); fsck_resolve_undo(istate, index_path); } static int mark_object_for_connectivity(const struct object_id *oid, struct object_info *oi UNUSED, - void *cb_data UNUSED) + void *cb_data) { - struct object *obj = lookup_unknown_object(the_repository, oid); + struct repository *repo = cb_data; + struct object *obj = lookup_unknown_object(repo, oid); obj->flags |= HAS_OBJ; return 0; } @@ -906,7 +928,7 @@ static int check_pack_rev_indexes(struct repository *r, int show_progress) if (show_progress) { repo_for_each_pack(r, p) pack_count++; - progress = start_delayed_progress(the_repository, + progress = start_delayed_progress(r, "Verifying reverse pack-indexes", pack_count); pack_count = 0; } @@ -986,7 +1008,7 @@ static struct option fsck_opts[] = { int cmd_fsck(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { struct odb_source *source; struct snapshot snap = { @@ -1004,7 +1026,10 @@ int cmd_fsck(int argc, argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0); + fsck_options_init(&fsck_walk_options, repo, FSCK_OPTIONS_DEFAULT); fsck_walk_options.walk = mark_object; + + fsck_options_init(&fsck_obj_options, repo, FSCK_OPTIONS_DEFAULT); fsck_obj_options.walk = mark_used; fsck_obj_options.error_func = fsck_objects_error_func; if (check_strict) @@ -1023,11 +1048,11 @@ int cmd_fsck(int argc, if (name_objects) fsck_enable_object_names(&fsck_walk_options); - repo_config(the_repository, git_fsck_config, &fsck_obj_options); - prepare_repo_settings(the_repository); + repo_config(repo, git_fsck_config, &fsck_obj_options); + prepare_repo_settings(repo); if (check_references) - fsck_refs(the_repository); + fsck_refs(repo); /* * Take a snapshot of the refs before walking objects to avoid looking @@ -1035,18 +1060,18 @@ int cmd_fsck(int argc, * objects. We can still walk over new objects that are added during the * execution of fsck but won't miss any objects that were reachable. */ - snapshot_refs(&snap, argc, argv); + snapshot_refs(repo, &snap, argc, argv); /* Ensure we get a "fresh" view of the odb */ - odb_reprepare(the_repository->objects); + odb_reprepare(repo->objects); if (connectivity_only) { - odb_for_each_object(the_repository->objects, NULL, - mark_object_for_connectivity, NULL, 0); + odb_for_each_object(repo->objects, NULL, + mark_object_for_connectivity, repo, 0); } else { - odb_prepare_alternates(the_repository->objects); - for (source = the_repository->objects->sources; source; source = source->next) - fsck_source(source); + odb_prepare_alternates(repo->objects); + for (source = repo->objects->sources; source; source = source->next) + fsck_source(repo, source); if (check_full) { struct packed_git *p; @@ -1054,20 +1079,20 @@ int cmd_fsck(int argc, struct progress *progress = NULL; if (show_progress) { - repo_for_each_pack(the_repository, p) { + repo_for_each_pack(repo, p) { if (open_pack_index(p)) continue; total += p->num_objects; } - progress = start_progress(the_repository, + progress = start_progress(repo, _("Checking objects"), total); } - repo_for_each_pack(the_repository, p) { + repo_for_each_pack(repo, p) { /* verify gives error messages itself */ - if (verify_pack(the_repository, - p, fsck_obj_buffer, + if (verify_pack(repo, + p, fsck_obj_buffer, repo, progress, count)) errors_found |= ERROR_PACK; count += p->num_objects; @@ -1080,7 +1105,7 @@ int cmd_fsck(int argc, } /* Process the snapshotted refs and the reflogs. */ - process_refs(&snap); + process_refs(repo, &snap); /* If not given any explicit objects, process index files too. */ if (!argc) @@ -1100,7 +1125,7 @@ int cmd_fsck(int argc, for (p = worktrees; *p; p++) { struct worktree *wt = *p; struct index_state istate = - INDEX_STATE_INIT(the_repository); + INDEX_STATE_INIT(repo); char *path, *wt_gitdir; /* @@ -1121,17 +1146,17 @@ int cmd_fsck(int argc, free_worktrees(worktrees); } - errors_found |= check_pack_rev_indexes(the_repository, show_progress); - if (verify_bitmap_files(the_repository)) + errors_found |= check_pack_rev_indexes(repo, show_progress); + if (verify_bitmap_files(repo)) errors_found |= ERROR_BITMAP; - check_connectivity(); + check_connectivity(repo); - if (the_repository->settings.core_commit_graph) { + if (repo->settings.core_commit_graph) { struct child_process commit_graph_verify = CHILD_PROCESS_INIT; - odb_prepare_alternates(the_repository->objects); - for (source = the_repository->objects->sources; source; source = source->next) { + odb_prepare_alternates(repo->objects); + for (source = repo->objects->sources; source; source = source->next) { child_process_init(&commit_graph_verify); commit_graph_verify.git_cmd = 1; strvec_pushl(&commit_graph_verify.args, "commit-graph", @@ -1145,11 +1170,11 @@ int cmd_fsck(int argc, } } - if (the_repository->settings.core_multi_pack_index) { + if (repo->settings.core_multi_pack_index) { struct child_process midx_verify = CHILD_PROCESS_INIT; - odb_prepare_alternates(the_repository->objects); - for (source = the_repository->objects->sources; source; source = source->next) { + odb_prepare_alternates(repo->objects); + for (source = repo->objects->sources; source; source = source->next) { child_process_init(&midx_verify); midx_verify.git_cmd = 1; strvec_pushl(&midx_verify.args, "multi-pack-index", diff --git a/builtin/gc.c b/builtin/gc.c index fb329c2cff..3a71e314c9 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -467,37 +467,19 @@ out: static int too_many_loose_objects(int limit) { /* - * Quickly check if a "gc" is needed, by estimating how - * many loose objects there are. Because SHA-1 is evenly - * distributed, we can check only one and get a reasonable - * estimate. + * This is weird, but stems from legacy behaviour: the GC auto + * threshold was always essentially interpreted as if it was rounded up + * to the next multiple 256 of, so we retain this behaviour for now. */ - DIR *dir; - struct dirent *ent; - int auto_threshold; - int num_loose = 0; - int needed = 0; - const unsigned hexsz_loose = the_hash_algo->hexsz - 2; - char *path; + int auto_threshold = DIV_ROUND_UP(limit, 256) * 256; + unsigned long loose_count; - path = repo_git_path(the_repository, "objects/17"); - dir = opendir(path); - free(path); - if (!dir) + if (odb_source_loose_count_objects(the_repository->objects->sources, + ODB_COUNT_OBJECTS_APPROXIMATE, + &loose_count) < 0) return 0; - auto_threshold = DIV_ROUND_UP(limit, 256); - while ((ent = readdir(dir)) != NULL) { - if (strspn(ent->d_name, "0123456789abcdef") != hexsz_loose || - ent->d_name[hexsz_loose] != '\0') - continue; - if (++num_loose > auto_threshold) { - needed = 1; - break; - } - } - closedir(dir); - return needed; + return loose_count > auto_threshold; } static struct packed_git *find_base_packs(struct string_list *packs, @@ -592,9 +574,13 @@ static uint64_t total_ram(void) static uint64_t estimate_repack_memory(struct gc_config *cfg, struct packed_git *pack) { - unsigned long nr_objects = repo_approximate_object_count(the_repository); + unsigned long nr_objects; size_t os_cache, heap; + if (odb_count_objects(the_repository->objects, + ODB_COUNT_OBJECTS_APPROXIMATE, &nr_objects) < 0) + return 0; + if (!pack || !nr_objects) return 0; @@ -1030,7 +1016,7 @@ int cmd_gc(int argc, struct child_process repack_cmd = CHILD_PROCESS_INIT; repack_cmd.git_cmd = 1; - repack_cmd.close_object_store = 1; + repack_cmd.odb_to_close = the_repository->objects; strvec_pushv(&repack_cmd.args, repack_args.v); if (run_command(&repack_cmd)) die(FAILED_RUN, repack_args.v[0]); @@ -1199,7 +1185,8 @@ static int run_write_commit_graph(struct maintenance_run_opts *opts) { struct child_process child = CHILD_PROCESS_INIT; - child.git_cmd = child.close_object_store = 1; + child.git_cmd = 1; + child.odb_to_close = the_repository->objects; strvec_pushl(&child.args, "commit-graph", "write", "--split", "--reachable", NULL); @@ -1268,7 +1255,8 @@ static int maintenance_task_gc_background(struct maintenance_run_opts *opts, { struct child_process child = CHILD_PROCESS_INIT; - child.git_cmd = child.close_object_store = 1; + child.git_cmd = 1; + child.odb_to_close = the_repository->objects; strvec_push(&child.args, "gc"); if (opts->auto_flag) @@ -1484,7 +1472,8 @@ static int multi_pack_index_expire(struct maintenance_run_opts *opts) { struct child_process child = CHILD_PROCESS_INIT; - child.git_cmd = child.close_object_store = 1; + child.git_cmd = 1; + child.odb_to_close = the_repository->objects; strvec_pushl(&child.args, "multi-pack-index", "expire", NULL); if (opts->quiet) @@ -1542,7 +1531,8 @@ static int multi_pack_index_repack(struct maintenance_run_opts *opts) { struct child_process child = CHILD_PROCESS_INIT; - child.git_cmd = child.close_object_store = 1; + child.git_cmd = 1; + child.odb_to_close = the_repository->objects; strvec_pushl(&child.args, "multi-pack-index", "repack", NULL); if (opts->quiet) diff --git a/builtin/help.c b/builtin/help.c index 86a3d03a9b..c0aece4da3 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -114,6 +114,49 @@ struct slot_expansion { int found; }; +static void set_config_vars(struct string_list *keys_uniq, struct string_list_item *var) +{ + struct strbuf sb = STRBUF_INIT; + const char *str = var->string; + const char *wildcard = strchr(str, '*'); + const char *tag = strchr(str, '<'); + const char *cut; + + if (wildcard && tag) + cut = wildcard < tag ? wildcard : tag; + else if (wildcard) + cut = wildcard; + else if (tag) + cut = tag; + else { + string_list_append(keys_uniq, str); + return; + } + + strbuf_add(&sb, str, cut - str); + string_list_append(keys_uniq, sb.buf); + strbuf_release(&sb); +} + +static void set_config_sections(struct string_list *keys_uniq, struct string_list_item *var) +{ + struct strbuf sb = STRBUF_INIT; + const char *str = var->string; + const char *dot = strchr(str, '.'); + const char *cut; + + if (dot) + cut = dot; + else { + set_config_vars(keys_uniq, var); + return; + } + + strbuf_add(&sb, str, cut - str); + string_list_append(keys_uniq, sb.buf); + strbuf_release(&sb); +} + static void list_config_help(enum show_config_type type) { struct slot_expansion slot_expansions[] = { @@ -134,13 +177,12 @@ static void list_config_help(enum show_config_type type) struct string_list keys = STRING_LIST_INIT_DUP; struct string_list keys_uniq = STRING_LIST_INIT_DUP; struct string_list_item *item; + struct strbuf sb = STRBUF_INIT; for (p = config_name_list; *p; p++) { const char *var = *p; - struct strbuf sb = STRBUF_INIT; for (e = slot_expansions; e->prefix; e++) { - strbuf_reset(&sb); strbuf_addf(&sb, "%s.%s", e->prefix, e->placeholder); if (!strcasecmp(var, sb.buf)) { @@ -149,60 +191,39 @@ static void list_config_help(enum show_config_type type) break; } } - strbuf_release(&sb); + if (!e->prefix) string_list_append(&keys, var); } + strbuf_release(&sb); + for (e = slot_expansions; e->prefix; e++) if (!e->found) BUG("slot_expansion %s.%s is not used", e->prefix, e->placeholder); - string_list_sort(&keys); for (size_t i = 0; i < keys.nr; i++) { - const char *var = keys.items[i].string; - const char *wildcard, *tag, *cut; - const char *dot = NULL; - struct strbuf sb = STRBUF_INIT; - switch (type) { case SHOW_CONFIG_HUMAN: - puts(var); - continue; + string_list_append(&keys_uniq, keys.items[i].string); + break; case SHOW_CONFIG_SECTIONS: - dot = strchr(var, '.'); + set_config_sections(&keys_uniq, &keys.items[i]); break; case SHOW_CONFIG_VARS: + set_config_vars(&keys_uniq, &keys.items[i]); break; + default: + BUG("%d: unexpected type", type); } - wildcard = strchr(var, '*'); - tag = strchr(var, '<'); - - if (!dot && !wildcard && !tag) { - string_list_append(&keys_uniq, var); - continue; - } - - if (dot) - cut = dot; - else if (wildcard && !tag) - cut = wildcard; - else if (!wildcard && tag) - cut = tag; - else - cut = wildcard < tag ? wildcard : tag; - - strbuf_add(&sb, var, cut - var); - string_list_append(&keys_uniq, sb.buf); - strbuf_release(&sb); - } - string_list_clear(&keys, 0); - string_list_remove_duplicates(&keys_uniq, 0); + + string_list_sort_u(&keys_uniq, 0); for_each_string_list_item(item, &keys_uniq) puts(item->string); string_list_clear(&keys_uniq, 0); + string_list_clear(&keys, 0); } static enum help_format parse_help_format(const char *format) diff --git a/builtin/history.c b/builtin/history.c index 88822a184f..568dc75ee7 100644 --- a/builtin/history.c +++ b/builtin/history.c @@ -1,6 +1,7 @@ #define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" +#include "cache-tree.h" #include "commit.h" #include "commit-reach.h" #include "config.h" @@ -8,17 +9,24 @@ #include "environment.h" #include "gettext.h" #include "hex.h" +#include "lockfile.h" +#include "oidmap.h" #include "parse-options.h" +#include "path.h" +#include "read-cache.h" #include "refs.h" #include "replay.h" #include "revision.h" #include "sequencer.h" #include "strvec.h" #include "tree.h" +#include "unpack-trees.h" #include "wt-status.h" #define GIT_HISTORY_REWORD_USAGE \ N_("git history reword <commit> [--dry-run] [--update-refs=(branches|head)]") +#define GIT_HISTORY_SPLIT_USAGE \ + N_("git history split <commit> [--dry-run] [--update-refs=(branches|head)] [--] [<pathspec>...]") static void change_data_free(void *util, const char *str UNUSED) { @@ -83,10 +91,13 @@ static int fill_commit_message(struct repository *repo, return 0; } -static int commit_tree_with_edited_message(struct repository *repo, - const char *action, - struct commit *original, - struct commit **out) +static int commit_tree_with_edited_message_ext(struct repository *repo, + const char *action, + struct commit *commit_with_message, + const struct commit_list *parents, + const struct object_id *old_tree, + const struct object_id *new_tree, + struct commit **out) { const char *exclude_gpgsig[] = { /* We reencode the message, so the encoding needs to be stripped. */ @@ -100,44 +111,27 @@ static int commit_tree_with_edited_message(struct repository *repo, struct commit_extra_header *original_extra_headers = NULL; struct strbuf commit_message = STRBUF_INIT; struct object_id rewritten_commit_oid; - struct object_id original_tree_oid; - struct object_id parent_tree_oid; char *original_author = NULL; - struct commit *parent; size_t len; int ret; - original_tree_oid = repo_get_commit_tree(repo, original)->object.oid; - - parent = original->parents ? original->parents->item : NULL; - if (parent) { - if (repo_parse_commit(repo, parent)) { - ret = error(_("unable to parse parent commit %s"), - oid_to_hex(&parent->object.oid)); - goto out; - } - - parent_tree_oid = repo_get_commit_tree(repo, parent)->object.oid; - } else { - oidcpy(&parent_tree_oid, repo->hash_algo->empty_tree); - } - /* We retain authorship of the original commit. */ - original_message = repo_logmsg_reencode(repo, original, NULL, NULL); + original_message = repo_logmsg_reencode(repo, commit_with_message, NULL, NULL); ptr = find_commit_header(original_message, "author", &len); if (ptr) original_author = xmemdupz(ptr, len); find_commit_subject(original_message, &original_body); - ret = fill_commit_message(repo, &parent_tree_oid, &original_tree_oid, + ret = fill_commit_message(repo, old_tree, new_tree, original_body, action, &commit_message); if (ret < 0) goto out; - original_extra_headers = read_commit_extra_headers(original, exclude_gpgsig); + original_extra_headers = read_commit_extra_headers(commit_with_message, + exclude_gpgsig); - ret = commit_tree_extended(commit_message.buf, commit_message.len, &original_tree_oid, - original->parents, &rewritten_commit_oid, original_author, + ret = commit_tree_extended(commit_message.buf, commit_message.len, new_tree, + parents, &rewritten_commit_oid, original_author, NULL, NULL, original_extra_headers); if (ret < 0) goto out; @@ -151,6 +145,33 @@ out: return ret; } +static int commit_tree_with_edited_message(struct repository *repo, + const char *action, + struct commit *original, + struct commit **out) +{ + struct object_id parent_tree_oid; + const struct object_id *tree_oid; + struct commit *parent; + + tree_oid = &repo_get_commit_tree(repo, original)->object.oid; + + parent = original->parents ? original->parents->item : NULL; + if (parent) { + if (repo_parse_commit(repo, parent)) { + return error(_("unable to parse parent commit %s"), + oid_to_hex(&parent->object.oid)); + } + + parent_tree_oid = repo_get_commit_tree(repo, parent)->object.oid; + } else { + oidcpy(&parent_tree_oid, repo->hash_algo->empty_tree); + } + + return commit_tree_with_edited_message_ext(repo, action, original, original->parents, + &parent_tree_oid, tree_oid, out); +} + enum ref_action { REF_ACTION_DEFAULT, REF_ACTION_BRANCHES, @@ -471,6 +492,246 @@ out: return ret; } +static int write_ondisk_index(struct repository *repo, + struct object_id *oid, + const char *path) +{ + struct unpack_trees_options opts = { 0 }; + struct lock_file lock = LOCK_INIT; + struct tree_desc tree_desc; + struct index_state index; + struct tree *tree; + int ret; + + index_state_init(&index, repo); + + opts.head_idx = -1; + opts.src_index = &index; + opts.dst_index = &index; + + tree = repo_parse_tree_indirect(repo, oid); + init_tree_desc(&tree_desc, &tree->object.oid, tree->buffer, tree->size); + + if (unpack_trees(1, &tree_desc, &opts)) { + ret = error(_("unable to populate index with tree")); + goto out; + } + + prime_cache_tree(repo, &index, tree); + + if (hold_lock_file_for_update(&lock, path, 0) < 0) { + ret = error_errno(_("unable to acquire index lock")); + goto out; + } + + if (write_locked_index(&index, &lock, COMMIT_LOCK)) { + ret = error(_("unable to write new index file")); + goto out; + } + + ret = 0; + +out: + rollback_lock_file(&lock); + release_index(&index); + return ret; +} + +static int split_commit(struct repository *repo, + struct commit *original, + struct pathspec *pathspec, + struct commit **out) +{ + struct interactive_options interactive_opts = INTERACTIVE_OPTIONS_INIT; + struct strbuf index_file = STRBUF_INIT; + struct index_state index = INDEX_STATE_INIT(repo); + const struct object_id *original_commit_tree_oid; + const struct object_id *old_tree_oid, *new_tree_oid; + struct object_id parent_tree_oid; + char original_commit_oid[GIT_MAX_HEXSZ + 1]; + struct commit *first_commit, *second_commit; + struct commit_list *parents = NULL; + struct tree *split_tree; + int ret; + + if (original->parents) { + if (repo_parse_commit(repo, original->parents->item)) { + ret = error(_("unable to parse parent commit %s"), + oid_to_hex(&original->parents->item->object.oid)); + goto out; + } + + parent_tree_oid = *get_commit_tree_oid(original->parents->item); + } else { + oidcpy(&parent_tree_oid, repo->hash_algo->empty_tree); + } + original_commit_tree_oid = get_commit_tree_oid(original); + + /* + * Construct the first commit. This is done by taking the original + * commit parent's tree and selectively patching changes from the diff + * between that parent and its child. + */ + repo_git_path_replace(repo, &index_file, "%s", "history-split.index"); + + ret = write_ondisk_index(repo, &parent_tree_oid, index_file.buf); + if (ret < 0) + goto out; + + ret = read_index_from(&index, index_file.buf, repo->gitdir); + if (ret < 0) { + ret = error(_("failed reading temporary index")); + goto out; + } + + oid_to_hex_r(original_commit_oid, &original->object.oid); + ret = run_add_p_index(repo, &index, index_file.buf, &interactive_opts, + original_commit_oid, pathspec, ADD_P_DISALLOW_EDIT); + if (ret < 0) + goto out; + + split_tree = write_in_core_index_as_tree(repo, &index); + if (!split_tree) { + ret = error(_("failed split tree")); + goto out; + } + + unlink(index_file.buf); + strbuf_release(&index_file); + + /* + * We disallow the cases where either the split-out commit or the + * original commit would become empty. Consequently, if we see that the + * new tree ID matches either of those trees we abort. + */ + if (oideq(&split_tree->object.oid, &parent_tree_oid)) { + ret = error(_("split commit is empty")); + goto out; + } else if (oideq(&split_tree->object.oid, original_commit_tree_oid)) { + ret = error(_("split commit tree matches original commit")); + goto out; + } + + /* + * The first commit is constructed from the split-out tree. The base + * that shall be diffed against is the parent of the original commit. + */ + ret = commit_tree_with_edited_message_ext(repo, "split-out", original, + original->parents, &parent_tree_oid, + &split_tree->object.oid, &first_commit); + if (ret < 0) { + ret = error(_("failed writing first commit")); + goto out; + } + + /* + * The second commit is constructed from the original tree. The base to + * diff against and the parent in this case is the first split-out + * commit. + */ + commit_list_append(first_commit, &parents); + + old_tree_oid = &repo_get_commit_tree(repo, first_commit)->object.oid; + new_tree_oid = &repo_get_commit_tree(repo, original)->object.oid; + + ret = commit_tree_with_edited_message_ext(repo, "split-out", original, + parents, old_tree_oid, + new_tree_oid, &second_commit); + if (ret < 0) { + ret = error(_("failed writing second commit")); + goto out; + } + + *out = second_commit; + ret = 0; + +out: + if (index_file.len) + unlink(index_file.buf); + strbuf_release(&index_file); + free_commit_list(parents); + release_index(&index); + return ret; +} + +static int cmd_history_split(int argc, + const char **argv, + const char *prefix, + struct repository *repo) +{ + const char * const usage[] = { + GIT_HISTORY_SPLIT_USAGE, + NULL, + }; + enum ref_action action = REF_ACTION_DEFAULT; + int dry_run = 0; + struct option options[] = { + OPT_CALLBACK_F(0, "update-refs", &action, N_("<refs>"), + N_("control ref update behavior (branches|head|print)"), + PARSE_OPT_NONEG, parse_ref_action), + OPT_BOOL('n', "dry-run", &dry_run, + N_("perform a dry-run without updating any refs")), + OPT_END(), + }; + struct commit *original, *rewritten = NULL; + struct strbuf reflog_msg = STRBUF_INIT; + struct pathspec pathspec = { 0 }; + struct rev_info revs = { 0 }; + int ret; + + argc = parse_options(argc, argv, prefix, options, usage, 0); + if (argc < 1) { + ret = error(_("command expects a committish")); + goto out; + } + repo_config(repo, git_default_config, NULL); + + if (action == REF_ACTION_DEFAULT) + action = REF_ACTION_BRANCHES; + + parse_pathspec(&pathspec, 0, + PATHSPEC_PREFER_FULL | + PATHSPEC_SYMLINK_LEADING_PATH | + PATHSPEC_PREFIX_ORIGIN, + prefix, argv + 1); + + original = lookup_commit_reference_by_name(argv[0]); + if (!original) { + ret = error(_("commit cannot be found: %s"), argv[0]); + goto out; + } + + ret = setup_revwalk(repo, action, original, &revs); + if (ret < 0) + goto out; + + if (original->parents && original->parents->next) { + ret = error(_("cannot split up merge commit")); + goto out; + } + + ret = split_commit(repo, original, &pathspec, &rewritten); + if (ret < 0) + goto out; + + strbuf_addf(&reflog_msg, "split: updating %s", argv[0]); + + ret = handle_reference_updates(&revs, action, original, rewritten, + reflog_msg.buf, dry_run); + if (ret < 0) { + ret = error(_("failed replaying descendants")); + goto out; + } + + ret = 0; + +out: + strbuf_release(&reflog_msg); + clear_pathspec(&pathspec); + release_revisions(&revs); + return ret; +} + int cmd_history(int argc, const char **argv, const char *prefix, @@ -478,11 +739,13 @@ int cmd_history(int argc, { const char * const usage[] = { GIT_HISTORY_REWORD_USAGE, + GIT_HISTORY_SPLIT_USAGE, NULL, }; parse_opt_subcommand_fn *fn = NULL; struct option options[] = { OPT_SUBCOMMAND("reword", &fn, cmd_history_reword), + OPT_SUBCOMMAND("split", &fn, cmd_history_split), OPT_END(), }; diff --git a/builtin/hook.c b/builtin/hook.c index 83020dfb4f..c0585587e5 100644 --- a/builtin/hook.c +++ b/builtin/hook.c @@ -4,14 +4,22 @@ #include "environment.h" #include "gettext.h" #include "hook.h" +#include "hook-list.h" #include "parse-options.h" -#include "strvec.h" -#include "abspath.h" #define BUILTIN_HOOK_RUN_USAGE \ - N_("git hook run [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]") + N_("git hook run [--allow-unknown-hook-name] [--ignore-missing] [--to-stdin=<path>] <hook-name> [-- <hook-args>]") #define BUILTIN_HOOK_LIST_USAGE \ - N_("git hook list [-z] <hook-name>") + N_("git hook list [--allow-unknown-hook-name] [-z] [--show-scope] <hook-name>") + +static int is_known_hook(const char *name) +{ + const char **p; + for (p = hook_name_list; *p; p++) + if (!strcmp(*p, name)) + return 1; + return 0; +} static const char * const builtin_hook_usage[] = { BUILTIN_HOOK_RUN_USAGE, @@ -35,11 +43,17 @@ static int list(int argc, const char **argv, const char *prefix, struct string_list_item *item; const char *hookname = NULL; int line_terminator = '\n'; + int show_scope = 0; + int allow_unknown = 0; int ret = 0; struct option list_options[] = { OPT_SET_INT('z', NULL, &line_terminator, N_("use NUL as line terminator"), '\0'), + OPT_BOOL(0, "show-scope", &show_scope, + N_("show the config scope that defined each hook")), + OPT_BOOL(0, "allow-unknown-hook-name", &allow_unknown, + N_("allow running a hook with a non-native hook name")), OPT_END(), }; @@ -51,15 +65,22 @@ static int list(int argc, const char **argv, const char *prefix, * arguments later they probably should be caught by parse_options. */ if (argc != 1) - usage_msg_opt(_("You must specify a hook event name to list."), + usage_msg_opt(_("you must specify a hook event name to list"), builtin_hook_list_usage, list_options); hookname = argv[0]; + if (!allow_unknown && !is_known_hook(hookname)) { + error(_("unknown hook event '%s';\n" + "use --allow-unknown-hook-name to allow non-native hook names"), + hookname); + return 1; + } + head = list_hooks(repo, hookname, NULL); if (!head->nr) { - warning(_("No hooks found for event '%s'"), hookname); + warning(_("no hooks found for event '%s'"), hookname); ret = 1; /* no hooks found */ goto cleanup; } @@ -71,16 +92,27 @@ static int list(int argc, const char **argv, const char *prefix, case HOOK_TRADITIONAL: printf("%s%c", _("hook from hookdir"), line_terminator); break; - case HOOK_CONFIGURED: - printf("%s%c", h->u.configured.friendly_name, line_terminator); + case HOOK_CONFIGURED: { + const char *name = h->u.configured.friendly_name; + const char *scope = show_scope ? + config_scope_name(h->u.configured.scope) : NULL; + if (scope) + printf("%s\t%s%s%c", scope, + h->u.configured.disabled ? "disabled\t" : "", + name, line_terminator); + else + printf("%s%s%c", + h->u.configured.disabled ? "disabled\t" : "", + name, line_terminator); break; + } default: BUG("unknown hook kind"); } } cleanup: - hook_list_clear(head, NULL); + string_list_clear_func(head, hook_free); free(head); return ret; } @@ -91,8 +123,11 @@ static int run(int argc, const char **argv, const char *prefix, int i; struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT; int ignore_missing = 0; + int allow_unknown = 0; const char *hook_name; struct option run_options[] = { + OPT_BOOL(0, "allow-unknown-hook-name", &allow_unknown, + N_("allow running a hook with a non-native hook name")), OPT_BOOL(0, "ignore-missing", &ignore_missing, N_("silently ignore missing requested <hook-name>")), OPT_STRING(0, "to-stdin", &opt.path_to_stdin, N_("path"), @@ -124,6 +159,14 @@ static int run(int argc, const char **argv, const char *prefix, repo_config(the_repository, git_default_config, NULL); hook_name = argv[0]; + + if (!allow_unknown && !is_known_hook(hook_name)) { + error(_("unknown hook event '%s';\n" + "use --allow-unknown-hook-name to allow non-native hook names"), + hook_name); + return 1; + } + if (!ignore_missing) opt.error_if_missing = 1; ret = run_hooks_opt(the_repository, hook_name, &opt); diff --git a/builtin/index-pack.c b/builtin/index-pack.c index d1e47279a8..e4129bd605 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -136,7 +136,7 @@ static int nr_threads; static int from_stdin; static int strict; static int do_fsck_object; -static struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES; +static struct fsck_options fsck_options; static int verbose; static const char *progress_title; static int show_resolving_progress; @@ -1908,6 +1908,8 @@ int cmd_index_pack(int argc, show_usage_if_asked(argc, argv, index_pack_usage); disable_replace_refs(); + + fsck_options_init(&fsck_options, the_repository, FSCK_OPTIONS_MISSING_GITMODULES); fsck_options.walk = mark_link; reset_pack_idx_option(&opts); diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c index 41b0750e5a..e7e86e9523 100644 --- a/builtin/interpret-trailers.c +++ b/builtin/interpret-trailers.c @@ -93,37 +93,6 @@ static int parse_opt_parse(const struct option *opt, const char *arg, return 0; } -static struct tempfile *trailers_tempfile; - -static FILE *create_in_place_tempfile(const char *file) -{ - struct stat st; - struct strbuf filename_template = STRBUF_INIT; - const char *tail; - FILE *outfile; - - if (stat(file, &st)) - die_errno(_("could not stat %s"), file); - if (!S_ISREG(st.st_mode)) - die(_("file %s is not a regular file"), file); - if (!(st.st_mode & S_IWUSR)) - die(_("file %s is not writable by user"), file); - - /* Create temporary file in the same directory as the original */ - tail = strrchr(file, '/'); - if (tail) - strbuf_add(&filename_template, file, tail - file + 1); - strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX"); - - trailers_tempfile = xmks_tempfile_m(filename_template.buf, st.st_mode); - strbuf_release(&filename_template); - outfile = fdopen_tempfile(trailers_tempfile, "w"); - if (!outfile) - die_errno(_("could not open temporary file")); - - return outfile; -} - static void read_input_file(struct strbuf *sb, const char *file) { if (file) { @@ -140,55 +109,31 @@ static void interpret_trailers(const struct process_trailer_options *opts, struct list_head *new_trailer_head, const char *file) { - LIST_HEAD(head); - struct strbuf sb = STRBUF_INIT; - struct strbuf trailer_block_sb = STRBUF_INIT; - struct trailer_block *trailer_block; - FILE *outfile = stdout; + struct strbuf input = STRBUF_INIT; + struct strbuf out = STRBUF_INIT; + struct tempfile *tempfile = NULL; + int fd = 1; trailer_config_init(); - read_input_file(&sb, file); + read_input_file(&input, file); - if (opts->in_place) - outfile = create_in_place_tempfile(file); - - trailer_block = parse_trailers(opts, sb.buf, &head); - - /* Print the lines before the trailer block */ - if (!opts->only_trailers) - fwrite(sb.buf, 1, trailer_block_start(trailer_block), outfile); - - if (!opts->only_trailers && !blank_line_before_trailer_block(trailer_block)) - fprintf(outfile, "\n"); - - - if (!opts->only_input) { - LIST_HEAD(config_head); - LIST_HEAD(arg_head); - parse_trailers_from_config(&config_head); - parse_trailers_from_command_line_args(&arg_head, new_trailer_head); - list_splice(&config_head, &arg_head); - process_trailers_lists(&head, &arg_head); + if (opts->in_place) { + tempfile = trailer_create_in_place_tempfile(file); + if (!tempfile) + die(NULL); + fd = tempfile->fd; } + process_trailers(opts, new_trailer_head, &input, &out); - /* Print trailer block. */ - format_trailers(opts, &head, &trailer_block_sb); - free_trailers(&head); - fwrite(trailer_block_sb.buf, 1, trailer_block_sb.len, outfile); - strbuf_release(&trailer_block_sb); - - /* Print the lines after the trailer block as is. */ - if (!opts->only_trailers) - fwrite(sb.buf + trailer_block_end(trailer_block), 1, - sb.len - trailer_block_end(trailer_block), outfile); - trailer_block_release(trailer_block); - + if (write_in_full(fd, out.buf, out.len) < 0) + die_errno(_("could not write to temporary file '%s'"), file); if (opts->in_place) - if (rename_tempfile(&trailers_tempfile, file)) + if (rename_tempfile(&tempfile, file)) die_errno(_("could not rename temporary file to %s"), file); - strbuf_release(&sb); + strbuf_release(&input); + strbuf_release(&out); } int cmd_interpret_trailers(int argc, @@ -211,7 +156,7 @@ int cmd_interpret_trailers(int argc, N_("action if trailer is missing"), option_parse_if_missing), OPT_BOOL(0, "only-trailers", &opts.only_trailers, N_("output only the trailers")), - OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply trailer.* configuration variables")), + OPT_BOOL(0, "only-input", &opts.only_input, N_("do not apply trailer.<key-alias> configuration variables")), OPT_BOOL(0, "unfold", &opts.unfold, N_("reformat multiline trailer values as single-line values")), OPT_CALLBACK_F(0, "parse", &opts, NULL, N_("alias for --only-trailers --only-input --unfold"), PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_parse), diff --git a/builtin/log.c b/builtin/log.c index 89e8b8f011..8c0939dd42 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -40,6 +40,7 @@ #include "progress.h" #include "commit-slab.h" #include "advice.h" +#include "utf8.h" #include "commit-reach.h" #include "range-diff.h" @@ -886,6 +887,7 @@ struct format_config { char *signature; char *signature_file; enum cover_setting config_cover_letter; + char *fmt_cover_letter_commit_list; char *config_output_directory; enum cover_from_description cover_from_description_mode; int show_notes; @@ -930,6 +932,7 @@ static void format_config_release(struct format_config *cfg) string_list_clear(&cfg->extra_cc, 0); strbuf_release(&cfg->sprefix); free(cfg->fmt_patch_suffix); + free(cfg->fmt_cover_letter_commit_list); } static enum cover_from_description parse_cover_from_description(const char *arg) @@ -1052,6 +1055,10 @@ static int git_format_config(const char *var, const char *value, cfg->config_cover_letter = git_config_bool(var, value) ? COVER_ON : COVER_OFF; return 0; } + if (!strcmp(var, "format.commitlistformat")) { + FREE_AND_NULL(cfg->fmt_cover_letter_commit_list); + return git_config_string(&cfg->fmt_cover_letter_commit_list, var, value); + } if (!strcmp(var, "format.outputdirectory")) { FREE_AND_NULL(cfg->config_output_directory); return git_config_string(&cfg->config_output_directory, var, value); @@ -1335,13 +1342,54 @@ static void get_notes_args(struct strvec *arg, struct rev_info *rev) } } +static void generate_shortlog_cover_letter(struct shortlog *log, + struct rev_info *rev, + struct commit **list, + int nr) +{ + shortlog_init(log); + log->wrap_lines = 1; + log->wrap = MAIL_DEFAULT_WRAP; + log->in1 = 2; + log->in2 = 4; + log->file = rev->diffopt.file; + log->groups = SHORTLOG_GROUP_AUTHOR; + shortlog_finish_setup(log); + for (int i = 0; i < nr; i++) + shortlog_add_commit(log, list[i]); + + shortlog_output(log); +} + +static void generate_commit_list_cover(FILE *cover_file, const char *format, + struct commit **list, int n) +{ + struct strbuf commit_line = STRBUF_INIT; + struct pretty_print_context ctx = {0}; + struct rev_info rev = REV_INFO_INIT; + + rev.total = n; + ctx.rev = &rev; + for (int i = 1; i <= n; i++) { + rev.nr = i; + repo_format_commit_message(the_repository, list[n - i], format, + &commit_line, &ctx); + fprintf(cover_file, "%s\n", commit_line.buf); + strbuf_reset(&commit_line); + } + fprintf(cover_file, "\n"); + + strbuf_release(&commit_line); +} + static void make_cover_letter(struct rev_info *rev, int use_separate_file, struct commit *origin, int nr, struct commit **list, const char *description_file, const char *branch_name, int quiet, - const struct format_config *cfg) + const struct format_config *cfg, + const char *format) { const char *from; struct shortlog log; @@ -1388,18 +1436,17 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, free(pp.after_subject); strbuf_release(&sb); - shortlog_init(&log); - log.wrap_lines = 1; - log.wrap = MAIL_DEFAULT_WRAP; - log.in1 = 2; - log.in2 = 4; - log.file = rev->diffopt.file; - log.groups = SHORTLOG_GROUP_AUTHOR; - shortlog_finish_setup(&log); - for (i = 0; i < nr; i++) - shortlog_add_commit(&log, list[i]); - - shortlog_output(&log); + if (skip_prefix(format, "log:", &format)) + generate_commit_list_cover(rev->diffopt.file, format, list, nr); + else if (!strcmp(format, "shortlog")) + generate_shortlog_cover_letter(&log, rev, list, nr); + else if (!strcmp(format, "modern")) + generate_commit_list_cover(rev->diffopt.file, "%w(72)[%(count)/%(total)] %s", + list, nr); + else if (strchr(format, '%')) + generate_commit_list_cover(rev->diffopt.file, format, list, nr); + else + die(_("'%s' is not a valid format string"), format); /* We can only do diffstat with a unique reference point */ if (origin) @@ -1917,6 +1964,7 @@ int cmd_format_patch(int argc, int just_numbers = 0; int ignore_if_in_upstream = 0; int cover_letter = -1; + const char *cover_letter_fmt = NULL; int boundary_count = 0; int no_binary_diff = 0; int zero_commit = 0; @@ -1963,6 +2011,8 @@ int cmd_format_patch(int argc, N_("print patches to standard out")), OPT_BOOL(0, "cover-letter", &cover_letter, N_("generate a cover letter")), + OPT_STRING(0, "commit-list-format", &cover_letter_fmt, N_("format-spec"), + N_("format spec used for the commit list in the cover letter")), OPT_BOOL(0, "numbered-files", &just_numbers, N_("use simple number sequence for output file names")), OPT_STRING(0, "suffix", &fmt_patch_suffix, N_("sfx"), @@ -2300,6 +2350,15 @@ int cmd_format_patch(int argc, /* nothing to do */ goto done; total = list.nr; + + if (!cover_letter_fmt) { + cover_letter_fmt = cfg.fmt_cover_letter_commit_list; + if (!cover_letter_fmt) + cover_letter_fmt = "shortlog"; + } else if (cover_letter == -1) { + cover_letter = 1; + } + if (cover_letter == -1) { if (cfg.config_cover_letter == COVER_AUTO) cover_letter = (total > 1); @@ -2386,12 +2445,14 @@ int cmd_format_patch(int argc, } rev.numbered_files = just_numbers; rev.patch_suffix = fmt_patch_suffix; + if (cover_letter) { if (cfg.thread) gen_message_id(&rev, "cover"); make_cover_letter(&rev, !!output_directory, origin, list.nr, list.items, - description_file, branch_name, quiet, &cfg); + description_file, branch_name, quiet, &cfg, + cover_letter_fmt); print_bases(&bases, rev.diffopt.file); print_signature(signature, rev.diffopt.file); total++; diff --git a/builtin/merge-file.c b/builtin/merge-file.c index 084fdfec58..59a9792208 100644 --- a/builtin/merge-file.c +++ b/builtin/merge-file.c @@ -108,7 +108,8 @@ int cmd_merge_file(int argc, return error_errno("failed to redirect stderr to /dev/null"); } - if (object_id) + if (!repo && object_id) + /* emit the correct "not a git repo" error in this case */ setup_git_directory(); for (i = 0; i < 3; i++) { diff --git a/builtin/merge.c b/builtin/merge.c index 4e456a381c..2cbce56f8d 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -506,7 +506,7 @@ static void finish(struct commit *head_commit, * We ignore errors in 'gc --auto', since the * user should see them. */ - run_auto_maintenance(verbosity < 0); + run_auto_maintenance(the_repository, verbosity < 0); } } if (new_head && show_diffstat) { diff --git a/builtin/mktag.c b/builtin/mktag.c index 7cf6e1230a..f40264a878 100644 --- a/builtin/mktag.c +++ b/builtin/mktag.c @@ -16,7 +16,7 @@ static char const * const builtin_mktag_usage[] = { }; static int option_strict = 1; -static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT; +static struct fsck_options fsck_options; static int mktag_fsck_error_func(struct fsck_options *o UNUSED, void *fsck_report UNUSED, @@ -75,7 +75,7 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type) int cmd_mktag(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { static struct option builtin_mktag_options[] = { OPT_BOOL(0, "strict", &option_strict, @@ -94,6 +94,7 @@ int cmd_mktag(int argc, if (strbuf_read(&buf, 0, 0) < 0) die_errno(_("could not read from stdin")); + fsck_options_init(&fsck_options, repo, FSCK_OPTIONS_STRICT); fsck_options.error_func = mktag_fsck_error_func; fsck_set_msg_type_from_ids(&fsck_options, FSCK_MSG_EXTRA_HEADER_ENTRY, FSCK_WARN); diff --git a/builtin/mktree.c b/builtin/mktree.c index 12772303f5..4084e32476 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -3,7 +3,6 @@ * * Copyright (c) Junio C Hamano, 2006, 2009 */ -#define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" #include "gettext.h" #include "hex.h" @@ -46,7 +45,7 @@ static int ent_compare(const void *a_, const void *b_) b->name, b->len, b->mode); } -static void write_tree(struct object_id *oid) +static void write_tree(struct repository *repo, struct object_id *oid) { struct strbuf buf; size_t size; @@ -60,10 +59,10 @@ static void write_tree(struct object_id *oid) for (i = 0; i < used; i++) { struct treeent *ent = entries[i]; strbuf_addf(&buf, "%o %s%c", ent->mode, ent->name, '\0'); - strbuf_add(&buf, ent->oid.hash, the_hash_algo->rawsz); + strbuf_add(&buf, ent->oid.hash, repo->hash_algo->rawsz); } - odb_write_object(the_repository->objects, buf.buf, buf.len, OBJ_TREE, oid); + odb_write_object(repo->objects, buf.buf, buf.len, OBJ_TREE, oid); strbuf_release(&buf); } @@ -72,7 +71,7 @@ static const char *const mktree_usage[] = { NULL }; -static void mktree_line(char *buf, int nul_term_line, int allow_missing) +static void mktree_line(struct repository *repo, char *buf, int nul_term_line, int allow_missing) { char *ptr, *ntr; const char *p; @@ -93,7 +92,7 @@ static void mktree_line(char *buf, int nul_term_line, int allow_missing) die("input format error: %s", buf); ptr = ntr + 1; /* type */ ntr = strchr(ptr, ' '); - if (!ntr || parse_oid_hex(ntr + 1, &oid, &p) || + if (!ntr || parse_oid_hex_algop(ntr + 1, &oid, &p, repo->hash_algo) || *p != '\t') die("input format error: %s", buf); @@ -124,7 +123,7 @@ static void mktree_line(char *buf, int nul_term_line, int allow_missing) /* Check the type of object identified by oid without fetching objects */ oi.typep = &obj_type; - if (odb_read_object_info_extended(the_repository->objects, &oid, &oi, + if (odb_read_object_info_extended(repo->objects, &oid, &oi, OBJECT_INFO_LOOKUP_REPLACE | OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT) < 0) @@ -155,7 +154,7 @@ static void mktree_line(char *buf, int nul_term_line, int allow_missing) int cmd_mktree(int ac, const char **av, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { struct strbuf sb = STRBUF_INIT; struct object_id oid; @@ -187,7 +186,7 @@ int cmd_mktree(int ac, break; die("input format error: (blank line only valid in batch mode)"); } - mktree_line(sb.buf, nul_term_line, allow_missing); + mktree_line(repo, sb.buf, nul_term_line, allow_missing); } if (is_batch_mode && got_eof && used < 1) { /* @@ -197,7 +196,7 @@ int cmd_mktree(int ac, */ ; /* skip creating an empty tree */ } else { - write_tree(&oid); + write_tree(repo, &oid); puts(oid_to_hex(&oid)); fflush(stdout); } diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c index 5f364aa816..0f72d96c02 100644 --- a/builtin/multi-pack-index.c +++ b/builtin/multi-pack-index.c @@ -9,12 +9,18 @@ #include "strbuf.h" #include "trace2.h" #include "odb.h" +#include "odb/source.h" #include "replace-object.h" #include "repository.h" #define BUILTIN_MIDX_WRITE_USAGE \ - N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]" \ - "[--refs-snapshot=<path>]") + N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]\n" \ + " [--[no-]bitmap] [--[no-]incremental] [--[no-]stdin-packs]\n" \ + " [--refs-snapshot=<path>]") + +#define BUILTIN_MIDX_COMPACT_USAGE \ + N_("git multi-pack-index [<options>] compact [--[no-]incremental]\n" \ + " [--[no-]bitmap] <from> <to>") #define BUILTIN_MIDX_VERIFY_USAGE \ N_("git multi-pack-index [<options>] verify") @@ -29,6 +35,10 @@ static char const * const builtin_multi_pack_index_write_usage[] = { BUILTIN_MIDX_WRITE_USAGE, NULL }; +static char const * const builtin_multi_pack_index_compact_usage[] = { + BUILTIN_MIDX_COMPACT_USAGE, + NULL +}; static char const * const builtin_multi_pack_index_verify_usage[] = { BUILTIN_MIDX_VERIFY_USAGE, NULL @@ -43,6 +53,7 @@ static char const * const builtin_multi_pack_index_repack_usage[] = { }; static char const * const builtin_multi_pack_index_usage[] = { BUILTIN_MIDX_WRITE_USAGE, + BUILTIN_MIDX_COMPACT_USAGE, BUILTIN_MIDX_VERIFY_USAGE, BUILTIN_MIDX_EXPIRE_USAGE, BUILTIN_MIDX_REPACK_USAGE, @@ -84,6 +95,8 @@ static struct option common_opts[] = { N_("directory"), N_("object directory containing set of packfile and pack-index pairs"), parse_object_dir), + OPT_BIT(0, "progress", &opts.flags, N_("force progress reporting"), + MIDX_PROGRESS), OPT_END(), }; @@ -138,8 +151,6 @@ static int cmd_multi_pack_index_write(int argc, const char **argv, N_("pack for reuse when computing a multi-pack bitmap")), OPT_BIT(0, "bitmap", &opts.flags, N_("write multi-pack bitmap"), MIDX_WRITE_BITMAP | MIDX_WRITE_REV_INDEX), - OPT_BIT(0, "progress", &opts.flags, - N_("force progress reporting"), MIDX_PROGRESS), OPT_BIT(0, "incremental", &opts.flags, N_("write a new incremental MIDX"), MIDX_WRITE_INCREMENTAL), OPT_BOOL(0, "stdin-packs", &opts.stdin_packs, @@ -194,14 +205,78 @@ static int cmd_multi_pack_index_write(int argc, const char **argv, return ret; } +static int cmd_multi_pack_index_compact(int argc, const char **argv, + const char *prefix, + struct repository *repo) +{ + struct multi_pack_index *m, *cur; + struct multi_pack_index *from_midx = NULL; + struct multi_pack_index *to_midx = NULL; + struct odb_source *source; + int ret; + + struct option *options; + static struct option builtin_multi_pack_index_compact_options[] = { + OPT_BIT(0, "bitmap", &opts.flags, N_("write multi-pack bitmap"), + MIDX_WRITE_BITMAP | MIDX_WRITE_REV_INDEX), + OPT_BIT(0, "incremental", &opts.flags, + N_("write a new incremental MIDX"), MIDX_WRITE_INCREMENTAL), + OPT_END(), + }; + + repo_config(repo, git_multi_pack_index_write_config, NULL); + + options = add_common_options(builtin_multi_pack_index_compact_options); + + trace2_cmd_mode(argv[0]); + + if (isatty(2)) + opts.flags |= MIDX_PROGRESS; + argc = parse_options(argc, argv, prefix, + options, builtin_multi_pack_index_compact_usage, + 0); + + if (argc != 2) + usage_with_options(builtin_multi_pack_index_compact_usage, + options); + source = handle_object_dir_option(the_repository); + + FREE_AND_NULL(options); + + m = get_multi_pack_index(source); + + for (cur = m; cur && !(from_midx && to_midx); cur = cur->base_midx) { + const char *midx_csum = midx_get_checksum_hex(cur); + + if (!from_midx && !strcmp(midx_csum, argv[0])) + from_midx = cur; + if (!to_midx && !strcmp(midx_csum, argv[1])) + to_midx = cur; + } + + if (!from_midx) + die(_("could not find MIDX: %s"), argv[0]); + if (!to_midx) + die(_("could not find MIDX: %s"), argv[1]); + if (from_midx == to_midx) + die(_("MIDX compaction endpoints must be unique")); + + for (m = from_midx; m; m = m->base_midx) { + if (m == to_midx) + die(_("MIDX %s must be an ancestor of %s"), argv[0], argv[1]); + } + + ret = write_midx_file_compact(source, from_midx, to_midx, opts.flags); + + return ret; +} + static int cmd_multi_pack_index_verify(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) { struct option *options; static struct option builtin_multi_pack_index_verify_options[] = { - OPT_BIT(0, "progress", &opts.flags, - N_("force progress reporting"), MIDX_PROGRESS), OPT_END(), }; struct odb_source *source; @@ -231,8 +306,6 @@ static int cmd_multi_pack_index_expire(int argc, const char **argv, { struct option *options; static struct option builtin_multi_pack_index_expire_options[] = { - OPT_BIT(0, "progress", &opts.flags, - N_("force progress reporting"), MIDX_PROGRESS), OPT_END(), }; struct odb_source *source; @@ -264,8 +337,6 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv, static struct option builtin_multi_pack_index_repack_options[] = { OPT_UNSIGNED(0, "batch-size", &opts.batch_size, N_("during repack, collect pack-files of smaller size into a batch that is larger than this size")), - OPT_BIT(0, "progress", &opts.flags, - N_("force progress reporting"), MIDX_PROGRESS), OPT_END(), }; struct odb_source *source; @@ -300,6 +371,7 @@ int cmd_multi_pack_index(int argc, struct option builtin_multi_pack_index_options[] = { OPT_SUBCOMMAND("repack", &fn, cmd_multi_pack_index_repack), OPT_SUBCOMMAND("write", &fn, cmd_multi_pack_index_write), + OPT_SUBCOMMAND("compact", &fn, cmd_multi_pack_index_compact), OPT_SUBCOMMAND("verify", &fn, cmd_multi_pack_index_verify), OPT_SUBCOMMAND("expire", &fn, cmd_multi_pack_index_expire), OPT_END(), diff --git a/builtin/name-rev.c b/builtin/name-rev.c index 6188cf98ce..d6594ada53 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -12,7 +12,6 @@ #include "object-name.h" #include "pager.h" #include "parse-options.h" -#include "prio-queue.h" #include "hash-lookup.h" #include "commit-slab.h" #include "commit-graph.h" @@ -178,7 +177,7 @@ static void name_rev(struct commit *start_commit, const char *tip_name, timestamp_t taggerdate, int from_tag, int deref, struct mem_pool *string_pool) { - struct prio_queue queue; + struct commit_stack stack = COMMIT_STACK_INIT; struct commit *commit; struct commit_stack parents_to_queue = COMMIT_STACK_INIT; struct rev_name *start_name; @@ -197,10 +196,9 @@ static void name_rev(struct commit *start_commit, else start_name->tip_name = mem_pool_strdup(string_pool, tip_name); - memset(&queue, 0, sizeof(queue)); /* Use the prio_queue as LIFO */ - prio_queue_put(&queue, start_commit); + commit_stack_push(&stack, start_commit); - while ((commit = prio_queue_get(&queue))) { + while ((commit = commit_stack_pop(&stack))) { struct rev_name *name = get_commit_rev_name(commit); struct commit_list *parents; int parent_number = 1; @@ -241,13 +239,13 @@ static void name_rev(struct commit *start_commit, } } - /* The first parent must come out first from the prio_queue */ + /* The first parent must come out first from the stack */ while (parents_to_queue.nr) - prio_queue_put(&queue, - commit_stack_pop(&parents_to_queue)); + commit_stack_push(&stack, + commit_stack_pop(&parents_to_queue)); } - clear_prio_queue(&queue); + commit_stack_clear(&stack); commit_stack_clear(&parents_to_queue); } diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index cd013c0b68..dd2480a73d 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -28,6 +28,7 @@ #include "reachable.h" #include "oid-array.h" #include "strvec.h" +#include "strmap.h" #include "list.h" #include "packfile.h" #include "object-file.h" @@ -41,6 +42,7 @@ #include "promisor-remote.h" #include "pack-mtimes.h" #include "parse-options.h" +#include "pkt-line.h" #include "blob.h" #include "tree.h" #include "path-walk.h" @@ -216,6 +218,7 @@ static int have_non_local_packs; static int incremental; static int ignore_packed_keep_on_disk; static int ignore_packed_keep_in_core; +static int ignore_packed_keep_in_core_open; static int ignore_packed_keep_in_core_has_cruft; static int allow_ofs_delta; static struct pack_idx_option pack_idx_opts; @@ -1330,11 +1333,25 @@ static void write_pack_file(void) unsigned char hash[GIT_MAX_RAWSZ]; char *pack_tmp_name = NULL; - if (pack_to_stdout) - f = hashfd_throughput(the_repository->hash_algo, 1, - "<stdout>", progress_state); - else + if (pack_to_stdout) { + /* + * This command is most often invoked via + * git-upload-pack(1), which will typically chunk data + * into pktlines. As such, we use the maximum data + * length of them as buffer length. + * + * Note that we need to subtract one though to + * accomodate for the sideband byte. + */ + struct hashfd_options opts = { + .progress = progress_state, + .buffer_len = LARGE_PACKET_DATA_MAX - 1, + }; + f = hashfd_ext(the_repository->hash_algo, 1, + "<stdout>", &opts); + } else { f = create_tmp_packfile(the_repository, &pack_tmp_name); + } offset = write_pack_header(f, nr_remaining); @@ -1617,7 +1634,8 @@ static int want_found_object(const struct object_id *oid, int exclude, /* * Then handle .keep first, as we have a fast(er) path there. */ - if (ignore_packed_keep_on_disk || ignore_packed_keep_in_core) { + if (ignore_packed_keep_on_disk || ignore_packed_keep_in_core || + ignore_packed_keep_in_core_open) { /* * Set the flags for the kept-pack cache to be the ones we want * to ignore. @@ -1631,6 +1649,8 @@ static int want_found_object(const struct object_id *oid, int exclude, flags |= KEPT_PACK_ON_DISK; if (ignore_packed_keep_in_core) flags |= KEPT_PACK_IN_CORE; + if (ignore_packed_keep_in_core_open) + flags |= KEPT_PACK_IN_CORE_OPEN; /* * If the object is in a pack that we want to ignore, *and* we @@ -1642,6 +1662,8 @@ static int want_found_object(const struct object_id *oid, int exclude, return 0; if (ignore_packed_keep_in_core && p->pack_keep_in_core) return 0; + if (ignore_packed_keep_in_core_open && p->pack_keep_in_core_open) + return 0; if (has_object_kept_pack(p->repo, oid, flags)) return 0; } else { @@ -3741,6 +3763,7 @@ static int add_object_entry_from_pack(const struct object_id *oid, void *_data) { off_t ofs; + struct object_info oi = OBJECT_INFO_INIT; enum object_type type = OBJ_NONE; display_progress(progress_state, ++nr_seen); @@ -3748,29 +3771,34 @@ static int add_object_entry_from_pack(const struct object_id *oid, if (have_duplicate_entry(oid, 0)) return 0; - ofs = nth_packed_object_offset(p, pos); - if (!want_object_in_pack(oid, 0, &p, &ofs)) - return 0; + stdin_packs_found_nr++; - if (p) { - struct object_info oi = OBJECT_INFO_INIT; - - oi.typep = &type; - if (packed_object_info(p, ofs, &oi) < 0) { - die(_("could not get type of object %s in pack %s"), - oid_to_hex(oid), p->pack_name); - } else if (type == OBJ_COMMIT) { - struct rev_info *revs = _data; - /* - * commits in included packs are used as starting points for the - * subsequent revision walk - */ - add_pending_oid(revs, NULL, oid, 0); - } + ofs = nth_packed_object_offset(p, pos); - stdin_packs_found_nr++; + oi.typep = &type; + if (packed_object_info(p, ofs, &oi) < 0) { + die(_("could not get type of object %s in pack %s"), + oid_to_hex(oid), p->pack_name); + } else if (type == OBJ_COMMIT) { + struct rev_info *revs = _data; + /* + * commits in included packs are used as starting points + * for the subsequent revision walk + * + * Note that we do want to walk through commits that are + * present in excluded-open ('!') packs to pick up any + * objects reachable from them not present in the + * excluded-closed ('^') packs. + * + * However, we'll only add those objects to the packing + * list after checking `want_object_in_pack()` below. + */ + add_pending_oid(revs, NULL, oid, 0); } + if (!want_object_in_pack(oid, 0, &p, &ofs)) + return 0; + create_object_entry(oid, type, 0, 0, 0, p, ofs); return 0; @@ -3820,107 +3848,197 @@ static void show_commit_pack_hint(struct commit *commit, void *data) } +/* + * stdin_pack_info_kind specifies how a pack specified over stdin + * should be treated when pack-objects is invoked with --stdin-packs. + * + * - STDIN_PACK_INCLUDE: objects in any packs with this flag bit set + * should be included in the output pack, unless they appear in an + * excluded pack. + * + * - STDIN_PACK_EXCLUDE_CLOSED: objects in any packs with this flag + * bit set should be excluded from the output pack. + * + * - STDIN_PACK_EXCLUDE_OPEN: objects in any packs with this flag + * bit set should be excluded from the output pack, but are not + * guaranteed to be closed under reachability. + * + * Objects in packs whose 'kind' bits include STDIN_PACK_INCLUDE or + * STDIN_PACK_EXCLUDE_OPEN are used as traversal tips when invoked + * with --stdin-packs=follow. + */ +enum stdin_pack_info_kind { + STDIN_PACK_INCLUDE = (1<<0), + STDIN_PACK_EXCLUDE_CLOSED = (1<<1), + STDIN_PACK_EXCLUDE_OPEN = (1<<2), +}; + +struct stdin_pack_info { + struct packed_git *p; + enum stdin_pack_info_kind kind; +}; + static int pack_mtime_cmp(const void *_a, const void *_b) { - struct packed_git *a = ((const struct string_list_item*)_a)->util; - struct packed_git *b = ((const struct string_list_item*)_b)->util; + struct stdin_pack_info *a = ((const struct string_list_item*)_a)->util; + struct stdin_pack_info *b = ((const struct string_list_item*)_b)->util; /* * order packs by descending mtime so that objects are laid out * roughly as newest-to-oldest */ - if (a->mtime < b->mtime) + if (a->p->mtime < b->p->mtime) return 1; - else if (b->mtime < a->mtime) + else if (b->p->mtime < a->p->mtime) return -1; else return 0; } -static void read_packs_list_from_stdin(struct rev_info *revs) +static int stdin_packs_include_check_obj(struct object *obj, void *data UNUSED) +{ + return !has_object_kept_pack(to_pack.repo, &obj->oid, + KEPT_PACK_IN_CORE); +} + +static int stdin_packs_include_check(struct commit *commit, void *data) +{ + return stdin_packs_include_check_obj((struct object *)commit, data); +} + +static void stdin_packs_add_pack_entries(struct strmap *packs, + struct rev_info *revs) +{ + struct string_list keys = STRING_LIST_INIT_NODUP; + struct string_list_item *item; + struct hashmap_iter iter; + struct strmap_entry *entry; + + strmap_for_each_entry(packs, &iter, entry) { + struct stdin_pack_info *info = entry->value; + if (!info->p) + die(_("could not find pack '%s'"), entry->key); + + string_list_append(&keys, entry->key)->util = info; + } + + /* + * Order packs by ascending mtime; use QSORT directly to access the + * string_list_item's ->util pointer, which string_list_sort() does not + * provide. + */ + QSORT(keys.items, keys.nr, pack_mtime_cmp); + + for_each_string_list_item(item, &keys) { + struct stdin_pack_info *info = item->util; + + if (info->kind & STDIN_PACK_EXCLUDE_OPEN) { + /* + * When open-excluded packs ("!") are present, stop + * the parent walk at closed-excluded ("^") packs. + * Objects behind a "^" boundary are guaranteed to + * have closure and should not be rescued. + */ + revs->include_check = stdin_packs_include_check; + revs->include_check_obj = stdin_packs_include_check_obj; + } + + if ((info->kind & STDIN_PACK_INCLUDE) || + (info->kind & STDIN_PACK_EXCLUDE_OPEN)) + for_each_object_in_pack(info->p, + add_object_entry_from_pack, + revs, + ODB_FOR_EACH_OBJECT_PACK_ORDER); + } + + string_list_clear(&keys, 0); +} + +static void stdin_packs_read_input(struct rev_info *revs, + enum stdin_packs_mode mode) { struct strbuf buf = STRBUF_INIT; - struct string_list include_packs = STRING_LIST_INIT_DUP; - struct string_list exclude_packs = STRING_LIST_INIT_DUP; - struct string_list_item *item = NULL; + struct strmap packs = STRMAP_INIT; struct packed_git *p; while (strbuf_getline(&buf, stdin) != EOF) { - if (!buf.len) + struct stdin_pack_info *info; + enum stdin_pack_info_kind kind = STDIN_PACK_INCLUDE; + const char *key = buf.buf; + + if (!*key) continue; + else if (*key == '^') + kind = STDIN_PACK_EXCLUDE_CLOSED; + else if (*key == '!' && mode == STDIN_PACKS_MODE_FOLLOW) + kind = STDIN_PACK_EXCLUDE_OPEN; - if (*buf.buf == '^') - string_list_append(&exclude_packs, buf.buf + 1); - else - string_list_append(&include_packs, buf.buf); + if (kind != STDIN_PACK_INCLUDE) + key++; + + info = strmap_get(&packs, key); + if (!info) { + CALLOC_ARRAY(info, 1); + strmap_put(&packs, key, info); + } + + info->kind |= kind; strbuf_reset(&buf); } - string_list_sort_u(&include_packs, 0); - string_list_sort_u(&exclude_packs, 0); - repo_for_each_pack(the_repository, p) { - const char *pack_name = pack_basename(p); + struct stdin_pack_info *info; + + info = strmap_get(&packs, pack_basename(p)); + if (!info) + continue; - if ((item = string_list_lookup(&include_packs, pack_name))) { + if (info->kind & STDIN_PACK_INCLUDE) { if (exclude_promisor_objects && p->pack_promisor) die(_("packfile %s is a promisor but --exclude-promisor-objects was given"), p->pack_name); - item->util = p; - } - if ((item = string_list_lookup(&exclude_packs, pack_name))) - item->util = p; - } - /* - * Arguments we got on stdin may not even be packs. First - * check that to avoid segfaulting later on in - * e.g. pack_mtime_cmp(), excluded packs are handled below. - * - * Since we first parsed our STDIN and then sorted the input - * lines the pack we error on will be whatever line happens to - * sort first. This is lazy, it's enough that we report one - * bad case here, we don't need to report the first/last one, - * or all of them. - */ - for_each_string_list_item(item, &include_packs) { - struct packed_git *p = item->util; - if (!p) - die(_("could not find pack '%s'"), item->string); - if (!is_pack_valid(p)) - die(_("packfile %s cannot be accessed"), p->pack_name); - } + /* + * Arguments we got on stdin may not even be + * packs. First check that to avoid segfaulting + * later on in e.g. pack_mtime_cmp(), excluded + * packs are handled below. + */ + if (!is_pack_valid(p)) + die(_("packfile %s cannot be accessed"), p->pack_name); + } - /* - * Then, handle all of the excluded packs, marking them as - * kept in-core so that later calls to add_object_entry() - * discards any objects that are also found in excluded packs. - */ - for_each_string_list_item(item, &exclude_packs) { - struct packed_git *p = item->util; - if (!p) - die(_("could not find pack '%s'"), item->string); - p->pack_keep_in_core = 1; - } + if (info->kind & STDIN_PACK_EXCLUDE_CLOSED) { + /* + * Marking excluded packs as kept in-core so + * that later calls to add_object_entry() + * discards any objects that are also found in + * excluded packs. + */ + p->pack_keep_in_core = 1; + } - /* - * Order packs by ascending mtime; use QSORT directly to access the - * string_list_item's ->util pointer, which string_list_sort() does not - * provide. - */ - QSORT(include_packs.items, include_packs.nr, pack_mtime_cmp); + if (info->kind & STDIN_PACK_EXCLUDE_OPEN) { + /* + * Marking excluded open packs as kept in-core + * (open) for the same reason as we marked + * exclude closed packs as kept in-core. + * + * Use a separate flag here to ensure we don't + * halt our traversal at these packs, since they + * are not guaranteed to have closure. + * + */ + p->pack_keep_in_core_open = 1; + } - for_each_string_list_item(item, &include_packs) { - struct packed_git *p = item->util; - for_each_object_in_pack(p, - add_object_entry_from_pack, - revs, - ODB_FOR_EACH_OBJECT_PACK_ORDER); + info->p = p; } + stdin_packs_add_pack_entries(&packs, revs); + strbuf_release(&buf); - string_list_clear(&include_packs, 0); - string_list_clear(&exclude_packs, 0); + strmap_clear(&packs, 1); } static void add_unreachable_loose_objects(struct rev_info *revs); @@ -3957,7 +4075,15 @@ static void read_stdin_packs(enum stdin_packs_mode mode, int rev_list_unpacked) /* avoids adding objects in excluded packs */ ignore_packed_keep_in_core = 1; - read_packs_list_from_stdin(&revs); + if (mode == STDIN_PACKS_MODE_FOLLOW) { + /* + * In '--stdin-packs=follow' mode, additionally ignore + * objects in excluded-open packs to prevent them from + * appearing in the resulting pack. + */ + ignore_packed_keep_in_core_open = 1; + } + stdin_packs_read_input(&revs, mode); if (rev_list_unpacked) add_unreachable_loose_objects(&revs); @@ -3968,6 +4094,8 @@ static void read_stdin_packs(enum stdin_packs_mode mode, int rev_list_unpacked) show_object_pack_hint, &mode); + release_revisions(&revs); + trace2_data_intmax("pack-objects", the_repository, "stdin_packs_found", stdin_packs_found_nr); trace2_data_intmax("pack-objects", the_repository, "stdin_packs_hints", @@ -4344,6 +4472,12 @@ static void add_objects_in_unpacked_packs(void) { struct odb_source *source; time_t mtime; + struct odb_for_each_object_options opts = { + .flags = ODB_FOR_EACH_OBJECT_PACK_ORDER | + ODB_FOR_EACH_OBJECT_LOCAL_ONLY | + ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS | + ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS, + }; struct object_info oi = { .mtimep = &mtime, }; @@ -4356,11 +4490,7 @@ static void add_objects_in_unpacked_packs(void) continue; if (packfile_store_for_each_object(files->packed, &oi, - add_object_in_unpacked_pack, NULL, - ODB_FOR_EACH_OBJECT_PACK_ORDER | - ODB_FOR_EACH_OBJECT_LOCAL_ONLY | - ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS | - ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS)) + add_object_in_unpacked_pack, NULL, &opts)) die(_("cannot open pack index")); } } diff --git a/builtin/pull.c b/builtin/pull.c index 6ad420ce6f..7e67fdce97 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -454,7 +454,7 @@ static int run_fetch(const char *repo, const char **refspecs) } else if (*refspecs) BUG("refspecs without repo?"); cmd.git_cmd = 1; - cmd.close_object_store = 1; + cmd.odb_to_close = the_repository->objects; return run_command(&cmd); } diff --git a/builtin/rebase.c b/builtin/rebase.c index c487e10907..fa4f5d9306 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -36,6 +36,7 @@ #include "reset.h" #include "trace2.h" #include "hook.h" +#include "trailer.h" static char const * const builtin_rebase_usage[] = { N_("git rebase [-i] [options] [--exec <cmd>] " @@ -113,6 +114,7 @@ struct rebase_options { enum action action; char *reflog_action; int signoff; + struct strvec trailer_args; int allow_rerere_autoupdate; int keep_empty; int autosquash; @@ -143,6 +145,7 @@ struct rebase_options { .flags = REBASE_NO_QUIET, \ .git_am_opts = STRVEC_INIT, \ .exec = STRING_LIST_INIT_NODUP, \ + .trailer_args = STRVEC_INIT, \ .git_format_patch_opt = STRBUF_INIT, \ .fork_point = -1, \ .reapply_cherry_picks = -1, \ @@ -166,6 +169,7 @@ static void rebase_options_release(struct rebase_options *opts) free(opts->strategy); string_list_clear(&opts->strategy_opts, 0); strbuf_release(&opts->git_format_patch_opt); + strvec_clear(&opts->trailer_args); } static struct replay_opts get_replay_opts(const struct rebase_options *opts) @@ -177,6 +181,9 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts) sequencer_init_config(&replay); replay.signoff = opts->signoff; + + strvec_pushv(&replay.trailer_args, opts->trailer_args.v); + replay.allow_ff = !(opts->flags & REBASE_FORCE); if (opts->allow_rerere_autoupdate) replay.allow_rerere_auto = opts->allow_rerere_autoupdate; @@ -562,7 +569,9 @@ static int finish_rebase(struct rebase_options *opts) * We ignore errors in 'git maintenance run --auto', since the * user should see them. */ - run_auto_maintenance(!(opts->flags & (REBASE_NO_QUIET|REBASE_VERBOSE))); + run_auto_maintenance(the_repository, + !(opts->flags & (REBASE_NO_QUIET|REBASE_VERBOSE))); + if (opts->type == REBASE_MERGE) { struct replay_opts replay = REPLAY_OPTS_INIT; @@ -1132,6 +1141,8 @@ int cmd_rebase(int argc, .flags = PARSE_OPT_NOARG, .defval = REBASE_DIFFSTAT, }, + OPT_STRVEC(0, "trailer", &options.trailer_args, N_("trailer"), + N_("add custom trailer(s)")), OPT_BOOL(0, "signoff", &options.signoff, N_("add a Signed-off-by trailer to each commit")), OPT_BOOL(0, "committer-date-is-author-date", @@ -1285,6 +1296,12 @@ int cmd_rebase(int argc, builtin_rebase_options, builtin_rebase_usage, 0); + if (options.trailer_args.nr) { + if (validate_trailer_args(&options.trailer_args)) + die(NULL); + options.flags |= REBASE_FORCE; + } + if (preserve_merges_selected) die(_("--preserve-merges was replaced by --rebase-merges\n" "Note: Your `pull.rebase` configuration may also be set to 'preserve',\n" @@ -1542,6 +1559,9 @@ int cmd_rebase(int argc, if (options.root && !options.onto_name) imply_merge(&options, "--root without --onto"); + if (options.trailer_args.nr) + imply_merge(&options, "--trailer"); + if (isatty(2) && options.flags & REBASE_NO_QUIET) strbuf_addstr(&options.git_format_patch_opt, " --progress"); diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index d6225df890..cb3656a034 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -3,46 +3,45 @@ #include "builtin.h" #include "abspath.h" - +#include "commit.h" +#include "commit-reach.h" #include "config.h" +#include "connect.h" +#include "connected.h" #include "environment.h" +#include "exec-cmd.h" +#include "fsck.h" #include "gettext.h" +#include "gpg-interface.h" #include "hex.h" -#include "lockfile.h" -#include "pack.h" -#include "refs.h" -#include "pkt-line.h" -#include "sideband.h" -#include "run-command.h" #include "hook.h" -#include "exec-cmd.h" -#include "commit.h" +#include "lockfile.h" #include "object.h" -#include "remote.h" -#include "connect.h" -#include "string-list.h" -#include "oid-array.h" -#include "connected.h" -#include "strvec.h" -#include "version.h" -#include "gpg-interface.h" -#include "sigchain.h" -#include "fsck.h" -#include "tmp-objdir.h" -#include "oidset.h" -#include "packfile.h" #include "object-file.h" #include "object-name.h" #include "odb.h" +#include "oid-array.h" +#include "oidset.h" +#include "pack.h" +#include "packfile.h" +#include "parse-options.h" +#include "pkt-line.h" #include "protocol.h" -#include "commit-reach.h" +#include "refs.h" +#include "remote.h" +#include "run-command.h" #include "server-info.h" +#include "setup.h" +#include "shallow.h" +#include "sideband.h" +#include "sigchain.h" +#include "string-list.h" +#include "strvec.h" +#include "tmp-objdir.h" #include "trace.h" #include "trace2.h" +#include "version.h" #include "worktree.h" -#include "shallow.h" -#include "setup.h" -#include "parse-options.h" static const char * const receive_pack_usage[] = { N_("git receive-pack <git-dir>"), @@ -904,11 +903,14 @@ static int feed_receive_hook_cb(int hook_stdin_fd, void *pp_cb UNUSED, void *pp_ static void *receive_hook_feed_state_alloc(void *feed_pipe_ctx) { struct receive_hook_feed_state *init_state = feed_pipe_ctx; - struct receive_hook_feed_state *data = xcalloc(1, sizeof(*data)); + struct receive_hook_feed_state *data; + + CALLOC_ARRAY(data, 1); data->report = init_state->report; data->cmd = init_state->cmd; data->skip_broken = init_state->skip_broken; strbuf_init(&data->buf, 0); + return data; } @@ -928,7 +930,11 @@ static int run_receive_hook(struct command *commands, { struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT; struct command *iter = commands; - struct receive_hook_feed_state feed_init_state = { 0 }; + struct receive_hook_feed_state feed_init_state = { + .cmd = commands, + .skip_broken = skip_broken, + .buf = STRBUF_INIT, + }; struct async sideband_async; int sideband_async_started = 0; int saved_stderr = -1; @@ -961,8 +967,6 @@ static int run_receive_hook(struct command *commands, prepare_sideband_async(&sideband_async, &saved_stderr, &sideband_async_started); /* set up stdin callback */ - feed_init_state.cmd = commands; - feed_init_state.skip_broken = skip_broken; opt.feed_pipe_ctx = &feed_init_state; opt.feed_pipe = feed_receive_hook_cb; opt.feed_pipe_cb_data_alloc = receive_hook_feed_state_alloc; @@ -2727,7 +2731,7 @@ int cmd_receive_pack(int argc, if (auto_gc) { struct child_process proc = CHILD_PROCESS_INIT; - if (prepare_auto_maintenance(1, &proc)) { + if (prepare_auto_maintenance(the_repository, 1, &proc)) { proc.no_stdin = 1; proc.stdout_to_stderr = 1; proc.err = use_sideband ? -1 : 0; diff --git a/builtin/refs.c b/builtin/refs.c index 3064f888b2..e3125bc61b 100644 --- a/builtin/refs.c +++ b/builtin/refs.c @@ -78,9 +78,9 @@ out: } static int cmd_refs_verify(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { - struct fsck_options fsck_refs_options = FSCK_REFS_OPTIONS_DEFAULT; + struct fsck_options fsck_refs_options; struct worktree **worktrees; const char * const verify_usage[] = { REFS_VERIFY_USAGE, @@ -93,6 +93,8 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix, }; int ret = 0; + fsck_options_init(&fsck_refs_options, repo, FSCK_OPTIONS_REFS); + argc = parse_options(argc, argv, prefix, options, verify_usage, 0); if (argc) usage(_("'git refs verify' takes no arguments")); diff --git a/builtin/repack.c b/builtin/repack.c index f6bb04bef7..4c5a82c2c8 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -369,8 +369,23 @@ int cmd_repack(int argc, */ for (i = 0; i < geometry.split; i++) fprintf(in, "%s\n", pack_basename(geometry.pack[i])); - for (i = geometry.split; i < geometry.pack_nr; i++) - fprintf(in, "^%s\n", pack_basename(geometry.pack[i])); + for (i = geometry.split; i < geometry.pack_nr; i++) { + const char *basename = pack_basename(geometry.pack[i]); + char marker = '^'; + + if (!midx_must_contain_cruft && + !string_list_has_string(&existing.midx_packs, + basename)) { + /* + * Assume non-MIDX'd packs are not + * necessarily closed under + * reachability. + */ + marker = '!'; + } + + fprintf(in, "%c%s\n", marker, basename); + } fclose(in); } diff --git a/builtin/replay.c b/builtin/replay.c index 2cdde830a8..a0879b020f 100644 --- a/builtin/replay.c +++ b/builtin/replay.c @@ -79,11 +79,12 @@ int cmd_replay(int argc, struct ref_transaction *transaction = NULL; struct strbuf transaction_err = STRBUF_INIT; struct strbuf reflog_msg = STRBUF_INIT; + int desired_reverse; int ret = 0; const char *const replay_usage[] = { N_("(EXPERIMENTAL!) git replay " - "([--contained] --onto <newbase> | --advance <branch>) " + "([--contained] --onto <newbase> | --advance <branch> | --revert <branch>) " "[--ref-action[=<mode>]] <revision-range>"), NULL }; @@ -96,6 +97,9 @@ int cmd_replay(int argc, N_("replay onto given commit")), OPT_BOOL(0, "contained", &opts.contained, N_("update all branches that point at commits in <revision-range>")), + OPT_STRING(0, "revert", &opts.revert, + N_("branch"), + N_("revert commits onto given branch")), OPT_STRING(0, "ref-action", &ref_action, N_("mode"), N_("control ref update behavior (update|print)")), @@ -105,19 +109,31 @@ int cmd_replay(int argc, argc = parse_options(argc, argv, prefix, replay_options, replay_usage, PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT); - if (!opts.onto && !opts.advance) { - error(_("option --onto or --advance is mandatory")); + /* Exactly one mode must be specified */ + if (!opts.onto && !opts.advance && !opts.revert) { + error(_("exactly one of --onto, --advance, or --revert is required")); usage_with_options(replay_usage, replay_options); } + die_for_incompatible_opt3(!!opts.onto, "--onto", + !!opts.advance, "--advance", + !!opts.revert, "--revert"); die_for_incompatible_opt2(!!opts.advance, "--advance", opts.contained, "--contained"); - die_for_incompatible_opt2(!!opts.advance, "--advance", - !!opts.onto, "--onto"); + die_for_incompatible_opt2(!!opts.revert, "--revert", + opts.contained, "--contained"); /* Parse ref action mode from command line or config */ ref_mode = get_ref_action_mode(repo, ref_action); + /* + * Cherry-pick/rebase need oldest-first ordering so that each + * replayed commit can build on its already-replayed parent. + * Revert needs newest-first ordering (like git revert) to + * reduce conflicts by peeling off changes from the top. + */ + desired_reverse = !opts.revert; + repo_init_revisions(repo, &revs, prefix); /* @@ -129,7 +145,7 @@ int cmd_replay(int argc, * some options changing these values if we think they could * be useful. */ - revs.reverse = 1; + revs.reverse = desired_reverse; revs.sort_order = REV_SORT_IN_GRAPH_ORDER; revs.topo_order = 1; revs.simplify_history = 0; @@ -144,11 +160,11 @@ int cmd_replay(int argc, * Detect and warn if we override some user specified rev * walking options. */ - if (revs.reverse != 1) { + if (revs.reverse != desired_reverse) { warning(_("some rev walking options will be overridden as " "'%s' bit in 'struct rev_info' will be forced"), "reverse"); - revs.reverse = 1; + revs.reverse = desired_reverse; } if (revs.sort_order != REV_SORT_IN_GRAPH_ORDER) { warning(_("some rev walking options will be overridden as " @@ -174,7 +190,9 @@ int cmd_replay(int argc, goto cleanup; /* Build reflog message */ - if (opts.advance) { + if (opts.revert) { + strbuf_addf(&reflog_msg, "replay --revert %s", opts.revert); + } else if (opts.advance) { strbuf_addf(&reflog_msg, "replay --advance %s", opts.advance); } else { struct object_id oid; diff --git a/builtin/repo.c b/builtin/repo.c index 55f9b9095c..71a5c1c29c 100644 --- a/builtin/repo.c +++ b/builtin/repo.c @@ -20,11 +20,27 @@ #include "tree-walk.h" #include "utf8.h" +#define REPO_INFO_USAGE \ + "git repo info [--format=(lines|nul) | -z] [--all | <key>...]", \ + "git repo info --keys [--format=(lines|nul) | -z]" + +#define REPO_STRUCTURE_USAGE \ + "git repo structure [--format=(table|lines|nul) | -z]" + static const char *const repo_usage[] = { - "git repo info [--format=(lines|nul) | -z] [--all | <key>...]", - "git repo info --keys [--format=(lines|nul) | -z]", - "git repo structure [--format=(table|lines|nul) | -z]", - NULL + REPO_INFO_USAGE, + REPO_STRUCTURE_USAGE, + NULL, +}; + +static const char *const repo_info_usage[] = { + REPO_INFO_USAGE, + NULL, +}; + +static const char *const repo_structure_usage[] = { + REPO_STRUCTURE_USAGE, + NULL, }; typedef int get_value_fn(struct repository *repo, struct strbuf *buf); @@ -214,7 +230,7 @@ static int cmd_repo_info(int argc, const char **argv, const char *prefix, OPT_END() }; - argc = parse_options(argc, argv, prefix, options, repo_usage, 0); + argc = parse_options(argc, argv, prefix, options, repo_info_usage, 0); if (show_keys && (all_keys || argc)) die(_("--keys cannot be used with a <key> or --all")); @@ -879,7 +895,7 @@ static int cmd_repo_structure(int argc, const char **argv, const char *prefix, OPT_END() }; - argc = parse_options(argc, argv, prefix, options, repo_usage, 0); + argc = parse_options(argc, argv, prefix, options, repo_structure_usage, 0); if (argc) usage(_("too many arguments")); diff --git a/builtin/reset.c b/builtin/reset.c index 88f95f9fc7..3590be57a5 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -346,7 +346,7 @@ int cmd_reset(int argc, struct object_id oid; struct pathspec pathspec; int intent_to_add = 0; - struct add_p_opt add_p_opt = ADD_P_OPT_INIT; + struct interactive_options interactive_opts = INTERACTIVE_OPTIONS_INIT; const struct option options[] = { OPT__QUIET(&quiet, N_("be quiet, only report errors")), OPT_BOOL(0, "no-refresh", &no_refresh, @@ -371,10 +371,10 @@ int cmd_reset(int argc, PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater), OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), - OPT_BOOL(0, "auto-advance", &add_p_opt.auto_advance, + OPT_BOOL(0, "auto-advance", &interactive_opts.auto_advance, N_("auto advance to the next file when selecting hunks interactively")), - OPT_DIFF_UNIFIED(&add_p_opt.context), - OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), + OPT_DIFF_UNIFIED(&interactive_opts.context), + OPT_DIFF_INTERHUNK_CONTEXT(&interactive_opts.interhunkcontext), OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that removed paths will be added later")), OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), @@ -425,9 +425,9 @@ int cmd_reset(int argc, oidcpy(&oid, &tree->object.oid); } - if (add_p_opt.context < -1) + if (interactive_opts.context < -1) die(_("'%s' cannot be negative"), "--unified"); - if (add_p_opt.interhunkcontext < -1) + if (interactive_opts.interhunkcontext < -1) die(_("'%s' cannot be negative"), "--inter-hunk-context"); prepare_repo_settings(the_repository); @@ -438,14 +438,14 @@ int cmd_reset(int argc, die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}"); trace2_cmd_mode("patch-interactive"); update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, - &add_p_opt, rev, &pathspec); + &interactive_opts, rev, &pathspec, 0); goto cleanup; } else { - if (add_p_opt.context != -1) + if (interactive_opts.context != -1) die(_("the option '%s' requires '%s'"), "--unified", "--patch"); - if (add_p_opt.interhunkcontext != -1) + if (interactive_opts.interhunkcontext != -1) die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); - if (!add_p_opt.auto_advance) + if (!interactive_opts.auto_advance) die(_("the option '%s' requires '%s'"), "--no-auto-advance", "--patch"); } diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 01a62800e8..218b5f34d6 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -267,21 +267,20 @@ static int show_file(const char *arg, int output_prefix) static int try_difference(const char *arg) { - char *dotdot; + const char *dotdot; struct object_id start_oid; struct object_id end_oid; const char *end; const char *start; + char *to_free; int symmetric; static const char head_by_default[] = "HEAD"; if (!(dotdot = strstr(arg, ".."))) return 0; + start = to_free = xmemdupz(arg, dotdot - arg); end = dotdot + 2; - start = arg; symmetric = (*end == '.'); - - *dotdot = 0; end += symmetric; if (!*end) @@ -295,7 +294,7 @@ static int try_difference(const char *arg) * Just ".."? That is not a range but the * pathspec for the parent directory. */ - *dotdot = '.'; + free(to_free); return 0; } @@ -308,7 +307,7 @@ static int try_difference(const char *arg) a = lookup_commit_reference(the_repository, &start_oid); b = lookup_commit_reference(the_repository, &end_oid); if (!a || !b) { - *dotdot = '.'; + free(to_free); return 0; } if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0) @@ -318,16 +317,16 @@ static int try_difference(const char *arg) show_rev(REVERSED, &commit->object.oid, NULL); } } - *dotdot = '.'; + free(to_free); return 1; } - *dotdot = '.'; + free(to_free); return 0; } static int try_parent_shorthands(const char *arg) { - char *dotdot; + const char *mark; struct object_id oid; struct commit *commit; struct commit_list *parents; @@ -335,38 +334,39 @@ static int try_parent_shorthands(const char *arg) int include_rev = 0; int include_parents = 0; int exclude_parent = 0; + char *to_free; - if ((dotdot = strstr(arg, "^!"))) { + if ((mark = strstr(arg, "^!"))) { include_rev = 1; - if (dotdot[2]) + if (mark[2]) return 0; - } else if ((dotdot = strstr(arg, "^@"))) { + } else if ((mark = strstr(arg, "^@"))) { include_parents = 1; - if (dotdot[2]) + if (mark[2]) return 0; - } else if ((dotdot = strstr(arg, "^-"))) { + } else if ((mark = strstr(arg, "^-"))) { include_rev = 1; exclude_parent = 1; - if (dotdot[2]) { + if (mark[2]) { char *end; - exclude_parent = strtoul(dotdot + 2, &end, 10); + exclude_parent = strtoul(mark + 2, &end, 10); if (*end != '\0' || !exclude_parent) return 0; } } else return 0; - *dotdot = 0; + arg = to_free = xmemdupz(arg, mark - arg); if (repo_get_oid_committish(the_repository, arg, &oid) || !(commit = lookup_commit_reference(the_repository, &oid))) { - *dotdot = '^'; + free(to_free); return 0; } if (exclude_parent && exclude_parent > commit_list_count(commit->parents)) { - *dotdot = '^'; + free(to_free); return 0; } @@ -387,7 +387,7 @@ static int try_parent_shorthands(const char *arg) free(name); } - *dotdot = '^'; + free(to_free); return 1; } diff --git a/builtin/stash.c b/builtin/stash.c index e79d612e57..0d27b2fb1f 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -50,10 +50,10 @@ #define BUILTIN_STASH_STORE_USAGE \ N_("git stash store [(-m | --message) <message>] [-q | --quiet] <commit>") #define BUILTIN_STASH_PUSH_USAGE \ - N_("git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \ + N_("git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \ " [-u | --include-untracked] [-a | --all] [(-m | --message) <message>]\n" \ " [--pathspec-from-file=<file> [--pathspec-file-nul]]\n" \ - " [--] [<pathspec>...]]") + " [--] [<pathspec>...]") #define BUILTIN_STASH_SAVE_USAGE \ N_("git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]\n" \ " [-u | --include-untracked] [-a | --all] [<message>]") @@ -1232,7 +1232,7 @@ static int check_changes(const struct pathspec *ps, int include_untracked, } static int save_untracked_files(struct stash_info *info, struct strbuf *msg, - struct strbuf files) + struct strbuf *files) { int ret = 0; struct strbuf untracked_msg = STRBUF_INIT; @@ -1246,7 +1246,7 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg, stash_index_path.buf); strbuf_addf(&untracked_msg, "untracked files on %s\n", msg->buf); - if (pipe_command(&cp_upd_index, files.buf, files.len, NULL, 0, + if (pipe_command(&cp_upd_index, files->buf, files->len, NULL, 0, NULL, 0)) { ret = -1; goto done; @@ -1306,7 +1306,7 @@ done: static int stash_patch(struct stash_info *info, const struct pathspec *ps, struct strbuf *out_patch, int quiet, - struct add_p_opt *add_p_opt) + struct interactive_options *interactive_opts) { int ret = 0; struct child_process cp_read_tree = CHILD_PROCESS_INIT; @@ -1331,7 +1331,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps, old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); - ret = !!run_add_p(the_repository, ADD_P_STASH, add_p_opt, NULL, ps); + ret = !!run_add_p(the_repository, ADD_P_STASH, interactive_opts, NULL, ps, 0); the_repository->index_file = old_repo_index_file; if (old_index_env && *old_index_env) @@ -1427,7 +1427,8 @@ done: } static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf, - int include_untracked, int patch_mode, struct add_p_opt *add_p_opt, + int include_untracked, int patch_mode, + struct interactive_options *interactive_opts, int only_staged, struct stash_info *info, struct strbuf *patch, int quiet) { @@ -1499,7 +1500,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b parents = NULL; if (include_untracked) { - if (save_untracked_files(info, &msg, untracked_files)) { + if (save_untracked_files(info, &msg, &untracked_files)) { if (!quiet) fprintf_ln(stderr, _("Cannot save " "the untracked files")); @@ -1509,7 +1510,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b untracked_commit_option = 1; } if (patch_mode) { - ret = stash_patch(info, ps, patch, quiet, add_p_opt); + ret = stash_patch(info, ps, patch, quiet, interactive_opts); if (ret < 0) { if (!quiet) fprintf_ln(stderr, _("Cannot save the current " @@ -1595,7 +1596,8 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED, } static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet, - int keep_index, int patch_mode, struct add_p_opt *add_p_opt, + int keep_index, int patch_mode, + struct interactive_options *interactive_opts, int include_untracked, int only_staged) { int ret = 0; @@ -1667,7 +1669,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q if (stash_msg) strbuf_addstr(&stash_msg_buf, stash_msg); if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, - add_p_opt, only_staged, &info, &patch, quiet)) { + interactive_opts, only_staged, &info, &patch, quiet)) { ret = -1; goto done; } @@ -1841,7 +1843,7 @@ static int push_stash(int argc, const char **argv, const char *prefix, const char *stash_msg = NULL; char *pathspec_from_file = NULL; struct pathspec ps; - struct add_p_opt add_p_opt = ADD_P_OPT_INIT; + struct interactive_options interactive_opts = INTERACTIVE_OPTIONS_INIT; struct option options[] = { OPT_BOOL('k', "keep-index", &keep_index, N_("keep index")), @@ -1849,10 +1851,10 @@ static int push_stash(int argc, const char **argv, const char *prefix, N_("stash staged changes only")), OPT_BOOL('p', "patch", &patch_mode, N_("stash in patch mode")), - OPT_BOOL(0, "auto-advance", &add_p_opt.auto_advance, + OPT_BOOL(0, "auto-advance", &interactive_opts.auto_advance, N_("auto advance to the next file when selecting hunks interactively")), - OPT_DIFF_UNIFIED(&add_p_opt.context), - OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), + OPT_DIFF_UNIFIED(&interactive_opts.context), + OPT_DIFF_INTERHUNK_CONTEXT(&interactive_opts.interhunkcontext), OPT__QUIET(&quiet, N_("quiet mode")), OPT_BOOL('u', "include-untracked", &include_untracked, N_("include untracked files in stash")), @@ -1909,21 +1911,21 @@ static int push_stash(int argc, const char **argv, const char *prefix, } if (!patch_mode) { - if (add_p_opt.context != -1) + if (interactive_opts.context != -1) die(_("the option '%s' requires '%s'"), "--unified", "--patch"); - if (add_p_opt.interhunkcontext != -1) + if (interactive_opts.interhunkcontext != -1) die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); - if (!add_p_opt.auto_advance) + if (!interactive_opts.auto_advance) die(_("the option '%s' requires '%s'"), "--no-auto-advance", "--patch"); } - if (add_p_opt.context < -1) + if (interactive_opts.context < -1) die(_("'%s' cannot be negative"), "--unified"); - if (add_p_opt.interhunkcontext < -1) + if (interactive_opts.interhunkcontext < -1) die(_("'%s' cannot be negative"), "--inter-hunk-context"); ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode, - &add_p_opt, include_untracked, only_staged); + &interactive_opts, include_untracked, only_staged); clear_pathspec(&ps); free(pathspec_from_file); @@ -1948,7 +1950,7 @@ static int save_stash(int argc, const char **argv, const char *prefix, const char *stash_msg = NULL; struct pathspec ps; struct strbuf stash_msg_buf = STRBUF_INIT; - struct add_p_opt add_p_opt = ADD_P_OPT_INIT; + struct interactive_options interactive_opts = INTERACTIVE_OPTIONS_INIT; struct option options[] = { OPT_BOOL('k', "keep-index", &keep_index, N_("keep index")), @@ -1956,10 +1958,10 @@ static int save_stash(int argc, const char **argv, const char *prefix, N_("stash staged changes only")), OPT_BOOL('p', "patch", &patch_mode, N_("stash in patch mode")), - OPT_BOOL(0, "auto-advance", &add_p_opt.auto_advance, + OPT_BOOL(0, "auto-advance", &interactive_opts.auto_advance, N_("auto advance to the next file when selecting hunks interactively")), - OPT_DIFF_UNIFIED(&add_p_opt.context), - OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), + OPT_DIFF_UNIFIED(&interactive_opts.context), + OPT_DIFF_INTERHUNK_CONTEXT(&interactive_opts.interhunkcontext), OPT__QUIET(&quiet, N_("quiet mode")), OPT_BOOL('u', "include-untracked", &include_untracked, N_("include untracked files in stash")), @@ -1979,22 +1981,22 @@ static int save_stash(int argc, const char **argv, const char *prefix, memset(&ps, 0, sizeof(ps)); - if (add_p_opt.context < -1) + if (interactive_opts.context < -1) die(_("'%s' cannot be negative"), "--unified"); - if (add_p_opt.interhunkcontext < -1) + if (interactive_opts.interhunkcontext < -1) die(_("'%s' cannot be negative"), "--inter-hunk-context"); if (!patch_mode) { - if (add_p_opt.context != -1) + if (interactive_opts.context != -1) die(_("the option '%s' requires '%s'"), "--unified", "--patch"); - if (add_p_opt.interhunkcontext != -1) + if (interactive_opts.interhunkcontext != -1) die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); - if (!add_p_opt.auto_advance) + if (!interactive_opts.auto_advance) die(_("the option '%s' requires '%s'"), "--no-auto-advance", "--patch"); } ret = do_push_stash(&ps, stash_msg, quiet, keep_index, - patch_mode, &add_p_opt, include_untracked, + patch_mode, &interactive_opts, include_untracked, only_staged); strbuf_release(&stash_msg_buf); diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 143f7cb3cc..2f589e3b37 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -29,6 +29,7 @@ #include "object-file.h" #include "object-name.h" #include "odb.h" +#include "odb/source.h" #include "advice.h" #include "branch.h" #include "list-objects-filter-options.h" @@ -113,6 +114,43 @@ static int get_default_remote_submodule(const char *module_path, char **default_ return 0; } +static int module_get_default_remote(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) +{ + const char *path; + char *resolved_path = NULL; + char *default_remote = NULL; + int code; + struct option options[] = { + OPT_END() + }; + const char *const usage[] = { + N_("git submodule--helper get-default-remote <path>"), + NULL + }; + + argc = parse_options(argc, argv, prefix, options, usage, 0); + if (argc != 1) + usage_with_options(usage, options); + + path = argv[0]; + if (prefix && *prefix && !is_absolute_path(path)) { + resolved_path = xstrfmt("%s%s", prefix, path); + path = resolved_path; + } + + code = get_default_remote_submodule(path, &default_remote); + if (code) { + free(resolved_path); + return code; + } + + printf("%s\n", default_remote); + free(default_remote); + free(resolved_path); + return 0; +} + /* the result should be freed by the caller. */ static char *get_submodule_displaypath(const char *path, const char *prefix, const char *super_prefix) @@ -1160,7 +1198,7 @@ static void submodule_summary_callback(struct diff_queue_struct *q, if (!S_ISGITLINK(p->one->mode) && !S_ISGITLINK(p->two->mode)) continue; - temp = (struct module_cb*)malloc(sizeof(struct module_cb)); + temp = xmalloc(sizeof(*temp)); temp->mod_src = p->one->mode; temp->mod_dst = p->two->mode; temp->oid_src = p->one->oid; @@ -3789,6 +3827,7 @@ int cmd_submodule__helper(int argc, OPT_SUBCOMMAND("set-url", &fn, module_set_url), OPT_SUBCOMMAND("set-branch", &fn, module_set_branch), OPT_SUBCOMMAND("create-branch", &fn, module_create_branch), + OPT_SUBCOMMAND("get-default-remote", &fn, module_get_default_remote), OPT_END() }; argc = parse_options(argc, argv, prefix, options, usage, 0); diff --git a/builtin/tag.c b/builtin/tag.c index aeb04c487f..d51c2e3349 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -167,7 +167,7 @@ static int do_sign(struct strbuf *buffer, struct object_id **compat_oid, char *keyid = get_signing_key(); int ret = -1; - if (sign_buffer(buffer, &sig, keyid)) + if (sign_buffer(buffer, &sig, keyid, 0)) goto out; if (compat) { @@ -176,7 +176,7 @@ static int do_sign(struct strbuf *buffer, struct object_id **compat_oid, if (convert_object_file(the_repository ,&compat_buf, algo, compat, buffer->buf, buffer->len, OBJ_TAG, 1)) goto out; - if (sign_buffer(&compat_buf, &compat_sig, keyid)) + if (sign_buffer(&compat_buf, &compat_sig, keyid, 0)) goto out; add_header_signature(&compat_buf, &sig, algo); strbuf_addbuf(&compat_buf, &compat_sig); @@ -499,8 +499,8 @@ int cmd_tag(int argc, OPT_CALLBACK_F('m', "message", &msg, N_("message"), N_("tag message"), PARSE_OPT_NONEG, parse_msg_arg), OPT_FILENAME('F', "file", &msgfile, N_("read message from file")), - OPT_PASSTHRU_ARGV(0, "trailer", &trailer_args, N_("trailer"), - N_("add custom trailer(s)"), PARSE_OPT_NONEG), + OPT_STRVEC(0, "trailer", &trailer_args, N_("trailer"), + N_("add custom trailer(s)")), OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")), OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")), OPT_CLEANUP(&cleanup_arg), @@ -568,6 +568,9 @@ int cmd_tag(int argc, if (cmdmode == 'l') setup_auto_pager("tag", 1); + if (trailer_args.nr) + trailer_config_init(); + if (opt.sign == -1) opt.sign = cmdmode ? 0 : config_sign_tag > 0; diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index 6fc64e9e4b..d863912b24 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -29,7 +29,7 @@ static unsigned int offset, len; static off_t consumed_bytes; static off_t max_input_size; static struct git_hash_ctx ctx; -static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT; +static struct fsck_options fsck_options; static struct progress *progress; /* @@ -613,7 +613,7 @@ static void unpack_all(void) int cmd_unpack_objects(int argc, const char **argv, const char *prefix UNUSED, - struct repository *repo UNUSED) + struct repository *repo) { int i; struct object_id oid; @@ -627,6 +627,8 @@ int cmd_unpack_objects(int argc, show_usage_if_asked(argc, argv, unpack_usage); + fsck_options_init(&fsck_options, repo, FSCK_OPTIONS_STRICT); + for (i = 1 ; i < argc; i++) { const char *arg = argv[i]; diff --git a/builtin/worktree.c b/builtin/worktree.c index bc2d0d645b..4fd6f7575f 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -539,7 +539,7 @@ static int add_worktree(const char *path, const char *refname, strbuf_reset(&sb); strbuf_addf(&sb, "%s/gitdir", sb_repo.buf); - write_worktree_linking_files(sb_git, sb, opts->relative_paths); + write_worktree_linking_files(sb_git.buf, sb.buf, opts->relative_paths); strbuf_reset(&sb); strbuf_addf(&sb, "%s/commondir", sb_repo.buf); write_file(sb.buf, "../.."); @@ -692,25 +692,8 @@ static int can_use_local_refs(const struct add_opts *opts) if (refs_head_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) { return 1; } else if (refs_for_each_branch_ref(get_main_ref_store(the_repository), first_valid_ref, NULL)) { - if (!opts->quiet) { - struct strbuf path = STRBUF_INIT; - struct strbuf contents = STRBUF_INIT; - char *wt_gitdir = get_worktree_git_dir(NULL); - - strbuf_add_real_path(&path, wt_gitdir); - strbuf_addstr(&path, "/HEAD"); - strbuf_read_file(&contents, path.buf, 64); - strbuf_stripspace(&contents, NULL); - strbuf_strip_suffix(&contents, "\n"); - - warning(_("HEAD points to an invalid (or orphaned) reference.\n" - "HEAD path: '%s'\n" - "HEAD contents: '%s'"), - path.buf, contents.buf); - strbuf_release(&path); - strbuf_release(&contents); - free(wt_gitdir); - } + if (!opts->quiet) + warning(_("HEAD points to an invalid (or orphaned) reference.\n")); return 1; } return 0; diff --git a/cache-tree.c b/cache-tree.c index 16c3a36b48..60bcc07c3b 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -723,11 +723,11 @@ static int write_index_as_tree_internal(struct object_id *oid, return 0; } -struct tree* write_in_core_index_as_tree(struct repository *repo) { +struct tree *write_in_core_index_as_tree(struct repository *repo, + struct index_state *index_state) { struct object_id o; int was_valid, ret; - struct index_state *index_state = repo->index; was_valid = index_state->cache_tree && cache_tree_fully_valid(index_state->cache_tree); diff --git a/cache-tree.h b/cache-tree.h index b82c4963e7..f8bddae523 100644 --- a/cache-tree.h +++ b/cache-tree.h @@ -47,7 +47,8 @@ int cache_tree_verify(struct repository *, struct index_state *); #define WRITE_TREE_UNMERGED_INDEX (-2) #define WRITE_TREE_PREFIX_ERROR (-3) -struct tree* write_in_core_index_as_tree(struct repository *repo); +struct tree *write_in_core_index_as_tree(struct repository *repo, + struct index_state *index_state); int write_index_as_tree(struct object_id *oid, struct index_state *index_state, const char *index_path, int flags, const char *prefix); void prime_cache_tree(struct repository *, struct index_state *, struct tree *); @@ -96,26 +96,28 @@ struct cb_node *cb_lookup(struct cb_tree *t, const uint8_t *k, size_t klen) return p && !memcmp(p->k, k, klen) ? p : NULL; } -static enum cb_next cb_descend(struct cb_node *p, cb_iter fn, void *arg) +static int cb_descend(struct cb_node *p, cb_iter fn, void *arg) { if (1 & (uintptr_t)p) { struct cb_node *q = cb_node_of(p); - enum cb_next n = cb_descend(q->child[0], fn, arg); - - return n == CB_BREAK ? n : cb_descend(q->child[1], fn, arg); + int ret = cb_descend(q->child[0], fn, arg); + if (ret) + return ret; + return cb_descend(q->child[1], fn, arg); } else { return fn(p, arg); } } -void cb_each(struct cb_tree *t, const uint8_t *kpfx, size_t klen, - cb_iter fn, void *arg) +int cb_each(struct cb_tree *t, const uint8_t *kpfx, size_t klen, + cb_iter fn, void *arg) { struct cb_node *p = t->root; struct cb_node *top = p; size_t i = 0; - if (!p) return; /* empty tree */ + if (!p) + return 0; /* empty tree */ /* Walk tree, maintaining top pointer */ while (1 & (uintptr_t)p) { @@ -130,7 +132,8 @@ void cb_each(struct cb_tree *t, const uint8_t *kpfx, size_t klen, for (i = 0; i < klen; i++) { if (p->k[i] != kpfx[i]) - return; /* "best" match failed */ + return 0; /* "best" match failed */ } - cb_descend(top, fn, arg); + + return cb_descend(top, fn, arg); } @@ -30,11 +30,6 @@ struct cb_tree { struct cb_node *root; }; -enum cb_next { - CB_CONTINUE = 0, - CB_BREAK = 1 -}; - #define CBTREE_INIT { 0 } static inline void cb_init(struct cb_tree *t) @@ -46,9 +41,15 @@ static inline void cb_init(struct cb_tree *t) struct cb_node *cb_lookup(struct cb_tree *, const uint8_t *k, size_t klen); struct cb_node *cb_insert(struct cb_tree *, struct cb_node *, size_t klen); -typedef enum cb_next (*cb_iter)(struct cb_node *, void *arg); +/* + * Callback invoked by `cb_each()` for each node in the critbit tree. A return + * value of 0 will cause the iteration to continue, a non-zero return code will + * cause iteration to abort. The error code will be relayed back from + * `cb_each()` in that case. + */ +typedef int (*cb_iter)(struct cb_node *, void *arg); -void cb_each(struct cb_tree *, const uint8_t *kpfx, size_t klen, - cb_iter, void *arg); +int cb_each(struct cb_tree *, const uint8_t *kpfx, size_t klen, + cb_iter, void *arg); #endif /* CBTREE_H */ diff --git a/ci/run-static-analysis.sh b/ci/run-static-analysis.sh index 9e9c72681d..ba67e80b4d 100755 --- a/ci/run-static-analysis.sh +++ b/ci/run-static-analysis.sh @@ -10,7 +10,7 @@ make coccicheck set +x fail= -for cocci_patch in contrib/coccinelle/*.patch +for cocci_patch in tools/coccinelle/*.patch do if test -s "$cocci_patch" then diff --git a/commit-graph.c b/commit-graph.c index f8e24145a5..9abe62bd5a 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -1319,6 +1319,37 @@ static int write_graph_chunk_data(struct hashfile *f, return 0; } +/* + * Compute the generation offset between the commit date and its generation. + * This is what's ultimately stored as generation number in the commit graph. + * + * Note that the computation of the commit date is more involved than you might + * think. Instead of using the full commit date, we're in fact masking bits so + * that only the 34 lowest bits are considered. This results from the fact that + * commit graphs themselves only ever store 34 bits of the commit date + * themselves. + * + * This means that if we have a commit date that exceeds 34 bits we'll end up + * in situations where depending on whether the commit has been parsed from the + * object database or the commit graph we'll have different dates, where the + * ones parsed from the object database would have full 64 bit precision. + * + * But ultimately, we only ever want the offset to be relative to what we + * actually end up storing on disk, and hence we have to mask all the other + * bits. + */ +static timestamp_t compute_generation_offset(struct commit *c) +{ + timestamp_t masked_date; + + if (sizeof(timestamp_t) > 4) + masked_date = c->date & (((timestamp_t) 1 << 34) - 1); + else + masked_date = c->date; + + return commit_graph_data_at(c)->generation - masked_date; +} + static int write_graph_chunk_generation_data(struct hashfile *f, void *data) { @@ -1329,7 +1360,7 @@ static int write_graph_chunk_generation_data(struct hashfile *f, struct commit *c = ctx->commits.items[i]; timestamp_t offset; repo_parse_commit(ctx->r, c); - offset = commit_graph_data_at(c)->generation - c->date; + offset = compute_generation_offset(c); display_progress(ctx->progress, ++ctx->progress_cnt); if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) { @@ -1350,7 +1381,7 @@ static int write_graph_chunk_generation_data_overflow(struct hashfile *f, int i; for (i = 0; i < ctx->commits.nr; i++) { struct commit *c = ctx->commits.items[i]; - timestamp_t offset = commit_graph_data_at(c)->generation - c->date; + timestamp_t offset = compute_generation_offset(c); display_progress(ctx->progress, ++ctx->progress_cnt); if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) { @@ -1741,7 +1772,7 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx) for (i = 0; i < ctx->commits.nr; i++) { struct commit *c = ctx->commits.items[i]; - timestamp_t offset = commit_graph_data_at(c)->generation - c->date; + timestamp_t offset = compute_generation_offset(c); if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) ctx->num_generation_data_overflows++; } @@ -1969,6 +2000,9 @@ static void fill_oids_from_all_packs(struct write_commit_graph_context *ctx) { struct odb_source *source; enum object_type type; + struct odb_for_each_object_options opts = { + .flags = ODB_FOR_EACH_OBJECT_PACK_ORDER, + }; struct object_info oi = { .typep = &type, }; @@ -1983,7 +2017,7 @@ static void fill_oids_from_all_packs(struct write_commit_graph_context *ctx) for (source = ctx->r->objects->sources; source; source = source->next) { struct odb_source_files *files = odb_source_files_downcast(source); packfile_store_for_each_object(files->packed, &oi, add_packed_commits_oi, - ctx, ODB_FOR_EACH_OBJECT_PACK_ORDER); + ctx, &opts); } if (ctx->progress_done < ctx->approx_nr_objects) @@ -2607,7 +2641,8 @@ int write_commit_graph(struct odb_source *source, replace = ctx.opts->split_flags & COMMIT_GRAPH_SPLIT_REPLACE; } - ctx.approx_nr_objects = repo_approximate_object_count(r); + if (odb_count_objects(r->objects, ODB_COUNT_OBJECTS_APPROXIMATE, &ctx.approx_nr_objects) < 0) + ctx.approx_nr_objects = 0; if (ctx.append && g) { for (i = 0; i < g->num_commits; i++) { diff --git a/commit-reach.c b/commit-reach.c index 9604bbdcce..d3a9b3ed6f 100644 --- a/commit-reach.c +++ b/commit-reach.c @@ -1117,10 +1117,8 @@ void ahead_behind(struct repository *r, /* STALE is used here, PARENT2 is used by insert_no_dup(). */ repo_clear_commit_marks(r, PARENT2 | STALE); - while (prio_queue_peek(&queue)) { - struct commit *c = prio_queue_get(&queue); - free_bit_array(c); - } + for (size_t i = 0; i < queue.nr; i++) + free_bit_array(queue.array[i].data); clear_bit_arrays(&bit_arrays); clear_prio_queue(&queue); } @@ -1170,18 +1170,6 @@ int add_header_signature(struct strbuf *buf, struct strbuf *sig, const struct gi return 0; } -static int sign_commit_to_strbuf(struct strbuf *sig, struct strbuf *buf, const char *keyid) -{ - char *keyid_to_free = NULL; - int ret = 0; - if (!keyid || !*keyid) - keyid = keyid_to_free = get_signing_key(); - if (sign_buffer(buf, sig, keyid)) - ret = -1; - free(keyid_to_free); - return ret; -} - int parse_signed_commit(const struct commit *commit, struct strbuf *payload, struct strbuf *signature, const struct git_hash_algo *algop) @@ -1759,7 +1747,8 @@ int commit_tree_extended(const char *msg, size_t msg_len, oidcpy(&parent_buf[i++], &p->item->object.oid); write_commit_tree(&buffer, msg, msg_len, tree, parent_buf, nparents, author, committer, extra); - if (sign_commit && sign_commit_to_strbuf(&sig, &buffer, sign_commit)) { + if (sign_commit && sign_buffer(&buffer, &sig, sign_commit, + SIGN_BUFFER_USE_DEFAULT_KEY)) { result = -1; goto out; } @@ -1791,7 +1780,9 @@ int commit_tree_extended(const char *msg, size_t msg_len, free_commit_extra_headers(compat_extra); free(mapped_parents); - if (sign_commit && sign_commit_to_strbuf(&compat_sig, &compat_buffer, sign_commit)) { + if (sign_commit && sign_buffer(&compat_buffer, &compat_sig, + sign_commit, + SIGN_BUFFER_USE_DEFAULT_KEY)) { result = -1; goto out; } @@ -287,7 +287,7 @@ int for_each_commit_graft(each_commit_graft_fn, void *); int interactive_add(struct repository *repo, const char **argv, const char *prefix, - int patch, struct add_p_opt *add_p_opt); + int patch, struct interactive_options *opts); struct commit_extra_header { struct commit_extra_header *next; @@ -400,8 +400,6 @@ LAST_ARG_MUST_BE_NULL int run_commit_hook(int editor_is_used, const char *index_file, int *invoked_hook, const char *name, ...); -/* Sign a commit or tag buffer, storing the result in a header. */ -int sign_with_header(struct strbuf *buf, const char *keyid); /* Parse the signature out of a header. */ int parse_buffer_signed_by_header(const char *buffer, unsigned long size, diff --git a/compat/mingw.c b/compat/mingw.c index c667a2dcda..2023c16db6 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1394,6 +1394,9 @@ revert_attrs: size_t mingw_strftime(char *s, size_t max, const char *format, const struct tm *tm) { +#ifdef _UCRT + size_t ret = strftime(s, max, format, tm); +#else /* a pointer to the original strftime in case we can't find the UCRT version */ static size_t (*fallback)(char *, size_t, const char *, const struct tm *) = strftime; size_t ret; @@ -1404,6 +1407,7 @@ size_t mingw_strftime(char *s, size_t max, ret = strftime(s, max, format, tm); else ret = fallback(s, max, format, tm); +#endif if (!ret && errno == EINVAL) die("invalid strftime format: '%s'", format); @@ -2460,7 +2464,7 @@ repeat: if (supports_file_rename_info_ex) { /* * Our minimum required Windows version is still set to Windows - * Vista. We thus have to declare required infrastructure for + * 8.1. We thus have to declare required infrastructure for * FileRenameInfoEx ourselves until we bump _WIN32_WINNT to * 0x0A00. Furthermore, we have to handle cases where the * FileRenameInfoEx call isn't supported yet. diff --git a/compat/nedmalloc/malloc.c.h b/compat/nedmalloc/malloc.c.h index 814845d4b3..e0c567586c 100644 --- a/compat/nedmalloc/malloc.c.h +++ b/compat/nedmalloc/malloc.c.h @@ -500,7 +500,7 @@ MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP #ifdef WIN32 #define WIN32_LEAN_AND_MEAN #ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x403 +#define _WIN32_WINNT 0x603 #endif #include <windows.h> #define HAVE_MMAP 1 diff --git a/compat/poll/poll.c b/compat/poll/poll.c index a2becd16cd..ea362b4a8e 100644 --- a/compat/poll/poll.c +++ b/compat/poll/poll.c @@ -20,7 +20,7 @@ #define DISABLE_SIGN_COMPARE_WARNINGS -/* To bump the minimum Windows version to Windows Vista */ +/* To bump the minimum Windows version to Windows 8.1 */ #include "git-compat-util.h" /* Tell gcc not to warn about the (nfd < 0) tests, below. */ @@ -41,7 +41,7 @@ #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ # define WIN32_NATIVE # if defined (_MSC_VER) && !defined(_WIN32_WINNT) -# define _WIN32_WINNT 0x0502 +# define _WIN32_WINNT 0x0603 # endif # include <winsock2.h> # include <windows.h> diff --git a/compat/posix.h b/compat/posix.h index 245386fa4a..94699a03fa 100644 --- a/compat/posix.h +++ b/compat/posix.h @@ -76,7 +76,7 @@ #if defined(WIN32) && !defined(__CYGWIN__) /* Both MinGW and MSVC */ # if !defined(_WIN32_WINNT) -# define _WIN32_WINNT 0x0600 +# define _WIN32_WINNT 0x0603 # endif #define WIN32_LEAN_AND_MEAN /* stops windows.h including winsock.h */ #include <winsock2.h> @@ -137,6 +137,9 @@ #include <sys/socket.h> #include <sys/ioctl.h> #include <sys/statvfs.h> +#ifndef NO_WRITEV +#include <sys/uio.h> +#endif #include <termios.h> #ifndef NO_SYS_SELECT_H #include <sys/select.h> @@ -323,6 +326,17 @@ int git_lstat(const char *, struct stat *); ssize_t git_pread(int fd, void *buf, size_t count, off_t offset); #endif +#ifdef NO_WRITEV +#define writev git_writev +#define iovec git_iovec +struct git_iovec { + void *iov_base; + size_t iov_len; +}; + +ssize_t git_writev(int fd, const struct iovec *iov, int iovcnt); +#endif + #ifdef NO_SETENV #define setenv gitsetenv int gitsetenv(const char *, const char *, int); diff --git a/compat/regcomp_enhanced.c b/compat/regcomp_enhanced.c index 84193ce53b..29c74eee99 100644 --- a/compat/regcomp_enhanced.c +++ b/compat/regcomp_enhanced.c @@ -3,6 +3,11 @@ int git_regcomp(regex_t *preg, const char *pattern, int cflags) { + /* + * If you are on macOS with clang and fail to compile this line, + * https://lore.kernel.org/git/458ad3c1-96df-4575-ee42-e6eb754f25f6@gmx.de/ + * might be relevant. + */ if (!(cflags & REG_EXTENDED)) cflags |= REG_ENHANCED; return regcomp(preg, pattern, cflags); diff --git a/compat/win32/flush.c b/compat/win32/flush.c index 291f90ea94..7244ff69ac 100644 --- a/compat/win32/flush.c +++ b/compat/win32/flush.c @@ -6,7 +6,9 @@ int win32_fsync_no_flush(int fd) { IO_STATUS_BLOCK io_status; +#ifndef FLUSH_FLAGS_FILE_DATA_ONLY #define FLUSH_FLAGS_FILE_DATA_ONLY 1 +#endif DECLARE_PROC_ADDR(ntdll.dll, NTSTATUS, NTAPI, NtFlushBuffersFileEx, HANDLE FileHandle, ULONG Flags, PVOID Parameters, ULONG ParameterSize, diff --git a/compat/winansi.c b/compat/winansi.c index ac2ffb7869..3ce1900939 100644 --- a/compat/winansi.c +++ b/compat/winansi.c @@ -32,47 +32,18 @@ static int non_ascii_used = 0; static HANDLE hthread, hread, hwrite; static HANDLE hconsole1, hconsole2; -#ifdef __MINGW32__ -#if !defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 5 -typedef struct _CONSOLE_FONT_INFOEX { - ULONG cbSize; - DWORD nFont; - COORD dwFontSize; - UINT FontFamily; - UINT FontWeight; - WCHAR FaceName[LF_FACESIZE]; -} CONSOLE_FONT_INFOEX, *PCONSOLE_FONT_INFOEX; -#endif -#endif - static void warn_if_raster_font(void) { DWORD fontFamily = 0; - DECLARE_PROC_ADDR(kernel32.dll, BOOL, WINAPI, - GetCurrentConsoleFontEx, HANDLE, BOOL, - PCONSOLE_FONT_INFOEX); + CONSOLE_FONT_INFOEX cfi; /* don't bother if output was ascii only */ if (!non_ascii_used) return; - /* GetCurrentConsoleFontEx is available since Vista */ - if (INIT_PROC_ADDR(GetCurrentConsoleFontEx)) { - CONSOLE_FONT_INFOEX cfi; - cfi.cbSize = sizeof(cfi); - if (GetCurrentConsoleFontEx(console, 0, &cfi)) - fontFamily = cfi.FontFamily; - } else { - /* pre-Vista: check default console font in registry */ - HKEY hkey; - if (ERROR_SUCCESS == RegOpenKeyExA(HKEY_CURRENT_USER, "Console", - 0, KEY_READ, &hkey)) { - DWORD size = sizeof(fontFamily); - RegQueryValueExA(hkey, "FontFamily", NULL, NULL, - (LPVOID) &fontFamily, &size); - RegCloseKey(hkey); - } - } + cfi.cbSize = sizeof(cfi); + if (GetCurrentConsoleFontEx(console, 0, &cfi)) + fontFamily = cfi.FontFamily; if (!(fontFamily & TMPF_TRUETYPE)) { const wchar_t *msg = L"\nWarning: Your console font probably " diff --git a/compat/writev.c b/compat/writev.c new file mode 100644 index 0000000000..3a94870a2f --- /dev/null +++ b/compat/writev.c @@ -0,0 +1,44 @@ +#include "../git-compat-util.h" +#include "../wrapper.h" + +ssize_t git_writev(int fd, const struct iovec *iov, int iovcnt) +{ + size_t total_written = 0; + size_t sum = 0; + + /* + * According to writev(3p), the syscall shall error with EINVAL in case + * the sum of `iov_len` overflows `ssize_t`. + */ + for (int i = 0; i < iovcnt; i++) { + if (iov[i].iov_len > maximum_signed_value_of_type(ssize_t) || + iov[i].iov_len + sum > maximum_signed_value_of_type(ssize_t)) { + errno = EINVAL; + return -1; + } + + sum += iov[i].iov_len; + } + + for (int i = 0; i < iovcnt; i++) { + const char *bytes = iov[i].iov_base; + size_t iovec_written = 0; + + while (iovec_written < iov[i].iov_len) { + ssize_t bytes_written = xwrite(fd, bytes + iovec_written, + iov[i].iov_len - iovec_written); + if (bytes_written < 0) { + if (total_written) + goto out; + return bytes_written; + } + if (!bytes_written) + goto out; + iovec_written += bytes_written; + total_written += bytes_written; + } + } + +out: + return (ssize_t) total_written; +} diff --git a/config.mak.dev b/config.mak.dev index e86b6e1b34..c8dcf78779 100644 --- a/config.mak.dev +++ b/config.mak.dev @@ -1,5 +1,5 @@ ifndef COMPILER_FEATURES -COMPILER_FEATURES := $(shell ./detect-compiler $(CC)) +COMPILER_FEATURES := $(shell ./tools/detect-compiler $(CC)) endif ifeq ($(filter no-error,$(DEVOPTS)),) diff --git a/config.mak.uname b/config.mak.uname index 5feb582558..ccb3f71881 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -459,6 +459,7 @@ ifeq ($(uname_S),Windows) SANE_TOOL_PATH ?= $(msvc_bin_dir_msys) HAVE_ALLOCA_H = YesPlease NO_PREAD = YesPlease + NO_WRITEV = YesPlease NEEDS_CRYPTO_WITH_SSL = YesPlease NO_LIBGEN_H = YesPlease NO_POLL = YesPlease @@ -674,6 +675,7 @@ ifeq ($(uname_S),MINGW) pathsep = ; HAVE_ALLOCA_H = YesPlease NO_PREAD = YesPlease + NO_WRITEV = YesPlease NEEDS_CRYPTO_WITH_SSL = YesPlease NO_LIBGEN_H = YesPlease NO_POLL = YesPlease @@ -1054,6 +1054,8 @@ static struct child_process *git_proxy_connect(int fd[2], char *host) strvec_push(&proxy->args, port); proxy->in = -1; proxy->out = -1; + proxy->clean_on_exit = 1; + proxy->wait_after_clean = 1; if (start_command(proxy)) die(_("cannot start proxy %s"), git_proxy_command); fd[0] = proxy->out; /* read from proxy stdout */ @@ -1515,6 +1517,8 @@ struct child_process *git_connect(int fd[2], const char *url, } strvec_push(&conn->args, cmd.buf); + conn->clean_on_exit = 1; + conn->wait_after_clean = 1; if (start_command(conn)) die(_("unable to fork")); diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt index c6cfb874ef..d7a087e584 100644 --- a/contrib/buildsystems/CMakeLists.txt +++ b/contrib/buildsystems/CMakeLists.txt @@ -376,7 +376,7 @@ endif() #function checks set(function_checks strcasestr memmem strlcpy strtoimax strtoumax strtoull - setenv mkdtemp poll pread memmem) + setenv mkdtemp poll pread memmem writev) #unsetenv,hstrerror are incompatible with windows build if(NOT WIN32) @@ -421,6 +421,10 @@ if(NOT HAVE_MEMMEM) list(APPEND compat_SOURCES compat/memmem.c) endif() +if(NOT HAVE_WRITEV) + list(APPEND compat_SOURCES compat/writev.c) +endif() + if(NOT WIN32) if(NOT HAVE_UNSETENV) list(APPEND compat_SOURCES compat/unsetenv.c) @@ -636,7 +640,7 @@ set(EXCLUSION_PROGS_CACHE ${EXCLUSION_PROGS} CACHE STRING "Programs not built" F if(NOT EXISTS ${CMAKE_BINARY_DIR}/command-list.h OR NOT EXCLUSION_PROGS_CACHE STREQUAL EXCLUSION_PROGS) list(REMOVE_ITEM EXCLUSION_PROGS empty) message("Generating command-list.h") - execute_process(COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/generate-cmdlist.sh" + execute_process(COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/tools/generate-cmdlist.sh" ${EXCLUSION_PROGS} "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}/command-list.h") @@ -644,14 +648,14 @@ endif() if(NOT EXISTS ${CMAKE_BINARY_DIR}/config-list.h) message("Generating config-list.h") - execute_process(COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/generate-configlist.sh" + execute_process(COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/tools/generate-configlist.sh" "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}/config-list.h") endif() if(NOT EXISTS ${CMAKE_BINARY_DIR}/hook-list.h) message("Generating hook-list.h") - execute_process(COMMAND "${SH_EXE}" ${CMAKE_SOURCE_DIR}/generate-hooklist.sh + execute_process(COMMAND "${SH_EXE}" ${CMAKE_SOURCE_DIR}/tools/generate-hooklist.sh "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}/hook-list.h") endif() @@ -832,11 +836,11 @@ foreach(script ${git_shell_scripts}) endif() add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/${shell_gen_path}" - COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/generate-script.sh" + COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/tools/generate-script.sh" "${CMAKE_SOURCE_DIR}/${script}.sh" "${CMAKE_BINARY_DIR}/${shell_gen_path}" "${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS" - DEPENDS "${CMAKE_SOURCE_DIR}/generate-script.sh" + DEPENDS "${CMAKE_SOURCE_DIR}/tools/generate-script.sh" "${CMAKE_SOURCE_DIR}/${script}.sh" VERBATIM) list(APPEND shell_gen ${CMAKE_BINARY_DIR}/${shell_gen_path}) @@ -875,13 +879,13 @@ foreach(script ${git_perl_scripts} ${perl_modules}) file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/${perl_gen_dir}") add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/${perl_gen_path}" - COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/generate-perl.sh" + COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/tools/generate-perl.sh" "${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS" "${CMAKE_BINARY_DIR}/GIT-VERSION-FILE" "${CMAKE_BINARY_DIR}/GIT-PERL-HEADER" "${CMAKE_SOURCE_DIR}/${script}" "${CMAKE_BINARY_DIR}/${perl_gen_path}" - DEPENDS "${CMAKE_SOURCE_DIR}/generate-perl.sh" + DEPENDS "${CMAKE_SOURCE_DIR}/tools/generate-perl.sh" "${CMAKE_SOURCE_DIR}/${script}" "${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS" "${CMAKE_BINARY_DIR}/GIT-VERSION-FILE" @@ -892,11 +896,11 @@ add_custom_target(perl-gen ALL DEPENDS ${perl_gen}) # Python script add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/git-p4" - COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/generate-python.sh" + COMMAND "${SH_EXE}" "${CMAKE_SOURCE_DIR}/tools/generate-python.sh" "${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS" "${CMAKE_SOURCE_DIR}/git-p4.py" "${CMAKE_BINARY_DIR}/git-p4" - DEPENDS "${CMAKE_SOURCE_DIR}/generate-python.sh" + DEPENDS "${CMAKE_SOURCE_DIR}/tools/generate-python.sh" "${CMAKE_SOURCE_DIR}/git-p4.py" "${CMAKE_BINARY_DIR}/GIT-BUILD-OPTIONS" VERBATIM) diff --git a/contrib/diff-highlight/DiffHighlight.pm b/contrib/diff-highlight/DiffHighlight.pm index 3d061bc0b7..abe457882e 100644 --- a/contrib/diff-highlight/DiffHighlight.pm +++ b/contrib/diff-highlight/DiffHighlight.pm @@ -1,6 +1,6 @@ package DiffHighlight; -require v5.26; +require v5.008; use warnings FATAL => 'all'; use strict; @@ -9,18 +9,11 @@ use File::Spec; my $NULL = File::Spec->devnull(); -# Highlight by reversing foreground and background. You could do -# other things like bold or underline if you prefer. -my @OLD_HIGHLIGHT = ( - color_config('color.diff-highlight.oldnormal'), - color_config('color.diff-highlight.oldhighlight', "\x1b[7m"), - color_config('color.diff-highlight.oldreset', "\x1b[27m") -); -my @NEW_HIGHLIGHT = ( - color_config('color.diff-highlight.newnormal', $OLD_HIGHLIGHT[0]), - color_config('color.diff-highlight.newhighlight', $OLD_HIGHLIGHT[1]), - color_config('color.diff-highlight.newreset', $OLD_HIGHLIGHT[2]) -); +# The color theme is initially set to nothing here to allow outside callers +# to set the colors for their application. If nothing is sent in we use +# colors from git config in load_color_config(). +our @OLD_HIGHLIGHT = (); +our @NEW_HIGHLIGHT = (); my $RESET = "\x1b[m"; my $COLOR = qr/\x1b\[[0-9;]*m/; @@ -138,9 +131,21 @@ sub highlight_stdin { # of it being used in other settings. Let's handle our own # fallback, which means we will work even if git can't be run. sub color_config { + our $cached_config; my ($key, $default) = @_; - my $s = `git config --get-color $key 2>$NULL`; - return length($s) ? $s : $default; + + if (!defined $cached_config) { + $cached_config = {}; + my $data = `git config --type=color --get-regexp '^color\.diff-highlight\.' 2>$NULL`; + for my $line (split /\n/, $data) { + my ($key, $color) = split ' ', $line, 2; + $key =~ s/^color\.diff-highlight\.// or next; + $cached_config->{$key} = $color; + } + } + + my $s = $cached_config->{$key}; + return defined($s) ? $s : $default; } sub show_hunk { @@ -170,6 +175,29 @@ sub show_hunk { $line_cb->(@queue); } +sub load_color_config { + # If the colors were NOT set from outside this module we load them on-demand + # from the git config. Note that only one of elements 0 and 2 in each + # array is used (depending on whether you are doing set/unset on an + # attribute, or specifying normal vs highlighted coloring). So we use + # element 1 as our check for whether colors were passed in; it should + # always be set if you want highlighting to do anything. + if (!defined $OLD_HIGHLIGHT[1]) { + @OLD_HIGHLIGHT = ( + color_config('oldnormal'), + color_config('oldhighlight', "\x1b[7m"), + color_config('oldreset', "\x1b[27m") + ); + } + if (!defined $NEW_HIGHLIGHT[1]) { + @NEW_HIGHLIGHT = ( + color_config('newnormal', $OLD_HIGHLIGHT[0]), + color_config('newhighlight', $OLD_HIGHLIGHT[1]), + color_config('newreset', $OLD_HIGHLIGHT[2]) + ); + }; +} + sub highlight_pair { my @a = split_line(shift); my @b = split_line(shift); @@ -218,6 +246,7 @@ sub highlight_pair { } if (is_pair_interesting(\@a, $pa, $sa, \@b, $pb, $sb)) { + load_color_config(); return highlight_line(\@a, $pa, $sa, \@OLD_HIGHLIGHT), highlight_line(\@b, $pb, $sb, \@NEW_HIGHLIGHT); } @@ -273,6 +302,18 @@ sub highlight_line { # or suffix (disregarding boring bits like whitespace and colorization). sub is_pair_interesting { my ($a, $pa, $sa, $b, $pb, $sb) = @_; + + # We hit this case if the prefix consumed the entire line, meaning + # that two lines are identical. This generally shouldn't happen, + # since it implies the diff isn't minimal (you could shrink the hunk by + # making this a context line). But you can see it when the line + # content is the same, but the trailing newline is dropped, like: + # + # -foo + # +foo + # \No newline at end of file + return 0 if $pa == @$a || $pb == @$b; + my $prefix_a = join('', @$a[0..($pa-1)]); my $prefix_b = join('', @$b[0..($pb-1)]); my $suffix_a = join('', @$a[($sa+1)..$#$a]); diff --git a/contrib/diff-highlight/README b/contrib/diff-highlight/README index 1db4440e68..ed8d876a18 100644 --- a/contrib/diff-highlight/README +++ b/contrib/diff-highlight/README @@ -39,10 +39,21 @@ visually distracting. Non-diff lines and existing diff coloration is preserved; the intent is that the output should look exactly the same as the input, except for the occasional highlight. +Build/Install +------------- + +You can build the `diff-highlight` script by running `make` from within +the diff-highlight directory. There is no `make install` target; you can +copy the built script to your $PATH. + +You can run diff-highlight's internal tests by running `make test`. Note +that you must also build Git itself first (by running `make` from the +top-level of the project). + Use --- -You can try out the diff-highlight program with: +You can try out the built diff-highlight program with: --------------------------------------------- git log -p --color | /path/to/diff-highlight @@ -127,6 +138,12 @@ Your script may set up one or more of the following variables: processing a logical chunk of input). The default function flushes stdout. + - @DiffHighlight::OLD_HIGHLIGHT and @DiffHighlight::NEW_HIGHLIGHT - these + arrays specify the normal, highlighted, and reset colors (in that order) + for old/new lines. If unset, values will be retrieved by calling `git + config` (see "Color Config" above). Note that these should be the literal + color bytes (starting with an ANSI escape code), not color names. + The script may then feed lines, one at a time, to DiffHighlight::handle_line(). When lines are done processing, they will be fed to $line_cb. Note that DiffHighlight may queue up many input lines (to analyze a whole hunk) diff --git a/contrib/diff-highlight/t/t9400-diff-highlight.sh b/contrib/diff-highlight/t/t9400-diff-highlight.sh index dee296739c..b38fe2196a 100755 --- a/contrib/diff-highlight/t/t9400-diff-highlight.sh +++ b/contrib/diff-highlight/t/t9400-diff-highlight.sh @@ -7,9 +7,6 @@ TEST_OUTPUT_DIRECTORY=$(pwd) TEST_DIRECTORY="$CURR_DIR"/../../../t DIFF_HIGHLIGHT="$CURR_DIR"/../diff-highlight -CW="$(printf "\033[7m")" # white -CR="$(printf "\033[27m")" # reset - GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME . "$TEST_DIRECTORY"/test-lib.sh @@ -41,8 +38,10 @@ dh_test () { git show >commit.raw } >/dev/null && - "$DIFF_HIGHLIGHT" <diff.raw | test_strip_patch_header >diff.act && - "$DIFF_HIGHLIGHT" <commit.raw | test_strip_patch_header >commit.act && + "$DIFF_HIGHLIGHT" <diff.raw >diff.hi && + test_strip_patch_header <diff.hi | test_decode_color >diff.act && + "$DIFF_HIGHLIGHT" <commit.raw >commit.hi && + test_strip_patch_header <commit.hi | test_decode_color >commit.act && test_cmp patch.exp diff.act && test_cmp patch.exp commit.act } @@ -124,8 +123,8 @@ test_expect_success 'diff-highlight highlights the beginning of a line' ' dh_test a b <<-EOF @@ -1,3 +1,3 @@ aaa - -${CW}b${CR}bb - +${CW}0${CR}bb + -<REVERSE>b<NOREVERSE>bb + +<REVERSE>0<NOREVERSE>bb ccc EOF ' @@ -146,8 +145,8 @@ test_expect_success 'diff-highlight highlights the end of a line' ' dh_test a b <<-EOF @@ -1,3 +1,3 @@ aaa - -bb${CW}b${CR} - +bb${CW}0${CR} + -bb<REVERSE>b<NOREVERSE> + +bb<REVERSE>0<NOREVERSE> ccc EOF ' @@ -168,8 +167,8 @@ test_expect_success 'diff-highlight highlights the middle of a line' ' dh_test a b <<-EOF @@ -1,3 +1,3 @@ aaa - -b${CW}b${CR}b - +b${CW}0${CR}b + -b<REVERSE>b<NOREVERSE>b + +b<REVERSE>0<NOREVERSE>b ccc EOF ' @@ -211,8 +210,8 @@ test_expect_failure 'diff-highlight highlights mismatched hunk size' ' dh_test a b <<-EOF @@ -1,3 +1,3 @@ aaa - -b${CW}b${CR}b - +b${CW}0${CR}b + -b<REVERSE>b<NOREVERSE>b + +b<REVERSE>0<NOREVERSE>b +ccc EOF ' @@ -230,8 +229,8 @@ test_expect_success 'diff-highlight treats multibyte utf-8 as a unit' ' echo "unic${o_stroke}de" >b && dh_test a b <<-EOF @@ -1 +1 @@ - -unic${CW}${o_accent}${CR}de - +unic${CW}${o_stroke}${CR}de + -unic<REVERSE>${o_accent}<NOREVERSE>de + +unic<REVERSE>${o_stroke}<NOREVERSE>de EOF ' @@ -248,8 +247,8 @@ test_expect_failure 'diff-highlight treats combining code points as a unit' ' echo "unico${combine_circum}de" >b && dh_test a b <<-EOF @@ -1 +1 @@ - -unic${CW}o${combine_accent}${CR}de - +unic${CW}o${combine_circum}${CR}de + -unic<REVERSE>o${combine_accent}<NOREVERSE>de + +unic<REVERSE>o${combine_circum}<NOREVERSE>de EOF ' @@ -331,13 +330,52 @@ test_expect_success 'diff-highlight handles --graph with leading dash' ' +++ b/file @@ -1,3 +1,3 @@ before - -the ${CW}old${CR} line - +the ${CW}new${CR} line + -the <REVERSE>old<NOREVERSE> line + +the <REVERSE>new<NOREVERSE> line -leading dash EOF git log --graph -p -1 | "$DIFF_HIGHLIGHT" >actual.raw && - trim_graph <actual.raw | sed -n "/^---/,\$p" >actual && + trim_graph <actual.raw | sed -n "/^---/,\$p" | test_decode_color >actual && test_cmp expect actual ' +test_expect_success 'highlight diff that removes final newline' ' + printf "content\n" >a && + printf "content" >b && + dh_test a b <<-\EOF + @@ -1 +1 @@ + -content + +content + \ No newline at end of file + EOF +' + +test_expect_success 'configure set/reset colors' ' + test_config color.diff-highlight.oldhighlight bold && + test_config color.diff-highlight.oldreset nobold && + test_config color.diff-highlight.newhighlight italic && + test_config color.diff-highlight.newreset noitalic && + echo "prefix a suffix" >a && + echo "prefix b suffix" >b && + dh_test a b <<-\EOF + @@ -1 +1 @@ + -prefix <BOLD>a<NORMAL_INTENSITY> suffix + +prefix <ITALIC>b<NOITALIC> suffix + EOF +' + +test_expect_success 'configure normal/highlight colors' ' + test_config color.diff-highlight.oldnormal red && + test_config color.diff-highlight.oldhighlight magenta && + test_config color.diff-highlight.newnormal green && + test_config color.diff-highlight.newhighlight yellow && + echo "prefix a suffix" >a && + echo "prefix b suffix" >b && + dh_test a b <<-\EOF + @@ -1 +1 @@ + <RED>-prefix <RESET><MAGENTA>a<RESET><RED> suffix<RESET> + <GREEN>+prefix <RESET><YELLOW>b<RESET><GREEN> suffix<RESET> + EOF +' + test_done diff --git a/contrib/meson.build b/contrib/meson.build index a88c5dfe09..569c23ee76 100644 --- a/contrib/meson.build +++ b/contrib/meson.build @@ -2,5 +2,4 @@ foreach feature : get_option('contrib') subdir(feature) endforeach -subdir('coccinelle') subdir('credential') diff --git a/contrib/subtree/meson.build b/contrib/subtree/meson.build index 161435abeb..804c315894 100644 --- a/contrib/subtree/meson.build +++ b/contrib/subtree/meson.build @@ -3,7 +3,7 @@ git_subtree = custom_target( output: 'git-subtree', command: [ shell, - meson.project_source_root() / 'generate-script.sh', + meson.project_source_root() / 'tools/generate-script.sh', '@INPUT@', '@OUTPUT@', meson.project_build_root() / 'GIT-BUILD-OPTIONS', diff --git a/csum-file.c b/csum-file.c index 3d3047c776..9558177a11 100644 --- a/csum-file.c +++ b/csum-file.c @@ -161,17 +161,16 @@ struct hashfile *hashfd_check(const struct git_hash_algo *algop, return f; } -static struct hashfile *hashfd_internal(const struct git_hash_algo *algop, - int fd, const char *name, - struct progress *tp, - size_t buffer_len) +struct hashfile *hashfd_ext(const struct git_hash_algo *algop, + int fd, const char *name, + const struct hashfd_options *opts) { struct hashfile *f = xmalloc(sizeof(*f)); f->fd = fd; f->check_fd = -1; f->offset = 0; f->total = 0; - f->tp = tp; + f->tp = opts->progress; f->name = name; f->do_crc = 0; f->skip_hash = 0; @@ -179,8 +178,8 @@ static struct hashfile *hashfd_internal(const struct git_hash_algo *algop, f->algop = unsafe_hash_algo(algop); f->algop->init_fn(&f->ctx); - f->buffer_len = buffer_len; - f->buffer = xmalloc(buffer_len); + f->buffer_len = opts->buffer_len ? opts->buffer_len : 128 * 1024; + f->buffer = xmalloc(f->buffer_len); f->check_buffer = NULL; return f; @@ -194,19 +193,8 @@ struct hashfile *hashfd(const struct git_hash_algo *algop, * measure the rate of data passing through this hashfile, * use a larger buffer size to reduce fsync() calls. */ - return hashfd_internal(algop, fd, name, NULL, 128 * 1024); -} - -struct hashfile *hashfd_throughput(const struct git_hash_algo *algop, - int fd, const char *name, struct progress *tp) -{ - /* - * Since we are expecting to report progress of the - * write into this hashfile, use a smaller buffer - * size so the progress indicators arrive at a more - * frequent rate. - */ - return hashfd_internal(algop, fd, name, tp, 8 * 1024); + struct hashfd_options opts = { 0 }; + return hashfd_ext(algop, fd, name, &opts); } void hashfile_checkpoint_init(struct hashfile *f, diff --git a/csum-file.h b/csum-file.h index ecce9d27b0..a9b390d336 100644 --- a/csum-file.h +++ b/csum-file.h @@ -45,12 +45,24 @@ int hashfile_truncate(struct hashfile *, struct hashfile_checkpoint *); #define CSUM_FSYNC 2 #define CSUM_HASH_IN_STREAM 4 +struct hashfd_options { + /* + * Throughput progress that counts the number of bytes that have been + * hashed. + */ + struct progress *progress; + + /* The length of the buffer that shall be used read read data. */ + size_t buffer_len; +}; + +struct hashfile *hashfd_ext(const struct git_hash_algo *algop, + int fd, const char *name, + const struct hashfd_options *opts); struct hashfile *hashfd(const struct git_hash_algo *algop, int fd, const char *name); struct hashfile *hashfd_check(const struct git_hash_algo *algop, const char *name); -struct hashfile *hashfd_throughput(const struct git_hash_algo *algop, - int fd, const char *name, struct progress *tp); /* * Free the hashfile without flushing its contents to disk. This only @@ -3518,15 +3518,15 @@ int get_sparse_checkout_patterns(struct pattern_list *pl) int remove_path(const char *name) { - char *slash; + const char *last; if (unlink(name) && !is_missing_file_error(errno)) return -1; - slash = strrchr(name, '/'); - if (slash) { + last = strrchr(name, '/'); + if (last) { char *dirs = xstrdup(name); - slash = dirs + (slash - name); + char *slash = dirs + (last - name); do { *slash = '\0'; if (startup_info->original_cwd && diff --git a/fetch-pack.c b/fetch-pack.c index 6ecd468ef7..7b580920a3 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -51,7 +51,6 @@ static int server_supports_filtering; static int advertise_sid; static struct shallow_lock shallow_lock; static const char *alternate_shallow_file; -static struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES; static struct strbuf fsck_msg_types = STRBUF_INIT; static struct string_list uri_protocols = STRING_LIST_INIT_DUP; @@ -1024,12 +1023,8 @@ static int get_pack(struct fetch_pack_args *args, fsck_msg_types.buf); } - if (index_pack_args) { - int i; - - for (i = 0; i < cmd.args.nr; i++) - strvec_push(index_pack_args, cmd.args.v[i]); - } + if (index_pack_args) + strvec_pushv(index_pack_args, cmd.args.v); sigchain_push(SIGPIPE, SIG_IGN); @@ -1100,6 +1095,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, struct shallow_info *si, struct string_list *pack_lockfiles) { + struct fsck_options fsck_options = { 0 }; struct repository *r = the_repository; struct ref *ref = copy_ref_list(orig_ref); struct object_id oid; @@ -1228,6 +1224,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, alternate_shallow_file = setup_temporary_shallow(si->shallow); } else alternate_shallow_file = NULL; + + fsck_options_init(&fsck_options, the_repository, FSCK_OPTIONS_MISSING_GITMODULES); if (get_pack(args, fd, pack_lockfiles, NULL, sought, nr_sought, &fsck_options.gitmodules_found)) die(_("git fetch-pack: fetch failed.")); @@ -1235,6 +1233,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, die("fsck failed"); all_done: + fsck_options_clear(&fsck_options); if (negotiator) negotiator->release(negotiator); return ref; @@ -1654,6 +1653,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, struct string_list *pack_lockfiles) { struct repository *r = the_repository; + struct fsck_options fsck_options; struct ref *ref = copy_ref_list(orig_ref); enum fetch_state state = FETCH_CHECK_LOCAL; struct oidset common = OIDSET_INIT; @@ -1671,6 +1671,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, struct strvec index_pack_args = STRVEC_INIT; const char *promisor_remote_config; + fsck_options_init(&fsck_options, the_repository, FSCK_OPTIONS_MISSING_GITMODULES); + if (server_feature_v2("promisor-remote", &promisor_remote_config)) promisor_remote_reply(promisor_remote_config, NULL); @@ -1882,6 +1884,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args, if (negotiator) negotiator->release(negotiator); + fsck_options_clear(&fsck_options); oidset_clear(&common); return ref; } @@ -1,5 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE - #include "git-compat-util.h" #include "date.h" #include "dir.h" @@ -207,7 +205,7 @@ void fsck_set_msg_types(struct fsck_options *options, const char *values) if (equal == len) die("skiplist requires a path"); oidset_parse_file(&options->skip_oids, buf + equal + 1, - the_repository->hash_algo); + options->repo->hash_algo); buf += len + 1; continue; } @@ -360,7 +358,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op int res = 0; const char *name; - if (repo_parse_tree(the_repository, tree)) + if (repo_parse_tree(options->repo, tree)) return -1; name = fsck_get_object_name(options, &tree->object.oid); @@ -375,14 +373,14 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op continue; if (S_ISDIR(entry.mode)) { - obj = (struct object *)lookup_tree(the_repository, &entry.oid); + obj = (struct object *)lookup_tree(options->repo, &entry.oid); if (name && obj) fsck_put_object_name(options, &entry.oid, "%s%s/", name, entry.path); result = options->walk(obj, OBJ_TREE, data, options); } else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) { - obj = (struct object *)lookup_blob(the_repository, &entry.oid); + obj = (struct object *)lookup_blob(options->repo, &entry.oid); if (name && obj) fsck_put_object_name(options, &entry.oid, "%s%s", name, entry.path); @@ -409,7 +407,7 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio int result; const char *name; - if (repo_parse_commit(the_repository, commit)) + if (repo_parse_commit(options->repo, commit)) return -1; name = fsck_get_object_name(options, &commit->object.oid); @@ -417,7 +415,7 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio fsck_put_object_name(options, get_commit_tree_oid(commit), "%s:", name); - result = options->walk((struct object *) repo_get_commit_tree(the_repository, commit), + result = options->walk((struct object *) repo_get_commit_tree(options->repo, commit), OBJ_TREE, data, options); if (result < 0) return result; @@ -474,7 +472,7 @@ static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *optio { const char *name = fsck_get_object_name(options, &tag->object.oid); - if (parse_tag(the_repository, tag)) + if (parse_tag(options->repo, tag)) return -1; if (name) fsck_put_object_name(options, &tag->tagged->oid, "%s", name); @@ -487,7 +485,7 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options) return -1; if (obj->type == OBJ_NONE) - parse_object(the_repository, &obj->oid); + parse_object(options->repo, &obj->oid); switch (obj->type) { case OBJ_BLOB: @@ -970,14 +968,14 @@ static int fsck_commit(const struct object_id *oid, if (buffer >= buffer_end || !skip_prefix(buffer, "tree ", &buffer)) return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line"); - if (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\n') { + if (parse_oid_hex_algop(buffer, &tree_oid, &p, options->repo->hash_algo) || *p != '\n') { err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1"); if (err) return err; } buffer = p + 1; while (buffer < buffer_end && skip_prefix(buffer, "parent ", &buffer)) { - if (parse_oid_hex(buffer, &parent_oid, &p) || *p != '\n') { + if (parse_oid_hex_algop(buffer, &parent_oid, &p, options->repo->hash_algo) || *p != '\n') { err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1"); if (err) return err; @@ -1044,7 +1042,7 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer, ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line"); goto done; } - if (parse_oid_hex(buffer, tagged_oid, &p) || *p != '\n') { + if (parse_oid_hex_algop(buffer, tagged_oid, &p, options->repo->hash_algo) || *p != '\n') { ret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1"); if (ret) goto done; @@ -1336,9 +1334,9 @@ static int fsck_blobs(struct oidset *blobs_found, struct oidset *blobs_done, if (oidset_contains(blobs_done, oid)) continue; - buf = odb_read_object(the_repository->objects, oid, &type, &size); + buf = odb_read_object(options->repo->objects, oid, &type, &size); if (!buf) { - if (is_promisor_object(the_repository, oid)) + if (is_promisor_object(options->repo, oid)) continue; ret |= report(options, oid, OBJ_BLOB, msg_missing, @@ -1380,6 +1378,54 @@ bool fsck_has_queued_checks(struct fsck_options *options) !oidset_equal(&options->gitattributes_found, &options->gitattributes_done); } +void fsck_options_init(struct fsck_options *options, + struct repository *repo, + enum fsck_options_type type) +{ + static const struct fsck_options defaults[] = { + [FSCK_OPTIONS_DEFAULT] = { + .skip_oids = OIDSET_INIT, + .gitmodules_found = OIDSET_INIT, + .gitmodules_done = OIDSET_INIT, + .gitattributes_found = OIDSET_INIT, + .gitattributes_done = OIDSET_INIT, + .error_func = fsck_objects_error_function + }, + [FSCK_OPTIONS_STRICT] = { + .strict = 1, + .gitmodules_found = OIDSET_INIT, + .gitmodules_done = OIDSET_INIT, + .gitattributes_found = OIDSET_INIT, + .gitattributes_done = OIDSET_INIT, + .error_func = fsck_objects_error_function, + }, + [FSCK_OPTIONS_MISSING_GITMODULES] = { + .strict = 1, + .gitmodules_found = OIDSET_INIT, + .gitmodules_done = OIDSET_INIT, + .gitattributes_found = OIDSET_INIT, + .gitattributes_done = OIDSET_INIT, + .error_func = fsck_objects_error_cb_print_missing_gitmodules, + }, + [FSCK_OPTIONS_REFS] = { + .error_func = fsck_refs_error_function, + }, + }; + + switch (type) { + case FSCK_OPTIONS_DEFAULT: + case FSCK_OPTIONS_STRICT: + case FSCK_OPTIONS_MISSING_GITMODULES: + case FSCK_OPTIONS_REFS: + memcpy(options, &defaults[type], sizeof(*options)); + break; + default: + BUG("unknown fsck options type %d", type); + } + + options->repo = repo; +} + void fsck_options_clear(struct fsck_options *options) { free(options->msg_type); @@ -166,7 +166,10 @@ struct fsck_ref_report { const char *path; }; +struct repository; + struct fsck_options { + struct repository *repo; fsck_walk_func walk; fsck_error error_func; unsigned strict; @@ -180,34 +183,6 @@ struct fsck_options { kh_oid_map_t *object_names; }; -#define FSCK_OPTIONS_DEFAULT { \ - .skip_oids = OIDSET_INIT, \ - .gitmodules_found = OIDSET_INIT, \ - .gitmodules_done = OIDSET_INIT, \ - .gitattributes_found = OIDSET_INIT, \ - .gitattributes_done = OIDSET_INIT, \ - .error_func = fsck_objects_error_function \ -} -#define FSCK_OPTIONS_STRICT { \ - .strict = 1, \ - .gitmodules_found = OIDSET_INIT, \ - .gitmodules_done = OIDSET_INIT, \ - .gitattributes_found = OIDSET_INIT, \ - .gitattributes_done = OIDSET_INIT, \ - .error_func = fsck_objects_error_function, \ -} -#define FSCK_OPTIONS_MISSING_GITMODULES { \ - .strict = 1, \ - .gitmodules_found = OIDSET_INIT, \ - .gitmodules_done = OIDSET_INIT, \ - .gitattributes_found = OIDSET_INIT, \ - .gitattributes_done = OIDSET_INIT, \ - .error_func = fsck_objects_error_cb_print_missing_gitmodules, \ -} -#define FSCK_REFS_OPTIONS_DEFAULT { \ - .error_func = fsck_refs_error_function, \ -} - /* descend in all linked child objects * the return value is: * -1 error in processing the object @@ -255,6 +230,17 @@ int fsck_finish(struct fsck_options *options); */ bool fsck_has_queued_checks(struct fsck_options *options); +enum fsck_options_type { + FSCK_OPTIONS_DEFAULT, + FSCK_OPTIONS_STRICT, + FSCK_OPTIONS_MISSING_GITMODULES, + FSCK_OPTIONS_REFS, +}; + +void fsck_options_init(struct fsck_options *options, + struct repository *repo, + enum fsck_options_type type); + /* * Clear the fsck_options struct, freeing any allocated memory. */ diff --git a/git-compat-util.h b/git-compat-util.h index bebcf9f698..4b4ea2498f 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -34,10 +34,6 @@ struct strbuf; # define DISABLE_WARNING(warning) #endif -#ifdef DISABLE_SIGN_COMPARE_WARNINGS -DISABLE_WARNING(-Wsign-compare) -#endif - #undef FLEX_ARRAY #define FLEX_ARRAY /* empty - weather balloon to require C99 FAM */ @@ -1099,3 +1095,7 @@ extern int not_supposed_to_survive; #endif /* CHECK_ASSERTION_SIDE_EFFECTS */ #endif + +#ifdef DISABLE_SIGN_COMPARE_WARNINGS +DISABLE_WARNING(-Wsign-compare) +#endif diff --git a/git-curl-compat.h b/git-curl-compat.h index 659e5a3875..dccdd4d6e5 100644 --- a/git-curl-compat.h +++ b/git-curl-compat.h @@ -38,6 +38,14 @@ #endif /** + * CURLINFO_RETRY_AFTER was added in 7.66.0, released in September 2019. + * It allows curl to automatically parse Retry-After headers. + */ +#if LIBCURL_VERSION_NUM >= 0x074200 +#define GIT_CURL_HAVE_CURLINFO_RETRY_AFTER 1 +#endif + +/** * CURLOPT_PROTOCOLS_STR and CURLOPT_REDIR_PROTOCOLS_STR were added in 7.85.0, * released in August 2022. */ diff --git a/git-gui/.gitignore b/git-gui/.gitignore index 5130b4f018..38a41ebc58 100644 --- a/git-gui/.gitignore +++ b/git-gui/.gitignore @@ -5,4 +5,5 @@ GIT-GUI-BUILD-OPTIONS GIT-VERSION-FILE git-gui git-gui--askpass +git-gui--askyesno lib/tclIndex diff --git a/git-gui/GIT-VERSION-GEN b/git-gui/GIT-VERSION-GEN index c2767b4136..2f729de4bb 100755 --- a/git-gui/GIT-VERSION-GEN +++ b/git-gui/GIT-VERSION-GEN @@ -5,19 +5,27 @@ DEF_VER=0.21.GITGUI LF=' ' -if test "$#" -ne 2 +if test "$#" -lt 2 then - echo >&2 "usage: $0 <SOURCE_DIR> <OUTPUT>" + echo >&2 "usage: $0 <SOURCE_DIR> <OUTPUT> [<PARENT_PROJECT_DIR>]" exit 1 fi SOURCE_DIR="$1" OUTPUT="$2" +PARENT_PROJECT_DIR="$3" # Protect us from reading Git version information outside of the Git directory # in case it is not a repository itself, but embedded in an unrelated -# repository. -GIT_CEILING_DIRECTORIES="$SOURCE_DIR/.." +# repository. The PARENT_PROJECT_DIR variable can be used to override this, for +# example when git-gui is included as a subproject. +if test -n "$PARENT_PROJECT_DIR" +then + GIT_CEILING_DIRECTORIES="$PARENT_PROJECT_DIR/.." +else + GIT_CEILING_DIRECTORIES="$SOURCE_DIR/.." +fi + export GIT_CEILING_DIRECTORIES tree_search () diff --git a/git-gui/Makefile b/git-gui/Makefile index 69b0b84435..ca01068810 100644 --- a/git-gui/Makefile +++ b/git-gui/Makefile @@ -9,7 +9,7 @@ all:: # GIT-VERSION-FILE: FORCE - @$(SHELL_PATH) ./GIT-VERSION-GEN . $@ + @$(SHELL_PATH) ./GIT-VERSION-GEN . $@ "$(PARENT_PROJECT_DIR)" uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not') @@ -177,10 +177,13 @@ GIT-GUI-BUILD-OPTIONS: FORCE git-gui--askpass: git-gui--askpass.sh GIT-GUI-BUILD-OPTIONS generate-script.sh $(QUIET_GEN)$(SHELL_PATH) generate-script.sh $@ $< ./GIT-GUI-BUILD-OPTIONS +git-gui--askyesno: git-gui--askyesno.sh GIT-GUI-BUILD-OPTIONS generate-script.sh + $(QUIET_GEN)$(SHELL_PATH) generate-script.sh $@ $< ./GIT-GUI-BUILD-OPTIONS + ifdef GITGUI_WINDOWS_WRAPPER all:: git-gui endif -all:: $(GITGUI_MAIN) git-gui--askpass lib/tclIndex $(ALL_MSGFILES) +all:: $(GITGUI_MAIN) git-gui--askpass git-gui--askyesno lib/tclIndex $(ALL_MSGFILES) install: all $(QUIET)$(INSTALL_D0)'$(DESTDIR_SQ)$(gitexecdir_SQ)' $(INSTALL_D1) @@ -221,7 +224,7 @@ dist-version: GIT-VERSION-FILE @sed 's|^GITGUI_VERSION=||' <GIT-VERSION-FILE >$(TARDIR)/version clean:: - $(RM_RF) $(GITGUI_MAIN) git-gui--askpass lib/tclIndex po/*.msg $(PO_TEMPLATE) + $(RM_RF) $(GITGUI_MAIN) git-gui--askpass git-gui--askyesno lib/tclIndex po/*.msg $(PO_TEMPLATE) $(RM_RF) GIT-VERSION-FILE GIT-GUI-BUILD-OPTIONS ifdef GITGUI_WINDOWS_WRAPPER $(RM_RF) git-gui diff --git a/git-gui/git-gui--askyesno b/git-gui/git-gui--askyesno.sh index 142d1bc3de..142d1bc3de 100755 --- a/git-gui/git-gui--askyesno +++ b/git-gui/git-gui--askyesno.sh diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh index d3d3aa14a9..23fe76e498 100755 --- a/git-gui/git-gui.sh +++ b/git-gui/git-gui.sh @@ -3900,6 +3900,24 @@ if {[winfo exists $ui_comm]} { backup_commit_buffer + # Grey out comment lines (which are stripped from the final commit message by + # wash_commit_message). + $ui_comm tag configure commit_comment -foreground gray + proc dim_commit_comment_lines {} { + global ui_comm comment_string + $ui_comm tag remove commit_comment 1.0 end + set text [$ui_comm get 1.0 end] + # See also cmt_rx in wash_commit_message + set cmt_rx [strcat {^} [regsub -all {\W} $comment_string {\\&}]] + set ranges [regexp -all -indices -inline -line -- $cmt_rx $text] + foreach pair $ranges { + set idx "1.0 + [lindex $pair 0] chars" + $ui_comm tag add commit_comment $idx "$idx lineend + 1 char" + } + } + dim_commit_comment_lines + bind $ui_comm <<Modified>> { after idle dim_commit_comment_lines } + # -- If the user has aspell available we can drive it # in pipe mode to spellcheck the commit message. # diff --git a/git-gui/lib/diff.tcl b/git-gui/lib/diff.tcl index 442737ba4f..8be1a613fb 100644 --- a/git-gui/lib/diff.tcl +++ b/git-gui/lib/diff.tcl @@ -385,6 +385,8 @@ proc read_diff {fd conflict_size cont_info} { # if {[string match {@@@ *} $line]} { set is_3way_diff 1 + apply_tab_size 2 + } elseif {[string match {@@ *} $line]} { apply_tab_size 1 } diff --git a/git-gui/meson.build b/git-gui/meson.build index 320ba09ecf..a8119aa29f 100644 --- a/git-gui/meson.build +++ b/git-gui/meson.build @@ -4,7 +4,7 @@ project('git-gui', fs = import('fs') -shell = find_program('sh') +shell = find_program('/bin/sh', 'sh') tclsh = find_program('tclsh') wish = find_program('wish') @@ -34,6 +34,7 @@ version_file = custom_target( '@INPUT@', meson.current_source_dir(), '@OUTPUT@', + get_option('parent_project_dir'), ], build_always_stale: true, ) @@ -53,19 +54,21 @@ if target_machine.system() == 'windows' ) endif -custom_target( - output: 'git-gui--askpass', - input: 'git-gui--askpass.sh', - command: [ - shell, - meson.current_source_dir() / 'generate-script.sh', - '@OUTPUT@', - '@INPUT@', - meson.current_build_dir() / 'GIT-GUI-BUILD-OPTIONS', - ], - install: true, - install_dir: get_option('libexecdir') / 'git-core', -) +foreach script : [ 'git-gui--askpass', 'git-gui--askyesno' ] + custom_target( + output: script, + input: script + '.sh', + command: [ + shell, + meson.current_source_dir() / 'generate-script.sh', + '@OUTPUT@', + '@INPUT@', + meson.current_build_dir() / 'GIT-GUI-BUILD-OPTIONS', + ], + install: true, + install_dir: get_option('libexecdir') / 'git-core', + ) +endforeach custom_target( input: 'git-gui.sh', diff --git a/git-gui/meson_options.txt b/git-gui/meson_options.txt new file mode 100644 index 0000000000..7591a34218 --- /dev/null +++ b/git-gui/meson_options.txt @@ -0,0 +1,2 @@ +option('parent_project_dir', type: 'string', value: '', + description: 'The directory of the parent project. This is used so that the version can be determined even in case git-gui is included as a subtree.') @@ -877,8 +877,7 @@ static int run_argv(struct strvec *args) commit_pager_choice(); strvec_push(&cmd.args, "git"); - for (size_t i = 0; i < args->nr; i++) - strvec_push(&cmd.args, args->v[i]); + strvec_pushv(&cmd.args, args->v); trace_argv_printf(cmd.args.v, "trace: exec:"); diff --git a/gitk-git/.gitignore b/gitk-git/.gitignore index d7ebcaf366..15f96aad5e 100644 --- a/gitk-git/.gitignore +++ b/gitk-git/.gitignore @@ -1,2 +1,3 @@ /GIT-TCLTK-VARS /gitk-wish +po/gitk.pot diff --git a/gitk-git/Makefile b/gitk-git/Makefile index cc32dcab4b..41116d8a14 100644 --- a/gitk-git/Makefile +++ b/gitk-git/Makefile @@ -68,9 +68,12 @@ gitk-wish: gitk GIT-TCLTK-VARS $(SHELL_PATH) ./generate-tcl.sh "$(TCLTK_PATH_SQ)" "$<" "$@" $(PO_TEMPLATE): gitk - $(XGETTEXT) -kmc -LTcl -o $@ gitk + $(XGETTEXT) -kmc -LTcl --package-name=Gitk -o $@ gitk update-po:: $(PO_TEMPLATE) - $(foreach p, $(ALL_POFILES), echo Updating $p ; msgmerge -U $p $(PO_TEMPLATE) ; ) + $(foreach p, $(ALL_POFILES), echo Updating $p ; msgmerge -U --add-location $p $(PO_TEMPLATE) ; ) + @echo "Before committing changes, ensure that a clean-filter is installed:"; \ + echo; \ + echo " git config filter.gettext-no-location.clean \"msgcat --no-location -\"" $(ALL_MSGFILES): %.msg : %.po @echo Generating catalog $@ $(MSGFMT) --statistics --tcl -l $(basename $(notdir $<)) -d $(dir $@) $< diff --git a/gitk-git/gitk b/gitk-git/gitk index cbaaee994e..2730274966 100755 --- a/gitk-git/gitk +++ b/gitk-git/gitk @@ -6831,16 +6831,18 @@ proc drawtags {id x xt y1} { } else { # draw a head or other ref if {[incr nheads -1] >= 0} { - set col $headbgcolor + set refoutlinecol $headoutlinecolor + set reffillcol $headbgcolor if {$tag eq $mainhead} { set font mainfontbold } } else { - set col "#ddddff" + set refoutlinecol black + set reffillcol "#ddddff" } set xl [expr {$xl - $delta/2}] $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \ - -width 1 -outline black -fill $col -tags tag.$id + -width 1 -outline $refoutlinecol -fill $reffillcol -tags tag.$id if {[regexp {^(remotes/[^/]*/|remotes/)} $tag match remoteprefix]} { set rwid [font measure mainfont $remoteprefix] set xi [expr {$x + 1}] @@ -6850,7 +6852,8 @@ proc drawtags {id x xt y1} { -width 0 -fill $remotebgcolor -tags tag.$id } } - set t [$canv create text $xl $y1 -anchor w -text $tag -fill $headfgcolor \ + set textfgcolor [expr {$ntags >= 0 ? $tagfgcolor : $headfgcolor}] + set t [$canv create text $xl $y1 -anchor w -text $tag -fill $textfgcolor \ -font $font -tags [list tag.$id text]] if {$ntags >= 0} { $canv bind $t <1> $tagclick @@ -11796,7 +11799,7 @@ proc prefspage_general {notebook} { proc prefspage_colors {notebook} { global bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor - global diffbgcolors + global diffbgcolors linkfgcolor global themeloader set page [create_prefs_page $notebook.colors] @@ -11873,6 +11876,11 @@ proc prefspage_colors {notebook} { -command [list choosecolor selectbgcolor {} $page [mc "background"]] grid x $page.selbgbut $page.selbgsep -sticky w + label $page.linkfg -padx 40 -relief sunk -background $linkfgcolor + ttk::button $page.linkfgbut -text [mc "Link"] \ + -command [list choosecolor linkfgcolor {} $page [mc "link"]] + grid x $page.linkfgbut $page.linkfg -sticky w + grid columnconfigure $page 2 -weight 1 return $page @@ -11880,7 +11888,7 @@ proc prefspage_colors {notebook} { proc prefspage_set_colorswatches {page} { global bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor - global diffbgcolors + global diffbgcolors linkfgcolor $page.bg configure -background $bgcolor $page.fg configure -background $fgcolor @@ -11891,6 +11899,7 @@ proc prefspage_set_colorswatches {page} { $page.hunksep configure -background [lindex $diffcolors 2] $page.markbgsep configure -background $markbgcolor $page.selbgsep configure -background $selectbgcolor + $page.linkfg configure -background $linkfgcolor } proc prefspage_fonts {notebook} { diff --git a/gitk-git/po/.gitattributes b/gitk-git/po/.gitattributes new file mode 100644 index 0000000000..938309e6f4 --- /dev/null +++ b/gitk-git/po/.gitattributes @@ -0,0 +1 @@ +/*.po filter=gettext-no-location diff --git a/gitk-git/po/bg.po b/gitk-git/po/bg.po index d1e7d92425..e7e2f87321 100644 --- a/gitk-git/po/bg.po +++ b/gitk-git/po/bg.po @@ -6,7 +6,7 @@ # msgid "" msgstr "" -"Project-Id-Version: gitk master\n" +"Project-Id-Version: Gitk master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-07-22 18:34+0200\n" "PO-Revision-Date: 2025-07-28 13:38+0200\n" diff --git a/gitk-git/po/ca.po b/gitk-git/po/ca.po index 87dfc18b44..d588d0990f 100644 --- a/gitk-git/po/ca.po +++ b/gitk-git/po/ca.po @@ -6,7 +6,7 @@ # msgid "" msgstr "" -"Project-Id-Version: gitk\n" +"Project-Id-Version: Gitk\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-05-17 14:32+1000\n" "PO-Revision-Date: 2015-10-05 22:23-0600\n" @@ -19,33 +19,26 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 1.8.5\n" -#: gitk:140 msgid "Couldn't get list of unmerged files:" msgstr "No s'ha pogut obtenir la llista de fitxers no fusionats:" -#: gitk:212 gitk:2381 msgid "Color words" msgstr "Colora les paraules" -#: gitk:217 gitk:2381 gitk:8220 gitk:8253 msgid "Markup words" msgstr "Marca les paraules" -#: gitk:324 msgid "Error parsing revisions:" msgstr "Error en analitzar les revisions:" -#: gitk:380 msgid "Error executing --argscmd command:" msgstr "Error en executar l'ordre --argscmd:" -#: gitk:393 msgid "No files selected: --merge specified but no files are unmerged." msgstr "" "No hi ha fitxers seleccionats: s'ha especificat --merge però cap fitxer està " "sense fusionar." -#: gitk:396 msgid "" "No files selected: --merge specified but no unmerged files are within file " "limit." @@ -53,314 +46,234 @@ msgstr "" "No hi ha fitxers seleccionats: s'ha especificat --merge però cap fitxer " "sense fusionar està dins del límit de fitxers." -#: gitk:418 gitk:566 msgid "Error executing git log:" msgstr "Error en executar git log:" -#: gitk:436 gitk:582 msgid "Reading" msgstr "Llegint" -#: gitk:496 gitk:4525 msgid "Reading commits..." msgstr "Llegint les revisions..." -#: gitk:499 gitk:1637 gitk:4528 msgid "No commits selected" msgstr "Cap comissió seleccionada" -#: gitk:1445 gitk:4045 gitk:12432 msgid "Command line" msgstr "Línia d'ordres" -#: gitk:1511 msgid "Can't parse git log output:" msgstr "No es pot analitzar la sortida del git log:" -#: gitk:1740 msgid "No commit information available" msgstr "Cap informació de comissió disponible" -#: gitk:1903 gitk:1932 gitk:4315 gitk:9669 gitk:11241 gitk:11521 msgid "OK" msgstr "D'acord" -#: gitk:1934 gitk:4317 gitk:9196 gitk:9275 gitk:9391 gitk:9440 gitk:9671 -#: gitk:11242 gitk:11522 msgid "Cancel" msgstr "Cancel·la" -#: gitk:2069 msgid "&Update" msgstr "Actualitza" -#: gitk:2070 msgid "&Reload" msgstr "Recarrega" -#: gitk:2071 msgid "Reread re&ferences" msgstr "Rellegeix les referències" -#: gitk:2072 msgid "&List references" msgstr "Llista les referències" -#: gitk:2074 msgid "Start git &gui" msgstr "Inicia el git gui" -#: gitk:2076 msgid "&Quit" msgstr "Surt" -#: gitk:2068 msgid "&File" msgstr "Fitxer" -#: gitk:2080 msgid "&Preferences" msgstr "Preferències" -#: gitk:2079 msgid "&Edit" msgstr "Edita" -#: gitk:2084 msgid "&New view..." msgstr "Vista nova..." -#: gitk:2085 msgid "&Edit view..." msgstr "Edita la vista..." -#: gitk:2086 msgid "&Delete view" msgstr "Suprimeix la vista" -#: gitk:2088 gitk:4043 msgid "&All files" msgstr "Tots els fitxers" -#: gitk:2083 gitk:4067 msgid "&View" msgstr "Vista" -#: gitk:2093 gitk:2103 gitk:3012 msgid "&About gitk" msgstr "Quant al gitk" -#: gitk:2094 gitk:2108 msgid "&Key bindings" msgstr "Associacions de tecles" -#: gitk:2092 gitk:2107 msgid "&Help" msgstr "Ajuda" -#: gitk:2185 gitk:8652 msgid "SHA1 ID:" msgstr "ID SHA1:" -#: gitk:2229 msgid "Row" msgstr "Fila" -#: gitk:2267 msgid "Find" msgstr "Cerca" -#: gitk:2295 msgid "commit" msgstr "comissió" -#: gitk:2299 gitk:2301 gitk:4687 gitk:4710 gitk:4734 gitk:6755 gitk:6827 -#: gitk:6912 msgid "containing:" msgstr "que contingui:" -#: gitk:2302 gitk:3526 gitk:3531 gitk:4763 msgid "touching paths:" msgstr "que toqui els camins:" -#: gitk:2303 gitk:4777 msgid "adding/removing string:" msgstr "que afegeixi/elimini la cadena:" -#: gitk:2304 gitk:4779 msgid "changing lines matching:" msgstr "que tingui línies canviades coincidents amb:" -#: gitk:2313 gitk:2315 gitk:4766 msgid "Exact" msgstr "Exacte" -#: gitk:2315 gitk:4854 gitk:6723 msgid "IgnCase" msgstr "Ignora majúscula i minúscula" -#: gitk:2315 gitk:4736 gitk:4852 gitk:6719 msgid "Regexp" msgstr "Regexp" -#: gitk:2317 gitk:2318 gitk:4874 gitk:4904 gitk:4911 gitk:6848 gitk:6916 msgid "All fields" msgstr "Tots els camps" -#: gitk:2318 gitk:4871 gitk:4904 gitk:6786 msgid "Headline" msgstr "Titular" -#: gitk:2319 gitk:4871 gitk:6786 gitk:6916 gitk:7389 msgid "Comments" msgstr "Comentaris" -#: gitk:2319 gitk:4871 gitk:4876 gitk:4911 gitk:6786 gitk:7324 gitk:8830 -#: gitk:8845 msgid "Author" msgstr "Autor" -#: gitk:2319 gitk:4871 gitk:6786 gitk:7326 msgid "Committer" msgstr "Comitent" -#: gitk:2350 msgid "Search" msgstr "Cerca" -#: gitk:2358 msgid "Diff" msgstr "Diferència" -#: gitk:2360 msgid "Old version" msgstr "Versió antiga" -#: gitk:2362 msgid "New version" msgstr "Versió nova" -#: gitk:2364 msgid "Lines of context" msgstr "Línies de context" -#: gitk:2374 msgid "Ignore space change" msgstr "Ignora canvis d'espai" -#: gitk:2378 gitk:2380 gitk:7959 gitk:8206 msgid "Line diff" msgstr "Diferència de línies" -#: gitk:2445 msgid "Patch" msgstr "Pedaç" -#: gitk:2447 msgid "Tree" msgstr "Arbre" -#: gitk:2617 gitk:2637 msgid "Diff this -> selected" msgstr "Diferencia aquesta -> la seleccionada" -#: gitk:2618 gitk:2638 msgid "Diff selected -> this" msgstr "Diferencia la seleccionada -> aquesta" -#: gitk:2619 gitk:2639 msgid "Make patch" msgstr "Fes pedaç" -#: gitk:2620 gitk:9254 msgid "Create tag" msgstr "Crea etiqueta" -#: gitk:2621 gitk:9371 msgid "Write commit to file" msgstr "Escriu la comissió a un fitxer" -#: gitk:2622 gitk:9428 msgid "Create new branch" msgstr "Crea una branca nova" -#: gitk:2623 msgid "Cherry-pick this commit" msgstr "Recull aquesta comissió com a cirera" -#: gitk:2624 msgid "Reset HEAD branch to here" msgstr "Restableix la branca HEAD aquí" -#: gitk:2625 msgid "Mark this commit" msgstr "Marca aquesta comissió" -#: gitk:2626 msgid "Return to mark" msgstr "Torna a la marca" -#: gitk:2627 msgid "Find descendant of this and mark" msgstr "Troba la descendent d'aquesta i marca-la" -#: gitk:2628 msgid "Compare with marked commit" msgstr "Compara amb la comissió marcada" -#: gitk:2629 gitk:2640 msgid "Diff this -> marked commit" msgstr "Diferencia aquesta -> la comissió marcada" -#: gitk:2630 gitk:2641 msgid "Diff marked commit -> this" msgstr "Diferencia la comissió seleccionada -> aquesta" -#: gitk:2631 msgid "Revert this commit" msgstr "Reverteix aquesta comissió" -#: gitk:2647 msgid "Check out this branch" msgstr "Agafa aquesta branca" -#: gitk:2648 msgid "Remove this branch" msgstr "Elimina aquesta branca" -#: gitk:2649 msgid "Copy branch name" msgstr "Copia el nom de branca" -#: gitk:2656 msgid "Highlight this too" msgstr "Ressalta aquest també" -#: gitk:2657 msgid "Highlight this only" msgstr "Ressalta només aquest" -#: gitk:2658 msgid "External diff" msgstr "Diferència externa" -#: gitk:2659 msgid "Blame parent commit" msgstr "Culpabilitat de la comissió mare" -#: gitk:2660 msgid "Copy path" msgstr "Copia el camí" -#: gitk:2667 msgid "Show origin of this line" msgstr "Mostra l'origen d'aquesta línia" -#: gitk:2668 msgid "Run git gui blame on this line" msgstr "Executa git gui blame en aquesta línia" -#: gitk:3014 msgid "" "\n" "Gitk - a commit viewer for git\n" @@ -376,317 +289,245 @@ msgstr "" "\n" "Useu-lo i redistribuïu-lo sota els termes de la Llicència Pública General GNU" -#: gitk:3022 gitk:3089 gitk:9857 msgid "Close" msgstr "Tanca" -#: gitk:3043 msgid "Gitk key bindings" msgstr "Associacions de tecles del Gitk" -#: gitk:3046 msgid "Gitk key bindings:" msgstr "Associacions de tecles del Gitk:" -#: gitk:3048 #, tcl-format msgid "<%s-Q>\t\tQuit" msgstr "<%s-Q>\t\tSurt" -#: gitk:3049 #, tcl-format msgid "<%s-W>\t\tClose window" msgstr "<%s-W>\t\tTanca la finestra" -#: gitk:3050 msgid "<Home>\t\tMove to first commit" msgstr "<Inici>\t\tVés a la primera comissió" -#: gitk:3051 msgid "<End>\t\tMove to last commit" msgstr "<Fi>\t\tVés a l'última comissió" -#: gitk:3052 msgid "<Up>, p, k\tMove up one commit" msgstr "<Amunt>, p, k\tMou-te cap amunt per una comissió" -#: gitk:3053 msgid "<Down>, n, j\tMove down one commit" msgstr "<Avall>, n, j\tMou-te cap avall per una comissió" -#: gitk:3054 msgid "<Left>, z, h\tGo back in history list" msgstr "<Esquerra>, z, h\tRetrocedeix en la llista d'història" -#: gitk:3055 msgid "<Right>, x, l\tGo forward in history list" msgstr "<Dreta>, x, l\tAvança en la llista d'història" -#: gitk:3056 #, tcl-format msgid "<%s-n>\tGo to n-th parent of current commit in history list" msgstr "" "<%s-n>\tVés a l'enèsima mare de la comissió actual en la llista d'història" -#: gitk:3057 msgid "<PageUp>\tMove up one page in commit list" msgstr "<RePàg>\tMou-te cap amunt per una pàgina en la llista de comissions" -#: gitk:3058 msgid "<PageDown>\tMove down one page in commit list" msgstr "<AvPàg>\tMou-te cap avall per una pàgina en la llista de comissions" -#: gitk:3059 #, tcl-format msgid "<%s-Home>\tScroll to top of commit list" msgstr "<%s-Inici>\tDesplaça't a la part superior de la llista de comissions" -#: gitk:3060 #, tcl-format msgid "<%s-End>\tScroll to bottom of commit list" msgstr "<%s-Fi>\tDesplaça't a la part inferior de la llista de comissions" -#: gitk:3061 #, tcl-format msgid "<%s-Up>\tScroll commit list up one line" msgstr "<%s-Amunt>\tDesplaça la llista de comissions cap amunt per una línia" -#: gitk:3062 #, tcl-format msgid "<%s-Down>\tScroll commit list down one line" msgstr "<%s-Avall>\tDesplaça la llista de comissions cap avall per una línia" -#: gitk:3063 #, tcl-format msgid "<%s-PageUp>\tScroll commit list up one page" msgstr "<%s-RePàg>\tDesplaça la llista de comissions cap amunt per una pàgina" -#: gitk:3064 #, tcl-format msgid "<%s-PageDown>\tScroll commit list down one page" msgstr "<%s-AvPàg>\tDesplaça la llista de comissions cap avall per una pàgina" -#: gitk:3065 msgid "<Shift-Up>\tFind backwards (upwards, later commits)" msgstr "<Maj-Amunt>\tCerca cap enrere (cap amunt, les comissions més noves)" -#: gitk:3066 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)" msgstr "<Maj-Avall>\tCerca cap endavant (cap avall, les comissions més velles)" -#: gitk:3067 msgid "<Delete>, b\tScroll diff view up one page" msgstr "<Supr>, b\tDesplaça la vista de diferència cap amunt per una pàgina" -#: gitk:3068 msgid "<Backspace>\tScroll diff view up one page" msgstr "<Retrocés>\tDesplaça la vista de diferència cap amunt per una pàgina" -#: gitk:3069 msgid "<Space>\t\tScroll diff view down one page" msgstr "<Espai>\t\tDesplaça la vista de diferència cap avall per una pàgina" -#: gitk:3070 msgid "u\t\tScroll diff view up 18 lines" msgstr "u\t\tDesplaça la vista de diferència cap amunt per 18 línies" -#: gitk:3071 msgid "d\t\tScroll diff view down 18 lines" msgstr "d\t\tDesplaça la vista de diferència cap avall per 18 línies" -#: gitk:3072 #, tcl-format msgid "<%s-F>\t\tFind" msgstr "<%s-F>\t\tCerca" -#: gitk:3073 #, tcl-format msgid "<%s-G>\t\tMove to next find hit" msgstr "<%s-G>\t\tMou-te a la propera coincidència de la cerca" -#: gitk:3074 msgid "<Return>\tMove to next find hit" msgstr "<Retorn>\tMou-te a la propera coincidència de la cerca" -#: gitk:3075 msgid "g\t\tGo to commit" msgstr "g\t\tVés a l'última comissió" -#: gitk:3076 msgid "/\t\tFocus the search box" msgstr "/\t\tPosa el focus a la caixa de cerca" -#: gitk:3077 msgid "?\t\tMove to previous find hit" msgstr "?\t\tMou a la coincidència prèvia de la cerca" -#: gitk:3078 msgid "f\t\tScroll diff view to next file" msgstr "f\t\tDesplaça la vista de diferència al proper fitxer" -#: gitk:3079 #, tcl-format msgid "<%s-S>\t\tSearch for next hit in diff view" msgstr "<%s-S>\t\tCerca la propera coincidència en la vista de diferència" -#: gitk:3080 #, tcl-format msgid "<%s-R>\t\tSearch for previous hit in diff view" msgstr "<%s-R>\t\tCerca la coincidència prèvia en la vista de diferència" -#: gitk:3081 #, tcl-format msgid "<%s-KP+>\tIncrease font size" msgstr "<%s-KP+>\tAugmenta la mida de lletra" -#: gitk:3082 #, tcl-format msgid "<%s-plus>\tIncrease font size" msgstr "<%s-més>\tAugmenta la mida de lletra" -#: gitk:3083 #, tcl-format msgid "<%s-KP->\tDecrease font size" msgstr "<%s-KP->\tDisminueix la mida de lletra" -#: gitk:3084 #, tcl-format msgid "<%s-minus>\tDecrease font size" msgstr "<%s-menys>\tDisminueix la mida de lletra" -#: gitk:3085 msgid "<F5>\t\tUpdate" msgstr "<F5>\t\tActualitza" -#: gitk:3550 gitk:3559 #, tcl-format msgid "Error creating temporary directory %s:" msgstr "Error en crear el directori temporal %s:" -#: gitk:3572 #, tcl-format msgid "Error getting \"%s\" from %s:" msgstr "Error en obtenir \"%s\" de %s:" -#: gitk:3635 msgid "command failed:" msgstr "l'ordre ha fallat:" -#: gitk:3784 msgid "No such commit" msgstr "Cap comissió així" -#: gitk:3798 msgid "git gui blame: command failed:" msgstr "git gui blame: l'ordre ha fallat:" -#: gitk:3829 #, tcl-format msgid "Couldn't read merge head: %s" msgstr "No s'ha pogut llegir el cap de fusió: %s" -#: gitk:3837 #, tcl-format msgid "Error reading index: %s" msgstr "Error en llegir l'índex: %s" -#: gitk:3862 #, tcl-format msgid "Couldn't start git blame: %s" msgstr "No s'ha pogut iniciar el git blame: %s" -#: gitk:3865 gitk:6754 msgid "Searching" msgstr "Cercant" -#: gitk:3897 #, tcl-format msgid "Error running git blame: %s" msgstr "Error en executar el git blame: %s" -#: gitk:3925 #, tcl-format msgid "That line comes from commit %s, which is not in this view" msgstr "" "Aquella línia ve de la comissió %s, la qual no és en aquesta visualització" -#: gitk:3939 msgid "External diff viewer failed:" msgstr "El visualitzador de diferència extern ha fallat:" -#: gitk:4070 msgid "Gitk view definition" msgstr "Definició de vista del Gitk" -#: gitk:4074 msgid "Remember this view" msgstr "Recorda aquesta vista" -#: gitk:4075 msgid "References (space separated list):" msgstr "Referències (llista separada per espais)" -#: gitk:4076 msgid "Branches & tags:" msgstr "Branques i etiquetes:" -#: gitk:4077 msgid "All refs" msgstr "Totes les referències" -#: gitk:4078 msgid "All (local) branches" msgstr "Totes les branques (locals)" -#: gitk:4079 msgid "All tags" msgstr "Totes les etiquetes" -#: gitk:4080 msgid "All remote-tracking branches" msgstr "Totes les branques amb seguiment remot" -#: gitk:4081 msgid "Commit Info (regular expressions):" msgstr "Informació de comissió (expressions regulars):" -#: gitk:4082 msgid "Author:" msgstr "Autor:" -#: gitk:4083 msgid "Committer:" msgstr "Comitent:" -#: gitk:4084 msgid "Commit Message:" msgstr "Missatge de comissió:" -#: gitk:4085 msgid "Matches all Commit Info criteria" msgstr "Coincideix amb tots els criteris d'informació de comissió" -#: gitk:4086 msgid "Matches no Commit Info criteria" msgstr "No coincideix amb cap criteri d'informació de comissió" -#: gitk:4087 msgid "Changes to Files:" msgstr "Canvis als fitxers:" -#: gitk:4088 msgid "Fixed String" msgstr "Cadena fixa" -#: gitk:4089 msgid "Regular Expression" msgstr "Expressió regular" -#: gitk:4090 msgid "Search string:" msgstr "Cadena de cerca:" -#: gitk:4091 msgid "" "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" @@ -694,201 +535,153 @@ msgstr "" "Dates de comissió (\"fa 2 setmanes\", \"2009-03-17 15:27:38\", \"17 abr 2009 " "15:27:38\"):" -#: gitk:4092 msgid "Since:" msgstr "Des de:" -#: gitk:4093 msgid "Until:" msgstr "Fins:" -#: gitk:4094 msgid "Limit and/or skip a number of revisions (positive integer):" msgstr "Limita o salta un nombre de revisions (nombre enter positiu)" -#: gitk:4095 msgid "Number to show:" msgstr "Nombre a mostrar:" -#: gitk:4096 msgid "Number to skip:" msgstr "Nombre a saltar:" -#: gitk:4097 msgid "Miscellaneous options:" msgstr "Opcions miscel·lànies:" -#: gitk:4098 msgid "Strictly sort by date" msgstr "Ordena estrictament per data" -#: gitk:4099 msgid "Mark branch sides" msgstr "Marca els costats de les branques" -#: gitk:4100 msgid "Limit to first parent" msgstr "Limita a la primera mare" -#: gitk:4101 msgid "Simple history" msgstr "Història senzilla" -#: gitk:4102 msgid "Additional arguments to git log:" msgstr "Paràmetres addicionals al git log:" -#: gitk:4103 msgid "Enter files and directories to include, one per line:" msgstr "Introduïu els fitxers i directoris a incloure, un per línia:" -#: gitk:4104 msgid "Command to generate more commits to include:" msgstr "Ordre per a generar més comissions a incloure:" -#: gitk:4228 msgid "Gitk: edit view" msgstr "Gitk: vista d'edició" -#: gitk:4236 msgid "-- criteria for selecting revisions" msgstr "-- criteris per a seleccionar les revisions" -#: gitk:4241 msgid "View Name" msgstr "Nom de vista" -#: gitk:4316 msgid "Apply (F5)" msgstr "Aplica (F5)" -#: gitk:4354 msgid "Error in commit selection arguments:" msgstr "Error en els paràmetres de selecció de comissions:" -#: gitk:4409 gitk:4462 gitk:4924 gitk:4938 gitk:6208 gitk:12373 gitk:12374 msgid "None" msgstr "Cap" -#: gitk:5021 gitk:5026 msgid "Descendant" msgstr "Descendent" -#: gitk:5022 msgid "Not descendant" msgstr "No descendent" -#: gitk:5029 gitk:5034 msgid "Ancestor" msgstr "Avantpassat" -#: gitk:5030 msgid "Not ancestor" msgstr "No avantpassat" -#: gitk:5324 msgid "Local changes checked in to index but not committed" msgstr "Canvis locals registrats en l'índex però no comesos" -#: gitk:5360 msgid "Local uncommitted changes, not checked in to index" msgstr "Canvis locals sense cometre, no registrats en l'índex" -#: gitk:7134 msgid "and many more" msgstr "i moltes més" -#: gitk:7137 msgid "many" msgstr "moltes" -#: gitk:7328 msgid "Tags:" msgstr "Etiquetes:" -#: gitk:7345 gitk:7351 gitk:8825 msgid "Parent" msgstr "Mare" -#: gitk:7356 msgid "Child" msgstr "Filla" -#: gitk:7365 msgid "Branch" msgstr "Branca" -#: gitk:7368 msgid "Follows" msgstr "Segueix" -#: gitk:7371 msgid "Precedes" msgstr "Precedeix" -#: gitk:7966 #, tcl-format msgid "Error getting diffs: %s" msgstr "Error en obtenir les diferències: %s" -#: gitk:8650 msgid "Goto:" msgstr "Vés a:" -#: gitk:8671 #, tcl-format msgid "Short SHA1 id %s is ambiguous" msgstr "L'id SHA1 curta %s és ambigua" -#: gitk:8678 #, tcl-format msgid "Revision %s is not known" msgstr "La revisió %s és desconeguda" -#: gitk:8688 #, tcl-format msgid "SHA1 id %s is not known" msgstr "L'id SHA1 %s és desconeguda" -#: gitk:8690 #, tcl-format msgid "Revision %s is not in the current view" msgstr "La revisió %s no és en la vista actual" -#: gitk:8832 gitk:8847 msgid "Date" msgstr "Data" -#: gitk:8835 msgid "Children" msgstr "Filles" -#: gitk:8898 #, tcl-format msgid "Reset %s branch to here" msgstr "Restableix la branca %s aquí" -#: gitk:8900 msgid "Detached head: can't reset" msgstr "Cap separat: no es pot restablir" -#: gitk:9005 gitk:9011 msgid "Skipping merge commit " msgstr "Saltant la comissió de fusió " -#: gitk:9020 gitk:9025 msgid "Error getting patch ID for " msgstr "Error en obtenir l'ID de pedaç de " -#: gitk:9021 gitk:9026 msgid " - stopping\n" msgstr " - aturant\n" -#: gitk:9031 gitk:9034 gitk:9042 gitk:9056 gitk:9065 msgid "Commit " msgstr "Comissió " -#: gitk:9035 msgid "" " is the same patch as\n" " " @@ -896,7 +689,6 @@ msgstr "" " és el mateix pedaç que\n" " " -#: gitk:9043 msgid "" " differs from\n" " " @@ -904,7 +696,6 @@ msgstr "" " difereix de\n" " " -#: gitk:9045 msgid "" "Diff of commits:\n" "\n" @@ -912,131 +703,101 @@ msgstr "" "Diferència entre comissions:\n" "\n" -#: gitk:9057 gitk:9066 #, tcl-format msgid " has %s children - stopping\n" msgstr " té %s filles - aturant\n" -#: gitk:9085 #, tcl-format msgid "Error writing commit to file: %s" msgstr "Error en escriure la comissió al fitxer: %s" -#: gitk:9091 #, tcl-format msgid "Error diffing commits: %s" msgstr "Error en diferenciar les comissions: %s" -#: gitk:9137 msgid "Top" msgstr "Part superior" -#: gitk:9138 msgid "From" msgstr "De" -#: gitk:9143 msgid "To" msgstr "A" -#: gitk:9167 msgid "Generate patch" msgstr "Genera pedaç" -#: gitk:9169 msgid "From:" msgstr "De:" -#: gitk:9178 msgid "To:" msgstr "A:" -#: gitk:9187 msgid "Reverse" msgstr "Inverteix" -#: gitk:9189 gitk:9385 msgid "Output file:" msgstr "Fitxer de sortida:" -#: gitk:9195 msgid "Generate" msgstr "Genera" -#: gitk:9233 msgid "Error creating patch:" msgstr "Error en crear el pedaç:" -#: gitk:9256 gitk:9373 gitk:9430 msgid "ID:" msgstr "ID:" -#: gitk:9265 msgid "Tag name:" msgstr "Nom d'etiqueta:" -#: gitk:9268 msgid "Tag message is optional" msgstr "El missatge d'etiqueta és opcional" -#: gitk:9270 msgid "Tag message:" msgstr "Missatge d'etiqueta:" -#: gitk:9274 gitk:9439 msgid "Create" msgstr "Crea" -#: gitk:9292 msgid "No tag name specified" msgstr "No s'ha especificat cap nom d'etiqueta" -#: gitk:9296 #, tcl-format msgid "Tag \"%s\" already exists" msgstr "L'etiqueta \"%s\" ja existeix" -#: gitk:9306 msgid "Error creating tag:" msgstr "Error en crear l'etiqueta:" -#: gitk:9382 msgid "Command:" msgstr "Ordre:" -#: gitk:9390 msgid "Write" msgstr "Escriu" -#: gitk:9408 msgid "Error writing commit:" msgstr "Error en escriure la comissió:" -#: gitk:9435 msgid "Name:" msgstr "Nom:" -#: gitk:9458 msgid "Please specify a name for the new branch" msgstr "Si us plau, especifiqueu un nom per a la branca nova" -#: gitk:9463 #, tcl-format msgid "Branch '%s' already exists. Overwrite?" msgstr "La branca '%s' ja existeix. Voleu sobreescriure?" -#: gitk:9530 #, tcl-format msgid "Commit %s is already included in branch %s -- really re-apply it?" msgstr "" "La comissió %s ja està inclosa en la branca %s -- realment voleu tornar a " "aplicar-la?" -#: gitk:9535 msgid "Cherry-picking" msgstr "Recollint cireres" -#: gitk:9544 #, tcl-format msgid "" "Cherry-pick failed because of local changes to file '%s'.\n" @@ -1046,7 +807,6 @@ msgstr "" "Si us plau, cometeu, restabliu o emmagatzemeu els vostres canvis i torneu a " "intentar." -#: gitk:9550 msgid "" "Cherry-pick failed because of merge conflict.\n" "Do you wish to run git citool to resolve it?" @@ -1054,21 +814,17 @@ msgstr "" "El recull de cireres ha fallat a causa d'un conflicte de fusió.\n" "Voleu executar el git citool per a resoldre'l?" -#: gitk:9566 gitk:9624 msgid "No changes committed" msgstr "Cap canvi comès" -#: gitk:9593 #, tcl-format msgid "Commit %s is not included in branch %s -- really revert it?" msgstr "" "La comissió %s no s'inclou en la branca %s -- realment voleu revertir-la?" -#: gitk:9598 msgid "Reverting" msgstr "Revertint" -#: gitk:9606 #, tcl-format msgid "" "Revert failed because of local changes to the following files:%s Please " @@ -1078,7 +834,6 @@ msgstr "" "plau, cometeu, restabliu o emmagatzemeu els vostres canvis i torneu-ho a " "intentar." -#: gitk:9610 msgid "" "Revert failed because of merge conflict.\n" " Do you wish to run git citool to resolve it?" @@ -1086,28 +841,22 @@ msgstr "" "La reversió ha fallat a causa d'un conflicte de fusió.\n" " Voleu executar el git citool per a resoldre'l?" -#: gitk:9653 msgid "Confirm reset" msgstr "Confirma el restabliment" -#: gitk:9655 #, tcl-format msgid "Reset branch %s to %s?" msgstr "Voleu restablir la branca %s a %s?" -#: gitk:9657 msgid "Reset type:" msgstr "Tipus de restabliment:" -#: gitk:9660 msgid "Soft: Leave working tree and index untouched" msgstr "Suau: Deixa l'arbre de treball i l'índex sense tocar" -#: gitk:9663 msgid "Mixed: Leave working tree untouched, reset index" msgstr "Mixt: Deixa l'arbre de treball sense tocar, restableix l'índex" -#: gitk:9666 msgid "" "Hard: Reset working tree and index\n" "(discard ALL local changes)" @@ -1115,19 +864,15 @@ msgstr "" "Dur: Restableix l'arbre de treball i l'índex\n" "(descarta TOTS els canvis locals)" -#: gitk:9683 msgid "Resetting" msgstr "Restablint" -#: gitk:9743 msgid "Checking out" msgstr "Agafant" -#: gitk:9796 msgid "Cannot delete the currently checked-out branch" msgstr "No es pot suprimir la branca actualment agafada" -#: gitk:9802 #, tcl-format msgid "" "The commits on branch %s aren't on any other branch.\n" @@ -1136,16 +881,13 @@ msgstr "" "Les comissions en la branca %s no són en cap altra branca.\n" "Realment voleu suprimir la branca %s?" -#: gitk:9833 #, tcl-format msgid "Tags and heads: %s" msgstr "Etiquetes i caps: %s" -#: gitk:9850 msgid "Filter" msgstr "Filtre" -#: gitk:10146 msgid "" "Error reading commit topology information; branch and preceding/following " "tag information will be incomplete." @@ -1153,201 +895,152 @@ msgstr "" "Error en llegir la informació de topologia de comissió; la informació sobre " "branques i etiquetes precedents/següents serà incompleta." -#: gitk:11123 msgid "Tag" msgstr "Etiqueta" -#: gitk:11127 msgid "Id" msgstr "Id" -#: gitk:11210 msgid "Gitk font chooser" msgstr "Selector de tipus de lletra del Gitk" -#: gitk:11227 msgid "B" msgstr "B" -#: gitk:11230 msgid "I" msgstr "I" -#: gitk:11348 msgid "Commit list display options" msgstr "Opcions de visualització de la llista de comissions" -#: gitk:11351 msgid "Maximum graph width (lines)" msgstr "Amplada màxima del gràfic (línies)" -#: gitk:11355 #, no-tcl-format msgid "Maximum graph width (% of pane)" msgstr "Amplada màxima del gràfic (% del panell)" -#: gitk:11358 msgid "Show local changes" msgstr "Mostra els canvis locals" -#: gitk:11361 msgid "Auto-select SHA1 (length)" msgstr "Selecciona automàticament l'SHA1 (longitud)" -#: gitk:11365 msgid "Hide remote refs" msgstr "Amaga les referències remotes" -#: gitk:11369 msgid "Diff display options" msgstr "Opcions de visualització de diferència" -#: gitk:11371 msgid "Tab spacing" msgstr "Espaiat de tabulació" -#: gitk:11374 msgid "Display nearby tags/heads" msgstr "Mostra etiquetes/caps propers" -#: gitk:11377 msgid "Maximum # tags/heads to show" msgstr "Nombre màxim d'etiquetes/caps a mostrar" -#: gitk:11380 msgid "Limit diffs to listed paths" msgstr "Limita les diferències als camins llistats" -#: gitk:11383 msgid "Support per-file encodings" msgstr "Admet codificacions específiques per a cada fitxer" -#: gitk:11389 gitk:11536 msgid "External diff tool" msgstr "Eina de diferència externa" -#: gitk:11390 msgid "Choose..." msgstr "Trieu..." -#: gitk:11395 msgid "General options" msgstr "Opcions generals" -#: gitk:11398 msgid "Use themed widgets" msgstr "Usa els ginys tematitzats" -#: gitk:11400 msgid "(change requires restart)" msgstr "(el canvi requereix reiniciar)" -#: gitk:11402 msgid "(currently unavailable)" msgstr "(actualment no disponible)" -#: gitk:11413 msgid "Colors: press to choose" msgstr "Colors: pressiona per a triar" -#: gitk:11416 msgid "Interface" msgstr "Interfície" -#: gitk:11417 msgid "interface" msgstr "interfície" -#: gitk:11420 msgid "Background" msgstr "Fons" -#: gitk:11421 gitk:11451 msgid "background" msgstr "fons" -#: gitk:11424 msgid "Foreground" msgstr "Primer pla" -#: gitk:11425 msgid "foreground" msgstr "primer pla" -#: gitk:11428 msgid "Diff: old lines" msgstr "Diferència: línies velles" -#: gitk:11429 msgid "diff old lines" msgstr "diferencia les línies velles" -#: gitk:11433 msgid "Diff: new lines" msgstr "Diferència: línies noves" -#: gitk:11434 msgid "diff new lines" msgstr "diferencia les línies noves" -#: gitk:11438 msgid "Diff: hunk header" msgstr "Diferència: capçalera de tros" -#: gitk:11440 msgid "diff hunk header" msgstr "diferencia la capçalera de tros" -#: gitk:11444 msgid "Marked line bg" msgstr "Fons de la línia marcada" -#: gitk:11446 msgid "marked line background" msgstr "fons de la línia marcada" -#: gitk:11450 msgid "Select bg" msgstr "Fons de la selecció" -#: gitk:11459 msgid "Fonts: press to choose" msgstr "Tipus de lletra: pressiona per a triar" -#: gitk:11461 msgid "Main font" msgstr "Tipus de lletra principal" -#: gitk:11462 msgid "Diff display font" msgstr "Tipus de lletra de visualització de diferència" -#: gitk:11463 msgid "User interface font" msgstr "Tipus de lletra de la interfície d'usuari" -#: gitk:11485 msgid "Gitk preferences" msgstr "Preferències del Gitk" -#: gitk:11494 msgid "General" msgstr "General" -#: gitk:11495 msgid "Colors" msgstr "Colors" -#: gitk:11496 msgid "Fonts" msgstr "Tipus de lletra" -#: gitk:11546 #, tcl-format msgid "Gitk: choose color for %s" msgstr "Gitk: tria el color per a %s" -#: gitk:12059 msgid "" "Sorry, gitk cannot run with this version of Tcl/Tk.\n" " Gitk requires at least Tcl/Tk 8.4." @@ -1355,15 +1048,12 @@ msgstr "" "Perdó, el gitk no pot executar-se amb aquesta versió de Tcl/Tk.\n" " El Gitk requereix com a mínim el Tcl/Tk 8.4." -#: gitk:12269 msgid "Cannot find a git repository here." msgstr "No es pot trobar cap dipòsit de git aquí." -#: gitk:12316 #, tcl-format msgid "Ambiguous argument '%s': both revision and filename" msgstr "Paràmetre ambigu '%s': és tant revisió com nom de fitxer" -#: gitk:12328 msgid "Bad arguments to gitk:" msgstr "Paràmetres dolents al gitk:" diff --git a/gitk-git/po/de.po b/gitk-git/po/de.po index 5db3824828..fea18fa16e 100644 --- a/gitk-git/po/de.po +++ b/gitk-git/po/de.po @@ -6,7 +6,7 @@ # Frederik Schwarzer <schwarzerf@gmail.com>, 2008. msgid "" msgstr "" -"Project-Id-Version: git-gui\n" +"Project-Id-Version: Gitk\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-05-17 14:32+1000\n" "PO-Revision-Date: 2015-10-20 14:20+0200\n" @@ -17,33 +17,26 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: gitk:140 msgid "Couldn't get list of unmerged files:" msgstr "Liste der nicht zusammengeführten Dateien nicht gefunden:" -#: gitk:212 gitk:2381 msgid "Color words" msgstr "Wörter einfärben" -#: gitk:217 gitk:2381 gitk:8220 gitk:8253 msgid "Markup words" msgstr "Wörter kennzeichnen" -#: gitk:324 msgid "Error parsing revisions:" msgstr "Fehler beim Laden der Versionen:" -#: gitk:380 msgid "Error executing --argscmd command:" msgstr "Fehler beim Ausführen des --argscmd-Kommandos:" -#: gitk:393 msgid "No files selected: --merge specified but no files are unmerged." msgstr "" "Keine Dateien ausgewählt: Es wurde --merge angegeben, aber es existieren " "keine nicht zusammengeführten Dateien." -#: gitk:396 msgid "" "No files selected: --merge specified but no unmerged files are within file " "limit." @@ -51,314 +44,234 @@ msgstr "" "Keine Dateien ausgewählt: Es wurde --merge angegeben, aber es sind keine " "nicht zusammengeführten Dateien in der Dateiauswahl." -#: gitk:418 gitk:566 msgid "Error executing git log:" msgstr "Fehler beim Ausführen von »git log«:" -#: gitk:436 gitk:582 msgid "Reading" msgstr "Lesen" -#: gitk:496 gitk:4525 msgid "Reading commits..." msgstr "Versionen werden gelesen ..." -#: gitk:499 gitk:1637 gitk:4528 msgid "No commits selected" msgstr "Keine Versionen ausgewählt" -#: gitk:1445 gitk:4045 gitk:12432 msgid "Command line" msgstr "Kommandozeile" -#: gitk:1511 msgid "Can't parse git log output:" msgstr "Ausgabe von »git log« kann nicht erkannt werden:" -#: gitk:1740 msgid "No commit information available" msgstr "Keine Versionsinformation verfügbar" -#: gitk:1903 gitk:1932 gitk:4315 gitk:9669 gitk:11241 gitk:11521 msgid "OK" msgstr "Ok" -#: gitk:1934 gitk:4317 gitk:9196 gitk:9275 gitk:9391 gitk:9440 gitk:9671 -#: gitk:11242 gitk:11522 msgid "Cancel" msgstr "Abbrechen" -#: gitk:2069 msgid "&Update" msgstr "&Aktualisieren" -#: gitk:2070 msgid "&Reload" msgstr "&Neu laden" -#: gitk:2071 msgid "Reread re&ferences" msgstr "&Zweige neu laden" -#: gitk:2072 msgid "&List references" msgstr "Zweige/Markierungen auf&listen" -#: gitk:2074 msgid "Start git &gui" msgstr "»git &gui« starten" -#: gitk:2076 msgid "&Quit" msgstr "&Beenden" -#: gitk:2068 msgid "&File" msgstr "&Datei" -#: gitk:2080 msgid "&Preferences" msgstr "&Einstellungen" -#: gitk:2079 msgid "&Edit" msgstr "&Bearbeiten" -#: gitk:2084 msgid "&New view..." msgstr "&Neue Ansicht ..." -#: gitk:2085 msgid "&Edit view..." msgstr "Ansicht &bearbeiten ..." -#: gitk:2086 msgid "&Delete view" msgstr "Ansicht &entfernen" -#: gitk:2088 gitk:4043 msgid "&All files" msgstr "&Alle Dateien" -#: gitk:2083 gitk:4067 msgid "&View" msgstr "&Ansicht" -#: gitk:2093 gitk:2103 gitk:3012 msgid "&About gitk" msgstr "Über &gitk" -#: gitk:2094 gitk:2108 msgid "&Key bindings" msgstr "&Tastenkürzel" -#: gitk:2092 gitk:2107 msgid "&Help" msgstr "&Hilfe" -#: gitk:2185 gitk:8652 msgid "SHA1 ID:" msgstr "SHA1 ID:" -#: gitk:2229 msgid "Row" msgstr "Zeile" -#: gitk:2267 msgid "Find" msgstr "Suche" -#: gitk:2295 msgid "commit" msgstr "Version nach" -#: gitk:2299 gitk:2301 gitk:4687 gitk:4710 gitk:4734 gitk:6755 gitk:6827 -#: gitk:6912 msgid "containing:" msgstr "Beschreibung:" -#: gitk:2302 gitk:3526 gitk:3531 gitk:4763 msgid "touching paths:" msgstr "Dateien:" -#: gitk:2303 gitk:4777 msgid "adding/removing string:" msgstr "Änderungen:" -#: gitk:2304 gitk:4779 msgid "changing lines matching:" msgstr "Geänderte Zeilen entsprechen:" -#: gitk:2313 gitk:2315 gitk:4766 msgid "Exact" msgstr "Exakt" -#: gitk:2315 gitk:4854 gitk:6723 msgid "IgnCase" msgstr "Kein Groß/Klein" -#: gitk:2315 gitk:4736 gitk:4852 gitk:6719 msgid "Regexp" msgstr "Regexp" -#: gitk:2317 gitk:2318 gitk:4874 gitk:4904 gitk:4911 gitk:6848 gitk:6916 msgid "All fields" msgstr "Alle Felder" -#: gitk:2318 gitk:4871 gitk:4904 gitk:6786 msgid "Headline" msgstr "Überschrift" -#: gitk:2319 gitk:4871 gitk:6786 gitk:6916 gitk:7389 msgid "Comments" msgstr "Beschreibung" -#: gitk:2319 gitk:4871 gitk:4876 gitk:4911 gitk:6786 gitk:7324 gitk:8830 -#: gitk:8845 msgid "Author" msgstr "Autor" -#: gitk:2319 gitk:4871 gitk:6786 gitk:7326 msgid "Committer" msgstr "Eintragender" -#: gitk:2350 msgid "Search" msgstr "Suchen" -#: gitk:2358 msgid "Diff" msgstr "Vergleich" -#: gitk:2360 msgid "Old version" msgstr "Alte Version" -#: gitk:2362 msgid "New version" msgstr "Neue Version" -#: gitk:2364 msgid "Lines of context" msgstr "Kontextzeilen" -#: gitk:2374 msgid "Ignore space change" msgstr "Leerzeichenänderungen ignorieren" -#: gitk:2378 gitk:2380 gitk:7959 gitk:8206 msgid "Line diff" msgstr "Zeilenunterschied" -#: gitk:2445 msgid "Patch" msgstr "Patch" -#: gitk:2447 msgid "Tree" msgstr "Baum" -#: gitk:2617 gitk:2637 msgid "Diff this -> selected" msgstr "Vergleich: diese -> gewählte" -#: gitk:2618 gitk:2638 msgid "Diff selected -> this" msgstr "Vergleich: gewählte -> diese" -#: gitk:2619 gitk:2639 msgid "Make patch" msgstr "Patch erstellen" -#: gitk:2620 gitk:9254 msgid "Create tag" msgstr "Markierung erstellen" -#: gitk:2621 gitk:9371 msgid "Write commit to file" msgstr "Version in Datei schreiben" -#: gitk:2622 gitk:9428 msgid "Create new branch" msgstr "Neuen Zweig erstellen" -#: gitk:2623 msgid "Cherry-pick this commit" msgstr "Diese Version pflücken" -#: gitk:2624 msgid "Reset HEAD branch to here" msgstr "HEAD-Zweig auf diese Version zurücksetzen" -#: gitk:2625 msgid "Mark this commit" msgstr "Lesezeichen setzen" -#: gitk:2626 msgid "Return to mark" msgstr "Zum Lesezeichen" -#: gitk:2627 msgid "Find descendant of this and mark" msgstr "Abkömmling von Lesezeichen und dieser Version finden" -#: gitk:2628 msgid "Compare with marked commit" msgstr "Mit Lesezeichen vergleichen" -#: gitk:2629 gitk:2640 msgid "Diff this -> marked commit" msgstr "Vergleich: diese -> gewählte Version" -#: gitk:2630 gitk:2641 msgid "Diff marked commit -> this" msgstr "Vergleich: gewählte -> diese Version" -#: gitk:2631 msgid "Revert this commit" msgstr "Version umkehren" -#: gitk:2647 msgid "Check out this branch" msgstr "Auf diesen Zweig umstellen" -#: gitk:2648 msgid "Remove this branch" msgstr "Zweig löschen" -#: gitk:2649 msgid "Copy branch name" msgstr "Zweigname kopieren" -#: gitk:2656 msgid "Highlight this too" msgstr "Diesen auch hervorheben" -#: gitk:2657 msgid "Highlight this only" msgstr "Nur diesen hervorheben" -#: gitk:2658 msgid "External diff" msgstr "Externes Diff-Programm" -#: gitk:2659 msgid "Blame parent commit" msgstr "Annotieren der Elternversion" -#: gitk:2660 msgid "Copy path" msgstr "Pfad kopieren" -#: gitk:2667 msgid "Show origin of this line" msgstr "Herkunft dieser Zeile anzeigen" -#: gitk:2668 msgid "Run git gui blame on this line" msgstr "Diese Zeile annotieren (»git gui blame«)" -#: gitk:3014 msgid "" "\n" "Gitk - a commit viewer for git\n" @@ -375,517 +288,397 @@ msgstr "" "Benutzung und Weiterverbreitung gemäß den Bedingungen der GNU General Public " "License" -#: gitk:3022 gitk:3089 gitk:9857 msgid "Close" msgstr "Schließen" -#: gitk:3043 msgid "Gitk key bindings" msgstr "Gitk-Tastaturbelegung" -#: gitk:3046 msgid "Gitk key bindings:" msgstr "Gitk-Tastaturbelegung:" -#: gitk:3048 #, tcl-format msgid "<%s-Q>\t\tQuit" msgstr "<%s-Q>\t\tBeenden" -#: gitk:3049 #, tcl-format msgid "<%s-W>\t\tClose window" msgstr "<%s-F>\t\tFenster schließen" -#: gitk:3050 msgid "<Home>\t\tMove to first commit" msgstr "<Pos1>\t\tZur neuesten Version springen" -#: gitk:3051 msgid "<End>\t\tMove to last commit" msgstr "<Ende>\t\tZur ältesten Version springen" -#: gitk:3052 msgid "<Up>, p, k\tMove up one commit" msgstr "<Hoch>, p, k\tNächste neuere Version" -#: gitk:3053 msgid "<Down>, n, j\tMove down one commit" msgstr "<Runter>, n, j\tNächste ältere Version" -#: gitk:3054 msgid "<Left>, z, h\tGo back in history list" msgstr "<Links>, z, h\tEine Version zurückgehen" -#: gitk:3055 msgid "<Right>, x, l\tGo forward in history list" msgstr "<Rechts>, x, l\tEine Version weitergehen" -#: gitk:3056 #, tcl-format msgid "<%s-n>\tGo to n-th parent of current commit in history list" msgstr "<%s-n>\tZu n-ter Elternversion in Versionshistorie springen" -#: gitk:3057 msgid "<PageUp>\tMove up one page in commit list" msgstr "<BildHoch>\tEine Seite nach oben blättern" -#: gitk:3058 msgid "<PageDown>\tMove down one page in commit list" msgstr "<BildRunter>\tEine Seite nach unten blättern" -#: gitk:3059 #, tcl-format msgid "<%s-Home>\tScroll to top of commit list" msgstr "<%s-Pos1>\tZum oberen Ende der Versionsliste blättern" -#: gitk:3060 #, tcl-format msgid "<%s-End>\tScroll to bottom of commit list" msgstr "<%s-Ende>\tZum unteren Ende der Versionsliste blättern" -#: gitk:3061 #, tcl-format msgid "<%s-Up>\tScroll commit list up one line" msgstr "<%s-Hoch>\tVersionsliste eine Zeile nach oben blättern" -#: gitk:3062 #, tcl-format msgid "<%s-Down>\tScroll commit list down one line" msgstr "<%s-Runter>\tVersionsliste eine Zeile nach unten blättern" -#: gitk:3063 #, tcl-format msgid "<%s-PageUp>\tScroll commit list up one page" msgstr "<%s-BildHoch>\tVersionsliste eine Seite nach oben blättern" -#: gitk:3064 #, tcl-format msgid "<%s-PageDown>\tScroll commit list down one page" msgstr "<%s-BildRunter>\tVersionsliste eine Seite nach unten blättern" -#: gitk:3065 msgid "<Shift-Up>\tFind backwards (upwards, later commits)" msgstr "<Umschalt-Hoch>\tRückwärts suchen (nach oben; neuere Versionen)" -#: gitk:3066 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)" msgstr "<Umschalt-Runter> Suchen (nach unten; ältere Versionen)" -#: gitk:3067 msgid "<Delete>, b\tScroll diff view up one page" msgstr "<Entf>, b\t\tVergleich eine Seite nach oben blättern" -#: gitk:3068 msgid "<Backspace>\tScroll diff view up one page" msgstr "<Löschtaste>\tVergleich eine Seite nach oben blättern" -#: gitk:3069 msgid "<Space>\t\tScroll diff view down one page" msgstr "<Leertaste>\tVergleich eine Seite nach unten blättern" -#: gitk:3070 msgid "u\t\tScroll diff view up 18 lines" msgstr "u\t\tVergleich um 18 Zeilen nach oben blättern" -#: gitk:3071 msgid "d\t\tScroll diff view down 18 lines" msgstr "d\t\tVergleich um 18 Zeilen nach unten blättern" -#: gitk:3072 #, tcl-format msgid "<%s-F>\t\tFind" msgstr "<%s-F>\t\tSuchen" -#: gitk:3073 #, tcl-format msgid "<%s-G>\t\tMove to next find hit" msgstr "<%s-G>\t\tWeitersuchen" -#: gitk:3074 msgid "<Return>\tMove to next find hit" msgstr "<Eingabetaste>\tWeitersuchen" -#: gitk:3075 msgid "g\t\tGo to commit" msgstr "g\t\tZu Version springen" -#: gitk:3076 msgid "/\t\tFocus the search box" msgstr "/\t\tTastaturfokus ins Suchfeld" -#: gitk:3077 msgid "?\t\tMove to previous find hit" msgstr "?\t\tRückwärts weitersuchen" -#: gitk:3078 msgid "f\t\tScroll diff view to next file" msgstr "f\t\tVergleich zur nächsten Datei blättern" -#: gitk:3079 #, tcl-format msgid "<%s-S>\t\tSearch for next hit in diff view" msgstr "<%s-S>\t\tWeitersuchen im Vergleich" -#: gitk:3080 #, tcl-format msgid "<%s-R>\t\tSearch for previous hit in diff view" msgstr "<%s-R>\t\tRückwärts weitersuchen im Vergleich" -#: gitk:3081 #, tcl-format msgid "<%s-KP+>\tIncrease font size" msgstr "<%s-Nummerblock-Plus>\tSchrift vergrößern" -#: gitk:3082 #, tcl-format msgid "<%s-plus>\tIncrease font size" msgstr "<%s-Plus>\tSchrift vergrößern" -#: gitk:3083 #, tcl-format msgid "<%s-KP->\tDecrease font size" msgstr "<%s-Nummernblock-Minus> Schrift verkleinern" -#: gitk:3084 #, tcl-format msgid "<%s-minus>\tDecrease font size" msgstr "<%s-Minus>\tSchrift verkleinern" -#: gitk:3085 msgid "<F5>\t\tUpdate" msgstr "<F5>\t\tAktualisieren" -#: gitk:3550 gitk:3559 #, tcl-format msgid "Error creating temporary directory %s:" msgstr "Fehler beim Erzeugen des temporären Verzeichnisses »%s«:" -#: gitk:3572 #, tcl-format msgid "Error getting \"%s\" from %s:" msgstr "Fehler beim Holen von »%s« von »%s«:" -#: gitk:3635 msgid "command failed:" msgstr "Kommando fehlgeschlagen:" -#: gitk:3784 msgid "No such commit" msgstr "Version nicht gefunden" -#: gitk:3798 msgid "git gui blame: command failed:" msgstr "git gui blame: Kommando fehlgeschlagen:" -#: gitk:3829 #, tcl-format msgid "Couldn't read merge head: %s" msgstr "Zusammenführungs-Spitze konnte nicht gelesen werden: %s" -#: gitk:3837 #, tcl-format msgid "Error reading index: %s" msgstr "Fehler beim Lesen der Bereitstellung (»index«): %s" -#: gitk:3862 #, tcl-format msgid "Couldn't start git blame: %s" msgstr "»git blame« konnte nicht gestartet werden: %s" -#: gitk:3865 gitk:6754 msgid "Searching" msgstr "Suchen" -#: gitk:3897 #, tcl-format msgid "Error running git blame: %s" msgstr "Fehler beim Ausführen von »git blame«: %s" -#: gitk:3925 #, tcl-format msgid "That line comes from commit %s, which is not in this view" msgstr "" "Diese Zeile stammt aus Version %s, die nicht in dieser Ansicht gezeigt wird" -#: gitk:3939 msgid "External diff viewer failed:" msgstr "Externes Diff-Programm fehlgeschlagen:" -#: gitk:4070 msgid "Gitk view definition" msgstr "Gitk-Ansichten" -#: gitk:4074 msgid "Remember this view" msgstr "Diese Ansicht speichern" -#: gitk:4075 msgid "References (space separated list):" msgstr "Zweige/Markierungen (durch Leerzeichen getrennte Liste):" -#: gitk:4076 msgid "Branches & tags:" msgstr "Zweige/Markierungen:" -#: gitk:4077 msgid "All refs" msgstr "Alle Markierungen und Zweige" -#: gitk:4078 msgid "All (local) branches" msgstr "Alle (lokalen) Zweige" -#: gitk:4079 msgid "All tags" msgstr "Alle Markierungen" -#: gitk:4080 msgid "All remote-tracking branches" msgstr "Alle Übernahmezweige" -#: gitk:4081 msgid "Commit Info (regular expressions):" msgstr "Versionsinformationen (reguläre Ausdrücke):" -#: gitk:4082 msgid "Author:" msgstr "Autor:" -#: gitk:4083 msgid "Committer:" msgstr "Eintragender:" -#: gitk:4084 msgid "Commit Message:" msgstr "Versionsbeschreibung:" -#: gitk:4085 msgid "Matches all Commit Info criteria" msgstr "Alle Versionsinformationen-Kriterien erfüllen" -#: gitk:4086 msgid "Matches no Commit Info criteria" msgstr "keine Versionsinformationen-Kriterien erfüllen" -#: gitk:4087 msgid "Changes to Files:" msgstr "Dateien:" -#: gitk:4088 msgid "Fixed String" msgstr "Zeichenkette" -#: gitk:4089 msgid "Regular Expression" msgstr "Regulärer Ausdruck" -#: gitk:4090 msgid "Search string:" msgstr "Suchausdruck:" -#: gitk:4091 msgid "" "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" msgstr "" "Datum (»2 weeks ago«, »2009-03-17 15:27:38«, »March 17, 2009 15:27:38«)" -#: gitk:4092 msgid "Since:" msgstr "Von:" -#: gitk:4093 msgid "Until:" msgstr "Bis:" -#: gitk:4094 msgid "Limit and/or skip a number of revisions (positive integer):" msgstr "Versionsanzahl begrenzen oder einige überspringen (ganzzahliger Wert):" -#: gitk:4095 msgid "Number to show:" msgstr "Anzeigen:" -#: gitk:4096 msgid "Number to skip:" msgstr "Überspringen:" -#: gitk:4097 msgid "Miscellaneous options:" msgstr "Sonstiges:" -#: gitk:4098 msgid "Strictly sort by date" msgstr "Streng nach Datum sortieren" -#: gitk:4099 msgid "Mark branch sides" msgstr "Zweig-Seiten markieren" -#: gitk:4100 msgid "Limit to first parent" msgstr "Auf erste Elternversion beschränken" -#: gitk:4101 msgid "Simple history" msgstr "Einfache Historie" -#: gitk:4102 msgid "Additional arguments to git log:" msgstr "Zusätzliche Argumente für »git log«:" -#: gitk:4103 msgid "Enter files and directories to include, one per line:" msgstr "Folgende Dateien und Verzeichnisse anzeigen (eine pro Zeile):" -#: gitk:4104 msgid "Command to generate more commits to include:" msgstr "Versionsliste durch folgendes Kommando erzeugen lassen:" -#: gitk:4228 msgid "Gitk: edit view" msgstr "Gitk: Ansicht bearbeiten" -#: gitk:4236 msgid "-- criteria for selecting revisions" msgstr "-- Auswahl der angezeigten Versionen" -#: gitk:4241 msgid "View Name" msgstr "Ansichtsname" -#: gitk:4316 msgid "Apply (F5)" msgstr "Anwenden (F5)" -#: gitk:4354 msgid "Error in commit selection arguments:" msgstr "Fehler in den ausgewählten Versionen:" -#: gitk:4409 gitk:4462 gitk:4924 gitk:4938 gitk:6208 gitk:12373 gitk:12374 msgid "None" msgstr "Keine" -#: gitk:5021 gitk:5026 msgid "Descendant" msgstr "Abkömmling" -#: gitk:5022 msgid "Not descendant" msgstr "Kein Abkömmling" -#: gitk:5029 gitk:5034 msgid "Ancestor" msgstr "Vorgänger" -#: gitk:5030 msgid "Not ancestor" msgstr "Kein Vorgänger" -#: gitk:5324 msgid "Local changes checked in to index but not committed" msgstr "Lokale Änderungen bereitgestellt, aber nicht eingetragen" -#: gitk:5360 msgid "Local uncommitted changes, not checked in to index" msgstr "Lokale Änderungen, nicht bereitgestellt" -#: gitk:7134 msgid "and many more" msgstr "und weitere" -#: gitk:7137 msgid "many" msgstr "viele" -#: gitk:7328 msgid "Tags:" msgstr "Markierungen:" -#: gitk:7345 gitk:7351 gitk:8825 msgid "Parent" msgstr "Eltern" -#: gitk:7356 msgid "Child" msgstr "Kind" -#: gitk:7365 msgid "Branch" msgstr "Zweig" -#: gitk:7368 msgid "Follows" msgstr "Folgt auf" -#: gitk:7371 msgid "Precedes" msgstr "Vorgänger von" -#: gitk:7966 #, tcl-format msgid "Error getting diffs: %s" msgstr "Fehler beim Laden des Vergleichs: %s" -#: gitk:8650 msgid "Goto:" msgstr "Gehe zu:" -#: gitk:8671 #, tcl-format msgid "Short SHA1 id %s is ambiguous" msgstr "Kurzer SHA1-Hashwert »%s« ist mehrdeutig" -#: gitk:8678 #, tcl-format msgid "Revision %s is not known" msgstr "Version »%s« ist unbekannt" -#: gitk:8688 #, tcl-format msgid "SHA1 id %s is not known" msgstr "SHA1-Hashwert »%s« ist unbekannt" -#: gitk:8690 #, tcl-format msgid "Revision %s is not in the current view" msgstr "Version »%s« wird in der aktuellen Ansicht nicht angezeigt" -#: gitk:8832 gitk:8847 msgid "Date" msgstr "Datum" -#: gitk:8835 msgid "Children" msgstr "Kinder" -#: gitk:8898 #, tcl-format msgid "Reset %s branch to here" msgstr "Zweig »%s« hierher zurücksetzen" -#: gitk:8900 msgid "Detached head: can't reset" msgstr "Zweigspitze ist abgetrennt: Zurücksetzen nicht möglich" -#: gitk:9005 gitk:9011 msgid "Skipping merge commit " msgstr "Überspringe Zusammenführungs-Version " -#: gitk:9020 gitk:9025 msgid "Error getting patch ID for " msgstr "Fehler beim Holen der Patch-ID für " -#: gitk:9021 gitk:9026 msgid " - stopping\n" msgstr " - Abbruch.\n" -#: gitk:9031 gitk:9034 gitk:9042 gitk:9056 gitk:9065 msgid "Commit " msgstr "Version " -#: gitk:9035 msgid "" " is the same patch as\n" " " @@ -893,7 +686,6 @@ msgstr "" " ist das gleiche Patch wie\n" " " -#: gitk:9043 msgid "" " differs from\n" " " @@ -901,7 +693,6 @@ msgstr "" " ist unterschiedlich von\n" " " -#: gitk:9045 msgid "" "Diff of commits:\n" "\n" @@ -909,131 +700,101 @@ msgstr "" "Vergleich der Versionen:\n" "\n" -#: gitk:9057 gitk:9066 #, tcl-format msgid " has %s children - stopping\n" msgstr " hat %s Kinder. Abbruch\n" -#: gitk:9085 #, tcl-format msgid "Error writing commit to file: %s" msgstr "Fehler beim Schreiben der Version in Datei: %s" -#: gitk:9091 #, tcl-format msgid "Error diffing commits: %s" msgstr "Fehler beim Vergleichen der Versionen: %s" -#: gitk:9137 msgid "Top" msgstr "Oben" -#: gitk:9138 msgid "From" msgstr "Von" -#: gitk:9143 msgid "To" msgstr "bis" -#: gitk:9167 msgid "Generate patch" msgstr "Patch erstellen" -#: gitk:9169 msgid "From:" msgstr "Von:" -#: gitk:9178 msgid "To:" msgstr "bis:" -#: gitk:9187 msgid "Reverse" msgstr "Umgekehrt" -#: gitk:9189 gitk:9385 msgid "Output file:" msgstr "Ausgabedatei:" -#: gitk:9195 msgid "Generate" msgstr "Erzeugen" -#: gitk:9233 msgid "Error creating patch:" msgstr "Fehler beim Erzeugen des Patches:" -#: gitk:9256 gitk:9373 gitk:9430 msgid "ID:" msgstr "ID:" -#: gitk:9265 msgid "Tag name:" msgstr "Markierungsname:" -#: gitk:9268 msgid "Tag message is optional" msgstr "Eine Markierungsbeschreibung ist optional" -#: gitk:9270 msgid "Tag message:" msgstr "Markierungsbeschreibung:" -#: gitk:9274 gitk:9439 msgid "Create" msgstr "Erstellen" -#: gitk:9292 msgid "No tag name specified" msgstr "Kein Markierungsname angegeben" -#: gitk:9296 #, tcl-format msgid "Tag \"%s\" already exists" msgstr "Markierung »%s« existiert bereits." -#: gitk:9306 msgid "Error creating tag:" msgstr "Fehler beim Erstellen der Markierung:" -#: gitk:9382 msgid "Command:" msgstr "Kommando:" -#: gitk:9390 msgid "Write" msgstr "Schreiben" -#: gitk:9408 msgid "Error writing commit:" msgstr "Fehler beim Schreiben der Version:" -#: gitk:9435 msgid "Name:" msgstr "Name:" -#: gitk:9458 msgid "Please specify a name for the new branch" msgstr "Bitte geben Sie einen Namen für den neuen Zweig an." -#: gitk:9463 #, tcl-format msgid "Branch '%s' already exists. Overwrite?" msgstr "Zweig »%s« existiert bereits. Soll er überschrieben werden?" -#: gitk:9530 #, tcl-format msgid "Commit %s is already included in branch %s -- really re-apply it?" msgstr "" "Version »%s« ist bereits im Zweig »%s« enthalten -- trotzdem erneut " "eintragen?" -#: gitk:9535 msgid "Cherry-picking" msgstr "Version pflücken" -#: gitk:9544 #, tcl-format msgid "" "Cherry-pick failed because of local changes to file '%s'.\n" @@ -1043,7 +804,6 @@ msgstr "" "vorliegen. Bitte diese Änderungen eintragen, zurücksetzen oder\n" "zwischenspeichern (»git stash«) und dann erneut versuchen." -#: gitk:9550 msgid "" "Cherry-pick failed because of merge conflict.\n" "Do you wish to run git citool to resolve it?" @@ -1052,21 +812,16 @@ msgstr "" "ist. Soll das Zusammenführungs-Werkzeug (»git citool«) aufgerufen\n" "werden, um diesen Konflikt aufzulösen?" -#: gitk:9566 gitk:9624 msgid "No changes committed" msgstr "Keine Änderungen eingetragen" -#: gitk:9593 #, tcl-format msgid "Commit %s is not included in branch %s -- really revert it?" -msgstr "" -"Version »%s« ist nicht im Zweig »%s« enthalten -- trotzdem umkehren?" +msgstr "Version »%s« ist nicht im Zweig »%s« enthalten -- trotzdem umkehren?" -#: gitk:9598 msgid "Reverting" msgstr "Umkehren" -#: gitk:9606 #, tcl-format msgid "" "Revert failed because of local changes to the following files:%s Please " @@ -1076,7 +831,6 @@ msgstr "" "vorliegen. Bitte diese Änderungen eintragen, zurücksetzen oder\n" "zwischenspeichern (»git stash«) und dann erneut versuchen." -#: gitk:9610 msgid "" "Revert failed because of merge conflict.\n" " Do you wish to run git citool to resolve it?" @@ -1085,30 +839,24 @@ msgstr "" "ist. Soll das Zusammenführungs-Werkzeug (»git citool«) aufgerufen\n" "werden, um diesen Konflikt aufzulösen?" -#: gitk:9653 msgid "Confirm reset" msgstr "Zurücksetzen bestätigen" -#: gitk:9655 #, tcl-format msgid "Reset branch %s to %s?" msgstr "Zweig »%s« auf »%s« zurücksetzen?" -#: gitk:9657 msgid "Reset type:" msgstr "Art des Zurücksetzens:" -#: gitk:9660 msgid "Soft: Leave working tree and index untouched" msgstr "Harmlos: Arbeitskopie und Bereitstellung unverändert" -#: gitk:9663 msgid "Mixed: Leave working tree untouched, reset index" msgstr "" "Gemischt: Arbeitskopie unverändert,\n" "Bereitstellung zurückgesetzt" -#: gitk:9666 msgid "" "Hard: Reset working tree and index\n" "(discard ALL local changes)" @@ -1116,21 +864,17 @@ msgstr "" "Hart: Arbeitskopie und Bereitstellung\n" "(Alle lokalen Änderungen werden gelöscht)" -#: gitk:9683 msgid "Resetting" msgstr "Zurücksetzen" -#: gitk:9743 msgid "Checking out" msgstr "Umstellen" -#: gitk:9796 msgid "Cannot delete the currently checked-out branch" msgstr "" "Der Zweig, auf den die Arbeitskopie momentan umgestellt ist, kann nicht " "gelöscht werden." -#: gitk:9802 #, tcl-format msgid "" "The commits on branch %s aren't on any other branch.\n" @@ -1139,16 +883,13 @@ msgstr "" "Die Versionen auf Zweig »%s« existieren auf keinem anderen Zweig.\n" "Zweig »%s« trotzdem löschen?" -#: gitk:9833 #, tcl-format msgid "Tags and heads: %s" msgstr "Markierungen und Zweige: %s" -#: gitk:9850 msgid "Filter" msgstr "Filtern" -#: gitk:10146 msgid "" "Error reading commit topology information; branch and preceding/following " "tag information will be incomplete." @@ -1156,218 +897,167 @@ msgstr "" "Fehler beim Lesen der Strukturinformationen; Zweige und Informationen zu " "Vorgänger/Nachfolger werden unvollständig sein." -#: gitk:11123 msgid "Tag" msgstr "Markierung" -#: gitk:11127 msgid "Id" msgstr "Id" -#: gitk:11210 msgid "Gitk font chooser" msgstr "Gitk-Schriften wählen" -#: gitk:11227 msgid "B" msgstr "F" -#: gitk:11230 msgid "I" msgstr "K" -#: gitk:11348 msgid "Commit list display options" msgstr "Anzeige der Versionsliste" -#: gitk:11351 msgid "Maximum graph width (lines)" msgstr "Maximale Graphenbreite (Zeilen)" -#: gitk:11355 #, no-tcl-format msgid "Maximum graph width (% of pane)" msgstr "Maximale Graphenbreite (% des Fensters)" -#: gitk:11358 msgid "Show local changes" msgstr "Lokale Änderungen anzeigen" -#: gitk:11361 msgid "Auto-select SHA1 (length)" msgstr "SHA1-Hashwert (Länge) automatisch auswählen" -#: gitk:11365 msgid "Hide remote refs" msgstr "Entfernte Zweige/Markierungen ausblenden" -#: gitk:11369 msgid "Diff display options" msgstr "Anzeige des Vergleichs" -#: gitk:11371 msgid "Tab spacing" msgstr "Tabulatorbreite" -#: gitk:11374 msgid "Display nearby tags/heads" msgstr "Naheliegende Markierungen/Zweigspitzen anzeigen" -#: gitk:11377 msgid "Maximum # tags/heads to show" msgstr "Maximale Anzahl anzuzeigender Markierungen/Zweigspitzen" -#: gitk:11380 msgid "Limit diffs to listed paths" msgstr "Vergleich nur für angezeigte Pfade" -#: gitk:11383 msgid "Support per-file encodings" msgstr "Zeichenkodierung pro Datei ermitteln" -#: gitk:11389 gitk:11536 msgid "External diff tool" msgstr "Externes Diff-Programm" -#: gitk:11390 msgid "Choose..." msgstr "Wählen ..." -#: gitk:11395 msgid "General options" msgstr "Allgemeine Optionen" -#: gitk:11398 msgid "Use themed widgets" msgstr "Aussehen der Benutzeroberfläche durch Thema bestimmen" -#: gitk:11400 msgid "(change requires restart)" msgstr "(Änderungen werden erst nach Neustart wirksam)" -#: gitk:11402 msgid "(currently unavailable)" msgstr "(Momentan nicht verfügbar)" -#: gitk:11413 msgid "Colors: press to choose" msgstr "Farben: Klicken zum Wählen" -#: gitk:11416 msgid "Interface" msgstr "Benutzeroberfläche" -#: gitk:11417 msgid "interface" msgstr "Benutzeroberfläche" -#: gitk:11420 msgid "Background" msgstr "Hintergrund" -#: gitk:11421 gitk:11451 msgid "background" msgstr "Hintergrund" -#: gitk:11424 msgid "Foreground" msgstr "Vordergrund" -#: gitk:11425 msgid "foreground" msgstr "Vordergrund" -#: gitk:11428 msgid "Diff: old lines" msgstr "Vergleich: Alte Zeilen" -#: gitk:11429 msgid "diff old lines" msgstr "Vergleich - Alte Zeilen" -#: gitk:11433 msgid "Diff: new lines" msgstr "Vergleich: Neue Zeilen" -#: gitk:11434 msgid "diff new lines" msgstr "Vergleich - Neue Zeilen" -#: gitk:11438 msgid "Diff: hunk header" msgstr "Vergleich: Änderungstitel" -#: gitk:11440 msgid "diff hunk header" msgstr "Vergleich - Änderungstitel" -#: gitk:11444 msgid "Marked line bg" msgstr "Hintergrund für markierte Zeile" -#: gitk:11446 msgid "marked line background" msgstr "Hintergrund für markierte Zeile" -#: gitk:11450 msgid "Select bg" msgstr "Hintergrundfarbe auswählen" -#: gitk:11459 msgid "Fonts: press to choose" msgstr "Schriftart: Klicken zum Wählen" -#: gitk:11461 msgid "Main font" msgstr "Programmschriftart" -#: gitk:11462 msgid "Diff display font" msgstr "Schriftart für Vergleich" -#: gitk:11463 msgid "User interface font" msgstr "Beschriftungen" -#: gitk:11485 msgid "Gitk preferences" msgstr "Gitk-Einstellungen" -#: gitk:11494 msgid "General" msgstr "Allgemein" -#: gitk:11495 msgid "Colors" msgstr "Farben" -#: gitk:11496 msgid "Fonts" msgstr "Schriftarten" -#: gitk:11546 #, tcl-format msgid "Gitk: choose color for %s" msgstr "Gitk: Farbe wählen für %s" -#: gitk:12059 msgid "" "Sorry, gitk cannot run with this version of Tcl/Tk.\n" " Gitk requires at least Tcl/Tk 8.4." msgstr "" -"Entschuldigung, gitk kann nicht mit dieser Tcl/Tk Version ausgeführt werden.\n" +"Entschuldigung, gitk kann nicht mit dieser Tcl/Tk Version ausgeführt " +"werden.\n" " Gitk erfordert mindestens Tcl/Tk 8.4." -#: gitk:12269 msgid "Cannot find a git repository here." msgstr "Kein Git-Projektarchiv gefunden." -#: gitk:12316 #, tcl-format msgid "Ambiguous argument '%s': both revision and filename" msgstr "Mehrdeutige Angabe »%s«: Sowohl Version als auch Dateiname existiert." -#: gitk:12328 msgid "Bad arguments to gitk:" msgstr "Falsche Kommandozeilen-Parameter für gitk:" diff --git a/gitk-git/po/es.po b/gitk-git/po/es.po index fef3bbafee..25260c6571 100644 --- a/gitk-git/po/es.po +++ b/gitk-git/po/es.po @@ -6,7 +6,7 @@ # msgid "" msgstr "" -"Project-Id-Version: gitk\n" +"Project-Id-Version: Gitk\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-05-17 14:32+1000\n" "PO-Revision-Date: 2008-03-25 11:20+0100\n" @@ -17,34 +17,27 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: gitk:140 msgid "Couldn't get list of unmerged files:" msgstr "Imposible obtener la lista de archivos pendientes de fusión:" -#: gitk:212 gitk:2381 msgid "Color words" msgstr "" -#: gitk:217 gitk:2381 gitk:8220 gitk:8253 msgid "Markup words" msgstr "" -#: gitk:324 #, fuzzy msgid "Error parsing revisions:" msgstr "Error al leer las diferencias de fusión:" -#: gitk:380 msgid "Error executing --argscmd command:" msgstr "" -#: gitk:393 msgid "No files selected: --merge specified but no files are unmerged." msgstr "" "No hay archivos seleccionados: se seleccionó la opción --merge pero no hay " "archivos pendientes de fusión." -#: gitk:396 msgid "" "No files selected: --merge specified but no unmerged files are within file " "limit." @@ -52,319 +45,239 @@ msgstr "" "No hay archivos seleccionados: se seleccionó la opción --merge pero los " "archivos especificados no necesitan fusión." -#: gitk:418 gitk:566 #, fuzzy msgid "Error executing git log:" msgstr "Error al crear la etiqueta:" -#: gitk:436 gitk:582 msgid "Reading" msgstr "Leyendo" -#: gitk:496 gitk:4525 msgid "Reading commits..." msgstr "Leyendo revisiones..." -#: gitk:499 gitk:1637 gitk:4528 msgid "No commits selected" msgstr "No se seleccionaron revisiones" -#: gitk:1445 gitk:4045 gitk:12432 msgid "Command line" msgstr "Línea de comandos" -#: gitk:1511 msgid "Can't parse git log output:" msgstr "Error analizando la salida de git log:" -#: gitk:1740 msgid "No commit information available" msgstr "Falta información sobre las revisiones" -#: gitk:1903 gitk:1932 gitk:4315 gitk:9669 gitk:11241 gitk:11521 msgid "OK" msgstr "Aceptar" -#: gitk:1934 gitk:4317 gitk:9196 gitk:9275 gitk:9391 gitk:9440 gitk:9671 -#: gitk:11242 gitk:11522 msgid "Cancel" msgstr "Cancelar" -#: gitk:2069 msgid "&Update" msgstr "Actualizar" -#: gitk:2070 msgid "&Reload" msgstr "" -#: gitk:2071 msgid "Reread re&ferences" msgstr "Releer referencias" -#: gitk:2072 msgid "&List references" msgstr "Lista de referencias" -#: gitk:2074 msgid "Start git &gui" msgstr "" -#: gitk:2076 msgid "&Quit" msgstr "Salir" -#: gitk:2068 msgid "&File" msgstr "Archivo" -#: gitk:2080 msgid "&Preferences" msgstr "Preferencias" -#: gitk:2079 msgid "&Edit" msgstr "Editar" -#: gitk:2084 msgid "&New view..." msgstr "Nueva vista..." -#: gitk:2085 msgid "&Edit view..." msgstr "Modificar vista..." -#: gitk:2086 msgid "&Delete view" msgstr "Eliminar vista" -#: gitk:2088 gitk:4043 msgid "&All files" msgstr "Todos los archivos" -#: gitk:2083 gitk:4067 msgid "&View" msgstr "Vista" -#: gitk:2093 gitk:2103 gitk:3012 msgid "&About gitk" msgstr "Acerca de gitk" -#: gitk:2094 gitk:2108 msgid "&Key bindings" msgstr "Combinaciones de teclas" -#: gitk:2092 gitk:2107 msgid "&Help" msgstr "Ayuda" -#: gitk:2185 gitk:8652 msgid "SHA1 ID:" msgstr "SHA1 ID:" -#: gitk:2229 msgid "Row" msgstr "" -#: gitk:2267 msgid "Find" msgstr "Buscar" -#: gitk:2295 msgid "commit" msgstr "revisión" -#: gitk:2299 gitk:2301 gitk:4687 gitk:4710 gitk:4734 gitk:6755 gitk:6827 -#: gitk:6912 msgid "containing:" msgstr "que contiene:" -#: gitk:2302 gitk:3526 gitk:3531 gitk:4763 msgid "touching paths:" msgstr "que modifica la ruta:" -#: gitk:2303 gitk:4777 msgid "adding/removing string:" msgstr "que añade/elimina cadena:" -#: gitk:2304 gitk:4779 msgid "changing lines matching:" msgstr "" -#: gitk:2313 gitk:2315 gitk:4766 msgid "Exact" msgstr "Exacto" -#: gitk:2315 gitk:4854 gitk:6723 msgid "IgnCase" msgstr "NoMayús" -#: gitk:2315 gitk:4736 gitk:4852 gitk:6719 msgid "Regexp" msgstr "Regex" -#: gitk:2317 gitk:2318 gitk:4874 gitk:4904 gitk:4911 gitk:6848 gitk:6916 msgid "All fields" msgstr "Todos los campos" -#: gitk:2318 gitk:4871 gitk:4904 gitk:6786 msgid "Headline" msgstr "Título" -#: gitk:2319 gitk:4871 gitk:6786 gitk:6916 gitk:7389 msgid "Comments" msgstr "Comentarios" -#: gitk:2319 gitk:4871 gitk:4876 gitk:4911 gitk:6786 gitk:7324 gitk:8830 -#: gitk:8845 msgid "Author" msgstr "Autor" -#: gitk:2319 gitk:4871 gitk:6786 gitk:7326 msgid "Committer" msgstr "" -#: gitk:2350 msgid "Search" msgstr "Buscar" -#: gitk:2358 msgid "Diff" msgstr "Diferencia" -#: gitk:2360 msgid "Old version" msgstr "Versión antigua" -#: gitk:2362 msgid "New version" msgstr "Versión nueva" -#: gitk:2364 msgid "Lines of context" msgstr "Líneas de contexto" -#: gitk:2374 msgid "Ignore space change" msgstr "Ignora cambios de espaciado" -#: gitk:2378 gitk:2380 gitk:7959 gitk:8206 msgid "Line diff" msgstr "" -#: gitk:2445 msgid "Patch" msgstr "Parche" -#: gitk:2447 msgid "Tree" msgstr "Árbol" -#: gitk:2617 gitk:2637 msgid "Diff this -> selected" msgstr "Diferencia de esta -> seleccionada" -#: gitk:2618 gitk:2638 msgid "Diff selected -> this" msgstr "Diferencia de seleccionada -> esta" -#: gitk:2619 gitk:2639 msgid "Make patch" msgstr "Crear patch" -#: gitk:2620 gitk:9254 msgid "Create tag" msgstr "Crear etiqueta" -#: gitk:2621 gitk:9371 msgid "Write commit to file" msgstr "Escribir revisiones a archivo" -#: gitk:2622 gitk:9428 msgid "Create new branch" msgstr "Crear nueva rama" -#: gitk:2623 msgid "Cherry-pick this commit" msgstr "Añadir esta revisión a la rama actual (cherry-pick)" -#: gitk:2624 msgid "Reset HEAD branch to here" msgstr "Traer la rama HEAD aquí" -#: gitk:2625 #, fuzzy msgid "Mark this commit" msgstr "Añadir esta revisión a la rama actual (cherry-pick)" -#: gitk:2626 msgid "Return to mark" msgstr "" -#: gitk:2627 msgid "Find descendant of this and mark" msgstr "" -#: gitk:2628 msgid "Compare with marked commit" msgstr "" -#: gitk:2629 gitk:2640 #, fuzzy msgid "Diff this -> marked commit" msgstr "Diferencia de esta -> seleccionada" -#: gitk:2630 gitk:2641 #, fuzzy msgid "Diff marked commit -> this" msgstr "Diferencia de seleccionada -> esta" -#: gitk:2631 #, fuzzy msgid "Revert this commit" msgstr "Añadir esta revisión a la rama actual (cherry-pick)" -#: gitk:2647 msgid "Check out this branch" msgstr "Cambiar a esta rama" -#: gitk:2648 msgid "Remove this branch" msgstr "Eliminar esta rama" -#: gitk:2649 msgid "Copy branch name" msgstr "" -#: gitk:2656 msgid "Highlight this too" msgstr "Seleccionar también" -#: gitk:2657 msgid "Highlight this only" msgstr "Seleccionar sólo" -#: gitk:2658 msgid "External diff" msgstr "" -#: gitk:2659 msgid "Blame parent commit" msgstr "" -#: gitk:2660 msgid "Copy path" msgstr "" -#: gitk:2667 msgid "Show origin of this line" msgstr "" -#: gitk:2668 msgid "Run git gui blame on this line" msgstr "" -#: gitk:3014 #, fuzzy msgid "" "\n" @@ -382,733 +295,569 @@ msgstr "" "Uso y redistribución permitidos según los términos de la Licencia Pública " "General de GNU (GNU GPL)" -#: gitk:3022 gitk:3089 gitk:9857 msgid "Close" msgstr "Cerrar" -#: gitk:3043 msgid "Gitk key bindings" msgstr "Combinaciones de tecla de Gitk" -#: gitk:3046 msgid "Gitk key bindings:" msgstr "Combinaciones de tecla de Gitk:" -#: gitk:3048 #, tcl-format msgid "<%s-Q>\t\tQuit" msgstr "<%s-Q>\t\tSalir" -#: gitk:3049 #, fuzzy, tcl-format msgid "<%s-W>\t\tClose window" msgstr "<%s-F>\t\tBuscar" -#: gitk:3050 msgid "<Home>\t\tMove to first commit" msgstr "<Home>\t\tIr a la primera revisión" -#: gitk:3051 msgid "<End>\t\tMove to last commit" msgstr "<End>\t\tIr a la última revisión" -#: gitk:3052 #, fuzzy msgid "<Up>, p, k\tMove up one commit" msgstr "<Up>, p, i\tSubir una revisión" -#: gitk:3053 #, fuzzy msgid "<Down>, n, j\tMove down one commit" msgstr "<Down>, n, k\tBajar una revisión" -#: gitk:3054 #, fuzzy msgid "<Left>, z, h\tGo back in history list" msgstr "<Left>, z, j\tRetroceder en la historia" -#: gitk:3055 msgid "<Right>, x, l\tGo forward in history list" msgstr "<Right>, x, l\tAvanzar en la historia" -#: gitk:3056 #, tcl-format msgid "<%s-n>\tGo to n-th parent of current commit in history list" msgstr "" -#: gitk:3057 msgid "<PageUp>\tMove up one page in commit list" msgstr "<PageUp>\tSubir una página en la lista de revisiones" -#: gitk:3058 msgid "<PageDown>\tMove down one page in commit list" msgstr "<PageDown>\tBajar una página en la lista de revisiones" -#: gitk:3059 #, tcl-format msgid "<%s-Home>\tScroll to top of commit list" msgstr "<%s-Home>\tDesplazarse al inicio de la lista de revisiones" -#: gitk:3060 #, tcl-format msgid "<%s-End>\tScroll to bottom of commit list" msgstr "<%s-End>\tDesplazarse al final de la lista de revisiones" -#: gitk:3061 #, tcl-format msgid "<%s-Up>\tScroll commit list up one line" msgstr "<%s-Up>\tDesplazar una línea hacia arriba la lista de revisiones" -#: gitk:3062 #, tcl-format msgid "<%s-Down>\tScroll commit list down one line" msgstr "<%s-Down>\tDesplazar una línea hacia abajo la lista de revisiones" -#: gitk:3063 #, tcl-format msgid "<%s-PageUp>\tScroll commit list up one page" msgstr "<%s-PageUp>\tDesplazar una página hacia arriba la lista de revisiones" -#: gitk:3064 #, tcl-format msgid "<%s-PageDown>\tScroll commit list down one page" msgstr "<%s-PageDown>\tDesplazar una página hacia abajo la lista de revisiones" -#: gitk:3065 msgid "<Shift-Up>\tFind backwards (upwards, later commits)" msgstr "<Shift-Up>\tBuscar hacia atrás (arriba, revisiones siguientes)" -#: gitk:3066 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)" msgstr "<Shift-Down>\tBuscar hacia adelante (abajo, revisiones anteriores)" -#: gitk:3067 msgid "<Delete>, b\tScroll diff view up one page" msgstr "<Delete>, b\tDesplaza hacia arriba una página la vista de diferencias" -#: gitk:3068 msgid "<Backspace>\tScroll diff view up one page" msgstr "<Backspace>\tDesplaza hacia arriba una página la vista de diferencias" -#: gitk:3069 msgid "<Space>\t\tScroll diff view down one page" msgstr "<Space>\t\tDesplaza hacia abajo una página la vista de diferencias" -#: gitk:3070 msgid "u\t\tScroll diff view up 18 lines" msgstr "u\t\tDesplaza hacia arriba 18 líneas la vista de diferencias" -#: gitk:3071 msgid "d\t\tScroll diff view down 18 lines" msgstr "d\t\tDesplaza hacia abajo 18 líneas la vista de diferencias" -#: gitk:3072 #, tcl-format msgid "<%s-F>\t\tFind" msgstr "<%s-F>\t\tBuscar" -#: gitk:3073 #, tcl-format msgid "<%s-G>\t\tMove to next find hit" msgstr "<%s-G>\t\tBuscar el siguiente" -#: gitk:3074 msgid "<Return>\tMove to next find hit" msgstr "<Return>\tBuscar el siguiente" -#: gitk:3075 #, fuzzy msgid "g\t\tGo to commit" msgstr "<End>\t\tIr a la última revisión" -#: gitk:3076 msgid "/\t\tFocus the search box" msgstr "" -#: gitk:3077 msgid "?\t\tMove to previous find hit" msgstr "?\t\tBuscar el anterior" -#: gitk:3078 msgid "f\t\tScroll diff view to next file" msgstr "f\t\tDesplazar la vista de diferencias al archivo siguiente" -#: gitk:3079 #, tcl-format msgid "<%s-S>\t\tSearch for next hit in diff view" msgstr "<%s-S>\t\tBuscar siguiente en la vista de diferencias" -#: gitk:3080 #, tcl-format msgid "<%s-R>\t\tSearch for previous hit in diff view" msgstr "<%s-R>\t\tBuscar anterior en la vista de diferencias" -#: gitk:3081 #, tcl-format msgid "<%s-KP+>\tIncrease font size" msgstr "<%s-KP+>\tAumentar tamaño del texto" -#: gitk:3082 #, tcl-format msgid "<%s-plus>\tIncrease font size" msgstr "<%s-plus>\tAumentar tamaño del texto" -#: gitk:3083 #, tcl-format msgid "<%s-KP->\tDecrease font size" msgstr "<%s-KP->\tDisminuir tamaño del texto" -#: gitk:3084 #, tcl-format msgid "<%s-minus>\tDecrease font size" msgstr "<%s-minus>\tDisminuir tamaño del texto" -#: gitk:3085 msgid "<F5>\t\tUpdate" msgstr "<F5>\t\tActualizar" -#: gitk:3550 gitk:3559 #, fuzzy, tcl-format msgid "Error creating temporary directory %s:" msgstr "Error en la creación del parche:" -#: gitk:3572 #, fuzzy, tcl-format msgid "Error getting \"%s\" from %s:" msgstr "Error al leer las diferencias de fusión:" -#: gitk:3635 #, fuzzy msgid "command failed:" msgstr "Línea de comandos" -#: gitk:3784 #, fuzzy msgid "No such commit" msgstr "No se han guardado cambios" -#: gitk:3798 msgid "git gui blame: command failed:" msgstr "" -#: gitk:3829 #, tcl-format msgid "Couldn't read merge head: %s" msgstr "" -#: gitk:3837 #, fuzzy, tcl-format msgid "Error reading index: %s" msgstr "Error al crear la etiqueta:" -#: gitk:3862 #, tcl-format msgid "Couldn't start git blame: %s" msgstr "" -#: gitk:3865 gitk:6754 msgid "Searching" msgstr "Buscando" -#: gitk:3897 #, fuzzy, tcl-format msgid "Error running git blame: %s" msgstr "Error al crear la etiqueta:" -#: gitk:3925 #, tcl-format msgid "That line comes from commit %s, which is not in this view" msgstr "" -#: gitk:3939 #, fuzzy msgid "External diff viewer failed:" msgstr "f\t\tDesplazar la vista de diferencias al archivo siguiente" -#: gitk:4070 msgid "Gitk view definition" msgstr "Definición de vistas de Gitk" -#: gitk:4074 msgid "Remember this view" msgstr "Recordar esta vista" -#: gitk:4075 msgid "References (space separated list):" msgstr "" -#: gitk:4076 msgid "Branches & tags:" msgstr "" -#: gitk:4077 #, fuzzy msgid "All refs" msgstr "Todos los archivos" -#: gitk:4078 msgid "All (local) branches" msgstr "" -#: gitk:4079 msgid "All tags" msgstr "" -#: gitk:4080 msgid "All remote-tracking branches" msgstr "" -#: gitk:4081 msgid "Commit Info (regular expressions):" msgstr "" -#: gitk:4082 #, fuzzy msgid "Author:" msgstr "Autor" -#: gitk:4083 #, fuzzy msgid "Committer:" msgstr "revisión" -#: gitk:4084 msgid "Commit Message:" msgstr "" -#: gitk:4085 msgid "Matches all Commit Info criteria" msgstr "" -#: gitk:4086 msgid "Matches no Commit Info criteria" msgstr "" -#: gitk:4087 msgid "Changes to Files:" msgstr "" -#: gitk:4088 msgid "Fixed String" msgstr "" -#: gitk:4089 msgid "Regular Expression" msgstr "" -#: gitk:4090 #, fuzzy msgid "Search string:" msgstr "Buscando" -#: gitk:4091 msgid "" "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" msgstr "" -#: gitk:4092 msgid "Since:" msgstr "" -#: gitk:4093 msgid "Until:" msgstr "" -#: gitk:4094 msgid "Limit and/or skip a number of revisions (positive integer):" msgstr "" -#: gitk:4095 msgid "Number to show:" msgstr "" -#: gitk:4096 msgid "Number to skip:" msgstr "" -#: gitk:4097 msgid "Miscellaneous options:" msgstr "" -#: gitk:4098 msgid "Strictly sort by date" msgstr "" -#: gitk:4099 msgid "Mark branch sides" msgstr "" -#: gitk:4100 #, fuzzy msgid "Limit to first parent" msgstr "Limitar las diferencias a las rutas seleccionadas" -#: gitk:4101 msgid "Simple history" msgstr "" -#: gitk:4102 #, fuzzy msgid "Additional arguments to git log:" msgstr "Revisiones a incluir (argumentos a git log):" -#: gitk:4103 msgid "Enter files and directories to include, one per line:" msgstr "Introducir archivos y directorios a incluir, uno por línea:" -#: gitk:4104 msgid "Command to generate more commits to include:" msgstr "Comando que genera más revisiones a incluir:" -#: gitk:4228 msgid "Gitk: edit view" msgstr "" -#: gitk:4236 msgid "-- criteria for selecting revisions" msgstr "" -#: gitk:4241 #, fuzzy msgid "View Name" msgstr "Vista" -#: gitk:4316 msgid "Apply (F5)" msgstr "" -#: gitk:4354 msgid "Error in commit selection arguments:" msgstr "Error en los argumentos de selección de las revisiones:" -#: gitk:4409 gitk:4462 gitk:4924 gitk:4938 gitk:6208 gitk:12373 gitk:12374 msgid "None" msgstr "Ninguno" -#: gitk:5021 gitk:5026 msgid "Descendant" msgstr "Descendiente" -#: gitk:5022 msgid "Not descendant" msgstr "No descendiente" -#: gitk:5029 gitk:5034 msgid "Ancestor" msgstr "Antepasado" -#: gitk:5030 msgid "Not ancestor" msgstr "No antepasado" -#: gitk:5324 msgid "Local changes checked in to index but not committed" msgstr "Cambios locales añadidos al índice pero sin completar revisión" -#: gitk:5360 msgid "Local uncommitted changes, not checked in to index" msgstr "Cambios locales sin añadir al índice" -#: gitk:7134 msgid "and many more" msgstr "" -#: gitk:7137 msgid "many" msgstr "" -#: gitk:7328 msgid "Tags:" msgstr "Etiquetas:" -#: gitk:7345 gitk:7351 gitk:8825 msgid "Parent" msgstr "Padre" -#: gitk:7356 msgid "Child" msgstr "Hija" -#: gitk:7365 msgid "Branch" msgstr "Rama" -#: gitk:7368 msgid "Follows" msgstr "Sigue-a" -#: gitk:7371 msgid "Precedes" msgstr "Precede-a" -#: gitk:7966 #, fuzzy, tcl-format msgid "Error getting diffs: %s" msgstr "Error al leer las diferencias de fusión:" -#: gitk:8650 msgid "Goto:" msgstr "Ir a:" -#: gitk:8671 #, tcl-format msgid "Short SHA1 id %s is ambiguous" msgstr "La id SHA1 abreviada %s es ambigua" -#: gitk:8678 #, fuzzy, tcl-format msgid "Revision %s is not known" msgstr "La id SHA1 %s es desconocida" -#: gitk:8688 #, tcl-format msgid "SHA1 id %s is not known" msgstr "La id SHA1 %s es desconocida" -#: gitk:8690 #, tcl-format msgid "Revision %s is not in the current view" msgstr "" -#: gitk:8832 gitk:8847 msgid "Date" msgstr "Fecha" -#: gitk:8835 msgid "Children" msgstr "Hijas" -#: gitk:8898 #, tcl-format msgid "Reset %s branch to here" msgstr "Poner la rama %s en esta revisión" -#: gitk:8900 msgid "Detached head: can't reset" msgstr "" -#: gitk:9005 gitk:9011 msgid "Skipping merge commit " msgstr "" -#: gitk:9020 gitk:9025 #, fuzzy msgid "Error getting patch ID for " msgstr "Error en la creación del parche:" -#: gitk:9021 gitk:9026 msgid " - stopping\n" msgstr "" -#: gitk:9031 gitk:9034 gitk:9042 gitk:9056 gitk:9065 #, fuzzy msgid "Commit " msgstr "revisión" -#: gitk:9035 msgid "" " is the same patch as\n" " " msgstr "" -#: gitk:9043 msgid "" " differs from\n" " " msgstr "" -#: gitk:9045 msgid "" "Diff of commits:\n" "\n" msgstr "" -#: gitk:9057 gitk:9066 #, tcl-format msgid " has %s children - stopping\n" msgstr "" -#: gitk:9085 #, fuzzy, tcl-format msgid "Error writing commit to file: %s" msgstr "Error al escribir revisión:" -#: gitk:9091 #, fuzzy, tcl-format msgid "Error diffing commits: %s" msgstr "Error al escribir revisión:" -#: gitk:9137 msgid "Top" msgstr "Origen" -#: gitk:9138 msgid "From" msgstr "De" -#: gitk:9143 msgid "To" msgstr "A" -#: gitk:9167 msgid "Generate patch" msgstr "Generar parche" -#: gitk:9169 msgid "From:" msgstr "De:" -#: gitk:9178 msgid "To:" msgstr "Para:" -#: gitk:9187 msgid "Reverse" msgstr "Invertir" -#: gitk:9189 gitk:9385 msgid "Output file:" msgstr "Escribir a archivo:" -#: gitk:9195 msgid "Generate" msgstr "Generar" -#: gitk:9233 msgid "Error creating patch:" msgstr "Error en la creación del parche:" -#: gitk:9256 gitk:9373 gitk:9430 msgid "ID:" msgstr "ID:" -#: gitk:9265 msgid "Tag name:" msgstr "Nombre de etiqueta:" -#: gitk:9268 msgid "Tag message is optional" msgstr "" -#: gitk:9270 #, fuzzy msgid "Tag message:" msgstr "Nombre de etiqueta:" -#: gitk:9274 gitk:9439 msgid "Create" msgstr "Crear" -#: gitk:9292 msgid "No tag name specified" msgstr "No se ha especificado etiqueta" -#: gitk:9296 #, tcl-format msgid "Tag \"%s\" already exists" msgstr "La etiqueta \"%s\" ya existe" -#: gitk:9306 msgid "Error creating tag:" msgstr "Error al crear la etiqueta:" -#: gitk:9382 msgid "Command:" msgstr "Comando:" -#: gitk:9390 msgid "Write" msgstr "Escribir" -#: gitk:9408 msgid "Error writing commit:" msgstr "Error al escribir revisión:" -#: gitk:9435 msgid "Name:" msgstr "Nombre:" -#: gitk:9458 msgid "Please specify a name for the new branch" msgstr "Especifique un nombre para la nueva rama" -#: gitk:9463 #, fuzzy, tcl-format msgid "Branch '%s' already exists. Overwrite?" msgstr "La etiqueta \"%s\" ya existe" -#: gitk:9530 #, tcl-format msgid "Commit %s is already included in branch %s -- really re-apply it?" msgstr "La revisión %s ya está incluida en la rama %s -- ¿Volver a aplicarla?" -#: gitk:9535 msgid "Cherry-picking" msgstr "Eligiendo revisiones (cherry-picking)" -#: gitk:9544 #, tcl-format msgid "" "Cherry-pick failed because of local changes to file '%s'.\n" "Please commit, reset or stash your changes and try again." msgstr "" -#: gitk:9550 msgid "" "Cherry-pick failed because of merge conflict.\n" "Do you wish to run git citool to resolve it?" msgstr "" -#: gitk:9566 gitk:9624 msgid "No changes committed" msgstr "No se han guardado cambios" -#: gitk:9593 #, fuzzy, tcl-format msgid "Commit %s is not included in branch %s -- really revert it?" msgstr "La revisión %s ya está incluida en la rama %s -- ¿Volver a aplicarla?" -#: gitk:9598 #, fuzzy msgid "Reverting" msgstr "Reponiendo" -#: gitk:9606 #, tcl-format msgid "" "Revert failed because of local changes to the following files:%s Please " "commit, reset or stash your changes and try again." msgstr "" -#: gitk:9610 msgid "" "Revert failed because of merge conflict.\n" " Do you wish to run git citool to resolve it?" msgstr "" -#: gitk:9653 msgid "Confirm reset" msgstr "Confirmar git reset" -#: gitk:9655 #, tcl-format msgid "Reset branch %s to %s?" msgstr "¿Reponer la rama %s a %s?" -#: gitk:9657 msgid "Reset type:" msgstr "Tipo de reposición:" -#: gitk:9660 msgid "Soft: Leave working tree and index untouched" msgstr "Suave: No altera la copia de trabajo ni el índice" -#: gitk:9663 msgid "Mixed: Leave working tree untouched, reset index" msgstr "Mixta: Actualiza el índice, no altera la copia de trabajo" -#: gitk:9666 msgid "" "Hard: Reset working tree and index\n" "(discard ALL local changes)" @@ -1116,19 +865,15 @@ msgstr "" "Dura: Actualiza el índice y la copia de trabajo\n" "(abandona TODAS las modificaciones locales)" -#: gitk:9683 msgid "Resetting" msgstr "Reponiendo" -#: gitk:9743 msgid "Checking out" msgstr "Creando copia de trabajo" -#: gitk:9796 msgid "Cannot delete the currently checked-out branch" msgstr "No se puede borrar la rama actual" -#: gitk:9802 #, tcl-format msgid "" "The commits on branch %s aren't on any other branch.\n" @@ -1137,16 +882,13 @@ msgstr "" "Las revisiones de la rama %s no están presentes en otras ramas.\n" "¿Borrar la rama %s?" -#: gitk:9833 #, tcl-format msgid "Tags and heads: %s" msgstr "Etiquetas y ramas: %s" -#: gitk:9850 msgid "Filter" msgstr "Filtro" -#: gitk:10146 msgid "" "Error reading commit topology information; branch and preceding/following " "tag information will be incomplete." @@ -1154,211 +896,162 @@ msgstr "" "Error al leer la topología de revisiones: la información sobre las ramas y " "etiquetas precedentes y siguientes será incompleta." -#: gitk:11123 msgid "Tag" msgstr "Etiqueta" -#: gitk:11127 msgid "Id" msgstr "Id" -#: gitk:11210 msgid "Gitk font chooser" msgstr "Selector de tipografías gitk" -#: gitk:11227 msgid "B" msgstr "B" -#: gitk:11230 msgid "I" msgstr "I" -#: gitk:11348 msgid "Commit list display options" msgstr "Opciones de visualización de la lista de revisiones" -#: gitk:11351 msgid "Maximum graph width (lines)" msgstr "Ancho máximo del gráfico (en líneas)" -#: gitk:11355 #, no-tcl-format msgid "Maximum graph width (% of pane)" msgstr "Ancho máximo del gráfico (en % del panel)" -#: gitk:11358 msgid "Show local changes" msgstr "Mostrar cambios locales" -#: gitk:11361 #, fuzzy msgid "Auto-select SHA1 (length)" msgstr "Seleccionar automáticamente SHA1 hash" -#: gitk:11365 msgid "Hide remote refs" msgstr "" -#: gitk:11369 msgid "Diff display options" msgstr "Opciones de visualización de diferencias" -#: gitk:11371 msgid "Tab spacing" msgstr "Espaciado de tabulador" -#: gitk:11374 #, fuzzy msgid "Display nearby tags/heads" msgstr "Mostrar etiquetas cercanas" -#: gitk:11377 msgid "Maximum # tags/heads to show" msgstr "" -#: gitk:11380 msgid "Limit diffs to listed paths" msgstr "Limitar las diferencias a las rutas seleccionadas" -#: gitk:11383 msgid "Support per-file encodings" msgstr "" -#: gitk:11389 gitk:11536 msgid "External diff tool" msgstr "" -#: gitk:11390 msgid "Choose..." msgstr "" -#: gitk:11395 #, fuzzy msgid "General options" msgstr "Generar parche" -#: gitk:11398 msgid "Use themed widgets" msgstr "" -#: gitk:11400 msgid "(change requires restart)" msgstr "" -#: gitk:11402 msgid "(currently unavailable)" msgstr "" -#: gitk:11413 msgid "Colors: press to choose" msgstr "Colores: pulse para seleccionar" -#: gitk:11416 msgid "Interface" msgstr "" -#: gitk:11417 #, fuzzy msgid "interface" msgstr "Tipografía para interfaz de usuario" -#: gitk:11420 msgid "Background" msgstr "Fondo" -#: gitk:11421 gitk:11451 #, fuzzy msgid "background" msgstr "Fondo" -#: gitk:11424 msgid "Foreground" msgstr "Primer plano" -#: gitk:11425 #, fuzzy msgid "foreground" msgstr "Primer plano" -#: gitk:11428 msgid "Diff: old lines" msgstr "Diff: líneas viejas" -#: gitk:11429 #, fuzzy msgid "diff old lines" msgstr "Diff: líneas viejas" -#: gitk:11433 msgid "Diff: new lines" msgstr "Diff: líneas nuevas" -#: gitk:11434 #, fuzzy msgid "diff new lines" msgstr "Diff: líneas nuevas" -#: gitk:11438 msgid "Diff: hunk header" msgstr "Diff: cabecera de fragmento" -#: gitk:11440 #, fuzzy msgid "diff hunk header" msgstr "Diff: cabecera de fragmento" -#: gitk:11444 msgid "Marked line bg" msgstr "" -#: gitk:11446 msgid "marked line background" msgstr "" -#: gitk:11450 msgid "Select bg" msgstr "Color de fondo de la selección" -#: gitk:11459 msgid "Fonts: press to choose" msgstr "Tipografías: pulse para elegir" -#: gitk:11461 msgid "Main font" msgstr "Tipografía principal" -#: gitk:11462 msgid "Diff display font" msgstr "Tipografía para diferencias" -#: gitk:11463 msgid "User interface font" msgstr "Tipografía para interfaz de usuario" -#: gitk:11485 msgid "Gitk preferences" msgstr "Preferencias de gitk" -#: gitk:11494 #, fuzzy msgid "General" msgstr "Generar" -#: gitk:11495 msgid "Colors" msgstr "" -#: gitk:11496 msgid "Fonts" msgstr "" -#: gitk:11546 #, tcl-format msgid "Gitk: choose color for %s" msgstr "Gitk: elegir color para %s" -#: gitk:12059 msgid "" "Sorry, gitk cannot run with this version of Tcl/Tk.\n" " Gitk requires at least Tcl/Tk 8.4." @@ -1366,17 +1059,14 @@ msgstr "" "Esta versión de Tcl/Tk es demasiado antigua.\n" " Gitk requiere Tcl/Tk versión 8.4 o superior." -#: gitk:12269 msgid "Cannot find a git repository here." msgstr "No hay un repositorio git aquí." -#: gitk:12316 #, tcl-format msgid "Ambiguous argument '%s': both revision and filename" msgstr "" "Argumento ambiguo: '%s' es tanto una revisión como un nombre de archivo" -#: gitk:12328 msgid "Bad arguments to gitk:" msgstr "Argumentos incorrectos a Gitk:" diff --git a/gitk-git/po/fr.po b/gitk-git/po/fr.po index e4fac932e5..f1c54b6b6a 100644 --- a/gitk-git/po/fr.po +++ b/gitk-git/po/fr.po @@ -6,7 +6,7 @@ # Jean-Noël Avila <jn.avila@free.fr> msgid "" msgstr "" -"Project-Id-Version: gitk\n" +"Project-Id-Version: Gitk\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2016-01-22 22:04+0100\n" "PO-Revision-Date: 2016-01-22 23:28+0100\n" @@ -19,355 +19,268 @@ msgstr "" "X-Poedit-Language: French\n" "X-Poedit-Country: FRANCE\n" -#: gitk:140 msgid "Couldn't get list of unmerged files:" msgstr "Impossible de récupérer la liste des fichiers non fusionnés :" -#: gitk:212 gitk:2399 msgid "Color words" msgstr "Colorier les mots différents" -#: gitk:217 gitk:2399 gitk:8239 gitk:8272 msgid "Markup words" msgstr "Marquer les mots différents" -#: gitk:324 msgid "Error parsing revisions:" msgstr "Erreur lors du parcours des révisions :" -#: gitk:380 msgid "Error executing --argscmd command:" msgstr "Erreur à l'exécution de la commande --argscmd :" -#: gitk:393 msgid "No files selected: --merge specified but no files are unmerged." msgstr "" "Aucun fichier sélectionné : --merge précisé, mais tous les fichiers sont " "fusionnés." # FIXME : améliorer la traduction de 'file limite' -#: gitk:396 msgid "" "No files selected: --merge specified but no unmerged files are within file " "limit." -msgstr "Aucun fichier sélectionné : --merge précisé mais aucun fichier non fusionné n'est dans la limite des fichiers." +msgstr "" +"Aucun fichier sélectionné : --merge précisé mais aucun fichier non fusionné " +"n'est dans la limite des fichiers." -#: gitk:418 gitk:566 msgid "Error executing git log:" msgstr "Erreur à l'exécution de git log :" -#: gitk:436 gitk:582 msgid "Reading" msgstr "Lecture en cours" -#: gitk:496 gitk:4544 msgid "Reading commits..." msgstr "Lecture des commits..." -#: gitk:499 gitk:1637 gitk:4547 msgid "No commits selected" msgstr "Aucun commit sélectionné" -#: gitk:1445 gitk:4064 gitk:12469 msgid "Command line" msgstr "Ligne de commande" -#: gitk:1511 msgid "Can't parse git log output:" msgstr "Impossible de lire la sortie de git log :" -#: gitk:1740 msgid "No commit information available" msgstr "Aucune information disponible sur le commit" -#: gitk:1903 gitk:1932 gitk:4334 gitk:9702 gitk:11274 gitk:11554 msgid "OK" msgstr "OK" -#: gitk:1934 gitk:4336 gitk:9215 gitk:9294 gitk:9424 gitk:9473 gitk:9704 -#: gitk:11275 gitk:11555 msgid "Cancel" msgstr "Annuler" -#: gitk:2083 msgid "&Update" msgstr "Mise à jour" -#: gitk:2084 msgid "&Reload" msgstr "&Recharger" -#: gitk:2085 msgid "Reread re&ferences" msgstr "Relire les ré&férences" -#: gitk:2086 msgid "&List references" msgstr "&Lister les références" -#: gitk:2088 msgid "Start git &gui" msgstr "Démarrer git &gui" -#: gitk:2090 msgid "&Quit" msgstr "&Quitter" -#: gitk:2082 msgid "&File" msgstr "&Fichier" -#: gitk:2094 msgid "&Preferences" msgstr "Préférences" -#: gitk:2093 msgid "&Edit" msgstr "&Éditer" -#: gitk:2098 msgid "&New view..." msgstr "&Nouvelle vue..." -#: gitk:2099 msgid "&Edit view..." msgstr "&Éditer la vue..." -#: gitk:2100 msgid "&Delete view" msgstr "Supprimer la vue" -#: gitk:2102 msgid "&All files" msgstr "Tous les fichiers" -#: gitk:2097 msgid "&View" msgstr "&Vue" -#: gitk:2107 gitk:2117 msgid "&About gitk" msgstr "À propos de gitk" -#: gitk:2108 gitk:2122 msgid "&Key bindings" msgstr "Raccourcis clavier" -#: gitk:2106 gitk:2121 msgid "&Help" msgstr "Aide" -#: gitk:2199 gitk:8671 msgid "SHA1 ID:" msgstr "Id SHA1 :" -#: gitk:2243 msgid "Row" msgstr "Colonne" -#: gitk:2281 msgid "Find" msgstr "Recherche" -#: gitk:2309 msgid "commit" msgstr "commit" -#: gitk:2313 gitk:2315 gitk:4706 gitk:4729 gitk:4753 gitk:6774 gitk:6846 -#: gitk:6931 msgid "containing:" msgstr "contient :" -#: gitk:2316 gitk:3545 gitk:3550 gitk:4782 msgid "touching paths:" msgstr "chemins modifiés :" -#: gitk:2317 gitk:4796 msgid "adding/removing string:" msgstr "ajoute/supprime la chaîne :" -#: gitk:2318 gitk:4798 msgid "changing lines matching:" msgstr "modifie les lignes vérifiant :" -#: gitk:2327 gitk:2329 gitk:4785 msgid "Exact" msgstr "Exact" -#: gitk:2329 gitk:4873 gitk:6742 msgid "IgnCase" msgstr "Ignorer la casse" -#: gitk:2329 gitk:4755 gitk:4871 gitk:6738 msgid "Regexp" msgstr "Expression régulière" -#: gitk:2331 gitk:2332 gitk:4893 gitk:4923 gitk:4930 gitk:6867 gitk:6935 msgid "All fields" msgstr "Tous les champs" -#: gitk:2332 gitk:4890 gitk:4923 gitk:6805 msgid "Headline" msgstr "Titre" -#: gitk:2333 gitk:4890 gitk:6805 gitk:6935 gitk:7408 msgid "Comments" msgstr "Commentaires" -#: gitk:2333 gitk:4890 gitk:4895 gitk:4930 gitk:6805 gitk:7343 gitk:8849 -#: gitk:8864 msgid "Author" msgstr "Auteur" -#: gitk:2333 gitk:4890 gitk:6805 gitk:7345 msgid "Committer" msgstr "Validateur" -#: gitk:2367 msgid "Search" msgstr "Rechercher" -#: gitk:2375 msgid "Diff" msgstr "Diff" -#: gitk:2377 msgid "Old version" msgstr "Ancienne version" -#: gitk:2379 msgid "New version" msgstr "Nouvelle version" -#: gitk:2382 msgid "Lines of context" msgstr "Lignes de contexte" -#: gitk:2392 msgid "Ignore space change" msgstr "Ignorer les modifications d'espace" -#: gitk:2396 gitk:2398 gitk:7978 gitk:8225 msgid "Line diff" msgstr "différence par ligne" -#: gitk:2463 msgid "Patch" msgstr "Patch" -#: gitk:2465 msgid "Tree" msgstr "Arbre" -#: gitk:2635 gitk:2656 msgid "Diff this -> selected" msgstr "Diff ceci -> la sélection" -#: gitk:2636 gitk:2657 msgid "Diff selected -> this" msgstr "Diff sélection -> ceci" -#: gitk:2637 gitk:2658 msgid "Make patch" msgstr "Créer patch" -#: gitk:2638 gitk:9273 msgid "Create tag" msgstr "Créer étiquette" -#: gitk:2639 msgid "Copy commit summary" msgstr "Copié le résumé du commit" -#: gitk:2640 gitk:9404 msgid "Write commit to file" msgstr "Écrire le commit dans un fichier" -#: gitk:2641 gitk:9461 msgid "Create new branch" msgstr "Créer une nouvelle branche" -#: gitk:2642 msgid "Cherry-pick this commit" msgstr "Cueillir (cherry-pick) ce commit" -#: gitk:2643 msgid "Reset HEAD branch to here" msgstr "Réinitialiser la branche HEAD vers cet état" -#: gitk:2644 msgid "Mark this commit" msgstr "Marquer ce commit" -#: gitk:2645 msgid "Return to mark" msgstr "Retourner à la marque" -#: gitk:2646 msgid "Find descendant of this and mark" msgstr "Chercher le descendant de ceci et le marquer" -#: gitk:2647 msgid "Compare with marked commit" msgstr "Comparer avec le commit marqué" -#: gitk:2648 gitk:2659 msgid "Diff this -> marked commit" msgstr "Diff ceci -> sélection" -#: gitk:2649 gitk:2660 msgid "Diff marked commit -> this" msgstr "Diff entre sélection -> ceci" -#: gitk:2650 msgid "Revert this commit" msgstr "Défaire ce commit" -#: gitk:2666 msgid "Check out this branch" msgstr "Récupérer cette branche" -#: gitk:2667 msgid "Remove this branch" msgstr "Supprimer cette branche" -#: gitk:2668 msgid "Copy branch name" msgstr "Copier la nom de la branche" -#: gitk:2675 msgid "Highlight this too" msgstr "Surligner également ceci" -#: gitk:2676 msgid "Highlight this only" msgstr "Surligner seulement ceci" -#: gitk:2677 msgid "External diff" msgstr "Diff externe" -#: gitk:2678 msgid "Blame parent commit" msgstr "Blâmer le commit parent" -#: gitk:2679 msgid "Copy path" msgstr "Copier le chemin" -#: gitk:2686 msgid "Show origin of this line" msgstr "Montrer l'origine de cette ligne" -#: gitk:2687 msgid "Run git gui blame on this line" msgstr "Exécuter git gui blame sur cette ligne" -#: gitk:3031 msgid "About gitk" msgstr "À propos de gitk" -#: gitk:3033 msgid "" "\n" "Gitk - a commit viewer for git\n" @@ -381,327 +294,254 @@ msgstr "" "\n" "Copyright \\u00a9 2005-2016 Paul Mackerras\n" "\n" -"Utilisation et redistribution soumises aux termes de la GNU General Public License" +"Utilisation et redistribution soumises aux termes de la GNU General Public " +"License" -#: gitk:3041 gitk:3108 gitk:9890 msgid "Close" msgstr "Fermer" -#: gitk:3062 msgid "Gitk key bindings" msgstr "Raccourcis clavier de Gitk" -#: gitk:3065 msgid "Gitk key bindings:" msgstr "Raccourcis clavier de Gitk :" -#: gitk:3067 #, tcl-format msgid "<%s-Q>\t\tQuit" msgstr "<%s-Q>\t\tQuitter" -#: gitk:3068 #, tcl-format msgid "<%s-W>\t\tClose window" msgstr "<%s-W>\t\tFermer la fenêtre" -#: gitk:3069 msgid "<Home>\t\tMove to first commit" msgstr "<Début>\t\tAller au premier commit" -#: gitk:3070 msgid "<End>\t\tMove to last commit" msgstr "<Fin>\t\tAller au dernier commit" -#: gitk:3071 msgid "<Up>, p, k\tMove up one commit" msgstr "<Haut>, p, k\t Aller au commit précédent" -#: gitk:3072 msgid "<Down>, n, j\tMove down one commit" msgstr "<Bas>, n, j\t Aller au commit suivant" -#: gitk:3073 msgid "<Left>, z, h\tGo back in history list" msgstr "<Gauche>, z, h\tReculer dans l'historique" -#: gitk:3074 msgid "<Right>, x, l\tGo forward in history list" msgstr "<Droite>, x, l\tAvancer dans l'historique" -#: gitk:3075 #, tcl-format msgid "<%s-n>\tGo to n-th parent of current commit in history list" msgstr "<%s-n>\tAller sur le n-ième parent du commit dans l'historique" -#: gitk:3076 msgid "<PageUp>\tMove up one page in commit list" msgstr "<PageUp>\tMonter d'une page dans la liste des commits" -#: gitk:3077 msgid "<PageDown>\tMove down one page in commit list" msgstr "<PageDown>\tDescendre d'une page dans la liste des commits" -#: gitk:3078 #, tcl-format msgid "<%s-Home>\tScroll to top of commit list" msgstr "<%s-Début>\tAller en haut de la liste des commits" -#: gitk:3079 #, tcl-format msgid "<%s-End>\tScroll to bottom of commit list" msgstr "<%s-End>\tAller en bas de la liste des commits" -#: gitk:3080 #, tcl-format msgid "<%s-Up>\tScroll commit list up one line" msgstr "<%s-Up>\tMonter d'une ligne dans la liste des commits" -#: gitk:3081 #, tcl-format msgid "<%s-Down>\tScroll commit list down one line" msgstr "<%s-Down>\tDescendre d'une ligne dans la liste des commits" -#: gitk:3082 #, tcl-format msgid "<%s-PageUp>\tScroll commit list up one page" msgstr "<%s-PageUp>\tMonter d'une page dans la liste des commits" -#: gitk:3083 #, tcl-format msgid "<%s-PageDown>\tScroll commit list down one page" msgstr "<%s-PageDown>\tDescendre d'une page dans la liste des commits" -#: gitk:3084 msgid "<Shift-Up>\tFind backwards (upwards, later commits)" msgstr "" "<Shift-Up>\tRecherche en arrière (vers l'avant, commits les plus anciens)" -#: gitk:3085 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)" msgstr "" "<Shift-Down>\tRecherche en avant (vers l'arrière, commit les plus récents)" -#: gitk:3086 msgid "<Delete>, b\tScroll diff view up one page" msgstr "<Supprimer>, b\tMonter d'une page dans la vue des diff" -#: gitk:3087 msgid "<Backspace>\tScroll diff view up one page" msgstr "<Backspace>\tMonter d'une page dans la vue des diff" -#: gitk:3088 msgid "<Space>\t\tScroll diff view down one page" msgstr "<Espace>\t\tDescendre d'une page dans la vue des diff" -#: gitk:3089 msgid "u\t\tScroll diff view up 18 lines" msgstr "u\t\tMonter de 18 lignes dans la vue des diff" -#: gitk:3090 msgid "d\t\tScroll diff view down 18 lines" msgstr "d\t\tDescendre de 18 lignes dans la vue des diff" -#: gitk:3091 #, tcl-format msgid "<%s-F>\t\tFind" msgstr "<%s-F>\t\tRechercher" -#: gitk:3092 #, tcl-format msgid "<%s-G>\t\tMove to next find hit" msgstr "<%s-G>\t\tAller au résultat de recherche suivant" -#: gitk:3093 msgid "<Return>\tMove to next find hit" msgstr "<Return>\t\tAller au résultat de recherche suivant" -#: gitk:3094 msgid "g\t\tGo to commit" msgstr "g\t\tAller au commit" -#: gitk:3095 msgid "/\t\tFocus the search box" msgstr "/\t\tFocus sur la zone de recherche" -#: gitk:3096 msgid "?\t\tMove to previous find hit" msgstr "?\t\tAller au résultat de recherche précédent" -#: gitk:3097 msgid "f\t\tScroll diff view to next file" msgstr "f\t\tAller au prochain fichier dans la vue des diff" -#: gitk:3098 #, tcl-format msgid "<%s-S>\t\tSearch for next hit in diff view" msgstr "<%s-S>\t\tAller au résultat suivant dans la vue des diff" -#: gitk:3099 #, tcl-format msgid "<%s-R>\t\tSearch for previous hit in diff view" msgstr "<%s-R>\t\tAller au résultat précédent dans la vue des diff" -#: gitk:3100 #, tcl-format msgid "<%s-KP+>\tIncrease font size" msgstr "<%s-KP+>\tAugmenter la taille de la police" -#: gitk:3101 #, tcl-format msgid "<%s-plus>\tIncrease font size" msgstr "<%s-plus>\tAugmenter la taille de la police" -#: gitk:3102 #, tcl-format msgid "<%s-KP->\tDecrease font size" msgstr "<%s-KP->\tDiminuer la taille de la police" -#: gitk:3103 #, tcl-format msgid "<%s-minus>\tDecrease font size" msgstr "<%s-minus>\tDiminuer la taille de la police" -#: gitk:3104 msgid "<F5>\t\tUpdate" msgstr "<F5>\t\tMise à jour" -#: gitk:3569 gitk:3578 #, tcl-format msgid "Error creating temporary directory %s:" msgstr "Erreur lors de la création du répertoire temporaire %s :" -#: gitk:3591 #, tcl-format msgid "Error getting \"%s\" from %s:" msgstr "Erreur en obtenant \"%s\" de %s:" -#: gitk:3654 msgid "command failed:" msgstr "échec de la commande :" -#: gitk:3803 msgid "No such commit" msgstr "Commit inexistant" -#: gitk:3817 msgid "git gui blame: command failed:" msgstr "git gui blame : échec de la commande :" -#: gitk:3848 #, tcl-format msgid "Couldn't read merge head: %s" msgstr "Impossible de lire le head de la fusion : %s" -#: gitk:3856 #, tcl-format msgid "Error reading index: %s" msgstr "Erreur à la lecture de l'index : %s" -#: gitk:3881 #, tcl-format msgid "Couldn't start git blame: %s" msgstr "Impossible de démarrer git blame : %s" -#: gitk:3884 gitk:6773 msgid "Searching" msgstr "Recherche en cours" -#: gitk:3916 #, tcl-format msgid "Error running git blame: %s" msgstr "Erreur à l'exécution de git blame : %s" -#: gitk:3944 #, tcl-format msgid "That line comes from commit %s, which is not in this view" msgstr "Cette ligne est issue du commit %s, qui n'est pas dans cette vue" -#: gitk:3958 msgid "External diff viewer failed:" msgstr "Échec de l'outil externe de visualisation des diff :" -#: gitk:4062 msgid "All files" msgstr "Tous les fichiers" -#: gitk:4086 msgid "View" msgstr "Vue" -#: gitk:4089 msgid "Gitk view definition" msgstr "Définition des vues de Gitk" -#: gitk:4093 msgid "Remember this view" msgstr "Se souvenir de cette vue" -#: gitk:4094 msgid "References (space separated list):" msgstr "Références (liste d'éléments séparés par des espaces) :" -#: gitk:4095 msgid "Branches & tags:" msgstr "Branches & étiquettes :" -#: gitk:4096 msgid "All refs" msgstr "Toutes les références" -#: gitk:4097 msgid "All (local) branches" msgstr "Toutes les branches (locales)" -#: gitk:4098 msgid "All tags" msgstr "Toutes les étiquettes" -#: gitk:4099 msgid "All remote-tracking branches" msgstr "Toutes les branches de suivi à distance" -#: gitk:4100 msgid "Commit Info (regular expressions):" msgstr "Info sur les commits (expressions régulières) :" -#: gitk:4101 msgid "Author:" msgstr "Auteur :" -#: gitk:4102 msgid "Committer:" msgstr "Validateur :" -#: gitk:4103 msgid "Commit Message:" msgstr "Message de commit :" -#: gitk:4104 msgid "Matches all Commit Info criteria" msgstr "Correspond à tous les critères d'Info sur les commits" -#: gitk:4105 msgid "Matches no Commit Info criteria" msgstr "Ne correspond à aucun des critères d'Info sur les commits" -#: gitk:4106 msgid "Changes to Files:" msgstr "Changements des fichiers :" -#: gitk:4107 msgid "Fixed String" msgstr "Chaîne Figée" -#: gitk:4108 msgid "Regular Expression" msgstr "Expression Régulière" -#: gitk:4109 msgid "Search string:" msgstr "Recherche de la chaîne :" -#: gitk:4110 msgid "" "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" @@ -709,201 +549,153 @@ msgstr "" "Dates des commits (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, " "2009 15:27:38\") :" -#: gitk:4111 msgid "Since:" msgstr "Depuis :" -#: gitk:4112 msgid "Until:" msgstr "Jusqu'au :" -#: gitk:4113 msgid "Limit and/or skip a number of revisions (positive integer):" msgstr "Limiter et/ou sauter un certain nombre (entier positif) de révisions :" -#: gitk:4114 msgid "Number to show:" msgstr "Nombre à afficher :" -#: gitk:4115 msgid "Number to skip:" msgstr "Nombre à sauter :" -#: gitk:4116 msgid "Miscellaneous options:" msgstr "Options diverses :" -#: gitk:4117 msgid "Strictly sort by date" msgstr "Trier par date" -#: gitk:4118 msgid "Mark branch sides" msgstr "Indiquer les côtés des branches" -#: gitk:4119 msgid "Limit to first parent" msgstr "Limiter au premier ancêtre" -#: gitk:4120 msgid "Simple history" msgstr "Historique simple" -#: gitk:4121 msgid "Additional arguments to git log:" msgstr "Arguments supplémentaires de git log :" -#: gitk:4122 msgid "Enter files and directories to include, one per line:" msgstr "Saisir les fichiers et répertoires à inclure, un par ligne :" -#: gitk:4123 msgid "Command to generate more commits to include:" msgstr "Commande pour générer plus de commits à inclure :" -#: gitk:4247 msgid "Gitk: edit view" msgstr "Gitk : éditer la vue" -#: gitk:4255 msgid "-- criteria for selecting revisions" msgstr "-- critère pour la sélection des révisions" -#: gitk:4260 msgid "View Name" msgstr "Nom de la vue" -#: gitk:4335 msgid "Apply (F5)" msgstr "Appliquer (F5)" -#: gitk:4373 msgid "Error in commit selection arguments:" msgstr "Erreur dans les arguments de sélection des commits :" -#: gitk:4428 gitk:4481 gitk:4943 gitk:4957 gitk:6227 gitk:12410 gitk:12411 msgid "None" msgstr "Aucun" -#: gitk:5040 gitk:5045 msgid "Descendant" msgstr "Descendant" -#: gitk:5041 msgid "Not descendant" msgstr "Pas un descendant" -#: gitk:5048 gitk:5053 msgid "Ancestor" msgstr "Ancêtre" -#: gitk:5049 msgid "Not ancestor" msgstr "Pas un ancêtre" -#: gitk:5343 msgid "Local changes checked in to index but not committed" msgstr "Modifications locales enregistrées dans l'index mais non validées" -#: gitk:5379 msgid "Local uncommitted changes, not checked in to index" msgstr "Modifications locales non enregistrées dans l'index et non validées" -#: gitk:7153 msgid "and many more" msgstr "et beaucoup plus" -#: gitk:7156 msgid "many" msgstr "nombreux" -#: gitk:7347 msgid "Tags:" msgstr "Étiquettes :" -#: gitk:7364 gitk:7370 gitk:8844 msgid "Parent" msgstr "Parent" -#: gitk:7375 msgid "Child" msgstr "Enfant" -#: gitk:7384 msgid "Branch" msgstr "Branche" -#: gitk:7387 msgid "Follows" msgstr "Suit" -#: gitk:7390 msgid "Precedes" msgstr "Précède" -#: gitk:7985 #, tcl-format msgid "Error getting diffs: %s" msgstr "Erreur lors de la récupération des diff : %s" -#: gitk:8669 msgid "Goto:" msgstr "Aller à :" -#: gitk:8690 #, tcl-format msgid "Short SHA1 id %s is ambiguous" msgstr "L'id SHA1 court %s est ambigu" -#: gitk:8697 #, tcl-format msgid "Revision %s is not known" msgstr "La révision %s est inconnu" -#: gitk:8707 #, tcl-format msgid "SHA1 id %s is not known" msgstr "L'id SHA1 %s est inconnu" -#: gitk:8709 #, tcl-format msgid "Revision %s is not in the current view" msgstr "La révision %s n'est pas dans la vue courante" -#: gitk:8851 gitk:8866 msgid "Date" msgstr "Date" -#: gitk:8854 msgid "Children" msgstr "Enfants" -#: gitk:8917 #, tcl-format msgid "Reset %s branch to here" msgstr "Réinitialiser la branche %s vers cet état" -#: gitk:8919 msgid "Detached head: can't reset" msgstr "Head détaché : impossible de réinitialiser" -#: gitk:9024 gitk:9030 msgid "Skipping merge commit " msgstr "Éviter le commit de la fusion " -#: gitk:9039 gitk:9044 msgid "Error getting patch ID for " msgstr "Erreur à l'obtention de l'ID du patch pour " -#: gitk:9040 gitk:9045 msgid " - stopping\n" msgstr " - arrêt en cours\n" -#: gitk:9050 gitk:9053 gitk:9061 gitk:9075 gitk:9084 msgid "Commit " msgstr "Commit " -#: gitk:9054 msgid "" " is the same patch as\n" " " @@ -911,7 +703,6 @@ msgstr "" "est le même patch que \n" " " -#: gitk:9062 msgid "" " differs from\n" " " @@ -919,146 +710,118 @@ msgstr "" " diffère de\n" " " -#: gitk:9064 msgid "" "Diff of commits:\n" "\n" -msgstr "Diff des commits :\n\n" +msgstr "" +"Diff des commits :\n" +"\n" -#: gitk:9076 gitk:9085 #, tcl-format msgid " has %s children - stopping\n" msgstr " a %s enfants - arrêt en cours\n" -#: gitk:9104 #, tcl-format msgid "Error writing commit to file: %s" msgstr "Erreur à l'écriture du commit dans le fichier : %s" -#: gitk:9110 #, tcl-format msgid "Error diffing commits: %s" msgstr "Erreur à la différence des commits : %s" -#: gitk:9156 msgid "Top" msgstr "Haut" -#: gitk:9157 msgid "From" msgstr "De" -#: gitk:9162 msgid "To" msgstr "À" -#: gitk:9186 msgid "Generate patch" msgstr "Générer le patch" -#: gitk:9188 msgid "From:" msgstr "De :" -#: gitk:9197 msgid "To:" msgstr "À :" -#: gitk:9206 msgid "Reverse" msgstr "Inverser" -#: gitk:9208 gitk:9418 msgid "Output file:" msgstr "Fichier de sortie :" -#: gitk:9214 msgid "Generate" msgstr "Générer" -#: gitk:9252 msgid "Error creating patch:" msgstr "Erreur à la création du patch :" -#: gitk:9275 gitk:9406 gitk:9463 msgid "ID:" msgstr "ID :" -#: gitk:9284 msgid "Tag name:" msgstr "Nom de l'étiquette :" -#: gitk:9287 msgid "Tag message is optional" msgstr "Le message d'étiquette est optionnel" -#: gitk:9289 msgid "Tag message:" msgstr "Message d'étiquette :" -#: gitk:9293 gitk:9472 msgid "Create" msgstr "Créer" -#: gitk:9311 msgid "No tag name specified" msgstr "Aucun nom d'étiquette spécifié" -#: gitk:9315 #, tcl-format msgid "Tag \"%s\" already exists" msgstr "L'étiquette \"%s\" existe déjà" -#: gitk:9325 msgid "Error creating tag:" msgstr "Erreur à la création de l'étiquette :" -#: gitk:9415 msgid "Command:" msgstr "Commande :" -#: gitk:9423 msgid "Write" msgstr "Écrire" -#: gitk:9441 msgid "Error writing commit:" msgstr "Erreur à l'ecriture du commit :" -#: gitk:9468 msgid "Name:" msgstr "Nom :" -#: gitk:9491 msgid "Please specify a name for the new branch" msgstr "Veuillez spécifier un nom pour la nouvelle branche" -#: gitk:9496 #, tcl-format msgid "Branch '%s' already exists. Overwrite?" msgstr "La branche '%s' existe déjà. Écraser?" -#: gitk:9563 #, tcl-format msgid "Commit %s is already included in branch %s -- really re-apply it?" msgstr "" "Le Commit %s est déjà inclus dans la branche %s -- le ré-appliquer malgré " "tout?" -#: gitk:9568 msgid "Cherry-picking" msgstr "Picorer (Cherry-picking)" -#: gitk:9577 #, tcl-format msgid "" "Cherry-pick failed because of local changes to file '%s'.\n" "Please commit, reset or stash your changes and try again." msgstr "" -"Le picorage (cherry-pick) a échouée à cause de modifications locales du fichier '%s'.\n" -"Veuillez commiter, réinitialiser ou stasher vos changements et essayer de nouveau." +"Le picorage (cherry-pick) a échouée à cause de modifications locales du " +"fichier '%s'.\n" +"Veuillez commiter, réinitialiser ou stasher vos changements et essayer de " +"nouveau." -#: gitk:9583 msgid "" "Cherry-pick failed because of merge conflict.\n" "Do you wish to run git citool to resolve it?" @@ -1066,27 +829,26 @@ msgstr "" "Le picorage (cherry-pick) a échouée à cause d'un conflit lors d'une fusion.\n" "Souhaitez-vous exécuter git citool pour le résoudre ?" -#: gitk:9599 gitk:9657 msgid "No changes committed" msgstr "Aucune modification validée" -#: gitk:9626 #, tcl-format msgid "Commit %s is not included in branch %s -- really revert it?" -msgstr "Le Commit %s n'est pas inclus dans la branche %s -- le défaire malgré tout?" +msgstr "" +"Le Commit %s n'est pas inclus dans la branche %s -- le défaire malgré tout?" -#: gitk:9631 msgid "Reverting" msgstr "Commit défait" -#: gitk:9639 #, tcl-format msgid "" "Revert failed because of local changes to the following files:%s Please " "commit, reset or stash your changes and try again." -msgstr "Échec en tentant de défaire le commit à cause de modifications locales des fichiers : %s. Veuillez valider, réinitialiser ou remiser vos modifications et essayer de nouveau." +msgstr "" +"Échec en tentant de défaire le commit à cause de modifications locales des " +"fichiers : %s. Veuillez valider, réinitialiser ou remiser vos modifications " +"et essayer de nouveau." -#: gitk:9643 msgid "" "Revert failed because of merge conflict.\n" " Do you wish to run git citool to resolve it?" @@ -1094,30 +856,24 @@ msgstr "" "Échec en tentant de défaire à cause d'un conflit de fusion.\n" "Souhaitez-vous exécuter git citool pour le résoudre ?" -#: gitk:9686 msgid "Confirm reset" msgstr "Confirmer la réinitialisation" -#: gitk:9688 #, tcl-format msgid "Reset branch %s to %s?" msgstr "Réinitialiser la branche %s à %s?" -#: gitk:9690 msgid "Reset type:" msgstr "Type de réinitialisation :" -#: gitk:9693 msgid "Soft: Leave working tree and index untouched" msgstr "Douce : Laisse le répertoire de travail et l'index intacts" -#: gitk:9696 msgid "Mixed: Leave working tree untouched, reset index" msgstr "" "Hybride : Laisse le répertoire de travail dans son état courant, " "réinitialise l'index" -#: gitk:9699 msgid "" "Hard: Reset working tree and index\n" "(discard ALL local changes)" @@ -1125,20 +881,16 @@ msgstr "" "Dure : Réinitialise le répertoire de travail et l'index\n" "(abandonne TOUTES les modifications locale)" -#: gitk:9716 msgid "Resetting" msgstr "Réinitialisation" # Fixme: Récupération est-il vraiment une mauvaise traduction? -#: gitk:9776 msgid "Checking out" msgstr "Extraction" -#: gitk:9829 msgid "Cannot delete the currently checked-out branch" msgstr "Impossible de supprimer la branche extraite" -#: gitk:9835 #, tcl-format msgid "" "The commits on branch %s aren't on any other branch.\n" @@ -1147,16 +899,13 @@ msgstr "" "Les commits de la branche %s ne sont dans aucune autre branche.\n" "Voulez-vous vraiment supprimer cette branche %s ?" -#: gitk:9866 #, tcl-format msgid "Tags and heads: %s" msgstr "Étiquettes et heads : %s" -#: gitk:9883 msgid "Filter" msgstr "Filtrer" -#: gitk:10179 msgid "" "Error reading commit topology information; branch and preceding/following " "tag information will be incomplete." @@ -1165,202 +914,153 @@ msgstr "" "informations sur les branches et les tags précédents/suivants seront " "incomplètes." -#: gitk:11156 msgid "Tag" msgstr "Étiquette" -#: gitk:11160 msgid "Id" msgstr "Id" -#: gitk:11243 msgid "Gitk font chooser" msgstr "Sélecteur de police de Gitk" -#: gitk:11260 msgid "B" msgstr "B" -#: gitk:11263 msgid "I" msgstr "I" -#: gitk:11381 msgid "Commit list display options" msgstr "Options d'affichage de la liste des commits" -#: gitk:11384 msgid "Maximum graph width (lines)" msgstr "Longueur maximum du graphe (lignes)" # FIXME : Traduction standard de "pane"? -#: gitk:11388 #, no-tcl-format msgid "Maximum graph width (% of pane)" msgstr "Largeur maximum du graphe (% du panneau)" -#: gitk:11391 msgid "Show local changes" msgstr "Montrer les modifications locales" -#: gitk:11394 msgid "Auto-select SHA1 (length)" msgstr "Sélection auto. du SHA1 (longueur)" -#: gitk:11398 msgid "Hide remote refs" msgstr "Cacher les refs distantes" -#: gitk:11402 msgid "Diff display options" msgstr "Options d'affichage des diff" -#: gitk:11404 msgid "Tab spacing" msgstr "Taille des tabulations" -#: gitk:11407 msgid "Display nearby tags/heads" msgstr "Afficher les tags les plus proches" -#: gitk:11410 msgid "Maximum # tags/heads to show" msgstr "Nombre maximum d'étiquettes/heads à afficher" -#: gitk:11413 msgid "Limit diffs to listed paths" msgstr "Limiter les différences aux chemins listés" -#: gitk:11416 msgid "Support per-file encodings" msgstr "Support pour un encodage des caractères par fichier" -#: gitk:11422 gitk:11569 msgid "External diff tool" msgstr "Outil diff externe" -#: gitk:11423 msgid "Choose..." msgstr "Choisir..." -#: gitk:11428 msgid "General options" msgstr "Options générales" -#: gitk:11431 msgid "Use themed widgets" msgstr "Utiliser des widgets en thème" -#: gitk:11433 msgid "(change requires restart)" msgstr "(la modification nécessite un redémarrage)" -#: gitk:11435 msgid "(currently unavailable)" msgstr "(non disponible actuellement)" -#: gitk:11446 msgid "Colors: press to choose" msgstr "Couleurs : cliquer pour choisir" -#: gitk:11449 msgid "Interface" msgstr "Interface" -#: gitk:11450 msgid "interface" msgstr "interface" -#: gitk:11453 msgid "Background" msgstr "Arrière-plan" -#: gitk:11454 gitk:11484 msgid "background" msgstr "arrière-plan" -#: gitk:11457 msgid "Foreground" msgstr "Premier plan" -#: gitk:11458 msgid "foreground" msgstr "premier plan" -#: gitk:11461 msgid "Diff: old lines" msgstr "Diff : anciennes lignes" -#: gitk:11462 msgid "diff old lines" msgstr "diff anciennes lignes" -#: gitk:11466 msgid "Diff: new lines" msgstr "Diff : nouvelles lignes" -#: gitk:11467 msgid "diff new lines" msgstr "diff nouvelles lignes" -#: gitk:11471 msgid "Diff: hunk header" msgstr "Diff : entête du hunk" -#: gitk:11473 msgid "diff hunk header" msgstr "diff : entête du hunk" -#: gitk:11477 msgid "Marked line bg" msgstr "Fond de la ligne marquée" -#: gitk:11479 msgid "marked line background" msgstr "Fond de la ligne marquée" -#: gitk:11483 msgid "Select bg" msgstr "Sélectionner le fond" -#: gitk:11492 msgid "Fonts: press to choose" msgstr "Polices : cliquer pour choisir" -#: gitk:11494 msgid "Main font" msgstr "Police principale" -#: gitk:11495 msgid "Diff display font" msgstr "Police d'affichage des diff" -#: gitk:11496 msgid "User interface font" msgstr "Police de l'interface utilisateur" -#: gitk:11518 msgid "Gitk preferences" msgstr "Préférences de Gitk" -#: gitk:11527 msgid "General" msgstr "Général" -#: gitk:11528 msgid "Colors" msgstr "Couleurs" -#: gitk:11529 msgid "Fonts" msgstr "Polices" -#: gitk:11579 #, tcl-format msgid "Gitk: choose color for %s" msgstr "Gitk : choisir la couleur de %s" -#: gitk:12092 msgid "" "Sorry, gitk cannot run with this version of Tcl/Tk.\n" " Gitk requires at least Tcl/Tk 8.4." @@ -1368,16 +1068,13 @@ msgstr "" "Désolé, gitk ne peut être exécuté avec cette version de Tcl/Tk.\n" " Gitk requiert Tcl/Tk version 8.4 ou supérieur." -#: gitk:12302 msgid "Cannot find a git repository here." msgstr "Impossible de trouver un dépôt git ici." -#: gitk:12349 #, tcl-format msgid "Ambiguous argument '%s': both revision and filename" msgstr "Argument '%s' ambigu : à la fois une révision et un nom de fichier" -#: gitk:12361 msgid "Bad arguments to gitk:" msgstr "Arguments invalides pour gitk :" diff --git a/gitk-git/po/hu.po b/gitk-git/po/hu.po index 79ec5a5656..9ab3f9617d 100644 --- a/gitk-git/po/hu.po +++ b/gitk-git/po/hu.po @@ -6,7 +6,7 @@ # msgid "" msgstr "" -"Project-Id-Version: git-gui\n" +"Project-Id-Version: Gitk\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-05-17 14:32+1000\n" "PO-Revision-Date: 2009-12-14 14:04+0100\n" @@ -17,32 +17,25 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: gitk:140 msgid "Couldn't get list of unmerged files:" msgstr "Nem sikerült letölteni az unmerged fájl listát:" -#: gitk:212 gitk:2381 msgid "Color words" msgstr "" -#: gitk:217 gitk:2381 gitk:8220 gitk:8253 msgid "Markup words" msgstr "" -#: gitk:324 msgid "Error parsing revisions:" msgstr "Hiba történt értelmezés közben:" -#: gitk:380 msgid "Error executing --argscmd command:" msgstr "Hiba történt a végrehajtáskor --argscmd parancs:" -#: gitk:393 msgid "No files selected: --merge specified but no files are unmerged." msgstr "" "Nincsen fájl kiválasztva: --merge megadve, de egyetlen fájl sem unmerged." -#: gitk:396 msgid "" "No files selected: --merge specified but no unmerged files are within file " "limit." @@ -50,317 +43,237 @@ msgstr "" "Nincsen fájl kiválasztva: --merge megadva, de nincsenek unmerged fájlok a " "fájlon belül limit." -#: gitk:418 gitk:566 msgid "Error executing git log:" msgstr "Hiba történt a git log végrehajtása közben:" -#: gitk:436 gitk:582 msgid "Reading" msgstr "Olvasás" -#: gitk:496 gitk:4525 msgid "Reading commits..." msgstr "Commitok olvasása ..." -#: gitk:499 gitk:1637 gitk:4528 msgid "No commits selected" msgstr "Nincsen commit kiválasztva" -#: gitk:1445 gitk:4045 gitk:12432 msgid "Command line" msgstr "Parancs sor" -#: gitk:1511 msgid "Can't parse git log output:" msgstr "Nem lehet értelmezni a git log kimenetét:" -#: gitk:1740 msgid "No commit information available" msgstr "Nincsen elérhető commit információ" -#: gitk:1903 gitk:1932 gitk:4315 gitk:9669 gitk:11241 gitk:11521 msgid "OK" msgstr "OK" -#: gitk:1934 gitk:4317 gitk:9196 gitk:9275 gitk:9391 gitk:9440 gitk:9671 -#: gitk:11242 gitk:11522 msgid "Cancel" msgstr "Visszavonás" -#: gitk:2069 msgid "&Update" msgstr "Frissités" -#: gitk:2070 msgid "&Reload" msgstr "Újratöltés" -#: gitk:2071 msgid "Reread re&ferences" msgstr "Referenciák újraolvasása" -#: gitk:2072 msgid "&List references" msgstr "Referenciák listázása" -#: gitk:2074 msgid "Start git &gui" msgstr "Git gui indítása" -#: gitk:2076 msgid "&Quit" msgstr "Kilépés" -#: gitk:2068 msgid "&File" msgstr "Fájl" -#: gitk:2080 msgid "&Preferences" msgstr "Beállítások" -#: gitk:2079 msgid "&Edit" msgstr "Szerkesztés" -#: gitk:2084 msgid "&New view..." msgstr "Új nézet ..." -#: gitk:2085 msgid "&Edit view..." msgstr "Nézet szerkesztése ..." -#: gitk:2086 msgid "&Delete view" msgstr "Nézet törlése" -#: gitk:2088 gitk:4043 msgid "&All files" msgstr "Minden fájl" -#: gitk:2083 gitk:4067 msgid "&View" msgstr "Nézet" -#: gitk:2093 gitk:2103 gitk:3012 msgid "&About gitk" msgstr "Gitk névjegy" -#: gitk:2094 gitk:2108 msgid "&Key bindings" msgstr "Billentyűkombináció" -#: gitk:2092 gitk:2107 msgid "&Help" msgstr "Segítség" -#: gitk:2185 gitk:8652 msgid "SHA1 ID:" msgstr "SHA1 ID:" -#: gitk:2229 msgid "Row" msgstr "Sor" -#: gitk:2267 msgid "Find" msgstr "Keresés" -#: gitk:2295 msgid "commit" msgstr "commit" -#: gitk:2299 gitk:2301 gitk:4687 gitk:4710 gitk:4734 gitk:6755 gitk:6827 -#: gitk:6912 msgid "containing:" msgstr "tartalmazás:" -#: gitk:2302 gitk:3526 gitk:3531 gitk:4763 msgid "touching paths:" msgstr "érintendő útvonalak:" -#: gitk:2303 gitk:4777 msgid "adding/removing string:" msgstr "string hozzáadása/törlése:" -#: gitk:2304 gitk:4779 msgid "changing lines matching:" msgstr "" -#: gitk:2313 gitk:2315 gitk:4766 msgid "Exact" msgstr "Pontos" -#: gitk:2315 gitk:4854 gitk:6723 msgid "IgnCase" msgstr "Kis/nagy betű nem számít" -#: gitk:2315 gitk:4736 gitk:4852 gitk:6719 msgid "Regexp" msgstr "Regexp" -#: gitk:2317 gitk:2318 gitk:4874 gitk:4904 gitk:4911 gitk:6848 gitk:6916 msgid "All fields" msgstr "Minden mező" -#: gitk:2318 gitk:4871 gitk:4904 gitk:6786 msgid "Headline" msgstr "Főcím" -#: gitk:2319 gitk:4871 gitk:6786 gitk:6916 gitk:7389 msgid "Comments" msgstr "Megjegyzések" -#: gitk:2319 gitk:4871 gitk:4876 gitk:4911 gitk:6786 gitk:7324 gitk:8830 -#: gitk:8845 msgid "Author" msgstr "Szerző" -#: gitk:2319 gitk:4871 gitk:6786 gitk:7326 msgid "Committer" msgstr "Commitoló" -#: gitk:2350 msgid "Search" msgstr "Keresés" -#: gitk:2358 msgid "Diff" msgstr "Diff" -#: gitk:2360 msgid "Old version" msgstr "Régi verzió" -#: gitk:2362 msgid "New version" msgstr "Új verzió" -#: gitk:2364 msgid "Lines of context" msgstr "Tartalmi sorok" -#: gitk:2374 msgid "Ignore space change" msgstr "Space váltás mellőzése" -#: gitk:2378 gitk:2380 gitk:7959 gitk:8206 msgid "Line diff" msgstr "" -#: gitk:2445 msgid "Patch" msgstr "Patch" -#: gitk:2447 msgid "Tree" msgstr "Tree" -#: gitk:2617 gitk:2637 msgid "Diff this -> selected" msgstr "Diff ezeket -> kiválasztott" -#: gitk:2618 gitk:2638 msgid "Diff selected -> this" msgstr "Diff kiválasztottakat -> ezt" -#: gitk:2619 gitk:2639 msgid "Make patch" msgstr "Patch készítése" -#: gitk:2620 gitk:9254 msgid "Create tag" msgstr "Tag készítése" -#: gitk:2621 gitk:9371 msgid "Write commit to file" msgstr "Commit fáljba írása" -#: gitk:2622 gitk:9428 msgid "Create new branch" msgstr "Új branch készítése" -#: gitk:2623 msgid "Cherry-pick this commit" msgstr "Cherry-pick erre a commitra" -#: gitk:2624 msgid "Reset HEAD branch to here" msgstr "HEAD branch újraindítása ide" -#: gitk:2625 msgid "Mark this commit" msgstr "Ezen commit megjelölése" -#: gitk:2626 msgid "Return to mark" msgstr "Visszatérés a megjelöléshez" -#: gitk:2627 msgid "Find descendant of this and mark" msgstr "Találd meg ezen utódokat és jelöld meg" -#: gitk:2628 msgid "Compare with marked commit" msgstr "Összehasonlítás a megjelölt commit-tal" -#: gitk:2629 gitk:2640 #, fuzzy msgid "Diff this -> marked commit" msgstr "Diff ezeket -> kiválasztott" -#: gitk:2630 gitk:2641 #, fuzzy msgid "Diff marked commit -> this" msgstr "Diff kiválasztottakat -> ezt" -#: gitk:2631 #, fuzzy msgid "Revert this commit" msgstr "Ezen commit megjelölése" -#: gitk:2647 msgid "Check out this branch" msgstr "Check out ezt a branchot" -#: gitk:2648 msgid "Remove this branch" msgstr "Töröld ezt a branch-ot" -#: gitk:2649 msgid "Copy branch name" msgstr "" -#: gitk:2656 msgid "Highlight this too" msgstr "Emeld ki ezt is" -#: gitk:2657 msgid "Highlight this only" msgstr "Csak ezt emeld ki" -#: gitk:2658 msgid "External diff" msgstr "Külső diff" -#: gitk:2659 msgid "Blame parent commit" msgstr "Blame szülő kommitra" -#: gitk:2660 msgid "Copy path" msgstr "" -#: gitk:2667 msgid "Show origin of this line" msgstr "Mutasd meg ennek a sornak az eredetét" -#: gitk:2668 msgid "Run git gui blame on this line" msgstr "Futtasd a git gui blame-t ezen a soron" -#: gitk:3014 #, fuzzy msgid "" "\n" @@ -377,321 +290,249 @@ msgstr "" "\n" "Használd és terjeszd a GNU General Public License feltételei mellett" -#: gitk:3022 gitk:3089 gitk:9857 msgid "Close" msgstr "Bezárás" -#: gitk:3043 msgid "Gitk key bindings" msgstr "Gitk-billentyű hozzárendelés" -#: gitk:3046 msgid "Gitk key bindings:" msgstr "Gitk-billentyű hozzaárendelés:" -#: gitk:3048 #, tcl-format msgid "<%s-Q>\t\tQuit" msgstr "<%s-Q>\t\tKilépés" -#: gitk:3049 #, fuzzy, tcl-format msgid "<%s-W>\t\tClose window" msgstr "<%s-F>\t\tKeresés" -#: gitk:3050 msgid "<Home>\t\tMove to first commit" msgstr "<Pos1>\t\tElső commithoz" -#: gitk:3051 msgid "<End>\t\tMove to last commit" msgstr "<Ende>\t\tUtolsó commithoz" -#: gitk:3052 #, fuzzy msgid "<Up>, p, k\tMove up one commit" msgstr "<Hoch>, p, i\tEgy committal feljebb" -#: gitk:3053 #, fuzzy msgid "<Down>, n, j\tMove down one commit" msgstr "<Runter>, n, k\tEgy committal lejjebb" -#: gitk:3054 #, fuzzy msgid "<Left>, z, h\tGo back in history list" msgstr "<Links>, z, j\tVissza a history listába" -#: gitk:3055 msgid "<Right>, x, l\tGo forward in history list" msgstr "<Rechts>, x, l\tElőre a history listába" -#: gitk:3056 #, tcl-format msgid "<%s-n>\tGo to n-th parent of current commit in history list" msgstr "" -#: gitk:3057 msgid "<PageUp>\tMove up one page in commit list" msgstr "<BildHoch>\tEgy lappal feljebb a commit listába" -#: gitk:3058 msgid "<PageDown>\tMove down one page in commit list" msgstr "<BildRunter>\tEgy lappal lejjebb a commit listába" -#: gitk:3059 #, tcl-format msgid "<%s-Home>\tScroll to top of commit list" msgstr "<%s-Pos1>\tGörgetés a commit lista tetejéhez" -#: gitk:3060 #, tcl-format msgid "<%s-End>\tScroll to bottom of commit list" msgstr "<%s-Ende>\tGörgetés a commit lista aljához" -#: gitk:3061 #, tcl-format msgid "<%s-Up>\tScroll commit list up one line" msgstr "<%s-Hoch>\tEgy sorral feljebb görgetés a commit listában" -#: gitk:3062 #, tcl-format msgid "<%s-Down>\tScroll commit list down one line" msgstr "<%s-Runter>\tEgy sorral lejjebb görgetés a commit listában" -#: gitk:3063 #, tcl-format msgid "<%s-PageUp>\tScroll commit list up one page" msgstr "<%s-BildHoch>\tEgy lappal feljebb görgetés a commit listában" -#: gitk:3064 #, tcl-format msgid "<%s-PageDown>\tScroll commit list down one page" msgstr "<%s-BildRunter>\tEgy sorral lejjebb görgetés a commit listában" -#: gitk:3065 msgid "<Shift-Up>\tFind backwards (upwards, later commits)" msgstr "<Umschalt-Hoch>\tKeresés visszafele (felfele, utolsó commitok)" -#: gitk:3066 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)" msgstr "<Umschalt-Runter>\tKeresés előre (lefelé; korábbi commitok)" -#: gitk:3067 msgid "<Delete>, b\tScroll diff view up one page" msgstr "<Entf>, b\t\tEgy lappal feljebb görgetés a diff nézetben" -#: gitk:3068 msgid "<Backspace>\tScroll diff view up one page" msgstr "<Löschtaste>\tEgy lappal feljebb görgetés a diff nézetben" -#: gitk:3069 msgid "<Space>\t\tScroll diff view down one page" msgstr "<Leertaste>\tEgy lappal lejjebb görgetés a diff nézetben" -#: gitk:3070 msgid "u\t\tScroll diff view up 18 lines" msgstr "u\t\t18 sorral felfelé görgetés diff nézetben" -#: gitk:3071 msgid "d\t\tScroll diff view down 18 lines" msgstr "d\t\t18 sorral lejjebb görgetés a diff nézetben" -#: gitk:3072 #, tcl-format msgid "<%s-F>\t\tFind" msgstr "<%s-F>\t\tKeresés" -#: gitk:3073 #, tcl-format msgid "<%s-G>\t\tMove to next find hit" msgstr "<%s-G>\t\tKövetkező találathoz" -#: gitk:3074 msgid "<Return>\tMove to next find hit" msgstr "<Eingabetaste>\tKövetkező találathoz" -#: gitk:3075 #, fuzzy msgid "g\t\tGo to commit" msgstr "<Ende>\t\tUtolsó commithoz" -#: gitk:3076 msgid "/\t\tFocus the search box" msgstr "/\t\tLépj a keresési mezőre" -#: gitk:3077 msgid "?\t\tMove to previous find hit" msgstr "?\t\tElőző találathoz" -#: gitk:3078 msgid "f\t\tScroll diff view to next file" msgstr "f\t\tKövetkező fájlra görgetés diff nézetben" -#: gitk:3079 #, tcl-format msgid "<%s-S>\t\tSearch for next hit in diff view" msgstr "<%s-S>\t\tKövetkező találatra keresés diff nézetben" -#: gitk:3080 #, tcl-format msgid "<%s-R>\t\tSearch for previous hit in diff view" msgstr "<%s-R>\t\tElőző találatra keresés diff nézetben" -#: gitk:3081 #, tcl-format msgid "<%s-KP+>\tIncrease font size" msgstr "<%s-Nummerblock-Plus>\tBetűméret növelése" -#: gitk:3082 #, tcl-format msgid "<%s-plus>\tIncrease font size" msgstr "<%s-Plus>\tBetűméret növelése" -#: gitk:3083 #, tcl-format msgid "<%s-KP->\tDecrease font size" msgstr "<%s-Nummernblock-Minus> Betűméret csökkentése" -#: gitk:3084 #, tcl-format msgid "<%s-minus>\tDecrease font size" msgstr "<%s-Minus>\tBetűméret csökkentése" -#: gitk:3085 msgid "<F5>\t\tUpdate" msgstr "<F5>\t\tFrissítés" -#: gitk:3550 gitk:3559 #, tcl-format msgid "Error creating temporary directory %s:" msgstr "Hiba történt az ideiglenes könyvtár létrehozása közben %s:" -#: gitk:3572 #, tcl-format msgid "Error getting \"%s\" from %s:" msgstr "Hiba történt \"%s\" letöltése közben %s-ről:" -#: gitk:3635 msgid "command failed:" msgstr "parancs hiba:" -#: gitk:3784 msgid "No such commit" msgstr "Nincs ilyen commit" -#: gitk:3798 msgid "git gui blame: command failed:" msgstr "git gui blame: parancs hiba:" -#: gitk:3829 #, tcl-format msgid "Couldn't read merge head: %s" msgstr "Nem sikerült a Merge head olvasása: %s" -#: gitk:3837 #, tcl-format msgid "Error reading index: %s" msgstr "Hiba történt az index olvasása közben: %s" -#: gitk:3862 #, tcl-format msgid "Couldn't start git blame: %s" msgstr "Nem sikerült a git blame indítása: %s" -#: gitk:3865 gitk:6754 msgid "Searching" msgstr "Keresés" -#: gitk:3897 #, tcl-format msgid "Error running git blame: %s" msgstr "Hiba történt a git blame futtatása közben: %s" -#: gitk:3925 #, tcl-format msgid "That line comes from commit %s, which is not in this view" msgstr "" "A %s commitból származik az a sor, amelyik nem található ebben a nézetben" -#: gitk:3939 msgid "External diff viewer failed:" msgstr "Külső diff nézegető hiba:" -#: gitk:4070 msgid "Gitk view definition" msgstr "Gitk nézet meghatározása" -#: gitk:4074 msgid "Remember this view" msgstr "Maradj ennél a nézetnél" -#: gitk:4075 msgid "References (space separated list):" msgstr "Referenciák (szóközzel tagolt lista" -#: gitk:4076 msgid "Branches & tags:" msgstr "Branch-ek & tagek:" -#: gitk:4077 msgid "All refs" msgstr "Minden ref" -#: gitk:4078 msgid "All (local) branches" msgstr "Minden (helyi) branch" -#: gitk:4079 msgid "All tags" msgstr "Minden tag" -#: gitk:4080 msgid "All remote-tracking branches" msgstr "Minden távoli követő branch" -#: gitk:4081 msgid "Commit Info (regular expressions):" msgstr "Commit Infó (reguláris kifejezés):" -#: gitk:4082 msgid "Author:" msgstr "Szerző:" -#: gitk:4083 msgid "Committer:" msgstr "Commitoló:" -#: gitk:4084 msgid "Commit Message:" msgstr "Commit üzenet:" -#: gitk:4085 msgid "Matches all Commit Info criteria" msgstr "Egyezik minen Commit Infó feltétellel" -#: gitk:4086 #, fuzzy msgid "Matches no Commit Info criteria" msgstr "Egyezik minen Commit Infó feltétellel" -#: gitk:4087 msgid "Changes to Files:" msgstr "Fájl változások:" -#: gitk:4088 msgid "Fixed String" msgstr "Fix String" -#: gitk:4089 msgid "Regular Expression" msgstr "Reguláris kifejezés" -#: gitk:4090 msgid "Search string:" msgstr "Keresés szöveg:" -#: gitk:4091 msgid "" "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" @@ -699,203 +540,155 @@ msgstr "" "Commit Dátumok (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" -#: gitk:4092 msgid "Since:" msgstr "Ettől:" -#: gitk:4093 msgid "Until:" msgstr "Eddig:" -#: gitk:4094 msgid "Limit and/or skip a number of revisions (positive integer):" msgstr "Limitálva és/vagy kihagyva egy adott számú revíziót (pozitív egész):" -#: gitk:4095 msgid "Number to show:" msgstr "Mutatandó szám:" -#: gitk:4096 msgid "Number to skip:" msgstr "Kihagyandó szám:" -#: gitk:4097 msgid "Miscellaneous options:" msgstr "Különféle opciók:" -#: gitk:4098 msgid "Strictly sort by date" msgstr "Szigorú rendezás dátum alapján" -#: gitk:4099 msgid "Mark branch sides" msgstr "Jelölje meg az ágakat" -#: gitk:4100 msgid "Limit to first parent" msgstr "Korlátozás az első szülőre" -#: gitk:4101 msgid "Simple history" msgstr "Egyszerű history" -#: gitk:4102 msgid "Additional arguments to git log:" msgstr "További argumentok a git log-hoz:" -#: gitk:4103 msgid "Enter files and directories to include, one per line:" msgstr "Fájlok és könyvtárak bejegyzése amiket tartalmaz, soronként:" -#: gitk:4104 msgid "Command to generate more commits to include:" msgstr "Parancs több tartalmazó commit generálására:" -#: gitk:4228 msgid "Gitk: edit view" msgstr "Gitk: szerkesztés nézet" -#: gitk:4236 msgid "-- criteria for selecting revisions" msgstr "-- kritériumok a revíziók kiválasztásához" -#: gitk:4241 msgid "View Name" msgstr "Nézet neve" -#: gitk:4316 msgid "Apply (F5)" msgstr "Alkalmaz (F5)" -#: gitk:4354 msgid "Error in commit selection arguments:" msgstr "Hiba történt a commit argumentumok kiválasztása közben:" -#: gitk:4409 gitk:4462 gitk:4924 gitk:4938 gitk:6208 gitk:12373 gitk:12374 msgid "None" msgstr "Keine" -#: gitk:5021 gitk:5026 msgid "Descendant" msgstr "Leszármazott" -#: gitk:5022 msgid "Not descendant" msgstr "Nem leszármazott" -#: gitk:5029 gitk:5034 msgid "Ancestor" msgstr "Előd" -#: gitk:5030 msgid "Not ancestor" msgstr "Nem előd" -#: gitk:5324 msgid "Local changes checked in to index but not committed" msgstr "" "Lokális változtatások, melyek be vannak téve az indexbe, de még nincsenek " "commitolva" -#: gitk:5360 msgid "Local uncommitted changes, not checked in to index" msgstr "Lokális nem commitolt változások, nincsenek betéve az indexbe" -#: gitk:7134 msgid "and many more" msgstr "" -#: gitk:7137 msgid "many" msgstr "sok" -#: gitk:7328 msgid "Tags:" msgstr "Tagek:" -#: gitk:7345 gitk:7351 gitk:8825 msgid "Parent" msgstr "Eltern" -#: gitk:7356 msgid "Child" msgstr "Gyerek" -#: gitk:7365 msgid "Branch" msgstr "Ág" -#: gitk:7368 msgid "Follows" msgstr "Következők" -#: gitk:7371 msgid "Precedes" msgstr "Megelőzők" -#: gitk:7966 #, tcl-format msgid "Error getting diffs: %s" msgstr "Hiba történt a diff-ek letöltése közben: %s" -#: gitk:8650 msgid "Goto:" msgstr "Menj:" -#: gitk:8671 #, tcl-format msgid "Short SHA1 id %s is ambiguous" msgstr "Rövid SHA1 id %s félreérthető" -#: gitk:8678 #, tcl-format msgid "Revision %s is not known" msgstr "A(z) %s revízió nem ismert" -#: gitk:8688 #, tcl-format msgid "SHA1 id %s is not known" msgstr "SHA1 id %s nem ismert" -#: gitk:8690 #, tcl-format msgid "Revision %s is not in the current view" msgstr "A(z) %s revízió nincs a jelenlegi nézetben" -#: gitk:8832 gitk:8847 msgid "Date" msgstr "Dátum" -#: gitk:8835 msgid "Children" msgstr "Gyerekek" -#: gitk:8898 #, tcl-format msgid "Reset %s branch to here" msgstr "Állítsd vissza a %s branch-ot ide" -#: gitk:8900 msgid "Detached head: can't reset" msgstr "Elkülönített head: nem lehet visszaállítani" -#: gitk:9005 gitk:9011 msgid "Skipping merge commit " msgstr "Merge commit kihagyása " -#: gitk:9020 gitk:9025 msgid "Error getting patch ID for " msgstr "Hiba történt a patch ID megszerzése közben a következőnél " -#: gitk:9021 gitk:9026 msgid " - stopping\n" msgstr " - abbahagyás\n" -#: gitk:9031 gitk:9034 gitk:9042 gitk:9056 gitk:9065 msgid "Commit " msgstr "Commit " -#: gitk:9035 msgid "" " is the same patch as\n" " " @@ -903,7 +696,6 @@ msgstr "" " Ugyanaz a patch mint\n" " " -#: gitk:9043 msgid "" " differs from\n" " " @@ -911,7 +703,6 @@ msgstr "" " különbözik innentől\n" " " -#: gitk:9045 msgid "" "Diff of commits:\n" "\n" @@ -919,132 +710,102 @@ msgstr "" "A commitok diffje:\n" "\n" -#: gitk:9057 gitk:9066 #, tcl-format msgid " has %s children - stopping\n" msgstr " %s gyereke van. abbahagyás\n" -#: gitk:9085 #, tcl-format msgid "Error writing commit to file: %s" msgstr "Hiba történt a commit fájlba írása közben: %s" -#: gitk:9091 #, tcl-format msgid "Error diffing commits: %s" msgstr "Hiba történt a commitok diffelése közben: %s" -#: gitk:9137 msgid "Top" msgstr "Teteje" -#: gitk:9138 msgid "From" msgstr "Innen" -#: gitk:9143 msgid "To" msgstr "Ide" -#: gitk:9167 msgid "Generate patch" msgstr "Patch generálása" -#: gitk:9169 msgid "From:" msgstr "Innen:" -#: gitk:9178 msgid "To:" msgstr "Ide:" -#: gitk:9187 msgid "Reverse" msgstr "Visszafele" -#: gitk:9189 gitk:9385 msgid "Output file:" msgstr "Kimeneti fájl:" -#: gitk:9195 msgid "Generate" msgstr "Generálás" -#: gitk:9233 msgid "Error creating patch:" msgstr "Hiba törtét a patch készítése közben:" -#: gitk:9256 gitk:9373 gitk:9430 msgid "ID:" msgstr "ID:" -#: gitk:9265 msgid "Tag name:" msgstr "Tag név:" -#: gitk:9268 msgid "Tag message is optional" msgstr "" -#: gitk:9270 #, fuzzy msgid "Tag message:" msgstr "Tag név:" -#: gitk:9274 gitk:9439 msgid "Create" msgstr "Létrehozás" -#: gitk:9292 msgid "No tag name specified" msgstr "A tag neve nincsen megadva" -#: gitk:9296 #, tcl-format msgid "Tag \"%s\" already exists" msgstr "%s Tag már létezik" -#: gitk:9306 msgid "Error creating tag:" msgstr "Hiba történt a tag létrehozása közben:" -#: gitk:9382 msgid "Command:" msgstr "Parancs:" -#: gitk:9390 msgid "Write" msgstr "Írás" -#: gitk:9408 msgid "Error writing commit:" msgstr "Hiba történt a commit írása közben:" -#: gitk:9435 msgid "Name:" msgstr "Név:" -#: gitk:9458 msgid "Please specify a name for the new branch" msgstr "Kérem adja meg a nevét az új branchhoz" -#: gitk:9463 #, tcl-format msgid "Branch '%s' already exists. Overwrite?" msgstr "%s branch már létezik. Felülírja?" -#: gitk:9530 #, tcl-format msgid "Commit %s is already included in branch %s -- really re-apply it?" msgstr "" "%s commit már benne van a %s branchban -- biztos hogy újra csinálja ?" "eintragen?" -#: gitk:9535 msgid "Cherry-picking" msgstr "Cherry-picking" -#: gitk:9544 #, tcl-format msgid "" "Cherry-pick failed because of local changes to file '%s'.\n" @@ -1054,7 +815,6 @@ msgstr "" "Kérem commitolja, indítsa újra vagy rejtse el a változtatásait és próbálja " "újra." -#: gitk:9550 msgid "" "Cherry-pick failed because of merge conflict.\n" "Do you wish to run git citool to resolve it?" @@ -1062,23 +822,19 @@ msgstr "" "Cherry-pick hiba történt merge konfliktus miatt.\n" "Kívánja futtatni a git citool-t a probléma megoldásához?" -#: gitk:9566 gitk:9624 msgid "No changes committed" msgstr "Nincsen változás commitolva" -#: gitk:9593 #, fuzzy, tcl-format msgid "Commit %s is not included in branch %s -- really revert it?" msgstr "" "%s commit már benne van a %s branchban -- biztos hogy újra csinálja ?" "eintragen?" -#: gitk:9598 #, fuzzy msgid "Reverting" msgstr "Újraindítás" -#: gitk:9606 #, fuzzy, tcl-format msgid "" "Revert failed because of local changes to the following files:%s Please " @@ -1088,7 +844,6 @@ msgstr "" "Kérem commitolja, indítsa újra vagy rejtse el a változtatásait és próbálja " "újra." -#: gitk:9610 #, fuzzy msgid "" "Revert failed because of merge conflict.\n" @@ -1097,28 +852,22 @@ msgstr "" "Cherry-pick hiba történt merge konfliktus miatt.\n" "Kívánja futtatni a git citool-t a probléma megoldásához?" -#: gitk:9653 msgid "Confirm reset" msgstr "Újraindítás megerősítése" -#: gitk:9655 #, tcl-format msgid "Reset branch %s to %s?" msgstr "Újraindítja a %s branchot %s-ig?" -#: gitk:9657 msgid "Reset type:" msgstr "Újraindítás típusa:" -#: gitk:9660 msgid "Soft: Leave working tree and index untouched" msgstr "Soft: Hagyd a working tree-t és az indexet érintetlenül" -#: gitk:9663 msgid "Mixed: Leave working tree untouched, reset index" msgstr "Kevert: Hagyd a working tree-t érintetlenül, töröld az indexet" -#: gitk:9666 msgid "" "Hard: Reset working tree and index\n" "(discard ALL local changes)" @@ -1126,19 +875,15 @@ msgstr "" "Hard: Indítsd újra a working tree-t és az indexet\n" "(MINDEN lokális változás eldobása)" -#: gitk:9683 msgid "Resetting" msgstr "Újraindítás" -#: gitk:9743 msgid "Checking out" msgstr "Kivesz" -#: gitk:9796 msgid "Cannot delete the currently checked-out branch" msgstr "Nem lehet a jelenleg kivett branch-ot törölni" -#: gitk:9802 #, tcl-format msgid "" "The commits on branch %s aren't on any other branch.\n" @@ -1147,16 +892,13 @@ msgstr "" "A %s branchon található commit nem található meg semelyik másik branchon.\n" "Tényleg törli a %s branchot?" -#: gitk:9833 #, tcl-format msgid "Tags and heads: %s" msgstr "Tagek és headek: %s" -#: gitk:9850 msgid "Filter" msgstr "Szűrő" -#: gitk:10146 msgid "" "Error reading commit topology information; branch and preceding/following " "tag information will be incomplete." @@ -1164,204 +906,155 @@ msgstr "" "Hiba történt a commit topológiai információ olvasása közben; branch ésa " "megelőző/következő információ nem lesz teljes." -#: gitk:11123 msgid "Tag" msgstr "Tag" -#: gitk:11127 msgid "Id" msgstr "Id" -#: gitk:11210 msgid "Gitk font chooser" msgstr "Gitk-betű kiválasztó" -#: gitk:11227 msgid "B" msgstr "F" -#: gitk:11230 msgid "I" msgstr "K" -#: gitk:11348 msgid "Commit list display options" msgstr "Commit lista kijelzési opciók" -#: gitk:11351 msgid "Maximum graph width (lines)" msgstr "Maximális grafikon szélesség (sorok)" -#: gitk:11355 #, no-tcl-format msgid "Maximum graph width (% of pane)" msgstr "Maximális grafikon szélesség (táble %-je)" -#: gitk:11358 msgid "Show local changes" msgstr "Mutasd a lokális változtatásokat" -#: gitk:11361 #, fuzzy msgid "Auto-select SHA1 (length)" msgstr "SHA1 Automatikus kiválasztása" -#: gitk:11365 msgid "Hide remote refs" msgstr "A távoli refek elrejtése" -#: gitk:11369 msgid "Diff display options" msgstr "Diff kijelző opciók" -#: gitk:11371 msgid "Tab spacing" msgstr "Tab sorköz" -#: gitk:11374 #, fuzzy msgid "Display nearby tags/heads" msgstr "Szomszédos tagek kijelzése" -#: gitk:11377 msgid "Maximum # tags/heads to show" msgstr "" -#: gitk:11380 msgid "Limit diffs to listed paths" msgstr "Korlátozott diffek a kilistázott útvonalakhoz" -#: gitk:11383 msgid "Support per-file encodings" msgstr "Fájlonkénti kódolás támgatása" -#: gitk:11389 gitk:11536 msgid "External diff tool" msgstr "Külső diff alkalmazás" -#: gitk:11390 msgid "Choose..." msgstr "Válaszd ..." -#: gitk:11395 msgid "General options" msgstr "Általános opciók" -#: gitk:11398 msgid "Use themed widgets" msgstr "Témázott vezérlők használata" -#: gitk:11400 msgid "(change requires restart)" msgstr "(a változás újraindítást igényel)" -#: gitk:11402 msgid "(currently unavailable)" msgstr "(jelenleg nem elérhető)" -#: gitk:11413 msgid "Colors: press to choose" msgstr "Színek: nyomja meg a kiválasztáshoz" -#: gitk:11416 msgid "Interface" msgstr "Interfész" -#: gitk:11417 msgid "interface" msgstr "interfész" -#: gitk:11420 msgid "Background" msgstr "Háttér" -#: gitk:11421 gitk:11451 msgid "background" msgstr "háttér" -#: gitk:11424 msgid "Foreground" msgstr "Előtér" -#: gitk:11425 msgid "foreground" msgstr "előtér" -#: gitk:11428 msgid "Diff: old lines" msgstr "Diff: régi sorok" -#: gitk:11429 msgid "diff old lines" msgstr "diff régi sorok" -#: gitk:11433 msgid "Diff: new lines" msgstr "Diff: új sorok" -#: gitk:11434 msgid "diff new lines" msgstr "diff - új sorok" -#: gitk:11438 msgid "Diff: hunk header" msgstr "Diff: nagy headerök" -#: gitk:11440 msgid "diff hunk header" msgstr "diff - nagy headerök" -#: gitk:11444 msgid "Marked line bg" msgstr "Megjelölt sor háttér" -#: gitk:11446 msgid "marked line background" msgstr "megjelölt sor háttér" -#: gitk:11450 msgid "Select bg" msgstr "Válasszon hátteret" -#: gitk:11459 msgid "Fonts: press to choose" msgstr "Betű: nyomja meg a kiválasztáshoz" -#: gitk:11461 msgid "Main font" msgstr "Fő betű" -#: gitk:11462 msgid "Diff display font" msgstr "Diff kijelző betű" -#: gitk:11463 msgid "User interface font" msgstr "Felhasználói interfész betű" -#: gitk:11485 msgid "Gitk preferences" msgstr "Gitk beállítások" -#: gitk:11494 #, fuzzy msgid "General" msgstr "Generálás" -#: gitk:11495 msgid "Colors" msgstr "" -#: gitk:11496 msgid "Fonts" msgstr "" -#: gitk:11546 #, tcl-format msgid "Gitk: choose color for %s" msgstr "Gitk: válasszon színt a %s-ra" -#: gitk:12059 msgid "" "Sorry, gitk cannot run with this version of Tcl/Tk.\n" " Gitk requires at least Tcl/Tk 8.4." @@ -1369,16 +1062,13 @@ msgstr "" "Sajnáljuk, de a gitk nem futtatható ezzel a Tcl/Tk verzióval.\n" "Gitk futtatásához legalább Tcl/Tk 8.4 szükséges." -#: gitk:12269 msgid "Cannot find a git repository here." msgstr "Nem találhatü git repository itt." -#: gitk:12316 #, tcl-format msgid "Ambiguous argument '%s': both revision and filename" msgstr "Félreérthető argumentum '%s': revízió és fájlnév is" -#: gitk:12328 msgid "Bad arguments to gitk:" msgstr "Rossz gitk argumentumok:" diff --git a/gitk-git/po/it.po b/gitk-git/po/it.po index b58d23eb2b..aa34a34280 100644 --- a/gitk-git/po/it.po +++ b/gitk-git/po/it.po @@ -6,7 +6,7 @@ # msgid "" msgstr "" -"Project-Id-Version: gitk\n" +"Project-Id-Version: Gitk\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-05-17 14:32+1000\n" "PO-Revision-Date: 2010-01-28 18:41+0100\n" @@ -17,33 +17,26 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: gitk:140 msgid "Couldn't get list of unmerged files:" msgstr "Impossibile ottenere l'elenco dei file in attesa di fusione:" -#: gitk:212 gitk:2381 msgid "Color words" msgstr "" -#: gitk:217 gitk:2381 gitk:8220 gitk:8253 msgid "Markup words" msgstr "" -#: gitk:324 msgid "Error parsing revisions:" msgstr "Errore nella lettura delle revisioni:" -#: gitk:380 msgid "Error executing --argscmd command:" msgstr "Errore nell'esecuzione del comando specificato con --argscmd:" -#: gitk:393 msgid "No files selected: --merge specified but no files are unmerged." msgstr "" "Nessun file selezionato: è stata specificata l'opzione --merge ma non ci " "sono file in attesa di fusione." -#: gitk:396 msgid "" "No files selected: --merge specified but no unmerged files are within file " "limit." @@ -51,317 +44,237 @@ msgstr "" "Nessun file selezionato: è stata specificata l'opzione --merge ma i file " "specificati non sono in attesa di fusione." -#: gitk:418 gitk:566 msgid "Error executing git log:" msgstr "Errore nell'esecuzione di git log:" -#: gitk:436 gitk:582 msgid "Reading" msgstr "Lettura in corso" -#: gitk:496 gitk:4525 msgid "Reading commits..." msgstr "Lettura delle revisioni in corso..." -#: gitk:499 gitk:1637 gitk:4528 msgid "No commits selected" msgstr "Nessuna revisione selezionata" -#: gitk:1445 gitk:4045 gitk:12432 msgid "Command line" msgstr "Linea di comando" -#: gitk:1511 msgid "Can't parse git log output:" msgstr "Impossibile elaborare i dati di git log:" -#: gitk:1740 msgid "No commit information available" msgstr "Nessuna informazione disponibile sulle revisioni" -#: gitk:1903 gitk:1932 gitk:4315 gitk:9669 gitk:11241 gitk:11521 msgid "OK" msgstr "OK" -#: gitk:1934 gitk:4317 gitk:9196 gitk:9275 gitk:9391 gitk:9440 gitk:9671 -#: gitk:11242 gitk:11522 msgid "Cancel" msgstr "Annulla" -#: gitk:2069 msgid "&Update" msgstr "Aggiorna" -#: gitk:2070 msgid "&Reload" msgstr "Ricarica" -#: gitk:2071 msgid "Reread re&ferences" msgstr "Rileggi riferimenti" -#: gitk:2072 msgid "&List references" msgstr "Elenca riferimenti" -#: gitk:2074 msgid "Start git &gui" msgstr "Avvia git gui" -#: gitk:2076 msgid "&Quit" msgstr "Esci" -#: gitk:2068 msgid "&File" msgstr "&File" -#: gitk:2080 msgid "&Preferences" msgstr "Preferenze" -#: gitk:2079 msgid "&Edit" msgstr "Modifica" -#: gitk:2084 msgid "&New view..." msgstr "Nuova vista..." -#: gitk:2085 msgid "&Edit view..." msgstr "Modifica vista..." -#: gitk:2086 msgid "&Delete view" msgstr "Elimina vista" -#: gitk:2088 gitk:4043 msgid "&All files" msgstr "Tutti i file" -#: gitk:2083 gitk:4067 msgid "&View" msgstr "Vista" -#: gitk:2093 gitk:2103 gitk:3012 msgid "&About gitk" msgstr "Informazioni su gitk" -#: gitk:2094 gitk:2108 msgid "&Key bindings" msgstr "Scorciatoie da tastiera" -#: gitk:2092 gitk:2107 msgid "&Help" msgstr "Aiuto" -#: gitk:2185 gitk:8652 msgid "SHA1 ID:" msgstr "SHA1 ID:" -#: gitk:2229 msgid "Row" msgstr "Riga" -#: gitk:2267 msgid "Find" msgstr "Trova" -#: gitk:2295 msgid "commit" msgstr "revisione" -#: gitk:2299 gitk:2301 gitk:4687 gitk:4710 gitk:4734 gitk:6755 gitk:6827 -#: gitk:6912 msgid "containing:" msgstr "contenente:" -#: gitk:2302 gitk:3526 gitk:3531 gitk:4763 msgid "touching paths:" msgstr "che riguarda i percorsi:" -#: gitk:2303 gitk:4777 msgid "adding/removing string:" msgstr "che aggiunge/rimuove la stringa:" -#: gitk:2304 gitk:4779 msgid "changing lines matching:" msgstr "" -#: gitk:2313 gitk:2315 gitk:4766 msgid "Exact" msgstr "Esatto" -#: gitk:2315 gitk:4854 gitk:6723 msgid "IgnCase" msgstr "" -#: gitk:2315 gitk:4736 gitk:4852 gitk:6719 msgid "Regexp" msgstr "" -#: gitk:2317 gitk:2318 gitk:4874 gitk:4904 gitk:4911 gitk:6848 gitk:6916 msgid "All fields" msgstr "Tutti i campi" -#: gitk:2318 gitk:4871 gitk:4904 gitk:6786 msgid "Headline" msgstr "Titolo" -#: gitk:2319 gitk:4871 gitk:6786 gitk:6916 gitk:7389 msgid "Comments" msgstr "Commenti" -#: gitk:2319 gitk:4871 gitk:4876 gitk:4911 gitk:6786 gitk:7324 gitk:8830 -#: gitk:8845 msgid "Author" msgstr "Autore" -#: gitk:2319 gitk:4871 gitk:6786 gitk:7326 msgid "Committer" msgstr "Revisione creata da" -#: gitk:2350 msgid "Search" msgstr "Cerca" -#: gitk:2358 msgid "Diff" msgstr "" -#: gitk:2360 msgid "Old version" msgstr "Vecchia versione" -#: gitk:2362 msgid "New version" msgstr "Nuova versione" -#: gitk:2364 msgid "Lines of context" msgstr "Linee di contesto" -#: gitk:2374 msgid "Ignore space change" msgstr "Ignora modifiche agli spazi" -#: gitk:2378 gitk:2380 gitk:7959 gitk:8206 msgid "Line diff" msgstr "" -#: gitk:2445 msgid "Patch" msgstr "Modifiche" -#: gitk:2447 msgid "Tree" msgstr "Directory" -#: gitk:2617 gitk:2637 msgid "Diff this -> selected" msgstr "Diff questo -> selezionato" -#: gitk:2618 gitk:2638 msgid "Diff selected -> this" msgstr "Diff selezionato -> questo" -#: gitk:2619 gitk:2639 msgid "Make patch" msgstr "Crea patch" -#: gitk:2620 gitk:9254 msgid "Create tag" msgstr "Crea etichetta" -#: gitk:2621 gitk:9371 msgid "Write commit to file" msgstr "Scrivi revisione in un file" -#: gitk:2622 gitk:9428 msgid "Create new branch" msgstr "Crea un nuovo ramo" -#: gitk:2623 msgid "Cherry-pick this commit" msgstr "Porta questa revisione in cima al ramo attuale" -#: gitk:2624 msgid "Reset HEAD branch to here" msgstr "Aggiorna il ramo HEAD a questa revisione" -#: gitk:2625 msgid "Mark this commit" msgstr "Segna questa revisione" -#: gitk:2626 msgid "Return to mark" msgstr "Torna alla revisione segnata" -#: gitk:2627 msgid "Find descendant of this and mark" msgstr "Trova il discendente di questa revisione e di quella segnata" -#: gitk:2628 msgid "Compare with marked commit" msgstr "Confronta con la revisione segnata" -#: gitk:2629 gitk:2640 #, fuzzy msgid "Diff this -> marked commit" msgstr "Diff questo -> selezionato" -#: gitk:2630 gitk:2641 #, fuzzy msgid "Diff marked commit -> this" msgstr "Diff selezionato -> questo" -#: gitk:2631 #, fuzzy msgid "Revert this commit" msgstr "Segna questa revisione" -#: gitk:2647 msgid "Check out this branch" msgstr "Attiva questo ramo" -#: gitk:2648 msgid "Remove this branch" msgstr "Elimina questo ramo" -#: gitk:2649 msgid "Copy branch name" msgstr "" -#: gitk:2656 msgid "Highlight this too" msgstr "Evidenzia anche questo" -#: gitk:2657 msgid "Highlight this only" msgstr "Evidenzia solo questo" -#: gitk:2658 msgid "External diff" msgstr "Visualizza differenze in un altro programma" -#: gitk:2659 msgid "Blame parent commit" msgstr "Annota la revisione precedente" -#: gitk:2660 msgid "Copy path" msgstr "" -#: gitk:2667 msgid "Show origin of this line" msgstr "Mostra la provenienza di questa riga" -#: gitk:2668 msgid "Run git gui blame on this line" msgstr "Esegui git gui blame su questa riga" -#: gitk:3014 #, fuzzy msgid "" "\n" @@ -379,321 +292,249 @@ msgstr "" "Utilizzo e redistribuzione permessi sotto i termini della GNU General Public " "License" -#: gitk:3022 gitk:3089 gitk:9857 msgid "Close" msgstr "Chiudi" -#: gitk:3043 msgid "Gitk key bindings" msgstr "Scorciatoie da tastiera di Gitk" -#: gitk:3046 msgid "Gitk key bindings:" msgstr "Scorciatoie da tastiera di Gitk:" -#: gitk:3048 #, tcl-format msgid "<%s-Q>\t\tQuit" msgstr "<%s-Q>\t\tEsci" -#: gitk:3049 #, fuzzy, tcl-format msgid "<%s-W>\t\tClose window" msgstr "<%s-F>\t\tTrova" -#: gitk:3050 msgid "<Home>\t\tMove to first commit" msgstr "<Home>\t\tVai alla prima revisione" -#: gitk:3051 msgid "<End>\t\tMove to last commit" msgstr "<End>\t\tVai all'ultima revisione" -#: gitk:3052 #, fuzzy msgid "<Up>, p, k\tMove up one commit" msgstr "<Su>, p, i\tVai più in alto di una revisione" -#: gitk:3053 #, fuzzy msgid "<Down>, n, j\tMove down one commit" msgstr "<Giù>, n, k\tVai più in basso di una revisione" -#: gitk:3054 #, fuzzy msgid "<Left>, z, h\tGo back in history list" msgstr "<Sinistra>, z, j\tTorna indietro nella cronologia" -#: gitk:3055 msgid "<Right>, x, l\tGo forward in history list" msgstr "<Destra>, x, l\tVai avanti nella cronologia" -#: gitk:3056 #, tcl-format msgid "<%s-n>\tGo to n-th parent of current commit in history list" msgstr "" -#: gitk:3057 msgid "<PageUp>\tMove up one page in commit list" msgstr "<PaginaSu>\tVai più in alto di una pagina nella lista delle revisioni" -#: gitk:3058 msgid "<PageDown>\tMove down one page in commit list" msgstr "" "<PaginaGiù>\tVai più in basso di una pagina nella lista delle revisioni" -#: gitk:3059 #, tcl-format msgid "<%s-Home>\tScroll to top of commit list" msgstr "<%s-Home>\tScorri alla cima della lista delle revisioni" -#: gitk:3060 #, tcl-format msgid "<%s-End>\tScroll to bottom of commit list" msgstr "<%s-End>\tScorri alla fine della lista delle revisioni" -#: gitk:3061 #, tcl-format msgid "<%s-Up>\tScroll commit list up one line" msgstr "<%s-Su>\tScorri la lista delle revisioni in alto di una riga" -#: gitk:3062 #, tcl-format msgid "<%s-Down>\tScroll commit list down one line" msgstr "<%s-Giù>\tScorri la lista delle revisioni in basso di una riga" -#: gitk:3063 #, tcl-format msgid "<%s-PageUp>\tScroll commit list up one page" msgstr "<%s-PaginaSu>\tScorri la lista delle revisioni in alto di una pagina" -#: gitk:3064 #, tcl-format msgid "<%s-PageDown>\tScroll commit list down one page" msgstr "<%s-PaginaGiù>\tScorri la lista delle revisioni in basso di una pagina" -#: gitk:3065 msgid "<Shift-Up>\tFind backwards (upwards, later commits)" msgstr "<Shift-Su>\tTrova all'indietro (verso l'alto, revisioni successive)" -#: gitk:3066 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)" msgstr "<Shift-Giù>\tTrova in avanti (verso il basso, revisioni precedenti)" -#: gitk:3067 msgid "<Delete>, b\tScroll diff view up one page" msgstr "<Delete>, b\tScorri la vista delle differenze in alto di una pagina" -#: gitk:3068 msgid "<Backspace>\tScroll diff view up one page" msgstr "<Backspace>\tScorri la vista delle differenze in alto di una pagina" -#: gitk:3069 msgid "<Space>\t\tScroll diff view down one page" msgstr "<Spazio>\t\tScorri la vista delle differenze in basso di una pagina" -#: gitk:3070 msgid "u\t\tScroll diff view up 18 lines" msgstr "u\t\tScorri la vista delle differenze in alto di 18 linee" -#: gitk:3071 msgid "d\t\tScroll diff view down 18 lines" msgstr "d\t\tScorri la vista delle differenze in basso di 18 linee" -#: gitk:3072 #, tcl-format msgid "<%s-F>\t\tFind" msgstr "<%s-F>\t\tTrova" -#: gitk:3073 #, tcl-format msgid "<%s-G>\t\tMove to next find hit" msgstr "<%s-G>\t\tTrova in avanti" -#: gitk:3074 msgid "<Return>\tMove to next find hit" msgstr "<Invio>\tTrova in avanti" -#: gitk:3075 #, fuzzy msgid "g\t\tGo to commit" msgstr "<End>\t\tVai all'ultima revisione" -#: gitk:3076 msgid "/\t\tFocus the search box" msgstr "/\t\tCursore nel box di ricerca" -#: gitk:3077 msgid "?\t\tMove to previous find hit" msgstr "?\t\tTrova all'indietro" -#: gitk:3078 msgid "f\t\tScroll diff view to next file" msgstr "f\t\tScorri la vista delle differenze al file successivo" -#: gitk:3079 #, tcl-format msgid "<%s-S>\t\tSearch for next hit in diff view" msgstr "<%s-S>\t\tCerca in avanti nella vista delle differenze" -#: gitk:3080 #, tcl-format msgid "<%s-R>\t\tSearch for previous hit in diff view" msgstr "<%s-R>\t\tCerca all'indietro nella vista delle differenze" -#: gitk:3081 #, tcl-format msgid "<%s-KP+>\tIncrease font size" msgstr "<%s-KP+>\tAumenta dimensione carattere" -#: gitk:3082 #, tcl-format msgid "<%s-plus>\tIncrease font size" msgstr "<%s-più>\tAumenta dimensione carattere" -#: gitk:3083 #, tcl-format msgid "<%s-KP->\tDecrease font size" msgstr "<%s-KP->\tDiminuisci dimensione carattere" -#: gitk:3084 #, tcl-format msgid "<%s-minus>\tDecrease font size" msgstr "<%s-meno>\tDiminuisci dimensione carattere" -#: gitk:3085 msgid "<F5>\t\tUpdate" msgstr "<F5>\t\tAggiorna" -#: gitk:3550 gitk:3559 #, tcl-format msgid "Error creating temporary directory %s:" msgstr "Errore durante la creazione della directory temporanea %s:" -#: gitk:3572 #, tcl-format msgid "Error getting \"%s\" from %s:" msgstr "Errore nella lettura di \"%s\" da %s:" -#: gitk:3635 msgid "command failed:" msgstr "impossibile eseguire il comando:" -#: gitk:3784 msgid "No such commit" msgstr "Revisione inesistente" -#: gitk:3798 msgid "git gui blame: command failed:" msgstr "git gui blame: impossibile eseguire il comando:" -#: gitk:3829 #, tcl-format msgid "Couldn't read merge head: %s" msgstr "Impossibile leggere merge head: %s" -#: gitk:3837 #, tcl-format msgid "Error reading index: %s" msgstr "Errore nella lettura dell'indice: %s" -#: gitk:3862 #, tcl-format msgid "Couldn't start git blame: %s" msgstr "Impossibile eseguire git blame: %s" -#: gitk:3865 gitk:6754 msgid "Searching" msgstr "Ricerca in corso" -#: gitk:3897 #, tcl-format msgid "Error running git blame: %s" msgstr "Errore nell'esecuzione di git blame: %s" -#: gitk:3925 #, tcl-format msgid "That line comes from commit %s, which is not in this view" msgstr "Quella riga proviene dalla revisione %s, non presente in questa vista" -#: gitk:3939 msgid "External diff viewer failed:" msgstr "Impossibile eseguire il visualizzatore di differenze:" -#: gitk:4070 msgid "Gitk view definition" msgstr "Scelta vista Gitk" -#: gitk:4074 msgid "Remember this view" msgstr "Ricorda questa vista" -#: gitk:4075 msgid "References (space separated list):" msgstr "Riferimenti (lista di elementi separati da spazi)" -#: gitk:4076 msgid "Branches & tags:" msgstr "Rami ed etichette" -#: gitk:4077 msgid "All refs" msgstr "Tutti i riferimenti" -#: gitk:4078 msgid "All (local) branches" msgstr "Tutti i rami (locali)" -#: gitk:4079 msgid "All tags" msgstr "Tutte le etichette" -#: gitk:4080 msgid "All remote-tracking branches" msgstr "Tutti i rami remoti" -#: gitk:4081 msgid "Commit Info (regular expressions):" msgstr "Informazioni sulla revisione (espressioni regolari):" -#: gitk:4082 msgid "Author:" msgstr "Autore:" -#: gitk:4083 msgid "Committer:" msgstr "Revisione creata da:" -#: gitk:4084 msgid "Commit Message:" msgstr "Messaggio di revisione:" -#: gitk:4085 msgid "Matches all Commit Info criteria" msgstr "Risponde a tutti i criteri di ricerca sulle revisioni" -#: gitk:4086 #, fuzzy msgid "Matches no Commit Info criteria" msgstr "Risponde a tutti i criteri di ricerca sulle revisioni" -#: gitk:4087 msgid "Changes to Files:" msgstr "Modifiche ai file:" -#: gitk:4088 msgid "Fixed String" msgstr "Stringa fissa" -#: gitk:4089 msgid "Regular Expression" msgstr "Espressione regolare" -#: gitk:4090 msgid "Search string:" msgstr "Cerca stringa:" -#: gitk:4091 msgid "" "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" @@ -701,201 +542,153 @@ msgstr "" "Date di revisione (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, " "2009 15:27:38\"):" -#: gitk:4092 msgid "Since:" msgstr "Da:" -#: gitk:4093 msgid "Until:" msgstr "A:" -#: gitk:4094 msgid "Limit and/or skip a number of revisions (positive integer):" msgstr "Limita e/o salta N revisioni (intero positivo):" -#: gitk:4095 msgid "Number to show:" msgstr "Numero di revisioni da mostrare:" -#: gitk:4096 msgid "Number to skip:" msgstr "Numero di revisioni da saltare:" -#: gitk:4097 msgid "Miscellaneous options:" msgstr "Altre opzioni:" -#: gitk:4098 msgid "Strictly sort by date" msgstr "Ordina solo per data" -#: gitk:4099 msgid "Mark branch sides" msgstr "Segna i lati del ramo" -#: gitk:4100 msgid "Limit to first parent" msgstr "Limita al primo genitore" -#: gitk:4101 msgid "Simple history" msgstr "Cronologia semplificata" -#: gitk:4102 msgid "Additional arguments to git log:" msgstr "Ulteriori argomenti da passare a git log:" -#: gitk:4103 msgid "Enter files and directories to include, one per line:" msgstr "Inserire file e directory da includere, uno per riga:" -#: gitk:4104 msgid "Command to generate more commits to include:" msgstr "Comando che genera altre revisioni da visualizzare:" -#: gitk:4228 msgid "Gitk: edit view" msgstr "Gitk: modifica vista" -#: gitk:4236 msgid "-- criteria for selecting revisions" msgstr "-- criteri per la scelta delle revisioni" -#: gitk:4241 msgid "View Name" msgstr "Nome vista" -#: gitk:4316 msgid "Apply (F5)" msgstr "Applica (F5)" -#: gitk:4354 msgid "Error in commit selection arguments:" msgstr "Errore negli argomenti di selezione delle revisioni:" -#: gitk:4409 gitk:4462 gitk:4924 gitk:4938 gitk:6208 gitk:12373 gitk:12374 msgid "None" msgstr "Nessuno" -#: gitk:5021 gitk:5026 msgid "Descendant" msgstr "Discendente" -#: gitk:5022 msgid "Not descendant" msgstr "Non discendente" -#: gitk:5029 gitk:5034 msgid "Ancestor" msgstr "Ascendente" -#: gitk:5030 msgid "Not ancestor" msgstr "Non ascendente" -#: gitk:5324 msgid "Local changes checked in to index but not committed" msgstr "Modifiche locali presenti nell'indice ma non nell'archivio" -#: gitk:5360 msgid "Local uncommitted changes, not checked in to index" msgstr "Modifiche locali non presenti né nell'archivio né nell'indice" -#: gitk:7134 msgid "and many more" msgstr "" -#: gitk:7137 msgid "many" msgstr "molti" -#: gitk:7328 msgid "Tags:" msgstr "Etichette:" -#: gitk:7345 gitk:7351 gitk:8825 msgid "Parent" msgstr "Genitore" -#: gitk:7356 msgid "Child" msgstr "Figlio" -#: gitk:7365 msgid "Branch" msgstr "Ramo" -#: gitk:7368 msgid "Follows" msgstr "Segue" -#: gitk:7371 msgid "Precedes" msgstr "Precede" -#: gitk:7966 #, tcl-format msgid "Error getting diffs: %s" msgstr "Errore nella lettura delle differenze:" -#: gitk:8650 msgid "Goto:" msgstr "Vai a:" -#: gitk:8671 #, tcl-format msgid "Short SHA1 id %s is ambiguous" msgstr "La SHA1 id abbreviata %s è ambigua" -#: gitk:8678 #, tcl-format msgid "Revision %s is not known" msgstr "La revisione %s è sconosciuta" -#: gitk:8688 #, tcl-format msgid "SHA1 id %s is not known" msgstr "La SHA1 id %s è sconosciuta" -#: gitk:8690 #, tcl-format msgid "Revision %s is not in the current view" msgstr "La revisione %s non è presente nella vista attuale" -#: gitk:8832 gitk:8847 msgid "Date" msgstr "Data" -#: gitk:8835 msgid "Children" msgstr "Figli" -#: gitk:8898 #, tcl-format msgid "Reset %s branch to here" msgstr "Aggiorna il ramo %s a questa revisione" -#: gitk:8900 msgid "Detached head: can't reset" msgstr "Nessun ramo attivo: reset impossibile" -#: gitk:9005 gitk:9011 msgid "Skipping merge commit " msgstr "Salto la revisione di fusione " -#: gitk:9020 gitk:9025 msgid "Error getting patch ID for " msgstr "Errore nella identificazione della patch per " -#: gitk:9021 gitk:9026 msgid " - stopping\n" msgstr " - fine\n" -#: gitk:9031 gitk:9034 gitk:9042 gitk:9056 gitk:9065 msgid "Commit " msgstr "La revisione " -#: gitk:9035 msgid "" " is the same patch as\n" " " @@ -903,7 +696,6 @@ msgstr "" " ha le stesse differenze di\n" " " -#: gitk:9043 msgid "" " differs from\n" " " @@ -911,7 +703,6 @@ msgstr "" " è diversa da\n" " " -#: gitk:9045 msgid "" "Diff of commits:\n" "\n" @@ -919,129 +710,99 @@ msgstr "" "Differenze tra le revisioni:\n" "\n" -#: gitk:9057 gitk:9066 #, tcl-format msgid " has %s children - stopping\n" msgstr " ha %s figli - fine\n" -#: gitk:9085 #, tcl-format msgid "Error writing commit to file: %s" msgstr "Errore nella scrittura della revisione nel file: %s" -#: gitk:9091 #, tcl-format msgid "Error diffing commits: %s" msgstr "Errore nelle differenze tra le revisioni: %s" -#: gitk:9137 msgid "Top" msgstr "Inizio" -#: gitk:9138 msgid "From" msgstr "Da" -#: gitk:9143 msgid "To" msgstr "A" -#: gitk:9167 msgid "Generate patch" msgstr "Genera patch" -#: gitk:9169 msgid "From:" msgstr "Da:" -#: gitk:9178 msgid "To:" msgstr "A:" -#: gitk:9187 msgid "Reverse" msgstr "Inverti" -#: gitk:9189 gitk:9385 msgid "Output file:" msgstr "Scrivi sul file:" -#: gitk:9195 msgid "Generate" msgstr "Genera" -#: gitk:9233 msgid "Error creating patch:" msgstr "Errore nella creazione della patch:" -#: gitk:9256 gitk:9373 gitk:9430 msgid "ID:" msgstr "ID:" -#: gitk:9265 msgid "Tag name:" msgstr "Nome etichetta:" -#: gitk:9268 msgid "Tag message is optional" msgstr "Il messaggio dell'etichetta è opzionale" -#: gitk:9270 msgid "Tag message:" msgstr "Messaggio dell'etichetta:" -#: gitk:9274 gitk:9439 msgid "Create" msgstr "Crea" -#: gitk:9292 msgid "No tag name specified" msgstr "Nessuna etichetta specificata" -#: gitk:9296 #, tcl-format msgid "Tag \"%s\" already exists" msgstr "L'etichetta \"%s\" esiste già" -#: gitk:9306 msgid "Error creating tag:" msgstr "Errore nella creazione dell'etichetta:" -#: gitk:9382 msgid "Command:" msgstr "Comando:" -#: gitk:9390 msgid "Write" msgstr "Scrivi" -#: gitk:9408 msgid "Error writing commit:" msgstr "Errore nella scrittura della revisione:" -#: gitk:9435 msgid "Name:" msgstr "Nome:" -#: gitk:9458 msgid "Please specify a name for the new branch" msgstr "Specificare un nome per il nuovo ramo" -#: gitk:9463 #, tcl-format msgid "Branch '%s' already exists. Overwrite?" msgstr "Il ramo '%s' esiste già. Sovrascrivere?" -#: gitk:9530 #, tcl-format msgid "Commit %s is already included in branch %s -- really re-apply it?" msgstr "La revisione %s è già inclusa nel ramo %s -- applicarla di nuovo?" -#: gitk:9535 msgid "Cherry-picking" msgstr "" -#: gitk:9544 #, tcl-format msgid "" "Cherry-pick failed because of local changes to file '%s'.\n" @@ -1052,7 +813,6 @@ msgstr "" "Prima di riprovare, bisogna creare una nuova revisione, annullare le " "modifiche o usare 'git stash'." -#: gitk:9550 msgid "" "Cherry-pick failed because of merge conflict.\n" "Do you wish to run git citool to resolve it?" @@ -1060,21 +820,17 @@ msgstr "" "Impossibile eseguire cherry-pick a causa di un conflitto nella fusione.\n" "Vuoi avviare git citool per risolverlo?" -#: gitk:9566 gitk:9624 msgid "No changes committed" msgstr "Nessuna modifica archiviata" -#: gitk:9593 #, fuzzy, tcl-format msgid "Commit %s is not included in branch %s -- really revert it?" msgstr "La revisione %s è già inclusa nel ramo %s -- applicarla di nuovo?" -#: gitk:9598 #, fuzzy msgid "Reverting" msgstr "git reset in corso" -#: gitk:9606 #, fuzzy, tcl-format msgid "" "Revert failed because of local changes to the following files:%s Please " @@ -1085,7 +841,6 @@ msgstr "" "Prima di riprovare, bisogna creare una nuova revisione, annullare le " "modifiche o usare 'git stash'." -#: gitk:9610 #, fuzzy msgid "" "Revert failed because of merge conflict.\n" @@ -1094,28 +849,22 @@ msgstr "" "Impossibile eseguire cherry-pick a causa di un conflitto nella fusione.\n" "Vuoi avviare git citool per risolverlo?" -#: gitk:9653 msgid "Confirm reset" msgstr "Conferma git reset" -#: gitk:9655 #, tcl-format msgid "Reset branch %s to %s?" msgstr "Aggiornare il ramo %s a %s?" -#: gitk:9657 msgid "Reset type:" msgstr "Tipo di aggiornamento:" -#: gitk:9660 msgid "Soft: Leave working tree and index untouched" msgstr "Soft: Lascia la direcory di lavoro e l'indice come sono" -#: gitk:9663 msgid "Mixed: Leave working tree untouched, reset index" msgstr "Mixed: Lascia la directory di lavoro come è, aggiorna l'indice" -#: gitk:9666 msgid "" "Hard: Reset working tree and index\n" "(discard ALL local changes)" @@ -1123,19 +872,15 @@ msgstr "" "Hard: Aggiorna la directory di lavoro e l'indice\n" "(abbandona TUTTE le modifiche locali)" -#: gitk:9683 msgid "Resetting" msgstr "git reset in corso" -#: gitk:9743 msgid "Checking out" msgstr "Attivazione in corso" -#: gitk:9796 msgid "Cannot delete the currently checked-out branch" msgstr "Impossibile cancellare il ramo attualmente attivo" -#: gitk:9802 #, tcl-format msgid "" "The commits on branch %s aren't on any other branch.\n" @@ -1144,16 +889,13 @@ msgstr "" "Le revisioni nel ramo %s non sono presenti su altri rami.\n" "Cancellare il ramo %s?" -#: gitk:9833 #, tcl-format msgid "Tags and heads: %s" msgstr "Etichette e rami: %s" -#: gitk:9850 msgid "Filter" msgstr "Filtro" -#: gitk:10146 msgid "" "Error reading commit topology information; branch and preceding/following " "tag information will be incomplete." @@ -1161,219 +903,167 @@ msgstr "" "Errore nella lettura della topologia delle revisioni: le informazioni sul " "ramo e le etichette precedenti e seguenti saranno incomplete." -#: gitk:11123 msgid "Tag" msgstr "Etichetta" -#: gitk:11127 msgid "Id" msgstr "Id" -#: gitk:11210 msgid "Gitk font chooser" msgstr "Scelta caratteri gitk" -#: gitk:11227 msgid "B" msgstr "B" -#: gitk:11230 msgid "I" msgstr "I" -#: gitk:11348 msgid "Commit list display options" msgstr "Opzioni visualizzazione dell'elenco revisioni" -#: gitk:11351 msgid "Maximum graph width (lines)" msgstr "Larghezza massima del grafico (in linee)" -#: gitk:11355 #, no-tcl-format msgid "Maximum graph width (% of pane)" msgstr "Larghezza massima del grafico (% del pannello)" -#: gitk:11358 msgid "Show local changes" msgstr "Mostra modifiche locali" -#: gitk:11361 #, fuzzy msgid "Auto-select SHA1 (length)" msgstr "Seleziona automaticamente SHA1 hash" -#: gitk:11365 msgid "Hide remote refs" msgstr "Nascondi i riferimenti remoti" -#: gitk:11369 msgid "Diff display options" msgstr "Opzioni di visualizzazione delle differenze" -#: gitk:11371 msgid "Tab spacing" msgstr "Spaziatura tabulazioni" -#: gitk:11374 #, fuzzy msgid "Display nearby tags/heads" msgstr "Mostra etichette vicine" -#: gitk:11377 msgid "Maximum # tags/heads to show" msgstr "" -#: gitk:11380 msgid "Limit diffs to listed paths" msgstr "Limita le differenze ai percorsi elencati" -#: gitk:11383 msgid "Support per-file encodings" msgstr "Attiva codifica file per file" -#: gitk:11389 gitk:11536 msgid "External diff tool" msgstr "Visualizzatore di differenze" -#: gitk:11390 msgid "Choose..." msgstr "Scegli..." -#: gitk:11395 msgid "General options" msgstr "Opzioni generali" -#: gitk:11398 msgid "Use themed widgets" msgstr "Utilizza interfaccia a tema" -#: gitk:11400 msgid "(change requires restart)" msgstr "(una modifica richiede il riavvio)" -#: gitk:11402 msgid "(currently unavailable)" msgstr "(momentaneamente non disponibile)" -#: gitk:11413 msgid "Colors: press to choose" msgstr "Colori: premere per scegliere" -#: gitk:11416 msgid "Interface" msgstr "Interfaccia" -#: gitk:11417 msgid "interface" msgstr "interfaccia" -#: gitk:11420 msgid "Background" msgstr "Sfondo" -#: gitk:11421 gitk:11451 msgid "background" msgstr "sfondo" -#: gitk:11424 msgid "Foreground" msgstr "Primo piano" -#: gitk:11425 msgid "foreground" msgstr "primo piano" -#: gitk:11428 msgid "Diff: old lines" msgstr "Diff: vecchie linee" -#: gitk:11429 msgid "diff old lines" msgstr "vecchie linee" -#: gitk:11433 msgid "Diff: new lines" msgstr "Diff: nuove linee" -#: gitk:11434 msgid "diff new lines" msgstr "nuove linee" -#: gitk:11438 msgid "Diff: hunk header" msgstr "Diff: intestazione della sezione" -#: gitk:11440 msgid "diff hunk header" msgstr "intestazione della sezione" -#: gitk:11444 msgid "Marked line bg" msgstr "Sfondo riga selezionata" -#: gitk:11446 msgid "marked line background" msgstr "sfondo riga selezionata" -#: gitk:11450 msgid "Select bg" msgstr "Sfondo" -#: gitk:11459 msgid "Fonts: press to choose" msgstr "Carattere: premere per scegliere" -#: gitk:11461 msgid "Main font" msgstr "Carattere principale" -#: gitk:11462 msgid "Diff display font" msgstr "Carattere per differenze" -#: gitk:11463 msgid "User interface font" msgstr "Carattere per interfaccia utente" -#: gitk:11485 msgid "Gitk preferences" msgstr "Preferenze gitk" -#: gitk:11494 #, fuzzy msgid "General" msgstr "Genera" -#: gitk:11495 msgid "Colors" msgstr "" -#: gitk:11496 msgid "Fonts" msgstr "" -#: gitk:11546 #, tcl-format msgid "Gitk: choose color for %s" msgstr "Gitk: scegliere un colore per %s" -#: gitk:12059 msgid "" "Sorry, gitk cannot run with this version of Tcl/Tk.\n" " Gitk requires at least Tcl/Tk 8.4." msgstr "" -#: gitk:12269 msgid "Cannot find a git repository here." msgstr "Archivio git non trovato." -#: gitk:12316 #, tcl-format msgid "Ambiguous argument '%s': both revision and filename" msgstr "Argomento ambiguo: '%s' è sia revisione che nome di file" -#: gitk:12328 msgid "Bad arguments to gitk:" msgstr "Gitk: argomenti errati:" diff --git a/gitk-git/po/ja.po b/gitk-git/po/ja.po index ca3c29b457..2c40b76dc0 100644 --- a/gitk-git/po/ja.po +++ b/gitk-git/po/ja.po @@ -8,7 +8,7 @@ # Satoshi Yasushima <s.yasushima@gmail.com>, 2016. msgid "" msgstr "" -"Project-Id-Version: gitk\n" +"Project-Id-Version: Gitk\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-05-17 14:32+1000\n" "PO-Revision-Date: 2015-11-12 13:00+0900\n" @@ -20,33 +20,26 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -#: gitk:140 msgid "Couldn't get list of unmerged files:" msgstr "マージされていないファイルのリストを取得できません:" -#: gitk:212 gitk:2381 msgid "Color words" msgstr "変更を着色" -#: gitk:217 gitk:2381 gitk:8221 gitk:8254 msgid "Markup words" msgstr "変更をマークアップ" -#: gitk:324 msgid "Error parsing revisions:" msgstr "リビジョン解析エラー:" -#: gitk:380 msgid "Error executing --argscmd command:" msgstr "--argscmd コマンド実行エラー:" -#: gitk:393 msgid "No files selected: --merge specified but no files are unmerged." msgstr "" "ファイル未選択: --merge が指定されましたが、マージされていないファイルはあり" "ません。" -#: gitk:396 msgid "" "No files selected: --merge specified but no unmerged files are within file " "limit." @@ -54,322 +47,240 @@ msgstr "" "ファイル未選択: --merge が指定されましたが、ファイル制限内にマージされていな" "いファイルはありません。" -#: gitk:418 gitk:566 msgid "Error executing git log:" msgstr "git log 実行エラー:" -#: gitk:436 gitk:582 msgid "Reading" msgstr "読み込み中" -#: gitk:496 gitk:4526 msgid "Reading commits..." msgstr "コミット読み込み中..." -#: gitk:499 gitk:1637 gitk:4529 msgid "No commits selected" msgstr "コミットが選択されていません" -#: gitk:1445 gitk:4046 gitk:12447 msgid "Command line" msgstr "コマンド行" -#: gitk:1511 msgid "Can't parse git log output:" msgstr "git log の出力を解析できません:" -#: gitk:1740 msgid "No commit information available" msgstr "有効なコミットの情報がありません" -#: gitk:1903 gitk:1932 gitk:4316 gitk:9684 gitk:11256 gitk:11536 msgid "OK" msgstr "OK" -#: gitk:1934 gitk:4318 gitk:9197 gitk:9276 gitk:9406 gitk:9455 gitk:9686 -#: gitk:11257 gitk:11537 msgid "Cancel" msgstr "キャンセル" -#: gitk:2069 msgid "&Update" msgstr "更新(&U)" -#: gitk:2070 msgid "&Reload" msgstr "リロード(&R)" -#: gitk:2071 msgid "Reread re&ferences" msgstr "リファレンスを再読み込み(&F)" -#: gitk:2072 msgid "&List references" msgstr "リファレンスリストを表示(&L)" -#: gitk:2074 msgid "Start git &gui" msgstr "git gui の開始(&G)" -#: gitk:2076 msgid "&Quit" msgstr "終了(&Q)" -#: gitk:2068 msgid "&File" msgstr "ファイル(&F)" -#: gitk:2080 msgid "&Preferences" msgstr "設定(&P)" -#: gitk:2079 msgid "&Edit" msgstr "編集(&E)" -#: gitk:2084 msgid "&New view..." msgstr "新規ビュー(&N)..." -#: gitk:2085 msgid "&Edit view..." msgstr "ビュー編集(&E)..." -#: gitk:2086 msgid "&Delete view" msgstr "ビュー削除(&D)" -#: gitk:2088 msgid "&All files" msgstr "全てのファイル(&A)" -#: gitk:2083 msgid "&View" msgstr "ビュー(&V)" -#: gitk:2093 gitk:2103 msgid "&About gitk" msgstr "gitk について(&A)" -#: gitk:2094 gitk:2108 msgid "&Key bindings" msgstr "キーバインディング(&K)" -#: gitk:2092 gitk:2107 msgid "&Help" msgstr "ヘルプ(&H)" -#: gitk:2185 gitk:8653 msgid "SHA1 ID:" msgstr "SHA1 ID:" -#: gitk:2229 msgid "Row" msgstr "行" -#: gitk:2267 msgid "Find" msgstr "検索" -#: gitk:2295 msgid "commit" msgstr "コミット" -#: gitk:2299 gitk:2301 gitk:4688 gitk:4711 gitk:4735 gitk:6756 gitk:6828 -#: gitk:6913 msgid "containing:" msgstr "含む:" -#: gitk:2302 gitk:3527 gitk:3532 gitk:4764 msgid "touching paths:" msgstr "パスの一部:" -#: gitk:2303 gitk:4778 msgid "adding/removing string:" msgstr "追加/除去される文字列:" -#: gitk:2304 gitk:4780 msgid "changing lines matching:" msgstr "変更される文字列" -#: gitk:2313 gitk:2315 gitk:4767 msgid "Exact" msgstr "英字の大小を区別する" -#: gitk:2315 gitk:4855 gitk:6724 msgid "IgnCase" msgstr "英字の大小を区別しない" -#: gitk:2315 gitk:4737 gitk:4853 gitk:6720 msgid "Regexp" msgstr "正規表現" -#: gitk:2317 gitk:2318 gitk:4875 gitk:4905 gitk:4912 gitk:6849 gitk:6917 msgid "All fields" msgstr "全ての項目" -#: gitk:2318 gitk:4872 gitk:4905 gitk:6787 msgid "Headline" msgstr "ヘッドライン" -#: gitk:2319 gitk:4872 gitk:6787 gitk:6917 gitk:7390 msgid "Comments" msgstr "コメント" -#: gitk:2319 gitk:4872 gitk:4877 gitk:4912 gitk:6787 gitk:7325 gitk:8831 -#: gitk:8846 msgid "Author" msgstr "作者" -#: gitk:2319 gitk:4872 gitk:6787 gitk:7327 msgid "Committer" msgstr "コミット者" -#: gitk:2350 msgid "Search" msgstr "検索" -#: gitk:2358 msgid "Diff" msgstr "Diff" -#: gitk:2360 msgid "Old version" msgstr "旧バージョン" -#: gitk:2362 msgid "New version" msgstr "新バージョン" -#: gitk:2364 msgid "Lines of context" msgstr "文脈行数" -#: gitk:2374 msgid "Ignore space change" msgstr "空白の違いを無視" -#: gitk:2378 gitk:2380 gitk:7960 gitk:8207 msgid "Line diff" msgstr "行毎のdiff" -#: gitk:2445 msgid "Patch" msgstr "パッチ" -#: gitk:2447 msgid "Tree" msgstr "ツリー" -#: gitk:2617 gitk:2638 msgid "Diff this -> selected" msgstr "これと選択したコミットのdiffを見る" -#: gitk:2618 gitk:2639 msgid "Diff selected -> this" msgstr "選択したコミットとこれのdiffを見る" -#: gitk:2619 gitk:2640 msgid "Make patch" msgstr "パッチ作成" -#: gitk:2620 gitk:9255 msgid "Create tag" msgstr "タグ生成" -#: gitk:2621 msgid "Copy commit summary" msgstr "コミットの要約をコピーする" -#: gitk:2622 gitk:9386 msgid "Write commit to file" msgstr "コミットをファイルに書き出す" -#: gitk:2623 gitk:9443 msgid "Create new branch" msgstr "新規ブランチ生成" -#: gitk:2624 msgid "Cherry-pick this commit" msgstr "このコミットをチェリーピックする" -#: gitk:2625 msgid "Reset HEAD branch to here" msgstr "ブランチのHEADをここにリセットする" -#: gitk:2626 msgid "Mark this commit" msgstr "このコミットにマークをつける" -#: gitk:2627 msgid "Return to mark" msgstr "マークを付けた所に戻る" -#: gitk:2628 msgid "Find descendant of this and mark" msgstr "これとマークをつけた所との子孫を見つける" -#: gitk:2629 msgid "Compare with marked commit" msgstr "マークを付けたコミットと比較する" -#: gitk:2630 gitk:2641 msgid "Diff this -> marked commit" msgstr "これとマークを付けたコミットのdiffを見る" -#: gitk:2631 gitk:2642 msgid "Diff marked commit -> this" msgstr "マークを付けたコミットとこれのdiffを見る" -#: gitk:2632 msgid "Revert this commit" msgstr "このコミットを撤回する" -#: gitk:2648 msgid "Check out this branch" msgstr "このブランチをチェックアウトする" -#: gitk:2649 msgid "Remove this branch" msgstr "このブランチを除去する" -#: gitk:2650 msgid "Copy branch name" msgstr "ブランチ名をコピーする" -#: gitk:2657 msgid "Highlight this too" msgstr "これもハイライトさせる" -#: gitk:2658 msgid "Highlight this only" msgstr "これだけをハイライトさせる" -#: gitk:2659 msgid "External diff" msgstr "外部diffツール" -#: gitk:2660 msgid "Blame parent commit" msgstr "親コミットから blame をかける" -#: gitk:2661 msgid "Copy path" msgstr "パス名をコピーする" -#: gitk:2668 msgid "Show origin of this line" msgstr "この行の出自を表示する" -#: gitk:2669 msgid "Run git gui blame on this line" msgstr "この行に git gui で blame をかける" -#: gitk:3013 msgid "About gitk" msgstr "gitk について" -#: gitk:3015 msgid "" "\n" "Gitk - a commit viewer for git\n" @@ -385,323 +296,251 @@ msgstr "" "\n" "使用および再配布は GNU General Public License に従ってください" -#: gitk:3023 gitk:3090 gitk:9872 msgid "Close" msgstr "閉じる" -#: gitk:3044 msgid "Gitk key bindings" msgstr "Gitk キーバインディング" -#: gitk:3047 msgid "Gitk key bindings:" msgstr "Gitk キーバインディング:" -#: gitk:3049 #, tcl-format msgid "<%s-Q>\t\tQuit" msgstr "<%s-Q>\t\t終了" -#: gitk:3050 #, tcl-format msgid "<%s-W>\t\tClose window" msgstr "<%s-W>\t\tウィンドウを閉じる" -#: gitk:3051 msgid "<Home>\t\tMove to first commit" msgstr "<Home>\t\t最初のコミットに移動" -#: gitk:3052 msgid "<End>\t\tMove to last commit" msgstr "<End>\t\t最後のコミットに移動" -#: gitk:3053 msgid "<Up>, p, k\tMove up one commit" msgstr "<Up>, p, k\t一つ上のコミットに移動" -#: gitk:3054 msgid "<Down>, n, j\tMove down one commit" msgstr "<Down>, n, j\t一つ下のコミットに移動" -#: gitk:3055 msgid "<Left>, z, h\tGo back in history list" msgstr "<Left>, z, h\t履歴の前に戻る" -#: gitk:3056 msgid "<Right>, x, l\tGo forward in history list" msgstr "<Right>, x, l\t履歴の次へ進む" -#: gitk:3057 #, tcl-format msgid "<%s-n>\tGo to n-th parent of current commit in history list" -msgstr "<%s-n(数字)>\t履歴上で現在のコミットの親コミットの内のn(数字)番目のコミットへ移動" +msgstr "" +"<%s-n(数字)>\t履歴上で現在のコミットの親コミットの内のn(数字)番目のコミットへ" +"移動" -#: gitk:3058 msgid "<PageUp>\tMove up one page in commit list" msgstr "<PageUp>\tコミットリストの一つ上のページに移動" -#: gitk:3059 msgid "<PageDown>\tMove down one page in commit list" msgstr "<PageDown>\tコミットリストの一つ下のページに移動" -#: gitk:3060 #, tcl-format msgid "<%s-Home>\tScroll to top of commit list" msgstr "<%s-Home>\tコミットリストの一番上にスクロールする" -#: gitk:3061 #, tcl-format msgid "<%s-End>\tScroll to bottom of commit list" msgstr "<%s-End>\tコミットリストの一番下にスクロールする" -#: gitk:3062 #, tcl-format msgid "<%s-Up>\tScroll commit list up one line" msgstr "<%s-Up>\tコミットリストの一つ下の行にスクロールする" -#: gitk:3063 #, tcl-format msgid "<%s-Down>\tScroll commit list down one line" msgstr "<%s-Down>\tコミットリストの一つ下の行にスクロールする" -#: gitk:3064 #, tcl-format msgid "<%s-PageUp>\tScroll commit list up one page" msgstr "<%s-PageUp>\tコミットリストの上のページにスクロールする" -#: gitk:3065 #, tcl-format msgid "<%s-PageDown>\tScroll commit list down one page" msgstr "<%s-PageDown>\tコミットリストの下のページにスクロールする" -#: gitk:3066 msgid "<Shift-Up>\tFind backwards (upwards, later commits)" msgstr "<Shift-Up>\t後方を検索 (上方の・新しいコミット)" -#: gitk:3067 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)" msgstr "<Shift-Down>\t前方を検索(下方の・古いコミット)" -#: gitk:3068 msgid "<Delete>, b\tScroll diff view up one page" msgstr "<Delete>, b\tdiff画面を上のページにスクロールする" -#: gitk:3069 msgid "<Backspace>\tScroll diff view up one page" msgstr "<Backspace>\tdiff画面を上のページにスクロールする" -#: gitk:3070 msgid "<Space>\t\tScroll diff view down one page" msgstr "<Space>\t\tdiff画面を下のページにスクロールする" -#: gitk:3071 msgid "u\t\tScroll diff view up 18 lines" msgstr "u\t\tdiff画面を上に18行スクロールする" -#: gitk:3072 msgid "d\t\tScroll diff view down 18 lines" msgstr "d\t\tdiff画面を下に18行スクロールする" -#: gitk:3073 #, tcl-format msgid "<%s-F>\t\tFind" msgstr "<%s-F>\t\t検索" -#: gitk:3074 #, tcl-format msgid "<%s-G>\t\tMove to next find hit" msgstr "<%s-G>\t\t次を検索して移動" -#: gitk:3075 msgid "<Return>\tMove to next find hit" msgstr "<Return>\t次を検索して移動" -#: gitk:3076 msgid "g\t\tGo to commit" msgstr "g\t\t指定してコミットに移動" -#: gitk:3077 msgid "/\t\tFocus the search box" msgstr "/\t\t検索ボックスにフォーカス" -#: gitk:3078 msgid "?\t\tMove to previous find hit" msgstr "?\t\t前を検索して移動" -#: gitk:3079 msgid "f\t\tScroll diff view to next file" msgstr "f\t\t次のファイルにdiff画面をスクロールする" -#: gitk:3080 #, tcl-format msgid "<%s-S>\t\tSearch for next hit in diff view" msgstr "<%s-S>\t\tdiff画面の次を検索" -#: gitk:3081 #, tcl-format msgid "<%s-R>\t\tSearch for previous hit in diff view" msgstr "<%s-R>\t\tdiff画面の前を検索" -#: gitk:3082 #, tcl-format msgid "<%s-KP+>\tIncrease font size" msgstr "<%s-KP+>\t文字サイズを拡大" -#: gitk:3083 #, tcl-format msgid "<%s-plus>\tIncrease font size" msgstr "<%s-plus>\t文字サイズを拡大" -#: gitk:3084 #, tcl-format msgid "<%s-KP->\tDecrease font size" msgstr "<%s-KP->\t文字サイズを縮小" -#: gitk:3085 #, tcl-format msgid "<%s-minus>\tDecrease font size" msgstr "<%s-minus>\t文字サイズを縮小" -#: gitk:3086 msgid "<F5>\t\tUpdate" msgstr "<F5>\t\t更新" -#: gitk:3551 gitk:3560 #, tcl-format msgid "Error creating temporary directory %s:" msgstr "一時ディレクトリ %s 生成時エラー:" -#: gitk:3573 #, tcl-format msgid "Error getting \"%s\" from %s:" msgstr "\"%s\" のエラーが %s に発生:" -#: gitk:3636 msgid "command failed:" msgstr "コマンド失敗:" -#: gitk:3785 msgid "No such commit" msgstr "そのようなコミットはありません" -#: gitk:3799 msgid "git gui blame: command failed:" msgstr "git gui blame: コマンド失敗:" -#: gitk:3830 #, tcl-format msgid "Couldn't read merge head: %s" msgstr "マージする HEAD を読み込めません: %s" -#: gitk:3838 #, tcl-format msgid "Error reading index: %s" msgstr "インデックス読み込みエラー: %s" -#: gitk:3863 #, tcl-format msgid "Couldn't start git blame: %s" msgstr "git blame を始められません: %s" -#: gitk:3866 gitk:6755 msgid "Searching" msgstr "検索中" -#: gitk:3898 #, tcl-format msgid "Error running git blame: %s" msgstr "git blame 実行エラー: %s" -#: gitk:3926 #, tcl-format msgid "That line comes from commit %s, which is not in this view" msgstr "コミット %s に由来するその行は、このビューに表示されていません" -#: gitk:3940 msgid "External diff viewer failed:" msgstr "外部diffビューアが失敗:" -#: gitk:4044 msgid "All files" msgstr "全てのファイル" -#: gitk:4068 msgid "View" msgstr "ビュー" -#: gitk:4071 msgid "Gitk view definition" msgstr "Gitk ビュー定義" -#: gitk:4075 msgid "Remember this view" msgstr "このビューを記憶する" -#: gitk:4076 msgid "References (space separated list):" msgstr "リファレンス(スペース区切りのリスト):" -#: gitk:4077 msgid "Branches & tags:" msgstr "ブランチ&タグ:" -#: gitk:4078 msgid "All refs" msgstr "全てのリファレンス" -#: gitk:4079 msgid "All (local) branches" msgstr "全ての(ローカルな)ブランチ" -#: gitk:4080 msgid "All tags" msgstr "全てのタグ" -#: gitk:4081 msgid "All remote-tracking branches" msgstr "全てのリモート追跡ブランチ" -#: gitk:4082 msgid "Commit Info (regular expressions):" msgstr "コミット情報(正規表現):" -#: gitk:4083 msgid "Author:" msgstr "作者:" -#: gitk:4084 msgid "Committer:" msgstr "コミット者:" -#: gitk:4085 msgid "Commit Message:" msgstr "コミットメッセージ:" -#: gitk:4086 msgid "Matches all Commit Info criteria" msgstr "コミット情報の全ての条件に一致" -#: gitk:4087 msgid "Matches no Commit Info criteria" msgstr "コミット情報の全ての条件に不一致" -#: gitk:4088 msgid "Changes to Files:" msgstr "変更したファイル:" -#: gitk:4089 msgid "Fixed String" msgstr "固定文字列" -#: gitk:4090 msgid "Regular Expression" msgstr "正規表現" -#: gitk:4091 msgid "Search string:" msgstr "検索文字列:" -#: gitk:4092 msgid "" "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" @@ -709,201 +548,153 @@ msgstr "" "コミット日時 (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" -#: gitk:4093 msgid "Since:" msgstr "期間の始め:" -#: gitk:4094 msgid "Until:" msgstr "期間の終わり:" -#: gitk:4095 msgid "Limit and/or skip a number of revisions (positive integer):" msgstr "制限・省略するリビジョンの数(正の整数):" -#: gitk:4096 msgid "Number to show:" msgstr "表示する数:" -#: gitk:4097 msgid "Number to skip:" msgstr "省略する数:" -#: gitk:4098 msgid "Miscellaneous options:" msgstr "その他のオプション:" -#: gitk:4099 msgid "Strictly sort by date" msgstr "厳密に日付順で並び替え" -#: gitk:4100 msgid "Mark branch sides" msgstr "側枝マーク" -#: gitk:4101 msgid "Limit to first parent" msgstr "最初の親に制限" -#: gitk:4102 msgid "Simple history" msgstr "簡易な履歴" -#: gitk:4103 msgid "Additional arguments to git log:" msgstr "git log への追加の引数:" -#: gitk:4104 msgid "Enter files and directories to include, one per line:" msgstr "含まれるファイル・ディレクトリを一行ごとに入力:" -#: gitk:4105 msgid "Command to generate more commits to include:" msgstr "コミット追加コマンド:" -#: gitk:4229 msgid "Gitk: edit view" msgstr "Gitk: ビュー編集" -#: gitk:4237 msgid "-- criteria for selecting revisions" msgstr "― リビジョンの選択条件" -#: gitk:4242 msgid "View Name" msgstr "ビュー名:" -#: gitk:4317 msgid "Apply (F5)" msgstr "適用 (F5)" -#: gitk:4355 msgid "Error in commit selection arguments:" msgstr "コミット選択引数のエラー:" -#: gitk:4410 gitk:4463 gitk:4925 gitk:4939 gitk:6209 gitk:12388 gitk:12389 msgid "None" msgstr "無し" -#: gitk:5022 gitk:5027 msgid "Descendant" msgstr "子孫" -#: gitk:5023 msgid "Not descendant" msgstr "非子孫" -#: gitk:5030 gitk:5035 msgid "Ancestor" msgstr "祖先" -#: gitk:5031 msgid "Not ancestor" msgstr "非祖先" -#: gitk:5325 msgid "Local changes checked in to index but not committed" msgstr "ステージされた、コミット前のローカルな変更" -#: gitk:5361 msgid "Local uncommitted changes, not checked in to index" msgstr "ステージされていない、コミット前のローカルな変更" -#: gitk:7135 msgid "and many more" msgstr "他多数" -#: gitk:7138 msgid "many" msgstr "多数" -#: gitk:7329 msgid "Tags:" msgstr "タグ:" -#: gitk:7346 gitk:7352 gitk:8826 msgid "Parent" msgstr "親" -#: gitk:7357 msgid "Child" msgstr "子" -#: gitk:7366 msgid "Branch" msgstr "ブランチ" -#: gitk:7369 msgid "Follows" msgstr "下位" -#: gitk:7372 msgid "Precedes" msgstr "上位" -#: gitk:7967 #, tcl-format msgid "Error getting diffs: %s" msgstr "diff取得エラー: %s" -#: gitk:8651 msgid "Goto:" msgstr "Goto:" -#: gitk:8672 #, tcl-format msgid "Short SHA1 id %s is ambiguous" msgstr "%s を含む SHA1 ID は複数存在します" -#: gitk:8679 #, tcl-format msgid "Revision %s is not known" msgstr "リビジョン %s は不明です" -#: gitk:8689 #, tcl-format msgid "SHA1 id %s is not known" msgstr "SHA1 id %s は不明です" -#: gitk:8691 #, tcl-format msgid "Revision %s is not in the current view" msgstr "リビジョン %s は現在のビューにはありません" -#: gitk:8833 gitk:8848 msgid "Date" msgstr "日付" -#: gitk:8836 msgid "Children" msgstr "子" -#: gitk:8899 #, tcl-format msgid "Reset %s branch to here" msgstr "%s ブランチをここにリセットする" -#: gitk:8901 msgid "Detached head: can't reset" msgstr "切り離されたHEAD: リセットできません" -#: gitk:9006 gitk:9012 msgid "Skipping merge commit " msgstr "コミットマージをスキップ: " -#: gitk:9021 gitk:9026 msgid "Error getting patch ID for " msgstr "パッチ取得エラー: ID " -#: gitk:9022 gitk:9027 msgid " - stopping\n" msgstr " - 停止\n" -#: gitk:9032 gitk:9035 gitk:9043 gitk:9057 gitk:9066 msgid "Commit " msgstr "コミット " -#: gitk:9036 msgid "" " is the same patch as\n" " " @@ -911,7 +702,6 @@ msgstr "" " は下記のパッチと同等\n" " " -#: gitk:9044 msgid "" " differs from\n" " " @@ -919,7 +709,6 @@ msgstr "" " 下記からのdiff\n" " " -#: gitk:9046 msgid "" "Diff of commits:\n" "\n" @@ -927,130 +716,100 @@ msgstr "" "コミットのdiff:\n" "\n" -#: gitk:9058 gitk:9067 #, tcl-format msgid " has %s children - stopping\n" msgstr " には %s の子があります - 停止\n" -#: gitk:9086 #, tcl-format msgid "Error writing commit to file: %s" msgstr "ファイルへのコミット書き出しエラー: %s" -#: gitk:9092 #, tcl-format msgid "Error diffing commits: %s" msgstr "コミットのdiff実行エラー: %s" -#: gitk:9138 msgid "Top" msgstr "Top" -#: gitk:9139 msgid "From" msgstr "From" -#: gitk:9144 msgid "To" msgstr "To" -#: gitk:9168 msgid "Generate patch" msgstr "パッチ生成" -#: gitk:9170 msgid "From:" msgstr "From:" -#: gitk:9179 msgid "To:" msgstr "To:" -#: gitk:9188 msgid "Reverse" msgstr "逆" -#: gitk:9190 gitk:9400 msgid "Output file:" msgstr "出力ファイル:" -#: gitk:9196 msgid "Generate" msgstr "生成" -#: gitk:9234 msgid "Error creating patch:" msgstr "パッチ生成エラー:" -#: gitk:9257 gitk:9388 gitk:9445 msgid "ID:" msgstr "ID:" -#: gitk:9266 msgid "Tag name:" msgstr "タグ名:" -#: gitk:9269 msgid "Tag message is optional" msgstr "タグメッセージを付ける事も出来ます" -#: gitk:9271 msgid "Tag message:" msgstr "タグメッセージ:" -#: gitk:9275 gitk:9454 msgid "Create" msgstr "生成" -#: gitk:9293 msgid "No tag name specified" msgstr "タグの名称が指定されていません" -#: gitk:9297 #, tcl-format msgid "Tag \"%s\" already exists" msgstr "タグ \"%s\" は既に存在します" -#: gitk:9307 msgid "Error creating tag:" msgstr "タグ生成エラー:" -#: gitk:9397 msgid "Command:" msgstr "コマンド:" -#: gitk:9405 msgid "Write" msgstr "書き出し" -#: gitk:9423 msgid "Error writing commit:" msgstr "コミット書き出しエラー:" -#: gitk:9450 msgid "Name:" msgstr "名前:" -#: gitk:9473 msgid "Please specify a name for the new branch" msgstr "新しいブランチの名前を指定してください" -#: gitk:9478 #, tcl-format msgid "Branch '%s' already exists. Overwrite?" msgstr "ブランチ '%s' は既に存在します。上書きしますか?" -#: gitk:9545 #, tcl-format msgid "Commit %s is already included in branch %s -- really re-apply it?" msgstr "" "コミット %s は既にブランチ %s に含まれています ― 本当にこれを再適用しますか?" -#: gitk:9550 msgid "Cherry-picking" msgstr "チェリーピック中" -#: gitk:9559 #, tcl-format msgid "" "Cherry-pick failed because of local changes to file '%s'.\n" @@ -1060,7 +819,6 @@ msgstr "" "あなたの変更に commit, reset, stash のいずれかを行ってからやり直してくださ" "い。" -#: gitk:9565 msgid "" "Cherry-pick failed because of merge conflict.\n" "Do you wish to run git citool to resolve it?" @@ -1068,27 +826,25 @@ msgstr "" "マージの衝突によってチェリーピックは失敗しました。\n" "この解決のために git citool を実行したいですか?" -#: gitk:9581 gitk:9639 msgid "No changes committed" msgstr "何の変更もコミットされていません" -#: gitk:9608 #, tcl-format msgid "Commit %s is not included in branch %s -- really revert it?" -msgstr "コミット %s は既にブランチ %s に含まれています ― 本当にこれを撤回しますか?" +msgstr "" +"コミット %s は既にブランチ %s に含まれています ― 本当にこれを撤回しますか?" -#: gitk:9613 msgid "Reverting" msgstr "撤回中" -#: gitk:9621 #, tcl-format msgid "" "Revert failed because of local changes to the following files:%s Please " "commit, reset or stash your changes and try again." -msgstr "ファイル '%s' のローカルな変更のために撤回は失敗しました。 あなたの変更に commit, reset, stash のいずれかを行ってからやり直してください。" +msgstr "" +"ファイル '%s' のローカルな変更のために撤回は失敗しました。 あなたの変更に " +"commit, reset, stash のいずれかを行ってからやり直してください。" -#: gitk:9625 msgid "" "Revert failed because of merge conflict.\n" " Do you wish to run git citool to resolve it?" @@ -1096,28 +852,22 @@ msgstr "" "マージの衝突によって撤回は失敗しました。\n" "この解決のために git citool を実行したいですか?" -#: gitk:9668 msgid "Confirm reset" msgstr "確認を取り消す" -#: gitk:9670 #, tcl-format msgid "Reset branch %s to %s?" msgstr "ブランチ %s を %s にリセットしますか?" -#: gitk:9672 msgid "Reset type:" msgstr "Reset タイプ:" -#: gitk:9675 msgid "Soft: Leave working tree and index untouched" msgstr "Soft: 作業ツリーもインデックスもそのままにする" -#: gitk:9678 msgid "Mixed: Leave working tree untouched, reset index" msgstr "Mixed: 作業ツリーをそのままにして、インデックスをリセット" -#: gitk:9681 msgid "" "Hard: Reset working tree and index\n" "(discard ALL local changes)" @@ -1125,19 +875,15 @@ msgstr "" "Hard: 作業ツリーやインデックスをリセット\n" "(「全ての」ローカルな変更を破棄)" -#: gitk:9698 msgid "Resetting" msgstr "リセット中" -#: gitk:9758 msgid "Checking out" msgstr "チェックアウト" -#: gitk:9811 msgid "Cannot delete the currently checked-out branch" msgstr "現在チェックアウトされているブランチを削除することはできません" -#: gitk:9817 #, tcl-format msgid "" "The commits on branch %s aren't on any other branch.\n" @@ -1146,16 +892,13 @@ msgstr "" "ブランチ %s には他のブランチに存在しないコミットがあります。\n" "本当にブランチ %s を削除しますか?" -#: gitk:9848 #, tcl-format msgid "Tags and heads: %s" msgstr "タグとHEAD: %s" -#: gitk:9865 msgid "Filter" msgstr "フィルター" -#: gitk:10161 msgid "" "Error reading commit topology information; branch and preceding/following " "tag information will be incomplete." @@ -1163,201 +906,152 @@ msgstr "" "コミット構造情報読み込みエラー; ブランチ及び上位/下位のタグ情報が不完全である" "ようです。" -#: gitk:11138 msgid "Tag" msgstr "タグ" -#: gitk:11142 msgid "Id" msgstr "ID" -#: gitk:11225 msgid "Gitk font chooser" msgstr "Gitk フォント選択" -#: gitk:11242 msgid "B" msgstr "B" -#: gitk:11245 msgid "I" msgstr "I" -#: gitk:11363 msgid "Commit list display options" msgstr "コミットリスト表示オプション" -#: gitk:11366 msgid "Maximum graph width (lines)" msgstr "最大グラフ幅(線の本数)" -#: gitk:11370 #, no-tcl-format msgid "Maximum graph width (% of pane)" msgstr "最大グラフ幅(ペインに対する%)" -#: gitk:11373 msgid "Show local changes" msgstr "ローカルな変更を表示" -#: gitk:11376 msgid "Auto-select SHA1 (length)" msgstr "SHA1 の自動選択 (選択文字数指定)" -#: gitk:11380 msgid "Hide remote refs" msgstr "リモートリファレンスを隠す" -#: gitk:11384 msgid "Diff display options" msgstr "diff表示オプション" -#: gitk:11386 msgid "Tab spacing" msgstr "タブ空白幅" -#: gitk:11389 msgid "Display nearby tags/heads" msgstr "近くの タグ/head を表示する" -#: gitk:11392 msgid "Maximum # tags/heads to show" msgstr "タグ/head の最大表示数" -#: gitk:11395 msgid "Limit diffs to listed paths" msgstr "diff をリストのパスに制限" -#: gitk:11398 msgid "Support per-file encodings" msgstr "ファイルごとのエンコーディングのサポート" -#: gitk:11404 gitk:11551 msgid "External diff tool" msgstr "外部diffツール" -#: gitk:11405 msgid "Choose..." msgstr "選択..." -#: gitk:11410 msgid "General options" msgstr "全体設定" -#: gitk:11413 msgid "Use themed widgets" msgstr "テーマウィジェットを使用する" -#: gitk:11415 msgid "(change requires restart)" msgstr "(変更には再起動が必要です)" -#: gitk:11417 msgid "(currently unavailable)" msgstr "(現在は使用出来ません)" -#: gitk:11428 msgid "Colors: press to choose" msgstr "色: ボタンを押して選択" -#: gitk:11431 msgid "Interface" msgstr "インターフェイス" -#: gitk:11432 msgid "interface" msgstr "インターフェイス" -#: gitk:11435 msgid "Background" msgstr "背景" -#: gitk:11436 gitk:11466 msgid "background" msgstr "背景" -#: gitk:11439 msgid "Foreground" msgstr "前景" -#: gitk:11440 msgid "foreground" msgstr "前景" -#: gitk:11443 msgid "Diff: old lines" msgstr "Diff: 旧バージョン" -#: gitk:11444 msgid "diff old lines" msgstr "diff 旧バージョン" -#: gitk:11448 msgid "Diff: new lines" msgstr "Diff: 新バージョン" -#: gitk:11449 msgid "diff new lines" msgstr "diff 新バージョン" -#: gitk:11453 msgid "Diff: hunk header" msgstr "Diff: hunkヘッダ" -#: gitk:11455 msgid "diff hunk header" msgstr "diff hunkヘッダ" -#: gitk:11459 msgid "Marked line bg" msgstr "マーク行の背景" -#: gitk:11461 msgid "marked line background" msgstr "マーク行の背景" -#: gitk:11465 msgid "Select bg" msgstr "選択の背景" -#: gitk:11474 msgid "Fonts: press to choose" msgstr "フォント: ボタンを押して選択" -#: gitk:11476 msgid "Main font" msgstr "主フォント" -#: gitk:11477 msgid "Diff display font" msgstr "Diff表示用フォント" -#: gitk:11478 msgid "User interface font" msgstr "UI用フォント" -#: gitk:11500 msgid "Gitk preferences" msgstr "Gitk 設定" -#: gitk:11509 msgid "General" msgstr "一般" -#: gitk:11510 msgid "Colors" msgstr "色" -#: gitk:11511 msgid "Fonts" msgstr "フォント" -#: gitk:11561 #, tcl-format msgid "Gitk: choose color for %s" msgstr "Gitk: 「%s」 の色を選択" -#: gitk:12074 msgid "" "Sorry, gitk cannot run with this version of Tcl/Tk.\n" " Gitk requires at least Tcl/Tk 8.4." @@ -1365,15 +1059,12 @@ msgstr "" "申し訳ありませんが、このバージョンの Tcl/Tk では gitk を実行出来ません。\n" "Gitkの実行には Tcl/Tk 8.4 以上が必要です。" -#: gitk:12284 msgid "Cannot find a git repository here." msgstr "ここにはgitリポジトリがありません。" -#: gitk:12331 #, tcl-format msgid "Ambiguous argument '%s': both revision and filename" msgstr "あいまいな引数 '%s': リビジョンとファイル名の両方に解釈できます" -#: gitk:12343 msgid "Bad arguments to gitk:" msgstr "gitkへの不正な引数:" diff --git a/gitk-git/po/pt_br.po b/gitk-git/po/pt_br.po index 1feb34854b..e78fb26a2e 100644 --- a/gitk-git/po/pt_br.po +++ b/gitk-git/po/pt_br.po @@ -7,7 +7,7 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: gitk\n" +"Project-Id-Version: Gitk\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-05-17 14:32+1000\n" "PO-Revision-Date: 2010-12-06 23:39-0200\n" @@ -18,33 +18,26 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: gitk:140 msgid "Couldn't get list of unmerged files:" msgstr "Não foi possível obter a lista dos arquivos não mesclados:" -#: gitk:212 gitk:2381 msgid "Color words" msgstr "" -#: gitk:217 gitk:2381 gitk:8220 gitk:8253 msgid "Markup words" msgstr "" -#: gitk:324 msgid "Error parsing revisions:" msgstr "Erro ao interpretar revisões:" -#: gitk:380 msgid "Error executing --argscmd command:" msgstr "Erro ao executar o comando--argscmd:" -#: gitk:393 msgid "No files selected: --merge specified but no files are unmerged." msgstr "" "Nenhum arquivo foi selecionado: --merge especificado mas não há arquivos não-" "mesclados." -#: gitk:396 msgid "" "No files selected: --merge specified but no unmerged files are within file " "limit." @@ -52,317 +45,237 @@ msgstr "" "Nenhum arquivo foi selecionado: --merge especificado mas não há arquivos não-" "mesclados dentro dos limites." -#: gitk:418 gitk:566 msgid "Error executing git log:" msgstr "Erro ao executar git log:" -#: gitk:436 gitk:582 msgid "Reading" msgstr "Lendo" -#: gitk:496 gitk:4525 msgid "Reading commits..." msgstr "Lendo revisões..." -#: gitk:499 gitk:1637 gitk:4528 msgid "No commits selected" msgstr "Nenhuma revisão foi selecionada" -#: gitk:1445 gitk:4045 gitk:12432 msgid "Command line" msgstr "Linha de comando" -#: gitk:1511 msgid "Can't parse git log output:" msgstr "Não foi possível interpretar a saída do \"git log\":" -#: gitk:1740 msgid "No commit information available" msgstr "Não há informações disponíveis sobre a revisão" -#: gitk:1903 gitk:1932 gitk:4315 gitk:9669 gitk:11241 gitk:11521 msgid "OK" msgstr "Ok" -#: gitk:1934 gitk:4317 gitk:9196 gitk:9275 gitk:9391 gitk:9440 gitk:9671 -#: gitk:11242 gitk:11522 msgid "Cancel" msgstr "Cancelar" -#: gitk:2069 msgid "&Update" msgstr "Atualizar" -#: gitk:2070 msgid "&Reload" msgstr "Recarregar" -#: gitk:2071 msgid "Reread re&ferences" msgstr "Ler as referências novamente" -#: gitk:2072 msgid "&List references" msgstr "Listar referências" -#: gitk:2074 msgid "Start git &gui" msgstr "Iniciar Git GUI" -#: gitk:2076 msgid "&Quit" msgstr "Sair" -#: gitk:2068 msgid "&File" msgstr "Arquivo" -#: gitk:2080 msgid "&Preferences" msgstr "Preferências" -#: gitk:2079 msgid "&Edit" msgstr "Editar" -#: gitk:2084 msgid "&New view..." msgstr "Nova vista..." -#: gitk:2085 msgid "&Edit view..." msgstr "Editar vista..." -#: gitk:2086 msgid "&Delete view" msgstr "Apagar vista" -#: gitk:2088 gitk:4043 msgid "&All files" msgstr "Todos os arquivos" -#: gitk:2083 gitk:4067 msgid "&View" msgstr "Exibir" -#: gitk:2093 gitk:2103 gitk:3012 msgid "&About gitk" msgstr "Sobre o gitk" -#: gitk:2094 gitk:2108 msgid "&Key bindings" msgstr "Atalhos de teclado" -#: gitk:2092 gitk:2107 msgid "&Help" msgstr "Ajuda" -#: gitk:2185 gitk:8652 msgid "SHA1 ID:" msgstr "SHA1 ID:" -#: gitk:2229 msgid "Row" msgstr "Linha" -#: gitk:2267 msgid "Find" msgstr "Encontrar" -#: gitk:2295 msgid "commit" msgstr "Revisão" -#: gitk:2299 gitk:2301 gitk:4687 gitk:4710 gitk:4734 gitk:6755 gitk:6827 -#: gitk:6912 msgid "containing:" msgstr "contendo:" -#: gitk:2302 gitk:3526 gitk:3531 gitk:4763 msgid "touching paths:" msgstr "envolvendo os caminhos:" -#: gitk:2303 gitk:4777 msgid "adding/removing string:" msgstr "Adicionando/removendo texto:" -#: gitk:2304 gitk:4779 msgid "changing lines matching:" msgstr "" -#: gitk:2313 gitk:2315 gitk:4766 msgid "Exact" msgstr "Exatamente" -#: gitk:2315 gitk:4854 gitk:6723 msgid "IgnCase" msgstr "Ignorar maiúsculas/minúsculas" -#: gitk:2315 gitk:4736 gitk:4852 gitk:6719 msgid "Regexp" msgstr "Expressão regular" -#: gitk:2317 gitk:2318 gitk:4874 gitk:4904 gitk:4911 gitk:6848 gitk:6916 msgid "All fields" msgstr "Todos os campos" -#: gitk:2318 gitk:4871 gitk:4904 gitk:6786 msgid "Headline" msgstr "Assunto" -#: gitk:2319 gitk:4871 gitk:6786 gitk:6916 gitk:7389 msgid "Comments" msgstr "Descrição da revisão" -#: gitk:2319 gitk:4871 gitk:4876 gitk:4911 gitk:6786 gitk:7324 gitk:8830 -#: gitk:8845 msgid "Author" msgstr "Autor" -#: gitk:2319 gitk:4871 gitk:6786 gitk:7326 msgid "Committer" msgstr "Revisor" -#: gitk:2350 msgid "Search" msgstr "Buscar" -#: gitk:2358 msgid "Diff" msgstr "Diferenças" -#: gitk:2360 msgid "Old version" msgstr "Versão antiga" -#: gitk:2362 msgid "New version" msgstr "Versão nova" -#: gitk:2364 msgid "Lines of context" msgstr "Número de linhas de contexto" -#: gitk:2374 msgid "Ignore space change" msgstr "Ignorar mudanças de caixa" -#: gitk:2378 gitk:2380 gitk:7959 gitk:8206 msgid "Line diff" msgstr "" -#: gitk:2445 msgid "Patch" msgstr "Diferenças" -#: gitk:2447 msgid "Tree" msgstr "Árvore" -#: gitk:2617 gitk:2637 msgid "Diff this -> selected" msgstr "Comparar esta revisão com a selecionada" -#: gitk:2618 gitk:2638 msgid "Diff selected -> this" msgstr "Comparar a revisão selecionada com esta" -#: gitk:2619 gitk:2639 msgid "Make patch" msgstr "Criar patch" -#: gitk:2620 gitk:9254 msgid "Create tag" msgstr "Criar etiqueta" -#: gitk:2621 gitk:9371 msgid "Write commit to file" msgstr "Salvar revisão para um arquivo" -#: gitk:2622 gitk:9428 msgid "Create new branch" msgstr "Criar novo ramo" -#: gitk:2623 msgid "Cherry-pick this commit" msgstr "Fazer cherry-pick desta revisão" -#: gitk:2624 msgid "Reset HEAD branch to here" msgstr "Redefinir HEAD para cá" -#: gitk:2625 msgid "Mark this commit" msgstr "Marcar esta revisão" -#: gitk:2626 msgid "Return to mark" msgstr "Voltar à marca" -#: gitk:2627 msgid "Find descendant of this and mark" msgstr "Encontrar descendente e marcar" -#: gitk:2628 msgid "Compare with marked commit" msgstr "Comparar com a revisão marcada" -#: gitk:2629 gitk:2640 #, fuzzy msgid "Diff this -> marked commit" msgstr "Comparar esta revisão com a selecionada" -#: gitk:2630 gitk:2641 #, fuzzy msgid "Diff marked commit -> this" msgstr "Comparar a revisão selecionada com esta" -#: gitk:2631 #, fuzzy msgid "Revert this commit" msgstr "Marcar esta revisão" -#: gitk:2647 msgid "Check out this branch" msgstr "Efetuar checkout deste ramo" -#: gitk:2648 msgid "Remove this branch" msgstr "Excluir este ramo" -#: gitk:2649 msgid "Copy branch name" msgstr "" -#: gitk:2656 msgid "Highlight this too" msgstr "Marcar este também" -#: gitk:2657 msgid "Highlight this only" msgstr "Marcar apenas este" -#: gitk:2658 msgid "External diff" msgstr "Diff externo" -#: gitk:2659 msgid "Blame parent commit" msgstr "Anotar revisão anterior" -#: gitk:2660 msgid "Copy path" msgstr "" -#: gitk:2667 msgid "Show origin of this line" msgstr "Exibir origem desta linha" -#: gitk:2668 msgid "Run git gui blame on this line" msgstr "Executar 'git blame' nesta linha" -#: gitk:3014 #, fuzzy msgid "" "\n" @@ -379,320 +292,248 @@ msgstr "" "\n" "Uso e distribuição segundo os termos da Licença Pública Geral GNU" -#: gitk:3022 gitk:3089 gitk:9857 msgid "Close" msgstr "Fechar" -#: gitk:3043 msgid "Gitk key bindings" msgstr "Atalhos de teclado" -#: gitk:3046 msgid "Gitk key bindings:" msgstr "Atalhos de teclado:" -#: gitk:3048 #, tcl-format msgid "<%s-Q>\t\tQuit" msgstr "<%s-Q>\t\tSair" -#: gitk:3049 #, tcl-format msgid "<%s-W>\t\tClose window" msgstr "<%s-W>\t\tFechar janela" -#: gitk:3050 msgid "<Home>\t\tMove to first commit" msgstr "<Home>\t\tIr para a primeira revisão" -#: gitk:3051 msgid "<End>\t\tMove to last commit" msgstr "<End>\t\tIr para a última revisão" -#: gitk:3052 #, fuzzy msgid "<Up>, p, k\tMove up one commit" msgstr "<Up>, p, i\tIr para uma revisão acima" -#: gitk:3053 #, fuzzy msgid "<Down>, n, j\tMove down one commit" msgstr "<Down>, n, k\tIr para uma revisão abaixo" -#: gitk:3054 #, fuzzy msgid "<Left>, z, h\tGo back in history list" msgstr "<Left>, z, j\tVoltar no histórico" -#: gitk:3055 msgid "<Right>, x, l\tGo forward in history list" msgstr "<Right>, x, l\tAvançar no histórico" -#: gitk:3056 #, tcl-format msgid "<%s-n>\tGo to n-th parent of current commit in history list" msgstr "" -#: gitk:3057 msgid "<PageUp>\tMove up one page in commit list" msgstr "<PageUp>\tSubir uma página na lista de revisões" -#: gitk:3058 msgid "<PageDown>\tMove down one page in commit list" msgstr "<PageDown>\tDescer uma página na lista de revisões" -#: gitk:3059 #, tcl-format msgid "<%s-Home>\tScroll to top of commit list" msgstr "<%s-Home>\tRolar para o início da lista de revisões" -#: gitk:3060 #, tcl-format msgid "<%s-End>\tScroll to bottom of commit list" msgstr "<%s-End>\tRolar para o final da lista de revisões" -#: gitk:3061 #, tcl-format msgid "<%s-Up>\tScroll commit list up one line" msgstr "<%s-Up>\tRolar uma linha acima na lista de revisões" -#: gitk:3062 #, tcl-format msgid "<%s-Down>\tScroll commit list down one line" msgstr "<%s-Down>\tRolar uma linha abaixo na lista de revisões" -#: gitk:3063 #, tcl-format msgid "<%s-PageUp>\tScroll commit list up one page" msgstr "<%s-PageUp>\tRolar uma página acima na lista de revisões" -#: gitk:3064 #, tcl-format msgid "<%s-PageDown>\tScroll commit list down one page" msgstr "<%s-PageDown>\tRolar uma página abaixo na lista de revisões" -#: gitk:3065 msgid "<Shift-Up>\tFind backwards (upwards, later commits)" msgstr "<Shift-Up>\tProcurar próxima (revisões mas recentes)" -#: gitk:3066 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)" msgstr "<Shift-Down>\tProcurar anterior (revisões mais antigas)" -#: gitk:3067 msgid "<Delete>, b\tScroll diff view up one page" msgstr "<Delete>, b\tRola alterações uma página acima" -#: gitk:3068 msgid "<Backspace>\tScroll diff view up one page" msgstr "<Backspace>\tRolar alterações uma página abaixo" -#: gitk:3069 msgid "<Space>\t\tScroll diff view down one page" msgstr "<Space>\t\tRolar alterações uma página abaixo" -#: gitk:3070 msgid "u\t\tScroll diff view up 18 lines" msgstr "u\t\tRolar alterações 18 linhas acima" -#: gitk:3071 msgid "d\t\tScroll diff view down 18 lines" msgstr "d\t\tRolar alterações 18 linhas abaixo" -#: gitk:3072 #, tcl-format msgid "<%s-F>\t\tFind" msgstr "<%s-F>\t\tProcurar" -#: gitk:3073 #, tcl-format msgid "<%s-G>\t\tMove to next find hit" msgstr "<%s-G>\t\tIr para a próxima ocorrência" -#: gitk:3074 msgid "<Return>\tMove to next find hit" msgstr "<Return>\tIr para a próxima ocorrência" -#: gitk:3075 #, fuzzy msgid "g\t\tGo to commit" msgstr "<End>\t\tIr para a última revisão" -#: gitk:3076 msgid "/\t\tFocus the search box" msgstr "/\t\tPor foco na caixa de busca" -#: gitk:3077 msgid "?\t\tMove to previous find hit" msgstr "?\t\tIr para a ocorrência anterior" -#: gitk:3078 msgid "f\t\tScroll diff view to next file" msgstr "f\t\tRolar alterações para o próximo arquivo" -#: gitk:3079 #, tcl-format msgid "<%s-S>\t\tSearch for next hit in diff view" msgstr "<%s-S>\t\tProcurar a próxima ocorrência na lista de alterações" -#: gitk:3080 #, tcl-format msgid "<%s-R>\t\tSearch for previous hit in diff view" msgstr "<%s-R>\t\tProcurar ocorrência anterior na lista de alterações" -#: gitk:3081 #, tcl-format msgid "<%s-KP+>\tIncrease font size" msgstr "<%s-KP+>\tAumentar tamanho da fonte" -#: gitk:3082 #, tcl-format msgid "<%s-plus>\tIncrease font size" msgstr "<%s-plus>\tAumentar tamanho da fonte" -#: gitk:3083 #, tcl-format msgid "<%s-KP->\tDecrease font size" msgstr "<%s-KP->\tReduzir tamanho da fonte" -#: gitk:3084 #, tcl-format msgid "<%s-minus>\tDecrease font size" msgstr "<%s-minus>\tReduzir tamanho da fonte" -#: gitk:3085 msgid "<F5>\t\tUpdate" msgstr "<F5>\t\tAtualizar" -#: gitk:3550 gitk:3559 #, tcl-format msgid "Error creating temporary directory %s:" msgstr "Erro ao criar o diretório temporário %s:" -#: gitk:3572 #, tcl-format msgid "Error getting \"%s\" from %s:" msgstr "Erro ao ler \"%s\" de %s:" -#: gitk:3635 msgid "command failed:" msgstr "O comando falhou:" -#: gitk:3784 msgid "No such commit" msgstr "Revisão não encontrada" -#: gitk:3798 msgid "git gui blame: command failed:" msgstr "Comando 'git gui blame' falhou:" -#: gitk:3829 #, tcl-format msgid "Couldn't read merge head: %s" msgstr "Impossível ler merge head: %s" -#: gitk:3837 #, tcl-format msgid "Error reading index: %s" msgstr "Erro ao ler o índice: %s" -#: gitk:3862 #, tcl-format msgid "Couldn't start git blame: %s" msgstr "Não foi possível inciar o 'git blame': %s" -#: gitk:3865 gitk:6754 msgid "Searching" msgstr "Procurando" -#: gitk:3897 #, tcl-format msgid "Error running git blame: %s" msgstr "Erro ao executar 'git blame': %s" -#: gitk:3925 #, tcl-format msgid "That line comes from commit %s, which is not in this view" msgstr "Esta linha vem da revisão %s, que não está nesta vista" -#: gitk:3939 msgid "External diff viewer failed:" msgstr "Erro do visualizador de alterações externo:" -#: gitk:4070 msgid "Gitk view definition" msgstr "Definir vista" -#: gitk:4074 msgid "Remember this view" msgstr "Lembrar esta vista" -#: gitk:4075 msgid "References (space separated list):" msgstr "Referências (separar a lista com um espaço):" -#: gitk:4076 msgid "Branches & tags:" msgstr "Ramos & etiquetas:" -#: gitk:4077 msgid "All refs" msgstr "Todas as referências" -#: gitk:4078 msgid "All (local) branches" msgstr "Todos os ramos locais" -#: gitk:4079 msgid "All tags" msgstr "Todas as etiquetas" -#: gitk:4080 msgid "All remote-tracking branches" msgstr "Todos os ramos de rastreio" -#: gitk:4081 msgid "Commit Info (regular expressions):" msgstr "Informações da revisão (expressões regulares):" -#: gitk:4082 msgid "Author:" msgstr "Autor:" -#: gitk:4083 msgid "Committer:" msgstr "Revisor:" -#: gitk:4084 msgid "Commit Message:" msgstr "Descrição da revisão:" -#: gitk:4085 msgid "Matches all Commit Info criteria" msgstr "Coincidir todos os critérios de informações da revisão" -#: gitk:4086 #, fuzzy msgid "Matches no Commit Info criteria" msgstr "Coincidir todos os critérios de informações da revisão" -#: gitk:4087 msgid "Changes to Files:" msgstr "Mudanças para os arquivos:" -#: gitk:4088 msgid "Fixed String" msgstr "Texto fixo" -#: gitk:4089 msgid "Regular Expression" msgstr "Expressão regular" -#: gitk:4090 msgid "Search string:" msgstr "Texto de busca" -#: gitk:4091 msgid "" "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" @@ -700,201 +541,153 @@ msgstr "" "Datas de revisão (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" -#: gitk:4092 msgid "Since:" msgstr "Desde:" -#: gitk:4093 msgid "Until:" msgstr "Até:" -#: gitk:4094 msgid "Limit and/or skip a number of revisions (positive integer):" msgstr "Limitar e/ou ignorar um número de revisões (inteiro positivo):" -#: gitk:4095 msgid "Number to show:" msgstr "Número para mostrar:" -#: gitk:4096 msgid "Number to skip:" msgstr "Número para ignorar:" -#: gitk:4097 msgid "Miscellaneous options:" msgstr "Opções diversas:" -#: gitk:4098 msgid "Strictly sort by date" msgstr "Ordenar estritamente pela data" -#: gitk:4099 msgid "Mark branch sides" msgstr "Marcar os dois lados do ramo" -#: gitk:4100 msgid "Limit to first parent" msgstr "Limitar ao primeiro antecessor" -#: gitk:4101 msgid "Simple history" msgstr "Histórico simplificado" -#: gitk:4102 msgid "Additional arguments to git log:" msgstr "Argumentos adicionais para o 'git log':" -#: gitk:4103 msgid "Enter files and directories to include, one per line:" msgstr "Arquivos e diretórios para incluir, um por linha" -#: gitk:4104 msgid "Command to generate more commits to include:" msgstr "Comando para gerar mais revisões para incluir:" -#: gitk:4228 msgid "Gitk: edit view" msgstr "Gitk: editar vista" -#: gitk:4236 msgid "-- criteria for selecting revisions" msgstr "-- critérios para selecionar revisões" -#: gitk:4241 msgid "View Name" msgstr "Nome da vista" -#: gitk:4316 msgid "Apply (F5)" msgstr "Aplicar (F5)" -#: gitk:4354 msgid "Error in commit selection arguments:" msgstr "Erro nos argumentos de seleção de revisões:" -#: gitk:4409 gitk:4462 gitk:4924 gitk:4938 gitk:6208 gitk:12373 gitk:12374 msgid "None" msgstr "Nenhum" -#: gitk:5021 gitk:5026 msgid "Descendant" msgstr "Descendente de" -#: gitk:5022 msgid "Not descendant" msgstr "Não descendente de" -#: gitk:5029 gitk:5034 msgid "Ancestor" msgstr "Antecessor de" -#: gitk:5030 msgid "Not ancestor" msgstr "Não antecessor de" -#: gitk:5324 msgid "Local changes checked in to index but not committed" msgstr "Mudanças locais marcadas, porém não salvas" -#: gitk:5360 msgid "Local uncommitted changes, not checked in to index" msgstr "Mudanças locais não marcadas" -#: gitk:7134 msgid "and many more" msgstr "" -#: gitk:7137 msgid "many" msgstr "muitas" -#: gitk:7328 msgid "Tags:" msgstr "Etiquetas:" -#: gitk:7345 gitk:7351 gitk:8825 msgid "Parent" msgstr "Antecessor" -#: gitk:7356 msgid "Child" msgstr "Descendente" -#: gitk:7365 msgid "Branch" msgstr "Ramo" -#: gitk:7368 msgid "Follows" msgstr "Segue" -#: gitk:7371 msgid "Precedes" msgstr "Precede" -#: gitk:7966 #, tcl-format msgid "Error getting diffs: %s" msgstr "Erro ao obter diferenças: %s" -#: gitk:8650 msgid "Goto:" msgstr "Ir para:" -#: gitk:8671 #, tcl-format msgid "Short SHA1 id %s is ambiguous" msgstr "O id SHA1 %s é ambíguo" -#: gitk:8678 #, tcl-format msgid "Revision %s is not known" msgstr "Revisão %s desconhecida" -#: gitk:8688 #, tcl-format msgid "SHA1 id %s is not known" msgstr "Id SHA1 %s desconhecido" -#: gitk:8690 #, tcl-format msgid "Revision %s is not in the current view" msgstr "A revisão %s não está na vista atual" -#: gitk:8832 gitk:8847 msgid "Date" msgstr "Data" -#: gitk:8835 msgid "Children" msgstr "Descendentes" -#: gitk:8898 #, tcl-format msgid "Reset %s branch to here" msgstr "Redefinir ramo %s para este ponto" -#: gitk:8900 msgid "Detached head: can't reset" msgstr "Detached head: impossível redefinir" -#: gitk:9005 gitk:9011 msgid "Skipping merge commit " msgstr "Saltando revisão de mesclagem" -#: gitk:9020 gitk:9025 msgid "Error getting patch ID for " msgstr "Erro ao obter patch ID para" -#: gitk:9021 gitk:9026 msgid " - stopping\n" msgstr "- parando\n" -#: gitk:9031 gitk:9034 gitk:9042 gitk:9056 gitk:9065 msgid "Commit " msgstr "Revisão" -#: gitk:9035 msgid "" " is the same patch as\n" " " @@ -902,13 +695,11 @@ msgstr "" "é o mesmo patch que\n" " " -#: gitk:9043 msgid "" " differs from\n" " " msgstr "difere de" -#: gitk:9045 msgid "" "Diff of commits:\n" "\n" @@ -916,129 +707,99 @@ msgstr "" "Diferença de revisões:\n" "\n" -#: gitk:9057 gitk:9066 #, tcl-format msgid " has %s children - stopping\n" msgstr "possui %s descendentes - parando\n" -#: gitk:9085 #, tcl-format msgid "Error writing commit to file: %s" msgstr "Erro ao salvar revisão para o arquivo: %s" -#: gitk:9091 #, tcl-format msgid "Error diffing commits: %s" msgstr "Erro ao comparar revisões: %s" -#: gitk:9137 msgid "Top" msgstr "Início" -#: gitk:9138 msgid "From" msgstr "De" -#: gitk:9143 msgid "To" msgstr "Para" -#: gitk:9167 msgid "Generate patch" msgstr "Gerar patch" -#: gitk:9169 msgid "From:" msgstr "De:" -#: gitk:9178 msgid "To:" msgstr "Para:" -#: gitk:9187 msgid "Reverse" msgstr "Inverter" -#: gitk:9189 gitk:9385 msgid "Output file:" msgstr "Arquivo de saída:" -#: gitk:9195 msgid "Generate" msgstr "Gerar" -#: gitk:9233 msgid "Error creating patch:" msgstr "Erro ao criar patch:" -#: gitk:9256 gitk:9373 gitk:9430 msgid "ID:" msgstr "ID:" -#: gitk:9265 msgid "Tag name:" msgstr "Nome da etiqueta:" -#: gitk:9268 msgid "Tag message is optional" msgstr "A descrição da etiqueta é opcional" -#: gitk:9270 msgid "Tag message:" msgstr "Descrição da etiqueta" -#: gitk:9274 gitk:9439 msgid "Create" msgstr "Criar" -#: gitk:9292 msgid "No tag name specified" msgstr "Nome da etiqueta não indicado" -#: gitk:9296 #, tcl-format msgid "Tag \"%s\" already exists" msgstr "Etiqueta \"%s\" já existe" -#: gitk:9306 msgid "Error creating tag:" msgstr "Erro ao criar etiqueta:" -#: gitk:9382 msgid "Command:" msgstr "Comando:" -#: gitk:9390 msgid "Write" msgstr "Exportar" -#: gitk:9408 msgid "Error writing commit:" msgstr "Erro ao exportar revisão" -#: gitk:9435 msgid "Name:" msgstr "Nome:" -#: gitk:9458 msgid "Please specify a name for the new branch" msgstr "Indique um nome para o novo ramo" -#: gitk:9463 #, tcl-format msgid "Branch '%s' already exists. Overwrite?" msgstr "O ramo \"%s\" já existe. Sobrescrever?" -#: gitk:9530 #, tcl-format msgid "Commit %s is already included in branch %s -- really re-apply it?" msgstr "Revisão %s já inclusa no ramo %s -- você realmente deseja reaplicá-la?" -#: gitk:9535 msgid "Cherry-picking" msgstr "Cherry-picking" -#: gitk:9544 #, tcl-format msgid "" "Cherry-pick failed because of local changes to file '%s'.\n" @@ -1048,7 +809,6 @@ msgstr "" "Salve a uma revisão, redefina ou armazene (stash) suas mudanças e tente " "novamente." -#: gitk:9550 msgid "" "Cherry-pick failed because of merge conflict.\n" "Do you wish to run git citool to resolve it?" @@ -1056,21 +816,17 @@ msgstr "" "O cherry-pick falhou porque houve um conflito na mesclagem.\n" "Executar o 'git citool' para resolvê-lo?" -#: gitk:9566 gitk:9624 msgid "No changes committed" msgstr "Nenhuma revisão foi salva" -#: gitk:9593 #, fuzzy, tcl-format msgid "Commit %s is not included in branch %s -- really revert it?" msgstr "Revisão %s já inclusa no ramo %s -- você realmente deseja reaplicá-la?" -#: gitk:9598 #, fuzzy msgid "Reverting" msgstr "Redefinindo" -#: gitk:9606 #, fuzzy, tcl-format msgid "" "Revert failed because of local changes to the following files:%s Please " @@ -1080,7 +836,6 @@ msgstr "" "Salve a uma revisão, redefina ou armazene (stash) suas mudanças e tente " "novamente." -#: gitk:9610 #, fuzzy msgid "" "Revert failed because of merge conflict.\n" @@ -1089,28 +844,22 @@ msgstr "" "O cherry-pick falhou porque houve um conflito na mesclagem.\n" "Executar o 'git citool' para resolvê-lo?" -#: gitk:9653 msgid "Confirm reset" msgstr "Confirmar redefinição" -#: gitk:9655 #, tcl-format msgid "Reset branch %s to %s?" msgstr "Você realmente deseja redefinir o ramo %s para %s?" -#: gitk:9657 msgid "Reset type:" msgstr "Tipo de redefinição" -#: gitk:9660 msgid "Soft: Leave working tree and index untouched" msgstr "Soft: deixa a árvore de trabalho e o índice intocados" -#: gitk:9663 msgid "Mixed: Leave working tree untouched, reset index" msgstr "Misto: Deixa a árvore de trabalho intocada, redefine o índice" -#: gitk:9666 msgid "" "Hard: Reset working tree and index\n" "(discard ALL local changes)" @@ -1118,19 +867,15 @@ msgstr "" "Hard: Redefine a árvore de trabalho e o índice\n" "(descarta TODAS as mudanças locais)" -#: gitk:9683 msgid "Resetting" msgstr "Redefinindo" -#: gitk:9743 msgid "Checking out" msgstr "Abrindo" -#: gitk:9796 msgid "Cannot delete the currently checked-out branch" msgstr "Impossível excluir o ramo atualmente aberto" -#: gitk:9802 #, tcl-format msgid "" "The commits on branch %s aren't on any other branch.\n" @@ -1139,16 +884,13 @@ msgstr "" "As revisões do ramo \"%s\" não existem em nenhum outro ramo.\n" "Você realmente deseja excluir ramo \"%s\"?" -#: gitk:9833 #, tcl-format msgid "Tags and heads: %s" msgstr "Referências: %s" -#: gitk:9850 msgid "Filter" msgstr "Filtro" -#: gitk:10146 msgid "" "Error reading commit topology information; branch and preceding/following " "tag information will be incomplete." @@ -1156,221 +898,169 @@ msgstr "" "Erro ao ler a topologia das revisões; as informações dos ramos e etiquetas " "antecessoras/sucessoras estarão incompletas" -#: gitk:11123 msgid "Tag" msgstr "Etiqueta" -#: gitk:11127 msgid "Id" msgstr "Id" -#: gitk:11210 msgid "Gitk font chooser" msgstr "Selecionar fontes do Gitk" -#: gitk:11227 msgid "B" msgstr "B" -#: gitk:11230 msgid "I" msgstr "I" -#: gitk:11348 msgid "Commit list display options" msgstr "Opções da lista de revisões" -#: gitk:11351 msgid "Maximum graph width (lines)" msgstr "Largura máxima do grafo (linhas)" -#: gitk:11355 #, no-tcl-format msgid "Maximum graph width (% of pane)" msgstr "Largura máxima do grafo (% do painel)" -#: gitk:11358 msgid "Show local changes" msgstr "Exibir mudanças locais" -#: gitk:11361 #, fuzzy msgid "Auto-select SHA1 (length)" msgstr "Selecionar o SHA1 automaticamente" -#: gitk:11365 msgid "Hide remote refs" msgstr "Ocultar referências remotas" -#: gitk:11369 msgid "Diff display options" msgstr "Opções de exibição das alterações" -#: gitk:11371 msgid "Tab spacing" msgstr "Espaços por tabulação" -#: gitk:11374 #, fuzzy msgid "Display nearby tags/heads" msgstr "Exibir etiquetas próximas" -#: gitk:11377 msgid "Maximum # tags/heads to show" msgstr "" -#: gitk:11380 msgid "Limit diffs to listed paths" msgstr "Limitar diferenças aos caminhos listados" -#: gitk:11383 msgid "Support per-file encodings" msgstr "Usar codificações distintas por arquivo" -#: gitk:11389 gitk:11536 msgid "External diff tool" msgstr "Ferramenta 'diff' externa" -#: gitk:11390 msgid "Choose..." msgstr "Selecionar..." -#: gitk:11395 msgid "General options" msgstr "Opções gerais" -#: gitk:11398 msgid "Use themed widgets" msgstr "Usar temas para as janelas" -#: gitk:11400 msgid "(change requires restart)" msgstr "(exige reinicialização)" -#: gitk:11402 msgid "(currently unavailable)" msgstr "(atualmente indisponível)" -#: gitk:11413 msgid "Colors: press to choose" msgstr "Cores: clique para escolher" -#: gitk:11416 msgid "Interface" msgstr "Interface" -#: gitk:11417 msgid "interface" msgstr "interface" -#: gitk:11420 msgid "Background" msgstr "Segundo plano" -#: gitk:11421 gitk:11451 msgid "background" msgstr "segundo plano" -#: gitk:11424 msgid "Foreground" msgstr "Primeiro plano" -#: gitk:11425 msgid "foreground" msgstr "primeiro plano" -#: gitk:11428 msgid "Diff: old lines" msgstr "Diff: linhas excluídas" -#: gitk:11429 msgid "diff old lines" msgstr "linhas excluídas" -#: gitk:11433 msgid "Diff: new lines" msgstr "Diff: linhas adicionadas" -#: gitk:11434 msgid "diff new lines" msgstr "linhas adicionadas" -#: gitk:11438 msgid "Diff: hunk header" msgstr "Diff: cabeçalho do bloco" -#: gitk:11440 msgid "diff hunk header" msgstr "cabeçalho do bloco" -#: gitk:11444 msgid "Marked line bg" msgstr "2º plano da linha marcada" -#: gitk:11446 msgid "marked line background" msgstr "segundo plano da linha marcada" -#: gitk:11450 msgid "Select bg" msgstr "2º plano da seleção" -#: gitk:11459 msgid "Fonts: press to choose" msgstr "Fontes: clique para escolher" -#: gitk:11461 msgid "Main font" msgstr "Fonte principal" -#: gitk:11462 msgid "Diff display font" msgstr "Fonte da lista de mudanças" -#: gitk:11463 msgid "User interface font" msgstr "Fonte da interface" -#: gitk:11485 msgid "Gitk preferences" msgstr "Preferências do Gitk" -#: gitk:11494 #, fuzzy msgid "General" msgstr "Gerar" -#: gitk:11495 msgid "Colors" msgstr "" -#: gitk:11496 msgid "Fonts" msgstr "" -#: gitk:11546 #, tcl-format msgid "Gitk: choose color for %s" msgstr "Gitk: selecionar cor para %s" -#: gitk:12059 msgid "" "Sorry, gitk cannot run with this version of Tcl/Tk.\n" " Gitk requires at least Tcl/Tk 8.4." msgstr "" -#: gitk:12269 msgid "Cannot find a git repository here." msgstr "Não há nenhum repositório git aqui." -#: gitk:12316 #, tcl-format msgid "Ambiguous argument '%s': both revision and filename" msgstr "" "O argumento \"%s\" é ambíguo (especifica tanto uma revisão e um nome de " "arquivo)" -#: gitk:12328 msgid "Bad arguments to gitk:" msgstr "Argumentos incorretos para o gitk:" diff --git a/gitk-git/po/pt_pt.po b/gitk-git/po/pt_pt.po index f680ea86aa..66d3159d47 100644 --- a/gitk-git/po/pt_pt.po +++ b/gitk-git/po/pt_pt.po @@ -4,7 +4,7 @@ # Vasco Almeida <vascomalmeida@sapo.pt>, 2016. msgid "" msgstr "" -"Project-Id-Version: gitk\n" +"Project-Id-Version: Gitk\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2016-04-15 16:52+0000\n" "PO-Revision-Date: 2016-05-06 15:35+0000\n" @@ -17,33 +17,26 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Virtaal 0.7.1\n" -#: gitk:140 msgid "Couldn't get list of unmerged files:" msgstr "Não foi possível obter lista de ficheiros não integrados:" -#: gitk:212 gitk:2399 msgid "Color words" msgstr "Colorir palavras" -#: gitk:217 gitk:2399 gitk:8239 gitk:8272 msgid "Markup words" msgstr "Marcar palavras" -#: gitk:324 msgid "Error parsing revisions:" msgstr "Erro ao analisar revisões:" -#: gitk:380 msgid "Error executing --argscmd command:" msgstr "Erro ao executar o comando de --argscmd:" -#: gitk:393 msgid "No files selected: --merge specified but no files are unmerged." msgstr "" "Nenhum ficheiro selecionado: --merge especificado mas não há ficheiros por " "integrar." -#: gitk:396 msgid "" "No files selected: --merge specified but no unmerged files are within file " "limit." @@ -51,322 +44,240 @@ msgstr "" "Nenhum ficheiro selecionado: --merge especificado mas não há ficheiros por " "integrar ao nível de ficheiro." -#: gitk:418 gitk:566 msgid "Error executing git log:" msgstr "Erro ao executar git log:" -#: gitk:436 gitk:582 msgid "Reading" msgstr "A ler" -#: gitk:496 gitk:4544 msgid "Reading commits..." msgstr "A ler commits..." -#: gitk:499 gitk:1637 gitk:4547 msgid "No commits selected" msgstr "Nenhum commit selecionado" -#: gitk:1445 gitk:4064 gitk:12469 msgid "Command line" msgstr "Linha de comandos" -#: gitk:1511 msgid "Can't parse git log output:" msgstr "Não é possível analisar a saída de git log:" -#: gitk:1740 msgid "No commit information available" msgstr "Não há informação disponível sobre o commit" -#: gitk:1903 gitk:1932 gitk:4334 gitk:9702 gitk:11274 gitk:11554 msgid "OK" msgstr "OK" -#: gitk:1934 gitk:4336 gitk:9215 gitk:9294 gitk:9424 gitk:9473 gitk:9704 -#: gitk:11275 gitk:11555 msgid "Cancel" msgstr "Cancelar" -#: gitk:2083 msgid "&Update" msgstr "At&ualizar" -#: gitk:2084 msgid "&Reload" msgstr "&Recarregar" -#: gitk:2085 msgid "Reread re&ferences" msgstr "Reler re&ferências" -#: gitk:2086 msgid "&List references" msgstr "&Listar referências" -#: gitk:2088 msgid "Start git &gui" msgstr "Iniciar git &gui" -#: gitk:2090 msgid "&Quit" msgstr "&Sair" -#: gitk:2082 msgid "&File" msgstr "&Ficheiro" -#: gitk:2094 msgid "&Preferences" msgstr "&Preferências" -#: gitk:2093 msgid "&Edit" msgstr "&Editar" -#: gitk:2098 msgid "&New view..." msgstr "&Nova vista..." -#: gitk:2099 msgid "&Edit view..." msgstr "&Editar vista..." -#: gitk:2100 msgid "&Delete view" msgstr "Elimina&r vista" -#: gitk:2102 msgid "&All files" msgstr "&Todos os ficheiros" -#: gitk:2097 msgid "&View" msgstr "&Ver" -#: gitk:2107 gitk:2117 msgid "&About gitk" msgstr "&Sobre gitk" -#: gitk:2108 gitk:2122 msgid "&Key bindings" msgstr "&Atalhos" -#: gitk:2106 gitk:2121 msgid "&Help" msgstr "&Ajuda" -#: gitk:2199 gitk:8671 msgid "SHA1 ID:" msgstr "ID SHA1:" -#: gitk:2243 msgid "Row" msgstr "Linha" -#: gitk:2281 msgid "Find" msgstr "Procurar" -#: gitk:2309 msgid "commit" msgstr "commit" -#: gitk:2313 gitk:2315 gitk:4706 gitk:4729 gitk:4753 gitk:6774 gitk:6846 -#: gitk:6931 msgid "containing:" msgstr "contendo:" -#: gitk:2316 gitk:3545 gitk:3550 gitk:4782 msgid "touching paths:" msgstr "altera os caminhos:" -#: gitk:2317 gitk:4796 msgid "adding/removing string:" msgstr "adiciona/remove a cadeia:" -#: gitk:2318 gitk:4798 msgid "changing lines matching:" msgstr "altera linhas com:" -#: gitk:2327 gitk:2329 gitk:4785 msgid "Exact" msgstr "Exato" -#: gitk:2329 gitk:4873 gitk:6742 msgid "IgnCase" msgstr "IgnMaiúsculas" -#: gitk:2329 gitk:4755 gitk:4871 gitk:6738 msgid "Regexp" msgstr "Expr. regular" -#: gitk:2331 gitk:2332 gitk:4893 gitk:4923 gitk:4930 gitk:6867 gitk:6935 msgid "All fields" msgstr "Todos os campos" -#: gitk:2332 gitk:4890 gitk:4923 gitk:6805 msgid "Headline" msgstr "Cabeçalho" -#: gitk:2333 gitk:4890 gitk:6805 gitk:6935 gitk:7408 msgid "Comments" msgstr "Comentários" -#: gitk:2333 gitk:4890 gitk:4895 gitk:4930 gitk:6805 gitk:7343 gitk:8849 -#: gitk:8864 msgid "Author" msgstr "Autor" -#: gitk:2333 gitk:4890 gitk:6805 gitk:7345 msgid "Committer" msgstr "Committer" -#: gitk:2367 msgid "Search" msgstr "Pesquisar" -#: gitk:2375 msgid "Diff" msgstr "Diff" -#: gitk:2377 msgid "Old version" msgstr "Versão antiga" -#: gitk:2379 msgid "New version" msgstr "Versão nova" -#: gitk:2382 msgid "Lines of context" msgstr "Linhas de contexto" -#: gitk:2392 msgid "Ignore space change" msgstr "Ignorar espaços" -#: gitk:2396 gitk:2398 gitk:7978 gitk:8225 msgid "Line diff" msgstr "Diff de linha" -#: gitk:2463 msgid "Patch" msgstr "Patch" -#: gitk:2465 msgid "Tree" msgstr "Árvore" -#: gitk:2635 gitk:2656 msgid "Diff this -> selected" msgstr "Diff este -> seleção" -#: gitk:2636 gitk:2657 msgid "Diff selected -> this" msgstr "Diff seleção -> este" -#: gitk:2637 gitk:2658 msgid "Make patch" msgstr "Gerar patch" -#: gitk:2638 gitk:9273 msgid "Create tag" msgstr "Criar tag" -#: gitk:2639 msgid "Copy commit summary" msgstr "Copiar sumário do commit" -#: gitk:2640 gitk:9404 msgid "Write commit to file" msgstr "Escrever commit num ficheiro" -#: gitk:2641 gitk:9461 msgid "Create new branch" msgstr "Criar novo ramo" -#: gitk:2642 msgid "Cherry-pick this commit" msgstr "Efetuar cherry-pick deste commit" -#: gitk:2643 msgid "Reset HEAD branch to here" msgstr "Repor ramo HEAD para aqui" -#: gitk:2644 msgid "Mark this commit" msgstr "Marcar este commit" -#: gitk:2645 msgid "Return to mark" msgstr "Voltar à marca" -#: gitk:2646 msgid "Find descendant of this and mark" msgstr "Encontrar descendeste deste e da marca" -#: gitk:2647 msgid "Compare with marked commit" msgstr "Comparar com o commit marcado" -#: gitk:2648 gitk:2659 msgid "Diff this -> marked commit" msgstr "Diff este -> commit marcado" -#: gitk:2649 gitk:2660 msgid "Diff marked commit -> this" msgstr "Diff commit marcado -> este" -#: gitk:2650 msgid "Revert this commit" msgstr "Reverter este commit" -#: gitk:2666 msgid "Check out this branch" msgstr "Extrair este ramo" -#: gitk:2667 msgid "Remove this branch" msgstr "Remover este ramo" -#: gitk:2668 msgid "Copy branch name" msgstr "Copiar nome do ramo" -#: gitk:2675 msgid "Highlight this too" msgstr "Realçar este também" -#: gitk:2676 msgid "Highlight this only" msgstr "Realçar apenas este" -#: gitk:2677 msgid "External diff" msgstr "Diff externo" -#: gitk:2678 msgid "Blame parent commit" msgstr "Culpar commit pai" -#: gitk:2679 msgid "Copy path" msgstr "Copiar caminho" -#: gitk:2686 msgid "Show origin of this line" msgstr "Mostrar origem deste ficheiro" -#: gitk:2687 msgid "Run git gui blame on this line" msgstr "Executar git gui blame sobre esta linha" -#: gitk:3031 msgid "About gitk" msgstr "Sobre gitk" -#: gitk:3033 msgid "" "\n" "Gitk - a commit viewer for git\n" @@ -382,323 +293,249 @@ msgstr "" "\n" "Use e redistribua sob os termos da GNU General Public License" -#: gitk:3041 gitk:3108 gitk:9890 msgid "Close" msgstr "Fechar" -#: gitk:3062 msgid "Gitk key bindings" msgstr "Atalhos do gitk" -#: gitk:3065 msgid "Gitk key bindings:" msgstr "Atalhos do gitk:" -#: gitk:3067 #, tcl-format msgid "<%s-Q>\t\tQuit" msgstr "<%s-Q>\t\tSair" -#: gitk:3068 #, tcl-format msgid "<%s-W>\t\tClose window" msgstr "<%s-W>\t\tFechar janela" -#: gitk:3069 msgid "<Home>\t\tMove to first commit" msgstr "<Home>\t\tMover para o primeiro commit" -#: gitk:3070 msgid "<End>\t\tMove to last commit" msgstr "<End>\t\tMover para o último commit" -#: gitk:3071 msgid "<Up>, p, k\tMove up one commit" msgstr "<Cima>, p, k\tMover para o commit acima" -#: gitk:3072 msgid "<Down>, n, j\tMove down one commit" msgstr "<Baixo>, n, j\tMover para o commit abaixo" -#: gitk:3073 msgid "<Left>, z, h\tGo back in history list" msgstr "<Esquerda>, z, h\tRecuar no histórico" -#: gitk:3074 msgid "<Right>, x, l\tGo forward in history list" msgstr "<Direita>, x, l\tAvançar no histórico" -#: gitk:3075 #, tcl-format msgid "<%s-n>\tGo to n-th parent of current commit in history list" msgstr "<%s-n>\tIr para o n-ésimo pai do commit atual no histórico" -#: gitk:3076 msgid "<PageUp>\tMove up one page in commit list" msgstr "<PageUp>\tMover a lista de commits uma página para cima" -#: gitk:3077 msgid "<PageDown>\tMove down one page in commit list" msgstr "<PageDown>\tMover a lista de commits uma página para baixo" -#: gitk:3078 #, tcl-format msgid "<%s-Home>\tScroll to top of commit list" msgstr "<%s-Home>\tDeslocar para o topo da lista" -#: gitk:3079 #, tcl-format msgid "<%s-End>\tScroll to bottom of commit list" msgstr "<%s-End>\tDeslocar para o fim da lista" -#: gitk:3080 #, tcl-format msgid "<%s-Up>\tScroll commit list up one line" msgstr "<%s-Cima>\tDeslocar a lista de commits uma linha para cima" -#: gitk:3081 #, tcl-format msgid "<%s-Down>\tScroll commit list down one line" msgstr "<%s-Baixo>\tDeslocar a lista de commits uma linha para baixo" -#: gitk:3082 #, tcl-format msgid "<%s-PageUp>\tScroll commit list up one page" msgstr "<%s-PageUp>\tDeslocar a lista de commits uma página para cima" -#: gitk:3083 #, tcl-format msgid "<%s-PageDown>\tScroll commit list down one page" msgstr "<%s-PageDown>\tDeslocar a lista de commits uma página para baixo" -#: gitk:3084 msgid "<Shift-Up>\tFind backwards (upwards, later commits)" msgstr "<Shift-Cima>\tProcurar para trás (para cima, commits posteriores)" -#: gitk:3085 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)" msgstr "<Shift-Baixo>\tProcurar para a frente (para baixo, commits anteriores)" -#: gitk:3086 msgid "<Delete>, b\tScroll diff view up one page" msgstr "<Delete>, b\tDeslocar vista diff uma página para cima" -#: gitk:3087 msgid "<Backspace>\tScroll diff view up one page" msgstr "<Retrocesso>\tDeslocar vista diff uma página para cima" -#: gitk:3088 msgid "<Space>\t\tScroll diff view down one page" msgstr "<Espaço>\tDeslocar vista diff uma página para baixo" -#: gitk:3089 msgid "u\t\tScroll diff view up 18 lines" msgstr "u\t\tDeslocar vista diff 18 linhas para cima" -#: gitk:3090 msgid "d\t\tScroll diff view down 18 lines" msgstr "d\t\tDeslocar vista diff 18 linhas para baixo" -#: gitk:3091 #, tcl-format msgid "<%s-F>\t\tFind" msgstr "<%s-F>\t\tProcurar" -#: gitk:3092 #, tcl-format msgid "<%s-G>\t\tMove to next find hit" msgstr "<%s-G>\t\tMover para a ocorrência seguinte" -#: gitk:3093 msgid "<Return>\tMove to next find hit" msgstr "<Return>\tMover para a ocorrência seguinte" -#: gitk:3094 msgid "g\t\tGo to commit" msgstr "g\t\tIr para o commit" -#: gitk:3095 msgid "/\t\tFocus the search box" msgstr "/\t\tFocar a caixa de pesquisa" -#: gitk:3096 msgid "?\t\tMove to previous find hit" msgstr "?\t\tMover para a ocorrência anterior" -#: gitk:3097 msgid "f\t\tScroll diff view to next file" msgstr "f\t\tDeslocar vista diff para o ficheiro seguinte" -#: gitk:3098 #, tcl-format msgid "<%s-S>\t\tSearch for next hit in diff view" msgstr "<%s-S>\t\tProcurar pela ocorrência seguinte na vista diff" -#: gitk:3099 #, tcl-format msgid "<%s-R>\t\tSearch for previous hit in diff view" msgstr "<%s-R>\t\tProcurar pela ocorrência anterior na vista diff" -#: gitk:3100 #, tcl-format msgid "<%s-KP+>\tIncrease font size" msgstr "<%s-KP+>\tAumentar o tamanho da letra" -#: gitk:3101 #, tcl-format msgid "<%s-plus>\tIncrease font size" msgstr "<%s-mais>\tAumentar o tamanho da letra" -#: gitk:3102 #, tcl-format msgid "<%s-KP->\tDecrease font size" msgstr "<%s-KP->\tDiminuir o tamanho da letra" -#: gitk:3103 #, tcl-format msgid "<%s-minus>\tDecrease font size" msgstr "<%s-menos>\tDiminuir o tamanho da letra" -#: gitk:3104 msgid "<F5>\t\tUpdate" msgstr "<F5>\t\tAtualizar" -#: gitk:3569 gitk:3578 #, tcl-format msgid "Error creating temporary directory %s:" msgstr "Erro ao criar ficheiro temporário %s:" -#: gitk:3591 #, tcl-format msgid "Error getting \"%s\" from %s:" msgstr "Erro ao obter \"%s\" de %s:" -#: gitk:3654 msgid "command failed:" msgstr "o comando falhou:" -#: gitk:3803 msgid "No such commit" msgstr "Commit inexistente" -#: gitk:3817 msgid "git gui blame: command failed:" msgstr "git gui blame: o comando falhou:" -#: gitk:3848 #, tcl-format msgid "Couldn't read merge head: %s" msgstr "Não foi possível ler a cabeça de integração: %s" -#: gitk:3856 #, tcl-format msgid "Error reading index: %s" msgstr "Erro ao ler o índice: %s" -#: gitk:3881 #, tcl-format msgid "Couldn't start git blame: %s" msgstr "Não foi possível iniciar git blame: %s" -#: gitk:3884 gitk:6773 msgid "Searching" msgstr "A procurar" -#: gitk:3916 #, tcl-format msgid "Error running git blame: %s" msgstr "Erro ao executar git blame: %s" -#: gitk:3944 #, tcl-format msgid "That line comes from commit %s, which is not in this view" msgstr "Essa linha provém do commit %s, que não está nesta vista" -#: gitk:3958 msgid "External diff viewer failed:" msgstr "Visualizador diff externo falhou:" -#: gitk:4062 msgid "All files" msgstr "Todos os ficheiros" -#: gitk:4086 msgid "View" msgstr "Vista" -#: gitk:4089 msgid "Gitk view definition" msgstr "Definição de vistas do gitk" -#: gitk:4093 msgid "Remember this view" msgstr "Recordar esta vista" -#: gitk:4094 msgid "References (space separated list):" msgstr "Referências (lista separada por espaço):" -#: gitk:4095 msgid "Branches & tags:" msgstr "Ramos e tags:" -#: gitk:4096 msgid "All refs" msgstr "Todas as referências" -#: gitk:4097 msgid "All (local) branches" msgstr "Todos os ramos (locais)" -#: gitk:4098 msgid "All tags" msgstr "Todas as tags" -#: gitk:4099 msgid "All remote-tracking branches" msgstr "Todos os ramos remotos de monitorização" -#: gitk:4100 msgid "Commit Info (regular expressions):" msgstr "Informação Sobre o Commit (expressões regulares):" -#: gitk:4101 msgid "Author:" msgstr "Autor:" -#: gitk:4102 msgid "Committer:" msgstr "Committer:" -#: gitk:4103 msgid "Commit Message:" msgstr "Mensagem de Commit:" -#: gitk:4104 msgid "Matches all Commit Info criteria" msgstr "Corresponde a todos os critérios da Informação Sobre o Commit" -#: gitk:4105 msgid "Matches no Commit Info criteria" msgstr "Não corresponde a nenhum critério da Informação Sobre o Commit" -#: gitk:4106 msgid "Changes to Files:" msgstr "Alterações nos Ficheiros:" -#: gitk:4107 msgid "Fixed String" msgstr "Cadeia Fixa" -#: gitk:4108 msgid "Regular Expression" msgstr "Expressão Regular" -#: gitk:4109 msgid "Search string:" msgstr "Procurar pela cadeia:" -#: gitk:4110 msgid "" "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" @@ -706,201 +543,153 @@ msgstr "" "Datas de Commit (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" -#: gitk:4111 msgid "Since:" msgstr "Desde:" -#: gitk:4112 msgid "Until:" msgstr "Até:" -#: gitk:4113 msgid "Limit and/or skip a number of revisions (positive integer):" msgstr "Limitar e/ou ignorar um número de revisões (inteiro positivo):" -#: gitk:4114 msgid "Number to show:" msgstr "Número a mostrar:" -#: gitk:4115 msgid "Number to skip:" msgstr "Número a ignorar:" -#: gitk:4116 msgid "Miscellaneous options:" msgstr "Opções diversas:" -#: gitk:4117 msgid "Strictly sort by date" msgstr "Ordenar estritamente pela data" -#: gitk:4118 msgid "Mark branch sides" msgstr "Marcar lado dos ramos" -#: gitk:4119 msgid "Limit to first parent" msgstr "Restringir ao primeiro pai" -#: gitk:4120 msgid "Simple history" msgstr "Histórico simples" -#: gitk:4121 msgid "Additional arguments to git log:" msgstr "Argumentos adicionais ao git log:" -#: gitk:4122 msgid "Enter files and directories to include, one per line:" msgstr "Introduza ficheiros e diretórios para incluir, um por linha:" -#: gitk:4123 msgid "Command to generate more commits to include:" msgstr "Comando para gerar mais commits para incluir:" -#: gitk:4247 msgid "Gitk: edit view" msgstr "Gitk: editar vista" -#: gitk:4255 msgid "-- criteria for selecting revisions" msgstr "-- critério para selecionar revisões" -#: gitk:4260 msgid "View Name" msgstr "Nome da Vista" -#: gitk:4335 msgid "Apply (F5)" msgstr "Aplicar (F5)" -#: gitk:4373 msgid "Error in commit selection arguments:" msgstr "Erro nos argumentos de seleção de commits:" -#: gitk:4428 gitk:4481 gitk:4943 gitk:4957 gitk:6227 gitk:12410 gitk:12411 msgid "None" msgstr "Nenhum" -#: gitk:5040 gitk:5045 msgid "Descendant" msgstr "Descendente" -#: gitk:5041 msgid "Not descendant" msgstr "Não descendente" -#: gitk:5048 gitk:5053 msgid "Ancestor" msgstr "Antecessor" -#: gitk:5049 msgid "Not ancestor" msgstr "Não antecessor" -#: gitk:5343 msgid "Local changes checked in to index but not committed" msgstr "Alterações locais preparadas no índice mas não submetidas" -#: gitk:5379 msgid "Local uncommitted changes, not checked in to index" msgstr "Alterações locais não submetidas, não preparadas no índice" -#: gitk:7153 msgid "and many more" msgstr "e muitos mais" -#: gitk:7156 msgid "many" msgstr "muitos" -#: gitk:7347 msgid "Tags:" msgstr "Tags:" -#: gitk:7364 gitk:7370 gitk:8844 msgid "Parent" msgstr "Pai" -#: gitk:7375 msgid "Child" msgstr "Filho" -#: gitk:7384 msgid "Branch" msgstr "Ramo" -#: gitk:7387 msgid "Follows" msgstr "Sucede" -#: gitk:7390 msgid "Precedes" msgstr "Precede" -#: gitk:7985 #, tcl-format msgid "Error getting diffs: %s" msgstr "Erro ao obter diferenças: %s" -#: gitk:8669 msgid "Goto:" msgstr "Ir para:" -#: gitk:8690 #, tcl-format msgid "Short SHA1 id %s is ambiguous" msgstr "O id SHA1 abreviado %s é ambíguo" -#: gitk:8697 #, tcl-format msgid "Revision %s is not known" msgstr "A revisão %s não é conhecida" -#: gitk:8707 #, tcl-format msgid "SHA1 id %s is not known" msgstr "O id SHA1 %s não é conhecido" -#: gitk:8709 #, tcl-format msgid "Revision %s is not in the current view" msgstr "A revisão %s não se encontra na vista atual" -#: gitk:8851 gitk:8866 msgid "Date" msgstr "Data" -#: gitk:8854 msgid "Children" msgstr "Filhos" -#: gitk:8917 #, tcl-format msgid "Reset %s branch to here" msgstr "Repor o ramo %s para aqui" -#: gitk:8919 msgid "Detached head: can't reset" msgstr "Cabeça destacada: não é possível repor" -#: gitk:9024 gitk:9030 msgid "Skipping merge commit " msgstr "A ignorar commit de integração " -#: gitk:9039 gitk:9044 msgid "Error getting patch ID for " msgstr "Erro ao obter ID de patch de " -#: gitk:9040 gitk:9045 msgid " - stopping\n" msgstr " - a interromper\n" -#: gitk:9050 gitk:9053 gitk:9061 gitk:9075 gitk:9084 msgid "Commit " msgstr "Commit " -#: gitk:9054 msgid "" " is the same patch as\n" " " @@ -908,7 +697,6 @@ msgstr "" " é o mesmo patch que\n" " " -#: gitk:9062 msgid "" " differs from\n" " " @@ -916,7 +704,6 @@ msgstr "" " difere de\n" " " -#: gitk:9064 msgid "" "Diff of commits:\n" "\n" @@ -924,129 +711,99 @@ msgstr "" "Diferença dos commits:\n" "\n" -#: gitk:9076 gitk:9085 #, tcl-format msgid " has %s children - stopping\n" msgstr " tem %s filhos - a interromper\n" -#: gitk:9104 #, tcl-format msgid "Error writing commit to file: %s" msgstr "Erro ao escrever commit no ficheiro: %s" -#: gitk:9110 #, tcl-format msgid "Error diffing commits: %s" msgstr "Erro ao calcular as diferenças dos commits: %s" -#: gitk:9156 msgid "Top" msgstr "Topo" -#: gitk:9157 msgid "From" msgstr "De" -#: gitk:9162 msgid "To" msgstr "Para" -#: gitk:9186 msgid "Generate patch" msgstr "Gerar patch" -#: gitk:9188 msgid "From:" msgstr "De:" -#: gitk:9197 msgid "To:" msgstr "Para:" -#: gitk:9206 msgid "Reverse" msgstr "Reverter" -#: gitk:9208 gitk:9418 msgid "Output file:" msgstr "Ficheiro de saída:" -#: gitk:9214 msgid "Generate" msgstr "Gerar" -#: gitk:9252 msgid "Error creating patch:" msgstr "Erro ao criar patch:" -#: gitk:9275 gitk:9406 gitk:9463 msgid "ID:" msgstr "ID:" -#: gitk:9284 msgid "Tag name:" msgstr "Nome da tag:" -#: gitk:9287 msgid "Tag message is optional" msgstr "A mensagem da tag é opcional" -#: gitk:9289 msgid "Tag message:" msgstr "Mensagem da tag:" -#: gitk:9293 gitk:9472 msgid "Create" msgstr "Criar" -#: gitk:9311 msgid "No tag name specified" msgstr "Nenhum nome de tag especificado" -#: gitk:9315 #, tcl-format msgid "Tag \"%s\" already exists" msgstr "A tag \"%s\" já existe" -#: gitk:9325 msgid "Error creating tag:" msgstr "Erro ao criar tag:" -#: gitk:9415 msgid "Command:" msgstr "Comando:" -#: gitk:9423 msgid "Write" msgstr "Escrever" -#: gitk:9441 msgid "Error writing commit:" msgstr "Erro ao escrever commit:" -#: gitk:9468 msgid "Name:" msgstr "Nome:" -#: gitk:9491 msgid "Please specify a name for the new branch" msgstr "Especifique um nome para o novo ramo" -#: gitk:9496 #, tcl-format msgid "Branch '%s' already exists. Overwrite?" msgstr "O ramo '%s' já existe. Substituí-lo?" -#: gitk:9563 #, tcl-format msgid "Commit %s is already included in branch %s -- really re-apply it?" msgstr "O commit %s já está incluído no ramo %s -- reaplicá-lo mesmo assim?" -#: gitk:9568 msgid "Cherry-picking" msgstr "A efetuar cherry-pick" -#: gitk:9577 #, tcl-format msgid "" "Cherry-pick failed because of local changes to file '%s'.\n" @@ -1055,7 +812,6 @@ msgstr "" "Falha ao efetuar cherry-pick devido a alterações locais no ficheiro '%s'.\n" "Submeta, empilhe ou reponha as alterações e tente de novo." -#: gitk:9583 msgid "" "Cherry-pick failed because of merge conflict.\n" "Do you wish to run git citool to resolve it?" @@ -1063,20 +819,16 @@ msgstr "" "Falha ao efetuar cherry-pick devido a conflito de integração.\n" "Deseja executar git citool para resolvê-lo?" -#: gitk:9599 gitk:9657 msgid "No changes committed" msgstr "Não foi submetida nenhum alteração" -#: gitk:9626 #, tcl-format msgid "Commit %s is not included in branch %s -- really revert it?" msgstr "O commit %s não está incluído no ramo %s -- revertê-lo mesmo assim?" -#: gitk:9631 msgid "Reverting" msgstr "A reverter" -#: gitk:9639 #, tcl-format msgid "" "Revert failed because of local changes to the following files:%s Please " @@ -1085,7 +837,6 @@ msgstr "" "Falha ao reverter devido a alterações locais nos seguintes ficheiros:%s " "Submeta, empilhe ou reponha as alterações e tente de novo." -#: gitk:9643 msgid "" "Revert failed because of merge conflict.\n" " Do you wish to run git citool to resolve it?" @@ -1093,28 +844,22 @@ msgstr "" "Falha ao reverter devido a conflito de integração.\n" "Deseja executar git citool para resolvê-lo?" -#: gitk:9686 msgid "Confirm reset" msgstr "Confirmar reposição" -#: gitk:9688 #, tcl-format msgid "Reset branch %s to %s?" msgstr "Repor o ramo %s para %s?" -#: gitk:9690 msgid "Reset type:" msgstr "Tipo de reposição:" -#: gitk:9693 msgid "Soft: Leave working tree and index untouched" msgstr "Suave: Deixar a árvore de trabalho e o índice intactos" -#: gitk:9696 msgid "Mixed: Leave working tree untouched, reset index" msgstr "Misto: Deixar a árvore de trabalho intacta, repor índice" -#: gitk:9699 msgid "" "Hard: Reset working tree and index\n" "(discard ALL local changes)" @@ -1122,19 +867,15 @@ msgstr "" "Forte: Repor árvore de trabalho e índice\n" "(descartar TODAS as alterações locais)" -#: gitk:9716 msgid "Resetting" msgstr "A repor" -#: gitk:9776 msgid "Checking out" msgstr "A extrair" -#: gitk:9829 msgid "Cannot delete the currently checked-out branch" msgstr "Não é possível eliminar o ramo atual extraído" -#: gitk:9835 #, tcl-format msgid "" "The commits on branch %s aren't on any other branch.\n" @@ -1143,16 +884,13 @@ msgstr "" "Os commits no ramo %s não estão presentes em mais nenhum ramo.\n" "Eliminar o ramo %s mesmo assim?" -#: gitk:9866 #, tcl-format msgid "Tags and heads: %s" msgstr "Tags e cabeças: %s" -#: gitk:9883 msgid "Filter" msgstr "Filtrar" -#: gitk:10179 msgid "" "Error reading commit topology information; branch and preceding/following " "tag information will be incomplete." @@ -1160,201 +898,152 @@ msgstr "" "Erro ao ler informação de topologia do commit; a informação do ramo e da tag " "precedente/seguinte ficará incompleta." -#: gitk:11156 msgid "Tag" msgstr "Tag" -#: gitk:11160 msgid "Id" msgstr "Id" -#: gitk:11243 msgid "Gitk font chooser" msgstr "Escolha de tipo de letra do gitk" -#: gitk:11260 msgid "B" msgstr "B" -#: gitk:11263 msgid "I" msgstr "I" -#: gitk:11381 msgid "Commit list display options" msgstr "Opções de visualização da lista de commits" -#: gitk:11384 msgid "Maximum graph width (lines)" msgstr "Largura máxima do gráfico (linhas)" -#: gitk:11388 #, no-tcl-format msgid "Maximum graph width (% of pane)" msgstr "Largura máxima do gráfico (% do painel)" -#: gitk:11391 msgid "Show local changes" msgstr "Mostrar alterações locais" -#: gitk:11394 msgid "Auto-select SHA1 (length)" msgstr "Selecionar automaticamente SHA1 (largura)" -#: gitk:11398 msgid "Hide remote refs" msgstr "Ocultar referências remotas" -#: gitk:11402 msgid "Diff display options" msgstr "Opções de visualização de diferenças" -#: gitk:11404 msgid "Tab spacing" msgstr "Espaçamento da tabulação" -#: gitk:11407 msgid "Display nearby tags/heads" msgstr "Mostrar tags/cabeças próximas" -#: gitk:11410 msgid "Maximum # tags/heads to show" msgstr "Nº máximo de tags/cabeças a mostrar" -#: gitk:11413 msgid "Limit diffs to listed paths" msgstr "Limitar diferenças aos caminhos listados" -#: gitk:11416 msgid "Support per-file encodings" msgstr "Suportar codificação por cada ficheiro" -#: gitk:11422 gitk:11569 msgid "External diff tool" msgstr "Ferramenta diff externa" -#: gitk:11423 msgid "Choose..." msgstr "Escolher..." -#: gitk:11428 msgid "General options" msgstr "Opções gerais" -#: gitk:11431 msgid "Use themed widgets" msgstr "Usar widgets com estilo" -#: gitk:11433 msgid "(change requires restart)" msgstr "(alteração exige reiniciar)" -#: gitk:11435 msgid "(currently unavailable)" msgstr "(não disponível de momento)" -#: gitk:11446 msgid "Colors: press to choose" msgstr "Cores: pressione para escolher" -#: gitk:11449 msgid "Interface" msgstr "Interface" -#: gitk:11450 msgid "interface" msgstr "interface" -#: gitk:11453 msgid "Background" msgstr "Fundo" -#: gitk:11454 gitk:11484 msgid "background" msgstr "fundo" -#: gitk:11457 msgid "Foreground" msgstr "Primeiro plano" -#: gitk:11458 msgid "foreground" msgstr "primeiro plano" -#: gitk:11461 msgid "Diff: old lines" msgstr "Diff: linhas antigas" -#: gitk:11462 msgid "diff old lines" msgstr "diff linhas antigas" -#: gitk:11466 msgid "Diff: new lines" msgstr "Diff: linhas novas" -#: gitk:11467 msgid "diff new lines" msgstr "diff linhas novas" -#: gitk:11471 msgid "Diff: hunk header" msgstr "Diff: cabeçalho do excerto" -#: gitk:11473 msgid "diff hunk header" msgstr "diff cabeçalho do excerto" -#: gitk:11477 msgid "Marked line bg" msgstr "Fundo da linha marcada" -#: gitk:11479 msgid "marked line background" msgstr "fundo da linha marcada" -#: gitk:11483 msgid "Select bg" msgstr "Selecionar fundo" -#: gitk:11492 msgid "Fonts: press to choose" msgstr "Tipo de letra: pressione para escolher" -#: gitk:11494 msgid "Main font" msgstr "Tipo de letra principal" -#: gitk:11495 msgid "Diff display font" msgstr "Tipo de letra ao mostrar diferenças" -#: gitk:11496 msgid "User interface font" msgstr "Tipo de letra da interface de utilizador" -#: gitk:11518 msgid "Gitk preferences" msgstr "Preferências do gitk" -#: gitk:11527 msgid "General" msgstr "Geral" -#: gitk:11528 msgid "Colors" msgstr "Cores" -#: gitk:11529 msgid "Fonts" msgstr "Tipos de letra" -#: gitk:11579 #, tcl-format msgid "Gitk: choose color for %s" msgstr "Gitk: escolher cor de %s" -#: gitk:12092 msgid "" "Sorry, gitk cannot run with this version of Tcl/Tk.\n" " Gitk requires at least Tcl/Tk 8.4." @@ -1362,15 +1051,12 @@ msgstr "" "Não é possível executar o gitk com esta versão do Tcl/Tk.\n" "O gitk requer pelo menos Tcl/Tk 8.4." -#: gitk:12302 msgid "Cannot find a git repository here." msgstr "Não foi encontrado nenhum repositório git aqui." -#: gitk:12349 #, tcl-format msgid "Ambiguous argument '%s': both revision and filename" msgstr "Argumento '%s' ambíguo: pode ser uma revisão ou um ficheiro" -#: gitk:12361 msgid "Bad arguments to gitk:" msgstr "Argumentos do gitk incorretos:" diff --git a/gitk-git/po/ru.po b/gitk-git/po/ru.po index 9b08c263ea..e102fb7200 100644 --- a/gitk-git/po/ru.po +++ b/gitk-git/po/ru.po @@ -8,368 +8,285 @@ # Skip <bsvskip@rambler.ru>, 2011 msgid "" msgstr "" -"Project-Id-Version: Git Russian Localization Project\n" +"Project-Id-Version: Gitk Russian Localization Project\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2016-12-15 00:18+0200\n" "PO-Revision-Date: 2016-12-14 22:23+0000\n" "Last-Translator: Dimitriy Ryazantcev <DJm00n@mail.ru>\n" -"Language-Team: Russian (http://www.transifex.com/djm00n/git-po-ru/language/ru/)\n" +"Language-Team: Russian (http://www.transifex.com/djm00n/git-po-ru/language/" +"ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: ru\n" -"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || " +"(n%100>=11 && n%100<=14)? 2 : 3);\n" -#: gitk:140 msgid "Couldn't get list of unmerged files:" msgstr "Невозможно получить список файлов незавершённой операции слияния:" -#: gitk:212 gitk:2403 msgid "Color words" msgstr "Цветные слова" -#: gitk:217 gitk:2403 gitk:8249 gitk:8282 msgid "Markup words" msgstr "Помеченые слова" -#: gitk:324 msgid "Error parsing revisions:" msgstr "Ошибка при разборе редакции:" -#: gitk:380 msgid "Error executing --argscmd command:" msgstr "Ошибка выполнения команды заданной --argscmd:" -#: gitk:393 msgid "No files selected: --merge specified but no files are unmerged." -msgstr "Файлы не выбраны: указан --merge, но не было найдено ни одного файла где эта операция должна быть завершена." +msgstr "" +"Файлы не выбраны: указан --merge, но не было найдено ни одного файла где эта " +"операция должна быть завершена." -#: gitk:396 msgid "" "No files selected: --merge specified but no unmerged files are within file " "limit." -msgstr "Файлы не выбраны: указан --merge, но в рамках указанного ограничения на имена файлов нет ни одного где эта операция должна быть завершена." +msgstr "" +"Файлы не выбраны: указан --merge, но в рамках указанного ограничения на " +"имена файлов нет ни одного где эта операция должна быть завершена." -#: gitk:418 gitk:566 msgid "Error executing git log:" msgstr "Ошибка запуска git log:" -#: gitk:436 gitk:582 msgid "Reading" msgstr "Чтение" -#: gitk:496 gitk:4549 msgid "Reading commits..." msgstr "Чтение коммитов..." -#: gitk:499 gitk:1641 gitk:4552 msgid "No commits selected" msgstr "Ничего не выбрано" -#: gitk:1449 gitk:4069 gitk:12583 msgid "Command line" msgstr "Командная строка" -#: gitk:1515 msgid "Can't parse git log output:" msgstr "Ошибка обработки вывода команды git log:" -#: gitk:1744 msgid "No commit information available" msgstr "Нет информации о коммите" -#: gitk:1907 gitk:1936 gitk:4339 gitk:9789 gitk:11388 gitk:11668 msgid "OK" msgstr "Ok" -#: gitk:1938 gitk:4341 gitk:9225 gitk:9304 gitk:9434 gitk:9520 gitk:9791 -#: gitk:11389 gitk:11669 msgid "Cancel" msgstr "Отмена" -#: gitk:2087 msgid "&Update" msgstr "Обновить" -#: gitk:2088 msgid "&Reload" msgstr "Перечитать" -#: gitk:2089 msgid "Reread re&ferences" msgstr "Обновить список ссылок" -#: gitk:2090 msgid "&List references" msgstr "Список ссылок" -#: gitk:2092 msgid "Start git &gui" msgstr "Запустить git gui" -#: gitk:2094 msgid "&Quit" msgstr "Завершить" -#: gitk:2086 msgid "&File" msgstr "Файл" -#: gitk:2098 msgid "&Preferences" msgstr "Настройки" -#: gitk:2097 msgid "&Edit" msgstr "Редактировать" -#: gitk:2102 msgid "&New view..." msgstr "Новое представление..." -#: gitk:2103 msgid "&Edit view..." msgstr "Редактировать представление..." -#: gitk:2104 msgid "&Delete view" msgstr "Удалить представление" -#: gitk:2106 msgid "&All files" msgstr "Все файлы" -#: gitk:2101 msgid "&View" msgstr "Представление" -#: gitk:2111 gitk:2121 msgid "&About gitk" msgstr "О gitk" -#: gitk:2112 gitk:2126 msgid "&Key bindings" msgstr "Назначения клавиатуры" -#: gitk:2110 gitk:2125 msgid "&Help" msgstr "Подсказка" -#: gitk:2203 gitk:8681 msgid "SHA1 ID:" msgstr "SHA1 ID:" -#: gitk:2247 msgid "Row" msgstr "Строка" -#: gitk:2285 msgid "Find" msgstr "Поиск" -#: gitk:2313 msgid "commit" msgstr "коммит" -#: gitk:2317 gitk:2319 gitk:4711 gitk:4734 gitk:4758 gitk:6779 gitk:6851 -#: gitk:6936 msgid "containing:" msgstr "содержащее:" -#: gitk:2320 gitk:3550 gitk:3555 gitk:4787 msgid "touching paths:" msgstr "касательно файлов:" -#: gitk:2321 gitk:4801 msgid "adding/removing string:" msgstr "добавив/удалив строку:" -#: gitk:2322 gitk:4803 msgid "changing lines matching:" msgstr "изменяя совпадающие строки:" -#: gitk:2331 gitk:2333 gitk:4790 msgid "Exact" msgstr "Точно" -#: gitk:2333 gitk:4878 gitk:6747 msgid "IgnCase" msgstr "Игнорировать большие/маленькие" -#: gitk:2333 gitk:4760 gitk:4876 gitk:6743 msgid "Regexp" msgstr "Регулярные выражения" -#: gitk:2335 gitk:2336 gitk:4898 gitk:4928 gitk:4935 gitk:6872 gitk:6940 msgid "All fields" msgstr "Во всех полях" -#: gitk:2336 gitk:4895 gitk:4928 gitk:6810 msgid "Headline" msgstr "Заголовок" -#: gitk:2337 gitk:4895 gitk:6810 gitk:6940 gitk:7413 msgid "Comments" msgstr "Комментарии" -#: gitk:2337 gitk:4895 gitk:4900 gitk:4935 gitk:6810 gitk:7348 gitk:8859 -#: gitk:8874 msgid "Author" msgstr "Автор" -#: gitk:2337 gitk:4895 gitk:6810 gitk:7350 msgid "Committer" msgstr "Коммитер" -#: gitk:2371 msgid "Search" msgstr "Найти" -#: gitk:2379 msgid "Diff" msgstr "Сравнить" -#: gitk:2381 msgid "Old version" msgstr "Старая версия" -#: gitk:2383 msgid "New version" msgstr "Новая версия" -#: gitk:2386 msgid "Lines of context" msgstr "Строк контекста" -#: gitk:2396 msgid "Ignore space change" msgstr "Игнорировать пробелы" -#: gitk:2400 gitk:2402 gitk:7983 gitk:8235 msgid "Line diff" msgstr "Изменения строк" -#: gitk:2467 msgid "Patch" msgstr "Патч" -#: gitk:2469 msgid "Tree" msgstr "Файлы" -#: gitk:2639 gitk:2660 msgid "Diff this -> selected" msgstr "Сравнить этот коммит с выделенным" -#: gitk:2640 gitk:2661 msgid "Diff selected -> this" msgstr "Сравнить выделенный с этим коммитом" -#: gitk:2641 gitk:2662 msgid "Make patch" msgstr "Создать патч" -#: gitk:2642 gitk:9283 msgid "Create tag" msgstr "Создать метку" -#: gitk:2643 msgid "Copy commit summary" msgstr "Копировать информацию о коммите" -#: gitk:2644 gitk:9414 msgid "Write commit to file" msgstr "Сохранить коммит в файл" -#: gitk:2645 msgid "Create new branch" msgstr "Создать ветку" -#: gitk:2646 msgid "Cherry-pick this commit" msgstr "Копировать этот коммит в текущую ветку" -#: gitk:2647 msgid "Reset HEAD branch to here" msgstr "Установить HEAD на этот коммит" -#: gitk:2648 msgid "Mark this commit" msgstr "Пометить этот коммит" -#: gitk:2649 msgid "Return to mark" msgstr "Вернуться на пометку" -#: gitk:2650 msgid "Find descendant of this and mark" msgstr "Найти и пометить потомка этого коммита" -#: gitk:2651 msgid "Compare with marked commit" msgstr "Сравнить с помеченным коммитом" -#: gitk:2652 gitk:2663 msgid "Diff this -> marked commit" msgstr "Сравнить выделенное с помеченным коммитом" -#: gitk:2653 gitk:2664 msgid "Diff marked commit -> this" msgstr "Сравнить помеченный с этим коммитом" -#: gitk:2654 msgid "Revert this commit" msgstr "Обратить изменения этого коммита" -#: gitk:2670 msgid "Check out this branch" msgstr "Перейти на эту ветку" -#: gitk:2671 msgid "Rename this branch" msgstr "Переименовать эту ветку" -#: gitk:2672 msgid "Remove this branch" msgstr "Удалить эту ветку" -#: gitk:2673 msgid "Copy branch name" msgstr "Копировать имя ветки" -#: gitk:2680 msgid "Highlight this too" msgstr "Подсветить этот тоже" -#: gitk:2681 msgid "Highlight this only" msgstr "Подсветить только этот" -#: gitk:2682 msgid "External diff" msgstr "Программа сравнения" -#: gitk:2683 msgid "Blame parent commit" msgstr "Авторы изменений родительского коммита" -#: gitk:2684 msgid "Copy path" msgstr "Копировать путь" -#: gitk:2691 msgid "Show origin of this line" msgstr "Показать источник этой строки" -#: gitk:2692 msgid "Run git gui blame on this line" msgstr "Запустить git gui blame для этой строки" -#: gitk:3036 msgid "About gitk" msgstr "О gitk" -#: gitk:3038 msgid "" "\n" "Gitk - a commit viewer for git\n" @@ -377,995 +294,797 @@ msgid "" "Copyright © 2005-2016 Paul Mackerras\n" "\n" "Use and redistribute under the terms of the GNU General Public License" -msgstr "\nGitk — программа просмотра истории репозиториев git\n\n© 2005-2016 Paul Mackerras\n\nИспользование и распространение согласно условиям GNU General Public License" +msgstr "" +"\n" +"Gitk — программа просмотра истории репозиториев git\n" +"\n" +"© 2005-2016 Paul Mackerras\n" +"\n" +"Использование и распространение согласно условиям GNU General Public License" -#: gitk:3046 gitk:3113 gitk:10004 msgid "Close" msgstr "Закрыть" -#: gitk:3067 msgid "Gitk key bindings" msgstr "Назначения клавиатуры в Gitk" -#: gitk:3070 msgid "Gitk key bindings:" msgstr "Назначения клавиатуры в Gitk:" -#: gitk:3072 #, tcl-format msgid "<%s-Q>\t\tQuit" msgstr "<%s-Q>\t\tЗавершить" -#: gitk:3073 #, tcl-format msgid "<%s-W>\t\tClose window" msgstr "<%s-W>\t\tЗакрыть окно" -#: gitk:3074 msgid "<Home>\t\tMove to first commit" msgstr "<Home>\t\tПерейти к первому коммиту" -#: gitk:3075 msgid "<End>\t\tMove to last commit" msgstr "<End>\t\tПерейти к последнему коммиту" -#: gitk:3076 msgid "<Up>, p, k\tMove up one commit" msgstr "<Up>, p, k\tПерейти на один коммит вверх" -#: gitk:3077 msgid "<Down>, n, j\tMove down one commit" msgstr "<Down>, n, j\tПерейти на один коммит вниз" -#: gitk:3078 msgid "<Left>, z, h\tGo back in history list" msgstr "<Left>, z, h\tПоказать ранее посещённое состояние" -#: gitk:3079 msgid "<Right>, x, l\tGo forward in history list" msgstr "<Right>, x, l\tПоказать следующий посещённый коммит" -#: gitk:3080 #, tcl-format msgid "<%s-n>\tGo to n-th parent of current commit in history list" msgstr "<%s-n>\tПерейти на n родителя от текущего коммита" -#: gitk:3081 msgid "<PageUp>\tMove up one page in commit list" msgstr "<PageUp>\tПерейти на страницу выше в списке коммитов" -#: gitk:3082 msgid "<PageDown>\tMove down one page in commit list" msgstr "<PageDown>\tПерейти на страницу ниже в списке коммитов" -#: gitk:3083 #, tcl-format msgid "<%s-Home>\tScroll to top of commit list" msgstr "<%s-Home>\tПерейти на начало списка коммитов" -#: gitk:3084 #, tcl-format msgid "<%s-End>\tScroll to bottom of commit list" msgstr "<%s-End>\tПерейти на конец списка коммитов" -#: gitk:3085 #, tcl-format msgid "<%s-Up>\tScroll commit list up one line" msgstr "<%s-Up>\tПровернуть список коммитов вверх" -#: gitk:3086 #, tcl-format msgid "<%s-Down>\tScroll commit list down one line" msgstr "<%s-Down>\tПровернуть список коммитов вниз" -#: gitk:3087 #, tcl-format msgid "<%s-PageUp>\tScroll commit list up one page" msgstr "<%s-PageUp>\tПровернуть список коммитов на страницу вверх" -#: gitk:3088 #, tcl-format msgid "<%s-PageDown>\tScroll commit list down one page" msgstr "<%s-PageDown>\tПровернуть список коммитов на страницу вниз" -#: gitk:3089 msgid "<Shift-Up>\tFind backwards (upwards, later commits)" msgstr "<Shift-Up>\tПоиск в обратном порядке (вверх, среди новых коммитов)" -#: gitk:3090 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)" msgstr "<Shift-Down>\tПоиск (вниз, среди старых коммитов)" -#: gitk:3091 msgid "<Delete>, b\tScroll diff view up one page" msgstr "<Delete>, b\tПрокрутить список изменений на страницу выше" -#: gitk:3092 msgid "<Backspace>\tScroll diff view up one page" msgstr "<Backspace>\tПрокрутить список изменений на страницу выше" -#: gitk:3093 msgid "<Space>\t\tScroll diff view down one page" msgstr "<Leertaste>\t\tПрокрутить список изменений на страницу ниже" -#: gitk:3094 msgid "u\t\tScroll diff view up 18 lines" msgstr "u\t\tПрокрутить список изменений на 18 строк вверх" -#: gitk:3095 msgid "d\t\tScroll diff view down 18 lines" msgstr "d\t\tПрокрутить список изменений на 18 строк вниз" -#: gitk:3096 #, tcl-format msgid "<%s-F>\t\tFind" msgstr "<%s-F>\t\tПоиск" -#: gitk:3097 #, tcl-format msgid "<%s-G>\t\tMove to next find hit" msgstr "<%s-G>\t\tПерейти к следующему найденному коммиту" -#: gitk:3098 msgid "<Return>\tMove to next find hit" msgstr "<Return>\tПерейти к следующему найденному коммиту" -#: gitk:3099 msgid "g\t\tGo to commit" msgstr "g\t\tПерейти на коммит" -#: gitk:3100 msgid "/\t\tFocus the search box" msgstr "/\t\tПерейти к полю поиска" -#: gitk:3101 msgid "?\t\tMove to previous find hit" msgstr "?\t\tПерейти к предыдущему найденному коммиту" -#: gitk:3102 msgid "f\t\tScroll diff view to next file" msgstr "f\t\tПрокрутить список изменений к следующему файлу" -#: gitk:3103 #, tcl-format msgid "<%s-S>\t\tSearch for next hit in diff view" msgstr "<%s-S>\t\tПродолжить поиск в списке изменений" -#: gitk:3104 #, tcl-format msgid "<%s-R>\t\tSearch for previous hit in diff view" msgstr "<%s-R>\t\tПерейти к предыдущему найденному тексту в списке изменений" -#: gitk:3105 #, tcl-format msgid "<%s-KP+>\tIncrease font size" msgstr "<%s-KP+>\tУвеличить размер шрифта" -#: gitk:3106 #, tcl-format msgid "<%s-plus>\tIncrease font size" msgstr "<%s-plus>\tУвеличить размер шрифта" -#: gitk:3107 #, tcl-format msgid "<%s-KP->\tDecrease font size" msgstr "<%s-KP->\tУменьшить размер шрифта" -#: gitk:3108 #, tcl-format msgid "<%s-minus>\tDecrease font size" msgstr "<%s-minus>\tУменьшить размер шрифта" -#: gitk:3109 msgid "<F5>\t\tUpdate" msgstr "<F5>\t\tОбновить" -#: gitk:3574 gitk:3583 #, tcl-format msgid "Error creating temporary directory %s:" msgstr "Ошибка создания временного каталога %s:" -#: gitk:3596 #, tcl-format msgid "Error getting \"%s\" from %s:" msgstr "Ошибка получения «%s» из %s:" -#: gitk:3659 msgid "command failed:" msgstr "ошибка выполнения команды:" -#: gitk:3808 msgid "No such commit" msgstr "Коммит не найден" -#: gitk:3822 msgid "git gui blame: command failed:" msgstr "git gui blame: ошибка выполнения команды:" -#: gitk:3853 #, tcl-format msgid "Couldn't read merge head: %s" msgstr "Ошибка чтения MERGE_HEAD: %s" -#: gitk:3861 #, tcl-format msgid "Error reading index: %s" msgstr "Ошибка чтения индекса: %s" -#: gitk:3886 #, tcl-format msgid "Couldn't start git blame: %s" msgstr "Ошибка запуска git blame: %s" -#: gitk:3889 gitk:6778 msgid "Searching" msgstr "Поиск" -#: gitk:3921 #, tcl-format msgid "Error running git blame: %s" msgstr "Ошибка выполнения git blame: %s" -#: gitk:3949 #, tcl-format msgid "That line comes from commit %s, which is not in this view" -msgstr "Эта строка принадлежит коммиту %s, который не показан в этом представлении" +msgstr "" +"Эта строка принадлежит коммиту %s, который не показан в этом представлении" -#: gitk:3963 msgid "External diff viewer failed:" msgstr "Ошибка выполнения программы сравнения:" -#: gitk:4067 msgid "All files" msgstr "Все файлы" -#: gitk:4091 msgid "View" msgstr "Представление" -#: gitk:4094 msgid "Gitk view definition" msgstr "Gitk определение представлений" -#: gitk:4098 msgid "Remember this view" msgstr "Запомнить представление" -#: gitk:4099 msgid "References (space separated list):" msgstr "Ссылки (разделённые пробелом):" -#: gitk:4100 msgid "Branches & tags:" msgstr "Ветки и метки" -#: gitk:4101 msgid "All refs" msgstr "Все ссылки" -#: gitk:4102 msgid "All (local) branches" msgstr "Все (локальные) ветки" -#: gitk:4103 msgid "All tags" msgstr "Все метки" -#: gitk:4104 msgid "All remote-tracking branches" msgstr "Все внешние отслеживаемые ветки" -#: gitk:4105 msgid "Commit Info (regular expressions):" msgstr "Информация о коммите (регулярные выражения):" -#: gitk:4106 msgid "Author:" msgstr "Автор:" -#: gitk:4107 msgid "Committer:" msgstr "Коммитер:" -#: gitk:4108 msgid "Commit Message:" msgstr "Сообщение коммита:" -#: gitk:4109 msgid "Matches all Commit Info criteria" msgstr "Совпадает со всеми условиями информации о коммите" -#: gitk:4110 msgid "Matches no Commit Info criteria" msgstr "Не совпадает с условиями информации о коммите" -#: gitk:4111 msgid "Changes to Files:" msgstr "Изменения файлов:" -#: gitk:4112 msgid "Fixed String" msgstr "Обычная строка" -#: gitk:4113 msgid "Regular Expression" msgstr "Регулярное выражение:" -#: gitk:4114 msgid "Search string:" msgstr "Строка для поиска:" -#: gitk:4115 msgid "" "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" -msgstr "Даты коммита («2 недели назад», «2009-03-17 15:27:38», «17 марта 2009 15:27:38»):" +msgstr "" +"Даты коммита («2 недели назад», «2009-03-17 15:27:38», «17 марта 2009 " +"15:27:38»):" -#: gitk:4116 msgid "Since:" msgstr "С даты:" -#: gitk:4117 msgid "Until:" msgstr "По дату:" -#: gitk:4118 msgid "Limit and/or skip a number of revisions (positive integer):" msgstr "Ограничить и/или пропустить количество редакций (положительное число):" -#: gitk:4119 msgid "Number to show:" msgstr "Показать количество:" -#: gitk:4120 msgid "Number to skip:" msgstr "Пропустить количество:" -#: gitk:4121 msgid "Miscellaneous options:" msgstr "Различные опции:" -#: gitk:4122 msgid "Strictly sort by date" msgstr "Строгая сортировка по дате" -#: gitk:4123 msgid "Mark branch sides" msgstr "Отметить стороны веток" -#: gitk:4124 msgid "Limit to first parent" msgstr "Ограничить первым предком" -#: gitk:4125 msgid "Simple history" msgstr "Упрощенная история" -#: gitk:4126 msgid "Additional arguments to git log:" msgstr "Дополнительные аргументы для git log:" -#: gitk:4127 msgid "Enter files and directories to include, one per line:" msgstr "Файлы и каталоги для ограничения истории, по одному на строку:" -#: gitk:4128 msgid "Command to generate more commits to include:" msgstr "Дополнительная команда для списка коммитов:" -#: gitk:4252 msgid "Gitk: edit view" msgstr "Gitk: изменить представление" -#: gitk:4260 msgid "-- criteria for selecting revisions" msgstr "— критерий поиска редакций" -#: gitk:4265 msgid "View Name" msgstr "Имя представления" -#: gitk:4340 msgid "Apply (F5)" msgstr "Применить (F5)" -#: gitk:4378 msgid "Error in commit selection arguments:" msgstr "Ошибка в параметрах выбора коммитов:" -#: gitk:4433 gitk:4486 gitk:4948 gitk:4962 gitk:6232 gitk:12524 gitk:12525 msgid "None" msgstr "Ни одного" -#: gitk:5045 gitk:5050 msgid "Descendant" msgstr "Порождённое" -#: gitk:5046 msgid "Not descendant" msgstr "Не порождённое" -#: gitk:5053 gitk:5058 msgid "Ancestor" msgstr "Предок" -#: gitk:5054 msgid "Not ancestor" msgstr "Не предок" -#: gitk:5348 msgid "Local changes checked in to index but not committed" msgstr "Проиндексированные изменения" -#: gitk:5384 msgid "Local uncommitted changes, not checked in to index" msgstr "Непроиндексированные изменения" -#: gitk:7158 msgid "and many more" msgstr "и многое другое" -#: gitk:7161 msgid "many" msgstr "много" -#: gitk:7352 msgid "Tags:" msgstr "Метки:" -#: gitk:7369 gitk:7375 gitk:8854 msgid "Parent" msgstr "Предок" -#: gitk:7380 msgid "Child" msgstr "Потомок" -#: gitk:7389 msgid "Branch" msgstr "Ветка" -#: gitk:7392 msgid "Follows" msgstr "Следует за" -#: gitk:7395 msgid "Precedes" msgstr "Предшествует" -#: gitk:7990 #, tcl-format msgid "Error getting diffs: %s" msgstr "Ошибка получения изменений: %s" -#: gitk:8679 msgid "Goto:" msgstr "Перейти к:" -#: gitk:8700 #, tcl-format msgid "Short SHA1 id %s is ambiguous" msgstr "Сокращённый SHA1 идентификатор %s неоднозначен" -#: gitk:8707 #, tcl-format msgid "Revision %s is not known" msgstr "Редакция %s не найдена" -#: gitk:8717 #, tcl-format msgid "SHA1 id %s is not known" msgstr "SHA1 идентификатор %s не найден" -#: gitk:8719 #, tcl-format msgid "Revision %s is not in the current view" msgstr "Редакция %s не найдена в текущем представлении" -#: gitk:8861 gitk:8876 msgid "Date" msgstr "Дата" -#: gitk:8864 msgid "Children" msgstr "Потомки" -#: gitk:8927 #, tcl-format msgid "Reset %s branch to here" msgstr "Сбросить ветку %s на этот коммит" -#: gitk:8929 msgid "Detached head: can't reset" msgstr "Коммит не принадлежит ни одной ветке, сбросить невозможно" -#: gitk:9034 gitk:9040 msgid "Skipping merge commit " msgstr "Пропускаю коммит-слияние" -#: gitk:9049 gitk:9054 msgid "Error getting patch ID for " msgstr "Не удалось получить идентификатор патча для " -#: gitk:9050 gitk:9055 msgid " - stopping\n" msgstr " — останов\n" -#: gitk:9060 gitk:9063 gitk:9071 gitk:9085 gitk:9094 msgid "Commit " msgstr "Коммит" -#: gitk:9064 msgid "" " is the same patch as\n" " " -msgstr " такой же патч, как и\n " +msgstr "" +" такой же патч, как и\n" +" " -#: gitk:9072 msgid "" " differs from\n" " " -msgstr " отличается от\n " +msgstr "" +" отличается от\n" +" " -#: gitk:9074 msgid "" "Diff of commits:\n" "\n" -msgstr "Различия коммитов:\n\n" +msgstr "" +"Различия коммитов:\n" +"\n" -#: gitk:9086 gitk:9095 #, tcl-format msgid " has %s children - stopping\n" msgstr " является %s потомком — останов\n" -#: gitk:9114 #, tcl-format msgid "Error writing commit to file: %s" msgstr "Произошла ошибка при записи коммита в файл: %s" -#: gitk:9120 #, tcl-format msgid "Error diffing commits: %s" msgstr "Произошла ошибка при выводе различий коммитов: %s" -#: gitk:9166 msgid "Top" msgstr "Верх" -#: gitk:9167 msgid "From" msgstr "От" -#: gitk:9172 msgid "To" msgstr "До" -#: gitk:9196 msgid "Generate patch" msgstr "Создать патч" -#: gitk:9198 msgid "From:" msgstr "От:" -#: gitk:9207 msgid "To:" msgstr "До:" -#: gitk:9216 msgid "Reverse" msgstr "В обратном порядке" -#: gitk:9218 gitk:9428 msgid "Output file:" msgstr "Файл для сохранения:" -#: gitk:9224 msgid "Generate" msgstr "Создать" -#: gitk:9262 msgid "Error creating patch:" msgstr "Ошибка создания патча:" -#: gitk:9285 gitk:9416 gitk:9504 msgid "ID:" msgstr "ID:" -#: gitk:9294 msgid "Tag name:" msgstr "Имя метки:" -#: gitk:9297 msgid "Tag message is optional" msgstr "Описание метки указывать не обязательно" -#: gitk:9299 msgid "Tag message:" msgstr "Описание метки:" -#: gitk:9303 gitk:9474 msgid "Create" msgstr "Создать" -#: gitk:9321 msgid "No tag name specified" msgstr "Не задано имя метки" -#: gitk:9325 #, tcl-format msgid "Tag \"%s\" already exists" msgstr "Метка «%s» уже существует" -#: gitk:9335 msgid "Error creating tag:" msgstr "Ошибка создания метки:" -#: gitk:9425 msgid "Command:" msgstr "Команда:" -#: gitk:9433 msgid "Write" msgstr "Запись" -#: gitk:9451 msgid "Error writing commit:" msgstr "Произошла ошибка при записи коммита:" -#: gitk:9473 msgid "Create branch" msgstr "Создать ветку" -#: gitk:9489 #, tcl-format msgid "Rename branch %s" msgstr "Переименовать ветку %s" -#: gitk:9490 msgid "Rename" msgstr "Переименовать" -#: gitk:9514 msgid "Name:" msgstr "Имя:" -#: gitk:9538 msgid "Please specify a name for the new branch" msgstr "Укажите имя для новой ветки" -#: gitk:9543 #, tcl-format msgid "Branch '%s' already exists. Overwrite?" msgstr "Ветка «%s» уже существует. Переписать?" -#: gitk:9587 msgid "Please specify a new name for the branch" msgstr "Укажите имя для новой ветки" -#: gitk:9650 #, tcl-format msgid "Commit %s is already included in branch %s -- really re-apply it?" msgstr "Коммит %s уже включён в ветку %s. Продолжить операцию?" -#: gitk:9655 msgid "Cherry-picking" msgstr "Копирование коммита" -#: gitk:9664 #, tcl-format msgid "" "Cherry-pick failed because of local changes to file '%s'.\n" "Please commit, reset or stash your changes and try again." -msgstr "Копирование коммита невозможно из-за изменений в файле «%s».\nЗакоммитьте, сбросьте или спрячьте изменения и повторите операцию." +msgstr "" +"Копирование коммита невозможно из-за изменений в файле «%s».\n" +"Закоммитьте, сбросьте или спрячьте изменения и повторите операцию." -#: gitk:9670 msgid "" "Cherry-pick failed because of merge conflict.\n" "Do you wish to run git citool to resolve it?" -msgstr "Копирование изменений невозможно из-за незавершённой операции слияния.\nЗапустить git citool для завершения этой операции?" +msgstr "" +"Копирование изменений невозможно из-за незавершённой операции слияния.\n" +"Запустить git citool для завершения этой операции?" -#: gitk:9686 gitk:9744 msgid "No changes committed" msgstr "Изменения не закоммичены" -#: gitk:9713 #, tcl-format msgid "Commit %s is not included in branch %s -- really revert it?" msgstr "Коммит %s не включён в ветку %s. Продолжить операцию?" -#: gitk:9718 msgid "Reverting" msgstr "Обращение изменений" -#: gitk:9726 #, tcl-format msgid "" "Revert failed because of local changes to the following files:%s Please " "commit, reset or stash your changes and try again." -msgstr "Возврат изменений коммита не удался из-за локальных изменений в указанных файлах: %s\nЗакоммитьте, сбросьте или спрячьте изменения и повторите операцию." +msgstr "" +"Возврат изменений коммита не удался из-за локальных изменений в указанных " +"файлах: %s\n" +"Закоммитьте, сбросьте или спрячьте изменения и повторите операцию." -#: gitk:9730 msgid "" "Revert failed because of merge conflict.\n" " Do you wish to run git citool to resolve it?" -msgstr "Возврат изменений невозможен из-за незавершённой операции слияния.\nЗапустить git citool для завершения этой операции?" +msgstr "" +"Возврат изменений невозможен из-за незавершённой операции слияния.\n" +"Запустить git citool для завершения этой операции?" -#: gitk:9773 msgid "Confirm reset" msgstr "Подтвердите операцию перехода" -#: gitk:9775 #, tcl-format msgid "Reset branch %s to %s?" msgstr "Сбросить ветку %s на коммит %s?" -#: gitk:9777 msgid "Reset type:" msgstr "Тип операции перехода:" -#: gitk:9780 msgid "Soft: Leave working tree and index untouched" msgstr "Лёгкий: оставить рабочий каталог и индекс неизменными" -#: gitk:9783 msgid "Mixed: Leave working tree untouched, reset index" msgstr "Смешанный: оставить рабочий каталог неизменным, установить индекс" -#: gitk:9786 msgid "" "Hard: Reset working tree and index\n" "(discard ALL local changes)" -msgstr "Жесткий: переписать индекс и рабочий каталог\n(все изменения в рабочем каталоге будут потеряны)" +msgstr "" +"Жесткий: переписать индекс и рабочий каталог\n" +"(все изменения в рабочем каталоге будут потеряны)" -#: gitk:9803 msgid "Resetting" msgstr "Сброс" -#: gitk:9876 #, tcl-format msgid "A local branch named %s exists already" msgstr "Локальная ветка с именем %s уже существует" -#: gitk:9884 msgid "Checking out" msgstr "Переход" -#: gitk:9943 msgid "Cannot delete the currently checked-out branch" msgstr "Активная ветка не может быть удалена" -#: gitk:9949 #, tcl-format msgid "" "The commits on branch %s aren't on any other branch.\n" "Really delete branch %s?" -msgstr "Коммиты из ветки %s не принадлежат больше никакой другой ветке.\nДействительно удалить ветку %s?" +msgstr "" +"Коммиты из ветки %s не принадлежат больше никакой другой ветке.\n" +"Действительно удалить ветку %s?" -#: gitk:9980 #, tcl-format msgid "Tags and heads: %s" msgstr "Метки и ветки: %s" -#: gitk:9997 msgid "Filter" msgstr "Фильтровать" -#: gitk:10293 msgid "" "Error reading commit topology information; branch and preceding/following " "tag information will be incomplete." -msgstr "Ошибка чтения истории проекта; информация о ветках и коммитах вокруг меток (до/после) может быть неполной." +msgstr "" +"Ошибка чтения истории проекта; информация о ветках и коммитах вокруг меток " +"(до/после) может быть неполной." -#: gitk:11270 msgid "Tag" msgstr "Метка" -#: gitk:11274 msgid "Id" msgstr "Id" -#: gitk:11357 msgid "Gitk font chooser" msgstr "Шрифт Gitk" -#: gitk:11374 msgid "B" msgstr "Ж" -#: gitk:11377 msgid "I" msgstr "К" -#: gitk:11495 msgid "Commit list display options" msgstr "Параметры показа списка коммитов" -#: gitk:11498 msgid "Maximum graph width (lines)" msgstr "Макс. ширина графа (строк)" -#: gitk:11502 #, no-tcl-format msgid "Maximum graph width (% of pane)" msgstr "Макс. ширина графа (% ширины панели)" -#: gitk:11505 msgid "Show local changes" msgstr "Показывать изменения в рабочем каталоге" -#: gitk:11508 msgid "Auto-select SHA1 (length)" msgstr "Автоматически выделить SHA1 (длинна)" -#: gitk:11512 msgid "Hide remote refs" msgstr "Скрыть внешние ссылки" -#: gitk:11516 msgid "Diff display options" msgstr "Параметры показа изменений" -#: gitk:11518 msgid "Tab spacing" msgstr "Ширина табуляции" -#: gitk:11521 msgid "Display nearby tags/heads" msgstr "Показывать близкие метки/ветки" -#: gitk:11524 msgid "Maximum # tags/heads to show" msgstr "Показывать максимальное количество меток/веток" -#: gitk:11527 msgid "Limit diffs to listed paths" msgstr "Ограничить показ изменений выбранными файлами" -#: gitk:11530 msgid "Support per-file encodings" msgstr "Поддержка кодировок в отдельных файлах" -#: gitk:11536 gitk:11683 msgid "External diff tool" msgstr "Программа для показа изменений" -#: gitk:11537 msgid "Choose..." msgstr "Выберите..." -#: gitk:11542 msgid "General options" msgstr "Общие опции" -#: gitk:11545 msgid "Use themed widgets" msgstr "Использовать стили виджетов" -#: gitk:11547 msgid "(change requires restart)" msgstr "(изменение потребует перезапуск)" -#: gitk:11549 msgid "(currently unavailable)" msgstr "(недоступно в данный момент)" -#: gitk:11560 msgid "Colors: press to choose" msgstr "Цвета: нажмите для выбора" -#: gitk:11563 msgid "Interface" msgstr "Интерфейс" -#: gitk:11564 msgid "interface" msgstr "интерфейс" -#: gitk:11567 msgid "Background" msgstr "Фон" -#: gitk:11568 gitk:11598 msgid "background" msgstr "фон" -#: gitk:11571 msgid "Foreground" msgstr "Передний план" -#: gitk:11572 msgid "foreground" msgstr "передний план" -#: gitk:11575 msgid "Diff: old lines" msgstr "Изменения: старый текст" -#: gitk:11576 msgid "diff old lines" msgstr "старый текст изменения" -#: gitk:11580 msgid "Diff: new lines" msgstr "Изменения: новый текст" -#: gitk:11581 msgid "diff new lines" msgstr "новый текст изменения" -#: gitk:11585 msgid "Diff: hunk header" msgstr "Изменения: заголовок блока" -#: gitk:11587 msgid "diff hunk header" msgstr "заголовок блока изменений" -#: gitk:11591 msgid "Marked line bg" msgstr "Фон выбранной строки" -#: gitk:11593 msgid "marked line background" msgstr "фон выбранной строки" -#: gitk:11597 msgid "Select bg" msgstr "Выберите фон" -#: gitk:11606 msgid "Fonts: press to choose" msgstr "Шрифт: нажмите для выбора" -#: gitk:11608 msgid "Main font" msgstr "Основной шрифт" -#: gitk:11609 msgid "Diff display font" msgstr "Шрифт показа изменений" -#: gitk:11610 msgid "User interface font" msgstr "Шрифт интерфейса" -#: gitk:11632 msgid "Gitk preferences" msgstr "Настройки Gitk" -#: gitk:11641 msgid "General" msgstr "Общие" -#: gitk:11642 msgid "Colors" msgstr "Цвета" -#: gitk:11643 msgid "Fonts" msgstr "Шрифты" -#: gitk:11693 #, tcl-format msgid "Gitk: choose color for %s" msgstr "Gitk: выберите цвет для %s" -#: gitk:12206 msgid "" "Sorry, gitk cannot run with this version of Tcl/Tk.\n" " Gitk requires at least Tcl/Tk 8.4." -msgstr "К сожалению gitk не может работать с этой версий Tcl/Tk.\nТребуется как минимум Tcl/Tk 8.4." +msgstr "" +"К сожалению gitk не может работать с этой версий Tcl/Tk.\n" +"Требуется как минимум Tcl/Tk 8.4." -#: gitk:12416 msgid "Cannot find a git repository here." msgstr "Git-репозитарий не найден в текущем каталоге." -#: gitk:12463 #, tcl-format msgid "Ambiguous argument '%s': both revision and filename" msgstr "Неоднозначный аргумент «%s»: существует как редакция и как имя файла" -#: gitk:12475 msgid "Bad arguments to gitk:" msgstr "Неправильные аргументы для gitk:" diff --git a/gitk-git/po/sv.po b/gitk-git/po/sv.po index 5afbe6da1d..c9290625c0 100644 --- a/gitk-git/po/sv.po +++ b/gitk-git/po/sv.po @@ -7,7 +7,7 @@ # msgid "" msgstr "" -"Project-Id-Version: sv\n" +"Project-Id-Version: Gitk\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-10-26 21:39+0100\n" "PO-Revision-Date: 2023-10-26 21:42+0100\n" @@ -20,33 +20,26 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Gtranslator 3.38.0\n" -#: gitk:139 msgid "Couldn't get list of unmerged files:" msgstr "Kunde inte hämta lista över ej sammanslagna filer:" -#: gitk:211 gitk:2406 msgid "Color words" msgstr "Färga ord" -#: gitk:216 gitk:2406 gitk:8307 gitk:8340 msgid "Markup words" msgstr "Märk upp ord" -#: gitk:323 msgid "Error parsing revisions:" msgstr "Fel vid tolkning av revisioner:" -#: gitk:379 msgid "Error executing --argscmd command:" msgstr "Fel vid körning av --argscmd-kommando:" -#: gitk:392 msgid "No files selected: --merge specified but no files are unmerged." msgstr "" "Inga filer valdes: --merge angavs men det finns inga filer som inte har " "slagits samman." -#: gitk:395 msgid "" "No files selected: --merge specified but no unmerged files are within file " "limit." @@ -54,326 +47,243 @@ msgstr "" "Inga filer valdes: --merge angavs men det finns inga filer inom " "filbegränsningen." -#: gitk:417 gitk:565 msgid "Error executing git log:" msgstr "Fel vid körning av git log:" -#: gitk:435 gitk:581 msgid "Reading" msgstr "Läser" -#: gitk:495 gitk:4572 msgid "Reading commits..." msgstr "Läser incheckningar..." -#: gitk:498 gitk:1640 gitk:4575 msgid "No commits selected" msgstr "Inga incheckningar markerade" -#: gitk:1448 gitk:4092 gitk:12674 msgid "Command line" msgstr "Kommandorad" -#: gitk:1514 msgid "Can't parse git log output:" msgstr "Kan inte tolka utdata från git log:" -#: gitk:1743 msgid "No commit information available" msgstr "Ingen incheckningsinformation är tillgänglig" -#: gitk:1910 gitk:1939 gitk:4362 gitk:9847 gitk:11451 gitk:11751 msgid "OK" msgstr "OK" -#: gitk:1941 gitk:4364 gitk:9283 gitk:9362 gitk:9492 gitk:9578 gitk:9849 -#: gitk:11452 gitk:11752 msgid "Cancel" msgstr "Avbryt" -#: gitk:2090 msgid "&Update" msgstr "&Uppdatera" -#: gitk:2091 msgid "&Reload" msgstr "Läs &om" -#: gitk:2092 msgid "Reread re&ferences" msgstr "Läs om &referenser" -#: gitk:2093 msgid "&List references" msgstr "&Visa referenser" -#: gitk:2095 msgid "Start git &gui" msgstr "Starta git &gui" -#: gitk:2097 msgid "&Quit" msgstr "&Avsluta" -#: gitk:2089 msgid "&File" msgstr "&Arkiv" -#: gitk:2101 msgid "&Preferences" msgstr "&Inställningar" -#: gitk:2100 msgid "&Edit" msgstr "&Redigera" -#: gitk:2105 msgid "&New view..." msgstr "&Ny vy..." -#: gitk:2106 msgid "&Edit view..." msgstr "&Ändra vy..." -#: gitk:2107 msgid "&Delete view" msgstr "&Ta bort vy" -#: gitk:2109 msgid "&All files" msgstr "&Alla filer" -#: gitk:2104 msgid "&View" msgstr "&Visa" -#: gitk:2114 gitk:2124 msgid "&About gitk" msgstr "&Om gitk" -#: gitk:2115 gitk:2129 msgid "&Key bindings" msgstr "&Tangentbordsbindningar" -#: gitk:2113 gitk:2128 msgid "&Help" msgstr "&Hjälp" -#: gitk:2206 gitk:8739 msgid "SHA1 ID:" msgstr "SHA1-id:" -#: gitk:2250 msgid "Row" msgstr "Rad" -#: gitk:2288 msgid "Find" msgstr "Sök" -#: gitk:2316 msgid "commit" msgstr "incheckning" -#: gitk:2320 gitk:2322 gitk:4734 gitk:4757 gitk:4781 gitk:6802 gitk:6874 -#: gitk:6959 msgid "containing:" msgstr "som innehåller:" -#: gitk:2323 gitk:3573 gitk:3578 gitk:4810 msgid "touching paths:" msgstr "som rör sökväg:" -#: gitk:2324 gitk:4824 msgid "adding/removing string:" msgstr "som lägger/till tar bort sträng:" -#: gitk:2325 gitk:4826 msgid "changing lines matching:" msgstr "ändrar rader som matchar:" -#: gitk:2334 gitk:2336 gitk:4813 msgid "Exact" msgstr "Exakt" -#: gitk:2336 gitk:4901 gitk:6770 msgid "IgnCase" msgstr "IgnVersaler" -#: gitk:2336 gitk:4783 gitk:4899 gitk:6766 msgid "Regexp" msgstr "Reg.uttr." -#: gitk:2338 gitk:2339 gitk:4921 gitk:4951 gitk:4958 gitk:6895 gitk:6963 msgid "All fields" msgstr "Alla fält" -#: gitk:2339 gitk:4918 gitk:4951 gitk:6833 msgid "Headline" msgstr "Rubrik" -#: gitk:2340 gitk:4918 gitk:6833 gitk:6963 gitk:7471 msgid "Comments" msgstr "Kommentarer" -#: gitk:2340 gitk:4918 gitk:4923 gitk:4958 gitk:6833 gitk:7406 gitk:8917 -#: gitk:8932 msgid "Author" msgstr "Författare" -#: gitk:2340 gitk:4918 gitk:6833 gitk:7408 msgid "Committer" msgstr "Incheckare" -#: gitk:2374 msgid "Search" msgstr "Sök" -#: gitk:2382 msgid "Diff" msgstr "Diff" -#: gitk:2384 msgid "Old version" msgstr "Gammal version" -#: gitk:2386 msgid "New version" msgstr "Ny version" -#: gitk:2389 msgid "Lines of context" msgstr "Rader sammanhang" -#: gitk:2399 msgid "Ignore space change" msgstr "Ignorera ändringar i blanksteg" -#: gitk:2403 gitk:2405 gitk:8041 gitk:8293 msgid "Line diff" msgstr "Rad-diff" -#: gitk:2478 msgid "Patch" msgstr "Patch" -#: gitk:2480 msgid "Tree" msgstr "Träd" -#: gitk:2650 gitk:2671 msgid "Diff this -> selected" msgstr "Diff denna -> markerad" -#: gitk:2651 gitk:2672 msgid "Diff selected -> this" msgstr "Diff markerad -> denna" -#: gitk:2652 gitk:2673 msgid "Make patch" msgstr "Skapa patch" -#: gitk:2653 gitk:9341 msgid "Create tag" msgstr "Skapa tagg" -#: gitk:2654 msgid "Copy commit reference" msgstr "Kopiera incheckningsreferens" -#: gitk:2655 gitk:9472 msgid "Write commit to file" msgstr "Skriv incheckning till fil" -#: gitk:2656 msgid "Create new branch" msgstr "Skapa ny gren" -#: gitk:2657 msgid "Cherry-pick this commit" msgstr "Plocka denna incheckning" -#: gitk:2658 msgid "Reset HEAD branch to here" msgstr "Återställ HEAD-grenen hit" -#: gitk:2659 msgid "Mark this commit" msgstr "Markera denna incheckning" -#: gitk:2660 msgid "Return to mark" msgstr "Återgå till markering" -#: gitk:2661 msgid "Find descendant of this and mark" msgstr "Hitta efterföljare till denna och markera" -#: gitk:2662 msgid "Compare with marked commit" msgstr "Jämför med markerad incheckning" -#: gitk:2663 gitk:2674 msgid "Diff this -> marked commit" msgstr "Diff denna -> markerad incheckning" -#: gitk:2664 gitk:2675 msgid "Diff marked commit -> this" msgstr "Diff markerad incheckning -> denna" -#: gitk:2665 msgid "Revert this commit" msgstr "Ångra denna incheckning" -#: gitk:2681 msgid "Check out this branch" msgstr "Checka ut denna gren" -#: gitk:2682 msgid "Rename this branch" msgstr "Byt namn på denna gren" -#: gitk:2683 msgid "Remove this branch" msgstr "Ta bort denna gren" -#: gitk:2684 msgid "Copy branch name" msgstr "Kopiera namn på gren" -#: gitk:2691 msgid "Highlight this too" msgstr "Markera även detta" -#: gitk:2692 msgid "Highlight this only" msgstr "Markera bara detta" -#: gitk:2693 msgid "External diff" msgstr "Extern diff" -#: gitk:2694 msgid "Blame parent commit" msgstr "Klandra föräldraincheckning" -#: gitk:2695 msgid "Copy path" msgstr "Kopiera sökväg" -#: gitk:2702 msgid "Show origin of this line" msgstr "Visa ursprunget för den här raden" -#: gitk:2703 msgid "Run git gui blame on this line" msgstr "Kör git gui blame på den här raden" -#: gitk:3057 msgid "About gitk" msgstr "Om gitk" -#: gitk:3059 msgid "" "\n" "Gitk - a commit viewer for git\n" @@ -389,323 +299,249 @@ msgstr "" "\n" "Använd och vidareförmedla enligt villkoren i GNU General Public License" -#: gitk:3067 gitk:3134 gitk:10062 msgid "Close" msgstr "Stäng" -#: gitk:3088 msgid "Gitk key bindings" msgstr "Tangentbordsbindningar för Gitk" -#: gitk:3091 msgid "Gitk key bindings:" msgstr "Tangentbordsbindningar för Gitk:" -#: gitk:3093 #, tcl-format msgid "<%s-Q>\t\tQuit" msgstr "<%s-Q>\t\tAvsluta" -#: gitk:3094 #, tcl-format msgid "<%s-W>\t\tClose window" msgstr "<%s-W>\t\tStäng fönster" -#: gitk:3095 msgid "<Home>\t\tMove to first commit" msgstr "<Home>\t\tGå till första incheckning" -#: gitk:3096 msgid "<End>\t\tMove to last commit" msgstr "<End>\t\tGå till sista incheckning" -#: gitk:3097 msgid "<Up>, p, k\tMove up one commit" msgstr "<Upp>, p, k\tGå en incheckning upp" -#: gitk:3098 msgid "<Down>, n, j\tMove down one commit" msgstr "<Ned>, n, j\tGå en incheckning ned" -#: gitk:3099 msgid "<Left>, z, h\tGo back in history list" msgstr "<Vänster>, z, h\tGå bakåt i historiken" -#: gitk:3100 msgid "<Right>, x, l\tGo forward in history list" msgstr "<Höger>, x, l\tGå framåt i historiken" -#: gitk:3101 #, tcl-format msgid "<%s-n>\tGo to n-th parent of current commit in history list" msgstr "<%s-n>\tGå till aktuell inchecknings n:te förälder i historielistan" -#: gitk:3102 msgid "<PageUp>\tMove up one page in commit list" msgstr "<PageUp>\tGå upp en sida i incheckningslistan" -#: gitk:3103 msgid "<PageDown>\tMove down one page in commit list" msgstr "<PageDown>\tGå ned en sida i incheckningslistan" -#: gitk:3104 #, tcl-format msgid "<%s-Home>\tScroll to top of commit list" msgstr "<%s-Home>\tRulla till början av incheckningslistan" -#: gitk:3105 #, tcl-format msgid "<%s-End>\tScroll to bottom of commit list" msgstr "<%s-End>\tRulla till slutet av incheckningslistan" -#: gitk:3106 #, tcl-format msgid "<%s-Up>\tScroll commit list up one line" msgstr "<%s-Upp>\tRulla incheckningslistan upp ett steg" -#: gitk:3107 #, tcl-format msgid "<%s-Down>\tScroll commit list down one line" msgstr "<%s-Ned>\tRulla incheckningslistan ned ett steg" -#: gitk:3108 #, tcl-format msgid "<%s-PageUp>\tScroll commit list up one page" msgstr "<%s-PageUp>\tRulla incheckningslistan upp en sida" -#: gitk:3109 #, tcl-format msgid "<%s-PageDown>\tScroll commit list down one page" msgstr "<%s-PageDown>\tRulla incheckningslistan ned en sida" -#: gitk:3110 msgid "<Shift-Up>\tFind backwards (upwards, later commits)" msgstr "<Skift-Upp>\tSök bakåt (uppåt, senare incheckningar)" -#: gitk:3111 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)" msgstr "<Skift-Ned>\tSök framåt (nedåt, tidigare incheckningar)" -#: gitk:3112 msgid "<Delete>, b\tScroll diff view up one page" msgstr "<Delete>, b\tRulla diffvisningen upp en sida" -#: gitk:3113 msgid "<Backspace>\tScroll diff view up one page" msgstr "<Baksteg>\tRulla diffvisningen upp en sida" -#: gitk:3114 msgid "<Space>\t\tScroll diff view down one page" msgstr "<Blanksteg>\tRulla diffvisningen ned en sida" -#: gitk:3115 msgid "u\t\tScroll diff view up 18 lines" msgstr "u\t\tRulla diffvisningen upp 18 rader" -#: gitk:3116 msgid "d\t\tScroll diff view down 18 lines" msgstr "d\t\tRulla diffvisningen ned 18 rader" -#: gitk:3117 #, tcl-format msgid "<%s-F>\t\tFind" msgstr "<%s-F>\t\tSök" -#: gitk:3118 #, tcl-format msgid "<%s-G>\t\tMove to next find hit" msgstr "<%s-G>\t\tGå till nästa sökträff" -#: gitk:3119 msgid "<Return>\tMove to next find hit" msgstr "<Return>\t\tGå till nästa sökträff" -#: gitk:3120 msgid "g\t\tGo to commit" msgstr "g\t\tGå till incheckning" -#: gitk:3121 msgid "/\t\tFocus the search box" msgstr "/\t\tFokusera sökrutan" -#: gitk:3122 msgid "?\t\tMove to previous find hit" msgstr "?\t\tGå till föregående sökträff" -#: gitk:3123 msgid "f\t\tScroll diff view to next file" msgstr "f\t\tRulla diffvisningen till nästa fil" -#: gitk:3124 #, tcl-format msgid "<%s-S>\t\tSearch for next hit in diff view" msgstr "<%s-S>\t\tGå till nästa sökträff i diffvisningen" -#: gitk:3125 #, tcl-format msgid "<%s-R>\t\tSearch for previous hit in diff view" msgstr "<%s-R>\t\tGå till föregående sökträff i diffvisningen" -#: gitk:3126 #, tcl-format msgid "<%s-KP+>\tIncrease font size" msgstr "<%s-Num+>\tÖka teckenstorlek" -#: gitk:3127 #, tcl-format msgid "<%s-plus>\tIncrease font size" msgstr "<%s-plus>\tÖka teckenstorlek" -#: gitk:3128 #, tcl-format msgid "<%s-KP->\tDecrease font size" msgstr "<%s-Num->\tMinska teckenstorlek" -#: gitk:3129 #, tcl-format msgid "<%s-minus>\tDecrease font size" msgstr "<%s-minus>\tMinska teckenstorlek" -#: gitk:3130 msgid "<F5>\t\tUpdate" msgstr "<F5>\t\tUppdatera" -#: gitk:3597 gitk:3606 #, tcl-format msgid "Error creating temporary directory %s:" msgstr "Fel vid skapande av temporär katalog %s:" -#: gitk:3619 #, tcl-format msgid "Error getting \"%s\" from %s:" msgstr "Fel vid hämtning av ”%s” från %s:" -#: gitk:3682 msgid "command failed:" msgstr "kommando misslyckades:" -#: gitk:3831 msgid "No such commit" msgstr "Incheckning saknas" -#: gitk:3845 msgid "git gui blame: command failed:" msgstr "git gui blame: kommando misslyckades:" -#: gitk:3876 #, tcl-format msgid "Couldn't read merge head: %s" msgstr "Kunde inte läsa sammanslagningshuvud: %s" -#: gitk:3884 #, tcl-format msgid "Error reading index: %s" msgstr "Fel vid läsning av index: %s" -#: gitk:3909 #, tcl-format msgid "Couldn't start git blame: %s" msgstr "Kunde inte starta git blame: %s" -#: gitk:3912 gitk:6801 msgid "Searching" msgstr "Söker" -#: gitk:3944 #, tcl-format msgid "Error running git blame: %s" msgstr "Fel vid körning av git blame: %s" -#: gitk:3972 #, tcl-format msgid "That line comes from commit %s, which is not in this view" msgstr "Raden kommer från incheckningen %s, som inte finns i denna vy" -#: gitk:3986 msgid "External diff viewer failed:" msgstr "Externt diff-verktyg misslyckades:" -#: gitk:4090 msgid "All files" msgstr "Alla filer" -#: gitk:4114 msgid "View" msgstr "Visa" -#: gitk:4117 msgid "Gitk view definition" msgstr "Definition av Gitk-vy" -#: gitk:4121 msgid "Remember this view" msgstr "Spara denna vy" -#: gitk:4122 msgid "References (space separated list):" msgstr "Referenser (blankstegsavdelad lista):" -#: gitk:4123 msgid "Branches & tags:" msgstr "Grenar & taggar:" -#: gitk:4124 msgid "All refs" msgstr "Alla referenser" -#: gitk:4125 msgid "All (local) branches" msgstr "Alla (lokala) grenar" -#: gitk:4126 msgid "All tags" msgstr "Alla taggar" -#: gitk:4127 msgid "All remote-tracking branches" msgstr "Alla fjärrspårande grenar" -#: gitk:4128 msgid "Commit Info (regular expressions):" msgstr "Incheckningsinfo (reguljära uttryck):" -#: gitk:4129 msgid "Author:" msgstr "Författare:" -#: gitk:4130 msgid "Committer:" msgstr "Incheckare:" -#: gitk:4131 msgid "Commit Message:" msgstr "Incheckningsmeddelande:" -#: gitk:4132 msgid "Matches all Commit Info criteria" msgstr "Motsvarar alla kriterier för incheckningsinfo" -#: gitk:4133 msgid "Matches no Commit Info criteria" msgstr "Motsvarar inga kriterier för incheckningsinfo" -#: gitk:4134 msgid "Changes to Files:" msgstr "Ändringar av filer:" -#: gitk:4135 msgid "Fixed String" msgstr "Fast sträng" -#: gitk:4136 msgid "Regular Expression" msgstr "Reguljärt uttryck" -#: gitk:4137 msgid "Search string:" msgstr "Söksträng:" -#: gitk:4138 msgid "" "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" @@ -713,205 +549,156 @@ msgstr "" "Incheckningsdatum (”2 weeks ago”, ”2009-03-17 15:27:38”, ”March 17, 2009 " "15:27:38”):" -#: gitk:4139 msgid "Since:" msgstr "Från:" -#: gitk:4140 msgid "Until:" msgstr "Till:" -#: gitk:4141 msgid "Limit and/or skip a number of revisions (positive integer):" msgstr "Begränsa och/eller hoppa över ett antal revisioner (positivt heltal):" -#: gitk:4142 msgid "Number to show:" msgstr "Antal att visa:" -#: gitk:4143 msgid "Number to skip:" msgstr "Antal att hoppa över:" -#: gitk:4144 msgid "Miscellaneous options:" msgstr "Diverse alternativ:" -#: gitk:4145 msgid "Strictly sort by date" msgstr "Strikt datumsortering" -#: gitk:4146 msgid "Mark branch sides" msgstr "Markera sidogrenar" -#: gitk:4147 msgid "Limit to first parent" msgstr "Begränsa till första förälder" -#: gitk:4148 msgid "Simple history" msgstr "Enkel historik" -#: gitk:4149 msgid "Additional arguments to git log:" msgstr "Ytterligare argument till git log:" -#: gitk:4150 msgid "Enter files and directories to include, one per line:" msgstr "Ange filer och kataloger att ta med, en per rad:" -#: gitk:4151 msgid "Command to generate more commits to include:" msgstr "Kommando för att generera fler incheckningar att ta med:" -#: gitk:4275 msgid "Gitk: edit view" msgstr "Gitk: redigera vy" -#: gitk:4283 msgid "-- criteria for selecting revisions" msgstr " - kriterier för val av revisioner" -#: gitk:4288 msgid "View Name" msgstr "Namn på vy" -#: gitk:4363 msgid "Apply (F5)" msgstr "Använd (F5)" -#: gitk:4401 msgid "Error in commit selection arguments:" msgstr "Fel i argument för val av incheckningar:" -#: gitk:4456 gitk:4509 gitk:4971 gitk:4985 gitk:6255 gitk:12615 gitk:12616 msgid "None" msgstr "Inget" -#: gitk:5068 gitk:5073 msgid "Descendant" msgstr "Avkomling" -#: gitk:5069 msgid "Not descendant" msgstr "Inte avkomling" -#: gitk:5076 gitk:5081 msgid "Ancestor" msgstr "Förfader" -#: gitk:5077 msgid "Not ancestor" msgstr "Inte förfader" -#: gitk:5371 msgid "Local changes checked in to index but not committed" msgstr "Lokala ändringar sparade i indexet men inte incheckade" -#: gitk:5407 msgid "Local uncommitted changes, not checked in to index" msgstr "Lokala ändringar, ej sparade i indexet" -#: gitk:7155 msgid "Error starting web browser:" msgstr "Fel när webbläsaren skulle startas:" -#: gitk:7216 msgid "and many more" msgstr "med många flera" -#: gitk:7219 msgid "many" msgstr "många" -#: gitk:7410 msgid "Tags:" msgstr "Taggar:" -#: gitk:7427 gitk:7433 gitk:8912 msgid "Parent" msgstr "Förälder" -#: gitk:7438 msgid "Child" msgstr "Barn" -#: gitk:7447 msgid "Branch" msgstr "Gren" -#: gitk:7450 msgid "Follows" msgstr "Följer" -#: gitk:7453 msgid "Precedes" msgstr "Föregår" -#: gitk:8048 #, tcl-format msgid "Error getting diffs: %s" msgstr "Fel vid hämtning av diff: %s" -#: gitk:8737 msgid "Goto:" msgstr "Gå till:" -#: gitk:8758 #, tcl-format msgid "Short SHA1 id %s is ambiguous" msgstr "Förkortat SHA1-id %s är tvetydigt" -#: gitk:8765 #, tcl-format msgid "Revision %s is not known" msgstr "Revisionen %s är inte känd" -#: gitk:8775 #, tcl-format msgid "SHA1 id %s is not known" msgstr "SHA-id:t %s är inte känt" -#: gitk:8777 #, tcl-format msgid "Revision %s is not in the current view" msgstr "Revisionen %s finns inte i den nuvarande vyn" -#: gitk:8919 gitk:8934 msgid "Date" msgstr "Datum" -#: gitk:8922 msgid "Children" msgstr "Barn" -#: gitk:8985 #, tcl-format msgid "Reset %s branch to here" msgstr "Återställ grenen %s hit" -#: gitk:8987 msgid "Detached head: can't reset" msgstr "Frånkopplad head: kan inte återställa" -#: gitk:9092 gitk:9098 msgid "Skipping merge commit " msgstr "Hoppar över sammanslagningsincheckning " -#: gitk:9107 gitk:9112 msgid "Error getting patch ID for " msgstr "Fel vid hämtning av patch-id för " -#: gitk:9108 gitk:9113 msgid " - stopping\n" msgstr " - stannar\n" -#: gitk:9118 gitk:9121 gitk:9129 gitk:9143 gitk:9152 msgid "Commit " msgstr "Incheckning " -#: gitk:9122 msgid "" " is the same patch as\n" " " @@ -919,7 +706,6 @@ msgstr "" " är samma patch som\n" " " -#: gitk:9130 msgid "" " differs from\n" " " @@ -927,7 +713,6 @@ msgstr "" " skiljer sig från\n" " " -#: gitk:9132 msgid "" "Diff of commits:\n" "\n" @@ -935,148 +720,114 @@ msgstr "" "Skillnad mellan incheckningar:\n" "\n" -#: gitk:9144 gitk:9153 #, tcl-format msgid " has %s children - stopping\n" msgstr " har %s barn - stannar\n" -#: gitk:9172 #, tcl-format msgid "Error writing commit to file: %s" msgstr "Fel vid skrivning av incheckning till fil: %s" -#: gitk:9178 #, tcl-format msgid "Error diffing commits: %s" msgstr "Fel vid jämförelse av incheckningar: %s" -#: gitk:9224 msgid "Top" msgstr "Topp" -#: gitk:9225 msgid "From" msgstr "Från" -#: gitk:9230 msgid "To" msgstr "Till" -#: gitk:9254 msgid "Generate patch" msgstr "Generera patch" -#: gitk:9256 msgid "From:" msgstr "Från:" -#: gitk:9265 msgid "To:" msgstr "Till:" -#: gitk:9274 msgid "Reverse" msgstr "Vänd" -#: gitk:9276 gitk:9486 msgid "Output file:" msgstr "Utdatafil:" -#: gitk:9282 msgid "Generate" msgstr "Generera" -#: gitk:9320 msgid "Error creating patch:" msgstr "Fel vid generering av patch:" -#: gitk:9343 gitk:9474 gitk:9562 msgid "ID:" msgstr "Id:" -#: gitk:9352 msgid "Tag name:" msgstr "Taggnamn:" -#: gitk:9355 msgid "Tag message is optional" msgstr "Taggmeddelandet är valfritt" -#: gitk:9357 msgid "Tag message:" msgstr "Taggmeddelande:" -#: gitk:9361 gitk:9532 msgid "Create" msgstr "Skapa" -#: gitk:9379 msgid "No tag name specified" msgstr "Inget taggnamn angavs" -#: gitk:9383 #, tcl-format msgid "Tag \"%s\" already exists" msgstr "Taggen ”%s” finns redan" -#: gitk:9393 msgid "Error creating tag:" msgstr "Fel vid skapande av tagg:" -#: gitk:9483 msgid "Command:" msgstr "Kommando:" -#: gitk:9491 msgid "Write" msgstr "Skriv" -#: gitk:9509 msgid "Error writing commit:" msgstr "Fel vid skrivning av incheckning:" -#: gitk:9531 msgid "Create branch" msgstr "Skapa gren" -#: gitk:9547 #, tcl-format msgid "Rename branch %s" msgstr "Byt namn på grenen %s" -#: gitk:9548 msgid "Rename" msgstr "Byt namn" -#: gitk:9572 msgid "Name:" msgstr "Namn:" -#: gitk:9596 msgid "Please specify a name for the new branch" msgstr "Ange ett namn för den nya grenen" -#: gitk:9601 #, tcl-format msgid "Branch '%s' already exists. Overwrite?" msgstr "Grenen ”%s” finns redan. Skriva över?" -#: gitk:9645 msgid "Please specify a new name for the branch" msgstr "Ange ett nytt namn för grenen" -#: gitk:9708 #, tcl-format msgid "Commit %s is already included in branch %s -- really re-apply it?" msgstr "" "Incheckningen %s finns redan på grenen %s -- skall den verkligen appliceras " "på nytt?" -#: gitk:9713 msgid "Cherry-picking" msgstr "Plockar" -#: gitk:9722 #, tcl-format msgid "" "Cherry-pick failed because of local changes to file '%s'.\n" @@ -1086,7 +837,6 @@ msgstr "" "Checka in, återställ eller spara undan (stash) dina ändringar och försök " "igen." -#: gitk:9728 msgid "" "Cherry-pick failed because of merge conflict.\n" "Do you wish to run git citool to resolve it?" @@ -1094,20 +844,16 @@ msgstr "" "Cherry-pick misslyckades på grund av en sammanslagningskonflikt.\n" "Vill du köra git citool för att lösa den?" -#: gitk:9744 gitk:9802 msgid "No changes committed" msgstr "Inga ändringar incheckade" -#: gitk:9771 #, tcl-format msgid "Commit %s is not included in branch %s -- really revert it?" msgstr "Incheckningen %s finns inte på grenen %s -- vill du verkligen ångra?" -#: gitk:9776 msgid "Reverting" msgstr "Ångrar" -#: gitk:9784 #, tcl-format msgid "" "Revert failed because of local changes to the following files:%s Please " @@ -1117,7 +863,6 @@ msgstr "" "Checka in, återställ eller spara undan (stash) dina ändringar och försök " "igen." -#: gitk:9788 msgid "" "Revert failed because of merge conflict.\n" " Do you wish to run git citool to resolve it?" @@ -1125,28 +870,22 @@ msgstr "" "Misslyckades med att ångra på grund av en sammanslagningskonflikt.\n" " Vill du köra git citool för att lösa den?" -#: gitk:9831 msgid "Confirm reset" msgstr "Bekräfta återställning" -#: gitk:9833 #, tcl-format msgid "Reset branch %s to %s?" msgstr "Återställa grenen %s till %s?" -#: gitk:9835 msgid "Reset type:" msgstr "Typ av återställning:" -#: gitk:9838 msgid "Soft: Leave working tree and index untouched" msgstr "Mjuk: Rör inte utcheckning och index" -#: gitk:9841 msgid "Mixed: Leave working tree untouched, reset index" msgstr "Blandad: Rör inte utcheckning, återställ index" -#: gitk:9844 msgid "" "Hard: Reset working tree and index\n" "(discard ALL local changes)" @@ -1154,24 +893,19 @@ msgstr "" "Hård: Återställ utcheckning och index\n" "(förkastar ALLA lokala ändringar)" -#: gitk:9861 msgid "Resetting" msgstr "Återställer" -#: gitk:9934 #, tcl-format msgid "A local branch named %s exists already" msgstr "Det finns redan en lokal gren som heter %s" -#: gitk:9942 msgid "Checking out" msgstr "Checkar ut" -#: gitk:10001 msgid "Cannot delete the currently checked-out branch" msgstr "Kan inte ta bort den just nu utcheckade grenen" -#: gitk:10007 #, tcl-format msgid "" "The commits on branch %s aren't on any other branch.\n" @@ -1180,16 +914,13 @@ msgstr "" "Incheckningarna på grenen %s existerar inte på någon annan gren.\n" "Vill du verkligen ta bort grenen %s?" -#: gitk:10038 #, tcl-format msgid "Tags and heads: %s" msgstr "Taggar och huvuden: %s" -#: gitk:10055 msgid "Filter" msgstr "Filter" -#: gitk:10356 msgid "" "Error reading commit topology information; branch and preceding/following " "tag information will be incomplete." @@ -1197,221 +928,167 @@ msgstr "" "Fel vid läsning av information om incheckningstopologi; information om " "grenar och föregående/senare taggar kommer inte vara komplett." -#: gitk:11333 msgid "Tag" msgstr "Tagg" -#: gitk:11337 msgid "Id" msgstr "Id" -#: gitk:11420 msgid "Gitk font chooser" msgstr "Teckensnittsväljare för Gitk" -#: gitk:11437 msgid "B" msgstr "F" -#: gitk:11440 msgid "I" msgstr "K" -#: gitk:11558 msgid "Commit list display options" msgstr "Alternativ för incheckningslistvy" -#: gitk:11561 msgid "Maximum graph width (lines)" msgstr "Maximal grafbredd (rader)" -#: gitk:11565 #, no-tcl-format msgid "Maximum graph width (% of pane)" msgstr "Maximal grafbredd (% av ruta)" -#: gitk:11568 msgid "Show local changes" msgstr "Visa lokala ändringar" -#: gitk:11571 msgid "Auto-select SHA1 (length)" msgstr "Välj SHA1 (längd) automatiskt" -#: gitk:11575 msgid "Hide remote refs" msgstr "Dölj fjärr-referenser" -#: gitk:11579 msgid "Diff display options" msgstr "Alternativ för diffvy" -#: gitk:11581 msgid "Tab spacing" msgstr "Blanksteg för tabulatortecken" -#: gitk:11584 msgid "Display nearby tags/heads" msgstr "Visa närliggande taggar/huvuden" -#: gitk:11587 msgid "Maximum # tags/heads to show" msgstr "Maximalt antal taggar/huvuden att visa" -#: gitk:11590 msgid "Limit diffs to listed paths" msgstr "Begränsa diff till listade sökvägar" -#: gitk:11593 msgid "Support per-file encodings" msgstr "Stöd för filspecifika teckenkodningar" -#: gitk:11599 gitk:11766 msgid "External diff tool" msgstr "Externt diff-verktyg" -#: gitk:11600 msgid "Choose..." msgstr "Välj..." -#: gitk:11607 msgid "Web browser" msgstr "Webbläsare" -#: gitk:11612 msgid "General options" msgstr "Allmänna inställningar" -#: gitk:11615 msgid "Use themed widgets" msgstr "Använd tema på fönsterelement" -#: gitk:11617 msgid "(change requires restart)" msgstr "(ändringen kräver omstart)" -#: gitk:11619 msgid "(currently unavailable)" msgstr "(för närvarande inte tillgängligt)" -#: gitk:11631 msgid "Colors: press to choose" msgstr "Färger: tryck för att välja" -#: gitk:11634 msgid "Interface" msgstr "Gränssnitt" -#: gitk:11635 msgid "interface" msgstr "gränssnitt" -#: gitk:11638 msgid "Background" msgstr "Bakgrund" -#: gitk:11639 gitk:11681 msgid "background" msgstr "bakgrund" -#: gitk:11642 msgid "Foreground" msgstr "Förgrund" -#: gitk:11643 msgid "foreground" msgstr "förgrund" -#: gitk:11646 msgid "Diff: old lines" msgstr "Diff: gamla rader" -#: gitk:11647 msgid "diff old lines" msgstr "diff gamla rader" -#: gitk:11651 msgid "Diff: old lines bg" msgstr "Diff: gamla rader bg" -#: gitk:11653 msgid "diff old lines bg" msgstr "diff gamla rader bg" -#: gitk:11657 msgid "Diff: new lines" msgstr "Diff: nya rader" -#: gitk:11658 msgid "diff new lines" msgstr "diff nya rader" -#: gitk:11662 msgid "Diff: new lines bg" msgstr "Diff: nya rader bg" -#: gitk:11664 msgid "diff new lines bg" msgstr "diff nya rader bg" -#: gitk:11668 msgid "Diff: hunk header" msgstr "Diff: delhuvud" -#: gitk:11670 msgid "diff hunk header" msgstr "diff delhuvud" -#: gitk:11674 msgid "Marked line bg" msgstr "Markerad rad bakgrund" -#: gitk:11676 msgid "marked line background" msgstr "markerad rad bakgrund" -#: gitk:11680 msgid "Select bg" msgstr "Markerad bakgrund" -#: gitk:11689 msgid "Fonts: press to choose" msgstr "Teckensnitt: tryck för att välja" -#: gitk:11691 msgid "Main font" msgstr "Huvudteckensnitt" -#: gitk:11692 msgid "Diff display font" msgstr "Teckensnitt för diffvisning" -#: gitk:11693 msgid "User interface font" msgstr "Teckensnitt för användargränssnitt" -#: gitk:11715 msgid "Gitk preferences" msgstr "Inställningar för Gitk" -#: gitk:11724 msgid "General" msgstr "Allmänt" -#: gitk:11725 msgid "Colors" msgstr "Färger" -#: gitk:11726 msgid "Fonts" msgstr "Teckensnitt" -#: gitk:11776 #, tcl-format msgid "Gitk: choose color for %s" msgstr "Gitk: välj färg för %s" -#: gitk:12289 msgid "" "Sorry, gitk cannot run with this version of Tcl/Tk.\n" " Gitk requires at least Tcl/Tk 8.4." @@ -1419,15 +1096,12 @@ msgstr "" "Gitk kan tyvärr inte köra med denna version av Tcl/Tk.\n" " Gitk kräver åtminstone Tcl/Tk 8.4." -#: gitk:12507 msgid "Cannot find a git repository here." msgstr "Hittar inget git-arkiv här." -#: gitk:12554 #, tcl-format msgid "Ambiguous argument '%s': both revision and filename" msgstr "Tvetydigt argument ”%s”: både revision och filnamn" -#: gitk:12566 msgid "Bad arguments to gitk:" msgstr "Felaktiga argument till gitk:" diff --git a/gitk-git/po/ta.po b/gitk-git/po/ta.po index 0e390c5153..9b9176a4aa 100644 --- a/gitk-git/po/ta.po +++ b/gitk-git/po/ta.po @@ -6,7 +6,7 @@ # msgid "" msgstr "" -"Project-Id-Version: gitk\n" +"Project-Id-Version: Gitk\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-05-07 08:01+0530\n" "PO-Revision-Date: 2025-05-07 09:17\n" @@ -17,360 +17,270 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: gitk:274 msgid "Couldn't get list of unmerged files:" msgstr "ஒருங்கிணைக்கப்படாத கோப்புகளின் பட்டியலைப் பெற முடியவில்லை:" -#: gitk:346 gitk:2565 msgid "Color words" msgstr "வண்ண சொற்கள்" -#: gitk:351 gitk:2565 gitk:8476 gitk:8509 msgid "Markup words" msgstr "குறிக்கப்பட்ட சொற்கள்" -#: gitk:458 msgid "Error parsing revisions:" msgstr "பிழைகளை பாகுபடுத்துதல்:" -#: gitk:524 msgid "Error executing --argscmd command:" msgstr "--argscmd கட்டளையை இயக்குவதில் பிழை:" -#: gitk:537 msgid "No files selected: --merge specified but no files are unmerged." msgstr "" -"கோப்புகள் எதுவும் தேர்ந்தெடுக்கப்படவில்லை: --ஒன்றிணை குறிப்பிடப்பட்டுள்ளது, " -"ஆனால் கோப்புகள் எதுவும் அவிழ்க்கப்படவில்லை." +"கோப்புகள் எதுவும் தேர்ந்தெடுக்கப்படவில்லை: --ஒன்றிணை குறிப்பிடப்பட்டுள்ளது, ஆனால் கோப்புகள் " +"எதுவும் அவிழ்க்கப்படவில்லை." -#: gitk:540 msgid "" "No files selected: --merge specified but no unmerged files are within file " "limit." msgstr "" -"கோப்புகள் எதுவும் தேர்ந்தெடுக்கப்படவில்லை: --ஒன்றிணை குறிப்பிடப்பட்டுள்ளது, " -"ஆனால் அவிழ்க்கப்படாத கோப்புகள் எதுவும் கோப்பு வரம்பிற்குள் இல்லை." +"கோப்புகள் எதுவும் தேர்ந்தெடுக்கப்படவில்லை: --ஒன்றிணை குறிப்பிடப்பட்டுள்ளது, ஆனால் " +"அவிழ்க்கப்படாத கோப்புகள் எதுவும் கோப்பு வரம்பிற்குள் இல்லை." -#: gitk:565 gitk:720 msgid "Error executing git log:" msgstr "அறிவிலி பதிவை இயக்குவதில் பிழை:" -#: gitk:583 gitk:736 msgid "Reading" msgstr "படித்தல்" -#: gitk:643 gitk:4736 msgid "Reading commits..." msgstr "உறுதிமொழிகளைப் படித்தல்..." -#: gitk:646 gitk:1795 gitk:4739 msgid "No commits selected" msgstr "எந்த உறுதிமொழிகளும் தேர்ந்தெடுக்கப்படவில்லை" -#: gitk:1603 gitk:4256 gitk:12883 msgid "Command line" msgstr "கட்டளை வரி" -#: gitk:1669 msgid "Can't parse git log output:" msgstr "அறிவிலி பதிவு வெளியீட்டை அலச முடியாது:" -#: gitk:1898 msgid "No commit information available" msgstr "உறுதிமொழி செய்தி எதுவும் கிடைக்கவில்லை" -#: gitk:2065 gitk:2094 gitk:4526 gitk:10016 gitk:11626 gitk:11946 msgid "OK" msgstr "சரி" -#: gitk:2096 gitk:4528 gitk:9452 gitk:9531 gitk:9661 gitk:9747 gitk:10018 -#: gitk:11627 gitk:11947 msgid "Cancel" msgstr "நீக்கறல்" -#: gitk:2249 msgid "&Update" msgstr "புதுப்பித்தல்" -#: gitk:2250 msgid "&Reload" msgstr "மீண்டும் ஏற்று" -#: gitk:2251 msgid "Reread re&ferences" msgstr "குறிப்புகளை மீண்டும் படி" -#: gitk:2252 msgid "&List references" msgstr "பட்டியல் குறிப்புகள்" -#: gitk:2254 msgid "Start git &gui" msgstr "அறிவிலி இடைமுகத்தைத் தொடங்கு" -#: gitk:2256 msgid "&Quit" msgstr "வெளியேறு" -#: gitk:2248 msgid "&File" msgstr "கோப்பு" -#: gitk:2260 msgid "&Preferences" msgstr "விருப்பத்தேர்வுகள்" -#: gitk:2259 msgid "&Edit" msgstr "திருத்து" -#: gitk:2264 msgid "&New view..." msgstr "புதிய பார்வை..." -#: gitk:2265 msgid "&Edit view..." msgstr "பார்வையைத் திருத்து..." -#: gitk:2266 msgid "&Delete view" msgstr "பார்வையை நீக்கு" -#: gitk:2268 msgid "&All files" msgstr "அனைத்து கோப்புகளும்" -#: gitk:2263 msgid "&View" msgstr "காண்க" -#: gitk:2273 gitk:2283 msgid "&About gitk" msgstr "அறிவிலிகே பற்றி" -#: gitk:2274 gitk:2288 msgid "&Key bindings" msgstr "முக்கிய பிணைப்புகள்" -#: gitk:2272 gitk:2287 msgid "&Help" msgstr "உதவி" -#: gitk:2365 gitk:8908 msgid "Commit ID:" msgstr "உறுதிமொழி அடையாளம்:" -#: gitk:2409 msgid "Row" msgstr "நிரை" -#: gitk:2447 msgid "Find" msgstr "கண்டுபிடி" -#: gitk:2475 msgid "commit" msgstr "உறுதிமொழி" -#: gitk:2479 gitk:2481 gitk:4898 gitk:4921 gitk:4945 gitk:6966 gitk:7038 -#: gitk:7123 msgid "containing:" msgstr "கொண்டிருக்கிறது:" -#: gitk:2482 gitk:3737 gitk:3742 gitk:4974 msgid "touching paths:" msgstr "தொடும் பாதைகள்:" -#: gitk:2483 gitk:4988 msgid "adding/removing string:" msgstr "சரத்தைச் சேர்ப்பது/அகற்றுவது:" -#: gitk:2484 gitk:4990 msgid "changing lines matching:" msgstr "பொருந்தக்கூடிய வரிகளை மாற்றுதல்:" -#: gitk:2493 gitk:2495 gitk:4977 msgid "Exact" msgstr "சரியான" -#: gitk:2495 gitk:5065 gitk:6934 msgid "IgnCase" msgstr "வழக்குதவிர்" -#: gitk:2495 gitk:4947 gitk:5063 gitk:6930 msgid "Regexp" msgstr "வழக்கவெளி" -#: gitk:2497 gitk:2498 gitk:5085 gitk:5115 gitk:5122 gitk:7059 gitk:7127 msgid "All fields" msgstr "அனைத்து புலங்களும்" -#: gitk:2498 gitk:5082 gitk:5115 gitk:6997 msgid "Headline" msgstr "தலைப்பு" -#: gitk:2499 gitk:5082 gitk:6997 gitk:7127 gitk:7639 msgid "Comments" msgstr "கருத்துகள்" -#: gitk:2499 gitk:5082 gitk:5087 gitk:5122 gitk:6997 gitk:7574 gitk:9086 -#: gitk:9101 msgid "Author" msgstr "நூலாசிரியர்" -#: gitk:2499 gitk:5082 gitk:6997 gitk:7576 msgid "Committer" msgstr "உறுதிமொழிபவர்" -#: gitk:2533 msgid "Search" msgstr "தேடு" -#: gitk:2541 msgid "Diff" msgstr "வேறுபாடு" -#: gitk:2543 msgid "Old version" msgstr "பழைய பதிப்பு" -#: gitk:2545 msgid "New version" msgstr "புதிய பதிப்பு" -#: gitk:2548 msgid "Lines of context" msgstr "சூழலின் வரிகள்" -#: gitk:2558 msgid "Ignore space change" msgstr "இடைவெளி மாற்றத்தை புறக்கணி" -#: gitk:2562 gitk:2564 gitk:8209 gitk:8462 msgid "Line diff" msgstr "வரி வேறுபாடு" -#: gitk:2637 msgid "Patch" msgstr "ஒட்டு" -#: gitk:2639 msgid "Tree" msgstr "மரம்" -#: gitk:2814 gitk:2835 msgid "Diff this -> selected" msgstr "இதை வேறுபடுத்துங்கள் -> தேர்ந்தெடுக்கப்பட்டது" -#: gitk:2815 gitk:2836 msgid "Diff selected -> this" msgstr "வேறுபாடு தேர்ந்தெடுக்கப்பட்டது -> இது" -#: gitk:2816 gitk:2837 msgid "Make patch" msgstr "ஒட்டு செய்" -#: gitk:2817 gitk:9510 msgid "Create tag" msgstr "குறிச்சொல்லை உருவாக்கு" -#: gitk:2818 msgid "Copy commit reference" msgstr "உறுதிமொழி குறிப்பு நகலெடு" -#: gitk:2819 gitk:9641 msgid "Write commit to file" msgstr "கோப்பில் உறவை எழுதுங்கள்" -#: gitk:2820 msgid "Create new branch" msgstr "புதிய கிளையை உருவாக்கு" -#: gitk:2821 msgid "Cherry-pick this commit" msgstr "கனி-எடு இந்த உறுதிமொழி" -#: gitk:2822 msgid "Reset HEAD branch to here" msgstr "தலை கிளையை இங்கே மீட்டமை" -#: gitk:2823 msgid "Mark this commit" msgstr "இந்த உறுதிமொழியைக் குறி" -#: gitk:2824 msgid "Return to mark" msgstr "மார்க்குக்குத் திரும்பு" -#: gitk:2825 msgid "Find descendant of this and mark" msgstr "இதன் வழித்தோன்றலைக் கண்டுபிடித்து குறி" -#: gitk:2826 msgid "Compare with marked commit" msgstr "குறிக்கப்பட்ட உறுதிப்பாட்டுடன் ஒப்பிடுக" -#: gitk:2827 gitk:2838 msgid "Diff this -> marked commit" msgstr "இதை வேறுபடுத்துங்கள் -> குறிக்கப்பட்ட உறுதிமொழி" -#: gitk:2828 gitk:2839 msgid "Diff marked commit -> this" msgstr "வேறுபாடு குறிக்கப்பட்ட உறுதிமொழி -> இது" -#: gitk:2829 msgid "Revert this commit" msgstr "இந்த உறுதிப்பாட்டை மாற்றவும்" -#: gitk:2845 msgid "Check out this branch" msgstr "இந்த கிளையைப் பாருங்கள்" -#: gitk:2846 msgid "Rename this branch" msgstr "இந்த கிளையை மறுபெயரிடு" -#: gitk:2847 msgid "Remove this branch" msgstr "இந்த கிளையை அகற்று" -#: gitk:2848 msgid "Copy branch name" msgstr "கிளை பெயரை நகலெடு" -#: gitk:2855 msgid "Highlight this too" msgstr "இதை முன்னிலைப்படுத்து" -#: gitk:2856 msgid "Highlight this only" msgstr "இதை முன்னிலைப்படுத்து" -#: gitk:2857 msgid "External diff" msgstr "வெளிப்புற வேறுபாடு" -#: gitk:2858 msgid "Blame parent commit" msgstr "பெற்றோரை குற்றம் சாட்டு" -#: gitk:2859 msgid "Copy path" msgstr "நகல் பாதை" -#: gitk:2866 msgid "Show origin of this line" msgstr "இந்த வரியின் தோற்றத்தைக் காட்டு" -#: gitk:2867 msgid "Run git gui blame on this line" msgstr "இந்த வரியில் அறிவிலி இடைமுகம் பழியை இயக்கு" -#: gitk:3221 msgid "About gitk" msgstr "அறிவிலிகே பற்றி" -#: gitk:3223 msgid "" "\n" "Gitk - a commit viewer for git\n" @@ -386,697 +296,533 @@ msgstr "" "\n" "குனு பொது பொதுமக்கள் உரிமத்தின் விதிமுறைகளின் கீழ் பயன்படுத்தவும் மறுபகிர்வு செய்யவும்" -#: gitk:3231 gitk:3298 gitk:10231 msgid "Close" msgstr "மூடு" -#: gitk:3252 msgid "Gitk key bindings" msgstr "அறிவிலிகே விசை பிணைப்புகள்" -#: gitk:3255 msgid "Gitk key bindings:" msgstr "அறிவிலிகே விசை பிணைப்புகள்:" -#: gitk:3257 #, tcl-format msgid "<%s-Q>\t\tQuit" msgstr "<%s-Q>\t\tவெளியேறு" -#: gitk:3258 #, tcl-format msgid "<%s-W>\t\tClose window" msgstr "<%s-w>\t\tசாளரத்தை மூடு" -#: gitk:3259 msgid "<Home>\t\tMove to first commit" msgstr "<வீடு> முதல் உறுதிமொழிக்கு நகர்த்து" -#: gitk:3260 msgid "<End>\t\tMove to last commit" msgstr "<முடி> கடைசி உறுதிமொழிக்கு நகர்த்து" -#: gitk:3261 msgid "<Up>, p, k\tMove up one commit" msgstr "<மேலே>, பி, கே\tஒரு உறுதிமொழியை மேலே நகர்த்து" -#: gitk:3262 msgid "<Down>, n, j\tMove down one commit" msgstr "<கீழ்>, n, j\tஒரு உறுதிமொழியை கீழே நகர்த்து" -#: gitk:3263 msgid "<Left>, z, h\tGo back in history list" msgstr "<இடது>, z, h\tவரலாற்று பட்டியலில் திரும்பிச் செல்" -#: gitk:3264 msgid "<Right>, x, l\tGo forward in history list" msgstr "<வலது>, x, l\tவரலாற்று பட்டியலில் முன்னோக்கி செல்" -#: gitk:3265 #, tcl-format msgid "<%s-n>\tGo to n-th parent of current commit in history list" -msgstr "" -"<%s-n> வரலாற்று பட்டியலில் தற்போதைய உறுதிப்பாட்டின் n- வது பெற்றோரிடம் " -"செல்" +msgstr "<%s-n> வரலாற்று பட்டியலில் தற்போதைய உறுதிப்பாட்டின் n- வது பெற்றோரிடம் செல்" -#: gitk:3266 msgid "<PageUp>\tMove up one page in commit list" msgstr "<பக்கம்மேல்>\tஉறுதிமொழி பட்டியலில் ஒரு பக்கத்தை நகர்த்து" -#: gitk:3267 msgid "<PageDown>\tMove down one page in commit list" msgstr "<பக்கம்கீழ்>\tஉறுதிமொழி பட்டியலில் ஒரு பக்கத்தை நகர்த்து" -#: gitk:3268 #, tcl-format msgid "<%s-Home>\tScroll to top of commit list" msgstr "<%s-வீடு>\tஉறுதிமொழி பட்டியலை மேல் பகுதிக்கு உருட்டவும்" -#: gitk:3269 #, tcl-format msgid "<%s-End>\tScroll to bottom of commit list" msgstr "<%s-முடி> உறுதிமொழி பட்டியலின் கீழ் பகுதிக்கு உருட்டவும்" -#: gitk:3270 #, tcl-format msgid "<%s-Up>\tScroll commit list up one line" msgstr "<%s-மேலே>\tஉறுதிமொழி பட்டியலை ஒரு வரி மேலே உருட்டவும்" -#: gitk:3271 #, tcl-format msgid "<%s-Down>\tScroll commit list down one line" msgstr "<%s-கீழ்>\tஉறுதிமொழி பட்டியலை ஒரு வரி கீழே உருட்டவும்" -#: gitk:3272 #, tcl-format msgid "<%s-PageUp>\tScroll commit list up one page" msgstr "<%s-பக்கம்மேலே>\tஉறுதிமொழி பட்டியலை ஒரு பக்கம் மேலே உருட்டவும்" -#: gitk:3273 #, tcl-format msgid "<%s-PageDown>\tScroll commit list down one page" msgstr "<%s-பக்கம்கீழ்>\tஉறுதிமொழி பட்டியலை ஒரு பக்கம் கீழே உருட்டவும்" -#: gitk:3274 msgid "<Shift-Up>\tFind backwards (upwards, later commits)" msgstr "<உயர்த்து-மேலே>\tபின்னோக்கி கண்டுபிடி (மேல்நோக்கி, பின்னர் உறுதிமொழிகள்)" -#: gitk:3275 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)" -msgstr "" -"<உயர்த்து-கீழே>\tமுன்னோக்குகளைக் கண்டறியவும் (கீழ்நோக்கி, முந்தைய " -"உறுதிமொழிகள்)" +msgstr "<உயர்த்து-கீழே>\tமுன்னோக்குகளைக் கண்டறியவும் (கீழ்நோக்கி, முந்தைய உறுதிமொழிகள்)" -#: gitk:3276 msgid "<Delete>, b\tScroll diff view up one page" msgstr "<நீக்கு>, b\tசுருள் வேறுபாடு ஒரு பக்கத்தை மேலே காண்க" -#: gitk:3277 msgid "<Backspace>\tScroll diff view up one page" msgstr "<பின்வெளி>\tசுருள் வேறுபாடு ஒரு பக்கத்தை மேலே காண்க" -#: gitk:3278 msgid "<Space>\t\tScroll diff view down one page" msgstr "<Space>\t\tசுருள் வேறுபாடு ஒரு பக்கத்தைக் கீழே காண்க" -#: gitk:3279 msgid "u\t\tScroll diff view up 18 lines" msgstr "u\t\tசுருள் வேறுபாடு 18 வரிகளை மேலே காண்க" -#: gitk:3280 msgid "d\t\tScroll diff view down 18 lines" msgstr "d\t\tசுருள் வேறுபாடு 18 வரிகளைக் கீழே காண்க" -#: gitk:3281 #, tcl-format msgid "<%s-F>\t\tFind" msgstr "<%s-F>\t\tகண்டுபிடி" -#: gitk:3282 #, tcl-format msgid "<%s-G>\t\tMove to next find hit" msgstr "<%s-G>\t\tஅடுத்த கண்டுபிடிப்பு வெற்றிக்கு செல்" -#: gitk:3283 msgid "<Return>\tMove to next find hit" msgstr "<திரும்பு>\tஅடுத்ததைக் கண்டுபிடி" -#: gitk:3284 msgid "g\t\tGo to commit" msgstr "g\t\tஉறுதிமொழிக்கு செல்" -#: gitk:3285 msgid "/\t\tFocus the search box" msgstr "/\t\tதேடல் பெட்டியில் கவனம் செலுத்து" -#: gitk:3286 msgid "?\t\tMove to previous find hit" msgstr "?\t\tமுந்தைய கண்டுபிடிப்பு வெற்றிக்கு செல்" -#: gitk:3287 msgid "f\t\tScroll diff view to next file" msgstr "f\t\tஅடுத்த கோப்பிற்கு உருள் வேறுபாடு பார்வை" -#: gitk:3288 #, tcl-format msgid "<%s-S>\t\tSearch for next hit in diff view" msgstr "<%s-S>\t\tவேறுபாடு பார்வையில் அடுத்த வெற்றியைத் தேடுங்கள்" -#: gitk:3289 #, tcl-format msgid "<%s-R>\t\tSearch for previous hit in diff view" msgstr "<%s-r> வேறுபட்ட பார்வையில் முந்தைய வெற்றியைத் தேடுங்கள்" -#: gitk:3290 #, tcl-format msgid "<%s-KP+>\tIncrease font size" msgstr "<%s-KP+>\tஎழுத்துரு அளவை அதிகரி" -#: gitk:3291 #, tcl-format msgid "<%s-plus>\tIncrease font size" msgstr "<%s-plus>\tஎழுத்துரு அளவை அதிகரி" -#: gitk:3292 #, tcl-format msgid "<%s-KP->\tDecrease font size" msgstr "<%s-KP->\tஎழுத்துரு அளவைக் குறை" -#: gitk:3293 #, tcl-format msgid "<%s-minus>\tDecrease font size" msgstr "<%s-minus>\tஎழுத்துரு அளவைக் குறை" -#: gitk:3294 msgid "<F5>\t\tUpdate" msgstr "<F5>\t\tபுதுப்பிப்பு" -#: gitk:3761 gitk:3770 #, tcl-format msgid "Error creating temporary directory %s:" msgstr "தற்காலிக அடைவு %s ஐ உருவாக்குவது பிழை:" -#: gitk:3783 #, tcl-format msgid "Error getting \"%s\" from %s:" msgstr "%s இலிருந்து \" %s\" பெறுவது பிழை:" -#: gitk:3846 msgid "command failed:" msgstr "கட்டளை தோல்வியுற்றது:" -#: gitk:3995 msgid "No such commit" msgstr "அத்தகைய உறுதிமொழி இல்லை" -#: gitk:4009 msgid "git gui blame: command failed:" msgstr "அறிவிலி இடைமுக பழி: கட்டளை தோல்வியுற்றது:" -#: gitk:4040 #, tcl-format msgid "Couldn't read merge head: %s" msgstr "ஒன்றிணைப்பு தலையைப் படிக்க முடியவில்லை: %s" -#: gitk:4048 #, tcl-format msgid "Error reading index: %s" msgstr "பிழை வாசிப்பு குறியீடு: %s" -#: gitk:4073 #, tcl-format msgid "Couldn't start git blame: %s" msgstr "அறிவிலி பழியைத் தொடங்க முடியவில்லை: %s" -#: gitk:4076 gitk:6965 msgid "Searching" msgstr "தேடுகிறது" -#: gitk:4108 #, tcl-format msgid "Error running git blame: %s" msgstr "பிழை இயங்கும் அறிவிலி பழி: %s" -#: gitk:4136 #, tcl-format msgid "That line comes from commit %s, which is not in this view" -msgstr "" -"அந்த வரி உறுதிமொழி %s என்பதிலிருந்து வருகிறது, இது இந்த பார்வையில் இல்லை" +msgstr "அந்த வரி உறுதிமொழி %s என்பதிலிருந்து வருகிறது, இது இந்த பார்வையில் இல்லை" -#: gitk:4150 msgid "External diff viewer failed:" msgstr "வெளிப்புற வேறுபாடு பார்வையாளர் தோல்வியுற்றது:" -#: gitk:4254 msgid "All files" msgstr "அனைத்து கோப்புகளும்" -#: gitk:4278 msgid "View" msgstr "காண்க" -#: gitk:4281 msgid "Gitk view definition" msgstr "அறிவிலிகே பார்வை வரையறை" -#: gitk:4285 msgid "Remember this view" msgstr "இந்த பார்வையை நினைவில் கொள்ளுங்கள்" -#: gitk:4286 msgid "References (space separated list):" msgstr "குறிப்புகள் (இடைவெளி பிரிக்கப்பட்ட பட்டியல்):" -#: gitk:4287 msgid "Branches & tags:" msgstr "கிளைகள் மற்றும் குறிச்சொற்கள்:" -#: gitk:4288 msgid "All refs" msgstr "அனைத்து குறிப்புகள்" -#: gitk:4289 msgid "All (local) branches" msgstr "அனைத்து (உள்ளக) கிளைகளும்" -#: gitk:4290 msgid "All tags" msgstr "அனைத்து குறிச்சொற்களும்" -#: gitk:4291 msgid "All remote-tracking branches" msgstr "அனைத்து தொலை-கண்காணிப்பு கிளைகளும்" -#: gitk:4292 msgid "Commit Info (regular expressions):" msgstr "உறுதிமொழி செய்தி (வழக்கமான வெளிப்பாடுகள்):" -#: gitk:4293 msgid "Author:" msgstr "ஆசிரியர்:" -#: gitk:4294 msgid "Committer:" msgstr "உறுதிமொழிபவர்:" -#: gitk:4295 msgid "Commit Message:" msgstr "உறுதிமொழி செய்தி:" -#: gitk:4296 msgid "Matches all Commit Info criteria" msgstr "அனைத்து உறுதிமொழி செய்தி அளவுகோல்களையும் பொருத்துகிறது" -#: gitk:4297 msgid "Matches no Commit Info criteria" msgstr "உறுதிமொழி செய்தி அளவுகோல்களுடன் பொருந்தவில்லை" -#: gitk:4298 msgid "Changes to Files:" msgstr "கோப்புகளில் மாற்றங்கள்:" -#: gitk:4299 msgid "Fixed String" msgstr "நிலையான சரம்" -#: gitk:4300 msgid "Regular Expression" msgstr "வழக்கமான வெளிப்பாடு" -#: gitk:4301 msgid "Search string:" msgstr "தேடல் சரம்:" -#: gitk:4302 msgid "" "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" msgstr "" -"உறுதிமொழி தேதிகள் (\"2 வாரங்களுக்கு முன்பு\", \"2009-01-16 15:27:38\", \"மார்ச் 17, " -"2009 15:27:38\"):" +"உறுதிமொழி தேதிகள் (\"2 வாரங்களுக்கு முன்பு\", \"2009-01-16 15:27:38\", \"மார்ச் " +"17, 2009 15:27:38\"):" -#: gitk:4303 msgid "Since:" msgstr "பின்னர்:" -#: gitk:4304 msgid "Until:" msgstr "வரை:" -#: gitk:4305 msgid "Limit and/or skip a number of revisions (positive integer):" -msgstr "" -"பல திருத்தங்களை (நேர்மறை முழு எண்) கட்டுப்படுத்து மற்றும்/அல்லது தவிர்:" +msgstr "பல திருத்தங்களை (நேர்மறை முழு எண்) கட்டுப்படுத்து மற்றும்/அல்லது தவிர்:" -#: gitk:4306 msgid "Number to show:" msgstr "காண்பிக்க எண்:" -#: gitk:4307 msgid "Number to skip:" msgstr "தவிர்க்க எண்:" -#: gitk:4308 msgid "Miscellaneous options:" msgstr "இதர விருப்பங்கள்:" -#: gitk:4309 msgid "Strictly sort by date" msgstr "கண்டிப்பாக தேதியின்படி வரிசைப்படுத்து" -#: gitk:4310 msgid "Mark branch sides" msgstr "கிளை பக்கங்களைக் குறி" -#: gitk:4311 msgid "Limit to first parent" msgstr "முதல் பெற்றோருக்கு வரம்பு" -#: gitk:4312 msgid "Simple history" msgstr "எளிய வரலாறு" -#: gitk:4313 msgid "Additional arguments to git log:" msgstr "அறிவிலி பதிவுக்கு கூடுதல் வாதங்கள்:" -#: gitk:4314 msgid "Enter files and directories to include, one per line:" msgstr "சேர்க்க கோப்புகள் மற்றும் கோப்பகங்களை உள்ளிடவும், ஒரு வரிக்கு ஒன்று:" -#: gitk:4315 msgid "Command to generate more commits to include:" msgstr "சேர்க்க கூடுதல் உறுதிமொழிகளை உருவாக்க கட்டளை:" -#: gitk:4439 msgid "Gitk: edit view" msgstr "அறிவிலிகே: திருத்து பார்வை" -#: gitk:4447 msgid "-- criteria for selecting revisions" msgstr "-- திருத்தங்களைத் தேர்ந்தெடுப்பதற்கான அளவுகோல்கள்" -#: gitk:4452 msgid "View Name" msgstr "பெயரைக் காண்க" -#: gitk:4527 msgid "Apply (F5)" msgstr "இடு (F5)" -#: gitk:4565 msgid "Error in commit selection arguments:" msgstr "உறுதிமொழி தேர்வு வாதங்களில் பிழை:" -#: gitk:4620 gitk:4673 gitk:5135 gitk:5149 gitk:6419 gitk:12820 gitk:12821 msgid "None" msgstr "எதுவுமில்லை" -#: gitk:5232 gitk:5237 msgid "Descendant" msgstr "வழித்தோன்றல்" -#: gitk:5233 msgid "Not descendant" msgstr "வழித்தோன்றல் அல்ல" -#: gitk:5240 gitk:5245 msgid "Ancestor" msgstr "மூதாதையர்" -#: gitk:5241 msgid "Not ancestor" msgstr "மூதாதையர் அல்ல" -#: gitk:5535 msgid "Local changes checked in to index but not committed" -msgstr "" -"உள்ளக மாற்றங்கள் குறியீட்டில் சரிபார்க்கப்பட்டன, ஆனால் உறுதிமொழியவில்லை" +msgstr "உள்ளக மாற்றங்கள் குறியீட்டில் சரிபார்க்கப்பட்டன, ஆனால் உறுதிமொழியவில்லை" -#: gitk:5571 msgid "Local uncommitted changes, not checked in to index" msgstr "உள்ளக உறுதிமொழியாத மாற்றங்கள், குறியீட்டில் சரிபார்க்கப்படவில்லை" -#: gitk:7319 msgid "Error starting web browser:" msgstr "வலை உலாவியைத் தொடங்குவதில் பிழை:" -#: gitk:7380 msgid "and many more" msgstr "மற்றும் மேலும் பல" -#: gitk:7383 msgid "many" msgstr "பல" -#: gitk:7578 msgid "Tags:" msgstr "குறிச்சொற்கள்:" -#: gitk:7595 gitk:7601 gitk:9081 msgid "Parent" msgstr "பெற்றோர்" -#: gitk:7606 msgid "Child" msgstr "குழந்தை" -#: gitk:7615 msgid "Branch" msgstr "கிளை" -#: gitk:7618 msgid "Follows" msgstr "பின்வருமாறு" -#: gitk:7621 msgid "Precedes" msgstr "முன்னால்" -#: gitk:8216 #, tcl-format msgid "Error getting diffs: %s" msgstr "வேறுபாடு பெறுவதில் பிழை: %s" -#: gitk:8906 msgid "Goto:" msgstr "செல்:" -#: gitk:8927 #, tcl-format msgid "Short commit ID %s is ambiguous" msgstr "குறுகிய உறுதிமொழி அடையாளம் %s தெளிவற்றவை" -#: gitk:8934 #, tcl-format msgid "Revision %s is not known" msgstr "திருத்தம் %s தெரியவில்லை" -#: gitk:8944 #, tcl-format msgid "Commit ID %s is not known" msgstr "உறுதிமொழி அடையாளம் %s அறியப்படவில்லை" -#: gitk:8946 #, tcl-format msgid "Revision %s is not in the current view" msgstr "திருத்தம் %s தற்போதைய பார்வையில் இல்லை" -#: gitk:9088 gitk:9103 msgid "Date" msgstr "திகதி" -#: gitk:9091 msgid "Children" msgstr "குழந்தைகள்" -#: gitk:9154 #, tcl-format msgid "Reset %s branch to here" msgstr "%s கிளையை இங்கே மீட்டமை" -#: gitk:9156 msgid "Detached head: can't reset" msgstr "பிரிக்கப்பட்ட தலை: மீட்டமைக்க முடியாது" -#: gitk:9261 gitk:9267 msgid "Skipping merge commit " msgstr "ஒன்றிணை உறுதிமொழியை தவர்கிறது " -#: gitk:9276 gitk:9281 msgid "Error getting patch ID for " msgstr "ஒட்டு அடையாளத்தைப் பெறுவதில் பிழை" -#: gitk:9277 gitk:9282 msgid " - stopping\n" msgstr "- நிறுத்துதல்\n" -#: gitk:9287 gitk:9290 gitk:9298 gitk:9312 gitk:9321 msgid "Commit " msgstr "உறுதிமொழி" -#: gitk:9291 msgid "" " is the same patch as\n" " " -msgstr "அதே ஒட்டு\n" +msgstr "" +"அதே ஒட்டு\n" " " -#: gitk:9299 msgid "" " differs from\n" " " -msgstr "இருந்து வேறுபடுகிறது\n" +msgstr "" +"இருந்து வேறுபடுகிறது\n" " " -#: gitk:9301 msgid "" "Diff of commits:\n" "\n" -msgstr "உறுதிமொழியின் வேறுபாடு:\n" +msgstr "" +"உறுதிமொழியின் வேறுபாடு:\n" "\n" -#: gitk:9313 gitk:9322 #, tcl-format msgid " has %s children - stopping\n" msgstr "%s குழந்தைகள் உள்ளனர் - நிறுத்துதல்\n" -#: gitk:9341 #, tcl-format msgid "Error writing commit to file: %s" msgstr "உறுதிமொழி கோப்பில் எழுதுதல் பிழை: %s" -#: gitk:9347 #, tcl-format msgid "Error diffing commits: %s" msgstr "உறுதிமொழிகள் வேறுபாடு பிழை: %s" -#: gitk:9393 msgid "Top" msgstr "மேலே" -#: gitk:9394 msgid "From" msgstr "இருந்து" -#: gitk:9399 msgid "To" msgstr "பெறுநர்" -#: gitk:9423 msgid "Generate patch" msgstr "ஒட்டை உருவாக்கு" -#: gitk:9425 msgid "From:" msgstr "இருந்து:" -#: gitk:9434 msgid "To:" msgstr "இதற்கு:" -#: gitk:9443 msgid "Reverse" msgstr "தலைகீழ்" -#: gitk:9445 gitk:9655 msgid "Output file:" msgstr "வெளியீட்டு கோப்பு:" -#: gitk:9451 msgid "Generate" msgstr "உருவாக்கு" -#: gitk:9489 msgid "Error creating patch:" msgstr "ஒட்டை உருவாக்கு பிழை:" -#: gitk:9512 gitk:9643 gitk:9731 msgid "ID:" msgstr "அடையாளம்:" -#: gitk:9521 msgid "Tag name:" msgstr "குறிச்சொல் பெயர்:" -#: gitk:9524 msgid "Tag message is optional" msgstr "குறிச்சொல் செய்தி விருப்பமானது" -#: gitk:9526 msgid "Tag message:" msgstr "குறிச்சொல் செய்தி:" -#: gitk:9530 gitk:9701 msgid "Create" msgstr "உருவாக்கு" -#: gitk:9548 msgid "No tag name specified" msgstr "குறிச்சொல் பெயர் குறிப்பிடப்படவில்லை" -#: gitk:9552 #, tcl-format msgid "Tag \"%s\" already exists" msgstr "குறிச்சொல் \"%s\" ஏற்கனவே உள்ளது" -#: gitk:9562 msgid "Error creating tag:" msgstr "குறிச்சொல்லை உருவாக்கு பிழை:" -#: gitk:9652 msgid "Command:" msgstr "கட்டளை:" -#: gitk:9660 msgid "Write" msgstr "எழுது" -#: gitk:9678 msgid "Error writing commit:" msgstr "பிழை எழுதுதல் உறுதிமொழி:" -#: gitk:9700 msgid "Create branch" msgstr "கிளையை உருவாக்கு" -#: gitk:9716 #, tcl-format msgid "Rename branch %s" msgstr "%s கிளையை மறுபெயரிடு" -#: gitk:9717 msgid "Rename" msgstr "மறுபெயரிடு" -#: gitk:9741 msgid "Name:" msgstr "பெயர்:" -#: gitk:9765 msgid "Please specify a name for the new branch" msgstr "புதிய கிளைக்கு ஒரு பெயரைக் குறிப்பிடு" -#: gitk:9770 #, tcl-format msgid "Branch '%s' already exists. Overwrite?" msgstr "கிளை '%s' ஏற்கனவே உள்ளது. மேலெழுதவா?" -#: gitk:9814 msgid "Please specify a new name for the branch" msgstr "கிளைக்கு ஒரு புதிய பெயரைக் குறிப்பிடு" -#: gitk:9877 #, tcl-format msgid "Commit %s is already included in branch %s -- really re-apply it?" -msgstr "" -"உறுதிமொழி %s ஏற்கனவே கிளை %s சேர்க்கப்பட்டுள்ளன-உண்மையில் அதை மீண்டும் இடவா?" +msgstr "உறுதிமொழி %s ஏற்கனவே கிளை %s சேர்க்கப்பட்டுள்ளன-உண்மையில் அதை மீண்டும் இடவா?" -#: gitk:9882 msgid "Cherry-picking" msgstr "கனி எடுக்கும்" -#: gitk:9891 #, tcl-format msgid "" "Cherry-pick failed because of local changes to file '%s'.\n" @@ -1085,7 +831,6 @@ msgstr "" "'%s' கோப்பில் உள்ளக மாற்றங்கள் காரணமாக கனி-எடு தோல்வியடைந்தது. \n" "தயவுசெய்து உங்கள் மாற்றங்களைச் உறுதிமொழி, மீட்டமை அல்லது சேமி பிறகு மீண்டும் முயற்சி." -#: gitk:9897 msgid "" "Cherry-pick failed because of merge conflict.\n" "Do you wish to run git citool to resolve it?" @@ -1093,31 +838,24 @@ msgstr "" "ஒன்றிணைக்கும் மோதல் காரணமாக கனி-எடு தோல்வியடைந்தது. \n" "அதை தீர்க்க அறிவிலி சிஐகருவியை இயக்க விரும்புகிறீர்களா?" -#: gitk:9913 gitk:9971 msgid "No changes committed" msgstr "எந்த மாற்றங்களும் உறுதிமொழியப்படவில்லை" -#: gitk:9940 #, tcl-format msgid "Commit %s is not included in branch %s -- really revert it?" -msgstr "" -"உறுதிமொழி %s கிளை %s சேர்க்கப்படவில்லை - உண்மையில் அதை மீட்டெடுக்கவா?" +msgstr "உறுதிமொழி %s கிளை %s சேர்க்கப்படவில்லை - உண்மையில் அதை மீட்டெடுக்கவா?" -#: gitk:9945 msgid "Reverting" msgstr "மீட்டெடுத்தல்" -#: gitk:9953 #, tcl-format msgid "" "Revert failed because of local changes to the following files:%s Please " "commit, reset or stash your changes and try again." msgstr "" -"பின்வரும் கோப்புகளில் உள்ளக மாற்றங்கள் காரணமாக மீட்டெடு தோல்வியுற்றது:%s " -"தயவுசெய்து உங்கள் மாற்றங்களைச் உறுதிமொழி, மீட்டமை அல்லது " -"சேமி மற்றும் மீண்டும் முயற்சி." +"பின்வரும் கோப்புகளில் உள்ளக மாற்றங்கள் காரணமாக மீட்டெடு தோல்வியுற்றது:%s தயவுசெய்து உங்கள் " +"மாற்றங்களைச் உறுதிமொழி, மீட்டமை அல்லது சேமி மற்றும் மீண்டும் முயற்சி." -#: gitk:9957 msgid "" "Revert failed because of merge conflict.\n" " Do you wish to run git citool to resolve it?" @@ -1125,30 +863,22 @@ msgstr "" "ஒன்றிணைக்கும் மோதல் காரணமாக மீட்டெடு தோல்வியடைந்தது. \n" "அதை தீர்க்க அறிவிலி சிஐகருவியை இயக்க விரும்புகிறீர்களா?" -#: gitk:10000 msgid "Confirm reset" msgstr "மீட்டமைப்பை உறுதிப்படுத்து" -#: gitk:10002 #, tcl-format msgid "Reset branch %s to %s?" msgstr "%s கிளையை %s க்கு மீட்டமைக்கவா?" -#: gitk:10004 msgid "Reset type:" msgstr "மீட்டமை வகை:" -#: gitk:10007 msgid "Soft: Leave working tree and index untouched" -msgstr "" -"மென்மை: வேலை செய்யும் மரம் மற்றும் குறியீட்டைத் தீண்டாமல் விடு" +msgstr "மென்மை: வேலை செய்யும் மரம் மற்றும் குறியீட்டைத் தீண்டாமல் விடு" -#: gitk:10010 msgid "Mixed: Leave working tree untouched, reset index" -msgstr "" -"கலப்பு: வேலை செய்யும் மரத்தை தீண்டாமல் விடு, குறியீட்டை மீட்டமை" +msgstr "கலப்பு: வேலை செய்யும் மரத்தை தீண்டாமல் விடு, குறியீட்டை மீட்டமை" -#: gitk:10013 msgid "" "Hard: Reset working tree and index\n" "(discard ALL local changes)" @@ -1156,24 +886,19 @@ msgstr "" "கடினம்: வேலை செய்யும் மரம் மற்றும் குறியீட்டை மீட்டமை \n" "(அனைத்து உள்ளக மாற்றங்களையும் நிராகரி)" -#: gitk:10030 msgid "Resetting" msgstr "மீட்டமைத்தல்" -#: gitk:10103 #, tcl-format msgid "A local branch named %s exists already" msgstr "%s என்ற உள்ளக கிளை ஏற்கனவே உள்ளது" -#: gitk:10111 msgid "Checking out" msgstr "சரிபார்" -#: gitk:10170 msgid "Cannot delete the currently checked-out branch" msgstr "தற்போது சரிபார்க்கப்பட்ட கிளையை நீக்க முடியாது" -#: gitk:10176 #, tcl-format msgid "" "The commits on branch %s aren't on any other branch.\n" @@ -1182,254 +907,193 @@ msgstr "" "கிளை %s மீதான உறுதிமொழிகள் வேறு எந்த கிளையிலும் இல்லை. \n" "உண்மையில் கிளை %s நீக்கவா?" -#: gitk:10207 #, tcl-format msgid "Tags and heads: %s" msgstr "குறிச்சொற்கள் மற்றும் தலைகள்: %s" -#: gitk:10224 msgid "Filter" msgstr "வடிப்பி" -#: gitk:10531 msgid "" "Error reading commit topology information; branch and preceding/following " "tag information will be incomplete." msgstr "" -"உறுதிமொழி இடவியல் தகவலை படிப்பதில் பிழை; கிளை மற்றும் அதற்கு " -"முந்தைய/பின்வரும் குறிச்சொல் செய்தி முழுமையடையாது." +"உறுதிமொழி இடவியல் தகவலை படிப்பதில் பிழை; கிளை மற்றும் அதற்கு முந்தைய/பின்வரும் " +"குறிச்சொல் செய்தி முழுமையடையாது." -#: gitk:11508 msgid "Tag" msgstr "குறிச்சொல்" -#: gitk:11512 msgid "Id" msgstr "அடையாளம்" -#: gitk:11595 msgid "Gitk font chooser" msgstr "அறிவிலிகே எழுத்துரு தேர்வு" -#: gitk:11612 msgid "B" msgstr "பி" -#: gitk:11615 msgid "I" msgstr "ஐ" -#: gitk:11734 msgid "Commit list display options" msgstr "உறுதிமொழி பட்டியல் காட்சி விருப்பங்கள்" -#: gitk:11737 msgid "Maximum graph width (lines)" msgstr "அதிகபட்ச வரைபட அகலம் (கோடுகள்)" -#: gitk:11741 #, no-tcl-format msgid "Maximum graph width (% of pane)" msgstr "அதிகபட்ச வரைபட அகலம் (பலகத்தின் %)" -#: gitk:11744 msgid "Show local changes" msgstr "உள்ளக மாற்றங்களைக் காட்டு" -#: gitk:11747 msgid "Hide remote refs" msgstr "தொலை குறிகளை மறை" -#: gitk:11751 msgid "Copy commit ID to clipboard" msgstr "இடைநிலைப்பலகைக்கு அடையாளத்தை நகலெடு" -#: gitk:11755 msgid "Copy commit ID to X11 selection" msgstr "உறுதிமொழி அடையாளத்தை ஃ11 பகுதிக்கு நகலெடு" -#: gitk:11760 msgid "Length of commit ID to copy" msgstr "நகலெடுக்க உறுதிமொழி அடையாளத்தின் நீளம்" -#: gitk:11763 msgid "Diff display options" msgstr "வேறுபாடு காட்சி விருப்பங்கள்" -#: gitk:11765 msgid "Tab spacing" msgstr "தாவல் இடைவெளி" -#: gitk:11769 msgid "Wrap comment text" msgstr "கருத்து உரையை மடி" -#: gitk:11774 msgid "Wrap other text" msgstr "மற்ற உரையை மடி" -#: gitk:11779 msgid "Display nearby tags/heads" msgstr "அருகிலுள்ள குறிச்சொற்கள்/தலைகளைக் காண்பி" -#: gitk:11782 msgid "Maximum # tags/heads to show" msgstr "காண்பிக்க அதிகபட்ச # குறிச்சொற்கள்/தலைகள்" -#: gitk:11785 msgid "Limit diffs to listed paths" msgstr "பட்டியலிடப்பட்ட பாதைகளுக்கு வரம்பு வேறுபடுகிறது" -#: gitk:11788 msgid "Support per-file encodings" msgstr "ஒரு கோப்பு குறியீடுகளை ஆதரி" -#: gitk:11794 gitk:11961 msgid "External diff tool" msgstr "வெளிப்புற வேறுபாடு கருவி" -#: gitk:11795 msgid "Choose..." msgstr "தேர்வு..." -#: gitk:11802 msgid "Web browser" msgstr "வலை உலாவி" -#: gitk:11807 msgid "General options" msgstr "பொது விருப்பங்கள்" -#: gitk:11810 msgid "Use themed widgets" msgstr "கருப்பொருள் நிரல்பலகைகளைப் பயன்படுத்து" -#: gitk:11812 msgid "(change requires restart)" msgstr "(மாற்றத்திற்கு மறுதொடக்கம் தேவை)" -#: gitk:11814 msgid "(currently unavailable)" msgstr "(தற்போது கிடைக்கவில்லை)" -#: gitk:11826 msgid "Colors: press to choose" msgstr "நிறங்கள்: தேர்வு செய்ய அழுத்தவும்" -#: gitk:11829 msgid "Interface" msgstr "இடைமுகம்" -#: gitk:11830 msgid "interface" msgstr "இடைமுகம்" -#: gitk:11833 msgid "Background" msgstr "பின்னணி" -#: gitk:11834 gitk:11876 msgid "background" msgstr "பின்னணி" -#: gitk:11837 msgid "Foreground" msgstr "முன்புறம்" -#: gitk:11838 msgid "foreground" msgstr "முன்புறம்" -#: gitk:11841 msgid "Diff: old lines" msgstr "வேறுபாடு: பழைய வரிகள்" -#: gitk:11842 msgid "diff old lines" msgstr "பழைய வரிகள் வேறுபாடு" -#: gitk:11846 msgid "Diff: old lines bg" msgstr "வேறுபாடு: பழைய வரிகள் பின்ணனி" -#: gitk:11848 msgid "diff old lines bg" msgstr "பழைய வரிகள் பின்ணனி வேறுபாடு" -#: gitk:11852 msgid "Diff: new lines" msgstr "வேறுபாடு: புதிய கோடுகள்" -#: gitk:11853 msgid "diff new lines" msgstr "புதிய வரிகள் வேறுபாடு" -#: gitk:11857 msgid "Diff: new lines bg" msgstr "வேறுபாடு: புதிய வரிகள் பின்ணனி" -#: gitk:11859 msgid "diff new lines bg" msgstr "புதிய வரிகளை பின்ணனி வேறுபாடு" -#: gitk:11863 msgid "Diff: hunk header" msgstr "வேறுபாடு: அங்க் தலைப்பு" -#: gitk:11865 msgid "diff hunk header" msgstr "அங்க் தலைப்பு வேறுபாடு" -#: gitk:11869 msgid "Marked line bg" msgstr "குறிக்கப்பட்ட வரி பின்னணி" -#: gitk:11871 msgid "marked line background" msgstr "குறிக்கப்பட்ட வரி பின்னணி" -#: gitk:11875 msgid "Select bg" msgstr "பின்னணி தேர்வு" -#: gitk:11884 msgid "Fonts: press to choose" msgstr "எழுத்துருக்கள்: தேர்வு செய்ய அழுத்து" -#: gitk:11886 msgid "Main font" msgstr "முதன்மையான எழுத்துரு" -#: gitk:11887 msgid "Diff display font" msgstr "காட்சி எழுத்துரு வேறுபாடு" -#: gitk:11888 msgid "User interface font" msgstr "பயனர் இடைமுக எழுத்துரு" -#: gitk:11910 msgid "Gitk preferences" msgstr "அறிவிலிகே விருப்பத்தேர்வுகள்" -#: gitk:11919 msgid "General" msgstr "பொது" -#: gitk:11920 msgid "Colors" msgstr "நிறங்கள்" -#: gitk:11921 msgid "Fonts" msgstr "எழுத்துருக்கள்" -#: gitk:11971 #, tcl-format msgid "Gitk: choose color for %s" msgstr "அறிவிலிகே: %s க்கு வண்ணத்தைத் தேர்வுசெய்க" -#: gitk:12490 msgid "" "Sorry, gitk cannot run with this version of Tcl/Tk.\n" " Gitk requires at least Tcl/Tk 8.4." @@ -1437,16 +1101,13 @@ msgstr "" "மன்னிக்கவும், டிசிஎல்/டிகேயின் இந்த பதிப்பைக் கொண்டு அறிவிலிகே இயக்க முடியாது. \n" "அறிவிலிகேவுக்கு குறைந்தபட்சம் டிசிஎல்/டிகே 8.4 தேவைப்படுகிறது." -#: gitk:12711 msgid "Cannot find a git repository here." msgstr "இங்கே ஒரு அறிவிலி களஞ்சியத்தைக் கண்டுபிடிக்க முடியவில்லை." -#: gitk:12758 #, tcl-format msgid "Ambiguous argument '%s': both revision and filename" msgstr "தெளிவற்ற வாதம் '%s': திருத்தம் மற்றும் கோப்பு பெயர்" -#: gitk:12770 msgid "Bad arguments to gitk:" msgstr "அறிவிலிகேவிற்கு மோசமான வாதங்கள்:" diff --git a/gitk-git/po/vi.po b/gitk-git/po/vi.po index 5967498660..52c9d09a09 100644 --- a/gitk-git/po/vi.po +++ b/gitk-git/po/vi.po @@ -5,7 +5,7 @@ # msgid "" msgstr "" -"Project-Id-Version: gitk @@GIT_VERSION@@\n" +"Project-Id-Version: Gitk @@GIT_VERSION@@\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-05-17 14:32+1000\n" "PO-Revision-Date: 2015-09-15 07:33+0700\n" @@ -18,32 +18,25 @@ msgstr "" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Gtranslator 2.91.7\n" -#: gitk:140 msgid "Couldn't get list of unmerged files:" msgstr "Không thể lấy danh sách các tập-tin chưa được hòa trộn:" -#: gitk:212 gitk:2381 msgid "Color words" msgstr "Tô màu chữ" -#: gitk:217 gitk:2381 gitk:8220 gitk:8253 msgid "Markup words" msgstr "Đánh dấu chữ" -#: gitk:324 msgid "Error parsing revisions:" msgstr "Gặp lỗi khi phân tích điểm xét duyệt:" -#: gitk:380 msgid "Error executing --argscmd command:" msgstr "Gặp lỗi khi thực hiện lệnh --argscmd:" -#: gitk:393 msgid "No files selected: --merge specified but no files are unmerged." msgstr "" "Chưa chọn tập tin: --merge đã chỉ định nhưng không có tập tin chưa hòa trộn." -#: gitk:396 msgid "" "No files selected: --merge specified but no unmerged files are within file " "limit." @@ -51,314 +44,234 @@ msgstr "" "Chưa chọn tập tin: --merge đã chỉ định nhưng không có tập tin chưa hòa trộn " "trong giới hạn tập tin." -#: gitk:418 gitk:566 msgid "Error executing git log:" msgstr "Gặp lỗi khi thực hiện lệnh git log:" -#: gitk:436 gitk:582 msgid "Reading" msgstr "Đang đọc" -#: gitk:496 gitk:4525 msgid "Reading commits..." msgstr "Đang đọc các lần chuyển giao…" -#: gitk:499 gitk:1637 gitk:4528 msgid "No commits selected" msgstr "Chưa chọn các lần chuyển giao" -#: gitk:1445 gitk:4045 gitk:12432 msgid "Command line" msgstr "Dòng lệnh" -#: gitk:1511 msgid "Can't parse git log output:" msgstr "Không thể phân tích kết xuất từ lệnh git log:" -#: gitk:1740 msgid "No commit information available" msgstr "Không có thông tin về lần chuyển giao nào" -#: gitk:1903 gitk:1932 gitk:4315 gitk:9669 gitk:11241 gitk:11521 msgid "OK" msgstr "Đồng ý" -#: gitk:1934 gitk:4317 gitk:9196 gitk:9275 gitk:9391 gitk:9440 gitk:9671 -#: gitk:11242 gitk:11522 msgid "Cancel" msgstr "Thôi" -#: gitk:2069 msgid "&Update" msgstr "Cập nhật" -#: gitk:2070 msgid "&Reload" msgstr "Tải lại" -#: gitk:2071 msgid "Reread re&ferences" msgstr "Đọc lại tham chiếu" -#: gitk:2072 msgid "&List references" msgstr "Liệt kê các tham chiếu" -#: gitk:2074 msgid "Start git &gui" msgstr "Khởi chạy git gui" -#: gitk:2076 msgid "&Quit" msgstr "Thoát" -#: gitk:2068 msgid "&File" msgstr "Chính" -#: gitk:2080 msgid "&Preferences" msgstr "Tùy thích" -#: gitk:2079 msgid "&Edit" msgstr "Chỉnh sửa" -#: gitk:2084 msgid "&New view..." msgstr "Thêm trình bày mới…" -#: gitk:2085 msgid "&Edit view..." msgstr "Sửa cách trình bày…" -#: gitk:2086 msgid "&Delete view" msgstr "Xóa cách trình bày" -#: gitk:2088 gitk:4043 msgid "&All files" msgstr "Mọi tập tin" -#: gitk:2083 gitk:4067 msgid "&View" msgstr "Trình bày" -#: gitk:2093 gitk:2103 gitk:3012 msgid "&About gitk" msgstr "Giới thiệu về gitk" -#: gitk:2094 gitk:2108 msgid "&Key bindings" msgstr "Tổ hợp phím" -#: gitk:2092 gitk:2107 msgid "&Help" msgstr "Trợ giúp" -#: gitk:2185 gitk:8652 msgid "SHA1 ID:" msgstr "SHA1 ID:" -#: gitk:2229 msgid "Row" msgstr "Hàng" -#: gitk:2267 msgid "Find" msgstr "Tìm" -#: gitk:2295 msgid "commit" msgstr "lần chuyển giao" -#: gitk:2299 gitk:2301 gitk:4687 gitk:4710 gitk:4734 gitk:6755 gitk:6827 -#: gitk:6912 msgid "containing:" msgstr "có chứa:" -#: gitk:2302 gitk:3526 gitk:3531 gitk:4763 msgid "touching paths:" msgstr "đang chạm đường dẫn:" -#: gitk:2303 gitk:4777 msgid "adding/removing string:" msgstr "thêm/gỡ bỏ chuỗi:" -#: gitk:2304 gitk:4779 msgid "changing lines matching:" msgstr "những dòng thay đổi khớp mẫu:" -#: gitk:2313 gitk:2315 gitk:4766 msgid "Exact" msgstr "Chính xác" -#: gitk:2315 gitk:4854 gitk:6723 msgid "IgnCase" msgstr "BquaHt" -#: gitk:2315 gitk:4736 gitk:4852 gitk:6719 msgid "Regexp" msgstr "BTCQ" -#: gitk:2317 gitk:2318 gitk:4874 gitk:4904 gitk:4911 gitk:6848 gitk:6916 msgid "All fields" msgstr "Mọi trường" -#: gitk:2318 gitk:4871 gitk:4904 gitk:6786 msgid "Headline" msgstr "Nội dung chính" -#: gitk:2319 gitk:4871 gitk:6786 gitk:6916 gitk:7389 msgid "Comments" msgstr "Ghi chú" -#: gitk:2319 gitk:4871 gitk:4876 gitk:4911 gitk:6786 gitk:7324 gitk:8830 -#: gitk:8845 msgid "Author" msgstr "Tác giả" -#: gitk:2319 gitk:4871 gitk:6786 gitk:7326 msgid "Committer" msgstr "Người chuyển giao" -#: gitk:2350 msgid "Search" msgstr "Tìm kiếm" -#: gitk:2358 msgid "Diff" msgstr "So sánh" -#: gitk:2360 msgid "Old version" msgstr "Phiên bản cũ" -#: gitk:2362 msgid "New version" msgstr "Phiên bản mới" -#: gitk:2364 msgid "Lines of context" msgstr "Các dòng của nội dung" -#: gitk:2374 msgid "Ignore space change" msgstr "Không xét đến thay đổi do khoảng trắng" -#: gitk:2378 gitk:2380 gitk:7959 gitk:8206 msgid "Line diff" msgstr "Khác biệt theo dòng" -#: gitk:2445 msgid "Patch" msgstr "Vá" -#: gitk:2447 msgid "Tree" msgstr "Cây" -#: gitk:2617 gitk:2637 msgid "Diff this -> selected" msgstr "So sánh cái này -> cái đã chọn" -#: gitk:2618 gitk:2638 msgid "Diff selected -> this" msgstr "So sánh cái đã chọn -> cái này" -#: gitk:2619 gitk:2639 msgid "Make patch" msgstr "Tạo miếng vá" -#: gitk:2620 gitk:9254 msgid "Create tag" msgstr "Tạo thẻ" -#: gitk:2621 gitk:9371 msgid "Write commit to file" msgstr "Ghi lần chuyển giao ra tập tin" -#: gitk:2622 gitk:9428 msgid "Create new branch" msgstr "Tạo nhánh mới" -#: gitk:2623 msgid "Cherry-pick this commit" msgstr "Cherry-pick lần chuyển giao này" -#: gitk:2624 msgid "Reset HEAD branch to here" msgstr "Đặt lại HEAD của nhánh vào đây" -#: gitk:2625 msgid "Mark this commit" msgstr "Đánh dấu lần chuyển giao này" -#: gitk:2626 msgid "Return to mark" msgstr "Quay lại vị trí dấu" -#: gitk:2627 msgid "Find descendant of this and mark" msgstr "Tìm con cháu của cái này và cái đã đánh dấu" -#: gitk:2628 msgid "Compare with marked commit" msgstr "So sánh với lần chuyển giao đã đánh dấu" -#: gitk:2629 gitk:2640 msgid "Diff this -> marked commit" msgstr "So sánh cái này -> lần chuyển giao đã đánh dấu" -#: gitk:2630 gitk:2641 msgid "Diff marked commit -> this" msgstr "So sánh lần chuyển giao đã đánh dấu -> cái này" -#: gitk:2631 msgid "Revert this commit" msgstr "Hoàn lại lần chuyển giao này" -#: gitk:2647 msgid "Check out this branch" msgstr "Lấy ra nhánh này" -#: gitk:2648 msgid "Remove this branch" msgstr "Gỡ bỏ nhánh này" -#: gitk:2649 msgid "Copy branch name" msgstr "Chép tên nhánh" -#: gitk:2656 msgid "Highlight this too" msgstr "Cũng tô sáng nó" -#: gitk:2657 msgid "Highlight this only" msgstr "Chỉ tô sáng cái này" -#: gitk:2658 msgid "External diff" msgstr "diff từ bên ngoài" -#: gitk:2659 msgid "Blame parent commit" msgstr "Xem công trạng lần chuyển giao cha mẹ" -#: gitk:2660 msgid "Copy path" msgstr "Chép đường dẫn" -#: gitk:2667 msgid "Show origin of this line" msgstr "Hiển thị nguyên gốc của dòng này" -#: gitk:2668 msgid "Run git gui blame on this line" msgstr "Chạy lệnh git gui blame cho dòng này" -#: gitk:3014 msgid "" "\n" "Gitk - a commit viewer for git\n" @@ -374,318 +287,246 @@ msgstr "" "\n" "Dùng và phân phối lại phần mềm này theo các điều khoản của Giấy Phép Công GNU" -#: gitk:3022 gitk:3089 gitk:9857 msgid "Close" msgstr "Đóng" -#: gitk:3043 msgid "Gitk key bindings" msgstr "Tổ hợp phím gitk" -#: gitk:3046 msgid "Gitk key bindings:" msgstr "Tổ hợp phím gitk:" -#: gitk:3048 #, tcl-format msgid "<%s-Q>\t\tQuit" msgstr "<%s-Q>\t\tThoát" -#: gitk:3049 #, tcl-format msgid "<%s-W>\t\tClose window" msgstr "<%s-W>\t\tĐóng cửa sổ" -#: gitk:3050 msgid "<Home>\t\tMove to first commit" msgstr "<Home>\t\tChuyển đến lần chuyển giao đầu tiên" -#: gitk:3051 msgid "<End>\t\tMove to last commit" msgstr "<End>\t\tChuyển đến lần chuyển giao cuối" -#: gitk:3052 msgid "<Up>, p, k\tMove up one commit" msgstr "<Up>, p, k\tDi chuyển lên một lần chuyển giao" -#: gitk:3053 msgid "<Down>, n, j\tMove down one commit" msgstr "<Down>, n, j\tDi chuyển xuống một lần chuyển giao" -#: gitk:3054 msgid "<Left>, z, h\tGo back in history list" msgstr "<Left>, z, h\tQuay trở lại danh sách lịch sử" -#: gitk:3055 msgid "<Right>, x, l\tGo forward in history list" msgstr "<Right>, x, l\tDi chuyển tiếp trong danh sách lịch sử" -#: gitk:3056 #, tcl-format msgid "<%s-n>\tGo to n-th parent of current commit in history list" msgstr "" "<%s-n>\tĐến cha thứ n của lần chuyển giao hiện tại trong danh sách lịch sử" -#: gitk:3057 msgid "<PageUp>\tMove up one page in commit list" msgstr "<PageUp>\tDi chuyển lên một trang trong danh sách lần chuyển giao" -#: gitk:3058 msgid "<PageDown>\tMove down one page in commit list" msgstr "<PageDown>\tDi chuyển xuống một trang trong danh sách lần chuyển giao" -#: gitk:3059 #, tcl-format msgid "<%s-Home>\tScroll to top of commit list" msgstr "<%s-Home>\tCuộn lên trên cùng của danh sách lần chuyển giao" -#: gitk:3060 #, tcl-format msgid "<%s-End>\tScroll to bottom of commit list" msgstr "<%s-End>\tCuộn xuống dưới cùng của danh sách lần chuyển giao" -#: gitk:3061 #, tcl-format msgid "<%s-Up>\tScroll commit list up one line" msgstr "<%s-Up>\tCuộn danh sách lần chuyển giao lên một dòng" -#: gitk:3062 #, tcl-format msgid "<%s-Down>\tScroll commit list down one line" msgstr "<%s-Down>\tCuộn danh sách lần chuyển giao xuống một dòng" -#: gitk:3063 #, tcl-format msgid "<%s-PageUp>\tScroll commit list up one page" msgstr "<%s-PageUp>\tCuộn danh sách lần chuyển giao lên một trang" -#: gitk:3064 #, tcl-format msgid "<%s-PageDown>\tScroll commit list down one page" msgstr "<%s-PageDown>\tCuộn danh sách lần chuyển giao xuống một trang" -#: gitk:3065 msgid "<Shift-Up>\tFind backwards (upwards, later commits)" msgstr "<Shift-Up>\tTìm về phía sau (hướng lên trên, lần chuyển giao sau này)" -#: gitk:3066 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)" msgstr "" "<Shift-Down>\tTìm về phía trước (hướng xuống dưới, lần chuyển giao trước đây)" -#: gitk:3067 msgid "<Delete>, b\tScroll diff view up one page" msgstr "<Delete>, b\tCuộn phần trình bày diff lên một trang" -#: gitk:3068 msgid "<Backspace>\tScroll diff view up one page" msgstr "<Backspace>\tCuộn phần trình bày diff lên một trang" -#: gitk:3069 msgid "<Space>\t\tScroll diff view down one page" msgstr "<Space>\t\tCuộn phần trình bày diff xuống một trang" -#: gitk:3070 msgid "u\t\tScroll diff view up 18 lines" msgstr "u\t\tCuộn phần trình bày diff lên 18 dòng" -#: gitk:3071 msgid "d\t\tScroll diff view down 18 lines" msgstr "d\t\tCuộn phần trình bày diff xuống 18 dòng" -#: gitk:3072 #, tcl-format msgid "<%s-F>\t\tFind" msgstr "<%s-F>\t\tTìm kiếm" -#: gitk:3073 #, tcl-format msgid "<%s-G>\t\tMove to next find hit" msgstr "<%s-G>\t\tDi chuyển đến chỗ gặp kế tiếp" -#: gitk:3074 msgid "<Return>\tMove to next find hit" msgstr "<Return>\t\tDi chuyển đến chỗ gặp kế tiếp" -#: gitk:3075 msgid "g\t\tGo to commit" msgstr "g\t\tChuyển đến lần chuyển giao" -#: gitk:3076 msgid "/\t\tFocus the search box" msgstr "/\t\tĐưa con trỏ chuột vào ô tìm kiếm" -#: gitk:3077 msgid "?\t\tMove to previous find hit" msgstr "?\t\tDi chuyển đến chỗ gặp kế trước" -#: gitk:3078 msgid "f\t\tScroll diff view to next file" msgstr "f\t\tCuộn phần trình bày diff sang tập-tin kế" -#: gitk:3079 #, tcl-format msgid "<%s-S>\t\tSearch for next hit in diff view" msgstr "<%s-S>\t\tTìm đến chỗ khác biệt kế tiếp" -#: gitk:3080 #, tcl-format msgid "<%s-R>\t\tSearch for previous hit in diff view" msgstr "<%s-R>\t\tTìm đến chỗ khác biệt kế trước" -#: gitk:3081 #, tcl-format msgid "<%s-KP+>\tIncrease font size" msgstr "<%s-KP+>\tTăng cỡ chữ" -#: gitk:3082 #, tcl-format msgid "<%s-plus>\tIncrease font size" msgstr "<%s-plus>\tTăng cỡ chữ" -#: gitk:3083 #, tcl-format msgid "<%s-KP->\tDecrease font size" msgstr "<%s-KP->\tGiảm cỡ chữ" -#: gitk:3084 #, tcl-format msgid "<%s-minus>\tDecrease font size" msgstr "<%s-minus>\tGiảm cỡ chữ" -#: gitk:3085 msgid "<F5>\t\tUpdate" msgstr "<F5>\t\tCập nhật" -#: gitk:3550 gitk:3559 #, tcl-format msgid "Error creating temporary directory %s:" msgstr "Gặp lỗi khi tạo thư mục tạm %s:" -#: gitk:3572 #, tcl-format msgid "Error getting \"%s\" from %s:" msgstr "Lỗi chào hỏi \"%s\" từ %s:" -#: gitk:3635 msgid "command failed:" msgstr "lệnh gặp lỗi:" -#: gitk:3784 msgid "No such commit" msgstr "Không có lần chuyển giao như vậy" -#: gitk:3798 msgid "git gui blame: command failed:" msgstr "git gui blame: lệnh gặp lỗi:" -#: gitk:3829 #, tcl-format msgid "Couldn't read merge head: %s" msgstr "Không thể độc đầu của hòa trộn: %s" # tcl-format -#: gitk:3837 #, tcl-format msgid "Error reading index: %s" msgstr "Gặp lỗi khi đọc chỉ mục: %s" -#: gitk:3862 #, tcl-format msgid "Couldn't start git blame: %s" msgstr "Không thể khởi chạy git blame: %s" -#: gitk:3865 gitk:6754 msgid "Searching" msgstr "Đang tìm kiếm" -#: gitk:3897 #, tcl-format msgid "Error running git blame: %s" msgstr "Gặp lỗi khi chạy git blame: %s" -#: gitk:3925 #, tcl-format msgid "That line comes from commit %s, which is not in this view" msgstr "Dòng đến từ lần chuyển giao %s, cái mà không trong trình bày này" -#: gitk:3939 msgid "External diff viewer failed:" msgstr "Bộ trình bày diff từ bên ngoài gặp lỗi:" -#: gitk:4070 msgid "Gitk view definition" msgstr "Định nghĩa cách trình bày gitk" -#: gitk:4074 msgid "Remember this view" msgstr "Nhớ cách trình bày này" -#: gitk:4075 msgid "References (space separated list):" msgstr "Tham chiếu (danh sách ngăn cách bằng dấu cách):" -#: gitk:4076 msgid "Branches & tags:" msgstr "Nhánh & thẻ:" -#: gitk:4077 msgid "All refs" msgstr "Mọi tham chiếu" -#: gitk:4078 msgid "All (local) branches" msgstr "Mọi nhánh (nội bộ)" -#: gitk:4079 msgid "All tags" msgstr "Mọi thẻ" -#: gitk:4080 msgid "All remote-tracking branches" msgstr "Mọi nhánh remote-tracking" -#: gitk:4081 msgid "Commit Info (regular expressions):" msgstr "Thông tin chuyển giao (biểu thức chính quy):" -#: gitk:4082 msgid "Author:" msgstr "Tác giả:" -#: gitk:4083 msgid "Committer:" msgstr "Người chuyển giao:" -#: gitk:4084 msgid "Commit Message:" msgstr "Chú thích của lần chuyển giao:" -#: gitk:4085 msgid "Matches all Commit Info criteria" msgstr "Khớp mọi điều kiện Thông tin Chuyển giao" -#: gitk:4086 msgid "Matches no Commit Info criteria" msgstr "Khớp không điều kiện Thông tin Chuyển giao" -#: gitk:4087 msgid "Changes to Files:" msgstr "Đổi thành Tập tin:" -#: gitk:4088 msgid "Fixed String" msgstr "Chuỗi cố định" -#: gitk:4089 msgid "Regular Expression" msgstr "Biểu thức chính quy" -#: gitk:4090 msgid "Search string:" msgstr "Chuỗi tìm kiếm:" -#: gitk:4091 msgid "" "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" @@ -693,203 +534,155 @@ msgstr "" "Ngày chuyển giao (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" -#: gitk:4092 msgid "Since:" msgstr "Kể từ:" -#: gitk:4093 msgid "Until:" msgstr "Đến:" -#: gitk:4094 msgid "Limit and/or skip a number of revisions (positive integer):" msgstr "Giới hạn và/hoặc bỏ số của điểm xét (số nguyên âm):" -#: gitk:4095 msgid "Number to show:" msgstr "Số lượng hiển thị:" -#: gitk:4096 msgid "Number to skip:" msgstr "Số lượng sẽ bỏ qua:" -#: gitk:4097 msgid "Miscellaneous options:" msgstr "Tùy chọn hỗn hợp:" -#: gitk:4098 msgid "Strictly sort by date" msgstr "Sắp xếp chặt chẽ theo ngày" -#: gitk:4099 msgid "Mark branch sides" msgstr "Đánh dấu các cạnh nhánh" -#: gitk:4100 msgid "Limit to first parent" msgstr "Giới hạn thành cha mẹ đầu tiên" -#: gitk:4101 msgid "Simple history" msgstr "Lịch sử dạng đơn giản" -#: gitk:4102 msgid "Additional arguments to git log:" msgstr "Đối số bổ xung cho lệnh git log:" -#: gitk:4103 msgid "Enter files and directories to include, one per line:" msgstr "Nhập vào các tập tin và thư mục bao gồm, mỗi dòng một cái:" -#: gitk:4104 msgid "Command to generate more commits to include:" msgstr "Lệnh tạo ra nhiều lần chuyển giao hơn bao gồm:" -#: gitk:4228 msgid "Gitk: edit view" msgstr "Gitk: sửa cách trình bày" -#: gitk:4236 msgid "-- criteria for selecting revisions" msgstr "-- tiêu chuẩn chọn điểm xét duyệt" -#: gitk:4241 msgid "View Name" msgstr "Tên cách trình bày" -#: gitk:4316 msgid "Apply (F5)" msgstr "Áp dụng (F5)" -#: gitk:4354 msgid "Error in commit selection arguments:" msgstr "Lỗi trong các đối số chọn chuyển giao:" -#: gitk:4409 gitk:4462 gitk:4924 gitk:4938 gitk:6208 gitk:12373 gitk:12374 msgid "None" msgstr "Không" -#: gitk:5021 gitk:5026 msgid "Descendant" msgstr "Con cháu" -#: gitk:5022 msgid "Not descendant" msgstr "Không có con cháu" -#: gitk:5029 gitk:5034 msgid "Ancestor" msgstr "Tổ tiên chung" -#: gitk:5030 msgid "Not ancestor" msgstr "Không có chung tổ tiên" -#: gitk:5324 msgid "Local changes checked in to index but not committed" msgstr "" "Có thay đổi nội bộ đã được đưa vào bảng mục lục, nhưng chưa được chuyển giao" -#: gitk:5360 msgid "Local uncommitted changes, not checked in to index" msgstr "Có thay đổi nội bộ, nhưng chưa được đưa vào bảng mục lục" -#: gitk:7134 msgid "and many more" msgstr "và nhiều nữa" -#: gitk:7137 msgid "many" msgstr "nhiều" -#: gitk:7328 msgid "Tags:" msgstr "Thẻ:" -#: gitk:7345 gitk:7351 gitk:8825 msgid "Parent" msgstr "Cha" -#: gitk:7356 msgid "Child" msgstr "Con" -#: gitk:7365 msgid "Branch" msgstr "Nhánh" -#: gitk:7368 msgid "Follows" msgstr "Đứng sau" -#: gitk:7371 msgid "Precedes" msgstr "Đứng trước" # tcl-format -#: gitk:7966 #, tcl-format msgid "Error getting diffs: %s" msgstr "Lỗi lấy diff: %s" -#: gitk:8650 msgid "Goto:" msgstr "Nhảy tới:" -#: gitk:8671 #, tcl-format msgid "Short SHA1 id %s is ambiguous" msgstr "Định danh SHA1 dạng ngắn %s là chưa đủ rõ ràng" -#: gitk:8678 #, tcl-format msgid "Revision %s is not known" msgstr "Không hiểu điểm xét duyệt %s" -#: gitk:8688 #, tcl-format msgid "SHA1 id %s is not known" msgstr "Không hiểu định danh SHA1 %s" -#: gitk:8690 #, tcl-format msgid "Revision %s is not in the current view" msgstr "Điểm %s không ở trong phần hiển thị hiện tại" -#: gitk:8832 gitk:8847 msgid "Date" msgstr "Ngày" -#: gitk:8835 msgid "Children" msgstr "Con cháu" -#: gitk:8898 #, tcl-format msgid "Reset %s branch to here" msgstr "Đặt lại nhánh %s tại đây" -#: gitk:8900 msgid "Detached head: can't reset" msgstr "Head đã bị tách rời: không thể đặt lại" -#: gitk:9005 gitk:9011 msgid "Skipping merge commit " msgstr "Bỏ qua lần chuyển giao hòa trộn " -#: gitk:9020 gitk:9025 msgid "Error getting patch ID for " msgstr "Gặp lỗi khi lấy ID miếng vá cho " -#: gitk:9021 gitk:9026 msgid " - stopping\n" msgstr " - dừng\n" -#: gitk:9031 gitk:9034 gitk:9042 gitk:9056 gitk:9065 msgid "Commit " msgstr "Commit " -#: gitk:9035 msgid "" " is the same patch as\n" " " @@ -897,7 +690,6 @@ msgstr "" " là cùng một miếng vá với\n" " " -#: gitk:9043 msgid "" " differs from\n" " " @@ -905,7 +697,6 @@ msgstr "" " khác biệt từ\n" " " -#: gitk:9045 msgid "" "Diff of commits:\n" "\n" @@ -913,131 +704,101 @@ msgstr "" "Khác biệt của lần chuyển giao (commit):\n" "\n" -#: gitk:9057 gitk:9066 #, tcl-format msgid " has %s children - stopping\n" msgstr " có %s con - dừng\n" -#: gitk:9085 #, tcl-format msgid "Error writing commit to file: %s" msgstr "Gặp lỗi trong quá trình ghi lần chuyển giao vào tập tin: %s" -#: gitk:9091 #, tcl-format msgid "Error diffing commits: %s" msgstr "Gặp lỗi khi so sánh sự khác biệt giữa các lần chuyển giao: %s" -#: gitk:9137 msgid "Top" msgstr "Đỉnh" -#: gitk:9138 msgid "From" msgstr "Từ" -#: gitk:9143 msgid "To" msgstr "Đến" -#: gitk:9167 msgid "Generate patch" msgstr "Tạo miếng vá" -#: gitk:9169 msgid "From:" msgstr "Từ:" -#: gitk:9178 msgid "To:" msgstr "Đến:" -#: gitk:9187 msgid "Reverse" msgstr "Đảo ngược" -#: gitk:9189 gitk:9385 msgid "Output file:" msgstr "Tập tin kết xuất:" -#: gitk:9195 msgid "Generate" msgstr "Tạo" -#: gitk:9233 msgid "Error creating patch:" msgstr "Gặp lỗi khi tạo miếng vá:" -#: gitk:9256 gitk:9373 gitk:9430 msgid "ID:" msgstr "Mã số:" -#: gitk:9265 msgid "Tag name:" msgstr "Tên thẻ:" -#: gitk:9268 msgid "Tag message is optional" msgstr "Ghi chú thẻ chỉ là tùy chọn" -#: gitk:9270 msgid "Tag message:" msgstr "Ghi chú cho thẻ:" -#: gitk:9274 gitk:9439 msgid "Create" msgstr "Tạo" -#: gitk:9292 msgid "No tag name specified" msgstr "Chưa chỉ ra tên của thẻ" -#: gitk:9296 #, tcl-format msgid "Tag \"%s\" already exists" msgstr "Thẻ “%s” đã có sẵn rồi" -#: gitk:9306 msgid "Error creating tag:" msgstr "Gặp lỗi khi tạo thẻ:" -#: gitk:9382 msgid "Command:" msgstr "Lệnh:" -#: gitk:9390 msgid "Write" msgstr "Ghi" -#: gitk:9408 msgid "Error writing commit:" msgstr "Gặp lỗi trong quá trình ghi chuyển giao:" -#: gitk:9435 msgid "Name:" msgstr "Tên:" -#: gitk:9458 msgid "Please specify a name for the new branch" msgstr "Vui lòng chỉ định tên cho nhánh mới" -#: gitk:9463 #, tcl-format msgid "Branch '%s' already exists. Overwrite?" msgstr "Nhánh “%s” đã có từ trước rồi. Ghi đè?" -#: gitk:9530 #, tcl-format msgid "Commit %s is already included in branch %s -- really re-apply it?" msgstr "" "Lần chuyển giao %s đã sẵn được bao gồm trong nhánh %s -- bạn có thực sự muốn " "áp dụng lại nó không?" -#: gitk:9535 msgid "Cherry-picking" msgstr "Đang cherry-pick" -#: gitk:9544 #, tcl-format msgid "" "Cherry-pick failed because of local changes to file '%s'.\n" @@ -1046,7 +807,6 @@ msgstr "" "Cherry-pick gặp lỗi bởi vì các thay đổi nội bộ tập tin “%s”.\n" "Xin hãy chuyển giao, reset hay stash các thay đổi của bạn sau đó thử lại." -#: gitk:9550 msgid "" "Cherry-pick failed because of merge conflict.\n" "Do you wish to run git citool to resolve it?" @@ -1054,22 +814,18 @@ msgstr "" "Cherry-pick gặp lỗi bởi vì xung đột trong hòa trộn.\n" "Bạn có muốn chạy lệnh “git citool” để giải quyết vấn đề này không?" -#: gitk:9566 gitk:9624 msgid "No changes committed" msgstr "Không có thay đổi nào cần chuyển giao" -#: gitk:9593 #, tcl-format msgid "Commit %s is not included in branch %s -- really revert it?" msgstr "" "Lần chuyển giao %s không được bao gồm trong nhánh %s -- bạn có thực sự muốn " "“revert” nó không?" -#: gitk:9598 msgid "Reverting" msgstr "Đang hoàn tác" -#: gitk:9606 #, tcl-format msgid "" "Revert failed because of local changes to the following files:%s Please " @@ -1078,7 +834,6 @@ msgstr "" "Revert gặp lỗi bởi vì tập tin sau đã được thay đổi nội bộ:%s\n" "Xin hãy chạy lệnh “commit”, “reset” hoặc “stash” rồi thử lại." -#: gitk:9610 msgid "" "Revert failed because of merge conflict.\n" " Do you wish to run git citool to resolve it?" @@ -1086,29 +841,23 @@ msgstr "" "Revert gặp lỗi bởi vì xung đột hòa trộn.\n" " Bạn có muốn chạy lệnh “git citool” để phân giải nó không?" -#: gitk:9653 msgid "Confirm reset" msgstr "Xác nhật đặt lại" -#: gitk:9655 #, tcl-format msgid "Reset branch %s to %s?" msgstr "Đặt lại nhánh “%s” thành “%s”?" -#: gitk:9657 msgid "Reset type:" msgstr "Kiểu đặt lại:" -#: gitk:9660 msgid "Soft: Leave working tree and index untouched" msgstr "Mềm: Không động đến thư mục làm việc và bảng mục lục" -#: gitk:9663 msgid "Mixed: Leave working tree untouched, reset index" msgstr "" "Pha trộn: Không động chạm đến thư mục làm việc nhưng đặt lại bảng mục lục" -#: gitk:9666 msgid "" "Hard: Reset working tree and index\n" "(discard ALL local changes)" @@ -1116,19 +865,15 @@ msgstr "" "Hard: Đặt lại cây làm việc và mục lục\n" "(hủy bỏ MỌI thay đổi nội bộ)" -#: gitk:9683 msgid "Resetting" msgstr "Đang đặt lại" -#: gitk:9743 msgid "Checking out" msgstr "Đang checkout" -#: gitk:9796 msgid "Cannot delete the currently checked-out branch" msgstr "Không thể xóa nhánh hiện tại đang được lấy ra" -#: gitk:9802 #, tcl-format msgid "" "The commits on branch %s aren't on any other branch.\n" @@ -1137,16 +882,13 @@ msgstr "" "Các lần chuyển giao trên nhánh %s không ở trên nhánh khác.\n" "Thực sự muốn xóa nhánh %s?" -#: gitk:9833 #, tcl-format msgid "Tags and heads: %s" msgstr "Thẻ và Đầu: %s" -#: gitk:9850 msgid "Filter" msgstr "Bộ lọc" -#: gitk:10146 msgid "" "Error reading commit topology information; branch and preceding/following " "tag information will be incomplete." @@ -1154,201 +896,152 @@ msgstr "" "Gặp lỗi khi đọc thông tin hình học lần chuyển giao; thông tin nhánh và thẻ " "trước/sau sẽ không hoàn thiện." -#: gitk:11123 msgid "Tag" msgstr "Thẻ" -#: gitk:11127 msgid "Id" msgstr "Id" -#: gitk:11210 msgid "Gitk font chooser" msgstr "Hộp thoại chọn phông Gitk" -#: gitk:11227 msgid "B" msgstr "B" -#: gitk:11230 msgid "I" msgstr "I" -#: gitk:11348 msgid "Commit list display options" msgstr "Các tùy chọn về hiển thị danh sách lần chuyển giao" -#: gitk:11351 msgid "Maximum graph width (lines)" msgstr "Độ rộng biểu đồ tối đa (dòng)" -#: gitk:11355 #, no-tcl-format msgid "Maximum graph width (% of pane)" msgstr "Độ rộng đồ thị tối đa (% của bảng)" -#: gitk:11358 msgid "Show local changes" msgstr "Hiển thị các thay đổi nội bộ" -#: gitk:11361 msgid "Auto-select SHA1 (length)" msgstr "Tự chọn (độ dài) SHA1" -#: gitk:11365 msgid "Hide remote refs" msgstr "Ẩn tham chiếu đến máy chủ" -#: gitk:11369 msgid "Diff display options" msgstr "Các tùy chọn trình bày các khác biệt" -#: gitk:11371 msgid "Tab spacing" msgstr "Khoảng cách tab" -#: gitk:11374 msgid "Display nearby tags/heads" msgstr "Hiển thị các thẻ/đầu xung quanh" -#: gitk:11377 msgid "Maximum # tags/heads to show" msgstr "Số lượng thẻ/đầu tối đa sẽ hiển thị" -#: gitk:11380 msgid "Limit diffs to listed paths" msgstr "Giới hạn các khác biệt cho đường dẫn đã liệt kê" -#: gitk:11383 msgid "Support per-file encodings" msgstr "Hỗ trợ mã hóa mỗi-dòng" -#: gitk:11389 gitk:11536 msgid "External diff tool" msgstr "Công cụ so sánh từ bên ngoài" -#: gitk:11390 msgid "Choose..." msgstr "Chọn…" -#: gitk:11395 msgid "General options" msgstr "Các tùy chọn chung" -#: gitk:11398 msgid "Use themed widgets" msgstr "Dùng các widget chủ đề" -#: gitk:11400 msgid "(change requires restart)" msgstr "(để thay đổi cần khởi động lại)" -#: gitk:11402 msgid "(currently unavailable)" msgstr "(hiện tại không sẵn sàng)" -#: gitk:11413 msgid "Colors: press to choose" msgstr "Màu sắc: bấm vào nút phía dưới để chọn màu" -#: gitk:11416 msgid "Interface" msgstr "Giao diện" -#: gitk:11417 msgid "interface" msgstr "giao diện" -#: gitk:11420 msgid "Background" msgstr "Nền" -#: gitk:11421 gitk:11451 msgid "background" msgstr "nền" -#: gitk:11424 msgid "Foreground" msgstr "Tiền cảnh" -#: gitk:11425 msgid "foreground" msgstr "tiền cảnh" -#: gitk:11428 msgid "Diff: old lines" msgstr "So sánh: dòng cũ" -#: gitk:11429 msgid "diff old lines" msgstr "diff dòng cũ" -#: gitk:11433 msgid "Diff: new lines" msgstr "So sánh: dòng mới" -#: gitk:11434 msgid "diff new lines" msgstr "màu dòng mới" -#: gitk:11438 msgid "Diff: hunk header" msgstr "So sánh: phần đầu của đoạn" -#: gitk:11440 msgid "diff hunk header" msgstr "màu của phần đầu của đoạn khi so sánh" -#: gitk:11444 msgid "Marked line bg" msgstr "Nền dòng đánh dấu" -#: gitk:11446 msgid "marked line background" msgstr "nền dòng được đánh dấu" -#: gitk:11450 msgid "Select bg" msgstr "Màu nền" -#: gitk:11459 msgid "Fonts: press to choose" msgstr "Phông chữ: bấm vào các nút ở dưới để chọn" -#: gitk:11461 msgid "Main font" msgstr "Phông chữ chính" -#: gitk:11462 msgid "Diff display font" msgstr "Phông chữ dùng khi so sánh" -#: gitk:11463 msgid "User interface font" msgstr "Phông chữ giao diện" -#: gitk:11485 msgid "Gitk preferences" msgstr "Cá nhân hóa các cài đặt cho Gitk" -#: gitk:11494 msgid "General" msgstr "Chung" -#: gitk:11495 msgid "Colors" msgstr "Màu sắc" -#: gitk:11496 msgid "Fonts" msgstr "Phông chữ" -#: gitk:11546 #, tcl-format msgid "Gitk: choose color for %s" msgstr "Gitk: chọn màu cho %s" -#: gitk:12059 msgid "" "Sorry, gitk cannot run with this version of Tcl/Tk.\n" " Gitk requires at least Tcl/Tk 8.4." @@ -1356,16 +1049,13 @@ msgstr "" "Rất tiếc, gitk không thể chạy Tcl/Tk phiên bản này.\n" " Gitk cần ít nhất là Tcl/Tk 8.4." -#: gitk:12269 msgid "Cannot find a git repository here." msgstr "Không thể tìm thấy kho git ở đây." -#: gitk:12316 #, tcl-format msgid "Ambiguous argument '%s': both revision and filename" msgstr "Đối số “%s” chưa rõ ràng: vừa là điểm xét duyệt vừa là tên tập tin" -#: gitk:12328 msgid "Bad arguments to gitk:" msgstr "Đối số cho gitk không hợp lệ:" diff --git a/gitk-git/po/zh_cn.po b/gitk-git/po/zh_cn.po index 17b7f899da..71a9878318 100644 --- a/gitk-git/po/zh_cn.po +++ b/gitk-git/po/zh_cn.po @@ -2,10 +2,9 @@ # # Translators: # YanKe <imyanke@163.com>, 2017 - msgid "" msgstr "" -"Project-Id-Version: Git Chinese Localization Project\n" +"Project-Id-Version: Gitk Chinese Localization Project\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-02-28 23:11+0800\n" "PO-Revision-Date: 2017-03-11 02:27+0800\n" @@ -16,356 +15,266 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Language: zh_CN\n" -#: gitk:140 msgid "Couldn't get list of unmerged files:" msgstr "不能获取未合并文件列表:" -#: gitk:212 gitk:2403 msgid "Color words" msgstr "着色显示差异" -#: gitk:217 gitk:2403 gitk:8249 gitk:8282 msgid "Markup words" msgstr "标记显示差异" -#: gitk:324 msgid "Error parsing revisions:" msgstr "解析版本错误:" -#: gitk:380 msgid "Error executing --argscmd command:" msgstr "运行 --argscmd命令出错" -#: gitk:393 msgid "No files selected: --merge specified but no files are unmerged." msgstr "没有选中文件:--指定merge参数但没有未合并的文件。" -#: gitk:396 msgid "" "No files selected: --merge specified but no unmerged files are within file " "limit." msgstr "没有选中文件:--指定merge参数但没有未合并的文件在文件中" -#: gitk:418 gitk:566 msgid "Error executing git log:" msgstr "执行git log命令出错:" -#: gitk:436 gitk:582 msgid "Reading" msgstr "读取中" -#: gitk:496 gitk:4549 msgid "Reading commits..." msgstr "提交记录读取中..." -#: gitk:499 gitk:1641 gitk:4552 msgid "No commits selected" msgstr "未选中任何提交" -#: gitk:1449 gitk:4069 gitk:12583 msgid "Command line" msgstr "命令行" -#: gitk:1515 msgid "Can't parse git log output:" msgstr "不能解析git log输出:" -#: gitk:1744 msgid "No commit information available" msgstr "无可用提交信息" -#: gitk:1907 gitk:1936 gitk:4339 gitk:9789 gitk:11388 gitk:11668 msgid "OK" msgstr "确定" -#: gitk:1938 gitk:4341 gitk:9225 gitk:9304 gitk:9434 gitk:9520 gitk:9791 -#: gitk:11389 gitk:11669 msgid "Cancel" msgstr "取消" -#: gitk:2087 msgid "&Update" msgstr "更新" -#: gitk:2088 msgid "&Reload" msgstr "重新加载" -#: gitk:2089 msgid "Reread re&ferences" msgstr "重新读取引用" -#: gitk:2090 msgid "&List references" msgstr "列出引用(分支以及tag)" -#: gitk:2092 msgid "Start git &gui" msgstr "启动git gui客户端" -#: gitk:2094 msgid "&Quit" msgstr "退出" -#: gitk:2086 msgid "&File" msgstr "文件" -#: gitk:2098 msgid "&Preferences" msgstr "偏好设置" -#: gitk:2097 msgid "&Edit" msgstr "编辑" -#: gitk:2102 msgid "&New view..." msgstr "新视图..." -#: gitk:2103 msgid "&Edit view..." msgstr "编辑视图..." -#: gitk:2104 msgid "&Delete view" msgstr "删除视图" -#: gitk:2106 msgid "&All files" msgstr "所有文件" -#: gitk:2101 msgid "&View" msgstr "视图" -#: gitk:2111 gitk:2121 msgid "&About gitk" msgstr "关于gitk" -#: gitk:2112 gitk:2126 msgid "&Key bindings" msgstr "快捷键" -#: gitk:2110 gitk:2125 msgid "&Help" msgstr "帮助" -#: gitk:2203 gitk:8681 msgid "SHA1 ID:" msgstr "SHA1 ID:" -#: gitk:2247 msgid "Row" msgstr "行" -#: gitk:2285 msgid "Find" msgstr "查找" -#: gitk:2313 msgid "commit" msgstr "提交" -#: gitk:2317 gitk:2319 gitk:4711 gitk:4734 gitk:4758 gitk:6779 gitk:6851 -#: gitk:6936 msgid "containing:" msgstr "包含:" -#: gitk:2320 gitk:3550 gitk:3555 gitk:4787 msgid "touching paths:" msgstr "影响路径:" -#: gitk:2321 gitk:4801 msgid "adding/removing string:" msgstr "增加/删除字符串:" -#: gitk:2322 gitk:4803 msgid "changing lines matching:" msgstr "改变行匹配:" -#: gitk:2331 gitk:2333 gitk:4790 msgid "Exact" msgstr "精确匹配" -#: gitk:2333 gitk:4878 gitk:6747 msgid "IgnCase" msgstr "忽略大小写" -#: gitk:2333 gitk:4760 gitk:4876 gitk:6743 msgid "Regexp" msgstr "正则" -#: gitk:2335 gitk:2336 gitk:4898 gitk:4928 gitk:4935 gitk:6872 gitk:6940 msgid "All fields" msgstr "所有字段" -#: gitk:2336 gitk:4895 gitk:4928 gitk:6810 msgid "Headline" msgstr "标题" -#: gitk:2337 gitk:4895 gitk:6810 gitk:6940 gitk:7413 msgid "Comments" msgstr "提交注释" -#: gitk:2337 gitk:4895 gitk:4900 gitk:4935 gitk:6810 gitk:7348 gitk:8859 -#: gitk:8874 msgid "Author" msgstr "作者" -#: gitk:2337 gitk:4895 gitk:6810 gitk:7350 msgid "Committer" msgstr "提交者" -#: gitk:2371 msgid "Search" msgstr "搜索" -#: gitk:2379 msgid "Diff" msgstr "差异" -#: gitk:2381 msgid "Old version" msgstr "老版本" -#: gitk:2383 msgid "New version" msgstr "新版本" -#: gitk:2386 msgid "Lines of context" msgstr "Diff上下文显示行数" -#: gitk:2396 msgid "Ignore space change" msgstr "忽略空格修改" -#: gitk:2400 gitk:2402 gitk:7983 gitk:8235 msgid "Line diff" msgstr "按行显示差异" -#: gitk:2467 msgid "Patch" msgstr "补丁" -#: gitk:2469 msgid "Tree" msgstr "树" -#: gitk:2639 gitk:2660 msgid "Diff this -> selected" msgstr "比较从当前提交到选中提交的差异" -#: gitk:2640 gitk:2661 msgid "Diff selected -> this" msgstr "比较从选中提交到当前提交的差异" -#: gitk:2641 gitk:2662 msgid "Make patch" msgstr "制作补丁" -#: gitk:2642 gitk:9283 msgid "Create tag" msgstr "创建tag" -#: gitk:2643 msgid "Copy commit summary" msgstr "复制提交摘要" -#: gitk:2644 gitk:9414 msgid "Write commit to file" msgstr "写入提交到文件" -#: gitk:2645 msgid "Create new branch" msgstr "创建新分支" -#: gitk:2646 msgid "Cherry-pick this commit" msgstr "在此提交运用补丁(cherry-pick)命令" -#: gitk:2647 msgid "Reset HEAD branch to here" msgstr "将分支头(HEAD)重置到此处" -#: gitk:2648 msgid "Mark this commit" msgstr "标记此提交" -#: gitk:2649 msgid "Return to mark" msgstr "返回到标记" -#: gitk:2650 msgid "Find descendant of this and mark" msgstr "查找本次提交的子提交并标记" -#: gitk:2651 msgid "Compare with marked commit" msgstr "和已标记的提交作比较" -#: gitk:2652 gitk:2663 msgid "Diff this -> marked commit" msgstr "比较从当前提交到已标记提交的差异" -#: gitk:2653 gitk:2664 msgid "Diff marked commit -> this" msgstr "比较从已标记提交到当前提交的差异" -#: gitk:2654 msgid "Revert this commit" msgstr "撤销(revert)此提交" -#: gitk:2670 msgid "Check out this branch" msgstr "检出(checkout)此分支" -#: gitk:2671 msgid "Rename this branch" msgstr "重命名(Rename)此分支" -#: gitk:2672 msgid "Remove this branch" msgstr "删除(Remove)此分支" -#: gitk:2673 msgid "Copy branch name" msgstr "复制分支名称" -#: gitk:2680 msgid "Highlight this too" msgstr "高亮此处" -#: gitk:2681 msgid "Highlight this only" msgstr "只高亮此处" -#: gitk:2682 msgid "External diff" msgstr "外部diff" -#: gitk:2683 msgid "Blame parent commit" msgstr "Blame父提交" -#: gitk:2684 msgid "Copy path" msgstr "复制路径" -#: gitk:2691 msgid "Show origin of this line" msgstr "显示此行原始提交" -#: gitk:2692 msgid "Run git gui blame on this line" msgstr "在此行运行git gui客户端的blame" -#: gitk:3036 msgid "About gitk" msgstr "关于gitk" -#: gitk:3038 msgid "" "\n" "Gitk - a commit viewer for git\n" @@ -373,995 +282,792 @@ msgid "" "Copyright © 2005-2016 Paul Mackerras\n" "\n" "Use and redistribute under the terms of the GNU General Public License" -msgstr "\nGitk — 一个git的提交查看器\n\n© 2005-2016 Paul Mackerras\n\n在GNU许可证下使用以及分发" +msgstr "" +"\n" +"Gitk — 一个git的提交查看器\n" +"\n" +"© 2005-2016 Paul Mackerras\n" +"\n" +"在GNU许可证下使用以及分发" -#: gitk:3046 gitk:3113 gitk:10004 msgid "Close" msgstr "关闭" -#: gitk:3067 msgid "Gitk key bindings" msgstr "Gitk快捷键" -#: gitk:3070 msgid "Gitk key bindings:" msgstr "Gitk快捷键:" -#: gitk:3072 #, tcl-format msgid "<%s-Q>\t\tQuit" msgstr "<%s-Q>\t\t退出" -#: gitk:3073 #, tcl-format msgid "<%s-W>\t\tClose window" msgstr "<%s-W>\t\t关闭窗口" -#: gitk:3074 msgid "<Home>\t\tMove to first commit" msgstr "<Home>\t\t移动到第一次提交" -#: gitk:3075 msgid "<End>\t\tMove to last commit" msgstr "<End>\t\t移动到最后一次提交" -#: gitk:3076 msgid "<Up>, p, k\tMove up one commit" msgstr "<Up>, p, k\t移动到上一次提交" -#: gitk:3077 msgid "<Down>, n, j\tMove down one commit" msgstr "<Down>, n, j\t移动到下一次提交" -#: gitk:3078 msgid "<Left>, z, h\tGo back in history list" msgstr "<Left>, z, h\t历史列表的上一项" -#: gitk:3079 msgid "<Right>, x, l\tGo forward in history list" msgstr "<Right>, x, l\t历史列表的下一项" -#: gitk:3080 #, tcl-format msgid "<%s-n>\tGo to n-th parent of current commit in history list" msgstr "<%s-n>\t在历史列表中前往本次提交的第n个父提交" -#: gitk:3081 msgid "<PageUp>\tMove up one page in commit list" msgstr "<PageUp>\t上一页提交列表" -#: gitk:3082 msgid "<PageDown>\tMove down one page in commit list" msgstr "<PageDown>\t下一页提交列表" -#: gitk:3083 #, tcl-format msgid "<%s-Home>\tScroll to top of commit list" msgstr "<%s-Home>\t滚动到提交列表顶部" -#: gitk:3084 #, tcl-format msgid "<%s-End>\tScroll to bottom of commit list" msgstr "<%s-End>\t滚动到提交列表底部" -#: gitk:3085 #, tcl-format msgid "<%s-Up>\tScroll commit list up one line" msgstr "<%s-Up>\t向上滚动一行提交列表" -#: gitk:3086 #, tcl-format msgid "<%s-Down>\tScroll commit list down one line" msgstr "<%s-Down>\t向下滚动一行提交列表" -#: gitk:3087 #, tcl-format msgid "<%s-PageUp>\tScroll commit list up one page" msgstr "<%s-PageUp>\t向上滚动一页提交列表" -#: gitk:3088 #, tcl-format msgid "<%s-PageDown>\tScroll commit list down one page" msgstr "<%s-PageDown>\t向下滚动一页提交列表" -#: gitk:3089 msgid "<Shift-Up>\tFind backwards (upwards, later commits)" msgstr "<Shift-Up>\t向后查找(向上的,更晚的提交)" -#: gitk:3090 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)" msgstr "<Shift-Down>\t向前查找(向下的,更早的提交)" -#: gitk:3091 msgid "<Delete>, b\tScroll diff view up one page" msgstr "<Delete>, b\t向上滚动diff视图一页" -#: gitk:3092 msgid "<Backspace>\tScroll diff view up one page" msgstr "<Backspace>\t向上滚动diff视图一页" -#: gitk:3093 msgid "<Space>\t\tScroll diff view down one page" msgstr "<Space>\t\t向下滚动diff视图一页" -#: gitk:3094 msgid "u\t\tScroll diff view up 18 lines" msgstr "u\t\t向上滚动diff视图18行" -#: gitk:3095 msgid "d\t\tScroll diff view down 18 lines" msgstr "d\t\t向下滚动diff视图18行" -#: gitk:3096 #, tcl-format msgid "<%s-F>\t\tFind" msgstr "<%s-F>\t\t查找" -#: gitk:3097 #, tcl-format msgid "<%s-G>\t\tMove to next find hit" msgstr "<%s-G>\t\t移动到下一次查找命中" -#: gitk:3098 msgid "<Return>\tMove to next find hit" msgstr "<Return>\t\t移动到下一次查找命中" -#: gitk:3099 msgid "g\t\tGo to commit" msgstr "g\t\t转到提交" -#: gitk:3100 msgid "/\t\tFocus the search box" msgstr "/\t\t选中搜索框" -#: gitk:3101 msgid "?\t\tMove to previous find hit" msgstr "?\t\t移动到上一次查找命中" -#: gitk:3102 msgid "f\t\tScroll diff view to next file" msgstr "f\t\t滚动diff视图到下一个文件" -#: gitk:3103 #, tcl-format msgid "<%s-S>\t\tSearch for next hit in diff view" msgstr "<%s-S>\t\t在diff视图中查找下一此命中" -#: gitk:3104 #, tcl-format msgid "<%s-R>\t\tSearch for previous hit in diff view" msgstr "<%s-R>\t\t在diff视图中查找上一次命中" -#: gitk:3105 #, tcl-format msgid "<%s-KP+>\tIncrease font size" msgstr "<%s-KP+>\t增大字体大小" -#: gitk:3106 #, tcl-format msgid "<%s-plus>\tIncrease font size" msgstr "<%s-plus>\t增大字体大小" -#: gitk:3107 #, tcl-format msgid "<%s-KP->\tDecrease font size" msgstr "<%s-KP->\t减小字体大小" -#: gitk:3108 #, tcl-format msgid "<%s-minus>\tDecrease font size" msgstr "<%s-minus>\t减小字体大小" -#: gitk:3109 msgid "<F5>\t\tUpdate" msgstr "<F5>\t\t更新" -#: gitk:3574 gitk:3583 #, tcl-format msgid "Error creating temporary directory %s:" msgstr "创建临时目录出错%s:" -#: gitk:3596 #, tcl-format msgid "Error getting \"%s\" from %s:" msgstr "从%s获取\"%s\"出错:" -#: gitk:3659 msgid "command failed:" msgstr "执行命令失败:" -#: gitk:3808 msgid "No such commit" msgstr "无此提交" -#: gitk:3822 msgid "git gui blame: command failed:" msgstr "git gui blame:执行命令失败:" -#: gitk:3853 #, tcl-format msgid "Couldn't read merge head: %s" msgstr "不能读取合并头(merge head):%s" -#: gitk:3861 #, tcl-format msgid "Error reading index: %s" msgstr "读取索引出错:%s" -#: gitk:3886 #, tcl-format msgid "Couldn't start git blame: %s" msgstr "不能执行git blame:%s" -#: gitk:3889 gitk:6778 msgid "Searching" msgstr "搜索中" -#: gitk:3921 #, tcl-format msgid "Error running git blame: %s" msgstr "运行git blame出错:%s" -#: gitk:3949 #, tcl-format msgid "That line comes from commit %s, which is not in this view" msgstr "此行来自提交%s,不在此视图中" -#: gitk:3963 msgid "External diff viewer failed:" msgstr "外部diff查看器失败:" -#: gitk:4067 msgid "All files" msgstr "所有文件" -#: gitk:4091 msgid "View" msgstr "视图" -#: gitk:4094 msgid "Gitk view definition" msgstr "Gitk视图定义" -#: gitk:4098 msgid "Remember this view" msgstr "记住此视图" -#: gitk:4099 msgid "References (space separated list):" msgstr "引用(空格切分的列表):" -#: gitk:4100 msgid "Branches & tags:" msgstr "分支和tags" -#: gitk:4101 msgid "All refs" msgstr "所有引用" -#: gitk:4102 msgid "All (local) branches" msgstr "所有(本地)分支" -#: gitk:4103 msgid "All tags" msgstr "所有tag" -#: gitk:4104 msgid "All remote-tracking branches" msgstr "所有远程跟踪分支" -#: gitk:4105 msgid "Commit Info (regular expressions):" msgstr "提交信息 (正则表达式):" -#: gitk:4106 msgid "Author:" msgstr "作者:" -#: gitk:4107 msgid "Committer:" msgstr "提交者:" -#: gitk:4108 msgid "Commit Message:" msgstr "提交信息:" -#: gitk:4109 msgid "Matches all Commit Info criteria" msgstr "匹配所有提交信息标准" -#: gitk:4110 msgid "Matches no Commit Info criteria" msgstr "匹配无提交信息标准" -#: gitk:4111 msgid "Changes to Files:" msgstr "文件修改列表:" -#: gitk:4112 msgid "Fixed String" msgstr "固定字符串" -#: gitk:4113 msgid "Regular Expression" msgstr "正则表达式:" -#: gitk:4114 msgid "Search string:" msgstr "搜索字符串:" -#: gitk:4115 msgid "" "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" -msgstr "提交日期 (\"2星期之前\", \"2009-03-17 15:27:38\", \"5月 17, 2009 15:27:38\"):" +msgstr "" +"提交日期 (\"2星期之前\", \"2009-03-17 15:27:38\", \"5月 17, 2009 15:27:38\"):" -#: gitk:4116 msgid "Since:" msgstr "自:" -#: gitk:4117 msgid "Until:" msgstr "到:" -#: gitk:4118 msgid "Limit and/or skip a number of revisions (positive integer):" msgstr "限制 且/或 跳过一定数量的版本(正整数):" -#: gitk:4119 msgid "Number to show:" msgstr "显示数量:" -#: gitk:4120 msgid "Number to skip:" msgstr "跳过数量:" -#: gitk:4121 msgid "Miscellaneous options:" msgstr "其他选项:" -#: gitk:4122 msgid "Strictly sort by date" msgstr "严格按日期整理" -#: gitk:4123 msgid "Mark branch sides" msgstr "标记分支边界" -#: gitk:4124 msgid "Limit to first parent" msgstr "限制到第一个父提交" -#: gitk:4125 msgid "Simple history" msgstr "简易历史" -#: gitk:4126 msgid "Additional arguments to git log:" msgstr "git log命令的额外参数:" -#: gitk:4127 msgid "Enter files and directories to include, one per line:" msgstr "输入文件和文件夹来引用,每行一个:" -#: gitk:4128 msgid "Command to generate more commits to include:" msgstr "命令产生更多的提交来引用:" -#: gitk:4252 msgid "Gitk: edit view" msgstr "Gitk: 编辑视图" -#: gitk:4260 msgid "-- criteria for selecting revisions" msgstr "-- 用来选择版本的规则" -#: gitk:4265 msgid "View Name" msgstr "视图名称" -#: gitk:4340 msgid "Apply (F5)" msgstr "应用(F5)" -#: gitk:4378 msgid "Error in commit selection arguments:" msgstr "提交选择参数错误:" -#: gitk:4433 gitk:4486 gitk:4948 gitk:4962 gitk:6232 gitk:12524 gitk:12525 msgid "None" msgstr "无" -#: gitk:5045 gitk:5050 msgid "Descendant" msgstr "子提交" -#: gitk:5046 msgid "Not descendant" msgstr "非子提交" -#: gitk:5053 gitk:5058 msgid "Ancestor" msgstr "父提交" -#: gitk:5054 msgid "Not ancestor" msgstr "非父提交" -#: gitk:5348 msgid "Local changes checked in to index but not committed" msgstr "已添加到索引但未提交的修改" -#: gitk:5384 msgid "Local uncommitted changes, not checked in to index" msgstr "未添加到索引且未提交的修改" -#: gitk:7158 msgid "and many more" msgstr "更多" -#: gitk:7161 msgid "many" msgstr "很多" -#: gitk:7352 msgid "Tags:" msgstr "Tags:" -#: gitk:7369 gitk:7375 gitk:8854 msgid "Parent" msgstr "父节点" -#: gitk:7380 msgid "Child" msgstr "子节点" -#: gitk:7389 msgid "Branch" msgstr "分支" -#: gitk:7392 msgid "Follows" msgstr "之后的tag" -#: gitk:7395 msgid "Precedes" msgstr "之前的tag" -#: gitk:7990 #, tcl-format msgid "Error getting diffs: %s" msgstr "获取差异错误:%s" -#: gitk:8679 msgid "Goto:" msgstr "转到:" -#: gitk:8700 #, tcl-format msgid "Short SHA1 id %s is ambiguous" msgstr "短格式的SHA1提交号%s不明确、有歧义" -#: gitk:8707 #, tcl-format msgid "Revision %s is not known" msgstr "版本%s未知" -#: gitk:8717 #, tcl-format msgid "SHA1 id %s is not known" msgstr "提交号(SHA1 id)%s未知" -#: gitk:8719 #, tcl-format msgid "Revision %s is not in the current view" msgstr "版本%s不在当前视图中" -#: gitk:8861 gitk:8876 msgid "Date" msgstr "日期" -#: gitk:8864 msgid "Children" msgstr "子节点" -#: gitk:8927 #, tcl-format msgid "Reset %s branch to here" msgstr "重置分支%s到此处" -#: gitk:8929 msgid "Detached head: can't reset" msgstr "分离的头(head):不能重置(reset)" -#: gitk:9034 gitk:9040 msgid "Skipping merge commit " msgstr "跳过合并提交" -#: gitk:9049 gitk:9054 msgid "Error getting patch ID for " msgstr "获取补丁ID出错" -#: gitk:9050 gitk:9055 msgid " - stopping\n" msgstr " — 停止中\n" -#: gitk:9060 gitk:9063 gitk:9071 gitk:9085 gitk:9094 msgid "Commit " msgstr "提交" -#: gitk:9064 msgid "" " is the same patch as\n" " " -msgstr " 是相同的补丁(patch)\n " +msgstr "" +" 是相同的补丁(patch)\n" +" " -#: gitk:9072 msgid "" " differs from\n" " " -msgstr " 差异来自\n " +msgstr "" +" 差异来自\n" +" " -#: gitk:9074 msgid "" "Diff of commits:\n" "\n" -msgstr "提交的差异(Diff):\n\n" +msgstr "" +"提交的差异(Diff):\n" +"\n" -#: gitk:9086 gitk:9095 #, tcl-format msgid " has %s children - stopping\n" msgstr "有%s子节点 — 停止中\n" -#: gitk:9114 #, tcl-format msgid "Error writing commit to file: %s" msgstr "写入提交到文件出错:%s" -#: gitk:9120 #, tcl-format msgid "Error diffing commits: %s" msgstr "比较提交差异出错:%s" -#: gitk:9166 msgid "Top" msgstr "顶部" -#: gitk:9167 msgid "From" msgstr "从" -#: gitk:9172 msgid "To" msgstr "到" -#: gitk:9196 msgid "Generate patch" msgstr "生成补丁(patch)" -#: gitk:9198 msgid "From:" msgstr "从:" -#: gitk:9207 msgid "To:" msgstr "到:" -#: gitk:9216 msgid "Reverse" msgstr "反向(Reverse)" -#: gitk:9218 gitk:9428 msgid "Output file:" msgstr "输出文件:" -#: gitk:9224 msgid "Generate" msgstr "生成" -#: gitk:9262 msgid "Error creating patch:" msgstr "创建补丁(patch)出错:" -#: gitk:9285 gitk:9416 gitk:9504 msgid "ID:" msgstr "ID:" -#: gitk:9294 msgid "Tag name:" msgstr "Tag名称:" -#: gitk:9297 msgid "Tag message is optional" msgstr "Tag信息是可选的" -#: gitk:9299 msgid "Tag message:" msgstr "Tag信息:" -#: gitk:9303 gitk:9474 msgid "Create" msgstr "创建" -#: gitk:9321 msgid "No tag name specified" msgstr "未指定tag名称" -#: gitk:9325 #, tcl-format msgid "Tag \"%s\" already exists" msgstr "Tag\"%s\"已经存在" -#: gitk:9335 msgid "Error creating tag:" msgstr "创建tag出错:" -#: gitk:9425 msgid "Command:" msgstr "命令:" -#: gitk:9433 msgid "Write" msgstr "写入" -#: gitk:9451 msgid "Error writing commit:" msgstr "写入提交出错:" -#: gitk:9473 msgid "Create branch" msgstr "创建分支" -#: gitk:9489 #, tcl-format msgid "Rename branch %s" msgstr "重命名分支%s" -#: gitk:9490 msgid "Rename" msgstr "重命名" -#: gitk:9514 msgid "Name:" msgstr "名称:" -#: gitk:9538 msgid "Please specify a name for the new branch" msgstr "请指定新分支的名称" -#: gitk:9543 #, tcl-format msgid "Branch '%s' already exists. Overwrite?" msgstr "分支\"%s\"已经存在。覆盖它?" -#: gitk:9587 msgid "Please specify a new name for the branch" msgstr "请重新指定新分支的名称" -#: gitk:9650 #, tcl-format msgid "Commit %s is already included in branch %s -- really re-apply it?" msgstr "提交%s已经存在于分支%s。确定重新应用它?" -#: gitk:9655 msgid "Cherry-picking" msgstr "打补丁中(Cherry-picking)" -#: gitk:9664 #, tcl-format msgid "" "Cherry-pick failed because of local changes to file '%s'.\n" "Please commit, reset or stash your changes and try again." -msgstr "打补丁(Cherry-pick)失败,因为本地修改了文件\"%s\"。\n请提交(commit)、重置(reset)或暂存(stash)修改后重试。" +msgstr "" +"打补丁(Cherry-pick)失败,因为本地修改了文件\"%s\"。\n" +"请提交(commit)、重置(reset)或暂存(stash)修改后重试。" -#: gitk:9670 msgid "" "Cherry-pick failed because of merge conflict.\n" "Do you wish to run git citool to resolve it?" -msgstr "打补丁(Cherry-pick)失败因为合并冲突。\n你是否希望运行git citool 来解决冲突?" +msgstr "" +"打补丁(Cherry-pick)失败因为合并冲突。\n" +"你是否希望运行git citool 来解决冲突?" -#: gitk:9686 gitk:9744 msgid "No changes committed" msgstr "无已经提交的修改" -#: gitk:9713 #, tcl-format msgid "Commit %s is not included in branch %s -- really revert it?" msgstr "提交%s不包含在分支%s中,确认回滚(revert)它?" -#: gitk:9718 msgid "Reverting" msgstr "回滚中(Reverting)" -#: gitk:9726 #, tcl-format msgid "" "Revert failed because of local changes to the following files:%s Please " "commit, reset or stash your changes and try again." -msgstr "回滚(revert)失败,因为如下的本地文件修改:%s\n请提交(commit)、重置(reset)或者暂存(stash)改变后重试。" +msgstr "" +"回滚(revert)失败,因为如下的本地文件修改:%s\n" +"请提交(commit)、重置(reset)或者暂存(stash)改变后重试。" -#: gitk:9730 msgid "" "Revert failed because of merge conflict.\n" " Do you wish to run git citool to resolve it?" -msgstr "回滚(revert)失败,因为合并冲突。\n你是否希望运行git citool来解决冲突?" +msgstr "" +"回滚(revert)失败,因为合并冲突。\n" +"你是否希望运行git citool来解决冲突?" -#: gitk:9773 msgid "Confirm reset" msgstr "确认重置(reset)" -#: gitk:9775 #, tcl-format msgid "Reset branch %s to %s?" msgstr "重置(reset)分支%s到%s?" -#: gitk:9777 msgid "Reset type:" msgstr "重置(reset)类型:" -#: gitk:9780 msgid "Soft: Leave working tree and index untouched" msgstr "软性:离开工作树,索引未改变" -#: gitk:9783 msgid "Mixed: Leave working tree untouched, reset index" msgstr "混合:离开工作树(未改变),索引重置" -#: gitk:9786 msgid "" "Hard: Reset working tree and index\n" "(discard ALL local changes)" -msgstr "硬性:重置工作树和索引\n(丢弃所有的本地修改)" +msgstr "" +"硬性:重置工作树和索引\n" +"(丢弃所有的本地修改)" -#: gitk:9803 msgid "Resetting" msgstr "重置中(Resetting)" -#: gitk:9876 #, tcl-format msgid "A local branch named %s exists already" msgstr "本地分支%s已经存在" -#: gitk:9884 msgid "Checking out" msgstr "检出中(Checking out)" -#: gitk:9943 msgid "Cannot delete the currently checked-out branch" msgstr "不能删除当前检出(checkout)分支" -#: gitk:9949 #, tcl-format msgid "" "The commits on branch %s aren't on any other branch.\n" "Really delete branch %s?" -msgstr "在分支%s上的提交不在其他任何分支上。\n确认删除分支%s?" +msgstr "" +"在分支%s上的提交不在其他任何分支上。\n" +"确认删除分支%s?" -#: gitk:9980 #, tcl-format msgid "Tags and heads: %s" msgstr "Tags和头指针(heads):%s" -#: gitk:9997 msgid "Filter" msgstr "过滤器" -#: gitk:10293 msgid "" "Error reading commit topology information; branch and preceding/following " "tag information will be incomplete." msgstr "读取提交拓扑信息出错;分支和之前/之后的tag信息将不能完成。" -#: gitk:11270 msgid "Tag" msgstr "标签(Tag)" -#: gitk:11274 msgid "Id" msgstr "Id" -#: gitk:11357 msgid "Gitk font chooser" msgstr "Gitk字体选择" -#: gitk:11374 msgid "B" msgstr "粗体" -#: gitk:11377 msgid "I" msgstr "斜体" -#: gitk:11495 msgid "Commit list display options" msgstr "提交列表展示选项" -#: gitk:11498 msgid "Maximum graph width (lines)" msgstr "最大图宽度(行数)" -#: gitk:11502 #, no-tcl-format msgid "Maximum graph width (% of pane)" msgstr "最大图宽度(%窗口百分比)" -#: gitk:11505 msgid "Show local changes" msgstr "显示本地修改" -#: gitk:11508 msgid "Auto-select SHA1 (length)" msgstr "自动选择SHA1(长度)" -#: gitk:11512 msgid "Hide remote refs" msgstr "隐藏远程引用" -#: gitk:11516 msgid "Diff display options" msgstr "差异(Diff)展示选项" -#: gitk:11518 msgid "Tab spacing" msgstr "制表符宽度" -#: gitk:11521 msgid "Display nearby tags/heads" msgstr "显示临近的tags/heads" -#: gitk:11524 msgid "Maximum # tags/heads to show" msgstr "最大tags/heads展示数量" -#: gitk:11527 msgid "Limit diffs to listed paths" msgstr "diff中列出文件限制" -#: gitk:11530 msgid "Support per-file encodings" msgstr "单独文件编码支持" -#: gitk:11536 gitk:11683 msgid "External diff tool" msgstr "外部差异(diff)工具" -#: gitk:11537 msgid "Choose..." msgstr "选择..." -#: gitk:11542 msgid "General options" msgstr "常规选项" -#: gitk:11545 msgid "Use themed widgets" msgstr "使用主题小部件" -#: gitk:11547 msgid "(change requires restart)" msgstr "(需重启生效)" -#: gitk:11549 msgid "(currently unavailable)" msgstr "(当前不可用)" -#: gitk:11560 msgid "Colors: press to choose" msgstr "颜色:点击来选择" -#: gitk:11563 msgid "Interface" msgstr "界面" -#: gitk:11564 msgid "interface" msgstr "界面" -#: gitk:11567 msgid "Background" msgstr "背景" -#: gitk:11568 gitk:11598 msgid "background" msgstr "背景" -#: gitk:11571 msgid "Foreground" msgstr "前景" -#: gitk:11572 msgid "foreground" msgstr "前景" -#: gitk:11575 msgid "Diff: old lines" msgstr "差异(Diff):老代码行" -#: gitk:11576 msgid "diff old lines" msgstr "差异(diff)老代码行" -#: gitk:11580 msgid "Diff: new lines" msgstr "差异(Diff):新代码行" -#: gitk:11581 msgid "diff new lines" msgstr "差异(diff)新代码行" -#: gitk:11585 msgid "Diff: hunk header" msgstr "差异(Diff):补丁片段头信息" -#: gitk:11587 msgid "diff hunk header" msgstr "差异(diff)补丁片段头信息" -#: gitk:11591 msgid "Marked line bg" msgstr "已标记代码行背景" -#: gitk:11593 msgid "marked line background" msgstr "已标记代码行背景" -#: gitk:11597 msgid "Select bg" msgstr "选择背景" -#: gitk:11606 msgid "Fonts: press to choose" msgstr "字体:点击来选择" -#: gitk:11608 msgid "Main font" msgstr "主字体" -#: gitk:11609 msgid "Diff display font" msgstr "差异(Diff)显示字体" -#: gitk:11610 msgid "User interface font" msgstr "用户界面字体" -#: gitk:11632 msgid "Gitk preferences" msgstr "Gitk偏好设置" -#: gitk:11641 msgid "General" msgstr "常规" -#: gitk:11642 msgid "Colors" msgstr "颜色" -#: gitk:11643 msgid "Fonts" msgstr "字体" -#: gitk:11693 #, tcl-format msgid "Gitk: choose color for %s" msgstr "Gitk:选择颜色用于%s" -#: gitk:12206 msgid "" "Sorry, gitk cannot run with this version of Tcl/Tk.\n" " Gitk requires at least Tcl/Tk 8.4." -msgstr "对不起,gitk不能运行在当前版本的Tcl/Tk中。\nGitk运行需要最低版本为Tcl/Tk8.4。" +msgstr "" +"对不起,gitk不能运行在当前版本的Tcl/Tk中。\n" +"Gitk运行需要最低版本为Tcl/Tk8.4。" -#: gitk:12416 msgid "Cannot find a git repository here." msgstr "在此位置未发现git仓库。" -#: gitk:12463 #, tcl-format msgid "Ambiguous argument '%s': both revision and filename" msgstr "不明确有歧义的参数\"%s\":版本和文件名称" -#: gitk:12475 msgid "Bad arguments to gitk:" msgstr "运行gitk参数错误:" diff --git a/gpg-interface.c b/gpg-interface.c index 7e6a1520bd..d517425034 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -974,11 +974,20 @@ const char *gpg_trust_level_to_str(enum signature_trust_level level) return sigcheck_gpg_trust_level[level].display_key; } -int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key) +int sign_buffer(struct strbuf *buffer, struct strbuf *signature, + const char *signing_key, enum sign_buffer_flags flags) { + char *keyid_to_free = NULL; + int ret = 0; + gpg_interface_lazy_init(); - return use_format->sign_buffer(buffer, signature, signing_key); + if ((flags & SIGN_BUFFER_USE_DEFAULT_KEY) && (!signing_key || !*signing_key)) + signing_key = keyid_to_free = get_signing_key(); + + ret = use_format->sign_buffer(buffer, signature, signing_key); + free(keyid_to_free); + return ret; } /* @@ -1143,21 +1152,28 @@ out: return ret; } -int parse_sign_mode(const char *arg, enum sign_mode *mode) +int parse_sign_mode(const char *arg, enum sign_mode *mode, const char **keyid) { - if (!strcmp(arg, "abort")) + if (!strcmp(arg, "abort")) { *mode = SIGN_ABORT; - else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore")) + } else if (!strcmp(arg, "verbatim") || !strcmp(arg, "ignore")) { *mode = SIGN_VERBATIM; - else if (!strcmp(arg, "warn-verbatim") || !strcmp(arg, "warn")) + } else if (!strcmp(arg, "warn-verbatim") || !strcmp(arg, "warn")) { *mode = SIGN_WARN_VERBATIM; - else if (!strcmp(arg, "warn-strip")) + } else if (!strcmp(arg, "warn-strip")) { *mode = SIGN_WARN_STRIP; - else if (!strcmp(arg, "strip")) + } else if (!strcmp(arg, "strip")) { *mode = SIGN_STRIP; - else if (!strcmp(arg, "strip-if-invalid")) + } else if (!strcmp(arg, "strip-if-invalid")) { *mode = SIGN_STRIP_IF_INVALID; - else + } else if (!strcmp(arg, "sign-if-invalid")) { + *mode = SIGN_SIGN_IF_INVALID; + } else if (skip_prefix(arg, "sign-if-invalid=", &arg)) { + *mode = SIGN_SIGN_IF_INVALID; + if (keyid) + *keyid = arg; + } else { return -1; + } return 0; } diff --git a/gpg-interface.h b/gpg-interface.h index 789d1ffac4..a365586ce1 100644 --- a/gpg-interface.h +++ b/gpg-interface.h @@ -74,6 +74,15 @@ int parse_signature(const char *buf, size_t size, struct strbuf *payload, struct */ size_t parse_signed_buffer(const char *buf, size_t size); +/* Flags for sign_buffer(). */ +enum sign_buffer_flags { + /* + * Use the default configured signing key as returned by `get_signing_key()` + * when the provided "signing_key" is NULL or empty. + */ + SIGN_BUFFER_USE_DEFAULT_KEY = (1 << 0), +}; + /* * Create a detached signature for the contents of "buffer" and append * it after "signature"; "buffer" and "signature" can be the same @@ -81,8 +90,7 @@ size_t parse_signed_buffer(const char *buf, size_t size); * at the end. Returns 0 on success, non-zero on failure. */ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, - const char *signing_key); - + const char *signing_key, enum sign_buffer_flags flags); /* * Returns corresponding string in lowercase for a given member of @@ -112,12 +120,15 @@ enum sign_mode { SIGN_WARN_STRIP, SIGN_STRIP, SIGN_STRIP_IF_INVALID, + SIGN_SIGN_IF_INVALID, }; /* * Return 0 if `arg` can be parsed into an `enum sign_mode`. Return -1 - * otherwise. + * otherwise. If the parsed mode is SIGN_SIGN_IF_INVALID and GPG key provided in + * the arguments in the form `sign-if-invalid=<keyid>`, the key-ID is parsed + * into `char **keyid`. */ -int parse_sign_mode(const char *arg, enum sign_mode *mode); +int parse_sign_mode(const char *arg, enum sign_mode *mode, const char **keyid); #endif @@ -317,3 +317,21 @@ const struct git_hash_algo *unsafe_hash_algo(const struct git_hash_algo *algop) /* Otherwise use the default one. */ return algop; } + +unsigned oid_common_prefix_hexlen(const struct object_id *a, + const struct object_id *b) +{ + unsigned rawsz = hash_algos[a->algo].rawsz; + + for (unsigned i = 0; i < rawsz; i++) { + if (a->hash[i] == b->hash[i]) + continue; + + if ((a->hash[i] ^ b->hash[i]) & 0xf0) + return i * 2; + else + return i * 2 + 1; + } + + return rawsz * 2; +} @@ -396,6 +396,9 @@ static inline int oideq(const struct object_id *oid1, const struct object_id *oi return !memcmp(oid1->hash, oid2->hash, GIT_MAX_RAWSZ); } +unsigned oid_common_prefix_hexlen(const struct object_id *a, + const struct object_id *b); + static inline void oidcpy(struct object_id *dst, const struct object_id *src) { memcpy(dst->hash, src->hash, GIT_MAX_RAWSZ); @@ -1,16 +1,16 @@ #include "git-compat-util.h" #include "abspath.h" #include "advice.h" +#include "config.h" +#include "environment.h" #include "gettext.h" #include "hook.h" -#include "path.h" #include "parse.h" +#include "path.h" #include "run-command.h" -#include "config.h" +#include "setup.h" #include "strbuf.h" #include "strmap.h" -#include "environment.h" -#include "setup.h" const char *find_hook(struct repository *r, const char *name) { @@ -52,34 +52,26 @@ const char *find_hook(struct repository *r, const char *name) return path.buf; } -static void hook_clear(struct hook *h, cb_data_free_fn cb_data_free) +void hook_free(void *p, const char *str UNUSED) { + struct hook *h = p; + if (!h) return; - if (h->kind == HOOK_TRADITIONAL) + if (h->kind == HOOK_TRADITIONAL) { free((void *)h->u.traditional.path); - else if (h->kind == HOOK_CONFIGURED) { + } else if (h->kind == HOOK_CONFIGURED) { free((void *)h->u.configured.friendly_name); free((void *)h->u.configured.command); } - if (cb_data_free) - cb_data_free(h->feed_pipe_cb_data); + if (h->data_free && h->feed_pipe_cb_data) + h->data_free(h->feed_pipe_cb_data); free(h); } -void hook_list_clear(struct string_list *hooks, cb_data_free_fn cb_data_free) -{ - struct string_list_item *item; - - for_each_string_list_item(item, hooks) - hook_clear(item->util, cb_data_free); - - string_list_clear(hooks, 0); -} - /* Helper to detect and add default "traditional" hooks from the hookdir. */ static void list_hooks_add_default(struct repository *r, const char *hookname, struct string_list *hook_list, @@ -91,7 +83,7 @@ static void list_hooks_add_default(struct repository *r, const char *hookname, if (!hook_path) return; - h = xcalloc(1, sizeof(struct hook)); + CALLOC_ARRAY(h, 1); /* * If the hook is to run in a specific dir, a relative path can @@ -100,9 +92,15 @@ static void list_hooks_add_default(struct repository *r, const char *hookname, if (options && options->dir) hook_path = absolute_path(hook_path); - /* Setup per-hook internal state cb data */ - if (options && options->feed_pipe_cb_data_alloc) + /* + * Setup per-hook internal state callback data. + * When provided, the alloc/free callbacks are always provided + * together, so use them to alloc/free the internal hook state. + */ + if (options && options->feed_pipe_cb_data_alloc) { h->feed_pipe_cb_data = options->feed_pipe_cb_data_alloc(options->feed_pipe_ctx); + h->data_free = options->feed_pipe_cb_data_free; + } h->kind = HOOK_TRADITIONAL; h->u.traditional.path = xstrdup(hook_path); @@ -110,19 +108,21 @@ static void list_hooks_add_default(struct repository *r, const char *hookname, string_list_append(hook_list, hook_path)->util = h; } -static void unsorted_string_list_remove(struct string_list *list, - const char *str) -{ - struct string_list_item *item = unsorted_string_list_lookup(list, str); - if (item) - unsorted_string_list_delete_item(list, item - list->items, 0); -} +/* + * Cache entry stored as the .util pointer of string_list items inside the + * hook config cache. + */ +struct hook_config_cache_entry { + char *command; + enum config_scope scope; + bool disabled; +}; /* * Callback struct to collect all hook.* keys in a single config pass. * commands: friendly-name to command map. * event_hooks: event-name to list of friendly-names map. - * disabled_hooks: set of friendly-names with hook.name.enabled = false. + * disabled_hooks: set of friendly-names with hook.<friendly-name>.enabled = false. */ struct hook_all_config_cb { struct strmap commands; @@ -132,7 +132,7 @@ struct hook_all_config_cb { /* repo_config() callback that collects all hook.* configuration in one pass. */ static int hook_config_lookup_all(const char *key, const char *value, - const struct config_context *ctx UNUSED, + const struct config_context *ctx, void *cb_data) { struct hook_all_config_cb *data = cb_data; @@ -156,20 +156,32 @@ static int hook_config_lookup_all(const char *key, const char *value, struct strmap_entry *e; strmap_for_each_entry(&data->event_hooks, &iter, e) - unsorted_string_list_remove(e->value, hook_name); + unsorted_string_list_remove(e->value, hook_name, 0); } else { struct string_list *hooks = strmap_get(&data->event_hooks, value); if (!hooks) { - hooks = xcalloc(1, sizeof(*hooks)); + CALLOC_ARRAY(hooks, 1); string_list_init_dup(hooks); strmap_put(&data->event_hooks, value, hooks); } /* Re-insert if necessary to preserve last-seen order. */ - unsorted_string_list_remove(hooks, hook_name); - string_list_append(hooks, hook_name); + unsorted_string_list_remove(hooks, hook_name, 0); + + if (!ctx->kvi) + BUG("hook config callback called without key-value info"); + + /* + * Stash the config scope in the util pointer for + * later retrieval in build_hook_config_map(). This + * intermediate struct is transient and never leaves + * that function, so we pack the enum value into the + * pointer rather than heap-allocating a wrapper. + */ + string_list_append(hooks, hook_name)->util = + (void *)(uintptr_t)ctx->kvi->scope; } } else if (!strcmp(subkey, "command")) { /* Store command overwriting the old value */ @@ -186,7 +198,7 @@ static int hook_config_lookup_all(const char *key, const char *value, break; case 1: /* enabled: undo a prior disabled entry */ unsorted_string_list_remove(&data->disabled_hooks, - hook_name); + hook_name, 0); break; default: break; /* ignore unrecognised values */ @@ -202,8 +214,10 @@ static int hook_config_lookup_all(const char *key, const char *value, * every item's string is the hook's friendly-name and its util pointer is * the corresponding command string. Both strings are owned by the map. * - * Disabled hooks and hooks missing a command are already filtered out at - * parse time, so callers can iterate the list directly. + * Disabled hooks are kept in the cache with entry->disabled set, so that + * "git hook list" can display them. A non-disabled hook missing a command + * is fatal; a disabled hook missing a command emits a warning and is kept + * in the cache with entry->command = NULL. */ void hook_cache_clear(struct strmap *cache) { @@ -212,7 +226,12 @@ void hook_cache_clear(struct strmap *cache) strmap_for_each_entry(cache, &iter, e) { struct string_list *hooks = e->value; - string_list_clear(hooks, 1); /* free util (command) pointers */ + for (size_t i = 0; i < hooks->nr; i++) { + struct hook_config_cache_entry *entry = hooks->items[i].util; + free(entry->command); + free(entry); + } + string_list_clear(hooks, 0); free(hooks); } strmap_clear(cache, 0); @@ -235,28 +254,39 @@ static void build_hook_config_map(struct repository *r, struct strmap *cache) /* Construct the cache from parsed configs. */ strmap_for_each_entry(&cb_data.event_hooks, &iter, e) { struct string_list *hook_names = e->value; - struct string_list *hooks = xcalloc(1, sizeof(*hooks)); + struct string_list *hooks; + CALLOC_ARRAY(hooks, 1); string_list_init_dup(hooks); for (size_t i = 0; i < hook_names->nr; i++) { const char *hname = hook_names->items[i].string; + enum config_scope scope = + (enum config_scope)(uintptr_t)hook_names->items[i].util; + struct hook_config_cache_entry *entry; char *command; - /* filter out disabled hooks */ - if (unsorted_string_list_lookup(&cb_data.disabled_hooks, - hname)) - continue; + bool is_disabled = + !!unsorted_string_list_lookup( + &cb_data.disabled_hooks, hname); command = strmap_get(&cb_data.commands, hname); - if (!command) - die(_("'hook.%s.command' must be configured or " - "'hook.%s.event' must be removed;" - " aborting."), hname, hname); + if (!command) { + if (is_disabled) + warning(_("disabled hook '%s' has no " + "command configured"), hname); + else + die(_("'hook.%s.command' must be configured or " + "'hook.%s.event' must be removed;" + " aborting."), hname, hname); + } - /* util stores the command; owned by the cache. */ - string_list_append(hooks, hname)->util = - xstrdup(command); + /* util stores a cache entry; owned by the cache. */ + CALLOC_ARRAY(entry, 1); + entry->command = xstrdup_or_null(command); + entry->scope = scope; + entry->disabled = is_disabled; + string_list_append(hooks, hname)->util = entry; } strmap_put(cache, e->key, hooks); @@ -289,7 +319,7 @@ static struct strmap *get_hook_config_cache(struct repository *r) * it just once on the first call. */ if (!r->hook_config_cache) { - r->hook_config_cache = xcalloc(1, sizeof(*cache)); + CALLOC_ARRAY(r->hook_config_cache, 1); strmap_init(r->hook_config_cache); build_hook_config_map(r, r->hook_config_cache); } @@ -297,9 +327,9 @@ static struct strmap *get_hook_config_cache(struct repository *r) } else { /* * Out-of-repo calls (no gitdir) allocate and return a temporary - * map cache which gets free'd immediately by the caller. + * cache which gets freed immediately by the caller. */ - cache = xcalloc(1, sizeof(*cache)); + CALLOC_ARRAY(cache, 1); strmap_init(cache); build_hook_config_map(r, cache); } @@ -318,17 +348,28 @@ static void list_hooks_add_configured(struct repository *r, /* Iterate through configured hooks and initialize internal states */ for (size_t i = 0; configured_hooks && i < configured_hooks->nr; i++) { const char *friendly_name = configured_hooks->items[i].string; - const char *command = configured_hooks->items[i].util; - struct hook *hook = xcalloc(1, sizeof(struct hook)); + struct hook_config_cache_entry *entry = configured_hooks->items[i].util; + struct hook *hook; - if (options && options->feed_pipe_cb_data_alloc) + CALLOC_ARRAY(hook, 1); + + /* + * When provided, the alloc/free callbacks are always provided + * together, so use them to alloc/free the internal hook state. + */ + if (options && options->feed_pipe_cb_data_alloc) { hook->feed_pipe_cb_data = options->feed_pipe_cb_data_alloc( options->feed_pipe_ctx); + hook->data_free = options->feed_pipe_cb_data_free; + } hook->kind = HOOK_CONFIGURED; hook->u.configured.friendly_name = xstrdup(friendly_name); - hook->u.configured.command = xstrdup(command); + hook->u.configured.command = + entry->command ? xstrdup(entry->command) : NULL; + hook->u.configured.scope = entry->scope; + hook->u.configured.disabled = entry->disabled; string_list_append(list, friendly_name)->util = hook; } @@ -351,7 +392,7 @@ struct string_list *list_hooks(struct repository *r, const char *hookname, if (!hookname) BUG("null hookname was provided to hook_list()!"); - hook_head = xmalloc(sizeof(struct string_list)); + CALLOC_ARRAY(hook_head, 1); string_list_init_dup(hook_head); /* Add hooks from the config, e.g. hook.myhook.event = pre-commit */ @@ -366,8 +407,17 @@ struct string_list *list_hooks(struct repository *r, const char *hookname, int hook_exists(struct repository *r, const char *name) { struct string_list *hooks = list_hooks(r, name, NULL); - int exists = hooks->nr > 0; - hook_list_clear(hooks, NULL); + int exists = 0; + + for (size_t i = 0; i < hooks->nr; i++) { + struct hook *h = hooks->items[i].util; + if (h->kind == HOOK_TRADITIONAL || + !h->u.configured.disabled) { + exists = 1; + break; + } + } + string_list_clear_func(hooks, hook_free); free(hooks); return exists; } @@ -381,10 +431,11 @@ static int pick_next_hook(struct child_process *cp, struct string_list *hook_list = hook_cb->hook_command_list; struct hook *h; - if (hook_cb->hook_to_run_index >= hook_list->nr) - return 0; - - h = hook_list->items[hook_cb->hook_to_run_index++].util; + do { + if (hook_cb->hook_to_run_index >= hook_list->nr) + return 0; + h = hook_list->items[hook_cb->hook_to_run_index++].util; + } while (h->kind == HOOK_CONFIGURED && h->u.configured.disabled); cp->no_stdin = 1; strvec_pushv(&cp->env, hook_cb->options->env.v); @@ -414,7 +465,11 @@ static int pick_next_hook(struct child_process *cp, } else if (h->kind == HOOK_CONFIGURED) { /* to enable oneliners, let config-specified hooks run in shell. */ cp->use_shell = true; + if (!h->u.configured.command) + BUG("non-disabled HOOK_CONFIGURED hook has no command"); strvec_push(&cp->args, h->u.configured.command); + } else { + BUG("unknown hook kind"); } if (!cp->args.nr) @@ -501,8 +556,7 @@ int run_hooks_opt(struct repository *r, const char *hook_name, * Ensure cb_data copy and free functions are either provided together, * or neither one is provided. */ - if ((options->feed_pipe_cb_data_alloc && !options->feed_pipe_cb_data_free) || - (!options->feed_pipe_cb_data_alloc && options->feed_pipe_cb_data_free)) + if (!options->feed_pipe_cb_data_alloc != !options->feed_pipe_cb_data_free) BUG("feed_pipe_cb_data_alloc and feed_pipe_cb_data_free must be set together"); if (options->invoked_hook) @@ -518,7 +572,7 @@ int run_hooks_opt(struct repository *r, const char *hook_name, run_processes_parallel(&opts); ret = cb_data.rc; cleanup: - hook_list_clear(cb_data.hook_command_list, options->feed_pipe_cb_data_free); + string_list_clear_func(cb_data.hook_command_list, hook_free); free(cb_data.hook_command_list); run_hooks_opt_clear(options); return ret; @@ -1,17 +1,21 @@ #ifndef HOOK_H #define HOOK_H -#include "strvec.h" +#include "config.h" #include "run-command.h" #include "string-list.h" #include "strmap.h" +#include "strvec.h" struct repository; +typedef void (*hook_data_free_fn)(void *data); +typedef void *(*hook_data_alloc_fn)(void *init_ctx); + /** * Represents a hook command to be run. * Hooks can be: * 1. "traditional" (found in the hooks directory) - * 2. "configured" (defined in Git's configuration via hook.<name>.event). + * 2. "configured" (defined in Git's configuration via hook.<friendly-name>.event). * The 'kind' field determines which part of the union 'u' is valid. */ struct hook { @@ -26,6 +30,8 @@ struct hook { struct { const char *friendly_name; const char *command; + enum config_scope scope; + bool disabled; } configured; } u; @@ -41,13 +47,17 @@ struct hook { * Only useful when using `run_hooks_opt.feed_pipe`, otherwise ignore it. */ void *feed_pipe_cb_data; -}; -typedef void (*cb_data_free_fn)(void *data); -typedef void *(*cb_data_alloc_fn)(void *init_ctx); + /** + * Callback to free `feed_pipe_cb_data`. + * + * It is called automatically and points to the `feed_pipe_cb_data_free` + * provided via the `run_hook_opt` parameter. + */ + hook_data_free_fn data_free; +}; -struct run_hooks_opt -{ +struct run_hooks_opt { /* Environment vars to be set for each hook */ struct strvec env; @@ -132,14 +142,14 @@ struct run_hooks_opt * * The `feed_pipe_ctx` pointer can be used to pass initialization data. */ - cb_data_alloc_fn feed_pipe_cb_data_alloc; + hook_data_alloc_fn feed_pipe_cb_data_alloc; /** * Called to free the memory initialized by `feed_pipe_cb_data_alloc`. * * Must always be provided when `feed_pipe_cb_data_alloc` is provided. */ - cb_data_free_fn feed_pipe_cb_data_free; + hook_data_free_fn feed_pipe_cb_data_free; }; #define RUN_HOOKS_OPT_INIT { \ @@ -186,10 +196,10 @@ struct string_list *list_hooks(struct repository *r, const char *hookname, struct run_hooks_opt *options); /** - * Frees the memory allocated for the hook list, including the `struct hook` - * items and their internal state. + * Frees a struct hook stored as the util pointer of a string_list_item. + * Suitable for use as a string_list_clear_func_t callback. */ -void hook_list_clear(struct string_list *hooks, cb_data_free_fn cb_data_free); +void hook_free(void *p, const char *str); /** * Frees the hook configuration cache stored in `struct repository`. @@ -22,6 +22,8 @@ #include "object-file.h" #include "odb.h" #include "tempfile.h" +#include "date.h" +#include "trace2.h" static struct trace_key trace_curl = TRACE_KEY_INIT(CURL); static int trace_curl_data = 1; @@ -149,6 +151,11 @@ static char *cached_accept_language; static char *http_ssl_backend; static int http_schannel_check_revoke = 1; + +static long http_retry_after = 0; +static long http_max_retries = 0; +static long http_max_retry_time = 300; + /* * With the backend being set to `schannel`, setting sslCAinfo would override * the Certificate Store in cURL v7.60.0 and later, which is not what we want @@ -209,7 +216,7 @@ static inline int is_hdr_continuation(const char *ptr, const size_t size) return size && (*ptr == ' ' || *ptr == '\t'); } -static size_t fwrite_wwwauth(char *ptr, size_t eltsize, size_t nmemb, void *p UNUSED) +static size_t fwrite_wwwauth(char *ptr, size_t eltsize, size_t nmemb, void *p MAYBE_UNUSED) { size_t size = eltsize * nmemb; struct strvec *values = &http_auth.wwwauth_headers; @@ -575,6 +582,21 @@ static int http_options(const char *var, const char *value, return 0; } + if (!strcmp("http.retryafter", var)) { + http_retry_after = git_config_int(var, value, ctx->kvi); + return 0; + } + + if (!strcmp("http.maxretries", var)) { + http_max_retries = git_config_int(var, value, ctx->kvi); + return 0; + } + + if (!strcmp("http.maxretrytime", var)) { + http_max_retry_time = git_config_int(var, value, ctx->kvi); + return 0; + } + /* Fall back on the default ones */ return git_default_config(var, value, ctx, data); } @@ -1422,6 +1444,10 @@ void http_init(struct remote *remote, const char *url, int proactive_auth) set_long_from_env(&curl_tcp_keepintvl, "GIT_TCP_KEEPINTVL"); set_long_from_env(&curl_tcp_keepcnt, "GIT_TCP_KEEPCNT"); + set_long_from_env(&http_retry_after, "GIT_HTTP_RETRY_AFTER"); + set_long_from_env(&http_max_retries, "GIT_HTTP_MAX_RETRIES"); + set_long_from_env(&http_max_retry_time, "GIT_HTTP_MAX_RETRY_TIME"); + curl_default = get_curl_handle(); } @@ -1871,6 +1897,10 @@ static int handle_curl_result(struct slot_results *results) } return HTTP_REAUTH; } + } else if (results->http_code == 429) { + trace2_data_intmax("http", the_repository, "http/429-retry-after", + results->retry_after); + return HTTP_RATE_LIMITED; } else { if (results->http_connectcode == 407) credential_reject(the_repository, &proxy_auth); @@ -1886,6 +1916,7 @@ int run_one_slot(struct active_request_slot *slot, struct slot_results *results) { slot->results = results; + if (!start_active_slot(slot)) { xsnprintf(curl_errorstr, sizeof(curl_errorstr), "failed to start HTTP request"); @@ -2119,10 +2150,10 @@ static void http_opt_request_remainder(CURL *curl, off_t pos) static int http_request(const char *url, void *result, int target, - const struct http_get_options *options) + struct http_get_options *options) { struct active_request_slot *slot; - struct slot_results results; + struct slot_results results = { .retry_after = -1 }; struct curl_slist *headers = http_copy_default_headers(); struct strbuf buf = STRBUF_INIT; const char *accept_language; @@ -2156,22 +2187,19 @@ static int http_request(const char *url, headers = curl_slist_append(headers, accept_language); strbuf_addstr(&buf, "Pragma:"); - if (options && options->no_cache) + if (options->no_cache) strbuf_addstr(&buf, " no-cache"); - if (options && options->initial_request && + if (options->initial_request && http_follow_config == HTTP_FOLLOW_INITIAL) curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 1L); headers = curl_slist_append(headers, buf.buf); /* Add additional headers here */ - if (options && options->extra_headers) { + if (options->extra_headers) { const struct string_list_item *item; - if (options && options->extra_headers) { - for_each_string_list_item(item, options->extra_headers) { - headers = curl_slist_append(headers, item->string); - } - } + for_each_string_list_item(item, options->extra_headers) + headers = curl_slist_append(headers, item->string); } headers = http_append_auth_header(&http_auth, headers); @@ -2183,7 +2211,18 @@ static int http_request(const char *url, ret = run_one_slot(slot, &results); - if (options && options->content_type) { +#ifdef GIT_CURL_HAVE_CURLINFO_RETRY_AFTER + if (ret == HTTP_RATE_LIMITED) { + curl_off_t retry_after; + if (curl_easy_getinfo(slot->curl, CURLINFO_RETRY_AFTER, + &retry_after) == CURLE_OK && retry_after > 0) + results.retry_after = (long)retry_after; + } +#endif + + options->retry_after = results.retry_after; + + if (options->content_type) { struct strbuf raw = STRBUF_INIT; curlinfo_strbuf(slot->curl, CURLINFO_CONTENT_TYPE, &raw); extract_content_type(&raw, options->content_type, @@ -2191,7 +2230,7 @@ static int http_request(const char *url, strbuf_release(&raw); } - if (options && options->effective_url) + if (options->effective_url) curlinfo_strbuf(slot->curl, CURLINFO_EFFECTIVE_URL, options->effective_url); @@ -2253,22 +2292,66 @@ static int update_url_from_redirect(struct strbuf *base, return 1; } -static int http_request_reauth(const char *url, +/* + * Compute the retry delay for an HTTP 429 response. + * Returns a negative value if configuration is invalid (delay exceeds + * http.maxRetryTime), otherwise returns the delay in seconds (>= 0). + */ +static long handle_rate_limit_retry(long slot_retry_after) +{ + /* Use the slot-specific retry_after value or configured default */ + if (slot_retry_after >= 0) { + /* Check if retry delay exceeds maximum allowed */ + if (slot_retry_after > http_max_retry_time) { + error(_("response requested a delay greater than http.maxRetryTime (%ld > %ld seconds)"), + slot_retry_after, http_max_retry_time); + trace2_data_string("http", the_repository, + "http/429-error", "exceeds-max-retry-time"); + trace2_data_intmax("http", the_repository, + "http/429-requested-delay", slot_retry_after); + return -1; + } + return slot_retry_after; + } else { + /* No Retry-After header provided, use configured default */ + if (http_retry_after > http_max_retry_time) { + error(_("configured http.retryAfter exceeds http.maxRetryTime (%ld > %ld seconds)"), + http_retry_after, http_max_retry_time); + trace2_data_string("http", the_repository, + "http/429-error", "config-exceeds-max-retry-time"); + return -1; + } + trace2_data_string("http", the_repository, + "http/429-retry-source", "config-default"); + return http_retry_after; + } +} + +static int http_request_recoverable(const char *url, void *result, int target, struct http_get_options *options) { + static struct http_get_options empty_opts; int i = 3; int ret; + int rate_limit_retries = http_max_retries; + + if (!options) + options = &empty_opts; if (always_auth_proactively()) credential_fill(the_repository, &http_auth, 1); ret = http_request(url, result, target, options); - if (ret != HTTP_OK && ret != HTTP_REAUTH) + if (ret != HTTP_OK && ret != HTTP_REAUTH && ret != HTTP_RATE_LIMITED) return ret; - if (options && options->effective_url && options->base_url) { + /* If retries are disabled and we got a 429, fail immediately */ + if (ret == HTTP_RATE_LIMITED && !http_max_retries) + return HTTP_ERROR; + + if (options->effective_url && options->base_url) { if (update_url_from_redirect(options->base_url, url, options->effective_url)) { credential_from_url(&http_auth, options->base_url->buf); @@ -2276,7 +2359,9 @@ static int http_request_reauth(const char *url, } } - while (ret == HTTP_REAUTH && --i) { + while ((ret == HTTP_REAUTH && --i) || + (ret == HTTP_RATE_LIMITED && --rate_limit_retries)) { + long retry_delay = -1; /* * The previous request may have put cruft into our output stream; we * should clear it out before making our next request. @@ -2301,11 +2386,28 @@ static int http_request_reauth(const char *url, default: BUG("Unknown http_request target"); } + if (ret == HTTP_RATE_LIMITED) { + retry_delay = handle_rate_limit_retry(options->retry_after); + if (retry_delay < 0) + return HTTP_ERROR; - credential_fill(the_repository, &http_auth, 1); + if (retry_delay > 0) { + warning(_("rate limited, waiting %ld seconds before retry"), retry_delay); + trace2_data_intmax("http", the_repository, + "http/retry-sleep-seconds", retry_delay); + sleep(retry_delay); + } + } else if (ret == HTTP_REAUTH) { + credential_fill(the_repository, &http_auth, 1); + } ret = http_request(url, result, target, options); } + if (ret == HTTP_RATE_LIMITED) { + trace2_data_string("http", the_repository, + "http/429-error", "retries-exhausted"); + return HTTP_RATE_LIMITED; + } return ret; } @@ -2313,7 +2415,7 @@ int http_get_strbuf(const char *url, struct strbuf *result, struct http_get_options *options) { - return http_request_reauth(url, result, HTTP_REQUEST_STRBUF, options); + return http_request_recoverable(url, result, HTTP_REQUEST_STRBUF, options); } /* @@ -2337,7 +2439,7 @@ int http_get_file(const char *url, const char *filename, goto cleanup; } - ret = http_request_reauth(url, result, HTTP_REQUEST_FILE, options); + ret = http_request_recoverable(url, result, HTTP_REQUEST_FILE, options); fclose(result); if (ret == HTTP_OK && finalize_object_file(the_repository, tmpfile.buf, filename)) @@ -20,6 +20,7 @@ struct slot_results { long http_code; long auth_avail; long http_connectcode; + long retry_after; }; struct active_request_slot { @@ -157,6 +158,13 @@ struct http_get_options { * request has completed. */ struct string_list *extra_headers; + + /* + * After a request completes, contains the Retry-After delay in seconds + * if the server returned HTTP 429 with a Retry-After header (requires + * libcurl 7.66.0 or later), or -1 if no such header was present. + */ + long retry_after; }; /* Return values for http_get_*() */ @@ -167,6 +175,7 @@ struct http_get_options { #define HTTP_REAUTH 4 #define HTTP_NOAUTH 5 #define HTTP_NOMATCHPUBLICKEY 6 +#define HTTP_RATE_LIMITED 7 /* * Requests a URL and stores the result in a strbuf. diff --git a/imap-send.c b/imap-send.c index 26dda7f328..af02c6a689 100644 --- a/imap-send.c +++ b/imap-send.c @@ -219,8 +219,14 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED, #else -static int host_matches(const char *host, const char *pattern) +static int host_matches(const char *host, const ASN1_STRING *asn1_str) { + const char *pattern = (const char *)ASN1_STRING_get0_data(asn1_str); + + /* embedded NUL characters may open a security hole */ + if (memchr(pattern, '\0', ASN1_STRING_length(asn1_str))) + return 0; + if (pattern[0] == '*' && pattern[1] == '.') { pattern += 2; if (!(host = strchr(host, '.'))) @@ -233,9 +239,13 @@ static int host_matches(const char *host, const char *pattern) static int verify_hostname(X509 *cert, const char *hostname) { - int len; +#if (OPENSSL_VERSION_NUMBER >= 0x40000000L) + const X509_NAME *subj; +#else X509_NAME *subj; - char cname[1000]; +#endif + const X509_NAME_ENTRY *cname_entry; + const ASN1_STRING *cname; int i, found; STACK_OF(GENERAL_NAME) *subj_alt_names; @@ -244,10 +254,11 @@ static int verify_hostname(X509 *cert, const char *hostname) if ((subj_alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL))) { int num_subj_alt_names = sk_GENERAL_NAME_num(subj_alt_names); for (i = 0; !found && i < num_subj_alt_names; i++) { + int ntype; GENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i); - if (subj_alt_name->type == GEN_DNS && - strlen((const char *)subj_alt_name->d.ia5->data) == (size_t)subj_alt_name->d.ia5->length && - host_matches(hostname, (const char *)(subj_alt_name->d.ia5->data))) + ASN1_STRING *subj_alt_str = GENERAL_NAME_get0_value(subj_alt_name, &ntype); + + if (ntype == GEN_DNS && host_matches(hostname, subj_alt_str)) found = 1; } sk_GENERAL_NAME_pop_free(subj_alt_names, GENERAL_NAME_free); @@ -258,12 +269,14 @@ static int verify_hostname(X509 *cert, const char *hostname) /* try the common name */ if (!(subj = X509_get_subject_name(cert))) return error("cannot get certificate subject"); - if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0) + if ((i = X509_NAME_get_index_by_NID(subj, NID_commonName, -1)) < 0 || + (cname_entry = X509_NAME_get_entry(subj, i)) == NULL || + (cname = X509_NAME_ENTRY_get_data(cname_entry)) == NULL) return error("cannot get certificate common name"); - if (strlen(cname) == (size_t)len && host_matches(hostname, cname)) + if (host_matches(hostname, cname)) return 0; return error("certificate owner '%s' does not match hostname '%s'", - cname, hostname); + ASN1_STRING_get0_data(cname), hostname); } static int ssl_socket_connect(struct imap_socket *sock, diff --git a/list-objects-filter-options.c b/list-objects-filter-options.c index 7f3e7b8f50..cef67e5919 100644 --- a/list-objects-filter-options.c +++ b/list-objects-filter-options.c @@ -125,9 +125,9 @@ int gently_parse_list_objects_filter( static const char *RESERVED_NON_WS = "~`!@#$^&*()[]{}\\;'\",<>?"; static int has_reserved_character( - struct strbuf *sub_spec, struct strbuf *errbuf) + const char *sub_spec, struct strbuf *errbuf) { - const char *c = sub_spec->buf; + const char *c = sub_spec; while (*c) { if (*c <= ' ' || strchr(RESERVED_NON_WS, *c)) { strbuf_addf( @@ -144,7 +144,7 @@ static int has_reserved_character( static int parse_combine_subfilter( struct list_objects_filter_options *filter_options, - struct strbuf *subspec, + const char *subspec, struct strbuf *errbuf) { size_t new_index = filter_options->sub_nr; @@ -155,7 +155,7 @@ static int parse_combine_subfilter( filter_options->sub_alloc); list_objects_filter_init(&filter_options->sub[new_index]); - decoded = url_percent_decode(subspec->buf); + decoded = url_percent_decode(subspec); result = has_reserved_character(subspec, errbuf); if (result) @@ -182,34 +182,34 @@ static int parse_combine_filter( const char *arg, struct strbuf *errbuf) { - struct strbuf **subspecs = strbuf_split_str(arg, '+', 0); - size_t sub; + const char *p = arg; + struct strbuf sub = STRBUF_INIT; int result = 0; - if (!subspecs[0]) { + if (!*p) { strbuf_addstr(errbuf, _("expected something after combine:")); result = 1; goto cleanup; } - for (sub = 0; subspecs[sub] && !result; sub++) { - if (subspecs[sub + 1]) { - /* - * This is not the last subspec. Remove trailing "+" so - * we can parse it. - */ - size_t last = subspecs[sub]->len - 1; - assert(subspecs[sub]->buf[last] == '+'); - strbuf_remove(subspecs[sub], last, 1); - } - result = parse_combine_subfilter( - filter_options, subspecs[sub], errbuf); + while (*p && !result) { + const char *end = strchrnul(p, '+'); + + strbuf_reset(&sub); + strbuf_add(&sub, p, end - p); + + if (sub.len) + result = parse_combine_subfilter(filter_options, sub.buf, errbuf); + + if (!*end) + break; + p = end + 1; } + strbuf_release(&sub); filter_options->choice = LOFC_COMBINE; cleanup: - strbuf_list_free(subspecs); if (result) list_objects_filter_release(filter_options); return result; diff --git a/mailinfo.c b/mailinfo.c index a2f06dbd96..13949ff31e 100644 --- a/mailinfo.c +++ b/mailinfo.c @@ -470,7 +470,7 @@ static int convert_to_utf8(struct mailinfo *mi, return error("cannot convert from %s to %s", charset, mi->metainfo_charset); } - strbuf_attach(line, out, out_len, out_len); + strbuf_attach(line, out, out_len, out_len + 1); return 0; } diff --git a/meson.build b/meson.build index 9e9ba9b82b..8309942d18 100644 --- a/meson.build +++ b/meson.build @@ -271,6 +271,13 @@ version_gen_environment.set('GIT_VERSION', get_option('version')) compiler = meson.get_compiler('c') +compat_sources = [ + 'compat/nonblock.c', + 'compat/obstack.c', + 'compat/open.c', + 'compat/terminal.c', +] + libgit_sources = [ 'abspath.c', 'add-interactive.c', @@ -304,10 +311,6 @@ libgit_sources = [ 'commit.c', 'common-exit.c', 'common-init.c', - 'compat/nonblock.c', - 'compat/obstack.c', - 'compat/open.c', - 'compat/terminal.c', 'compiler-tricks/not-constant.c', 'config.c', 'connect.c', @@ -556,7 +559,7 @@ libgit_sources = [ libgit_sources += custom_target( input: 'command-list.txt', output: 'command-list.h', - command: [shell, meson.current_source_dir() + '/generate-cmdlist.sh', meson.current_source_dir(), '@OUTPUT@'], + command: [shell, meson.current_source_dir() + '/tools/generate-cmdlist.sh', meson.current_source_dir(), '@OUTPUT@'], env: script_environment, ) @@ -725,10 +728,10 @@ endif builtin_sources += custom_target( output: 'config-list.h', depfile: 'config-list.h.d', - depend_files: [ 'generate-configlist.sh' ], + depend_files: [ 'tools/generate-configlist.sh' ], command: [ shell, - meson.current_source_dir() / 'generate-configlist.sh', + meson.current_source_dir() / 'tools/generate-configlist.sh', meson.current_source_dir(), '@OUTPUT@', '@DEPFILE@', @@ -741,7 +744,7 @@ builtin_sources += custom_target( output: 'hook-list.h', command: [ shell, - meson.current_source_dir() + '/generate-hooklist.sh', + meson.current_source_dir() + '/tools/generate-hooklist.sh', meson.current_source_dir(), '@OUTPUT@', ], @@ -1175,7 +1178,7 @@ endif if not has_poll_h and not has_sys_poll_h libgit_c_args += '-DNO_POLL' - libgit_sources += 'compat/poll/poll.c' + compat_sources += 'compat/poll/poll.c' libgit_include_directories += 'compat/poll' endif @@ -1191,7 +1194,7 @@ endif # implementation to threat things like drive prefixes specially. if host_machine.system() == 'windows' or not compiler.has_header('libgen.h') libgit_c_args += '-DNO_LIBGEN_H' - libgit_sources += 'compat/basename.c' + compat_sources += 'compat/basename.c' endif if compiler.has_header('paths.h') @@ -1221,7 +1224,7 @@ if host_machine.system() != 'windows' foreach symbol : ['inet_ntop', 'inet_pton', 'hstrerror'] if not compiler.has_function(symbol, dependencies: networking_dependencies) libgit_c_args += '-DNO_' + symbol.to_upper() - libgit_sources += 'compat/' + symbol + '.c' + compat_sources += 'compat/' + symbol + '.c' endif endforeach endif @@ -1263,18 +1266,18 @@ else endif if host_machine.system() == 'darwin' - libgit_sources += 'compat/precompose_utf8.c' + compat_sources += 'compat/precompose_utf8.c' libgit_c_args += '-DPRECOMPOSE_UNICODE' libgit_c_args += '-DPROTECT_HFS_DEFAULT' endif # Configure general compatibility wrappers. if host_machine.system() == 'cygwin' - libgit_sources += [ + compat_sources += [ 'compat/win32/path-utils.c', ] elif host_machine.system() == 'windows' - libgit_sources += [ + compat_sources += [ 'compat/winansi.c', 'compat/win32/dirent.c', 'compat/win32/flush.c', @@ -1301,20 +1304,20 @@ elif host_machine.system() == 'windows' libgit_include_directories += 'compat/win32' if compiler.get_id() == 'msvc' libgit_include_directories += 'compat/vcbuild/include' - libgit_sources += 'compat/msvc.c' + compat_sources += 'compat/msvc.c' else - libgit_sources += 'compat/mingw.c' + compat_sources += 'compat/mingw.c' endif endif if host_machine.system() == 'linux' - libgit_sources += 'compat/linux/procinfo.c' + compat_sources += 'compat/linux/procinfo.c' elif host_machine.system() == 'windows' - libgit_sources += 'compat/win32/trace2_win32_process_info.c' + compat_sources += 'compat/win32/trace2_win32_process_info.c' elif host_machine.system() == 'darwin' - libgit_sources += 'compat/darwin/procinfo.c' + compat_sources += 'compat/darwin/procinfo.c' else - libgit_sources += 'compat/stub/procinfo.c' + compat_sources += 'compat/stub/procinfo.c' endif if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' @@ -1327,13 +1330,13 @@ endif # Configure the simple-ipc subsystem required fro the fsmonitor. if host_machine.system() == 'windows' - libgit_sources += [ + compat_sources += [ 'compat/simple-ipc/ipc-shared.c', 'compat/simple-ipc/ipc-win32.c', ] libgit_c_args += '-DSUPPORTS_SIMPLE_IPC' else - libgit_sources += [ + compat_sources += [ 'compat/simple-ipc/ipc-shared.c', 'compat/simple-ipc/ipc-unix-socket.c', ] @@ -1351,7 +1354,7 @@ if fsmonitor_backend != '' libgit_c_args += '-DHAVE_FSMONITOR_DAEMON_BACKEND' libgit_c_args += '-DHAVE_FSMONITOR_OS_SETTINGS' - libgit_sources += [ + compat_sources += [ 'compat/fsmonitor/fsm-health-' + fsmonitor_backend + '.c', 'compat/fsmonitor/fsm-ipc-' + fsmonitor_backend + '.c', 'compat/fsmonitor/fsm-listen-' + fsmonitor_backend + '.c', @@ -1367,7 +1370,7 @@ if not get_option('b_sanitize').contains('address') and get_option('regex').allo if compiler.get_define('REG_ENHANCED', prefix: '#include <regex.h>') != '' libgit_c_args += '-DUSE_ENHANCED_BASIC_REGULAR_EXPRESSIONS' - libgit_sources += 'compat/regcomp_enhanced.c' + compat_sources += 'compat/regcomp_enhanced.c' endif elif not get_option('regex').enabled() libgit_c_args += [ @@ -1376,7 +1379,7 @@ elif not get_option('regex').enabled() '-DNO_MBSUPPORT', ] build_options_config.set('NO_REGEX', '1') - libgit_sources += 'compat/regex/regex.c' + compat_sources += 'compat/regex/regex.c' libgit_include_directories += 'compat/regex' else error('Native regex support requested but not found') @@ -1426,6 +1429,7 @@ checkfuncs = { 'initgroups' : [], 'strtoumax' : ['strtoumax.c', 'strtoimax.c'], 'pread' : ['pread.c'], + 'writev' : ['writev.c'], } if host_machine.system() == 'windows' @@ -1440,7 +1444,7 @@ else if get_option('b_sanitize').contains('address') or get_option('b_sanitize').contains('leak') libgit_c_args += '-DNO_MMAP' - libgit_sources += 'compat/mmap.c' + compat_sources += 'compat/mmap.c' else checkfuncs += { 'mmap': ['mmap.c'] } endif @@ -1450,7 +1454,7 @@ foreach func, impls : checkfuncs if not compiler.has_function(func) libgit_c_args += '-DNO_' + func.to_upper() foreach impl : impls - libgit_sources += 'compat/' + impl + compat_sources += 'compat/' + impl endforeach endif endforeach @@ -1461,13 +1465,13 @@ endif if not compiler.has_function('strdup') libgit_c_args += '-DOVERRIDE_STRDUP' - libgit_sources += 'compat/strdup.c' + compat_sources += 'compat/strdup.c' endif if not compiler.has_function('qsort') libgit_c_args += '-DINTERNAL_QSORT' endif -libgit_sources += 'compat/qsort_s.c' +compat_sources += 'compat/qsort_s.c' if compiler.has_function('getdelim') libgit_c_args += '-DHAVE_GETDELIM' @@ -1523,7 +1527,7 @@ if meson.can_run_host_binaries() and compiler.run(''' } ''', name: 'fread reads directories').returncode() == 0 libgit_c_args += '-DFREAD_READS_DIRECTORIES' - libgit_sources += 'compat/fopen.c' + compat_sources += 'compat/fopen.c' endif if not meson.is_cross_build() and fs.exists('/dev/tty') @@ -1757,14 +1761,23 @@ else endif libgit = declare_dependency( - link_with: static_library('git', - sources: libgit_sources, - c_args: libgit_c_args + [ - '-DGIT_VERSION_H="' + version_def_h.full_path() + '"', - ], - dependencies: libgit_dependencies, - include_directories: libgit_include_directories, - ), + link_with: [ + static_library('compat', + sources: compat_sources, + c_args: libgit_c_args, + dependencies: libgit_dependencies, + include_directories: libgit_include_directories, + ), + static_library('git', + sources: libgit_sources, + c_args: libgit_c_args + [ + '-DGIT_VERSION_H="' + version_def_h.full_path() + '"', + ], + c_pch: 'tools/precompiled.h', + dependencies: libgit_dependencies, + include_directories: libgit_include_directories, + ), + ], compile_args: libgit_c_args, dependencies: libgit_dependencies, include_directories: libgit_include_directories, @@ -1821,6 +1834,7 @@ test_dependencies = [ ] git_builtin = executable('git', sources: builtin_sources + 'git.c', + c_pch: 'tools/precompiled.h', dependencies: [libgit_commonmain], install: true, install_dir: git_exec_path, @@ -1971,7 +1985,7 @@ foreach script : scripts_sh output: fs.stem(script), command: [ shell, - meson.project_source_root() / 'generate-script.sh', + meson.project_source_root() / 'tools/generate-script.sh', '@INPUT@', '@OUTPUT@', meson.project_build_root() / 'GIT-BUILD-OPTIONS', @@ -2020,7 +2034,7 @@ if perl_features_enabled generate_perl_command = [ shell, - meson.project_source_root() / 'generate-perl.sh', + meson.project_source_root() / 'tools/generate-perl.sh', meson.project_build_root() / 'GIT-BUILD-OPTIONS', git_version_file.full_path(), perl_header, @@ -2069,7 +2083,7 @@ if target_python.found() output: fs.stem(script), command: [ shell, - meson.project_source_root() / 'generate-python.sh', + meson.project_source_root() / 'tools/generate-python.sh', meson.project_build_root() / 'GIT-BUILD-OPTIONS', '@INPUT@', '@OUTPUT@', @@ -2161,6 +2175,7 @@ else endif subdir('contrib') +subdir('tools') # Note that the target is intentionally configured after including the # 'contrib' directory, as some tool there also have their own manpages. diff --git a/midx-write.c b/midx-write.c index 6485cb6706..0ff2e45aa7 100644 --- a/midx-write.c +++ b/midx-write.c @@ -36,10 +36,13 @@ extern int cmp_idx_or_pack_name(const char *idx_or_pack_name, static size_t write_midx_header(const struct git_hash_algo *hash_algo, struct hashfile *f, unsigned char num_chunks, - uint32_t num_packs) + uint32_t num_packs, int version) { + if (version != MIDX_VERSION_V1 && version != MIDX_VERSION_V2) + BUG("unexpected MIDX version: %d", version); + hashwrite_be32(f, MIDX_SIGNATURE); - hashwrite_u8(f, MIDX_VERSION); + hashwrite_u8(f, version); hashwrite_u8(f, oid_version(hash_algo)); hashwrite_u8(f, num_chunks); hashwrite_u8(f, 0); /* unused */ @@ -105,15 +108,29 @@ struct write_midx_context { uint32_t preferred_pack_idx; + int version; /* must be MIDX_VERSION_V1 or _V2 */ + int incremental; uint32_t num_multi_pack_indexes_before; + struct multi_pack_index *compact_from; + struct multi_pack_index *compact_to; + int compact; + struct string_list *to_include; struct repository *repo; struct odb_source *source; }; +static uint32_t midx_pack_perm(struct write_midx_context *ctx, + uint32_t orig_pack_int_id) +{ + if (ctx->compact) + orig_pack_int_id -= ctx->compact_from->num_packs_in_base; + return ctx->pack_perm[orig_pack_int_id]; +} + static int should_include_pack(const struct write_midx_context *ctx, const char *file_name) { @@ -257,18 +274,14 @@ static void midx_fanout_sort(struct midx_fanout *fanout) QSORT(fanout->entries, fanout->nr, midx_oid_compare); } -static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout, - struct multi_pack_index *m, - uint32_t cur_fanout, - uint32_t preferred_pack) +static void midx_fanout_add_midx_fanout_1(struct midx_fanout *fanout, + struct multi_pack_index *m, + uint32_t cur_fanout, + uint32_t preferred_pack) { uint32_t start = m->num_objects_in_base, end; uint32_t cur_object; - if (m->base_midx) - midx_fanout_add_midx_fanout(fanout, m->base_midx, cur_fanout, - preferred_pack); - if (cur_fanout) start += ntohl(m->chunk_oid_fanout[cur_fanout - 1]); end = m->num_objects_in_base + ntohl(m->chunk_oid_fanout[cur_fanout]); @@ -292,6 +305,17 @@ static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout, } } +static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout, + struct multi_pack_index *m, + uint32_t cur_fanout, + uint32_t preferred_pack) +{ + if (m->base_midx) + midx_fanout_add_midx_fanout(fanout, m->base_midx, cur_fanout, + preferred_pack); + midx_fanout_add_midx_fanout_1(fanout, m, cur_fanout, preferred_pack); +} + static void midx_fanout_add_pack_fanout(struct midx_fanout *fanout, struct pack_info *info, uint32_t cur_pack, @@ -317,6 +341,45 @@ static void midx_fanout_add_pack_fanout(struct midx_fanout *fanout, } } +static void midx_fanout_add(struct midx_fanout *fanout, + struct write_midx_context *ctx, + uint32_t start_pack, + uint32_t cur_fanout) +{ + uint32_t cur_pack; + + if (ctx->m && !ctx->incremental) + midx_fanout_add_midx_fanout(fanout, ctx->m, cur_fanout, + ctx->preferred_pack_idx); + + for (cur_pack = start_pack; cur_pack < ctx->nr; cur_pack++) { + int preferred = cur_pack == ctx->preferred_pack_idx; + midx_fanout_add_pack_fanout(fanout, ctx->info, cur_pack, + preferred, cur_fanout); + } + + if (ctx->preferred_pack_idx != NO_PREFERRED_PACK && + ctx->preferred_pack_idx < start_pack) + midx_fanout_add_pack_fanout(fanout, ctx->info, + ctx->preferred_pack_idx, 1, + cur_fanout); +} + +static void midx_fanout_add_compact(struct midx_fanout *fanout, + struct write_midx_context *ctx, + uint32_t cur_fanout) +{ + struct multi_pack_index *m = ctx->compact_to; + + ASSERT(ctx->compact); + + while (m && m != ctx->compact_from->base_midx) { + midx_fanout_add_midx_fanout_1(fanout, m, cur_fanout, + NO_PREFERRED_PACK); + m = m->base_midx; + } +} + /* * It is possible to artificially get into a state where there are many * duplicate copies of objects. That can create high memory pressure if @@ -335,6 +398,9 @@ static void compute_sorted_entries(struct write_midx_context *ctx, size_t alloc_objects, total_objects = 0; struct midx_fanout fanout = { 0 }; + if (ctx->compact) + ASSERT(!start_pack); + for (cur_pack = start_pack; cur_pack < ctx->nr; cur_pack++) total_objects = st_add(total_objects, ctx->info[cur_pack].p->num_objects); @@ -353,23 +419,10 @@ static void compute_sorted_entries(struct write_midx_context *ctx, for (cur_fanout = 0; cur_fanout < 256; cur_fanout++) { fanout.nr = 0; - if (ctx->m && !ctx->incremental) - midx_fanout_add_midx_fanout(&fanout, ctx->m, cur_fanout, - ctx->preferred_pack_idx); - - for (cur_pack = start_pack; cur_pack < ctx->nr; cur_pack++) { - int preferred = cur_pack == ctx->preferred_pack_idx; - midx_fanout_add_pack_fanout(&fanout, - ctx->info, cur_pack, - preferred, cur_fanout); - } - - if (ctx->preferred_pack_idx != NO_PREFERRED_PACK && - ctx->preferred_pack_idx < start_pack) - midx_fanout_add_pack_fanout(&fanout, ctx->info, - ctx->preferred_pack_idx, 1, - cur_fanout); - + if (ctx->compact) + midx_fanout_add_compact(&fanout, ctx, cur_fanout); + else + midx_fanout_add(&fanout, ctx, start_pack, cur_fanout); midx_fanout_sort(&fanout); /* @@ -410,7 +463,9 @@ static int write_midx_pack_names(struct hashfile *f, void *data) if (ctx->info[i].expired) continue; - if (i && strcmp(ctx->info[i].pack_name, ctx->info[i - 1].pack_name) <= 0) + if (ctx->version == MIDX_VERSION_V1 && + i && strcmp(ctx->info[i].pack_name, + ctx->info[i - 1].pack_name) <= 0) BUG("incorrect pack-file order: %s before %s", ctx->info[i - 1].pack_name, ctx->info[i].pack_name); @@ -514,12 +569,12 @@ static int write_midx_object_offsets(struct hashfile *f, for (i = 0; i < ctx->entries_nr; i++) { struct pack_midx_entry *obj = list++; - if (ctx->pack_perm[obj->pack_int_id] == PACK_EXPIRED) + if (midx_pack_perm(ctx, obj->pack_int_id) == PACK_EXPIRED) BUG("object %s is in an expired pack with int-id %d", oid_to_hex(&obj->oid), obj->pack_int_id); - hashwrite_be32(f, ctx->pack_perm[obj->pack_int_id]); + hashwrite_be32(f, midx_pack_perm(ctx, obj->pack_int_id)); if (ctx->large_offsets_needed && obj->offset >> 31) hashwrite_be32(f, MIDX_LARGE_OFFSET_NEEDED | nr_large_offset++); @@ -620,8 +675,8 @@ static uint32_t *midx_pack_order(struct write_midx_context *ctx) for (i = 0; i < ctx->entries_nr; i++) { struct pack_midx_entry *e = &ctx->entries[i]; data[i].nr = i; - data[i].pack = ctx->pack_perm[e->pack_int_id]; - if (!e->preferred) + data[i].pack = midx_pack_perm(ctx, e->pack_int_id); + if (!e->preferred || ctx->compact) data[i].pack |= (1U << 31); data[i].offset = e->offset; } @@ -630,14 +685,14 @@ static uint32_t *midx_pack_order(struct write_midx_context *ctx) for (i = 0; i < ctx->entries_nr; i++) { struct pack_midx_entry *e = &ctx->entries[data[i].nr]; - struct pack_info *pack = &ctx->info[ctx->pack_perm[e->pack_int_id]]; + struct pack_info *pack = &ctx->info[midx_pack_perm(ctx, e->pack_int_id)]; if (pack->bitmap_pos == BITMAP_POS_UNKNOWN) pack->bitmap_pos = i + base_objects; pack->bitmap_nr++; pack_order[i] = data[i].nr; } for (i = 0; i < ctx->nr; i++) { - struct pack_info *pack = &ctx->info[ctx->pack_perm[i]]; + struct pack_info *pack = &ctx->info[i]; if (pack->bitmap_pos == BITMAP_POS_UNKNOWN) pack->bitmap_pos = 0; } @@ -691,7 +746,7 @@ static void prepare_midx_packing_data(struct packing_data *pdata, struct object_entry *to = packlist_alloc(pdata, &from->oid); oe_set_in_pack(pdata, to, - ctx->info[ctx->pack_perm[from->pack_int_id]].p); + ctx->info[midx_pack_perm(ctx, from->pack_int_id)].p); } trace2_region_leave("midx", "prepare_midx_packing_data", ctx->repo); @@ -900,6 +955,21 @@ cleanup: return ret; } +static int fill_pack_from_midx(struct pack_info *info, + struct multi_pack_index *m, + uint32_t pack_int_id) +{ + if (prepare_midx_pack(m, pack_int_id)) + return error(_("could not load pack %d"), pack_int_id); + + fill_pack_info(info, + m->packs[pack_int_id - m->num_packs_in_base], + m->pack_names[pack_int_id - m->num_packs_in_base], + pack_int_id); + + return 0; +} + static int fill_packs_from_midx(struct write_midx_context *ctx) { struct multi_pack_index *m; @@ -907,19 +977,88 @@ static int fill_packs_from_midx(struct write_midx_context *ctx) for (m = ctx->m; m; m = m->base_midx) { uint32_t i; - for (i = 0; i < m->num_packs; i++) { - if (prepare_midx_pack(m, m->num_packs_in_base + i)) - return error(_("could not load pack")); - + for (i = m->num_packs_in_base; + i < m->num_packs_in_base + m->num_packs; i++) { ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc); - fill_pack_info(&ctx->info[ctx->nr++], m->packs[i], - m->pack_names[i], - m->num_packs_in_base + i); + + if (fill_pack_from_midx(&ctx->info[ctx->nr], m, i) < 0) + return -1; + + ctx->nr++; } } return 0; } +static uint32_t compactible_packs_between(const struct multi_pack_index *from, + const struct multi_pack_index *to) +{ + uint32_t nr; + + ASSERT(from && to); + + if (unsigned_add_overflows(to->num_packs, to->num_packs_in_base)) + die(_("too many packs, unable to compact")); + + nr = to->num_packs + to->num_packs_in_base; + if (nr < from->num_packs_in_base) + BUG("unexpected number of packs in base during compaction: " + "%"PRIu32" < %"PRIu32, nr, from->num_packs_in_base); + + return nr - from->num_packs_in_base; +} + +static int fill_packs_from_midx_range(struct write_midx_context *ctx, + int bitmap_order) +{ + struct multi_pack_index *m = ctx->compact_to; + uint32_t packs_nr; + + ASSERT(ctx->compact && !ctx->nr); + ASSERT(ctx->compact_from); + ASSERT(ctx->compact_to); + + packs_nr = compactible_packs_between(ctx->compact_from, + ctx->compact_to); + + ALLOC_GROW(ctx->info, packs_nr, ctx->alloc); + + while (m != ctx->compact_from->base_midx) { + uint32_t pack_int_id, preferred_pack_id; + uint32_t i; + + if (bitmap_order) { + if (midx_preferred_pack(m, &preferred_pack_id) < 0) + die(_("could not determine preferred pack")); + } else { + preferred_pack_id = m->num_packs_in_base; + } + + pack_int_id = m->num_packs_in_base - ctx->compact_from->num_packs_in_base; + + if (fill_pack_from_midx(&ctx->info[pack_int_id++], m, + preferred_pack_id) < 0) + return -1; + + for (i = m->num_packs_in_base; + i < m->num_packs_in_base + m->num_packs; i++) { + if (preferred_pack_id == i) + continue; + + if (fill_pack_from_midx(&ctx->info[pack_int_id++], m, + i) < 0) + return -1; + } + + ctx->nr += m->num_packs; + m = m->base_midx; + } + + ASSERT(ctx->nr == packs_nr); + + return 0; +} + static struct { const char *non_split; const char *split; @@ -946,7 +1085,7 @@ static int link_midx_to_chain(struct multi_pack_index *m) } for (i = 0; i < ARRAY_SIZE(midx_exts); i++) { - const unsigned char *hash = get_midx_checksum(m); + const unsigned char *hash = midx_get_checksum_hash(m); get_midx_filename_ext(m->source, &from, hash, midx_exts[i].non_split); @@ -1026,6 +1165,12 @@ static bool midx_needs_update(struct multi_pack_index *midx, struct write_midx_c goto out; /* + * If the version differs, we need to update. + */ + if (midx->version != ctx->version) + goto out; + + /* * Ignore incremental updates for now. The assumption is that any * incremental update would be either empty (in which case we will bail * out later) or it would actually cover at least one new pack. @@ -1033,6 +1178,9 @@ static bool midx_needs_update(struct multi_pack_index *midx, struct write_midx_c if (ctx->incremental) goto out; + if (ctx->compact) + goto out; /* Compaction always requires an update. */ + /* * Otherwise, we need to verify that the packs covered by the existing * MIDX match the packs that we already have. The logic to do so is way @@ -1078,14 +1226,31 @@ out: return needed; } -static int write_midx_internal(struct odb_source *source, - struct string_list *packs_to_include, - struct string_list *packs_to_drop, - const char *preferred_pack_name, - const char *refs_snapshot, - unsigned flags) +static int midx_hashcmp(const struct multi_pack_index *a, + const struct multi_pack_index *b, + const struct git_hash_algo *algop) { - struct repository *r = source->odb->repo; + return hashcmp(midx_get_checksum_hash(a), midx_get_checksum_hash(b), + algop); +} + +struct write_midx_opts { + struct odb_source *source; /* non-optional */ + + struct string_list *packs_to_include; + struct string_list *packs_to_drop; + + struct multi_pack_index *compact_from; + struct multi_pack_index *compact_to; + + const char *preferred_pack_name; + const char *refs_snapshot; + unsigned flags; +}; + +static int write_midx_internal(struct write_midx_opts *opts) +{ + struct repository *r = opts->source->odb->repo; struct strbuf midx_name = STRBUF_INIT; unsigned char midx_hash[GIT_MAX_RAWSZ]; uint32_t start_pack; @@ -1094,6 +1259,7 @@ static int write_midx_internal(struct odb_source *source, struct tempfile *incr; struct write_midx_context ctx = { .preferred_pack_idx = NO_PREFERRED_PACK, + .version = MIDX_VERSION_V2, }; struct multi_pack_index *midx_to_free = NULL; int bitmapped_packs_concat_len = 0; @@ -1101,27 +1267,45 @@ static int write_midx_internal(struct odb_source *source, int dropped_packs = 0; int result = -1; const char **keep_hashes = NULL; + size_t keep_hashes_nr = 0; struct chunkfile *cf; trace2_region_enter("midx", "write_midx_internal", r); ctx.repo = r; - ctx.source = source; + ctx.source = opts->source; + + repo_config_get_int(ctx.repo, "midx.version", &ctx.version); + if (ctx.version != MIDX_VERSION_V1 && ctx.version != MIDX_VERSION_V2) + die(_("unknown MIDX version: %d"), ctx.version); + + ctx.incremental = !!(opts->flags & MIDX_WRITE_INCREMENTAL); + ctx.compact = !!(opts->flags & MIDX_WRITE_COMPACT); + + if (ctx.compact) { + if (ctx.version != MIDX_VERSION_V2) + die(_("cannot perform MIDX compaction with v1 format")); + if (!opts->compact_from) + BUG("expected non-NULL 'from' MIDX during compaction"); + if (!opts->compact_to) + BUG("expected non-NULL 'to' MIDX during compaction"); - ctx.incremental = !!(flags & MIDX_WRITE_INCREMENTAL); + ctx.compact_from = opts->compact_from; + ctx.compact_to = opts->compact_to; + } if (ctx.incremental) strbuf_addf(&midx_name, "%s/pack/multi-pack-index.d/tmp_midx_XXXXXX", - source->path); + opts->source->path); else - get_midx_filename(source, &midx_name); + get_midx_filename(opts->source, &midx_name); if (safe_create_leading_directories(r, midx_name.buf)) die_errno(_("unable to create leading directories of %s"), midx_name.buf); - if (!packs_to_include || ctx.incremental) { - struct multi_pack_index *m = get_multi_pack_index(source); + if (!opts->packs_to_include || ctx.incremental) { + struct multi_pack_index *m = get_multi_pack_index(opts->source); if (m && !midx_checksum_valid(m)) { warning(_("ignoring existing multi-pack-index; checksum mismatch")); m = NULL; @@ -1136,11 +1320,18 @@ static int write_midx_internal(struct odb_source *source, */ if (ctx.incremental) ctx.base_midx = m; - else if (!packs_to_include) + if (!opts->packs_to_include) ctx.m = m; } } + /* + * If compacting MIDX layer(s) in the range [from, to], then the + * compacted MIDX will share the same base MIDX as 'from'. + */ + if (ctx.compact) + ctx.base_midx = ctx.compact_from->base_midx; + ctx.nr = 0; ctx.alloc = ctx.m ? ctx.m->num_packs + ctx.m->num_packs_in_base : 16; ctx.info = NULL; @@ -1149,34 +1340,42 @@ static int write_midx_internal(struct odb_source *source, if (ctx.incremental) { struct multi_pack_index *m = ctx.base_midx; while (m) { - if (flags & MIDX_WRITE_BITMAP && load_midx_revindex(m)) { + if (opts->flags & MIDX_WRITE_BITMAP && load_midx_revindex(m)) { error(_("could not load reverse index for MIDX %s"), - hash_to_hex_algop(get_midx_checksum(m), - m->source->odb->repo->hash_algo)); + midx_get_checksum_hex(m)); goto cleanup; } ctx.num_multi_pack_indexes_before++; m = m->base_midx; } - } else if (ctx.m && fill_packs_from_midx(&ctx)) { + } else if (ctx.m && !ctx.compact && fill_packs_from_midx(&ctx)) { goto cleanup; } start_pack = ctx.nr; ctx.pack_paths_checked = 0; - if (flags & MIDX_PROGRESS) + if (opts->flags & MIDX_PROGRESS) ctx.progress = start_delayed_progress(r, _("Adding packfiles to multi-pack-index"), 0); else ctx.progress = NULL; - ctx.to_include = packs_to_include; + if (ctx.compact) { + int bitmap_order = 0; + if (opts->preferred_pack_name) + bitmap_order |= 1; + else if (opts->flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP)) + bitmap_order |= 1; - for_each_file_in_pack_dir(source->path, add_pack_to_midx, &ctx); + fill_packs_from_midx_range(&ctx, bitmap_order); + } else { + ctx.to_include = opts->packs_to_include; + for_each_file_in_pack_dir(opts->source->path, add_pack_to_midx, &ctx); + } stop_progress(&ctx.progress); - if (!packs_to_drop) { + if (!opts->packs_to_drop) { /* * If there is no MIDX then either it doesn't exist, or we're * doing a geometric repack. Try to load it from the source to @@ -1189,7 +1388,7 @@ static int write_midx_internal(struct odb_source *source, if (midx && !midx_needs_update(midx, &ctx)) { struct bitmap_index *bitmap_git; int bitmap_exists; - int want_bitmap = flags & MIDX_WRITE_BITMAP; + int want_bitmap = opts->flags & MIDX_WRITE_BITMAP; bitmap_git = prepare_midx_bitmap_git(midx); bitmap_exists = bitmap_git && bitmap_is_midx(bitmap_git); @@ -1201,7 +1400,7 @@ static int write_midx_internal(struct odb_source *source, * corresponding bitmap (or one wasn't requested). */ if (!want_bitmap) - clear_midx_files_ext(source, "bitmap", NULL); + clear_midx_files_ext(ctx.source, "bitmap", NULL); result = 0; goto cleanup; } @@ -1216,11 +1415,11 @@ static int write_midx_internal(struct odb_source *source, goto cleanup; /* nothing to do */ } - if (preferred_pack_name) { + if (opts->preferred_pack_name) { ctx.preferred_pack_idx = NO_PREFERRED_PACK; for (size_t i = 0; i < ctx.nr; i++) { - if (!cmp_idx_or_pack_name(preferred_pack_name, + if (!cmp_idx_or_pack_name(opts->preferred_pack_name, ctx.info[i].pack_name)) { ctx.preferred_pack_idx = i; break; @@ -1229,9 +1428,9 @@ static int write_midx_internal(struct odb_source *source, if (ctx.preferred_pack_idx == NO_PREFERRED_PACK) warning(_("unknown preferred pack: '%s'"), - preferred_pack_name); + opts->preferred_pack_name); } else if (ctx.nr && - (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP))) { + (opts->flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP))) { struct packed_git *oldest = ctx.info[0].p; ctx.preferred_pack_idx = 0; @@ -1242,7 +1441,7 @@ static int write_midx_internal(struct odb_source *source, */ open_pack_index(oldest); - if (packs_to_drop && packs_to_drop->nr) + if (opts->packs_to_drop && opts->packs_to_drop->nr) BUG("cannot write a MIDX bitmap during expiration"); /* @@ -1302,22 +1501,30 @@ static int write_midx_internal(struct odb_source *source, ctx.large_offsets_needed = 1; } - QSORT(ctx.info, ctx.nr, pack_info_compare); + if (ctx.compact) { + if (ctx.version != MIDX_VERSION_V2) + BUG("performing MIDX compaction with v1 MIDX"); + } else { + QSORT(ctx.info, ctx.nr, pack_info_compare); + } - if (packs_to_drop && packs_to_drop->nr) { + if (opts->packs_to_drop && opts->packs_to_drop->nr) { size_t drop_index = 0; int missing_drops = 0; - for (size_t i = 0; i < ctx.nr && drop_index < packs_to_drop->nr; i++) { + ASSERT(!ctx.compact); + + for (size_t i = 0; + i < ctx.nr && drop_index < opts->packs_to_drop->nr; i++) { int cmp = strcmp(ctx.info[i].pack_name, - packs_to_drop->items[drop_index].string); + opts->packs_to_drop->items[drop_index].string); if (!cmp) { drop_index++; ctx.info[i].expired = 1; } else if (cmp > 0) { error(_("did not see pack-file %s to drop"), - packs_to_drop->items[drop_index].string); + opts->packs_to_drop->items[drop_index].string); drop_index++; missing_drops++; i--; @@ -1338,12 +1545,20 @@ static int write_midx_internal(struct odb_source *source, */ ALLOC_ARRAY(ctx.pack_perm, ctx.nr); for (size_t i = 0; i < ctx.nr; i++) { + uint32_t from = ctx.info[i].orig_pack_int_id; + uint32_t to; + if (ctx.info[i].expired) { + to = PACK_EXPIRED; dropped_packs++; - ctx.pack_perm[ctx.info[i].orig_pack_int_id] = PACK_EXPIRED; } else { - ctx.pack_perm[ctx.info[i].orig_pack_int_id] = i - dropped_packs; + to = i - dropped_packs; } + + if (ctx.compact) + from -= ctx.compact_from->num_packs_in_base; + + ctx.pack_perm[from] = to; } for (size_t i = 0; i < ctx.nr; i++) { @@ -1354,16 +1569,16 @@ static int write_midx_internal(struct odb_source *source, } /* Check that the preferred pack wasn't expired (if given). */ - if (preferred_pack_name) { - struct pack_info *preferred = bsearch(preferred_pack_name, + if (opts->preferred_pack_name) { + struct pack_info *preferred = bsearch(opts->preferred_pack_name, ctx.info, ctx.nr, sizeof(*ctx.info), idx_or_pack_name_cmp); if (preferred) { - uint32_t perm = ctx.pack_perm[preferred->orig_pack_int_id]; + uint32_t perm = midx_pack_perm(&ctx, preferred->orig_pack_int_id); if (perm == PACK_EXPIRED) warning(_("preferred pack '%s' is expired"), - preferred_pack_name); + opts->preferred_pack_name); } } @@ -1377,15 +1592,15 @@ static int write_midx_internal(struct odb_source *source, } if (!ctx.entries_nr) { - if (flags & MIDX_WRITE_BITMAP) + if (opts->flags & MIDX_WRITE_BITMAP) warning(_("refusing to write multi-pack .bitmap without any objects")); - flags &= ~(MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP); + opts->flags &= ~(MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP); } if (ctx.incremental) { struct strbuf lock_name = STRBUF_INIT; - get_midx_chain_filename(source, &lock_name); + get_midx_chain_filename(opts->source, &lock_name); hold_lock_file_for_update(&lk, lock_name.buf, LOCK_DIE_ON_ERROR); strbuf_release(&lock_name); @@ -1428,7 +1643,7 @@ static int write_midx_internal(struct odb_source *source, MIDX_CHUNK_LARGE_OFFSET_WIDTH), write_midx_large_offsets); - if (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP)) { + if (opts->flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP)) { ctx.pack_order = midx_pack_order(&ctx); add_chunk(cf, MIDX_CHUNKID_REVINDEX, st_mult(ctx.entries_nr, sizeof(uint32_t)), @@ -1439,18 +1654,18 @@ static int write_midx_internal(struct odb_source *source, } write_midx_header(r->hash_algo, f, get_num_chunks(cf), - ctx.nr - dropped_packs); + ctx.nr - dropped_packs, ctx.version); write_chunkfile(cf, &ctx); finalize_hashfile(f, midx_hash, FSYNC_COMPONENT_PACK_METADATA, CSUM_FSYNC | CSUM_HASH_IN_STREAM); free_chunkfile(cf); - if (flags & MIDX_WRITE_REV_INDEX && + if (opts->flags & MIDX_WRITE_REV_INDEX && git_env_bool("GIT_TEST_MIDX_WRITE_REV", 0)) write_midx_reverse_index(&ctx, midx_hash); - if (flags & MIDX_WRITE_BITMAP) { + if (opts->flags & MIDX_WRITE_BITMAP) { struct packing_data pdata; struct commit_stack commits = COMMIT_STACK_INIT; @@ -1459,7 +1674,7 @@ static int write_midx_internal(struct odb_source *source, prepare_midx_packing_data(&pdata, &ctx); - find_commits_for_midx_bitmap(&commits, refs_snapshot, &ctx); + find_commits_for_midx_bitmap(&commits, opts->refs_snapshot, &ctx); /* * The previous steps translated the information from @@ -1470,8 +1685,8 @@ static int write_midx_internal(struct odb_source *source, FREE_AND_NULL(ctx.entries); ctx.entries_nr = 0; - if (write_midx_bitmap(&ctx, midx_hash, &pdata, - commits.items, commits.nr, flags) < 0) { + if (write_midx_bitmap(&ctx, midx_hash, &pdata, commits.items, + commits.nr, opts->flags) < 0) { error(_("could not write multi-pack bitmap")); clear_packing_data(&pdata); commit_stack_clear(&commits); @@ -1489,7 +1704,24 @@ static int write_midx_internal(struct odb_source *source, if (ctx.num_multi_pack_indexes_before == UINT32_MAX) die(_("too many multi-pack-indexes")); - CALLOC_ARRAY(keep_hashes, ctx.num_multi_pack_indexes_before + 1); + if (ctx.compact) { + struct multi_pack_index *m; + + /* + * Keep all MIDX layers excluding those in the range [from, to]. + */ + for (m = ctx.base_midx; m; m = m->base_midx) + keep_hashes_nr++; + for (m = ctx.m; + m && midx_hashcmp(m, ctx.compact_to, r->hash_algo); + m = m->base_midx) + keep_hashes_nr++; + + keep_hashes_nr++; /* include the compacted layer */ + } else { + keep_hashes_nr = ctx.num_multi_pack_indexes_before + 1; + } + CALLOC_ARRAY(keep_hashes, keep_hashes_nr); if (ctx.incremental) { FILE *chainf = fdopen_lock_file(&lk, "w"); @@ -1504,7 +1736,7 @@ static int write_midx_internal(struct odb_source *source, if (link_midx_to_chain(ctx.base_midx) < 0) goto cleanup; - get_split_midx_filename_ext(source, &final_midx_name, + get_split_midx_filename_ext(opts->source, &final_midx_name, midx_hash, MIDX_EXT_MIDX); if (rename_tempfile(&incr, final_midx_name.buf) < 0) { @@ -1514,18 +1746,47 @@ static int write_midx_internal(struct odb_source *source, strbuf_release(&final_midx_name); - keep_hashes[ctx.num_multi_pack_indexes_before] = - xstrdup(hash_to_hex_algop(midx_hash, r->hash_algo)); + if (ctx.compact) { + struct multi_pack_index *m; + uint32_t num_layers_before_from = 0; + uint32_t i; - for (uint32_t i = 0; i < ctx.num_multi_pack_indexes_before; i++) { - uint32_t j = ctx.num_multi_pack_indexes_before - i - 1; + for (m = ctx.base_midx; m; m = m->base_midx) + num_layers_before_from++; - keep_hashes[j] = xstrdup(hash_to_hex_algop(get_midx_checksum(m), + m = ctx.base_midx; + for (i = 0; i < num_layers_before_from; i++) { + uint32_t j = num_layers_before_from - i - 1; + + keep_hashes[j] = xstrdup(midx_get_checksum_hex(m)); + m = m->base_midx; + } + + keep_hashes[i] = xstrdup(hash_to_hex_algop(midx_hash, r->hash_algo)); - m = m->base_midx; + + i = 0; + for (m = ctx.m; + m && midx_hashcmp(m, ctx.compact_to, r->hash_algo); + m = m->base_midx) { + keep_hashes[keep_hashes_nr - i - 1] = + xstrdup(midx_get_checksum_hex(m)); + i++; + } + } else { + keep_hashes[ctx.num_multi_pack_indexes_before] = + xstrdup(hash_to_hex_algop(midx_hash, + r->hash_algo)); + + for (uint32_t i = 0; i < ctx.num_multi_pack_indexes_before; i++) { + uint32_t j = ctx.num_multi_pack_indexes_before - i - 1; + + keep_hashes[j] = xstrdup(midx_get_checksum_hex(m)); + m = m->base_midx; + } } - for (uint32_t i = 0; i <= ctx.num_multi_pack_indexes_before; i++) + for (uint32_t i = 0; i < keep_hashes_nr; i++) fprintf(get_lock_file_fp(&lk), "%s\n", keep_hashes[i]); } else { keep_hashes[ctx.num_multi_pack_indexes_before] = @@ -1538,8 +1799,7 @@ static int write_midx_internal(struct odb_source *source, if (commit_lock_file(&lk) < 0) die_errno(_("could not write multi-pack-index")); - clear_midx_files(source, keep_hashes, - ctx.num_multi_pack_indexes_before + 1, + clear_midx_files(opts->source, keep_hashes, keep_hashes_nr, ctx.incremental); result = 0; @@ -1557,7 +1817,7 @@ cleanup: free(ctx.pack_perm); free(ctx.pack_order); if (keep_hashes) { - for (uint32_t i = 0; i <= ctx.num_multi_pack_indexes_before; i++) + for (uint32_t i = 0; i < keep_hashes_nr; i++) free((char *)keep_hashes[i]); free(keep_hashes); } @@ -1573,9 +1833,14 @@ int write_midx_file(struct odb_source *source, const char *preferred_pack_name, const char *refs_snapshot, unsigned flags) { - return write_midx_internal(source, NULL, NULL, - preferred_pack_name, refs_snapshot, - flags); + struct write_midx_opts opts = { + .source = source, + .preferred_pack_name = preferred_pack_name, + .refs_snapshot = refs_snapshot, + .flags = flags, + }; + + return write_midx_internal(&opts); } int write_midx_file_only(struct odb_source *source, @@ -1583,8 +1848,30 @@ int write_midx_file_only(struct odb_source *source, const char *preferred_pack_name, const char *refs_snapshot, unsigned flags) { - return write_midx_internal(source, packs_to_include, NULL, - preferred_pack_name, refs_snapshot, flags); + struct write_midx_opts opts = { + .source = source, + .packs_to_include = packs_to_include, + .preferred_pack_name = preferred_pack_name, + .refs_snapshot = refs_snapshot, + .flags = flags, + }; + + return write_midx_internal(&opts); +} + +int write_midx_file_compact(struct odb_source *source, + struct multi_pack_index *from, + struct multi_pack_index *to, + unsigned flags) +{ + struct write_midx_opts opts = { + .source = source, + .compact_from = from, + .compact_to = to, + .flags = flags | MIDX_WRITE_COMPACT, + }; + + return write_midx_internal(&opts); } int expire_midx_packs(struct odb_source *source, unsigned flags) @@ -1643,9 +1930,14 @@ int expire_midx_packs(struct odb_source *source, unsigned flags) free(count); - if (packs_to_drop.nr) - result = write_midx_internal(source, NULL, - &packs_to_drop, NULL, NULL, flags); + if (packs_to_drop.nr) { + struct write_midx_opts opts = { + .source = source, + .packs_to_drop = &packs_to_drop, + .flags = flags & MIDX_PROGRESS, + }; + result = write_midx_internal(&opts); + } string_list_clear(&packs_to_drop, 0); @@ -1778,6 +2070,10 @@ int midx_repack(struct odb_source *source, size_t batch_size, unsigned flags) struct child_process cmd = CHILD_PROCESS_INIT; FILE *cmd_in; struct multi_pack_index *m = get_multi_pack_index(source); + struct write_midx_opts opts = { + .source = source, + .flags = flags, + }; /* * When updating the default for these configuration @@ -1852,8 +2148,7 @@ int midx_repack(struct odb_source *source, size_t batch_size, unsigned flags) goto cleanup; } - result = write_midx_internal(source, NULL, NULL, NULL, NULL, - flags); + result = write_midx_internal(&opts); cleanup: free(include_pack); @@ -24,7 +24,13 @@ void clear_incremental_midx_files_ext(struct odb_source *source, const char *ext int cmp_idx_or_pack_name(const char *idx_or_pack_name, const char *idx_name); -const unsigned char *get_midx_checksum(struct multi_pack_index *m) +const char *midx_get_checksum_hex(const struct multi_pack_index *m) +{ + return hash_to_hex_algop(midx_get_checksum_hash(m), + m->source->odb->repo->hash_algo); +} + +const unsigned char *midx_get_checksum_hash(const struct multi_pack_index *m) { return m->data + m->data_len - m->source->odb->repo->hash_algo->rawsz; } @@ -144,7 +150,7 @@ static struct multi_pack_index *load_multi_pack_index_one(struct odb_source *sou m->signature, MIDX_SIGNATURE); m->version = m->data[MIDX_BYTE_FILE_VERSION]; - if (m->version != MIDX_VERSION) + if (m->version != MIDX_VERSION_V1 && m->version != MIDX_VERSION_V2) die(_("multi-pack-index version %d not recognized"), m->version); @@ -205,7 +211,8 @@ static struct multi_pack_index *load_multi_pack_index_one(struct odb_source *sou die(_("multi-pack-index pack-name chunk is too short")); cur_pack_name = end + 1; - if (i && strcmp(m->pack_names[i], m->pack_names[i - 1]) <= 0) + if (m->version == MIDX_VERSION_V1 && + i && strcmp(m->pack_names[i], m->pack_names[i - 1]) <= 0) die(_("multi-pack-index pack names out of order: '%s' before '%s'"), m->pack_names[i - 1], m->pack_names[i]); @@ -406,6 +413,7 @@ void close_midx(struct multi_pack_index *m) } FREE_AND_NULL(m->packs); FREE_AND_NULL(m->pack_names); + FREE_AND_NULL(m->pack_names_sorted); free(m); } @@ -651,17 +659,40 @@ int cmp_idx_or_pack_name(const char *idx_or_pack_name, return strcmp(idx_or_pack_name, idx_name); } + +static int midx_pack_names_cmp(const void *a, const void *b, void *m_) +{ + struct multi_pack_index *m = m_; + return strcmp(m->pack_names[*(const size_t *)a], + m->pack_names[*(const size_t *)b]); +} + static int midx_contains_pack_1(struct multi_pack_index *m, const char *idx_or_pack_name) { uint32_t first = 0, last = m->num_packs; + if (m->version == MIDX_VERSION_V2 && !m->pack_names_sorted) { + uint32_t i; + + ALLOC_ARRAY(m->pack_names_sorted, m->num_packs); + + for (i = 0; i < m->num_packs; i++) + m->pack_names_sorted[i] = i; + + QSORT_S(m->pack_names_sorted, m->num_packs, midx_pack_names_cmp, + m); + } + while (first < last) { uint32_t mid = first + (last - first) / 2; const char *current; int cmp; - current = m->pack_names[mid]; + if (m->pack_names_sorted) + current = m->pack_names[m->pack_names_sorted[mid]]; + else + current = m->pack_names[mid]; cmp = cmp_idx_or_pack_name(idx_or_pack_name, current); if (!cmp) return 1; @@ -11,7 +11,8 @@ struct git_hash_algo; struct odb_source; #define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */ -#define MIDX_VERSION 1 +#define MIDX_VERSION_V1 1 +#define MIDX_VERSION_V2 2 #define MIDX_BYTE_FILE_VERSION 4 #define MIDX_BYTE_HASH_VERSION 5 #define MIDX_BYTE_NUM_CHUNKS 6 @@ -71,6 +72,7 @@ struct multi_pack_index { uint32_t num_packs_in_base; const char **pack_names; + size_t *pack_names_sorted; struct packed_git **packs; }; @@ -80,12 +82,14 @@ struct multi_pack_index { #define MIDX_WRITE_BITMAP_HASH_CACHE (1 << 3) #define MIDX_WRITE_BITMAP_LOOKUP_TABLE (1 << 4) #define MIDX_WRITE_INCREMENTAL (1 << 5) +#define MIDX_WRITE_COMPACT (1 << 6) #define MIDX_EXT_REV "rev" #define MIDX_EXT_BITMAP "bitmap" #define MIDX_EXT_MIDX "midx" -const unsigned char *get_midx_checksum(struct multi_pack_index *m); +const char *midx_get_checksum_hex(const struct multi_pack_index *m) /* static buffer */; +const unsigned char *midx_get_checksum_hash(const struct multi_pack_index *m); void get_midx_filename(struct odb_source *source, struct strbuf *out); void get_midx_filename_ext(struct odb_source *source, struct strbuf *out, const unsigned char *hash, const char *ext); @@ -128,6 +132,10 @@ int write_midx_file_only(struct odb_source *source, struct string_list *packs_to_include, const char *preferred_pack_name, const char *refs_snapshot, unsigned flags); +int write_midx_file_compact(struct odb_source *source, + struct multi_pack_index *from, + struct multi_pack_index *to, + unsigned flags); void clear_midx_file(struct repository *r); int verify_midx_file(struct odb_source *source, unsigned flags); int expire_midx_packs(struct odb_source *source, unsigned flags); diff --git a/negotiator/default.c b/negotiator/default.c index 116dedcf83..3cac0476a7 100644 --- a/negotiator/default.c +++ b/negotiator/default.c @@ -57,19 +57,19 @@ static int clear_marks(const struct reference *ref, void *cb_data UNUSED) static void mark_common(struct negotiation_state *ns, struct commit *commit, int ancestors_only, int dont_parse) { - struct prio_queue queue = { NULL }; + struct commit_stack stack = COMMIT_STACK_INIT; if (!commit || (commit->object.flags & COMMON)) return; - prio_queue_put(&queue, commit); + commit_stack_push(&stack, commit); if (!ancestors_only) { commit->object.flags |= COMMON; if ((commit->object.flags & SEEN) && !(commit->object.flags & POPPED)) ns->non_common_revs--; } - while ((commit = prio_queue_get(&queue))) { + while ((commit = commit_stack_pop(&stack))) { struct object *o = (struct object *)commit; if (!(o->flags & SEEN)) @@ -94,12 +94,12 @@ static void mark_common(struct negotiation_state *ns, struct commit *commit, if ((p->object.flags & SEEN) && !(p->object.flags & POPPED)) ns->non_common_revs--; - prio_queue_put(&queue, parents->item); + commit_stack_push(&stack, parents->item); } } } - clear_prio_queue(&queue); + commit_stack_clear(&stack); } /* diff --git a/negotiator/skipping.c b/negotiator/skipping.c index 0a272130fb..fe4126ca4d 100644 --- a/negotiator/skipping.c +++ b/negotiator/skipping.c @@ -91,15 +91,15 @@ static int clear_marks(const struct reference *ref, void *cb_data UNUSED) */ static void mark_common(struct data *data, struct commit *seen_commit) { - struct prio_queue queue = { NULL }; + struct commit_stack stack = COMMIT_STACK_INIT; struct commit *c; if (seen_commit->object.flags & COMMON) return; - prio_queue_put(&queue, seen_commit); + commit_stack_push(&stack, seen_commit); seen_commit->object.flags |= COMMON; - while ((c = prio_queue_get(&queue))) { + while ((c = commit_stack_pop(&stack))) { struct commit_list *p; if (!(c->object.flags & POPPED)) @@ -113,11 +113,11 @@ static void mark_common(struct data *data, struct commit *seen_commit) continue; p->item->object.flags |= COMMON; - prio_queue_put(&queue, p->item); + commit_stack_push(&stack, p->item); } } - clear_prio_queue(&queue); + commit_stack_clear(&stack); } /* diff --git a/object-file.c b/object-file.c index c62e5496e0..9e57eb6c27 100644 --- a/object-file.c +++ b/object-file.c @@ -33,6 +33,9 @@ /* The maximum size for an object header. */ #define MAX_HEADER_LEN 32 +static struct oidtree *odb_source_loose_cache(struct odb_source *source, + const struct object_id *oid); + static int get_conv_flags(unsigned flags) { if (flags & INDEX_RENORMALIZE) @@ -1279,8 +1282,9 @@ static int index_mem(struct index_state *istate, } } if (flags & INDEX_FORMAT_CHECK) { - struct fsck_options opts = FSCK_OPTIONS_DEFAULT; + struct fsck_options opts; + fsck_options_init(&opts, the_repository, FSCK_OPTIONS_DEFAULT); opts.strict = 1; opts.error_func = hash_format_check_report; if (fsck_buffer(null_oid(istate->repo->hash_algo), type, buf, size, &opts)) @@ -1845,11 +1849,28 @@ static int for_each_object_wrapper_cb(const struct object_id *oid, } } +static int for_each_prefixed_object_wrapper_cb(const struct object_id *oid, + void *cb_data) +{ + struct for_each_object_wrapper_data *data = cb_data; + if (data->request) { + struct object_info oi = *data->request; + + if (odb_source_loose_read_object_info(data->source, + oid, &oi, 0) < 0) + return -1; + + return data->cb(oid, &oi, data->cb_data); + } else { + return data->cb(oid, NULL, data->cb_data); + } +} + int odb_source_loose_for_each_object(struct odb_source *source, const struct object_info *request, odb_for_each_object_cb cb, void *cb_data, - unsigned flags) + const struct odb_for_each_object_options *opts) { struct for_each_object_wrapper_data data = { .source = source, @@ -1859,15 +1880,117 @@ int odb_source_loose_for_each_object(struct odb_source *source, }; /* There are no loose promisor objects, so we can return immediately. */ - if ((flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY)) + if ((opts->flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY)) return 0; - if ((flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY) && !source->local) + if ((opts->flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY) && !source->local) return 0; + if (opts->prefix) + return oidtree_each(odb_source_loose_cache(source, opts->prefix), + opts->prefix, opts->prefix_hex_len, + for_each_prefixed_object_wrapper_cb, &data); + return for_each_loose_file_in_source(source, for_each_object_wrapper_cb, NULL, NULL, &data); } +static int count_loose_object(const struct object_id *oid UNUSED, + struct object_info *oi UNUSED, + void *payload) +{ + unsigned long *count = payload; + (*count)++; + return 0; +} + +int odb_source_loose_count_objects(struct odb_source *source, + enum odb_count_objects_flags flags, + unsigned long *out) +{ + const unsigned hexsz = source->odb->repo->hash_algo->hexsz - 2; + char *path = NULL; + DIR *dir = NULL; + int ret; + + if (flags & ODB_COUNT_OBJECTS_APPROXIMATE) { + unsigned long count = 0; + struct dirent *ent; + + path = xstrfmt("%s/17", source->path); + + dir = opendir(path); + if (!dir) { + if (errno == ENOENT) { + *out = 0; + ret = 0; + goto out; + } + + ret = error_errno("cannot open object shard '%s'", path); + goto out; + } + + while ((ent = readdir(dir)) != NULL) { + if (strspn(ent->d_name, "0123456789abcdef") != hexsz || + ent->d_name[hexsz] != '\0') + continue; + count++; + } + + *out = count * 256; + ret = 0; + } else { + struct odb_for_each_object_options opts = { 0 }; + *out = 0; + ret = odb_source_loose_for_each_object(source, NULL, count_loose_object, + out, &opts); + } + +out: + if (dir) + closedir(dir); + free(path); + return ret; +} + +struct find_abbrev_len_data { + const struct object_id *oid; + unsigned len; +}; + +static int find_abbrev_len_cb(const struct object_id *oid, + struct object_info *oi UNUSED, + void *cb_data) +{ + struct find_abbrev_len_data *data = cb_data; + unsigned len = oid_common_prefix_hexlen(oid, data->oid); + if (len != hash_algos[oid->algo].hexsz && len >= data->len) + data->len = len + 1; + return 0; +} + +int odb_source_loose_find_abbrev_len(struct odb_source *source, + const struct object_id *oid, + unsigned min_len, + unsigned *out) +{ + struct odb_for_each_object_options opts = { + .prefix = oid, + .prefix_hex_len = min_len, + }; + struct find_abbrev_len_data data = { + .oid = oid, + .len = min_len, + }; + int ret; + + ret = odb_source_loose_for_each_object(source, NULL, find_abbrev_len_cb, + &data, &opts); + *out = data.len; + + return ret; +} + static int append_loose_object(const struct object_id *oid, const char *path UNUSED, void *data) @@ -1876,8 +1999,8 @@ static int append_loose_object(const struct object_id *oid, return 0; } -struct oidtree *odb_source_loose_cache(struct odb_source *source, - const struct object_id *oid) +static struct oidtree *odb_source_loose_cache(struct odb_source *source, + const struct object_id *oid) { struct odb_source_files *files = odb_source_files_downcast(source); int subdir_nr = oid->hash[0]; diff --git a/object-file.h b/object-file.h index ff6da65296..3686f182e4 100644 --- a/object-file.h +++ b/object-file.h @@ -75,13 +75,6 @@ int odb_source_loose_write_stream(struct odb_source *source, struct object_id *oid); /* - * Populate and return the loose object cache array corresponding to the - * given object ID. - */ -struct oidtree *odb_source_loose_cache(struct odb_source *source, - const struct object_id *oid); - -/* * Put in `buf` the name of the file in the local object database that * would be used to store a loose object with the specified oid. */ @@ -137,7 +130,33 @@ int odb_source_loose_for_each_object(struct odb_source *source, const struct object_info *request, odb_for_each_object_cb cb, void *cb_data, - unsigned flags); + const struct odb_for_each_object_options *opts); + +/* + * Count the number of loose objects in this source. + * + * The object count is approximated by opening a single sharding directory for + * loose objects and scanning its contents. The result is then extrapolated by + * 256. This should generally work as a reasonable estimate given that the + * object hash is supposed to be indistinguishable from random. + * + * Returns 0 on success, a negative error code otherwise. + */ +int odb_source_loose_count_objects(struct odb_source *source, + enum odb_count_objects_flags flags, + unsigned long *out); + +/* + * Find the shortest unique prefix for the given object ID, where `min_len` is + * the minimum length that the prefix should have. + * + * Returns 0 on success, in which case the computed length will be written to + * `out`. Otherwise, a negative error code is returned. + */ +int odb_source_loose_find_abbrev_len(struct odb_source *source, + const struct object_id *oid, + unsigned min_len, + unsigned *out); /** * format_object_header() is a thin wrapper around s xsnprintf() that diff --git a/object-name.c b/object-name.c index 7b14c3bf9b..21dcdc4a0e 100644 --- a/object-name.c +++ b/object-name.c @@ -15,11 +15,9 @@ #include "refs.h" #include "remote.h" #include "dir.h" +#include "odb.h" #include "oid-array.h" -#include "oidtree.h" -#include "packfile.h" #include "pretty.h" -#include "object-file.h" #include "read-cache-ll.h" #include "repo-settings.h" #include "repository.h" @@ -49,30 +47,29 @@ struct disambiguate_state { unsigned candidate_ok:1; unsigned disambiguate_fn_used:1; unsigned ambiguous:1; - unsigned always_call_fn:1; }; -static void update_candidates(struct disambiguate_state *ds, const struct object_id *current) +static int update_disambiguate_state(const struct object_id *current, + struct object_info *oi UNUSED, + void *cb_data) { + struct disambiguate_state *ds = cb_data; + /* The hash algorithm of current has already been filtered */ - if (ds->always_call_fn) { - ds->ambiguous = ds->fn(ds->repo, current, ds->cb_data) ? 1 : 0; - return; - } if (!ds->candidate_exists) { /* this is the first candidate */ oidcpy(&ds->candidate, current); ds->candidate_exists = 1; - return; + return 0; } else if (oideq(&ds->candidate, current)) { /* the same as what we already have seen */ - return; + return 0; } if (!ds->fn) { /* cannot disambiguate between ds->candidate and current */ ds->ambiguous = 1; - return; + return ds->ambiguous; } if (!ds->candidate_checked) { @@ -85,7 +82,7 @@ static void update_candidates(struct disambiguate_state *ds, const struct object /* discard the candidate; we know it does not satisfy fn */ oidcpy(&ds->candidate, current); ds->candidate_checked = 0; - return; + return 0; } /* if we reach this point, we know ds->candidate satisfies fn */ @@ -96,128 +93,12 @@ static void update_candidates(struct disambiguate_state *ds, const struct object */ ds->candidate_ok = 0; ds->ambiguous = 1; + return ds->ambiguous; } /* otherwise, current can be discarded and candidate is still good */ -} - -static int match_hash(unsigned, const unsigned char *, const unsigned char *); - -static enum cb_next match_prefix(const struct object_id *oid, void *arg) -{ - struct disambiguate_state *ds = arg; - /* no need to call match_hash, oidtree_each did prefix match */ - update_candidates(ds, oid); - return ds->ambiguous ? CB_BREAK : CB_CONTINUE; -} - -static void find_short_object_filename(struct disambiguate_state *ds) -{ - struct odb_source *source; - - for (source = ds->repo->objects->sources; source && !ds->ambiguous; source = source->next) - oidtree_each(odb_source_loose_cache(source, &ds->bin_pfx), - &ds->bin_pfx, ds->len, match_prefix, ds); -} - -static int match_hash(unsigned len, const unsigned char *a, const unsigned char *b) -{ - do { - if (*a != *b) - return 0; - a++; - b++; - len -= 2; - } while (len > 1); - if (len) - if ((*a ^ *b) & 0xf0) - return 0; - return 1; -} - -static void unique_in_midx(struct multi_pack_index *m, - struct disambiguate_state *ds) -{ - for (; m; m = m->base_midx) { - uint32_t num, i, first = 0; - const struct object_id *current = NULL; - int len = ds->len > ds->repo->hash_algo->hexsz ? - ds->repo->hash_algo->hexsz : ds->len; - - if (!m->num_objects) - continue; - - num = m->num_objects + m->num_objects_in_base; - - bsearch_one_midx(&ds->bin_pfx, m, &first); - - /* - * At this point, "first" is the location of the lowest - * object with an object name that could match - * "bin_pfx". See if we have 0, 1 or more objects that - * actually match(es). - */ - for (i = first; i < num && !ds->ambiguous; i++) { - struct object_id oid; - current = nth_midxed_object_oid(&oid, m, i); - if (!match_hash(len, ds->bin_pfx.hash, current->hash)) - break; - update_candidates(ds, current); - } - } -} - -static void unique_in_pack(struct packed_git *p, - struct disambiguate_state *ds) -{ - uint32_t num, i, first = 0; - int len = ds->len > ds->repo->hash_algo->hexsz ? - ds->repo->hash_algo->hexsz : ds->len; - - if (p->multi_pack_index) - return; - - if (open_pack_index(p) || !p->num_objects) - return; - - num = p->num_objects; - bsearch_pack(&ds->bin_pfx, p, &first); - - /* - * At this point, "first" is the location of the lowest object - * with an object name that could match "bin_pfx". See if we have - * 0, 1 or more objects that actually match(es). - */ - for (i = first; i < num && !ds->ambiguous; i++) { - struct object_id oid; - nth_packed_object_id(&oid, p, i); - if (!match_hash(len, ds->bin_pfx.hash, oid.hash)) - break; - update_candidates(ds, &oid); - } -} - -static void find_short_packed_object(struct disambiguate_state *ds) -{ - struct odb_source *source; - struct packed_git *p; - - /* Skip, unless oids from the storage hash algorithm are wanted */ - if (ds->bin_pfx.algo && (&hash_algos[ds->bin_pfx.algo] != ds->repo->hash_algo)) - return; - - odb_prepare_alternates(ds->repo->objects); - for (source = ds->repo->objects->sources; source && !ds->ambiguous; source = source->next) { - struct multi_pack_index *m = get_multi_pack_index(source); - if (m) - unique_in_midx(m, ds); - } - repo_for_each_pack(ds->repo, p) { - if (ds->ambiguous) - break; - unique_in_pack(p, ds); - } + return 0; } static int finish_object_disambiguation(struct disambiguate_state *ds, @@ -348,41 +229,57 @@ int set_disambiguate_hint_config(const char *var, const char *value) return error("unknown hint type for '%s': %s", var, value); } +static int parse_oid_prefix(const char *name, int len, + const struct git_hash_algo *algo, + char *hex_out, + struct object_id *oid_out) +{ + for (int i = 0; i < len; i++) { + unsigned char c = name[i]; + unsigned char val; + if (c >= '0' && c <= '9') { + val = c - '0'; + } else if (c >= 'a' && c <= 'f') { + val = c - 'a' + 10; + } else if (c >= 'A' && c <='F') { + val = c - 'A' + 10; + c -= 'A' - 'a'; + } else { + return -1; + } + + if (hex_out) + hex_out[i] = c; + if (oid_out) { + if (!(i & 1)) + val <<= 4; + oid_out->hash[i >> 1] |= val; + } + } + + if (hex_out) + hex_out[len] = '\0'; + if (oid_out) + oid_out->algo = algo ? hash_algo_by_ptr(algo) : GIT_HASH_UNKNOWN; + + return 0; +} + static int init_object_disambiguation(struct repository *r, const char *name, int len, const struct git_hash_algo *algo, struct disambiguate_state *ds) { - int i; - if (len < MINIMUM_ABBREV || len > GIT_MAX_HEXSZ) return -1; memset(ds, 0, sizeof(*ds)); - for (i = 0; i < len ;i++) { - unsigned char c = name[i]; - unsigned char val; - if (c >= '0' && c <= '9') - val = c - '0'; - else if (c >= 'a' && c <= 'f') - val = c - 'a' + 10; - else if (c >= 'A' && c <='F') { - val = c - 'A' + 10; - c -= 'A' - 'a'; - } - else - return -1; - ds->hex_pfx[i] = c; - if (!(i & 1)) - val <<= 4; - ds->bin_pfx.hash[i >> 1] |= val; - } + if (parse_oid_prefix(name, len, algo, ds->hex_pfx, &ds->bin_pfx) < 0) + return -1; ds->len = len; - ds->hex_pfx[len] = '\0'; ds->repo = r; - ds->bin_pfx.algo = algo ? hash_algo_by_ptr(algo) : GIT_HASH_UNKNOWN; odb_prepare_alternates(r->objects); return 0; } @@ -510,8 +407,8 @@ static int collect_ambiguous(const struct object_id *oid, void *data) return 0; } -static int repo_collect_ambiguous(struct repository *r UNUSED, - const struct object_id *oid, +static int repo_collect_ambiguous(const struct object_id *oid, + struct object_info *oi UNUSED, void *data) { return collect_ambiguous(oid, data); @@ -561,6 +458,7 @@ static enum get_oid_result get_short_oid(struct repository *r, struct object_id *oid, unsigned flags) { + struct odb_for_each_object_options opts = { 0 }; int status; struct disambiguate_state ds; int quietly = !!(flags & GET_OID_QUIETLY); @@ -588,8 +486,11 @@ static enum get_oid_result get_short_oid(struct repository *r, else ds.fn = default_disambiguate_hint; - find_short_object_filename(&ds); - find_short_packed_object(&ds); + opts.prefix = &ds.bin_pfx; + opts.prefix_hex_len = ds.len; + + odb_for_each_object_ext(r->objects, NULL, update_disambiguate_state, + &ds, &opts); status = finish_object_disambiguation(&ds, oid); /* @@ -599,8 +500,8 @@ static enum get_oid_result get_short_oid(struct repository *r, */ if (status == MISSING_OBJECT) { odb_reprepare(r->objects); - find_short_object_filename(&ds); - find_short_packed_object(&ds); + odb_for_each_object_ext(r->objects, NULL, update_disambiguate_state, + &ds, &opts); status = finish_object_disambiguation(&ds, oid); } @@ -648,169 +549,25 @@ int repo_for_each_abbrev(struct repository *r, const char *prefix, const struct git_hash_algo *algo, each_abbrev_fn fn, void *cb_data) { + struct object_id prefix_oid = { 0 }; + struct odb_for_each_object_options opts = { + .prefix = &prefix_oid, + .prefix_hex_len = strlen(prefix), + }; struct oid_array collect = OID_ARRAY_INIT; - struct disambiguate_state ds; int ret; - if (init_object_disambiguation(r, prefix, strlen(prefix), algo, &ds) < 0) + if (parse_oid_prefix(prefix, opts.prefix_hex_len, algo, NULL, &prefix_oid) < 0) return -1; - ds.always_call_fn = 1; - ds.fn = repo_collect_ambiguous; - ds.cb_data = &collect; - find_short_object_filename(&ds); - find_short_packed_object(&ds); + if (odb_for_each_object_ext(r->objects, NULL, repo_collect_ambiguous, &collect, &opts) < 0) + return -1; ret = oid_array_for_each_unique(&collect, fn, cb_data); oid_array_clear(&collect); return ret; } -/* - * Return the slot of the most-significant bit set in "val". There are various - * ways to do this quickly with fls() or __builtin_clzl(), but speed is - * probably not a big deal here. - */ -static unsigned msb(unsigned long val) -{ - unsigned r = 0; - while (val >>= 1) - r++; - return r; -} - -struct min_abbrev_data { - unsigned int init_len; - unsigned int cur_len; - char *hex; - struct repository *repo; - const struct object_id *oid; -}; - -static inline char get_hex_char_from_oid(const struct object_id *oid, - unsigned int pos) -{ - static const char hex[] = "0123456789abcdef"; - - if ((pos & 1) == 0) - return hex[oid->hash[pos >> 1] >> 4]; - else - return hex[oid->hash[pos >> 1] & 0xf]; -} - -static int extend_abbrev_len(const struct object_id *oid, - struct min_abbrev_data *mad) -{ - unsigned int i = mad->init_len; - while (mad->hex[i] && mad->hex[i] == get_hex_char_from_oid(oid, i)) - i++; - - if (mad->hex[i] && i >= mad->cur_len) - mad->cur_len = i + 1; - - return 0; -} - -static int repo_extend_abbrev_len(struct repository *r UNUSED, - const struct object_id *oid, - void *cb_data) -{ - return extend_abbrev_len(oid, cb_data); -} - -static void find_abbrev_len_for_midx(struct multi_pack_index *m, - struct min_abbrev_data *mad) -{ - for (; m; m = m->base_midx) { - int match = 0; - uint32_t num, first = 0; - struct object_id oid; - const struct object_id *mad_oid; - - if (!m->num_objects) - continue; - - num = m->num_objects + m->num_objects_in_base; - mad_oid = mad->oid; - match = bsearch_one_midx(mad_oid, m, &first); - - /* - * first is now the position in the packfile where we - * would insert mad->hash if it does not exist (or the - * position of mad->hash if it does exist). Hence, we - * consider a maximum of two objects nearby for the - * abbreviation length. - */ - mad->init_len = 0; - if (!match) { - if (nth_midxed_object_oid(&oid, m, first)) - extend_abbrev_len(&oid, mad); - } else if (first < num - 1) { - if (nth_midxed_object_oid(&oid, m, first + 1)) - extend_abbrev_len(&oid, mad); - } - if (first > 0) { - if (nth_midxed_object_oid(&oid, m, first - 1)) - extend_abbrev_len(&oid, mad); - } - mad->init_len = mad->cur_len; - } -} - -static void find_abbrev_len_for_pack(struct packed_git *p, - struct min_abbrev_data *mad) -{ - int match = 0; - uint32_t num, first = 0; - struct object_id oid; - const struct object_id *mad_oid; - - if (p->multi_pack_index) - return; - - if (open_pack_index(p) || !p->num_objects) - return; - - num = p->num_objects; - mad_oid = mad->oid; - match = bsearch_pack(mad_oid, p, &first); - - /* - * first is now the position in the packfile where we would insert - * mad->hash if it does not exist (or the position of mad->hash if - * it does exist). Hence, we consider a maximum of two objects - * nearby for the abbreviation length. - */ - mad->init_len = 0; - if (!match) { - if (!nth_packed_object_id(&oid, p, first)) - extend_abbrev_len(&oid, mad); - } else if (first < num - 1) { - if (!nth_packed_object_id(&oid, p, first + 1)) - extend_abbrev_len(&oid, mad); - } - if (first > 0) { - if (!nth_packed_object_id(&oid, p, first - 1)) - extend_abbrev_len(&oid, mad); - } - mad->init_len = mad->cur_len; -} - -static void find_abbrev_len_packed(struct min_abbrev_data *mad) -{ - struct packed_git *p; - - odb_prepare_alternates(mad->repo->objects); - for (struct odb_source *source = mad->repo->objects->sources; source; source = source->next) { - struct multi_pack_index *m = get_multi_pack_index(source); - if (m) - find_abbrev_len_for_midx(m, mad); - } - - repo_for_each_pack(mad->repo, p) - find_abbrev_len_for_pack(p, mad); -} - void strbuf_repo_add_unique_abbrev(struct strbuf *sb, struct repository *repo, const struct object_id *oid, int abbrev_len) { @@ -827,61 +584,19 @@ void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid, } int repo_find_unique_abbrev_r(struct repository *r, char *hex, - const struct object_id *oid, int len) + const struct object_id *oid, int min_len) { const struct git_hash_algo *algo = oid->algo ? &hash_algos[oid->algo] : r->hash_algo; - struct disambiguate_state ds; - struct min_abbrev_data mad; - struct object_id oid_ret; - const unsigned hexsz = algo->hexsz; + unsigned len; - if (len < 0) { - unsigned long count = repo_approximate_object_count(r); - /* - * Add one because the MSB only tells us the highest bit set, - * not including the value of all the _other_ bits (so "15" - * is only one off of 2^4, but the MSB is the 3rd bit. - */ - len = msb(count) + 1; - /* - * We now know we have on the order of 2^len objects, which - * expects a collision at 2^(len/2). But we also care about hex - * chars, not bits, and there are 4 bits per hex. So all - * together we need to divide by 2 and round up. - */ - len = DIV_ROUND_UP(len, 2); - /* - * For very small repos, we stick with our regular fallback. - */ - if (len < FALLBACK_DEFAULT_ABBREV) - len = FALLBACK_DEFAULT_ABBREV; - } + if (odb_find_abbrev_len(r->objects, oid, min_len, &len) < 0) + len = algo->hexsz; oid_to_hex_r(hex, oid); - if (len >= hexsz || !len) - return hexsz; - - mad.repo = r; - mad.init_len = len; - mad.cur_len = len; - mad.hex = hex; - mad.oid = oid; - - find_abbrev_len_packed(&mad); - - if (init_object_disambiguation(r, hex, mad.cur_len, algo, &ds) < 0) - return -1; - - ds.fn = repo_extend_abbrev_len; - ds.always_call_fn = 1; - ds.cb_data = (void *)&mad; - - find_short_object_filename(&ds); - (void)finish_object_disambiguation(&ds, &oid_ret); + hex[len] = 0; - hex[mad.cur_len] = 0; - return mad.cur_len; + return len; } const char *repo_find_unique_abbrev(struct repository *r, @@ -1660,7 +1375,8 @@ static int interpret_empty_at(const char *name, int namelen, int len, struct str static int reinterpret(struct repository *r, const char *name, int namelen, int len, - struct strbuf *buf, unsigned allowed) + struct strbuf *buf, + enum interpret_branch_kind allowed) { /* we have extra data, which might need further processing */ struct strbuf tmp = STRBUF_INIT; @@ -1692,7 +1408,8 @@ static void set_shortened_ref(struct repository *r, struct strbuf *buf, const ch free(s); } -static int branch_interpret_allowed(const char *refname, unsigned allowed) +static int branch_interpret_allowed(const char *refname, + enum interpret_branch_kind allowed) { if (!allowed) return 1; diff --git a/object-name.h b/object-name.h index cda4934cd5..167a9154ea 100644 --- a/object-name.h +++ b/object-name.h @@ -101,9 +101,12 @@ int set_disambiguate_hint_config(const char *var, const char *value); * If the input was ok but there are not N branch switches in the * reflog, it returns 0. */ -#define INTERPRET_BRANCH_LOCAL (1<<0) -#define INTERPRET_BRANCH_REMOTE (1<<1) -#define INTERPRET_BRANCH_HEAD (1<<2) +enum interpret_branch_kind { + INTERPRET_BRANCH_LOCAL = (1 << 0), + INTERPRET_BRANCH_REMOTE = (1 << 1), + INTERPRET_BRANCH_HEAD = (1 << 2), +}; + struct interpret_branch_name_options { /* * If "allowed" is non-zero, it is a treated as a bitfield of allowable @@ -111,7 +114,7 @@ struct interpret_branch_name_options { * ("refs/remotes/"), or "HEAD". If no "allowed" bits are set, any expansion is * allowed, even ones to refs outside of those namespaces. */ - unsigned allowed; + enum interpret_branch_kind allowed; /* * If ^{upstream} or ^{push} (or equivalent) is requested, and the @@ -12,6 +12,7 @@ #include "midx.h" #include "object-file-convert.h" #include "object-file.h" +#include "object-name.h" #include "odb.h" #include "packfile.h" #include "path.h" @@ -896,25 +897,144 @@ int odb_freshen_object(struct object_database *odb, return 0; } +int odb_for_each_object_ext(struct object_database *odb, + const struct object_info *request, + odb_for_each_object_cb cb, + void *cb_data, + const struct odb_for_each_object_options *opts) +{ + int ret; + + odb_prepare_alternates(odb); + for (struct odb_source *source = odb->sources; source; source = source->next) { + if (opts->flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY && !source->local) + continue; + + ret = odb_source_for_each_object(source, request, cb, cb_data, opts); + if (ret) + return ret; + } + + return 0; +} + int odb_for_each_object(struct object_database *odb, const struct object_info *request, odb_for_each_object_cb cb, void *cb_data, unsigned flags) { + struct odb_for_each_object_options opts = { + .flags = flags, + }; + return odb_for_each_object_ext(odb, request, cb, cb_data, &opts); +} + +int odb_count_objects(struct object_database *odb, + enum odb_count_objects_flags flags, + unsigned long *out) +{ + struct odb_source *source; + unsigned long count = 0; int ret; + if (odb->object_count_valid && odb->object_count_flags == flags) { + *out = odb->object_count; + return 0; + } + odb_prepare_alternates(odb); - for (struct odb_source *source = odb->sources; source; source = source->next) { - if (flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY && !source->local) - continue; + for (source = odb->sources; source; source = source->next) { + unsigned long c; + + ret = odb_source_count_objects(source, flags, &c); + if (ret < 0) + goto out; + + count += c; + } + + odb->object_count = count; + odb->object_count_valid = 1; + odb->object_count_flags = flags; + + *out = count; + ret = 0; + +out: + return ret; +} - ret = odb_source_for_each_object(source, request, cb, cb_data, flags); +/* + * Return the slot of the most-significant bit set in "val". There are various + * ways to do this quickly with fls() or __builtin_clzl(), but speed is + * probably not a big deal here. + */ +static unsigned msb(unsigned long val) +{ + unsigned r = 0; + while (val >>= 1) + r++; + return r; +} + +int odb_find_abbrev_len(struct object_database *odb, + const struct object_id *oid, + int min_length, + unsigned *out) +{ + const struct git_hash_algo *algo = + oid->algo ? &hash_algos[oid->algo] : odb->repo->hash_algo; + const unsigned hexsz = algo->hexsz; + unsigned len; + int ret; + + if (min_length < 0) { + unsigned long count; + + if (odb_count_objects(odb, ODB_COUNT_OBJECTS_APPROXIMATE, &count) < 0) + count = 0; + + /* + * Add one because the MSB only tells us the highest bit set, + * not including the value of all the _other_ bits (so "15" + * is only one off of 2^4, but the MSB is the 3rd bit. + */ + len = msb(count) + 1; + /* + * We now know we have on the order of 2^len objects, which + * expects a collision at 2^(len/2). But we also care about hex + * chars, not bits, and there are 4 bits per hex. So all + * together we need to divide by 2 and round up. + */ + len = DIV_ROUND_UP(len, 2); + /* + * For very small repos, we stick with our regular fallback. + */ + if (len < FALLBACK_DEFAULT_ABBREV) + len = FALLBACK_DEFAULT_ABBREV; + } else { + len = min_length; + } + + if (len >= hexsz || !len) { + *out = hexsz; + ret = 0; + goto out; + } + + odb_prepare_alternates(odb); + for (struct odb_source *source = odb->sources; source; source = source->next) { + ret = odb_source_find_abbrev_len(source, oid, len, &len); if (ret) - return ret; + goto out; } - return 0; + ret = 0; + *out = len; + +out: + return ret; } void odb_assert_oid_type(struct object_database *odb, @@ -1030,7 +1150,7 @@ void odb_reprepare(struct object_database *o) for (source = o->sources; source; source = source->next) odb_source_reprepare(source); - o->approximate_object_count_valid = 0; + o->object_count_valid = 0; obj_read_unlock(); } @@ -3,7 +3,6 @@ #include "hashmap.h" #include "object.h" -#include "odb/source.h" #include "oidset.h" #include "oidmap.h" #include "string-list.h" @@ -12,6 +11,7 @@ struct oidmap; struct oidtree; struct strbuf; +struct strvec; struct repository; struct multi_pack_index; @@ -110,10 +110,11 @@ struct object_database { /* * A fast, rough count of the number of objects in the repository. * These two fields are not meant for direct access. Use - * repo_approximate_object_count() instead. + * odb_count_objects() instead. */ - unsigned long approximate_object_count; - unsigned approximate_object_count_valid : 1; + unsigned long object_count; + unsigned object_count_flags; + unsigned object_count_valid : 1; /* * Submodule source paths that will be added as additional sources to @@ -339,6 +340,42 @@ struct object_info { */ #define OBJECT_INFO_INIT { 0 } +/* Flags that can be passed to `odb_read_object_info_extended()`. */ +enum object_info_flags { + /* Invoke lookup_replace_object() on the given hash. */ + OBJECT_INFO_LOOKUP_REPLACE = (1 << 0), + + /* Do not reprepare object sources when the first lookup has failed. */ + OBJECT_INFO_QUICK = (1 << 1), + + /* + * Do not attempt to fetch the object if missing (even if fetch_is_missing is + * nonzero). + */ + OBJECT_INFO_SKIP_FETCH_OBJECT = (1 << 2), + + /* Die if object corruption (not just an object being missing) was detected. */ + OBJECT_INFO_DIE_IF_CORRUPT = (1 << 3), + + /* + * We have already tried reading the object, but it couldn't be found + * via any of the attached sources, and are now doing a second read. + * This second read asks the individual sources to also evaluate + * whether any on-disk state may have changed that may have caused the + * object to appear. + * + * This flag is for internal use, only. The second read only occurs + * when `OBJECT_INFO_QUICK` was not passed. + */ + OBJECT_INFO_SECOND_READ = (1 << 4), + + /* + * This is meant for bulk prefetching of missing blobs in a partial + * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK. + */ + OBJECT_INFO_FOR_PREFETCH = (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK), +}; + /* * Read object info from the object database and populate the `object_info` * structure. Returns 0 on success, a negative error code otherwise. @@ -433,6 +470,34 @@ enum odb_for_each_object_flags { }; /* + * A callback function that can be used to iterate through objects. If given, + * the optional `oi` parameter will be populated the same as if you would call + * `odb_read_object_info()`. + * + * Returning a non-zero error code will cause iteration to abort. The error + * code will be propagated. + */ +typedef int (*odb_for_each_object_cb)(const struct object_id *oid, + struct object_info *oi, + void *cb_data); + +/* + * Options that can be passed to `odb_for_each_object()` and its + * backend-specific implementations. + */ +struct odb_for_each_object_options { + /* A bitfield of `odb_for_each_object_flags`. */ + enum odb_for_each_object_flags flags; + + /* + * If set, only iterate through objects whose first `prefix_hex_len` + * hex characters matches the given prefix. + */ + const struct object_id *prefix; + size_t prefix_hex_len; +}; + +/* * Iterate through all objects contained in the object database. Note that * objects may be iterated over multiple times in case they are either stored * in different backends or in case they are stored in multiple sources. @@ -446,12 +511,56 @@ enum odb_for_each_object_flags { * Returns 0 on success, a negative error code in case a failure occurred, or * an arbitrary non-zero error code returned by the callback itself. */ +int odb_for_each_object_ext(struct object_database *odb, + const struct object_info *request, + odb_for_each_object_cb cb, + void *cb_data, + const struct odb_for_each_object_options *opts); + +/* Same as `odb_for_each_object_ext()` with `opts.flags` set to the given flags. */ int odb_for_each_object(struct object_database *odb, const struct object_info *request, odb_for_each_object_cb cb, void *cb_data, unsigned flags); +enum odb_count_objects_flags { + /* + * Instead of providing an accurate count, allow the number of objects + * to be approximated. Details of how this approximation works are + * subject to the specific source's implementation. + */ + ODB_COUNT_OBJECTS_APPROXIMATE = (1 << 0), +}; + +/* + * Count the number of objects in the given object database. This object count + * may double-count objects that are stored in multiple backends, or which are + * stored multiple times in a single backend. + * + * Returns 0 on success, a negative error code otherwise. The number of objects + * will be assigned to the `out` pointer on success. + */ +int odb_count_objects(struct object_database *odb, + enum odb_count_objects_flags flags, + unsigned long *out); + +/* + * Given an object ID, find the minimum required length required to make the + * object ID unique across the whole object database. + * + * The `min_len` determines the minimum abbreviated length that'll be returned + * by this function. If `min_len < 0`, then the function will set a sensible + * default minimum abbreviation length. + * + * Returns 0 on success, a negative error code otherwise. The computed length + * will be assigned to `*out`. + */ +int odb_find_abbrev_len(struct object_database *odb, + const struct object_id *oid, + int min_len, + unsigned *out); + enum { /* * By default, `odb_write_object()` does not actually write anything diff --git a/odb/source-files.c b/odb/source-files.c index 14cb9adeca..76797569de 100644 --- a/odb/source-files.c +++ b/odb/source-files.c @@ -75,24 +75,77 @@ static int odb_source_files_for_each_object(struct odb_source *source, const struct object_info *request, odb_for_each_object_cb cb, void *cb_data, - unsigned flags) + const struct odb_for_each_object_options *opts) { struct odb_source_files *files = odb_source_files_downcast(source); int ret; - if (!(flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY)) { - ret = odb_source_loose_for_each_object(source, request, cb, cb_data, flags); + if (!(opts->flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY)) { + ret = odb_source_loose_for_each_object(source, request, cb, cb_data, opts); if (ret) return ret; } - ret = packfile_store_for_each_object(files->packed, request, cb, cb_data, flags); + ret = packfile_store_for_each_object(files->packed, request, cb, cb_data, opts); if (ret) return ret; return 0; } +static int odb_source_files_count_objects(struct odb_source *source, + enum odb_count_objects_flags flags, + unsigned long *out) +{ + struct odb_source_files *files = odb_source_files_downcast(source); + unsigned long count; + int ret; + + ret = packfile_store_count_objects(files->packed, flags, &count); + if (ret < 0) + goto out; + + if (!(flags & ODB_COUNT_OBJECTS_APPROXIMATE)) { + unsigned long loose_count; + + ret = odb_source_loose_count_objects(source, flags, &loose_count); + if (ret < 0) + goto out; + + count += loose_count; + } + + *out = count; + ret = 0; + +out: + return ret; +} + +static int odb_source_files_find_abbrev_len(struct odb_source *source, + const struct object_id *oid, + unsigned min_len, + unsigned *out) +{ + struct odb_source_files *files = odb_source_files_downcast(source); + unsigned len = min_len; + int ret; + + ret = packfile_store_find_abbrev_len(files->packed, oid, len, &len); + if (ret < 0) + goto out; + + ret = odb_source_loose_find_abbrev_len(source, oid, len, &len); + if (ret < 0) + goto out; + + *out = len; + ret = 0; + +out: + return ret; +} + static int odb_source_files_freshen_object(struct odb_source *source, const struct object_id *oid) { @@ -220,6 +273,8 @@ struct odb_source_files *odb_source_files_new(struct object_database *odb, files->base.read_object_info = odb_source_files_read_object_info; files->base.read_object_stream = odb_source_files_read_object_stream; files->base.for_each_object = odb_source_files_for_each_object; + files->base.count_objects = odb_source_files_count_objects; + files->base.find_abbrev_len = odb_source_files_find_abbrev_len; files->base.freshen_object = odb_source_files_freshen_object; files->base.write_object = odb_source_files_write_object; files->base.write_object_stream = odb_source_files_write_object_stream; diff --git a/odb/source.h b/odb/source.h index caac558149..a9d7d0b96f 100644 --- a/odb/source.h +++ b/odb/source.h @@ -2,6 +2,7 @@ #define ODB_SOURCE_H #include "object.h" +#include "odb.h" enum odb_source_type { /* @@ -14,62 +15,11 @@ enum odb_source_type { ODB_SOURCE_FILES, }; -/* Flags that can be passed to `odb_read_object_info_extended()`. */ -enum object_info_flags { - /* Invoke lookup_replace_object() on the given hash. */ - OBJECT_INFO_LOOKUP_REPLACE = (1 << 0), - - /* Do not reprepare object sources when the first lookup has failed. */ - OBJECT_INFO_QUICK = (1 << 1), - - /* - * Do not attempt to fetch the object if missing (even if fetch_is_missing is - * nonzero). - */ - OBJECT_INFO_SKIP_FETCH_OBJECT = (1 << 2), - - /* Die if object corruption (not just an object being missing) was detected. */ - OBJECT_INFO_DIE_IF_CORRUPT = (1 << 3), - - /* - * We have already tried reading the object, but it couldn't be found - * via any of the attached sources, and are now doing a second read. - * This second read asks the individual sources to also evaluate - * whether any on-disk state may have changed that may have caused the - * object to appear. - * - * This flag is for internal use, only. The second read only occurs - * when `OBJECT_INFO_QUICK` was not passed. - */ - OBJECT_INFO_SECOND_READ = (1 << 4), - - /* - * This is meant for bulk prefetching of missing blobs in a partial - * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK. - */ - OBJECT_INFO_FOR_PREFETCH = (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK), -}; - struct object_id; -struct object_info; struct odb_read_stream; -struct odb_transaction; -struct odb_write_stream; struct strvec; /* - * A callback function that can be used to iterate through objects. If given, - * the optional `oi` parameter will be populated the same as if you would call - * `odb_read_object_info()`. - * - * Returning a non-zero error code will cause iteration to abort. The error - * code will be propagated. - */ -typedef int (*odb_for_each_object_cb)(const struct object_id *oid, - struct object_info *oi, - void *cb_data); - -/* * The source is the part of the object database that stores the actual * objects. It thus encapsulates the logic to read and write the specific * on-disk format. An object database can have multiple sources: @@ -190,7 +140,34 @@ struct odb_source { const struct object_info *request, odb_for_each_object_cb cb, void *cb_data, - unsigned flags); + const struct odb_for_each_object_options *opts); + + /* + * This callback is expected to count objects in the given object + * database source. The callback function does not have to guarantee + * that only unique objects are counted. The result shall be assigned + * to the `out` pointer. + * + * Accepts `enum odb_count_objects_flag` flags to alter the behaviour. + * + * The callback is expected to return 0 on success, or a negative error + * code otherwise. + */ + int (*count_objects)(struct odb_source *source, + enum odb_count_objects_flags flags, + unsigned long *out); + + /* + * This callback is expected to find the minimum required length to + * make the given object ID unique. + * + * The callback is expected to return a negative error code in case it + * failed, 0 otherwise. + */ + int (*find_abbrev_len)(struct odb_source *source, + const struct object_id *oid, + unsigned min_length, + unsigned *out); /* * This callback is expected to freshen the given object so that its @@ -378,9 +355,33 @@ static inline int odb_source_for_each_object(struct odb_source *source, const struct object_info *request, odb_for_each_object_cb cb, void *cb_data, - unsigned flags) + const struct odb_for_each_object_options *opts) +{ + return source->for_each_object(source, request, cb, cb_data, opts); +} + +/* + * Count the number of objects in the given object database source. + * + * Returns 0 on success, a negative error code otherwise. + */ +static inline int odb_source_count_objects(struct odb_source *source, + enum odb_count_objects_flags flags, + unsigned long *out) +{ + return source->count_objects(source, flags, out); +} + +/* + * Determine the minimum required length to make the given object ID unique in + * the given source. Returns 0 on success, a negative error code otherwise. + */ +static inline int odb_source_find_abbrev_len(struct odb_source *source, + const struct object_id *oid, + unsigned min_len, + unsigned *out) { - return source->for_each_object(source, request, cb, cb_data, flags); + return source->find_abbrev_len(source, oid, min_len, out); } /* diff --git a/odb/streaming.c b/odb/streaming.c index a4355cd245..5927a12954 100644 --- a/odb/streaming.c +++ b/odb/streaming.c @@ -7,6 +7,7 @@ #include "environment.h" #include "repository.h" #include "odb.h" +#include "odb/source.h" #include "odb/streaming.h" #include "replace-object.h" @@ -6,14 +6,6 @@ #include "oidtree.h" #include "hash.h" -struct oidtree_iter_data { - oidtree_iter fn; - void *arg; - size_t *last_nibble_at; - uint32_t algo; - uint8_t last_byte; -}; - void oidtree_init(struct oidtree *ot) { cb_init(&ot->tree); @@ -54,8 +46,7 @@ void oidtree_insert(struct oidtree *ot, const struct object_id *oid) cb_insert(&ot->tree, on, sizeof(*oid)); } - -int oidtree_contains(struct oidtree *ot, const struct object_id *oid) +bool oidtree_contains(struct oidtree *ot, const struct object_id *oid) { struct object_id k; size_t klen = sizeof(k); @@ -69,41 +60,51 @@ int oidtree_contains(struct oidtree *ot, const struct object_id *oid) klen += BUILD_ASSERT_OR_ZERO(offsetof(struct object_id, hash) < offsetof(struct object_id, algo)); - return cb_lookup(&ot->tree, (const uint8_t *)&k, klen) ? 1 : 0; + return !!cb_lookup(&ot->tree, (const uint8_t *)&k, klen); } -static enum cb_next iter(struct cb_node *n, void *arg) +struct oidtree_each_data { + oidtree_each_cb cb; + void *cb_data; + size_t *last_nibble_at; + uint32_t algo; + uint8_t last_byte; +}; + +static int iter(struct cb_node *n, void *cb_data) { - struct oidtree_iter_data *x = arg; + struct oidtree_each_data *data = cb_data; struct object_id k; /* Copy to provide 4-byte alignment needed by struct object_id. */ memcpy(&k, n->k, sizeof(k)); - if (x->algo != GIT_HASH_UNKNOWN && x->algo != k.algo) - return CB_CONTINUE; + if (data->algo != GIT_HASH_UNKNOWN && data->algo != k.algo) + return 0; - if (x->last_nibble_at) { - if ((k.hash[*x->last_nibble_at] ^ x->last_byte) & 0xf0) - return CB_CONTINUE; + if (data->last_nibble_at) { + if ((k.hash[*data->last_nibble_at] ^ data->last_byte) & 0xf0) + return 0; } - return x->fn(&k, x->arg); + return data->cb(&k, data->cb_data); } -void oidtree_each(struct oidtree *ot, const struct object_id *oid, - size_t oidhexsz, oidtree_iter fn, void *arg) +int oidtree_each(struct oidtree *ot, const struct object_id *prefix, + size_t prefix_hex_len, oidtree_each_cb cb, void *cb_data) { - size_t klen = oidhexsz / 2; - struct oidtree_iter_data x = { 0 }; - assert(oidhexsz <= GIT_MAX_HEXSZ); + struct oidtree_each_data data = { + .cb = cb, + .cb_data = cb_data, + .algo = prefix->algo, + }; + size_t klen = prefix_hex_len / 2; + assert(prefix_hex_len <= GIT_MAX_HEXSZ); - x.fn = fn; - x.arg = arg; - x.algo = oid->algo; - if (oidhexsz & 1) { - x.last_byte = oid->hash[klen]; - x.last_nibble_at = &klen; + if (prefix_hex_len & 1) { + data.last_byte = prefix->hash[klen]; + data.last_nibble_at = &klen; } - cb_each(&ot->tree, (const uint8_t *)oid, klen, iter, &x); + + return cb_each(&ot->tree, prefix->hash, klen, iter, &data); } @@ -5,18 +5,52 @@ #include "hash.h" #include "mem-pool.h" +/* + * OID trees are an efficient storage for object IDs that use a critbit tree + * internally. Common prefixes are duplicated and object IDs are stored in a + * way that allow easy iteration over the objects in lexicographic order. As a + * consequence, operations that want to enumerate all object IDs that match a + * given prefix can be answered efficiently. + * + * Note that it is not (yet) possible to store data other than the object IDs + * themselves in this tree. + */ struct oidtree { struct cb_tree tree; struct mem_pool mem_pool; }; -void oidtree_init(struct oidtree *); -void oidtree_clear(struct oidtree *); -void oidtree_insert(struct oidtree *, const struct object_id *); -int oidtree_contains(struct oidtree *, const struct object_id *); +/* Initialize the oidtree so that it is ready for use. */ +void oidtree_init(struct oidtree *ot); -typedef enum cb_next (*oidtree_iter)(const struct object_id *, void *data); -void oidtree_each(struct oidtree *, const struct object_id *, - size_t oidhexsz, oidtree_iter, void *data); +/* + * Release all memory associated with the oidtree and reinitialize it for + * subsequent use. + */ +void oidtree_clear(struct oidtree *ot); + +/* Insert the object ID into the tree. */ +void oidtree_insert(struct oidtree *ot, const struct object_id *oid); + +/* Check whether the tree contains the given object ID. */ +bool oidtree_contains(struct oidtree *ot, const struct object_id *oid); + +/* + * Callback function used for `oidtree_each()`. Returning a non-zero exit code + * will cause iteration to stop. The exit code will be propagated to the caller + * of `oidtree_each()`. + */ +typedef int (*oidtree_each_cb)(const struct object_id *oid, + void *cb_data); + +/* + * Iterate through all object IDs in the tree whose prefix matches the given + * object ID prefix and invoke the callback function on each of them. + * + * Returns any non-zero exit code from the provided callback function. + */ +int oidtree_each(struct oidtree *ot, + const struct object_id *prefix, size_t prefix_hex_len, + oidtree_each_cb cb, void *cb_data); #endif /* OIDTREE_H */ diff --git a/pack-bitmap.c b/pack-bitmap.c index 22419bfb33..f6ec18d83a 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c @@ -441,11 +441,11 @@ char *midx_bitmap_filename(struct multi_pack_index *midx) struct strbuf buf = STRBUF_INIT; if (midx->has_chain) get_split_midx_filename_ext(midx->source, &buf, - get_midx_checksum(midx), + midx_get_checksum_hash(midx), MIDX_EXT_BITMAP); else get_midx_filename_ext(midx->source, &buf, - get_midx_checksum(midx), + midx_get_checksum_hash(midx), MIDX_EXT_BITMAP); return strbuf_detach(&buf, NULL); @@ -502,7 +502,7 @@ static int open_midx_bitmap_1(struct bitmap_index *bitmap_git, if (load_bitmap_header(bitmap_git) < 0) goto cleanup; - if (!hasheq(get_midx_checksum(bitmap_git->midx), bitmap_git->checksum, + if (!hasheq(midx_get_checksum_hash(bitmap_git->midx), bitmap_git->checksum, bitmap_repo(bitmap_git)->hash_algo)) { error(_("checksum doesn't match in MIDX and bitmap")); goto cleanup; @@ -2819,8 +2819,7 @@ void test_bitmap_walk(struct rev_info *revs) if (bitmap_is_midx(found)) fprintf_ln(stderr, "Located via MIDX '%s'.", - hash_to_hex_algop(get_midx_checksum(found->midx), - revs->repo->hash_algo)); + midx_get_checksum_hex(found->midx)); else fprintf_ln(stderr, "Located via pack '%s'.", hash_to_hex_algop(found->pack->hash, diff --git a/pack-check.c b/pack-check.c index 7378c80730..79992bb509 100644 --- a/pack-check.c +++ b/pack-check.c @@ -53,6 +53,7 @@ static int verify_packfile(struct repository *r, struct packed_git *p, struct pack_window **w_curs, verify_fn fn, + void *fn_data, struct progress *progress, uint32_t base_count) { @@ -161,7 +162,7 @@ static int verify_packfile(struct repository *r, oid_to_hex(&oid), p->pack_name); else if (fn) { int eaten = 0; - err |= fn(&oid, type, size, data, &eaten); + err |= fn(&oid, type, size, data, &eaten, fn_data); if (eaten) data = NULL; } @@ -192,7 +193,7 @@ int verify_pack_index(struct packed_git *p) return err; } -int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn, +int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn, void *fn_data, struct progress *progress, uint32_t base_count) { int err = 0; @@ -202,7 +203,7 @@ int verify_pack(struct repository *r, struct packed_git *p, verify_fn fn, if (!p->index_data) return -1; - err |= verify_packfile(r, p, &w_curs, fn, progress, base_count); + err |= verify_packfile(r, p, &w_curs, fn, fn_data, progress, base_count); unuse_pack(&w_curs); return err; diff --git a/pack-revindex.c b/pack-revindex.c index 1fe0afe899..1b67863606 100644 --- a/pack-revindex.c +++ b/pack-revindex.c @@ -394,11 +394,11 @@ int load_midx_revindex(struct multi_pack_index *m) if (m->has_chain) get_split_midx_filename_ext(m->source, &revindex_name, - get_midx_checksum(m), + midx_get_checksum_hash(m), MIDX_EXT_REV); else get_midx_filename_ext(m->source, &revindex_name, - get_midx_checksum(m), + midx_get_checksum_hash(m), MIDX_EXT_REV); ret = load_revindex_from_disk(m->source->odb->repo->hash_algo, @@ -85,7 +85,11 @@ struct pack_idx_entry { struct progress; /* Note, the data argument could be NULL if object type is blob */ -typedef int (*verify_fn)(const struct object_id *, enum object_type, unsigned long, void*, int*); +typedef int (*verify_fn)(const struct object_id *oid, + enum object_type type, + unsigned long size, + void *buffer, int *eaten, + void *fn_data); const char *write_idx_file(struct repository *repo, const char *index_name, @@ -95,7 +99,8 @@ const char *write_idx_file(struct repository *repo, const unsigned char *sha1); int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr); int verify_pack_index(struct packed_git *); -int verify_pack(struct repository *, struct packed_git *, verify_fn fn, struct progress *, uint32_t); +int verify_pack(struct repository *, struct packed_git *, verify_fn fn, void *fn_data, + struct progress *, uint32_t); off_t write_pack_header(struct hashfile *f, uint32_t); void fixup_pack_header_footer(const struct git_hash_algo *, int, unsigned char *, const char *, uint32_t, diff --git a/packfile.c b/packfile.c index 215a23e42b..48c88748b6 100644 --- a/packfile.c +++ b/packfile.c @@ -1101,37 +1101,35 @@ struct packfile_list_entry *packfile_store_get_packs(struct packfile_store *stor return store->packs.head; } -/* - * Give a fast, rough count of the number of objects in the repository. This - * ignores loose objects completely. If you have a lot of them, then either - * you should repack because your performance will be awful, or they are - * all unreachable objects about to be pruned, in which case they're not really - * interesting as a measure of repo size in the first place. - */ -unsigned long repo_approximate_object_count(struct repository *r) +int packfile_store_count_objects(struct packfile_store *store, + enum odb_count_objects_flags flags UNUSED, + unsigned long *out) { - if (!r->objects->approximate_object_count_valid) { - struct odb_source *source; - unsigned long count = 0; - struct packed_git *p; + struct packfile_list_entry *e; + struct multi_pack_index *m; + unsigned long count = 0; + int ret; - odb_prepare_alternates(r->objects); + m = get_multi_pack_index(store->source); + if (m) + count += m->num_objects + m->num_objects_in_base; - for (source = r->objects->sources; source; source = source->next) { - struct multi_pack_index *m = get_multi_pack_index(source); - if (m) - count += m->num_objects + m->num_objects_in_base; + for (e = packfile_store_get_packs(store); e; e = e->next) { + if (e->pack->multi_pack_index) + continue; + if (open_pack_index(e->pack)) { + ret = -1; + goto out; } - repo_for_each_pack(r, p) { - if (p->multi_pack_index || open_pack_index(p)) - continue; - count += p->num_objects; - } - r->objects->approximate_object_count = count; - r->objects->approximate_object_count_valid = 1; + count += e->pack->num_objects; } - return r->objects->approximate_object_count; + + *out = count; + ret = 0; + +out: + return ret; } unsigned long unpack_object_header_buffer(const unsigned char *buf, @@ -2246,7 +2244,8 @@ struct packed_git **packfile_store_get_kept_pack_cache(struct packfile_store *st struct packed_git *p = e->pack; if ((p->pack_keep && (flags & KEPT_PACK_ON_DISK)) || - (p->pack_keep_in_core && (flags & KEPT_PACK_IN_CORE))) { + (p->pack_keep_in_core && (flags & KEPT_PACK_IN_CORE)) || + (p->pack_keep_in_core_open && (flags & KEPT_PACK_IN_CORE_OPEN))) { ALLOC_GROW(packs, nr + 1, alloc); packs[nr++] = p; } @@ -2373,11 +2372,182 @@ static int packfile_store_for_each_object_wrapper(const struct object_id *oid, } } +static int match_hash(unsigned len, const unsigned char *a, const unsigned char *b) +{ + do { + if (*a != *b) + return 0; + a++; + b++; + len -= 2; + } while (len > 1); + if (len) + if ((*a ^ *b) & 0xf0) + return 0; + return 1; +} + +static int for_each_prefixed_object_in_midx( + struct packfile_store *store, + struct multi_pack_index *m, + const struct odb_for_each_object_options *opts, + struct packfile_store_for_each_object_wrapper_data *data) +{ + int ret; + + for (; m; m = m->base_midx) { + uint32_t num, i, first = 0; + int len = opts->prefix_hex_len > m->source->odb->repo->hash_algo->hexsz ? + m->source->odb->repo->hash_algo->hexsz : opts->prefix_hex_len; + + if (!m->num_objects) + continue; + + num = m->num_objects + m->num_objects_in_base; + + bsearch_one_midx(opts->prefix, m, &first); + + /* + * At this point, "first" is the location of the lowest + * object with an object name that could match "opts->prefix". + * See if we have 0, 1 or more objects that actually match(es). + */ + for (i = first; i < num; i++) { + const struct object_id *current = NULL; + struct object_id oid; + + current = nth_midxed_object_oid(&oid, m, i); + + if (!match_hash(len, opts->prefix->hash, current->hash)) + break; + + if (data->request) { + struct object_info oi = *data->request; + + ret = packfile_store_read_object_info(store, current, + &oi, 0); + if (ret) + goto out; + + ret = data->cb(&oid, &oi, data->cb_data); + if (ret) + goto out; + } else { + ret = data->cb(&oid, NULL, data->cb_data); + if (ret) + goto out; + } + } + } + + ret = 0; + +out: + return ret; +} + +static int for_each_prefixed_object_in_pack( + struct packfile_store *store, + struct packed_git *p, + const struct odb_for_each_object_options *opts, + struct packfile_store_for_each_object_wrapper_data *data) +{ + uint32_t num, i, first = 0; + int len = opts->prefix_hex_len > p->repo->hash_algo->hexsz ? + p->repo->hash_algo->hexsz : opts->prefix_hex_len; + int ret; + + num = p->num_objects; + bsearch_pack(opts->prefix, p, &first); + + /* + * At this point, "first" is the location of the lowest object + * with an object name that could match "bin_pfx". See if we have + * 0, 1 or more objects that actually match(es). + */ + for (i = first; i < num; i++) { + struct object_id oid; + + nth_packed_object_id(&oid, p, i); + if (!match_hash(len, opts->prefix->hash, oid.hash)) + break; + + if (data->request) { + struct object_info oi = *data->request; + + ret = packfile_store_read_object_info(store, &oid, &oi, 0); + if (ret) + goto out; + + ret = data->cb(&oid, &oi, data->cb_data); + if (ret) + goto out; + } else { + ret = data->cb(&oid, NULL, data->cb_data); + if (ret) + goto out; + } + } + + ret = 0; + +out: + return ret; +} + +static int packfile_store_for_each_prefixed_object( + struct packfile_store *store, + const struct odb_for_each_object_options *opts, + struct packfile_store_for_each_object_wrapper_data *data) +{ + struct packfile_list_entry *e; + struct multi_pack_index *m; + bool pack_errors = false; + int ret; + + if (opts->flags) + BUG("flags unsupported"); + + store->skip_mru_updates = true; + + m = get_multi_pack_index(store->source); + if (m) { + ret = for_each_prefixed_object_in_midx(store, m, opts, data); + if (ret) + goto out; + } + + for (e = packfile_store_get_packs(store); e; e = e->next) { + if (e->pack->multi_pack_index) + continue; + + if (open_pack_index(e->pack)) { + pack_errors = true; + continue; + } + + if (!e->pack->num_objects) + continue; + + ret = for_each_prefixed_object_in_pack(store, e->pack, opts, data); + if (ret) + goto out; + } + + ret = 0; + +out: + store->skip_mru_updates = false; + if (!ret && pack_errors) + ret = -1; + return ret; +} + int packfile_store_for_each_object(struct packfile_store *store, const struct object_info *request, odb_for_each_object_cb cb, void *cb_data, - unsigned flags) + const struct odb_for_each_object_options *opts) { struct packfile_store_for_each_object_wrapper_data data = { .store = store, @@ -2388,20 +2558,23 @@ int packfile_store_for_each_object(struct packfile_store *store, struct packfile_list_entry *e; int pack_errors = 0, ret; + if (opts->prefix) + return packfile_store_for_each_prefixed_object(store, opts, &data); + store->skip_mru_updates = true; for (e = packfile_store_get_packs(store); e; e = e->next) { struct packed_git *p = e->pack; - if ((flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local) + if ((opts->flags & ODB_FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local) continue; - if ((flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY) && + if ((opts->flags & ODB_FOR_EACH_OBJECT_PROMISOR_ONLY) && !p->pack_promisor) continue; - if ((flags & ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS) && + if ((opts->flags & ODB_FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS) && p->pack_keep_in_core) continue; - if ((flags & ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS) && + if ((opts->flags & ODB_FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS) && p->pack_keep) continue; if (open_pack_index(p)) { @@ -2410,7 +2583,7 @@ int packfile_store_for_each_object(struct packfile_store *store, } ret = for_each_object_in_pack(p, packfile_store_for_each_object_wrapper, - &data, flags); + &data, opts->flags); if (ret) goto out; } @@ -2425,6 +2598,117 @@ out: return ret; } +static int extend_abbrev_len(const struct object_id *a, + const struct object_id *b, + unsigned *out) +{ + unsigned len = oid_common_prefix_hexlen(a, b); + if (len != hash_algos[a->algo].hexsz && len >= *out) + *out = len + 1; + return 0; +} + +static void find_abbrev_len_for_midx(struct multi_pack_index *m, + const struct object_id *oid, + unsigned min_len, + unsigned *out) +{ + unsigned len = min_len; + + for (; m; m = m->base_midx) { + int match = 0; + uint32_t num, first = 0; + struct object_id found_oid; + + if (!m->num_objects) + continue; + + num = m->num_objects + m->num_objects_in_base; + match = bsearch_one_midx(oid, m, &first); + + /* + * first is now the position in the packfile where we + * would insert the object ID if it does not exist (or the + * position of the object ID if it does exist). Hence, we + * consider a maximum of two objects nearby for the + * abbreviation length. + */ + + if (!match) { + if (nth_midxed_object_oid(&found_oid, m, first)) + extend_abbrev_len(&found_oid, oid, &len); + } else if (first < num - 1) { + if (nth_midxed_object_oid(&found_oid, m, first + 1)) + extend_abbrev_len(&found_oid, oid, &len); + } + if (first > 0) { + if (nth_midxed_object_oid(&found_oid, m, first - 1)) + extend_abbrev_len(&found_oid, oid, &len); + } + } + + *out = len; +} + +static void find_abbrev_len_for_pack(struct packed_git *p, + const struct object_id *oid, + unsigned min_len, + unsigned *out) +{ + int match; + uint32_t num, first = 0; + struct object_id found_oid; + unsigned len = min_len; + + num = p->num_objects; + match = bsearch_pack(oid, p, &first); + + /* + * first is now the position in the packfile where we would insert + * the object ID if it does not exist (or the position of mad->hash if + * it does exist). Hence, we consider a maximum of two objects + * nearby for the abbreviation length. + */ + if (!match) { + if (!nth_packed_object_id(&found_oid, p, first)) + extend_abbrev_len(&found_oid, oid, &len); + } else if (first < num - 1) { + if (!nth_packed_object_id(&found_oid, p, first + 1)) + extend_abbrev_len(&found_oid, oid, &len); + } + if (first > 0) { + if (!nth_packed_object_id(&found_oid, p, first - 1)) + extend_abbrev_len(&found_oid, oid, &len); + } + + *out = len; +} + +int packfile_store_find_abbrev_len(struct packfile_store *store, + const struct object_id *oid, + unsigned min_len, + unsigned *out) +{ + struct packfile_list_entry *e; + struct multi_pack_index *m; + + m = get_multi_pack_index(store->source); + if (m) + find_abbrev_len_for_midx(m, oid, min_len, &min_len); + + for (e = packfile_store_get_packs(store); e; e = e->next) { + if (e->pack->multi_pack_index) + continue; + if (open_pack_index(e->pack) || !e->pack->num_objects) + continue; + + find_abbrev_len_for_pack(e->pack, oid, min_len, &min_len); + } + + *out = min_len; + return 0; +} + struct add_promisor_object_data { struct repository *repo; struct oidset *set; diff --git a/packfile.h b/packfile.h index 8b04a258a7..6e8802e2ed 100644 --- a/packfile.h +++ b/packfile.h @@ -28,6 +28,7 @@ struct packed_git { unsigned pack_local:1, pack_keep:1, pack_keep_in_core:1, + pack_keep_in_core_open:1, freshened:1, do_not_close:1, pack_promisor:1, @@ -266,9 +267,20 @@ int packfile_store_freshen_object(struct packfile_store *store, enum kept_pack_type { KEPT_PACK_ON_DISK = (1 << 0), KEPT_PACK_IN_CORE = (1 << 1), + KEPT_PACK_IN_CORE_OPEN = (1 << 2), }; /* + * Count the number objects contained in the given packfile store. If + * successful, the number of objects will be written to the `out` pointer. + * + * Return 0 on success, a negative error code otherwise. + */ +int packfile_store_count_objects(struct packfile_store *store, + enum odb_count_objects_flags flags, + unsigned long *out); + +/* * Retrieve the cache of kept packs from the given packfile store. Accepts a * combination of `kept_pack_type` flags. The cache is computed on demand and * will be recomputed whenever the flags change. @@ -357,7 +369,12 @@ int packfile_store_for_each_object(struct packfile_store *store, const struct object_info *request, odb_for_each_object_cb cb, void *cb_data, - unsigned flags); + const struct odb_for_each_object_options *opts); + +int packfile_store_find_abbrev_len(struct packfile_store *store, + const struct object_id *oid, + unsigned min_len, + unsigned *out); /* A hook to report invalid files in pack directory */ #define PACKDIR_FILE_PACK 1 @@ -365,12 +382,6 @@ int packfile_store_for_each_object(struct packfile_store *store, #define PACKDIR_FILE_GARBAGE 4 extern void (*report_garbage)(unsigned seen_bits, const char *path); -/* - * Give a rough count of objects in the repository. This sacrifices accuracy - * for speed. - */ -unsigned long repo_approximate_object_count(struct repository *r); - void pack_report(struct repository *repo); /* diff --git a/path-walk.c b/path-walk.c index 364e4cfa19..6e426af433 100644 --- a/path-walk.c +++ b/path-walk.c @@ -11,6 +11,7 @@ #include "list-objects.h" #include "object.h" #include "oid-array.h" +#include "path.h" #include "prio-queue.h" #include "repository.h" #include "revision.h" @@ -62,6 +63,8 @@ struct path_walk_context { */ struct prio_queue path_stack; struct strset path_stack_pushed; + + unsigned exact_pathspecs:1; }; static int compare_by_type(const void *one, const void *two, void *cb_data) @@ -171,7 +174,7 @@ static int add_tree_entries(struct path_walk_context *ctx, if (!o) { error(_("failed to find object %s"), - oid_to_hex(&o->oid)); + oid_to_hex(&entry.oid)); return -1; } @@ -206,6 +209,33 @@ static int add_tree_entries(struct path_walk_context *ctx, match != MATCHED) continue; } + if (ctx->revs->prune_data.nr && ctx->exact_pathspecs) { + struct pathspec *pd = &ctx->revs->prune_data; + bool found = false; + int did_strip_suffix = strbuf_strip_suffix(&path, "/"); + + + for (int i = 0; i < pd->nr; i++) { + struct pathspec_item *item = &pd->items[i]; + + /* + * Continue if either is a directory prefix + * of the other. + */ + if (dir_prefix(path.buf, item->match) || + dir_prefix(item->match, path.buf)) { + found = true; + break; + } + } + + if (did_strip_suffix) + strbuf_addch(&path, '/'); + + /* Skip paths that do not match the prefix. */ + if (!found) + continue; + } add_path_to_list(ctx, path.buf, type, &entry.oid, !(o->flags & UNINTERESTING)); @@ -274,6 +304,13 @@ static int walk_path(struct path_walk_context *ctx, return 0; } + if (list->type == OBJ_BLOB && + ctx->revs->prune_data.nr && + !match_pathspec(ctx->repo->index, &ctx->revs->prune_data, + path, strlen(path), 0, + NULL, 0)) + return 0; + /* Evaluate function pointer on this data, if requested. */ if ((list->type == OBJ_TREE && ctx->info->trees) || (list->type == OBJ_BLOB && ctx->info->blobs) || @@ -481,6 +518,12 @@ int walk_objects_by_path(struct path_walk_info *info) if (info->tags) info->revs->tag_objects = 1; + if (ctx.revs->prune_data.nr) { + if (!ctx.revs->prune_data.has_wildcard && + !ctx.revs->prune_data.magic) + ctx.exact_pathspecs = 1; + } + /* Insert a single list for the root tree into the paths. */ CALLOC_ARRAY(root_tree_list, 1); root_tree_list->type = OBJ_TREE; @@ -56,7 +56,7 @@ static void strbuf_cleanup_path(struct strbuf *sb) strbuf_remove(sb, 0, path - sb->buf); } -static int dir_prefix(const char *buf, const char *dir) +int dir_prefix(const char *buf, const char *dir) { size_t len = strlen(dir); return !strncmp(buf, dir, len) && @@ -112,6 +112,12 @@ const char *repo_submodule_path_replace(struct repository *repo, const char *fmt, ...) __attribute__((format (printf, 4, 5))); +/* + * Given a directory name 'dir' (not ending with a trailing '/'), + * determine if 'buf' is equal to 'dir' or has prefix 'dir'+'/'. + */ +int dir_prefix(const char *buf, const char *dir); + void report_linked_checkout_garbage(struct repository *r); /* @@ -1549,6 +1549,21 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */ if (!commit->object.parsed) parse_object(the_repository, &commit->object.oid); + if (starts_with(placeholder, "(count)")) { + if (!c->pretty_ctx->rev) + die(_("%s is not supported by this command"), "%(count)"); + strbuf_addf(sb, "%0*d", decimal_width(c->pretty_ctx->rev->total), + c->pretty_ctx->rev->nr); + return 7; + } + + if (starts_with(placeholder, "(total)")) { + if (!c->pretty_ctx->rev) + die(_("%s is not supported by this command"), "%(total)"); + strbuf_addf(sb, "%d", c->pretty_ctx->rev->total); + return 7; + } + switch (placeholder[0]) { case 'H': /* commit hash */ strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_COMMIT)); diff --git a/range-diff.c b/range-diff.c index 57edff40a8..2712a9a107 100644 --- a/range-diff.c +++ b/range-diff.c @@ -140,7 +140,7 @@ static int read_patches(const char *range, struct string_list *list, if (eol) *eol = '\n'; orig_len = len; - len = parse_git_diff_header(&root, &linenr, 0, line, + len = parse_git_diff_header(&root, NULL, &linenr, 0, line, len, size, &patch); if (len < 0) { error(_("could not parse git header '%.*s'"), diff --git a/read-cache.c b/read-cache.c index 5049f9baca..b1074fbf06 100644 --- a/read-cache.c +++ b/read-cache.c @@ -2309,13 +2309,9 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist) } munmap((void *)mmap, mmap_size); - /* - * TODO trace2: replace "the_repository" with the actual repo instance - * that is associated with the given "istate". - */ - trace2_data_intmax("index", the_repository, "read/version", + trace2_data_intmax("index", istate->repo, "read/version", istate->version); - trace2_data_intmax("index", the_repository, "read/cache_nr", + trace2_data_intmax("index", istate->repo, "read/cache_nr", istate->cache_nr); /* @@ -2360,16 +2356,12 @@ int read_index_from(struct index_state *istate, const char *path, if (istate->initialized) return istate->cache_nr; - /* - * TODO trace2: replace "the_repository" with the actual repo instance - * that is associated with the given "istate". - */ - trace2_region_enter_printf("index", "do_read_index", the_repository, + trace2_region_enter_printf("index", "do_read_index", istate->repo, "%s", path); trace_performance_enter(); ret = do_read_index(istate, path, 0); trace_performance_leave("read cache %s", path); - trace2_region_leave_printf("index", "do_read_index", the_repository, + trace2_region_leave_printf("index", "do_read_index", istate->repo, "%s", path); split_index = istate->split_index; @@ -3096,13 +3088,9 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, istate->timestamp.nsec = ST_MTIME_NSEC(st); trace_performance_since(start, "write index, changed mask = %x", istate->cache_changed); - /* - * TODO trace2: replace "the_repository" with the actual repo instance - * that is associated with the given "istate". - */ - trace2_data_intmax("index", the_repository, "write/version", + trace2_data_intmax("index", istate->repo, "write/version", istate->version); - trace2_data_intmax("index", the_repository, "write/cache_nr", + trace2_data_intmax("index", istate->repo, "write/cache_nr", istate->cache_nr); ret = 0; @@ -3144,14 +3132,10 @@ static int do_write_locked_index(struct index_state *istate, return ret; } - /* - * TODO trace2: replace "the_repository" with the actual repo instance - * that is associated with the given "istate". - */ - trace2_region_enter_printf("index", "do_write_index", the_repository, + trace2_region_enter_printf("index", "do_write_index", istate->repo, "%s", get_lock_file_path(lock)); ret = do_write_index(istate, lock->tempfile, write_extensions, flags); - trace2_region_leave_printf("index", "do_write_index", the_repository, + trace2_region_leave_printf("index", "do_write_index", istate->repo, "%s", get_lock_file_path(lock)); if (was_full) @@ -64,6 +64,9 @@ const char *ref_storage_format_to_name(enum ref_storage_format ref_storage_forma return be->name; } +static const char *abort_by_ref_transaction_hook = + N_("in '%s' phase, update aborted by the reference-transaction hook"); + /* * How to handle various characters in refnames: * 0: An acceptable character for refs @@ -740,7 +743,8 @@ static char *substitute_branch_name(struct repository *r, return NULL; } -void copy_branchname(struct strbuf *sb, const char *name, unsigned allowed) +void copy_branchname(struct strbuf *sb, const char *name, + enum interpret_branch_kind allowed) { int len = strlen(name); struct interpret_branch_name_options options = { @@ -2591,7 +2595,8 @@ static int transaction_hook_feed_stdin(int hook_stdin_fd, void *pp_cb, void *pp_ static void *transaction_feed_cb_data_alloc(void *feed_pipe_ctx UNUSED) { - struct transaction_feed_cb_data *data = xmalloc(sizeof(*data)); + struct transaction_feed_cb_data *data; + CALLOC_ARRAY(data, 1); strbuf_init(&data->buf, 0); data->index = 0; return data; @@ -2655,6 +2660,13 @@ int ref_transaction_prepare(struct ref_transaction *transaction, if (ref_update_reject_duplicates(&transaction->refnames, err)) return REF_TRANSACTION_ERROR_GENERIC; + /* Preparing checks before locking references */ + ret = run_transaction_hook(transaction, "preparing"); + if (ret) { + ref_transaction_abort(transaction, err); + die(_(abort_by_ref_transaction_hook), "preparing"); + } + ret = refs->be->transaction_prepare(refs, transaction, err); if (ret) return ret; @@ -2662,7 +2674,7 @@ int ref_transaction_prepare(struct ref_transaction *transaction, ret = run_transaction_hook(transaction, "prepared"); if (ret) { ref_transaction_abort(transaction, err); - die(_("ref updates aborted by hook")); + die(_(abort_by_ref_transaction_hook), "prepared"); } return 0; @@ -1,6 +1,7 @@ #ifndef REFS_H #define REFS_H +#include "object-name.h" #include "commit.h" #include "repository.h" #include "repo-settings.h" @@ -225,7 +226,7 @@ char *repo_default_branch_name(struct repository *r, int quiet); * repo_interpret_branch_name() for details. */ void copy_branchname(struct strbuf *sb, const char *name, - unsigned allowed); + enum interpret_branch_kind allowed); /* * Like copy_branchname() above, but confirm that the result is diff --git a/refs/files-backend.c b/refs/files-backend.c index 7ce0d57478..0537a72b2a 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1813,7 +1813,7 @@ static int commit_ref(struct ref_lock *lock) size_t len = strlen(path); struct strbuf sb_path = STRBUF_INIT; - strbuf_attach(&sb_path, path, len, len); + strbuf_attach(&sb_path, path, len, len + 1); /* * If this fails, commit_lock_file() will also fail @@ -85,7 +85,7 @@ static int parse_refspec(struct refspec_item *item, const char *refspec, int fet if (!*item->src) return 0; /* negative refspecs must not be empty */ else if (llen == the_hash_algo->hexsz && !get_oid_hex(item->src, &unused)) - return 0; /* negative refpsecs cannot be exact sha1 */ + return 0; /* negative refspecs cannot be exact sha1 */ else if (!check_refname_format(item->src, flags)) ; /* valid looking ref is ok */ else diff --git a/remote-curl.c b/remote-curl.c index 92e40bb682..aba60d5712 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -529,6 +529,17 @@ static struct discovery *discover_refs(const char *service, int for_push) show_http_message(&type, &charset, &buffer); die(_("unable to access '%s' with http.pinnedPubkey configuration: %s"), transport_anonymize_url(url.buf), curl_errorstr); + case HTTP_RATE_LIMITED: + if (http_options.retry_after > 0) { + show_http_message(&type, &charset, &buffer); + die(_("rate limited by '%s', please try again in %ld seconds"), + transport_anonymize_url(url.buf), + http_options.retry_after); + } else { + show_http_message(&type, &charset, &buffer); + die(_("rate limited by '%s', please try again later"), + transport_anonymize_url(url.buf)); + } default: show_http_message(&type, &charset, &buffer); die(_("unable to access '%s': %s"), @@ -1552,6 +1563,13 @@ int cmd_main(int argc, const char **argv) goto cleanup; } + /* + * yuck, see 9e89dcb66a (builtin/ls-remote: fall back to SHA1 outside + * of a repo, 2024-08-02) + */ + if (nongit) + repo_set_hash_algo(the_repository, GIT_HASH_DEFAULT); + options.verbosity = 1; options.progress = !!isatty(2); options.thin = 1; @@ -8,6 +8,7 @@ #include "refs.h" #include "replay.h" #include "revision.h" +#include "sequencer.h" #include "strmap.h" #include "tree.h" @@ -17,6 +18,11 @@ */ #define the_repository DO_NOT_USE_THE_REPOSITORY +enum replay_mode { + REPLAY_MODE_PICK, + REPLAY_MODE_REVERT, +}; + static const char *short_commit_name(struct repository *repo, struct commit *commit) { @@ -50,15 +56,37 @@ static char *get_author(const char *message) return NULL; } +static void generate_revert_message(struct strbuf *msg, + struct commit *commit, + struct repository *repo) +{ + const char *out_enc = get_commit_output_encoding(); + const char *message = repo_logmsg_reencode(repo, commit, NULL, out_enc); + const char *subject_start; + int subject_len; + char *subject; + + subject_len = find_commit_subject(message, &subject_start); + subject = xmemdupz(subject_start, subject_len); + + sequencer_format_revert_message(repo, subject, commit, + commit->parents ? commit->parents->item : NULL, + false, msg); + + free(subject); + repo_unuse_commit_buffer(repo, commit, message); +} + static struct commit *create_commit(struct repository *repo, struct tree *tree, struct commit *based_on, - struct commit *parent) + struct commit *parent, + enum replay_mode mode) { struct object_id ret; struct object *obj = NULL; struct commit_list *parents = NULL; - char *author; + char *author = NULL; char *sign_commit = NULL; /* FIXME: cli users might want to sign again */ struct commit_extra_header *extra = NULL; struct strbuf msg = STRBUF_INIT; @@ -70,9 +98,16 @@ static struct commit *create_commit(struct repository *repo, commit_list_insert(parent, &parents); extra = read_commit_extra_headers(based_on, exclude_gpgsig); - find_commit_subject(message, &orig_message); - strbuf_addstr(&msg, orig_message); - author = get_author(message); + if (mode == REPLAY_MODE_REVERT) { + generate_revert_message(&msg, based_on, repo); + /* For revert, use current user as author (NULL = use default) */ + } else if (mode == REPLAY_MODE_PICK) { + find_commit_subject(message, &orig_message); + strbuf_addstr(&msg, orig_message); + author = get_author(message); + } else { + BUG("unexpected replay mode %d", mode); + } reset_ident_date(); if (commit_tree_extended(msg.buf, msg.len, &tree->object.oid, parents, &ret, author, NULL, sign_commit, extra)) { @@ -153,11 +188,35 @@ static void get_ref_information(struct repository *repo, } } +static void set_up_branch_mode(struct repository *repo, + char **branch_name, + const char *option_name, + struct ref_info *rinfo, + struct commit **onto) +{ + struct object_id oid; + char *fullname = NULL; + + if (repo_dwim_ref(repo, *branch_name, strlen(*branch_name), + &oid, &fullname, 0) == 1) { + free(*branch_name); + *branch_name = fullname; + } else { + die(_("argument to %s must be a reference"), option_name); + } + *onto = peel_committish(repo, *branch_name, option_name); + if (rinfo->positive_refexprs > 1) + die(_("'%s' cannot be used with multiple revision ranges " + "because the ordering would be ill-defined"), + option_name); +} + static void set_up_replay_mode(struct repository *repo, struct rev_cmdline_info *cmd_info, const char *onto_name, bool *detached_head, char **advance_name, + char **revert_name, struct commit **onto, struct strset **update_refs) { @@ -172,9 +231,6 @@ static void set_up_replay_mode(struct repository *repo, if (!rinfo.positive_refexprs) die(_("need some commits to replay")); - if (!onto_name == !*advance_name) - BUG("one and only one of onto_name and *advance_name must be given"); - if (onto_name) { *onto = peel_committish(repo, onto_name, "--onto"); if (rinfo.positive_refexprs < @@ -183,23 +239,12 @@ static void set_up_replay_mode(struct repository *repo, *update_refs = xcalloc(1, sizeof(**update_refs)); **update_refs = rinfo.positive_refs; memset(&rinfo.positive_refs, 0, sizeof(**update_refs)); + } else if (*advance_name) { + set_up_branch_mode(repo, advance_name, "--advance", &rinfo, onto); + } else if (*revert_name) { + set_up_branch_mode(repo, revert_name, "--revert", &rinfo, onto); } else { - struct object_id oid; - char *fullname = NULL; - - if (!*advance_name) - BUG("expected either onto_name or *advance_name in this function"); - - if (repo_dwim_ref(repo, *advance_name, strlen(*advance_name), - &oid, &fullname, 0) == 1) { - free(*advance_name); - *advance_name = fullname; - } else { - die(_("argument to --advance must be a reference")); - } - *onto = peel_committish(repo, *advance_name, "--advance"); - if (rinfo.positive_refexprs > 1) - die(_("cannot advance target with multiple sources because ordering would be ill-defined")); + BUG("expected one of onto_name, *advance_name, or *revert_name"); } strset_clear(&rinfo.negative_refs); strset_clear(&rinfo.positive_refs); @@ -209,7 +254,10 @@ static struct commit *mapped_commit(kh_oid_map_t *replayed_commits, struct commit *commit, struct commit *fallback) { - khint_t pos = kh_get_oid_map(replayed_commits, commit->object.oid); + khint_t pos; + if (!commit) + return fallback; + pos = kh_get_oid_map(replayed_commits, commit->object.oid); if (pos == kh_end(replayed_commits)) return fallback; return kh_value(replayed_commits, pos); @@ -220,37 +268,66 @@ static struct commit *pick_regular_commit(struct repository *repo, kh_oid_map_t *replayed_commits, struct commit *onto, struct merge_options *merge_opt, - struct merge_result *result) + struct merge_result *result, + enum replay_mode mode) { struct commit *base, *replayed_base; struct tree *pickme_tree, *base_tree, *replayed_base_tree; - base = pickme->parents->item; - replayed_base = mapped_commit(replayed_commits, base, onto); + if (pickme->parents) { + base = pickme->parents->item; + base_tree = repo_get_commit_tree(repo, base); + } else { + base = NULL; + base_tree = lookup_tree(repo, repo->hash_algo->empty_tree); + } + replayed_base = mapped_commit(replayed_commits, base, onto); replayed_base_tree = repo_get_commit_tree(repo, replayed_base); pickme_tree = repo_get_commit_tree(repo, pickme); - base_tree = repo_get_commit_tree(repo, base); - merge_opt->branch1 = short_commit_name(repo, replayed_base); - merge_opt->branch2 = short_commit_name(repo, pickme); - merge_opt->ancestor = xstrfmt("parent of %s", merge_opt->branch2); + if (mode == REPLAY_MODE_PICK) { + /* Cherry-pick: normal order */ + merge_opt->branch1 = short_commit_name(repo, replayed_base); + merge_opt->branch2 = short_commit_name(repo, pickme); + if (pickme->parents) + merge_opt->ancestor = xstrfmt("parent of %s", merge_opt->branch2); + else + merge_opt->ancestor = xstrdup("empty tree"); + + merge_incore_nonrecursive(merge_opt, + base_tree, + replayed_base_tree, + pickme_tree, + result); + + free((char *)merge_opt->ancestor); + } else if (mode == REPLAY_MODE_REVERT) { + /* Revert: swap base and pickme to reverse the diff */ + const char *pickme_name = short_commit_name(repo, pickme); + merge_opt->branch1 = short_commit_name(repo, replayed_base); + merge_opt->branch2 = xstrfmt("parent of %s", pickme_name); + merge_opt->ancestor = pickme_name; - merge_incore_nonrecursive(merge_opt, - base_tree, - replayed_base_tree, - pickme_tree, - result); + merge_incore_nonrecursive(merge_opt, + pickme_tree, + replayed_base_tree, + base_tree, + result); - free((char*)merge_opt->ancestor); + free((char *)merge_opt->branch2); + } else { + BUG("unexpected replay mode %d", mode); + } merge_opt->ancestor = NULL; + merge_opt->branch2 = NULL; if (!result->clean) return NULL; /* Drop commits that become empty */ if (oideq(&replayed_base_tree->object.oid, &result->tree->object.oid) && !oideq(&pickme_tree->object.oid, &base_tree->object.oid)) return replayed_base; - return create_commit(repo, result->tree, pickme, replayed_base); + return create_commit(repo, result->tree, pickme, replayed_base, mode); } void replay_result_release(struct replay_result *result) @@ -287,13 +364,16 @@ int replay_revisions(struct rev_info *revs, }; bool detached_head; char *advance; + char *revert; + enum replay_mode mode = REPLAY_MODE_PICK; int ret; advance = xstrdup_or_null(opts->advance); + revert = xstrdup_or_null(opts->revert); + if (revert) + mode = REPLAY_MODE_REVERT; set_up_replay_mode(revs->repo, &revs->cmdline, opts->onto, - &detached_head, &advance, &onto, &update_refs); - - /* FIXME: Should allow replaying commits with the first as a root commit */ + &detached_head, &advance, &revert, &onto, &update_refs); if (prepare_revision_walk(revs) < 0) { ret = error(_("error preparing revisions")); @@ -309,13 +389,12 @@ int replay_revisions(struct rev_info *revs, khint_t pos; int hr; - if (!commit->parents) - die(_("replaying down from root commit is not supported yet!")); - if (commit->parents->next) + if (commit->parents && commit->parents->next) die(_("replaying merge commits is not supported yet!")); last_commit = pick_regular_commit(revs->repo, commit, replayed_commits, - onto, &merge_opt, &result); + mode == REPLAY_MODE_REVERT ? last_commit : onto, + &merge_opt, &result, mode); if (!last_commit) break; @@ -327,7 +406,7 @@ int replay_revisions(struct rev_info *revs, kh_value(replayed_commits, pos) = last_commit; /* Update any necessary branches */ - if (advance) + if (advance || revert) continue; for (decoration = get_name_decoration(&commit->object); @@ -361,11 +440,13 @@ int replay_revisions(struct rev_info *revs, goto out; } - /* In --advance mode, advance the target ref */ - if (advance) - replay_result_queue_update(out, advance, + /* In --advance or --revert mode, update the target ref */ + if (advance || revert) { + const char *ref = advance ? advance : revert; + replay_result_queue_update(out, ref, &onto->object.oid, &last_commit->object.oid); + } ret = 0; @@ -377,5 +458,6 @@ out: kh_destroy_oid_map(replayed_commits); merge_finalize(&merge_opt, &result); free(advance); + free(revert); return ret; } @@ -13,7 +13,7 @@ struct replay_revisions_options { /* * Starting point at which to create the new commits; must be a branch * name. The branch will be updated to point to the rewritten commits. - * This option is mutually exclusive with `onto`. + * This option is mutually exclusive with `onto` and `revert`. */ const char *advance; @@ -22,7 +22,14 @@ struct replay_revisions_options { * committish. References pointing at decendants of `onto` will be * updated to point to the new commits. */ - const char *onto; + const char *onto; + + /* + * Starting point at which to create revert commits; must be a branch + * name. The branch will be updated to point to the revert commits. + * This option is mutually exclusive with `onto` and `advance`. + */ + const char *revert; /* * Update branches that point at commits in the given revision range. diff --git a/repository.c b/repository.c index 0b8f7ec200..9e5537f539 100644 --- a/repository.c +++ b/repository.c @@ -3,6 +3,7 @@ #include "repository.h" #include "hook.h" #include "odb.h" +#include "odb/source.h" #include "config.h" #include "gettext.h" #include "object.h" @@ -403,12 +403,8 @@ static int handle_conflict(struct strbuf *out, struct rerere_io *io, strbuf_addbuf(out, &two); rerere_strbuf_putconflict(out, '>', marker_size); if (ctx) { - git_hash_update(ctx, one.buf ? - one.buf : "", - one.len + 1); - git_hash_update(ctx, two.buf ? - two.buf : "", - two.len + 1); + git_hash_update(ctx, one.buf, one.len + 1); + git_hash_update(ctx, two.buf, two.len + 1); } break; } else if (hunk == RR_SIDE_1) diff --git a/revision.c b/revision.c index 31808e3df0..fda405bf65 100644 --- a/revision.c +++ b/revision.c @@ -2038,41 +2038,32 @@ static void prepare_show_merge(struct rev_info *revs) free(prune); } -static int dotdot_missing(const char *arg, char *dotdot, +static int dotdot_missing(const char *full_name, struct rev_info *revs, int symmetric) { if (revs->ignore_missing) return 0; - /* de-munge so we report the full argument */ - *dotdot = '.'; die(symmetric ? "Invalid symmetric difference expression %s" - : "Invalid revision range %s", arg); + : "Invalid revision range %s", full_name); } -static int handle_dotdot_1(const char *arg, char *dotdot, +static int handle_dotdot_1(const char *a_name, const char *b_name, + const char *full_name, int symmetric, struct rev_info *revs, int flags, int cant_be_filename, struct object_context *a_oc, struct object_context *b_oc) { - const char *a_name, *b_name; struct object_id a_oid, b_oid; struct object *a_obj, *b_obj; unsigned int a_flags, b_flags; - int symmetric = 0; unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM); unsigned int oc_flags = GET_OID_COMMITTISH | GET_OID_RECORD_PATH; - a_name = arg; if (!*a_name) a_name = "HEAD"; - b_name = dotdot + 2; - if (*b_name == '.') { - symmetric = 1; - b_name++; - } if (!*b_name) b_name = "HEAD"; @@ -2081,15 +2072,13 @@ static int handle_dotdot_1(const char *arg, char *dotdot, return -1; if (!cant_be_filename) { - *dotdot = '.'; - verify_non_filename(revs->prefix, arg); - *dotdot = '\0'; + verify_non_filename(revs->prefix, full_name); } a_obj = parse_object(revs->repo, &a_oid); b_obj = parse_object(revs->repo, &b_oid); if (!a_obj || !b_obj) - return dotdot_missing(arg, dotdot, revs, symmetric); + return dotdot_missing(full_name, revs, symmetric); if (!symmetric) { /* just A..B */ @@ -2103,7 +2092,7 @@ static int handle_dotdot_1(const char *arg, char *dotdot, a = lookup_commit_reference(revs->repo, &a_obj->oid); b = lookup_commit_reference(revs->repo, &b_obj->oid); if (!a || !b) - return dotdot_missing(arg, dotdot, revs, symmetric); + return dotdot_missing(full_name, revs, symmetric); if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0) { commit_list_free(exclude); @@ -2132,16 +2121,23 @@ static int handle_dotdot(const char *arg, int cant_be_filename) { struct object_context a_oc = {0}, b_oc = {0}; - char *dotdot = strstr(arg, ".."); + const char *dotdot = strstr(arg, ".."); + char *tmp; + int symmetric = 0; int ret; if (!dotdot) return -1; - *dotdot = '\0'; - ret = handle_dotdot_1(arg, dotdot, revs, flags, cant_be_filename, - &a_oc, &b_oc); - *dotdot = '.'; + tmp = xmemdupz(arg, dotdot - arg); + dotdot += 2; + if (*dotdot == '.') { + symmetric = 1; + dotdot++; + } + ret = handle_dotdot_1(tmp, dotdot, arg, symmetric, revs, flags, + cant_be_filename, &a_oc, &b_oc); + free(tmp); object_context_release(&a_oc); object_context_release(&b_oc); @@ -2151,7 +2147,10 @@ static int handle_dotdot(const char *arg, static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt) { struct object_context oc = {0}; - char *mark; + const char *mark; + char *arg_minus_at = NULL; + char *arg_minus_excl = NULL; + char *arg_minus_dash = NULL; struct object *object; struct object_id oid; int local_flags; @@ -2178,18 +2177,17 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl mark = strstr(arg, "^@"); if (mark && !mark[2]) { - *mark = 0; - if (add_parents_only(revs, arg, flags, 0)) { + arg_minus_at = xmemdupz(arg, mark - arg); + if (add_parents_only(revs, arg_minus_at, flags, 0)) { ret = 0; goto out; } - *mark = '^'; } mark = strstr(arg, "^!"); if (mark && !mark[2]) { - *mark = 0; - if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), 0)) - *mark = '^'; + arg_minus_excl = xmemdupz(arg, mark - arg); + if (add_parents_only(revs, arg_minus_excl, flags ^ (UNINTERESTING | BOTTOM), 0)) + arg = arg_minus_excl; } mark = strstr(arg, "^-"); if (mark) { @@ -2203,9 +2201,9 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl } } - *mark = 0; - if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), exclude_parent)) - *mark = '^'; + arg_minus_dash = xmemdupz(arg, mark - arg); + if (add_parents_only(revs, arg_minus_dash, flags ^ (UNINTERESTING | BOTTOM), exclude_parent)) + arg = arg_minus_dash; } local_flags = 0; @@ -2240,6 +2238,9 @@ static int handle_revision_arg_1(const char *arg_, struct rev_info *revs, int fl out: object_context_release(&oc); + free(arg_minus_at); + free(arg_minus_excl); + free(arg_minus_dash); return ret; } diff --git a/revision.h b/revision.h index 69242ecb18..584f1338b5 100644 --- a/revision.h +++ b/revision.h @@ -4,6 +4,7 @@ #include "commit.h" #include "grep.h" #include "notes.h" +#include "object-name.h" #include "oidset.h" #include "pretty.h" #include "diff.h" diff --git a/run-command.c b/run-command.c index a8a8974638..32c290ee6a 100644 --- a/run-command.c +++ b/run-command.c @@ -1,4 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE #define DISABLE_SIGN_COMPARE_WARNINGS #include "git-compat-util.h" @@ -742,8 +741,8 @@ fail_pipe: fflush(NULL); - if (cmd->close_object_store) - odb_close(the_repository->objects); + if (cmd->odb_to_close) + odb_close(cmd->odb_to_close); #ifndef GIT_WINDOWS_NATIVE { @@ -1937,11 +1936,12 @@ void run_processes_parallel(const struct run_process_parallel_opts *opts) trace2_region_leave(tr2_category, tr2_label, NULL); } -int prepare_auto_maintenance(int quiet, struct child_process *maint) +int prepare_auto_maintenance(struct repository *r, int quiet, + struct child_process *maint) { int enabled, auto_detach; - if (!repo_config_get_bool(the_repository, "maintenance.auto", &enabled) && + if (!repo_config_get_bool(r, "maintenance.auto", &enabled) && !enabled) return 0; @@ -1950,12 +1950,12 @@ int prepare_auto_maintenance(int quiet, struct child_process *maint) * honoring `gc.autoDetach`. This is somewhat weird, but required to * retain behaviour from when we used to run git-gc(1) here. */ - if (repo_config_get_bool(the_repository, "maintenance.autodetach", &auto_detach) && - repo_config_get_bool(the_repository, "gc.autodetach", &auto_detach)) + if (repo_config_get_bool(r, "maintenance.autodetach", &auto_detach) && + repo_config_get_bool(r, "gc.autodetach", &auto_detach)) auto_detach = git_env_bool("GIT_TEST_MAINT_AUTO_DETACH", true); maint->git_cmd = 1; - maint->close_object_store = 1; + maint->odb_to_close = r->objects; strvec_pushl(&maint->args, "maintenance", "run", "--auto", NULL); strvec_push(&maint->args, quiet ? "--quiet" : "--no-quiet"); strvec_push(&maint->args, auto_detach ? "--detach" : "--no-detach"); @@ -1963,10 +1963,10 @@ int prepare_auto_maintenance(int quiet, struct child_process *maint) return 1; } -int run_auto_maintenance(int quiet) +int run_auto_maintenance(struct repository *r, int quiet) { struct child_process maint = CHILD_PROCESS_INIT; - if (!prepare_auto_maintenance(quiet, &maint)) + if (!prepare_auto_maintenance(r, quiet, &maint)) return 0; return run_command(&maint); } diff --git a/run-command.h b/run-command.h index 428d76ebd6..8ca496d7bd 100644 --- a/run-command.h +++ b/run-command.h @@ -5,6 +5,8 @@ #include "strvec.h" +struct repository; + /** * The run-command API offers a versatile tool to run sub-processes with * redirected input and output as well as with a modified environment @@ -136,7 +138,7 @@ struct child_process { * want to repack because that would delete `.pack` files (and on * Windows, you cannot delete files that are still in use). */ - unsigned close_object_store:1; + struct object_database *odb_to_close; unsigned stdout_to_stderr:1; unsigned clean_on_exit:1; @@ -227,12 +229,13 @@ int run_command(struct child_process *); * process has been prepared and is ready to run, or 0 in case auto-maintenance * should be skipped. */ -int prepare_auto_maintenance(int quiet, struct child_process *maint); +int prepare_auto_maintenance(struct repository *r, int quiet, + struct child_process *maint); /* * Trigger an auto-gc */ -int run_auto_maintenance(int quiet); +int run_auto_maintenance(struct repository *r, int quiet); /** * Execute the given command, sending "in" to its stdin, and capturing its diff --git a/send-pack.c b/send-pack.c index 67d6987b1c..07ecfae4de 100644 --- a/send-pack.c +++ b/send-pack.c @@ -391,7 +391,7 @@ static int generate_push_cert(struct strbuf *req_buf, if (!update_seen) goto free_return; - if (sign_buffer(&cert, &cert, signing_key)) + if (sign_buffer(&cert, &cert, signing_key, 0)) die(_("failed to sign the push certificate")); packet_buf_write(req_buf, "push-cert%c%s", 0, cap_string); diff --git a/sequencer.c b/sequencer.c index aafd0bc959..b7d8dca47f 100644 --- a/sequencer.c +++ b/sequencer.c @@ -209,6 +209,7 @@ static GIT_PATH_FUNC(rebase_path_reschedule_failed_exec, "rebase-merge/reschedul static GIT_PATH_FUNC(rebase_path_no_reschedule_failed_exec, "rebase-merge/no-reschedule-failed-exec") static GIT_PATH_FUNC(rebase_path_drop_redundant_commits, "rebase-merge/drop_redundant_commits") static GIT_PATH_FUNC(rebase_path_keep_redundant_commits, "rebase-merge/keep_redundant_commits") +static GIT_PATH_FUNC(rebase_path_trailer, "rebase-merge/trailer") /* * A 'struct replay_ctx' represents the private state of the sequencer. @@ -420,6 +421,7 @@ void replay_opts_release(struct replay_opts *opts) if (opts->revs) release_revisions(opts->revs); free(opts->revs); + strvec_clear(&opts->trailer_args); replay_ctx_release(ctx); free(opts->ctx); } @@ -2027,12 +2029,15 @@ static int append_squash_message(struct strbuf *buf, const char *body, if (is_fixup_flag(command, flag) && !seen_squash(ctx)) { /* * We're replacing the commit message so we need to - * append the Signed-off-by: trailer if the user - * requested '--signoff'. + * append any trailers if the user requested + * '--signoff' or '--trailer'. */ if (opts->signoff) append_signoff(buf, 0, 0); + if (opts->trailer_args.nr) + amend_strbuf_with_trailers(buf, &opts->trailer_args); + if ((command == TODO_FIXUP) && (flag & TODO_REPLACE_FIXUP_MSG) && (file_exists(rebase_path_fixup_msg()) || @@ -2206,15 +2211,16 @@ static int should_edit(struct replay_opts *opts) { return opts->edit; } -static void refer_to_commit(struct replay_opts *opts, - struct strbuf *msgbuf, struct commit *commit) +static void refer_to_commit(struct repository *r, struct strbuf *msgbuf, + const struct commit *commit, + bool use_commit_reference) { - if (opts->commit_use_reference) { + if (use_commit_reference) { struct pretty_print_context ctx = { .abbrev = DEFAULT_ABBREV, .date_mode.type = DATE_SHORT, }; - repo_format_commit_message(the_repository, commit, + repo_format_commit_message(r, commit, "%h (%s, %ad)", msgbuf, &ctx); } else { strbuf_addstr(msgbuf, oid_to_hex(&commit->object.oid)); @@ -2364,38 +2370,14 @@ static int do_pick_commit(struct repository *r, */ if (command == TODO_REVERT) { - const char *orig_subject; - base = commit; base_label = msg.label; next = parent; next_label = msg.parent_label; - if (opts->commit_use_reference) { - strbuf_commented_addf(&ctx->message, comment_line_str, - "*** SAY WHY WE ARE REVERTING ON THE TITLE LINE ***"); - } else if (skip_prefix(msg.subject, "Revert \"", &orig_subject) && - /* - * We don't touch pre-existing repeated reverts, because - * theoretically these can be nested arbitrarily deeply, - * thus requiring excessive complexity to deal with. - */ - !starts_with(orig_subject, "Revert \"")) { - strbuf_addstr(&ctx->message, "Reapply \""); - strbuf_addstr(&ctx->message, orig_subject); - strbuf_addstr(&ctx->message, "\n"); - } else { - strbuf_addstr(&ctx->message, "Revert \""); - strbuf_addstr(&ctx->message, msg.subject); - strbuf_addstr(&ctx->message, "\"\n"); - } - strbuf_addstr(&ctx->message, "\nThis reverts commit "); - refer_to_commit(opts, &ctx->message, commit); - - if (commit->parents && commit->parents->next) { - strbuf_addstr(&ctx->message, ", reversing\nchanges made to "); - refer_to_commit(opts, &ctx->message, parent); - } - strbuf_addstr(&ctx->message, ".\n"); + sequencer_format_revert_message(r, msg.subject, commit, + parent, + opts->commit_use_reference, + &ctx->message); } else { const char *p; @@ -2451,6 +2433,9 @@ static int do_pick_commit(struct repository *r, if (opts->signoff && !is_fixup(command)) append_signoff(&ctx->message, 0, 0); + if (opts->trailer_args.nr && !is_fixup(command)) + amend_strbuf_with_trailers(&ctx->message, &opts->trailer_args); + if (is_rebase_i(opts) && write_author_script(msg.message) < 0) res = -1; else if (!opts->strategy || @@ -3180,6 +3165,33 @@ static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf) parse_strategy_opts(opts, buf->buf); } +static int read_trailers(struct replay_opts *opts, struct strbuf *buf) +{ + ssize_t len; + + strbuf_reset(buf); + len = strbuf_read_file(buf, rebase_path_trailer(), 0); + if (len > 0) { + char *p = buf->buf, *nl; + + trailer_config_init(); + + while ((nl = strchr(p, '\n'))) { + *nl = '\0'; + if (!*p) + return error(_("trailers file contains empty line")); + strvec_push(&opts->trailer_args, p); + p = nl + 1; + } + } else if (!len) { + return error(_("trailers file is empty")); + } else if (errno != ENOENT) { + return error(_("cannot read trailers files")); + } + + return 0; +} + static int read_populate_opts(struct replay_opts *opts) { struct replay_ctx *ctx = opts->ctx; @@ -3241,6 +3253,11 @@ static int read_populate_opts(struct replay_opts *opts) opts->keep_redundant_commits = 1; read_strategy_opts(opts, &buf); + + if (read_trailers(opts, &buf)) { + ret = -1; + goto done_rebase_i; + } strbuf_reset(&buf); if (read_oneliner(&ctx->current_fixups, @@ -3336,6 +3353,14 @@ int write_basic_state(struct replay_opts *opts, const char *head_name, write_file(rebase_path_reschedule_failed_exec(), "%s", ""); else write_file(rebase_path_no_reschedule_failed_exec(), "%s", ""); + if (opts->trailer_args.nr) { + struct strbuf buf = STRBUF_INIT; + + for (size_t i = 0; i < opts->trailer_args.nr; i++) + strbuf_addf(&buf, "%s\n", opts->trailer_args.v[i]); + write_file(rebase_path_trailer(), "%s", buf.buf); + strbuf_release(&buf); + } return 0; } @@ -5580,6 +5605,43 @@ out: return res; } +void sequencer_format_revert_message(struct repository *r, + const char *subject, + const struct commit *commit, + const struct commit *parent, + bool use_commit_reference, + struct strbuf *message) +{ + const char *orig_subject; + + if (use_commit_reference) { + strbuf_commented_addf(message, comment_line_str, + "*** SAY WHY WE ARE REVERTING ON THE TITLE LINE ***"); + } else if (skip_prefix(subject, "Revert \"", &orig_subject) && + /* + * We don't touch pre-existing repeated reverts, because + * theoretically these can be nested arbitrarily deeply, + * thus requiring excessive complexity to deal with. + */ + !starts_with(orig_subject, "Revert \"")) { + strbuf_addstr(message, "Reapply \""); + strbuf_addstr(message, orig_subject); + strbuf_addstr(message, "\n"); + } else { + strbuf_addstr(message, "Revert \""); + strbuf_addstr(message, subject); + strbuf_addstr(message, "\"\n"); + } + strbuf_addstr(message, "\nThis reverts commit "); + refer_to_commit(r, message, commit, use_commit_reference); + + if (commit->parents && commit->parents->next) { + strbuf_addstr(message, ", reversing\nchanges made to "); + refer_to_commit(r, message, parent, use_commit_reference); + } + strbuf_addstr(message, ".\n"); +} + void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag) { unsigned no_dup_sob = flag & APPEND_SIGNOFF_DEDUP; diff --git a/sequencer.h b/sequencer.h index 719684c8a9..a6fa670c7c 100644 --- a/sequencer.h +++ b/sequencer.h @@ -57,6 +57,8 @@ struct replay_opts { int ignore_date; int commit_use_reference; + struct strvec trailer_args; + int mainline; char *gpg_sign; @@ -84,6 +86,7 @@ struct replay_opts { #define REPLAY_OPTS_INIT { \ .edit = -1, \ .action = -1, \ + .trailer_args = STRVEC_INIT, \ .xopts = STRVEC_INIT, \ .ctx = replay_ctx_new(), \ } @@ -271,4 +274,17 @@ int sequencer_determine_whence(struct repository *r, enum commit_whence *whence) */ int sequencer_get_update_refs_state(const char *wt_dir, struct string_list *refs); +/* + * Format a revert commit message with appropriate 'Revert "<subject>"' or + * 'Reapply "<subject>"' prefix and 'This reverts commit <ref>.' body. + * When use_commit_reference is set, <ref> is an abbreviated hash with + * subject and date; otherwise the full hex hash is used. + */ +void sequencer_format_revert_message(struct repository *r, + const char *subject, + const struct commit *commit, + const struct commit *parent, + bool use_commit_reference, + struct strbuf *message); + #endif /* SEQUENCER_H */ diff --git a/sideband.c b/sideband.c index ea7c25211e..1ed6614eaf 100644 --- a/sideband.c +++ b/sideband.c @@ -264,6 +264,7 @@ void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_ma const char *p = data; while (sz) { + struct iovec iov[2]; unsigned n; char hdr[5]; @@ -273,12 +274,19 @@ void send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_ma if (0 <= band) { xsnprintf(hdr, sizeof(hdr), "%04x", n + 5); hdr[4] = band; - write_or_die(fd, hdr, 5); + iov[0].iov_base = hdr; + iov[0].iov_len = 5; } else { xsnprintf(hdr, sizeof(hdr), "%04x", n + 4); - write_or_die(fd, hdr, 4); + iov[0].iov_base = hdr; + iov[0].iov_len = 4; } - write_or_die(fd, p, n); + + iov[1].iov_base = (void *) p; + iov[1].iov_len = n; + + writev_or_die(fd, iov, ARRAY_SIZE(iov)); + p += n; sz -= n; } diff --git a/split-index.c b/split-index.c index 4c74c4adda..6ba210738c 100644 --- a/split-index.c +++ b/split-index.c @@ -1,4 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE #define DISABLE_SIGN_COMPARE_WARNINGS #include "git-compat-util.h" @@ -6,6 +5,7 @@ #include "hash.h" #include "mem-pool.h" #include "read-cache-ll.h" +#include "repository.h" #include "split-index.h" #include "strbuf.h" #include "ewah/ewok.h" @@ -25,16 +25,17 @@ struct split_index *init_split_index(struct index_state *istate) int read_link_extension(struct index_state *istate, const void *data_, unsigned long sz) { + const struct git_hash_algo *algo = istate->repo->hash_algo; const unsigned char *data = data_; struct split_index *si; int ret; - if (sz < the_hash_algo->rawsz) + if (sz < algo->rawsz) return error("corrupt link extension (too short)"); si = init_split_index(istate); - oidread(&si->base_oid, data, the_repository->hash_algo); - data += the_hash_algo->rawsz; - sz -= the_hash_algo->rawsz; + oidread(&si->base_oid, data, algo); + data += algo->rawsz; + sz -= algo->rawsz; if (!sz) return 0; si->delete_bitmap = ewah_new(); @@ -56,7 +57,7 @@ int write_link_extension(struct strbuf *sb, struct index_state *istate) { struct split_index *si = istate->split_index; - strbuf_add(sb, si->base_oid.hash, the_hash_algo->rawsz); + strbuf_add(sb, si->base_oid.hash, istate->repo->hash_algo->rawsz); if (!si->delete_bitmap && !si->replace_bitmap) return 0; ewah_serialize_strbuf(si->delete_bitmap, sb); @@ -168,7 +168,7 @@ int strbuf_reencode(struct strbuf *sb, const char *from, const char *to) if (!out) return -1; - strbuf_attach(sb, out, len, len); + strbuf_attach(sb, out, len, len + 1); return 0; } diff --git a/string-list.c b/string-list.c index fffa2ad4b6..d260b873c8 100644 --- a/string-list.c +++ b/string-list.c @@ -281,6 +281,15 @@ void unsorted_string_list_delete_item(struct string_list *list, int i, int free_ list->nr--; } +void unsorted_string_list_remove(struct string_list *list, const char *str, + int free_util) +{ + struct string_list_item *item = unsorted_string_list_lookup(list, str); + if (item) + unsorted_string_list_delete_item(list, item - list->items, + free_util); +} + /* * append a substring [p..end] to list; return number of things it * appended to the list. diff --git a/string-list.h b/string-list.h index 3ad862a187..b86ee7c099 100644 --- a/string-list.h +++ b/string-list.h @@ -266,6 +266,14 @@ struct string_list_item *unsorted_string_list_lookup(struct string_list *list, void unsorted_string_list_delete_item(struct string_list *list, int i, int free_util); /** + * Remove the first item matching `str` from an unsorted string_list. + * No-op if `str` is not found. If `free_util` is non-zero, the `util` + * pointer of the removed item is freed before deletion. + */ +void unsorted_string_list_remove(struct string_list *list, const char *str, + int free_util); + +/** * Split string into substrings on characters in `delim` and append the * substrings to `list`. The input string is not modified. * list->strdup_strings must be set, as new memory needs to be diff --git a/submodule-config.c b/submodule-config.c index 1f19fe2077..72a46b7a54 100644 --- a/submodule-config.c +++ b/submodule-config.c @@ -14,6 +14,7 @@ #include "strbuf.h" #include "object-name.h" #include "odb.h" +#include "odb/source.h" #include "parse-options.h" #include "thread-utils.h" #include "tree-walk.h" diff --git a/submodule.c b/submodule.c index cd879a5cfe..b1a0363f9d 100644 --- a/submodule.c +++ b/submodule.c @@ -1708,6 +1708,8 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err, if (spf->oid_fetch_tasks_nr) { struct fetch_task *task = spf->oid_fetch_tasks[spf->oid_fetch_tasks_nr - 1]; + struct child_process cp_remote = CHILD_PROCESS_INIT; + struct strbuf remote_name = STRBUF_INIT; spf->oid_fetch_tasks_nr--; child_process_init(cp); @@ -1721,8 +1723,19 @@ static int get_next_submodule(struct child_process *cp, struct strbuf *err, strvec_pushf(&cp->args, "--submodule-prefix=%s%s/", spf->prefix, task->sub->path); - /* NEEDSWORK: have get_default_remote from submodule--helper */ - strvec_push(&cp->args, "origin"); + cp_remote.git_cmd = 1; + strvec_pushl(&cp_remote.args, "submodule--helper", + "get-default-remote", task->sub->path, NULL); + + if (!capture_command(&cp_remote, &remote_name, 0)) { + strbuf_trim_trailing_newline(&remote_name); + strvec_push(&cp->args, remote_name.buf); + } else { + /* Fallback to "origin" if the helper fails */ + strvec_push(&cp->args, "origin"); + } + strbuf_release(&remote_name); + oid_array_for_each_unique(task->commits, append_oid_to_argv, &cp->args); @@ -1815,7 +1828,6 @@ int fetch_submodules(struct repository *r, int default_option, int quiet, int max_parallel_jobs) { - int i; struct submodule_parallel_fetch spf = SPF_INIT; const struct run_process_parallel_opts opts = { .tr2_category = "submodule", @@ -1842,8 +1854,7 @@ int fetch_submodules(struct repository *r, die(_("index file corrupt")); strvec_push(&spf.args, "fetch"); - for (i = 0; i < options->nr; i++) - strvec_push(&spf.args, options->v[i]); + strvec_pushv(&spf.args, options->v); strvec_push(&spf.args, "--recurse-submodules-default"); /* default value, "--submodule-prefix" and its value are added later */ diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl index 6ee7700eb4..18d944b810 100755 --- a/t/check-non-portable-shell.pl +++ b/t/check-non-portable-shell.pl @@ -36,7 +36,7 @@ while (<>) { $_ = $line; /\bcp\s+-a/ and err 'cp -a is not portable'; - /\bsed\s+-[^efn]\s+/ and err 'sed option not portable (use only -n, -e, -f)'; + /\bsed\s+-[^Eefn]\s+/ and err 'sed option not portable (use only -E, -n, -e, -f)'; /\becho\s+-[neE]/ and err 'echo with option is not portable (use printf)'; /^\s*declare\s+/ and err 'arrays/declare not portable'; /^\s*[^#]\s*which\s/ and err 'which is not portable (use type)'; diff --git a/t/helper/test-example-tap.c b/t/helper/test-example-tap.c index 229d495ecf..998a1f0b42 100644 --- a/t/helper/test-example-tap.c +++ b/t/helper/test-example-tap.c @@ -63,6 +63,8 @@ static void t_messages(void) check_str("NULL", NULL); check_char('a', ==, '\n'); check_char('\\', ==, '\''); + check_char('\a', ==, '\v'); + check_char('\x00', ==, '\x01'); } static void t_empty(void) @@ -123,6 +125,8 @@ int cmd__example_tap(int argc UNUSED, const char **argv UNUSED) check_str("NULL", NULL); check_char('a', ==, '\n'); check_char('\\', ==, '\''); + check_char('\a', ==, '\v'); + check_char('\x00', ==, '\x01'); } if_test ("if_test test with no checks") ; /* nothing */ diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c index 6de5d1665a..388d29e2b5 100644 --- a/t/helper/test-read-midx.c +++ b/t/helper/test-read-midx.c @@ -26,18 +26,22 @@ static int read_midx_file(const char *object_dir, const char *checksum, int show_objects) { uint32_t i; - struct multi_pack_index *m; + struct multi_pack_index *m, *tip; + int ret = 0; - m = setup_midx(object_dir); + m = tip = setup_midx(object_dir); if (!m) return 1; if (checksum) { - while (m && strcmp(hash_to_hex(get_midx_checksum(m)), checksum)) + while (m && strcmp(midx_get_checksum_hex(m), checksum)) m = m->base_midx; - if (!m) - return 1; + if (!m) { + ret = error(_("could not find MIDX with checksum %s"), + checksum); + goto out; + } } printf("header: %08x %d %d %d %d\n", @@ -82,9 +86,10 @@ static int read_midx_file(const char *object_dir, const char *checksum, } } - close_midx(m); +out: + close_midx(tip); - return 0; + return ret; } static int read_midx_checksum(const char *object_dir) @@ -94,7 +99,7 @@ static int read_midx_checksum(const char *object_dir) m = setup_midx(object_dir); if (!m) return 1; - printf("%s\n", hash_to_hex(get_midx_checksum(m))); + printf("%s\n", midx_get_checksum_hex(m)); close_midx(m); return 0; diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index 5f42c311c2..4c76e813e3 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -167,6 +167,7 @@ prepare_httpd() { install_script error.sh install_script apply-one-time-script.sh install_script nph-custom-auth.sh + install_script http-429.sh ln -s "$LIB_HTTPD_MODULE_PATH" "$HTTPD_ROOT_PATH/modules" diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index 6b8c50a51a..40a690b0bb 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -139,6 +139,10 @@ SetEnv PERL_PATH ${PERL_PATH} SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH} SetEnv GIT_HTTP_EXPORT_ALL </LocationMatch> +<LocationMatch /http_429/> + SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH} + SetEnv GIT_HTTP_EXPORT_ALL +</LocationMatch> <LocationMatch /smart_v0/> SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH} SetEnv GIT_HTTP_EXPORT_ALL @@ -160,6 +164,7 @@ ScriptAlias /broken_smart/ broken-smart-http.sh/ ScriptAlias /error_smart/ error-smart-http.sh/ ScriptAlias /error/ error.sh/ ScriptAliasMatch /one_time_script/(.*) apply-one-time-script.sh/$1 +ScriptAliasMatch /http_429/(.*) http-429.sh/$1 ScriptAliasMatch /custom_auth/(.*) nph-custom-auth.sh/$1 <Directory ${GIT_EXEC_PATH}> Options FollowSymlinks @@ -185,6 +190,9 @@ ScriptAliasMatch /custom_auth/(.*) nph-custom-auth.sh/$1 <Files apply-one-time-script.sh> Options ExecCGI </Files> +<Files http-429.sh> + Options ExecCGI +</Files> <Files ${GIT_EXEC_PATH}/git-http-backend> Options ExecCGI </Files> diff --git a/t/lib-httpd/http-429.sh b/t/lib-httpd/http-429.sh new file mode 100644 index 0000000000..c97b16145b --- /dev/null +++ b/t/lib-httpd/http-429.sh @@ -0,0 +1,98 @@ +#!/bin/sh + +# Script to return HTTP 429 Too Many Requests responses for testing retry logic. +# Usage: /http_429/<test-context>/<retry-after-value>/<repo-path> +# +# The test-context is a unique identifier for each test to isolate state files. +# The retry-after-value can be: +# - A number (e.g., "1", "2", "100") - sets Retry-After header to that many seconds +# - "none" - no Retry-After header +# - "invalid" - invalid Retry-After format +# - "permanent" - always return 429 (never succeed) +# - An HTTP-date string (RFC 2822 format) - sets Retry-After to that date +# +# On first call, returns 429. On subsequent calls (after retry), forwards to git-http-backend +# unless retry-after-value is "permanent". + +# Extract test context, retry-after value and repo path from PATH_INFO +# PATH_INFO format: /<test-context>/<retry-after-value>/<repo-path> +path_info="${PATH_INFO#/}" # Remove leading slash +test_context="${path_info%%/*}" # Get first component (test context) +remaining="${path_info#*/}" # Get rest +retry_after="${remaining%%/*}" # Get second component (retry-after value) +repo_path="${remaining#*/}" # Get rest (repo path) + +# Extract repository name from repo_path (e.g., "repo.git" from "repo.git/info/refs") +# The repo name is the first component before any "/" +repo_name="${repo_path%%/*}" + +# Use current directory (HTTPD_ROOT_PATH) for state file +# Create a safe filename from test_context, retry_after and repo_name +# This ensures all requests for the same test context share the same state file +safe_name=$(echo "${test_context}-${retry_after}-${repo_name}" | tr '/' '_' | tr -cd 'a-zA-Z0-9_-') +state_file="http-429-state-${safe_name}" + +# Check if this is the first call (no state file exists) +if test -f "$state_file" +then + # Already returned 429 once, forward to git-http-backend + # Set PATH_INFO to just the repo path (without retry-after value) + # Set GIT_PROJECT_ROOT so git-http-backend can find the repository + # Use exec to replace this process so git-http-backend gets the updated environment + PATH_INFO="/$repo_path" + export PATH_INFO + # GIT_PROJECT_ROOT points to the document root where repositories are stored + # The script runs from HTTPD_ROOT_PATH, and www/ is the document root + if test -z "$GIT_PROJECT_ROOT" + then + # Construct path: current directory (HTTPD_ROOT_PATH) + /www + GIT_PROJECT_ROOT="$(pwd)/www" + export GIT_PROJECT_ROOT + fi + exec "$GIT_EXEC_PATH/git-http-backend" +fi + +# Mark that we've returned 429 +touch "$state_file" + +# Output HTTP 429 response +printf "Status: 429 Too Many Requests\r\n" + +# Set Retry-After header based on retry_after value +case "$retry_after" in + none) + # No Retry-After header + ;; + invalid) + printf "Retry-After: invalid-format-123abc\r\n" + ;; + permanent) + # Always return 429, don't set state file for success + rm -f "$state_file" + printf "Retry-After: 1\r\n" + printf "Content-Type: text/plain\r\n" + printf "\r\n" + printf "Permanently rate limited\n" + exit 0 + ;; + *) + # Check if it's a number + case "$retry_after" in + [0-9]*) + # Numeric value + printf "Retry-After: %s\r\n" "$retry_after" + ;; + *) + # Assume it's an HTTP-date format (passed as-is, URL decoded) + # Apache may URL-encode the path, so decode common URL-encoded characters + # %20 = space, %2C = comma, %3A = colon + retry_value=$(echo "$retry_after" | sed -e 's/%20/ /g' -e 's/%2C/,/g' -e 's/%3A/:/g') + printf "Retry-After: %s\r\n" "$retry_value" + ;; + esac + ;; +esac + +printf "Content-Type: text/plain\r\n" +printf "\r\n" +printf "Rate limited\n" diff --git a/t/meson.build b/t/meson.build index 9b2fa4dee8..7528e5cda5 100644 --- a/t/meson.build +++ b/t/meson.build @@ -393,8 +393,10 @@ integration_tests = [ 't3436-rebase-more-options.sh', 't3437-rebase-fixup-options.sh', 't3438-rebase-broken-files.sh', + 't3440-rebase-trailer.sh', 't3450-history.sh', 't3451-history-reword.sh', + 't3452-history-split.sh', 't3500-cherry.sh', 't3501-revert-cherry-pick.sh', 't3502-cherry-pick-merge.sh', @@ -507,6 +509,7 @@ integration_tests = [ 't4071-diff-minimal.sh', 't4072-diff-max-depth.sh', 't4073-diff-stat-name-width.sh', + 't4074-diff-shifted-matched-group.sh', 't4100-apply-stat.sh', 't4101-apply-nonl.sh', 't4102-apply-rename.sh', @@ -623,6 +626,7 @@ integration_tests = [ 't5332-multi-pack-reuse.sh', 't5333-pseudo-merge-bitmaps.sh', 't5334-incremental-multi-pack-index.sh', + 't5335-compact-multi-pack-index.sh', 't5351-unpack-large-objects.sh', 't5400-send-pack.sh', 't5401-update-hooks.sh', @@ -709,6 +713,7 @@ integration_tests = [ 't5581-http-curl-verbose.sh', 't5582-fetch-negative-refspec.sh', 't5583-push-branches.sh', + 't5584-http-429-retry.sh', 't5600-clone-fail-cleanup.sh', 't5601-clone.sh', 't5602-clone-remote-exec.sh', @@ -897,6 +902,7 @@ integration_tests = [ 't7423-submodule-symlinks.sh', 't7424-submodule-mixed-ref-formats.sh', 't7425-submodule-gitdir-path-extension.sh', + 't7426-submodule-get-default-remote.sh', 't7450-bad-git-dotfiles.sh', 't7500-commit-template-squash-signoff.sh', 't7501-commit-basic-functionality.sh', diff --git a/t/pack-refs-tests.sh b/t/pack-refs-tests.sh index 2fdaccb6c7..d76b087b09 100644 --- a/t/pack-refs-tests.sh +++ b/t/pack-refs-tests.sh @@ -61,13 +61,13 @@ test_expect_success 'see if a branch still exists after git ${pack_refs} --prune test_expect_success 'see if git ${pack_refs} --prune remove ref files' ' git branch f && git ${pack_refs} --all --prune && - ! test -f .git/refs/heads/f + test_path_is_missing .git/refs/heads/f ' test_expect_success 'see if git ${pack_refs} --prune removes empty dirs' ' git branch r/s/t && git ${pack_refs} --all --prune && - ! test -e .git/refs/heads/r + test_path_is_missing .git/refs/heads/r ' test_expect_success 'git branch g should work when git branch g/h has been deleted' ' @@ -111,43 +111,43 @@ test_expect_success 'test excluded refs are not packed' ' git branch dont_pack2 && git branch pack_this && git ${pack_refs} --all --exclude "refs/heads/dont_pack*" && - test -f .git/refs/heads/dont_pack1 && - test -f .git/refs/heads/dont_pack2 && - ! test -f .git/refs/heads/pack_this' + test_path_is_file .git/refs/heads/dont_pack1 && + test_path_is_file .git/refs/heads/dont_pack2 && + test_path_is_missing .git/refs/heads/pack_this' test_expect_success 'test --no-exclude refs clears excluded refs' ' git branch dont_pack3 && git branch dont_pack4 && git ${pack_refs} --all --exclude "refs/heads/dont_pack*" --no-exclude && - ! test -f .git/refs/heads/dont_pack3 && - ! test -f .git/refs/heads/dont_pack4' + test_path_is_missing .git/refs/heads/dont_pack3 && + test_path_is_missing .git/refs/heads/dont_pack4' test_expect_success 'test only included refs are packed' ' git branch pack_this1 && git branch pack_this2 && git tag dont_pack5 && git ${pack_refs} --include "refs/heads/pack_this*" && - test -f .git/refs/tags/dont_pack5 && - ! test -f .git/refs/heads/pack_this1 && - ! test -f .git/refs/heads/pack_this2' + test_path_is_file .git/refs/tags/dont_pack5 && + test_path_is_missing .git/refs/heads/pack_this1 && + test_path_is_missing .git/refs/heads/pack_this2' test_expect_success 'test --no-include refs clears included refs' ' git branch pack1 && git branch pack2 && git ${pack_refs} --include "refs/heads/pack*" --no-include && - test -f .git/refs/heads/pack1 && - test -f .git/refs/heads/pack2' + test_path_is_file .git/refs/heads/pack1 && + test_path_is_file .git/refs/heads/pack2' test_expect_success 'test --exclude takes precedence over --include' ' git branch dont_pack5 && git ${pack_refs} --include "refs/heads/pack*" --exclude "refs/heads/pack*" && - test -f .git/refs/heads/dont_pack5' + test_path_is_file .git/refs/heads/dont_pack5' test_expect_success 'see if up-to-date packed refs are preserved' ' git branch q && git ${pack_refs} --all --prune && git update-ref refs/heads/q refs/heads/q && - ! test -f .git/refs/heads/q + test_path_is_missing .git/refs/heads/q ' test_expect_success 'pack, prune and repack' ' diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh index db8bde280e..e716b5cdfa 100755 --- a/t/t0008-ignores.sh +++ b/t/t0008-ignores.sh @@ -946,7 +946,7 @@ test_expect_success SYMLINKS 'symlinks respected in info/exclude' ' ' test_expect_success SYMLINKS 'symlinks not respected in-tree' ' - test_when_finished "rm .gitignore" && + test_when_finished "rm -rf subdir .gitignore err actual" && ln -s ignore .gitignore && mkdir subdir && ln -s ignore subdir/.gitignore && @@ -957,6 +957,7 @@ test_expect_success SYMLINKS 'symlinks not respected in-tree' ' test_expect_success EXPENSIVE 'large exclude file ignored in tree' ' test_when_finished "rm .gitignore" && + find . -name .gitignore -exec rm "{}" ";" && dd if=/dev/zero of=.gitignore bs=101M count=1 && git ls-files -o --exclude-standard 2>err && echo "warning: ignoring excessively large pattern file: .gitignore" >expect && diff --git a/t/t0012-help.sh b/t/t0012-help.sh index d3a0967e9d..c33501bdcd 100755 --- a/t/t0012-help.sh +++ b/t/t0012-help.sh @@ -141,20 +141,23 @@ test_expect_success 'git help -c' ' '\''git help config'\'' for more information EOF - grep -v -E \ - -e "^[^.]+\.[^.]+$" \ - -e "^[^.]+\.[^.]+\.[^.]+$" \ - help.output >actual && + sed -E -e " + /^[^.]+\.[^.]+$/d + /^[^.]+\.[^.]+\.[^.]+$/d + " help.output >actual && test_cmp expect actual ' test_expect_success 'git help --config-for-completion' ' git help -c >human && - grep -E \ - -e "^[^.]+\.[^.]+$" \ - -e "^[^.]+\.[^.]+\.[^.]+$" human | - sed -e "s/\*.*//" -e "s/<.*//" | - sort -u >human.munged && + sed -E -e " + /^[^.]+\.[^.]+$/b out + /^[^.]+\.[^.]+\.[^.]+$/b out + d + : out + s/\*.*// + s/<.*// + " human | sort -u >human.munged && git help --config-for-completion >vars && test_cmp human.munged vars @@ -162,14 +165,16 @@ test_expect_success 'git help --config-for-completion' ' test_expect_success 'git help --config-sections-for-completion' ' git help -c >human && - grep -E \ - -e "^[^.]+\.[^.]+$" \ - -e "^[^.]+\.[^.]+\.[^.]+$" human | - sed -e "s/\..*//" | - sort -u >human.munged && + sed -E -e " + /^[^.]+\.[^.]+$/b out + /^[^.]+\.[^.]+\.[^.]+$/b out + d + : out + s/\..*// + " human | sort -u >expect && - git help --config-sections-for-completion >sections && - test_cmp human.munged sections + git help --config-sections-for-completion >actual && + test_cmp expect actual ' test_section_spacing () { diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh index 2f77fde0d9..60cfe65979 100755 --- a/t/t0061-run-command.sh +++ b/t/t0061-run-command.sh @@ -287,16 +287,8 @@ test_expect_success MINGW 'can spawn .bat with argv[0] containing spaces' ' rm -f out && echo "echo %* >>out" >"$bat" && - # Ask git to invoke .bat; clone will fail due to fake SSH helper - test_must_fail env GIT_SSH="$bat" git clone myhost:src ssh-clone && - - # Spawning .bat can fail if there are two quoted cmd.exe arguments. - # .bat itself is first (due to spaces in name), so just one more is - # needed to verify. GIT_SSH will invoke .bat multiple times: - # 1) -G myhost - # 2) myhost "git-upload-pack src" - # First invocation will always succeed. Test the second one. - grep "git-upload-pack" out + test-tool run-command run-command "$bat" "arg with spaces" && + test_grep "arg with spaces" out ' test_done diff --git a/t/t0080-unit-test-output.sh b/t/t0080-unit-test-output.sh index 3db10f095c..66838a00b2 100755 --- a/t/t0080-unit-test-output.sh +++ b/t/t0080-unit-test-output.sh @@ -6,10 +6,10 @@ test_description='Test the output of the unit test framework' test_expect_success 'TAP output from unit tests' - <<\EOT cat >expect <<-EOF && - # BUG: check outside of test at t/helper/test-example-tap.c:75 + # BUG: check outside of test at t/helper/test-example-tap.c:77 ok 1 - passing test ok 2 - passing test and assertion return 1 - # check "1 == 2" failed at t/helper/test-example-tap.c:79 + # check "1 == 2" failed at t/helper/test-example-tap.c:81 # left: 1 # right: 2 not ok 3 - failing test @@ -34,53 +34,65 @@ test_expect_success 'TAP output from unit tests' - <<\EOT not ok 15 - failing check after TEST_TODO() ok 16 - failing check after TEST_TODO() returns 0 # check "!strcmp("\thello\\\\", "there\"\n")" failed at t/helper/test-example-tap.c:62 - # left: "\011hello\\\\" - # right: "there\"\012" + # left: "\thello\\\\" + # right: "there\"\n" # check "!strcmp("NULL", NULL)" failed at t/helper/test-example-tap.c:63 # left: "NULL" # right: NULL # check "'a' == '\n'" failed at t/helper/test-example-tap.c:64 # left: 'a' - # right: '\012' + # right: '\n' # check "'\\\\' == '\\''" failed at t/helper/test-example-tap.c:65 # left: '\\\\' # right: '\\'' + # check "'\a' == '\v'" failed at t/helper/test-example-tap.c:66 + # left: '\a' + # right: '\v' + # check "'\x00' == '\x01'" failed at t/helper/test-example-tap.c:67 + # left: '\000' + # right: '\001' not ok 17 - messages from failing string and char comparison - # BUG: test has no checks at t/helper/test-example-tap.c:94 + # BUG: test has no checks at t/helper/test-example-tap.c:96 not ok 18 - test with no checks ok 19 - test with no checks returns 0 ok 20 - if_test passing test - # check "1 == 2" failed at t/helper/test-example-tap.c:100 + # check "1 == 2" failed at t/helper/test-example-tap.c:102 # left: 1 # right: 2 not ok 21 - if_test failing test not ok 22 - if_test passing TEST_TODO() # TODO - # todo check 'check(1)' succeeded at t/helper/test-example-tap.c:104 + # todo check 'check(1)' succeeded at t/helper/test-example-tap.c:106 not ok 23 - if_test failing TEST_TODO() - # check "0" failed at t/helper/test-example-tap.c:106 + # check "0" failed at t/helper/test-example-tap.c:108 # skipping test - missing prerequisite - # skipping check '1' at t/helper/test-example-tap.c:108 + # skipping check '1' at t/helper/test-example-tap.c:110 ok 24 - if_test test_skip() # SKIP # skipping test - missing prerequisite ok 25 - if_test test_skip() inside TEST_TODO() # SKIP - # check "0" failed at t/helper/test-example-tap.c:113 + # check "0" failed at t/helper/test-example-tap.c:115 not ok 26 - if_test TEST_TODO() after failing check - # check "0" failed at t/helper/test-example-tap.c:119 + # check "0" failed at t/helper/test-example-tap.c:121 not ok 27 - if_test failing check after TEST_TODO() - # check "!strcmp("\thello\\\\", "there\"\n")" failed at t/helper/test-example-tap.c:122 - # left: "\011hello\\\\" - # right: "there\"\012" - # check "!strcmp("NULL", NULL)" failed at t/helper/test-example-tap.c:123 + # check "!strcmp("\thello\\\\", "there\"\n")" failed at t/helper/test-example-tap.c:124 + # left: "\thello\\\\" + # right: "there\"\n" + # check "!strcmp("NULL", NULL)" failed at t/helper/test-example-tap.c:125 # left: "NULL" # right: NULL - # check "'a' == '\n'" failed at t/helper/test-example-tap.c:124 + # check "'a' == '\n'" failed at t/helper/test-example-tap.c:126 # left: 'a' - # right: '\012' - # check "'\\\\' == '\\''" failed at t/helper/test-example-tap.c:125 + # right: '\n' + # check "'\\\\' == '\\''" failed at t/helper/test-example-tap.c:127 # left: '\\\\' # right: '\\'' + # check "'\a' == '\v'" failed at t/helper/test-example-tap.c:128 + # left: '\a' + # right: '\v' + # check "'\x00' == '\x01'" failed at t/helper/test-example-tap.c:129 + # left: '\000' + # right: '\001' not ok 28 - if_test messages from failing string and char comparison - # BUG: test has no checks at t/helper/test-example-tap.c:127 + # BUG: test has no checks at t/helper/test-example-tap.c:131 not ok 29 - if_test test with no checks 1..29 EOF diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh index 07aa834d33..64ead1571a 100755 --- a/t/t0300-credentials.sh +++ b/t/t0300-credentials.sh @@ -675,7 +675,9 @@ test_expect_success 'match percent-encoded values' ' test_expect_success 'match percent-encoded UTF-8 values in path' ' test_config credential.https://example.com.useHttpPath true && test_config credential.https://example.com/perú.git.helper "$HELPER" && - check fill <<-\EOF + # NOTE: do not quote this heredoc, Dash 0.5.13 has a bug with heredocs + # that contain multibyte chars. + check fill <<-EOF url=https://example.com/per%C3%BA.git -- protocol=https diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh index 2a5bdbeeb8..52e19728a3 100755 --- a/t/t0410-partial-clone.sh +++ b/t/t0410-partial-clone.sh @@ -11,7 +11,10 @@ test_description='partial clone' GIT_TEST_COMMIT_GRAPH=0 delete_object () { - rm $1/.git/objects/$(echo $2 | sed -e 's|^..|&/|') + local repo="$1" + local obj="$2" + local path="$repo/.git/objects/$(test_oid_to_path "$obj")" && + rm "$path" } pack_as_from_promisor () { diff --git a/t/t0450/adoc-help-mismatches b/t/t0450/adoc-help-mismatches index 8ee2d3f7c8..e8d6c13ccd 100644 --- a/t/t0450/adoc-help-mismatches +++ b/t/t0450/adoc-help-mismatches @@ -33,7 +33,6 @@ merge merge-file merge-index merge-one-file -multi-pack-index name-rev notes push diff --git a/t/t1416-ref-transaction-hooks.sh b/t/t1416-ref-transaction-hooks.sh index d91dd3a3b5..4fe9d9b234 100755 --- a/t/t1416-ref-transaction-hooks.sh +++ b/t/t1416-ref-transaction-hooks.sh @@ -20,6 +20,7 @@ test_expect_success 'hook allows updating ref if successful' ' echo "$*" >>actual EOF cat >expect <<-EOF && + preparing prepared committed EOF @@ -27,6 +28,18 @@ test_expect_success 'hook allows updating ref if successful' ' test_cmp expect actual ' +test_expect_success 'hook aborts updating ref in preparing state' ' + git reset --hard PRE && + test_hook reference-transaction <<-\EOF && + if test "$1" = preparing + then + exit 1 + fi + EOF + test_must_fail git update-ref HEAD POST 2>err && + test_grep "in '\''preparing'\'' phase, update aborted by the reference-transaction hook" err +' + test_expect_success 'hook aborts updating ref in prepared state' ' git reset --hard PRE && test_hook reference-transaction <<-\EOF && @@ -36,7 +49,7 @@ test_expect_success 'hook aborts updating ref in prepared state' ' fi EOF test_must_fail git update-ref HEAD POST 2>err && - test_grep "ref updates aborted by hook" err + test_grep "in '\''prepared'\'' phase, update aborted by the reference-transaction hook" err ' test_expect_success 'hook gets all queued updates in prepared state' ' @@ -121,6 +134,7 @@ test_expect_success 'interleaving hook calls succeed' ' cat >expect <<-EOF && hooks/update refs/tags/PRE $ZERO_OID $PRE_OID hooks/update refs/tags/POST $ZERO_OID $POST_OID + hooks/reference-transaction preparing hooks/reference-transaction prepared hooks/reference-transaction committed EOF @@ -143,6 +157,8 @@ test_expect_success 'hook captures git-symbolic-ref updates' ' git symbolic-ref refs/heads/symref refs/heads/main && cat >expect <<-EOF && + preparing + $ZERO_OID ref:refs/heads/main refs/heads/symref prepared $ZERO_OID ref:refs/heads/main refs/heads/symref committed @@ -171,14 +187,20 @@ test_expect_success 'hook gets all queued symref updates' ' # In the files backend, "delete" also triggers an additional transaction # update on the packed-refs backend, which constitutes additional reflog # entries. + cat >expect <<-EOF && + preparing + ref:refs/heads/main $ZERO_OID refs/heads/symref + ref:refs/heads/main $ZERO_OID refs/heads/symrefd + $ZERO_OID ref:refs/heads/main refs/heads/symrefc + ref:refs/heads/main ref:refs/heads/branch refs/heads/symrefu + EOF + if test_have_prereq REFFILES then - cat >expect <<-EOF + cat >>expect <<-EOF aborted $ZERO_OID $ZERO_OID refs/heads/symrefd EOF - else - >expect fi && cat >>expect <<-EOF && diff --git a/t/t1800-hook.sh b/t/t1800-hook.sh index b1583e9ef9..96749fc06d 100755 --- a/t/t1800-hook.sh +++ b/t/t1800-hook.sh @@ -25,18 +25,47 @@ test_expect_success 'git hook usage' ' test_expect_code 129 git hook && test_expect_code 129 git hook run && test_expect_code 129 git hook run -h && - test_expect_code 129 git hook list -h && test_expect_code 129 git hook run --unknown 2>err && test_expect_code 129 git hook list && test_expect_code 129 git hook list -h && grep "unknown option" err ' +test_expect_success 'git hook list: unknown hook name is rejected' ' + test_must_fail git hook list prereceive 2>err && + test_grep "unknown hook event" err +' + +test_expect_success 'git hook run: unknown hook name is rejected' ' + test_must_fail git hook run prereceive 2>err && + test_grep "unknown hook event" err +' + +test_expect_success 'git hook list: known hook name is accepted' ' + test_must_fail git hook list pre-receive 2>err && + test_grep ! "unknown hook event" err +' + +test_expect_success 'git hook run: known hook name is accepted' ' + git hook run --ignore-missing pre-receive 2>err && + test_grep ! "unknown hook event" err +' + +test_expect_success 'git hook run: --allow-unknown-hook-name overrides rejection' ' + git hook run --allow-unknown-hook-name --ignore-missing custom-hook 2>err && + test_grep ! "unknown hook event" err +' + +test_expect_success 'git hook list: --allow-unknown-hook-name overrides rejection' ' + test_must_fail git hook list --allow-unknown-hook-name custom-hook 2>err && + test_grep ! "unknown hook event" err +' + test_expect_success 'git hook list: nonexistent hook' ' cat >stderr.expect <<-\EOF && - warning: No hooks found for event '\''test-hook'\'' + warning: no hooks found for event '\''test-hook'\'' EOF - test_expect_code 1 git hook list test-hook 2>stderr.actual && + test_expect_code 1 git hook list --allow-unknown-hook-name test-hook 2>stderr.actual && test_cmp stderr.expect stderr.actual ' @@ -48,7 +77,7 @@ test_expect_success 'git hook list: traditional hook from hookdir' ' cat >expect <<-\EOF && hook from hookdir EOF - git hook list test-hook >actual && + git hook list --allow-unknown-hook-name test-hook >actual && test_cmp expect actual ' @@ -57,7 +86,7 @@ test_expect_success 'git hook list: configured hook' ' test_config hook.myhook.event test-hook --add && echo "myhook" >expect && - git hook list test-hook >actual && + git hook list --allow-unknown-hook-name test-hook >actual && test_cmp expect actual ' @@ -69,7 +98,7 @@ test_expect_success 'git hook list: -z shows NUL-terminated output' ' test_config hook.myhook.event test-hook --add && printf "myhookQhook from hookdirQ" >expect && - git hook list -z test-hook >actual.raw && + git hook list --allow-unknown-hook-name -z test-hook >actual.raw && nul_to_q <actual.raw >actual && test_cmp expect actual ' @@ -78,12 +107,12 @@ test_expect_success 'git hook run: nonexistent hook' ' cat >stderr.expect <<-\EOF && error: cannot find a hook named test-hook EOF - test_expect_code 1 git hook run test-hook 2>stderr.actual && + test_expect_code 1 git hook run --allow-unknown-hook-name test-hook 2>stderr.actual && test_cmp stderr.expect stderr.actual ' test_expect_success 'git hook run: nonexistent hook with --ignore-missing' ' - git hook run --ignore-missing does-not-exist 2>stderr.actual && + git hook run --allow-unknown-hook-name --ignore-missing does-not-exist 2>stderr.actual && test_must_be_empty stderr.actual ' @@ -95,7 +124,7 @@ test_expect_success 'git hook run: basic' ' cat >expect <<-\EOF && Test hook EOF - git hook run test-hook 2>actual && + git hook run --allow-unknown-hook-name test-hook 2>actual && test_cmp expect actual ' @@ -109,7 +138,7 @@ test_expect_success 'git hook run: stdout and stderr both write to our stderr' ' Will end up on stderr Will end up on stderr EOF - git hook run test-hook >stdout.actual 2>stderr.actual && + git hook run --allow-unknown-hook-name test-hook >stdout.actual 2>stderr.actual && test_cmp stderr.expect stderr.actual && test_must_be_empty stdout.actual ' @@ -121,12 +150,12 @@ do exit $code EOF - test_expect_code $code git hook run test-hook + test_expect_code $code git hook run --allow-unknown-hook-name test-hook ' done test_expect_success 'git hook run arg u ments without -- is not allowed' ' - test_expect_code 129 git hook run test-hook arg u ments + test_expect_code 129 git hook run --allow-unknown-hook-name test-hook arg u ments ' test_expect_success 'git hook run -- pass arguments' ' @@ -140,7 +169,7 @@ test_expect_success 'git hook run -- pass arguments' ' u ments EOF - git hook run test-hook -- arg "u ments" 2>actual && + git hook run --allow-unknown-hook-name test-hook -- arg "u ments" 2>actual && test_cmp expect actual ' @@ -149,12 +178,12 @@ test_expect_success 'git hook run: out-of-repo runs execute global hooks' ' test_config_global hook.global-hook.command "echo no repo no problems" --add && echo "global-hook" >expect && - nongit git hook list test-hook >actual && + nongit git hook list --allow-unknown-hook-name test-hook >actual && test_cmp expect actual && echo "no repo no problems" >expect && - nongit git hook run test-hook 2>actual && + nongit git hook run --allow-unknown-hook-name test-hook 2>actual && test_cmp expect actual ' @@ -179,11 +208,11 @@ test_expect_success 'git -c core.hooksPath=<PATH> hook run' ' # Test various ways of specifying the path. See also # t1350-config-hooks-path.sh >actual && - git hook run test-hook -- ignored 2>>actual && - git -c core.hooksPath=my-hooks hook run test-hook -- one 2>>actual && - git -c core.hooksPath=my-hooks/ hook run test-hook -- two 2>>actual && - git -c core.hooksPath="$PWD/my-hooks" hook run test-hook -- three 2>>actual && - git -c core.hooksPath="$PWD/my-hooks/" hook run test-hook -- four 2>>actual && + git hook run --allow-unknown-hook-name test-hook -- ignored 2>>actual && + git -c core.hooksPath=my-hooks hook run --allow-unknown-hook-name test-hook -- one 2>>actual && + git -c core.hooksPath=my-hooks/ hook run --allow-unknown-hook-name test-hook -- two 2>>actual && + git -c core.hooksPath="$PWD/my-hooks" hook run --allow-unknown-hook-name test-hook -- three 2>>actual && + git -c core.hooksPath="$PWD/my-hooks/" hook run --allow-unknown-hook-name test-hook -- four 2>>actual && test_cmp expect actual ' @@ -263,7 +292,7 @@ test_expect_success 'hook can be configured for multiple events' ' # 'ghi' should be included in both 'pre-commit' and 'test-hook' git hook list pre-commit >actual && grep "ghi" actual && - git hook list test-hook >actual && + git hook list --allow-unknown-hook-name test-hook >actual && grep "ghi" actual ' @@ -337,15 +366,15 @@ test_expect_success 'stdin to multiple hooks' ' b3 EOF - git hook run --to-stdin=input test-hook 2>actual && + git hook run --allow-unknown-hook-name --to-stdin=input test-hook 2>actual && test_cmp expected actual ' test_expect_success 'rejects hooks with no commands configured' ' test_config hook.broken.event "test-hook" && - test_must_fail git hook list test-hook 2>actual && + test_must_fail git hook list --allow-unknown-hook-name test-hook 2>actual && test_grep "hook.broken.command" actual && - test_must_fail git hook run test-hook 2>actual && + test_must_fail git hook run --allow-unknown-hook-name test-hook 2>actual && test_grep "hook.broken.command" actual ' @@ -354,11 +383,19 @@ test_expect_success 'disabled hook is not run' ' test_config hook.skipped.command "echo \"Should not run\"" && test_config hook.skipped.enabled false && - git hook run --ignore-missing test-hook 2>actual && + git hook run --allow-unknown-hook-name --ignore-missing test-hook 2>actual && test_must_be_empty actual ' -test_expect_success 'disabled hook does not appear in git hook list' ' +test_expect_success 'disabled hook with no command warns' ' + test_config hook.nocommand.event "pre-commit" && + test_config hook.nocommand.enabled false && + + git hook list pre-commit 2>actual && + test_grep "disabled hook.*nocommand.*no command configured" actual +' + +test_expect_success 'disabled hook appears as disabled in git hook list' ' test_config hook.active.event "pre-commit" && test_config hook.active.command "echo active" && test_config hook.inactive.event "pre-commit" && @@ -366,8 +403,27 @@ test_expect_success 'disabled hook does not appear in git hook list' ' test_config hook.inactive.enabled false && git hook list pre-commit >actual && - test_grep "active" actual && - test_grep ! "inactive" actual + test_grep "^active$" actual && + test_grep "^disabled inactive$" actual +' + +test_expect_success 'disabled hook shows scope with --show-scope' ' + test_config hook.myhook.event "pre-commit" && + test_config hook.myhook.command "echo hi" && + test_config hook.myhook.enabled false && + + git hook list --show-scope pre-commit >actual && + test_grep "^local disabled myhook$" actual +' + +test_expect_success 'disabled configured hook is not reported as existing by hook_exists' ' + test_when_finished "rm -f git-bugreport-hook-exists-test.txt" && + test_config hook.linter.event "pre-commit" && + test_config hook.linter.command "echo lint" && + test_config hook.linter.enabled false && + + git bugreport -s hook-exists-test && + test_grep ! "pre-commit" git-bugreport-hook-exists-test.txt ' test_expect_success 'globally disabled hook can be re-enabled locally' ' @@ -377,10 +433,59 @@ test_expect_success 'globally disabled hook can be re-enabled locally' ' test_config hook.global-hook.enabled true && echo "global-hook ran" >expected && - git hook run test-hook 2>actual && + git hook run --allow-unknown-hook-name test-hook 2>actual && + test_cmp expected actual +' + +test_expect_success 'configured hooks run before hookdir hook' ' + setup_hookdir && + test_config hook.first.event "pre-commit" && + test_config hook.first.command "echo first" && + test_config hook.second.event "pre-commit" && + test_config hook.second.command "echo second" && + + cat >expected <<-\EOF && + first + second + hook from hookdir + EOF + + git hook list pre-commit >actual && + test_cmp expected actual && + + # "Legacy Hook" is the output of the hookdir pre-commit script + # written by setup_hookdir() above. + cat >expected <<-\EOF && + first + second + "Legacy Hook" + EOF + + git hook run pre-commit 2>actual && test_cmp expected actual ' +test_expect_success 'git hook list --show-scope shows config scope' ' + setup_hookdir && + test_config_global hook.global-hook.command "echo global" && + test_config_global hook.global-hook.event pre-commit --add && + test_config hook.local-hook.command "echo local" && + test_config hook.local-hook.event pre-commit --add && + + cat >expected <<-\EOF && + global global-hook + local local-hook + hook from hookdir + EOF + git hook list --show-scope pre-commit >actual && + test_cmp expected actual && + + # without --show-scope the scope must not appear + git hook list pre-commit >actual && + test_grep ! "^global " actual && + test_grep ! "^local " actual +' + test_expect_success 'git hook run a hook with a bad shebang' ' test_when_finished "rm -rf bad-hooks" && mkdir bad-hooks && @@ -388,7 +493,7 @@ test_expect_success 'git hook run a hook with a bad shebang' ' test_expect_code 1 git \ -c core.hooksPath=bad-hooks \ - hook run test-hook >out 2>err && + hook run --allow-unknown-hook-name test-hook >out 2>err && test_must_be_empty out && # TODO: We should emit the same (or at least a more similar) @@ -412,7 +517,7 @@ test_expect_success 'stdin to hooks' ' EOF echo hello >input && - git hook run --to-stdin=input test-hook 2>actual && + git hook run --allow-unknown-hook-name --to-stdin=input test-hook 2>actual && test_cmp expect actual ' diff --git a/t/t1900-repo-info.sh b/t/t1900-repo-info.sh index a9eb07abe8..39bb77dda0 100755 --- a/t/t1900-repo-info.sh +++ b/t/t1900-repo-info.sh @@ -149,4 +149,10 @@ test_expect_success 'git repo info --keys uses lines as its default output forma test_cmp expect actual ' +test_expect_success 'git repo info -h shows only repo info usage' ' + test_must_fail git repo info -h >actual && + test_grep "git repo info" actual && + test_grep ! "git repo structure" actual +' + test_done diff --git a/t/t1901-repo-structure.sh b/t/t1901-repo-structure.sh index 98921ce1cb..10050abd70 100755 --- a/t/t1901-repo-structure.sh +++ b/t/t1901-repo-structure.sh @@ -224,4 +224,10 @@ test_expect_success 'progress meter option' ' ) ' +test_expect_success 'git repo structure -h shows only repo structure usage' ' + test_must_fail git repo structure -h >actual && + test_grep "git repo structure" actual && + test_grep ! "git repo info" actual +' + test_done diff --git a/t/t2000-conflict-when-checking-files-out.sh b/t/t2000-conflict-when-checking-files-out.sh index f18616ad2b..af199d8191 100755 --- a/t/t2000-conflict-when-checking-files-out.sh +++ b/t/t2000-conflict-when-checking-files-out.sh @@ -35,30 +35,30 @@ show_files() { sed -e 's/^\([0-9]*\) [^ ]* [0-9a-f]* /tr: \1 /' } -date >path0 -mkdir path1 -date >path1/file1 - -test_expect_success \ - 'git update-index --add various paths.' \ - 'git update-index --add path0 path1/file1' - -rm -fr path0 path1 -mkdir path0 -date >path0/file0 -date >path1 +test_expect_success 'prepare files path0 and path1/file1' ' + date >path0 && + mkdir path1 && + date >path1/file1 && + git update-index --add path0 path1/file1 +' -test_expect_success \ - 'git checkout-index without -f should fail on conflicting work tree.' \ - 'test_must_fail git checkout-index -a' +test_expect_success 'prepare working tree files with D/F conflicts' ' + rm -fr path0 path1 && + mkdir path0 && + date >path0/file0 && + date >path1 +' -test_expect_success \ - 'git checkout-index with -f should succeed.' \ - 'git checkout-index -f -a' +test_expect_success 'git checkout-index without -f should fail on conflicting work tree.' ' + test_must_fail git checkout-index -a +' -test_expect_success \ - 'git checkout-index conflicting paths.' \ - 'test -f path0 && test -d path1 && test -f path1/file1' +test_expect_success 'git checkout-index with -f should succeed.' ' + git checkout-index -f -a && + test_path_is_file path0 && + test_path_is_dir path1 && + test_path_is_file path1/file1 +' test_expect_success SYMLINKS 'checkout-index -f twice with --prefix' ' mkdir -p tar/get && @@ -83,53 +83,63 @@ test_expect_success SYMLINKS 'checkout-index -f twice with --prefix' ' # path path3 is occupied by a non-directory. With "-f" it should remove # the symlink path3 and create directory path3 and file path3/file1. -mkdir path2 -date >path2/file0 -test_expect_success \ - 'git update-index --add path2/file0' \ - 'git update-index --add path2/file0' -test_expect_success \ - 'writing tree out with git write-tree' \ - 'tree1=$(git write-tree)' +test_expect_success 'prepare path2/file0 and index' ' + mkdir path2 && + date >path2/file0 && + git update-index --add path2/file0 +' + +test_expect_success 'write tree with path2/file0' ' + tree1=$(git write-tree) +' + test_debug 'show_files $tree1' -mkdir path3 -date >path3/file1 -test_expect_success \ - 'git update-index --add path3/file1' \ - 'git update-index --add path3/file1' -test_expect_success \ - 'writing tree out with git write-tree' \ - 'tree2=$(git write-tree)' +test_expect_success 'prepare path3/file1 and index' ' + mkdir path3 && + date >path3/file1 && + git update-index --add path3/file1 +' + +test_expect_success 'write tree with path3/file1' ' + tree2=$(git write-tree) +' + test_debug 'show_files $tree2' -rm -fr path3 -test_expect_success \ - 'read previously written tree and checkout.' \ - 'git read-tree -m $tree1 && git checkout-index -f -a' +test_expect_success 'read previously written tree and checkout.' ' + rm -fr path3 && + git read-tree -m $tree1 && + git checkout-index -f -a +' + test_debug 'show_files $tree1' -test_expect_success \ - 'add a symlink' \ - 'test_ln_s_add path2 path3' -test_expect_success \ - 'writing tree out with git write-tree' \ - 'tree3=$(git write-tree)' +test_expect_success 'add a symlink' ' + test_ln_s_add path2 path3 +' + +test_expect_success 'write tree with symlink path3' ' + tree3=$(git write-tree) +' + test_debug 'show_files $tree3' # Morten says "Got that?" here. # Test begins. -test_expect_success \ - 'read previously written tree and checkout.' \ - 'git read-tree $tree2 && git checkout-index -f -a' +test_expect_success 'read previously written tree and checkout.' ' + git read-tree $tree2 && + git checkout-index -f -a +' + test_debug 'show_files $tree2' -test_expect_success \ - 'checking out conflicting path with -f' \ - 'test ! -h path2 && test -d path2 && - test ! -h path3 && test -d path3 && - test ! -h path2/file0 && test -f path2/file0 && - test ! -h path3/file1 && test -f path3/file1' +test_expect_success 'checking out conflicting path with -f' ' + test_path_is_dir_not_symlink path2 && + test_path_is_dir_not_symlink path3 && + test_path_is_file_not_symlink path2/file0 && + test_path_is_file_not_symlink path3/file1 +' test_done diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh index cc72ead79f..3bffe5da8a 100755 --- a/t/t2107-update-index-basic.sh +++ b/t/t2107-update-index-basic.sh @@ -86,7 +86,7 @@ test_expect_success '.lock files cleaned up' ' # the_index.cache_changed is zero, rollback_lock_file fails git update-index --refresh --verbose >out && test_must_be_empty out && - ! test -f .git/index.lock + test_path_is_missing .git/index.lock ) ' diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh index 192ad14b5f..44c1936e4d 100755 --- a/t/t2203-add-intent.sh +++ b/t/t2203-add-intent.sh @@ -16,7 +16,8 @@ test_expect_success 'intent to add' ' ' test_expect_success 'git status' ' - git status --porcelain | grep -v actual >actual && + git status --porcelain >actual.raw && + grep -v actual actual.raw >actual && cat >expect <<-\EOF && DA 1.t A elif @@ -26,7 +27,8 @@ test_expect_success 'git status' ' ' test_expect_success 'git status with porcelain v2' ' - git status --porcelain=v2 | grep -v "^?" >actual && + git status --porcelain=v2 >actual.raw && + grep -v "^?" actual.raw >actual && nam1=$(echo 1 | git hash-object --stdin) && nam2=$(git hash-object elif) && cat >expect <<-EOF && @@ -171,17 +173,20 @@ test_expect_success 'rename detection finds the right names' ' mv first third && git add -N third && - git status | grep -v "^?" >actual.1 && + git status >actual.raw.1 && + grep -v "^?" actual.raw.1 >actual.1 && test_grep "renamed: *first -> third" actual.1 && - git status --porcelain | grep -v "^?" >actual.2 && + git status --porcelain >actual.raw.2 && + grep -v "^?" actual.raw.2 >actual.2 && cat >expected.2 <<-\EOF && R first -> third EOF test_cmp expected.2 actual.2 && hash=$(git hash-object third) && - git status --porcelain=v2 | grep -v "^?" >actual.3 && + git status --porcelain=v2 >actual.raw.3 && + grep -v "^?" actual.raw.3 >actual.3 && cat >expected.3 <<-EOF && 2 .R N... 100644 100644 100644 $hash $hash R100 third first EOF @@ -211,11 +216,13 @@ test_expect_success 'double rename detection in status' ' mv second third && git add -N third && - git status | grep -v "^?" >actual.1 && + git status >actual.raw.1 && + grep -v "^?" actual.raw.1 >actual.1 && test_grep "renamed: *first -> second" actual.1 && test_grep "renamed: *second -> third" actual.1 && - git status --porcelain | grep -v "^?" >actual.2 && + git status --porcelain >actual.raw.2 && + grep -v "^?" actual.raw.2 >actual.2 && cat >expected.2 <<-\EOF && R first -> second R second -> third @@ -223,7 +230,8 @@ test_expect_success 'double rename detection in status' ' test_cmp expected.2 actual.2 && hash=$(git hash-object third) && - git status --porcelain=v2 | grep -v "^?" >actual.3 && + git status --porcelain=v2 >actual.raw.3 && + grep -v "^?" actual.raw.3 >actual.3 && cat >expected.3 <<-EOF && 2 R. N... 100644 100644 100644 $hash $hash R100 second first 2 .R N... 100644 100644 100644 $hash $hash R100 third second diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh index 023e1301c8..58b4445cc4 100755 --- a/t/t2400-worktree-add.sh +++ b/t/t2400-worktree-add.sh @@ -987,7 +987,7 @@ test_dwim_orphan () { then test_must_be_empty actual else - grep "$info_text" actual + test_grep "$info_text" actual fi elif [ "$outcome" = "no_infer" ] then @@ -996,39 +996,35 @@ test_dwim_orphan () { then test_must_be_empty actual else - ! grep "$info_text" actual + test_grep ! "$info_text" actual fi elif [ "$outcome" = "fetch_error" ] then test_must_fail git $dashc_args worktree add $args 2>actual && - grep "$fetch_error_text" actual + test_grep "$fetch_error_text" actual elif [ "$outcome" = "fatal_orphan_bad_combo" ] then test_must_fail git $dashc_args worktree add $args 2>actual && if [ $use_quiet -eq 1 ] then - ! grep "$info_text" actual + test_grep ! "$info_text" actual else - grep "$info_text" actual + test_grep "$info_text" actual fi && - grep "$bad_combo_regex" actual + test_grep "$bad_combo_regex" actual elif [ "$outcome" = "warn_bad_head" ] then test_must_fail git $dashc_args worktree add $args 2>actual && if [ $use_quiet -eq 1 ] then - grep "$invalid_ref_regex" actual && - ! grep "$orphan_hint" actual + test_grep "$invalid_ref_regex" actual && + test_grep ! "$orphan_hint" actual else - headpath=$(git $dashc_args rev-parse --path-format=absolute --git-path HEAD) && - headcontents=$(cat "$headpath") && - grep "HEAD points to an invalid (or orphaned) reference" actual && - grep "HEAD path: .$headpath." actual && - grep "HEAD contents: .$headcontents." actual && - grep "$orphan_hint" actual && - ! grep "$info_text" actual + test_grep "HEAD points to an invalid (or orphaned) reference" actual && + test_grep "$orphan_hint" actual && + test_grep ! "$info_text" actual fi && - grep "$invalid_ref_regex" actual + test_grep "$invalid_ref_regex" actual else # Unreachable false diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index c58e505c43..e7829c2c4b 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -1494,7 +1494,8 @@ test_expect_success 'refuse --edit-description on unborn branch for now' ' ' test_expect_success '--merged catches invalid object names' ' - test_must_fail git branch --merged 0000000000000000000000000000000000000000 + test_must_fail git branch --merged $ZERO_OID 2>err && + test_grep "must point to a commit" err ' test_expect_success '--list during rebase' ' diff --git a/t/t3430-rebase-merges.sh b/t/t3430-rebase-merges.sh index cc627e34a7..84b2d0e664 100755 --- a/t/t3430-rebase-merges.sh +++ b/t/t3430-rebase-merges.sh @@ -507,9 +507,11 @@ test_expect_success 'octopus merges' ' git rebase -i --force-rebase -r HEAD^^ && test "Hank" = "$(git show -s --format=%an HEAD)" && test "$before" != $(git rev-parse HEAD) && - test_cmp_graph HEAD^^.. <<-\EOF + # NOTE: do not quote this heredoc, Dash 0.5.13 has a bug with heredocs + # that contain multibyte chars. + test_cmp_graph HEAD^^.. <<-EOF *-. Tüntenfüsch - |\ \ + |\\ \\ | | * three | * | two | |/ diff --git a/t/t3440-rebase-trailer.sh b/t/t3440-rebase-trailer.sh new file mode 100755 index 0000000000..8b47579566 --- /dev/null +++ b/t/t3440-rebase-trailer.sh @@ -0,0 +1,147 @@ +#!/bin/sh +# + +test_description='git rebase --trailer integration tests +We verify that --trailer works with the merge backend, +and that it is rejected early when the apply backend is requested.' + +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME + +. ./test-lib.sh +. "$TEST_DIRECTORY"/lib-rebase.sh # test_commit_message, helpers + +REVIEWED_BY_TRAILER="Reviewed-by: Dev <dev@example.com>" +SP=" " + +test_expect_success 'setup repo with a small history' ' + git commit --allow-empty -m "Initial empty commit" && + test_commit first file a && + test_commit second file && + git checkout -b conflict-branch first && + test_commit file-2 file-2 && + test_commit conflict file && + test_commit third file && + git checkout main +' + +test_expect_success 'apply backend is rejected with --trailer' ' + git checkout -B apply-backend third && + test_expect_code 128 \ + git rebase --apply --trailer "$REVIEWED_BY_TRAILER" HEAD^ 2>err && + test_grep "fatal: --trailer requires the merge backend" err +' + +test_expect_success 'reject empty --trailer argument' ' + git checkout -B empty-trailer third && + test_expect_code 128 git rebase --trailer "" HEAD^ 2>err && + test_grep "empty --trailer" err +' + +test_expect_success 'reject trailer with missing key before separator' ' + git checkout -B missing-key third && + test_expect_code 128 git rebase --trailer ": no-key" HEAD^ 2>err && + test_grep "missing key before separator" err +' + +test_expect_success 'allow trailer with missing value after separator' ' + git checkout -B missing-value third && + git rebase --trailer "Acked-by:" HEAD^ && + test_commit_message HEAD <<-EOF + third + + Acked-by:${SP} + EOF +' + +test_expect_success 'CLI trailer duplicates allowed; replace policy keeps last' ' + git checkout -B replace-policy third && + git -c trailer.Bug.ifexists=replace -c trailer.Bug.ifmissing=add \ + rebase --trailer "Bug: 123" --trailer "Bug: 456" HEAD^ && + test_commit_message HEAD <<-EOF + third + + Bug: 456 + EOF +' + +test_expect_success 'multiple Signed-off-by trailers all preserved' ' + git checkout -B multiple-signoff third && + git rebase --trailer "Signed-off-by: Dev A <a@example.com>" \ + --trailer "Signed-off-by: Dev B <b@example.com>" HEAD^ && + test_commit_message HEAD <<-EOF + third + + Signed-off-by: Dev A <a@example.com> + Signed-off-by: Dev B <b@example.com> + EOF +' + +test_expect_success 'rebase --trailer adds trailer after conflicts' ' + git checkout -B trailer-conflict third && + test_commit fourth file && + test_must_fail git rebase --trailer "$REVIEWED_BY_TRAILER" second && + git checkout --theirs file && + git add file && + git rebase --continue && + test_commit_message HEAD <<-EOF && + fourth + + $REVIEWED_BY_TRAILER + EOF + test_commit_message HEAD^ <<-EOF + third + + $REVIEWED_BY_TRAILER + EOF +' + +test_expect_success '--trailer handles fixup commands in todo list' ' + git checkout -B fixup-trailer third && + test_commit fixup-base base && + test_commit fixup-second second && + cat >todo <<-\EOF && + pick fixup-base fixup-base + fixup fixup-second fixup-second + EOF + ( + set_replace_editor todo && + git rebase -i --trailer "$REVIEWED_BY_TRAILER" HEAD~2 + ) && + test_commit_message HEAD <<-EOF && + fixup-base + + $REVIEWED_BY_TRAILER + EOF + git reset --hard fixup-second && + cat >todo <<-\EOF && + pick fixup-base fixup-base + fixup -C fixup-second fixup-second + EOF + ( + set_replace_editor todo && + git rebase -i --trailer "$REVIEWED_BY_TRAILER" HEAD~2 + ) && + test_commit_message HEAD <<-EOF + fixup-second + + $REVIEWED_BY_TRAILER + EOF +' + +test_expect_success 'rebase --root honors trailer.<name>.key' ' + git checkout -B root-trailer first && + git -c trailer.review.key=Reviewed-by rebase --root \ + --trailer=review="Dev <dev@example.com>" && + test_commit_message HEAD <<-EOF && + first + + Reviewed-by: Dev <dev@example.com> + EOF + test_commit_message HEAD^ <<-EOF + Initial empty commit + + Reviewed-by: Dev <dev@example.com> + EOF +' +test_done diff --git a/t/t3452-history-split.sh b/t/t3452-history-split.sh new file mode 100755 index 0000000000..8ed0cebb50 --- /dev/null +++ b/t/t3452-history-split.sh @@ -0,0 +1,757 @@ +#!/bin/sh + +test_description='tests for git-history split subcommand' + +. ./test-lib.sh +. "$TEST_DIRECTORY/lib-log-graph.sh" + +# The fake editor takes multiple arguments, each of which represents a commit +# message. Subsequent invocations of the editor will then yield those messages +# in order. +# +set_fake_editor () { + printf "%s\n" "$@" >fake-input && + write_script fake-editor.sh <<-\EOF && + head -n1 fake-input >"$1" + sed 1d fake-input >fake-input.trimmed && + mv fake-input.trimmed fake-input + EOF + test_set_editor "$(pwd)"/fake-editor.sh +} + +expect_graph () { + cat >expect && + lib_test_cmp_graph --graph --format=%s "$@" +} + +expect_log () { + git log --format="%s" >actual && + cat >expect && + test_cmp expect actual +} + +expect_tree_entries () { + git ls-tree --name-only "$1" >actual && + cat >expect && + test_cmp expect actual +} + +test_expect_success 'refuses to work with merge commits' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit base && + git branch branch && + test_commit ours && + git switch branch && + test_commit theirs && + git switch - && + git merge theirs && + test_must_fail git history split HEAD 2>err && + test_grep "cannot split up merge commit" err && + test_must_fail git history split HEAD~ 2>err && + test_grep "replaying merge commits is not supported yet" err + ) +' + +test_expect_success 'errors on missing commit argument' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit initial && + test_must_fail git history split 2>err && + test_grep "command expects a committish" err + ) +' + +test_expect_success 'errors on unknown revision' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit initial && + test_must_fail git history split does-not-exist 2>err && + test_grep "commit cannot be found" err + ) +' + +test_expect_success '--dry-run does not modify any refs' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit base && + touch bar foo && + git add . && + git commit -m split-me && + + git refs list --include-root-refs >before && + + set_fake_editor "first" "second" && + git history split --dry-run HEAD <<-EOF && + y + n + EOF + + git refs list --include-root-refs >after && + test_cmp before after + ) +' + +test_expect_success 'can split up tip commit' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit initial && + touch bar foo && + git add . && + git commit -m split-me && + + git symbolic-ref HEAD >expect && + set_fake_editor "first" "second" && + git history split HEAD <<-EOF && + y + n + EOF + git symbolic-ref HEAD >actual && + test_cmp expect actual && + + expect_log <<-EOF && + second + first + initial + EOF + + expect_tree_entries HEAD~ <<-EOF && + bar + initial.t + EOF + + expect_tree_entries HEAD <<-EOF && + bar + foo + initial.t + EOF + + git reflog >reflog && + test_grep "split: updating HEAD" reflog + ) +' + +test_expect_success 'can split up root commit' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + touch bar foo && + git add . && + git commit -m root && + test_commit tip && + + set_fake_editor "first" "second" && + git history split HEAD~ <<-EOF && + y + n + EOF + + expect_log <<-EOF && + tip + second + first + EOF + + expect_tree_entries HEAD~2 <<-EOF && + bar + EOF + + expect_tree_entries HEAD~ <<-EOF && + bar + foo + EOF + + expect_tree_entries HEAD <<-EOF + bar + foo + tip.t + EOF + ) +' + +test_expect_success 'can split up in-between commit' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit initial && + touch bar foo && + git add . && + git commit -m split-me && + test_commit tip && + + set_fake_editor "first" "second" && + git history split HEAD~ <<-EOF && + y + n + EOF + + expect_log <<-EOF && + tip + second + first + initial + EOF + + expect_tree_entries HEAD~2 <<-EOF && + bar + initial.t + EOF + + expect_tree_entries HEAD~ <<-EOF && + bar + foo + initial.t + EOF + + expect_tree_entries HEAD <<-EOF + bar + foo + initial.t + tip.t + EOF + ) +' + +test_expect_success 'can split HEAD only' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit base && + touch a b && + git add . && + git commit -m split-me && + git branch unrelated && + + set_fake_editor "ours-a" "ours-b" && + git history split --update-refs=head HEAD <<-EOF && + y + n + EOF + expect_graph --branches <<-EOF + * ours-b + * ours-a + | * split-me + |/ + * base + EOF + ) +' + +test_expect_success 'can split detached HEAD' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit initial && + touch bar foo && + git add . && + git commit -m split-me && + git checkout --detach HEAD && + + set_fake_editor "first" "second" && + git history split --update-refs=head HEAD <<-EOF && + y + n + EOF + + # HEAD should be detached and updated. + test_must_fail git symbolic-ref HEAD && + + expect_log <<-EOF + second + first + initial + EOF + ) +' + +test_expect_success 'can split commit in unrelated branch' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit base && + git branch ours && + git switch --create theirs && + touch theirs-a theirs-b && + git add . && + git commit -m theirs && + git switch ours && + test_commit ours && + + # With --update-refs=head it is not possible to split up a + # commit that is unrelated to HEAD. + test_must_fail git history split --update-refs=head theirs 2>err && + test_grep "rewritten commit must be an ancestor of HEAD" err && + + set_fake_editor "theirs-rewritten-a" "theirs-rewritten-b" && + git history split theirs <<-EOF && + y + n + EOF + expect_graph --branches <<-EOF && + * ours + | * theirs-rewritten-b + | * theirs-rewritten-a + |/ + * base + EOF + + expect_tree_entries theirs~ <<-EOF && + base.t + theirs-a + EOF + + expect_tree_entries theirs <<-EOF + base.t + theirs-a + theirs-b + EOF + ) +' + +test_expect_success 'updates multiple descendant branches' ' + test_when_finished "rm -rf repo" && + git init repo --initial-branch=main && + ( + cd repo && + test_commit base && + touch file-a file-b && + git add . && + git commit -m split-me && + git branch branch && + test_commit on-main && + git switch branch && + test_commit on-branch && + git switch main && + + set_fake_editor "split-a" "split-b" && + git history split HEAD~ <<-EOF && + y + n + EOF + + # Both branches should now descend from the split commits. + expect_graph --branches <<-EOF + * on-branch + | * on-main + |/ + * split-b + * split-a + * base + EOF + ) +' + +test_expect_success 'can pick multiple hunks' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + touch bar baz foo qux && + git add . && + git commit -m split-me && + + set_fake_editor "first" "second" && + git history split HEAD <<-EOF && + y + n + y + n + EOF + + expect_tree_entries HEAD~ <<-EOF && + bar + foo + EOF + + expect_tree_entries HEAD <<-EOF + bar + baz + foo + qux + EOF + ) +' + +test_expect_success 'can use only last hunk' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + touch bar foo && + git add . && + git commit -m split-me && + + set_fake_editor "first" "second" && + git history split HEAD <<-EOF && + n + y + EOF + + expect_log <<-EOF && + second + first + EOF + + expect_tree_entries HEAD~ <<-EOF && + foo + EOF + + expect_tree_entries HEAD <<-EOF + bar + foo + EOF + ) +' + +test_expect_success 'can split commit with file deletions' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + echo a >a && + echo b >b && + echo c >c && + git add . && + git commit -m base && + git rm a b && + git commit -m delete-both && + + set_fake_editor "delete-a" "delete-b" && + git history split HEAD <<-EOF && + y + n + EOF + + expect_log <<-EOF && + delete-b + delete-a + base + EOF + + expect_tree_entries HEAD~ <<-EOF && + b + c + EOF + + expect_tree_entries HEAD <<-EOF + c + EOF + ) +' + +test_expect_success 'preserves original authorship' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit initial && + touch bar foo && + git add . && + GIT_AUTHOR_NAME="Other Author" \ + GIT_AUTHOR_EMAIL="other@example.com" \ + git commit -m split-me && + + set_fake_editor "first" "second" && + git history split HEAD <<-EOF && + y + n + EOF + + git log -1 --format="%an <%ae>" HEAD~ >actual && + echo "Other Author <other@example.com>" >expect && + test_cmp expect actual && + + git log -1 --format="%an <%ae>" HEAD >actual && + test_cmp expect actual + ) +' + +test_expect_success 'aborts with empty commit message' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + touch bar foo && + git add . && + git commit -m split-me && + + set_fake_editor "" && + test_must_fail git history split HEAD <<-EOF 2>err && + y + n + EOF + test_grep "Aborting commit due to empty commit message." err + ) +' + +test_expect_success 'commit message editor sees split-out changes' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + touch bar foo && + git add . && + git commit -m split-me && + + write_script fake-editor.sh <<-\EOF && + cat "$1" >>MESSAGES && + echo "some commit message" >"$1" + EOF + test_set_editor "$(pwd)"/fake-editor.sh && + + git history split HEAD <<-EOF && + y + n + EOF + + # Note that we expect to see the messages twice, once for each + # of the commits. The committed files are different though. + cat >expect <<-EOF && + split-me + + # Please enter the commit message for the split-out changes. Lines starting + # with ${SQ}#${SQ} will be ignored, and an empty message aborts the commit. + # Changes to be committed: + # new file: bar + # + split-me + + # Please enter the commit message for the split-out changes. Lines starting + # with ${SQ}#${SQ} will be ignored, and an empty message aborts the commit. + # Changes to be committed: + # new file: foo + # + EOF + test_cmp expect MESSAGES && + + expect_log <<-EOF + some commit message + some commit message + EOF + ) +' + +test_expect_success 'can use pathspec to limit what gets split' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + touch bar foo && + git add . && + git commit -m split-me && + + set_fake_editor "first" "second" && + git history split HEAD -- foo <<-EOF && + y + EOF + + expect_tree_entries HEAD~ <<-EOF && + foo + EOF + + expect_tree_entries HEAD <<-EOF + bar + foo + EOF + ) +' + +test_expect_success 'pathspec matching no files produces empty split error' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit initial && + touch bar foo && + git add . && + git commit -m split-me && + + set_fake_editor "first" "second" && + test_must_fail git history split HEAD -- nonexistent 2>err && + test_grep "split commit is empty" err + ) +' + +test_expect_success 'split with multiple pathspecs' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit initial && + touch a b c d && + git add . && + git commit -m split-me && + + # Only a and c should be offered for splitting. + set_fake_editor "split-ac" "remainder" && + git history split HEAD -- a c <<-EOF && + y + y + EOF + + expect_tree_entries HEAD~ <<-EOF && + a + c + initial.t + EOF + + expect_tree_entries HEAD <<-EOF + a + b + c + d + initial.t + EOF + ) +' + +test_expect_success 'split with file mode change' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + echo content >script && + git add . && + git commit -m base && + test_chmod +x script && + echo change >script && + git commit -a -m "mode and content change" && + + set_fake_editor "mode-change" "content-change" && + git history split HEAD <<-EOF && + y + n + EOF + + expect_log <<-EOF + content-change + mode-change + base + EOF + ) +' + +test_expect_success 'refuses to create empty split-out commit' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit base && + touch bar foo && + git add . && + git commit -m split-me && + + test_must_fail git history split HEAD 2>err <<-EOF && + n + n + EOF + test_grep "split commit is empty" err + ) +' + +test_expect_success 'hooks are not executed for rewritten commits' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + touch bar foo && + git add . && + git commit -m split-me && + old_head=$(git rev-parse HEAD) && + + ORIG_PATH="$(pwd)" && + export ORIG_PATH && + for hook in prepare-commit-msg pre-commit post-commit post-rewrite commit-msg + do + write_script .git/hooks/$hook <<-\EOF || exit 1 + touch "$ORIG_PATH"/hooks.log + EOF + done && + + set_fake_editor "first" "second" && + git history split HEAD <<-EOF && + y + n + EOF + + expect_log <<-EOF && + second + first + EOF + + test_path_is_missing hooks.log + ) +' + +test_expect_success 'refuses to create empty original commit' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + touch bar foo && + git add . && + git commit -m split-me && + + test_must_fail git history split HEAD 2>err <<-EOF && + y + y + EOF + test_grep "split commit tree matches original commit" err + ) +' + +test_expect_success 'retains changes in the worktree and index' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + echo a >a && + echo b >b && + git add . && + git commit -m "initial commit" && + echo a-modified >a && + echo b-modified >b && + git add b && + set_fake_editor "a-only" "remainder" && + git history split HEAD <<-EOF && + y + n + EOF + + expect_tree_entries HEAD~ <<-EOF && + a + EOF + expect_tree_entries HEAD <<-EOF && + a + b + EOF + + cat >expect <<-\EOF && + M a + M b + ?? actual + ?? expect + ?? fake-editor.sh + ?? fake-input + EOF + git status --porcelain >actual && + test_cmp expect actual + ) +' + +test_done diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh index a03f8f9293..89819ad4d2 100755 --- a/t/t3650-replay-basics.sh +++ b/t/t3650-replay-basics.sh @@ -74,29 +74,31 @@ test_expect_success '--onto with invalid commit-ish' ' test_cmp expect actual ' -test_expect_success 'option --onto or --advance is mandatory' ' - echo "error: option --onto or --advance is mandatory" >expect && +test_expect_success 'exactly one of --onto, --advance, or --revert is required' ' + echo "error: exactly one of --onto, --advance, or --revert is required" >expect && test_might_fail git replay -h >>expect && test_must_fail git replay topic1..topic2 2>actual && test_cmp expect actual ' -test_expect_success 'no base or negative ref gives no-replaying down to root error' ' - echo "fatal: replaying down from root commit is not supported yet!" >expect && - test_must_fail git replay --onto=topic1 topic2 2>actual && +test_expect_success 'replay down to root onto another branch' ' + git replay --ref-action=print --onto main topic2 >result && + + test_line_count = 1 result && + + git log --format=%s $(cut -f 3 -d " " result) >actual && + test_write_lines E D C M L B A >expect && test_cmp expect actual ' -test_expect_success 'options --advance and --contained cannot be used together' ' - printf "fatal: options ${SQ}--advance${SQ} " >expect && - printf "and ${SQ}--contained${SQ} cannot be used together\n" >>expect && +test_expect_success '--advance and --contained cannot be used together' ' test_must_fail git replay --advance=main --contained \ topic1..topic2 2>actual && - test_cmp expect actual + test_grep "cannot be used together" actual ' test_expect_success 'cannot advance target ... ordering would be ill-defined' ' - echo "fatal: cannot advance target with multiple sources because ordering would be ill-defined" >expect && + echo "fatal: ${SQ}--advance${SQ} cannot be used with multiple revision ranges because the ordering would be ill-defined" >expect && test_must_fail git replay --advance=main main topic1 topic2 2>actual && test_cmp expect actual ' @@ -398,4 +400,103 @@ test_expect_success 'invalid replay.refAction value' ' test_grep "invalid.*replay.refAction.*value" error ' +test_expect_success 'argument to --revert must be a reference' ' + echo "fatal: argument to --revert must be a reference" >expect && + oid=$(git rev-parse main) && + test_must_fail git replay --revert=$oid topic1..topic2 2>actual && + test_cmp expect actual +' + +test_expect_success 'cannot revert with multiple sources' ' + echo "fatal: ${SQ}--revert${SQ} cannot be used with multiple revision ranges because the ordering would be ill-defined" >expect && + test_must_fail git replay --revert main main topic1 topic2 2>actual && + test_cmp expect actual +' + +test_expect_success 'using replay --revert to revert commits' ' + # Reuse existing topic4 branch (has commits I and J on top of main) + START=$(git rev-parse topic4) && + test_when_finished "git branch -f topic4 $START" && + + # Revert commits I and J + git replay --revert topic4 topic4~2..topic4 && + + # Verify the revert commits were created (newest-first ordering + # means J is reverted first, then I on top) + git log --format=%s -4 topic4 >actual && + cat >expect <<-\EOF && + Revert "I" + Revert "J" + J + I + EOF + test_cmp expect actual && + + # Verify commit message format includes hash (tip is Revert "I") + test_commit_message topic4 <<-EOF && + Revert "I" + + This reverts commit $(git rev-parse I). + EOF + + # Verify reflog message + git reflog topic4 -1 --format=%gs >reflog-msg && + echo "replay --revert topic4" >expect-reflog && + test_cmp expect-reflog reflog-msg +' + +test_expect_success 'using replay --revert in bare repo' ' + # Reuse existing topic4 in bare repo + START=$(git -C bare rev-parse topic4) && + test_when_finished "git -C bare update-ref refs/heads/topic4 $START" && + + # Revert commit J in bare repo + git -C bare replay --revert topic4 topic4~1..topic4 && + + # Verify revert was created + git -C bare log -1 --format=%s topic4 >actual && + echo "Revert \"J\"" >expect && + test_cmp expect actual +' + +test_expect_success 'revert of revert uses Reapply' ' + # Use topic4 and first revert J, then revert the revert + START=$(git rev-parse topic4) && + test_when_finished "git branch -f topic4 $START" && + + # First revert J + git replay --revert topic4 topic4~1..topic4 && + REVERT_J=$(git rev-parse topic4) && + + # Now revert the revert - should become Reapply + git replay --revert topic4 topic4~1..topic4 && + + # Verify Reapply prefix and message format + test_commit_message topic4 <<-EOF + Reapply "J" + + This reverts commit $REVERT_J. + EOF +' + +test_expect_success 'git replay --revert with conflict' ' + # conflict branch has C.conflict which conflicts with topic1s C + test_expect_code 1 git replay --revert conflict B..topic1 +' + +test_expect_success 'git replay --revert incompatible with --contained' ' + test_must_fail git replay --revert topic4 --contained topic4~1..topic4 2>error && + test_grep "cannot be used together" error +' + +test_expect_success 'git replay --revert incompatible with --onto' ' + test_must_fail git replay --revert topic4 --onto main topic4~1..topic4 2>error && + test_grep "cannot be used together" error +' + +test_expect_success 'git replay --revert incompatible with --advance' ' + test_must_fail git replay --revert topic4 --advance main topic4~1..topic4 2>error && + test_grep "cannot be used together" error +' + test_done diff --git a/t/t3902-quoted.sh b/t/t3902-quoted.sh index f528008c36..8660ec5cb0 100755 --- a/t/t3902-quoted.sh +++ b/t/t3902-quoted.sh @@ -60,16 +60,18 @@ With SP in it "\346\277\261\351\207\216\347\264\224" EOF -cat >expect.raw <<\EOF +# NOTE: do not quote this heredoc, Dash 0.5.13 has a bug with heredocs +# that contain multibyte chars. +cat >expect.raw <<EOF Name -"Name and a\nLF" -"Name and an\tHT" -"Name\"" +"Name and a\\nLF" +"Name and an\\tHT" +"Name\\"" With SP in it -"濱野\t純" -"濱野\n純" +"濱野\\t純" +"濱野\\n純" 濱野 純 -"濱野\"純" +"濱野\\"純" 濱野/file 濱野純 EOF diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh index d1d30ac2a9..97b5ac0407 100755 --- a/t/t4012-diff-binary.sh +++ b/t/t4012-diff-binary.sh @@ -68,7 +68,7 @@ test_expect_success 'apply detecting corrupt patch correctly' ' sed -e "s/-CIT/xCIT/" <output >broken && test_must_fail git apply --stat --summary broken 2>detected && detected=$(cat detected) && - detected=$(expr "$detected" : "error.*at line \\([0-9]*\\)\$") && + detected=$(expr "$detected" : "error.*broken:\\([0-9]*\\)\$") && detected=$(sed -ne "${detected}p" broken) && test "$detected" = xCIT ' @@ -77,7 +77,7 @@ test_expect_success 'apply detecting corrupt patch correctly' ' git diff --binary | sed -e "s/-CIT/xCIT/" >broken && test_must_fail git apply --stat --summary broken 2>detected && detected=$(cat detected) && - detected=$(expr "$detected" : "error.*at line \\([0-9]*\\)\$") && + detected=$(expr "$detected" : "error.*broken:\\([0-9]*\\)\$") && detected=$(sed -ne "${detected}p" broken) && test "$detected" = xCIT ' diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index bcdb944017..0b89d127b5 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -380,6 +380,131 @@ test_expect_success 'filename limit applies only to basename' ' done ' +test_expect_success 'cover letter with subject, author and count' ' + rm -rf patches && + test_when_finished "git reset --hard HEAD~1" && + test_when_finished "rm -rf patches test_file" && + touch test_file && + git add test_file && + git commit -m "This is a subject" && + git format-patch --commit-list-format="log:[%(count)/%(total)] %s (%an)" \ + -o patches HEAD~1 && + test_grep "^\[1/1\] This is a subject (A U Thor)$" patches/0000-cover-letter.patch +' + +test_expect_success 'cover letter with custom format no prefix' ' + rm -rf patches && + test_when_finished "git reset --hard HEAD~1" && + test_when_finished "rm -rf patches test_file" && + touch test_file && + git add test_file && + git commit -m "This is a subject" && + git format-patch --commit-list-format="[%(count)/%(total)] %s (%an)" \ + -o patches HEAD~1 && + test_grep "^\[1/1\] This is a subject (A U Thor)$" patches/0000-cover-letter.patch +' + +test_expect_success 'cover letter fail when no prefix and no placeholder' ' + rm -rf patches && + test_when_finished "git reset --hard HEAD~1" && + test_when_finished "rm -rf patches test_file err" && + touch test_file && + git add test_file && + git commit -m "This is a subject" && + test_must_fail git format-patch --commit-list-format="this should fail" \ + -o patches HEAD~1 2>err && + test_grep "is not a valid format string" err +' + +test_expect_success 'cover letter modern format' ' + test_when_finished "git reset --hard HEAD~1" && + test_when_finished "rm -rf patches test_file" && + touch test_file && + git add test_file && + git commit -m "This is a subject" && + git format-patch --commit-list-format="modern" -o patches HEAD~1 && + test_grep "^\[1/1\] This is a subject$" patches/0000-cover-letter.patch +' + +test_expect_success 'cover letter shortlog format' ' + test_when_finished "git reset --hard HEAD~1" && + test_when_finished "rm -rf expect patches result test_file" && + cat >expect <<-"EOF" && + A U Thor (1): + This is a subject + EOF + touch test_file && + git add test_file && + git commit -m "This is a subject" && + git format-patch --commit-list-format=shortlog -o patches HEAD~1 && + grep -E -A 1 "^A U Thor \([[:digit:]]+\):$" patches/0000-cover-letter.patch >result && + cat result && + test_cmp expect result +' + +test_expect_success 'no cover letter but with format specified' ' + test_when_finished "git reset --hard HEAD~1" && + test_when_finished "rm -rf patches result test_file" && + touch test_file && + git add test_file && + git commit -m "This is a subject" && + git format-patch --no-cover-letter --commit-list-format="[%(count)] %s" -o patches HEAD~1 && + test_path_is_missing patches/0000-cover-letter.patch +' + +test_expect_success 'cover letter config with count, subject and author' ' + test_when_finished "rm -rf patches result" && + test_when_finished "git config unset format.coverletter" && + test_when_finished "git config unset format.commitlistformat" && + git config set format.coverletter true && + git config set format.commitlistformat "log:[%(count)/%(total)] %s (%an)" && + git format-patch -o patches HEAD~2 && + grep -E "^[[[:digit:]]+/[[:digit:]]+] .* \(A U Thor\)" patches/0000-cover-letter.patch >result && + test_line_count = 2 result +' + +test_expect_success 'cover letter config with count and author' ' + test_when_finished "rm -rf patches result" && + test_when_finished "git config unset format.coverletter" && + test_when_finished "git config unset format.commitlistformat" && + git config set format.coverletter true && + git config set format.commitlistformat "log:[%(count)/%(total)] (%an)" && + git format-patch -o patches HEAD~2 && + grep -E "^[[[:digit:]]+/[[:digit:]]+] \(A U Thor\)" patches/0000-cover-letter.patch >result && + test_line_count = 2 result +' + +test_expect_success 'cover letter config commitlistformat set to modern' ' + test_when_finished "rm -rf patches result" && + test_when_finished "git config unset format.coverletter" && + test_when_finished "git config unset format.commitlistformat" && + git config set format.coverletter true && + git config set format.commitlistformat modern && + git format-patch -o patches HEAD~2 && + grep -E "^[[[:digit:]]+/[[:digit:]]+] .*$" patches/0000-cover-letter.patch >result && + test_line_count = 2 result +' + +test_expect_success 'cover letter config commitlistformat set to shortlog' ' + test_when_finished "rm -rf patches result" && + test_when_finished "git config unset format.coverletter" && + test_when_finished "git config unset format.commitlistformat" && + git config set format.coverletter true && + git config set format.commitlistformat shortlog && + git format-patch -o patches HEAD~2 && + grep -E "^A U Thor \([[:digit:]]+\)" patches/0000-cover-letter.patch >result && + test_line_count = 1 result +' + +test_expect_success 'cover letter config commitlistformat not set' ' + test_when_finished "rm -rf patches result" && + test_when_finished "git config unset format.coverletter" && + git config set format.coverletter true && + git format-patch -o patches HEAD~2 && + grep -E "^A U Thor \([[:digit:]]+\)" patches/0000-cover-letter.patch >result && + test_line_count = 1 result +' + test_expect_success 'reroll count' ' rm -fr patches && git format-patch -o patches --cover-letter --reroll-count 4 main..side >list && @@ -1285,7 +1410,9 @@ test_expect_success 'format-patch wraps extremely long from-header (rfc2047)' ' check_author "Foö Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar" ' -cat >expect <<'EOF' +# NOTE: do not quote this heredoc, Dash 0.5.13 has a bug with heredocs +# that contain multibyte chars. +cat >expect <<EOF From: Foö Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar <author@example.com> @@ -1300,7 +1427,9 @@ test_expect_success 'format-patch wraps extremely long from-header (non-ASCII wi test_cmp expect actual ' -cat >expect <<'EOF' +# NOTE: do not quote this heredoc, Dash 0.5.13 has a bug with heredocs +# that contain multibyte chars. +cat >expect <<EOF Subject: [PATCH] Foö EOF test_expect_success 'subject lines are unencoded with --no-encode-email-headers' ' @@ -1312,7 +1441,9 @@ test_expect_success 'subject lines are unencoded with --no-encode-email-headers' test_cmp expect actual ' -cat >expect <<'EOF' +# NOTE: do not quote this heredoc, Dash 0.5.13 has a bug with heredocs +# that contain multibyte chars. +cat >expect <<EOF Subject: [PATCH] Foö EOF test_expect_success 'subject lines are unencoded with format.encodeEmailHeaders=false' ' @@ -1531,7 +1662,9 @@ test_expect_success 'in-body headers trigger content encoding' ' test_env GIT_AUTHOR_NAME="éxötìc" test_commit exotic && test_when_finished "git reset --hard HEAD^" && git format-patch -1 --stdout --from >patch && - cat >expect <<-\EOF && + # NOTE: do not quote this heredoc, Dash 0.5.13 has a bug with heredocs + # that contain multibyte chars. + cat >expect <<-EOF && From: C O Mitter <committer@example.com> Content-Type: text/plain; charset=UTF-8 diff --git a/t/t4074-diff-shifted-matched-group.sh b/t/t4074-diff-shifted-matched-group.sh new file mode 100755 index 0000000000..d77fa3b79d --- /dev/null +++ b/t/t4074-diff-shifted-matched-group.sh @@ -0,0 +1,164 @@ +#!/bin/sh + +test_description='shifted diff groups re-diffing during histogram diff' + +. ./test-lib.sh + +test_expect_success 'shifted/merged diff group should re-diff to minimize patch' ' + test_write_lines A x A A A x A A A >file1 && + test_write_lines A x A Z A x A A A >file2 && + + file1_h=$(git rev-parse --short $(git hash-object file1)) && + file2_h=$(git rev-parse --short $(git hash-object file2)) && + + cat >expect <<-EOF && + diff --git a/file1 b/file2 + index $file1_h..$file2_h 100644 + --- a/file1 + +++ b/file2 + @@ -1,7 +1,7 @@ + A + x + A + -A + +Z + A + x + A + EOF + + test_expect_code 1 git diff --no-index --histogram file1 file2 >output && + test_cmp expect output +' + +test_expect_success 'merged diff group with no shift' ' + test_write_lines A Z B x >file1 && + test_write_lines C D x Z E x >file2 && + + file1_h=$(git rev-parse --short $(git hash-object file1)) && + file2_h=$(git rev-parse --short $(git hash-object file2)) && + + cat >expect <<-EOF && + diff --git a/file1 b/file2 + index $file1_h..$file2_h 100644 + --- a/file1 + +++ b/file2 + @@ -1,4 +1,6 @@ + -A + +C + +D + +x + Z + -B + +E + x + EOF + + test_expect_code 1 git diff --no-index --histogram file1 file2 >output && + test_cmp expect output +' + +test_expect_success 're-diff should preserve diff flags' ' + test_write_lines a b c a b c >file1 && + test_write_lines x " b" z a b c >file2 && + + file1_h=$(git rev-parse --short $(git hash-object file1)) && + file2_h=$(git rev-parse --short $(git hash-object file2)) && + + cat >expect <<-EOF && + diff --git a/file1 b/file2 + index $file1_h..$file2_h 100644 + --- a/file1 + +++ b/file2 + @@ -1,6 +1,6 @@ + -a + -b + -c + +x + + b + +z + a + b + c + EOF + + test_expect_code 1 git diff --no-index --histogram file1 file2 >output && + test_cmp expect output && + + cat >expect_iwhite <<-EOF && + diff --git a/file1 b/file2 + index $file1_h..$file2_h 100644 + --- a/file1 + +++ b/file2 + @@ -1,6 +1,6 @@ + -a + +x + b + -c + +z + a + b + c + EOF + + test_expect_code 1 git diff --no-index --histogram --ignore-all-space file1 file2 >output_iwhite && + test_cmp expect_iwhite output_iwhite +' + +test_expect_success 'shifting on either side should trigger re-diff properly' ' + test_write_lines a b c a b c a b c >file1 && + test_write_lines a b c a1 a2 a3 b c1 a b c >file2 && + + file1_h=$(git rev-parse --short $(git hash-object file1)) && + file2_h=$(git rev-parse --short $(git hash-object file2)) && + + cat >expect1 <<-EOF && + diff --git a/file1 b/file2 + index $file1_h..$file2_h 100644 + --- a/file1 + +++ b/file2 + @@ -1,9 +1,11 @@ + a + b + c + -a + +a1 + +a2 + +a3 + b + -c + +c1 + a + b + c + EOF + + test_expect_code 1 git diff --no-index --histogram file1 file2 >output1 && + test_cmp expect1 output1 && + + cat >expect2 <<-EOF && + diff --git a/file2 b/file1 + index $file2_h..$file1_h 100644 + --- a/file2 + +++ b/file1 + @@ -1,11 +1,9 @@ + a + b + c + -a1 + -a2 + -a3 + +a + b + -c1 + +c + a + b + c + EOF + + test_expect_code 1 git diff --no-index --histogram file2 file1 >output2 && + test_cmp expect2 output2 +' + +test_done diff --git a/t/t4100-apply-stat.sh b/t/t4100-apply-stat.sh index a5664f3eb3..8393076469 100755 --- a/t/t4100-apply-stat.sh +++ b/t/t4100-apply-stat.sh @@ -48,7 +48,93 @@ test_expect_success 'applying a hunk header which overflows fails' ' +b EOF test_must_fail git apply patch 2>err && - echo "error: corrupt patch at line 4" >expect && + echo "error: corrupt patch at patch:4" >expect && + test_cmp expect err +' + +test_expect_success 'applying a hunk header which overflows from stdin fails' ' + cat >patch <<-\EOF && + diff -u a/file b/file + --- a/file + +++ b/file + @@ -98765432109876543210 +98765432109876543210 @@ + -a + +b + EOF + test_must_fail git apply <patch 2>err && + echo "error: corrupt patch at <stdin>:4" >expect && + test_cmp expect err +' + +test_expect_success 'applying multiple patches reports the corrupted input' ' + cat >good.patch <<-\EOF && + diff -u a/file b/file + --- a/file + +++ b/file + @@ -1 +1 @@ + -a + +b + EOF + cat >bad.patch <<-\EOF && + diff -u a/file b/file + --- a/file + +++ b/file + @@ -98765432109876543210 +98765432109876543210 @@ + -a + +b + EOF + test_must_fail git apply --stat --summary good.patch bad.patch 2>err && + echo "error: corrupt patch at bad.patch:4" >expect && + test_cmp expect err +' + +test_expect_success 'applying a patch without a header reports the input' ' + cat >fragment.patch <<-\EOF && + @@ -1 +1 @@ + -a + +b + EOF + test_must_fail git apply fragment.patch 2>err && + echo "error: patch fragment without header at fragment.patch:1: @@ -1 +1 @@" >expect && + test_cmp expect err +' + +test_expect_success 'applying a patch with a missing filename reports the input' ' + cat >missing.patch <<-\EOF && + diff --git a/f b/f + index 7898192..6178079 100644 + --- a/f + @@ -1 +1 @@ + -a + +b + EOF + test_must_fail git apply missing.patch 2>err && + echo "error: git diff header lacks filename information at missing.patch:4" >expect && + test_cmp expect err +' + +test_expect_success 'applying a patch with an invalid mode reports the input' ' + cat >mode.patch <<-\EOF && + diff --git a/f b/f + old mode 10x644 + EOF + test_must_fail git apply mode.patch 2>err && + cat >expect <<-\EOF && + error: invalid mode at mode.patch:2: 10x644 + + EOF + test_cmp expect err +' + +test_expect_success 'applying a patch with only garbage reports the input' ' + cat >garbage.patch <<-\EOF && + diff --git a/f b/f + --- a/f + +++ b/f + this is garbage + EOF + test_must_fail git apply garbage.patch 2>err && + echo "error: patch with only garbage at garbage.patch:4" >expect && test_cmp expect err ' test_done diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh index 8e302a5a57..f2d41e06bc 100755 --- a/t/t4103-apply-binary.sh +++ b/t/t4103-apply-binary.sh @@ -179,6 +179,24 @@ test_expect_success PERL_TEST_HELPERS 'reject truncated binary diff' ' " <patch >patch.trunc && do_reset && - test_must_fail git apply patch.trunc + test_must_fail git apply patch.trunc 2>err && + line=$(awk "END { print NR + 1 }" patch.trunc) && + grep "error: corrupt binary patch at patch.trunc:$line: " err +' + +test_expect_success 'reject unrecognized binary diff' ' + cat >patch.bad <<-\EOF && + diff --git a/f b/f + new file mode 100644 + index 0000000..7898192 + GIT binary patch + bogus + EOF + test_must_fail git apply patch.bad 2>err && + cat >expect <<-\EOF && + error: unrecognized binary patch at patch.bad:4 + error: No valid patches in input (allow with "--allow-empty") + EOF + test_cmp expect err ' test_done diff --git a/t/t4120-apply-popt.sh b/t/t4120-apply-popt.sh index 697e86c0ff..c960fdf622 100755 --- a/t/t4120-apply-popt.sh +++ b/t/t4120-apply-popt.sh @@ -23,6 +23,47 @@ test_expect_success setup ' rmdir süb ' +test_expect_success 'git apply -p 1 patch' ' + cat >patch <<-\EOF && + From 90ad11d5b2d437e82d4d992f72fb44c2227798b5 Mon Sep 17 00:00:00 2001 + From: Mroik <mroik@delayed.space> + Date: Mon, 9 Mar 2026 23:25:00 +0100 + Subject: [PATCH] Test + + --- + t/test/test | 0 + 1 file changed, 0 insertions(+), 0 deletions(-) + create mode 100644 t/test/test + + diff --git a/t/test/test b/t/test/test + new file mode 100644 + index 0000000000..e69de29bb2 + -- + 2.53.0.851.ga537e3e6e9 + EOF + test_when_finished "rm -rf t" && + git apply -p 1 patch && + test_path_is_dir t +' + +test_expect_success 'apply fails due to non-num -p' ' + test_when_finished "rm -rf t test err" && + test_must_fail git apply -p malformed patch 2>err && + test_grep "option -p expects a non-negative integer" err +' + +test_expect_success 'apply fails due to trailing non-digit in -p' ' + test_when_finished "rm -rf t test err" && + test_must_fail git apply -p 2q patch 2>err && + test_grep "option -p expects a non-negative integer" err +' + +test_expect_success 'apply fails due to negative number in -p' ' + test_when_finished "rm -rf t test err patch" && + test_must_fail git apply -p -1 patch 2> err && + test_grep "option -p expects a non-negative integer" err +' + test_expect_success 'apply git diff with -p2' ' cp file1.saved file1 && git apply -p2 patch.file diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh index 29ea7d4268..205d86d05e 100755 --- a/t/t4124-apply-ws-rule.sh +++ b/t/t4124-apply-ws-rule.sh @@ -561,6 +561,22 @@ test_expect_success 'check incomplete lines (setup)' ' git config core.whitespace incomplete-line ' +test_expect_success 'no incomplete context line (not an error)' ' + test_when_finished "rm -f sample*-i patch patch-new target" && + test_write_lines 1 2 3 "" 4 5 >sample-i && + test_write_lines 1 2 3 "" 0 5 >sample2-i && + cat sample-i >target && + git add target && + cat sample2-i >target && + git diff-files -p target >patch && + sed -e "s/^ $//" <patch >patch-new && + + cat sample-i >target && + git apply --whitespace=fix <patch-new 2>error && + test_cmp sample2-i target && + test_must_be_empty error +' + test_expect_success 'incomplete context line (not an error)' ' (test_write_lines 1 2 3 4 5 && printf 6) >sample-i && (test_write_lines 1 2 3 0 5 && printf 6) >sample2-i && diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh index 204325f4d5..1717f407c8 100755 --- a/t/t4200-rerere.sh +++ b/t/t4200-rerere.sh @@ -72,7 +72,7 @@ test_expect_success 'nothing recorded without rerere' ' rm -rf .git/rr-cache && git config rerere.enabled false && test_must_fail git merge first && - ! test -d .git/rr-cache + test_path_is_missing .git/rr-cache ' test_expect_success 'activate rerere, old style (conflicting merge)' ' @@ -84,8 +84,8 @@ test_expect_success 'activate rerere, old style (conflicting merge)' ' sha1=$(sed "s/ .*//" .git/MERGE_RR) && rr=.git/rr-cache/$sha1 && grep "^=======\$" $rr/preimage && - ! test -f $rr/postimage && - ! test -f $rr/thisimage + test_path_is_missing $rr/postimage && + test_path_is_missing $rr/thisimage ' test_expect_success 'rerere.enabled works, too' ' @@ -110,8 +110,8 @@ test_expect_success 'set up rr-cache' ' test_expect_success 'rr-cache looks sane' ' # no postimage or thisimage yet - ! test -f $rr/postimage && - ! test -f $rr/thisimage && + test_path_is_missing $rr/postimage && + test_path_is_missing $rr/thisimage && # preimage has right number of lines cnt=$(sed -ne "/^<<<<<<</,/^>>>>>>>/p" $rr/preimage | wc -l) && @@ -167,7 +167,7 @@ test_expect_success 'first postimage wins' ' git show first:a1 | sed "s/To die: t/To die! T/" >expect && git commit -q -a -m "prefer first over second" && - test -f $rr/postimage && + test_path_is_file $rr/postimage && oldmtimepost=$(test-tool chmtime --get -60 $rr/postimage) && @@ -190,14 +190,14 @@ test_expect_success 'rerere clear' ' mv $rr/postimage .git/post-saved && echo "$sha1 a1" | tr "\012" "\000" >.git/MERGE_RR && git rerere clear && - ! test -d $rr + test_path_is_missing $rr ' test_expect_success 'leftover directory' ' git reset --hard && mkdir -p $rr && test_must_fail git merge first && - test -f $rr/preimage + test_path_is_file $rr/preimage ' test_expect_success 'missing preimage' ' @@ -205,7 +205,7 @@ test_expect_success 'missing preimage' ' mkdir -p $rr && cp .git/post-saved $rr/postimage && test_must_fail git merge first && - test -f $rr/preimage + test_path_is_file $rr/preimage ' test_expect_success 'set up for garbage collection tests' ' @@ -230,16 +230,16 @@ test_expect_success 'set up for garbage collection tests' ' test_expect_success 'gc preserves young or recently used records' ' git rerere gc && - test -f $rr/preimage && - test -f $rr2/preimage + test_path_is_file $rr/preimage && + test_path_is_file $rr2/preimage ' test_expect_success 'old records rest in peace' ' test-tool chmtime =$just_over_60_days_ago $rr/postimage && test-tool chmtime =$just_over_15_days_ago $rr2/preimage && git rerere gc && - ! test -f $rr/preimage && - ! test -f $rr2/preimage + test_path_is_missing $rr/preimage && + test_path_is_missing $rr2/preimage ' rerere_gc_custom_expiry_test () { diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh index 5f23fc147b..9f41d56d9a 100755 --- a/t/t4201-shortlog.sh +++ b/t/t4201-shortlog.sh @@ -105,7 +105,9 @@ test_expect_success 'output from user-defined format is re-wrapped' ' ' test_expect_success !MINGW,ICONV 'shortlog wrapping' ' - cat >expect <<\EOF && + # NOTE: do not quote this heredoc, Dash 0.5.13 has a bug with heredocs + # that contain multibyte chars. + cat >expect <<EOF && A U Thor (5): Test This is a very, very long first line for the commit message to see if diff --git a/t/t4254-am-corrupt.sh b/t/t4254-am-corrupt.sh index ae0a56cf5e..96ddf3c53a 100755 --- a/t/t4254-am-corrupt.sh +++ b/t/t4254-am-corrupt.sh @@ -65,9 +65,8 @@ test_expect_success setup ' test_expect_success 'try to apply corrupted patch' ' test_when_finished "git am --abort" && test_must_fail git -c advice.amWorkDir=false -c advice.mergeConflict=false am bad-patch.diff 2>actual && - echo "error: git diff header lacks filename information (line 4)" >expected && test_path_is_file f && - test_cmp expected actual + test_grep "error: git diff header lacks filename information at .*rebase-apply/patch:4" actual ' test_expect_success "NUL in commit message's body" ' diff --git a/t/t5315-pack-objects-compression.sh b/t/t5315-pack-objects-compression.sh index 8bacd96275..d0feab17b4 100755 --- a/t/t5315-pack-objects-compression.sh +++ b/t/t5315-pack-objects-compression.sh @@ -10,7 +10,7 @@ test_expect_success setup ' # make sure it resulted in a loose object ob=$(sed -e "s/\(..\).*/\1/" object-name) && ject=$(sed -e "s/..\(.*\)/\1/" object-name) && - test -f .git/objects/$ob/$ject + test_path_is_file .git/objects/$ob/$ject ' while read expect config diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh index 98c6910963..1c40f904f8 100755 --- a/t/t5318-commit-graph.sh +++ b/t/t5318-commit-graph.sh @@ -417,6 +417,26 @@ test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT 'lower layers have overflow ch test_cmp full/.git/objects/info/commit-graph commit-graph-upgraded ' +test_expect_success TIME_IS_64BIT,TIME_T_IS_64BIT 'overflow chunk when replacing commit-graph' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + cat >commit <<-EOF && + tree $(test_oid empty_tree) + author Example <committer@example.com> 9223372036854775 +0000 + committer Example <committer@example.com> 9223372036854775 +0000 + + Weird commit date + EOF + commit_id=$(git hash-object -t commit -w commit) && + git reset --hard "$commit_id" && + git commit-graph write --reachable && + git commit-graph write --reachable --split=replace && + git log + ) +' + # the verify tests below expect the commit-graph to contain # exactly the commits reachable from the commits/8 branch. # If the file changes the set of commits in the list, then the diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh index a7c79225f6..58e0b685b1 100755 --- a/t/t5319-multi-pack-index.sh +++ b/t/t5319-multi-pack-index.sh @@ -21,7 +21,7 @@ midx_read_expect () { EXTRA_CHUNKS="$5" { cat <<-EOF && - header: 4d494458 1 $HASH_LEN $NUM_CHUNKS $NUM_PACKS + header: 4d494458 2 $HASH_LEN $NUM_CHUNKS $NUM_PACKS chunks: pack-names oid-fanout oid-lookup object-offsets$EXTRA_CHUNKS num_objects: $NUM_OBJECTS packs: @@ -512,12 +512,7 @@ test_expect_success 'verify invalid chunk offset' ' "improper chunk offset(s)" ' -test_expect_success 'verify packnames out of order' ' - corrupt_midx_and_verify $MIDX_BYTE_PACKNAME_ORDER "z" $objdir \ - "pack names out of order" -' - -test_expect_success 'verify packnames out of order' ' +test_expect_success 'verify missing pack' ' corrupt_midx_and_verify $MIDX_BYTE_PACKNAME_ORDER "a" $objdir \ "failed to load pack" ' @@ -578,6 +573,15 @@ test_expect_success 'verify incorrect checksum' ' $objdir "incorrect checksum" ' +test_expect_success 'setup for v1-specific fsck tests' ' + git -c midx.version=1 multi-pack-index write +' + +test_expect_success 'verify packnames out of order (v1)' ' + corrupt_midx_and_verify $MIDX_BYTE_PACKNAME_ORDER "z" $objdir \ + "pack names out of order" +' + test_expect_success 'repack progress off for redirected stderr' ' GIT_PROGRESS_DELAY=0 git multi-pack-index --object-dir=$objdir repack 2>err && test_line_count = 0 err diff --git a/t/t5331-pack-objects-stdin.sh b/t/t5331-pack-objects-stdin.sh index 7eb79bc2cd..c74b5861af 100755 --- a/t/t5331-pack-objects-stdin.sh +++ b/t/t5331-pack-objects-stdin.sh @@ -415,4 +415,109 @@ test_expect_success '--stdin-packs=follow tolerates missing commits' ' stdin_packs__follow_with_only HEAD HEAD^{tree} ' +test_expect_success '--stdin-packs=follow with open-excluded packs' ' + test_when_finished "rm -fr repo" && + + git init repo && + ( + cd repo && + git config set maintenance.auto false && + + git branch -M main && + + # Create the following commit structure: + # + # A <-- B <-- D (main) + # ^ + # \ + # C (other) + test_commit A && + test_commit B && + git checkout -B other && + test_commit C && + git checkout main && + test_commit D && + + A="$(echo A | git pack-objects --revs $packdir/pack)" && + B="$(echo A..B | git pack-objects --revs $packdir/pack)" && + C="$(echo B..C | git pack-objects --revs $packdir/pack)" && + D="$(echo B..D | git pack-objects --revs $packdir/pack)" && + + C_ONLY="$(git rev-parse other | git pack-objects $packdir/pack)" && + + git prune-packed && + + # Create a pack using --stdin-packs=follow where: + # + # - pack D is included, + # - pack C_ONLY is excluded, but open, + # - pack B is excluded, but closed, and + # - packs A and C are unknown + # + # The resulting pack should therefore contain: + # + # - objects from the included pack D, + # - A.t (rescued via D^{tree}), and + # - C^{tree} and C.t (rescued via pack C_ONLY) + # + # , but should omit: + # + # - C (excluded via C_ONLY), + # - objects from pack B (trivially excluded-closed) + # - A and A^{tree} (ancestors of B) + P=$(git pack-objects --stdin-packs=follow $packdir/pack <<-EOF + pack-$D.pack + !pack-$C_ONLY.pack + ^pack-$B.pack + EOF + ) && + + { + objects_in_packs $D && + git rev-parse A:A.t "C^{tree}" C:C.t + } >expect.raw && + sort expect.raw >expect && + + objects_in_packs $P >actual && + test_cmp expect actual + ) +' + +test_expect_success '--stdin-packs with !-delimited pack without follow' ' + test_when_finished "rm -fr repo" && + + git init repo && + ( + test_commit A && + test_commit B && + test_commit C && + + A="$(echo A | git pack-objects --revs $packdir/pack)" && + B="$(echo A..B | git pack-objects --revs $packdir/pack)" && + C="$(echo B..C | git pack-objects --revs $packdir/pack)" && + + cat >in <<-EOF && + !pack-$A.pack + pack-$B.pack + pack-$C.pack + EOF + + # Without --stdin-packs=follow, we treat the first + # line of input as a literal packfile name, and thus + # expect pack-objects to complain of a missing pack + test_must_fail git pack-objects --stdin-packs --stdout \ + >/dev/null <in 2>err && + test_grep "could not find pack .!pack-$A.pack." err && + + # With --stdin-packs=follow, we treat the second line + # of input as indicating pack-$A.pack is an excluded + # open pack, and thus expect pack-objects to succeed + P=$(git pack-objects --stdin-packs=follow $packdir/pack <in) && + + objects_in_packs $B $C >expect && + objects_in_packs $P >actual && + test_cmp expect actual + ) +' + test_done diff --git a/t/t5335-compact-multi-pack-index.sh b/t/t5335-compact-multi-pack-index.sh new file mode 100755 index 0000000000..40f3844282 --- /dev/null +++ b/t/t5335-compact-multi-pack-index.sh @@ -0,0 +1,293 @@ +#!/bin/sh + +test_description='multi-pack-index compaction' + +. ./test-lib.sh + +GIT_TEST_MULTI_PACK_INDEX=0 +GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=0 +GIT_TEST_MULTI_PACK_INDEX_WRITE_INCREMENTAL=0 + +objdir=.git/objects +packdir=$objdir/pack +midxdir=$packdir/multi-pack-index.d +midx_chain=$midxdir/multi-pack-index-chain + +nth_line() { + local n="$1" + shift + awk "NR==$n" "$@" +} + +write_packs () { + for c in "$@" + do + test_commit "$c" && + + git pack-objects --all --unpacked $packdir/pack-$c && + git prune-packed && + + git multi-pack-index write --incremental --bitmap || return 1 + done +} + +test_midx_layer_packs () { + local checksum="$1" && + shift && + + test-tool read-midx $objdir "$checksum" >out && + + printf "%s\n" "$@" >expect && + # NOTE: do *not* pipe through sort here, we want to ensure the + # order of packs is preserved during compaction. + grep "^pack-" out | cut -d"-" -f2 >actual && + + test_cmp expect actual +} + +test_midx_layer_object_uniqueness () { + : >objs.all + while read layer + do + test-tool read-midx --show-objects $objdir "$layer" >out && + grep "\.pack$" out | cut -d" " -f1 | sort >objs.layer && + test_stdout_line_count = 0 comm -12 objs.all objs.layer && + cat objs.all objs.layer | sort >objs.tmp && + mv objs.tmp objs.all || return 1 + done <$midx_chain +} + +test_expect_success 'MIDX compaction with lex-ordered pack names' ' + git init midx-compact-lex-order && + ( + cd midx-compact-lex-order && + + git config maintenance.auto false && + + write_packs A B C D E && + test_line_count = 5 $midx_chain && + + git multi-pack-index compact --incremental --bitmap \ + "$(nth_line 2 "$midx_chain")" \ + "$(nth_line 4 "$midx_chain")" && + test_line_count = 3 $midx_chain && + + test_midx_layer_packs "$(nth_line 1 "$midx_chain")" A && + test_midx_layer_packs "$(nth_line 2 "$midx_chain")" B C D && + test_midx_layer_packs "$(nth_line 3 "$midx_chain")" E && + + test_midx_layer_object_uniqueness + ) +' + +test_expect_success 'MIDX compaction with non-lex-ordered pack names' ' + git init midx-compact-non-lex-order && + ( + cd midx-compact-non-lex-order && + + git config maintenance.auto false && + + write_packs D C A B E && + test_line_count = 5 $midx_chain && + + git multi-pack-index compact --incremental --bitmap \ + "$(nth_line 2 "$midx_chain")" \ + "$(nth_line 4 "$midx_chain")" && + test_line_count = 3 $midx_chain && + + test_midx_layer_packs "$(nth_line 1 "$midx_chain")" D && + test_midx_layer_packs "$(nth_line 2 "$midx_chain")" C A B && + test_midx_layer_packs "$(nth_line 3 "$midx_chain")" E && + + test_midx_layer_object_uniqueness + ) +' + +test_expect_success 'setup for bogus MIDX compaction scenarios' ' + git init midx-compact-bogus && + ( + cd midx-compact-bogus && + + git config maintenance.auto false && + + write_packs A B C + ) +' + +test_expect_success 'MIDX compaction with missing endpoints' ' + ( + cd midx-compact-bogus && + + test_must_fail git multi-pack-index compact --incremental \ + "<missing>" "<missing>" 2>err && + test_grep "could not find MIDX: <missing>" err && + + test_must_fail git multi-pack-index compact --incremental \ + "<missing>" "$(nth_line 2 "$midx_chain")" 2>err && + test_grep "could not find MIDX: <missing>" err && + + test_must_fail git multi-pack-index compact --incremental \ + "$(nth_line 2 "$midx_chain")" "<missing>" 2>err && + test_grep "could not find MIDX: <missing>" err + ) +' + +test_expect_success 'MIDX compaction with reversed endpoints' ' + ( + cd midx-compact-bogus && + + from="$(nth_line 3 "$midx_chain")" && + to="$(nth_line 1 "$midx_chain")" && + + test_must_fail git multi-pack-index compact --incremental \ + "$from" "$to" 2>err && + + test_grep "MIDX $from must be an ancestor of $to" err + ) +' + +test_expect_success 'MIDX compaction with identical endpoints' ' + ( + cd midx-compact-bogus && + + from="$(nth_line 3 "$midx_chain")" && + to="$(nth_line 3 "$midx_chain")" && + + test_must_fail git multi-pack-index compact --incremental \ + "$from" "$to" 2>err && + + test_grep "MIDX compaction endpoints must be unique" err + ) +' + +test_expect_success 'MIDX compaction with midx.version=1' ' + ( + cd midx-compact-bogus && + + test_must_fail git -c midx.version=1 multi-pack-index compact \ + "$(nth_line 1 "$midx_chain")" \ + "$(nth_line 2 "$midx_chain")" 2>err && + + test_grep "fatal: cannot perform MIDX compaction with v1 format" err + ) +' + +midx_objs_by_pack () { + awk '/\.pack$/ { split($3, a, "-"); print a[2], $1 }' | sort +} + +tag_objs_from_pack () { + objs="$(git rev-list --objects --no-object-names "$2")" && + printf "$1 %s\n" $objs | sort +} + +test_expect_success 'MIDX compaction preserves pack object selection' ' + git init midx-compact-preserve-selection && + ( + cd midx-compact-preserve-selection && + + git config maintenance.auto false && + + test_commit A && + test_commit B && + + # Create two packs, one containing just the objects from + # A, and another containing all objects from the + # repository. + p1="$(echo A | git pack-objects --revs --delta-base-offset \ + $packdir/pack-1)" && + p0="$(echo B | git pack-objects --revs --delta-base-offset \ + $packdir/pack-0)" && + + echo "pack-1-$p1.idx" | git multi-pack-index write \ + --incremental --bitmap --stdin-packs && + echo "pack-0-$p0.idx" | git multi-pack-index write \ + --incremental --bitmap --stdin-packs && + + write_packs C && + + git multi-pack-index compact --incremental --bitmap \ + "$(nth_line 1 "$midx_chain")" \ + "$(nth_line 2 "$midx_chain")" && + + + test-tool read-midx --show-objects $objdir \ + "$(nth_line 1 "$midx_chain")" >AB.info && + test-tool read-midx --show-objects $objdir \ + "$(nth_line 2 "$midx_chain")" >C.info && + + midx_objs_by_pack <AB.info >AB.actual && + midx_objs_by_pack <C.info >C.actual && + + { + tag_objs_from_pack 1 A && + tag_objs_from_pack 0 A..B + } | sort >AB.expect && + tag_objs_from_pack C B..C >C.expect && + + test_cmp AB.expect AB.actual && + test_cmp C.expect C.actual + ) +' + +test_expect_success 'MIDX compaction with bitmaps' ' + git init midx-compact-with-bitmaps && + ( + cd midx-compact-with-bitmaps && + + git config maintenance.auto false && + + write_packs foo bar baz quux woot && + + test-tool read-midx --bitmap $objdir >bitmap.expect && + git multi-pack-index compact --incremental --bitmap \ + "$(nth_line 2 "$midx_chain")" \ + "$(nth_line 4 "$midx_chain")" && + test-tool read-midx --bitmap $objdir >bitmap.actual && + + test_cmp bitmap.expect bitmap.actual && + + true + ) +' + +test_expect_success 'MIDX compaction with bitmaps (non-trivial)' ' + git init midx-compact-with-bitmaps-non-trivial && + ( + cd midx-compact-with-bitmaps-non-trivial && + + git config maintenance.auto false && + + git branch -m main && + + # D(4) + # / + # A(1) --- B(2) --- C(3) --- G(7) + # \ + # E(5) --- F(6) + write_packs A B C && + git checkout -b side && + write_packs D && + git checkout -b other B && + write_packs E F && + git checkout main && + write_packs G && + + # Compact layers 2-4, leaving us with: + # + # [A, [B, C, D], E, F, G] + git multi-pack-index compact --incremental --bitmap \ + "$(nth_line 2 "$midx_chain")" \ + "$(nth_line 4 "$midx_chain")" && + + # Then compact the top two layers, condensing the above + # such that the new 4th layer contains F and G. + # + # [A, [B, C, D], E, [F, G]] + git multi-pack-index compact --incremental --bitmap \ + "$(nth_line 4 "$midx_chain")" \ + "$(nth_line 5 "$midx_chain")" + ) +' + +test_done diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 5dcb4b51a4..6fe21e2b3a 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -469,12 +469,17 @@ test_expect_success 'fetch --atomic executes a single reference transaction only head_oid=$(git rev-parse HEAD) && cat >expected <<-EOF && + preparing + $ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-1 + $ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-2 prepared $ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-1 $ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-2 committed $ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-1 $ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-2 + preparing + $ZERO_OID ref:refs/remotes/origin/main refs/remotes/origin/HEAD EOF rm -f atomic/actual && @@ -497,7 +502,7 @@ test_expect_success 'fetch --atomic aborts all reference updates if hook aborts' head_oid=$(git rev-parse HEAD) && cat >expected <<-EOF && - prepared + preparing $ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-abort-1 $ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-abort-2 $ZERO_OID $head_oid refs/remotes/origin/atomic-hooks-abort-3 diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh index 5e566205ba..1242ee9185 100755 --- a/t/t5526-fetch-submodules.sh +++ b/t/t5526-fetch-submodules.sh @@ -834,11 +834,18 @@ test_expect_success "fetch new submodule commits on-demand outside standard refs git commit -m "updated submodules outside of refs/heads" && E=$(git rev-parse HEAD) && git update-ref refs/changes/3 $E && + FETCH_TRACE="$(pwd)/trace.out" && + test_when_finished "rm -f \"$FETCH_TRACE\"" && ( cd downstream && - git fetch --recurse-submodules origin refs/changes/3:refs/heads/my_branch && + GIT_TRACE="$FETCH_TRACE" git fetch --recurse-submodules origin \ + refs/changes/3:refs/heads/my_branch && git -C submodule cat-file -t $C && git -C sub1 cat-file -t $D && + test_grep "trace: built-in: git submodule--helper get-default-remote sub1" \ + "$FETCH_TRACE" && + test_grep "trace: built-in: git fetch .* --submodule-prefix=sub1/ origin" \ + "$FETCH_TRACE" && git checkout --recurse-submodules FETCH_HEAD ) ' @@ -929,6 +936,68 @@ test_expect_success 'fetch new submodule commit intermittently referenced by sup ) ' +test_expect_success 'fetch new submodule commits on-demand outside standard refspec with custom remote name' ' + # depends on the previous test for setup + + # Rename the remote in sub1 from "origin" to "custom_remote" + git -C downstream/sub1 remote rename origin custom_remote && + + # Create new commits in the original submodules + C=$(git -C submodule commit-tree \ + -m "change outside refs/heads for custom remote" HEAD^{tree}) && + git -C submodule update-ref refs/changes/custom1 $C && + git update-index --cacheinfo 160000 $C submodule && + test_tick && + + D=$(git -C sub1 commit-tree \ + -m "change outside refs/heads for custom remote" HEAD^{tree}) && + git -C sub1 update-ref refs/changes/custom2 $D && + git update-index --cacheinfo 160000 $D sub1 && + + git commit \ + -m "updated submodules outside of refs/heads for custom remote" && + E=$(git rev-parse HEAD) && + git update-ref refs/changes/custom3 $E && + FETCH_TRACE="$(pwd)/trace.out" && + test_when_finished "rm -f \"$FETCH_TRACE\"" && + ( + cd downstream && + GIT_TRACE="$FETCH_TRACE" git fetch --recurse-submodules origin \ + refs/changes/custom3:refs/heads/my_other_branch && + git -C submodule cat-file -t $C && + git -C sub1 cat-file -t $D && + test_grep "trace: built-in: git submodule--helper get-default-remote sub1" \ + "$FETCH_TRACE" && + test_grep "trace: built-in: git fetch .* --submodule-prefix=sub1/ custom_remote $D" \ + "$FETCH_TRACE" && + git checkout --recurse-submodules FETCH_HEAD + ) +' + +test_expect_success 'fetch new submodule commit on-demand in FETCH_HEAD from custom remote' ' + # depends on the previous test for setup + + C=$(git -C submodule commit-tree -m "another change outside refs/heads for custom remote" HEAD^{tree}) && + git -C submodule update-ref refs/changes/custom4 $C && + git update-index --cacheinfo 160000 $C submodule && + test_tick && + + D=$(git -C sub1 commit-tree -m "another change outside refs/heads for custom remote" HEAD^{tree}) && + git -C sub1 update-ref refs/changes/custom5 $D && + git update-index --cacheinfo 160000 $D sub1 && + + git commit -m "updated submodules outside of refs/heads" && + E=$(git rev-parse HEAD) && + git update-ref refs/changes/custom6 $E && + ( + cd downstream && + git fetch --recurse-submodules origin refs/changes/custom6 && + git -C submodule cat-file -t $C && + git -C sub1 cat-file -t $D && + git checkout --recurse-submodules FETCH_HEAD + ) +' + add_commit_push () { dir="$1" && msg="$2" && diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh index 73cf531580..a26b6c2844 100755 --- a/t/t5551-http-fetch-smart.sh +++ b/t/t5551-http-fetch-smart.sh @@ -782,4 +782,11 @@ test_expect_success 'tag following always works over v0 http' ' test_cmp expect actual ' +test_expect_success 'ls-remote outside repo does not segfault with fetch refspec' ' + nongit git \ + -c remote.origin.url="$HTTPD_URL/smart/repo.git" \ + -c remote.origin.fetch=anything \ + ls-remote origin +' + test_done diff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh index 45f384dd32..42d14328b6 100755 --- a/t/t5572-pull-submodule.sh +++ b/t/t5572-pull-submodule.sh @@ -257,7 +257,26 @@ test_expect_success 'fetch submodule remote of different name from superproject' git -C a-submodule reset --hard HEAD^^ && git -C child pull --no-recurse-submodules && - git -C child submodule update + git -C child submodule update && + test_path_is_file child/a-submodule/moreecho.t +' + +test_expect_success 'fetch non-origin submodule remote named different from superproject' ' + git -C child/a-submodule remote rename origin o2 && + + # Create commit that is unreachable from current master branch + # newmain is already reset in the previous test + test_commit -C a-submodule echo_o2 && + test_commit -C a-submodule moreecho_o2 && + subc=$(git -C a-submodule rev-parse --short HEAD) && + + git -C parent/a-submodule fetch && + git -C parent/a-submodule checkout "$subc" && + git -C parent commit -m "update submodule o2" a-submodule && + git -C a-submodule reset --hard HEAD^^ && + + git -C child pull --recurse-submodules && + test_path_is_file child/a-submodule/moreecho_o2.t ' test_done diff --git a/t/t5584-http-429-retry.sh b/t/t5584-http-429-retry.sh new file mode 100755 index 0000000000..a22007b2cf --- /dev/null +++ b/t/t5584-http-429-retry.sh @@ -0,0 +1,266 @@ +#!/bin/sh + +test_description='test HTTP 429 Too Many Requests retry logic' + +. ./test-lib.sh + +. "$TEST_DIRECTORY"/lib-httpd.sh + +start_httpd + +test_expect_success 'setup test repository' ' + test_commit initial && + git clone --bare . "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" config http.receivepack true +' + +# This test suite uses a special HTTP 429 endpoint at /http_429/ that simulates +# rate limiting. The endpoint format is: +# /http_429/<test-context>/<retry-after-value>/<repo-path> +# The http-429.sh script (in t/lib-httpd) returns a 429 response with the +# specified Retry-After header on the first request for each test context, +# then forwards subsequent requests to git-http-backend. Each test context +# is isolated, allowing multiple tests to run independently. + +test_expect_success 'HTTP 429 with retries disabled (maxRetries=0) fails immediately' ' + # Set maxRetries to 0 (disabled) + test_config http.maxRetries 0 && + test_config http.retryAfter 1 && + + # Should fail immediately without any retry attempt + test_must_fail git ls-remote "$HTTPD_URL/http_429/retries-disabled/1/repo.git" 2>err && + + # Verify no retry happened (no "waiting" message in stderr) + test_grep ! -i "waiting.*retry" err +' + +test_expect_success 'HTTP 429 permanent should fail after max retries' ' + # Enable retries with a limit + test_config http.maxRetries 2 && + + # Git should retry but eventually fail when 429 persists + test_must_fail git ls-remote "$HTTPD_URL/http_429/permanent-fail/permanent/repo.git" 2>err +' + +test_expect_success 'HTTP 429 with Retry-After is retried and succeeds' ' + # Enable retries + test_config http.maxRetries 3 && + + # Git should retry after receiving 429 and eventually succeed + git ls-remote "$HTTPD_URL/http_429/retry-succeeds/1/repo.git" >output 2>err && + test_grep "refs/heads/" output +' + +test_expect_success 'HTTP 429 without Retry-After uses configured default' ' + # Enable retries and configure default delay + test_config http.maxRetries 3 && + test_config http.retryAfter 1 && + + # Git should retry using configured default and succeed + git ls-remote "$HTTPD_URL/http_429/no-retry-after-header/none/repo.git" >output 2>err && + test_grep "refs/heads/" output +' + +test_expect_success 'HTTP 429 retry delays are respected' ' + # Enable retries + test_config http.maxRetries 3 && + + # Time the operation - it should take at least 2 seconds due to retry delay + start=$(test-tool date getnanos) && + git ls-remote "$HTTPD_URL/http_429/retry-delays-respected/2/repo.git" >output 2>err && + duration=$(test-tool date getnanos $start) && + + # Verify it took at least 2 seconds (allowing some tolerance) + duration_int=${duration%.*} && + test "$duration_int" -ge 1 && + test_grep "refs/heads/" output +' + +test_expect_success 'HTTP 429 fails immediately if Retry-After exceeds http.maxRetryTime' ' + # Configure max retry time to 3 seconds (much less than requested 100) + test_config http.maxRetries 3 && + test_config http.maxRetryTime 3 && + + # Should fail immediately without waiting + start=$(test-tool date getnanos) && + test_must_fail git ls-remote "$HTTPD_URL/http_429/retry-after-exceeds-max-time/100/repo.git" 2>err && + duration=$(test-tool date getnanos $start) && + + # Should fail quickly (no 100 second wait) + duration_int=${duration%.*} && + test "$duration_int" -lt 99 && + test_grep "greater than http.maxRetryTime" err +' + +test_expect_success 'HTTP 429 fails if configured http.retryAfter exceeds http.maxRetryTime' ' + # Test misconfiguration: retryAfter > maxRetryTime + # Configure retryAfter larger than maxRetryTime + test_config http.maxRetries 3 && + test_config http.retryAfter 100 && + test_config http.maxRetryTime 5 && + + # Should fail immediately with configuration error + start=$(test-tool date getnanos) && + test_must_fail git ls-remote "$HTTPD_URL/http_429/config-retry-after-exceeds-max-time/none/repo.git" 2>err && + duration=$(test-tool date getnanos $start) && + + # Should fail quickly (no 100 second wait) + duration_int=${duration%.*} && + test "$duration_int" -lt 99 && + test_grep "configured http.retryAfter.*exceeds.*http.maxRetryTime" err +' + +test_expect_success 'HTTP 429 with Retry-After HTTP-date format' ' + # Test HTTP-date format (RFC 2822) in Retry-After header + raw=$(test-tool date timestamp now) && + now="${raw#* -> }" && + future_time=$((now + 2)) && + raw=$(test-tool date show:rfc2822 $future_time) && + future_date="${raw#* -> }" && + future_date_encoded=$(echo "$future_date" | sed "s/ /%20/g") && + + # Enable retries + test_config http.maxRetries 3 && + + # Git should parse the HTTP-date and retry after the delay + start=$(test-tool date getnanos) && + git ls-remote "$HTTPD_URL/http_429/http-date-format/$future_date_encoded/repo.git" >output 2>err && + duration=$(test-tool date getnanos $start) && + + # Should take at least 1 second (allowing tolerance for processing time) + duration_int=${duration%.*} && + test "$duration_int" -ge 1 && + test_grep "refs/heads/" output +' + +test_expect_success 'HTTP 429 with HTTP-date exceeding maxRetryTime fails immediately' ' + raw=$(test-tool date timestamp now) && + now="${raw#* -> }" && + future_time=$((now + 200)) && + raw=$(test-tool date show:rfc2822 $future_time) && + future_date="${raw#* -> }" && + future_date_encoded=$(echo "$future_date" | sed "s/ /%20/g") && + + # Configure max retry time much less than the 200 second delay + test_config http.maxRetries 3 && + test_config http.maxRetryTime 10 && + + # Should fail immediately without waiting 200 seconds + start=$(test-tool date getnanos) && + test_must_fail git ls-remote "$HTTPD_URL/http_429/http-date-exceeds-max-time/$future_date_encoded/repo.git" 2>err && + duration=$(test-tool date getnanos $start) && + + # Should fail quickly (not wait 200 seconds) + duration_int=${duration%.*} && + test "$duration_int" -lt 199 && + test_grep "http.maxRetryTime" err +' + +test_expect_success 'HTTP 429 with past HTTP-date should not wait' ' + raw=$(test-tool date timestamp now) && + now="${raw#* -> }" && + past_time=$((now - 10)) && + raw=$(test-tool date show:rfc2822 $past_time) && + past_date="${raw#* -> }" && + past_date_encoded=$(echo "$past_date" | sed "s/ /%20/g") && + + # Enable retries + test_config http.maxRetries 3 && + + # Git should retry immediately without waiting + start=$(test-tool date getnanos) && + git ls-remote "$HTTPD_URL/http_429/past-http-date/$past_date_encoded/repo.git" >output 2>err && + duration=$(test-tool date getnanos $start) && + + # Should complete quickly (no wait for a past-date Retry-After) + duration_int=${duration%.*} && + test "$duration_int" -lt 5 && + test_grep "refs/heads/" output +' + +test_expect_success 'HTTP 429 with invalid Retry-After format uses configured default' ' + # Configure default retry-after + test_config http.maxRetries 3 && + test_config http.retryAfter 1 && + + # Should use configured default (1 second) since header is invalid + start=$(test-tool date getnanos) && + git ls-remote "$HTTPD_URL/http_429/invalid-retry-after-format/invalid/repo.git" >output 2>err && + duration=$(test-tool date getnanos $start) && + + # Should take at least 1 second (the configured default) + duration_int=${duration%.*} && + test "$duration_int" -ge 1 && + test_grep "refs/heads/" output && + test_grep "waiting.*retry" err +' + +test_expect_success 'HTTP 429 will not be retried without config' ' + # Default config means http.maxRetries=0 (retries disabled) + # When 429 is received, it should fail immediately without retry + # Do NOT configure anything - use defaults (http.maxRetries defaults to 0) + + # Should fail immediately without retry + test_must_fail git ls-remote "$HTTPD_URL/http_429/no-retry-without-config/1/repo.git" 2>err && + + # Verify no retry happened (no "waiting" message) + test_grep ! -i "waiting.*retry" err && + + # Should get 429 error + test_grep "429" err +' + +test_expect_success 'GIT_HTTP_RETRY_AFTER overrides http.retryAfter config' ' + # Configure retryAfter to 10 seconds + test_config http.maxRetries 3 && + test_config http.retryAfter 10 && + + # Override with environment variable to 1 second + start=$(test-tool date getnanos) && + GIT_HTTP_RETRY_AFTER=1 git ls-remote "$HTTPD_URL/http_429/env-retry-after-override/none/repo.git" >output 2>err && + duration=$(test-tool date getnanos $start) && + + # Should use env var (1 second), not config (10 seconds) + duration_int=${duration%.*} && + test "$duration_int" -ge 1 && + test "$duration_int" -lt 5 && + test_grep "refs/heads/" output && + test_grep "waiting.*retry" err +' + +test_expect_success 'GIT_HTTP_MAX_RETRIES overrides http.maxRetries config' ' + # Configure maxRetries to 0 (disabled) + test_config http.maxRetries 0 && + test_config http.retryAfter 1 && + + # Override with environment variable to enable retries + GIT_HTTP_MAX_RETRIES=3 git ls-remote "$HTTPD_URL/http_429/env-max-retries-override/1/repo.git" >output 2>err && + + # Should retry (env var enables it despite config saying disabled) + test_grep "refs/heads/" output && + test_grep "waiting.*retry" err +' + +test_expect_success 'GIT_HTTP_MAX_RETRY_TIME overrides http.maxRetryTime config' ' + # Configure maxRetryTime to 100 seconds (would accept 50 second delay) + test_config http.maxRetries 3 && + test_config http.maxRetryTime 100 && + + # Override with environment variable to 10 seconds (should reject 50 second delay) + start=$(test-tool date getnanos) && + test_must_fail env GIT_HTTP_MAX_RETRY_TIME=10 \ + git ls-remote "$HTTPD_URL/http_429/env-max-retry-time-override/50/repo.git" 2>err && + duration=$(test-tool date getnanos $start) && + + # Should fail quickly (not wait 50 seconds) because env var limits to 10 + duration_int=${duration%.*} && + test "$duration_int" -lt 49 && + test_grep "greater than http.maxRetryTime" err +' + +test_expect_success 'verify normal repository access still works' ' + git ls-remote "$HTTPD_URL/smart/repo.git" >output && + test_grep "refs/heads/" output +' + +test_done diff --git a/t/t5620-backfill.sh b/t/t5620-backfill.sh index 58c81556e7..2c347a91fe 100755 --- a/t/t5620-backfill.sh +++ b/t/t5620-backfill.sh @@ -7,6 +7,14 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME . ./test-lib.sh +test_expect_success 'backfill rejects unexpected arguments' ' + test_must_fail git backfill unexpected-arg 2>err && + test_grep "ambiguous argument .*unexpected-arg" err && + + test_must_fail git backfill --all --unexpected-arg --first-parent 2>err && + test_grep "unrecognized argument: --unexpected-arg" err +' + # We create objects in the 'src' repo. test_expect_success 'setup repo for object creation' ' echo "{print \$1}" >print_1.awk && @@ -15,7 +23,7 @@ test_expect_success 'setup repo for object creation' ' git init src && mkdir -p src/a/b/c && - mkdir -p src/d/e && + mkdir -p src/d/f && for i in 1 2 do @@ -26,8 +34,9 @@ test_expect_success 'setup repo for object creation' ' echo "Version $i of file a/b/$n" > src/a/b/file.$n.txt && echo "Version $i of file a/b/c/$n" > src/a/b/c/file.$n.txt && echo "Version $i of file d/$n" > src/d/file.$n.txt && - echo "Version $i of file d/e/$n" > src/d/e/file.$n.txt && + echo "Version $i of file d/f/$n" > src/d/f/file.$n.txt && git -C src add . && + test_tick && git -C src commit -m "Iteration $n" || return 1 done done @@ -41,6 +50,53 @@ test_expect_success 'setup bare clone for server' ' git -C srv.bare config --local uploadpack.allowanysha1inwant 1 ' +# Create a version of the repo with branches for testing revision +# arguments like --all, --first-parent, and --since. +# +# main: 8 commits (linear) + merge of side branch +# 48 original blobs + 4 side blobs = 52 blobs from main HEAD +# side: 2 commits adding s/file.{1,2}.txt (v1, v2), merged into main +# other: 1 commit adding o/file.{1,2}.txt (not merged) +# 54 total blobs reachable from --all +test_expect_success 'setup branched repo for revision tests' ' + git clone src src-revs && + + # Side branch from tip of main with unique files + git -C src-revs checkout -b side HEAD && + mkdir -p src-revs/s && + echo "Side version 1 of file 1" >src-revs/s/file.1.txt && + echo "Side version 1 of file 2" >src-revs/s/file.2.txt && + test_tick && + git -C src-revs add . && + git -C src-revs commit -m "Side commit 1" && + + echo "Side version 2 of file 1" >src-revs/s/file.1.txt && + echo "Side version 2 of file 2" >src-revs/s/file.2.txt && + test_tick && + git -C src-revs add . && + git -C src-revs commit -m "Side commit 2" && + + # Merge side into main + git -C src-revs checkout main && + test_tick && + git -C src-revs merge side --no-ff -m "Merge side branch" && + + # Other branch (not merged) for --all testing + git -C src-revs checkout -b other main~1 && + mkdir -p src-revs/o && + echo "Other content 1" >src-revs/o/file.1.txt && + echo "Other content 2" >src-revs/o/file.2.txt && + test_tick && + git -C src-revs add . && + git -C src-revs commit -m "Other commit" && + + git -C src-revs checkout main && + + git clone --bare "file://$(pwd)/src-revs" srv-revs.bare && + git -C srv-revs.bare config --local uploadpack.allowfilter 1 && + git -C srv-revs.bare config --local uploadpack.allowanysha1inwant 1 +' + # do basic partial clone from "srv.bare" test_expect_success 'do partial clone 1, backfill gets all objects' ' git clone --no-checkout --filter=blob:none \ @@ -176,6 +232,157 @@ test_expect_success 'backfill --sparse without cone mode (negative)' ' test_line_count = 12 missing ' +test_expect_success 'backfill with revision range' ' + test_when_finished rm -rf backfill-revs && + git clone --no-checkout --filter=blob:none \ + --single-branch --branch=main \ + "file://$(pwd)/srv.bare" backfill-revs && + + # No blobs yet + git -C backfill-revs rev-list --quiet --objects --missing=print HEAD >missing && + test_line_count = 48 missing && + + git -C backfill-revs backfill HEAD~2..HEAD && + + # 30 objects downloaded. + git -C backfill-revs rev-list --quiet --objects --missing=print HEAD >missing && + test_line_count = 18 missing +' + +test_expect_success 'backfill with revisions over stdin' ' + test_when_finished rm -rf backfill-revs && + git clone --no-checkout --filter=blob:none \ + --single-branch --branch=main \ + "file://$(pwd)/srv.bare" backfill-revs && + + # No blobs yet + git -C backfill-revs rev-list --quiet --objects --missing=print HEAD >missing && + test_line_count = 48 missing && + + cat >in <<-EOF && + HEAD + ^HEAD~2 + EOF + + git -C backfill-revs backfill --stdin <in && + + # 30 objects downloaded. + git -C backfill-revs rev-list --quiet --objects --missing=print HEAD >missing && + test_line_count = 18 missing +' + +test_expect_success 'backfill with prefix pathspec' ' + test_when_finished rm -rf backfill-path && + git clone --bare --filter=blob:none \ + --single-branch --branch=main \ + "file://$(pwd)/srv.bare" backfill-path && + + # No blobs yet + git -C backfill-path rev-list --quiet --objects --missing=print HEAD >missing && + test_line_count = 48 missing && + + git -C backfill-path backfill HEAD -- d/f 2>err && + test_must_be_empty err && + + git -C backfill-path rev-list --quiet --objects --missing=print HEAD >missing && + test_line_count = 40 missing +' + +test_expect_success 'backfill with multiple pathspecs' ' + test_when_finished rm -rf backfill-path && + git clone --bare --filter=blob:none \ + --single-branch --branch=main \ + "file://$(pwd)/srv.bare" backfill-path && + + # No blobs yet + git -C backfill-path rev-list --quiet --objects --missing=print HEAD >missing && + test_line_count = 48 missing && + + git -C backfill-path backfill HEAD -- d/f a 2>err && + test_must_be_empty err && + + git -C backfill-path rev-list --quiet --objects --missing=print HEAD >missing && + test_line_count = 16 missing +' + +test_expect_success 'backfill with wildcard pathspec' ' + test_when_finished rm -rf backfill-path && + git clone --bare --filter=blob:none \ + --single-branch --branch=main \ + "file://$(pwd)/srv.bare" backfill-path && + + # No blobs yet + git -C backfill-path rev-list --quiet --objects --missing=print HEAD >missing && + test_line_count = 48 missing && + + git -C backfill-path backfill HEAD -- "d/file.*.txt" 2>err && + test_must_be_empty err && + + git -C backfill-path rev-list --quiet --objects --missing=print HEAD >missing && + test_line_count = 40 missing +' + +test_expect_success 'backfill with --all' ' + test_when_finished rm -rf backfill-all && + git clone --no-checkout --filter=blob:none \ + "file://$(pwd)/srv-revs.bare" backfill-all && + + # All blobs from all refs are missing + git -C backfill-all rev-list --quiet --objects --all --missing=print >missing && + test_line_count = 54 missing && + + # Backfill from HEAD gets main blobs only + git -C backfill-all backfill HEAD && + + # Other branch blobs still missing + git -C backfill-all rev-list --quiet --objects --all --missing=print >missing && + test_line_count = 2 missing && + + # Backfill with --all gets everything + git -C backfill-all backfill --all && + + git -C backfill-all rev-list --quiet --objects --all --missing=print >missing && + test_line_count = 0 missing +' + +test_expect_success 'backfill with --first-parent' ' + test_when_finished rm -rf backfill-fp && + git clone --no-checkout --filter=blob:none \ + --single-branch --branch=main \ + "file://$(pwd)/srv-revs.bare" backfill-fp && + + git -C backfill-fp rev-list --quiet --objects --missing=print HEAD >missing && + test_line_count = 52 missing && + + # --first-parent skips the side branch commits, so + # s/file.{1,2}.txt v1 blobs (only in side commit 1) are missed. + git -C backfill-fp backfill --first-parent HEAD && + + git -C backfill-fp rev-list --quiet --objects --missing=print HEAD >missing && + test_line_count = 2 missing +' + +test_expect_success 'backfill with --since' ' + test_when_finished rm -rf backfill-since && + git clone --no-checkout --filter=blob:none \ + --single-branch --branch=main \ + "file://$(pwd)/srv-revs.bare" backfill-since && + + git -C backfill-since rev-list --quiet --objects --missing=print HEAD >missing && + test_line_count = 52 missing && + + # Use a cutoff between commits 4 and 5 (between v1 and v2 + # iterations). Commits 5-8 still carry v1 of files 2-4 in + # their trees, but v1 of file.1.txt is only in commits 1-4. + SINCE=$(git -C backfill-since log --first-parent --reverse \ + --format=%ct HEAD~1 | sed -n 5p) && + git -C backfill-since backfill --since="@$((SINCE - 1))" HEAD && + + # 6 missing: v1 of file.1.txt in all 6 directories + git -C backfill-since rev-list --quiet --objects --missing=print HEAD >missing && + test_line_count = 6 missing +' + . "$TEST_DIRECTORY"/lib-httpd.sh start_httpd diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index cdc0270640..1ba9ca219e 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -402,7 +402,7 @@ test_expect_success 'git bisect run: negative exit code' " git bisect good $HASH1 && git bisect bad $HASH4 && ! git bisect run ./fail.sh 2>err && - sed -En 's/.*(bisect.*code) (-?[0-9]+) (from.*)/\1 -1 \3/p' err >actual && + sed -E -n 's/.*(bisect.*code) (-?[0-9]+) (from.*)/\1 -1 \3/p' err >actual && test_cmp expect actual " diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh index 5f55ab98d3..7281889717 100755 --- a/t/t6101-rev-parse-parents.sh +++ b/t/t6101-rev-parse-parents.sh @@ -39,7 +39,8 @@ test_expect_success 'setup' ' ' test_expect_success 'start is valid' ' - git rev-parse start | grep "^$OID_REGEX$" + git rev-parse start >actual && + test_grep "^$OID_REGEX$" actual ' test_expect_success 'start^0' ' diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh index 0387f35a32..39211ef989 100755 --- a/t/t6112-rev-list-filters-objects.sh +++ b/t/t6112-rev-list-filters-objects.sh @@ -483,10 +483,6 @@ test_expect_success 'combine:... with non-encoded reserved chars' ' "must escape char in sub-filter-spec: .\~." ' -test_expect_success 'validate err msg for "combine:<valid-filter>+"' ' - expect_invalid_filter_spec combine:tree:2+ "expected .tree:<depth>." -' - test_expect_success 'combine:... with edge-case hex digits: Ff Aa 0 9' ' git -C r3 rev-list --objects --filter="combine:tree:2+bl%6Fb:n%6fne" \ HEAD >actual && diff --git a/t/t6403-merge-file.sh b/t/t6403-merge-file.sh index 4d6e748320..801284cf8f 100755 --- a/t/t6403-merge-file.sh +++ b/t/t6403-merge-file.sh @@ -542,6 +542,15 @@ test_expect_success '--object-id fails without repository' ' grep "not a git repository" err ' +test_expect_success 'run in a linked worktree with --object-id' ' + empty="$(test_oid empty_blob)" && + git worktree add work && + git -C work merge-file --object-id $empty $empty $empty >actual && + git worktree remove work && + git merge-file --object-id $empty $empty $empty >expected && + test_cmp actual expected +' + test_expect_success 'merging C files with "myers" diff algorithm creates some spurious conflicts' ' cat >expect.c <<-\EOF && int g(size_t u) diff --git a/t/t7426-submodule-get-default-remote.sh b/t/t7426-submodule-get-default-remote.sh new file mode 100755 index 0000000000..b842af9a2d --- /dev/null +++ b/t/t7426-submodule-get-default-remote.sh @@ -0,0 +1,186 @@ +#!/bin/sh + +test_description='git submodule--helper get-default-remote' + +TEST_NO_CREATE_REPO=1 +. ./test-lib.sh + +test_expect_success 'setup' ' + git config --global protocol.file.allow always +' + +test_expect_success 'setup repositories' ' + # Create a repository to be used as submodule + git init sub && + test_commit --no-tag -C sub "initial commit in sub" file.txt "sub content" && + + # Create main repository + git init super && + ( + cd super && + mkdir subdir && + test_commit --no-tag -C subdir "initial commit in super" main.txt "super content" && + git submodule add ../sub subpath && + git commit -m "add submodule 'sub' at subpath" + ) +' + +test_expect_success 'get-default-remote returns origin for initialized submodule' ' + ( + cd super && + git submodule update --init && + echo "origin" >expect && + git submodule--helper get-default-remote subpath >actual && + test_cmp expect actual + ) +' + +test_expect_success 'get-default-remote works from subdirectory' ' + ( + cd super/subdir && + echo "origin" >expect && + git submodule--helper get-default-remote ../subpath >actual && + test_cmp expect actual + ) +' + +test_expect_success 'get-default-remote fails with non-existent path' ' + ( + cd super && + test_must_fail git submodule--helper get-default-remote nonexistent 2>err && + test_grep "could not get a repository handle" err + ) +' + +test_expect_success 'get-default-remote fails with non-submodule path' ' + ( + cd super && + test_must_fail git submodule--helper get-default-remote subdir 2>err && + test_grep "could not get a repository handle" err + ) +' + +test_expect_success 'get-default-remote fails without path argument' ' + ( + cd super && + test_must_fail git submodule--helper get-default-remote 2>err && + test_grep "usage:" err + ) +' + +test_expect_success 'get-default-remote fails with too many arguments' ' + ( + cd super && + test_must_fail git submodule--helper get-default-remote subpath subdir 2>err && + test_grep "usage:" err + ) +' + +test_expect_success 'setup submodule with non-origin default remote name' ' + # Create another submodule path with a different remote name + ( + cd super && + git submodule add ../sub upstream-subpath && + git commit -m "add second submodule in upstream-subpath" && + git submodule update --init upstream-subpath && + + # Change the remote name in the submodule + cd upstream-subpath && + git remote rename origin upstream + ) +' + +test_expect_success 'get-default-remote returns non-origin remote name' ' + ( + cd super && + echo "upstream" >expect && + git submodule--helper get-default-remote upstream-subpath >actual && + test_cmp expect actual + ) +' + +test_expect_success 'get-default-remote handles submodule with multiple remotes' ' + ( + cd super/subpath && + git remote add other-upstream ../../sub && + git remote add myfork ../../sub + ) && + + ( + cd super && + echo "origin" >expect && + git submodule--helper get-default-remote subpath >actual && + test_cmp expect actual + ) +' + +test_expect_success 'get-default-remote handles submodule with multiple remotes and none are origin' ' + ( + cd super/upstream-subpath && + git remote add yet-another-upstream ../../sub && + git remote add yourfork ../../sub + ) && + + ( + cd super && + echo "upstream" >expect && + git submodule--helper get-default-remote upstream-subpath >actual && + test_cmp expect actual + ) +' + +test_expect_success 'setup nested submodule with non-origin remote' ' + git init innersub && + test_commit --no-tag -C innersub "initial commit in innersub" inner.txt "innersub content" && + + ( + cd sub && + git submodule add ../innersub innersubpath && + git commit -m "add nested submodule at innersubpath" + ) && + + ( + cd super/upstream-subpath && + git pull upstream && + git submodule update --init --recursive . && + ( + cd innersubpath && + git remote rename origin another_upstream + ) + ) +' + +test_expect_success 'get-default-remote works with nested submodule' ' + ( + cd super && + echo "another_upstream" >expect && + git submodule--helper get-default-remote upstream-subpath/innersubpath >actual && + test_cmp expect actual + ) +' + +test_expect_success 'get-default-remote works with submodule that has no remotes' ' + # Create a submodule directory manually without remotes + ( + cd super && + git init no-remote-sub && + test_commit --no-tag -C no-remote-sub "local commit" local.txt "local content" + ) && + + # Add it as a submodule + ( + cd super && + git submodule add ./no-remote-sub && + git commit -m "add local submodule 'no-remote-sub'" + ) && + + ( + cd super && + # Should fall back to "origin" remote name when no remotes exist + echo "origin" >expect && + git submodule--helper get-default-remote no-remote-sub >actual && + test_cmp expect actual + ) +' + +test_done diff --git a/t/t7605-merge-resolve.sh b/t/t7605-merge-resolve.sh index 5d56c38546..44de97a480 100755 --- a/t/t7605-merge-resolve.sh +++ b/t/t7605-merge-resolve.sh @@ -34,9 +34,9 @@ merge_c1_to_c2_cmds=' test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" && test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" && git diff --exit-code && - test -f c0.c && - test -f c1.c && - test -f c2.c && + test_path_is_file c0.c && + test_path_is_file c1.c && + test_path_is_file c2.c && test 3 = $(git ls-tree -r HEAD | wc -l) && test 3 = $(git ls-files | wc -l) ' diff --git a/t/t7704-repack-cruft.sh b/t/t7704-repack-cruft.sh index aa2e2e6ad8..9e03b04315 100755 --- a/t/t7704-repack-cruft.sh +++ b/t/t7704-repack-cruft.sh @@ -869,4 +869,26 @@ test_expect_success 'repack --write-midx includes cruft when already geometric' ) ' +test_expect_success 'repack rescues once-cruft objects above geometric split' ' + git config repack.midxMustContainCruft false && + + test_commit reachable && + test_commit unreachable && + + unreachable="$(git rev-parse HEAD)" && + + git reset --hard HEAD^ && + git tag -d unreachable && + git reflog expire --all --expire=all && + + git repack --cruft -d && + + echo $unreachable | git pack-objects .git/objects/pack/pack && + + test_commit new && + + git update-ref refs/heads/other $unreachable && + git repack --geometric=2 -d --write-midx --write-bitmap-index +' + test_done diff --git a/t/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh index 731265541a..30e7960ace 100755 --- a/t/t8003-blame-corner-cases.sh +++ b/t/t8003-blame-corner-cases.sh @@ -49,80 +49,69 @@ test_expect_success setup ' ' test_expect_success 'straight copy without -C' ' - - git blame uno | grep Second - + git blame uno >actual && + test_grep Second actual ' test_expect_success 'straight move without -C' ' - - git blame dos | grep Initial - + git blame dos >actual && + test_grep Initial actual ' test_expect_success 'straight copy with -C' ' - - git blame -C1 uno | grep Second - + git blame -C1 uno >actual && + test_grep Second actual ' test_expect_success 'straight move with -C' ' - - git blame -C1 dos | grep Initial - + git blame -C1 dos >actual && + test_grep Initial actual ' test_expect_success 'straight copy with -C -C' ' - - git blame -C -C1 uno | grep Initial - + git blame -C -C1 uno >actual && + test_grep Initial actual ' test_expect_success 'straight move with -C -C' ' - - git blame -C -C1 dos | grep Initial - + git blame -C -C1 dos >actual && + test_grep Initial actual ' test_expect_success 'append without -C' ' - - git blame -L2 tres | grep Second - + git blame -L2 tres >actual && + test_grep Second actual ' test_expect_success 'append with -C' ' - - git blame -L2 -C1 tres | grep Second - + git blame -L2 -C1 tres >actual && + test_grep Second actual ' test_expect_success 'append with -C -C' ' - - git blame -L2 -C -C1 tres | grep Second - + git blame -L2 -C -C1 tres >actual && + test_grep Second actual ' test_expect_success 'append with -C -C -C' ' - - git blame -L2 -C -C -C1 tres | grep Initial - + git blame -L2 -C -C -C1 tres >actual && + test_grep Initial actual ' test_expect_success 'blame wholesale copy' ' - - git blame -f -C -C1 HEAD^ -- cow | sed -e "$pick_fc" >current && + git blame -f -C -C1 HEAD^ -- cow >actual && + sed -e "$pick_fc" actual >current && cat >expected <<-\EOF && mouse-Initial mouse-Second mouse-Third EOF test_cmp expected current - ' test_expect_success 'blame wholesale copy and more' ' - - git blame -f -C -C1 HEAD -- cow | sed -e "$pick_fc" >current && + git blame -f -C -C1 HEAD -- cow >actual && + sed -e "$pick_fc" actual >current && cat >expected <<-\EOF && mouse-Initial mouse-Second @@ -130,11 +119,9 @@ test_expect_success 'blame wholesale copy and more' ' mouse-Third EOF test_cmp expected current - ' test_expect_success 'blame wholesale copy and more in the index' ' - cat >horse <<-\EOF && ABC DEF @@ -144,7 +131,8 @@ test_expect_success 'blame wholesale copy and more in the index' ' EOF git add horse && test_when_finished "git rm -f horse" && - git blame -f -C -C1 -- horse | sed -e "$pick_fc" >current && + git blame -f -C -C1 -- horse >actual && + sed -e "$pick_fc" actual >current && cat >expected <<-\EOF && mouse-Initial mouse-Second @@ -153,11 +141,9 @@ test_expect_success 'blame wholesale copy and more in the index' ' mouse-Third EOF test_cmp expected current - ' test_expect_success 'blame during cherry-pick with file rename conflict' ' - test_when_finished "git reset --hard && git checkout main" && git checkout HEAD~3 && echo MOUSE >> mouse && @@ -168,7 +154,8 @@ test_expect_success 'blame during cherry-pick with file rename conflict' ' (git cherry-pick HEAD@{1} || test $? -eq 1) && git show HEAD@{1}:rodent > rodent && git add rodent && - git blame -f -C -C1 rodent | sed -e "$pick_fc" >current && + git blame -f -C -C1 rodent >actual && + sed -e "$pick_fc" actual >current && cat >expected <<-\EOF && mouse-Initial mouse-Second @@ -246,14 +233,14 @@ test_expect_success 'setup file with CRLF newlines' ' test_expect_success 'blame file with CRLF core.autocrlf true' ' git config core.autocrlf true && git blame crlffile >actual && - grep "A U Thor" actual + test_grep "A U Thor" actual ' test_expect_success 'blame file with CRLF attributes text' ' git config core.autocrlf false && echo "crlffile text" >.gitattributes && git blame crlffile >actual && - grep "A U Thor" actual + test_grep "A U Thor" actual ' test_expect_success 'blame file with CRLF core.autocrlf=true' ' @@ -267,7 +254,7 @@ test_expect_success 'blame file with CRLF core.autocrlf=true' ' git checkout crlfinrepo && rm tmp && git blame crlfinrepo >actual && - grep "A U Thor" actual + test_grep "A U Thor" actual ' test_expect_success 'setup coalesce tests' ' diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 24f6c76aee..e7ab645a3d 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -1649,7 +1649,9 @@ test_expect_success $PREREQ 'To headers from files reset each patch' ' ' test_expect_success $PREREQ 'setup expect' ' -cat >email-using-8bit <<\EOF +# NOTE: do not quote this heredoc, Dash 0.5.13 has a bug with heredocs +# that contain multibyte chars. +cat >email-using-8bit <<EOF From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001 Message-ID: <bogus-message-id@example.com> From: author@example.com @@ -1735,7 +1737,9 @@ test_expect_success $PREREQ '--8bit-encoding overrides sendemail.8bitEncoding' ' ' test_expect_success $PREREQ 'setup expect' ' - cat >email-using-8bit <<-\EOF + # NOTE: do not quote this heredoc, Dash 0.5.13 has a bug with heredocs + # that contain multibyte chars. + cat >email-using-8bit <<-EOF From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001 Message-ID: <bogus-message-id@example.com> From: author@example.com @@ -1764,7 +1768,9 @@ test_expect_success $PREREQ '--8bit-encoding also treats subject' ' ' test_expect_success $PREREQ 'setup expect' ' - cat >email-using-8bit <<-\EOF + # NOTE: do not quote this heredoc, Dash 0.5.13 has a bug with heredocs + # that contain multibyte chars. + cat >email-using-8bit <<-EOF From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001 Message-ID: <bogus-message-id@example.com> From: A U Thor <author@example.com> diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh index a44eabf0d8..14cbe96527 100755 --- a/t/t9200-git-cvsexportcommit.sh +++ b/t/t9200-git-cvsexportcommit.sh @@ -30,13 +30,17 @@ export CVSROOT CVSWORK GIT_DIR rm -rf "$CVSROOT" "$CVSWORK" -cvs init && -test -d "$CVSROOT" && -cvs -Q co -d "$CVSWORK" . && -echo >empty && -git add empty && -git commit -q -a -m "Initial" 2>/dev/null || -exit 1 +if ! cvs init || ! test -d "$CVSROOT" || ! cvs -Q co -d "$CVSWORK" . +then + skip_all="cvs repository set-up fails" + test_done +fi + +test_expect_success 'git setup' ' + echo >empty && + git add empty && + git commit -q -a -m Initial +' check_entries () { # $1 == directory, $2 == expected @@ -303,7 +307,7 @@ test_expect_success 're-commit a removed filename which remains in CVS attic' ' git commit -m "Added attic_gremlin" && git cvsexportcommit -w "$CVSWORK" -c HEAD && (cd "$CVSWORK" && cvs -Q update -d) && - test -f "$CVSWORK/attic_gremlin" + test_path_is_file "$CVSWORK/attic_gremlin" ' # the state of the CVS sandbox may be indeterminate for ' space' diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index 5685cce6fe..479437760b 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -3635,25 +3635,21 @@ background_import_then_checkpoint () { echo "progress checkpoint" ) >&8 & - error=1 ;# assume the worst - while read output <&9 - do - if test "$output" = "progress checkpoint" - then - error=0 - break - elif test "$output" = "UNEXPECTED" - then - break - fi - # otherwise ignore cruft - echo >&2 "cruft: $output" - done + last=$( + while read output <&9 + do + if test "$output" = "progress checkpoint" || test "$output" = "UNEXPECTED" + then + echo "$output" + break + else + # otherwise ignore cruft + echo >&2 "cruft: $output" + fi + done + ) - if test $error -eq 1 - then - false - fi + test "$last" = "progress checkpoint" } background_import_still_running () { diff --git a/t/t9305-fast-import-signatures.sh b/t/t9305-fast-import-signatures.sh index a5406793f7..18707b3f6c 100755 --- a/t/t9305-fast-import-signatures.sh +++ b/t/t9305-fast-import-signatures.sh @@ -103,71 +103,111 @@ test_expect_success RUST,GPG 'strip both OpenPGP signatures with --signed-commit test_line_count = 2 out ' -test_expect_success GPG 'import commit with no signature with --signed-commits=strip-if-invalid' ' - git fast-export main >output && - git -C new fast-import --quiet --signed-commits=strip-if-invalid <output >log 2>&1 && - test_must_be_empty log -' +for mode in strip-if-invalid sign-if-invalid +do + test_expect_success GPG "import commit with no signature with --signed-commits=$mode" ' + git fast-export main >output && + git -C new fast-import --quiet --signed-commits=$mode <output >log 2>&1 && + test_must_be_empty log + ' -test_expect_success GPG 'keep valid OpenPGP signature with --signed-commits=strip-if-invalid' ' - rm -rf new && - git init new && + test_expect_success GPG "keep valid OpenPGP signature with --signed-commits=$mode" ' + rm -rf new && + git init new && - git fast-export --signed-commits=verbatim openpgp-signing >output && - git -C new fast-import --quiet --signed-commits=strip-if-invalid <output >log 2>&1 && - IMPORTED=$(git -C new rev-parse --verify refs/heads/openpgp-signing) && - test $OPENPGP_SIGNING = $IMPORTED && - git -C new cat-file commit "$IMPORTED" >actual && - test_grep -E "^gpgsig(-sha256)? " actual && - test_must_be_empty log -' + git fast-export --signed-commits=verbatim openpgp-signing >output && + git -C new fast-import --quiet --signed-commits=$mode <output >log 2>&1 && + IMPORTED=$(git -C new rev-parse --verify refs/heads/openpgp-signing) && + test $OPENPGP_SIGNING = $IMPORTED && + git -C new cat-file commit "$IMPORTED" >actual && + test_grep -E "^gpgsig(-sha256)? " actual && + test_must_be_empty log + ' -test_expect_success GPG 'strip signature invalidated by message change with --signed-commits=strip-if-invalid' ' - rm -rf new && - git init new && + test_expect_success GPG "handle signature invalidated by message change with --signed-commits=$mode" ' + rm -rf new && + git init new && - git fast-export --signed-commits=verbatim openpgp-signing >output && + git fast-export --signed-commits=verbatim openpgp-signing >output && - # Change the commit message, which invalidates the signature. - # The commit message length should not change though, otherwise the - # corresponding `data <length>` command would have to be changed too. - sed "s/OpenPGP signed commit/OpenPGP forged commit/" output >modified && + # Change the commit message, which invalidates the signature. + # The commit message length should not change though, otherwise the + # corresponding `data <length>` command would have to be changed too. + sed "s/OpenPGP signed commit/OpenPGP forged commit/" output >modified && - git -C new fast-import --quiet --signed-commits=strip-if-invalid <modified >log 2>&1 && + git -C new fast-import --quiet --signed-commits=$mode <modified >log 2>&1 && - IMPORTED=$(git -C new rev-parse --verify refs/heads/openpgp-signing) && - test $OPENPGP_SIGNING != $IMPORTED && - git -C new cat-file commit "$IMPORTED" >actual && - test_grep ! -E "^gpgsig" actual && - test_grep "stripping invalid signature" log -' + IMPORTED=$(git -C new rev-parse --verify refs/heads/openpgp-signing) && + test $OPENPGP_SIGNING != $IMPORTED && + git -C new cat-file commit "$IMPORTED" >actual && -test_expect_success GPGSM 'keep valid X.509 signature with --signed-commits=strip-if-invalid' ' - rm -rf new && - git init new && + if test "$mode" = strip-if-invalid + then + test_grep "stripping invalid signature" log && + test_grep ! -E "^gpgsig" actual + else + test_grep "replacing invalid signature" log && + test_grep -E "^gpgsig(-sha256)? " actual && + git -C new verify-commit "$IMPORTED" + fi + ' - git fast-export --signed-commits=verbatim x509-signing >output && - git -C new fast-import --quiet --signed-commits=strip-if-invalid <output >log 2>&1 && - IMPORTED=$(git -C new rev-parse --verify refs/heads/x509-signing) && - test $X509_SIGNING = $IMPORTED && - git -C new cat-file commit "$IMPORTED" >actual && - test_grep -E "^gpgsig(-sha256)? " actual && - test_must_be_empty log -' + test_expect_success GPGSM "keep valid X.509 signature with --signed-commits=$mode" ' + rm -rf new && + git init new && + + git fast-export --signed-commits=verbatim x509-signing >output && + git -C new fast-import --quiet --signed-commits=$mode <output >log 2>&1 && + IMPORTED=$(git -C new rev-parse --verify refs/heads/x509-signing) && + test $X509_SIGNING = $IMPORTED && + git -C new cat-file commit "$IMPORTED" >actual && + test_grep -E "^gpgsig(-sha256)? " actual && + test_must_be_empty log + ' + + test_expect_success GPGSSH "keep valid SSH signature with --signed-commits=$mode" ' + rm -rf new && + git init new && + + test_config -C new gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && -test_expect_success GPGSSH 'keep valid SSH signature with --signed-commits=strip-if-invalid' ' + git fast-export --signed-commits=verbatim ssh-signing >output && + git -C new fast-import --quiet --signed-commits=$mode <output >log 2>&1 && + IMPORTED=$(git -C new rev-parse --verify refs/heads/ssh-signing) && + test $SSH_SIGNING = $IMPORTED && + git -C new cat-file commit "$IMPORTED" >actual && + test_grep -E "^gpgsig(-sha256)? " actual && + test_must_be_empty log + ' +done + +test_expect_success GPGSSH "sign invalid commit with explicit keyid" ' rm -rf new && git init new && + git fast-export --signed-commits=verbatim ssh-signing >output && + + # Change the commit message, which invalidates the signature. + # The commit message length should not change though, otherwise the + # corresponding `data <length>` command would have to be changed too. + sed "s/SSH signed commit/SSH forged commit/" output >modified && + + # Configure the target repository with an invalid default signing key. + test_config -C new user.signingkey "not-a-real-key-id" && + test_config -C new gpg.format ssh && test_config -C new gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + test_must_fail git -C new fast-import --quiet \ + --signed-commits=sign-if-invalid <modified >/dev/null 2>&1 && + + # Import using explicitly provided signing key. + git -C new fast-import --quiet \ + --signed-commits=sign-if-invalid="${GPGSSH_KEY_PRIMARY}" <modified && - git fast-export --signed-commits=verbatim ssh-signing >output && - git -C new fast-import --quiet --signed-commits=strip-if-invalid <output >log 2>&1 && IMPORTED=$(git -C new rev-parse --verify refs/heads/ssh-signing) && - test $SSH_SIGNING = $IMPORTED && + test $SSH_SIGNING != $IMPORTED && git -C new cat-file commit "$IMPORTED" >actual && test_grep -E "^gpgsig(-sha256)? " actual && - test_must_be_empty log + git -C new verify-commit "$IMPORTED" ' test_done diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index 14e238d24d..f3af10fb7e 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -48,6 +48,9 @@ test_decode_color () { if (n == 2) return "FAINT"; if (n == 3) return "ITALIC"; if (n == 7) return "REVERSE"; + if (n == 22) return "NORMAL_INTENSITY"; + if (n == 23) return "NOITALIC"; + if (n == 27) return "NOREVERSE"; if (n == 30) return "BLACK"; if (n == 31) return "RED"; if (n == 32) return "GREEN"; diff --git a/t/unit-tests/clar/clar.h b/t/unit-tests/clar/clar.h index f7e4363022..9ea91d3d0e 100644 --- a/t/unit-tests/clar/clar.h +++ b/t/unit-tests/clar/clar.h @@ -15,8 +15,10 @@ # define CLAR_MAX_PATH 4096 #elif defined(_WIN32) # define CLAR_MAX_PATH MAX_PATH -#else +#elif defined(PATH_MAX) # define CLAR_MAX_PATH PATH_MAX +#else +# define CLAR_MAX_PATH 4096 #endif #ifndef CLAR_SELFTEST diff --git a/t/unit-tests/clar/generate.py b/t/unit-tests/clar/generate.py index fd2f0ee83b..2357b2d6d2 100755 --- a/t/unit-tests/clar/generate.py +++ b/t/unit-tests/clar/generate.py @@ -8,7 +8,7 @@ from __future__ import with_statement from string import Template -import re, fnmatch, os, sys, codecs, pickle +import re, fnmatch, os, sys, codecs, pickle, io class Module(object): class Template(object): @@ -147,7 +147,7 @@ class TestSuite(object): self.path = path self.output = output - def should_generate(self, path): + def maybe_generate(self, path): if not os.path.isfile(path): return True @@ -223,34 +223,85 @@ class TestSuite(object): return sum(len(module.callbacks) for module in self.modules.values()) def write(self): - output = os.path.join(self.output, 'clar.suite') - os.makedirs(self.output, exist_ok=True) + if not os.path.exists(self.output): + os.makedirs(self.output) - if not self.should_generate(output): + wrote_suite = self.write_suite() + wrote_header = self.write_header() + + if wrote_suite or wrote_header: + self.save_cache() + return True + + return False + + def write_output(self, fn, data): + if not self.maybe_generate(fn): + return False + + current = None + + try: + with open(fn, 'r') as input: + current = input.read() + except OSError: + pass + except IOError: + pass + + if current == data: return False - with open(output, 'w') as data: + with open(fn, 'w') as output: + output.write(data) + + return True + + def write_suite(self): + suite_fn = os.path.join(self.output, 'clar.suite') + + with io.StringIO() as suite_file: modules = sorted(self.modules.values(), key=lambda module: module.name) for module in modules: t = Module.DeclarationTemplate(module) - data.write(t.render()) + suite_file.write(t.render()) for module in modules: t = Module.CallbacksTemplate(module) - data.write(t.render()) + suite_file.write(t.render()) suites = "static struct clar_suite _clar_suites[] = {" + ','.join( Module.InfoTemplate(module).render() for module in modules ) + "\n};\n" - data.write(suites) + suite_file.write(suites) - data.write("static const size_t _clar_suite_count = %d;\n" % self.suite_count()) - data.write("static const size_t _clar_callback_count = %d;\n" % self.callback_count()) + suite_file.write(u"static const size_t _clar_suite_count = %d;\n" % self.suite_count()) + suite_file.write(u"static const size_t _clar_callback_count = %d;\n" % self.callback_count()) - self.save_cache() - return True + return self.write_output(suite_fn, suite_file.getvalue()) + + return False + + def write_header(self): + header_fn = os.path.join(self.output, 'clar_suite.h') + + with io.StringIO() as header_file: + header_file.write(u"#ifndef _____clar_suite_h_____\n") + header_file.write(u"#define _____clar_suite_h_____\n") + + modules = sorted(self.modules.values(), key=lambda module: module.name) + + for module in modules: + t = Module.DeclarationTemplate(module) + header_file.write(t.render()) + + header_file.write(u"#endif\n") + + return self.write_output(header_fn, header_file.getvalue()) + + return False if __name__ == '__main__': from optparse import OptionParser @@ -275,4 +326,4 @@ if __name__ == '__main__': suite.load(options.force) suite.disable(options.excluded) if suite.write(): - print("Written `clar.suite` (%d tests in %d suites)" % (suite.callback_count(), suite.suite_count())) + print("Written `clar.suite`, `clar_suite.h` (%d tests in %d suites)" % (suite.callback_count(), suite.suite_count())) diff --git a/t/unit-tests/test-lib.c b/t/unit-tests/test-lib.c index 87e1f5c201..72ee20a06f 100644 --- a/t/unit-tests/test-lib.c +++ b/t/unit-tests/test-lib.c @@ -396,8 +396,23 @@ int check_uint_loc(const char *loc, const char *check, int ok, static void print_one_char(char ch, char quote) { if ((unsigned char)ch < 0x20u || ch == 0x7f) { - /* TODO: improve handling of \a, \b, \f ... */ - printf("\\%03o", (unsigned char)ch); + char esc; + switch (ch) { + case '\a': esc = 'a'; break; + case '\b': esc = 'b'; break; + case '\t': esc = 't'; break; + case '\n': esc = 'n'; break; + case '\v': esc = 'v'; break; + case '\f': esc = 'f'; break; + case '\r': esc = 'r'; break; + default: esc = 0; break; + } + if (esc) { + putc('\\', stdout); + putc(esc, stdout); + } else { + printf("\\%03o", (unsigned char)ch); + } } else { if (ch == '\\' || ch == quote) putc('\\', stdout); diff --git a/t/unit-tests/u-oidtree.c b/t/unit-tests/u-oidtree.c index e6eede2740..d4d05c7dc3 100644 --- a/t/unit-tests/u-oidtree.c +++ b/t/unit-tests/u-oidtree.c @@ -24,7 +24,7 @@ static int fill_tree_loc(struct oidtree *ot, const char *hexes[], size_t n) return 0; } -static void check_contains(struct oidtree *ot, const char *hex, int expected) +static void check_contains(struct oidtree *ot, const char *hex, bool expected) { struct object_id oid; @@ -38,7 +38,7 @@ struct expected_hex_iter { const char *query; }; -static enum cb_next check_each_cb(const struct object_id *oid, void *data) +static int check_each_cb(const struct object_id *oid, void *data) { struct expected_hex_iter *hex_iter = data; struct object_id expected; @@ -49,7 +49,7 @@ static enum cb_next check_each_cb(const struct object_id *oid, void *data) &expected); cl_assert_equal_s(oid_to_hex(oid), oid_to_hex(&expected)); hex_iter->i += 1; - return CB_CONTINUE; + return 0; } LAST_ARG_MUST_BE_NULL @@ -88,12 +88,12 @@ void test_oidtree__cleanup(void) void test_oidtree__contains(void) { FILL_TREE(&ot, "444", "1", "2", "3", "4", "5", "a", "b", "c", "d", "e"); - check_contains(&ot, "44", 0); - check_contains(&ot, "441", 0); - check_contains(&ot, "440", 0); - check_contains(&ot, "444", 1); - check_contains(&ot, "4440", 1); - check_contains(&ot, "4444", 0); + check_contains(&ot, "44", false); + check_contains(&ot, "441", false); + check_contains(&ot, "440", false); + check_contains(&ot, "444", true); + check_contains(&ot, "4440", true); + check_contains(&ot, "4444", false); } void test_oidtree__each(void) diff --git a/tmp-objdir.c b/tmp-objdir.c index e436eed07e..d199d39e7c 100644 --- a/tmp-objdir.c +++ b/tmp-objdir.c @@ -11,6 +11,7 @@ #include "strvec.h" #include "quote.h" #include "odb.h" +#include "odb/source.h" #include "repository.h" struct tmp_objdir { diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 0000000000..d732997136 --- /dev/null +++ b/tools/README.md @@ -0,0 +1,7 @@ +Developer Tooling +----------------- + +This directory is expected to contain all sorts of tooling that +relates to our build infrastructure. This includes scripts and +inputs required by our build systems, but also scripts that +developers are expected to run manually. diff --git a/check-builtins.sh b/tools/check-builtins.sh index a0aaf3a347..a0aaf3a347 100755 --- a/check-builtins.sh +++ b/tools/check-builtins.sh diff --git a/contrib/coccinelle/.gitignore b/tools/coccinelle/.gitignore index 1d45c0a40c..1d45c0a40c 100644 --- a/contrib/coccinelle/.gitignore +++ b/tools/coccinelle/.gitignore diff --git a/contrib/coccinelle/README b/tools/coccinelle/README index 055ad0e06a..fd0a543cc2 100644 --- a/contrib/coccinelle/README +++ b/tools/coccinelle/README @@ -38,7 +38,7 @@ that might be useful to developers. So to aid these large scale refactorings, semantic patches can be used. However we do not want to store them in the same place as the checks for bad patterns, as then automated builds would fail. - That is why semantic patches 'contrib/coccinelle/*.pending.cocci' + That is why semantic patches 'tools/coccinelle/*.pending.cocci' are ignored for checks, and can be applied using 'make coccicheck-pending'. This allows to expose plans of pending large scale refactorings without diff --git a/contrib/coccinelle/array.cocci b/tools/coccinelle/array.cocci index e71baea00b..e71baea00b 100644 --- a/contrib/coccinelle/array.cocci +++ b/tools/coccinelle/array.cocci diff --git a/contrib/coccinelle/commit.cocci b/tools/coccinelle/commit.cocci index 42725161e9..42725161e9 100644 --- a/contrib/coccinelle/commit.cocci +++ b/tools/coccinelle/commit.cocci diff --git a/contrib/coccinelle/config_fn_ctx.pending.cocci b/tools/coccinelle/config_fn_ctx.pending.cocci index 54f09fcbcd..54f09fcbcd 100644 --- a/contrib/coccinelle/config_fn_ctx.pending.cocci +++ b/tools/coccinelle/config_fn_ctx.pending.cocci diff --git a/contrib/coccinelle/equals-null.cocci b/tools/coccinelle/equals-null.cocci index 92c7054013..92c7054013 100644 --- a/contrib/coccinelle/equals-null.cocci +++ b/tools/coccinelle/equals-null.cocci diff --git a/contrib/coccinelle/flex_alloc.cocci b/tools/coccinelle/flex_alloc.cocci index e9f7f6d861..e9f7f6d861 100644 --- a/contrib/coccinelle/flex_alloc.cocci +++ b/tools/coccinelle/flex_alloc.cocci diff --git a/contrib/coccinelle/free.cocci b/tools/coccinelle/free.cocci index 03799e1908..03799e1908 100644 --- a/contrib/coccinelle/free.cocci +++ b/tools/coccinelle/free.cocci diff --git a/contrib/coccinelle/git_config_number.cocci b/tools/coccinelle/git_config_number.cocci index 7b57dceefe..7b57dceefe 100644 --- a/contrib/coccinelle/git_config_number.cocci +++ b/tools/coccinelle/git_config_number.cocci diff --git a/contrib/coccinelle/hashmap.cocci b/tools/coccinelle/hashmap.cocci index c5dbb4557b..c5dbb4557b 100644 --- a/contrib/coccinelle/hashmap.cocci +++ b/tools/coccinelle/hashmap.cocci diff --git a/contrib/coccinelle/index-compatibility.cocci b/tools/coccinelle/index-compatibility.cocci index 31e36cf3c4..31e36cf3c4 100644 --- a/contrib/coccinelle/index-compatibility.cocci +++ b/tools/coccinelle/index-compatibility.cocci diff --git a/contrib/coccinelle/meson.build b/tools/coccinelle/meson.build index ae7f5b5460..ae7f5b5460 100644 --- a/contrib/coccinelle/meson.build +++ b/tools/coccinelle/meson.build diff --git a/contrib/coccinelle/object_id.cocci b/tools/coccinelle/object_id.cocci index 01f8d6935b..01f8d6935b 100644 --- a/contrib/coccinelle/object_id.cocci +++ b/tools/coccinelle/object_id.cocci diff --git a/contrib/coccinelle/preincr.cocci b/tools/coccinelle/preincr.cocci index ae42cb0730..ae42cb0730 100644 --- a/contrib/coccinelle/preincr.cocci +++ b/tools/coccinelle/preincr.cocci diff --git a/contrib/coccinelle/qsort.cocci b/tools/coccinelle/qsort.cocci index 22b93a9966..22b93a9966 100644 --- a/contrib/coccinelle/qsort.cocci +++ b/tools/coccinelle/qsort.cocci diff --git a/contrib/coccinelle/refs.cocci b/tools/coccinelle/refs.cocci index 31d9cad8f3..31d9cad8f3 100644 --- a/contrib/coccinelle/refs.cocci +++ b/tools/coccinelle/refs.cocci diff --git a/contrib/coccinelle/spatchcache b/tools/coccinelle/spatchcache index 29e9352d8a..efbcbc3827 100755 --- a/contrib/coccinelle/spatchcache +++ b/tools/coccinelle/spatchcache @@ -30,7 +30,7 @@ # out of control. # # This along with the general incremental "make" support for -# "contrib/coccinelle" makes it viable to (re-)run coccicheck +# "tools/coccinelle" makes it viable to (re-)run coccicheck # e.g. when merging integration branches. # # Note that the "--very-quiet" flag is currently critical. The cache @@ -42,7 +42,7 @@ # to change, so just supply "--very-quiet" for now. # # To use this, simply set SPATCH to -# contrib/coccinelle/spatchcache. Then optionally set: +# tools/coccinelle/spatchcache. Then optionally set: # # [spatchCache] # # Optional: path to a custom spatch @@ -65,7 +65,7 @@ # # redis-cli FLUSHALL # <make && make coccicheck, as above> -# grep -hore HIT -e MISS -e SET -e NOCACHE -e CANTCACHE .build/contrib/coccinelle | sort | uniq -c +# grep -hore HIT -e MISS -e SET -e NOCACHE -e CANTCACHE .build/tools/coccinelle | sort | uniq -c # 600 CANTCACHE # 7365 MISS # 7365 SET diff --git a/contrib/coccinelle/strbuf.cocci b/tools/coccinelle/strbuf.cocci index 5f06105df6..f586128329 100644 --- a/contrib/coccinelle/strbuf.cocci +++ b/tools/coccinelle/strbuf.cocci @@ -60,3 +60,21 @@ expression E1, E2; @@ - strbuf_addstr(E1, real_path(E2)); + strbuf_add_real_path(E1, E2); + +@@ +identifier fn, param; +@@ + fn(..., +- struct strbuf param ++ struct strbuf *param + ,...) + { + ... + } + +// In modern codebase, .buf member of an empty strbuf is not NULL. +@@ +struct strbuf SB; +@@ +- SB.buf ? SB.buf : "" ++ SB.buf diff --git a/tools/coccinelle/strvec.cocci b/tools/coccinelle/strvec.cocci new file mode 100644 index 0000000000..64edb09f1c --- /dev/null +++ b/tools/coccinelle/strvec.cocci @@ -0,0 +1,46 @@ +@@ +type T; +identifier i; +expression dst; +struct strvec *src_ptr; +struct strvec src_arr; +@@ +( +- for (T i = 0; i < src_ptr->nr; i++) { strvec_push(dst, src_ptr->v[i]); } ++ strvec_pushv(dst, src_ptr->v); +| +- for (T i = 0; i < src_arr.nr; i++) { strvec_push(dst, src_arr.v[i]); } ++ strvec_pushv(dst, src_arr.v); +) + +@ separate_loop_index @ +type T; +identifier i; +expression dst; +struct strvec *src_ptr; +struct strvec src_arr; +@@ + T i; + ... +( +- for (i = 0; i < src_ptr->nr; i++) { strvec_push(dst, src_ptr->v[i]); } ++ strvec_pushv(dst, src_ptr->v); +| +- for (i = 0; i < src_arr.nr; i++) { strvec_push(dst, src_arr.v[i]); } ++ strvec_pushv(dst, src_arr.v); +) + +@ unused_loop_index extends separate_loop_index @ +@@ + { + ... +- T i; + ... when != i + } + +@ depends on unused_loop_index @ +@@ + if (...) +- { + strvec_pushv(...); +- } diff --git a/contrib/coccinelle/swap.cocci b/tools/coccinelle/swap.cocci index 522177afb6..522177afb6 100644 --- a/contrib/coccinelle/swap.cocci +++ b/tools/coccinelle/swap.cocci diff --git a/contrib/coccinelle/tests/free.c b/tools/coccinelle/tests/free.c index 96d4abc0c7..96d4abc0c7 100644 --- a/contrib/coccinelle/tests/free.c +++ b/tools/coccinelle/tests/free.c diff --git a/contrib/coccinelle/tests/free.res b/tools/coccinelle/tests/free.res index f90fd9f48e..f90fd9f48e 100644 --- a/contrib/coccinelle/tests/free.res +++ b/tools/coccinelle/tests/free.res diff --git a/contrib/coccinelle/the_repository.cocci b/tools/coccinelle/the_repository.cocci index f1129f7985..f1129f7985 100644 --- a/contrib/coccinelle/the_repository.cocci +++ b/tools/coccinelle/the_repository.cocci diff --git a/contrib/coccinelle/xcalloc.cocci b/tools/coccinelle/xcalloc.cocci index c291011607..c291011607 100644 --- a/contrib/coccinelle/xcalloc.cocci +++ b/tools/coccinelle/xcalloc.cocci diff --git a/contrib/coccinelle/xopen.cocci b/tools/coccinelle/xopen.cocci index b71db67019..b71db67019 100644 --- a/contrib/coccinelle/xopen.cocci +++ b/tools/coccinelle/xopen.cocci diff --git a/contrib/coccinelle/xstrdup_or_null.cocci b/tools/coccinelle/xstrdup_or_null.cocci index 9c1d2939b6..9c1d2939b6 100644 --- a/contrib/coccinelle/xstrdup_or_null.cocci +++ b/tools/coccinelle/xstrdup_or_null.cocci diff --git a/contrib/coccinelle/xstrncmpz.cocci b/tools/coccinelle/xstrncmpz.cocci index ccb39e2bc0..ccb39e2bc0 100644 --- a/contrib/coccinelle/xstrncmpz.cocci +++ b/tools/coccinelle/xstrncmpz.cocci diff --git a/contrib/coverage-diff.sh b/tools/coverage-diff.sh index 6ce9603568..6ce9603568 100755 --- a/contrib/coverage-diff.sh +++ b/tools/coverage-diff.sh diff --git a/detect-compiler b/tools/detect-compiler index 124ebdd4c9..124ebdd4c9 100755 --- a/detect-compiler +++ b/tools/detect-compiler diff --git a/generate-cmdlist.sh b/tools/generate-cmdlist.sh index 0ed39c4c5d..0ed39c4c5d 100755 --- a/generate-cmdlist.sh +++ b/tools/generate-cmdlist.sh diff --git a/generate-configlist.sh b/tools/generate-configlist.sh index e28054f9e0..e28054f9e0 100755 --- a/generate-configlist.sh +++ b/tools/generate-configlist.sh diff --git a/generate-hooklist.sh b/tools/generate-hooklist.sh index e0cdf26944..e0cdf26944 100755 --- a/generate-hooklist.sh +++ b/tools/generate-hooklist.sh diff --git a/generate-perl.sh b/tools/generate-perl.sh index 796d835932..796d835932 100755 --- a/generate-perl.sh +++ b/tools/generate-perl.sh diff --git a/generate-python.sh b/tools/generate-python.sh index 31ac115689..31ac115689 100755 --- a/generate-python.sh +++ b/tools/generate-python.sh diff --git a/generate-script.sh b/tools/generate-script.sh index a149e4f0ba..a149e4f0ba 100755 --- a/generate-script.sh +++ b/tools/generate-script.sh diff --git a/tools/meson.build b/tools/meson.build new file mode 100644 index 0000000000..f731f74312 --- /dev/null +++ b/tools/meson.build @@ -0,0 +1 @@ +subdir('coccinelle') diff --git a/tools/precompiled.h b/tools/precompiled.h new file mode 100644 index 0000000000..b2bec0d2b4 --- /dev/null +++ b/tools/precompiled.h @@ -0,0 +1 @@ +#include "git-compat-util.h" diff --git a/contrib/update-unicode/.gitignore b/tools/update-unicode/.gitignore index b0ebc6aad2..b0ebc6aad2 100644 --- a/contrib/update-unicode/.gitignore +++ b/tools/update-unicode/.gitignore diff --git a/contrib/update-unicode/README b/tools/update-unicode/README index 151a197041..151a197041 100644 --- a/contrib/update-unicode/README +++ b/tools/update-unicode/README diff --git a/contrib/update-unicode/update_unicode.sh b/tools/update-unicode/update_unicode.sh index aa90865bef..aa90865bef 100755 --- a/contrib/update-unicode/update_unicode.sh +++ b/tools/update-unicode/update_unicode.sh @@ -7,8 +7,11 @@ #include "string-list.h" #include "run-command.h" #include "commit.h" +#include "strvec.h" #include "trailer.h" #include "list.h" +#include "tempfile.h" + /* * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org> */ @@ -772,6 +775,35 @@ void parse_trailers_from_command_line_args(struct list_head *arg_head, free(cl_separators); } +int validate_trailer_args(const struct strvec *cli_args) +{ + char *cl_separators; + int ret = 0; + + trailer_config_init(); + + cl_separators = xstrfmt("=%s", separators); + + for (size_t i = 0; i < cli_args->nr; i++) { + const char *txt = cli_args->v[i]; + ssize_t separator_pos; + + if (!*txt) { + ret = error(_("empty --trailer argument")); + goto out; + } + separator_pos = find_separator(txt, cl_separators); + if (separator_pos == 0) { + ret = error(_("invalid trailer '%s': missing key before separator"), + txt); + goto out; + } + } +out: + free(cl_separators); + return ret; +} + static const char *next_line(const char *str) { const char *nl = strchrnul(str, '\n'); @@ -1009,7 +1041,7 @@ static struct trailer_block *trailer_block_get(const struct process_trailer_opti for (ptr = trailer_lines; *ptr; ptr++) { if (last && isspace((*ptr)->buf[0])) { struct strbuf sb = STRBUF_INIT; - strbuf_attach(&sb, *last, strlen(*last), strlen(*last)); + strbuf_attach(&sb, *last, strlen(*last), strlen(*last) + 1); strbuf_addbuf(&sb, *ptr); *last = strbuf_detach(&sb, NULL); continue; @@ -1224,14 +1256,146 @@ void trailer_iterator_release(struct trailer_iterator *iter) strbuf_release(&iter->key); } -int amend_file_with_trailers(const char *path, const struct strvec *trailer_args) +struct tempfile *trailer_create_in_place_tempfile(const char *file) +{ + struct tempfile *tempfile = NULL; + struct stat st; + struct strbuf filename_template = STRBUF_INIT; + const char *tail; + + if (stat(file, &st)) { + error_errno(_("could not stat %s"), file); + return NULL; + } + if (!S_ISREG(st.st_mode)) { + error(_("file %s is not a regular file"), file); + return NULL; + } + if (!(st.st_mode & S_IWUSR)) { + error(_("file %s is not writable by user"), file); + return NULL; + } + /* Create temporary file in the same directory as the original */ + tail = find_last_dir_sep(file); + if (tail) + strbuf_add(&filename_template, file, tail - file + 1); + strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX"); + + tempfile = mks_tempfile_m(filename_template.buf, st.st_mode); + + strbuf_release(&filename_template); + + return tempfile; +} + +int amend_strbuf_with_trailers(struct strbuf *buf, + const struct strvec *trailer_args) { - struct child_process run_trailer = CHILD_PROCESS_INIT; + struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT; + LIST_HEAD(new_trailer_head); + struct strbuf out = STRBUF_INIT; + size_t i; + int ret = 0; + + opts.no_divider = 1; + + for (i = 0; i < trailer_args->nr; i++) { + const char *text = trailer_args->v[i]; + struct new_trailer_item *item; + + if (!*text) { + ret = error(_("empty --trailer argument")); + goto out; + } + item = xcalloc(1, sizeof(*item)); + item->text = xstrdup(text); + list_add_tail(&item->list, &new_trailer_head); + } - run_trailer.git_cmd = 1; - strvec_pushl(&run_trailer.args, "interpret-trailers", - "--in-place", "--no-divider", - path, NULL); - strvec_pushv(&run_trailer.args, trailer_args->v); - return run_command(&run_trailer); + process_trailers(&opts, &new_trailer_head, buf, &out); + + strbuf_swap(buf, &out); +out: + strbuf_release(&out); + free_trailers(&new_trailer_head); + + return ret; +} + +static int write_file_in_place(const char *path, const struct strbuf *buf) +{ + struct tempfile *tempfile = trailer_create_in_place_tempfile(path); + if (!tempfile) + return -1; + + if (write_in_full(tempfile->fd, buf->buf, buf->len) < 0) + return error_errno(_("could not write to temporary file")); + + if (rename_tempfile(&tempfile, path)) + return error_errno(_("could not rename temporary file to %s"), path); + + return 0; +} + +int amend_file_with_trailers(const char *path, + const struct strvec *trailer_args) +{ + struct strbuf buf = STRBUF_INIT; + int ret = 0; + + if (!trailer_args) + BUG("amend_file_with_trailers called with NULL trailer_args"); + if (!trailer_args->nr) + return 0; + + if (validate_trailer_args(trailer_args)) { + ret = -1; + goto out; + } + if (strbuf_read_file(&buf, path, 0) < 0) + ret = error_errno(_("could not read '%s'"), path); + else + amend_strbuf_with_trailers(&buf, trailer_args); + + if (!ret) + ret = write_file_in_place(path, &buf); +out: + strbuf_release(&buf); + return ret; +} + +void process_trailers(const struct process_trailer_options *opts, + struct list_head *new_trailer_head, + struct strbuf *input, struct strbuf *out) +{ + LIST_HEAD(head); + struct trailer_block *trailer_block; + + trailer_block = parse_trailers(opts, input->buf, &head); + + /* Print the lines before the trailer block */ + if (!opts->only_trailers) + strbuf_add(out, input->buf, trailer_block_start(trailer_block)); + + if (!opts->only_trailers && !blank_line_before_trailer_block(trailer_block)) + strbuf_addch(out, '\n'); + + if (!opts->only_input) { + LIST_HEAD(config_head); + LIST_HEAD(arg_head); + parse_trailers_from_config(&config_head); + parse_trailers_from_command_line_args(&arg_head, new_trailer_head); + list_splice(&config_head, &arg_head); + process_trailers_lists(&head, &arg_head); + } + + /* Print trailer block. */ + format_trailers(opts, &head, out); + free_trailers(&head); + + /* Print the lines after the trailer block as is. */ + if (!opts->only_trailers) + strbuf_add(out, input->buf + trailer_block_end(trailer_block), + input->len - trailer_block_end(trailer_block)); + trailer_block_release(trailer_block); } @@ -68,6 +68,8 @@ void parse_trailers_from_config(struct list_head *config_head); void parse_trailers_from_command_line_args(struct list_head *arg_head, struct list_head *new_trailer_head); +int validate_trailer_args(const struct strvec *cli_args); + void process_trailers_lists(struct list_head *head, struct list_head *arg_head); @@ -196,10 +198,38 @@ int trailer_iterator_advance(struct trailer_iterator *iter); void trailer_iterator_release(struct trailer_iterator *iter); /* - * Augment a file to add trailers to it by running git-interpret-trailers. - * This calls run_command() and its return value is the same (i.e. 0 for - * success, various non-zero for other errors). See run-command.h. + * Append trailers specified in trailer_args to buf in-place. + * + * Each element of trailer_args should be in the same format as the value + * accepted by --trailer=<trailer> (i.e., without the --trailer= prefix). + */ +int amend_strbuf_with_trailers(struct strbuf *buf, + const struct strvec *trailer_args); + +/* + * Augment a file by appending trailers specified in trailer_args. + * + * Each element of trailer_args should be in the same format as the value + * accepted by --trailer=<trailer> (i.e., without the --trailer= prefix). + * + * Returns 0 on success or a non-zero error code on failure. */ int amend_file_with_trailers(const char *path, const struct strvec *trailer_args); +/* + * Create a tempfile ""git-interpret-trailers-XXXXXX" in the same + * directory as file. + */ +struct tempfile *trailer_create_in_place_tempfile(const char *file); + +/* + * Rewrite the contents of input by processing its trailer block according to + * opts and (optionally) appending trailers from new_trailer_head. + * + * The rewritten message is appended to out (callers should strbuf_reset() + * first if needed). + */ +void process_trailers(const struct process_trailer_options *opts, + struct list_head *new_trailer_head, + struct strbuf *input, struct strbuf *out); #endif /* TRAILER_H */ diff --git a/transport-helper.c b/transport-helper.c index 4d95d84f9e..570d7c6439 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -154,6 +154,8 @@ static struct child_process *get_helper(struct transport *transport) helper->trace2_child_class = helper->args.v[0]; /* "remote-<name>" */ + helper->clean_on_exit = 1; + helper->wait_after_clean = 1; code = start_command(helper); if (code < 0 && errno == ENOENT) die(_("unable to find remote helper for '%s'"), data->name); diff --git a/transport.c b/transport.c index 107f4fa5dc..e53936d87b 100644 --- a/transport.c +++ b/transport.c @@ -47,21 +47,21 @@ static int transport_color_config(void) "color.transport.reset", "color.transport.rejected" }, *key = "color.transport"; - char *value; + const char *value; static int initialized; if (initialized) return 0; initialized = 1; - if (!repo_config_get_string(the_repository, key, &value)) + if (!repo_config_get_string_tmp(the_repository, key, &value)) transport_use_color = git_config_colorbool(key, value); if (!want_color_stderr(transport_use_color)) return 0; for (size_t i = 0; i < ARRAY_SIZE(keys); i++) - if (!repo_config_get_string(the_repository, keys[i], &value)) { + if (!repo_config_get_string_tmp(the_repository, keys[i], &value)) { if (!value) return config_error_nonbool(keys[i]); if (color_parse(value, transport_colors[i]) < 0) @@ -1360,7 +1360,8 @@ static int pre_push_hook_feed_stdin(int hook_stdin_fd, void *pp_cb UNUSED, void static void *pre_push_hook_data_alloc(void *feed_pipe_ctx) { - struct feed_pre_push_hook_data *data = xmalloc(sizeof(*data)); + struct feed_pre_push_hook_data *data; + CALLOC_ARRAY(data, 1); strbuf_init(&data->buf, 0); data->refs = (struct ref *)feed_pipe_ctx; return data; diff --git a/upload-pack.c b/upload-pack.c index e8c5cce1c7..9f6d6fe48c 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -29,6 +29,7 @@ #include "commit-graph.h" #include "commit-reach.h" #include "shallow.h" +#include "trace.h" #include "write-or-die.h" #include "json-writer.h" #include "strmap.h" @@ -218,7 +219,8 @@ struct output_state { }; static int relay_pack_data(int pack_objects_out, struct output_state *os, - int use_sideband, int write_packfile_line) + int use_sideband, int write_packfile_line, + bool *did_send_data) { /* * We keep the last byte to ourselves @@ -232,6 +234,8 @@ static int relay_pack_data(int pack_objects_out, struct output_state *os, */ ssize_t readsz; + *did_send_data = false; + readsz = xread(pack_objects_out, os->buffer + os->used, sizeof(os->buffer) - os->used); if (readsz < 0) { @@ -247,6 +251,7 @@ static int relay_pack_data(int pack_objects_out, struct output_state *os, if (os->packfile_uris_started) packet_delim(1); packet_write_fmt(1, "\1packfile\n"); + *did_send_data = true; } break; } @@ -259,6 +264,7 @@ static int relay_pack_data(int pack_objects_out, struct output_state *os, } *p = '\0'; packet_write_fmt(1, "\1%s\n", os->buffer); + *did_send_data = true; os->used -= p - os->buffer + 1; memmove(os->buffer, p + 1, os->used); @@ -270,6 +276,13 @@ static int relay_pack_data(int pack_objects_out, struct output_state *os, } } + /* + * Make sure that we buffer some data before sending it to the client. + * This significantly reduces the number of write(3p) syscalls. + */ + if (readsz && os->used < (sizeof(os->buffer) * 2 / 3)) + return readsz; + if (os->used > 1) { send_client_data(1, os->buffer, os->used - 1, use_sideband); os->buffer[0] = os->buffer[os->used - 1]; @@ -279,6 +292,7 @@ static int relay_pack_data(int pack_objects_out, struct output_state *os, os->used = 0; } + *did_send_data = true; return readsz; } @@ -290,6 +304,7 @@ static void create_pack_file(struct upload_pack_data *pack_data, char progress[128]; char abort_msg[] = "aborting due to possible repository " "corruption on the remote side."; + uint64_t last_sent_ms = 0; ssize_t sz; int i; FILE *pipe_fd; @@ -365,10 +380,14 @@ static void create_pack_file(struct upload_pack_data *pack_data, */ while (1) { + uint64_t now_ms = getnanotime() / 1000000; struct pollfd pfd[2]; - int pe, pu, pollsize, polltimeout; + int pe, pu, pollsize, polltimeout_ms; int ret; + if (!last_sent_ms) + last_sent_ms = now_ms; + reset_timeout(pack_data->timeout); pollsize = 0; @@ -390,11 +409,21 @@ static void create_pack_file(struct upload_pack_data *pack_data, if (!pollsize) break; - polltimeout = pack_data->keepalive < 0 - ? -1 - : 1000 * pack_data->keepalive; + if (pack_data->keepalive < 0) { + polltimeout_ms = -1; + } else { + /* + * The polling timeout needs to be adjusted based on + * the time we have sent our last package. The longer + * it's been in the past, the shorter the timeout + * becomes until we eventually don't block at all. + */ + polltimeout_ms = 1000 * pack_data->keepalive - (now_ms - last_sent_ms); + if (polltimeout_ms < 0) + polltimeout_ms = 0; + } - ret = poll(pfd, pollsize, polltimeout); + ret = poll(pfd, pollsize, polltimeout_ms); if (ret < 0) { if (errno != EINTR) { @@ -403,16 +432,18 @@ static void create_pack_file(struct upload_pack_data *pack_data, } continue; } + if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) { /* Status ready; we ship that in the side-band * or dump to the standard error. */ sz = xread(pack_objects.err, progress, sizeof(progress)); - if (0 < sz) + if (0 < sz) { send_client_data(2, progress, sz, pack_data->use_sideband); - else if (sz == 0) { + last_sent_ms = now_ms; + } else if (sz == 0) { close(pack_objects.err); pack_objects.err = -1; } @@ -421,11 +452,14 @@ static void create_pack_file(struct upload_pack_data *pack_data, /* give priority to status messages */ continue; } + if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) { + bool did_send_data; int result = relay_pack_data(pack_objects.out, output_state, pack_data->use_sideband, - !!uri_protocols); + !!uri_protocols, + &did_send_data); if (result == 0) { close(pack_objects.out); @@ -433,21 +467,34 @@ static void create_pack_file(struct upload_pack_data *pack_data, } else if (result < 0) { goto fail; } + + if (did_send_data) + last_sent_ms = now_ms; } /* - * We hit the keepalive timeout without saying anything; send - * an empty message on the data sideband just to let the other - * side know we're still working on it, but don't have any data - * yet. + * We hit the keepalive timeout without saying anything. If we + * have pending data we flush it out to the caller now. + * Otherwise, we send an empty message on the data sideband + * just to let the other side know we're still working on it, + * but don't have any data yet. * * If we don't have a sideband channel, there's no room in the * protocol to say anything, so those clients are just out of * luck. */ if (!ret && pack_data->use_sideband) { - static const char buf[] = "0005\1"; - write_or_die(1, buf, 5); + if (output_state->packfile_started && output_state->used > 1) { + send_client_data(1, output_state->buffer, output_state->used - 1, + pack_data->use_sideband); + output_state->buffer[0] = output_state->buffer[output_state->used - 1]; + output_state->used = 1; + } else { + static const char buf[] = "0005\1"; + write_or_die(1, buf, 5); + } + + last_sent_ms = now_ms; } } @@ -457,11 +504,9 @@ static void create_pack_file(struct upload_pack_data *pack_data, } /* flush the data */ - if (output_state->used > 0) { + if (output_state->used > 0) send_client_data(1, output_state->buffer, output_state->used, pack_data->use_sideband); - fprintf(stderr, "flushed.\n"); - } free(output_state); if (pack_data->use_sideband) packet_flush(1); diff --git a/worktree.c b/worktree.c index 6552e30053..d874e23b4e 100644 --- a/worktree.c +++ b/worktree.c @@ -58,7 +58,7 @@ static void add_head_info(struct worktree *wt) static int is_current_worktree(struct worktree *wt) { - char *git_dir = absolute_pathdup(repo_get_git_dir(the_repository)); + char *git_dir = absolute_pathdup(repo_get_git_dir(wt->repo)); char *wt_git_dir = get_worktree_git_dir(wt); int is_current = !fspathcmp(git_dir, absolute_path(wt_git_dir)); free(wt_git_dir); @@ -78,7 +78,7 @@ struct worktree *get_worktree_from_repository(struct repository *repo) wt->is_bare = !repo->worktree; if (fspathcmp(gitdir, commondir)) wt->id = xstrdup(find_last_dir_sep(gitdir) + 1); - wt->is_current = is_current_worktree(wt); + wt->is_current = true; add_head_info(wt); free(gitdir); @@ -227,11 +227,11 @@ struct worktree **get_worktrees_without_reading_head(void) char *get_worktree_git_dir(const struct worktree *wt) { if (!wt) - return xstrdup(repo_get_git_dir(the_repository)); + BUG("%s() called with NULL worktree", __func__); else if (!wt->id) - return xstrdup(repo_get_common_dir(the_repository)); + return xstrdup(repo_get_common_dir(wt->repo)); else - return repo_common_path(the_repository, "worktrees/%s", wt->id); + return repo_common_path(wt->repo, "worktrees/%s", wt->id); } static struct worktree *find_worktree_by_suffix(struct worktree **list, @@ -445,7 +445,7 @@ void update_worktree_location(struct worktree *wt, const char *path_, strbuf_realpath(&path, path_, 1); strbuf_addf(&dotgit, "%s/.git", path.buf); if (fspathcmp(wt->path, path.buf)) { - write_worktree_linking_files(dotgit, gitdir, use_relative_paths); + write_worktree_linking_files(dotgit.buf, gitdir.buf, use_relative_paths); free(wt->path); wt->path = strbuf_detach(&path, NULL); @@ -685,7 +685,7 @@ static void repair_gitfile(struct worktree *wt, if (repair) { fn(0, wt->path, repair, cb_data); - write_worktree_linking_files(dotgit, gitdir, use_relative_paths); + write_worktree_linking_files(dotgit.buf, gitdir.buf, use_relative_paths); } done: @@ -743,7 +743,7 @@ void repair_worktree_after_gitdir_move(struct worktree *wt, const char *old_path if (!file_exists(dotgit.buf)) goto done; - write_worktree_linking_files(dotgit, gitdir, is_relative_path); + write_worktree_linking_files(dotgit.buf, gitdir.buf, is_relative_path); done: strbuf_release(&gitdir); strbuf_release(&dotgit); @@ -915,7 +915,7 @@ void repair_worktree_at_path(const char *path, if (repair) { fn(0, gitdir.buf, repair, cb_data); - write_worktree_linking_files(dotgit, gitdir, use_relative_paths); + write_worktree_linking_files(dotgit.buf, gitdir.buf, use_relative_paths); } done: free(dotgit_contents); @@ -1089,17 +1089,17 @@ cleanup: return res; } -void write_worktree_linking_files(struct strbuf dotgit, struct strbuf gitdir, +void write_worktree_linking_files(const char *dotgit, const char *gitdir, int use_relative_paths) { struct strbuf path = STRBUF_INIT; struct strbuf repo = STRBUF_INIT; struct strbuf tmp = STRBUF_INIT; - strbuf_addbuf(&path, &dotgit); + strbuf_addstr(&path, dotgit); strbuf_strip_suffix(&path, "/.git"); strbuf_realpath(&path, path.buf, 1); - strbuf_addbuf(&repo, &gitdir); + strbuf_addstr(&repo, gitdir); strbuf_strip_suffix(&repo, "/gitdir"); strbuf_realpath(&repo, repo.buf, 1); @@ -1112,11 +1112,11 @@ void write_worktree_linking_files(struct strbuf dotgit, struct strbuf gitdir, } if (use_relative_paths) { - write_file(gitdir.buf, "%s/.git", relative_path(path.buf, repo.buf, &tmp)); - write_file(dotgit.buf, "gitdir: %s", relative_path(repo.buf, path.buf, &tmp)); + write_file(gitdir, "%s/.git", relative_path(path.buf, repo.buf, &tmp)); + write_file(dotgit, "gitdir: %s", relative_path(repo.buf, path.buf, &tmp)); } else { - write_file(gitdir.buf, "%s/.git", path.buf); - write_file(dotgit.buf, "gitdir: %s", repo.buf); + write_file(gitdir, "%s/.git", path.buf); + write_file(dotgit, "gitdir: %s", repo.buf); } strbuf_release(&path); diff --git a/worktree.h b/worktree.h index e450d1a331..d19ec29dbb 100644 --- a/worktree.h +++ b/worktree.h @@ -16,7 +16,7 @@ struct worktree { struct object_id head_oid; int is_detached; int is_bare; - int is_current; + int is_current; /* does `path` match `repo->worktree` */ int lock_reason_valid; /* private */ int prune_reason_valid; /* private */ }; @@ -51,7 +51,6 @@ int submodule_uses_worktrees(const char *path); /* * Return git dir of the worktree. Note that the path may be relative. - * If wt is NULL, git dir of current worktree is returned. */ char *get_worktree_git_dir(const struct worktree *wt); @@ -240,7 +239,7 @@ int init_worktree_config(struct repository *r); * dotgit: "/path/to/foo/.git" * gitdir: "/path/to/repo/worktrees/foo/gitdir" */ -void write_worktree_linking_files(struct strbuf dotgit, struct strbuf gitdir, +void write_worktree_linking_files(const char *dotgit, const char *gitdir, int use_relative_paths); #endif @@ -323,6 +323,47 @@ ssize_t write_in_full(int fd, const void *buf, size_t count) return total; } +ssize_t writev_in_full(int fd, struct iovec *iov, int iovcnt) +{ + ssize_t total_written = 0; + + while (iovcnt) { + ssize_t bytes_written = writev(fd, iov, iovcnt); + if (bytes_written < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } + if (!bytes_written) { + errno = ENOSPC; + return -1; + } + + total_written += bytes_written; + + /* + * We first need to discard any iovec entities that have been + * fully written. + */ + while (iovcnt && (size_t)bytes_written >= iov->iov_len) { + bytes_written -= iov->iov_len; + iov++; + iovcnt--; + } + + /* + * Finally, we need to adjust the last iovec in case we have + * performed a partial write. + */ + if (iovcnt && bytes_written) { + iov->iov_base = (char *) iov->iov_base + bytes_written; + iov->iov_len -= bytes_written; + } + } + + return total_written; +} + ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset) { char *p = buf; @@ -47,6 +47,15 @@ ssize_t read_in_full(int fd, void *buf, size_t count); ssize_t write_in_full(int fd, const void *buf, size_t count); ssize_t pread_in_full(int fd, void *buf, size_t count, off_t offset); +/* + * Try to write all iovecs. Returns -1 in case an error occurred with a proper + * errno set, the number of bytes written otherwise. + * + * Note that the iovec will be modified as a result of this call to adjust for + * partial writes! + */ +ssize_t writev_in_full(int fd, struct iovec *iov, int iovcnt); + static inline ssize_t write_str_in_full(int fd, const char *str) { return write_in_full(fd, str, strlen(str)); diff --git a/write-or-die.c b/write-or-die.c index 01a9a51fa2..5f522fb728 100644 --- a/write-or-die.c +++ b/write-or-die.c @@ -96,6 +96,14 @@ void write_or_die(int fd, const void *buf, size_t count) } } +void writev_or_die(int fd, struct iovec *iov, int iovlen) +{ + if (writev_in_full(fd, iov, iovlen) < 0) { + check_pipe(errno); + die_errno("writev error"); + } +} + void fwrite_or_die(FILE *f, const void *buf, size_t count) { if (fwrite(buf, 1, count, f) != count) diff --git a/write-or-die.h b/write-or-die.h index ff0408bd84..a045bdfaef 100644 --- a/write-or-die.h +++ b/write-or-die.h @@ -7,6 +7,7 @@ void fprintf_or_die(FILE *, const char *fmt, ...); void fwrite_or_die(FILE *f, const void *buf, size_t count); void fflush_or_die(FILE *f); void write_or_die(int fd, const void *buf, size_t count); +void writev_or_die(int fd, struct iovec *iov, int iovlen); /* * These values are used to help identify parts of a repository to fsync. diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c index 4376f943db..5455b4690d 100644 --- a/xdiff/xdiffi.c +++ b/xdiff/xdiffi.c @@ -792,6 +792,7 @@ static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g) */ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { struct xdlgroup g, go; + struct xdlgroup g_orig; long earliest_end, end_matching_other; long groupsize; @@ -805,10 +806,12 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { if (g.end == g.start) goto next; + g_orig = g; + /* * Now shift the change up and then down as far as possible in * each direction. If it bumps into any other changes, merge - * them. + * them and restart the process. */ do { groupsize = g.end - g.start; @@ -861,7 +864,8 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { /* * Move the possibly merged group of changes back to * line up with the last group of changes from the - * other file that it can align with. + * other file that it can align with. This avoids breaking + * a single change into a separate addition/deletion. */ while (go.end == go.start) { if (group_slide_up(xdf, &g)) @@ -914,6 +918,45 @@ int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { } } + /* + * If we merged change groups during shifting, the new + * combined group could now have matching lines in both files, + * even if the original separate groups did not. Re-diff the + * new group to find these matching lines to mark them as + * unchanged. + * + * Only do this if the corresponding group in the other file is + * non-empty, as it's trivial otherwise. + * + * Only do this for histogram diff as its LCS algorithm allows + * for this scenario. In contrast, patience diff finds LCS + * of unique lines that groups cannot be shifted across. + * Myer's diff (standalone or used as fall-back in patience + * diff) already finds minimal edits so it is not possible for + * shifted groups to result in a smaller diff. (Without + * XDF_NEED_MINIMAL, Myer's isn't technically guaranteed to be + * minimal, but it should be so most of the time) + */ + if (go.end != go.start && + XDF_DIFF_ALG(flags) == XDF_HISTOGRAM_DIFF && + (g.start != g_orig.start || + g.end != g_orig.end)) { + xpparam_t xpp; + xdfenv_t xe; + + memset(&xpp, 0, sizeof(xpp)); + xpp.flags = flags & ~XDF_DIFF_ALGORITHM_MASK; + + xe.xdf1 = *xdf; + xe.xdf2 = *xdfo; + + if (xdl_fall_back_diff(&xe, &xpp, + g.start + 1, g.end - g.start, + go.start + 1, go.end - go.start)) { + return -1; + } + } + next: /* Move past the just-processed group: */ if (group_next(xdf, &g)) |
