diff options
880 files changed, 32734 insertions, 28306 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 1fbdc2652b..fef04a3840 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -5,11 +5,13 @@ freebsd_task: env: GIT_PROVE_OPTS: "--timer --jobs 10" GIT_TEST_OPTS: "--no-chain-lint --no-bin-wrappers" - MAKEFLAGS: "-j4" + GIT_SKIP_TESTS: t7815.12 + MAKEFLAGS: -j4 DEFAULT_TEST_TARGET: prove + DEFAULT_UNIT_TEST_TARGET: unit-tests-prove DEVELOPER: 1 freebsd_instance: - image_family: freebsd-13-4 + image_family: freebsd-14-3 memory: 2G install_script: pkg install -y gettext gmake perl5 @@ -19,4 +21,4 @@ freebsd_task: build_script: - su git -c gmake test_script: - - su git -c 'gmake DEFAULT_UNIT_TEST_TARGET=unit-tests-prove test unit-tests' + - su git -c 'gmake test unit-tests' diff --git a/.clang-format b/.clang-format index 9547fe1b77..dcfd0aad60 100644 --- a/.clang-format +++ b/.clang-format @@ -12,7 +12,15 @@ UseTab: Always TabWidth: 8 IndentWidth: 8 ContinuationIndentWidth: 8 -ColumnLimit: 80 + +# While we do want to enforce a character limit of 80 characters, we often +# allow lines to overflow that limit to prioritize readability. Setting a +# character limit here with penalties has been finicky and creates too many +# false positives. +# +# NEEDSWORK: It would be nice if we can find optimal settings to ensure we +# can re-enable the limit here. +ColumnLimit: 0 # C Language specifics Language: Cpp @@ -210,16 +218,11 @@ MaxEmptyLinesToKeep: 1 # No empty line at the start of a block. KeepEmptyLinesAtTheStartOfBlocks: false -# Penalties -# This decides what order things should be done if a line is too long -PenaltyBreakAssignment: 5 -PenaltyBreakBeforeFirstCallParameter: 5 -PenaltyBreakComment: 5 -PenaltyBreakFirstLessLess: 0 -PenaltyBreakOpenParenthesis: 300 -PenaltyBreakString: 5 -PenaltyExcessCharacter: 10 -PenaltyReturnTypeOnItsOwnLine: 300 - # Don't sort #include's SortIncludes: false + +# Remove optional braces of control statements (if, else, for, and while) +# according to the LLVM coding style. This avoids braces on simple +# single-statement bodies of statements but keeps braces if one side of +# if/else if/.../else cascade has multi-statement body. +RemoveBracesLLVM: true diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7dbf9f7f12..d122e79415 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -298,7 +298,7 @@ jobs: path: build - name: Test shell: pwsh - run: meson test -C build --list | Select-Object -Skip 1 | Select-String .* | Group-Object -Property { $_.LineNumber % 10 } | Where-Object Name -EQ ${{ matrix.nr }} | ForEach-Object { meson test -C build --no-rebuild --print-errorlogs $_.Group } + run: meson test -C build --no-rebuild --print-errorlogs --slice "$(1+${{ matrix.nr }})/10" regular: name: ${{matrix.vector.jobname}} (${{matrix.vector.pool}}) diff --git a/.gitignore b/.gitignore index 04c444404e..802ce70e48 100644 --- a/.gitignore +++ b/.gitignore @@ -87,6 +87,7 @@ /git-init-db /git-interpret-trailers /git-instaweb +/git-last-modified /git-log /git-ls-files /git-ls-remote @@ -139,6 +140,7 @@ /git-repack /git-replace /git-replay +/git-repo /git-request-pull /git-rerere /git-reset diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bb6d5b976c..cf122e706f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -119,6 +119,7 @@ build:mingw64: variables: NO_PERL: 1 before_script: + - Set-MpPreference -DisableRealtimeMonitoring $true - ./ci/install-sdk.ps1 -directory "git-sdk" script: - git-sdk/usr/bin/bash.exe -l -c 'ci/make-test-artifacts.sh artifacts' @@ -135,6 +136,7 @@ test:mingw64: - job: "build:mingw64" artifacts: true before_script: + - Set-MpPreference -DisableRealtimeMonitoring $true - git-sdk/usr/bin/bash.exe -l -c 'tar xf artifacts/artifacts.tar.gz' - New-Item -Path .git/info -ItemType Directory - New-Item .git/info/exclude -ItemType File -Value "/git-sdk" @@ -148,6 +150,7 @@ test:mingw64: tags: - saas-windows-medium-amd64 before_script: + - Set-MpPreference -DisableRealtimeMonitoring $true - choco install -y git meson ninja openssl - Import-Module $env:ChocolateyInstall\helpers\chocolateyProfile.psm1 - refreshenv @@ -178,7 +181,7 @@ test:msvc-meson: - job: "build:msvc-meson" artifacts: true script: - - meson test -C build --list | Select-Object -Skip 1 | Select-String .* | Group-Object -Property { $_.LineNumber % $Env:CI_NODE_TOTAL + 1 } | Where-Object Name -EQ $Env:CI_NODE_INDEX | ForEach-Object { meson test -C build --no-rebuild --print-errorlogs $_.Group; if (!$?) { exit $LASTEXITCODE } } + - meson test -C build --no-rebuild --print-errorlogs --slice $Env:CI_NODE_INDEX/$Env:CI_NODE_TOTAL parallel: 10 test:fuzz-smoke-tests: @@ -81,6 +81,8 @@ Fredrik Kuivinen <frekui@gmail.com> <freku045@student.liu.se> Frédéric Heitzmann <frederic.heitzmann@gmail.com> Garry Dolley <gdolley@ucla.edu> <gdolley@arpnetworks.com> Glen Choo <glencbz@gmail.com> <chooglen@google.com> +Greg Hurrell <greg@hurrell.net> <greg.hurrell@datadoghq.com> +Greg Hurrell <greg@hurrell.net> <win@wincent.com> Greg Price <price@mit.edu> <price@MIT.EDU> Greg Price <price@mit.edu> <price@ksplice.com> Heiko Voigt <hvoigt@hvoigt.net> <git-list@hvoigt.net> diff --git a/Documentation/BreakingChanges.adoc b/Documentation/BreakingChanges.adoc index 61bdd586b9..0cba20fadb 100644 --- a/Documentation/BreakingChanges.adoc +++ b/Documentation/BreakingChanges.adoc @@ -118,6 +118,59 @@ Cf. <2f5de416-04ba-c23d-1e0b-83bb655829a7@zombino.com>, <20170223155046.e7nxivfwqqoprsqj@LykOS.localdomain>, <CA+EOSBncr=4a4d8n9xS4FNehyebpmX8JiUwCsXD47EQDE+DiUQ@mail.gmail.com>. +* The default storage format for references in newly created repositories will + be changed from "files" to "reftable". The "reftable" format provides + multiple advantages over the "files" format: ++ + ** It is impossible to store two references that only differ in casing on + case-insensitive filesystems with the "files" format. This issue is common + on Windows and macOS platforms. As the "reftable" backend does not use + filesystem paths to encode reference names this problem goes away. + ** Similarly, macOS normalizes path names that contain unicode characters, + which has the consequence that you cannot store two names with unicode + characters that are encoded differently with the "files" backend. Again, + this is not an issue with the "reftable" backend. + ** Deleting references with the "files" backend requires Git to rewrite the + complete "packed-refs" file. In large repositories with many references + this file can easily be dozens of megabytes in size, in extreme cases it + may be gigabytes. The "reftable" backend uses tombstone markers for + deleted references and thus does not have to rewrite all of its data. + ** Repository housekeeping with the "files" backend typically performs + all-into-one repacks of references. This can be quite expensive, and + consequently housekeeping is a tradeoff between the number of loose + references that accumulate and slow down operations that read references, + and compressing those loose references into the "packed-refs" file. The + "reftable" backend uses geometric compaction after every write, which + amortizes costs and ensures that the backend is always in a + well-maintained state. + ** Operations that write multiple references at once are not atomic with the + "files" backend. Consequently, Git may see in-between states when it reads + references while a reference transaction is in the process of being + committed to disk. + ** Writing many references at once is slow with the "files" backend because + every reference is created as a separate file. The "reftable" backend + significantly outperforms the "files" backend by multiple orders of + magnitude. + ** The reftable backend uses a binary format with prefix compression for + reference names. As a result, the format uses less space compared to the + "packed-refs" file. ++ +Users that get immediate benefit from the "reftable" backend could continue to +opt-in to the "reftable" format manually by setting the "init.defaultRefFormat" +config. But defaults matter, and we think that overall users will have a better +experience with less platform-specific quirks when they use the new backend by +default. ++ +A prerequisite for this change is that the ecosystem is ready to support the +"reftable" format. Most importantly, alternative implementations of Git like +JGit, libgit2 and Gitoxide need to support it. + +* In new repositories, the default branch name will be `main`. We have been + warning that the default name will change since 675704c74dd (init: + provide useful advice about init.defaultBranch, 2020-12-11). The new name + matches the default branch name used in new repositories by many of the + big Git forges. + === Removals * Support for grafting commits has long been superseded by git-replace(1). @@ -183,6 +236,19 @@ These features will be removed. timeframe, in preference to its synonym "--annotate-stdin". Git 3.0 removes the support for "--stdin" altogether. +* The git-whatchanged(1) command has outlived its usefulness more than + 10 years ago, and takes more keystrokes to type than its rough + equivalent `git log --raw`. We have nominated the command for + removal, have changed the command to refuse to work unless the + `--i-still-use-this` option is given, and asked the users to report + when they do so. So far there hasn't been a single complaint. ++ +The command will be removed. + +* Support for `core.commentString=auto` has been deprecated and will + be removed in Git 3.0. ++ +cf. <xmqqa59i45wc.fsf@gitster.g> == Superseded features that will not be deprecated diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines index c1046abfb7..df72fe0177 100644 --- a/Documentation/CodingGuidelines +++ b/Documentation/CodingGuidelines @@ -298,6 +298,17 @@ For C programs: . since late 2021 with 44ba10d6, we have had variables declared in the for loop "for (int i = 0; i < 10; i++)". + . since late 2023 with 8277dbe987 we have been using the bool type + from <stdbool.h>. + + C99 features we have test balloons for: + + . since late 2024 with v2.48.0-rc0~20, we have test balloons for + compound literal syntax, e.g., (struct foo){ .member = value }; + our hope is that no platforms we care about have trouble using + them, and officially adopt its wider use in mid 2026. Do not add + more use of the syntax until that happens. + New C99 features that we cannot use yet: . %z and %zu as a printf() argument for a size_t (the %z being for @@ -315,6 +326,9 @@ For C programs: encouraged to have a blank line between the end of the declarations and the first statement in the block. + - Do not explicitly initialize global variables to 0 or NULL; + instead, let BSS take care of the zero initialization. + - NULL pointers shall be written as NULL, not as 0. - When declaring pointers, the star sides with the variable @@ -610,8 +624,9 @@ For C programs: - `S_init()` initializes a structure without allocating the structure itself. - - `S_release()` releases a structure's contents without freeing the - structure. + - `S_release()` releases a structure's contents without reinitializing + the structure for immediate reuse, and without freeing the structure + itself. - `S_clear()` is equivalent to `S_release()` followed by `S_init()` such that the structure is directly usable after clearing it. When @@ -635,6 +650,12 @@ For C programs: cases. However, it is recommended to find a more descriptive name wherever possible to improve the readability and maintainability of the code. + - Bit fields should be defined without a space around the colon. E.g. + + unsigned my_field:1; + unsigned other_field:1; + unsigned field_with_longer_name:1; + For Perl programs: - Most of the C guidelines above apply. @@ -877,6 +898,17 @@ Characters are also surrounded by underscores: As a side effect, backquoted placeholders are correctly typeset, but this style is not recommended. + When documenting multiple related `git config` variables, place them on + a separate line instead of separating them by commas. For example, do + not write this: + `core.var1`, `core.var2`:: + Description common to `core.var1` and `core.var2`. + +Instead write this: + `core.var1`:: + `core.var2`:: + Description common to `core.var1` and `core.var2`. + Synopsis Syntax The synopsis (a paragraph with [synopsis] attribute) is automatically diff --git a/Documentation/Makefile b/Documentation/Makefile index b109d25e9c..6fb83d0c6e 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -497,9 +497,26 @@ $(LINT_DOCS_FSCK_MSGIDS): ../fsck.h fsck-msgids.adoc $(call mkdir_p_parent_template) $(QUIET_GEN)$(PERL_PATH) lint-fsck-msgids.perl \ ../fsck.h fsck-msgids.adoc $@ - lint-docs-fsck-msgids: $(LINT_DOCS_FSCK_MSGIDS) +## Lint: delimited sections +LINT_DOCS_DELIMITED_SECTIONS = $(patsubst %.adoc,.build/lint-docs/delimited-sections/%.ok,$(MAN_TXT)) +$(LINT_DOCS_DELIMITED_SECTIONS): lint-delimited-sections.perl +$(LINT_DOCS_DELIMITED_SECTIONS): .build/lint-docs/delimited-sections/%.ok: %.adoc + $(call mkdir_p_parent_template) + $(QUIET_LINT_DELIMSEC)$(PERL_PATH) lint-delimited-sections.perl $< >$@ +.PHONY: lint-docs-delimited-sections +lint-docs-delimited-sections: $(LINT_DOCS_DELIMITED_SECTIONS) + +## Lint: Documentation style +LINT_DOCS_DOC_STYLE = $(patsubst %.adoc,.build/lint-docs/doc-style/%.ok,$(DOC_DEP_TXT)) +$(LINT_DOCS_DOC_STYLE): lint-documentation-style.perl +$(LINT_DOCS_DOC_STYLE): .build/lint-docs/doc-style/%.ok: %.adoc + $(call mkdir_p_parent_template) + $(QUIET_LINT_DOCSTYLE)$(PERL_PATH) lint-documentation-style.perl $< >$@ +.PHONY: lint-docs-doc-style +lint-docs-doc-style: $(LINT_DOCS_DOC_STYLE) + lint-docs-manpages: $(QUIET_GEN)./lint-manpages.sh @@ -510,7 +527,12 @@ lint-docs-meson: awk "/^manpages = {$$/ {flag=1 ; next } /^}$$/ { flag=0 } flag { gsub(/^ \047/, \"\"); gsub(/\047 : [157],\$$/, \"\"); print }" meson.build | \ grep -v -e '#' -e '^$$' | \ sort >tmp-meson-diff/meson.adoc && \ - ls git*.adoc scalar.adoc | grep -v -e git-bisect-lk2009.adoc -e git-pack-redundant.adoc -e git-tools.adoc >tmp-meson-diff/actual.adoc && \ + ls git*.adoc scalar.adoc | \ + grep -v -e git-bisect-lk2009.adoc \ + -e git-pack-redundant.adoc \ + -e git-tools.adoc \ + -e git-whatchanged.adoc \ + >tmp-meson-diff/actual.adoc && \ if ! cmp tmp-meson-diff/meson.adoc tmp-meson-diff/actual.adoc; then \ echo "Meson man pages differ from actual man pages:"; \ diff -u tmp-meson-diff/meson.adoc tmp-meson-diff/actual.adoc; \ @@ -523,6 +545,8 @@ lint-docs: lint-docs-fsck-msgids lint-docs: lint-docs-gitlink lint-docs: lint-docs-man-end-blurb lint-docs: lint-docs-man-section-order +lint-docs: lint-docs-delimited-sections +lint-docs: lint-docs-doc-style lint-docs: lint-docs-manpages lint-docs: lint-docs-meson diff --git a/Documentation/MyFirstContribution.adoc b/Documentation/MyFirstContribution.adoc index aca7212cfe..02ba8ba5f6 100644 --- a/Documentation/MyFirstContribution.adoc +++ b/Documentation/MyFirstContribution.adoc @@ -52,6 +52,15 @@ respond to you. It's better to ask your questions in the channel so that you can be answered if you disconnect and so that others can learn from the conversation. +==== https://discord.gg/GRFVkzgxRd[#discord] on Discord +This is an unofficial Git Discord server for everyone, from people just +starting out with Git to those who develop it. It's a great place to ask +questions, share tips, and connect with the broader Git community in real time. + +The server has channels for general discussions and specific channels for those +who use Git and those who develop it. The server's search functionality also +allows you to find previous conversations and answers to common questions. + [[getting-started]] == Getting Started @@ -908,10 +917,13 @@ Now you should be able to go and check out your newly created branch on GitHub. === Sending a PR to GitGitGadget In order to have your code tested and formatted for review, you need to start by -opening a Pull Request against `gitgitgadget/git`. Head to -https://github.com/gitgitgadget/git and open a PR either with the "New pull -request" button or the convenient "Compare & pull request" button that may -appear with the name of your newly pushed branch. +opening a Pull Request against either `gitgitgadget/git` or `git/git`. Head to +https://github.com/gitgitgadget/git or https://github.com/git/git and open a PR +either with the "New pull request" button or the convenient "Compare & pull +request" button that may appear with the name of your newly pushed branch. + +The differences between using `gitgitgadget/git` and `git/git` as your base can +be found [here](https://gitgitgadget.github.io/#should-i-use-gitgitgadget-on-gitgitgadgets-git-fork-or-on-gits-github-mirror) Review the PR's title and description, as they're used by GitGitGadget respectively as the subject and body of the cover letter for your change. Refer diff --git a/Documentation/MyFirstObjectWalk.adoc b/Documentation/MyFirstObjectWalk.adoc index bfe8f5f561..413a9fdb05 100644 --- a/Documentation/MyFirstObjectWalk.adoc +++ b/Documentation/MyFirstObjectWalk.adoc @@ -43,7 +43,7 @@ Open up a new file `builtin/walken.c` and set up the command handler: #include "builtin.h" #include "trace.h" -int cmd_walken(int argc, const char **argv, const char *prefix) +int cmd_walken(int argc, const char **argv, const char *prefix, struct repository *repo) { trace_printf(_("cmd_walken incoming...\n")); return 0; @@ -83,23 +83,36 @@ int cmd_walken(int argc, const char **argv, const char *prefix) } ---- -Also add the relevant line in `builtin.h` near `cmd_whatchanged()`: +Also add the relevant line in `builtin.h` near `cmd_version()`: ---- -int cmd_walken(int argc, const char **argv, const char *prefix); +int cmd_walken(int argc, const char **argv, const char *prefix, struct repository *repo); ---- -Include the command in `git.c` in `commands[]` near the entry for `whatchanged`, +Include the command in `git.c` in `commands[]` near the entry for `version`, maintaining alphabetical ordering: ---- { "walken", cmd_walken, RUN_SETUP }, ---- -Add it to the `Makefile` near the line for `builtin/worktree.o`: +Add an entry for the new command in the both the Make and Meson build system, +before the entry for `worktree`: +- In the `Makefile`: ---- +... BUILTIN_OBJS += builtin/walken.o +... +---- + +- In the `meson.build` file: +---- +builtin_sources = [ + ... + 'builtin/walken.c', + ... +] ---- Build and test out your command, without forgetting to ensure the `DEVELOPER` @@ -193,7 +206,7 @@ initialization functions. Next, we should have a look at any relevant configuration settings (i.e., settings readable and settable from `git config`). This is done by providing a -callback to `git_config()`; within that callback, you can also invoke methods +callback to `repo_config()`; within that callback, you can also invoke methods from other components you may need that need to intercept these options. Your callback will be invoked once per each configuration value which Git knows about (global, local, worktree, etc.). @@ -221,14 +234,14 @@ static int git_walken_config(const char *var, const char *value, } ---- -Make sure to invoke `git_config()` with it in your `cmd_walken()`: +Make sure to invoke `repo_config()` with it in your `cmd_walken()`: ---- -int cmd_walken(int argc, const char **argv, const char *prefix) +int cmd_walken(int argc, const char **argv, const char *prefix, struct repository *repo) { ... - git_config(git_walken_config, NULL); + repo_config(repo, git_walken_config, NULL); ... } @@ -250,14 +263,14 @@ We'll also need to include the `revision.h` header: ... -int cmd_walken(int argc, const char **argv, const char *prefix) +int cmd_walken(int argc, const char **argv, const char *prefix, struct repository *repo) { /* This can go wherever you like in your declarations.*/ struct rev_info rev; ... - /* This should go after the git_config() call. */ - repo_init_revisions(the_repository, &rev, prefix); + /* This should go after the repo_config() call. */ + repo_init_revisions(repo, &rev, prefix); ... } @@ -305,7 +318,7 @@ Then let's invoke `final_rev_info_setup()` after the call to `repo_init_revisions()`: ---- -int cmd_walken(int argc, const char **argv, const char *prefix) +int cmd_walken(int argc, const char **argv, const char *prefix, struct repository *repo) { ... diff --git a/Documentation/RelNotes/1.6.2.4.adoc b/Documentation/RelNotes/1.6.2.4.adoc index f4bf1d0986..053dbb604d 100644 --- a/Documentation/RelNotes/1.6.2.4.adoc +++ b/Documentation/RelNotes/1.6.2.4.adoc @@ -37,3 +37,4 @@ exec >/var/tmp/1 echo O=$(git describe maint) O=v1.6.2.3-38-g318b847 git shortlog --no-merges $O..maint +--- diff --git a/Documentation/RelNotes/2.51.0.adoc b/Documentation/RelNotes/2.51.0.adoc new file mode 100644 index 0000000000..a73ea3e808 --- /dev/null +++ b/Documentation/RelNotes/2.51.0.adoc @@ -0,0 +1,341 @@ +Git v2.51 Release Notes +======================= + +UI, Workflows & Features +------------------------ + + * Userdiff patterns for the R language have been added. + + * Documentation for "git send-email" has been updated with a bit more + credential helper and OAuth information. + + * "git cat-file --batch" learns to understand %(objectmode) atom to + allow the caller to tell missing objects (due to repository + corruption) and submodules (whose commit objects are OK to be + missing) apart. + + * "git diff --no-index dirA dirB" can limit the comparison with + pathspec at the end of the command line, just like normal "git + diff". + + * "git subtree" (in contrib/) learned to grok GPG signing its commits. + + * "git whatchanged" that is longer to type than "git log --raw" + which is its modern rough equivalent has outlived its usefulness + more than 10 years ago. Plan to deprecate and remove it. + + * An interchange format for stash entries is defined, and subcommand + of "git stash" to import/export has been added. + + * "git merge/pull" has been taught the "--compact-summary" option to + use the compact-summary format, intead of diffstat, when showing + the summary of the incoming changes. + + * "git imap-send" has been broken for a long time, which has been + resurrected and then taught to talk OAuth2.0 etc. + + * Some error messages from "git imap-send" has been updated. + + * When "git daemon" sees a signal while attempting to accept() a new + client, instead of retrying, it skipped it by mistake, which has + been corrected. + + * The reftable ref backend has matured enough; Git 3.0 will make it + the default format in a newly created repositories by default. + + * "netrc" credential helper has been improved to understand textual + service names (like smtp) in addition to the numeric port numbers + (like 25). + + * Lift the limitation to use changed-path filter in "git log" so that + it can be used for a pathspec with multiple literal paths. + + * Clean up the way how signature on commit objects are exported to + and imported from fast-import stream. + + * Remove unsupported, unused, and unsupportable old option from "git + log". + + * Document recently added "git imap-send --list" with an example. + + * "git pull" learned to pay attention to pull.autostash configuration + variable, which overrides rebase/merge.autostash. + + * "git for-each-ref" learns "--start-after" option to help + applications that want to page its output. + + * "git switch" and "git restore" are declared to be no longer + experimental. + + * "git -c alias.foo=bar foo -h baz" reported "'foo' is aliased to + 'bar'" and then went on to run "git foo -h baz", which was + unexpected. Tighten the rule so that alias expansion is reported + only when "-h" is the sole option. + + +Performance, Internal Implementation, Development Support etc. +-------------------------------------------------------------- + + * "git pack-objects" learned to find delta bases from blobs at the + same path, using the --path-walk API. + + * CodingGuidelines update. + + * Add settings for Solaris 10 & 11. + + * Meson-based build/test framework now understands TAP output + generated by our tests. + + * "Do not explicitly initialize to zero" rule has been clarified in + the CodingGuidelines document. + + * A test helper "test_seq" function learned the "-f <fmt>" option, + which allowed us to simplify a lot of test scripts. + + * A lot of stale stuff has been removed from the contrib/ hierarchy. + + * "git push" and "git fetch" are taught to update refs in batches to + gain performance. + + * Some code paths in "git prune" used to ignore the passed-in + repository object and used the `the_repository` singleton instance + instead, which has been corrected. + + * Update ".clang-format" and ".editorconfig" to match our style guide + a bit better. + + * "make coccicheck" succeeds even when spatch made suggestions, which + has been updated to fail in such a case. + + * Code clean-up around object access API. + + * Define .precision to more canned parse-options type to avoid bugs + coming from using a variable with a wrong type to capture the + parsed values. + + * Flipping the default hash function to SHA-256 at Git 3.0 boundary + is planned. + + * Declare weather-balloon we raised for "bool" type 18 months ago a + success and officially allow using the type in our codebase. + + * GIT_TEST_INSTALLED was not honored in the recent topic related to + SHA256 hashes, which has been corrected. + + * The pop_most_recent_commit() function can have quite expensive + worst case performance characteristics, which has been optimized by + using prio-queue data structure. + + * Move structure definition from unrelated header file to where it + belongs. + + * To help our developers, document what C99 language features are + being considered for adoption, in addition to what past experiments + have already decided. + + * The reftable unit tests are now ported to the "clar" unit testing + framework. + + * Redefine where the multi-pack-index sits in the object subsystem, + which recently was restructured to allow multiple backends that + support a single object source that belongs to one repository. A + MIDX does span multiple "object sources". + + * Reduce implicit assumption and dependence on the_repository in the + object-file subsystem. + + +Fixes since v2.50 +----------------- + +Unless otherwise noted, all the changes in 2.50.X maintenance track, +including security updates, are included in this release. + + * A memory-leak in an error code path has been plugged. + (merge 7082da85cb ly/commit-graph-graph-write-leakfix later to maint). + + * A memory-leak in an error code path has been plugged. + (merge aedebdb6b9 ly/fetch-pack-leakfix later to maint). + + * Some leftover references to documentation source files that no + longer exist, due to recent ".txt" -> ".adoc" renaming, have been + corrected. + (merge 3717a5775a jw/doc-txt-to-adoc-refs later to maint). + + * "git stash -p <pathspec>" improvements. + (merge 468817bab2 pw/stash-p-pathspec-fixes later to maint). + + * "git send-email" incremented its internal message counter when a + message was edited, which made logic that treats the first message + specially misbehave, which has been corrected. + (merge 2cc27b3501 ag/send-email-edit-threading-fix later to maint). + + * "git stash" recorded a wrong branch name when submodules are + present in the current checkout, which has been corrected. + (merge ffb36c64f2 kj/stash-onbranch-submodule-fix later to maint). + + * When asking to apply mailmap to both author and committer field + while showing a commit object, the field that appears later was not + correctly parsed and replaced, which has been corrected. + (merge abf94a283f sa/multi-mailmap-fix later to maint). + + * "git maintenance" lacked the care "git gc" had to avoid holding + onto the repository lock for too long during packing refs, which + has been remedied. + (merge 1b5074e614 ps/maintenance-ref-lock later to maint). + + * Avoid regexp_constraint and instead use comparison_constraint when + listing functions to exclude from application of coccinelle rules, + as spatch can be built with different regexp engine X-<. + (merge f2ad545813 jc/cocci-avoid-regexp-constraint later to maint). + + * Updating submodules from the upstream did not work well when + submodule's HEAD is detached, which has been improved. + (merge ca62f524c1 jk/submodule-remote-lookup-cleanup later to maint). + + * Remove unnecessary check from "git daemon" code. + (merge 0c856224d2 cb/daemon-fd-check-fix later to maint). + + * Use of sysctl() system call to learn the total RAM size used on + BSDs has been corrected. + (merge 781c1cf571 cb/total-ram-bsd-fix later to maint). + + * Drop FreeBSD 4 support and declare that we support only FreeBSD 12 + or later, which has memmem() supported. + (merge 0392f976a7 bs/config-mak-freebsd later to maint). + + * A diff-filter with negative-only specification like "git log + --diff-filter=d" did not trigger correctly, which has been fixed. + (merge 375ac087c5 jk/all-negative-diff-filter-fix later to maint). + + * A failure to open the index file for writing due to conflicting + access did not state what went wrong, which has been corrected. + (merge 9455397a5c hy/read-cache-lock-error-fix later to maint). + + * Tempfile removal fix in the codepath to sign commits with SSH keys. + (merge 4498127b04 re/ssh-sign-buffer-fix later to maint). + + * Code and test clean-up around string-list API. + (merge 6e5b26c3ff sj/string-list later to maint). + + * "git apply -N" should start from the current index and register + only new files, but it instead started from an empty index, which + has been corrected. + (merge 2b49d97fcb rp/apply-intent-to-add-fix later to maint). + + * Leakfix with a new and a bit invasive test on pack-bitmap files. + (merge bfd5522e98 ly/load-bitmap-leakfix later to maint). + + * "git fetch --prune" used to be O(n^2) expensive when there are many + refs, which has been corrected. + (merge 87d8d8c5d0 ph/fetch-prune-optim later to maint). + + * When a ref creation at refs/heads/foo/bar fails, the files backend + now removes refs/heads/foo/ if the directory is otherwise not used. + (merge a3a7f20516 ps/refs-files-remove-empty-parent later to maint). + + * "pack-objects" has been taught to avoid pointing into objects in + cruft packs from midx. + + * "git remote" now detects remote names that overlap with each other + (e.g., remote nickname "outer" and "outer/inner" are used at the + same time), as it will lead to overlapping remote-tracking + branches. + (merge a5a727c448 jk/remote-avoid-overlapping-names later to maint). + + * The gpg.program configuration variable, which names a pathname to + the (custom) GPG compatible program, can now be spelled with ~tilde + expansion. + (merge 7d275cd5c0 jb/gpg-program-variable-is-a-pathname later to maint). + + * Our <sane-ctype.h> header file relied on that the system-supplied + <ctype.h> header is not later included, which would override our + macro definitions, but "amazon linux" broke this assumption. Fix + this by preemptively including <ctype.h> near the beginning of + <sane-ctype.h> ourselves. + (merge 9d3b33125f ps/sane-ctype-workaround later to maint). + + * Clean-up compat/bswap.h mess. + (merge f4ac32c03a ss/compat-bswap-revamp later to maint). + + * Meson-based build did not handle libexecdir setting correctly, + which has been corrected. + (merge 056dbe8612 rj/meson-libexecdir-fix later to maint). + + * Document that we do not require "real" name when signing your + patches off. + (merge 1f0fed312a bc/contribution-under-non-real-names later to maint). + + * "git commit" that concludes a conflicted merge failed to notice and remove + existing comment added automatically (like "# Conflicts:") when the + core.commentstring is set to 'auto'. + (merge 92b7c7c9f5 ac/auto-comment-char-fix later to maint). + + * "git rebase -i" with bogus rebase.instructionFormat configuration + failed to produce the todo file after recording the state files, + leading to confused "git status"; this has been corrected. + (merge ade14bffd7 ow/rebase-verify-insn-fmt-before-initializing-state later to maint). + + * A few file descriptors left unclosed upon program completion in a + few test helper programs are now closed. + (merge 0f1b33815b hl/test-helper-fd-close later to maint). + + * Interactive prompt code did not correctly strip CRLF from the end + of line on Windows. + (merge 711a20827b js/prompt-crlf-fix later to maint). + + * The config API had a set of convenience wrapper functions that + implicitly use the_repository instance; they have been removed and + inlined at the calling sites. + + * "git add/etc -p" now honor the diff.context configuration variable, + and also they learn to honor the -U<n> command-line option. + (merge 2b3ae04011 lm/add-p-context later to maint). + + * The case where a new submodule takes a path where there used to be a + completely different subproject is now dealt with a bit better than + before. + (merge 5ed8c5b465 kj/renamed-submodule later to maint). + + * The deflate codepath in "git archive --format=zip" had a + longstanding bug coming from misuse of zlib API, which has been + corrected. + + * Other code cleanup, docfix, build fix, etc. + (merge b257adb571 lo/my-first-ow-doc-update later to maint). + (merge 8b34b6a220 ly/sequencer-update-squash-is-fixup-only later to maint). + (merge 5dceb8bd05 ly/do-not-localize-bug-messages later to maint). + (merge 61372dd613 ly/commit-buffer-reencode-leakfix later to maint). + (merge 81cd1eef7d ly/pack-bitmap-root-leakfix later to maint). + (merge bfc9f9cc64 ly/submodule-update-failure-leakfix later to maint). + (merge 65dff89c6b ma/doc-diff-cc-headers later to maint). + (merge efb61591ee jm/bundle-uri-debug-output-to-fp later to maint). + (merge a3d278bb64 ly/prepare-show-merge-leakfix later to maint). + (merge 1fde1c5daf ac/preload-index-wo-the-repository later to maint). + (merge 855cfc65ae rm/t2400-modernize later to maint). + (merge 2939494284 ly/run-builtin-use-passed-in-repo later to maint). + (merge ff73f375bb jg/mailinfo-leakfix later to maint). + (merge 996f14c02b jj/doc-branch-markup-fix later to maint). + (merge 1e77de1864 cb/ci-freebsd-update-to-14.3 later to maint). + (merge b0e9d25865 jk/fix-leak-send-pack later to maint). + (merge f3a9558c8c bs/remote-helpers-doc-markup-fix later to maint). + (merge c4e9775c60 kh/doc-config-subcommands later to maint). + (merge de404249ab ps/perlless-test-fixes later to maint). + (merge 953049eed8 ts/merge-orig-head-doc-fix later to maint). + (merge 0c83bbc704 rj/freebsd-sysinfo-build-fix later to maint). + (merge ad7780b38f ps/doc-pack-refs-auto-with-files-backend-fix later to maint). + (merge f4fa8a3687 rh/doc-glob-pathspec-fix later to maint). + (merge b27be108c8 ja/doc-git-log-markup later to maint). + (merge 14d7583beb pw/config-kvi-remove-path later to maint). + (merge f31abb421d jc/do-not-scan-argv-without-parsing later to maint). + (merge 26552cb62a jk/unleak-reflog-expire-entry later to maint). + (merge 339d95fda9 jc/ci-print-test-failures-fix later to maint). + (merge 8c3add51a8 cb/meson-avoid-broken-macos-pcre2 later to maint). + (merge 5247da07b8 ps/meson-clar-decls-fix later to maint). + (merge f3ef347bb2 ch/t7450-recursive-clone-test-fix later to maint). + (merge 4ac3302a1a jc/doc-release-vs-clear later to maint). + (merge 3bdd897413 ms/meson-with-ancient-git-wo-ls-files-dedup later to maint). + (merge cca758d324 kh/doc-fast-import-historical later to maint). + (merge 9b0781196a jc/test-hashmap-is-still-here later to maint). + (merge 1bad05bacc jk/revert-squelch-compiler-warning later to maint). + (merge 3a7e783d9c dl/squelch-maybe-uninitialized later to maint). diff --git a/Documentation/RelNotes/2.52.0.adoc b/Documentation/RelNotes/2.52.0.adoc new file mode 100644 index 0000000000..eae371f239 --- /dev/null +++ b/Documentation/RelNotes/2.52.0.adoc @@ -0,0 +1,231 @@ +Git v2.52 Release Notes +======================= + +UI, Workflows & Features +------------------------ + + * The "list" subcommand of "git refs" acts as a front-end for + "git for-each-ref". + + * "git cmd --help-all" now works outside repositories. + + * "git diff-tree" learned "--max-depth" option. + + * A new subcommand "git repo" gives users a way to grab various + repository characteristics. + + * A new command "git last-modified" has been added to show the closest + ancestor commit that touched each path. + + * "git refs exists" that works like "git show-ref --exists" has been + added. + + * "repo info" learns a short-hand option "-z" that is the same as + "--format=nul", and learns to report the objects format used in the + repository. + + * "core.commentChar=auto" that attempts to dynamically pick a + suitable comment character is non-workable, as it is too much + trouble to support for little benefit, and is marked as deprecated. + + * "git send-email" learned to drive "git imap-send" to store already + sent e-mails in an IMAP folder. + + +Performance, Internal Implementation, Development Support etc. +-------------------------------------------------------------- + + * string_list_split*() family of functions have been extended to + simplify common use cases. + + * Arrays of strbuf is often a wrong data structure to use, and + strbuf_split*() family of functions that create them often have + better alternatives. Update several code paths and replace + strbuf_split*(). + + * Revision traversal limited with pathspec, like "git log dir/*", + used to ignore changed-paths Bloom filter when the pathspec + contained wildcards; now they take advantage of the filter when + they can. + + * Doc lint updates to encourage the newer and easier-to-use + `synopsis` format, with fixes to a handful of existing uses. + + * Remove dependency on the_repository and other globals from the + commit-graph code, and other changes unrelated to de-globaling. + + * Discord has been added to the first contribution documentation as + another way to ask for help. + + * Inspired by Ezekiel's recent effort to showcase Rust interface, the + hash function implementation used to hash lines have been updated + to the one used for ELF symbol lookup by Glibc. + + * Instead of scanning for the remaining items to see if there are + still commits to be explored in the queue, use khash to remember + which items are still on the queue (an unacceptable alternative is + to reserve one object flag bits). + + * The bulk-checkin code used to depend on a file-scope static + singleton variable, which has been updated to pass an instance + throughout the callchain. + + * CodingGuidelines now spells out how bitfields are to be written. + + * Adjust to the way newer versions of cURL selectivel enables tracing + options, so that our tests can continue to work. + (merge 1b5a6bfff3 jk/curl-global-trace-components later to maint). + + * The clear_alloc_state() API function was not fully clearing the + structure for reuse, but since nobody reuses it, replace it with a + variant that frees the structure as well, making the callers simpler. + + * "git range-diff" learned a way to limit the memory consumed by + O(N*N) cost matrix. + + +Fixes since v2.51 +----------------- + +Unless otherwise noted, all the changes in 2.51.X maintenance track, +including security updates, are included in this release. + + * During interactive rebase, using 'drop' on a merge commit lead to + an error, which was incorrect. + (merge 4d491ade8f js/rebase-i-allow-drop-on-a-merge later to maint). + + * "git refs migrate" to migrate the reflog entries from a refs + backend to another had a handful of bugs squashed. + (merge 465eff81de ps/reflog-migrate-fixes later to maint). + + * "git remote rename origin upstream" failed to move origin/HEAD to + upstream/HEAD when origin/HEAD is unborn and performed other + renames extremely inefficiently, which has been corrected. + (merge 16c4fa26b9 ps/remote-rename-fix later to maint). + + * "git describe" has been optimized by using better data structure. + (merge 08bb69d70f rs/describe-with-prio-queue later to maint). + + * "git push" had a code path that led to BUG() but it should have + been a die(), as it is a response to a usual but invalid end-user + action to attempt pushing an object that does not exist. + (merge dfbfc2221b dl/push-missing-object-error later to maint). + + * Various bugs about rename handling in "ort" merge strategy have + been fixed. + (merge f6ecb603ff en/ort-rename-fixes later to maint). + + * "git jump" (in contrib/) fails to parse the diff header correctly + when a file has a space in its name, which has been corrected. + (merge 621ce9c1c6 gh/git-jump-pathname-with-sp later to maint). + + * "git diff --no-index" run inside a subdirectory under control of a + Git repository operated at the top of the working tree and stripped + the prefix from the output, and oddballs like "-" (stdin) did not + work correctly because of it. Correct the set-up by undoing what + the set-up sequence did to cwd and prefix. + (merge e1d3d61a45 jc/diff-no-index-in-subdir later to maint). + + * Various options to "git diff" that makes comparison ignore certain + aspects of the differences (like "space changes are ignored", + "differences in lines that match these regular expressions are + ignored") did not work well with "--name-only" and friends. + (merge b55e6d36eb ly/diff-name-only-with-diff-from-content later to maint). + + * Documentation for "git rebase" has been updated. + (merge 3f7f2b0359 je/doc-rebase later to maint). + + * The start_delayed_progress() function in the progress eye-candy API + did not clear its internal state, making an initial delay value + larger than 1 second ineffective, which has been corrected. + (merge 457534d041 js/progress-delay-fix later to maint). + + * The compatObjectFormat extension is used to hide an incomplete + feature that is not yet usable for any purpose other than + developing the feature further. Document it as such to discourage + its use by mere mortals. + (merge 716d905792 bc/doc-compat-object-format-not-working later to maint). + + * "git log -L..." compared trees of multiple parents with the tree of the + merge result in an unnecessarily inefficient way. + (merge 0a15bb634c sg/line-log-merge-optim later to maint). + + * Under a race against another process that is repacking the + repository, especially a partially cloned one, "git fetch" may + mistakenly think some objects we do have are missing, which has + been corrected. + (merge 8f32a5a6c0 jk/fetch-check-graph-objects-fix later to maint). + + * "git fetch" can clobber a symref that is dangling when the + remote-tracking HEAD is set to auto update, which has been + corrected. + + * "git describe <blob>" misbehaves and/or crashes in some corner + cases, which has been taught to exit with failure gracefully. + (merge 7c10e48e81 jk/describe-blob later to maint). + + * Manual page for "gitk" is updated with the current maintainer's + name. + (merge bcb20dda83 js/doc-gitk-history later to maint). + + * Update the instruction to use of GGG in the MyFirstContribution + document to say that a GitHub PR could be made against `git/git` + instead of `gitgitgadget/git`. + (merge 37001cdbc4 ds/doc-ggg-pr-fork-clarify later to maint). + + * Makefile tried to run multiple "cargo build" which would not work + very well; serialize their execution to work it around. + (merge 0eeacde50e da/cargo-serialize later to maint). + + * "git repack --path-walk" lost objects in some corner cases, which + has been corrected. + (merge 93afe9b060 ds/path-walk-repack-fix later to maint). + + * "git ls-files <pathspec>..." should not necessarily have to expand + the index fully if a sparsified directory is excluded by the + pathspec; the code is taught to expand the index on demand to avoid + this. + (merge 681f26bccc ds/ls-files-lazy-unsparse later to maint). + + * Windows "real-time monitoring" interferes with the execution of + tests and affects negatively in both correctness and performance, + which has been disabled in Gitlab CI. + (merge 608cf5b793 ps/gitlab-ci-disable-windows-monitoring later to maint). + + * A broken or malicious "git fetch" can say that it has the same + object for many many times, and the upload-pack serving it can + exhaust memory storing them redundantly, which has been corrected. + (merge 88a2dc68c8 ps/upload-pack-oom-protection later to maint). + + * A corner case bug in "git log -L..." has been corrected. + (merge e3106998ff sg/line-log-boundary-fixes later to maint). + + * "git rev-parse --short" and friends failed to disambiguate two + objects with object names that share common prefix longer than 32 + characters, which has been fixed. + (merge 8655908b9e jc/longer-disambiguation-fix later to maint). + + * Other code cleanup, docfix, build fix, etc. + (merge 823d537fa7 kh/doc-git-log-markup-fix later to maint). + (merge cf7efa4f33 rj/t6137-cygwin-fix later to maint). + (merge 529a60a885 ua/t1517-short-help-tests later to maint). + (merge 22d421fed9 ac/deglobal-fmt-merge-log-config later to maint). + (merge 741f36c7d9 kr/clone-synopsis-fix later to maint). + (merge a60203a015 dk/t7005-editor-updates later to maint). + (merge 7d4a5fef7d ds/doc-count-objects-fix later to maint). + (merge 16684b6fae ps/reftable-libgit2-cleanup later to maint). + (merge f38786baa7 ja/asciidoc-doctor-verbatim-fixes later to maint). + (merge 374579c6d4 kh/doc-interpret-trailers-markup-fix later to maint). + (merge 44dce6541c kh/doc-config-typofix later to maint). + (merge 785628b173 js/doc-sending-patch-via-thunderbird later to maint). + (merge e5c27bd3d8 je/doc-add later to maint). + (merge 13296ac909 ps/object-store-midx-dedup-info later to maint). + (merge 2f4bf83ffc km/alias-doc-markup-fix later to maint). + (merge b0d97aac19 kh/doc-markup-fixes later to maint). + (merge f9a6705d9a tc/t0450-harden later to maint). + (merge c25651aefd ds/midx-write-fixes later to maint). + (merge 069c15d256 rs/object-name-extend-abbrev-len-update later to maint). + (merge bf5c224537 mm/worktree-doc-typofix later to maint). + (merge 31397bc4f7 kh/doc-fast-import-markup-fix later to maint). + (merge ac7096723b jc/doc-includeif-hasconfig-remote-url-fix later to maint). + (merge fafc9b08b8 ag/doc-sendmail-gmail-example-update later to maint). diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches index 958e3cc3d5..86ca7f6a78 100644 --- a/Documentation/SubmittingPatches +++ b/Documentation/SubmittingPatches @@ -408,8 +408,15 @@ your patch differs from project to project, so it may be different from that of the project you are accustomed to. [[real-name]] -Also notice that a real name is used in the `Signed-off-by` trailer. Please -don't hide your real name. +Please use a known identity in the `Signed-off-by` trailer, since we cannot +accept anonymous contributions. It is common, but not required, to use some form +of your real name. We realize that some contributors are not comfortable doing +so or prefer to contribute under a pseudonym or preferred name and we can accept +your patch either way, as long as the name and email you use are distinctive, +identifying, and not misleading. + +The goal of this policy is to allow us to have sufficient information to contact +you if questions arise about your contribution. [[commit-trailers]] If you like, you can put extra trailers at the end: diff --git a/Documentation/asciidoc.conf.in b/Documentation/asciidoc.conf.in index 9d9139306e..ff9ea0a294 100644 --- a/Documentation/asciidoc.conf.in +++ b/Documentation/asciidoc.conf.in @@ -43,7 +43,7 @@ ifdef::doctype-book[] endif::doctype-book[] [literal-inlinemacro] -{eval:re.sub(r'(<[-a-zA-Z0-9.]+>)', r'<emphasis>\1</emphasis>', re.sub(r'([\[\s|()>]|^|\]|>)(\.?([-a-zA-Z0-9:+=~@\\\*\/_^\$]+\.?)+|,)',r'\1<literal>\2</literal>', re.sub(r'(\.\.\.?)([^\]$.])', r'<literal>\1</literal>\2', macros.passthroughs[int(attrs['passtext'][1:-1])] if attrs['passtext'][1:-1].isnumeric() else attrs['passtext'][1:-1])))} +{eval:re.sub(r'(<[-a-zA-Z0-9.]+>)', r'<emphasis>\1</emphasis>', re.sub(r'([\[\s|()>]|^|\]|>)(\.?([-a-zA-Z0-9:+=~@\\\*\/_^\$%]+\.?)+|,)',r'\1<literal>\2</literal>', re.sub(r'(\.\.\.?)([^\]$.])', r'<literal>\1</literal>\2', macros.passthroughs[int(attrs['passtext'][1:-1])] if attrs['passtext'][1:-1].isnumeric() else attrs['passtext'][1:-1])))} endif::backend-docbook[] diff --git a/Documentation/asciidoctor-extensions.rb.in b/Documentation/asciidoctor-extensions.rb.in index 8b7b161349..fe64a62d96 100644 --- a/Documentation/asciidoctor-extensions.rb.in +++ b/Documentation/asciidoctor-extensions.rb.in @@ -73,7 +73,7 @@ module Git elsif type == :monospaced node.text.gsub(/(\.\.\.?)([^\]$\.])/, '<literal>\1</literal>\2') .gsub(/^\.\.\.?$/, '<literal>\0</literal>') - .gsub(%r{([\[\s|()>.]|^|\]|>)(\.?([-a-zA-Z0-9:+=~@/_^\$\\\*]+\.{0,2})+|,)}, '\1<literal>\2</literal>') + .gsub(%r{([\[\s|()>.]|^|\]|>)(\.?([-a-zA-Z0-9:+=~@/_^\$\\\*%]+\.{0,2})+|,)}, '\1<literal>\2</literal>') .gsub(/(<[-a-zA-Z0-9.]+>)/, '<emphasis>\1</emphasis>') else open, close, supports_phrase = QUOTE_TAGS[type] @@ -102,7 +102,7 @@ module Git if node.type == :monospaced node.text.gsub(/(\.\.\.?)([^\]$.])/, '<code>\1</code>\2') .gsub(/^\.\.\.?$/, '<code>\0</code>') - .gsub(%r{([\[\s|()>.]|^|\]|>)(\.?([-a-zA-Z0-9:+=~@,/_^\$\\\*]+\.{0,2})+)}, '\1<code>\2</code>') + .gsub(%r{([\[\s|()>.]|^|\]|>)(\.?([-a-zA-Z0-9:+=~@,/_^\$\\\*%]+\.{0,2})+)}, '\1<code>\2</code>') .gsub(/(<[-a-zA-Z0-9.]+>)/, '<em>\1</em>') else diff --git a/Documentation/blame-options.adoc b/Documentation/blame-options.adoc index 19ea187238..1fb948fc76 100644 --- a/Documentation/blame-options.adoc +++ b/Documentation/blame-options.adoc @@ -75,7 +75,8 @@ include::line-range-format.adoc[] iso format is used. For supported values, see the discussion of the --date option at linkgit:git-log[1]. ---[no-]progress:: +--progress:: +--no-progress:: Progress status is reported on the standard error stream by default when it is attached to a terminal. This flag enables progress reporting even if not attached to a diff --git a/Documentation/config.adoc b/Documentation/config.adoc index cc769251be..05f1ca7293 100644 --- a/Documentation/config.adoc +++ b/Documentation/config.adoc @@ -114,8 +114,7 @@ whose format and meaning depends on the keyword. Supported keywords are: `gitdir`:: - - The data that follows the keyword `gitdir:` is used as a glob + The data that follows the keyword `gitdir` and a colon is used as a glob pattern. If the location of the .git directory matches the pattern, the include condition is met. + @@ -148,7 +147,7 @@ refer to linkgit:gitignore[5] for details. For convenience: case-insensitively (e.g. on case-insensitive file systems) `onbranch`:: - The data that follows the keyword `onbranch:` is taken to be a + The data that follows the keyword `onbranch` and a colon is taken to be a pattern with standard globbing wildcards and two additional ones, `**/` and `/**`, that can match multiple path components. If we are in a worktree where the name of the branch that is @@ -161,8 +160,8 @@ all branches that begin with `foo/`. This is useful if your branches are organized hierarchically and you would like to apply a configuration to all the branches in that hierarchy. -`hasconfig:remote.*.url:`:: - The data that follows this keyword is taken to +`hasconfig:remote.*.url`:: + The data that follows this keyword and a colon is taken to be a pattern with standard globbing wildcards and two additional ones, `**/` and `/**`, that can match multiple components. The first time this keyword is seen, the rest of diff --git a/Documentation/config/alias.adoc b/Documentation/config/alias.adoc index 2c5db0ad84..95825354bf 100644 --- a/Documentation/config/alias.adoc +++ b/Documentation/config/alias.adoc @@ -38,6 +38,6 @@ it will be treated as a shell command. For example, defining ** A convenient way to deal with this is to write your script operations in an inline function that is then called with any arguments from the command-line. For example `alias.cmd = "!c() { - echo $1 | grep $2 ; }; c" will correctly execute the prior example. + echo $1 | grep $2 ; }; c"` will correctly execute the prior example. ** Setting `GIT_TRACE=1` can help you debug the command being run for your alias. diff --git a/Documentation/config/branch.adoc b/Documentation/config/branch.adoc index e35ea7ac64..a4db9fa5c8 100644 --- a/Documentation/config/branch.adoc +++ b/Documentation/config/branch.adoc @@ -69,9 +69,9 @@ This option defaults to `never`. `git fetch`) to lookup the default branch for merging. Without this option, `git pull` defaults to merge the first refspec fetched. Specify multiple values to get an octopus merge. - If you wish to setup `git pull` so that it merges into <name> from + If you wish to setup `git pull` so that it merges into _<name>_ from another branch in the local repository, you can point - branch.<name>.merge to the desired branch, and use the relative path + `branch.<name>.merge` to the desired branch, and use the relative path setting `.` (a period) for `branch.<name>.remote`. `branch.<name>.mergeOptions`:: diff --git a/Documentation/config/core.adoc b/Documentation/config/core.adoc index 9fde1ab63a..08739bb9d4 100644 --- a/Documentation/config/core.adoc +++ b/Documentation/config/core.adoc @@ -531,9 +531,25 @@ core.commentString:: commented, and removes them after the editor returns (default '#'). + -If set to "auto", `git-commit` would select a character that is not +ifndef::with-breaking-changes[] +If set to "auto", `git-commit` will select a character that is not the beginning character of any line in existing commit messages. -+ +Support for this value is deprecated and will be removed in Git 3.0 +due to the following limitations: ++ +-- +* It is incompatible with adding comments in a commit message + template. This includes the conflicts comments added to + the commit message by `cherry-pick`, `merge`, `rebase` and + `revert`. +* It is incompatible with adding comments to the commit message + in the `prepare-commit-msg` hook. +* It is incompatible with the `fixup` and `squash` commands when + rebasing, +* It is not respected by `git notes` +-- ++ +endif::with-breaking-changes[] Note that these two variables are aliases of each other, and in modern versions of Git you are free to use a string (e.g., `//` or `â‘â•â‘`) with `commentChar`. Versions of Git prior to v2.45.0 will ignore @@ -696,12 +712,6 @@ core.unsetenvvars:: Defaults to `PERL5LIB` to account for the fact that Git for Windows insists on using its own Perl interpreter. -core.restrictinheritedhandles:: - Windows-only: override whether spawned processes inherit only standard - file handles (`stdin`, `stdout` and `stderr`) or all handles. Can be - `auto`, `true` or `false`. Defaults to `auto`, which means `true` on - Windows 7 and later, and `false` on older Windows versions. - core.createObject:: You can set this to 'link', in which case a hardlink followed by a delete of the source are used to make sure that object creation diff --git a/Documentation/config/extensions.adoc b/Documentation/config/extensions.adoc index 9e2f321a6d..829f2523fc 100644 --- a/Documentation/config/extensions.adoc +++ b/Documentation/config/extensions.adoc @@ -14,6 +14,10 @@ compatObjectFormat:: compatObjectFormat. As well as being able to use oids encoded in compatObjectFormat in addition to oids encoded with objectFormat to locally specify objects. ++ +Note that the functionality enabled by this extension is incomplete and subject +to change. It currently exists only to allow development and testing of +the underlying feature and is not designed to be enabled by end users. noop:: This extension does not change git's behavior at all. It is useful only diff --git a/Documentation/config/feature.adoc b/Documentation/config/feature.adoc index f061b64b74..924f5ff4e3 100644 --- a/Documentation/config/feature.adoc +++ b/Documentation/config/feature.adoc @@ -20,6 +20,16 @@ walking fewer objects. + * `pack.allowPackReuse=multi` may improve the time it takes to create a pack by reusing objects from multiple packs instead of just one. ++ +* `pack.usePathWalk` may speed up packfile creation and make the packfiles be +significantly smaller in the presence of certain filename collisions with Git's +default name-hash. ++ +* `init.defaultRefFormat=reftable` causes newly initialized repositories to use +the reftable format for storing references. This new format solves issues with +case-insensitive filesystems, compresses better and performs significantly +better with many use cases. Refer to Documentation/technical/reftable.adoc for +more information on this new storage format. feature.manyFiles:: Enable config options that optimize for repos with many files in the diff --git a/Documentation/config/format.adoc b/Documentation/config/format.adoc index 7410e930e5..ab0710e86a 100644 --- a/Documentation/config/format.adoc +++ b/Documentation/config/format.adoc @@ -68,9 +68,15 @@ format.encodeEmailHeaders:: Defaults to true. format.pretty:: +ifndef::with-breaking-changes[] The default pretty format for log/show/whatchanged command. See linkgit:git-log[1], linkgit:git-show[1], linkgit:git-whatchanged[1]. +endif::with-breaking-changes[] +ifdef::with-breaking-changes[] + The default pretty format for log/show command. + See linkgit:git-log[1], linkgit:git-show[1]. +endif::with-breaking-changes[] format.thread:: The default threading style for 'git format-patch'. Can be diff --git a/Documentation/config/gpg.adoc b/Documentation/config/gpg.adoc index 5cf32b179d..240e46c050 100644 --- a/Documentation/config/gpg.adoc +++ b/Documentation/config/gpg.adoc @@ -1,5 +1,5 @@ gpg.program:: - Use this custom program instead of "`gpg`" found on `$PATH` when + Pathname of the program to use instead of "`gpg`" when making or verifying a PGP signature. The program must support the same command-line interface as GPG, namely, to verify a detached signature, "`gpg --verify $signature - <$file`" is run, and the diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc index 3d28f72643..4682a6bd03 100644 --- a/Documentation/config/imap.adoc +++ b/Documentation/config/imap.adoc @@ -1,7 +1,9 @@ imap.folder:: The folder to drop the mails into, which is typically the Drafts - folder. For example: "INBOX.Drafts", "INBOX/Drafts" or - "[Gmail]/Drafts". Required. + folder. For example: `INBOX.Drafts`, `INBOX/Drafts` or + `[Gmail]/Drafts`. The IMAP folder to interact with MUST be specified; + the value of this configuration variable is used as the fallback + default value when the `--folder` option is not given. imap.tunnel:: Command used to set up a tunnel to the IMAP server through which @@ -40,5 +42,6 @@ imap.authMethod:: Specify the authentication method for authenticating with the IMAP server. If Git was built with the NO_CURL option, or if your curl version is older than 7.34.0, or if you're running git-imap-send with the `--no-curl` - option, the only supported method is 'CRAM-MD5'. If this is not set - then 'git imap-send' uses the basic IMAP plaintext LOGIN command. + option, the only supported methods are `PLAIN`, `CRAM-MD5`, `OAUTHBEARER` + and `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP + plaintext `LOGIN` command. diff --git a/Documentation/config/log.adoc b/Documentation/config/log.adoc index 9003a82191..f20cc25cd7 100644 --- a/Documentation/config/log.adoc +++ b/Documentation/config/log.adoc @@ -1,64 +1,76 @@ -log.abbrevCommit:: - If true, makes linkgit:git-log[1], linkgit:git-show[1], and - linkgit:git-whatchanged[1] assume `--abbrev-commit`. You may +`log.abbrevCommit`:: + If `true`, make +ifndef::with-breaking-changes[] + linkgit:git-log[1], linkgit:git-show[1], and + linkgit:git-whatchanged[1] +endif::with-breaking-changes[] +ifdef::with-breaking-changes[] + linkgit:git-log[1] and linkgit:git-show[1] +endif::with-breaking-changes[] + assume `--abbrev-commit`. You may override this option with `--no-abbrev-commit`. -log.date:: - Set the default date-time mode for the 'log' command. - Setting a value for log.date is similar to using 'git log''s +`log.date`:: + Set the default date-time mode for the `log` command. + Setting a value for log.date is similar to using `git log`'s `--date` option. See linkgit:git-log[1] for details. + If the format is set to "auto:foo" and the pager is in use, format "foo" will be used for the date format. Otherwise, "default" will be used. -log.decorate:: +`log.decorate`:: Print out the ref names of any commits that are shown by the log - command. If 'short' is specified, the ref name prefixes 'refs/heads/', - 'refs/tags/' and 'refs/remotes/' will not be printed. If 'full' is - specified, the full ref name (including prefix) will be printed. - If 'auto' is specified, then if the output is going to a terminal, - the ref names are shown as if 'short' were given, otherwise no ref - names are shown. This is the same as the `--decorate` option - of the `git log`. + command. Possible values are: ++ +-- +`short`;; the ref name prefixes `refs/heads/`, `refs/tags/` and + `refs/remotes/` are not printed. +`full`;; the full ref name (including prefix) are printed. +`auto`;; if the output is going to a terminal, + the ref names are shown as if `short` were given, otherwise no ref + names are shown. +-- ++ +This is the same as the `--decorate` option of the `git log`. -log.initialDecorationSet:: +`log.initialDecorationSet`:: By default, `git log` only shows decorations for certain known ref namespaces. If 'all' is specified, then show all refs as decorations. -log.excludeDecoration:: +`log.excludeDecoration`:: Exclude the specified patterns from the log decorations. This is similar to the `--decorate-refs-exclude` command-line option, but the config option can be overridden by the `--decorate-refs` option. -log.diffMerges:: +`log.diffMerges`:: Set diff format to be used when `--diff-merges=on` is specified, see `--diff-merges` in linkgit:git-log[1] for details. Defaults to `separate`. -log.follow:: +`log.follow`:: If `true`, `git log` will act as if the `--follow` option was used when a single <path> is given. This has the same limitations as `--follow`, i.e. it cannot be used to follow multiple files and does not work well on non-linear history. -log.graphColors:: +`log.graphColors`:: A list of colors, separated by commas, that can be used to draw history lines in `git log --graph`. -log.showRoot:: +`log.showRoot`:: If true, the initial commit will be shown as a big creation event. This is equivalent to a diff against an empty tree. Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which normally hide the root commit will now show it. True by default. -log.showSignature:: +`log.showSignature`:: If true, makes linkgit:git-log[1], linkgit:git-show[1], and linkgit:git-whatchanged[1] assume `--show-signature`. -log.mailmap:: +`log.mailmap`:: If true, makes linkgit:git-log[1], linkgit:git-show[1], and linkgit:git-whatchanged[1] assume `--use-mailmap`, otherwise assume `--no-use-mailmap`. True by default. diff --git a/Documentation/config/merge.adoc b/Documentation/config/merge.adoc index 86359f6dd2..15a4c14c38 100644 --- a/Documentation/config/merge.adoc +++ b/Documentation/config/merge.adoc @@ -81,8 +81,18 @@ as `false`. Defaults to `conflict`. attributes" in linkgit:gitattributes[5]. `merge.stat`:: - Whether to print the diffstat between `ORIG_HEAD` and the merge result - at the end of the merge. True by default. + What, if anything, to print between `ORIG_HEAD` and the merge result + at the end of the merge. Possible values are: ++ +-- +`false`;; Show nothing. +`true`;; Show `git diff --diffstat --summary ORIG_HEAD`. +`compact`;; Show `git diff --compact-summary ORIG_HEAD`. +-- ++ +but any unrecognised value (e.g., a value added by a future version of +Git) is taken as `true` instead of triggering an error. Defaults to +`true`. `merge.autoStash`:: When set to `true`, automatically create a temporary stash entry diff --git a/Documentation/config/mergetool.adoc b/Documentation/config/mergetool.adoc index 6be506145c..7064f5a462 100644 --- a/Documentation/config/mergetool.adoc +++ b/Documentation/config/mergetool.adoc @@ -65,7 +65,7 @@ endif::[] During a merge, Git will automatically resolve as many conflicts as possible and write the `$MERGED` file containing conflict markers around any conflicts that it cannot resolve; `$LOCAL` and `$REMOTE` normally - are the versions of the file from before Git`s conflict + are the versions of the file from before Git's conflict resolution. This flag causes `$LOCAL` and `$REMOTE` to be overwritten so that only the unresolved conflicts are presented to the merge tool. Can be configured per-tool via the `mergetool.<tool>.hideResolved` diff --git a/Documentation/config/pack.adoc b/Documentation/config/pack.adoc index da527377fa..75402d5579 100644 --- a/Documentation/config/pack.adoc +++ b/Documentation/config/pack.adoc @@ -155,6 +155,10 @@ pack.useSparse:: commits contain certain types of direct renames. Default is `true`. +pack.usePathWalk:: + Enable the `--path-walk` option by default for `git pack-objects` + processes. See linkgit:git-pack-objects[1] for full details. + pack.preferBitmapTips:: When selecting which commits will receive bitmaps, prefer a commit at the tip of any reference that is a suffix of any value diff --git a/Documentation/config/promisor.adoc b/Documentation/config/promisor.adoc index 2638b01f83..93e5e0d9b5 100644 --- a/Documentation/config/promisor.adoc +++ b/Documentation/config/promisor.adoc @@ -9,6 +9,28 @@ promisor.advertise:: "false", which means the "promisor-remote" capability is not advertised. +promisor.sendFields:: + A comma or space separated list of additional remote related + field names. A server sends these field names and the + associated field values from its configuration when + advertising its promisor remotes using the "promisor-remote" + capability, see linkgit:gitprotocol-v2[5]. Currently, only the + "partialCloneFilter" and "token" field names are supported. ++ +`partialCloneFilter`:: contains the partial clone filter +used for the remote. ++ +`token`:: contains an authentication token for the remote. ++ +When a field name is part of this list and a corresponding +"remote.foo.<field-name>" config variable is set on the server to a +non-empty value, then the field name and value are sent when +advertising the promisor remote "foo". ++ +This list has no effect unless the "promisor.advertise" config +variable is set to "true", and the "name" and "url" fields are always +advertised regardless of this setting. + promisor.acceptFromServer:: If set to "all", a client will accept all the promisor remotes a server might advertise using the "promisor-remote" @@ -28,3 +50,42 @@ promisor.acceptFromServer:: lazily fetchable from this promisor remote from its responses to "fetch" and "clone" requests from the client. Name and URL comparisons are case sensitive. See linkgit:gitprotocol-v2[5]. + +promisor.checkFields:: + A comma or space separated list of additional remote related + field names. A client checks if the values of these fields + transmitted by a server correspond to the values of these + fields in its own configuration before accepting a promisor + remote. Currently, "partialCloneFilter" and "token" are the + only supported field names. ++ +If one of these field names (e.g., "token") is being checked for an +advertised promisor remote (e.g., "foo"), three conditions must be met +for the check of this specific field to pass: ++ +1. The corresponding local configuration (e.g., `remote.foo.token`) + must be set. +2. The server must advertise the "token" field for remote "foo". +3. The value of the locally configured `remote.foo.token` must exactly + match the value advertised by the server for the "token" field. ++ +If any of these conditions is not met for any field name listed in +`promisor.checkFields`, the advertised remote "foo" is rejected. ++ +For the "partialCloneFilter" field, this allows the client to ensure +that the server's filter matches what it expects locally, preventing +inconsistencies in filtering behavior. For the "token" field, this can +be used to verify that authentication credentials match expected +values. ++ +Field values are compared case-sensitively. ++ +The "name" and "url" fields are always checked according to the +`promisor.acceptFromServer` policy, independently of this setting. ++ +The field names and values should be passed by the server through the +"promisor-remote" capability by using the `promisor.sendFields` config +variable. The fields are checked only if the +`promisor.acceptFromServer` config variable is not set to "None". If +set to "None", this config variable has no effect. See +linkgit:gitprotocol-v2[5]. diff --git a/Documentation/config/pull.adoc b/Documentation/config/pull.adoc index 9349e09261..125c930f72 100644 --- a/Documentation/config/pull.adoc +++ b/Documentation/config/pull.adoc @@ -29,5 +29,21 @@ pull.octopus:: The default merge strategy to use when pulling multiple branches at once. +pull.autoStash:: + When set to true, automatically create a temporary stash entry + to record the local changes before the operation begins, and + restore them after the operation completes. When your "git + pull" rebases (instead of merges), this may be convenient, since + unlike merging pull that tolerates local changes that do not + interfere with the merge, rebasing pull refuses to work with any + local changes. ++ +If `pull.autostash` is set (either to true or false), +`merge.autostash` and `rebase.autostash` are ignored. If +`pull.autostash` is not set at all, depending on the value of +`pull.rebase`, `merge.autostash` or `rebase.autostash` is used +instead. Can be overridden by the `--[no-]autostash` command line +option. + pull.twohead:: The default merge strategy to use when pulling a single branch. diff --git a/Documentation/config/repack.adoc b/Documentation/config/repack.adoc index c79af6d7b8..e9e78dcb19 100644 --- a/Documentation/config/repack.adoc +++ b/Documentation/config/repack.adoc @@ -39,3 +39,10 @@ repack.cruftThreads:: a cruft pack and the respective parameters are not given over the command line. See similarly named `pack.*` configuration variables for defaults and meaning. + +repack.midxMustContainCruft:: + When set to true, linkgit:git-repack[1] will unconditionally include + cruft pack(s), if any, in the multi-pack index when invoked with + `--write-midx`. When false, cruft packs are only included in the MIDX + when necessary (e.g., because they might be required to form a + reachability closure with MIDX bitmaps). Defaults to true. diff --git a/Documentation/config/sendemail.adoc b/Documentation/config/sendemail.adoc index 5ffcfc9f2a..90164c734d 100644 --- a/Documentation/config/sendemail.adoc +++ b/Documentation/config/sendemail.adoc @@ -1,38 +1,38 @@ sendemail.identity:: A configuration identity. When given, causes values in the - 'sendemail.<identity>' subsection to take precedence over - values in the 'sendemail' section. The default identity is + `sendemail.<identity>` subsection to take precedence over + values in the `sendemail` section. The default identity is the value of `sendemail.identity`. sendemail.smtpEncryption:: See linkgit:git-send-email[1] for description. Note that this - setting is not subject to the 'identity' mechanism. + setting is not subject to the `identity` mechanism. sendemail.smtpSSLCertPath:: Path to ca-certificates (either a directory or a single file). Set it to an empty string to disable certificate verification. sendemail.<identity>.*:: - Identity-specific versions of the 'sendemail.*' parameters + Identity-specific versions of the `sendemail.*` parameters found below, taking precedence over those when this identity is selected, through either the command-line or `sendemail.identity`. sendemail.multiEdit:: - If true (default), a single editor instance will be spawned to edit + If `true` (default), a single editor instance will be spawned to edit files you have to edit (patches when `--annotate` is used, and the - summary when `--compose` is used). If false, files will be edited one + summary when `--compose` is used). If `false`, files will be edited one after the other, spawning a new editor each time. sendemail.confirm:: Sets the default for whether to confirm before sending. Must be - one of 'always', 'never', 'cc', 'compose', or 'auto'. See `--confirm` + one of `always`, `never`, `cc`, `compose`, or `auto`. See `--confirm` in the linkgit:git-send-email[1] documentation for the meaning of these values. sendemail.mailmap:: - If true, makes linkgit:git-send-email[1] assume `--mailmap`, - otherwise assume `--no-mailmap`. False by default. + If `true`, makes linkgit:git-send-email[1] assume `--mailmap`, + otherwise assume `--no-mailmap`. `False` by default. sendemail.mailmap.file:: The location of a linkgit:git-send-email[1] specific augmenting @@ -51,7 +51,7 @@ sendemail.aliasesFile:: sendemail.aliasFileType:: Format of the file(s) specified in sendemail.aliasesFile. Must be - one of 'mutt', 'mailrc', 'pine', 'elm', 'gnus', or 'sendmail'. + one of `mutt`, `mailrc`, `pine`, `elm`, `gnus`, or `sendmail`. + What an alias file in each format looks like can be found in the documentation of the email program of the same name. The @@ -88,6 +88,8 @@ sendemail.smtpServer:: sendemail.smtpServerPort:: sendemail.smtpServerOption:: sendemail.smtpUser:: +sendemail.imapSentFolder:: +sendemail.useImapOnly:: sendemail.thread:: sendemail.transferEncoding:: sendemail.validate:: @@ -96,12 +98,17 @@ sendemail.xmailer:: linkgit:git-send-email[1] command-line options. See its documentation for details. +sendemail.outlookidfix:: + If `true`, makes linkgit:git-send-email[1] assume `--outlook-id-fix`, + and if `false` assume `--no-outlook-id-fix`. If not specified, it will + behave the same way as if `--outlook-id-fix` is not specified. + sendemail.signedOffCc (deprecated):: Deprecated alias for `sendemail.signedOffByCc`. sendemail.smtpBatchSize:: Number of messages to be sent per connection, after that a relogin - will happen. If the value is 0 or undefined, send all messages in + will happen. If the value is `0` or undefined, send all messages in one connection. See also the `--batch-size` option of linkgit:git-send-email[1]. @@ -111,5 +118,5 @@ sendemail.smtpReloginDelay:: sendemail.forbidSendmailVariables:: To avoid common misconfiguration mistakes, linkgit:git-send-email[1] - will abort with a warning if any configuration options for "sendmail" + will abort with a warning if any configuration options for `sendmail` exist. Set this variable to bypass the check. diff --git a/Documentation/config/worktree.adoc b/Documentation/config/worktree.adoc index 5e35c7d018..9e3f84f748 100644 --- a/Documentation/config/worktree.adoc +++ b/Documentation/config/worktree.adoc @@ -15,5 +15,5 @@ worktree.useRelativePaths:: different locations or environments. Defaults to "false". + Note that setting `worktree.useRelativePaths` to "true" implies enabling the -`extension.relativeWorktrees` config (see linkgit:git-config[1]), +`extensions.relativeWorktrees` config (see linkgit:git-config[1]), thus making it incompatible with older versions of Git. diff --git a/Documentation/diff-context-options.adoc b/Documentation/diff-context-options.adoc new file mode 100644 index 0000000000..e161260358 --- /dev/null +++ b/Documentation/diff-context-options.adoc @@ -0,0 +1,10 @@ +`-U<n>`:: +`--unified=<n>`:: + Generate diffs with _<n>_ lines of context. Defaults to `diff.context` + or 3 if the config option is unset. + +`--inter-hunk-context=<n>`:: + Show the context between diff hunks, up to the specified _<number>_ + of lines, thereby fusing hunks that are close to each other. + Defaults to `diff.interHunkContext` or 0 if the config option + is unset. diff --git a/Documentation/diff-format.adoc b/Documentation/diff-format.adoc index 80e36e153d..9f7e988241 100644 --- a/Documentation/diff-format.adoc +++ b/Documentation/diff-format.adoc @@ -103,6 +103,7 @@ if the file was renamed on any side of history. With followed by the name of the path in the merge commit. Examples for `-c` and `--cc` without `--combined-all-paths`: + ------------------------------------------------ ::100644 100644 100644 fabadb8 cc95eb0 4866510 MM desc.c ::100755 100755 100755 52b7a2d 6d1ac04 d2ac7d7 RM bar.sh diff --git a/Documentation/diff-generate-patch.adoc b/Documentation/diff-generate-patch.adoc index e5c813c96f..7b6cdd1980 100644 --- a/Documentation/diff-generate-patch.adoc +++ b/Documentation/diff-generate-patch.adoc @@ -138,7 +138,7 @@ or like this (when the `--cc` option is used): + [synopsis] index <hash>,<hash>..<hash> -mode <mode>,<mode>`..`<mode> +mode <mode>,<mode>..<mode> new file mode <mode> deleted file mode <mode>,<mode> + diff --git a/Documentation/diff-options.adoc b/Documentation/diff-options.adoc index 640eb6e7db..ae31520f7f 100644 --- a/Documentation/diff-options.adoc +++ b/Documentation/diff-options.adoc @@ -37,32 +37,32 @@ endif::git-diff[] endif::git-format-patch[] ifdef::git-log[] --m:: +`-m`:: Show diffs for merge commits in the default format. This is similar to `--diff-merges=on`, except `-m` will produce no output unless `-p` is given as well. --c:: +`-c`:: Produce combined diff output for merge commits. Shortcut for `--diff-merges=combined -p`. ---cc:: +`--cc`:: Produce dense combined diff output for merge commits. Shortcut for `--diff-merges=dense-combined -p`. ---dd:: +`--dd`:: Produce diff with respect to first parent for both merge and regular commits. Shortcut for `--diff-merges=first-parent -p`. ---remerge-diff:: +`--remerge-diff`:: Produce remerge-diff output for merge commits. Shortcut for `--diff-merges=remerge -p`. ---no-diff-merges:: +`--no-diff-merges`:: Synonym for `--diff-merges=off`. ---diff-merges=<format>:: +`--diff-merges=<format>`:: Specify diff format to be used for merge commits. Default is {diff-merges-default} unless `--first-parent` is in use, in which case `first-parent` is the default. @@ -70,48 +70,54 @@ ifdef::git-log[] The following formats are supported: + -- -off, none:: +`off`:: +`none`:: Disable output of diffs for merge commits. Useful to override implied value. -on, m:: +`on`:: +`m`:: Make diff output for merge commits to be shown in the default format. The default format can be changed using `log.diffMerges` configuration variable, whose default value is `separate`. -first-parent, 1:: +`first-parent`:: +`1`:: Show full diff with respect to first parent. This is the same format as `--patch` produces for non-merge commits. -separate:: +`separate`:: Show full diff with respect to each of parents. Separate log entry and diff is generated for each parent. -combined, c:: +`combined`:: +`c`:: Show differences from each of the parents to the merge result simultaneously instead of showing pairwise diff between a parent and the result one at a time. Furthermore, it lists only files which were modified from all parents. -dense-combined, cc:: +`dense-combined`:: +`cc`:: Further compress output produced by `--diff-merges=combined` by omitting uninteresting hunks whose contents in the parents have only two variants and the merge result picks one of them without modification. -remerge, r:: - Remerge two-parent merge commits to create a temporary tree +`remerge`:: +`r`:: Remerge two-parent merge commits to create a temporary tree object--potentially containing files with conflict markers and such. A diff is then shown between that temporary tree and the actual merge commit. +-- + The output emitted when this option is used is subject to change, and so is its interaction with other options (unless explicitly documented). --- ---combined-all-paths:: + +`--combined-all-paths`:: Cause combined diffs (used for merge commits) to list the name of the file from all parents. It thus only has effect when `--diff-merges=[dense-]combined` is in use, and @@ -499,7 +505,8 @@ endif::git-format-patch[] Turn off rename detection, even when the configuration file gives the default to do so. -`--[no-]rename-empty`:: +`--rename-empty`:: +`--no-rename-empty`:: Whether to use empty blobs as rename source. ifndef::git-format-patch[] @@ -887,5 +894,33 @@ endif::git-format-patch[] reverted with `--ita-visible-in-index`. Both options are experimental and could be removed in future. +--max-depth=<depth>:: + For each pathspec given on command line, descend at most `<depth>` + levels of directories. A value of `-1` means no limit. + Cannot be combined with wildcards in the pathspec. + Given a tree containing `foo/bar/baz`, the following list shows the + matches generated by each set of options: ++ +-- + - `--max-depth=0 -- foo`: `foo` + + - `--max-depth=1 -- foo`: `foo/bar` + + - `--max-depth=1 -- foo/bar`: `foo/bar/baz` + + - `--max-depth=1 -- foo foo/bar`: `foo/bar/baz` + + - `--max-depth=2 -- foo`: `foo/bar/baz` +-- ++ +If no pathspec is given, the depth is measured as if all +top-level entries were specified. Note that this is different +than measuring from the root, in that `--max-depth=0` would +still return `foo`. This allows you to still limit depth while +asking for a subset of the top-level entries. ++ +Note that this option is only supported for diffs between tree objects, +not against the index or working tree. + For more detailed explanation on these common options, see also linkgit:gitdiffcore[7]. diff --git a/Documentation/fetch-options.adoc b/Documentation/fetch-options.adoc index b01372e4b3..ad1e1f49be 100644 --- a/Documentation/fetch-options.adoc +++ b/Documentation/fetch-options.adoc @@ -1,7 +1,8 @@ ---[no-]all:: +--all:: +--no-all:: Fetch all remotes, except for the ones that has the `remote.<name>.skipFetchAll` configuration variable set. - This overrides the configuration variable fetch.all`. + This overrides the configuration variable `fetch.all`. -a:: --append:: @@ -88,7 +89,8 @@ This is incompatible with `--recurse-submodules=[yes|on-demand]` and takes precedence over the `fetch.output` config option. ifndef::git-pull[] ---[no-]write-fetch-head:: +--write-fetch-head:: +--no-write-fetch-head:: Write the list of remote refs fetched in the `FETCH_HEAD` file directly under `$GIT_DIR`. This is the default. Passing `--no-write-fetch-head` from the command line tells @@ -118,13 +120,16 @@ ifndef::git-pull[] Allow several <repository> and <group> arguments to be specified. No <refspec>s may be specified. ---[no-]auto-maintenance:: ---[no-]auto-gc:: +--auto-maintenance:: +--no-auto-maintenance:: +--auto-gc:: +--no-auto-gc:: Run `git maintenance run --auto` at the end to perform automatic repository maintenance if needed. (`--[no-]auto-gc` is a synonym.) This is enabled by default. ---[no-]write-commit-graph:: +--write-commit-graph:: +--no-write-commit-graph:: Write a commit-graph after fetching. This overrides the config setting `fetch.writeCommitGraph`. endif::git-pull[] diff --git a/Documentation/for-each-ref-options.adoc b/Documentation/for-each-ref-options.adoc new file mode 100644 index 0000000000..f13efb5f25 --- /dev/null +++ b/Documentation/for-each-ref-options.adoc @@ -0,0 +1,85 @@ +`<pattern>...`:: + If one or more _<pattern>_ parameters are given, only refs are shown that + match against at least one pattern, either using `fnmatch`(3) or + literally, in the latter case matching completely or from the + beginning up to a slash. + +`--stdin`:: + The list of patterns is read from standard input instead of from + the argument list. + +`--count=<count>`:: + Stop after showing _<count>_ refs. + +`--sort=<key>`:: + Sort on the field name _<key>_. Prefix `-` to sort in + descending order of the value. When unspecified, + `refname` is used. You may use the `--sort=<key>` option + multiple times, in which case the last key becomes the primary + key. + +`--format[=<format>]`:: + A string that interpolates `%(fieldname)` from a ref being shown and + the object it points at. In addition, the string literal `%%` + renders as `%` and `%xx` - where `xx` are hex digits - renders as + the character with hex code `xx`. For example, `%00` interpolates to + `\0` (_NUL_), `%09` to `\t` (_TAB_), and `%0a` to `\n` (_LF_). + +When unspecified, _<format>_ defaults to `%(objectname) SPC %(objecttype) +TAB %(refname)`. + +`--color[=<when>]`:: + Respect any colors specified in the `--format` option. The + _<when__ field must be one of `always`, `never`, or `auto` (if + `<when>` is absent, behave as if `always` was given). + +`--shell`:: +`--perl`:: +`--python`:: +`--tcl`:: + If given, strings that substitute `%(fieldname)` + placeholders are quoted as string literals suitable for + the specified host language. This is meant to produce + a scriptlet that can directly be "eval"ed. + +`--points-at=<object>`:: + Only list refs which points at the given object. + +`--merged[=<object>]`:: + Only list refs whose tips are reachable from the + specified commit (`HEAD` if not specified). + +`--no-merged[=<object>]`:: + Only list refs whose tips are not reachable from _<object>_(`HEAD` if not + specified). + +`--contains[=<object>]`:: + Only list refs which contain _<object>_(`HEAD` if not specified). + +`--no-contains[=<object>]`:: + Only list refs which don't contain _<object>_ (`HEAD` + if not specified). + +`--ignore-case`:: + Sorting and filtering refs are case insensitive. + +`--omit-empty`:: + Do not print a newline after formatted refs where the format expands + to the empty string. + +`--exclude=<excluded-pattern>`:: + If one or more `--exclude` options are given, only refs which do not + match any _<excluded-pattern>_ parameters are shown. Matching is done + using the same rules as _<pattern>_ above. + +`--include-root-refs`:: + List root refs (`HEAD` and pseudorefs) apart from regular refs. + +`--start-after=<marker>`:: + Allows paginating the output by skipping references up to and including the + specified marker. When paging, it should be noted that references may be + deleted, modified or added between invocations. Output will only yield those + references which follow the marker lexicographically. Output begins from the + first reference that would come after the marker alphabetically. Cannot be + used with `--sort=<key>` or `--stdin` options, or the _<pattern>_ argument(s) + to limit the refs. diff --git a/Documentation/git-add.adoc b/Documentation/git-add.adoc index eba0b419ce..ad629c46c5 100644 --- a/Documentation/git-add.adoc +++ b/Documentation/git-add.adoc @@ -16,18 +16,18 @@ git add [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [- DESCRIPTION ----------- -This command updates the index using the current content found in -the working tree, to prepare the content staged for the next commit. -It typically adds the current content of existing paths as a whole, -but with some options it can also be used to add content with -only part of the changes made to the working tree files applied, or -remove paths that do not exist in the working tree anymore. - -The "index" holds a snapshot of the content of the working tree, and it -is this snapshot that is taken as the contents of the next commit. Thus -after making any changes to the working tree, and before running -the commit command, you must use the `add` command to add any new or -modified files to the index. +Add contents of new or changed files to the index. The "index" (also +known as the "staging area") is what you use to prepare the contents of +the next commit. + +When you run `git commit` without any other arguments, it will only +commit staged changes. For example, if you've edited `file.c` and want +to commit your changes to that file, you can run: + + git add file.c + git commit + +You can also add only part of your changes to a file with `git add -p`. This command can be performed multiple times before a commit. It only adds the content of the specified file(s) at the time the add command is @@ -37,12 +37,10 @@ you must run `git add` again to add the new content to the index. The `git status` command can be used to obtain a summary of which files have changes that are staged for the next commit. -The `git add` command will not add ignored files by default. If any -ignored files were explicitly specified on the command line, `git add` -will fail with a list of ignored files. Ignored files reached by -directory recursion or filename globbing performed by Git (quote your -globs before the shell) will be silently ignored. The `git add` command can -be used to add ignored files with the `-f` (force) option. +The `git add` command will not add ignored files by default. You can +use the `--force` option to add ignored files. If you specify the exact +filename of an ignored file, `git add` will fail with a list of ignored +files. Otherwise it will silently ignore the file. Please see linkgit:git-commit[1] for alternative ways to add content to a commit. @@ -104,6 +102,8 @@ This effectively runs `add --interactive`, but bypasses the initial command menu and directly jumps to the `patch` subcommand. See ``Interactive mode'' for details. +include::diff-context-options.adoc[] + `-e`:: `--edit`:: Open the diff vs. the index in an editor and let the user diff --git a/Documentation/git-am.adoc b/Documentation/git-am.adoc index 221070de48..b23b4fba20 100644 --- a/Documentation/git-am.adoc +++ b/Documentation/git-am.adoc @@ -48,7 +48,8 @@ OPTIONS --keep-non-patch:: Pass `-b` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]). ---[no-]keep-cr:: +--keep-cr:: +--no-keep-cr:: With `--keep-cr`, call 'git mailsplit' (see linkgit:git-mailsplit[1]) with the same option, to prevent it from stripping CR at the end of lines. `am.keepcr` configuration variable can be used to specify the diff --git a/Documentation/git-apply.adoc b/Documentation/git-apply.adoc index 952518b8af..6c71ee69da 100644 --- a/Documentation/git-apply.adoc +++ b/Documentation/git-apply.adoc @@ -75,13 +75,14 @@ OPTIONS tree. If `--check` is in effect, merely check that it would apply cleanly to the index entry. +-N:: --intent-to-add:: When applying the patch only to the working tree, mark new files to be added to the index later (see `--intent-to-add` - option in linkgit:git-add[1]). This option is ignored unless - running in a Git repository and `--index` is not specified. - Note that `--index` could be implied by other options such - as `--cached` or `--3way`. + option in linkgit:git-add[1]). This option is ignored if + `--index` or `--cached` are used, and has no effect outside a Git + repository. Note that `--index` could be implied by other options + such as `--3way`. -3:: --3way:: diff --git a/Documentation/git-backfill.adoc b/Documentation/git-backfill.adoc index 95623051f7..b8394dcf22 100644 --- a/Documentation/git-backfill.adoc +++ b/Documentation/git-backfill.adoc @@ -57,7 +57,8 @@ OPTIONS blobs seen at a given path. The default minimum batch size is 50,000. -`--[no-]sparse`:: +`--sparse`:: +`--no-sparse`:: Only download objects if they appear at a path that matches the current sparse-checkout. If the sparse-checkout feature is enabled, then `--sparse` is assumed and can be disabled with `--no-sparse`. diff --git a/Documentation/git-cat-file.adoc b/Documentation/git-cat-file.adoc index cde79ad242..c139f55a16 100644 --- a/Documentation/git-cat-file.adoc +++ b/Documentation/git-cat-file.adoc @@ -62,8 +62,10 @@ OPTIONS or to ask for a "blob" with `<object>` being a tag object that points at it. ---[no-]mailmap:: ---[no-]use-mailmap:: +--mailmap:: +--no-mailmap:: +--use-mailmap:: +--no-use-mailmap:: Use mailmap file to map author, committer and tagger names and email addresses to canonical real names and email addresses. See linkgit:git-shortlog[1]. @@ -307,6 +309,11 @@ newline. The available atoms are: `objecttype`:: The type of the object (the same as `cat-file -t` reports). +`objectmode`:: + If the specified object has mode information (such as a tree or + index entry), the mode expressed as an octal integer. Otherwise, + empty string. + `objectsize`:: The size, in bytes, of the object (the same as `cat-file -s` reports). @@ -368,6 +375,14 @@ If a name is specified that might refer to more than one object (an ambiguous sh <object> SP ambiguous LF ------------ +If a name is specified that refers to a submodule entry in a tree and the +target object does not exist in the repository, then `cat-file` will ignore +any custom format and print (with the object ID of the submodule): + +------------ +<oid> SP submodule LF +------------ + If `--follow-symlinks` is used, and a symlink in the repository points outside the repository, then `cat-file` will ignore any custom format and print: diff --git a/Documentation/git-check-attr.adoc b/Documentation/git-check-attr.adoc index 503b644657..15a37a38e3 100644 --- a/Documentation/git-check-attr.adoc +++ b/Documentation/git-check-attr.adoc @@ -19,7 +19,8 @@ For every pathname, this command will list if each attribute is 'unspecified', OPTIONS ------- --a, --all:: +-a:: +--all:: List all attributes that are associated with the specified paths. If this option is used, then 'unspecified' attributes will not be included in the output. diff --git a/Documentation/git-check-ignore.adoc b/Documentation/git-check-ignore.adoc index 3e3b4e3446..a6c6c1b6e5 100644 --- a/Documentation/git-check-ignore.adoc +++ b/Documentation/git-check-ignore.adoc @@ -25,11 +25,13 @@ subject to exclude rules; but see `--no-index'. OPTIONS ------- --q, --quiet:: +-q:: +--quiet:: Don't output anything, just set exit status. This is only valid with a single pathname. --v, --verbose:: +-v:: +--verbose:: Instead of printing the paths that are excluded, for each path that matches an exclude pattern, print the exclude pattern together with the path. (Matching an exclude pattern usually @@ -49,7 +51,8 @@ linkgit:gitignore[5]. below). If `--stdin` is also given, input paths are separated with a NUL character instead of a linefeed character. --n, --non-matching:: +-n:: +--non-matching:: Show given paths which don't match any pattern. This only makes sense when `--verbose` is enabled, otherwise it would not be possible to distinguish between paths which match a diff --git a/Documentation/git-check-ref-format.adoc b/Documentation/git-check-ref-format.adoc index 2aacfd1808..0c3abf9146 100644 --- a/Documentation/git-check-ref-format.adoc +++ b/Documentation/git-check-ref-format.adoc @@ -98,7 +98,8 @@ a branch. OPTIONS ------- ---[no-]allow-onelevel:: +--allow-onelevel:: +--no-allow-onelevel:: Controls whether one-level refnames are accepted (i.e., refnames that do not contain multiple `/`-separated components). The default is `--no-allow-onelevel`. diff --git a/Documentation/git-checkout.adoc b/Documentation/git-checkout.adoc index ee83b6d9ba..ff1cb29bc1 100644 --- a/Documentation/git-checkout.adoc +++ b/Documentation/git-checkout.adoc @@ -289,6 +289,8 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode. Note that this option uses the no overlay mode by default (see also `--overlay`), and currently doesn't support overlay mode. +include::diff-context-options.adoc[] + `--ignore-other-worktrees`:: `git checkout` refuses when the wanted branch is already checked out or otherwise in use by another worktree. This option makes @@ -332,7 +334,7 @@ Note that this option uses the no overlay mode by default (see also separated with _NUL_ character and all other characters are taken literally (including newlines and quotes). -<branch>:: +`<branch>`:: Branch to checkout; if it refers to a branch (i.e., a name that, when prepended with "refs/heads/", is a valid ref), then that branch is checked out. Otherwise, if it refers to a valid diff --git a/Documentation/git-clone.adoc b/Documentation/git-clone.adoc index 222d558290..57cdfb7620 100644 --- a/Documentation/git-clone.adoc +++ b/Documentation/git-clone.adoc @@ -16,7 +16,7 @@ git clone [--template=<template-directory>] [--depth <depth>] [--[no-]single-branch] [--[no-]tags] [--recurse-submodules[=<pathspec>]] [--[no-]shallow-submodules] [--[no-]remote-submodules] [--jobs <n>] [--sparse] [--[no-]reject-shallow] - [--filter=<filter-spec>] [--also-filter-submodules]] [--] <repository> + [--filter=<filter-spec> [--also-filter-submodules]] [--] <repository> [<directory>] DESCRIPTION @@ -272,7 +272,8 @@ corresponding `--mirror` and `--no-tags` options instead. reachable from a specified remote branch or tag. This option can be specified multiple times. -`--[no-]single-branch`:: +`--single-branch`:: +`--no-single-branch`:: Clone only the history leading to the tip of a single branch, either specified by the `--branch` option or the primary branch remote's `HEAD` points at. @@ -282,7 +283,8 @@ corresponding `--mirror` and `--no-tags` options instead. branch when `--single-branch` clone was made, no remote-tracking branch is created. -`--[no-]tags`:: +`--tags`:: +`--no-tags`:: Control whether or not tags will be cloned. When `--no-tags` is given, the option will be become permanent by setting the `remote.<remote>.tagOpt=--no-tags` configuration. This ensures that @@ -313,10 +315,12 @@ the clone is finished. This option is ignored if the cloned repository does not have a worktree/checkout (i.e. if any of `--no-checkout`/`-n`, `--bare`, or `--mirror` is given) -`--[no-]shallow-submodules`:: +`--shallow-submodules`:: +`--no-shallow-submodules`:: All submodules which are cloned will be shallow with a depth of 1. -`--[no-]remote-submodules`:: +`--remote-submodules`:: +`--no-remote-submodules`:: All submodules which are cloned will use the status of the submodule's remote-tracking branch to update the submodule, rather than the superproject's recorded SHA-1. Equivalent to passing `--remote` to diff --git a/Documentation/git-commit-graph.adoc b/Documentation/git-commit-graph.adoc index 50b5016804..e9558173c0 100644 --- a/Documentation/git-commit-graph.adoc +++ b/Documentation/git-commit-graph.adoc @@ -34,7 +34,8 @@ OPTIONS object directory, `git commit-graph ...` will exit with non-zero status. ---[no-]progress:: +--progress:: +--no-progress:: Turn progress on/off explicitly. If neither is specified, progress is shown if standard error is connected to a terminal. diff --git a/Documentation/git-commit.adoc b/Documentation/git-commit.adoc index dc219025f1..54c207ad45 100644 --- a/Documentation/git-commit.adoc +++ b/Documentation/git-commit.adoc @@ -76,6 +76,8 @@ OPTIONS which changes to commit. See linkgit:git-add[1] for details. +include::diff-context-options.adoc[] + `-C <commit>`:: `--reuse-message=<commit>`:: Take an existing _<commit>_ object, and reuse the log message @@ -212,7 +214,8 @@ include::signoff-option.adoc[] each trailer would appear, and other details. `-n`:: -`--[no-]verify`:: +`--verify`:: +`--no-verify`:: Bypass the `pre-commit` and `commit-msg` hooks. See also linkgit:githooks[5]. @@ -279,6 +282,7 @@ variable (see linkgit:git-config[1]). + -- It is a rough equivalent for: + ------ $ git reset --soft HEAD^ $ ... do something else to come up with the right tree ... diff --git a/Documentation/git-config.adoc b/Documentation/git-config.adoc index 936e0c5130..36d2845152 100644 --- a/Documentation/git-config.adoc +++ b/Documentation/git-config.adoc @@ -10,9 +10,9 @@ SYNOPSIS -------- [verse] 'git config list' [<file-option>] [<display-option>] [--includes] -'git config get' [<file-option>] [<display-option>] [--includes] [--all] [--regexp] [--value=<value>] [--fixed-value] [--default=<default>] <name> -'git config set' [<file-option>] [--type=<type>] [--all] [--value=<value>] [--fixed-value] <name> <value> -'git config unset' [<file-option>] [--all] [--value=<value>] [--fixed-value] <name> +'git config get' [<file-option>] [<display-option>] [--includes] [--all] [--regexp] [--value=<pattern>] [--fixed-value] [--default=<default>] [--url=<url>] <name> +'git config set' [<file-option>] [--type=<type>] [--all] [--value=<pattern>] [--fixed-value] <name> <value> +'git config unset' [<file-option>] [--all] [--value=<pattern>] [--fixed-value] <name> 'git config rename-section' [<file-option>] <old-name> <new-name> 'git config remove-section' [<file-option>] <name> 'git config edit' [<file-option>] @@ -26,7 +26,7 @@ escaped. Multiple lines can be added to an option by using the `--append` option. If you want to update or unset an option which can occur on multiple -lines, a `value-pattern` (which is an extended regular expression, +lines, `--value=<pattern>` (which is an extended regular expression, unless the `--fixed-value` option is given) needs to be given. Only the existing values that match the pattern are updated or unset. If you want to handle the lines that do *not* match the pattern, just @@ -109,7 +109,7 @@ OPTIONS --replace-all:: Default behavior is to replace at most one line. This replaces - all lines matching the key (and optionally the `value-pattern`). + all lines matching the key (and optionally `--value=<pattern>`). --append:: Adds a new line to the option without altering any existing @@ -200,11 +200,19 @@ See also <<FILES>>. section in linkgit:gitrevisions[7] for a more complete list of ways to spell blob names. +`--value=<pattern>`:: +`--no-value`:: + With `get`, `set`, and `unset`, match only against + _<pattern>_. The pattern is an extended regular expression unless + `--fixed-value` is given. ++ +Use `--no-value` to unset _<pattern>_. + --fixed-value:: - When used with the `value-pattern` argument, treat `value-pattern` as + When used with `--value=<pattern>`, treat _<pattern>_ as an exact string instead of a regular expression. This will restrict the name/value pairs that are matched to only those where the value - is exactly equal to the `value-pattern`. + is exactly equal to _<pattern>_. --type <type>:: 'git config' will ensure that any input or output is valid under the given @@ -259,6 +267,12 @@ Valid `<type>`'s include: Output only the names of config variables for `list` or `get`. +`--show-names`:: +`--no-show-names`:: + With `get`, show config keys in addition to their values. The + default is `--no-show-names` unless `--url` is given and there + are no subsections in _<name>_. + --show-origin:: Augment the output of all queried config options with the origin type (file, standard input, blob, command line) and @@ -281,7 +295,8 @@ Valid `<type>`'s include: When the color setting for `name` is undefined, the command uses `color.ui` as fallback. ---[no-]includes:: +--includes:: +--no-includes:: Respect `include.*` directives in config files when looking up values. Defaults to `off` when a specific file is given (e.g., using `--file`, `--global`, etc) and `on` when searching all diff --git a/Documentation/git-count-objects.adoc b/Documentation/git-count-objects.adoc index 97f9f12610..eeee6b9f7f 100644 --- a/Documentation/git-count-objects.adoc +++ b/Documentation/git-count-objects.adoc @@ -28,6 +28,8 @@ size: disk space consumed by loose objects, in KiB (unless -H is specified) + in-pack: the number of in-pack objects + +packs: the number of pack files ++ size-pack: disk space consumed by the packs, in KiB (unless -H is specified) + prune-packable: the number of loose objects that are also present in diff --git a/Documentation/git-diff.adoc b/Documentation/git-diff.adoc index dec173a345..272331afba 100644 --- a/Documentation/git-diff.adoc +++ b/Documentation/git-diff.adoc @@ -14,7 +14,7 @@ git diff [<options>] --cached [--merge-base] [<commit>] [--] [<path>...] git diff [<options>] [--merge-base] <commit> [<commit>...] <commit> [--] [<path>...] git diff [<options>] <commit>...<commit> [--] [<path>...] git diff [<options>] <blob> <blob> -git diff [<options>] --no-index [--] <path> <path> +git diff [<options>] --no-index [--] <path> <path> [<pathspec>...] DESCRIPTION ----------- @@ -31,14 +31,18 @@ files on disk. further add to the index but you still haven't. You can stage these changes by using linkgit:git-add[1]. -`git diff [<options>] --no-index [--] <path> <path>`:: +`git diff [<options>] --no-index [--] <path> <path> [<pathspec>...]`:: This form is to compare the given two paths on the filesystem. You can omit the `--no-index` option when running the command in a working tree controlled by Git and at least one of the paths points outside the working tree, or when running the command outside a working tree - controlled by Git. This form implies `--exit-code`. + controlled by Git. This form implies `--exit-code`. If both + paths point to directories, additional pathspecs may be + provided. These will limit the files included in the + difference. All such pathspecs must be relative as they + apply to both sides of the diff. `git diff [<options>] --cached [--merge-base] [<commit>] [--] [<path>...]`:: diff --git a/Documentation/git-difftool.adoc b/Documentation/git-difftool.adoc index d596205eaf..064bc68347 100644 --- a/Documentation/git-difftool.adoc +++ b/Documentation/git-difftool.adoc @@ -77,7 +77,8 @@ with custom merge tool commands and has the same value as `$MERGED`. --tool-help:: Print a list of diff tools that may be used with `--tool`. ---[no-]symlinks:: +--symlinks:: +--no-symlinks:: 'git difftool''s default behavior is to create symlinks to the working tree when run in `--dir-diff` mode and the right-hand side of the comparison yields the same content as the file in @@ -94,7 +95,8 @@ instead. `--no-symlinks` is the default on Windows. Additionally, `$BASE` is set in the environment. -g:: ---[no-]gui:: +--gui:: +--no-gui:: When 'git-difftool' is invoked with the `-g` or `--gui` option the default diff tool will be read from the configured `diff.guitool` variable instead of `diff.tool`. This may be @@ -104,7 +106,8 @@ instead. `--no-symlinks` is the default on Windows. fallback in the order of `merge.guitool`, `diff.tool`, `merge.tool` until a tool is found. ---[no-]trust-exit-code:: +--trust-exit-code:: +--no-trust-exit-code:: Errors reported by the diff tool are ignored by default. Use `--trust-exit-code` to make 'git-difftool' exit when an invoked diff tool returns a non-zero exit code. diff --git a/Documentation/git-fast-export.adoc b/Documentation/git-fast-export.adoc index 43bbb4f63c..297b57bb2e 100644 --- a/Documentation/git-fast-export.adoc +++ b/Documentation/git-fast-export.adoc @@ -50,6 +50,23 @@ resulting tag will have an invalid signature. is the same as how earlier versions of this command without this option behaved. + +When exported, a signature starts with: ++ +gpgsig <git-hash-algo> <signature-format> ++ +where <git-hash-algo> is the Git object hash so either "sha1" or +"sha256", and <signature-format> is the signature type, so "openpgp", +"x509", "ssh" or "unknown". ++ +For example, an OpenPGP signature on a SHA-1 commit starts with +`gpgsig sha1 openpgp`, while an SSH signature on a SHA-256 commit +starts with `gpgsig sha256 ssh`. ++ +While all the signatures of a commit are exported, an importer may +choose to accept only some of them. For example +linkgit:git-fast-import[1] currently stores at most one signature per +Git hash algorithm in each commit. ++ NOTE: This is highly experimental and the format of the data stream may change in the future without compatibility guarantees. diff --git a/Documentation/git-fast-import.adoc b/Documentation/git-fast-import.adoc index 250d866652..6e095b02a1 100644 --- a/Documentation/git-fast-import.adoc +++ b/Documentation/git-fast-import.adoc @@ -61,10 +61,10 @@ OPTIONS currently impacts only the `export-marks`, `import-marks`, and `import-marks-if-exists` feature commands. + - Only enable this option if you trust the program generating the - fast-import stream! This option is enabled automatically for - remote-helpers that use the `import` capability, as they are - already trusted to run their own code. +Only enable this option if you trust the program generating the +fast-import stream! This option is enabled automatically for +remote-helpers that use the `import` capability, as they are +already trusted to run their own code. Options for Frontends ~~~~~~~~~~~~~~~~~~~~~ @@ -111,7 +111,8 @@ Locations of Marks Files Like --import-marks but instead of erroring out, silently skips the file if it does not exist. ---[no-]relative-marks:: +--relative-marks:: +--no-relative-marks:: After specifying --relative-marks the paths specified with --import-marks= and --export-marks= are relative to an internal directory in the current repository. @@ -182,7 +183,7 @@ amount of memory usage and processing time. Assuming the frontend is able to keep up with fast-import and feed it a constant stream of data, import times for projects holding 10+ years of history and containing 100,000+ individual commits are generally completed in just 1-2 -hours on quite modest (~$2,000 USD) hardware. +hours on quite modest hardware (~$2,000 USD in 2007). Most bottlenecks appear to be in foreign source data access (the source just cannot extract revisions fast enough) or disk IO (fast-import @@ -445,7 +446,7 @@ one). original-oid? ('author' (SP <name>)? SP LT <email> GT SP <when> LF)? 'committer' (SP <name>)? SP LT <email> GT SP <when> LF - ('gpgsig' SP <alg> LF data)? + ('gpgsig' SP <algo> SP <format> LF data)? ('encoding' SP <encoding> LF)? data ('from' SP <commit-ish> LF)? @@ -518,13 +519,39 @@ their syntax. ^^^^^^^^ The optional `gpgsig` command is used to include a PGP/GPG signature -that signs the commit data. +or other cryptographic signature that signs the commit data. -Here <alg> specifies which hashing algorithm is used for this -signature, either `sha1` or `sha256`. +.... + 'gpgsig' SP <git-hash-algo> SP <signature-format> LF data +.... + +The `gpgsig` command takes two arguments: + +* `<git-hash-algo>` specifies which Git object format this signature + applies to, either `sha1` or `sha256`. This allows to know which + representation of the commit was signed (the SHA-1 or the SHA-256 + version) which helps with both signature verification and + interoperability between repos with different hash functions. + +* `<signature-format>` specifies the type of signature, such as + `openpgp`, `x509`, `ssh`, or `unknown`. This is a convenience for + tools that process the stream, so they don't have to parse the ASCII + armor to identify the signature type. -NOTE: This is highly experimental and the format of the data stream may -change in the future without compatibility guarantees. +A commit may have at most one signature for the SHA-1 object format +(stored in the "gpgsig" header) and one for the SHA-256 object format +(stored in the "gpgsig-sha256" header). + +See below for a detailed description of the `data` command which +contains the raw signature data. + +Signatures are not yet checked in the current implementation +though. (Already setting the `extensions.compatObjectFormat` +configuration option might help with verifying both SHA-1 and SHA-256 +object format signatures when it will be implemented.) + +NOTE: This is highly experimental and the format of the `gpgsig` +command may change in the future without compatibility guarantees. `encoding` ^^^^^^^^^^ @@ -579,9 +606,11 @@ Marks must be declared (via `mark`) before they can be used. The special case of restarting an incremental import from the current branch value should be written as: + ---- from refs/heads/branch^0 ---- + The `^0` suffix is necessary as fast-import does not permit a branch to start from itself, and the branch is created in memory before the `from` command is even read from the input. Adding `^0` will force @@ -618,7 +647,7 @@ External data format:: + Here usually `<dataref>` must be either a mark reference (`:<idnum>`) set by a prior `blob` command, or a full 40-byte SHA-1 of an -existing Git blob object. If `<mode>` is `040000`` then +existing Git blob object. If `<mode>` is `040000` then `<dataref>` must be the full 40-byte SHA-1 of an existing Git tree object or a mark reference set with `--import-marks`. diff --git a/Documentation/git-fmt-merge-msg.adoc b/Documentation/git-fmt-merge-msg.adoc index 0f3328956d..6d91620be9 100644 --- a/Documentation/git-fmt-merge-msg.adoc +++ b/Documentation/git-fmt-merge-msg.adoc @@ -35,7 +35,8 @@ OPTIONS Do not list one-line descriptions from the actual commits being merged. ---[no-]summary:: +--summary:: +--no-summary:: Synonyms to --log and --no-log; these are deprecated and will be removed in the future. diff --git a/Documentation/git-for-each-ref.adoc b/Documentation/git-for-each-ref.adoc index 5ef89fc0fe..c02cb7f886 100644 --- a/Documentation/git-for-each-ref.adoc +++ b/Documentation/git-for-each-ref.adoc @@ -7,106 +7,28 @@ git-for-each-ref - Output information on each ref SYNOPSIS -------- -[verse] -'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl] +[synopsis] +git for-each-ref [--count=<count>] [--shell|--perl|--python|--tcl] [(--sort=<key>)...] [--format=<format>] - [--include-root-refs] [ --stdin | <pattern>... ] - [--points-at=<object>] + [--include-root-refs] [--points-at=<object>] [--merged[=<object>]] [--no-merged[=<object>]] [--contains[=<object>]] [--no-contains[=<object>]] - [--exclude=<pattern> ...] + [(--exclude=<pattern>)...] [--start-after=<marker>] + [ --stdin | (<pattern>...)] DESCRIPTION ----------- -Iterate over all refs that match `<pattern>` and show them -according to the given `<format>`, after sorting them according -to the given set of `<key>`. If `<count>` is given, stop after -showing that many refs. The interpolated values in `<format>` +Iterate over all refs that match _<pattern>_ and show them +according to the given _<format>_, after sorting them according +to the given set of _<key>_. If _<count>_ is given, stop after +showing that many refs. The interpolated values in _<format>_ can optionally be quoted as string literals in the specified host language allowing their direct evaluation in that language. OPTIONS ------- -<pattern>...:: - If one or more patterns are given, only refs are shown that - match against at least one pattern, either using fnmatch(3) or - literally, in the latter case matching completely or from the - beginning up to a slash. - ---stdin:: - If `--stdin` is supplied, then the list of patterns is read from - standard input instead of from the argument list. - ---count=<count>:: - By default the command shows all refs that match - `<pattern>`. This option makes it stop after showing - that many refs. - ---sort=<key>:: - A field name to sort on. Prefix `-` to sort in - descending order of the value. When unspecified, - `refname` is used. You may use the --sort=<key> option - multiple times, in which case the last key becomes the primary - key. - ---format=<format>:: - A string that interpolates `%(fieldname)` from a ref being shown and - the object it points at. In addition, the string literal `%%` - renders as `%` and `%xx` - where `xx` are hex digits - renders as - the character with hex code `xx`. For example, `%00` interpolates to - `\0` (NUL), `%09` to `\t` (TAB), and `%0a` to `\n` (LF). -+ -When unspecified, `<format>` defaults to `%(objectname) SPC %(objecttype) -TAB %(refname)`. - ---color[=<when>]:: - Respect any colors specified in the `--format` option. The - `<when>` field must be one of `always`, `never`, or `auto` (if - `<when>` is absent, behave as if `always` was given). - ---shell:: ---perl:: ---python:: ---tcl:: - If given, strings that substitute `%(fieldname)` - placeholders are quoted as string literals suitable for - the specified host language. This is meant to produce - a scriptlet that can directly be `eval`ed. - ---points-at=<object>:: - Only list refs which points at the given object. - ---merged[=<object>]:: - Only list refs whose tips are reachable from the - specified commit (HEAD if not specified). - ---no-merged[=<object>]:: - Only list refs whose tips are not reachable from the - specified commit (HEAD if not specified). - ---contains[=<object>]:: - Only list refs which contain the specified commit (HEAD if not - specified). - ---no-contains[=<object>]:: - Only list refs which don't contain the specified commit (HEAD - if not specified). - ---ignore-case:: - Sorting and filtering refs are case insensitive. - ---omit-empty:: - Do not print a newline after formatted refs where the format expands - to the empty string. - ---exclude=<pattern>:: - If one or more patterns are given, only refs which do not match - any excluded pattern(s) are shown. Matching is done using the - same rules as `<pattern>` above. - ---include-root-refs:: - List root refs (HEAD and pseudorefs) apart from regular refs. +include::for-each-ref-options.adoc[] FIELD NAMES ----------- @@ -117,44 +39,44 @@ keys. For all objects, the following names can be used: -refname:: - The name of the ref (the part after $GIT_DIR/). +`refname`:: + The name of the ref (the part after `$GIT_DIR/`). For a non-ambiguous short name of the ref append `:short`. - The option core.warnAmbiguousRefs is used to select the strict - abbreviation mode. If `lstrip=<N>` (`rstrip=<N>`) is appended, strips `<N>` + The option `core.warnAmbiguousRefs` is used to select the strict + abbreviation mode. If `lstrip=<n>` (`rstrip=<n>`) is appended, strip _<n>_ slash-separated path components from the front (back) of the refname (e.g. `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo` and `%(refname:rstrip=2)` turns `refs/tags/foo` into `refs`). - If `<N>` is a negative number, strip as many path components as - necessary from the specified end to leave `-<N>` path components + If _<n>_ is a negative number, strip as many path components as + necessary from the specified end to leave `-<n>` path components (e.g. `%(refname:lstrip=-2)` turns `refs/tags/foo` into `tags/foo` and `%(refname:rstrip=-1)` turns `refs/tags/foo` into `refs`). When the ref does not have enough components, the result becomes an empty string if - stripping with positive <N>, or it becomes the full refname if - stripping with negative <N>. Neither is an error. + stripping with positive _<n>_, or it becomes the full refname if + stripping with negative _<N>_. Neither is an error. + `strip` can be used as a synonym to `lstrip`. -objecttype:: +`objecttype`:: The type of the object (`blob`, `tree`, `commit`, `tag`). -objectsize:: +`objectsize`:: The size of the object (the same as 'git cat-file -s' reports). Append `:disk` to get the size, in bytes, that the object takes up on - disk. See the note about on-disk sizes in the `CAVEATS` section below. -objectname:: + disk. See the note about on-disk sizes in the 'CAVEATS' section below. +`objectname`:: The object name (aka SHA-1). For a non-ambiguous abbreviation of the object name append `:short`. For an abbreviation of the object name with desired length append - `:short=<length>`, where the minimum length is MINIMUM_ABBREV. The + `:short=<length>`, where the minimum length is `MINIMUM_ABBREV`. The length may be exceeded to ensure unique object names. -deltabase:: +`deltabase`:: This expands to the object name of the delta base for the given object, if it is stored as a delta. Otherwise it expands to the null object name (all zeroes). -upstream:: +`upstream`:: The name of a local ref which can be considered ``upstream'' from the displayed ref. Respects `:short`, `:lstrip` and `:rstrip` in the same way as `refname` above. Additionally @@ -176,100 +98,103 @@ Has no effect if the ref does not have tracking information associated with it. All the options apart from `nobracket` are mutually exclusive, but if used together the last option is selected. -push:: +`push`:: The name of a local ref which represents the `@{push}` location for the displayed ref. Respects `:short`, `:lstrip`, `:rstrip`, `:track`, `:trackshort`, `:remotename`, and `:remoteref` options as `upstream` does. Produces an empty string if no `@{push}` ref is configured. -HEAD:: - '*' if HEAD matches current ref (the checked out branch), ' ' +`HEAD`:: + `*` if `HEAD` matches current ref (the checked out branch), ' ' otherwise. -color:: +`color`:: Change output color. Followed by `:<colorname>`, where color names are described under Values in the "CONFIGURATION FILE" section of linkgit:git-config[1]. For example, `%(color:bold red)`. -align:: +`align`:: Left-, middle-, or right-align the content between - %(align:...) and %(end). The "align:" is followed by + `%(align:...)` and `%(end)`. The "`align:`" is followed by `width=<width>` and `position=<position>` in any order - separated by a comma, where the `<position>` is either left, - right or middle, default being left and `<width>` is the total + separated by a comma, where the _<position>_ is either `left`, + `right` or `middle`, default being `left` and _<width>_ is the total length of the content with alignment. For brevity, the "width=" and/or "position=" prefixes may be omitted, and bare - <width> and <position> used instead. For instance, + _<width>_ and _<position>_ used instead. For instance, `%(align:<width>,<position>)`. If the contents length is more than the width then no alignment is performed. If used with - `--quote` everything in between %(align:...) and %(end) is + `--quote` everything in between `%(align:...)` and `%(end)` is quoted, but if nested then only the topmost level performs quoting. -if:: - Used as %(if)...%(then)...%(end) or - %(if)...%(then)...%(else)...%(end). If there is an atom with - value or string literal after the %(if) then everything after - the %(then) is printed, else if the %(else) atom is used, then +`if`:: + Used as `%(if)...%(then)...%(end)` or + `%(if)...%(then)...%(else)...%(end)`. If there is an atom with + value or string literal after the `%(if)` then everything after + the `%(then)` is printed, else if the `%(else)` atom is used, then everything after %(else) is printed. We ignore space when - evaluating the string before %(then), this is useful when we - use the %(HEAD) atom which prints either "*" or " " and we - want to apply the 'if' condition only on the 'HEAD' ref. - Append ":equals=<string>" or ":notequals=<string>" to compare - the value between the %(if:...) and %(then) atoms with the + evaluating the string before `%(then)`, this is useful when we + use the `%(HEAD)` atom which prints either "`*`" or " " and we + want to apply the 'if' condition only on the `HEAD` ref. + Append "`:equals=<string>`" or "`:notequals=<string>`" to compare + the value between the `%(if:...)` and `%(then)` atoms with the given string. -symref:: +`symref`:: The ref which the given symbolic ref refers to. If not a symbolic ref, nothing is printed. Respects the `:short`, `:lstrip` and `:rstrip` options in the same way as `refname` above. -signature:: +`signature`:: The GPG signature of a commit. -signature:grade:: - Show "G" for a good (valid) signature, "B" for a bad - signature, "U" for a good signature with unknown validity, "X" - for a good signature that has expired, "Y" for a good - signature made by an expired key, "R" for a good signature - made by a revoked key, "E" if the signature cannot be - checked (e.g. missing key) and "N" for no signature. - -signature:signer:: +`signature:grade`:: + Show +`G`;; for a good (valid) signature +`B`;; for a bad signature +`U`;; for a good signature with unknown validity +`X`;; for a good signature that has expired +`Y`;; for a good signature made by an expired key +`R`;; for a good signature made by a revoked key +`E`;; if the signature cannot be checked (e.g. missing key) +`N`;; for no signature. + +`signature:signer`:: The signer of the GPG signature of a commit. -signature:key:: +`signature:key`:: The key of the GPG signature of a commit. -signature:fingerprint:: +`signature:fingerprint`:: The fingerprint of the GPG signature of a commit. -signature:primarykeyfingerprint:: +`signature:primarykeyfingerprint`:: The primary key fingerprint of the GPG signature of a commit. -signature:trustlevel:: +`signature:trustlevel`:: The trust level of the GPG signature of a commit. Possible outputs are `ultimate`, `fully`, `marginal`, `never` and `undefined`. -worktreepath:: +`worktreepath`:: The absolute path to the worktree in which the ref is checked out, if it is checked out in any linked worktree. Empty string otherwise. -ahead-behind:<committish>:: +`ahead-behind:<commit-ish>`:: Two integers, separated by a space, demonstrating the number of commits ahead and behind, respectively, when comparing the output - ref to the `<committish>` specified in the format. + ref to the _<committish>_ specified in the format. -is-base:<committish>:: - In at most one row, `(<committish>)` will appear to indicate the ref +`is-base:<commit-ish>`:: + In at most one row, `(<commit-ish>)` will appear to indicate the ref that is most likely the ref used as a starting point for the branch - that produced `<committish>`. This choice is made using a heuristic: + that produced _<commit-ish>_. This choice is made using a heuristic: choose the ref that minimizes the number of commits in the - first-parent history of `<committish>` and not in the first-parent + first-parent history of _<commit-ish>_ and not in the first-parent history of the ref. + For example, consider the following figure of first-parent histories of @@ -303,29 +228,29 @@ common first-parent ancestor of `B` and `C` and ties are broken by the earliest ref in the sorted order. + Note that this token will not appear if the first-parent history of -`<committish>` does not intersect the first-parent histories of the +_<commit-ish>_ does not intersect the first-parent histories of the filtered refs. -describe[:options]:: +`describe[:<option>,...]`:: A human-readable name, like linkgit:git-describe[1]; empty string for undescribable commits. The `describe` string may be followed by a colon and one or more comma-separated options. + -- -tags=<bool-value>;; +`tags=<bool-value>`;; Instead of only considering annotated tags, consider lightweight tags as well; see the corresponding option in linkgit:git-describe[1] for details. -abbrev=<number>;; - Use at least <number> hexadecimal digits; see the corresponding +`abbrev=<number>`;; + Use at least _<number>_ hexadecimal digits; see the corresponding option in linkgit:git-describe[1] for details. -match=<pattern>;; - Only consider tags matching the given `glob(7)` pattern, - excluding the "refs/tags/" prefix; see the corresponding option +`match=<pattern>`;; + Only consider tags matching the `glob`(7) _<pattern>_, + excluding the `refs/tags/` prefix; see the corresponding option in linkgit:git-describe[1] for details. -exclude=<pattern>;; - Do not consider tags matching the given `glob(7)` pattern, - excluding the "refs/tags/" prefix; see the corresponding option +`exclude=<pattern>`;; + Do not consider tags matching the `glob`(7) _<pattern>_, + excluding the `refs/tags/` prefix; see the corresponding option in linkgit:git-describe[1] for details. -- @@ -357,7 +282,7 @@ variable (see linkgit:gitmailmap[5]). The raw data in an object is `raw`. -raw:size:: +`raw:size`:: The raw data size of the object. Note that `--format=%(raw)` can not be used with `--python`, `--shell`, `--tcl`, @@ -367,10 +292,10 @@ variable type. The message in a commit or a tag object is `contents`, from which `contents:<part>` can be used to extract various parts out of: -contents:size:: +`contents:size`:: The size in bytes of the commit or tag message. -contents:subject:: +`contents:subject`:: The first paragraph of the message, which typically is a single line, is taken as the "subject" of the commit or the tag message. @@ -378,19 +303,19 @@ contents:subject:: obtain same results. `:sanitize` can be appended to `subject` for subject line suitable for filename. -contents:body:: +`contents:body`:: The remainder of the commit or the tag message that follows the "subject". -contents:signature:: +`contents:signature`:: The optional GPG signature of the tag. -contents:lines=N:: - The first `N` lines of the message. +`contents:lines=<n>`:: + The first _<n>_ lines of the message. Additionally, the trailers as interpreted by linkgit:git-interpret-trailers[1] -are obtained as `trailers[:options]` (or by using the historical alias -`contents:trailers[:options]`). For valid [:option] values see `trailers` +are obtained as `trailers[:<option>,...]` (or by using the historical alias +`contents:trailers[:<option>,...]`). For valid _<option>_ values see `trailers` section of linkgit:git-log[1]. For sorting purposes, fields with numeric values sort in numeric order @@ -410,8 +335,8 @@ option to linkgit:git-rev-list[1] takes). If this formatting is provided in a `--sort` key, references will be sorted according to the byte-value of the formatted string rather than the numeric value of the underlying timestamp. -Some atoms like %(align) and %(if) always require a matching %(end). -We call them "opening atoms" and sometimes denote them as %($open). +Some atoms like `%(align)` and `%(if)` always require a matching `%(end)`. +We call them "opening atoms" and sometimes denote them as `%($open)`. When a scripting language specific quoting is in effect, everything between a top-level opening atom and its matching %(end) is evaluated @@ -429,7 +354,7 @@ An example directly producing formatted text. Show the most recent #!/bin/sh git for-each-ref --count=3 --sort='-*authordate' \ ---format='From: %(*authorname) %(*authoremail) +`--format='From: %(*authorname) %(*authoremail) Subject: %(*subject) Date: %(*authordate) Ref: %(*refname) @@ -440,7 +365,7 @@ Ref: %(*refname) A simple example showing the use of shell eval on the output, -demonstrating the use of --shell. List the prefixes of all heads: +demonstrating the use of `--shell`. List the prefixes of all heads: ------------ #!/bin/sh @@ -508,7 +433,7 @@ eval "$eval" ------------ -An example to show the usage of %(if)...%(then)...%(else)...%(end). +An example to show the usage of `%(if)...%(then)...%(else)...%(end)`. This prefixes the current branch with a star. ------------ @@ -516,7 +441,7 @@ git for-each-ref --format="%(if)%(HEAD)%(then)* %(else) %(end)%(refname:short)" ------------ -An example to show the usage of %(if)...%(then)...%(end). +An example to show the usage of `%(if)...%(then)...%(end)`. This prints the authorname, if present. ------------ diff --git a/Documentation/git-format-patch.adoc b/Documentation/git-format-patch.adoc index a8b53db9a6..9a7807ca71 100644 --- a/Documentation/git-format-patch.adoc +++ b/Documentation/git-format-patch.adoc @@ -295,7 +295,8 @@ header). Note also that `git send-email` already handles this transformation for you, and this option should not be used if you are feeding the result to `git send-email`. ---[no-]force-in-body-from:: +--force-in-body-from:: +--no-force-in-body-from:: With the e-mail sender specified via the `--from` option, by default, an in-body "From:" to identify the real author of the commit is added at the top of the commit log message if @@ -314,7 +315,8 @@ feeding the result to `git send-email`. `Cc:`, and custom) headers added so far from config or command line. ---[no-]cover-letter:: +--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. @@ -379,7 +381,8 @@ configuration options in linkgit:git-notes[1] to use this workflow). The default is `--no-notes`, unless the `format.notes` configuration is set. ---[no-]signature=<signature>:: +--signature=<signature>:: +--no-signature:: Add a signature to each message produced. Per RFC 3676 the signature is separated from the body by a line with '-- ' on it. If the signature option is omitted the signature defaults to the Git version @@ -411,7 +414,8 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`. Output an all-zero hash in each patch's From header instead of the hash of the commit. ---[no-]base[=<commit>]:: +--no-base:: +--base[=<commit>]:: Record the base tree information to identify the state the patch series applies to. See the BASE TREE INFORMATION section below for details. If <commit> is "auto", a base commit is @@ -587,13 +591,19 @@ an external editor to keep Thunderbird from mangling the patches. Approach #1 (add-on) ^^^^^^^^^^^^^^^^^^^^ -Install the Toggle Word Wrap add-on that is available from -https://addons.mozilla.org/thunderbird/addon/toggle-word-wrap/ -It adds a menu entry "Enable Word Wrap" in the composer's "Options" menu +Install the Toggle Line Wrap add-on that is available from +https://addons.thunderbird.net/thunderbird/addon/toggle-line-wrap +It adds a button "Line Wrap" to the composer's toolbar that you can tick off. Now you can compose the message as you otherwise do (cut + paste, 'git format-patch' | 'git imap-send', etc), but you have to insert line breaks manually in any text that you type. +As a bonus feature, the add-on can detect patch text in the composer +and warns when line wrapping has not yet been turned off. + +The add-on requires a few tweaks of the advanced configuration +(about:config). These are listed on the download page. + Approach #2 (configuration) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Three steps: diff --git a/Documentation/git-fsck.adoc b/Documentation/git-fsck.adoc index 11203ba925..1751f692d4 100644 --- a/Documentation/git-fsck.adoc +++ b/Documentation/git-fsck.adoc @@ -31,7 +31,8 @@ index file, all SHA-1 references in the `refs` namespace, and all reflogs Print out objects that exist but that aren't reachable from any of the reference nodes. ---[no-]dangling:: +--dangling:: +--no-dangling:: Print objects that exist but that are never 'directly' used (default). `--no-dangling` can be used to omit this information from the output. @@ -97,14 +98,16 @@ care about this output and want to speed it up further. compatible with linkgit:git-rev-parse[1], e.g. `HEAD@{1234567890}~25^2:src/`. ---[no-]progress:: +--progress:: +--no-progress:: Progress status is reported on the standard error stream by default when it is attached to a terminal, unless --no-progress or --verbose is specified. --progress forces progress status even if the standard error stream is not directed to a terminal. ---[no-]references:: +--references:: +--no-references:: Control whether to check the references database consistency via 'git refs verify'. See linkgit:git-refs[1] for details. The default is to check the references database. diff --git a/Documentation/git-gc.adoc b/Documentation/git-gc.adoc index 526ce01463..6fed646dd8 100644 --- a/Documentation/git-gc.adoc +++ b/Documentation/git-gc.adoc @@ -53,11 +53,13 @@ configuration options such as `gc.auto` and `gc.autoPackLimit`, all other housekeeping tasks (e.g. rerere, working trees, reflog...) will be performed as well. ---[no-]detach:: +--detach:: +--no-detach:: Run in the background if the system supports it. This option overrides the `gc.autoDetach` config. ---[no-]cruft:: +--cruft:: +--no-cruft:: When expiring unreachable objects, pack them separately into a cruft pack instead of storing them as loose objects. `--cruft` is on by default. diff --git a/Documentation/git-http-fetch.adoc b/Documentation/git-http-fetch.adoc index 4ec7c68d3b..2200f073c4 100644 --- a/Documentation/git-http-fetch.adoc +++ b/Documentation/git-http-fetch.adoc @@ -25,8 +25,11 @@ commit-id:: Either the hash or the filename under [URL]/refs/ to pull. --a, -c, -t:: +-a:: +-c:: +-t:: These options are ignored for historical reasons. + -v:: Report what is downloaded. diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc index 26ccf4e433..278e5ccd36 100644 --- a/Documentation/git-imap-send.adoc +++ b/Documentation/git-imap-send.adoc @@ -9,21 +9,24 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder SYNOPSIS -------- [verse] -'git imap-send' [-v] [-q] [--[no-]curl] +'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] +'git imap-send' --list DESCRIPTION ----------- -This command uploads a mailbox generated with 'git format-patch' +This command uploads a mailbox generated with `git format-patch` into an IMAP drafts folder. This allows patches to be sent as other email is when using mail clients that cannot read mailbox files directly. The command also works with any general mailbox -in which emails have the fields "From", "Date", and "Subject" in +in which emails have the fields `From`, `Date`, and `Subject` in that order. Typical usage is something like: -git format-patch --signoff --stdout --attach origin | git imap-send +------ +$ git format-patch --signoff --stdout --attach origin | git imap-send +------ OPTIONS @@ -37,6 +40,11 @@ OPTIONS --quiet:: Be quiet. +-f <folder>:: +--folder=<folder>:: + Specify the folder in which the emails have to saved. + For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`. + --curl:: Use libcurl to communicate with the IMAP server, unless tunneling into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND @@ -47,6 +55,8 @@ OPTIONS using libcurl. Ignored if Git was built with the NO_OPENSSL option set. +--list:: + Run the IMAP LIST command to output a list of all the folders present. CONFIGURATION ------------- @@ -58,6 +68,34 @@ include::includes/cmd-config-section-rest.adoc[] include::config/imap.adoc[] +GETTING A LIST OF AVAILABLE FOLDERS +----------------------------------- + +In order to send an email to a specific folder, you need to know the correct name of +intended folder in your mailbox. The names like "Junk", "Trash" etc. displayed by +various email clients need not be the actual names of the folders stored in the mail +server of your email provider. + +In order to get the correct folder name to be used with `git imap-send`, you can run +`git imap-send --list`. This will display a list of valid folder names. An example +of such an output when run on a Gmail account is: + +......................... +* LIST (\HasNoChildren) "/" "INBOX" +* LIST (\HasChildren \Noselect) "/" "[Gmail]" +* LIST (\All \HasNoChildren) "/" "[Gmail]/All Mail" +* LIST (\Drafts \HasNoChildren) "/" "[Gmail]/Drafts" +* LIST (\HasNoChildren \Important) "/" "[Gmail]/Important" +* LIST (\HasNoChildren \Sent) "/" "[Gmail]/Sent Mail" +* LIST (\HasNoChildren \Junk) "/" "[Gmail]/Spam" +* LIST (\Flagged \HasNoChildren) "/" "[Gmail]/Starred" +* LIST (\HasNoChildren \Trash) "/" "[Gmail]/Trash" +......................... + +Here, you can observe that the correct name for the "Junk" folder is `[Gmail]/Spam` +and for the "Trash" folder is `[Gmail]/Trash`. Similar logic can be used to determine +other folders as well. + EXAMPLES -------- Using tunnel mode: @@ -102,20 +140,56 @@ Using Gmail's IMAP interface: --------- [imap] - folder = "[Gmail]/Drafts" - host = imaps://imap.gmail.com - user = user@gmail.com - port = 993 + folder = "[Gmail]/Drafts" + host = imaps://imap.gmail.com + user = user@gmail.com + port = 993 --------- +Gmail does not allow using your regular password for `git imap-send`. +If you have multi-factor authentication set up on your Gmail account, you +can generate an app-specific password for use with `git imap-send`. +Visit https://security.google.com/settings/security/apppasswords to create +it. Alternatively, use OAuth2.0 authentication as described below. + [NOTE] You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error -that the "Folder doesn't exist". +that the "Folder doesn't exist". You can also run `git imap-send --list` to get a +list of available folders. [NOTE] If your Gmail account is set to another language than English, the name of the "Drafts" folder will be localized. +If you want to use OAuth2.0 based authentication, you can specify +`OAUTHBEARER` or `XOAUTH2` mechanism in your config. It is more secure +than using app-specific passwords, and also does not enforce the need of +having multi-factor authentication. You will have to use an OAuth2.0 +access token in place of your password when using this authentication. + +--------- +[imap] + folder = "[Gmail]/Drafts" + host = imaps://imap.gmail.com + user = user@gmail.com + port = 993 + authmethod = OAUTHBEARER +--------- + +Using Outlook's IMAP interface: + +Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it +supports only `XOAUTH2` as the mechanism. + +--------- +[imap] + folder = "Drafts" + host = imaps://outlook.office365.com + user = user@outlook.com + port = 993 + authmethod = XOAUTH2 +--------- + Once the commits are ready to be sent, run the following command: $ git format-patch --cover-letter -M --stdout origin/master | git imap-send @@ -124,6 +198,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web interface will wrap lines no matter what, so you need to use a real IMAP client). +In case you are using OAuth2.0 authentication, it is easier to use credential +helpers to generate tokens. Credential helpers suggested in +linkgit:git-send-email[1] can be used for `git imap-send` as well. + CAUTION ------- It is still your responsibility to make sure that the email message diff --git a/Documentation/git-index-pack.adoc b/Documentation/git-index-pack.adoc index 270056cf63..18036953c0 100644 --- a/Documentation/git-index-pack.adoc +++ b/Documentation/git-index-pack.adoc @@ -36,7 +36,8 @@ OPTIONS fails if the name of packed archive does not end with .pack). ---[no-]rev-index:: +--rev-index:: +--no-rev-index:: When this flag is provided, generate a reverse index (a `.rev` file) corresponding to the given pack. If `--verify` is given, ensure that the existing diff --git a/Documentation/git-init.adoc b/Documentation/git-init.adoc index a0dffba665..bab99b9b47 100644 --- a/Documentation/git-init.adoc +++ b/Documentation/git-init.adoc @@ -77,9 +77,15 @@ If this is a reinitialization, the repository will be moved to the specified pat `-b <branch-name>`:: `--initial-branch=<branch-name>`:: Use _<branch-name>_ for the initial branch in the newly created -repository. If not specified, fall back to the default name (currently -`master`, but this is subject to change in the future; the name can be -customized via the `init.defaultBranch` configuration variable). +repository. If not specified, fall back to the default name +ifndef::with-breaking-changes[] +(currently `master`, but this will change to `main` when Git 3.0 is released). +endif::with-breaking-changes[] +ifdef::with-breaking-changes[] +`main`. +endif::with-breaking-changes[] +The default name can be customized via the `init.defaultBranch` configuration +variable. `--shared[=(false|true|umask|group|all|world|everybody|<perm>)]`:: diff --git a/Documentation/git-interpret-trailers.adoc b/Documentation/git-interpret-trailers.adoc index 82c8780d93..fd335fe772 100644 --- a/Documentation/git-interpret-trailers.adoc +++ b/Documentation/git-interpret-trailers.adoc @@ -142,8 +142,8 @@ OPTIONS 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 + '--if-exists' or '--no-if-exists'. Upon encountering '--no-if-exists', clear the + effect of any previous use of '--if-exists', such that the relevant configuration variables are no longer overridden. Possible actions are `addIfDifferent`, `addIfDifferentNeighbor`, `add`, `replace` and `doNothing`. @@ -154,8 +154,8 @@ OPTIONS 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 + '--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`. diff --git a/Documentation/git-last-modified.adoc b/Documentation/git-last-modified.adoc new file mode 100644 index 0000000000..602843e095 --- /dev/null +++ b/Documentation/git-last-modified.adoc @@ -0,0 +1,54 @@ +git-last-modified(1) +==================== + +NAME +---- +git-last-modified - EXPERIMENTAL: Show when files were last modified + + +SYNOPSIS +-------- +[synopsis] +git last-modified [--recursive] [--show-trees] [<revision-range>] [[--] <path>...] + +DESCRIPTION +----------- + +Shows which commit last modified each of the relevant files and subdirectories. +A commit renaming a path, or changing it's mode is also taken into account. + +THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE. + +OPTIONS +------- + +`-r`:: +`--recursive`:: + Instead of showing tree entries, step into subtrees and show all entries + inside them recursively. + +`-t`:: +`--show-trees`:: + Show tree entries even when recursing into them. It has no effect + without `--recursive`. + +`<revision-range>`:: + Only traverse commits in the specified revision range. When no + `<revision-range>` is specified, it defaults to `HEAD` (i.e. the whole + history leading to the current commit). For a complete list of ways to + spell `<revision-range>`, see the 'Specifying Ranges' section of + linkgit:gitrevisions[7]. + +`[--] <path>...`:: + For each _<path>_ given, the commit which last modified it is returned. + Without an optional path parameter, all files and subdirectories + in path traversal the are included in the output. + +SEE ALSO +-------- +linkgit:git-blame[1], +linkgit:git-log[1]. + +GIT +--- +Part of the linkgit:git[1] suite diff --git a/Documentation/git-log.adoc b/Documentation/git-log.adoc index ae8a7e2d63..e304739c5e 100644 --- a/Documentation/git-log.adoc +++ b/Documentation/git-log.adoc @@ -8,8 +8,8 @@ git-log - Show commit logs SYNOPSIS -------- -[verse] -'git log' [<options>] [<revision-range>] [[--] <path>...] +[synopsis] +git log [<options>] [<revision-range>] [[--] <path>...] DESCRIPTION ----------- @@ -27,28 +27,34 @@ each commit introduces are shown. OPTIONS ------- ---follow:: +`--follow`:: Continue listing the history of a file beyond renames (works only for a single file). ---no-decorate:: ---decorate[=short|full|auto|no]:: - Print out the ref names of any commits that are shown. If 'short' is - specified, the ref name prefixes 'refs/heads/', 'refs/tags/' and - 'refs/remotes/' will not be printed. If 'full' is specified, the - full ref name (including prefix) will be printed. If 'auto' is - specified, then if the output is going to a terminal, the ref names - are shown as if 'short' were given, otherwise no ref names are - shown. The option `--decorate` is short-hand for `--decorate=short`. - Default to configuration value of `log.decorate` if configured, - otherwise, `auto`. - ---decorate-refs=<pattern>:: ---decorate-refs-exclude=<pattern>:: +`--no-decorate`:: +`--decorate[=(short|full|auto|no)]`:: + Print out the ref names of any commits that are shown. Possible values + are: ++ +---- +`short`;; the ref name prefixes `refs/heads/`, `refs/tags/` and + `refs/remotes/` are not printed. +`full`;; the full ref name (including prefix) is printed. +`auto`:: if the output is going to a terminal, the ref names + are shown as if `short` were given, otherwise no ref names are + shown. +---- ++ +The option `--decorate` is short-hand for `--decorate=short`. Default to +configuration value of `log.decorate` if configured, otherwise, `auto`. + +`--decorate-refs=<pattern>`:: +`--decorate-refs-exclude=<pattern>`:: For each candidate reference, do not use it for decoration if it - matches any patterns given to `--decorate-refs-exclude` or if it - doesn't match any of the patterns given to `--decorate-refs`. The - `log.excludeDecoration` config option allows excluding refs from + matches any of the _<pattern>_ parameters given to + `--decorate-refs-exclude` or if it doesn't match any of the + _<pattern>_ parameters given to `--decorate-refs`. + The `log.excludeDecoration` config option allows excluding refs from the decorations, but an explicit `--decorate-refs` pattern will override a match in `log.excludeDecoration`. + @@ -56,51 +62,53 @@ If none of these options or config settings are given, then references are used as decoration if they match `HEAD`, `refs/heads/`, `refs/remotes/`, `refs/stash/`, or `refs/tags/`. ---clear-decorations:: +`--clear-decorations`:: When specified, this option clears all previous `--decorate-refs` or `--decorate-refs-exclude` options and relaxes the default decoration filter to include all references. This option is assumed if the config value `log.initialDecorationSet` is set to `all`. ---source:: +`--source`:: Print out the ref name given on the command line by which each commit was reached. ---[no-]mailmap:: ---[no-]use-mailmap:: +`--mailmap`:: +`--no-mailmap`:: +`--use-mailmap`:: +`--no-use-mailmap`:: Use mailmap file to map author and committer names and email addresses to canonical real names and email addresses. See linkgit:git-shortlog[1]. ---full-diff:: +`--full-diff`:: Without this flag, `git log -p <path>...` shows commits that touch the specified paths, and diffs about the same specified paths. With this, the full diff is shown for commits that touch - the specified paths; this means that "<path>..." limits only + the specified paths; this means that "`<path>...`" limits only commits, and doesn't limit diff for those commits. + Note that this affects all diff-based output types, e.g. those produced by `--stat`, etc. ---log-size:: - Include a line ``log size <number>'' in the output for each commit, - where <number> is the length of that commit's message in bytes. +`--log-size`:: + Include a line `log size <number>` in the output for each commit, + where _<number>_ is the length of that commit's message in bytes. Intended to speed up tools that read log messages from `git log` output by allowing them to allocate space in advance. include::line-range-options.adoc[] -<revision-range>:: +_<revision-range>_:: Show only commits in the specified revision range. When no - <revision-range> is specified, it defaults to `HEAD` (i.e. the + _<revision-range>_ is specified, it defaults to `HEAD` (i.e. the whole history leading to the current commit). `origin..HEAD` specifies all the commits reachable from the current commit (i.e. `HEAD`), but not from `origin`. For a complete list of - ways to spell <revision-range>, see the 'Specifying Ranges' + ways to spell _<revision-range>_, see the 'Specifying Ranges' section of linkgit:gitrevisions[7]. -[--] <path>...:: +`[--] <path>...`:: Show only commits that are enough to explain how the files that match the specified paths came to be. See 'History Simplification' below for details and other simplification @@ -145,14 +153,14 @@ EXAMPLES `git log --since="2 weeks ago" -- gitk`:: - Show the changes during the last two weeks to the file 'gitk'. + Show the changes during the last two weeks to the file `gitk`. The `--` is necessary to avoid confusion with the *branch* named - 'gitk' + `gitk` `git log --name-status release..test`:: - Show the commits that are in the "test" branch but not yet - in the "release" branch, along with the list of paths + Show the commits that are in the "`test`" branch but not yet + in the "`release`" branch, along with the list of paths each commit modifies. `git log --follow builtin/rev-list.c`:: @@ -164,7 +172,7 @@ EXAMPLES `git log --branches --not --remotes=origin`:: Shows all commits that are in any of local branches but not in - any of remote-tracking branches for 'origin' (what you have that + any of remote-tracking branches for `origin` (what you have that origin doesn't). `git log master --not --remotes=*/master`:: @@ -200,11 +208,11 @@ CONFIGURATION See linkgit:git-config[1] for core variables and linkgit:git-diff[1] for settings related to diff generation. -format.pretty:: +`format.pretty`:: Default for the `--format` option. (See 'Pretty Formats' above.) Defaults to `medium`. -i18n.logOutputEncoding:: +`i18n.logOutputEncoding`:: Encoding to use when displaying logs. (See 'Discussion' above.) Defaults to the value of `i18n.commitEncoding` if set, and UTF-8 otherwise. diff --git a/Documentation/git-merge-tree.adoc b/Documentation/git-merge-tree.adoc index f824eea61f..271ab220e8 100644 --- a/Documentation/git-merge-tree.adoc +++ b/Documentation/git-merge-tree.adoc @@ -59,7 +59,8 @@ OPTIONS do not list filenames multiple times if they have multiple conflicting stages). ---[no-]messages:: +--messages:: +--no-messages:: Write any informational messages such as "Auto-merging <path>" or CONFLICT notices to the end of stdout. If unspecified, the default is to include these messages if there are merge diff --git a/Documentation/git-merge.adoc b/Documentation/git-merge.adoc index 12aa859d16..a055384ad6 100644 --- a/Documentation/git-merge.adoc +++ b/Documentation/git-merge.adoc @@ -9,7 +9,7 @@ git-merge - Join two or more development histories together SYNOPSIS -------- [synopsis] -git merge [-n] [--stat] [--no-commit] [--squash] [--[no-]edit] +git merge [-n] [--stat] [--compact-summary] [--no-commit] [--squash] [--[no-]edit] [--no-verify] [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]] [--[no-]allow-unrelated-histories] [--[no-]rerere-autoupdate] [-m <msg>] [-F <file>] @@ -28,8 +28,8 @@ Assume the following history exists and the current branch is `master`: ------------ - A---B---C topic - / + A---B---C topic + / D---E---F---G master ------------ @@ -38,11 +38,11 @@ Then `git merge topic` will replay the changes made on the its current commit (`C`) on top of `master`, and record the result in a new commit along with the names of the two parent commits and a log message from the user describing the changes. Before the operation, -`ORIG_HEAD` is set to the tip of the current branch (`C`). +`ORIG_HEAD` is set to the tip of the current branch (`G`). ------------ - A---B---C topic - / \ + A---B---C topic + / \ D---E---F---G---H master ------------ diff --git a/Documentation/git-multi-pack-index.adoc b/Documentation/git-multi-pack-index.adoc index b6cd0d7f85..2f642697e9 100644 --- a/Documentation/git-multi-pack-index.adoc +++ b/Documentation/git-multi-pack-index.adoc @@ -25,10 +25,11 @@ OPTIONS + `<dir>` must be an alternate of the current repository. ---[no-]progress:: +--progress:: +--no-progress:: Turn progress on/off explicitly. If neither is specified, progress is shown if standard error is connected to a terminal. Supported by - sub-commands `write`, `verify`, `expire`, and `repack. + sub-commands `write`, `verify`, `expire`, and `repack`. The following subcommands are available: diff --git a/Documentation/git-p4.adoc b/Documentation/git-p4.adoc index f97b786bf9..59edd24134 100644 --- a/Documentation/git-p4.adoc +++ b/Documentation/git-p4.adoc @@ -66,6 +66,7 @@ Clone ~~~~~ Generally, 'git p4 clone' is used to create a new Git directory from an existing p4 repository: + ------------ $ git p4 clone //depot/path/project ------------ diff --git a/Documentation/git-pack-objects.adoc b/Documentation/git-pack-objects.adoc index 7f69ae4855..71b9682485 100644 --- a/Documentation/git-pack-objects.adoc +++ b/Documentation/git-pack-objects.adoc @@ -10,13 +10,13 @@ SYNOPSIS -------- [verse] 'git pack-objects' [-q | --progress | --all-progress] [--all-progress-implied] - [--no-reuse-delta] [--delta-base-offset] [--non-empty] - [--local] [--incremental] [--window=<n>] [--depth=<n>] - [--revs [--unpacked | --all]] [--keep-pack=<pack-name>] - [--cruft] [--cruft-expiration=<time>] - [--stdout [--filter=<filter-spec>] | <base-name>] - [--shallow] [--keep-true-parents] [--[no-]sparse] - [--name-hash-version=<n>] < <object-list> + [--no-reuse-delta] [--delta-base-offset] [--non-empty] + [--local] [--incremental] [--window=<n>] [--depth=<n>] + [--revs [--unpacked | --all]] [--keep-pack=<pack-name>] + [--cruft] [--cruft-expiration=<time>] + [--stdout [--filter=<filter-spec>] | <base-name>] + [--shallow] [--keep-true-parents] [--[no-]sparse] + [--name-hash-version=<n>] [--path-walk] < <object-list> DESCRIPTION @@ -87,13 +87,21 @@ base-name:: reference was included in the resulting packfile. This can be useful to send new tags to native Git clients. ---stdin-packs:: +--stdin-packs[=<mode>]:: Read the basenames of packfiles (e.g., `pack-1234abcd.pack`) from the standard input, instead of object names or revision arguments. The resulting pack contains all objects listed in the 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. ++ Incompatible with `--revs`, or options that imply `--revs` (such as `--all`), with the exception of `--unpacked`, which is compatible. @@ -235,7 +243,8 @@ depth is 4095. Add --no-reuse-object if you want to force a uniform compression level on all data no matter the source. ---[no-]sparse:: +--sparse:: +--no-sparse:: Toggle the "sparse" algorithm to determine which objects to include in the pack, when combined with the "--revs" option. This algorithm only walks trees that appear in paths that introduce new objects. @@ -375,6 +384,17 @@ many different directories. At the moment, this version is not allowed when writing reachability bitmap files with `--write-bitmap-index` and it will be automatically changed to version `1`. +--path-walk:: + Perform compression by first organizing objects by path, then a + second pass that compresses across paths as normal. This has the + potential to improve delta compression especially in the presence + of filenames that cause collisions in Git's default name-hash + algorithm. ++ +Incompatible with `--delta-islands`, `--shallow`, or `--filter`. The +`--use-bitmap-index` option will be ignored in the presence of +`--path-walk.` + DELTA ISLANDS ------------- diff --git a/Documentation/git-pack-refs.adoc b/Documentation/git-pack-refs.adoc index 652c549771..42b90051e6 100644 --- a/Documentation/git-pack-refs.adoc +++ b/Documentation/git-pack-refs.adoc @@ -66,7 +66,10 @@ Pack refs as needed depending on the current state of the ref database. The behavior depends on the ref format used by the repository and may change in the future. + - - "files": No special handling for `--auto` has been implemented. + - "files": Loose references are packed into the `packed-refs` file + based on the ratio of loose references to the size of the + `packed-refs` file. The bigger the `packed-refs` file, the more loose + references need to exist before we repack. + - "reftable": Tables are compacted such that they form a geometric sequence. For two tables N and N+1, where N+1 is newer, this diff --git a/Documentation/git-pull.adoc b/Documentation/git-pull.adoc index 3f4ecc4730..48e924a10a 100644 --- a/Documentation/git-pull.adoc +++ b/Documentation/git-pull.adoc @@ -87,7 +87,8 @@ OPTIONS --verbose:: Pass --verbose to git-fetch and git-merge. ---[no-]recurse-submodules[=(yes|on-demand|no)]:: +--recurse-submodules[=(yes|on-demand|no)]:: +--no-recurse-submodules:: This option controls if new commits of populated submodules should be fetched, and if the working trees of active submodules should be updated, too (see linkgit:git-fetch[1], linkgit:git-config[1] and diff --git a/Documentation/git-push.adoc b/Documentation/git-push.adoc index d1978650d6..5f5408e2c0 100644 --- a/Documentation/git-push.adoc +++ b/Documentation/git-push.adoc @@ -197,7 +197,8 @@ already exists on the remote side. with configuration variable `push.followTags`. For more information, see `push.followTags` in linkgit:git-config[1]. ---[no-]signed:: +--signed:: +--no-signed:: --signed=(true|false|if-asked):: GPG-sign the push request to update refs on the receiving side, to allow it to be checked by the hooks and/or be @@ -208,7 +209,8 @@ already exists on the remote side. will also fail if the actual call to `gpg --sign` fails. See linkgit:git-receive-pack[1] for the details on the receiving end. ---[no-]atomic:: +--atomic:: +--no-atomic:: Use an atomic transaction on the remote side if available. Either all refs are updated, or on error, no refs are updated. If the server does not support atomic pushes the push will fail. @@ -232,7 +234,8 @@ already exists on the remote side. repository over ssh, and you do not have the program in a directory on the default $PATH. ---[no-]force-with-lease:: +--force-with-lease:: +--no-force-with-lease:: --force-with-lease=<refname>:: --force-with-lease=<refname>:<expect>:: Usually, "git push" refuses to update a remote ref that is @@ -350,7 +353,8 @@ one branch, use a `+` in front of the refspec to push (e.g `git push origin +master` to force a push to the `master` branch). See the `<refspec>...` section above for details. ---[no-]force-if-includes:: +--force-if-includes:: +--no-force-if-includes:: Force an update only if the tip of the remote-tracking ref has been integrated locally. + @@ -377,7 +381,8 @@ Specifying `--no-force-if-includes` disables this behavior. linkgit:git-pull[1] and other commands. For more information, see `branch.<name>.merge` in linkgit:git-config[1]. ---[no-]thin:: +--thin:: +--no-thin:: These options are passed to linkgit:git-send-pack[1]. A thin transfer significantly reduces the amount of sent data when the sender and receiver share many of the same objects in common. The default is @@ -419,7 +424,8 @@ When using 'on-demand' or 'only', if a submodule has a "push.recurseSubmodules={on-demand,only}" or "submodule.recurse" configuration, further recursion will occur. In this case, "only" is treated as "on-demand". ---[no-]verify:: +--verify:: +--no-verify:: Toggle the pre-push hook (see linkgit:githooks[5]). The default is --verify, giving the hook a chance to prevent the push. With --no-verify, the hook is bypassed completely. diff --git a/Documentation/git-range-diff.adoc b/Documentation/git-range-diff.adoc index db0e4279b5..b5e85d37f1 100644 --- a/Documentation/git-range-diff.adoc +++ b/Documentation/git-range-diff.adoc @@ -96,7 +96,8 @@ diff. --remerge-diff:: Convenience option, equivalent to `--diff-merges=remerge`. ---[no-]notes[=<ref>]:: +--notes[=<ref>]:: +--no-notes:: This flag is passed to the `git log` program (see linkgit:git-log[1]) that generates the patches. diff --git a/Documentation/git-read-tree.adoc b/Documentation/git-read-tree.adoc index 1c48c28996..1c04bba2b7 100644 --- a/Documentation/git-read-tree.adoc +++ b/Documentation/git-read-tree.adoc @@ -100,7 +100,8 @@ OPTIONS directories the index file and index output file are located in. ---[no-]recurse-submodules:: +--recurse-submodules:: +--no-recurse-submodules:: Using --recurse-submodules will update the content of all active submodules according to the commit recorded in the superproject by calling read-tree recursively, also setting the submodules' HEAD to be diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc index 956d3048f5..005caf6164 100644 --- a/Documentation/git-rebase.adoc +++ b/Documentation/git-rebase.adoc @@ -16,49 +16,12 @@ SYNOPSIS DESCRIPTION ----------- -If `<branch>` is specified, `git rebase` will perform an automatic -`git switch <branch>` before doing anything else. Otherwise -it remains on the current branch. +Transplant a series of commits onto a different starting point. +You can also use `git rebase` to reorder or combine commits: see INTERACTIVE +MODE below for how to do that. -If `<upstream>` is not specified, the upstream configured in -`branch.<name>.remote` and `branch.<name>.merge` options will be used (see -linkgit:git-config[1] for details) and the `--fork-point` option is -assumed. If you are currently not on any branch or if the current -branch does not have a configured upstream, the rebase will abort. - -All changes made by commits in the current branch but that are not -in `<upstream>` are saved to a temporary area. This is the same set -of commits that would be shown by `git log <upstream>..HEAD`; or by -`git log 'fork_point'..HEAD`, if `--fork-point` is active (see the -description on `--fork-point` below); or by `git log HEAD`, if the -`--root` option is specified. - -The current branch is reset to `<upstream>` or `<newbase>` if the -`--onto` option was supplied. This has the exact same effect as -`git reset --hard <upstream>` (or `<newbase>`). `ORIG_HEAD` is set -to point at the tip of the branch before the reset. - -[NOTE] -`ORIG_HEAD` is not guaranteed to still point to the previous branch tip -at the end of the rebase if other commands that write that pseudo-ref -(e.g. `git reset`) are used during the rebase. The previous branch tip, -however, is accessible using the reflog of the current branch -(i.e. `@{1}`, see linkgit:gitrevisions[7]). - -The commits that were previously saved into the temporary area are -then reapplied to the current branch, one by one, in order. Note that -any commits in `HEAD` which introduce the same textual changes as a commit -in `HEAD..<upstream>` are omitted (i.e., a patch already accepted upstream -with a different commit message or timestamp will be skipped). - -It is possible that a merge failure will prevent this process from being -completely automatic. You will have to resolve any such merge failure -and run `git rebase --continue`. Another option is to bypass the commit -that caused the merge failure with `git rebase --skip`. To check out the -original `<branch>` and remove the `.git/rebase-apply` working files, use -the command `git rebase --abort` instead. - -Assume the following history exists and the current branch is "topic": +For example, imagine that you have been working on the `topic` branch in this +history, and you want to "catch up" to the work done on the `master` branch. ------------ A---B---C topic @@ -66,13 +29,11 @@ Assume the following history exists and the current branch is "topic": D---E---F---G master ------------ -From this point, the result of either of the following commands: - - - git rebase master - git rebase master topic - -would be: +You want to transplant the commits you made on `topic` since it diverged from +`master` (i.e. A, B, and C), on top of the current `master`. You can do this +by running `git rebase master` while the `topic` branch is checked out. If you +want to rebase `topic` while on another branch, `git rebase master topic` is a +shortcut for `git checkout topic && git rebase master`. ------------ A'--B'--C' topic @@ -80,30 +41,56 @@ would be: D---E---F---G master ------------ -*NOTE:* The latter form is just a short-hand of `git checkout topic` -followed by `git rebase master`. When rebase exits `topic` will -remain the checked-out branch. -If the upstream branch already contains a change you have made (e.g., -because you mailed a patch which was applied upstream), then that commit -will be skipped and warnings will be issued (if the 'merge' backend is -used). For example, running `git rebase master` on the following -history (in which `A'` and `A` introduce the same set of changes, but -have different committer information): +If there is a merge conflict during this process, `git rebase` will stop at the +first problematic commit and leave conflict markers. If this happens, you can do +one of these things: ------------- - A---B---C topic - / - D---E---A'---F master ------------- +1. Resolve the conflict. You can use `git diff` to find the markers (<<<<<<) + and make edits to resolve the conflict. For each file you edit, you need to + tell Git that the conflict has been resolved. You can mark the conflict as + resolved with `git add <filename>`. After resolving all of the conflicts, + you can continue the rebasing process with -will result in: + git rebase --continue ------------- - B'---C' topic - / - D---E---A'---F master ------------- +2. Stop the `git rebase` and return your branch to its original state with + + git rebase --abort + +3. Skip the commit that caused the merge conflict with + + git rebase --skip + +If you don't specify an `<upstream>` to rebase onto, the upstream configured in +`branch.<name>.remote` and `branch.<name>.merge` options will be used (see +linkgit:git-config[1] for details) and the `--fork-point` option is +assumed. If you are currently not on any branch or if the current +branch does not have a configured upstream, the rebase will abort. + +Here is a simplified description of what `git rebase <upstream>` does: + +1. Make a list of all commits on your current branch since it branched + off from `<upstream>` that do not have an equivalent commit in + `<upstream>`. +2. Check out `<upstream>` with the equivalent of + `git checkout --detach <upstream>`. +3. Replay the commits, one by one, in order. This is similar to running + `git cherry-pick <commit>` for each commit. See REBASING MERGES for how merges + are handled. +4. Update your branch to point to the final commit with the equivalent + of `git checkout -B <branch>`. + +[NOTE] +When starting the rebase, `ORIG_HEAD` is set to point to the commit at the tip +of the to-be-rebased branch. However, `ORIG_HEAD` is not guaranteed to still +point to that commit at the end of the rebase if other commands that change +`ORIG_HEAD` (like `git reset`) are used during the rebase. The previous branch +tip, however, is accessible using the reflog of the current branch (i.e. `@{1}`, +see linkgit:gitrevisions[7]. + +TRANSPLANTING A TOPIC BRANCH WITH --ONTO +---------------------------------------- Here is how you would transplant a topic branch based on one branch to another, to pretend that you forked the topic branch @@ -186,28 +173,6 @@ This is useful if F and G were flawed in some way, or should not be part of topicA. Note that the argument to `--onto` and the `<upstream>` parameter can be any valid commit-ish. -In case of conflict, `git rebase` will stop at the first problematic commit -and leave conflict markers in the tree. You can use `git diff` to locate -the markers (<<<<<<) and make edits to resolve the conflict. For each -file you edit, you need to tell Git that the conflict has been resolved, -typically this would be done with - - - git add <filename> - - -After resolving the conflict manually and updating the index with the -desired resolution, you can continue the rebasing process with - - - git rebase --continue - - -Alternatively, you can undo the 'git rebase' with - - - git rebase --abort - MODE OPTIONS ------------ @@ -253,6 +218,8 @@ As a special case, you may use "A\...B" as a shortcut for the merge base of A and B if there is exactly one merge base. You can leave out at most one of A and B, in which case it defaults to HEAD. +See TRANSPLANTING A TOPIC BRANCH WITH --ONTO above for examples. + --keep-base:: Set the starting point at which to create the new commits to the merge base of `<upstream>` and `<branch>`. Running @@ -687,7 +654,7 @@ In addition, the following pairs of options are incompatible: * --fork-point and --root BEHAVIORAL DIFFERENCES ------------------------ +---------------------- `git rebase` has two primary backends: 'apply' and 'merge'. (The 'apply' backend used to be known as the 'am' backend, but the name led to diff --git a/Documentation/git-reflog.adoc b/Documentation/git-reflog.adoc index 412f06b8fe..38af0c977a 100644 --- a/Documentation/git-reflog.adoc +++ b/Documentation/git-reflog.adoc @@ -8,16 +8,17 @@ git-reflog - Manage reflog information SYNOPSIS -------- -[verse] -'git reflog' [show] [<log-options>] [<ref>] -'git reflog list' -'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>] +[synopsis] +git reflog [show] [<log-options>] [<ref>] +git reflog list +git reflog exists <ref> +git reflog write <ref> <old-oid> <new-oid> <message> +git reflog delete [--rewrite] [--updateref] + [--dry-run | -n] [--verbose] <ref>@{<specifier>}... +git reflog drop [--all [--single-worktree] | <refs>...] +git reflog expire [--expire=<time>] [--expire-unreachable=<time>] [--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...] -'git reflog delete' [--rewrite] [--updateref] - [--dry-run | -n] [--verbose] <ref>@{<specifier>}... -'git reflog drop' [--all [--single-worktree] | <refs>...] -'git reflog exists' <ref> DESCRIPTION ----------- @@ -43,11 +44,15 @@ actions, and in addition the `HEAD` reflog records branch switching. The "list" subcommand lists all refs which have a corresponding reflog. -The "expire" subcommand prunes older reflog entries. Entries older -than `expire` time, or entries older than `expire-unreachable` time -and not reachable from the current tip, are removed from the reflog. -This is typically not used directly by end users -- instead, see -linkgit:git-gc[1]. +The "exists" subcommand checks whether a ref has a reflog. It exits +with zero status if the reflog exists, and non-zero status if it does +not. + +The "write" subcommand writes a single entry to the reflog of a given +reference. This new entry is appended to the reflog and will thus become +the most recent entry. The reference name must be fully qualified. Both the old +and new object IDs must not be abbreviated and must point to existing objects. +The reflog message gets normalized. The "delete" subcommand deletes single entries from the reflog, but not the reflog itself. Its argument must be an _exact_ entry (e.g. "`git @@ -58,9 +63,11 @@ The "drop" subcommand completely removes the reflog for the specified references. This is in contrast to "expire" and "delete", both of which can be used to delete reflog entries, but not the reflog itself. -The "exists" subcommand checks whether a ref has a reflog. It exits -with zero status if the reflog exists, and non-zero status if it does -not. +The "expire" subcommand prunes older reflog entries. Entries older +than `expire` time, or entries older than `expire-unreachable` time +and not reachable from the current tip, are removed from the reflog. +This is typically not used directly by end users -- instead, see +linkgit:git-gc[1]. OPTIONS ------- @@ -71,18 +78,37 @@ Options for `show` `git reflog show` accepts any of the options accepted by `git log`. +Options for `delete` +~~~~~~~~~~~~~~~~~~~~ + +`git reflog delete` accepts options `--updateref`, `--rewrite`, `-n`, +`--dry-run`, and `--verbose`, with the same meanings as when they are +used with `expire`. + +Options for `drop` +~~~~~~~~~~~~~~~~~~ + +`--all`:: + Drop the reflogs of all references from all worktrees. + +`--single-worktree`:: + By default when `--all` is specified, reflogs from all working + trees are dropped. This option limits the processing to reflogs + from the current working tree only. + + Options for `expire` ~~~~~~~~~~~~~~~~~~~~ ---all:: +`--all`:: Process the reflogs of all references. ---single-worktree:: +`--single-worktree`:: By default when `--all` is specified, reflogs from all working trees are processed. This option limits the processing to reflogs from the current working tree only. ---expire=<time>:: +`--expire=<time>`:: Prune entries older than the specified time. If this option is not specified, the expiration time is taken from the configuration setting `gc.reflogExpire`, which in turn @@ -90,7 +116,7 @@ Options for `expire` of their age; `--expire=never` turns off pruning of reachable entries (but see `--expire-unreachable`). ---expire-unreachable=<time>:: +`--expire-unreachable=<time>`:: Prune entries older than `<time>` that are not reachable from the current tip of the branch. If this option is not specified, the expiration time is taken from the configuration @@ -100,17 +126,17 @@ Options for `expire` turns off early pruning of unreachable entries (but see `--expire`). ---updateref:: +`--updateref`:: Update the reference to the value of the top reflog entry (i.e. <ref>@\{0\}) if the previous top entry was pruned. (This option is ignored for symbolic references.) ---rewrite:: +`--rewrite`:: If a reflog entry's predecessor is pruned, adjust its "old" SHA-1 to be equal to the "new" SHA-1 field of the entry that now precedes it. ---stale-fix:: +`--stale-fix`:: Prune any reflog entries that point to "broken commits". A broken commit is a commit that is not reachable from any of the reference tips and that refers, directly or indirectly, to @@ -121,33 +147,15 @@ has the same cost as 'git prune'. It is primarily intended to fix corruption caused by garbage collecting using older versions of Git, which didn't protect objects referred to by reflogs. --n:: ---dry-run:: +`-n`:: +`--dry-run`:: Do not actually prune any entries; just show what would have been pruned. ---verbose:: +`--verbose`:: Print extra information on screen. -Options for `delete` -~~~~~~~~~~~~~~~~~~~~ - -`git reflog delete` accepts options `--updateref`, `--rewrite`, `-n`, -`--dry-run`, and `--verbose`, with the same meanings as when they are -used with `expire`. - -Options for `drop` -~~~~~~~~~~~~~~~~~~ - ---all:: - Drop the reflogs of all references from all worktrees. - ---single-worktree:: - By default when `--all` is specified, reflogs from all working - trees are dropped. This option limits the processing to reflogs - from the current working tree only. - GIT --- Part of the linkgit:git[1] suite diff --git a/Documentation/git-refs.adoc b/Documentation/git-refs.adoc index 4d6dc994f9..bfa9b3ea2d 100644 --- a/Documentation/git-refs.adoc +++ b/Documentation/git-refs.adoc @@ -11,6 +11,14 @@ SYNOPSIS [synopsis] git refs migrate --ref-format=<format> [--no-reflog] [--dry-run] git refs verify [--strict] [--verbose] +git refs list [--count=<count>] [--shell|--perl|--python|--tcl] + [(--sort=<key>)...] [--format=<format>] + [--include-root-refs] [--points-at=<object>] + [--merged[=<object>]] [--no-merged[=<object>]] + [--contains[=<object>]] [--no-contains[=<object>]] + [(--exclude=<pattern>)...] [--start-after=<marker>] + [ --stdin | (<pattern>...)] +git refs exists <ref> DESCRIPTION ----------- @@ -20,43 +28,58 @@ This command provides low-level access to refs. COMMANDS -------- -migrate:: +`migrate`:: Migrate ref store between different formats. -verify:: +`verify`:: Verify reference database consistency. +list:: + List references in the repository with support for filtering, + formatting, and sorting. This subcommand is an alias for + linkgit:git-for-each-ref[1] and offers identical functionality. + +exists:: + Check whether the given reference exists. Returns an exit code of 0 if + it does, 2 if it is missing, and 1 in case looking up the reference + failed with an error other than the reference being missing. This does + not verify whether the reference resolves to an actual object. + OPTIONS ------- -The following options are specific to 'git refs migrate': +The following options are specific to `git refs migrate`: ---ref-format=<format>:: +`--ref-format=<format>`:: The ref format to migrate the ref store to. Can be one of: + include::ref-storage-format.adoc[] ---dry-run:: +`--dry-run`:: Perform the migration, but do not modify the repository. The migrated refs will be written into a separate directory that can be inspected separately. The name of the directory will be reported on stdout. This can be used to double check that the migration works as expected before performing the actual migration. ---reflog:: ---no-reflog:: +`--reflog`:: +`--no-reflog`:: Choose between migrating the reflog data to the new backend, and discarding them. The default is "--reflog", to migrate. -The following options are specific to 'git refs verify': +The following options are specific to `git refs verify`: ---strict:: +`--strict`:: Enable stricter error checking. This will cause warnings to be reported as errors. See linkgit:git-fsck[1]. ---verbose:: +`--verbose`:: When verifying the reference database consistency, be chatty. +The following options are specific to 'git refs list': + +include::for-each-ref-options.adoc[] + KNOWN LIMITATIONS ----------------- diff --git a/Documentation/git-repack.adoc b/Documentation/git-repack.adoc index e1cd75eebe..d12c4985f6 100644 --- a/Documentation/git-repack.adoc +++ b/Documentation/git-repack.adoc @@ -11,7 +11,7 @@ SYNOPSIS [verse] 'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m] [--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>] - [--write-midx] [--name-hash-version=<n>] + [--write-midx] [--name-hash-version=<n>] [--path-walk] DESCRIPTION ----------- @@ -258,6 +258,9 @@ linkgit:git-multi-pack-index[1]). Provide this argument to the underlying `git pack-objects` process. See linkgit:git-pack-objects[1] for full details. +--path-walk:: + Pass the `--path-walk` option to the underlying `git pack-objects` + process. See linkgit:git-pack-objects[1] for full details. CONFIGURATION ------------- diff --git a/Documentation/git-repo.adoc b/Documentation/git-repo.adoc new file mode 100644 index 0000000000..209afd1b61 --- /dev/null +++ b/Documentation/git-repo.adoc @@ -0,0 +1,89 @@ +git-repo(1) +=========== + +NAME +---- +git-repo - Retrieve information about the repository + +SYNOPSIS +-------- +[synopsis] +git repo info [--format=(keyvalue|nul)] [-z] [<key>...] + +DESCRIPTION +----------- +Retrieve information about the repository. + +THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE. + +COMMANDS +-------- +`info [--format=(keyvalue|nul)] [-z] [<key>...]`:: + Retrieve metadata-related information about the current repository. Only + the requested data will be returned based on their keys (see "INFO KEYS" + section below). ++ +The values are returned in the same order in which their respective keys were +requested. ++ +The output format can be chosen through the flag `--format`. Two formats are +supported: ++ +`keyvalue`::: + output key-value pairs one per line using the `=` character as + the delimiter between the key and the value. Values containing "unusual" + characters are quoted as explained for the configuration variable + `core.quotePath` (see linkgit:git-config[1]). This is the default. + +`nul`::: + similar to `keyvalue`, but using a newline character as the delimiter + between the key and the value and using a NUL character after each value. + This format is better suited for being parsed by another applications than + `keyvalue`. Unlike in the `keyvalue` format, the values are never quoted. ++ +`-z` is an alias for `--format=nul`. + +INFO KEYS +--------- +In order to obtain a set of values from `git repo info`, you should provide +the keys that identify them. Here's a list of the available keys and the +values that they return: + +`layout.bare`:: + `true` if this is a bare repository, otherwise `false`. + +`layout.shallow`:: + `true` if this is a shallow repository, otherwise `false`. + +`object.format`:: + The object format (hash algorithm) used in the repository. + +`references.format`:: + The reference storage format. The valid values are: ++ +include::ref-storage-format.adoc[] + +EXAMPLES +-------- + +* Retrieves the reference format of the current repository: ++ +------------ +git repo info references.format +------------ ++ + +* Retrieves whether the current repository is bare and whether it is shallow +using the `nul` format: ++ +------------ +git repo info --format=nul layout.bare layout.shallow +------------ + +SEE ALSO +-------- +linkgit:git-rev-parse[1] + +GIT +--- +Part of the linkgit:git[1] suite diff --git a/Documentation/git-reset.adoc b/Documentation/git-reset.adoc index 53ab88c545..3b9ba9aee9 100644 --- a/Documentation/git-reset.adoc +++ b/Documentation/git-reset.adoc @@ -90,7 +90,8 @@ but carries forward unmerged index entries. If a file that is different between _<commit>_ and `HEAD` has local changes, reset is aborted. -`--[no-]recurse-submodules`:: +`--recurse-submodules`:: +`--no-recurse-submodules`:: When the working tree is updated, using `--recurse-submodules` will also recursively reset the working tree of all active submodules according to the commit recorded in the superproject, also setting @@ -125,6 +126,8 @@ OPTIONS separated with _NUL_ character and all other characters are taken literally (including newlines and quotes). +include::diff-context-options.adoc[] + `--`:: Do not interpret any more arguments as options. diff --git a/Documentation/git-restore.adoc b/Documentation/git-restore.adoc index 877b7772e6..961eef0137 100644 --- a/Documentation/git-restore.adoc +++ b/Documentation/git-restore.adoc @@ -28,8 +28,6 @@ otherwise from the index. Use `--source` to restore from a different commit. See "Reset, restore and revert" in linkgit:git[1] for the differences between the three commands. -THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE. - OPTIONS ------- `-s <tree>`:: @@ -52,6 +50,8 @@ leave out at most one of _<rev-A>__ and _<rev-B>_, in which case it defaults to Mode" section of linkgit:git-add[1] to learn how to operate the `--patch` mode. +include::diff-context-options.adoc[] + `-W`:: `--worktree`:: `-S`:: diff --git a/Documentation/git-send-email.adoc b/Documentation/git-send-email.adoc index 26fda63c2f..263b977353 100644 --- a/Documentation/git-send-email.adoc +++ b/Documentation/git-send-email.adoc @@ -21,7 +21,7 @@ Takes the patches given on the command line and emails them out. Patches can be specified as files, directories (which will send all files in the directory), or directly as a revision list. In the last case, any format accepted by linkgit:git-format-patch[1] can -be passed to git send-email, as well as options understood by +be passed to `git send-email`, as well as options understood by linkgit:git-format-patch[1]. The header of the email is configurable via command-line options. If not @@ -35,11 +35,11 @@ There are two formats accepted for patch files: This is what linkgit:git-format-patch[1] generates. Most headers and MIME formatting are ignored. -2. The original format used by Greg Kroah-Hartman's 'send_lots_of_email.pl' +2. The original format used by Greg Kroah-Hartman's `send_lots_of_email.pl` script + -This format expects the first line of the file to contain the "Cc:" value -and the "Subject:" of the message as the second line. +This format expects the first line of the file to contain the `Cc:` value +and the `Subject:` of the message as the second line. OPTIONS @@ -54,13 +54,13 @@ Composing `sendemail.multiEdit`. --bcc=<address>,...:: - Specify a "Bcc:" value for each email. Default is the value of + Specify a `Bcc:` value for each email. Default is the value of `sendemail.bcc`. + This option may be specified multiple times. --cc=<address>,...:: - Specify a starting "Cc:" value for each email. + Specify a starting `Cc:` value for each email. Default is the value of `sendemail.cc`. + This option may be specified multiple times. @@ -69,14 +69,14 @@ This option may be specified multiple times. Invoke a text editor (see GIT_EDITOR in linkgit:git-var[1]) to edit an introductory message for the patch series. + -When `--compose` is used, git send-email will use the From, To, Cc, Bcc, -Subject, Reply-To, and In-Reply-To headers specified in the message. If -the body of the message (what you type after the headers and a blank -line) only contains blank (or Git: prefixed) lines, the summary won't be +When `--compose` is used, `git send-email` will use the `From`, `To`, `Cc`, +`Bcc`, `Subject`, `Reply-To`, and `In-Reply-To` headers specified in the +message. If the body of the message (what you type after the headers and a +blank line) only contains blank (or `Git:` prefixed) lines, the summary won't be sent, but the headers mentioned above will be used unless they are removed. + -Missing From or In-Reply-To headers will be prompted for. +Missing `From` or `In-Reply-To` headers will be prompted for. + See the CONFIGURATION section for `sendemail.multiEdit`. @@ -85,13 +85,13 @@ See the CONFIGURATION section for `sendemail.multiEdit`. the value of the `sendemail.from` configuration option is used. If neither the command-line option nor `sendemail.from` are set, then the user will be prompted for the value. The default for the prompt will be - the value of GIT_AUTHOR_IDENT, or GIT_COMMITTER_IDENT if that is not - set, as returned by "git var -l". + the value of `GIT_AUTHOR_IDENT`, or `GIT_COMMITTER_IDENT` if that is not + set, as returned by `git var -l`. --reply-to=<address>:: Specify the address where replies from recipients should go to. Use this if replies to messages should go to another address than what - is specified with the --from parameter. + is specified with the `--from` parameter. --in-reply-to=<identifier>:: Make the first mail (or all the mails with `--no-thread`) appear as a @@ -112,14 +112,15 @@ illustration below where `[PATCH v2 0/3]` is in reply to `[PATCH 0/2]`: [PATCH v2 2/3] New tests [PATCH v2 3/3] Implementation + -Only necessary if --compose is also set. If --compose +Only necessary if `--compose` is also set. If `--compose` is not set, this will be prompted for. ---[no-]outlook-id-fix:: +--outlook-id-fix:: +--no-outlook-id-fix:: Microsoft Outlook SMTP servers discard the Message-ID sent via email and assign a new random Message-ID, thus breaking threads. + -With `--outlook-id-fix`, 'git send-email' uses a mechanism specific to +With `--outlook-id-fix`, `git send-email` uses a mechanism specific to Outlook servers to learn the Message-ID the server assigned to fix the threading. Use it only when you know that the server reports the rewritten Message-ID the same way as Outlook servers do. @@ -130,14 +131,14 @@ to 'smtp.office365.com' or 'smtp-mail.outlook.com'. Use --subject=<string>:: Specify the initial subject of the email thread. - Only necessary if --compose is also set. If --compose + Only necessary if `--compose` is also set. If `--compose` is not set, this will be prompted for. --to=<address>,...:: Specify the primary recipient of the emails generated. Generally, this will be the upstream maintainer of the project involved. Default is the value of the `sendemail.to` configuration value; if that is unspecified, - and --to-cmd is not specified, this will be prompted for. + and `--to-cmd` is not specified, this will be prompted for. + This option may be specified multiple times. @@ -145,30 +146,30 @@ This option may be specified multiple times. When encountering a non-ASCII message or subject that does not declare its encoding, add headers/quoting to indicate it is encoded in <encoding>. Default is the value of the - 'sendemail.assume8bitEncoding'; if that is unspecified, this + `sendemail.assume8bitEncoding`; if that is unspecified, this will be prompted for if any non-ASCII files are encountered. + Note that no attempts whatsoever are made to validate the encoding. --compose-encoding=<encoding>:: Specify encoding of compose message. Default is the value of the - 'sendemail.composeEncoding'; if that is unspecified, UTF-8 is assumed. + `sendemail.composeEncoding`; if that is unspecified, UTF-8 is assumed. --transfer-encoding=(7bit|8bit|quoted-printable|base64|auto):: Specify the transfer encoding to be used to send the message over SMTP. - 7bit will fail upon encountering a non-ASCII message. quoted-printable + `7bit` will fail upon encountering a non-ASCII message. `quoted-printable` can be useful when the repository contains files that contain carriage - returns, but makes the raw patch email file (as saved from a MUA) much - harder to inspect manually. base64 is even more fool proof, but also - even more opaque. auto will use 8bit when possible, and quoted-printable - otherwise. + returns, but makes the raw patch email file (as saved from an MUA) much + harder to inspect manually. `base64` is even more fool proof, but also + even more opaque. `auto` will use `8bit` when possible, and + `quoted-printable` otherwise. + Default is the value of the `sendemail.transferEncoding` configuration value; if that is unspecified, default to `auto`. --xmailer:: --no-xmailer:: - Add (or prevent adding) the "X-Mailer:" header. By default, + Add (or prevent adding) the `X-Mailer:` header. By default, the header is added, but it can be turned off by setting the `sendemail.xmailer` configuration variable to `false`. @@ -178,9 +179,9 @@ Sending --envelope-sender=<address>:: Specify the envelope sender used to send the emails. This is useful if your default address is not the address that is - subscribed to a list. In order to use the 'From' address, set the - value to "auto". If you use the sendmail binary, you must have - suitable privileges for the -f parameter. Default is the value of the + subscribed to a list. In order to use the `From` address, set the + value to `auto`. If you use the `sendmail` binary, you must have + suitable privileges for the `-f` parameter. Default is the value of the `sendemail.envelopeSender` configuration variable; if that is unspecified, choosing the envelope sender is left to your MTA. @@ -189,27 +190,27 @@ Sending be sendmail-like; specifically, it must support the `-i` option. The command will be executed in the shell if necessary. Default is the value of `sendemail.sendmailCmd`. If unspecified, and if - --smtp-server is also unspecified, git-send-email will search - for `sendmail` in `/usr/sbin`, `/usr/lib` and $PATH. + `--smtp-server` is also unspecified, `git send-email` will search + for `sendmail` in `/usr/sbin`, `/usr/lib` and `$PATH`. --smtp-encryption=<encryption>:: Specify in what way encrypting begins for the SMTP connection. - Valid values are 'ssl' and 'tls'. Any other value reverts to plain + Valid values are `ssl` and `tls`. Any other value reverts to plain (unencrypted) SMTP, which defaults to port 25. Despite the names, both values will use the same newer version of TLS, - but for historic reasons have these names. 'ssl' refers to "implicit" + but for historic reasons have these names. `ssl` refers to "implicit" encryption (sometimes called SMTPS), that uses port 465 by default. - 'tls' refers to "explicit" encryption (often known as STARTTLS), + `tls` refers to "explicit" encryption (often known as STARTTLS), that uses port 25 by default. Other ports might be used by the SMTP server, which are not the default. Commonly found alternative port for - 'tls' and unencrypted is 587. You need to check your provider's + `tls` and unencrypted is 587. You need to check your provider's documentation or your server configuration to make sure for your own case. Default is the value of `sendemail.smtpEncryption`. --smtp-domain=<FQDN>:: Specifies the Fully Qualified Domain Name (FQDN) used in the HELO/EHLO command to the SMTP server. Some servers require the - FQDN to match your IP address. If not set, git send-email attempts + FQDN to match your IP address. If not set, `git send-email` attempts to determine your FQDN automatically. Default is the value of `sendemail.smtpDomain`. @@ -223,10 +224,10 @@ $ git send-email --smtp-auth="PLAIN LOGIN GSSAPI" ... + If at least one of the specified mechanisms matches the ones advertised by the SMTP server and if it is supported by the utilized SASL library, the mechanism -is used for authentication. If neither 'sendemail.smtpAuth' nor `--smtp-auth` +is used for authentication. If neither `sendemail.smtpAuth` nor `--smtp-auth` is specified, all mechanisms supported by the SASL library can be used. The -special value 'none' maybe specified to completely disable authentication -independently of `--smtp-user` +special value `none` maybe specified to completely disable authentication +independently of `--smtp-user`. --smtp-pass[=<password>]:: Password for SMTP-AUTH. The argument is optional: If no @@ -238,16 +239,16 @@ Furthermore, passwords need not be specified in configuration files or on the command line. If a username has been specified (with `--smtp-user` or a `sendemail.smtpUser`), but no password has been specified (with `--smtp-pass` or `sendemail.smtpPass`), then -a password is obtained using 'git-credential'. +a password is obtained using linkgit:git-credential[1]. --no-smtp-auth:: - Disable SMTP authentication. Short hand for `--smtp-auth=none` + Disable SMTP authentication. Short hand for `--smtp-auth=none`. --smtp-server=<host>:: If set, specifies the outgoing SMTP server to use (e.g. `smtp.example.com` or a raw IP address). If unspecified, and if `--sendmail-cmd` is also unspecified, the default is to search - for `sendmail` in `/usr/sbin`, `/usr/lib` and $PATH if such a + for `sendmail` in `/usr/sbin`, `/usr/lib` and `$PATH` if such a program is available, falling back to `localhost` otherwise. + For backward compatibility, this option can also specify a full pathname @@ -260,7 +261,7 @@ instead. Specifies a port different from the default port (SMTP servers typically listen to smtp port 25, but may also listen to submission port 587, or the common SSL smtp port 465); - symbolic port names (e.g. "submission" instead of 587) + symbolic port names (e.g. `submission` instead of 587) are also accepted. The port can also be set with the `sendemail.smtpServerPort` configuration variable. @@ -269,23 +270,25 @@ instead. Default value can be specified by the `sendemail.smtpServerOption` configuration option. + -The --smtp-server-option option must be repeated for each option you want +The `--smtp-server-option` option must be repeated for each option you want to pass to the server. Likewise, different lines in the configuration files must be used for each option. --smtp-ssl:: - Legacy alias for '--smtp-encryption ssl'. + Legacy alias for `--smtp-encryption ssl`. --smtp-ssl-cert-path:: Path to a store of trusted CA certificates for SMTP SSL/TLS certificate validation (either a directory that has been processed - by 'c_rehash', or a single file containing one or more PEM format - certificates concatenated together: see verify(1) -CAfile and - -CApath for more information on these). Set it to an empty string - to disable certificate verification. Defaults to the value of the - `sendemail.smtpSSLCertPath` configuration variable, if set, or the - backing SSL library's compiled-in default otherwise (which should - be the best choice on most platforms). + by `c_rehash`, or a single file containing one or more PEM format + certificates concatenated together: see the description of the + `-CAfile` _<file>_ and the `-CApath` _<dir>_ options of + https://docs.openssl.org/master/man1/openssl-verify/ + [OpenSSL's verify(1) manual page] for more information on these). + Set it to an empty string to disable certificate verification. + Defaults to the value of the `sendemail.smtpSSLCertPath` configuration + variable, if set, or the backing SSL library's compiled-in default + otherwise (which should be the best choice on most platforms). --smtp-user=<user>:: Username for SMTP-AUTH. Default is the value of `sendemail.smtpUser`; @@ -297,19 +300,45 @@ must be used for each option. commands and replies will be printed. Useful to debug TLS connection and authentication problems. +--imap-sent-folder=<folder>:: + Some email providers (e.g. iCloud) do not send a copy of the emails sent + using SMTP to the `Sent` folder or similar in your mailbox. Use this option + to use `git imap-send` to send a copy of the emails to the folder specified + using this option. You can run `git imap-send --list` to get a list of + valid folder names, including the correct name of the `Sent` folder in + your mailbox. You can also use this option to send emails to a dedicated + IMAP folder of your choice. ++ +This feature requires setting up `git imap-send`. See linkgit:git-imap-send[1] +for instructions. + +--use-imap-only:: +--no-use-imap-only:: + If this is set, all emails will only be copied to the IMAP folder specified + with `--imap-sent-folder` or `sendemail.imapSentFolder` and will not be sent + to the recipients. Useful if you just want to create a draft of the emails + and use another email client to send them. + If disabled with `--no-use-imap-only`, the emails will be sent like usual. + Disabled by default, but the `sendemail.useImapOnly` configuration + variable can be used to enable it. + ++ +This feature requires setting up `git imap-send`. See linkgit:git-imap-send[1] +for instructions. + --batch-size=<num>:: - Some email servers (e.g. smtp.163.com) limit the number emails to be + Some email servers (e.g. 'smtp.163.com') limit the number of emails to be sent per session (connection) and this will lead to a failure when sending many messages. With this option, send-email will disconnect after - sending $<num> messages and wait for a few seconds (see --relogin-delay) - and reconnect, to work around such a limit. You may want to - use some form of credential helper to avoid having to retype - your password every time this happens. Defaults to the + sending _<num>_ messages and wait for a few seconds + (see `--relogin-delay`) and reconnect, to work around such a limit. + You may want to use some form of credential helper to avoid having to + retype your password every time this happens. Defaults to the `sendemail.smtpBatchSize` configuration variable. --relogin-delay=<int>:: - Waiting $<int> seconds before reconnecting to SMTP server. Used together - with --batch-size option. Defaults to the `sendemail.smtpReloginDelay` + Waiting _<int>_ seconds before reconnecting to SMTP server. Used together + with `--batch-size` option. Defaults to the `sendemail.smtpReloginDelay` configuration variable. Automating @@ -318,7 +347,7 @@ Automating --no-to:: --no-cc:: --no-bcc:: - Clears any list of "To:", "Cc:", "Bcc:" addresses previously + Clears any list of `To:`, `Cc:`, `Bcc:` addresses previously set via config. --no-identity:: @@ -327,13 +356,13 @@ Automating --to-cmd=<command>:: Specify a command to execute once per patch file which - should generate patch file specific "To:" entries. + should generate patch file specific `To:` entries. Output of this command must be single email address per line. - Default is the value of 'sendemail.toCmd' configuration value. + Default is the value of `sendemail.toCmd` configuration value. --cc-cmd=<command>:: Specify a command to execute once per patch file which - should generate patch file specific "Cc:" entries. + should generate patch file specific `Cc:` entries. Output of this command must be single email address per line. Default is the value of `sendemail.ccCmd` configuration value. @@ -341,16 +370,17 @@ Automating Specify a command that is executed once per outgoing message and output RFC 2822 style header lines to be inserted into them. When the `sendemail.headerCmd` configuration variable is - set, its value is always used. When --header-cmd is provided + set, its value is always used. When `--header-cmd` is provided at the command line, its value takes precedence over the `sendemail.headerCmd` configuration variable. --no-header-cmd:: Disable any header command in use. ---[no-]chain-reply-to:: +--chain-reply-to:: +--no-chain-reply-to:: If this is set, each email will be sent as a reply to the previous - email sent. If disabled with "--no-chain-reply-to", all emails after + email sent. If disabled with `--no-chain-reply-to`, all emails after the first will be sent as replies to the first email sent. When using this, it is recommended that the first file given be an overview of the entire patch series. Disabled by default, but the `sendemail.chainReplyTo` @@ -358,79 +388,86 @@ Automating --identity=<identity>:: A configuration identity. When given, causes values in the - 'sendemail.<identity>' subsection to take precedence over - values in the 'sendemail' section. The default identity is + `sendemail.<identity>` subsection to take precedence over + values in the `sendemail` section. The default identity is the value of `sendemail.identity`. ---[no-]signed-off-by-cc:: - If this is set, add emails found in the `Signed-off-by` trailer or Cc: lines to the - cc list. Default is the value of `sendemail.signedOffByCc` configuration - value; if that is unspecified, default to --signed-off-by-cc. +--signed-off-by-cc:: +--no-signed-off-by-cc:: + If this is set, add emails found in the `Signed-off-by` trailer or `Cc:` + lines to the cc list. Default is the value of `sendemail.signedOffByCc` + configuration value; if that is unspecified, default to + `--signed-off-by-cc`. ---[no-]cc-cover:: - If this is set, emails found in Cc: headers in the first patch of +--cc-cover:: +--no-cc-cover:: + If this is set, emails found in `Cc:` headers in the first patch of the series (typically the cover letter) are added to the cc list - for each email set. Default is the value of 'sendemail.ccCover' - configuration value; if that is unspecified, default to --no-cc-cover. + for each email set. Default is the value of `sendemail.ccCover` + configuration value; if that is unspecified, default to `--no-cc-cover`. ---[no-]to-cover:: - If this is set, emails found in To: headers in the first patch of +--to-cover:: +--no-to-cover:: + If this is set, emails found in `To:` headers in the first patch of the series (typically the cover letter) are added to the to list - for each email set. Default is the value of 'sendemail.toCover' - configuration value; if that is unspecified, default to --no-to-cover. + for each email set. Default is the value of `sendemail.toCover` + configuration value; if that is unspecified, default to `--no-to-cover`. --suppress-cc=<category>:: Specify an additional category of recipients to suppress the auto-cc of: + -- -- 'author' will avoid including the patch author. -- 'self' will avoid including the sender. -- 'cc' will avoid including anyone mentioned in Cc lines in the patch header - except for self (use 'self' for that). -- 'bodycc' will avoid including anyone mentioned in Cc lines in the - patch body (commit message) except for self (use 'self' for that). -- 'sob' will avoid including anyone mentioned in the Signed-off-by trailers except - for self (use 'self' for that). -- 'misc-by' will avoid including anyone mentioned in Acked-by, +- `author` will avoid including the patch author. +- `self` will avoid including the sender. +- `cc` will avoid including anyone mentioned in Cc lines in the patch header + except for self (use `self` for that). +- `bodycc` will avoid including anyone mentioned in Cc lines in the + patch body (commit message) except for self (use `self` for that). +- `sob` will avoid including anyone mentioned in the Signed-off-by trailers except + for self (use `self` for that). +- `misc-by` will avoid including anyone mentioned in Acked-by, Reviewed-by, Tested-by and other "-by" lines in the patch body, - except Signed-off-by (use 'sob' for that). -- 'cccmd' will avoid running the --cc-cmd. -- 'body' is equivalent to 'sob' + 'bodycc' + 'misc-by'. -- 'all' will suppress all auto cc values. + except Signed-off-by (use `sob` for that). +- `cccmd` will avoid running the --cc-cmd. +- `body` is equivalent to `sob` + `bodycc` + `misc-by`. +- `all` will suppress all auto cc values. -- + Default is the value of `sendemail.suppressCc` configuration value; if -that is unspecified, default to 'self' if --suppress-from is -specified, as well as 'body' if --no-signed-off-cc is specified. +that is unspecified, default to `self` if `--suppress-from` is +specified, as well as `body` if `--no-signed-off-cc` is specified. ---[no-]suppress-from:: - If this is set, do not add the From: address to the cc: list. +--suppress-from:: +--no-suppress-from:: + If this is set, do not add the `From:` address to the `Cc:` list. Default is the value of `sendemail.suppressFrom` configuration - value; if that is unspecified, default to --no-suppress-from. + value; if that is unspecified, default to `--no-suppress-from`. ---[no-]thread:: - If this is set, the In-Reply-To and References headers will be +--thread:: +--no-thread:: + If this is set, the `In-Reply-To` and `References` headers will be added to each email sent. Whether each mail refers to the - previous email (`deep` threading per 'git format-patch' + previous email (`deep` threading per `git format-patch` wording) or to the first email (`shallow` threading) is - governed by "--[no-]chain-reply-to". + governed by `--[no-]chain-reply-to`. + -If disabled with "--no-thread", those headers will not be added -(unless specified with --in-reply-to). Default is the value of the +If disabled with `--no-thread`, those headers will not be added +(unless specified with `--in-reply-to`). Default is the value of the `sendemail.thread` configuration value; if that is unspecified, -default to --thread. +default to `--thread`. + It is up to the user to ensure that no In-Reply-To header already -exists when 'git send-email' is asked to add it (especially note that -'git format-patch' can be configured to do the threading itself). +exists when `git send-email` is asked to add it (especially note that +`git format-patch` can be configured to do the threading itself). Failure to do so may not produce the expected result in the recipient's MUA. ---[no-]mailmap:: +--mailmap:: +--no-mailmap:: Use the mailmap file (see linkgit:gitmailmap[5]) to map all addresses to their canonical real name and email address. Additional - mailmap data specific to git-send-email may be provided using the + mailmap data specific to `git send-email` may be provided using the `sendemail.mailmap.file` or `sendemail.mailmap.blob` configuration values. Defaults to `sendemail.mailmap`. @@ -441,32 +478,34 @@ Administering Confirm just before sending: + -- -- 'always' will always confirm before sending -- 'never' will never confirm before sending -- 'cc' will confirm before sending when send-email has automatically - added addresses from the patch to the Cc list -- 'compose' will confirm before sending the first message when using --compose. -- 'auto' is equivalent to 'cc' + 'compose' +- `always` will always confirm before sending. +- `never` will never confirm before sending. +- `cc` will confirm before sending when send-email has automatically + added addresses from the patch to the Cc list. +- `compose` will confirm before sending the first message when using --compose. +- `auto` is equivalent to `cc` + `compose`. -- + Default is the value of `sendemail.confirm` configuration value; if that -is unspecified, default to 'auto' unless any of the suppress options -have been specified, in which case default to 'compose'. +is unspecified, default to `auto` unless any of the suppress options +have been specified, in which case default to `compose`. --dry-run:: Do everything except actually send the emails. ---[no-]format-patch:: +--format-patch:: +--no-format-patch:: When an argument may be understood either as a reference or as a file name, choose to understand it as a format-patch argument (`--format-patch`) or as a file name (`--no-format-patch`). By default, when such a conflict - occurs, git send-email will fail. + occurs, `git send-email` will fail. --quiet:: - Make git-send-email less verbose. One line per email should be + Make `git send-email` less verbose. One line per email should be all that is output. ---[no-]validate:: +--validate:: +--no-validate:: Perform sanity checks on patches. Currently, validation means the following: + @@ -474,7 +513,7 @@ have been specified, in which case default to 'compose'. * Invoke the sendemail-validate hook if present (see linkgit:githooks[5]). * Warn of patches that contain lines longer than 998 characters unless a suitable transfer encoding - ('auto', 'base64', or 'quoted-printable') is used; + (`auto`, `base64`, or `quoted-printable`) is used; this is due to SMTP limits as described by https://www.ietf.org/rfc/rfc5322.txt. -- @@ -493,13 +532,13 @@ Information Instead of the normal operation, dump the shorthand alias names from the configured alias file(s), one per line in alphabetical order. Note that this only includes the alias name and not its expanded email addresses. - See 'sendemail.aliasesFile' for more information about aliases. + See `sendemail.aliasesFile` for more information about aliases. --translate-aliases:: Instead of the normal operation, read from standard input and interpret each line as an email alias. Translate it according to the configured alias file(s). Output each translated name and email - address to standard output, one per line. See 'sendemail.aliasFile' + address to standard output, one per line. See `sendemail.aliasFile` for more information about aliases. CONFIGURATION @@ -518,36 +557,43 @@ edit `~/.gitconfig` to specify your account settings: ---- [sendemail] - smtpEncryption = tls + smtpEncryption = ssl smtpServer = smtp.gmail.com smtpUser = yourname@gmail.com - smtpServerPort = 587 + smtpServerPort = 465 ---- +Gmail does not allow using your regular password for `git send-email`. If you have multi-factor authentication set up on your Gmail account, you can -generate an app-specific password for use with 'git send-email'. Visit +generate an app-specific password for use with `git send-email`. Visit https://security.google.com/settings/security/apppasswords to create it. -You can also use OAuth2.0 authentication with Gmail. `OAUTHBEARER` and -`XOAUTH2` are common methods used for this type of authentication. Gmail -supports both of them. As an example, if you want to use `OAUTHBEARER`, edit -your `~/.gitconfig` file and add `smtpAuth = OAUTHBEARER` to your account -settings: +Alternatively, instead of using an app-specific password, you can use +OAuth2.0 authentication with Gmail. OAuth2.0 is more secure than +app-specific passwords, and works regardless of whether you have multi-factor +authentication set up. `OAUTHBEARER` and `XOAUTH2` are common mechanisms used +for this type of authentication. Gmail supports both of them. As an example, +if you want to use `OAUTHBEARER`, edit your `~/.gitconfig` file and add +`smtpAuth = OAUTHBEARER` to your account settings: ---- [sendemail] - smtpEncryption = tls + smtpEncryption = ssl smtpServer = smtp.gmail.com smtpUser = yourname@gmail.com - smtpServerPort = 587 + smtpServerPort = 465 smtpAuth = OAUTHBEARER ---- +Another alternative is using a tool developed by Google known as +https://github.com/google/gmail-oauth2-tools/tree/master/go/sendgmail[sendgmail] +to send emails using `git send-email`. + Use Microsoft Outlook as the SMTP Server ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Unlike Gmail, Microsoft Outlook no longer supports app-specific passwords. Therefore, OAuth2.0 authentication must be used for Outlook. Also, it only -supports `XOAUTH2` authentication method. +supports `XOAUTH2` authentication mechanism. Edit `~/.gitconfig` to specify your account settings for Outlook and use its SMTP server with `git send-email`: @@ -579,8 +625,7 @@ next time. If you are using OAuth2.0 authentication, you need to use an access token in place of a password when prompted. Various OAuth2.0 token generators are -available online. Community maintained credential helpers for Gmail and Outlook -are also available: +available online. Community maintained credential helpers are also available: - https://github.com/AdityaGarg8/git-credential-email[git-credential-gmail] (cross platform, dedicated helper for authenticating Gmail accounts) @@ -588,15 +633,65 @@ are also available: - https://github.com/AdityaGarg8/git-credential-email[git-credential-outlook] (cross platform, dedicated helper for authenticating Microsoft Outlook accounts) + - https://github.com/AdityaGarg8/git-credential-email[git-credential-yahoo] + (cross platform, dedicated helper for authenticating Yahoo accounts) + + - https://github.com/AdityaGarg8/git-credential-email[git-credential-aol] + (cross platform, dedicated helper for authenticating AOL accounts) + You can also see linkgit:gitcredentials[7] for more OAuth based authentication helpers. +Proton Mail does not provide an SMTP server to send emails. If you are a paid +customer of Proton Mail, you can use +https://proton.me/mail/bridge[Proton Mail Bridge] +officially provided by Proton Mail to create a local SMTP server for sending +emails. For both free and paid users, community maintained projects like +https://github.com/AdityaGarg8/git-credential-email[git-protonmail] can be +used. + Note: the following core Perl modules that may be installed with your distribution of Perl are required: -MIME::Base64, MIME::QuotedPrint, Net::Domain and Net::SMTP. + +https://metacpan.org/pod/MIME::Base64[MIME::Base64], +https://metacpan.org/pod/MIME::QuotedPrint[MIME::QuotedPrint], +https://metacpan.org/pod/Net::Domain[Net::Domain] and +https://metacpan.org/pod/Net::SMTP[Net::SMTP]. + These additional Perl modules are also required: -Authen::SASL and Mail::Address. +https://metacpan.org/pod/Authen::SASL[Authen::SASL] and +https://metacpan.org/pod/Mail::Address[Mail::Address]. + +Exploiting the `sendmailCmd` option of `git send-email` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Apart from sending emails via an SMTP server, `git send-email` can also send +emails through any application that supports sendmail-like commands. You can +read documentation of `--sendmail-cmd=<command>` above for more information. +This ability can be very useful if you want to use another application as an +SMTP client for `git send-email`, or if your email provider uses proprietary +APIs instead of SMTP to send emails. + +As an example, lets see how to configure https://marlam.de/msmtp/[msmtp], a +popular SMTP client found in many Linux distributions. Edit `~/.gitconfig` +to instruct `git-send-email` to use it for sending emails. + +---- +[sendemail] + sendmailCmd = /usr/bin/msmtp # Change this to the path where msmtp is installed +---- + +Links of a few such community maintained helpers are: + + - https://marlam.de/msmtp/[msmtp] + (popular SMTP client with many features, available for Linux and macOS) + + - https://github.com/AdityaGarg8/git-credential-email[git-protonmail] + (cross platform client that can send emails using the ProtonMail API) + + - https://github.com/AdityaGarg8/git-credential-email[git-msgraph] + (cross platform client that can send emails using the Microsoft Graph API) SEE ALSO -------- diff --git a/Documentation/git-send-pack.adoc b/Documentation/git-send-pack.adoc index b9e73f2e77..811193f16c 100644 --- a/Documentation/git-send-pack.adoc +++ b/Documentation/git-send-pack.adoc @@ -71,7 +71,8 @@ be in a separate packet, and the list must end with a flush packet. fails to update then the entire push will fail without changing any refs. ---[no-]signed:: +--signed:: +--no-signed:: --signed=(true|false|if-asked):: GPG-sign the push request to update refs on the receiving side, to allow it to be checked by the hooks and/or be diff --git a/Documentation/git-stash.adoc b/Documentation/git-stash.adoc index 1a5177f498..e2300a19a2 100644 --- a/Documentation/git-stash.adoc +++ b/Documentation/git-stash.adoc @@ -23,6 +23,8 @@ SYNOPSIS 'git stash' clear 'git stash' create [<message>] 'git stash' store [(-m | --message) <message>] [-q | --quiet] <commit> +'git stash' export (--print | --to-ref <ref>) [<stash>...] +'git stash' import <commit> DESCRIPTION ----------- @@ -154,6 +156,18 @@ store:: reflog. This is intended to be useful for scripts. It is probably not the command you want to use; see "push" above. +export ( --print | --to-ref <ref> ) [<stash>...]:: + + Export the specified stashes, or all of them if none are specified, to + a chain of commits which can be transferred using the normal fetch and + push mechanisms, then imported using the `import` subcommand. + +import <commit>:: + + Import the specified stashes from the specified commit, which must have been + created by `export`, and add them to the list of stashes. To replace the + existing stashes, use `clear` first. + OPTIONS ------- -a:: @@ -208,6 +222,8 @@ to learn how to operate the `--patch` mode. The `--patch` option implies `--keep-index`. You can use `--no-keep-index` to override this. +include::diff-context-options.adoc[] + -S:: --staged:: This option is only valid for `push` and `save` commands. @@ -242,6 +258,19 @@ literally (including newlines and quotes). + Quiet, suppress feedback messages. +--print:: + This option is only valid for the `export` command. ++ +Create the chain of commits representing the exported stashes without +storing it anywhere in the ref namespace and print the object ID to +standard output. This is designed for scripts. + +--to-ref:: + This option is only valid for the `export` command. ++ +Create the chain of commits representing the exported stashes and store +it to the specified ref. + \--:: This option is only valid for `push` command. + @@ -259,7 +288,7 @@ For more details, see the 'pathspec' entry in linkgit:gitglossary[7]. <stash>:: This option is only valid for `apply`, `branch`, `drop`, `pop`, - `show` commands. + `show`, and `export` commands. + A reference of the form `stash@{<revision>}`. When no `<stash>` is given, the latest stash is assumed (that is, `stash@{0}`). diff --git a/Documentation/git-submodule.adoc b/Documentation/git-submodule.adoc index 87d8e0f0c5..95beaee561 100644 --- a/Documentation/git-submodule.adoc +++ b/Documentation/git-submodule.adoc @@ -307,6 +307,13 @@ OPTIONS --force:: This option is only valid for add, deinit and update commands. When running add, allow adding an otherwise ignored submodule path. + This option is also used to bypass a check that the submodule's name + is not already in use. By default, 'git submodule add' will fail if + the proposed name (which is derived from the path) is already registered + for another submodule in the repository. Using '--force' allows the command + to proceed by automatically generating a unique name by appending a number + to the conflicting name (e.g., if a submodule named 'child' exists, it will + try 'child1', and so on). When running deinit the submodule working trees will be removed even if they contain local changes. When running update (only effective with the checkout procedure), @@ -435,7 +442,8 @@ options carefully. clone with a history truncated to the specified number of revisions. See linkgit:git-clone[1] ---[no-]recommend-shallow:: +--recommend-shallow:: +--no-recommend-shallow:: This option is only valid for the update command. The initial clone of a submodule will use the recommended `submodule.<name>.shallow` as provided by the `.gitmodules` file @@ -447,7 +455,8 @@ options carefully. Clone new submodules in parallel with as many jobs. Defaults to the `submodule.fetchJobs` option. ---[no-]single-branch:: +--single-branch:: +--no-single-branch:: This option is only valid for the update command. Clone only one branch during update: HEAD or one specified by --branch. diff --git a/Documentation/git-svn.adoc b/Documentation/git-svn.adoc index bcf7d84a87..c26c12bab3 100644 --- a/Documentation/git-svn.adoc +++ b/Documentation/git-svn.adoc @@ -1012,9 +1012,11 @@ branch. If you do merge, note the following rule: 'git svn dcommit' will attempt to commit on top of the SVN commit named in + ------------------------------------------------------------------------ git log --grep=^git-svn-id: --first-parent -1 ------------------------------------------------------------------------ + You 'must' therefore ensure that the most recent commit of the branch you want to dcommit to is the 'first' parent of the merge. Chaos will ensue otherwise, especially if the first parent is an older commit on diff --git a/Documentation/git-switch.adoc b/Documentation/git-switch.adoc index 9f62abf9e2..87707e9265 100644 --- a/Documentation/git-switch.adoc +++ b/Documentation/git-switch.adoc @@ -29,8 +29,6 @@ Switching branches does not require a clean index and working tree however if the operation leads to loss of local changes, unless told otherwise with `--discard-changes` or `--merge`. -THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE. - OPTIONS ------- _<branch>_:: diff --git a/Documentation/git-update-index.adoc b/Documentation/git-update-index.adoc index 7128aed540..9bea9fab9a 100644 --- a/Documentation/git-update-index.adoc +++ b/Documentation/git-update-index.adoc @@ -86,7 +86,8 @@ OPTIONS --chmod=(+|-)x:: Set the execute permissions on the updated files. ---[no-]assume-unchanged:: +--assume-unchanged:: +--no-assume-unchanged:: When this flag is specified, the object names recorded for the paths are not updated. Instead, this option sets/unsets the "assume unchanged" bit for the @@ -108,18 +109,21 @@ you will need to handle the situation manually. Like `--refresh`, but checks stat information unconditionally, without regard to the "assume unchanged" setting. ---[no-]skip-worktree:: +--skip-worktree:: +--no-skip-worktree:: When one of these flags is specified, the object names recorded for the paths are not updated. Instead, these options set and unset the "skip-worktree" bit for the paths. See section "Skip-worktree bit" below for more information. ---[no-]ignore-skip-worktree-entries:: +--ignore-skip-worktree-entries:: +--no-ignore-skip-worktree-entries:: Do not remove skip-worktree (AKA "index-only") entries even when the `--remove` option was specified. ---[no-]fsmonitor-valid:: +--fsmonitor-valid:: +--no-fsmonitor-valid:: When one of these flags is specified, the object names recorded for the paths are not updated. Instead, these options set and unset the "fsmonitor valid" bit for the paths. See diff --git a/Documentation/git-upload-pack.adoc b/Documentation/git-upload-pack.adoc index 516d1639d9..9167a321d0 100644 --- a/Documentation/git-upload-pack.adoc +++ b/Documentation/git-upload-pack.adoc @@ -25,7 +25,8 @@ repository. For push operations, see 'git send-pack'. OPTIONS ------- ---[no-]strict:: +--strict:: +--no-strict:: Do not try <directory>/.git/ if <directory> is not a Git directory. --timeout=<n>:: diff --git a/Documentation/git-whatchanged.adoc b/Documentation/git-whatchanged.adoc index 8e55e0bb1e..d21484026f 100644 --- a/Documentation/git-whatchanged.adoc +++ b/Documentation/git-whatchanged.adoc @@ -8,8 +8,14 @@ git-whatchanged - Show logs with differences each commit introduces SYNOPSIS -------- -[verse] -'git whatchanged' <option>... +[synopsis] +git whatchanged <option>... + +WARNING +------- +`git whatchanged` has been deprecated and is scheduled for removal in +a future version of Git, as it is merely `git log` with different +default; `whatchanged` is not even shorter to type than `log --raw`. DESCRIPTION ----------- diff --git a/Documentation/git-worktree.adoc b/Documentation/git-worktree.adoc index 8340b7f028..389e669ac0 100644 --- a/Documentation/git-worktree.adoc +++ b/Documentation/git-worktree.adoc @@ -200,13 +200,15 @@ To remove a locked worktree, specify `--force` twice. With `add`, detach `HEAD` in the new worktree. See "DETACHED HEAD" in linkgit:git-checkout[1]. ---[no-]checkout:: +--checkout:: +--no-checkout:: By default, `add` checks out `<commit-ish>`, however, `--no-checkout` can be used to suppress checkout in order to make customizations, such as configuring sparse-checkout. See "Sparse checkout" in linkgit:git-read-tree[1]. ---[no-]guess-remote:: +--guess-remote:: +--no-guess-remote:: With `worktree add <path>`, without `<commit-ish>`, instead of creating a new branch from `HEAD`, if there exists a tracking branch in exactly one remote matching the basename of `<path>`, @@ -216,7 +218,8 @@ To remove a locked worktree, specify `--force` twice. This can also be set up as the default behaviour by using the `worktree.guessRemote` config option. ---[no-]relative-paths:: +--relative-paths:: +--no-relative-paths:: Link worktrees using relative paths or absolute paths (default). Overrides the `worktree.useRelativePaths` config option, see linkgit:git-config[1]. @@ -224,7 +227,8 @@ This can also be set up as the default behaviour by using the With `repair`, the linking files will be updated if there's an absolute/relative mismatch, even if the links are correct. ---[no-]track:: +--track:: +--no-track:: When creating a new branch, if `<commit-ish>` is a branch, mark it as "upstream" from the new branch. This is the default if `<commit-ish>` is a remote-tracking branch. See diff --git a/Documentation/git.adoc b/Documentation/git.adoc index 743b7b00e4..03e9e69d25 100644 --- a/Documentation/git.adoc +++ b/Documentation/git.adoc @@ -684,7 +684,7 @@ other `GIT_PROGRESS_DELAY`:: A number controlling how many seconds to delay before showing - optional progress indicators. Defaults to 2. + optional progress indicators. Defaults to 1. `GIT_EDITOR`:: This environment variable overrides `$EDITOR` and `$VISUAL`. diff --git a/Documentation/gitcredentials.adoc b/Documentation/gitcredentials.adoc index b49923db02..3337bb475d 100644 --- a/Documentation/gitcredentials.adoc +++ b/Documentation/gitcredentials.adoc @@ -133,10 +133,6 @@ Popular helpers with OAuth support include: - https://github.com/hickford/git-credential-oauth[git-credential-oauth] (cross platform, included in many Linux distributions) - - https://github.com/AdityaGarg8/git-credential-email[git-credential-gmail] (cross platform, dedicated helper to authenticate Gmail accounts for linkgit:git-send-email[1]) - - - https://github.com/AdityaGarg8/git-credential-email[git-credential-outlook] (cross platform, dedicated helper to authenticate Microsoft Outlook accounts for linkgit:git-send-email[1]) - CREDENTIAL CONTEXTS ------------------- diff --git a/Documentation/gitk.adoc b/Documentation/gitk.adoc index 58ce40ddb1..5b34dcd077 100644 --- a/Documentation/gitk.adoc +++ b/Documentation/gitk.adoc @@ -163,16 +163,16 @@ used by default. If '$XDG_CONFIG_HOME' is not set it defaults to History ------- -Gitk was the first graphical repository browser. It's written in -tcl/tk. +Gitk was the first graphical repository browser, written by +Paul Mackerras in Tcl/Tk. 'gitk' is actually maintained as an independent project, but stable versions are distributed as part of the Git suite for the convenience of end users. -gitk-git/ comes from Paul Mackerras's gitk project: +`gitk-git/` comes from Johannes Sixt's gitk project: - git://ozlabs.org/~paulus/gitk + https://github.com/j6t/gitk SEE ALSO -------- diff --git a/Documentation/gitprotocol-http.adoc b/Documentation/gitprotocol-http.adoc index ec40a550cc..d024010414 100644 --- a/Documentation/gitprotocol-http.adoc +++ b/Documentation/gitprotocol-http.adoc @@ -318,7 +318,7 @@ Extra Parameter. Smart Service git-upload-pack ------------------------------- +----------------------------- This service reads from the repository pointed to by `$GIT_URL`. Clients MUST first perform ref discovery with diff --git a/Documentation/gitprotocol-v2.adoc b/Documentation/gitprotocol-v2.adoc index 5598c93e67..c7db103299 100644 --- a/Documentation/gitprotocol-v2.adoc +++ b/Documentation/gitprotocol-v2.adoc @@ -54,7 +54,7 @@ In general a client can request to speak protocol v2 by sending `version=2` through the respective side-channel for the transport being used which inevitably sets `GIT_PROTOCOL`. More information can be found in linkgit:gitprotocol-pack[5] and linkgit:gitprotocol-http[5], as well as the -`GIT_PROTOCOL` definition in `git.txt`. In all cases the +`GIT_PROTOCOL` definition in linkgit:git[1]. In all cases the response from the server is the capability advertisement. Git Transport @@ -99,7 +99,7 @@ Uses the `--http-backend-info-refs` option to linkgit:git-upload-pack[1]. The server may need to be configured to pass this header's contents via -the `GIT_PROTOCOL` variable. See the discussion in `git-http-backend.txt`. +the `GIT_PROTOCOL` variable. See the discussion in linkgit:git-http-backend[1]. Capability Advertisement ------------------------ @@ -785,33 +785,64 @@ retrieving the header from a bundle at the indicated URI, and thus save themselves and the server(s) the request(s) needed to inspect the headers of that bundle or bundles. -promisor-remote=<pr-infos> -~~~~~~~~~~~~~~~~~~~~~~~~~~ +promisor-remote=<pr-info> +~~~~~~~~~~~~~~~~~~~~~~~~~ The server may advertise some promisor remotes it is using or knows about to a client which may want to use them as its promisor remotes, -instead of this repository. In this case <pr-infos> should be of the +instead of this repository. In this case <pr-info> should be of the form: - pr-infos = pr-info | pr-infos ";" pr-info + pr-info = pr-fields | pr-info ";" pr-fields - pr-info = "name=" pr-name | "name=" pr-name "," "url=" pr-url + pr-fields = pr-field | pr-fields "," pr-field -where `pr-name` is the urlencoded name of a promisor remote, and -`pr-url` the urlencoded URL of that promisor remote. + pr-field = field-name "=" field-value -In this case, if the client decides to use one or more promisor -remotes the server advertised, it can reply with -"promisor-remote=<pr-names>" where <pr-names> should be of the form: +where all the `field-name` and `field-value` in a given `pr-fields` +are field names and values related to a single promisor remote. A +given `field-name` MUST NOT appear more than once in given +`pr-fields`. + +The server MUST advertise at least the "name" and "url" field names +along with the associated field values, which are the name of a valid +remote and its URL, in each `pr-fields`. The "name" and "url" fields +MUST appear first in each pr-fields, in that order. + +After these mandatory fields, the server MAY advertise the following +optional fields in any order: + +`partialCloneFilter`:: The filter specification used by the remote. +Clients can use this to determine if the remote's filtering strategy +is compatible with their needs (e.g., checking if both use "blob:none"). +It corresponds to the "remote.<name>.partialCloneFilter" config setting. + +`token`:: An authentication token that clients can use when +connecting to the remote. It corresponds to the "remote.<name>.token" +config setting. + +No other fields are defined by the protocol at this time. Field names +are case-sensitive and MUST be transmitted exactly as specified +above. Clients MUST ignore fields they don't recognize to allow for +future protocol extensions. + +For now, the client can only use information transmitted through these +fields to decide if it accepts the advertised promisor remote. In the +future that information might be used for other purposes though. + +Field values MUST be urlencoded. + +If the client decides to use one or more promisor remotes the server +advertised, it can reply with "promisor-remote=<pr-names>" where +<pr-names> should be of the form: pr-names = pr-name | pr-names ";" pr-name where `pr-name` is the urlencoded name of a promisor remote the server advertised and the client accepts. -Note that, everywhere in this document, `pr-name` MUST be a valid -remote name, and the ';' and ',' characters MUST be encoded if they -appear in `pr-name` or `pr-url`. +Note that, everywhere in this document, the ';' and ',' characters +MUST be encoded if they appear in `pr-name` or `field-value`. If the server doesn't know any promisor remote that could be good for a client to use, or prefers a client not to use any promisor remote it @@ -822,9 +853,10 @@ In this case, or if the client doesn't want to use any promisor remote the server advertised, the client shouldn't advertise the "promisor-remote" capability at all in its reply. -The "promisor.advertise" and "promisor.acceptFromServer" configuration -options can be used on the server and client side to control what they -advertise or accept respectively. See the documentation of these +On the server side, the "promisor.advertise" and "promisor.sendFields" +configuration options can be used to control what it advertises. On +the client side, the "promisor.acceptFromServer" configuration option +can be used to control what it accepts. See the documentation of these configuration options for more information. Note that in the future it would be nice if the "promisor-remote" diff --git a/Documentation/gitremote-helpers.adoc b/Documentation/gitremote-helpers.adoc index d0be008e5e..39cdece16e 100644 --- a/Documentation/gitremote-helpers.adoc +++ b/Documentation/gitremote-helpers.adoc @@ -498,7 +498,7 @@ set by Git if the remote helper has the 'option' capability. ask for the tag specifically. Some helpers may be able to use this option to avoid a second network connection. -'option dry-run' {'true'|'false'}: +'option dry-run' {'true'|'false'}:: If true, pretend the operation completed successfully, but don't actually change any repository data. For most helpers this only applies to the 'push', if supported. diff --git a/Documentation/gitsubmodules.adoc b/Documentation/gitsubmodules.adoc index f7b5a25a0c..2082296199 100644 --- a/Documentation/gitsubmodules.adoc +++ b/Documentation/gitsubmodules.adoc @@ -8,6 +8,7 @@ gitsubmodules - Mounting one repository inside another SYNOPSIS -------- .gitmodules, $GIT_DIR/config + ------------------ git submodule git <command> --recurse-submodules @@ -240,7 +241,7 @@ Workflow for a third party library Workflow for an artificially split repo --------------------------------------- +--------------------------------------- # Enable recursion for relevant commands, such that # regular commands recurse into submodules by default diff --git a/Documentation/gitweb.conf.adoc b/Documentation/gitweb.conf.adoc index 1348e9b125..64bebb811c 100644 --- a/Documentation/gitweb.conf.adoc +++ b/Documentation/gitweb.conf.adoc @@ -178,7 +178,7 @@ $export_ok:: Show repository only if this file exists (in repository). Only effective if this variable evaluates to true. Can be set when building gitweb by setting `GITWEB_EXPORT_OK`. This path is - relative to `GIT_DIR`. git-daemon[1] uses 'git-daemon-export-ok', + relative to `GIT_DIR`. linkgit:git-daemon[1] uses 'git-daemon-export-ok', unless started with `--export-all`. By default this variable is not set, which means that this feature is turned off. diff --git a/Documentation/glossary-content.adoc b/Documentation/glossary-content.adoc index 575c18f776..e423e4765b 100644 --- a/Documentation/glossary-content.adoc +++ b/Documentation/glossary-content.adoc @@ -418,9 +418,8 @@ full pathname may have special meaning: - A leading "`**`" followed by a slash means match in all directories. For example, "`**/foo`" matches file or directory - "`foo`" anywhere, the same as pattern "`foo`". "`**/foo/bar`" - matches file or directory "`bar`" anywhere that is directly - under directory "`foo`". + "`foo`" anywhere. "`**/foo/bar`" matches file or directory "`bar`" + anywhere that is directly under directory "`foo`". - A trailing "`/**`" matches everything inside. For example, "`abc/**`" matches all files inside directory "abc", relative diff --git a/Documentation/line-range-format.adoc b/Documentation/line-range-format.adoc index 9b51e9fb66..3cc2a14544 100644 --- a/Documentation/line-range-format.adoc +++ b/Documentation/line-range-format.adoc @@ -1,30 +1,30 @@ -'<start>' and '<end>' can take one of these forms: +_<start>_ and _<end>_ can take one of these forms: -- number +- _<number>_ + -If '<start>' or '<end>' is a number, it specifies an +If _<start>_ or _<end>_ is a number, it specifies an absolute line number (lines count from 1). + -- `/regex/` +- `/<regex>/` + This form will use the first line matching the given -POSIX regex. If '<start>' is a regex, it will search from the end of +POSIX _<regex>_. If _<start>_ is a regex, it will search from the end of the previous `-L` range, if any, otherwise from the start of file. -If '<start>' is `^/regex/`, it will search from the start of file. -If '<end>' is a regex, it will search -starting at the line given by '<start>'. +If _<start>_ is `^/<regex>/`, it will search from the start of file. +If _<end>_ is a regex, it will search starting at the line given by +_<start>_. + -- +offset or -offset +- `+<offset>` or `-<offset>` + -This is only valid for '<end>' and will specify a number -of lines before or after the line given by '<start>'. +This is only valid for _<end>_ and will specify a number +of lines before or after the line given by _<start>_. + -If `:<funcname>` is given in place of '<start>' and '<end>', it is a +If `:<funcname>` is given in place of _<start>_ and _<end>_, it is a regular expression that denotes the range from the first funcname line -that matches '<funcname>', up to the next funcname line. `:<funcname>` +that matches _<funcname>_, up to the next funcname line. `:<funcname>` searches from the end of the previous `-L` range, if any, otherwise from the start of file. `^:<funcname>` searches from the start of file. The function names are determined in the same way as `git diff` diff --git a/Documentation/line-range-options.adoc b/Documentation/line-range-options.adoc index f275df3b69..c44ba05320 100644 --- a/Documentation/line-range-options.adoc +++ b/Documentation/line-range-options.adoc @@ -1,12 +1,12 @@ --L<start>,<end>:<file>:: --L:<funcname>:<file>:: +`-L<start>,<end>:<file>`:: +`-L:<funcname>:<file>`:: - Trace the evolution of the line range given by '<start>,<end>', - or by the function name regex '<funcname>', within the '<file>'. You may + Trace the evolution of the line range given by `<start>,<end>`, + or by the function name regex _<funcname>_, within the _<file>_. You may not give any pathspec limiters. This is currently limited to a walk starting from a single revision, i.e., you may only give zero or one positive revision arguments, and - '<start>' and '<end>' (or '<funcname>') must exist in the starting revision. + _<start>_ and _<end>_ (or _<funcname>_) must exist in the starting revision. You can specify this option more than once. Implies `--patch`. Patch output can be suppressed using `--no-patch`, but other diff formats (namely `--raw`, `--numstat`, `--shortstat`, `--dirstat`, `--summary`, diff --git a/Documentation/lint-delimited-sections.perl b/Documentation/lint-delimited-sections.perl new file mode 100755 index 0000000000..140b852e5d --- /dev/null +++ b/Documentation/lint-delimited-sections.perl @@ -0,0 +1,48 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my $exit_code = 0; +sub report { + my ($msg) = @_; + print STDERR "$ARGV:$.: $msg\n"; + $exit_code = 1; +} + +my $line_length = 0; +my $in_section = 0; +my $section_header = ""; + + +while (my $line = <>) { + if (($line =~ /^\+?$/) || + ($line =~ /^\[.*\]$/) || + ($line =~ /^ifdef::/)) { + $line_length = 0; + } elsif ($line =~ /^[^-.]/) { + $line_length = length($line); + } elsif (($line =~ /^-{3,}$/) || ($line =~ /^\.{3,}$/)) { + if ($in_section) { + if ($line eq $section_header) { + $in_section = 0; + } + next; + } + if ($line_length == 0) { + $in_section = 1; + $section_header = $line; + next; + } + if (($line_length != 0) && (length($line) != $line_length)) { + report("section delimiter not preceded by an empty line"); + } + $line_length = 0; + } +} + +if ($in_section) { + report("section not finished"); +} + +exit $exit_code; diff --git a/Documentation/lint-documentation-style.perl b/Documentation/lint-documentation-style.perl new file mode 100755 index 0000000000..d7ab732293 --- /dev/null +++ b/Documentation/lint-documentation-style.perl @@ -0,0 +1,33 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my $exit_code = 0; +sub report { + my ($line, $msg) = @_; + chomp $line; + print STDERR "$ARGV:$.: '$line' $msg\n"; + $exit_code = 1; +} + +my $synopsis_style = 0; + +while (my $line = <>) { + if ($line =~ /^[ \t]*`?[-a-z0-9.]+`?(, `?[-a-z0-9.]+`?)+(::|;;)$/) { + + report($line, "multiple parameters in a definition list item"); + } + if ($line =~ /^`?--\[no-\][a-z0-9-]+.*(::|;;)$/) { + report($line, "definition list item with a `--[no-]` parameter"); + } + if ($line =~ /^\[synopsis\]$/) { + $synopsis_style = 1; + } + if (($line =~ /^(-[-a-z].*|<[-a-z0-9]+>(\.{3})?)(::|;;)$/) && ($synopsis_style)) { + report($line, "synopsis style and definition list item not backquoted"); + } +} + + +exit $exit_code; diff --git a/Documentation/lint-gitlink.perl b/Documentation/lint-gitlink.perl index aea564dad7..f183a18df2 100755 --- a/Documentation/lint-gitlink.perl +++ b/Documentation/lint-gitlink.perl @@ -41,6 +41,13 @@ die "BUG: No list of valid linkgit:* files given" unless @ARGV; @ARGV = $to_check; while (<>) { my $line = $_; + while ($line =~ m/(.{,8})((git[-a-z]+|scalar)\[(\d)*\])/g) { + my $pos = pos $line; + my ($macro, $target, $page, $section) = ($1, $2, $3, $4); + if ( $macro ne "linkgit:" && $macro !~ "ifn?def::" && $macro ne "endif::" ) { + report($pos, $line, $target, "linkgit: macro expected"); + } + } while ($line =~ m/linkgit:((.*?)\[(\d)\])/g) { my $pos = pos $line; my ($target, $page, $section) = ($1, $2, $3); diff --git a/Documentation/merge-options.adoc b/Documentation/merge-options.adoc index 078f4f6157..9d433265b2 100644 --- a/Documentation/merge-options.adoc +++ b/Documentation/merge-options.adoc @@ -113,6 +113,9 @@ include::signoff-option.adoc[] With `-n` or `--no-stat` do not show a diffstat at the end of the merge. +`--compact-summary`:: + Show a compact-summary at the end of the merge. + `--squash`:: `--no-squash`:: Produce the working tree and index state as if a real merge @@ -132,7 +135,8 @@ ifdef::git-pull[] Only useful when merging. endif::git-pull[] -`--[no-]verify`:: +`--verify`:: +`--no-verify`:: By default, the pre-merge and commit-msg hooks are run. When `--no-verify` is given, these are bypassed. See also linkgit:githooks[5]. diff --git a/Documentation/mergetools/vimdiff.adoc b/Documentation/mergetools/vimdiff.adoc index abfd426f74..b4ab83a510 100644 --- a/Documentation/mergetools/vimdiff.adoc +++ b/Documentation/mergetools/vimdiff.adoc @@ -3,6 +3,7 @@ Description When specifying `--tool=vimdiff` in `git mergetool` Git will open Vim with a 4 windows layout distributed in the following way: + .... ------------------------------------------ | | | | @@ -56,6 +57,7 @@ needed in this case. The next layout definition is equivalent: + -- If, for some reason, we are not interested in the `BASE` buffer. + .... ------------------------------------------ | | | | @@ -72,6 +74,7 @@ If, for some reason, we are not interested in the `BASE` buffer. Only the `MERGED` buffer will be shown. Note, however, that all the other ones are still loaded in vim, and you can access them with the "buffers" command. + .... ------------------------------------------ | | @@ -88,6 +91,7 @@ command. When `MERGED` is not present in the layout, you must "mark" one of the buffers with an arobase (`@`). That will become the buffer you need to edit and save after resolving the conflicts. + .... ------------------------------------------ | | | @@ -106,6 +110,7 @@ save after resolving the conflicts. Three tabs will open: the first one is a copy of the default layout, while the other two only show the differences between (`BASE` and `LOCAL`) and (`BASE` and `REMOTE`) respectively. + .... ------------------------------------------ | <TAB #1> | TAB #2 | TAB #3 | | @@ -119,6 +124,7 @@ the other two only show the differences between (`BASE` and `LOCAL`) and | | ------------------------------------------ .... + .... ------------------------------------------ | TAB #1 | <TAB #2> | TAB #3 | | @@ -132,6 +138,7 @@ the other two only show the differences between (`BASE` and `LOCAL`) and | | | ------------------------------------------ .... + .... ------------------------------------------ | TAB #1 | TAB #2 | <TAB #3> | | @@ -151,6 +158,7 @@ the other two only show the differences between (`BASE` and `LOCAL`) and -- Same as the previous example, but adds a fourth tab with the same information as the first tab, with a different layout. + .... --------------------------------------------- | TAB #1 | TAB #2 | TAB #3 | <TAB #4> | diff --git a/Documentation/meson.build b/Documentation/meson.build index 1433acfd31..e34965c5b0 100644 --- a/Documentation/meson.build +++ b/Documentation/meson.build @@ -74,6 +74,7 @@ manpages = { 'git-init.adoc' : 1, 'git-instaweb.adoc' : 1, 'git-interpret-trailers.adoc' : 1, + 'git-last-modified.adoc' : 1, 'git-log.adoc' : 1, 'git-ls-files.adoc' : 1, 'git-ls-remote.adoc' : 1, @@ -116,6 +117,7 @@ manpages = { 'git-repack.adoc' : 1, 'git-replace.adoc' : 1, 'git-replay.adoc' : 1, + 'git-repo.adoc' : 1, 'git-request-pull.adoc' : 1, 'git-rerere.adoc' : 1, 'git-reset.adoc' : 1, @@ -158,7 +160,6 @@ manpages = { 'git-verify-tag.adoc' : 1, 'git-version.adoc' : 1, 'git-web--browse.adoc' : 1, - 'git-whatchanged.adoc' : 1, 'git-worktree.adoc' : 1, 'git-write-tree.adoc' : 1, 'git.adoc' : 1, @@ -207,6 +208,7 @@ manpages = { manpages_breaking_changes = { 'git-pack-redundant.adoc' : 1, + 'git-whatchanged.adoc' : 1, } if not get_option('breaking_changes') @@ -375,8 +377,7 @@ foreach manpage, category : manpages output: fs.stem(manpage) + '.xml', ) - manpage_path = fs.stem(manpage) + '.' + category.to_string() - manpage_target = custom_target( + custom_target( command: [ xmlto, '-m', '@INPUT0@', @@ -392,7 +393,7 @@ foreach manpage, category : manpages 'manpage-normal.xsl', 'manpage-bold-literal.xsl', ], - output: manpage_path, + output: fs.stem(manpage) + '.' + category.to_string(), install: true, install_dir: get_option('mandir') / 'man' + category.to_string(), ) diff --git a/Documentation/pretty-formats.adoc b/Documentation/pretty-formats.adoc index 07475de8c3..618ddc4a0c 100644 --- a/Documentation/pretty-formats.adoc +++ b/Documentation/pretty-formats.adoc @@ -2,11 +2,11 @@ PRETTY FORMATS -------------- If the commit is a merge, and if the pretty-format -is not 'oneline', 'email' or 'raw', an additional line is -inserted before the 'Author:' line. This line begins with +is not `oneline`, `email` or `raw`, an additional line is +inserted before the `Author:` line. This line begins with "Merge: " and the hashes of ancestral commits are printed, separated by spaces. Note that the listed commits may not -necessarily be the list of the *direct* parent commits if you +necessarily be the list of the 'direct' parent commits if you have limited your view of history: for example, if you are only interested in changes related to a certain directory or file. @@ -14,24 +14,24 @@ file. There are several built-in formats, and you can define additional formats by setting a pretty.<name> config option to either another format name, or a -'format:' string, as described below (see +`format:` string, as described below (see linkgit:git-config[1]). Here are the details of the built-in formats: -* 'oneline' +* `oneline` <hash> <title-line> + This is designed to be as compact as possible. -* 'short' +* `short` commit <hash> Author: <author> <title-line> -* 'medium' +* `medium` commit <hash> Author: <author> @@ -41,7 +41,7 @@ This is designed to be as compact as possible. <full-commit-message> -* 'full' +* `full` commit <hash> Author: <author> @@ -51,7 +51,7 @@ This is designed to be as compact as possible. <full-commit-message> -* 'fuller' +* `fuller` commit <hash> Author: <author> @@ -63,18 +63,18 @@ This is designed to be as compact as possible. <full-commit-message> -* 'reference' +* `reference` <abbrev-hash> (<title-line>, <short-author-date>) + This format is used to refer to another commit in a commit message and -is the same as `--pretty='format:%C(auto)%h (%s, %ad)'`. By default, +is the same as ++--pretty=\'format:%C(auto)%h (%s, %ad)'++. By default, the date is formatted with `--date=short` unless another `--date` option is explicitly specified. As with any `format:` with format placeholders, its output is not affected by other options like `--decorate` and `--walk-reflogs`. -* 'email' +* `email` From <hash> <date> From: <author> @@ -83,30 +83,30 @@ placeholders, its output is not affected by other options like <full-commit-message> -* 'mboxrd' +* `mboxrd` + -Like 'email', but lines in the commit message starting with "From " +Like `email`, but lines in the commit message starting with "From " (preceded by zero or more ">") are quoted with ">" so they aren't confused as starting a new commit. -* 'raw' +* `raw` + -The 'raw' format shows the entire commit exactly as +The `raw` format shows the entire commit exactly as stored in the commit object. Notably, the hashes are -displayed in full, regardless of whether --abbrev or ---no-abbrev are used, and 'parents' information show the +displayed in full, regardless of whether `--abbrev` or +`--no-abbrev` are used, and 'parents' information show the true parent commits, without taking grafts or history simplification into account. Note that this format affects the way commits are displayed, but not the way the diff is shown e.g. with `git log --raw`. To get full object names in a raw diff format, use `--no-abbrev`. -* 'format:<format-string>' +* `format:<format-string>` + -The 'format:<format-string>' format allows you to specify which information +The `format:<format-string>` format allows you to specify which information you want to show. It works a little bit like printf format, -with the notable exception that you get a newline with '%n' -instead of '\n'. +with the notable exception that you get a newline with `%n` +instead of `\n`. + E.g, 'format:"The author of %h was %an, %ar%nThe title was >>%s<<%n"' would show something like this: @@ -120,158 +120,161 @@ The title was >>t4119: test autocomputing -p<n> for traditional diff input.<< The placeholders are: - Placeholders that expand to a single literal character: -'%n':: newline -'%%':: a raw '%' -'%x00':: '%x' followed by two hexadecimal digits is replaced with a +++%n++:: newline +++%%++:: a raw ++%++ +++%x00++:: ++%x++ followed by two hexadecimal digits is replaced with a byte with the hexadecimal digits' value (we will call this "literal formatting code" in the rest of this document). - Placeholders that affect formatting of later placeholders: -'%Cred':: switch color to red -'%Cgreen':: switch color to green -'%Cblue':: switch color to blue -'%Creset':: reset color -'%C(...)':: color specification, as described under Values in the +++%Cred++:: switch color to red +++%Cgreen++:: switch color to green +++%Cblue++:: switch color to blue +++%Creset++:: reset color +++%C(++_<spec>_++)++:: color specification, as described under Values in the "CONFIGURATION FILE" section of linkgit:git-config[1]. By default, colors are shown only when enabled for log output (by `color.diff`, `color.ui`, or `--color`, and respecting the `auto` settings of the former if we are going to a - terminal). `%C(auto,...)` is accepted as a historical - synonym for the default (e.g., `%C(auto,red)`). Specifying - `%C(always,...)` will show the colors even when color is + terminal). ++%C(auto,++_<spec>_++)++ is accepted as a historical + synonym for the default (e.g., ++%C(auto,red)++). Specifying + ++%C(always,++_<spec>_++)++ will show the colors even when color is not otherwise enabled (though consider just using - `--color=always` to enable color for the whole output, + `--color=always` to enable color for the whole output, including this format and anything else git might color). - `auto` alone (i.e. `%C(auto)`) will turn on auto coloring + `auto` alone (i.e. ++%C(auto)++) will turn on auto coloring on the next placeholders until the color is switched again. -'%m':: left (`<`), right (`>`) or boundary (`-`) mark -'%w([<w>[,<i1>[,<i2>]]])':: switch line wrapping, like the -w option of +++%m++:: left (`<`), right (`>`) or boundary (`-`) mark +++%w(++`[<w>[,<i1>[,<i2>]]]`++)++:: switch line wrapping, like the `-w` option of linkgit:git-shortlog[1]. -'%<( <N> [,trunc|ltrunc|mtrunc])':: make the next placeholder take at +++%<(++`<n>[,(trunc|ltrunc|mtrunc)]`++)++:: make the next placeholder take at least N column widths, padding spaces on the right if necessary. Optionally - truncate (with ellipsis '..') at the left (ltrunc) `..ft`, + truncate (with ellipsis `..`) at the left (ltrunc) `..ft`, the middle (mtrunc) `mi..le`, or the end (trunc) `rig..`, if the output is longer than - N columns. + _<n>_ columns. Note 1: that truncating - only works correctly with N >= 2. - Note 2: spaces around the N and M (see below) + only works correctly with _<n>_ >= 2. + Note 2: spaces around the _<n>_ and _<m>_ (see below) values are optional. Note 3: Emojis and other wide characters will take two display columns, which may over-run column boundaries. Note 4: decomposed character combining marks may be misplaced at padding boundaries. -'%<|( <M> )':: make the next placeholder take at least until Mth +++%<|(++_<m>_ ++)++:: make the next placeholder take at least until _<m>_ th display column, padding spaces on the right if necessary. - Use negative M values for column positions measured + Use negative _<m>_ values for column positions measured from the right hand edge of the terminal window. -'%>( <N> )', '%>|( <M> )':: similar to '%<( <N> )', '%<|( <M> )' respectively, +++%>(++_<n>_++)++:: +++%>|(++_<m>_++)++:: similar to ++%<(++_<n>_++)++, ++%<|(++_<m>_++)++ respectively, but padding spaces on the left -'%>>( <N> )', '%>>|( <M> )':: similar to '%>( <N> )', '%>|( <M> )' +++%>>(++_<n>_++)++:: +++%>>|(++_<m>_++)++:: similar to ++%>(++_<n>_++)++, ++%>|(++_<m>_++)++ respectively, except that if the next placeholder takes more spaces than given and there are spaces on its left, use those spaces -'%><( <N> )', '%><|( <M> )':: similar to '%<( <N> )', '%<|( <M> )' +++%><(++_<n>_++)++:: +++%><|(++_<m>_++)++:: similar to ++%<(++_<n>_++)++, ++%<|(++_<m>_++)++ respectively, but padding both sides (i.e. the text is centered) - Placeholders that expand to information extracted from the commit: -'%H':: commit hash -'%h':: abbreviated commit hash -'%T':: tree hash -'%t':: abbreviated tree hash -'%P':: parent hashes -'%p':: abbreviated parent hashes -'%an':: author name -'%aN':: author name (respecting .mailmap, see linkgit:git-shortlog[1] ++%H+:: commit hash ++%h+:: abbreviated commit hash ++%T+:: tree hash ++%t+:: abbreviated tree hash ++%P+:: parent hashes ++%p+:: abbreviated parent hashes ++%an+:: author name ++%aN+:: author name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1]) -'%ae':: author email -'%aE':: author email (respecting .mailmap, see linkgit:git-shortlog[1] ++%ae+:: author email ++%aE+:: author email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1]) -'%al':: author email local-part (the part before the '@' sign) -'%aL':: author local-part (see '%al') respecting .mailmap, see ++%al+:: author email local-part (the part before the `@` sign) ++%aL+:: author local-part (see +%al+) respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1]) -'%ad':: author date (format respects --date= option) -'%aD':: author date, RFC2822 style -'%ar':: author date, relative -'%at':: author date, UNIX timestamp -'%ai':: author date, ISO 8601-like format -'%aI':: author date, strict ISO 8601 format -'%as':: author date, short format (`YYYY-MM-DD`) -'%ah':: author date, human style (like the `--date=human` option of ++%ad+:: author date (format respects --date= option) ++%aD+:: author date, RFC2822 style ++%ar+:: author date, relative ++%at+:: author date, UNIX timestamp ++%ai+:: author date, ISO 8601-like format ++%aI+:: author date, strict ISO 8601 format ++%as+:: author date, short format (`YYYY-MM-DD`) ++%ah+:: author date, human style (like the `--date=human` option of linkgit:git-rev-list[1]) -'%cn':: committer name -'%cN':: committer name (respecting .mailmap, see ++%cn+:: committer name ++%cN+:: committer name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1]) -'%ce':: committer email -'%cE':: committer email (respecting .mailmap, see ++%ce+:: committer email ++%cE+:: committer email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1]) -'%cl':: committer email local-part (the part before the '@' sign) -'%cL':: committer local-part (see '%cl') respecting .mailmap, see ++%cl+:: committer email local-part (the part before the `@` sign) ++%cL+:: committer local-part (see +%cl+) respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1]) -'%cd':: committer date (format respects --date= option) -'%cD':: committer date, RFC2822 style -'%cr':: committer date, relative -'%ct':: committer date, UNIX timestamp -'%ci':: committer date, ISO 8601-like format -'%cI':: committer date, strict ISO 8601 format -'%cs':: committer date, short format (`YYYY-MM-DD`) -'%ch':: committer date, human style (like the `--date=human` option of ++%cd+:: committer date (format respects --date= option) ++%cD+:: committer date, RFC2822 style ++%cr+:: committer date, relative ++%ct+:: committer date, UNIX timestamp ++%ci+:: committer date, ISO 8601-like format ++%cI+:: committer date, strict ISO 8601 format ++%cs+:: committer date, short format (`YYYY-MM-DD`) ++%ch+:: committer date, human style (like the `--date=human` option of linkgit:git-rev-list[1]) -'%d':: ref names, like the --decorate option of linkgit:git-log[1] -'%D':: ref names without the " (", ")" wrapping. -'%(decorate[:<options>])':: ++%d+:: ref names, like the --decorate option of linkgit:git-log[1] ++%D+:: ref names without the " (", ")" wrapping. +++%(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 literal formatting codes. These must be used for commas (`%x2C`) and closing parentheses (`%x29`), due to their role in the option syntax. + -** 'prefix=<value>': Shown before the list of ref names. Defaults to "{nbsp}`(`". -** 'suffix=<value>': Shown after the list of ref names. Defaults to "`)`". -** 'separator=<value>': Shown between ref names. Defaults to "`,`{nbsp}". -** 'pointer=<value>': Shown between HEAD and the branch it points to, if any. - Defaults to "{nbsp}`->`{nbsp}". -** 'tag=<value>': Shown before tag names. Defaults to "`tag:`{nbsp}". +** `prefix=<value>`: Shown before the list of ref names. Defaults to "{nbsp}++(++". +** `suffix=<value>`: Shown after the list of ref names. Defaults to "+)+". +** `separator=<value>`: Shown between ref names. Defaults to "+,+{nbsp}". +** `pointer=<value>`: Shown between HEAD and the branch it points to, if any. + Defaults to "{nbsp}++->++{nbsp}". +** `tag=<value>`: Shown before tag names. Defaults to "`tag:`{nbsp}". + For example, to produce decorations with no wrapping or tag annotations, and spaces as separators: + -`%(decorate:prefix=,suffix=,tag=,separator= )` +++%(decorate:prefix=,suffix=,tag=,separator= )++ -'%(describe[:<options>])':: +++%(describe++`[:<option>,...]`++)++:: human-readable name, like linkgit:git-describe[1]; empty string for undescribable commits. The `describe` string may be followed by a colon and zero or more comma-separated options. Descriptions can be inconsistent when tags are added or removed at the same time. + -** 'tags[=<bool-value>]': Instead of only considering annotated tags, +** `tags[=<bool-value>]`: Instead of only considering annotated tags, consider lightweight tags as well. -** 'abbrev=<number>': Instead of using the default number of hexadecimal digits +** `abbrev=<number>`: Instead of using the default number of hexadecimal digits (which will vary according to the number of objects in the repository with a default of 7) of the abbreviated object name, use <number> digits, or as many digits as needed to form a unique object name. -** 'match=<pattern>': Only consider tags matching the given - `glob(7)` pattern, excluding the "refs/tags/" prefix. -** 'exclude=<pattern>': Do not consider tags matching the given - `glob(7)` pattern, excluding the "refs/tags/" prefix. +** `match=<pattern>`: Only consider tags matching the given + `glob(7)` _<pattern>_, excluding the `refs/tags/` prefix. +** `exclude=<pattern>`: Do not consider tags matching the given + `glob(7)` _<pattern>_, excluding the `refs/tags/` prefix. -'%S':: ref name given on the command line by which the commit was reached ++%S+:: ref name given on the command line by which the commit was reached (like `git log --source`), only works with `git log` -'%e':: encoding -'%s':: subject -'%f':: sanitized subject line, suitable for a filename -'%b':: body -'%B':: raw body (unwrapped subject and body) ++%e+:: encoding ++%s+:: subject ++%f+:: sanitized subject line, suitable for a filename ++%b+:: body ++%B+:: raw body (unwrapped subject and body) ifndef::git-rev-list[] -'%N':: commit notes ++%N+:: commit notes endif::git-rev-list[] -'%GG':: raw verification message from GPG for a signed commit -'%G?':: show "G" for a good (valid) signature, ++%GG+:: raw verification message from GPG for a signed commit ++%G?+:: show "G" for a good (valid) signature, "B" for a bad signature, "U" for a good signature with unknown validity, "X" for a good signature that has expired, @@ -279,86 +282,86 @@ endif::git-rev-list[] "R" for a good signature made by a revoked key, "E" if the signature cannot be checked (e.g. missing key) and "N" for no signature -'%GS':: show the name of the signer for a signed commit -'%GK':: show the key used to sign a signed commit -'%GF':: show the fingerprint of the key used to sign a signed commit -'%GP':: show the fingerprint of the primary key whose subkey was used ++%GS+:: show the name of the signer for a signed commit ++%GK+:: show the key used to sign a signed commit ++%GF+:: show the fingerprint of the key used to sign a signed commit ++%GP+:: show the fingerprint of the primary key whose subkey was used to sign a signed commit -'%GT':: show the trust level for the key used to sign a signed commit -'%gD':: reflog selector, e.g., `refs/stash@{1}` or `refs/stash@{2 ++%GT+:: show the trust level for the key used to sign a signed commit ++%gD+:: reflog selector, e.g., `refs/stash@{1}` or `refs/stash@{2 minutes ago}`; the format follows the rules described for the `-g` option. The portion before the `@` is the refname as given on the command line (so `git log -g refs/heads/master` would yield `refs/heads/master@{0}`). -'%gd':: shortened reflog selector; same as `%gD`, but the refname ++%gd+:: shortened reflog selector; same as `%gD`, but the refname portion is shortened for human readability (so `refs/heads/master` becomes just `master`). -'%gn':: reflog identity name -'%gN':: reflog identity name (respecting .mailmap, see ++%gn+:: reflog identity name ++%gN+:: reflog identity name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1]) -'%ge':: reflog identity email -'%gE':: reflog identity email (respecting .mailmap, see ++%ge+:: reflog identity email ++%gE+:: reflog identity email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1]) -'%gs':: reflog subject -'%(trailers[:<options>])':: ++%gs+:: reflog subject +++%(trailers++`[:<option>,...]`++)++:: display the trailers of the body as interpreted by linkgit:git-interpret-trailers[1]. The `trailers` string may be followed by a colon and zero or more comma-separated options. If any option is provided multiple times, the last occurrence wins. + -** 'key=<key>': only show trailers with specified <key>. Matching is done +** `key=<key>`: only show trailers with specified <key>. Matching is done case-insensitively and trailing colon is optional. If option is given multiple times trailer lines matching any of the keys are shown. This option automatically enables the `only` option so that non-trailer lines in the trailer block are hidden. If that is not desired it can be disabled with `only=false`. E.g., - `%(trailers:key=Reviewed-by)` shows trailer lines with key + +%(trailers:key=Reviewed-by)+ shows trailer lines with key `Reviewed-by`. -** 'only[=<bool>]': select whether non-trailer lines from the trailer +** `only[=<bool>]`: select whether non-trailer lines from the trailer block should be included. -** 'separator=<sep>': specify the separator inserted between trailer +** `separator=<sep>`: specify the separator inserted between trailer lines. Defaults to a line feed character. The string <sep> may contain the literal formatting codes described above. To use comma as separator one must use `%x2C` as it would otherwise be parsed as - next option. E.g., `%(trailers:key=Ticket,separator=%x2C )` + next option. E.g., +%(trailers:key=Ticket,separator=%x2C )+ shows all trailer lines whose key is "Ticket" separated by a comma and a space. -** 'unfold[=<bool>]': make it behave as if interpret-trailer's `--unfold` +** `unfold[=<bool>]`: make it behave as if interpret-trailer's `--unfold` option was given. E.g., - `%(trailers:only,unfold=true)` unfolds and shows all trailer lines. -** 'keyonly[=<bool>]': only show the key part of the trailer. -** 'valueonly[=<bool>]': only show the value part of the trailer. -** 'key_value_separator=<sep>': specify the separator inserted between + +%(trailers:only,unfold=true)+ unfolds and shows all trailer lines. +** `keyonly[=<bool>]`: only show the key part of the trailer. +** `valueonly[=<bool>]`: only show the value part of the trailer. +** `key_value_separator=<sep>`: specify the separator inserted between the key and value of each trailer. Defaults to ": ". Otherwise it - shares the same semantics as 'separator=<sep>' above. + shares the same semantics as `separator=<sep>` above. NOTE: Some placeholders may depend on other options given to the -revision traversal engine. For example, the `%g*` reflog options will +revision traversal engine. For example, the +%g*+ reflog options will insert an empty string unless we are traversing reflog entries (e.g., by -`git log -g`). The `%d` and `%D` placeholders will use the "short" +`git log -g`). The +%d+ and +%D+ placeholders will use the "short" decoration format if `--decorate` was not already provided on the command line. The boolean options accept an optional value `[=<bool-value>]`. The -values taken by `--type=bool` git-config[1], like `yes` and `off`, +values taken by `--type=bool` linkgit:git-config[1], like `yes` and `off`, are all accepted. Giving a boolean option without `=<value>` is equivalent to giving it with `=true`. -If you add a `+` (plus sign) after '%' of a placeholder, a line-feed +If you add a `+` (plus sign) after +%+ of a placeholder, a line-feed is inserted immediately before the expansion if and only if the placeholder expands to a non-empty string. -If you add a `-` (minus sign) after '%' of a placeholder, all consecutive +If you add a `-` (minus sign) after +%+ of a placeholder, all consecutive line-feeds immediately preceding the expansion are deleted if and only if the placeholder expands to an empty string. -If you add a ` ` (space) after '%' of a placeholder, a space +If you add a `' '` (space) after +%+ of a placeholder, a space is inserted immediately before the expansion if and only if the placeholder expands to a non-empty string. -* 'tformat:' +* `tformat:` + -The 'tformat:' format works exactly like 'format:', except that it +The `tformat:` format works exactly like `format:`, except that it provides "terminator" semantics instead of "separator" semantics. In other words, each commit has the message terminator character (usually a newline) appended, rather than a separator placed between entries. @@ -378,7 +381,7 @@ $ git log -2 --pretty=tformat:%h 4da45bef \ 7134973 --------------------- + -In addition, any unrecognized string that has a `%` in it is interpreted +In addition, any unrecognized string that has a +%+ in it is interpreted as if it has `tformat:` in front of it. For example, these two are equivalent: + diff --git a/Documentation/pretty-options.adoc b/Documentation/pretty-options.adoc index 23888cd612..8aac51dbe7 100644 --- a/Documentation/pretty-options.adoc +++ b/Documentation/pretty-options.adoc @@ -1,38 +1,38 @@ ---pretty[=<format>]:: ---format=<format>:: +`--pretty[=<format>]`:: +`--format=<format>`:: Pretty-print the contents of the commit logs in a given format, - where '<format>' can be one of 'oneline', 'short', 'medium', - 'full', 'fuller', 'reference', 'email', 'raw', 'format:<string>' - and 'tformat:<string>'. When '<format>' is none of the above, - and has '%placeholder' in it, it acts as if - '--pretty=tformat:<format>' were given. + where '<format>' can be one of `oneline`, `short`, `medium`, + `full`, `fuller`, `reference`, `email`, `raw`, `format:<string>` + and `tformat:<string>`. When _<format>_ is none of the above, + and has `%<placeholder>` in it, it acts as if + `--pretty=tformat:<format>` were given. + See the "PRETTY FORMATS" section for some additional details for each -format. When '=<format>' part is omitted, it defaults to 'medium'. +format. When `=<format>` part is omitted, it defaults to `medium`. + -Note: you can specify the default pretty format in the repository +NOTE: you can specify the default pretty format in the repository configuration (see linkgit:git-config[1]). ---abbrev-commit:: +`--abbrev-commit`:: Instead of showing the full 40-byte hexadecimal commit object name, show a prefix that names the object uniquely. - "--abbrev=<n>" (which also modifies diff output, if it is displayed) + `--abbrev=<n>` (which also modifies diff output, if it is displayed) option can be used to specify the minimum length of the prefix. + -This should make "--pretty=oneline" a whole lot more readable for +This should make `--pretty=oneline` a whole lot more readable for people using 80-column terminals. ---no-abbrev-commit:: +`--no-abbrev-commit`:: Show the full 40-byte hexadecimal commit object name. This negates `--abbrev-commit`, either explicit or implied by other options such - as "--oneline". It also overrides the `log.abbrevCommit` variable. + as `--oneline`. It also overrides the `log.abbrevCommit` variable. ---oneline:: - This is a shorthand for "--pretty=oneline --abbrev-commit" +`--oneline`:: + This is a shorthand for `--pretty=oneline --abbrev-commit` used together. ---encoding=<encoding>:: +`--encoding=<encoding>`:: Commit objects record the character encoding used for the log message in their encoding header; this option can be used to tell the command to re-code the commit log message in the encoding @@ -44,25 +44,30 @@ people using 80-column terminals. to convert the commit, we will quietly output the original object verbatim. ---expand-tabs=<n>:: ---expand-tabs:: ---no-expand-tabs:: +`--expand-tabs=<n>`:: +`--expand-tabs`:: +`--no-expand-tabs`:: Perform a tab expansion (replace each tab with enough spaces - to fill to the next display column that is a multiple of '<n>') + to fill to the next display column that is a multiple of _<n>_) in the log message before showing it in the output. `--expand-tabs` is a short-hand for `--expand-tabs=8`, and `--no-expand-tabs` is a short-hand for `--expand-tabs=0`, which disables tab expansion. + By default, tabs are expanded in pretty formats that indent the log -message by 4 spaces (i.e. 'medium', which is the default, 'full', -and 'fuller'). +message by 4 spaces (i.e. `medium`, which is the default, `full`, +and `fuller`). ifndef::git-rev-list[] ---notes[=<ref>]:: +`--notes[=<ref>]`:: Show the notes (see linkgit:git-notes[1]) that annotate the commit, when showing the commit log message. This is the default +ifndef::with-breaking-changes[] for `git log`, `git show` and `git whatchanged` commands when +endif::with-breaking-changes[] +ifdef::with-breaking-changes[] + for `git log` and `git show` commands when +endif::with-breaking-changes[] there is no `--pretty`, `--format`, or `--oneline` option given on the command line. + @@ -75,28 +80,29 @@ to display. The ref can specify the full refname when it begins with `refs/notes/`; when it begins with `notes/`, `refs/` and otherwise `refs/notes/` is prefixed to form the full name of the ref. + -Multiple --notes options can be combined to control which notes are -being displayed. Examples: "--notes=foo" will show only notes from -"refs/notes/foo"; "--notes=foo --notes" will show both notes from +Multiple `--notes` options can be combined to control which notes are +being displayed. Examples: "`--notes=foo`" will show only notes from +`refs/notes/foo`; "`--notes=foo --notes`" will show both notes from "refs/notes/foo" and from the default notes ref(s). ---no-notes:: +`--no-notes`:: Do not show notes. This negates the above `--notes` option, by resetting the list of notes refs from which notes are shown. Options are parsed in the order given on the command line, so e.g. - "--notes --notes=foo --no-notes --notes=bar" will only show notes - from "refs/notes/bar". + "`--notes --notes=foo --no-notes --notes=bar`" will only show notes + from `refs/notes/bar`. ---show-notes-by-default:: +`--show-notes-by-default`:: Show the default notes unless options for displaying specific notes are given. ---show-notes[=<ref>]:: ---[no-]standard-notes:: - These options are deprecated. Use the above --notes/--no-notes +`--show-notes[=<ref>]`:: +`--standard-notes`:: +`--no-standard-notes`:: + These options are deprecated. Use the above `--notes`/`--no-notes` options instead. endif::git-rev-list[] ---show-signature:: +`--show-signature`:: Check the validity of a signed commit object by passing the signature to `gpg --verify` and show the output. diff --git a/Documentation/rev-list-description.adoc b/Documentation/rev-list-description.adoc index a9efa7fa27..82c680e570 100644 --- a/Documentation/rev-list-description.adoc +++ b/Documentation/rev-list-description.adoc @@ -26,8 +26,8 @@ endif::git-log[] means "list all the commits which are reachable from 'foo' or 'bar', but not from 'baz'". -A special notation "'<commit1>'..'<commit2>'" can be used as a -short-hand for "^'<commit1>' '<commit2>'". For example, either of +A special notation "`<commit1>..<commit2>`" can be used as a +short-hand for "`^<commit1> <commit2>`". For example, either of the following may be used interchangeably: ifdef::git-rev-list[] @@ -43,7 +43,7 @@ $ git log HEAD ^origin ----------------------------------------------------------------------- endif::git-log[] -Another special notation is "'<commit1>'...'<commit2>'" which is useful +Another special notation is "`<commit1>...<commit2>`" which is useful for merges. The resulting set of commits is the symmetric difference between the two operands. The following two commands are equivalent: diff --git a/Documentation/rev-list-options.adoc b/Documentation/rev-list-options.adoc index d38875efda..d9665d82c8 100644 --- a/Documentation/rev-list-options.adoc +++ b/Documentation/rev-list-options.adoc @@ -6,60 +6,60 @@ special notations explained in the description, additional commit limiting may be applied. Using more options generally further limits the output (e.g. -`--since=<date1>` limits to commits newer than `<date1>`, and using it +`--since=<date1>` limits to commits newer than _<date1>_, and using it with `--grep=<pattern>` further limits to commits whose log message -has a line that matches `<pattern>`), unless otherwise noted. +has a line that matches _<pattern>_), unless otherwise noted. Note that these are applied before commit ordering and formatting options, such as `--reverse`. --<number>:: --n <number>:: ---max-count=<number>:: - Limit the number of commits to output. +`-<number>`:: +`-n <number>`:: +`--max-count=<number>`:: + Limit the output to _<number>_ commits. ---skip=<number>:: - Skip 'number' commits before starting to show the commit output. +`--skip=<number>`:: + Skip _<number>_ commits before starting to show the commit output. ---since=<date>:: ---after=<date>:: - Show commits more recent than a specific date. +`--since=<date>`:: +`--after=<date>`:: + Show commits more recent than _<date>_. ---since-as-filter=<date>:: - Show all commits more recent than a specific date. This visits +`--since-as-filter=<date>`:: + Show all commits more recent than _<date>_. This visits all commits in the range, rather than stopping at the first commit which - is older than a specific date. + is older than _<date>_. ---until=<date>:: ---before=<date>:: - Show commits older than a specific date. +`--until=<date>`:: +`--before=<date>`:: + Show commits older than _<date>_. ifdef::git-rev-list[] ---max-age=<timestamp>:: ---min-age=<timestamp>:: +`--max-age=<timestamp>`:: +`--min-age=<timestamp>`:: Limit the commits output to specified time range. endif::git-rev-list[] ---author=<pattern>:: ---committer=<pattern>:: +`--author=<pattern>`:: +`--committer=<pattern>`:: Limit the commits output to ones with author/committer - header lines that match the specified pattern (regular - expression). With more than one `--author=<pattern>`, - commits whose author matches any of the given patterns are + header lines that match the _<pattern>_ regular + expression. With more than one `--author=<pattern>`, + commits whose author matches any of the _<pattern>_ are chosen (similarly for multiple `--committer=<pattern>`). ---grep-reflog=<pattern>:: +`--grep-reflog=<pattern>`:: Limit the commits output to ones with reflog entries that - match the specified pattern (regular expression). With + match the _<pattern>_ regular expression. With more than one `--grep-reflog`, commits whose reflog message matches any of the given patterns are chosen. It is an error to use this option unless `--walk-reflogs` is in use. ---grep=<pattern>:: +`--grep=<pattern>`:: Limit the commits output to ones with a log message that - matches the specified pattern (regular expression). With + matches the _<pattern>_ regular expression. With more than one `--grep=<pattern>`, commits whose message - matches any of the given patterns are chosen (but see + matches any of the _<pattern>_ are chosen (but see `--all-match`). ifndef::git-rev-list[] + @@ -67,35 +67,35 @@ When `--notes` is in effect, the message from the notes is matched as if it were part of the log message. endif::git-rev-list[] ---all-match:: +`--all-match`:: Limit the commits output to ones that match all given `--grep`, instead of ones that match at least one. ---invert-grep:: +`--invert-grep`:: Limit the commits output to ones with a log message that do not - match the pattern specified with `--grep=<pattern>`. + match the _<pattern>_ specified with `--grep=<pattern>`. --i:: ---regexp-ignore-case:: +`-i`:: +`--regexp-ignore-case`:: Match the regular expression limiting patterns without regard to letter case. ---basic-regexp:: +`--basic-regexp`:: Consider the limiting patterns to be basic regular expressions; this is the default. --E:: ---extended-regexp:: +`-E`:: +`--extended-regexp`:: Consider the limiting patterns to be extended regular expressions instead of the default basic regular expressions. --F:: ---fixed-strings:: +`-F`:: +`--fixed-strings`:: Consider the limiting patterns to be fixed strings (don't interpret pattern as a regular expression). --P:: ---perl-regexp:: +`-P`:: +`--perl-regexp`:: Consider the limiting patterns to be Perl-compatible regular expressions. + @@ -103,20 +103,20 @@ Support for these types of regular expressions is an optional compile-time dependency. If Git wasn't compiled with support for them providing this option will cause it to die. ---remove-empty:: +`--remove-empty`:: Stop when a given path disappears from the tree. ---merges:: +`--merges`:: Print only merge commits. This is exactly the same as `--min-parents=2`. ---no-merges:: +`--no-merges`:: Do not print commits with more than one parent. This is exactly the same as `--max-parents=1`. ---min-parents=<number>:: ---max-parents=<number>:: ---no-min-parents:: ---no-max-parents:: +`--min-parents=<number>`:: +`--max-parents=<number>`:: +`--no-min-parents`:: +`--no-max-parents`:: Show only commits which have at least (or at most) that many parent commits. In particular, `--max-parents=1` is the same as `--no-merges`, `--min-parents=2` is the same as `--merges`. `--max-parents=0` @@ -126,7 +126,7 @@ providing this option will cause it to die. again. Equivalent forms are `--min-parents=0` (any commit has 0 or more parents) and `--max-parents=-1` (negative numbers denote no upper limit). ---first-parent:: +`--first-parent`:: When finding commits to include, follow only the first parent commit upon seeing a merge commit. This option can give a better overview when viewing the evolution of @@ -141,14 +141,14 @@ This option also changes default diff format for merge commits to `first-parent`, see `--diff-merges=first-parent` for details. endif::git-log[] ---exclude-first-parent-only:: +`--exclude-first-parent-only`:: When finding commits to exclude (with a '{caret}'), follow only the first parent commit upon seeing a merge commit. This can be used to find the set of changes in a topic branch from the point where it diverged from the remote branch, given that arbitrary merges can be valid topic branch changes. ---not:: +`--not`:: Reverses the meaning of the '{caret}' prefix (or lack thereof) for all following revision specifiers, up to the next `--not`. When used on the command line before --stdin, the revisions passed @@ -156,37 +156,37 @@ endif::git-log[] via standard input, the revisions passed on the command line will not be affected by it. ---all:: +`--all`:: Pretend as if all the refs in `refs/`, along with `HEAD`, are - listed on the command line as '<commit>'. + listed on the command line as _<commit>_. ---branches[=<pattern>]:: +`--branches[=<pattern>]`:: Pretend as if all the refs in `refs/heads` are listed - on the command line as '<commit>'. If '<pattern>' is given, limit - branches to ones matching given shell glob. If pattern lacks '?', + on the command line as _<commit>_. If _<pattern>_ is given, limit + branches to ones matching given shell glob. If _<pattern>_ lacks '?', '{asterisk}', or '[', '/{asterisk}' at the end is implied. ---tags[=<pattern>]:: +`--tags[=<pattern>]`:: Pretend as if all the refs in `refs/tags` are listed - on the command line as '<commit>'. If '<pattern>' is given, limit + on the command line as _<commit>_. If _<pattern>_ is given, limit tags to ones matching given shell glob. If pattern lacks '?', '{asterisk}', or '[', '/{asterisk}' at the end is implied. ---remotes[=<pattern>]:: +`--remotes[=<pattern>]`:: Pretend as if all the refs in `refs/remotes` are listed - on the command line as '<commit>'. If '<pattern>' is given, limit + on the command line as _<commit>_. If _<pattern>_ is given, limit remote-tracking branches to ones matching given shell glob. If pattern lacks '?', '{asterisk}', or '[', '/{asterisk}' at the end is implied. ---glob=<glob-pattern>:: - Pretend as if all the refs matching shell glob '<glob-pattern>' - are listed on the command line as '<commit>'. Leading 'refs/', +`--glob=<glob-pattern>`:: + Pretend as if all the refs matching shell glob _<glob-pattern>_ + are listed on the command line as _<commit>_. Leading 'refs/', is automatically prepended if missing. If pattern lacks '?', '{asterisk}', or '[', '/{asterisk}' at the end is implied. ---exclude=<glob-pattern>:: +`--exclude=<glob-pattern>`:: - Do not include refs matching '<glob-pattern>' that the next `--all`, + Do not include refs matching _<glob-pattern>_ that the next `--all`, `--branches`, `--tags`, `--remotes`, or `--glob` would otherwise consider. Repetitions of this option accumulate exclusion patterns up to the next `--all`, `--branches`, `--tags`, `--remotes`, or @@ -199,7 +199,7 @@ respectively, and they must begin with `refs/` when applied to `--glob` or `--all`. If a trailing '/{asterisk}' is intended, it must be given explicitly. ---exclude-hidden=[fetch|receive|uploadpack]:: +`--exclude-hidden=(fetch|receive|uploadpack)`:: Do not include refs that would be hidden by `git-fetch`, `git-receive-pack` or `git-upload-pack` by consulting the appropriate `fetch.hideRefs`, `receive.hideRefs` or `uploadpack.hideRefs` @@ -207,11 +207,11 @@ explicitly. linkgit:git-config[1]). This option affects the next pseudo-ref option `--all` or `--glob` and is cleared after processing them. ---reflog:: +`--reflog`:: Pretend as if all objects mentioned by reflogs are listed on the - command line as `<commit>`. + command line as _<commit>_. ---alternate-refs:: +`--alternate-refs`:: Pretend as if all objects mentioned as ref tips of alternate repositories were listed on the command line. An alternate repository is any repository whose object directory is specified @@ -219,7 +219,7 @@ explicitly. be modified by `core.alternateRefsCommand`, etc. See linkgit:git-config[1]. ---single-worktree:: +`--single-worktree`:: By default, all working trees will be examined by the following options when there are more than one (see linkgit:git-worktree[1]): `--all`, `--reflog` and @@ -227,19 +227,19 @@ explicitly. This option forces them to examine the current working tree only. ---ignore-missing:: +`--ignore-missing`:: Upon seeing an invalid object name in the input, pretend as if the bad input was not given. ifndef::git-rev-list[] ---bisect:: +`--bisect`:: Pretend as if the bad bisection ref `refs/bisect/bad` was listed and as if it was followed by `--not` and the good bisection refs `refs/bisect/good-*` on the command line. endif::git-rev-list[] ---stdin:: +`--stdin`:: In addition to getting arguments from the command line, read them from standard input as well. This accepts commits and pseudo-options like `--all` and `--glob=`. When a `--` separator @@ -249,15 +249,15 @@ endif::git-rev-list[] influence any subsequent command line arguments. ifdef::git-rev-list[] ---quiet:: +`--quiet`:: Don't print anything to standard output. This form is primarily meant to allow the caller to test the exit status to see if a range of objects is fully connected (or not). It is faster than redirecting stdout to `/dev/null` as the output does not have to be formatted. ---disk-usage:: ---disk-usage=human:: +`--disk-usage`:: +`--disk-usage=human`:: Suppress normal output; instead, print the sum of the bytes used for on-disk storage by the selected commits or objects. This is equivalent to piping the output into `git cat-file @@ -269,11 +269,11 @@ ifdef::git-rev-list[] in human-readable string(e.g. 12.24 Kib, 3.50 Mib). endif::git-rev-list[] ---cherry-mark:: +`--cherry-mark`:: Like `--cherry-pick` (see below) but mark equivalent commits with `=` rather than omitting them, and inequivalent ones with `+`. ---cherry-pick:: +`--cherry-pick`:: Omit any commit that introduces the same change as another commit on the ``other side'' when the set of commits are limited with symmetric difference. @@ -286,8 +286,8 @@ cherry-picked from the other branch (for example, ``3rd on b'' may be cherry-picked from branch A). With this option, such pairs of commits are excluded from the output. ---left-only:: ---right-only:: +`--left-only`:: +`--right-only`:: List only commits on the respective side of a symmetric difference, i.e. only those which would be marked `<` resp. `>` by `--left-right`. @@ -298,20 +298,20 @@ commits from `B` which are in `A` or are patch-equivalent to a commit in More precisely, `--cherry-pick --right-only --no-merges` gives the exact list. ---cherry:: +`--cherry`:: A synonym for `--right-only --cherry-mark --no-merges`; useful to limit the output to the commits on our side and mark those that have been applied to the other side of a forked history with `git log --cherry upstream...mybranch`, similar to `git cherry upstream mybranch`. --g:: ---walk-reflogs:: +`-g`:: +`--walk-reflogs`:: Instead of walking the commit ancestry chain, walk reflog entries from the most recent one to older ones. When this option is used you cannot specify commits to - exclude (that is, '{caret}commit', 'commit1..commit2', - and 'commit1\...commit2' notations cannot be used). + exclude (that is, `^<commit>`, `<commit1>..<commit2>`, + and `<commit1>...<commit2>` notations cannot be used). + With `--pretty` format other than `oneline` and `reference` (for obvious reasons), this causes the output to have two extra lines of information @@ -340,29 +340,29 @@ See also linkgit:git-reflog[1]. + Under `--pretty=reference`, this information will not be shown at all. ---merge:: +`--merge`:: Show commits touching conflicted paths in the range `HEAD...<other>`, where `<other>` is the first existing pseudoref in `MERGE_HEAD`, `CHERRY_PICK_HEAD`, `REVERT_HEAD` or `REBASE_HEAD`. Only works when the index has unmerged entries. This option can be used to show relevant commits when resolving conflicts from a 3-way merge. ---boundary:: +`--boundary`:: Output excluded boundary commits. Boundary commits are prefixed with `-`. ifdef::git-rev-list[] ---use-bitmap-index:: +`--use-bitmap-index`:: Try to speed up the traversal using the pack bitmap index (if one is available). Note that when traversing with `--objects`, trees and blobs will not have their associated path printed. ---progress=<header>:: +`--progress=<header>`:: Show progress reports on stderr as objects are considered. The `<header>` text will be printed with each progress update. --z:: +`-z`:: Instead of being newline-delimited, each outputted object and its accompanying metadata is delimited using NUL bytes. Output is printed in the following form: @@ -397,56 +397,56 @@ is how to do it, as there are various strategies to simplify the history. The following options select the commits to be shown: -<paths>:: +`<paths>`:: Commits modifying the given <paths> are selected. ---simplify-by-decoration:: +`--simplify-by-decoration`:: Commits that are referred by some branch or tag are selected. Note that extra commits can be shown to give a meaningful history. The following options affect the way the simplification is performed: -Default mode:: +`Default mode`:: Simplifies the history to the simplest history explaining the final state of the tree. Simplest because it prunes some side branches if the end result is the same (i.e. merging branches with the same content) ---show-pulls:: +`--show-pulls`:: Include all commits from the default mode, but also any merge commits that are not TREESAME to the first parent but are TREESAME to a later parent. This mode is helpful for showing the merge commits that "first introduced" a change to a branch. ---full-history:: +`--full-history`:: Same as the default mode, but does not prune some history. ---dense:: +`--dense`:: Only the selected commits are shown, plus some to have a meaningful history. ---sparse:: +`--sparse`:: All commits in the simplified history are shown. ---simplify-merges:: +`--simplify-merges`:: Additional option to `--full-history` to remove some needless merges from the resulting history, as there are no selected commits contributing to this merge. ---ancestry-path[=<commit>]:: - When given a range of commits to display (e.g. 'commit1..commit2' - or 'commit2 {caret}commit1'), and a commit <commit> in that range, +`--ancestry-path[=<commit>]`:: + When given a range of commits to display (e.g. `<commit1>..<commit2>` + or `<commit2> ^<commit1>`), and a commit _<commit>_ in that range, only display commits in that range - that are ancestors of <commit>, descendants of <commit>, or - <commit> itself. If no commit is specified, use 'commit1' (the - excluded part of the range) as <commit>. Can be passed multiple + that are ancestors of _<commit>_, descendants of _<commit>_, or + _<commit>_ itself. If no commit is specified, use _<commit1>_ (the + excluded part of the range) as _<commit>_. Can be passed multiple times; if so, a commit is included if it is any of the commits given or if it is an ancestor or descendant of one of them. A more detailed explanation follows. -Suppose you specified `foo` as the <paths>. We shall call commits +Suppose you specified `foo` as the _<paths>_. We shall call commits that modify `foo` !TREESAME, and the rest TREESAME. (In a diff filtered for `foo`, they look different and equal, respectively.) @@ -466,22 +466,22 @@ The horizontal line of history A---Q is taken to be the first parent of each merge. The commits are: * `I` is the initial commit, in which `foo` exists with contents - ``asdf'', and a file `quux` exists with contents ``quux''. Initial + `asdf`, and a file `quux` exists with contents `quux`. Initial commits are compared to an empty tree, so `I` is !TREESAME. -* In `A`, `foo` contains just ``foo''. +* In `A`, `foo` contains just `foo`. * `B` contains the same change as `A`. Its merge `M` is trivial and hence TREESAME to all parents. -* `C` does not change `foo`, but its merge `N` changes it to ``foobar'', +* `C` does not change `foo`, but its merge `N` changes it to `foobar`, so it is not TREESAME to any parent. -* `D` sets `foo` to ``baz''. Its merge `O` combines the strings from - `N` and `D` to ``foobarbaz''; i.e., it is not TREESAME to any parent. +* `D` sets `foo` to `baz`. Its merge `O` combines the strings from + `N` and `D` to `foobarbaz`; i.e., it is not TREESAME to any parent. -* `E` changes `quux` to ``xyzzy'', and its merge `P` combines the - strings to ``quux xyzzy''. `P` is TREESAME to `O`, but not to `E`. +* `E` changes `quux` to `xyzzy`, and its merge `P` combines the + strings to `quux xyzzy`. `P` is TREESAME to `O`, but not to `E`. * `X` is an independent root commit that added a new file `side`, and `Y` modified it. `Y` is TREESAME to `X`. Its merge `Q` added `side` to `P`, and @@ -517,7 +517,7 @@ Parent/child relations are only visible with `--parents`, but that does not affect the commits selected in default mode, so we have shown the parent lines. ---full-history without parent rewriting:: +`--full-history` without parent rewriting:: This mode differs from the default in one point: always follow all parents of a merge, even if it is TREESAME to one of them. Even if more than one side of the merge has commits that are @@ -536,7 +536,7 @@ Note that without parent rewriting, it is not really possible to talk about the parent/child relationships between the commits, so we show them disconnected. ---full-history with parent rewriting:: +`--full-history` with parent rewriting:: Ordinary commits are only included if they are !TREESAME (though this can be changed, see `--sparse` below). + @@ -560,18 +560,18 @@ rewritten to contain `E`'s parent `I`. The same happened for `C` and In addition to the above settings, you can change whether TREESAME affects inclusion: ---dense:: +`--dense`:: Commits that are walked are included if they are not TREESAME to any parent. ---sparse:: +`--sparse`:: All commits that are walked are included. + Note that without `--full-history`, this still simplifies merges: if one of the parents is TREESAME, we follow only that one, so the other sides of the merge are never walked. ---simplify-merges:: +`--simplify-merges`:: First, build a history graph in the same way that `--full-history` with parent rewriting does (see above). + @@ -618,9 +618,9 @@ Note the major differences in `N`, `P`, and `Q` over `--full-history`: There is another simplification mode available: ---ancestry-path[=<commit>]:: +`--ancestry-path[=<commit>]`:: Limit the displayed commits to those which are an ancestor of - <commit>, or which are a descendant of <commit>, or are <commit> + _<commit>_, or which are a descendant of _<commit>_, or are _<commit>_ itself. + As an example use case, consider the following commit history: @@ -636,15 +636,15 @@ As an example use case, consider the following commit history: A regular 'D..M' computes the set of commits that are ancestors of `M`, but excludes the ones that are ancestors of `D`. This is useful to see what happened to the history leading to `M` since `D`, in the sense -that ``what does `M` have that did not exist in `D`''. The result in this +that "what does `M` have that did not exist in `D`". The result in this example would be all the commits, except `A` and `B` (and `D` itself, of course). + When we want to find out what commits in `M` are contaminated with the bug introduced by `D` and need fixing, however, we might want to view -only the subset of 'D..M' that are actually descendants of `D`, i.e. +only the subset of `D..M` that are actually descendants of `D`, i.e. excluding `C` and `K`. This is exactly what the `--ancestry-path` -option does. Applied to the 'D..M' range, it results in: +option does. Applied to the `D..M` range, it results in: + ----------------------------------------------------------------------- E-------F @@ -655,7 +655,7 @@ option does. Applied to the 'D..M' range, it results in: ----------------------------------------------------------------------- + We can also use `--ancestry-path=D` instead of `--ancestry-path` which -means the same thing when applied to the 'D..M' range but is just more +means the same thing when applied to the `D..M` range but is just more explicit. + If we instead are interested in a given topic within this range, and all @@ -770,7 +770,7 @@ into the important branch. This commit may have information about why the change `X` came to override the changes from `A` and `B` in its commit message. ---show-pulls:: +`--show-pulls`:: In addition to the commits shown in the default history, show each merge commit that is not TREESAME to its first parent but is TREESAME to a later parent. @@ -819,7 +819,7 @@ ifdef::git-rev-list[] Bisection Helpers ~~~~~~~~~~~~~~~~~ ---bisect:: +`--bisect`:: Limit output to the one commit object which is roughly halfway between included and excluded commits. Note that the bad bisection ref `refs/bisect/bad` is added to the included commits (if it @@ -843,7 +843,7 @@ introduces a regression is thus reduced to a binary search: repeatedly generate and test new 'midpoint's until the commit chain is of length one. ---bisect-vars:: +`--bisect-vars`:: This calculates the same as `--bisect`, except that refs in `refs/bisect/` are not used, and except that this outputs text ready to be eval'ed by the shell. These lines will assign the @@ -855,7 +855,7 @@ one. `bisect_bad`, and the number of commits we are bisecting right now to `bisect_all`. ---bisect-all:: +`--bisect-all`:: This outputs all the commit objects between the included and excluded commits, ordered by their distance to the included and excluded commits. Refs in `refs/bisect/` are not used. The farthest @@ -878,15 +878,15 @@ Commit Ordering By default, the commits are shown in reverse chronological order. ---date-order:: +`--date-order`:: Show no parents before all of its children are shown, but otherwise show commits in the commit timestamp order. ---author-date-order:: +`--author-date-order`:: Show no parents before all of its children are shown, but otherwise show commits in the author timestamp order. ---topo-order:: +`--topo-order`:: Show no parents before all of its children are shown, and avoid showing commits on multiple lines of history intermixed. @@ -910,8 +910,8 @@ With `--topo-order`, they would show 8 6 5 3 7 4 2 1 (or 8 7 4 2 6 5 avoid showing the commits from two parallel development track mixed together. ---reverse:: - Output the commits chosen to be shown (see Commit Limiting +`--reverse`:: + Output the commits chosen to be shown (see 'Commit Limiting' section above) in reverse order. Cannot be combined with `--walk-reflogs`. endif::git-shortlog[] @@ -923,39 +923,39 @@ Object Traversal These options are mostly targeted for packing of Git repositories. ifdef::git-rev-list[] ---objects:: +`--objects`:: Print the object IDs of any object referenced by the listed - commits. `--objects foo ^bar` thus means ``send me + commits. `--objects foo ^bar` thus means "send me all object IDs which I need to download if I have the commit - object _bar_ but not _foo_''. See also `--object-names` below. + object `bar` but not `foo`". See also `--object-names` below. ---in-commit-order:: +`--in-commit-order`:: Print tree and blob ids in order of the commits. The tree and blob ids are printed after they are first referenced by a commit. ---objects-edge:: +`--objects-edge`:: Similar to `--objects`, but also print the IDs of excluded - commits prefixed with a ``-'' character. This is used by + commits prefixed with a "`-`" character. This is used by linkgit:git-pack-objects[1] to build a ``thin'' pack, which records objects in deltified form based on objects contained in these excluded commits to reduce network traffic. ---objects-edge-aggressive:: +`--objects-edge-aggressive`:: Similar to `--objects-edge`, but it tries harder to find excluded commits at the cost of increased time. This is used instead of `--objects-edge` to build ``thin'' packs for shallow repositories. ---indexed-objects:: +`--indexed-objects`:: Pretend as if all trees and blobs used by the index are listed on the command line. Note that you probably want to use `--objects`, too. ---unpacked:: +`--unpacked`:: Only useful with `--objects`; print the object IDs that are not in packs. ---object-names:: +`--object-names`:: Only useful with `--objects`; print the names of the object IDs that are found. This is the default behavior. Note that the "name" of each object is ambiguous, and mostly intended as a @@ -964,52 +964,52 @@ ifdef::git-rev-list[] to remove newlines; and if an object would appear multiple times with different names, only one name is shown. ---no-object-names:: +`--no-object-names`:: Only useful with `--objects`; does not print the names of the object IDs that are found. This inverts `--object-names`. This flag allows the output to be more easily parsed by commands such as linkgit:git-cat-file[1]. ---filter=<filter-spec>:: +`--filter=<filter-spec>`:: Only useful with one of the `--objects*`; omits objects (usually - blobs) from the list of printed objects. The '<filter-spec>' + blobs) from the list of printed objects. The _<filter-spec>_ may be one of the following: + -The form '--filter=blob:none' omits all blobs. +The form `--filter=blob:none` omits all blobs. + -The form '--filter=blob:limit=<n>[kmg]' omits blobs of size at least n -bytes or units. n may be zero. The suffixes k, m, and g can be used -to name units in KiB, MiB, or GiB. For example, 'blob:limit=1k' +The form `--filter=blob:limit=<n>[kmg]` omits blobs of size at least _<n>_ +bytes or units. _<n>_ may be zero. The suffixes `k`, `m`, and `g` can be used +to name units in KiB, MiB, or GiB. For example, `blob:limit=1k` is the same as 'blob:limit=1024'. + -The form '--filter=object:type=(tag|commit|tree|blob)' omits all objects +The form `--filter=object:type=(tag|commit|tree|blob)` omits all objects which are not of the requested type. + -The form '--filter=sparse:oid=<blob-ish>' uses a sparse-checkout -specification contained in the blob (or blob-expression) '<blob-ish>' +The form `--filter=sparse:oid=<blob-ish>` uses a sparse-checkout +specification contained in the blob (or blob-expression) _<blob-ish>_ to omit blobs that would not be required for a sparse checkout on the requested refs. + -The form '--filter=tree:<depth>' omits all blobs and trees whose depth -from the root tree is >= <depth> (minimum depth if an object is located -at multiple depths in the commits traversed). <depth>=0 will not include +The form `--filter=tree:<depth>` omits all blobs and trees whose depth +from the root tree is >= _<depth>_ (minimum depth if an object is located +at multiple depths in the commits traversed). _<depth>_=0 will not include any trees or blobs unless included explicitly in the command-line (or -standard input when --stdin is used). <depth>=1 will include only the +standard input when `--stdin` is used). _<depth>_=1 will include only the tree and blobs which are referenced directly by a commit reachable from -<commit> or an explicitly-given object. <depth>=2 is like <depth>=1 +_<commit>_ or an explicitly-given object. _<depth>_=2 is like <depth>=1 while also including trees and blobs one more level removed from an explicitly-given commit or tree. + -Note that the form '--filter=sparse:path=<path>' that wants to read +Note that the form `--filter=sparse:path=<path>` that wants to read from an arbitrary path on the filesystem has been dropped for security reasons. + -Multiple '--filter=' flags can be specified to combine filters. Only +Multiple `--filter=` flags can be specified to combine filters. Only objects which are accepted by every filter are included. + -The form '--filter=combine:<filter1>+<filter2>+...<filterN>' can also be +The form `--filter=combine:<filter1>+<filter2>+...<filterN>` can also be used to combined several filters, but this is harder than just repeating -the '--filter' flag and is usually not necessary. Filters are joined by +the `--filter` flag and is usually not necessary. Filters are joined by '{plus}' and individual filters are %-encoded (i.e. URL-encoded). Besides the '{plus}' and '%' characters, the following characters are reserved and also must be encoded: `~!@#$^&*()[]{}\;",<>?`+'`+ @@ -1017,52 +1017,52 @@ as well as all characters with ASCII code <= `0x20`, which includes space and newline. + Other arbitrary characters can also be encoded. For instance, -'combine:tree:3+blob:none' and 'combine:tree%3A3+blob%3Anone' are +`combine:tree:3+blob:none` and `combine:tree%3A3+blob%3Anone` are equivalent. ---no-filter:: +`--no-filter`:: Turn off any previous `--filter=` argument. ---filter-provided-objects:: +`--filter-provided-objects`:: Filter the list of explicitly provided objects, which would otherwise always be printed even if they did not match any of the filters. Only useful with `--filter=`. ---filter-print-omitted:: +`--filter-print-omitted`:: Only useful with `--filter=`; prints a list of the objects omitted by the filter. Object IDs are prefixed with a ``~'' character. ---missing=<missing-action>:: +`--missing=<missing-action>`:: A debug option to help with future "partial clone" development. This option specifies how missing objects are handled. + -The form '--missing=error' requests that rev-list stop with an error if +The form `--missing=error` requests that rev-list stop with an error if a missing object is encountered. This is the default action. + -The form '--missing=allow-any' will allow object traversal to continue +The form `--missing=allow-any` will allow object traversal to continue if a missing object is encountered. Missing objects will silently be omitted from the results. + -The form '--missing=allow-promisor' is like 'allow-any', but will only +The form `--missing=allow-promisor` is like `allow-any`, but will only allow object traversal to continue for EXPECTED promisor missing objects. Unexpected missing objects will raise an error. + -The form '--missing=print' is like 'allow-any', but will also print a +The form `--missing=print` is like `allow-any`, but will also print a list of the missing objects. Object IDs are prefixed with a ``?'' character. + -The form '--missing=print-info' is like 'print', but will also print additional +The form `--missing=print-info` is like `print`, but will also print additional information about the missing object inferred from its containing object. The information is all printed on the same line with the missing object ID in the form: `?<oid> [<token>=<value>]...`. The `<token>=<value>` pairs containing -additional information are separated from each other by a SP. The value is -encoded in a token specific fashion, but SP or LF contained in value are always +additional information are separated from each other by a _SP_. The value is +encoded in a token specific fashion, but _SP_ or _LF_ contained in value are always expected to be represented in such a way that the resulting encoded value does not have either of these two problematic bytes. Each `<token>=<value>` may be one of the following: + -- * The `path=<path>` shows the path of the missing object inferred from a - containing object. A path containing SP or special characters is enclosed in + containing object. A path containing _SP_ or special characters is enclosed in double-quotes in the C style as needed. + * The `type=<type>` shows the type of the missing object inferred from a @@ -1073,7 +1073,7 @@ If some tips passed to the traversal are missing, they will be considered as missing too, and the traversal will ignore them. In case we cannot get their Object ID though, an error will be raised. ---exclude-promisor-objects:: +`--exclude-promisor-objects`:: (For internal use only.) Prefilter object traversal at promisor boundary. This is used with partial clone. This is stronger than `--missing=allow-promisor` because it limits the @@ -1081,7 +1081,7 @@ we cannot get their Object ID though, an error will be raised. objects. endif::git-rev-list[] ---no-walk[=(sorted|unsorted)]:: +`--no-walk[=(sorted|unsorted)]`:: Only show the given commits, but do not traverse their ancestors. This has no effect if a range is specified. If the argument `unsorted` is given, the commits are shown in the order they were @@ -1090,7 +1090,7 @@ endif::git-rev-list[] by commit time. Cannot be combined with `--graph`. ---do-walk:: +`--do-walk`:: Overrides a previous `--no-walk`. endif::git-shortlog[] @@ -1100,16 +1100,21 @@ Commit Formatting ifdef::git-rev-list[] Using these options, linkgit:git-rev-list[1] will act similar to the -more specialized family of commit log tools: linkgit:git-log[1], -linkgit:git-show[1], and linkgit:git-whatchanged[1] +more specialized family of commit log tools: +ifndef::with-breaking-changes[] +linkgit:git-log[1], linkgit:git-show[1], and linkgit:git-whatchanged[1]. +endif::with-breaking-changes[] +ifdef::with-breaking-changes[] +linkgit:git-log[1] and linkgit:git-show[1]. +endif::with-breaking-changes[] endif::git-rev-list[] include::pretty-options.adoc[] ---relative-date:: +`--relative-date`:: Synonym for `--date=relative`. ---date=<format>:: +`--date=<format>`:: Only takes effect for dates shown in human-readable format, such as when using `--pretty`. `log.date` config variable sets a default value for the log command's `--date` option. By default, dates @@ -1159,12 +1164,12 @@ omitted. 1970). As with `--raw`, this is always in UTC and therefore `-local` has no effect. -`--date=format:...` feeds the format `...` to your system `strftime`, -except for %s, %z, and %Z, which are handled internally. +`--date=format:<format>` feeds the _<format>_ to your system `strftime`, +except for `%s`, `%z`, and `%Z`, which are handled internally. Use `--date=format:%c` to show the date in your system locale's -preferred format. See the `strftime` manual for a complete list of +preferred format. See the `strftime`(3) manual for a complete list of format placeholders. When using `-local`, the correct syntax is -`--date=format-local:...`. +`--date=format-local:<format>`. `--date=default` is the default format, and is based on ctime(3) output. It shows a single line with three-letter day of the week, @@ -1174,33 +1179,33 @@ the local time zone is used, e.g. `Thu Jan 1 00:00:00 1970 +0000`. -- ifdef::git-rev-list[] ---header:: +`--header`:: Print the contents of the commit in raw-format; each record is separated with a NUL character. ---no-commit-header:: +`--no-commit-header`:: Suppress the header line containing "commit" and the object ID printed before the specified format. This has no effect on the built-in formats; only custom formats are affected. ---commit-header:: +`--commit-header`:: Overrides a previous `--no-commit-header`. endif::git-rev-list[] ---parents:: +`--parents`:: Print also the parents of the commit (in the form "commit parent..."). Also enables parent rewriting, see 'History Simplification' above. ---children:: +`--children`:: Print also the children of the commit (in the form "commit child..."). Also enables parent rewriting, see 'History Simplification' above. ifdef::git-rev-list[] ---timestamp:: +`--timestamp`:: Print the raw commit timestamp. endif::git-rev-list[] ---left-right:: +`--left-right`:: Mark which side of a symmetric difference a commit is reachable from. Commits from the left side are prefixed with `<` and those from the right with `>`. If combined with `--boundary`, those @@ -1229,7 +1234,7 @@ you would get an output like this: -xxxxxxx... 1st on a ----------------------------------------------------------------------- ---graph:: +`--graph`:: Draw a text-based graphical representation of the commit history on the left hand side of the output. This may cause extra lines to be printed in between commits, in order for the graph history @@ -1241,15 +1246,15 @@ This enables parent rewriting, see 'History Simplification' above. This implies the `--topo-order` option by default, but the `--date-order` option may also be specified. ---show-linear-break[=<barrier>]:: - When --graph is not used, all history branches are flattened +`--show-linear-break[=<barrier>]`:: + When `--graph` is not used, all history branches are flattened which can make it hard to see that the two consecutive commits do not belong to a linear branch. This option puts a barrier - in between them in that case. If `<barrier>` is specified, it + in between them in that case. If _<barrier>_ is specified, it is the string that will be shown instead of the default one. ifdef::git-rev-list[] ---count:: +`--count`:: Print a number stating how many commits would have been listed, and suppress all other output. When used together with `--left-right`, instead print the counts for left and diff --git a/Documentation/scalar.adoc b/Documentation/scalar.adoc index 4bd5b150e8..f81b2832f8 100644 --- a/Documentation/scalar.adoc +++ b/Documentation/scalar.adoc @@ -71,7 +71,8 @@ HEAD[:<directory>]`. Instead of checking out the branch pointed to by the cloned repository's HEAD, check out the `<name>` branch instead. ---[no-]single-branch:: +--single-branch:: +--no-single-branch:: Clone only the history leading to the tip of a single branch, either specified by the `--branch` option or the primary branch remote's `HEAD` points at. @@ -81,23 +82,27 @@ remote-tracking branch for the branch this option was used for the initial cloning. If the HEAD at the remote did not point at any branch when `--single-branch` clone was made, no remote-tracking branch is created. ---[no-]src:: +--src:: +--no-src:: By default, `scalar clone` places the cloned repository within a `<entlistment>/src` directory. Use `--no-src` to place the cloned repository directly in the `<enlistment>` directory. ---[no-]tags:: +--tags:: +--no-tags:: By default, `scalar clone` will fetch the tag objects advertised by the remote and future `git fetch` commands will do the same. Use `--no-tags` to avoid fetching tags in `scalar clone` and to configure the repository to avoid fetching tags in the future. To fetch tags after cloning with `--no-tags`, run `git fetch --tags`. ---[no-]full-clone:: +--full-clone:: +--no-full-clone:: A sparse-checkout is initialized by default. This behavior can be turned off via `--full-clone`. ---[no-]maintenance:: +--maintenance:: +--no-maintenance:: By default, `scalar clone` configures the enlistment to use Git's background maintenance feature. Use the `--no-maintenance` to skip this configuration. @@ -122,7 +127,8 @@ Note: when this subcommand is called in a worktree that is called `src/`, its parent directory is considered to be the Scalar enlistment. If the worktree is _not_ called `src/`, it itself will be considered to be the Scalar enlistment. ---[no-]maintenance:: +--maintenance:: +--no-maintenance:: By default, `scalar register` configures the enlistment to use Git's background maintenance feature. Use the `--no-maintenance` to skip this configuration. This does not disable any maintenance that may diff --git a/Documentation/technical/api-path-walk.adoc b/Documentation/technical/api-path-walk.adoc index 3e089211fb..a67de1b143 100644 --- a/Documentation/technical/api-path-walk.adoc +++ b/Documentation/technical/api-path-walk.adoc @@ -39,7 +39,10 @@ It is also important that you do not specify the `--objects` flag for the the objects will be walked in a separate way based on those starting commits. -`commits`, `blobs`, `trees`, `tags`:: +`commits`:: +`blobs`:: +`trees`:: +`tags`:: By default, these members are enabled and signal that the path-walk API should call the `path_fn` on objects of these types. Specialized applications could disable some options to make it simpler to walk @@ -56,6 +59,14 @@ better off using the revision walk API instead. the revision walk so that the walk emits commits marked with the `UNINTERESTING` flag. +`edge_aggressive`:: + For performance reasons, usually only the boundary commits are + explored to find UNINTERESTING objects. However, in the case of + shallow clones it can be helpful to mark all trees and blobs + reachable from UNINTERESTING tip commits as UNINTERESTING. This + matches the behavior of `--objects-edge-aggressive` in the + revision API. + `pl`:: This pattern list pointer allows focusing the path-walk search to a set of patterns, only emitting paths that match the given @@ -69,4 +80,5 @@ Examples See example usages in: `t/helper/test-path-walk.c`, + `builtin/pack-objects.c`, `builtin/backfill.c` diff --git a/Documentation/technical/build-systems.adoc b/Documentation/technical/build-systems.adoc index d9dafb407c..3c5237b9fd 100644 --- a/Documentation/technical/build-systems.adoc +++ b/Documentation/technical/build-systems.adoc @@ -32,7 +32,10 @@ that generally have somebody running test pipelines against regularly: - OpenBSD The platforms which must be supported by the tool should be aligned with our -[platform support policy](platform-support.txt). +platform support policy (see platform-support.adoc). +// once we lose AsciiDoc compatibility, we can start writing the above as: +// xref:platform-support.adoc#platform-support-policy[platform support policy] +// or something like that, but until then.... === Auto-detection of supported features diff --git a/Documentation/technical/long-running-process-protocol.adoc b/Documentation/technical/long-running-process-protocol.adoc index 6f33654b42..39bd89d467 100644 --- a/Documentation/technical/long-running-process-protocol.adoc +++ b/Documentation/technical/long-running-process-protocol.adoc @@ -24,6 +24,7 @@ After the version negotiation Git sends a list of all capabilities that it supports and a flush packet. Git expects to read a list of desired capabilities, which must be a subset of the supported capabilities list, and a flush packet as response: + ------------------------ packet: git> git-filter-client packet: git> version=2 diff --git a/Documentation/technical/sparse-checkout.adoc b/Documentation/technical/sparse-checkout.adoc index 8202172b70..0f750ef3e3 100644 --- a/Documentation/technical/sparse-checkout.adoc +++ b/Documentation/technical/sparse-checkout.adoc @@ -440,7 +440,7 @@ understanding these differences can be beneficial. * blame (only matters when one or more -C flags are passed) * and annotate * log - * whatchanged + * whatchanged (may not exist anymore) * ls-files * diff-index * diff-tree diff --git a/Documentation/user-manual.adoc b/Documentation/user-manual.adoc index d2b478ad23..7696987117 100644 --- a/Documentation/user-manual.adoc +++ b/Documentation/user-manual.adoc @@ -4240,7 +4240,7 @@ command `git`. The source side of a builtin is - an entry in `BUILTIN_OBJECTS` in the `Makefile`. Sometimes, more than one builtin is contained in one source file. For -example, `cmd_whatchanged()` and `cmd_log()` both reside in `builtin/log.c`, +example, `cmd_show()` and `cmd_log()` both reside in `builtin/log.c`, since they share quite a bit of code. In that case, the commands which are _not_ named like the `.c` file in which they live have to be listed in `BUILT_INS` in the `Makefile`. @@ -4270,7 +4270,7 @@ So, look into `builtin/cat-file.c`, search for `cmd_cat_file()` and look what it does. ------------------------------------------------------------------ - git_config(git_default_config); + repo_config(the_repository, git_default_config); if (argc != 3) usage("git cat-file [-t|-s|-e|-p|<type>] <sha1>"); if (get_sha1(argv[2], sha1)) @@ -4301,11 +4301,11 @@ Now, for the meat: ----------------------------------------------------------------------------- case 0: - buf = read_object_with_reference(sha1, argv[1], &size, NULL); + buf = odb_read_object_peeled(r->objects, sha1, argv[1], &size, NULL); ----------------------------------------------------------------------------- This is how you read a blob (actually, not only a blob, but any type of -object). To know how the function `read_object_with_reference()` actually +object). To know how the function `odb_read_object_peeled()` actually works, find the source code for it (something like `git grep read_object_with | grep ":[a-z]"` in the Git repository), and read the source. diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 2df7e186c9..b16db85e77 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,6 +1,6 @@ #!/bin/sh -DEF_VER=v2.50.1 +DEF_VER=v2.51.GIT LF=' ' @@ -114,8 +114,6 @@ include shared.mak # # Define NO_INTPTR_T if you don't have intptr_t or uintptr_t. # -# Define NO_UINTMAX_T if you don't have uintmax_t. -# # Define NEEDS_SOCKET if linking with libc is not enough (SunOS, # Patrick Mauritz). # @@ -1085,8 +1083,8 @@ LIB_OBJS += notes.o LIB_OBJS += object-file-convert.o LIB_OBJS += object-file.o LIB_OBJS += object-name.o -LIB_OBJS += object-store.o LIB_OBJS += object.o +LIB_OBJS += odb.o LIB_OBJS += oid-array.o LIB_OBJS += oidmap.o LIB_OBJS += oidset.o @@ -1267,6 +1265,7 @@ BUILTIN_OBJS += builtin/hook.o BUILTIN_OBJS += builtin/index-pack.o BUILTIN_OBJS += builtin/init-db.o BUILTIN_OBJS += builtin/interpret-trailers.o +BUILTIN_OBJS += builtin/last-modified.o BUILTIN_OBJS += builtin/log.o BUILTIN_OBJS += builtin/ls-files.o BUILTIN_OBJS += builtin/ls-remote.o @@ -1308,6 +1307,7 @@ BUILTIN_OBJS += builtin/remote.o BUILTIN_OBJS += builtin/repack.o BUILTIN_OBJS += builtin/replace.o BUILTIN_OBJS += builtin/replay.o +BUILTIN_OBJS += builtin/repo.o BUILTIN_OBJS += builtin/rerere.o BUILTIN_OBJS += builtin/reset.o BUILTIN_OBJS += builtin/rev-list.o @@ -1356,6 +1356,7 @@ THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/% THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/% CLAR_TEST_SUITES += u-ctype +CLAR_TEST_SUITES += u-dir CLAR_TEST_SUITES += u-example-decorate CLAR_TEST_SUITES += u-hash CLAR_TEST_SUITES += u-hashmap @@ -1364,29 +1365,28 @@ CLAR_TEST_SUITES += u-oid-array CLAR_TEST_SUITES += u-oidmap CLAR_TEST_SUITES += u-oidtree CLAR_TEST_SUITES += u-prio-queue +CLAR_TEST_SUITES += u-reftable-basics +CLAR_TEST_SUITES += u-reftable-block +CLAR_TEST_SUITES += u-reftable-merged +CLAR_TEST_SUITES += u-reftable-pq +CLAR_TEST_SUITES += u-reftable-readwrite +CLAR_TEST_SUITES += u-reftable-stack +CLAR_TEST_SUITES += u-reftable-table CLAR_TEST_SUITES += u-reftable-tree CLAR_TEST_SUITES += u-strbuf CLAR_TEST_SUITES += u-strcmp-offset +CLAR_TEST_SUITES += u-string-list CLAR_TEST_SUITES += u-strvec CLAR_TEST_SUITES += u-trailer CLAR_TEST_SUITES += u-urlmatch-normalization CLAR_TEST_PROG = $(UNIT_TEST_BIN)/unit-tests$(X) CLAR_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(CLAR_TEST_SUITES)) CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/clar/clar.o -CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/lib-oid.o +CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/lib-reftable.o +CLAR_TEST_OBJS += $(UNIT_TEST_DIR)/unit-test.o -UNIT_TEST_PROGRAMS += t-reftable-basics -UNIT_TEST_PROGRAMS += t-reftable-block -UNIT_TEST_PROGRAMS += t-reftable-merged -UNIT_TEST_PROGRAMS += t-reftable-pq -UNIT_TEST_PROGRAMS += t-reftable-readwrite -UNIT_TEST_PROGRAMS += t-reftable-record -UNIT_TEST_PROGRAMS += t-reftable-stack -UNIT_TEST_PROGRAMS += t-reftable-table -UNIT_TEST_PROGS = $(patsubst %,$(UNIT_TEST_BIN)/%$X,$(UNIT_TEST_PROGRAMS)) UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/test-lib.o -UNIT_TEST_OBJS += $(UNIT_TEST_DIR)/lib-reftable.o # xdiff and reftable libs may in turn depend on what is in libgit.a GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB) $(REFTABLE_LIB) $(LIB_FILE) @@ -1918,9 +1918,6 @@ endif ifdef NO_INTPTR_T COMPAT_CFLAGS += -DNO_INTPTR_T endif -ifdef NO_UINTMAX_T - BASIC_CFLAGS += -Duintmax_t=uint32_t -endif ifdef NO_SOCKADDR_STORAGE ifdef NO_IPV6 BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in @@ -3473,11 +3470,14 @@ endif coccicheck-test: $(COCCI_TEST_RES_GEN) coccicheck: coccicheck-test + ifdef SPATCH_CONCAT_COCCI -coccicheck: contrib/coccinelle/ALL.cocci.patch +COCCICHECK_PATCH_MUST_BE_EMPTY_FILES = contrib/coccinelle/ALL.cocci.patch else -coccicheck: $(COCCICHECK_PATCHES_INTREE) +COCCICHECK_PATCH_MUST_BE_EMPTY_FILES = $(COCCICHECK_PATCHES_INTREE) endif +coccicheck: $(COCCICHECK_PATCH_MUST_BE_EMPTY_FILES) + ! grep -q ^ $(COCCICHECK_PATCH_MUST_BE_EMPTY_FILES) /dev/null # See contrib/coccinelle/README coccicheck-pending: coccicheck-test @@ -3946,13 +3946,12 @@ unit-tests: $(UNIT_TEST_PROGS) $(CLAR_TEST_PROG) t/helper/test-tool$X $(MAKE) -C t/ unit-tests .PHONY: libgit-sys libgit-rs -libgit-sys libgit-rs: - $(QUIET)(\ - cd contrib/$@ && \ - cargo build \ - ) +libgit-sys: + $(QUIET)cargo build --manifest-path contrib/libgit-sys/Cargo.toml +libgit-rs: libgit-sys + $(QUIET)cargo build --manifest-path contrib/libgit-rs/Cargo.toml ifdef INCLUDE_LIBGIT_RS -all:: libgit-sys libgit-rs +all:: libgit-rs endif LIBGIT_PUB_OBJS += contrib/libgit-sys/public_symbol_export.o @@ -1 +1 @@ -Documentation/RelNotes/2.50.1.adoc
\ No newline at end of file +Documentation/RelNotes/2.52.0.adoc
\ No newline at end of file diff --git a/add-interactive.c b/add-interactive.c index 97ff35b6f1..4604c69140 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -20,14 +20,14 @@ #include "prompt.h" #include "tree.h" -static void init_color(struct repository *r, struct add_i_state *s, +static void init_color(struct repository *r, int 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 (!s->use_color) + if (!use_color) dst[0] = '\0'; else if (repo_config_get_value(r, key, &value) || color_parse(value, dst)) @@ -36,39 +36,63 @@ static void init_color(struct repository *r, struct add_i_state *s, free(key); } -void init_add_i_state(struct add_i_state *s, struct repository *r) +static int check_color_config(struct repository *r, const char *var) { const char *value; + int ret; - s->r = r; - - if (repo_config_get_value(r, "color.interactive", &value)) - s->use_color = -1; + if (repo_config_get_value(r, var, &value)) + ret = -1; else - s->use_color = - git_config_colorbool("color.interactive", value); - s->use_color = want_color(s->use_color); - - init_color(r, s, "interactive.header", s->header_color, GIT_COLOR_BOLD); - init_color(r, s, "interactive.help", s->help_color, GIT_COLOR_BOLD_RED); - init_color(r, s, "interactive.prompt", s->prompt_color, - GIT_COLOR_BOLD_BLUE); - init_color(r, s, "interactive.error", s->error_color, - GIT_COLOR_BOLD_RED); - - init_color(r, s, "diff.frag", s->fraginfo_color, - diff_get_color(s->use_color, DIFF_FRAGINFO)); - init_color(r, s, "diff.context", s->context_color, "fall back"); - if (!strcmp(s->context_color, "fall back")) - init_color(r, s, "diff.plain", s->context_color, - diff_get_color(s->use_color, DIFF_CONTEXT)); - init_color(r, s, "diff.old", s->file_old_color, - diff_get_color(s->use_color, DIFF_FILE_OLD)); - init_color(r, s, "diff.new", s->file_new_color, - diff_get_color(s->use_color, DIFF_FILE_NEW)); + 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 < 0 && !repo_config_get_value(r, "color.ui", &value)) + ret = git_config_colorbool("color.ui", value); - strlcpy(s->reset_color, - s->use_color ? GIT_COLOR_RESET : "", COLOR_MAXLEN); + return want_color(ret); +} + +void init_add_i_state(struct add_i_state *s, struct repository *r, + struct add_p_opt *add_p_opt) +{ + s->r = r; + s->context = -1; + s->interhunkcontext = -1; + + 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, + 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, + s->use_color_diff ? GIT_COLOR_RESET : "", COLOR_MAXLEN); FREE_AND_NULL(s->interactive_diff_filter); repo_config_get_string(r, "interactive.difffilter", @@ -78,9 +102,27 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) 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; + } } void clear_add_i_state(struct add_i_state *s) @@ -88,7 +130,8 @@ void clear_add_i_state(struct add_i_state *s) FREE_AND_NULL(s->interactive_diff_filter); FREE_AND_NULL(s->interactive_diff_algorithm); memset(s, 0, sizeof(*s)); - s->use_color = -1; + s->use_color_interactive = -1; + s->use_color_diff = -1; } /* @@ -969,6 +1012,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, + }; struct strvec args = STRVEC_INIT; struct pathspec ps_selected = { 0 }; @@ -979,7 +1026,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, NULL, &ps_selected); + res = run_add_p(s->r, ADD_P_ADD, &add_p_opt, NULL, &ps_selected); strvec_clear(&args); clear_pathspec(&ps_selected); } @@ -1014,10 +1061,13 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps, if (count > 0) { struct child_process cmd = CHILD_PROCESS_INIT; - strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", - oid_to_hex(!is_initial ? &oid : - s->r->hash_algo->empty_tree), - "--", NULL); + 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); + 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++) if (files->selected[i]) strvec_push(&cmd.args, @@ -1110,7 +1160,8 @@ static void command_prompt_help(struct add_i_state *s) _("(empty) select nothing")); } -int run_add_i(struct repository *r, const struct pathspec *ps) +int run_add_i(struct repository *r, const struct pathspec *ps, + struct add_p_opt *add_p_opt) { struct add_i_state s = { NULL }; struct print_command_item_data data = { "[", "]" }; @@ -1153,15 +1204,15 @@ int run_add_i(struct repository *r, const struct pathspec *ps) ->util = util; } - init_add_i_state(&s, r); + init_add_i_state(&s, r, add_p_opt); /* * When color was asked for, use the prompt color for * highlighting, otherwise use square brackets. */ - if (s.use_color) { + if (s.use_color_interactive) { data.color = s.prompt_color; - data.reset = s.reset_color; + data.reset = s.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 693f125e8e..ceadfa6bb6 100644 --- a/add-interactive.h +++ b/add-interactive.h @@ -3,29 +3,42 @@ #include "color.h" +struct add_p_opt { + int context; + int interhunkcontext; +}; + +#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1 } + struct add_i_state { struct repository *r; - int use_color; + int use_color_interactive; + int 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[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; }; -void init_add_i_state(struct add_i_state *s, struct repository *r); +void init_add_i_state(struct add_i_state *s, struct repository *r, + struct add_p_opt *add_p_opt); 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); +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, @@ -36,6 +49,7 @@ enum add_p_mode { }; int run_add_p(struct repository *r, enum add_p_mode mode, - const char *revision, const struct pathspec *ps); + struct add_p_opt *o, const char *revision, + const struct pathspec *ps); #endif diff --git a/add-patch.c b/add-patch.c index 95c67d8c80..b0389c5d5b 100644 --- a/add-patch.c +++ b/add-patch.c @@ -300,7 +300,7 @@ static void err(struct add_p_state *s, const char *fmt, ...) va_start(args, fmt); fputs(s->s.error_color, stdout); vprintf(fmt, args); - puts(s->s.reset_color); + puts(s->s.reset_color_interactive); va_end(args); } @@ -414,7 +414,6 @@ static int normalize_marker(const char *p) static int parse_diff(struct add_p_state *s, const struct pathspec *ps) { struct strvec args = STRVEC_INIT; - const char *diff_algorithm = s->s.interactive_diff_algorithm; struct strbuf *plain = &s->plain, *colored = NULL; struct child_process cp = CHILD_PROCESS_INIT; char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0'; @@ -424,8 +423,12 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) int res; strvec_pushv(&args, s->mode->diff_cmd); - if (diff_algorithm) - strvec_pushf(&args, "--diff-algorithm=%s", diff_algorithm); + 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->revision) { struct object_id oid; strvec_push(&args, @@ -454,7 +457,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) } strbuf_complete_line(plain); - if (want_color_fd(1, -1)) { + if (want_color_fd(1, s->s.use_color_diff)) { struct child_process colored_cp = CHILD_PROCESS_INIT; const char *diff_filter = s->s.interactive_diff_filter; @@ -711,7 +714,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); + strbuf_addf(out, "%s\n", s->s.reset_color_diff); else strbuf_addch(out, '\n'); } @@ -1104,7 +1107,7 @@ static void recolor_hunk(struct add_p_state *s, struct hunk *hunk) s->s.file_new_color : s->s.context_color); strbuf_add(&s->colored, plain + current, eol - current); - strbuf_addstr(&s->colored, s->s.reset_color); + strbuf_addstr(&s->colored, s->s.reset_color_diff); if (next > eol) strbuf_add(&s->colored, plain + eol, next - eol); current = next; @@ -1525,8 +1528,8 @@ static int patch_update_file(struct add_p_state *s, : 1)); printf(_(s->mode->prompt_mode[prompt_mode_type]), s->buf.buf); - if (*s->s.reset_color) - fputs(s->s.reset_color, stdout); + if (*s->s.reset_color_interactive) + fputs(s->s.reset_color_interactive, stdout); fflush(stdout); if (read_single_character(s) == EOF) break; @@ -1760,14 +1763,15 @@ soft_increment: } int run_add_p(struct repository *r, enum add_p_mode mode, - const char *revision, const struct pathspec *ps) + struct add_p_opt *o, const char *revision, + const struct pathspec *ps) { struct add_p_state s = { { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; size_t i, binary_count = 0; - init_add_i_state(&s.s, r); + init_add_i_state(&s.s, r, o); if (mode == ADD_P_STASH) s.mode = &patch_mode_stash; @@ -18,7 +18,7 @@ enum advice_type { ADVICE_AM_WORK_DIR, ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME, ADVICE_COMMIT_BEFORE_MERGE, - ADVICE_DEFAULT_BRANCH_NAME, + ADVICE_DEFAULT_BRANCH_NAME, /* To be retired sometime after Git 3.0 */ ADVICE_DETACHED_HEAD, ADVICE_DIVERGING, ADVICE_FETCH_SET_HEAD_WARN, @@ -36,19 +36,25 @@ struct alloc_state { int slab_nr, slab_alloc; }; -struct alloc_state *allocate_alloc_state(void) +struct alloc_state *alloc_state_alloc(void) { return xcalloc(1, sizeof(struct alloc_state)); } -void clear_alloc_state(struct alloc_state *s) +void alloc_state_free_and_null(struct alloc_state **s_) { + struct alloc_state *s = *s_; + + if (!s) + return; + while (s->slab_nr > 0) { s->slab_nr--; free(s->slabs[s->slab_nr]); } FREE_AND_NULL(s->slabs); + FREE_AND_NULL(*s_); } static inline void *alloc_node(struct alloc_state *s, size_t node_size) @@ -14,7 +14,7 @@ void *alloc_commit_node(struct repository *r); void *alloc_tag_node(struct repository *r); void *alloc_object_node(struct repository *r); -struct alloc_state *allocate_alloc_state(void); -void clear_alloc_state(struct alloc_state *s); +struct alloc_state *alloc_state_alloc(void); +void alloc_state_free_and_null(struct alloc_state **s_); #endif @@ -14,7 +14,7 @@ #include "abspath.h" #include "base85.h" #include "config.h" -#include "object-store.h" +#include "odb.h" #include "delta.h" #include "diff.h" #include "dir.h" @@ -48,9 +48,9 @@ struct gitdiff_data { static void git_apply_config(void) { - git_config_get_string("apply.whitespace", &apply_default_whitespace); - git_config_get_string("apply.ignorewhitespace", &apply_default_ignorewhitespace); - git_config(git_xmerge_config, NULL); + repo_config_get_string(the_repository, "apply.whitespace", &apply_default_whitespace); + repo_config_get_string(the_repository, "apply.ignorewhitespace", &apply_default_ignorewhitespace); + repo_config(the_repository, git_xmerge_config, NULL); } static int parse_whitespace_option(struct apply_state *state, const char *option) @@ -3204,14 +3204,14 @@ static int apply_binary(struct apply_state *state, return 0; /* deletion patch */ } - if (has_object(the_repository, &oid, 0)) { + if (odb_has_object(the_repository->objects, &oid, 0)) { /* We already have the postimage */ enum object_type type; unsigned long size; char *result; - result = repo_read_object_file(the_repository, &oid, &type, - &size); + result = odb_read_object(the_repository->objects, &oid, + &type, &size); if (!result) return error(_("the necessary postimage %s for " "'%s' cannot be read"), @@ -3273,8 +3273,8 @@ static int read_blob_object(struct strbuf *buf, const struct object_id *oid, uns unsigned long sz; char *result; - result = repo_read_object_file(the_repository, oid, &type, - &sz); + result = odb_read_object(the_repository->objects, oid, + &type, &sz); if (!result) return -1; /* XXX read_sha1_file NUL-terminates */ @@ -3503,7 +3503,7 @@ static int resolve_to(struct image *image, const struct object_id *result_id) image_clear(image); - data = repo_read_object_file(the_repository, result_id, &type, &size); + data = odb_read_object(the_repository->objects, result_id, &type, &size); if (!data || type != OBJ_BLOB) die("unable to read blob object %s", oid_to_hex(result_id)); strbuf_attach(&image->buf, data, size, size + 1); @@ -3621,7 +3621,7 @@ static int try_threeway(struct apply_state *state, /* Preimage the patch was prepared for */ if (patch->is_new) - write_object_file("", 0, OBJ_BLOB, &pre_oid); + odb_write_object(the_repository->objects, "", 0, OBJ_BLOB, &pre_oid); else if (repo_get_oid(the_repository, patch->old_oid_prefix, &pre_oid) || read_blob_object(&buf, &pre_oid, patch->old_mode)) return error(_("repository lacks the necessary blob to perform 3-way merge.")); @@ -3637,7 +3637,8 @@ static int try_threeway(struct apply_state *state, return -1; } /* post_oid is theirs */ - write_object_file(tmp_image.buf.buf, tmp_image.buf.len, OBJ_BLOB, &post_oid); + odb_write_object(the_repository->objects, tmp_image.buf.buf, + tmp_image.buf.len, OBJ_BLOB, &post_oid); image_clear(&tmp_image); /* our_oid is ours */ @@ -3650,7 +3651,8 @@ static int try_threeway(struct apply_state *state, return error(_("cannot read the current contents of '%s'"), patch->old_name); } - write_object_file(tmp_image.buf.buf, tmp_image.buf.len, OBJ_BLOB, &our_oid); + odb_write_object(the_repository->objects, tmp_image.buf.buf, + tmp_image.buf.len, OBJ_BLOB, &our_oid); image_clear(&tmp_image); /* in-core three-way merge between post and our using pre as base */ @@ -4360,7 +4362,8 @@ static int add_index_file(struct apply_state *state, } fill_stat_cache_info(state->repo->index, ce, &st); } - if (write_object_file(buf, size, OBJ_BLOB, &ce->oid) < 0) { + if (odb_write_object(the_repository->objects, buf, size, + OBJ_BLOB, &ce->oid) < 0) { discard_cache_entry(ce); return error(_("unable to create backing store " "for newly created file %s"), path); @@ -4565,7 +4568,7 @@ static int create_file(struct apply_state *state, struct patch *patch) if (patch->conflicted_threeway) return add_conflicted_stages_file(state, patch); - else if (state->update_index) + else if (state->check_index || (state->ita_only && patch->is_new > 0)) return add_index_file(state, path, mode, buf, size); return 0; } @@ -4833,7 +4836,7 @@ static int apply_patch(struct apply_state *state, LOCK_DIE_ON_ERROR); } - if (state->check_index && read_apply_cache(state) < 0) { + if ((state->check_index || state->update_index) && read_apply_cache(state) < 0) { error(_("unable to read index file")); res = -128; goto end; diff --git a/archive-tar.c b/archive-tar.c index 282b48196f..73b63ddc41 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -11,7 +11,7 @@ #include "hex.h" #include "tar.h" #include "archive.h" -#include "object-store.h" +#include "odb.h" #include "strbuf.h" #include "streaming.h" #include "run-command.h" @@ -537,7 +537,7 @@ void init_tar_archiver(void) tar_filter_config("tar.tgz.remote", "true", NULL); tar_filter_config("tar.tar.gz.command", internal_gzip_command, NULL); tar_filter_config("tar.tar.gz.remote", "true", NULL); - git_config(git_tar_config, NULL); + repo_config(the_repository, git_tar_config, NULL); for (i = 0; i < nr_tar_filters; i++) { /* omit any filters that never had a command configured */ if (tar_filters[i]->filter_command) diff --git a/archive-zip.c b/archive-zip.c index 405da6f3d8..bea5bdd43d 100644 --- a/archive-zip.c +++ b/archive-zip.c @@ -12,7 +12,7 @@ #include "hex.h" #include "streaming.h" #include "utf8.h" -#include "object-store.h" +#include "odb.h" #include "strbuf.h" #include "userdiff.h" #include "write-or-die.h" @@ -492,14 +492,22 @@ static int write_zip_entry(struct archiver_args *args, zstream.next_in = buf; zstream.avail_in = 0; - result = git_deflate(&zstream, Z_FINISH); - if (result != Z_STREAM_END) - die("deflate error (%d)", result); + + do { + result = git_deflate(&zstream, Z_FINISH); + if (result != Z_OK && result != Z_STREAM_END) + die("deflate error (%d)", result); + + out_len = zstream.next_out - compressed; + if (out_len > 0) { + write_or_die(1, compressed, out_len); + compressed_size += out_len; + zstream.next_out = compressed; + zstream.avail_out = sizeof(compressed); + } + } while (result != Z_STREAM_END); git_deflate_end(&zstream); - out_len = zstream.next_out - compressed; - write_or_die(1, compressed, out_len); - compressed_size += out_len; zip_offset += compressed_size; write_zip_data_desc(size, compressed_size, crc); @@ -632,7 +640,7 @@ static int write_zip_archive(const struct archiver *ar UNUSED, { int err; - git_config(archive_zip_config, NULL); + repo_config(the_repository, archive_zip_config, NULL); dos_time(&args->time, &zip_date, &zip_time); @@ -14,7 +14,7 @@ #include "pretty.h" #include "setup.h" #include "refs.h" -#include "object-store.h" +#include "odb.h" #include "commit.h" #include "tree.h" #include "tree-walk.h" @@ -98,7 +98,7 @@ static void *object_file_to_archive(const struct archiver_args *args, (args->tree ? &args->tree->object.oid : NULL), oid); path += args->baselen; - buffer = repo_read_object_file(the_repository, oid, type, sizep); + buffer = odb_read_object(the_repository->objects, oid, type, sizep); if (buffer && S_ISREG(mode)) { struct strbuf buf = STRBUF_INIT; size_t size = 0; @@ -215,7 +215,7 @@ static int write_archive_entry(const struct object_id *oid, const char *base, /* Stream it? */ if (S_ISREG(mode) && !args->convert && - oid_object_info(args->repo, oid, &size) == OBJ_BLOB && + odb_read_object_info(args->repo->objects, oid, &size) == OBJ_BLOB && size > repo_settings_get_big_file_threshold(the_repository)) return write_entry(args, oid, path.buf, path.len, mode, NULL, size); @@ -760,8 +760,8 @@ int write_archive(int argc, const char **argv, const char *prefix, const char **argv_copy; int rc; - git_config_get_bool("uploadarchive.allowunreachable", &remote_allow_unreachable); - git_config(git_default_config, NULL); + repo_config_get_bool(the_repository, "uploadarchive.allowunreachable", &remote_allow_unreachable); + repo_config(the_repository, git_default_config, NULL); describe_status.max_invocations = 1; ctx.date_mode.type = DATE_NORMAL; @@ -22,7 +22,7 @@ #include "read-cache-ll.h" #include "refs.h" #include "revision.h" -#include "object-store.h" +#include "odb.h" #include "setup.h" #include "thread-utils.h" #include "tree-walk.h" @@ -779,7 +779,7 @@ static struct attr_stack *read_attr_from_blob(struct index_state *istate, if (get_tree_entry(istate->repo, tree_oid, path, &oid, &mode)) return NULL; - buf = repo_read_object_file(istate->repo, &oid, &type, &sz); + buf = odb_read_object(istate->repo->objects, &oid, &type, &sz); if (!buf || type != OBJ_BLOB) { free(buf); return NULL; @@ -20,7 +20,7 @@ #include "commit-slab.h" #include "commit-reach.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "path.h" #include "dir.h" @@ -155,9 +155,9 @@ static void show_list(const char *debug, int counted, int nr, unsigned commit_flags = commit->object.flags; enum object_type type; unsigned long size; - char *buf = repo_read_object_file(the_repository, - &commit->object.oid, &type, - &size); + char *buf = odb_read_object(the_repository->objects, + &commit->object.oid, &type, + &size); const char *subject_start; int subject_len; @@ -27,14 +27,6 @@ struct commit_list *filter_skipped(struct commit_list *list, #define FIND_BISECTION_ALL (1u<<0) #define FIND_BISECTION_FIRST_PARENT_ONLY (1u<<1) -struct rev_list_info { - struct rev_info *revs; - int flags; - int show_timestamp; - int hdr_termination; - const char *header_prefix; -}; - /* * enum bisect_error represents the following return codes: * BISECT_OK: success code. Internally, it means that next @@ -3,7 +3,7 @@ #include "git-compat-util.h" #include "refs.h" -#include "object-store.h" +#include "odb.h" #include "cache-tree.h" #include "mergesort.h" #include "commit.h" @@ -116,7 +116,7 @@ static void verify_working_tree_path(struct repository *r, unsigned short mode; if (!get_tree_entry(r, commit_oid, path, &blob_oid, &mode) && - oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB) + odb_read_object_info(r->objects, &blob_oid, NULL) == OBJ_BLOB) return; } @@ -277,7 +277,8 @@ static struct commit *fake_working_tree_commit(struct repository *r, convert_to_git(r->index, path, buf.buf, buf.len, &buf, 0); origin->file.ptr = buf.buf; origin->file.size = buf.len; - pretend_object_file(the_repository, buf.buf, buf.len, OBJ_BLOB, &origin->blob_oid); + odb_pretend_object(the_repository->objects, buf.buf, buf.len, + OBJ_BLOB, &origin->blob_oid); /* * Read the current index, replace the path entry with @@ -1041,9 +1042,9 @@ static void fill_origin_blob(struct diff_options *opt, &o->blob_oid, 1, &file->ptr, &file_size)) ; else - file->ptr = repo_read_object_file(the_repository, - &o->blob_oid, &type, - &file_size); + file->ptr = odb_read_object(the_repository->objects, + &o->blob_oid, &type, + &file_size); file->size = file_size; if (!file->ptr) @@ -1245,7 +1246,7 @@ static int fill_blob_sha1_and_mode(struct repository *r, return 0; if (get_tree_entry(r, &origin->commit->object.oid, origin->path, &origin->blob_oid, &origin->mode)) goto error_out; - if (oid_object_info(r, &origin->blob_oid, NULL) != OBJ_BLOB) + if (odb_read_object_info(r->objects, &origin->blob_oid, NULL) != OBJ_BLOB) goto error_out; return 0; error_out: @@ -1310,7 +1311,7 @@ static void add_bloom_key(struct blame_bloom_data *bd, } bd->keys[bd->nr] = xmalloc(sizeof(struct bloom_key)); - fill_bloom_key(path, strlen(path), bd->keys[bd->nr], bd->settings); + bloom_key_fill(bd->keys[bd->nr], path, strlen(path), bd->settings); bd->nr++; } @@ -2869,10 +2870,9 @@ void setup_scoreboard(struct blame_scoreboard *sb, &sb->final_buf_size)) ; else - sb->final_buf = repo_read_object_file(the_repository, - &o->blob_oid, - &type, - &sb->final_buf_size); + sb->final_buf = odb_read_object(the_repository->objects, + &o->blob_oid, &type, + &sb->final_buf_size); if (!sb->final_buf) die(_("cannot read blob %s for path %s"), @@ -107,7 +107,7 @@ int load_bloom_filter_from_graph(struct commit_graph *g, * Not considered to be cryptographically secure. * Implemented as described in https://en.wikipedia.org/wiki/MurmurHash#Algorithm */ -uint32_t murmur3_seeded_v2(uint32_t seed, const char *data, size_t len) +static uint32_t murmur3_seeded_v2(uint32_t seed, const char *data, size_t len) { const uint32_t c1 = 0xcc9e2d51; const uint32_t c2 = 0x1b873593; @@ -221,9 +221,7 @@ static uint32_t murmur3_seeded_v1(uint32_t seed, const char *data, size_t len) return seed; } -void fill_bloom_key(const char *data, - size_t len, - struct bloom_key *key, +void bloom_key_fill(struct bloom_key *key, const char *data, size_t len, const struct bloom_filter_settings *settings) { int i; @@ -243,7 +241,7 @@ void fill_bloom_key(const char *data, key->hashes[i] = hash0 + i * hash1; } -void clear_bloom_key(struct bloom_key *key) +void bloom_key_clear(struct bloom_key *key) { FREE_AND_NULL(key->hashes); } @@ -280,6 +278,55 @@ void deinit_bloom_filters(void) deep_clear_bloom_filter_slab(&bloom_filters, free_one_bloom_filter); } +struct bloom_keyvec *bloom_keyvec_new(const char *path, size_t len, + const struct bloom_filter_settings *settings) +{ + struct bloom_keyvec *vec; + const char *p; + size_t sz; + size_t nr = 1; + + p = path; + while (*p) { + /* + * At this point, the path is normalized to use Unix-style + * path separators. This is required due to how the + * changed-path Bloom filters store the paths. + */ + if (*p == '/') + nr++; + p++; + } + + sz = sizeof(struct bloom_keyvec); + sz += nr * sizeof(struct bloom_key); + vec = (struct bloom_keyvec *)xcalloc(1, sz); + if (!vec) + return NULL; + vec->count = nr; + + bloom_key_fill(&vec->key[0], path, len, settings); + nr = 1; + p = path + len - 1; + while (p > path) { + if (*p == '/') { + bloom_key_fill(&vec->key[nr++], path, p - path, settings); + } + p--; + } + assert(nr == vec->count); + return vec; +} + +void bloom_keyvec_free(struct bloom_keyvec *vec) +{ + if (!vec) + return; + for (size_t nr = 0; nr < vec->count; nr++) + bloom_key_clear(&vec->key[nr]); + free(vec); +} + static int pathmap_cmp(const void *hashmap_cmp_fn_data UNUSED, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, @@ -500,9 +547,9 @@ struct bloom_filter *get_or_compute_bloom_filter(struct repository *r, hashmap_for_each_entry(&pathmap, &iter, e, entry) { struct bloom_key key; - fill_bloom_key(e->path, strlen(e->path), &key, settings); + bloom_key_fill(&key, e->path, strlen(e->path), settings); add_key_to_filter(&key, filter, settings); - clear_bloom_key(&key); + bloom_key_clear(&key); } cleanup: @@ -540,3 +587,26 @@ int bloom_filter_contains(const struct bloom_filter *filter, return 1; } + +int bloom_filter_contains_vec(const struct bloom_filter *filter, + const struct bloom_keyvec *vec, + const struct bloom_filter_settings *settings) +{ + int ret = 1; + + for (size_t nr = 0; ret > 0 && nr < vec->count; nr++) + ret = bloom_filter_contains(filter, &vec->key[nr], settings); + + return ret; +} + +uint32_t test_bloom_murmur3_seeded(uint32_t seed, const char *data, size_t len, + int version) +{ + assert(version == 1 || version == 2); + + if (version == 2) + return murmur3_seeded_v2(seed, data, len); + else + return murmur3_seeded_v1(seed, data, len); +} @@ -74,24 +74,40 @@ struct bloom_key { uint32_t *hashes; }; +/* + * A bloom_keyvec is a vector of bloom_keys, which + * can be used to store multiple keys for a single + * pathspec item. + */ +struct bloom_keyvec { + size_t count; + struct bloom_key key[FLEX_ARRAY]; +}; + int load_bloom_filter_from_graph(struct commit_graph *g, struct bloom_filter *filter, uint32_t graph_pos); +void bloom_key_fill(struct bloom_key *key, const char *data, size_t len, + const struct bloom_filter_settings *settings); +void bloom_key_clear(struct bloom_key *key); + /* - * Calculate the murmur3 32-bit hash value for the given data - * using the given seed. - * Produces a uniformly distributed hash value. - * Not considered to be cryptographically secure. - * Implemented as described in https://en.wikipedia.org/wiki/MurmurHash#Algorithm + * bloom_keyvec_new - Allocate and populate a bloom_keyvec with keys for the + * given path. + * + * This function splits the input path by '/' and generates a bloom key for each + * prefix, in reverse order of specificity. For example, given the input + * "a/b/c", it will generate bloom keys for: + * - "a/b/c" + * - "a/b" + * - "a" + * + * The resulting keys are stored in a newly allocated bloom_keyvec. */ -uint32_t murmur3_seeded_v2(uint32_t seed, const char *data, size_t len); - -void fill_bloom_key(const char *data, - size_t len, - struct bloom_key *key, - const struct bloom_filter_settings *settings); -void clear_bloom_key(struct bloom_key *key); +struct bloom_keyvec *bloom_keyvec_new(const char *path, size_t len, + const struct bloom_filter_settings *settings); +void bloom_keyvec_free(struct bloom_keyvec *vec); void add_key_to_filter(const struct bloom_key *key, struct bloom_filter *filter, @@ -137,4 +153,18 @@ int bloom_filter_contains(const struct bloom_filter *filter, const struct bloom_key *key, const struct bloom_filter_settings *settings); +/* + * bloom_filter_contains_vec - Check if all keys in a key vector are in the + * Bloom filter. + * + * Returns 1 if **all** keys in the vector are present in the filter, + * 0 if **any** key is not present. + */ +int bloom_filter_contains_vec(const struct bloom_filter *filter, + const struct bloom_keyvec *v, + const struct bloom_filter_settings *settings); + +uint32_t test_bloom_murmur3_seeded(uint32_t seed, const char *data, size_t len, + int version); + #endif @@ -116,7 +116,7 @@ static int install_branch_config_multiple_remotes(int flag, const char *local, } strbuf_addf(&key, "branch.%s.remote", local); - if (git_config_set_gently(key.buf, origin ? origin : ".") < 0) + if (repo_config_set_gently(the_repository, key.buf, origin ? origin : ".") < 0) goto out_err; strbuf_reset(&key); @@ -127,16 +127,16 @@ static int install_branch_config_multiple_remotes(int flag, const char *local, * more than one is provided, use CONFIG_REGEX_NONE to preserve what * we've written so far. */ - if (git_config_set_gently(key.buf, NULL) < 0) + if (repo_config_set_gently(the_repository, key.buf, NULL) < 0) goto out_err; for_each_string_list_item(item, remotes) - if (git_config_set_multivar_gently(key.buf, item->string, CONFIG_REGEX_NONE, 0) < 0) + if (repo_config_set_multivar_gently(the_repository, key.buf, item->string, CONFIG_REGEX_NONE, 0) < 0) goto out_err; if (rebasing) { strbuf_reset(&key); strbuf_addf(&key, "branch.%s.rebase", local); - if (git_config_set_gently(key.buf, "true") < 0) + if (repo_config_set_gently(the_repository, key.buf, "true") < 0) goto out_err; } strbuf_release(&key); @@ -230,7 +230,7 @@ static int inherit_tracking(struct tracking *tracking, const char *orig_ref) return -1; } - if (branch->merge_nr < 1 || !branch->merge_name || !branch->merge_name[0]) { + if (branch->merge_nr < 1 || !branch->merge || !branch->merge[0] || !branch->merge[0]->src) { warning(_("asked to inherit tracking from '%s', but no merge configuration is set"), bare_ref); return -1; @@ -238,7 +238,7 @@ static int inherit_tracking(struct tracking *tracking, const char *orig_ref) tracking->remote = branch->remote_name; for (i = 0; i < branch->merge_nr; i++) - string_list_append(tracking->srcs, branch->merge_name[i]); + string_list_append(tracking->srcs, branch->merge[i]->src); return 0; } @@ -355,7 +355,7 @@ int read_branch_desc(struct strbuf *buf, const char *branch_name) char *v = NULL; struct strbuf name = STRBUF_INIT; strbuf_addf(&name, "branch.%s.description", branch_name); - if (git_config_get_string(name.buf, &v)) { + if (repo_config_get_string(the_repository, name.buf, &v)) { strbuf_release(&name); return -1; } @@ -176,6 +176,7 @@ int cmd_hook(int argc, const char **argv, const char *prefix, struct repository int cmd_index_pack(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_init_db(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_interpret_trailers(int argc, const char **argv, const char *prefix, struct repository *repo); +int cmd_last_modified(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_log_reflog(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_log(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_ls_files(int argc, const char **argv, const char *prefix, struct repository *repo); @@ -216,6 +217,7 @@ int cmd_remote_ext(int argc, const char **argv, const char *prefix, struct repos int cmd_remote_fd(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_repack(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_replay(int argc, const char **argv, const char *prefix, struct repository *repo); +int cmd_repo(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_rerere(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_reset(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_restore(int argc, const char **argv, const char *prefix, struct repository *repo); diff --git a/builtin/add.c b/builtin/add.c index 7c292ffdc6..740c7c4581 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -7,6 +7,7 @@ #include "builtin.h" #include "advice.h" #include "config.h" +#include "environment.h" #include "lockfile.h" #include "editor.h" #include "dir.h" @@ -29,6 +30,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 int take_worktree_changes; static int add_renormalize; static int pathspec_file_nul; @@ -157,7 +159,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) + int patch, struct add_p_opt *add_p_opt) { struct pathspec pathspec; int ret; @@ -169,9 +171,9 @@ int interactive_add(struct repository *repo, prefix, argv); if (patch) - ret = !!run_add_p(repo, ADD_P_ADD, NULL, &pathspec); + ret = !!run_add_p(repo, ADD_P_ADD, add_p_opt, NULL, &pathspec); else - ret = !!run_add_i(repo, &pathspec); + ret = !!run_add_i(repo, &pathspec, add_p_opt); clear_pathspec(&pathspec); return ret; @@ -253,6 +255,8 @@ 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_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.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")), @@ -385,6 +389,7 @@ int cmd_add(int argc, char *seen = NULL; char *ps_matched = NULL; struct lock_file lock_file = LOCK_INIT; + struct odb_transaction *transaction; repo_config(repo, add_config, NULL); @@ -394,6 +399,11 @@ int cmd_add(int argc, prepare_repo_settings(repo); repo->settings.command_requires_full_index = 0; + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + if (patch_interactive) add_interactive = 1; if (add_interactive) { @@ -401,7 +411,12 @@ 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)); + exit(interactive_add(repo, argv + 1, prefix, patch_interactive, &add_p_opt)); + } else { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch"); } if (edit_interactive) { @@ -560,7 +575,7 @@ int cmd_add(int argc, string_list_clear(&only_match_skip_worktree, 0); } - begin_odb_transaction(); + transaction = begin_odb_transaction(repo->objects); ps_matched = xcalloc(pathspec.nr, 1); if (add_renormalize) @@ -579,7 +594,7 @@ int cmd_add(int argc, if (chmod_arg && pathspec.nr) exit_status |= chmod_pathspec(repo, &pathspec, chmod_arg[0], show_only); - end_odb_transaction(); + end_odb_transaction(transaction); finish: if (write_locked_index(repo->index, &lock_file, diff --git a/builtin/am.c b/builtin/am.c index e32a3b4c97..6073d64ae9 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -162,18 +162,18 @@ static void am_state_init(struct am_state *state) state->prec = 4; - git_config_get_bool("am.threeway", &state->threeway); + repo_config_get_bool(the_repository, "am.threeway", &state->threeway); state->utf8 = 1; - git_config_get_bool("am.messageid", &state->message_id); + repo_config_get_bool(the_repository, "am.messageid", &state->message_id); state->scissors = SCISSORS_UNSET; state->quoted_cr = quoted_cr_unset; strvec_init(&state->git_apply_opts); - if (!git_config_get_bool("commit.gpgsign", &gpgsign)) + if (!repo_config_get_bool(the_repository, "commit.gpgsign", &gpgsign)) state->sign_commit = gpgsign ? "" : NULL; } @@ -965,7 +965,7 @@ static int split_mail(struct am_state *state, enum patch_format patch_format, { if (keep_cr < 0) { keep_cr = 0; - git_config_get_bool("am.keepcr", &keep_cr); + repo_config_get_bool(the_repository, "am.keepcr", &keep_cr); } switch (patch_format) { @@ -1000,7 +1000,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format, if (!patch_format) { fprintf_ln(stderr, _("Patch format detection failed.")); - exit(128); + die(NULL); } if (mkdir(state->dir, 0777) < 0 && errno != EEXIST) @@ -1178,7 +1178,7 @@ static void NORETURN die_user_resolve(const struct am_state *state) strbuf_release(&sb); } - exit(128); + die(NULL); } /** @@ -2406,6 +2406,7 @@ int cmd_am(int argc, .type = OPTION_CALLBACK, .long_name = "show-current-patch", .value = &resume_mode, + .precision = sizeof(resume_mode), .argh = "(diff|raw)", .help = N_("show the patch being applied"), .flags = PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, @@ -2444,7 +2445,7 @@ int cmd_am(int argc, show_usage_with_options_if_asked(argc, argv, usage, options); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); am_state_init(&state); diff --git a/builtin/apply.c b/builtin/apply.c index a1e20c593d..d642a40251 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -29,7 +29,7 @@ int cmd_apply(int argc, * cf. https://lore.kernel.org/git/xmqqcypfcmn4.fsf@gitster.g/ */ if (!the_hash_algo) - repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + repo_set_hash_algo(the_repository, GIT_HASH_DEFAULT); argc = apply_parse_options(argc, argv, &state, &force_apply, &options, diff --git a/builtin/backfill.c b/builtin/backfill.c index fa82ad2f6f..80056abe47 100644 --- a/builtin/backfill.c +++ b/builtin/backfill.c @@ -13,7 +13,7 @@ #include "tree.h" #include "tree-walk.h" #include "object.h" -#include "object-store.h" +#include "odb.h" #include "oid-array.h" #include "oidset.h" #include "promisor-remote.h" @@ -67,8 +67,8 @@ static int fill_missing_blobs(const char *path UNUSED, return 0; for (size_t i = 0; i < list->nr; i++) { - if (!has_object(ctx->repo, &list->oid[i], - OBJECT_INFO_FOR_PREFETCH)) + if (!odb_has_object(ctx->repo->objects, &list->oid[i], + OBJECT_INFO_FOR_PREFETCH)) oid_array_append(&ctx->current_batch, &list->oid[i]); } diff --git a/builtin/blame.c b/builtin/blame.c index 944952e30e..2703820258 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -28,7 +28,7 @@ #include "line-log.h" #include "progress.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "pager.h" #include "blame.h" #include "refs.h" @@ -197,9 +197,7 @@ static void commit_info_destroy(struct commit_info *ci) strbuf_release(&ci->summary); } -static void get_commit_info(struct commit *commit, - struct commit_info *ret, - int detailed) +static void get_commit_info(struct commit *commit, struct commit_info *ret) { int len; const char *subject, *encoding; @@ -211,11 +209,6 @@ static void get_commit_info(struct commit *commit, &ret->author, &ret->author_mail, &ret->author_time, &ret->author_tz); - if (!detailed) { - repo_unuse_commit_buffer(the_repository, commit, message); - return; - } - get_ac_line(message, "\ncommitter ", &ret->committer, &ret->committer_mail, &ret->committer_time, &ret->committer_tz); @@ -263,7 +256,7 @@ static int emit_one_suspect_detail(struct blame_origin *suspect, int repeat) return 0; suspect->commit->object.flags |= METAINFO_SHOWN; - get_commit_info(suspect->commit, &ci, 1); + get_commit_info(suspect->commit, &ci); printf("author %s\n", ci.author.buf); printf("author-mail %s\n", ci.author_mail.buf); printf("author-time %"PRItime"\n", ci.author_time); @@ -420,7 +413,7 @@ static void parse_color_fields(const char *s) colorfield_nr = 0; /* Ideally this would be stripped and split at the same time? */ - string_list_split(&l, s, ',', -1); + string_list_split(&l, s, ",", -1); ALLOC_GROW(colorfield, colorfield_nr + 1, colorfield_alloc); for_each_string_list_item(item, &l) { @@ -471,7 +464,7 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP); const char *default_color = NULL, *color = NULL, *reset = NULL; - get_commit_info(suspect->commit, &ci, 1); + get_commit_info(suspect->commit, &ci); oid_to_hex_r(hex, &suspect->commit->object.oid); cp = blame_nth_line(sb, ent->lno); @@ -665,7 +658,7 @@ static void find_alignment(struct blame_scoreboard *sb, int *option) if (!(suspect->commit->object.flags & METAINFO_SHOWN)) { struct commit_info ci = COMMIT_INFO_INIT; suspect->commit->object.flags |= METAINFO_SHOWN; - get_commit_info(suspect->commit, &ci, 1); + get_commit_info(suspect->commit, &ci); if (*option & OUTPUT_SHOW_EMAIL) num = utf8_strwidth(ci.author_mail.buf); else @@ -837,7 +830,7 @@ static int is_a_rev(const char *name) if (repo_get_oid(the_repository, name, &oid)) return 0; - return OBJ_NONE < oid_object_info(the_repository, &oid, NULL); + return OBJ_NONE < odb_read_object_info(the_repository->objects, &oid, NULL); } static int peel_to_commit_oid(struct object_id *oid_ret, void *cbdata) @@ -848,7 +841,7 @@ static int peel_to_commit_oid(struct object_id *oid_ret, void *cbdata) oidcpy(&oid, oid_ret); while (1) { struct object *obj; - int kind = oid_object_info(r, &oid, NULL); + int kind = odb_read_object_info(r->objects, &oid, NULL); if (kind == OBJ_COMMIT) { oidcpy(oid_ret, &oid); return 0; @@ -947,7 +940,7 @@ int cmd_blame(int argc, const char *const *opt_usage = cmd_is_annotate ? annotate_opt_usage : blame_opt_usage; setup_default_color_by_age(); - git_config(git_blame_config, &output_option); + repo_config(the_repository, git_blame_config, &output_option); repo_init_revisions(the_repository, &revs, NULL); revs.date_mode = blame_date_mode; revs.diffopt.flags.allow_textconv = 1; diff --git a/builtin/branch.c b/builtin/branch.c index c150131bd9..fa5ced452e 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -699,7 +699,7 @@ static int edit_branch_description(const char *branch_name) strbuf_addf(&name, "branch.%s.description", branch_name); if (buf.len || exists) - git_config_set(name.buf, buf.len ? buf.buf : NULL); + repo_config_set(the_repository, name.buf, buf.len ? buf.buf : NULL); strbuf_release(&name); strbuf_release(&buf); @@ -791,7 +791,7 @@ int cmd_branch(int argc, * Try to set sort keys from config. If config does not set any, * fall back on default (refname) sorting. */ - git_config(git_branch_config, &sorting_options); + repo_config(the_repository, git_branch_config, &sorting_options); if (!sorting_options.nr) string_list_append(&sorting_options, "refname"); @@ -987,10 +987,10 @@ int cmd_branch(int argc, strbuf_reset(&buf); strbuf_addf(&buf, "branch.%s.remote", branch->name); - git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); + repo_config_set_multivar(the_repository, buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); strbuf_reset(&buf); strbuf_addf(&buf, "branch.%s.merge", branch->name); - git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); + repo_config_set_multivar(the_repository, buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); strbuf_release(&buf); } else if (!noncreate_actions && argc > 0 && argc <= 2) { const char *branch_name = argv[0]; diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 67a5ff2b9e..fce0b06451 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -24,7 +24,7 @@ #include "pack-bitmap.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "replace-object.h" #include "promisor-remote.h" #include "mailmap.h" @@ -74,7 +74,7 @@ static int filter_object(const char *path, unsigned mode, { enum object_type type; - *buf = repo_read_object_file(the_repository, oid, &type, size); + *buf = odb_read_object(the_repository->objects, oid, &type, size); if (!*buf) return error(_("cannot read object %s '%s'"), oid_to_hex(oid), path); @@ -132,7 +132,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) switch (opt) { case 't': oi.typep = &type; - if (oid_object_info_extended(the_repository, &oid, &oi, flags) < 0) + if (odb_read_object_info_extended(the_repository->objects, &oid, &oi, flags) < 0) die("git cat-file: could not get object info"); printf("%s\n", type_name(type)); ret = 0; @@ -146,7 +146,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) oi.contentp = (void**)&buf; } - if (oid_object_info_extended(the_repository, &oid, &oi, flags) < 0) + if (odb_read_object_info_extended(the_repository->objects, &oid, &oi, flags) < 0) die("git cat-file: could not get object info"); if (use_mailmap && (type == OBJ_COMMIT || type == OBJ_TAG)) { @@ -160,8 +160,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) goto cleanup; case 'e': - ret = !has_object(the_repository, &oid, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR); + ret = !odb_has_object(the_repository->objects, &oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR); goto cleanup; case 'w': @@ -180,7 +180,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) /* else fallthrough */ case 'p': - type = oid_object_info(the_repository, &oid, NULL); + type = odb_read_object_info(the_repository->objects, &oid, NULL); if (type < 0) die("Not a valid object name %s", obj_name); @@ -197,8 +197,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) ret = stream_blob(&oid); goto cleanup; } - buf = repo_read_object_file(the_repository, &oid, &type, - &size); + buf = odb_read_object(the_repository->objects, &oid, + &type, &size); if (!buf) die("Cannot read object %s", obj_name); @@ -217,11 +217,10 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) if (exp_type_id == OBJ_BLOB) { struct object_id blob_oid; - if (oid_object_info(the_repository, &oid, NULL) == OBJ_TAG) { - char *buffer = repo_read_object_file(the_repository, - &oid, - &type, - &size); + if (odb_read_object_info(the_repository->objects, + &oid, NULL) == OBJ_TAG) { + char *buffer = odb_read_object(the_repository->objects, + &oid, &type, &size); const char *target; if (!buffer) @@ -235,7 +234,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) } else oidcpy(&blob_oid, &oid); - if (oid_object_info(the_repository, &blob_oid, NULL) == OBJ_BLOB) { + if (odb_read_object_info(the_repository->objects, + &blob_oid, NULL) == OBJ_BLOB) { ret = stream_blob(&blob_oid); goto cleanup; } @@ -246,8 +246,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) * fall-back to the usual case. */ } - buf = read_object_with_reference(the_repository, &oid, - exp_type_id, &size, NULL); + buf = odb_read_object_peeled(the_repository->objects, &oid, + exp_type_id, &size, NULL); if (use_mailmap) { size_t s = size; @@ -275,6 +275,7 @@ struct expand_data { struct object_id oid; enum object_type type; unsigned long size; + unsigned short mode; off_t disk_size; const char *rest; struct object_id delta_base_oid; @@ -294,7 +295,7 @@ struct expand_data { /* * After a mark_query run, this object_info is set up to be - * passed to oid_object_info_extended. It will point to the data + * passed to odb_read_object_info_extended. It will point to the data * elements above, so you can retrieve the response from there. */ struct object_info info; @@ -306,6 +307,7 @@ struct expand_data { */ unsigned skip_object_info : 1; }; +#define EXPAND_DATA_INIT { .mode = S_IFINVALID } static int is_atom(const char *atom, const char *s, int slen) { @@ -345,6 +347,9 @@ static int expand_atom(struct strbuf *sb, const char *atom, int len, else strbuf_addstr(sb, oid_to_hex(&data->delta_base_oid)); + } else if (is_atom("objectmode", atom, len)) { + if (!data->mark_query && !(S_IFINVALID == data->mode)) + strbuf_addf(sb, "%06o", data->mode); } else return 0; return 1; @@ -401,10 +406,8 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d if (!textconv_object(the_repository, data->rest, 0100644, oid, 1, &contents, &size)) - contents = repo_read_object_file(the_repository, - oid, - &type, - &size); + contents = odb_read_object(the_repository->objects, + oid, &type, &size); if (!contents) die("could not convert '%s' %s", oid_to_hex(oid), data->rest); @@ -421,8 +424,8 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d unsigned long size; void *contents; - contents = repo_read_object_file(the_repository, oid, &type, - &size); + contents = odb_read_object(the_repository->objects, oid, + &type, &size); if (!contents) die("object %s disappeared", oid_to_hex(oid)); @@ -484,14 +487,17 @@ static void batch_object_write(const char *obj_name, data->info.sizep = &data->size; if (pack) - ret = packed_object_info(the_repository, pack, offset, - &data->info); + ret = packed_object_info(the_repository, pack, + offset, &data->info); else - ret = oid_object_info_extended(the_repository, - &data->oid, &data->info, - OBJECT_INFO_LOOKUP_REPLACE); + ret = odb_read_object_info_extended(the_repository->objects, + &data->oid, &data->info, + OBJECT_INFO_LOOKUP_REPLACE); if (ret < 0) { - report_object_status(opt, obj_name, &data->oid, "missing"); + if (data->mode == S_IFGITLINK) + report_object_status(opt, oid_to_hex(&data->oid), &data->oid, "submodule"); + else + report_object_status(opt, obj_name, &data->oid, "missing"); return; } @@ -531,8 +537,8 @@ static void batch_object_write(const char *obj_name, size_t s = data->size; char *buf = NULL; - buf = repo_read_object_file(the_repository, &data->oid, &data->type, - &data->size); + buf = odb_read_object(the_repository->objects, &data->oid, + &data->type, &data->size); if (!buf) die(_("unable to read %s"), oid_to_hex(&data->oid)); buf = replace_idents_using_mailmap(buf, &s); @@ -613,6 +619,7 @@ static void batch_one_object(const char *obj_name, goto out; } + data->mode = ctx.mode; batch_object_write(obj_name, scratch, opt, data, NULL, 0); out: @@ -841,7 +848,7 @@ static void batch_each_object(struct batch_options *opt, }; struct bitmap_index *bitmap = prepare_bitmap_git(the_repository); - for_each_loose_object(batch_one_object_loose, &payload, 0); + for_each_loose_object(the_repository->objects, batch_one_object_loose, &payload, 0); if (bitmap && !for_each_bitmapped_object(bitmap, &opt->objects_filter, batch_one_object_bitmapped, &payload)) { @@ -866,16 +873,15 @@ static int batch_objects(struct batch_options *opt) { struct strbuf input = STRBUF_INIT; struct strbuf output = STRBUF_INIT; - struct expand_data data; + struct expand_data data = EXPAND_DATA_INIT; int save_warning; int retval = 0; /* * Expand once with our special mark_query flag, which will prime the - * object_info to be handed to oid_object_info_extended for each + * object_info to be handed to odb_read_object_info_extended for each * object. */ - memset(&data, 0, sizeof(data)); data.mark_query = 1; expand_format(&output, opt->format ? opt->format : DEFAULT_FORMAT, @@ -1089,7 +1095,7 @@ int cmd_cat_file(int argc, OPT_END() }; - git_config(git_cat_file_config, NULL); + repo_config(the_repository, git_cat_file_config, NULL); batch.buffer_output = -1; diff --git a/builtin/check-attr.c b/builtin/check-attr.c index 7cf275b893..51ed48ce43 100644 --- a/builtin/check-attr.c +++ b/builtin/check-attr.c @@ -119,7 +119,7 @@ int cmd_check_attr(int argc, if (!is_bare_repository()) setup_work_tree(); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, check_attr_options, check_attr_usage, PARSE_OPT_KEEP_DASHDASH); diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c index 7b7831d13a..644c9a414f 100644 --- a/builtin/check-ignore.c +++ b/builtin/check-ignore.c @@ -2,6 +2,7 @@ #include "builtin.h" #include "config.h" #include "dir.h" +#include "environment.h" #include "gettext.h" #include "quote.h" #include "pathspec.h" @@ -159,7 +160,7 @@ int cmd_check_ignore(int argc, int num_ignored; struct dir_struct dir = DIR_INIT; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, check_ignore_options, check_ignore_usage, 0); diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c index be2cebe121..9cc5c59830 100644 --- a/builtin/check-mailmap.c +++ b/builtin/check-mailmap.c @@ -1,6 +1,7 @@ #define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "ident.h" #include "mailmap.h" @@ -56,7 +57,7 @@ int cmd_check_mailmap(int argc, int i; struct string_list mailmap = STRING_LIST_INIT_NODUP; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, check_mailmap_options, check_mailmap_usage, 0); if (argc == 0 && !use_stdin) diff --git a/builtin/checkout--worker.c b/builtin/checkout--worker.c index da9345a44b..e0772b718b 100644 --- a/builtin/checkout--worker.c +++ b/builtin/checkout--worker.c @@ -4,6 +4,7 @@ #include "builtin.h" #include "config.h" #include "entry.h" +#include "environment.h" #include "gettext.h" #include "parallel-checkout.h" #include "parse-options.h" @@ -132,7 +133,7 @@ int cmd_checkout__worker(int argc, checkout_worker_usage, checkout_worker_options); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, checkout_worker_options, checkout_worker_usage, 0); if (argc > 0) diff --git a/builtin/checkout-index.c b/builtin/checkout-index.c index 7f74bc702f..188128aebd 100644 --- a/builtin/checkout-index.c +++ b/builtin/checkout-index.c @@ -9,6 +9,7 @@ #include "builtin.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "lockfile.h" #include "quote.h" diff --git a/builtin/checkout.c b/builtin/checkout.c index d185982f3a..f9453473fe 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -20,7 +20,7 @@ #include "merge-ort-wrappers.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "parse-options.h" #include "path.h" #include "preload-index.h" @@ -61,6 +61,8 @@ static const char * const restore_usage[] = { struct checkout_opts { int patch_mode; + int patch_context; + int patch_interhunk_context; int quiet; int merge; int force; @@ -104,7 +106,12 @@ struct checkout_opts { struct tree *source_tree; }; -#define CHECKOUT_OPTS_INIT { .conflict_style = -1, .merge = -1 } +#define CHECKOUT_OPTS_INIT { \ + .conflict_style = -1, \ + .merge = -1, \ + .patch_context = -1, \ + .patch_interhunk_context = -1, \ +} struct branch_info { char *name; /* The short name used */ @@ -291,7 +298,7 @@ static int checkout_merged(int pos, const struct checkout *state, read_mmblob(&ours, &threeway[1]); read_mmblob(&theirs, &threeway[2]); - git_config_get_bool("merge.renormalize", &renormalize); + repo_config_get_bool(the_repository, "merge.renormalize", &renormalize); ll_opts.renormalize = renormalize; ll_opts.conflict_style = conflict_style; merge_status = ll_merge(&result_buf, path, &ancestor, "base", @@ -320,7 +327,7 @@ static int checkout_merged(int pos, const struct checkout *state, * (it also writes the merge result to the object database even * when it may contain conflicts). */ - if (write_object_file(result_buf.ptr, result_buf.size, OBJ_BLOB, &oid)) + if (odb_write_object(the_repository->objects, result_buf.ptr, result_buf.size, OBJ_BLOB, &oid)) die(_("Unable to add merge result for '%s'"), path); free(result_buf.ptr); ce = make_transient_cache_entry(mode, &oid, path, 2, ce_mem_pool); @@ -539,6 +546,10 @@ 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 = { + .context = opts->patch_context, + .interhunkcontext = opts->patch_interhunk_context, + }; const char *rev = new_branch_info->name; char rev_oid[GIT_MAX_HEXSZ + 1]; @@ -564,8 +575,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, rev, - &opts->pathspec); + return !!run_add_p(the_repository, patch_mode, &add_p_opt, + rev, &opts->pathspec); } repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); @@ -838,7 +849,7 @@ static int merge_working_tree(const struct checkout_opts *opts, init_tree_desc(&trees[0], &tree->object.oid, tree->buffer, tree->size); if (parse_tree(new_tree) < 0) - exit(128); + die(NULL); tree = new_tree; init_tree_desc(&trees[1], &tree->object.oid, tree->buffer, tree->size); @@ -913,7 +924,7 @@ static int merge_working_tree(const struct checkout_opts *opts, work, old_tree); if (ret < 0) - exit(128); + die(NULL); ret = reset_tree(new_tree, opts, 0, writeout_error, new_branch_info); @@ -1738,6 +1749,8 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts, N_("checkout their version for unmerged files"), 3, PARSE_OPT_NONEG), OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")), + OPT_DIFF_UNIFIED(&opts->patch_context), + OPT_DIFF_INTERHUNK_CONTEXT(&opts->patch_interhunk_context), OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree, N_("do not limit pathspecs to sparse entries only")), OPT_PATHSPEC_FROM_FILE(&opts->pathspec_from_file), @@ -1764,7 +1777,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix, opts->prefix = prefix; opts->show_progress = -1; - git_config(git_checkout_config, opts); + repo_config(the_repository, git_checkout_config, opts); if (the_repository->gitdir) { prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; @@ -1780,6 +1793,18 @@ static int checkout_main(int argc, const char **argv, const char *prefix, argc = parse_options(argc, argv, prefix, options, usagestr, parseopt_flags); + if (opts->patch_context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (opts->patch_interhunk_context < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + + if (!opts->patch_mode) { + if (opts->patch_context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (opts->patch_interhunk_context != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } + if (opts->show_progress < 0) { if (opts->quiet) opts->show_progress = 0; diff --git a/builtin/clean.c b/builtin/clean.c index 053c94fc6b..38b67923a6 100644 --- a/builtin/clean.c +++ b/builtin/clean.c @@ -13,6 +13,7 @@ #include "abspath.h" #include "config.h" #include "dir.h" +#include "environment.h" #include "gettext.h" #include "parse-options.h" #include "path.h" @@ -477,43 +478,39 @@ static int find_unique(const char *choice, struct menu_stuff *menu_stuff) */ static int parse_choice(struct menu_stuff *menu_stuff, int is_single, - struct strbuf input, + char *input, int **chosen) { - struct strbuf **choice_list, **ptr; + struct string_list choice = STRING_LIST_INIT_NODUP; + struct string_list_item *item; int nr = 0; int i; - if (is_single) { - choice_list = strbuf_split_max(&input, '\n', 0); - } else { - char *p = input.buf; - do { - if (*p == ',') - *p = ' '; - } while (*p++); - choice_list = strbuf_split_max(&input, ' ', 0); - } + string_list_split_in_place_f(&choice, input, + is_single ? "\n" : ", ", -1, + STRING_LIST_SPLIT_TRIM); - for (ptr = choice_list; *ptr; ptr++) { - char *p; - int choose = 1; + for_each_string_list_item(item, &choice) { + const char *string; + int choose; int bottom = 0, top = 0; int is_range, is_number; - strbuf_trim(*ptr); - if (!(*ptr)->len) + string = item->string; + if (!*string) continue; /* Input that begins with '-'; unchoose */ - if (*(*ptr)->buf == '-') { + if (string[0] == '-') { choose = 0; - strbuf_remove((*ptr), 0, 1); + string++; + } else { + choose = 1; } is_range = 0; is_number = 1; - for (p = (*ptr)->buf; *p; p++) { + for (const char *p = string; *p; p++) { if ('-' == *p) { if (!is_range) { is_range = 1; @@ -531,27 +528,27 @@ static int parse_choice(struct menu_stuff *menu_stuff, } if (is_number) { - bottom = atoi((*ptr)->buf); + bottom = atoi(string); top = bottom; } else if (is_range) { - bottom = atoi((*ptr)->buf); + bottom = atoi(string); /* a range can be specified like 5-7 or 5- */ - if (!*(strchr((*ptr)->buf, '-') + 1)) + if (!*(strchr(string, '-') + 1)) top = menu_stuff->nr; else - top = atoi(strchr((*ptr)->buf, '-') + 1); - } else if (!strcmp((*ptr)->buf, "*")) { + top = atoi(strchr(string, '-') + 1); + } else if (!strcmp(string, "*")) { bottom = 1; top = menu_stuff->nr; } else { - bottom = find_unique((*ptr)->buf, menu_stuff); + bottom = find_unique(string, menu_stuff); top = bottom; } if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > top || (is_single && bottom != top)) { clean_print_color(CLEAN_COLOR_ERROR); - printf(_("Huh (%s)?\n"), (*ptr)->buf); + printf(_("Huh (%s)?\n"), string); clean_print_color(CLEAN_COLOR_RESET); continue; } @@ -560,7 +557,7 @@ static int parse_choice(struct menu_stuff *menu_stuff, (*chosen)[i-1] = choose; } - strbuf_list_free(choice_list); + string_list_clear(&choice, 0); for (i = 0; i < menu_stuff->nr; i++) nr += (*chosen)[i]; @@ -630,7 +627,7 @@ static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff) nr = parse_choice(stuff, opts->flags & MENU_OPTS_SINGLETON, - choice, + choice.buf, &chosen); if (opts->flags & MENU_OPTS_SINGLETON) { @@ -678,12 +675,13 @@ static int filter_by_patterns_cmd(void) { struct dir_struct dir = DIR_INIT; struct strbuf confirm = STRBUF_INIT; - struct strbuf **ignore_list; - struct string_list_item *item; struct pattern_list *pl; int changed = -1, i; for (;;) { + struct string_list ignore_list = STRING_LIST_INIT_NODUP; + struct string_list_item *item; + if (!del_list.nr) break; @@ -701,14 +699,15 @@ static int filter_by_patterns_cmd(void) break; pl = add_pattern_list(&dir, EXC_CMDL, "manual exclude"); - ignore_list = strbuf_split_max(&confirm, ' ', 0); - for (i = 0; ignore_list[i]; i++) { - strbuf_trim(ignore_list[i]); - if (!ignore_list[i]->len) - continue; + string_list_split_in_place_f(&ignore_list, confirm.buf, " ", -1, + STRING_LIST_SPLIT_TRIM); - add_pattern(ignore_list[i]->buf, "", 0, pl, -(i+1)); + for (i = 0; i < ignore_list.nr; i++) { + item = &ignore_list.items[i]; + if (!*item->string) + continue; + add_pattern(item->string, "", 0, pl, -(i+1)); } changed = 0; @@ -729,7 +728,7 @@ static int filter_by_patterns_cmd(void) clean_print_color(CLEAN_COLOR_RESET); } - strbuf_list_free(ignore_list); + string_list_clear(&ignore_list, 0); dir_clear(&dir); } @@ -949,7 +948,7 @@ int cmd_clean(int argc, OPT_END() }; - git_config(git_clean_config, NULL); + repo_config(the_repository, git_clean_config, NULL); argc = parse_options(argc, argv, prefix, options, builtin_clean_usage, 0); diff --git a/builtin/clone.c b/builtin/clone.c index 91b9cd0d16..c990f398ef 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -25,7 +25,7 @@ #include "refs.h" #include "refspec.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "tree.h" #include "tree-walk.h" #include "unpack-trees.h" @@ -171,7 +171,7 @@ static int add_one_reference(struct string_list_item *item, void *cb_data) } else { struct strbuf sb = STRBUF_INIT; strbuf_addf(&sb, "%s/objects", ref_git); - add_to_alternates_file(sb.buf); + odb_add_to_alternates_file(the_repository->objects, sb.buf); strbuf_release(&sb); } @@ -212,12 +212,14 @@ static void copy_alternates(struct strbuf *src, const char *src_repo) if (!line.len || line.buf[0] == '#') continue; if (is_absolute_path(line.buf)) { - add_to_alternates_file(line.buf); + odb_add_to_alternates_file(the_repository->objects, + line.buf); continue; } abs_path = mkpathdup("%s/objects/%s", src_repo, line.buf); if (!normalize_path_copy(abs_path, abs_path)) - add_to_alternates_file(abs_path); + odb_add_to_alternates_file(the_repository->objects, + abs_path); else warning("skipping invalid relative alternate: %s/%s", src_repo, line.buf); @@ -352,7 +354,7 @@ static void clone_local(const char *src_repo, const char *dest_repo) struct strbuf alt = STRBUF_INIT; get_common_dir(&alt, src_repo); strbuf_addstr(&alt, "/objects"); - add_to_alternates_file(alt.buf); + odb_add_to_alternates_file(the_repository->objects, alt.buf); strbuf_release(&alt); } else { struct strbuf src = STRBUF_INIT; @@ -504,7 +506,7 @@ static void write_followtags(const struct ref *refs, const char *msg) continue; if (ends_with(ref->name, "^{}")) continue; - if (!has_object(the_repository, &ref->old_oid, 0)) + if (!odb_has_object(the_repository->objects, &ref->old_oid, 0)) continue; refs_update_ref(get_main_ref_store(the_repository), msg, ref->name, &ref->old_oid, NULL, 0, @@ -760,16 +762,16 @@ static int write_one_config(const char *key, const char *value, { /* * give git_clone_config a chance to write config values back to the - * environment, since git_config_set_multivar_gently only deals with + * environment, since repo_config_set_multivar_gently only deals with * config-file writes */ int apply_failed = git_clone_config(key, value, ctx, data); if (apply_failed) return apply_failed; - return git_config_set_multivar_gently(key, - value ? value : "true", - CONFIG_REGEX_NONE, 0); + return repo_config_set_multivar_gently(the_repository, key, + value ? value : "true", + CONFIG_REGEX_NONE, 0); } static void write_config(struct string_list *config) @@ -820,12 +822,12 @@ static void write_refspec_config(const char *src_ref_prefix, /* Configure the remote */ if (value.len) { strbuf_addf(&key, "remote.%s.fetch", remote_name); - git_config_set_multivar(key.buf, value.buf, "^$", 0); + repo_config_set_multivar(the_repository, key.buf, value.buf, "^$", 0); strbuf_reset(&key); if (option_mirror) { strbuf_addf(&key, "remote.%s.mirror", remote_name); - git_config_set(key.buf, "true"); + repo_config_set(the_repository, key.buf, "true"); strbuf_reset(&key); } } @@ -999,7 +1001,7 @@ int cmd_clone(int argc, packet_trace_identity("clone"); - git_config(git_clone_config, NULL); + repo_config(the_repository, git_clone_config, NULL); argc = parse_options(argc, argv, prefix, builtin_clone_options, builtin_clone_usage, 0); @@ -1148,7 +1150,7 @@ int cmd_clone(int argc, strbuf_reset(&sb); } - if (!git_config_get_bool("submodule.stickyRecursiveClone", &val) && + if (!repo_config_get_bool(the_repository, "submodule.stickyRecursiveClone", &val) && val) string_list_append(&option_config, "submodule.recurse=true"); @@ -1240,7 +1242,7 @@ int cmd_clone(int argc, * re-read config after init_db and write_config to pick up any config * injected by --template and --config, respectively. */ - git_config(git_clone_config, NULL); + repo_config(the_repository, git_clone_config, NULL); /* * If option_reject_shallow is specified from CLI option, @@ -1292,18 +1294,18 @@ int cmd_clone(int argc, src_ref_prefix = "refs/"; strbuf_addstr(&branch_top, src_ref_prefix); - git_config_set("core.bare", "true"); + repo_config_set(the_repository, "core.bare", "true"); } else if (!option_rev) { strbuf_addf(&branch_top, "refs/remotes/%s/", remote_name); } strbuf_addf(&key, "remote.%s.url", remote_name); - git_config_set(key.buf, repo); + repo_config_set(the_repository, key.buf, repo); strbuf_reset(&key); if (!option_tags) { strbuf_addf(&key, "remote.%s.tagOpt", remote_name); - git_config_set(key.buf, "--no-tags"); + repo_config_set(the_repository, key.buf, "--no-tags"); strbuf_reset(&key); } @@ -1465,7 +1467,7 @@ int cmd_clone(int argc, warning(_("failed to fetch objects from bundle URI '%s'"), bundle_uri); else if (has_heuristic) - git_config_set_gently("fetch.bundleuri", bundle_uri); + repo_config_set_gently(the_repository, "fetch.bundleuri", bundle_uri); remote_state_clear(the_repository->remote_state); free(the_repository->remote_state); diff --git a/builtin/column.c b/builtin/column.c index ce6443d5fa..87dce3c6e5 100644 --- a/builtin/column.c +++ b/builtin/column.c @@ -42,9 +42,9 @@ int cmd_column(int argc, /* This one is special and must be the first one */ if (argc > 1 && starts_with(argv[1], "--command=")) { command = argv[1] + 10; - git_config(column_config, (void *)command); + repo_config(the_repository, column_config, (void *)command); } else - git_config(column_config, NULL); + repo_config(the_repository, column_config, NULL); memset(&copts, 0, sizeof(copts)); copts.padding = 1; diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c index a783a86e79..fe3ebaadad 100644 --- a/builtin/commit-graph.c +++ b/builtin/commit-graph.c @@ -2,11 +2,12 @@ #include "builtin.h" #include "commit.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "hex.h" #include "parse-options.h" #include "commit-graph.h" -#include "object-store.h" +#include "odb.h" #include "progress.h" #include "replace-object.h" #include "strbuf.h" @@ -66,7 +67,7 @@ static int graph_verify(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) { struct commit_graph *graph = NULL; - struct object_directory *odb = NULL; + struct odb_source *source = NULL; char *graph_name; char *chain_name; enum { OPENED_NONE, OPENED_GRAPH, OPENED_CHAIN } opened = OPENED_NONE; @@ -101,14 +102,15 @@ static int graph_verify(int argc, const char **argv, const char *prefix, if (opts.progress) flags |= COMMIT_GRAPH_WRITE_PROGRESS; - odb = find_odb(the_repository, opts.obj_dir); - graph_name = get_commit_graph_filename(odb); - chain_name = get_commit_graph_chain_filename(odb); + source = odb_find_source_or_die(the_repository->objects, opts.obj_dir); + graph_name = get_commit_graph_filename(source); + chain_name = get_commit_graph_chain_filename(source); if (open_commit_graph(graph_name, &fd, &st)) opened = OPENED_GRAPH; else if (errno != ENOENT) die_errno(_("Could not open commit-graph '%s'"), graph_name); - else if (open_commit_graph_chain(chain_name, &fd, &st)) + else if (open_commit_graph_chain(chain_name, &fd, &st, + the_repository->hash_algo)) opened = OPENED_CHAIN; else if (errno != ENOENT) die_errno(_("could not open commit-graph chain '%s'"), chain_name); @@ -120,15 +122,15 @@ static int graph_verify(int argc, const char **argv, const char *prefix, if (opened == OPENED_NONE) return 0; else if (opened == OPENED_GRAPH) - graph = load_commit_graph_one_fd_st(the_repository, fd, &st, odb); + graph = load_commit_graph_one_fd_st(source, fd, &st); else - graph = load_commit_graph_chain_fd_st(the_repository, fd, &st, + graph = load_commit_graph_chain_fd_st(the_repository->objects, fd, &st, &incomplete_chain); if (!graph) return 1; - ret = verify_commit_graph(the_repository, graph, flags); + ret = verify_commit_graph(graph, flags); free_commit_graph(graph); if (incomplete_chain) { @@ -221,7 +223,7 @@ static int graph_write(int argc, const char **argv, const char *prefix, struct string_list pack_indexes = STRING_LIST_INIT_DUP; struct strbuf buf = STRBUF_INIT; struct oidset commits = OIDSET_INIT; - struct object_directory *odb = NULL; + struct odb_source *source = NULL; int result = 0; enum commit_graph_write_flags flags = 0; struct progress *progress = NULL; @@ -265,7 +267,7 @@ static int graph_write(int argc, const char **argv, const char *prefix, trace2_cmd_mode("write"); - git_config(git_commit_graph_write_config, &opts); + repo_config(the_repository, git_commit_graph_write_config, &opts); argc = parse_options(argc, argv, prefix, options, @@ -289,10 +291,10 @@ static int graph_write(int argc, const char **argv, const char *prefix, git_env_bool(GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS, 0)) flags |= COMMIT_GRAPH_WRITE_BLOOM_FILTERS; - odb = find_odb(the_repository, opts.obj_dir); + source = odb_find_source_or_die(the_repository->objects, opts.obj_dir); if (opts.reachable) { - if (write_commit_graph_reachable(odb, flags, &write_opts)) + if (write_commit_graph_reachable(source, flags, &write_opts)) result = 1; goto cleanup; } @@ -311,6 +313,7 @@ static int graph_write(int argc, const char **argv, const char *prefix, while (strbuf_getline(&buf, stdin) != EOF) { if (read_one_commit(&commits, progress, buf.buf)) { result = 1; + stop_progress(&progress); goto cleanup; } } @@ -318,7 +321,7 @@ static int graph_write(int argc, const char **argv, const char *prefix, stop_progress(&progress); } - if (write_commit_graph(odb, + if (write_commit_graph(source, opts.stdin_packs ? &pack_indexes : NULL, opts.stdin_commits ? &commits : NULL, flags, @@ -346,7 +349,7 @@ int cmd_commit_graph(int argc, }; struct option *options = parse_options_concat(builtin_commit_graph_options, common_opts); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); disable_replace_refs(); save_commit_buffer = 0; diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c index ad6b2c9320..5189e685a7 100644 --- a/builtin/commit-tree.c +++ b/builtin/commit-tree.c @@ -6,10 +6,11 @@ #define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "hex.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "commit.h" #include "parse-options.h" @@ -48,7 +49,7 @@ static int parse_parent_arg_callback(const struct option *opt, if (repo_get_oid_commit(the_repository, arg, &oid)) die(_("not a valid object name %s"), arg); - assert_oid_type(&oid, OBJ_COMMIT); + odb_assert_oid_type(the_repository->objects, &oid, OBJ_COMMIT); new_parent(lookup_commit(the_repository, &oid), parents); return 0; } @@ -125,7 +126,7 @@ int cmd_commit_tree(int argc, }; int ret; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); show_usage_with_options_if_asked(argc, argv, commit_tree_usage, options); diff --git a/builtin/commit.c b/builtin/commit.c index fba0dded64..384d0b4e93 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -19,6 +19,7 @@ #include "environment.h" #include "diff.h" #include "commit.h" +#include "add-interactive.h" #include "gettext.h" #include "revision.h" #include "wt-status.h" @@ -122,6 +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 int edit_flag = -1; /* unspecified */ static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship; static int config_commit_verbose = -1; /* unspecified */ @@ -207,9 +209,9 @@ static void status_init_config(struct wt_status *s, config_fn_t fn) { wt_status_prepare(the_repository, s); init_diff_ui_defaults(); - git_config(fn, s); + repo_config(the_repository, fn, s); determine_whence(s); - s->hints = advice_enabled(ADVICE_STATUS_HINTS); /* must come after git_config() */ + s->hints = advice_enabled(ADVICE_STATUS_HINTS); /* must come after repo_config() */ } static void rollback_index_files(void) @@ -354,6 +356,11 @@ static const char *prepare_index(const char **argv, const char *prefix, const char *ret; char *path = NULL; + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + if (is_status) refresh_flags |= REFRESH_UNMERGED; parse_pathspec(&pathspec, 0, @@ -400,7 +407,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) != 0) + if (interactive_add(the_repository, argv, prefix, patch_interactive, &add_p_opt) != 0) die(_("interactive add failed")); the_repository->index_file = old_repo_index_file; @@ -424,6 +431,11 @@ static const char *prepare_index(const char **argv, const char *prefix, commit_style = COMMIT_NORMAL; ret = get_lock_file_path(&index_lock); goto out; + } else { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch"); } /* @@ -683,11 +695,16 @@ static int author_date_is_interesting(void) return author_message || force_date; } +#ifndef WITH_BREAKING_CHANGES static void adjust_comment_line_char(const struct strbuf *sb) { char candidates[] = "#;@!$%^&|:"; char *candidate; const char *p; + size_t cutoff; + + /* Ignore comment chars in trailing comments (e.g., Conflicts:) */ + cutoff = sb->len - ignored_log_message_bytes(sb->buf, sb->len); if (!memchr(sb->buf, candidates[0], sb->len)) { free(comment_line_str_to_free); @@ -700,7 +717,7 @@ static void adjust_comment_line_char(const struct strbuf *sb) candidate = strchr(candidates, *p); if (candidate) *candidate = ' '; - for (p = sb->buf; *p; p++) { + for (p = sb->buf; p + 1 < sb->buf + cutoff; p++) { if ((p[0] == '\n' || p[0] == '\r') && p[1]) { candidate = strchr(candidates, p[1]); if (candidate) @@ -716,6 +733,7 @@ static void adjust_comment_line_char(const struct strbuf *sb) free(comment_line_str_to_free); comment_line_str = comment_line_str_to_free = xstrfmt("%c", *p); } +#endif /* !WITH_BREAKING_CHANGES */ static void prepare_amend_commit(struct commit *commit, struct strbuf *sb, struct pretty_print_context *ctx) @@ -912,8 +930,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix, if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len) die_errno(_("could not write commit template")); +#ifndef WITH_BREAKING_CHANGES if (auto_comment_line_char) adjust_comment_line_char(&sb); +#endif /* !WITH_BREAKING_CHANGES */ strbuf_release(&sb); /* This checks if committer ident is explicitly given */ @@ -1722,6 +1742,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_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")), @@ -1775,6 +1797,9 @@ int cmd_commit(int argc, show_usage_with_options_if_asked(argc, argv, builtin_commit_usage, builtin_commit_options); +#ifndef WITH_BREAKING_CHANGES + warn_on_auto_comment_char = true; +#endif /* !WITH_BREAKING_CHANGES */ prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; @@ -1929,7 +1954,7 @@ int cmd_commit(int argc, "new index file. Check that disk is not full and quota is\n" "not exceeded, and then \"git restore --staged :/\" to recover.")); - git_test_write_commit_graph_or_die(); + git_test_write_commit_graph_or_die(the_repository->objects->sources); repo_rerere(the_repository, 0); run_auto_maintenance(quiet); diff --git a/builtin/config.c b/builtin/config.c index f70d635477..59fb113b07 100644 --- a/builtin/config.c +++ b/builtin/config.c @@ -17,9 +17,9 @@ static const char *const builtin_config_usage[] = { N_("git config list [<file-option>] [<display-option>] [--includes]"), - N_("git config get [<file-option>] [<display-option>] [--includes] [--all] [--regexp] [--value=<value>] [--fixed-value] [--default=<default>] <name>"), - N_("git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--fixed-value] <name> <value>"), - N_("git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] <name>"), + N_("git config get [<file-option>] [<display-option>] [--includes] [--all] [--regexp] [--value=<pattern>] [--fixed-value] [--default=<default>] [--url=<url>] <name>"), + N_("git config set [<file-option>] [--type=<type>] [--all] [--value=<pattern>] [--fixed-value] <name> <value>"), + N_("git config unset [<file-option>] [--all] [--value=<pattern>] [--fixed-value] <name>"), N_("git config rename-section [<file-option>] <old-name> <new-name>"), N_("git config remove-section [<file-option>] <name>"), N_("git config edit [<file-option>]"), @@ -33,17 +33,17 @@ static const char *const builtin_config_list_usage[] = { }; static const char *const builtin_config_get_usage[] = { - N_("git config get [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] <name>"), + N_("git config get [<file-option>] [<display-option>] [--includes] [--all] [--regexp=<regexp>] [--value=<pattern>] [--fixed-value] [--default=<default>] <name>"), NULL }; static const char *const builtin_config_set_usage[] = { - N_("git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] [--value=<value>] [--fixed-value] <name> <value>"), + N_("git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] [--value=<pattern>] [--fixed-value] <name> <value>"), NULL }; static const char *const builtin_config_unset_usage[] = { - N_("git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] <name>"), + N_("git config unset [<file-option>] [--all] [--value=<pattern>] [--fixed-value] <name>"), NULL }; @@ -966,12 +966,12 @@ static int cmd_config_set(int argc, const char **argv, const char *prefix, value = normalize_value(argv[0], argv[1], type, &default_kvi); if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern) { - ret = git_config_set_multivar_in_file_gently(location_opts.source.file, - argv[0], value, value_pattern, - comment, flags); + ret = repo_config_set_multivar_in_file_gently(the_repository, location_opts.source.file, + argv[0], value, value_pattern, + comment, flags); } else { - ret = git_config_set_in_file_gently(location_opts.source.file, - argv[0], comment, value); + ret = repo_config_set_in_file_gently(the_repository, location_opts.source.file, + argv[0], comment, value); if (ret == CONFIG_NOTHING_SET) error(_("cannot overwrite multiple values with a single value\n" " Use a regexp, --add or --replace-all to change %s."), argv[0]); @@ -1010,12 +1010,12 @@ static int cmd_config_unset(int argc, const char **argv, const char *prefix, check_write(&location_opts.source); if ((flags & CONFIG_FLAGS_MULTI_REPLACE) || value_pattern) - ret = git_config_set_multivar_in_file_gently(location_opts.source.file, - argv[0], NULL, value_pattern, - NULL, flags); + ret = repo_config_set_multivar_in_file_gently(the_repository, location_opts.source.file, + argv[0], NULL, value_pattern, + NULL, flags); else - ret = git_config_set_in_file_gently(location_opts.source.file, argv[0], - NULL, NULL); + ret = repo_config_set_in_file_gently(the_repository, location_opts.source.file, argv[0], + NULL, NULL); location_options_release(&location_opts); return ret; @@ -1091,7 +1091,7 @@ static int show_editor(struct config_location_options *opts) die(_("editing stdin is not supported")); if (opts->source.blob) die(_("editing blobs is not supported")); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); config_file = opts->source.file ? xstrdup(opts->source.file) : repo_git_path(the_repository, "config"); @@ -1296,7 +1296,7 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix) check_write(&location_opts.source); check_argc(argc, 2, 2); value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi); - ret = git_config_set_in_file_gently(location_opts.source.file, argv[0], comment, value); + ret = repo_config_set_in_file_gently(the_repository, location_opts.source.file, argv[0], comment, value); if (ret == CONFIG_NOTHING_SET) error(_("cannot overwrite multiple values with a single value\n" " Use a regexp, --add or --replace-all to change %s."), argv[0]); @@ -1305,26 +1305,26 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix) check_write(&location_opts.source); check_argc(argc, 2, 3); value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi); - ret = git_config_set_multivar_in_file_gently(location_opts.source.file, - argv[0], value, argv[2], - comment, flags); + ret = repo_config_set_multivar_in_file_gently(the_repository, location_opts.source.file, + argv[0], value, argv[2], + comment, flags); } else if (actions == ACTION_ADD) { check_write(&location_opts.source); check_argc(argc, 2, 2); value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi); - ret = git_config_set_multivar_in_file_gently(location_opts.source.file, - argv[0], value, - CONFIG_REGEX_NONE, - comment, flags); + ret = repo_config_set_multivar_in_file_gently(the_repository, location_opts.source.file, + argv[0], value, + CONFIG_REGEX_NONE, + comment, flags); } else if (actions == ACTION_REPLACE_ALL) { check_write(&location_opts.source); check_argc(argc, 2, 3); value = normalize_value(argv[0], argv[1], display_opts.type, &default_kvi); - ret = git_config_set_multivar_in_file_gently(location_opts.source.file, - argv[0], value, argv[2], - comment, flags | CONFIG_FLAGS_MULTI_REPLACE); + ret = repo_config_set_multivar_in_file_gently(the_repository, location_opts.source.file, + argv[0], value, argv[2], + comment, flags | CONFIG_FLAGS_MULTI_REPLACE); } else if (actions == ACTION_GET) { check_argc(argc, 1, 2); @@ -1350,19 +1350,19 @@ static int cmd_config_actions(int argc, const char **argv, const char *prefix) check_write(&location_opts.source); check_argc(argc, 1, 2); if (argc == 2) - ret = git_config_set_multivar_in_file_gently(location_opts.source.file, - argv[0], NULL, argv[1], - NULL, flags); + ret = repo_config_set_multivar_in_file_gently(the_repository, location_opts.source.file, + argv[0], NULL, argv[1], + NULL, flags); else - ret = git_config_set_in_file_gently(location_opts.source.file, - argv[0], NULL, NULL); + ret = repo_config_set_in_file_gently(the_repository, location_opts.source.file, + argv[0], NULL, NULL); } else if (actions == ACTION_UNSET_ALL) { check_write(&location_opts.source); check_argc(argc, 1, 2); - ret = git_config_set_multivar_in_file_gently(location_opts.source.file, - argv[0], NULL, argv[1], - NULL, flags | CONFIG_FLAGS_MULTI_REPLACE); + ret = repo_config_set_multivar_in_file_gently(the_repository, location_opts.source.file, + argv[0], NULL, argv[1], + NULL, flags | CONFIG_FLAGS_MULTI_REPLACE); } else if (actions == ACTION_RENAME_SECTION) { check_write(&location_opts.source); diff --git a/builtin/count-objects.c b/builtin/count-objects.c index a88c0c9c09..a61d3b46aa 100644 --- a/builtin/count-objects.c +++ b/builtin/count-objects.c @@ -7,6 +7,7 @@ #include "builtin.h" #include "config.h" #include "dir.h" +#include "environment.h" #include "gettext.h" #include "path.h" #include "parse-options.h" @@ -80,10 +81,10 @@ static int count_cruft(const char *basename UNUSED, const char *path, return 0; } -static int print_alternate(struct object_directory *odb, void *data UNUSED) +static int print_alternate(struct odb_source *alternate, void *data UNUSED) { printf("alternate: "); - quote_c_style(odb->path, NULL, stdout, 0); + quote_c_style(alternate->path, NULL, stdout, 0); putchar('\n'); return 0; } @@ -106,7 +107,7 @@ int cmd_count_objects(int argc, OPT_END(), }; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, opts, count_objects_usage, 0); /* we do not take arguments other than flags for now */ @@ -117,7 +118,7 @@ int cmd_count_objects(int argc, report_linked_checkout_garbage(the_repository); } - for_each_loose_file_in_objdir(repo_get_object_directory(the_repository), + for_each_loose_file_in_source(the_repository->objects->sources, count_loose, count_cruft, NULL, NULL); if (verbose) { @@ -159,7 +160,7 @@ int cmd_count_objects(int argc, printf("prune-packable: %lu\n", packed_loose); printf("garbage: %lu\n", garbage); printf("size-garbage: %s\n", garbage_buf.buf); - foreach_alt_odb(print_alternate, NULL); + odb_for_each_alternate(the_repository->objects, print_alternate, NULL); strbuf_release(&loose_buf); strbuf_release(&pack_buf); strbuf_release(&garbage_buf); diff --git a/builtin/credential-cache--daemon.c b/builtin/credential-cache--daemon.c index 5065ff4660..65cc619bec 100644 --- a/builtin/credential-cache--daemon.c +++ b/builtin/credential-cache--daemon.c @@ -307,7 +307,7 @@ int cmd_credential_cache_daemon(int argc, OPT_END() }; - git_config_get_bool("credentialcache.ignoresighup", &ignore_sighup); + repo_config_get_bool(the_repository, "credentialcache.ignoresighup", &ignore_sighup); argc = parse_options(argc, argv, prefix, options, usage, 0); socket_path = argv[0]; diff --git a/builtin/credential-store.c b/builtin/credential-store.c index e669e99dbf..b74e06cc93 100644 --- a/builtin/credential-store.c +++ b/builtin/credential-store.c @@ -66,7 +66,7 @@ static void rewrite_credential_file(const char *fn, struct credential *c, { int timeout_ms = 1000; - git_config_get_int("credentialstore.locktimeoutms", &timeout_ms); + repo_config_get_int(the_repository, "credentialstore.locktimeoutms", &timeout_ms); if (hold_lock_file_for_update_timeout(&credential_lock, fn, 0, timeout_ms) < 0) die_errno(_("unable to get credential storage lock in %d ms"), timeout_ms); if (extra) diff --git a/builtin/credential.c b/builtin/credential.c index 2e11b15dde..a295c80b36 100644 --- a/builtin/credential.c +++ b/builtin/credential.c @@ -3,6 +3,7 @@ #include "git-compat-util.h" #include "credential.h" #include "builtin.h" +#include "environment.h" #include "config.h" static const char usage_msg[] = @@ -16,7 +17,7 @@ int cmd_credential(int argc, const char *op; struct credential c = CREDENTIAL_INIT; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); show_usage_if_asked(argc, argv, usage_msg); if (argc != 2) diff --git a/builtin/describe.c b/builtin/describe.c index 2d50883b72..9f4e26d7ff 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -19,10 +19,12 @@ #include "setup.h" #include "strvec.h" #include "run-command.h" -#include "object-store.h" +#include "odb.h" #include "list-objects.h" #include "commit-slab.h" #include "wildmatch.h" +#include "prio-queue.h" +#include "oidset.h" #define MAX_TAGS (FLAG_BITS - 1) #define DEFAULT_CANDIDATES 10 @@ -249,36 +251,83 @@ static int compare_pt(const void *a_, const void *b_) return 0; } -static unsigned long finish_depth_computation( - struct commit_list **list, - struct possible_tag *best) +struct lazy_queue { + struct prio_queue queue; + bool get_pending; +}; + +#define LAZY_QUEUE_INIT { { compare_commits_by_commit_date }, false } + +static void *lazy_queue_get(struct lazy_queue *queue) +{ + if (queue->get_pending) + prio_queue_get(&queue->queue); + else + queue->get_pending = true; + return prio_queue_peek(&queue->queue); +} + +static void lazy_queue_put(struct lazy_queue *queue, void *thing) +{ + if (queue->get_pending) + prio_queue_replace(&queue->queue, thing); + else + prio_queue_put(&queue->queue, thing); + queue->get_pending = false; +} + +static bool lazy_queue_empty(const struct lazy_queue *queue) +{ + return queue->queue.nr == (queue->get_pending ? 1 : 0); +} + +static void lazy_queue_clear(struct lazy_queue *queue) +{ + clear_prio_queue(&queue->queue); + queue->get_pending = false; +} + +static unsigned long finish_depth_computation(struct lazy_queue *queue, + struct possible_tag *best) { unsigned long seen_commits = 0; - while (*list) { - struct commit *c = pop_commit(list); + struct oidset unflagged = OIDSET_INIT; + + for (size_t i = queue->get_pending ? 1 : 0; i < queue->queue.nr; i++) { + struct commit *commit = queue->queue.array[i].data; + if (!(commit->object.flags & best->flag_within)) + oidset_insert(&unflagged, &commit->object.oid); + } + + while (!lazy_queue_empty(queue)) { + struct commit *c = lazy_queue_get(queue); struct commit_list *parents = c->parents; seen_commits++; if (c->object.flags & best->flag_within) { - struct commit_list *a = *list; - while (a) { - struct commit *i = a->item; - if (!(i->object.flags & best->flag_within)) - break; - a = a->next; - } - if (!a) + if (!oidset_size(&unflagged)) break; - } else + } else { + oidset_remove(&unflagged, &c->object.oid); best->depth++; + } while (parents) { + unsigned seen, flag_before, flag_after; struct commit *p = parents->item; repo_parse_commit(the_repository, p); - if (!(p->object.flags & SEEN)) - commit_list_insert_by_date(p, list); + seen = p->object.flags & SEEN; + if (!seen) + lazy_queue_put(queue, p); + flag_before = p->object.flags & best->flag_within; p->object.flags |= c->object.flags; + flag_after = p->object.flags & best->flag_within; + if (!seen && !flag_after) + oidset_insert(&unflagged, &p->object.oid); + if (seen && !flag_before && flag_after) + oidset_remove(&unflagged, &p->object.oid); parents = parents->next; } } + oidset_clear(&unflagged); return seen_commits; } @@ -313,18 +362,16 @@ static void append_suffix(int depth, const struct object_id *oid, struct strbuf repo_find_unique_abbrev(the_repository, oid, abbrev)); } -static void describe_commit(struct object_id *oid, struct strbuf *dst) +static void describe_commit(struct commit *cmit, struct strbuf *dst) { - struct commit *cmit, *gave_up_on = NULL; - struct commit_list *list; + struct commit *gave_up_on = NULL; + struct lazy_queue queue = LAZY_QUEUE_INIT; struct commit_name *n; struct possible_tag all_matches[MAX_TAGS]; unsigned int match_cnt = 0, annotated_cnt = 0, cur_match; unsigned long seen_commits = 0; unsigned int unannotated_cnt = 0; - cmit = lookup_commit_reference(the_repository, oid); - n = find_commit_name(&cmit->object.oid); if (n && (tags || all || n->prio == 2)) { /* @@ -332,7 +379,7 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) */ append_name(n, dst); if (n->misnamed || longformat) - append_suffix(0, n->tag ? get_tagged_oid(n->tag) : oid, dst); + append_suffix(0, n->tag ? get_tagged_oid(n->tag) : &cmit->object.oid, dst); if (suffix) strbuf_addstr(dst, suffix); return; @@ -359,11 +406,10 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) have_util = 1; } - list = NULL; cmit->object.flags = SEEN; - commit_list_insert(cmit, &list); - while (list) { - struct commit *c = pop_commit(&list); + lazy_queue_put(&queue, cmit); + while (!lazy_queue_empty(&queue)) { + struct commit *c = lazy_queue_get(&queue); struct commit_list *parents = c->parents; struct commit_name **slot; @@ -397,7 +443,7 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) t->depth++; } /* Stop if last remaining path already covered by best candidate(s) */ - if (annotated_cnt && !list) { + if (annotated_cnt && lazy_queue_empty(&queue)) { int best_depth = INT_MAX; unsigned best_within = 0; for (cur_match = 0; cur_match < match_cnt; cur_match++) { @@ -420,7 +466,7 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) struct commit *p = parents->item; repo_parse_commit(the_repository, p); if (!(p->object.flags & SEEN)) - commit_list_insert_by_date(p, &list); + lazy_queue_put(&queue, p); p->object.flags |= c->object.flags; parents = parents->next; @@ -435,6 +481,7 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) strbuf_add_unique_abbrev(dst, cmit_oid, abbrev); if (suffix) strbuf_addstr(dst, suffix); + lazy_queue_clear(&queue); return; } if (unannotated_cnt) @@ -450,11 +497,11 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) QSORT(all_matches, match_cnt, compare_pt); if (gave_up_on) { - commit_list_insert_by_date(gave_up_on, &list); + lazy_queue_put(&queue, gave_up_on); seen_commits--; } - seen_commits += finish_depth_computation(&list, &all_matches[0]); - free_commit_list(list); + seen_commits += finish_depth_computation(&queue, &all_matches[0]); + lazy_queue_clear(&queue); if (debug) { static int label_width = -1; @@ -489,8 +536,8 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst) } struct process_commit_data { - struct object_id current_commit; - struct object_id looking_for; + struct commit *current_commit; + const struct object_id *looking_for; struct strbuf *dst; struct rev_info *revs; }; @@ -498,30 +545,38 @@ struct process_commit_data { static void process_commit(struct commit *commit, void *data) { struct process_commit_data *pcd = data; - pcd->current_commit = commit->object.oid; + pcd->current_commit = commit; } static void process_object(struct object *obj, const char *path, void *data) { struct process_commit_data *pcd = data; - if (oideq(&pcd->looking_for, &obj->oid) && !pcd->dst->len) { + if (oideq(pcd->looking_for, &obj->oid) && !pcd->dst->len) { reset_revision_walk(); - describe_commit(&pcd->current_commit, pcd->dst); - strbuf_addf(pcd->dst, ":%s", path); + if (pcd->current_commit) { + describe_commit(pcd->current_commit, pcd->dst); + strbuf_addf(pcd->dst, ":%s", path); + } free_commit_list(pcd->revs->commits); pcd->revs->commits = NULL; } } -static void describe_blob(struct object_id oid, struct strbuf *dst) +static void describe_blob(const struct object_id *oid, struct strbuf *dst) { struct rev_info revs; struct strvec args = STRVEC_INIT; - struct process_commit_data pcd = { *null_oid(the_hash_algo), oid, dst, &revs}; + struct object_id head_oid; + struct process_commit_data pcd = { NULL, oid, dst, &revs}; + + if (repo_get_oid(the_repository, "HEAD", &head_oid)) + die(_("cannot search for blob '%s' on an unborn branch"), + oid_to_hex(oid)); strvec_pushl(&args, "internal: The first arg is not parsed", - "--objects", "--in-commit-order", "--reverse", "HEAD", + "--objects", "--in-commit-order", "--reverse", + oid_to_hex(&head_oid), NULL); repo_init_revisions(the_repository, &revs, NULL); @@ -535,6 +590,9 @@ static void describe_blob(struct object_id oid, struct strbuf *dst) reset_revision_walk(); release_revisions(&revs); strvec_clear(&args); + + if (!dst->len) + die(_("blob '%s' not reachable from HEAD"), oid_to_hex(oid)); } static void describe(const char *arg, int last_one) @@ -551,9 +609,10 @@ static void describe(const char *arg, int last_one) cmit = lookup_commit_reference_gently(the_repository, &oid, 1); if (cmit) - describe_commit(&oid, &sb); - else if (oid_object_info(the_repository, &oid, NULL) == OBJ_BLOB) - describe_blob(oid, &sb); + describe_commit(cmit, &sb); + else if (odb_read_object_info(the_repository->objects, + &oid, NULL) == OBJ_BLOB) + describe_blob(&oid, &sb); else die(_("%s is neither a commit nor blob"), arg); @@ -622,7 +681,7 @@ int cmd_describe(int argc, OPT_END(), }; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, describe_usage, 0); if (abbrev < 0) abbrev = DEFAULT_ABBREV; diff --git a/builtin/diff-files.c b/builtin/diff-files.c index 99b1749723..ea91347ce2 100644 --- a/builtin/diff-files.c +++ b/builtin/diff-files.c @@ -31,7 +31,7 @@ int cmd_diff_files(int argc, show_usage_if_asked(argc, argv, diff_files_usage); - git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ + repo_config(the_repository, git_diff_basic_config, NULL); /* no "diff" UI options */ prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; diff --git a/builtin/diff-index.c b/builtin/diff-index.c index 81c0bc8ed7..522dacfc4c 100644 --- a/builtin/diff-index.c +++ b/builtin/diff-index.c @@ -28,7 +28,7 @@ int cmd_diff_index(int argc, show_usage_if_asked(argc, argv, diff_cache_usage); - git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ + repo_config(the_repository, git_diff_basic_config, NULL); /* no "diff" UI options */ prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c index e31cc797fe..49dd4d00eb 100644 --- a/builtin/diff-tree.c +++ b/builtin/diff-tree.c @@ -124,7 +124,7 @@ int cmd_diff_tree(int argc, show_usage_if_asked(argc, argv, diff_tree_usage); - git_config(git_diff_basic_config, NULL); /* no "diff" UI options */ + repo_config(the_repository, git_diff_basic_config, NULL); /* no "diff" UI options */ prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; diff --git a/builtin/diff.c b/builtin/diff.c index fa963808c3..0b23c41456 100644 --- a/builtin/diff.c +++ b/builtin/diff.c @@ -35,7 +35,7 @@ static const char builtin_diff_usage[] = " or: git diff [<options>] [--merge-base] <commit> [<commit>...] <commit> [--] [<path>...]\n" " or: git diff [<options>] <commit>...<commit> [--] [<path>...]\n" " or: git diff [<options>] <blob> <blob>\n" -" or: git diff [<options>] --no-index [--] <path> <path>" +" or: git diff [<options>] --no-index [--] <path> <path> [<pathspec>...]" "\n" COMMON_DIFF_OPTIONS_HELP; @@ -483,10 +483,25 @@ int cmd_diff(int argc, * configurable via a command line option. */ if (nongit) - repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + repo_set_hash_algo(the_repository, GIT_HASH_DEFAULT); init_diff_ui_defaults(); - git_config(git_diff_ui_config, NULL); + repo_config(the_repository, git_diff_ui_config, NULL); + + /* + * If we are ignoring the fact that our current directory may + * be part of a working tree controlled by a Git repository to + * pretend to be a "better GNU diff", we should undo the + * effect of the setup code that did a chdir() to the top of + * the working tree. Where we came from is recorded in the + * prefix. + */ + if (no_index && prefix) { + if (chdir(prefix)) + die(_("cannot come back to cwd")); + prefix = NULL; + } + prefix = precompose_argv_prefix(argc, argv, prefix); repo_init_revisions(the_repository, &rev, prefix); diff --git a/builtin/difftool.c b/builtin/difftool.c index a3b64ce694..e4bc1f8316 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -30,7 +30,7 @@ #include "strbuf.h" #include "lockfile.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "dir.h" #include "entry.h" #include "setup.h" @@ -320,7 +320,7 @@ static char *get_symlink(struct repository *repo, } else { enum object_type type; unsigned long size; - data = repo_read_object_file(repo, oid, &type, &size); + data = odb_read_object(repo->objects, oid, &type, &size); if (!data) die(_("could not read object %s for symlink %s"), oid_to_hex(oid), path); diff --git a/builtin/fast-export.c b/builtin/fast-export.c index fcf6b00d5f..c06ee0b213 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -9,12 +9,13 @@ #include "builtin.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "hex.h" #include "refs.h" #include "refspec.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "commit.h" #include "object.h" #include "tag.h" @@ -29,6 +30,7 @@ #include "quote.h" #include "remote.h" #include "blob.h" +#include "gpg-interface.h" static const char *const fast_export_usage[] = { N_("git fast-export [<rev-list-opts>]"), @@ -323,7 +325,7 @@ static void export_blob(const struct object_id *oid) object = (struct object *)lookup_blob(the_repository, oid); eaten = 0; } else { - buf = repo_read_object_file(the_repository, oid, &type, &size); + buf = odb_read_object(the_repository->objects, oid, &type, &size); if (!buf) die("could not read blob %s", oid_to_hex(oid)); if (check_object_signature(the_repository, oid, buf, size, @@ -652,6 +654,38 @@ static const char *find_commit_multiline_header(const char *msg, return strbuf_detach(&val, NULL); } +static void print_signature(const char *signature, const char *object_hash) +{ + if (!signature) + return; + + printf("gpgsig %s %s\ndata %u\n%s\n", + object_hash, + get_signature_format(signature), + (unsigned)strlen(signature), + signature); +} + +static const char *append_signatures_for_header(struct string_list *signatures, + const char *pos, + const char *header, + const char *object_hash) +{ + const char *signature; + const char *start = pos; + const char *end = pos; + + while ((signature = find_commit_multiline_header(start + 1, + header, + &end))) { + string_list_append(signatures, signature)->util = (void *)object_hash; + free((char *)signature); + start = end; + } + + return end; +} + static void handle_commit(struct commit *commit, struct rev_info *rev, struct string_list *paths_of_changed_objects) { @@ -660,7 +694,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev, const char *author, *author_end, *committer, *committer_end; const char *encoding = NULL; size_t encoding_len; - const char *signature_alg = NULL, *signature = NULL; + struct string_list signatures = STRING_LIST_INIT_DUP; const char *message; char *reencoded = NULL; struct commit_list *p; @@ -700,10 +734,11 @@ static void handle_commit(struct commit *commit, struct rev_info *rev, } if (*commit_buffer_cursor == '\n') { - if ((signature = find_commit_multiline_header(commit_buffer_cursor + 1, "gpgsig", &commit_buffer_cursor))) - signature_alg = "sha1"; - else if ((signature = find_commit_multiline_header(commit_buffer_cursor + 1, "gpgsig-sha256", &commit_buffer_cursor))) - signature_alg = "sha256"; + const char *after_sha1 = append_signatures_for_header(&signatures, commit_buffer_cursor, + "gpgsig", "sha1"); + const char *after_sha256 = append_signatures_for_header(&signatures, commit_buffer_cursor, + "gpgsig-sha256", "sha256"); + commit_buffer_cursor = (after_sha1 > after_sha256) ? after_sha1 : after_sha256; } message = strstr(commit_buffer_cursor, "\n\n"); @@ -769,30 +804,30 @@ static void handle_commit(struct commit *commit, struct rev_info *rev, printf("%.*s\n%.*s\n", (int)(author_end - author), author, (int)(committer_end - committer), committer); - if (signature) { + if (signatures.nr) { switch (signed_commit_mode) { case SIGN_ABORT: die("encountered signed commit %s; use " "--signed-commits=<mode> to handle it", oid_to_hex(&commit->object.oid)); case SIGN_WARN_VERBATIM: - warning("exporting signed commit %s", - oid_to_hex(&commit->object.oid)); + warning("exporting %"PRIuMAX" signature(s) for commit %s", + (uintmax_t)signatures.nr, oid_to_hex(&commit->object.oid)); /* fallthru */ case SIGN_VERBATIM: - printf("gpgsig %s\ndata %u\n%s", - signature_alg, - (unsigned)strlen(signature), - signature); + for (size_t i = 0; i < signatures.nr; i++) { + struct string_list_item *item = &signatures.items[i]; + print_signature(item->string, item->util); + } break; case SIGN_WARN_STRIP: - warning("stripping signature from commit %s", + warning("stripping signature(s) from commit %s", oid_to_hex(&commit->object.oid)); /* fallthru */ case SIGN_STRIP: break; } - free((char *)signature); + string_list_clear(&signatures, 0); } if (!reencoded && encoding) printf("encoding %.*s\n", (int)encoding_len, encoding); @@ -869,8 +904,8 @@ static void handle_tag(const char *name, struct tag *tag) return; } - buf = repo_read_object_file(the_repository, &tag->object.oid, &type, - &size); + buf = odb_read_object(the_repository->objects, &tag->object.oid, + &type, &size); if (!buf) die("could not read tag %s", oid_to_hex(&tag->object.oid)); message = memmem(buf, size, "\n\n", 2); @@ -1200,7 +1235,7 @@ static void import_marks(char *input_file, int check_exists) if (last_idnum < mark) last_idnum = mark; - type = oid_object_info(the_repository, &oid, NULL); + type = odb_read_object_info(the_repository->objects, &oid, NULL); if (type < 0) die("object not found: %s", oid_to_hex(&oid)); @@ -1327,7 +1362,7 @@ int cmd_fast_export(int argc, usage_with_options (fast_export_usage, options); /* we handle encodings */ - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); repo_init_revisions(the_repository, &revs, prefix); init_revision_sources(&revision_sources); diff --git a/builtin/fast-import.c b/builtin/fast-import.c index b2839c5f43..2c35f9345d 100644 --- a/builtin/fast-import.c +++ b/builtin/fast-import.c @@ -24,11 +24,12 @@ #include "packfile.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "mem-pool.h" #include "commit-reach.h" #include "khash.h" #include "date.h" +#include "gpg-interface.h" #define PACK_ID_BITS 16 #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1) @@ -763,7 +764,8 @@ static void start_packfile(void) struct packed_git *p; int pack_fd; - pack_fd = odb_mkstemp(&tmp_file, "pack/tmp_pack_XXXXXX"); + pack_fd = odb_mkstemp(the_repository->objects, &tmp_file, + "pack/tmp_pack_XXXXXX"); FLEX_ALLOC_STR(p, pack_name, tmp_file.buf); strbuf_release(&tmp_file); @@ -820,11 +822,11 @@ static char *keep_pack(const char *curr_index_name) die_errno("failed to write keep file"); odb_pack_name(pack_data->repo, &name, pack_data->hash, "pack"); - if (finalize_object_file(pack_data->pack_name, name.buf)) + if (finalize_object_file(pack_data->repo, pack_data->pack_name, name.buf)) die("cannot store pack file"); odb_pack_name(pack_data->repo, &name, pack_data->hash, "idx"); - if (finalize_object_file(curr_index_name, name.buf)) + if (finalize_object_file(pack_data->repo, curr_index_name, name.buf)) die("cannot store index file"); free((void *)curr_index_name); return strbuf_detach(&name, NULL); @@ -1264,7 +1266,7 @@ static void load_tree(struct tree_entry *root) die("Can't load tree %s", oid_to_hex(oid)); } else { enum object_type type; - buf = repo_read_object_file(the_repository, oid, &type, &size); + buf = odb_read_object(the_repository->objects, oid, &type, &size); if (!buf || type != OBJ_TREE) die("Can't load tree %s", oid_to_hex(oid)); } @@ -1755,8 +1757,8 @@ static void insert_object_entry(struct mark_set **s, struct object_id *oid, uint struct object_entry *e; e = find_object(oid); if (!e) { - enum object_type type = oid_object_info(the_repository, - oid, NULL); + enum object_type type = odb_read_object_info(the_repository->objects, + oid, NULL); if (type < 0) die("object not found: %s", oid_to_hex(oid)); e = insert_object(oid); @@ -2415,8 +2417,8 @@ static void file_change_m(const char *p, struct branch *b) enum object_type expected = S_ISDIR(mode) ? OBJ_TREE: OBJ_BLOB; enum object_type type = oe ? oe->type : - oid_object_info(the_repository, &oid, - NULL); + odb_read_object_info(the_repository->objects, + &oid, NULL); if (type < 0) die("%s not found: %s", S_ISDIR(mode) ? "Tree" : "Blob", @@ -2534,10 +2536,9 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa oidcpy(&commit_oid, &commit_oe->idx.oid); } else if (!repo_get_oid(the_repository, p, &commit_oid)) { unsigned long size; - char *buf = read_object_with_reference(the_repository, - &commit_oid, - OBJ_COMMIT, &size, - &commit_oid); + char *buf = odb_read_object_peeled(the_repository->objects, + &commit_oid, OBJ_COMMIT, &size, + &commit_oid); if (!buf || size < the_hash_algo->hexsz + 6) die("Not a valid commit: %s", p); free(buf); @@ -2552,7 +2553,7 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa die("Not a blob (actually a %s): %s", type_name(oe->type), command_buf.buf); } else if (!is_null_oid(&oid)) { - enum object_type type = oid_object_info(the_repository, &oid, + enum object_type type = odb_read_object_info(the_repository->objects, &oid, NULL); if (type < 0) die("Blob not found: %s", command_buf.buf); @@ -2603,9 +2604,8 @@ static void parse_from_existing(struct branch *b) unsigned long size; char *buf; - buf = read_object_with_reference(the_repository, - &b->oid, OBJ_COMMIT, &size, - &b->oid); + buf = odb_read_object_peeled(the_repository->objects, &b->oid, + OBJ_COMMIT, &size, &b->oid); parse_from_commit(b, buf, size); free(buf); } @@ -2698,10 +2698,9 @@ static struct hash_list *parse_merge(unsigned int *count) oidcpy(&n->oid, &oe->idx.oid); } else if (!repo_get_oid(the_repository, from, &n->oid)) { unsigned long size; - char *buf = read_object_with_reference(the_repository, - &n->oid, - OBJ_COMMIT, - &size, &n->oid); + char *buf = odb_read_object_peeled(the_repository->objects, + &n->oid, OBJ_COMMIT, + &size, &n->oid); if (!buf || size < the_hash_algo->hexsz + 6) die("Not a valid commit: %s", from); free(buf); @@ -2718,15 +2717,82 @@ static struct hash_list *parse_merge(unsigned int *count) return list; } +struct signature_data { + char *hash_algo; /* "sha1" or "sha256" */ + char *sig_format; /* "openpgp", "x509", "ssh", or "unknown" */ + struct strbuf data; /* The actual signature data */ +}; + +static void parse_one_signature(struct signature_data *sig, const char *v) +{ + char *args = xstrdup(v); /* Will be freed when sig->hash_algo is freed */ + char *space = strchr(args, ' '); + + if (!space) + die("Expected gpgsig format: 'gpgsig <hash-algo> <signature-format>', " + "got 'gpgsig %s'", args); + *space = '\0'; + + sig->hash_algo = args; + sig->sig_format = space + 1; + + /* Validate hash algorithm */ + if (strcmp(sig->hash_algo, "sha1") && + strcmp(sig->hash_algo, "sha256")) + die("Unknown git hash algorithm in gpgsig: '%s'", sig->hash_algo); + + /* Validate signature format */ + if (!valid_signature_format(sig->sig_format)) + die("Invalid signature format in gpgsig: '%s'", sig->sig_format); + if (!strcmp(sig->sig_format, "unknown")) + warning("'unknown' signature format in gpgsig"); + + /* Read signature data */ + read_next_command(); + parse_data(&sig->data, 0, NULL); +} + +static void add_gpgsig_to_commit(struct strbuf *commit_data, + const char *header, + struct signature_data *sig) +{ + struct string_list siglines = STRING_LIST_INIT_NODUP; + + if (!sig->hash_algo) + return; + + strbuf_addstr(commit_data, header); + string_list_split_in_place(&siglines, sig->data.buf, "\n", -1); + strbuf_add_separated_string_list(commit_data, "\n ", &siglines); + strbuf_addch(commit_data, '\n'); + string_list_clear(&siglines, 1); + strbuf_release(&sig->data); + free(sig->hash_algo); +} + +static void store_signature(struct signature_data *stored_sig, + struct signature_data *new_sig, + const char *hash_type) +{ + if (stored_sig->hash_algo) { + warning("multiple %s signatures found, " + "ignoring additional signature", + hash_type); + strbuf_release(&new_sig->data); + free(new_sig->hash_algo); + } else { + *stored_sig = *new_sig; + } +} + static void parse_new_commit(const char *arg) { - static struct strbuf sig = STRBUF_INIT; static struct strbuf msg = STRBUF_INIT; - struct string_list siglines = STRING_LIST_INIT_NODUP; + struct signature_data sig_sha1 = { NULL, NULL, STRBUF_INIT }; + struct signature_data sig_sha256 = { NULL, NULL, STRBUF_INIT }; struct branch *b; char *author = NULL; char *committer = NULL; - char *sig_alg = NULL; char *encoding = NULL; struct hash_list *merge_list = NULL; unsigned int merge_count; @@ -2750,13 +2816,23 @@ static void parse_new_commit(const char *arg) } if (!committer) die("Expected committer but didn't get one"); - if (skip_prefix(command_buf.buf, "gpgsig ", &v)) { - sig_alg = xstrdup(v); - read_next_command(); - parse_data(&sig, 0, NULL); + + /* Process signatures (up to 2: one "sha1" and one "sha256") */ + while (skip_prefix(command_buf.buf, "gpgsig ", &v)) { + struct signature_data sig = { NULL, NULL, STRBUF_INIT }; + + parse_one_signature(&sig, v); + + if (!strcmp(sig.hash_algo, "sha1")) + store_signature(&sig_sha1, &sig, "SHA-1"); + else if (!strcmp(sig.hash_algo, "sha256")) + store_signature(&sig_sha256, &sig, "SHA-256"); + else + BUG("parse_one_signature() returned unknown hash algo"); + read_next_command(); - } else - strbuf_setlen(&sig, 0); + } + if (skip_prefix(command_buf.buf, "encoding ", &v)) { encoding = xstrdup(v); read_next_command(); @@ -2830,23 +2906,14 @@ static void parse_new_commit(const char *arg) strbuf_addf(&new_data, "encoding %s\n", encoding); - if (sig_alg) { - if (!strcmp(sig_alg, "sha1")) - strbuf_addstr(&new_data, "gpgsig "); - else if (!strcmp(sig_alg, "sha256")) - strbuf_addstr(&new_data, "gpgsig-sha256 "); - else - die("Expected gpgsig algorithm sha1 or sha256, got %s", sig_alg); - string_list_split_in_place(&siglines, sig.buf, "\n", -1); - strbuf_add_separated_string_list(&new_data, "\n ", &siglines); - strbuf_addch(&new_data, '\n'); - } + + add_gpgsig_to_commit(&new_data, "gpgsig ", &sig_sha1); + add_gpgsig_to_commit(&new_data, "gpgsig-sha256 ", &sig_sha256); + strbuf_addch(&new_data, '\n'); strbuf_addbuf(&new_data, &msg); - string_list_clear(&siglines, 1); free(author); free(committer); - free(sig_alg); free(encoding); if (!store_object(OBJ_COMMIT, &new_data, NULL, &b->oid, next_mark)) @@ -2894,7 +2961,8 @@ static void parse_new_tag(const char *arg) } else if (!repo_get_oid(the_repository, from, &oid)) { struct object_entry *oe = find_object(&oid); if (!oe) { - type = oid_object_info(the_repository, &oid, NULL); + type = odb_read_object_info(the_repository->objects, + &oid, NULL); if (type < 0) die("Not a valid object: %s", from); } else @@ -3000,7 +3068,7 @@ static void cat_blob(struct object_entry *oe, struct object_id *oid) char *buf; if (!oe || oe->pack_id == MAX_PACK_ID) { - buf = repo_read_object_file(the_repository, oid, &type, &size); + buf = odb_read_object(the_repository->objects, oid, &type, &size); } else { type = oe->type; buf = gfi_unpack_entry(oe, &size); @@ -3084,8 +3152,8 @@ static struct object_entry *dereference(struct object_entry *oe, const unsigned hexsz = the_hash_algo->hexsz; if (!oe) { - enum object_type type = oid_object_info(the_repository, oid, - NULL); + enum object_type type = odb_read_object_info(the_repository->objects, + oid, NULL); if (type < 0) die("object not found: %s", oid_to_hex(oid)); /* cache it! */ @@ -3108,8 +3176,8 @@ static struct object_entry *dereference(struct object_entry *oe, buf = gfi_unpack_entry(oe, &size); } else { enum object_type unused; - buf = repo_read_object_file(the_repository, oid, &unused, - &size); + buf = odb_read_object(the_repository->objects, oid, + &unused, &size); } if (!buf) die("Can't load object %s", oid_to_hex(oid)); @@ -3524,25 +3592,25 @@ static void git_pack_config(void) int limit; unsigned long packsizelimit_value; - if (!git_config_get_ulong("pack.depth", &max_depth)) { + if (!repo_config_get_ulong(the_repository, "pack.depth", &max_depth)) { if (max_depth > MAX_DEPTH) max_depth = MAX_DEPTH; } - if (!git_config_get_int("pack.indexversion", &indexversion_value)) { + if (!repo_config_get_int(the_repository, "pack.indexversion", &indexversion_value)) { pack_idx_opts.version = indexversion_value; if (pack_idx_opts.version > 2) git_die_config(the_repository, "pack.indexversion", "bad pack.indexVersion=%"PRIu32, pack_idx_opts.version); } - if (!git_config_get_ulong("pack.packsizelimit", &packsizelimit_value)) + if (!repo_config_get_ulong(the_repository, "pack.packsizelimit", &packsizelimit_value)) max_packsize = packsizelimit_value; - if (!git_config_get_int("fastimport.unpacklimit", &limit)) + if (!repo_config_get_int(the_repository, "fastimport.unpacklimit", &limit)) unpack_limit = limit; - else if (!git_config_get_int("transfer.unpacklimit", &limit)) + else if (!repo_config_get_int(the_repository, "transfer.unpacklimit", &limit)) unpack_limit = limit; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); } static const char fast_import_usage[] = diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index d07eec9e55..d9e42bad58 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -274,8 +274,10 @@ int cmd_fetch_pack(int argc, } close(fd[0]); close(fd[1]); - if (finish_connect(conn)) - return 1; + if (finish_connect(conn)) { + ret = 1; + goto cleanup; + } ret = !fetched_refs; @@ -291,6 +293,7 @@ int cmd_fetch_pack(int argc, printf("%s %s\n", oid_to_hex(&ref->old_oid), ref->name); +cleanup: for (size_t i = 0; i < nr_sought; i++) free_one_ref(sought_to_free[i]); free(sought_to_free); diff --git a/builtin/fetch.c b/builtin/fetch.c index 40a0e8d244..24645c4653 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -14,7 +14,7 @@ #include "refs.h" #include "refspec.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "oidset.h" #include "oid-array.h" #include "commit.h" @@ -366,9 +366,9 @@ static void find_non_local_tags(const struct ref *refs, */ if (ends_with(ref->name, "^{}")) { if (item && - !has_object(the_repository, &ref->old_oid, 0) && + !odb_has_object(the_repository->objects, &ref->old_oid, 0) && !oidset_contains(&fetch_oids, &ref->old_oid) && - !has_object(the_repository, &item->oid, 0) && + !odb_has_object(the_repository->objects, &item->oid, 0) && !oidset_contains(&fetch_oids, &item->oid)) clear_item(item); item = NULL; @@ -382,7 +382,7 @@ static void find_non_local_tags(const struct ref *refs, * fetch. */ if (item && - !has_object(the_repository, &item->oid, 0) && + !odb_has_object(the_repository->objects, &item->oid, 0) && !oidset_contains(&fetch_oids, &item->oid)) clear_item(item); @@ -403,7 +403,7 @@ static void find_non_local_tags(const struct ref *refs, * checked to see if it needs fetching. */ if (item && - !has_object(the_repository, &item->oid, 0) && + !odb_has_object(the_repository->objects, &item->oid, 0) && !oidset_contains(&fetch_oids, &item->oid)) clear_item(item); @@ -640,9 +640,6 @@ static struct ref *get_ref_map(struct remote *remote, return ref_map; } -#define STORE_REF_ERROR_OTHER 1 -#define STORE_REF_ERROR_DF_CONFLICT 2 - static int s_update_ref(const char *action, struct ref *ref, struct ref_transaction *transaction, @@ -650,7 +647,6 @@ static int s_update_ref(const char *action, { char *msg; char *rla = getenv("GIT_REFLOG_ACTION"); - struct ref_transaction *our_transaction = NULL; struct strbuf err = STRBUF_INIT; int ret; @@ -660,43 +656,10 @@ static int s_update_ref(const char *action, rla = default_rla.buf; msg = xstrfmt("%s: %s", rla, action); - /* - * If no transaction was passed to us, we manage the transaction - * ourselves. Otherwise, we trust the caller to handle the transaction - * lifecycle. - */ - if (!transaction) { - transaction = our_transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), - 0, &err); - if (!transaction) { - ret = STORE_REF_ERROR_OTHER; - goto out; - } - } - ret = ref_transaction_update(transaction, ref->name, &ref->new_oid, check_old ? &ref->old_oid : NULL, NULL, NULL, 0, msg, &err); - if (ret) { - ret = STORE_REF_ERROR_OTHER; - goto out; - } - - if (our_transaction) { - switch (ref_transaction_commit(our_transaction, &err)) { - case 0: - break; - case REF_TRANSACTION_ERROR_NAME_CONFLICT: - ret = STORE_REF_ERROR_DF_CONFLICT; - goto out; - default: - ret = STORE_REF_ERROR_OTHER; - goto out; - } - } -out: - ref_transaction_free(our_transaction); if (ret) error("%s", err.buf); strbuf_release(&err); @@ -910,8 +873,8 @@ static int update_local_ref(struct ref *ref, struct commit *current = NULL, *updated; int fast_forward = 0; - if (!has_object(the_repository, &ref->new_oid, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) + if (!odb_has_object(the_repository->objects, &ref->new_oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) die(_("object %s not found"), oid_to_hex(&ref->new_oid)); if (oideq(&ref->old_oid, &ref->new_oid)) { @@ -992,7 +955,7 @@ static int update_local_ref(struct ref *ref, fast_forward = repo_in_merge_bases(the_repository, current, updated); if (fast_forward < 0) - exit(128); + die(NULL); forced_updates_ms += (getnanotime() - t_before) / 1000000; } else { fast_forward = 1; @@ -1139,7 +1102,6 @@ N_("it took %.2f seconds to check forced updates; you can use\n" "to avoid this check\n"); static int store_updated_refs(struct display_state *display_state, - const char *remote_name, int connectivity_checked, struct ref_transaction *transaction, struct ref *ref_map, struct fetch_head *fetch_head, @@ -1277,11 +1239,6 @@ static int store_updated_refs(struct display_state *display_state, } } - if (rc & STORE_REF_ERROR_DF_CONFLICT) - error(_("some local refs could not be updated; try running\n" - " 'git remote prune %s' to remove any old, conflicting " - "branches"), remote_name); - if (advice_enabled(ADVICE_FETCH_SHOW_FORCED_UPDATES)) { if (!config->show_forced_updates) { warning(_(warn_show_forced_updates)); @@ -1330,7 +1287,8 @@ static int check_exist_and_connected(struct ref *ref_map) * we need all direct targets to exist. */ for (r = rm; r; r = r->next) { - if (!has_object(the_repository, &r->old_oid, HAS_OBJECT_RECHECK_PACKED)) + if (!odb_has_object(the_repository->objects, &r->old_oid, + HAS_OBJECT_RECHECK_PACKED)) return -1; } @@ -1365,9 +1323,8 @@ static int fetch_and_consume_refs(struct display_state *display_state, } trace2_region_enter("fetch", "consume_refs", the_repository); - ret = store_updated_refs(display_state, transport->remote->name, - connectivity_checked, transaction, ref_map, - fetch_head, config); + ret = store_updated_refs(display_state, connectivity_checked, + transaction, ref_map, fetch_head, config); trace2_region_leave("fetch", "consume_refs", the_repository); out: @@ -1383,9 +1340,10 @@ static int prune_refs(struct display_state *display_state, int result = 0; struct ref *ref, *stale_refs = get_stale_heads(rs, ref_map); struct strbuf err = STRBUF_INIT; - const char *dangling_msg = dry_run - ? _(" (%s will become dangling)") - : _(" (%s has become dangling)"); + struct string_list refnames = STRING_LIST_INIT_NODUP; + + for (ref = stale_refs; ref; ref = ref->next) + string_list_append(&refnames, ref->name); if (!dry_run) { if (transaction) { @@ -1396,15 +1354,9 @@ static int prune_refs(struct display_state *display_state, goto cleanup; } } else { - struct string_list refnames = STRING_LIST_INIT_NODUP; - - for (ref = stale_refs; ref; ref = ref->next) - string_list_append(&refnames, ref->name); - result = refs_delete_refs(get_main_ref_store(the_repository), "fetch: prune", &refnames, 0); - string_list_clear(&refnames, 0); } } @@ -1416,12 +1368,14 @@ static int prune_refs(struct display_state *display_state, _("(none)"), ref->name, &ref->new_oid, &ref->old_oid, summary_width); - refs_warn_dangling_symref(get_main_ref_store(the_repository), - stderr, dangling_msg, ref->name); } + string_list_sort(&refnames); + refs_warn_dangling_symrefs(get_main_ref_store(the_repository), + stderr, " ", dry_run, &refnames); } cleanup: + string_list_clear(&refnames, 0); strbuf_release(&err); free_refs(stale_refs); return result; @@ -1485,7 +1439,7 @@ static void add_negotiation_tips(struct git_transport_options *smart_options) struct object_id oid; if (repo_get_oid(the_repository, s, &oid)) die(_("%s is not a valid object"), s); - if (!has_object(the_repository, &oid, 0)) + if (!odb_has_object(the_repository->objects, &oid, 0)) die(_("the object %s does not exist"), s); oid_array_append(oids, &oid); continue; @@ -1687,6 +1641,36 @@ cleanup: return result; } +struct ref_rejection_data { + int *retcode; + int conflict_msg_shown; + const char *remote_name; +}; + +static void ref_transaction_rejection_handler(const char *refname, + const struct object_id *old_oid UNUSED, + const struct object_id *new_oid UNUSED, + const char *old_target UNUSED, + const char *new_target UNUSED, + enum ref_transaction_error err, + void *cb_data) +{ + struct ref_rejection_data *data = cb_data; + + if (err == REF_TRANSACTION_ERROR_NAME_CONFLICT && !data->conflict_msg_shown) { + error(_("some local refs could not be updated; try running\n" + " 'git remote prune %s' to remove any old, conflicting " + "branches"), data->remote_name); + data->conflict_msg_shown = 1; + } else { + const char *reason = ref_transaction_error_msg(err); + + error(_("fetching ref %s failed: %s"), refname, reason); + } + + *data->retcode = 1; +} + static int do_fetch(struct transport *transport, struct refspec *rs, const struct fetch_config *config) @@ -1807,6 +1791,24 @@ static int do_fetch(struct transport *transport, retcode = 1; } + /* + * If not atomic, we can still use batched updates, which would be much + * more performant. We don't initiate the transaction before pruning, + * since pruning must be an independent step, to avoid F/D conflicts. + * + * TODO: if reference transactions gain logical conflict resolution, we + * can delete and create refs (with F/D conflicts) in the same transaction + * and this can be moved above the 'prune_refs()' block. + */ + if (!transaction) { + transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), + REF_TRANSACTION_ALLOW_FAILURE, &err); + if (!transaction) { + retcode = -1; + goto cleanup; + } + } + if (fetch_and_consume_refs(&display_state, transport, transaction, ref_map, &fetch_head, config)) { retcode = 1; @@ -1838,16 +1840,31 @@ static int do_fetch(struct transport *transport, free_refs(tags_ref_map); } - if (transaction) { - if (retcode) - goto cleanup; + if (retcode) + goto cleanup; - retcode = ref_transaction_commit(transaction, &err); + retcode = ref_transaction_commit(transaction, &err); + if (retcode) { + /* + * Explicitly handle transaction cleanup to avoid + * aborting an already closed transaction. + */ + ref_transaction_free(transaction); + transaction = NULL; + goto cleanup; + } + + if (!atomic_fetch) { + struct ref_rejection_data data = { + .retcode = &retcode, + .conflict_msg_shown = 0, + .remote_name = transport->remote->name, + }; + + ref_transaction_for_each_rejected_update(transaction, + ref_transaction_rejection_handler, + &data); if (retcode) { - /* - * Explicitly handle transaction cleanup to avoid - * aborting an already closed transaction. - */ ref_transaction_free(transaction); transaction = NULL; goto cleanup; @@ -1978,7 +1995,7 @@ static int add_remote_or_group(const char *name, struct string_list *list) struct remote_group_data g; g.name = name; g.list = list; - git_config(get_remote_group, &g); + repo_config(the_repository, get_remote_group, &g); if (list->nr == prev_nr) { struct remote *remote = remote_get(name); if (!remote_is_configured(remote, 0)) @@ -2400,7 +2417,7 @@ int cmd_fetch(int argc, free(anon); } - git_config(git_fetch_config, &config); + repo_config(the_repository, git_fetch_config, &config); if (the_repository->gitdir) { prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; @@ -2491,7 +2508,7 @@ int cmd_fetch(int argc, if (!max_jobs) max_jobs = online_cpus(); - if (!git_config_get_string_tmp("fetch.bundleuri", &bundle_uri) && + if (!repo_config_get_string_tmp(the_repository, "fetch.bundleuri", &bundle_uri) && fetch_bundle_uri(the_repository, bundle_uri, NULL)) warning(_("failed to fetch bundles from '%s'"), bundle_uri); @@ -2653,7 +2670,7 @@ int cmd_fetch(int argc, commit_graph_flags |= COMMIT_GRAPH_WRITE_PROGRESS; trace2_region_enter("fetch", "write-commit-graph", the_repository); - write_commit_graph_reachable(the_repository->objects->odb, + write_commit_graph_reachable(the_repository->objects->sources, commit_graph_flags, NULL); trace2_region_leave("fetch", "write-commit-graph", the_repository); @@ -2666,12 +2683,12 @@ int cmd_fetch(int argc, * but respect config settings disabling it. */ int opt_val; - if (git_config_get_int("gc.autopacklimit", &opt_val)) + if (repo_config_get_int(the_repository, "gc.autopacklimit", &opt_val)) opt_val = -1; if (opt_val != 0) git_config_push_parameter("gc.autoPackLimit=1"); - if (git_config_get_int("maintenance.incremental-repack.auto", &opt_val)) + if (repo_config_get_int(the_repository, "maintenance.incremental-repack.auto", &opt_val)) opt_val = -1; if (opt_val != 0) git_config_push_parameter("maintenance.incremental-repack.auto=-1"); diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c index 3b6aac2cf7..cf4273a52c 100644 --- a/builtin/fmt-merge-msg.c +++ b/builtin/fmt-merge-msg.c @@ -1,4 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" #include "config.h" #include "fmt-merge-msg.h" @@ -13,12 +12,13 @@ static const char * const fmt_merge_msg_usage[] = { int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { char *inpath = NULL; const char *message = NULL; char *into_name = NULL; int shortlog_len = -1; + int merge_log_config = -1; struct option options[] = { { .type = OPTION_INTEGER, @@ -53,7 +53,7 @@ int cmd_fmt_merge_msg(int argc, int ret; struct fmt_merge_msg_opts opts; - git_config(fmt_merge_msg_config, NULL); + repo_config(repo, fmt_merge_msg_config, &merge_log_config); argc = parse_options(argc, argv, prefix, options, fmt_merge_msg_usage, 0); if (argc > 0) diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index 3d2207ec77..4a2fc421db 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -1,6 +1,8 @@ #include "builtin.h" #include "commit.h" #include "config.h" +#include "environment.h" +#include "for-each-ref.h" #include "gettext.h" #include "object.h" #include "parse-options.h" @@ -8,18 +10,7 @@ #include "strbuf.h" #include "strvec.h" -static char const * const for_each_ref_usage[] = { - N_("git for-each-ref [<options>] [<pattern>]"), - N_("git for-each-ref [--points-at <object>]"), - N_("git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]"), - N_("git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"), - NULL -}; - -int cmd_for_each_ref(int argc, - const char **argv, - const char *prefix, - struct repository *repo) +int for_each_ref_core(int argc, const char **argv, const char *prefix, struct repository *repo, const char *const *usage) { struct ref_sorting *sorting; struct string_list sorting_options = STRING_LIST_INIT_DUP; @@ -44,6 +35,7 @@ int cmd_for_each_ref(int argc, OPT_GROUP(""), OPT_INTEGER( 0 , "count", &format.array_opts.max_count, N_("show only <n> matched refs")), OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")), + OPT_STRING( 0 , "start-after", &filter.start_after, N_("marker"), N_("start iteration after the provided marker")), OPT__COLOR(&format.use_color, N_("respect format colors")), OPT_REF_FILTER_EXCLUDE(&filter), OPT_REF_SORT(&sorting_options), @@ -67,17 +59,20 @@ int cmd_for_each_ref(int argc, /* Set default (refname) sorting */ string_list_append(&sorting_options, "refname"); - parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0); + parse_options(argc, argv, prefix, opts, usage, 0); if (format.array_opts.max_count < 0) { error("invalid --count argument: `%d'", format.array_opts.max_count); - usage_with_options(for_each_ref_usage, opts); + usage_with_options(usage, opts); } if (HAS_MULTI_BITS(format.quote_style)) { error("more than one quoting style?"); - usage_with_options(for_each_ref_usage, opts); + usage_with_options(usage, opts); } if (verify_ref_format(&format)) - usage_with_options(for_each_ref_usage, opts); + usage_with_options(usage, opts); + + if (filter.start_after && sorting_options.nr > 1) + die(_("cannot use --start-after with custom sort options")); sorting = ref_sorting_options(&sorting_options); ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase); @@ -100,6 +95,9 @@ int cmd_for_each_ref(int argc, filter.name_patterns = argv; } + if (filter.start_after && filter.name_patterns && filter.name_patterns[0]) + die(_("cannot use --start-after with patterns")); + if (include_root_refs) flags |= FILTER_REFS_ROOT_REFS | FILTER_REFS_DETACHED_HEAD; @@ -111,3 +109,16 @@ int cmd_for_each_ref(int argc, strvec_clear(&vec); return 0; } + +int cmd_for_each_ref(int argc, + const char **argv, + const char *prefix, + struct repository *repo) +{ + static char const * const for_each_ref_usage[] = { + N_("git for-each-ref " COMMON_USAGE_FOR_EACH_REF), + NULL + }; + + return for_each_ref_core(argc, argv, prefix, repo, for_each_ref_usage); +} diff --git a/builtin/fsck.c b/builtin/fsck.c index e7d96a9c8e..d2eb9d4fbe 100644 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@ -17,7 +17,7 @@ #include "packfile.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "path.h" #include "read-cache-ll.h" #include "replace-object.h" @@ -71,7 +71,8 @@ static const char *printable_type(const struct object_id *oid, const char *ret; if (type == OBJ_NONE) - type = oid_object_info(the_repository, oid, NULL); + type = odb_read_object_info(the_repository->objects, + oid, NULL); ret = type_name(type); if (!ret) @@ -160,7 +161,7 @@ static int mark_object(struct object *obj, enum object_type type, return 0; if (!(obj->flags & HAS_OBJ)) { - if (parent && !has_object(the_repository, &obj->oid, 1)) { + if (parent && !odb_has_object(the_repository->objects, &obj->oid, 1)) { printf_ln(_("broken link from %7s %s\n" " to %7s %s"), printable_type(&parent->oid, parent->type), @@ -232,8 +233,8 @@ static void mark_unreachable_referents(const struct object_id *oid) * (and we want to avoid parsing blobs). */ if (obj->type == OBJ_NONE) { - enum object_type type = oid_object_info(the_repository, - &obj->oid, NULL); + enum object_type type = odb_read_object_info(the_repository->objects, + &obj->oid, NULL); if (type > 0) object_as_type(obj, type, 0); } @@ -392,7 +393,8 @@ static void check_connectivity(void) * and ignore any that weren't present in our earlier * traversal. */ - for_each_loose_object(mark_loose_unreachable_referents, NULL, 0); + for_each_loose_object(the_repository->objects, + mark_loose_unreachable_referents, NULL, 0); for_each_packed_object(the_repository, mark_packed_unreachable_referents, NULL, @@ -501,13 +503,12 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, } } -static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid, +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) + const char *message UNUSED, void *cb_data UNUSED) { - const char *refname = cb_data; - if (verbose) fprintf_ln(stderr, _("Checking reflog %s->%s"), oid_to_hex(ooid), oid_to_hex(noid)); @@ -524,7 +525,7 @@ static int fsck_handle_reflog(const char *logname, void *cb_data) strbuf_worktree_ref(cb_data, &refname, logname); refs_for_each_reflog_ent(get_main_ref_store(the_repository), refname.buf, fsck_handle_reflog_ent, - refname.buf); + NULL); strbuf_release(&refname); return 0; } @@ -631,7 +632,7 @@ static int fsck_loose(const struct object_id *oid, const char *path, oi.sizep = &size; oi.typep = &type; - if (read_loose_object(path, oid, &real_oid, &contents, &oi) < 0) { + if (read_loose_object(the_repository, 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); @@ -686,7 +687,7 @@ static int fsck_subdir(unsigned int nr, const char *path UNUSED, void *data) return 0; } -static void fsck_object_dir(const char *path) +static void fsck_source(struct odb_source *source) { struct progress *progress = NULL; struct for_each_loose_cb cb_data = { @@ -700,8 +701,8 @@ static void fsck_object_dir(const char *path) progress = start_progress(the_repository, _("Checking object directories"), 256); - for_each_loose_file_in_objdir(path, fsck_loose, fsck_cruft, fsck_subdir, - &cb_data); + for_each_loose_file_in_source(source, fsck_loose, + fsck_cruft, fsck_subdir, &cb_data); display_progress(progress, 256); stop_progress(&progress); } @@ -956,7 +957,7 @@ int cmd_fsck(int argc, struct repository *repo UNUSED) { int i; - struct object_directory *odb; + struct odb_source *source; /* fsck knows how to handle missing promisor objects */ fetch_if_missing = 0; @@ -986,20 +987,21 @@ int cmd_fsck(int argc, if (name_objects) fsck_enable_object_names(&fsck_walk_options); - git_config(git_fsck_config, &fsck_obj_options); + repo_config(the_repository, git_fsck_config, &fsck_obj_options); prepare_repo_settings(the_repository); if (check_references) fsck_refs(the_repository); if (connectivity_only) { - for_each_loose_object(mark_loose_for_connectivity, NULL, 0); + for_each_loose_object(the_repository->objects, + mark_loose_for_connectivity, NULL, 0); for_each_packed_object(the_repository, mark_packed_for_connectivity, NULL, 0); } else { - prepare_alt_odb(the_repository); - for (odb = the_repository->objects->odb; odb; odb = odb->next) - fsck_object_dir(odb->path); + odb_prepare_alternates(the_repository->objects); + for (source = the_repository->objects->sources; source; source = source->next) + fsck_source(source); if (check_full) { struct packed_git *p; @@ -1108,12 +1110,12 @@ int cmd_fsck(int argc, if (the_repository->settings.core_commit_graph) { struct child_process commit_graph_verify = CHILD_PROCESS_INIT; - prepare_alt_odb(the_repository); - for (odb = the_repository->objects->odb; odb; odb = odb->next) { + odb_prepare_alternates(the_repository->objects); + for (source = the_repository->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", - "verify", "--object-dir", odb->path, NULL); + "verify", "--object-dir", source->path, NULL); if (show_progress) strvec_push(&commit_graph_verify.args, "--progress"); else @@ -1126,12 +1128,12 @@ int cmd_fsck(int argc, if (the_repository->settings.core_multi_pack_index) { struct child_process midx_verify = CHILD_PROCESS_INIT; - prepare_alt_odb(the_repository); - for (odb = the_repository->objects->odb; odb; odb = odb->next) { + odb_prepare_alternates(the_repository->objects); + for (source = the_repository->objects->sources; source; source = source->next) { child_process_init(&midx_verify); midx_verify.git_cmd = 1; strvec_pushl(&midx_verify.args, "multi-pack-index", - "verify", "--object-dir", odb->path, NULL); + "verify", "--object-dir", source->path, NULL); if (show_progress) strvec_push(&midx_verify.args, "--progress"); else diff --git a/builtin/fsmonitor--daemon.c b/builtin/fsmonitor--daemon.c index 0820e524f1..242c594646 100644 --- a/builtin/fsmonitor--daemon.c +++ b/builtin/fsmonitor--daemon.c @@ -5,6 +5,7 @@ #include "abspath.h" #include "config.h" #include "dir.h" +#include "environment.h" #include "gettext.h" #include "parse-options.h" #include "fsmonitor-ll.h" @@ -1547,7 +1548,7 @@ int cmd_fsmonitor__daemon(int argc, OPT_END() }; - git_config(fsmonitor_config, NULL); + repo_config(the_repository, fsmonitor_config, NULL); argc = parse_options(argc, argv, prefix, options, builtin_fsmonitor__daemon_usage, 0); diff --git a/builtin/gc.c b/builtin/gc.c index 7dc94f243d..03ae4926b2 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -114,7 +114,7 @@ static int gc_config_is_timestamp_never(const char *var) const char *value; timestamp_t expire; - if (!git_config_get_value(var, &value) && value) { + if (!repo_config_get_value(the_repository, var, &value) && value) { if (parse_expiry_date(value, &expire)) die(_("failed to parse '%s' value '%s'"), var, value); return expire == 0; @@ -178,7 +178,7 @@ static void gc_config(struct gc_config *cfg) char *owned = NULL; unsigned long ulongval; - if (!git_config_get_value("gc.packrefs", &value)) { + if (!repo_config_get_value(the_repository, "gc.packrefs", &value)) { if (value && !strcmp(value, "notbare")) cfg->pack_refs = -1; else @@ -189,13 +189,13 @@ static void gc_config(struct gc_config *cfg) gc_config_is_timestamp_never("gc.reflogexpireunreachable")) cfg->prune_reflogs = 0; - git_config_get_int("gc.aggressivewindow", &cfg->aggressive_window); - git_config_get_int("gc.aggressivedepth", &cfg->aggressive_depth); - git_config_get_int("gc.auto", &cfg->gc_auto_threshold); - git_config_get_int("gc.autopacklimit", &cfg->gc_auto_pack_limit); - git_config_get_bool("gc.autodetach", &cfg->detach_auto); - git_config_get_bool("gc.cruftpacks", &cfg->cruft_packs); - git_config_get_ulong("gc.maxcruftsize", &cfg->max_cruft_size); + repo_config_get_int(the_repository, "gc.aggressivewindow", &cfg->aggressive_window); + repo_config_get_int(the_repository, "gc.aggressivedepth", &cfg->aggressive_depth); + repo_config_get_int(the_repository, "gc.auto", &cfg->gc_auto_threshold); + repo_config_get_int(the_repository, "gc.autopacklimit", &cfg->gc_auto_pack_limit); + repo_config_get_bool(the_repository, "gc.autodetach", &cfg->detach_auto); + repo_config_get_bool(the_repository, "gc.cruftpacks", &cfg->cruft_packs); + repo_config_get_ulong(the_repository, "gc.maxcruftsize", &cfg->max_cruft_size); if (!repo_config_get_expiry(the_repository, "gc.pruneexpire", &owned)) { free(cfg->prune_expire); @@ -212,23 +212,23 @@ static void gc_config(struct gc_config *cfg) cfg->gc_log_expire = owned; } - git_config_get_ulong("gc.bigpackthreshold", &cfg->big_pack_threshold); - git_config_get_ulong("pack.deltacachesize", &cfg->max_delta_cache_size); + repo_config_get_ulong(the_repository, "gc.bigpackthreshold", &cfg->big_pack_threshold); + repo_config_get_ulong(the_repository, "pack.deltacachesize", &cfg->max_delta_cache_size); - if (!git_config_get_ulong("core.deltabasecachelimit", &ulongval)) + if (!repo_config_get_ulong(the_repository, "core.deltabasecachelimit", &ulongval)) cfg->delta_base_cache_limit = ulongval; - if (!git_config_get_string("gc.repackfilter", &owned)) { + if (!repo_config_get_string(the_repository, "gc.repackfilter", &owned)) { free(cfg->repack_filter); cfg->repack_filter = owned; } - if (!git_config_get_string("gc.repackfilterto", &owned)) { + if (!repo_config_get_string(the_repository, "gc.repackfilterto", &owned)) { free(cfg->repack_filter_to); cfg->repack_filter_to = owned; } - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); } enum schedule_priority { @@ -251,7 +251,24 @@ static enum schedule_priority parse_schedule(const char *value) return SCHEDULE_NONE; } +enum maintenance_task_label { + TASK_PREFETCH, + TASK_LOOSE_OBJECTS, + TASK_INCREMENTAL_REPACK, + TASK_GC, + TASK_COMMIT_GRAPH, + TASK_PACK_REFS, + TASK_REFLOG_EXPIRE, + TASK_WORKTREE_PRUNE, + TASK_RERERE_GC, + + /* Leave as final value */ + TASK__COUNT +}; + struct maintenance_run_opts { + enum maintenance_task_label *tasks; + size_t tasks_nr, tasks_alloc; int auto_flag; int detach; int quiet; @@ -261,6 +278,11 @@ struct maintenance_run_opts { .detach = -1, \ } +static void maintenance_run_opts_release(struct maintenance_run_opts *opts) +{ + free(opts->tasks); +} + static int pack_refs_condition(UNUSED struct gc_config *cfg) { /* @@ -290,7 +312,8 @@ struct count_reflog_entries_data { size_t limit; }; -static int count_reflog_entries(struct object_id *old_oid, struct object_id *new_oid, +static int count_reflog_entries(const char *refname UNUSED, + struct object_id *old_oid, struct object_id *new_oid, const char *committer, timestamp_t timestamp, int tz, const char *msg, void *cb_data) { @@ -310,7 +333,7 @@ static int reflog_expire_condition(struct gc_config *cfg UNUSED) }; int limit = 100; - git_config_get_int("maintenance.reflog-expire.auto", &limit); + repo_config_get_int(the_repository, "maintenance.reflog-expire.auto", &limit); if (!limit) return 0; if (limit < 0) @@ -324,6 +347,7 @@ static int reflog_expire_condition(struct gc_config *cfg UNUSED) count_reflog_entries, &data); reflog_expiry_cleanup(&data.policy); + reflog_clear_expire_config(&data.policy.opts); return data.count >= data.limit; } @@ -356,7 +380,7 @@ static int worktree_prune_condition(struct gc_config *cfg) struct dirent *d; DIR *dir = NULL; - git_config_get_int("maintenance.worktree-prune.auto", &limit); + repo_config_get_int(the_repository, "maintenance.worktree-prune.auto", &limit); if (limit <= 0) { should_prune = limit < 0; goto out; @@ -401,7 +425,7 @@ static int rerere_gc_condition(struct gc_config *cfg UNUSED) int should_gc = 0, limit = 1; DIR *dir = NULL; - git_config_get_int("maintenance.rerere-gc.auto", &limit); + repo_config_get_int(the_repository, "maintenance.rerere-gc.auto", &limit); if (limit <= 0) { should_gc = limit < 0; goto out; @@ -517,7 +541,7 @@ static uint64_t total_ram(void) return total; } #elif defined(HAVE_BSD_SYSCTL) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM) || defined(HW_PHYSMEM64)) - int64_t physical_memory; + uint64_t physical_memory; int mib[2]; size_t length; @@ -529,9 +553,16 @@ static uint64_t total_ram(void) # else mib[1] = HW_PHYSMEM; # endif - length = sizeof(int64_t); - if (!sysctl(mib, 2, &physical_memory, &length, NULL, 0)) + length = sizeof(physical_memory); + if (!sysctl(mib, 2, &physical_memory, &length, NULL, 0)) { + if (length == 4) { + uint32_t mem; + + if (!sysctl(mib, 2, &mem, &length, NULL, 0)) + physical_memory = mem; + } return physical_memory; + } #elif defined(GIT_WINDOWS_NATIVE) MEMORYSTATUSEX memInfo; @@ -796,22 +827,14 @@ done: return ret; } -static void gc_before_repack(struct maintenance_run_opts *opts, - struct gc_config *cfg) +static int gc_foreground_tasks(struct maintenance_run_opts *opts, + struct gc_config *cfg) { - /* - * We may be called twice, as both the pre- and - * post-daemonized phases will call us, but running these - * commands more than once is pointless and wasteful. - */ - static int done = 0; - if (done++) - return; - if (cfg->pack_refs && maintenance_task_pack_refs(opts, cfg)) - die(FAILED_RUN, "pack-refs"); + return error(FAILED_RUN, "pack-refs"); if (cfg->prune_reflogs && maintenance_task_reflog_expire(opts, cfg)) - die(FAILED_RUN, "reflog"); + return error(FAILED_RUN, "reflog"); + return 0; } int cmd_gc(int argc, @@ -820,12 +843,12 @@ int cmd_gc(int argc, struct repository *repo UNUSED) { int aggressive = 0; - int quiet = 0; int force = 0; const char *name; pid_t pid; int daemonized = 0; int keep_largest_pack = -1; + int skip_foreground_tasks = 0; timestamp_t dummy; struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT; struct gc_config cfg = GC_CONFIG_INIT; @@ -833,7 +856,7 @@ int cmd_gc(int argc, const char *prune_expire_arg = prune_expire_sentinel; int ret; struct option builtin_gc_options[] = { - OPT__QUIET(&quiet, N_("suppress progress reporting")), + OPT__QUIET(&opts.quiet, N_("suppress progress reporting")), { .type = OPTION_STRING, .long_name = "prune", @@ -858,6 +881,8 @@ int cmd_gc(int argc, N_("repack all other packs except the largest pack")), OPT_STRING(0, "expire-to", &cfg.repack_expire_to, N_("dir"), N_("pack prefix to store a pack containing pruned objects")), + OPT_HIDDEN_BOOL(0, "skip-foreground-tasks", &skip_foreground_tasks, + N_("skip maintenance tasks typically done in the foreground")), OPT_END() }; @@ -893,7 +918,7 @@ int cmd_gc(int argc, if (cfg.aggressive_window > 0) strvec_pushf(&repack, "--window=%d", cfg.aggressive_window); } - if (quiet) + if (opts.quiet) strvec_push(&repack, "-q"); if (opts.auto_flag) { @@ -908,7 +933,7 @@ int cmd_gc(int argc, goto out; } - if (!quiet) { + if (!opts.quiet) { if (opts.detach > 0) fprintf(stderr, _("Auto packing the repository in background for optimum performance.\n")); else @@ -941,13 +966,16 @@ int cmd_gc(int argc, goto out; } - if (lock_repo_for_gc(force, &pid)) { - ret = 0; - goto out; - } + if (!skip_foreground_tasks) { + if (lock_repo_for_gc(force, &pid)) { + ret = 0; + goto out; + } - gc_before_repack(&opts, &cfg); /* dies on failure */ - delete_tempfile(&pidfile); + if (gc_foreground_tasks(&opts, &cfg) < 0) + die(NULL); + delete_tempfile(&pidfile); + } /* * failure to daemonize is ok, we'll continue @@ -976,9 +1004,10 @@ int cmd_gc(int argc, free(path); } - gc_before_repack(&opts, &cfg); + if (opts.detach <= 0 && !skip_foreground_tasks) + gc_foreground_tasks(&opts, &cfg); - if (!repository_format_precious_objects) { + if (!the_repository->repository_format_precious_objects) { struct child_process repack_cmd = CHILD_PROCESS_INIT; repack_cmd.git_cmd = 1; @@ -993,7 +1022,7 @@ int cmd_gc(int argc, strvec_pushl(&prune_cmd.args, "prune", "--expire", NULL); /* run `git prune` even if using cruft packs */ strvec_push(&prune_cmd.args, cfg.prune_expire); - if (quiet) + if (opts.quiet) strvec_push(&prune_cmd.args, "--no-progress"); if (repo_has_promisor_remote(the_repository)) strvec_push(&prune_cmd.args, @@ -1020,8 +1049,8 @@ int cmd_gc(int argc, } if (the_repository->settings.gc_write_commit_graph == 1) - write_commit_graph_reachable(the_repository->objects->odb, - !quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0, + write_commit_graph_reachable(the_repository->objects->sources, + !opts.quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0, NULL); if (opts.auto_flag && too_many_loose_objects(&cfg)) @@ -1035,6 +1064,7 @@ int cmd_gc(int argc, } out: + maintenance_run_opts_release(&opts); gc_config_release(&cfg); return 0; } @@ -1082,7 +1112,7 @@ static int dfs_on_ref(const char *refname UNUSED, if (!peel_iterated_oid(the_repository, oid, &peeled)) oid = &peeled; - if (oid_object_info(the_repository, oid, NULL) != OBJ_COMMIT) + if (odb_read_object_info(the_repository->objects, oid, NULL) != OBJ_COMMIT) return 0; commit = lookup_commit(the_repository, oid); @@ -1133,8 +1163,8 @@ static int should_write_commit_graph(struct gc_config *cfg UNUSED) data.num_not_in_graph = 0; data.limit = 100; - git_config_get_int("maintenance.commit-graph.auto", - &data.limit); + repo_config_get_int(the_repository, "maintenance.commit-graph.auto", + &data.limit); if (!data.limit) return 0; @@ -1211,8 +1241,14 @@ static int maintenance_task_prefetch(struct maintenance_run_opts *opts, return 0; } -static int maintenance_task_gc(struct maintenance_run_opts *opts, - struct gc_config *cfg UNUSED) +static int maintenance_task_gc_foreground(struct maintenance_run_opts *opts, + struct gc_config *cfg) +{ + return gc_foreground_tasks(opts, cfg); +} + +static int maintenance_task_gc_background(struct maintenance_run_opts *opts, + struct gc_config *cfg UNUSED) { struct child_process child = CHILD_PROCESS_INIT; @@ -1226,6 +1262,7 @@ static int maintenance_task_gc(struct maintenance_run_opts *opts, else strvec_push(&child.args, "--no-quiet"); strvec_push(&child.args, "--no-detach"); + strvec_push(&child.args, "--skip-foreground-tasks"); return run_command(&child); } @@ -1265,15 +1302,15 @@ static int loose_object_auto_condition(struct gc_config *cfg UNUSED) { int count = 0; - git_config_get_int("maintenance.loose-objects.auto", - &loose_object_auto_limit); + repo_config_get_int(the_repository, "maintenance.loose-objects.auto", + &loose_object_auto_limit); if (!loose_object_auto_limit) return 0; if (loose_object_auto_limit < 0) return 1; - return for_each_loose_file_in_objdir(the_repository->objects->odb->path, + return for_each_loose_file_in_source(the_repository->objects->sources, loose_object_count, NULL, NULL, &count); } @@ -1308,7 +1345,7 @@ static int pack_loose(struct maintenance_run_opts *opts) * Do not start pack-objects process * if there are no loose objects. */ - if (!for_each_loose_file_in_objdir(r->objects->odb->path, + if (!for_each_loose_file_in_source(r->objects->sources, bail_on_loose, NULL, NULL, NULL)) return 0; @@ -1320,7 +1357,7 @@ static int pack_loose(struct maintenance_run_opts *opts) strvec_push(&pack_proc.args, "--quiet"); else strvec_push(&pack_proc.args, "--no-quiet"); - strvec_pushf(&pack_proc.args, "%s/pack/loose", r->objects->odb->path); + strvec_pushf(&pack_proc.args, "%s/pack/loose", r->objects->sources->path); pack_proc.in = -1; @@ -1348,11 +1385,9 @@ static int pack_loose(struct maintenance_run_opts *opts) else if (data.batch_size > 0) data.batch_size--; /* Decrease for equality on limit. */ - for_each_loose_file_in_objdir(r->objects->odb->path, + for_each_loose_file_in_source(r->objects->sources, write_loose_object_to_stdin, - NULL, - NULL, - &data); + NULL, NULL, &data); fclose(data.in); @@ -1380,8 +1415,8 @@ static int incremental_repack_auto_condition(struct gc_config *cfg UNUSED) if (!the_repository->settings.core_multi_pack_index) return 0; - git_config_get_int("maintenance.incremental-repack.auto", - &incremental_repack_auto_limit); + repo_config_get_int(the_repository, "maintenance.incremental-repack.auto", + &incremental_repack_auto_limit); if (!incremental_repack_auto_limit) return 0; @@ -1513,107 +1548,120 @@ static int maintenance_task_incremental_repack(struct maintenance_run_opts *opts return 0; } -typedef int maintenance_task_fn(struct maintenance_run_opts *opts, - struct gc_config *cfg); - -/* - * An auto condition function returns 1 if the task should run - * and 0 if the task should NOT run. See needs_to_gc() for an - * example. - */ -typedef int maintenance_auto_fn(struct gc_config *cfg); +typedef int (*maintenance_task_fn)(struct maintenance_run_opts *opts, + struct gc_config *cfg); +typedef int (*maintenance_auto_fn)(struct gc_config *cfg); struct maintenance_task { const char *name; - maintenance_task_fn *fn; - maintenance_auto_fn *auto_condition; - unsigned enabled:1; - - enum schedule_priority schedule; - /* -1 if not selected. */ - int selected_order; -}; + /* + * Work that will be executed before detaching. This should not include + * tasks that may run for an extended amount of time as it does cause + * auto-maintenance to block until foreground tasks have been run. + */ + maintenance_task_fn foreground; -enum maintenance_task_label { - TASK_PREFETCH, - TASK_LOOSE_OBJECTS, - TASK_INCREMENTAL_REPACK, - TASK_GC, - TASK_COMMIT_GRAPH, - TASK_PACK_REFS, - TASK_REFLOG_EXPIRE, - TASK_WORKTREE_PRUNE, - TASK_RERERE_GC, + /* + * Work that will be executed after detaching. When not detaching the + * work will be run in the foreground, as well. + */ + maintenance_task_fn background; - /* Leave as final value */ - TASK__COUNT + /* + * An auto condition function returns 1 if the task should run and 0 if + * the task should NOT run. See needs_to_gc() for an example. + */ + maintenance_auto_fn auto_condition; }; -static struct maintenance_task tasks[] = { +static const struct maintenance_task tasks[] = { [TASK_PREFETCH] = { - "prefetch", - maintenance_task_prefetch, + .name = "prefetch", + .background = maintenance_task_prefetch, }, [TASK_LOOSE_OBJECTS] = { - "loose-objects", - maintenance_task_loose_objects, - loose_object_auto_condition, + .name = "loose-objects", + .background = maintenance_task_loose_objects, + .auto_condition = loose_object_auto_condition, }, [TASK_INCREMENTAL_REPACK] = { - "incremental-repack", - maintenance_task_incremental_repack, - incremental_repack_auto_condition, + .name = "incremental-repack", + .background = maintenance_task_incremental_repack, + .auto_condition = incremental_repack_auto_condition, }, [TASK_GC] = { - "gc", - maintenance_task_gc, - need_to_gc, - 1, + .name = "gc", + .foreground = maintenance_task_gc_foreground, + .background = maintenance_task_gc_background, + .auto_condition = need_to_gc, }, [TASK_COMMIT_GRAPH] = { - "commit-graph", - maintenance_task_commit_graph, - should_write_commit_graph, + .name = "commit-graph", + .background = maintenance_task_commit_graph, + .auto_condition = should_write_commit_graph, }, [TASK_PACK_REFS] = { - "pack-refs", - maintenance_task_pack_refs, - pack_refs_condition, + .name = "pack-refs", + .foreground = maintenance_task_pack_refs, + .auto_condition = pack_refs_condition, }, [TASK_REFLOG_EXPIRE] = { - "reflog-expire", - maintenance_task_reflog_expire, - reflog_expire_condition, + .name = "reflog-expire", + .foreground = maintenance_task_reflog_expire, + .auto_condition = reflog_expire_condition, }, [TASK_WORKTREE_PRUNE] = { - "worktree-prune", - maintenance_task_worktree_prune, - worktree_prune_condition, + .name = "worktree-prune", + .background = maintenance_task_worktree_prune, + .auto_condition = worktree_prune_condition, }, [TASK_RERERE_GC] = { - "rerere-gc", - maintenance_task_rerere_gc, - rerere_gc_condition, + .name = "rerere-gc", + .background = maintenance_task_rerere_gc, + .auto_condition = rerere_gc_condition, }, }; -static int compare_tasks_by_selection(const void *a_, const void *b_) +enum task_phase { + TASK_PHASE_FOREGROUND, + TASK_PHASE_BACKGROUND, +}; + +static int maybe_run_task(const struct maintenance_task *task, + struct repository *repo, + struct maintenance_run_opts *opts, + struct gc_config *cfg, + enum task_phase phase) { - const struct maintenance_task *a = a_; - const struct maintenance_task *b = b_; + int foreground = (phase == TASK_PHASE_FOREGROUND); + maintenance_task_fn fn = foreground ? task->foreground : task->background; + const char *region = foreground ? "maintenance foreground" : "maintenance"; + int ret = 0; - return b->selected_order - a->selected_order; + if (!fn) + return 0; + if (opts->auto_flag && + (!task->auto_condition || !task->auto_condition(cfg))) + return 0; + + trace2_region_enter(region, task->name, repo); + if (fn(opts, cfg)) { + error(_("task '%s' failed"), task->name); + ret = 1; + } + trace2_region_leave(region, task->name, repo); + + return ret; } static int maintenance_run_tasks(struct maintenance_run_opts *opts, struct gc_config *cfg) { - int i, found_selected = 0; int result = 0; struct lock_file lk; struct repository *r = the_repository; - char *lock_path = xstrfmt("%s/maintenance", r->objects->odb->path); + char *lock_path = xstrfmt("%s/maintenance", r->objects->sources->path); if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0) { /* @@ -1631,6 +1679,11 @@ static int maintenance_run_tasks(struct maintenance_run_opts *opts, } free(lock_path); + for (size_t i = 0; i < opts->tasks_nr; i++) + if (maybe_run_task(&tasks[opts->tasks[i]], r, opts, cfg, + TASK_PHASE_FOREGROUND)) + result = 1; + /* Failure to daemonize is ok, we'll continue in foreground. */ if (opts->detach > 0) { trace2_region_enter("maintenance", "detach", the_repository); @@ -1638,120 +1691,138 @@ static int maintenance_run_tasks(struct maintenance_run_opts *opts, trace2_region_leave("maintenance", "detach", the_repository); } - for (i = 0; !found_selected && i < TASK__COUNT; i++) - found_selected = tasks[i].selected_order >= 0; - - if (found_selected) - QSORT(tasks, TASK__COUNT, compare_tasks_by_selection); - - for (i = 0; i < TASK__COUNT; i++) { - if (found_selected && tasks[i].selected_order < 0) - continue; - - if (!found_selected && !tasks[i].enabled) - continue; - - if (opts->auto_flag && - (!tasks[i].auto_condition || - !tasks[i].auto_condition(cfg))) - continue; - - if (opts->schedule && tasks[i].schedule < opts->schedule) - continue; - - trace2_region_enter("maintenance", tasks[i].name, r); - if (tasks[i].fn(opts, cfg)) { - error(_("task '%s' failed"), tasks[i].name); + for (size_t i = 0; i < opts->tasks_nr; i++) + if (maybe_run_task(&tasks[opts->tasks[i]], r, opts, cfg, + TASK_PHASE_BACKGROUND)) result = 1; - } - trace2_region_leave("maintenance", tasks[i].name, r); - } rollback_lock_file(&lk); return result; } -static void initialize_maintenance_strategy(void) +struct maintenance_strategy { + struct { + int enabled; + enum schedule_priority schedule; + } tasks[TASK__COUNT]; +}; + +static const struct maintenance_strategy none_strategy = { 0 }; +static const struct maintenance_strategy default_strategy = { + .tasks = { + [TASK_GC].enabled = 1, + }, +}; +static const struct maintenance_strategy incremental_strategy = { + .tasks = { + [TASK_COMMIT_GRAPH].enabled = 1, + [TASK_COMMIT_GRAPH].schedule = SCHEDULE_HOURLY, + [TASK_PREFETCH].enabled = 1, + [TASK_PREFETCH].schedule = SCHEDULE_HOURLY, + [TASK_INCREMENTAL_REPACK].enabled = 1, + [TASK_INCREMENTAL_REPACK].schedule = SCHEDULE_DAILY, + [TASK_LOOSE_OBJECTS].enabled = 1, + [TASK_LOOSE_OBJECTS].schedule = SCHEDULE_DAILY, + [TASK_PACK_REFS].enabled = 1, + [TASK_PACK_REFS].schedule = SCHEDULE_WEEKLY, + }, +}; + +static void initialize_task_config(struct maintenance_run_opts *opts, + const struct string_list *selected_tasks) { + struct strbuf config_name = STRBUF_INIT; + struct maintenance_strategy strategy; const char *config_str; - if (git_config_get_string_tmp("maintenance.strategy", &config_str)) - return; + /* + * In case the user has asked us to run tasks explicitly we only use + * those specified tasks. Specifically, we do _not_ want to consult the + * config or maintenance strategy. + */ + if (selected_tasks->nr) { + for (size_t i = 0; i < selected_tasks->nr; i++) { + enum maintenance_task_label label = (intptr_t)selected_tasks->items[i].util;; + ALLOC_GROW(opts->tasks, opts->tasks_nr + 1, opts->tasks_alloc); + opts->tasks[opts->tasks_nr++] = label; + } - if (!strcasecmp(config_str, "incremental")) { - tasks[TASK_GC].schedule = SCHEDULE_NONE; - tasks[TASK_COMMIT_GRAPH].enabled = 1; - tasks[TASK_COMMIT_GRAPH].schedule = SCHEDULE_HOURLY; - tasks[TASK_PREFETCH].enabled = 1; - tasks[TASK_PREFETCH].schedule = SCHEDULE_HOURLY; - tasks[TASK_INCREMENTAL_REPACK].enabled = 1; - tasks[TASK_INCREMENTAL_REPACK].schedule = SCHEDULE_DAILY; - tasks[TASK_LOOSE_OBJECTS].enabled = 1; - tasks[TASK_LOOSE_OBJECTS].schedule = SCHEDULE_DAILY; - tasks[TASK_PACK_REFS].enabled = 1; - tasks[TASK_PACK_REFS].schedule = SCHEDULE_WEEKLY; + return; } -} -static void initialize_task_config(int schedule) -{ - int i; - struct strbuf config_name = STRBUF_INIT; + /* + * Otherwise, the strategy depends on whether we run as part of a + * scheduled job or not: + * + * - Scheduled maintenance does not perform any housekeeping by + * default, but requires the user to pick a maintenance strategy. + * + * - Unscheduled maintenance uses our default strategy. + * + * Both of these are affected by the gitconfig though, which may + * override specific aspects of our strategy. + */ + if (opts->schedule) { + strategy = none_strategy; - if (schedule) - initialize_maintenance_strategy(); + if (!repo_config_get_string_tmp(the_repository, "maintenance.strategy", &config_str)) { + if (!strcasecmp(config_str, "incremental")) + strategy = incremental_strategy; + } + } else { + strategy = default_strategy; + } - for (i = 0; i < TASK__COUNT; i++) { + for (size_t i = 0; i < TASK__COUNT; i++) { int config_value; - char *config_str; strbuf_reset(&config_name); strbuf_addf(&config_name, "maintenance.%s.enabled", tasks[i].name); + if (!repo_config_get_bool(the_repository, config_name.buf, &config_value)) + strategy.tasks[i].enabled = config_value; + if (!strategy.tasks[i].enabled) + continue; - if (!git_config_get_bool(config_name.buf, &config_value)) - tasks[i].enabled = config_value; - - strbuf_reset(&config_name); - strbuf_addf(&config_name, "maintenance.%s.schedule", - tasks[i].name); - - if (!git_config_get_string(config_name.buf, &config_str)) { - tasks[i].schedule = parse_schedule(config_str); - free(config_str); + if (opts->schedule) { + strbuf_reset(&config_name); + strbuf_addf(&config_name, "maintenance.%s.schedule", + tasks[i].name); + if (!repo_config_get_string_tmp(the_repository, config_name.buf, &config_str)) + strategy.tasks[i].schedule = parse_schedule(config_str); + if (strategy.tasks[i].schedule < opts->schedule) + continue; } + + ALLOC_GROW(opts->tasks, opts->tasks_nr + 1, opts->tasks_alloc); + opts->tasks[opts->tasks_nr++] = i; } strbuf_release(&config_name); } -static int task_option_parse(const struct option *opt UNUSED, +static int task_option_parse(const struct option *opt, const char *arg, int unset) { - int i, num_selected = 0; - struct maintenance_task *task = NULL; + struct string_list *selected_tasks = opt->value; + size_t i; BUG_ON_OPT_NEG(unset); - for (i = 0; i < TASK__COUNT; i++) { - if (tasks[i].selected_order >= 0) - num_selected++; - if (!strcasecmp(tasks[i].name, arg)) { - task = &tasks[i]; - } - } - - if (!task) { + for (i = 0; i < TASK__COUNT; i++) + if (!strcasecmp(tasks[i].name, arg)) + break; + if (i >= TASK__COUNT) { error(_("'%s' is not a valid task"), arg); return 1; } - if (task->selected_order >= 0) { + if (unsorted_string_list_has_string(selected_tasks, arg)) { error(_("task '%s' cannot be selected multiple times"), arg); return 1; } - task->selected_order = num_selected + 1; + string_list_append(selected_tasks, arg)->util = (void *)(intptr_t)i; return 0; } @@ -1759,8 +1830,8 @@ static int task_option_parse(const struct option *opt UNUSED, static int maintenance_run(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) { - int i; struct maintenance_run_opts opts = MAINTENANCE_RUN_OPTS_INIT; + struct string_list selected_tasks = STRING_LIST_INIT_DUP; struct gc_config cfg = GC_CONFIG_INIT; struct option builtin_maintenance_run_options[] = { OPT_BOOL(0, "auto", &opts.auto_flag, @@ -1772,7 +1843,7 @@ static int maintenance_run(int argc, const char **argv, const char *prefix, maintenance_opt_schedule), OPT_BOOL(0, "quiet", &opts.quiet, N_("do not report progress or other information over stderr")), - OPT_CALLBACK_F(0, "task", NULL, N_("task"), + OPT_CALLBACK_F(0, "task", &selected_tasks, N_("task"), N_("run a specific task"), PARSE_OPT_NONEG, task_option_parse), OPT_END() @@ -1781,25 +1852,27 @@ static int maintenance_run(int argc, const char **argv, const char *prefix, opts.quiet = !isatty(2); - for (i = 0; i < TASK__COUNT; i++) - tasks[i].selected_order = -1; - argc = parse_options(argc, argv, prefix, builtin_maintenance_run_options, builtin_maintenance_run_usage, PARSE_OPT_STOP_AT_NON_OPTION); - if (opts.auto_flag && opts.schedule) - die(_("use at most one of --auto and --schedule=<frequency>")); + die_for_incompatible_opt2(opts.auto_flag, "--auto", + opts.schedule, "--schedule="); + die_for_incompatible_opt2(selected_tasks.nr, "--task=", + opts.schedule, "--schedule="); gc_config(&cfg); - initialize_task_config(opts.schedule); + initialize_task_config(&opts, &selected_tasks); if (argc != 0) usage_with_options(builtin_maintenance_run_usage, builtin_maintenance_run_options); ret = maintenance_run_tasks(&opts, &cfg); + + string_list_clear(&selected_tasks, 0); + maintenance_run_opts_release(&opts); gc_config_release(&cfg); return ret; } @@ -1840,13 +1913,13 @@ static int maintenance_register(int argc, const char **argv, const char *prefix, options); /* Disable foreground maintenance */ - git_config_set("maintenance.auto", "false"); + repo_config_set(the_repository, "maintenance.auto", "false"); /* Set maintenance strategy, if unset */ - if (git_config_get("maintenance.strategy")) - git_config_set("maintenance.strategy", "incremental"); + if (repo_config_get(the_repository, "maintenance.strategy")) + repo_config_set(the_repository, "maintenance.strategy", "incremental"); - if (!git_config_get_string_multi(key, &list)) { + if (!repo_config_get_string_multi(the_repository, key, &list)) { for_each_string_list_item(item, list) { if (!strcmp(maintpath, item->string)) { found = 1; @@ -1865,7 +1938,7 @@ static int maintenance_register(int argc, const char **argv, const char *prefix, } if (!config_file) die(_("$HOME not set")); - rc = git_config_set_multivar_in_file_gently( + rc = repo_config_set_multivar_in_file_gently(the_repository, config_file, "maintenance.repo", maintpath, CONFIG_REGEX_NONE, NULL, 0); free(global_config_file); @@ -1915,7 +1988,7 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi } if (!(config_file ? git_configset_get_string_multi(&cs, key, &list) - : git_config_get_string_multi(key, &list))) { + : repo_config_get_string_multi(the_repository, key, &list))) { for_each_string_list_item(item, list) { if (!strcmp(maintpath, item->string)) { found = 1; @@ -1934,7 +2007,7 @@ static int maintenance_unregister(int argc, const char **argv, const char *prefi } if (!config_file) die(_("$HOME not set")); - rc = git_config_set_multivar_in_file_gently( + rc = repo_config_set_multivar_in_file_gently(the_repository, config_file, key, NULL, maintpath, NULL, CONFIG_FLAGS_MULTI_REPLACE | CONFIG_FLAGS_FIXED_VALUE); free(global_config_file); @@ -2271,7 +2344,7 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit die(_("failed to create directories for '%s'"), filename); if ((long)lock_file_timeout_ms < 0 && - git_config_get_ulong("gc.launchctlplistlocktimeoutms", + repo_config_get_ulong(the_repository, "gc.launchctlplistlocktimeoutms", &lock_file_timeout_ms)) lock_file_timeout_ms = 150; @@ -3085,7 +3158,7 @@ static int update_background_schedule(const struct maintenance_start_opts *opts, unsigned int i; int result = 0; struct lock_file lk; - char *lock_path = xstrfmt("%s/schedule", the_repository->objects->odb->path); + char *lock_path = xstrfmt("%s/schedule", the_repository->objects->sources->path); if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0) { if (errno == EEXIST) diff --git a/builtin/grep.c b/builtin/grep.c index 3ce574a605..5df6537333 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -9,6 +9,7 @@ #include "builtin.h" #include "abspath.h" +#include "environment.h" #include "gettext.h" #include "hex.h" #include "config.h" @@ -26,7 +27,7 @@ #include "submodule-config.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "packfile.h" #include "pager.h" #include "path.h" @@ -462,7 +463,7 @@ static int grep_submodule(struct grep_opt *opt, /* * NEEDSWORK: repo_read_gitmodules() might call - * add_to_alternates_memory() via config_from_gitmodules(). This + * odb_add_to_alternates_memory() via config_from_gitmodules(). This * operation causes a race condition with concurrent object readings * performed by the worker threads. That's why we need obj_read_lock() * here. It should be removed once it's no longer necessary to add the @@ -505,7 +506,8 @@ static int grep_submodule(struct grep_opt *opt, * lazily registered as alternates when needed (and except in an * unexpected code interaction, it won't be needed). */ - add_submodule_odb_by_path(subrepo->objects->odb->path); + odb_add_submodule_source_by_path(the_repository->objects, + subrepo->objects->sources->path); obj_read_unlock(); memcpy(&subopt, opt, sizeof(subopt)); @@ -519,11 +521,9 @@ static int grep_submodule(struct grep_opt *opt, struct strbuf base = STRBUF_INIT; obj_read_lock(); - object_type = oid_object_info(subrepo, oid, NULL); + object_type = odb_read_object_info(subrepo->objects, oid, NULL); obj_read_unlock(); - data = read_object_with_reference(subrepo, - oid, OBJ_TREE, - &size, NULL); + data = odb_read_object_peeled(subrepo->objects, oid, OBJ_TREE, &size, NULL); if (!data) die(_("unable to read tree (%s)"), oid_to_hex(oid)); @@ -572,8 +572,8 @@ static int grep_cache(struct grep_opt *opt, void *data; unsigned long size; - data = repo_read_object_file(the_repository, &ce->oid, - &type, &size); + data = odb_read_object(the_repository->objects, &ce->oid, + &type, &size); if (!data) die(_("unable to read tree %s"), oid_to_hex(&ce->oid)); init_tree_desc(&tree, &ce->oid, data, size); @@ -665,8 +665,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec, void *data; unsigned long size; - data = repo_read_object_file(the_repository, - &entry.oid, &type, &size); + data = odb_read_object(the_repository->objects, + &entry.oid, &type, &size); if (!data) die(_("unable to read tree (%s)"), oid_to_hex(&entry.oid)); @@ -704,9 +704,8 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec, struct strbuf base; int hit, len; - data = read_object_with_reference(opt->repo, - &obj->oid, OBJ_TREE, - &size, NULL); + data = odb_read_object_peeled(opt->repo->objects, &obj->oid, + OBJ_TREE, &size, NULL); if (!data) die(_("unable to read tree (%s)"), oid_to_hex(&obj->oid)); @@ -1037,7 +1036,7 @@ int cmd_grep(int argc, grep_prefix = prefix; grep_init(&opt, the_repository); - git_config(grep_cmd_config, &opt); + repo_config(the_repository, grep_cmd_config, &opt); /* * If there is no -- then the paths must exist in the working @@ -1060,7 +1059,7 @@ int cmd_grep(int argc, if (use_index && !startup_info->have_repository) { int fallback = 0; - git_config_get_bool("grep.fallbacktonoindex", &fallback); + repo_config_get_bool(the_repository, "grep.fallbacktonoindex", &fallback); if (fallback) use_index = 0; else diff --git a/builtin/hash-object.c b/builtin/hash-object.c index 6a99ec250d..5d900a6b8c 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -8,10 +8,11 @@ #include "builtin.h" #include "abspath.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "hex.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "blob.h" #include "quote.h" #include "parse-options.h" @@ -104,14 +105,14 @@ int cmd_hash_object(int argc, prefix = setup_git_directory_gently(&nongit); if (nongit && !the_hash_algo) - repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + repo_set_hash_algo(the_repository, GIT_HASH_DEFAULT); if (vpath && prefix) { vpath_free = prefix_filename(prefix, vpath); vpath = vpath_free; } - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); if (stdin_paths) { if (hashstdin) diff --git a/builtin/help.c b/builtin/help.c index c257079ceb..c09cbc8912 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -6,6 +6,7 @@ #include "builtin.h" #include "config.h" +#include "environment.h" #include "exec-cmd.h" #include "gettext.h" #include "pager.h" @@ -210,7 +211,7 @@ static enum help_format parse_help_format(const char *format) if (!strcmp(format, "web") || !strcmp(format, "html")) return HELP_FORMAT_WEB; /* - * Please update _git_config() in git-completion.bash when you + * Please update _repo_config() in git-completion.bash when you * add new help formats. */ die(_("unrecognized help format '%s'"), format); @@ -706,7 +707,7 @@ int cmd_help(int argc, } setup_git_directory_gently(&nongit); - git_config(git_help_config, NULL); + repo_config(the_repository, git_help_config, NULL); if (parsed_help_format != HELP_FORMAT_NONE) help_format = parsed_help_format; diff --git a/builtin/hook.c b/builtin/hook.c index 672d2e37e8..7afec380d2 100644 --- a/builtin/hook.c +++ b/builtin/hook.c @@ -1,6 +1,7 @@ #define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "hook.h" #include "parse-options.h" @@ -55,7 +56,7 @@ static int run(int argc, const char **argv, const char *prefix, strvec_push(&opt.args, argv[i]); /* Need to take into account core.hooksPath */ - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); hook_name = argv[0]; if (!ignore_missing) diff --git a/builtin/index-pack.c b/builtin/index-pack.c index bb7925bd29..f91c301bba 100644 --- a/builtin/index-pack.c +++ b/builtin/index-pack.c @@ -21,7 +21,7 @@ #include "packfile.h" #include "pack-revindex.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "oid-array.h" #include "oidset.h" #include "path.h" @@ -260,7 +260,8 @@ static unsigned check_object(struct object *obj) if (!(obj->flags & FLAG_CHECKED)) { unsigned long size; - int type = oid_object_info(the_repository, &obj->oid, &size); + int type = odb_read_object_info(the_repository->objects, + &obj->oid, &size); if (type <= 0) die(_("did not receive expected object %s"), oid_to_hex(&obj->oid)); @@ -362,7 +363,7 @@ static const char *open_pack_file(const char *pack_name) input_fd = 0; if (!pack_name) { struct strbuf tmp_file = STRBUF_INIT; - output_fd = odb_mkstemp(&tmp_file, + output_fd = odb_mkstemp(the_repository->objects, &tmp_file, "pack/tmp_pack_XXXXXX"); pack_name = strbuf_detach(&tmp_file, NULL); } else { @@ -892,8 +893,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, if (startup_info->have_repository) { read_lock(); - collision_test_needed = has_object(the_repository, oid, - HAS_OBJECT_FETCH_PROMISOR); + collision_test_needed = odb_has_object(the_repository->objects, oid, + HAS_OBJECT_FETCH_PROMISOR); read_unlock(); } @@ -908,13 +909,13 @@ static void sha1_object(const void *data, struct object_entry *obj_entry, enum object_type has_type; unsigned long has_size; read_lock(); - has_type = oid_object_info(the_repository, oid, &has_size); + has_type = odb_read_object_info(the_repository->objects, oid, &has_size); if (has_type < 0) die(_("cannot read existing object info %s"), oid_to_hex(oid)); if (has_type != type || has_size != size) die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid)); - has_data = repo_read_object_file(the_repository, oid, - &has_type, &has_size); + has_data = odb_read_object(the_repository->objects, oid, + &has_type, &has_size); read_unlock(); if (!data) data = new_data = get_data_from_pack(obj_entry); @@ -1501,9 +1502,9 @@ static void fix_unresolved_deltas(struct hashfile *f) struct oid_array to_fetch = OID_ARRAY_INIT; for (i = 0; i < nr_ref_deltas; i++) { struct ref_delta_entry *d = sorted_by_pos[i]; - if (!oid_object_info_extended(the_repository, &d->oid, - NULL, - OBJECT_INFO_FOR_PREFETCH)) + if (!odb_read_object_info_extended(the_repository->objects, + &d->oid, NULL, + OBJECT_INFO_FOR_PREFETCH)) continue; oid_array_append(&to_fetch, &d->oid); } @@ -1520,8 +1521,8 @@ static void fix_unresolved_deltas(struct hashfile *f) if (objects[d->obj_no].real_type != OBJ_REF_DELTA) continue; - data = repo_read_object_file(the_repository, &d->oid, &type, - &size); + data = odb_read_object(the_repository->objects, &d->oid, + &type, &size); if (!data) continue; @@ -1597,7 +1598,7 @@ static void rename_tmp_packfile(const char **final_name, if (!*final_name || strcmp(*final_name, curr_name)) { if (!*final_name) *final_name = odb_pack_name(the_repository, name, hash, ext); - if (finalize_object_file(curr_name, *final_name)) + if (finalize_object_file(the_repository, curr_name, *final_name)) die(_("unable to rename temporary '*.%s' file to '%s'"), ext, *final_name); } else if (make_read_only_if_same) { @@ -1829,7 +1830,7 @@ static void repack_local_links(void) oidset_iter_init(&outgoing_links, &iter); while ((oid = oidset_iter_next(&iter))) { struct object_info info = OBJECT_INFO_INIT; - if (oid_object_info_extended(the_repository, oid, &info, 0)) + if (odb_read_object_info_extended(the_repository->objects, oid, &info, 0)) /* Missing; assume it is a promisor object */ continue; if (info.whence == OI_PACKED && info.u.packed.pack->pack_promisor) @@ -1916,7 +1917,7 @@ int cmd_index_pack(int argc, reset_pack_idx_option(&opts); opts.flags |= WRITE_REV; - git_config(git_index_pack_config, &opts); + repo_config(the_repository, git_index_pack_config, &opts); if (prefix && chdir(prefix)) die(_("Cannot come back to cwd")); @@ -2034,7 +2035,7 @@ int cmd_index_pack(int argc, * choice but to guess the object hash. */ if (!the_repository->hash_algo) - repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + repo_set_hash_algo(the_repository, GIT_HASH_DEFAULT); opts.flags &= ~(WRITE_REV | WRITE_REV_VERIFY); if (rev_index) { diff --git a/builtin/interpret-trailers.c b/builtin/interpret-trailers.c index 44d8ccddc9..41b0750e5a 100644 --- a/builtin/interpret-trailers.c +++ b/builtin/interpret-trailers.c @@ -6,6 +6,7 @@ */ #define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" +#include "environment.h" #include "gettext.h" #include "parse-options.h" #include "string-list.h" @@ -220,7 +221,7 @@ int cmd_interpret_trailers(int argc, OPT_END() }; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, git_interpret_trailers_usage, 0); diff --git a/builtin/last-modified.c b/builtin/last-modified.c new file mode 100644 index 0000000000..886ba12cb5 --- /dev/null +++ b/builtin/last-modified.c @@ -0,0 +1,326 @@ +#include "git-compat-util.h" +#include "bloom.h" +#include "builtin.h" +#include "commit-graph.h" +#include "commit.h" +#include "config.h" +#include "environment.h" +#include "diff.h" +#include "diffcore.h" +#include "environment.h" +#include "hashmap.h" +#include "hex.h" +#include "log-tree.h" +#include "object-name.h" +#include "object.h" +#include "parse-options.h" +#include "quote.h" +#include "repository.h" +#include "revision.h" + +struct last_modified_entry { + struct hashmap_entry hashent; + struct object_id oid; + struct bloom_key key; + const char path[FLEX_ARRAY]; +}; + +static int last_modified_entry_hashcmp(const void *unused UNUSED, + const struct hashmap_entry *hent1, + const struct hashmap_entry *hent2, + const void *path) +{ + const struct last_modified_entry *ent1 = + container_of(hent1, const struct last_modified_entry, hashent); + const struct last_modified_entry *ent2 = + container_of(hent2, const struct last_modified_entry, hashent); + return strcmp(ent1->path, path ? path : ent2->path); +} + +struct last_modified { + struct hashmap paths; + struct rev_info rev; + bool recursive; + bool show_trees; +}; + +static void last_modified_release(struct last_modified *lm) +{ + struct hashmap_iter iter; + struct last_modified_entry *ent; + + hashmap_for_each_entry(&lm->paths, &iter, ent, hashent) + bloom_key_clear(&ent->key); + + hashmap_clear_and_free(&lm->paths, struct last_modified_entry, hashent); + release_revisions(&lm->rev); +} + +struct last_modified_callback_data { + struct last_modified *lm; + struct commit *commit; +}; + +static void add_path_from_diff(struct diff_queue_struct *q, + struct diff_options *opt UNUSED, void *data) +{ + struct last_modified *lm = data; + + for (int i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + struct last_modified_entry *ent; + const char *path = p->two->path; + + FLEX_ALLOC_STR(ent, path, path); + oidcpy(&ent->oid, &p->two->oid); + if (lm->rev.bloom_filter_settings) + bloom_key_fill(&ent->key, path, strlen(path), + lm->rev.bloom_filter_settings); + hashmap_entry_init(&ent->hashent, strhash(ent->path)); + hashmap_add(&lm->paths, &ent->hashent); + } +} + +static int populate_paths_from_revs(struct last_modified *lm) +{ + int num_interesting = 0; + struct diff_options diffopt; + + /* + * Create a copy of `struct diff_options`. In this copy a callback is + * set that when called adds entries to `paths` in `struct last_modified`. + * This copy is used to diff the tree of the target revision against an + * empty tree. This results in all paths in the target revision being + * listed. After `paths` is populated, we don't need this copy no more. + */ + memcpy(&diffopt, &lm->rev.diffopt, sizeof(diffopt)); + copy_pathspec(&diffopt.pathspec, &lm->rev.diffopt.pathspec); + diffopt.output_format = DIFF_FORMAT_CALLBACK; + diffopt.format_callback = add_path_from_diff; + diffopt.format_callback_data = lm; + + for (size_t i = 0; i < lm->rev.pending.nr; i++) { + struct object_array_entry *obj = lm->rev.pending.objects + i; + + if (obj->item->flags & UNINTERESTING) + continue; + + if (num_interesting++) + return error(_("last-modified can only operate on one tree at a time")); + + diff_tree_oid(lm->rev.repo->hash_algo->empty_tree, + &obj->item->oid, "", &diffopt); + diff_flush(&diffopt); + } + clear_pathspec(&diffopt.pathspec); + + return 0; +} + +static void last_modified_emit(struct last_modified *lm, + const char *path, const struct commit *commit) + +{ + if (commit->object.flags & BOUNDARY) + putchar('^'); + printf("%s\t", oid_to_hex(&commit->object.oid)); + + if (lm->rev.diffopt.line_termination) + write_name_quoted(path, stdout, '\n'); + else + printf("%s%c", path, '\0'); +} + +static void mark_path(const char *path, const struct object_id *oid, + struct last_modified_callback_data *data) +{ + struct last_modified_entry *ent; + + /* Is it even a path that we are interested in? */ + ent = hashmap_get_entry_from_hash(&data->lm->paths, strhash(path), path, + struct last_modified_entry, hashent); + if (!ent) + return; + + /* + * Is it arriving at a version of interest, or is it from a side branch + * which did not contribute to the final state? + */ + if (!oideq(oid, &ent->oid)) + return; + + last_modified_emit(data->lm, path, data->commit); + + hashmap_remove(&data->lm->paths, &ent->hashent, path); + bloom_key_clear(&ent->key); + free(ent); +} + +static void last_modified_diff(struct diff_queue_struct *q, + struct diff_options *opt UNUSED, void *cbdata) +{ + struct last_modified_callback_data *data = cbdata; + + for (int i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + switch (p->status) { + case DIFF_STATUS_DELETED: + /* + * There's no point in feeding a deletion, as it could + * not have resulted in our current state, which + * actually has the file. + */ + break; + + default: + /* + * Otherwise, we care only that we somehow arrived at + * a final oid state. Note that this covers some + * potentially controversial areas, including: + * + * 1. A rename or copy will be found, as it is the + * first time the content has arrived at the given + * path. + * + * 2. Even a non-content modification like a mode or + * type change will trigger it. + * + * We take the inclusive approach for now, and find + * anything which impacts the path. Options to tweak + * the behavior (e.g., to "--follow" the content across + * renames) can come later. + */ + mark_path(p->two->path, &p->two->oid, data); + break; + } + } +} + +static bool maybe_changed_path(struct last_modified *lm, struct commit *origin) +{ + struct bloom_filter *filter; + struct last_modified_entry *ent; + struct hashmap_iter iter; + + if (!lm->rev.bloom_filter_settings) + return true; + + if (commit_graph_generation(origin) == GENERATION_NUMBER_INFINITY) + return true; + + filter = get_bloom_filter(lm->rev.repo, origin); + if (!filter) + return true; + + hashmap_for_each_entry(&lm->paths, &iter, ent, hashent) { + if (bloom_filter_contains(filter, &ent->key, + lm->rev.bloom_filter_settings)) + return true; + } + return false; +} + +static int last_modified_run(struct last_modified *lm) +{ + struct last_modified_callback_data data = { .lm = lm }; + + lm->rev.diffopt.output_format = DIFF_FORMAT_CALLBACK; + lm->rev.diffopt.format_callback = last_modified_diff; + lm->rev.diffopt.format_callback_data = &data; + + prepare_revision_walk(&lm->rev); + + while (hashmap_get_size(&lm->paths)) { + data.commit = get_revision(&lm->rev); + if (!data.commit) + BUG("paths remaining beyond boundary in last-modified"); + + if (data.commit->object.flags & BOUNDARY) { + diff_tree_oid(lm->rev.repo->hash_algo->empty_tree, + &data.commit->object.oid, "", + &lm->rev.diffopt); + diff_flush(&lm->rev.diffopt); + + break; + } + + if (!maybe_changed_path(lm, data.commit)) + continue; + + log_tree_commit(&lm->rev, data.commit); + } + + return 0; +} + +static int last_modified_init(struct last_modified *lm, struct repository *r, + const char *prefix, int argc, const char **argv) +{ + hashmap_init(&lm->paths, last_modified_entry_hashcmp, NULL, 0); + + repo_init_revisions(r, &lm->rev, prefix); + lm->rev.def = "HEAD"; + lm->rev.combine_merges = 1; + lm->rev.show_root_diff = 1; + lm->rev.boundary = 1; + lm->rev.no_commit_id = 1; + lm->rev.diff = 1; + lm->rev.diffopt.flags.recursive = lm->recursive; + lm->rev.diffopt.flags.tree_in_recursive = lm->show_trees; + + argc = setup_revisions(argc, argv, &lm->rev, NULL); + if (argc > 1) { + error(_("unknown last-modified argument: %s"), argv[1]); + return argc; + } + + lm->rev.bloom_filter_settings = get_bloom_filter_settings(lm->rev.repo); + + if (populate_paths_from_revs(lm) < 0) + return error(_("unable to setup last-modified")); + + return 0; +} + +int cmd_last_modified(int argc, const char **argv, const char *prefix, + struct repository *repo) +{ + int ret; + struct last_modified lm = { 0 }; + + const char * const last_modified_usage[] = { + N_("git last-modified [--recursive] [--show-trees] " + "[<revision-range>] [[--] <path>...]"), + NULL + }; + + struct option last_modified_options[] = { + OPT_BOOL('r', "recursive", &lm.recursive, + N_("recurse into subtrees")), + OPT_BOOL('t', "show-trees", &lm.show_trees, + N_("show tree entries when recursing into subtrees")), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, last_modified_options, + last_modified_usage, + PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN_OPT); + + repo_config(repo, git_default_config, NULL); + + ret = last_modified_init(&lm, repo, prefix, argc, argv); + if (ret > 0) + usage_with_options(last_modified_usage, + last_modified_options); + if (ret) + goto out; + + ret = last_modified_run(&lm); + if (ret) + goto out; + +out: + last_modified_release(&lm); + + return ret; +} diff --git a/builtin/log.c b/builtin/log.c index b450cd3bde..5f552d14c0 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -15,7 +15,7 @@ #include "hex.h" #include "refs.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "pager.h" #include "color.h" #include "commit.h" @@ -113,6 +113,15 @@ struct log_config { int fmt_patch_name_max; char *fmt_pretty; char *default_date_mode; + +#ifndef WITH_BREAKING_CHANGES + /* + * Note: git_log_config() does not touch this member and that + * is very deliberate. This member is only to be used to + * resurrect whatchanged that is deprecated. + */ + int i_still_use_this; +#endif }; static void log_config_init(struct log_config *cfg) @@ -212,7 +221,7 @@ static void set_default_decoration_filter(struct decoration_filter *decoration_f struct string_list *include = decoration_filter->include_ref_pattern; const struct string_list *config_exclude; - if (!git_config_get_string_multi("log.excludeDecoration", + if (!repo_config_get_string_multi(the_repository, "log.excludeDecoration", &config_exclude)) { struct string_list_item *item; for_each_string_list_item(item, config_exclude) @@ -226,7 +235,7 @@ static void set_default_decoration_filter(struct decoration_filter *decoration_f * since the command-line takes precedent. */ if (use_default_decoration_filter && - !git_config_get_string("log.initialdecorationset", &value) && + !repo_config_get_string(the_repository, "log.initialdecorationset", &value) && !strcmp("all", value)) use_default_decoration_filter = 0; free(value); @@ -267,6 +276,10 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, OPT__QUIET(&quiet, N_("suppress diff output")), OPT_BOOL(0, "source", &source, N_("show source")), OPT_BOOL(0, "use-mailmap", &mailmap, N_("use mail map file")), +#ifndef WITH_BREAKING_CHANGES + OPT_HIDDEN_BOOL(0, "i-still-use-this", &cfg->i_still_use_this, + "<use this deprecated command>"), +#endif OPT_ALIAS(0, "mailmap", "use-mailmap"), OPT_CALLBACK_F(0, "clear-decorations", NULL, NULL, N_("clear all previously-defined decoration filters"), @@ -378,129 +391,6 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix, cmd_log_init_finish(argc, argv, prefix, rev, opt, cfg); } -/* - * This gives a rough estimate for how many commits we - * will print out in the list. - */ -static int estimate_commit_count(struct commit_list *list) -{ - int n = 0; - - while (list) { - struct commit *commit = list->item; - unsigned int flags = commit->object.flags; - list = list->next; - if (!(flags & (TREESAME | UNINTERESTING))) - n++; - } - return n; -} - -static void show_early_header(struct rev_info *rev, const char *stage, int nr) -{ - if (rev->shown_one) { - rev->shown_one = 0; - if (rev->commit_format != CMIT_FMT_ONELINE) - putchar(rev->diffopt.line_termination); - } - fprintf(rev->diffopt.file, _("Final output: %d %s\n"), nr, stage); -} - -static struct itimerval early_output_timer; - -static void log_show_early(struct rev_info *revs, struct commit_list *list) -{ - int i = revs->early_output; - int show_header = 1; - int no_free = revs->diffopt.no_free; - - revs->diffopt.no_free = 0; - sort_in_topological_order(&list, revs->sort_order); - while (list && i) { - struct commit *commit = list->item; - switch (simplify_commit(revs, commit)) { - case commit_show: - if (show_header) { - int n = estimate_commit_count(list); - show_early_header(revs, "incomplete", n); - show_header = 0; - } - log_tree_commit(revs, commit); - i--; - break; - case commit_ignore: - break; - case commit_error: - revs->diffopt.no_free = no_free; - diff_free(&revs->diffopt); - return; - } - list = list->next; - } - - /* Did we already get enough commits for the early output? */ - if (!i) { - revs->diffopt.no_free = 0; - diff_free(&revs->diffopt); - return; - } - - /* - * ..if no, then repeat it twice a second until we - * do. - * - * NOTE! We don't use "it_interval", because if the - * reader isn't listening, we want our output to be - * throttled by the writing, and not have the timer - * trigger every second even if we're blocked on a - * reader! - */ - early_output_timer.it_value.tv_sec = 0; - early_output_timer.it_value.tv_usec = 500000; - setitimer(ITIMER_REAL, &early_output_timer, NULL); -} - -static void early_output(int signal UNUSED) -{ - show_early_output = log_show_early; -} - -static void setup_early_output(void) -{ - struct sigaction sa; - - /* - * Set up the signal handler, minimally intrusively: - * we only set a single volatile integer word (not - * using sigatomic_t - trying to avoid unnecessary - * system dependencies and headers), and using - * SA_RESTART. - */ - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = early_output; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - sigaction(SIGALRM, &sa, NULL); - - /* - * If we can get the whole output in less than a - * tenth of a second, don't even bother doing the - * early-output thing.. - * - * This is a one-time-only trigger. - */ - early_output_timer.it_value.tv_sec = 0; - early_output_timer.it_value.tv_usec = 100000; - setitimer(ITIMER_REAL, &early_output_timer, NULL); -} - -static void finish_early_output(struct rev_info *rev) -{ - int n = estimate_commit_count(rev->commits); - signal(SIGALRM, SIG_IGN); - show_early_header(rev, "done", n); -} - static int cmd_log_walk_no_free(struct rev_info *rev) { struct commit *commit; @@ -508,15 +398,9 @@ static int cmd_log_walk_no_free(struct rev_info *rev) int saved_dcctc = 0; int result; - if (rev->early_output) - setup_early_output(); - if (prepare_revision_walk(rev)) die(_("revision walk setup failed")); - if (rev->early_output) - finish_early_output(rev); - /* * For --check and --exit-code, the exit code is based on CHECK_FAILED * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to @@ -633,6 +517,7 @@ static int git_log_config(const char *var, const char *value, return git_diff_ui_config(var, value, ctx, cb); } +#ifndef WITH_BREAKING_CHANGES int cmd_whatchanged(int argc, const char **argv, const char *prefix, @@ -645,10 +530,10 @@ int cmd_whatchanged(int argc, log_config_init(&cfg); init_diff_ui_defaults(); - git_config(git_log_config, &cfg); + repo_config(the_repository, git_log_config, &cfg); repo_init_revisions(the_repository, &rev, prefix); - git_config(grep_config, &rev.grep_filter); + repo_config(the_repository, grep_config, &rev.grep_filter); rev.diff = 1; rev.simplify_history = 0; @@ -656,6 +541,10 @@ int cmd_whatchanged(int argc, opt.def = "HEAD"; opt.revarg_opt = REVARG_COMMITTISH; cmd_log_init(argc, argv, prefix, &rev, &opt, &cfg); + + if (!cfg.i_still_use_this) + you_still_use_that("git whatchanged"); + if (!rev.diffopt.output_format) rev.diffopt.output_format = DIFF_FORMAT_RAW; @@ -665,6 +554,7 @@ int cmd_whatchanged(int argc, log_config_release(&cfg); return ret; } +#endif static void show_tagger(const char *buf, struct rev_info *rev) { @@ -714,7 +604,7 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev) { unsigned long size; enum object_type type; - char *buf = repo_read_object_file(the_repository, oid, &type, &size); + char *buf = odb_read_object(the_repository->objects, oid, &type, &size); unsigned long offset = 0; if (!buf) @@ -771,7 +661,7 @@ int cmd_show(int argc, log_config_init(&cfg); init_diff_ui_defaults(); - git_config(git_log_config, &cfg); + repo_config(the_repository, git_log_config, &cfg); if (the_repository->gitdir) { prepare_repo_settings(the_repository); @@ -780,7 +670,7 @@ int cmd_show(int argc, memset(&match_all, 0, sizeof(match_all)); repo_init_revisions(the_repository, &rev, prefix); - git_config(grep_config, &rev.grep_filter); + repo_config(the_repository, grep_config, &rev.grep_filter); rev.diff = 1; rev.always_show_header = 1; @@ -888,11 +778,11 @@ int cmd_log_reflog(int argc, log_config_init(&cfg); init_diff_ui_defaults(); - git_config(git_log_config, &cfg); + repo_config(the_repository, git_log_config, &cfg); repo_init_revisions(the_repository, &rev, prefix); init_reflog_walk(&rev.reflog_info); - git_config(grep_config, &rev.grep_filter); + repo_config(the_repository, grep_config, &rev.grep_filter); rev.verbose_header = 1; memset(&opt, 0, sizeof(opt)); @@ -933,10 +823,10 @@ int cmd_log(int argc, log_config_init(&cfg); init_diff_ui_defaults(); - git_config(git_log_config, &cfg); + repo_config(the_repository, git_log_config, &cfg); repo_init_revisions(the_repository, &rev, prefix); - git_config(grep_config, &rev.grep_filter); + repo_config(the_repository, grep_config, &rev.grep_filter); rev.always_show_header = 1; memset(&opt, 0, sizeof(opt)); @@ -1514,6 +1404,7 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file, struct range_diff_options range_diff_opts = { .creation_factor = rev->creation_factor, .dual_color = 1, + .max_memory = RANGE_DIFF_MAX_MEMORY_DEFAULT, .diffopt = &opts, .other_arg = &other_arg }; @@ -2139,9 +2030,9 @@ int cmd_format_patch(int argc, format_config_init(&cfg); init_diff_ui_defaults(); init_display_notes(&cfg.notes_opt); - git_config(git_format_config, &cfg); + repo_config(the_repository, git_format_config, &cfg); repo_init_revisions(the_repository, &rev, prefix); - git_config(grep_config, &rev.grep_filter); + repo_config(the_repository, grep_config, &rev.grep_filter); rev.show_notes = cfg.show_notes; memcpy(&rev.notes_opt, &cfg.notes_opt, sizeof(cfg.notes_opt)); diff --git a/builtin/ls-files.c b/builtin/ls-files.c index be74f0a03b..b148607f7a 100644 --- a/builtin/ls-files.c +++ b/builtin/ls-files.c @@ -11,6 +11,7 @@ #include "builtin.h" #include "config.h" #include "convert.h" +#include "environment.h" #include "quote.h" #include "dir.h" #include "gettext.h" @@ -25,7 +26,7 @@ #include "setup.h" #include "sparse-index.h" #include "submodule.h" -#include "object-store.h" +#include "odb.h" #include "hex.h" @@ -251,7 +252,7 @@ static void expand_objectsize(struct repository *repo, struct strbuf *line, { if (type == OBJ_BLOB) { unsigned long size; - if (oid_object_info(repo, oid, &size) < 0) + if (odb_read_object_info(repo->objects, oid, &size) < 0) die(_("could not get object info about '%s'"), oid_to_hex(oid)); if (padded) @@ -413,14 +414,21 @@ static void show_files(struct repository *repo, struct dir_struct *dir) if (!(show_cached || show_stage || show_deleted || show_modified)) return; - if (!show_sparse_dirs) - ensure_full_index(repo->index); - for (i = 0; i < repo->index->cache_nr; i++) { const struct cache_entry *ce = repo->index->cache[i]; struct stat st; int stat_err; + if (S_ISSPARSEDIR(ce->ce_mode) && !show_sparse_dirs) { + /* + * This is the first time we've hit a sparse dir, + * so expansion will leave the first 'i' entries + * alone. + */ + ensure_full_index(repo->index); + ce = repo->index->cache[i]; + } + construct_fullname(&fullname, repo, ce); if ((dir->flags & DIR_SHOW_IGNORED) && diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c index 01a4d4daa1..df09000b30 100644 --- a/builtin/ls-remote.c +++ b/builtin/ls-remote.c @@ -112,7 +112,7 @@ int cmd_ls_remote(int argc, * depending on what object hash the remote uses. */ if (!the_repository->hash_algo) - repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + repo_set_hash_algo(the_repository, GIT_HASH_DEFAULT); packet_trace_identity("ls-remote"); diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c index 8aafc30ca4..ec6940fc7c 100644 --- a/builtin/ls-tree.c +++ b/builtin/ls-tree.c @@ -7,10 +7,11 @@ #include "builtin.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "hex.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "tree.h" #include "path.h" #include "quote.h" @@ -27,7 +28,7 @@ static void expand_objectsize(struct strbuf *line, const struct object_id *oid, { if (type == OBJ_BLOB) { unsigned long size; - if (oid_object_info(the_repository, oid, &size) < 0) + if (odb_read_object_info(the_repository->objects, oid, &size) < 0) die(_("could not get object info about '%s'"), oid_to_hex(oid)); if (padded) @@ -217,7 +218,7 @@ static int show_tree_long(const struct object_id *oid, struct strbuf *base, if (type == OBJ_BLOB) { unsigned long size; - if (oid_object_info(the_repository, oid, &size) == OBJ_BAD) + if (odb_read_object_info(the_repository->objects, oid, &size) == OBJ_BAD) xsnprintf(size_text, sizeof(size_text), "BAD"); else xsnprintf(size_text, sizeof(size_text), @@ -372,10 +373,9 @@ int cmd_ls_tree(int argc, OPT_END() }; struct ls_tree_cmdmode_to_fmt *m2f = ls_tree_cmdmode_format; - struct object_context obj_context = {0}; int ret; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, ls_tree_options, ls_tree_usage, 0); @@ -404,9 +404,8 @@ int cmd_ls_tree(int argc, ls_tree_usage, ls_tree_options); if (argc < 1) usage_with_options(ls_tree_usage, ls_tree_options); - if (get_oid_with_context(the_repository, argv[0], - GET_OID_HASH_ANY, &oid, - &obj_context)) + if (repo_get_oid_with_flags(the_repository, argv[0], &oid, + GET_OID_HASH_ANY)) die("Not a valid object name %s", argv[0]); /* @@ -446,6 +445,5 @@ int cmd_ls_tree(int argc, ret = !!read_tree(the_repository, tree, &options.pathspec, fn, &options); clear_pathspec(&options.pathspec); - object_context_release(&obj_context); return ret; } diff --git a/builtin/merge-base.c b/builtin/merge-base.c index 123c81515e..3f82781245 100644 --- a/builtin/merge-base.c +++ b/builtin/merge-base.c @@ -2,6 +2,7 @@ #include "builtin.h" #include "config.h" #include "commit.h" +#include "environment.h" #include "gettext.h" #include "hex.h" #include "object-name.h" @@ -167,7 +168,7 @@ int cmd_merge_base(int argc, OPT_END() }; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0); if (cmdmode == 'a') { diff --git a/builtin/merge-file.c b/builtin/merge-file.c index 2b16b10d2c..46775d0c79 100644 --- a/builtin/merge-file.c +++ b/builtin/merge-file.c @@ -7,7 +7,7 @@ #include "hex.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "config.h" #include "gettext.h" #include "setup.h" @@ -97,7 +97,7 @@ int cmd_merge_file(int argc, if (startup_info->have_repository) { /* Read the configuration file */ - git_config(git_xmerge_config, NULL); + repo_config(the_repository, git_xmerge_config, NULL); if (0 <= git_xmerge_style) xmp.style = git_xmerge_style; } @@ -155,7 +155,8 @@ int cmd_merge_file(int argc, if (object_id && !to_stdout) { struct object_id oid; if (result.size) { - if (write_object_file(result.ptr, result.size, OBJ_BLOB, &oid) < 0) + if (odb_write_object(the_repository->objects, result.ptr, + result.size, OBJ_BLOB, &oid) < 0) ret = error(_("Could not write object file")); } else { oidcpy(&oid, the_hash_algo->empty_blob); diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c index 03b5100cfa..17aa4db37a 100644 --- a/builtin/merge-recursive.c +++ b/builtin/merge-recursive.c @@ -38,7 +38,8 @@ int cmd_merge_recursive(int argc, if (argv[0] && ends_with(argv[0], "-subtree")) o.subtree_shift = ""; - if (argc == 2 && !strcmp(argv[1], "-h")) { + if (argc == 2 && (!strcmp(argv[1], "-h") || + !strcmp(argv[1], "--help-all"))) { struct strbuf msg = STRBUF_INIT; strbuf_addf(&msg, builtin_merge_recursive_usage, argv[0]); show_usage_if_asked(argc, argv, msg.buf); diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c index 7f41665dfd..1c063d9a41 100644 --- a/builtin/merge-tree.c +++ b/builtin/merge-tree.c @@ -1,6 +1,7 @@ #define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" +#include "environment.h" #include "tree-walk.h" #include "xdiff-interface.h" #include "help.h" @@ -10,7 +11,7 @@ #include "commit-reach.h" #include "merge-ort.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "parse-options.h" #include "blob.h" #include "merge-blobs.h" @@ -75,9 +76,9 @@ static void *result(struct merge_list *entry, unsigned long *size) const char *path = entry->path; if (!entry->stage) - return repo_read_object_file(the_repository, - &entry->blob->object.oid, &type, - size); + return odb_read_object(the_repository->objects, + &entry->blob->object.oid, &type, + size); base = NULL; if (entry->stage == 1) { base = entry->blob; @@ -100,9 +101,9 @@ static void *origin(struct merge_list *entry, unsigned long *size) enum object_type type; while (entry) { if (entry->stage == 2) - return repo_read_object_file(the_repository, - &entry->blob->object.oid, - &type, size); + return odb_read_object(the_repository->objects, + &entry->blob->object.oid, + &type, size); entry = entry->link; } return NULL; @@ -618,32 +619,34 @@ int cmd_merge_tree(int argc, "--merge-base", "--stdin"); line_termination = '\0'; while (strbuf_getline_lf(&buf, stdin) != EOF) { - struct strbuf **split; + struct string_list split = STRING_LIST_INIT_NODUP; const char *input_merge_base = NULL; - split = strbuf_split(&buf, ' '); - if (!split[0] || !split[1]) + string_list_split_in_place_f(&split, buf.buf, " ", -1, + STRING_LIST_SPLIT_TRIM); + + if (split.nr < 2) die(_("malformed input line: '%s'."), buf.buf); - strbuf_rtrim(split[0]); - strbuf_rtrim(split[1]); /* parse the merge-base */ - if (!strcmp(split[1]->buf, "--")) { - input_merge_base = split[0]->buf; + if (!strcmp(split.items[1].string, "--")) { + input_merge_base = split.items[0].string; } - if (input_merge_base && split[2] && split[3] && !split[4]) { - strbuf_rtrim(split[2]); - strbuf_rtrim(split[3]); - real_merge(&o, input_merge_base, split[2]->buf, split[3]->buf, prefix); - } else if (!input_merge_base && !split[2]) { - real_merge(&o, NULL, split[0]->buf, split[1]->buf, prefix); + if (input_merge_base && split.nr == 4) { + real_merge(&o, input_merge_base, + split.items[2].string, split.items[3].string, + prefix); + } else if (!input_merge_base && split.nr == 2) { + real_merge(&o, NULL, + split.items[0].string, split.items[1].string, + prefix); } else { die(_("malformed input line: '%s'."), buf.buf); } maybe_flush_or_die(stdout, "stdout"); - strbuf_list_free(split); + string_list_clear(&split, 0); } strbuf_release(&buf); @@ -683,7 +686,7 @@ int cmd_merge_tree(int argc, if (argc != expected_remaining_argc) usage_with_options(merge_tree_usage, mt_options); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); /* Do the relevant type of merge */ if (o.mode == MODE_REAL) diff --git a/builtin/merge.c b/builtin/merge.c index ce90e52fe4..c421a11b0b 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -69,7 +69,10 @@ static const char * const builtin_merge_usage[] = { NULL }; -static int show_diffstat = 1, shortlog_len = -1, squash; +#define MERGE_SHOW_DIFFSTAT 1 +#define MERGE_SHOW_COMPACTSUMMARY 2 + +static int show_diffstat = MERGE_SHOW_DIFFSTAT, shortlog_len = -1, squash; static int option_commit = -1; static int option_edit = -1; static int allow_trivial = 1, have_message, verify_signatures; @@ -243,12 +246,28 @@ static int option_parse_strategy(const struct option *opt UNUSED, return 0; } +static int option_parse_compact_summary(const struct option *opt, + const char *name UNUSED, int unset) +{ + int *setting = opt->value; + + if (unset) + *setting = 0; + else + *setting = MERGE_SHOW_COMPACTSUMMARY; + return 0; +} + static struct option builtin_merge_options[] = { OPT_SET_INT('n', NULL, &show_diffstat, N_("do not show a diffstat at the end of the merge"), 0), OPT_BOOL(0, "stat", &show_diffstat, N_("show a diffstat at the end of the merge")), OPT_BOOL(0, "summary", &show_diffstat, N_("(synonym to --stat)")), + OPT_CALLBACK_F(0, "compact-summary", &show_diffstat, NULL, + N_("show a compact-summary at the end of the merge"), + PARSE_OPT_NOARG, + option_parse_compact_summary), { .type = OPTION_INTEGER, .long_name = "log", @@ -494,8 +513,19 @@ static void finish(struct commit *head_commit, struct diff_options opts; repo_diff_setup(the_repository, &opts); init_diffstat_widths(&opts); - opts.output_format |= - DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; + + switch (show_diffstat) { + case MERGE_SHOW_DIFFSTAT: /* 1 */ + opts.output_format |= + DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT; + break; + case MERGE_SHOW_COMPACTSUMMARY: /* 2 */ + opts.output_format |= DIFF_FORMAT_DIFFSTAT; + opts.flags.stat_with_summary = 1; + break; + default: + break; + } opts.detect_rename = DIFF_DETECT_RENAME; diff_setup_done(&opts); diff_tree_oid(head, new_head, "", &opts); @@ -643,7 +673,35 @@ static int git_merge_config(const char *k, const char *v, } if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat")) { - show_diffstat = git_config_bool(k, v); + int val = git_parse_maybe_bool_text(v); + switch (val) { + case 0: + show_diffstat = 0; + break; + case 1: + show_diffstat = MERGE_SHOW_DIFFSTAT; + break; + default: + if (!strcmp(v, "compact")) + show_diffstat = MERGE_SHOW_COMPACTSUMMARY; + /* + * We do not need to have an explicit + * + * else if (!strcmp(v, "diffstat")) + * show_diffstat = MERGE_SHOW_DIFFSTAT; + * + * here, because the catch-all uses the + * diffstat style anyway. + */ + else + /* + * A setting from a future? It is not an + * error grave enough to fail the command. + * proceed using the default one. + */ + show_diffstat = MERGE_SHOW_DIFFSTAT; + break; + } } else if (!strcmp(k, "merge.verifysignatures")) { verify_signatures = git_config_bool(k, v); } else if (!strcmp(k, "pull.twohead")) { @@ -817,7 +875,7 @@ static void add_strategies(const char *string, unsigned attr) if (string) { struct string_list list = STRING_LIST_INIT_DUP; struct string_list_item *item; - string_list_split(&list, string, ' ', -1); + string_list_split(&list, string, " ", -1); for_each_string_list_item(item, &list) append_strategy(get_strategy(item->string)); string_list_clear(&list, 0); @@ -1316,10 +1374,14 @@ int cmd_merge(int argc, struct commit_list *remoteheads = NULL, *p; void *branch_to_free; int orig_argc = argc; + int merge_log_config = -1; show_usage_with_options_if_asked(argc, argv, builtin_merge_usage, builtin_merge_options); +#ifndef WITH_BREAKING_CHANGES + warn_on_auto_comment_char = true; +#endif /* !WITH_BREAKING_CHANGES */ prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; @@ -1334,7 +1396,7 @@ int cmd_merge(int argc, skip_prefix(branch, "refs/heads/", &branch); init_diff_ui_defaults(); - git_config(git_merge_config, NULL); + repo_config(the_repository, git_merge_config, &merge_log_config); if (!branch || is_null_oid(&head_oid)) head_commit = NULL; @@ -1804,7 +1866,7 @@ int cmd_merge(int argc, if (squash) { finish(head_commit, remoteheads, NULL, NULL); - git_test_write_commit_graph_or_die(); + git_test_write_commit_graph_or_die(the_repository->objects->sources); } else write_merge_state(remoteheads); diff --git a/builtin/mktag.c b/builtin/mktag.c index 7ac11c46d5..7cf6e1230a 100644 --- a/builtin/mktag.c +++ b/builtin/mktag.c @@ -6,7 +6,7 @@ #include "strbuf.h" #include "replace-object.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "fsck.h" #include "config.h" @@ -41,7 +41,7 @@ static int mktag_fsck_error_func(struct fsck_options *o UNUSED, fprintf_ln(stderr, _("error: tag input does not pass fsck: %s"), message); return 1; default: - BUG(_("%d (FSCK_IGNORE?) should never trigger this callback"), + BUG("%d (FSCK_IGNORE?) should never trigger this callback", msg_type); } } @@ -54,8 +54,8 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type) void *buffer; const struct object_id *repl; - buffer = repo_read_object_file(the_repository, tagged_oid, &type, - &size); + buffer = odb_read_object(the_repository->objects, tagged_oid, + &type, &size); if (!buffer) die(_("could not read tagged object '%s'"), oid_to_hex(tagged_oid)); @@ -98,7 +98,7 @@ int cmd_mktag(int argc, fsck_set_msg_type_from_ids(&fsck_options, FSCK_MSG_EXTRA_HEADER_ENTRY, FSCK_WARN); /* config might set fsck.extraHeaderEntry=* again */ - git_config(git_fsck_config, &fsck_options); + repo_config(the_repository, git_fsck_config, &fsck_options); if (fsck_tag_standalone(NULL, buf.buf, buf.len, &fsck_options, &tagged_oid, &tagged_type)) die(_("tag on stdin did not pass our strict fsck check")); @@ -106,7 +106,7 @@ int cmd_mktag(int argc, if (verify_object_in_tag(&tagged_oid, &tagged_type) < 0) die(_("tag on stdin did not refer to a valid object")); - if (write_object_file(buf.buf, buf.len, OBJ_TAG, &result) < 0) + if (odb_write_object(the_repository->objects, buf.buf, buf.len, OBJ_TAG, &result) < 0) die(_("unable to write tag file")); strbuf_release(&buf); diff --git a/builtin/mktree.c b/builtin/mktree.c index 4b47803467..12772303f5 100644 --- a/builtin/mktree.c +++ b/builtin/mktree.c @@ -12,7 +12,7 @@ #include "tree.h" #include "parse-options.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" static struct treeent { unsigned mode; @@ -63,7 +63,7 @@ static void write_tree(struct object_id *oid) strbuf_add(&buf, ent->oid.hash, the_hash_algo->rawsz); } - write_object_file(buf.buf, buf.len, OBJ_TREE, oid); + odb_write_object(the_repository->objects, buf.buf, buf.len, OBJ_TREE, oid); strbuf_release(&buf); } @@ -124,10 +124,10 @@ 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 (oid_object_info_extended(the_repository, &oid, &oi, - OBJECT_INFO_LOOKUP_REPLACE | - OBJECT_INFO_QUICK | - OBJECT_INFO_SKIP_FETCH_OBJECT) < 0) + if (odb_read_object_info_extended(the_repository->objects, &oid, &oi, + OBJECT_INFO_LOOKUP_REPLACE | + OBJECT_INFO_QUICK | + OBJECT_INFO_SKIP_FETCH_OBJECT) < 0) obj_type = -1; if (obj_type < 0) { diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c index 69a9750732..5f364aa816 100644 --- a/builtin/multi-pack-index.c +++ b/builtin/multi-pack-index.c @@ -2,12 +2,13 @@ #include "builtin.h" #include "abspath.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "parse-options.h" #include "midx.h" #include "strbuf.h" #include "trace2.h" -#include "object-store.h" +#include "odb.h" #include "replace-object.h" #include "repository.h" @@ -64,12 +65,20 @@ static int parse_object_dir(const struct option *opt, const char *arg, char **value = opt->value; free(*value); if (unset) - *value = xstrdup(repo_get_object_directory(the_repository)); + *value = xstrdup(the_repository->objects->sources->path); else *value = real_pathdup(arg, 1); return 0; } +static struct odb_source *handle_object_dir_option(struct repository *repo) +{ + struct odb_source *source = odb_find_source(repo->objects, opts.object_dir); + if (!source) + source = odb_add_to_alternates_memory(repo->objects, opts.object_dir); + return source; +} + static struct option common_opts[] = { OPT_CALLBACK(0, "object-dir", &opts.object_dir, N_("directory"), @@ -139,11 +148,12 @@ static int cmd_multi_pack_index_write(int argc, const char **argv, N_("refs snapshot for selecting bitmap commits")), OPT_END(), }; + struct odb_source *source; int ret; opts.flags |= MIDX_WRITE_BITMAP_HASH_CACHE; - git_config(git_multi_pack_index_write_config, NULL); + repo_config(the_repository, git_multi_pack_index_write_config, NULL); options = add_common_options(builtin_multi_pack_index_write_options); @@ -157,6 +167,7 @@ static int cmd_multi_pack_index_write(int argc, const char **argv, if (argc) usage_with_options(builtin_multi_pack_index_write_usage, options); + source = handle_object_dir_option(repo); FREE_AND_NULL(options); @@ -165,7 +176,7 @@ static int cmd_multi_pack_index_write(int argc, const char **argv, read_packs_from_stdin(&packs); - ret = write_midx_file_only(repo, opts.object_dir, &packs, + ret = write_midx_file_only(source, &packs, opts.preferred_pack, opts.refs_snapshot, opts.flags); @@ -176,7 +187,7 @@ static int cmd_multi_pack_index_write(int argc, const char **argv, } - ret = write_midx_file(repo, opts.object_dir, opts.preferred_pack, + ret = write_midx_file(source, opts.preferred_pack, opts.refs_snapshot, opts.flags); free(opts.refs_snapshot); @@ -193,6 +204,8 @@ static int cmd_multi_pack_index_verify(int argc, const char **argv, N_("force progress reporting"), MIDX_PROGRESS), OPT_END(), }; + struct odb_source *source; + options = add_common_options(builtin_multi_pack_index_verify_options); trace2_cmd_mode(argv[0]); @@ -205,10 +218,11 @@ static int cmd_multi_pack_index_verify(int argc, const char **argv, if (argc) usage_with_options(builtin_multi_pack_index_verify_usage, options); + source = handle_object_dir_option(the_repository); FREE_AND_NULL(options); - return verify_midx_file(the_repository, opts.object_dir, opts.flags); + return verify_midx_file(source, opts.flags); } static int cmd_multi_pack_index_expire(int argc, const char **argv, @@ -221,6 +235,8 @@ static int cmd_multi_pack_index_expire(int argc, const char **argv, N_("force progress reporting"), MIDX_PROGRESS), OPT_END(), }; + struct odb_source *source; + options = add_common_options(builtin_multi_pack_index_expire_options); trace2_cmd_mode(argv[0]); @@ -233,10 +249,11 @@ static int cmd_multi_pack_index_expire(int argc, const char **argv, if (argc) usage_with_options(builtin_multi_pack_index_expire_usage, options); + source = handle_object_dir_option(the_repository); FREE_AND_NULL(options); - return expire_midx_packs(the_repository, opts.object_dir, opts.flags); + return expire_midx_packs(source, opts.flags); } static int cmd_multi_pack_index_repack(int argc, const char **argv, @@ -251,6 +268,7 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv, N_("force progress reporting"), MIDX_PROGRESS), OPT_END(), }; + struct odb_source *source; options = add_common_options(builtin_multi_pack_index_repack_options); @@ -265,11 +283,11 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv, if (argc) usage_with_options(builtin_multi_pack_index_repack_usage, options); + source = handle_object_dir_option(the_repository); FREE_AND_NULL(options); - return midx_repack(the_repository, opts.object_dir, - (size_t)opts.batch_size, opts.flags); + return midx_repack(source, (size_t)opts.batch_size, opts.flags); } int cmd_multi_pack_index(int argc, @@ -290,12 +308,12 @@ int cmd_multi_pack_index(int argc, disable_replace_refs(); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); if (the_repository && the_repository->objects && - the_repository->objects->odb) - opts.object_dir = xstrdup(the_repository->objects->odb->path); + the_repository->objects->sources) + opts.object_dir = xstrdup(the_repository->objects->sources->path); argc = parse_options(argc, argv, prefix, options, builtin_multi_pack_index_usage, 0); diff --git a/builtin/mv.c b/builtin/mv.c index 07548fe96a..d43925097b 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -239,7 +239,7 @@ int cmd_mv(int argc, struct strbuf pathbuf = STRBUF_INIT; int ret; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_mv_options, builtin_mv_usage, 0); diff --git a/builtin/name-rev.c b/builtin/name-rev.c index ff199638de..74512e54a3 100644 --- a/builtin/name-rev.c +++ b/builtin/name-rev.c @@ -600,7 +600,7 @@ int cmd_name_rev(int argc, mem_pool_init(&string_pool, 0); init_commit_rev_name(&rev_names); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, opts, name_rev_usage, 0); #ifndef WITH_BREAKING_CHANGES diff --git a/builtin/notes.c b/builtin/notes.c index a3f433ca4c..9af602bdd7 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -16,7 +16,7 @@ #include "notes.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "path.h" #include "pretty.h" @@ -152,7 +152,7 @@ static void copy_obj_to_fd(int fd, const struct object_id *oid) { unsigned long size; enum object_type type; - char *buf = repo_read_object_file(the_repository, oid, &type, &size); + char *buf = odb_read_object(the_repository->objects, oid, &type, &size); if (buf) { if (size) write_or_die(fd, buf, size); @@ -229,7 +229,8 @@ static void prepare_note_data(const struct object_id *object, struct note_data * static void write_note_data(struct note_data *d, struct object_id *oid) { - if (write_object_file(d->buf.buf, d->buf.len, OBJ_BLOB, oid)) { + if (odb_write_object(the_repository->objects, d->buf.buf, + d->buf.len, OBJ_BLOB, oid)) { int status = die_message(_("unable to write note object")); if (d->edit_path) @@ -319,7 +320,7 @@ static int parse_reuse_arg(const struct option *opt, const char *arg, int unset) strbuf_init(&msg->buf, 0); if (repo_get_oid(the_repository, arg, &object)) die(_("failed to resolve '%s' as a valid ref."), arg); - if (!(value = repo_read_object_file(the_repository, &object, &type, &len))) + if (!(value = odb_read_object(the_repository->objects, &object, &type, &len))) die(_("failed to read object '%s'."), arg); if (type != OBJ_BLOB) { strbuf_release(&msg->buf); @@ -375,18 +376,19 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd) while (strbuf_getline_lf(&buf, stdin) != EOF) { struct object_id from_obj, to_obj; - struct strbuf **split; + struct string_list split = STRING_LIST_INIT_NODUP; int err; - split = strbuf_split(&buf, ' '); - if (!split[0] || !split[1]) + string_list_split_in_place_f(&split, buf.buf, " ", -1, + STRING_LIST_SPLIT_TRIM); + if (split.nr < 2) die(_("malformed input line: '%s'."), buf.buf); - strbuf_rtrim(split[0]); - strbuf_rtrim(split[1]); - if (repo_get_oid(the_repository, split[0]->buf, &from_obj)) - die(_("failed to resolve '%s' as a valid ref."), split[0]->buf); - if (repo_get_oid(the_repository, split[1]->buf, &to_obj)) - die(_("failed to resolve '%s' as a valid ref."), split[1]->buf); + if (repo_get_oid(the_repository, split.items[0].string, &from_obj)) + die(_("failed to resolve '%s' as a valid ref."), + split.items[0].string); + if (repo_get_oid(the_repository, split.items[1].string, &to_obj)) + die(_("failed to resolve '%s' as a valid ref."), + split.items[1].string); if (rewrite_cmd) err = copy_note_for_rewrite(c, &from_obj, &to_obj); @@ -396,11 +398,11 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd) if (err) { error(_("failed to copy notes from '%s' to '%s'"), - split[0]->buf, split[1]->buf); + split.items[0].string, split.items[1].string); ret = 1; } - strbuf_list_free(split); + string_list_clear(&split, 0); } if (!rewrite_cmd) { @@ -722,7 +724,7 @@ static int append_edit(int argc, const char **argv, const char *prefix, unsigned long size; enum object_type type; struct strbuf buf = STRBUF_INIT; - char *prev_buf = repo_read_object_file(the_repository, note, &type, &size); + char *prev_buf = odb_read_object(the_repository->objects, note, &type, &size); if (!prev_buf) die(_("unable to read %s"), oid_to_hex(note)); @@ -873,7 +875,7 @@ static int git_config_get_notes_strategy(const char *key, { char *value; - if (git_config_get_string(key, &value)) + if (repo_config_get_string(the_repository, key, &value)) return 1; if (parse_notes_merge_strategy(value, strategy)) git_die_config(the_repository, key, _("unknown notes merge strategy %s"), value); @@ -1145,7 +1147,7 @@ int cmd_notes(int argc, OPT_END() }; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, git_notes_usage, PARSE_OPT_SUBCOMMAND_OPTIONAL); if (!fn) { diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c index 8b33edc2ff..1494afcf3d 100644 --- a/builtin/pack-objects.c +++ b/builtin/pack-objects.c @@ -32,7 +32,7 @@ #include "list.h" #include "packfile.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "replace-object.h" #include "dir.h" #include "midx.h" @@ -41,6 +41,10 @@ #include "promisor-remote.h" #include "pack-mtimes.h" #include "parse-options.h" +#include "blob.h" +#include "tree.h" +#include "path-walk.h" +#include "trace2.h" /* * Objects we are going to pack are collected in the `to_pack` structure. @@ -184,8 +188,14 @@ static inline void oe_set_delta_size(struct packing_data *pack, #define SET_DELTA_SIBLING(obj, val) oe_set_delta_sibling(&to_pack, obj, val) static const char *const pack_usage[] = { - N_("git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]"), - N_("git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]"), + N_("git pack-objects [-q | --progress | --all-progress] [--all-progress-implied]\n" + " [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" + " [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" + " [--revs [--unpacked | --all]] [--keep-pack=<pack-name>]\n" + " [--cruft] [--cruft-expiration=<time>]\n" + " [--stdout [--filter=<filter-spec>] | <base-name>]\n" + " [--shallow] [--keep-true-parents] [--[no-]sparse]\n" + " [--name-hash-version=<n>] [--path-walk] < <object-list>"), NULL }; @@ -200,6 +210,7 @@ static int keep_unreachable, unpack_unreachable, include_tag; static timestamp_t unpack_unreachable_expiration; static int pack_loose_unreachable; static int cruft; +static int shallow = 0; static timestamp_t cruft_expiration; static int local; static int have_non_local_packs; @@ -218,6 +229,7 @@ static int delta_search_threads; static int pack_to_stdout; static int sparse; static int thin; +static int path_walk = -1; static int num_preferred_base; static struct progress *progress_state; @@ -272,6 +284,12 @@ static struct oidmap configured_exclusions; static struct oidset excluded_by_config; static int name_hash_version = -1; +enum stdin_packs_mode { + STDIN_PACKS_MODE_NONE, + STDIN_PACKS_MODE_STANDARD, + STDIN_PACKS_MODE_FOLLOW, +}; + /** * Check whether the name_hash_version chosen by user input is appropriate, * and also validate whether it is compatible with other features. @@ -337,13 +355,13 @@ static void *get_delta(struct object_entry *entry) void *buf, *base_buf, *delta_buf; enum object_type type; - buf = repo_read_object_file(the_repository, &entry->idx.oid, &type, - &size); + buf = odb_read_object(the_repository->objects, &entry->idx.oid, + &type, &size); if (!buf) die(_("unable to read %s"), oid_to_hex(&entry->idx.oid)); - base_buf = repo_read_object_file(the_repository, - &DELTA(entry)->idx.oid, &type, - &base_size); + base_buf = odb_read_object(the_repository->objects, + &DELTA(entry)->idx.oid, &type, + &base_size); if (!base_buf) die("unable to read %s", oid_to_hex(&DELTA(entry)->idx.oid)); @@ -506,9 +524,9 @@ static unsigned long write_no_reuse_object(struct hashfile *f, struct object_ent &size, NULL)) != NULL) buf = NULL; else { - buf = repo_read_object_file(the_repository, - &entry->idx.oid, &type, - &size); + buf = odb_read_object(the_repository->objects, + &entry->idx.oid, &type, + &size); if (!buf) die(_("unable to read %s"), oid_to_hex(&entry->idx.oid)); @@ -1437,7 +1455,7 @@ static void write_pack_file(void) strbuf_setlen(&tmpname, tmpname_len); } - rename_tmp_packfile_idx(&tmpname, &idx_tmp_name); + rename_tmp_packfile_idx(the_repository, &tmpname, &idx_tmp_name); free(idx_tmp_name); strbuf_release(&tmpname); @@ -1688,11 +1706,19 @@ static int want_object_in_pack_mtime(const struct object_id *oid, uint32_t found_mtime) { int want; + struct odb_source *source; struct list_head *pos; - struct multi_pack_index *m; - if (!exclude && local && has_loose_object_nonlocal(oid)) - return 0; + if (!exclude && local) { + /* + * Note that we start iterating at `sources->next` so that we + * skip the local object source. + */ + struct odb_source *source = the_repository->objects->sources->next; + for (; source; source = source->next) + if (has_loose_object(source, oid)) + return 0; + } /* * If we already know the pack object lives in, start checks from that @@ -1709,9 +1735,13 @@ static int want_object_in_pack_mtime(const struct object_id *oid, *found_offset = 0; } - for (m = get_multi_pack_index(the_repository); m; m = m->next) { + odb_prepare_alternates(the_repository->objects); + + for (source = the_repository->objects->sources; source; source = source->next) { + struct multi_pack_index *m = get_multi_pack_index(source); struct pack_entry e; - if (fill_midx_entry(the_repository, oid, &e, m)) { + + if (m && fill_midx_entry(m, oid, &e)) { want = want_object_in_pack_one(e.p, oid, exclude, found_pack, found_offset, found_mtime); if (want != -1) return want; @@ -1895,7 +1925,7 @@ static struct pbase_tree_cache *pbase_tree_get(const struct object_id *oid) /* Did not find one. Either we got a bogus request or * we need to read and perhaps cache. */ - data = repo_read_object_file(the_repository, oid, &type, &size); + data = odb_read_object(the_repository->objects, oid, &type, &size); if (!data) return NULL; if (type != OBJ_TREE) { @@ -2055,8 +2085,8 @@ static void add_preferred_base(struct object_id *oid) if (window <= num_preferred_base++) return; - data = read_object_with_reference(the_repository, oid, - OBJ_TREE, &size, &tree_oid); + data = odb_read_object_peeled(the_repository->objects, oid, + OBJ_TREE, &size, &tree_oid); if (!data) return; @@ -2154,10 +2184,10 @@ static void prefetch_to_pack(uint32_t object_index_start) { for (i = object_index_start; i < to_pack.nr_objects; i++) { struct object_entry *entry = to_pack.objects + i; - if (!oid_object_info_extended(the_repository, - &entry->idx.oid, - NULL, - OBJECT_INFO_FOR_PREFETCH)) + if (!odb_read_object_info_extended(the_repository->objects, + &entry->idx.oid, + NULL, + OBJECT_INFO_FOR_PREFETCH)) continue; oid_array_append(&to_fetch, &entry->idx.oid); } @@ -2298,19 +2328,19 @@ static void check_object(struct object_entry *entry, uint32_t object_index) /* * No choice but to fall back to the recursive delta walk - * with oid_object_info() to find about the object type + * with odb_read_object_info() to find about the object type * at this point... */ give_up: unuse_pack(&w_curs); } - if (oid_object_info_extended(the_repository, &entry->idx.oid, &oi, - OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_LOOKUP_REPLACE) < 0) { + if (odb_read_object_info_extended(the_repository->objects, &entry->idx.oid, &oi, + OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_LOOKUP_REPLACE) < 0) { if (repo_has_promisor_remote(the_repository)) { prefetch_to_pack(object_index); - if (oid_object_info_extended(the_repository, &entry->idx.oid, &oi, - OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_LOOKUP_REPLACE) < 0) + if (odb_read_object_info_extended(the_repository->objects, &entry->idx.oid, &oi, + OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_LOOKUP_REPLACE) < 0) type = -1; } else { type = -1; @@ -2384,12 +2414,13 @@ static void drop_reused_delta(struct object_entry *entry) if (packed_object_info(the_repository, IN_PACK(entry), entry->in_pack_offset, &oi) < 0) { /* * We failed to get the info from this pack for some reason; - * fall back to oid_object_info, which may find another copy. + * fall back to odb_read_object_info, which may find another copy. * And if that fails, the error will be recorded in oe_type(entry) * and dealt with in prepare_pack(). */ oe_set_type(entry, - oid_object_info(the_repository, &entry->idx.oid, &size)); + odb_read_object_info(the_repository->objects, + &entry->idx.oid, &size)); } else { oe_set_type(entry, type); } @@ -2677,7 +2708,8 @@ unsigned long oe_get_size_slow(struct packing_data *pack, if (e->type_ != OBJ_OFS_DELTA && e->type_ != OBJ_REF_DELTA) { packing_data_lock(&to_pack); - if (oid_object_info(the_repository, &e->idx.oid, &size) < 0) + if (odb_read_object_info(the_repository->objects, + &e->idx.oid, &size) < 0) die(_("unable to get size of %s"), oid_to_hex(&e->idx.oid)); packing_data_unlock(&to_pack); @@ -2760,9 +2792,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, /* Load data if not already done */ if (!trg->data) { packing_data_lock(&to_pack); - trg->data = repo_read_object_file(the_repository, - &trg_entry->idx.oid, &type, - &sz); + trg->data = odb_read_object(the_repository->objects, + &trg_entry->idx.oid, &type, + &sz); packing_data_unlock(&to_pack); if (!trg->data) die(_("object %s cannot be read"), @@ -2775,9 +2807,9 @@ static int try_delta(struct unpacked *trg, struct unpacked *src, } if (!src->data) { packing_data_lock(&to_pack); - src->data = repo_read_object_file(the_repository, - &src_entry->idx.oid, &type, - &sz); + src->data = odb_read_object(the_repository->objects, + &src_entry->idx.oid, &type, + &sz); packing_data_unlock(&to_pack); if (!src->data) { if (src_entry->preferred_base) { @@ -3041,6 +3073,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size, struct thread_params { pthread_t thread; struct object_entry **list; + struct packing_region *regions; unsigned list_size; unsigned remaining; int window; @@ -3283,6 +3316,242 @@ static int add_ref_tag(const char *tag UNUSED, const char *referent UNUSED, cons return 0; } +static int should_attempt_deltas(struct object_entry *entry) +{ + if (DELTA(entry)) + /* This happens if we decided to reuse existing + * delta from a pack. "reuse_delta &&" is implied. + */ + return 0; + + if (!entry->type_valid || + oe_size_less_than(&to_pack, entry, 50)) + return 0; + + if (entry->no_try_delta) + return 0; + + if (!entry->preferred_base) { + if (oe_type(entry) < 0) + die(_("unable to get type of object %s"), + oid_to_hex(&entry->idx.oid)); + } else if (oe_type(entry) < 0) { + /* + * This object is not found, but we + * don't have to include it anyway. + */ + return 0; + } + + return 1; +} + +static void find_deltas_for_region(struct object_entry *list, + struct packing_region *region, + unsigned int *processed) +{ + struct object_entry **delta_list; + unsigned int delta_list_nr = 0; + + ALLOC_ARRAY(delta_list, region->nr); + for (size_t i = 0; i < region->nr; i++) { + struct object_entry *entry = list + region->start + i; + if (should_attempt_deltas(entry)) + delta_list[delta_list_nr++] = entry; + } + + QSORT(delta_list, delta_list_nr, type_size_sort); + find_deltas(delta_list, &delta_list_nr, window, depth, processed); + free(delta_list); +} + +static void find_deltas_by_region(struct object_entry *list, + struct packing_region *regions, + size_t start, size_t nr) +{ + unsigned int processed = 0; + size_t progress_nr; + + if (!nr) + return; + + progress_nr = regions[nr - 1].start + regions[nr - 1].nr; + + if (progress) + progress_state = start_progress(the_repository, + _("Compressing objects by path"), + progress_nr); + + while (nr--) + find_deltas_for_region(list, + ®ions[start++], + &processed); + + display_progress(progress_state, progress_nr); + stop_progress(&progress_state); +} + +static void *threaded_find_deltas_by_path(void *arg) +{ + struct thread_params *me = arg; + + progress_lock(); + while (me->remaining) { + while (me->remaining) { + progress_unlock(); + find_deltas_for_region(to_pack.objects, + me->regions, + me->processed); + progress_lock(); + me->remaining--; + me->regions++; + } + + me->working = 0; + pthread_cond_signal(&progress_cond); + progress_unlock(); + + /* + * We must not set ->data_ready before we wait on the + * condition because the main thread may have set it to 1 + * before we get here. In order to be sure that new + * work is available if we see 1 in ->data_ready, it + * was initialized to 0 before this thread was spawned + * and we reset it to 0 right away. + */ + pthread_mutex_lock(&me->mutex); + while (!me->data_ready) + pthread_cond_wait(&me->cond, &me->mutex); + me->data_ready = 0; + pthread_mutex_unlock(&me->mutex); + + progress_lock(); + } + progress_unlock(); + /* leave ->working 1 so that this doesn't get more work assigned */ + return NULL; +} + +static void ll_find_deltas_by_region(struct object_entry *list, + struct packing_region *regions, + uint32_t start, uint32_t nr) +{ + struct thread_params *p; + int i, ret, active_threads = 0; + unsigned int processed = 0; + uint32_t progress_nr; + init_threaded_search(); + + if (!nr) + return; + + progress_nr = regions[nr - 1].start + regions[nr - 1].nr; + if (delta_search_threads <= 1) { + find_deltas_by_region(list, regions, start, nr); + cleanup_threaded_search(); + return; + } + + if (progress > pack_to_stdout) + fprintf_ln(stderr, + Q_("Path-based delta compression using up to %d thread", + "Path-based delta compression using up to %d threads", + delta_search_threads), + delta_search_threads); + CALLOC_ARRAY(p, delta_search_threads); + + if (progress) + progress_state = start_progress(the_repository, + _("Compressing objects by path"), + progress_nr); + /* Partition the work amongst work threads. */ + for (i = 0; i < delta_search_threads; i++) { + unsigned sub_size = nr / (delta_search_threads - i); + + p[i].window = window; + p[i].depth = depth; + p[i].processed = &processed; + p[i].working = 1; + p[i].data_ready = 0; + + p[i].regions = regions; + p[i].list_size = sub_size; + p[i].remaining = sub_size; + + regions += sub_size; + nr -= sub_size; + } + + /* Start work threads. */ + for (i = 0; i < delta_search_threads; i++) { + if (!p[i].list_size) + continue; + pthread_mutex_init(&p[i].mutex, NULL); + pthread_cond_init(&p[i].cond, NULL); + ret = pthread_create(&p[i].thread, NULL, + threaded_find_deltas_by_path, &p[i]); + if (ret) + die(_("unable to create thread: %s"), strerror(ret)); + active_threads++; + } + + /* + * Now let's wait for work completion. Each time a thread is done + * with its work, we steal half of the remaining work from the + * thread with the largest number of unprocessed objects and give + * it to that newly idle thread. This ensure good load balancing + * until the remaining object list segments are simply too short + * to be worth splitting anymore. + */ + while (active_threads) { + struct thread_params *target = NULL; + struct thread_params *victim = NULL; + unsigned sub_size = 0; + + progress_lock(); + for (;;) { + for (i = 0; !target && i < delta_search_threads; i++) + if (!p[i].working) + target = &p[i]; + if (target) + break; + pthread_cond_wait(&progress_cond, &progress_mutex); + } + + for (i = 0; i < delta_search_threads; i++) + if (p[i].remaining > 2*window && + (!victim || victim->remaining < p[i].remaining)) + victim = &p[i]; + if (victim) { + sub_size = victim->remaining / 2; + target->regions = victim->regions + victim->remaining - sub_size; + victim->list_size -= sub_size; + victim->remaining -= sub_size; + } + target->list_size = sub_size; + target->remaining = sub_size; + target->working = 1; + progress_unlock(); + + pthread_mutex_lock(&target->mutex); + target->data_ready = 1; + pthread_cond_signal(&target->cond); + pthread_mutex_unlock(&target->mutex); + + if (!sub_size) { + pthread_join(target->thread, NULL); + pthread_cond_destroy(&target->cond); + pthread_mutex_destroy(&target->mutex); + active_threads--; + } + } + cleanup_threaded_search(); + free(p); + + display_progress(progress_state, progress_nr); + stop_progress(&progress_state); +} + static void prepare_pack(int window, int depth) { struct object_entry **delta_list; @@ -3307,39 +3576,21 @@ static void prepare_pack(int window, int depth) if (!to_pack.nr_objects || !window || !depth) return; + if (path_walk) + ll_find_deltas_by_region(to_pack.objects, to_pack.regions, + 0, to_pack.nr_regions); + ALLOC_ARRAY(delta_list, to_pack.nr_objects); nr_deltas = n = 0; for (i = 0; i < to_pack.nr_objects; i++) { struct object_entry *entry = to_pack.objects + i; - if (DELTA(entry)) - /* This happens if we decided to reuse existing - * delta from a pack. "reuse_delta &&" is implied. - */ - continue; - - if (!entry->type_valid || - oe_size_less_than(&to_pack, entry, 50)) + if (!should_attempt_deltas(entry)) continue; - if (entry->no_try_delta) - continue; - - if (!entry->preferred_base) { + if (!entry->preferred_base) nr_deltas++; - if (oe_type(entry) < 0) - die(_("unable to get type of object %s"), - oid_to_hex(&entry->idx.oid)); - } else { - if (oe_type(entry) < 0) { - /* - * This object is not found, but we - * don't have to include it anyway. - */ - continue; - } - } delta_list[n++] = entry; } @@ -3494,7 +3745,6 @@ static int add_object_entry_from_pack(const struct object_id *oid, return 0; if (p) { - struct rev_info *revs = _data; struct object_info oi = OBJECT_INFO_INIT; oi.typep = &type; @@ -3502,6 +3752,7 @@ static int add_object_entry_from_pack(const struct object_id *oid, 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 @@ -3517,32 +3768,48 @@ static int add_object_entry_from_pack(const struct object_id *oid, return 0; } -static void show_commit_pack_hint(struct commit *commit UNUSED, - void *data UNUSED) +static void show_object_pack_hint(struct object *object, const char *name, + void *data) { - /* nothing to do; commits don't have a namehash */ + enum stdin_packs_mode mode = *(enum stdin_packs_mode *)data; + if (mode == STDIN_PACKS_MODE_FOLLOW) { + if (object->type == OBJ_BLOB && + !has_object(the_repository, &object->oid, 0)) + return; + add_object_entry(&object->oid, object->type, name, 0); + } else { + struct object_entry *oe = packlist_find(&to_pack, &object->oid); + if (!oe) + return; + + /* + * Our 'to_pack' list was constructed by iterating all + * objects packed in included packs, and so doesn't have + * a non-zero hash field that you would typically pick + * up during a reachability traversal. + * + * Make a best-effort attempt to fill in the ->hash and + * ->no_try_delta fields here in order to perhaps + * improve the delta selection process. + */ + oe->hash = pack_name_hash_fn(name); + oe->no_try_delta = name && no_try_delta(name); + + stdin_packs_hints_nr++; + } } -static void show_object_pack_hint(struct object *object, const char *name, - void *data UNUSED) +static void show_commit_pack_hint(struct commit *commit, void *data) { - struct object_entry *oe = packlist_find(&to_pack, &object->oid); - if (!oe) + enum stdin_packs_mode mode = *(enum stdin_packs_mode *)data; + + if (mode == STDIN_PACKS_MODE_FOLLOW) { + show_object_pack_hint((struct object *)commit, "", data); return; + } - /* - * Our 'to_pack' list was constructed by iterating all objects packed in - * included packs, and so doesn't have a non-zero hash field that you - * would typically pick up during a reachability traversal. - * - * Make a best-effort attempt to fill in the ->hash and ->no_try_delta - * here using a now in order to perhaps improve the delta selection - * process. - */ - oe->hash = pack_name_hash_fn(name); - oe->no_try_delta = name && no_try_delta(name); + /* nothing to do; commits don't have a namehash */ - stdin_packs_hints_nr++; } static int pack_mtime_cmp(const void *_a, const void *_b) @@ -3562,7 +3829,7 @@ static int pack_mtime_cmp(const void *_a, const void *_b) return 0; } -static void read_packs_list_from_stdin(void) +static void read_packs_list_from_stdin(struct rev_info *revs) { struct strbuf buf = STRBUF_INIT; struct string_list include_packs = STRING_LIST_INIT_DUP; @@ -3570,24 +3837,6 @@ static void read_packs_list_from_stdin(void) struct string_list_item *item = NULL; struct packed_git *p; - struct rev_info revs; - - repo_init_revisions(the_repository, &revs, NULL); - /* - * Use a revision walk to fill in the namehash of objects in the include - * packs. To save time, we'll avoid traversing through objects that are - * in excluded packs. - * - * That may cause us to avoid populating all of the namehash fields of - * all included objects, but our goal is best-effort, since this is only - * an optimization during delta selection. - */ - revs.no_kept_objects = 1; - revs.keep_pack_cache_flags |= IN_CORE_KEEP_PACKS; - revs.blob_objects = 1; - revs.tree_objects = 1; - revs.tag_objects = 1; - revs.ignore_missing_links = 1; while (strbuf_getline(&buf, stdin) != EOF) { if (!buf.len) @@ -3657,25 +3906,55 @@ static void read_packs_list_from_stdin(void) struct packed_git *p = item->util; for_each_object_in_pack(p, add_object_entry_from_pack, - &revs, + revs, FOR_EACH_OBJECT_PACK_ORDER); } + strbuf_release(&buf); + string_list_clear(&include_packs, 0); + string_list_clear(&exclude_packs, 0); +} + +static void add_unreachable_loose_objects(struct rev_info *revs); + +static void read_stdin_packs(enum stdin_packs_mode mode, int rev_list_unpacked) +{ + struct rev_info revs; + + repo_init_revisions(the_repository, &revs, NULL); + /* + * Use a revision walk to fill in the namehash of objects in the include + * packs. To save time, we'll avoid traversing through objects that are + * in excluded packs. + * + * That may cause us to avoid populating all of the namehash fields of + * all included objects, but our goal is best-effort, since this is only + * an optimization during delta selection. + */ + revs.no_kept_objects = 1; + revs.keep_pack_cache_flags |= IN_CORE_KEEP_PACKS; + revs.blob_objects = 1; + revs.tree_objects = 1; + revs.tag_objects = 1; + revs.ignore_missing_links = 1; + + /* avoids adding objects in excluded packs */ + ignore_packed_keep_in_core = 1; + read_packs_list_from_stdin(&revs); + if (rev_list_unpacked) + add_unreachable_loose_objects(&revs); + if (prepare_revision_walk(&revs)) die(_("revision walk setup failed")); traverse_commit_list(&revs, show_commit_pack_hint, show_object_pack_hint, - NULL); + &mode); trace2_data_intmax("pack-objects", the_repository, "stdin_packs_found", stdin_packs_found_nr); trace2_data_intmax("pack-objects", the_repository, "stdin_packs_hints", stdin_packs_hints_nr); - - strbuf_release(&buf); - string_list_clear(&include_packs, 0); - string_list_clear(&exclude_packs, 0); } static void add_cruft_object_entry(const struct object_id *oid, enum object_type type, @@ -3695,7 +3974,14 @@ static void add_cruft_object_entry(const struct object_id *oid, enum object_type } else { if (!want_object_in_pack_mtime(oid, 0, &pack, &offset, mtime)) return; - if (!pack && type == OBJ_BLOB && !has_loose_object(oid)) { + if (!pack && type == OBJ_BLOB) { + struct odb_source *source = the_repository->objects->sources; + int found = 0; + + for (; !found && source; source = source->next) + if (has_loose_object(source, oid)) + found = 1; + /* * If a traversed tree has a missing blob then we want * to avoid adding that missing object to our pack. @@ -3709,7 +3995,8 @@ static void add_cruft_object_entry(const struct object_id *oid, enum object_type * limited to "ensure non-tip blobs which don't exist in * packs do exist via loose objects". Confused? */ - return; + if (!found) + return; } entry = create_object_entry(oid, type, pack_name_hash_fn(name), @@ -3773,7 +4060,6 @@ static void mark_pack_kept_in_core(struct string_list *packs, unsigned keep) } } -static void add_unreachable_loose_objects(void); static void add_objects_in_unpacked_packs(void); static void enumerate_cruft_objects(void) @@ -3783,7 +4069,7 @@ static void enumerate_cruft_objects(void) _("Enumerating cruft objects"), 0); add_objects_in_unpacked_packs(); - add_unreachable_loose_objects(); + add_unreachable_loose_objects(NULL); stop_progress(&progress_state); } @@ -3966,7 +4252,7 @@ static void show_object__ma_allow_any(struct object *obj, const char *name, void * Quietly ignore ALL missing objects. This avoids problems with * staging them now and getting an odd error later. */ - if (!has_object(the_repository, &obj->oid, 0)) + if (!odb_has_object(the_repository->objects, &obj->oid, 0)) return; show_object(obj, name, data); @@ -3980,7 +4266,7 @@ static void show_object__ma_allow_promisor(struct object *obj, const char *name, * Quietly ignore EXPECTED missing objects. This avoids problems with * staging them now and getting an odd error later. */ - if (!has_object(the_repository, &obj->oid, 0) && + if (!odb_has_object(the_repository->objects, &obj->oid, 0) && is_promisor_object(to_pack.repo, &obj->oid)) return; @@ -4061,9 +4347,10 @@ static void add_objects_in_unpacked_packs(void) } static int add_loose_object(const struct object_id *oid, const char *path, - void *data UNUSED) + void *data) { - enum object_type type = oid_object_info(the_repository, oid, NULL); + struct rev_info *revs = data; + enum object_type type = odb_read_object_info(the_repository->objects, oid, NULL); if (type < 0) { warning(_("loose object at %s could not be examined"), path); @@ -4083,6 +4370,10 @@ static int add_loose_object(const struct object_id *oid, const char *path, } else { add_object_entry(oid, type, "", 0); } + + if (revs && type == OBJ_COMMIT) + add_pending_oid(revs, NULL, oid, 0); + return 0; } @@ -4091,11 +4382,10 @@ static int add_loose_object(const struct object_id *oid, const char *path, * add_object_entry will weed out duplicates, so we just add every * loose object we find. */ -static void add_unreachable_loose_objects(void) +static void add_unreachable_loose_objects(struct rev_info *revs) { - for_each_loose_file_in_objdir(repo_get_object_directory(the_repository), - add_loose_object, - NULL, NULL, NULL); + for_each_loose_file_in_source(the_repository->objects->sources, + add_loose_object, NULL, NULL, revs); } static int has_sha1_pack_kept_or_nonlocal(const struct object_id *oid) @@ -4163,7 +4453,8 @@ static void loosen_unused_packed_objects(void) if (!packlist_find(&to_pack, &oid) && !has_sha1_pack_kept_or_nonlocal(&oid) && !loosened_object_can_be_discarded(&oid, p->mtime)) { - if (force_object_loose(&oid, p->mtime)) + if (force_object_loose(the_repository->objects->sources, + &oid, p->mtime)) die(_("unable to force loose object")); loosened_objects_nr++; } @@ -4272,6 +4563,93 @@ static void mark_bitmap_preferred_tips(void) } } +static inline int is_oid_uninteresting(struct repository *repo, + struct object_id *oid) +{ + struct object *o = lookup_object(repo, oid); + return !o || (o->flags & UNINTERESTING); +} + +static int add_objects_by_path(const char *path, + struct oid_array *oids, + enum object_type type, + void *data) +{ + size_t oe_start = to_pack.nr_objects; + size_t oe_end; + unsigned int *processed = data; + + /* + * First, add all objects to the packing data, including the ones + * marked UNINTERESTING (translated to 'exclude') as they can be + * used as delta bases. + */ + for (size_t i = 0; i < oids->nr; i++) { + int exclude; + struct object_info oi = OBJECT_INFO_INIT; + struct object_id *oid = &oids->oid[i]; + + /* Skip objects that do not exist locally. */ + if ((exclude_promisor_objects || arg_missing_action != MA_ERROR) && + oid_object_info_extended(the_repository, oid, &oi, + OBJECT_INFO_FOR_PREFETCH) < 0) + continue; + + exclude = is_oid_uninteresting(the_repository, oid); + + if (exclude && !thin) + continue; + + add_object_entry(oid, type, path, exclude); + } + + oe_end = to_pack.nr_objects; + + /* We can skip delta calculations if it is a no-op. */ + if (oe_end == oe_start || !window) + return 0; + + ALLOC_GROW(to_pack.regions, + to_pack.nr_regions + 1, + to_pack.nr_regions_alloc); + + to_pack.regions[to_pack.nr_regions].start = oe_start; + to_pack.regions[to_pack.nr_regions].nr = oe_end - oe_start; + to_pack.nr_regions++; + + *processed += oids->nr; + display_progress(progress_state, *processed); + + return 0; +} + +static void get_object_list_path_walk(struct rev_info *revs) +{ + struct path_walk_info info = PATH_WALK_INFO_INIT; + unsigned int processed = 0; + int result; + + info.revs = revs; + info.path_fn = add_objects_by_path; + info.path_fn_data = &processed; + + /* + * Allow the --[no-]sparse option to be interesting here, if only + * for testing purposes. Paths with no interesting objects will not + * contribute to the resulting pack, but only create noisy preferred + * base objects. + */ + info.prune_all_uninteresting = sparse; + info.edge_aggressive = shallow; + + trace2_region_enter("pack-objects", "path-walk", revs->repo); + result = walk_objects_by_path(&info); + trace2_region_leave("pack-objects", "path-walk", revs->repo); + + if (result) + die(_("failed to pack objects via path-walk")); +} + static void get_object_list(struct rev_info *revs, int ac, const char **av) { struct setup_revision_opt s_r_opt = { @@ -4327,15 +4705,19 @@ static void get_object_list(struct rev_info *revs, int ac, const char **av) if (write_bitmap_index) mark_bitmap_preferred_tips(); - if (prepare_revision_walk(revs)) - die(_("revision walk setup failed")); - mark_edges_uninteresting(revs, show_edge, sparse); - if (!fn_show_object) fn_show_object = show_object; - traverse_commit_list(revs, - show_commit, fn_show_object, - NULL); + + if (path_walk) { + get_object_list_path_walk(revs); + } else { + if (prepare_revision_walk(revs)) + die(_("revision walk setup failed")); + mark_edges_uninteresting(revs, show_edge, sparse); + traverse_commit_list(revs, + show_commit, fn_show_object, + NULL); + } if (unpack_unreachable_expiration) { revs->ignore_missing_links = 1; @@ -4351,7 +4733,7 @@ static void get_object_list(struct rev_info *revs, int ac, const char **av) if (keep_unreachable) add_objects_in_unpacked_packs(); if (pack_loose_unreachable) - add_unreachable_loose_objects(); + add_unreachable_loose_objects(NULL); if (unpack_unreachable) loosen_unused_packed_objects(); @@ -4449,7 +4831,7 @@ static int option_parse_cruft_expiration(const struct option *opt UNUSED, static int is_not_in_promisor_pack_obj(struct object *obj, void *data UNUSED) { struct object_info info = OBJECT_INFO_INIT; - if (oid_object_info_extended(the_repository, &obj->oid, &info, 0)) + if (odb_read_object_info_extended(the_repository->objects, &obj->oid, &info, 0)) BUG("should_include_obj should only be called on existing objects"); return info.whence != OI_PACKED || !info.u.packed.pack->pack_promisor; } @@ -4458,18 +4840,34 @@ static int is_not_in_promisor_pack(struct commit *commit, void *data) { return is_not_in_promisor_pack_obj((struct object *) commit, data); } +static int parse_stdin_packs_mode(const struct option *opt, const char *arg, + int unset) +{ + enum stdin_packs_mode *mode = opt->value; + + if (unset) + *mode = STDIN_PACKS_MODE_NONE; + else if (!arg || !*arg) + *mode = STDIN_PACKS_MODE_STANDARD; + else if (!strcmp(arg, "follow")) + *mode = STDIN_PACKS_MODE_FOLLOW; + else + die(_("invalid value for '%s': '%s'"), opt->long_name, arg); + + return 0; +} + int cmd_pack_objects(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) { int use_internal_rev_list = 0; - int shallow = 0; int all_progress_implied = 0; struct strvec rp = STRVEC_INIT; int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0; int rev_list_index = 0; - int stdin_packs = 0; + enum stdin_packs_mode stdin_packs = STDIN_PACKS_MODE_NONE; struct string_list keep_pack_list = STRING_LIST_INIT_NODUP; struct list_objects_filter_options filter_options = LIST_OBJECTS_FILTER_INIT; @@ -4524,6 +4922,9 @@ int cmd_pack_objects(int argc, OPT_SET_INT_F(0, "indexed-objects", &rev_list_index, N_("include objects referred to by the index"), 1, PARSE_OPT_NONEG), + OPT_CALLBACK_F(0, "stdin-packs", &stdin_packs, N_("mode"), + N_("read packs from stdin"), + PARSE_OPT_OPTARG, parse_stdin_packs_mode), OPT_BOOL(0, "stdin-packs", &stdin_packs, N_("read packs from stdin")), OPT_BOOL(0, "stdout", &pack_to_stdout, @@ -4545,6 +4946,8 @@ int cmd_pack_objects(int argc, N_("use the sparse reachability algorithm")), OPT_BOOL(0, "thin", &thin, N_("create thin packs")), + OPT_BOOL(0, "path-walk", &path_walk, + N_("use the path-walk API to walk objects when possible")), OPT_BOOL(0, "shallow", &shallow, N_("create packs suitable for shallow fetches")), OPT_BOOL(0, "honor-pack-keep", &ignore_packed_keep_on_disk, @@ -4599,7 +5002,7 @@ int cmd_pack_objects(int argc, reset_pack_idx_option(&pack_idx_opts); pack_idx_opts.flags |= WRITE_REV; - git_config(git_pack_config, NULL); + repo_config(the_repository, git_pack_config, NULL); if (git_env_bool(GIT_TEST_NO_WRITE_REV_INDEX, 0)) pack_idx_opts.flags &= ~WRITE_REV; @@ -4614,6 +5017,17 @@ int cmd_pack_objects(int argc, if (pack_to_stdout != !base_name || argc) usage_with_options(pack_usage, pack_objects_options); + if (path_walk < 0) { + if (use_bitmap_index > 0 || + !use_internal_rev_list) + path_walk = 0; + else if (the_repository->gitdir && + the_repository->settings.pack_use_path_walk) + path_walk = 1; + else + path_walk = git_env_bool("GIT_TEST_PACK_PATH_WALK", 0); + } + if (depth < 0) depth = 0; if (depth >= (1 << OE_DEPTH_BITS)) { @@ -4630,7 +5044,28 @@ int cmd_pack_objects(int argc, window = 0; strvec_push(&rp, "pack-objects"); - if (thin) { + + if (path_walk) { + const char *option = NULL; + if (filter_options.choice) + option = "--filter"; + else if (use_delta_islands) + option = "--delta-islands"; + + if (option) { + warning(_("cannot use %s with %s"), + option, "--path-walk"); + path_walk = 0; + } + } + if (path_walk) { + strvec_push(&rp, "--boundary"); + /* + * We must disable the bitmaps because we are removing + * the --objects / --objects-edge[-aggressive] options. + */ + use_bitmap_index = 0; + } else if (thin) { use_internal_rev_list = 1; strvec_push(&rp, shallow ? "--objects-edge-aggressive" @@ -4655,9 +5090,10 @@ int cmd_pack_objects(int argc, strvec_push(&rp, "--unpacked"); } - if (exclude_promisor_objects && exclude_promisor_objects_best_effort) - die(_("options '%s' and '%s' cannot be used together"), - "--exclude-promisor-objects", "--exclude-promisor-objects-best-effort"); + die_for_incompatible_opt2(exclude_promisor_objects, + "--exclude-promisor-objects", + exclude_promisor_objects_best_effort, + "--exclude-promisor-objects-best-effort"); if (exclude_promisor_objects) { use_internal_rev_list = 1; fetch_if_missing = 0; @@ -4695,13 +5131,14 @@ int cmd_pack_objects(int argc, if (!pack_to_stdout && thin) die(_("--thin cannot be used to build an indexable pack")); - if (keep_unreachable && unpack_unreachable) - die(_("options '%s' and '%s' cannot be used together"), "--keep-unreachable", "--unpack-unreachable"); + die_for_incompatible_opt2(keep_unreachable, "--keep-unreachable", + unpack_unreachable, "--unpack-unreachable"); if (!rev_list_all || !rev_list_reflog || !rev_list_index) unpack_unreachable_expiration = 0; - if (stdin_packs && filter_options.choice) - die(_("cannot use --filter with --stdin-packs")); + die_for_incompatible_opt2(stdin_packs, "--stdin-packs", + filter_options.choice, "--filter"); + if (stdin_packs && use_internal_rev_list) die(_("cannot use internal rev list with --stdin-packs")); @@ -4709,8 +5146,8 @@ int cmd_pack_objects(int argc, if (cruft) { if (use_internal_rev_list) die(_("cannot use internal rev list with --cruft")); - if (stdin_packs) - die(_("cannot use --stdin-packs with --cruft")); + die_for_incompatible_opt2(stdin_packs, "--stdin-packs", + cruft, "--cruft"); } /* @@ -4778,11 +5215,7 @@ int cmd_pack_objects(int argc, progress_state = start_progress(the_repository, _("Enumerating objects"), 0); if (stdin_packs) { - /* avoids adding objects in excluded packs */ - ignore_packed_keep_in_core = 1; - read_packs_list_from_stdin(); - if (rev_list_unpacked) - add_unreachable_loose_objects(); + read_stdin_packs(stdin_packs, rev_list_unpacked); } else if (cruft) { read_cruft_objects(); } else if (!use_internal_rev_list) { diff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c index 5d1fc78176..fe81c293e3 100644 --- a/builtin/pack-redundant.c +++ b/builtin/pack-redundant.c @@ -13,7 +13,7 @@ #include "hex.h" #include "packfile.h" -#include "object-store.h" +#include "odb.h" #include "strbuf.h" #define BLKSIZE 512 @@ -625,14 +625,8 @@ int cmd_pack_redundant(int argc, const char **argv, const char *prefix UNUSED, s break; } - if (!i_still_use_this) { - fputs(_("'git pack-redundant' is nominated for removal.\n" - "If you still use this command, please add an extra\n" - "option, '--i-still-use-this', on the command line\n" - "and let us know you still use it by sending an e-mail\n" - "to <git@vger.kernel.org>. Thanks.\n"), stderr); - die(_("refusing to run without --i-still-use-this")); - } + if (!i_still_use_this) + you_still_use_that("git pack-redundant"); if (load_all_packs) load_all(); diff --git a/builtin/pack-refs.c b/builtin/pack-refs.c index e47bae1c80..5e28d0f9e8 100644 --- a/builtin/pack-refs.c +++ b/builtin/pack-refs.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "parse-options.h" #include "refs.h" diff --git a/builtin/patch-id.c b/builtin/patch-id.c index cdef2ec10a..d26e9d0c1e 100644 --- a/builtin/patch-id.c +++ b/builtin/patch-id.c @@ -3,6 +3,7 @@ #include "builtin.h" #include "config.h" #include "diff.h" +#include "environment.h" #include "gettext.h" #include "hash.h" #include "hex.h" @@ -235,7 +236,7 @@ int cmd_patch_id(int argc, OPT_END() }; - git_config(git_patch_id_config, &config); + repo_config(the_repository, git_patch_id_config, &config); /* verbatim implies stable */ if (config.verbatim) @@ -254,7 +255,7 @@ int cmd_patch_id(int argc, * the code that computes patch IDs to always use SHA1. */ if (!the_hash_algo) - repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + repo_set_hash_algo(the_repository, GIT_HASH_DEFAULT); generate_id_list(opts ? opts > 1 : config.stable, opts ? opts == 3 : config.verbatim); diff --git a/builtin/prune.c b/builtin/prune.c index e930caa0c0..55635a891f 100644 --- a/builtin/prune.c +++ b/builtin/prune.c @@ -1,4 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE #define DISABLE_SIGN_COMPARE_WARNINGS #include "builtin.h" @@ -17,7 +16,7 @@ #include "replace-object.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "shallow.h" static const char * const prune_usage[] = { @@ -64,7 +63,7 @@ static void perform_reachability_traversal(struct rev_info *revs) return; if (show_progress) - progress = start_delayed_progress(the_repository, + progress = start_delayed_progress(revs->repo, _("Checking connectivity"), 0); mark_reachable_objects(revs, 1, expire, progress); stop_progress(&progress); @@ -78,7 +77,7 @@ static int is_object_reachable(const struct object_id *oid, perform_reachability_traversal(revs); - obj = lookup_object(the_repository, oid); + obj = lookup_object(revs->repo, oid); return obj && (obj->flags & SEEN); } @@ -99,8 +98,8 @@ static int prune_object(const struct object_id *oid, const char *fullpath, if (st.st_mtime > expire) return 0; if (show_only || verbose) { - enum object_type type = oid_object_info(the_repository, oid, - NULL); + enum object_type type = + odb_read_object_info(revs->repo->objects, oid, NULL); printf("%s %s\n", oid_to_hex(oid), (type > 0) ? type_name(type) : "unknown"); } @@ -154,7 +153,7 @@ static void remove_temporary_files(const char *path) int cmd_prune(int argc, const char **argv, const char *prefix, - struct repository *repo UNUSED) + struct repository *repo) { struct rev_info revs; int exclude_promisor_objects = 0; @@ -173,20 +172,19 @@ int cmd_prune(int argc, expire = TIME_MAX; save_commit_buffer = 0; disable_replace_refs(); - repo_init_revisions(the_repository, &revs, prefix); argc = parse_options(argc, argv, prefix, options, prune_usage, 0); - if (repository_format_precious_objects) + repo_init_revisions(repo, &revs, prefix); + if (repo->repository_format_precious_objects) die(_("cannot prune in a precious-objects repo")); while (argc--) { struct object_id oid; const char *name = *argv++; - if (!repo_get_oid(the_repository, name, &oid)) { - struct object *object = parse_object_or_die(the_repository, &oid, - name); + if (!repo_get_oid(repo, name, &oid)) { + struct object *object = parse_object_or_die(repo, &oid, name); add_pending_object(&revs, object, ""); } else @@ -200,16 +198,16 @@ int cmd_prune(int argc, revs.exclude_promisor_objects = 1; } - for_each_loose_file_in_objdir(repo_get_object_directory(the_repository), + for_each_loose_file_in_source(repo->objects->sources, prune_object, prune_cruft, prune_subdir, &revs); prune_packed_objects(show_only ? PRUNE_PACKED_DRY_RUN : 0); - remove_temporary_files(repo_get_object_directory(the_repository)); - s = mkpathdup("%s/pack", repo_get_object_directory(the_repository)); + remove_temporary_files(repo_get_object_directory(repo)); + s = mkpathdup("%s/pack", repo_get_object_directory(repo)); remove_temporary_files(s); free(s); - if (is_repository_shallow(the_repository)) { + if (is_repository_shallow(repo)) { perform_reachability_traversal(&revs); prune_shallow(show_only ? PRUNE_SHOW_ONLY : 0); } diff --git a/builtin/pull.c b/builtin/pull.c index a1ebc6ad33..5ebd529620 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -11,6 +11,7 @@ #include "builtin.h" #include "advice.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "hex.h" #include "merge.h" @@ -90,7 +91,8 @@ static char *opt_ff; static const char *opt_verify_signatures; static const char *opt_verify; static int opt_autostash = -1; -static int config_autostash; +static int config_rebase_autostash; +static int config_pull_autostash = -1; static int check_trust_level = 1; static struct strvec opt_strategies = STRVEC_INIT; static struct strvec opt_strategy_opts = STRVEC_INIT; @@ -143,6 +145,9 @@ static struct option pull_options[] = { OPT_PASSTHRU(0, "summary", &opt_diffstat, NULL, N_("(synonym to --stat)"), PARSE_OPT_NOARG | PARSE_OPT_HIDDEN), + OPT_PASSTHRU(0, "compact-summary", &opt_diffstat, NULL, + N_("show a compact-summary at the end of the merge"), + PARSE_OPT_NOARG), OPT_PASSTHRU(0, "log", &opt_log, N_("n"), N_("add (at most <n>) entries from shortlog to merge commit message"), PARSE_OPT_OPTARG), @@ -309,7 +314,7 @@ static const char *config_get_ff(void) { const char *value; - if (git_config_get_value("pull.ff", &value)) + if (repo_config_get_value(the_repository, "pull.ff", &value)) return NULL; switch (git_parse_maybe_bool(value)) { @@ -340,7 +345,7 @@ static enum rebase_type config_get_rebase(int *rebase_unspecified) if (curr_branch) { char *key = xstrfmt("branch.%s.rebase", curr_branch->name); - if (!git_config_get_value(key, &value)) { + if (!repo_config_get_value(the_repository, key, &value)) { enum rebase_type ret = parse_config_rebase(key, value, 1); free(key); return ret; @@ -349,7 +354,7 @@ static enum rebase_type config_get_rebase(int *rebase_unspecified) free(key); } - if (!git_config_get_value("pull.rebase", &value)) + if (!repo_config_get_value(the_repository, "pull.rebase", &value)) return parse_config_rebase("pull.rebase", value, 1); *rebase_unspecified = 1; @@ -364,7 +369,18 @@ static int git_pull_config(const char *var, const char *value, const struct config_context *ctx, void *cb) { if (!strcmp(var, "rebase.autostash")) { - config_autostash = git_config_bool(var, value); + /* + * run_rebase() also reads this option. The reason we handle it here is + * that when pull.rebase is true, a fast-forward may occur without + * invoking run_rebase(). We need to ensure that autostash is set even + * in the fast-forward case. + * + * run_merge() handles merge.autostash, so we don't handle it here. + */ + config_rebase_autostash = git_config_bool(var, value); + return 0; + } else if (!strcmp(var, "pull.autostash")) { + config_pull_autostash = git_config_bool(var, value); return 0; } else if (!strcmp(var, "submodule.recurse")) { recurse_submodules = git_config_bool(var, value) ? @@ -487,7 +503,7 @@ static void NORETURN die_no_merge_candidates(const char *repo, const char **refs } else fprintf_ln(stderr, _("Your configuration specifies to merge with the ref '%s'\n" "from the remote, but no such ref was fetched."), - *curr_branch->merge_name); + curr_branch->merge[0]->src); exit(1); } @@ -996,13 +1012,15 @@ int cmd_pull(int argc, if (!getenv("GIT_REFLOG_ACTION")) set_reflog_message(argc, argv); - git_config(git_pull_config, NULL); + repo_config(the_repository, git_pull_config, NULL); if (the_repository->gitdir) { prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; } argc = parse_options(argc, argv, prefix, pull_options, pull_usage, 0); + if (opt_autostash == -1) + opt_autostash = config_pull_autostash; if (recurse_submodules_cli != RECURSE_SUBMODULES_DEFAULT) recurse_submodules = recurse_submodules_cli; @@ -1049,7 +1067,7 @@ int cmd_pull(int argc, if (opt_rebase) { if (opt_autostash == -1) - opt_autostash = config_autostash; + opt_autostash = config_rebase_autostash; if (is_null_oid(&orig_head) && !is_index_unborn(the_repository->index)) die(_("Updating an unborn branch with changes added to the index.")); diff --git a/builtin/push.c b/builtin/push.c index 92d530e5c4..d0794b7b30 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -598,7 +598,7 @@ int cmd_push(int argc, }; packet_trace_identity("push"); - git_config(git_push_config, &flags); + repo_config(the_repository, git_push_config, &flags); argc = parse_options(argc, argv, prefix, options, push_usage, 0); push_options = (push_options_cmdline.nr ? &push_options_cmdline diff --git a/builtin/range-diff.c b/builtin/range-diff.c index 32ddb6613f..aafcc99b96 100644 --- a/builtin/range-diff.c +++ b/builtin/range-diff.c @@ -6,6 +6,7 @@ #include "parse-options.h" #include "range-diff.h" #include "config.h" +#include "parse.h" static const char * const builtin_range_diff_usage[] = { @@ -15,6 +16,21 @@ N_("git range-diff [<options>] <base> <old-tip> <new-tip>"), NULL }; +static int parse_max_memory(const struct option *opt, const char *arg, int unset) +{ + size_t *max_memory = opt->value; + uintmax_t val; + + if (unset) + return 0; + + if (!git_parse_unsigned(arg, &val, SIZE_MAX)) + return error(_("invalid max-memory value: %s"), arg); + + *max_memory = (size_t)val; + return 0; +} + int cmd_range_diff(int argc, const char **argv, const char *prefix, @@ -25,6 +41,7 @@ int cmd_range_diff(int argc, struct strvec diff_merges_arg = STRVEC_INIT; struct range_diff_options range_diff_opts = { .creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT, + .max_memory = RANGE_DIFF_MAX_MEMORY_DEFAULT, .diffopt = &diffopt, .other_arg = &other_arg }; @@ -40,6 +57,10 @@ int cmd_range_diff(int argc, PARSE_OPT_OPTARG), OPT_PASSTHRU_ARGV(0, "diff-merges", &diff_merges_arg, N_("style"), N_("passed to 'git log'"), 0), + OPT_CALLBACK(0, "max-memory", &range_diff_opts.max_memory, + N_("size"), + N_("maximum memory for cost matrix (default 4G)"), + parse_max_memory), OPT_PASSTHRU_ARGV(0, "remerge-diff", &diff_merges_arg, NULL, N_("passed to 'git log'"), PARSE_OPT_NOARG), OPT_BOOL(0, "left-only", &left_only, @@ -54,7 +75,7 @@ int cmd_range_diff(int argc, struct object_id oid; const char *three_dots = NULL; - git_config(git_diff_ui_config, NULL); + repo_config(the_repository, git_diff_ui_config, NULL); repo_diff_setup(the_repository, &diffopt); diff --git a/builtin/read-tree.c b/builtin/read-tree.c index a8f352f7cd..34f7a59f38 100644 --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@ -6,6 +6,7 @@ #define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "hex.h" #include "lockfile.h" @@ -168,7 +169,7 @@ int cmd_read_tree(int argc, opts.src_index = the_repository->index; opts.dst_index = the_repository->index; - git_config(git_read_tree_config, NULL); + repo_config(the_repository, git_read_tree_config, NULL); argc = parse_options(argc, argv, cmd_prefix, read_tree_options, read_tree_usage, 0); diff --git a/builtin/rebase.c b/builtin/rebase.c index 2e8c4ee678..67c0352bf8 100644 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@ -293,15 +293,6 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags) &revisions, &shortrevisions)) goto cleanup; - if (init_basic_state(&replay, - opts->head_name ? opts->head_name : "detached HEAD", - opts->onto, &opts->orig_head->object.oid)) - goto cleanup; - - if (!opts->upstream && opts->squash_onto) - write_file(path_squash_onto(), "%s\n", - oid_to_hex(opts->squash_onto)); - strvec_pushl(&make_script_args, "", revisions, NULL); if (opts->restrict_revision) strvec_pushf(&make_script_args, "^%s", @@ -310,21 +301,30 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags) ret = sequencer_make_script(the_repository, &todo_list.buf, make_script_args.nr, make_script_args.v, flags); - - if (ret) + if (ret) { error(_("could not generate todo list")); - else { - discard_index(the_repository->index); - if (todo_list_parse_insn_buffer(the_repository, &replay, - todo_list.buf.buf, &todo_list)) - BUG("unusable todo list"); - - ret = complete_action(the_repository, &replay, flags, - shortrevisions, opts->onto_name, opts->onto, - &opts->orig_head->object.oid, &opts->exec, - opts->autosquash, opts->update_refs, &todo_list); + goto cleanup; } + if (init_basic_state(&replay, + opts->head_name ? opts->head_name : "detached HEAD", + opts->onto, &opts->orig_head->object.oid)) + goto cleanup; + + if (!opts->upstream && opts->squash_onto) + write_file(path_squash_onto(), "%s\n", + oid_to_hex(opts->squash_onto)); + + discard_index(the_repository->index); + if (todo_list_parse_insn_buffer(the_repository, &replay, + todo_list.buf.buf, &todo_list)) + BUG("unusable todo list"); + + ret = complete_action(the_repository, &replay, flags, + shortrevisions, opts->onto_name, opts->onto, + &opts->orig_head->object.oid, &opts->exec, + opts->autosquash, opts->update_refs, &todo_list); + cleanup: replay_opts_release(&replay); free(revisions); @@ -340,7 +340,7 @@ static int run_sequencer_rebase(struct rebase_options *opts) unsigned flags = 0; int abbreviate_commands = 0, ret = 0; - git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands); + repo_config_get_bool(the_repository, "rebase.abbreviatecommands", &abbreviate_commands); flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0; flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0; @@ -1128,6 +1128,7 @@ int cmd_rebase(int argc, .short_name = 'n', .long_name = "no-stat", .value = &options.flags, + .precision = sizeof(options.flags), .help = N_("do not show diffstat of what changed upstream"), .flags = PARSE_OPT_NOARG, .defval = REBASE_DIFFSTAT, @@ -1241,10 +1242,13 @@ int cmd_rebase(int argc, builtin_rebase_usage, builtin_rebase_options); +#ifndef WITH_BREAKING_CHANGES + warn_on_auto_comment_char = true; +#endif /* !WITH_BREAKING_CHANGES */ prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; - git_config(rebase_config, &options); + repo_config(the_repository, rebase_config, &options); /* options.gpg_sign_opt will be either "-S" or NULL */ gpg_sign = options.gpg_sign_opt ? "" : NULL; FREE_AND_NULL(options.gpg_sign_opt); diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index a317d6c278..1113137a6f 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -33,7 +33,7 @@ #include "packfile.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "path.h" #include "protocol.h" #include "commit-reach.h" @@ -359,7 +359,8 @@ static void write_head_info(void) refs_for_each_fullref_in(get_main_ref_store(the_repository), "", exclude_patterns, show_ref_cb, &seen); - for_each_alternate_ref(show_one_alternate_ref, &seen); + odb_for_each_alternate_ref(the_repository->objects, + show_one_alternate_ref, &seen); oidset_clear(&seen); strvec_clear(&excludes_vector); @@ -759,8 +760,8 @@ static void prepare_push_cert_sha1(struct child_process *proc) int bogs /* beginning_of_gpg_sig */; already_done = 1; - if (write_object_file(push_cert.buf, push_cert.len, OBJ_BLOB, - &push_cert_oid)) + if (odb_write_object(the_repository->objects, push_cert.buf, + push_cert.len, OBJ_BLOB, &push_cert_oid)) oidclr(&push_cert_oid, the_repository->hash_algo); memset(&sigcheck, '\0', sizeof(sigcheck)); @@ -1508,8 +1509,8 @@ static const char *update(struct command *cmd, struct shallow_info *si) } if (!is_null_oid(new_oid) && - !has_object(the_repository, new_oid, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) { + !odb_has_object(the_repository->objects, new_oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) { error("unpack should have generated %s, " "but I can't find it!", oid_to_hex(new_oid)); ret = "bad pack"; @@ -1846,36 +1847,102 @@ static void BUG_if_skipped_connectivity_check(struct command *commands, BUG_if_bug("connectivity check skipped???"); } +static void ref_transaction_rejection_handler(const char *refname, + const struct object_id *old_oid UNUSED, + const struct object_id *new_oid UNUSED, + const char *old_target UNUSED, + const char *new_target UNUSED, + enum ref_transaction_error err, + void *cb_data) +{ + struct strmap *failed_refs = cb_data; + + strmap_put(failed_refs, refname, (char *)ref_transaction_error_msg(err)); +} + static void execute_commands_non_atomic(struct command *commands, struct shallow_info *si) { struct command *cmd; struct strbuf err = STRBUF_INIT; + const char *reported_error = NULL; + struct strmap failed_refs = STRMAP_INIT; - for (cmd = commands; cmd; cmd = cmd->next) { - if (!should_process_cmd(cmd) || cmd->run_proc_receive) - continue; + /* + * Reference updates, where D/F conflicts shouldn't arise due to + * one reference being deleted, while the other being created + * are treated as conflicts in batched updates. This is because + * we don't do conflict resolution inside a transaction. To + * mitigate this, delete references in a separate batch. + * + * NEEDSWORK: Add conflict resolution between deletion and creation + * of reference updates within a transaction. With that, we can + * combine the two phases. + */ + enum processing_phase { + PHASE_DELETIONS, + PHASE_OTHERS + }; - transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), - 0, &err); - if (!transaction) { - rp_error("%s", err.buf); - strbuf_reset(&err); - cmd->error_string = "transaction failed to start"; - continue; + for (enum processing_phase phase = PHASE_DELETIONS; phase <= PHASE_OTHERS; phase++) { + for (cmd = commands; cmd; cmd = cmd->next) { + if (!should_process_cmd(cmd) || cmd->run_proc_receive) + continue; + + if (phase == PHASE_DELETIONS && !is_null_oid(&cmd->new_oid)) + continue; + else if (phase == PHASE_OTHERS && is_null_oid(&cmd->new_oid)) + continue; + + /* + * Lazily create a transaction only when we know there are + * updates to be added. + */ + if (!transaction) { + transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), + REF_TRANSACTION_ALLOW_FAILURE, &err); + if (!transaction) { + rp_error("%s", err.buf); + strbuf_reset(&err); + reported_error = "transaction failed to start"; + goto failure; + } + } + + cmd->error_string = update(cmd, si); } - cmd->error_string = update(cmd, si); + /* No transaction, so nothing to commit */ + if (!transaction) + goto cleanup; - if (!cmd->error_string - && ref_transaction_commit(transaction, &err)) { + if (ref_transaction_commit(transaction, &err)) { rp_error("%s", err.buf); - strbuf_reset(&err); - cmd->error_string = "failed to update ref"; + reported_error = "failed to update refs"; + goto failure; } + + ref_transaction_for_each_rejected_update(transaction, + ref_transaction_rejection_handler, + &failed_refs); + + if (strmap_empty(&failed_refs)) + goto cleanup; + + failure: + for (cmd = commands; cmd; cmd = cmd->next) { + if (reported_error) + cmd->error_string = reported_error; + else if (strmap_contains(&failed_refs, cmd->ref_name)) + cmd->error_string = strmap_get(&failed_refs, cmd->ref_name); + } + + cleanup: ref_transaction_free(transaction); + transaction = NULL; + strmap_clear(&failed_refs, 0); + strbuf_release(&err); } - strbuf_release(&err); } static void execute_commands_atomic(struct command *commands, @@ -2136,7 +2203,7 @@ static struct command *read_head_info(struct packet_reader *reader, use_push_options = 1; hash = parse_feature_value(feature_list, "object-format", &len, NULL); if (!hash) { - hash = hash_algos[GIT_HASH_SHA1].name; + hash = hash_algos[GIT_HASH_SHA1_LEGACY].name; len = strlen(hash); } if (xstrncmpz(the_hash_algo->name, hash, len)) @@ -2546,7 +2613,7 @@ int cmd_receive_pack(int argc, if (!enter_repo(service_dir, 0)) die("'%s' does not appear to be a git repository", service_dir); - git_config(receive_pack_config, NULL); + repo_config(the_repository, receive_pack_config, NULL); if (cert_nonce_seed) push_cert_nonce = prepare_push_cert_nonce(service_dir, time(NULL)); diff --git a/builtin/reflog.c b/builtin/reflog.c index 3acaf3e32c..c8f6b93d60 100644 --- a/builtin/reflog.c +++ b/builtin/reflog.c @@ -3,6 +3,8 @@ #include "builtin.h" #include "config.h" #include "gettext.h" +#include "hex.h" +#include "odb.h" #include "revision.h" #include "reachable.h" #include "wildmatch.h" @@ -17,21 +19,24 @@ #define BUILTIN_REFLOG_LIST_USAGE \ N_("git reflog list") -#define BUILTIN_REFLOG_EXPIRE_USAGE \ - N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \ - " [--rewrite] [--updateref] [--stale-fix]\n" \ - " [--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]") +#define BUILTIN_REFLOG_EXISTS_USAGE \ + N_("git reflog exists <ref>") + +#define BUILTIN_REFLOG_WRITE_USAGE \ + N_("git reflog write <ref> <old-oid> <new-oid> <message>") #define BUILTIN_REFLOG_DELETE_USAGE \ N_("git reflog delete [--rewrite] [--updateref]\n" \ " [--dry-run | -n] [--verbose] <ref>@{<specifier>}...") -#define BUILTIN_REFLOG_EXISTS_USAGE \ - N_("git reflog exists <ref>") - #define BUILTIN_REFLOG_DROP_USAGE \ N_("git reflog drop [--all [--single-worktree] | <refs>...]") +#define BUILTIN_REFLOG_EXPIRE_USAGE \ + N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \ + " [--rewrite] [--updateref] [--stale-fix]\n" \ + " [--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...]") + static const char *const reflog_show_usage[] = { BUILTIN_REFLOG_SHOW_USAGE, NULL, @@ -42,9 +47,14 @@ static const char *const reflog_list_usage[] = { NULL, }; -static const char *const reflog_expire_usage[] = { - BUILTIN_REFLOG_EXPIRE_USAGE, - NULL +static const char *const reflog_exists_usage[] = { + BUILTIN_REFLOG_EXISTS_USAGE, + NULL, +}; + +static const char *const reflog_write_usage[] = { + BUILTIN_REFLOG_WRITE_USAGE, + NULL, }; static const char *const reflog_delete_usage[] = { @@ -52,23 +62,24 @@ static const char *const reflog_delete_usage[] = { NULL }; -static const char *const reflog_exists_usage[] = { - BUILTIN_REFLOG_EXISTS_USAGE, - NULL, -}; - static const char *const reflog_drop_usage[] = { BUILTIN_REFLOG_DROP_USAGE, NULL, }; +static const char *const reflog_expire_usage[] = { + BUILTIN_REFLOG_EXPIRE_USAGE, + NULL +}; + static const char *const reflog_usage[] = { BUILTIN_REFLOG_SHOW_USAGE, BUILTIN_REFLOG_LIST_USAGE, - BUILTIN_REFLOG_EXPIRE_USAGE, + BUILTIN_REFLOG_EXISTS_USAGE, + BUILTIN_REFLOG_WRITE_USAGE, BUILTIN_REFLOG_DELETE_USAGE, BUILTIN_REFLOG_DROP_USAGE, - BUILTIN_REFLOG_EXISTS_USAGE, + BUILTIN_REFLOG_EXPIRE_USAGE, NULL }; @@ -202,7 +213,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix, OPT_END() }; - git_config(reflog_expire_config, &opts); + repo_config(the_repository, reflog_expire_config, &opts); save_commit_buffer = 0; do_all = status = 0; @@ -283,6 +294,9 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix, &cb); free(ref); } + + reflog_clear_expire_config(&opts); + return status; } @@ -392,6 +406,59 @@ static int cmd_reflog_drop(int argc, const char **argv, const char *prefix, return ret; } +static int cmd_reflog_write(int argc, const char **argv, const char *prefix, + struct repository *repo) +{ + const struct option options[] = { + OPT_END() + }; + struct object_id old_oid, new_oid; + struct strbuf err = STRBUF_INIT; + struct ref_transaction *tx; + const char *ref, *message; + int ret; + + argc = parse_options(argc, argv, prefix, options, reflog_write_usage, 0); + if (argc != 4) + usage_with_options(reflog_write_usage, options); + + ref = argv[0]; + if (!is_root_ref(ref) && check_refname_format(ref, 0)) + die(_("invalid reference name: %s"), ref); + + ret = get_oid_hex_algop(argv[1], &old_oid, repo->hash_algo); + if (ret) + die(_("invalid old object ID: '%s'"), argv[1]); + if (!is_null_oid(&old_oid) && !odb_has_object(repo->objects, &old_oid, 0)) + die(_("old object '%s' does not exist"), argv[1]); + + ret = get_oid_hex_algop(argv[2], &new_oid, repo->hash_algo); + if (ret) + die(_("invalid new object ID: '%s'"), argv[2]); + if (!is_null_oid(&new_oid) && !odb_has_object(repo->objects, &new_oid, 0)) + die(_("new object '%s' does not exist"), argv[2]); + + message = argv[3]; + + tx = ref_store_transaction_begin(get_main_ref_store(repo), 0, &err); + if (!tx) + die(_("cannot start transaction: %s"), err.buf); + + ret = ref_transaction_update_reflog(tx, ref, &new_oid, &old_oid, + git_committer_info(0), + message, 0, &err); + if (ret) + die(_("cannot queue reflog update: %s"), err.buf); + + ret = ref_transaction_commit(tx, &err); + if (ret) + die(_("cannot commit reflog update: %s"), err.buf); + + ref_transaction_free(tx); + strbuf_release(&err); + return 0; +} + /* * main "reflog" */ @@ -404,10 +471,11 @@ int cmd_reflog(int argc, struct option options[] = { OPT_SUBCOMMAND("show", &fn, cmd_reflog_show), OPT_SUBCOMMAND("list", &fn, cmd_reflog_list), - OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire), - OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete), OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists), + OPT_SUBCOMMAND("write", &fn, cmd_reflog_write), + OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete), OPT_SUBCOMMAND("drop", &fn, cmd_reflog_drop), + OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire), OPT_END() }; diff --git a/builtin/refs.c b/builtin/refs.c index 998d2a2c1c..91548783b7 100644 --- a/builtin/refs.c +++ b/builtin/refs.c @@ -6,6 +6,8 @@ #include "refs.h" #include "strbuf.h" #include "worktree.h" +#include "for-each-ref.h" +#include "refs/refs-internal.h" #define REFS_MIGRATE_USAGE \ N_("git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]") @@ -13,6 +15,9 @@ #define REFS_VERIFY_USAGE \ N_("git refs verify [--strict] [--verbose]") +#define REFS_EXISTS_USAGE \ + N_("git refs exists <ref>") + static int cmd_refs_migrate(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) { @@ -88,7 +93,7 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix, if (argc) usage(_("'git refs verify' takes no arguments")); - git_config(git_fsck_config, &fsck_refs_options); + repo_config(the_repository, git_fsck_config, &fsck_refs_options); prepare_repo_settings(the_repository); worktrees = get_worktrees_without_reading_head(); @@ -101,6 +106,59 @@ static int cmd_refs_verify(int argc, const char **argv, const char *prefix, return ret; } +static int cmd_refs_list(int argc, const char **argv, const char *prefix, + struct repository *repo) +{ + static char const * const refs_list_usage[] = { + N_("git refs list " COMMON_USAGE_FOR_EACH_REF), + NULL + }; + + return for_each_ref_core(argc, argv, prefix, repo, refs_list_usage); +} + +static int cmd_refs_exists(int argc, const char **argv, const char *prefix, + struct repository *repo UNUSED) +{ + struct strbuf unused_referent = STRBUF_INIT; + struct object_id unused_oid; + unsigned int unused_type; + int failure_errno = 0; + const char *ref; + int ret = 0; + const char * const exists_usage[] = { + REFS_EXISTS_USAGE, + NULL, + }; + struct option options[] = { + OPT_END(), + }; + + argc = parse_options(argc, argv, prefix, options, exists_usage, 0); + if (argc != 1) + die(_("'git refs exists' requires a reference")); + + ref = *argv++; + if (refs_read_raw_ref(get_main_ref_store(the_repository), ref, + &unused_oid, &unused_referent, &unused_type, + &failure_errno)) { + if (failure_errno == ENOENT || failure_errno == EISDIR) { + error(_("reference does not exist")); + ret = 2; + } else { + errno = failure_errno; + error_errno(_("failed to look up reference")); + ret = 1; + } + + goto out; + } + +out: + strbuf_release(&unused_referent); + return ret; +} + int cmd_refs(int argc, const char **argv, const char *prefix, @@ -109,12 +167,16 @@ int cmd_refs(int argc, const char * const refs_usage[] = { REFS_MIGRATE_USAGE, REFS_VERIFY_USAGE, + "git refs list " COMMON_USAGE_FOR_EACH_REF, + REFS_EXISTS_USAGE, NULL, }; parse_opt_subcommand_fn *fn = NULL; struct option opts[] = { OPT_SUBCOMMAND("migrate", &fn, cmd_refs_migrate), OPT_SUBCOMMAND("verify", &fn, cmd_refs_verify), + OPT_SUBCOMMAND("list", &fn, cmd_refs_list), + OPT_SUBCOMMAND("exists", &fn, cmd_refs_exists), OPT_END(), }; diff --git a/builtin/remote.c b/builtin/remote.c index 0d6755bcb7..8a7ed4299a 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -1,9 +1,11 @@ #define USE_THE_REPOSITORY_VARIABLE -#define DISABLE_SIGN_COMPARE_WARNINGS #include "builtin.h" +#include "advice.h" #include "config.h" +#include "date.h" #include "gettext.h" +#include "ident.h" #include "parse-options.h" #include "path.h" #include "transport.h" @@ -14,7 +16,7 @@ #include "rebase.h" #include "refs.h" #include "refspec.h" -#include "object-store.h" +#include "odb.h" #include "strvec.h" #include "commit-reach.h" #include "progress.h" @@ -132,7 +134,7 @@ static void add_branch(const char *key, const char *branchname, else strbuf_addf(tmp, "refs/heads/%s:refs/remotes/%s/%s", branchname, remotename, branchname); - git_config_set_multivar(key, tmp->buf, "^$", 0); + repo_config_set_multivar(the_repository, key, tmp->buf, "^$", 0); } static const char mirror_advice[] = @@ -157,6 +159,21 @@ static int parse_mirror_opt(const struct option *opt, const char *arg, int not) return 0; } +static int check_remote_collision(struct remote *remote, void *data) +{ + const char *name = data; + const char *p; + + if (skip_prefix(name, remote->name, &p) && *p == '/') + die(_("remote name '%s' is a subset of existing remote '%s'"), + name, remote->name); + if (skip_prefix(remote->name, name, &p) && *p == '/') + die(_("remote name '%s' is a superset of existing remote '%s'"), + name, remote->name); + + return 0; +} + static int add(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) { @@ -167,7 +184,6 @@ static int add(int argc, const char **argv, const char *prefix, struct remote *remote; struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT; const char *name, *url; - int i; int result = 0; struct option options[] = { @@ -208,15 +224,17 @@ static int add(int argc, const char **argv, const char *prefix, if (!valid_remote_name(name)) die(_("'%s' is not a valid remote name"), name); + for_each_remote(check_remote_collision, (void *)name); + strbuf_addf(&buf, "remote.%s.url", name); - git_config_set(buf.buf, url); + repo_config_set(the_repository, buf.buf, url); if (!mirror || mirror & MIRROR_FETCH) { strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.fetch", name); if (track.nr == 0) string_list_append(&track, "*"); - for (i = 0; i < track.nr; i++) { + for (size_t i = 0; i < track.nr; i++) { add_branch(buf.buf, track.items[i].string, name, mirror, &buf2); } @@ -225,14 +243,14 @@ static int add(int argc, const char **argv, const char *prefix, if (mirror & MIRROR_PUSH) { strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.mirror", name); - git_config_set(buf.buf, "true"); + repo_config_set(the_repository, buf.buf, "true"); } if (fetch_tags != TAGS_DEFAULT) { strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.tagOpt", name); - git_config_set(buf.buf, - fetch_tags == TAGS_SET ? "--tags" : "--no-tags"); + repo_config_set(the_repository, buf.buf, + fetch_tags == TAGS_SET ? "--tags" : "--no-tags"); } if (fetch && fetch_remote(name)) { @@ -353,7 +371,7 @@ static void read_branches(void) { if (branch_list.nr) return; - git_config(config_read_branches, NULL); + repo_config(the_repository, config_read_branches, NULL); } struct ref_states { @@ -454,8 +472,8 @@ static int get_push_ref_states(const struct ref *remote_refs, info->status = PUSH_STATUS_UPTODATE; else if (is_null_oid(&ref->old_oid)) info->status = PUSH_STATUS_CREATE; - else if (has_object(the_repository, &ref->old_oid, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) && + else if (odb_has_object(the_repository->objects, &ref->old_oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) && ref_newer(&ref->new_oid, &ref->old_oid)) info->status = PUSH_STATUS_FASTFORWARD; else @@ -595,54 +613,170 @@ static int add_branch_for_removal(const char *refname, struct rename_info { const char *old_name; const char *new_name; - struct string_list *remote_branches; - uint32_t symrefs_nr; + struct ref_transaction *transaction; + struct progress *progress; + struct strbuf *err; + uint32_t progress_nr; + uint64_t index; }; -static int read_remote_branches(const char *refname, const char *referent UNUSED, - const struct object_id *oid UNUSED, - int flags UNUSED, void *cb_data) +static void compute_renamed_ref(struct rename_info *rename, + const char *refname, + struct strbuf *out) +{ + strbuf_reset(out); + strbuf_addstr(out, refname); + strbuf_splice(out, strlen("refs/remotes/"), strlen(rename->old_name), + rename->new_name, strlen(rename->new_name)); +} + +static int rename_one_reflog_entry(const char *old_refname, + struct object_id *old_oid, + struct object_id *new_oid, + const char *committer, + timestamp_t timestamp, int tz, + const char *msg, void *cb_data) { struct rename_info *rename = cb_data; - struct strbuf buf = STRBUF_INIT; - struct string_list_item *item; - int flag; - const char *symref; - - strbuf_addf(&buf, "refs/remotes/%s/", rename->old_name); - if (starts_with(refname, buf.buf)) { - item = string_list_append(rename->remote_branches, refname); - symref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), - refname, RESOLVE_REF_READING, - NULL, &flag); - if (symref && (flag & REF_ISSYMREF)) { - item->util = xstrdup(symref); - rename->symrefs_nr++; - } else { - item->util = NULL; - } + struct strbuf new_refname = STRBUF_INIT; + struct strbuf identity = STRBUF_INIT; + struct strbuf name = STRBUF_INIT; + struct strbuf mail = STRBUF_INIT; + struct ident_split ident; + const char *date; + int error; + + compute_renamed_ref(rename, old_refname, &new_refname); + + if (split_ident_line(&ident, committer, strlen(committer)) < 0) { + error = -1; + goto out; } - strbuf_release(&buf); - return 0; + strbuf_add(&name, ident.name_begin, ident.name_end - ident.name_begin); + strbuf_add(&mail, ident.mail_begin, ident.mail_end - ident.mail_begin); + + date = show_date(timestamp, tz, DATE_MODE(NORMAL)); + strbuf_addstr(&identity, fmt_ident(name.buf, mail.buf, + WANT_BLANK_IDENT, date, 0)); + + error = ref_transaction_update_reflog(rename->transaction, new_refname.buf, + new_oid, old_oid, identity.buf, msg, + rename->index++, rename->err); + +out: + strbuf_release(&new_refname); + strbuf_release(&identity); + strbuf_release(&name); + strbuf_release(&mail); + return error; +} + +static int rename_one_reflog(const char *old_refname, + const struct object_id *old_oid, + struct rename_info *rename) +{ + struct strbuf new_refname = STRBUF_INIT; + struct strbuf message = STRBUF_INIT; + int error; + + if (!refs_reflog_exists(get_main_ref_store(the_repository), old_refname)) + return 0; + + error = refs_for_each_reflog_ent(get_main_ref_store(the_repository), + old_refname, rename_one_reflog_entry, rename); + if (error < 0) + goto out; + + compute_renamed_ref(rename, old_refname, &new_refname); + + /* + * Manually write the reflog entry for the now-renamed ref. We cannot + * rely on `rename_one_ref()` to do this for us as that would screw + * over order in which reflog entries are being written. + * + * Furthermore, we only append the entry in case the reference + * resolves. Missing references shouldn't have reflogs anyway. + */ + strbuf_addf(&message, "remote: renamed %s to %s", old_refname, + new_refname.buf); + + error = ref_transaction_update_reflog(rename->transaction, new_refname.buf, + old_oid, old_oid, git_committer_info(0), + message.buf, rename->index++, rename->err); + if (error < 0) + return error; + +out: + strbuf_release(&new_refname); + strbuf_release(&message); + return error; +} + +static int rename_one_ref(const char *old_refname, const char *referent, + const struct object_id *oid, + int flags, void *cb_data) +{ + struct strbuf new_referent = STRBUF_INIT; + struct strbuf new_refname = STRBUF_INIT; + struct rename_info *rename = cb_data; + int error; + + compute_renamed_ref(rename, old_refname, &new_refname); + + if (flags & REF_ISSYMREF) { + /* + * Stupidly enough `referent` is not pointing to the immediate + * target of a symref, but it's the recursively resolved value. + * So symrefs pointing to symrefs would be misresolved, and + * unborn symrefs don't have any value for the `referent` at all. + */ + referent = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), + old_refname, RESOLVE_REF_NO_RECURSE, + NULL, NULL); + compute_renamed_ref(rename, referent, &new_referent); + oid = NULL; + } + + error = ref_transaction_delete(rename->transaction, old_refname, + oid, referent, REF_NO_DEREF, NULL, rename->err); + if (error < 0) + goto out; + + error = ref_transaction_update(rename->transaction, new_refname.buf, oid, null_oid(the_hash_algo), + (flags & REF_ISSYMREF) ? new_referent.buf : NULL, NULL, + REF_SKIP_CREATE_REFLOG | REF_NO_DEREF | REF_SKIP_OID_VERIFICATION, + NULL, rename->err); + if (error < 0) + goto out; + + error = rename_one_reflog(old_refname, oid, rename); + if (error < 0) + goto out; + + display_progress(rename->progress, ++rename->progress_nr); + +out: + strbuf_release(&new_referent); + strbuf_release(&new_refname); + return error; } static int migrate_file(struct remote *remote) { struct strbuf buf = STRBUF_INIT; - int i; strbuf_addf(&buf, "remote.%s.url", remote->name); - for (i = 0; i < remote->url.nr; i++) - git_config_set_multivar(buf.buf, remote->url.v[i], "^$", 0); + for (size_t i = 0; i < remote->url.nr; i++) + repo_config_set_multivar(the_repository, buf.buf, remote->url.v[i], "^$", 0); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.push", remote->name); - for (i = 0; i < remote->push.nr; i++) - git_config_set_multivar(buf.buf, remote->push.items[i].raw, "^$", 0); + for (int i = 0; i < remote->push.nr; i++) + repo_config_set_multivar(the_repository, buf.buf, remote->push.items[i].raw, "^$", 0); strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.fetch", remote->name); - for (i = 0; i < remote->fetch.nr; i++) - git_config_set_multivar(buf.buf, remote->fetch.items[i].raw, "^$", 0); + for (int i = 0; i < remote->fetch.nr; i++) + repo_config_set_multivar(the_repository, buf.buf, remote->fetch.items[i].raw, "^$", 0); #ifndef WITH_BREAKING_CHANGES if (remote->origin == REMOTE_REMOTES) unlink_or_warn(repo_git_path_replace(the_repository, &buf, @@ -690,12 +824,12 @@ static void handle_push_default(const char* old_name, const char* new_name) .origin = STRBUF_INIT, .linenr = -1, }; - git_config(config_read_push_default, &push_default); + repo_config(the_repository, config_read_push_default, &push_default); if (push_default.scope >= CONFIG_SCOPE_COMMAND) ; /* pass */ else if (push_default.scope >= CONFIG_SCOPE_LOCAL) { - int result = git_config_set_gently("remote.pushDefault", - new_name); + int result = repo_config_set_gently(the_repository, "remote.pushDefault", + new_name); if (new_name && result && result != CONFIG_NOTHING_SET) die(_("could not set '%s'"), "remote.pushDefault"); else if (!new_name && result && result != CONFIG_NOTHING_SET) @@ -713,6 +847,14 @@ static void handle_push_default(const char* old_name, const char* new_name) strbuf_release(&push_default.origin); } +static const char conflicting_remote_refs_advice[] = N_( + "The remote you are trying to rename has conflicting references in the\n" + "new target refspec. This is most likely caused by you trying to nest\n" + "a remote into itself, e.g. by renaming 'parent' into 'parent/child'\n" + "or by unnesting a remote, e.g. the other way round.\n" + "\n" + "If that is the case, you can address this by first renaming the\n" + "remote to a different name.\n"); static int mv(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) @@ -724,11 +866,11 @@ static int mv(int argc, const char **argv, const char *prefix, }; struct remote *oldremote, *newremote; struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT, - old_remote_context = STRBUF_INIT; - struct string_list remote_branches = STRING_LIST_INIT_DUP; - struct rename_info rename; - int i, refs_renamed_nr = 0, refspec_updated = 0; - struct progress *progress = NULL; + old_remote_context = STRBUF_INIT, err = STRBUF_INIT; + struct rename_info rename = { + .err = &err, + }; + int refspecs_need_update = 0; int result = 0; argc = parse_options(argc, argv, prefix, options, @@ -739,8 +881,6 @@ static int mv(int argc, const char **argv, const char *prefix, rename.old_name = argv[0]; rename.new_name = argv[1]; - rename.remote_branches = &remote_branches; - rename.symrefs_nr = 0; oldremote = remote_get(rename.old_name); if (!remote_is_configured(oldremote, 1)) { @@ -768,19 +908,50 @@ static int mv(int argc, const char **argv, const char *prefix, goto out; } + strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name); + + for (int i = 0; i < oldremote->fetch.nr && !refspecs_need_update; i++) + refspecs_need_update = !!strstr(oldremote->fetch.items[i].raw, + old_remote_context.buf); + + if (refspecs_need_update) { + rename.transaction = ref_store_transaction_begin(get_main_ref_store(the_repository), + 0, &err); + if (!rename.transaction) + goto out; + + if (show_progress) + rename.progress = start_delayed_progress(the_repository, + _("Renaming remote references"), 0); + + strbuf_reset(&buf); + strbuf_addf(&buf, "refs/remotes/%s/", rename.old_name); + + result = refs_for_each_rawref_in(get_main_ref_store(the_repository), buf.buf, + rename_one_ref, &rename); + if (result < 0) + die(_("queueing remote ref renames failed: %s"), rename.err->buf); + + result = ref_transaction_prepare(rename.transaction, &err); + if (result < 0) { + error("renaming remote references failed: %s", err.buf); + if (result == REF_TRANSACTION_ERROR_NAME_CONFLICT) + advise(conflicting_remote_refs_advice); + die(NULL); + } + } + if (oldremote->fetch.nr) { strbuf_reset(&buf); strbuf_addf(&buf, "remote.%s.fetch", rename.new_name); - git_config_set_multivar(buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); - strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name); - for (i = 0; i < oldremote->fetch.nr; i++) { + repo_config_set_multivar(the_repository, buf.buf, NULL, NULL, CONFIG_FLAGS_MULTI_REPLACE); + for (int i = 0; i < oldremote->fetch.nr; i++) { char *ptr; strbuf_reset(&buf2); strbuf_addstr(&buf2, oldremote->fetch.items[i].raw); ptr = strstr(buf2.buf, old_remote_context.buf); if (ptr) { - refspec_updated = 1; strbuf_splice(&buf2, ptr-buf2.buf + strlen(":refs/remotes/"), strlen(rename.old_name), rename.new_name, @@ -791,103 +962,43 @@ static int mv(int argc, const char **argv, const char *prefix, "\tPlease update the configuration manually if necessary."), buf2.buf); - git_config_set_multivar(buf.buf, buf2.buf, "^$", 0); + repo_config_set_multivar(the_repository, buf.buf, buf2.buf, "^$", 0); } } read_branches(); - for (i = 0; i < branch_list.nr; i++) { + for (size_t i = 0; i < branch_list.nr; i++) { struct string_list_item *item = branch_list.items + i; struct branch_info *info = item->util; if (info->remote_name && !strcmp(info->remote_name, rename.old_name)) { strbuf_reset(&buf); strbuf_addf(&buf, "branch.%s.remote", item->string); - git_config_set(buf.buf, rename.new_name); + repo_config_set(the_repository, buf.buf, rename.new_name); } if (info->push_remote_name && !strcmp(info->push_remote_name, rename.old_name)) { strbuf_reset(&buf); strbuf_addf(&buf, "branch.%s.pushRemote", item->string); - git_config_set(buf.buf, rename.new_name); + repo_config_set(the_repository, buf.buf, rename.new_name); } } - if (!refspec_updated) - goto out; - - /* - * First remove symrefs, then rename the rest, finally create - * the new symrefs. - */ - refs_for_each_ref(get_main_ref_store(the_repository), - read_remote_branches, &rename); - if (show_progress) { - /* - * Count symrefs twice, since "renaming" them is done by - * deleting and recreating them in two separate passes. - */ - progress = start_progress(the_repository, - _("Renaming remote references"), - rename.remote_branches->nr + rename.symrefs_nr); - } - for (i = 0; i < remote_branches.nr; i++) { - struct string_list_item *item = remote_branches.items + i; - struct strbuf referent = STRBUF_INIT; - - if (refs_read_symbolic_ref(get_main_ref_store(the_repository), item->string, - &referent)) - continue; - if (refs_delete_ref(get_main_ref_store(the_repository), NULL, item->string, NULL, REF_NO_DEREF)) - die(_("deleting '%s' failed"), item->string); - - strbuf_release(&referent); - display_progress(progress, ++refs_renamed_nr); - } - for (i = 0; i < remote_branches.nr; i++) { - struct string_list_item *item = remote_branches.items + i; + if (refspecs_need_update) { + result = ref_transaction_commit(rename.transaction, &err); + if (result < 0) + die(_("renaming remote refs failed: %s"), rename.err->buf); - if (item->util) - continue; - strbuf_reset(&buf); - strbuf_addstr(&buf, item->string); - strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name), - rename.new_name, strlen(rename.new_name)); - strbuf_reset(&buf2); - strbuf_addf(&buf2, "remote: renamed %s to %s", - item->string, buf.buf); - if (refs_rename_ref(get_main_ref_store(the_repository), item->string, buf.buf, buf2.buf)) - die(_("renaming '%s' failed"), item->string); - display_progress(progress, ++refs_renamed_nr); - } - for (i = 0; i < remote_branches.nr; i++) { - struct string_list_item *item = remote_branches.items + i; + stop_progress(&rename.progress); - if (!item->util) - continue; - strbuf_reset(&buf); - strbuf_addstr(&buf, item->string); - strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name), - rename.new_name, strlen(rename.new_name)); - strbuf_reset(&buf2); - strbuf_addstr(&buf2, item->util); - strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old_name), - rename.new_name, strlen(rename.new_name)); - strbuf_reset(&buf3); - strbuf_addf(&buf3, "remote: renamed %s to %s", - item->string, buf.buf); - if (refs_update_symref(get_main_ref_store(the_repository), buf.buf, buf2.buf, buf3.buf)) - die(_("creating '%s' failed"), buf.buf); - display_progress(progress, ++refs_renamed_nr); + handle_push_default(rename.old_name, rename.new_name); } - stop_progress(&progress); - - handle_push_default(rename.old_name, rename.new_name); out: - string_list_clear(&remote_branches, 1); + ref_transaction_free(rename.transaction); strbuf_release(&old_remote_context); strbuf_release(&buf); strbuf_release(&buf2); strbuf_release(&buf3); + strbuf_release(&err); return result; } @@ -903,7 +1014,7 @@ static int rm(int argc, const char **argv, const char *prefix, struct string_list branches = STRING_LIST_INIT_DUP; struct string_list skipped = STRING_LIST_INIT_DUP; struct branches_for_remote cb_data; - int i, result; + int result; memset(&cb_data, 0, sizeof(cb_data)); cb_data.branches = &branches; @@ -925,7 +1036,7 @@ static int rm(int argc, const char **argv, const char *prefix, for_each_remote(add_known_remote, &known_remotes); read_branches(); - for (i = 0; i < branch_list.nr; i++) { + for (size_t i = 0; i < branch_list.nr; i++) { struct string_list_item *item = branch_list.items + i; struct branch_info *info = item->util; if (info->remote_name && !strcmp(info->remote_name, remote->name)) { @@ -934,7 +1045,7 @@ static int rm(int argc, const char **argv, const char *prefix, strbuf_reset(&buf); strbuf_addf(&buf, "branch.%s.%s", item->string, *k); - result = git_config_set_gently(buf.buf, NULL); + result = repo_config_set_gently(the_repository, buf.buf, NULL); if (result && result != CONFIG_NOTHING_SET) die(_("could not unset '%s'"), buf.buf); } @@ -942,7 +1053,7 @@ static int rm(int argc, const char **argv, const char *prefix, if (info->push_remote_name && !strcmp(info->push_remote_name, remote->name)) { strbuf_reset(&buf); strbuf_addf(&buf, "branch.%s.pushremote", item->string); - result = git_config_set_gently(buf.buf, NULL); + result = repo_config_set_gently(the_repository, buf.buf, NULL); if (result && result != CONFIG_NOTHING_SET) die(_("could not unset '%s'"), buf.buf); } @@ -971,7 +1082,7 @@ static int rm(int argc, const char **argv, const char *prefix, "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n" "to delete them, use:", skipped.nr)); - for (i = 0; i < skipped.nr; i++) + for (size_t i = 0; i < skipped.nr; i++) fprintf(stderr, " git branch -d %s\n", skipped.items[i].string); } @@ -1149,7 +1260,6 @@ static int show_local_info_item(struct string_list_item *item, void *cb_data) struct branch_info *branch_info = item->util; struct string_list *merge = &branch_info->merge; int width = show_info->width + 4; - int i; if (branch_info->rebase >= REBASE_TRUE && branch_info->merge.nr > 1) { error(_("invalid branch.%s.merge; cannot rebase onto > 1 branch"), @@ -1175,7 +1285,7 @@ static int show_local_info_item(struct string_list_item *item, void *cb_data) } else { printf_ln(_("merges with remote %s"), merge->items[0].string); } - for (i = 1; i < merge->nr; i++) + for (size_t i = 1; i < merge->nr; i++) printf(_("%-*s and with remote %s\n"), width, "", merge->items[i].string); @@ -1260,7 +1370,6 @@ static int get_one_entry(struct remote *remote, void *priv) struct string_list *list = priv; struct strbuf remote_info_buf = STRBUF_INIT; struct strvec *url; - int i; if (remote->url.nr > 0) { struct strbuf promisor_config = STRBUF_INIT; @@ -1268,7 +1377,7 @@ static int get_one_entry(struct remote *remote, void *priv) strbuf_addf(&promisor_config, "remote.%s.partialclonefilter", remote->name); strbuf_addf(&remote_info_buf, "%s (fetch)", remote->url.v[0]); - if (!git_config_get_string_tmp(promisor_config.buf, &partial_clone_filter)) + if (!repo_config_get_string_tmp(the_repository, promisor_config.buf, &partial_clone_filter)) strbuf_addf(&remote_info_buf, " [%s]", partial_clone_filter); strbuf_release(&promisor_config); @@ -1277,8 +1386,7 @@ static int get_one_entry(struct remote *remote, void *priv) } else string_list_append(list, remote->name)->util = NULL; url = push_url_of_remote(remote); - for (i = 0; i < url->nr; i++) - { + for (size_t i = 0; i < url->nr; i++) { strbuf_addf(&remote_info_buf, "%s (push)", url->v[i]); string_list_append(list, remote->name)->util = strbuf_detach(&remote_info_buf, NULL); @@ -1295,10 +1403,8 @@ static int show_all(void) result = for_each_remote(get_one_entry, &list); if (!result) { - int i; - string_list_sort(&list); - for (i = 0; i < list.nr; i++) { + for (size_t i = 0; i < list.nr; i++) { struct string_list_item *item = list.items + i; if (verbose) printf("%s\t%s\n", item->string, @@ -1335,7 +1441,7 @@ static int show(int argc, const char **argv, const char *prefix, query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES); for (; argc; argc--, argv++) { - int i; + size_t i; struct strvec *url; get_remote_ref_states(*argv, &info.states, query_flag); @@ -1441,7 +1547,7 @@ static void report_set_head_auto(const char *remote, const char *head_name, static int set_head(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) { - int i, opt_a = 0, opt_d = 0, result = 0, was_detached; + int opt_a = 0, opt_d = 0, result = 0, was_detached; struct strbuf b_head = STRBUF_INIT, b_remote_head = STRBUF_INIT, b_local_head = STRBUF_INIT; char *head_name = NULL; @@ -1457,10 +1563,13 @@ static int set_head(int argc, const char **argv, const char *prefix, }; argc = parse_options(argc, argv, prefix, options, builtin_remote_sethead_usage, 0); - if (argc) { - strbuf_addf(&b_head, "refs/remotes/%s/HEAD", argv[0]); - remote = remote_get(argv[0]); - } + + /* All modes require at least a remote name. */ + if (!argc) + usage_with_options(builtin_remote_sethead_usage, options); + + strbuf_addf(&b_head, "refs/remotes/%s/HEAD", argv[0]); + remote = remote_get(argv[0]); if (!opt_a && !opt_d && argc == 2) { head_name = xstrdup(argv[1]); @@ -1472,7 +1581,7 @@ static int set_head(int argc, const char **argv, const char *prefix, else if (states.heads.nr > 1) { result |= error(_("Multiple remote HEAD branches. " "Please choose one explicitly with:")); - for (i = 0; i < states.heads.nr; i++) + for (size_t i = 0; i < states.heads.nr; i++) fprintf(stderr, " git remote set-head %s %s\n", argv[0], states.heads.items[i].string); } else @@ -1503,7 +1612,7 @@ static int set_head(int argc, const char **argv, const char *prefix, struct strbuf config_name = STRBUF_INIT; strbuf_addf(&config_name, "remote.%s.followremotehead", remote->name); - git_config_set(config_name.buf, "warn"); + repo_config_set(the_repository, config_name.buf, "warn"); strbuf_release(&config_name); } @@ -1521,9 +1630,6 @@ static int prune_remote(const char *remote, int dry_run) struct ref_states states = REF_STATES_INIT; struct string_list refs_to_prune = STRING_LIST_INIT_NODUP; struct string_list_item *item; - const char *dangling_msg = dry_run - ? _(" %s will become dangling!") - : _(" %s has become dangling!"); get_remote_ref_states(remote, &states, GET_REF_STATES); @@ -1555,7 +1661,7 @@ static int prune_remote(const char *remote, int dry_run) } refs_warn_dangling_symrefs(get_main_ref_store(the_repository), - stdout, dangling_msg, &refs_to_prune); + stdout, " ", dry_run, &refs_to_prune); string_list_clear(&refs_to_prune, 0); free_remote_ref_states(&states); @@ -1623,7 +1729,7 @@ static int update(int argc, const char **argv, const char *prefix, strvec_push(&cmd.args, argv[i]); if (strcmp(cmd.args.v[cmd.args.nr-1], "default") == 0) { - git_config(get_remote_default, &default_defined); + repo_config(the_repository, get_remote_default, &default_defined); if (!default_defined) { strvec_pop(&cmd.args); strvec_push(&cmd.args, "--all"); @@ -1636,8 +1742,8 @@ static int update(int argc, const char **argv, const char *prefix, static int remove_all_fetch_refspecs(const char *key) { - return git_config_set_multivar_gently(key, NULL, NULL, - CONFIG_FLAGS_MULTI_REPLACE); + return repo_config_set_multivar_gently(the_repository, key, NULL, NULL, + CONFIG_FLAGS_MULTI_REPLACE); } static void add_branches(struct remote *remote, const char **branches, @@ -1700,7 +1806,7 @@ static int set_branches(int argc, const char **argv, const char *prefix, static int get_url(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) { - int i, push_mode = 0, all_mode = 0; + int push_mode = 0, all_mode = 0; const char *remotename = NULL; struct remote *remote; struct strvec *url; @@ -1728,7 +1834,7 @@ static int get_url(int argc, const char **argv, const char *prefix, url = push_mode ? push_url_of_remote(remote) : &remote->url; if (all_mode) { - for (i = 0; i < url->nr; i++) + for (size_t i = 0; i < url->nr; i++) printf_ln("%s", url->v[i]); } else { printf_ln("%s", url->v[0]); @@ -1740,7 +1846,7 @@ static int get_url(int argc, const char **argv, const char *prefix, static int set_url(int argc, const char **argv, const char *prefix, struct repository *repo UNUSED) { - int i, push_mode = 0, add_mode = 0, delete_mode = 0; + int push_mode = 0, add_mode = 0, delete_mode = 0; int matches = 0, negative_matches = 0; const char *remotename = NULL; const char *newurl = NULL; @@ -1793,10 +1899,10 @@ static int set_url(int argc, const char **argv, const char *prefix, /* Special cases that add new entry. */ if ((!oldurl && !delete_mode) || add_mode) { if (add_mode) - git_config_set_multivar(name_buf.buf, newurl, + repo_config_set_multivar(the_repository, name_buf.buf, newurl, "^$", 0); else - git_config_set(name_buf.buf, newurl); + repo_config_set(the_repository, name_buf.buf, newurl); goto out; } @@ -1804,7 +1910,7 @@ static int set_url(int argc, const char **argv, const char *prefix, if (regcomp(&old_regex, oldurl, REG_EXTENDED)) die(_("Invalid old URL pattern: %s"), oldurl); - for (i = 0; i < urlset->nr; i++) + for (size_t i = 0; i < urlset->nr; i++) if (!regexec(&old_regex, urlset->v[i], 0, NULL, 0)) matches++; else @@ -1817,10 +1923,10 @@ static int set_url(int argc, const char **argv, const char *prefix, regfree(&old_regex); if (!delete_mode) - git_config_set_multivar(name_buf.buf, newurl, oldurl, 0); + repo_config_set_multivar(the_repository, name_buf.buf, newurl, oldurl, 0); else - git_config_set_multivar(name_buf.buf, NULL, oldurl, - CONFIG_FLAGS_MULTI_REPLACE); + repo_config_set_multivar(the_repository, name_buf.buf, NULL, oldurl, + CONFIG_FLAGS_MULTI_REPLACE); out: strbuf_release(&name_buf); return 0; diff --git a/builtin/repack.c b/builtin/repack.c index 59214dbdfd..c490a51e91 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -17,7 +17,7 @@ #include "midx.h" #include "packfile.h" #include "prune-packed.h" -#include "object-store.h" +#include "odb.h" #include "promisor-remote.h" #include "shallow.h" #include "pack.h" @@ -39,11 +39,12 @@ static int write_bitmaps = -1; static int use_delta_islands; static int run_update_server_info = 1; static char *packdir, *packtmp_name, *packtmp; +static int midx_must_contain_cruft = 1; static const char *const git_repack_usage[] = { N_("git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n" - "[--write-midx] [--name-hash-version=<n>]"), + "[--write-midx] [--name-hash-version=<n>] [--path-walk]"), NULL }; @@ -63,6 +64,7 @@ struct pack_objects_args { int quiet; int local; int name_hash_version; + int path_walk; struct list_objects_filter_options filter_options; }; @@ -107,6 +109,10 @@ static int repack_config(const char *var, const char *value, free(cruft_po_args->threads); return git_config_string(&cruft_po_args->threads, var, value); } + if (!strcmp(var, "repack.midxmustcontaincruft")) { + midx_must_contain_cruft = git_config_bool(var, value); + return 0; + } return git_default_config(var, value, ctx, cb); } @@ -217,9 +223,10 @@ static void mark_packs_for_deletion(struct existing_packs *existing, static void remove_redundant_pack(const char *dir_name, const char *base_name) { struct strbuf buf = STRBUF_INIT; - struct multi_pack_index *m = get_local_multi_pack_index(the_repository); + struct odb_source *source = the_repository->objects->sources; + struct multi_pack_index *m = get_multi_pack_index(source); strbuf_addf(&buf, "%s.pack", base_name); - if (m && midx_contains_pack(m, buf.buf)) + if (m && source->local && midx_contains_pack(m, buf.buf)) clear_midx_file(the_repository); strbuf_insertf(&buf, 0, "%s/", dir_name); unlink_pack_path(buf.buf, 1); @@ -313,6 +320,8 @@ static void prepare_pack_objects(struct child_process *cmd, strvec_pushf(&cmd->args, "--no-reuse-object"); if (args->name_hash_version) strvec_pushf(&cmd->args, "--name-hash-version=%d", args->name_hash_version); + if (args->path_walk) + strvec_pushf(&cmd->args, "--path-walk"); if (args->local) strvec_push(&cmd->args, "--local"); if (args->quiet) @@ -687,6 +696,77 @@ static void free_pack_geometry(struct pack_geometry *geometry) free(geometry->pack); } +static int midx_has_unknown_packs(char **midx_pack_names, + size_t midx_pack_names_nr, + struct string_list *include, + struct pack_geometry *geometry, + struct existing_packs *existing) +{ + size_t i; + + string_list_sort(include); + + for (i = 0; i < midx_pack_names_nr; i++) { + const char *pack_name = midx_pack_names[i]; + + /* + * Determine whether or not each MIDX'd pack from the existing + * MIDX (if any) is represented in the new MIDX. For each pack + * in the MIDX, it must either be: + * + * - In the "include" list of packs to be included in the new + * MIDX. Note this function is called before the include + * list is populated with any cruft pack(s). + * + * - Below the geometric split line (if using pack geometry), + * indicating that the pack won't be included in the new + * MIDX, but its contents were rolled up as part of the + * geometric repack. + * + * - In the existing non-kept packs list (if not using pack + * geometry), and marked as non-deleted. + */ + if (string_list_has_string(include, pack_name)) { + continue; + } else if (geometry) { + struct strbuf buf = STRBUF_INIT; + uint32_t j; + + for (j = 0; j < geometry->split; j++) { + strbuf_reset(&buf); + strbuf_addstr(&buf, pack_basename(geometry->pack[j])); + strbuf_strip_suffix(&buf, ".pack"); + strbuf_addstr(&buf, ".idx"); + + if (!strcmp(pack_name, buf.buf)) { + strbuf_release(&buf); + break; + } + } + + strbuf_release(&buf); + + if (j < geometry->split) + continue; + } else { + struct string_list_item *item; + + item = string_list_lookup(&existing->non_kept_packs, + pack_name); + if (item && !pack_is_marked_for_deletion(item)) + continue; + } + + /* + * If we got to this point, the MIDX includes some pack that we + * don't know about. + */ + return 1; + } + + return 0; +} + struct midx_snapshot_ref_data { struct tempfile *f; struct oidset seen; @@ -707,7 +787,7 @@ static int midx_snapshot_ref_one(const char *refname UNUSED, if (oidset_insert(&data->seen, oid)) return 0; /* already seen */ - if (oid_object_info(the_repository, oid, NULL) != OBJ_COMMIT) + if (odb_read_object_info(the_repository->objects, oid, NULL) != OBJ_COMMIT) return 0; fprintf(data->f->fp, "%s%s\n", data->preferred ? "+" : "", @@ -755,6 +835,8 @@ static void midx_snapshot_refs(struct tempfile *f) static void midx_included_packs(struct string_list *include, struct existing_packs *existing, + char **midx_pack_names, + size_t midx_pack_names_nr, struct string_list *names, struct pack_geometry *geometry) { @@ -808,26 +890,56 @@ static void midx_included_packs(struct string_list *include, } } - for_each_string_list_item(item, &existing->cruft_packs) { + if (midx_must_contain_cruft || + midx_has_unknown_packs(midx_pack_names, midx_pack_names_nr, + include, geometry, existing)) { /* - * When doing a --geometric repack, there is no need to check - * for deleted packs, since we're by definition not doing an - * ALL_INTO_ONE repack (hence no packs will be deleted). - * Otherwise we must check for and exclude any packs which are - * enqueued for deletion. + * If there are one or more unknown pack(s) present (see + * midx_has_unknown_packs() for what makes a pack + * "unknown") in the MIDX before the repack, keep them + * as they may be required to form a reachability + * closure if the MIDX is bitmapped. * - * So we could omit the conditional below in the --geometric - * case, but doing so is unnecessary since no packs are marked - * as pending deletion (since we only call - * `mark_packs_for_deletion()` when doing an all-into-one - * repack). + * For example, a cruft pack can be required to form a + * reachability closure if the MIDX is bitmapped and one + * or more of the bitmap's selected commits reaches a + * once-cruft object that was later made reachable. */ - if (pack_is_marked_for_deletion(item)) - continue; + for_each_string_list_item(item, &existing->cruft_packs) { + /* + * When doing a --geometric repack, there is no + * need to check for deleted packs, since we're + * by definition not doing an ALL_INTO_ONE + * repack (hence no packs will be deleted). + * Otherwise we must check for and exclude any + * packs which are enqueued for deletion. + * + * So we could omit the conditional below in the + * --geometric case, but doing so is unnecessary + * since no packs are marked as pending + * deletion (since we only call + * `mark_packs_for_deletion()` when doing an + * all-into-one repack). + */ + if (pack_is_marked_for_deletion(item)) + continue; - strbuf_reset(&buf); - strbuf_addf(&buf, "%s.idx", item->string); - string_list_insert(include, buf.buf); + strbuf_reset(&buf); + strbuf_addf(&buf, "%s.idx", item->string); + string_list_insert(include, buf.buf); + } + } else { + /* + * Modern versions of Git (with the appropriate + * configuration setting) will write new copies of + * once-cruft objects when doing a --geometric repack. + * + * If the MIDX has no cruft pack, new packs written + * during a --geometric repack will not rely on the + * cruft pack to form a reachability closure, so we can + * avoid including them in the MIDX in that case. + */ + ; } strbuf_release(&buf); @@ -1142,6 +1254,8 @@ int cmd_repack(int argc, struct tempfile *refs_snapshot = NULL; int i, ext, ret; int show_progress; + char **midx_pack_names = NULL; + size_t midx_pack_names_nr = 0; /* variables to be filled by option parsing */ int delete_redundant = 0; @@ -1184,6 +1298,8 @@ int cmd_repack(int argc, N_("pass --no-reuse-object to git-pack-objects")), OPT_INTEGER(0, "name-hash-version", &po_args.name_hash_version, N_("specify the name hash version to use for grouping similar objects by path")), + OPT_BOOL(0, "path-walk", &po_args.path_walk, + N_("pass --path-walk to git-pack-objects")), OPT_NEGBIT('n', NULL, &run_update_server_info, N_("do not run git-update-server-info"), 1), OPT__QUIET(&po_args.quiet, N_("be quiet")), @@ -1225,7 +1341,7 @@ int cmd_repack(int argc, list_objects_filter_init(&po_args.filter_options); - git_config(repack_config, &cruft_po_args); + repo_config(the_repository, repack_config, &cruft_po_args); argc = parse_options(argc, argv, prefix, builtin_repack_options, git_repack_usage, 0); @@ -1235,7 +1351,7 @@ int cmd_repack(int argc, po_args.depth = xstrdup_or_null(opt_depth); po_args.threads = xstrdup_or_null(opt_threads); - if (delete_redundant && repository_format_precious_objects) + if (delete_redundant && the_repository->repository_format_precious_objects) die(_("cannot delete packs in a precious-objects repo")); die_for_incompatible_opt3(unpack_unreachable || (pack_everything & LOOSEN_UNREACHABLE), "-A", @@ -1256,7 +1372,8 @@ int cmd_repack(int argc, if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) && !write_midx) die(_(incremental_bitmap_conflict_error)); - if (write_bitmaps && po_args.local && has_alt_odb(the_repository)) { + if (write_bitmaps && po_args.local && + odb_has_alternates(the_repository->objects)) { /* * When asked to do a local repack, but we have * packfiles that are inherited from an alternate, then @@ -1356,7 +1473,10 @@ int cmd_repack(int argc, !(pack_everything & PACK_CRUFT)) strvec_push(&cmd.args, "--pack-loose-unreachable"); } else if (geometry.split_factor) { - strvec_push(&cmd.args, "--stdin-packs"); + if (midx_must_contain_cruft) + strvec_push(&cmd.args, "--stdin-packs"); + else + strvec_push(&cmd.args, "--stdin-packs=follow"); strvec_push(&cmd.args, "--unpacked"); } else { strvec_push(&cmd.args, "--unpacked"); @@ -1396,8 +1516,25 @@ int cmd_repack(int argc, if (ret) goto cleanup; - if (!names.nr && !po_args.quiet) - printf_ln(_("Nothing new to pack.")); + if (!names.nr) { + if (!po_args.quiet) + printf_ln(_("Nothing new to pack.")); + /* + * If we didn't write any new packs, the non-cruft packs + * may refer to once-unreachable objects in the cruft + * pack(s). + * + * If there isn't already a MIDX, the one we write + * must include the cruft pack(s), in case the + * non-cruft pack(s) refer to once-cruft objects. + * + * If there is already a MIDX, we can punt here, since + * midx_has_unknown_packs() will make the decision for + * us. + */ + if (!get_multi_pack_index(the_repository->objects->sources)) + midx_must_contain_cruft = 1; + } if (pack_everything & PACK_CRUFT) { const char *pack_prefix = find_pack_prefix(packdir, packtmp); @@ -1478,6 +1615,19 @@ int cmd_repack(int argc, string_list_sort(&names); + if (get_multi_pack_index(the_repository->objects->sources)) { + struct multi_pack_index *m = + get_multi_pack_index(the_repository->objects->sources); + + ALLOC_ARRAY(midx_pack_names, + m->num_packs + m->num_packs_in_base); + + for (; m; m = m->base_midx) + for (uint32_t i = 0; i < m->num_packs; i++) + midx_pack_names[midx_pack_names_nr++] = + xstrdup(m->pack_names[i]); + } + close_object_store(the_repository->objects); /* @@ -1519,7 +1669,8 @@ int cmd_repack(int argc, if (write_midx) { struct string_list include = STRING_LIST_INIT_DUP; - midx_included_packs(&include, &existing, &names, &geometry); + midx_included_packs(&include, &existing, midx_pack_names, + midx_pack_names_nr, &names, &geometry); ret = write_midx_included_packs(&include, &geometry, &names, refs_snapshot ? get_tempfile_path(refs_snapshot) : NULL, @@ -1561,7 +1712,7 @@ int cmd_repack(int argc, unsigned flags = 0; if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX_WRITE_INCREMENTAL, 0)) flags |= MIDX_WRITE_INCREMENTAL; - write_midx_file(the_repository, repo_get_object_directory(the_repository), + write_midx_file(the_repository->objects->sources, NULL, NULL, flags); } @@ -1570,6 +1721,9 @@ cleanup: string_list_clear(&names, 1); existing_packs_release(&existing); free_pack_geometry(&geometry); + for (size_t i = 0; i < midx_pack_names_nr; i++) + free(midx_pack_names[i]); + free(midx_pack_names); pack_objects_args_release(&po_args); pack_objects_args_release(&cruft_po_args); diff --git a/builtin/replace.c b/builtin/replace.c index 48c7c6a2d5..900b560a77 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -11,6 +11,7 @@ #include "builtin.h" #include "config.h" #include "editor.h" +#include "environment.h" #include "gettext.h" #include "hex.h" #include "refs.h" @@ -19,7 +20,7 @@ #include "run-command.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "replace-object.h" #include "tag.h" #include "wildmatch.h" @@ -65,8 +66,8 @@ static int show_reference(const char *refname, if (repo_get_oid(data->repo, refname, &object)) return error(_("failed to resolve '%s' as a valid ref"), refname); - obj_type = oid_object_info(data->repo, &object, NULL); - repl_type = oid_object_info(data->repo, oid, NULL); + obj_type = odb_read_object_info(data->repo->objects, &object, NULL); + repl_type = odb_read_object_info(data->repo->objects, oid, NULL); printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type), oid_to_hex(oid), type_name(repl_type)); @@ -185,8 +186,8 @@ static int replace_object_oid(const char *object_ref, struct strbuf err = STRBUF_INIT; int res = 0; - obj_type = oid_object_info(the_repository, object, NULL); - repl_type = oid_object_info(the_repository, repl, NULL); + obj_type = odb_read_object_info(the_repository->objects, object, NULL); + repl_type = odb_read_object_info(the_repository->objects, repl, NULL); if (!force && obj_type != repl_type) return error(_("Objects must be of the same type.\n" "'%s' points to a replaced object of type '%s'\n" @@ -334,7 +335,7 @@ static int edit_and_replace(const char *object_ref, int force, int raw) if (repo_get_oid(the_repository, object_ref, &old_oid) < 0) return error(_("not a valid object name: '%s'"), object_ref); - type = oid_object_info(the_repository, &old_oid, NULL); + type = odb_read_object_info(the_repository->objects, &old_oid, NULL); if (type < 0) return error(_("unable to get object type for %s"), oid_to_hex(&old_oid)); @@ -488,7 +489,8 @@ static int create_graft(int argc, const char **argv, int force, int gentle) return -1; } - if (write_object_file(buf.buf, buf.len, OBJ_COMMIT, &new_oid)) { + if (odb_write_object(the_repository->objects, buf.buf, + buf.len, OBJ_COMMIT, &new_oid)) { strbuf_release(&buf); return error(_("could not write replacement commit for: '%s'"), old_ref); @@ -574,7 +576,7 @@ int cmd_replace(int argc, }; disable_replace_refs(); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0); diff --git a/builtin/replay.c b/builtin/replay.c index 225cef0880..6172c8aacc 100644 --- a/builtin/replay.c +++ b/builtin/replay.c @@ -84,6 +84,7 @@ static struct commit *create_commit(struct repository *repo, obj = parse_object(repo, &ret); out: + repo_unuse_commit_buffer(the_repository, based_on, message); free_commit_extra_headers(extra); free_commit_list(parents); strbuf_release(&msg); diff --git a/builtin/repo.c b/builtin/repo.c new file mode 100644 index 0000000000..bbb0966f2d --- /dev/null +++ b/builtin/repo.c @@ -0,0 +1,171 @@ +#define USE_THE_REPOSITORY_VARIABLE + +#include "builtin.h" +#include "environment.h" +#include "parse-options.h" +#include "quote.h" +#include "refs.h" +#include "strbuf.h" +#include "shallow.h" + +static const char *const repo_usage[] = { + "git repo info [--format=(keyvalue|nul)] [-z] [<key>...]", + NULL +}; + +typedef int get_value_fn(struct repository *repo, struct strbuf *buf); + +enum output_format { + FORMAT_KEYVALUE, + FORMAT_NUL_TERMINATED, +}; + +struct field { + const char *key; + get_value_fn *get_value; +}; + +static int get_layout_bare(struct repository *repo UNUSED, struct strbuf *buf) +{ + strbuf_addstr(buf, is_bare_repository() ? "true" : "false"); + return 0; +} + +static int get_layout_shallow(struct repository *repo, struct strbuf *buf) +{ + strbuf_addstr(buf, + is_repository_shallow(repo) ? "true" : "false"); + return 0; +} + +static int get_object_format(struct repository *repo, struct strbuf *buf) +{ + strbuf_addstr(buf, repo->hash_algo->name); + return 0; +} + +static int get_references_format(struct repository *repo, struct strbuf *buf) +{ + strbuf_addstr(buf, + ref_storage_format_to_name(repo->ref_storage_format)); + return 0; +} + +/* repo_info_fields keys must be in lexicographical order */ +static const struct field repo_info_fields[] = { + { "layout.bare", get_layout_bare }, + { "layout.shallow", get_layout_shallow }, + { "object.format", get_object_format }, + { "references.format", get_references_format }, +}; + +static int repo_info_fields_cmp(const void *va, const void *vb) +{ + const struct field *a = va; + const struct field *b = vb; + + return strcmp(a->key, b->key); +} + +static get_value_fn *get_value_fn_for_key(const char *key) +{ + const struct field search_key = { key, NULL }; + const struct field *found = bsearch(&search_key, repo_info_fields, + ARRAY_SIZE(repo_info_fields), + sizeof(*found), + repo_info_fields_cmp); + return found ? found->get_value : NULL; +} + +static int print_fields(int argc, const char **argv, + struct repository *repo, + enum output_format format) +{ + int ret = 0; + struct strbuf valbuf = STRBUF_INIT; + struct strbuf quotbuf = STRBUF_INIT; + + for (int i = 0; i < argc; i++) { + get_value_fn *get_value; + const char *key = argv[i]; + + get_value = get_value_fn_for_key(key); + + if (!get_value) { + ret = error(_("key '%s' not found"), key); + continue; + } + + strbuf_reset(&valbuf); + strbuf_reset("buf); + + get_value(repo, &valbuf); + + switch (format) { + case FORMAT_KEYVALUE: + quote_c_style(valbuf.buf, "buf, NULL, 0); + printf("%s=%s\n", key, quotbuf.buf); + break; + case FORMAT_NUL_TERMINATED: + printf("%s\n%s%c", key, valbuf.buf, '\0'); + break; + default: + BUG("not a valid output format: %d", format); + } + } + + strbuf_release(&valbuf); + strbuf_release("buf); + return ret; +} + +static int parse_format_cb(const struct option *opt, + const char *arg, int unset UNUSED) +{ + enum output_format *format = opt->value; + + if (opt->short_name == 'z') + *format = FORMAT_NUL_TERMINATED; + else if (!strcmp(arg, "nul")) + *format = FORMAT_NUL_TERMINATED; + else if (!strcmp(arg, "keyvalue")) + *format = FORMAT_KEYVALUE; + else + die(_("invalid format '%s'"), arg); + + return 0; +} + +static int repo_info(int argc, const char **argv, const char *prefix, + struct repository *repo) +{ + enum output_format format = FORMAT_KEYVALUE; + struct option options[] = { + OPT_CALLBACK_F(0, "format", &format, N_("format"), + N_("output format"), + PARSE_OPT_NONEG, parse_format_cb), + OPT_CALLBACK_F('z', NULL, &format, NULL, + N_("synonym for --format=nul"), + PARSE_OPT_NONEG | PARSE_OPT_NOARG, + parse_format_cb), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, repo_usage, 0); + + return print_fields(argc, argv, repo, format); +} + +int cmd_repo(int argc, const char **argv, const char *prefix, + struct repository *repo) +{ + parse_opt_subcommand_fn *fn = NULL; + struct option options[] = { + OPT_SUBCOMMAND("info", &fn, repo_info), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, repo_usage, 0); + + return fn(argc, argv, prefix, repo); +} diff --git a/builtin/rerere.c b/builtin/rerere.c index 1312e79d89..a056cb791b 100644 --- a/builtin/rerere.c +++ b/builtin/rerere.c @@ -66,7 +66,7 @@ int cmd_rerere(int argc, argc = parse_options(argc, argv, prefix, options, rerere_usage, 0); - git_config(git_xmerge_config, NULL); + repo_config(the_repository, git_xmerge_config, NULL); if (autoupdate == 1) flags = RERERE_AUTOUPDATE; diff --git a/builtin/reset.c b/builtin/reset.c index dc50ffc1ac..ed35802af1 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -346,6 +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; const struct option options[] = { OPT__QUIET(&quiet, N_("be quiet, only report errors")), OPT_BOOL(0, "no-refresh", &no_refresh, @@ -370,6 +371,8 @@ 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_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.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), @@ -377,7 +380,7 @@ int cmd_reset(int argc, OPT_END() }; - git_config(git_reset_config, NULL); + repo_config(the_repository, git_reset_config, NULL); argc = parse_options(argc, argv, prefix, options, git_reset_usage, PARSE_OPT_KEEP_DASHDASH); @@ -420,6 +423,11 @@ int cmd_reset(int argc, oidcpy(&oid, &tree->object.oid); } + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; @@ -427,9 +435,14 @@ int cmd_reset(int argc, if (reset_type != NONE) 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, rev, - &pathspec); + update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, + &add_p_opt, rev, &pathspec); goto cleanup; + } else { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); } /* git reset tree [--] paths... can be used to diff --git a/builtin/rev-list.c b/builtin/rev-list.c index 0984b607bf..99f876ba85 100644 --- a/builtin/rev-list.c +++ b/builtin/rev-list.c @@ -14,7 +14,7 @@ #include "object.h" #include "object-name.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "pack-bitmap.h" #include "parse-options.h" #include "log-tree.h" @@ -28,6 +28,14 @@ #include "quote.h" #include "strbuf.h" +struct rev_list_info { + struct rev_info *revs; + int flags; + int show_timestamp; + int hdr_termination; + const char *header_prefix; +}; + static const char rev_list_usage[] = "git rev-list [<options>] <commit>... [--] [<path>...]\n" "\n" @@ -110,7 +118,8 @@ static off_t get_object_disk_usage(struct object *obj) off_t size; struct object_info oi = OBJECT_INFO_INIT; oi.disk_sizep = &size; - if (oid_object_info_extended(the_repository, &obj->oid, &oi, 0) < 0) + if (odb_read_object_info_extended(the_repository->objects, + &obj->oid, &oi, 0) < 0) die(_("unable to get disk usage of %s"), oid_to_hex(&obj->oid)); return size; } @@ -346,7 +355,8 @@ static void show_commit(struct commit *commit, void *data) static int finish_object(struct object *obj, const char *name, void *cb_data) { struct rev_list_info *info = cb_data; - if (oid_object_info_extended(the_repository, &obj->oid, NULL, 0) < 0) { + if (odb_read_object_info_extended(the_repository->objects, + &obj->oid, NULL, 0) < 0) { finish_object__ma(obj, name); return 1; } @@ -634,7 +644,7 @@ int cmd_rev_list(int argc, show_usage_if_asked(argc, argv, rev_list_usage); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); repo_init_revisions(the_repository, &revs, prefix); revs.abbrev = DEFAULT_ABBREV; revs.commit_format = CMIT_FMT_UNSPECIFIED; @@ -650,17 +660,21 @@ int cmd_rev_list(int argc, * * Let "--missing" to conditionally set fetch_if_missing. */ + /* - * NEEDSWORK: These loops that attempt to find presence of - * options without understanding that the options they are - * skipping are broken (e.g., it would not know "--grep + * NEEDSWORK: The next loop is utterly broken. It tries to + * notice an option is used, but without understanding if each + * option takes an argument, which fundamentally would not + * work. It would not know "--grep * --exclude-promisor-objects" is not triggering - * "--exclude-promisor-objects" option). We really need - * setup_revisions() to have a mechanism to allow and disallow - * some sets of options for different commands (like rev-list, - * replay, etc). Such a mechanism should do an early parsing - * of options and be able to manage the `--missing=...` and - * `--exclude-promisor-objects` options below. + * "--exclude-promisor-objects" option, for example. + * + * We really need setup_revisions() to have a mechanism to + * allow and disallow some sets of options for different + * commands (like rev-list, replay, etc). Such a mechanism + * should do an early parsing of options and be able to manage + * the `--missing=...` and `--exclude-promisor-objects` + * options below. */ for (i = 1; i < argc; i++) { const char *arg = argv[i]; diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index 490da33bec..9da92b990d 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -708,7 +708,6 @@ int cmd_rev_parse(int argc, struct object_id oid; unsigned int flags = 0; const char *name = NULL; - struct object_context unused; struct strbuf buf = STRBUF_INIT; int seen_end_of_options = 0; enum format_type format = FORMAT_DEFAULT; @@ -734,7 +733,7 @@ int cmd_rev_parse(int argc, /* No options; just report on whether we're in a git repo or not. */ if (argc == 1) { setup_git_directory(); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); return 0; } @@ -769,7 +768,7 @@ int cmd_rev_parse(int argc, /* The rest of the options require a git repository. */ if (!did_repo_setup) { prefix = setup_git_directory(); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); did_repo_setup = 1; prepare_repo_settings(the_repository); @@ -1141,9 +1140,8 @@ int cmd_rev_parse(int argc, name++; type = REVERSED; } - if (!get_oid_with_context(the_repository, name, - flags, &oid, &unused)) { - object_context_release(&unused); + if (!repo_get_oid_with_flags(the_repository, name, &oid, + flags)) { if (output_algo) repo_oid_to_algop(the_repository, &oid, output_algo, &oid); @@ -1153,7 +1151,6 @@ int cmd_rev_parse(int argc, show_rev(type, &oid, name); continue; } - object_context_release(&unused); if (verify) die_no_single_rev(quiet); if (has_dashdash) diff --git a/builtin/revert.c b/builtin/revert.c index e07c2217fe..bedc40f368 100644 --- a/builtin/revert.c +++ b/builtin/revert.c @@ -4,6 +4,7 @@ #include "builtin.h" #include "parse-options.h" #include "diff.h" +#include "environment.h" #include "gettext.h" #include "revision.h" #include "rerere.h" @@ -111,7 +112,7 @@ static int run_sequencer(int argc, const char **argv, const char *prefix, const char * const * usage_str = revert_or_cherry_pick_usage(opts); const char *me = action_name(opts); const char *cleanup_arg = NULL; - const char sentinel_value; + const char sentinel_value = 0; /* value not important */ const char *strategy = &sentinel_value; const char *gpg_sign = &sentinel_value; enum empty_action empty_opt = EMPTY_COMMIT_UNSPECIFIED; @@ -285,6 +286,9 @@ int cmd_revert(int argc, struct replay_opts opts = REPLAY_OPTS_INIT; int res; +#ifndef WITH_BREAKING_CHANGES + warn_on_auto_comment_char = true; +#endif /* !WITH_BREAKING_CHANGES */ opts.action = REPLAY_REVERT; sequencer_init_config(&opts); res = run_sequencer(argc, argv, prefix, &opts); @@ -302,6 +306,9 @@ struct repository *repo UNUSED) struct replay_opts opts = REPLAY_OPTS_INIT; int res; +#ifndef WITH_BREAKING_CHANGES + warn_on_auto_comment_char = true; +#endif /* !WITH_BREAKING_CHANGES */ opts.action = REPLAY_PICK; sequencer_init_config(&opts); res = run_sequencer(argc, argv, prefix, &opts); diff --git a/builtin/rm.c b/builtin/rm.c index a6565a69cf..05d89e98c3 100644 --- a/builtin/rm.c +++ b/builtin/rm.c @@ -9,6 +9,7 @@ #include "builtin.h" #include "advice.h" #include "config.h" +#include "environment.h" #include "lockfile.h" #include "dir.h" #include "gettext.h" @@ -271,7 +272,7 @@ int cmd_rm(int argc, struct pathspec pathspec; char *seen; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, builtin_rm_options, builtin_rm_usage, 0); diff --git a/builtin/send-pack.c b/builtin/send-pack.c index c6e0e9d051..8b81c8a848 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "config.h" +#include "environment.h" #include "hex.h" #include "pkt-line.h" #include "run-command.h" @@ -304,9 +305,10 @@ int cmd_send_pack(int argc, flags |= MATCH_REFS_MIRROR; /* match them up */ - if (match_push_refs(local_refs, &remote_refs, &rs, flags)) - return -1; - + if (match_push_refs(local_refs, &remote_refs, &rs, flags)) { + ret = -1; + goto cleanup; + } if (!is_empty_cas(&cas)) apply_push_cas(&cas, remote, remote_refs); @@ -339,10 +341,12 @@ int cmd_send_pack(int argc, /* stable plumbing output; do not modify or localize */ fprintf(stderr, "Everything up-to-date\n"); +cleanup: string_list_clear(&push_options, 0); free_refs(remote_refs); free_refs(local_refs); refspec_clear(&rs); + oid_array_clear(&extra_have); oid_array_clear(&shallow); clear_cas_option(&cas); return ret; diff --git a/builtin/shortlog.c b/builtin/shortlog.c index 30075b67be..b91acf45c8 100644 --- a/builtin/shortlog.c +++ b/builtin/shortlog.c @@ -187,7 +187,7 @@ static void insert_records_from_trailers(struct shortlog *log, ctx->output_encoding); body = strstr(commit_buffer, "\n\n"); if (!body) - return; + goto out; trailer_iterator_init(&iter, body); while (trailer_iterator_advance(&iter)) { @@ -206,6 +206,7 @@ static void insert_records_from_trailers(struct shortlog *log, } trailer_iterator_release(&iter); +out: strbuf_release(&ident); repo_unuse_commit_buffer(the_repository, commit, commit_buffer); } @@ -418,9 +419,9 @@ int cmd_shortlog(int argc, * git/nongit so that we do not have to do this. */ if (nongit && !the_hash_algo) - repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + repo_set_hash_algo(the_repository, GIT_HASH_DEFAULT); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); shortlog_init(&log); repo_init_revisions(the_repository, &rev, prefix); parse_options_start(&ctx, argc, argv, prefix, options, diff --git a/builtin/show-branch.c b/builtin/show-branch.c index 525b231d87..1ab7db9d2c 100644 --- a/builtin/show-branch.c +++ b/builtin/show-branch.c @@ -710,7 +710,7 @@ int cmd_show_branch(int ac, init_commit_name_slab(&name_slab); - git_config(git_show_branch_config, NULL); + repo_config(the_repository, git_show_branch_config, NULL); /* If nothing is specified, try the default first */ if (ac == 1 && default_args.nr) { diff --git a/builtin/show-index.c b/builtin/show-index.c index 9d4ecf5e7b..2c3e2940ce 100644 --- a/builtin/show-index.c +++ b/builtin/show-index.c @@ -47,7 +47,7 @@ int cmd_show_index(int argc, * the index file passed in and use that instead. */ if (!the_hash_algo) - repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + repo_set_hash_algo(the_repository, GIT_HASH_DEFAULT); hashsz = the_hash_algo->rawsz; diff --git a/builtin/show-ref.c b/builtin/show-ref.c index 623a52a45f..0b6f9edf86 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -1,11 +1,12 @@ #define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "hex.h" #include "refs/refs-internal.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "object.h" #include "string-list.h" #include "parse-options.h" @@ -35,8 +36,8 @@ static void show_one(const struct show_one_options *opts, const char *hex; struct object_id peeled; - if (!has_object(the_repository, oid, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) + if (!odb_has_object(the_repository->objects, oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) die("git show-ref: bad ref %s (%s)", refname, oid_to_hex(oid)); @@ -324,7 +325,7 @@ struct repository *repo UNUSED) OPT_END() }; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, show_ref_options, show_ref_usage, 0); diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c index 1bf01591b2..8c333b3e2e 100644 --- a/builtin/sparse-checkout.c +++ b/builtin/sparse-checkout.c @@ -1082,7 +1082,7 @@ int cmd_sparse_checkout(int argc, builtin_sparse_checkout_options, builtin_sparse_checkout_usage, 0); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; diff --git a/builtin/stash.c b/builtin/stash.c index cfbd92852a..b7db7c8364 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -28,7 +28,10 @@ #include "log-tree.h" #include "diffcore.h" #include "reflog.h" +#include "reflog-walk.h" #include "add-interactive.h" +#include "oid-array.h" +#include "commit.h" #define INCLUDE_ALL_FILES 2 @@ -56,6 +59,10 @@ " [-u | --include-untracked] [-a | --all] [<message>]") #define BUILTIN_STASH_CREATE_USAGE \ N_("git stash create [<message>]") +#define BUILTIN_STASH_EXPORT_USAGE \ + N_("git stash export (--print | --to-ref <ref>) [<stash>...]") +#define BUILTIN_STASH_IMPORT_USAGE \ + N_("git stash import <commit>") #define BUILTIN_STASH_CLEAR_USAGE \ "git stash clear" @@ -71,6 +78,8 @@ static const char * const git_stash_usage[] = { BUILTIN_STASH_CLEAR_USAGE, BUILTIN_STASH_CREATE_USAGE, BUILTIN_STASH_STORE_USAGE, + BUILTIN_STASH_EXPORT_USAGE, + BUILTIN_STASH_IMPORT_USAGE, NULL }; @@ -124,6 +133,16 @@ static const char * const git_stash_save_usage[] = { NULL }; +static const char * const git_stash_export_usage[] = { + BUILTIN_STASH_EXPORT_USAGE, + NULL +}; + +static const char * const git_stash_import_usage[] = { + BUILTIN_STASH_IMPORT_USAGE, + NULL +}; + static const char ref_stash[] = "refs/stash"; static struct strbuf stash_index_path = STRBUF_INIT; @@ -132,6 +151,7 @@ static struct strbuf stash_index_path = STRBUF_INIT; * b_commit is set to the base commit * i_commit is set to the commit containing the index tree * u_commit is set to the commit containing the untracked files tree + * c_commit is set to the first parent (chain commit) when importing and is otherwise unset * w_tree is set to the working tree * b_tree is set to the base tree * i_tree is set to the index tree @@ -142,6 +162,7 @@ struct stash_info { struct object_id b_commit; struct object_id i_commit; struct object_id u_commit; + struct object_id c_commit; struct object_id w_tree; struct object_id b_tree; struct object_id i_tree; @@ -160,6 +181,33 @@ static void free_stash_info(struct stash_info *info) strbuf_release(&info->revision); } +static int check_stash_topology(struct repository *r, struct commit *stash) +{ + struct commit *p1, *p2, *p3 = NULL; + + /* stash must have two or three parents */ + if (!stash->parents || !stash->parents->next || + (stash->parents->next->next && stash->parents->next->next->next)) + return -1; + p1 = stash->parents->item; + p2 = stash->parents->next->item; + if (stash->parents->next->next) + p3 = stash->parents->next->next->item; + if (repo_parse_commit(r, p1) || repo_parse_commit(r, p2) || + (p3 && repo_parse_commit(r, p3))) + return -1; + /* p2 must have a single parent, p3 must have no parents */ + if (!p2->parents || p2->parents->next || (p3 && p3->parents)) + return -1; + if (repo_parse_commit(r, p2->parents->item)) + return -1; + /* p2^1 must equal p1 */ + if (!oideq(&p1->object.oid, &p2->parents->item->object.oid)) + return -1; + + return 0; +} + static void assert_stash_like(struct stash_info *info, const char *revision) { if (get_oidf(&info->b_commit, "%s^1", revision) || @@ -169,6 +217,25 @@ static void assert_stash_like(struct stash_info *info, const char *revision) die(_("'%s' is not a stash-like commit"), revision); } +static int parse_stash_revision(struct strbuf *revision, const char *commit, int quiet) +{ + strbuf_reset(revision); + if (!commit) { + if (!refs_ref_exists(get_main_ref_store(the_repository), ref_stash)) { + if (!quiet) + fprintf_ln(stderr, _("No stash entries found.")); + return -1; + } + + strbuf_addf(revision, "%s@{0}", ref_stash); + } else if (strspn(commit, "0123456789") == strlen(commit)) { + strbuf_addf(revision, "%s@{%s}", ref_stash, commit); + } else { + strbuf_addstr(revision, commit); + } + return 0; +} + static int get_stash_info(struct stash_info *info, int argc, const char **argv) { int ret; @@ -196,17 +263,9 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv) if (argc == 1) commit = argv[0]; - if (!commit) { - if (!refs_ref_exists(get_main_ref_store(the_repository), ref_stash)) { - fprintf_ln(stderr, _("No stash entries found.")); - return -1; - } - - strbuf_addf(&info->revision, "%s@{0}", ref_stash); - } else if (strspn(commit, "0123456789") == strlen(commit)) { - strbuf_addf(&info->revision, "%s@{%s}", ref_stash, commit); - } else { - strbuf_addstr(&info->revision, commit); + strbuf_init(&info->revision, 0); + if (parse_stash_revision(&info->revision, commit, 0)) { + return -1; } revision = info->revision.buf; @@ -318,7 +377,7 @@ static int diff_tree_binary(struct strbuf *out, struct object_id *w_commit) * however it should be done together with apply_cached. */ cp.git_cmd = 1; - strvec_pushl(&cp.args, "diff-tree", "--binary", NULL); + strvec_pushl(&cp.args, "diff-tree", "--binary", "--no-color", NULL); strvec_pushf(&cp.args, "%s^2^..%s^2", w_commit_hex, w_commit_hex); return pipe_command(&cp, NULL, 0, out, 0, NULL, 0); @@ -679,7 +738,8 @@ cleanup: return ret; } -static int reject_reflog_ent(struct object_id *ooid UNUSED, +static int reject_reflog_ent(const char *refname UNUSED, + struct object_id *ooid UNUSED, struct object_id *noid UNUSED, const char *email UNUSED, timestamp_t timestamp UNUSED, @@ -920,7 +980,7 @@ static int show_stash(int argc, const char **argv, const char *prefix, int do_usage = 0; init_diff_ui_defaults(); - git_config(git_diff_ui_config, NULL); + repo_config(the_repository, git_diff_ui_config, NULL); repo_init_revisions(the_repository, &rev, prefix); argc = parse_options(argc, argv, prefix, options, git_stash_show_usage, @@ -1029,7 +1089,6 @@ static int store_stash(int argc, const char **argv, const char *prefix, int quiet = 0; const char *stash_msg = NULL; struct object_id obj; - struct object_context dummy = {0}; struct option options[] = { OPT__QUIET(&quiet, N_("be quiet")), OPT_STRING('m', "message", &stash_msg, "message", @@ -1049,9 +1108,8 @@ static int store_stash(int argc, const char **argv, const char *prefix, return -1; } - if (get_oid_with_context(the_repository, - argv[0], quiet ? GET_OID_QUIETLY : 0, &obj, - &dummy)) { + if (repo_get_oid_with_flags(the_repository, argv[0], &obj, + quiet ? GET_OID_QUIETLY : 0)) { if (!quiet) fprintf_ln(stderr, _("Cannot update %s with %s"), ref_stash, argv[0]); @@ -1062,7 +1120,6 @@ static int store_stash(int argc, const char **argv, const char *prefix, ret = do_store_stash(&obj, stash_msg, quiet); out: - object_context_release(&dummy); return ret; } @@ -1224,6 +1281,7 @@ static int stash_staged(struct stash_info *info, struct strbuf *out_patch, cp_diff_tree.git_cmd = 1; strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "--binary", + "--no-color", "-U1", "HEAD", oid_to_hex(&info->w_tree), "--", NULL); if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) { ret = -1; @@ -1242,7 +1300,8 @@ done: } static int stash_patch(struct stash_info *info, const struct pathspec *ps, - struct strbuf *out_patch, int quiet) + struct strbuf *out_patch, int quiet, + struct add_p_opt *add_p_opt) { int ret = 0; struct child_process cp_read_tree = CHILD_PROCESS_INIT; @@ -1267,7 +1326,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, NULL, ps); + ret = !!run_add_p(the_repository, ADD_P_STASH, add_p_opt, NULL, ps); the_repository->index_file = old_repo_index_file; if (old_index_env && *old_index_env) @@ -1285,6 +1344,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps, cp_diff_tree.git_cmd = 1; strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "-U1", "HEAD", + "--no-color", oid_to_hex(&info->w_tree), "--", NULL); if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) { ret = -1; @@ -1362,8 +1422,8 @@ done: } static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf, - int include_untracked, int patch_mode, int only_staged, - struct stash_info *info, struct strbuf *patch, + int include_untracked, int patch_mode, struct add_p_opt *add_p_opt, + int only_staged, struct stash_info *info, struct strbuf *patch, int quiet) { int ret = 0; @@ -1372,6 +1432,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b const char *head_short_sha1 = NULL; const char *branch_ref = NULL; const char *branch_name = "(no branch)"; + char *branch_name_buf = NULL; struct commit *head_commit = NULL; struct commit_list *parents = NULL; struct strbuf msg = STRBUF_INIT; @@ -1404,8 +1465,12 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b branch_ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), "HEAD", 0, NULL, &flags); - if (flags & REF_ISSYMREF) - skip_prefix(branch_ref, "refs/heads/", &branch_name); + + if (flags & REF_ISSYMREF) { + if (skip_prefix(branch_ref, "refs/heads/", &branch_name)) + branch_name = branch_name_buf = xstrdup(branch_name); + } + head_short_sha1 = repo_find_unique_abbrev(the_repository, &head_commit->object.oid, DEFAULT_ABBREV); @@ -1439,7 +1504,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); + ret = stash_patch(info, ps, patch, quiet, add_p_opt); if (ret < 0) { if (!quiet) fprintf_ln(stderr, _("Cannot save the current " @@ -1495,6 +1560,7 @@ done: strbuf_release(&msg); strbuf_release(&untracked_files); free_commit_list(parents); + free(branch_name_buf); return ret; } @@ -1513,7 +1579,7 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED, if (!check_changes_tracked_files(&ps)) return 0; - ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, 0, &info, + ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, NULL, 0, &info, NULL, 0); if (!ret) printf_ln("%s", oid_to_hex(&info.w_commit)); @@ -1524,7 +1590,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, int include_untracked, int only_staged) + int keep_index, int patch_mode, struct add_p_opt *add_p_opt, + int include_untracked, int only_staged) { int ret = 0; struct stash_info info = STASH_INFO_INIT; @@ -1594,8 +1661,8 @@ 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, only_staged, - &info, &patch, quiet)) { + if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, + add_p_opt, only_staged, &info, &patch, quiet)) { ret = -1; goto done; } @@ -1652,6 +1719,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q cp_diff.git_cmd = 1; strvec_pushl(&cp_diff.args, "diff-index", "-p", + "--no-color", "--cached", "--binary", "HEAD", "--", NULL); add_pathspecs(&cp_diff.args, ps); @@ -1768,6 +1836,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 option options[] = { OPT_BOOL('k', "keep-index", &keep_index, N_("keep index")), @@ -1775,6 +1844,8 @@ 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_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT__QUIET(&quiet, N_("quiet mode")), OPT_BOOL('u', "include-untracked", &include_untracked, N_("include untracked files in stash")), @@ -1789,11 +1860,15 @@ static int push_stash(int argc, const char **argv, const char *prefix, int ret; if (argc) { - force_assume = !strcmp(argv[0], "-p"); + int flags = PARSE_OPT_KEEP_DASHDASH; + + if (push_assumed) + flags |= PARSE_OPT_STOP_AT_NON_OPTION; + argc = parse_options(argc, argv, prefix, options, push_assumed ? git_stash_usage : - git_stash_push_usage, - PARSE_OPT_KEEP_DASHDASH); + git_stash_push_usage, flags); + force_assume |= patch_mode; } if (argc) { @@ -1826,8 +1901,20 @@ static int push_stash(int argc, const char **argv, const char *prefix, die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file"); } + if (!patch_mode) { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } + + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode, - include_untracked, only_staged); + &add_p_opt, include_untracked, only_staged); clear_pathspec(&ps); free(pathspec_from_file); @@ -1852,6 +1939,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 option options[] = { OPT_BOOL('k', "keep-index", &keep_index, N_("keep index")), @@ -1859,6 +1947,8 @@ 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_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT__QUIET(&quiet, N_("quiet mode")), OPT_BOOL('u', "include-untracked", &include_untracked, N_("include untracked files in stash")), @@ -1877,13 +1967,404 @@ static int save_stash(int argc, const char **argv, const char *prefix, stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' '); memset(&ps, 0, sizeof(ps)); + + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + + if (!patch_mode) { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } + ret = do_push_stash(&ps, stash_msg, quiet, keep_index, - patch_mode, include_untracked, only_staged); + patch_mode, &add_p_opt, include_untracked, + only_staged); strbuf_release(&stash_msg_buf); return ret; } +static int write_commit_with_parents(struct repository *r, + struct object_id *out, + const struct object_id *oid, + struct commit_list *parents) +{ + size_t author_len, committer_len; + struct commit *this; + const char *orig_author, *orig_committer; + char *author = NULL, *committer = NULL; + const char *buffer; + unsigned long bufsize; + const char *p; + struct strbuf msg = STRBUF_INIT; + int ret = 0; + struct ident_split id; + + this = lookup_commit_reference(r, oid); + buffer = repo_get_commit_buffer(r, this, &bufsize); + orig_author = find_commit_header(buffer, "author", &author_len); + orig_committer = find_commit_header(buffer, "committer", &committer_len); + + if (!orig_author || !orig_committer) { + ret = error(_("cannot parse commit %s"), oid_to_hex(oid)); + goto out; + } + + if (split_ident_line(&id, orig_author, author_len) < 0 || + split_ident_line(&id, orig_committer, committer_len) < 0) { + ret = error(_("invalid author or committer for %s"), oid_to_hex(oid)); + goto out; + } + + p = strstr(buffer, "\n\n"); + strbuf_addstr(&msg, "git stash: "); + + if (p) + strbuf_add(&msg, p + 2, bufsize - (p + 2 - buffer)); + strbuf_complete_line(&msg); + + author = xmemdupz(orig_author, author_len); + committer = xmemdupz(orig_committer, committer_len); + + if (commit_tree_extended(msg.buf, msg.len, + r->hash_algo->empty_tree, parents, + out, author, committer, + NULL, NULL)) { + ret = error(_("could not write commit")); + goto out; + } +out: + strbuf_release(&msg); + repo_unuse_commit_buffer(r, this, buffer); + free(author); + free(committer); + return ret; +} + +static int do_import_stash(struct repository *r, const char *rev) +{ + struct object_id chain; + int res = 0; + const char *buffer = NULL; + unsigned long bufsize; + struct commit *this = NULL; + struct commit_list *items = NULL, *cur; + char *msg = NULL; + + if (repo_get_oid(r, rev, &chain)) + return error(_("not a valid revision: %s"), rev); + + this = lookup_commit_reference(r, &chain); + if (!this) + return error(_("not a commit: %s"), rev); + + /* + * Walk the commit history, finding each stash entry, and load data into + * the array. + */ + for (;;) { + const char *author, *committer; + size_t author_len, committer_len; + const char *p; + const char *expected = "git stash <git@stash> 1000684800 +0000"; + const char *prefix = "git stash: "; + struct commit *stash; + struct tree *tree = repo_get_commit_tree(r, this); + + if (!tree || + !oideq(&tree->object.oid, r->hash_algo->empty_tree) || + (this->parents && + (!this->parents->next || this->parents->next->next))) { + res = error(_("%s is not a valid exported stash commit"), + oid_to_hex(&this->object.oid)); + goto out; + } + + buffer = repo_get_commit_buffer(r, this, &bufsize); + + if (!this->parents) { + /* + * We don't have any parents. Make sure this is our + * root commit. + */ + author = find_commit_header(buffer, "author", &author_len); + committer = find_commit_header(buffer, "committer", &committer_len); + + if (!author || !committer) { + error(_("cannot parse commit %s"), oid_to_hex(&this->object.oid)); + goto out; + } + + if (author_len != strlen(expected) || + committer_len != strlen(expected) || + memcmp(author, expected, author_len) || + memcmp(committer, expected, committer_len)) { + res = error(_("found root commit %s with invalid data"), oid_to_hex(&this->object.oid)); + goto out; + } + break; + } + + p = strstr(buffer, "\n\n"); + if (!p) { + res = error(_("cannot parse commit %s"), oid_to_hex(&this->object.oid)); + goto out; + } + + p += 2; + if (((size_t)(bufsize - (p - buffer)) < strlen(prefix)) || + memcmp(prefix, p, strlen(prefix))) { + res = error(_("found stash commit %s without expected prefix"), oid_to_hex(&this->object.oid)); + goto out; + } + + stash = this->parents->next->item; + + if (repo_parse_commit(r, this->parents->item) || + repo_parse_commit(r, stash)) { + res = error(_("cannot parse parents of commit: %s"), + oid_to_hex(&this->object.oid)); + goto out; + } + + if (check_stash_topology(r, stash)) { + res = error(_("%s does not look like a stash commit"), + oid_to_hex(&stash->object.oid)); + goto out; + } + + repo_unuse_commit_buffer(r, this, buffer); + buffer = NULL; + items = commit_list_insert(stash, &items); + this = this->parents->item; + } + + /* + * Now, walk each entry, adding it to the stash as a normal stash + * commit. + */ + for (cur = items; cur; cur = cur->next) { + const char *p; + struct object_id *oid; + + this = cur->item; + oid = &this->object.oid; + buffer = repo_get_commit_buffer(r, this, &bufsize); + if (!buffer) { + res = error(_("cannot read commit buffer for %s"), oid_to_hex(oid)); + goto out; + } + + p = strstr(buffer, "\n\n"); + if (!p) { + res = error(_("cannot parse commit %s"), oid_to_hex(oid)); + goto out; + } + + p += 2; + msg = xmemdupz(p, bufsize - (p - buffer)); + repo_unuse_commit_buffer(r, this, buffer); + buffer = NULL; + + if (do_store_stash(oid, msg, 1)) { + res = error(_("cannot save the stash for %s"), oid_to_hex(oid)); + goto out; + } + FREE_AND_NULL(msg); + } +out: + if (this && buffer) + repo_unuse_commit_buffer(r, this, buffer); + free_commit_list(items); + free(msg); + + return res; +} + +static int import_stash(int argc, const char **argv, const char *prefix, + struct repository *repo) +{ + struct option options[] = { + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, + git_stash_import_usage, + PARSE_OPT_KEEP_DASHDASH); + + if (argc != 1) + usage_msg_opt("a revision is required", git_stash_import_usage, options); + + return do_import_stash(repo, argv[0]); +} + +struct stash_entry_data { + struct repository *r; + struct commit_list **items; + size_t count; +}; + +static int collect_stash_entries(const char *refname UNUSED, + struct object_id *old_oid UNUSED, + struct object_id *new_oid, + const char *committer UNUSED, + timestamp_t timestamp UNUSED, + int tz UNUSED, const char *msg UNUSED, + void *cb_data) +{ + struct stash_entry_data *data = cb_data; + struct commit *stash; + + data->count++; + stash = lookup_commit_reference(data->r, new_oid); + if (!stash || check_stash_topology(data->r, stash)) { + return error(_("%s does not look like a stash commit"), + oid_to_hex(new_oid)); + } + data->items = commit_list_append(stash, data->items); + return 0; +} + +static int do_export_stash(struct repository *r, + const char *ref, + int argc, + const char **argv) +{ + struct object_id base; + struct commit *prev; + struct commit_list *items = NULL, **iter = &items, *cur; + int res = 0; + int i; + struct strbuf revision = STRBUF_INIT; + const char *author, *committer; + + /* + * This is an arbitrary, fixed date, specifically the one used by git + * format-patch. The goal is merely to produce reproducible output. + */ + prepare_fallback_ident("git stash", "git@stash"); + author = fmt_ident("git stash", "git@stash", WANT_BLANK_IDENT, + "2001-09-17T00:00:00Z", 0); + committer = fmt_ident("git stash", "git@stash", WANT_BLANK_IDENT, + "2001-09-17T00:00:00Z", 0); + + /* First, we create a single empty commit. */ + if (commit_tree_extended("", 0, r->hash_algo->empty_tree, NULL, + &base, author, committer, NULL, NULL)) + return error(_("unable to write base commit")); + + prev = lookup_commit_reference(r, &base); + + if (argc) { + /* + * Find each specified stash, and load data into the array. + */ + for (i = 0; i < argc; i++) { + struct object_id oid; + struct commit *stash; + + if (parse_stash_revision(&revision, argv[i], 1) || + repo_get_oid_with_flags(r, revision.buf, &oid, + GET_OID_QUIETLY | + GET_OID_GENTLY)) { + res = error(_("unable to find stash entry %s"), argv[i]); + goto out; + } + + stash = lookup_commit_reference(r, &oid); + if (!stash || check_stash_topology(r, stash)) { + res = error(_("%s does not look like a stash commit"), + revision.buf); + goto out; + } + iter = commit_list_append(stash, iter); + } + } else { + /* + * Walk the reflog, finding each stash entry, and load data into the + * array. + */ + struct stash_entry_data cb_data = { + .r = r, .items = iter, + }; + if (refs_for_each_reflog_ent_reverse(get_main_ref_store(r), + "refs/stash", + collect_stash_entries, + &cb_data) && cb_data.count) + goto out; + } + + /* + * Now, create a set of commits identical to the regular stash commits, + * but where their first parents form a chain to our original empty + * base commit. + */ + items = reverse_commit_list(items); + for (cur = items; cur; cur = cur->next) { + struct commit_list *parents = NULL; + struct commit_list **next = &parents; + struct object_id out; + struct commit *stash = cur->item; + + next = commit_list_append(prev, next); + next = commit_list_append(stash, next); + res = write_commit_with_parents(r, &out, &stash->object.oid, parents); + free_commit_list(parents); + if (res) + goto out; + prev = lookup_commit_reference(r, &out); + } + if (ref) + refs_update_ref(get_main_ref_store(r), NULL, ref, + &prev->object.oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR); + else + puts(oid_to_hex(&prev->object.oid)); +out: + strbuf_release(&revision); + free_commit_list(items); + + return res; +} + +enum export_action { + ACTION_NONE, + ACTION_PRINT, + ACTION_TO_REF, +}; + +static int export_stash(int argc, + const char **argv, + const char *prefix, + struct repository *repo) +{ + const char *ref = NULL; + enum export_action action = ACTION_NONE; + struct option options[] = { + OPT_CMDMODE(0, "print", &action, + N_("print the object ID instead of writing it to a ref"), + ACTION_PRINT), + OPT_STRING(0, "to-ref", &ref, "ref", + N_("save the data to the given ref")), + OPT_END() + }; + + argc = parse_options(argc, argv, prefix, options, + git_stash_export_usage, + PARSE_OPT_KEEP_DASHDASH); + + if (ref && action == ACTION_NONE) + action = ACTION_TO_REF; + + if (action == ACTION_NONE || (ref && action == ACTION_PRINT)) + return error(_("exactly one of --print and --to-ref is required")); + + return do_export_stash(repo, ref, argc, argv); +} + int cmd_stash(int argc, const char **argv, const char *prefix, @@ -1904,13 +2385,15 @@ int cmd_stash(int argc, OPT_SUBCOMMAND("store", &fn, store_stash), OPT_SUBCOMMAND("create", &fn, create_stash), OPT_SUBCOMMAND("push", &fn, push_stash_unassumed), + OPT_SUBCOMMAND("export", &fn, export_stash), + OPT_SUBCOMMAND("import", &fn, import_stash), OPT_SUBCOMMAND_F("save", &fn, save_stash, PARSE_OPT_NOCOMPLETE), OPT_END() }; const char **args_copy; int ret; - git_config(git_stash_config, NULL); + repo_config(the_repository, git_stash_config, NULL); argc = parse_options(argc, argv, prefix, options, git_stash_usage, PARSE_OPT_SUBCOMMAND_OPTIONAL | diff --git a/builtin/stripspace.c b/builtin/stripspace.c index e147f3ff92..4a566cbc5d 100644 --- a/builtin/stripspace.c +++ b/builtin/stripspace.c @@ -55,7 +55,7 @@ int cmd_stripspace(int argc, if (mode == STRIP_COMMENTS || mode == COMMENT_LINES) { setup_git_directory_gently(&nongit); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); } if (strbuf_read(&buf, 0, 1024) < 0) diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 53da2116dd..07a1935cbe 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -28,10 +28,12 @@ #include "diff.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "advice.h" #include "branch.h" #include "list-objects-filter-options.h" +#include "wildmatch.h" +#include "strbuf.h" #define OPT_QUIET (1 << 0) #define OPT_CACHED (1 << 1) @@ -41,61 +43,9 @@ typedef void (*each_submodule_fn)(const struct cache_entry *list_item, void *cb_data); -static int repo_get_default_remote(struct repository *repo, char **default_remote) -{ - char *dest = NULL; - struct strbuf sb = STRBUF_INIT; - struct ref_store *store = get_main_ref_store(repo); - const char *refname = refs_resolve_ref_unsafe(store, "HEAD", 0, NULL, - NULL); - - if (!refname) - return die_message(_("No such ref: %s"), "HEAD"); - - /* detached HEAD */ - if (!strcmp(refname, "HEAD")) { - *default_remote = xstrdup("origin"); - return 0; - } - - if (!skip_prefix(refname, "refs/heads/", &refname)) - return die_message(_("Expecting a full ref name, got %s"), - refname); - - strbuf_addf(&sb, "branch.%s.remote", refname); - if (repo_config_get_string(repo, sb.buf, &dest)) - *default_remote = xstrdup("origin"); - else - *default_remote = dest; - - strbuf_release(&sb); - return 0; -} - -static int get_default_remote_submodule(const char *module_path, char **default_remote) -{ - struct repository subrepo; - int ret; - - if (repo_submodule_init(&subrepo, the_repository, module_path, - null_oid(the_hash_algo)) < 0) - return die_message(_("could not get a repository handle for submodule '%s'"), - module_path); - ret = repo_get_default_remote(&subrepo, default_remote); - repo_clear(&subrepo); - - return ret; -} - static char *get_default_remote(void) { - char *default_remote; - int code = repo_get_default_remote(the_repository, &default_remote); - - if (code) - exit(code); - - return default_remote; + return xstrdup(repo_default_remote(the_repository)); } static char *resolve_relative_url(const char *rel_url, const char *up_path, int quiet) @@ -105,7 +55,7 @@ static char *resolve_relative_url(const char *rel_url, const char *up_path, int struct strbuf remotesb = STRBUF_INIT; strbuf_addf(&remotesb, "remote.%s.url", remote); - if (git_config_get_string(remotesb.buf, &remoteurl)) { + if (repo_config_get_string(the_repository, remotesb.buf, &remoteurl)) { if (!quiet) warning(_("could not look up configuration '%s'. " "Assuming this repository is its own " @@ -122,6 +72,46 @@ static char *resolve_relative_url(const char *rel_url, const char *up_path, int return resolved_url; } +static int get_default_remote_submodule(const char *module_path, char **default_remote) +{ + const struct submodule *sub; + struct repository subrepo; + const char *remote_name = NULL; + char *url = NULL; + + sub = submodule_from_path(the_repository, null_oid(the_hash_algo), module_path); + if (sub && sub->url) { + url = xstrdup(sub->url); + + /* Possibly a url relative to parent */ + if (starts_with_dot_dot_slash(url) || + starts_with_dot_slash(url)) { + char *oldurl = url; + + url = resolve_relative_url(oldurl, NULL, 1); + free(oldurl); + } + } + + if (repo_submodule_init(&subrepo, the_repository, module_path, + null_oid(the_hash_algo)) < 0) + return die_message(_("could not get a repository handle for submodule '%s'"), + module_path); + + /* Look up by URL first */ + if (url) + remote_name = repo_remote_from_url(&subrepo, url); + if (!remote_name) + remote_name = repo_default_remote(&subrepo); + + *default_remote = xstrdup(remote_name); + + repo_clear(&subrepo); + free(url); + + 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) @@ -303,7 +293,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item, char *displaypath; if (validate_submodule_path(path) < 0) - exit(128); + die(NULL); displaypath = get_submodule_displaypath(path, info->prefix, info->super_prefix); @@ -438,18 +428,6 @@ cleanup: return ret; } -static int starts_with_dot_slash(const char *const path) -{ - return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_SLASH | - PATH_MATCH_XPLATFORM); -} - -static int starts_with_dot_dot_slash(const char *const path) -{ - return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH | - PATH_MATCH_XPLATFORM); -} - struct init_cb { const char *prefix; const char *super_prefix; @@ -482,7 +460,7 @@ static void init_submodule(const char *path, const char *prefix, */ if (!is_submodule_active(the_repository, path)) { strbuf_addf(&sb, "submodule.%s.active", sub->name); - git_config_set_gently(sb.buf, "true"); + repo_config_set_gently(the_repository, sb.buf, "true"); strbuf_reset(&sb); } @@ -492,7 +470,7 @@ static void init_submodule(const char *path, const char *prefix, * .gitmodules, so look it up directly. */ strbuf_addf(&sb, "submodule.%s.url", sub->name); - if (git_config_get_string(sb.buf, &url)) { + if (repo_config_get_string(the_repository, sb.buf, &url)) { if (!sub->url) die(_("No url found for submodule path '%s' in .gitmodules"), displaypath); @@ -508,7 +486,7 @@ static void init_submodule(const char *path, const char *prefix, free(oldurl); } - if (git_config_set_gently(sb.buf, url)) + if (repo_config_set_gently(the_repository, sb.buf, url)) die(_("Failed to register url for submodule path '%s'"), displaypath); if (!(flags & OPT_QUIET)) @@ -520,7 +498,7 @@ static void init_submodule(const char *path, const char *prefix, /* Copy "update" setting when it is not set yet */ strbuf_addf(&sb, "submodule.%s.update", sub->name); - if (git_config_get_string_tmp(sb.buf, &upd) && + if (repo_config_get_string_tmp(the_repository, sb.buf, &upd) && sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) { if (sub->update_strategy.type == SM_UPDATE_COMMAND) { fprintf(stderr, _("warning: command update mode suggested for submodule '%s'\n"), @@ -530,7 +508,7 @@ static void init_submodule(const char *path, const char *prefix, upd = submodule_update_type_to_string(sub->update_strategy.type); } - if (git_config_set_gently(sb.buf, upd)) + if (repo_config_set_gently(the_repository, sb.buf, upd)) die(_("Failed to register update mode for submodule path '%s'"), displaypath); } strbuf_release(&sb); @@ -573,7 +551,7 @@ static int module_init(int argc, const char **argv, const char *prefix, * If there are no path args and submodule.active is set then, * by default, only initialize 'active' modules. */ - if (!argc && !git_config_get("submodule.active")) + if (!argc && !repo_config_get(the_repository, "submodule.active")) module_list_active(&list); info.prefix = prefix; @@ -643,7 +621,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, }; if (validate_submodule_path(path) < 0) - exit(128); + die(NULL); if (!submodule_from_path(the_repository, null_oid(the_hash_algo), path)) die(_("no submodule mapping found in .gitmodules for path '%s'"), @@ -673,7 +651,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid, "--ignore-submodules=dirty", "--quiet", "--", path, NULL); - git_config(git_diff_basic_config, NULL); + repo_config(the_repository, git_diff_basic_config, NULL); repo_init_revisions(the_repository, &rev, NULL); rev.abbrev = 0; @@ -1058,7 +1036,7 @@ static void prepare_submodule_summary(struct summary_cb *info, config_key = xstrfmt("submodule.%s.ignore", sub->name); - if (!git_config_get_string_tmp(config_key, &value)) + if (!repo_config_get_string_tmp(the_repository, config_key, &value)) ignore_all = !strcmp(value, "all"); else if (sub->ignore) ignore_all = !strcmp(sub->ignore, "all"); @@ -1132,7 +1110,7 @@ static int compute_summary_module_list(struct object_id *head_oid, if (info->argc) strvec_pushv(&diff_args, info->argv); - git_config(git_diff_basic_config, NULL); + repo_config(the_repository, git_diff_basic_config, NULL); repo_init_revisions(the_repository, &rev, info->prefix); rev.abbrev = 0; precompose_argv_prefix(diff_args.nr, diff_args.v, NULL); @@ -1257,7 +1235,7 @@ static void sync_submodule(const char *path, const char *prefix, return; if (validate_submodule_path(path) < 0) - exit(128); + die(NULL); sub = submodule_from_path(the_repository, null_oid(the_hash_algo), path); @@ -1286,7 +1264,7 @@ static void sync_submodule(const char *path, const char *prefix, strbuf_reset(&sb); strbuf_addf(&sb, "submodule.%s.url", sub->name); - if (git_config_set_gently(sb.buf, super_config_url)) + if (repo_config_set_gently(the_repository, sb.buf, super_config_url)) die(_("failed to register url for submodule path '%s'"), displaypath); @@ -1304,7 +1282,7 @@ static void sync_submodule(const char *path, const char *prefix, submodule_to_gitdir(the_repository, &sb, path); strbuf_addstr(&sb, "/config"); - if (git_config_set_in_file_gently(sb.buf, remote_key, NULL, sub_origin_url)) + if (repo_config_set_in_file_gently(the_repository, sb.buf, remote_key, NULL, sub_origin_url)) die(_("failed to update remote for submodule '%s'"), path); @@ -1402,7 +1380,7 @@ static void deinit_submodule(const char *path, const char *prefix, char *sub_git_dir = xstrfmt("%s/.git", path); if (validate_submodule_path(path) < 0) - exit(128); + die(NULL); sub = submodule_from_path(the_repository, null_oid(the_hash_algo), path); @@ -1582,7 +1560,7 @@ static const char alternate_error_advice[] = N_( ); static int add_possible_reference_from_superproject( - struct object_directory *odb, void *sas_cb) + struct odb_source *alt_odb, void *sas_cb) { struct submodule_alternate_setup *sas = sas_cb; size_t len; @@ -1591,12 +1569,12 @@ static int add_possible_reference_from_superproject( * If the alternate object store is another repository, try the * standard layout with .git/(modules/<name>)+/objects */ - if (strip_suffix(odb->path, "/objects", &len)) { + if (strip_suffix(alt_odb->path, "/objects", &len)) { struct repository alternate; char *sm_alternate; struct strbuf sb = STRBUF_INIT; struct strbuf err = STRBUF_INIT; - strbuf_add(&sb, odb->path, len); + strbuf_add(&sb, alt_odb->path, len); if (repo_init(&alternate, sb.buf, NULL) < 0) die(_("could not get a repository handle for gitdir '%s'"), @@ -1647,11 +1625,11 @@ static void prepare_possible_alternates(const char *sm_name, char *sm_alternate = NULL, *error_strategy = NULL; struct submodule_alternate_setup sas = SUBMODULE_ALTERNATE_SETUP_INIT; - git_config_get_string("submodule.alternateLocation", &sm_alternate); + repo_config_get_string(the_repository, "submodule.alternateLocation", &sm_alternate); if (!sm_alternate) return; - git_config_get_string("submodule.alternateErrorStrategy", &error_strategy); + repo_config_get_string(the_repository, "submodule.alternateErrorStrategy", &error_strategy); if (!error_strategy) error_strategy = xstrdup("die"); @@ -1668,7 +1646,8 @@ static void prepare_possible_alternates(const char *sm_name, die(_("Value '%s' for submodule.alternateErrorStrategy is not recognized"), error_strategy); if (!strcmp(sm_alternate, "superproject")) - foreach_alt_odb(add_possible_reference_from_superproject, &sas); + odb_for_each_alternate(the_repository->objects, + add_possible_reference_from_superproject, &sas); else if (!strcmp(sm_alternate, "no")) ; /* do nothing */ else @@ -1724,7 +1703,7 @@ static int clone_submodule(const struct module_clone_data *clone_data, char *to_free = NULL; if (validate_submodule_path(clone_data_path) < 0) - exit(128); + die(NULL); if (!is_absolute_path(clone_data->path)) clone_data_path = to_free = xstrfmt("%s/%s", repo_get_work_tree(the_repository), @@ -1831,14 +1810,14 @@ static int clone_submodule(const struct module_clone_data *clone_data, die(_("could not get submodule directory for '%s'"), clone_data_path); /* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */ - git_config_get_string("submodule.alternateLocation", &sm_alternate); + repo_config_get_string(the_repository, "submodule.alternateLocation", &sm_alternate); if (sm_alternate) - git_config_set_in_file(p, "submodule.alternateLocation", - sm_alternate); - git_config_get_string("submodule.alternateErrorStrategy", &error_strategy); + repo_config_set_in_file(the_repository, p, "submodule.alternateLocation", + sm_alternate); + repo_config_get_string(the_repository, "submodule.alternateErrorStrategy", &error_strategy); if (error_strategy) - git_config_set_in_file(p, "submodule.alternateErrorStrategy", - error_strategy); + repo_config_set_in_file(the_repository, p, "submodule.alternateErrorStrategy", + error_strategy); free(sm_alternate); free(error_strategy); @@ -2545,7 +2524,7 @@ static int ensure_core_worktree(const char *path) abs_path = absolute_pathdup(path); rel_path = relative_path(abs_path, subrepo.gitdir, &sb); - git_config_set_in_file(cfg_file, "core.worktree", rel_path); + repo_config_set_in_file(the_repository, cfg_file, "core.worktree", rel_path); free(cfg_file); free(abs_path); @@ -2660,8 +2639,10 @@ static int update_submodule(struct update_data *update_data) if (code) return code; code = remote_submodule_branch(update_data->sm_path, &branch); - if (code) + if (code) { + free(remote_name); return code; + } remote_ref = xstrfmt("refs/remotes/%s/%s", remote_name, branch); free(remote_name); @@ -2851,7 +2832,7 @@ static int module_update(int argc, const char **argv, const char *prefix, }; update_clone_config_from_gitmodules(&opt.max_jobs); - git_config(git_update_clone_config, &opt.max_jobs); + repo_config(the_repository, git_update_clone_config, &opt.max_jobs); argc = parse_options(argc, argv, prefix, module_update_options, git_submodule_helper_usage, 0); @@ -2899,7 +2880,7 @@ static int module_update(int argc, const char **argv, const char *prefix, * If there are no path args and submodule.active is set then, * by default, only initialize 'active' modules. */ - if (!argc && !git_config_get("submodule.active")) + if (!argc && !repo_config_get(the_repository, "submodule.active")) module_list_active(&list); info.prefix = opt.prefix; @@ -3149,7 +3130,7 @@ static int module_create_branch(int argc, const char **argv, const char *prefix, NULL }; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); track = git_branch_track; argc = parse_options(argc, argv, prefix, options, usage, 0); @@ -3328,9 +3309,11 @@ static void configure_added_submodule(struct add_data *add_data) char *key; struct child_process add_submod = CHILD_PROCESS_INIT; struct child_process add_gitmodules = CHILD_PROCESS_INIT; + const struct string_list *values; + int matched = 0; key = xstrfmt("submodule.%s.url", add_data->sm_name); - git_config_set_gently(key, add_data->realrepo); + repo_config_set_gently(the_repository, key, add_data->realrepo); free(key); add_submod.git_cmd = 1; @@ -3370,20 +3353,28 @@ static void configure_added_submodule(struct add_data *add_data) * is_submodule_active(), since that function needs to find * out the value of "submodule.active" again anyway. */ - if (!git_config_get("submodule.active")) { + if (repo_config_get(the_repository, "submodule.active") || /* key absent */ + repo_config_get_string_multi(the_repository, "submodule.active", &values)) { /* * If the submodule being added isn't already covered by the * current configured pathspec, set the submodule's active flag */ - if (!is_submodule_active(the_repository, add_data->sm_path)) { + key = xstrfmt("submodule.%s.active", add_data->sm_name); + repo_config_set_gently(the_repository, key, "true"); + free(key); + } else { + for (size_t i = 0; i < values->nr; i++) { + const char *pat = values->items[i].string; + if (!wildmatch(pat, add_data->sm_path, 0)) { /* match found */ + matched = 1; + break; + } + } + if (!matched) { /* no pattern matched -> force-enable */ key = xstrfmt("submodule.%s.active", add_data->sm_name); - git_config_set_gently(key, "true"); + repo_config_set_gently(the_repository, key, "true"); free(key); } - } else { - key = xstrfmt("submodule.%s.active", add_data->sm_name); - git_config_set_gently(key, "true"); - free(key); } } @@ -3444,6 +3435,9 @@ static int module_add(int argc, const char **argv, const char *prefix, struct add_data add_data = ADD_DATA_INIT; const char *ref_storage_format = NULL; char *to_free = NULL; + const struct submodule *existing; + struct strbuf buf = STRBUF_INIT; + char *sm_name_to_free = NULL; struct option options[] = { OPT_STRING('b', "branch", &add_data.branch, N_("branch"), N_("branch of repository to add as submodule")), @@ -3524,7 +3518,7 @@ static int module_add(int argc, const char **argv, const char *prefix, strip_dir_trailing_slashes(add_data.sm_path); if (validate_submodule_path(add_data.sm_path) < 0) - exit(128); + die(NULL); die_on_index_match(add_data.sm_path, force); die_on_repo_without_commits(add_data.sm_path); @@ -3546,6 +3540,28 @@ static int module_add(int argc, const char **argv, const char *prefix, if(!add_data.sm_name) add_data.sm_name = add_data.sm_path; + existing = submodule_from_name(the_repository, + null_oid(the_hash_algo), + add_data.sm_name); + + if (existing && strcmp(existing->path, add_data.sm_path)) { + if (!force) { + die(_("submodule name '%s' already used for path '%s'"), + add_data.sm_name, existing->path); + } + /* --force: build <name><n> until unique */ + for (int i = 1; ; i++) { + strbuf_reset(&buf); + strbuf_addf(&buf, "%s%d", add_data.sm_name, i); + if (!submodule_from_name(the_repository, + null_oid(the_hash_algo), + buf.buf)) { + break; + } + } + add_data.sm_name = sm_name_to_free = strbuf_detach(&buf, NULL); + } + if (check_submodule_name(add_data.sm_name)) die(_("'%s' is not a valid submodule name"), add_data.sm_name); @@ -3561,6 +3577,7 @@ static int module_add(int argc, const char **argv, const char *prefix, ret = 0; cleanup: + free(sm_name_to_free); free(add_data.sm_path); free(to_free); strbuf_release(&sb); diff --git a/builtin/symbolic-ref.c b/builtin/symbolic-ref.c index 299d23d76a..231e41e715 100644 --- a/builtin/symbolic-ref.c +++ b/builtin/symbolic-ref.c @@ -1,6 +1,7 @@ #define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "refs.h" #include "parse-options.h" @@ -59,7 +60,7 @@ int cmd_symbolic_ref(int argc, OPT_END(), }; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, git_symbolic_ref_usage, 0); if (msg && !*msg) diff --git a/builtin/tag.c b/builtin/tag.c index 4742b27d16..f0665af3ac 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -19,7 +19,7 @@ #include "refs.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "path.h" #include "tag.h" #include "parse-options.h" @@ -244,7 +244,7 @@ static void write_tag_body(int fd, const struct object_id *oid) struct strbuf payload = STRBUF_INIT; struct strbuf signature = STRBUF_INIT; - orig = buf = repo_read_object_file(the_repository, oid, &type, &size); + orig = buf = odb_read_object(the_repository->objects, oid, &type, &size); if (!buf) return; if (parse_signature(buf, size, &payload, &signature)) { @@ -271,8 +271,8 @@ static int build_tag_object(struct strbuf *buf, int sign, struct object_id *resu struct object_id *compat_oid = NULL, compat_oid_buf; if (sign && do_sign(buf, &compat_oid, &compat_oid_buf) < 0) return error(_("unable to sign the tag")); - if (write_object_file_flags(buf->buf, buf->len, OBJ_TAG, result, - compat_oid, 0) < 0) + if (odb_write_object_ext(the_repository->objects, buf->buf, + buf->len, OBJ_TAG, result, compat_oid, 0) < 0) return error(_("unable to write tag file")); return 0; } @@ -304,7 +304,7 @@ static void create_tag(const struct object_id *object, const char *object_ref, struct strbuf header = STRBUF_INIT; int should_edit; - type = oid_object_info(the_repository, object, NULL); + type = odb_read_object_info(the_repository->objects, object, NULL); if (type <= OBJ_NONE) die(_("bad object type.")); @@ -401,13 +401,13 @@ static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb) } strbuf_addstr(sb, " ("); - type = oid_object_info(the_repository, oid, NULL); + type = odb_read_object_info(the_repository->objects, oid, NULL); switch (type) { default: strbuf_addstr(sb, "object of unknown type"); break; case OBJ_COMMIT: - if ((buf = repo_read_object_file(the_repository, oid, &type, &size))) { + if ((buf = odb_read_object(the_repository->objects, oid, &type, &size))) { subject_len = find_commit_subject(buf, &subject_start); strbuf_insert(sb, sb->len, subject_start, subject_len); } else { @@ -546,7 +546,7 @@ int cmd_tag(int argc, * Try to set sort keys from config. If config does not set any, * fall back on default (refname) sorting. */ - git_config(git_tag_config, &sorting_options); + repo_config(the_repository, git_tag_config, &sorting_options); if (!sorting_options.nr) string_list_append(&sorting_options, "refname"); diff --git a/builtin/unpack-file.c b/builtin/unpack-file.c index e33acfc4ee..87877a9fab 100644 --- a/builtin/unpack-file.c +++ b/builtin/unpack-file.c @@ -1,10 +1,11 @@ #define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" #include "config.h" +#include "environment.h" #include "hex.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" static char *create_temp_file(struct object_id *oid) { @@ -14,7 +15,7 @@ static char *create_temp_file(struct object_id *oid) unsigned long size; int fd; - buf = repo_read_object_file(the_repository, oid, &type, &size); + buf = odb_read_object(the_repository->objects, oid, &type, &size); if (!buf || type != OBJ_BLOB) die("unable to read blob object %s", oid_to_hex(oid)); @@ -43,7 +44,7 @@ int cmd_unpack_file(int argc, if (repo_get_oid(the_repository, argv[1], &oid)) die("Not a valid object name %s", argv[1]); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); puts(create_temp_file(&oid)); return 0; diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c index e905d5f4e1..28124b324d 100644 --- a/builtin/unpack-objects.c +++ b/builtin/unpack-objects.c @@ -9,7 +9,7 @@ #include "git-zlib.h" #include "hex.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "object.h" #include "delta.h" #include "pack.h" @@ -204,8 +204,8 @@ static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf) { struct object_id oid; - if (write_object_file(obj_buf->buffer, obj_buf->size, - obj->type, &oid) < 0) + if (odb_write_object(the_repository->objects, obj_buf->buffer, obj_buf->size, + obj->type, &oid) < 0) die("failed to write object %s", oid_to_hex(&obj->oid)); obj->flags |= FLAG_WRITTEN; } @@ -232,7 +232,7 @@ static int check_object(struct object *obj, enum object_type type, if (!(obj->flags & FLAG_OPEN)) { unsigned long size; - int type = oid_object_info(the_repository, &obj->oid, &size); + int type = odb_read_object_info(the_repository->objects, &obj->oid, &size); if (type != obj->type || type <= 0) die("object of unexpected type"); obj->flags |= FLAG_WRITTEN; @@ -272,16 +272,16 @@ static void write_object(unsigned nr, enum object_type type, void *buf, unsigned long size) { if (!strict) { - if (write_object_file(buf, size, type, - &obj_list[nr].oid) < 0) + if (odb_write_object(the_repository->objects, buf, size, type, + &obj_list[nr].oid) < 0) die("failed to write object"); added_object(nr, type, buf, size); free(buf); obj_list[nr].obj = NULL; } else if (type == OBJ_BLOB) { struct blob *blob; - if (write_object_file(buf, size, type, - &obj_list[nr].oid) < 0) + if (odb_write_object(the_repository->objects, buf, size, type, + &obj_list[nr].oid) < 0) die("failed to write object"); added_object(nr, type, buf, size); free(buf); @@ -403,7 +403,8 @@ static void stream_blob(unsigned long size, unsigned nr) data.zstream = &zstream; git_inflate_init(&zstream); - if (stream_loose_object(&in_stream, size, &info->oid)) + if (stream_loose_object(the_repository->objects->sources, + &in_stream, size, &info->oid)) die(_("failed to write object in stream")); if (data.status != Z_STREAM_END) @@ -449,8 +450,8 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, delta_data = get_data(delta_size); if (!delta_data) return; - if (has_object(the_repository, &base_oid, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) + if (odb_has_object(the_repository->objects, &base_oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) ; /* Ok we have this one */ else if (resolve_against_held(nr, &base_oid, delta_data, delta_size)) @@ -516,8 +517,8 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size, if (resolve_against_held(nr, &base_oid, delta_data, delta_size)) return; - base = repo_read_object_file(the_repository, &base_oid, &type, - &base_size); + base = odb_read_object(the_repository->objects, &base_oid, + &type, &base_size); if (!base) { error("failed to read delta-pack base object %s", oid_to_hex(&base_oid)); @@ -583,6 +584,7 @@ static void unpack_all(void) { int i; unsigned char *hdr = fill(sizeof(struct pack_header)); + struct odb_transaction *transaction; if (get_be32(hdr) != PACK_SIGNATURE) die("bad pack file"); @@ -598,12 +600,12 @@ static void unpack_all(void) progress = start_progress(the_repository, _("Unpacking objects"), nr_objects); CALLOC_ARRAY(obj_list, nr_objects); - begin_odb_transaction(); + transaction = begin_odb_transaction(the_repository->objects); for (i = 0; i < nr_objects; i++) { unpack_one(i); display_progress(progress, i + 1); } - end_odb_transaction(); + end_odb_transaction(transaction); stop_progress(&progress); if (delta_list) @@ -621,7 +623,7 @@ int cmd_unpack_objects(int argc, disable_replace_refs(); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); quiet = !isatty(2); diff --git a/builtin/update-index.c b/builtin/update-index.c index 538b619ba4..2ba2d29c95 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -77,7 +77,7 @@ static void report(const char *fmt, ...) * objects invisible while a transaction is active, so flush the * transaction here before reporting a change made by update-index. */ - flush_odb_transaction(); + flush_odb_transaction(the_repository->objects->transaction); va_start(vp, fmt); vprintf(fmt, vp); putchar('\n'); @@ -940,6 +940,7 @@ int cmd_update_index(int argc, strbuf_getline_fn getline_fn; int parseopt_state = PARSE_OPT_UNKNOWN; struct repository *r = the_repository; + struct odb_transaction *transaction; struct option options[] = { OPT_BIT('q', NULL, &refresh_args.flags, N_("continue refresh even when index needs update"), @@ -981,6 +982,7 @@ int cmd_update_index(int argc, .type = OPTION_SET_INT, .long_name = "assume-unchanged", .value = &mark_valid_only, + .precision = sizeof(mark_valid_only), .help = N_("mark files as \"not changing\""), .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, .defval = MARK_FLAG, @@ -989,6 +991,7 @@ int cmd_update_index(int argc, .type = OPTION_SET_INT, .long_name = "no-assume-unchanged", .value = &mark_valid_only, + .precision = sizeof(mark_valid_only), .help = N_("clear assumed-unchanged bit"), .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, .defval = UNMARK_FLAG, @@ -997,6 +1000,7 @@ int cmd_update_index(int argc, .type = OPTION_SET_INT, .long_name = "skip-worktree", .value = &mark_skip_worktree_only, + .precision = sizeof(mark_skip_worktree_only), .help = N_("mark files as \"index-only\""), .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, .defval = MARK_FLAG, @@ -1005,6 +1009,7 @@ int cmd_update_index(int argc, .type = OPTION_SET_INT, .long_name = "no-skip-worktree", .value = &mark_skip_worktree_only, + .precision = sizeof(mark_skip_worktree_only), .help = N_("clear skip-worktree bit"), .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, .defval = UNMARK_FLAG, @@ -1079,6 +1084,7 @@ int cmd_update_index(int argc, .type = OPTION_SET_INT, .long_name = "fsmonitor-valid", .value = &mark_fsmonitor_only, + .precision = sizeof(mark_fsmonitor_only), .help = N_("mark files as fsmonitor valid"), .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, .defval = MARK_FLAG, @@ -1087,6 +1093,7 @@ int cmd_update_index(int argc, .type = OPTION_SET_INT, .long_name = "no-fsmonitor-valid", .value = &mark_fsmonitor_only, + .precision = sizeof(mark_fsmonitor_only), .help = N_("clear fsmonitor valid bit"), .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, .defval = UNMARK_FLAG, @@ -1097,7 +1104,7 @@ int cmd_update_index(int argc, show_usage_with_options_if_asked(argc, argv, update_index_usage, options); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); prepare_repo_settings(r); the_repository->settings.command_requires_full_index = 0; @@ -1124,7 +1131,7 @@ int cmd_update_index(int argc, * Allow the object layer to optimize adding multiple objects in * a batch. */ - begin_odb_transaction(); + transaction = begin_odb_transaction(the_repository->objects); while (ctx.argc) { if (parseopt_state != PARSE_OPT_DONE) parseopt_state = parse_options_step(&ctx, options, @@ -1207,7 +1214,7 @@ int cmd_update_index(int argc, /* * By now we have added all of the new objects */ - end_odb_transaction(); + end_odb_transaction(transaction); if (split_index > 0) { if (repo_config_get_split_index(the_repository) == 0) diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 2b1e336ba1..195437e7c6 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -3,6 +3,7 @@ #include "builtin.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "hash.h" #include "hex.h" @@ -575,30 +576,7 @@ static void print_rejected_refs(const char *refname, void *cb_data UNUSED) { struct strbuf sb = STRBUF_INIT; - const char *reason = ""; - - switch (err) { - case REF_TRANSACTION_ERROR_NAME_CONFLICT: - reason = "refname conflict"; - break; - case REF_TRANSACTION_ERROR_CREATE_EXISTS: - reason = "reference already exists"; - break; - case REF_TRANSACTION_ERROR_NONEXISTENT_REF: - reason = "reference does not exist"; - break; - case REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE: - reason = "incorrect old value provided"; - break; - case REF_TRANSACTION_ERROR_INVALID_NEW_VALUE: - reason = "invalid new value provided"; - break; - case REF_TRANSACTION_ERROR_EXPECTED_SYMREF: - reason = "expected symref but found regular ref"; - break; - default: - reason = "unkown failure"; - } + const char *reason = ref_transaction_error_msg(err); strbuf_addf(&sb, "rejected %s %s %s %s\n", refname, new_oid ? oid_to_hex(new_oid) : new_target, @@ -792,7 +770,7 @@ int cmd_update_ref(int argc, OPT_END(), }; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, git_update_ref_usage, 0); if (msg && !*msg) diff --git a/builtin/update-server-info.c b/builtin/update-server-info.c index ba702d30ef..4c12968a83 100644 --- a/builtin/update-server-info.c +++ b/builtin/update-server-info.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "parse-options.h" #include "server-info.h" diff --git a/builtin/var.c b/builtin/var.c index ada642a9fe..cc3a43cde2 100644 --- a/builtin/var.c +++ b/builtin/var.c @@ -11,6 +11,7 @@ #include "attr.h" #include "config.h" #include "editor.h" +#include "environment.h" #include "ident.h" #include "pager.h" #include "refs.h" @@ -181,7 +182,7 @@ static void list_vars(void) if (ptr->multivalued && *val) { struct string_list list = STRING_LIST_INIT_DUP; - string_list_split(&list, val, '\n', -1); + string_list_split(&list, val, "\n", -1); for (size_t i = 0; i < list.nr; i++) printf("%s=%s\n", ptr->name, list.items[i].string); string_list_clear(&list, 0); @@ -226,11 +227,11 @@ int cmd_var(int argc, usage(var_usage); if (strcmp(argv[1], "-l") == 0) { - git_config(show_config, NULL); + repo_config(the_repository, show_config, NULL); list_vars(); return 0; } - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); git_var = get_git_var(argv[1]); if (!git_var) diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c index 5f749a30da..62398acd72 100644 --- a/builtin/verify-commit.c +++ b/builtin/verify-commit.c @@ -7,6 +7,7 @@ */ #include "builtin.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "object-name.h" #include "commit.h" diff --git a/builtin/verify-pack.c b/builtin/verify-pack.c index 34e4ed715f..65fd6629a0 100644 --- a/builtin/verify-pack.c +++ b/builtin/verify-pack.c @@ -1,6 +1,7 @@ #define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "run-command.h" #include "parse-options.h" @@ -81,7 +82,7 @@ int cmd_verify_pack(int argc, OPT_END() }; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, prefix, verify_pack_options, verify_pack_usage, 0); if (argc < 1) diff --git a/builtin/verify-tag.c b/builtin/verify-tag.c index ed1c40338f..cd6bc11095 100644 --- a/builtin/verify-tag.c +++ b/builtin/verify-tag.c @@ -7,6 +7,7 @@ */ #include "builtin.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "tag.h" #include "object-name.h" diff --git a/builtin/worktree.c b/builtin/worktree.c index 88a36ea9f8..812774a5ca 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -379,13 +379,13 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir) if (!git_configset_get_bool(&cs, "core.bare", &bare) && bare && - git_config_set_multivar_in_file_gently( + repo_config_set_multivar_in_file_gently(the_repository, to_file, "core.bare", NULL, "true", NULL, 0)) error(_("failed to unset '%s' in '%s'"), "core.bare", to_file); if (!git_configset_get(&cs, "core.worktree") && - git_config_set_in_file_gently(to_file, - "core.worktree", NULL, NULL)) + repo_config_set_in_file_gently(the_repository, to_file, + "core.worktree", NULL, NULL)) error(_("failed to unset '%s' in '%s'"), "core.worktree", to_file); @@ -621,7 +621,7 @@ static void print_preparing_worktree_line(int detach, else { struct commit *commit = lookup_commit_reference_by_name(branch); if (!commit) - BUG(_("unreachable: invalid reference: %s"), branch); + BUG("unreachable: invalid reference: %s", branch); fprintf_ln(stderr, _("Preparing worktree (detached HEAD %s)"), repo_find_unique_abbrev(the_repository, &commit->object.oid, DEFAULT_ABBREV)); } @@ -1448,7 +1448,7 @@ int cmd_worktree(int ac, OPT_END() }; - git_config(git_worktree_config, NULL); + repo_config(the_repository, git_worktree_config, NULL); if (!prefix) prefix = ""; diff --git a/builtin/write-tree.c b/builtin/write-tree.c index 5a8dc377ec..e3bd1a40db 100644 --- a/builtin/write-tree.c +++ b/builtin/write-tree.c @@ -6,6 +6,7 @@ #define USE_THE_REPOSITORY_VARIABLE #include "builtin.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "hex.h" #include "tree.h" @@ -35,6 +36,7 @@ int cmd_write_tree(int argc, .type = OPTION_BIT, .long_name = "ignore-cache-tree", .value = &flags, + .precision = sizeof(flags), .help = N_("only useful for debugging"), .flags = PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, .defval = WRITE_TREE_IGNORE_CACHE_TREE, @@ -42,7 +44,7 @@ int cmd_write_tree(int argc, OPT_END() }; - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); argc = parse_options(argc, argv, cmd_prefix, write_tree_options, write_tree_usage, 0); diff --git a/bulk-checkin.c b/bulk-checkin.c index 678e2ecc2c..124c493067 100644 --- a/bulk-checkin.c +++ b/bulk-checkin.c @@ -17,13 +17,9 @@ #include "tmp-objdir.h" #include "packfile.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" -static int odb_transaction_nesting; - -static struct tmp_objdir *bulk_fsync_objdir; - -static struct bulk_checkin_packfile { +struct bulk_checkin_packfile { char *pack_tmp_name; struct hashfile *f; off_t offset; @@ -32,27 +28,36 @@ static struct bulk_checkin_packfile { struct pack_idx_entry **written; uint32_t alloc_written; uint32_t nr_written; -} bulk_checkin_packfile; +}; + +struct odb_transaction { + struct object_database *odb; + + int nesting; + struct tmp_objdir *objdir; + struct bulk_checkin_packfile packfile; +}; -static void finish_tmp_packfile(struct strbuf *basename, - const char *pack_tmp_name, - struct pack_idx_entry **written_list, - uint32_t nr_written, - struct pack_idx_option *pack_idx_opts, +static void finish_tmp_packfile(struct odb_transaction *transaction, + struct strbuf *basename, unsigned char hash[]) { + struct bulk_checkin_packfile *state = &transaction->packfile; + struct repository *repo = transaction->odb->repo; char *idx_tmp_name = NULL; - stage_tmp_packfiles(the_repository, basename, pack_tmp_name, - written_list, nr_written, NULL, pack_idx_opts, hash, - &idx_tmp_name); - rename_tmp_packfile_idx(basename, &idx_tmp_name); + stage_tmp_packfiles(repo, basename, state->pack_tmp_name, + state->written, state->nr_written, NULL, + &state->pack_idx_opts, hash, &idx_tmp_name); + rename_tmp_packfile_idx(repo, basename, &idx_tmp_name); free(idx_tmp_name); } -static void flush_bulk_checkin_packfile(struct bulk_checkin_packfile *state) +static void flush_bulk_checkin_packfile(struct odb_transaction *transaction) { + struct bulk_checkin_packfile *state = &transaction->packfile; + struct repository *repo = transaction->odb->repo; unsigned char hash[GIT_MAX_RAWSZ]; struct strbuf packname = STRBUF_INIT; @@ -69,17 +74,17 @@ static void flush_bulk_checkin_packfile(struct bulk_checkin_packfile *state) CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE); } else { int fd = finalize_hashfile(state->f, hash, FSYNC_COMPONENT_PACK, 0); - fixup_pack_header_footer(the_hash_algo, fd, hash, state->pack_tmp_name, + fixup_pack_header_footer(repo->hash_algo, fd, hash, state->pack_tmp_name, state->nr_written, hash, state->offset); close(fd); } - strbuf_addf(&packname, "%s/pack/pack-%s.", repo_get_object_directory(the_repository), - hash_to_hex(hash)); - finish_tmp_packfile(&packname, state->pack_tmp_name, - state->written, state->nr_written, - &state->pack_idx_opts, hash); + strbuf_addf(&packname, "%s/pack/pack-%s.", + repo_get_object_directory(transaction->odb->repo), + hash_to_hex_algop(hash, repo->hash_algo)); + + finish_tmp_packfile(transaction, &packname, hash); for (uint32_t i = 0; i < state->nr_written; i++) free(state->written[i]); @@ -90,18 +95,18 @@ clear_exit: strbuf_release(&packname); /* Make objects we just wrote available to ourselves */ - reprepare_packed_git(the_repository); + reprepare_packed_git(repo); } /* * Cleanup after batch-mode fsync_object_files. */ -static void flush_batch_fsync(void) +static void flush_batch_fsync(struct odb_transaction *transaction) { struct strbuf temp_path = STRBUF_INIT; struct tempfile *temp; - if (!bulk_fsync_objdir) + if (!transaction->objdir) return; /* @@ -113,7 +118,8 @@ static void flush_batch_fsync(void) * to ensure that the data in each new object file is durable before * the final name is visible. */ - strbuf_addf(&temp_path, "%s/bulk_fsync_XXXXXX", repo_get_object_directory(the_repository)); + strbuf_addf(&temp_path, "%s/bulk_fsync_XXXXXX", + repo_get_object_directory(transaction->odb->repo)); temp = xmks_tempfile(temp_path.buf); fsync_or_die(get_tempfile_fd(temp), get_tempfile_path(temp)); delete_tempfile(&temp); @@ -123,20 +129,21 @@ static void flush_batch_fsync(void) * Make the object files visible in the primary ODB after their data is * fully durable. */ - tmp_objdir_migrate(bulk_fsync_objdir); - bulk_fsync_objdir = NULL; + tmp_objdir_migrate(transaction->objdir); + transaction->objdir = NULL; } -static int already_written(struct bulk_checkin_packfile *state, struct object_id *oid) +static int already_written(struct odb_transaction *transaction, + struct object_id *oid) { /* The object may already exist in the repository */ - if (has_object(the_repository, oid, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) + if (odb_has_object(transaction->odb, oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) return 1; /* Might want to keep the list sorted */ - for (uint32_t i = 0; i < state->nr_written; i++) - if (oideq(&state->written[i]->oid, oid)) + for (uint32_t i = 0; i < transaction->packfile.nr_written; i++) + if (oideq(&transaction->packfile.written[i]->oid, oid)) return 1; /* This is a new object we need to keep */ @@ -235,13 +242,15 @@ static int stream_blob_to_pack(struct bulk_checkin_packfile *state, } /* Lazily create backing packfile for the state */ -static void prepare_to_stream(struct bulk_checkin_packfile *state, +static void prepare_to_stream(struct odb_transaction *transaction, unsigned flags) { + struct bulk_checkin_packfile *state = &transaction->packfile; if (!(flags & INDEX_WRITE_OBJECT) || state->f) return; - state->f = create_tmp_packfile(the_repository, &state->pack_tmp_name); + state->f = create_tmp_packfile(transaction->odb->repo, + &state->pack_tmp_name); reset_pack_idx_option(&state->pack_idx_opts); /* Pretend we are going to write only one object */ @@ -250,11 +259,11 @@ static void prepare_to_stream(struct bulk_checkin_packfile *state, die_errno("unable to write pack header"); } -static int deflate_blob_to_pack(struct bulk_checkin_packfile *state, - struct object_id *result_oid, - int fd, size_t size, - const char *path, unsigned flags) +int index_blob_bulk_checkin(struct odb_transaction *transaction, + struct object_id *result_oid, int fd, size_t size, + const char *path, unsigned flags) { + struct bulk_checkin_packfile *state = &transaction->packfile; off_t seekback, already_hashed_to; struct git_hash_ctx ctx; unsigned char obuf[16384]; @@ -268,21 +277,21 @@ static int deflate_blob_to_pack(struct bulk_checkin_packfile *state, header_len = format_object_header((char *)obuf, sizeof(obuf), OBJ_BLOB, size); - the_hash_algo->init_fn(&ctx); + transaction->odb->repo->hash_algo->init_fn(&ctx); git_hash_update(&ctx, obuf, header_len); /* Note: idx is non-NULL when we are writing */ if ((flags & INDEX_WRITE_OBJECT) != 0) { CALLOC_ARRAY(idx, 1); - prepare_to_stream(state, flags); + prepare_to_stream(transaction, flags); hashfile_checkpoint_init(state->f, &checkpoint); } already_hashed_to = 0; while (1) { - prepare_to_stream(state, flags); + prepare_to_stream(transaction, flags); if (idx) { hashfile_checkpoint(state->f, &checkpoint); idx->offset = state->offset; @@ -300,7 +309,7 @@ static int deflate_blob_to_pack(struct bulk_checkin_packfile *state, BUG("should not happen"); hashfile_truncate(state->f, &checkpoint); state->offset = checkpoint.offset; - flush_bulk_checkin_packfile(state); + flush_bulk_checkin_packfile(transaction); if (lseek(fd, seekback, SEEK_SET) == (off_t) -1) return error("cannot seek back"); } @@ -309,7 +318,7 @@ static int deflate_blob_to_pack(struct bulk_checkin_packfile *state, return 0; idx->crc32 = crc32_end(state->f); - if (already_written(state, result_oid)) { + if (already_written(transaction, result_oid)) { hashfile_truncate(state->f, &checkpoint); state->offset = checkpoint.offset; free(idx); @@ -323,7 +332,7 @@ static int deflate_blob_to_pack(struct bulk_checkin_packfile *state, return 0; } -void prepare_loose_object_bulk_checkin(void) +void prepare_loose_object_bulk_checkin(struct odb_transaction *transaction) { /* * We lazily create the temporary object directory @@ -331,15 +340,16 @@ void prepare_loose_object_bulk_checkin(void) * callers may not know whether any objects will be * added at the time they call begin_odb_transaction. */ - if (!odb_transaction_nesting || bulk_fsync_objdir) + if (!transaction || transaction->objdir) return; - bulk_fsync_objdir = tmp_objdir_create(the_repository, "bulk-fsync"); - if (bulk_fsync_objdir) - tmp_objdir_replace_primary_odb(bulk_fsync_objdir, 0); + transaction->objdir = tmp_objdir_create(transaction->odb->repo, "bulk-fsync"); + if (transaction->objdir) + tmp_objdir_replace_primary_odb(transaction->objdir, 0); } -void fsync_loose_object_bulk_checkin(int fd, const char *filename) +void fsync_loose_object_bulk_checkin(struct odb_transaction *transaction, + int fd, const char *filename) { /* * If we have an active ODB transaction, we issue a call that @@ -348,7 +358,7 @@ void fsync_loose_object_bulk_checkin(int fd, const char *filename) * before renaming the objects to their final names as part of * flush_batch_fsync. */ - if (!bulk_fsync_objdir || + if (!transaction || !transaction->objdir || git_fsync(fd, FSYNC_WRITEOUT_ONLY) < 0) { if (errno == ENOSYS) warning(_("core.fsyncMethod = batch is unsupported on this platform")); @@ -356,36 +366,38 @@ void fsync_loose_object_bulk_checkin(int fd, const char *filename) } } -int index_blob_bulk_checkin(struct object_id *oid, - int fd, size_t size, - const char *path, unsigned flags) +struct odb_transaction *begin_odb_transaction(struct object_database *odb) { - int status = deflate_blob_to_pack(&bulk_checkin_packfile, oid, fd, size, - path, flags); - if (!odb_transaction_nesting) - flush_bulk_checkin_packfile(&bulk_checkin_packfile); - return status; -} + if (!odb->transaction) { + CALLOC_ARRAY(odb->transaction, 1); + odb->transaction->odb = odb; + } -void begin_odb_transaction(void) -{ - odb_transaction_nesting += 1; + odb->transaction->nesting += 1; + + return odb->transaction; } -void flush_odb_transaction(void) +void flush_odb_transaction(struct odb_transaction *transaction) { - flush_batch_fsync(); - flush_bulk_checkin_packfile(&bulk_checkin_packfile); + if (!transaction) + return; + + flush_batch_fsync(transaction); + flush_bulk_checkin_packfile(transaction); } -void end_odb_transaction(void) +void end_odb_transaction(struct odb_transaction *transaction) { - odb_transaction_nesting -= 1; - if (odb_transaction_nesting < 0) + if (!transaction || transaction->nesting == 0) BUG("Unbalanced ODB transaction nesting"); - if (odb_transaction_nesting) + transaction->nesting -= 1; + + if (transaction->nesting) return; - flush_odb_transaction(); + flush_odb_transaction(transaction); + transaction->odb->transaction = NULL; + free(transaction); } diff --git a/bulk-checkin.h b/bulk-checkin.h index 7246ea58dc..ac8887f476 100644 --- a/bulk-checkin.h +++ b/bulk-checkin.h @@ -5,13 +5,20 @@ #define BULK_CHECKIN_H #include "object.h" +#include "odb.h" -void prepare_loose_object_bulk_checkin(void); -void fsync_loose_object_bulk_checkin(int fd, const char *filename); +struct odb_transaction; + +void prepare_loose_object_bulk_checkin(struct odb_transaction *transaction); +void fsync_loose_object_bulk_checkin(struct odb_transaction *transaction, + int fd, const char *filename); /* - * This creates one packfile per large blob unless bulk-checkin - * machinery is "plugged". + * This writes the specified object to a packfile. Objects written here + * during the same transaction are written to the same packfile. The + * packfile is not flushed until the transaction is flushed. The caller + * is expected to ensure a valid transaction is setup for objects to be + * recorded to. * * This also bypasses the usual "convert-to-git" dance, and that is on * purpose. We could write a streaming version of the converting @@ -24,8 +31,8 @@ void fsync_loose_object_bulk_checkin(int fd, const char *filename); * binary blobs, they generally do not want to get any conversion, and * callers should avoid this code path when filters are requested. */ -int index_blob_bulk_checkin(struct object_id *oid, - int fd, size_t size, +int index_blob_bulk_checkin(struct odb_transaction *transaction, + struct object_id *oid, int fd, size_t size, const char *path, unsigned flags); /* @@ -35,20 +42,20 @@ int index_blob_bulk_checkin(struct object_id *oid, * and objects are only visible after the outermost transaction * is complete or the transaction is flushed. */ -void begin_odb_transaction(void); +struct odb_transaction *begin_odb_transaction(struct object_database *odb); /* * Make any objects that are currently part of a pending object * database transaction visible. It is valid to call this function * even if no transaction is active. */ -void flush_odb_transaction(void); +void flush_odb_transaction(struct odb_transaction *transaction); /* * Tell the object database to make any objects from the * current transaction visible if this is the final nested * transaction. */ -void end_odb_transaction(void); +void end_odb_transaction(struct odb_transaction *transaction); #endif diff --git a/bundle-uri.c b/bundle-uri.c index 0050c62bd5..57cccfc6b8 100644 --- a/bundle-uri.c +++ b/bundle-uri.c @@ -14,7 +14,7 @@ #include "fetch-pack.h" #include "remote.h" #include "trace2.h" -#include "object-store.h" +#include "odb.h" static struct { enum bundle_list_heuristic heuristic; @@ -122,7 +122,7 @@ void print_bundle_list(FILE *fp, struct bundle_list *list) int i; for (i = 0; i < BUNDLE_HEURISTIC__COUNT; i++) { if (heuristics[i].heuristic == list->heuristic) { - printf("\theuristic = %s\n", + fprintf(fp, "\theuristic = %s\n", heuristics[list->heuristic].name); break; } @@ -278,7 +278,8 @@ static char *find_temp_filename(void) * Find a temporary filename that is available. This is briefly * racy, but unlikely to collide. */ - fd = odb_mkstemp(&name, "bundles/tmp_uri_XXXXXX"); + fd = odb_mkstemp(the_repository->objects, &name, + "bundles/tmp_uri_XXXXXX"); if (fd < 0) { warning(_("failed to create temporary file")); return NULL; @@ -7,7 +7,7 @@ #include "environment.h" #include "gettext.h" #include "hex.h" -#include "object-store.h" +#include "odb.h" #include "repository.h" #include "object.h" #include "commit.h" @@ -95,7 +95,7 @@ int read_bundle_header_fd(int fd, struct bundle_header *header, * by an "object-format=" capability, which is being handled in * `parse_capability()`. */ - header->hash_algo = &hash_algos[GIT_HASH_SHA1]; + header->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY]; /* The bundle header ends with an empty line */ while (!strbuf_getwholeline_fd(&buf, fd, '\n') && @@ -233,7 +233,7 @@ int verify_bundle(struct repository *r, .quiet = 1, }; - if (!r || !r->objects || !r->objects->odb) + if (!r || !r->objects || !r->objects->sources) return error(_("need a repository to verify a bundle")); for (i = 0; i < p->nr; i++) { @@ -305,7 +305,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs) if (revs->max_age == -1 && revs->min_age == -1) goto out; - buf = repo_read_object_file(the_repository, &tag->oid, &type, &size); + buf = odb_read_object(the_repository->objects, &tag->oid, &type, &size); if (!buf) goto out; line = memmem(buf, size, "\ntagger ", 8); @@ -507,7 +507,7 @@ int create_bundle(struct repository *r, const char *path, * SHA1. * 2. @filter is required because we parsed an object filter. */ - if (the_hash_algo != &hash_algos[GIT_HASH_SHA1] || revs.filter.choice) + if (the_hash_algo != &hash_algos[GIT_HASH_SHA1_LEGACY] || revs.filter.choice) min_version = 3; if (argc > 1) { diff --git a/cache-tree.c b/cache-tree.c index fa3858e282..d225554eed 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -10,7 +10,7 @@ #include "cache-tree.h" #include "bulk-checkin.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "read-cache-ll.h" #include "replace-object.h" #include "repository.h" @@ -239,8 +239,8 @@ int cache_tree_fully_valid(struct cache_tree *it) if (!it) return 0; if (it->entry_count < 0 || - has_object(the_repository, &it->oid, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) + odb_has_object(the_repository->objects, &it->oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) return 0; for (i = 0; i < it->subtree_nr; i++) { if (!cache_tree_fully_valid(it->down[i]->cache_tree)) @@ -292,8 +292,8 @@ static int update_one(struct cache_tree *it, } if (0 <= it->entry_count && - has_object(the_repository, &it->oid, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) + odb_has_object(the_repository->objects, &it->oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) return it->entry_count; /* @@ -399,8 +399,9 @@ static int update_one(struct cache_tree *it, ce_missing_ok = mode == S_IFGITLINK || missing_ok || !must_check_existence(ce); if (is_null_oid(oid) || - (!ce_missing_ok && !has_object(the_repository, oid, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))) { + (!ce_missing_ok && + !odb_has_object(the_repository->objects, oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR))) { strbuf_release(&buffer); if (expected_missing) return -1; @@ -448,16 +449,15 @@ static int update_one(struct cache_tree *it, struct object_id oid; hash_object_file(the_hash_algo, buffer.buf, buffer.len, OBJ_TREE, &oid); - if (has_object(the_repository, &oid, HAS_OBJECT_RECHECK_PACKED)) + if (odb_has_object(the_repository->objects, &oid, HAS_OBJECT_RECHECK_PACKED)) oidcpy(&it->oid, &oid); else to_invalidate = 1; } else if (dryrun) { hash_object_file(the_hash_algo, buffer.buf, buffer.len, OBJ_TREE, &it->oid); - } else if (write_object_file_flags(buffer.buf, buffer.len, OBJ_TREE, - &it->oid, NULL, flags & WRITE_TREE_SILENT - ? WRITE_OBJECT_FILE_SILENT : 0)) { + } else if (odb_write_object_ext(the_repository->objects, buffer.buf, buffer.len, OBJ_TREE, + &it->oid, NULL, flags & WRITE_TREE_SILENT ? WRITE_OBJECT_SILENT : 0)) { strbuf_release(&buffer); return -1; } @@ -474,6 +474,7 @@ static int update_one(struct cache_tree *it, int cache_tree_update(struct index_state *istate, int flags) { + struct odb_transaction *transaction; int skip, i; i = verify_cache(istate, flags); @@ -489,10 +490,10 @@ int cache_tree_update(struct index_state *istate, int flags) trace_performance_enter(); trace2_region_enter("cache_tree", "update", the_repository); - begin_odb_transaction(); + transaction = begin_odb_transaction(the_repository->objects); i = update_one(istate->cache_tree, istate->cache, istate->cache_nr, "", 0, &skip, flags); - end_odb_transaction(); + end_odb_transaction(transaction); trace2_region_leave("cache_tree", "update", the_repository); trace_performance_leave("cache_tree_update"); if (i < 0) diff --git a/checkout.c b/checkout.c index 0b1cf8b40b..1588b116ee 100644 --- a/checkout.c +++ b/checkout.c @@ -52,7 +52,7 @@ char *unique_tracking_name(const char *name, struct object_id *oid, { struct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT; const char *default_remote = NULL; - if (!git_config_get_string_tmp("checkout.defaultremote", &default_remote)) + if (!repo_config_get_string_tmp(the_repository, "checkout.defaultremote", &default_remote)) cb_data.default_remote = default_remote; cb_data.src_ref = xstrfmt("refs/heads/%s", name); cb_data.dst_oid = oid; diff --git a/ci/print-test-failures.sh b/ci/print-test-failures.sh index dc910e5160..5545e77c13 100755 --- a/ci/print-test-failures.sh +++ b/ci/print-test-failures.sh @@ -41,7 +41,7 @@ do case "$CI_TYPE" in github-actions) mkdir -p failed-test-artifacts - echo "FAILED_TEST_ARTIFACTS=${TEST_OUTPUT_DIRECTORY:t}/failed-test-artifacts" >>$GITHUB_ENV + echo "FAILED_TEST_ARTIFACTS=${TEST_OUTPUT_DIRECTORY:-t}/failed-test-artifacts" >>$GITHUB_ENV cp "${TEST_EXIT%.exit}.out" failed-test-artifacts/ tar czf failed-test-artifacts/"$test_name".trash.tar.gz "$trash_dir" continue diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh index 01823fd0f1..a21834043f 100755 --- a/ci/run-build-and-tests.sh +++ b/ci/run-build-and-tests.sh @@ -9,7 +9,6 @@ run_tests=t case "$jobname" in linux-breaking-changes) - export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export WITH_BREAKING_CHANGES=YesPlease ;; linux-TEST-vars) diff --git a/ci/run-style-check.sh b/ci/run-style-check.sh index 6cd4b1d934..0832c19df0 100755 --- a/ci/run-style-check.sh +++ b/ci/run-style-check.sh @@ -5,21 +5,5 @@ baseCommit=$1 -# Remove optional braces of control statements (if, else, for, and while) -# according to the LLVM coding style. This avoids braces on simple -# single-statement bodies of statements but keeps braces if one side of -# if/else if/.../else cascade has multi-statement body. -# -# As this rule comes with a warning [1], we want to experiment with it -# before adding it in-tree. since the CI job for the style check is allowed -# to fail, appending the rule here allows us to validate its efficacy. -# While also ensuring that end-users are not affected directly. -# -# [1]: https://clang.llvm.org/docs/ClangFormatStyleOptions.html#removebracesllvm -{ - cat .clang-format - echo "RemoveBracesLLVM: true" -} >/tmp/clang-format-rules - -git clang-format --style=file:/tmp/clang-format-rules \ +git clang-format --style=file:.clang-format \ --diff --extensions c,h "$baseCommit" diff --git a/combine-diff.c b/combine-diff.c index dfae9f7995..3878faabe7 100644 --- a/combine-diff.c +++ b/combine-diff.c @@ -2,7 +2,7 @@ #define DISABLE_SIGN_COMPARE_WARNINGS #include "git-compat-util.h" -#include "object-store.h" +#include "odb.h" #include "commit.h" #include "convert.h" #include "diff.h" @@ -325,7 +325,7 @@ static char *grab_blob(struct repository *r, *size = fill_textconv(r, textconv, df, &blob); free_filespec(df); } else { - blob = repo_read_object_file(r, oid, &type, size); + blob = odb_read_object(r->objects, oid, &type, size); if (!blob) die(_("unable to read %s"), oid_to_hex(oid)); if (type != OBJ_BLOB) @@ -1315,7 +1315,7 @@ static struct diff_filepair *combined_pair(struct combine_diff_path *p, struct diff_filepair *pair; struct diff_filespec *pool; - pair = xmalloc(sizeof(*pair)); + CALLOC_ARRAY(pair, 1); CALLOC_ARRAY(pool, st_add(num_parent, 1)); pair->one = pool + 1; pair->two = pool; diff --git a/command-list.txt b/command-list.txt index b7ade3ab9f..accd3d0c4b 100644 --- a/command-list.txt +++ b/command-list.txt @@ -124,6 +124,7 @@ git-index-pack plumbingmanipulators git-init mainporcelain init git-instaweb ancillaryinterrogators complete git-interpret-trailers purehelpers +git-last-modified plumbinginterrogators git-log mainporcelain info git-ls-files plumbinginterrogators git-ls-remote plumbinginterrogators @@ -164,6 +165,7 @@ git-remote ancillarymanipulators complete git-repack ancillarymanipulators complete git-replace ancillarymanipulators complete git-replay plumbingmanipulators +git-repo plumbinginterrogators git-request-pull foreignscminterface complete git-rerere ancillaryinterrogators git-reset mainporcelain history diff --git a/commit-graph.c b/commit-graph.c index ad3943b690..2f20f66cfd 100644 --- a/commit-graph.c +++ b/commit-graph.c @@ -1,9 +1,9 @@ -#define USE_THE_REPOSITORY_VARIABLE #define DISABLE_SIGN_COMPARE_WARNINGS #include "git-compat-util.h" #include "config.h" #include "csum-file.h" +#include "environment.h" #include "gettext.h" #include "hex.h" #include "lockfile.h" @@ -13,7 +13,7 @@ #include "refs.h" #include "hash-lookup.h" #include "commit-graph.h" -#include "object-store.h" +#include "odb.h" #include "oid-array.h" #include "path.h" #include "alloc.h" @@ -28,7 +28,7 @@ #include "tree.h" #include "chunk-format.h" -void git_test_write_commit_graph_or_die(void) +void git_test_write_commit_graph_or_die(struct odb_source *source) { int flags = 0; if (!git_env_bool(GIT_TEST_COMMIT_GRAPH, 0)) @@ -37,8 +37,7 @@ void git_test_write_commit_graph_or_die(void) if (git_env_bool(GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS, 0)) flags = COMMIT_GRAPH_WRITE_BLOOM_FILTERS; - if (write_commit_graph_reachable(the_repository->objects->odb, - flags, NULL)) + if (write_commit_graph_reachable(source, flags, NULL)) die("failed to write commit-graph under GIT_TEST_COMMIT_GRAPH"); } @@ -53,8 +52,6 @@ void git_test_write_commit_graph_or_die(void) #define GRAPH_CHUNKID_BLOOMDATA 0x42444154 /* "BDAT" */ #define GRAPH_CHUNKID_BASE 0x42415345 /* "BASE" */ -#define GRAPH_DATA_WIDTH (the_hash_algo->rawsz + 16) - #define GRAPH_VERSION_1 0x1 #define GRAPH_VERSION GRAPH_VERSION_1 @@ -66,8 +63,6 @@ void git_test_write_commit_graph_or_die(void) #define GRAPH_HEADER_SIZE 8 #define GRAPH_FANOUT_SIZE (4 * 256) -#define GRAPH_MIN_SIZE (GRAPH_HEADER_SIZE + 4 * CHUNK_TOC_ENTRY_SIZE \ - + GRAPH_FANOUT_SIZE + the_hash_algo->rawsz) #define CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW (1ULL << 31) @@ -80,6 +75,16 @@ define_commit_slab(topo_level_slab, uint32_t); define_commit_slab(commit_pos, int); static struct commit_pos commit_pos = COMMIT_SLAB_INIT(1, commit_pos); +static size_t graph_data_width(const struct git_hash_algo *algop) +{ + return algop->rawsz + 16; +} + +static size_t graph_min_size(const struct git_hash_algo *algop) +{ + return GRAPH_HEADER_SIZE + 4 * CHUNK_TOC_ENTRY_SIZE + GRAPH_FANOUT_SIZE + algop->rawsz; +} + static void set_commit_pos(struct repository *r, const struct object_id *oid) { static int32_t max_pos; @@ -191,21 +196,21 @@ static int commit_gen_cmp(const void *va, const void *vb) return 0; } -char *get_commit_graph_filename(struct object_directory *obj_dir) +char *get_commit_graph_filename(struct odb_source *source) { - return xstrfmt("%s/info/commit-graph", obj_dir->path); + return xstrfmt("%s/info/commit-graph", source->path); } -static char *get_split_graph_filename(struct object_directory *odb, +static char *get_split_graph_filename(struct odb_source *source, const char *oid_hex) { - return xstrfmt("%s/info/commit-graphs/graph-%s.graph", odb->path, + return xstrfmt("%s/info/commit-graphs/graph-%s.graph", source->path, oid_hex); } -char *get_commit_graph_chain_filename(struct object_directory *odb) +char *get_commit_graph_chain_filename(struct odb_source *source) { - return xstrfmt("%s/info/commit-graphs/commit-graph-chain", odb->path); + return xstrfmt("%s/info/commit-graphs/commit-graph-chain", source->path); } static struct commit_graph *alloc_commit_graph(void) @@ -248,9 +253,8 @@ int open_commit_graph(const char *graph_file, int *fd, struct stat *st) return 1; } -struct commit_graph *load_commit_graph_one_fd_st(struct repository *r, - int fd, struct stat *st, - struct object_directory *odb) +struct commit_graph *load_commit_graph_one_fd_st(struct odb_source *source, + int fd, struct stat *st) { void *graph_map; size_t graph_size; @@ -258,18 +262,17 @@ struct commit_graph *load_commit_graph_one_fd_st(struct repository *r, graph_size = xsize_t(st->st_size); - if (graph_size < GRAPH_MIN_SIZE) { + if (graph_size < graph_min_size(source->odb->repo->hash_algo)) { close(fd); error(_("commit-graph file is too small")); return NULL; } graph_map = xmmap(NULL, graph_size, PROT_READ, MAP_PRIVATE, fd, 0); close(fd); - prepare_repo_settings(r); - ret = parse_commit_graph(&r->settings, graph_map, graph_size); + ret = parse_commit_graph(source->odb->repo, graph_map, graph_size); if (ret) - ret->odb = odb; + ret->odb_source = source; else munmap(graph_map, graph_size); @@ -305,7 +308,7 @@ static int graph_read_oid_lookup(const unsigned char *chunk_start, { struct commit_graph *g = data; g->chunk_oid_lookup = chunk_start; - if (chunk_size / g->hash_len != g->num_commits) + if (chunk_size / g->hash_algo->rawsz != g->num_commits) return error(_("commit-graph OID lookup chunk is the wrong size")); return 0; } @@ -314,7 +317,7 @@ static int graph_read_commit_data(const unsigned char *chunk_start, size_t chunk_size, void *data) { struct commit_graph *g = data; - if (chunk_size / GRAPH_DATA_WIDTH != g->num_commits) + if (chunk_size / graph_data_width(g->hash_algo) != g->num_commits) return error(_("commit-graph commit data chunk is wrong size")); g->chunk_commit_data = chunk_start; return 0; @@ -367,7 +370,7 @@ static int graph_read_bloom_data(const unsigned char *chunk_start, return 0; } -struct commit_graph *parse_commit_graph(struct repo_settings *s, +struct commit_graph *parse_commit_graph(struct repository *r, void *graph_map, size_t graph_size) { const unsigned char *data; @@ -379,7 +382,7 @@ struct commit_graph *parse_commit_graph(struct repo_settings *s, if (!graph_map) return NULL; - if (graph_size < GRAPH_MIN_SIZE) + if (graph_size < graph_min_size(r->hash_algo)) return NULL; data = (const unsigned char *)graph_map; @@ -399,22 +402,22 @@ struct commit_graph *parse_commit_graph(struct repo_settings *s, } hash_version = *(unsigned char*)(data + 5); - if (hash_version != oid_version(the_hash_algo)) { + if (hash_version != oid_version(r->hash_algo)) { error(_("commit-graph hash version %X does not match version %X"), - hash_version, oid_version(the_hash_algo)); + hash_version, oid_version(r->hash_algo)); return NULL; } graph = alloc_commit_graph(); - graph->hash_len = the_hash_algo->rawsz; + graph->hash_algo = r->hash_algo; graph->num_chunks = *(unsigned char*)(data + 6); graph->data = graph_map; graph->data_len = graph_size; if (graph_size < GRAPH_HEADER_SIZE + (graph->num_chunks + 1) * CHUNK_TOC_ENTRY_SIZE + - GRAPH_FANOUT_SIZE + the_hash_algo->rawsz) { + GRAPH_FANOUT_SIZE + r->hash_algo->rawsz) { error(_("commit-graph file is too small to hold %u chunks"), graph->num_chunks); free(graph); @@ -445,7 +448,9 @@ struct commit_graph *parse_commit_graph(struct repo_settings *s, pair_chunk(cf, GRAPH_CHUNKID_BASE, &graph->chunk_base_graphs, &graph->chunk_base_graphs_size); - if (s->commit_graph_generation_version >= 2) { + prepare_repo_settings(r); + + if (r->settings.commit_graph_generation_version >= 2) { read_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA, graph_read_generation_data, graph); pair_chunk(cf, GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW, @@ -456,7 +461,7 @@ struct commit_graph *parse_commit_graph(struct repo_settings *s, graph->read_generation_data = 1; } - if (s->commit_graph_changed_paths_version) { + if (r->settings.commit_graph_changed_paths_version) { read_chunk(cf, GRAPH_CHUNKID_BLOOMINDEXES, graph_read_bloom_index, graph); read_chunk(cf, GRAPH_CHUNKID_BLOOMDATA, @@ -472,8 +477,8 @@ struct commit_graph *parse_commit_graph(struct repo_settings *s, FREE_AND_NULL(graph->bloom_filter_settings); } - oidread(&graph->oid, graph->data + graph->data_len - graph->hash_len, - the_repository->hash_algo); + oidread(&graph->oid, graph->data + graph->data_len - graph->hash_algo->rawsz, + r->hash_algo); free_chunkfile(cf); return graph; @@ -485,11 +490,9 @@ free_and_return: return NULL; } -static struct commit_graph *load_commit_graph_one(struct repository *r, - const char *graph_file, - struct object_directory *odb) +static struct commit_graph *load_commit_graph_one(struct odb_source *source, + const char *graph_file) { - struct stat st; int fd; struct commit_graph *g; @@ -498,19 +501,17 @@ static struct commit_graph *load_commit_graph_one(struct repository *r, if (!open_ok) return NULL; - g = load_commit_graph_one_fd_st(r, fd, &st, odb); - + g = load_commit_graph_one_fd_st(source, fd, &st); if (g) g->filename = xstrdup(graph_file); return g; } -static struct commit_graph *load_commit_graph_v1(struct repository *r, - struct object_directory *odb) +static struct commit_graph *load_commit_graph_v1(struct odb_source *source) { - char *graph_name = get_commit_graph_filename(odb); - struct commit_graph *g = load_commit_graph_one(r, graph_name, odb); + char *graph_name = get_commit_graph_filename(source); + struct commit_graph *g = load_commit_graph_one(source, graph_name); free(graph_name); return g; @@ -578,7 +579,7 @@ static int add_graph_to_chain(struct commit_graph *g, return 0; } - if (g->chunk_base_graphs_size / g->hash_len < n) { + if (g->chunk_base_graphs_size / g->hash_algo->rawsz < n) { warning(_("commit-graph base graphs chunk is too small")); return 0; } @@ -588,8 +589,8 @@ static int add_graph_to_chain(struct commit_graph *g, if (!cur_g || !oideq(&oids[n], &cur_g->oid) || - !hasheq(oids[n].hash, g->chunk_base_graphs + st_mult(g->hash_len, n), - the_repository->hash_algo)) { + !hasheq(oids[n].hash, g->chunk_base_graphs + st_mult(g->hash_algo->rawsz, n), + g->hash_algo)) { warning(_("commit-graph chain does not match")); return 0; } @@ -613,7 +614,8 @@ static int add_graph_to_chain(struct commit_graph *g, } int open_commit_graph_chain(const char *chain_file, - int *fd, struct stat *st) + int *fd, struct stat *st, + const struct git_hash_algo *hash_algo) { *fd = git_open(chain_file); if (*fd < 0) @@ -622,7 +624,7 @@ int open_commit_graph_chain(const char *chain_file, close(*fd); return 0; } - if (st->st_size < the_hash_algo->hexsz) { + if (st->st_size < hash_algo->hexsz) { close(*fd); if (!st->st_size) { /* treat empty files the same as missing */ @@ -636,7 +638,7 @@ int open_commit_graph_chain(const char *chain_file, return 1; } -struct commit_graph *load_commit_graph_chain_fd_st(struct repository *r, +struct commit_graph *load_commit_graph_chain_fd_st(struct object_database *odb, int fd, struct stat *st, int *incomplete_chain) { @@ -646,18 +648,18 @@ struct commit_graph *load_commit_graph_chain_fd_st(struct repository *r, int i = 0, valid = 1, count; FILE *fp = xfdopen(fd, "r"); - count = st->st_size / (the_hash_algo->hexsz + 1); + count = st->st_size / (odb->repo->hash_algo->hexsz + 1); CALLOC_ARRAY(oids, count); - prepare_alt_odb(r); + odb_prepare_alternates(odb); for (i = 0; i < count; i++) { - struct object_directory *odb; + struct odb_source *source; if (strbuf_getline_lf(&line, fp) == EOF) break; - if (get_oid_hex(line.buf, &oids[i])) { + if (get_oid_hex_algop(line.buf, &oids[i], odb->repo->hash_algo)) { warning(_("invalid commit-graph chain: line '%s' not a hash"), line.buf); valid = 0; @@ -665,9 +667,9 @@ struct commit_graph *load_commit_graph_chain_fd_st(struct repository *r, } valid = 0; - for (odb = r->objects->odb; odb; odb = odb->next) { - char *graph_name = get_split_graph_filename(odb, line.buf); - struct commit_graph *g = load_commit_graph_one(r, graph_name, odb); + for (source = odb->sources; source; source = source->next) { + char *graph_name = get_split_graph_filename(source, line.buf); + struct commit_graph *g = load_commit_graph_one(source, graph_name); free(graph_name); @@ -700,54 +702,42 @@ struct commit_graph *load_commit_graph_chain_fd_st(struct repository *r, return graph_chain; } -static struct commit_graph *load_commit_graph_chain(struct repository *r, - struct object_directory *odb) +static struct commit_graph *load_commit_graph_chain(struct odb_source *source) { - char *chain_file = get_commit_graph_chain_filename(odb); + char *chain_file = get_commit_graph_chain_filename(source); struct stat st; int fd; struct commit_graph *g = NULL; - if (open_commit_graph_chain(chain_file, &fd, &st)) { + if (open_commit_graph_chain(chain_file, &fd, &st, source->odb->repo->hash_algo)) { int incomplete; /* ownership of fd is taken over by load function */ - g = load_commit_graph_chain_fd_st(r, fd, &st, &incomplete); + g = load_commit_graph_chain_fd_st(source->odb, fd, &st, &incomplete); } free(chain_file); return g; } -struct commit_graph *read_commit_graph_one(struct repository *r, - struct object_directory *odb) +struct commit_graph *read_commit_graph_one(struct odb_source *source) { - struct commit_graph *g = load_commit_graph_v1(r, odb); + struct commit_graph *g = load_commit_graph_v1(source); if (!g) - g = load_commit_graph_chain(r, odb); + g = load_commit_graph_chain(source); return g; } -static void prepare_commit_graph_one(struct repository *r, - struct object_directory *odb) -{ - - if (r->objects->commit_graph) - return; - - r->objects->commit_graph = read_commit_graph_one(r, odb); -} - /* * Return 1 if commit_graph is non-NULL, and 0 otherwise. * * On the first invocation, this function attempts to load the commit - * graph if the_repository is configured to have one. + * graph if the repository is configured to have one. */ static int prepare_commit_graph(struct repository *r) { - struct object_directory *odb; + struct odb_source *source; /* * Early return if there is no git dir or if the commit graph is @@ -778,11 +768,13 @@ static int prepare_commit_graph(struct repository *r) if (!commit_graph_compatible(r)) return 0; - prepare_alt_odb(r); - for (odb = r->objects->odb; - !r->objects->commit_graph && odb; - odb = odb->next) - prepare_commit_graph_one(r, odb); + odb_prepare_alternates(r->objects); + for (source = r->objects->sources; source; source = source->next) { + r->objects->commit_graph = read_commit_graph_one(source); + if (r->objects->commit_graph) + break; + } + return !!r->objects->commit_graph; } @@ -799,7 +791,7 @@ int generation_numbers_enabled(struct repository *r) return 0; first_generation = get_be32(g->chunk_commit_data + - g->hash_len + 8) >> 2; + g->hash_algo->rawsz + 8) >> 2; return !!first_generation; } @@ -820,7 +812,12 @@ int corrected_commit_dates_enabled(struct repository *r) struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r) { - struct commit_graph *g = r->objects->commit_graph; + struct commit_graph *g; + + if (!prepare_commit_graph(r)) + return NULL; + + g = r->objects->commit_graph; while (g) { if (g->bloom_filter_settings) return g->bloom_filter_settings; @@ -829,7 +826,7 @@ struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r) return NULL; } -void close_commit_graph(struct raw_object_store *o) +void close_commit_graph(struct object_database *o) { if (!o->commit_graph) return; @@ -843,7 +840,7 @@ void close_commit_graph(struct raw_object_store *o) static int bsearch_graph(struct commit_graph *g, const struct object_id *oid, uint32_t *pos) { return bsearch_hash(oid->hash, g->chunk_oid_fanout, - g->chunk_oid_lookup, g->hash_len, pos); + g->chunk_oid_lookup, g->hash_algo->rawsz, pos); } static void load_oid_from_graph(struct commit_graph *g, @@ -863,12 +860,11 @@ static void load_oid_from_graph(struct commit_graph *g, lex_index = pos - g->num_commits_in_base; - oidread(oid, g->chunk_oid_lookup + st_mult(g->hash_len, lex_index), - the_repository->hash_algo); + oidread(oid, g->chunk_oid_lookup + st_mult(g->hash_algo->rawsz, lex_index), + g->hash_algo); } -static struct commit_list **insert_parent_or_die(struct repository *r, - struct commit_graph *g, +static struct commit_list **insert_parent_or_die(struct commit_graph *g, uint32_t pos, struct commit_list **pptr) { @@ -879,7 +875,7 @@ static struct commit_list **insert_parent_or_die(struct repository *r, die("invalid parent position %"PRIu32, pos); load_oid_from_graph(g, pos, &oid); - c = lookup_commit(r, &oid); + c = lookup_commit(g->odb_source->odb->repo, &oid); if (!c) die(_("could not find commit %s"), oid_to_hex(&oid)); commit_graph_data_at(c)->graph_pos = pos; @@ -900,13 +896,13 @@ static void fill_commit_graph_info(struct commit *item, struct commit_graph *g, die(_("invalid commit position. commit-graph is likely corrupt")); lex_index = pos - g->num_commits_in_base; - commit_data = g->chunk_commit_data + st_mult(GRAPH_DATA_WIDTH, lex_index); + commit_data = g->chunk_commit_data + st_mult(graph_data_width(g->hash_algo), lex_index); graph_data = commit_graph_data_at(item); graph_data->graph_pos = pos; - date_high = get_be32(commit_data + g->hash_len + 8) & 0x3; - date_low = get_be32(commit_data + g->hash_len + 12); + date_high = get_be32(commit_data + g->hash_algo->rawsz + 8) & 0x3; + date_low = get_be32(commit_data + g->hash_algo->rawsz + 12); item->date = (timestamp_t)((date_high << 32) | date_low); if (g->read_generation_data) { @@ -924,10 +920,10 @@ static void fill_commit_graph_info(struct commit *item, struct commit_graph *g, } else graph_data->generation = item->date + offset; } else - graph_data->generation = get_be32(commit_data + g->hash_len + 8) >> 2; + graph_data->generation = get_be32(commit_data + g->hash_algo->rawsz + 8) >> 2; if (g->topo_levels) - *topo_level_slab_at(g->topo_levels, item) = get_be32(commit_data + g->hash_len + 8) >> 2; + *topo_level_slab_at(g->topo_levels, item) = get_be32(commit_data + g->hash_algo->rawsz + 8) >> 2; } static inline void set_commit_tree(struct commit *c, struct tree *t) @@ -935,8 +931,7 @@ static inline void set_commit_tree(struct commit *c, struct tree *t) c->maybe_tree = t; } -static int fill_commit_in_graph(struct repository *r, - struct commit *item, +static int fill_commit_in_graph(struct commit *item, struct commit_graph *g, uint32_t pos) { uint32_t edge_value; @@ -951,7 +946,7 @@ static int fill_commit_in_graph(struct repository *r, fill_commit_graph_info(item, g, pos); lex_index = pos - g->num_commits_in_base; - commit_data = g->chunk_commit_data + st_mult(g->hash_len + 16, lex_index); + commit_data = g->chunk_commit_data + st_mult(g->hash_algo->rawsz + 16, lex_index); item->object.parsed = 1; @@ -959,16 +954,16 @@ static int fill_commit_in_graph(struct repository *r, pptr = &item->parents; - edge_value = get_be32(commit_data + g->hash_len); + edge_value = get_be32(commit_data + g->hash_algo->rawsz); if (edge_value == GRAPH_PARENT_NONE) return 1; - pptr = insert_parent_or_die(r, g, edge_value, pptr); + pptr = insert_parent_or_die(g, edge_value, pptr); - edge_value = get_be32(commit_data + g->hash_len + 4); + edge_value = get_be32(commit_data + g->hash_algo->rawsz + 4); if (edge_value == GRAPH_PARENT_NONE) return 1; if (!(edge_value & GRAPH_EXTRA_EDGES_NEEDED)) { - pptr = insert_parent_or_die(r, g, edge_value, pptr); + pptr = insert_parent_or_die(g, edge_value, pptr); return 1; } @@ -983,7 +978,7 @@ static int fill_commit_in_graph(struct repository *r, } edge_value = get_be32(g->chunk_extra_edges + sizeof(uint32_t) * parent_data_pos); - pptr = insert_parent_or_die(r, g, + pptr = insert_parent_or_die(g, edge_value & GRAPH_EDGE_LAST_MASK, pptr); parent_data_pos++; @@ -1040,7 +1035,7 @@ struct commit *lookup_commit_in_graph(struct repository *repo, const struct obje return NULL; if (!search_commit_pos_in_graph(id, repo->objects->commit_graph, &pos)) return NULL; - if (commit_graph_paranoia && !has_object(repo, id, 0)) + if (commit_graph_paranoia && !odb_has_object(repo->objects, id, 0)) return NULL; commit = lookup_commit(repo, id); @@ -1049,14 +1044,13 @@ struct commit *lookup_commit_in_graph(struct repository *repo, const struct obje if (commit->object.parsed) return commit; - if (!fill_commit_in_graph(repo, commit, repo->objects->commit_graph, pos)) + if (!fill_commit_in_graph(commit, repo->objects->commit_graph, pos)) return NULL; return commit; } -static int parse_commit_in_graph_one(struct repository *r, - struct commit_graph *g, +static int parse_commit_in_graph_one(struct commit_graph *g, struct commit *item) { uint32_t pos; @@ -1065,7 +1059,7 @@ static int parse_commit_in_graph_one(struct repository *r, return 1; if (find_commit_pos_in_graph(item, g, &pos)) - return fill_commit_in_graph(r, item, g, pos); + return fill_commit_in_graph(item, g, pos); return 0; } @@ -1082,7 +1076,7 @@ int parse_commit_in_graph(struct repository *r, struct commit *item) if (!prepare_commit_graph(r)) return 0; - return parse_commit_in_graph_one(r, r->objects->commit_graph, item); + return parse_commit_in_graph_one(r->objects->commit_graph, item); } void load_commit_graph_info(struct repository *r, struct commit *item) @@ -1092,8 +1086,7 @@ void load_commit_graph_info(struct repository *r, struct commit *item) fill_commit_graph_info(item, r->objects->commit_graph, pos); } -static struct tree *load_tree_for_commit(struct repository *r, - struct commit_graph *g, +static struct tree *load_tree_for_commit(struct commit_graph *g, struct commit *c) { struct object_id oid; @@ -1104,16 +1097,16 @@ static struct tree *load_tree_for_commit(struct repository *r, g = g->base_graph; commit_data = g->chunk_commit_data + - st_mult(GRAPH_DATA_WIDTH, graph_pos - g->num_commits_in_base); + st_mult(graph_data_width(g->hash_algo), + graph_pos - g->num_commits_in_base); - oidread(&oid, commit_data, the_repository->hash_algo); - set_commit_tree(c, lookup_tree(r, &oid)); + oidread(&oid, commit_data, g->hash_algo); + set_commit_tree(c, lookup_tree(g->odb_source->odb->repo, &oid)); return c->maybe_tree; } -static struct tree *get_commit_tree_in_graph_one(struct repository *r, - struct commit_graph *g, +static struct tree *get_commit_tree_in_graph_one(struct commit_graph *g, const struct commit *c) { if (c->maybe_tree) @@ -1121,12 +1114,12 @@ static struct tree *get_commit_tree_in_graph_one(struct repository *r, if (commit_graph_position(c) == COMMIT_NOT_FROM_GRAPH) BUG("get_commit_tree_in_graph_one called from non-commit-graph commit"); - return load_tree_for_commit(r, g, (struct commit *)c); + return load_tree_for_commit(g, (struct commit *)c); } struct tree *get_commit_tree_in_graph(struct repository *r, const struct commit *c) { - return get_commit_tree_in_graph_one(r, r->objects->commit_graph, c); + return get_commit_tree_in_graph_one(r->objects->commit_graph, c); } struct packed_commit_list { @@ -1137,7 +1130,7 @@ struct packed_commit_list { struct write_commit_graph_context { struct repository *r; - struct object_directory *odb; + struct odb_source *odb_source; char *graph_name; struct oid_array oids; struct packed_commit_list commits; @@ -1212,7 +1205,7 @@ static int write_graph_chunk_oids(struct hashfile *f, int count; for (count = 0; count < ctx->commits.nr; count++, list++) { display_progress(ctx->progress, ++ctx->progress_cnt); - hashwrite(f, (*list)->object.oid.hash, the_hash_algo->rawsz); + hashwrite(f, (*list)->object.oid.hash, f->algop->rawsz); } return 0; @@ -1243,7 +1236,7 @@ static int write_graph_chunk_data(struct hashfile *f, die(_("unable to parse commit %s"), oid_to_hex(&(*list)->object.oid)); tree = get_commit_tree_oid(*list); - hashwrite(f, tree->hash, the_hash_algo->rawsz); + hashwrite(f, tree->hash, ctx->r->hash_algo->rawsz); parent = (*list)->parents; @@ -1533,7 +1526,7 @@ static void close_reachable(struct write_commit_graph_context *ctx) if (ctx->report_progress) ctx->progress = start_delayed_progress( - the_repository, + ctx->r, _("Loading known commits in commit graph"), ctx->oids.nr); for (i = 0; i < ctx->oids.nr; i++) { @@ -1551,7 +1544,7 @@ static void close_reachable(struct write_commit_graph_context *ctx) */ if (ctx->report_progress) ctx->progress = start_delayed_progress( - the_repository, + ctx->r, _("Expanding reachable commits in commit graph"), 0); for (i = 0; i < ctx->oids.nr; i++) { @@ -1572,7 +1565,7 @@ static void close_reachable(struct write_commit_graph_context *ctx) if (ctx->report_progress) ctx->progress = start_delayed_progress( - the_repository, + ctx->r, _("Clearing commit marks in commit graph"), ctx->oids.nr); for (i = 0; i < ctx->oids.nr; i++) { @@ -1690,7 +1683,7 @@ static void compute_topological_levels(struct write_commit_graph_context *ctx) if (ctx->report_progress) info.progress = ctx->progress = start_delayed_progress( - the_repository, + ctx->r, _("Computing commit graph topological levels"), ctx->commits.nr); @@ -1725,7 +1718,7 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx) if (ctx->report_progress) info.progress = ctx->progress = start_delayed_progress( - the_repository, + ctx->r, _("Computing commit graph generation numbers"), ctx->commits.nr); @@ -1802,7 +1795,7 @@ static void compute_bloom_filters(struct write_commit_graph_context *ctx) if (ctx->report_progress) progress = start_delayed_progress( - the_repository, + ctx->r, _("Computing commit changed paths Bloom filters"), ctx->commits.nr); @@ -1848,6 +1841,7 @@ static void compute_bloom_filters(struct write_commit_graph_context *ctx) } struct refs_cb_data { + struct repository *repo; struct oidset *commits; struct progress *progress; }; @@ -1860,9 +1854,9 @@ static int add_ref_to_set(const char *refname UNUSED, struct object_id peeled; struct refs_cb_data *data = (struct refs_cb_data *)cb_data; - if (!peel_iterated_oid(the_repository, oid, &peeled)) + if (!peel_iterated_oid(data->repo, oid, &peeled)) oid = &peeled; - if (oid_object_info(the_repository, oid, NULL) == OBJ_COMMIT) + if (odb_read_object_info(data->repo->objects, oid, NULL) == OBJ_COMMIT) oidset_insert(data->commits, oid); display_progress(data->progress, oidset_size(data->commits)); @@ -1870,7 +1864,7 @@ static int add_ref_to_set(const char *refname UNUSED, return 0; } -int write_commit_graph_reachable(struct object_directory *odb, +int write_commit_graph_reachable(struct odb_source *source, enum commit_graph_write_flags flags, const struct commit_graph_opts *opts) { @@ -1879,18 +1873,20 @@ int write_commit_graph_reachable(struct object_directory *odb, int result; memset(&data, 0, sizeof(data)); + data.repo = source->odb->repo; data.commits = &commits; + if (flags & COMMIT_GRAPH_WRITE_PROGRESS) data.progress = start_delayed_progress( - the_repository, + source->odb->repo, _("Collecting referenced commits"), 0); - refs_for_each_ref(get_main_ref_store(the_repository), add_ref_to_set, + refs_for_each_ref(get_main_ref_store(source->odb->repo), add_ref_to_set, &data); stop_progress(&data.progress); - result = write_commit_graph(odb, NULL, &commits, + result = write_commit_graph(source, NULL, &commits, flags, opts); oidset_clear(&commits); @@ -1906,7 +1902,7 @@ static int fill_oids_from_packs(struct write_commit_graph_context *ctx, int dirlen; int ret = 0; - strbuf_addf(&packname, "%s/pack/", ctx->odb->path); + strbuf_addf(&packname, "%s/pack/", ctx->odb_source->path); dirlen = packname.len; if (ctx->report_progress) { strbuf_addf(&progress_title, @@ -1914,7 +1910,7 @@ static int fill_oids_from_packs(struct write_commit_graph_context *ctx, "Finding commits for commit graph in %"PRIuMAX" packs", pack_indexes->nr), (uintmax_t)pack_indexes->nr); - ctx->progress = start_delayed_progress(the_repository, + ctx->progress = start_delayed_progress(ctx->r, progress_title.buf, 0); ctx->progress_done = 0; } @@ -1968,7 +1964,7 @@ static void fill_oids_from_all_packs(struct write_commit_graph_context *ctx) { if (ctx->report_progress) ctx->progress = start_delayed_progress( - the_repository, + ctx->r, _("Finding commits for commit graph among packed objects"), ctx->approx_nr_objects); for_each_packed_object(ctx->r, add_packed_commits, ctx, @@ -1987,7 +1983,7 @@ static void copy_oids_to_commits(struct write_commit_graph_context *ctx) ctx->num_extra_edges = 0; if (ctx->report_progress) ctx->progress = start_delayed_progress( - the_repository, + ctx->r, _("Finding extra edges in commit graph"), ctx->oids.nr); oid_array_sort(&ctx->oids); @@ -2026,7 +2022,7 @@ static int write_graph_chunk_base_1(struct hashfile *f, return 0; num = write_graph_chunk_base_1(f, g->base_graph); - hashwrite(f, g->oid.hash, the_hash_algo->rawsz); + hashwrite(f, g->oid.hash, g->hash_algo->rawsz); return num + 1; } @@ -2050,7 +2046,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx) struct hashfile *f; struct tempfile *graph_layer; /* when ctx->split is non-zero */ struct lock_file lk = LOCK_INIT; - const unsigned hashsz = the_hash_algo->rawsz; + const unsigned hashsz = ctx->r->hash_algo->rawsz; struct strbuf progress_title = STRBUF_INIT; struct chunkfile *cf; unsigned char file_hash[GIT_MAX_RAWSZ]; @@ -2060,20 +2056,20 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx) strbuf_addf(&tmp_file, "%s/info/commit-graphs/tmp_graph_XXXXXX", - ctx->odb->path); + ctx->odb_source->path); ctx->graph_name = strbuf_detach(&tmp_file, NULL); } else { - ctx->graph_name = get_commit_graph_filename(ctx->odb); + ctx->graph_name = get_commit_graph_filename(ctx->odb_source); } - if (safe_create_leading_directories(the_repository, ctx->graph_name)) { + if (safe_create_leading_directories(ctx->r, ctx->graph_name)) { error(_("unable to create leading directories of %s"), ctx->graph_name); return -1; } if (ctx->split) { - char *lock_name = get_commit_graph_chain_filename(ctx->odb); + char *lock_name = get_commit_graph_chain_filename(ctx->odb_source); hold_lock_file_for_update_mode(&lk, lock_name, LOCK_DIE_ON_ERROR, 0444); @@ -2085,18 +2081,18 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx) return -1; } - if (adjust_shared_perm(the_repository, get_tempfile_path(graph_layer))) { + if (adjust_shared_perm(ctx->r, get_tempfile_path(graph_layer))) { error(_("unable to adjust shared permissions for '%s'"), get_tempfile_path(graph_layer)); return -1; } - f = hashfd(the_repository->hash_algo, + f = hashfd(ctx->r->hash_algo, get_tempfile_fd(graph_layer), get_tempfile_path(graph_layer)); } else { hold_lock_file_for_update_mode(&lk, ctx->graph_name, LOCK_DIE_ON_ERROR, 0444); - f = hashfd(the_repository->hash_algo, + f = hashfd(ctx->r->hash_algo, get_lock_file_fd(&lk), get_lock_file_path(&lk)); } @@ -2138,7 +2134,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx) hashwrite_be32(f, GRAPH_SIGNATURE); hashwrite_u8(f, GRAPH_VERSION); - hashwrite_u8(f, oid_version(the_hash_algo)); + hashwrite_u8(f, oid_version(ctx->r->hash_algo)); hashwrite_u8(f, get_num_chunks(cf)); hashwrite_u8(f, ctx->num_commit_graphs_after - 1); @@ -2149,7 +2145,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx) get_num_chunks(cf)), get_num_chunks(cf)); ctx->progress = start_delayed_progress( - the_repository, + ctx->r, progress_title.buf, st_mult(get_num_chunks(cf), ctx->commits.nr)); } @@ -2161,7 +2157,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx) if (ctx->split && ctx->base_graph_name && ctx->num_commit_graphs_after > 1) { char *new_base_hash = xstrdup(oid_to_hex(&ctx->new_base_graph->oid)); - char *new_base_name = get_split_graph_filename(ctx->new_base_graph->odb, new_base_hash); + char *new_base_name = get_split_graph_filename(ctx->new_base_graph->odb_source, new_base_hash); free(ctx->commit_graph_filenames_after[ctx->num_commit_graphs_after - 2]); free(ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 2]); @@ -2201,14 +2197,15 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx) } } } else { - char *graph_name = get_commit_graph_filename(ctx->odb); + char *graph_name = get_commit_graph_filename(ctx->odb_source); unlink(graph_name); free(graph_name); } free(ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1]); - ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1] = xstrdup(hash_to_hex(file_hash)); - final_graph_name = get_split_graph_filename(ctx->odb, + ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1] = + xstrdup(hash_to_hex_algop(file_hash, ctx->r->hash_algo)); + final_graph_name = get_split_graph_filename(ctx->odb_source, ctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1]); free(ctx->commit_graph_filenames_after[ctx->num_commit_graphs_after - 1]); ctx->commit_graph_filenames_after[ctx->num_commit_graphs_after - 1] = final_graph_name; @@ -2259,7 +2256,7 @@ static void split_graph_merge_strategy(struct write_commit_graph_context *ctx) flags != COMMIT_GRAPH_SPLIT_REPLACE) { while (g && (g->num_commits <= st_mult(size_mult, num_commits) || (max_commits && num_commits > max_commits))) { - if (g->odb != ctx->odb) + if (g->odb_source != ctx->odb_source) break; if (unsigned_add_overflows(num_commits, g->num_commits)) @@ -2281,10 +2278,10 @@ static void split_graph_merge_strategy(struct write_commit_graph_context *ctx) "should be 1 with --split=replace"); if (ctx->num_commit_graphs_after == 2) { - char *old_graph_name = get_commit_graph_filename(g->odb); + char *old_graph_name = get_commit_graph_filename(g->odb_source); if (!strcmp(g->filename, old_graph_name) && - g->odb != ctx->odb) { + g->odb_source != ctx->odb_source) { ctx->num_commit_graphs_after = 1; ctx->new_base_graph = NULL; } @@ -2362,7 +2359,7 @@ static void sort_and_scan_merged_commits(struct write_commit_graph_context *ctx) if (ctx->report_progress) ctx->progress = start_delayed_progress( - the_repository, + ctx->r, _("Scanning merged commits"), ctx->commits.nr); @@ -2407,7 +2404,7 @@ static void merge_commit_graphs(struct write_commit_graph_context *ctx) current_graph_number--; if (ctx->report_progress) - ctx->progress = start_delayed_progress(the_repository, + ctx->progress = start_delayed_progress(ctx->r, _("Merging commit-graph"), 0); merge_commit_graph(ctx, g); @@ -2456,13 +2453,13 @@ static void expire_commit_graphs(struct write_commit_graph_context *ctx) if (ctx->opts && ctx->opts->expire_time) expire_time = ctx->opts->expire_time; if (!ctx->split) { - char *chain_file_name = get_commit_graph_chain_filename(ctx->odb); + char *chain_file_name = get_commit_graph_chain_filename(ctx->odb_source); unlink(chain_file_name); free(chain_file_name); ctx->num_commit_graphs_after = 0; } - strbuf_addstr(&path, ctx->odb->path); + strbuf_addstr(&path, ctx->odb_source->path); strbuf_addstr(&path, "/info/commit-graphs"); dir = opendir(path.buf); @@ -2504,16 +2501,16 @@ out: strbuf_release(&path); } -int write_commit_graph(struct object_directory *odb, +int write_commit_graph(struct odb_source *source, const struct string_list *const pack_indexes, struct oidset *commits, enum commit_graph_write_flags flags, const struct commit_graph_opts *opts) { - struct repository *r = the_repository; + struct repository *r = source->odb->repo; struct write_commit_graph_context ctx = { .r = r, - .odb = odb, + .odb_source = source, .append = flags & COMMIT_GRAPH_WRITE_APPEND ? 1 : 0, .report_progress = flags & COMMIT_GRAPH_WRITE_PROGRESS ? 1 : 0, .split = flags & COMMIT_GRAPH_WRITE_SPLIT ? 1 : 0, @@ -2610,14 +2607,14 @@ int write_commit_graph(struct object_directory *odb, replace = ctx.opts->split_flags & COMMIT_GRAPH_SPLIT_REPLACE; } - ctx.approx_nr_objects = repo_approximate_object_count(the_repository); + ctx.approx_nr_objects = repo_approximate_object_count(r); if (ctx.append && ctx.r->objects->commit_graph) { struct commit_graph *g = ctx.r->objects->commit_graph; for (i = 0; i < g->num_commits; i++) { struct object_id oid; - oidread(&oid, g->chunk_oid_lookup + st_mult(g->hash_len, i), - the_repository->hash_algo); + oidread(&oid, g->chunk_oid_lookup + st_mult(g->hash_algo->rawsz, i), + r->hash_algo); oid_array_append(&ctx.oids, &oid); } } @@ -2725,15 +2722,15 @@ static void graph_report(const char *fmt, ...) static int commit_graph_checksum_valid(struct commit_graph *g) { - return hashfile_checksum_valid(the_repository->hash_algo, + return hashfile_checksum_valid(g->hash_algo, g->data, g->data_len); } -static int verify_one_commit_graph(struct repository *r, - struct commit_graph *g, +static int verify_one_commit_graph(struct commit_graph *g, struct progress *progress, uint64_t *seen) { + struct repository *r = g->odb_source->odb->repo; uint32_t i, cur_fanout_pos = 0; struct object_id prev_oid, cur_oid; struct commit *seen_gen_zero = NULL; @@ -2747,8 +2744,8 @@ static int verify_one_commit_graph(struct repository *r, for (i = 0; i < g->num_commits; i++) { struct commit *graph_commit; - oidread(&cur_oid, g->chunk_oid_lookup + st_mult(g->hash_len, i), - the_repository->hash_algo); + oidread(&cur_oid, g->chunk_oid_lookup + st_mult(g->hash_algo->rawsz, i), + g->hash_algo); if (i && oidcmp(&prev_oid, &cur_oid) >= 0) graph_report(_("commit-graph has incorrect OID order: %s then %s"), @@ -2767,7 +2764,7 @@ static int verify_one_commit_graph(struct repository *r, } graph_commit = lookup_commit(r, &cur_oid); - if (!parse_commit_in_graph_one(r, g, graph_commit)) + if (!parse_commit_in_graph_one(g, graph_commit)) graph_report(_("failed to parse commit %s from commit-graph"), oid_to_hex(&cur_oid)); } @@ -2792,8 +2789,8 @@ static int verify_one_commit_graph(struct repository *r, timestamp_t generation; display_progress(progress, ++(*seen)); - oidread(&cur_oid, g->chunk_oid_lookup + st_mult(g->hash_len, i), - the_repository->hash_algo); + oidread(&cur_oid, g->chunk_oid_lookup + st_mult(g->hash_algo->rawsz, i), + g->hash_algo); graph_commit = lookup_commit(r, &cur_oid); odb_commit = (struct commit *)create_object(r, &cur_oid, alloc_commit_node(r)); @@ -2803,7 +2800,7 @@ static int verify_one_commit_graph(struct repository *r, continue; } - if (!oideq(&get_commit_tree_in_graph_one(r, g, graph_commit)->object.oid, + if (!oideq(&get_commit_tree_in_graph_one(g, graph_commit)->object.oid, get_commit_tree_oid(odb_commit))) graph_report(_("root tree OID for commit %s in commit-graph is %s != %s"), oid_to_hex(&cur_oid), @@ -2821,7 +2818,7 @@ static int verify_one_commit_graph(struct repository *r, } /* parse parent in case it is in a base graph */ - parse_commit_in_graph_one(r, g, graph_parents->item); + parse_commit_in_graph_one(g, graph_parents->item); if (!oideq(&graph_parents->item->object.oid, &odb_parents->item->object.oid)) graph_report(_("commit-graph parent for %s is %s != %s"), @@ -2881,7 +2878,7 @@ static int verify_one_commit_graph(struct repository *r, return verify_commit_graph_error; } -int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags) +int verify_commit_graph(struct commit_graph *g, int flags) { struct progress *progress = NULL; int local_error = 0; @@ -2897,13 +2894,13 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags) if (!(flags & COMMIT_GRAPH_VERIFY_SHALLOW)) total += g->num_commits_in_base; - progress = start_progress(the_repository, + progress = start_progress(g->odb_source->odb->repo, _("Verifying commits in commit graph"), total); } for (; g; g = g->base_graph) { - local_error |= verify_one_commit_graph(r, g, progress, &seen); + local_error |= verify_one_commit_graph(g, progress, &seen); if (flags & COMMIT_GRAPH_VERIFY_SHALLOW) break; } diff --git a/commit-graph.h b/commit-graph.h index 13f662827d..4899b54ef8 100644 --- a/commit-graph.h +++ b/commit-graph.h @@ -1,7 +1,7 @@ #ifndef COMMIT_GRAPH_H #define COMMIT_GRAPH_H -#include "object-store.h" +#include "odb.h" #include "oidset.h" #define GIT_TEST_COMMIT_GRAPH "GIT_TEST_COMMIT_GRAPH" @@ -21,18 +21,19 @@ * call this method oustide of a builtin, and only if you know what * you are doing! */ -void git_test_write_commit_graph_or_die(void); +void git_test_write_commit_graph_or_die(struct odb_source *source); struct commit; struct bloom_filter_settings; struct repository; -struct raw_object_store; +struct object_database; struct string_list; -char *get_commit_graph_filename(struct object_directory *odb); -char *get_commit_graph_chain_filename(struct object_directory *odb); +char *get_commit_graph_filename(struct odb_source *source); +char *get_commit_graph_chain_filename(struct odb_source *source); int open_commit_graph(const char *graph_file, int *fd, struct stat *st); -int open_commit_graph_chain(const char *chain_file, int *fd, struct stat *st); +int open_commit_graph_chain(const char *chain_file, int *fd, struct stat *st, + const struct git_hash_algo *hash_algo); /* * Given a commit struct, try to fill the commit struct info, including: @@ -84,12 +85,12 @@ struct commit_graph { const unsigned char *data; size_t data_len; - unsigned char hash_len; + const struct git_hash_algo *hash_algo; unsigned char num_chunks; uint32_t num_commits; struct object_id oid; char *filename; - struct object_directory *odb; + struct odb_source *odb_source; uint32_t num_commits_in_base; unsigned int read_generation_data; @@ -113,14 +114,12 @@ struct commit_graph { struct bloom_filter_settings *bloom_filter_settings; }; -struct commit_graph *load_commit_graph_one_fd_st(struct repository *r, - int fd, struct stat *st, - struct object_directory *odb); -struct commit_graph *load_commit_graph_chain_fd_st(struct repository *r, +struct commit_graph *load_commit_graph_one_fd_st(struct odb_source *source, + int fd, struct stat *st); +struct commit_graph *load_commit_graph_chain_fd_st(struct object_database *odb, int fd, struct stat *st, int *incomplete_chain); -struct commit_graph *read_commit_graph_one(struct repository *r, - struct object_directory *odb); +struct commit_graph *read_commit_graph_one(struct odb_source *source); struct repo_settings; @@ -128,7 +127,7 @@ struct repo_settings; * Callers should initialize the repo_settings with prepare_repo_settings() * prior to calling parse_commit_graph(). */ -struct commit_graph *parse_commit_graph(struct repo_settings *s, +struct commit_graph *parse_commit_graph(struct repository *r, void *graph_map, size_t graph_size); /* @@ -173,10 +172,10 @@ struct commit_graph_opts { * is not compatible with the commit-graph feature, then the * methods will return 0 without writing a commit-graph. */ -int write_commit_graph_reachable(struct object_directory *odb, +int write_commit_graph_reachable(struct odb_source *source, enum commit_graph_write_flags flags, const struct commit_graph_opts *opts); -int write_commit_graph(struct object_directory *odb, +int write_commit_graph(struct odb_source *source, const struct string_list *pack_indexes, struct oidset *commits, enum commit_graph_write_flags flags, @@ -184,9 +183,9 @@ int write_commit_graph(struct object_directory *odb, #define COMMIT_GRAPH_VERIFY_SHALLOW (1 << 0) -int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags); +int verify_commit_graph(struct commit_graph *g, int flags); -void close_commit_graph(struct raw_object_store *); +void close_commit_graph(struct object_database *); void free_commit_graph(struct commit_graph *); /* @@ -9,7 +9,7 @@ #include "hex.h" #include "repository.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "utf8.h" #include "diff.h" #include "revision.h" @@ -31,6 +31,7 @@ #include "parse.h" #include "object-file.h" #include "object-file-convert.h" +#include "prio-queue.h" static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **); @@ -374,7 +375,7 @@ const void *repo_get_commit_buffer(struct repository *r, if (!ret) { enum object_type type; unsigned long size; - ret = repo_read_object_file(r, &commit->object.oid, &type, &size); + ret = odb_read_object(r->objects, &commit->object.oid, &type, &size); if (!ret) die("cannot read commit object %s", oid_to_hex(&commit->object.oid)); @@ -575,7 +576,7 @@ int repo_parse_commit_internal(struct repository *r, if (commit_graph_paranoia == -1) commit_graph_paranoia = git_env_bool(GIT_COMMIT_GRAPH_PARANOIA, 0); - if (commit_graph_paranoia && !has_object(r, &item->object.oid, 0)) { + if (commit_graph_paranoia && !odb_has_object(r->objects, &item->object.oid, 0)) { unparse_commit(r, &item->object.oid); return quiet_on_missing ? -1 : error(_("commit %s exists in commit-graph but not in the object database"), @@ -585,7 +586,8 @@ int repo_parse_commit_internal(struct repository *r, return 0; } - if (oid_object_info_extended(r, &item->object.oid, &oi, flags) < 0) + if (odb_read_object_info_extended(r->objects, &item->object.oid, + &oi, flags) < 0) return quiet_on_missing ? -1 : error("Could not read %s", oid_to_hex(&item->object.oid)); @@ -738,20 +740,27 @@ void commit_list_sort_by_date(struct commit_list **list) commit_list_sort(list, commit_list_compare_by_date); } -struct commit *pop_most_recent_commit(struct commit_list **list, +struct commit *pop_most_recent_commit(struct prio_queue *queue, unsigned int mark) { - struct commit *ret = pop_commit(list); + struct commit *ret = prio_queue_peek(queue); + int get_pending = 1; struct commit_list *parents = ret->parents; while (parents) { struct commit *commit = parents->item; if (!repo_parse_commit(the_repository, commit) && !(commit->object.flags & mark)) { commit->object.flags |= mark; - commit_list_insert_by_date(commit, list); + if (get_pending) + prio_queue_replace(queue, commit); + else + prio_queue_put(queue, commit); + get_pending = 0; } parents = parents->next; } + if (get_pending) + prio_queue_get(queue); return ret; } @@ -1030,7 +1039,8 @@ static void add_one_commit(struct object_id *oid, struct rev_collect *revs) commit->object.flags |= TMP_MARK; } -static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid, +static int collect_one_reflog_ent(const char *refname UNUSED, + struct object_id *ooid, struct object_id *noid, const char *ident UNUSED, timestamp_t timestamp UNUSED, int tz UNUSED, const char *message UNUSED, void *cbdata) @@ -1274,8 +1284,8 @@ static void handle_signed_tag(const struct commit *parent, struct commit_extra_h desc = merge_remote_util(parent); if (!desc || !desc->obj) return; - buf = repo_read_object_file(the_repository, &desc->obj->oid, &type, - &size); + buf = odb_read_object(the_repository->objects, &desc->obj->oid, + &type, &size); if (!buf || type != OBJ_TAG) goto free_return; if (!parse_signature(buf, size, &payload, &signature)) @@ -1706,7 +1716,7 @@ int commit_tree_extended(const char *msg, size_t msg_len, /* Not having i18n.commitencoding is the same as having utf-8 */ encoding_is_utf8 = is_encoding_utf8(git_commit_encoding); - assert_oid_type(tree, OBJ_TREE); + odb_assert_oid_type(the_repository->objects, tree, OBJ_TREE); if (memchr(msg, '\0', msg_len)) return error("a NUL byte in commit log message not allowed."); @@ -1796,8 +1806,8 @@ int commit_tree_extended(const char *msg, size_t msg_len, compat_oid = &compat_oid_buf; } - result = write_object_file_flags(buffer.buf, buffer.len, OBJ_COMMIT, - ret, compat_oid, 0); + result = odb_write_object_ext(the_repository->objects, buffer.buf, buffer.len, + OBJ_COMMIT, ret, compat_oid, 0); out: free(parent_buf); strbuf_release(&buffer); @@ -2,6 +2,7 @@ #define COMMIT_H #include "object.h" +#include "add-interactive.h" struct signature_check; struct strbuf; @@ -201,10 +202,10 @@ const char *repo_logmsg_reencode(struct repository *r, const char *skip_blank_lines(const char *msg); -/** Removes the first commit from a list sorted by date, and adds all - * of its parents. - **/ -struct commit *pop_most_recent_commit(struct commit_list **list, +struct prio_queue; + +/* Removes the first commit from a prio_queue and adds its parents. */ +struct commit *pop_most_recent_commit(struct prio_queue *queue, unsigned int mark); struct commit *pop_commit(struct commit_list **stack); @@ -257,7 +258,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); + int patch, struct add_p_opt *add_p_opt); struct commit_extra_header { struct commit_extra_header *next; diff --git a/compat/bswap.h b/compat/bswap.h index b34054f2bd..28635ebc69 100644 --- a/compat/bswap.h +++ b/compat/bswap.h @@ -32,78 +32,35 @@ static inline uint64_t default_bswap64(uint64_t val) ((val & (uint64_t)0xff00000000000000ULL) >> 56)); } +/* + * __has_builtin is available since Clang 10 and GCC 10. + * Below is a fallback for older compilers. + */ +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + #undef bswap32 #undef bswap64 -#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) - -#define bswap32 git_bswap32 -static inline uint32_t git_bswap32(uint32_t x) -{ - uint32_t result; - if (__builtin_constant_p(x)) - result = default_swab32(x); - else - __asm__("bswap %0" : "=r" (result) : "0" (x)); - return result; -} - -#define bswap64 git_bswap64 -#if defined(__x86_64__) -static inline uint64_t git_bswap64(uint64_t x) -{ - uint64_t result; - if (__builtin_constant_p(x)) - result = default_bswap64(x); - else - __asm__("bswap %q0" : "=r" (result) : "0" (x)); - return result; -} -#else -static inline uint64_t git_bswap64(uint64_t x) -{ - union { uint64_t i64; uint32_t i32[2]; } tmp, result; - if (__builtin_constant_p(x)) - result.i64 = default_bswap64(x); - else { - tmp.i64 = x; - result.i32[0] = git_bswap32(tmp.i32[1]); - result.i32[1] = git_bswap32(tmp.i32[0]); - } - return result.i64; -} -#endif - -#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM64)) +#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM64)) #include <stdlib.h> #define bswap32(x) _byteswap_ulong(x) #define bswap64(x) _byteswap_uint64(x) -#endif +#define GIT_LITTLE_ENDIAN 1234 +#define GIT_BIG_ENDIAN 4321 +#define GIT_BYTE_ORDER GIT_LITTLE_ENDIAN -#if defined(bswap32) +#elif __has_builtin(__builtin_bswap32) && __has_builtin(__builtin_bswap64) -#undef ntohl -#undef htonl -#define ntohl(x) bswap32(x) -#define htonl(x) bswap32(x) +#define bswap32(x) __builtin_bswap32((x)) +#define bswap64(x) __builtin_bswap64((x)) #endif -#if defined(bswap64) - -#undef ntohll -#undef htonll -#define ntohll(x) bswap64(x) -#define htonll(x) bswap64(x) - -#else - -#undef ntohll -#undef htonll - #if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && defined(__BIG_ENDIAN) # define GIT_BYTE_ORDER __BYTE_ORDER @@ -116,7 +73,13 @@ static inline uint64_t git_bswap64(uint64_t x) # define GIT_LITTLE_ENDIAN LITTLE_ENDIAN # define GIT_BIG_ENDIAN BIG_ENDIAN -#else +#elif defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) + +# define GIT_BYTE_ORDER __BYTE_ORDER__ +# define GIT_LITTLE_ENDIAN __ORDER_LITTLE_ENDIAN__ +# define GIT_BIG_ENDIAN __ORDER_BIG_ENDIAN__ + +#elif !defined(GIT_BYTE_ORDER) # define GIT_BIG_ENDIAN 4321 # define GIT_LITTLE_ENDIAN 1234 @@ -135,14 +98,33 @@ static inline uint64_t git_bswap64(uint64_t x) #endif +#undef ntohl +#undef htonl +#undef ntohll +#undef htonll + #if GIT_BYTE_ORDER == GIT_BIG_ENDIAN -# define ntohll(n) (n) -# define htonll(n) (n) +# define ntohl(x) (x) +# define htonl(x) (x) +# define ntohll(x) (x) +# define htonll(x) (x) #else -# define ntohll(n) default_bswap64(n) -# define htonll(n) default_bswap64(n) -#endif +# if defined(bswap32) +# define ntohl(x) bswap32(x) +# define htonl(x) bswap32(x) +# else +# define ntohl(x) default_swab32(x) +# define htonl(x) default_swab32(x) +# endif + +# if defined(bswap64) +# define ntohll(x) bswap64(x) +# define htonll(x) bswap64(x) +# else +# define ntohll(x) default_bswap64(x) +# define htonll(x) default_bswap64(x) +# endif #endif static inline uint16_t get_be16(const void *ptr) diff --git a/compat/mingw-posix.h b/compat/mingw-posix.h index 88e0cf9292..631a208684 100644 --- a/compat/mingw-posix.h +++ b/compat/mingw-posix.h @@ -96,6 +96,7 @@ struct sigaction { unsigned sa_flags; }; #define SA_RESTART 0 +#define SA_NOCLDSTOP 1 struct itimerval { struct timeval it_value, it_interval; diff --git a/compat/mingw.c b/compat/mingw.c index 8a9972a1ca..8538e3d172 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -244,7 +244,6 @@ enum hide_dotfiles_type { HIDE_DOTFILES_DOTGITONLY }; -static int core_restrict_inherited_handles = -1; static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY; static char *unset_environment_variables; @@ -268,15 +267,6 @@ int mingw_core_config(const char *var, const char *value, return 0; } - if (!strcmp(var, "core.restrictinheritedhandles")) { - if (value && !strcasecmp(value, "auto")) - core_restrict_inherited_handles = -1; - else - core_restrict_inherited_handles = - git_config_bool(var, value); - return 0; - } - return 0; } @@ -588,13 +578,24 @@ static int mingw_open_existing(const wchar_t *filename, int oflags, ...) &security_attributes, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (handle == INVALID_HANDLE_VALUE) { DWORD err = GetLastError(); + if (err == ERROR_ACCESS_DENIED) { + DWORD attrs = GetFileAttributesW(filename); + if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY)) + handle = CreateFileW(filename, access, + FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, + &security_attributes, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL| FILE_FLAG_BACKUP_SEMANTICS, NULL); + } - /* See `mingw_open_append()` for why we have this conversion. */ - if (err == ERROR_INVALID_PARAMETER) - err = ERROR_PATH_NOT_FOUND; + if (handle == INVALID_HANDLE_VALUE) { + err = GetLastError(); - errno = err_win_to_posix(err); - return -1; + /* See `mingw_open_append()` for why we have this conversion. */ + if (err == ERROR_INVALID_PARAMETER) + err = ERROR_PATH_NOT_FOUND; + + errno = err_win_to_posix(err); + return -1; + } } fd = _open_osfhandle((intptr_t)handle, oflags | O_BINARY); @@ -1656,7 +1657,6 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen const char *dir, int prepend_cmd, int fhin, int fhout, int fherr) { - static int restrict_handle_inheritance = -1; STARTUPINFOEXW si; PROCESS_INFORMATION pi; LPPROC_THREAD_ATTRIBUTE_LIST attr_list = NULL; @@ -1676,16 +1676,6 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen /* Make sure to override previous errors, if any */ errno = 0; - if (restrict_handle_inheritance < 0) - restrict_handle_inheritance = core_restrict_inherited_handles; - /* - * The following code to restrict which handles are inherited seems - * to work properly only on Windows 7 and later, so let's disable it - * on Windows Vista and 2008. - */ - if (restrict_handle_inheritance < 0) - restrict_handle_inheritance = GetVersion() >> 16 >= 7601; - do_unset_environment_variables(); /* Determine whether or not we are associated to a console */ @@ -1787,7 +1777,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen wenvblk = make_environment_block(deltaenv); memset(&pi, 0, sizeof(pi)); - if (restrict_handle_inheritance && stdhandles_count && + if (stdhandles_count && (InitializeProcThreadAttributeList(NULL, 1, 0, &size) || GetLastError() == ERROR_INSUFFICIENT_BUFFER) && (attr_list = (LPPROC_THREAD_ATTRIBUTE_LIST) @@ -1808,52 +1798,13 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen &si.StartupInfo, &pi); /* - * On Windows 2008 R2, it seems that specifying certain types of handles - * (such as FILE_TYPE_CHAR or FILE_TYPE_PIPE) will always produce an - * error. Rather than playing finicky and fragile games, let's just try - * to detect this situation and simply try again without restricting any - * handle inheritance. This is still better than failing to create - * processes. + * On the off-chance that something with the file handle restriction + * went wrong, silently fall back to trying without it. */ - if (!ret && restrict_handle_inheritance && stdhandles_count) { + if (!ret && stdhandles_count) { DWORD err = GetLastError(); struct strbuf buf = STRBUF_INIT; - if (err != ERROR_NO_SYSTEM_RESOURCES && - /* - * On Windows 7 and earlier, handles on pipes and character - * devices are inherited automatically, and cannot be - * specified in the thread handle list. Rather than trying - * to catch each and every corner case (and running the - * chance of *still* forgetting a few), let's just fall - * back to creating the process without trying to limit the - * handle inheritance. - */ - !(err == ERROR_INVALID_PARAMETER && - GetVersion() >> 16 < 9200) && - !getenv("SUPPRESS_HANDLE_INHERITANCE_WARNING")) { - DWORD fl = 0; - int i; - - setenv("SUPPRESS_HANDLE_INHERITANCE_WARNING", "1", 1); - - for (i = 0; i < stdhandles_count; i++) { - HANDLE h = stdhandles[i]; - strbuf_addf(&buf, "handle #%d: %p (type %lx, " - "handle info (%d) %lx\n", i, h, - GetFileType(h), - GetHandleInformation(h, &fl), - fl); - } - strbuf_addstr(&buf, "\nThis is a bug; please report it " - "at\nhttps://github.com/git-for-windows/" - "git/issues/new\n\n" - "To suppress this warning, please set " - "the environment variable\n\n" - "\tSUPPRESS_HANDLE_INHERITANCE_WARNING=1" - "\n"); - } - restrict_handle_inheritance = 0; flags &= ~EXTENDED_STARTUPINFO_PRESENT; ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL, TRUE, flags, wenvblk, dir ? wdir : NULL, @@ -2326,7 +2277,9 @@ repeat: * current system doesn't support FileRenameInfoEx. Keep us * from using it in future calls and retry. */ - if (gle == ERROR_INVALID_PARAMETER) { + if (gle == ERROR_INVALID_PARAMETER || + gle == ERROR_NOT_SUPPORTED || + gle == ERROR_INVALID_FUNCTION) { supports_file_rename_info_ex = 0; goto repeat; } @@ -2561,7 +2514,9 @@ int setitimer(int type UNUSED, struct itimerval *in, struct itimerval *out) int sigaction(int sig, struct sigaction *in, struct sigaction *out) { - if (sig != SIGALRM) + if (sig == SIGCHLD) + return -1; + else if (sig != SIGALRM) return errno = EINVAL, error("sigaction only implemented for SIGALRM"); if (out) diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c index 12e38e0ea3..43b3be0114 100644 --- a/compat/precompose_utf8.c +++ b/compat/precompose_utf8.c @@ -56,8 +56,8 @@ void probe_utf8_pathname_composition(void) close(output_fd); repo_git_path_replace(the_repository, &path, "%s", auml_nfd); precomposed_unicode = access(path.buf, R_OK) ? 0 : 1; - git_config_set("core.precomposeunicode", - precomposed_unicode ? "true" : "false"); + repo_config_set(the_repository, "core.precomposeunicode", + precomposed_unicode ? "true" : "false"); repo_git_path_replace(the_repository, &path, "%s", auml_nfc); if (unlink(path.buf)) die_errno(_("failed to unlink '%s'"), path.buf); @@ -75,7 +75,7 @@ const char *precompose_string_if_needed(const char *in) iconv_t ic_prec; char *out; if (precomposed_unicode < 0) - git_config_get_bool("core.precomposeunicode", &precomposed_unicode); + repo_config_get_bool(the_repository, "core.precomposeunicode", &precomposed_unicode); if (precomposed_unicode != 1) return in; ic_prec = iconv_open(repo_encoding, path_encoding); @@ -6,33 +6,27 @@ * */ -#define USE_THE_REPOSITORY_VARIABLE -#define DISABLE_SIGN_COMPARE_WARNINGS - #include "git-compat-util.h" #include "abspath.h" #include "advice.h" #include "date.h" #include "branch.h" #include "config.h" +#include "dir.h" #include "parse.h" #include "convert.h" #include "environment.h" #include "gettext.h" #include "git-zlib.h" -#include "ident.h" #include "repository.h" #include "lockfile.h" -#include "mailmap.h" -#include "attr.h" #include "exec-cmd.h" #include "strbuf.h" #include "quote.h" #include "hashmap.h" #include "string-list.h" #include "object-name.h" -#include "object-store.h" -#include "pager.h" +#include "odb.h" #include "path.h" #include "utf8.h" #include "color.h" @@ -41,7 +35,6 @@ #include "strvec.h" #include "trace2.h" #include "wildmatch.h" -#include "ws.h" #include "write-or-die.h" struct config_source { @@ -56,7 +49,6 @@ struct config_source { } u; enum config_origin_type origin_type; const char *name; - const char *path; enum config_error_action default_error_action; int linenr; int eof; @@ -71,9 +63,6 @@ struct config_source { }; #define CONFIG_SOURCE_INIT { 0 } -static int pack_compression_seen; -static int zlib_compression_seen; - /* * Config that comes from trusted scopes, namely: * - CONFIG_SCOPE_SYSTEM (e.g. /etc/gitconfig) @@ -173,14 +162,14 @@ static int handle_path_include(const struct key_value_info *kvi, if (!is_absolute_path(path)) { char *slash; - if (!kvi || !kvi->path) { + if (!kvi || kvi->origin_type != CONFIG_ORIGIN_FILE) { ret = error(_("relative config includes must come from files")); goto cleanup; } - slash = find_last_dir_sep(kvi->path); + slash = find_last_dir_sep(kvi->filename); if (slash) - strbuf_add(&buf, kvi->path, slash - kvi->path + 1); + strbuf_add(&buf, kvi->filename, slash - kvi->filename + 1); strbuf_addstr(&buf, path); path = buf.buf; } @@ -208,11 +197,12 @@ static void add_trailing_starstar_for_dir(struct strbuf *pat) } static int prepare_include_condition_pattern(const struct key_value_info *kvi, - struct strbuf *pat) + struct strbuf *pat, + size_t *out) { struct strbuf path = STRBUF_INIT; char *expanded; - int prefix = 0; + size_t prefix = 0; expanded = interpolate_path(pat->buf, 1); if (expanded) { @@ -224,11 +214,11 @@ static int prepare_include_condition_pattern(const struct key_value_info *kvi, if (pat->buf[0] == '.' && is_dir_sep(pat->buf[1])) { const char *slash; - if (!kvi || !kvi->path) + if (!kvi || kvi->origin_type != CONFIG_ORIGIN_FILE) return error(_("relative config include " "conditionals must come from files")); - strbuf_realpath(&path, kvi->path, 1); + strbuf_realpath(&path, kvi->filename, 1); slash = find_last_dir_sep(path.buf); if (!slash) BUG("how is this possible?"); @@ -239,8 +229,10 @@ static int prepare_include_condition_pattern(const struct key_value_info *kvi, add_trailing_starstar_for_dir(pat); + *out = prefix; + strbuf_release(&path); - return prefix; + return 0; } static int include_by_gitdir(const struct key_value_info *kvi, @@ -249,7 +241,8 @@ static int include_by_gitdir(const struct key_value_info *kvi, { struct strbuf text = STRBUF_INIT; struct strbuf pattern = STRBUF_INIT; - int ret = 0, prefix; + size_t prefix; + int ret = 0; const char *git_dir; int already_tried_absolute = 0; @@ -260,12 +253,11 @@ static int include_by_gitdir(const struct key_value_info *kvi, strbuf_realpath(&text, git_dir, 1); strbuf_add(&pattern, cond, cond_len); - prefix = prepare_include_condition_pattern(kvi, &pattern); - -again: - if (prefix < 0) + ret = prepare_include_condition_pattern(kvi, &pattern, &prefix); + if (ret < 0) goto done; +again: if (prefix > 0) { /* * perform literal matching on the prefix part so that @@ -633,38 +625,34 @@ void kvi_from_param(struct key_value_info *out) out->linenr = -1; out->origin_type = CONFIG_ORIGIN_CMDLINE; out->scope = CONFIG_SCOPE_COMMAND; - out->path = NULL; } int git_config_parse_parameter(const char *text, config_fn_t fn, void *data) { const char *value; - struct strbuf **pair; + struct string_list pair = STRING_LIST_INIT_DUP; int ret; struct key_value_info kvi = KVI_INIT; kvi_from_param(&kvi); - pair = strbuf_split_str(text, '=', 2); - if (!pair[0]) + string_list_split(&pair, text, "=", 1); + if (!pair.nr) return error(_("bogus config parameter: %s"), text); - if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=') { - strbuf_setlen(pair[0], pair[0]->len - 1); - value = pair[1] ? pair[1]->buf : ""; - } else { + if (pair.nr == 1) value = NULL; - } + else + value = pair.items[1].string; - strbuf_trim(pair[0]); - if (!pair[0]->len) { - strbuf_list_free(pair); + if (!*pair.items[0].string) { + string_list_clear(&pair, 0); return error(_("bogus config parameter: %s"), text); } - ret = config_parse_pair(pair[0]->buf, value, &kvi, fn, data); - strbuf_list_free(pair); + ret = config_parse_pair(pair.items[0].string, value, &kvi, fn, data); + string_list_clear(&pair, 0); return ret; } @@ -734,7 +722,6 @@ int git_config_from_parameters(config_fn_t fn, void *data) if (env) { unsigned long count; char *endp; - int i; count = strtoul(env, &endp, 10); if (*endp) { @@ -746,10 +733,10 @@ int git_config_from_parameters(config_fn_t fn, void *data) goto out; } - for (i = 0; i < count; i++) { + for (unsigned long i = 0; i < count; i++) { const char *key, *value; - strbuf_addf(&envvar, "GIT_CONFIG_KEY_%d", i); + strbuf_addf(&envvar, "GIT_CONFIG_KEY_%lu", i); key = getenv_safe(&to_free, envvar.buf); if (!key) { ret = error(_("missing config key %s"), envvar.buf); @@ -757,7 +744,7 @@ int git_config_from_parameters(config_fn_t fn, void *data) } strbuf_reset(&envvar); - strbuf_addf(&envvar, "GIT_CONFIG_VALUE_%d", i); + strbuf_addf(&envvar, "GIT_CONFIG_VALUE_%lu", i); value = getenv_safe(&to_free, envvar.buf); if (!value) { ret = error(_("missing config value %s"), envvar.buf); @@ -1036,7 +1023,6 @@ static void kvi_from_source(struct config_source *cs, out->origin_type = cs->origin_type; out->linenr = cs->linenr; out->scope = scope; - out->path = cs->path; } static int git_parse_source(struct config_source *cs, config_fn_t fn, @@ -1262,80 +1248,6 @@ double git_config_double(const char *name, const char *value, return ret; } -static const struct fsync_component_name { - const char *name; - enum fsync_component component_bits; -} fsync_component_names[] = { - { "loose-object", FSYNC_COMPONENT_LOOSE_OBJECT }, - { "pack", FSYNC_COMPONENT_PACK }, - { "pack-metadata", FSYNC_COMPONENT_PACK_METADATA }, - { "commit-graph", FSYNC_COMPONENT_COMMIT_GRAPH }, - { "index", FSYNC_COMPONENT_INDEX }, - { "objects", FSYNC_COMPONENTS_OBJECTS }, - { "reference", FSYNC_COMPONENT_REFERENCE }, - { "derived-metadata", FSYNC_COMPONENTS_DERIVED_METADATA }, - { "committed", FSYNC_COMPONENTS_COMMITTED }, - { "added", FSYNC_COMPONENTS_ADDED }, - { "all", FSYNC_COMPONENTS_ALL }, -}; - -static enum fsync_component parse_fsync_components(const char *var, const char *string) -{ - enum fsync_component current = FSYNC_COMPONENTS_PLATFORM_DEFAULT; - enum fsync_component positive = 0, negative = 0; - - while (string) { - int i; - size_t len; - const char *ep; - int negated = 0; - int found = 0; - - string = string + strspn(string, ", \t\n\r"); - ep = strchrnul(string, ','); - len = ep - string; - if (!strcmp(string, "none")) { - current = FSYNC_COMPONENT_NONE; - goto next_name; - } - - if (*string == '-') { - negated = 1; - string++; - len--; - if (!len) - warning(_("invalid value for variable %s"), var); - } - - if (!len) - break; - - for (i = 0; i < ARRAY_SIZE(fsync_component_names); ++i) { - const struct fsync_component_name *n = &fsync_component_names[i]; - - if (strncmp(n->name, string, len)) - continue; - - found = 1; - if (negated) - negative |= n->component_bits; - else - positive |= n->component_bits; - } - - if (!found) { - char *component = xstrndup(string, len); - warning(_("ignoring unknown core.fsync component '%s'"), component); - free(component); - } - -next_name: - string = ep; - } - - return (current & ~negative) | positive; -} - int git_config_bool_or_int(const char *name, const char *value, const struct key_value_info *kvi, int *is_bool) { @@ -1393,438 +1305,6 @@ int git_config_color(char *dest, const char *var, const char *value) return 0; } -static int git_default_core_config(const char *var, const char *value, - const struct config_context *ctx, void *cb) -{ - /* This needs a better name */ - if (!strcmp(var, "core.filemode")) { - trust_executable_bit = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "core.trustctime")) { - trust_ctime = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "core.checkstat")) { - if (!value) - return config_error_nonbool(var); - if (!strcasecmp(value, "default")) - check_stat = 1; - else if (!strcasecmp(value, "minimal")) - check_stat = 0; - else - return error(_("invalid value for '%s': '%s'"), - var, value); - } - - if (!strcmp(var, "core.quotepath")) { - quote_path_fully = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "core.symlinks")) { - has_symlinks = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "core.ignorecase")) { - ignore_case = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "core.attributesfile")) { - FREE_AND_NULL(git_attributes_file); - return git_config_pathname(&git_attributes_file, var, value); - } - - if (!strcmp(var, "core.bare")) { - is_bare_repository_cfg = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "core.ignorestat")) { - assume_unchanged = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "core.abbrev")) { - if (!value) - return config_error_nonbool(var); - if (!strcasecmp(value, "auto")) - default_abbrev = -1; - else if (!git_parse_maybe_bool_text(value)) - default_abbrev = GIT_MAX_HEXSZ; - else { - int abbrev = git_config_int(var, value, ctx->kvi); - if (abbrev < minimum_abbrev) - return error(_("abbrev length out of range: %d"), abbrev); - default_abbrev = abbrev; - } - return 0; - } - - if (!strcmp(var, "core.disambiguate")) - return set_disambiguate_hint_config(var, value); - - if (!strcmp(var, "core.loosecompression")) { - int level = git_config_int(var, value, ctx->kvi); - if (level == -1) - level = Z_DEFAULT_COMPRESSION; - else if (level < 0 || level > Z_BEST_COMPRESSION) - die(_("bad zlib compression level %d"), level); - zlib_compression_level = level; - zlib_compression_seen = 1; - return 0; - } - - if (!strcmp(var, "core.compression")) { - int level = git_config_int(var, value, ctx->kvi); - if (level == -1) - level = Z_DEFAULT_COMPRESSION; - else if (level < 0 || level > Z_BEST_COMPRESSION) - die(_("bad zlib compression level %d"), level); - if (!zlib_compression_seen) - zlib_compression_level = level; - if (!pack_compression_seen) - pack_compression_level = level; - return 0; - } - - if (!strcmp(var, "core.autocrlf")) { - if (value && !strcasecmp(value, "input")) { - auto_crlf = AUTO_CRLF_INPUT; - return 0; - } - auto_crlf = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "core.safecrlf")) { - int eol_rndtrp_die; - if (value && !strcasecmp(value, "warn")) { - global_conv_flags_eol = CONV_EOL_RNDTRP_WARN; - return 0; - } - eol_rndtrp_die = git_config_bool(var, value); - global_conv_flags_eol = eol_rndtrp_die ? - CONV_EOL_RNDTRP_DIE : 0; - return 0; - } - - if (!strcmp(var, "core.eol")) { - if (value && !strcasecmp(value, "lf")) - core_eol = EOL_LF; - else if (value && !strcasecmp(value, "crlf")) - core_eol = EOL_CRLF; - else if (value && !strcasecmp(value, "native")) - core_eol = EOL_NATIVE; - else - core_eol = EOL_UNSET; - return 0; - } - - if (!strcmp(var, "core.checkroundtripencoding")) { - FREE_AND_NULL(check_roundtrip_encoding); - return git_config_string(&check_roundtrip_encoding, var, value); - } - - if (!strcmp(var, "core.editor")) { - FREE_AND_NULL(editor_program); - return git_config_string(&editor_program, var, value); - } - - if (!strcmp(var, "core.commentchar") || - !strcmp(var, "core.commentstring")) { - if (!value) - return config_error_nonbool(var); - else if (!strcasecmp(value, "auto")) - auto_comment_line_char = 1; - else if (value[0]) { - if (strchr(value, '\n')) - return error(_("%s cannot contain newline"), var); - comment_line_str = value; - FREE_AND_NULL(comment_line_str_to_free); - auto_comment_line_char = 0; - } else - return error(_("%s must have at least one character"), var); - return 0; - } - - if (!strcmp(var, "core.askpass")) { - FREE_AND_NULL(askpass_program); - return git_config_string(&askpass_program, var, value); - } - - if (!strcmp(var, "core.excludesfile")) { - FREE_AND_NULL(excludes_file); - return git_config_pathname(&excludes_file, var, value); - } - - if (!strcmp(var, "core.whitespace")) { - if (!value) - return config_error_nonbool(var); - whitespace_rule_cfg = parse_whitespace_rule(value); - return 0; - } - - if (!strcmp(var, "core.fsync")) { - if (!value) - return config_error_nonbool(var); - fsync_components = parse_fsync_components(var, value); - return 0; - } - - if (!strcmp(var, "core.fsyncmethod")) { - if (!value) - return config_error_nonbool(var); - if (!strcmp(value, "fsync")) - fsync_method = FSYNC_METHOD_FSYNC; - else if (!strcmp(value, "writeout-only")) - fsync_method = FSYNC_METHOD_WRITEOUT_ONLY; - else if (!strcmp(value, "batch")) - fsync_method = FSYNC_METHOD_BATCH; - else - warning(_("ignoring unknown core.fsyncMethod value '%s'"), value); - - } - - if (!strcmp(var, "core.fsyncobjectfiles")) { - if (fsync_object_files < 0) - warning(_("core.fsyncObjectFiles is deprecated; use core.fsync instead")); - fsync_object_files = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "core.preloadindex")) { - core_preload_index = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "core.createobject")) { - if (!value) - return config_error_nonbool(var); - if (!strcmp(value, "rename")) - object_creation_mode = OBJECT_CREATION_USES_RENAMES; - else if (!strcmp(value, "link")) - object_creation_mode = OBJECT_CREATION_USES_HARDLINKS; - else - die(_("invalid mode for object creation: %s"), value); - return 0; - } - - if (!strcmp(var, "core.sparsecheckout")) { - core_apply_sparse_checkout = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "core.sparsecheckoutcone")) { - core_sparse_checkout_cone = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "core.precomposeunicode")) { - precomposed_unicode = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "core.protecthfs")) { - protect_hfs = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "core.protectntfs")) { - protect_ntfs = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "core.maxtreedepth")) { - max_allowed_tree_depth = git_config_int(var, value, ctx->kvi); - return 0; - } - - /* Add other config variables here and to Documentation/config.adoc. */ - return platform_core_config(var, value, ctx, cb); -} - -static int git_default_sparse_config(const char *var, const char *value) -{ - if (!strcmp(var, "sparse.expectfilesoutsideofpatterns")) { - sparse_expect_files_outside_of_patterns = git_config_bool(var, value); - return 0; - } - - /* Add other config variables here and to Documentation/config/sparse.adoc. */ - return 0; -} - -static int git_default_i18n_config(const char *var, const char *value) -{ - if (!strcmp(var, "i18n.commitencoding")) { - FREE_AND_NULL(git_commit_encoding); - return git_config_string(&git_commit_encoding, var, value); - } - - if (!strcmp(var, "i18n.logoutputencoding")) { - FREE_AND_NULL(git_log_output_encoding); - return git_config_string(&git_log_output_encoding, var, value); - } - - /* Add other config variables here and to Documentation/config.adoc. */ - return 0; -} - -static int git_default_branch_config(const char *var, const char *value) -{ - if (!strcmp(var, "branch.autosetupmerge")) { - if (value && !strcmp(value, "always")) { - git_branch_track = BRANCH_TRACK_ALWAYS; - return 0; - } else if (value && !strcmp(value, "inherit")) { - git_branch_track = BRANCH_TRACK_INHERIT; - return 0; - } else if (value && !strcmp(value, "simple")) { - git_branch_track = BRANCH_TRACK_SIMPLE; - return 0; - } - git_branch_track = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "branch.autosetuprebase")) { - if (!value) - return config_error_nonbool(var); - else if (!strcmp(value, "never")) - autorebase = AUTOREBASE_NEVER; - else if (!strcmp(value, "local")) - autorebase = AUTOREBASE_LOCAL; - else if (!strcmp(value, "remote")) - autorebase = AUTOREBASE_REMOTE; - else if (!strcmp(value, "always")) - autorebase = AUTOREBASE_ALWAYS; - else - return error(_("malformed value for %s"), var); - return 0; - } - - /* Add other config variables here and to Documentation/config.adoc. */ - return 0; -} - -static int git_default_push_config(const char *var, const char *value) -{ - if (!strcmp(var, "push.default")) { - if (!value) - return config_error_nonbool(var); - else if (!strcmp(value, "nothing")) - push_default = PUSH_DEFAULT_NOTHING; - else if (!strcmp(value, "matching")) - push_default = PUSH_DEFAULT_MATCHING; - else if (!strcmp(value, "simple")) - push_default = PUSH_DEFAULT_SIMPLE; - else if (!strcmp(value, "upstream")) - push_default = PUSH_DEFAULT_UPSTREAM; - else if (!strcmp(value, "tracking")) /* deprecated */ - push_default = PUSH_DEFAULT_UPSTREAM; - else if (!strcmp(value, "current")) - push_default = PUSH_DEFAULT_CURRENT; - else { - error(_("malformed value for %s: %s"), var, value); - return error(_("must be one of nothing, matching, simple, " - "upstream or current")); - } - return 0; - } - - /* Add other config variables here and to Documentation/config.adoc. */ - return 0; -} - -static int git_default_mailmap_config(const char *var, const char *value) -{ - if (!strcmp(var, "mailmap.file")) { - FREE_AND_NULL(git_mailmap_file); - return git_config_pathname(&git_mailmap_file, var, value); - } - - if (!strcmp(var, "mailmap.blob")) { - FREE_AND_NULL(git_mailmap_blob); - return git_config_string(&git_mailmap_blob, var, value); - } - - /* Add other config variables here and to Documentation/config.adoc. */ - return 0; -} - -static int git_default_attr_config(const char *var, const char *value) -{ - if (!strcmp(var, "attr.tree")) { - FREE_AND_NULL(git_attr_tree); - return git_config_string(&git_attr_tree, var, value); - } - - /* - * Add other attribute related config variables here and to - * Documentation/config/attr.adoc. - */ - return 0; -} - -int git_default_config(const char *var, const char *value, - const struct config_context *ctx, void *cb) -{ - if (starts_with(var, "core.")) - return git_default_core_config(var, value, ctx, cb); - - if (starts_with(var, "user.") || - starts_with(var, "author.") || - starts_with(var, "committer.")) - return git_ident_config(var, value, ctx, cb); - - if (starts_with(var, "i18n.")) - return git_default_i18n_config(var, value); - - if (starts_with(var, "branch.")) - return git_default_branch_config(var, value); - - if (starts_with(var, "push.")) - return git_default_push_config(var, value); - - if (starts_with(var, "mailmap.")) - return git_default_mailmap_config(var, value); - - if (starts_with(var, "attr.")) - return git_default_attr_config(var, value); - - if (starts_with(var, "advice.") || starts_with(var, "color.advice")) - return git_default_advice_config(var, value); - - if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) { - pager_use_color = git_config_bool(var,value); - return 0; - } - - if (!strcmp(var, "pack.packsizelimit")) { - pack_size_limit_cfg = git_config_ulong(var, value, ctx->kvi); - return 0; - } - - if (!strcmp(var, "pack.compression")) { - int level = git_config_int(var, value, ctx->kvi); - if (level == -1) - level = Z_DEFAULT_COMPRESSION; - else if (level < 0 || level > Z_BEST_COMPRESSION) - die(_("bad pack compression level %d"), level); - pack_compression_level = level; - pack_compression_seen = 1; - return 0; - } - - if (starts_with(var, "sparse.")) - return git_default_sparse_config(var, value); - - /* Add other config variables here and to Documentation/config.adoc. */ - return 0; -} - /* * All source specific fields in the union, die_on_error, name and the callbacks * fgetc, ungetc, ftell of top need to be initialized before calling @@ -1855,17 +1335,19 @@ static int do_config_from(struct config_source *top, config_fn_t fn, static int do_config_from_file(config_fn_t fn, const enum config_origin_type origin_type, - const char *name, const char *path, FILE *f, - void *data, enum config_scope scope, + const char *name, FILE *f, void *data, + enum config_scope scope, const struct config_options *opts) { struct config_source top = CONFIG_SOURCE_INIT; int ret; + if (origin_type == CONFIG_ORIGIN_FILE && (!name || !*name)) + BUG("missing filename for CONFIG_ORIGIN_FILE"); + top.u.file = f; top.origin_type = origin_type; top.name = name; - top.path = path; top.default_error_action = CONFIG_ERROR_DIE; top.do_fgetc = config_file_fgetc; top.do_ungetc = config_file_ungetc; @@ -1880,8 +1362,8 @@ static int do_config_from_file(config_fn_t fn, static int git_config_from_stdin(config_fn_t fn, void *data, enum config_scope scope) { - return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin, - data, scope, NULL); + return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", stdin, data, + scope, NULL); } int git_config_from_file_with_options(config_fn_t fn, const char *filename, @@ -1896,7 +1378,7 @@ int git_config_from_file_with_options(config_fn_t fn, const char *filename, f = fopen_or_warn(filename, "r"); if (f) { ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename, - filename, f, data, scope, opts); + f, data, scope, opts); fclose(f); } return ret; @@ -1921,7 +1403,6 @@ int git_config_from_mem(config_fn_t fn, top.u.buf.pos = 0; top.origin_type = origin_type; top.name = name; - top.path = NULL; top.default_error_action = CONFIG_ERROR_ERROR; top.do_fgetc = config_buf_fgetc; top.do_ungetc = config_buf_ungetc; @@ -1942,7 +1423,7 @@ int git_config_from_blob_oid(config_fn_t fn, unsigned long size; int ret; - buf = repo_read_object_file(repo, oid, &type, &size); + buf = odb_read_object(repo->objects, oid, &type, &size); if (!buf) return error(_("unable to load config blob object '%s'"), name); if (type != OBJ_BLOB) { @@ -2130,13 +1611,13 @@ int config_with_options(config_fn_t fn, void *data, static void configset_iter(struct config_set *set, config_fn_t fn, void *data) { - int i, value_index; + int value_index; struct string_list *values; struct config_set_element *entry; struct configset_list *list = &set->list; struct config_context ctx = CONFIG_CONTEXT_INIT; - for (i = 0; i < list->nr; i++) { + for (size_t i = 0; i < list->nr; i++) { entry = list->items[i].e; value_index = list->items[i].value_index; values = &entry->value_list; @@ -2469,10 +1950,290 @@ int git_configset_get_pathname(struct config_set *set, const char *key, char **d return 1; } +struct comment_char_config { + unsigned last_key_id; + bool auto_set; + bool auto_set_in_file; + struct strintmap key_flags; + size_t alloc, nr; + struct comment_char_config_item { + unsigned key_id; + char *path; + enum config_scope scope; + } *item; +}; + +#define COMMENT_CHAR_CFG_INIT { \ + .key_flags = STRINTMAP_INIT, \ + } + +static void comment_char_config_release(struct comment_char_config *config) +{ + strintmap_clear(&config->key_flags); + for (size_t i = 0; i < config->nr; i++) + free(config->item[i].path); + free(config->item); +} + +/* Used to track whether the key occurs more than once in a given file */ +#define KEY_SEEN_ONCE 1u +#define KEY_SEEN_TWICE 2u +#define COMMENT_KEY_SHIFT(id) (2 * (id)) +#define COMMENT_KEY_MASK(id) (3u << COMMENT_KEY_SHIFT(id)) + +static void set_comment_key_flags(struct comment_char_config *config, + const char *path, unsigned id, unsigned value) +{ + unsigned old = strintmap_get(&config->key_flags, path); + unsigned new = (old & ~COMMENT_KEY_MASK(id)) | + value << COMMENT_KEY_SHIFT(id); + + strintmap_set(&config->key_flags, path, new); +} + +static unsigned get_comment_key_flags(struct comment_char_config *config, + const char *path, unsigned id) +{ + unsigned value = strintmap_get(&config->key_flags, path); + + return (value & COMMENT_KEY_MASK(id)) >> COMMENT_KEY_SHIFT(id); +} + +static const char *comment_key_name(unsigned id) +{ + static const char *name[] = { + "core.commentChar", + "core.commentString", + }; + + if (id >= ARRAY_SIZE(name)) + BUG("invalid comment key id"); + + return name[id]; +} + +static void comment_char_callback(const char *key, const char *value, + const struct config_context *ctx, void *data) +{ + struct comment_char_config *config = data; + const struct key_value_info *kvi = ctx->kvi; + unsigned key_id; + + if (!strcmp(key, "core.commentchar")) + key_id = 0; + else if (!strcmp(key, "core.commentstring")) + key_id = 1; + else + return; + + config->last_key_id = key_id; + config->auto_set = value && !strcmp(value, "auto"); + if (kvi->origin_type != CONFIG_ORIGIN_FILE) { + return; + } else if (get_comment_key_flags(config, kvi->filename, key_id)) { + set_comment_key_flags(config, kvi->filename, key_id, + KEY_SEEN_TWICE); + } else { + struct comment_char_config_item *item; + + ALLOC_GROW_BY(config->item, config->nr, 1, config->alloc); + item = &config->item[config->nr - 1]; + item->key_id = key_id; + item->scope = kvi->scope; + item->path = xstrdup(kvi->filename); + set_comment_key_flags(config, kvi->filename, key_id, + KEY_SEEN_ONCE); + } + config->auto_set_in_file = config->auto_set; +} + +static void add_config_scope_arg(struct repository *repo, struct strbuf *buf, + struct comment_char_config_item *item) +{ + char *global_config = git_global_config(); + char *system_config = git_system_config(); + + if (item->scope == CONFIG_SCOPE_SYSTEM && access(item->path, W_OK)) { + /* + * If the user cannot write to the system config recommend + * setting the global config instead. + */ + strbuf_addstr(buf, "--global "); + } else if (fspatheq(item->path, system_config)) { + strbuf_addstr(buf, "--system "); + } else if (fspatheq(item->path, global_config)) { + strbuf_addstr(buf, "--global "); + } else if (fspatheq(item->path, + mkpath("%s/config", + repo_get_git_dir(repo)))) { + ; /* --local is the default */ + } else if (fspatheq(item->path, + mkpath("%s/config.worktree", + repo_get_common_dir(repo)))) { + strbuf_addstr(buf, "--worktree "); + } else { + const char *path = item->path; + const char *home = getenv("HOME"); + + strbuf_addstr(buf, "--file "); + if (home && !fspathncmp(path, home, strlen(home))) { + path += strlen(home); + if (!fspathncmp(path, "/", 1)) + path++; + strbuf_addstr(buf, "~/"); + } + sq_quote_buf_pretty(buf, path); + strbuf_addch(buf, ' '); + } + + free(global_config); + free(system_config); +} + +static bool can_unset_comment_char_config(struct comment_char_config *config) +{ + for (size_t i = 0; i < config->nr; i++) { + struct comment_char_config_item *item = &config->item[i]; + + if (item->scope == CONFIG_SCOPE_SYSTEM && + access(item->path, W_OK)) + return false; + } + + return true; +} + +static void add_unset_auto_comment_char_advice(struct repository *repo, + struct comment_char_config *config) +{ + struct strbuf buf = STRBUF_INIT; + + if (!can_unset_comment_char_config(config)) + return; + + for (size_t i = 0; i < config->nr; i++) { + struct comment_char_config_item *item = &config->item[i]; + + strbuf_addstr(&buf, " git config unset "); + add_config_scope_arg(repo, &buf, item); + if (get_comment_key_flags(config, item->path, item->key_id) == KEY_SEEN_TWICE) + strbuf_addstr(&buf, "--all "); + strbuf_addf(&buf, "%s\n", comment_key_name(item->key_id)); + } + advise(_("\nTo use the default comment string (#) please run\n\n%s"), + buf.buf); + strbuf_release(&buf); +} + +static void add_comment_char_advice(struct repository *repo, + struct comment_char_config *config) +{ + struct strbuf buf = STRBUF_INIT; + struct comment_char_config_item *item; + /* TRANSLATORS this is a place holder for the value of core.commentString */ + const char *placeholder = _("<comment string>"); + + /* + * If auto is set in the last file that we saw advise the user how to + * update their config. + */ + if (!config->auto_set_in_file) + return; + + add_unset_auto_comment_char_advice(repo, config); + item = &config->item[config->nr - 1]; + strbuf_reset(&buf); + strbuf_addstr(&buf, " git config set "); + add_config_scope_arg(repo, &buf, item); + strbuf_addf(&buf, "%s %s\n", comment_key_name(item->key_id), + placeholder); + advise(_("\nTo set a custom comment string please run\n\n" + "%s\nwhere '%s' is the string you wish to use.\n"), + buf.buf, placeholder); + strbuf_release(&buf); +} + +#undef KEY_SEEN_ONCE +#undef KEY_SEEN_TWICE +#undef COMMENT_KEY_SHIFT +#undef COMMENT_KEY_MASK + +struct repo_config { + struct repository *repo; + struct comment_char_config comment_char_config; +}; + +#define REPO_CONFIG_INIT(repo_) { \ + .comment_char_config = COMMENT_CHAR_CFG_INIT, \ + .repo = repo_, \ + }; + +static void repo_config_release(struct repo_config *config) +{ + comment_char_config_release(&config->comment_char_config); +} + +#ifdef WITH_BREAKING_CHANGES +static void check_auto_comment_char_config(struct repository *repo, + struct comment_char_config *config) +{ + if (!config->auto_set) + return; + + die_message(_("Support for '%s=auto' has been removed in Git 3.0"), + comment_key_name(config->last_key_id)); + add_comment_char_advice(repo, config); + die(NULL); +} +#else +static void check_auto_comment_char_config(struct repository *repo, + struct comment_char_config *config) +{ + extern bool warn_on_auto_comment_char; + const char *DEPRECATED_CONFIG_ENV = + "GIT_AUTO_COMMENT_CHAR_CONFIG_WARNING_GIVEN"; + + if (!config->auto_set || !warn_on_auto_comment_char) + return; + + /* + * Use an environment variable to ensure that subprocesses do not repeat + * the warning. + */ + if (git_env_bool(DEPRECATED_CONFIG_ENV, false)) + return; + + setenv(DEPRECATED_CONFIG_ENV, "true", true); + + warning(_("Support for '%s=auto' is deprecated and will be removed in " + "Git 3.0"), comment_key_name(config->last_key_id)); + add_comment_char_advice(repo, config); +} +#endif /* WITH_BREAKING_CHANGES */ + +static void check_deprecated_config(struct repo_config *config) +{ + if (!config->repo->check_deprecated_config) + return; + + check_auto_comment_char_config(config->repo, + &config->comment_char_config); +} + +static int repo_config_callback(const char *key, const char *value, + const struct config_context *ctx, void *data) +{ + struct repo_config *config = data; + + comment_char_callback(key, value, ctx, &config->comment_char_config); + return config_set_callback(key, value, ctx, config->repo->config); +} + /* Functions use to read configuration from a repository */ static void repo_read_config(struct repository *repo) { struct config_options opts = { 0 }; + struct repo_config config = REPO_CONFIG_INIT(repo); opts.respect_includes = 1; opts.commondir = repo->commondir; @@ -2484,8 +2245,8 @@ static void repo_read_config(struct repository *repo) git_configset_clear(repo->config); git_configset_init(repo->config); - if (config_with_options(config_set_callback, repo->config, NULL, - repo, &opts) < 0) + if (config_with_options(repo_config_callback, &config, NULL, repo, + &opts) < 0) /* * config_with_options() normally returns only * zero, as most errors are fatal, and @@ -2498,6 +2259,8 @@ static void repo_read_config(struct repository *repo) * immediately. */ die(_("unknown error occurred while reading the configuration files")); + check_deprecated_config(&config); + repo_config_release(&config); } static void git_config_check_init(struct repository *repo) @@ -2753,7 +2516,7 @@ void git_die_config(struct repository *r, const char *key, const char *err, ...) } /* - * Find all the stuff for git_config_set() below. + * Find all the stuff for repo_config_set() below. */ struct config_store_data { @@ -2986,10 +2749,11 @@ static ssize_t write_pair(int fd, const char *key, const char *value, */ static void maybe_remove_section(struct config_store_data *store, size_t *begin_offset, size_t *end_offset, - int *seen_ptr) + unsigned *seen_ptr) { size_t begin; - int i, seen, section_seen = 0; + int section_seen = 0; + unsigned int i, seen; /* * First, ensure that this is the first key, and that there are no @@ -3184,6 +2948,14 @@ int repo_config_set_multivar_in_file_gently(struct repository *r, char *contents = NULL; size_t contents_sz; struct config_store_data store = CONFIG_STORE_INIT; + bool saved_check_deprecated_config = r->check_deprecated_config; + + /* + * Do not warn or die if there are deprecated config settings as + * we want the user to be able to change those settings by running + * "git config". + */ + r->check_deprecated_config = false; validate_comment_string(comment); @@ -3232,7 +3004,8 @@ int repo_config_set_multivar_in_file_gently(struct repository *r, } else { struct stat st; size_t copy_begin, copy_end; - int i, new_line = 0; + unsigned i; + int new_line = 0; struct config_options opts; if (!value_pattern) @@ -3414,6 +3187,7 @@ out_free: if (in_fd >= 0) close(in_fd); config_store_data_clear(&store); + r->check_deprecated_config = saved_check_deprecated_config; return ret; write_err_out: @@ -122,14 +122,12 @@ struct key_value_info { int linenr; enum config_origin_type origin_type; enum config_scope scope; - const char *path; }; #define KVI_INIT { \ .filename = NULL, \ .linenr = -1, \ .origin_type = CONFIG_ORIGIN_UNKNOWN, \ .scope = CONFIG_SCOPE_UNKNOWN, \ - .path = NULL, \ } /* Captures additional information that a config callback can use. */ @@ -165,9 +163,6 @@ struct config_context { typedef int (*config_fn_t)(const char *, const char *, const struct config_context *, void *); -int git_default_config(const char *, const char *, - const struct config_context *, void *); - /** * Read a specific file in git-config format. * This function takes the same callback and data parameters as `repo_config`. @@ -718,140 +713,4 @@ NORETURN void git_die_config_linenr(const char *key, const char *filename, int l lookup_config(mapping, ARRAY_SIZE(mapping), var) int lookup_config(const char **mapping, int nr_mapping, const char *var); -# ifdef USE_THE_REPOSITORY_VARIABLE -static inline void git_config(config_fn_t fn, void *data) -{ - repo_config(the_repository, fn, data); -} - -static inline void git_config_clear(void) -{ - repo_config_clear(the_repository); -} - -static inline int git_config_get(const char *key) -{ - return repo_config_get(the_repository, key); -} - -static inline int git_config_get_value(const char *key, const char **value) -{ - return repo_config_get_value(the_repository, key, value); -} - -static inline int git_config_get_value_multi(const char *key, const struct string_list **dest) -{ - return repo_config_get_value_multi(the_repository, key, dest); -} - -static inline int git_config_get_string_multi(const char *key, - const struct string_list **dest) -{ - return repo_config_get_string_multi(the_repository, key, dest); -} - -static inline int git_config_get_string(const char *key, char **dest) -{ - return repo_config_get_string(the_repository, key, dest); -} - -static inline int git_config_get_string_tmp(const char *key, const char **dest) -{ - return repo_config_get_string_tmp(the_repository, key, dest); -} - -static inline int git_config_get_int(const char *key, int *dest) -{ - return repo_config_get_int(the_repository, key, dest); -} - -static inline int git_config_get_ulong(const char *key, unsigned long *dest) -{ - return repo_config_get_ulong(the_repository, key, dest); -} - -static inline int git_config_get_bool(const char *key, int *dest) -{ - return repo_config_get_bool(the_repository, key, dest); -} - -static inline int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest) -{ - return repo_config_get_bool_or_int(the_repository, key, is_bool, dest); -} - -static inline int git_config_get_maybe_bool(const char *key, int *dest) -{ - return repo_config_get_maybe_bool(the_repository, key, dest); -} - -static inline int git_config_get_pathname(const char *key, char **dest) -{ - return repo_config_get_pathname(the_repository, key, dest); -} - -static inline void git_config_set_in_file(const char *config_filename, - const char *key, const char *value) -{ - repo_config_set_in_file(the_repository, config_filename, key, value); -} - -static inline int git_config_set_gently(const char *key, const char *value) -{ - return repo_config_set_gently(the_repository, key, value); -} - -static inline void git_config_set(const char *key, const char *value) -{ - repo_config_set(the_repository, key, value); -} - -static inline int git_config_set_in_file_gently( - const char *config_filename, - const char *key, - const char *comment, - const char *value) -{ - return repo_config_set_in_file_gently(the_repository, config_filename, - key, comment, value); -} - -static inline int git_config_set_multivar_in_file_gently( - const char *config_filename, - const char *key, const char *value, - const char *value_pattern, - const char *comment, - unsigned flags) -{ - return repo_config_set_multivar_in_file_gently(the_repository, config_filename, - key, value, value_pattern, - comment, flags); -} - -static inline void git_config_set_multivar_in_file( - const char *config_filename, - const char *key, - const char *value, - const char *value_pattern, - unsigned flags) -{ - repo_config_set_multivar_in_file(the_repository, config_filename, - key, value, value_pattern, flags); -} - -static inline int git_config_set_multivar_gently(const char *key, const char *value, - const char *value_pattern, unsigned flags) -{ - return repo_config_set_multivar_gently(the_repository, key, value, - value_pattern, flags); -} - -static inline void git_config_set_multivar(const char *key, const char *value, - const char *value_pattern, unsigned flags) -{ - repo_config_set_multivar(the_repository, key, value, - value_pattern, flags); -} -# endif /* USE_THE_REPOSITORY_VARIABLE */ - #endif /* CONFIG_H */ diff --git a/config.mak.uname b/config.mak.uname index 3e26bb074a..1691c6ae6e 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -190,9 +190,6 @@ ifeq ($(uname_S),SunOS) SHELL_PATH = /bin/bash SANE_TOOL_PATH = /usr/xpg6/bin:/usr/xpg4/bin HAVE_ALLOCA_H = YesPlease - NO_STRCASESTR = YesPlease - NO_MEMMEM = YesPlease - NO_MKDTEMP = YesPlease NO_REGEX = YesPlease NO_MSGFMT_EXTENDED_OPTIONS = YesPlease HAVE_DEV_TTY = YesPlease @@ -202,7 +199,10 @@ ifeq ($(uname_S),SunOS) NO_IPV6 = YesPlease NO_SOCKADDR_STORAGE = YesPlease NO_UNSETENV = YesPlease + NO_MKDTEMP = YesPlease + NO_MEMMEM = YesPlease NO_SETENV = YesPlease + NO_STRCASESTR = YesPlease NO_STRLCPY = YesPlease NO_STRTOUMAX = YesPlease GIT_TEST_CMP = cmp @@ -212,23 +212,45 @@ ifeq ($(uname_S),SunOS) NO_IPV6 = YesPlease NO_SOCKADDR_STORAGE = YesPlease NO_UNSETENV = YesPlease + NO_MKDTEMP = YesPlease + NO_MEMMEM = YesPlease NO_SETENV = YesPlease + NO_STRCASESTR = YesPlease NO_STRLCPY = YesPlease NO_STRTOUMAX = YesPlease GIT_TEST_CMP = cmp endif ifeq ($(uname_R),5.8) NO_UNSETENV = YesPlease + NO_MKDTEMP = YesPlease + NO_MEMMEM = YesPlease NO_SETENV = YesPlease + NO_STRCASESTR = YesPlease NO_STRTOUMAX = YesPlease GIT_TEST_CMP = cmp endif ifeq ($(uname_R),5.9) NO_UNSETENV = YesPlease + NO_MKDTEMP = YesPlease + NO_MEMMEM = YesPlease NO_SETENV = YesPlease + NO_STRCASESTR = YesPlease NO_STRTOUMAX = YesPlease GIT_TEST_CMP = cmp endif + ifeq ($(uname_R),5.10) + NO_UNSETENV = YesPlease + NO_MKDTEMP = YesPlease + NO_MEMMEM = YesPlease + NO_SETENV = YesPlease + NO_STRCASESTR = YesPlease + GIT_TEST_CMP = cmp + endif + ifeq ($(uname_R),5.11) + NO_UNSETENV = YesPlease + NO_SETENV = YesPlease + GIT_TEST_CMP = cmp + endif INSTALL = /usr/ucb/install TAR = gtar BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__ @@ -280,16 +302,13 @@ ifeq ($(uname_S),FreeBSD) ifeq ($(firstword $(subst -, ,$(uname_R))),10.1) OLD_ICONV = YesPlease endif - NO_MEMMEM = YesPlease + ifeq ($(shell v=$(uname_R) && test $${v%%.*} -lt 12 && echo 1),1) + NO_MEMMEM = UnfortunatelyYes + endif BASIC_CFLAGS += -I/usr/local/include BASIC_LDFLAGS += -L/usr/local/lib DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease USE_ST_TIMESPEC = YesPlease - ifeq ($(shell expr "$(uname_R)" : '4\.'),2) - PTHREAD_LIBS = -pthread - NO_UINTMAX_T = YesPlease - NO_STRTOUMAX = YesPlease - endif PYTHON_PATH = /usr/local/bin/python PERL_PATH = /usr/local/bin/perl HAVE_PATHS_H = YesPlease diff --git a/configure.ac b/configure.ac index f6caab919a..cfb50112bf 100644 --- a/configure.ac +++ b/configure.ac @@ -1068,32 +1068,6 @@ AC_CHECK_LIB([iconv], [locale_charset], GIT_CONF_SUBST([CHARSET_LIB]) # -# Define HAVE_SYSINFO=YesPlease if sysinfo is available. -# -AC_DEFUN([HAVE_SYSINFO_SRC], [ -AC_LANG_PROGRAM([[ -#include <stdint.h> -#include <sys/sysinfo.h> -]], [[ -struct sysinfo si; -uint64_t t = 0; -if (!sysinfo(&si)) { - t = si.totalram; - if (si.mem_unit > 1) - t *= (uint64_t)si.mem_unit; -} -return t; -]])]) - -AC_MSG_CHECKING([for sysinfo]) -AC_COMPILE_IFELSE([HAVE_SYSINFO_SRC], - [AC_MSG_RESULT([yes]) - HAVE_SYSINFO=YesPlease], - [AC_MSG_RESULT([no]) - HAVE_SYSINFO=]) -GIT_CONF_SUBST([HAVE_SYSINFO]) - -# # Define HAVE_CLOCK_GETTIME=YesPlease if clock_gettime is available. GIT_CHECK_FUNC(clock_gettime, [HAVE_CLOCK_GETTIME=YesPlease], @@ -1148,14 +1122,6 @@ GIT_CHECK_FUNC(strlcpy, [NO_STRLCPY=YesPlease]) GIT_CONF_SUBST([NO_STRLCPY]) # -# Define NO_UINTMAX_T if your platform does not have uintmax_t -AC_CHECK_TYPE(uintmax_t, -[NO_UINTMAX_T=], -[NO_UINTMAX_T=YesPlease],[ -#include <inttypes.h> -]) -GIT_CONF_SUBST([NO_UINTMAX_T]) -# # Define NO_STRTOUMAX if you don't have strtoumax in the C library. GIT_CHECK_FUNC(strtoumax, [NO_STRTOUMAX=], @@ -1221,6 +1187,41 @@ AC_COMPILE_IFELSE([BSD_SYSCTL_SRC], HAVE_BSD_SYSCTL=]) GIT_CONF_SUBST([HAVE_BSD_SYSCTL]) +# +# Define HAVE_SYSINFO=YesPlease if sysinfo is available. +# + +HAVE_SYSINFO= +# on a *BSD system, sysctl() takes precedence over the +# sysinfo() compatibility library (if installed). + +if test -z "$HAVE_BSD_SYSCTL"; then + + AC_DEFUN([HAVE_SYSINFO_SRC], [ + AC_LANG_PROGRAM([[ + #include <stdint.h> + #include <sys/sysinfo.h> + ]], [[ + struct sysinfo si; + uint64_t t = 0; + if (!sysinfo(&si)) { + t = si.totalram; + if (si.mem_unit > 1) + t *= (uint64_t)si.mem_unit; + } + return t; + ]])]) + + AC_MSG_CHECKING([for sysinfo]) + AC_COMPILE_IFELSE([HAVE_SYSINFO_SRC], + [AC_MSG_RESULT([yes]) + HAVE_SYSINFO=YesPlease], + [AC_MSG_RESULT([no]) + HAVE_SYSINFO=]) + GIT_CONF_SUBST([HAVE_SYSINFO]) + +fi + ## Other checks. # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link. # Enable it on Windows. By default, symrefs are still used. @@ -251,7 +251,7 @@ static void process_capabilities(struct packet_reader *reader, size_t *linelen) reader->hash_algo = &hash_algos[hash_algo]; free(hash_name); } else { - reader->hash_algo = &hash_algos[GIT_HASH_SHA1]; + reader->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY]; } } @@ -407,7 +407,7 @@ static int process_ref_v2(struct packet_reader *reader, struct ref ***list, * name. Subsequent fields (symref-target and peeled) are optional and * don't have a particular order. */ - if (string_list_split(&line_sections, line, ' ', -1) < 2) { + if (string_list_split(&line_sections, line, " ", -1) < 2) { ret = 0; goto out; } @@ -500,7 +500,7 @@ static void send_capabilities(int fd_out, struct packet_reader *reader) reader->hash_algo = &hash_algos[hash_algo]; packet_write_fmt(fd_out, "object-format=%s", reader->hash_algo->name); } else { - reader->hash_algo = &hash_algos[GIT_HASH_SHA1]; + reader->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY]; } if (server_feature_v2("promisor-remote", &promisor_remote_info)) { char *reply = promisor_remote_reply(promisor_remote_info); @@ -665,7 +665,7 @@ int server_supports_hash(const char *desired, int *feature_supported) if (feature_supported) *feature_supported = !!hash; if (!hash) { - hash = hash_algos[GIT_HASH_SHA1].name; + hash = hash_algos[GIT_HASH_SHA1_LEGACY].name; len = strlen(hash); } while (hash) { @@ -1028,7 +1028,7 @@ static int git_proxy_command_options(const char *var, const char *value, static int git_use_proxy(const char *host) { git_proxy_command = getenv("GIT_PROXY_COMMAND"); - git_config(git_proxy_command_options, (void*)host); + repo_config(the_repository, git_proxy_command_options, (void*)host); return (git_proxy_command && *git_proxy_command); } @@ -1154,7 +1154,7 @@ static const char *get_ssh_command(void) if ((ssh = getenv("GIT_SSH_COMMAND"))) return ssh; - if (!git_config_get_string_tmp("core.sshcommand", &ssh)) + if (!repo_config_get_string_tmp(the_repository, "core.sshcommand", &ssh)) return ssh; return NULL; @@ -1173,7 +1173,7 @@ static void override_ssh_variant(enum ssh_variant *ssh_variant) { const char *variant = getenv("GIT_SSH_VARIANT"); - if (!variant && git_config_get_string_tmp("ssh.variant", &variant)) + if (!variant && repo_config_get_string_tmp(the_repository, "ssh.variant", &variant)) return; if (!strcmp(variant, "auto")) diff --git a/connected.c b/connected.c index 4415388beb..18c13245d8 100644 --- a/connected.c +++ b/connected.c @@ -3,7 +3,7 @@ #include "git-compat-util.h" #include "gettext.h" #include "hex.h" -#include "object-store.h" +#include "odb.h" #include "run-command.h" #include "sigchain.h" #include "connected.h" diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt index 25b495fa73..edb0fc04ad 100644 --- a/contrib/buildsystems/CMakeLists.txt +++ b/contrib/buildsystems/CMakeLists.txt @@ -1005,7 +1005,9 @@ parse_makefile_for_sources(clar-test_SOURCES ${CMAKE_SOURCE_DIR}/Makefile "CLAR_ list(TRANSFORM clar-test_SOURCES REPLACE "\\$\\(UNIT_TEST_DIR\\)/" "${CMAKE_SOURCE_DIR}/t/unit-tests/") add_library(clar-test-lib STATIC ${clar-test_SOURCES}) -parse_makefile_for_scripts(unit_test_PROGRAMS "UNIT_TEST_PROGRAMS" "") +file(GLOB unit_test_PROGRAMS "${CMAKE_SOURCE_DIR}/t/unit-tests/t-*.c") +list(TRANSFORM unit_test_PROGRAMS REPLACE "${CMAKE_SOURCE_DIR}/" "") +list(TRANSFORM unit_test_PROGRAMS REPLACE ".c" "") foreach(unit_test ${unit_test_PROGRAMS}) add_executable("${unit_test}" "${CMAKE_SOURCE_DIR}/t/unit-tests/${unit_test}.c") target_link_libraries("${unit_test}" unit-test-lib clar-test-lib common-main) diff --git a/contrib/coccinelle/commit.cocci b/contrib/coccinelle/commit.cocci index af6dd4c20c..c5284604c5 100644 --- a/contrib/coccinelle/commit.cocci +++ b/contrib/coccinelle/commit.cocci @@ -25,7 +25,8 @@ expression s; // functions, then the recommended transformation will be bogus with // repo_get_commit_tree() on the LHS. @@ -identifier f !~ "^(repo_get_commit_tree|get_commit_tree_in_graph_one|load_tree_for_commit|set_commit_tree)$"; +identifier f != { repo_get_commit_tree, get_commit_tree_in_graph_one, + load_tree_for_commit, set_commit_tree }; expression c; @@ f(...) {<... diff --git a/contrib/coccinelle/config_fn_ctx.pending.cocci b/contrib/coccinelle/config_fn_ctx.pending.cocci index 6d3d1000a9..54f09fcbcd 100644 --- a/contrib/coccinelle/config_fn_ctx.pending.cocci +++ b/contrib/coccinelle/config_fn_ctx.pending.cocci @@ -83,7 +83,7 @@ int fn(const char *C1, const char *C2, // The previous rules don't catch all callbacks, especially if they're defined -// in a separate file from the git_config() call. Fix these manually. +// in a separate file from the repo_config() call. Fix these manually. @@ identifier C1, C2, D; attribute name UNUSED; diff --git a/contrib/coccinelle/the_repository.cocci b/contrib/coccinelle/the_repository.cocci index 765ad68967..ea7fe1c8db 100644 --- a/contrib/coccinelle/the_repository.cocci +++ b/contrib/coccinelle/the_repository.cocci @@ -77,7 +77,7 @@ | - diff_setup + repo_diff_setup -// object-store.h +// odb.h | - read_object_file + repo_read_object_file diff --git a/contrib/credential/netrc/git-credential-netrc.perl b/contrib/credential/netrc/git-credential-netrc.perl index 9fb998ae09..3c0a532d0e 100755 --- a/contrib/credential/netrc/git-credential-netrc.perl +++ b/contrib/credential/netrc/git-credential-netrc.perl @@ -1,4 +1,4 @@ -#!/usr/bin/perl +#!/usr/bin/env perl use strict; use warnings; @@ -267,8 +267,16 @@ sub load_netrc { if (!defined $nentry->{machine}) { next; } - if (defined $nentry->{port} && $nentry->{port} =~ m/^\d+$/) { - $num_port = $nentry->{port}; + if (defined $nentry->{port}) { + $num_port = Git::port_num($nentry->{port}); + unless ($num_port) { + printf(STDERR "ignoring invalid port `%s' " . + "from netrc file\n", $nentry->{port}); + } + # Since we've already validated and converted + # the port to its numerical value, do not + # capture it as the `protocol' value, as used + # to be the case for symbolic port names. delete $nentry->{port}; } diff --git a/contrib/credential/netrc/meson.build b/contrib/credential/netrc/meson.build index 3d74547c8a..16fa69e317 100644 --- a/contrib/credential/netrc/meson.build +++ b/contrib/credential/netrc/meson.build @@ -17,6 +17,6 @@ if get_option('tests') workdir: meson.current_source_dir(), env: credential_netrc_testenv, depends: test_dependencies + bin_wrappers + [credential_netrc], - timeout: 0, + kwargs: test_kwargs, ) endif diff --git a/contrib/credential/netrc/test.pl b/contrib/credential/netrc/test.pl index 67a0ede564..8a7fc2588a 100755 --- a/contrib/credential/netrc/test.pl +++ b/contrib/credential/netrc/test.pl @@ -45,7 +45,7 @@ chmod 0600, $netrc; diag "Testing with invalid data\n"; $cred = run_credential(['-f', $netrc, 'get'], "bad data"); -ok(scalar keys %$cred == 4, "Got first found keys with bad data"); +ok(scalar keys %$cred == 3, "Got first found keys with bad data"); diag "Testing netrc file for a missing corovamilkbar entry\n"; $cred = run_credential(['-f', $netrc, 'get'], @@ -64,12 +64,12 @@ is($cred->{username}, 'carol', "Got correct Github username"); diag "Testing netrc file for a username-specific entry\n"; $cred = run_credential(['-f', $netrc, 'get'], - { host => 'imap', username => 'bob' }); + { host => 'imap:993', username => 'bob' }); -ok(scalar keys %$cred == 2, "Got 2 username-specific keys"); +# Only the password field gets returned. +ok(scalar keys %$cred == 1, "Got 1 username-specific keys"); is($cred->{password}, 'bobwillknow', "Got correct user-specific password"); -is($cred->{protocol}, 'imaps', "Got correct user-specific protocol"); diag "Testing netrc file for a host:port-specific entry\n"; $cred = run_credential(['-f', $netrc, 'get'], diff --git a/contrib/diff-highlight/README b/contrib/diff-highlight/README index d4c2343175..1db4440e68 100644 --- a/contrib/diff-highlight/README +++ b/contrib/diff-highlight/README @@ -58,6 +58,14 @@ following in your git configuration: diff = diff-highlight | less --------------------------------------------- +If you use the interactive patch mode of `git add -p`, `git checkout +-p`, etc, you may also want to configure it to be used there: + +--------------------------------------------- +[interactive] + diffFilter = diff-highlight +--------------------------------------------- + Color Config ------------ diff --git a/contrib/emacs/README b/contrib/emacs/README deleted file mode 100644 index 977a16f1e3..0000000000 --- a/contrib/emacs/README +++ /dev/null @@ -1,33 +0,0 @@ -This directory used to contain various modules for Emacs support. - -These were added shortly after Git was first released. Since then -Emacs's own support for Git got better than what was offered by these -modes. There are also popular 3rd-party Git modes such as Magit which -offer replacements for these. - -The following modules were available, and can be dug up from the Git -history: - -* git.el: - - Wrapper for "git status" that provided access to other git commands. - - Modern alternatives to this include Magit, and VC mode that ships - with Emacs. - -* git-blame.el: - - A wrapper for "git blame" written before Emacs's own vc-annotate - mode learned to invoke git-blame, which can be done via C-x v g. - -* vc-git.el: - - This file used to contain the VC-mode backend for git, but it is no - longer distributed with git. It is now maintained as part of Emacs - and included in standard Emacs distributions starting from version - 22.2. - - If you have an earlier Emacs version, upgrading to Emacs 22 is - recommended, since the VC mode in older Emacs is not generic enough - to be able to support git in a reasonable manner, and no attempt has - been made to backport vc-git.el. diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el deleted file mode 100644 index 6a8a2b8ff1..0000000000 --- a/contrib/emacs/git-blame.el +++ /dev/null @@ -1,6 +0,0 @@ -(error "git-blame.el no longer ships with git. It's recommended -to replace its use with Emacs's own vc-annotate. See -contrib/emacs/README in git's -sources (https://github.com/git/git/blob/master/contrib/emacs/README) -for more info on suggested alternatives and for why this -happened.") diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el deleted file mode 100644 index 03f926281f..0000000000 --- a/contrib/emacs/git.el +++ /dev/null @@ -1,6 +0,0 @@ -(error "git.el no longer ships with git. It's recommended to -replace its use with Magit, or simply delete references to git.el -in your initialization file(s). See contrib/emacs/README in git's -sources (https://github.com/git/git/blob/master/contrib/emacs/README) -for suggested alternatives and for why this happened. Emacs's own -VC mode and Magit are viable alternatives.") diff --git a/contrib/examples/README b/contrib/examples/README deleted file mode 100644 index 18bc60b021..0000000000 --- a/contrib/examples/README +++ /dev/null @@ -1,20 +0,0 @@ -This directory used to contain scripted implementations of builtins -that have since been rewritten in C. - -They have now been removed, but can be retrieved from an older commit -that removed them from this directory. - -They're interesting for their reference value to any aspiring plumbing -users who want to learn how pieces can be fit together, but in many -cases have drifted enough from the actual implementations Git uses to -be instructive. - -Other things that can be useful: - - * Some commands such as git-gc wrap other commands, and what they're - doing behind the scenes can be seen by running them under - GIT_TRACE=1 - - * Doing `git log` on paths matching '*--helper.c' will show - incremental effort in the direction of moving existing shell - scripts to C. diff --git a/contrib/git-jump/git-jump b/contrib/git-jump/git-jump index 3f69675961..8d1d5d79a6 100755 --- a/contrib/git-jump/git-jump +++ b/contrib/git-jump/git-jump @@ -44,7 +44,7 @@ open_editor() { mode_diff() { git diff --no-prefix --relative "$@" | perl -ne ' - if (m{^\+\+\+ (.*)}) { $file = $1 eq "/dev/null" ? undef : $1; next } + if (m{^\+\+\+ (.*?)\t?$}) { $file = $1 eq "/dev/null" ? undef : $1; next } defined($file) or next; if (m/^@@ .*?\+(\d+)/) { $line = $1; next } defined($line) or next; diff --git a/contrib/git-resurrect.sh b/contrib/git-resurrect.sh deleted file mode 100755 index d843df3afd..0000000000 --- a/contrib/git-resurrect.sh +++ /dev/null @@ -1,181 +0,0 @@ -#!/bin/sh - -USAGE="[-a] [-r] [-m] [-t] [-n] [-b <newname>] <name>" -LONG_USAGE="git-resurrect attempts to find traces of a branch tip -called <name>, and tries to resurrect it. Currently, the reflog is -searched for checkout messages, and with -r also merge messages. With --m and -t, the history of all refs is scanned for Merge <name> into -other/Merge <other> into <name> (respectively) commit subjects, which -is rather slow but allows you to resurrect other people's topic -branches." - -OPTIONS_KEEPDASHDASH= -OPTIONS_STUCKLONG= -OPTIONS_SPEC="\ -git resurrect $USAGE --- -b,branch= save branch as <newname> instead of <name> -a,all same as -l -r -m -t -k,keep-going full rev-list scan (instead of first match) -l,reflog scan reflog for checkouts (enabled by default) -r,reflog-merges scan for merges recorded in reflog -m,merges scan for merges into other branches (slow) -t,merge-targets scan for merges of other branches into <name> -n,dry-run don't recreate the branch" - -. git-sh-setup - -search_reflog () { - sed -ne 's~^\([^ ]*\) .* checkout: moving from '"$1"' .*~\1~p' \ - < "$GIT_DIR"/logs/HEAD -} - -search_reflog_merges () { - git rev-parse $( - sed -ne 's~^[^ ]* \([^ ]*\) .* merge '"$1"':.*~\1^2~p' \ - < "$GIT_DIR"/logs/HEAD - ) -} - -oid_pattern=$(git hash-object --stdin </dev/null | sed -e 's/./[0-9a-f]/g') - -search_merges () { - git rev-list --all --grep="Merge branch '$1'" \ - --pretty=tformat:"%P %s" | - sed -ne "/^$oid_pattern \($oid_pattern\) Merge .*/ {s//\1/p;$early_exit}" -} - -search_merge_targets () { - git rev-list --all --grep="Merge branch '[^']*' into $branch\$" \ - --pretty=tformat:"%H %s" --all | - sed -ne "/^\($oid_pattern\) Merge .*/ {s//\1/p;$early_exit} " -} - -dry_run= -early_exit=q -scan_reflog=t -scan_reflog_merges= -scan_merges= -scan_merge_targets= -new_name= - -while test "$#" != 0; do - case "$1" in - -b|--branch) - shift - new_name="$1" - ;; - -n|--dry-run) - dry_run=t - ;; - --no-dry-run) - dry_run= - ;; - -k|--keep-going) - early_exit= - ;; - --no-keep-going) - early_exit=q - ;; - -m|--merges) - scan_merges=t - ;; - --no-merges) - scan_merges= - ;; - -l|--reflog) - scan_reflog=t - ;; - --no-reflog) - scan_reflog= - ;; - -r|--reflog_merges) - scan_reflog_merges=t - ;; - --no-reflog_merges) - scan_reflog_merges= - ;; - -t|--merge-targets) - scan_merge_targets=t - ;; - --no-merge-targets) - scan_merge_targets= - ;; - -a|--all) - scan_reflog=t - scan_reflog_merges=t - scan_merges=t - scan_merge_targets=t - ;; - --) - shift - break - ;; - *) - usage - ;; - esac - shift -done - -test "$#" = 1 || usage - -all_strategies="$scan_reflog$scan_reflog_merges$scan_merges$scan_merge_targets" -if test -z "$all_strategies"; then - die "must enable at least one of -lrmt" -fi - -branch="$1" -test -z "$new_name" && new_name="$branch" - -if test ! -z "$scan_reflog"; then - if test -r "$GIT_DIR"/logs/HEAD; then - candidates="$(search_reflog $branch)" - else - die 'reflog scanning requested, but' \ - '$GIT_DIR/logs/HEAD not readable' - fi -fi -if test ! -z "$scan_reflog_merges"; then - if test -r "$GIT_DIR"/logs/HEAD; then - candidates="$candidates $(search_reflog_merges $branch)" - else - die 'reflog scanning requested, but' \ - '$GIT_DIR/logs/HEAD not readable' - fi -fi -if test ! -z "$scan_merges"; then - candidates="$candidates $(search_merges $branch)" -fi -if test ! -z "$scan_merge_targets"; then - candidates="$candidates $(search_merge_targets $branch)" -fi - -candidates="$(git rev-parse $candidates | sort -u)" - -if test -z "$candidates"; then - hint= - test "z$all_strategies" != "ztttt" \ - && hint=" (maybe try again with -a)" - die "no candidates for $branch found$hint" -fi - -echo "** Candidates for $branch **" -for cmt in $candidates; do - git --no-pager log --pretty=tformat:"%ct:%h [%cr] %s" --abbrev-commit -1 $cmt -done \ -| sort -n | cut -d: -f2- - -newest="$(git rev-list -1 $candidates)" -if test ! -z "$dry_run"; then - printf "** Most recent: " - git --no-pager log -1 --pretty=tformat:"%h %s" $newest -elif ! git rev-parse --verify --quiet $new_name >/dev/null; then - printf "** Restoring $new_name to " - git --no-pager log -1 --pretty=tformat:"%h %s" $newest - git branch $new_name $newest -else - printf "Most recent: " - git --no-pager log -1 --pretty=tformat:"%h %s" $newest - echo "** $new_name already exists, doing nothing" -fi diff --git a/contrib/hooks/multimail/README.Git b/contrib/hooks/multimail/README.Git deleted file mode 100644 index c427efc7bd..0000000000 --- a/contrib/hooks/multimail/README.Git +++ /dev/null @@ -1,7 +0,0 @@ -git-multimail is developed as an independent project at the following -website: - - https://github.com/git-multimail/git-multimail - -Please refer to that project page for information about how to report -bugs or contribute to git-multimail. diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email deleted file mode 100755 index ff565eb3d8..0000000000 --- a/contrib/hooks/post-receive-email +++ /dev/null @@ -1,759 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2007 Andy Parkins -# -# An example hook script to mail out commit update information. -# -# NOTE: This script is no longer under active development. There -# is another script, git-multimail, which is more capable and -# configurable and is largely backwards-compatible with this script; -# please see "contrib/hooks/multimail/". For instructions on how to -# migrate from post-receive-email to git-multimail, please see -# "README.migrate-from-post-receive-email" in that directory. -# -# This hook sends emails listing new revisions to the repository -# introduced by the change being reported. The rule is that (for -# branch updates) each commit will appear on one email and one email -# only. -# -# This hook is stored in the contrib/hooks directory. Your distribution -# will have put this somewhere standard. You should make this script -# executable then link to it in the repository you would like to use it in. -# For example, on debian the hook is stored in -# /usr/share/git-core/contrib/hooks/post-receive-email: -# -# cd /path/to/your/repository.git -# ln -sf /usr/share/git-core/contrib/hooks/post-receive-email hooks/post-receive -# -# This hook script assumes it is enabled on the central repository of a -# project, with all users pushing only to it and not between each other. It -# will still work if you don't operate in that style, but it would become -# possible for the email to be from someone other than the person doing the -# push. -# -# To help with debugging and use on pre-v1.5.1 git servers, this script will -# also obey the interface of hooks/update, taking its arguments on the -# command line. Unfortunately, hooks/update is called once for each ref. -# To avoid firing one email per ref, this script just prints its output to -# the screen when used in this mode. The output can then be redirected if -# wanted. -# -# Config -# ------ -# hooks.mailinglist -# This is the list that all pushes will go to; leave it blank to not send -# emails for every ref update. -# hooks.announcelist -# This is the list that all pushes of annotated tags will go to. Leave it -# blank to default to the mailinglist field. The announce emails lists -# the short log summary of the changes since the last annotated tag. -# hooks.envelopesender -# If set then the -f option is passed to sendmail to allow the envelope -# sender address to be set -# hooks.emailprefix -# All emails have their subjects prefixed with this prefix, or "[SCM]" -# if emailprefix is unset, to aid filtering -# hooks.showrev -# The shell command used to format each revision in the email, with -# "%s" replaced with the commit id. Defaults to "git rev-list -1 -# --pretty %s", displaying the commit id, author, date and log -# message. To list full patches separated by a blank line, you -# could set this to "git show -C %s; echo". -# To list a gitweb/cgit URL *and* a full patch for each change set, use this: -# "t=%s; printf 'http://.../?id=%%s' \$t; echo;echo; git show -C \$t; echo" -# Be careful if "..." contains things that will be expanded by shell "eval" -# or printf. -# hooks.emailmaxlines -# The maximum number of lines that should be included in the generated -# email body. If not specified, there is no limit. -# Lines beyond the limit are suppressed and counted, and a final -# line is added indicating the number of suppressed lines. -# hooks.diffopts -# Alternate options for the git diff-tree invocation that shows changes. -# Default is "--stat --summary --find-copies-harder". Add -p to those -# options to include a unified diff of changes in addition to the usual -# summary output. -# -# Notes -# ----- -# All emails include the headers "X-Git-Refname", "X-Git-Oldrev", -# "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and -# give information for debugging. -# - -# ---------------------------- Functions - -# -# Function to prepare for email generation. This decides what type -# of update this is and whether an email should even be generated. -# -prep_for_email() -{ - # --- Arguments - oldrev=$(git rev-parse $1) - newrev=$(git rev-parse $2) - refname="$3" - - # --- Interpret - # 0000->1234 (create) - # 1234->2345 (update) - # 2345->0000 (delete) - if expr "$oldrev" : '0*$' >/dev/null - then - change_type="create" - else - if expr "$newrev" : '0*$' >/dev/null - then - change_type="delete" - else - change_type="update" - fi - fi - - # --- Get the revision types - newrev_type=$(git cat-file -t $newrev 2> /dev/null) - oldrev_type=$(git cat-file -t "$oldrev" 2> /dev/null) - case "$change_type" in - create|update) - rev="$newrev" - rev_type="$newrev_type" - ;; - delete) - rev="$oldrev" - rev_type="$oldrev_type" - ;; - esac - - # The revision type tells us what type the commit is, combined with - # the location of the ref we can decide between - # - working branch - # - tracking branch - # - unannoted tag - # - annotated tag - case "$refname","$rev_type" in - refs/tags/*,commit) - # un-annotated tag - refname_type="tag" - short_refname=${refname##refs/tags/} - ;; - refs/tags/*,tag) - # annotated tag - refname_type="annotated tag" - short_refname=${refname##refs/tags/} - # change recipients - if [ -n "$announcerecipients" ]; then - recipients="$announcerecipients" - fi - ;; - refs/heads/*,commit) - # branch - refname_type="branch" - short_refname=${refname##refs/heads/} - ;; - refs/remotes/*,commit) - # tracking branch - refname_type="tracking branch" - short_refname=${refname##refs/remotes/} - echo >&2 "*** Push-update of tracking branch, $refname" - echo >&2 "*** - no email generated." - return 1 - ;; - *) - # Anything else (is there anything else?) - echo >&2 "*** Unknown type of update to $refname ($rev_type)" - echo >&2 "*** - no email generated" - return 1 - ;; - esac - - # Check if we've got anyone to send to - if [ -z "$recipients" ]; then - case "$refname_type" in - "annotated tag") - config_name="hooks.announcelist" - ;; - *) - config_name="hooks.mailinglist" - ;; - esac - echo >&2 "*** $config_name is not set so no email will be sent" - echo >&2 "*** for $refname update $oldrev->$newrev" - return 1 - fi - - return 0 -} - -# -# Top level email generation function. This calls the appropriate -# body-generation routine after outputting the common header. -# -# Note this function doesn't actually generate any email output, that is -# taken care of by the functions it calls: -# - generate_email_header -# - generate_create_XXXX_email -# - generate_update_XXXX_email -# - generate_delete_XXXX_email -# - generate_email_footer -# -# Note also that this function cannot 'exit' from the script; when this -# function is running (in hook script mode), the send_mail() function -# is already executing in another process, connected via a pipe, and -# if this function exits without, whatever has been generated to that -# point will be sent as an email... even if nothing has been generated. -# -generate_email() -{ - # Email parameters - # The email subject will contain the best description of the ref - # that we can build from the parameters - describe=$(git describe $rev 2>/dev/null) - if [ -z "$describe" ]; then - describe=$rev - fi - - generate_email_header - - # Call the correct body generation function - fn_name=general - case "$refname_type" in - "tracking branch"|branch) - fn_name=branch - ;; - "annotated tag") - fn_name=atag - ;; - esac - - if [ -z "$maxlines" ]; then - generate_${change_type}_${fn_name}_email - else - generate_${change_type}_${fn_name}_email | limit_lines $maxlines - fi - - generate_email_footer -} - -generate_email_header() -{ - # --- Email (all stdout will be the email) - # Generate header - cat <<-EOF - To: $recipients - Subject: ${emailprefix}$projectdesc $refname_type $short_refname ${change_type}d. $describe - MIME-Version: 1.0 - Content-Type: text/plain; charset=utf-8 - Content-Transfer-Encoding: 8bit - X-Git-Refname: $refname - X-Git-Reftype: $refname_type - X-Git-Oldrev: $oldrev - X-Git-Newrev: $newrev - Auto-Submitted: auto-generated - - This is an automated email from the git hooks/post-receive script. It was - generated because a ref change was pushed to the repository containing - the project "$projectdesc". - - The $refname_type, $short_refname has been ${change_type}d - EOF -} - -generate_email_footer() -{ - SPACE=" " - cat <<-EOF - - - hooks/post-receive - --${SPACE} - $projectdesc - EOF -} - -# --------------- Branches - -# -# Called for the creation of a branch -# -generate_create_branch_email() -{ - # This is a new branch and so oldrev is not valid - echo " at $newrev ($newrev_type)" - echo "" - - echo $LOGBEGIN - show_new_revisions - echo $LOGEND -} - -# -# Called for the change of a pre-existing branch -# -generate_update_branch_email() -{ - # Consider this: - # 1 --- 2 --- O --- X --- 3 --- 4 --- N - # - # O is $oldrev for $refname - # N is $newrev for $refname - # X is a revision pointed to by some other ref, for which we may - # assume that an email has already been generated. - # In this case we want to issue an email containing only revisions - # 3, 4, and N. Given (almost) by - # - # git rev-list N ^O --not --all - # - # The reason for the "almost", is that the "--not --all" will take - # precedence over the "N", and effectively will translate to - # - # git rev-list N ^O ^X ^N - # - # So, we need to build up the list more carefully. git rev-parse - # will generate a list of revs that may be fed into git rev-list. - # We can get it to make the "--not --all" part and then filter out - # the "^N" with: - # - # git rev-parse --not --all | grep -v N - # - # Then, using the --stdin switch to git rev-list we have effectively - # manufactured - # - # git rev-list N ^O ^X - # - # This leaves a problem when someone else updates the repository - # while this script is running. Their new value of the ref we're - # working on would be included in the "--not --all" output; and as - # our $newrev would be an ancestor of that commit, it would exclude - # all of our commits. What we really want is to exclude the current - # value of $refname from the --not list, rather than N itself. So: - # - # git rev-parse --not --all | grep -v $(git rev-parse $refname) - # - # Gets us to something pretty safe (apart from the small time - # between refname being read, and git rev-parse running - for that, - # I give up) - # - # - # Next problem, consider this: - # * --- B --- * --- O ($oldrev) - # \ - # * --- X --- * --- N ($newrev) - # - # That is to say, there is no guarantee that oldrev is a strict - # subset of newrev (it would have required a --force, but that's - # allowed). So, we can't simply say rev-list $oldrev..$newrev. - # Instead we find the common base of the two revs and list from - # there. - # - # As above, we need to take into account the presence of X; if - # another branch is already in the repository and points at some of - # the revisions that we are about to output - we don't want them. - # The solution is as before: git rev-parse output filtered. - # - # Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N - # - # Tags pushed into the repository generate nice shortlog emails that - # summarise the commits between them and the previous tag. However, - # those emails don't include the full commit messages that we output - # for a branch update. Therefore we still want to output revisions - # that have been output on a tag email. - # - # Luckily, git rev-parse includes just the tool. Instead of using - # "--all" we use "--branches"; this has the added benefit that - # "remotes/" will be ignored as well. - - # List all of the revisions that were removed by this update, in a - # fast-forward update, this list will be empty, because rev-list O - # ^N is empty. For a non-fast-forward, O ^N is the list of removed - # revisions - fast_forward="" - rev="" - for rev in $(git rev-list $newrev..$oldrev) - do - revtype=$(git cat-file -t "$rev") - echo " discards $rev ($revtype)" - done - if [ -z "$rev" ]; then - fast_forward=1 - fi - - # List all the revisions from baserev to newrev in a kind of - # "table-of-contents"; note this list can include revisions that - # have already had notification emails and is present to show the - # full detail of the change from rolling back the old revision to - # the base revision and then forward to the new revision - for rev in $(git rev-list $oldrev..$newrev) - do - revtype=$(git cat-file -t "$rev") - echo " via $rev ($revtype)" - done - - if [ "$fast_forward" ]; then - echo " from $oldrev ($oldrev_type)" - else - # 1. Existing revisions were removed. In this case newrev - # is a subset of oldrev - this is the reverse of a - # fast-forward, a rewind - # 2. New revisions were added on top of an old revision, - # this is a rewind and addition. - - # (1) certainly happened, (2) possibly. When (2) hasn't - # happened, we set a flag to indicate that no log printout - # is required. - - echo "" - - # Find the common ancestor of the old and new revisions and - # compare it with newrev - baserev=$(git merge-base $oldrev $newrev) - rewind_only="" - if [ "$baserev" = "$newrev" ]; then - echo "This update discarded existing revisions and left the branch pointing at" - echo "a previous point in the repository history." - echo "" - echo " * -- * -- N ($newrev)" - echo " \\" - echo " O -- O -- O ($oldrev)" - echo "" - echo "The removed revisions are not necessarily gone - if another reference" - echo "still refers to them they will stay in the repository." - rewind_only=1 - else - echo "This update added new revisions after undoing existing revisions. That is" - echo "to say, the old revision is not a strict subset of the new revision. This" - echo "situation occurs when you --force push a change and generate a repository" - echo "containing something like this:" - echo "" - echo " * -- * -- B -- O -- O -- O ($oldrev)" - echo " \\" - echo " N -- N -- N ($newrev)" - echo "" - echo "When this happens we assume that you've already had alert emails for all" - echo "of the O revisions, and so we here report only the revisions in the N" - echo "branch from the common base, B." - fi - fi - - echo "" - if [ -z "$rewind_only" ]; then - echo "Those revisions listed above that are new to this repository have" - echo "not appeared on any other notification email; so we list those" - echo "revisions in full, below." - - echo "" - echo $LOGBEGIN - show_new_revisions - - # XXX: Need a way of detecting whether git rev-list actually - # outputted anything, so that we can issue a "no new - # revisions added by this update" message - - echo $LOGEND - else - echo "No new revisions were added by this update." - fi - - # The diffstat is shown from the old revision to the new revision. - # This is to show the truth of what happened in this change. - # There's no point showing the stat from the base to the new - # revision because the base is effectively a random revision at this - # point - the user will be interested in what this revision changed - # - including the undoing of previous revisions in the case of - # non-fast-forward updates. - echo "" - echo "Summary of changes:" - git diff-tree $diffopts $oldrev..$newrev -} - -# -# Called for the deletion of a branch -# -generate_delete_branch_email() -{ - echo " was $oldrev" - echo "" - echo $LOGBEGIN - git diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev - echo $LOGEND -} - -# --------------- Annotated tags - -# -# Called for the creation of an annotated tag -# -generate_create_atag_email() -{ - echo " at $newrev ($newrev_type)" - - generate_atag_email -} - -# -# Called for the update of an annotated tag (this is probably a rare event -# and may not even be allowed) -# -generate_update_atag_email() -{ - echo " to $newrev ($newrev_type)" - echo " from $oldrev (which is now obsolete)" - - generate_atag_email -} - -# -# Called when an annotated tag is created or changed -# -generate_atag_email() -{ - # Use git for-each-ref to pull out the individual fields from the - # tag - eval $(git for-each-ref --shell --format=' - tagobject=%(*objectname) - tagtype=%(*objecttype) - tagger=%(taggername) - tagged=%(taggerdate)' $refname - ) - - echo " tagging $tagobject ($tagtype)" - case "$tagtype" in - commit) - - # If the tagged object is a commit, then we assume this is a - # release, and so we calculate which tag this tag is - # replacing - prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null) - - if [ -n "$prevtag" ]; then - echo " replaces $prevtag" - fi - ;; - *) - echo " length $(git cat-file -s $tagobject) bytes" - ;; - esac - echo " tagged by $tagger" - echo " on $tagged" - - echo "" - echo $LOGBEGIN - - # Show the content of the tag message; this might contain a change - # log or release notes so is worth displaying. - git cat-file tag $newrev | sed -e '1,/^$/d' - - echo "" - case "$tagtype" in - commit) - # Only commit tags make sense to have rev-list operations - # performed on them - if [ -n "$prevtag" ]; then - # Show changes since the previous release - git shortlog "$prevtag..$newrev" - else - # No previous tag, show all the changes since time - # began - git shortlog $newrev - fi - ;; - *) - # XXX: Is there anything useful we can do for non-commit - # objects? - ;; - esac - - echo $LOGEND -} - -# -# Called for the deletion of an annotated tag -# -generate_delete_atag_email() -{ - echo " was $oldrev" - echo "" - echo $LOGBEGIN - git diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev - echo $LOGEND -} - -# --------------- General references - -# -# Called when any other type of reference is created (most likely a -# non-annotated tag) -# -generate_create_general_email() -{ - echo " at $newrev ($newrev_type)" - - generate_general_email -} - -# -# Called when any other type of reference is updated (most likely a -# non-annotated tag) -# -generate_update_general_email() -{ - echo " to $newrev ($newrev_type)" - echo " from $oldrev" - - generate_general_email -} - -# -# Called for creation or update of any other type of reference -# -generate_general_email() -{ - # Unannotated tags are more about marking a point than releasing a - # version; therefore we don't do the shortlog summary that we do for - # annotated tags above - we simply show that the point has been - # marked, and print the log message for the marked point for - # reference purposes - # - # Note this section also catches any other reference type (although - # there aren't any) and deals with them in the same way. - - echo "" - if [ "$newrev_type" = "commit" ]; then - echo $LOGBEGIN - git diff-tree -s --always --encoding=UTF-8 --pretty=medium $newrev - echo $LOGEND - else - # What can we do here? The tag marks an object that is not - # a commit, so there is no log for us to display. It's - # probably not wise to output git cat-file as it could be a - # binary blob. We'll just say how big it is - echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long." - fi -} - -# -# Called for the deletion of any other type of reference -# -generate_delete_general_email() -{ - echo " was $oldrev" - echo "" - echo $LOGBEGIN - git diff-tree -s --always --encoding=UTF-8 --pretty=oneline $oldrev - echo $LOGEND -} - - -# --------------- Miscellaneous utilities - -# -# Show new revisions as the user would like to see them in the email. -# -show_new_revisions() -{ - # This shows all log entries that are not already covered by - # another ref - i.e. commits that are now accessible from this - # ref that were previously not accessible - # (see generate_update_branch_email for the explanation of this - # command) - - # Revision range passed to rev-list differs for new vs. updated - # branches. - if [ "$change_type" = create ] - then - # Show all revisions exclusive to this (new) branch. - revspec=$newrev - else - # Branch update; show revisions not part of $oldrev. - revspec=$oldrev..$newrev - fi - - other_branches=$(git for-each-ref --format='%(refname)' refs/heads/ | - grep -F -v $refname) - git rev-parse --not $other_branches | - if [ -z "$custom_showrev" ] - then - git rev-list --pretty --stdin $revspec - else - git rev-list --stdin $revspec | - while read onerev - do - eval $(printf "$custom_showrev" $onerev) - done - fi -} - - -limit_lines() -{ - lines=0 - skipped=0 - while IFS="" read -r line; do - lines=$((lines + 1)) - if [ $lines -gt $1 ]; then - skipped=$((skipped + 1)) - else - printf "%s\n" "$line" - fi - done - if [ $skipped -ne 0 ]; then - echo "... $skipped lines suppressed ..." - fi -} - - -send_mail() -{ - if [ -n "$envelopesender" ]; then - /usr/sbin/sendmail -t -f "$envelopesender" - else - /usr/sbin/sendmail -t - fi -} - -# ---------------------------- main() - -# --- Constants -LOGBEGIN="- Log -----------------------------------------------------------------" -LOGEND="-----------------------------------------------------------------------" - -# --- Config -# Set GIT_DIR either from the working directory, or from the environment -# variable. -GIT_DIR=$(git rev-parse --git-dir 2>/dev/null) -if [ -z "$GIT_DIR" ]; then - echo >&2 "fatal: post-receive: GIT_DIR not set" - exit 1 -fi - -projectdesc=$(sed -ne '1p' "$GIT_DIR/description" 2>/dev/null) -# Check if the description is unchanged from it's default, and shorten it to -# a more manageable length if it is -if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null -then - projectdesc="UNNAMED PROJECT" -fi - -recipients=$(git config hooks.mailinglist) -announcerecipients=$(git config hooks.announcelist) -envelopesender=$(git config hooks.envelopesender) -emailprefix=$(git config hooks.emailprefix || echo '[SCM] ') -custom_showrev=$(git config hooks.showrev) -maxlines=$(git config hooks.emailmaxlines) -diffopts=$(git config hooks.diffopts) -: ${diffopts:="--stat --summary --find-copies-harder"} - -# --- Main loop -# Allow dual mode: run from the command line just like the update hook, or -# if no arguments are given then run as a hook script -if [ -n "$1" -a -n "$2" -a -n "$3" ]; then - # Output to the terminal in command line mode - if someone wanted to - # resend an email; they could redirect the output to sendmail - # themselves - prep_for_email $2 $3 $1 && PAGER= generate_email -else - while read oldrev newrev refname - do - prep_for_email $oldrev $newrev $refname || continue - generate_email $maxlines | send_mail - done -fi diff --git a/contrib/hooks/pre-auto-gc-battery b/contrib/hooks/pre-auto-gc-battery deleted file mode 100755 index 7ba78c4dff..0000000000 --- a/contrib/hooks/pre-auto-gc-battery +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/sh -# -# An example hook script to verify if you are on battery, in case you -# are running Linux or OS X. Called by git-gc --auto with no arguments. -# The hook should exit with non-zero status after issuing an appropriate -# message if it wants to stop the auto repacking. -# -# This hook is stored in the contrib/hooks directory. Your distribution -# may have put this somewhere else. If you want to use this hook, you -# should make this script executable then link to it in the repository -# you would like to use it in. -# -# For example, if the hook is stored in -# /usr/share/git-core/contrib/hooks/pre-auto-gc-battery: -# -# cd /path/to/your/repository.git -# ln -sf /usr/share/git-core/contrib/hooks/pre-auto-gc-battery \ -# hooks/pre-auto-gc - -if test -x /sbin/on_ac_power && (/sbin/on_ac_power;test $? -ne 1) -then - exit 0 -elif test "$(cat /sys/class/power_supply/AC/online 2>/dev/null)" = 1 -then - exit 0 -elif grep -q 'on-line' /proc/acpi/ac_adapter/AC/state 2>/dev/null -then - exit 0 -elif grep -q '0x01$' /proc/apm 2>/dev/null -then - exit 0 -elif grep -q "AC Power \+: 1" /proc/pmu/info 2>/dev/null -then - exit 0 -elif test -x /usr/bin/pmset && /usr/bin/pmset -g batt | - grep -q "drawing from 'AC Power'" -then - exit 0 -fi - -echo "Auto packing deferred; not on AC" -exit 1 diff --git a/contrib/hooks/setgitperms.perl b/contrib/hooks/setgitperms.perl deleted file mode 100755 index 2770a1b1d2..0000000000 --- a/contrib/hooks/setgitperms.perl +++ /dev/null @@ -1,214 +0,0 @@ -#!/usr/bin/perl -# -# Copyright (c) 2006 Josh England -# -# This script can be used to save/restore full permissions and ownership data -# within a git working tree. -# -# To save permissions/ownership data, place this script in your .git/hooks -# directory and enable a `pre-commit` hook with the following lines: -# #!/bin/sh -# SUBDIRECTORY_OK=1 . git-sh-setup -# $GIT_DIR/hooks/setgitperms.perl -r -# -# To restore permissions/ownership data, place this script in your .git/hooks -# directory and enable a `post-merge` and `post-checkout` hook with the -# following lines: -# #!/bin/sh -# SUBDIRECTORY_OK=1 . git-sh-setup -# $GIT_DIR/hooks/setgitperms.perl -w -# -use strict; -use Getopt::Long; -use File::Find; -use File::Basename; - -my $usage = -"usage: setgitperms.perl [OPTION]... <--read|--write> -This program uses a file `.gitmeta` to store/restore permissions and uid/gid -info for all files/dirs tracked by git in the repository. - ----------------------------------Read Mode------------------------------------- --r, --read Reads perms/etc from working dir into a .gitmeta file --s, --stdout Output to stdout instead of .gitmeta --d, --diff Show unified diff of perms file (XOR with --stdout) - ----------------------------------Write Mode------------------------------------ --w, --write Modify perms/etc in working dir to match the .gitmeta file --v, --verbose Be verbose - -\n"; - -my ($stdout, $showdiff, $verbose, $read_mode, $write_mode); - -if ((@ARGV < 0) || !GetOptions( - "stdout", \$stdout, - "diff", \$showdiff, - "read", \$read_mode, - "write", \$write_mode, - "verbose", \$verbose, - )) { die $usage; } -die $usage unless ($read_mode xor $write_mode); - -my $topdir = `git rev-parse --show-cdup` or die "\n"; chomp $topdir; -my $gitdir = $topdir . '.git'; -my $gitmeta = $topdir . '.gitmeta'; - -if ($write_mode) { - # Update the working dir permissions/ownership based on data from .gitmeta - open (IN, "<$gitmeta") or die "Could not open $gitmeta for reading: $!\n"; - while (defined ($_ = <IN>)) { - chomp; - if (/^(.*) mode=(\S+)\s+uid=(\d+)\s+gid=(\d+)/) { - # Compare recorded perms to actual perms in the working dir - my ($path, $mode, $uid, $gid) = ($1, $2, $3, $4); - my $fullpath = $topdir . $path; - my (undef,undef,$wmode,undef,$wuid,$wgid) = lstat($fullpath); - $wmode = sprintf "%04o", $wmode & 07777; - if ($mode ne $wmode) { - $verbose && print "Updating permissions on $path: old=$wmode, new=$mode\n"; - chmod oct($mode), $fullpath; - } - if ($uid != $wuid || $gid != $wgid) { - if ($verbose) { - # Print out user/group names instead of uid/gid - my $pwname = getpwuid($uid); - my $grpname = getgrgid($gid); - my $wpwname = getpwuid($wuid); - my $wgrpname = getgrgid($wgid); - $pwname = $uid if !defined $pwname; - $grpname = $gid if !defined $grpname; - $wpwname = $wuid if !defined $wpwname; - $wgrpname = $wgid if !defined $wgrpname; - - print "Updating uid/gid on $path: old=$wpwname/$wgrpname, new=$pwname/$grpname\n"; - } - chown $uid, $gid, $fullpath; - } - } - else { - warn "Invalid input format in $gitmeta:\n\t$_\n"; - } - } - close IN; -} -elsif ($read_mode) { - # Handle merge conflicts in the .gitperms file - if (-e "$gitdir/MERGE_MSG") { - if (`grep ====== $gitmeta`) { - # Conflict not resolved -- abort the commit - print "PERMISSIONS/OWNERSHIP CONFLICT\n"; - print " Resolve the conflict in the $gitmeta file and then run\n"; - print " `.git/hooks/setgitperms.perl --write` to reconcile.\n"; - exit 1; - } - elsif (`grep $gitmeta $gitdir/MERGE_MSG`) { - # A conflict in .gitmeta has been manually resolved. Verify that - # the working dir perms matches the current .gitmeta perms for - # each file/dir that conflicted. - # This is here because a `setgitperms.perl --write` was not - # performed due to a merge conflict, so permissions/ownership - # may not be consistent with the manually merged .gitmeta file. - my @conflict_diff = `git show \$(cat $gitdir/MERGE_HEAD)`; - my @conflict_files; - my $metadiff = 0; - - # Build a list of files that conflicted from the .gitmeta diff - foreach my $line (@conflict_diff) { - if ($line =~ m|^diff --git a/$gitmeta b/$gitmeta|) { - $metadiff = 1; - } - elsif ($line =~ /^diff --git/) { - $metadiff = 0; - } - elsif ($metadiff && $line =~ /^\+(.*) mode=/) { - push @conflict_files, $1; - } - } - - # Verify that each conflict file now has permissions consistent - # with the .gitmeta file - foreach my $file (@conflict_files) { - my $absfile = $topdir . $file; - my $gm_entry = `grep "^$file mode=" $gitmeta`; - if ($gm_entry =~ /mode=(\d+) uid=(\d+) gid=(\d+)/) { - my ($gm_mode, $gm_uid, $gm_gid) = ($1, $2, $3); - my (undef,undef,$mode,undef,$uid,$gid) = lstat("$absfile"); - $mode = sprintf("%04o", $mode & 07777); - if (($gm_mode ne $mode) || ($gm_uid != $uid) - || ($gm_gid != $gid)) { - print "PERMISSIONS/OWNERSHIP CONFLICT\n"; - print " Mismatch found for file: $file\n"; - print " Run `.git/hooks/setgitperms.perl --write` to reconcile.\n"; - exit 1; - } - } - else { - print "Warning! Permissions/ownership no longer being tracked for file: $file\n"; - } - } - } - } - - # No merge conflicts -- write out perms/ownership data to .gitmeta file - unless ($stdout) { - open (OUT, ">$gitmeta.tmp") or die "Could not open $gitmeta.tmp for writing: $!\n"; - } - - my @files = `git ls-files`; - my %dirs; - - foreach my $path (@files) { - chomp $path; - # We have to manually add stats for parent directories - my $parent = dirname($path); - while (!exists $dirs{$parent}) { - $dirs{$parent} = 1; - next if $parent eq '.'; - printstats($parent); - $parent = dirname($parent); - } - # Now the git-tracked file - printstats($path); - } - - # diff the temporary metadata file to see if anything has changed - # If no metadata has changed, don't overwrite the real file - # This is just so `git commit -a` doesn't try to commit a bogus update - unless ($stdout) { - if (! -e $gitmeta) { - rename "$gitmeta.tmp", $gitmeta; - } - else { - my $diff = `diff -U 0 $gitmeta $gitmeta.tmp`; - if ($diff ne '') { - rename "$gitmeta.tmp", $gitmeta; - } - else { - unlink "$gitmeta.tmp"; - } - if ($showdiff) { - print $diff; - } - } - close OUT; - } - # Make sure the .gitmeta file is tracked - system("git add $gitmeta"); -} - - -sub printstats { - my $path = $_[0]; - $path =~ s/@/\@/g; - my (undef,undef,$mode,undef,$uid,$gid) = lstat($path); - $path =~ s/%/\%/g; - if ($stdout) { - print $path; - printf " mode=%04o uid=$uid gid=$gid\n", $mode & 07777; - } - else { - print OUT $path; - printf OUT " mode=%04o uid=$uid gid=$gid\n", $mode & 07777; - } -} diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid deleted file mode 100755 index 0092d67b8a..0000000000 --- a/contrib/hooks/update-paranoid +++ /dev/null @@ -1,421 +0,0 @@ -#!/usr/bin/perl - -use strict; -use File::Spec; - -$ENV{PATH} = '/opt/git/bin'; -my $acl_git = '/vcs/acls.git'; -my $acl_branch = 'refs/heads/master'; -my $debug = 0; - -=doc -Invoked as: update refname old-sha1 new-sha1 - -This script is run by git-receive-pack once for each ref that the -client is trying to modify. If we exit with a non-zero exit value -then the update for that particular ref is denied, but updates for -other refs in the same run of receive-pack may still be allowed. - -We are run after the objects have been uploaded, but before the -ref is actually modified. We take advantage of that fact when we -look for "new" commits and tags (the new objects won't show up in -`rev-list --all`). - -This script loads and parses the content of the config file -"users/$this_user.acl" from the $acl_branch commit of $acl_git ODB. -The acl file is a git-config style file, but uses a slightly more -restricted syntax as the Perl parser contained within this script -is not nearly as permissive as git-config. - -Example: - - [user] - committer = John Doe <john.doe@example.com> - committer = John R. Doe <john.doe@example.com> - - [repository "acls"] - allow = heads/master - allow = CDUR for heads/jd/ - allow = C for ^tags/v\\d+$ - -For all new commit or tag objects the committer (or tagger) line -within the object must exactly match one of the user.committer -values listed in the acl file ("HEAD:users/$this_user.acl"). - -For a branch to be modified an allow line within the matching -repository section must be matched for both the refname and the -opcode. - -Repository sections are matched on the basename of the repository -(after removing the .git suffix). - -The opcode abbreviations are: - - C: create new ref - D: delete existing ref - U: fast-forward existing ref (no commit loss) - R: rewind/rebase existing ref (commit loss) - -if no opcodes are listed before the "for" keyword then "U" (for -fast-forward update only) is assumed as this is the most common -usage. - -Refnames are matched by always assuming a prefix of "refs/". -This hook forbids pushing or deleting anything not under "refs/". - -Refnames that start with ^ are Perl regular expressions, and the ^ -is kept as part of the regexp. \\ is needed to get just one \, so -\\d expands to \d in Perl. The 3rd allow line above is an example. - -Refnames that don't start with ^ but that end with / are prefix -matches (2nd allow line above); all other refnames are strict -equality matches (1st allow line). - -Anything pushed to "heads/" (ok, really "refs/heads/") must be -a commit. Tags are not permitted here. - -Anything pushed to "tags/" (err, really "refs/tags/") must be an -annotated tag. Commits, blobs, trees, etc. are not permitted here. -Annotated tag signatures aren't checked, nor are they required. - -The special subrepository of 'info/new-commit-check' can -be created and used to allow users to push new commits and -tags from another local repository to this one, even if they -aren't the committer/tagger of those objects. In a nut shell -the info/new-commit-check directory is a Git repository whose -objects/info/alternates file lists this repository and all other -possible sources, and whose refs subdirectory contains symlinks -to this repository's refs subdirectory, and to all other possible -sources refs subdirectories. Yes, this means that you cannot -use packed-refs in those repositories as they won't be resolved -correctly. - -=cut - -my $git_dir = $ENV{GIT_DIR}; -my $new_commit_check = "$git_dir/info/new-commit-check"; -my $ref = $ARGV[0]; -my $old = $ARGV[1]; -my $new = $ARGV[2]; -my $new_type; -my ($this_user) = getpwuid $<; # REAL_USER_ID -my $repository_name; -my %user_committer; -my @allow_rules; -my @path_rules; -my %diff_cache; - -sub deny ($) { - print STDERR "-Deny- $_[0]\n" if $debug; - print STDERR "\ndenied: $_[0]\n\n"; - exit 1; -} - -sub grant ($) { - print STDERR "-Grant- $_[0]\n" if $debug; - exit 0; -} - -sub info ($) { - print STDERR "-Info- $_[0]\n" if $debug; -} - -sub git_value (@) { - open(T,'-|','git',@_); local $_ = <T>; chop; close T; $_; -} - -sub match_string ($$) { - my ($acl_n, $ref) = @_; - ($acl_n eq $ref) - || ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n) - || ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:); -} - -sub parse_config ($$$$) { - my $data = shift; - local $ENV{GIT_DIR} = shift; - my $br = shift; - my $fn = shift; - return unless git_value('rev-list','--max-count=1',$br,'--',$fn); - info "Loading $br:$fn"; - open(I,'-|','git','cat-file','blob',"$br:$fn"); - my $section = ''; - while (<I>) { - chomp; - if (/^\s*$/ || /^\s*#/) { - } elsif (/^\[([a-z]+)\]$/i) { - $section = lc $1; - } elsif (/^\[([a-z]+)\s+"(.*)"\]$/i) { - $section = join('.',lc $1,$2); - } elsif (/^\s*([a-z][a-z0-9]+)\s*=\s*(.*?)\s*$/i) { - push @{$data->{join('.',$section,lc $1)}}, $2; - } else { - deny "bad config file line $. in $br:$fn"; - } - } - close I; -} - -sub all_new_committers () { - local $ENV{GIT_DIR} = $git_dir; - $ENV{GIT_DIR} = $new_commit_check if -d $new_commit_check; - - info "Getting committers of new commits."; - my %used; - open(T,'-|','git','rev-list','--pretty=raw',$new,'--not','--all'); - while (<T>) { - next unless s/^committer //; - chop; - s/>.*$/>/; - info "Found $_." unless $used{$_}++; - } - close T; - info "No new commits." unless %used; - keys %used; -} - -sub all_new_taggers () { - my %exists; - open(T,'-|','git','for-each-ref','--format=%(objectname)','refs/tags'); - while (<T>) { - chop; - $exists{$_} = 1; - } - close T; - - info "Getting taggers of new tags."; - my %used; - my $obj = $new; - my $obj_type = $new_type; - while ($obj_type eq 'tag') { - last if $exists{$obj}; - $obj_type = ''; - open(T,'-|','git','cat-file','tag',$obj); - while (<T>) { - chop; - if (/^object ([a-z0-9]{40})$/) { - $obj = $1; - } elsif (/^type (.+)$/) { - $obj_type = $1; - } elsif (s/^tagger //) { - s/>.*$/>/; - info "Found $_." unless $used{$_}++; - last; - } - } - close T; - } - info "No new tags." unless %used; - keys %used; -} - -sub check_committers (@) { - my @bad; - foreach (@_) { push @bad, $_ unless $user_committer{$_}; } - if (@bad) { - print STDERR "\n"; - print STDERR "You are not $_.\n" foreach (sort @bad); - deny "You cannot push changes not committed by you."; - } -} - -sub load_diff ($) { - my $base = shift; - my $d = $diff_cache{$base}; - unless ($d) { - local $/ = "\0"; - my %this_diff; - if ($base =~ /^0{40}$/) { - # Don't load the diff at all; we are making the - # branch and have no base to compare to in this - # case. A file level ACL makes no sense in this - # context. Having an empty diff will allow the - # branch creation. - # - } else { - open(T,'-|','git','diff-tree', - '-r','--name-status','-z', - $base,$new) or return undef; - while (<T>) { - my $op = $_; - chop $op; - - my $path = <T>; - chop $path; - - $this_diff{$path} = $op; - } - close T or return undef; - } - $d = \%this_diff; - $diff_cache{$base} = $d; - } - return $d; -} - -deny "No GIT_DIR inherited from caller" unless $git_dir; -deny "Need a ref name" unless $ref; -deny "Refusing funny ref $ref" unless $ref =~ s,^refs/,,; -deny "Bad old value $old" unless $old =~ /^[a-z0-9]{40}$/; -deny "Bad new value $new" unless $new =~ /^[a-z0-9]{40}$/; -deny "Cannot determine who you are." unless $this_user; -grant "No change requested." if $old eq $new; - -$repository_name = File::Spec->rel2abs($git_dir); -$repository_name =~ m,/([^/]+)(?:\.git|/\.git)$,; -$repository_name = $1; -info "Updating in '$repository_name'."; - -my $op; -if ($old =~ /^0{40}$/) { $op = 'C'; } -elsif ($new =~ /^0{40}$/) { $op = 'D'; } -else { $op = 'R'; } - -# This is really an update (fast-forward) if the -# merge base of $old and $new is $old. -# -$op = 'U' if ($op eq 'R' - && $ref =~ m,^heads/, - && $old eq git_value('merge-base',$old,$new)); - -# Load the user's ACL file. Expand groups (user.memberof) one level. -{ - my %data = ('user.committer' => []); - parse_config(\%data,$acl_git,$acl_branch,"external/$repository_name.acl"); - - %data = ( - 'user.committer' => $data{'user.committer'}, - 'user.memberof' => [], - ); - parse_config(\%data,$acl_git,$acl_branch,"users/$this_user.acl"); - - %user_committer = map {$_ => $_} @{$data{'user.committer'}}; - my $rule_key = "repository.$repository_name.allow"; - my $rules = $data{$rule_key} || []; - - foreach my $group (@{$data{'user.memberof'}}) { - my %g; - parse_config(\%g,$acl_git,$acl_branch,"groups/$group.acl"); - my $group_rules = $g{$rule_key}; - push @$rules, @$group_rules if $group_rules; - } - -RULE: - foreach (@$rules) { - while (/\${user\.([a-z][a-zA-Z0-9]+)}/) { - my $k = lc $1; - my $v = $data{"user.$k"}; - next RULE unless defined $v; - next RULE if @$v != 1; - next RULE unless defined $v->[0]; - s/\${user\.$k}/$v->[0]/g; - } - - if (/^([AMD ]+)\s+of\s+([^\s]+)\s+for\s+([^\s]+)\s+diff\s+([^\s]+)$/) { - my ($ops, $pth, $ref, $bst) = ($1, $2, $3, $4); - $ops =~ s/ //g; - $pth =~ s/\\\\/\\/g; - $ref =~ s/\\\\/\\/g; - push @path_rules, [$ops, $pth, $ref, $bst]; - } elsif (/^([AMD ]+)\s+of\s+([^\s]+)\s+for\s+([^\s]+)$/) { - my ($ops, $pth, $ref) = ($1, $2, $3); - $ops =~ s/ //g; - $pth =~ s/\\\\/\\/g; - $ref =~ s/\\\\/\\/g; - push @path_rules, [$ops, $pth, $ref, $old]; - } elsif (/^([CDRU ]+)\s+for\s+([^\s]+)$/) { - my $ops = $1; - my $ref = $2; - $ops =~ s/ //g; - $ref =~ s/\\\\/\\/g; - push @allow_rules, [$ops, $ref]; - } elsif (/^for\s+([^\s]+)$/) { - # Mentioned, but nothing granted? - } elsif (/^[^\s]+$/) { - s/\\\\/\\/g; - push @allow_rules, ['U', $_]; - } - } -} - -if ($op ne 'D') { - $new_type = git_value('cat-file','-t',$new); - - if ($ref =~ m,^heads/,) { - deny "$ref must be a commit." unless $new_type eq 'commit'; - } elsif ($ref =~ m,^tags/,) { - deny "$ref must be an annotated tag." unless $new_type eq 'tag'; - } - - check_committers (all_new_committers); - check_committers (all_new_taggers) if $new_type eq 'tag'; -} - -info "$this_user wants $op for $ref"; -foreach my $acl_entry (@allow_rules) { - my ($acl_ops, $acl_n) = @$acl_entry; - next unless $acl_ops =~ /^[CDRU]+$/; # Uhh.... shouldn't happen. - next unless $acl_n; - next unless $op =~ /^[$acl_ops]$/; - next unless match_string $acl_n, $ref; - - # Don't test path rules on branch deletes. - # - grant "Allowed by: $acl_ops for $acl_n" if $op eq 'D'; - - # Aggregate matching path rules; allow if there aren't - # any matching this ref. - # - my %pr; - foreach my $p_entry (@path_rules) { - my ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry; - next unless $p_ref; - push @{$pr{$p_bst}}, $p_entry if match_string $p_ref, $ref; - } - grant "Allowed by: $acl_ops for $acl_n" unless %pr; - - # Allow only if all changes against a single base are - # allowed by file path rules. - # - my @bad; - foreach my $p_bst (keys %pr) { - my $diff_ref = load_diff $p_bst; - deny "Cannot difference trees." unless ref $diff_ref; - - my %fd = %$diff_ref; - foreach my $p_entry (@{$pr{$p_bst}}) { - my ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry; - next unless $p_ops =~ /^[AMD]+$/; - next unless $p_n; - - foreach my $f_n (keys %fd) { - my $f_op = $fd{$f_n}; - next unless $f_op; - next unless $f_op =~ /^[$p_ops]$/; - delete $fd{$f_n} if match_string $p_n, $f_n; - } - last unless %fd; - } - - if (%fd) { - push @bad, [$p_bst, \%fd]; - } else { - # All changes relative to $p_bst were allowed. - # - grant "Allowed by: $acl_ops for $acl_n diff $p_bst"; - } - } - - foreach my $bad_ref (@bad) { - my ($p_bst, $fd) = @$bad_ref; - print STDERR "\n"; - print STDERR "Not allowed to make the following changes:\n"; - print STDERR "(base: $p_bst)\n"; - foreach my $f_n (sort keys %$fd) { - print STDERR " $fd->{$f_n} $f_n\n"; - } - } - deny "You are not permitted to $op $ref"; -} -close A; -deny "You are not permitted to $op $ref"; diff --git a/contrib/mw-to-git/.gitignore b/contrib/mw-to-git/.gitignore deleted file mode 100644 index ae545b013d..0000000000 --- a/contrib/mw-to-git/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -git-remote-mediawiki -git-mw diff --git a/contrib/mw-to-git/.perlcriticrc b/contrib/mw-to-git/.perlcriticrc deleted file mode 100644 index b7333267ad..0000000000 --- a/contrib/mw-to-git/.perlcriticrc +++ /dev/null @@ -1,28 +0,0 @@ -# These 3 rules demand to add the s, m and x flag to *every* regexp. This is -# overkill and would be harmful for readability. -[-RegularExpressions::RequireExtendedFormatting] -[-RegularExpressions::RequireDotMatchAnything] -[-RegularExpressions::RequireLineBoundaryMatching] - -# This rule says that builtin functions should not be called with parentheses -# e.g.: (taken from CPAN's documentation) -# open($handle, '>', $filename); #not ok -# open $handle, '>', $filename; #ok -# Applying such a rule would mean modifying a huge number of lines for a -# question of style. -[-CodeLayout::ProhibitParensWithBuiltins] - -# This rule states that each system call should have its return value checked -# The problem is that it includes the print call. Checking every print call's -# return value would be harmful to the code readability. -# This configuration keeps all default function but print. -[InputOutput::RequireCheckedSyscalls] -functions = open say close - -# This rule demands to add a dependency for the Readonly module. This is not -# wished. -[-ValuesAndExpressions::ProhibitConstantPragma] - -# This rule is not really useful (rather a question of style) and produces many -# warnings among the code. -[-ValuesAndExpressions::ProhibitNoisyQuotes] diff --git a/contrib/mw-to-git/Git/Mediawiki.pm b/contrib/mw-to-git/Git/Mediawiki.pm deleted file mode 100644 index 629c0cea44..0000000000 --- a/contrib/mw-to-git/Git/Mediawiki.pm +++ /dev/null @@ -1,101 +0,0 @@ -package Git::Mediawiki; - -require v5.26; -use strict; -use POSIX; -use Git; - -BEGIN { - -our ($VERSION, @ISA, @EXPORT, @EXPORT_OK); - -# Totally unstable API. -$VERSION = '0.01'; - -require Exporter; - -@ISA = qw(Exporter); - -@EXPORT = (); - -# Methods which can be called as standalone functions as well: -@EXPORT_OK = qw(clean_filename smudge_filename connect_maybe - EMPTY HTTP_CODE_OK HTTP_CODE_PAGE_NOT_FOUND); -} - -# Mediawiki filenames can contain forward slashes. This variable decides by which pattern they should be replaced -use constant SLASH_REPLACEMENT => '%2F'; - -# Used to test for empty strings -use constant EMPTY => q{}; - -# HTTP codes -use constant HTTP_CODE_OK => 200; -use constant HTTP_CODE_PAGE_NOT_FOUND => 404; - -sub clean_filename { - my $filename = shift; - $filename =~ s{@{[SLASH_REPLACEMENT]}}{/}g; - # [, ], |, {, and } are forbidden by MediaWiki, even URL-encoded. - # Do a variant of URL-encoding, i.e. looks like URL-encoding, - # but with _ added to prevent MediaWiki from thinking this is - # an actual special character. - $filename =~ s/[\[\]\{\}\|]/sprintf("_%%_%x", ord($&))/ge; - # If we use the uri escape before - # we should unescape here, before anything - - return $filename; -} - -sub smudge_filename { - my $filename = shift; - $filename =~ s{/}{@{[SLASH_REPLACEMENT]}}g; - $filename =~ s/ /_/g; - # Decode forbidden characters encoded in clean_filename - $filename =~ s/_%_([0-9a-fA-F][0-9a-fA-F])/sprintf('%c', hex($1))/ge; - return substr($filename, 0, NAME_MAX-length('.mw')); -} - -sub connect_maybe { - my $wiki = shift; - if ($wiki) { - return $wiki; - } - - my $remote_name = shift; - my $remote_url = shift; - my ($wiki_login, $wiki_password, $wiki_domain); - - $wiki_login = Git::config("remote.${remote_name}.mwLogin"); - $wiki_password = Git::config("remote.${remote_name}.mwPassword"); - $wiki_domain = Git::config("remote.${remote_name}.mwDomain"); - - $wiki = MediaWiki::API->new; - $wiki->{config}->{api_url} = "${remote_url}/api.php"; - if ($wiki_login) { - my %credential = ( - 'url' => $remote_url, - 'username' => $wiki_login, - 'password' => $wiki_password - ); - Git::credential(\%credential); - my $request = {lgname => $credential{username}, - lgpassword => $credential{password}, - lgdomain => $wiki_domain}; - if ($wiki->login($request)) { - Git::credential(\%credential, 'approve'); - print {*STDERR} qq(Logged in mediawiki user "$credential{username}".\n); - } else { - print {*STDERR} qq(Failed to log in mediawiki user "$credential{username}" on ${remote_url}\n); - print {*STDERR} ' (error ' . - $wiki->{error}->{code} . ': ' . - $wiki->{error}->{details} . ")\n"; - Git::credential(\%credential, 'reject'); - exit 1; - } - } - - return $wiki; -} - -1; # Famous last words diff --git a/contrib/mw-to-git/Makefile b/contrib/mw-to-git/Makefile deleted file mode 100644 index 497ac434d6..0000000000 --- a/contrib/mw-to-git/Makefile +++ /dev/null @@ -1,61 +0,0 @@ -# -# Copyright (C) 2013 -# Matthieu Moy <Matthieu.Moy@imag.fr> -# -# To build and test: -# -# make -# bin-wrapper/git mw preview Some_page.mw -# bin-wrapper/git clone mediawiki::http://example.com/wiki/ -# -# To install, run Git's toplevel 'make install' then run: -# -# make install - -# The default target of this Makefile is... -all:: - -GIT_MEDIAWIKI_PM=Git/Mediawiki.pm -SCRIPT_PERL=git-remote-mediawiki.perl -SCRIPT_PERL+=git-mw.perl -GIT_ROOT_DIR=../.. -HERE=contrib/mw-to-git/ - -INSTALL = install - -SCRIPT_PERL_FULL=$(patsubst %,$(HERE)/%,$(SCRIPT_PERL)) -INSTLIBDIR=$(shell $(MAKE) -C $(GIT_ROOT_DIR)/ \ - -s --no-print-directory prefix=$(prefix) \ - perllibdir=$(perllibdir) perllibdir) -DESTDIR_SQ = $(subst ','\'',$(DESTDIR)) -INSTLIBDIR_SQ = $(subst ','\'',$(INSTLIBDIR)) - -all:: build - -test: all - $(MAKE) -C t - -check: perlcritic test - -install_pm: - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(INSTLIBDIR_SQ)/Git' - $(INSTALL) -m 644 $(GIT_MEDIAWIKI_PM) \ - '$(DESTDIR_SQ)$(INSTLIBDIR_SQ)/$(GIT_MEDIAWIKI_PM)' - -build: - $(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL="$(SCRIPT_PERL_FULL)" \ - build-perl-script - -install: install_pm - $(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL="$(SCRIPT_PERL_FULL)" \ - install-perl-script - -clean: - $(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL="$(SCRIPT_PERL_FULL)" \ - clean-perl-script - -perlcritic: - perlcritic -5 $(SCRIPT_PERL) - -perlcritic -2 $(SCRIPT_PERL) - -.PHONY: all test check install_pm install clean perlcritic diff --git a/contrib/mw-to-git/bin-wrapper/git b/contrib/mw-to-git/bin-wrapper/git deleted file mode 100755 index 6663ae57e8..0000000000 --- a/contrib/mw-to-git/bin-wrapper/git +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh - -# git executable wrapper script for Git-Mediawiki to run tests without -# installing all the scripts and perl packages. - -GIT_ROOT_DIR=../../.. -GIT_EXEC_PATH=$(cd "$(dirname "$0")" && cd ${GIT_ROOT_DIR} && pwd) - -GITPERLLIB="$GIT_EXEC_PATH"'/contrib/mw-to-git'"${GITPERLLIB:+:$GITPERLLIB}" -PATH="$GIT_EXEC_PATH"'/contrib/mw-to-git:'"$PATH" - -export GITPERLLIB PATH - -exec "${GIT_EXEC_PATH}/bin-wrappers/git" "$@" diff --git a/contrib/mw-to-git/git-mw.perl b/contrib/mw-to-git/git-mw.perl deleted file mode 100755 index eb52a53d32..0000000000 --- a/contrib/mw-to-git/git-mw.perl +++ /dev/null @@ -1,368 +0,0 @@ -#!/usr/bin/perl - -# Copyright (C) 2013 -# Benoit Person <benoit.person@ensimag.imag.fr> -# Celestin Matte <celestin.matte@ensimag.imag.fr> -# License: GPL v2 or later - -# Set of tools for git repo with a mediawiki remote. -# Documentation & bugtracker: https://github.com/Git-Mediawiki/Git-Mediawiki - -use strict; -use warnings; - -use Getopt::Long; -use URI::URL qw(url); -use LWP::UserAgent; -use HTML::TreeBuilder; - -use Git; -use MediaWiki::API; -use Git::Mediawiki qw(clean_filename connect_maybe - EMPTY HTTP_CODE_PAGE_NOT_FOUND); - -# By default, use UTF-8 to communicate with Git and the user -binmode STDERR, ':encoding(UTF-8)'; -binmode STDOUT, ':encoding(UTF-8)'; - -# Global parameters -my $verbose = 0; -sub v_print { - if ($verbose) { - return print {*STDERR} @_; - } - return; -} - -# Preview parameters -my $file_name = EMPTY; -my $remote_name = EMPTY; -my $preview_file_name = EMPTY; -my $autoload = 0; -sub file { - $file_name = shift; - return $file_name; -} - -my %commands = ( - 'help' => - [\&help, {}, \&help], - 'preview' => - [\&preview, { - '<>' => \&file, - 'output|o=s' => \$preview_file_name, - 'remote|r=s' => \$remote_name, - 'autoload|a' => \$autoload - }, \&preview_help] -); - -# Search for sub-command -my $cmd = $commands{'help'}; -for (0..@ARGV-1) { - if (defined $commands{$ARGV[$_]}) { - $cmd = $commands{$ARGV[$_]}; - splice @ARGV, $_, 1; - last; - } -}; -GetOptions( %{$cmd->[1]}, - 'help|h' => \&{$cmd->[2]}, - 'verbose|v' => \$verbose); - -# Launch command -&{$cmd->[0]}; - -############################# Preview Functions ################################ - -sub preview_help { - print {*STDOUT} <<'END'; -USAGE: git mw preview [--remote|-r <remote name>] [--autoload|-a] - [--output|-o <output filename>] [--verbose|-v] - <blob> | <filename> - -DESCRIPTION: -Preview is an utiliy to preview local content of a mediawiki repo as if it was -pushed on the remote. - -For that, preview searches for the remote name of the current branch's -upstream if --remote is not set. If that remote is not found or if it -is not a mediawiki, it lists all mediawiki remotes configured and asks -you to replay your command with the --remote option set properly. - -Then, it searches for a file named 'filename'. If it's not found in -the current dir, it will assume it's a blob. - -The content retrieved in the file (or in the blob) will then be parsed -by the remote mediawiki and combined with a template retrieved from -the mediawiki. - -Finally, preview will save the HTML result in a file. and autoload it -in your default web browser if the option --autoload is present. - -OPTIONS: - -r <remote name>, --remote <remote name> - If the remote is a mediawiki, the template and the parse engine - used for the preview will be those of that remote. - If not, a list of valid remotes will be shown. - - -a, --autoload - Try to load the HTML output in a new tab (or new window) of your - default web browser. - - -o <output filename>, --output <output filename> - Change the HTML output filename. Default filename is based on the - input filename with its extension replaced by '.html'. - - -v, --verbose - Show more information on what's going on under the hood. -END - exit; -} - -sub preview { - my $wiki; - my ($remote_url, $wiki_page_name); - my ($new_content, $template); - my $file_content; - - if ($file_name eq EMPTY) { - die "Missing file argument, see `git mw help`\n"; - } - - v_print("### Selecting remote\n"); - if ($remote_name eq EMPTY) { - $remote_name = find_upstream_remote_name(); - if ($remote_name) { - $remote_url = mediawiki_remote_url_maybe($remote_name); - } - - if (! $remote_url) { - my @valid_remotes = find_mediawiki_remotes(); - - if ($#valid_remotes == 0) { - print {*STDERR} "No mediawiki remote in this repo. \n"; - exit 1; - } else { - my $remotes_list = join("\n\t", @valid_remotes); - print {*STDERR} <<"MESSAGE"; -There are multiple mediawiki remotes, which of: - ${remotes_list} -do you want ? Use the -r option to specify the remote. -MESSAGE - } - - exit 1; - } - } else { - if (!is_valid_remote($remote_name)) { - die "${remote_name} is not a remote\n"; - } - - $remote_url = mediawiki_remote_url_maybe($remote_name); - if (! $remote_url) { - die "${remote_name} is not a mediawiki remote\n"; - } - } - v_print("selected remote:\n\tname: ${remote_name}\n\turl: ${remote_url}\n"); - - $wiki = connect_maybe($wiki, $remote_name, $remote_url); - - # Read file content - if (! -e $file_name) { - $file_content = git_cmd_try { - Git::command('cat-file', 'blob', $file_name); } - "%s failed w/ code %d"; - - if ($file_name =~ /(.+):(.+)/) { - $file_name = $2; - } - } else { - open my $read_fh, "<", $file_name - or die "could not open ${file_name}: $!\n"; - $file_content = do { local $/ = undef; <$read_fh> }; - close $read_fh - or die "unable to close: $!\n"; - } - - v_print("### Retrieving template\n"); - ($wiki_page_name = clean_filename($file_name)) =~ s/\.[^.]+$//; - $template = get_template($remote_url, $wiki_page_name); - - v_print("### Parsing local content\n"); - $new_content = $wiki->api({ - action => 'parse', - text => $file_content, - title => $wiki_page_name - }, { - skip_encoding => 1 - }) or die "No response from remote mediawiki\n"; - $new_content = $new_content->{'parse'}->{'text'}->{'*'}; - - v_print("### Merging contents\n"); - if ($preview_file_name eq EMPTY) { - ($preview_file_name = $file_name) =~ s/\.[^.]+$/.html/; - } - open(my $save_fh, '>:encoding(UTF-8)', $preview_file_name) - or die "Could not open: $!\n"; - print {$save_fh} merge_contents($template, $new_content, $remote_url); - close($save_fh) - or die "Could not close: $!\n"; - - v_print("### Results\n"); - if ($autoload) { - v_print("Launching browser w/ file: ${preview_file_name}"); - system('git', 'web--browse', $preview_file_name); - } else { - print {*STDERR} "Preview file saved as: ${preview_file_name}\n"; - } - - exit; -} - -# uses global scope variable: $remote_name -sub merge_contents { - my $template = shift; - my $content = shift; - my $remote_url = shift; - my ($content_tree, $html_tree, $mw_content_text); - my $template_content_id = 'bodyContent'; - - $html_tree = HTML::TreeBuilder->new; - $html_tree->parse($template); - - $content_tree = HTML::TreeBuilder->new; - $content_tree->parse($content); - - $template_content_id = Git::config("remote.${remote_name}.mwIDcontent") - || $template_content_id; - v_print("Using '${template_content_id}' as the content ID\n"); - - $mw_content_text = $html_tree->look_down('id', $template_content_id); - if (!defined $mw_content_text) { - print {*STDERR} <<"CONFIG"; -Could not combine the new content with the template. You might want to -configure `mediawiki.IDContent` in your config: - git config --add remote.${remote_name}.mwIDcontent <id> -and re-run the command afterward. -CONFIG - exit 1; - } - $mw_content_text->delete_content(); - $mw_content_text->push_content($content_tree); - - make_links_absolute($html_tree, $remote_url); - - return $html_tree->as_HTML; -} - -sub make_links_absolute { - my $html_tree = shift; - my $remote_url = shift; - for (@{ $html_tree->extract_links() }) { - my ($link, $element, $attr) = @{ $_ }; - my $url = url($link)->canonical; - if ($url !~ /#/) { - $element->attr($attr, URI->new_abs($url, $remote_url)); - } - } - return $html_tree; -} - -sub is_valid_remote { - my $remote = shift; - my @remotes = git_cmd_try { - Git::command('remote') } - "%s failed w/ code %d"; - my $found_remote = 0; - foreach my $remote (@remotes) { - if ($remote eq $remote) { - $found_remote = 1; - last; - } - } - return $found_remote; -} - -sub find_mediawiki_remotes { - my @remotes = git_cmd_try { - Git::command('remote'); } - "%s failed w/ code %d"; - my $remote_url; - my @valid_remotes = (); - foreach my $remote (@remotes) { - $remote_url = mediawiki_remote_url_maybe($remote); - if ($remote_url) { - push(@valid_remotes, $remote); - } - } - return @valid_remotes; -} - -sub find_upstream_remote_name { - my $current_branch = git_cmd_try { - Git::command_oneline('symbolic-ref', '--short', 'HEAD') } - "%s failed w/ code %d"; - return Git::config("branch.${current_branch}.remote"); -} - -sub mediawiki_remote_url_maybe { - my $remote = shift; - - # Find remote url - my $remote_url = Git::config("remote.${remote}.url"); - if ($remote_url =~ s/mediawiki::(.*)/$1/) { - return url($remote_url)->canonical; - } - - return; -} - -sub get_template { - my $url = shift; - my $page_name = shift; - my ($req, $res, $code, $url_after); - - $req = LWP::UserAgent->new; - if ($verbose) { - $req->show_progress(1); - } - - $res = $req->get("${url}/index.php?title=${page_name}"); - if (!$res->is_success) { - $code = $res->code; - $url_after = $res->request()->uri(); # resolve all redirections - if ($code == HTTP_CODE_PAGE_NOT_FOUND) { - if ($verbose) { - print {*STDERR} <<"WARNING"; -Warning: Failed to retrieve '$page_name'. Create it on the mediawiki if you want -all the links to work properly. -Trying to use the mediawiki homepage as a fallback template ... -WARNING - } - - # LWP automatically redirects GET request - $res = $req->get("${url}/index.php"); - if (!$res->is_success) { - $url_after = $res->request()->uri(); # resolve all redirections - die "Failed to get homepage @ ${url_after} w/ code ${code}\n"; - } - } else { - die "Failed to get '${page_name}' @ ${url_after} w/ code ${code}\n"; - } - } - - return $res->decoded_content; -} - -############################## Help Functions ################################## - -sub help { - print {*STDOUT} <<'END'; -usage: git mw <command> <args> - -git mw commands are: - help Display help information about git mw - preview Parse and render local file into HTML -END - exit; -} diff --git a/contrib/mw-to-git/git-remote-mediawiki.perl b/contrib/mw-to-git/git-remote-mediawiki.perl deleted file mode 100755 index a5624413dc..0000000000 --- a/contrib/mw-to-git/git-remote-mediawiki.perl +++ /dev/null @@ -1,1390 +0,0 @@ -#! /usr/bin/perl - -# Copyright (C) 2011 -# Jérémie Nikaes <jeremie.nikaes@ensimag.imag.fr> -# Arnaud Lacurie <arnaud.lacurie@ensimag.imag.fr> -# Claire Fousse <claire.fousse@ensimag.imag.fr> -# David Amouyal <david.amouyal@ensimag.imag.fr> -# Matthieu Moy <matthieu.moy@grenoble-inp.fr> -# License: GPL v2 or later - -# Gateway between Git and MediaWiki. -# Documentation & bugtracker: https://github.com/Git-Mediawiki/Git-Mediawiki - -use strict; -use MediaWiki::API; -use Git; -use Git::Mediawiki qw(clean_filename smudge_filename connect_maybe - EMPTY HTTP_CODE_OK); -use DateTime::Format::ISO8601; -use warnings; - -# By default, use UTF-8 to communicate with Git and the user -binmode STDERR, ':encoding(UTF-8)'; -binmode STDOUT, ':encoding(UTF-8)'; - -use URI::Escape; - -# It's not always possible to delete pages (may require some -# privileges). Deleted pages are replaced with this content. -use constant DELETED_CONTENT => "[[Category:Deleted]]\n"; - -# It's not possible to create empty pages. New empty files in Git are -# sent with this content instead. -use constant EMPTY_CONTENT => "<!-- empty page -->\n"; - -# used to reflect file creation or deletion in diff. -use constant NULL_SHA1 => '0000000000000000000000000000000000000000'; - -# Used on Git's side to reflect empty edit messages on the wiki -use constant EMPTY_MESSAGE => '*Empty MediaWiki Message*'; - -# Number of pages taken into account at once in submodule get_mw_page_list -use constant SLICE_SIZE => 50; - -# Number of linked mediafile to get at once in get_linked_mediafiles -# The query is split in small batches because of the MW API limit of -# the number of links to be returned (500 links max). -use constant BATCH_SIZE => 10; - -if (@ARGV != 2) { - exit_error_usage(); -} - -my $remotename = $ARGV[0]; -my $url = $ARGV[1]; - -# Accept both space-separated and multiple keys in config file. -# Spaces should be written as _ anyway because we'll use chomp. -my @tracked_pages = split(/[ \n]/, run_git_quoted(["config", "--get-all", "remote.${remotename}.pages"])); -chomp(@tracked_pages); - -# Just like @tracked_pages, but for MediaWiki categories. -my @tracked_categories = split(/[ \n]/, run_git_quoted(["config", "--get-all", "remote.${remotename}.categories"])); -chomp(@tracked_categories); - -# Just like @tracked_categories, but for MediaWiki namespaces. -my @tracked_namespaces = split(/[ \n]/, run_git_quoted(["config", "--get-all", "remote.${remotename}.namespaces"])); -for (@tracked_namespaces) { s/_/ /g; } -chomp(@tracked_namespaces); - -# Import media files on pull -my $import_media = run_git_quoted(["config", "--get", "--bool", "remote.${remotename}.mediaimport"]); -chomp($import_media); -$import_media = ($import_media eq 'true'); - -# Export media files on push -my $export_media = run_git_quoted(["config", "--get", "--bool", "remote.${remotename}.mediaexport"]); -chomp($export_media); -$export_media = !($export_media eq 'false'); - -my $wiki_login = run_git_quoted(["config", "--get", "remote.${remotename}.mwLogin"]); -# Note: mwPassword is discouraged. Use the credential system instead. -my $wiki_passwd = run_git_quoted(["config", "--get", "remote.${remotename}.mwPassword"]); -my $wiki_domain = run_git_quoted(["config", "--get", "remote.${remotename}.mwDomain"]); -chomp($wiki_login); -chomp($wiki_passwd); -chomp($wiki_domain); - -# Import only last revisions (both for clone and fetch) -my $shallow_import = run_git_quoted(["config", "--get", "--bool", "remote.${remotename}.shallow"]); -chomp($shallow_import); -$shallow_import = ($shallow_import eq 'true'); - -# Fetch (clone and pull) by revisions instead of by pages. This behavior -# is more efficient when we have a wiki with lots of pages and we fetch -# the revisions quite often so that they concern only few pages. -# Possible values: -# - by_rev: perform one query per new revision on the remote wiki -# - by_page: query each tracked page for new revision -my $fetch_strategy = run_git_quoted(["config", "--get", "remote.${remotename}.fetchStrategy"]); -if (!$fetch_strategy) { - $fetch_strategy = run_git_quoted(["config", "--get", "mediawiki.fetchStrategy"]); -} -chomp($fetch_strategy); -if (!$fetch_strategy) { - $fetch_strategy = 'by_page'; -} - -# Remember the timestamp corresponding to a revision id. -my %basetimestamps; - -# Dumb push: don't update notes and mediawiki ref to reflect the last push. -# -# Configurable with mediawiki.dumbPush, or per-remote with -# remote.<remotename>.dumbPush. -# -# This means the user will have to re-import the just-pushed -# revisions. On the other hand, this means that the Git revisions -# corresponding to MediaWiki revisions are all imported from the wiki, -# regardless of whether they were initially created in Git or from the -# web interface, hence all users will get the same history (i.e. if -# the push from Git to MediaWiki loses some information, everybody -# will get the history with information lost). If the import is -# deterministic, this means everybody gets the same sha1 for each -# MediaWiki revision. -my $dumb_push = run_git_quoted(["config", "--get", "--bool", "remote.${remotename}.dumbPush"]); -if (!$dumb_push) { - $dumb_push = run_git_quoted(["config", "--get", "--bool", "mediawiki.dumbPush"]); -} -chomp($dumb_push); -$dumb_push = ($dumb_push eq 'true'); - -my $wiki_name = $url; -$wiki_name =~ s{[^/]*://}{}; -# If URL is like http://user:password@example.com/, we clearly don't -# want the password in $wiki_name. While we're there, also remove user -# and '@' sign, to avoid author like MWUser@HTTPUser@host.com -$wiki_name =~ s/^.*@//; - -# Commands parser -while (<STDIN>) { - chomp; - - if (!parse_command($_)) { - last; - } - - BEGIN { $| = 1 } # flush STDOUT, to make sure the previous - # command is fully processed. -} - -########################## Functions ############################## - -## error handling -sub exit_error_usage { - die "ERROR: git-remote-mediawiki module was not called with a correct number of\n" . - "parameters\n" . - "You may obtain this error because you attempted to run the git-remote-mediawiki\n" . - "module directly.\n" . - "This module can be used the following way:\n" . - "\tgit clone mediawiki://<address of a mediawiki>\n" . - "Then, use git commit, push and pull as with every normal git repository.\n"; -} - -sub parse_command { - my ($line) = @_; - my @cmd = split(/ /, $line); - if (!defined $cmd[0]) { - return 0; - } - if ($cmd[0] eq 'capabilities') { - die("Too many arguments for capabilities\n") - if (defined($cmd[1])); - mw_capabilities(); - } elsif ($cmd[0] eq 'list') { - die("Too many arguments for list\n") if (defined($cmd[2])); - mw_list($cmd[1]); - } elsif ($cmd[0] eq 'import') { - die("Invalid argument for import\n") - if ($cmd[1] eq EMPTY); - die("Too many arguments for import\n") - if (defined($cmd[2])); - mw_import($cmd[1]); - } elsif ($cmd[0] eq 'option') { - die("Invalid arguments for option\n") - if ($cmd[1] eq EMPTY || $cmd[2] eq EMPTY); - die("Too many arguments for option\n") - if (defined($cmd[3])); - mw_option($cmd[1],$cmd[2]); - } elsif ($cmd[0] eq 'push') { - mw_push($cmd[1]); - } else { - print {*STDERR} "Unknown command. Aborting...\n"; - return 0; - } - return 1; -} - -# MediaWiki API instance, created lazily. -my $mediawiki; - -sub fatal_mw_error { - my $action = shift; - print STDERR "fatal: could not $action.\n"; - print STDERR "fatal: '$url' does not appear to be a mediawiki\n"; - if ($url =~ /^https/) { - print STDERR "fatal: make sure '$url/api.php' is a valid page\n"; - print STDERR "fatal: and the SSL certificate is correct.\n"; - } else { - print STDERR "fatal: make sure '$url/api.php' is a valid page.\n"; - } - print STDERR "fatal: (error " . - $mediawiki->{error}->{code} . ': ' . - $mediawiki->{error}->{details} . ")\n"; - exit 1; -} - -## Functions for listing pages on the remote wiki -sub get_mw_tracked_pages { - my $pages = shift; - get_mw_page_list(\@tracked_pages, $pages); - return; -} - -sub get_mw_page_list { - my $page_list = shift; - my $pages = shift; - my @some_pages = @{$page_list}; - while (@some_pages) { - my $last_page = SLICE_SIZE; - if ($#some_pages < $last_page) { - $last_page = $#some_pages; - } - my @slice = @some_pages[0..$last_page]; - get_mw_first_pages(\@slice, $pages); - @some_pages = @some_pages[(SLICE_SIZE + 1)..$#some_pages]; - } - return; -} - -sub get_mw_tracked_categories { - my $pages = shift; - foreach my $category (@tracked_categories) { - if (index($category, ':') < 0) { - # Mediawiki requires the Category - # prefix, but let's not force the user - # to specify it. - $category = "Category:${category}"; - } - my $mw_pages = $mediawiki->list( { - action => 'query', - list => 'categorymembers', - cmtitle => $category, - cmlimit => 'max' } ) - || die $mediawiki->{error}->{code} . ': ' - . $mediawiki->{error}->{details} . "\n"; - foreach my $page (@{$mw_pages}) { - $pages->{$page->{title}} = $page; - } - } - return; -} - -sub get_mw_tracked_namespaces { - my $pages = shift; - foreach my $local_namespace (sort @tracked_namespaces) { - my $namespace_id; - if ($local_namespace eq "(Main)") { - $namespace_id = 0; - } else { - $namespace_id = get_mw_namespace_id($local_namespace); - } - # virtual namespaces don't support allpages - next if !defined($namespace_id) || $namespace_id < 0; - my $mw_pages = $mediawiki->list( { - action => 'query', - list => 'allpages', - apnamespace => $namespace_id, - aplimit => 'max' } ) - || die $mediawiki->{error}->{code} . ': ' - . $mediawiki->{error}->{details} . "\n"; - print {*STDERR} "$#{$mw_pages} found in namespace $local_namespace ($namespace_id)\n"; - foreach my $page (@{$mw_pages}) { - $pages->{$page->{title}} = $page; - } - } - return; -} - -sub get_mw_all_pages { - my $pages = shift; - # No user-provided list, get the list of pages from the API. - my $mw_pages = $mediawiki->list({ - action => 'query', - list => 'allpages', - aplimit => 'max' - }); - if (!defined($mw_pages)) { - fatal_mw_error("get the list of wiki pages"); - } - foreach my $page (@{$mw_pages}) { - $pages->{$page->{title}} = $page; - } - return; -} - -# queries the wiki for a set of pages. Meant to be used within a loop -# querying the wiki for slices of page list. -sub get_mw_first_pages { - my $some_pages = shift; - my @some_pages = @{$some_pages}; - - my $pages = shift; - - # pattern 'page1|page2|...' required by the API - my $titles = join('|', @some_pages); - - my $mw_pages = $mediawiki->api({ - action => 'query', - titles => $titles, - }); - if (!defined($mw_pages)) { - fatal_mw_error("query the list of wiki pages"); - } - while (my ($id, $page) = each(%{$mw_pages->{query}->{pages}})) { - if ($id < 0) { - print {*STDERR} "Warning: page $page->{title} not found on wiki\n"; - } else { - $pages->{$page->{title}} = $page; - } - } - return; -} - -# Get the list of pages to be fetched according to configuration. -sub get_mw_pages { - $mediawiki = connect_maybe($mediawiki, $remotename, $url); - - print {*STDERR} "Listing pages on remote wiki...\n"; - - my %pages; # hash on page titles to avoid duplicates - my $user_defined; - if (@tracked_pages) { - $user_defined = 1; - # The user provided a list of pages titles, but we - # still need to query the API to get the page IDs. - get_mw_tracked_pages(\%pages); - } - if (@tracked_categories) { - $user_defined = 1; - get_mw_tracked_categories(\%pages); - } - if (@tracked_namespaces) { - $user_defined = 1; - get_mw_tracked_namespaces(\%pages); - } - if (!$user_defined) { - get_mw_all_pages(\%pages); - } - if ($import_media) { - print {*STDERR} "Getting media files for selected pages...\n"; - if ($user_defined) { - get_linked_mediafiles(\%pages); - } else { - get_all_mediafiles(\%pages); - } - } - print {*STDERR} (scalar keys %pages) . " pages found.\n"; - return %pages; -} - -# usage: $out = run_git_quoted(["command", "args", ...]); -# $out = run_git_quoted(["command", "args", ...], "raw"); # don't interpret output as UTF-8. -# $out = run_git_quoted_nostderr(["command", "args", ...]); # discard stderr -# $out = run_git_quoted_nostderr(["command", "args", ...], "raw"); # ditto but raw instead of UTF-8 as above -sub _run_git { - my $args = shift; - my $encoding = (shift || 'encoding(UTF-8)'); - open(my $git, "-|:${encoding}", @$args) - or die "Unable to fork: $!\n"; - my $res = do { - local $/ = undef; - <$git> - }; - close($git); - - return $res; -} - -sub run_git_quoted { - _run_git(["git", @{$_[0]}], $_[1]); -} - -sub run_git_quoted_nostderr { - _run_git(['sh', '-c', 'git "$@" 2>/dev/null', '--', @{$_[0]}], $_[1]); -} - -sub get_all_mediafiles { - my $pages = shift; - # Attach list of all pages for media files from the API, - # they are in a different namespace, only one namespace - # can be queried at the same moment - my $mw_pages = $mediawiki->list({ - action => 'query', - list => 'allpages', - apnamespace => get_mw_namespace_id('File'), - aplimit => 'max' - }); - if (!defined($mw_pages)) { - print {*STDERR} "fatal: could not get the list of pages for media files.\n"; - print {*STDERR} "fatal: '$url' does not appear to be a mediawiki\n"; - print {*STDERR} "fatal: make sure '$url/api.php' is a valid page.\n"; - exit 1; - } - foreach my $page (@{$mw_pages}) { - $pages->{$page->{title}} = $page; - } - return; -} - -sub get_linked_mediafiles { - my $pages = shift; - my @titles = map { $_->{title} } values(%{$pages}); - - my $batch = BATCH_SIZE; - while (@titles) { - if ($#titles < $batch) { - $batch = $#titles; - } - my @slice = @titles[0..$batch]; - - # pattern 'page1|page2|...' required by the API - my $mw_titles = join('|', @slice); - - # Media files could be included or linked from - # a page, get all related - my $query = { - action => 'query', - prop => 'links|images', - titles => $mw_titles, - plnamespace => get_mw_namespace_id('File'), - pllimit => 'max' - }; - my $result = $mediawiki->api($query); - - while (my ($id, $page) = each(%{$result->{query}->{pages}})) { - my @media_titles; - if (defined($page->{links})) { - my @link_titles - = map { $_->{title} } @{$page->{links}}; - push(@media_titles, @link_titles); - } - if (defined($page->{images})) { - my @image_titles - = map { $_->{title} } @{$page->{images}}; - push(@media_titles, @image_titles); - } - if (@media_titles) { - get_mw_page_list(\@media_titles, $pages); - } - } - - @titles = @titles[($batch+1)..$#titles]; - } - return; -} - -sub get_mw_mediafile_for_page_revision { - # Name of the file on Wiki, with the prefix. - my $filename = shift; - my $timestamp = shift; - my %mediafile; - - # Search if on a media file with given timestamp exists on - # MediaWiki. In that case download the file. - my $query = { - action => 'query', - prop => 'imageinfo', - titles => "File:${filename}", - iistart => $timestamp, - iiend => $timestamp, - iiprop => 'timestamp|archivename|url', - iilimit => 1 - }; - my $result = $mediawiki->api($query); - - my ($fileid, $file) = each( %{$result->{query}->{pages}} ); - # If not defined it means there is no revision of the file for - # given timestamp. - if (defined($file->{imageinfo})) { - $mediafile{title} = $filename; - - my $fileinfo = pop(@{$file->{imageinfo}}); - $mediafile{timestamp} = $fileinfo->{timestamp}; - # Mediawiki::API's download function doesn't support https URLs - # and can't download old versions of files. - print {*STDERR} "\tDownloading file $mediafile{title}, version $mediafile{timestamp}\n"; - $mediafile{content} = download_mw_mediafile($fileinfo->{url}); - } - return %mediafile; -} - -sub download_mw_mediafile { - my $download_url = shift; - - my $response = $mediawiki->{ua}->get($download_url); - if ($response->code == HTTP_CODE_OK) { - # It is tempting to return - # $response->decoded_content({charset => "none"}), but - # when doing so, utf8::downgrade($content) fails with - # "Wide character in subroutine entry". - $response->decode(); - return $response->content(); - } else { - print {*STDERR} "Error downloading mediafile from :\n"; - print {*STDERR} "URL: ${download_url}\n"; - print {*STDERR} 'Server response: ' . $response->code . q{ } . $response->message . "\n"; - exit 1; - } -} - -sub get_last_local_revision { - # Get note regarding last mediawiki revision. - my $note = run_git_quoted_nostderr(["notes", "--ref=${remotename}/mediawiki", - "show", "refs/mediawiki/${remotename}/master"]); - my @note_info = split(/ /, $note); - - my $lastrevision_number; - if (!(defined($note_info[0]) && $note_info[0] eq 'mediawiki_revision:')) { - print {*STDERR} 'No previous mediawiki revision found'; - $lastrevision_number = 0; - } else { - # Notes are formatted : mediawiki_revision: #number - $lastrevision_number = $note_info[1]; - chomp($lastrevision_number); - print {*STDERR} "Last local mediawiki revision found is ${lastrevision_number}"; - } - return $lastrevision_number; -} - -# Get the last remote revision without taking in account which pages are -# tracked or not. This function makes a single request to the wiki thus -# avoid a loop onto all tracked pages. This is useful for the fetch-by-rev -# option. -sub get_last_global_remote_rev { - $mediawiki = connect_maybe($mediawiki, $remotename, $url); - - my $query = { - action => 'query', - list => 'recentchanges', - prop => 'revisions', - rclimit => '1', - rcdir => 'older', - }; - my $result = $mediawiki->api($query); - return $result->{query}->{recentchanges}[0]->{revid}; -} - -# Get the last remote revision concerning the tracked pages and the tracked -# categories. -sub get_last_remote_revision { - $mediawiki = connect_maybe($mediawiki, $remotename, $url); - - my %pages_hash = get_mw_pages(); - my @pages = values(%pages_hash); - - my $max_rev_num = 0; - - print {*STDERR} "Getting last revision id on tracked pages...\n"; - - foreach my $page (@pages) { - my $id = $page->{pageid}; - - my $query = { - action => 'query', - prop => 'revisions', - rvprop => 'ids|timestamp', - pageids => $id, - }; - - my $result = $mediawiki->api($query); - - my $lastrev = pop(@{$result->{query}->{pages}->{$id}->{revisions}}); - - $basetimestamps{$lastrev->{revid}} = $lastrev->{timestamp}; - - $max_rev_num = ($lastrev->{revid} > $max_rev_num ? $lastrev->{revid} : $max_rev_num); - } - - print {*STDERR} "Last remote revision found is $max_rev_num.\n"; - return $max_rev_num; -} - -# Clean content before sending it to MediaWiki -sub mediawiki_clean { - my $string = shift; - my $page_created = shift; - # Mediawiki does not allow blank space at the end of a page and ends with a single \n. - # This function right trims a string and adds a \n at the end to follow this rule - $string =~ s/\s+$//; - if ($string eq EMPTY && $page_created) { - # Creating empty pages is forbidden. - $string = EMPTY_CONTENT; - } - return $string."\n"; -} - -# Filter applied on MediaWiki data before adding them to Git -sub mediawiki_smudge { - my $string = shift; - if ($string eq EMPTY_CONTENT) { - $string = EMPTY; - } - # This \n is important. This is due to mediawiki's way to handle end of files. - return "${string}\n"; -} - -sub literal_data { - my ($content) = @_; - print {*STDOUT} 'data ', bytes::length($content), "\n", $content; - return; -} - -sub literal_data_raw { - # Output possibly binary content. - my ($content) = @_; - # Avoid confusion between size in bytes and in characters - utf8::downgrade($content); - binmode STDOUT, ':raw'; - print {*STDOUT} 'data ', bytes::length($content), "\n", $content; - binmode STDOUT, ':encoding(UTF-8)'; - return; -} - -sub mw_capabilities { - # Revisions are imported to the private namespace - # refs/mediawiki/$remotename/ by the helper and fetched into - # refs/remotes/$remotename later by fetch. - print {*STDOUT} "refspec refs/heads/*:refs/mediawiki/${remotename}/*\n"; - print {*STDOUT} "import\n"; - print {*STDOUT} "list\n"; - print {*STDOUT} "push\n"; - if ($dumb_push) { - print {*STDOUT} "no-private-update\n"; - } - print {*STDOUT} "\n"; - return; -} - -sub mw_list { - # MediaWiki do not have branches, we consider one branch arbitrarily - # called master, and HEAD pointing to it. - print {*STDOUT} "? refs/heads/master\n"; - print {*STDOUT} "\@refs/heads/master HEAD\n"; - print {*STDOUT} "\n"; - return; -} - -sub mw_option { - print {*STDERR} "remote-helper command 'option $_[0]' not yet implemented\n"; - print {*STDOUT} "unsupported\n"; - return; -} - -sub fetch_mw_revisions_for_page { - my $page = shift; - my $id = shift; - my $fetch_from = shift; - my @page_revs = (); - my $query = { - action => 'query', - prop => 'revisions', - rvprop => 'ids', - rvdir => 'newer', - rvstartid => $fetch_from, - rvlimit => 500, - pageids => $id, - - # Let MediaWiki know that we support the latest API. - continue => '', - }; - - my $revnum = 0; - # Get 500 revisions at a time due to the mediawiki api limit - while (1) { - my $result = $mediawiki->api($query); - - # Parse each of those 500 revisions - foreach my $revision (@{$result->{query}->{pages}->{$id}->{revisions}}) { - my $page_rev_ids; - $page_rev_ids->{pageid} = $page->{pageid}; - $page_rev_ids->{revid} = $revision->{revid}; - push(@page_revs, $page_rev_ids); - $revnum++; - } - - if ($result->{'query-continue'}) { # For legacy APIs - $query->{rvstartid} = $result->{'query-continue'}->{revisions}->{rvstartid}; - } elsif ($result->{continue}) { # For newer APIs - $query->{rvstartid} = $result->{continue}->{rvcontinue}; - $query->{continue} = $result->{continue}->{continue}; - } else { - last; - } - } - if ($shallow_import && @page_revs) { - print {*STDERR} " Found 1 revision (shallow import).\n"; - @page_revs = sort {$b->{revid} <=> $a->{revid}} (@page_revs); - return $page_revs[0]; - } - print {*STDERR} " Found ${revnum} revision(s).\n"; - return @page_revs; -} - -sub fetch_mw_revisions { - my $pages = shift; my @pages = @{$pages}; - my $fetch_from = shift; - - my @revisions = (); - my $n = 1; - foreach my $page (@pages) { - my $id = $page->{pageid}; - print {*STDERR} "page ${n}/", scalar(@pages), ': ', $page->{title}, "\n"; - $n++; - my @page_revs = fetch_mw_revisions_for_page($page, $id, $fetch_from); - @revisions = (@page_revs, @revisions); - } - - return ($n, @revisions); -} - -sub fe_escape_path { - my $path = shift; - $path =~ s/\\/\\\\/g; - $path =~ s/"/\\"/g; - $path =~ s/\n/\\n/g; - return qq("${path}"); -} - -sub import_file_revision { - my $commit = shift; - my %commit = %{$commit}; - my $full_import = shift; - my $n = shift; - my $mediafile = shift; - my %mediafile; - if ($mediafile) { - %mediafile = %{$mediafile}; - } - - my $title = $commit{title}; - my $comment = $commit{comment}; - my $content = $commit{content}; - my $author = $commit{author}; - my $date = $commit{date}; - - print {*STDOUT} "commit refs/mediawiki/${remotename}/master\n"; - print {*STDOUT} "mark :${n}\n"; - print {*STDOUT} "committer ${author} <${author}\@${wiki_name}> " . $date->epoch . " +0000\n"; - literal_data($comment); - - # If it's not a clone, we need to know where to start from - if (!$full_import && $n == 1) { - print {*STDOUT} "from refs/mediawiki/${remotename}/master^0\n"; - } - if ($content ne DELETED_CONTENT) { - print {*STDOUT} 'M 644 inline ' . - fe_escape_path("${title}.mw") . "\n"; - literal_data($content); - if (%mediafile) { - print {*STDOUT} 'M 644 inline ' - . fe_escape_path($mediafile{title}) . "\n"; - literal_data_raw($mediafile{content}); - } - print {*STDOUT} "\n\n"; - } else { - print {*STDOUT} 'D ' . fe_escape_path("${title}.mw") . "\n"; - } - - # mediawiki revision number in the git note - if ($full_import && $n == 1) { - print {*STDOUT} "reset refs/notes/${remotename}/mediawiki\n"; - } - print {*STDOUT} "commit refs/notes/${remotename}/mediawiki\n"; - print {*STDOUT} "committer ${author} <${author}\@${wiki_name}> " . $date->epoch . " +0000\n"; - literal_data('Note added by git-mediawiki during import'); - if (!$full_import && $n == 1) { - print {*STDOUT} "from refs/notes/${remotename}/mediawiki^0\n"; - } - print {*STDOUT} "N inline :${n}\n"; - literal_data("mediawiki_revision: $commit{mw_revision}"); - print {*STDOUT} "\n\n"; - return; -} - -# parse a sequence of -# <cmd> <arg1> -# <cmd> <arg2> -# \n -# (like batch sequence of import and sequence of push statements) -sub get_more_refs { - my $cmd = shift; - my @refs; - while (1) { - my $line = <STDIN>; - if ($line =~ /^$cmd (.*)$/) { - push(@refs, $1); - } elsif ($line eq "\n") { - return @refs; - } else { - die("Invalid command in a '$cmd' batch: $_\n"); - } - } - return; -} - -sub mw_import { - # multiple import commands can follow each other. - my @refs = (shift, get_more_refs('import')); - my $processedRefs; - foreach my $ref (@refs) { - next if $processedRefs->{$ref}; # skip duplicates: "import refs/heads/master" being issued twice; TODO: why? - $processedRefs->{$ref} = 1; - mw_import_ref($ref); - } - print {*STDOUT} "done\n"; - return; -} - -sub mw_import_ref { - my $ref = shift; - # The remote helper will call "import HEAD" and - # "import refs/heads/master". - # Since HEAD is a symbolic ref to master (by convention, - # followed by the output of the command "list" that we gave), - # we don't need to do anything in this case. - if ($ref eq 'HEAD') { - return; - } - - $mediawiki = connect_maybe($mediawiki, $remotename, $url); - - print {*STDERR} "Searching revisions...\n"; - my $last_local = get_last_local_revision(); - my $fetch_from = $last_local + 1; - if ($fetch_from == 1) { - print {*STDERR} ", fetching from beginning.\n"; - } else { - print {*STDERR} ", fetching from here.\n"; - } - - my $n = 0; - if ($fetch_strategy eq 'by_rev') { - print {*STDERR} "Fetching & writing export data by revs...\n"; - $n = mw_import_ref_by_revs($fetch_from); - } elsif ($fetch_strategy eq 'by_page') { - print {*STDERR} "Fetching & writing export data by pages...\n"; - $n = mw_import_ref_by_pages($fetch_from); - } else { - print {*STDERR} qq(fatal: invalid fetch strategy "${fetch_strategy}".\n); - print {*STDERR} "Check your configuration variables remote.${remotename}.fetchStrategy and mediawiki.fetchStrategy\n"; - exit 1; - } - - if ($fetch_from == 1 && $n == 0) { - print {*STDERR} "You appear to have cloned an empty MediaWiki.\n"; - # Something has to be done remote-helper side. If nothing is done, an error is - # thrown saying that HEAD is referring to unknown object 0000000000000000000 - # and the clone fails. - } - return; -} - -sub mw_import_ref_by_pages { - - my $fetch_from = shift; - my %pages_hash = get_mw_pages(); - my @pages = values(%pages_hash); - - my ($n, @revisions) = fetch_mw_revisions(\@pages, $fetch_from); - - @revisions = sort {$a->{revid} <=> $b->{revid}} @revisions; - my @revision_ids = map { $_->{revid} } @revisions; - - return mw_import_revids($fetch_from, \@revision_ids, \%pages_hash); -} - -sub mw_import_ref_by_revs { - - my $fetch_from = shift; - my %pages_hash = get_mw_pages(); - - my $last_remote = get_last_global_remote_rev(); - my @revision_ids = $fetch_from..$last_remote; - return mw_import_revids($fetch_from, \@revision_ids, \%pages_hash); -} - -# Import revisions given in second argument (array of integers). -# Only pages appearing in the third argument (hash indexed by page titles) -# will be imported. -sub mw_import_revids { - my $fetch_from = shift; - my $revision_ids = shift; - my $pages = shift; - - my $n = 0; - my $n_actual = 0; - my $last_timestamp = 0; # Placeholder in case $rev->timestamp is undefined - - foreach my $pagerevid (@{$revision_ids}) { - # Count page even if we skip it, since we display - # $n/$total and $total includes skipped pages. - $n++; - - # fetch the content of the pages - my $query = { - action => 'query', - prop => 'revisions', - rvprop => 'content|timestamp|comment|user|ids', - revids => $pagerevid, - }; - - my $result = $mediawiki->api($query); - - if (!$result) { - die "Failed to retrieve modified page for revision $pagerevid\n"; - } - - if (defined($result->{query}->{badrevids}->{$pagerevid})) { - # The revision id does not exist on the remote wiki. - next; - } - - if (!defined($result->{query}->{pages})) { - die "Invalid revision ${pagerevid}.\n"; - } - - my @result_pages = values(%{$result->{query}->{pages}}); - my $result_page = $result_pages[0]; - my $rev = $result_pages[0]->{revisions}->[0]; - - my $page_title = $result_page->{title}; - - if (!exists($pages->{$page_title})) { - print {*STDERR} "${n}/", scalar(@{$revision_ids}), - ": Skipping revision #$rev->{revid} of ${page_title}\n"; - next; - } - - $n_actual++; - - my %commit; - $commit{author} = $rev->{user} || 'Anonymous'; - $commit{comment} = $rev->{comment} || EMPTY_MESSAGE; - $commit{title} = smudge_filename($page_title); - $commit{mw_revision} = $rev->{revid}; - $commit{content} = mediawiki_smudge($rev->{'*'}); - - if (!defined($rev->{timestamp})) { - $last_timestamp++; - } else { - $last_timestamp = $rev->{timestamp}; - } - $commit{date} = DateTime::Format::ISO8601->parse_datetime($last_timestamp); - - # Differentiates classic pages and media files. - my ($namespace, $filename) = $page_title =~ /^([^:]*):(.*)$/; - my %mediafile; - if ($namespace) { - my $id = get_mw_namespace_id($namespace); - if ($id && $id == get_mw_namespace_id('File')) { - %mediafile = get_mw_mediafile_for_page_revision($filename, $rev->{timestamp}); - } - } - # If this is a revision of the media page for new version - # of a file do one common commit for both file and media page. - # Else do commit only for that page. - print {*STDERR} "${n}/", scalar(@{$revision_ids}), ": Revision #$rev->{revid} of $commit{title}\n"; - import_file_revision(\%commit, ($fetch_from == 1), $n_actual, \%mediafile); - } - - return $n_actual; -} - -sub error_non_fast_forward { - my $advice = run_git_quoted(["config", "--bool", "advice.pushNonFastForward"]); - chomp($advice); - if ($advice ne 'false') { - # Native git-push would show this after the summary. - # We can't ask it to display it cleanly, so print it - # ourselves before. - print {*STDERR} "To prevent you from losing history, non-fast-forward updates were rejected\n"; - print {*STDERR} "Merge the remote changes (e.g. 'git pull') before pushing again. See the\n"; - print {*STDERR} "'Note about fast-forwards' section of 'git push --help' for details.\n"; - } - print {*STDOUT} qq(error $_[0] "non-fast-forward"\n); - return 0; -} - -sub mw_upload_file { - my $complete_file_name = shift; - my $new_sha1 = shift; - my $extension = shift; - my $file_deleted = shift; - my $summary = shift; - my $newrevid; - my $path = "File:${complete_file_name}"; - my %hashFiles = get_allowed_file_extensions(); - if (!exists($hashFiles{$extension})) { - print {*STDERR} "${complete_file_name} is not a permitted file on this wiki.\n"; - print {*STDERR} "Check the configuration of file uploads in your mediawiki.\n"; - return $newrevid; - } - # Deleting and uploading a file requires a privileged user - if ($file_deleted) { - $mediawiki = connect_maybe($mediawiki, $remotename, $url); - my $query = { - action => 'delete', - title => $path, - reason => $summary - }; - if (!$mediawiki->edit($query)) { - print {*STDERR} "Failed to delete file on remote wiki\n"; - print {*STDERR} "Check your permissions on the remote site. Error code:\n"; - print {*STDERR} $mediawiki->{error}->{code} . ':' . $mediawiki->{error}->{details}; - exit 1; - } - } else { - # Don't let perl try to interpret file content as UTF-8 => use "raw" - my $content = run_git_quoted(["cat-file", "blob", $new_sha1], 'raw'); - if ($content ne EMPTY) { - $mediawiki = connect_maybe($mediawiki, $remotename, $url); - $mediawiki->{config}->{upload_url} = - "${url}/index.php/Special:Upload"; - $mediawiki->edit({ - action => 'upload', - filename => $complete_file_name, - comment => $summary, - file => [undef, - $complete_file_name, - Content => $content], - ignorewarnings => 1, - }, { - skip_encoding => 1 - } ) || die $mediawiki->{error}->{code} . ':' - . $mediawiki->{error}->{details} . "\n"; - my $last_file_page = $mediawiki->get_page({title => $path}); - $newrevid = $last_file_page->{revid}; - print {*STDERR} "Pushed file: ${new_sha1} - ${complete_file_name}.\n"; - } else { - print {*STDERR} "Empty file ${complete_file_name} not pushed.\n"; - } - } - return $newrevid; -} - -sub mw_push_file { - my $diff_info = shift; - # $diff_info contains a string in this format: - # 100644 100644 <sha1_of_blob_before_commit> <sha1_of_blob_now> <status> - my @diff_info_split = split(/[ \t]/, $diff_info); - - # Filename, including .mw extension - my $complete_file_name = shift; - # Commit message - my $summary = shift; - # MediaWiki revision number. Keep the previous one by default, - # in case there's no edit to perform. - my $oldrevid = shift; - my $newrevid; - - if ($summary eq EMPTY_MESSAGE) { - $summary = EMPTY; - } - - my $new_sha1 = $diff_info_split[3]; - my $old_sha1 = $diff_info_split[2]; - my $page_created = ($old_sha1 eq NULL_SHA1); - my $page_deleted = ($new_sha1 eq NULL_SHA1); - $complete_file_name = clean_filename($complete_file_name); - - my ($title, $extension) = $complete_file_name =~ /^(.*)\.([^\.]*)$/; - if (!defined($extension)) { - $extension = EMPTY; - } - if ($extension eq 'mw') { - my $ns = get_mw_namespace_id_for_page($complete_file_name); - if ($ns && $ns == get_mw_namespace_id('File') && (!$export_media)) { - print {*STDERR} "Ignoring media file related page: ${complete_file_name}\n"; - return ($oldrevid, 'ok'); - } - my $file_content; - if ($page_deleted) { - # Deleting a page usually requires - # special privileges. A common - # convention is to replace the page - # with this content instead: - $file_content = DELETED_CONTENT; - } else { - $file_content = run_git_quoted(["cat-file", "blob", $new_sha1]); - } - - $mediawiki = connect_maybe($mediawiki, $remotename, $url); - - my $result = $mediawiki->edit( { - action => 'edit', - summary => $summary, - title => $title, - basetimestamp => $basetimestamps{$oldrevid}, - text => mediawiki_clean($file_content, $page_created), - }, { - skip_encoding => 1 # Helps with names with accentuated characters - }); - if (!$result) { - if ($mediawiki->{error}->{code} == 3) { - # edit conflicts, considered as non-fast-forward - print {*STDERR} 'Warning: Error ' . - $mediawiki->{error}->{code} . - ' from mediawiki: ' . $mediawiki->{error}->{details} . - ".\n"; - return ($oldrevid, 'non-fast-forward'); - } else { - # Other errors. Shouldn't happen => just die() - die 'Fatal: Error ' . - $mediawiki->{error}->{code} . - ' from mediawiki: ' . $mediawiki->{error}->{details} . "\n"; - } - } - $newrevid = $result->{edit}->{newrevid}; - print {*STDERR} "Pushed file: ${new_sha1} - ${title}\n"; - } elsif ($export_media) { - $newrevid = mw_upload_file($complete_file_name, $new_sha1, - $extension, $page_deleted, - $summary); - } else { - print {*STDERR} "Ignoring media file ${title}\n"; - } - $newrevid = ($newrevid or $oldrevid); - return ($newrevid, 'ok'); -} - -sub mw_push { - # multiple push statements can follow each other - my @refsspecs = (shift, get_more_refs('push')); - my $pushed; - for my $refspec (@refsspecs) { - my ($force, $local, $remote) = $refspec =~ /^(\+)?([^:]*):([^:]*)$/ - or die("Invalid refspec for push. Expected <src>:<dst> or +<src>:<dst>\n"); - if ($force) { - print {*STDERR} "Warning: forced push not allowed on a MediaWiki.\n"; - } - if ($local eq EMPTY) { - print {*STDERR} "Cannot delete remote branch on a MediaWiki\n"; - print {*STDOUT} "error ${remote} cannot delete\n"; - next; - } - if ($remote ne 'refs/heads/master') { - print {*STDERR} "Only push to the branch 'master' is supported on a MediaWiki\n"; - print {*STDOUT} "error ${remote} only master allowed\n"; - next; - } - if (mw_push_revision($local, $remote)) { - $pushed = 1; - } - } - - # Notify Git that the push is done - print {*STDOUT} "\n"; - - if ($pushed && $dumb_push) { - print {*STDERR} "Just pushed some revisions to MediaWiki.\n"; - print {*STDERR} "The pushed revisions now have to be re-imported, and your current branch\n"; - print {*STDERR} "needs to be updated with these re-imported commits. You can do this with\n"; - print {*STDERR} "\n"; - print {*STDERR} " git pull --rebase\n"; - print {*STDERR} "\n"; - } - return; -} - -sub mw_push_revision { - my $local = shift; - my $remote = shift; # actually, this has to be "refs/heads/master" at this point. - my $last_local_revid = get_last_local_revision(); - print {*STDERR} ".\n"; # Finish sentence started by get_last_local_revision() - my $last_remote_revid = get_last_remote_revision(); - my $mw_revision = $last_remote_revid; - - # Get sha1 of commit pointed by local HEAD - my $HEAD_sha1 = run_git_quoted_nostderr(["rev-parse", $local]); - chomp($HEAD_sha1); - # Get sha1 of commit pointed by remotes/$remotename/master - my $remoteorigin_sha1 = run_git_quoted_nostderr(["rev-parse", "refs/remotes/${remotename}/master"]); - chomp($remoteorigin_sha1); - - if ($last_local_revid > 0 && - $last_local_revid < $last_remote_revid) { - return error_non_fast_forward($remote); - } - - if ($HEAD_sha1 eq $remoteorigin_sha1) { - # nothing to push - return 0; - } - - # Get every commit in between HEAD and refs/remotes/origin/master, - # including HEAD and refs/remotes/origin/master - my @commit_pairs = (); - if ($last_local_revid > 0) { - my $parsed_sha1 = $remoteorigin_sha1; - # Find a path from last MediaWiki commit to pushed commit - print {*STDERR} "Computing path from local to remote ...\n"; - my @local_ancestry = split(/\n/, run_git_quoted(["rev-list", "--boundary", "--parents", $local, "^${parsed_sha1}"])); - my %local_ancestry; - foreach my $line (@local_ancestry) { - if (my ($child, $parents) = $line =~ /^-?([a-f0-9]+) ([a-f0-9 ]+)/) { - foreach my $parent (split(/ /, $parents)) { - $local_ancestry{$parent} = $child; - } - } elsif (!$line =~ /^([a-f0-9]+)/) { - die "Unexpected output from git rev-list: ${line}\n"; - } - } - while ($parsed_sha1 ne $HEAD_sha1) { - my $child = $local_ancestry{$parsed_sha1}; - if (!$child) { - print {*STDERR} "Cannot find a path in history from remote commit to last commit\n"; - return error_non_fast_forward($remote); - } - push(@commit_pairs, [$parsed_sha1, $child]); - $parsed_sha1 = $child; - } - } else { - # No remote mediawiki revision. Export the whole - # history (linearized with --first-parent) - print {*STDERR} "Warning: no common ancestor, pushing complete history\n"; - my $history = run_git_quoted(["rev-list", "--first-parent", "--children", $local]); - my @history = split(/\n/, $history); - @history = @history[1..$#history]; - foreach my $line (reverse @history) { - my @commit_info_split = split(/[ \n]/, $line); - push(@commit_pairs, \@commit_info_split); - } - } - - foreach my $commit_info_split (@commit_pairs) { - my $sha1_child = @{$commit_info_split}[0]; - my $sha1_commit = @{$commit_info_split}[1]; - my $diff_infos = run_git_quoted(["diff-tree", "-r", "--raw", "-z", $sha1_child, $sha1_commit]); - # TODO: we could detect rename, and encode them with a #redirect on the wiki. - # TODO: for now, it's just a delete+add - my @diff_info_list = split(/\0/, $diff_infos); - # Keep the subject line of the commit message as mediawiki comment for the revision - my $commit_msg = run_git_quoted(["log", "--no-walk", '--format="%s"', $sha1_commit]); - chomp($commit_msg); - # Push every blob - while (@diff_info_list) { - my $status; - # git diff-tree -z gives an output like - # <metadata>\0<filename1>\0 - # <metadata>\0<filename2>\0 - # and we've split on \0. - my $info = shift(@diff_info_list); - my $file = shift(@diff_info_list); - ($mw_revision, $status) = mw_push_file($info, $file, $commit_msg, $mw_revision); - if ($status eq 'non-fast-forward') { - # we may already have sent part of the - # commit to MediaWiki, but it's too - # late to cancel it. Stop the push in - # the middle, but still give an - # accurate error message. - return error_non_fast_forward($remote); - } - if ($status ne 'ok') { - die("Unknown error from mw_push_file()\n"); - } - } - if (!$dumb_push) { - run_git_quoted(["notes", "--ref=${remotename}/mediawiki", - "add", "-f", "-m", - "mediawiki_revision: ${mw_revision}", - $sha1_commit]); - } - } - - print {*STDOUT} "ok ${remote}\n"; - return 1; -} - -sub get_allowed_file_extensions { - $mediawiki = connect_maybe($mediawiki, $remotename, $url); - - my $query = { - action => 'query', - meta => 'siteinfo', - siprop => 'fileextensions' - }; - my $result = $mediawiki->api($query); - my @file_extensions = map { $_->{ext}} @{$result->{query}->{fileextensions}}; - my %hashFile = map { $_ => 1 } @file_extensions; - - return %hashFile; -} - -# In memory cache for MediaWiki namespace ids. -my %namespace_id; - -# Namespaces whose id is cached in the configuration file -# (to avoid duplicates) -my %cached_mw_namespace_id; - -# Return MediaWiki id for a canonical namespace name. -# Ex.: "File", "Project". -sub get_mw_namespace_id { - $mediawiki = connect_maybe($mediawiki, $remotename, $url); - my $name = shift; - - if (!exists $namespace_id{$name}) { - # Look at configuration file, if the record for that namespace is - # already cached. Namespaces are stored in form: - # "Name_of_namespace:Id_namespace", ex.: "File:6". - my @temp = split(/\n/, - run_git_quoted(["config", "--get-all", "remote.${remotename}.namespaceCache"])); - chomp(@temp); - foreach my $ns (@temp) { - my ($n, $id) = split(/:/, $ns); - if ($id eq 'notANameSpace') { - $namespace_id{$n} = {is_namespace => 0}; - } else { - $namespace_id{$n} = {is_namespace => 1, id => $id}; - } - $cached_mw_namespace_id{$n} = 1; - } - } - - if (!exists $namespace_id{$name}) { - print {*STDERR} "Namespace ${name} not found in cache, querying the wiki ...\n"; - # NS not found => get namespace id from MW and store it in - # configuration file. - my $query = { - action => 'query', - meta => 'siteinfo', - siprop => 'namespaces' - }; - my $result = $mediawiki->api($query); - - while (my ($id, $ns) = each(%{$result->{query}->{namespaces}})) { - if (defined($ns->{id}) && defined($ns->{canonical})) { - $namespace_id{$ns->{canonical}} = {is_namespace => 1, id => $ns->{id}}; - if ($ns->{'*'}) { - # alias (e.g. french Fichier: as alias for canonical File:) - $namespace_id{$ns->{'*'}} = {is_namespace => 1, id => $ns->{id}}; - } - } - } - } - - my $ns = $namespace_id{$name}; - my $id; - - if (!defined $ns) { - my @namespaces = map { s/ /_/g; $_; } sort keys %namespace_id; - print {*STDERR} "No such namespace ${name} on MediaWiki, known namespaces: @namespaces\n"; - $ns = {is_namespace => 0}; - $namespace_id{$name} = $ns; - } - - if ($ns->{is_namespace}) { - $id = $ns->{id}; - } - - # Store "notANameSpace" as special value for inexisting namespaces - my $store_id = ($id || 'notANameSpace'); - - # Store explicitly requested namespaces on disk - if (!exists $cached_mw_namespace_id{$name}) { - run_git_quoted(["config", "--add", "remote.${remotename}.namespaceCache", "${name}:${store_id}"]); - $cached_mw_namespace_id{$name} = 1; - } - return $id; -} - -sub get_mw_namespace_id_for_page { - my $namespace = shift; - if ($namespace =~ /^([^:]*):/) { - return get_mw_namespace_id($namespace); - } else { - return; - } -} diff --git a/contrib/mw-to-git/git-remote-mediawiki.txt b/contrib/mw-to-git/git-remote-mediawiki.txt deleted file mode 100644 index 5da825f61e..0000000000 --- a/contrib/mw-to-git/git-remote-mediawiki.txt +++ /dev/null @@ -1,7 +0,0 @@ -Git-Mediawiki is a project which aims the creation of a gate -between git and mediawiki, allowing git users to push and pull -objects from mediawiki just as one would do with a classic git -repository thanks to remote-helpers. - -For more information, visit the wiki at -https://github.com/Git-Mediawiki/Git-Mediawiki diff --git a/contrib/mw-to-git/t/.gitignore b/contrib/mw-to-git/t/.gitignore deleted file mode 100644 index 2b8dc30c6d..0000000000 --- a/contrib/mw-to-git/t/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -WEB/ -mediawiki/ -trash directory.t*/ -test-results/ diff --git a/contrib/mw-to-git/t/Makefile b/contrib/mw-to-git/t/Makefile deleted file mode 100644 index 6c9f377caa..0000000000 --- a/contrib/mw-to-git/t/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -# -# Copyright (C) 2012 -# Charles Roussel <charles.roussel@ensimag.imag.fr> -# Simon Cathebras <simon.cathebras@ensimag.imag.fr> -# Julien Khayat <julien.khayat@ensimag.imag.fr> -# Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr> -# Simon Perrat <simon.perrat@ensimag.imag.fr> -# -## Test git-remote-mediawiki - -# The default target of this Makefile is... -all:: test - --include ../../../config.mak.autogen --include ../../../config.mak - -T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh) - -.PHONY: help test clean all - -help: - @echo 'Run "$(MAKE) test" to launch test scripts' - @echo 'Run "$(MAKE) clean" to remove trash folders' - -test: - @for t in $(T); do \ - echo "$$t"; \ - "./$$t" || exit 1; \ - done - -clean: - $(RM) -r 'trash directory'.* diff --git a/contrib/mw-to-git/t/README b/contrib/mw-to-git/t/README deleted file mode 100644 index 72c4889db7..0000000000 --- a/contrib/mw-to-git/t/README +++ /dev/null @@ -1,124 +0,0 @@ -Tests for Mediawiki-to-Git -========================== - -Introduction ------------- -This manual describes how to install the git-remote-mediawiki test -environment on a machine with git installed on it. - -Prerequisite ------------- - -In order to run this test environment correctly, you will need to -install the following packages (Debian/Ubuntu names, may need to be -adapted for another distribution): - -* lighttpd -* php -* php-cgi -* php-cli -* php-curl -* php-sqlite - -Principles and Technical Choices --------------------------------- - -The test environment makes it easy to install and manipulate one or -several MediaWiki instances. To allow developers to run the testsuite -easily, the environment does not require root privilege (except to -install the required packages if needed). It starts a webserver -instance on the user's account (using lighttpd greatly helps for -that), and does not need a separate database daemon (thanks to the use -of sqlite). - -Run the test environment ------------------------- - -Install a new wiki -~~~~~~~~~~~~~~~~~~ - -Once you have all the prerequisite, you need to install a MediaWiki -instance on your machine. If you already have one, it is still -strongly recommended to install one with the script provided. Here's -how to work it: - -a. change directory to contrib/mw-to-git/t/ -b. if needed, edit test.config to choose your installation parameters -c. run `./install-wiki.sh install` -d. check on your favourite web browser if your wiki is correctly - installed. - -Remove an existing wiki -~~~~~~~~~~~~~~~~~~~~~~~ - -Edit the file test.config to fit the wiki you want to delete, and then -execute the command `./install-wiki.sh delete` from the -contrib/mw-to-git/t directory. - -Run the existing tests -~~~~~~~~~~~~~~~~~~~~~~ - -The provided tests are currently in the `contrib/mw-to-git/t` directory. -The files are all the t936[0-9]-*.sh shell scripts. - -a. Run all tests: -To do so, run "make test" from the contrib/mw-to-git/ directory. - -b. Run a specific test: -To run a given test <test_name>, run ./<test_name> from the -contrib/mw-to-git/t directory. - -How to create new tests ------------------------ - -Available functions -~~~~~~~~~~~~~~~~~~~ - -The test environment of git-remote-mediawiki provides some functions -useful to test its behaviour. for more details about the functions' -parameters, please refer to the `test-gitmw-lib.sh` and -`test-gitmw.pl` files. - -** `test_check_wiki_precond`: -Check if the tests must be skipped or not. Please use this function -at the beginning of each new test file. - -** `wiki_getpage`: -Fetch a given page from the wiki and puts its content in the -directory in parameter. - -** `wiki_delete_page`: -Delete a given page from the wiki. - -** `wiki_edit_page`: -Create or modify a given page in the wiki. You can specify several -parameters like a summary for the page edition, or add the page to a -given category. -See test-gitmw.pl for more details. - -** `wiki_getallpage`: -Fetch all pages from the wiki into a given directory. The directory -is created if it does not exists. - -** `test_diff_directories`: -Compare the content of two directories. The content must be the same. -Use this function to compare the content of a git directory and a wiki -one created by wiki_getallpage. - -** `test_contains_N_files`: -Check if the given directory contains a given number of file. - -** `wiki_page_exists`: -Tests if a given page exists on the wiki. - -** `wiki_reset`: -Reset the wiki, i.e. flush the database. Use this function at the -beginning of each new test, except if the test re-uses the same wiki -(and history) as the previous test. - -How to write a new test -~~~~~~~~~~~~~~~~~~~~~~~ - -Please, follow the standards given by git. See git/t/README. -New file should be named as t936[0-9]-*.sh. -Be sure to reset your wiki regularly with the function `wiki_reset`. diff --git a/contrib/mw-to-git/t/install-wiki.sh b/contrib/mw-to-git/t/install-wiki.sh deleted file mode 100755 index c215213c4b..0000000000 --- a/contrib/mw-to-git/t/install-wiki.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/sh - -# This script installs or deletes a MediaWiki on your computer. -# It requires a web server with PHP and SQLite running. In addition, if you -# do not have MediaWiki sources on your computer, the option 'install' -# downloads them for you. -# Please set the CONFIGURATION VARIABLES in ./test-gitmw-lib.sh - -WIKI_TEST_DIR=$(cd "$(dirname "$0")" && pwd) - -if test -z "$WIKI_TEST_DIR" -then - WIKI_TEST_DIR=. -fi - -. "$WIKI_TEST_DIR"/test-gitmw-lib.sh -usage () { - echo "usage: " - echo " ./install-wiki.sh <install | delete | --help>" - echo " install | -i : Install a wiki on your computer." - echo " delete | -d : Delete the wiki and all its pages and " - echo " content." - echo " start | -s : Start the previously configured lighttpd daemon" - echo " stop : Stop lighttpd daemon." -} - - -# Argument: install, delete, --help | -h -case "$1" in - "install" | "-i") - wiki_install - exit 0 - ;; - "delete" | "-d") - wiki_delete - exit 0 - ;; - "start" | "-s") - start_lighttpd - exit - ;; - "stop") - stop_lighttpd - exit - ;; - "--help" | "-h") - usage - exit 0 - ;; - *) - echo "Invalid argument: $1" - usage - exit 1 - ;; -esac diff --git a/contrib/mw-to-git/t/push-pull-tests.sh b/contrib/mw-to-git/t/push-pull-tests.sh deleted file mode 100644 index 9da2dc5ff0..0000000000 --- a/contrib/mw-to-git/t/push-pull-tests.sh +++ /dev/null @@ -1,144 +0,0 @@ -test_push_pull () { - - test_expect_success 'Git pull works after adding a new wiki page' ' - wiki_reset && - - git clone mediawiki::'"$WIKI_URL"' mw_dir_1 && - wiki_editpage Foo "page created after the git clone" false && - - ( - cd mw_dir_1 && - git pull - ) && - - wiki_getallpage ref_page_1 && - test_diff_directories mw_dir_1 ref_page_1 - ' - - test_expect_success 'Git pull works after editing a wiki page' ' - wiki_reset && - - wiki_editpage Foo "page created before the git clone" false && - git clone mediawiki::'"$WIKI_URL"' mw_dir_2 && - wiki_editpage Foo "new line added on the wiki" true && - - ( - cd mw_dir_2 && - git pull - ) && - - wiki_getallpage ref_page_2 && - test_diff_directories mw_dir_2 ref_page_2 - ' - - test_expect_success 'git pull works on conflict handled by auto-merge' ' - wiki_reset && - - wiki_editpage Foo "1 init -3 -5 - " false && - git clone mediawiki::'"$WIKI_URL"' mw_dir_3 && - - wiki_editpage Foo "1 init -2 content added on wiki after clone -3 -5 - " false && - - ( - cd mw_dir_3 && - echo "1 init -3 -4 content added on git after clone -5 -" >Foo.mw && - git commit -am "conflicting change on foo" && - git pull && - git push - ) - ' - - test_expect_success 'Git push works after adding a file .mw' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir_4 && - wiki_getallpage ref_page_4 && - ( - cd mw_dir_4 && - test_path_is_missing Foo.mw && - touch Foo.mw && - echo "hello world" >>Foo.mw && - git add Foo.mw && - git commit -m "Foo" && - git push - ) && - wiki_getallpage ref_page_4 && - test_diff_directories mw_dir_4 ref_page_4 - ' - - test_expect_success 'Git push works after editing a file .mw' ' - wiki_reset && - wiki_editpage "Foo" "page created before the git clone" false && - git clone mediawiki::'"$WIKI_URL"' mw_dir_5 && - - ( - cd mw_dir_5 && - echo "new line added in the file Foo.mw" >>Foo.mw && - git commit -am "edit file Foo.mw" && - git push - ) && - - wiki_getallpage ref_page_5 && - test_diff_directories mw_dir_5 ref_page_5 - ' - - test_expect_failure 'Git push works after deleting a file' ' - wiki_reset && - wiki_editpage Foo "wiki page added before git clone" false && - git clone mediawiki::'"$WIKI_URL"' mw_dir_6 && - - ( - cd mw_dir_6 && - git rm Foo.mw && - git commit -am "page Foo.mw deleted" && - git push - ) && - - test_must_fail wiki_page_exist Foo - ' - - test_expect_success 'Merge conflict expected and solving it' ' - wiki_reset && - - git clone mediawiki::'"$WIKI_URL"' mw_dir_7 && - wiki_editpage Foo "1 conflict -3 wiki -4" false && - - ( - cd mw_dir_7 && - echo "1 conflict -2 git -4" >Foo.mw && - git add Foo.mw && - git commit -m "conflict created" && - test_must_fail git pull && - "$PERL_PATH" -pi -e "s/[<=>].*//g" Foo.mw && - git commit -am "merge conflict solved" && - git push - ) - ' - - test_expect_failure 'git pull works after deleting a wiki page' ' - wiki_reset && - wiki_editpage Foo "wiki page added before the git clone" false && - git clone mediawiki::'"$WIKI_URL"' mw_dir_8 && - - wiki_delete_page Foo && - ( - cd mw_dir_8 && - git pull && - test_path_is_missing Foo.mw - ) - ' -} diff --git a/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh b/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh deleted file mode 100755 index f08890d9e7..0000000000 --- a/contrib/mw-to-git/t/t9360-mw-to-git-clone.sh +++ /dev/null @@ -1,257 +0,0 @@ -#!/bin/sh -# -# Copyright (C) 2012 -# Charles Roussel <charles.roussel@ensimag.imag.fr> -# Simon Cathebras <simon.cathebras@ensimag.imag.fr> -# Julien Khayat <julien.khayat@ensimag.imag.fr> -# Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr> -# Simon Perrat <simon.perrat@ensimag.imag.fr> -# -# License: GPL v2 or later - - -test_description='Test the Git Mediawiki remote helper: git clone' - -. ./test-gitmw-lib.sh -. $TEST_DIRECTORY/test-lib.sh - - -test_check_precond - - -test_expect_success 'Git clone creates the expected git log with one file' ' - wiki_reset && - wiki_editpage foo "this is not important" false -c cat -s "this must be the same" && - git clone mediawiki::'"$WIKI_URL"' mw_dir_1 && - ( - cd mw_dir_1 && - git log --format=%s HEAD^..HEAD >log.tmp - ) && - echo "this must be the same" >msg.tmp && - test_cmp msg.tmp mw_dir_1/log.tmp -' - - -test_expect_success 'Git clone creates the expected git log with multiple files' ' - wiki_reset && - wiki_editpage daddy "this is not important" false -s="this must be the same" && - wiki_editpage daddy "neither is this" true -s="this must also be the same" && - wiki_editpage daddy "neither is this" true -s="same same same" && - wiki_editpage dj "dont care" false -s="identical" && - wiki_editpage dj "dont care either" true -s="identical too" && - git clone mediawiki::'"$WIKI_URL"' mw_dir_2 && - ( - cd mw_dir_2 && - git log --format=%s Daddy.mw >logDaddy.tmp && - git log --format=%s Dj.mw >logDj.tmp - ) && - echo "same same same" >msgDaddy.tmp && - echo "this must also be the same" >>msgDaddy.tmp && - echo "this must be the same" >>msgDaddy.tmp && - echo "identical too" >msgDj.tmp && - echo "identical" >>msgDj.tmp && - test_cmp msgDaddy.tmp mw_dir_2/logDaddy.tmp && - test_cmp msgDj.tmp mw_dir_2/logDj.tmp -' - - -test_expect_success 'Git clone creates only Main_Page.mw with an empty wiki' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir_3 && - test_contains_N_files mw_dir_3 1 && - test_path_is_file mw_dir_3/Main_Page.mw -' - -test_expect_success 'Git clone does not fetch a deleted page' ' - wiki_reset && - wiki_editpage foo "this page must be deleted before the clone" false && - wiki_delete_page foo && - git clone mediawiki::'"$WIKI_URL"' mw_dir_4 && - test_contains_N_files mw_dir_4 1 && - test_path_is_file mw_dir_4/Main_Page.mw && - test_path_is_missing mw_dir_4/Foo.mw -' - -test_expect_success 'Git clone works with page added' ' - wiki_reset && - wiki_editpage foo " I will be cloned" false && - wiki_editpage bar "I will be cloned" false && - git clone mediawiki::'"$WIKI_URL"' mw_dir_5 && - wiki_getallpage ref_page_5 && - test_diff_directories mw_dir_5 ref_page_5 && - wiki_delete_page foo && - wiki_delete_page bar -' - -test_expect_success 'Git clone works with an edited page ' ' - wiki_reset && - wiki_editpage foo "this page will be edited" \ - false -s "first edition of page foo" && - wiki_editpage foo "this page has been edited and must be on the clone " true && - git clone mediawiki::'"$WIKI_URL"' mw_dir_6 && - test_path_is_file mw_dir_6/Foo.mw && - test_path_is_file mw_dir_6/Main_Page.mw && - wiki_getallpage mw_dir_6/page_ref_6 && - test_diff_directories mw_dir_6 mw_dir_6/page_ref_6 && - ( - cd mw_dir_6 && - git log --format=%s HEAD^ Foo.mw > ../Foo.log - ) && - echo "first edition of page foo" > FooExpect.log && - diff FooExpect.log Foo.log -' - - -test_expect_success 'Git clone works with several pages and some deleted ' ' - wiki_reset && - wiki_editpage foo "this page will not be deleted" false && - wiki_editpage bar "I must not be erased" false && - wiki_editpage namnam "I will not be there at the end" false && - wiki_editpage nyancat "nyan nyan nyan delete me" false && - wiki_delete_page namnam && - wiki_delete_page nyancat && - git clone mediawiki::'"$WIKI_URL"' mw_dir_7 && - test_path_is_file mw_dir_7/Foo.mw && - test_path_is_file mw_dir_7/Bar.mw && - test_path_is_missing mw_dir_7/Namnam.mw && - test_path_is_missing mw_dir_7/Nyancat.mw && - wiki_getallpage mw_dir_7/page_ref_7 && - test_diff_directories mw_dir_7 mw_dir_7/page_ref_7 -' - - -test_expect_success 'Git clone works with one specific page cloned ' ' - wiki_reset && - wiki_editpage foo "I will not be cloned" false && - wiki_editpage bar "Do not clone me" false && - wiki_editpage namnam "I will be cloned :)" false -s="this log must stay" && - wiki_editpage nyancat "nyan nyan nyan you cant clone me" false && - git clone -c remote.origin.pages=namnam \ - mediawiki::'"$WIKI_URL"' mw_dir_8 && - test_contains_N_files mw_dir_8 1 && - test_path_is_file mw_dir_8/Namnam.mw && - test_path_is_missing mw_dir_8/Main_Page.mw && - ( - cd mw_dir_8 && - echo "this log must stay" >msg.tmp && - git log --format=%s >log.tmp && - test_cmp msg.tmp log.tmp - ) && - wiki_check_content mw_dir_8/Namnam.mw Namnam -' - -test_expect_success 'Git clone works with multiple specific page cloned ' ' - wiki_reset && - wiki_editpage foo "I will be there" false && - wiki_editpage bar "I will not disappear" false && - wiki_editpage namnam "I be erased" false && - wiki_editpage nyancat "nyan nyan nyan you will not erase me" false && - wiki_delete_page namnam && - git clone -c remote.origin.pages="foo bar nyancat namnam" \ - mediawiki::'"$WIKI_URL"' mw_dir_9 && - test_contains_N_files mw_dir_9 3 && - test_path_is_missing mw_dir_9/Namnam.mw && - test_path_is_file mw_dir_9/Foo.mw && - test_path_is_file mw_dir_9/Nyancat.mw && - test_path_is_file mw_dir_9/Bar.mw && - wiki_check_content mw_dir_9/Foo.mw Foo && - wiki_check_content mw_dir_9/Bar.mw Bar && - wiki_check_content mw_dir_9/Nyancat.mw Nyancat -' - -test_expect_success 'Mediawiki-clone of several specific pages on wiki' ' - wiki_reset && - wiki_editpage foo "foo 1" false && - wiki_editpage bar "bar 1" false && - wiki_editpage dummy "dummy 1" false && - wiki_editpage cloned_1 "cloned_1 1" false && - wiki_editpage cloned_2 "cloned_2 2" false && - wiki_editpage cloned_3 "cloned_3 3" false && - mkdir -p ref_page_10 && - wiki_getpage cloned_1 ref_page_10 && - wiki_getpage cloned_2 ref_page_10 && - wiki_getpage cloned_3 ref_page_10 && - git clone -c remote.origin.pages="cloned_1 cloned_2 cloned_3" \ - mediawiki::'"$WIKI_URL"' mw_dir_10 && - test_diff_directories mw_dir_10 ref_page_10 -' - -test_expect_success 'Git clone works with the shallow option' ' - wiki_reset && - wiki_editpage foo "1st revision, should be cloned" false && - wiki_editpage bar "1st revision, should be cloned" false && - wiki_editpage nyan "1st revision, should not be cloned" false && - wiki_editpage nyan "2nd revision, should be cloned" false && - git -c remote.origin.shallow=true clone \ - mediawiki::'"$WIKI_URL"' mw_dir_11 && - test_contains_N_files mw_dir_11 4 && - test_path_is_file mw_dir_11/Nyan.mw && - test_path_is_file mw_dir_11/Foo.mw && - test_path_is_file mw_dir_11/Bar.mw && - test_path_is_file mw_dir_11/Main_Page.mw && - ( - cd mw_dir_11 && - test $(git log --oneline Nyan.mw | wc -l) -eq 1 && - test $(git log --oneline Foo.mw | wc -l) -eq 1 && - test $(git log --oneline Bar.mw | wc -l) -eq 1 && - test $(git log --oneline Main_Page.mw | wc -l ) -eq 1 - ) && - wiki_check_content mw_dir_11/Nyan.mw Nyan && - wiki_check_content mw_dir_11/Foo.mw Foo && - wiki_check_content mw_dir_11/Bar.mw Bar && - wiki_check_content mw_dir_11/Main_Page.mw Main_Page -' - -test_expect_success 'Git clone works with the shallow option with a delete page' ' - wiki_reset && - wiki_editpage foo "1st revision, will be deleted" false && - wiki_editpage bar "1st revision, should be cloned" false && - wiki_editpage nyan "1st revision, should not be cloned" false && - wiki_editpage nyan "2nd revision, should be cloned" false && - wiki_delete_page foo && - git -c remote.origin.shallow=true clone \ - mediawiki::'"$WIKI_URL"' mw_dir_12 && - test_contains_N_files mw_dir_12 3 && - test_path_is_file mw_dir_12/Nyan.mw && - test_path_is_missing mw_dir_12/Foo.mw && - test_path_is_file mw_dir_12/Bar.mw && - test_path_is_file mw_dir_12/Main_Page.mw && - ( - cd mw_dir_12 && - test $(git log --oneline Nyan.mw | wc -l) -eq 1 && - test $(git log --oneline Bar.mw | wc -l) -eq 1 && - test $(git log --oneline Main_Page.mw | wc -l ) -eq 1 - ) && - wiki_check_content mw_dir_12/Nyan.mw Nyan && - wiki_check_content mw_dir_12/Bar.mw Bar && - wiki_check_content mw_dir_12/Main_Page.mw Main_Page -' - -test_expect_success 'Test of fetching a category' ' - wiki_reset && - wiki_editpage Foo "I will be cloned" false -c=Category && - wiki_editpage Bar "Meet me on the repository" false -c=Category && - wiki_editpage Dummy "I will not come" false && - wiki_editpage BarWrong "I will stay online only" false -c=NotCategory && - git clone -c remote.origin.categories="Category" \ - mediawiki::'"$WIKI_URL"' mw_dir_13 && - wiki_getallpage ref_page_13 Category && - test_diff_directories mw_dir_13 ref_page_13 -' - -test_expect_success 'Test of resistance to modification of category on wiki for clone' ' - wiki_reset && - wiki_editpage Tobedeleted "this page will be deleted" false -c=Catone && - wiki_editpage Tobeedited "this page will be modified" false -c=Catone && - wiki_editpage Normalone "this page wont be modified and will be on git" false -c=Catone && - wiki_editpage Notconsidered "this page will not appear on local" false && - wiki_editpage Othercategory "this page will not appear on local" false -c=Cattwo && - wiki_editpage Tobeedited "this page have been modified" true -c=Catone && - wiki_delete_page Tobedeleted && - git clone -c remote.origin.categories="Catone" \ - mediawiki::'"$WIKI_URL"' mw_dir_14 && - wiki_getallpage ref_page_14 Catone && - test_diff_directories mw_dir_14 ref_page_14 -' - -test_done diff --git a/contrib/mw-to-git/t/t9361-mw-to-git-push-pull.sh b/contrib/mw-to-git/t/t9361-mw-to-git-push-pull.sh deleted file mode 100755 index 9ea201459b..0000000000 --- a/contrib/mw-to-git/t/t9361-mw-to-git-push-pull.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# -# Copyright (C) 2012 -# Charles Roussel <charles.roussel@ensimag.imag.fr> -# Simon Cathebras <simon.cathebras@ensimag.imag.fr> -# Julien Khayat <julien.khayat@ensimag.imag.fr> -# Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr> -# Simon Perrat <simon.perrat@ensimag.imag.fr> -# -# License: GPL v2 or later - -# tests for git-remote-mediawiki - -test_description='Test the Git Mediawiki remote helper: git push and git pull simple test cases' - -. ./test-gitmw-lib.sh -. ./push-pull-tests.sh -. $TEST_DIRECTORY/test-lib.sh - -test_check_precond - -test_push_pull - -test_done diff --git a/contrib/mw-to-git/t/t9362-mw-to-git-utf8.sh b/contrib/mw-to-git/t/t9362-mw-to-git-utf8.sh deleted file mode 100755 index 526d92850f..0000000000 --- a/contrib/mw-to-git/t/t9362-mw-to-git-utf8.sh +++ /dev/null @@ -1,347 +0,0 @@ -#!/bin/sh -# -# Copyright (C) 2012 -# Charles Roussel <charles.roussel@ensimag.imag.fr> -# Simon Cathebras <simon.cathebras@ensimag.imag.fr> -# Julien Khayat <julien.khayat@ensimag.imag.fr> -# Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr> -# Simon Perrat <simon.perrat@ensimag.imag.fr> -# -# License: GPL v2 or later - -# tests for git-remote-mediawiki - -test_description='Test git-mediawiki with special characters in filenames' - -. ./test-gitmw-lib.sh -. $TEST_DIRECTORY/test-lib.sh - - -test_check_precond - - -test_expect_success 'Git clone works for a wiki with accents in the page names' ' - wiki_reset && - wiki_editpage féé "This page must be délétéd before clone" false && - wiki_editpage kèè "This page must be deleted before clone" false && - wiki_editpage hà à "This page must be deleted before clone" false && - wiki_editpage kîî "This page must be deleted before clone" false && - wiki_editpage foo "This page must be deleted before clone" false && - git clone mediawiki::'"$WIKI_URL"' mw_dir_1 && - wiki_getallpage ref_page_1 && - test_diff_directories mw_dir_1 ref_page_1 -' - - -test_expect_success 'Git pull works with a wiki with accents in the pages names' ' - wiki_reset && - wiki_editpage kîî "this page must be cloned" false && - wiki_editpage foo "this page must be cloned" false && - git clone mediawiki::'"$WIKI_URL"' mw_dir_2 && - wiki_editpage éà îôû "This page must be pulled" false && - ( - cd mw_dir_2 && - git pull - ) && - wiki_getallpage ref_page_2 && - test_diff_directories mw_dir_2 ref_page_2 -' - - -test_expect_success 'Cloning a chosen page works with accents' ' - wiki_reset && - wiki_editpage kîî "this page must be cloned" false && - git clone -c remote.origin.pages=kîî \ - mediawiki::'"$WIKI_URL"' mw_dir_3 && - wiki_check_content mw_dir_3/Kîî.mw Kîî && - test_path_is_file mw_dir_3/Kîî.mw && - rm -rf mw_dir_3 -' - - -test_expect_success 'The shallow option works with accents' ' - wiki_reset && - wiki_editpage néoà "1st revision, should not be cloned" false && - wiki_editpage néoà "2nd revision, should be cloned" false && - git -c remote.origin.shallow=true clone \ - mediawiki::'"$WIKI_URL"' mw_dir_4 && - test_contains_N_files mw_dir_4 2 && - test_path_is_file mw_dir_4/Néoà .mw && - test_path_is_file mw_dir_4/Main_Page.mw && - ( - cd mw_dir_4 && - test $(git log --oneline Néoà .mw | wc -l) -eq 1 && - test $(git log --oneline Main_Page.mw | wc -l ) -eq 1 - ) && - wiki_check_content mw_dir_4/Néoà .mw Néoà && - wiki_check_content mw_dir_4/Main_Page.mw Main_Page -' - - -test_expect_success 'Cloning works when page name first letter has an accent' ' - wiki_reset && - wiki_editpage îî "this page must be cloned" false && - git clone -c remote.origin.pages=îî \ - mediawiki::'"$WIKI_URL"' mw_dir_5 && - test_path_is_file mw_dir_5/Îî.mw && - wiki_check_content mw_dir_5/Îî.mw Îî -' - - -test_expect_success 'Git push works with a wiki with accents' ' - wiki_reset && - wiki_editpage féé "lots of accents : éèà Ö" false && - wiki_editpage foo "this page must be cloned" false && - git clone mediawiki::'"$WIKI_URL"' mw_dir_6 && - ( - cd mw_dir_6 && - echo "A wild Pîkächû appears on the wiki" >Pîkächû.mw && - git add Pîkächû.mw && - git commit -m "A new page appears" && - git push - ) && - wiki_getallpage ref_page_6 && - test_diff_directories mw_dir_6 ref_page_6 -' - -test_expect_success 'Git clone works with accentsand spaces' ' - wiki_reset && - wiki_editpage "é à î" "this page must be délété before the clone" false && - git clone mediawiki::'"$WIKI_URL"' mw_dir_7 && - wiki_getallpage ref_page_7 && - test_diff_directories mw_dir_7 ref_page_7 -' - -test_expect_success 'character $ in page name (mw -> git)' ' - wiki_reset && - wiki_editpage file_\$_foo "expect to be called file_$_foo" false && - git clone mediawiki::'"$WIKI_URL"' mw_dir_8 && - test_path_is_file mw_dir_8/File_\$_foo.mw && - wiki_getallpage ref_page_8 && - test_diff_directories mw_dir_8 ref_page_8 -' - - - -test_expect_success 'character $ in file name (git -> mw) ' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir_9 && - ( - cd mw_dir_9 && - echo "this file is called File_\$_foo.mw" >File_\$_foo.mw && - git add . && - git commit -am "file File_\$_foo.mw" && - git pull && - git push - ) && - wiki_getallpage ref_page_9 && - test_diff_directories mw_dir_9 ref_page_9 -' - - -test_expect_failure 'capital at the beginning of file names' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir_10 && - ( - cd mw_dir_10 && - echo "my new file foo" >foo.mw && - echo "my new file Foo... Finger crossed" >Foo.mw && - git add . && - git commit -am "file foo.mw" && - git pull && - git push - ) && - wiki_getallpage ref_page_10 && - test_diff_directories mw_dir_10 ref_page_10 -' - - -test_expect_failure 'special character at the beginning of file name from mw to git' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir_11 && - wiki_editpage {char_1 "expect to be renamed {char_1" false && - wiki_editpage [char_2 "expect to be renamed [char_2" false && - ( - cd mw_dir_11 && - git pull - ) && - test_path_is_file mw_dir_11/{char_1 && - test_path_is_file mw_dir_11/[char_2 -' - -test_expect_success 'Pull page with title containing ":" other than namespace separator' ' - wiki_editpage Foo:Bar content false && - ( - cd mw_dir_11 && - git pull - ) && - test_path_is_file mw_dir_11/Foo:Bar.mw -' - -test_expect_success 'Push page with title containing ":" other than namespace separator' ' - ( - cd mw_dir_11 && - echo content >NotANameSpace:Page.mw && - git add NotANameSpace:Page.mw && - git commit -m "add page with colon" && - git push - ) && - wiki_page_exist NotANameSpace:Page -' - -test_expect_success 'test of correct formatting for file name from mw to git' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir_12 && - wiki_editpage char_%_7b_1 "expect to be renamed char{_1" false && - wiki_editpage char_%_5b_2 "expect to be renamed char{_2" false && - ( - cd mw_dir_12 && - git pull - ) && - test_path_is_file mw_dir_12/Char\{_1.mw && - test_path_is_file mw_dir_12/Char\[_2.mw && - wiki_getallpage ref_page_12 && - mv ref_page_12/Char_%_7b_1.mw ref_page_12/Char\{_1.mw && - mv ref_page_12/Char_%_5b_2.mw ref_page_12/Char\[_2.mw && - test_diff_directories mw_dir_12 ref_page_12 -' - - -test_expect_failure 'test of correct formatting for file name beginning with special character' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir_13 && - ( - cd mw_dir_13 && - echo "my new file {char_1" >\{char_1.mw && - echo "my new file [char_2" >\[char_2.mw && - git add . && - git commit -am "committing some exotic file name..." && - git push && - git pull - ) && - wiki_getallpage ref_page_13 && - test_path_is_file ref_page_13/{char_1.mw && - test_path_is_file ref_page_13/[char_2.mw && - test_diff_directories mw_dir_13 ref_page_13 -' - - -test_expect_success 'test of correct formatting for file name from git to mw' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir_14 && - ( - cd mw_dir_14 && - echo "my new file char{_1" >Char\{_1.mw && - echo "my new file char[_2" >Char\[_2.mw && - git add . && - git commit -m "committing some exotic file name..." && - git push - ) && - wiki_getallpage ref_page_14 && - mv mw_dir_14/Char\{_1.mw mw_dir_14/Char_%_7b_1.mw && - mv mw_dir_14/Char\[_2.mw mw_dir_14/Char_%_5b_2.mw && - test_diff_directories mw_dir_14 ref_page_14 -' - - -test_expect_success 'git clone with /' ' - wiki_reset && - wiki_editpage \/fo\/o "this is not important" false -c=Deleted && - git clone mediawiki::'"$WIKI_URL"' mw_dir_15 && - test_path_is_file mw_dir_15/%2Ffo%2Fo.mw && - wiki_check_content mw_dir_15/%2Ffo%2Fo.mw \/fo\/o -' - - -test_expect_success 'git push with /' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir_16 && - echo "I will be on the wiki" >mw_dir_16/%2Ffo%2Fo.mw && - ( - cd mw_dir_16 && - git add %2Ffo%2Fo.mw && - git commit -m " %2Ffo%2Fo added" && - git push - ) && - wiki_page_exist \/fo\/o && - wiki_check_content mw_dir_16/%2Ffo%2Fo.mw \/fo\/o - -' - - -test_expect_success 'git clone with \' ' - wiki_reset && - wiki_editpage \\ko\\o "this is not important" false -c=Deleted && - git clone mediawiki::'"$WIKI_URL"' mw_dir_17 && - test_path_is_file mw_dir_17/\\ko\\o.mw && - wiki_check_content mw_dir_17/\\ko\\o.mw \\ko\\o -' - - -test_expect_success 'git push with \' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir_18 && - echo "I will be on the wiki" >mw_dir_18/\\ko\\o.mw && - ( - cd mw_dir_18 && - git add \\ko\\o.mw && - git commit -m " \\ko\\o added" && - git push - ) && - wiki_page_exist \\ko\\o && - wiki_check_content mw_dir_18/\\ko\\o.mw \\ko\\o - -' - -test_expect_success 'git clone with \ in format control' ' - wiki_reset && - wiki_editpage \\no\\o "this is not important" false && - git clone mediawiki::'"$WIKI_URL"' mw_dir_19 && - test_path_is_file mw_dir_19/\\no\\o.mw && - wiki_check_content mw_dir_19/\\no\\o.mw \\no\\o -' - - -test_expect_success 'git push with \ in format control' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir_20 && - echo "I will be on the wiki" >mw_dir_20/\\fo\\o.mw && - ( - cd mw_dir_20 && - git add \\fo\\o.mw && - git commit -m " \\fo\\o added" && - git push - ) && - wiki_page_exist \\fo\\o && - wiki_check_content mw_dir_20/\\fo\\o.mw \\fo\\o - -' - - -test_expect_success 'fast-import meta-characters in page name (mw -> git)' ' - wiki_reset && - wiki_editpage \"file\"_\\_foo "expect to be called \"file\"_\\_foo" false && - git clone mediawiki::'"$WIKI_URL"' mw_dir_21 && - test_path_is_file mw_dir_21/\"file\"_\\_foo.mw && - wiki_getallpage ref_page_21 && - test_diff_directories mw_dir_21 ref_page_21 -' - - -test_expect_success 'fast-import meta-characters in page name (git -> mw) ' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir_22 && - ( - cd mw_dir_22 && - echo "this file is called \"file\"_\\_foo.mw" >\"file\"_\\_foo && - git add . && - git commit -am "file \"file\"_\\_foo" && - git pull && - git push - ) && - wiki_getallpage ref_page_22 && - test_diff_directories mw_dir_22 ref_page_22 -' - - -test_done diff --git a/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh b/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh deleted file mode 100755 index 7139995a40..0000000000 --- a/contrib/mw-to-git/t/t9363-mw-to-git-export-import.sh +++ /dev/null @@ -1,218 +0,0 @@ -#!/bin/sh -# -# Copyright (C) 2012 -# Charles Roussel <charles.roussel@ensimag.imag.fr> -# Simon Cathebras <simon.cathebras@ensimag.imag.fr> -# Julien Khayat <julien.khayat@ensimag.imag.fr> -# Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr> -# Simon Perrat <simon.perrat@ensimag.imag.fr> -# -# License: GPL v2 or later - -# tests for git-remote-mediawiki - -test_description='Test the Git Mediawiki remote helper: git push and git pull simple test cases' - -. ./test-gitmw-lib.sh -. $TEST_DIRECTORY/test-lib.sh - - -test_check_precond - - -test_git_reimport () { - git -c remote.origin.dumbPush=true push && - git -c remote.origin.mediaImport=true pull --rebase -} - -# Don't bother with permissions, be administrator by default -test_expect_success 'setup config' ' - git config --global remote.origin.mwLogin "$WIKI_ADMIN" && - git config --global remote.origin.mwPassword "$WIKI_PASSW" && - test_might_fail git config --global --unset remote.origin.mediaImport -' - -test_expect_failure 'git push can upload media (File:) files' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir && - ( - cd mw_dir && - echo "hello world" >Foo.txt && - git add Foo.txt && - git commit -m "add a text file" && - git push && - "$PERL_PATH" -e "print STDOUT \"binary content: \".chr(255);" >Foo.txt && - git add Foo.txt && - git commit -m "add a text file with binary content" && - git push - ) -' - -test_expect_failure 'git clone works on previously created wiki with media files' ' - test_when_finished "rm -rf mw_dir mw_dir_clone" && - git clone -c remote.origin.mediaimport=true \ - mediawiki::'"$WIKI_URL"' mw_dir_clone && - test_cmp mw_dir_clone/Foo.txt mw_dir/Foo.txt && - (cd mw_dir_clone && git checkout HEAD^) && - (cd mw_dir && git checkout HEAD^) && - test_path_is_file mw_dir_clone/Foo.txt && - test_cmp mw_dir_clone/Foo.txt mw_dir/Foo.txt -' - -test_expect_success 'git push can upload media (File:) files containing valid UTF-8' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir && - ( - cd mw_dir && - "$PERL_PATH" -e "print STDOUT \"UTF-8 content: éèà éê€.\";" >Bar.txt && - git add Bar.txt && - git commit -m "add a text file with UTF-8 content" && - git push - ) -' - -test_expect_success 'git clone works on previously created wiki with media files containing valid UTF-8' ' - test_when_finished "rm -rf mw_dir mw_dir_clone" && - git clone -c remote.origin.mediaimport=true \ - mediawiki::'"$WIKI_URL"' mw_dir_clone && - test_cmp mw_dir_clone/Bar.txt mw_dir/Bar.txt -' - -test_expect_success 'git push & pull work with locally renamed media files' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir && - test_when_finished "rm -fr mw_dir" && - ( - cd mw_dir && - echo "A File" >Foo.txt && - git add Foo.txt && - git commit -m "add a file" && - git mv Foo.txt Bar.txt && - git commit -m "Rename a file" && - test_git_reimport && - echo "A File" >expect && - test_cmp expect Bar.txt && - test_path_is_missing Foo.txt - ) -' - -test_expect_success 'git push can propagate local page deletion' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir && - test_when_finished "rm -fr mw_dir" && - ( - cd mw_dir && - test_path_is_missing Foo.mw && - echo "hello world" >Foo.mw && - git add Foo.mw && - git commit -m "Add the page Foo" && - git push && - rm -f Foo.mw && - git commit -am "Delete the page Foo" && - test_git_reimport && - test_path_is_missing Foo.mw - ) -' - -test_expect_success 'git push can propagate local media file deletion' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir && - test_when_finished "rm -fr mw_dir" && - ( - cd mw_dir && - echo "hello world" >Foo.txt && - git add Foo.txt && - git commit -m "Add the text file Foo" && - git rm Foo.txt && - git commit -m "Delete the file Foo" && - test_git_reimport && - test_path_is_missing Foo.txt - ) -' - -# test failure: the file is correctly uploaded, and then deleted but -# as no page link to it, the import (which looks at page revisions) -# doesn't notice the file deletion on the wiki. We fetch the list of -# files from the wiki, but as the file is deleted, it doesn't appear. -test_expect_failure 'git pull correctly imports media file deletion when no page link to it' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir && - test_when_finished "rm -fr mw_dir" && - ( - cd mw_dir && - echo "hello world" >Foo.txt && - git add Foo.txt && - git commit -m "Add the text file Foo" && - git push && - git rm Foo.txt && - git commit -m "Delete the file Foo" && - test_git_reimport && - test_path_is_missing Foo.txt - ) -' - -test_expect_success 'git push properly warns about insufficient permissions' ' - wiki_reset && - git clone mediawiki::'"$WIKI_URL"' mw_dir && - test_when_finished "rm -fr mw_dir" && - ( - cd mw_dir && - echo "A File" >foo.forbidden && - git add foo.forbidden && - git commit -m "add a file" && - git push 2>actual && - test_grep "foo.forbidden is not a permitted file" actual - ) -' - -test_expect_success 'setup a repository with media files' ' - wiki_reset && - wiki_editpage testpage "I am linking a file [[File:File.txt]]" false && - echo "File content" >File.txt && - wiki_upload_file File.txt && - echo "Another file content" >AnotherFile.txt && - wiki_upload_file AnotherFile.txt -' - -test_expect_success 'git clone works with one specific page cloned and mediaimport=true' ' - git clone -c remote.origin.pages=testpage \ - -c remote.origin.mediaimport=true \ - mediawiki::'"$WIKI_URL"' mw_dir_15 && - test_when_finished "rm -rf mw_dir_15" && - test_contains_N_files mw_dir_15 3 && - test_path_is_file mw_dir_15/Testpage.mw && - test_path_is_file mw_dir_15/File:File.txt.mw && - test_path_is_file mw_dir_15/File.txt && - test_path_is_missing mw_dir_15/Main_Page.mw && - test_path_is_missing mw_dir_15/File:AnotherFile.txt.mw && - test_path_is_missing mw_dir_15/AnothetFile.txt && - wiki_check_content mw_dir_15/Testpage.mw Testpage && - test_cmp mw_dir_15/File.txt File.txt -' - -test_expect_success 'git clone works with one specific page cloned and mediaimport=false' ' - test_when_finished "rm -rf mw_dir_16" && - git clone -c remote.origin.pages=testpage \ - mediawiki::'"$WIKI_URL"' mw_dir_16 && - test_contains_N_files mw_dir_16 1 && - test_path_is_file mw_dir_16/Testpage.mw && - test_path_is_missing mw_dir_16/File:File.txt.mw && - test_path_is_missing mw_dir_16/File.txt && - test_path_is_missing mw_dir_16/Main_Page.mw && - wiki_check_content mw_dir_16/Testpage.mw Testpage -' - -# should behave like mediaimport=false -test_expect_success 'git clone works with one specific page cloned and mediaimport unset' ' - test_when_finished "rm -fr mw_dir_17" && - git clone -c remote.origin.pages=testpage \ - mediawiki::'"$WIKI_URL"' mw_dir_17 && - test_contains_N_files mw_dir_17 1 && - test_path_is_file mw_dir_17/Testpage.mw && - test_path_is_missing mw_dir_17/File:File.txt.mw && - test_path_is_missing mw_dir_17/File.txt && - test_path_is_missing mw_dir_17/Main_Page.mw && - wiki_check_content mw_dir_17/Testpage.mw Testpage -' - -test_done diff --git a/contrib/mw-to-git/t/t9364-pull-by-rev.sh b/contrib/mw-to-git/t/t9364-pull-by-rev.sh deleted file mode 100755 index 5c22457a0b..0000000000 --- a/contrib/mw-to-git/t/t9364-pull-by-rev.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -test_description='Test the Git Mediawiki remote helper: git pull by revision' - -. ./test-gitmw-lib.sh -. ./push-pull-tests.sh -. $TEST_DIRECTORY/test-lib.sh - -test_check_precond - -test_expect_success 'configuration' ' - git config --global mediawiki.fetchStrategy by_rev -' - -test_push_pull - -test_done diff --git a/contrib/mw-to-git/t/t9365-continuing-queries.sh b/contrib/mw-to-git/t/t9365-continuing-queries.sh deleted file mode 100755 index d3e7312659..0000000000 --- a/contrib/mw-to-git/t/t9365-continuing-queries.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh - -test_description='Test the Git Mediawiki remote helper: queries w/ more than 500 results' - -. ./test-gitmw-lib.sh -. $TEST_DIRECTORY/test-lib.sh - -test_check_precond - -test_expect_success 'creating page w/ >500 revisions' ' - wiki_reset && - for i in $(test_seq 501) - do - echo "creating revision $i" && - wiki_editpage foo "revision $i<br/>" true || return 1 - done -' - -test_expect_success 'cloning page w/ >500 revisions' ' - git clone mediawiki::'"$WIKI_URL"' mw_dir -' - -test_done diff --git a/contrib/mw-to-git/t/test-gitmw-lib.sh b/contrib/mw-to-git/t/test-gitmw-lib.sh deleted file mode 100755 index 64e46c1671..0000000000 --- a/contrib/mw-to-git/t/test-gitmw-lib.sh +++ /dev/null @@ -1,432 +0,0 @@ -# Copyright (C) 2012 -# Charles Roussel <charles.roussel@ensimag.imag.fr> -# Simon Cathebras <simon.cathebras@ensimag.imag.fr> -# Julien Khayat <julien.khayat@ensimag.imag.fr> -# Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr> -# Simon Perrat <simon.perrat@ensimag.imag.fr> -# License: GPL v2 or later - -# -# CONFIGURATION VARIABLES -# You might want to change these ones -# - -. ./test.config - -WIKI_BASE_URL=http://$SERVER_ADDR:$PORT -WIKI_URL=$WIKI_BASE_URL/$WIKI_DIR_NAME -CURR_DIR=$(pwd) -TEST_OUTPUT_DIRECTORY=$(pwd) -TEST_DIRECTORY="$CURR_DIR"/../../../t - -export TEST_OUTPUT_DIRECTORY TEST_DIRECTORY CURR_DIR - -if test "$LIGHTTPD" = "false" ; then - PORT=80 -else - WIKI_DIR_INST="$CURR_DIR/$WEB_WWW" -fi - -wiki_upload_file () { - "$CURR_DIR"/test-gitmw.pl upload_file "$@" -} - -wiki_getpage () { - "$CURR_DIR"/test-gitmw.pl get_page "$@" -} - -wiki_delete_page () { - "$CURR_DIR"/test-gitmw.pl delete_page "$@" -} - -wiki_editpage () { - "$CURR_DIR"/test-gitmw.pl edit_page "$@" -} - -die () { - die_with_status 1 "$@" -} - -die_with_status () { - status=$1 - shift - echo >&2 "$*" - exit "$status" -} - - -# Check the preconditions to run git-remote-mediawiki's tests -test_check_precond () { - if ! test_have_prereq PERL - then - skip_all='skipping gateway git-mw tests, perl not available' - test_done - fi - - GIT_EXEC_PATH=$(cd "$(dirname "$0")" && cd "../.." && pwd) - PATH="$GIT_EXEC_PATH"'/bin-wrapper:'"$PATH" - - if ! test -d "$WIKI_DIR_INST/$WIKI_DIR_NAME" - then - skip_all='skipping gateway git-mw tests, no mediawiki found' - test_done - fi -} - -# test_diff_directories <dir_git> <dir_wiki> -# -# Compare the contents of directories <dir_git> and <dir_wiki> with diff -# and errors if they do not match. The program will -# not look into .git in the process. -# Warning: the first argument MUST be the directory containing the git data -test_diff_directories () { - rm -rf "$1_tmp" - mkdir -p "$1_tmp" - cp "$1"/*.mw "$1_tmp" - diff -r -b "$1_tmp" "$2" -} - -# $1=<dir> -# $2=<N> -# -# Check that <dir> contains exactly <N> files -test_contains_N_files () { - if test $(ls -- "$1" | wc -l) -ne "$2"; then - echo "directory $1 should contain $2 files" - echo "it contains these files:" - ls "$1" - false - fi -} - - -# wiki_check_content <file_name> <page_name> -# -# Compares the contents of the file <file_name> and the wiki page -# <page_name> and exits with error 1 if they do not match. -wiki_check_content () { - mkdir -p wiki_tmp - wiki_getpage "$2" wiki_tmp - # replacement of forbidden character in file name - page_name=$(printf "%s\n" "$2" | sed -e "s/\//%2F/g") - - diff -b "$1" wiki_tmp/"$page_name".mw - if test $? -ne 0 - then - rm -rf wiki_tmp - error "ERROR: file $2 not found on wiki" - fi - rm -rf wiki_tmp -} - -# wiki_page_exist <page_name> -# -# Check the existence of the page <page_name> on the wiki and exits -# with error if it is absent from it. -wiki_page_exist () { - mkdir -p wiki_tmp - wiki_getpage "$1" wiki_tmp - page_name=$(printf "%s\n" "$1" | sed "s/\//%2F/g") - if test -f wiki_tmp/"$page_name".mw ; then - rm -rf wiki_tmp - else - rm -rf wiki_tmp - error "test failed: file $1 not found on wiki" - fi -} - -# wiki_getallpagename -# -# Fetch the name of each page on the wiki. -wiki_getallpagename () { - "$CURR_DIR"/test-gitmw.pl getallpagename -} - -# wiki_getallpagecategory <category> -# -# Fetch the name of each page belonging to <category> on the wiki. -wiki_getallpagecategory () { - "$CURR_DIR"/test-gitmw.pl getallpagename "$@" -} - -# wiki_getallpage <dest_dir> [<category>] -# -# Fetch all the pages from the wiki and place them in the directory -# <dest_dir>. -# If <category> is define, then wiki_getallpage fetch the pages included -# in <category>. -wiki_getallpage () { - if test -z "$2"; - then - wiki_getallpagename - else - wiki_getallpagecategory "$2" - fi - mkdir -p "$1" - while read -r line; do - wiki_getpage "$line" $1; - done < all.txt -} - -# ================= Install part ================= - -error () { - echo "$@" >&2 - exit 1 -} - -# config_lighttpd -# -# Create the configuration files and the folders necessary to start lighttpd. -# Overwrite any existing file. -config_lighttpd () { - mkdir -p $WEB - mkdir -p $WEB_TMP - mkdir -p $WEB_WWW - cat > $WEB/lighttpd.conf <<EOF - server.document-root = "$CURR_DIR/$WEB_WWW" - server.port = $PORT - server.pid-file = "$CURR_DIR/$WEB_TMP/pid" - - server.modules = ( - "mod_rewrite", - "mod_redirect", - "mod_access", - "mod_accesslog", - "mod_fastcgi" - ) - - index-file.names = ("index.php" , "index.html") - - mimetype.assign = ( - ".pdf" => "application/pdf", - ".sig" => "application/pgp-signature", - ".spl" => "application/futuresplash", - ".class" => "application/octet-stream", - ".ps" => "application/postscript", - ".torrent" => "application/x-bittorrent", - ".dvi" => "application/x-dvi", - ".gz" => "application/x-gzip", - ".pac" => "application/x-ns-proxy-autoconfig", - ".swf" => "application/x-shockwave-flash", - ".tar.gz" => "application/x-tgz", - ".tgz" => "application/x-tgz", - ".tar" => "application/x-tar", - ".zip" => "application/zip", - ".mp3" => "audio/mpeg", - ".m3u" => "audio/x-mpegurl", - ".wma" => "audio/x-ms-wma", - ".wax" => "audio/x-ms-wax", - ".ogg" => "application/ogg", - ".wav" => "audio/x-wav", - ".gif" => "image/gif", - ".jpg" => "image/jpeg", - ".jpeg" => "image/jpeg", - ".png" => "image/png", - ".xbm" => "image/x-xbitmap", - ".xpm" => "image/x-xpixmap", - ".xwd" => "image/x-xwindowdump", - ".css" => "text/css", - ".html" => "text/html", - ".htm" => "text/html", - ".js" => "text/javascript", - ".asc" => "text/plain", - ".c" => "text/plain", - ".cpp" => "text/plain", - ".log" => "text/plain", - ".conf" => "text/plain", - ".text" => "text/plain", - ".txt" => "text/plain", - ".dtd" => "text/xml", - ".xml" => "text/xml", - ".mpeg" => "video/mpeg", - ".mpg" => "video/mpeg", - ".mov" => "video/quicktime", - ".qt" => "video/quicktime", - ".avi" => "video/x-msvideo", - ".asf" => "video/x-ms-asf", - ".asx" => "video/x-ms-asf", - ".wmv" => "video/x-ms-wmv", - ".bz2" => "application/x-bzip", - ".tbz" => "application/x-bzip-compressed-tar", - ".tar.bz2" => "application/x-bzip-compressed-tar", - "" => "text/plain" - ) - - fastcgi.server = ( ".php" => - ("localhost" => - ( "socket" => "$CURR_DIR/$WEB_TMP/php.socket", - "bin-path" => "$PHP_DIR/php-cgi -c $CURR_DIR/$WEB/php.ini" - - ) - ) - ) -EOF - - cat > $WEB/php.ini <<EOF - session.save_path ='$CURR_DIR/$WEB_TMP' -EOF -} - -# start_lighttpd -# -# Start or restart daemon lighttpd. If restart, rewrite configuration files. -start_lighttpd () { - if test -f "$WEB_TMP/pid"; then - echo "Instance already running. Restarting..." - stop_lighttpd - fi - config_lighttpd - "$LIGHTTPD_DIR"/lighttpd -f "$WEB"/lighttpd.conf - - if test $? -ne 0 ; then - echo "Could not execute http daemon lighttpd" - exit 1 - fi -} - -# stop_lighttpd -# -# Kill daemon lighttpd and removes files and folders associated. -stop_lighttpd () { - test -f "$WEB_TMP/pid" && kill $(cat "$WEB_TMP/pid") -} - -wiki_delete_db () { - rm -rf \ - "$FILES_FOLDER_DB"/* || error "Couldn't delete $FILES_FOLDER_DB/" -} - -wiki_delete_db_backup () { - rm -rf \ - "$FILES_FOLDER_POST_INSTALL_DB"/* || error "Couldn't delete $FILES_FOLDER_POST_INSTALL_DB/" -} - -# Install MediaWiki using its install.php script. If the database file -# already exists, it will be deleted. -install_mediawiki () { - - localsettings="$WIKI_DIR_INST/$WIKI_DIR_NAME/LocalSettings.php" - if test -f "$localsettings" - then - error "We already installed the wiki, since $localsettings exists" \ - "perhaps you wanted to run 'delete' first?" - fi - - wiki_delete_db - wiki_delete_db_backup - mkdir \ - "$FILES_FOLDER_DB/" \ - "$FILES_FOLDER_POST_INSTALL_DB/" - - install_script="$WIKI_DIR_INST/$WIKI_DIR_NAME/maintenance/install.php" - echo "Installing MediaWiki using $install_script. This may take some time ..." - - php "$WIKI_DIR_INST/$WIKI_DIR_NAME/maintenance/install.php" \ - --server $WIKI_BASE_URL \ - --scriptpath /wiki \ - --lang en \ - --dbtype sqlite \ - --dbpath $PWD/$FILES_FOLDER_DB/ \ - --pass "$WIKI_PASSW" \ - Git-MediaWiki-Test \ - "$WIKI_ADMIN" || - error "Couldn't run $install_script, see errors above. Try to run ./install-wiki.sh delete first." - cat <<-'EOF' >>$localsettings -# Custom settings added by test-gitmw-lib.sh -# -# Uploading text files is needed for -# t9363-mw-to-git-export-import.sh -$wgEnableUploads = true; -$wgFileExtensions[] = 'txt'; -EOF - - # Copy the initially generated database file into our backup - # folder - cp -R "$FILES_FOLDER_DB/"* "$FILES_FOLDER_POST_INSTALL_DB/" || - error "Unable to copy $FILES_FOLDER_DB/* to $FILES_FOLDER_POST_INSTALL_DB/*" -} - -# Install a wiki in your web server directory. -wiki_install () { - if test $LIGHTTPD = "true" ; then - start_lighttpd - fi - - # In this part, we change directory to $TMP in order to download, - # unpack and copy the files of MediaWiki - ( - mkdir -p "$WIKI_DIR_INST/$WIKI_DIR_NAME" - if ! test -d "$WIKI_DIR_INST/$WIKI_DIR_NAME" - then - error "Folder $WIKI_DIR_INST/$WIKI_DIR_NAME doesn't exist. - Please create it and launch the script again." - fi - - # Fetch MediaWiki's archive if not already present in the - # download directory - mkdir -p "$FILES_FOLDER_DOWNLOAD" - MW_FILENAME="mediawiki-$MW_VERSION_MAJOR.$MW_VERSION_MINOR.tar.gz" - cd "$FILES_FOLDER_DOWNLOAD" - if ! test -f $MW_FILENAME - then - echo "Downloading $MW_VERSION_MAJOR.$MW_VERSION_MINOR sources ..." - wget "http://download.wikimedia.org/mediawiki/$MW_VERSION_MAJOR/$MW_FILENAME" || - error "Unable to download "\ - "http://download.wikimedia.org/mediawiki/$MW_VERSION_MAJOR/"\ - "$MW_FILENAME. "\ - "Please fix your connection and launch the script again." - echo "$MW_FILENAME downloaded in $(pwd)/;" \ - "you can delete it later if you want." - else - echo "Reusing existing $MW_FILENAME downloaded in $(pwd)/" - fi - archive_abs_path=$(pwd)/$MW_FILENAME - cd "$WIKI_DIR_INST/$WIKI_DIR_NAME/" || - error "can't cd to $WIKI_DIR_INST/$WIKI_DIR_NAME/" - tar xzf "$archive_abs_path" --strip-components=1 || - error "Unable to extract WikiMedia's files from $archive_abs_path to "\ - "$WIKI_DIR_INST/$WIKI_DIR_NAME" - ) || exit 1 - echo Extracted in "$WIKI_DIR_INST/$WIKI_DIR_NAME" - - install_mediawiki - - echo "Your wiki has been installed. You can check it at - $WIKI_URL" -} - -# Reset the database of the wiki and the password of the admin -# -# Warning: This function must be called only in a subdirectory of t/ directory -wiki_reset () { - # Copy initial database of the wiki - if ! test -d "../$FILES_FOLDER_DB" - then - error "No wiki database at ../$FILES_FOLDER_DB, not installed yet?" - fi - if ! test -d "../$FILES_FOLDER_POST_INSTALL_DB" - then - error "No wiki backup database at ../$FILES_FOLDER_POST_INSTALL_DB, failed installation?" - fi - wiki_delete_db - cp -R "../$FILES_FOLDER_POST_INSTALL_DB/"* "../$FILES_FOLDER_DB/" || - error "Can't copy ../$FILES_FOLDER_POST_INSTALL_DB/* to ../$FILES_FOLDER_DB/*" - echo "File $FILES_FOLDER_DB/* has been reset" -} - -# Delete the wiki created in the web server's directory and all its content -# saved in the database. -wiki_delete () { - if test $LIGHTTPD = "true"; then - stop_lighttpd - rm -fr "$WEB" - else - # Delete the wiki's directory. - rm -rf "$WIKI_DIR_INST/$WIKI_DIR_NAME" || - error "Wiki's directory $WIKI_DIR_INST/" \ - "$WIKI_DIR_NAME could not be deleted" - fi - wiki_delete_db - wiki_delete_db_backup -} diff --git a/contrib/mw-to-git/t/test-gitmw.pl b/contrib/mw-to-git/t/test-gitmw.pl deleted file mode 100755 index c5d687f078..0000000000 --- a/contrib/mw-to-git/t/test-gitmw.pl +++ /dev/null @@ -1,223 +0,0 @@ -#!/usr/bin/perl -w -s -# Copyright (C) 2012 -# Charles Roussel <charles.roussel@ensimag.imag.fr> -# Simon Cathebras <simon.cathebras@ensimag.imag.fr> -# Julien Khayat <julien.khayat@ensimag.imag.fr> -# Guillaume Sasdy <guillaume.sasdy@ensimag.imag.fr> -# Simon Perrat <simon.perrat@ensimag.imag.fr> -# License: GPL v2 or later - -# Usage: -# ./test-gitmw.pl <command> [argument]* -# Execute in terminal using the name of the function to call as first -# parameter, and the function's arguments as following parameters -# -# Example: -# ./test-gitmw.pl "get_page" foo . -# will call <wiki_getpage> with arguments <foo> and <.> -# -# Available functions are: -# "get_page" -# "delete_page" -# "edit_page" -# "getallpagename" - -use MediaWiki::API; -use Getopt::Long; -use DateTime::Format::ISO8601; -use constant SLASH_REPLACEMENT => "%2F"; - -#Parsing of the config file - -my $configfile = "$ENV{'CURR_DIR'}/test.config"; -my %config; -open my $CONFIG, "<", $configfile or die "can't open $configfile: $!"; -while (<$CONFIG>) -{ - chomp; - s/#.*//; - s/^\s+//; - s/\s+$//; - next unless length; - my ($key, $value) = split (/\s*=\s*/,$_, 2); - $config{$key} = $value; - last if ($key eq 'LIGHTTPD' and $value eq 'false'); - last if ($key eq 'PORT'); -} -close $CONFIG or die "can't close $configfile: $!"; - -my $wiki_address = "http://$config{'SERVER_ADDR'}".":"."$config{'PORT'}"; -my $wiki_url = "$wiki_address/$config{'WIKI_DIR_NAME'}/api.php"; -my $wiki_admin = "$config{'WIKI_ADMIN'}"; -my $wiki_admin_pass = "$config{'WIKI_PASSW'}"; -my $mw = MediaWiki::API->new; -$mw->{config}->{api_url} = $wiki_url; - - -# wiki_login <name> <password> -# -# Logs the user with <name> and <password> in the global variable -# of the mediawiki $mw -sub wiki_login { - $mw->login( { lgname => "$_[0]",lgpassword => "$_[1]" } ) - || die "getpage: login failed"; -} - -# wiki_getpage <wiki_page> <dest_path> -# -# fetch a page <wiki_page> from the wiki referenced in the global variable -# $mw and copies its content in directory dest_path -sub wiki_getpage { - my $pagename = $_[0]; - my $destdir = $_[1]; - - my $page = $mw->get_page( { title => $pagename } ); - if (!defined($page)) { - die "getpage: wiki does not exist"; - } - - my $content = $page->{'*'}; - if (!defined($content)) { - die "getpage: page does not exist"; - } - - $pagename=$page->{'title'}; - # Replace spaces by underscore in the page name - $pagename =~ s/ /_/g; - $pagename =~ s/\//%2F/g; - open(my $file, ">:encoding(UTF-8)", "$destdir/$pagename.mw"); - print $file "$content"; - close ($file); - -} - -# wiki_delete_page <page_name> -# -# delete the page with name <page_name> from the wiki referenced -# in the global variable $mw -sub wiki_delete_page { - my $pagename = $_[0]; - - my $exist=$mw->get_page({title => $pagename}); - - if (defined($exist->{'*'})){ - $mw->edit({ action => 'delete', - title => $pagename}) - || die $mw->{error}->{code} . ": " . $mw->{error}->{details}; - } else { - die "no page with such name found: $pagename\n"; - } -} - -# wiki_editpage <wiki_page> <wiki_content> <wiki_append> [-c=<category>] [-s=<summary>] -# -# Edit a page named <wiki_page> with content <wiki_content> on the wiki -# referenced with the global variable $mw -# If <wiki_append> == true : append <wiki_content> at the end of the actual -# content of the page <wiki_page> -# If <wik_page> doesn't exist, that page is created with the <wiki_content> -sub wiki_editpage { - my $wiki_page = $_[0]; - my $wiki_content = $_[1]; - my $wiki_append = $_[2]; - my $summary = ""; - my ($summ, $cat) = (); - GetOptions('s=s' => \$summ, 'c=s' => \$cat); - - my $append = 0; - if (defined($wiki_append) && $wiki_append eq 'true') { - $append=1; - } - - my $previous_text =""; - - if ($append) { - my $ref = $mw->get_page( { title => $wiki_page } ); - $previous_text = $ref->{'*'}; - } - - my $text = $wiki_content; - if (defined($previous_text)) { - $text="$previous_text$text"; - } - - # Eventually, add this page to a category. - if (defined($cat)) { - my $category_name="[[Category:$cat]]"; - $text="$text\n $category_name"; - } - if(defined($summ)){ - $summary=$summ; - } - - $mw->edit( { action => 'edit', title => $wiki_page, summary => $summary, text => "$text"} ); -} - -# wiki_getallpagename [<category>] -# -# Fetch all pages of the wiki referenced by the global variable $mw -# and print the names of each one in the file all.txt with a new line -# ("\n") between these. -# If the argument <category> is defined, then this function get only the pages -# belonging to <category>. -sub wiki_getallpagename { - # fetch the pages of the wiki - if (defined($_[0])) { - my $mw_pages = $mw->list ( { action => 'query', - list => 'categorymembers', - cmtitle => "Category:$_[0]", - cmnamespace => 0, - cmlimit => 500 }, - ) - || die $mw->{error}->{code}.": ".$mw->{error}->{details}; - open(my $file, ">:encoding(UTF-8)", "all.txt"); - foreach my $page (@{$mw_pages}) { - print $file "$page->{title}\n"; - } - close ($file); - - } else { - my $mw_pages = $mw->list({ - action => 'query', - list => 'allpages', - aplimit => 500, - }) - || die $mw->{error}->{code}.": ".$mw->{error}->{details}; - open(my $file, ">:encoding(UTF-8)", "all.txt"); - foreach my $page (@{$mw_pages}) { - print $file "$page->{title}\n"; - } - close ($file); - } -} - -sub wiki_upload_file { - my $file_name = $_[0]; - my $resultat = $mw->edit ( { - action => 'upload', - filename => $file_name, - comment => 'upload a file', - file => [ $file_name ], - ignorewarnings=>1, - }, { - skip_encoding => 1 - } ) || die $mw->{error}->{code} . ' : ' . $mw->{error}->{details}; -} - - - -# Main part of this script: parse the command line arguments -# and select which function to execute -my $fct_to_call = shift; - -wiki_login($wiki_admin, $wiki_admin_pass); - -my %functions_to_call = ( - upload_file => \&wiki_upload_file, - get_page => \&wiki_getpage, - delete_page => \&wiki_delete_page, - edit_page => \&wiki_editpage, - getallpagename => \&wiki_getallpagename, -); -die "$0 ERROR: wrong argument" unless exists $functions_to_call{$fct_to_call}; -$functions_to_call{$fct_to_call}->(map { utf8::decode($_); $_ } @ARGV); diff --git a/contrib/mw-to-git/t/test.config b/contrib/mw-to-git/t/test.config deleted file mode 100644 index ed10b3e4a4..0000000000 --- a/contrib/mw-to-git/t/test.config +++ /dev/null @@ -1,40 +0,0 @@ -# Name of the web server's directory dedicated to the wiki is WIKI_DIR_NAME -WIKI_DIR_NAME=wiki - -# Login and password of the wiki's admin -WIKI_ADMIN=WikiAdmin -WIKI_PASSW=AdminPass1 - -# Address of the web server -SERVER_ADDR=localhost - -# If LIGHTTPD is not set to true, the script will use the default -# web server running in WIKI_DIR_INST. -WIKI_DIR_INST=/var/www - -# If LIGHTTPD is set to true, the script will use Lighttpd to run -# the wiki. -LIGHTTPD=true - -# The variables below are useful only if LIGHTTPD is set to true. -PORT=1234 -PHP_DIR=/usr/bin -LIGHTTPD_DIR=/usr/sbin -WEB=WEB -WEB_TMP=$WEB/tmp -WEB_WWW=$WEB/www - -# Where our configuration for the wiki is located -FILES_FOLDER=mediawiki -FILES_FOLDER_DOWNLOAD=$FILES_FOLDER/download -FILES_FOLDER_DB=$FILES_FOLDER/db -FILES_FOLDER_POST_INSTALL_DB=$FILES_FOLDER/post-install-db - -# The variables below are used by the script to install a wiki. -# You should not modify these unless you are modifying the script itself. -# tested versions: 1.19.X -> 1.21.1 -> 1.34.2 -# -# See https://www.mediawiki.org/wiki/Download for what the latest -# version is. -MW_VERSION_MAJOR=1.34 -MW_VERSION_MINOR=2 diff --git a/contrib/persistent-https/LICENSE b/contrib/persistent-https/LICENSE deleted file mode 100644 index d645695673..0000000000 --- a/contrib/persistent-https/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/contrib/persistent-https/Makefile b/contrib/persistent-https/Makefile deleted file mode 100644 index 691737e76b..0000000000 --- a/contrib/persistent-https/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright 2012 Google Inc. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# The default target of this Makefile is... -all:: - -BUILD_LABEL=$(shell cut -d" " -f3 ../../GIT-VERSION-FILE) -TAR_OUT=$(shell go env GOOS)_$(shell go env GOARCH).tar.gz - -all:: git-remote-persistent-https git-remote-persistent-https--proxy \ - git-remote-persistent-http - -git-remote-persistent-https--proxy: git-remote-persistent-https - ln -f -s git-remote-persistent-https git-remote-persistent-https--proxy - -git-remote-persistent-http: git-remote-persistent-https - ln -f -s git-remote-persistent-https git-remote-persistent-http - -git-remote-persistent-https: - case $$(go version) in \ - "go version go"1.[0-5].*) EQ=" " ;; *) EQ="=" ;; esac && \ - go build -o git-remote-persistent-https \ - -ldflags "-X main._BUILD_EMBED_LABEL$${EQ}$(BUILD_LABEL)" - -clean: - rm -f git-remote-persistent-http* *.tar.gz - -tar: clean all - @chmod 555 git-remote-persistent-https - @tar -czf $(TAR_OUT) git-remote-persistent-http* README LICENSE - @echo - @echo "Created $(TAR_OUT)" diff --git a/contrib/persistent-https/README b/contrib/persistent-https/README deleted file mode 100644 index 7c4cd8d257..0000000000 --- a/contrib/persistent-https/README +++ /dev/null @@ -1,72 +0,0 @@ -git-remote-persistent-https - -The git-remote-persistent-https binary speeds up SSL operations -by running a daemon job (git-remote-persistent-https--proxy) that -keeps a connection open to a server. - - -PRE-BUILT BINARIES - -Darwin amd64: -https://commondatastorage.googleapis.com/git-remote-persistent-https/darwin_amd64.tar.gz - -Linux amd64: -https://commondatastorage.googleapis.com/git-remote-persistent-https/linux_amd64.tar.gz - - -INSTALLING - -Move all of the git-remote-persistent-http* binaries to a directory -in PATH. - - -USAGE - -HTTPS requests can be delegated to the proxy by using the -"persistent-https" scheme, e.g. - -git clone persistent-https://kernel.googlesource.com/pub/scm/git/git - -Likewise, .gitconfig can be updated as follows to rewrite https urls -to use persistent-https: - -[url "persistent-https"] - insteadof = https -[url "persistent-http"] - insteadof = http - -You may also want to allow the use of the persistent-https helper for -submodule URLs (since any https URLs pointing to submodules will be -rewritten, and Git's out-of-the-box defaults forbid submodules from -using unknown remote helpers): - -[protocol "persistent-https"] - allow = always -[protocol "persistent-http"] - allow = always - - -##################################################################### -# BUILDING FROM SOURCE -##################################################################### - -LOCATION - -The source is available in the contrib/persistent-https directory of -the Git source repository. The Git source repository is available at -git://git.kernel.org/pub/scm/git/git.git/ -https://kernel.googlesource.com/pub/scm/git/git - - -PREREQUISITES - -The code is written in Go (http://golang.org/) and the Go compiler is -required. Currently, the compiler must be built and installed from tip -of source, in order to include a fix in the reverse http proxy: -http://code.google.com/p/go/source/detail?r=a615b796570a2cd8591884767a7d67ede74f6648 - - -BUILDING - -Run "make" to build the binaries. See the section on -INSTALLING above. diff --git a/contrib/persistent-https/client.go b/contrib/persistent-https/client.go deleted file mode 100644 index 71125b5832..0000000000 --- a/contrib/persistent-https/client.go +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "bufio" - "errors" - "fmt" - "net" - "net/url" - "os" - "os/exec" - "strings" - "syscall" - "time" -) - -type Client struct { - ProxyBin string - Args []string - - insecure bool -} - -func (c *Client) Run() error { - if err := c.resolveArgs(); err != nil { - return fmt.Errorf("resolveArgs() got error: %v", err) - } - - // Connect to the proxy. - uconn, hconn, addr, err := c.connect() - if err != nil { - return fmt.Errorf("connect() got error: %v", err) - } - // Keep the unix socket connection open for the duration of the request. - defer uconn.Close() - // Keep a connection to the HTTP server open, so no other user can - // bind on the same address so long as the process is running. - defer hconn.Close() - - // Start the git-remote-http subprocess. - cargs := []string{"-c", fmt.Sprintf("http.proxy=%v", addr), "remote-http"} - cargs = append(cargs, c.Args...) - cmd := exec.Command("git", cargs...) - - for _, v := range os.Environ() { - if !strings.HasPrefix(v, "GIT_PERSISTENT_HTTPS_SECURE=") { - cmd.Env = append(cmd.Env, v) - } - } - // Set the GIT_PERSISTENT_HTTPS_SECURE environment variable when - // the proxy is using a SSL connection. This allows credential helpers - // to identify secure proxy connections, despite being passed an HTTP - // scheme. - if !c.insecure { - cmd.Env = append(cmd.Env, "GIT_PERSISTENT_HTTPS_SECURE=1") - } - - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - if eerr, ok := err.(*exec.ExitError); ok { - if stat, ok := eerr.ProcessState.Sys().(syscall.WaitStatus); ok && stat.ExitStatus() != 0 { - os.Exit(stat.ExitStatus()) - } - } - return fmt.Errorf("git-remote-http subprocess got error: %v", err) - } - return nil -} - -func (c *Client) connect() (uconn net.Conn, hconn net.Conn, addr string, err error) { - uconn, err = DefaultSocket.Dial() - if err != nil { - if e, ok := err.(*net.OpError); ok && (os.IsNotExist(e.Err) || e.Err == syscall.ECONNREFUSED) { - if err = c.startProxy(); err == nil { - uconn, err = DefaultSocket.Dial() - } - } - if err != nil { - return - } - } - - if addr, err = c.readAddr(uconn); err != nil { - return - } - - // Open a tcp connection to the proxy. - if hconn, err = net.Dial("tcp", addr); err != nil { - return - } - - // Verify the address hasn't changed ownership. - var addr2 string - if addr2, err = c.readAddr(uconn); err != nil { - return - } else if addr != addr2 { - err = fmt.Errorf("address changed after connect. got %q, want %q", addr2, addr) - return - } - return -} - -func (c *Client) readAddr(conn net.Conn) (string, error) { - conn.SetDeadline(time.Now().Add(5 * time.Second)) - data := make([]byte, 100) - n, err := conn.Read(data) - if err != nil { - return "", fmt.Errorf("error reading unix socket: %v", err) - } else if n == 0 { - return "", errors.New("empty data response") - } - conn.Write([]byte{1}) // Ack - - var addr string - if addrs := strings.Split(string(data[:n]), "\n"); len(addrs) != 2 { - return "", fmt.Errorf("got %q, wanted 2 addresses", data[:n]) - } else if c.insecure { - addr = addrs[1] - } else { - addr = addrs[0] - } - return addr, nil -} - -func (c *Client) startProxy() error { - cmd := exec.Command(c.ProxyBin) - cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} - stdout, err := cmd.StdoutPipe() - if err != nil { - return err - } - defer stdout.Close() - if err := cmd.Start(); err != nil { - return err - } - result := make(chan error) - go func() { - bytes, _, err := bufio.NewReader(stdout).ReadLine() - if line := string(bytes); err == nil && line != "OK" { - err = fmt.Errorf("proxy returned %q, want \"OK\"", line) - } - result <- err - }() - select { - case err := <-result: - return err - case <-time.After(5 * time.Second): - return errors.New("timeout waiting for proxy to start") - } - panic("not reachable") -} - -func (c *Client) resolveArgs() error { - if nargs := len(c.Args); nargs == 0 { - return errors.New("remote needed") - } else if nargs > 2 { - return fmt.Errorf("want at most 2 args, got %v", c.Args) - } - - // Rewrite the url scheme to be http. - idx := len(c.Args) - 1 - rawurl := c.Args[idx] - rurl, err := url.Parse(rawurl) - if err != nil { - return fmt.Errorf("invalid remote: %v", err) - } - c.insecure = rurl.Scheme == "persistent-http" - rurl.Scheme = "http" - c.Args[idx] = rurl.String() - if idx != 0 && c.Args[0] == rawurl { - c.Args[0] = c.Args[idx] - } - return nil -} diff --git a/contrib/persistent-https/main.go b/contrib/persistent-https/main.go deleted file mode 100644 index fd1b107743..0000000000 --- a/contrib/persistent-https/main.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// The git-remote-persistent-https binary speeds up SSL operations by running -// a daemon job that keeps a connection open to a Git server. This ensures the -// git-remote-persistent-https--proxy is running and delegating execution -// to the git-remote-http binary with the http_proxy set to the daemon job. -// A unix socket is used to authenticate the proxy and discover the -// HTTP address. Note, both the client and proxy are included in the same -// binary. -package main - -import ( - "flag" - "fmt" - "log" - "os" - "strings" - "time" -) - -var ( - forceProxy = flag.Bool("proxy", false, "Whether to start the binary in proxy mode") - proxyBin = flag.String("proxy_bin", "git-remote-persistent-https--proxy", "Path to the proxy binary") - printLabel = flag.Bool("print_label", false, "Prints the build label for the binary") - - // Variable that should be defined through the -X linker flag. - _BUILD_EMBED_LABEL string -) - -const ( - defaultMaxIdleDuration = 24 * time.Hour - defaultPollUpdateInterval = 15 * time.Minute -) - -func main() { - flag.Parse() - if *printLabel { - // Short circuit execution to print the build label - fmt.Println(buildLabel()) - return - } - - var err error - if *forceProxy || strings.HasSuffix(os.Args[0], "--proxy") { - log.SetPrefix("git-remote-persistent-https--proxy: ") - proxy := &Proxy{ - BuildLabel: buildLabel(), - MaxIdleDuration: defaultMaxIdleDuration, - PollUpdateInterval: defaultPollUpdateInterval, - } - err = proxy.Run() - } else { - log.SetPrefix("git-remote-persistent-https: ") - client := &Client{ - ProxyBin: *proxyBin, - Args: flag.Args(), - } - err = client.Run() - } - if err != nil { - log.Fatalln(err) - } -} - -func buildLabel() string { - if _BUILD_EMBED_LABEL == "" { - log.Println(`unlabeled build; build with "make" to label`) - } - return _BUILD_EMBED_LABEL -} diff --git a/contrib/persistent-https/proxy.go b/contrib/persistent-https/proxy.go deleted file mode 100644 index bb0cdba386..0000000000 --- a/contrib/persistent-https/proxy.go +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "fmt" - "log" - "net" - "net/http" - "net/http/httputil" - "os" - "os/exec" - "os/signal" - "sync" - "syscall" - "time" -) - -type Proxy struct { - BuildLabel string - MaxIdleDuration time.Duration - PollUpdateInterval time.Duration - - ul net.Listener - httpAddr string - httpsAddr string -} - -func (p *Proxy) Run() error { - hl, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - return fmt.Errorf("http listen failed: %v", err) - } - defer hl.Close() - - hsl, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - return fmt.Errorf("https listen failed: %v", err) - } - defer hsl.Close() - - p.ul, err = DefaultSocket.Listen() - if err != nil { - c, derr := DefaultSocket.Dial() - if derr == nil { - c.Close() - fmt.Println("OK\nA proxy is already running... exiting") - return nil - } else if e, ok := derr.(*net.OpError); ok && e.Err == syscall.ECONNREFUSED { - // Nothing is listening on the socket, unlink it and try again. - syscall.Unlink(DefaultSocket.Path()) - p.ul, err = DefaultSocket.Listen() - } - if err != nil { - return fmt.Errorf("unix listen failed on %v: %v", DefaultSocket.Path(), err) - } - } - defer p.ul.Close() - go p.closeOnSignal() - go p.closeOnUpdate() - - p.httpAddr = hl.Addr().String() - p.httpsAddr = hsl.Addr().String() - fmt.Printf("OK\nListening on unix socket=%v http=%v https=%v\n", - p.ul.Addr(), p.httpAddr, p.httpsAddr) - - result := make(chan error, 2) - go p.serveUnix(result) - go func() { - result <- http.Serve(hl, &httputil.ReverseProxy{ - FlushInterval: 500 * time.Millisecond, - Director: func(r *http.Request) {}, - }) - }() - go func() { - result <- http.Serve(hsl, &httputil.ReverseProxy{ - FlushInterval: 500 * time.Millisecond, - Director: func(r *http.Request) { - r.URL.Scheme = "https" - }, - }) - }() - return <-result -} - -type socketContext struct { - sync.WaitGroup - mutex sync.Mutex - last time.Time -} - -func (sc *socketContext) Done() { - sc.mutex.Lock() - defer sc.mutex.Unlock() - sc.last = time.Now() - sc.WaitGroup.Done() -} - -func (p *Proxy) serveUnix(result chan<- error) { - sockCtx := &socketContext{} - go p.closeOnIdle(sockCtx) - - var err error - for { - var uconn net.Conn - uconn, err = p.ul.Accept() - if err != nil { - err = fmt.Errorf("accept failed: %v", err) - break - } - sockCtx.Add(1) - go p.handleUnixConn(sockCtx, uconn) - } - sockCtx.Wait() - result <- err -} - -func (p *Proxy) handleUnixConn(sockCtx *socketContext, uconn net.Conn) { - defer sockCtx.Done() - defer uconn.Close() - data := []byte(fmt.Sprintf("%v\n%v", p.httpsAddr, p.httpAddr)) - uconn.SetDeadline(time.Now().Add(5 * time.Second)) - for i := 0; i < 2; i++ { - if n, err := uconn.Write(data); err != nil { - log.Printf("error sending http addresses: %+v\n", err) - return - } else if n != len(data) { - log.Printf("sent %d data bytes, wanted %d\n", n, len(data)) - return - } - if _, err := uconn.Read([]byte{0, 0, 0, 0}); err != nil { - log.Printf("error waiting for Ack: %+v\n", err) - return - } - } - // Wait without a deadline for the client to finish via EOF - uconn.SetDeadline(time.Time{}) - uconn.Read([]byte{0, 0, 0, 0}) -} - -func (p *Proxy) closeOnIdle(sockCtx *socketContext) { - for d := p.MaxIdleDuration; d > 0; { - time.Sleep(d) - sockCtx.Wait() - sockCtx.mutex.Lock() - if d = sockCtx.last.Add(p.MaxIdleDuration).Sub(time.Now()); d <= 0 { - log.Println("graceful shutdown from idle timeout") - p.ul.Close() - } - sockCtx.mutex.Unlock() - } -} - -func (p *Proxy) closeOnUpdate() { - for { - time.Sleep(p.PollUpdateInterval) - if out, err := exec.Command(os.Args[0], "--print_label").Output(); err != nil { - log.Printf("error polling for updated binary: %v\n", err) - } else if s := string(out[:len(out)-1]); p.BuildLabel != s { - log.Printf("graceful shutdown from updated binary: %q --> %q\n", p.BuildLabel, s) - p.ul.Close() - break - } - } -} - -func (p *Proxy) closeOnSignal() { - ch := make(chan os.Signal, 10) - signal.Notify(ch, os.Interrupt, os.Kill, os.Signal(syscall.SIGTERM), os.Signal(syscall.SIGHUP)) - sig := <-ch - p.ul.Close() - switch sig { - case os.Signal(syscall.SIGHUP): - log.Printf("graceful shutdown from signal: %v\n", sig) - default: - log.Fatalf("exiting from signal: %v\n", sig) - } -} diff --git a/contrib/persistent-https/socket.go b/contrib/persistent-https/socket.go deleted file mode 100644 index 193b911dd1..0000000000 --- a/contrib/persistent-https/socket.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2012 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "fmt" - "log" - "net" - "os" - "path/filepath" - "syscall" -) - -// A Socket is a wrapper around a Unix socket that verifies directory -// permissions. -type Socket struct { - Dir string -} - -func defaultDir() string { - sockPath := ".git-credential-cache" - if home := os.Getenv("HOME"); home != "" { - return filepath.Join(home, sockPath) - } - log.Printf("socket: cannot find HOME path. using relative directory %q for socket", sockPath) - return sockPath -} - -// DefaultSocket is a Socket in the $HOME/.git-credential-cache directory. -var DefaultSocket = Socket{Dir: defaultDir()} - -// Listen announces the local network address of the unix socket. The -// permissions on the socket directory are verified before attempting -// the actual listen. -func (s Socket) Listen() (net.Listener, error) { - network, addr := "unix", s.Path() - if err := s.mkdir(); err != nil { - return nil, &net.OpError{Op: "listen", Net: network, Addr: &net.UnixAddr{Name: addr, Net: network}, Err: err} - } - return net.Listen(network, addr) -} - -// Dial connects to the unix socket. The permissions on the socket directory -// are verified before attempting the actual dial. -func (s Socket) Dial() (net.Conn, error) { - network, addr := "unix", s.Path() - if err := s.checkPermissions(); err != nil { - return nil, &net.OpError{Op: "dial", Net: network, Addr: &net.UnixAddr{Name: addr, Net: network}, Err: err} - } - return net.Dial(network, addr) -} - -// Path returns the fully specified file name of the unix socket. -func (s Socket) Path() string { - return filepath.Join(s.Dir, "persistent-https-proxy-socket") -} - -func (s Socket) mkdir() error { - if err := s.checkPermissions(); err == nil { - return nil - } else if !os.IsNotExist(err) { - return err - } - if err := os.MkdirAll(s.Dir, 0700); err != nil { - return err - } - return s.checkPermissions() -} - -func (s Socket) checkPermissions() error { - fi, err := os.Stat(s.Dir) - if err != nil { - return err - } - if !fi.IsDir() { - return fmt.Errorf("socket: got file, want directory for %q", s.Dir) - } - if fi.Mode().Perm() != 0700 { - return fmt.Errorf("socket: got perm %o, want 700 for %q", fi.Mode().Perm(), s.Dir) - } - if st := fi.Sys().(*syscall.Stat_t); int(st.Uid) != os.Getuid() { - return fmt.Errorf("socket: got uid %d, want %d for %q", st.Uid, os.Getuid(), s.Dir) - } - return nil -} diff --git a/contrib/remote-helpers/README b/contrib/remote-helpers/README deleted file mode 100644 index ac72332517..0000000000 --- a/contrib/remote-helpers/README +++ /dev/null @@ -1,15 +0,0 @@ -The remote-helper bridges to access data stored in Mercurial and -Bazaar are maintained outside the git.git tree in the repositories -of their primary author: - - https://github.com/felipec/git-remote-hg (for Mercurial) - https://github.com/felipec/git-remote-bzr (for Bazaar) - -You can pick a directory on your $PATH and download them from these -repositories, e.g.: - - $ wget -O $HOME/bin/git-remote-hg \ - https://raw.github.com/felipec/git-remote-hg/master/git-remote-hg - $ wget -O $HOME/bin/git-remote-bzr \ - https://raw.github.com/felipec/git-remote-bzr/master/git-remote-bzr - $ chmod +x $HOME/bin/git-remote-hg $HOME/bin/git-remote-bzr diff --git a/contrib/remote-helpers/git-remote-bzr b/contrib/remote-helpers/git-remote-bzr deleted file mode 100755 index 1c3d87f861..0000000000 --- a/contrib/remote-helpers/git-remote-bzr +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -cat >&2 <<'EOT' -WARNING: git-remote-bzr is now maintained independently. -WARNING: For more information visit https://github.com/felipec/git-remote-bzr -WARNING: -WARNING: You can pick a directory on your $PATH and download it, e.g.: -WARNING: $ wget -O $HOME/bin/git-remote-bzr \ -WARNING: https://raw.github.com/felipec/git-remote-bzr/master/git-remote-bzr -WARNING: $ chmod +x $HOME/bin/git-remote-bzr -EOT diff --git a/contrib/remote-helpers/git-remote-hg b/contrib/remote-helpers/git-remote-hg deleted file mode 100755 index 8e9188364c..0000000000 --- a/contrib/remote-helpers/git-remote-hg +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -cat >&2 <<'EOT' -WARNING: git-remote-hg is now maintained independently. -WARNING: For more information visit https://github.com/felipec/git-remote-hg -WARNING: -WARNING: You can pick a directory on your $PATH and download it, e.g.: -WARNING: $ wget -O $HOME/bin/git-remote-hg \ -WARNING: https://raw.github.com/felipec/git-remote-hg/master/git-remote-hg -WARNING: $ chmod +x $HOME/bin/git-remote-hg -EOT diff --git a/contrib/remotes2config.sh b/contrib/remotes2config.sh deleted file mode 100755 index 1cda19f66a..0000000000 --- a/contrib/remotes2config.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh - -# Use this tool to rewrite your .git/remotes/ files into the config. - -. git-sh-setup - -if [ -d "$GIT_DIR"/remotes ]; then - echo "Rewriting $GIT_DIR/remotes" >&2 - error=0 - # rewrite into config - { - cd "$GIT_DIR"/remotes - ls | while read f; do - name=$(printf "$f" | tr -c "A-Za-z0-9-" ".") - sed -n \ - -e "s/^URL:[ ]*\(.*\)$/remote.$name.url \1 ./p" \ - -e "s/^Pull:[ ]*\(.*\)$/remote.$name.fetch \1 ^$ /p" \ - -e "s/^Push:[ ]*\(.*\)$/remote.$name.push \1 ^$ /p" \ - < "$f" - done - echo done - } | while read key value regex; do - case $key in - done) - if [ $error = 0 ]; then - mv "$GIT_DIR"/remotes "$GIT_DIR"/remotes.old - fi ;; - *) - echo "git config $key "$value" $regex" - git config $key "$value" $regex || error=1 ;; - esac - done -fi diff --git a/contrib/stats/git-common-hash b/contrib/stats/git-common-hash deleted file mode 100755 index e27fd088be..0000000000 --- a/contrib/stats/git-common-hash +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh - -# This script displays the distribution of longest common hash prefixes. -# This can be used to determine the minimum prefix length to use -# for object names to be unique. - -git rev-list --objects --all | sort | perl -lne ' - substr($_, 40) = ""; - # uncomment next line for a distribution of bits instead of hex chars - # $_ = unpack("B*",pack("H*",$_)); - if (defined $p) { - ($p ^ $_) =~ /^(\0*)/; - $common = length $1; - if (defined $pcommon) { - $count[$pcommon > $common ? $pcommon : $common]++; - } else { - $count[$common]++; # first item - } - } - $p = $_; - $pcommon = $common; - END { - $count[$common]++; # last item - print "$_: $count[$_]" for 0..$#count; - } -' diff --git a/contrib/stats/mailmap.pl b/contrib/stats/mailmap.pl deleted file mode 100755 index 9513f5e35b..0000000000 --- a/contrib/stats/mailmap.pl +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/perl - -use warnings 'all'; -use strict; -use Getopt::Long; - -my $match_emails; -my $match_names; -my $order_by = 'count'; -Getopt::Long::Configure(qw(bundling)); -GetOptions( - 'emails|e!' => \$match_emails, - 'names|n!' => \$match_names, - 'count|c' => sub { $order_by = 'count' }, - 'time|t' => sub { $order_by = 'stamp' }, -) or exit 1; -$match_emails = 1 unless $match_names; - -my $email = {}; -my $name = {}; - -open(my $fh, '-|', "git log --format='%at <%aE> %aN'"); -while(<$fh>) { - my ($t, $e, $n) = /(\S+) <(\S+)> (.*)/; - mark($email, $e, $n, $t); - mark($name, $n, $e, $t); -} -close($fh); - -if ($match_emails) { - foreach my $e (dups($email)) { - foreach my $n (vals($email->{$e})) { - show($n, $e, $email->{$e}->{$n}); - } - print "\n"; - } -} -if ($match_names) { - foreach my $n (dups($name)) { - foreach my $e (vals($name->{$n})) { - show($n, $e, $name->{$n}->{$e}); - } - print "\n"; - } -} -exit 0; - -sub mark { - my ($h, $k, $v, $t) = @_; - my $e = $h->{$k}->{$v} ||= { count => 0, stamp => 0 }; - $e->{count}++; - $e->{stamp} = $t unless $t < $e->{stamp}; -} - -sub dups { - my $h = shift; - return grep { keys($h->{$_}) > 1 } keys($h); -} - -sub vals { - my $h = shift; - return sort { - $h->{$b}->{$order_by} <=> $h->{$a}->{$order_by} - } keys($h); -} - -sub show { - my ($n, $e, $h) = @_; - print "$n <$e> ($h->{$order_by})\n"; -} diff --git a/contrib/subtree/README b/contrib/subtree/README index c686b4a69b..65d167b678 100644 --- a/contrib/subtree/README +++ b/contrib/subtree/README @@ -1,5 +1,5 @@ -Please read git-subtree.txt for documentation. +Please read git-subtree.adoc for documentation. Please don't contact me using github mail; it's slow, ugly, and worst of all, redundant. Email me instead at apenwarr@gmail.com and I'll be happy to diff --git a/contrib/subtree/git-subtree.adoc b/contrib/subtree/git-subtree.adoc index 004abf415b..b2bcbcad0d 100644 --- a/contrib/subtree/git-subtree.adoc +++ b/contrib/subtree/git-subtree.adoc @@ -9,14 +9,14 @@ git-subtree - Merge subtrees together and split repository into subtrees SYNOPSIS -------- [verse] -'git subtree' [<options>] -P <prefix> add <local-commit> -'git subtree' [<options>] -P <prefix> add <repository> <remote-ref> -'git subtree' [<options>] -P <prefix> merge <local-commit> [<repository>] -'git subtree' [<options>] -P <prefix> split [<local-commit>] +'git subtree' [<options>] -P <prefix> [-S[<keyid>]] add <local-commit> +'git subtree' [<options>] -P <prefix> [-S[<keyid>]] add <repository> <remote-ref> +'git subtree' [<options>] -P <prefix> [-S[<keyid>]] merge <local-commit> [<repository>] +'git subtree' [<options>] -P <prefix> [-S[<keyid>]] split [<local-commit>] [verse] -'git subtree' [<options>] -P <prefix> pull <repository> <remote-ref> -'git subtree' [<options>] -P <prefix> push <repository> <refspec> +'git subtree' [<options>] -P <prefix> [-S[<keyid>]] pull <repository> <remote-ref> +'git subtree' [<options>] -P <prefix> [-S[<keyid>]] push <repository> <refspec> DESCRIPTION ----------- @@ -149,6 +149,13 @@ OPTIONS FOR ALL COMMANDS want to manipulate. This option is mandatory for all commands. +-S[<keyid>]:: +--gpg-sign[=<keyid>]:: +--no-gpg-sign:: + GPG-sign commits. The `keyid` argument is optional and + defaults to the committer identity; `--no-gpg-sign` is useful to + countermand a `--gpg-sign` option given earlier on the command line. + OPTIONS FOR 'add' AND 'merge' (ALSO: 'pull', 'split --rejoin', AND 'push --rejoin') ----------------------------------------------------------------------------------- These options for 'add' and 'merge' may also be given to 'pull' (which diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh index 15ae86db1b..17106d1a72 100755 --- a/contrib/subtree/git-subtree.sh +++ b/contrib/subtree/git-subtree.sh @@ -26,12 +26,12 @@ then fi OPTS_SPEC="\ -git subtree add --prefix=<prefix> <commit> -git subtree add --prefix=<prefix> <repository> <ref> -git subtree merge --prefix=<prefix> <commit> -git subtree split --prefix=<prefix> [<commit>] -git subtree pull --prefix=<prefix> <repository> <ref> -git subtree push --prefix=<prefix> <repository> <refspec> +git subtree add --prefix=<prefix> [-S[=<key-id>]] <commit> +git subtree add --prefix=<prefix> [-S[=<key-id>]] <repository> <ref> +git subtree merge --prefix=<prefix> [-S[=<key-id>]] <commit> +git subtree split --prefix=<prefix> [-S[=<key-id>]] [<commit>] +git subtree pull --prefix=<prefix> [-S[=<key-id>]] <repository> <ref> +git subtree push --prefix=<prefix> [-S[=<key-id>]] <repository> <refspec> -- h,help! show the help q,quiet! quiet @@ -46,6 +46,7 @@ rejoin merge the new branch back into HEAD options for 'add' and 'merge' (also: 'pull', 'split --rejoin', and 'push --rejoin') squash merge subtree changes as a single commit m,message!= use the given message as the commit message for the merge commit +S,gpg-sign?key-id GPG-sign commits. The keyid argument is optional and defaults to the committer identity " indent=0 @@ -115,7 +116,7 @@ main () { then set -- -h fi - set_args="$(echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)" + set_args="$(echo "$OPTS_SPEC" | git rev-parse --parseopt --stuck-long -- "$@" || echo exit $?)" eval "$set_args" . git-sh-setup require_work_tree @@ -131,9 +132,6 @@ main () { opt="$1" shift case "$opt" in - --annotate|-b|-P|-m|--onto) - shift - ;; --rejoin) arg_split_rejoin=1 ;; @@ -171,48 +169,44 @@ main () { arg_split_annotate= arg_addmerge_squash= arg_addmerge_message= + arg_gpg_sign= while test $# -gt 0 do opt="$1" shift case "$opt" in - -q) + --quiet) arg_quiet=1 ;; - -d) + --debug) arg_debug=1 ;; - --annotate) + --annotate=*) test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" - arg_split_annotate="$1" - shift + arg_split_annotate="${opt#*=}" ;; --no-annotate) test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" arg_split_annotate= ;; - -b) + --branch=*) test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" - arg_split_branch="$1" - shift + arg_split_branch="${opt#*=}" ;; - -P) - arg_prefix="${1%/}" - shift + --prefix=*) + arg_prefix="${opt#*=}" ;; - -m) + --message=*) test -n "$allow_addmerge" || die_incompatible_opt "$opt" "$arg_command" - arg_addmerge_message="$1" - shift + arg_addmerge_message="${opt#*=}" ;; --no-prefix) arg_prefix= ;; - --onto) + --onto=*) test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" - arg_split_onto="$1" - shift + arg_split_onto="${opt#*=}" ;; --no-onto) test -n "$allow_split" || die_incompatible_opt "$opt" "$arg_command" @@ -240,6 +234,9 @@ main () { test -n "$allow_addmerge" || die_incompatible_opt "$opt" "$arg_command" arg_addmerge_squash= ;; + --gpg-sign=* | --gpg-sign | --no-gpg-sign) + arg_gpg_sign="$opt" + ;; --) break ;; @@ -272,6 +269,7 @@ main () { debug "quiet: {$arg_quiet}" debug "dir: {$dir}" debug "opts: {$*}" + debug "gpg-sign: {$arg_gpg_sign}" debug "cmd_$arg_command" "$@" @@ -537,7 +535,7 @@ copy_commit () { printf "%s" "$arg_split_annotate" cat ) | - git commit-tree "$2" $3 # reads the rest of stdin + git commit-tree $arg_gpg_sign "$2" $3 # reads the rest of stdin ) || die "fatal: can't copy commit $1" } @@ -683,10 +681,10 @@ new_squash_commit () { if test -n "$old" then squash_msg "$dir" "$oldsub" "$newsub" | - git commit-tree "$tree" -p "$old" || exit $? + git commit-tree $arg_gpg_sign "$tree" -p "$old" || exit $? else squash_msg "$dir" "" "$newsub" | - git commit-tree "$tree" || exit $? + git commit-tree $arg_gpg_sign "$tree" || exit $? fi } @@ -787,20 +785,40 @@ ensure_valid_ref_format () { die "fatal: '$1' does not look like a ref" } -# Usage: check if a commit from another subtree should be +# Usage: should_ignore_subtree_split_commit REV +# +# Check if REV is a commit from another subtree and should be # ignored from processing for splits should_ignore_subtree_split_commit () { assert test $# = 1 - local rev="$1" - if test -n "$(git log -1 --grep="git-subtree-dir:" $rev)" + + git show \ + --no-patch \ + --no-show-signature \ + --format='%(trailers:key=git-subtree-dir,key=git-subtree-mainline)' \ + "$1" | + ( + have_mainline= + subtree_dir= + + while read -r trailer val + do + case "$trailer" in + git-subtree-dir:) + subtree_dir="${val%/}" ;; + git-subtree-mainline:) + have_mainline=y ;; + esac + done + + if test -n "${subtree_dir}" && + test -z "${have_mainline}" && + test "${subtree_dir}" != "$arg_prefix" then - if test -z "$(git log -1 --grep="git-subtree-mainline:" $rev)" && - test -z "$(git log -1 --grep="git-subtree-dir: $arg_prefix$" $rev)" - then - return 0 - fi + return 0 fi return 1 + ) } # Usage: process_split_commit REV PARENTS @@ -925,11 +943,11 @@ cmd_add_commit () { then rev=$(new_squash_commit "" "" "$rev") || exit $? commit=$(add_squashed_msg "$rev" "$dir" | - git commit-tree "$tree" $headp -p "$rev") || exit $? + git commit-tree $arg_gpg_sign "$tree" $headp -p "$rev") || exit $? else revp=$(peel_committish "$rev") || exit $? commit=$(add_msg "$dir" $headrev "$rev" | - git commit-tree "$tree" $headp -p "$revp") || exit $? + git commit-tree $arg_gpg_sign "$tree" $headp -p "$revp") || exit $? fi git reset "$commit" || exit $? @@ -1080,9 +1098,9 @@ cmd_merge () { if test -n "$arg_addmerge_message" then git merge --no-ff -Xsubtree="$arg_prefix" \ - --message="$arg_addmerge_message" "$rev" + --message="$arg_addmerge_message" $arg_gpg_sign "$rev" else - git merge --no-ff -Xsubtree="$arg_prefix" $rev + git merge --no-ff -Xsubtree="$arg_prefix" $arg_gpg_sign $rev fi } diff --git a/contrib/subtree/meson.build b/contrib/subtree/meson.build index 63714166a6..98dd8e0c8e 100644 --- a/contrib/subtree/meson.build +++ b/contrib/subtree/meson.build @@ -21,7 +21,7 @@ if get_option('tests') env: subtree_test_environment, workdir: meson.current_source_dir() / 't', depends: test_dependencies + bin_wrappers + [ git_subtree ], - timeout: 0, + kwargs: test_kwargs, ) endif diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh index 3c6103f6d2..316dc5269e 100755 --- a/contrib/subtree/t/t7900-subtree.sh +++ b/contrib/subtree/t/t7900-subtree.sh @@ -9,8 +9,12 @@ This test verifies the basic operation of the add, merge, split, pull, and push subcommands of git subtree. ' +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME + TEST_DIRECTORY=$(pwd)/../../../t . "$TEST_DIRECTORY"/test-lib.sh +. "$TEST_DIRECTORY"/lib-gpg.sh # Use our own wrapper around test-lib.sh's test_create_repo, in order # to set log.date=relative. `git subtree` parses the output of `git @@ -67,6 +71,33 @@ test_create_pre2_32_repo () { git -C "$1-clone" replace HEAD^2 $new_commit } +# test_create_subtree_add REPO ORPHAN PREFIX FILENAME ... +# +# Create a simple subtree on a new branch named ORPHAN in REPO. +# The subtree is then merged into the current branch of REPO, +# under PREFIX. The generated subtree has has one commit +# with subject and tag FILENAME with a single file "FILENAME.t" +# +# When this method returns: +# - the current branch of REPO will have file PREFIX/FILENAME.t +# - REPO will have a branch named ORPHAN with subtree history +# +# additional arguments are forwarded to "subtree add" +test_create_subtree_add () { + ( + cd "$1" && + orphan="$2" && + prefix="$3" && + filename="$4" && + shift 4 && + last="$(git branch --show-current)" && + git switch --orphan "$orphan" && + test_commit "$filename" && + git checkout "$last" && + git subtree add --prefix="$prefix" "$@" "$orphan" + ) +} + test_expect_success 'shows short help text for -h' ' test_expect_code 129 git subtree -h >out 2>err && test_must_be_empty err && @@ -425,6 +456,47 @@ test_expect_success 'split with multiple subtrees' ' --squash --rejoin -d -m "Sub B Split 1" 2>&1 | grep -w "\[1\]")" = "" ' +# When subtree split-ing a directory that has other subtree +# *merges* underneath it, the split must include those subtrees. +# This test creates a nested subtree, `subA/subB`, and tests +# that the tree is correct after a subtree split of `subA/`. +# The test covers: +# - An initial `subtree add`; and +# - A follow-up `subtree merge` +# both with and without `--squashed`. +for is_squashed in '' 'y' +do + test_expect_success "split keeps nested ${is_squashed:+--squash }subtrees that are part of the split" ' + subtree_test_create_repo "$test_count" && + ( + cd "$test_count" && + mkdir subA && + test_commit subA/file1 && + test_create_subtree_add \ + . mksubtree subA/subB file2 ${is_squashed:+--squash} && + test_path_is_file subA/file1.t && + test_path_is_file subA/subB/file2.t && + git subtree split --prefix=subA --branch=bsplit && + git checkout bsplit && + test_path_is_file file1.t && + test_path_is_file subB/file2.t && + git checkout mksubtree && + git branch -D bsplit && + test_commit file3 && + git checkout main && + git subtree merge \ + ${is_squashed:+--squash} \ + --prefix=subA/subB mksubtree && + test_path_is_file subA/subB/file3.t && + git subtree split --prefix=subA --branch=bsplit && + git checkout bsplit && + test_path_is_file file1.t && + test_path_is_file subB/file2.t && + test_path_is_file subB/file3.t + ) + ' +done + test_expect_success 'split sub dir/ with --rejoin from scratch' ' subtree_test_create_repo "$test_count" && test_create_commit "$test_count" main1 && @@ -1563,4 +1635,116 @@ test_expect_success 'subtree descendant check' ' ) ' +test_expect_success GPG 'add subproj with GPG signing using -S flag' ' + subtree_test_create_repo "$test_count" && + subtree_test_create_repo "$test_count/sub proj" && + test_create_commit "$test_count" main1 && + test_create_commit "$test_count/sub proj" sub1 && + ( + cd "$test_count" && + git fetch ./"sub proj" HEAD && + git subtree add --prefix="sub dir" -S FETCH_HEAD && + git verify-commit HEAD && + test "$(last_commit_subject)" = "Add '\''sub dir/'\'' from commit '\''$(git rev-parse FETCH_HEAD)'\''" + ) +' + +test_expect_success GPG 'add subproj with GPG signing using --gpg-sign flag' ' + subtree_test_create_repo "$test_count" && + subtree_test_create_repo "$test_count/sub proj" && + test_create_commit "$test_count" main1 && + test_create_commit "$test_count/sub proj" sub1 && + ( + cd "$test_count" && + git fetch ./"sub proj" HEAD && + git subtree add --prefix="sub dir" --gpg-sign FETCH_HEAD && + git verify-commit HEAD && + test "$(last_commit_subject)" = "Add '\''sub dir/'\'' from commit '\''$(git rev-parse FETCH_HEAD)'\''" + ) +' + +test_expect_success GPG 'add subproj with GPG signing using specific key ID' ' + subtree_test_create_repo "$test_count" && + subtree_test_create_repo "$test_count/sub proj" && + test_create_commit "$test_count" main1 && + test_create_commit "$test_count/sub proj" sub1 && + ( + cd "$test_count" && + git fetch ./"sub proj" HEAD && + git subtree add --prefix="sub dir" -S"$GIT_COMMITTER_EMAIL" FETCH_HEAD && + git verify-commit HEAD && + test "$(last_commit_subject)" = "Add '\''sub dir/'\'' from commit '\''$(git rev-parse FETCH_HEAD)'\''" + ) +' + +test_expect_success GPG 'merge with GPG signing' ' + subtree_test_create_repo "$test_count" && + subtree_test_create_repo "$test_count/sub proj" && + test_create_commit "$test_count" main1 && + test_create_commit "$test_count/sub proj" sub1 && + ( + cd "$test_count" && + git fetch ./"sub proj" HEAD && + git subtree add --prefix="sub dir" FETCH_HEAD + ) && + test_create_commit "$test_count/sub proj" sub2 && + ( + cd "$test_count" && + git fetch ./"sub proj" HEAD && + git subtree merge --prefix="sub dir" -S FETCH_HEAD && + git verify-commit HEAD + ) +' + +test_expect_success GPG 'split with GPG signing and --rejoin' ' + subtree_test_create_repo "$test_count" && + subtree_test_create_repo "$test_count/sub proj" && + test_create_commit "$test_count" main1 && + test_create_commit "$test_count/sub proj" sub1 && + ( + cd "$test_count" && + git fetch ./"sub proj" HEAD && + git subtree add --prefix="sub dir" FETCH_HEAD + ) && + test_create_commit "$test_count" "sub dir/main-sub1" && + ( + cd "$test_count" && + git subtree split --prefix="sub dir" --rejoin -S && + git verify-commit HEAD + ) +' + +test_expect_success GPG 'add with --squash and GPG signing' ' + subtree_test_create_repo "$test_count" && + subtree_test_create_repo "$test_count/sub proj" && + test_create_commit "$test_count" main1 && + test_create_commit "$test_count/sub proj" sub1 && + ( + cd "$test_count" && + git fetch ./"sub proj" HEAD && + git subtree add --prefix="sub dir" --squash -S FETCH_HEAD && + git verify-commit HEAD && + # With --squash, the commit subject should reference the squash commit (first parent of merge) + squash_commit=$(git rev-parse HEAD^2) && + test "$(last_commit_subject)" = "Merge commit '\''$squash_commit'\'' as '\''sub dir'\''" + ) +' + +test_expect_success GPG 'pull with GPG signing' ' + subtree_test_create_repo "$test_count" && + subtree_test_create_repo "$test_count/sub proj" && + test_create_commit "$test_count" main1 && + test_create_commit "$test_count/sub proj" sub1 && + ( + cd "$test_count" && + git subtree add --prefix="sub dir" ./"sub proj" HEAD + ) && + test_create_commit "$test_count/sub proj" sub2 && + ( + cd "$test_count" && + git subtree pull --prefix="sub dir" -S ./"sub proj" HEAD && + git verify-commit HEAD + ) +' + test_done diff --git a/contrib/thunderbird-patch-inline/README b/contrib/thunderbird-patch-inline/README deleted file mode 100644 index 000147bbe4..0000000000 --- a/contrib/thunderbird-patch-inline/README +++ /dev/null @@ -1,20 +0,0 @@ -appp.sh is a script that is supposed to be used together with ExternalEditor -for Mozilla Thunderbird. It will let you include patches inline in e-mails -in an easy way. - -Usage: -- Generate the patch with git format-patch. -- Start writing a new e-mail in Thunderbird. -- Press the external editor button (or Ctrl-E) to run appp.sh -- Select the previously generated patch file. -- Finish editing the e-mail. - -Any text that is entered into the message editor before appp.sh is called -will be moved to the section between the --- and the diffstat. - -All S-O-B:s and Cc:s in the patch will be added to the CC list. - -To set it up, just install External Editor and tell it to use appp.sh as the -editor. - -Zenity is a required dependency. diff --git a/contrib/thunderbird-patch-inline/appp.sh b/contrib/thunderbird-patch-inline/appp.sh deleted file mode 100755 index fdcc948352..0000000000 --- a/contrib/thunderbird-patch-inline/appp.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/sh -# Copyright 2008 Lukas Sandström <luksan@gmail.com> -# -# AppendPatch - A script to be used together with ExternalEditor -# for Mozilla Thunderbird to properly include patches inline in e-mails. - -# ExternalEditor can be downloaded at http://globs.org/articles.php?lng=en&pg=2 - -CONFFILE=~/.appprc - -SEP="-=-=-=-=-=-=-=-=-=# Don't remove this line #=-=-=-=-=-=-=-=-=-" -if [ -e "$CONFFILE" ] ; then - LAST_DIR=$(grep -m 1 "^LAST_DIR=" "${CONFFILE}"|sed -e 's/^LAST_DIR=//') - cd "${LAST_DIR}" -else - cd > /dev/null -fi - -PATCH=$(zenity --file-selection) - -if [ "$?" != "0" ] ; then - #zenity --error --text "No patchfile given." - exit 1 -fi - -cd - > /dev/null - -SUBJECT=$(sed -n -e '/^Subject: /p' "${PATCH}") -HEADERS=$(sed -e '/^'"${SEP}"'$/,$d' $1) -BODY=$(sed -e "1,/${SEP}/d" $1) -CMT_MSG=$(sed -e '1,/^$/d' -e '/^---$/,$d' "${PATCH}") -DIFF=$(sed -e '1,/^---$/d' "${PATCH}") - -CCS=$(printf '%s\n%s\n' "$CMT_MSG" "$HEADERS" | sed -n -e 's/^Cc: \(.*\)$/\1,/gp' \ - -e 's/^Signed-off-by: \(.*\)/\1,/gp') - -echo "$SUBJECT" > $1 -echo "Cc: $CCS" >> $1 -echo "$HEADERS" | sed -e '/^Subject: /d' -e '/^Cc: /d' >> $1 -echo "$SEP" >> $1 - -echo "$CMT_MSG" >> $1 -echo "---" >> $1 -if [ "x${BODY}x" != "xx" ] ; then - echo >> $1 - echo "$BODY" >> $1 - echo >> $1 -fi -echo "$DIFF" >> $1 - -LAST_DIR=$(dirname "${PATCH}") - -grep -v "^LAST_DIR=" "${CONFFILE}" > "${CONFFILE}_" -echo "LAST_DIR=${LAST_DIR}" >> "${CONFFILE}_" -mv "${CONFFILE}_" "${CONFFILE}" diff --git a/contrib/workdir/.gitattributes b/contrib/workdir/.gitattributes deleted file mode 100644 index 1f78c5d1bd..0000000000 --- a/contrib/workdir/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -/git-new-workdir eol=lf diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir deleted file mode 100755 index 989197aace..0000000000 --- a/contrib/workdir/git-new-workdir +++ /dev/null @@ -1,105 +0,0 @@ -#!/bin/sh - -usage () { - echo "usage:" $@ - exit 127 -} - -die () { - echo $@ - exit 128 -} - -failed () { - die "unable to create new workdir '$new_workdir'!" -} - -if test $# -lt 2 || test $# -gt 3 -then - usage "$0 <repository> <new_workdir> [<branch>]" -fi - -orig_git=$1 -new_workdir=$2 -branch=$3 - -# want to make sure that what is pointed to has a .git directory ... -git_dir=$(cd "$orig_git" 2>/dev/null && - git rev-parse --git-dir 2>/dev/null) || - die "Not a git repository: \"$orig_git\"" - -case "$git_dir" in -.git) - git_dir="$orig_git/.git" - ;; -.) - git_dir=$orig_git - ;; -esac - -# don't link to a configured bare repository -isbare=$(git --git-dir="$git_dir" config --bool --get core.bare) -if test ztrue = "z$isbare" -then - die "\"$git_dir\" has core.bare set to true," \ - " remove from \"$git_dir/config\" to use $0" -fi - -# don't link to a workdir -if test -h "$git_dir/config" -then - die "\"$orig_git\" is a working directory only, please specify" \ - "a complete repository." -fi - -# make sure the links in the workdir have full paths to the original repo -git_dir=$(cd "$git_dir" && pwd) || exit 1 - -# don't recreate a workdir over an existing directory, unless it's empty -if test -d "$new_workdir" -then - if test $(ls -a1 "$new_workdir/." | wc -l) -ne 2 - then - die "destination directory '$new_workdir' is not empty." - fi - cleandir="$new_workdir/.git" -else - cleandir="$new_workdir" -fi - -mkdir -p "$new_workdir/.git" || failed -cleandir=$(cd "$cleandir" && pwd) || failed - -cleanup () { - rm -rf "$cleandir" -} -siglist="0 1 2 15" -trap cleanup $siglist - -# create the links to the original repo. explicitly exclude index, HEAD and -# logs/HEAD from the list since they are purely related to the current working -# directory, and should not be shared. -for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn reftable -do - # create a containing directory if needed - case $x in - */*) - mkdir -p "$new_workdir/.git/${x%/*}" - ;; - esac - - ln -s "$git_dir/$x" "$new_workdir/.git/$x" || failed -done - -# commands below this are run in the context of the new workdir -cd "$new_workdir" || failed - -# copy the HEAD from the original repository as a default branch -cp "$git_dir/HEAD" .git/HEAD || failed - -# the workdir is set up. if the checkout fails, the user can fix it. -trap - $siglist - -# checkout the branch (either the same as HEAD from the original repository, -# or the one that was asked for) -git checkout -f $branch @@ -1326,7 +1326,7 @@ void convert_attrs(struct index_state *istate, "eol", "text", "working-tree-encoding", NULL); user_convert_tail = &user_convert; - git_config(read_convert_config, NULL); + repo_config(the_repository, read_convert_config, NULL); } git_check_attr(istate, path, check); @@ -402,7 +402,7 @@ static int run_service(const char *dir, struct daemon_service *service, if (service->overridable) { strbuf_addf(&var, "daemon.%s", service->config_name); - git_config_get_bool(var.buf, &enabled); + repo_config_get_bool(the_repository, var.buf, &enabled); strbuf_release(&var); } if (!enabled) { @@ -915,11 +915,9 @@ static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen) static void child_handler(int signo UNUSED) { /* - * Otherwise empty handler because systemcalls will get interrupted - * upon signal receipt - * SysV needs the handler to be rearmed + * Otherwise empty handler because systemcalls should get interrupted + * upon signal receipt. */ - signal(SIGCHLD, child_handler); } static int set_reuse_addr(int sockfd) @@ -990,11 +988,6 @@ static int setup_named_sock(char *listen_addr, int listen_port, struct socketlis sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sockfd < 0) continue; - if (sockfd >= FD_SETSIZE) { - logerror("Socket descriptor too large"); - close(sockfd); - continue; - } #ifdef IPV6_V6ONLY if (ai->ai_family == AF_INET6) { @@ -1120,6 +1113,7 @@ static void socksetup(struct string_list *listen_addr, int listen_port, struct s static int service_loop(struct socketlist *socklist) { + struct sigaction sa; struct pollfd *pfd; CALLOC_ARRAY(pfd, socklist->nr); @@ -1129,7 +1123,10 @@ static int service_loop(struct socketlist *socklist) pfd[i].events = POLLIN; } - signal(SIGCHLD, child_handler); + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_NOCLDSTOP; + sa.sa_handler = child_handler; + sigaction(SIGCHLD, &sa, NULL); for (;;) { check_dead_children(); @@ -1153,11 +1150,19 @@ static int service_loop(struct socketlist *socklist) #endif } ss; socklen_t sslen = sizeof(ss); - int incoming = accept(pfd[i].fd, &ss.sa, &sslen); + int incoming; + int retry = 3; + + redo: + incoming = accept(pfd[i].fd, &ss.sa, &sslen); if (incoming < 0) { switch (errno) { - case EAGAIN: case EINTR: + if (--retry) + goto redo; + + /* fallthrough */ + case EAGAIN: case ECONNABORTED: continue; default: diff --git a/diagnose.c b/diagnose.c index b1be74be98..5092bf80d3 100644 --- a/diagnose.c +++ b/diagnose.c @@ -7,7 +7,7 @@ #include "gettext.h" #include "hex.h" #include "strvec.h" -#include "object-store.h" +#include "odb.h" #include "packfile.h" #include "parse-options.h" #include "repository.h" @@ -59,13 +59,13 @@ static void dir_file_stats_objects(const char *full_path, (uintmax_t)st.st_size); } -static int dir_file_stats(struct object_directory *object_dir, void *data) +static int dir_file_stats(struct odb_source *source, void *data) { struct strbuf *buf = data; - strbuf_addf(buf, "Contents of %s:\n", object_dir->path); + strbuf_addf(buf, "Contents of %s:\n", source->path); - for_each_file_in_pack_dir(object_dir->path, dir_file_stats_objects, + for_each_file_in_pack_dir(source->path, dir_file_stats_objects, data); return 0; @@ -228,8 +228,8 @@ int create_diagnostics_archive(struct repository *r, strbuf_reset(&buf); strbuf_addstr(&buf, "--add-virtual-file=packs-local.txt:"); - dir_file_stats(r->objects->odb, &buf); - foreach_alt_odb(dir_file_stats, &buf); + dir_file_stats(r->objects->sources, &buf); + odb_for_each_alternate(r->objects, dir_file_stats, &buf); strvec_push(&archiver_args, buf.buf); strbuf_reset(&buf); diff --git a/diff-lib.c b/diff-lib.c index 244468dd1a..b8f8f3bc31 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -115,6 +115,9 @@ void run_diff_files(struct rev_info *revs, unsigned int option) uint64_t start = getnanotime(); struct index_state *istate = revs->diffopt.repo->index; + if (revs->diffopt.max_depth_valid) + die(_("max-depth is not supported for worktree diffs")); + diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/"); refresh_fsmonitor(istate); @@ -560,6 +563,8 @@ static int diff_cache(struct rev_info *revs, opts.dst_index = NULL; opts.pathspec = &revs->diffopt.pathspec; opts.pathspec->recursive = 1; + if (revs->diffopt.max_depth_valid) + die(_("max-depth is not supported for index diffs")); init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size); return unpack_trees(1, &t, &opts); diff --git a/diff-no-index.c b/diff-no-index.c index 9739b2b268..88ae4cee56 100644 --- a/diff-no-index.c +++ b/diff-no-index.c @@ -15,20 +15,57 @@ #include "gettext.h" #include "revision.h" #include "parse-options.h" +#include "pathspec.h" #include "string-list.h" #include "dir.h" -static int read_directory_contents(const char *path, struct string_list *list) +static int read_directory_contents(const char *path, struct string_list *list, + const struct pathspec *pathspec, + int skip) { + struct strbuf match = STRBUF_INIT; + int len; DIR *dir; struct dirent *e; if (!(dir = opendir(path))) return error("Could not open directory %s", path); - while ((e = readdir_skip_dot_and_dotdot(dir))) + if (pathspec) { + strbuf_addstr(&match, path); + strbuf_complete(&match, '/'); + strbuf_remove(&match, 0, skip); + + len = match.len; + } + + while ((e = readdir_skip_dot_and_dotdot(dir))) { + if (pathspec) { + int is_dir = 0; + + strbuf_setlen(&match, len); + strbuf_addstr(&match, e->d_name); + if (NOT_CONSTANT(DTYPE(e)) != DT_UNKNOWN) { + is_dir = (DTYPE(e) == DT_DIR); + } else { + struct strbuf pathbuf = STRBUF_INIT; + + strbuf_addstr(&pathbuf, path); + strbuf_complete(&pathbuf, '/'); + is_dir = get_dtype(e, &pathbuf, 0) == DT_DIR; + strbuf_release(&pathbuf); + } + + if (!match_leading_pathspec(NULL, pathspec, + match.buf, match.len, + 0, NULL, is_dir)) + continue; + } + string_list_insert(list, e->d_name); + } + strbuf_release(&match); closedir(dir); return 0; } @@ -131,7 +168,8 @@ static struct diff_filespec *noindex_filespec(const struct git_hash_algo *algop, } static int queue_diff(struct diff_options *o, const struct git_hash_algo *algop, - const char *name1, const char *name2, int recursing) + const char *name1, const char *name2, int recursing, + const struct pathspec *ps, int skip1, int skip2) { int mode1 = 0, mode2 = 0; enum special special1 = SPECIAL_NONE, special2 = SPECIAL_NONE; @@ -171,9 +209,9 @@ static int queue_diff(struct diff_options *o, const struct git_hash_algo *algop, int i1, i2, ret = 0; size_t len1 = 0, len2 = 0; - if (name1 && read_directory_contents(name1, &p1)) + if (name1 && read_directory_contents(name1, &p1, ps, skip1)) return -1; - if (name2 && read_directory_contents(name2, &p2)) { + if (name2 && read_directory_contents(name2, &p2, ps, skip2)) { string_list_clear(&p1, 0); return -1; } @@ -218,7 +256,7 @@ static int queue_diff(struct diff_options *o, const struct git_hash_algo *algop, n2 = buffer2.buf; } - ret = queue_diff(o, algop, n1, n2, 1); + ret = queue_diff(o, algop, n1, n2, 1, ps, skip1, skip2); } string_list_clear(&p1, 0); string_list_clear(&p2, 0); @@ -258,8 +296,10 @@ static void append_basename(struct strbuf *path, const char *dir, const char *fi * DWIM "diff D F" into "diff D/F F" and "diff F D" into "diff F D/F" * Note that we append the basename of F to D/, so "diff a/b/file D" * becomes "diff a/b/file D/file", not "diff a/b/file D/a/b/file". + * + * Return 1 if both paths are directories, 0 otherwise. */ -static void fixup_paths(const char **path, struct strbuf *replacement) +static int fixup_paths(const char **path, struct strbuf *replacement) { struct stat st; unsigned int isdir0 = 0, isdir1 = 0; @@ -282,26 +322,31 @@ static void fixup_paths(const char **path, struct strbuf *replacement) if ((isdir0 && ispipe1) || (ispipe0 && isdir1)) die(_("cannot compare a named pipe to a directory")); - if (isdir0 == isdir1) - return; + /* if both paths are directories, we will enable pathspecs */ + if (isdir0 && isdir1) + return 1; + if (isdir0) { append_basename(replacement, path[0], path[1]); path[0] = replacement->buf; - } else { + } else if (isdir1) { append_basename(replacement, path[1], path[0]); path[1] = replacement->buf; } + + return 0; } static const char * const diff_no_index_usage[] = { - N_("git diff --no-index [<options>] <path> <path>"), + N_("git diff --no-index [<options>] <path> <path> [<pathspec>...]"), NULL }; int diff_no_index(struct rev_info *revs, const struct git_hash_algo *algop, int implicit_no_index, int argc, const char **argv) { - int i, no_index; + struct pathspec pathspec, *ps = NULL; + int i, no_index, skip1 = 0, skip2 = 0; int ret = 1; const char *paths[2]; char *to_free[ARRAY_SIZE(paths)] = { 0 }; @@ -317,13 +362,12 @@ int diff_no_index(struct rev_info *revs, const struct git_hash_algo *algop, options = add_diff_options(no_index_options, &revs->diffopt); argc = parse_options(argc, argv, revs->prefix, options, diff_no_index_usage, 0); - if (argc != 2) { + if (argc < 2) { if (implicit_no_index) warning(_("Not a git repository. Use --no-index to " "compare two paths outside a working tree")); usage_with_options(diff_no_index_usage, options); } - FREE_AND_NULL(options); for (i = 0; i < 2; i++) { const char *p = argv[i]; if (!strcmp(p, "-")) @@ -337,7 +381,23 @@ int diff_no_index(struct rev_info *revs, const struct git_hash_algo *algop, paths[i] = p; } - fixup_paths(paths, &replacement); + if (fixup_paths(paths, &replacement)) { + parse_pathspec(&pathspec, PATHSPEC_FROMTOP | PATHSPEC_ATTR, + PATHSPEC_PREFER_FULL | PATHSPEC_NO_REPOSITORY, + NULL, &argv[2]); + if (pathspec.nr) + ps = &pathspec; + + skip1 = strlen(paths[0]); + skip1 += paths[0][skip1] == '/' ? 0 : 1; + skip2 = strlen(paths[1]); + skip2 += paths[1][skip2] == '/' ? 0 : 1; + } else if (argc > 2) { + warning(_("Limiting comparison with pathspecs is only " + "supported if both paths are directories.")); + usage_with_options(diff_no_index_usage, options); + } + FREE_AND_NULL(options); revs->diffopt.skip_stat_unmatch = 1; if (!revs->diffopt.output_format) @@ -354,7 +414,8 @@ int diff_no_index(struct rev_info *revs, const struct git_hash_algo *algop, setup_diff_pager(&revs->diffopt); revs->diffopt.flags.exit_with_status = 1; - if (queue_diff(&revs->diffopt, algop, paths[0], paths[1], 0)) + if (queue_diff(&revs->diffopt, algop, paths[0], paths[1], 0, ps, + skip1, skip2)) goto out; diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/"); diffcore_std(&revs->diffopt); @@ -370,5 +431,7 @@ out: for (i = 0; i < ARRAY_SIZE(to_free); i++) free(to_free[i]); strbuf_release(&replacement); + if (ps) + clear_pathspec(ps); return ret; } @@ -23,7 +23,7 @@ #include "color.h" #include "run-command.h" #include "utf8.h" -#include "object-store.h" +#include "odb.h" #include "userdiff.h" #include "submodule.h" #include "hashmap.h" @@ -327,29 +327,23 @@ static unsigned parse_color_moved_ws(const char *arg) struct string_list l = STRING_LIST_INIT_DUP; struct string_list_item *i; - string_list_split(&l, arg, ',', -1); + string_list_split_f(&l, arg, ",", -1, STRING_LIST_SPLIT_TRIM); for_each_string_list_item(i, &l) { - struct strbuf sb = STRBUF_INIT; - strbuf_addstr(&sb, i->string); - strbuf_trim(&sb); - - if (!strcmp(sb.buf, "no")) + if (!strcmp(i->string, "no")) ret = 0; - else if (!strcmp(sb.buf, "ignore-space-change")) + else if (!strcmp(i->string, "ignore-space-change")) ret |= XDF_IGNORE_WHITESPACE_CHANGE; - else if (!strcmp(sb.buf, "ignore-space-at-eol")) + else if (!strcmp(i->string, "ignore-space-at-eol")) ret |= XDF_IGNORE_WHITESPACE_AT_EOL; - else if (!strcmp(sb.buf, "ignore-all-space")) + else if (!strcmp(i->string, "ignore-all-space")) ret |= XDF_IGNORE_WHITESPACE; - else if (!strcmp(sb.buf, "allow-indentation-change")) + else if (!strcmp(i->string, "allow-indentation-change")) ret |= COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE; else { ret |= COLOR_MOVED_WS_ERROR; - error(_("unknown color-moved-ws mode '%s', possible values are 'ignore-space-change', 'ignore-space-at-eol', 'ignore-all-space', 'allow-indentation-change'"), sb.buf); + error(_("unknown color-moved-ws mode '%s', possible values are 'ignore-space-change', 'ignore-space-at-eol', 'ignore-all-space', 'allow-indentation-change'"), i->string); } - - strbuf_release(&sb); } if ((ret & COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) && @@ -2444,6 +2438,15 @@ static int fn_out_consume(void *priv, char *line, unsigned long len) return 0; } +static int quick_consume(void *priv, char *line UNUSED, unsigned long len UNUSED) +{ + struct emit_callback *ecbdata = priv; + struct diff_options *o = ecbdata->opt; + + o->found_changes = 1; + return 1; +} + static void pprint_rename(struct strbuf *name, const char *a, const char *b) { const char *old_name = a; @@ -3759,8 +3762,21 @@ static void builtin_diff(const char *name_a, if (o->word_diff) init_diff_words_data(&ecbdata, o, one, two); - if (xdi_diff_outf(&mf1, &mf2, NULL, fn_out_consume, - &ecbdata, &xpp, &xecfg)) + if (o->dry_run) { + /* + * Unlike the !dry_run case, we need to ignore the + * return value from xdi_diff_outf() here, because + * xdi_diff_outf() takes non-zero return from its + * callback function as a sign of error and returns + * early (which is why we return non-zero from our + * callback, quick_consume()). Unfortunately, + * xdi_diff_outf() signals an error by returning + * non-zero. + */ + xdi_diff_outf(&mf1, &mf2, NULL, quick_consume, + &ecbdata, &xpp, &xecfg); + } else if (xdi_diff_outf(&mf1, &mf2, NULL, fn_out_consume, + &ecbdata, &xpp, &xecfg)) die("unable to generate diff for %s", one->path); if (o->word_diff) free_diff_words_data(&ecbdata); @@ -4230,14 +4246,14 @@ int diff_populate_filespec(struct repository *r, info.contentp = &s->data; if (options && options->missing_object_cb) { - if (!oid_object_info_extended(r, &s->oid, &info, - OBJECT_INFO_LOOKUP_REPLACE | - OBJECT_INFO_SKIP_FETCH_OBJECT)) + if (!odb_read_object_info_extended(r->objects, &s->oid, &info, + OBJECT_INFO_LOOKUP_REPLACE | + OBJECT_INFO_SKIP_FETCH_OBJECT)) goto object_read; options->missing_object_cb(options->missing_object_data); } - if (oid_object_info_extended(r, &s->oid, &info, - OBJECT_INFO_LOOKUP_REPLACE)) + if (odb_read_object_info_extended(r->objects, &s->oid, &info, + OBJECT_INFO_LOOKUP_REPLACE)) die("unable to read %s", oid_to_hex(&s->oid)); object_read: @@ -4252,8 +4268,8 @@ object_read: } if (!info.contentp) { info.contentp = &s->data; - if (oid_object_info_extended(r, &s->oid, &info, - OBJECT_INFO_LOOKUP_REPLACE)) + if (odb_read_object_info_extended(r->objects, &s->oid, &info, + OBJECT_INFO_LOOKUP_REPLACE)) die("unable to read %s", oid_to_hex(&s->oid)); } s->should_free = 1; @@ -4988,6 +5004,9 @@ void diff_setup_done(struct diff_options *options) options->filter = ~filter_bit[DIFF_STATUS_FILTER_AON]; options->filter &= ~options->filter_not; } + + if (options->pathspec.has_wildcard && options->max_depth_valid) + die("max-depth cannot be used with wildcard pathspecs"); } int parse_long_opt(const char *opt, const char **argv, @@ -5622,6 +5641,23 @@ static int diff_opt_rotate_to(const struct option *opt, const char *arg, int uns return 0; } +static int diff_opt_max_depth(const struct option *opt, + const char *arg, int unset) +{ + struct diff_options *options = opt->value; + + BUG_ON_OPT_NEG(unset); + + if (!git_parse_int(arg, &options->max_depth)) + return error(_("invalid value for '%s': '%s'"), + "--max-depth", arg); + + options->flags.recursive = 1; + options->max_depth_valid = options->max_depth >= 0; + + return 0; +} + /* * Consider adding new flags to __git_diff_common_options * in contrib/completion/git-completion.bash @@ -5894,6 +5930,10 @@ struct option *add_diff_options(const struct option *opts, OPT_CALLBACK_F(0, "diff-filter", options, N_("[(A|C|D|M|R|T|U|X|B)...[*]]"), N_("select files by diff type"), PARSE_OPT_NONEG, diff_opt_diff_filter), + OPT_CALLBACK_F(0, "max-depth", options, N_("<depth>"), + N_("maximum tree depth to recurse"), + PARSE_OPT_NONEG, diff_opt_max_depth), + { .type = OPTION_CALLBACK, .long_name = "output", @@ -6150,6 +6190,22 @@ static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o) run_diff(p, o); } +/* return 1 if any change is found; otherwise, return 0 */ +static int diff_flush_patch_quietly(struct diff_filepair *p, struct diff_options *o) +{ + int saved_dry_run = o->dry_run; + int saved_found_changes = o->found_changes; + int ret; + + o->dry_run = 1; + o->found_changes = 0; + diff_flush_patch(p, o); + ret = o->found_changes; + o->dry_run = saved_dry_run; + o->found_changes |= saved_found_changes; + return ret; +} + static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o, struct diffstat_t *diffstat) { @@ -6778,8 +6834,15 @@ void diff_flush(struct diff_options *options) DIFF_FORMAT_CHECKDIFF)) { for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; - if (check_pair_status(p)) - flush_one_pair(p, options); + + if (!check_pair_status(p)) + continue; + + if (options->flags.diff_from_contents && + !diff_flush_patch_quietly(p, options)) + continue; + + flush_one_pair(p, options); } separator++; } @@ -6831,19 +6894,10 @@ void diff_flush(struct diff_options *options) if (output_format & DIFF_FORMAT_NO_OUTPUT && options->flags.exit_with_status && options->flags.diff_from_contents) { - /* - * run diff_flush_patch for the exit status. setting - * options->file to /dev/null should be safe, because we - * aren't supposed to produce any output anyway. - */ - diff_free_file(options); - options->file = xfopen("/dev/null", "w"); - options->close_file = 1; - options->color_moved = 0; for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; if (check_pair_status(p)) - diff_flush_patch(p, options); + diff_flush_patch_quietly(p, options); if (options->found_changes) break; } @@ -7019,8 +7073,8 @@ void diff_add_if_missing(struct repository *r, { if (filespec && filespec->oid_valid && !S_ISGITLINK(filespec->mode) && - oid_object_info_extended(r, &filespec->oid, NULL, - OBJECT_INFO_FOR_PREFETCH)) + odb_read_object_info_extended(r->objects, &filespec->oid, NULL, + OBJECT_INFO_FOR_PREFETCH)) oid_array_append(to_fetch, &filespec->oid); } @@ -400,10 +400,20 @@ struct diff_options { #define COLOR_MOVED_WS_ERROR (1<<0) unsigned color_moved_ws_handling; + bool dry_run; + struct repository *repo; struct strmap *additional_path_headers; int no_free; + + /* + * The value '0' is a valid max-depth (for no recursion), and value '-1' + * also (for unlimited recursion), so the extra "valid" flag is used to + * determined whether the user specified option --max-depth. + */ + int max_depth; + int max_depth_valid; }; unsigned diff_filter_bit(char status); @@ -277,7 +277,7 @@ int within_depth(const char *name, int namelen, if (depth > max_depth) return 0; } - return 1; + return depth <= max_depth; } /* @@ -302,7 +302,7 @@ static int do_read_blob(const struct object_id *oid, struct oid_stat *oid_stat, *size_out = 0; *data_out = NULL; - data = repo_read_object_file(the_repository, oid, &type, &sz); + data = odb_read_object(the_repository->objects, oid, &type, &sz); if (!data || type != OBJ_BLOB) { free(data); return -1; @@ -397,9 +397,12 @@ static int match_pathspec_item(struct index_state *istate, strncmp(item->match, name - prefix, item->prefix)) return 0; - if (item->attr_match_nr && - !match_pathspec_attrs(istate, name - prefix, namelen + prefix, item)) - return 0; + if (item->attr_match_nr) { + if (!istate) + BUG("magic PATHSPEC_ATTR requires an index"); + if (!match_pathspec_attrs(istate, name - prefix, namelen + prefix, item)) + return 0; + } /* If the match was just the prefix, we matched */ if (!*match) @@ -577,6 +580,16 @@ int match_pathspec(struct index_state *istate, prefix, seen, flags); } +int match_leading_pathspec(struct index_state *istate, + const struct pathspec *ps, + const char *name, int namelen, + int prefix, char *seen, int is_dir) +{ + unsigned flags = is_dir ? DO_MATCH_DIRECTORY | DO_MATCH_LEADING_PATHSPEC : 0; + return match_pathspec_with_flags(istate, ps, name, namelen, + prefix, seen, flags); +} + /** * Check if a submodule is a superset of the pathspec */ @@ -4078,8 +4091,8 @@ void connect_work_tree_and_git_dir(const char *work_tree_, write_file(gitfile_sb.buf, "gitdir: %s", relative_path(git_dir, work_tree, &rel_path)); /* Update core.worktree setting */ - git_config_set_in_file(cfg_sb.buf, "core.worktree", - relative_path(work_tree, git_dir, &rel_path)); + repo_config_set_in_file(the_repository, cfg_sb.buf, "core.worktree", + relative_path(work_tree, git_dir, &rel_path)); strbuf_release(&gitfile_sb); strbuf_release(&cfg_sb); @@ -676,4 +676,27 @@ static inline int starts_with_dot_dot_slash_native(const char *const path) return path_match_flags(path, what | PATH_MATCH_NATIVE); } +/** + * starts_with_dot_slash: convenience wrapper for + * patch_match_flags() with PATH_MATCH_STARTS_WITH_DOT_SLASH and + * PATH_MATCH_XPLATFORM. + */ +static inline int starts_with_dot_slash(const char *const path) +{ + const enum path_match_flags what = PATH_MATCH_STARTS_WITH_DOT_SLASH; + + return path_match_flags(path, what | PATH_MATCH_XPLATFORM); +} + +/** + * starts_with_dot_dot_slash: convenience wrapper for + * patch_match_flags() with PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH and + * PATH_MATCH_XPLATFORM. + */ +static inline int starts_with_dot_dot_slash(const char *const path) +{ + const enum path_match_flags what = PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH; + + return path_match_flags(path, what | PATH_MATCH_XPLATFORM); +} #endif @@ -50,7 +50,7 @@ const char *git_sequence_editor(void) const char *editor = getenv("GIT_SEQUENCE_EDITOR"); if (!editor) - git_config_get_string_tmp("sequence.editor", &editor); + repo_config_get_string_tmp(the_repository, "sequence.editor", &editor); if (!editor) editor = git_editor(); @@ -1,7 +1,7 @@ #define USE_THE_REPOSITORY_VARIABLE #include "git-compat-util.h" -#include "object-store.h" +#include "odb.h" #include "dir.h" #include "environment.h" #include "gettext.h" @@ -93,8 +93,8 @@ void *read_blob_entry(const struct cache_entry *ce, size_t *size) { enum object_type type; unsigned long ul; - void *blob_data = repo_read_object_file(the_repository, &ce->oid, - &type, &ul); + void *blob_data = odb_read_object(the_repository->objects, &ce->oid, + &type, &ul); *size = ul; if (blob_data) { diff --git a/environment.c b/environment.c index c61d773e7e..a770b5921d 100644 --- a/environment.c +++ b/environment.c @@ -12,22 +12,34 @@ #include "git-compat-util.h" #include "abspath.h" +#include "advice.h" +#include "attr.h" #include "branch.h" +#include "color.h" #include "convert.h" #include "environment.h" #include "gettext.h" #include "git-zlib.h" +#include "ident.h" +#include "mailmap.h" +#include "object-name.h" #include "repository.h" #include "config.h" #include "refs.h" #include "fmt-merge-msg.h" #include "commit.h" #include "strvec.h" +#include "pager.h" #include "path.h" +#include "quote.h" #include "chdir-notify.h" #include "setup.h" +#include "ws.h" #include "write-or-die.h" +static int pack_compression_seen; +static int zlib_compression_seen; + int trust_executable_bit = 1; int trust_ctime = 1; int check_stat = 1; @@ -37,7 +49,6 @@ int ignore_case; int assume_unchanged; int is_bare_repository_cfg = -1; /* unspecified */ int warn_on_object_refname_ambiguity = 1; -int repository_format_precious_objects; char *git_commit_encoding; char *git_log_output_encoding; char *apply_default_whitespace; @@ -67,7 +78,6 @@ int grafts_keep_true_parents; int core_apply_sparse_checkout; int core_sparse_checkout_cone; int sparse_expect_files_outside_of_patterns; -int merge_log_config = -1; int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */ unsigned long pack_size_limit_cfg; int max_allowed_tree_depth = @@ -111,10 +121,10 @@ int protect_ntfs = PROTECT_NTFS_DEFAULT; */ const char *comment_line_str = "#"; char *comment_line_str_to_free; +#ifndef WITH_BREAKING_CHANGES int auto_comment_line_char; - -/* Parallel index stat data preload? */ -int core_preload_index = 1; +bool warn_on_auto_comment_char; +#endif /* !WITH_BREAKING_CHANGES */ /* This is set by setup_git_directory_gently() and/or git_default_config() */ char *git_work_tree_cfg; @@ -167,10 +177,10 @@ int have_git_dir(void) const char *get_git_namespace(void) { static const char *namespace; - struct strbuf buf = STRBUF_INIT; - struct strbuf **components, **c; const char *raw_namespace; + struct string_list components = STRING_LIST_INIT_DUP; + struct string_list_item *item; if (namespace) return namespace; @@ -182,12 +192,17 @@ const char *get_git_namespace(void) } strbuf_addstr(&buf, raw_namespace); - components = strbuf_split(&buf, '/'); + + string_list_split(&components, buf.buf, "/", -1); strbuf_reset(&buf); - for (c = components; *c; c++) - if (strcmp((*c)->buf, "/") != 0) - strbuf_addf(&buf, "refs/namespaces/%s", (*c)->buf); - strbuf_list_free(components); + + for_each_string_list_item(item, &components) { + if (item->string[0]) + strbuf_addf(&buf, "refs/namespaces/%s/", item->string); + } + string_list_clear(&components, 0); + + strbuf_trim_trailing_dir_sep(&buf); if (check_refname_format(buf.buf, 0)) die(_("bad git namespace path \"%s\""), raw_namespace); strbuf_addch(&buf, '/'); @@ -235,3 +250,509 @@ int print_sha1_ellipsis(void) } return cached_result; } + +static const struct fsync_component_name { + const char *name; + enum fsync_component component_bits; +} fsync_component_names[] = { + { "loose-object", FSYNC_COMPONENT_LOOSE_OBJECT }, + { "pack", FSYNC_COMPONENT_PACK }, + { "pack-metadata", FSYNC_COMPONENT_PACK_METADATA }, + { "commit-graph", FSYNC_COMPONENT_COMMIT_GRAPH }, + { "index", FSYNC_COMPONENT_INDEX }, + { "objects", FSYNC_COMPONENTS_OBJECTS }, + { "reference", FSYNC_COMPONENT_REFERENCE }, + { "derived-metadata", FSYNC_COMPONENTS_DERIVED_METADATA }, + { "committed", FSYNC_COMPONENTS_COMMITTED }, + { "added", FSYNC_COMPONENTS_ADDED }, + { "all", FSYNC_COMPONENTS_ALL }, +}; + +static enum fsync_component parse_fsync_components(const char *var, const char *string) +{ + enum fsync_component current = FSYNC_COMPONENTS_PLATFORM_DEFAULT; + enum fsync_component positive = 0, negative = 0; + + while (string) { + size_t len; + const char *ep; + int negated = 0; + int found = 0; + + string = string + strspn(string, ", \t\n\r"); + ep = strchrnul(string, ','); + len = ep - string; + if (!strcmp(string, "none")) { + current = FSYNC_COMPONENT_NONE; + goto next_name; + } + + if (*string == '-') { + negated = 1; + string++; + len--; + if (!len) + warning(_("invalid value for variable %s"), var); + } + + if (!len) + break; + + for (size_t i = 0; i < ARRAY_SIZE(fsync_component_names); ++i) { + const struct fsync_component_name *n = &fsync_component_names[i]; + + if (strncmp(n->name, string, len)) + continue; + + found = 1; + if (negated) + negative |= n->component_bits; + else + positive |= n->component_bits; + } + + if (!found) { + char *component = xstrndup(string, len); + warning(_("ignoring unknown core.fsync component '%s'"), component); + free(component); + } + +next_name: + string = ep; + } + + return (current & ~negative) | positive; +} + +static int git_default_core_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) +{ + /* This needs a better name */ + if (!strcmp(var, "core.filemode")) { + trust_executable_bit = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "core.trustctime")) { + trust_ctime = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "core.checkstat")) { + if (!value) + return config_error_nonbool(var); + if (!strcasecmp(value, "default")) + check_stat = 1; + else if (!strcasecmp(value, "minimal")) + check_stat = 0; + else + return error(_("invalid value for '%s': '%s'"), + var, value); + } + + if (!strcmp(var, "core.quotepath")) { + quote_path_fully = git_config_bool(var, value); + return 0; + } + + if (!strcmp(var, "core.symlinks")) { + has_symlinks = git_config_bool(var, value); + return 0; + } + + if (!strcmp(var, "core.ignorecase")) { + ignore_case = git_config_bool(var, value); + return 0; + } + + if (!strcmp(var, "core.attributesfile")) { + FREE_AND_NULL(git_attributes_file); + return git_config_pathname(&git_attributes_file, var, value); + } + + if (!strcmp(var, "core.bare")) { + is_bare_repository_cfg = git_config_bool(var, value); + return 0; + } + + if (!strcmp(var, "core.ignorestat")) { + assume_unchanged = git_config_bool(var, value); + return 0; + } + + if (!strcmp(var, "core.abbrev")) { + if (!value) + return config_error_nonbool(var); + if (!strcasecmp(value, "auto")) + default_abbrev = -1; + else if (!git_parse_maybe_bool_text(value)) + default_abbrev = GIT_MAX_HEXSZ; + else { + int abbrev = git_config_int(var, value, ctx->kvi); + if (abbrev < minimum_abbrev) + return error(_("abbrev length out of range: %d"), abbrev); + default_abbrev = abbrev; + } + return 0; + } + + if (!strcmp(var, "core.disambiguate")) + return set_disambiguate_hint_config(var, value); + + if (!strcmp(var, "core.loosecompression")) { + int level = git_config_int(var, value, ctx->kvi); + if (level == -1) + level = Z_DEFAULT_COMPRESSION; + else if (level < 0 || level > Z_BEST_COMPRESSION) + die(_("bad zlib compression level %d"), level); + zlib_compression_level = level; + zlib_compression_seen = 1; + return 0; + } + + if (!strcmp(var, "core.compression")) { + int level = git_config_int(var, value, ctx->kvi); + if (level == -1) + level = Z_DEFAULT_COMPRESSION; + else if (level < 0 || level > Z_BEST_COMPRESSION) + die(_("bad zlib compression level %d"), level); + if (!zlib_compression_seen) + zlib_compression_level = level; + if (!pack_compression_seen) + pack_compression_level = level; + return 0; + } + + if (!strcmp(var, "core.autocrlf")) { + if (value && !strcasecmp(value, "input")) { + auto_crlf = AUTO_CRLF_INPUT; + return 0; + } + auto_crlf = git_config_bool(var, value); + return 0; + } + + if (!strcmp(var, "core.safecrlf")) { + int eol_rndtrp_die; + if (value && !strcasecmp(value, "warn")) { + global_conv_flags_eol = CONV_EOL_RNDTRP_WARN; + return 0; + } + eol_rndtrp_die = git_config_bool(var, value); + global_conv_flags_eol = eol_rndtrp_die ? + CONV_EOL_RNDTRP_DIE : 0; + return 0; + } + + if (!strcmp(var, "core.eol")) { + if (value && !strcasecmp(value, "lf")) + core_eol = EOL_LF; + else if (value && !strcasecmp(value, "crlf")) + core_eol = EOL_CRLF; + else if (value && !strcasecmp(value, "native")) + core_eol = EOL_NATIVE; + else + core_eol = EOL_UNSET; + return 0; + } + + if (!strcmp(var, "core.checkroundtripencoding")) { + FREE_AND_NULL(check_roundtrip_encoding); + return git_config_string(&check_roundtrip_encoding, var, value); + } + + if (!strcmp(var, "core.editor")) { + FREE_AND_NULL(editor_program); + return git_config_string(&editor_program, var, value); + } + + if (!strcmp(var, "core.commentchar") || + !strcmp(var, "core.commentstring")) { + if (!value) { + return config_error_nonbool(var); +#ifndef WITH_BREAKING_CHANGES + } else if (!strcasecmp(value, "auto")) { + auto_comment_line_char = 1; + FREE_AND_NULL(comment_line_str_to_free); + comment_line_str = "#"; +#endif /* !WITH_BREAKING_CHANGES */ + } else if (value[0]) { + if (strchr(value, '\n')) + return error(_("%s cannot contain newline"), var); + comment_line_str = value; + FREE_AND_NULL(comment_line_str_to_free); +#ifndef WITH_BREAKING_CHANGES + auto_comment_line_char = 0; +#endif /* !WITH_BREAKING_CHANGES */ + } else + return error(_("%s must have at least one character"), var); + return 0; + } + + if (!strcmp(var, "core.askpass")) { + FREE_AND_NULL(askpass_program); + return git_config_string(&askpass_program, var, value); + } + + if (!strcmp(var, "core.excludesfile")) { + FREE_AND_NULL(excludes_file); + return git_config_pathname(&excludes_file, var, value); + } + + if (!strcmp(var, "core.whitespace")) { + if (!value) + return config_error_nonbool(var); + whitespace_rule_cfg = parse_whitespace_rule(value); + return 0; + } + + if (!strcmp(var, "core.fsync")) { + if (!value) + return config_error_nonbool(var); + fsync_components = parse_fsync_components(var, value); + return 0; + } + + if (!strcmp(var, "core.fsyncmethod")) { + if (!value) + return config_error_nonbool(var); + if (!strcmp(value, "fsync")) + fsync_method = FSYNC_METHOD_FSYNC; + else if (!strcmp(value, "writeout-only")) + fsync_method = FSYNC_METHOD_WRITEOUT_ONLY; + else if (!strcmp(value, "batch")) + fsync_method = FSYNC_METHOD_BATCH; + else + warning(_("ignoring unknown core.fsyncMethod value '%s'"), value); + + } + + if (!strcmp(var, "core.fsyncobjectfiles")) { + if (fsync_object_files < 0) + warning(_("core.fsyncObjectFiles is deprecated; use core.fsync instead")); + fsync_object_files = git_config_bool(var, value); + return 0; + } + + if (!strcmp(var, "core.createobject")) { + if (!value) + return config_error_nonbool(var); + if (!strcmp(value, "rename")) + object_creation_mode = OBJECT_CREATION_USES_RENAMES; + else if (!strcmp(value, "link")) + object_creation_mode = OBJECT_CREATION_USES_HARDLINKS; + else + die(_("invalid mode for object creation: %s"), value); + return 0; + } + + if (!strcmp(var, "core.sparsecheckout")) { + core_apply_sparse_checkout = git_config_bool(var, value); + return 0; + } + + if (!strcmp(var, "core.sparsecheckoutcone")) { + core_sparse_checkout_cone = git_config_bool(var, value); + return 0; + } + + if (!strcmp(var, "core.precomposeunicode")) { + precomposed_unicode = git_config_bool(var, value); + return 0; + } + + if (!strcmp(var, "core.protecthfs")) { + protect_hfs = git_config_bool(var, value); + return 0; + } + + if (!strcmp(var, "core.protectntfs")) { + protect_ntfs = git_config_bool(var, value); + return 0; + } + + if (!strcmp(var, "core.maxtreedepth")) { + max_allowed_tree_depth = git_config_int(var, value, ctx->kvi); + return 0; + } + + /* Add other config variables here and to Documentation/config.adoc. */ + return platform_core_config(var, value, ctx, cb); +} + +static int git_default_sparse_config(const char *var, const char *value) +{ + if (!strcmp(var, "sparse.expectfilesoutsideofpatterns")) { + sparse_expect_files_outside_of_patterns = git_config_bool(var, value); + return 0; + } + + /* Add other config variables here and to Documentation/config/sparse.adoc. */ + return 0; +} + +static int git_default_i18n_config(const char *var, const char *value) +{ + if (!strcmp(var, "i18n.commitencoding")) { + FREE_AND_NULL(git_commit_encoding); + return git_config_string(&git_commit_encoding, var, value); + } + + if (!strcmp(var, "i18n.logoutputencoding")) { + FREE_AND_NULL(git_log_output_encoding); + return git_config_string(&git_log_output_encoding, var, value); + } + + /* Add other config variables here and to Documentation/config.adoc. */ + return 0; +} + +static int git_default_branch_config(const char *var, const char *value) +{ + if (!strcmp(var, "branch.autosetupmerge")) { + if (value && !strcmp(value, "always")) { + git_branch_track = BRANCH_TRACK_ALWAYS; + return 0; + } else if (value && !strcmp(value, "inherit")) { + git_branch_track = BRANCH_TRACK_INHERIT; + return 0; + } else if (value && !strcmp(value, "simple")) { + git_branch_track = BRANCH_TRACK_SIMPLE; + return 0; + } + git_branch_track = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "branch.autosetuprebase")) { + if (!value) + return config_error_nonbool(var); + else if (!strcmp(value, "never")) + autorebase = AUTOREBASE_NEVER; + else if (!strcmp(value, "local")) + autorebase = AUTOREBASE_LOCAL; + else if (!strcmp(value, "remote")) + autorebase = AUTOREBASE_REMOTE; + else if (!strcmp(value, "always")) + autorebase = AUTOREBASE_ALWAYS; + else + return error(_("malformed value for %s"), var); + return 0; + } + + /* Add other config variables here and to Documentation/config.adoc. */ + return 0; +} + +static int git_default_push_config(const char *var, const char *value) +{ + if (!strcmp(var, "push.default")) { + if (!value) + return config_error_nonbool(var); + else if (!strcmp(value, "nothing")) + push_default = PUSH_DEFAULT_NOTHING; + else if (!strcmp(value, "matching")) + push_default = PUSH_DEFAULT_MATCHING; + else if (!strcmp(value, "simple")) + push_default = PUSH_DEFAULT_SIMPLE; + else if (!strcmp(value, "upstream")) + push_default = PUSH_DEFAULT_UPSTREAM; + else if (!strcmp(value, "tracking")) /* deprecated */ + push_default = PUSH_DEFAULT_UPSTREAM; + else if (!strcmp(value, "current")) + push_default = PUSH_DEFAULT_CURRENT; + else { + error(_("malformed value for %s: %s"), var, value); + return error(_("must be one of nothing, matching, simple, " + "upstream or current")); + } + return 0; + } + + /* Add other config variables here and to Documentation/config.adoc. */ + return 0; +} + +static int git_default_mailmap_config(const char *var, const char *value) +{ + if (!strcmp(var, "mailmap.file")) { + FREE_AND_NULL(git_mailmap_file); + return git_config_pathname(&git_mailmap_file, var, value); + } + + if (!strcmp(var, "mailmap.blob")) { + FREE_AND_NULL(git_mailmap_blob); + return git_config_string(&git_mailmap_blob, var, value); + } + + /* Add other config variables here and to Documentation/config.adoc. */ + return 0; +} + +static int git_default_attr_config(const char *var, const char *value) +{ + if (!strcmp(var, "attr.tree")) { + FREE_AND_NULL(git_attr_tree); + return git_config_string(&git_attr_tree, var, value); + } + + /* + * Add other attribute related config variables here and to + * Documentation/config/attr.adoc. + */ + return 0; +} + +int git_default_config(const char *var, const char *value, + const struct config_context *ctx, void *cb) +{ + if (starts_with(var, "core.")) + return git_default_core_config(var, value, ctx, cb); + + if (starts_with(var, "user.") || + starts_with(var, "author.") || + starts_with(var, "committer.")) + return git_ident_config(var, value, ctx, cb); + + if (starts_with(var, "i18n.")) + return git_default_i18n_config(var, value); + + if (starts_with(var, "branch.")) + return git_default_branch_config(var, value); + + if (starts_with(var, "push.")) + return git_default_push_config(var, value); + + if (starts_with(var, "mailmap.")) + return git_default_mailmap_config(var, value); + + if (starts_with(var, "attr.")) + return git_default_attr_config(var, value); + + if (starts_with(var, "advice.") || starts_with(var, "color.advice")) + return git_default_advice_config(var, value); + + if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) { + pager_use_color = git_config_bool(var,value); + return 0; + } + + if (!strcmp(var, "pack.packsizelimit")) { + pack_size_limit_cfg = git_config_ulong(var, value, ctx->kvi); + return 0; + } + + if (!strcmp(var, "pack.compression")) { + int level = git_config_int(var, value, ctx->kvi); + if (level == -1) + level = Z_DEFAULT_COMPRESSION; + else if (level < 0 || level > Z_BEST_COMPRESSION) + die(_("bad pack compression level %d"), level); + pack_compression_level = level; + pack_compression_seen = 1; + return 0; + } + + if (starts_with(var, "sparse.")) + return git_default_sparse_config(var, value); + + /* Add other config variables here and to Documentation/config.adoc. */ + return 0; +} diff --git a/environment.h b/environment.h index 3d98461a06..51898c99cd 100644 --- a/environment.h +++ b/environment.h @@ -104,6 +104,9 @@ int use_optional_locks(void); const char *get_git_namespace(void); const char *strip_namespace(const char *namespaced_ref); +int git_default_config(const char *, const char *, + const struct config_context *, void *); + /* * TODO: All the below state either explicitly or implicitly relies on * `the_repository`. We should eventually get rid of these and make the @@ -155,7 +158,6 @@ extern int pack_compression_level; extern unsigned long pack_size_limit_cfg; extern int max_allowed_tree_depth; -extern int core_preload_index; extern int precomposed_unicode; extern int protect_hfs; extern int protect_ntfs; @@ -190,8 +192,6 @@ extern enum object_creation_mode object_creation_mode; extern int grafts_keep_true_parents; -extern int repository_format_precious_objects; - const char *get_log_output_encoding(void); const char *get_commit_output_encoding(void); @@ -208,7 +208,10 @@ extern char *excludes_file; */ extern const char *comment_line_str; extern char *comment_line_str_to_free; +#ifndef WITH_BREAKING_CHANGES extern int auto_comment_line_char; +extern bool warn_on_auto_comment_char; +#endif /* !WITH_BREAKING_CHANGES */ # endif /* USE_THE_REPOSITORY_VARIABLE */ #endif /* ENVIRONMENT_H */ diff --git a/fetch-pack.c b/fetch-pack.c index fa4231fee7..6ed5662951 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -24,7 +24,7 @@ #include "oid-array.h" #include "oidset.h" #include "packfile.h" -#include "object-store.h" +#include "odb.h" #include "path.h" #include "connected.h" #include "fetch-negotiator.h" @@ -34,6 +34,7 @@ #include "commit-graph.h" #include "sigchain.h" #include "mergesort.h" +#include "prio-queue.h" static int transfer_unpack_limit = -1; static int fetch_unpack_limit = -1; @@ -115,7 +116,8 @@ static void for_each_cached_alternate(struct fetch_negotiator *negotiator, size_t i; if (!initialized) { - for_each_alternate_ref(cache_one_alternate, &cache); + odb_for_each_alternate_ref(the_repository->objects, + cache_one_alternate, &cache); initialized = 1; } @@ -141,15 +143,16 @@ static struct commit *deref_without_lazy_fetch(const struct object_id *oid, commit = lookup_commit_in_graph(the_repository, oid); if (commit) { if (mark_tags_complete_and_check_obj_db) { - if (!has_object(the_repository, oid, 0)) + if (!odb_has_object(the_repository->objects, oid, + HAS_OBJECT_RECHECK_PACKED)) die_in_commit_graph_only(oid); } return commit; } while (1) { - if (oid_object_info_extended(the_repository, oid, &info, - OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK)) + if (odb_read_object_info_extended(the_repository->objects, oid, &info, + OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK)) return NULL; if (type == OBJ_TAG) { struct tag *tag = (struct tag *) @@ -600,7 +603,7 @@ done: return count ? retval : 0; } -static struct commit_list *complete; +static struct prio_queue complete = { compare_commits_by_commit_date }; static int mark_complete(const struct object_id *oid) { @@ -608,7 +611,7 @@ static int mark_complete(const struct object_id *oid) if (commit && !(commit->object.flags & COMPLETE)) { commit->object.flags |= COMPLETE; - commit_list_insert(commit, &complete); + prio_queue_put(&complete, commit); } return 0; } @@ -625,9 +628,12 @@ static int mark_complete_oid(const char *refname UNUSED, static void mark_recent_complete_commits(struct fetch_pack_args *args, timestamp_t cutoff) { - while (complete && cutoff <= complete->item->date) { + while (complete.nr) { + struct commit *item = prio_queue_peek(&complete); + if (item->date < cutoff) + break; print_verbose(args, _("Marking %s as complete"), - oid_to_hex(&complete->item->object.oid)); + oid_to_hex(&item->object.oid)); pop_most_recent_commit(&complete, COMPLETE); } } @@ -769,7 +775,7 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator, if (!commit) { struct object *o; - if (!has_object(the_repository, &ref->old_oid, 0)) + if (!odb_has_object(the_repository->objects, &ref->old_oid, 0)) continue; o = parse_object(the_repository, &ref->old_oid); if (!o || o->type != OBJ_COMMIT) @@ -797,7 +803,6 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator, refs_for_each_rawref(get_main_ref_store(the_repository), mark_complete_oid, NULL); for_each_cached_alternate(NULL, mark_alternate_complete); - commit_list_sort_by_date(&complete); if (cutoff) mark_recent_complete_commits(args, cutoff); } @@ -1342,7 +1347,7 @@ static void write_fetch_command_and_capabilities(struct strbuf *req_buf, die(_("mismatched algorithms: client %s; server %s"), the_hash_algo->name, hash_name); packet_buf_write(req_buf, "object-format=%s", the_hash_algo->name); - } else if (hash_algo_by_ptr(the_hash_algo) != GIT_HASH_SHA1) { + } else if (hash_algo_by_ptr(the_hash_algo) != GIT_HASH_SHA1_LEGACY) { die(_("the server does not support algorithm '%s'"), the_hash_algo->name); } @@ -1900,22 +1905,22 @@ static int fetch_pack_config_cb(const char *var, const char *value, static void fetch_pack_config(void) { - git_config_get_int("fetch.unpacklimit", &fetch_unpack_limit); - git_config_get_int("transfer.unpacklimit", &transfer_unpack_limit); - git_config_get_bool("repack.usedeltabaseoffset", &prefer_ofs_delta); - git_config_get_bool("fetch.fsckobjects", &fetch_fsck_objects); - git_config_get_bool("transfer.fsckobjects", &transfer_fsck_objects); - git_config_get_bool("transfer.advertisesid", &advertise_sid); + repo_config_get_int(the_repository, "fetch.unpacklimit", &fetch_unpack_limit); + repo_config_get_int(the_repository, "transfer.unpacklimit", &transfer_unpack_limit); + repo_config_get_bool(the_repository, "repack.usedeltabaseoffset", &prefer_ofs_delta); + repo_config_get_bool(the_repository, "fetch.fsckobjects", &fetch_fsck_objects); + repo_config_get_bool(the_repository, "transfer.fsckobjects", &transfer_fsck_objects); + repo_config_get_bool(the_repository, "transfer.advertisesid", &advertise_sid); if (!uri_protocols.nr) { char *str; - if (!git_config_get_string("fetch.uriprotocols", &str) && str) { - string_list_split(&uri_protocols, str, ',', -1); + if (!repo_config_get_string(the_repository, "fetch.uriprotocols", &str) && str) { + string_list_split(&uri_protocols, str, ",", -1); free(str); } } - git_config(fetch_pack_config_cb, NULL); + repo_config(the_repository, fetch_pack_config_cb, NULL); } static void fetch_pack_setup(void) @@ -1983,8 +1988,8 @@ static void update_shallow(struct fetch_pack_args *args, struct oid_array extra = OID_ARRAY_INIT; struct object_id *oid = si->shallow->oid; for (i = 0; i < si->shallow->nr; i++) - if (has_object(the_repository, &oid[i], - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) + if (odb_has_object(the_repository->objects, &oid[i], + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) oid_array_append(&extra, &oid[i]); if (extra.nr) { setup_alternate_shallow(&shallow_lock, diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c index 501b5acdd4..c9085edc40 100644 --- a/fmt-merge-msg.c +++ b/fmt-merge-msg.c @@ -6,7 +6,7 @@ #include "environment.h" #include "refs.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "diff.h" #include "diff-merges.h" #include "hex.h" @@ -26,13 +26,15 @@ static struct string_list suppress_dest_patterns = STRING_LIST_INIT_DUP; int fmt_merge_msg_config(const char *key, const char *value, const struct config_context *ctx, void *cb) { + int *merge_log_config = cb; + if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) { int is_bool; - merge_log_config = git_config_bool_or_int(key, value, ctx->kvi, &is_bool); - if (!is_bool && merge_log_config < 0) + *merge_log_config = git_config_bool_or_int(key, value, ctx->kvi, &is_bool); + if (!is_bool && *merge_log_config < 0) return error("%s: negative length %s", key, value); - if (is_bool && merge_log_config) - merge_log_config = DEFAULT_MERGE_LOG_LEN; + if (is_bool && *merge_log_config) + *merge_log_config = DEFAULT_MERGE_LOG_LEN; } else if (!strcmp(key, "merge.branchdesc")) { use_branch_desc = git_config_bool(key, value); } else if (!strcmp(key, "merge.suppressdest")) { @@ -526,8 +528,8 @@ static void fmt_merge_msg_sigs(struct strbuf *out) struct object_id *oid = origins.items[i].util; enum object_type type; unsigned long size; - char *buf = repo_read_object_file(the_repository, oid, &type, - &size); + char *buf = odb_read_object(the_repository->objects, oid, + &type, &size); char *origbuf = buf; unsigned long len = size; struct signature_check sigc = { NULL }; diff --git a/fmt-merge-msg.h b/fmt-merge-msg.h index 73ca3e4465..c066d83761 100644 --- a/fmt-merge-msg.h +++ b/fmt-merge-msg.h @@ -12,7 +12,6 @@ struct fmt_merge_msg_opts { const char *into_name; }; -extern int merge_log_config; int fmt_merge_msg_config(const char *key, const char *value, const struct config_context *ctx, void *cb); int fmt_merge_msg(struct strbuf *in, struct strbuf *out, diff --git a/for-each-ref.h b/for-each-ref.h new file mode 100644 index 0000000000..c8d0219179 --- /dev/null +++ b/for-each-ref.h @@ -0,0 +1,26 @@ +#ifndef FOR_EACH_REF_H +#define FOR_EACH_REF_H + +struct repository; + +/* + * Shared usage string for options common to git-for-each-ref(1) + * and git-refs-list(1). The command-specific part (e.g., "git refs list ") + * must be prepended by the caller. + */ +#define COMMON_USAGE_FOR_EACH_REF \ + "[--count=<count>] [--shell|--perl|--python|--tcl]\n" \ + " [(--sort=<key>)...] [--format=<format>]\n" \ + " [--include-root-refs] [--points-at=<object>]\n" \ + " [--merged[=<object>]] [--no-merged[=<object>]]\n" \ + " [--contains[=<object>]] [--no-contains[=<object>]]\n" \ + " [(--exclude=<pattern>)...] [--start-after=<marker>]\n" \ + " [ --stdin | (<pattern>...)]" + +/* + * The core logic for for-each-ref and its clones. + */ +int for_each_ref_core(int argc, const char **argv, const char *prefix, + struct repository *repo, const char *const *usage); + +#endif /* FOR_EACH_REF_H */ @@ -3,8 +3,9 @@ #include "git-compat-util.h" #include "date.h" #include "dir.h" +#include "environment.h" #include "hex.h" -#include "object-store.h" +#include "odb.h" #include "path.h" #include "repository.h" #include "object.h" @@ -1293,7 +1294,7 @@ static int fsck_blobs(struct oidset *blobs_found, struct oidset *blobs_done, if (oidset_contains(blobs_done, oid)) continue; - buf = repo_read_object_file(the_repository, oid, &type, &size); + buf = odb_read_object(the_repository->objects, oid, &type, &size); if (!buf) { if (is_promisor_object(the_repository, oid)) continue; @@ -287,7 +287,7 @@ const char *fsck_describe_object(struct fsck_options *options, struct key_value_info; /* - * git_config() callback for use by fsck-y tools that want to support + * repo_config() callback for use by fsck-y tools that want to support * fsck.<msg> fsck.skipList etc. */ int git_fsck_config(const char *var, const char *value, diff --git a/fsmonitor.c b/fsmonitor.c index 98b2b476f0..d07dc18967 100644 --- a/fsmonitor.c +++ b/fsmonitor.c @@ -43,7 +43,7 @@ static int fsmonitor_hook_version(void) { int hook_version; - if (git_config_get_int("core.fsmonitorhookversion", &hook_version)) + if (repo_config_get_int(the_repository, "core.fsmonitorhookversion", &hook_version)) return -1; if (hook_version == HOOK_INTERFACE_VERSION1 || diff --git a/git-compat-util.h b/git-compat-util.h index 4678e21c4c..9408f463e3 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -460,6 +460,8 @@ void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2))); void show_usage_if_asked(int ac, const char **av, const char *err); +NORETURN void you_still_use_that(const char *command_name); + #ifndef NO_OPENSSL #ifdef APPLE_COMMON_CRYPTO #include "compat/apple-common-crypto.h" @@ -895,16 +897,16 @@ static inline size_t xsize_t(off_t len) * is done via tolower(), so it is strictly ASCII (no multi-byte characters or * locale-specific conversions). */ -static inline int skip_iprefix(const char *str, const char *prefix, +static inline bool skip_iprefix(const char *str, const char *prefix, const char **out) { do { if (!*prefix) { *out = str; - return 1; + return true; } } while (tolower(*str++) == tolower(*prefix++)); - return 0; + return false; } /* @@ -912,7 +914,7 @@ static inline int skip_iprefix(const char *str, const char *prefix, * comparison is done via tolower(), so it is strictly ASCII (no multi-byte * characters or locale-specific conversions). */ -static inline int skip_iprefix_mem(const char *buf, size_t len, +static inline bool skip_iprefix_mem(const char *buf, size_t len, const char *prefix, const char **out, size_t *outlen) { @@ -920,10 +922,10 @@ static inline int skip_iprefix_mem(const char *buf, size_t len, if (!*prefix) { *out = buf; *outlen = len; - return 1; + return true; } } while (len-- > 0 && tolower(*buf++) == tolower(*prefix++)); - return 0; + return false; } static inline int strtoul_ui(char const *s, int base, unsigned int *result) diff --git a/git-curl-compat.h b/git-curl-compat.h index aa8eed7ed2..659e5a3875 100644 --- a/git-curl-compat.h +++ b/git-curl-compat.h @@ -46,6 +46,13 @@ #endif /** + * curl_global_trace() was added in 8.3.0, released September 2023. + */ +#if LIBCURL_VERSION_NUM >= 0x080300 +#define GIT_CURL_HAVE_GLOBAL_TRACE 1 +#endif + +/** * CURLOPT_TCP_KEEPCNT was added in 8.9.0, released in July, 2024. */ #if LIBCURL_VERSION_NUM >= 0x080900 diff --git a/git-gui/.gitignore b/git-gui/.gitignore index ff6e0be4b4..5130b4f018 100644 --- a/git-gui/.gitignore +++ b/git-gui/.gitignore @@ -1,8 +1,8 @@ .DS_Store config.mak -Git Gui.app* git-gui.tcl GIT-GUI-BUILD-OPTIONS GIT-VERSION-FILE git-gui +git-gui--askpass lib/tclIndex diff --git a/git-gui/GIT-GUI-BUILD-OPTIONS.in b/git-gui/GIT-GUI-BUILD-OPTIONS.in index 5fd885c2bf..3c112af578 100644 --- a/git-gui/GIT-GUI-BUILD-OPTIONS.in +++ b/git-gui/GIT-GUI-BUILD-OPTIONS.in @@ -4,4 +4,3 @@ GITGUI_RELATIVE=@GITGUI_RELATIVE@ SHELL_PATH=@SHELL_PATH@ TCLTK_PATH=@TCLTK_PATH@ TCL_PATH=@TCL_PATH@ -TKEXECUTABLE=@TKEXECUTABLE@ diff --git a/git-gui/Makefile b/git-gui/Makefile index 8672dd2d6b..69b0b84435 100644 --- a/git-gui/Makefile +++ b/git-gui/Makefile @@ -12,7 +12,6 @@ GIT-VERSION-FILE: FORCE @$(SHELL_PATH) ./GIT-VERSION-GEN . $@ uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') -uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not') uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not') SCRIPT_SH = git-gui.sh @@ -54,8 +53,6 @@ INSTALL_R0 = $(INSTALL) -m 644 # space is required here INSTALL_R1 = INSTALL_X0 = $(INSTALL) -m 755 # space is required here INSTALL_X1 = -INSTALL_A0 = find # space is required here -INSTALL_A1 = | cpio -pud INSTALL_L0 = rm -f # space is required here INSTALL_L1 = && ln # space is required here INSTALL_L2 = @@ -80,8 +77,6 @@ ifndef V INSTALL_R1 = && echo ' ' INSTALL 644 `basename $$src` && $(INSTALL) -m 644 $$src INSTALL_X0 = src= INSTALL_X1 = && echo ' ' INSTALL 755 `basename $$src` && $(INSTALL) -m 755 $$src - INSTALL_A0 = src= - INSTALL_A1 = && echo ' ' INSTALL ' ' `basename "$$src"` && find "$$src" | cpio -pud INSTALL_L0 = dst= INSTALL_L1 = && src= @@ -102,18 +97,6 @@ else TCL_PATH ?= $(dir $(TCLTK_PATH))$(notdir $(subst wish,tclsh,$(TCLTK_PATH))) endif -ifeq ($(uname_S),Darwin) - TKFRAMEWORK = /Library/Frameworks/Tk.framework/Resources/Wish.app - ifeq ($(shell echo "$(uname_R)" | awk -F. '{if ($$1 >= 9) print "y"}')_$(shell test -d $(TKFRAMEWORK) || echo n),y_n) - TKFRAMEWORK = /System/Library/Frameworks/Tk.framework/Resources/Wish.app - ifeq ($(shell test -d $(TKFRAMEWORK) || echo n),n) - TKFRAMEWORK = /System/Library/Frameworks/Tk.framework/Resources/Wish\ Shell.app - endif - endif - TKEXECUTABLE = $(TKFRAMEWORK)/Contents/MacOS/$(shell basename "$(TKFRAMEWORK)" .app) - TKEXECUTABLE_SQ = $(subst ','\'',$(TKEXECUTABLE)) -endif - ifeq ($(findstring $(firstword -$(MAKEFLAGS)),s),s) QUIET_GEN = endif @@ -131,16 +114,10 @@ libdir_SQ = $(subst ','\'',$(gg_libdir)) exedir = $(dir $(gitexecdir))share/git-gui/lib GITGUI_RELATIVE := -GITGUI_MACOSXAPP := ifeq ($(exedir),$(gg_libdir)) GITGUI_RELATIVE := 1 endif -ifeq ($(uname_S),Darwin) - ifeq ($(shell test -d $(TKFRAMEWORK) && echo y),y) - GITGUI_MACOSXAPP := YesPlease - endif -endif ifneq (,$(findstring MINGW,$(uname_S))) ifeq ($(shell expr "$(uname_R)" : '1\.'),2) NO_MSGFMT=1 @@ -149,20 +126,6 @@ endif GITGUI_RELATIVE := 1 endif -ifdef GITGUI_MACOSXAPP -GITGUI_MAIN := git-gui.tcl - -git-gui: generate-macos-wrapper.sh GIT-VERSION-FILE GIT-GUI-BUILD-OPTIONS - $(QUIET_GEN)$(SHELL_PATH) generate-macos-wrapper.sh "$@" ./GIT-GUI-BUILD-OPTIONS ./GIT-VERSION-FILE - -Git\ Gui.app: GIT-VERSION-FILE GIT-GUI-BUILD-OPTIONS \ - macosx/Info.plist \ - macosx/git-gui.icns \ - macosx/AppMain.tcl \ - $(TKEXECUTABLE) - $(QUIET_GEN)$(SHELL_PATH) generate-macos-app.sh . "$@" ./GIT-GUI-BUILD-OPTIONS ./GIT-VERSION-FILE -endif - ifdef GITGUI_WINDOWS_WRAPPER GITGUI_MAIN := git-gui.tcl @@ -170,7 +133,7 @@ git-gui: windows/git-gui.sh cp $< $@ endif -$(GITGUI_MAIN): git-gui.sh GIT-VERSION-FILE GIT-GUI-BUILD-OPTIONS +$(GITGUI_MAIN): git-gui.sh GIT-VERSION-FILE GIT-GUI-BUILD-OPTIONS generate-git-gui.sh $(QUIET_GEN)$(SHELL_PATH) generate-git-gui.sh "$<" "$@" ./GIT-GUI-BUILD-OPTIONS ./GIT-VERSION-FILE XGETTEXT ?= xgettext @@ -207,33 +170,29 @@ GIT-GUI-BUILD-OPTIONS: FORCE -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \ -e 's|@TCLTK_PATH@|$(TCLTK_PATH_SQ)|' \ -e 's|@TCL_PATH@|$(TCL_PATH_SQ)|' \ - -e 's|@TKEXECUTABLE@|$(TKEXECUTABLE_SQ)|' \ $@.in >$@+ @if grep -q '^[A-Z][A-Z_]*=@.*@$$' $@+; then echo "Unsubstituted build options in $@" >&2 && exit 1; fi @if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi -ifdef GITGUI_MACOSXAPP -all:: git-gui Git\ Gui.app -endif +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 + ifdef GITGUI_WINDOWS_WRAPPER all:: git-gui endif -all:: $(GITGUI_MAIN) lib/tclIndex $(ALL_MSGFILES) +all:: $(GITGUI_MAIN) git-gui--askpass lib/tclIndex $(ALL_MSGFILES) install: all $(QUIET)$(INSTALL_D0)'$(DESTDIR_SQ)$(gitexecdir_SQ)' $(INSTALL_D1) $(QUIET)$(INSTALL_X0)git-gui $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)' $(QUIET)$(INSTALL_X0)git-gui--askpass $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)' + $(QUIET)$(INSTALL_X0)git-gui--askyesno $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)' $(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(INSTALL_L0)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L1)'$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' $(INSTALL_L2)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L3) &&) true ifdef GITGUI_WINDOWS_WRAPPER $(QUIET)$(INSTALL_R0)git-gui.tcl $(INSTALL_R1) '$(DESTDIR_SQ)$(gitexecdir_SQ)' endif $(QUIET)$(INSTALL_D0)'$(DESTDIR_SQ)$(libdir_SQ)' $(INSTALL_D1) $(QUIET)$(INSTALL_R0)lib/tclIndex $(INSTALL_R1) '$(DESTDIR_SQ)$(libdir_SQ)' -ifdef GITGUI_MACOSXAPP - $(QUIET)$(INSTALL_A0)'Git Gui.app' $(INSTALL_A1) '$(DESTDIR_SQ)$(libdir_SQ)' - $(QUIET)$(INSTALL_X0)git-gui.tcl $(INSTALL_X1) '$(DESTDIR_SQ)$(libdir_SQ)' -endif $(QUIET)$(foreach p,$(ALL_LIBFILES) $(NONTCL_LIBFILES), $(INSTALL_R0)$p $(INSTALL_R1) '$(DESTDIR_SQ)$(libdir_SQ)' &&) true $(QUIET)$(INSTALL_D0)'$(DESTDIR_SQ)$(msgsdir_SQ)' $(INSTALL_D1) $(QUIET)$(foreach p,$(ALL_MSGFILES), $(INSTALL_R0)$p $(INSTALL_R1) '$(DESTDIR_SQ)$(msgsdir_SQ)' &&) true @@ -242,16 +201,13 @@ uninstall: $(QUIET)$(CLEAN_DST) '$(DESTDIR_SQ)$(gitexecdir_SQ)' $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui $(REMOVE_F1) $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui--askpass $(REMOVE_F1) + $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui--askyesno $(REMOVE_F1) $(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/$p $(REMOVE_F1) &&) true ifdef GITGUI_WINDOWS_WRAPPER $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui.tcl $(REMOVE_F1) endif $(QUIET)$(CLEAN_DST) '$(DESTDIR_SQ)$(libdir_SQ)' $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(libdir_SQ)'/tclIndex $(REMOVE_F1) -ifdef GITGUI_MACOSXAPP - $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(libdir_SQ)/Git Gui.app' $(REMOVE_F1) - $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(libdir_SQ)'/git-gui.tcl $(REMOVE_F1) -endif $(QUIET)$(foreach p,$(ALL_LIBFILES) $(NONTCL_LIBFILES), $(REMOVE_F0)'$(DESTDIR_SQ)$(libdir_SQ)'/$(notdir $p) $(REMOVE_F1) &&) true $(QUIET)$(CLEAN_DST) '$(DESTDIR_SQ)$(msgsdir_SQ)' $(QUIET)$(foreach p,$(ALL_MSGFILES), $(REMOVE_F0)'$(DESTDIR_SQ)$(msgsdir_SQ)'/$(notdir $p) $(REMOVE_F1) &&) true @@ -265,11 +221,8 @@ dist-version: GIT-VERSION-FILE @sed 's|^GITGUI_VERSION=||' <GIT-VERSION-FILE >$(TARDIR)/version clean:: - $(RM_RF) $(GITGUI_MAIN) lib/tclIndex po/*.msg $(PO_TEMPLATE) + $(RM_RF) $(GITGUI_MAIN) git-gui--askpass lib/tclIndex po/*.msg $(PO_TEMPLATE) $(RM_RF) GIT-VERSION-FILE GIT-GUI-BUILD-OPTIONS -ifdef GITGUI_MACOSXAPP - $(RM_RF) 'Git Gui.app'* git-gui -endif ifdef GITGUI_WINDOWS_WRAPPER $(RM_RF) git-gui endif diff --git a/git-gui/generate-macos-app.sh b/git-gui/generate-macos-app.sh deleted file mode 100755 index 71b9fa67a4..0000000000 --- a/git-gui/generate-macos-app.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/sh - -set -e - -SOURCE_DIR="$1" -OUTPUT="$2" -BUILD_OPTIONS="$3" -VERSION_FILE="$4" - -. "$BUILD_OPTIONS" -. "$VERSION_FILE" - -rm -rf "$OUTPUT" "$OUTPUT+" - -mkdir -p "$OUTPUT+/Contents/MacOS" -mkdir -p "$OUTPUT+/Contents/Resources/Scripts" - -cp "$TKEXECUTABLE" "$OUTPUT+/Contents/MacOS" -cp "$SOURCE_DIR/macosx/git-gui.icns" "$OUTPUT+/Contents/Resources" -sed \ - -e "s/@@GITGUI_VERSION@@/$GITGUI_VERSION/g" \ - -e "s/@@GITGUI_TKEXECUTABLE@@/$(basename "$TKEXECUTABLE")/g" \ - "$SOURCE_DIR/macosx/Info.plist" \ - >"$OUTPUT+/Contents/Info.plist" -sed \ - -e "s|@@gitexecdir@@|$GITGUI_GITEXECDIR|" \ - -e "s|@@GITGUI_LIBDIR@@|$GITGUI_LIBDIR|" \ - "$SOURCE_DIR/macosx/AppMain.tcl" \ - >"$OUTPUT+/Contents/Resources/Scripts/AppMain.tcl" -mv "$OUTPUT+" "$OUTPUT" diff --git a/git-gui/generate-macos-wrapper.sh b/git-gui/generate-macos-wrapper.sh deleted file mode 100755 index 0304937f41..0000000000 --- a/git-gui/generate-macos-wrapper.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh - -set -e - -if test "$#" -ne 3 -then - echo >&2 "usage: $0 <OUTPUT> <BUILD_OPTIONS> <VERSION_FILE>" - exit 1 -fi - -OUTPUT="$1" -BUILD_OPTIONS="$2" -VERSION_FILE="$3" - -. "$BUILD_OPTIONS" - -rm -f "$OUTPUT" "$OUTPUT+" - -( - echo "#!$SHELL_PATH" - cat "$BUILD_OPTIONS" "$VERSION_FILE" - cat <<-'EOF' - if test "z$*" = zversion || - test "z$*" = z--version - then - echo "git-gui version $GITGUI_VERSION" - else - libdir="${GIT_GUI_LIB_DIR:-$GITGUI_LIBDIR}" - exec "$libdir/Git Gui.app/Contents/MacOS/$(basename "$TKEXECUTABLE")" "$0" "$@" - fi - EOF -) >"$OUTPUT+" - -chmod +x "$OUTPUT+" -mv "$OUTPUT+" "$OUTPUT" diff --git a/git-gui/generate-script.sh b/git-gui/generate-script.sh new file mode 100755 index 0000000000..0dd2da92e3 --- /dev/null +++ b/git-gui/generate-script.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +set -e + +if test $# -ne 3 +then + echo >&2 "USAGE: $0 <OUTPUT> <INPUT> <GIT-GUI-BUILD-OPTIONS>" + exit 1 +fi + +OUTPUT="$1" +INPUT="$2" +BUILD_OPTIONS="$3" + +. "$BUILD_OPTIONS" + +sed \ + -e "1s|#!.*/sh|#!$SHELL_PATH|" \ + -e "1,3s|^exec wish|exec '$TCLTK_PATH'|" \ + "$INPUT" >"$OUTPUT" + +chmod a+x "$OUTPUT" diff --git a/git-gui/git-gui--askpass b/git-gui/git-gui--askpass.sh index 71a536d232..71a536d232 100755 --- a/git-gui/git-gui--askpass +++ b/git-gui/git-gui--askpass.sh diff --git a/git-gui/git-gui--askyesno b/git-gui/git-gui--askyesno new file mode 100755 index 0000000000..142d1bc3de --- /dev/null +++ b/git-gui/git-gui--askyesno @@ -0,0 +1,63 @@ +#!/bin/sh +# Tcl ignores the next line -*- tcl -*- \ +exec wish "$0" -- "$@" + +# This is an implementation of a simple yes no dialog +# which is injected into the git commandline by git gui +# in case a yesno question needs to be answered. +# +# The window title, which defaults to "Question?", can be +# overridden via the optional `--title` command-line +# option. + +set NS {} +set use_ttk [package vsatisfies [package provide Tk] 8.5] +if {$use_ttk} { + set NS ttk +} + +set title "Question?" +if {$argc < 1} { + puts stderr "Usage: $argv0 <question>" + exit 1 +} else { + if {$argc > 2 && [lindex $argv 0] == "--title"} { + set title [lindex $argv 1] + set argv [lreplace $argv 0 1] + } + set prompt [join $argv " "] +} + +${NS}::frame .t +${NS}::label .t.m -text $prompt -justify center -width 40 +.t.m configure -wraplength 400 +pack .t.m -side top -fill x -padx 20 -pady 20 -expand 1 +pack .t -side top -fill x -ipadx 20 -ipady 20 -expand 1 + +${NS}::frame .b +${NS}::frame .b.left -width 200 +${NS}::button .b.yes -text Yes -command {exit 0} +${NS}::button .b.no -text No -command {exit 1} + +pack .b.left -side left -expand 1 -fill x +pack .b.yes -side left -expand 1 +pack .b.no -side right -expand 1 -ipadx 5 +pack .b -side bottom -fill x -ipadx 20 -ipady 15 + +bind . <Key-Return> {exit 0} +bind . <Key-Escape> {exit 1} + +if {$::tcl_platform(platform) eq {windows}} { + set icopath [file dirname [file normalize $argv0]] + if {[file tail $icopath] eq {git-core}} { + set icopath [file dirname $icopath] + } + set icopath [file dirname $icopath] + set icopath [file join $icopath share git git-for-windows.ico] + if {[file exists $icopath]} { + wm iconbitmap . -default $icopath + } +} + +wm title . $title +tk::PlaceWindow . diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh index c77c05edde..d3d3aa14a9 100755 --- a/git-gui/git-gui.sh +++ b/git-gui/git-gui.sh @@ -30,9 +30,7 @@ along with this program; if not, see <https://www.gnu.org/licenses/>.}] ## ## Tcl/Tk sanity check -if {[catch {package require Tcl 8.5} err] - || [catch {package require Tk 8.5} err] -} { +if {[catch {package require Tcl 8.6-} err]} { catch {wm withdraw .} tk_messageBox \ -icon error \ @@ -76,24 +74,35 @@ proc is_Cygwin {} { } ###################################################################### +## Enable Tcl8 profile in Tcl9, allowing consumption of data that has +## bytes not conforming to the assumed encoding profile. + +if {[package vcompare $::tcl_version 9.0] >= 0} { + rename open _strict_open + proc open args { + set f [_strict_open {*}$args] + chan configure $f -profile tcl8 + return $f + } + proc convertfrom args { + return [encoding convertfrom -profile tcl8 {*}$args] + } +} else { + proc convertfrom args { + return [encoding convertfrom {*}$args] + } +} + +###################################################################### ## ## PATH lookup. Sanitize $PATH, assure exec/open use only that if {[is_Windows]} { set _path_sep {;} - set _search_exe .exe } else { set _path_sep {:} - set _search_exe {} } -if {[is_Windows]} { - set gitguidir [file dirname [info script]] - regsub -all ";" $gitguidir "\\;" gitguidir - set env(PATH) "$gitguidir;$env(PATH)" -} - -set _search_path {} set _path_seen [dict create] foreach p [split $env(PATH) $_path_sep] { # Keep only absolute paths, getting rid of ., empty, etc. @@ -102,27 +111,24 @@ foreach p [split $env(PATH) $_path_sep] { } # Keep only the first occurence of any duplicates. set norm_p [file normalize $p] - if {[dict exists $_path_seen $norm_p]} { - continue - } dict set _path_seen $norm_p 1 - lappend _search_path $norm_p } +set _search_path [dict keys $_path_seen] unset _path_seen set env(PATH) [join $_search_path $_path_sep] if {[is_Windows]} { proc _which {what args} { - global _search_exe _search_path + global _search_path if {[lsearch -exact $args -script] >= 0} { set suffix {} - } elseif {[string match *$_search_exe [string tolower $what]]} { + } elseif {[string match *.exe [string tolower $what]]} { # The search string already has the file extension set suffix {} } else { - set suffix $_search_exe + set suffix .exe } foreach p $_search_path { @@ -187,7 +193,9 @@ if {[is_Windows]} { set command_line [string trim [string range $arg0 1 end]] lset args 0 "| [sanitize_command_line $command_line 0]" } - uplevel 1 real_open $args + set fd [real_open {*}$args] + fconfigure $fd -eofchar {} + return $fd } } else { @@ -365,7 +373,6 @@ set _appname {Git Gui} set _gitdir {} set _gitworktree {} set _isbare {} -set _gitexec {} set _githtmldir {} set _reponame {} set _shellpath {@@SHELL_PATH@@} @@ -430,20 +437,6 @@ proc gitdir {args} { return [eval [list file join $_gitdir] $args] } -proc gitexec {args} { - global _gitexec - if {$_gitexec eq {}} { - if {[catch {set _gitexec [git --exec-path]} err]} { - error "Git not installed?\n\n$err" - } - set _gitexec [file normalize $_gitexec] - } - if {$args eq {}} { - return $_gitexec - } - return [eval [list file join $_gitexec] $args] -} - proc githtmldir {args} { global _githtmldir if {$_githtmldir eq {}} { @@ -576,56 +569,6 @@ proc _trace_exec {cmd} { #'" fix poor old emacs font-lock mode -proc _git_cmd {name} { - global _git_cmd_path - - if {[catch {set v $_git_cmd_path($name)}]} { - switch -- $name { - version - - --version - - --exec-path { return [list $::_git $name] } - } - - set p [gitexec git-$name$::_search_exe] - if {[file exists $p]} { - set v [list $p] - } elseif {[is_Windows] && [file exists [gitexec git-$name]]} { - # Try to determine what sort of magic will make - # git-$name go and do its thing, because native - # Tcl on Windows doesn't know it. - # - set p [gitexec git-$name] - set f [safe_open_file $p r] - set s [gets $f] - close $f - - switch -glob -- [lindex $s 0] { - #!*sh { set i sh } - #!*perl { set i perl } - #!*python { set i python } - default { error "git-$name is not supported: $s" } - } - - upvar #0 _$i interp - if {![info exists interp]} { - set interp [_which $i] - } - if {$interp eq {}} { - error "git-$name requires $i (not in PATH)" - } - set v [concat [list $interp] [lrange $s 1 end] [list $p]] - } else { - # Assume it is builtin to git somehow and we - # aren't actually able to see a file for it. - # - set v [list $::_git $name] - } - set _git_cmd_path($name) $v - } - return $v -} - -# Run a shell command connected via pipes on stdout. # This is for use with textconv filters and uses sh -c "..." to allow it to # contain a command with arguments. We presume this # to be a shellscript that the configured shell (/bin/sh by default) knows @@ -636,30 +579,13 @@ proc open_cmd_pipe {cmd path} { return [open |$run r] } -proc _lappend_nice {cmd_var} { - global _nice - upvar $cmd_var cmd - - if {![info exists _nice]} { - set _nice [_which nice] - if {[catch {safe_exec [list $_nice git version]}]} { - set _nice {} - } elseif {[is_Windows] && [file dirname $_nice] ne [file dirname $::_git]} { - set _nice {} - } - } - if {$_nice ne {}} { - lappend cmd $_nice - } -} - proc git {args} { git_redir $args {} } proc git_redir {cmd redir} { set fd [git_read $cmd $redir] - fconfigure $fd -translation binary -encoding utf-8 + fconfigure $fd -encoding utf-8 set result [string trimright [read $fd] "\n"] close $fd if {$::_trace} { @@ -676,35 +602,33 @@ proc safe_open_command {cmd {redir {}}} { } err]} { error $err } - fconfigure $fd -eofchar {} return $fd } proc git_read {cmd {redir {}}} { - set cmdp [_git_cmd [lindex $cmd 0]] - set cmd [lrange $cmd 1 end] + global _git + set cmdp [concat [list $_git] $cmd] - return [safe_open_command [concat $cmdp $cmd] $redir] + return [safe_open_command $cmdp $redir] } -proc git_read_nice {cmd} { - set opt [list] - - _lappend_nice opt - - set cmdp [_git_cmd [lindex $cmd 0]] - set cmd [lrange $cmd 1 end] +set _nice [list [_which nice]] +if {[catch {safe_exec [list {*}$_nice git version]}]} { + set _nice {} +} - return [safe_open_command [concat $opt $cmdp $cmd]] +proc git_read_nice {cmd} { + set cmdp [list {*}$::_nice $::_git {*}$cmd] + return [safe_open_command $cmdp] } proc git_write {cmd} { + global _git set cmd [make_arglist_safe $cmd] - set cmdp [_git_cmd [lindex $cmd 0]] - set cmd [lrange $cmd 1 end] + set cmdp [concat [list $_git] $cmd] - _trace_exec [concat $cmdp $cmd] - return [open [concat [list | ] $cmdp $cmd] w] + _trace_exec $cmdp + return [open [concat [list | ] $cmdp] w] } proc githook_read {hook_name args} { @@ -744,27 +668,8 @@ proc sq {value} { proc load_current_branch {} { global current_branch is_detached - set fd [safe_open_file [gitdir HEAD] r] - fconfigure $fd -translation binary -encoding utf-8 - if {[gets $fd ref] < 1} { - set ref {} - } - close $fd - - set pfx {ref: refs/heads/} - set len [string length $pfx] - if {[string equal -length $len $pfx $ref]} { - # We're on a branch. It might not exist. But - # HEAD looks good enough to be a branch. - # - set current_branch [string range $ref $len end] - set is_detached 0 - } else { - # Assume this is a detached head. - # - set current_branch HEAD - set is_detached 1 - } + set current_branch [git branch --show-current] + set is_detached [expr [string length $current_branch] == 0] } auto_load tk_optionMenu @@ -914,18 +819,9 @@ proc apply_config {} { font configure ${font}italic -slant italic } - global use_ttk NS - set use_ttk 0 - set NS {} - if {$repo_config(gui.usettk)} { - set use_ttk [package vsatisfies [package provide Tk] 8.5] - if {$use_ttk} { - set NS ttk - bind [winfo class .] <<ThemeChanged>> [list InitTheme] - pave_toplevel . - color::sync_with_theme - } - } + bind [winfo class .] <<ThemeChanged>> [list InitTheme] + pave_toplevel . + color::sync_with_theme global comment_string set comment_string [get_config core.commentstring] @@ -992,6 +888,8 @@ if {$_git eq {}} { ## ## version check +set MIN_GIT_VERSION 2.36 + if {[catch {set _git_version [git --version]} err]} { catch {wm withdraw .} tk_messageBox \ @@ -1002,9 +900,10 @@ if {[catch {set _git_version [git --version]} err]} { $err -[appname] requires Git 1.5.0 or later." +[appname] requires Git $MIN_GIT_VERSION or later." exit 1 } + if {![regsub {^git version } $_git_version {} _git_version]} { catch {wm withdraw .} tk_messageBox \ @@ -1029,85 +928,21 @@ proc get_trimmed_version {s} { set _real_git_version $_git_version set _git_version [get_trimmed_version $_git_version] -if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} { - catch {wm withdraw .} - if {[tk_messageBox \ - -icon warning \ - -type yesno \ - -default no \ - -title "[appname]: warning" \ - -message [mc "Git version cannot be determined. - -%s claims it is version '%s'. - -%s requires at least Git 1.5.0 or later. - -Assume '%s' is version 1.5.0? -" $_git $_real_git_version [appname] $_real_git_version]] eq {yes}} { - set _git_version 1.5.0 - } else { - exit 1 - } -} -unset _real_git_version - -proc git-version {args} { - global _git_version - - switch [llength $args] { - 0 { - return $_git_version - } - - 2 { - set op [lindex $args 0] - set vr [lindex $args 1] - set cm [package vcompare $_git_version $vr] - return [expr $cm $op 0] - } - - 4 { - set type [lindex $args 0] - set name [lindex $args 1] - set parm [lindex $args 2] - set body [lindex $args 3] - - if {($type ne {proc} && $type ne {method})} { - error "Invalid arguments to git-version" - } - if {[llength $body] < 2 || [lindex $body end-1] ne {default}} { - error "Last arm of $type $name must be default" - } - - foreach {op vr cb} [lrange $body 0 end-2] { - if {[git-version $op $vr]} { - return [uplevel [list $type $name $parm $cb]] - } - } - - return [uplevel [list $type $name $parm [lindex $body end]]] - } - - default { - error "git-version >= x" - } - - } -} +if {[catch {set vcheck [package vcompare $_git_version $MIN_GIT_VERSION]}] || + [expr $vcheck < 0] } { -if {[git-version < 1.5]} { + set msg1 [mc "Insufficient git version, require: "] + set msg2 [mc "git returned:"] + set message "$msg1 $MIN_GIT_VERSION\n$msg2 $_real_git_version" catch {wm withdraw .} tk_messageBox \ -icon error \ -type ok \ -title [mc "git-gui: fatal error"] \ - -message "[appname] requires Git 1.5.0 or later. - -You are using [git-version]: - -[git --version]" + -message $message exit 1 } +unset _real_git_version ###################################################################### ## @@ -1161,7 +996,7 @@ proc _parse_config {arr_name args} { [concat config \ $args \ --null --list]] - fconfigure $fd_rc -translation binary -encoding utf-8 + fconfigure $fd_rc -encoding utf-8 set buf [read $fd_rc] close $fd_rc } @@ -1270,12 +1105,18 @@ citool { ## ## execution environment -set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}] - # Suggest our implementation of askpass, if none is set +set argv0dir [file dirname [file normalize $::argv0]] if {![info exists env(SSH_ASKPASS)]} { - set env(SSH_ASKPASS) [gitexec git-gui--askpass] + set env(SSH_ASKPASS) [file join $argv0dir git-gui--askpass] +} +if {![info exists env(GIT_ASKPASS)]} { + set env(GIT_ASKPASS) [file join $argv0dir git-gui--askpass] } +if {![info exists env(GIT_ASK_YESNO)]} { + set env(GIT_ASK_YESNO) [file join $argv0dir git-gui--askyesno] +} +unset argv0dir ###################################################################### ## @@ -1295,9 +1136,23 @@ if {[catch { load_config 1 apply_config choose_repository::pick + if {![file isdirectory $_gitdir]} { + exit 1 + } set picked 1 } +# Use object format as hash algorithm (either "sha1" or "sha256") +set hashalgorithm [git rev-parse --show-object-format] +if {$hashalgorithm eq "sha1"} { + set hashlength 40 +} elseif {$hashalgorithm eq "sha256"} { + set hashlength 64 +} else { + puts stderr "Unknown hash algorithm: $hashalgorithm" + exit 1 +} + # we expand the _gitdir when it's just a single dot (i.e. when we're being # run from the .git dir itself) lest the routines to find the worktree # get confused @@ -1314,20 +1169,7 @@ if {![file isdirectory $_gitdir]} { load_config 0 apply_config -# v1.7.0 introduced --show-toplevel to return the canonical work-tree -if {[package vcompare $_git_version 1.7.0] >= 0} { - set _gitworktree [git rev-parse --show-toplevel] -} else { - # try to set work tree from environment, core.worktree or use - # cdup to obtain a relative path to the top of the worktree. If - # run from the top, the ./ prefix ensures normalize expands pwd. - if {[catch { set _gitworktree $env(GIT_WORK_TREE) }]} { - set _gitworktree [get_config core.worktree] - if {$_gitworktree eq ""} { - set _gitworktree [file normalize ./[git rev-parse --show-cdup]] - } - } -} +set _gitworktree [git rev-parse --show-toplevel] if {$_prefix ne {}} { if {$_gitworktree eq {}} { @@ -1391,8 +1233,8 @@ set is_conflict_diff 0 set last_revert {} set last_revert_enc {} -set nullid "0000000000000000000000000000000000000000" -set nullid2 "0000000000000000000000000000000000000001" +set nullid [string repeat 0 $hashlength] +set nullid2 "[string repeat 0 [expr $hashlength - 1]]1" ###################################################################### ## @@ -1553,18 +1395,7 @@ proc rescan_stage2 {fd after} { close $fd } - if {[package vcompare $::_git_version 1.6.3] >= 0} { - set ls_others [list --exclude-standard] - } else { - set ls_others [list --exclude-per-directory=.gitignore] - if {[have_info_exclude]} { - lappend ls_others "--exclude-from=[gitdir info exclude]" - } - set user_exclude [get_config core.excludesfile] - if {$user_exclude ne {} && [file readable $user_exclude]} { - lappend ls_others "--exclude-from=[file normalize $user_exclude]" - } - } + set ls_others [list --exclude-standard] set buf_rdi {} set buf_rdf {} @@ -1572,22 +1403,18 @@ proc rescan_stage2 {fd after} { set rescan_active 2 ui_status [mc "Scanning for modified files ..."] - if {[git-version >= "1.7.2"]} { - set fd_di [git_read [list diff-index --cached --ignore-submodules=dirty -z [PARENT]]] - } else { - set fd_di [git_read [list diff-index --cached -z [PARENT]]] - } + set fd_di [git_read [list diff-index --cached --ignore-submodules=dirty -z [PARENT]]] set fd_df [git_read [list diff-files -z]] - fconfigure $fd_di -blocking 0 -translation binary -encoding binary - fconfigure $fd_df -blocking 0 -translation binary -encoding binary + fconfigure $fd_di -blocking 0 -translation binary + fconfigure $fd_df -blocking 0 -translation binary fileevent $fd_di readable [list read_diff_index $fd_di $after] fileevent $fd_df readable [list read_diff_files $fd_df $after] if {[is_config_true gui.displayuntracked]} { set fd_lo [git_read [concat ls-files --others -z $ls_others]] - fconfigure $fd_lo -blocking 0 -translation binary -encoding binary + fconfigure $fd_lo -blocking 0 -translation binary fileevent $fd_lo readable [list read_ls_others $fd_lo $after] incr rescan_active } @@ -1601,7 +1428,6 @@ proc load_message {file {encoding {}}} { if {[catch {set fd [safe_open_file $f r]}]} { return 0 } - fconfigure $fd -eofchar {} if {$encoding ne {}} { fconfigure $fd -encoding $encoding } @@ -1658,7 +1484,7 @@ proc run_prepare_commit_msg_hook {} { ui_status [mc "Calling prepare-commit-msg hook..."] set pch_error {} - fconfigure $fd_ph -blocking 0 -translation binary -eofchar {} + fconfigure $fd_ph -blocking 0 -translation binary fileevent $fd_ph readable \ [list prepare_commit_msg_hook_wait $fd_ph] @@ -1704,7 +1530,7 @@ proc read_diff_index {fd after} { set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }] set p [string range $buf_rdi $z1 [expr {$z2 - 1}]] merge_state \ - [encoding convertfrom utf-8 $p] \ + [convertfrom utf-8 $p] \ [lindex $i 4]? \ [list [lindex $i 0] [lindex $i 2]] \ [list] @@ -1737,7 +1563,7 @@ proc read_diff_files {fd after} { set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }] set p [string range $buf_rdf $z1 [expr {$z2 - 1}]] merge_state \ - [encoding convertfrom utf-8 $p] \ + [convertfrom utf-8 $p] \ ?[lindex $i 4] \ [list] \ [list [lindex $i 0] [lindex $i 2]] @@ -1760,7 +1586,7 @@ proc read_ls_others {fd after} { set pck [split $buf_rlo "\0"] set buf_rlo [lindex $pck end] foreach p [lrange $pck 0 end-1] { - set p [encoding convertfrom utf-8 $p] + set p [convertfrom utf-8 $p] if {[string index $p end] eq {/}} { set p [string range $p 0 end-1] } @@ -1845,10 +1671,9 @@ proc short_path {path} { } set next_icon_id 0 -set null_sha1 [string repeat 0 40] proc merge_state {path new_state {head_info {}} {index_info {}}} { - global file_states next_icon_id null_sha1 + global file_states next_icon_id nullid set s0 [string index $new_state 0] set s1 [string index $new_state 1] @@ -1870,7 +1695,7 @@ proc merge_state {path new_state {head_info {}} {index_info {}}} { elseif {$s1 eq {_}} {set s1 _} if {$s0 eq {A} && $s1 eq {_} && $head_info eq {}} { - set head_info [list 0 $null_sha1] + set head_info [list 0 $nullid] } elseif {$s0 ne {_} && [string index $state 0] eq {_} && $head_info eq {}} { set head_info $index_info @@ -2323,7 +2148,7 @@ proc do_quit {{rc {1}}} { global ui_comm is_quitting repo_config commit_type global GITGUI_BCK_exists GITGUI_BCK_i global ui_comm_spell - global ret_code use_ttk + global ret_code if {$is_quitting} return set is_quitting 1 @@ -2381,13 +2206,8 @@ proc do_quit {{rc {1}}} { } set cfg_geometry [list] lappend cfg_geometry [wm geometry .] - if {$use_ttk} { - lappend cfg_geometry [.vpane sashpos 0] - lappend cfg_geometry [.vpane.files sashpos 0] - } else { - lappend cfg_geometry [lindex [.vpane sash coord 0] 0] - lappend cfg_geometry [lindex [.vpane.files sash coord 0] 1] - } + lappend cfg_geometry [.vpane sashpos 0] + lappend cfg_geometry [.vpane.files sashpos 0] if {[catch {set rc_geometry $repo_config(gui.geometry)}]} { set rc_geometry {} } @@ -3203,7 +3023,7 @@ blame { if {$head eq {}} { load_current_branch } else { - if {[regexp {^[0-9a-f]{1,39}$} $head]} { + if {[regexp [string map "@@ [expr $hashlength - 1]" {^[0-9a-f]{1,@@}$}] $head]} { if {[catch { set head [git rev-parse --verify $head] } err]} { @@ -3269,13 +3089,12 @@ default { # -- Branch Control # -${NS}::frame .branch -if {!$use_ttk} {.branch configure -borderwidth 1 -relief sunken} -${NS}::label .branch.l1 \ +ttk::frame .branch +ttk::label .branch.l1 \ -text [mc "Current Branch:"] \ -anchor w \ -justify left -${NS}::label .branch.cb \ +ttk::label .branch.cb \ -textvariable current_branch \ -anchor w \ -justify left @@ -3285,13 +3104,9 @@ pack .branch -side top -fill x # -- Main Window Layout # -${NS}::panedwindow .vpane -orient horizontal -${NS}::panedwindow .vpane.files -orient vertical -if {$use_ttk} { - .vpane add .vpane.files -} else { - .vpane add .vpane.files -sticky nsew -height 100 -width 200 -} +ttk::panedwindow .vpane -orient horizontal +ttk::panedwindow .vpane.files -orient vertical +.vpane add .vpane.files pack .vpane -anchor n -side top -fill both -expand 1 # -- Working Directory File List @@ -3308,8 +3123,8 @@ ttext $ui_workdir \ -xscrollcommand {.vpane.files.workdir.sx set} \ -yscrollcommand {.vpane.files.workdir.sy set} \ -state disabled -${NS}::scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview] -${NS}::scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview] +ttk::scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview] +ttk::scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview] pack .vpane.files.workdir.title -side top -fill x pack .vpane.files.workdir.sx -side bottom -fill x pack .vpane.files.workdir.sy -side right -fill y @@ -3330,8 +3145,8 @@ ttext $ui_index \ -xscrollcommand {.vpane.files.index.sx set} \ -yscrollcommand {.vpane.files.index.sy set} \ -state disabled -${NS}::scrollbar .vpane.files.index.sx -orient h -command [list $ui_index xview] -${NS}::scrollbar .vpane.files.index.sy -orient v -command [list $ui_index yview] +ttk::scrollbar .vpane.files.index.sx -orient h -command [list $ui_index xview] +ttk::scrollbar .vpane.files.index.sy -orient v -command [list $ui_index yview] pack .vpane.files.index.title -side top -fill x pack .vpane.files.index.sx -side bottom -fill x pack .vpane.files.index.sy -side right -fill y @@ -3341,10 +3156,6 @@ pack $ui_index -side left -fill both -expand 1 # .vpane.files add .vpane.files.workdir .vpane.files add .vpane.files.index -if {!$use_ttk} { - .vpane.files paneconfigure .vpane.files.workdir -sticky news - .vpane.files paneconfigure .vpane.files.index -sticky news -} proc set_selection_colors {w has_focus} { foreach tag [list in_diff in_sel] { @@ -3365,78 +3176,63 @@ unset i # -- Diff and Commit Area # -if {$have_tk85} { - ${NS}::panedwindow .vpane.lower -orient vertical - ${NS}::frame .vpane.lower.commarea - ${NS}::frame .vpane.lower.diff -relief sunken -borderwidth 1 -height 500 - .vpane.lower add .vpane.lower.diff - .vpane.lower add .vpane.lower.commarea - .vpane add .vpane.lower - if {$use_ttk} { - .vpane.lower pane .vpane.lower.diff -weight 1 - .vpane.lower pane .vpane.lower.commarea -weight 0 - } else { - .vpane.lower paneconfigure .vpane.lower.diff -stretch always - .vpane.lower paneconfigure .vpane.lower.commarea -stretch never - } -} else { - frame .vpane.lower -height 300 -width 400 - frame .vpane.lower.commarea - frame .vpane.lower.diff -relief sunken -borderwidth 1 - pack .vpane.lower.diff -fill both -expand 1 - pack .vpane.lower.commarea -side bottom -fill x - .vpane add .vpane.lower - .vpane paneconfigure .vpane.lower -sticky nsew -} +ttk::panedwindow .vpane.lower -orient vertical +ttk::frame .vpane.lower.commarea +ttk::frame .vpane.lower.diff -relief sunken -borderwidth 1 -height 500 +.vpane.lower add .vpane.lower.diff +.vpane.lower add .vpane.lower.commarea +.vpane add .vpane.lower +.vpane.lower pane .vpane.lower.diff -weight 1 +.vpane.lower pane .vpane.lower.commarea -weight 0 # -- Commit Area Buttons # -${NS}::frame .vpane.lower.commarea.buttons -${NS}::label .vpane.lower.commarea.buttons.l -text {} \ +ttk::frame .vpane.lower.commarea.buttons +ttk::label .vpane.lower.commarea.buttons.l -text {} \ -anchor w \ -justify left pack .vpane.lower.commarea.buttons.l -side top -fill x pack .vpane.lower.commarea.buttons -side left -fill y -${NS}::button .vpane.lower.commarea.buttons.rescan -text [mc Rescan] \ +ttk::button .vpane.lower.commarea.buttons.rescan -text [mc Rescan] \ -command ui_do_rescan pack .vpane.lower.commarea.buttons.rescan -side top -fill x lappend disable_on_lock \ {.vpane.lower.commarea.buttons.rescan conf -state} -${NS}::button .vpane.lower.commarea.buttons.incall -text [mc "Stage Changed"] \ +ttk::button .vpane.lower.commarea.buttons.incall -text [mc "Stage Changed"] \ -command do_add_all pack .vpane.lower.commarea.buttons.incall -side top -fill x lappend disable_on_lock \ {.vpane.lower.commarea.buttons.incall conf -state} if {![is_enabled nocommitmsg]} { - ${NS}::button .vpane.lower.commarea.buttons.signoff -text [mc "Sign Off"] \ + ttk::button .vpane.lower.commarea.buttons.signoff -text [mc "Sign Off"] \ -command do_signoff pack .vpane.lower.commarea.buttons.signoff -side top -fill x } -${NS}::button .vpane.lower.commarea.buttons.commit -text [commit_btn_caption] \ +ttk::button .vpane.lower.commarea.buttons.commit -text [commit_btn_caption] \ -command do_commit pack .vpane.lower.commarea.buttons.commit -side top -fill x lappend disable_on_lock \ {.vpane.lower.commarea.buttons.commit conf -state} if {![is_enabled nocommit]} { - ${NS}::button .vpane.lower.commarea.buttons.push -text [mc Push] \ + ttk::button .vpane.lower.commarea.buttons.push -text [mc Push] \ -command do_push_anywhere pack .vpane.lower.commarea.buttons.push -side top -fill x } # -- Commit Message Buffer # -${NS}::frame .vpane.lower.commarea.buffer -${NS}::frame .vpane.lower.commarea.buffer.header +ttk::frame .vpane.lower.commarea.buffer +ttk::frame .vpane.lower.commarea.buffer.header set ui_comm .vpane.lower.commarea.buffer.frame.t set ui_coml .vpane.lower.commarea.buffer.header.l if {![is_enabled nocommit]} { - ${NS}::checkbutton .vpane.lower.commarea.buffer.header.amend \ + ttk::checkbutton .vpane.lower.commarea.buffer.header.amend \ -text [mc "Amend Last Commit"] \ -variable commit_type_is_amend \ -command do_select_commit_type @@ -3444,7 +3240,7 @@ if {![is_enabled nocommit]} { [list .vpane.lower.commarea.buffer.header.amend conf -state] } -${NS}::label $ui_coml \ +ttk::label $ui_coml \ -anchor w \ -justify left proc trace_commit_type {varname args} { @@ -3479,10 +3275,10 @@ ttext $ui_comm \ -font font_diff \ -xscrollcommand {.vpane.lower.commarea.buffer.frame.sbx set} \ -yscrollcommand {.vpane.lower.commarea.buffer.frame.sby set} -${NS}::scrollbar .vpane.lower.commarea.buffer.frame.sbx \ +ttk::scrollbar .vpane.lower.commarea.buffer.frame.sbx \ -orient horizontal \ -command [list $ui_comm xview] -${NS}::scrollbar .vpane.lower.commarea.buffer.frame.sby \ +ttk::scrollbar .vpane.lower.commarea.buffer.frame.sby \ -orient vertical \ -command [list $ui_comm yview] @@ -3605,9 +3401,9 @@ ttext $ui_diff \ -yscrollcommand {.vpane.lower.diff.body.sby set} \ -state disabled catch {$ui_diff configure -tabstyle wordprocessor} -${NS}::scrollbar .vpane.lower.diff.body.sbx -orient horizontal \ +ttk::scrollbar .vpane.lower.diff.body.sbx -orient horizontal \ -command [list $ui_diff xview] -${NS}::scrollbar .vpane.lower.diff.body.sby -orient vertical \ +ttk::scrollbar .vpane.lower.diff.body.sby -orient vertical \ -command [list $ui_diff yview] pack .vpane.lower.diff.body.sbx -side bottom -fill x pack .vpane.lower.diff.body.sby -side right -fill y @@ -3908,29 +3704,14 @@ proc on_ttk_pane_mapped {w pane pos} { bind $w <Map> {} after 0 [list after idle [list $w sashpos $pane $pos]] } -proc on_tk_pane_mapped {w pane x y} { - bind $w <Map> {} - after 0 [list after idle [list $w sash place $pane $x $y]] -} proc on_application_mapped {} { - global repo_config use_ttk + global repo_config bind . <Map> {} set gm $repo_config(gui.geometry) - if {$use_ttk} { - bind .vpane <Map> \ - [list on_ttk_pane_mapped %W 0 [lindex $gm 1]] - bind .vpane.files <Map> \ - [list on_ttk_pane_mapped %W 0 [lindex $gm 2]] - } else { - bind .vpane <Map> \ - [list on_tk_pane_mapped %W 0 \ - [lindex $gm 1] \ - [lindex [.vpane sash coord 0] 1]] - bind .vpane.files <Map> \ - [list on_tk_pane_mapped %W 0 \ - [lindex [.vpane.files sash coord 0] 0] \ - [lindex $gm 2]] - } + bind .vpane <Map> \ + [list on_ttk_pane_mapped %W 0 [lindex $gm 1]] + bind .vpane.files <Map> \ + [list on_ttk_pane_mapped %W 0 [lindex $gm 2]] wm geometry . [lindex $gm 0] } if {[info exists repo_config(gui.geometry)]} { diff --git a/git-gui/lib/about.tcl b/git-gui/lib/about.tcl index cfa50fca87..122ebfb71d 100644 --- a/git-gui/lib/about.tcl +++ b/git-gui/lib/about.tcl @@ -4,19 +4,19 @@ proc do_about {} { global appvers copyright oguilib global tcl_patchLevel tk_patchLevel - global ui_comm_spell NS use_ttk + global ui_comm_spell set w .about_dialog Dialog $w wm geometry $w "+[winfo rootx .]+[winfo rooty .]" pack [git_logo $w.git_logo] -side left -fill y -padx 10 -pady 10 - ${NS}::label $w.header -text [mc "About %s" [appname]] \ + ttk::label $w.header -text [mc "About %s" [appname]] \ -font font_uibold -anchor center pack $w.header -side top -fill x - ${NS}::frame $w.buttons - ${NS}::button $w.buttons.close -text {Close} \ + ttk::frame $w.buttons + ttk::button $w.buttons.close -text {Close} \ -default active \ -command [list destroy $w] pack $w.buttons.close -side right @@ -44,7 +44,7 @@ proc do_about {} { set d {} append d "git wrapper: $::_git\n" - append d "git exec dir: [gitexec]\n" + append d "git exec dir: [git --exec-path]\n" append d "git-gui lib: $oguilib" paddedlabel $w.vers -text $v diff --git a/git-gui/lib/blame.tcl b/git-gui/lib/blame.tcl index d6fd8bea91..4477b84eae 100644 --- a/git-gui/lib/blame.tcl +++ b/git-gui/lib/blame.tcl @@ -63,7 +63,7 @@ field tooltip_timer {} ; # Current timer event for our tooltip field tooltip_commit {} ; # Commit(s) in tooltip constructor new {i_commit i_path i_jump} { - global cursor_ptr M1B M1T have_tk85 use_ttk NS + global cursor_ptr M1B M1T variable active_color variable group_colors @@ -203,18 +203,17 @@ constructor new {i_commit i_path i_jump} { -width 80 \ -xscrollcommand [list $w.file_pane.out.sbx set] \ -font font_diff - if {$have_tk85} { $w_file configure -inactiveselectbackground darkblue - } + $w_file tag conf found \ -background yellow set w_columns [list $w_amov $w_asim $w_line $w_file] - ${NS}::scrollbar $w.file_pane.out.sbx \ + ttk::scrollbar $w.file_pane.out.sbx \ -orient h \ -command [list $w_file xview] - ${NS}::scrollbar $w.file_pane.out.sby \ + ttk::scrollbar $w.file_pane.out.sby \ -orient v \ -command [list scrollbar2many $w_columns yview] eval grid $w_columns $w.file_pane.out.sby -sticky nsew @@ -264,10 +263,10 @@ constructor new {i_commit i_path i_jump} { -background $active_color \ -font font_ui $w_cviewer tag raise sel - ${NS}::scrollbar $w.file_pane.cm.sbx \ + ttk::scrollbar $w.file_pane.cm.sbx \ -orient h \ -command [list $w_cviewer xview] - ${NS}::scrollbar $w.file_pane.cm.sby \ + ttk::scrollbar $w.file_pane.cm.sby \ -orient v \ -command [list $w_cviewer yview] pack $w.file_pane.cm.sby -side right -fill y @@ -426,6 +425,7 @@ method _kill {} { method _load {jump} { variable group_colors + global hashlength _hide_tooltip $this @@ -436,7 +436,7 @@ method _load {jump} { $i conf -state normal $i delete 0.0 end foreach g [$i tag names] { - if {[regexp {^g[0-9a-f]{40}$} $g]} { + if {[regexp [string map "@@ $hashlength" {^g[0-9a-f]{@@}$}] $g]} { $i tag delete $g } } @@ -470,7 +470,7 @@ method _load {jump} { $w_path conf -text [escape_path $path] set do_textconv 0 - if {![is_config_false gui.textconv] && [git-version >= 1.7.2]} { + if {![is_config_false gui.textconv]} { set filter [gitattr $path diff set] set textconv [get_config [join [list diff $filter textconv] .]] if {$filter ne {set} && $textconv ne {}} { @@ -483,7 +483,6 @@ method _load {jump} { } else { set fd [safe_open_file $path r] } - fconfigure $fd -eofchar {} } else { if {$do_textconv ne 0} { set fd [git_read [list cat-file --textconv "$commit:$path"]] @@ -493,13 +492,14 @@ method _load {jump} { } fconfigure $fd \ -blocking 0 \ - -translation lf \ -encoding [get_path_encoding $path] fileevent $fd readable [cb _read_file $fd $jump] set current_fd $fd } method _history_menu {} { + global hashlength + set m $w.backmenu if {[winfo exists $m]} { $m delete 0 end @@ -513,7 +513,7 @@ method _history_menu {} { set c [lindex $e 0] set f [lindex $e 1] - if {[regexp {^[0-9a-f]{40}$} $c]} { + if {[regexp [string map "@@ $hashlength" {^[0-9a-f]{@@}$}] $c]} { set t [string range $c 0 8]... } elseif {$c eq {}} { set t {Working Directory} @@ -618,7 +618,7 @@ method _exec_blame {cur_w cur_d options cur_s} { lappend options -- $path set fd [git_read_nice [concat blame $options]] - fconfigure $fd -blocking 0 -translation lf -encoding utf-8 + fconfigure $fd -blocking 0 -encoding utf-8 fileevent $fd readable [cb _read_blame $fd $cur_w $cur_d] set current_fd $fd set blame_lines 0 @@ -627,6 +627,7 @@ method _exec_blame {cur_w cur_d options cur_s} { method _read_blame {fd cur_w cur_d} { upvar #0 $cur_d line_data variable group_colors + global hashlength nullid if {$fd ne $current_fd} { catch {close $fd} @@ -635,7 +636,7 @@ method _read_blame {fd cur_w cur_d} { $cur_w conf -state normal while {[gets $fd line] >= 0} { - if {[regexp {^([a-z0-9]{40}) (\d+) (\d+) (\d+)$} $line line \ + if {[regexp [string map "@@ $hashlength" {^([a-z0-9]{@@}) (\d+) (\d+) (\d+)$}] $line line \ cmit original_line final_line line_count]} { set r_commit $cmit set r_orig_line $original_line @@ -648,7 +649,7 @@ method _read_blame {fd cur_w cur_d} { set oln $r_orig_line set cmit $r_commit - if {[regexp {^0{40}$} $cmit]} { + if {$cmit eq $nullid} { set commit_abbr work set commit_type curr_commit } elseif {$cmit eq $commit} { @@ -807,9 +808,7 @@ method _read_blame {fd cur_w cur_d} { # thorough copy search; insert before the threshold set original_options [linsert $original_options 0 -C] } - if {[git-version >= 1.5.3]} { - lappend original_options -w ; # ignore indentation changes - } + lappend original_options -w ; # ignore indentation changes _exec_blame $this $w_amov @amov_data \ $original_options \ @@ -857,9 +856,7 @@ method _fullcopyblame {} { set threshold [get_config gui.copyblamethreshold] set original_options [list -C -C "-C$threshold"] - if {[git-version >= 1.5.3]} { - lappend original_options -w ; # ignore indentation changes - } + lappend original_options -w ; # ignore indentation changes # Find the line range set pos @$::cursorX,$::cursorY @@ -987,7 +984,7 @@ method _showcommit {cur_w lno} { set msg {} catch { set fd [git_read [list cat-file commit $cmit]] - fconfigure $fd -encoding binary -translation lf + fconfigure $fd -encoding iso8859-1 # By default commits are assumed to be in utf-8 set enc utf-8 while {[gets $fd line] > 0} { @@ -1000,7 +997,7 @@ method _showcommit {cur_w lno} { set enc [tcl_encoding $enc] if {$enc ne {}} { - set msg [encoding convertfrom $enc $msg] + set msg [convertfrom $enc $msg] } set msg [string trim $msg] } @@ -1144,7 +1141,6 @@ method _blameparent {} { fconfigure $fd \ -blocking 0 \ - -encoding binary \ -translation binary fileevent $fd readable [cb _read_diff_load_commit \ $fd $cparent $new_path $r_orig_line] @@ -1298,7 +1294,7 @@ method _open_tooltip {cur_w} { # On MacOS raising a window causes it to acquire focus. # Tk 8.5 on MacOS seems to properly support wm transient, # so we can safely counter the effect there. - if {$::have_tk85 && [is_MacOSX]} { + if {[is_MacOSX]} { update if {$w eq {}} { raise . diff --git a/git-gui/lib/branch.tcl b/git-gui/lib/branch.tcl index 39e0f2dc98..97c9ec1c00 100644 --- a/git-gui/lib/branch.tcl +++ b/git-gui/lib/branch.tcl @@ -8,7 +8,7 @@ proc load_all_heads {} { set rh_len [expr {[string length $rh] + 1}] set all_heads [list] set fd [git_read [list for-each-ref --format=%(refname) $rh]] - fconfigure $fd -translation binary -encoding utf-8 + fconfigure $fd -encoding utf-8 while {[gets $fd line] > 0} { if {!$some_heads_tracking || ![is_tracking_branch $line]} { lappend all_heads [string range $line $rh_len end] @@ -25,7 +25,7 @@ proc load_all_tags {} { --sort=-taggerdate \ --format=%(refname) \ refs/tags]] - fconfigure $fd -translation binary -encoding utf-8 + fconfigure $fd -encoding utf-8 while {[gets $fd line] > 0} { if {![regsub ^refs/tags/ $line {} name]} continue lappend all_tags $name diff --git a/git-gui/lib/branch_checkout.tcl b/git-gui/lib/branch_checkout.tcl index d06037decc..1e6b757b35 100644 --- a/git-gui/lib/branch_checkout.tcl +++ b/git-gui/lib/branch_checkout.tcl @@ -10,7 +10,6 @@ field opt_fetch 1; # refetch tracking branch if used? field opt_detach 0; # force a detached head case? constructor dialog {} { - global use_ttk NS make_dialog top w wm withdraw $w wm title $top [mc "%s (%s): Checkout Branch" [appname] [reponame]] @@ -18,16 +17,16 @@ constructor dialog {} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } - ${NS}::label $w.header -text [mc "Checkout Branch"] \ + ttk::label $w.header -text [mc "Checkout Branch"] \ -font font_uibold -anchor center pack $w.header -side top -fill x - ${NS}::frame $w.buttons - ${NS}::button $w.buttons.create -text [mc Checkout] \ + ttk::frame $w.buttons + ttk::button $w.buttons.create -text [mc Checkout] \ -default active \ -command [cb _checkout] pack $w.buttons.create -side right - ${NS}::button $w.buttons.cancel -text [mc Cancel] \ + ttk::button $w.buttons.cancel -text [mc Cancel] \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 @@ -36,14 +35,14 @@ constructor dialog {} { $w_rev bind_listbox <Double-Button-1> [cb _checkout] pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5 - ${NS}::labelframe $w.options -text [mc Options] + ttk::labelframe $w.options -text [mc Options] - ${NS}::checkbutton $w.options.fetch \ + ttk::checkbutton $w.options.fetch \ -text [mc "Fetch Tracking Branch"] \ -variable @opt_fetch pack $w.options.fetch -anchor nw - ${NS}::checkbutton $w.options.detach \ + ttk::checkbutton $w.options.detach \ -text [mc "Detach From Local Branch"] \ -variable @opt_detach pack $w.options.detach -anchor nw diff --git a/git-gui/lib/branch_create.tcl b/git-gui/lib/branch_create.tcl index ba367d551d..9fded28b5c 100644 --- a/git-gui/lib/branch_create.tcl +++ b/git-gui/lib/branch_create.tcl @@ -16,7 +16,7 @@ field opt_fetch 1; # refetch tracking branch if used? field reset_ok 0; # did the user agree to reset? constructor dialog {} { - global repo_config use_ttk NS + global repo_config make_dialog top w wm withdraw $w @@ -25,39 +25,37 @@ constructor dialog {} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } - ${NS}::label $w.header -text [mc "Create New Branch"] \ + ttk::label $w.header -text [mc "Create New Branch"] \ -font font_uibold -anchor center pack $w.header -side top -fill x - ${NS}::frame $w.buttons - ${NS}::button $w.buttons.create -text [mc Create] \ + ttk::frame $w.buttons + ttk::button $w.buttons.create -text [mc Create] \ -default active \ -command [cb _create] pack $w.buttons.create -side right - ${NS}::button $w.buttons.cancel -text [mc Cancel] \ + ttk::button $w.buttons.cancel -text [mc Cancel] \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - ${NS}::labelframe $w.desc -text [mc "Branch Name"] - ${NS}::radiobutton $w.desc.name_r \ + ttk::labelframe $w.desc -text [mc "Branch Name"] + ttk::radiobutton $w.desc.name_r \ -text [mc "Name:"] \ -value user \ -variable @name_type - if {!$use_ttk} {$w.desc.name_r configure -anchor w} set w_name $w.desc.name_t - ${NS}::entry $w_name \ + ttk::entry $w_name \ -width 40 \ -textvariable @name \ -validate key \ -validatecommand [cb _validate %d %S] grid $w.desc.name_r $w_name -sticky we -padx {0 5} - ${NS}::radiobutton $w.desc.match_r \ + ttk::radiobutton $w.desc.match_r \ -text [mc "Match Tracking Branch Name"] \ -value match \ -variable @name_type - if {!$use_ttk} {$w.desc.match_r configure -anchor w} grid $w.desc.match_r -sticky we -padx {0 5} -columnspan 2 grid columnconfigure $w.desc 1 -weight 1 @@ -66,34 +64,34 @@ constructor dialog {} { set w_rev [::choose_rev::new $w.rev [mc "Starting Revision"]] pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5 - ${NS}::labelframe $w.options -text [mc Options] + ttk::labelframe $w.options -text [mc Options] - ${NS}::frame $w.options.merge - ${NS}::label $w.options.merge.l -text [mc "Update Existing Branch:"] + ttk::frame $w.options.merge + ttk::label $w.options.merge.l -text [mc "Update Existing Branch:"] pack $w.options.merge.l -side left - ${NS}::radiobutton $w.options.merge.no \ + ttk::radiobutton $w.options.merge.no \ -text [mc No] \ -value none \ -variable @opt_merge pack $w.options.merge.no -side left - ${NS}::radiobutton $w.options.merge.ff \ + ttk::radiobutton $w.options.merge.ff \ -text [mc "Fast Forward Only"] \ -value ff \ -variable @opt_merge pack $w.options.merge.ff -side left - ${NS}::radiobutton $w.options.merge.reset \ + ttk::radiobutton $w.options.merge.reset \ -text [mc Reset] \ -value reset \ -variable @opt_merge pack $w.options.merge.reset -side left pack $w.options.merge -anchor nw - ${NS}::checkbutton $w.options.fetch \ + ttk::checkbutton $w.options.fetch \ -text [mc "Fetch Tracking Branch"] \ -variable @opt_fetch pack $w.options.fetch -anchor nw - ${NS}::checkbutton $w.options.checkout \ + ttk::checkbutton $w.options.checkout \ -text [mc "Checkout After Creation"] \ -variable @opt_checkout pack $w.options.checkout -anchor nw diff --git a/git-gui/lib/branch_delete.tcl b/git-gui/lib/branch_delete.tcl index a5051637bb..deac74a644 100644 --- a/git-gui/lib/branch_delete.tcl +++ b/git-gui/lib/branch_delete.tcl @@ -9,7 +9,7 @@ field w_check ; # revision picker for merge test field w_delete ; # delete button constructor dialog {} { - global current_branch use_ttk NS + global current_branch make_dialog top w wm withdraw $w @@ -18,25 +18,25 @@ constructor dialog {} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } - ${NS}::label $w.header -text [mc "Delete Local Branch"] \ + ttk::label $w.header -text [mc "Delete Local Branch"] \ -font font_uibold -anchor center pack $w.header -side top -fill x - ${NS}::frame $w.buttons + ttk::frame $w.buttons set w_delete $w.buttons.delete - ${NS}::button $w_delete \ + ttk::button $w_delete \ -text [mc Delete] \ -default active \ -state disabled \ -command [cb _delete] pack $w_delete -side right - ${NS}::button $w.buttons.cancel \ + ttk::button $w.buttons.cancel \ -text [mc Cancel] \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - ${NS}::labelframe $w.list -text [mc "Local Branches"] + ttk::labelframe $w.list -text [mc "Local Branches"] set w_heads $w.list.l slistbox $w_heads \ -height 10 \ diff --git a/git-gui/lib/branch_rename.tcl b/git-gui/lib/branch_rename.tcl index 3a2d79a9cc..7a3b39d6a3 100644 --- a/git-gui/lib/branch_rename.tcl +++ b/git-gui/lib/branch_rename.tcl @@ -8,7 +8,7 @@ field oldname field newname constructor dialog {} { - global current_branch use_ttk NS + global current_branch make_dialog top w wm withdraw $w @@ -20,31 +20,27 @@ constructor dialog {} { set oldname $current_branch set newname [get_config gui.newbranchtemplate] - ${NS}::label $w.header -text [mc "Rename Branch"]\ + ttk::label $w.header -text [mc "Rename Branch"]\ -font font_uibold -anchor center pack $w.header -side top -fill x - ${NS}::frame $w.buttons - ${NS}::button $w.buttons.rename -text [mc Rename] \ + ttk::frame $w.buttons + ttk::button $w.buttons.rename -text [mc Rename] \ -default active \ -command [cb _rename] pack $w.buttons.rename -side right - ${NS}::button $w.buttons.cancel -text [mc Cancel] \ + ttk::button $w.buttons.cancel -text [mc Cancel] \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - ${NS}::frame $w.rename - ${NS}::label $w.rename.oldname_l -text [mc "Branch:"] - if {$use_ttk} { - ttk::combobox $w.rename.oldname_m -textvariable @oldname \ - -values [load_all_heads] -state readonly - } else { - eval tk_optionMenu $w.rename.oldname_m @oldname [load_all_heads] - } + ttk::frame $w.rename + ttk::label $w.rename.oldname_l -text [mc "Branch:"] + ttk::combobox $w.rename.oldname_m -textvariable @oldname \ + -values [load_all_heads] -state readonly - ${NS}::label $w.rename.newname_l -text [mc "New Name:"] - ${NS}::entry $w.rename.newname_t \ + ttk::label $w.rename.newname_l -text [mc "New Name:"] + ttk::entry $w.rename.newname_t \ -width 40 \ -textvariable @newname \ -validate key \ diff --git a/git-gui/lib/browser.tcl b/git-gui/lib/browser.tcl index 6fc8d4d637..fe72de025e 100644 --- a/git-gui/lib/browser.tcl +++ b/git-gui/lib/browser.tcl @@ -21,7 +21,7 @@ field browser_busy 1 field ls_buf {}; # Buffered record output from ls-tree constructor new {commit {path {}}} { - global cursor_ptr M1B use_ttk NS + global cursor_ptr M1B make_dialog top w wm withdraw $top wm title $top [mc "%s (%s): File Browser" [appname] [reponame]] @@ -35,15 +35,14 @@ constructor new {commit {path {}}} { set browser_commit $commit set browser_path "$browser_commit:[escape_path $path]" - ${NS}::label $w.path \ + ttk::label $w.path \ -textvariable @browser_path \ -anchor w \ -justify left \ -font font_uibold - if {!$use_ttk} { $w.path configure -borderwidth 1 -relief sunken} pack $w.path -anchor w -side top -fill x - ${NS}::frame $w.list + ttk::frame $w.list set w_list $w.list.l text $w_list -background white -foreground black \ -borderwidth 0 \ @@ -55,18 +54,17 @@ constructor new {commit {path {}}} { -xscrollcommand [list $w.list.sbx set] \ -yscrollcommand [list $w.list.sby set] rmsel_tag $w_list - ${NS}::scrollbar $w.list.sbx -orient h -command [list $w_list xview] - ${NS}::scrollbar $w.list.sby -orient v -command [list $w_list yview] + ttk::scrollbar $w.list.sbx -orient h -command [list $w_list xview] + ttk::scrollbar $w.list.sby -orient v -command [list $w_list yview] pack $w.list.sbx -side bottom -fill x pack $w.list.sby -side right -fill y pack $w_list -side left -fill both -expand 1 pack $w.list -side top -fill both -expand 1 - ${NS}::label $w.status \ + ttk::label $w.status \ -textvariable @browser_status \ -anchor w \ -justify left - if {!$use_ttk} { $w.status configure -borderwidth 1 -relief sunken} pack $w.status -anchor w -side bottom -fill x bind $w_list <Button-1> "[cb _click 0 @%x,%y];break" @@ -197,7 +195,7 @@ method _ls {tree_id {name {}}} { $w conf -state disabled set fd [git_read [list ls-tree -z $tree_id]] - fconfigure $fd -blocking 0 -translation binary -encoding utf-8 + fconfigure $fd -blocking 0 -encoding utf-8 fileevent $fd readable [cb _read $fd] } @@ -269,7 +267,6 @@ field w ; # widget path field w_rev ; # mega-widget to pick the initial revision constructor dialog {} { - global use_ttk NS make_dialog top w wm withdraw $top wm title $top [mc "%s (%s): Browse Branch Files" [appname] [reponame]] @@ -278,18 +275,18 @@ constructor dialog {} { wm transient $top . } - ${NS}::label $w.header \ + ttk::label $w.header \ -text [mc "Browse Branch Files"] \ -font font_uibold \ -anchor center pack $w.header -side top -fill x - ${NS}::frame $w.buttons - ${NS}::button $w.buttons.browse -text [mc Browse] \ + ttk::frame $w.buttons + ttk::button $w.buttons.browse -text [mc Browse] \ -default active \ -command [cb _open] pack $w.buttons.browse -side right - ${NS}::button $w.buttons.cancel -text [mc Cancel] \ + ttk::button $w.buttons.cancel -text [mc Cancel] \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 diff --git a/git-gui/lib/checkout_op.tcl b/git-gui/lib/checkout_op.tcl index 87ed0b4858..449e89e2bc 100644 --- a/git-gui/lib/checkout_op.tcl +++ b/git-gui/lib/checkout_op.tcl @@ -151,7 +151,7 @@ method _finish_fetch {ok} { } method _update_ref {} { - global null_sha1 current_branch repo_config + global nullid current_branch repo_config set ref $new_ref set new $new_hash @@ -177,7 +177,7 @@ method _update_ref {} { } set reflog_msg "branch: Created from $new_expr" - set cur $null_sha1 + set cur $nullid if {($repo_config(branch.autosetupmerge) eq {true} || $repo_config(branch.autosetupmerge) eq {always}) @@ -462,7 +462,7 @@ If you wanted to be on a branch, create one now starting from 'This Detached Che if {$fd_ph ne {}} { global pch_error set pch_error {} - fconfigure $fd_ph -blocking 0 -translation binary -eofchar {} + fconfigure $fd_ph -blocking 0 -translation binary fileevent $fd_ph readable [cb _postcheckout_wait $fd_ph] } else { _update_repo_state $this diff --git a/git-gui/lib/choose_font.tcl b/git-gui/lib/choose_font.tcl index ebe50bd7d0..a90908a8ec 100644 --- a/git-gui/lib/choose_font.tcl +++ b/git-gui/lib/choose_font.tcl @@ -17,7 +17,6 @@ variable all_families [list] ; # All fonts known to Tk constructor pick {path title a_family a_size} { variable all_families - global use_ttk NS set v_family $a_family set v_size $a_size @@ -33,25 +32,25 @@ constructor pick {path title a_family a_size} { wm title $top "[appname] ([reponame]): $title" wm geometry $top "+[winfo rootx $path]+[winfo rooty $path]" - ${NS}::label $w.header -text $title -font font_uibold -anchor center + ttk::label $w.header -text $title -font font_uibold -anchor center pack $w.header -side top -fill x - ${NS}::frame $w.buttons - ${NS}::button $w.buttons.select \ + ttk::frame $w.buttons + ttk::button $w.buttons.select \ -text [mc Select] \ -default active \ -command [cb _select] - ${NS}::button $w.buttons.cancel \ + ttk::button $w.buttons.cancel \ -text [mc Cancel] \ -command [list destroy $w] pack $w.buttons.select -side right pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - ${NS}::frame $w.inner + ttk::frame $w.inner - ${NS}::frame $w.inner.family - ${NS}::label $w.inner.family.l \ + ttk::frame $w.inner.family + ttk::label $w.inner.family.l \ -text [mc "Font Family"] \ -anchor w set w_family $w.inner.family.v @@ -66,13 +65,13 @@ constructor pick {path title a_family a_size} { -height 10 \ -yscrollcommand [list $w.inner.family.sby set] rmsel_tag $w_family - ${NS}::scrollbar $w.inner.family.sby -command [list $w_family yview] + ttk::scrollbar $w.inner.family.sby -command [list $w_family yview] pack $w.inner.family.l -side top -fill x pack $w.inner.family.sby -side right -fill y pack $w_family -fill both -expand 1 - ${NS}::frame $w.inner.size - ${NS}::label $w.inner.size.l \ + ttk::frame $w.inner.size + ttk::label $w.inner.size.l \ -text [mc "Font Size"] \ -anchor w tspinbox $w.inner.size.v \ @@ -88,8 +87,8 @@ constructor pick {path title a_family a_size} { grid columnconfigure $w.inner 0 -weight 1 pack $w.inner -fill both -expand 1 -padx 5 -pady 5 - ${NS}::frame $w.example - ${NS}::label $w.example.l \ + ttk::frame $w.example + ttk::label $w.example.l \ -text [mc "Font Example"] \ -anchor w set w_example $w.example.t diff --git a/git-gui/lib/choose_repository.tcl b/git-gui/lib/choose_repository.tcl index 5b361cc424..7e1462a20c 100644 --- a/git-gui/lib/choose_repository.tcl +++ b/git-gui/lib/choose_repository.tcl @@ -10,22 +10,12 @@ field w_next ; # Next button field w_quit ; # Quit button field o_cons ; # Console object (if active) -# Status mega-widget instance during _do_clone2 (used by _copy_files and -# _link_files). Widget is destroyed before _do_clone2 calls -# _do_clone_checkout -field o_status - -# Operation displayed by status mega-widget during _do_clone_checkout => -# _readtree_wait => _postcheckout_wait => _do_clone_submodules => -# _do_validate_submodule_cloning. The status mega-widget is a different -# instance than that stored in $o_status in earlier operations. -field o_status_op - field w_types ; # List of type buttons in clone field w_recentlist ; # Listbox containing recent repositories field w_localpath ; # Entry widget bound to local_path field done 0 ; # Finished picking the repository? +field clone_ok false ; # clone succeeeded field local_path {} ; # Where this repository is locally field origin_url {} ; # Where we are cloning from field origin_name origin ; # What we shall call 'origin' @@ -35,7 +25,7 @@ field readtree_err ; # Error output from read-tree (if any) field sorted_recent ; # recent repositories (sorted) constructor pick {} { - global M1T M1B use_ttk NS + global M1T M1B if {[set maxrecent [get_config gui.maxrecentrepo]] eq {}} { set maxrecent 10 @@ -88,7 +78,7 @@ constructor pick {} { set w_body $w.body set opts $w_body.options - ${NS}::frame $w_body + ttk::frame $w_body text $opts \ -cursor $::cursor_ptr \ -relief flat \ @@ -158,8 +148,8 @@ constructor pick {} { set lenrecent $maxrecent } - ${NS}::label $w_body.space - ${NS}::label $w_body.recentlabel \ + ttk::label $w_body.space + ttk::label $w_body.recentlabel \ -anchor w \ -text [mc "Open Recent Repository:"] set w_recentlist $w_body.recentlist @@ -199,10 +189,10 @@ constructor pick {} { } pack $w_body -fill x -padx 10 -pady 10 - ${NS}::frame $w.buttons + ttk::frame $w.buttons set w_next $w.buttons.next set w_quit $w.buttons.quit - ${NS}::button $w_quit \ + ttk::button $w_quit \ -text [mc "Quit"] \ -command exit pack $w_quit -side right -padx 5 @@ -303,10 +293,9 @@ method _open_recent_path {p} { } method _next {action} { - global NS destroy $w_body if {![winfo exists $w_next]} { - ${NS}::button $w_next -default active + ttk::button $w_next -default active set pos -before if {[tk windowingsystem] eq "win32"} { set pos -after } pack $w_next -side right -padx 5 $pos $w_quit @@ -323,7 +312,7 @@ method _write_local_path {args} { } method _git_init {} { - if {[catch {file mkdir $local_path} err]} { + if {[catch {git init $local_path} err]} { error_popup [strcat \ [mc "Failed to create repository %s:" $local_path] \ "\n\n$err"] @@ -337,13 +326,6 @@ method _git_init {} { return 0 } - if {[catch {git init} err]} { - error_popup [strcat \ - [mc "Failed to create repository %s:" $local_path] \ - "\n\n$err"] - return 0 - } - _append_recentrepos [pwd] set ::_gitdir .git set ::_prefix {} @@ -360,44 +342,29 @@ proc _is_git {path {outdir_var ""}} { return 1 } -proc _objdir {path} { - set objdir [file join $path .git objects] - if {[file isdirectory $objdir]} { - return $objdir - } - - set objdir [file join $path objects] - if {[file isdirectory $objdir]} { - return $objdir - } - - return {} -} - ###################################################################### ## ## Create New Repository method _do_new {} { - global use_ttk NS $w_next conf \ -state disabled \ -command [cb _do_new2] \ -text [mc "Create"] - ${NS}::frame $w_body - ${NS}::label $w_body.h \ + ttk::frame $w_body + ttk::label $w_body.h \ -font font_uibold -anchor center \ -text [mc "Create New Repository"] pack $w_body.h -side top -fill x -pady 10 pack $w_body -fill x -padx 10 - ${NS}::frame $w_body.where - ${NS}::label $w_body.where.l -text [mc "Directory:"] - ${NS}::entry $w_body.where.t \ + ttk::frame $w_body.where + ttk::label $w_body.where.l -text [mc "Directory:"] + ttk::entry $w_body.where.t \ -textvariable @local_path \ -width 50 - ${NS}::button $w_body.where.b \ + ttk::button $w_body.where.b \ -text [mc "Browse"] \ -command [cb _new_local_path] set w_localpath $w_body.where.t @@ -463,56 +430,55 @@ proc _new_ok {p} { ## Clone Existing Repository method _do_clone {} { - global use_ttk NS $w_next conf \ -state disabled \ -command [cb _do_clone2] \ -text [mc "Clone"] - ${NS}::frame $w_body - ${NS}::label $w_body.h \ + ttk::frame $w_body + ttk::label $w_body.h \ -font font_uibold -anchor center \ -text [mc "Clone Existing Repository"] pack $w_body.h -side top -fill x -pady 10 pack $w_body -fill x -padx 10 set args $w_body.args - ${NS}::frame $w_body.args + ttk::frame $w_body.args pack $args -fill both - ${NS}::label $args.origin_l -text [mc "Source Location:"] - ${NS}::entry $args.origin_t \ + ttk::label $args.origin_l -text [mc "Source Location:"] + ttk::entry $args.origin_t \ -textvariable @origin_url \ -width 50 - ${NS}::button $args.origin_b \ + ttk::button $args.origin_b \ -text [mc "Browse"] \ -command [cb _open_origin] grid $args.origin_l $args.origin_t $args.origin_b -sticky ew - ${NS}::label $args.where_l -text [mc "Target Directory:"] - ${NS}::entry $args.where_t \ + ttk::label $args.where_l -text [mc "Target Directory:"] + ttk::entry $args.where_t \ -textvariable @local_path \ -width 50 - ${NS}::button $args.where_b \ + ttk::button $args.where_b \ -text [mc "Browse"] \ -command [cb _new_local_path] grid $args.where_l $args.where_t $args.where_b -sticky ew set w_localpath $args.where_t - ${NS}::label $args.type_l -text [mc "Clone Type:"] - ${NS}::frame $args.type_f + ttk::label $args.type_l -text [mc "Clone Type:"] + ttk::frame $args.type_f set w_types [list] - lappend w_types [${NS}::radiobutton $args.type_f.hardlink \ + lappend w_types [ttk::radiobutton $args.type_f.hardlink \ -state disabled \ -text [mc "Standard (Fast, Semi-Redundant, Hardlinks)"] \ -variable @clone_type \ -value hardlink] - lappend w_types [${NS}::radiobutton $args.type_f.full \ + lappend w_types [ttk::radiobutton $args.type_f.full \ -state disabled \ -text [mc "Full Copy (Slower, Redundant Backup)"] \ -variable @clone_type \ -value full] - lappend w_types [${NS}::radiobutton $args.type_f.shared \ + lappend w_types [ttk::radiobutton $args.type_f.shared \ -state disabled \ -text [mc "Shared (Fastest, Not Recommended, No Backup)"] \ -variable @clone_type \ @@ -520,7 +486,7 @@ method _do_clone {} { foreach r $w_types { pack $r -anchor w } - ${NS}::checkbutton $args.type_f.recursive \ + ttk::checkbutton $args.type_f.recursive \ -text [mc "Recursively clone submodules too"] \ -variable @recursive \ -onvalue true -offvalue false @@ -588,6 +554,25 @@ method _update_clone {args} { method _do_clone2 {} { if {[file isdirectory $origin_url]} { set origin_url [file normalize $origin_url] + if {$clone_type eq {hardlink}} { + # cannot use hardlinks if this is a linked worktree (.gitfile or git-new-workdir) + if {[git -C $origin_url rev-parse --is-inside-work-tree] == {true}} { + set islink 0 + set dotgit [file join $origin_url .git] + if {[file isfile $dotgit]} { + set islink 1 + } else { + set objdir [file join $dotgit objects] + if {[file exists $objdir] && [file type $objdir] == {link}} { + set islink 1 + } + } + if {$islink} { + info_popup [mc "Hardlinks are unavailable. Falling back to copying."] + set clone_type full + } + } + } } if {$clone_type eq {hardlink} && ![file isdirectory $origin_url]} { @@ -599,14 +584,6 @@ method _do_clone2 {} { return } - if {$clone_type eq {hardlink} || $clone_type eq {shared}} { - set objdir [_objdir $origin_url] - if {$objdir eq {}} { - error_popup [mc "Not a Git repository: %s" [file tail $origin_url]] - return - } - } - set giturl $origin_url if {[file exists $local_path]} { @@ -614,459 +591,86 @@ method _do_clone2 {} { return } - if {![_git_init $this]} return - set local_path [pwd] - - if {[catch { - git config remote.$origin_name.url $giturl - git config remote.$origin_name.fetch +refs/heads/*:refs/remotes/$origin_name/* - } err]} { - error_popup [strcat [mc "Failed to configure origin"] "\n\n$err"] - return + set clone_options {--progress} + if {$recursive} { + append clone_options { --recurse-submodules} } destroy $w_body $w_next switch -exact -- $clone_type { - hardlink { - set o_status [status_bar::two_line $w_body] - pack $w_body -fill x -padx 10 -pady 10 - - set status_op [$o_status start \ - [mc "Counting objects"] \ - [mc "buckets"]] - update - - if {[file exists [file join $objdir info alternates]]} { - set pwd [pwd] - if {[catch { - file mkdir [gitdir objects info] - set f_in [safe_open_file [file join $objdir info alternates] r] - set f_cp [safe_open_file [gitdir objects info alternates] w] - fconfigure $f_in -translation binary -encoding binary - fconfigure $f_cp -translation binary -encoding binary - cd $objdir - while {[gets $f_in line] >= 0} { - puts $f_cp [file normalize $line] - } - close $f_in - close $f_cp - cd $pwd - } err]} { - catch {cd $pwd} - _clone_failed $this [mc "Unable to copy objects/info/alternates: %s" $err] - $status_op stop - return - } + full { + append clone_options { --no-hardlinks --no-local} } - - set tolink [list] - set buckets [glob \ - -tails \ - -nocomplain \ - -directory [file join $objdir] ??] - set bcnt [expr {[llength $buckets] + 2}] - set bcur 1 - $status_op update $bcur $bcnt - update - - file mkdir [file join .git objects pack] - foreach i [glob -tails -nocomplain \ - -directory [file join $objdir pack] *] { - lappend tolink [file join pack $i] - } - $status_op update [incr bcur] $bcnt - update - - foreach i $buckets { - file mkdir [file join .git objects $i] - foreach j [glob -tails -nocomplain \ - -directory [file join $objdir $i] *] { - lappend tolink [file join $i $j] - } - $status_op update [incr bcur] $bcnt - update - } - $status_op stop - - if {$tolink eq {}} { - info_popup [strcat \ - [mc "Nothing to clone from %s." $origin_url] \ - "\n" \ - [mc "The 'master' branch has not been initialized."] \ - ] - destroy $w_body - set done 1 - return - } - - set i [lindex $tolink 0] - if {[catch { - file link -hard \ - [file join .git objects $i] \ - [file join $objdir $i] - } err]} { - info_popup [mc "Hardlinks are unavailable. Falling back to copying."] - set i [_copy_files $this $objdir $tolink] - } else { - set i [_link_files $this $objdir [lrange $tolink 1 end]] + shared { + append clone_options { --shared} } - if {!$i} return - - destroy $w_body - - set o_status {} } - full { + + if {[catch { set o_cons [console::embed \ $w_body \ [mc "Cloning from %s" $origin_url]] pack $w_body -fill both -expand 1 -padx 10 $o_cons exec \ - [list git fetch --no-tags -k $origin_name] \ - [cb _do_clone_tags] - } - shared { - set fd [safe_open_file [gitdir objects info alternates] w] - fconfigure $fd -translation binary - puts $fd $objdir - close $fd - } - } - - if {$clone_type eq {hardlink} || $clone_type eq {shared}} { - if {![_clone_refs $this]} return - set pwd [pwd] - if {[catch { - cd $origin_url - set HEAD [git rev-parse --verify HEAD^0] - } err]} { - _clone_failed $this [mc "Not a Git repository: %s" [file tail $origin_url]] - return 0 - } - cd $pwd - _do_clone_checkout $this $HEAD - } -} - -method _copy_files {objdir tocopy} { - set status_op [$o_status start \ - [mc "Copying objects"] \ - [mc "KiB"]] - set tot 0 - set cmp 0 - foreach p $tocopy { - incr tot [file size [file join $objdir $p]] - } - foreach p $tocopy { - if {[catch { - set f_in [safe_open_file [file join $objdir $p] r] - set f_cp [safe_open_file [file join .git objects $p] w] - fconfigure $f_in -translation binary -encoding binary - fconfigure $f_cp -translation binary -encoding binary - - while {![eof $f_in]} { - incr cmp [fcopy $f_in $f_cp -size 16384] - $status_op update \ - [expr {$cmp / 1024}] \ - [expr {$tot / 1024}] - update - } - - close $f_in - close $f_cp - } err]} { - _clone_failed $this [mc "Unable to copy object: %s" $err] - $status_op stop - return 0 - } - } - $status_op stop - return 1 -} - -method _link_files {objdir tolink} { - set total [llength $tolink] - set status_op [$o_status start \ - [mc "Linking objects"] \ - [mc "objects"]] - for {set i 0} {$i < $total} {} { - set p [lindex $tolink $i] - if {[catch { - file link -hard \ - [file join .git objects $p] \ - [file join $objdir $p] - } err]} { - _clone_failed $this [mc "Unable to hardlink object: %s" $err] - $status_op stop - return 0 - } - - incr i - if {$i % 5 == 0} { - $status_op update $i $total - update - } - } - $status_op stop - return 1 -} - -method _clone_refs {} { - set pwd [pwd] - if {[catch {cd $origin_url} err]} { - error_popup [mc "Not a Git repository: %s" [file tail $origin_url]] - return 0 - } - set fd_in [git_read [list for-each-ref \ - --tcl \ - {--format=list %(refname) %(objectname) %(*objectname)}]] - cd $pwd - - set fd [safe_open_file [gitdir packed-refs] w] - fconfigure $fd -translation binary - puts $fd "# pack-refs with: peeled" - while {[gets $fd_in line] >= 0} { - set line [eval $line] - set refn [lindex $line 0] - set robj [lindex $line 1] - set tobj [lindex $line 2] - - if {[regsub ^refs/heads/ $refn \ - "refs/remotes/$origin_name/" refn]} { - puts $fd "$robj $refn" - } elseif {[string match refs/tags/* $refn]} { - puts $fd "$robj $refn" - if {$tobj ne {}} { - puts $fd "^$tobj" - } - } - } - close $fd_in - close $fd - return 1 -} - -method _do_clone_tags {ok} { - if {$ok} { - $o_cons exec \ - [list git fetch --tags -k $origin_name] \ - [cb _do_clone_HEAD] - } else { - $o_cons done $ok - _clone_failed $this [mc "Cannot fetch branches and objects. See console output for details."] + [list git clone {*}$clone_options $origin_url $local_path] \ + [cb _do_clone2_done] + } err]} { + error_popup [strcat [mc "Clone failed."] "\n" $err] + return } -} -method _do_clone_HEAD {ok} { - if {$ok} { - $o_cons exec \ - [list git fetch $origin_name HEAD] \ - [cb _do_clone_full_end] - } else { - $o_cons done $ok - _clone_failed $this [mc "Cannot fetch tags. See console output for details."] + tkwait variable @done + if {!$clone_ok} { + error_popup [mc "Clone failed."] + return } } -method _do_clone_full_end {ok} { +method _do_clone2_done {ok} { $o_cons done $ok - if {$ok} { - destroy $w_body - - set HEAD {} - if {[file exists [gitdir FETCH_HEAD]]} { - set fd [safe_open_file [gitdir FETCH_HEAD] r] - while {[gets $fd line] >= 0} { - if {[regexp "^(.{40})\t\t" $line line HEAD]} { - break - } - } - close $fd - } - - catch {git pack-refs} - _do_clone_checkout $this $HEAD - } else { - _clone_failed $this [mc "Cannot determine HEAD. See console output for details."] - } -} - -method _clone_failed {{why {}}} { - if {[catch {file delete -force $local_path} err]} { - set why [strcat \ - $why \ - "\n\n" \ - [mc "Unable to cleanup %s" $local_path] \ - "\n\n" \ - $err] - } - if {$why ne {}} { - update - error_popup [strcat [mc "Clone failed."] "\n" $why] - } -} - -method _do_clone_checkout {HEAD} { - if {$HEAD eq {}} { - info_popup [strcat \ - [mc "No default branch obtained."] \ - "\n" \ - [mc "The 'master' branch has not been initialized."] \ - ] - set done 1 - return - } - if {[catch { - git update-ref HEAD $HEAD^0 + if {[catch { + cd $local_path + set ::_gitdir .git + set ::_prefix {} + _append_recentrepos [pwd] } err]} { - info_popup [strcat \ - [mc "Cannot resolve %s as a commit." $HEAD^0] \ - "\n $err" \ - "\n" \ - [mc "The 'master' branch has not been initialized."] \ - ] - set done 1 - return - } - - set status [status_bar::two_line $w_body] - pack $w_body -fill x -padx 10 -pady 10 - - # We start the status operation here. - # - # This function calls _readtree_wait as a callback. - # - # _readtree_wait in turn either calls _do_clone_submodules directly, - # or calls _postcheckout_wait as a callback which then calls - # _do_clone_submodules. - # - # _do_clone_submodules calls _do_validate_submodule_cloning. - # - # _do_validate_submodule_cloning stops the status operation. - # - # There are no other calls into this chain from other code. - - set o_status_op [$status start \ - [mc "Creating working directory"] \ - [mc "files"]] - - set readtree_err {} - set fd [git_read [list read-tree \ - -m \ - -u \ - -v \ - HEAD \ - HEAD \ - ] \ - [list 2>@1]] - fconfigure $fd -blocking 0 -translation binary - fileevent $fd readable [cb _readtree_wait $fd] -} - -method _readtree_wait {fd} { - set buf [read $fd] - $o_status_op update_meter $buf - append readtree_err $buf - - fconfigure $fd -blocking 1 - if {![eof $fd]} { - fconfigure $fd -blocking 0 - return - } - - if {[catch {close $fd}]} { - set err $readtree_err - regsub {^fatal: } $err {} err - error_popup [strcat \ - [mc "Initial file checkout failed."] \ - "\n\n$err"] - return - } - - # -- Run the post-checkout hook. - # - set fd_ph [githook_read post-checkout [string repeat 0 40] \ - [git rev-parse HEAD] 1] - if {$fd_ph ne {}} { - global pch_error - set pch_error {} - fconfigure $fd_ph -blocking 0 -translation binary -eofchar {} - fileevent $fd_ph readable [cb _postcheckout_wait $fd_ph] - } else { - _do_clone_submodules $this - } -} - -method _postcheckout_wait {fd_ph} { - global pch_error - - append pch_error [read $fd_ph] - fconfigure $fd_ph -blocking 1 - if {[eof $fd_ph]} { - if {[catch {close $fd_ph}]} { - hook_failed_popup post-checkout $pch_error 0 + set ok 0 } - unset pch_error - _do_clone_submodules $this - return } - fconfigure $fd_ph -blocking 0 -} - -method _do_clone_submodules {} { - if {$recursive eq {true}} { - $o_status_op stop - set o_status_op {} - - destroy $w_body - - set o_cons [console::embed \ - $w_body \ - [mc "Cloning submodules"]] - pack $w_body -fill both -expand 1 -padx 10 - $o_cons exec \ - [list git submodule update --init --recursive] \ - [cb _do_validate_submodule_cloning] - } else { - set done 1 + if {!$ok} { + set ::_gitdir {} + set ::_prefix {} } + set clone_ok $ok + set done 1 } -method _do_validate_submodule_cloning {ok} { - if {$ok} { - $o_cons done $ok - set done 1 - } else { - _clone_failed $this [mc "Cannot clone submodules."] - } -} ###################################################################### ## ## Open Existing Repository method _do_open {} { - global NS $w_next conf \ -state disabled \ -command [cb _do_open2] \ -text [mc "Open"] - ${NS}::frame $w_body - ${NS}::label $w_body.h \ + ttk::frame $w_body + ttk::label $w_body.h \ -font font_uibold -anchor center \ -text [mc "Open Existing Repository"] pack $w_body.h -side top -fill x -pady 10 pack $w_body -fill x -padx 10 - ${NS}::frame $w_body.where - ${NS}::label $w_body.where.l -text [mc "Repository:"] - ${NS}::entry $w_body.where.t \ + ttk::frame $w_body.where + ttk::label $w_body.where.l -text [mc "Repository:"] + ttk::entry $w_body.where.t \ -textvariable @local_path \ -width 50 - ${NS}::button $w_body.where.b \ + ttk::button $w_body.where.b \ -text [mc "Browse"] \ -command [cb _open_local_path] diff --git a/git-gui/lib/choose_rev.tcl b/git-gui/lib/choose_rev.tcl index 8ae7e8a5c4..cd355cc92a 100644 --- a/git-gui/lib/choose_rev.tcl +++ b/git-gui/lib/choose_rev.tcl @@ -32,7 +32,7 @@ proc new_unmerged {path {title {}}} { } constructor _new {path unmerged_only title} { - global current_branch is_detached use_ttk NS + global current_branch is_detached if {![info exists ::all_remotes]} { load_all_remotes @@ -41,65 +41,60 @@ constructor _new {path unmerged_only title} { set w $path if {$title ne {}} { - ${NS}::labelframe $w -text $title + ttk::labelframe $w -text $title } else { - ${NS}::frame $w + ttk::frame $w } bind $w <Destroy> [cb _delete %W] if {$is_detached} { - ${NS}::radiobutton $w.detachedhead_r \ + ttk::radiobutton $w.detachedhead_r \ -text [mc "This Detached Checkout"] \ -value HEAD \ -variable @revtype - if {!$use_ttk} {$w.detachedhead_r configure -anchor w} grid $w.detachedhead_r -sticky we -padx {0 5} -columnspan 2 } - ${NS}::radiobutton $w.expr_r \ + ttk::radiobutton $w.expr_r \ -text [mc "Revision Expression:"] \ -value expr \ -variable @revtype - ${NS}::entry $w.expr_t \ + ttk::entry $w.expr_t \ -width 50 \ -textvariable @c_expr \ -validate key \ -validatecommand [cb _validate %d %S] grid $w.expr_r $w.expr_t -sticky we -padx {0 5} - ${NS}::frame $w.types - ${NS}::radiobutton $w.types.head_r \ + ttk::frame $w.types + ttk::radiobutton $w.types.head_r \ -text [mc "Local Branch"] \ -value head \ -variable @revtype pack $w.types.head_r -side left - ${NS}::radiobutton $w.types.trck_r \ + ttk::radiobutton $w.types.trck_r \ -text [mc "Tracking Branch"] \ -value trck \ -variable @revtype pack $w.types.trck_r -side left - ${NS}::radiobutton $w.types.tag_r \ + ttk::radiobutton $w.types.tag_r \ -text [mc "Tag"] \ -value tag \ -variable @revtype pack $w.types.tag_r -side left set w_filter $w.types.filter - ${NS}::entry $w_filter \ + ttk::entry $w_filter \ -width 12 \ -textvariable @filter \ -validate key \ -validatecommand [cb _filter %P] pack $w_filter -side right - pack [${NS}::label $w.types.filter_icon \ + pack [ttk::label $w.types.filter_icon \ -image ::choose_rev::img_find \ ] -side right grid $w.types -sticky we -padx {0 5} -columnspan 2 - if {$use_ttk} { - ttk::frame $w.list -style SListbox.TFrame -padding 2 - } else { - frame $w.list - } + ttk::frame $w.list -style SListbox.TFrame -padding 2 set w_list $w.list.l listbox $w_list \ -font font_diff \ @@ -109,9 +104,7 @@ constructor _new {path unmerged_only title} { -exportselection false \ -xscrollcommand [cb _sb_set $w.list.sbx h] \ -yscrollcommand [cb _sb_set $w.list.sby v] - if {$use_ttk} { - $w_list configure -relief flat -highlightthickness 0 -borderwidth 0 - } + $w_list configure -relief flat -highlightthickness 0 -borderwidth 0 pack $w_list -fill both -expand 1 grid $w.list -sticky nswe -padx {20 5} -columnspan 2 bind $w_list <Any-Motion> [cb _show_tooltip @%x,%y] @@ -154,7 +147,7 @@ constructor _new {path unmerged_only title} { refs/remotes \ refs/tags \ ]] - fconfigure $fr_fd -translation lf -encoding utf-8 + fconfigure $fr_fd -encoding utf-8 while {[gets $fr_fd line] > 0} { set line [eval $line] if {[lindex $line 1 0] eq {tag}} { @@ -238,12 +231,10 @@ constructor _new {path unmerged_only title} { } method none {text} { - global NS use_ttk if {![winfo exists $w.none_r]} { - ${NS}::radiobutton $w.none_r \ + ttk::radiobutton $w.none_r \ -value none \ -variable @revtype - if {!$use_ttk} {$w.none_r configure -anchor w} grid $w.none_r -sticky we -padx {0 5} -columnspan 2 } $w.none_r configure -text $text @@ -429,7 +420,6 @@ method _delete {current} { } method _sb_set {sb orient first last} { - global NS set old_focus [focus -lastfor $w] if {$first == 0 && $last == 1} { @@ -445,10 +435,10 @@ method _sb_set {sb orient first last} { if {![winfo exists $sb]} { if {$orient eq {h}} { - ${NS}::scrollbar $sb -orient h -command [list $w_list xview] + ttk::scrollbar $sb -orient h -command [list $w_list xview] pack $sb -fill x -side bottom -before $w_list } else { - ${NS}::scrollbar $sb -orient v -command [list $w_list yview] + ttk::scrollbar $sb -orient v -command [list $w_list yview] pack $sb -fill y -side right -before $w_list } if {$old_focus ne {}} { @@ -580,7 +570,7 @@ method _reflog_last {name} { set last {} if {[catch {set last [file mtime [gitdir $name]]}] && ![catch {set g [safe_open_file [gitdir logs $name] r]}]} { - fconfigure $g -translation binary + fconfigure $g -encoding iso8859-1 while {[gets $g line] >= 0} { if {[regexp {> ([1-9][0-9]*) } $line line when]} { set last $when diff --git a/git-gui/lib/class.tcl b/git-gui/lib/class.tcl index f08506f383..0b1e67103f 100644 --- a/git-gui/lib/class.tcl +++ b/git-gui/lib/class.tcl @@ -136,7 +136,6 @@ proc delete_this {{t {}}} { proc make_dialog {t w args} { upvar $t top $w pfx this this - global use_ttk uplevel [linsert $args 0 make_toplevel $t $w] catch {wm attributes $top -type dialog} pave_toplevel $pfx diff --git a/git-gui/lib/commit.tcl b/git-gui/lib/commit.tcl index 60d66172a1..89eb8c7b73 100644 --- a/git-gui/lib/commit.tcl +++ b/git-gui/lib/commit.tcl @@ -28,7 +28,7 @@ You are currently in the middle of a merge that has not been fully completed. Y set name "" set email "" set fd [git_read [list cat-file commit $curHEAD]] - fconfigure $fd -encoding binary -translation lf + fconfigure $fd -encoding iso8859-1 # By default commits are assumed to be in utf-8 set enc utf-8 while {[gets $fd line] > 0} { @@ -43,9 +43,9 @@ You are currently in the middle of a merge that has not been fully completed. Y set enc [tcl_encoding $enc] if {$enc ne {}} { - set msg [encoding convertfrom $enc $msg] - set name [encoding convertfrom $enc $name] - set email [encoding convertfrom $enc $email] + set msg [convertfrom $enc $msg] + set name [convertfrom $enc $name] + set email [convertfrom $enc $email] } if {$name ne {} && $email ne {}} { set commit_author [list name $name email $email date $time] @@ -208,30 +208,6 @@ You must stage at least 1 file before you can commit. # -- A message is required. # set msg [$ui_comm get 1.0 end] - # Strip trailing whitespace - regsub -all -line {[ \t\r]+$} $msg {} msg - # Strip comment lines - global comment_string - set cmt_rx [strcat {(^|\n)} [regsub -all {\W} $comment_string {\\&}] {[^\n]*}] - regsub -all $cmt_rx $msg {\1} msg - # Strip leading empty lines - regsub {^\n*} $msg {} msg - # Compress consecutive empty lines - regsub -all {\n{3,}} $msg "\n\n" msg - # Strip trailing empty line - regsub {\n\n$} $msg "\n" msg - if {$msg eq {}} { - error_popup [mc "Please supply a commit message. - -A good commit message has the following format: - -- First line: Describe in one sentence what you did. -- Second line: Blank -- Remaining lines: Describe why this change is good. -"] - unlock_index - return - } # -- Build the message file. # @@ -254,7 +230,7 @@ A good commit message has the following format: ui_status [mc "Calling pre-commit hook..."] set pch_error {} - fconfigure $fd_ph -blocking 0 -translation binary -eofchar {} + fconfigure $fd_ph -blocking 0 -translation binary fileevent $fd_ph readable \ [list commit_prehook_wait $fd_ph $curHEAD $msg_p] } @@ -309,7 +285,7 @@ Do you really want to proceed with your Commit?"] ui_status [mc "Calling commit-msg hook..."] set pch_error {} - fconfigure $fd_ph -blocking 0 -translation binary -eofchar {} + fconfigure $fd_ph -blocking 0 -translation binary fileevent $fd_ph readable \ [list commit_commitmsg_wait $fd_ph $curHEAD $msg_p] } @@ -334,7 +310,52 @@ proc commit_commitmsg_wait {fd_ph curHEAD msg_p} { fconfigure $fd_ph -blocking 0 } +proc wash_commit_message {msg} { + # Strip trailing whitespace + regsub -all -line {[ \t\r]+$} $msg {} msg + # Strip comment lines + global comment_string + set cmt_rx [strcat {(^|\n)} [regsub -all {\W} $comment_string {\\&}] {[^\n]*}] + regsub -all $cmt_rx $msg {\1} msg + # Strip leading and trailing empty lines (puts adds one \n) + set msg [string trim $msg \n] + # Compress consecutive empty lines + regsub -all {\n{3,}} $msg \n\n msg + + return $msg +} + proc commit_writetree {curHEAD msg_p} { + # -- Process the commit message after hooks have run. + # + set msg_fd [safe_open_file $msg_p r] + setup_commit_encoding $msg_fd 1 + set msg [read $msg_fd] + close $msg_fd + + # Process the message (strip whitespace, comments, etc.) + set msg [wash_commit_message $msg] + + if {$msg eq {}} { + error_popup [mc "Please supply a commit message. + +A good commit message has the following format: + +- First line: Describe in one sentence what you did. +- Second line: Blank +- Remaining lines: Describe why this change is good. +"] + unlock_index + return + } + + # Write the processed message back to the file + set msg_wt [safe_open_file $msg_p w] + fconfigure $msg_wt -translation lf + setup_commit_encoding $msg_wt + puts $msg_wt $msg + close $msg_wt + ui_status [mc "Committing changes..."] set fd_wt [git_read [list write-tree]] fileevent $fd_wt readable \ @@ -348,6 +369,7 @@ proc commit_committree {fd_wt curHEAD msg_p} { global file_states selected_paths rescan_active global repo_config global env + global hashlength gets $fd_wt tree_id if {[catch {close $fd_wt} err]} { @@ -362,12 +384,12 @@ proc commit_committree {fd_wt curHEAD msg_p} { # if {$commit_type eq {normal}} { set fd_ot [git_read [list cat-file commit $PARENT]] - fconfigure $fd_ot -encoding binary -translation lf + fconfigure $fd_ot -encoding iso8859-1 set old_tree [gets $fd_ot] close $fd_ot if {[string equal -length 5 {tree } $old_tree] - && [string length $old_tree] == 45} { + && [string length $old_tree] == [expr {$hashlength + 5}]} { set old_tree [string range $old_tree 5 end] } else { error [mc "Commit %s appears to be corrupt" $PARENT] @@ -461,7 +483,7 @@ A rescan will be automatically started now. if {$fd_ph ne {}} { global pch_error set pch_error {} - fconfigure $fd_ph -blocking 0 -translation binary -eofchar {} + fconfigure $fd_ph -blocking 0 -translation binary fileevent $fd_ph readable \ [list commit_postcommit_wait $fd_ph $cmt_id] } diff --git a/git-gui/lib/console.tcl b/git-gui/lib/console.tcl index a017cfeadd..267699408c 100644 --- a/git-gui/lib/console.tcl +++ b/git-gui/lib/console.tcl @@ -27,20 +27,20 @@ constructor embed {path title} { } method _init {} { - global M1B use_ttk NS + global M1B if {$is_toplevel} { make_dialog top w -autodelete 0 wm title $top "[appname] ([reponame]): $t_short" } else { - ${NS}::frame $w + ttk::frame $w } set console_cr 1.0 set w_t $w.m.t - ${NS}::frame $w.m - ${NS}::label $w.m.l1 \ + ttk::frame $w.m + ttk::label $w.m.l1 \ -textvariable @t_long \ -anchor w \ -justify left \ @@ -78,7 +78,7 @@ method _init {} { " if {$is_toplevel} { - ${NS}::button $w.ok -text [mc "Close"] \ + ttk::button $w.ok -text [mc "Close"] \ -state disabled \ -command [list destroy $w] pack $w.ok -side bottom -anchor e -pady 10 -padx 10 @@ -207,14 +207,13 @@ method done {ok} { } method _sb_set {sb orient first last} { - global NS if {![winfo exists $sb]} { if {$first == $last || ($first == 0 && $last == 1)} return if {$orient eq {h}} { - ${NS}::scrollbar $sb -orient h -command [list $w_t xview] + ttk::scrollbar $sb -orient h -command [list $w_t xview] pack $sb -fill x -side bottom -before $w_t } else { - ${NS}::scrollbar $sb -orient v -command [list $w_t yview] + ttk::scrollbar $sb -orient v -command [list $w_t yview] pack $sb -fill y -side right -before $w_t } } diff --git a/git-gui/lib/database.tcl b/git-gui/lib/database.tcl index 1fc0ea00b3..78732d8651 100644 --- a/git-gui/lib/database.tcl +++ b/git-gui/lib/database.tcl @@ -2,7 +2,6 @@ # Copyright (C) 2006, 2007 Shawn Pearce proc do_stats {} { - global use_ttk NS set fd [git_read [list count-objects -v]] while {[gets $fd line] > 0} { if {[regexp {^([^:]+): (\d+)$} $line _ name value]} { @@ -26,18 +25,18 @@ proc do_stats {} { wm withdraw $w wm geometry $w "+[winfo rootx .]+[winfo rooty .]" - ${NS}::frame $w.buttons - ${NS}::button $w.buttons.close -text [mc Close] \ + ttk::frame $w.buttons + ttk::button $w.buttons.close -text [mc Close] \ -default active \ -command [list destroy $w] - ${NS}::button $w.buttons.gc -text [mc "Compress Database"] \ + ttk::button $w.buttons.gc -text [mc "Compress Database"] \ -default normal \ -command "destroy $w;do_gc" pack $w.buttons.close -side right pack $w.buttons.gc -side left pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - ${NS}::labelframe $w.stat -text [mc "Database Statistics"] + ttk::labelframe $w.stat -text [mc "Database Statistics"] foreach s { {count {mc "Number of loose objects"}} {size {mc "Disk space used by loose objects"} { KiB}} @@ -54,8 +53,8 @@ proc do_stats {} { set value "$value[lindex $s 2]" } - ${NS}::label $w.stat.l_$name -text [mc "%s:" $label] -anchor w - ${NS}::label $w.stat.v_$name -text $value -anchor w + ttk::label $w.stat.l_$name -text [mc "%s:" $label] -anchor w + ttk::label $w.stat.v_$name -text $value -anchor w grid $w.stat.l_$name $w.stat.v_$name -sticky we -padx {0 5} } pack $w.stat -pady 10 -padx 10 diff --git a/git-gui/lib/diff.tcl b/git-gui/lib/diff.tcl index 84f0468c7c..442737ba4f 100644 --- a/git-gui/lib/diff.tcl +++ b/git-gui/lib/diff.tcl @@ -2,15 +2,13 @@ # Copyright (C) 2006, 2007 Shawn Pearce proc apply_tab_size {{firsttab {}}} { - global have_tk85 repo_config ui_diff + global repo_config ui_diff set w [font measure font_diff "0"] - if {$have_tk85 && $firsttab != 0} { + if {$firsttab != 0} { $ui_diff configure -tabs [list [expr {$firsttab * $w}] [expr {($firsttab + $repo_config(gui.tabsize)) * $w}]] - } elseif {$have_tk85 || $repo_config(gui.tabsize) != 8} { - $ui_diff configure -tabs [expr {$repo_config(gui.tabsize) * $w}] } else { - $ui_diff configure -tabs {} + $ui_diff configure -tabs [expr {$repo_config(gui.tabsize) * $w}] } } @@ -193,7 +191,6 @@ proc show_other_diff {path w m cont_info} { file { set fd [safe_open_file $path r] fconfigure $fd \ - -eofchar {} \ -encoding [get_path_encoding $path] set content [read $fd $max_sz] close $fd @@ -280,9 +277,7 @@ proc start_show_diff {cont_info {add_opts {}}} { if {$w eq $ui_index} { lappend cmd diff-index lappend cmd --cached - if {[git-version >= "1.7.2"]} { - lappend cmd --ignore-submodules=dirty - } + lappend cmd --ignore-submodules=dirty } elseif {$w eq $ui_workdir} { if {[string first {U} $m] >= 0} { lappend cmd diff @@ -290,17 +285,14 @@ proc start_show_diff {cont_info {add_opts {}}} { lappend cmd diff-files } } - if {![is_config_false gui.textconv] && [git-version >= 1.6.1]} { + if {![is_config_false gui.textconv]} { lappend cmd --textconv } if {[string match {160000 *} [lindex $s 2]] || [string match {160000 *} [lindex $s 3]]} { set is_submodule_diff 1 - - if {[git-version >= "1.6.6"]} { - lappend cmd --submodule - } + lappend cmd --submodule } lappend cmd -p @@ -319,14 +311,6 @@ proc start_show_diff {cont_info {add_opts {}}} { lappend cmd $path } - if {$is_submodule_diff && [git-version < "1.6.6"]} { - if {$w eq $ui_index} { - set cmd [list submodule summary --cached -- $path] - } else { - set cmd [list submodule summary --files -- $path] - } - } - if {[catch {set fd [git_read_nice $cmd]} err]} { set diff_active 0 unlock_index @@ -340,6 +324,8 @@ proc start_show_diff {cont_info {add_opts {}}} { # '++' lines which is not bijective. Thus, we need to maintain a state # across lines. set ::conflict_in_pre_image 0 + + # git-diff has eol==\n, \r if present is part of the text fconfigure $fd \ -blocking 0 \ -encoding [get_path_encoding $path] \ diff --git a/git-gui/lib/error.tcl b/git-gui/lib/error.tcl index 8968a57f33..fc0b5ad5e0 100644 --- a/git-gui/lib/error.tcl +++ b/git-gui/lib/error.tcl @@ -71,13 +71,12 @@ proc ask_popup {msg} { } proc hook_failed_popup {hook msg {is_fatal 1}} { - global use_ttk NS set w .hookfail Dialog $w wm withdraw $w - ${NS}::frame $w.m - ${NS}::label $w.m.l1 -text [mc "%s hook failed:" $hook] \ + ttk::frame $w.m + ttk::label $w.m.l1 -text [mc "%s hook failed:" $hook] \ -anchor w \ -justify left \ -font font_uibold @@ -89,10 +88,10 @@ proc hook_failed_popup {hook msg {is_fatal 1}} { -width 80 -height 10 \ -font font_diff \ -yscrollcommand [list $w.m.sby set] - ${NS}::scrollbar $w.m.sby -command [list $w.m.t yview] + ttk::scrollbar $w.m.sby -command [list $w.m.t yview] pack $w.m.l1 -side top -fill x if {$is_fatal} { - ${NS}::label $w.m.l2 \ + ttk::label $w.m.l2 \ -text [mc "You must correct the above errors before committing."] \ -anchor w \ -justify left \ @@ -106,7 +105,7 @@ proc hook_failed_popup {hook msg {is_fatal 1}} { $w.m.t insert 1.0 $msg $w.m.t conf -state disabled - ${NS}::button $w.ok -text OK \ + ttk::button $w.ok -text OK \ -width 15 \ -command "destroy $w" pack $w.ok -side bottom -anchor e -pady 10 -padx 10 diff --git a/git-gui/lib/index.tcl b/git-gui/lib/index.tcl index 857864ff2b..e1d38e54be 100644 --- a/git-gui/lib/index.tcl +++ b/git-gui/lib/index.tcl @@ -22,8 +22,6 @@ proc _close_updateindex {fd} { } proc rescan_on_error {err {after {}}} { - global use_ttk NS - set w .indexfried Dialog $w wm withdraw $w @@ -35,14 +33,14 @@ proc rescan_on_error {err {after {}}} { -borderwidth 0 -highlightthickness 0 \ -background [get_bg_color $w] $w.msg tag configure bold -font font_uibold -justify center - ${NS}::scrollbar $w.vs -command [list $w.msg yview] + ttk::scrollbar $w.vs -command [list $w.msg yview] $w.msg insert end $s bold \n\n$err {} $w.msg configure -state disabled - ${NS}::button $w.continue \ + ttk::button $w.continue \ -text [mc "Continue"] \ -command [list destroy $w] - ${NS}::button $w.unlock \ + ttk::button $w.unlock \ -text [mc "Unlock Index"] \ -command "destroy $w; _delete_indexlock" grid $w.msg - $w.vs -sticky news @@ -80,7 +78,6 @@ proc update_indexinfo {msg path_list after} { -blocking 0 \ -buffering full \ -buffersize 512 \ - -encoding binary \ -translation binary fileevent $fd writable [list \ write_update_indexinfo \ @@ -149,7 +146,6 @@ proc update_index {msg path_list after} { -blocking 0 \ -buffering full \ -buffersize 512 \ - -encoding binary \ -translation binary fileevent $fd writable [list \ write_update_index \ @@ -229,7 +225,6 @@ proc checkout_index {msg path_list after capture_error} { -blocking 0 \ -buffering full \ -buffersize 512 \ - -encoding binary \ -translation binary fileevent $fd writable [list \ write_checkout_index \ @@ -430,6 +425,11 @@ proc revert_helper {txt paths} { if {![lock_index begin-update]} return + # Workaround for Tcl < 9.0: chord namespaces are not obeyed and + # operated in the global namespace. This clears an error that could + # have been left over from a previous operation. + set ::err {} + # Common "after" functionality that waits until multiple asynchronous # operations are complete (by waiting for them to activate their notes # on the chord). @@ -437,7 +437,7 @@ proc revert_helper {txt paths} { # The asynchronous operations are each indicated below by a comment # before the code block that starts the async operation. set after_chord [SimpleChord::new { - if {[string trim $err] != ""} { + if {[info exists err] && [string trim $err] ne ""} { rescan_on_error $err } else { unlock_index diff --git a/git-gui/lib/line.tcl b/git-gui/lib/line.tcl index a026de954c..5980ae805c 100644 --- a/git-gui/lib/line.tcl +++ b/git-gui/lib/line.tcl @@ -9,18 +9,17 @@ field ctext field linenum {} constructor new {i_w i_text args} { - global use_ttk NS set w $i_w set ctext $i_text - ${NS}::frame $w - ${NS}::label $w.l -text [mc "Goto Line:"] + ttk::frame $w + ttk::label $w.l -text [mc "Goto Line:"] tentry $w.ent \ -textvariable ${__this}::linenum \ -background lightgreen \ -validate key \ -validatecommand [cb _validate %P] - ${NS}::button $w.bn -text [mc Go] -command [cb _goto] + ttk::button $w.bn -text [mc Go] -command [cb _goto] pack $w.l -side left pack $w.bn -side right diff --git a/git-gui/lib/merge.tcl b/git-gui/lib/merge.tcl index 44c3f93584..3490beddae 100644 --- a/git-gui/lib/merge.tcl +++ b/git-gui/lib/merge.tcl @@ -112,16 +112,7 @@ method _start {} { close $fh set _last_merged_branch $branch - if {[git-version >= "2.5.0"]} { - set cmd [list git merge --strategy=recursive FETCH_HEAD] - } else { - set cmd [list git] - lappend cmd merge - lappend cmd --strategy=recursive - lappend cmd [git_redir [list fmt-merge-msg] [list <[gitdir FETCH_HEAD]]] - lappend cmd HEAD - lappend cmd $name - } + set cmd [list git merge --strategy=recursive FETCH_HEAD] ui_status [mc "Merging %s and %s..." $current_branch $stitle] set cons [console::new [mc "Merge"] "merge $stitle"] @@ -145,7 +136,7 @@ method _finish {cons ok} { constructor dialog {} { global current_branch - global M1B use_ttk NS + global M1B if {![_can_merge $this]} { delete_this @@ -160,21 +151,21 @@ constructor dialog {} { set _start [cb _start] - ${NS}::label $w.header \ + ttk::label $w.header \ -text [mc "Merge Into %s" $current_branch] \ -font font_uibold pack $w.header -side top -fill x - ${NS}::frame $w.buttons - ${NS}::button $w.buttons.visualize \ + ttk::frame $w.buttons + ttk::button $w.buttons.visualize \ -text [mc Visualize] \ -command [cb _visualize] pack $w.buttons.visualize -side left - ${NS}::button $w.buttons.merge \ + ttk::button $w.buttons.merge \ -text [mc Merge] \ -command $_start pack $w.buttons.merge -side right - ${NS}::button $w.buttons.cancel \ + ttk::button $w.buttons.cancel \ -text [mc "Cancel"] \ -command [cb _cancel] pack $w.buttons.cancel -side right -padx 5 diff --git a/git-gui/lib/mergetool.tcl b/git-gui/lib/mergetool.tcl index 2c9bb3af40..44be4ed3ff 100644 --- a/git-gui/lib/mergetool.tcl +++ b/git-gui/lib/mergetool.tcl @@ -90,7 +90,7 @@ proc merge_load_stages {path cont} { set merge_stages_fd [git_read [list ls-files -u -z -- $path]] - fconfigure $merge_stages_fd -blocking 0 -translation binary -encoding binary + fconfigure $merge_stages_fd -blocking 0 -translation binary fileevent $merge_stages_fd readable [list read_merge_stages $merge_stages_fd $cont] } @@ -370,7 +370,7 @@ proc merge_tool_start {cmdline target backup stages} { ui_status [mc "Running merge tool..."] - fconfigure $mtool_fd -blocking 0 -translation binary -encoding binary + fconfigure $mtool_fd -blocking 0 -translation binary fileevent $mtool_fd readable [list read_mtool_output $mtool_fd] } diff --git a/git-gui/lib/option.tcl b/git-gui/lib/option.tcl index e43971bfa3..487d70691d 100644 --- a/git-gui/lib/option.tcl +++ b/git-gui/lib/option.tcl @@ -91,7 +91,7 @@ proc save_config {} { proc do_options {} { global repo_config global_config font_descs global repo_config_new global_config_new - global ui_comm_spell use_ttk NS + global ui_comm_spell array unset repo_config_new array unset global_config_new @@ -115,23 +115,23 @@ proc do_options {} { wm transient $w [winfo parent $w] wm geometry $w "+[winfo rootx .]+[winfo rooty .]" - ${NS}::frame $w.buttons - ${NS}::button $w.buttons.restore -text [mc "Restore Defaults"] \ + ttk::frame $w.buttons + ttk::button $w.buttons.restore -text [mc "Restore Defaults"] \ -default normal \ -command do_restore_defaults pack $w.buttons.restore -side left - ${NS}::button $w.buttons.save -text [mc Save] \ + ttk::button $w.buttons.save -text [mc Save] \ -default active \ -command [list do_save_config $w] pack $w.buttons.save -side right - ${NS}::button $w.buttons.cancel -text [mc "Cancel"] \ + ttk::button $w.buttons.cancel -text [mc "Cancel"] \ -default normal \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - ${NS}::labelframe $w.repo -text [mc "%s Repository" [reponame]] - ${NS}::labelframe $w.global -text [mc "Global (All Repositories)"] + ttk::labelframe $w.repo -text [mc "%s Repository" [reponame]] + ttk::labelframe $w.global -text [mc "Global (All Repositories)"] pack $w.repo -side left -fill both -expand 1 -pady 5 -padx 5 pack $w.global -side right -fill both -expand 1 -pady 5 -padx 5 @@ -170,7 +170,7 @@ proc do_options {} { foreach f {repo global} { switch -glob -- $type { b { - ${NS}::checkbutton $w.$f.$optid -text $text \ + ttk::checkbutton $w.$f.$optid -text $text \ -variable ${f}_config_new($name) \ -onvalue true \ -offvalue false @@ -178,8 +178,8 @@ proc do_options {} { } i-* { regexp -- {-(\d+)\.\.(\d+)$} $type _junk min max - ${NS}::frame $w.$f.$optid - ${NS}::label $w.$f.$optid.l -text [mc "%s:" $text] + ttk::frame $w.$f.$optid + ttk::label $w.$f.$optid.l -text [mc "%s:" $text] pack $w.$f.$optid.l -side left -anchor w -fill x tspinbox $w.$f.$optid.v \ -textvariable ${f}_config_new($name) \ @@ -193,9 +193,9 @@ proc do_options {} { } c - t { - ${NS}::frame $w.$f.$optid - ${NS}::label $w.$f.$optid.l -text [mc "%s:" $text] - ${NS}::entry $w.$f.$optid.v \ + ttk::frame $w.$f.$optid + ttk::label $w.$f.$optid.l -text [mc "%s:" $text] + ttk::entry $w.$f.$optid.v \ -width 20 \ -textvariable ${f}_config_new($name) pack $w.$f.$optid.l -side left -anchor w @@ -206,7 +206,7 @@ proc do_options {} { menu $w.$f.$optid.m build_encoding_menu $w.$f.$optid.m \ [list set ${f}_config_new($name)] 1 - ${NS}::button $w.$f.$optid.b \ + ttk::button $w.$f.$optid.b \ -text [mc "Change"] \ -command [list popup_btn_menu \ $w.$f.$optid.m $w.$f.$optid.b] @@ -216,17 +216,11 @@ proc do_options {} { } s { set opts [eval [lindex $option 3]] - ${NS}::frame $w.$f.$optid - ${NS}::label $w.$f.$optid.l -text [mc "%s:" $text] - if {$use_ttk} { - ttk::combobox $w.$f.$optid.v \ - -textvariable ${f}_config_new($name) \ - -values $opts -state readonly - } else { - eval tk_optionMenu $w.$f.$optid.v \ - ${f}_config_new($name) \ - $opts - } + ttk::frame $w.$f.$optid + ttk::label $w.$f.$optid.l -text [mc "%s:" $text] + ttk::combobox $w.$f.$optid.v \ + -textvariable ${f}_config_new($name) \ + -values $opts -state readonly pack $w.$f.$optid.l -side left -anchor w -fill x pack $w.$f.$optid.v -side right -anchor e -padx 5 pack $w.$f.$optid -side top -anchor w -fill x @@ -250,17 +244,11 @@ proc do_options {} { set ${f}_config_new(gui.spellingdictionary) $value } - ${NS}::frame $w.$f.$optid - ${NS}::label $w.$f.$optid.l -text [mc "Spelling Dictionary:"] - if {$use_ttk} { - ttk::combobox $w.$f.$optid.v \ - -textvariable ${f}_config_new(gui.spellingdictionary) \ - -values $all_dicts -state readonly - } else { - eval tk_optionMenu $w.$f.$optid.v \ - ${f}_config_new(gui.spellingdictionary) \ - $all_dicts - } + ttk::frame $w.$f.$optid + ttk::label $w.$f.$optid.l -text [mc "Spelling Dictionary:"] + ttk::combobox $w.$f.$optid.v \ + -textvariable ${f}_config_new(gui.spellingdictionary) \ + -values $all_dicts -state readonly pack $w.$f.$optid.l -side left -anchor w -fill x pack $w.$f.$optid.v -side right -anchor e -padx 5 pack $w.$f.$optid -side top -anchor w -fill x @@ -278,9 +266,9 @@ proc do_options {} { set global_config_new(gui.$font^^size) \ [font configure $font -size] - ${NS}::frame $w.global.$name - ${NS}::label $w.global.$name.l -text [mc "%s:" $text] - ${NS}::button $w.global.$name.b \ + ttk::frame $w.global.$name + ttk::label $w.global.$name.l -text [mc "%s:" $text] + ttk::button $w.global.$name.b \ -text [mc "Change Font"] \ -command [list \ tchoosefont \ @@ -289,9 +277,9 @@ proc do_options {} { global_config_new(gui.$font^^family) \ global_config_new(gui.$font^^size) \ ] - ${NS}::label $w.global.$name.f -textvariable global_config_new(gui.$font^^family) - ${NS}::label $w.global.$name.s -textvariable global_config_new(gui.$font^^size) - ${NS}::label $w.global.$name.pt -text [mc "pt."] + ttk::label $w.global.$name.f -textvariable global_config_new(gui.$font^^family) + ttk::label $w.global.$name.s -textvariable global_config_new(gui.$font^^size) + ttk::label $w.global.$name.pt -text [mc "pt."] pack $w.global.$name.l -side left -anchor w pack $w.global.$name.b -side right -anchor e pack $w.global.$name.pt -side right -anchor w diff --git a/git-gui/lib/remote.tcl b/git-gui/lib/remote.tcl index cf796d1601..9b49b6e462 100644 --- a/git-gui/lib/remote.tcl +++ b/git-gui/lib/remote.tcl @@ -233,8 +233,6 @@ proc make_sure_remote_submenues_exist {remote_m} { proc update_all_remotes_menu_entry {} { global all_remotes - if {[git-version < 1.6.6]} { return } - set have_remote 0 foreach r $all_remotes { incr have_remote diff --git a/git-gui/lib/remote_add.tcl b/git-gui/lib/remote_add.tcl index 480a6b30d0..bff1376cb3 100644 --- a/git-gui/lib/remote_add.tcl +++ b/git-gui/lib/remote_add.tcl @@ -13,7 +13,7 @@ field location {}; # location of the remote the user has chosen field opt_action fetch; # action to do after registering the remote locally constructor dialog {} { - global repo_config use_ttk NS + global repo_config make_dialog top w wm withdraw $top @@ -22,34 +22,34 @@ constructor dialog {} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } - ${NS}::label $w.header -text [mc "Add New Remote"] \ + ttk::label $w.header -text [mc "Add New Remote"] \ -font font_uibold -anchor center pack $w.header -side top -fill x - ${NS}::frame $w.buttons - ${NS}::button $w.buttons.create -text [mc Add] \ + ttk::frame $w.buttons + ttk::button $w.buttons.create -text [mc Add] \ -default active \ -command [cb _add] pack $w.buttons.create -side right - ${NS}::button $w.buttons.cancel -text [mc Cancel] \ + ttk::button $w.buttons.cancel -text [mc Cancel] \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - ${NS}::labelframe $w.desc -text [mc "Remote Details"] + ttk::labelframe $w.desc -text [mc "Remote Details"] - ${NS}::label $w.desc.name_l -text [mc "Name:"] + ttk::label $w.desc.name_l -text [mc "Name:"] set w_name $w.desc.name_t - ${NS}::entry $w_name \ + ttk::entry $w_name \ -width 40 \ -textvariable @name \ -validate key \ -validatecommand [cb _validate_name %d %S] grid $w.desc.name_l $w_name -sticky we -padx {0 5} - ${NS}::label $w.desc.loc_l -text [mc "Location:"] + ttk::label $w.desc.loc_l -text [mc "Location:"] set w_loc $w.desc.loc_t - ${NS}::entry $w_loc \ + ttk::entry $w_loc \ -width 40 \ -textvariable @location grid $w.desc.loc_l $w_loc -sticky we -padx {0 5} @@ -57,21 +57,21 @@ constructor dialog {} { grid columnconfigure $w.desc 1 -weight 1 pack $w.desc -anchor nw -fill x -pady 5 -padx 5 - ${NS}::labelframe $w.action -text [mc "Further Action"] + ttk::labelframe $w.action -text [mc "Further Action"] - ${NS}::radiobutton $w.action.fetch \ + ttk::radiobutton $w.action.fetch \ -text [mc "Fetch Immediately"] \ -value fetch \ -variable @opt_action pack $w.action.fetch -anchor nw - ${NS}::radiobutton $w.action.push \ + ttk::radiobutton $w.action.push \ -text [mc "Initialize Remote Repository and Push"] \ -value push \ -variable @opt_action pack $w.action.push -anchor nw - ${NS}::radiobutton $w.action.none \ + ttk::radiobutton $w.action.none \ -text [mc "Do Nothing Else Now"] \ -value none \ -variable @opt_action diff --git a/git-gui/lib/remote_branch_delete.tcl b/git-gui/lib/remote_branch_delete.tcl index c8c99b17a8..f0814efdd7 100644 --- a/git-gui/lib/remote_branch_delete.tcl +++ b/git-gui/lib/remote_branch_delete.tcl @@ -23,7 +23,7 @@ field full_cache field cached constructor dialog {} { - global all_remotes M1B use_ttk NS + global all_remotes M1B make_dialog top w wm title $top [mc "%s (%s): Delete Branch Remotely" [appname] [reponame]] @@ -31,32 +31,28 @@ constructor dialog {} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } - ${NS}::label $w.header -text [mc "Delete Branch Remotely"] \ + ttk::label $w.header -text [mc "Delete Branch Remotely"] \ -font font_uibold -anchor center pack $w.header -side top -fill x - ${NS}::frame $w.buttons - ${NS}::button $w.buttons.delete -text [mc Delete] \ + ttk::frame $w.buttons + ttk::button $w.buttons.delete -text [mc Delete] \ -default active \ -command [cb _delete] pack $w.buttons.delete -side right - ${NS}::button $w.buttons.cancel -text [mc "Cancel"] \ + ttk::button $w.buttons.cancel -text [mc "Cancel"] \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - ${NS}::labelframe $w.dest -text [mc "From Repository"] + ttk::labelframe $w.dest -text [mc "From Repository"] if {$all_remotes ne {}} { - ${NS}::radiobutton $w.dest.remote_r \ + ttk::radiobutton $w.dest.remote_r \ -text [mc "Remote:"] \ -value remote \ -variable @urltype - if {$use_ttk} { - ttk::combobox $w.dest.remote_m -textvariable @remote \ - -values $all_remotes -state readonly - } else { - eval tk_optionMenu $w.dest.remote_m @remote $all_remotes - } + ttk::combobox $w.dest.remote_m -textvariable @remote \ + -values $all_remotes -state readonly grid $w.dest.remote_r $w.dest.remote_m -sticky w if {[lsearch -sorted -exact $all_remotes origin] != -1} { set remote origin @@ -68,11 +64,11 @@ constructor dialog {} { } else { set urltype url } - ${NS}::radiobutton $w.dest.url_r \ + ttk::radiobutton $w.dest.url_r \ -text [mc "Arbitrary Location:"] \ -value url \ -variable @urltype - ${NS}::entry $w.dest.url_t \ + ttk::entry $w.dest.url_t \ -width 50 \ -textvariable @url \ -validate key \ @@ -85,19 +81,19 @@ constructor dialog {} { grid columnconfigure $w.dest 1 -weight 1 pack $w.dest -anchor nw -fill x -pady 5 -padx 5 - ${NS}::labelframe $w.heads -text [mc "Branches"] + ttk::labelframe $w.heads -text [mc "Branches"] slistbox $w.heads.l \ -height 10 \ -width 70 \ -listvariable @head_list \ -selectmode extended - ${NS}::frame $w.heads.footer - ${NS}::label $w.heads.footer.status \ + ttk::frame $w.heads.footer + ttk::label $w.heads.footer.status \ -textvariable @status \ -anchor w \ -justify left - ${NS}::button $w.heads.footer.rescan \ + ttk::button $w.heads.footer.rescan \ -text [mc "Rescan"] \ -command [cb _rescan] pack $w.heads.footer.status -side left -fill x @@ -107,8 +103,8 @@ constructor dialog {} { pack $w.heads.l -side left -fill both -expand 1 pack $w.heads -fill both -expand 1 -pady 5 -padx 5 - ${NS}::labelframe $w.validate -text [mc "Delete Only If"] - ${NS}::radiobutton $w.validate.head_r \ + ttk::labelframe $w.validate -text [mc "Delete Only If"] + ttk::radiobutton $w.validate.head_r \ -text [mc "Merged Into:"] \ -value head \ -variable @checktype @@ -116,7 +112,7 @@ constructor dialog {} { trace add variable @head_list write [cb _write_head_list] trace add variable @check_head write [cb _write_check_head] grid $w.validate.head_r $w.validate.head_m -sticky w - ${NS}::radiobutton $w.validate.always_r \ + ttk::radiobutton $w.validate.always_r \ -text [mc "Always (Do not perform merge checks)"] \ -value always \ -variable @checktype @@ -311,7 +307,6 @@ method _load {cache uri} { set active_ls [git_read [list ls-remote $uri]] fconfigure $active_ls \ -blocking 0 \ - -translation lf \ -encoding utf-8 fileevent $active_ls readable [cb _read $cache $active_ls] } else { @@ -323,6 +318,8 @@ method _load {cache uri} { } method _read {cache fd} { + global hashlength + if {$fd ne $active_ls} { catch {close $fd} return @@ -330,7 +327,7 @@ method _read {cache fd} { while {[gets $fd line] >= 0} { if {[string match {*^{}} $line]} continue - if {[regexp {^([0-9a-f]{40}) (.*)$} $line _junk obj ref]} { + if {[regexp [string map "@@ $hashlength" {^([0-9a-f]{@@}) (.*)$}] $line _junk obj ref]} { if {[regsub ^refs/heads/ $ref {} abr]} { lappend head_list $abr lappend head_cache($cache) $abr diff --git a/git-gui/lib/search.tcl b/git-gui/lib/search.tcl index ef1e55521d..47a0d8c961 100644 --- a/git-gui/lib/search.tcl +++ b/git-gui/lib/search.tcl @@ -21,7 +21,6 @@ field smarktop field smarkbot constructor new {i_w i_text args} { - global use_ttk NS set w $i_w set ctext $i_text @@ -44,14 +43,14 @@ constructor new {i_w i_text args} { set history [list] - ${NS}::frame $w - ${NS}::label $w.l -text [mc Find:] + ttk::frame $w + ttk::label $w.l -text [mc Find:] tentry $w.ent -textvariable ${__this}::searchstring -background lightgreen - ${NS}::button $w.bn -text [mc Next] -command [cb find_next] - ${NS}::button $w.bp -text [mc Prev] -command [cb find_prev] - ${NS}::checkbutton $w.re -text [mc RegExp] \ + ttk::button $w.bn -text [mc Next] -command [cb find_next] + ttk::button $w.bp -text [mc Prev] -command [cb find_prev] + ttk::checkbutton $w.re -text [mc RegExp] \ -variable ${__this}::regexpsearch -command [cb _incrsearch] - ${NS}::checkbutton $w.cs -text [mc Case] \ + ttk::checkbutton $w.cs -text [mc Case] \ -variable ${__this}::casesensitive -command [cb _incrsearch] pack $w.l -side left pack $w.cs -side right diff --git a/git-gui/lib/shortcut.tcl b/git-gui/lib/shortcut.tcl index 1d01d9cbfa..431665059e 100644 --- a/git-gui/lib/shortcut.tcl +++ b/git-gui/lib/shortcut.tcl @@ -3,27 +3,41 @@ proc do_windows_shortcut {} { global _gitworktree - set fn [tk_getSaveFile \ - -parent . \ - -title [mc "%s (%s): Create Desktop Icon" [appname] [reponame]] \ - -initialfile "Git [reponame].lnk"] - if {$fn != {}} { - if {[file extension $fn] ne {.lnk}} { - set fn ${fn}.lnk - } - # Use git-gui.exe if available (ie: git-for-windows) - set cmdLine [list [_which git-gui]] - if {$cmdLine eq {}} { - set cmdLine [list [info nameofexecutable] \ - [file normalize $::argv0]] - } - if {[catch { - win32_create_lnk $fn $cmdLine \ - [file normalize $_gitworktree] - } err]} { - error_popup [strcat [mc "Cannot write shortcut:"] "\n\n$err"] + + set desktop [safe_exec [list cygpath -mD]] + set link_file "Git [reponame].lnk" + set link_path [file normalize [file join $desktop $link_file]] + + # on Windows, tk_getSaveFile dereferences .lnk files, so no simple + # filename chooser is available. Use the default or quit. + if {[file exists $link_path]} { + set answer [tk_messageBox \ + -type yesno \ + -title [mc "%s (%s): Create Desktop Icon" [appname] [reponame]] \ + -default yes \ + -message [mc "Replace existing shortcut: %s?" $link_file]] + if {$answer == no} { + return } } + + # Use git-gui.exe if found, fall back to wish + launcher + set link_arguments {} + set link_target [safe_exec [list cygpath -m /cmd/git-gui.exe]] + if {![file executable $link_target]} { + set link_target [_which git-gui] + } + if {![file executable $link_target]} { + set link_target [file normalize [info nameofexecutable]] + set link_arguments [file normalize $::argv0] + } + set cmdLine [list $link_target $link_arguments] + if {[catch { + win32_create_lnk $link_path $cmdLine \ + [file normalize $_gitworktree] + } err]} { + error_popup [strcat [mc "Cannot write shortcut:"] "\n\n$err"] + } } proc do_cygwin_shortcut {} { diff --git a/git-gui/lib/spellcheck.tcl b/git-gui/lib/spellcheck.tcl index 538d61c792..634656820d 100644 --- a/git-gui/lib/spellcheck.tcl +++ b/git-gui/lib/spellcheck.tcl @@ -33,7 +33,6 @@ constructor init {pipe_fd ui_text ui_menu} { method _connect {pipe_fd} { fconfigure $pipe_fd \ -encoding utf-8 \ - -eofchar {} \ -translation lf if {[gets $pipe_fd s_version] <= 0} { diff --git a/git-gui/lib/sshkey.tcl b/git-gui/lib/sshkey.tcl index c3e681b899..7a6526d3db 100644 --- a/git-gui/lib/sshkey.tcl +++ b/git-gui/lib/sshkey.tcl @@ -18,7 +18,7 @@ proc find_ssh_key {} { } proc do_ssh_key {} { - global sshkey_title have_tk85 sshkey_fd use_ttk NS + global sshkey_title sshkey_fd set w .sshkey_dialog if {[winfo exists $w]} { @@ -38,9 +38,9 @@ proc do_ssh_key {} { set gen_state disabled } - ${NS}::frame $w.header - ${NS}::label $w.header.lbl -textvariable sshkey_title -anchor w - ${NS}::button $w.header.gen -text [mc "Generate Key"] \ + ttk::frame $w.header + ttk::label $w.header.lbl -textvariable sshkey_title -anchor w + ttk::button $w.header.gen -text [mc "Generate Key"] \ -command [list make_ssh_key $w] -state $gen_state pack $w.header.lbl -side left -expand 1 -fill x pack $w.header.gen -side right @@ -48,17 +48,14 @@ proc do_ssh_key {} { text $w.contents -width 60 -height 10 -wrap char -relief sunken pack $w.contents -fill both -expand 1 - if {$have_tk85} { - set clr darkblue - if {$use_ttk} { set clr [ttk::style lookup . -selectbackground] } - $w.contents configure -inactiveselectbackground $clr - } + set clr [ttk::style lookup . -selectbackground] + $w.contents configure -inactiveselectbackground $clr - ${NS}::frame $w.buttons - ${NS}::button $w.buttons.close -text [mc Close] \ + ttk::frame $w.buttons + ttk::button $w.buttons.close -text [mc Close] \ -default active -command [list destroy $w] pack $w.buttons.close -side right - ${NS}::button $w.buttons.copy -text [mc "Copy To Clipboard"] \ + ttk::button $w.buttons.copy -text [mc "Copy To Clipboard"] \ -command [list tk_textCopy $w.contents] pack $w.buttons.copy -side left pack $w.buttons -side bottom -fill x -pady 5 -padx 5 diff --git a/git-gui/lib/status_bar.tcl b/git-gui/lib/status_bar.tcl index d32b14142f..f5c0204a2d 100644 --- a/git-gui/lib/status_bar.tcl +++ b/git-gui/lib/status_bar.tcl @@ -39,7 +39,6 @@ field operations ; # list of current ongoing operations field completed_operation_count constructor new {path} { - global use_ttk NS set w $path set w_l $w.l set w_c $w.c @@ -51,11 +50,8 @@ constructor new {path} { set operations [list] set completed_operation_count 0 - ${NS}::frame $w - if {!$use_ttk} { - $w configure -borderwidth 1 -relief sunken - } - ${NS}::label $w_l \ + ttk::frame $w + ttk::label $w_l \ -textvariable @status_bar_text \ -anchor w \ -justify left @@ -72,7 +68,6 @@ method _oneline_pack {} { } constructor two_line {path} { - global NS set w $path set w_l $w.l set w_c $w.c @@ -84,8 +79,8 @@ constructor two_line {path} { set operations [list] set completed_operation_count 0 - ${NS}::frame $w - ${NS}::label $w_l \ + ttk::frame $w + ttk::label $w_l \ -textvariable @status_bar_text \ -anchor w \ -justify left diff --git a/git-gui/lib/themed.tcl b/git-gui/lib/themed.tcl index f43d84e54f..c18e201d85 100644 --- a/git-gui/lib/themed.tcl +++ b/git-gui/lib/themed.tcl @@ -21,10 +21,10 @@ namespace eval color { set inactive_select_bg [convert_rgb_to_gray $select_bg] set inactive_select_fg $select_fg - set color::select_bg $select_bg - set color::select_fg $select_fg - set color::inactive_select_bg $inactive_select_bg - set color::inactive_select_fg $inactive_select_fg + set ::color::select_bg $select_bg + set ::color::select_fg $select_fg + set ::color::inactive_select_bg $inactive_select_bg + set ::color::inactive_select_fg $inactive_select_fg proc add_option {key val} { option add $key $val widgetDefault @@ -190,8 +190,7 @@ proc InitEntryFrame {} { } proc gold_frame {w args} { - global use_ttk - if {$use_ttk && ![is_MacOSX]} { + if {![is_MacOSX]} { eval [linsert $args 0 ttk::frame $w -style Gold.TFrame] } else { eval [linsert $args 0 frame $w -background gold] @@ -199,8 +198,7 @@ proc gold_frame {w args} { } proc tlabel {w args} { - global use_ttk - if {$use_ttk && ![is_MacOSX]} { + if {![is_MacOSX]} { set cmd [list ttk::label $w -style Color.TLabel] foreach {k v} $args { switch -glob -- $k { @@ -216,17 +214,7 @@ proc tlabel {w args} { # The padded label gets used in the about class. proc paddedlabel {w args} { - global use_ttk - if {$use_ttk} { - eval [linsert $args 0 ttk::label $w -style Padded.TLabel] - } else { - eval [linsert $args 0 label $w \ - -padx 5 -pady 5 \ - -justify left \ - -anchor w \ - -borderwidth 1 \ - -relief solid] - } + eval [linsert $args 0 ttk::label $w -style Padded.TLabel] } # Create a toplevel for use as a dialog. @@ -242,8 +230,7 @@ proc Dialog {w args} { # Tk toplevels are not themed - so pave it over with a themed frame to get # the base color correct per theme. proc pave_toplevel {w} { - global use_ttk - if {$use_ttk && ![winfo exists $w.!paving]} { + if {![winfo exists $w.!paving]} { set paving [ttk::frame $w.!paving] place $paving -x 0 -y 0 -relwidth 1 -relheight 1 lower $paving @@ -254,20 +241,11 @@ proc pave_toplevel {w} { # On many themes the border for a scrolled listbox needs to go around the # listbox and the scrollbar. proc slistbox {w args} { - global use_ttk NS - if {$use_ttk} { - set f [ttk::frame $w -style SListbox.TFrame -padding 2] - } else { - set f [frame $w -relief flat] - } + set f [ttk::frame $w -style SListbox.TFrame -padding 2] if {[catch { - if {$use_ttk} { - eval [linsert $args 0 listbox $f.list -relief flat \ - -highlightthickness 0 -borderwidth 0] - } else { - eval [linsert $args 0 listbox $f.list] - } - ${NS}::scrollbar $f.vs -command [list $f.list yview] + eval [linsert $args 0 listbox $f.list -relief flat \ + -highlightthickness 0 -borderwidth 0] + ttk::scrollbar $f.vs -command [list $f.list yview] $f.list configure -yscrollcommand [list $f.vs set] grid $f.list $f.vs -sticky news grid rowconfigure $f 0 -weight 1 @@ -285,67 +263,42 @@ proc slistbox {w args} { # fetch the background color from a widget. proc get_bg_color {w} { - global use_ttk - if {$use_ttk} { - set bg [ttk::style lookup [winfo class $w] -background] - } else { - set bg [$w cget -background] - } + set bg [ttk::style lookup [winfo class $w] -background] return $bg } -# ttk::spinbox didn't get added until 8.6 +# ttk::spinbox proc tspinbox {w args} { - global use_ttk - if {$use_ttk && [llength [info commands ttk::spinbox]] > 0} { - eval [linsert $args 0 ttk::spinbox $w] - } else { - eval [linsert $args 0 spinbox $w] - } + eval [linsert $args 0 ttk::spinbox $w] } # Create a text widget with any theme specific properties. proc ttext {w args} { - global use_ttk - if {$use_ttk} { - switch -- [ttk_get_current_theme] { - "vista" - "xpnative" { - lappend args -highlightthickness 0 -borderwidth 0 - } + switch -- [ttk_get_current_theme] { + "vista" - "xpnative" { + lappend args -highlightthickness 0 -borderwidth 0 } } set w [eval [linsert $args 0 text $w]] - if {$use_ttk} { - if {[winfo class [winfo parent $w]] eq "EntryFrame"} { - bind $w <FocusIn> {[winfo parent %W] state focus} - bind $w <FocusOut> {[winfo parent %W] state !focus} - } + if {[winfo class [winfo parent $w]] eq "EntryFrame"} { + bind $w <FocusIn> {[winfo parent %W] state focus} + bind $w <FocusOut> {[winfo parent %W] state !focus} } return $w } # themed frame suitable for surrounding a text field. proc textframe {w args} { - global use_ttk - if {$use_ttk} { - if {[catch {ttk::style layout EntryFrame}]} { - InitEntryFrame - } - eval [linsert $args 0 ttk::frame $w -class EntryFrame -style EntryFrame] - } else { - eval [linsert $args 0 frame $w] + if {[catch {ttk::style layout EntryFrame}]} { + InitEntryFrame } + eval [linsert $args 0 ttk::frame $w -class EntryFrame -style EntryFrame] return $w } proc tentry {w args} { - global use_ttk - if {$use_ttk} { - InitTheme - ttk::entry $w -style Edged.Entry - } else { - entry $w - } + InitTheme + ttk::entry $w -style Edged.Entry rename $w _$w interp alias {} $w {} tentry_widgetproc $w @@ -353,25 +306,14 @@ proc tentry {w args} { return $w } proc tentry_widgetproc {w cmd args} { - global use_ttk switch -- $cmd { state { - if {$use_ttk} { - return [uplevel 1 [list _$w $cmd] $args] - } else { - if {[lsearch -exact $args pressed] != -1} { - _$w configure -background lightpink - } else { - _$w configure -background lightgreen - } - } + return [uplevel 1 [list _$w $cmd] $args] } configure { - if {$use_ttk} { - if {[set n [lsearch -exact $args -background]] != -1} { - set args [lreplace $args $n [incr n]] - if {[llength $args] == 0} {return} - } + if {[set n [lsearch -exact $args -background]] != -1} { + set args [lreplace $args $n [incr n]] + if {[llength $args] == 0} {return} } return [uplevel 1 [list _$w $cmd] $args] } diff --git a/git-gui/lib/tools_dlg.tcl b/git-gui/lib/tools_dlg.tcl index c05413ce43..73236215b5 100644 --- a/git-gui/lib/tools_dlg.tcl +++ b/git-gui/lib/tools_dlg.tcl @@ -16,7 +16,7 @@ field ask_branch 0; # ask for a revision field ask_args 0; # ask for additional args constructor dialog {} { - global repo_config use_ttk NS + global repo_config make_dialog top w wm title $top [mc "%s (%s): Add Tool" [appname] [reponame]] @@ -25,41 +25,41 @@ constructor dialog {} { wm transient $top . } - ${NS}::label $w.header -text [mc "Add New Tool Command"] \ + ttk::label $w.header -text [mc "Add New Tool Command"] \ -font font_uibold -anchor center pack $w.header -side top -fill x - ${NS}::frame $w.buttons - ${NS}::checkbutton $w.buttons.global \ + ttk::frame $w.buttons + ttk::checkbutton $w.buttons.global \ -text [mc "Add globally"] \ -variable @add_global pack $w.buttons.global -side left -padx 5 - ${NS}::button $w.buttons.create -text [mc Add] \ + ttk::button $w.buttons.create -text [mc Add] \ -default active \ -command [cb _add] pack $w.buttons.create -side right - ${NS}::button $w.buttons.cancel -text [mc Cancel] \ + ttk::button $w.buttons.cancel -text [mc Cancel] \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - ${NS}::labelframe $w.desc -text [mc "Tool Details"] + ttk::labelframe $w.desc -text [mc "Tool Details"] - ${NS}::label $w.desc.name_cmnt -anchor w\ + ttk::label $w.desc.name_cmnt -anchor w\ -text [mc "Use '/' separators to create a submenu tree:"] grid x $w.desc.name_cmnt -sticky we -padx {0 5} -pady {0 2} - ${NS}::label $w.desc.name_l -text [mc "Name:"] + ttk::label $w.desc.name_l -text [mc "Name:"] set w_name $w.desc.name_t - ${NS}::entry $w_name \ + ttk::entry $w_name \ -width 40 \ -textvariable @name \ -validate key \ -validatecommand [cb _validate_name %d %S] grid $w.desc.name_l $w_name -sticky we -padx {0 5} - ${NS}::label $w.desc.cmd_l -text [mc "Command:"] + ttk::label $w.desc.cmd_l -text [mc "Command:"] set w_cmd $w.desc.cmd_t - ${NS}::entry $w_cmd \ + ttk::entry $w_cmd \ -width 40 \ -textvariable @command grid $w.desc.cmd_l $w_cmd -sticky we -padx {0 5} -pady {0 3} @@ -67,30 +67,30 @@ constructor dialog {} { grid columnconfigure $w.desc 1 -weight 1 pack $w.desc -anchor nw -fill x -pady 5 -padx 5 - ${NS}::checkbutton $w.confirm \ + ttk::checkbutton $w.confirm \ -text [mc "Show a dialog before running"] \ -variable @confirm -command [cb _check_enable_dlg] - ${NS}::labelframe $w.dlg -labelwidget $w.confirm + ttk::labelframe $w.dlg -labelwidget $w.confirm - ${NS}::checkbutton $w.dlg.askbranch \ + ttk::checkbutton $w.dlg.askbranch \ -text [mc "Ask the user to select a revision (sets \$REVISION)"] \ -variable @ask_branch -state disabled pack $w.dlg.askbranch -anchor w -padx 15 - ${NS}::checkbutton $w.dlg.askargs \ + ttk::checkbutton $w.dlg.askargs \ -text [mc "Ask the user for additional arguments (sets \$ARGS)"] \ -variable @ask_args -state disabled pack $w.dlg.askargs -anchor w -padx 15 pack $w.dlg -anchor nw -fill x -pady {0 8} -padx 5 - ${NS}::checkbutton $w.noconsole \ + ttk::checkbutton $w.noconsole \ -text [mc "Don't show the command output window"] \ -variable @no_console pack $w.noconsole -anchor w -padx 5 - ${NS}::checkbutton $w.needsfile \ + ttk::checkbutton $w.needsfile \ -text [mc "Run only if a diff is selected (\$FILENAME not empty)"] \ -variable @needs_file pack $w.needsfile -anchor w -padx 5 @@ -179,7 +179,7 @@ field w ; # widget path field w_names ; # name list constructor dialog {} { - global repo_config global_config system_config use_ttk NS + global repo_config global_config system_config load_config 1 @@ -190,21 +190,21 @@ constructor dialog {} { wm transient $top . } - ${NS}::label $w.header -text [mc "Remove Tool Commands"] \ + ttk::label $w.header -text [mc "Remove Tool Commands"] \ -font font_uibold -anchor center pack $w.header -side top -fill x - ${NS}::frame $w.buttons - ${NS}::button $w.buttons.create -text [mc Remove] \ + ttk::frame $w.buttons + ttk::button $w.buttons.create -text [mc Remove] \ -default active \ -command [cb _remove] pack $w.buttons.create -side right - ${NS}::button $w.buttons.cancel -text [mc Cancel] \ + ttk::button $w.buttons.cancel -text [mc Cancel] \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - ${NS}::frame $w.list + ttk::frame $w.list set w_names $w.list.l slistbox $w_names \ -height 10 \ @@ -227,7 +227,7 @@ constructor dialog {} { } if {$local_cnt > 0} { - ${NS}::label $w.colorlbl -foreground blue \ + ttk::label $w.colorlbl -foreground blue \ -text [mc "(Blue denotes repository-local tools)"] pack $w.colorlbl -fill x -pady 5 -padx 5 } @@ -272,7 +272,7 @@ field is_ok 0; # ok to start field argstr {}; # arguments constructor dialog {fullname} { - global M1B use_ttk NS + global M1B set title [get_config "guitool.$fullname.title"] if {$title eq {}} { @@ -292,7 +292,7 @@ constructor dialog {fullname} { set prompt [mc "Run Command: %s" $command] } - ${NS}::label $w.header -text $prompt -font font_uibold -anchor center + ttk::label $w.header -text $prompt -font font_uibold -anchor center pack $w.header -side top -fill x set argprompt [get_config "guitool.$fullname.argprompt"] @@ -306,10 +306,10 @@ constructor dialog {fullname} { set argprompt [mc "Arguments"] } - ${NS}::labelframe $w.arg -text $argprompt + ttk::labelframe $w.arg -text $argprompt set w_args $w.arg.txt - ${NS}::entry $w_args \ + ttk::entry $w_args \ -width 40 \ -textvariable @argstr pack $w_args -padx 5 -pady 5 -fill both @@ -330,18 +330,18 @@ constructor dialog {fullname} { pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5 } - ${NS}::frame $w.buttons + ttk::frame $w.buttons if {$is_ask_revs} { - ${NS}::button $w.buttons.visualize \ + ttk::button $w.buttons.visualize \ -text [mc Visualize] \ -command [cb _visualize] pack $w.buttons.visualize -side left } - ${NS}::button $w.buttons.ok \ + ttk::button $w.buttons.ok \ -text [mc OK] \ -command [cb _start] pack $w.buttons.ok -side right - ${NS}::button $w.buttons.cancel \ + ttk::button $w.buttons.cancel \ -text [mc "Cancel"] \ -command [cb _cancel] pack $w.buttons.cancel -side right -padx 5 diff --git a/git-gui/lib/transport.tcl b/git-gui/lib/transport.tcl index a1a424aab5..020d09e112 100644 --- a/git-gui/lib/transport.tcl +++ b/git-gui/lib/transport.tcl @@ -120,7 +120,7 @@ trace add variable push_remote write \ proc do_push_anywhere {} { global all_remotes current_branch global push_urltype push_remote push_url push_thin push_tags - global push_force use_ttk NS + global push_force set w .push_setup toplevel $w @@ -129,22 +129,22 @@ proc do_push_anywhere {} { wm geometry $w "+[winfo rootx .]+[winfo rooty .]" pave_toplevel $w - ${NS}::label $w.header -text [mc "Push Branches"] \ + ttk::label $w.header -text [mc "Push Branches"] \ -font font_uibold -anchor center pack $w.header -side top -fill x - ${NS}::frame $w.buttons - ${NS}::button $w.buttons.create -text [mc Push] \ + ttk::frame $w.buttons + ttk::button $w.buttons.create -text [mc Push] \ -default active \ -command [list start_push_anywhere_action $w] pack $w.buttons.create -side right - ${NS}::button $w.buttons.cancel -text [mc "Cancel"] \ + ttk::button $w.buttons.cancel -text [mc "Cancel"] \ -default normal \ -command [list destroy $w] pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - ${NS}::labelframe $w.source -text [mc "Source Branches"] + ttk::labelframe $w.source -text [mc "Source Branches"] slistbox $w.source.l \ -height 10 \ -width 70 \ @@ -159,20 +159,16 @@ proc do_push_anywhere {} { pack $w.source.l -side left -fill both -expand 1 pack $w.source -fill both -expand 1 -pady 5 -padx 5 - ${NS}::labelframe $w.dest -text [mc "Destination Repository"] + ttk::labelframe $w.dest -text [mc "Destination Repository"] if {$all_remotes ne {}} { - ${NS}::radiobutton $w.dest.remote_r \ + ttk::radiobutton $w.dest.remote_r \ -text [mc "Remote:"] \ -value remote \ -variable push_urltype - if {$use_ttk} { - ttk::combobox $w.dest.remote_m -state readonly \ - -exportselection false \ - -textvariable push_remote \ - -values $all_remotes - } else { - eval tk_optionMenu $w.dest.remote_m push_remote $all_remotes - } + ttk::combobox $w.dest.remote_m -state readonly \ + -exportselection false \ + -textvariable push_remote \ + -values $all_remotes grid $w.dest.remote_r $w.dest.remote_m -sticky w if {[lsearch -sorted -exact $all_remotes origin] != -1} { set push_remote origin @@ -183,11 +179,11 @@ proc do_push_anywhere {} { } else { set push_urltype url } - ${NS}::radiobutton $w.dest.url_r \ + ttk::radiobutton $w.dest.url_r \ -text [mc "Arbitrary Location:"] \ -value url \ -variable push_urltype - ${NS}::entry $w.dest.url_t \ + ttk::entry $w.dest.url_t \ -width 50 \ -textvariable push_url \ -validate key \ @@ -202,16 +198,16 @@ proc do_push_anywhere {} { grid columnconfigure $w.dest 1 -weight 1 pack $w.dest -anchor nw -fill x -pady 5 -padx 5 - ${NS}::labelframe $w.options -text [mc "Transfer Options"] - ${NS}::checkbutton $w.options.force \ + ttk::labelframe $w.options -text [mc "Transfer Options"] + ttk::checkbutton $w.options.force \ -text [mc "Force overwrite existing branch (may discard changes)"] \ -variable push_force grid $w.options.force -columnspan 2 -sticky w - ${NS}::checkbutton $w.options.thin \ + ttk::checkbutton $w.options.thin \ -text [mc "Use thin pack (for slow network connections)"] \ -variable push_thin grid $w.options.thin -columnspan 2 -sticky w - ${NS}::checkbutton $w.options.tags \ + ttk::checkbutton $w.options.tags \ -text [mc "Include tags"] \ -variable push_tags grid $w.options.tags -columnspan 2 -sticky w diff --git a/git-gui/macosx/AppMain.tcl b/git-gui/macosx/AppMain.tcl deleted file mode 100644 index b6c6dc3500..0000000000 --- a/git-gui/macosx/AppMain.tcl +++ /dev/null @@ -1,29 +0,0 @@ -set gitexecdir {@@gitexecdir@@} -if { [info exists ::env(GIT_GUI_LIB_DIR) ] } { - set gitguilib $::env(GIT_GUI_LIB_DIR) -} else { - set gitguilib {@@GITGUI_LIBDIR@@} -} - -set env(PATH) "$gitexecdir:$env(PATH)" - -if {[string first -psn [lindex $argv 0]] == 0} { - lset argv 0 [file join $gitexecdir git-gui] -} - -if {[file tail [lindex $argv 0]] eq {gitk}} { - set argv0 [lindex $argv 0] - set AppMain_source $argv0 -} else { - set argv0 [file join $gitexecdir [file tail [lindex $argv 0]]] - set AppMain_source [file join $gitguilib git-gui.tcl] - if {[info exists env(PWD)]} { - cd $env(PWD) - } elseif {[pwd] eq {/}} { - cd $env(HOME) - } -} - -unset gitexecdir gitguilib -set argv [lrange $argv 1 end] -source $AppMain_source diff --git a/git-gui/macosx/Info.plist b/git-gui/macosx/Info.plist deleted file mode 100644 index 1ade121c4c..0000000000 --- a/git-gui/macosx/Info.plist +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>CFBundleDevelopmentRegion</key> - <string>English</string> - <key>CFBundleExecutable</key> - <string>@@GITGUI_TKEXECUTABLE@@</string> - <key>CFBundleGetInfoString</key> - <string>Git Gui @@GITGUI_VERSION@@ © 2006-2007 Shawn Pearce, et. al.</string> - <key>CFBundleIconFile</key> - <string>git-gui.icns</string> - <key>CFBundleIdentifier</key> - <string>cz.or.repo.git-gui</string> - <key>CFBundleInfoDictionaryVersion</key> - <string>6.0</string> - <key>CFBundleName</key> - <string>Git Gui</string> - <key>CFBundlePackageType</key> - <string>APPL</string> - <key>CFBundleShortVersionString</key> - <string>@@GITGUI_VERSION@@</string> - <key>CFBundleSignature</key> - <string>GITg</string> - <key>CFBundleVersion</key> - <string>@@GITGUI_VERSION@@</string> - <key>NSHighResolutionCapable</key> - <true/> -</dict> -</plist> diff --git a/git-gui/macosx/git-gui.icns b/git-gui/macosx/git-gui.icns Binary files differdeleted file mode 100644 index 77d88a77a7..0000000000 --- a/git-gui/macosx/git-gui.icns +++ /dev/null diff --git a/git-gui/meson.build b/git-gui/meson.build index cdae85e4b9..320ba09ecf 100644 --- a/git-gui/meson.build +++ b/git-gui/meson.build @@ -19,17 +19,6 @@ build_options_config.set_quoted('GITGUI_LIBDIR', get_option('prefix') / get_opti build_options_config.set_quoted('SHELL_PATH', fs.as_posix(shell.full_path())) build_options_config.set_quoted('TCLTK_PATH', fs.as_posix(wish.full_path())) build_options_config.set_quoted('TCL_PATH', fs.as_posix(tclsh.full_path())) -if target_machine.system() == 'darwin' - tkexecutables = [ - '/Library/Frameworks/Tk.framework/Resources/Wish.app/Contents/MacOS/Wish', - '/System/Library/Frameworks/Tk.framework/Resources/Wish.app/Contents/MacOS/Wish', - '/System/Library/Frameworks/Tk.framework/Resources/Wish Shell.app/Contents/MacOS/Wish Shell', - ] - tkexecutable = find_program(tkexecutables) - build_options_config.set_quoted('TKEXECUTABLE', tkexecutable.full_path()) -else - build_options_config.set('TKEXECUTABLE', '') -endif build_options = configure_file( input: 'GIT-GUI-BUILD-OPTIONS.in', @@ -49,14 +38,6 @@ version_file = custom_target( build_always_stale: true, ) -configure_file( - input: 'git-gui--askpass', - output: 'git-gui--askpass', - copy: true, - install: true, - install_dir: get_option('libexecdir') / 'git-core', -) - gitgui_main = 'git-gui' gitgui_main_install_dir = get_option('libexecdir') / 'git-core' @@ -70,55 +51,23 @@ if target_machine.system() == 'windows' install: true, install_dir: get_option('libexecdir') / 'git-core', ) -elif target_machine.system() == 'darwin' - gitgui_main = 'git-gui.tcl' - gitgui_main_install_dir = get_option('datadir') / 'git-gui/lib' - - custom_target( - output: 'git-gui', - command: [ - shell, - meson.current_source_dir() / 'generate-macos-wrapper.sh', - '@OUTPUT@', - meson.current_build_dir() / 'GIT-GUI-BUILD-OPTIONS', - meson.current_build_dir() / 'GIT-VERSION-FILE', - ], - depends: [ - version_file, - ], - depend_files: [ - build_options, - ], - install: true, - install_dir: get_option('libexecdir') / 'git-core', - ) - - custom_target( - output: 'Git Gui.app', - command: [ - shell, - meson.current_source_dir() / 'generate-macos-app.sh', - meson.current_source_dir(), - meson.current_build_dir() / 'Git Gui.app', - meson.current_build_dir() / 'GIT-GUI-BUILD-OPTIONS', - meson.current_build_dir() / 'GIT-VERSION-FILE', - ], - depends: [ - version_file, - ], - depend_files: [ - build_options, - 'macosx/AppMain.tcl', - 'macosx/Info.plist', - 'macosx/git-gui.icns', - ], - build_by_default: true, - install: true, - install_dir: get_option('datadir') / 'git-gui/lib', - ) 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', +) + +custom_target( input: 'git-gui.sh', output: gitgui_main, command: [ diff --git a/git-gui/po/bg.po b/git-gui/po/bg.po index 27b05038e4..21f5bd54e9 100644 --- a/git-gui/po/bg.po +++ b/git-gui/po/bg.po @@ -1,15 +1,15 @@ # Bulgarian translation of git-gui po-file. -# Copyright (C) 2012, 2013, 2014, 2015, 2016, 2024 Alexander Shopov <ash@kambanaria.org>. +# Copyright (C) 2012, 2013, 2014, 2015, 2016, 2024, 2025 Alexander Shopov <ash@kambanaria.org>. # This file is distributed under the same license as the git package. -# Alexander Shopov <ash@kambanaria.org>, 2012, 2013, 2014, 2015, 2016, 2024. +# Alexander Shopov <ash@kambanaria.org>, 2012, 2013, 2014, 2015, 2016, 2024, 2025. # # msgid "" msgstr "" "Project-Id-Version: git-gui master\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-02-08 22:54+0100\n" -"PO-Revision-Date: 2024-12-22 15:44+0100\n" +"POT-Creation-Date: 2025-07-22 17:37+0200\n" +"PO-Revision-Date: 2025-07-28 11:56+0200\n" "Last-Translator: Alexander Shopov <ash@kambanaria.org>\n" "Language-Team: Bulgarian <dict@fsa-bg.org>\n" "Language: bg\n" @@ -18,88 +18,58 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: git-gui.sh:847 #, tcl-format msgid "Invalid font specified in %s:" msgstr "Указан е неправилен шрифт в „%s“:" -#: git-gui.sh:901 msgid "Main Font" msgstr "ОÑновен шрифт" -#: git-gui.sh:902 msgid "Diff/Console Font" msgstr "Шрифт за разликите/конзолата" -#: git-gui.sh:917 git-gui.sh:931 git-gui.sh:944 git-gui.sh:1034 git-gui.sh:1053 -#: git-gui.sh:3212 msgid "git-gui: fatal error" msgstr "git-gui: фатална грешка" -#: git-gui.sh:918 msgid "Cannot find git in PATH." msgstr "Командата git липÑва в Ð¿ÑŠÑ‚Ñ (PATH)." -#: git-gui.sh:945 msgid "Cannot parse Git version string:" msgstr "Ðизът Ñ Ð²ÐµÑ€ÑиÑта на Git не може да Ñе анализира:" -#: git-gui.sh:970 -#, tcl-format -msgid "" -"Git version cannot be determined.\n" -"\n" -"%s claims it is version '%s'.\n" -"\n" -"%s requires at least Git 1.5.0 or later.\n" -"\n" -"Assume '%s' is version 1.5.0?\n" -msgstr "" -"ВерÑиÑта на Git не може да Ñе определи.\n" -"\n" -"ВерÑиÑта на „%s“ изглежда, че е „%s“.\n" -"\n" -"„%s“ изиÑква Git, верÑÐ¸Ñ Ð¿Ð¾Ð½Ðµ 1.5.0.\n" -"\n" -"Да Ñе приеме ли, че „%s“ е верÑÐ¸Ñ â€ž1.5.0“?\n" +msgid "Insufficient git version, require: " +msgstr "Прекалено ниÑка верÑÐ¸Ñ Ð½Ð° git, необходима е поне: " + +msgid "git returned:" +msgstr "git върна:" -#: git-gui.sh:1267 msgid "Git directory not found:" msgstr "ДиректориÑта на Git не е открита:" -#: git-gui.sh:1301 msgid "Cannot move to top of working directory:" msgstr "Ðе може да Ñе премине към родителÑката директориÑ." -#: git-gui.sh:1309 msgid "Cannot use bare repository:" msgstr "Голо хранилище не може да Ñе използва:" -#: git-gui.sh:1317 msgid "No working directory" msgstr "Работната Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ Ð»Ð¸Ð¿Ñва" -#: git-gui.sh:1491 lib/checkout_op.tcl:306 msgid "Refreshing file status..." msgstr "ОбновÑване на ÑÑŠÑтоÑнието на файла…" -#: git-gui.sh:1551 msgid "Scanning for modified files ..." msgstr "Проверка за променени файлове…" -#: git-gui.sh:1629 msgid "Calling prepare-commit-msg hook..." msgstr "Куката „prepare-commit-msg“ Ñе изпълнÑва в момента…" -#: git-gui.sh:1646 msgid "Commit declined by prepare-commit-msg hook." msgstr "Подаването е отхвърлено от куката „prepare-commit-msg“." -#: git-gui.sh:1804 lib/browser.tcl:252 msgid "Ready." msgstr "Готово." -#: git-gui.sh:1968 #, tcl-format msgid "" "Display limit (gui.maxfilesdisplayed = %s) reached, not showing all %s files." @@ -108,696 +78,630 @@ msgstr "" "извеждане(gui.maxfilesdisplayed = %s), Ñъответно не Ñа показани вÑички %s " "файла." -#: git-gui.sh:2091 msgid "Unmodified" msgstr "Ðепроменен" -#: git-gui.sh:2093 msgid "Modified, not staged" msgstr "Променен, но не е в индекÑа" -#: git-gui.sh:2094 git-gui.sh:2106 msgid "Staged for commit" msgstr "Ð’ индекÑа за подаване" -#: git-gui.sh:2095 git-gui.sh:2107 msgid "Portions staged for commit" msgstr "ЧаÑти Ñа в индекÑа за подаване" -#: git-gui.sh:2096 git-gui.sh:2108 msgid "Staged for commit, missing" msgstr "Ð’ индекÑа за подаване, но липÑва" -#: git-gui.sh:2098 msgid "File type changed, not staged" msgstr "Видът на файла е Ñменен, но не е в индекÑа" -#: git-gui.sh:2099 git-gui.sh:2100 msgid "File type changed, old type staged for commit" msgstr "Видът на файла е Ñменен, но новиÑÑ‚ вид не е в индекÑа" -#: git-gui.sh:2101 msgid "File type changed, staged" msgstr "Видът на файла е Ñменен и е в индекÑа" -#: git-gui.sh:2102 msgid "File type change staged, modification not staged" msgstr "Видът на файла е Ñменен в индекÑа, но не и Ñъдържанието" -#: git-gui.sh:2103 msgid "File type change staged, file missing" msgstr "Видът на файла е Ñменен в индекÑа, но файлът липÑва" -#: git-gui.sh:2105 msgid "Untracked, not staged" msgstr "ÐеÑледен" -#: git-gui.sh:2110 msgid "Missing" msgstr "ЛипÑващ" -#: git-gui.sh:2111 msgid "Staged for removal" msgstr "Ð’ индекÑа за изтриване" -#: git-gui.sh:2112 msgid "Staged for removal, still present" msgstr "Ð’ индекÑа за изтриване, но още го има" -#: git-gui.sh:2114 git-gui.sh:2115 git-gui.sh:2116 git-gui.sh:2117 -#: git-gui.sh:2118 git-gui.sh:2119 msgid "Requires merge resolution" msgstr "ИзиÑква коригиране при Ñливане" -#: git-gui.sh:2164 msgid "Couldn't find gitk in PATH" msgstr "Командата „gitk“ липÑва в пътищата, определени от променливата PATH." -#: git-gui.sh:2210 git-gui.sh:2245 #, tcl-format msgid "Starting %s... please wait..." msgstr "Стартиране на „%s“…, изчакайте…" -#: git-gui.sh:2224 msgid "Couldn't find git gui in PATH" msgstr "" "Командата „git gui“ липÑва в пътищата, определени от променливата PATH." -#: git-gui.sh:2726 lib/choose_repository.tcl:53 msgid "Repository" msgstr "Хранилище" -#: git-gui.sh:2727 msgid "Edit" msgstr "Редактиране" -#: git-gui.sh:2729 lib/choose_rev.tcl:567 msgid "Branch" msgstr "Клон" -#: git-gui.sh:2732 lib/choose_rev.tcl:554 msgid "Commit@@noun" msgstr "Подаване" -#: git-gui.sh:2735 lib/merge.tcl:127 lib/merge.tcl:174 msgid "Merge" msgstr "Сливане" -#: git-gui.sh:2736 lib/choose_rev.tcl:563 msgid "Remote" msgstr "Отдалечено хранилище" -#: git-gui.sh:2739 msgid "Tools" msgstr "Команди" -#: git-gui.sh:2748 msgid "Explore Working Copy" msgstr "Разглеждане на работното копие" -#: git-gui.sh:2763 msgid "Git Bash" msgstr "Bash за Git" -#: git-gui.sh:2772 msgid "Browse Current Branch's Files" msgstr "Разглеждане на файловете в Ñ‚ÐµÐºÑƒÑ‰Ð¸Ñ ÐºÐ»Ð¾Ð½" -#: git-gui.sh:2776 msgid "Browse Branch Files..." msgstr "Разглеждане на Ñ‚ÐµÐºÑƒÑ‰Ð¸Ñ ÐºÐ»Ð¾Ð½â€¦" -#: git-gui.sh:2781 msgid "Visualize Current Branch's History" msgstr "Ð’Ð¸Ð·ÑƒÐ°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð½Ð° иÑториÑта на Ñ‚ÐµÐºÑƒÑ‰Ð¸Ñ ÐºÐ»Ð¾Ð½" -#: git-gui.sh:2785 msgid "Visualize All Branch History" msgstr "Ð’Ð¸Ð·ÑƒÐ°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð½Ð° иÑториÑта на вÑички клонове" -#: git-gui.sh:2792 #, tcl-format msgid "Browse %s's Files" msgstr "Разглеждане на файловете в „%s“" -#: git-gui.sh:2794 #, tcl-format msgid "Visualize %s's History" msgstr "Ð’Ð¸Ð·ÑƒÐ°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð½Ð° иÑториÑта на „%s“" -#: git-gui.sh:2799 lib/database.tcl:40 msgid "Database Statistics" msgstr "СтатиÑтика на базата от данни" -#: git-gui.sh:2802 lib/database.tcl:33 msgid "Compress Database" msgstr "КомпреÑиране на базата от данни" -#: git-gui.sh:2805 msgid "Verify Database" msgstr "Проверка на базата от данни" -#: git-gui.sh:2812 git-gui.sh:2816 git-gui.sh:2820 msgid "Create Desktop Icon" msgstr "ДобавÑне на икона на Ñ€Ð°Ð±Ð¾Ñ‚Ð½Ð¸Ñ Ð¿Ð»Ð¾Ñ‚" -#: git-gui.sh:2828 lib/choose_repository.tcl:209 lib/choose_repository.tcl:217 msgid "Quit" msgstr "Спиране на програмата" -#: git-gui.sh:2836 msgid "Undo" msgstr "ОтмÑна" -#: git-gui.sh:2839 msgid "Redo" msgstr "Повторение" -#: git-gui.sh:2843 git-gui.sh:3461 msgid "Cut" msgstr "ОтрÑзване" -#: git-gui.sh:2846 git-gui.sh:3464 git-gui.sh:3540 git-gui.sh:3633 -#: lib/console.tcl:69 msgid "Copy" msgstr "Копиране" -#: git-gui.sh:2849 git-gui.sh:3467 msgid "Paste" msgstr "ПоÑтавÑне" -#: git-gui.sh:2852 git-gui.sh:3470 lib/remote_branch_delete.tcl:39 -#: lib/branch_delete.tcl:28 msgid "Delete" msgstr "Изтриване" -#: git-gui.sh:2856 git-gui.sh:3474 git-gui.sh:3637 lib/console.tcl:71 msgid "Select All" msgstr "Избиране на вÑичко" -#: git-gui.sh:2865 msgid "Create..." msgstr "Създаване…" -#: git-gui.sh:2871 msgid "Checkout..." msgstr "ИзтеглÑне…" -#: git-gui.sh:2877 msgid "Rename..." msgstr "Преименуване…" -#: git-gui.sh:2882 msgid "Delete..." msgstr "Изтриване…" -#: git-gui.sh:2887 msgid "Reset..." msgstr "ОтмÑна на промените…" -#: git-gui.sh:2897 msgid "Done" msgstr "Готово" -#: git-gui.sh:2899 msgid "Commit@@verb" msgstr "Подаване" -#: git-gui.sh:2908 git-gui.sh:3400 msgid "Amend Last Commit" msgstr "ПоправÑне на поÑледното подаване" -#: git-gui.sh:2918 git-gui.sh:3361 lib/remote_branch_delete.tcl:101 msgid "Rescan" msgstr "ОбновÑване" -#: git-gui.sh:2924 msgid "Stage To Commit" msgstr "Към индекÑа за подаване" -#: git-gui.sh:2930 msgid "Stage Changed Files To Commit" msgstr "Ð’Ñички променени файлове към индекÑа за подаване" -#: git-gui.sh:2936 msgid "Unstage From Commit" msgstr "Изваждане от индекÑа за подаване" -#: git-gui.sh:2942 lib/index.tcl:521 msgid "Revert Changes" msgstr "Връщане на оригинала" -#: git-gui.sh:2950 git-gui.sh:3700 git-gui.sh:3731 msgid "Show Less Context" msgstr "По-малко контекÑÑ‚" -#: git-gui.sh:2954 git-gui.sh:3704 git-gui.sh:3735 msgid "Show More Context" msgstr "Повече контекÑÑ‚" -#: git-gui.sh:2961 git-gui.sh:3374 git-gui.sh:3485 msgid "Sign Off" msgstr "ПодпиÑване" -#: git-gui.sh:2977 msgid "Local Merge..." msgstr "Локално Ñливане…" -#: git-gui.sh:2982 msgid "Abort Merge..." msgstr "ПреуÑтановÑване на Ñливане…" -#: git-gui.sh:2994 git-gui.sh:3022 msgid "Add..." msgstr "ДобавÑне…" -#: git-gui.sh:2998 msgid "Push..." msgstr "ИзтлаÑкване…" -#: git-gui.sh:3002 msgid "Delete Branch..." msgstr "Изтриване на клон…" -#: git-gui.sh:3012 git-gui.sh:3666 msgid "Options..." msgstr "Опции…" -#: git-gui.sh:3023 msgid "Remove..." msgstr "Премахване…" -#: git-gui.sh:3032 lib/choose_repository.tcl:67 msgid "Help" msgstr "Помощ" -#: git-gui.sh:3036 git-gui.sh:3040 lib/choose_repository.tcl:61 -#: lib/choose_repository.tcl:70 lib/about.tcl:14 #, tcl-format msgid "About %s" msgstr "ОтноÑно „%s“" -#: git-gui.sh:3064 msgid "Online Documentation" msgstr "Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ð¸Ñ Ð² Интернет" -#: git-gui.sh:3067 lib/choose_repository.tcl:64 lib/choose_repository.tcl:73 msgid "Show SSH Key" msgstr "Показване на ключа за SSH" -#: git-gui.sh:3097 git-gui.sh:3229 msgid "usage:" msgstr "употреба:" -#: git-gui.sh:3101 git-gui.sh:3233 msgid "Usage" msgstr "Употреба" -#: git-gui.sh:3182 lib/blame.tcl:575 msgid "Error" msgstr "Грешка" -#: git-gui.sh:3213 #, tcl-format msgid "fatal: cannot stat path %s: No such file or directory" msgstr "ФÐТÐЛÐРГРЕШКÐ: пътÑÑ‚ „%s“ липÑва: такъв файл или Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ Ð½Ñма" -#: git-gui.sh:3246 msgid "Current Branch:" msgstr "Текущ клон:" -#: git-gui.sh:3271 msgid "Unstaged Changes" msgstr "Промени извън индекÑа" -#: git-gui.sh:3293 msgid "Staged Changes (Will Commit)" msgstr "Промени в индекÑа (за подаване)" -#: git-gui.sh:3367 msgid "Stage Changed" msgstr "ИндекÑÑŠÑ‚ е променен" -#: git-gui.sh:3386 lib/transport.tcl:137 msgid "Push" msgstr "ИзтлаÑкване" -#: git-gui.sh:3413 msgid "Initial Commit Message:" msgstr "Първоначално Ñъобщение при подаване:" -#: git-gui.sh:3414 msgid "Amended Commit Message:" msgstr "Поправено Ñъобщение при подаване:" -#: git-gui.sh:3415 msgid "Amended Initial Commit Message:" msgstr "Поправено първоначално Ñъобщение при подаване:" -#: git-gui.sh:3416 msgid "Amended Merge Commit Message:" msgstr "Поправено Ñъобщение при подаване ÑÑŠÑ Ñливане:" -#: git-gui.sh:3417 msgid "Merge Commit Message:" msgstr "Съобщение при подаване ÑÑŠÑ Ñливане:" -#: git-gui.sh:3418 msgid "Commit Message:" msgstr "Съобщение при подаване:" -#: git-gui.sh:3477 git-gui.sh:3641 lib/console.tcl:73 msgid "Copy All" msgstr "Копиране на вÑичко" -#: git-gui.sh:3501 lib/blame.tcl:106 msgid "File:" msgstr "Файл:" -#: git-gui.sh:3549 lib/choose_repository.tcl:1100 msgid "Open" msgstr "ОтварÑне" -#: git-gui.sh:3629 msgid "Refresh" msgstr "ОбновÑване" -#: git-gui.sh:3650 msgid "Decrease Font Size" msgstr "По-дребен шрифт" -#: git-gui.sh:3654 msgid "Increase Font Size" msgstr "По-едър шрифт" -#: git-gui.sh:3662 lib/blame.tcl:296 msgid "Encoding" msgstr "Кодиране" -#: git-gui.sh:3673 msgid "Apply/Reverse Hunk" msgstr "Прилагане/връщане на парче" -#: git-gui.sh:3678 msgid "Apply/Reverse Line" msgstr "Прилагане/връщане на ред" -#: git-gui.sh:3684 git-gui.sh:3794 git-gui.sh:3805 msgid "Revert Hunk" msgstr "Връщане на парче" -#: git-gui.sh:3689 git-gui.sh:3801 git-gui.sh:3812 msgid "Revert Line" msgstr "Връщане на ред" -#: git-gui.sh:3694 git-gui.sh:3791 msgid "Undo Last Revert" msgstr "ОтмÑна на поÑледното връщане" -#: git-gui.sh:3713 msgid "Run Merge Tool" msgstr "Изпълнение на програмата за Ñливане" -#: git-gui.sh:3718 msgid "Use Remote Version" msgstr "ВерÑÐ¸Ñ Ð¾Ñ‚ отдалеченото хранилище" -#: git-gui.sh:3722 msgid "Use Local Version" msgstr "Локална верÑиÑ" -#: git-gui.sh:3726 msgid "Revert To Base" msgstr "Връщане към родителÑката верÑиÑ" -#: git-gui.sh:3744 msgid "Visualize These Changes In The Submodule" msgstr "Визуализиране на промените в подмодула" -#: git-gui.sh:3748 msgid "Visualize Current Branch History In The Submodule" msgstr "Ð’Ð¸Ð·ÑƒÐ°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð½Ð° иÑториÑта на Ñ‚ÐµÐºÑƒÑ‰Ð¸Ñ ÐºÐ»Ð¾Ð½ в иÑториÑта за подмодула" -#: git-gui.sh:3752 msgid "Visualize All Branch History In The Submodule" msgstr "Ð’Ð¸Ð·ÑƒÐ°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð½Ð° иÑториÑта на вÑички клони в иÑториÑта за подмодула" -#: git-gui.sh:3757 msgid "Start git gui In The Submodule" msgstr "Стартиране на „git gui“ за подмодула" -#: git-gui.sh:3793 msgid "Unstage Hunk From Commit" msgstr "Изваждане на парчето от подаването" -#: git-gui.sh:3797 msgid "Unstage Lines From Commit" msgstr "Изваждане на редовете от подаването" -#: git-gui.sh:3798 git-gui.sh:3809 msgid "Revert Lines" msgstr "Връщане на редовете" -#: git-gui.sh:3800 msgid "Unstage Line From Commit" msgstr "Изваждане на реда от подаването" -#: git-gui.sh:3804 msgid "Stage Hunk For Commit" msgstr "ДобавÑне на парчето за подаване" -#: git-gui.sh:3808 msgid "Stage Lines For Commit" msgstr "ДобавÑне на редовете за подаване" -#: git-gui.sh:3811 msgid "Stage Line For Commit" msgstr "ДобавÑне на реда за подаване" -#: git-gui.sh:3861 msgid "Initializing..." msgstr "Инициализиране…" -#: git-gui.sh:4017 +msgid "git-gui - a graphical user interface for Git." +msgstr "git-gui — графичен Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð·Ð° Git." + #, tcl-format -msgid "" -"Possible environment issues exist.\n" -"\n" -"The following environment variables are probably\n" -"going to be ignored by any Git subprocess run\n" -"by %s:\n" -"\n" -msgstr "" -"Възможно е да има проблем ÑÑŠÑ Ñредата.\n" -"\n" -"Ðай-вероÑтно Ñледните променливи нÑма да Ñе\n" -"вземат под внимание от подпроцеÑите на Git\n" -"от %s:\n" -"\n" +msgid "%s (%s): File Viewer" +msgstr "%s (%s): Преглед на файлове" -#: git-gui.sh:4046 -msgid "" -"\n" -"This is due to a known issue with the\n" -"Tcl binary distributed by Cygwin." -msgstr "" -"\n" -"Това е познат проблем и Ñе дължи на\n" -"верÑиÑта на Tcl включена в Cygwin." +msgid "Commit:" +msgstr "Подаване:" + +msgid "Copy Commit" +msgstr "Копиране на подаване" + +msgid "Find Text..." +msgstr "ТърÑене на текÑт…" + +msgid "Goto Line..." +msgstr "Към ред…" + +msgid "Do Full Copy Detection" +msgstr "Пълно търÑене на копиране" + +msgid "Show History Context" +msgstr "Показване на контекÑта от иÑториÑта" + +msgid "Blame Parent Commit" +msgstr "Ðнотиране на родителÑкото подаване" -#: git-gui.sh:4051 #, tcl-format -msgid "" -"\n" -"\n" -"A good replacement for %s\n" -"is placing values for the user.name and\n" -"user.email settings into your personal\n" -"~/.gitconfig file.\n" -msgstr "" -"\n" -"\n" -"Добър замеÑтител на „%s“\n" -"е да поÑтавите наÑтройките „user.name“ и\n" -"„user.email“ в Ð»Ð¸Ñ‡Ð½Ð¸Ñ Ñи файл „~/.gitconfig“.\n" +msgid "Reading %s..." +msgstr "Чете Ñе „%s“…" -#: lib/spellcheck.tcl:57 -msgid "Unsupported spell checker" -msgstr "Тази програма за проверка на правопиÑа не Ñе поддържа" +msgid "Loading copy/move tracking annotations..." +msgstr "Зареждане на анотациите за проÑледÑване на копирането/премеÑтването…" -#: lib/spellcheck.tcl:65 -msgid "Spell checking is unavailable" -msgstr "ЛипÑва програма за проверка на правопиÑа" +msgid "lines annotated" +msgstr "реда анотирани" -#: lib/spellcheck.tcl:68 -msgid "Invalid spell checking configuration" -msgstr "Ðеправилни наÑтройки на проверката на правопиÑа" +msgid "Loading original location annotations..." +msgstr "Зареждане на анотациите за първоначалното меÑтоположение…" + +msgid "Annotation complete." +msgstr "Ðнотирането завърши." + +msgid "Busy" +msgstr "ОперациÑта не е завършила" + +msgid "Annotation process is already running." +msgstr "Ð’ момента тече Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð½Ð° анотиране." + +msgid "Running thorough copy detection..." +msgstr "ИзпълнÑва Ñе цÑлоÑтен Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð½Ð° откриване на копиране…" + +msgid "Loading annotation..." +msgstr "Зареждане на анотации…" + +msgid "Author:" +msgstr "Ðвтор:" + +msgid "Committer:" +msgstr "Подал:" + +msgid "Original File:" +msgstr "Първоначален файл:" + +msgid "Cannot find HEAD commit:" +msgstr "Подаването за връх „HEAD“ не може да Ñе открие:" + +msgid "Cannot find parent commit:" +msgstr "РодителÑкото подаване не може да Ñе открие" + +msgid "Unable to display parent" +msgstr "РодителÑÑ‚ не може да Ñе покаже" + +msgid "Error loading diff:" +msgstr "Грешка при зареждане на разлика:" + +msgid "Originally By:" +msgstr "Първоначално от:" + +msgid "In File:" +msgstr "Във файл:" + +msgid "Copied Or Moved Here By:" +msgstr "Копирано или премеÑтено тук от:" -#: lib/spellcheck.tcl:70 #, tcl-format -msgid "Reverting dictionary to %s." -msgstr "Ползване на речник за език „%s“." +msgid "%s (%s): Checkout Branch" +msgstr "%s (%s): Клон за изтеглÑне" -#: lib/spellcheck.tcl:73 -msgid "Spell checker silently failed on startup" -msgstr "Програмата за Ð¿Ñ€Ð°Ð²Ð¾Ð¿Ð¸Ñ Ð´Ð°Ð¶Ðµ не Ñтартира уÑпешно." +msgid "Checkout Branch" +msgstr "Клон за изтеглÑне" -#: lib/spellcheck.tcl:80 -msgid "Unrecognized spell checker" -msgstr "Ðепозната програма за проверка на правопиÑа" +msgid "Checkout" +msgstr "ИзтеглÑне" -#: lib/spellcheck.tcl:186 -msgid "No Suggestions" -msgstr "ÐÑма предложениÑ" +msgid "Cancel" +msgstr "Отказване" -#: lib/spellcheck.tcl:388 -msgid "Unexpected EOF from spell checker" -msgstr "Ðеочакван край на файл от програмата за проверка на правопиÑа" +msgid "Revision" +msgstr "ВерÑиÑ" -#: lib/spellcheck.tcl:392 -msgid "Spell Checker Failed" -msgstr "Грешка в програмата за проверка на правопиÑа" +msgid "Options" +msgstr "Опции" + +msgid "Fetch Tracking Branch" +msgstr "ИзтеглÑне на промените от ÑÐ»ÐµÐ´ÐµÐ½Ð¸Ñ ÐºÐ»Ð¾Ð½" + +msgid "Detach From Local Branch" +msgstr "Изтриване от Ð»Ð¾ÐºÐ°Ð»Ð½Ð¸Ñ ÐºÐ»Ð¾Ð½" -#: lib/transport.tcl:6 lib/remote_add.tcl:132 #, tcl-format -msgid "fetch %s" -msgstr "доÑтавÑне на „%s“" +msgid "%s (%s): Create Branch" +msgstr "%s (%s): Създаване на клон" + +msgid "Create New Branch" +msgstr "Създаване на нов клон" + +msgid "Create" +msgstr "Създаване" + +msgid "Branch Name" +msgstr "Име на клона" + +msgid "Name:" +msgstr "Име:" + +msgid "Match Tracking Branch Name" +msgstr "Съвпадане по името на ÑÐ»ÐµÐ´ÐµÐ½Ð¸Ñ ÐºÐ»Ð¾Ð½" + +msgid "Starting Revision" +msgstr "Ðачална верÑиÑ" + +msgid "Update Existing Branch:" +msgstr "ОбновÑване на ÑъщеÑтвуващ клон:" + +msgid "No" +msgstr "Ðе" + +msgid "Fast Forward Only" +msgstr "Само тривиално превъртащо Ñливане" + +msgid "Reset" +msgstr "Отначало" + +msgid "Checkout After Creation" +msgstr "Преминаване към клона Ñлед Ñъздаването му" + +msgid "Please select a tracking branch." +msgstr "Изберете клон за Ñледени." -#: lib/transport.tcl:7 #, tcl-format -msgid "Fetching new changes from %s" -msgstr "ДоÑтавÑне на промените от „%s“" +msgid "Tracking branch %s is not a branch in the remote repository." +msgstr "СледÑщиÑÑ‚ клон — „%s“, не ÑъщеÑтвува в отдалеченото хранилище." + +msgid "Please supply a branch name." +msgstr "Дайте име на клона." -#: lib/transport.tcl:18 #, tcl-format -msgid "remote prune %s" -msgstr "окаÑтрÑне на ÑледÑщите клони към „%s“" +msgid "'%s' is not an acceptable branch name." +msgstr "„%s“ не може да Ñе използва за име на клон." -#: lib/transport.tcl:19 #, tcl-format -msgid "Pruning tracking branches deleted from %s" -msgstr "ОкаÑтрÑне на ÑледÑщите клони на изтритите клони от „%s“" +msgid "%s (%s): Delete Branch" +msgstr "%s (%s): Изтриване на клон" -#: lib/transport.tcl:25 -msgid "fetch all remotes" -msgstr "доÑтавÑне от вÑички отдалечени" +msgid "Delete Local Branch" +msgstr "Изтриване на локален клон" -#: lib/transport.tcl:26 -msgid "Fetching new changes from all remotes" -msgstr "ДоÑтавÑне на промените от вÑички отдалечени хранилища" +msgid "Local Branches" +msgstr "Локални клони" -#: lib/transport.tcl:40 -msgid "remote prune all remotes" -msgstr "окаÑтрÑне на ÑледÑщите изтрити" +msgid "Delete Only If Merged Into" +msgstr "Изтриване, Ñамо ако промените Ñа Ñлети и другаде" -#: lib/transport.tcl:41 -msgid "Pruning tracking branches deleted from all remotes" -msgstr "" -"ОкаÑтрÑне на ÑледÑщите клони на изтритите клони от вÑички отдалечени " -"хранилища" +msgid "Always (Do not perform merge checks)" +msgstr "Винаги (без проверка за Ñливане)" -#: lib/transport.tcl:54 lib/transport.tcl:92 lib/transport.tcl:110 -#: lib/remote_add.tcl:162 #, tcl-format -msgid "push %s" -msgstr "изтлаÑкване на „%s“" +msgid "The following branches are not completely merged into %s:" +msgstr "Ðе вÑички промени в клоните Ñа Ñлети в „%s“:" + +msgid "" +"Recovering deleted branches is difficult.\n" +"\n" +"Delete the selected branches?" +msgstr "" +"ВъзÑтановÑването на изтрити клони може да е трудно.\n" +"\n" +"Сигурни ли Ñте, че иÑкате да триете?" -#: lib/transport.tcl:55 #, tcl-format -msgid "Pushing changes to %s" -msgstr "ИзтлаÑкване на промените към „%s“" +msgid " - %s:" +msgstr " — „%s:“" -#: lib/transport.tcl:93 #, tcl-format -msgid "Mirroring to %s" -msgstr "ИзтлаÑкване на вÑичко към „%s“" +msgid "" +"Failed to delete branches:\n" +"%s" +msgstr "" +"ÐеуÑпешно триене на клони:\n" +"%s" -#: lib/transport.tcl:111 #, tcl-format -msgid "Pushing %s %s to %s" -msgstr "ИзтлаÑкване на %s „%sâ€œ към „%s“" +msgid "%s (%s): Rename Branch" +msgstr "%s (%s): Преименуване на клон" -#: lib/transport.tcl:132 -msgid "Push Branches" -msgstr "Клони за изтлаÑкване" +msgid "Rename Branch" +msgstr "Преименуване на клон" -#: lib/transport.tcl:141 lib/checkout_op.tcl:580 lib/remote_add.tcl:34 -#: lib/browser.tcl:292 lib/branch_checkout.tcl:30 lib/branch_rename.tcl:32 -#: lib/choose_font.tcl:45 lib/option.tcl:127 lib/tools_dlg.tcl:41 -#: lib/tools_dlg.tcl:202 lib/tools_dlg.tcl:345 lib/remote_branch_delete.tcl:43 -#: lib/branch_create.tcl:37 lib/branch_delete.tcl:34 lib/merge.tcl:178 -msgid "Cancel" -msgstr "Отказване" +msgid "Rename" +msgstr "Преименуване" -#: lib/transport.tcl:147 -msgid "Source Branches" -msgstr "Клони-източници" +msgid "Branch:" +msgstr "Клон:" -#: lib/transport.tcl:162 -msgid "Destination Repository" -msgstr "Целево хранилище" +msgid "New Name:" +msgstr "Ðово име:" -#: lib/transport.tcl:165 lib/remote_branch_delete.tcl:51 -msgid "Remote:" -msgstr "Отдалечено хранилище:" +msgid "Please select a branch to rename." +msgstr "Изберете клон за преименуване." -#: lib/transport.tcl:187 lib/remote_branch_delete.tcl:72 -msgid "Arbitrary Location:" -msgstr "Произволно меÑтоположение:" +#, tcl-format +msgid "Branch '%s' already exists." +msgstr "Клонът „%s“ вече ÑъщеÑтвува." -#: lib/transport.tcl:205 -msgid "Transfer Options" -msgstr "ÐаÑтройки при пренаÑÑнето" +#, tcl-format +msgid "Failed to rename '%s'." +msgstr "ÐеуÑпешно преименуване на „%s“." -#: lib/transport.tcl:207 -msgid "Force overwrite existing branch (may discard changes)" -msgstr "" -"Изрично презапиÑване на ÑъщеÑтвуващ клон (нÑкои промени може да Ñе загубÑÑ‚)" +msgid "Starting..." +msgstr "Стартиране…" -#: lib/transport.tcl:211 -msgid "Use thin pack (for slow network connections)" -msgstr "МакÑимална компреÑÐ¸Ñ (за бавни мрежови връзки)" +#, tcl-format +msgid "%s (%s): File Browser" +msgstr "%s (%s): Файлов браузър" -#: lib/transport.tcl:215 -msgid "Include tags" -msgstr "Включване на етикетите" +#, tcl-format +msgid "Loading %s..." +msgstr "Зареждане на „%s“…" + +msgid "[Up To Parent]" +msgstr "[Към родителÑ]" -#: lib/transport.tcl:229 #, tcl-format -msgid "%s (%s): Push" -msgstr "%s (%s): ИзтлаÑкване" +msgid "%s (%s): Browse Branch Files" +msgstr "%s (%s): Разглеждане на файловете в клона" + +msgid "Browse Branch Files" +msgstr "Разглеждане на файловете в клона" + +msgid "Browse" +msgstr "Разглеждане" -#: lib/checkout_op.tcl:85 #, tcl-format msgid "Fetching %s from %s" msgstr "ДоÑтавÑне на „%s“ от „%s“" -#: lib/checkout_op.tcl:133 #, tcl-format msgid "fatal: Cannot resolve %s" msgstr "фатална грешка: „%s“ не може да Ñе открие" -#: lib/checkout_op.tcl:146 lib/sshkey.tcl:58 lib/console.tcl:81 -#: lib/database.tcl:30 msgid "Close" msgstr "ЗатварÑне" -#: lib/checkout_op.tcl:175 #, tcl-format msgid "Branch '%s' does not exist." msgstr "Клонът „%s“ не ÑъщеÑтвува." -#: lib/checkout_op.tcl:194 #, tcl-format msgid "Failed to configure simplified git-pull for '%s'." msgstr "ÐеуÑпешно наÑтройване на опроÑтен git-pull за „%s“." -#: lib/checkout_op.tcl:202 lib/branch_rename.tcl:102 -#, tcl-format -msgid "Branch '%s' already exists." -msgstr "Клонът „%s“ вече ÑъщеÑтвува." - -#: lib/checkout_op.tcl:229 #, tcl-format msgid "" "Branch '%s' already exists.\n" @@ -810,21 +714,17 @@ msgstr "" "Той не може да Ñе Ñлее тривиално до „%s“.\n" "Ðеобходимо е Ñливане." -#: lib/checkout_op.tcl:243 #, tcl-format msgid "Merge strategy '%s' not supported." msgstr "Ð¡Ñ‚Ñ€Ð°Ñ‚ÐµÐ³Ð¸Ñ Ð·Ð° Ñливане „%s“ не Ñе поддържа." -#: lib/checkout_op.tcl:262 #, tcl-format msgid "Failed to update '%s'." msgstr "ÐеуÑпешно обновÑване на „%s“." -#: lib/checkout_op.tcl:274 msgid "Staging area (index) is already locked." msgstr "ИндекÑÑŠÑ‚ вече е заключен." -#: lib/checkout_op.tcl:289 msgid "" "Last scanned state does not match repository state.\n" "\n" @@ -841,31 +741,25 @@ msgstr "" "\n" "Ðвтоматично ще започне нова проверка.\n" -#: lib/checkout_op.tcl:345 #, tcl-format msgid "Updating working directory to '%s'..." msgstr "Работната Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ Ñе привежда към „%s“…" -#: lib/checkout_op.tcl:346 msgid "files checked out" msgstr "файла Ñа изтеглени" -#: lib/checkout_op.tcl:377 #, tcl-format msgid "Aborted checkout of '%s' (file level merging is required)." msgstr "" "ПреуÑтановÑване на изтеглÑнето на „%s“ (необходимо е пофайлово Ñливане)." -#: lib/checkout_op.tcl:378 msgid "File level merge required." msgstr "Ðеобходимо е пофайлово Ñливане." -#: lib/checkout_op.tcl:382 #, tcl-format msgid "Staying on branch '%s'." msgstr "ОÑтаване върху клона „%s“." -#: lib/checkout_op.tcl:453 msgid "" "You are no longer on a local branch.\n" "\n" @@ -876,35 +770,25 @@ msgstr "" "\n" "Ðко иÑкате да Ñте на клон, Ñъздайте базиран на „Това неÑвързано изтеглÑне“." -#: lib/checkout_op.tcl:504 lib/checkout_op.tcl:508 #, tcl-format msgid "Checked out '%s'." msgstr "„%s“ е изтеглен." -#: lib/checkout_op.tcl:536 #, tcl-format msgid "Resetting '%s' to '%s' will lose the following commits:" msgstr "" "ЗанулÑването на „%s“ към „%s“ ще доведе до загубването на Ñледните подаваниÑ:" -#: lib/checkout_op.tcl:558 msgid "Recovering lost commits may not be easy." msgstr "ВъзÑтановÑването на загубените Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ Ð¼Ð¾Ð¶Ðµ да е трудно." -#: lib/checkout_op.tcl:563 #, tcl-format msgid "Reset '%s'?" msgstr "ЗанулÑване на „%s“?" -#: lib/checkout_op.tcl:568 lib/tools_dlg.tcl:336 lib/merge.tcl:170 msgid "Visualize" msgstr "ВизуализациÑ" -#: lib/checkout_op.tcl:572 lib/branch_create.tcl:85 -msgid "Reset" -msgstr "Отначало" - -#: lib/checkout_op.tcl:636 #, tcl-format msgid "" "Failed to set current branch.\n" @@ -922,344 +806,18 @@ msgstr "" "Това ÑÑŠÑтоÑние е аварийно и не трÑбва да Ñе Ñлучва. Програмата „%s“ ще " "преуÑтанови работа." -#: lib/remote_add.tcl:20 -#, tcl-format -msgid "%s (%s): Add Remote" -msgstr "%s (%s): ДобавÑне на отдалечено хранилище" - -#: lib/remote_add.tcl:25 -msgid "Add New Remote" -msgstr "ДобавÑне на отдалечено хранилище" - -#: lib/remote_add.tcl:30 lib/tools_dlg.tcl:37 -msgid "Add" -msgstr "ДобавÑне" - -#: lib/remote_add.tcl:39 -msgid "Remote Details" -msgstr "Данни за отдалеченото хранилище" - -#: lib/remote_add.tcl:41 lib/tools_dlg.tcl:51 lib/branch_create.tcl:44 -msgid "Name:" -msgstr "Име:" - -#: lib/remote_add.tcl:50 -msgid "Location:" -msgstr "МеÑтоположение:" - -#: lib/remote_add.tcl:60 -msgid "Further Action" -msgstr "Следващо дейÑтвие" - -#: lib/remote_add.tcl:63 -msgid "Fetch Immediately" -msgstr "Ðезабавно доÑтавÑне" - -#: lib/remote_add.tcl:69 -msgid "Initialize Remote Repository and Push" -msgstr "Инициализиране на отдалеченото хранилище и изтлаÑкване на промените" - -#: lib/remote_add.tcl:75 -msgid "Do Nothing Else Now" -msgstr "Да не Ñе прави нищо" - -#: lib/remote_add.tcl:100 -msgid "Please supply a remote name." -msgstr "Задайте име за отдалеченото хранилище." - -#: lib/remote_add.tcl:113 -#, tcl-format -msgid "'%s' is not an acceptable remote name." -msgstr "Отдалечено хранилище не може да Ñе казва „%s“." - -#: lib/remote_add.tcl:124 -#, tcl-format -msgid "Failed to add remote '%s' of location '%s'." -msgstr "ÐеуÑпешно добавÑне на отдалеченото хранилище „%s“ от Ð°Ð´Ñ€ÐµÑ â€ž%s“." - -#: lib/remote_add.tcl:133 -#, tcl-format -msgid "Fetching the %s" -msgstr "ДоÑтавÑне на „%s“" - -#: lib/remote_add.tcl:156 -#, tcl-format -msgid "Do not know how to initialize repository at location '%s'." -msgstr "Хранилището Ñ Ð¼ÐµÑтоположение „%s“ не може да Ñе инициализира." - -#: lib/remote_add.tcl:163 -#, tcl-format -msgid "Setting up the %s (at %s)" -msgstr "ДобавÑне на хранилище „%s“ (Ñ Ð°Ð´Ñ€ÐµÑ â€ž%s“)" - -#: lib/browser.tcl:17 -msgid "Starting..." -msgstr "Стартиране…" - -#: lib/browser.tcl:27 -#, tcl-format -msgid "%s (%s): File Browser" -msgstr "%s (%s): Файлов браузър" - -#: lib/browser.tcl:132 lib/browser.tcl:149 -#, tcl-format -msgid "Loading %s..." -msgstr "Зареждане на „%s“…" - -#: lib/browser.tcl:193 -msgid "[Up To Parent]" -msgstr "[Към родителÑ]" - -#: lib/browser.tcl:275 -#, tcl-format -msgid "%s (%s): Browse Branch Files" -msgstr "%s (%s): Разглеждане на файловете в клона" - -#: lib/browser.tcl:282 -msgid "Browse Branch Files" -msgstr "Разглеждане на файловете в клона" - -#: lib/browser.tcl:288 lib/choose_repository.tcl:437 -#: lib/choose_repository.tcl:524 lib/choose_repository.tcl:533 -#: lib/choose_repository.tcl:1115 -msgid "Browse" -msgstr "Разглеждане" - -#: lib/browser.tcl:297 lib/branch_checkout.tcl:35 lib/tools_dlg.tcl:321 -msgid "Revision" -msgstr "ВерÑиÑ" - -#: lib/index.tcl:6 -msgid "Unable to unlock the index." -msgstr "ИндекÑÑŠÑ‚ не може да Ñе отключи." - -#: lib/index.tcl:30 -msgid "Index Error" -msgstr "Грешка в индекÑа" - -#: lib/index.tcl:32 -msgid "" -"Updating the Git index failed. A rescan will be automatically started to " -"resynchronize git-gui." -msgstr "" -"ÐеуÑпешно обновÑване на индекÑа на Git. Ðвтоматично ще започне нова проверка " -"за Ñинхронизирането на git-gui." - -#: lib/index.tcl:43 -msgid "Continue" -msgstr "Продължаване" - -#: lib/index.tcl:46 -msgid "Unlock Index" -msgstr "Отключване на индекÑа" - -#: lib/index.tcl:77 lib/index.tcl:146 lib/index.tcl:220 lib/index.tcl:587 -#: lib/choose_repository.tcl:999 -msgid "files" -msgstr "файлове" - -#: lib/index.tcl:326 -msgid "Unstaging selected files from commit" -msgstr "Изваждане на избраните файлове от подаването" - -#: lib/index.tcl:330 -#, tcl-format -msgid "Unstaging %s from commit" -msgstr "Изваждане на „%s“ от подаването" - -#: lib/index.tcl:369 -msgid "Ready to commit." -msgstr "ГотовноÑÑ‚ за подаване." - -#: lib/index.tcl:378 -msgid "Adding selected files" -msgstr "ДобавÑне на избраните файлове" - -#: lib/index.tcl:382 -#, tcl-format -msgid "Adding %s" -msgstr "ДобавÑне на „%s“" - -#: lib/index.tcl:412 -#, tcl-format -msgid "Stage %d untracked files?" -msgstr "Да Ñе добавÑÑ‚ ли %d неÑледени файла към индекÑа?" - -#: lib/index.tcl:420 -msgid "Adding all changed files" -msgstr "ДобавÑне на вÑички променени файлове" - -#: lib/index.tcl:503 -#, tcl-format -msgid "Revert changes in file %s?" -msgstr "Да Ñе махнат ли промените във файла „%s“?" - -#: lib/index.tcl:508 -#, tcl-format -msgid "Revert changes in these %i files?" -msgstr "Да Ñе махнат ли промените в тези %i файла?" - -#: lib/index.tcl:517 -msgid "Any unstaged changes will be permanently lost by the revert." -msgstr "" -"Ð’Ñички промени, които не Ñа били добавени в индекÑа, ще Ñе загубÑÑ‚ " -"безвъзвратно." - -#: lib/index.tcl:520 lib/index.tcl:563 -msgid "Do Nothing" -msgstr "Ðищо да не Ñе прави" - -#: lib/index.tcl:545 -#, tcl-format -msgid "Delete untracked file %s?" -msgstr "Да Ñе изтрие ли неÑледениÑÑ‚ файл „%s“?" - -#: lib/index.tcl:550 -#, tcl-format -msgid "Delete these %i untracked files?" -msgstr "Да Ñе изтриÑÑ‚ ли тези %d неÑледени файла?" - -#: lib/index.tcl:560 -msgid "Files will be permanently deleted." -msgstr "Файловете ще Ñе изтриÑÑ‚ окончателно." - -#: lib/index.tcl:564 -msgid "Delete Files" -msgstr "Изтриване на файлове" - -#: lib/index.tcl:586 -msgid "Deleting" -msgstr "Изтриване" - -#: lib/index.tcl:665 -msgid "Encountered errors deleting files:\n" -msgstr "Грешки при изтриване на файловете:\n" - -#: lib/index.tcl:674 -#, tcl-format -msgid "None of the %d selected files could be deleted." -msgstr "Ðикой от избраните %d файла не бе изтрит." - -#: lib/index.tcl:679 -#, tcl-format -msgid "%d of the %d selected files could not be deleted." -msgstr "%d от избраните %d файла не бÑха изтрити." - -#: lib/index.tcl:726 -msgid "Reverting selected files" -msgstr "Махане на промените в избраните файлове" - -#: lib/index.tcl:730 -#, tcl-format -msgid "Reverting %s" -msgstr "Махане на промените в „%s“" - -#: lib/branch_checkout.tcl:16 -#, tcl-format -msgid "%s (%s): Checkout Branch" -msgstr "%s (%s): Клон за изтеглÑне" - -#: lib/branch_checkout.tcl:21 -msgid "Checkout Branch" -msgstr "Клон за изтеглÑне" - -#: lib/branch_checkout.tcl:26 -msgid "Checkout" -msgstr "ИзтеглÑне" - -#: lib/branch_checkout.tcl:39 lib/option.tcl:310 lib/branch_create.tcl:69 -msgid "Options" -msgstr "Опции" - -#: lib/branch_checkout.tcl:42 lib/branch_create.tcl:92 -msgid "Fetch Tracking Branch" -msgstr "ИзтеглÑне на промените от ÑÐ»ÐµÐ´ÐµÐ½Ð¸Ñ ÐºÐ»Ð¾Ð½" - -#: lib/branch_checkout.tcl:47 -msgid "Detach From Local Branch" -msgstr "Изтриване от Ð»Ð¾ÐºÐ°Ð»Ð½Ð¸Ñ ÐºÐ»Ð¾Ð½" - -#: lib/status_bar.tcl:263 -#, tcl-format -msgid "%s ... %*i of %*i %s (%3i%%)" -msgstr "%s… %*i от общо %*i %s (%3i%%)" - -#: lib/remote.tcl:200 -msgid "Push to" -msgstr "ИзтлаÑкване към" - -#: lib/remote.tcl:218 -msgid "Remove Remote" -msgstr "Премахване на отдалечено хранилище" - -#: lib/remote.tcl:223 -msgid "Prune from" -msgstr "ОкаÑтрÑне от" - -#: lib/remote.tcl:228 -msgid "Fetch from" -msgstr "ДоÑтавÑне от" - -#: lib/remote.tcl:249 lib/remote.tcl:253 lib/remote.tcl:258 lib/remote.tcl:264 -msgid "All" -msgstr "Ð’Ñички" - -#: lib/branch_rename.tcl:15 -#, tcl-format -msgid "%s (%s): Rename Branch" -msgstr "%s (%s): Преименуване на клон" - -#: lib/branch_rename.tcl:23 -msgid "Rename Branch" -msgstr "Преименуване на клон" - -#: lib/branch_rename.tcl:28 -msgid "Rename" -msgstr "Преименуване" - -#: lib/branch_rename.tcl:38 -msgid "Branch:" -msgstr "Клон:" - -#: lib/branch_rename.tcl:46 -msgid "New Name:" -msgstr "Ðово име:" - -#: lib/branch_rename.tcl:81 -msgid "Please select a branch to rename." -msgstr "Изберете клон за преименуване." - -#: lib/branch_rename.tcl:92 lib/branch_create.tcl:154 -msgid "Please supply a branch name." -msgstr "Дайте име на клона." - -#: lib/branch_rename.tcl:112 lib/branch_create.tcl:165 -#, tcl-format -msgid "'%s' is not an acceptable branch name." -msgstr "„%s“ не може да Ñе използва за име на клон." - -#: lib/branch_rename.tcl:123 -#, tcl-format -msgid "Failed to rename '%s'." -msgstr "ÐеуÑпешно преименуване на „%s“." - -#: lib/choose_font.tcl:41 msgid "Select" msgstr "Избор" -#: lib/choose_font.tcl:55 msgid "Font Family" msgstr "Шрифт" -#: lib/choose_font.tcl:76 msgid "Font Size" msgstr "Размер" -#: lib/choose_font.tcl:93 msgid "Font Example" msgstr "МоÑтра" -#: lib/choose_font.tcl:105 msgid "" "This is example text.\n" "If you like this text, it can be your font." @@ -1267,1141 +825,137 @@ msgstr "" "Това е примерен текÑÑ‚.\n" "Ðко ви хареÑва как изглежда, изберете шрифта." -#: lib/option.tcl:11 -#, tcl-format -msgid "Invalid global encoding '%s'" -msgstr "Ðеправилно глобално кодиране „%s“" - -#: lib/option.tcl:19 -#, tcl-format -msgid "Invalid repo encoding '%s'" -msgstr "Ðеправилно кодиране „%s“ на хранилището" - -#: lib/option.tcl:119 -msgid "Restore Defaults" -msgstr "Стандартни наÑтройки" - -#: lib/option.tcl:123 -msgid "Save" -msgstr "Запазване" - -#: lib/option.tcl:133 -#, tcl-format -msgid "%s Repository" -msgstr "Хранилище „%s“" - -#: lib/option.tcl:134 -msgid "Global (All Repositories)" -msgstr "Глобално (за вÑички хранилища)" - -#: lib/option.tcl:140 -msgid "User Name" -msgstr "ПотребителÑко име" - -#: lib/option.tcl:141 -msgid "Email Address" -msgstr "ÐÐ´Ñ€ÐµÑ Ð½Ð° е-поща" - -#: lib/option.tcl:143 -msgid "Summarize Merge Commits" -msgstr "Обобщаване на подаваниÑта при Ñливане" - -#: lib/option.tcl:144 -msgid "Merge Verbosity" -msgstr "ПодробноÑти при ÑливаниÑта" - -#: lib/option.tcl:145 -msgid "Show Diffstat After Merge" -msgstr "Извеждане на ÑтатиÑтика Ñлед ÑливаниÑта" - -#: lib/option.tcl:146 -msgid "Use Merge Tool" -msgstr "Използване на програма за Ñливане" - -#: lib/option.tcl:148 -msgid "Trust File Modification Timestamps" -msgstr "Доверие във времето на промÑна на файловете" - -#: lib/option.tcl:149 -msgid "Prune Tracking Branches During Fetch" -msgstr "ОкаÑтрÑне на ÑледÑщите клонове при доÑтавÑне" - -#: lib/option.tcl:150 -msgid "Match Tracking Branches" -msgstr "ÐапаÑване на ÑледÑщите клонове" - -#: lib/option.tcl:151 -msgid "Use Textconv For Diffs and Blames" -msgstr "Използване на „textconv“ за разликите и анотирането" - -#: lib/option.tcl:152 -msgid "Blame Copy Only On Changed Files" -msgstr "Ðнотиране на копието Ñамо по променените файлове" - -#: lib/option.tcl:153 -msgid "Maximum Length of Recent Repositories List" -msgstr "МакÑимален брой на ÑпиÑъка „Скоро ползвани“ хранилища" - -#: lib/option.tcl:154 -msgid "Minimum Letters To Blame Copy On" -msgstr "Минимален брой знаци за анотиране на копието" - -#: lib/option.tcl:155 -msgid "Blame History Context Radius (days)" -msgstr "ИÑторичеÑки обхват за анотиране в дни" - -#: lib/option.tcl:156 -msgid "Number of Diff Context Lines" -msgstr "Брой редове за контекÑта на разликите" - -#: lib/option.tcl:157 -msgid "Additional Diff Parameters" -msgstr "Ðргументи към командата за разликите" - -#: lib/option.tcl:158 -msgid "Commit Message Text Width" -msgstr "Широчина на текÑта на Ñъобщението при подаване" - -#: lib/option.tcl:159 -msgid "New Branch Name Template" -msgstr "Шаблон за името на новите клони" - -#: lib/option.tcl:160 -msgid "Default File Contents Encoding" -msgstr "Кодиране на файловете" - -#: lib/option.tcl:161 -msgid "Warn before committing to a detached head" -msgstr "Предупреждаване при подаване към неÑвързан указател" - -#: lib/option.tcl:162 -msgid "Staging of untracked files" -msgstr "ДобавÑне на неÑледените файлове към индекÑа" - -#: lib/option.tcl:163 -msgid "Show untracked files" -msgstr "Показване на неÑледените файлове" - -#: lib/option.tcl:164 -msgid "Tab spacing" -msgstr "Ширина на табулациÑта" - -#: lib/option.tcl:182 lib/option.tcl:197 lib/option.tcl:220 lib/option.tcl:282 -#: lib/database.tcl:57 -#, tcl-format -msgid "%s:" -msgstr "%s:" - -#: lib/option.tcl:210 -msgid "Change" -msgstr "СмÑна" - -#: lib/option.tcl:254 -msgid "Spelling Dictionary:" -msgstr "ПравопиÑен речник:" - -#: lib/option.tcl:284 -msgid "Change Font" -msgstr "СмÑна на шрифта" - -#: lib/option.tcl:288 -#, tcl-format -msgid "Choose %s" -msgstr "Избор на „%s“" - -#: lib/option.tcl:294 -msgid "pt." -msgstr "тчк." - -#: lib/option.tcl:308 -msgid "Preferences" -msgstr "ÐаÑтройки" - -#: lib/option.tcl:345 -msgid "Failed to completely save options:" -msgstr "ÐеуÑпешно запазване на наÑтройките:" - -#: lib/encoding.tcl:443 -msgid "Default" -msgstr "Стандартното" - -#: lib/encoding.tcl:448 -#, tcl-format -msgid "System (%s)" -msgstr "СиÑтемното (%s)" - -#: lib/encoding.tcl:459 lib/encoding.tcl:465 -msgid "Other" -msgstr "Друго" - -#: lib/tools.tcl:76 -#, tcl-format -msgid "Running %s requires a selected file." -msgstr "За изпълнението на „%s“ трÑбва да изберете файл." - -#: lib/tools.tcl:92 -#, tcl-format -msgid "Are you sure you want to run %1$s on file \"%2$s\"?" -msgstr "Сигурни ли Ñте, че иÑкате да изпълните „%1$s“ върху файла „%2$s“?" - -#: lib/tools.tcl:96 -#, tcl-format -msgid "Are you sure you want to run %s?" -msgstr "Сигурни ли Ñте, че иÑкате да изпълните „%s“?" - -#: lib/tools.tcl:118 -#, tcl-format -msgid "Tool: %s" -msgstr "Команда: %s" - -#: lib/tools.tcl:119 -#, tcl-format -msgid "Running: %s" -msgstr "Изпълнение: %s" - -#: lib/tools.tcl:158 -#, tcl-format -msgid "Tool completed successfully: %s" -msgstr "Командата завърши уÑпешно: %s" - -#: lib/tools.tcl:160 -#, tcl-format -msgid "Tool failed: %s" -msgstr "Командата върна грешка: %s" - -#: lib/mergetool.tcl:8 -msgid "Force resolution to the base version?" -msgstr "Да Ñе използва базовата верÑиÑ" - -#: lib/mergetool.tcl:9 -msgid "Force resolution to this branch?" -msgstr "Да Ñе използва верÑиÑта от този клон" - -#: lib/mergetool.tcl:10 -msgid "Force resolution to the other branch?" -msgstr "Да Ñе използва верÑиÑта от Ð´Ñ€ÑƒÐ³Ð¸Ñ ÐºÐ»Ð¾Ð½" - -#: lib/mergetool.tcl:14 -#, tcl-format -msgid "" -"Note that the diff shows only conflicting changes.\n" -"\n" -"%s will be overwritten.\n" -"\n" -"This operation can be undone only by restarting the merge." -msgstr "" -"Разликата показва Ñамо разликите Ñ ÐºÐ¾Ð½Ñ„Ð»Ð¸ÐºÑ‚.\n" -"\n" -"Файлът „%s“ ще Ñе презапише.\n" -"\n" -"Тази Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð¼Ð¾Ð¶Ðµ да Ñе отмени Ñамо чрез започване на Ñливането наново." - -#: lib/mergetool.tcl:45 -#, tcl-format -msgid "File %s seems to have unresolved conflicts, still stage?" -msgstr "" -"Изглежда, че вÑе още има некоригирани конфликти във файла „%s“. Да Ñе добави " -"ли файлът към индекÑа?" - -#: lib/mergetool.tcl:60 -#, tcl-format -msgid "Adding resolution for %s" -msgstr "ДобавÑне на ÐºÐ¾Ñ€ÐµÐºÑ†Ð¸Ñ Ð½Ð° конфликтите в „%s“" - -#: lib/mergetool.tcl:141 -msgid "Cannot resolve deletion or link conflicts using a tool" -msgstr "" -"Конфликтите при Ñимволни връзки или изтриване не може да Ñе коригират Ñ " -"външна програма." - -#: lib/mergetool.tcl:146 -msgid "Conflict file does not exist" -msgstr "Файлът, в който е конфликтът, не ÑъщеÑтвува" - -#: lib/mergetool.tcl:246 -#, tcl-format -msgid "Not a GUI merge tool: '%s'" -msgstr "Това не е графична програма за Ñливане: „%s“" - -#: lib/mergetool.tcl:275 -#, tcl-format -msgid "Unsupported merge tool '%s'" -msgstr "Ðеподдържана програма за Ñливане: „%s“" - -#: lib/mergetool.tcl:310 -msgid "Merge tool is already running, terminate it?" -msgstr "Програмата за Ñливане вече е Ñтартирана. Да Ñе изключи ли?" - -#: lib/mergetool.tcl:330 -#, tcl-format -msgid "" -"Error retrieving versions:\n" -"%s" -msgstr "" -"Грешка при изтеглÑнето на верÑии:\n" -"%s" - -#: lib/mergetool.tcl:350 -#, tcl-format -msgid "" -"Could not start the merge tool:\n" -"\n" -"%s" -msgstr "" -"Програмата за Ñливане не може да Ñе Ñтартира:\n" -"\n" -"%s" - -#: lib/mergetool.tcl:354 -msgid "Running merge tool..." -msgstr "Стартиране на програмата за Ñливане…" - -#: lib/mergetool.tcl:382 lib/mergetool.tcl:390 -msgid "Merge tool failed." -msgstr "Грешка в програмата за Ñливане." - -#: lib/tools_dlg.tcl:22 -#, tcl-format -msgid "%s (%s): Add Tool" -msgstr "%s (%s): ДобавÑне на команда" - -#: lib/tools_dlg.tcl:28 -msgid "Add New Tool Command" -msgstr "ДобавÑне на команда" - -#: lib/tools_dlg.tcl:34 -msgid "Add globally" -msgstr "Глобално добавÑне" - -#: lib/tools_dlg.tcl:46 -msgid "Tool Details" -msgstr "ПодробноÑти за командата" - -#: lib/tools_dlg.tcl:49 -msgid "Use '/' separators to create a submenu tree:" -msgstr "За Ñъздаване на подменюта използвайте знака „/“ за разделител:" - -#: lib/tools_dlg.tcl:60 -msgid "Command:" -msgstr "Команда:" - -#: lib/tools_dlg.tcl:71 -msgid "Show a dialog before running" -msgstr "Преди изпълнение да Ñе извежда диалогов прозорец" - -#: lib/tools_dlg.tcl:77 -msgid "Ask the user to select a revision (sets $REVISION)" -msgstr "ПотребителÑÑ‚ да укаже верÑÐ¸Ñ (задаване на променливата $REVISION)" - -#: lib/tools_dlg.tcl:82 -msgid "Ask the user for additional arguments (sets $ARGS)" -msgstr "" -"ПотребителÑÑ‚ да укаже допълнителни аргументи (задаване на променливата $ARGS)" - -#: lib/tools_dlg.tcl:89 -msgid "Don't show the command output window" -msgstr "Без показване на прозорец Ñ Ð¸Ð·Ñ…Ð¾Ð´Ð° от командата" - -#: lib/tools_dlg.tcl:94 -msgid "Run only if a diff is selected ($FILENAME not empty)" -msgstr "" -"Стартиране Ñамо Ñлед избор на разлика (променливата $FILENAME не е празна)" - -#: lib/tools_dlg.tcl:118 -msgid "Please supply a name for the tool." -msgstr "Задайте име за командата." - -#: lib/tools_dlg.tcl:126 -#, tcl-format -msgid "Tool '%s' already exists." -msgstr "Командата „%s“ вече ÑъщеÑтвува." - -#: lib/tools_dlg.tcl:148 -#, tcl-format -msgid "" -"Could not add tool:\n" -"%s" -msgstr "" -"Командата не може да Ñе добави:\n" -"%s" - -#: lib/tools_dlg.tcl:187 -#, tcl-format -msgid "%s (%s): Remove Tool" -msgstr "%s (%s): Премахване на команда" - -#: lib/tools_dlg.tcl:193 -msgid "Remove Tool Commands" -msgstr "Премахване на команди" - -#: lib/tools_dlg.tcl:198 -msgid "Remove" -msgstr "Премахване" - -#: lib/tools_dlg.tcl:231 -msgid "(Blue denotes repository-local tools)" -msgstr "(командите към локалното хранилище Ñа обозначени в Ñиньо)" - -#: lib/tools_dlg.tcl:283 -#, tcl-format -msgid "%s (%s):" -msgstr "%s (%s):" - -#: lib/tools_dlg.tcl:292 -#, tcl-format -msgid "Run Command: %s" -msgstr "Изпълнение на командата „%s“" - -#: lib/tools_dlg.tcl:306 -msgid "Arguments" -msgstr "Ðргументи" - -#: lib/tools_dlg.tcl:341 -msgid "OK" -msgstr "Добре" - -#: lib/search.tcl:48 -msgid "Find:" -msgstr "ТърÑене:" - -#: lib/search.tcl:50 -msgid "Next" -msgstr "Следваща поÑва" - -#: lib/search.tcl:51 -msgid "Prev" -msgstr "Предишна поÑва" - -#: lib/search.tcl:52 -msgid "RegExp" -msgstr "РегИзр" - -#: lib/search.tcl:54 -msgid "Case" -msgstr "Главни/Малки" - -#: lib/shortcut.tcl:8 lib/shortcut.tcl:43 lib/shortcut.tcl:75 -#, tcl-format -msgid "%s (%s): Create Desktop Icon" -msgstr "%s (%s): ДобавÑне на икона на Ñ€Ð°Ð±Ð¾Ñ‚Ð½Ð¸Ñ Ð¿Ð»Ð¾Ñ‚" - -#: lib/shortcut.tcl:24 lib/shortcut.tcl:65 -msgid "Cannot write shortcut:" -msgstr "Клавишната ÐºÐ¾Ð¼Ð±Ð¸Ð½Ð°Ñ†Ð¸Ñ Ð½Ðµ може да Ñе запази:" - -#: lib/shortcut.tcl:140 -msgid "Cannot write icon:" -msgstr "Иконата не може да Ñе запази:" - -#: lib/remote_branch_delete.tcl:29 -#, tcl-format -msgid "%s (%s): Delete Branch Remotely" -msgstr "%s (%s): Изтриване на Ð¾Ñ‚Ð´Ð°Ð»ÐµÑ‡ÐµÐ½Ð¸Ñ ÐºÐ»Ð¾Ð½" - -#: lib/remote_branch_delete.tcl:34 -msgid "Delete Branch Remotely" -msgstr "Изтриване на Ð¾Ñ‚Ð´Ð°Ð»ÐµÑ‡ÐµÐ½Ð¸Ñ ÐºÐ»Ð¾Ð½" - -#: lib/remote_branch_delete.tcl:48 -msgid "From Repository" -msgstr "От хранилище" - -#: lib/remote_branch_delete.tcl:88 -msgid "Branches" -msgstr "Клони" - -#: lib/remote_branch_delete.tcl:110 -msgid "Delete Only If" -msgstr "Изтриване, Ñамо ако" - -#: lib/remote_branch_delete.tcl:112 -msgid "Merged Into:" -msgstr "СлÑÑ‚ в:" - -#: lib/remote_branch_delete.tcl:120 lib/branch_delete.tcl:53 -msgid "Always (Do not perform merge checks)" -msgstr "Винаги (без проверка за Ñливане)" - -#: lib/remote_branch_delete.tcl:153 -msgid "A branch is required for 'Merged Into'." -msgstr "За данните „СлÑÑ‚ в“ е необходимо да зададете клон." - -#: lib/remote_branch_delete.tcl:185 -#, tcl-format -msgid "" -"The following branches are not completely merged into %s:\n" -"\n" -" - %s" -msgstr "" -"Следните клони не Ñа Ñлети напълно в „%s“:\n" -"\n" -" â— %s" - -#: lib/remote_branch_delete.tcl:190 -#, tcl-format -msgid "" -"One or more of the merge tests failed because you have not fetched the " -"necessary commits. Try fetching from %s first." -msgstr "" -"Поне една от пробите за Ñливане е неуÑпешна, защото не Ñте доÑтавили вÑички " -"необходими подаваниÑ. Пробвайте първо да доÑтавите подаваниÑта от „%s“." - -#: lib/remote_branch_delete.tcl:208 -msgid "Please select one or more branches to delete." -msgstr "Изберете поне един клон за изтриване." - -#: lib/remote_branch_delete.tcl:218 lib/branch_delete.tcl:115 -msgid "" -"Recovering deleted branches is difficult.\n" -"\n" -"Delete the selected branches?" -msgstr "" -"ВъзÑтановÑването на изтрити клони може да е трудно.\n" -"\n" -"Сигурни ли Ñте, че иÑкате да триете?" - -#: lib/remote_branch_delete.tcl:227 -#, tcl-format -msgid "Deleting branches from %s" -msgstr "Изтриване на клони от „%s“" - -#: lib/remote_branch_delete.tcl:300 -msgid "No repository selected." -msgstr "Ðе е избрано хранилище." - -#: lib/remote_branch_delete.tcl:305 -#, tcl-format -msgid "Scanning %s..." -msgstr "ПретърÑване на „%s“…" - -#: lib/choose_repository.tcl:45 msgid "Git Gui" msgstr "ГПИ на Git" -#: lib/choose_repository.tcl:104 lib/choose_repository.tcl:427 msgid "Create New Repository" msgstr "Създаване на ново хранилище" -#: lib/choose_repository.tcl:110 msgid "New..." msgstr "Ðово…" -#: lib/choose_repository.tcl:117 lib/choose_repository.tcl:511 msgid "Clone Existing Repository" msgstr "Клониране на ÑъщеÑтвуващо хранилище" -#: lib/choose_repository.tcl:128 msgid "Clone..." msgstr "Клониране…" -#: lib/choose_repository.tcl:135 lib/choose_repository.tcl:1105 msgid "Open Existing Repository" msgstr "ОтварÑне на ÑъщеÑтвуващо хранилище" -#: lib/choose_repository.tcl:141 msgid "Open..." msgstr "ОтварÑне…" -#: lib/choose_repository.tcl:154 msgid "Recent Repositories" msgstr "Скоро ползвани" -#: lib/choose_repository.tcl:164 msgid "Open Recent Repository:" msgstr "ОтварÑне на хранилище ползвано наÑкоро:" -#: lib/choose_repository.tcl:331 lib/choose_repository.tcl:338 -#: lib/choose_repository.tcl:345 #, tcl-format msgid "Failed to create repository %s:" msgstr "ÐеуÑпешно Ñъздаване на хранилището „%s“:" -#: lib/choose_repository.tcl:422 lib/branch_create.tcl:33 -msgid "Create" -msgstr "Създаване" - -#: lib/choose_repository.tcl:432 msgid "Directory:" msgstr "ДиректориÑ:" -#: lib/choose_repository.tcl:462 lib/choose_repository.tcl:588 -#: lib/choose_repository.tcl:1139 msgid "Git Repository" msgstr "Хранилище на Git" -#: lib/choose_repository.tcl:487 #, tcl-format msgid "Directory %s already exists." msgstr "Вече ÑъщеÑтвува Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ â€ž%s“." -#: lib/choose_repository.tcl:491 #, tcl-format msgid "File %s already exists." msgstr "Вече ÑъщеÑтвува файл „%s“." -#: lib/choose_repository.tcl:506 msgid "Clone" msgstr "Клониране" -#: lib/choose_repository.tcl:519 msgid "Source Location:" msgstr "ÐÐ´Ñ€ÐµÑ Ð½Ð° източника:" -#: lib/choose_repository.tcl:528 msgid "Target Directory:" msgstr "Целева директориÑ:" -#: lib/choose_repository.tcl:538 msgid "Clone Type:" msgstr "Вид клониране:" -#: lib/choose_repository.tcl:543 msgid "Standard (Fast, Semi-Redundant, Hardlinks)" msgstr "Стандартно (бързо, чаÑтично ÑподелÑне на файлове, твърди връзки)" -#: lib/choose_repository.tcl:548 msgid "Full Copy (Slower, Redundant Backup)" msgstr "Пълно (бавно, пълноценно резервно копие)" -#: lib/choose_repository.tcl:553 msgid "Shared (Fastest, Not Recommended, No Backup)" msgstr "Споделено (най-бързо, не Ñе препоръчва, не прави резервно копие)" -#: lib/choose_repository.tcl:560 msgid "Recursively clone submodules too" msgstr "РекурÑивно клониране и на подмодулите" -#: lib/choose_repository.tcl:594 lib/choose_repository.tcl:641 -#: lib/choose_repository.tcl:790 lib/choose_repository.tcl:864 -#: lib/choose_repository.tcl:1145 lib/choose_repository.tcl:1153 #, tcl-format msgid "Not a Git repository: %s" msgstr "Това не е хранилище на Git: %s" -#: lib/choose_repository.tcl:630 +msgid "Hardlinks are unavailable. Falling back to copying." +msgstr "Ðе Ñе поддържат твърди връзки. Преминава Ñе към копиране." + msgid "Standard only available for local repository." msgstr "Само локални хранилища може да Ñе клонират Ñтандартно" -#: lib/choose_repository.tcl:634 msgid "Shared only available for local repository." msgstr "Само локални хранилища може да Ñе клонират Ñподелено" -#: lib/choose_repository.tcl:655 #, tcl-format msgid "Location %s already exists." msgstr "МеÑтоположението „%s“ вече ÑъщеÑтвува." -#: lib/choose_repository.tcl:666 -msgid "Failed to configure origin" -msgstr "ÐеуÑпешно наÑтройване на хранилището-източник" - -#: lib/choose_repository.tcl:678 -msgid "Counting objects" -msgstr "ПреброÑване на обекти" - -#: lib/choose_repository.tcl:679 -msgid "buckets" -msgstr "клетки" - -#: lib/choose_repository.tcl:703 -#, tcl-format -msgid "Unable to copy objects/info/alternates: %s" -msgstr "Обектите/ИнформациÑта/Синонимите не може да Ñе копират: %s" - -#: lib/choose_repository.tcl:740 -#, tcl-format -msgid "Nothing to clone from %s." -msgstr "ÐÑма какво да Ñе клонира от „%s“." - -#: lib/choose_repository.tcl:742 lib/choose_repository.tcl:962 -#: lib/choose_repository.tcl:974 -msgid "The 'master' branch has not been initialized." -msgstr "ОÑновниÑÑ‚ клон — „master“ не е инициализиран." - -#: lib/choose_repository.tcl:755 -msgid "Hardlinks are unavailable. Falling back to copying." -msgstr "Ðе Ñе поддържат твърди връзки. Преминава Ñе към копиране." - -#: lib/choose_repository.tcl:769 #, tcl-format msgid "Cloning from %s" msgstr "Клониране на „%s“" -#: lib/choose_repository.tcl:800 -msgid "Copying objects" -msgstr "Копиране на обекти" - -#: lib/choose_repository.tcl:801 -msgid "KiB" -msgstr "KiB" - -#: lib/choose_repository.tcl:825 -#, tcl-format -msgid "Unable to copy object: %s" -msgstr "ÐеуÑпешно копиране на обект: %s" - -#: lib/choose_repository.tcl:837 -msgid "Linking objects" -msgstr "Създаване на връзки към обектите" - -#: lib/choose_repository.tcl:838 -msgid "objects" -msgstr "обекти" - -#: lib/choose_repository.tcl:846 -#, tcl-format -msgid "Unable to hardlink object: %s" -msgstr "ÐеуÑпешно Ñъздаване на твърда връзка към обект: %s" - -#: lib/choose_repository.tcl:903 -msgid "Cannot fetch branches and objects. See console output for details." -msgstr "" -"Клоните и обектите не може да Ñе изтеглÑÑ‚. За повече Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¿Ð¾Ð³Ð»ÐµÐ´Ð½ÐµÑ‚Ðµ " -"изхода на конзолата." - -#: lib/choose_repository.tcl:914 -msgid "Cannot fetch tags. See console output for details." -msgstr "" -"Етикетите не може да Ñе изтеглÑÑ‚. За повече Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¿Ð¾Ð³Ð»ÐµÐ´Ð½ÐµÑ‚Ðµ изхода на " -"конзолата." - -#: lib/choose_repository.tcl:938 -msgid "Cannot determine HEAD. See console output for details." -msgstr "" -"Върхът „HEAD“ не може да Ñе определи. За повече Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¿Ð¾Ð³Ð»ÐµÐ´Ð½ÐµÑ‚Ðµ изхода " -"на конзолата." - -#: lib/choose_repository.tcl:947 -#, tcl-format -msgid "Unable to cleanup %s" -msgstr "„%s“ не може да Ñе изчиÑти" - -#: lib/choose_repository.tcl:953 msgid "Clone failed." msgstr "ÐеуÑпешно клониране." -#: lib/choose_repository.tcl:960 -msgid "No default branch obtained." -msgstr "Ðе е получен клон по подразбиране." - -#: lib/choose_repository.tcl:971 -#, tcl-format -msgid "Cannot resolve %s as a commit." -msgstr "ÐÑма подаване отговарÑщо на „%s“." - -#: lib/choose_repository.tcl:998 -msgid "Creating working directory" -msgstr "Създаване на работната директориÑ" - -#: lib/choose_repository.tcl:1028 -msgid "Initial file checkout failed." -msgstr "ÐеуÑпешно първоначално изтеглÑне." - -#: lib/choose_repository.tcl:1072 -msgid "Cloning submodules" -msgstr "Клониране на подмодули" - -#: lib/choose_repository.tcl:1087 -msgid "Cannot clone submodules." -msgstr "Подмодулите не може да Ñе клонират." - -#: lib/choose_repository.tcl:1110 msgid "Repository:" msgstr "Хранилище:" -#: lib/choose_repository.tcl:1159 #, tcl-format msgid "Failed to open repository %s:" msgstr "ÐеуÑпешно отварÑне на хранилището „%s“:" -#: lib/about.tcl:26 -msgid "git-gui - a graphical user interface for Git." -msgstr "git-gui — графичен Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð·Ð° Git." - -#: lib/blame.tcl:74 -#, tcl-format -msgid "%s (%s): File Viewer" -msgstr "%s (%s): Преглед на файлове" - -#: lib/blame.tcl:80 -msgid "Commit:" -msgstr "Подаване:" - -#: lib/blame.tcl:282 -msgid "Copy Commit" -msgstr "Копиране на подаване" - -#: lib/blame.tcl:286 -msgid "Find Text..." -msgstr "ТърÑене на текÑт…" - -#: lib/blame.tcl:290 -msgid "Goto Line..." -msgstr "Към ред…" - -#: lib/blame.tcl:299 -msgid "Do Full Copy Detection" -msgstr "Пълно търÑене на копиране" - -#: lib/blame.tcl:303 -msgid "Show History Context" -msgstr "Показване на контекÑта от иÑториÑта" - -#: lib/blame.tcl:306 -msgid "Blame Parent Commit" -msgstr "Ðнотиране на родителÑкото подаване" - -#: lib/blame.tcl:468 -#, tcl-format -msgid "Reading %s..." -msgstr "Чете Ñе „%s“…" - -#: lib/blame.tcl:596 -msgid "Loading copy/move tracking annotations..." -msgstr "Зареждане на анотациите за проÑледÑване на копирането/премеÑтването…" - -#: lib/blame.tcl:613 -msgid "lines annotated" -msgstr "реда анотирани" - -#: lib/blame.tcl:815 -msgid "Loading original location annotations..." -msgstr "Зареждане на анотациите за първоначалното меÑтоположение…" - -#: lib/blame.tcl:818 -msgid "Annotation complete." -msgstr "Ðнотирането завърши." - -#: lib/blame.tcl:849 -msgid "Busy" -msgstr "ОперациÑта не е завършила" - -#: lib/blame.tcl:850 -msgid "Annotation process is already running." -msgstr "Ð’ момента тече Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð½Ð° анотиране." - -#: lib/blame.tcl:889 -msgid "Running thorough copy detection..." -msgstr "ИзпълнÑва Ñе цÑлоÑтен Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð½Ð° откриване на копиране…" - -#: lib/blame.tcl:957 -msgid "Loading annotation..." -msgstr "Зареждане на анотации…" - -#: lib/blame.tcl:1010 -msgid "Author:" -msgstr "Ðвтор:" - -#: lib/blame.tcl:1014 -msgid "Committer:" -msgstr "Подал:" - -#: lib/blame.tcl:1019 -msgid "Original File:" -msgstr "Първоначален файл:" - -#: lib/blame.tcl:1067 -msgid "Cannot find HEAD commit:" -msgstr "Подаването за връх „HEAD“ не може да Ñе открие:" - -#: lib/blame.tcl:1122 -msgid "Cannot find parent commit:" -msgstr "РодителÑкото подаване не може да Ñе открие" - -#: lib/blame.tcl:1137 -msgid "Unable to display parent" -msgstr "РодителÑÑ‚ не може да Ñе покаже" - -#: lib/blame.tcl:1138 lib/diff.tcl:345 -msgid "Error loading diff:" -msgstr "Грешка при зареждане на разлика:" - -#: lib/blame.tcl:1279 -msgid "Originally By:" -msgstr "Първоначално от:" - -#: lib/blame.tcl:1285 -msgid "In File:" -msgstr "Във файл:" - -#: lib/blame.tcl:1290 -msgid "Copied Or Moved Here By:" -msgstr "Копирано или премеÑтено тук от:" - -#: lib/diff.tcl:77 -#, tcl-format -msgid "" -"No differences detected.\n" -"\n" -"%s has no changes.\n" -"\n" -"The modification date of this file was updated by another application, but " -"the content within the file was not changed.\n" -"\n" -"A rescan will be automatically started to find other files which may have " -"the same state." -msgstr "" -"Ðе Ñа открити разлики.\n" -"\n" -"ÐÑма промени в „%s“.\n" -"\n" -"Времето на промÑна на файла е бил зададен от друга програма, но Ñъдържанието " -"му не е променено.\n" -"\n" -"Ðвтоматично ще започне нова проверка дали нÑма други файлове в това " -"ÑÑŠÑтоÑние." - -#: lib/diff.tcl:117 -#, tcl-format -msgid "Loading diff of %s..." -msgstr "Зареждане на разликите в „%s“…" - -#: lib/diff.tcl:143 -msgid "" -"LOCAL: deleted\n" -"REMOTE:\n" -msgstr "" -"ЛОКÐЛÐО: изтрит\n" -"ОТДÐЛЕЧЕÐО:\n" - -#: lib/diff.tcl:148 -msgid "" -"REMOTE: deleted\n" -"LOCAL:\n" -msgstr "" -"ОТДÐЛЕЧЕÐО: изтрит\n" -"ЛОКÐЛÐО:\n" - -#: lib/diff.tcl:155 -msgid "LOCAL:\n" -msgstr "ЛОКÐЛÐО:\n" - -#: lib/diff.tcl:158 -msgid "REMOTE:\n" -msgstr "ОТДÐЛЕЧЕÐО:\n" - -#: lib/diff.tcl:220 lib/diff.tcl:344 -#, tcl-format -msgid "Unable to display %s" -msgstr "Файлът „%s“ не може да Ñе покаже" - -#: lib/diff.tcl:221 -msgid "Error loading file:" -msgstr "Грешка при зареждане на файл:" - -#: lib/diff.tcl:227 -msgid "Git Repository (subproject)" -msgstr "Хранилище на Git (подмодул)" - -#: lib/diff.tcl:239 -msgid "* Binary file (not showing content)." -msgstr "◠Двоичен файл (Ñъдържанието не Ñе показва)." - -#: lib/diff.tcl:244 -#, tcl-format -msgid "" -"* Untracked file is %d bytes.\n" -"* Showing only first %d bytes.\n" -msgstr "" -"â— ÐеÑледениÑÑ‚ файл е %d байта.\n" -"◠Показват Ñе Ñамо първите %d байта.\n" - -#: lib/diff.tcl:250 -#, tcl-format -msgid "" -"\n" -"* Untracked file clipped here by %s.\n" -"* To see the entire file, use an external editor.\n" -msgstr "" -"\n" -"â— ÐеÑледениÑÑ‚ файл е отрÑзан дотук от програмата „%s“.\n" -"◠Използвайте външен редактор, за да видите Ñ†ÐµÐ»Ð¸Ñ Ñ„Ð°Ð¹Ð».\n" - -#: lib/diff.tcl:583 -msgid "Failed to unstage selected hunk." -msgstr "Избраното парче не може да Ñе извади от индекÑа." - -#: lib/diff.tcl:591 -msgid "Failed to revert selected hunk." -msgstr "Избраното парче не може да Ñе върне." - -#: lib/diff.tcl:594 -msgid "Failed to stage selected hunk." -msgstr "Избраното парче не може да Ñе добави към индекÑа." - -#: lib/diff.tcl:687 -msgid "Failed to unstage selected line." -msgstr "ИзбраниÑÑ‚ ред не може да Ñе извади от индекÑа." - -#: lib/diff.tcl:696 -msgid "Failed to revert selected line." -msgstr "ИзбраниÑÑ‚ ред не може да Ñе върне." - -#: lib/diff.tcl:700 -msgid "Failed to stage selected line." -msgstr "ИзбраниÑÑ‚ ред не може да Ñе добави към индекÑа." - -#: lib/diff.tcl:889 -msgid "Failed to undo last revert." -msgstr "ÐеуÑпешна отмÑна на поÑледното връщане." - -#: lib/sshkey.tcl:34 -msgid "No keys found." -msgstr "Ðе Ñа открити ключове." - -#: lib/sshkey.tcl:37 -#, tcl-format -msgid "Found a public key in: %s" -msgstr "Открит е публичен ключ в „%s“" - -#: lib/sshkey.tcl:43 -msgid "Generate Key" -msgstr "Генериране на ключ" - -#: lib/sshkey.tcl:61 -msgid "Copy To Clipboard" -msgstr "Копиране към ÑиÑÑ‚ÐµÐ¼Ð½Ð¸Ñ Ð±ÑƒÑ„ÐµÑ€" - -#: lib/sshkey.tcl:75 -msgid "Your OpenSSH Public Key" -msgstr "ПубличниÑÑ‚ ви ключ за OpenSSH" - -#: lib/sshkey.tcl:83 -msgid "Generating..." -msgstr "Генериране…" - -#: lib/sshkey.tcl:89 -#, tcl-format -msgid "" -"Could not start ssh-keygen:\n" -"\n" -"%s" -msgstr "" -"Програмата „ssh-keygen“ не може да Ñе Ñтартира:\n" -"\n" -"%s" - -#: lib/sshkey.tcl:116 -msgid "Generation failed." -msgstr "ÐеуÑпешно генериране." - -#: lib/sshkey.tcl:123 -msgid "Generation succeeded, but no keys found." -msgstr "Генерирането завърши уÑпешно, а не Ñа намерени ключове." - -#: lib/sshkey.tcl:126 -#, tcl-format -msgid "Your key is in: %s" -msgstr "Ключът ви е в „%s“" - -#: lib/branch_create.tcl:23 -#, tcl-format -msgid "%s (%s): Create Branch" -msgstr "%s (%s): Създаване на клон" - -#: lib/branch_create.tcl:28 -msgid "Create New Branch" -msgstr "Създаване на нов клон" - -#: lib/branch_create.tcl:42 -msgid "Branch Name" -msgstr "Име на клона" - -#: lib/branch_create.tcl:57 -msgid "Match Tracking Branch Name" -msgstr "Съвпадане по името на ÑÐ»ÐµÐ´ÐµÐ½Ð¸Ñ ÐºÐ»Ð¾Ð½" - -#: lib/branch_create.tcl:66 -msgid "Starting Revision" -msgstr "Ðачална верÑиÑ" - -#: lib/branch_create.tcl:72 -msgid "Update Existing Branch:" -msgstr "ОбновÑване на ÑъщеÑтвуващ клон:" - -#: lib/branch_create.tcl:75 -msgid "No" -msgstr "Ðе" - -#: lib/branch_create.tcl:80 -msgid "Fast Forward Only" -msgstr "Само тривиално превъртащо Ñливане" - -#: lib/branch_create.tcl:97 -msgid "Checkout After Creation" -msgstr "Преминаване към клона Ñлед Ñъздаването му" - -#: lib/branch_create.tcl:132 -msgid "Please select a tracking branch." -msgstr "Изберете клон за Ñледени." - -#: lib/branch_create.tcl:141 -#, tcl-format -msgid "Tracking branch %s is not a branch in the remote repository." -msgstr "СледÑщиÑÑ‚ клон — „%s“, не ÑъщеÑтвува в отдалеченото хранилище." - -#: lib/console.tcl:59 -msgid "Working... please wait..." -msgstr "Ð’ момента Ñе извършва дейÑтвие, изчакайте…" - -#: lib/console.tcl:186 -msgid "Success" -msgstr "УÑпех" - -#: lib/console.tcl:200 -msgid "Error: Command Failed" -msgstr "Грешка: неуÑпешно изпълнение на команда" - -#: lib/line.tcl:17 -msgid "Goto Line:" -msgstr "Към ред:" - -#: lib/line.tcl:23 -msgid "Go" -msgstr "Към" - -#: lib/choose_rev.tcl:52 msgid "This Detached Checkout" msgstr "Това неÑвързано изтеглÑне" -#: lib/choose_rev.tcl:60 msgid "Revision Expression:" msgstr "Израз за верÑиÑ:" -#: lib/choose_rev.tcl:72 msgid "Local Branch" msgstr "Локален клон" -#: lib/choose_rev.tcl:77 msgid "Tracking Branch" msgstr "СледÑщ клон" -#: lib/choose_rev.tcl:82 lib/choose_rev.tcl:544 msgid "Tag" msgstr "Етикет" -#: lib/choose_rev.tcl:321 #, tcl-format msgid "Invalid revision: %s" msgstr "Ðеправилна верÑиÑ: %s" -#: lib/choose_rev.tcl:342 msgid "No revision selected." msgstr "Ðе е избрана верÑиÑ." -#: lib/choose_rev.tcl:350 msgid "Revision expression is empty." msgstr "Изразът за верÑÐ¸Ñ Ðµ празен." -#: lib/choose_rev.tcl:537 msgid "Updated" msgstr "Обновен" -#: lib/choose_rev.tcl:565 msgid "URL" msgstr "ÐдреÑ" -#: lib/commit.tcl:9 msgid "" "There is nothing to amend.\n" "\n" @@ -2413,7 +967,6 @@ msgstr "" "Ще Ñъздадете първоначалното подаване. Преди него нÑма други подаваниÑ, които " "да поправите.\n" -#: lib/commit.tcl:18 msgid "" "Cannot amend while merging.\n" "\n" @@ -2426,24 +979,19 @@ msgstr "" "Ð’ момента вÑе още не Ñте завършили Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð¿Ð¾ Ñливане. Ðе може да поправите " "предишното подаване, оÑвен ако първо не преуÑтановите текущото Ñливане.\n" -#: lib/commit.tcl:56 msgid "Error loading commit data for amend:" msgstr "Грешка при зареждане на данните от подаване, които да Ñе поправÑÑ‚:" -#: lib/commit.tcl:83 msgid "Unable to obtain your identity:" msgstr "ИдентификациÑта ви не може да Ñе определи:" -#: lib/commit.tcl:88 msgid "Invalid GIT_COMMITTER_IDENT:" msgstr "Ðеправилно поле „GIT_COMMITTER_IDENT“:" -#: lib/commit.tcl:138 #, tcl-format msgid "warning: Tcl does not support encoding '%s'." msgstr "предупреждение: Tcl не поддържа кодирането „%s“." -#: lib/commit.tcl:158 msgid "" "Last scanned state does not match repository state.\n" "\n" @@ -2460,7 +1008,6 @@ msgstr "" "\n" "Ðвтоматично ще започне нова проверка.\n" -#: lib/commit.tcl:182 #, tcl-format msgid "" "Unmerged files cannot be committed.\n" @@ -2473,7 +1020,6 @@ msgstr "" "Във файла „%s“ има конфликти при Ñливане. За да го подадете, трÑбва първо да " "коригирате конфликтите и да добавите файла към индекÑа за подаване.\n" -#: lib/commit.tcl:190 #, tcl-format msgid "" "Unknown file state %s detected.\n" @@ -2484,7 +1030,6 @@ msgstr "" "\n" "Файлът „%s“ не може да Ñе подаде чрез текущата програма.\n" -#: lib/commit.tcl:198 msgid "" "No changes to commit.\n" "\n" @@ -2494,7 +1039,6 @@ msgstr "" "\n" "ТрÑбва да добавите поне един файл към индекÑа, за да подадете.\n" -#: lib/commit.tcl:213 msgid "" "Please supply a commit message.\n" "\n" @@ -2512,15 +1056,12 @@ msgstr "" "◠Втори ред: празен.\n" "◠ОÑтаналите редове: опишете защо Ñе налага тази промÑна.\n" -#: lib/commit.tcl:244 msgid "Calling pre-commit hook..." msgstr "ИзпълнÑване на куката преди подаване…" -#: lib/commit.tcl:259 msgid "Commit declined by pre-commit hook." msgstr "Подаването е отхвърлено от куката преди подаване." -#: lib/commit.tcl:278 msgid "" "You are about to commit on a detached head. This is a potentially dangerous " "thing to do because if you switch to another branch you will lose your " @@ -2536,32 +1077,25 @@ msgstr "" " \n" "Сигурни ли Ñте, че иÑкате да извършите текущото подаване?" -#: lib/commit.tcl:299 msgid "Calling commit-msg hook..." msgstr "ИзпълнÑване на куката за Ñъобщението при подаване…" -#: lib/commit.tcl:314 msgid "Commit declined by commit-msg hook." msgstr "Подаването е отхвърлено от куката за Ñъобщението при подаване." -#: lib/commit.tcl:327 msgid "Committing changes..." msgstr "Подаване на промените…" -#: lib/commit.tcl:344 msgid "write-tree failed:" msgstr "неуÑпешно запазване на дървото (write-tree):" -#: lib/commit.tcl:345 lib/commit.tcl:395 lib/commit.tcl:422 msgid "Commit failed." msgstr "ÐеуÑпешно подаване." -#: lib/commit.tcl:362 #, tcl-format msgid "Commit %s appears to be corrupt" msgstr "Подаването „%s“ изглежда повредено" -#: lib/commit.tcl:367 msgid "" "No changes to commit.\n" "\n" @@ -2576,106 +1110,63 @@ msgstr "" "\n" "Ðвтоматично ще започне нова проверка.\n" -#: lib/commit.tcl:374 msgid "No changes to commit." msgstr "ÐÑма промени за подаване." -#: lib/commit.tcl:394 msgid "commit-tree failed:" msgstr "неуÑпешно подаване на дървото (commit-tree):" -#: lib/commit.tcl:421 msgid "update-ref failed:" msgstr "неуÑпешно обновÑване на указателите (update-ref):" -#: lib/commit.tcl:514 #, tcl-format msgid "Created commit %s: %s" msgstr "УÑпешно подаване %s: %s" -#: lib/branch_delete.tcl:16 -#, tcl-format -msgid "%s (%s): Delete Branch" -msgstr "%s (%s): Изтриване на клон" - -#: lib/branch_delete.tcl:21 -msgid "Delete Local Branch" -msgstr "Изтриване на локален клон" - -#: lib/branch_delete.tcl:39 -msgid "Local Branches" -msgstr "Локални клони" - -#: lib/branch_delete.tcl:51 -msgid "Delete Only If Merged Into" -msgstr "Изтриване, Ñамо ако промените Ñа Ñлети и другаде" - -#: lib/branch_delete.tcl:103 -#, tcl-format -msgid "The following branches are not completely merged into %s:" -msgstr "Ðе вÑички промени в клоните Ñа Ñлети в „%s“:" - -#: lib/branch_delete.tcl:131 -#, tcl-format -msgid " - %s:" -msgstr " — „%s:“" +msgid "Working... please wait..." +msgstr "Ð’ момента Ñе извършва дейÑтвие, изчакайте…" -#: lib/branch_delete.tcl:141 -#, tcl-format -msgid "" -"Failed to delete branches:\n" -"%s" -msgstr "" -"ÐеуÑпешно триене на клони:\n" -"%s" +msgid "Success" +msgstr "УÑпех" -#: lib/date.tcl:25 -#, tcl-format -msgid "Invalid date from Git: %s" -msgstr "Ðеправилни данни от Git: %s" +msgid "Error: Command Failed" +msgstr "Грешка: неуÑпешно изпълнение на команда" -#: lib/database.tcl:42 msgid "Number of loose objects" msgstr "Брой непакетирани обекти" -#: lib/database.tcl:43 msgid "Disk space used by loose objects" msgstr "ДиÑково проÑтранÑтво заето от непакетирани обекти" -#: lib/database.tcl:44 msgid "Number of packed objects" msgstr "Брой пакетирани обекти" -#: lib/database.tcl:45 msgid "Number of packs" msgstr "Брой пакети" -#: lib/database.tcl:46 msgid "Disk space used by packed objects" msgstr "ДиÑково проÑтранÑтво заето от пакетирани обекти" -#: lib/database.tcl:47 msgid "Packed objects waiting for pruning" msgstr "Пакетирани обекти за окаÑтрÑне" -#: lib/database.tcl:48 msgid "Garbage files" msgstr "Файлове за боклука" -#: lib/database.tcl:66 +#, tcl-format +msgid "%s:" +msgstr "%s:" + #, tcl-format msgid "%s (%s): Database Statistics" msgstr "%s (%s): СтатиÑтика на базата от данни" -#: lib/database.tcl:72 msgid "Compressing the object database" msgstr "КомпреÑиране на базата Ñ Ð´Ð°Ð½Ð½Ð¸ за обектите" -#: lib/database.tcl:83 msgid "Verifying the object database with fsck-objects" msgstr "Проверка на базата Ñ Ð´Ð°Ð½Ð½Ð¸ за обектите Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð°Ñ‚Ð° „fsck-objects“" -#: lib/database.tcl:107 #, tcl-format msgid "" "This repository currently has approximately %i loose objects.\n" @@ -2692,31 +1183,228 @@ msgstr "" "\n" "Да Ñе започне ли компреÑирането?" -#: lib/error.tcl:20 +#, tcl-format +msgid "Invalid date from Git: %s" +msgstr "Ðеправилни данни от Git: %s" + +msgid "" +"* No differences detected; stage the file to de-list it from Unstaged " +"Changes.\n" +msgstr "" +"â— ÐÑма разлики. Добавете файла към индекÑа, за да Ñе извади от промените " +"извън индекÑа.\n" + +msgid "* Click to find other files that may have the same state.\n" +msgstr "â— ÐатиÑнете, за да потърÑите други файлове в това ÑÑŠÑтоÑние.\n" + +#, tcl-format +msgid "Loading diff of %s..." +msgstr "Зареждане на разликите в „%s“…" + +msgid "" +"LOCAL: deleted\n" +"REMOTE:\n" +msgstr "" +"ЛОКÐЛÐО: изтрит\n" +"ОТДÐЛЕЧЕÐО:\n" + +msgid "" +"REMOTE: deleted\n" +"LOCAL:\n" +msgstr "" +"ОТДÐЛЕЧЕÐО: изтрит\n" +"ЛОКÐЛÐО:\n" + +msgid "LOCAL:\n" +msgstr "ЛОКÐЛÐО:\n" + +msgid "REMOTE:\n" +msgstr "ОТДÐЛЕЧЕÐО:\n" + +#, tcl-format +msgid "Unable to display %s" +msgstr "Файлът „%s“ не може да Ñе покаже" + +msgid "Error loading file:" +msgstr "Грешка при зареждане на файл:" + +msgid "Git Repository (subproject)" +msgstr "Хранилище на Git (подмодул)" + +msgid "* Binary file (not showing content)." +msgstr "◠Двоичен файл (Ñъдържанието не Ñе показва)." + +#, tcl-format +msgid "" +"* Untracked file is %d bytes.\n" +"* Showing only first %d bytes.\n" +msgstr "" +"â— ÐеÑледениÑÑ‚ файл е %d байта.\n" +"◠Показват Ñе Ñамо първите %d байта.\n" + +#, tcl-format +msgid "" +"\n" +"* Untracked file clipped here by %s.\n" +"* To see the entire file, use an external editor.\n" +msgstr "" +"\n" +"â— ÐеÑледениÑÑ‚ файл е отрÑзан дотук от програмата „%s“.\n" +"◠Използвайте външен редактор, за да видите Ñ†ÐµÐ»Ð¸Ñ Ñ„Ð°Ð¹Ð».\n" + +msgid "Failed to unstage selected hunk." +msgstr "Избраното парче не може да Ñе извади от индекÑа." + +msgid "Failed to revert selected hunk." +msgstr "Избраното парче не може да Ñе върне." + +msgid "Failed to stage selected hunk." +msgstr "Избраното парче не може да Ñе добави към индекÑа." + +msgid "Failed to unstage selected line." +msgstr "ИзбраниÑÑ‚ ред не може да Ñе извади от индекÑа." + +msgid "Failed to revert selected line." +msgstr "ИзбраниÑÑ‚ ред не може да Ñе върне." + +msgid "Failed to stage selected line." +msgstr "ИзбраниÑÑ‚ ред не може да Ñе добави към индекÑа." + +msgid "Failed to undo last revert." +msgstr "ÐеуÑпешна отмÑна на поÑледното връщане." + +msgid "Default" +msgstr "Стандартното" + +#, tcl-format +msgid "System (%s)" +msgstr "СиÑтемното (%s)" + +msgid "Other" +msgstr "Друго" + #, tcl-format msgid "%s: error" msgstr "%s: грешка" -#: lib/error.tcl:36 #, tcl-format msgid "%s: warning" msgstr "%s: предупреждение" -#: lib/error.tcl:80 #, tcl-format msgid "%s hook failed:" msgstr "%s: грешка от куката" -#: lib/error.tcl:96 msgid "You must correct the above errors before committing." msgstr "Преди да можете да подадете, коригирайте горните грешки." -#: lib/error.tcl:116 #, tcl-format msgid "%s (%s): error" msgstr "%s (%s): грешка" -#: lib/merge.tcl:13 +msgid "Unable to unlock the index." +msgstr "ИндекÑÑŠÑ‚ не може да Ñе отключи." + +msgid "Index Error" +msgstr "Грешка в индекÑа" + +msgid "" +"Updating the Git index failed. A rescan will be automatically started to " +"resynchronize git-gui." +msgstr "" +"ÐеуÑпешно обновÑване на индекÑа на Git. Ðвтоматично ще започне нова проверка " +"за Ñинхронизирането на git-gui." + +msgid "Continue" +msgstr "Продължаване" + +msgid "Unlock Index" +msgstr "Отключване на индекÑа" + +msgid "files" +msgstr "файлове" + +msgid "Unstaging selected files from commit" +msgstr "Изваждане на избраните файлове от подаването" + +#, tcl-format +msgid "Unstaging %s from commit" +msgstr "Изваждане на „%s“ от подаването" + +msgid "Ready to commit." +msgstr "ГотовноÑÑ‚ за подаване." + +msgid "Adding selected files" +msgstr "ДобавÑне на избраните файлове" + +#, tcl-format +msgid "Adding %s" +msgstr "ДобавÑне на „%s“" + +#, tcl-format +msgid "Stage %d untracked files?" +msgstr "Да Ñе добавÑÑ‚ ли %d неÑледени файла към индекÑа?" + +msgid "Adding all changed files" +msgstr "ДобавÑне на вÑички променени файлове" + +#, tcl-format +msgid "Revert changes in file %s?" +msgstr "Да Ñе махнат ли промените във файла „%s“?" + +#, tcl-format +msgid "Revert changes in these %i files?" +msgstr "Да Ñе махнат ли промените в тези %i файла?" + +msgid "Any unstaged changes will be permanently lost by the revert." +msgstr "" +"Ð’Ñички промени, които не Ñа били добавени в индекÑа, ще Ñе загубÑÑ‚ " +"безвъзвратно." + +msgid "Do Nothing" +msgstr "Ðищо да не Ñе прави" + +#, tcl-format +msgid "Delete untracked file %s?" +msgstr "Да Ñе изтрие ли неÑледениÑÑ‚ файл „%s“?" + +#, tcl-format +msgid "Delete these %i untracked files?" +msgstr "Да Ñе изтриÑÑ‚ ли тези %d неÑледени файла?" + +msgid "Files will be permanently deleted." +msgstr "Файловете ще Ñе изтриÑÑ‚ окончателно." + +msgid "Delete Files" +msgstr "Изтриване на файлове" + +msgid "Deleting" +msgstr "Изтриване" + +msgid "Encountered errors deleting files:\n" +msgstr "Грешки при изтриване на файловете:\n" + +#, tcl-format +msgid "None of the %d selected files could be deleted." +msgstr "Ðикой от избраните %d файла не бе изтрит." + +#, tcl-format +msgid "%d of the %d selected files could not be deleted." +msgstr "%d от избраните %d файла не бÑха изтрити." + +msgid "Reverting selected files" +msgstr "Махане на промените в избраните файлове" + +#, tcl-format +msgid "Reverting %s" +msgstr "Махане на промените в „%s“" + +msgid "Goto Line:" +msgstr "Към ред:" + +msgid "Go" +msgstr "Към" + msgid "" "Cannot merge while amending.\n" "\n" @@ -2727,7 +1415,6 @@ msgstr "" "ТрÑбва да завършите поправÑнето на текущото подаване, преди да започнете " "Ñливане.\n" -#: lib/merge.tcl:27 msgid "" "Last scanned state does not match repository state.\n" "\n" @@ -2744,7 +1431,6 @@ msgstr "" "Ðвтоматично ще започне нова проверка.\n" "\n" -#: lib/merge.tcl:45 #, tcl-format msgid "" "You are in the middle of a conflicted merge.\n" @@ -2762,7 +1448,6 @@ msgstr "" "завършите текущото Ñливане чрез подаване. Чак тогава може да започнете ново " "Ñливане.\n" -#: lib/merge.tcl:55 #, tcl-format msgid "" "You are in the middle of a change.\n" @@ -2779,39 +1464,31 @@ msgstr "" "ТрÑбва да завършите текущото подаване, преди да започнете Ñливане. Така ще " "можете леÑно да преуÑтановите Ñливането, ако възникне нужда.\n" -#: lib/merge.tcl:108 #, tcl-format msgid "%s of %s" msgstr "%s от общо %s" -#: lib/merge.tcl:126 #, tcl-format msgid "Merging %s and %s..." msgstr "Сливане на „%s“ и „%s“…" -#: lib/merge.tcl:137 msgid "Merge completed successfully." msgstr "Сливането завърши уÑпешно." -#: lib/merge.tcl:139 msgid "Merge failed. Conflict resolution is required." msgstr "ÐеуÑпешно Ñливане — има конфликти за коригиране." -#: lib/merge.tcl:156 #, tcl-format msgid "%s (%s): Merge" msgstr "%s (%s): Сливане" -#: lib/merge.tcl:164 #, tcl-format msgid "Merge Into %s" msgstr "Сливане в „%s“" -#: lib/merge.tcl:183 msgid "Revision To Merge" msgstr "ВерÑÐ¸Ñ Ð·Ð° Ñливане" -#: lib/merge.tcl:218 msgid "" "Cannot abort while amending.\n" "\n" @@ -2821,7 +1498,6 @@ msgstr "" "\n" "ТрÑбва да завършите поправката на това подаване.\n" -#: lib/merge.tcl:228 msgid "" "Abort merge?\n" "\n" @@ -2835,7 +1511,6 @@ msgstr "" "\n" "ÐаиÑтина ли да Ñе преуÑтанови Ñливането?" -#: lib/merge.tcl:234 msgid "" "Reset changes?\n" "\n" @@ -2849,18 +1524,621 @@ msgstr "" "\n" "ÐаиÑтина ли да Ñе занулÑÑ‚ промените?" -#: lib/merge.tcl:246 msgid "Aborting" msgstr "ПреуÑтановÑване" -#: lib/merge.tcl:247 msgid "files reset" msgstr "файла ÑÑŠÑ Ð·Ð°Ð½ÑƒÐ»ÐµÐ½Ð¸ промени" -#: lib/merge.tcl:277 msgid "Abort failed." msgstr "ÐеуÑпешно преуÑтановÑване." -#: lib/merge.tcl:279 msgid "Abort completed. Ready." msgstr "УÑпешно преуÑтановÑване. ГотовноÑÑ‚ за Ñледващо дейÑтвие." + +msgid "Force resolution to the base version?" +msgstr "Да Ñе използва базовата верÑиÑ" + +msgid "Force resolution to this branch?" +msgstr "Да Ñе използва верÑиÑта от този клон" + +msgid "Force resolution to the other branch?" +msgstr "Да Ñе използва верÑиÑта от Ð´Ñ€ÑƒÐ³Ð¸Ñ ÐºÐ»Ð¾Ð½" + +#, tcl-format +msgid "" +"Note that the diff shows only conflicting changes.\n" +"\n" +"%s will be overwritten.\n" +"\n" +"This operation can be undone only by restarting the merge." +msgstr "" +"Разликата показва Ñамо разликите Ñ ÐºÐ¾Ð½Ñ„Ð»Ð¸ÐºÑ‚.\n" +"\n" +"Файлът „%s“ ще Ñе презапише.\n" +"\n" +"Тази Ð¾Ð¿ÐµÑ€Ð°Ñ†Ð¸Ñ Ð¼Ð¾Ð¶Ðµ да Ñе отмени Ñамо чрез започване на Ñливането наново." + +#, tcl-format +msgid "File %s seems to have unresolved conflicts, still stage?" +msgstr "" +"Изглежда, че вÑе още има некоригирани конфликти във файла „%s“. Да Ñе добави " +"ли файлът към индекÑа?" + +#, tcl-format +msgid "Adding resolution for %s" +msgstr "ДобавÑне на ÐºÐ¾Ñ€ÐµÐºÑ†Ð¸Ñ Ð½Ð° конфликтите в „%s“" + +msgid "Cannot resolve deletion or link conflicts using a tool" +msgstr "" +"Конфликтите при Ñимволни връзки или изтриване не може да Ñе коригират Ñ " +"външна програма." + +msgid "Conflict file does not exist" +msgstr "Файлът, в който е конфликтът, не ÑъщеÑтвува" + +#, tcl-format +msgid "Not a GUI merge tool: '%s'" +msgstr "Това не е графична програма за Ñливане: „%s“" + +#, tcl-format +msgid "" +"Unable to process square brackets in \"mergetool.%s.cmd\" configuration " +"option.\n" +"\n" +"Please remove the square brackets." +msgstr "" +"Квадратните Ñкоби в наÑтройката „mergetool.%s.cmd“ не може да Ñе обработÑÑ‚.\n" +"\n" +"Махнете ги." + +#, tcl-format +msgid "" +"Unsupported merge tool '%s'.\n" +"\n" +"To use this tool, configure \"mergetool.%s.cmd\" as shown in the git-config " +"manual page." +msgstr "" +"Ðеподдържана програма за Ñливане: „%s“.\n" +"\n" +"За да Ñ Ð¸Ð·Ð¿Ð¾Ð»Ð·Ð²Ð°Ñ‚Ðµ, наÑтройте „mergetool.%s.cmd“ както както е обÑÑнено в " +"Ñтраницата на ръководÑтвото за „git-config“." + +msgid "Merge tool is already running, terminate it?" +msgstr "Програмата за Ñливане вече е Ñтартирана. Да Ñе изключи ли?" + +#, tcl-format +msgid "" +"Error retrieving versions:\n" +"%s" +msgstr "" +"Грешка при изтеглÑнето на верÑии:\n" +"%s" + +#, tcl-format +msgid "" +"Could not start the merge tool:\n" +"\n" +"%s" +msgstr "" +"Програмата за Ñливане не може да Ñе Ñтартира:\n" +"\n" +"%s" + +msgid "Running merge tool..." +msgstr "Стартиране на програмата за Ñливане…" + +msgid "Merge tool failed." +msgstr "Грешка в програмата за Ñливане." + +#, tcl-format +msgid "Invalid global encoding '%s'" +msgstr "Ðеправилно глобално кодиране „%s“" + +#, tcl-format +msgid "Invalid repo encoding '%s'" +msgstr "Ðеправилно кодиране „%s“ на хранилището" + +msgid "Restore Defaults" +msgstr "Стандартни наÑтройки" + +msgid "Save" +msgstr "Запазване" + +#, tcl-format +msgid "%s Repository" +msgstr "Хранилище „%s“" + +msgid "Global (All Repositories)" +msgstr "Глобално (за вÑички хранилища)" + +msgid "User Name" +msgstr "ПотребителÑко име" + +msgid "Email Address" +msgstr "ÐÐ´Ñ€ÐµÑ Ð½Ð° е-поща" + +msgid "Summarize Merge Commits" +msgstr "Обобщаване на подаваниÑта при Ñливане" + +msgid "Merge Verbosity" +msgstr "ПодробноÑти при ÑливаниÑта" + +msgid "Show Diffstat After Merge" +msgstr "Извеждане на ÑтатиÑтика Ñлед ÑливаниÑта" + +msgid "Use Merge Tool" +msgstr "Използване на програма за Ñливане" + +msgid "Trust File Modification Timestamps" +msgstr "Доверие във времето на промÑна на файловете" + +msgid "Prune Tracking Branches During Fetch" +msgstr "ОкаÑтрÑне на ÑледÑщите клонове при доÑтавÑне" + +msgid "Match Tracking Branches" +msgstr "ÐапаÑване на ÑледÑщите клонове" + +msgid "Use Textconv For Diffs and Blames" +msgstr "Използване на „textconv“ за разликите и анотирането" + +msgid "Blame Copy Only On Changed Files" +msgstr "Ðнотиране на копието Ñамо по променените файлове" + +msgid "Maximum Length of Recent Repositories List" +msgstr "МакÑимален брой на ÑпиÑъка „Скоро ползвани“ хранилища" + +msgid "Minimum Letters To Blame Copy On" +msgstr "Минимален брой знаци за анотиране на копието" + +msgid "Blame History Context Radius (days)" +msgstr "ИÑторичеÑки обхват за анотиране в дни" + +msgid "Number of Diff Context Lines" +msgstr "Брой редове за контекÑта на разликите" + +msgid "Additional Diff Parameters" +msgstr "Ðргументи към командата за разликите" + +msgid "Commit Message Text Width" +msgstr "Широчина на текÑта на Ñъобщението при подаване" + +msgid "New Branch Name Template" +msgstr "Шаблон за името на новите клони" + +msgid "Default File Contents Encoding" +msgstr "Кодиране на файловете" + +msgid "Warn before committing to a detached head" +msgstr "Предупреждаване при подаване към неÑвързан указател" + +msgid "Staging of untracked files" +msgstr "ДобавÑне на неÑледените файлове към индекÑа" + +msgid "Show untracked files" +msgstr "Показване на неÑледените файлове" + +msgid "Tab spacing" +msgstr "Ширина на табулациÑта" + +msgid "Change" +msgstr "СмÑна" + +msgid "Spelling Dictionary:" +msgstr "ПравопиÑен речник:" + +msgid "Change Font" +msgstr "СмÑна на шрифта" + +#, tcl-format +msgid "Choose %s" +msgstr "Избор на „%s“" + +msgid "pt." +msgstr "тчк." + +msgid "Preferences" +msgstr "ÐаÑтройки" + +msgid "Failed to completely save options:" +msgstr "ÐеуÑпешно запазване на наÑтройките:" + +#, tcl-format +msgid "%s (%s): Add Remote" +msgstr "%s (%s): ДобавÑне на отдалечено хранилище" + +msgid "Add New Remote" +msgstr "ДобавÑне на отдалечено хранилище" + +msgid "Add" +msgstr "ДобавÑне" + +msgid "Remote Details" +msgstr "Данни за отдалеченото хранилище" + +msgid "Location:" +msgstr "МеÑтоположение:" + +msgid "Further Action" +msgstr "Следващо дейÑтвие" + +msgid "Fetch Immediately" +msgstr "Ðезабавно доÑтавÑне" + +msgid "Initialize Remote Repository and Push" +msgstr "Инициализиране на отдалеченото хранилище и изтлаÑкване на промените" + +msgid "Do Nothing Else Now" +msgstr "Да не Ñе прави нищо" + +msgid "Please supply a remote name." +msgstr "Задайте име за отдалеченото хранилище." + +#, tcl-format +msgid "'%s' is not an acceptable remote name." +msgstr "Отдалечено хранилище не може да Ñе казва „%s“." + +#, tcl-format +msgid "Failed to add remote '%s' of location '%s'." +msgstr "ÐеуÑпешно добавÑне на отдалеченото хранилище „%s“ от Ð°Ð´Ñ€ÐµÑ â€ž%s“." + +#, tcl-format +msgid "fetch %s" +msgstr "доÑтавÑне на „%s“" + +#, tcl-format +msgid "Fetching the %s" +msgstr "ДоÑтавÑне на „%s“" + +#, tcl-format +msgid "Do not know how to initialize repository at location '%s'." +msgstr "Хранилището Ñ Ð¼ÐµÑтоположение „%s“ не може да Ñе инициализира." + +#, tcl-format +msgid "push %s" +msgstr "изтлаÑкване на „%s“" + +#, tcl-format +msgid "Setting up the %s (at %s)" +msgstr "ДобавÑне на хранилище „%s“ (Ñ Ð°Ð´Ñ€ÐµÑ â€ž%s“)" + +#, tcl-format +msgid "%s (%s): Delete Branch Remotely" +msgstr "%s (%s): Изтриване на Ð¾Ñ‚Ð´Ð°Ð»ÐµÑ‡ÐµÐ½Ð¸Ñ ÐºÐ»Ð¾Ð½" + +msgid "Delete Branch Remotely" +msgstr "Изтриване на Ð¾Ñ‚Ð´Ð°Ð»ÐµÑ‡ÐµÐ½Ð¸Ñ ÐºÐ»Ð¾Ð½" + +msgid "From Repository" +msgstr "От хранилище" + +msgid "Remote:" +msgstr "Отдалечено хранилище:" + +msgid "Arbitrary Location:" +msgstr "Произволно меÑтоположение:" + +msgid "Branches" +msgstr "Клони" + +msgid "Delete Only If" +msgstr "Изтриване, Ñамо ако" + +msgid "Merged Into:" +msgstr "СлÑÑ‚ в:" + +msgid "A branch is required for 'Merged Into'." +msgstr "За данните „СлÑÑ‚ в“ е необходимо да зададете клон." + +#, tcl-format +msgid "" +"The following branches are not completely merged into %s:\n" +"\n" +" - %s" +msgstr "" +"Следните клони не Ñа Ñлети напълно в „%s“:\n" +"\n" +" â— %s" + +#, tcl-format +msgid "" +"One or more of the merge tests failed because you have not fetched the " +"necessary commits. Try fetching from %s first." +msgstr "" +"Поне една от пробите за Ñливане е неуÑпешна, защото не Ñте доÑтавили вÑички " +"необходими подаваниÑ. Пробвайте първо да доÑтавите подаваниÑта от „%s“." + +msgid "Please select one or more branches to delete." +msgstr "Изберете поне един клон за изтриване." + +#, tcl-format +msgid "Deleting branches from %s" +msgstr "Изтриване на клони от „%s“" + +msgid "No repository selected." +msgstr "Ðе е избрано хранилище." + +#, tcl-format +msgid "Scanning %s..." +msgstr "ПретърÑване на „%s“…" + +msgid "Push to" +msgstr "ИзтлаÑкване към" + +msgid "Remove Remote" +msgstr "Премахване на отдалечено хранилище" + +msgid "Prune from" +msgstr "ОкаÑтрÑне от" + +msgid "Fetch from" +msgstr "ДоÑтавÑне от" + +msgid "All" +msgstr "Ð’Ñички" + +msgid "Find:" +msgstr "ТърÑене:" + +msgid "Next" +msgstr "Следваща поÑва" + +msgid "Prev" +msgstr "Предишна поÑва" + +msgid "RegExp" +msgstr "РегИзр" + +msgid "Case" +msgstr "Главни/Малки" + +#, tcl-format +msgid "%s (%s): Create Desktop Icon" +msgstr "%s (%s): ДобавÑне на икона на Ñ€Ð°Ð±Ð¾Ñ‚Ð½Ð¸Ñ Ð¿Ð»Ð¾Ñ‚" + +msgid "Cannot write shortcut:" +msgstr "Клавишната ÐºÐ¾Ð¼Ð±Ð¸Ð½Ð°Ñ†Ð¸Ñ Ð½Ðµ може да Ñе запази:" + +msgid "Cannot write icon:" +msgstr "Иконата не може да Ñе запази:" + +msgid "Unsupported spell checker" +msgstr "Тази програма за проверка на правопиÑа не Ñе поддържа" + +msgid "Spell checking is unavailable" +msgstr "ЛипÑва програма за проверка на правопиÑа" + +msgid "Invalid spell checking configuration" +msgstr "Ðеправилни наÑтройки на проверката на правопиÑа" + +#, tcl-format +msgid "Reverting dictionary to %s." +msgstr "Ползване на речник за език „%s“." + +msgid "Spell checker silently failed on startup" +msgstr "Програмата за Ð¿Ñ€Ð°Ð²Ð¾Ð¿Ð¸Ñ Ð´Ð°Ð¶Ðµ не Ñтартира уÑпешно." + +msgid "Unrecognized spell checker" +msgstr "Ðепозната програма за проверка на правопиÑа" + +msgid "No Suggestions" +msgstr "ÐÑма предложениÑ" + +msgid "Unexpected EOF from spell checker" +msgstr "Ðеочакван край на файл от програмата за проверка на правопиÑа" + +msgid "Spell Checker Failed" +msgstr "Грешка в програмата за проверка на правопиÑа" + +msgid "No keys found." +msgstr "Ðе Ñа открити ключове." + +#, tcl-format +msgid "Found a public key in: %s" +msgstr "Открит е публичен ключ в „%s“" + +msgid "Generate Key" +msgstr "Генериране на ключ" + +msgid "Copy To Clipboard" +msgstr "Копиране към ÑиÑÑ‚ÐµÐ¼Ð½Ð¸Ñ Ð±ÑƒÑ„ÐµÑ€" + +msgid "Your OpenSSH Public Key" +msgstr "ПубличниÑÑ‚ ви ключ за OpenSSH" + +msgid "Generating..." +msgstr "Генериране…" + +#, tcl-format +msgid "" +"Could not start ssh-keygen:\n" +"\n" +"%s" +msgstr "" +"Програмата „ssh-keygen“ не може да Ñе Ñтартира:\n" +"\n" +"%s" + +msgid "Generation failed." +msgstr "ÐеуÑпешно генериране." + +msgid "Generation succeeded, but no keys found." +msgstr "Генерирането завърши уÑпешно, а не Ñа намерени ключове." + +#, tcl-format +msgid "Your key is in: %s" +msgstr "Ключът ви е в „%s“" + +#, tcl-format +msgid "%s ... %*i of %*i %s (%3i%%)" +msgstr "%s… %*i от общо %*i %s (%3i%%)" + +#, tcl-format +msgid "%s (%s): Add Tool" +msgstr "%s (%s): ДобавÑне на команда" + +msgid "Add New Tool Command" +msgstr "ДобавÑне на команда" + +msgid "Add globally" +msgstr "Глобално добавÑне" + +msgid "Tool Details" +msgstr "ПодробноÑти за командата" + +msgid "Use '/' separators to create a submenu tree:" +msgstr "За Ñъздаване на подменюта използвайте знака „/“ за разделител:" + +msgid "Command:" +msgstr "Команда:" + +msgid "Show a dialog before running" +msgstr "Преди изпълнение да Ñе извежда диалогов прозорец" + +msgid "Ask the user to select a revision (sets $REVISION)" +msgstr "ПотребителÑÑ‚ да укаже верÑÐ¸Ñ (задаване на променливата $REVISION)" + +msgid "Ask the user for additional arguments (sets $ARGS)" +msgstr "" +"ПотребителÑÑ‚ да укаже допълнителни аргументи (задаване на променливата $ARGS)" + +msgid "Don't show the command output window" +msgstr "Без показване на прозорец Ñ Ð¸Ð·Ñ…Ð¾Ð´Ð° от командата" + +msgid "Run only if a diff is selected ($FILENAME not empty)" +msgstr "" +"Стартиране Ñамо Ñлед избор на разлика (променливата $FILENAME не е празна)" + +msgid "Please supply a name for the tool." +msgstr "Задайте име за командата." + +#, tcl-format +msgid "Tool '%s' already exists." +msgstr "Командата „%s“ вече ÑъщеÑтвува." + +#, tcl-format +msgid "" +"Could not add tool:\n" +"%s" +msgstr "" +"Командата не може да Ñе добави:\n" +"%s" + +#, tcl-format +msgid "%s (%s): Remove Tool" +msgstr "%s (%s): Премахване на команда" + +msgid "Remove Tool Commands" +msgstr "Премахване на команди" + +msgid "Remove" +msgstr "Премахване" + +msgid "(Blue denotes repository-local tools)" +msgstr "(командите към локалното хранилище Ñа обозначени в Ñиньо)" + +#, tcl-format +msgid "%s (%s):" +msgstr "%s (%s):" + +#, tcl-format +msgid "Run Command: %s" +msgstr "Изпълнение на командата „%s“" + +msgid "Arguments" +msgstr "Ðргументи" + +msgid "OK" +msgstr "Добре" + +#, tcl-format +msgid "Running %s requires a selected file." +msgstr "За изпълнението на „%s“ трÑбва да изберете файл." + +#, tcl-format +msgid "Are you sure you want to run %1$s on file \"%2$s\"?" +msgstr "Сигурни ли Ñте, че иÑкате да изпълните „%1$s“ върху файла „%2$s“?" + +#, tcl-format +msgid "Are you sure you want to run %s?" +msgstr "Сигурни ли Ñте, че иÑкате да изпълните „%s“?" + +#, tcl-format +msgid "Tool: %s" +msgstr "Команда: %s" + +#, tcl-format +msgid "Running: %s" +msgstr "Изпълнение: %s" + +#, tcl-format +msgid "Tool completed successfully: %s" +msgstr "Командата завърши уÑпешно: %s" + +#, tcl-format +msgid "Tool failed: %s" +msgstr "Командата върна грешка: %s" + +#, tcl-format +msgid "Fetching new changes from %s" +msgstr "ДоÑтавÑне на промените от „%s“" + +#, tcl-format +msgid "remote prune %s" +msgstr "окаÑтрÑне на ÑледÑщите клони към „%s“" + +#, tcl-format +msgid "Pruning tracking branches deleted from %s" +msgstr "ОкаÑтрÑне на ÑледÑщите клони на изтритите клони от „%s“" + +msgid "fetch all remotes" +msgstr "доÑтавÑне от вÑички отдалечени" + +msgid "Fetching new changes from all remotes" +msgstr "ДоÑтавÑне на промените от вÑички отдалечени хранилища" + +msgid "remote prune all remotes" +msgstr "окаÑтрÑне на ÑледÑщите изтрити" + +msgid "Pruning tracking branches deleted from all remotes" +msgstr "" +"ОкаÑтрÑне на ÑледÑщите клони на изтритите клони от вÑички отдалечени " +"хранилища" + +#, tcl-format +msgid "Pushing changes to %s" +msgstr "ИзтлаÑкване на промените към „%s“" + +#, tcl-format +msgid "Mirroring to %s" +msgstr "ИзтлаÑкване на вÑичко към „%s“" + +#, tcl-format +msgid "Pushing %s %s to %s" +msgstr "ИзтлаÑкване на %s „%s“ към „%s“" + +msgid "Push Branches" +msgstr "Клони за изтлаÑкване" + +msgid "Source Branches" +msgstr "Клони-източници" + +msgid "Destination Repository" +msgstr "Целево хранилище" + +msgid "Transfer Options" +msgstr "ÐаÑтройки при пренаÑÑнето" + +msgid "Force overwrite existing branch (may discard changes)" +msgstr "" +"Изрично презапиÑване на ÑъщеÑтвуващ клон (нÑкои промени може да Ñе загубÑÑ‚)" + +msgid "Use thin pack (for slow network connections)" +msgstr "МакÑимална компреÑÐ¸Ñ (за бавни мрежови връзки)" + +msgid "Include tags" +msgstr "Включване на етикетите" + +#, tcl-format +msgid "%s (%s): Push" +msgstr "%s (%s): ИзтлаÑкване" diff --git a/git-gui/windows/git-gui.sh b/git-gui/windows/git-gui.sh index b1845c5055..38debe376c 100755 --- a/git-gui/windows/git-gui.sh +++ b/git-gui/windows/git-gui.sh @@ -13,13 +13,5 @@ if { $argc >=2 && [lindex $argv 0] == "--working-dir" } { incr argc -2 } -set basedir [file dirname \ - [file dirname \ - [file dirname [info script]]]] -set bindir [file join $basedir bin] -set bindir "$bindir;[file join $basedir mingw bin]" -regsub -all ";" $bindir "\\;" bindir -set env(PATH) "$bindir;$env(PATH)" -unset bindir - -source [file join [file dirname [info script]] git-gui.tcl] +set thisdir [file normalize [file dirname [info script]]] +source [file join $thisdir git-gui.tcl] diff --git a/git-send-email.perl b/git-send-email.perl index 659e6c588b..cd4b316ddc 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -62,7 +62,7 @@ git send-email --translate-aliases --smtp-user <str> * Username for SMTP-AUTH. --smtp-pass <str> * Password for SMTP-AUTH; not necessary. --smtp-encryption <str> * tls or ssl; anything else disables. - --smtp-ssl * Deprecated. Use '--smtp-encryption ssl'. + --smtp-ssl * Deprecated. Use `--smtp-encryption ssl`. --smtp-ssl-cert-path <str> * Path to ca-certificates (either directory or file). Pass an empty string to disable certificate verification. @@ -73,6 +73,10 @@ git send-email --translate-aliases --no-smtp-auth * Disable SMTP authentication. Shorthand for `--smtp-auth=none` --smtp-debug <0|1> * Disable, enable Net::SMTP debug. + --imap-sent-folder <str> * IMAP folder where a copy of the emails should be sent. + Make sure `git imap-send` is set up to use this feature. + --[no-]use-imap-only * Only copy emails to the IMAP folder specified by + `--imap-sent-folder` instead of actually sending them. --batch-size <int> * send max <int> message per connection. --relogin-delay <int> * delay <int> seconds between two successive login. @@ -200,7 +204,7 @@ my $re_encoded_word = qr/=\?($re_token)\?($re_token)\?($re_encoded_text)\?=/; # Variables we fill in automatically, or via prompting: my (@to,@cc,@xh,$envelope_sender, - $initial_in_reply_to,$reply_to,$initial_subject,@files, + $initial_in_reply_to,$reply_to,$initial_subject,@files,@imap_copy, $author,$sender,$smtp_authpass,$annotate,$compose,$time); # Things we either get from config, *or* are overridden on the # command-line. @@ -277,6 +281,7 @@ my ($smtp_server, $smtp_server_port, @smtp_server_options); my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path); my ($batch_size, $relogin_delay); my ($identity, $aliasfiletype, @alias_files, $smtp_domain, $smtp_auth); +my ($imap_sent_folder); my ($confirm); my (@suppress_cc); my ($auto_8bit_encoding); @@ -293,6 +298,7 @@ my $mailmap = 0; my $target_xfer_encoding = 'auto'; my $forbid_sendmail_variables = 1; my $outlook_id_fix = 'auto'; +my $use_imap_only = 0; my %config_bool_settings = ( "thread" => \$thread, @@ -309,6 +315,7 @@ my %config_bool_settings = ( "forbidsendmailvariables" => \$forbid_sendmail_variables, "mailmap" => \$mailmap, "outlookidfix" => \$outlook_id_fix, + "useimaponly" => \$use_imap_only, ); my %config_settings = ( @@ -322,6 +329,7 @@ my %config_settings = ( "smtpauth" => \$smtp_auth, "smtpbatchsize" => \$batch_size, "smtprelogindelay" => \$relogin_delay, + "imapsentfolder" => \$imap_sent_folder, "to" => \@config_to, "tocmd" => \$to_cmd, "cc" => \@config_cc, @@ -527,6 +535,8 @@ my %options = ( "smtp-domain:s" => \$smtp_domain, "smtp-auth=s" => \$smtp_auth, "no-smtp-auth" => sub {$smtp_auth = 'none'}, + "imap-sent-folder=s" => \$imap_sent_folder, + "use-imap-only!" => \$use_imap_only, "annotate!" => \$annotate, "compose" => \$compose, "quiet" => \$quiet, @@ -1653,8 +1663,18 @@ EOF default => $ask_default); die __("Send this email reply required") unless defined $_; if (/^n/i) { + # If we are skipping a message, we should make sure that + # the next message is treated as the successor to the + # previously sent message, and not the skipped message. + $message_num--; return 0; } elsif (/^e/i) { + # Since the same message will be sent again, we need to + # decrement the message number to the previous message. + # Otherwise, the edited message will be treated as a + # different message sent after the original non-edited + # message. + $message_num--; return -1; } elsif (/^q/i) { cleanup_compose_files(); @@ -1668,6 +1688,8 @@ EOF if ($dry_run) { # We don't want to send the email. + } elsif ($use_imap_only) { + die __("The destination IMAP folder is not properly defined.") if !defined $imap_sent_folder; } elsif (defined $sendmail_cmd || file_name_is_absolute($smtp_server)) { my $pid = open my $sm, '|-'; defined $pid or die $!; @@ -1778,7 +1800,8 @@ EOF if (is_outlook($smtp_server)) { if ($smtp->message =~ /<([^>]+)>/) { $message_id = "<$1>"; - printf __("Outlook reassigned Message-ID to: %s\n"), $message_id; + $header =~ s/^(Message-ID:\s*).*\n/${1}$message_id\n/m; + printf __("Outlook reassigned Message-ID to: %s\n"), $message_id if $smtp->debug; } else { warn __("Warning: Could not retrieve Message-ID from server response.\n"); } @@ -1818,6 +1841,17 @@ EOF print "\n"; } + if ($imap_sent_folder && !$dry_run) { + my $imap_header = $header; + if (@initial_bcc) { + # Bcc is not a part of $header, so we add it here. + # This is only for the IMAP copy, not for the actual email + # sent to the recipients. + $imap_header .= "Bcc: " . join(", ", @initial_bcc) . "\n"; + } + push @imap_copy, "From git-send-email\n$imap_header\n$message"; + } + return 1; } @@ -1920,6 +1954,9 @@ sub pre_process_file { $in_reply_to = $1; } } + elsif (/^Reply-To: (.*)/i) { + $reply_to = $1; + } elsif (/^References: (.*)/i) { if (!$initial_in_reply_to || $thread) { $references = $1; @@ -2101,6 +2138,17 @@ if ($validate) { } } + # Validate the SMTP server port, if provided. + if (defined $smtp_server_port) { + my $port = Git::port_num($smtp_server_port); + if ($port) { + $smtp_server_port = $port; + } else { + die sprintf(__("error: invalid SMTP port '%s'\n"), + $smtp_server_port); + } + } + # Run the loop once again to avoid gaps in the counter due to FIFO # arguments provided by the user. my $num = 1; @@ -2201,6 +2249,19 @@ sub cleanup_compose_files { $smtp->quit if $smtp; +if ($imap_sent_folder && @imap_copy && !$dry_run) { + my $imap_input = join("\n", @imap_copy); + eval { + print "\nStarting git imap-send...\n"; + my ($fh, $ctx) = Git::command_input_pipe(['imap-send', '-f', $imap_sent_folder]); + print $fh $imap_input; + Git::command_close_pipe($fh, $ctx); + 1; + } or do { + warn "Warning: failed to send messages to IMAP folder $imap_sent_folder: $@"; + }; +} + sub apply_transfer_encoding { my $message = shift; my $from = shift; @@ -371,7 +371,7 @@ static int handle_alias(struct strvec *args) alias_command = args->v[0]; alias_string = alias_lookup(alias_command); if (alias_string) { - if (args->nr > 1 && !strcmp(args->v[1], "-h")) + if (args->nr == 2 && !strcmp(args->v[1], "-h")) fprintf_ln(stderr, _("'%s' is aliased to '%s'"), alias_command, alias_string); if (alias_string[0] == '!') { @@ -445,7 +445,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv, struct const char *prefix; int run_setup = (p->option & (RUN_SETUP | RUN_SETUP_GENTLY)); - help = argc == 2 && !strcmp(argv[1], "-h"); + help = argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help-all")); if (help && (run_setup & RUN_SETUP)) /* demote to GENTLY to allow 'git cmd -h' outside repo */ run_setup = RUN_SETUP_GENTLY; @@ -462,12 +462,12 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv, struct precompose_argv_prefix(argc, argv, NULL); if (use_pager == -1 && run_setup && !(p->option & DELAY_PAGER_CONFIG)) - use_pager = check_pager_config(the_repository, p->cmd); + use_pager = check_pager_config(repo, p->cmd); if (use_pager == -1 && p->option & USE_PAGER) use_pager = 1; if (run_setup && startup_info->have_repository) /* get_git_dir() may set up repo, avoid that */ - trace_repo_setup(the_repository); + trace_repo_setup(repo); commit_pager_choice(); if (!help && p->option & NEED_WORK_TREE) @@ -565,6 +565,7 @@ static struct cmd_struct commands[] = { { "init", cmd_init_db }, { "init-db", cmd_init_db }, { "interpret-trailers", cmd_interpret_trailers, RUN_SETUP_GENTLY }, + { "last-modified", cmd_last_modified, RUN_SETUP }, { "log", cmd_log, RUN_SETUP }, { "ls-files", cmd_ls_files, RUN_SETUP }, { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY }, @@ -611,6 +612,7 @@ static struct cmd_struct commands[] = { { "repack", cmd_repack, RUN_SETUP }, { "replace", cmd_replace, RUN_SETUP }, { "replay", cmd_replay, RUN_SETUP }, + { "repo", cmd_repo, RUN_SETUP }, { "rerere", cmd_rerere, RUN_SETUP }, { "reset", cmd_reset, RUN_SETUP }, { "restore", cmd_restore, RUN_SETUP | NEED_WORK_TREE }, @@ -646,7 +648,9 @@ static struct cmd_struct commands[] = { { "verify-pack", cmd_verify_pack }, { "verify-tag", cmd_verify_tag, RUN_SETUP }, { "version", cmd_version }, +#ifndef WITH_BREAKING_CHANGES { "whatchanged", cmd_whatchanged, RUN_SETUP }, +#endif { "worktree", cmd_worktree, RUN_SETUP }, { "write-tree", cmd_write_tree, RUN_SETUP }, }; diff --git a/gitk-git/README.md b/gitk-git/README.md new file mode 100644 index 0000000000..2e307463c6 --- /dev/null +++ b/gitk-git/README.md @@ -0,0 +1,93 @@ +Gitk - The Git Repository Browser +================================= + +Gitk is a graphical Git repository browser. It displays the commit +history of a Git repository as a graph, showing the relationships +between commits, branches, and tags. + +Usage +===== + +To view the history of the current repository: +```bash +gitk +``` + +To view the history of specific files or directories: +```bash +gitk path/to/file +gitk path/to/directory +``` + +To view a specific branch or range of commits: +```bash +gitk branch-name +gitk v1.0..v2.0 +``` + +For more usage examples and options, see the [gitk manual](https://git-scm.com/docs/gitk). + +Building +======== + +Gitk is a Tcl/Tk application. It requires Tcl/Tk to be installed on +your system. + +Running directly +---------------- + +Gitk can be run from the source directory without installation: + +```bash +./gitk +``` + +This allows for quick testing of changes. + +Installation +------------ + +To install system-wide, you can use either `make` or `meson`: + +```bash +# Install to default location ($HOME/bin) +make install + +# Install to system-wide location +sudo make install prefix=/usr/local + +# Install to custom location +make install prefix=/opt/gitk + +# Using Meson +meson setup builddir +meson compile -C builddir +meson install -C builddir +``` + +Both build systems will handle setting the correct Tcl/Tk interpreter +path and installing translation files. + +Contributing +============ + +Contributions are welcome! The preferred method for submitting patches +is via email to the Git mailing list, as this allows for more thorough +review and broader community feedback. However, GitHub pull requests +are also accepted. + +All commits must be signed off (use `git commit --signoff`) and should +have commit messages prefixed with `gitk:`. + +Email Patches +------------- + +Send patches to git@vger.kernel.org and CC j6t@kdbg.org. See the Git +project's [patch submission guidelines](https://git-scm.com/docs/SubmittingPatches) +for detailed instructions on creating and sending patches. + +License +======= + +Gitk is distributed under the GNU General Public License, either +version 2, or (at your option) any later version. diff --git a/gitk-git/gitk b/gitk-git/gitk index 5be8b2aeb0..6e4d71d585 100755 --- a/gitk-git/gitk +++ b/gitk-git/gitk @@ -7,7 +7,51 @@ exec wish "$0" -- "$@" # and distributed under the terms of the GNU General Public Licence, # either version 2, or (at your option) any later version. -package require Tk +if {[catch {package require Tcl 8.6-} err]} { + catch {wm withdraw .} + tk_messageBox \ + -icon error \ + -type ok \ + -title "gitk: fatal error" \ + -message $err + exit 1 +} + +set MIN_GIT_VERSION 2.20 +regexp {^git version ([\d.]*\d)} [exec git version] _ git_version +if {[package vcompare $git_version $MIN_GIT_VERSION] < 0} { + set message "The git executable found is too old. +The minimum required version is $MIN_GIT_VERSION.0. +The version of git found is $git_version." + + catch {wm withdraw .} + tk_messageBox \ + -icon error \ + -type ok \ + -title "gitk: fatal error" \ + -message $message + exit 1 +} + +###################################################################### +## Enable Tcl8 profile in Tcl9, allowing consumption of data that has +## bytes not conforming to the assumed encoding profile. + +if {[package vcompare $::tcl_version 9.0] >= 0} { + rename open _strict_open + proc open args { + set f [_strict_open {*}$args] + chan configure $f -profile tcl8 + return $f + } + proc convertfrom args { + return [encoding convertfrom -profile tcl8 {*}$args] + } +} else { + proc convertfrom args { + return [encoding convertfrom {*}$args] + } +} ###################################################################### ## @@ -345,7 +389,7 @@ proc unmerged_files {files} { proc parseviewargs {n arglist} { global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs env global vinlinediff - global worddiff git_version + global worddiff set vdatemode($n) 0 set vmergeonly($n) 0 @@ -396,14 +440,10 @@ proc parseviewargs {n arglist} { "--color-words*" - "--word-diff=color" { # These trigger a word diff in the console interface, # so help the user by enabling our own support - if {[package vcompare $git_version "1.7.2"] >= 0} { - set worddiff [mc "Color words"] - } + set worddiff [mc "Color words"] } "--word-diff*" { - if {[package vcompare $git_version "1.7.2"] >= 0} { - set worddiff [mc "Markup words"] - } + set worddiff [mc "Markup words"] } "--stat=*" - "--numstat" - "--shortstat" - "--summary" - "--check" - "--exit-code" - "--quiet" - "--topo-order" - @@ -479,6 +519,7 @@ proc parseviewargs {n arglist} { proc parseviewrevs {view revs} { global vposids vnegids + global hashlength if {$revs eq {}} { set revs HEAD @@ -492,7 +533,7 @@ proc parseviewrevs {view revs} { set badrev {} for {set l 0} {$l < [llength $errlines]} {incr l} { set line [lindex $errlines $l] - if {!([string length $line] == 40 && [string is xdigit $line])} { + if {!([string length $line] == $hashlength && [string is xdigit $line])} { if {[string match "fatal:*" $line]} { if {[string match "fatal: ambiguous argument*" $line] && $badrev ne {}} { @@ -551,7 +592,6 @@ proc start_rev_list {view} { global viewactive viewinstances vmergeonly global mainheadid viewmainheadid viewmainheadid_orig global vcanopt vflags vrevs vorigargs - global show_notes set startmsecs [clock clicks -milliseconds] set commitidx($view) 0 @@ -601,7 +641,7 @@ proc start_rev_list {view} { } if {[catch { - set fd [safe_open_command_redirect [concat git log --no-color -z --pretty=raw $show_notes \ + set fd [safe_open_command_redirect [concat git log --no-color -z --pretty=raw --show-notes \ --parents --boundary $args --stdin] \ [list "<<[join [concat $revs "--" $files] "\n"]"]] } err]} { @@ -697,7 +737,7 @@ proc updatecommits {} { global mainheadid viewmainheadid viewmainheadid_orig pending_select global hasworktree global varcid vposids vnegids vflags vrevs - global show_notes + global hashlength set hasworktree [hasworktree] rereadrefs @@ -731,7 +771,7 @@ proc updatecommits {} { # take out positive refs that we asked for before or # that we have already seen foreach rev $revs { - if {[string length $rev] == 40} { + if {[string length $rev] == $hashlength} { if {[lsearch -exact $oldpos $rev] < 0 && ![info exists varcid($view,$rev)]} { lappend newrevs $rev @@ -754,7 +794,7 @@ proc updatecommits {} { set args $vorigargs($view) } if {[catch { - set fd [safe_open_command_redirect [concat git log --no-color -z --pretty=raw $show_notes \ + set fd [safe_open_command_redirect [concat git log --no-color -z --pretty=raw --show-notes \ --parents --boundary $args --stdin] \ [list "<<[join [concat $revs "--" $vfilelimit($view)] "\n"]"]] } err]} { @@ -1614,6 +1654,7 @@ proc getcommitlines {fd inst view updating} { global parents children curview hlview global idpending ordertok global varccommits varcid varctok vtokmod vfilelimit vshortids + global hashlength set stuff [read $fd 500000] # git log doesn't terminate the last commit with a null... @@ -1696,7 +1737,7 @@ proc getcommitlines {fd inst view updating} { } set ok 1 foreach id $ids { - if {[string length $id] != 40} { + if {[string length $id] != $hashlength} { set ok 0 break } @@ -1942,8 +1983,8 @@ proc getcommit {id} { return 1 } -# Expand an abbreviated commit ID to a list of full 40-char IDs that match -# and are present in the current view. +# Expand an abbreviated commit ID to a list of full 40-char (or 64-char +# for SHA256 repo) IDs that match and are present in the current view. # This is fairly slow... proc longid {prefix} { global varcid curview vshortids @@ -1971,13 +2012,14 @@ proc longid {prefix} { } proc readrefs {} { - global tagids idtags headids idheads tagobjid + global tagids idtags headids idheads tagobjid upstreamofref global otherrefids idotherrefs mainhead mainheadid global selecthead selectheadid global hideremotes global tclencoding + global hashlength - foreach v {tagids idtags headids idheads otherrefids idotherrefs} { + foreach v {tagids idtags headids idheads otherrefids idotherrefs upstreamofref} { unset -nocomplain $v } set refd [safe_open_command [list git show-ref -d]] @@ -1985,9 +2027,9 @@ proc readrefs {} { fconfigure $refd -encoding $tclencoding } while {[gets $refd line] >= 0} { - if {[string index $line 40] ne " "} continue - set id [string range $line 0 39] - set ref [string range $line 41 end] + if {[string index $line $hashlength] ne " "} continue + set id [string range $line 0 [expr {$hashlength - 1}]] + set ref [string range $line [expr {$hashlength + 1}] end] if {![string match "refs/*" $ref]} continue set name [string range $ref 5 end] if {[string match "remotes/*" $name]} { @@ -2011,8 +2053,10 @@ proc readrefs {} { set tagids($name) $id lappend idtags($id) $name } else { - set otherrefids($name) $id - lappend idotherrefs($id) $name + if [is_other_ref_visible $name] { + set otherrefids($name) $id + lappend idotherrefs($id) $name + } } } catch {close $refd} @@ -2031,6 +2075,17 @@ proc readrefs {} { set selectheadid [safe_exec [list git rev-parse --verify $selecthead]] } } + #load the local_branch->upstream mapping + # the result of the for-each-ref command produces: local_branch NUL upstream + set refd [safe_open_command [list git for-each-ref {--format=%(refname:short)%00%(upstream)} refs/heads/]] + while {[gets $refd local_tracking] >= 0} { + set line [split $local_tracking \0] + if {[lindex $line 1] ne {}} { + set upstream_ref [string map {"refs/" ""} [lindex $line 1]] + set upstreamofref([lindex $line 0]) $upstream_ref + } + } + catch {close $refd} } # skip over fake commits @@ -2071,23 +2126,12 @@ proc removehead {id name} { } proc ttk_toplevel {w args} { - global use_ttk eval [linsert $args 0 ::toplevel $w] - if {$use_ttk} { - place [ttk::frame $w._toplevel_background] -x 0 -y 0 -relwidth 1 -relheight 1 - } + place [ttk::frame $w._toplevel_background] -x 0 -y 0 -relwidth 1 -relheight 1 return $w } proc make_transient {window origin} { - global have_tk85 - - # In MacOS Tk 8.4 transient appears to work by setting - # overrideredirect, which is utterly useless, since the - # windows get no border, and are not even kept above - # the parent. - if {!$have_tk85 && [tk windowingsystem] eq {aqua}} return - wm transient $window $origin # Windows fails to place transient windows normally, so @@ -2098,12 +2142,10 @@ proc make_transient {window origin} { } proc show_error {w top msg} { - global NS - if {![info exists NS]} {set NS ""} if {[wm state $top] eq "withdrawn"} { wm deiconify $top } message $w.m -text $msg -justify center -aspect 400 pack $w.m -side top -fill x -padx 20 -pady 20 - ${NS}::button $w.ok -default active -text [mc OK] -command "destroy $top" + ttk::button $w.ok -default active -text [mc OK] -command "destroy $top" pack $w.ok -side bottom -fill x bind $top <Visibility> "grab $top; focus $top" bind $top <Key-Return> "destroy $top" @@ -2125,16 +2167,16 @@ proc error_popup {msg {owner .}} { } proc confirm_popup {msg {owner .}} { - global confirm_ok NS + global confirm_ok set confirm_ok 0 set w .confirm ttk_toplevel $w make_transient $w $owner message $w.m -text $msg -justify center -aspect 400 pack $w.m -side top -fill x -padx 20 -pady 20 - ${NS}::button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w" + ttk::button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w" pack $w.ok -side left -fill x - ${NS}::button $w.cancel -text [mc Cancel] -command "destroy $w" + ttk::button $w.cancel -text [mc Cancel] -command "destroy $w" pack $w.cancel -side right -fill x bind $w <Visibility> "grab $w; focus $w" bind $w <Key-Return> "set confirm_ok 1; destroy $w" @@ -2150,8 +2192,6 @@ proc haveselectionclipboard {} { } proc setoptions {} { - global use_ttk - if {[tk windowingsystem] ne "win32"} { option add *Panedwindow.showHandle 1 startupFile option add *Panedwindow.sashRelief raised startupFile @@ -2244,23 +2284,86 @@ proc cleardropsel {w} { $w selection clear } proc makedroplist {w varname args} { - global use_ttk - if {$use_ttk} { - set width 0 - foreach label $args { - set cx [string length $label] - if {$cx > $width} {set width $cx} - } - set gm [ttk::combobox $w -width $width -state readonly\ - -textvariable $varname -values $args \ - -exportselection false] - bind $gm <<ComboboxSelected>> [list $gm selection clear] - } else { - set gm [eval [linsert $args 0 tk_optionMenu $w $varname]] - } + set width 0 + foreach label $args { + set cx [string length $label] + if {$cx > $width} {set width $cx} + } + set gm [ttk::combobox $w -width $width -state readonly\ + -textvariable $varname -values $args \ + -exportselection false] + bind $gm <<ComboboxSelected>> [list $gm selection clear] return $gm } +proc scrollval {D {koff 0}} { + global kscroll scroll_D0 + return [expr int(-($D / $scroll_D0) * max(1, $kscroll-$koff))] +} + +proc precisescrollval {D {koff 0}} { + global kscroll + return [expr (-($D / 10.0) * max(1, $kscroll-$koff))] +} + +proc bind_mousewheel {} { + global canv cflist ctext + bindall <MouseWheel> {allcanvs yview scroll [scrollval %D] units} + bindall <Shift-MouseWheel> break + bind $ctext <MouseWheel> {$ctext yview scroll [scrollval %D 2] units} + bind $ctext <Shift-MouseWheel> {$ctext xview scroll [scrollval %D 2] units} + bind $cflist <MouseWheel> {$cflist yview scroll [scrollval %D 2] units} + bind $cflist <Shift-MouseWheel> break + bind $canv <Shift-MouseWheel> {$canv xview scroll [scrollval %D] units} + + if {[package vcompare $::tcl_version 8.7] >= 0} { + bindall <Alt-MouseWheel> {allcanvs yview scroll [scrollval 5*%D] units} + bindall <Alt-Shift-MouseWheel> break + bind $ctext <Alt-MouseWheel> {$ctext yview scroll [scrollval 5*%D 2] units} + bind $ctext <Alt-Shift-MouseWheel> {$ctext xview scroll [scrollval 5*%D 2] units} + bind $cflist <Alt-MouseWheel> {$cflist yview scroll [scrollval 5*%D 2] units} + bind $cflist <Alt-Shift-MouseWheel> break + bind $canv <Alt-Shift-MouseWheel> {$canv xview scroll [scrollval 5*%D] units} + + bindall <TouchpadScroll> { + lassign [tk::PreciseScrollDeltas %D] deltaX deltaY + allcanvs yview scroll [precisescrollval $deltaY] units + } + bind $ctext <TouchpadScroll> { + lassign [tk::PreciseScrollDeltas %D] deltaX deltaY + $ctext yview scroll [precisescrollval $deltaY 2] units + $ctext xview scroll [precisescrollval $deltaX 2] units + } + bind $cflist <TouchpadScroll> { + lassign [tk::PreciseScrollDeltas %D] deltaX deltaY + $cflist yview scroll [precisescrollval $deltaY 2] units + } + bind $canv <TouchpadScroll> { + lassign [tk::PreciseScrollDeltas %D] deltaX deltaY + $canv xview scroll [precisescrollval $deltaX] units + allcanvs yview scroll [precisescrollval $deltaY] units + } + } +} + +proc bind_mousewheel_buttons {} { + global canv cflist ctext + bindall <ButtonRelease-4> {allcanvs yview scroll [scrollval 1] units} + bindall <ButtonRelease-5> {allcanvs yview scroll [scrollval -1] units} + bindall <Shift-ButtonRelease-4> break + bindall <Shift-ButtonRelease-5> break + bind $ctext <ButtonRelease-4> {$ctext yview scroll [scrollval 1 2] units} + bind $ctext <ButtonRelease-5> {$ctext yview scroll [scrollval -1 2] units} + bind $ctext <Shift-ButtonRelease-4> {$ctext xview scroll [scrollval 1 2] units} + bind $ctext <Shift-ButtonRelease-5> {$ctext xview scroll [scrollval -1 2] units} + bind $cflist <ButtonRelease-4> {$cflist yview scroll [scrollval 1 2] units} + bind $cflist <ButtonRelease-5> {$cflist yview scroll [scrollval -1 2] units} + bind $cflist <Shift-ButtonRelease-4> break + bind $cflist <Shift-ButtonRelease-5> break + bind $canv <Shift-ButtonRelease-4> {$canv xview scroll [scrollval 1] units} + bind $canv <Shift-ButtonRelease-5> {$canv xview scroll [scrollval -1] units} +} + proc makewindow {} { global canv canv2 canv3 linespc charspc ctext cflist cscroll global tabstop @@ -2279,9 +2382,8 @@ proc makewindow {} { global headctxmenu progresscanv progressitem progresscoords statusw global fprogitem fprogcoord lastprogupdate progupdatepending global rprogitem rprogcoord rownumsel numcommits - global have_tk85 have_tk86 use_ttk NS - global git_version global worddiff + global hashlength scroll_D0 # The "mc" arguments here are purely so that xgettext # sees the following string as needing to be translated @@ -2333,13 +2435,11 @@ proc makewindow {} { makemenu .bar $bar . configure -menu .bar - if {$use_ttk} { - # cover the non-themed toplevel with a themed frame. - place [ttk::frame ._main_background] -x 0 -y 0 -relwidth 1 -relheight 1 - } + # cover the non-themed toplevel with a themed frame. + place [ttk::frame ._main_background] -x 0 -y 0 -relwidth 1 -relheight 1 # the gui has upper and lower half, parts of a paned window. - ${NS}::panedwindow .ctop -orient vertical + ttk::panedwindow .ctop -orient vertical # possibly use assumed geometry if {![info exists geometry(pwsash0)]} { @@ -2352,12 +2452,9 @@ proc makewindow {} { } # the upper half will have a paned window, a scroll bar to the right, and some stuff below - ${NS}::frame .tf -height $geometry(topheight) -width $geometry(topwidth) - ${NS}::frame .tf.histframe - ${NS}::panedwindow .tf.histframe.pwclist -orient horizontal - if {!$use_ttk} { - .tf.histframe.pwclist configure -sashpad 0 -handlesize 4 - } + ttk::frame .tf -height $geometry(topheight) -width $geometry(topwidth) + ttk::frame .tf.histframe + ttk::panedwindow .tf.histframe.pwclist -orient horizontal # create three canvases set cscroll .tf.histframe.csb @@ -2365,6 +2462,7 @@ proc makewindow {} { canvas $canv \ -selectbackground $selectbgcolor \ -background $bgcolor -bd 0 \ + -xscrollincr $linespc \ -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll" .tf.histframe.pwclist add $canv set canv2 .tf.histframe.pwclist.canv2 @@ -2377,28 +2475,22 @@ proc makewindow {} { -selectbackground $selectbgcolor \ -background $bgcolor -bd 0 -yscrollincr $linespc .tf.histframe.pwclist add $canv3 - if {$use_ttk} { - bind .tf.histframe.pwclist <Map> { - bind %W <Map> {} - .tf.histframe.pwclist sashpos 1 [lindex $::geometry(pwsash1) 0] - .tf.histframe.pwclist sashpos 0 [lindex $::geometry(pwsash0) 0] - } - } else { - eval .tf.histframe.pwclist sash place 0 $geometry(pwsash0) - eval .tf.histframe.pwclist sash place 1 $geometry(pwsash1) + bind .tf.histframe.pwclist <Map> { + bind %W <Map> {} + .tf.histframe.pwclist sashpos 1 [lindex $::geometry(pwsash1) 0] + .tf.histframe.pwclist sashpos 0 [lindex $::geometry(pwsash0) 0] } # a scroll bar to rule them - ${NS}::scrollbar $cscroll -command {allcanvs yview} - if {!$use_ttk} {$cscroll configure -highlightthickness 0} + ttk::scrollbar $cscroll -command {allcanvs yview} pack $cscroll -side right -fill y bind .tf.histframe.pwclist <Configure> {resizeclistpanes %W %w} lappend bglist $canv $canv2 $canv3 pack .tf.histframe.pwclist -fill both -expand 1 -side left # we have two button bars at bottom of top frame. Bar 1 - ${NS}::frame .tf.bar - ${NS}::frame .tf.lbar -height 15 + ttk::frame .tf.bar + ttk::frame .tf.lbar -height 15 set sha1entry .tf.bar.sha1 set entries $sha1entry @@ -2407,7 +2499,7 @@ proc makewindow {} { -command gotocommit -width 8 $sha1but conf -disabledforeground [$sha1but cget -foreground] pack .tf.bar.sha1label -side left - ${NS}::entry $sha1entry -width 40 -font textfont -textvariable sha1string + ttk::entry $sha1entry -width $hashlength -font textfont -textvariable sha1string trace add variable sha1string write sha1change pack $sha1entry -side left -pady 2 @@ -2432,50 +2524,30 @@ proc makewindow {} { image create bitmap bm-right -data $bm_right_data -foreground $uifgcolor image create bitmap bm-right-gray -data $bm_right_data -foreground $uifgdisabledcolor - ${NS}::button .tf.bar.leftbut -command goback -state disabled -width 26 - if {$use_ttk} { - .tf.bar.leftbut configure -image [list bm-left disabled bm-left-gray] - } else { - .tf.bar.leftbut configure -image bm-left - } + ttk::button .tf.bar.leftbut -command goback -state disabled -width 26 + .tf.bar.leftbut configure -image [list bm-left disabled bm-left-gray] pack .tf.bar.leftbut -side left -fill y - ${NS}::button .tf.bar.rightbut -command goforw -state disabled -width 26 - if {$use_ttk} { - .tf.bar.rightbut configure -image [list bm-right disabled bm-right-gray] - } else { - .tf.bar.rightbut configure -image bm-right - } + ttk::button .tf.bar.rightbut -command goforw -state disabled -width 26 + .tf.bar.rightbut configure -image [list bm-right disabled bm-right-gray] pack .tf.bar.rightbut -side left -fill y - ${NS}::label .tf.bar.rowlabel -text [mc "Row"] + ttk::label .tf.bar.rowlabel -text [mc "Row"] set rownumsel {} - ${NS}::label .tf.bar.rownum -width 7 -textvariable rownumsel \ + ttk::label .tf.bar.rownum -width 7 -textvariable rownumsel \ -relief sunken -anchor e - ${NS}::label .tf.bar.rowlabel2 -text "/" - ${NS}::label .tf.bar.numcommits -width 7 -textvariable numcommits \ + ttk::label .tf.bar.rowlabel2 -text "/" + ttk::label .tf.bar.numcommits -width 7 -textvariable numcommits \ -relief sunken -anchor e pack .tf.bar.rowlabel .tf.bar.rownum .tf.bar.rowlabel2 .tf.bar.numcommits \ -side left - if {!$use_ttk} { - foreach w {rownum numcommits} {.tf.bar.$w configure -font textfont} - } global selectedline trace add variable selectedline write selectedline_change # Status label and progress bar set statusw .tf.bar.status - ${NS}::label $statusw -width 15 -relief sunken + ttk::label $statusw -width 15 -relief sunken pack $statusw -side left -padx 5 - if {$use_ttk} { - set progresscanv [ttk::progressbar .tf.bar.progress] - } else { - set h [expr {[font metrics uifont -linespace] + 2}] - set progresscanv .tf.bar.progress - canvas $progresscanv -relief sunken -height $h -borderwidth 2 - set progressitem [$progresscanv create rect -1 0 0 $h -fill "#00ff00"] - set fprogitem [$progresscanv create rect -1 0 0 $h -fill yellow] - set rprogitem [$progresscanv create rect -1 0 0 $h -fill red] - } + set progresscanv [ttk::progressbar .tf.bar.progress] pack $progresscanv -side right -expand 1 -fill x -padx {0 2} set progresscoords {0 0} set fprogcoord 0 @@ -2485,7 +2557,7 @@ proc makewindow {} { set progupdatepending 0 # build up the bottom bar of upper window - ${NS}::label .tf.lbar.flabel -text "[mc "Find"] " + ttk::label .tf.lbar.flabel -text "[mc "Find"] " set bm_down_data { #define down_width 16 @@ -2497,7 +2569,7 @@ proc makewindow {} { 0xf0, 0x0f, 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01}; } image create bitmap bm-down -data $bm_down_data -foreground $uifgcolor - ${NS}::button .tf.lbar.fnext -width 26 -command {dofind 1 1} + ttk::button .tf.lbar.fnext -width 26 -command {dofind 1 1} .tf.lbar.fnext configure -image bm-down set bm_up_data { @@ -2510,10 +2582,10 @@ proc makewindow {} { 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01}; } image create bitmap bm-up -data $bm_up_data -foreground $uifgcolor - ${NS}::button .tf.lbar.fprev -width 26 -command {dofind -1 1} + ttk::button .tf.lbar.fprev -width 26 -command {dofind -1 1} .tf.lbar.fprev configure -image bm-up - ${NS}::label .tf.lbar.flab2 -text " [mc "commit"] " + ttk::label .tf.lbar.flab2 -text " [mc "commit"] " pack .tf.lbar.flabel .tf.lbar.fnext .tf.lbar.fprev .tf.lbar.flab2 \ -side left -fill y @@ -2529,7 +2601,7 @@ proc makewindow {} { set findstring {} set fstring .tf.lbar.findstring lappend entries $fstring - ${NS}::entry $fstring -width 30 -textvariable findstring + ttk::entry $fstring -width 30 -textvariable findstring trace add variable findstring write find_change set findtype [mc "Exact"] set findtypemenu [makedroplist .tf.lbar.findtype \ @@ -2548,45 +2620,41 @@ proc makewindow {} { pack .tf.bar -in .tf -side bottom -fill x pack .tf.histframe -fill both -side top -expand 1 .ctop add .tf - if {!$use_ttk} { - .ctop paneconfigure .tf -height $geometry(topheight) - .ctop paneconfigure .tf -width $geometry(topwidth) - } # now build up the bottom - ${NS}::panedwindow .pwbottom -orient horizontal + ttk::panedwindow .pwbottom -orient horizontal # lower left, a text box over search bar, scroll bar to the right # if we know window height, then that will set the lower text height, otherwise # we set lower text height which will drive window height if {[info exists geometry(main)]} { - ${NS}::frame .bleft -width $geometry(botwidth) + ttk::frame .bleft -width $geometry(botwidth) } else { - ${NS}::frame .bleft -width $geometry(botwidth) -height $geometry(botheight) + ttk::frame .bleft -width $geometry(botwidth) -height $geometry(botheight) } - ${NS}::frame .bleft.top - ${NS}::frame .bleft.mid - ${NS}::frame .bleft.bottom + ttk::frame .bleft.top + ttk::frame .bleft.mid + ttk::frame .bleft.bottom # gap between sub-widgets set wgap [font measure uifont "i"] - ${NS}::button .bleft.top.search -text [mc "Search"] -command dosearch + ttk::button .bleft.top.search -text [mc "Search"] -command dosearch pack .bleft.top.search -side left -padx 5 set sstring .bleft.top.sstring set searchstring "" - ${NS}::entry $sstring -width 20 -textvariable searchstring + ttk::entry $sstring -width 20 -textvariable searchstring lappend entries $sstring trace add variable searchstring write incrsearch pack $sstring -side left -expand 1 -fill x - ${NS}::radiobutton .bleft.mid.diff -text [mc "Diff"] \ + ttk::radiobutton .bleft.mid.diff -text [mc "Diff"] \ -command changediffdisp -variable diffelide -value {0 0} - ${NS}::radiobutton .bleft.mid.old -text [mc "Old version"] \ + ttk::radiobutton .bleft.mid.old -text [mc "Old version"] \ -command changediffdisp -variable diffelide -value {0 1} - ${NS}::radiobutton .bleft.mid.new -text [mc "New version"] \ + ttk::radiobutton .bleft.mid.new -text [mc "New version"] \ -command changediffdisp -variable diffelide -value {1 0} - ${NS}::label .bleft.mid.labeldiffcontext -text " [mc "Lines of context"]: " + ttk::label .bleft.mid.labeldiffcontext -text " [mc "Lines of context"]: " pack .bleft.mid.diff .bleft.mid.old .bleft.mid.new -side left -ipadx $wgap spinbox .bleft.mid.diffcontext -width 5 \ -from 0 -increment 1 -to 10000000 \ @@ -2596,28 +2664,24 @@ proc makewindow {} { trace add variable diffcontextstring write diffcontextchange lappend entries .bleft.mid.diffcontext pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext -side left -ipadx $wgap - ${NS}::checkbutton .bleft.mid.ignspace -text [mc "Ignore space change"] \ + ttk::checkbutton .bleft.mid.ignspace -text [mc "Ignore space change"] \ -command changeignorespace -variable ignorespace pack .bleft.mid.ignspace -side left -padx 5 set worddiff [mc "Line diff"] - if {[package vcompare $git_version "1.7.2"] >= 0} { - makedroplist .bleft.mid.worddiff worddiff [mc "Line diff"] \ - [mc "Markup words"] [mc "Color words"] - trace add variable worddiff write changeworddiff - pack .bleft.mid.worddiff -side left -padx 5 - } + makedroplist .bleft.mid.worddiff worddiff [mc "Line diff"] \ + [mc "Markup words"] [mc "Color words"] + trace add variable worddiff write changeworddiff + pack .bleft.mid.worddiff -side left -padx 5 set ctext .bleft.bottom.ctext text $ctext -background $bgcolor -foreground $fgcolor \ -state disabled -undo 0 -font textfont \ -yscrollcommand scrolltext -wrap $wrapdefault \ -xscrollcommand ".bleft.bottom.sbhorizontal set" - if {$have_tk85} { - $ctext conf -tabstyle wordprocessor - } - ${NS}::scrollbar .bleft.bottom.sb -command "$ctext yview" - ${NS}::scrollbar .bleft.bottom.sbhorizontal -command "$ctext xview" -orient h + $ctext conf -tabstyle wordprocessor + ttk::scrollbar .bleft.bottom.sb -command "$ctext yview" + ttk::scrollbar .bleft.bottom.sbhorizontal -command "$ctext xview" -orient h pack .bleft.top -side top -fill x pack .bleft.mid -side top -fill x grid $ctext .bleft.bottom.sb -sticky nsew @@ -2668,16 +2732,13 @@ proc makewindow {} { $ctext tag lower d0 .pwbottom add .bleft - if {!$use_ttk} { - .pwbottom paneconfigure .bleft -width $geometry(botwidth) - } # lower right - ${NS}::frame .bright - ${NS}::frame .bright.mode - ${NS}::radiobutton .bright.mode.patch -text [mc "Patch"] \ + ttk::frame .bright + ttk::frame .bright.mode + ttk::radiobutton .bright.mode.patch -text [mc "Patch"] \ -command reselectline -variable cmitmode -value "patch" - ${NS}::radiobutton .bright.mode.tree -text [mc "Tree"] \ + ttk::radiobutton .bright.mode.tree -text [mc "Tree"] \ -command reselectline -variable cmitmode -value "tree" grid .bright.mode.patch .bright.mode.tree -sticky ew pack .bright.mode -side top -fill x @@ -2693,7 +2754,7 @@ proc makewindow {} { -spacing1 1 -spacing3 1 lappend bglist $cflist lappend fglist $cflist - ${NS}::scrollbar .bright.sb -command "$cflist yview" + ttk::scrollbar .bright.sb -command "$cflist yview" pack .bright.sb -side right -fill y pack $cflist -side left -fill both -expand 1 $cflist tag configure highlight \ @@ -2728,44 +2789,31 @@ proc makewindow {} { set ::BM "2" } - if {$use_ttk} { - bind .ctop <Map> { - bind %W <Map> {} - %W sashpos 0 $::geometry(topheight) - } - bind .pwbottom <Map> { - bind %W <Map> {} - %W sashpos 0 $::geometry(botwidth) - } - bind .pwbottom <Configure> {resizecdetpanes %W %w} + bind .ctop <Map> { + bind %W <Map> {} + %W sashpos 0 $::geometry(topheight) + } + bind .pwbottom <Map> { + bind %W <Map> {} + %W sashpos 0 $::geometry(botwidth) } + bind .pwbottom <Configure> {resizecdetpanes %W %w} pack .ctop -fill both -expand 1 bindall <1> {selcanvline %W %x %y} - #bindall <B1-Motion> {selcanvline %W %x %y} - if {[tk windowingsystem] == "win32"} { - bind . <MouseWheel> { windows_mousewheel_redirector %W %X %Y %D } - bind $ctext <MouseWheel> { windows_mousewheel_redirector %W %X %Y %D ; break } + + #Mouse / touchpad scrolling + if {[tk windowingsystem] == "win32" || [package vcompare $::tcl_version 8.7] >= 0} { + set scroll_D0 120 + bind_mousewheel + } elseif {[tk windowingsystem] == "x11"} { + set scroll_D0 1 + bind_mousewheel_buttons + } elseif {[tk windowingsystem] == "aqua"} { + set scroll_D0 1 + bind_mousewheel } else { - bindall <ButtonRelease-4> "allcanvs yview scroll -5 units" - bindall <ButtonRelease-5> "allcanvs yview scroll 5 units" - bind $ctext <Button> { - if {"%b" eq 6} { - $ctext xview scroll -5 units - } elseif {"%b" eq 7} { - $ctext xview scroll 5 units - } - } - if {[tk windowingsystem] eq "aqua"} { - bindall <MouseWheel> { - set delta [expr {- (%D)}] - allcanvs yview scroll $delta units - } - bindall <Shift-MouseWheel> { - set delta [expr {- (%D)}] - $canv xview scroll $delta units - } - } + puts stderr [mc "Unknown windowing system, cannot bind mouse"] } bindall <$::BM> "canvscan mark %W %x %y" bindall <B$::BM-Motion> "canvscan dragto %W %x %y" @@ -2777,13 +2825,8 @@ proc makewindow {} { bind . <Key-Down> "selnextline 1" bind . <Shift-Key-Up> "dofind -1 0" bind . <Shift-Key-Down> "dofind 1 0" - if {$have_tk86} { - bindkey <<NextChar>> "goforw" - bindkey <<PrevChar>> "goback" - } else { - bindkey <Key-Right> "goforw" - bindkey <Key-Left> "goback" - } + bindkey <<NextChar>> "goforw" + bindkey <<PrevChar>> "goback" bind . <Key-Prior> "selnextpage -1" bind . <Key-Next> "selnextpage 1" bind . <$M1B-Home> "allcanvs yview moveto 0.0" @@ -2910,24 +2953,6 @@ proc makewindow {} { $diff_menu configure -tearoff 0 } -# Windows sends all mouse wheel events to the current focused window, not -# the one where the mouse hovers, so bind those events here and redirect -# to the correct window -proc windows_mousewheel_redirector {W X Y D} { - global canv canv2 canv3 - set w [winfo containing -displayof $W $X $Y] - if {$w ne ""} { - set u [expr {$D < 0 ? 5 : -5}] - if {$w == $canv || $w == $canv2 || $w == $canv3} { - allcanvs yview scroll $u units - } else { - catch { - $w yview scroll $u units - } - } - } -} - # Update row number label when selectedline changes proc selectedline_change {n1 n2 op} { global selectedline rownumsel @@ -2990,30 +3015,10 @@ proc click {w} { # Adjust the progress bar for a change in requested extent or canvas size proc adjustprogress {} { - global progresscanv progressitem progresscoords - global fprogitem fprogcoord lastprogupdate progupdatepending - global rprogitem rprogcoord use_ttk + global progresscanv + global fprogcoord - if {$use_ttk} { - $progresscanv configure -value [expr {int($fprogcoord * 100)}] - return - } - - set w [expr {[winfo width $progresscanv] - 4}] - set x0 [expr {$w * [lindex $progresscoords 0]}] - set x1 [expr {$w * [lindex $progresscoords 1]}] - set h [winfo height $progresscanv] - $progresscanv coords $progressitem $x0 0 $x1 $h - $progresscanv coords $fprogitem 0 0 [expr {$w * $fprogcoord}] $h - $progresscanv coords $rprogitem 0 0 [expr {$w * $rprogcoord}] $h - set now [clock clicks -milliseconds] - if {$now >= $lastprogupdate + 100} { - set progupdatepending 0 - update - } elseif {!$progupdatepending} { - set progupdatepending 1 - after [expr {$lastprogupdate + 100 - $now}] doprogupdate - } + $progresscanv configure -value [expr {int($fprogcoord * 100)}] } proc doprogupdate {} { @@ -3072,7 +3077,6 @@ proc savestuff {w} { upvar #0 viewargscmd current_viewargscmd upvar #0 viewperm current_viewperm upvar #0 nextviewnum current_nextviewnum - upvar #0 use_ttk current_use_ttk if {$stuffsaved} return if {![winfo viewable .]} return @@ -3106,13 +3110,8 @@ proc savestuff {w} { puts $f "set geometry(state) [wm state .]" puts $f "set geometry(topwidth) [winfo width .tf]" puts $f "set geometry(topheight) [winfo height .tf]" - if {$current_use_ttk} { - puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sashpos 0] 1\"" - puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sashpos 1] 1\"" - } else { - puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sash coord 0]\"" - puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sash coord 1]\"" - } + puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sashpos 0] 1\"" + puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sashpos 1] 1\"" puts $f "set geometry(botwidth) [winfo width .bleft]" puts $f "set geometry(botheight) [winfo height .bleft]" @@ -3158,17 +3157,14 @@ proc savestuff {w} { } proc resizeclistpanes {win w} { - global oldwidth oldsash use_ttk + global oldwidth oldsash if {[info exists oldwidth($win)]} { if {[info exists oldsash($win)]} { set s0 [lindex $oldsash($win) 0] set s1 [lindex $oldsash($win) 1] - } elseif {$use_ttk} { + } else { set s0 [$win sashpos 0] set s1 [$win sashpos 1] - } else { - set s0 [$win sash coord 0] - set s1 [$win sash coord 1] } if {$w < 60} { set sash0 [expr {int($w/2 - 2)}] @@ -3190,29 +3186,20 @@ proc resizeclistpanes {win w} { } } } - if {$use_ttk} { - $win sashpos 0 $sash0 - $win sashpos 1 $sash1 - } else { - $win sash place 0 $sash0 [lindex $s0 1] - $win sash place 1 $sash1 [lindex $s1 1] - set sash0 [list $sash0 [lindex $s0 1]] - set sash1 [list $sash1 [lindex $s1 1]] - } + $win sashpos 0 $sash0 + $win sashpos 1 $sash1 set oldsash($win) [list $sash0 $sash1] } set oldwidth($win) $w } proc resizecdetpanes {win w} { - global oldwidth oldsash use_ttk + global oldwidth oldsash if {[info exists oldwidth($win)]} { if {[info exists oldsash($win)]} { set s0 $oldsash($win) - } elseif {$use_ttk} { - set s0 [$win sashpos 0] } else { - set s0 [$win sash coord 0] + set s0 [$win sashpos 0] } if {$w < 60} { set sash0 [expr {int($w*3/4 - 2)}] @@ -3226,12 +3213,7 @@ proc resizecdetpanes {win w} { set sash0 [expr {$w - 15}] } } - if {$use_ttk} { - $win sashpos 0 $sash0 - } else { - $win sash place 0 $sash0 [lindex $s0 1] - set sash0 [list $sash0 [lindex $s0 1]] - } + $win sashpos 0 $sash0 set oldsash($win) $sash0 } set oldwidth($win) $w @@ -3252,7 +3234,7 @@ proc bindall {event action} { } proc about {} { - global bgcolor NS + global bgcolor set w .about if {[winfo exists $w]} { raise $w @@ -3269,7 +3251,7 @@ Copyright \u00a9 2005-2016 Paul Mackerras Use and redistribute under the terms of the GNU General Public License"] \ -justify center -aspect 400 -border 2 -bg $bgcolor -relief groove pack $w.m -side top -fill x -padx 2 -pady 2 - ${NS}::button $w.ok -text [mc "Close"] -command "destroy $w" -default active + ttk::button $w.ok -text [mc "Close"] -command "destroy $w" -default active pack $w.ok -side bottom bind $w <Visibility> "focus $w.ok" bind $w <Key-Escape> "destroy $w" @@ -3278,7 +3260,7 @@ Use and redistribute under the terms of the GNU General Public License"] \ } proc keys {} { - global bgcolor NS + global bgcolor set w .keys if {[winfo exists $w]} { raise $w @@ -3336,7 +3318,7 @@ proc keys {} { " \ -justify left -bg $bgcolor -border 2 -relief groove pack $w.m -side top -fill both -padx 2 -pady 2 - ${NS}::button $w.ok -text [mc "Close"] -command "destroy $w" -default active + ttk::button $w.ok -text [mc "Close"] -command "destroy $w" -default active bind $w <Key-Escape> [list destroy $w] pack $w.ok -side bottom bind $w <Visibility> "focus $w.ok" @@ -4132,6 +4114,7 @@ proc stopblaming {} { proc read_line_source {fd inst} { global blamestuff curview commfd blameinst nullid nullid2 + global hashlength while {[gets $fd line] >= 0} { lappend blamestuff($inst) $line @@ -4152,7 +4135,7 @@ proc read_line_source {fd inst} { set line [split [lindex $blamestuff($inst) 0] " "] set id [lindex $line 0] set lnum [lindex $line 1] - if {[string length $id] == 40 && [string is xdigit $id] && + if {[string length $id] == $hashlength && [string is xdigit $id] && [string is digit -strict $lnum]} { # look for "filename" line foreach l $blamestuff($inst) { @@ -4480,16 +4463,16 @@ proc editview {} { proc vieweditor {top n title} { global newviewname newviewopts viewfiles bgcolor - global known_view_options NS + global known_view_options ttk_toplevel $top wm title $top [concat $title [mc "-- criteria for selecting revisions"]] make_transient $top . # View name - ${NS}::frame $top.nfr - ${NS}::label $top.nl -text [mc "View Name"] - ${NS}::entry $top.name -width 20 -textvariable newviewname($n) + ttk::frame $top.nfr + ttk::label $top.nl -text [mc "View Name"] + ttk::entry $top.name -width 20 -textvariable newviewname($n) pack $top.nfr -in $top -fill x -pady 5 -padx 3 pack $top.nl -in $top.nfr -side left -padx {0 5} pack $top.name -in $top.nfr -side left -padx {0 25} @@ -4508,13 +4491,13 @@ proc vieweditor {top n title} { if {$flags eq "+" || $flags eq "*"} { set cframe $top.fr$cnt incr cnt - ${NS}::frame $cframe + ttk::frame $cframe pack $cframe -in $top -fill x -pady 3 -padx 3 set cexpand [expr {$flags eq "*"}] } elseif {$flags eq ".." || $flags eq "*."} { set cframe $top.fr$cnt incr cnt - ${NS}::frame $cframe + ttk::frame $cframe pack $cframe -in $top -fill x -pady 3 -padx [list 15 3] set cexpand [expr {$flags eq "*."}] } else { @@ -4522,31 +4505,31 @@ proc vieweditor {top n title} { } if {$type eq "l"} { - ${NS}::label $cframe.l_$id -text $title + ttk::label $cframe.l_$id -text $title pack $cframe.l_$id -in $cframe -side left -pady [list 3 0] -anchor w } elseif {$type eq "b"} { - ${NS}::checkbutton $cframe.c_$id -text $title -variable newviewopts($n,$id) + ttk::checkbutton $cframe.c_$id -text $title -variable newviewopts($n,$id) pack $cframe.c_$id -in $cframe -side left \ -padx [list $lxpad 0] -expand $cexpand -anchor w } elseif {[regexp {^r(\d+)$} $type type sz]} { regexp {^(.*_)} $id uselessvar button_id - ${NS}::radiobutton $cframe.c_$id -text $title -variable newviewopts($n,$button_id) -value $sz + ttk::radiobutton $cframe.c_$id -text $title -variable newviewopts($n,$button_id) -value $sz pack $cframe.c_$id -in $cframe -side left \ -padx [list $lxpad 0] -expand $cexpand -anchor w } elseif {[regexp {^t(\d+)$} $type type sz]} { - ${NS}::label $cframe.l_$id -text $title - ${NS}::entry $cframe.e_$id -width $sz -background $bgcolor \ + ttk::label $cframe.l_$id -text $title + ttk::entry $cframe.e_$id -width $sz -background $bgcolor \ -textvariable newviewopts($n,$id) pack $cframe.l_$id -in $cframe -side left -padx [list $lxpad 0] pack $cframe.e_$id -in $cframe -side left -expand 1 -fill x } elseif {[regexp {^t(\d+)=$} $type type sz]} { - ${NS}::label $cframe.l_$id -text $title - ${NS}::entry $cframe.e_$id -width $sz -background $bgcolor \ + ttk::label $cframe.l_$id -text $title + ttk::entry $cframe.e_$id -width $sz -background $bgcolor \ -textvariable newviewopts($n,$id) pack $cframe.l_$id -in $cframe -side top -pady [list 3 0] -anchor w pack $cframe.e_$id -in $cframe -side top -fill x } elseif {$type eq "path"} { - ${NS}::label $top.l -text $title + ttk::label $top.l -text $title pack $top.l -in $top -side top -pady [list 3 0] -anchor w -padx 3 text $top.t -width 40 -height 5 -background $bgcolor if {[info exists viewfiles($n)]} { @@ -4561,10 +4544,10 @@ proc vieweditor {top n title} { } } - ${NS}::frame $top.buts - ${NS}::button $top.buts.ok -text [mc "OK"] -command [list newviewok $top $n] - ${NS}::button $top.buts.apply -text [mc "Apply (F5)"] -command [list newviewok $top $n 1] - ${NS}::button $top.buts.can -text [mc "Cancel"] -command [list destroy $top] + ttk::frame $top.buts + ttk::button $top.buts.ok -text [mc "OK"] -command [list newviewok $top $n] + ttk::button $top.buts.apply -text [mc "Apply (F5)"] -command [list newviewok $top $n 1] + ttk::button $top.buts.can -text [mc "Cancel"] -command [list destroy $top] bind $top <Control-Return> [list newviewok $top $n] bind $top <F5> [list newviewok $top $n 1] bind $top <Escape> [list destroy $top] @@ -5296,11 +5279,13 @@ proc askrelhighlight {row id} { # Graph layout functions proc shortids {ids} { + global hashlength + set res {} foreach id $ids { if {[llength $id] > 1} { lappend res [shortids $id] - } elseif {[regexp {^[0-9a-f]{40}$} $id]} { + } elseif {[regexp [string map "@@ $hashlength" {^[0-9a-f]{@@}$}] $id]} { lappend res [string range $id 0 7] } else { lappend res $id @@ -5475,13 +5460,14 @@ proc get_viewmainhead {view} { # git rev-list should give us just 1 line to use as viewmainheadid($view) proc getviewhead {fd inst view} { global viewmainheadid commfd curview viewinstances showlocalchanges + global hashlength set id {} if {[gets $fd line] < 0} { if {![eof $fd]} { return 1 } - } elseif {[string length $line] == 40 && [string is xdigit $line]} { + } elseif {[string length $line] == $hashlength && [string is xdigit $line]} { set id $line } set viewmainheadid($view) $id @@ -5523,15 +5509,11 @@ proc dohidelocalchanges {} { # spawn off a process to do git diff-index --cached HEAD proc dodiffindex {} { global lserial showlocalchanges vfilelimit curview - global hasworktree git_version + global hasworktree if {!$showlocalchanges || !$hasworktree} return incr lserial - if {[package vcompare $git_version "1.7.2"] >= 0} { - set cmd "git diff-index --cached --ignore-submodules=dirty HEAD" - } else { - set cmd "git diff-index --cached HEAD" - } + set cmd "git diff-index --cached --ignore-submodules=dirty HEAD" if {$vfilelimit($curview) ne {}} { set cmd [concat $cmd -- $vfilelimit($curview)] } @@ -6759,13 +6741,7 @@ proc bindline {t id} { } proc graph_pane_width {} { - global use_ttk - - if {$use_ttk} { - set g [.tf.histframe.pwclist sashpos 0] - } else { - set g [.tf.histframe.pwclist sash coord 0] - } + set g [.tf.histframe.pwclist sashpos 0] return [lindex $g 0] } @@ -7245,10 +7221,11 @@ proc commit_descriptor {p} { # Also look for URLs of the form "http[s]://..." and make them web links. proc appendwithlinks {text tags} { global ctext linknum curview + global hashlength set start [$ctext index "end - 1c"] $ctext insert end $text $tags - set links [regexp -indices -all -inline {(?:\m|-g)[0-9a-f]{6,40}\M} $text] + set links [regexp -indices -all -inline [string map "@@ $hashlength" {(?:\m|-g)[0-9a-f]{6,@@}\M}] $text] foreach l $links { set s [lindex $l 0] set e [lindex $l 1] @@ -7276,13 +7253,14 @@ proc appendwithlinks {text tags} { proc setlink {id lk} { global curview ctext pendinglinks global linkfgcolor + global hashlength if {[string range $id 0 1] eq "-g"} { set id [string range $id 2 end] } set known 0 - if {[string length $id] < 40} { + if {[string length $id] < $hashlength} { set matches [longid $id] if {[llength $matches] > 0} { if {[llength $matches] > 1} return @@ -7872,7 +7850,7 @@ proc gettree {id} { set treepending $id set treefilelist($id) {} set treeidlist($id) {} - fconfigure $gtf -blocking 0 -encoding binary + fconfigure $gtf -blocking 0 -translation binary filerun $gtf [list gettreeline $gtf $id] } } else { @@ -7899,7 +7877,7 @@ proc gettreeline {gtf id} { if {[string index $fname 0] eq "\""} { set fname [lindex $fname 0] } - set fname [encoding convertfrom utf-8 $fname] + set fname [convertfrom utf-8 $fname] lappend treefilelist($id) $fname } if {![eof $gtf]} { @@ -8079,7 +8057,7 @@ proc addtocflist {ids} { } proc diffcmd {ids flags} { - global log_showroot nullid nullid2 git_version + global log_showroot nullid nullid2 set i [lsearch -exact $ids $nullid] set j [lsearch -exact $ids $nullid2] @@ -8100,9 +8078,7 @@ proc diffcmd {ids flags} { } } } elseif {$j >= 0} { - if {[package vcompare $git_version "1.7.2"] >= 0} { - set flags "$flags --ignore-submodules=dirty" - } + set flags "$flags --ignore-submodules=dirty" set cmd [concat git diff-index --cached $flags] if {[llength $ids] > 1} { # comparing index with specific revision @@ -8135,7 +8111,7 @@ proc gettreediffs {ids} { set treepending $ids set treediff {} - fconfigure $gdtf -blocking 0 -encoding binary + fconfigure $gdtf -blocking 0 -translation binary filerun $gdtf [list gettreediffline $gdtf $ids] } @@ -8161,7 +8137,7 @@ proc gettreediffline {gdtf ids} { if {[string index $file 0] eq "\""} { set file [lindex $file 0] } - set file [encoding convertfrom utf-8 $file] + set file [convertfrom utf-8 $file] if {$file ne [lindex $treediff end]} { lappend treediff $file lappend sublist $file @@ -8231,17 +8207,8 @@ proc getblobdiffs {ids} { global ignorespace global worddiff global limitdiffs vfilelimit curview - global git_version - set textconv {} - if {[package vcompare $git_version "1.6.1"] >= 0} { - set textconv "--textconv" - } - set submodule {} - if {[package vcompare $git_version "1.6.6"] >= 0} { - set submodule "--submodule" - } - set cmd [diffcmd $ids "-p $textconv $submodule -C --cc --no-commit-id -U$diffcontext"] + set cmd [diffcmd $ids "-p --textconv --submodule -C --cc --no-commit-id -U$diffcontext"] if {$ignorespace} { append cmd " -w" } @@ -8255,7 +8222,7 @@ proc getblobdiffs {ids} { error_popup [mc "Error getting diffs: %s" $err] return } - fconfigure $bdf -blocking 0 -encoding binary -eofchar {} + fconfigure $bdf -blocking 0 -translation binary set blobdifffd($ids) $bdf initblobdiffvars filerun $bdf [list getblobdiffline $bdf $diffids] @@ -8306,7 +8273,7 @@ proc makediffhdr {fname ids} { global ctext curdiffstart treediffs diffencoding global ctext_file_names jump_to_here targetline diffline - set fname [encoding convertfrom utf-8 $fname] + set fname [convertfrom utf-8 $fname] set diffencoding [get_path_encoding $fname] set i [lsearch -exact $treediffs($ids) $fname] if {$i >= 0} { @@ -8368,7 +8335,7 @@ proc parseblobdiffline {ids line} { if {![string compare -length 5 "diff " $line]} { if {![regexp {^diff (--cc|--git) } $line m type]} { - set line [encoding convertfrom utf-8 $line] + set line [convertfrom utf-8 $line] $ctext insert end "$line\n" hunksep continue } @@ -8417,7 +8384,7 @@ proc parseblobdiffline {ids line} { makediffhdr $fname $ids } elseif {![string compare -length 16 "* Unmerged path " $line]} { - set fname [encoding convertfrom utf-8 [string range $line 16 end]] + set fname [convertfrom utf-8 [string range $line 16 end]] $ctext insert end "\n" set curdiffstart [$ctext index "end - 1c"] lappend ctext_file_names $fname @@ -8430,7 +8397,7 @@ proc parseblobdiffline {ids line} { } elseif {![string compare -length 2 "@@" $line]} { regexp {^@@+} $line ats - set line [encoding convertfrom $diffencoding $line] + set line [convertfrom $diffencoding $line] $ctext insert end "$line\n" hunksep if {[regexp { \+(\d+),\d+ @@} $line m nl]} { set diffline $nl @@ -8459,10 +8426,10 @@ proc parseblobdiffline {ids line} { $ctext insert end "$line\n" filesep } } elseif {$currdiffsubmod != "" && ![string compare -length 3 " >" $line]} { - set line [encoding convertfrom $diffencoding $line] + set line [convertfrom $diffencoding $line] $ctext insert end "$line\n" dresult } elseif {$currdiffsubmod != "" && ![string compare -length 3 " <" $line]} { - set line [encoding convertfrom $diffencoding $line] + set line [convertfrom $diffencoding $line] $ctext insert end "$line\n" d0 } elseif {$diffinhdr} { if {![string compare -length 12 "rename from " $line]} { @@ -8470,7 +8437,7 @@ proc parseblobdiffline {ids line} { if {[string index $fname 0] eq "\""} { set fname [lindex $fname 0] } - set fname [encoding convertfrom utf-8 $fname] + set fname [convertfrom utf-8 $fname] set i [lsearch -exact $treediffs($ids) $fname] if {$i >= 0} { setinlist difffilestart $i $curdiffstart @@ -8489,12 +8456,12 @@ proc parseblobdiffline {ids line} { set diffinhdr 0 return } - set line [encoding convertfrom utf-8 $line] + set line [convertfrom utf-8 $line] $ctext insert end "$line\n" filesep } else { set line [string map {\x1A ^Z} \ - [encoding convertfrom $diffencoding $line]] + [convertfrom $diffencoding $line]] # parse the prefix - one ' ', '-' or '+' for each parent set prefix [string range $line 0 [expr {$diffnparents - 1}]] set tag [expr {$diffnparents > 1? "m": "d"}] @@ -8646,19 +8613,17 @@ proc clear_ctext {{first 1.0}} { } proc settabs {{firstab {}}} { - global firsttabstop tabstop ctext have_tk85 + global firsttabstop tabstop ctext - if {$firstab ne {} && $have_tk85} { + if {$firstab ne {}} { set firsttabstop $firstab } set w [font measure textfont "0"] if {$firsttabstop != 0} { $ctext conf -tabs [list [expr {($firsttabstop + $tabstop) * $w}] \ [expr {($firsttabstop + 2 * $tabstop) * $w}]] - } elseif {$have_tk85 || $tabstop != 8} { - $ctext conf -tabs [expr {$tabstop * $w}] } else { - $ctext conf -tabs {} + $ctext conf -tabs [expr {$tabstop * $w}] } } @@ -8927,13 +8892,16 @@ proc incrfont {inc} { proc clearsha1 {} { global sha1entry sha1string - if {[string length $sha1string] == 40} { + global hashlength + + if {[string length $sha1string] == $hashlength} { $sha1entry delete 0 end } } proc sha1change {n1 n2 op} { global sha1string currentid sha1but + if {$sha1string == {} || ([info exists currentid] && $sha1string == $currentid)} { set state disabled @@ -8950,6 +8918,7 @@ proc sha1change {n1 n2 op} { proc gotocommit {} { global sha1string tagids headids curview varcid + global hashlength if {$sha1string == {} || ([info exists currentid] && $sha1string == $currentid)} return @@ -8959,7 +8928,7 @@ proc gotocommit {} { set id $headids($sha1string) } else { set id [string tolower $sha1string] - if {[regexp {^[0-9a-f]{4,39}$} $id]} { + if {[regexp {^[0-9a-f]{4,63}$} $id]} { set matches [longid $id] if {$matches ne {}} { if {[llength $matches] > 1} { @@ -9445,7 +9414,8 @@ proc doseldiff {oldid newid} { } proc mkpatch {} { - global rowmenuid currentid commitinfo patchtop patchnum NS + global rowmenuid currentid commitinfo patchtop patchnum + global hashlength if {![info exists currentid]} return set oldid $currentid @@ -9457,36 +9427,36 @@ proc mkpatch {} { catch {destroy $top} ttk_toplevel $top make_transient $top . - ${NS}::label $top.title -text [mc "Generate patch"] + ttk::label $top.title -text [mc "Generate patch"] grid $top.title - -pady 10 - ${NS}::label $top.from -text [mc "From:"] - ${NS}::entry $top.fromsha1 -width 40 + ttk::label $top.from -text [mc "From:"] + ttk::entry $top.fromsha1 -width $hashlength $top.fromsha1 insert 0 $oldid $top.fromsha1 conf -state readonly grid $top.from $top.fromsha1 -sticky w - ${NS}::entry $top.fromhead -width 60 + ttk::entry $top.fromhead -width 60 $top.fromhead insert 0 $oldhead $top.fromhead conf -state readonly grid x $top.fromhead -sticky w - ${NS}::label $top.to -text [mc "To:"] - ${NS}::entry $top.tosha1 -width 40 + ttk::label $top.to -text [mc "To:"] + ttk::entry $top.tosha1 -width $hashlength $top.tosha1 insert 0 $newid $top.tosha1 conf -state readonly grid $top.to $top.tosha1 -sticky w - ${NS}::entry $top.tohead -width 60 + ttk::entry $top.tohead -width 60 $top.tohead insert 0 $newhead $top.tohead conf -state readonly grid x $top.tohead -sticky w - ${NS}::button $top.rev -text [mc "Reverse"] -command mkpatchrev + ttk::button $top.rev -text [mc "Reverse"] -command mkpatchrev grid $top.rev x -pady 10 -padx 5 - ${NS}::label $top.flab -text [mc "Output file:"] - ${NS}::entry $top.fname -width 60 + ttk::label $top.flab -text [mc "Output file:"] + ttk::entry $top.fname -width 60 $top.fname insert 0 [file normalize "patch$patchnum.patch"] incr patchnum grid $top.flab $top.fname -sticky w - ${NS}::frame $top.buts - ${NS}::button $top.buts.gen -text [mc "Generate"] -command mkpatchgo - ${NS}::button $top.buts.can -text [mc "Cancel"] -command mkpatchcan + ttk::frame $top.buts + ttk::button $top.buts.gen -text [mc "Generate"] -command mkpatchgo + ttk::button $top.buts.can -text [mc "Cancel"] -command mkpatchcan bind $top <Key-Return> mkpatchgo bind $top <Key-Escape> mkpatchcan grid $top.buts.gen $top.buts.can @@ -9534,35 +9504,36 @@ proc mkpatchcan {} { } proc mktag {} { - global rowmenuid mktagtop commitinfo NS + global rowmenuid mktagtop commitinfo + global hashlength set top .maketag set mktagtop $top catch {destroy $top} ttk_toplevel $top make_transient $top . - ${NS}::label $top.title -text [mc "Create tag"] + ttk::label $top.title -text [mc "Create tag"] grid $top.title - -pady 10 - ${NS}::label $top.id -text [mc "ID:"] - ${NS}::entry $top.sha1 -width 40 + ttk::label $top.id -text [mc "ID:"] + ttk::entry $top.sha1 -width $hashlength $top.sha1 insert 0 $rowmenuid $top.sha1 conf -state readonly grid $top.id $top.sha1 -sticky w - ${NS}::entry $top.head -width 60 + ttk::entry $top.head -width 60 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0] $top.head conf -state readonly grid x $top.head -sticky w - ${NS}::label $top.tlab -text [mc "Tag name:"] - ${NS}::entry $top.tag -width 60 + ttk::label $top.tlab -text [mc "Tag name:"] + ttk::entry $top.tag -width 60 grid $top.tlab $top.tag -sticky w - ${NS}::label $top.op -text [mc "Tag message is optional"] + ttk::label $top.op -text [mc "Tag message is optional"] grid $top.op -columnspan 2 -sticky we - ${NS}::label $top.mlab -text [mc "Tag message:"] - ${NS}::entry $top.msg -width 60 + ttk::label $top.mlab -text [mc "Tag message:"] + ttk::entry $top.msg -width 60 grid $top.mlab $top.msg -sticky w - ${NS}::frame $top.buts - ${NS}::button $top.buts.gen -text [mc "Create"] -command mktaggo - ${NS}::button $top.buts.can -text [mc "Cancel"] -command mktagcan + ttk::frame $top.buts + ttk::button $top.buts.gen -text [mc "Create"] -command mktaggo + ttk::button $top.buts.can -text [mc "Cancel"] -command mktagcan bind $top <Key-Return> mktaggo bind $top <Key-Escape> mktagcan grid $top.buts.gen $top.buts.can @@ -9652,10 +9623,11 @@ proc mktaggo {} { proc copyreference {} { global rowmenuid autosellen + global hashlength set format "%h (\"%s\", %ad)" set cmd [list git show -s --pretty=format:$format --date=short] - if {$autosellen < 40} { + if {$autosellen < $hashlength} { lappend cmd --abbrev=$autosellen } set reference [safe_exec [concat $cmd $rowmenuid]] @@ -9665,34 +9637,35 @@ proc copyreference {} { } proc writecommit {} { - global rowmenuid wrcomtop commitinfo wrcomcmd NS + global rowmenuid wrcomtop commitinfo wrcomcmd + global hashlength set top .writecommit set wrcomtop $top catch {destroy $top} ttk_toplevel $top make_transient $top . - ${NS}::label $top.title -text [mc "Write commit to file"] + ttk::label $top.title -text [mc "Write commit to file"] grid $top.title - -pady 10 - ${NS}::label $top.id -text [mc "ID:"] - ${NS}::entry $top.sha1 -width 40 + ttk::label $top.id -text [mc "ID:"] + ttk::entry $top.sha1 -width $hashlength $top.sha1 insert 0 $rowmenuid $top.sha1 conf -state readonly grid $top.id $top.sha1 -sticky w - ${NS}::entry $top.head -width 60 + ttk::entry $top.head -width 60 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0] $top.head conf -state readonly grid x $top.head -sticky w - ${NS}::label $top.clab -text [mc "Command:"] - ${NS}::entry $top.cmd -width 60 -textvariable wrcomcmd + ttk::label $top.clab -text [mc "Command:"] + ttk::entry $top.cmd -width 60 -textvariable wrcomcmd grid $top.clab $top.cmd -sticky w -pady 10 - ${NS}::label $top.flab -text [mc "Output file:"] - ${NS}::entry $top.fname -width 60 + ttk::label $top.flab -text [mc "Output file:"] + ttk::entry $top.fname -width 60 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"] grid $top.flab $top.fname -sticky w - ${NS}::frame $top.buts - ${NS}::button $top.buts.gen -text [mc "Write"] -command wrcomgo - ${NS}::button $top.buts.can -text [mc "Cancel"] -command wrcomcan + ttk::frame $top.buts + ttk::button $top.buts.gen -text [mc "Write"] -command wrcomgo + ttk::button $top.buts.can -text [mc "Cancel"] -command wrcomcan bind $top <Key-Return> wrcomgo bind $top <Key-Escape> wrcomcan grid $top.buts.gen $top.buts.can @@ -9723,7 +9696,7 @@ proc wrcomcan {} { } proc mkbranch {} { - global NS rowmenuid + global rowmenuid set top .branchdialog @@ -9738,7 +9711,6 @@ proc mkbranch {} { } proc mvbranch {} { - global NS global headmenuid headmenuhead set top .branchdialog @@ -9754,31 +9726,32 @@ proc mvbranch {} { } proc branchdia {top valvar uivar} { - global NS commitinfo + global commitinfo + global hashlength upvar $valvar val $uivar ui catch {destroy $top} ttk_toplevel $top make_transient $top . - ${NS}::label $top.title -text $ui(title) + ttk::label $top.title -text $ui(title) grid $top.title - -pady 10 - ${NS}::label $top.id -text [mc "ID:"] - ${NS}::entry $top.sha1 -width 40 + ttk::label $top.id -text [mc "ID:"] + ttk::entry $top.sha1 -width $hashlength $top.sha1 insert 0 $val(id) $top.sha1 conf -state readonly grid $top.id $top.sha1 -sticky w - ${NS}::entry $top.head -width 60 + ttk::entry $top.head -width 60 $top.head insert 0 [lindex $commitinfo($val(id)) 0] $top.head conf -state readonly grid x $top.head -sticky ew grid columnconfigure $top 1 -weight 1 - ${NS}::label $top.nlab -text [mc "Name:"] - ${NS}::entry $top.name -width 40 + ttk::label $top.nlab -text [mc "Name:"] + ttk::entry $top.name -width $hashlength $top.name insert 0 $val(name) grid $top.nlab $top.name -sticky w - ${NS}::frame $top.buts - ${NS}::button $top.buts.go -text $ui(accept) -command $val(command) - ${NS}::button $top.buts.can -text [mc "Cancel"] -command "catch {destroy $top}" + ttk::frame $top.buts + ttk::button $top.buts.go -text $ui(accept) -command $val(command) + ttk::button $top.buts.can -text [mc "Cancel"] -command "catch {destroy $top}" bind $top <Key-Return> $val(command) bind $top <Key-Escape> "catch {destroy $top}" grid $top.buts.go $top.buts.can @@ -10025,31 +9998,31 @@ proc revert {} { } proc resethead {} { - global mainhead rowmenuid confirm_ok resettype NS + global mainhead rowmenuid confirm_ok resettype set confirm_ok 0 set w ".confirmreset" ttk_toplevel $w make_transient $w . wm title $w [mc "Confirm reset"] - ${NS}::label $w.m -text \ + ttk::label $w.m -text \ [mc "Reset branch %s to %s?" $mainhead [string range $rowmenuid 0 7]] pack $w.m -side top -fill x -padx 20 -pady 20 - ${NS}::labelframe $w.f -text [mc "Reset type:"] + ttk::labelframe $w.f -text [mc "Reset type:"] set resettype mixed - ${NS}::radiobutton $w.f.soft -value soft -variable resettype \ + ttk::radiobutton $w.f.soft -value soft -variable resettype \ -text [mc "Soft: Leave working tree and index untouched"] grid $w.f.soft -sticky w - ${NS}::radiobutton $w.f.mixed -value mixed -variable resettype \ + ttk::radiobutton $w.f.mixed -value mixed -variable resettype \ -text [mc "Mixed: Leave working tree untouched, reset index"] grid $w.f.mixed -sticky w - ${NS}::radiobutton $w.f.hard -value hard -variable resettype \ + ttk::radiobutton $w.f.hard -value hard -variable resettype \ -text [mc "Hard: Reset working tree and index\n(discard ALL local changes)"] grid $w.f.hard -sticky w pack $w.f -side top -fill x -padx 4 - ${NS}::button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w" + ttk::button $w.ok -text [mc OK] -command "set confirm_ok 1; destroy $w" pack $w.ok -side left -fill x -padx 20 -pady 20 - ${NS}::button $w.cancel -text [mc Cancel] -command "destroy $w" + ttk::button $w.cancel -text [mc Cancel] -command "destroy $w" bind $w <Key-Escape> [list destroy $w] pack $w.cancel -side right -fill x -padx 20 -pady 20 bind $w <Visibility> "grab $w; focus $w" @@ -10226,7 +10199,7 @@ proc rmbranch {} { # Display a list of tags and heads proc showrefs {} { - global showrefstop bgcolor fgcolor selectbgcolor NS + global showrefstop bgcolor fgcolor selectbgcolor global bglist fglist reflistfilter reflist maincursor set top .showrefs @@ -10249,19 +10222,22 @@ proc showrefs {} { lappend bglist $top.list lappend fglist $top.list } - ${NS}::scrollbar $top.ysb -command "$top.list yview" -orient vertical - ${NS}::scrollbar $top.xsb -command "$top.list xview" -orient horizontal + ttk::scrollbar $top.ysb -command "$top.list yview" -orient vertical + ttk::scrollbar $top.xsb -command "$top.list xview" -orient horizontal grid $top.list $top.ysb -sticky nsew grid $top.xsb x -sticky ew - ${NS}::frame $top.f - ${NS}::label $top.f.l -text "[mc "Filter"]: " - ${NS}::entry $top.f.e -width 20 -textvariable reflistfilter + ttk::frame $top.f + ttk::label $top.f.l -text "[mc "Filter"]: " + ttk::entry $top.f.e -width 20 -textvariable reflistfilter set reflistfilter "*" trace add variable reflistfilter write reflistfilter_change pack $top.f.e -side right -fill x -expand 1 pack $top.f.l -side left grid $top.f - -sticky ew -pady 2 - ${NS}::button $top.close -command [list destroy $top] -text [mc "Close"] + ttk::checkbutton $top.sort -text [mc "Sort refs by type"] \ + -variable sortrefsbytype -command {refill_reflist} + grid $top.sort - -sticky w -pady 2 + ttk::button $top.close -command [list destroy $top] -text [mc "Close"] bind $top <Key-Escape> [list destroy $top] grid $top.close - grid columnconfigure $top 0 -weight 1 @@ -10304,43 +10280,71 @@ proc reflistfilter_change {n1 n2 op} { } proc refill_reflist {} { - global reflist reflistfilter showrefstop headids tagids otherrefids - global curview + global reflist reflistfilter showrefstop headids tagids otherrefids sortrefsbytype + global curview upstreamofref if {![info exists showrefstop] || ![winfo exists $showrefstop]} return - set refs {} + set localrefs {} + set remoterefs {} + set trackedremoterefs {} + set tagrefs {} + set otherrefs {} + foreach n [array names headids] { - if {[string match $reflistfilter $n]} { + if {![string match "remotes/*" $n] && [string match $reflistfilter $n]} { if {[commitinview $headids($n) $curview]} { - if {[string match "remotes/*" $n]} { - lappend refs [list $n R] - } else { - lappend refs [list $n H] + lappend localrefs [list $n H] + if {[info exists upstreamofref($n)] && [commitinview $headids($upstreamofref($n)) $curview]} { + lappend trackedremoterefs [list $upstreamofref($n) R] } } else { interestedin $headids($n) {run refill_reflist} } } } + set trackedremoterefs [lsort -index 0 -unique $trackedremoterefs] + set localrefs [lsort -index 0 $localrefs] + + foreach n [array names headids] { + if {[string match "remotes/*" $n] && [string match $reflistfilter $n]} { + if {[commitinview $headids($n) $curview]} { + if {[lsearch -exact $trackedremoterefs [list $n R]] < 0} { + lappend remoterefs [list $n R] + } + } else { + interestedin $headids($n) {run refill_reflist} + } + } + } + set remoterefs [lsort -index 0 $remoterefs] + foreach n [array names tagids] { if {[string match $reflistfilter $n]} { if {[commitinview $tagids($n) $curview]} { - lappend refs [list $n T] + lappend tagrefs [list $n T] } else { interestedin $tagids($n) {run refill_reflist} } } } + set tagrefs [lsort -index 0 $tagrefs] + foreach n [array names otherrefids] { if {[string match $reflistfilter $n]} { if {[commitinview $otherrefids($n) $curview]} { - lappend refs [list $n o] + lappend otherrefs [list "$n" o] } else { interestedin $otherrefids($n) {run refill_reflist} } } } - set refs [lsort -index 0 $refs] + set otherrefs [lsort -index 0 $otherrefs] + + set refs [concat $localrefs $trackedremoterefs $remoterefs $tagrefs $otherrefs] + if {!$sortrefsbytype} { + set refs [lsort -index 0 $refs] + } + if {$refs eq $reflist} return # Update the contents of $showrefstop.list according to the @@ -11599,84 +11603,16 @@ proc doquit {} { } proc mkfontdisp {font top which} { - global fontattr fontpref $font NS use_ttk + global fontattr fontpref $font set fontpref($font) [set $font] - ${NS}::button $top.${font}but -text $which \ + ttk::button $top.${font}but -text $which \ -command [list choosefont $font $which] - ${NS}::label $top.$font -relief flat -font $font \ + ttk::label $top.$font -relief flat -font $font \ -text $fontattr($font,family) -justify left grid x $top.${font}but $top.$font -sticky w } -proc choosefont {font which} { - global fontparam fontlist fonttop fontattr - global prefstop NS - - set fontparam(which) $which - set fontparam(font) $font - set fontparam(family) [font actual $font -family] - set fontparam(size) $fontattr($font,size) - set fontparam(weight) $fontattr($font,weight) - set fontparam(slant) $fontattr($font,slant) - set top .gitkfont - set fonttop $top - if {![winfo exists $top]} { - font create sample - eval font config sample [font actual $font] - ttk_toplevel $top - make_transient $top $prefstop - wm title $top [mc "Gitk font chooser"] - ${NS}::label $top.l -textvariable fontparam(which) - pack $top.l -side top - set fontlist [lsort [font families]] - ${NS}::frame $top.f - listbox $top.f.fam -listvariable fontlist \ - -yscrollcommand [list $top.f.sb set] - bind $top.f.fam <<ListboxSelect>> selfontfam - ${NS}::scrollbar $top.f.sb -command [list $top.f.fam yview] - pack $top.f.sb -side right -fill y - pack $top.f.fam -side left -fill both -expand 1 - pack $top.f -side top -fill both -expand 1 - ${NS}::frame $top.g - spinbox $top.g.size -from 4 -to 40 -width 4 \ - -textvariable fontparam(size) \ - -validatecommand {string is integer -strict %s} - checkbutton $top.g.bold -padx 5 \ - -font {{Times New Roman} 12 bold} -text [mc "B"] -indicatoron 0 \ - -variable fontparam(weight) -onvalue bold -offvalue normal - checkbutton $top.g.ital -padx 5 \ - -font {{Times New Roman} 12 italic} -text [mc "I"] -indicatoron 0 \ - -variable fontparam(slant) -onvalue italic -offvalue roman - pack $top.g.size $top.g.bold $top.g.ital -side left - pack $top.g -side top - canvas $top.c -width 150 -height 50 -border 2 -relief sunk \ - -background white - $top.c create text 100 25 -anchor center -text $which -font sample \ - -fill black -tags text - bind $top.c <Configure> [list centertext $top.c] - pack $top.c -side top -fill x - ${NS}::frame $top.buts - ${NS}::button $top.buts.ok -text [mc "OK"] -command fontok -default active - ${NS}::button $top.buts.can -text [mc "Cancel"] -command fontcan -default normal - bind $top <Key-Return> fontok - bind $top <Key-Escape> fontcan - grid $top.buts.ok $top.buts.can - grid columnconfigure $top.buts 0 -weight 1 -uniform a - grid columnconfigure $top.buts 1 -weight 1 -uniform a - pack $top.buts -side bottom -fill x - trace add variable fontparam write chg_fontparam - } else { - raise $top - $top.c itemconf text -text $which - } - set i [lsearch -exact $fontlist $fontparam(family)] - if {$i >= 0} { - $top.f.fam selection set $i - $top.f.fam see $i - } -} - proc centertext {w} { $w coords text [expr {[winfo width $w] / 2}] [expr {[winfo height $w] / 2}] } @@ -11709,26 +11645,21 @@ proc fontcan {} { } } -if {[package vsatisfies [package provide Tk] 8.6]} { - # In Tk 8.6 we have a native font chooser dialog. Overwrite the above - # function to make use of it. - proc choosefont {font which} { - tk fontchooser configure -title $which -font $font \ - -command [list on_choosefont $font $which] - tk fontchooser show - } - proc on_choosefont {font which newfont} { - global fontparam - puts stderr "$font $newfont" - array set f [font actual $newfont] - set fontparam(which) $which - set fontparam(font) $font - set fontparam(family) $f(-family) - set fontparam(size) $f(-size) - set fontparam(weight) $f(-weight) - set fontparam(slant) $f(-slant) - fontok - } +proc choosefont {font which} { + tk fontchooser configure -title $which -font $font \ + -command [list on_choosefont $font $which] + tk fontchooser show +} +proc on_choosefont {font which newfont} { + global fontparam + array set f [font actual $newfont] + set fontparam(which) $which + set fontparam(font) $font + set fontparam(family) $f(-family) + set fontparam(size) $f(-size) + set fontparam(weight) $f(-weight) + set fontparam(slant) $f(-slant) + fontok } proc selfontfam {} { @@ -11748,172 +11679,170 @@ proc chg_fontparam {v sub op} { # Create a property sheet tab page proc create_prefs_page {w} { - global NS - set parent [join [lrange [split $w .] 0 end-1] .] - if {[winfo class $parent] eq "TNotebook"} { - ${NS}::frame $w - } else { - ${NS}::labelframe $w - } + ttk::frame $w } proc prefspage_general {notebook} { - global NS maxwidth maxgraphpct showneartags showlocalchanges - global tabstop wrapcomment wrapdefault limitdiffs - global autocopy autoselect autosellen extdifftool perfile_attrs - global hideremotes want_ttk have_ttk maxrefs web_browser + global {*}$::config_variables + global hashlength set page [create_prefs_page $notebook.general] - ${NS}::label $page.ldisp -text [mc "Commit list display options"] -font mainfontbold + ttk::label $page.ldisp -text [mc "Commit list display options"] -font mainfontbold grid $page.ldisp - -sticky w -pady 10 - ${NS}::label $page.spacer -text " " - ${NS}::label $page.maxwidthl -text [mc "Maximum graph width (lines)"] + ttk::label $page.spacer -text " " + ttk::label $page.maxwidthl -text [mc "Maximum graph width (lines)"] spinbox $page.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth grid $page.spacer $page.maxwidthl $page.maxwidth -sticky w #xgettext:no-tcl-format - ${NS}::label $page.maxpctl -text [mc "Maximum graph width (% of pane)"] + ttk::label $page.maxpctl -text [mc "Maximum graph width (% of pane)"] spinbox $page.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct grid x $page.maxpctl $page.maxpct -sticky w - ${NS}::checkbutton $page.showlocal -text [mc "Show local changes"] \ + ttk::checkbutton $page.showlocal -text [mc "Show local changes"] \ -variable showlocalchanges grid x $page.showlocal -sticky w - ${NS}::checkbutton $page.hideremotes -text [mc "Hide remote refs"] \ + ttk::checkbutton $page.hideremotes -text [mc "Hide remote refs"] \ -variable hideremotes grid x $page.hideremotes -sticky w - ${NS}::checkbutton $page.autocopy -text [mc "Copy commit ID to clipboard"] \ + ttk::entry $page.refstohide -textvariable refstohide + ttk::frame $page.refstohidef + ttk::label $page.refstohidef.l -text [mc "Refs to hide (space-separated globs)" ] + pack $page.refstohidef.l -side left + pack configure $page.refstohidef.l -padx 10 + grid x $page.refstohidef $page.refstohide -sticky ew + + ttk::checkbutton $page.autocopy -text [mc "Copy commit ID to clipboard"] \ -variable autocopy grid x $page.autocopy -sticky w if {[haveselectionclipboard]} { - ${NS}::checkbutton $page.autoselect -text [mc "Copy commit ID to X11 selection"] \ + ttk::checkbutton $page.autoselect -text [mc "Copy commit ID to X11 selection"] \ -variable autoselect grid x $page.autoselect -sticky w } - spinbox $page.autosellen -from 1 -to 40 -width 4 -textvariable autosellen - ${NS}::label $page.autosellenl -text [mc "Length of commit ID to copy"] + + spinbox $page.autosellen -from 1 -to $hashlength -width 4 -textvariable autosellen + ttk::label $page.autosellenl -text [mc "Length of commit ID to copy"] grid x $page.autosellenl $page.autosellen -sticky w + ttk::label $page.kscroll1 -text [mc "Wheel scrolling multiplier"] + spinbox $page.kscroll -from 1 -to 20 -width 4 -textvariable kscroll + grid x $page.kscroll1 $page.kscroll -sticky w - ${NS}::label $page.ddisp -text [mc "Diff display options"] -font mainfontbold + ttk::label $page.ddisp -text [mc "Diff display options"] -font mainfontbold grid $page.ddisp - -sticky w -pady 10 - ${NS}::label $page.tabstopl -text [mc "Tab spacing"] + ttk::label $page.tabstopl -text [mc "Tab spacing"] spinbox $page.tabstop -from 1 -to 20 -width 4 -textvariable tabstop grid x $page.tabstopl $page.tabstop -sticky w - ${NS}::label $page.wrapcommentl -text [mc "Wrap comment text"] + ttk::label $page.wrapcommentl -text [mc "Wrap comment text"] makedroplist $page.wrapcomment wrapcomment none char word grid x $page.wrapcommentl $page.wrapcomment -sticky w - ${NS}::label $page.wrapdefaultl -text [mc "Wrap other text"] + ttk::label $page.wrapdefaultl -text [mc "Wrap other text"] makedroplist $page.wrapdefault wrapdefault none char word grid x $page.wrapdefaultl $page.wrapdefault -sticky w - ${NS}::checkbutton $page.ntag -text [mc "Display nearby tags/heads"] \ + ttk::checkbutton $page.ntag -text [mc "Display nearby tags/heads"] \ -variable showneartags grid x $page.ntag -sticky w - ${NS}::label $page.maxrefsl -text [mc "Maximum # tags/heads to show"] + ttk::label $page.maxrefsl -text [mc "Maximum # tags/heads to show"] spinbox $page.maxrefs -from 1 -to 1000 -width 4 -textvariable maxrefs grid x $page.maxrefsl $page.maxrefs -sticky w - ${NS}::checkbutton $page.ldiff -text [mc "Limit diffs to listed paths"] \ + ttk::checkbutton $page.ldiff -text [mc "Limit diffs to listed paths"] \ -variable limitdiffs grid x $page.ldiff -sticky w - ${NS}::checkbutton $page.lattr -text [mc "Support per-file encodings"] \ + ttk::checkbutton $page.lattr -text [mc "Support per-file encodings"] \ -variable perfile_attrs grid x $page.lattr -sticky w - ${NS}::entry $page.extdifft -textvariable extdifftool - ${NS}::frame $page.extdifff - ${NS}::label $page.extdifff.l -text [mc "External diff tool" ] - ${NS}::button $page.extdifff.b -text [mc "Choose..."] -command choose_extdiff + ttk::entry $page.extdifft -textvariable extdifftool + ttk::frame $page.extdifff + ttk::label $page.extdifff.l -text [mc "External diff tool" ] + ttk::button $page.extdifff.b -text [mc "Choose..."] -command choose_extdiff pack $page.extdifff.l $page.extdifff.b -side left pack configure $page.extdifff.l -padx 10 grid x $page.extdifff $page.extdifft -sticky ew - ${NS}::entry $page.webbrowser -textvariable web_browser - ${NS}::frame $page.webbrowserf - ${NS}::label $page.webbrowserf.l -text [mc "Web browser" ] + ttk::entry $page.webbrowser -textvariable web_browser + ttk::frame $page.webbrowserf + ttk::label $page.webbrowserf.l -text [mc "Web browser" ] pack $page.webbrowserf.l -side left pack configure $page.webbrowserf.l -padx 10 grid x $page.webbrowserf $page.webbrowser -sticky ew - ${NS}::label $page.lgen -text [mc "General options"] -font mainfontbold - grid $page.lgen - -sticky w -pady 10 - ${NS}::checkbutton $page.want_ttk -variable want_ttk \ - -text [mc "Use themed widgets"] - if {$have_ttk} { - ${NS}::label $page.ttk_note -text [mc "(change requires restart)"] - } else { - ${NS}::label $page.ttk_note -text [mc "(currently unavailable)"] - } - grid x $page.want_ttk $page.ttk_note -sticky w return $page } proc prefspage_colors {notebook} { - global NS uicolor bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor + global uicolor bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor global diffbgcolors set page [create_prefs_page $notebook.colors] - ${NS}::label $page.cdisp -text [mc "Colors: press to choose"] -font mainfontbold + ttk::label $page.cdisp -text [mc "Colors: press to choose"] -font mainfontbold grid $page.cdisp - -sticky w -pady 10 label $page.ui -padx 40 -relief sunk -background $uicolor - ${NS}::button $page.uibut -text [mc "Interface"] \ - -command [list choosecolor uicolor {} $page.ui [mc "interface"] setui] + ttk::button $page.uibut -text [mc "Interface"] \ + -command [list choosecolor uicolor {} $page [mc "interface"]] grid x $page.uibut $page.ui -sticky w label $page.bg -padx 40 -relief sunk -background $bgcolor - ${NS}::button $page.bgbut -text [mc "Background"] \ - -command [list choosecolor bgcolor {} $page.bg [mc "background"] setbg] + ttk::button $page.bgbut -text [mc "Background"] \ + -command [list choosecolor bgcolor {} $page [mc "background"]] grid x $page.bgbut $page.bg -sticky w label $page.fg -padx 40 -relief sunk -background $fgcolor - ${NS}::button $page.fgbut -text [mc "Foreground"] \ - -command [list choosecolor fgcolor {} $page.fg [mc "foreground"] setfg] + ttk::button $page.fgbut -text [mc "Foreground"] \ + -command [list choosecolor fgcolor {} $page [mc "foreground"]] grid x $page.fgbut $page.fg -sticky w label $page.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0] - ${NS}::button $page.diffoldbut -text [mc "Diff: old lines"] \ - -command [list choosecolor diffcolors 0 $page.diffold [mc "diff old lines"] \ - [list $ctext tag conf d0 -foreground]] + ttk::button $page.diffoldbut -text [mc "Diff: old lines"] \ + -command [list choosecolor diffcolors 0 $page [mc "diff old lines"]] grid x $page.diffoldbut $page.diffold -sticky w label $page.diffoldbg -padx 40 -relief sunk -background [lindex $diffbgcolors 0] - ${NS}::button $page.diffoldbgbut -text [mc "Diff: old lines bg"] \ - -command [list choosecolor diffbgcolors 0 $page.diffoldbg \ - [mc "diff old lines bg"] \ - [list $ctext tag conf d0 -background]] + ttk::button $page.diffoldbgbut -text [mc "Diff: old lines bg"] \ + -command [list choosecolor diffbgcolors 0 $page [mc "diff old lines bg"]] grid x $page.diffoldbgbut $page.diffoldbg -sticky w label $page.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1] - ${NS}::button $page.diffnewbut -text [mc "Diff: new lines"] \ - -command [list choosecolor diffcolors 1 $page.diffnew [mc "diff new lines"] \ - [list $ctext tag conf dresult -foreground]] + ttk::button $page.diffnewbut -text [mc "Diff: new lines"] \ + -command [list choosecolor diffcolors 1 $page [mc "diff new lines"]] grid x $page.diffnewbut $page.diffnew -sticky w label $page.diffnewbg -padx 40 -relief sunk -background [lindex $diffbgcolors 1] - ${NS}::button $page.diffnewbgbut -text [mc "Diff: new lines bg"] \ - -command [list choosecolor diffbgcolors 1 $page.diffnewbg \ - [mc "diff new lines bg"] \ - [list $ctext tag conf dresult -background]] + ttk::button $page.diffnewbgbut -text [mc "Diff: new lines bg"] \ + -command [list choosecolor diffbgcolors 1 $page [mc "diff new lines bg"]] grid x $page.diffnewbgbut $page.diffnewbg -sticky w label $page.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2] - ${NS}::button $page.hunksepbut -text [mc "Diff: hunk header"] \ - -command [list choosecolor diffcolors 2 $page.hunksep \ - [mc "diff hunk header"] \ - [list $ctext tag conf hunksep -foreground]] + ttk::button $page.hunksepbut -text [mc "Diff: hunk header"] \ + -command [list choosecolor diffcolors 2 $page [mc "diff hunk header"]] grid x $page.hunksepbut $page.hunksep -sticky w label $page.markbgsep -padx 40 -relief sunk -background $markbgcolor - ${NS}::button $page.markbgbut -text [mc "Marked line bg"] \ - -command [list choosecolor markbgcolor {} $page.markbgsep \ - [mc "marked line background"] \ - [list $ctext tag conf omark -background]] + ttk::button $page.markbgbut -text [mc "Marked line bg"] \ + -command [list choosecolor markbgcolor {} $page [mc "marked line background"]] grid x $page.markbgbut $page.markbgsep -sticky w label $page.selbgsep -padx 40 -relief sunk -background $selectbgcolor - ${NS}::button $page.selbgbut -text [mc "Select bg"] \ - -command [list choosecolor selectbgcolor {} $page.selbgsep [mc "background"] setselbg] + ttk::button $page.selbgbut -text [mc "Select bg"] \ + -command [list choosecolor selectbgcolor {} $page [mc "background"]] grid x $page.selbgbut $page.selbgsep -sticky w return $page } +proc prefspage_set_colorswatches {page} { + global uicolor bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor + global diffbgcolors + + $page.ui configure -background $uicolor + $page.bg configure -background $bgcolor + $page.fg configure -background $fgcolor + $page.diffold configure -background [lindex $diffcolors 0] + $page.diffoldbg configure -background [lindex $diffbgcolors 0] + $page.diffnew configure -background [lindex $diffcolors 1] + $page.diffnewbg configure -background [lindex $diffbgcolors 1] + $page.hunksep configure -background [lindex $diffcolors 2] + $page.markbgsep configure -background $markbgcolor + $page.selbgsep configure -background $selectbgcolor +} + proc prefspage_fonts {notebook} { - global NS set page [create_prefs_page $notebook.fonts] - ${NS}::label $page.cfont -text [mc "Fonts: press to choose"] -font mainfontbold + ttk::label $page.cfont -text [mc "Fonts: press to choose"] -font mainfontbold grid $page.cfont - -sticky w -pady 10 mkfontdisp mainfont $page [mc "Main font"] mkfontdisp textfont $page [mc "Diff display font"] @@ -11922,11 +11851,8 @@ proc prefspage_fonts {notebook} { } proc doprefs {} { - global maxwidth maxgraphpct use_ttk NS - global oldprefs prefstop showneartags showlocalchanges - global uicolor bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor - global tabstop limitdiffs autoselect autosellen extdifftool perfile_attrs - global hideremotes want_ttk have_ttk wrapcomment wrapdefault + global oldprefs prefstop + global {*}$::config_variables set top .gitkprefs set prefstop $top @@ -11934,49 +11860,34 @@ proc doprefs {} { raise $top return } - foreach v {maxwidth maxgraphpct showneartags showlocalchanges \ - limitdiffs tabstop perfile_attrs hideremotes want_ttk wrapcomment wrapdefault} { + foreach v $::config_variables { set oldprefs($v) [set $v] } ttk_toplevel $top wm title $top [mc "Gitk preferences"] make_transient $top . - if {[set use_notebook [expr {$use_ttk && [info command ::ttk::notebook] ne ""}]]} { - set notebook [ttk::notebook $top.notebook] - } else { - set notebook [${NS}::frame $top.notebook -borderwidth 0 -relief flat] - } + set notebook [ttk::notebook $top.notebook] lappend pages [prefspage_general $notebook] [mc "General"] lappend pages [prefspage_colors $notebook] [mc "Colors"] lappend pages [prefspage_fonts $notebook] [mc "Fonts"] set col 0 foreach {page title} $pages { - if {$use_notebook} { - $notebook add $page -text $title - } else { - set btn [${NS}::button $notebook.b_[string map {. X} $page] \ - -text $title -command [list raise $page]] - $page configure -text $title - grid $btn -row 0 -column [incr col] -sticky w - grid $page -row 1 -column 0 -sticky news -columnspan 100 - } + $notebook add $page -text $title } - if {!$use_notebook} { - grid columnconfigure $notebook 0 -weight 1 - grid rowconfigure $notebook 1 -weight 1 - raise [lindex $pages 0] - } + grid columnconfigure $notebook 0 -weight 1 + grid rowconfigure $notebook 1 -weight 1 + raise [lindex $pages 0] grid $notebook -sticky news -padx 2 -pady 2 grid rowconfigure $top 0 -weight 1 grid columnconfigure $top 0 -weight 1 - ${NS}::frame $top.buts - ${NS}::button $top.buts.ok -text [mc "OK"] -command prefsok -default active - ${NS}::button $top.buts.can -text [mc "Cancel"] -command prefscan -default normal + ttk::frame $top.buts + ttk::button $top.buts.ok -text [mc "OK"] -command prefsok -default active + ttk::button $top.buts.can -text [mc "Cancel"] -command prefscan -default normal bind $top <Key-Return> prefsok bind $top <Key-Escape> prefscan grid $top.buts.ok $top.buts.can @@ -11996,15 +11907,15 @@ proc choose_extdiff {} { } } -proc choosecolor {v vi w x cmd} { +proc choosecolor {v vi prefspage x} { global $v set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \ -title [mc "Gitk: choose color for %s" $x]] if {$c eq {}} return - $w conf -background $c lset $v $vi $c - eval $cmd $c + set_gui_colors + prefspage_set_colorswatches $prefspage } proc setselbg {c} { @@ -12057,25 +11968,38 @@ proc setfg {c} { $canv itemconf markid -outline $c } +proc set_gui_colors {} { + global uicolor bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor + global diffbgcolors + + setui $uicolor + setbg $bgcolor + setfg $fgcolor + $ctext tag conf d0 -foreground [lindex $diffcolors 0] + $ctext tag conf d0 -background [lindex $diffbgcolors 0] + $ctext tag conf dresult -foreground [lindex $diffcolors 1] + $ctext tag conf dresult -background [lindex $diffbgcolors 1] + $ctext tag conf hunksep -foreground [lindex $diffcolors 2] + $ctext tag conf omark -background $markbgcolor + setselbg $selectbgcolor +} + proc prefscan {} { global oldprefs prefstop + global {*}$::config_variables - foreach v {maxwidth maxgraphpct showneartags showlocalchanges \ - limitdiffs tabstop perfile_attrs hideremotes want_ttk wrapcomment wrapdefault} { - global $v + foreach v $::config_variables { set $v $oldprefs($v) } catch {destroy $prefstop} unset prefstop fontcan + set_gui_colors } proc prefsok {} { - global maxwidth maxgraphpct - global oldprefs prefstop showneartags showlocalchanges - global fontpref mainfont textfont uifont - global limitdiffs treediffs perfile_attrs - global hideremotes wrapcomment wrapdefault + global oldprefs prefstop fontpref treediffs + global {*}$::config_variables global ctext catch {destroy $prefstop} @@ -12122,7 +12046,7 @@ proc prefsok {} { $limitdiffs != $oldprefs(limitdiffs)} { reselectline } - if {$hideremotes != $oldprefs(hideremotes)} { + if {$hideremotes != $oldprefs(hideremotes) || $refstohide != $oldprefs(refstohide)} { rereadrefs } if {$wrapcomment != $oldprefs(wrapcomment)} { @@ -12478,7 +12402,7 @@ proc cache_gitattr {attr pathlist} { foreach row [split $rlist "\n"] { if {[regexp "(.*): $attr: (.*)" $row m path value]} { if {[string index $path 0] eq "\""} { - set path [encoding convertfrom utf-8 [lindex $path 0]] + set path [convertfrom utf-8 [lindex $path 0]] } set path_attr_cache($attr,$path) $value } @@ -12499,6 +12423,23 @@ proc get_path_encoding {path} { return $tcl_enc } +proc is_other_ref_visible {ref} { + global refstohide + + if {$refstohide eq {}} { + return 1 + } + + foreach pat [split $refstohide " "] { + if {$pat eq {}} continue + if {[string match $pat $ref]} { + return 0 + } + } + + return 1 +} + ## For msgcat loading, first locate the installation location. if { [info exists ::env(GITK_MSGSDIR)] } { ## Msgsdir was manually set in the environment. @@ -12517,13 +12458,6 @@ namespace import ::msgcat::mc ## And eventually load the actual message catalog ::msgcat::mcload $gitk_msgsdir -# First check that Tcl/Tk is recent enough -if {[catch {package require Tk 8.4} err]} { - show_error {} . [mc "Sorry, gitk cannot run with this version of Tcl/Tk.\n\ - Gitk requires at least Tcl/Tk 8.4."] - exit 1 -} - # on OSX bring the current Wish process window to front if {[tk windowingsystem] eq "aqua"} { safe_exec [list osascript -e [format { @@ -12569,6 +12503,17 @@ catch { } } +# Use object format as hash algorightm (either "sha1" or "sha256") +set hashalgorithm [exec git rev-parse --show-object-format] +if {$hashalgorithm eq "sha1"} { + set hashlength 40 +} elseif {$hashalgorithm eq "sha256"} { + set hashlength 64 +} else { + puts stderr "Unknown hash algorithm: $hashalgorithm" + exit 1 +} + set log_showroot true catch { set log_showroot [exec git config --bool --get log.showroot] @@ -12602,17 +12547,19 @@ set wrapcomment "none" set wrapdefault "none" set showneartags 1 set hideremotes 0 +set refstohide "" +set sortrefsbytype 1 set maxrefs 20 set visiblerefs {"master"} set maxlinelen 200 set showlocalchanges 1 set limitdiffs 1 +set kscroll 3 set datetimeformat "%Y-%m-%d %H:%M:%S" set autocopy 0 set autoselect 1 -set autosellen 40 +set autosellen $hashlength set perfile_attrs 0 -set want_ttk 1 if {[tk windowingsystem] eq "aqua"} { set extdifftool "opendiff" @@ -12673,7 +12620,7 @@ set foundbgcolor yellow set currentsearchhitbgcolor orange # button for popping up context menus -if {[tk windowingsystem] eq "aqua"} { +if {[tk windowingsystem] eq "aqua" && [package vcompare $::tcl_version 8.7] < 0} { set ctxbut <Button-2> } else { set ctxbut <Button-3> @@ -12688,14 +12635,14 @@ catch { set config_file_tmp [file join $env(XDG_CONFIG_HOME) git gitk-tmp] } else { # default XDG_CONFIG_HOME - set config_file "~/.config/git/gitk" - set config_file_tmp "~/.config/git/gitk-tmp" + set config_file "$env(HOME)/.config/git/gitk" + set config_file_tmp "$env(HOME)/.config/git/gitk-tmp" } if {![file exists $config_file]} { # for backward compatibility use the old config file if it exists - if {[file exists "~/.gitk"]} { - set config_file "~/.gitk" - set config_file_tmp "~/.gitk-tmp" + if {[file exists "$env(HOME)/.gitk"]} { + set config_file "$env(HOME)/.gitk" + set config_file_tmp "$env(HOME)/.gitk-tmp" } elseif {![file exists [file dirname $config_file]]} { file mkdir [file dirname $config_file] } @@ -12705,19 +12652,67 @@ catch { config_check_tmp_exists 50 set config_variables { - mainfont textfont uifont tabstop findmergefiles maxgraphpct maxwidth - cmitmode wrapcomment wrapdefault autocopy autoselect autosellen - showneartags maxrefs visiblerefs - hideremotes showlocalchanges datetimeformat limitdiffs uicolor want_ttk - bgcolor fgcolor uifgcolor uifgdisabledcolor colors diffcolors mergecolors - markbgcolor diffcontext selectbgcolor foundbgcolor currentsearchhitbgcolor - extdifftool perfile_attrs headbgcolor headfgcolor headoutlinecolor - remotebgcolor tagbgcolor tagfgcolor tagoutlinecolor reflinecolor - filesepbgcolor filesepfgcolor linehoverbgcolor linehoverfgcolor - linehoveroutlinecolor mainheadcirclecolor workingfilescirclecolor - indexcirclecolor circlecolors linkfgcolor circleoutlinecolor diffbgcolors + autocopy + autoselect + autosellen + bgcolor + circlecolors + circleoutlinecolor + cmitmode + colors + currentsearchhitbgcolor + datetimeformat + diffbgcolors + diffcolors + diffcontext + extdifftool + fgcolor + filesepbgcolor + filesepfgcolor + findmergefiles + foundbgcolor + headbgcolor + headfgcolor + headoutlinecolor + hideremotes + indexcirclecolor + kscroll + limitdiffs + linehoverbgcolor + linehoverfgcolor + linehoveroutlinecolor + linkfgcolor + mainfont + mainheadcirclecolor + markbgcolor + maxgraphpct + maxrefs + maxwidth + mergecolors + perfile_attrs + reflinecolor + refstohide + remotebgcolor + selectbgcolor + showlocalchanges + showneartags + sortrefsbytype + tabstop + tagbgcolor + tagfgcolor + tagoutlinecolor + textfont + uicolor + uifgcolor + uifgdisabledcolor + uifont + visiblerefs web_browser + workingfilescirclecolor + wrapcomment + wrapdefault } + foreach var $config_variables { config_init_trace $var trace add variable $var write config_variable_change_cb @@ -12734,8 +12729,6 @@ eval font create textfontbold [fontflags textfont 1] parsefont uifont $uifont eval font create uifont [fontflags uifont] -setui $uicolor - setoptions # check that we can find a .git directory somewhere... @@ -12808,25 +12801,7 @@ set nullid "0000000000000000000000000000000000000000" set nullid2 "0000000000000000000000000000000000000001" set nullfile "/dev/null" -set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}] -set have_tk86 [expr {[package vcompare $tk_version "8.6"] >= 0}] -if {![info exists have_ttk]} { - set have_ttk [llength [info commands ::ttk::style]] -} -set use_ttk [expr {$have_ttk && $want_ttk}] -set NS [expr {$use_ttk ? "ttk" : ""}] - -if {$use_ttk} { - setttkstyle -} - -regexp {^git version ([\d.]*\d)} [exec git version] _ git_version - -set show_notes {} -if {[package vcompare $git_version "1.6.6.2"] >= 0} { - set show_notes "--show-notes" -} - +setttkstyle set appname "gitk" set runq {} @@ -12942,6 +12917,8 @@ if {[tk windowingsystem] eq "win32"} { focus -force . } +set_gui_colors + getcommits {} # Local variables: diff --git a/gitk-git/po/bg.po b/gitk-git/po/bg.po index 773a049831..d1e7d92425 100644 --- a/gitk-git/po/bg.po +++ b/gitk-git/po/bg.po @@ -1,15 +1,15 @@ # Bulgarian translation of gitk po-file. -# Copyright (C) 2014, 2015, 2019, 2020, 2024 Alexander Shopov <ash@kambanaria.org>. +# Copyright (C) 2014, 2015, 2019, 2020, 2024, 2025 Alexander Shopov <ash@kambanaria.org>. # This file is distributed under the same license as the git package. -# Alexander Shopov <ash@kambanaria.org>, 2014, 2015, 2019, 2020, 2024. +# Alexander Shopov <ash@kambanaria.org>, 2014, 2015, 2019, 2020, 2024, 2025. # # msgid "" msgstr "" "Project-Id-Version: gitk master\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-24 11:01+0100\n" -"PO-Revision-Date: 2024-12-24 11:05+0100\n" +"POT-Creation-Date: 2025-07-22 18:34+0200\n" +"PO-Revision-Date: 2025-07-28 13:38+0200\n" "Last-Translator: Alexander Shopov <ash@kambanaria.org>\n" "Language-Team: Bulgarian <dict@fsa-bg.org>\n" "Language: bg\n" @@ -18,32 +18,25 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: gitk:139 msgid "Couldn't get list of unmerged files:" msgstr "СпиÑъкът Ñ Ð½ÐµÑлети файлове не може да Ñе получи:" -#: gitk:211 gitk:2430 msgid "Color words" msgstr "ОцветÑване на думите" -#: gitk:216 gitk:2430 gitk:8335 gitk:8368 msgid "Markup words" msgstr "ОтбелÑзване на думите" -#: gitk:323 msgid "Error parsing revisions:" msgstr "Грешка при анализ на верÑиите:" -#: gitk:389 msgid "Error executing --argscmd command:" msgstr "Грешка при изпълнение на командата Ñ â€ž--argscmd“." -#: gitk:402 msgid "No files selected: --merge specified but no files are unmerged." msgstr "" "Ðе Ñа избрани файлове — указана е опциÑта „--merge“, но нÑма неÑлети файлове." -#: gitk:405 msgid "" "No files selected: --merge specified but no unmerged files are within file " "limit." @@ -51,326 +44,246 @@ msgstr "" "Ðе Ñа избрани файлове — указана е опциÑта „--merge“, но нÑма неÑлети файлове " "в ограничениÑта." -#: gitk:430 gitk:585 msgid "Error executing git log:" msgstr "Грешка при изпълнение на „git log“:" -#: gitk:448 gitk:601 msgid "Reading" msgstr "Прочитане" -#: gitk:508 gitk:4596 msgid "Reading commits..." msgstr "Прочитане на подаваниÑта…" -#: gitk:511 gitk:1660 gitk:4599 msgid "No commits selected" msgstr "Ðе Ñа избрани подаваниÑ" -#: gitk:1468 gitk:4116 gitk:12738 msgid "Command line" msgstr "Команден ред" -#: gitk:1534 msgid "Can't parse git log output:" msgstr "Изходът от „git log“ не може да Ñе анализира:" -#: gitk:1763 msgid "No commit information available" msgstr "ЛипÑва Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° подаваниÑ" -#: gitk:1930 gitk:1959 gitk:4386 gitk:9875 gitk:11485 gitk:11805 msgid "OK" msgstr "Добре" -#: gitk:1961 gitk:4388 gitk:9311 gitk:9390 gitk:9520 gitk:9606 gitk:9877 -#: gitk:11486 gitk:11806 msgid "Cancel" msgstr "Отказ" -#: gitk:2114 msgid "&Update" msgstr "&ОбновÑване" -#: gitk:2115 msgid "&Reload" msgstr "&Презареждане" -#: gitk:2116 msgid "Reread re&ferences" msgstr "Прочитане &наново" -#: gitk:2117 msgid "&List references" msgstr "&ИзброÑване на указателите" -#: gitk:2119 msgid "Start git &gui" msgstr "&Стартиране на „git gui“" -#: gitk:2121 msgid "&Quit" msgstr "&Спиране на програмата" -#: gitk:2113 msgid "&File" msgstr "&Файл" -#: gitk:2125 msgid "&Preferences" msgstr "&ÐаÑтройки" -#: gitk:2124 msgid "&Edit" msgstr "&Редактиране" -#: gitk:2129 msgid "&New view..." msgstr "&Ðов изглед…" -#: gitk:2130 msgid "&Edit view..." msgstr "&Редактиране на изгледа…" -#: gitk:2131 msgid "&Delete view" msgstr "&Изтриване на изгледа" -#: gitk:2133 msgid "&All files" msgstr "&Ð’Ñички файлове" -#: gitk:2128 msgid "&View" msgstr "&Изглед" -#: gitk:2138 gitk:2148 msgid "&About gitk" msgstr "&ОтноÑно gitk" -#: gitk:2139 gitk:2153 msgid "&Key bindings" msgstr "&Клавишни комбинации" -#: gitk:2137 gitk:2152 msgid "&Help" msgstr "Помо&щ" -#: gitk:2230 gitk:8767 msgid "Commit ID:" msgstr "Подаване:" -#: gitk:2274 msgid "Row" msgstr "Ред" -#: gitk:2312 msgid "Find" msgstr "ТърÑене" -#: gitk:2340 msgid "commit" msgstr "подаване" -#: gitk:2344 gitk:2346 gitk:4758 gitk:4781 gitk:4805 gitk:6826 gitk:6898 -#: gitk:6983 msgid "containing:" msgstr "Ñъдържащо:" -#: gitk:2347 gitk:3597 gitk:3602 gitk:4834 msgid "touching paths:" msgstr "в пътищата:" -#: gitk:2348 gitk:4848 msgid "adding/removing string:" msgstr "добавÑщо/премахващо низ" -#: gitk:2349 gitk:4850 msgid "changing lines matching:" msgstr "променÑщо редове напаÑващи:" -#: gitk:2358 gitk:2360 gitk:4837 msgid "Exact" msgstr "Точно" -#: gitk:2360 gitk:4925 gitk:6794 msgid "IgnCase" msgstr "Без региÑтър" -#: gitk:2360 gitk:4807 gitk:4923 gitk:6790 msgid "Regexp" msgstr "Рег. израз" -#: gitk:2362 gitk:2363 gitk:4945 gitk:4975 gitk:4982 gitk:6919 gitk:6987 msgid "All fields" msgstr "Ð’Ñички полета" -#: gitk:2363 gitk:4942 gitk:4975 gitk:6857 msgid "Headline" msgstr "Първи ред" -#: gitk:2364 gitk:4942 gitk:6857 gitk:6987 gitk:7499 msgid "Comments" msgstr "Коментари" -#: gitk:2364 gitk:4942 gitk:4947 gitk:4982 gitk:6857 gitk:7434 gitk:8945 -#: gitk:8960 msgid "Author" msgstr "Ðвтор" -#: gitk:2364 gitk:4942 gitk:6857 gitk:7436 msgid "Committer" msgstr "Подаващ" -#: gitk:2398 msgid "Search" msgstr "ТърÑене" -#: gitk:2406 msgid "Diff" msgstr "Разлики" -#: gitk:2408 msgid "Old version" msgstr "Стара верÑиÑ" -#: gitk:2410 msgid "New version" msgstr "Ðова верÑиÑ" -#: gitk:2413 msgid "Lines of context" msgstr "КонтекÑÑ‚ в редове" -#: gitk:2423 msgid "Ignore space change" msgstr "Празните знаци без значение" -#: gitk:2427 gitk:2429 gitk:8069 gitk:8321 msgid "Line diff" msgstr "Поредови разлики" -#: gitk:2502 msgid "Patch" msgstr "Кръпка" -#: gitk:2504 msgid "Tree" msgstr "Дърво" -#: gitk:2674 gitk:2695 +msgid "Unknown windowing system, cannot bind mouse" +msgstr "Ðепозната графична ÑиÑтема, не може да Ñе уÑтанови връзка Ñ Ð¼Ð¸ÑˆÐºÐ°" + msgid "Diff this -> selected" msgstr "Разлики между това и избраното" -#: gitk:2675 gitk:2696 msgid "Diff selected -> this" msgstr "Разлики между избраното и това" -#: gitk:2676 gitk:2697 msgid "Make patch" msgstr "Създаване на кръпка" -#: gitk:2677 gitk:9369 msgid "Create tag" msgstr "Създаване на етикет" -#: gitk:2678 msgid "Copy commit reference" msgstr "Копиране на ÑƒÐºÐ°Ð·Ð°Ñ‚ÐµÐ»Ñ Ð½Ð° подаване" -#: gitk:2679 gitk:9500 msgid "Write commit to file" msgstr "Запазване на подаването във файл" -#: gitk:2680 msgid "Create new branch" msgstr "Създаване на нов клон" -#: gitk:2681 msgid "Cherry-pick this commit" msgstr "Отбиране на това подаване" -#: gitk:2682 msgid "Reset HEAD branch to here" msgstr "Привеждане на върха на клона към текущото подаване" -#: gitk:2683 msgid "Mark this commit" msgstr "ОтбелÑзване на това подаване" -#: gitk:2684 msgid "Return to mark" msgstr "Връщане към отбелÑзаното подаване" -#: gitk:2685 msgid "Find descendant of this and mark" msgstr "Откриване и отбелÑзване на наÑледниците" -#: gitk:2686 msgid "Compare with marked commit" msgstr "Сравнение Ñ Ð¾Ñ‚Ð±ÐµÐ»Ñзаното подаване" -#: gitk:2687 gitk:2698 msgid "Diff this -> marked commit" msgstr "Разлики между това и отбелÑзаното" -#: gitk:2688 gitk:2699 msgid "Diff marked commit -> this" msgstr "Разлики между отбелÑзаното и това" -#: gitk:2689 msgid "Revert this commit" msgstr "ОтмÑна на това подаване" -#: gitk:2705 msgid "Check out this branch" msgstr "ИзтеглÑне на този клон" -#: gitk:2706 msgid "Rename this branch" msgstr "Преименуване на този клон" -#: gitk:2707 msgid "Remove this branch" msgstr "Изтриване на този клон" -#: gitk:2708 msgid "Copy branch name" msgstr "Копиране на името на клона" -#: gitk:2715 msgid "Highlight this too" msgstr "ОтбелÑзване и на това" -#: gitk:2716 msgid "Highlight this only" msgstr "ОтбелÑзване Ñамо на това" -#: gitk:2717 msgid "External diff" msgstr "Външна програма за разлики" -#: gitk:2718 msgid "Blame parent commit" msgstr "Ðнотиране на родителÑкото подаване" -#: gitk:2719 msgid "Copy path" msgstr "Копиране на пътÑ" -#: gitk:2726 msgid "Show origin of this line" msgstr "Показване на произхода на този ред" -#: gitk:2727 msgid "Run git gui blame on this line" msgstr "Изпълнение на „git gui blame“ върху този ред" -#: gitk:3081 msgid "About gitk" msgstr "ОтноÑно gitk" -#: gitk:3083 msgid "" "\n" "Gitk - a commit viewer for git\n" @@ -386,324 +299,250 @@ msgstr "" "\n" "Използвайте и разпроÑтранÑвайте при уÑловиÑта на ОПЛ на ГÐУ" -#: gitk:3091 gitk:3158 gitk:10090 msgid "Close" msgstr "ЗатварÑне" -#: gitk:3112 msgid "Gitk key bindings" msgstr "Клавишни комбинации" -#: gitk:3115 msgid "Gitk key bindings:" msgstr "Клавишни комбинации:" -#: gitk:3117 #, tcl-format msgid "<%s-Q>\t\tQuit" msgstr "<%s-Q>\t\tСпиране на програмата" -#: gitk:3118 #, tcl-format msgid "<%s-W>\t\tClose window" msgstr "<%s-W>\t\tЗатварÑне на прозореца" -#: gitk:3119 msgid "<Home>\t\tMove to first commit" msgstr "<Home>\t\tКъм първото подаване" -#: gitk:3120 msgid "<End>\t\tMove to last commit" msgstr "<End>\t\tКъм поÑледното подаване" -#: gitk:3121 msgid "<Up>, p, k\tMove up one commit" msgstr "<Up>, p, k\tЕдно подаване нагоре" -#: gitk:3122 msgid "<Down>, n, j\tMove down one commit" msgstr "<Down>, n, j\tЕдно подаване надолу" -#: gitk:3123 msgid "<Left>, z, h\tGo back in history list" msgstr "<Left>, z, h\tÐазад в иÑториÑта" -#: gitk:3124 msgid "<Right>, x, l\tGo forward in history list" msgstr "<Right>, x, l\tÐапред в иÑториÑта" -#: gitk:3125 #, tcl-format msgid "<%s-n>\tGo to n-th parent of current commit in history list" msgstr "<%s-n>\tКъм n-Ñ‚Ð¸Ñ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ» на текущото подаване в иÑториÑта" -#: gitk:3126 msgid "<PageUp>\tMove up one page in commit list" msgstr "<PageUp>\tСтраница нагоре в ÑпиÑъка Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта" -#: gitk:3127 msgid "<PageDown>\tMove down one page in commit list" msgstr "<PageDown>\tСтраница надолу в ÑпиÑъка Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта" -#: gitk:3128 #, tcl-format msgid "<%s-Home>\tScroll to top of commit list" msgstr "<%s-Home>\tКъм началото на ÑпиÑъка Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта" -#: gitk:3129 #, tcl-format msgid "<%s-End>\tScroll to bottom of commit list" msgstr "<%s-End>\tКъм ÐºÑ€Ð°Ñ Ð½Ð° ÑпиÑъка Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта" -#: gitk:3130 #, tcl-format msgid "<%s-Up>\tScroll commit list up one line" msgstr "<%s-Up>\tРед нагоре в ÑпиÑъка Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ" -#: gitk:3131 #, tcl-format msgid "<%s-Down>\tScroll commit list down one line" msgstr "<%s-Down>\tРед надолу в ÑпиÑъка Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ" -#: gitk:3132 #, tcl-format msgid "<%s-PageUp>\tScroll commit list up one page" msgstr "<%s-PageUp>\tСтраница нагоре в ÑпиÑъка Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ" -#: gitk:3133 #, tcl-format msgid "<%s-PageDown>\tScroll commit list down one page" msgstr "<%s-PageDown>\tСтраница надолу в ÑпиÑъка Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ" -#: gitk:3134 msgid "<Shift-Up>\tFind backwards (upwards, later commits)" msgstr "<Shift-Up>\tТърÑене назад (визуално нагоре, иÑторичеÑки — поÑледващи)" -#: gitk:3135 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)" msgstr "" "<Shift-Down>\tТърÑене напред (визуално надолу, иÑторичеÑки — предхождащи)" -#: gitk:3136 msgid "<Delete>, b\tScroll diff view up one page" msgstr "<Delete>, b\tСтраница нагоре в изгледа за разлики" -#: gitk:3137 msgid "<Backspace>\tScroll diff view up one page" msgstr "<Backspace>\tСтраница надолу в изгледа за разлики" -#: gitk:3138 msgid "<Space>\t\tScroll diff view down one page" msgstr "<Space>\t\tСтраница надолу в изгледа за разлики" -#: gitk:3139 msgid "u\t\tScroll diff view up 18 lines" msgstr "u\t\t18 реда нагоре в изгледа за разлики" -#: gitk:3140 msgid "d\t\tScroll diff view down 18 lines" msgstr "d\t\t18 реда надолу в изгледа за разлики" -#: gitk:3141 #, tcl-format msgid "<%s-F>\t\tFind" msgstr "<%s-F>\t\tТърÑене" -#: gitk:3142 #, tcl-format msgid "<%s-G>\t\tMove to next find hit" msgstr "<%s-G>\t\tКъм Ñледващата поÑва" -#: gitk:3143 msgid "<Return>\tMove to next find hit" msgstr "<Return>\tКъм Ñледващата поÑва" -#: gitk:3144 msgid "g\t\tGo to commit" msgstr "g\t\tКъм поÑледното подаване" -#: gitk:3145 msgid "/\t\tFocus the search box" msgstr "/\t\tÐ¤Ð¾ÐºÑƒÑ Ð²ÑŠÑ€Ñ…Ñƒ полето за търÑене" -#: gitk:3146 msgid "?\t\tMove to previous find hit" msgstr "?\t\tКъм предишната поÑва" -#: gitk:3147 msgid "f\t\tScroll diff view to next file" msgstr "f\t\tСледващ файл в изгледа за разлики" -#: gitk:3148 #, tcl-format msgid "<%s-S>\t\tSearch for next hit in diff view" msgstr "<%s-S>\t\tТърÑене на Ñледващата поÑва в изгледа за разлики" -#: gitk:3149 #, tcl-format msgid "<%s-R>\t\tSearch for previous hit in diff view" msgstr "<%s-R>\t\tТърÑене на предишната поÑва в изгледа за разлики" -#: gitk:3150 #, tcl-format msgid "<%s-KP+>\tIncrease font size" msgstr "<%s-KP+>\tПо-голÑм размер на шрифта" -#: gitk:3151 #, tcl-format msgid "<%s-plus>\tIncrease font size" msgstr "<%s-plus>\tПо-голÑм размер на шрифта" -#: gitk:3152 #, tcl-format msgid "<%s-KP->\tDecrease font size" msgstr "<%s-KP->\tПо-малък размер на шрифта" -#: gitk:3153 #, tcl-format msgid "<%s-minus>\tDecrease font size" msgstr "<%s-minus>\tПо-малък размер на шрифта" -#: gitk:3154 msgid "<F5>\t\tUpdate" msgstr "<F5>\t\tОбновÑване" -#: gitk:3621 gitk:3630 #, tcl-format msgid "Error creating temporary directory %s:" msgstr "Грешка при Ñъздаването на временната Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ â€ž%s“:" -#: gitk:3643 #, tcl-format msgid "Error getting \"%s\" from %s:" msgstr "Грешка при получаването на „%s“ от %s:" -#: gitk:3706 msgid "command failed:" msgstr "неуÑпешно изпълнение на команда:" -#: gitk:3855 msgid "No such commit" msgstr "Такова подаване нÑма" -#: gitk:3869 msgid "git gui blame: command failed:" msgstr "„git gui blame“: неуÑпешно изпълнение на команда:" -#: gitk:3900 #, tcl-format msgid "Couldn't read merge head: %s" msgstr "Върхът за Ñливане не може да Ñе прочете: %s" -#: gitk:3908 #, tcl-format msgid "Error reading index: %s" msgstr "Грешка при прочитане на индекÑа: %s" -#: gitk:3933 #, tcl-format msgid "Couldn't start git blame: %s" msgstr "Командата „git blame“ не може да Ñе Ñтартира: %s" -#: gitk:3936 gitk:6825 msgid "Searching" msgstr "ТърÑене" -#: gitk:3968 #, tcl-format msgid "Error running git blame: %s" msgstr "Грешка при изпълнението на „git blame“: %s" -#: gitk:3996 #, tcl-format msgid "That line comes from commit %s, which is not in this view" msgstr "Този ред идва от подаването %s, което не е в изгледа" -#: gitk:4010 msgid "External diff viewer failed:" msgstr "ÐеуÑпешно изпълнение на външната програма за разлики:" -#: gitk:4114 msgid "All files" msgstr "Ð’Ñички файлове" -#: gitk:4138 msgid "View" msgstr "Изглед" -#: gitk:4141 msgid "Gitk view definition" msgstr "Ð”ÐµÑ„Ð¸Ð½Ð¸Ñ†Ð¸Ñ Ð½Ð° изглед в Gitk" -#: gitk:4145 msgid "Remember this view" msgstr "Запазване на този изглед" -#: gitk:4146 msgid "References (space separated list):" msgstr "Указатели (ÑпиÑък Ñ Ñ€Ð°Ð·Ð´ÐµÐ»Ð¸Ñ‚ÐµÐ» интервал):" -#: gitk:4147 msgid "Branches & tags:" msgstr "Клони и етикети:" -#: gitk:4148 msgid "All refs" msgstr "Ð’Ñички указатели" -#: gitk:4149 msgid "All (local) branches" msgstr "Ð’Ñички (локални) клони" -#: gitk:4150 msgid "All tags" msgstr "Ð’Ñички етикети" -#: gitk:4151 msgid "All remote-tracking branches" msgstr "Ð’Ñички ÑледÑщи клони" -#: gitk:4152 msgid "Commit Info (regular expressions):" msgstr "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° подаване (рег. изр.):" -#: gitk:4153 msgid "Author:" msgstr "Ðвтор:" -#: gitk:4154 msgid "Committer:" msgstr "Подал:" -#: gitk:4155 msgid "Commit Message:" msgstr "Съобщение при подаване:" -#: gitk:4156 msgid "Matches all Commit Info criteria" msgstr "Съвпадение по вÑички характериÑтики на подаването" -#: gitk:4157 msgid "Matches no Commit Info criteria" msgstr "Ðе Ñъвпада по Ð½Ð¸ÐºÐ¾Ñ Ð¾Ñ‚ характериÑтиките на подаването" -#: gitk:4158 msgid "Changes to Files:" msgstr "Промени по файловете:" -#: gitk:4159 msgid "Fixed String" msgstr "ДоÑловен низ" -#: gitk:4160 msgid "Regular Expression" msgstr "РегулÑрен израз" -#: gitk:4161 msgid "Search string:" msgstr "Ðиз за търÑене:" -#: gitk:4162 msgid "" "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 " "15:27:38\"):" @@ -711,208 +550,159 @@ msgstr "" "Дата на подаване („2 weeks ago“ (преди 2 Ñедмици), „2009-03-17 15:27:38“, " "„March 17, 2009 15:27:38“):" -#: gitk:4163 msgid "Since:" msgstr "От:" -#: gitk:4164 msgid "Until:" msgstr "До:" -#: gitk:4165 msgid "Limit and/or skip a number of revisions (positive integer):" msgstr "" "Ограничаване и/или преÑкачане на определен брой верÑии (неотрицателно цÑло " "чиÑло):" -#: gitk:4166 msgid "Number to show:" msgstr "Брой показани:" -#: gitk:4167 msgid "Number to skip:" msgstr "Брой преÑкочени:" -#: gitk:4168 msgid "Miscellaneous options:" msgstr "Разни:" -#: gitk:4169 msgid "Strictly sort by date" msgstr "Подреждане по дата" -#: gitk:4170 msgid "Mark branch sides" msgstr "ОтбелÑзване на Ñтраните по клона" -#: gitk:4171 msgid "Limit to first parent" msgstr "Само Ð¿ÑŠÑ€Ð²Ð¸Ñ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»" -#: gitk:4172 msgid "Simple history" msgstr "ОпроÑтена иÑториÑ" -#: gitk:4173 msgid "Additional arguments to git log:" msgstr "Допълнителни аргументи към „git log“:" -#: gitk:4174 msgid "Enter files and directories to include, one per line:" msgstr "Въведете файловете и директориите за включване, по елемент на ред" -#: gitk:4175 msgid "Command to generate more commits to include:" msgstr "" "Команда за генерирането на допълнителни подаваниÑ, които да Ñе включат:" -#: gitk:4299 msgid "Gitk: edit view" msgstr "Gitk: редактиране на изглед" -#: gitk:4307 msgid "-- criteria for selecting revisions" msgstr "— критерии за избор на верÑии" -#: gitk:4312 msgid "View Name" msgstr "Име на изглед" -#: gitk:4387 msgid "Apply (F5)" msgstr "Прилагане (F5)" -#: gitk:4425 msgid "Error in commit selection arguments:" msgstr "Грешка в аргументите за избор на подаваниÑ:" -#: gitk:4480 gitk:4533 gitk:4995 gitk:5009 gitk:6279 gitk:12679 gitk:12680 msgid "None" msgstr "ÐÑма" -#: gitk:5092 gitk:5097 msgid "Descendant" msgstr "ÐаÑледник" -#: gitk:5093 msgid "Not descendant" msgstr "Ðе е наÑледник" -#: gitk:5100 gitk:5105 msgid "Ancestor" msgstr "ПредшеÑтвеник" -#: gitk:5101 msgid "Not ancestor" msgstr "Ðе е предшеÑтвеник" -#: gitk:5395 msgid "Local changes checked in to index but not committed" msgstr "Локални промени добавени към индекÑа, но неподадени" -#: gitk:5431 msgid "Local uncommitted changes, not checked in to index" msgstr "Локални промени извън индекÑа" -#: gitk:7179 msgid "Error starting web browser:" msgstr "Грешка при Ñтартирането на уеб браузър:" -#: gitk:7240 msgid "and many more" msgstr "и още много" -#: gitk:7243 msgid "many" msgstr "много" -#: gitk:7438 msgid "Tags:" msgstr "Етикети:" -#: gitk:7455 gitk:7461 gitk:8940 msgid "Parent" msgstr "Родител" -#: gitk:7466 msgid "Child" msgstr "Дете" -#: gitk:7475 msgid "Branch" msgstr "Клон" -#: gitk:7478 msgid "Follows" msgstr "Следва" -#: gitk:7481 msgid "Precedes" msgstr "ПредшеÑтва" -#: gitk:8076 #, tcl-format msgid "Error getting diffs: %s" msgstr "Грешка при получаването на разликите: %s" -#: gitk:8765 msgid "Goto:" msgstr "Към ред:" -#: gitk:8786 #, tcl-format msgid "Short commit ID %s is ambiguous" msgstr "Съкратената контролна Ñума %s не е еднозначна" -#: gitk:8793 #, tcl-format msgid "Revision %s is not known" msgstr "Ðепозната верÑÐ¸Ñ %s" -#: gitk:8803 #, tcl-format msgid "Commit ID %s is not known" msgstr "Ðепозната контролна Ñума %s" -#: gitk:8805 #, tcl-format msgid "Revision %s is not in the current view" msgstr "ВерÑÐ¸Ñ %s не е в Ñ‚ÐµÐºÑƒÑ‰Ð¸Ñ Ð¸Ð·Ð³Ð»ÐµÐ´" -#: gitk:8947 gitk:8962 msgid "Date" msgstr "Дата" -#: gitk:8950 msgid "Children" msgstr "Деца" -#: gitk:9013 #, tcl-format msgid "Reset %s branch to here" msgstr "ЗанулÑване на клона „%s“ към текущото подаване" -#: gitk:9015 msgid "Detached head: can't reset" msgstr "ÐеÑвързан връх: невъзможно занулÑване" -#: gitk:9120 gitk:9126 msgid "Skipping merge commit " msgstr "ПропуÑкане на подаването на Ñливането" -#: gitk:9135 gitk:9140 msgid "Error getting patch ID for " msgstr "Грешка при получаването на идентификатора на " -#: gitk:9136 gitk:9141 msgid " - stopping\n" msgstr " — Ñпиране\n" -#: gitk:9146 gitk:9149 gitk:9157 gitk:9171 gitk:9180 msgid "Commit " msgstr "Подаване" -#: gitk:9150 msgid "" " is the same patch as\n" " " @@ -920,7 +710,6 @@ msgstr "" " е Ñъщата кръпка като\n" " " -#: gitk:9158 msgid "" " differs from\n" " " @@ -928,7 +717,6 @@ msgstr "" " Ñе различава от\n" " " -#: gitk:9160 msgid "" "Diff of commits:\n" "\n" @@ -936,147 +724,113 @@ msgstr "" "Разлика между подаваниÑта:\n" "\n" -#: gitk:9172 gitk:9181 #, tcl-format msgid " has %s children - stopping\n" msgstr " има %s деца — Ñпиране\n" -#: gitk:9200 #, tcl-format msgid "Error writing commit to file: %s" msgstr "Грешка при запазването на подаването във файл: %s" -#: gitk:9206 #, tcl-format msgid "Error diffing commits: %s" msgstr "Грешка при изчиÑлÑването на разликите между подаваниÑта: %s" -#: gitk:9252 msgid "Top" msgstr "Ðай-горе" -#: gitk:9253 msgid "From" msgstr "От" -#: gitk:9258 msgid "To" msgstr "До" -#: gitk:9282 msgid "Generate patch" msgstr "Генериране на кръпка" -#: gitk:9284 msgid "From:" msgstr "От:" -#: gitk:9293 msgid "To:" msgstr "До:" -#: gitk:9302 msgid "Reverse" msgstr "Обръщане" -#: gitk:9304 gitk:9514 msgid "Output file:" msgstr "Запазване във файла:" -#: gitk:9310 msgid "Generate" msgstr "Генериране" -#: gitk:9348 msgid "Error creating patch:" msgstr "Грешка при Ñъздаването на кръпка:" -#: gitk:9371 gitk:9502 gitk:9590 msgid "ID:" msgstr "Идентификатор:" -#: gitk:9380 msgid "Tag name:" msgstr "Име на етикет:" -#: gitk:9383 msgid "Tag message is optional" msgstr "Съобщението за етикет е незадължително" -#: gitk:9385 msgid "Tag message:" msgstr "Съобщение за етикет:" -#: gitk:9389 gitk:9560 msgid "Create" msgstr "Създаване" -#: gitk:9407 msgid "No tag name specified" msgstr "ЛипÑва име на етикет" -#: gitk:9411 #, tcl-format msgid "Tag \"%s\" already exists" msgstr "Етикетът „%s“ вече ÑъщеÑтвува" -#: gitk:9421 msgid "Error creating tag:" msgstr "Грешка при Ñъздаването на етикет:" -#: gitk:9511 msgid "Command:" msgstr "Команда:" -#: gitk:9519 msgid "Write" msgstr "Запазване" -#: gitk:9537 msgid "Error writing commit:" msgstr "Грешка при запазването на подаването:" -#: gitk:9559 msgid "Create branch" msgstr "Създаване на клон" -#: gitk:9575 #, tcl-format msgid "Rename branch %s" msgstr "Преименуване на клона „%s“" -#: gitk:9576 msgid "Rename" msgstr "Преименуване" -#: gitk:9600 msgid "Name:" msgstr "Име:" -#: gitk:9624 msgid "Please specify a name for the new branch" msgstr "Укажете име за Ð½Ð¾Ð²Ð¸Ñ ÐºÐ»Ð¾Ð½" -#: gitk:9629 #, tcl-format msgid "Branch '%s' already exists. Overwrite?" msgstr "Клонът „%s“ вече ÑъщеÑтвува. Да Ñе презапише ли?" -#: gitk:9673 msgid "Please specify a new name for the branch" msgstr "Укажете ново име за клона" -#: gitk:9736 #, tcl-format msgid "Commit %s is already included in branch %s -- really re-apply it?" msgstr "" "Подаването „%s“ вече е включено в клона „%s“ — да Ñе приложи ли отново?" -#: gitk:9741 msgid "Cherry-picking" msgstr "Отбиране" -#: gitk:9750 #, tcl-format msgid "" "Cherry-pick failed because of local changes to file '%s'.\n" @@ -1085,7 +839,6 @@ msgstr "" "ÐеуÑпешно отбиране, защото във файла „%s“ има локални промени.\n" "Подайте, занулете или ги Ñкатайте и пробвайте отново." -#: gitk:9756 msgid "" "Cherry-pick failed because of merge conflict.\n" "Do you wish to run git citool to resolve it?" @@ -1093,20 +846,16 @@ msgstr "" "ÐеуÑпешно отбиране поради конфликти при Ñливане.\n" "ИÑкате ли да ги коригирате чрез „git citool“?" -#: gitk:9772 gitk:9830 msgid "No changes committed" msgstr "Ðе Ñа подадени промени" -#: gitk:9799 #, tcl-format msgid "Commit %s is not included in branch %s -- really revert it?" msgstr "Подаването „%s“ не е включено в клона „%s“. Да Ñе отменени ли?" -#: gitk:9804 msgid "Reverting" msgstr "ОтмÑна" -#: gitk:9812 #, tcl-format msgid "" "Revert failed because of local changes to the following files:%s Please " @@ -1115,7 +864,6 @@ msgstr "" "ÐеуÑпешна отмÑна, защото във файла „%s“ има локални промени.\n" "Подайте, занулете или ги Ñкатайте и пробвайте отново." -#: gitk:9816 msgid "" "Revert failed because of merge conflict.\n" " Do you wish to run git citool to resolve it?" @@ -1123,28 +871,22 @@ msgstr "" "ÐеуÑпешно отмÑна поради конфликти при Ñливане.\n" "ИÑкате ли да ги коригирате чрез „git citool“?" -#: gitk:9859 msgid "Confirm reset" msgstr "Потвърждаване на занулÑването" -#: gitk:9861 #, tcl-format msgid "Reset branch %s to %s?" msgstr "Да Ñе занули ли клонът „%s“ към „%s“?" -#: gitk:9863 msgid "Reset type:" msgstr "Вид занулÑване:" -#: gitk:9866 msgid "Soft: Leave working tree and index untouched" msgstr "Слабо: работното дърво и индекÑа оÑтават Ñъщите" -#: gitk:9869 msgid "Mixed: Leave working tree untouched, reset index" msgstr "СмеÑено: работното дърво оÑтава Ñъщото, индекÑÑŠÑ‚ Ñе занулÑва" -#: gitk:9872 msgid "" "Hard: Reset working tree and index\n" "(discard ALL local changes)" @@ -1152,24 +894,19 @@ msgstr "" "Силно: занулÑване и на работното дърво, и на индекÑа\n" "(ВСИЧКИ локални промени ще Ñе загубÑÑ‚ безвъзвратно)" -#: gitk:9889 msgid "Resetting" msgstr "ЗанулÑване" -#: gitk:9962 #, tcl-format msgid "A local branch named %s exists already" msgstr "Вече ÑъщеÑтвува локален клон „%s“." -#: gitk:9970 msgid "Checking out" msgstr "ИзтеглÑне" -#: gitk:10029 msgid "Cannot delete the currently checked-out branch" msgstr "Текущо изтеглениÑÑ‚ клон не може да Ñе изтрие" -#: gitk:10035 #, tcl-format msgid "" "The commits on branch %s aren't on any other branch.\n" @@ -1178,16 +915,16 @@ msgstr "" "ПодаваниÑта на клона „%s“ не Ñа на никой друг клон.\n" "ÐаиÑтина ли иÑкате да изтриете клона „%s“?" -#: gitk:10066 #, tcl-format msgid "Tags and heads: %s" msgstr "Етикети и върхове: %s" -#: gitk:10083 msgid "Filter" msgstr "Филтриране" -#: gitk:10390 +msgid "Sort refs by type" +msgstr "Подредба на указателите по вид" + msgid "" "Error reading commit topology information; branch and preceding/following " "tag information will be incomplete." @@ -1195,253 +932,167 @@ msgstr "" "Грешка при прочитането на топологиÑта на подаваниÑта. ИнформациÑта за клона " "и предшеÑтващите/Ñледващите етикети ще е непълна." -#: gitk:11367 msgid "Tag" msgstr "Етикет" -#: gitk:11371 msgid "Id" msgstr "Идентификатор" -#: gitk:11454 -msgid "Gitk font chooser" -msgstr "Избор на шрифт за Gitk" - -#: gitk:11471 -msgid "B" -msgstr "Ч" - -#: gitk:11474 -msgid "I" -msgstr "К" - -#: gitk:11593 msgid "Commit list display options" msgstr "ÐаÑтройки на ÑпиÑъка Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ" -#: gitk:11596 msgid "Maximum graph width (lines)" msgstr "МакÑимална широчина на графа (в редове)" -#: gitk:11600 #, no-tcl-format msgid "Maximum graph width (% of pane)" msgstr "МакÑимална широчина на графа (% от панела)" -#: gitk:11603 msgid "Show local changes" msgstr "Показване на локалните промени" -#: gitk:11606 msgid "Hide remote refs" msgstr "Скриване на отдалечените указатели" -#: gitk:11610 msgid "Copy commit ID to clipboard" msgstr "Копиране на контролната Ñума към буфера за обмен" -#: gitk:11614 msgid "Copy commit ID to X11 selection" msgstr "Копиране на контролната Ñума в ÑелекциÑта на X11" -#: gitk:11619 msgid "Length of commit ID to copy" msgstr "Дължина на контролната Ñума, коÑто Ñе копира" -#: gitk:11622 +msgid "Wheel scrolling multiplier" +msgstr "Множител за колелцето на мишката" + msgid "Diff display options" msgstr "ÐаÑтройки на показването на разликите" -#: gitk:11624 msgid "Tab spacing" msgstr "Широчина на табулатора" -#: gitk:11628 msgid "Wrap comment text" msgstr "ПренаÑÑне на думите в коментарите" -#: gitk:11633 msgid "Wrap other text" msgstr "ПренаÑÑне на Ð´Ñ€ÑƒÐ³Ð¸Ñ Ñ‚ÐµÐºÑÑ‚" -#: gitk:11638 msgid "Display nearby tags/heads" msgstr "Извеждане на близките етикети и върхове" -#: gitk:11641 msgid "Maximum # tags/heads to show" msgstr "МакÑимален брой етикети/върхове за показване" -#: gitk:11644 msgid "Limit diffs to listed paths" msgstr "Разлика Ñамо в избраните пътища" -#: gitk:11647 msgid "Support per-file encodings" msgstr "Поддръжка на различни ÐºÐ¾Ð´Ð¸Ñ€Ð°Ð½Ð¸Ñ Ð·Ð° вÑеки файл" -#: gitk:11653 gitk:11820 msgid "External diff tool" msgstr "Външен инÑтрумент за разлики" -#: gitk:11654 msgid "Choose..." msgstr "Избор…" -#: gitk:11661 msgid "Web browser" msgstr "Уеб браузър" -#: gitk:11666 -msgid "General options" -msgstr "Общи наÑтройки" - -#: gitk:11669 -msgid "Use themed widgets" -msgstr "Използване на тема за графичните обекти" - -#: gitk:11671 -msgid "(change requires restart)" -msgstr "(промÑната изиÑква реÑтартиране на Gitk)" - -#: gitk:11673 -msgid "(currently unavailable)" -msgstr "(в момента недоÑтъпно)" - -#: gitk:11685 msgid "Colors: press to choose" msgstr "Цветове: избира Ñе Ñ Ð½Ð°Ñ‚Ð¸Ñкане" -#: gitk:11688 msgid "Interface" msgstr "ИнтерфейÑ" -#: gitk:11689 msgid "interface" msgstr "интерфейÑ" -#: gitk:11692 msgid "Background" msgstr "Фон" -#: gitk:11693 gitk:11735 msgid "background" msgstr "фон" -#: gitk:11696 msgid "Foreground" msgstr "Знаци" -#: gitk:11697 msgid "foreground" msgstr "знаци" -#: gitk:11700 msgid "Diff: old lines" msgstr "Разлика: Ñтари редове" -#: gitk:11701 msgid "diff old lines" msgstr "разлика, Ñтари редове" -#: gitk:11705 msgid "Diff: old lines bg" msgstr "Разлика: фон на Ñтари редове" -#: gitk:11707 msgid "diff old lines bg" msgstr "разлика, фон на Ñтари редове" -#: gitk:11711 msgid "Diff: new lines" msgstr "Разлика: нови редове" -#: gitk:11712 msgid "diff new lines" msgstr "разлика, нови редове" -#: gitk:11716 msgid "Diff: new lines bg" msgstr "Разлика: фон на нови редове" -#: gitk:11718 msgid "diff new lines bg" msgstr "разлика, фон на нови редове" -#: gitk:11722 msgid "Diff: hunk header" msgstr "Разлика: начало на парче" -#: gitk:11724 msgid "diff hunk header" msgstr "разлика, начало на парче" -#: gitk:11728 msgid "Marked line bg" msgstr "Фон на отбелÑзан ред" -#: gitk:11730 msgid "marked line background" msgstr "фон на отбелÑзан ред" -#: gitk:11734 msgid "Select bg" msgstr "Избор на фон" -#: gitk:11743 msgid "Fonts: press to choose" msgstr "Шрифтове: избира Ñе Ñ Ð½Ð°Ñ‚Ð¸Ñкане" -#: gitk:11745 msgid "Main font" msgstr "ОÑновен шрифт" -#: gitk:11746 msgid "Diff display font" msgstr "Шрифт за разликите" -#: gitk:11747 msgid "User interface font" msgstr "Шрифт на интерфейÑа" -#: gitk:11769 msgid "Gitk preferences" msgstr "ÐаÑтройки на Gitk" -#: gitk:11778 msgid "General" msgstr "Общи" -#: gitk:11779 msgid "Colors" msgstr "Цветове" -#: gitk:11780 msgid "Fonts" msgstr "Шрифтове" -#: gitk:11830 #, tcl-format msgid "Gitk: choose color for %s" msgstr "Gitk: избор на цвÑÑ‚ на „%s“" -#: gitk:12350 -msgid "" -"Sorry, gitk cannot run with this version of Tcl/Tk.\n" -" Gitk requires at least Tcl/Tk 8.4." -msgstr "" -"Тази верÑÐ¸Ñ Ð½Ð° Tcl/Tk не Ñе поддържа от Gitk.\n" -" Ðеобходима ви е поне Tcl/Tk 8.4." - -#: gitk:12571 msgid "Cannot find a git repository here." msgstr "Тук липÑва хранилище на Git." -#: gitk:12618 #, tcl-format msgid "Ambiguous argument '%s': both revision and filename" msgstr "Ðееднозначен аргумент „%s“: има и такава верÑиÑ, и такъв файл" -#: gitk:12630 msgid "Bad arguments to gitk:" msgstr "Ðеправилни аргументи на gitk:" diff --git a/gpg-interface.c b/gpg-interface.c index 0896458de5..06e7fb5060 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -25,7 +25,7 @@ static void gpg_interface_lazy_init(void) if (done) return; done = 1; - git_config(git_gpg_config, NULL); + repo_config(the_repository, git_gpg_config, NULL); } static char *configured_signing_key; @@ -144,6 +144,18 @@ static struct gpg_format *get_format_by_sig(const char *sig) return NULL; } +const char *get_signature_format(const char *buf) +{ + struct gpg_format *format = get_format_by_sig(buf); + return format ? format->name : "unknown"; +} + +int valid_signature_format(const char *format) +{ + return (!!get_format_by_name(format) || + !strcmp(format, "unknown")); +} + void signature_check_clear(struct signature_check *sigc) { FREE_AND_NULL(sigc->payload); @@ -783,7 +795,7 @@ static int git_gpg_config(const char *var, const char *value, if (fmtname) { fmt = get_format_by_name(fmtname); - return git_config_string((char **) &fmt->program, var, value); + return git_config_pathname((char **) &fmt->program, var, value); } return 0; @@ -1048,7 +1060,7 @@ static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature, key_file->filename.buf); goto out; } - ssh_signing_key_file = strbuf_detach(&key_file->filename, NULL); + ssh_signing_key_file = xstrdup(key_file->filename.buf); } else { /* We assume a file */ ssh_signing_key_file = interpolate_path(signing_key, 1); diff --git a/gpg-interface.h b/gpg-interface.h index e09f12e8d0..60ddf8bbfa 100644 --- a/gpg-interface.h +++ b/gpg-interface.h @@ -48,6 +48,18 @@ struct signature_check { void signature_check_clear(struct signature_check *sigc); /* + * Return the format of the signature (like "openpgp", "x509", "ssh" + * or "unknown"). + */ +const char *get_signature_format(const char *buf); + +/* + * Is the signature format valid (like "openpgp", "x509", "ssh" or + * "unknown") + */ +int valid_signature_format(const char *format); + +/* * Look at a GPG signed tag object. If such a signature exists, store it in * signature and the signed content in payload. Return 1 if a signature was * found, and 0 otherwise. @@ -5,7 +5,7 @@ #include "gettext.h" #include "grep.h" #include "hex.h" -#include "object-store.h" +#include "odb.h" #include "pretty.h" #include "userdiff.h" #include "xdiff-interface.h" @@ -1931,8 +1931,8 @@ static int grep_source_load_oid(struct grep_source *gs) { enum object_type type; - gs->buf = repo_read_object_file(gs->repo, gs->identifier, &type, - &gs->size); + gs->buf = odb_read_object(gs->repo->objects, gs->identifier, + &type, &gs->size); if (!gs->buf) return error(_("'%s': unable to read %s"), gs->name, @@ -175,6 +175,16 @@ static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *s /* Number of algorithms supported (including unknown). */ #define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1) +/* Default hash algorithm if unspecified. */ +#ifdef WITH_BREAKING_CHANGES +# define GIT_HASH_DEFAULT GIT_HASH_SHA256 +#else +# define GIT_HASH_DEFAULT GIT_HASH_SHA1 +#endif + +/* Legacy hash algorithm. Implied for older data formats which don't specify. */ +#define GIT_HASH_SHA1_LEGACY GIT_HASH_SHA1 + /* "sha1", big-endian */ #define GIT_SHA1_FORMAT_ID 0x73686131 @@ -216,6 +226,7 @@ struct object_id { #define GET_OID_REQUIRE_PATH 010000 #define GET_OID_HASH_ANY 020000 #define GET_OID_SKIP_AMBIGUITY_CHECK 040000 +#define GET_OID_GENTLY 0100000 #define GET_OID_DISAMBIGUATORS \ (GET_OID_COMMIT | GET_OID_COMMITTISH | \ @@ -332,7 +332,7 @@ static int get_colopts(const char *var, const char *value, void list_commands(struct cmdnames *main_cmds, struct cmdnames *other_cmds) { unsigned int colopts = 0; - git_config(get_colopts, &colopts); + repo_config(the_repository, get_colopts, &colopts); if (main_cmds->cnt) { const char *exec_path = git_exec_path(); @@ -417,7 +417,7 @@ void list_cmds_by_config(struct string_list *list) { const char *cmd_list; - if (git_config_get_string_tmp("completion.commands", &cmd_list)) + if (repo_config_get_string_tmp(the_repository, "completion.commands", &cmd_list)) return; string_list_sort(list); @@ -502,7 +502,7 @@ static void list_all_cmds_help_aliases(int longest) struct cmdname_help *aliases; int i; - git_config(get_alias, &alias_list); + repo_config(the_repository, get_alias, &alias_list); string_list_sort(&alias_list); for (i = 0; i < alias_list.nr; i++) { @@ -810,6 +810,9 @@ void get_version_info(struct strbuf *buf, int show_build_options) SHA1_UNSAFE_BACKEND); #endif strbuf_addf(buf, "SHA-256: %s\n", SHA256_BACKEND); + strbuf_addf(buf, "default-ref-format: %s\n", + ref_storage_format_to_name(REF_STORAGE_FORMAT_DEFAULT)); + strbuf_addf(buf, "default-hash: %s\n", hash_algos[GIT_HASH_DEFAULT].name); } } diff --git a/http-backend.c b/http-backend.c index 0c575aa88a..d5dfe762bb 100644 --- a/http-backend.c +++ b/http-backend.c @@ -18,7 +18,7 @@ #include "url.h" #include "strvec.h" #include "packfile.h" -#include "object-store.h" +#include "odb.h" #include "protocol.h" #include "date.h" #include "write-or-die.h" @@ -246,13 +246,13 @@ static void http_config(void) int i, value = 0; struct strbuf var = STRBUF_INIT; - git_config_get_bool("http.getanyfile", &getanyfile); - git_config_get_ulong("http.maxrequestbuffer", &max_request_buffer); + repo_config_get_bool(the_repository, "http.getanyfile", &getanyfile); + repo_config_get_ulong(the_repository, "http.maxrequestbuffer", &max_request_buffer); for (i = 0; i < ARRAY_SIZE(rpc_service); i++) { struct rpc_service *svc = &rpc_service[i]; strbuf_addf(&var, "http.%s", svc->config_name); - if (!git_config_get_bool(var.buf, &value)) + if (!repo_config_get_bool(the_repository, var.buf, &value)) svc->enabled = value; strbuf_reset(&var); } diff --git a/http-fetch.c b/http-fetch.c index 02ab80533f..1922e23fcd 100644 --- a/http-fetch.c +++ b/http-fetch.c @@ -2,6 +2,7 @@ #include "git-compat-util.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "hex.h" #include "http.h" @@ -150,7 +151,7 @@ int cmd_main(int argc, const char **argv) trace2_cmd_name("http-fetch"); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); if (packfile) { if (!index_pack_args.nr) diff --git a/http-push.c b/http-push.c index f5a92529a8..91a5465afb 100644 --- a/http-push.c +++ b/http-push.c @@ -20,7 +20,7 @@ #include "url.h" #include "packfile.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "commit-reach.h" #ifdef EXPAT_NEEDS_XMLPARSE_H @@ -369,8 +369,8 @@ static void start_put(struct transfer_request *request) ssize_t size; git_zstream stream; - unpacked = repo_read_object_file(the_repository, &request->obj->oid, - &type, &len); + unpacked = odb_read_object(the_repository->objects, &request->obj->oid, + &type, &len); hdrlen = format_object_header(hdr, sizeof(hdr), type, len); /* Set it up */ @@ -1447,8 +1447,8 @@ static void one_remote_ref(const char *refname) * may be required for updating server info later. */ if (repo->can_update_info_refs && - !has_object(the_repository, &ref->old_oid, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) { + !odb_has_object(the_repository->objects, &ref->old_oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) { obj = lookup_unknown_object(the_repository, &ref->old_oid); fprintf(stderr, " fetch %s for %s\n", oid_to_hex(&ref->old_oid), refname); @@ -1653,14 +1653,16 @@ static int delete_remote_branch(const char *pattern, int force) return error("Remote HEAD symrefs too deep"); if (is_null_oid(&head_oid)) return error("Unable to resolve remote HEAD"); - if (!has_object(the_repository, &head_oid, HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) + if (!odb_has_object(the_repository->objects, &head_oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) return error("Remote HEAD resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", oid_to_hex(&head_oid)); /* Remote branch must resolve to a known object */ if (is_null_oid(&remote_ref->old_oid)) return error("Unable to resolve remote branch %s", remote_ref->name); - if (!has_object(the_repository, &remote_ref->old_oid, HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) + if (!odb_has_object(the_repository->objects, &remote_ref->old_oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) return error("Remote branch %s resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", remote_ref->name, oid_to_hex(&remote_ref->old_oid)); /* Remote branch must be an ancestor of remote HEAD */ @@ -1881,8 +1883,8 @@ int cmd_main(int argc, const char **argv) if (!force_all && !is_null_oid(&ref->old_oid) && !ref->force) { - if (!has_object(the_repository, &ref->old_oid, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) || + if (!odb_has_object(the_repository->objects, &ref->old_oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) || !ref_newer(&ref->peer_ref->new_oid, &ref->old_oid)) { /* diff --git a/http-walker.c b/http-walker.c index 463f7b119a..0f7ae46d7f 100644 --- a/http-walker.c +++ b/http-walker.c @@ -10,7 +10,7 @@ #include "transport.h" #include "packfile.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" struct alt_base { char *base; @@ -138,8 +138,8 @@ static int fill_active_slot(void *data UNUSED) list_for_each_safe(pos, tmp, head) { obj_req = list_entry(pos, struct object_request, node); if (obj_req->state == WAITING) { - if (has_object(the_repository, &obj_req->oid, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) + if (odb_has_object(the_repository->objects, &obj_req->oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) obj_req->state = COMPLETE; else { start_object_request(obj_req); @@ -497,8 +497,8 @@ static int fetch_object(struct walker *walker, const struct object_id *oid) if (!obj_req) return error("Couldn't find request for %s in the queue", hex); - if (has_object(the_repository, &obj_req->oid, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) { + if (odb_has_object(the_repository->objects, &obj_req->oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) { if (obj_req->req) abort_http_object_request(&obj_req->req); abort_object_request(obj_req); @@ -543,7 +543,7 @@ static int fetch_object(struct walker *walker, const struct object_id *oid) ret = error("File %s has bad hash", hex); } else if (req->rename < 0) { struct strbuf buf = STRBUF_INIT; - odb_loose_path(the_repository->objects->odb, &buf, &req->oid); + odb_loose_path(the_repository->objects->sources, &buf, &req->oid); ret = error("unable to write sha1 filename %s", buf.buf); strbuf_release(&buf); } @@ -3,6 +3,7 @@ #include "git-compat-util.h" #include "git-curl-compat.h" +#include "environment.h" #include "hex.h" #include "http.h" #include "config.h" @@ -19,7 +20,7 @@ #include "packfile.h" #include "string-list.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "tempfile.h" static struct trace_key trace_curl = TRACE_KEY_INIT(CURL); @@ -1315,7 +1316,7 @@ void http_init(struct remote *remote, const char *url, int proactive_auth) http_is_verbose = 0; normalized_url = url_normalize(url, &config.url); - git_config(urlmatch_config_entry, &config); + repo_config(the_repository, urlmatch_config_entry, &config); free(normalized_url); string_list_clear(&config.vars, 1); @@ -1347,6 +1348,14 @@ void http_init(struct remote *remote, const char *url, int proactive_auth) if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) die("curl_global_init failed"); +#ifdef GIT_CURL_HAVE_GLOBAL_TRACE + { + const char *comp = getenv("GIT_TRACE_CURL_COMPONENTS"); + if (comp) + curl_global_trace(comp); + } +#endif + if (proactive_auth && http_proactive_auth == PROACTIVE_AUTH_NONE) http_proactive_auth = PROACTIVE_AUTH_IF_CREDENTIALS; @@ -2331,7 +2340,7 @@ int http_get_file(const char *url, const char *filename, ret = http_request_reauth(url, result, HTTP_REQUEST_FILE, options); fclose(result); - if (ret == HTTP_OK && finalize_object_file(tmpfile.buf, filename)) + if (ret == HTTP_OK && finalize_object_file(the_repository, tmpfile.buf, filename)) ret = HTTP_ERROR; cleanup: strbuf_release(&tmpfile); @@ -2662,7 +2671,7 @@ struct http_object_request *new_http_object_request(const char *base_url, oidcpy(&freq->oid, oid); freq->localfile = -1; - odb_loose_path(the_repository->objects->odb, &filename, oid); + odb_loose_path(the_repository->objects->sources, &filename, oid); strbuf_addf(&freq->tmpfile, "%s.temp", filename.buf); strbuf_addf(&prevfile, "%s.prev", filename.buf); @@ -2814,8 +2823,8 @@ int finish_http_object_request(struct http_object_request *freq) unlink_or_warn(freq->tmpfile.buf); return -1; } - odb_loose_path(the_repository->objects->odb, &filename, &freq->oid); - freq->rename = finalize_object_file(freq->tmpfile.buf, filename.buf); + odb_loose_path(the_repository->objects->sources, &filename, &freq->oid); + freq->rename = finalize_object_file(the_repository, freq->tmpfile.buf, filename.buf); strbuf_release(&filename); return freq->rename; @@ -272,7 +272,7 @@ static void strbuf_addstr_without_crud(struct strbuf *sb, const char *src) * can still be NULL if the input line only has the name/email part * (e.g. reading from a reflog entry). */ -int split_ident_line(struct ident_split *split, const char *line, int len) +int split_ident_line(struct ident_split *split, const char *line, size_t len) { const char *cp; size_t span; @@ -412,6 +412,10 @@ void apply_mailmap_to_header(struct strbuf *buf, const char **header, found_header = 1; buf_offset += endp - line; buf_offset += rewrite_ident_line(person, endp - person, buf, mailmap); + /* Recompute endp after potential buffer reallocation */ + endp = buf->buf + buf_offset; + if (*endp == '\n') + buf_offset++; break; } @@ -35,7 +35,7 @@ void reset_ident_date(void); * Signals an success with 0, but time part of the result may be NULL * if the input lacks timestamp and zone */ -int split_ident_line(struct ident_split *, const char *, int); +int split_ident_line(struct ident_split *, const char *, size_t); /* * Given a commit or tag object buffer and the commit or tag headers, replaces diff --git a/imap-send.c b/imap-send.c index 2e812f5a6e..4bd5b8aa0d 100644 --- a/imap-send.c +++ b/imap-send.c @@ -25,8 +25,10 @@ #define DISABLE_SIGN_COMPARE_WARNINGS #include "git-compat-util.h" +#include "advice.h" #include "config.h" #include "credential.h" +#include "environment.h" #include "gettext.h" #include "run-command.h" #include "parse-options.h" @@ -45,13 +47,21 @@ #endif static int verbosity; +static int list_folders; static int use_curl = USE_CURL_DEFAULT; +static char *opt_folder; -static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL }; +static char const * const imap_send_usage[] = { + N_("git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>"), + "git imap-send --list", + NULL +}; static struct option imap_send_options[] = { OPT__VERBOSITY(&verbosity), OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"), + OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"), + OPT_BOOL(0, "list", &list_folders, "list all folders on the IMAP server"), OPT_END() }; @@ -139,7 +149,10 @@ enum CAPABILITY { LITERALPLUS, NAMESPACE, STARTTLS, - AUTH_CRAM_MD5 + AUTH_PLAIN, + AUTH_CRAM_MD5, + AUTH_OAUTHBEARER, + AUTH_XOAUTH2, }; static const char *cap_list[] = { @@ -148,7 +161,10 @@ static const char *cap_list[] = { "LITERAL+", "NAMESPACE", "STARTTLS", + "AUTH=PLAIN", "AUTH=CRAM-MD5", + "AUTH=OAUTHBEARER", + "AUTH=XOAUTH2", }; #define RESP_OK 0 @@ -197,7 +213,7 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED, const struct imap_server_conf *cfg UNUSED, int use_tls_only UNUSED) { - fprintf(stderr, "SSL requested but SSL support not compiled in\n"); + fprintf(stderr, "SSL requested, but SSL support is not compiled in\n"); return -1; } @@ -421,7 +437,7 @@ static int buffer_gets(struct imap_buffer *b, char **s) if (b->buf[b->offset + 1] == '\n') { b->buf[b->offset] = 0; /* terminate the string */ b->offset += 2; /* next line */ - if (0 < verbosity) + if ((0 < verbosity) || (list_folders && strstr(*s, "* LIST"))) puts(*s); return 0; } @@ -847,6 +863,38 @@ static char hexchar(unsigned int b) } #define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3)) +static char *plain_base64(const char *user, const char *pass) +{ + struct strbuf raw = STRBUF_INIT; + int b64_len; + char *b64; + + /* + * Compose the PLAIN string + * + * The username and password are combined to one string and base64 encoded. + * "\0user\0pass" + * + * The method has been described in RFC4616. + * + * https://datatracker.ietf.org/doc/html/rfc4616 + */ + strbuf_addch(&raw, '\0'); + strbuf_addstr(&raw, user); + strbuf_addch(&raw, '\0'); + strbuf_addstr(&raw, pass); + + b64 = xmallocz(ENCODED_SIZE(raw.len)); + b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw.buf, raw.len); + strbuf_release(&raw); + + if (b64_len < 0) { + free(b64); + return NULL; + } + return b64; +} + static char *cram(const char *challenge_64, const char *user, const char *pass) { int i, resp_len, encoded_len, decoded_len; @@ -885,17 +933,83 @@ static char *cram(const char *challenge_64, const char *user, const char *pass) return (char *)response_64; } -#else +static char *oauthbearer_base64(const char *user, const char *access_token) +{ + int b64_len; + char *raw, *b64; -static char *cram(const char *challenge_64 UNUSED, - const char *user UNUSED, - const char *pass UNUSED) + /* + * Compose the OAUTHBEARER string + * + * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A + * + * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801. + * * gs2-cb-flag `n` -> client does not support CB + * * gs2-authzid `a=" {User} "` + * + * The second part are key value pairs containing host, port and auth as + * described in RFC7628. + * + * https://datatracker.ietf.org/doc/html/rfc5801 + * https://datatracker.ietf.org/doc/html/rfc7628 + */ + raw = xstrfmt("n,a=%s,\001auth=Bearer %s\001\001", user, access_token); + + /* Base64 encode */ + b64 = xmallocz(ENCODED_SIZE(strlen(raw))); + b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw)); + free(raw); + + if (b64_len < 0) { + free(b64); + return NULL; + } + return b64; +} + +static char *xoauth2_base64(const char *user, const char *access_token) { - die("If you want to use CRAM-MD5 authenticate method, " - "you have to build git-imap-send with OpenSSL library."); + int b64_len; + char *raw, *b64; + + /* + * Compose the XOAUTH2 string + * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A" + * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response + */ + raw = xstrfmt("user=%s\001auth=Bearer %s\001\001", user, access_token); + + /* Base64 encode */ + b64 = xmallocz(ENCODED_SIZE(strlen(raw))); + b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw)); + free(raw); + + if (b64_len < 0) { + free(b64); + return NULL; + } + return b64; } -#endif +static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED) +{ + int ret; + char *b64; + + b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass); + if (!b64) + return error("PLAIN: base64 encoding failed"); + + /* Send the base64-encoded response */ + ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64)); + if (ret != (int)strlen(b64)) { + free(b64); + return error("IMAP error: sending PLAIN response failed"); + } + + free(b64); + return 0; +} static int auth_cram_md5(struct imap_store *ctx, const char *prompt) { @@ -905,21 +1019,72 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt) response = cram(prompt, ctx->cfg->user, ctx->cfg->pass); ret = socket_write(&ctx->imap->buf.sock, response, strlen(response)); - if (ret != strlen(response)) - return error("IMAP error: sending response failed"); + if (ret != strlen(response)) { + free(response); + return error("IMAP error: sending CRAM-MD5 response failed"); + } free(response); return 0; } +static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED) +{ + int ret; + char *b64; + + b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass); + if (!b64) + return error("OAUTHBEARER: base64 encoding failed"); + + /* Send the base64-encoded response */ + ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64)); + if (ret != (int)strlen(b64)) { + free(b64); + return error("IMAP error: sending OAUTHBEARER response failed"); + } + + free(b64); + return 0; +} + +static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED) +{ + int ret; + char *b64; + + b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass); + if (!b64) + return error("XOAUTH2: base64 encoding failed"); + + /* Send the base64-encoded response */ + ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64)); + if (ret != (int)strlen(b64)) { + free(b64); + return error("IMAP error: sending XOAUTH2 response failed"); + } + + free(b64); + return 0; +} + +#else + +#define auth_plain NULL +#define auth_cram_md5 NULL +#define auth_oauthbearer NULL +#define auth_xoauth2 NULL + +#endif + static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred) { if (srvc->user && srvc->pass) return; cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap"); - cred->host = xstrdup(srvc->host); + cred->host = xstrfmt("%s:%d", srvc->host, srvc->port); cred->username = xstrdup_or_null(srvc->user); cred->password = xstrdup_or_null(srvc->pass); @@ -932,6 +1097,38 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent srvc->pass = xstrdup(cred->password); } +static int try_auth_method(struct imap_server_conf *srvc, + struct imap_store *ctx, + struct imap *imap, + const char *auth_method, + enum CAPABILITY cap, + int (*fn)(struct imap_store *, const char *)) +{ + struct imap_cmd_cb cb = {0}; + + if (!CAP(cap)) { + fprintf(stderr, "You specified " + "%s as authentication method, " + "but %s doesn't support it.\n", + auth_method, srvc->host); + return -1; + } + cb.cont = fn; + + if (NOT_CONSTANT(!cb.cont)) { + fprintf(stderr, "If you want to use %s authentication mechanism, " + "you have to build git-imap-send with OpenSSL library.", + auth_method); + return -1; + } + if (imap_exec(ctx, &cb, "AUTHENTICATE %s", auth_method) != RESP_OK) { + fprintf(stderr, "IMAP error: AUTHENTICATE %s failed\n", + auth_method); + return -1; + } + return 0; +} + static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const char *folder) { struct credential cred = CREDENTIAL_INIT; @@ -964,7 +1161,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c imap->buf.sock.fd[0] = tunnel.out; imap->buf.sock.fd[1] = tunnel.in; - imap_info("ok\n"); + imap_info("OK\n"); } else { #ifndef NO_IPV6 struct addrinfo hints, *ai0, *ai; @@ -983,7 +1180,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai)); goto bail; } - imap_info("ok\n"); + imap_info("OK\n"); for (ai0 = ai; ai; ai = ai->ai_next) { char addr[NI_MAXHOST]; @@ -1021,7 +1218,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c perror("gethostbyname"); goto bail; } - imap_info("ok\n"); + imap_info("OK\n"); addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]); @@ -1035,7 +1232,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c } #endif if (s < 0) { - fputs("Error: unable to connect to server.\n", stderr); + fputs("error: unable to connect to server\n", stderr); goto bail; } @@ -1047,7 +1244,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c close(s); goto bail; } - imap_info("ok\n"); + imap_info("OK\n"); } /* read the greeting string */ @@ -1087,30 +1284,25 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c server_fill_credential(srvc, &cred); if (srvc->auth_method) { - struct imap_cmd_cb cb; - - if (!strcmp(srvc->auth_method, "CRAM-MD5")) { - if (!CAP(AUTH_CRAM_MD5)) { - fprintf(stderr, "You specified " - "CRAM-MD5 as authentication method, " - "but %s doesn't support it.\n", srvc->host); + if (!strcmp(srvc->auth_method, "PLAIN")) { + if (try_auth_method(srvc, ctx, imap, "PLAIN", AUTH_PLAIN, auth_plain)) goto bail; - } - /* CRAM-MD5 */ - - memset(&cb, 0, sizeof(cb)); - cb.cont = auth_cram_md5; - if (imap_exec(ctx, &cb, "AUTHENTICATE CRAM-MD5") != RESP_OK) { - fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n"); + } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) { + if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5)) + goto bail; + } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) { + if (try_auth_method(srvc, ctx, imap, "OAUTHBEARER", AUTH_OAUTHBEARER, auth_oauthbearer)) + goto bail; + } else if (!strcmp(srvc->auth_method, "XOAUTH2")) { + if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2)) goto bail; - } } else { - fprintf(stderr, "Unknown authentication method:%s\n", srvc->host); + fprintf(stderr, "unknown authentication mechanism: %s\n", srvc->auth_method); goto bail; } } else { if (CAP(NOLOGIN)) { - fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n", + fprintf(stderr, "skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host); goto bail; } @@ -1250,14 +1442,24 @@ static int count_messages(struct strbuf *all_msgs) while (1) { if (starts_with(p, "From ")) { - p = strstr(p+5, "\nFrom: "); - if (!p) break; - p = strstr(p+7, "\nDate: "); - if (!p) break; - p = strstr(p+7, "\nSubject: "); - if (!p) break; - p += 10; - count++; + if (starts_with(p, "From git-send-email")) { + p = strstr(p+5, "\nFrom: "); + if (!p) break; + p += 7; + p = strstr(p, "\nTo: "); + if (!p) break; + p += 5; + count++; + } else { + p = strstr(p+5, "\nFrom: "); + if (!p) break; + p = strstr(p+7, "\nDate: "); + if (!p) break; + p = strstr(p+7, "\nSubject: "); + if (!p) break; + p += 10; + count++; + } } p = strstr(p+5, "\nFrom "); if (!p) @@ -1316,16 +1518,16 @@ static int git_imap_config(const char *var, const char *val, FREE_AND_NULL(cfg->folder); return git_config_string(&cfg->folder, var, val); } else if (!strcmp("imap.user", var)) { - FREE_AND_NULL(cfg->folder); + FREE_AND_NULL(cfg->user); return git_config_string(&cfg->user, var, val); } else if (!strcmp("imap.pass", var)) { - FREE_AND_NULL(cfg->folder); + FREE_AND_NULL(cfg->pass); return git_config_string(&cfg->pass, var, val); } else if (!strcmp("imap.tunnel", var)) { - FREE_AND_NULL(cfg->folder); + FREE_AND_NULL(cfg->tunnel); return git_config_string(&cfg->tunnel, var, val); } else if (!strcmp("imap.authmethod", var)) { - FREE_AND_NULL(cfg->folder); + FREE_AND_NULL(cfg->auth_method); return git_config_string(&cfg->auth_method, var, val); } else if (!strcmp("imap.port", var)) { cfg->port = git_config_int(var, val, ctx->kvi); @@ -1366,7 +1568,8 @@ static int append_msgs_to_imap(struct imap_server_conf *server, } ctx->name = server->folder; - fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : ""); + fprintf(stderr, "Sending %d message%s to %s folder...\n", + total, (total != 1) ? "s" : "", server->folder); while (1) { unsigned percent = n * 100 / total; @@ -1388,6 +1591,26 @@ static int append_msgs_to_imap(struct imap_server_conf *server, return 0; } +static int list_imap_folders(struct imap_server_conf *server) +{ + struct imap_store *ctx = imap_open_store(server, "INBOX"); + if (!ctx) { + fprintf(stderr, "failed to connect to IMAP server\n"); + return 1; + } + + fprintf(stderr, "Fetching the list of available folders...\n"); + /* Issue the LIST command and print the results */ + if (imap_exec(ctx, NULL, "LIST \"\" \"*\"") != RESP_OK) { + fprintf(stderr, "failed to list folders\n"); + imap_close_store(ctx); + return 1; + } + + imap_close_store(ctx); + return 0; +} + #ifdef USE_CURL_FOR_IMAP_SEND static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred) { @@ -1405,29 +1628,51 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred) server_fill_credential(srvc, cred); curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user); - curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass); + + /* + * Use CURLOPT_PASSWORD irrespective of whether there is + * an auth method specified or not, unless it's OAuth2.0, + * where we use CURLOPT_XOAUTH2_BEARER. + */ + if (!srvc->auth_method || + (strcmp(srvc->auth_method, "XOAUTH2") && + strcmp(srvc->auth_method, "OAUTHBEARER"))) + curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass); strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://"); strbuf_addstr(&path, srvc->host); if (!path.len || path.buf[path.len - 1] != '/') strbuf_addch(&path, '/'); - uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0); - if (!uri_encoded_folder) - die("failed to encode server folder"); - strbuf_addstr(&path, uri_encoded_folder); - curl_free(uri_encoded_folder); + if (!list_folders) { + uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0); + if (!uri_encoded_folder) + die("failed to encode server folder"); + strbuf_addstr(&path, uri_encoded_folder); + curl_free(uri_encoded_folder); + } curl_easy_setopt(curl, CURLOPT_URL, path.buf); strbuf_release(&path); curl_easy_setopt(curl, CURLOPT_PORT, (long)srvc->port); if (srvc->auth_method) { - struct strbuf auth = STRBUF_INIT; - strbuf_addstr(&auth, "AUTH="); - strbuf_addstr(&auth, srvc->auth_method); - curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf); - strbuf_release(&auth); + if (!strcmp(srvc->auth_method, "XOAUTH2") || + !strcmp(srvc->auth_method, "OAUTHBEARER")) { + + /* + * While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2, + * upon debugging, it has been found that it is capable of detecting + * the best option out of OAUTHBEARER and XOAUTH2. + */ + curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass); + } else { + struct strbuf auth = STRBUF_INIT; + strbuf_addstr(&auth, "AUTH="); + strbuf_addstr(&auth, srvc->auth_method); + curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf); + strbuf_release(&auth); + } } if (!srvc->use_ssl) @@ -1436,10 +1681,6 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred) curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, (long)srvc->ssl_verify); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, (long)srvc->ssl_verify); - curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer); - - curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); - if (0 < verbosity || getenv("GIT_CURL_VERBOSE")) http_trace_curl_no_data(); setup_curl_trace(curl); @@ -1458,9 +1699,14 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server, struct credential cred = CREDENTIAL_INIT; curl = setup_curl(server, &cred); + + curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer); + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); + curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf); - fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : ""); + fprintf(stderr, "Sending %d message%s to %s folder...\n", + total, (total != 1) ? "s" : "", server->folder); while (1) { unsigned percent = n * 100 / total; int prev_len; @@ -1503,6 +1749,31 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server, return res != CURLE_OK; } + +static int curl_list_imap_folders(struct imap_server_conf *server) +{ + CURL *curl; + CURLcode res = CURLE_OK; + struct credential cred = CREDENTIAL_INIT; + + fprintf(stderr, "Fetching the list of available folders...\n"); + curl = setup_curl(server, &cred); + res = curl_easy_perform(curl); + + curl_easy_cleanup(curl); + curl_global_cleanup(); + + if (cred.username) { + if (res == CURLE_OK) + credential_approve(the_repository, &cred); + else if (res == CURLE_LOGIN_DENIED) + credential_reject(the_repository, &cred); + } + + credential_clear(&cred); + + return res != CURLE_OK; +} #endif int cmd_main(int argc, const char **argv) @@ -1516,10 +1787,15 @@ int cmd_main(int argc, const char **argv) int ret; setup_git_directory_gently(&nongit_ok); - git_config(git_imap_config, &server); + repo_config(the_repository, git_imap_config, &server); argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0); + if (opt_folder) { + free(server.folder); + server.folder = xstrdup(opt_folder); + } + if (argc) usage_with_options(imap_send_usage, imap_send_options); @@ -1538,20 +1814,37 @@ int cmd_main(int argc, const char **argv) if (!server.port) server.port = server.use_ssl ? 993 : 143; - if (!server.folder) { - fprintf(stderr, "no imap store specified\n"); - ret = 1; - goto out; - } if (!server.host) { if (!server.tunnel) { - fprintf(stderr, "no imap host specified\n"); + error(_("no IMAP host specified")); + advise(_("set the IMAP host with 'git config imap.host <host>'.\n" + "(e.g., 'git config imap.host imaps://imap.example.com')")); ret = 1; goto out; } server.host = xstrdup("tunnel"); } + if (list_folders) { + if (server.tunnel) + ret = list_imap_folders(&server); +#ifdef USE_CURL_FOR_IMAP_SEND + else if (use_curl) + ret = curl_list_imap_folders(&server); +#endif + else + ret = list_imap_folders(&server); + goto out; + } + + if (!server.folder) { + error(_("no IMAP folder specified")); + advise(_("set the target folder with 'git config imap.folder <folder>'.\n" + "(e.g., 'git config imap.folder Drafts')")); + ret = 1; + goto out; + } + /* read the messages */ if (strbuf_read(&all_msgs, 0, 0) < 0) { error_errno(_("could not read from stdin")); @@ -1567,7 +1860,7 @@ int cmd_main(int argc, const char **argv) total = count_messages(&all_msgs); if (!total) { - fprintf(stderr, "no messages to send\n"); + fprintf(stderr, "no messages found to send\n"); ret = 1; goto out; } diff --git a/line-log.c b/line-log.c index 628e3fe3ae..8bd422148d 100644 --- a/line-log.c +++ b/line-log.c @@ -201,7 +201,7 @@ static void range_set_difference(struct range_set *out, * b: ------| */ j++; - if (j >= b->nr || end < b->ranges[j].start) { + if (j >= b->nr || end <= b->ranges[j].start) { /* * b exhausted, or * a: ----| @@ -408,7 +408,7 @@ static void diff_ranges_filter_touched(struct diff_ranges *out, assert(out->target.nr == 0); for (i = 0; i < diff->target.nr; i++) { - while (diff->target.ranges[i].start > rs->ranges[j].end) { + while (diff->target.ranges[i].start >= rs->ranges[j].end) { j++; if (j == rs->nr) return; @@ -939,9 +939,18 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang long t_cur = t_start; unsigned int j_last; + /* + * If a diff range touches multiple line ranges, then all + * those line ranges should be shown, so take a step back if + * the current line range is still in the previous diff range + * (even if only partially). + */ + if (j > 0 && diff->target.ranges[j-1].end > t_start) + j--; + while (j < diff->target.nr && diff->target.ranges[j].end < t_start) j++; - if (j == diff->target.nr || diff->target.ranges[j].start > t_end) + if (j == diff->target.nr || diff->target.ranges[j].start >= t_end) continue; /* Scan ahead to determine the last diff that falls in this range */ @@ -1087,13 +1096,6 @@ static struct diff_filepair *diff_filepair_dup(struct diff_filepair *pair) return new_filepair; } -static void free_diffqueues(int n, struct diff_queue_struct *dq) -{ - for (int i = 0; i < n; i++) - diff_queue_clear(&dq[i]); - free(dq); -} - static int process_all_files(struct line_log_data **range_out, struct rev_info *rev, struct diff_queue_struct *queue, @@ -1172,12 +1174,13 @@ static int bloom_filter_check(struct rev_info *rev, return 0; while (!result && range) { - fill_bloom_key(range->path, strlen(range->path), &key, rev->bloom_filter_settings); + bloom_key_fill(&key, range->path, strlen(range->path), + rev->bloom_filter_settings); if (bloom_filter_contains(filter, &key, rev->bloom_filter_settings)) result = 1; - clear_bloom_key(&key); + bloom_key_clear(&key); range = range->next; } @@ -1188,7 +1191,7 @@ static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *c struct line_log_data *range) { struct commit *parent = NULL; - struct diff_queue_struct queue; + struct diff_queue_struct queue = DIFF_QUEUE_INIT; struct line_log_data *parent_range; int changed; @@ -1208,9 +1211,7 @@ static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *c static int process_ranges_merge_commit(struct rev_info *rev, struct commit *commit, struct line_log_data *range) { - struct diff_queue_struct *diffqueues; struct line_log_data **cand; - struct commit **parents; struct commit_list *p; int i; int nparents = commit_list_count(commit->parents); @@ -1219,28 +1220,27 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm if (nparents > 1 && rev->first_parent_only) nparents = 1; - ALLOC_ARRAY(diffqueues, nparents); CALLOC_ARRAY(cand, nparents); - ALLOC_ARRAY(parents, nparents); - p = commit->parents; - for (i = 0; i < nparents; i++) { - parents[i] = p->item; - p = p->next; - queue_diffs(range, &rev->diffopt, &diffqueues[i], commit, parents[i]); - } - - for (i = 0; i < nparents; i++) { + for (p = commit->parents, i = 0; + p && i < nparents; + p = p->next, i++) { + struct commit *parent = p->item; + struct diff_queue_struct diffqueue = DIFF_QUEUE_INIT; int changed; - changed = process_all_files(&cand[i], rev, &diffqueues[i], range); + + queue_diffs(range, &rev->diffopt, &diffqueue, commit, parent); + + changed = process_all_files(&cand[i], rev, &diffqueue, range); + diff_queue_clear(&diffqueue); if (!changed) { /* * This parent can take all the blame, so we * don't follow any other path in history */ - add_line_range(rev, parents[i], cand[i]); + add_line_range(rev, parent, cand[i]); free_commit_list(commit->parents); - commit_list_append(parents[i], &commit->parents); + commit_list_append(parent, &commit->parents); ret = 0; goto out; @@ -1251,14 +1251,15 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm * No single parent took the blame. We add the candidates * from the above loop to the parents. */ - for (i = 0; i < nparents; i++) - add_line_range(rev, parents[i], cand[i]); + for (p = commit->parents, i = 0; + p && i < nparents; + p = p->next, i++) + add_line_range(rev, p->item, cand[i]); ret = 1; out: clear_commit_line_range(rev, commit); - free(parents); for (i = 0; i < nparents; i++) { if (!cand[i]) continue; @@ -1266,7 +1267,6 @@ out: free(cand[i]); } free(cand); - free_diffqueues(nparents, diffqueues); return ret; /* NEEDSWORK evil merge detection stuff */ @@ -1282,10 +1282,10 @@ int line_log_process_ranges_arbitrary_commit(struct rev_info *rev, struct commit struct line_log_data *prange = line_log_data_copy(range); add_line_range(rev, commit->parents->item, prange); clear_commit_line_range(rev, commit); - } else if (!commit->parents || !commit->parents->next) - changed = process_ranges_ordinary_commit(rev, commit, range); - else + } else if (commit->parents && commit->parents->next) changed = process_ranges_merge_commit(rev, commit, range); + else + changed = process_ranges_ordinary_commit(rev, commit, range); } if (!changed) diff --git a/list-objects-filter-options.c b/list-objects-filter-options.c index 948376d42d..7420bf81fe 100644 --- a/list-objects-filter-options.c +++ b/list-objects-filter-options.c @@ -350,7 +350,7 @@ void partial_clone_register( /* Add promisor config for the remote */ cfg_name = xstrfmt("remote.%s.promisor", remote); - git_config_set(cfg_name, "true"); + repo_config_set(the_repository, cfg_name, "true"); free(cfg_name); } @@ -360,8 +360,8 @@ void partial_clone_register( */ filter_name = xstrfmt("remote.%s.partialclonefilter", remote); /* NEEDSWORK: 'expand' result leaking??? */ - git_config_set(filter_name, - expand_list_objects_filter_spec(filter_options)); + repo_config_set(the_repository, filter_name, + expand_list_objects_filter_spec(filter_options)); free(filter_name); /* Make sure the config info are reset */ diff --git a/list-objects-filter.c b/list-objects-filter.c index 78b397bc19..acd65ebb73 100644 --- a/list-objects-filter.c +++ b/list-objects-filter.c @@ -12,7 +12,7 @@ #include "oidmap.h" #include "oidset.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" /* Remember to update object flag allocation in object.h */ /* @@ -310,7 +310,7 @@ static enum list_objects_filter_result filter_blobs_limit( assert(obj->type == OBJ_BLOB); assert((obj->flags & SEEN) == 0); - t = oid_object_info(r, &obj->oid, &object_length); + t = odb_read_object_info(r->objects, &obj->oid, &object_length); if (t != OBJ_BLOB) { /* probably OBJ_NONE */ /* * We DO NOT have the blob locally, so we cannot @@ -524,12 +524,11 @@ static void filter_sparse_oid__init( struct filter *filter) { struct filter_sparse_data *d = xcalloc(1, sizeof(*d)); - struct object_context oc; struct object_id sparse_oid; - if (get_oid_with_context(the_repository, - filter_options->sparse_oid_name, - GET_OID_BLOB, &sparse_oid, &oc)) + if (repo_get_oid_with_flags(the_repository, + filter_options->sparse_oid_name, + &sparse_oid, GET_OID_BLOB)) die(_("unable to access sparse blob in '%s'"), filter_options->sparse_oid_name); if (add_patterns_from_blob_to_list(&sparse_oid, "", 0, &d->pl) < 0) @@ -544,8 +543,6 @@ static void filter_sparse_oid__init( filter->filter_data = d; filter->filter_object_fn = filter_sparse; filter->free_fn = filter_sparse_free; - - object_context_release(&oc); } /* diff --git a/list-objects.c b/list-objects.c index 597114281f..42c17d9573 100644 --- a/list-objects.c +++ b/list-objects.c @@ -14,7 +14,7 @@ #include "list-objects-filter.h" #include "list-objects-filter-options.h" #include "packfile.h" -#include "object-store.h" +#include "odb.h" #include "trace.h" #include "environment.h" @@ -74,8 +74,8 @@ static void process_blob(struct traversal_context *ctx, * of missing objects. */ if (ctx->revs->exclude_promisor_objects && - !has_object(the_repository, &obj->oid, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) && + !odb_has_object(the_repository->objects, &obj->oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) && is_promisor_object(ctx->revs->repo, &obj->oid)) return; diff --git a/log-tree.c b/log-tree.c index 1d05dc1c70..73d21f7176 100644 --- a/log-tree.c +++ b/log-tree.c @@ -176,7 +176,7 @@ static int add_ref_decoration(const char *refname, const char *referent UNUSED, return 0; } - objtype = oid_object_info(the_repository, oid, NULL); + objtype = odb_read_object_info(the_repository->objects, oid, NULL); if (objtype < 0) return 0; obj = lookup_object_by_type(the_repository, oid, objtype); @@ -717,6 +717,7 @@ static void show_diff_of_diff(struct rev_info *opt) struct range_diff_options range_diff_opts = { .creation_factor = opt->creation_factor, .dual_color = 1, + .max_memory = RANGE_DIFF_MAX_MEMORY_DEFAULT, .diffopt = &opts }; @@ -1,7 +1,7 @@ #include "git-compat-util.h" #include "hash.h" #include "path.h" -#include "object-store.h" +#include "odb.h" #include "hex.h" #include "repository.h" #include "wrapper.h" @@ -44,36 +44,36 @@ static int insert_oid_pair(kh_oid_map_t *map, const struct object_id *key, const return 1; } -static int insert_loose_map(struct object_directory *odb, +static int insert_loose_map(struct odb_source *source, const struct object_id *oid, const struct object_id *compat_oid) { - struct loose_object_map *map = odb->loose_map; + struct loose_object_map *map = source->loose_map; int inserted = 0; inserted |= insert_oid_pair(map->to_compat, oid, compat_oid); inserted |= insert_oid_pair(map->to_storage, compat_oid, oid); if (inserted) - oidtree_insert(odb->loose_objects_cache, compat_oid); + oidtree_insert(source->loose_objects_cache, compat_oid); return inserted; } -static int load_one_loose_object_map(struct repository *repo, struct object_directory *dir) +static int load_one_loose_object_map(struct repository *repo, struct odb_source *source) { struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT; FILE *fp; - if (!dir->loose_map) - loose_object_map_init(&dir->loose_map); - if (!dir->loose_objects_cache) { - ALLOC_ARRAY(dir->loose_objects_cache, 1); - oidtree_init(dir->loose_objects_cache); + if (!source->loose_map) + loose_object_map_init(&source->loose_map); + if (!source->loose_objects_cache) { + ALLOC_ARRAY(source->loose_objects_cache, 1); + oidtree_init(source->loose_objects_cache); } - insert_loose_map(dir, repo->hash_algo->empty_tree, repo->compat_hash_algo->empty_tree); - insert_loose_map(dir, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob); - insert_loose_map(dir, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid); + insert_loose_map(source, repo->hash_algo->empty_tree, repo->compat_hash_algo->empty_tree); + insert_loose_map(source, repo->hash_algo->empty_blob, repo->compat_hash_algo->empty_blob); + insert_loose_map(source, repo->hash_algo->null_oid, repo->compat_hash_algo->null_oid); repo_common_path_replace(repo, &path, "objects/loose-object-idx"); fp = fopen(path.buf, "rb"); @@ -93,7 +93,7 @@ static int load_one_loose_object_map(struct repository *repo, struct object_dire parse_oid_hex_algop(p, &compat_oid, &p, repo->compat_hash_algo) || p != buf.buf + buf.len) goto err; - insert_loose_map(dir, &oid, &compat_oid); + insert_loose_map(source, &oid, &compat_oid); } strbuf_release(&buf); @@ -107,15 +107,15 @@ err: int repo_read_loose_object_map(struct repository *repo) { - struct object_directory *dir; + struct odb_source *source; if (!should_use_loose_object_map(repo)) return 0; - prepare_alt_odb(repo); + odb_prepare_alternates(repo->objects); - for (dir = repo->objects->odb; dir; dir = dir->next) { - if (load_one_loose_object_map(repo, dir) < 0) { + for (source = repo->objects->sources; source; source = source->next) { + if (load_one_loose_object_map(repo, source) < 0) { return -1; } } @@ -124,7 +124,7 @@ int repo_read_loose_object_map(struct repository *repo) int repo_write_loose_object_map(struct repository *repo) { - kh_oid_map_t *map = repo->objects->odb->loose_map->to_compat; + kh_oid_map_t *map = repo->objects->sources->loose_map->to_compat; struct lock_file lock; int fd; khiter_t iter; @@ -166,7 +166,8 @@ errout: return -1; } -static int write_one_object(struct repository *repo, const struct object_id *oid, +static int write_one_object(struct odb_source *source, + const struct object_id *oid, const struct object_id *compat_oid) { struct lock_file lock; @@ -174,7 +175,7 @@ static int write_one_object(struct repository *repo, const struct object_id *oid struct stat st; struct strbuf buf = STRBUF_INIT, path = STRBUF_INIT; - repo_common_path_replace(repo, &path, "objects/loose-object-idx"); + strbuf_addf(&path, "%s/loose-object-idx", source->path); hold_lock_file_for_update_timeout(&lock, path.buf, LOCK_DIE_ON_ERROR, -1); fd = open(path.buf, O_WRONLY | O_CREAT | O_APPEND, 0666); @@ -190,7 +191,7 @@ static int write_one_object(struct repository *repo, const struct object_id *oid goto errout; if (close(fd)) goto errout; - adjust_shared_perm(repo, path.buf); + adjust_shared_perm(source->odb->repo, path.buf); rollback_lock_file(&lock); strbuf_release(&buf); strbuf_release(&path); @@ -204,17 +205,18 @@ errout: return -1; } -int repo_add_loose_object_map(struct repository *repo, const struct object_id *oid, +int repo_add_loose_object_map(struct odb_source *source, + const struct object_id *oid, const struct object_id *compat_oid) { int inserted = 0; - if (!should_use_loose_object_map(repo)) + if (!should_use_loose_object_map(source->odb->repo)) return 0; - inserted = insert_loose_map(repo->objects->odb, oid, compat_oid); + inserted = insert_loose_map(source, oid, compat_oid); if (inserted) - return write_one_object(repo, oid, compat_oid); + return write_one_object(source, oid, compat_oid); return 0; } @@ -223,12 +225,12 @@ int repo_loose_object_map_oid(struct repository *repo, const struct git_hash_algo *to, struct object_id *dest) { - struct object_directory *dir; + struct odb_source *source; kh_oid_map_t *map; khiter_t pos; - for (dir = repo->objects->odb; dir; dir = dir->next) { - struct loose_object_map *loose_map = dir->loose_map; + for (source = repo->objects->sources; source; source = source->next) { + struct loose_object_map *loose_map = source->loose_map; if (!loose_map) continue; map = (to == repo->compat_hash_algo) ? @@ -4,6 +4,7 @@ #include "khash.h" struct repository; +struct odb_source; struct loose_object_map { kh_oid_map_t *to_compat; @@ -16,7 +17,8 @@ int repo_loose_object_map_oid(struct repository *repo, const struct object_id *src, const struct git_hash_algo *dest_algo, struct object_id *dest); -int repo_add_loose_object_map(struct repository *repo, const struct object_id *oid, +int repo_add_loose_object_map(struct odb_source *source, + const struct object_id *oid, const struct object_id *compat_oid); int repo_read_loose_object_map(struct repository *repo); int repo_write_loose_object_map(struct repository *repo); @@ -159,7 +159,7 @@ int ls_refs(struct repository *r, struct packet_reader *request) strbuf_init(&data.buf, 0); strvec_init(&data.hidden_refs); - git_config(ls_refs_config, &data); + repo_config(the_repository, ls_refs_config, &data); while (packet_reader_read(request) == PACKET_READ_NORMAL) { const char *arg = request->line; diff --git a/mailinfo.c b/mailinfo.c index ee4597da6b..99ac596e09 100644 --- a/mailinfo.c +++ b/mailinfo.c @@ -2,6 +2,7 @@ #include "git-compat-util.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "hex-ll.h" #include "utf8.h" @@ -266,6 +267,8 @@ static void handle_content_type(struct mailinfo *mi, struct strbuf *line) error("Too many boundaries to handle"); mi->input_error = -1; mi->content_top = &mi->content[MAX_BOUNDARIES] - 1; + strbuf_release(boundary); + free(boundary); return; } *(mi->content_top) = boundary; @@ -6,7 +6,7 @@ #include "string-list.h" #include "mailmap.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "setup.h" char *git_mailmap_file; @@ -196,7 +196,7 @@ int read_mailmap_blob(struct string_list *map, const char *name) if (repo_get_oid(the_repository, name, &oid) < 0) return 0; - buf = repo_read_object_file(the_repository, &oid, &type, &size); + buf = odb_read_object(the_repository->objects, &oid, &type, &size); if (!buf) return error("unable to read mailmap object at %s", name); if (type != OBJ_BLOB) { diff --git a/match-trees.c b/match-trees.c index 72922d5d64..4216933d06 100644 --- a/match-trees.c +++ b/match-trees.c @@ -7,7 +7,7 @@ #include "tree.h" #include "tree-walk.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "repository.h" static int score_missing(unsigned mode) @@ -63,7 +63,7 @@ static void *fill_tree_desc_strict(struct repository *r, enum object_type type; unsigned long size; - buffer = repo_read_object_file(r, hash, &type, &size); + buffer = odb_read_object(r->objects, hash, &type, &size); if (!buffer) die("unable to read tree (%s)", oid_to_hex(hash)); if (type != OBJ_TREE) @@ -199,7 +199,7 @@ static int splice_tree(struct repository *r, if (*subpath) subpath++; - buf = repo_read_object_file(r, oid1, &type, &sz); + buf = odb_read_object(r->objects, oid1, &type, &sz); if (!buf) die("cannot read tree %s", oid_to_hex(oid1)); init_tree_desc(&desc, oid1, buf, sz); @@ -246,7 +246,7 @@ static int splice_tree(struct repository *r, rewrite_with = oid2; } hashcpy(rewrite_here, rewrite_with->hash, r->hash_algo); - status = write_object_file(buf, sz, OBJ_TREE, result); + status = odb_write_object(r->objects, buf, sz, OBJ_TREE, result); free(buf); return status; } diff --git a/merge-blobs.c b/merge-blobs.c index 53f36dbc17..6fc2799417 100644 --- a/merge-blobs.c +++ b/merge-blobs.c @@ -4,7 +4,7 @@ #include "merge-ll.h" #include "blob.h" #include "merge-blobs.h" -#include "object-store.h" +#include "odb.h" static int fill_mmfile_blob(mmfile_t *f, struct blob *obj) { @@ -12,8 +12,8 @@ static int fill_mmfile_blob(mmfile_t *f, struct blob *obj) unsigned long size; enum object_type type; - buf = repo_read_object_file(the_repository, &obj->object.oid, &type, - &size); + buf = odb_read_object(the_repository->objects, &obj->object.oid, + &type, &size); if (!buf) return -1; if (type != OBJ_BLOB) { @@ -79,8 +79,8 @@ void *merge_blobs(struct index_state *istate, const char *path, return NULL; if (!our) our = their; - return repo_read_object_file(the_repository, &our->object.oid, - &type, size); + return odb_read_object(the_repository->objects, &our->object.oid, + &type, size); } if (fill_mmfile_blob(&f1, our) < 0) diff --git a/merge-ll.c b/merge-ll.c index b2dc26da4f..fafe2c9197 100644 --- a/merge-ll.c +++ b/merge-ll.c @@ -357,7 +357,7 @@ static void initialize_ll_merge(void) if (ll_user_merge_tail) return; ll_user_merge_tail = &ll_user_merge; - git_config(read_merge_config, NULL); + repo_config(the_repository, read_merge_config, NULL); } static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr) diff --git a/merge-ort.c b/merge-ort.c index 47b3d1730e..29858074f9 100644 --- a/merge-ort.c +++ b/merge-ort.c @@ -39,7 +39,7 @@ #include "mem-pool.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "oid-array.h" #include "path.h" #include "promisor-remote.h" @@ -316,9 +316,14 @@ struct merge_options_internal { * (e.g. "drivers/firmware/raspberrypi.c"). * * store all relevant paths in the repo, both directories and * files (e.g. drivers, drivers/firmware would also be included) - * * these keys serve to intern all the path strings, which allows - * us to do pointer comparison on directory names instead of - * strcmp; we just have to be careful to use the interned strings. + * * these keys serve to intern *all* path strings, which allows us + * to do pointer comparisons on file & directory names instead of + * using strcmp; however, for this pointer-comparison optimization + * to work, any code path that independently computes a path needs + * to check for it existing in this strmap, and if so, point to + * the path in this strmap instead of their computed copy. See + * the "reuse known pointer" comment in + * apply_directory_rename_modifications() for an example. * * The values of paths: * * either a pointer to a merged_info, or a conflict_info struct @@ -2163,7 +2168,7 @@ static int handle_content_merge(struct merge_options *opt, /* * FIXME: If opt->priv->call_depth && !clean, then we really * should not make result->mode match either a->mode or - * b->mode; that causes t6036 "check conflicting mode for + * b->mode; that causes t6416 "check conflicting mode for * regular file" to fail. It would be best to use some other * mode, but we'll confuse all kinds of stuff if we use one * where S_ISREG(result->mode) isn't true, and if we use @@ -2216,8 +2221,8 @@ static int handle_content_merge(struct merge_options *opt, } if (!ret && record_object && - write_object_file(result_buf.ptr, result_buf.size, - OBJ_BLOB, &result->oid)) { + odb_write_object(the_repository->objects, result_buf.ptr, result_buf.size, + OBJ_BLOB, &result->oid)) { path_msg(opt, ERROR_OBJECT_WRITE_FAILED, 0, pathnames[0], pathnames[1], pathnames[2], NULL, _("error: unable to add %s to database"), path); @@ -2313,14 +2318,20 @@ static char *apply_dir_rename(struct strmap_entry *rename_info, return strbuf_detach(&new_path, NULL); } -static int path_in_way(struct strmap *paths, const char *path, unsigned side_mask) +static int path_in_way(struct strmap *paths, + const char *path, + unsigned side_mask, + struct diff_filepair *p) { struct merged_info *mi = strmap_get(paths, path); struct conflict_info *ci; if (!mi) return 0; INITIALIZE_CI(ci, mi); - return mi->clean || (side_mask & (ci->filemask | ci->dirmask)); + return mi->clean || (side_mask & (ci->filemask | ci->dirmask)) + /* See testcases 12[npq] of t6423 for this next condition */ + || ((ci->filemask & 0x01) && + strcmp(p->one->path, path)); } /* @@ -2332,6 +2343,7 @@ static int path_in_way(struct strmap *paths, const char *path, unsigned side_mas static char *handle_path_level_conflicts(struct merge_options *opt, const char *path, unsigned side_index, + struct diff_filepair *p, struct strmap_entry *rename_info, struct strmap *collisions) { @@ -2366,7 +2378,7 @@ static char *handle_path_level_conflicts(struct merge_options *opt, */ if (c_info->reported_already) { clean = 0; - } else if (path_in_way(&opt->priv->paths, new_path, 1 << side_index)) { + } else if (path_in_way(&opt->priv->paths, new_path, 1 << side_index, p)) { c_info->reported_already = 1; strbuf_add_separated_string_list(&collision_paths, ", ", &c_info->source_files); @@ -2520,7 +2532,7 @@ static void compute_collisions(struct strmap *collisions, * happening, and fall back to no-directory-rename detection * behavior for those paths. * - * See testcases 9e and all of section 5 from t6043 for examples. + * See testcases 9e and all of section 5 from t6423 for examples. */ for (i = 0; i < pairs->nr; ++i) { struct strmap_entry *rename_info; @@ -2573,6 +2585,7 @@ static void free_collisions(struct strmap *collisions) static char *check_for_directory_rename(struct merge_options *opt, const char *path, unsigned side_index, + struct diff_filepair *p, struct strmap *dir_renames, struct strmap *dir_rename_exclusions, struct strmap *collisions, @@ -2580,7 +2593,6 @@ static char *check_for_directory_rename(struct merge_options *opt, { char *new_path; struct strmap_entry *rename_info; - struct strmap_entry *otherinfo; const char *new_dir; int other_side = 3 - side_index; @@ -2615,14 +2627,13 @@ static char *check_for_directory_rename(struct merge_options *opt, * to not let Side1 do the rename to dumbdir, since we know that is * the source of one of our directory renames. * - * That's why otherinfo and dir_rename_exclusions is here. + * That's why dir_rename_exclusions is here. * * As it turns out, this also prevents N-way transient rename - * confusion; See testcases 9c and 9d of t6043. + * confusion; See testcases 9c and 9d of t6423. */ new_dir = rename_info->value; /* old_dir = rename_info->key; */ - otherinfo = strmap_get_entry(dir_rename_exclusions, new_dir); - if (otherinfo) { + if (strmap_contains(dir_rename_exclusions, new_dir)) { path_msg(opt, INFO_DIR_RENAME_SKIPPED_DUE_TO_RERENAME, 1, rename_info->key, path, new_dir, NULL, _("WARNING: Avoiding applying %s -> %s rename " @@ -2631,7 +2642,7 @@ static char *check_for_directory_rename(struct merge_options *opt, return NULL; } - new_path = handle_path_level_conflicts(opt, path, side_index, + new_path = handle_path_level_conflicts(opt, path, side_index, p, rename_info, &collisions[side_index]); *clean_merge &= (new_path != NULL); @@ -2876,6 +2887,20 @@ static int process_renames(struct merge_options *opt, } /* + * Directory renames can result in rename-to-self; the code + * below assumes we have A->B with different A & B, and tries + * to move all entries to path B. If A & B are the same path, + * the logic can get confused, so skip further processing when + * A & B are already the same path. + * + * As a reminder, we can avoid strcmp here because all paths + * are interned in opt->priv->paths; see the comment above + * "paths" in struct merge_options_internal. + */ + if (oldpath == newpath) + continue; + + /* * If pair->one->path isn't in opt->priv->paths, that means * that either directory rename detection removed that * path, or a parent directory of oldpath was resolved and @@ -3419,7 +3444,7 @@ static int collect_renames(struct merge_options *opt, } new_path = check_for_directory_rename(opt, p->two->path, - side_index, + side_index, p, dir_renames_for_side, rename_exclusions, collisions, @@ -3629,7 +3654,7 @@ static int read_oid_strbuf(struct merge_options *opt, void *buf; enum object_type type; unsigned long size; - buf = repo_read_object_file(the_repository, oid, &type, &size); + buf = odb_read_object(the_repository->objects, oid, &type, &size); if (!buf) { path_msg(opt, ERROR_OBJECT_READ_FAILED, 0, path, NULL, NULL, NULL, @@ -3772,7 +3797,8 @@ static int write_tree(struct object_id *result_oid, } /* Write this object file out, and record in result_oid */ - if (write_object_file(buf.buf, buf.len, OBJ_TREE, result_oid)) + if (odb_write_object(the_repository->objects, buf.buf, + buf.len, OBJ_TREE, result_oid)) ret = -1; strbuf_release(&buf); return ret; @@ -4385,8 +4411,8 @@ static void prefetch_for_content_merges(struct merge_options *opt, if ((ci->filemask & side_mask) && S_ISREG(vi->mode) && - oid_object_info_extended(opt->repo, &vi->oid, NULL, - OBJECT_INFO_FOR_PREFETCH)) + odb_read_object_info_extended(opt->repo->objects, &vi->oid, NULL, + OBJECT_INFO_FOR_PREFETCH)) oid_array_append(&to_fetch, &vi->oid); } } @@ -5353,20 +5379,20 @@ static void merge_recursive_config(struct merge_options *opt, int ui) { char *value = NULL; int renormalize = 0; - git_config_get_int("merge.verbosity", &opt->verbosity); - git_config_get_int("diff.renamelimit", &opt->rename_limit); - git_config_get_int("merge.renamelimit", &opt->rename_limit); - git_config_get_bool("merge.renormalize", &renormalize); + repo_config_get_int(the_repository, "merge.verbosity", &opt->verbosity); + repo_config_get_int(the_repository, "diff.renamelimit", &opt->rename_limit); + repo_config_get_int(the_repository, "merge.renamelimit", &opt->rename_limit); + repo_config_get_bool(the_repository, "merge.renormalize", &renormalize); opt->renormalize = renormalize; - if (!git_config_get_string("diff.renames", &value)) { + if (!repo_config_get_string(the_repository, "diff.renames", &value)) { opt->detect_renames = git_config_rename("diff.renames", value); free(value); } - if (!git_config_get_string("merge.renames", &value)) { + if (!repo_config_get_string(the_repository, "merge.renames", &value)) { opt->detect_renames = git_config_rename("merge.renames", value); free(value); } - if (!git_config_get_string("merge.directoryrenames", &value)) { + if (!repo_config_get_string(the_repository, "merge.directoryrenames", &value)) { int boolval = git_parse_maybe_bool(value); if (0 <= boolval) { opt->detect_directory_renames = boolval ? @@ -5379,7 +5405,7 @@ static void merge_recursive_config(struct merge_options *opt, int ui) free(value); } if (ui) { - if (!git_config_get_string("diff.algorithm", &value)) { + if (!repo_config_get_string(the_repository, "diff.algorithm", &value)) { long diff_algorithm = parse_algorithm_value(value); if (diff_algorithm < 0) die(_("unknown value for config '%s': %s"), "diff.algorithm", value); @@ -5387,7 +5413,7 @@ static void merge_recursive_config(struct merge_options *opt, int ui) free(value); } } - git_config(git_xmerge_config, NULL); + repo_config(the_repository, git_xmerge_config, NULL); } static void init_merge_options(struct merge_options *opt, diff --git a/mergetools/vimdiff b/mergetools/vimdiff index 78710858e8..fca1044f65 100644 --- a/mergetools/vimdiff +++ b/mergetools/vimdiff @@ -274,8 +274,8 @@ gen_cmd () { # definition. # # The syntax of the "layout definitions" is explained in "Documentation/ - # mergetools/vimdiff.txt" but you can already intuitively understand how - # it works by knowing that... + # mergetools/vimdiff.adoc" but you can already intuitively understand + # how it works by knowing that... # # * "+" means "a new vim tab" # * "/" means "a new vim horizontal split" diff --git a/meson.build b/meson.build index 596f5ac711..b3dfcc0497 100644 --- a/meson.build +++ b/meson.build @@ -245,7 +245,7 @@ time = find_program('time', dirs: program_path, required: get_option('benchmarks # "/bin/sh" over a PATH-based lookup, which provides a working shell on most # supported systems. This path is also the default shell path used by our # Makefile. This lookup can be overridden via `program_path`. -target_shell = find_program('sh', dirs: program_path + [ '/bin' ], native: false) +target_shell = find_program('/bin/sh', 'sh', dirs: program_path, native: false) # Sanity-check that programs required for the build exist. foreach tool : ['cat', 'cut', 'grep', 'sort', 'tr', 'uname'] @@ -396,8 +396,8 @@ libgit_sources = [ 'object-file-convert.c', 'object-file.c', 'object-name.c', - 'object-store.c', 'object.c', + 'odb.c', 'oid-array.c', 'oidmap.c', 'oidset.c', @@ -607,6 +607,7 @@ builtin_sources = [ 'builtin/index-pack.c', 'builtin/init-db.c', 'builtin/interpret-trailers.c', + 'builtin/last-modified.c', 'builtin/log.c', 'builtin/ls-files.c', 'builtin/ls-remote.c', @@ -645,6 +646,7 @@ builtin_sources = [ 'builtin/repack.c', 'builtin/replace.c', 'builtin/replay.c', + 'builtin/repo.c', 'builtin/rerere.c', 'builtin/reset.c', 'builtin/rev-list.c', @@ -694,9 +696,14 @@ third_party_excludes = [ headers_to_check = [] if git.found() and fs.exists(meson.project_source_root() / '.git') - foreach header : run_command(git, '-C', meson.project_source_root(), 'ls-files', '--deduplicate', '*.h', third_party_excludes, check: true).stdout().split() - headers_to_check += header - endforeach + ls_headers = run_command(git, '-C', meson.project_source_root(), 'ls-files', '--deduplicate', '*.h', third_party_excludes, check: false) + if ls_headers.returncode() == 0 + foreach header : ls_headers.stdout().split() + headers_to_check += header + endforeach + else + warning('could not list headers, disabling static analysis targets') + endif endif if not get_option('breaking_changes') @@ -866,9 +873,11 @@ if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' endif build_options_config.set_quoted('X', executable_suffix) -python = import('python').find_installation('python3', required: get_option('python')) -target_python = find_program('python3', native: false, required: python.found()) -if python.found() +# Python is not used for our build system, but exclusively for git-p4. +# Consequently we only need to determine whether Python is available for the +# build target. +target_python = find_program('python3', native: false, required: get_option('python')) +if target_python.found() build_options_config.set('NO_PYTHON', '') else libgit_c_args += '-DNO_PYTHON' @@ -1055,7 +1064,33 @@ else build_options_config.set('NO_ICONV', '1') endif -pcre2 = dependency('libpcre2-8', required: get_option('pcre2'), default_options: ['default_library=static', 'test=false']) +# can't use enable_auto_if() because it is only available in meson 1.1 +if host_machine.system() == 'windows' and get_option('pcre2').allowed() + pcre2_feature = true +else + pcre2_feature = get_option('pcre2') +endif +pcre2 = dependency('libpcre2-8', required: pcre2_feature, default_options: ['default_library=static', 'test=false']) +if pcre2.found() and pcre2.type_name() != 'internal' and host_machine.system() == 'darwin' + # macOS installs a broken system package, double check + if not compiler.has_header('pcre2.h', dependencies: pcre2) + if pcre2_feature.enabled() + pcre2_fallback = ['pcre2', 'libpcre2_8'] + else + pcre2_fallback = [] + endif + # Attempt to fallback or replace with not-found-dependency + pcre2 = dependency('', required: false, fallback: pcre2_fallback, default_options: ['default_library=static', 'test=false']) + if not pcre2.found() + if pcre2_feature.enabled() + error('only a broken pcre2 install found and pcre2 is required') + else + warning('broken pcre2 install found, disabling pcre2 feature') + endif + endif + endif +endif + if pcre2.found() libgit_dependencies += pcre2 libgit_c_args += '-DUSE_LIBPCRE2' @@ -1331,10 +1366,6 @@ if host_machine.system() != 'windows' endif endif -if compiler.has_member('struct sysinfo', 'totalram', prefix: '#include <sys/sysinfo.h>') - libgit_c_args += '-DHAVE_SYSINFO' -endif - if compiler.has_member('struct stat', 'st_mtimespec.tv_nsec', prefix: '#include <sys/stat.h>') libgit_c_args += '-DUSE_ST_TIMESPEC' elif not compiler.has_member('struct stat', 'st_mtim.tv_nsec', prefix: '#include <sys/stat.h>') @@ -1420,17 +1451,6 @@ if compiler.compiles(''' libgit_c_args += '-DHAVE_CLOCK_MONOTONIC' endif -if not compiler.compiles(''' - #include <inttypes.h> - - void func(void) - { - uintmax_t x = 0; - } -''', name: 'uintmax_t') - libgit_c_args += '-DNO_UINTMAX_T' -endif - has_bsd_sysctl = false if compiler.has_header('sys/sysctl.h') if compiler.compiles(''' @@ -1449,6 +1469,12 @@ if compiler.has_header('sys/sysctl.h') endif endif +if not has_bsd_sysctl + if compiler.has_member('struct sysinfo', 'totalram', prefix: '#include <sys/sysinfo.h>') + libgit_c_args += '-DHAVE_SYSINFO' + endif +endif + if not meson.is_cross_build() and compiler.run(''' #include <stdio.h> @@ -1744,7 +1770,7 @@ git_builtin = executable('git', sources: builtin_sources + 'git.c', dependencies: [libgit_commonmain], install: true, - install_dir: get_option('libexecdir') / 'git-core', + install_dir: git_exec_path, ) bin_wrappers += git_builtin @@ -1752,35 +1778,35 @@ test_dependencies += executable('git-daemon', sources: 'daemon.c', dependencies: [libgit_commonmain], install: true, - install_dir: get_option('libexecdir') / 'git-core', + install_dir: git_exec_path, ) test_dependencies += executable('git-sh-i18n--envsubst', sources: 'sh-i18n--envsubst.c', dependencies: [libgit_commonmain], install: true, - install_dir: get_option('libexecdir') / 'git-core', + install_dir: git_exec_path, ) bin_wrappers += executable('git-shell', sources: 'shell.c', dependencies: [libgit_commonmain], install: true, - install_dir: get_option('libexecdir') / 'git-core', + install_dir: git_exec_path, ) test_dependencies += executable('git-http-backend', sources: 'http-backend.c', dependencies: [libgit_commonmain], install: true, - install_dir: get_option('libexecdir') / 'git-core', + install_dir: git_exec_path, ) bin_wrappers += executable('scalar', sources: 'scalar.c', dependencies: [libgit_commonmain], install: true, - install_dir: get_option('libexecdir') / 'git-core', + install_dir: git_exec_path, ) if curl.found() @@ -1796,14 +1822,14 @@ if curl.found() sources: 'remote-curl.c', dependencies: [libgit_curl], install: true, - install_dir: get_option('libexecdir') / 'git-core', + install_dir: git_exec_path, ) test_dependencies += executable('git-http-fetch', sources: 'http-fetch.c', dependencies: [libgit_curl], install: true, - install_dir: get_option('libexecdir') / 'git-core', + install_dir: git_exec_path, ) if expat.found() @@ -1811,7 +1837,7 @@ if curl.found() sources: 'http-push.c', dependencies: [libgit_curl], install: true, - install_dir: get_option('libexecdir') / 'git-core', + install_dir: git_exec_path, ) endif @@ -1822,7 +1848,7 @@ if curl.found() ) install_symlink(alias + executable_suffix, - install_dir: get_option('libexecdir') / 'git-core', + install_dir: git_exec_path, pointing_to: 'git-remote-http', ) endforeach @@ -1832,7 +1858,7 @@ test_dependencies += executable('git-imap-send', sources: 'imap-send.c', dependencies: [ use_curl_for_imap_send ? libgit_curl : libgit_commonmain ], install: true, - install_dir: get_option('libexecdir') / 'git-core', + install_dir: git_exec_path, ) foreach alias : [ 'git-receive-pack', 'git-upload-archive', 'git-upload-pack' ] @@ -1842,7 +1868,7 @@ foreach alias : [ 'git-receive-pack', 'git-upload-archive', 'git-upload-pack' ] ) install_symlink(alias + executable_suffix, - install_dir: get_option('libexecdir') / 'git-core', + install_dir: git_exec_path, pointing_to: 'git', ) endforeach @@ -1856,9 +1882,9 @@ foreach symlink : [ 'scalar', ] if meson.version().version_compare('>=1.3.0') - pointing_to = fs.relative_to(get_option('libexecdir') / 'git-core' / symlink, get_option('bindir')) + pointing_to = fs.relative_to(git_exec_path / symlink, get_option('bindir')) else - pointing_to = '../libexec/git-core' / symlink + pointing_to = '..' / git_exec_path / symlink endif install_symlink(symlink, @@ -1898,7 +1924,7 @@ foreach script : scripts_sh meson.project_build_root() / 'GIT-BUILD-OPTIONS', ], install: true, - install_dir: get_option('libexecdir') / 'git-core', + install_dir: git_exec_path, ) endforeach @@ -1931,7 +1957,7 @@ if perl_features_enabled input: perl_header_template, output: 'GIT-PERL-HEADER', configuration: { - 'GITEXECDIR_REL': get_option('libexecdir') / 'git-core', + 'GITEXECDIR_REL': git_exec_path, 'PERLLIBDIR_REL': perllibdir, 'LOCALEDIR_REL': get_option('datadir') / 'locale', 'INSTLIBDIR': perllibdir, @@ -1955,7 +1981,7 @@ if perl_features_enabled output: fs.stem(script), command: generate_perl_command, install: true, - install_dir: get_option('libexecdir') / 'git-core', + install_dir: git_exec_path, depends: [git_version_file], ) test_dependencies += generated_script @@ -1964,9 +1990,9 @@ if perl_features_enabled bin_wrappers += generated_script if meson.version().version_compare('>=1.3.0') - pointing_to = fs.relative_to(get_option('libexecdir') / 'git-core' / fs.stem(script), get_option('bindir')) + pointing_to = fs.relative_to(git_exec_path / fs.stem(script), get_option('bindir')) else - pointing_to = '../libexec/git-core' / fs.stem(script) + pointing_to = '..' / git_exec_path / fs.stem(script) endif install_symlink(fs.stem(script), @@ -1979,7 +2005,7 @@ if perl_features_enabled subdir('perl') endif -if python.found() +if target_python.found() scripts_python = [ 'git-p4.py' ] @@ -1996,7 +2022,7 @@ if python.found() '@OUTPUT@', ], install: true, - install_dir: get_option('libexecdir') / 'git-core', + install_dir: git_exec_path, ) test_dependencies += generated_python endforeach @@ -2030,7 +2056,7 @@ mergetools = [ ] foreach mergetool : mergetools - install_data(mergetool, install_dir: get_option('libexecdir') / 'git-core' / 'mergetools') + install_data(mergetool, install_dir: git_exec_path / 'mergetools') endforeach if intl.found() @@ -2054,6 +2080,18 @@ subdir('templates') # can properly set up test dependencies. The bin-wrappers themselves are set up # at configuration time, so these are fine. if get_option('tests') + test_kwargs = { + 'timeout': 0, + } + + # The TAP protocol was already understood by previous versions of Meson, but + # it was incompatible with the `meson test --interactive` flag. + if meson.version().version_compare('>=1.8.0') + test_kwargs += { + 'protocol': 'tap', + } + endif + subdir('t') endif @@ -2132,6 +2170,18 @@ if headers_to_check.length() != 0 and compiler.get_argument_syntax() == 'gcc' alias_target('check-headers', hdr_check) endif +git_clang_format = find_program('git-clang-format', required: false, native: true) +if git_clang_format.found() + run_target('style', + command: [ + git_clang_format, + '--style', 'file', + '--diff', + '--extensions', 'c,h' + ] + ) +endif + foreach key, value : { 'DIFF': diff.full_path(), 'GIT_SOURCE_DIR': meson.project_source_root(), @@ -2182,16 +2232,15 @@ meson.add_dist_script( summary({ 'benchmarks': get_option('tests') and perl.found() and time.found(), - 'curl': curl.found(), - 'expat': expat.found(), - 'gettext': intl.found(), + 'curl': curl, + 'expat': expat, + 'gettext': intl, 'gitweb': gitweb_option.allowed(), - 'https': https_backend, - 'iconv': iconv.found(), - 'pcre2': pcre2.found(), + 'iconv': iconv, + 'pcre2': pcre2, 'perl': perl_features_enabled, - 'python': python.found(), -}, section: 'Auto-detected features') + 'python': target_python.found(), +}, section: 'Auto-detected features', bool_yn: true) summary({ 'csprng': csprng_backend, diff --git a/meson_options.txt b/meson_options.txt index e7f768df24..1668f260a1 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -45,7 +45,7 @@ option('gitweb', type: 'feature', value: 'auto', description: 'Build Git web interface. Requires Perl.') option('iconv', type: 'feature', value: 'auto', description: 'Support reencoding strings with different encodings.') -option('pcre2', type: 'feature', value: 'enabled', +option('pcre2', type: 'feature', value: 'auto', description: 'Support Perl-compatible regular expressions in e.g. git-grep(1).') option('perl', type: 'feature', value: 'auto', description: 'Build tools written in Perl.') diff --git a/midx-write.c b/midx-write.c index ba4a94950a..c73010df6d 100644 --- a/midx-write.c +++ b/midx-write.c @@ -1,5 +1,3 @@ -#define DISABLE_SIGN_COMPARE_WARNINGS - #include "git-compat-util.h" #include "abspath.h" #include "config.h" @@ -24,11 +22,12 @@ #define BITMAP_POS_UNKNOWN (~((uint32_t)0)) #define MIDX_CHUNK_FANOUT_SIZE (sizeof(uint32_t) * 256) #define MIDX_CHUNK_LARGE_OFFSET_WIDTH (sizeof(uint64_t)) +#define NO_PREFERRED_PACK (~((uint32_t)0)) extern int midx_checksum_valid(struct multi_pack_index *m); -extern void clear_midx_files_ext(const char *object_dir, const char *ext, +extern void clear_midx_files_ext(struct odb_source *source, const char *ext, const char *keep_hash); -extern void clear_incremental_midx_files_ext(const char *object_dir, +extern void clear_incremental_midx_files_ext(struct odb_source *source, const char *ext, const char **keep_hashes, uint32_t hashes_nr); @@ -104,7 +103,7 @@ struct write_midx_context { unsigned large_offsets_needed:1; uint32_t num_large_offsets; - int preferred_pack_idx; + uint32_t preferred_pack_idx; int incremental; uint32_t num_multi_pack_indexes_before; @@ -112,6 +111,7 @@ struct write_midx_context { struct string_list *to_include; struct repository *repo; + struct odb_source *source; }; static int should_include_pack(const struct write_midx_context *ctx, @@ -260,7 +260,7 @@ static void midx_fanout_sort(struct midx_fanout *fanout) static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout, struct multi_pack_index *m, uint32_t cur_fanout, - int preferred_pack) + uint32_t preferred_pack) { uint32_t start = m->num_objects_in_base, end; uint32_t cur_object; @@ -274,7 +274,7 @@ static void midx_fanout_add_midx_fanout(struct midx_fanout *fanout, end = m->num_objects_in_base + ntohl(m->chunk_oid_fanout[cur_fanout]); for (cur_object = start; cur_object < end; cur_object++) { - if ((preferred_pack > -1) && + if ((preferred_pack != NO_PREFERRED_PACK) && (preferred_pack == nth_midxed_pack_int_id(m, cur_object))) { /* * Objects from preferred packs are added @@ -364,7 +364,8 @@ static void compute_sorted_entries(struct write_midx_context *ctx, preferred, cur_fanout); } - if (-1 < ctx->preferred_pack_idx && ctx->preferred_pack_idx < start_pack) + 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); @@ -648,7 +649,6 @@ static uint32_t *midx_pack_order(struct write_midx_context *ctx) } static void write_midx_reverse_index(struct write_midx_context *ctx, - const char *object_dir, unsigned char *midx_hash) { struct strbuf buf = STRBUF_INIT; @@ -657,17 +657,16 @@ static void write_midx_reverse_index(struct write_midx_context *ctx, trace2_region_enter("midx", "write_midx_reverse_index", ctx->repo); if (ctx->incremental) - get_split_midx_filename_ext(ctx->repo->hash_algo, &buf, - object_dir, midx_hash, - MIDX_EXT_REV); + get_split_midx_filename_ext(ctx->source, &buf, + midx_hash, MIDX_EXT_REV); else - get_midx_filename_ext(ctx->repo->hash_algo, &buf, object_dir, + get_midx_filename_ext(ctx->source, &buf, midx_hash, MIDX_EXT_REV); tmp_file = write_rev_file_order(ctx->repo, NULL, ctx->pack_order, ctx->entries_nr, midx_hash, WRITE_REV); - if (finalize_object_file(tmp_file, buf.buf)) + if (finalize_object_file(ctx->repo, tmp_file, buf.buf)) die(_("cannot store reverse index file")); strbuf_release(&buf); @@ -836,14 +835,13 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr } static int write_midx_bitmap(struct write_midx_context *ctx, - const char *object_dir, const unsigned char *midx_hash, struct packing_data *pdata, struct commit **commits, uint32_t commits_nr, unsigned flags) { - int ret, i; + int ret; uint16_t options = 0; struct bitmap_writer writer; struct pack_idx_entry **index; @@ -852,12 +850,11 @@ static int write_midx_bitmap(struct write_midx_context *ctx, trace2_region_enter("midx", "write_midx_bitmap", ctx->repo); if (ctx->incremental) - get_split_midx_filename_ext(ctx->repo->hash_algo, &bitmap_name, - object_dir, midx_hash, - MIDX_EXT_BITMAP); + get_split_midx_filename_ext(ctx->source, &bitmap_name, + midx_hash, MIDX_EXT_BITMAP); else - get_midx_filename_ext(ctx->repo->hash_algo, &bitmap_name, - object_dir, midx_hash, MIDX_EXT_BITMAP); + get_midx_filename_ext(ctx->source, &bitmap_name, + midx_hash, MIDX_EXT_BITMAP); if (flags & MIDX_WRITE_BITMAP_HASH_CACHE) options |= BITMAP_OPT_HASH_CACHE; @@ -871,7 +868,7 @@ static int write_midx_bitmap(struct write_midx_context *ctx, * this order). */ ALLOC_ARRAY(index, pdata->nr_objects); - for (i = 0; i < pdata->nr_objects; i++) + for (uint32_t i = 0; i < pdata->nr_objects; i++) index[i] = &pdata->objects[i].idx; bitmap_writer_init(&writer, ctx->repo, pdata, @@ -892,7 +889,7 @@ static int write_midx_bitmap(struct write_midx_context *ctx, * happens between bitmap_writer_build_type_index() and * bitmap_writer_finish(). */ - for (i = 0; i < pdata->nr_objects; i++) + for (uint32_t i = 0; i < pdata->nr_objects; i++) index[ctx->pack_order[i]] = &pdata->objects[i].idx; bitmap_writer_select_commits(&writer, commits, commits_nr); @@ -913,33 +910,7 @@ cleanup: return ret; } -static struct multi_pack_index *lookup_multi_pack_index(struct repository *r, - const char *object_dir) -{ - struct multi_pack_index *result = NULL; - struct multi_pack_index *cur; - char *obj_dir_real = real_pathdup(object_dir, 1); - struct strbuf cur_path_real = STRBUF_INIT; - - /* Ensure the given object_dir is local, or a known alternate. */ - find_odb(r, obj_dir_real); - - for (cur = get_multi_pack_index(r); cur; cur = cur->next) { - strbuf_realpath(&cur_path_real, cur->object_dir, 1); - if (!strcmp(obj_dir_real, cur_path_real.buf)) { - result = cur; - goto cleanup; - } - } - -cleanup: - free(obj_dir_real); - strbuf_release(&cur_path_real); - return result; -} - -static int fill_packs_from_midx(struct write_midx_context *ctx, - const char *preferred_pack_name, uint32_t flags) +static int fill_packs_from_midx(struct write_midx_context *ctx) { struct multi_pack_index *m; @@ -947,30 +918,10 @@ static int fill_packs_from_midx(struct write_midx_context *ctx, uint32_t i; for (i = 0; i < m->num_packs; i++) { - ALLOC_GROW(ctx->info, ctx->nr + 1, ctx->alloc); - - /* - * If generating a reverse index, need to have - * packed_git's loaded to compare their - * mtimes and object count. - * - * If a preferred pack is specified, need to - * have packed_git's loaded to ensure the chosen - * preferred pack has a non-zero object count. - */ - if (flags & MIDX_WRITE_REV_INDEX || - preferred_pack_name) { - if (prepare_midx_pack(ctx->repo, m, - m->num_packs_in_base + i)) { - error(_("could not load pack")); - return 1; - } - - if (open_pack_index(m->packs[i])) - die(_("could not open index for %s"), - m->packs[i]->pack_name); - } + if (prepare_midx_pack(m, m->num_packs_in_base + i)) + return error(_("could not load pack")); + 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); @@ -1007,10 +958,9 @@ 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); - get_midx_filename_ext(m->repo->hash_algo, &from, m->object_dir, + get_midx_filename_ext(m->source, &from, hash, midx_exts[i].non_split); - get_split_midx_filename_ext(m->repo->hash_algo, &to, - m->object_dir, hash, + get_split_midx_filename_ext(m->source, &to, hash, midx_exts[i].split); if (link(from.buf, to.buf) < 0 && errno != ENOENT) { @@ -1029,7 +979,7 @@ done: return ret; } -static void clear_midx_files(struct repository *r, const char *object_dir, +static void clear_midx_files(struct odb_source *source, const char **hashes, uint32_t hashes_nr, unsigned incremental) { @@ -1048,16 +998,16 @@ static void clear_midx_files(struct repository *r, const char *object_dir, uint32_t i, j; for (i = 0; i < ARRAY_SIZE(exts); i++) { - clear_incremental_midx_files_ext(object_dir, exts[i], + clear_incremental_midx_files_ext(source, exts[i], hashes, hashes_nr); for (j = 0; j < hashes_nr; j++) - clear_midx_files_ext(object_dir, exts[i], hashes[j]); + clear_midx_files_ext(source, exts[i], hashes[j]); } if (incremental) - get_midx_filename(r->hash_algo, &buf, object_dir); + get_midx_filename(source, &buf); else - get_midx_chain_filename(&buf, object_dir); + get_midx_chain_filename(source, &buf); if (unlink(buf.buf) && errno != ENOENT) die_errno(_("failed to clear multi-pack-index at %s"), buf.buf); @@ -1065,45 +1015,49 @@ static void clear_midx_files(struct repository *r, const char *object_dir, strbuf_release(&buf); } -static int write_midx_internal(struct repository *r, const char *object_dir, +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) { + struct repository *r = source->odb->repo; struct strbuf midx_name = STRBUF_INIT; unsigned char midx_hash[GIT_MAX_RAWSZ]; - uint32_t i, start_pack; + uint32_t start_pack; struct hashfile *f = NULL; struct lock_file lk; struct tempfile *incr; - struct write_midx_context ctx = { 0 }; + struct write_midx_context ctx = { + .preferred_pack_idx = NO_PREFERRED_PACK, + }; int bitmapped_packs_concat_len = 0; int pack_name_concat_len = 0; int dropped_packs = 0; - int result = 0; + int result = -1; const char **keep_hashes = NULL; struct chunkfile *cf; trace2_region_enter("midx", "write_midx_internal", r); ctx.repo = r; + ctx.source = source; ctx.incremental = !!(flags & MIDX_WRITE_INCREMENTAL); if (ctx.incremental) strbuf_addf(&midx_name, "%s/pack/multi-pack-index.d/tmp_midx_XXXXXX", - object_dir); + source->path); else - get_midx_filename(r->hash_algo, &midx_name, object_dir); + get_midx_filename(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 = lookup_multi_pack_index(r, object_dir); + struct multi_pack_index *m = get_multi_pack_index(source); if (m && !midx_checksum_valid(m)) { warning(_("ignoring existing multi-pack-index; checksum mismatch")); m = NULL; @@ -1134,15 +1088,13 @@ static int write_midx_internal(struct repository *r, const char *object_dir, if (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->repo->hash_algo)); - result = 1; + m->source->odb->repo->hash_algo)); goto cleanup; } ctx.num_multi_pack_indexes_before++; m = m->base_midx; } - } else if (ctx.m && fill_packs_from_midx(&ctx, preferred_pack_name, - flags) < 0) { + } else if (ctx.m && fill_packs_from_midx(&ctx)) { goto cleanup; } @@ -1157,7 +1109,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir, ctx.to_include = packs_to_include; - for_each_file_in_pack_dir(object_dir, add_pack_to_midx, &ctx); + for_each_file_in_pack_dir(source->path, add_pack_to_midx, &ctx); stop_progress(&ctx.progress); if ((ctx.m && ctx.nr == ctx.m->num_packs + ctx.m->num_packs_in_base) && @@ -1177,18 +1129,21 @@ static int write_midx_internal(struct repository *r, const char *object_dir, * corresponding bitmap (or one wasn't requested). */ if (!want_bitmap) - clear_midx_files_ext(object_dir, "bitmap", NULL); + clear_midx_files_ext(source, "bitmap", NULL); + result = 0; goto cleanup; } } - if (ctx.incremental && !ctx.nr) + if (ctx.incremental && !ctx.nr) { + result = 0; goto cleanup; /* nothing to do */ + } if (preferred_pack_name) { - ctx.preferred_pack_idx = -1; + ctx.preferred_pack_idx = NO_PREFERRED_PACK; - for (i = 0; i < ctx.nr; i++) { + for (size_t i = 0; i < ctx.nr; i++) { if (!cmp_idx_or_pack_name(preferred_pack_name, ctx.info[i].pack_name)) { ctx.preferred_pack_idx = i; @@ -1196,14 +1151,21 @@ static int write_midx_internal(struct repository *r, const char *object_dir, } } - if (ctx.preferred_pack_idx == -1) + if (ctx.preferred_pack_idx == NO_PREFERRED_PACK) warning(_("unknown preferred pack: '%s'"), preferred_pack_name); } else if (ctx.nr && (flags & (MIDX_WRITE_REV_INDEX | MIDX_WRITE_BITMAP))) { - struct packed_git *oldest = ctx.info[ctx.preferred_pack_idx].p; + struct packed_git *oldest = ctx.info[0].p; ctx.preferred_pack_idx = 0; + /* + * Attempt opening the pack index to populate num_objects. + * Ignore failiures as they can be expected and are not + * fatal during this selection time. + */ + open_pack_index(oldest); + if (packs_to_drop && packs_to_drop->nr) BUG("cannot write a MIDX bitmap during expiration"); @@ -1213,11 +1175,12 @@ static int write_midx_internal(struct repository *r, const char *object_dir, * pack-order has all of its objects selected from that pack * (and not another pack containing a duplicate) */ - for (i = 1; i < ctx.nr; i++) { + for (size_t i = 1; i < ctx.nr; i++) { struct packed_git *p = ctx.info[i].p; if (!oldest->num_objects || p->mtime < oldest->mtime) { oldest = p; + open_pack_index(oldest); ctx.preferred_pack_idx = i; } } @@ -1229,22 +1192,26 @@ static int write_midx_internal(struct repository *r, const char *object_dir, * objects to resolve, so the preferred value doesn't * matter. */ - ctx.preferred_pack_idx = -1; + ctx.preferred_pack_idx = NO_PREFERRED_PACK; } } else { /* * otherwise don't mark any pack as preferred to avoid * interfering with expiration logic below */ - ctx.preferred_pack_idx = -1; + ctx.preferred_pack_idx = NO_PREFERRED_PACK; } - if (ctx.preferred_pack_idx > -1) { + if (ctx.preferred_pack_idx != NO_PREFERRED_PACK) { struct packed_git *preferred = ctx.info[ctx.preferred_pack_idx].p; + + if (open_pack_index(preferred)) + die(_("failed to open preferred pack %s"), + ctx.info[ctx.preferred_pack_idx].pack_name); + if (!preferred->num_objects) { error(_("cannot select preferred pack %s with no objects"), preferred->pack_name); - result = 1; goto cleanup; } } @@ -1252,7 +1219,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir, compute_sorted_entries(&ctx, start_pack); ctx.large_offsets_needed = 0; - for (i = 0; i < ctx.entries_nr; i++) { + for (size_t i = 0; i < ctx.entries_nr; i++) { if (ctx.entries[i].offset > 0x7fffffff) ctx.num_large_offsets++; if (ctx.entries[i].offset > 0xffffffff) @@ -1262,10 +1229,10 @@ static int write_midx_internal(struct repository *r, const char *object_dir, QSORT(ctx.info, ctx.nr, pack_info_compare); if (packs_to_drop && packs_to_drop->nr) { - int drop_index = 0; + size_t drop_index = 0; int missing_drops = 0; - for (i = 0; i < ctx.nr && drop_index < packs_to_drop->nr; i++) { + for (size_t i = 0; i < ctx.nr && drop_index < packs_to_drop->nr; i++) { int cmp = strcmp(ctx.info[i].pack_name, packs_to_drop->items[drop_index].string); @@ -1283,10 +1250,8 @@ static int write_midx_internal(struct repository *r, const char *object_dir, } } - if (missing_drops) { - result = 1; + if (missing_drops) goto cleanup; - } } /* @@ -1296,7 +1261,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir, * pack_perm[old_id] = new_id */ ALLOC_ARRAY(ctx.pack_perm, ctx.nr); - for (i = 0; i < ctx.nr; i++) { + for (size_t i = 0; i < ctx.nr; i++) { if (ctx.info[i].expired) { dropped_packs++; ctx.pack_perm[ctx.info[i].orig_pack_int_id] = PACK_EXPIRED; @@ -1305,7 +1270,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir, } } - for (i = 0; i < ctx.nr; i++) { + for (size_t i = 0; i < ctx.nr; i++) { if (ctx.info[i].expired) continue; pack_name_concat_len += strlen(ctx.info[i].pack_name) + 1; @@ -1332,7 +1297,6 @@ static int write_midx_internal(struct repository *r, const char *object_dir, if (ctx.nr - dropped_packs == 0) { error(_("no pack files to index.")); - result = 1; goto cleanup; } @@ -1345,20 +1309,20 @@ static int write_midx_internal(struct repository *r, const char *object_dir, if (ctx.incremental) { struct strbuf lock_name = STRBUF_INIT; - get_midx_chain_filename(&lock_name, object_dir); + get_midx_chain_filename(source, &lock_name); hold_lock_file_for_update(&lk, lock_name.buf, LOCK_DIE_ON_ERROR); strbuf_release(&lock_name); incr = mks_tempfile_m(midx_name.buf, 0444); if (!incr) { error(_("unable to create temporary MIDX layer")); - return -1; + goto cleanup; } if (adjust_shared_perm(r, get_tempfile_path(incr))) { error(_("unable to adjust shared permissions for '%s'"), get_tempfile_path(incr)); - return -1; + goto cleanup; } f = hashfd(r->hash_algo, get_tempfile_fd(incr), @@ -1408,7 +1372,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir, if (flags & MIDX_WRITE_REV_INDEX && git_env_bool("GIT_TEST_MIDX_WRITE_REV", 0)) - write_midx_reverse_index(&ctx, object_dir, midx_hash); + write_midx_reverse_index(&ctx, midx_hash); if (flags & MIDX_WRITE_BITMAP) { struct packing_data pdata; @@ -1431,11 +1395,10 @@ static int write_midx_internal(struct repository *r, const char *object_dir, FREE_AND_NULL(ctx.entries); ctx.entries_nr = 0; - if (write_midx_bitmap(&ctx, object_dir, + if (write_midx_bitmap(&ctx, midx_hash, &pdata, commits, commits_nr, flags) < 0) { error(_("could not write multi-pack bitmap")); - result = 1; clear_packing_data(&pdata); free(commits); goto cleanup; @@ -1449,6 +1412,9 @@ static int write_midx_internal(struct repository *r, const char *object_dir, * have been freed in the previous if block. */ + 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.incremental) { @@ -1458,18 +1424,18 @@ static int write_midx_internal(struct repository *r, const char *object_dir, if (!chainf) { error_errno(_("unable to open multi-pack-index chain file")); - return -1; + goto cleanup; } if (link_midx_to_chain(ctx.base_midx) < 0) - return -1; + goto cleanup; - get_split_midx_filename_ext(r->hash_algo, &final_midx_name, - object_dir, midx_hash, MIDX_EXT_MIDX); + get_split_midx_filename_ext(source, &final_midx_name, + midx_hash, MIDX_EXT_MIDX); if (rename_tempfile(&incr, final_midx_name.buf) < 0) { error_errno(_("unable to rename new multi-pack-index layer")); - return -1; + goto cleanup; } strbuf_release(&final_midx_name); @@ -1477,7 +1443,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir, keep_hashes[ctx.num_multi_pack_indexes_before] = xstrdup(hash_to_hex_algop(midx_hash, r->hash_algo)); - for (i = 0; i < ctx.num_multi_pack_indexes_before; 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; keep_hashes[j] = xstrdup(hash_to_hex_algop(get_midx_checksum(m), @@ -1485,7 +1451,7 @@ static int write_midx_internal(struct repository *r, const char *object_dir, m = m->base_midx; } - for (i = 0; i < ctx.num_multi_pack_indexes_before + 1; i++) + for (uint32_t i = 0; i <= ctx.num_multi_pack_indexes_before; i++) fprintf(get_lock_file_fp(&lk), "%s\n", keep_hashes[i]); } else { keep_hashes[ctx.num_multi_pack_indexes_before] = @@ -1498,12 +1464,13 @@ static int write_midx_internal(struct repository *r, const char *object_dir, if (commit_lock_file(&lk) < 0) die_errno(_("could not write multi-pack-index")); - clear_midx_files(r, object_dir, keep_hashes, + clear_midx_files(source, keep_hashes, ctx.num_multi_pack_indexes_before + 1, ctx.incremental); + result = 0; cleanup: - for (i = 0; i < ctx.nr; i++) { + for (size_t i = 0; i < ctx.nr; i++) { if (ctx.info[i].p) { close_pack(ctx.info[i].p); free(ctx.info[i].p); @@ -1516,7 +1483,7 @@ cleanup: free(ctx.pack_perm); free(ctx.pack_order); if (keep_hashes) { - for (i = 0; i < ctx.num_multi_pack_indexes_before + 1; i++) + for (uint32_t i = 0; i <= ctx.num_multi_pack_indexes_before; i++) free((char *)keep_hashes[i]); free(keep_hashes); } @@ -1527,29 +1494,29 @@ cleanup: return result; } -int write_midx_file(struct repository *r, const char *object_dir, +int write_midx_file(struct odb_source *source, const char *preferred_pack_name, const char *refs_snapshot, unsigned flags) { - return write_midx_internal(r, object_dir, NULL, NULL, + return write_midx_internal(source, NULL, NULL, preferred_pack_name, refs_snapshot, flags); } -int write_midx_file_only(struct repository *r, const char *object_dir, +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) { - return write_midx_internal(r, object_dir, packs_to_include, NULL, + return write_midx_internal(source, packs_to_include, NULL, preferred_pack_name, refs_snapshot, flags); } -int expire_midx_packs(struct repository *r, const char *object_dir, unsigned flags) +int expire_midx_packs(struct odb_source *source, unsigned flags) { uint32_t i, *count, result = 0; struct string_list packs_to_drop = STRING_LIST_INIT_DUP; - struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir); + struct multi_pack_index *m = get_multi_pack_index(source); struct progress *progress = NULL; if (!m) @@ -1562,7 +1529,7 @@ int expire_midx_packs(struct repository *r, const char *object_dir, unsigned fla if (flags & MIDX_PROGRESS) progress = start_delayed_progress( - r, + source->odb->repo, _("Counting referenced objects"), m->num_objects); for (i = 0; i < m->num_objects; i++) { @@ -1574,7 +1541,7 @@ int expire_midx_packs(struct repository *r, const char *object_dir, unsigned fla if (flags & MIDX_PROGRESS) progress = start_delayed_progress( - r, + source->odb->repo, _("Finding and deleting unreferenced packfiles"), m->num_packs); for (i = 0; i < m->num_packs; i++) { @@ -1584,7 +1551,7 @@ int expire_midx_packs(struct repository *r, const char *object_dir, unsigned fla if (count[i]) continue; - if (prepare_midx_pack(r, m, i)) + if (prepare_midx_pack(m, i)) continue; if (m->packs[i]->pack_keep || m->packs[i]->is_cruft) @@ -1602,7 +1569,7 @@ int expire_midx_packs(struct repository *r, const char *object_dir, unsigned fla free(count); if (packs_to_drop.nr) - result = write_midx_internal(r, object_dir, NULL, + result = write_midx_internal(source, NULL, &packs_to_drop, NULL, NULL, flags); string_list_clear(&packs_to_drop, 0); @@ -1630,13 +1597,12 @@ static int compare_by_mtime(const void *a_, const void *b_) return 0; } -static int want_included_pack(struct repository *r, - struct multi_pack_index *m, +static int want_included_pack(struct multi_pack_index *m, int pack_kept_objects, uint32_t pack_int_id) { struct packed_git *p; - if (prepare_midx_pack(r, m, pack_int_id)) + if (prepare_midx_pack(m, pack_int_id)) return 0; p = m->packs[pack_int_id]; if (!pack_kept_objects && p->pack_keep) @@ -1658,7 +1624,7 @@ static void fill_included_packs_all(struct repository *r, repo_config_get_bool(r, "repack.packkeptobjects", &pack_kept_objects); for (i = 0; i < m->num_packs; i++) { - if (!want_included_pack(r, m, pack_kept_objects, i)) + if (!want_included_pack(m, pack_kept_objects, i)) continue; include_pack[i] = 1; @@ -1682,7 +1648,7 @@ static void fill_included_packs_batch(struct repository *r, for (i = 0; i < m->num_packs; i++) { pack_info[i].pack_int_id = i; - if (prepare_midx_pack(r, m, i)) + if (prepare_midx_pack(m, i)) continue; pack_info[i].mtime = m->packs[i]->mtime; @@ -1701,7 +1667,7 @@ static void fill_included_packs_batch(struct repository *r, struct packed_git *p = m->packs[pack_int_id]; uint64_t expected_size; - if (!want_included_pack(r, m, pack_kept_objects, pack_int_id)) + if (!want_included_pack(m, pack_kept_objects, pack_int_id)) continue; /* @@ -1728,14 +1694,15 @@ static void fill_included_packs_batch(struct repository *r, free(pack_info); } -int midx_repack(struct repository *r, const char *object_dir, size_t batch_size, unsigned flags) +int midx_repack(struct odb_source *source, size_t batch_size, unsigned flags) { + struct repository *r = source->odb->repo; int result = 0; uint32_t i, packs_to_repack = 0; unsigned char *include_pack; struct child_process cmd = CHILD_PROCESS_INIT; FILE *cmd_in; - struct multi_pack_index *m = lookup_multi_pack_index(r, object_dir); + struct multi_pack_index *m = get_multi_pack_index(source); /* * When updating the default for these configuration @@ -1769,7 +1736,7 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size, strvec_push(&cmd.args, "pack-objects"); - strvec_pushf(&cmd.args, "%s/pack/pack", object_dir); + strvec_pushf(&cmd.args, "%s/pack/pack", source->path); if (delta_base_offset) strvec_push(&cmd.args, "--delta-base-offset"); @@ -1810,7 +1777,7 @@ int midx_repack(struct repository *r, const char *object_dir, size_t batch_size, goto cleanup; } - result = write_midx_internal(r, object_dir, NULL, NULL, NULL, NULL, + result = write_midx_internal(source, NULL, NULL, NULL, NULL, flags); cleanup: @@ -16,9 +16,9 @@ #define MIDX_PACK_ERROR ((void *)(intptr_t)-1) int midx_checksum_valid(struct multi_pack_index *m); -void clear_midx_files_ext(const char *object_dir, const char *ext, +void clear_midx_files_ext(struct odb_source *source, const char *ext, const char *keep_hash); -void clear_incremental_midx_files_ext(const char *object_dir, const char *ext, +void clear_incremental_midx_files_ext(struct odb_source *source, const char *ext, char **keep_hashes, uint32_t hashes_nr); int cmp_idx_or_pack_name(const char *idx_or_pack_name, @@ -26,22 +26,20 @@ int cmp_idx_or_pack_name(const char *idx_or_pack_name, const unsigned char *get_midx_checksum(struct multi_pack_index *m) { - return m->data + m->data_len - m->repo->hash_algo->rawsz; + return m->data + m->data_len - m->source->odb->repo->hash_algo->rawsz; } -void get_midx_filename(const struct git_hash_algo *hash_algo, - struct strbuf *out, const char *object_dir) +void get_midx_filename(struct odb_source *source, struct strbuf *out) { - get_midx_filename_ext(hash_algo, out, object_dir, NULL, NULL); + get_midx_filename_ext(source, out, NULL, NULL); } -void get_midx_filename_ext(const struct git_hash_algo *hash_algo, - struct strbuf *out, const char *object_dir, +void get_midx_filename_ext(struct odb_source *source, struct strbuf *out, const unsigned char *hash, const char *ext) { - strbuf_addf(out, "%s/pack/multi-pack-index", object_dir); + strbuf_addf(out, "%s/pack/multi-pack-index", source->path); if (ext) - strbuf_addf(out, "-%s.%s", hash_to_hex_algop(hash, hash_algo), ext); + strbuf_addf(out, "-%s.%s", hash_to_hex_algop(hash, source->odb->repo->hash_algo), ext); } static int midx_read_oid_fanout(const unsigned char *chunk_start, @@ -95,11 +93,10 @@ static int midx_read_object_offsets(const unsigned char *chunk_start, return 0; } -static struct multi_pack_index *load_multi_pack_index_one(struct repository *r, - const char *object_dir, - const char *midx_name, - int local) +static struct multi_pack_index *load_multi_pack_index_one(struct odb_source *source, + const char *midx_name) { + struct repository *r = source->odb->repo; struct multi_pack_index *m = NULL; int fd; struct stat st; @@ -129,11 +126,10 @@ static struct multi_pack_index *load_multi_pack_index_one(struct repository *r, midx_map = xmmap(NULL, midx_size, PROT_READ, MAP_PRIVATE, fd, 0); close(fd); - FLEX_ALLOC_STR(m, object_dir, object_dir); + CALLOC_ARRAY(m, 1); m->data = midx_map; m->data_len = midx_size; - m->local = local; - m->repo = r; + m->source = source; m->signature = get_be32(m->data); if (m->signature != MIDX_SIGNATURE) @@ -224,24 +220,23 @@ cleanup_fail: return NULL; } -void get_midx_chain_dirname(struct strbuf *buf, const char *object_dir) +void get_midx_chain_dirname(struct odb_source *source, struct strbuf *buf) { - strbuf_addf(buf, "%s/pack/multi-pack-index.d", object_dir); + strbuf_addf(buf, "%s/pack/multi-pack-index.d", source->path); } -void get_midx_chain_filename(struct strbuf *buf, const char *object_dir) +void get_midx_chain_filename(struct odb_source *source, struct strbuf *buf) { - get_midx_chain_dirname(buf, object_dir); + get_midx_chain_dirname(source, buf); strbuf_addstr(buf, "/multi-pack-index-chain"); } -void get_split_midx_filename_ext(const struct git_hash_algo *hash_algo, - struct strbuf *buf, const char *object_dir, +void get_split_midx_filename_ext(struct odb_source *source, struct strbuf *buf, const unsigned char *hash, const char *ext) { - get_midx_chain_dirname(buf, object_dir); + get_midx_chain_dirname(source, buf); strbuf_addf(buf, "/multi-pack-index-%s.%s", - hash_to_hex_algop(hash, hash_algo), ext); + hash_to_hex_algop(hash, source->odb->repo->hash_algo), ext); } static int open_multi_pack_index_chain(const struct git_hash_algo *hash_algo, @@ -297,19 +292,18 @@ static int add_midx_to_chain(struct multi_pack_index *midx, return 1; } -static struct multi_pack_index *load_midx_chain_fd_st(struct repository *r, - const char *object_dir, - int local, +static struct multi_pack_index *load_midx_chain_fd_st(struct odb_source *source, int fd, struct stat *st, int *incomplete_chain) { + const struct git_hash_algo *hash_algo = source->odb->repo->hash_algo; struct multi_pack_index *midx_chain = NULL; struct strbuf buf = STRBUF_INIT; int valid = 1; uint32_t i, count; FILE *fp = xfdopen(fd, "r"); - count = st->st_size / (r->hash_algo->hexsz + 1); + count = st->st_size / (hash_algo->hexsz + 1); for (i = 0; i < count; i++) { struct multi_pack_index *m; @@ -318,7 +312,7 @@ static struct multi_pack_index *load_midx_chain_fd_st(struct repository *r, if (strbuf_getline_lf(&buf, fp) == EOF) break; - if (get_oid_hex_algop(buf.buf, &layer, r->hash_algo)) { + if (get_oid_hex_algop(buf.buf, &layer, hash_algo)) { warning(_("invalid multi-pack-index chain: line '%s' " "not a hash"), buf.buf); @@ -329,9 +323,9 @@ static struct multi_pack_index *load_midx_chain_fd_st(struct repository *r, valid = 0; strbuf_reset(&buf); - get_split_midx_filename_ext(r->hash_algo, &buf, object_dir, + get_split_midx_filename_ext(source, &buf, layer.hash, MIDX_EXT_MIDX); - m = load_multi_pack_index_one(r, object_dir, buf.buf, local); + m = load_multi_pack_index_one(source, buf.buf); if (m) { if (add_midx_to_chain(m, midx_chain)) { @@ -354,40 +348,34 @@ static struct multi_pack_index *load_midx_chain_fd_st(struct repository *r, return midx_chain; } -static struct multi_pack_index *load_multi_pack_index_chain(struct repository *r, - const char *object_dir, - int local) +static struct multi_pack_index *load_multi_pack_index_chain(struct odb_source *source) { struct strbuf chain_file = STRBUF_INIT; struct stat st; int fd; struct multi_pack_index *m = NULL; - get_midx_chain_filename(&chain_file, object_dir); - if (open_multi_pack_index_chain(r->hash_algo, chain_file.buf, &fd, &st)) { + get_midx_chain_filename(source, &chain_file); + if (open_multi_pack_index_chain(source->odb->repo->hash_algo, chain_file.buf, &fd, &st)) { int incomplete; /* ownership of fd is taken over by load function */ - m = load_midx_chain_fd_st(r, object_dir, local, fd, &st, - &incomplete); + m = load_midx_chain_fd_st(source, fd, &st, &incomplete); } strbuf_release(&chain_file); return m; } -struct multi_pack_index *load_multi_pack_index(struct repository *r, - const char *object_dir, - int local) +struct multi_pack_index *load_multi_pack_index(struct odb_source *source) { struct strbuf midx_name = STRBUF_INIT; struct multi_pack_index *m; - get_midx_filename(r->hash_algo, &midx_name, object_dir); + get_midx_filename(source, &midx_name); - m = load_multi_pack_index_one(r, object_dir, - midx_name.buf, local); + m = load_multi_pack_index_one(source, midx_name.buf); if (!m) - m = load_multi_pack_index_chain(r, object_dir, local); + m = load_multi_pack_index_chain(source); strbuf_release(&midx_name); @@ -401,7 +389,6 @@ void close_midx(struct multi_pack_index *m) if (!m) return; - close_midx(m->next); close_midx(m->base_midx); munmap((unsigned char *)m->data, m->data_len); @@ -451,9 +438,10 @@ static uint32_t midx_for_pack(struct multi_pack_index **_m, return pack_int_id - m->num_packs_in_base; } -int prepare_midx_pack(struct repository *r, struct multi_pack_index *m, +int prepare_midx_pack(struct multi_pack_index *m, uint32_t pack_int_id) { + struct repository *r = m->source->odb->repo; struct strbuf pack_name = STRBUF_INIT; struct strbuf key = STRBUF_INIT; struct packed_git *p; @@ -465,7 +453,7 @@ int prepare_midx_pack(struct repository *r, struct multi_pack_index *m, if (m->packs[pack_int_id]) return 0; - strbuf_addf(&pack_name, "%s/pack/%s", m->object_dir, + strbuf_addf(&pack_name, "%s/pack/%s", m->source->path, m->pack_names[pack_int_id]); /* pack_map holds the ".pack" name, but we have the .idx */ @@ -476,7 +464,8 @@ int prepare_midx_pack(struct repository *r, struct multi_pack_index *m, strhash(key.buf), key.buf, struct packed_git, packmap_ent); if (!p) { - p = add_packed_git(r, pack_name.buf, pack_name.len, m->local); + p = add_packed_git(r, pack_name.buf, pack_name.len, + m->source->local); if (p) { install_packed_git(r, p); list_add_tail(&p->mru, &r->objects->packed_git_mru); @@ -508,7 +497,7 @@ struct packed_git *nth_midxed_pack(struct multi_pack_index *m, #define MIDX_CHUNK_BITMAPPED_PACKS_WIDTH (2 * sizeof(uint32_t)) -int nth_bitmapped_pack(struct repository *r, struct multi_pack_index *m, +int nth_bitmapped_pack(struct multi_pack_index *m, struct bitmapped_pack *bp, uint32_t pack_int_id) { uint32_t local_pack_int_id = midx_for_pack(&m, pack_int_id); @@ -516,7 +505,7 @@ int nth_bitmapped_pack(struct repository *r, struct multi_pack_index *m, if (!m->chunk_bitmapped_packs) return error(_("MIDX does not contain the BTMP chunk")); - if (prepare_midx_pack(r, m, pack_int_id)) + if (prepare_midx_pack(m, pack_int_id)) return error(_("could not load bitmapped pack %"PRIu32), pack_int_id); bp->p = m->packs[local_pack_int_id]; @@ -535,7 +524,8 @@ int bsearch_one_midx(const struct object_id *oid, struct multi_pack_index *m, uint32_t *result) { int ret = bsearch_hash(oid->hash, m->chunk_oid_fanout, - m->chunk_oid_lookup, m->repo->hash_algo->rawsz, + m->chunk_oid_lookup, + m->source->odb->repo->hash_algo->rawsz, result); if (result) *result += m->num_objects_in_base; @@ -566,7 +556,7 @@ struct object_id *nth_midxed_object_oid(struct object_id *oid, n = midx_for_object(&m, n); oidread(oid, m->chunk_oid_lookup + st_mult(m->hash_len, n), - m->repo->hash_algo); + m->source->odb->repo->hash_algo); return oid; } @@ -601,10 +591,9 @@ uint32_t nth_midxed_pack_int_id(struct multi_pack_index *m, uint32_t pos) (off_t)pos * MIDX_CHUNK_OFFSET_WIDTH); } -int fill_midx_entry(struct repository *r, +int fill_midx_entry(struct multi_pack_index *m, const struct object_id *oid, - struct pack_entry *e, - struct multi_pack_index *m) + struct pack_entry *e) { uint32_t pos; uint32_t pack_int_id; @@ -616,7 +605,7 @@ int fill_midx_entry(struct repository *r, midx_for_object(&m, pos); pack_int_id = nth_midxed_pack_int_id(m, pos); - if (prepare_midx_pack(r, m, pack_int_id)) + if (prepare_midx_pack(m, pack_int_id)) return 0; p = m->packs[pack_int_id - m->num_packs_in_base]; @@ -724,37 +713,25 @@ int midx_preferred_pack(struct multi_pack_index *m, uint32_t *pack_int_id) return 0; } -int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, int local) +int prepare_multi_pack_index_one(struct odb_source *source) { - struct multi_pack_index *m; - struct multi_pack_index *m_search; + struct repository *r = source->odb->repo; prepare_repo_settings(r); if (!r->settings.core_multi_pack_index) return 0; - for (m_search = r->objects->multi_pack_index; m_search; m_search = m_search->next) - if (!strcmp(object_dir, m_search->object_dir)) - return 1; - - m = load_multi_pack_index(r, object_dir, local); - - if (m) { - struct multi_pack_index *mp = r->objects->multi_pack_index; - if (mp) { - m->next = mp->next; - mp->next = m; - } else - r->objects->multi_pack_index = m; + if (source->midx) return 1; - } - return 0; + source->midx = load_multi_pack_index(source); + + return !!source->midx; } int midx_checksum_valid(struct multi_pack_index *m) { - return hashfile_checksum_valid(m->repo->hash_algo, + return hashfile_checksum_valid(m->source->odb->repo->hash_algo, m->data, m->data_len); } @@ -781,7 +758,7 @@ static void clear_midx_file_ext(const char *full_path, size_t full_path_len UNUS die_errno(_("failed to remove %s"), full_path); } -void clear_midx_files_ext(const char *object_dir, const char *ext, +void clear_midx_files_ext(struct odb_source *source, const char *ext, const char *keep_hash) { struct clear_midx_data data; @@ -795,7 +772,7 @@ void clear_midx_files_ext(const char *object_dir, const char *ext, } data.ext = ext; - for_each_file_in_pack_dir(object_dir, + for_each_file_in_pack_dir(source->path, clear_midx_file_ext, &data); @@ -804,7 +781,7 @@ void clear_midx_files_ext(const char *object_dir, const char *ext, free(data.keep); } -void clear_incremental_midx_files_ext(const char *object_dir, const char *ext, +void clear_incremental_midx_files_ext(struct odb_source *source, const char *ext, char **keep_hashes, uint32_t hashes_nr) { @@ -820,7 +797,7 @@ void clear_incremental_midx_files_ext(const char *object_dir, const char *ext, data.keep_nr = hashes_nr; data.ext = ext; - for_each_file_in_pack_subdir(object_dir, "multi-pack-index.d", + for_each_file_in_pack_subdir(source->path, "multi-pack-index.d", clear_midx_file_ext, &data); for (i = 0; i < hashes_nr; i++) @@ -832,18 +809,23 @@ void clear_midx_file(struct repository *r) { struct strbuf midx = STRBUF_INIT; - get_midx_filename(r->hash_algo, &midx, r->objects->odb->path); + get_midx_filename(r->objects->sources, &midx); - if (r->objects && r->objects->multi_pack_index) { - close_midx(r->objects->multi_pack_index); - r->objects->multi_pack_index = NULL; + if (r->objects) { + struct odb_source *source; + + for (source = r->objects->sources; source; source = source->next) { + if (source->midx) + close_midx(source->midx); + source->midx = NULL; + } } if (remove_path(midx.buf)) die(_("failed to clear multi-pack-index at %s"), midx.buf); - clear_midx_files_ext(r->objects->odb->path, MIDX_EXT_BITMAP, NULL); - clear_midx_files_ext(r->objects->odb->path, MIDX_EXT_REV, NULL); + clear_midx_files_ext(r->objects->sources, MIDX_EXT_BITMAP, NULL); + clear_midx_files_ext(r->objects->sources, MIDX_EXT_REV, NULL); strbuf_release(&midx); } @@ -887,12 +869,13 @@ static int compare_pair_pos_vs_id(const void *_a, const void *_b) display_progress(progress, _n); \ } while (0) -int verify_midx_file(struct repository *r, const char *object_dir, unsigned flags) +int verify_midx_file(struct odb_source *source, unsigned flags) { + struct repository *r = source->odb->repo; struct pair_pos_vs_id *pairs = NULL; uint32_t i; struct progress *progress = NULL; - struct multi_pack_index *m = load_multi_pack_index(r, object_dir, 1); + struct multi_pack_index *m = load_multi_pack_index(source); struct multi_pack_index *curr; verify_midx_error = 0; @@ -901,7 +884,7 @@ int verify_midx_file(struct repository *r, const char *object_dir, unsigned flag struct stat sb; struct strbuf filename = STRBUF_INIT; - get_midx_filename(r->hash_algo, &filename, object_dir); + get_midx_filename(source, &filename); if (!stat(filename.buf, &sb)) { error(_("multi-pack-index file exists, but failed to parse")); @@ -919,7 +902,7 @@ int verify_midx_file(struct repository *r, const char *object_dir, unsigned flag _("Looking for referenced packfiles"), m->num_packs + m->num_packs_in_base); for (i = 0; i < m->num_packs + m->num_packs_in_base; i++) { - if (prepare_midx_pack(r, m, i)) + if (prepare_midx_pack(m, i)) midx_report("failed to load pack in position %d", i); display_progress(progress, i + 1); @@ -996,7 +979,7 @@ int verify_midx_file(struct repository *r, const char *object_dir, unsigned flag nth_midxed_object_oid(&oid, m, pairs[i].pos); - if (!fill_midx_entry(r, &oid, &e, m)) { + if (!fill_midx_entry(m, &oid, &e)) { midx_report(_("failed to load pack entry for oid[%d] = %s"), pairs[i].pos, oid_to_hex(&oid)); continue; @@ -8,6 +8,7 @@ struct pack_entry; struct repository; struct bitmapped_pack; struct git_hash_algo; +struct odb_source; #define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */ #define MIDX_VERSION 1 @@ -34,7 +35,7 @@ struct git_hash_algo; "GIT_TEST_MULTI_PACK_INDEX_WRITE_INCREMENTAL" struct multi_pack_index { - struct multi_pack_index *next; + struct odb_source *source; const unsigned char *data; size_t data_len; @@ -51,7 +52,6 @@ struct multi_pack_index { uint32_t num_objects; int preferred_pack_idx; - int local; int has_chain; const unsigned char *chunk_pack_names; @@ -72,10 +72,6 @@ struct multi_pack_index { const char **pack_names; struct packed_git **packs; - - struct repository *repo; - - char object_dir[FLEX_ARRAY]; }; #define MIDX_PROGRESS (1 << 0) @@ -90,24 +86,19 @@ struct multi_pack_index { #define MIDX_EXT_MIDX "midx" const unsigned char *get_midx_checksum(struct multi_pack_index *m); -void get_midx_filename(const struct git_hash_algo *hash_algo, - struct strbuf *out, const char *object_dir); -void get_midx_filename_ext(const struct git_hash_algo *hash_algo, - struct strbuf *out, const char *object_dir, +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); -void get_midx_chain_dirname(struct strbuf *buf, const char *object_dir); -void get_midx_chain_filename(struct strbuf *buf, const char *object_dir); -void get_split_midx_filename_ext(const struct git_hash_algo *hash_algo, - struct strbuf *buf, const char *object_dir, +void get_midx_chain_dirname(struct odb_source *source, struct strbuf *out); +void get_midx_chain_filename(struct odb_source *source, struct strbuf *out); +void get_split_midx_filename_ext(struct odb_source *source, struct strbuf *buf, const unsigned char *hash, const char *ext); -struct multi_pack_index *load_multi_pack_index(struct repository *r, - const char *object_dir, - int local); -int prepare_midx_pack(struct repository *r, struct multi_pack_index *m, uint32_t pack_int_id); +struct multi_pack_index *load_multi_pack_index(struct odb_source *source); +int prepare_midx_pack(struct multi_pack_index *m, uint32_t pack_int_id); struct packed_git *nth_midxed_pack(struct multi_pack_index *m, uint32_t pack_int_id); -int nth_bitmapped_pack(struct repository *r, struct multi_pack_index *m, +int nth_bitmapped_pack(struct multi_pack_index *m, struct bitmapped_pack *bp, uint32_t pack_int_id); int bsearch_one_midx(const struct object_id *oid, struct multi_pack_index *m, uint32_t *result); @@ -119,27 +110,27 @@ uint32_t nth_midxed_pack_int_id(struct multi_pack_index *m, uint32_t pos); struct object_id *nth_midxed_object_oid(struct object_id *oid, struct multi_pack_index *m, uint32_t n); -int fill_midx_entry(struct repository *r, const struct object_id *oid, struct pack_entry *e, struct multi_pack_index *m); +int fill_midx_entry(struct multi_pack_index *m, const struct object_id *oid, struct pack_entry *e); int midx_contains_pack(struct multi_pack_index *m, const char *idx_or_pack_name); int midx_preferred_pack(struct multi_pack_index *m, uint32_t *pack_int_id); -int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, int local); +int prepare_multi_pack_index_one(struct odb_source *source); /* * Variant of write_midx_file which writes a MIDX containing only the packs * specified in packs_to_include. */ -int write_midx_file(struct repository *r, const char *object_dir, +int write_midx_file(struct odb_source *source, const char *preferred_pack_name, const char *refs_snapshot, unsigned flags); -int write_midx_file_only(struct repository *r, const char *object_dir, +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); void clear_midx_file(struct repository *r); -int verify_midx_file(struct repository *r, const char *object_dir, unsigned flags); -int expire_midx_packs(struct repository *r, const char *object_dir, unsigned flags); -int midx_repack(struct repository *r, const char *object_dir, size_t batch_size, unsigned flags); +int verify_midx_file(struct odb_source *source, unsigned flags); +int expire_midx_packs(struct odb_source *source, unsigned flags); +int midx_repack(struct odb_source *source, size_t batch_size, unsigned flags); void close_midx(struct multi_pack_index *m); diff --git a/notes-cache.c b/notes-cache.c index 150241b15e..bf5bb1f6c1 100644 --- a/notes-cache.c +++ b/notes-cache.c @@ -3,7 +3,7 @@ #include "git-compat-util.h" #include "notes-cache.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "pretty.h" #include "repository.h" #include "commit.h" @@ -87,7 +87,7 @@ char *notes_cache_get(struct notes_cache *c, struct object_id *key_oid, value_oid = get_note(&c->tree, key_oid); if (!value_oid) return NULL; - value = repo_read_object_file(the_repository, value_oid, &type, &size); + value = odb_read_object(the_repository->objects, value_oid, &type, &size); *outsize = size; return value; @@ -98,7 +98,8 @@ int notes_cache_put(struct notes_cache *c, struct object_id *key_oid, { struct object_id value_oid; - if (write_object_file(data, size, OBJ_BLOB, &value_oid) < 0) + if (odb_write_object(the_repository->objects, data, + size, OBJ_BLOB, &value_oid) < 0) return -1; return add_note(&c->tree, key_oid, &value_oid, NULL); } diff --git a/notes-merge.c b/notes-merge.c index dae8e6a281..586939939f 100644 --- a/notes-merge.c +++ b/notes-merge.c @@ -8,7 +8,7 @@ #include "refs.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "path.h" #include "repository.h" #include "diff.h" @@ -340,7 +340,7 @@ static void write_note_to_worktree(const struct object_id *obj, { enum object_type type; unsigned long size; - void *buf = repo_read_object_file(the_repository, note, &type, &size); + void *buf = odb_read_object(the_repository->objects, note, &type, &size); if (!buf) die("cannot read note %s for object %s", diff --git a/notes-utils.c b/notes-utils.c index ac66b82dd3..6a50c6d564 100644 --- a/notes-utils.c +++ b/notes-utils.c @@ -162,7 +162,7 @@ struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd) c->refs_from_env = 1; string_list_add_refs_from_colon_sep(c->refs, rewrite_refs_env); } - git_config(notes_rewrite_config, c); + repo_config(the_repository, notes_rewrite_config, c); if (!c->enabled || !c->refs->nr) { string_list_clear(c->refs, 0); free(c->refs); @@ -8,7 +8,7 @@ #include "notes.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "utf8.h" #include "strbuf.h" #include "tree-walk.h" @@ -682,7 +682,8 @@ static int tree_write_stack_finish_subtree(struct tree_write_stack *tws) ret = tree_write_stack_finish_subtree(n); if (ret) return ret; - ret = write_object_file(n->buf.buf, n->buf.len, OBJ_TREE, &s); + ret = odb_write_object(the_repository->objects, n->buf.buf, + n->buf.len, OBJ_TREE, &s); if (ret) return ret; strbuf_release(&n->buf); @@ -794,8 +795,8 @@ static int prune_notes_helper(const struct object_id *object_oid, struct note_delete_list **l = (struct note_delete_list **) cb_data; struct note_delete_list *n; - if (has_object(the_repository, object_oid, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) + if (odb_has_object(the_repository->objects, object_oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) return 0; /* nothing to do for this note */ /* failed to find object => prune this note */ @@ -816,15 +817,15 @@ int combine_notes_concatenate(struct object_id *cur_oid, /* read in both note blob objects */ if (!is_null_oid(new_oid)) - new_msg = repo_read_object_file(the_repository, new_oid, - &new_type, &new_len); + new_msg = odb_read_object(the_repository->objects, new_oid, + &new_type, &new_len); if (!new_msg || !new_len || new_type != OBJ_BLOB) { free(new_msg); return 0; } if (!is_null_oid(cur_oid)) - cur_msg = repo_read_object_file(the_repository, cur_oid, - &cur_type, &cur_len); + cur_msg = odb_read_object(the_repository->objects, cur_oid, + &cur_type, &cur_len); if (!cur_msg || !cur_len || cur_type != OBJ_BLOB) { free(cur_msg); free(new_msg); @@ -847,7 +848,8 @@ int combine_notes_concatenate(struct object_id *cur_oid, free(new_msg); /* create a new blob object from buf */ - ret = write_object_file(buf, buf_len, OBJ_BLOB, cur_oid); + ret = odb_write_object(the_repository->objects, buf, + buf_len, OBJ_BLOB, cur_oid); free(buf); return ret; } @@ -880,7 +882,7 @@ static int string_list_add_note_lines(struct string_list *list, return 0; /* read_sha1_file NUL-terminates */ - data = repo_read_object_file(the_repository, oid, &t, &len); + data = odb_read_object(the_repository->objects, oid, &t, &len); if (t != OBJ_BLOB || !data || !len) { free(data); return t != OBJ_BLOB || !data; @@ -892,7 +894,7 @@ static int string_list_add_note_lines(struct string_list *list, * later, along with any empty strings that came from empty * lines within the file. */ - string_list_split(list, data, '\n', -1); + string_list_split(list, data, "\n", -1); free(data); return 0; } @@ -927,7 +929,8 @@ int combine_notes_cat_sort_uniq(struct object_id *cur_oid, string_list_join_lines_helper, &buf)) goto out; - ret = write_object_file(buf.buf, buf.len, OBJ_BLOB, cur_oid); + ret = odb_write_object(the_repository->objects, buf.buf, + buf.len, OBJ_BLOB, cur_oid); out: strbuf_release(&buf); @@ -970,8 +973,8 @@ void string_list_add_refs_from_colon_sep(struct string_list *list, char *globs_copy = xstrdup(globs); int i; - string_list_split_in_place(&split, globs_copy, ":", -1); - string_list_remove_empty_items(&split, 0); + string_list_split_in_place_f(&split, globs_copy, ":", -1, + STRING_LIST_SPLIT_NONEMPTY); for (i = 0; i < split.nr; i++) string_list_add_refs_by_glob(list, split.items[i].string); @@ -1123,7 +1126,7 @@ void load_display_notes(struct display_notes_opt *opt) load_config_refs = 1; } - git_config(notes_display_config, &load_config_refs); + repo_config(the_repository, notes_display_config, &load_config_refs); if (opt) { struct string_list_item *item; @@ -1215,7 +1218,8 @@ int write_notes_tree(struct notes_tree *t, struct object_id *result) ret = for_each_note(t, flags, write_each_note, &cb_data) || write_each_non_note_until(NULL, &cb_data) || tree_write_stack_finish_subtree(&root) || - write_object_file(root.buf.buf, root.buf.len, OBJ_TREE, result); + odb_write_object(the_repository->objects, root.buf.buf, + root.buf.len, OBJ_TREE, result); strbuf_release(&root.buf); return ret; } @@ -1290,7 +1294,8 @@ static void format_note(struct notes_tree *t, const struct object_id *object_oid if (!oid) return; - if (!(msg = repo_read_object_file(the_repository, oid, &type, &msglen)) || type != OBJ_BLOB) { + if (!(msg = odb_read_object(the_repository->objects, oid, &type, &msglen)) || + type != OBJ_BLOB) { free(msg); return; } diff --git a/object-file.c b/object-file.c index 1ac04c2891..bc15af4245 100644 --- a/object-file.c +++ b/object-file.c @@ -8,7 +8,6 @@ */ #define USE_THE_REPOSITORY_VARIABLE -#define DISABLE_SIGN_COMPARE_WARNINGS #include "git-compat-util.h" #include "bulk-checkin.h" @@ -21,11 +20,12 @@ #include "loose.h" #include "object-file-convert.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "oidtree.h" #include "pack.h" #include "packfile.h" #include "path.h" +#include "read-cache-ll.h" #include "setup.h" #include "streaming.h" @@ -42,10 +42,11 @@ static int get_conv_flags(unsigned flags) return 0; } -static void fill_loose_path(struct strbuf *buf, const struct object_id *oid) +static void fill_loose_path(struct strbuf *buf, + const struct object_id *oid, + const struct git_hash_algo *algop) { - int i; - for (i = 0; i < the_hash_algo->rawsz; i++) { + for (size_t i = 0; i < algop->rawsz; i++) { static char hex[] = "0123456789abcdef"; unsigned int val = oid->hash[i]; strbuf_addch(buf, hex[val >> 4]); @@ -55,14 +56,14 @@ static void fill_loose_path(struct strbuf *buf, const struct object_id *oid) } } -const char *odb_loose_path(struct object_directory *odb, +const char *odb_loose_path(struct odb_source *source, struct strbuf *buf, const struct object_id *oid) { strbuf_reset(buf); - strbuf_addstr(buf, odb->path); + strbuf_addstr(buf, source->path); strbuf_addch(buf, '/'); - fill_loose_path(buf, oid); + fill_loose_path(buf, oid, source->odb->repo->hash_algo); return buf->buf; } @@ -88,46 +89,19 @@ int check_and_freshen_file(const char *fn, int freshen) return 1; } -static int check_and_freshen_odb(struct object_directory *odb, - const struct object_id *oid, - int freshen) +static int check_and_freshen_source(struct odb_source *source, + const struct object_id *oid, + int freshen) { static struct strbuf path = STRBUF_INIT; - odb_loose_path(odb, &path, oid); + odb_loose_path(source, &path, oid); return check_and_freshen_file(path.buf, freshen); } -static int check_and_freshen_local(const struct object_id *oid, int freshen) +int has_loose_object(struct odb_source *source, + const struct object_id *oid) { - return check_and_freshen_odb(the_repository->objects->odb, oid, freshen); -} - -static int check_and_freshen_nonlocal(const struct object_id *oid, int freshen) -{ - struct object_directory *odb; - - prepare_alt_odb(the_repository); - for (odb = the_repository->objects->odb->next; odb; odb = odb->next) { - if (check_and_freshen_odb(odb, oid, freshen)) - return 1; - } - return 0; -} - -static int check_and_freshen(const struct object_id *oid, int freshen) -{ - return check_and_freshen_local(oid, freshen) || - check_and_freshen_nonlocal(oid, freshen); -} - -int has_loose_object_nonlocal(const struct object_id *oid) -{ - return check_and_freshen_nonlocal(oid, 0); -} - -int has_loose_object(const struct object_id *oid) -{ - return check_and_freshen(oid, 0); + return check_and_freshen_source(source, oid, 0); } int format_object_header(char *str, size_t size, enum object_type type, @@ -202,12 +176,12 @@ int stream_object_signature(struct repository *r, const struct object_id *oid) static int stat_loose_object(struct repository *r, const struct object_id *oid, struct stat *st, const char **path) { - struct object_directory *odb; + struct odb_source *source; static struct strbuf buf = STRBUF_INIT; - prepare_alt_odb(r); - for (odb = r->objects->odb; odb; odb = odb->next) { - *path = odb_loose_path(odb, &buf, oid); + odb_prepare_alternates(r->objects); + for (source = r->objects->sources; source; source = source->next) { + *path = odb_loose_path(source, &buf, oid); if (!lstat(*path, st)) return 0; } @@ -223,13 +197,13 @@ static int open_loose_object(struct repository *r, const struct object_id *oid, const char **path) { int fd; - struct object_directory *odb; + struct odb_source *source; int most_interesting_errno = ENOENT; static struct strbuf buf = STRBUF_INIT; - prepare_alt_odb(r); - for (odb = r->objects->odb; odb; odb = odb->next) { - *path = odb_loose_path(odb, &buf, oid); + odb_prepare_alternates(r->objects); + for (source = r->objects->sources; source; source = source->next) { + *path = odb_loose_path(source, &buf, oid); fd = git_open(*path); if (fd >= 0) return fd; @@ -244,11 +218,11 @@ static int open_loose_object(struct repository *r, static int quick_has_loose(struct repository *r, const struct object_id *oid) { - struct object_directory *odb; + struct odb_source *source; - prepare_alt_odb(r); - for (odb = r->objects->odb; odb; odb = odb->next) { - if (oidtree_contains(odb_loose_cache(odb, oid), oid)) + odb_prepare_alternates(r->objects); + for (source = r->objects->sources; source; source = source->next) { + if (oidtree_contains(odb_loose_cache(source, oid), oid)) return 1; } return 0; @@ -327,9 +301,8 @@ static void *unpack_loose_rest(git_zstream *stream, void *buffer, unsigned long size, const struct object_id *oid) { - int bytes = strlen(buffer) + 1; + size_t bytes = strlen(buffer) + 1, n; unsigned char *buf = xmallocz(size); - unsigned long n; int status = Z_OK; n = stream->total_out - bytes; @@ -448,7 +421,7 @@ int loose_object_info(struct repository *r, enum object_type type_scratch; if (oi->delta_base_oid) - oidclr(oi->delta_base_oid, the_repository->hash_algo); + oidclr(oi->delta_base_oid, r->hash_algo); /* * If we don't care about type or size, then we don't @@ -596,7 +569,7 @@ static int check_collision(const char *source, const char *dest) goto out; } - if (sz_a < sizeof(buf_source)) + if ((size_t) sz_a < sizeof(buf_source)) break; } @@ -611,12 +584,14 @@ out: /* * Move the just written object into its final resting place. */ -int finalize_object_file(const char *tmpfile, const char *filename) +int finalize_object_file(struct repository *repo, + const char *tmpfile, const char *filename) { - return finalize_object_file_flags(tmpfile, filename, 0); + return finalize_object_file_flags(repo, tmpfile, filename, 0); } -int finalize_object_file_flags(const char *tmpfile, const char *filename, +int finalize_object_file_flags(struct repository *repo, + const char *tmpfile, const char *filename, enum finalize_object_file_flags flags) { unsigned retries = 0; @@ -676,7 +651,7 @@ retry: } out: - if (adjust_shared_perm(the_repository, filename)) + if (adjust_shared_perm(repo, filename)) return error(_("unable to set permission to '%s'"), filename); return 0; } @@ -692,13 +667,14 @@ void hash_object_file(const struct git_hash_algo *algo, const void *buf, } /* Finalize a file on disk, and close it. */ -static void close_loose_object(int fd, const char *filename) +static void close_loose_object(struct odb_source *source, + int fd, const char *filename) { - if (the_repository->objects->odb->will_destroy) + if (source->will_destroy) goto out; if (batch_fsync_enabled(FSYNC_COMPONENT_LOOSE_OBJECT)) - fsync_loose_object_bulk_checkin(fd, filename); + fsync_loose_object_bulk_checkin(source->odb->transaction, fd, filename); else if (fsync_object_files > 0) fsync_or_die(fd, filename); else @@ -726,7 +702,8 @@ static inline int directory_size(const char *filename) * We want to avoid cross-directory filename renames, because those * can have problems on various filesystems (FAT, NFS, Coda). */ -static int create_tmpfile(struct strbuf *tmp, const char *filename) +static int create_tmpfile(struct repository *repo, + struct strbuf *tmp, const char *filename) { int fd, dirlen = directory_size(filename); @@ -745,7 +722,7 @@ static int create_tmpfile(struct strbuf *tmp, const char *filename) strbuf_add(tmp, filename, dirlen - 1); if (mkdir(tmp->buf, 0777) && errno != EEXIST) return -1; - if (adjust_shared_perm(the_repository, tmp->buf)) + if (adjust_shared_perm(repo, tmp->buf)) return -1; /* Try again */ @@ -766,26 +743,26 @@ static int create_tmpfile(struct strbuf *tmp, const char *filename) * Returns a "fd", which should later be provided to * end_loose_object_common(). */ -static int start_loose_object_common(struct strbuf *tmp_file, +static int start_loose_object_common(struct odb_source *source, + struct strbuf *tmp_file, const char *filename, unsigned flags, git_zstream *stream, unsigned char *buf, size_t buflen, struct git_hash_ctx *c, struct git_hash_ctx *compat_c, char *hdr, int hdrlen) { - struct repository *repo = the_repository; - const struct git_hash_algo *algo = repo->hash_algo; - const struct git_hash_algo *compat = repo->compat_hash_algo; + const struct git_hash_algo *algo = source->odb->repo->hash_algo; + const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo; int fd; - fd = create_tmpfile(tmp_file, filename); + fd = create_tmpfile(source->odb->repo, tmp_file, filename); if (fd < 0) { - if (flags & WRITE_OBJECT_FILE_SILENT) + if (flags & WRITE_OBJECT_SILENT) return -1; else if (errno == EACCES) return error(_("insufficient permission for adding " "an object to repository database %s"), - repo_get_object_directory(the_repository)); + source->path); else return error_errno( _("unable to create temporary file")); @@ -815,14 +792,14 @@ static int start_loose_object_common(struct strbuf *tmp_file, * Common steps for the inner git_deflate() loop for writing loose * objects. Returns what git_deflate() returns. */ -static int write_loose_object_common(struct git_hash_ctx *c, struct git_hash_ctx *compat_c, +static int write_loose_object_common(struct odb_source *source, + struct git_hash_ctx *c, struct git_hash_ctx *compat_c, git_zstream *stream, const int flush, unsigned char *in0, const int fd, unsigned char *compressed, const size_t compressed_len) { - struct repository *repo = the_repository; - const struct git_hash_algo *compat = repo->compat_hash_algo; + const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo; int ret; ret = git_deflate(stream, flush ? Z_FINISH : 0); @@ -843,12 +820,12 @@ static int write_loose_object_common(struct git_hash_ctx *c, struct git_hash_ctx * - End the compression of zlib stream. * - Get the calculated oid to "oid". */ -static int end_loose_object_common(struct git_hash_ctx *c, struct git_hash_ctx *compat_c, +static int end_loose_object_common(struct odb_source *source, + struct git_hash_ctx *c, struct git_hash_ctx *compat_c, git_zstream *stream, struct object_id *oid, struct object_id *compat_oid) { - struct repository *repo = the_repository; - const struct git_hash_algo *compat = repo->compat_hash_algo; + const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo; int ret; ret = git_deflate_end_gently(stream); @@ -861,7 +838,8 @@ static int end_loose_object_common(struct git_hash_ctx *c, struct git_hash_ctx * return Z_OK; } -static int write_loose_object(const struct object_id *oid, char *hdr, +static int write_loose_object(struct odb_source *source, + const struct object_id *oid, char *hdr, int hdrlen, const void *buf, unsigned long len, time_t mtime, unsigned flags) { @@ -874,11 +852,11 @@ static int write_loose_object(const struct object_id *oid, char *hdr, static struct strbuf filename = STRBUF_INIT; if (batch_fsync_enabled(FSYNC_COMPONENT_LOOSE_OBJECT)) - prepare_loose_object_bulk_checkin(); + prepare_loose_object_bulk_checkin(source->odb->transaction); - odb_loose_path(the_repository->objects->odb, &filename, oid); + odb_loose_path(source, &filename, oid); - fd = start_loose_object_common(&tmp_file, filename.buf, flags, + fd = start_loose_object_common(source, &tmp_file, filename.buf, flags, &stream, compressed, sizeof(compressed), &c, NULL, hdr, hdrlen); if (fd < 0) @@ -890,14 +868,14 @@ static int write_loose_object(const struct object_id *oid, char *hdr, do { unsigned char *in0 = stream.next_in; - ret = write_loose_object_common(&c, NULL, &stream, 1, in0, fd, + ret = write_loose_object_common(source, &c, NULL, &stream, 1, in0, fd, compressed, sizeof(compressed)); } while (ret == Z_OK); if (ret != Z_STREAM_END) die(_("unable to deflate new object %s (%d)"), oid_to_hex(oid), ret); - ret = end_loose_object_common(&c, NULL, &stream, ¶no_oid, NULL); + ret = end_loose_object_common(source, &c, NULL, &stream, ¶no_oid, NULL); if (ret != Z_OK) die(_("deflateEnd on object %s failed (%d)"), oid_to_hex(oid), ret); @@ -905,30 +883,36 @@ static int write_loose_object(const struct object_id *oid, char *hdr, die(_("confused by unstable object source data for %s"), oid_to_hex(oid)); - close_loose_object(fd, tmp_file.buf); + close_loose_object(source, fd, tmp_file.buf); if (mtime) { struct utimbuf utb; utb.actime = mtime; utb.modtime = mtime; if (utime(tmp_file.buf, &utb) < 0 && - !(flags & WRITE_OBJECT_FILE_SILENT)) + !(flags & WRITE_OBJECT_SILENT)) warning_errno(_("failed utime() on %s"), tmp_file.buf); } - return finalize_object_file_flags(tmp_file.buf, filename.buf, + return finalize_object_file_flags(source->odb->repo, tmp_file.buf, filename.buf, FOF_SKIP_COLLISION_CHECK); } -static int freshen_loose_object(const struct object_id *oid) +static int freshen_loose_object(struct object_database *odb, + const struct object_id *oid) { - return check_and_freshen(oid, 1); + odb_prepare_alternates(odb); + for (struct odb_source *source = odb->sources; source; source = source->next) + if (check_and_freshen_source(source, oid, 1)) + return 1; + return 0; } -static int freshen_packed_object(const struct object_id *oid) +static int freshen_packed_object(struct object_database *odb, + const struct object_id *oid) { struct pack_entry e; - if (!find_pack_entry(the_repository, oid, &e)) + if (!find_pack_entry(odb->repo, oid, &e)) return 0; if (e.p->is_cruft) return 0; @@ -940,10 +924,11 @@ static int freshen_packed_object(const struct object_id *oid) return 1; } -int stream_loose_object(struct input_stream *in_stream, size_t len, +int stream_loose_object(struct odb_source *source, + struct input_stream *in_stream, size_t len, struct object_id *oid) { - const struct git_hash_algo *compat = the_repository->compat_hash_algo; + const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo; struct object_id compat_oid; int fd, ret, err = 0, flush = 0; unsigned char compressed[4096]; @@ -956,10 +941,10 @@ int stream_loose_object(struct input_stream *in_stream, size_t len, int hdrlen; if (batch_fsync_enabled(FSYNC_COMPONENT_LOOSE_OBJECT)) - prepare_loose_object_bulk_checkin(); + prepare_loose_object_bulk_checkin(source->odb->transaction); /* Since oid is not determined, save tmp file to odb path. */ - strbuf_addf(&filename, "%s/", repo_get_object_directory(the_repository)); + strbuf_addf(&filename, "%s/", source->path); hdrlen = format_object_header(hdr, sizeof(hdr), OBJ_BLOB, len); /* @@ -970,7 +955,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len, * - Setup zlib stream for compression. * - Start to feed header to zlib stream. */ - fd = start_loose_object_common(&tmp_file, filename.buf, 0, + fd = start_loose_object_common(source, &tmp_file, filename.buf, 0, &stream, compressed, sizeof(compressed), &c, &compat_c, hdr, hdrlen); if (fd < 0) { @@ -990,7 +975,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len, if (in_stream->is_finished) flush = 1; } - ret = write_loose_object_common(&c, &compat_c, &stream, flush, in0, fd, + ret = write_loose_object_common(source, &c, &compat_c, &stream, flush, in0, fd, compressed, sizeof(compressed)); /* * Unlike write_loose_object(), we do not have the entire @@ -1013,17 +998,18 @@ int stream_loose_object(struct input_stream *in_stream, size_t len, */ if (ret != Z_STREAM_END) die(_("unable to stream deflate new object (%d)"), ret); - ret = end_loose_object_common(&c, &compat_c, &stream, oid, &compat_oid); + ret = end_loose_object_common(source, &c, &compat_c, &stream, oid, &compat_oid); if (ret != Z_OK) die(_("deflateEnd on stream object failed (%d)"), ret); - close_loose_object(fd, tmp_file.buf); + close_loose_object(source, fd, tmp_file.buf); - if (freshen_packed_object(oid) || freshen_loose_object(oid)) { + if (freshen_packed_object(source->odb, oid) || + freshen_loose_object(source->odb, oid)) { unlink_or_warn(tmp_file.buf); goto cleanup; } - odb_loose_path(the_repository->objects->odb, &filename, oid); + odb_loose_path(source, &filename, oid); /* We finally know the object path, and create the missing dir. */ dirlen = directory_size(filename.buf); @@ -1031,7 +1017,7 @@ int stream_loose_object(struct input_stream *in_stream, size_t len, struct strbuf dir = STRBUF_INIT; strbuf_add(&dir, filename.buf, dirlen); - if (safe_create_dir_in_gitdir(the_repository, dir.buf) && + if (safe_create_dir_in_gitdir(source->odb->repo, dir.buf) && errno != EEXIST) { err = error_errno(_("unable to create directory %s"), dir.buf); strbuf_release(&dir); @@ -1040,23 +1026,23 @@ int stream_loose_object(struct input_stream *in_stream, size_t len, strbuf_release(&dir); } - err = finalize_object_file_flags(tmp_file.buf, filename.buf, + err = finalize_object_file_flags(source->odb->repo, tmp_file.buf, filename.buf, FOF_SKIP_COLLISION_CHECK); if (!err && compat) - err = repo_add_loose_object_map(the_repository, oid, &compat_oid); + err = repo_add_loose_object_map(source, oid, &compat_oid); cleanup: strbuf_release(&tmp_file); strbuf_release(&filename); return err; } -int write_object_file_flags(const void *buf, unsigned long len, - enum object_type type, struct object_id *oid, - struct object_id *compat_oid_in, unsigned flags) +int write_object_file(struct odb_source *source, + const void *buf, unsigned long len, + enum object_type type, struct object_id *oid, + struct object_id *compat_oid_in, unsigned flags) { - struct repository *repo = the_repository; - const struct git_hash_algo *algo = repo->hash_algo; - const struct git_hash_algo *compat = repo->compat_hash_algo; + const struct git_hash_algo *algo = source->odb->repo->hash_algo; + const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo; struct object_id compat_oid; char hdr[MAX_HEADER_LEN]; int hdrlen = sizeof(hdr); @@ -1069,7 +1055,7 @@ int write_object_file_flags(const void *buf, unsigned long len, hash_object_file(compat, buf, len, type, &compat_oid); else { struct strbuf converted = STRBUF_INIT; - convert_object_file(the_repository, &converted, algo, compat, + convert_object_file(source->odb->repo, &converted, algo, compat, buf, len, type, 0); hash_object_file(compat, converted.buf, converted.len, type, &compat_oid); @@ -1081,19 +1067,20 @@ int write_object_file_flags(const void *buf, unsigned long len, * it out into .git/objects/??/?{38} file. */ write_object_file_prepare(algo, buf, len, type, oid, hdr, &hdrlen); - if (freshen_packed_object(oid) || freshen_loose_object(oid)) + if (freshen_packed_object(source->odb, oid) || + freshen_loose_object(source->odb, oid)) return 0; - if (write_loose_object(oid, hdr, hdrlen, buf, len, 0, flags)) + if (write_loose_object(source, oid, hdr, hdrlen, buf, len, 0, flags)) return -1; if (compat) - return repo_add_loose_object_map(repo, oid, &compat_oid); + return repo_add_loose_object_map(source, oid, &compat_oid); return 0; } -int force_object_loose(const struct object_id *oid, time_t mtime) +int force_object_loose(struct odb_source *source, + const struct object_id *oid, time_t mtime) { - struct repository *repo = the_repository; - const struct git_hash_algo *compat = repo->compat_hash_algo; + const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo; void *buf; unsigned long len; struct object_info oi = OBJECT_INFO_INIT; @@ -1103,22 +1090,24 @@ int force_object_loose(const struct object_id *oid, time_t mtime) int hdrlen; int ret; - if (has_loose_object(oid)) - return 0; + for (struct odb_source *s = source->odb->sources; s; s = s->next) + if (has_loose_object(s, oid)) + return 0; + oi.typep = &type; oi.sizep = &len; oi.contentp = &buf; - if (oid_object_info_extended(the_repository, oid, &oi, 0)) + if (odb_read_object_info_extended(source->odb, oid, &oi, 0)) return error(_("cannot read object for %s"), oid_to_hex(oid)); if (compat) { - if (repo_oid_to_algop(repo, oid, compat, &compat_oid)) + if (repo_oid_to_algop(source->odb->repo, oid, compat, &compat_oid)) return error(_("cannot map object %s to %s"), oid_to_hex(oid), compat->name); } hdrlen = format_object_header(hdr, sizeof(hdr), type, len); - ret = write_loose_object(oid, hdr, hdrlen, buf, len, mtime, 0); + ret = write_loose_object(source, oid, hdr, hdrlen, buf, len, mtime, 0); if (!ret && compat) - ret = repo_add_loose_object_map(the_repository, oid, &compat_oid); + ret = repo_add_loose_object_map(source, oid, &compat_oid); free(buf); return ret; @@ -1168,15 +1157,15 @@ static int index_mem(struct index_state *istate, opts.strict = 1; opts.error_func = hash_format_check_report; - if (fsck_buffer(null_oid(the_hash_algo), type, buf, size, &opts)) + if (fsck_buffer(null_oid(istate->repo->hash_algo), type, buf, size, &opts)) die(_("refusing to create malformed object")); fsck_finish(&opts); } if (write_object) - ret = write_object_file(buf, size, type, oid); + ret = odb_write_object(istate->repo->objects, buf, size, type, oid); else - hash_object_file(the_hash_algo, buf, size, type, oid); + hash_object_file(istate->repo->hash_algo, buf, size, type, oid); strbuf_release(&nbuf); return ret; @@ -1199,10 +1188,10 @@ static int index_stream_convert_blob(struct index_state *istate, get_conv_flags(flags)); if (write_object) - ret = write_object_file(sbuf.buf, sbuf.len, OBJ_BLOB, - oid); + ret = odb_write_object(istate->repo->objects, sbuf.buf, sbuf.len, OBJ_BLOB, + oid); else - hash_object_file(the_hash_algo, sbuf.buf, sbuf.len, OBJ_BLOB, + hash_object_file(istate->repo->hash_algo, sbuf.buf, sbuf.len, OBJ_BLOB, oid); strbuf_release(&sbuf); return ret; @@ -1240,7 +1229,7 @@ static int index_core(struct index_state *istate, if (read_result < 0) ret = error_errno(_("read error while indexing %s"), path ? path : "<unknown>"); - else if (read_result != size) + else if ((size_t) read_result != size) ret = error(_("short read while indexing %s"), path ? path : "<unknown>"); else @@ -1264,18 +1253,26 @@ int index_fd(struct index_state *istate, struct object_id *oid, * Call xsize_t() only when needed to avoid potentially unnecessary * die() for large files. */ - if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(istate, path)) + if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(istate, path)) { ret = index_stream_convert_blob(istate, oid, fd, path, flags); - else if (!S_ISREG(st->st_mode)) + } else if (!S_ISREG(st->st_mode)) { ret = index_pipe(istate, oid, fd, type, path, flags); - else if (st->st_size <= repo_settings_get_big_file_threshold(the_repository) || - type != OBJ_BLOB || - (path && would_convert_to_git(istate, path))) + } else if ((st->st_size >= 0 && + (size_t)st->st_size <= repo_settings_get_big_file_threshold(istate->repo)) || + type != OBJ_BLOB || + (path && would_convert_to_git(istate, path))) { ret = index_core(istate, oid, fd, xsize_t(st->st_size), type, path, flags); - else - ret = index_blob_bulk_checkin(oid, fd, xsize_t(st->st_size), path, - flags); + } else { + struct odb_transaction *transaction; + + transaction = begin_odb_transaction(the_repository->objects); + ret = index_blob_bulk_checkin(transaction, + oid, fd, xsize_t(st->st_size), + path, flags); + end_odb_transaction(transaction); + } + close(fd); return ret; } @@ -1300,14 +1297,14 @@ int index_path(struct index_state *istate, struct object_id *oid, if (strbuf_readlink(&sb, path, st->st_size)) return error_errno("readlink(\"%s\")", path); if (!(flags & INDEX_WRITE_OBJECT)) - hash_object_file(the_hash_algo, sb.buf, sb.len, + hash_object_file(istate->repo->hash_algo, sb.buf, sb.len, OBJ_BLOB, oid); - else if (write_object_file(sb.buf, sb.len, OBJ_BLOB, oid)) + else if (odb_write_object(istate->repo->objects, sb.buf, sb.len, OBJ_BLOB, oid)) rc = error(_("%s: failed to insert into database"), path); strbuf_release(&sb); break; case S_IFDIR: - return repo_resolve_gitlink_ref(the_repository, path, "HEAD", oid); + return repo_resolve_gitlink_ref(istate->repo, path, "HEAD", oid); default: return error(_("%s: unsupported file type"), path); } @@ -1329,12 +1326,13 @@ int read_pack_header(int fd, struct pack_header *header) return 0; } -int for_each_file_in_obj_subdir(unsigned int subdir_nr, - struct strbuf *path, - each_loose_object_fn obj_cb, - each_loose_cruft_fn cruft_cb, - each_loose_subdir_fn subdir_cb, - void *data) +static int for_each_file_in_obj_subdir(unsigned int subdir_nr, + struct strbuf *path, + const struct git_hash_algo *algop, + each_loose_object_fn obj_cb, + each_loose_cruft_fn cruft_cb, + each_loose_subdir_fn subdir_cb, + void *data) { size_t origlen, baselen; DIR *dir; @@ -1367,12 +1365,12 @@ int for_each_file_in_obj_subdir(unsigned int subdir_nr, namelen = strlen(de->d_name); strbuf_setlen(path, baselen); strbuf_add(path, de->d_name, namelen); - if (namelen == the_hash_algo->hexsz - 2 && + if (namelen == algop->hexsz - 2 && !hex_to_bytes(oid.hash + 1, de->d_name, - the_hash_algo->rawsz - 1)) { - oid_set_algo(&oid, the_hash_algo); - memset(oid.hash + the_hash_algo->rawsz, 0, - GIT_MAX_RAWSZ - the_hash_algo->rawsz); + algop->rawsz - 1)) { + oid_set_algo(&oid, algop); + memset(oid.hash + algop->rawsz, 0, + GIT_MAX_RAWSZ - algop->rawsz); if (obj_cb) { r = obj_cb(&oid, path->buf, data); if (r) @@ -1398,26 +1396,7 @@ int for_each_file_in_obj_subdir(unsigned int subdir_nr, return r; } -int for_each_loose_file_in_objdir_buf(struct strbuf *path, - each_loose_object_fn obj_cb, - each_loose_cruft_fn cruft_cb, - each_loose_subdir_fn subdir_cb, - void *data) -{ - int r = 0; - int i; - - for (i = 0; i < 256; i++) { - r = for_each_file_in_obj_subdir(i, path, obj_cb, cruft_cb, - subdir_cb, data); - if (r) - break; - } - - return r; -} - -int for_each_loose_file_in_objdir(const char *path, +int for_each_loose_file_in_source(struct odb_source *source, each_loose_object_fn obj_cb, each_loose_cruft_fn cruft_cb, each_loose_subdir_fn subdir_cb, @@ -1426,22 +1405,27 @@ int for_each_loose_file_in_objdir(const char *path, struct strbuf buf = STRBUF_INIT; int r; - strbuf_addstr(&buf, path); - r = for_each_loose_file_in_objdir_buf(&buf, obj_cb, cruft_cb, - subdir_cb, data); - strbuf_release(&buf); + strbuf_addstr(&buf, source->path); + for (int i = 0; i < 256; i++) { + r = for_each_file_in_obj_subdir(i, &buf, source->odb->repo->hash_algo, + obj_cb, cruft_cb, subdir_cb, data); + if (r) + break; + } + strbuf_release(&buf); return r; } -int for_each_loose_object(each_loose_object_fn cb, void *data, +int for_each_loose_object(struct object_database *odb, + each_loose_object_fn cb, void *data, enum for_each_object_flags flags) { - struct object_directory *odb; + struct odb_source *source; - prepare_alt_odb(the_repository); - for (odb = the_repository->objects->odb; odb; odb = odb->next) { - int r = for_each_loose_file_in_objdir(odb->path, cb, NULL, + odb_prepare_alternates(odb); + for (source = odb->sources; source; source = source->next) { + int r = for_each_loose_file_in_source(source, cb, NULL, NULL, data); if (r) return r; @@ -1461,50 +1445,52 @@ static int append_loose_object(const struct object_id *oid, return 0; } -struct oidtree *odb_loose_cache(struct object_directory *odb, - const struct object_id *oid) +struct oidtree *odb_loose_cache(struct odb_source *source, + const struct object_id *oid) { int subdir_nr = oid->hash[0]; struct strbuf buf = STRBUF_INIT; - size_t word_bits = bitsizeof(odb->loose_objects_subdir_seen[0]); + size_t word_bits = bitsizeof(source->loose_objects_subdir_seen[0]); size_t word_index = subdir_nr / word_bits; size_t mask = (size_t)1u << (subdir_nr % word_bits); uint32_t *bitmap; if (subdir_nr < 0 || - subdir_nr >= bitsizeof(odb->loose_objects_subdir_seen)) + (size_t) subdir_nr >= bitsizeof(source->loose_objects_subdir_seen)) BUG("subdir_nr out of range"); - bitmap = &odb->loose_objects_subdir_seen[word_index]; + bitmap = &source->loose_objects_subdir_seen[word_index]; if (*bitmap & mask) - return odb->loose_objects_cache; - if (!odb->loose_objects_cache) { - ALLOC_ARRAY(odb->loose_objects_cache, 1); - oidtree_init(odb->loose_objects_cache); + return source->loose_objects_cache; + if (!source->loose_objects_cache) { + ALLOC_ARRAY(source->loose_objects_cache, 1); + oidtree_init(source->loose_objects_cache); } - strbuf_addstr(&buf, odb->path); + strbuf_addstr(&buf, source->path); for_each_file_in_obj_subdir(subdir_nr, &buf, + source->odb->repo->hash_algo, append_loose_object, NULL, NULL, - odb->loose_objects_cache); + source->loose_objects_cache); *bitmap |= mask; strbuf_release(&buf); - return odb->loose_objects_cache; + return source->loose_objects_cache; } -void odb_clear_loose_cache(struct object_directory *odb) +void odb_clear_loose_cache(struct odb_source *source) { - oidtree_clear(odb->loose_objects_cache); - FREE_AND_NULL(odb->loose_objects_cache); - memset(&odb->loose_objects_subdir_seen, 0, - sizeof(odb->loose_objects_subdir_seen)); + oidtree_clear(source->loose_objects_cache); + FREE_AND_NULL(source->loose_objects_cache); + memset(&source->loose_objects_subdir_seen, 0, + sizeof(source->loose_objects_subdir_seen)); } static int check_stream_oid(git_zstream *stream, const char *hdr, unsigned long size, const char *path, - const struct object_id *expected_oid) + const struct object_id *expected_oid, + const struct git_hash_algo *algop) { struct git_hash_ctx c; struct object_id real_oid; @@ -1512,7 +1498,7 @@ static int check_stream_oid(git_zstream *stream, unsigned long total_read; int status = Z_OK; - the_hash_algo->init_fn(&c); + algop->init_fn(&c); git_hash_update(&c, hdr, stream->total_out); /* @@ -1557,7 +1543,8 @@ static int check_stream_oid(git_zstream *stream, return 0; } -int read_loose_object(const char *path, +int read_loose_object(struct repository *repo, + const char *path, const struct object_id *expected_oid, struct object_id *real_oid, void **contents, @@ -1596,8 +1583,9 @@ int read_loose_object(const char *path, } if (*oi->typep == OBJ_BLOB && - *size > repo_settings_get_big_file_threshold(the_repository)) { - if (check_stream_oid(&stream, hdr, *size, path, expected_oid) < 0) + *size > repo_settings_get_big_file_threshold(repo)) { + if (check_stream_oid(&stream, hdr, *size, path, expected_oid, + repo->hash_algo) < 0) goto out_inflate; } else { *contents = unpack_loose_rest(&stream, hdr, *size, expected_oid); @@ -1605,7 +1593,7 @@ int read_loose_object(const char *path, error(_("unable to unpack contents of %s"), path); goto out_inflate; } - hash_object_file(the_repository->hash_algo, + hash_object_file(repo->hash_algo, *contents, *size, *oi->typep, real_oid); if (!oideq(expected_oid, real_oid)) diff --git a/object-file.h b/object-file.h index 6f41142452..15d97630d3 100644 --- a/object-file.h +++ b/object-file.h @@ -3,12 +3,12 @@ #include "git-zlib.h" #include "object.h" -#include "object-store.h" +#include "odb.h" struct index_state; /* - * Set this to 0 to prevent oid_object_info_extended() from fetching missing + * Set this to 0 to prevent odb_read_object_info_extended() from fetching missing * blobs. This has a difference only if extensions.partialClone is set. * * Its default value is 1. @@ -24,34 +24,33 @@ enum { int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags); int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags); -struct object_directory; +struct odb_source; /* * Populate and return the loose object cache array corresponding to the * given object ID. */ -struct oidtree *odb_loose_cache(struct object_directory *odb, +struct oidtree *odb_loose_cache(struct odb_source *source, const struct object_id *oid); /* Empty the loose object cache for the specified object directory. */ -void odb_clear_loose_cache(struct object_directory *odb); +void odb_clear_loose_cache(struct odb_source *source); /* * 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. */ -const char *odb_loose_path(struct object_directory *odb, +const char *odb_loose_path(struct odb_source *source, struct strbuf *buf, const struct object_id *oid); /* - * Return true iff an alternate object database has a loose object + * Return true iff an object database source has a loose object * with the specified name. This function does not respect replace * references. */ -int has_loose_object_nonlocal(const struct object_id *); - -int has_loose_object(const struct object_id *); +int has_loose_object(struct odb_source *source, + const struct object_id *oid); void *map_loose_object(struct repository *r, const struct object_id *oid, unsigned long *size); @@ -87,22 +86,11 @@ typedef int each_loose_cruft_fn(const char *basename, typedef int each_loose_subdir_fn(unsigned int nr, const char *path, void *data); -int for_each_file_in_obj_subdir(unsigned int subdir_nr, - struct strbuf *path, - each_loose_object_fn obj_cb, - each_loose_cruft_fn cruft_cb, - each_loose_subdir_fn subdir_cb, - void *data); -int for_each_loose_file_in_objdir(const char *path, +int for_each_loose_file_in_source(struct odb_source *source, each_loose_object_fn obj_cb, each_loose_cruft_fn cruft_cb, each_loose_subdir_fn subdir_cb, void *data); -int for_each_loose_file_in_objdir_buf(struct strbuf *path, - each_loose_object_fn obj_cb, - each_loose_cruft_fn cruft_cb, - each_loose_subdir_fn subdir_cb, - void *data); /* * Iterate over all accessible loose objects without respect to @@ -111,7 +99,8 @@ int for_each_loose_file_in_objdir_buf(struct strbuf *path, * * Any flags specific to packs are ignored. */ -int for_each_loose_object(each_loose_object_fn, void *, +int for_each_loose_object(struct object_database *odb, + each_loose_object_fn, void *, enum for_each_object_flags flags); @@ -157,29 +146,10 @@ enum unpack_loose_header_result unpack_loose_header(git_zstream *stream, struct object_info; int parse_loose_header(const char *hdr, struct object_info *oi); -enum { - /* - * By default, `write_object_file()` does not actually write - * anything into the object store, but only computes the object ID. - * This flag changes that so that the object will be written as a loose - * object and persisted. - */ - WRITE_OBJECT_FILE_PERSIST = (1 << 0), - - /* - * Do not print an error in case something gose wrong. - */ - WRITE_OBJECT_FILE_SILENT = (1 << 1), -}; - -int write_object_file_flags(const void *buf, unsigned long len, - enum object_type type, struct object_id *oid, - struct object_id *compat_oid_in, unsigned flags); -static inline int write_object_file(const void *buf, unsigned long len, - enum object_type type, struct object_id *oid) -{ - return write_object_file_flags(buf, len, type, oid, NULL, 0); -} +int write_object_file(struct odb_source *source, + const void *buf, unsigned long len, + enum object_type type, struct object_id *oid, + struct object_id *compat_oid_in, unsigned flags); struct input_stream { const void *(*read)(struct input_stream *, unsigned long *len); @@ -187,10 +157,12 @@ struct input_stream { int is_finished; }; -int stream_loose_object(struct input_stream *in_stream, size_t len, +int stream_loose_object(struct odb_source *source, + struct input_stream *in_stream, size_t len, struct object_id *oid); -int force_object_loose(const struct object_id *oid, time_t mtime); +int force_object_loose(struct odb_source *source, + const struct object_id *oid, time_t mtime); /** * With in-core object data in "buf", rehash it to make sure the @@ -218,8 +190,10 @@ enum finalize_object_file_flags { FOF_SKIP_COLLISION_CHECK = 1, }; -int finalize_object_file(const char *tmpfile, const char *filename); -int finalize_object_file_flags(const char *tmpfile, const char *filename, +int finalize_object_file(struct repository *repo, + const char *tmpfile, const char *filename); +int finalize_object_file_flags(struct repository *repo, + const char *tmpfile, const char *filename, enum finalize_object_file_flags flags); void hash_object_file(const struct git_hash_algo *algo, const void *buf, @@ -237,7 +211,8 @@ int check_and_freshen_file(const char *fn, int freshen); * * Returns 0 on success, negative on error (details may be written to stderr). */ -int read_loose_object(const char *path, +int read_loose_object(struct repository *repo, + const char *path, const struct object_id *expected_oid, struct object_id *real_oid, void **contents, diff --git a/object-name.c b/object-name.c index 9288b2dd24..7774991d28 100644 --- a/object-name.c +++ b/object-name.c @@ -28,6 +28,7 @@ #include "commit-reach.h" #include "date.h" #include "object-file-convert.h" +#include "prio-queue.h" static int get_oid_oneline(struct repository *r, const char *, struct object_id *, const struct commit_list *); @@ -112,10 +113,10 @@ static enum cb_next match_prefix(const struct object_id *oid, void *arg) static void find_short_object_filename(struct disambiguate_state *ds) { - struct object_directory *odb; + struct odb_source *source; - for (odb = ds->repo->objects->odb; odb && !ds->ambiguous; odb = odb->next) - oidtree_each(odb_loose_cache(odb, &ds->bin_pfx), + for (source = ds->repo->objects->sources; source && !ds->ambiguous; source = source->next) + oidtree_each(odb_loose_cache(source, &ds->bin_pfx), &ds->bin_pfx, ds->len, match_prefix, ds); } @@ -198,16 +199,20 @@ static void unique_in_pack(struct packed_git *p, static void find_short_packed_object(struct disambiguate_state *ds) { - struct multi_pack_index *m; + 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; - for (m = get_multi_pack_index(ds->repo); m && !ds->ambiguous; - m = m->next) - unique_in_midx(m, ds); + 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); + } + for (p = get_packed_git(ds->repo); p && !ds->ambiguous; p = p->next) unique_in_pack(p, ds); @@ -251,7 +256,7 @@ static int disambiguate_commit_only(struct repository *r, const struct object_id *oid, void *cb_data UNUSED) { - int kind = oid_object_info(r, oid, NULL); + int kind = odb_read_object_info(r->objects, oid, NULL); return kind == OBJ_COMMIT; } @@ -262,7 +267,7 @@ static int disambiguate_committish_only(struct repository *r, struct object *obj; int kind; - kind = oid_object_info(r, oid, NULL); + kind = odb_read_object_info(r->objects, oid, NULL); if (kind == OBJ_COMMIT) return 1; if (kind != OBJ_TAG) @@ -279,7 +284,7 @@ static int disambiguate_tree_only(struct repository *r, const struct object_id *oid, void *cb_data UNUSED) { - int kind = oid_object_info(r, oid, NULL); + int kind = odb_read_object_info(r->objects, oid, NULL); return kind == OBJ_TREE; } @@ -290,7 +295,7 @@ static int disambiguate_treeish_only(struct repository *r, struct object *obj; int kind; - kind = oid_object_info(r, oid, NULL); + kind = odb_read_object_info(r->objects, oid, NULL); if (kind == OBJ_TREE || kind == OBJ_COMMIT) return 1; if (kind != OBJ_TAG) @@ -307,7 +312,7 @@ static int disambiguate_blob_only(struct repository *r, const struct object_id *oid, void *cb_data UNUSED) { - int kind = oid_object_info(r, oid, NULL); + int kind = odb_read_object_info(r->objects, oid, NULL); return kind == OBJ_BLOB; } @@ -376,7 +381,7 @@ static int init_object_disambiguation(struct repository *r, ds->hex_pfx[len] = '\0'; ds->repo = r; ds->bin_pfx.algo = algo ? hash_algo_by_ptr(algo) : GIT_HASH_UNKNOWN; - prepare_alt_odb(r); + odb_prepare_alternates(r->objects); return 0; } @@ -399,7 +404,7 @@ static int show_ambiguous_object(const struct object_id *oid, void *data) return 0; hash = repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV); - type = oid_object_info(ds->repo, oid, NULL); + type = odb_read_object_info(ds->repo->objects, oid, NULL); if (type < 0) { /* @@ -514,8 +519,8 @@ static int sort_ambiguous(const void *va, const void *vb, void *ctx) { struct repository *sort_ambiguous_repo = ctx; const struct object_id *a = va, *b = vb; - int a_type = oid_object_info(sort_ambiguous_repo, a, NULL); - int b_type = oid_object_info(sort_ambiguous_repo, b, NULL); + int a_type = odb_read_object_info(sort_ambiguous_repo->objects, a, NULL); + int b_type = odb_read_object_info(sort_ambiguous_repo->objects, b, NULL); int a_type_sort; int b_type_sort; @@ -691,15 +696,14 @@ static inline char get_hex_char_from_oid(const struct object_id *oid, return hex[oid->hash[pos >> 1] & 0xf]; } -static int extend_abbrev_len(const struct object_id *oid, void *cb_data) +static int extend_abbrev_len(const struct object_id *oid, + struct min_abbrev_data *mad) { - struct min_abbrev_data *mad = cb_data; - unsigned int i = mad->init_len; while (mad->hex[i] && mad->hex[i] == get_hex_char_from_oid(oid, i)) i++; - if (i < GIT_MAX_RAWSZ && i >= mad->cur_len) + if (mad->hex[i] && i >= mad->cur_len) mad->cur_len = i + 1; return 0; @@ -792,11 +796,15 @@ static void find_abbrev_len_for_pack(struct packed_git *p, static void find_abbrev_len_packed(struct min_abbrev_data *mad) { - struct multi_pack_index *m; struct packed_git *p; - for (m = get_multi_pack_index(mad->repo); m; m = m->next) - find_abbrev_len_for_midx(m, mad); + 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); + } + for (p = get_packed_git(mad->repo); p; p = p->next) find_abbrev_len_for_pack(p, mad); } @@ -1081,13 +1089,17 @@ static int get_oid_basic(struct repository *r, const char *str, int len, * still fill in the oid with the "old" value, * which we can use. */ - } else { + } else if (!(flags & GET_OID_GENTLY)) { if (flags & GET_OID_QUIETLY) { exit(128); } die(_("log for '%.*s' only has %d entries"), len, str, co_cnt); } + if (flags & GET_OID_GENTLY) { + free(real_ref); + return -1; + } } } @@ -1457,7 +1469,7 @@ static int get_oid_oneline(struct repository *r, const char *prefix, struct object_id *oid, const struct commit_list *list) { - struct commit_list *copy = NULL, **copy_tail = © + struct prio_queue copy = { compare_commits_by_commit_date }; const struct commit_list *l; int found = 0; int negative = 0; @@ -1479,9 +1491,9 @@ static int get_oid_oneline(struct repository *r, for (l = list; l; l = l->next) { l->item->object.flags |= ONELINE_SEEN; - copy_tail = &commit_list_insert(l->item, copy_tail)->next; + prio_queue_put(©, l->item); } - while (copy) { + while (copy.nr) { const char *p, *buf; struct commit *commit; int matches; @@ -1503,7 +1515,7 @@ static int get_oid_oneline(struct repository *r, regfree(®ex); for (l = list; l; l = l->next) clear_commit_marks(l->item, ONELINE_SEEN); - free_commit_list(copy); + clear_prio_queue(©); return found ? 0 : -1; } @@ -1512,7 +1524,8 @@ struct grab_nth_branch_switch_cbdata { struct strbuf *sb; }; -static int grab_nth_branch_switch(struct object_id *ooid UNUSED, +static int grab_nth_branch_switch(const char *refname UNUSED, + struct object_id *ooid UNUSED, struct object_id *noid UNUSED, const char *email UNUSED, timestamp_t timestamp UNUSED, @@ -1844,55 +1857,35 @@ int repo_get_oid_committish(struct repository *r, const char *name, struct object_id *oid) { - struct object_context unused; - int ret = get_oid_with_context(r, name, GET_OID_COMMITTISH, - oid, &unused); - object_context_release(&unused); - return ret; + return repo_get_oid_with_flags(r, name, oid, GET_OID_COMMITTISH); } int repo_get_oid_treeish(struct repository *r, const char *name, struct object_id *oid) { - struct object_context unused; - int ret = get_oid_with_context(r, name, GET_OID_TREEISH, - oid, &unused); - object_context_release(&unused); - return ret; + return repo_get_oid_with_flags(r, name, oid, GET_OID_TREEISH); } int repo_get_oid_commit(struct repository *r, const char *name, struct object_id *oid) { - struct object_context unused; - int ret = get_oid_with_context(r, name, GET_OID_COMMIT, - oid, &unused); - object_context_release(&unused); - return ret; + return repo_get_oid_with_flags(r, name, oid, GET_OID_COMMIT); } int repo_get_oid_tree(struct repository *r, const char *name, struct object_id *oid) { - struct object_context unused; - int ret = get_oid_with_context(r, name, GET_OID_TREE, - oid, &unused); - object_context_release(&unused); - return ret; + return repo_get_oid_with_flags(r, name, oid, GET_OID_TREE); } int repo_get_oid_blob(struct repository *r, const char *name, struct object_id *oid) { - struct object_context unused; - int ret = get_oid_with_context(r, name, GET_OID_BLOB, - oid, &unused); - object_context_release(&unused); - return ret; + return repo_get_oid_with_flags(r, name, oid, GET_OID_BLOB); } /* Must be called only when object_name:filename doesn't exist. */ @@ -2057,7 +2050,6 @@ static enum get_oid_result get_oid_with_context_1(struct repository *repo, cb.list = &list; refs_for_each_ref(get_main_ref_store(repo), handle_one_ref, &cb); refs_head_ref(get_main_ref_store(repo), handle_one_ref, &cb); - commit_list_sort_by_date(&list); ret = get_oid_oneline(repo, name + 2, oid, list); free_commit_list(list); diff --git a/object-store.h b/object-store.h deleted file mode 100644 index c589008535..0000000000 --- a/object-store.h +++ /dev/null @@ -1,338 +0,0 @@ -#ifndef OBJECT_STORE_H -#define OBJECT_STORE_H - -#include "hashmap.h" -#include "object.h" -#include "list.h" -#include "oidset.h" -#include "oidmap.h" -#include "thread-utils.h" - -struct oidmap; -struct oidtree; -struct strbuf; -struct repository; - -struct object_directory { - struct object_directory *next; - - /* - * Used to store the results of readdir(3) calls when we are OK - * sacrificing accuracy due to races for speed. That includes - * object existence with OBJECT_INFO_QUICK, as well as - * our search for unique abbreviated hashes. Don't use it for tasks - * requiring greater accuracy! - * - * Be sure to call odb_load_loose_cache() before using. - */ - uint32_t loose_objects_subdir_seen[8]; /* 256 bits */ - struct oidtree *loose_objects_cache; - - /* Map between object IDs for loose objects. */ - struct loose_object_map *loose_map; - - /* - * This is a temporary object store created by the tmp_objdir - * facility. Disable ref updates since the objects in the store - * might be discarded on rollback. - */ - int disable_ref_updates; - - /* - * This object store is ephemeral, so there is no need to fsync. - */ - int will_destroy; - - /* - * Path to the alternative object store. If this is a relative path, - * it is relative to the current working directory. - */ - char *path; -}; - -void prepare_alt_odb(struct repository *r); -int has_alt_odb(struct repository *r); -char *compute_alternate_path(const char *path, struct strbuf *err); -struct object_directory *find_odb(struct repository *r, const char *obj_dir); -typedef int alt_odb_fn(struct object_directory *, void *); -int foreach_alt_odb(alt_odb_fn, void*); -typedef void alternate_ref_fn(const struct object_id *oid, void *); -void for_each_alternate_ref(alternate_ref_fn, void *); - -/* - * Add the directory to the on-disk alternates file; the new entry will also - * take effect in the current process. - */ -void add_to_alternates_file(const char *dir); - -/* - * Add the directory to the in-memory list of alternates (along with any - * recursive alternates it points to), but do not modify the on-disk alternates - * file. - */ -void add_to_alternates_memory(const char *dir); - -/* - * Replace the current writable object directory with the specified temporary - * object directory; returns the former primary object directory. - */ -struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy); - -/* - * Restore a previous ODB replaced by set_temporary_main_odb. - */ -void restore_primary_odb(struct object_directory *restore_odb, const char *old_path); - -struct packed_git; -struct multi_pack_index; -struct cached_object_entry; - -struct raw_object_store { - /* - * Set of all object directories; the main directory is first (and - * cannot be NULL after initialization). Subsequent directories are - * alternates. - */ - struct object_directory *odb; - struct object_directory **odb_tail; - struct kh_odb_path_map *odb_by_path; - - int loaded_alternates; - - /* - * A list of alternate object directories loaded from the environment; - * this should not generally need to be accessed directly, but will - * populate the "odb" list when prepare_alt_odb() is run. - */ - char *alternate_db; - - /* - * Objects that should be substituted by other objects - * (see git-replace(1)). - */ - struct oidmap replace_map; - unsigned replace_map_initialized : 1; - pthread_mutex_t replace_mutex; /* protect object replace functions */ - - struct commit_graph *commit_graph; - unsigned commit_graph_attempted : 1; /* if loading has been attempted */ - - /* - * private data - * - * should only be accessed directly by packfile.c and midx.c - */ - struct multi_pack_index *multi_pack_index; - - /* - * private data - * - * should only be accessed directly by packfile.c - */ - - struct packed_git *packed_git; - /* A most-recently-used ordered version of the packed_git list. */ - struct list_head packed_git_mru; - - struct { - struct packed_git **packs; - unsigned flags; - } kept_pack_cache; - - /* - * This is meant to hold a *small* number of objects that you would - * want repo_read_object_file() to be able to return, but yet you do not want - * to write them into the object store (e.g. a browse-only - * application). - */ - struct cached_object_entry *cached_objects; - size_t cached_object_nr, cached_object_alloc; - - /* - * A map of packfiles to packed_git structs for tracking which - * packs have been loaded already. - */ - struct hashmap pack_map; - - /* - * 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. - */ - unsigned long approximate_object_count; - unsigned approximate_object_count_valid : 1; - - /* - * Whether packed_git has already been populated with this repository's - * packs. - */ - unsigned packed_git_initialized : 1; -}; - -struct raw_object_store *raw_object_store_new(void); -void raw_object_store_clear(struct raw_object_store *o); - -/* - * Create a temporary file rooted in the object database directory, or - * die on failure. The filename is taken from "pattern", which should have the - * usual "XXXXXX" trailer, and the resulting filename is written into the - * "template" buffer. Returns the open descriptor. - */ -int odb_mkstemp(struct strbuf *temp_filename, const char *pattern); - -void *repo_read_object_file(struct repository *r, - const struct object_id *oid, - enum object_type *type, - unsigned long *size); - -/* Read and unpack an object file into memory, write memory to an object file */ -int oid_object_info(struct repository *r, const struct object_id *, unsigned long *); - -/* - * Add an object file to the in-memory object store, without writing it - * to disk. - * - * Callers are responsible for calling write_object_file to record the - * object in persistent storage before writing any other new objects - * that reference it. - */ -int pretend_object_file(struct repository *repo, - void *buf, unsigned long len, enum object_type type, - struct object_id *oid); - -struct object_info { - /* Request */ - enum object_type *typep; - unsigned long *sizep; - off_t *disk_sizep; - struct object_id *delta_base_oid; - void **contentp; - - /* Response */ - enum { - OI_CACHED, - OI_LOOSE, - OI_PACKED, - OI_DBCACHED - } whence; - union { - /* - * struct { - * ... Nothing to expose in this case - * } cached; - * struct { - * ... Nothing to expose in this case - * } loose; - */ - struct { - struct packed_git *pack; - off_t offset; - unsigned int is_delta; - } packed; - } u; -}; - -/* - * Initializer for a "struct object_info" that wants no items. You may - * also memset() the memory to all-zeroes. - */ -#define OBJECT_INFO_INIT { 0 } - -/* Invoke lookup_replace_object() on the given hash */ -#define OBJECT_INFO_LOOKUP_REPLACE 1 -/* Do not retry packed storage after checking packed and loose storage */ -#define OBJECT_INFO_QUICK 8 -/* - * Do not attempt to fetch the object if missing (even if fetch_is_missing is - * nonzero). - */ -#define OBJECT_INFO_SKIP_FETCH_OBJECT 16 -/* - * This is meant for bulk prefetching of missing blobs in a partial - * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK - */ -#define OBJECT_INFO_FOR_PREFETCH (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK) - -/* Die if object corruption (not just an object being missing) was detected. */ -#define OBJECT_INFO_DIE_IF_CORRUPT 32 - -int oid_object_info_extended(struct repository *r, - const struct object_id *, - struct object_info *, unsigned flags); - -enum { - /* Retry packed storage after checking packed and loose storage */ - HAS_OBJECT_RECHECK_PACKED = (1 << 0), - /* Allow fetching the object in case the repository has a promisor remote. */ - HAS_OBJECT_FETCH_PROMISOR = (1 << 1), -}; - -/* - * Returns 1 if the object exists. This function will not lazily fetch objects - * in a partial clone by default. - */ -int has_object(struct repository *r, const struct object_id *oid, - unsigned flags); - -void assert_oid_type(const struct object_id *oid, enum object_type expect); - -/* - * Enabling the object read lock allows multiple threads to safely call the - * following functions in parallel: repo_read_object_file(), - * read_object_with_reference(), oid_object_info() and oid_object_info_extended(). - * - * obj_read_lock() and obj_read_unlock() may also be used to protect other - * section which cannot execute in parallel with object reading. Since the used - * lock is a recursive mutex, these sections can even contain calls to object - * reading functions. However, beware that in these cases zlib inflation won't - * be performed in parallel, losing performance. - * - * TODO: oid_object_info_extended()'s call stack has a recursive behavior. If - * any of its callees end up calling it, this recursive call won't benefit from - * parallel inflation. - */ -void enable_obj_read_lock(void); -void disable_obj_read_lock(void); - -extern int obj_read_use_lock; -extern pthread_mutex_t obj_read_mutex; - -static inline void obj_read_lock(void) -{ - if(obj_read_use_lock) - pthread_mutex_lock(&obj_read_mutex); -} - -static inline void obj_read_unlock(void) -{ - if(obj_read_use_lock) - pthread_mutex_unlock(&obj_read_mutex); -} -/* Flags for for_each_*_object(). */ -enum for_each_object_flags { - /* Iterate only over local objects, not alternates. */ - FOR_EACH_OBJECT_LOCAL_ONLY = (1<<0), - - /* Only iterate over packs obtained from the promisor remote. */ - FOR_EACH_OBJECT_PROMISOR_ONLY = (1<<1), - - /* - * Visit objects within a pack in packfile order rather than .idx order - */ - FOR_EACH_OBJECT_PACK_ORDER = (1<<2), - - /* Only iterate over packs that are not marked as kept in-core. */ - FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS = (1<<3), - - /* Only iterate over packs that do not have .keep files. */ - FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS = (1<<4), -}; - - -void *read_object_with_reference(struct repository *r, - const struct object_id *oid, - enum object_type required_type, - unsigned long *size, - struct object_id *oid_ret); - -#endif /* OBJECT_STORE_H */ @@ -214,7 +214,7 @@ enum peel_status peel_object(struct repository *r, struct object *o = lookup_unknown_object(r, name); if (o->type == OBJ_NONE) { - int type = oid_object_info(r, name, NULL); + int type = odb_read_object_info(r->objects, name, NULL); if (type < 0 || !object_as_type(o, type, 0)) return PEEL_INVALID; } @@ -315,7 +315,7 @@ struct object *parse_object_with_flags(struct repository *r, } if ((!obj || obj->type == OBJ_BLOB) && - oid_object_info(r, oid, NULL) == OBJ_BLOB) { + odb_read_object_info(r->objects, oid, NULL) == OBJ_BLOB) { if (!skip_hash && stream_object_signature(r, repl) < 0) { error(_("hash mismatch %s"), oid_to_hex(oid)); return NULL; @@ -331,11 +331,11 @@ struct object *parse_object_with_flags(struct repository *r, */ if (skip_hash && discard_tree && (!obj || obj->type == OBJ_TREE) && - oid_object_info(r, oid, NULL) == OBJ_TREE) { + odb_read_object_info(r->objects, oid, NULL) == OBJ_TREE) { return &lookup_tree(r, oid)->object; } - buffer = repo_read_object_file(r, oid, &type, &size); + buffer = odb_read_object(r->objects, oid, &type, &size); if (buffer) { if (!skip_hash && check_object_signature(r, repl, buffer, size, type) < 0) { @@ -517,12 +517,11 @@ struct parsed_object_pool *parsed_object_pool_new(struct repository *repo) memset(o, 0, sizeof(*o)); o->repo = repo; - o->blob_state = allocate_alloc_state(); - o->tree_state = allocate_alloc_state(); - o->commit_state = allocate_alloc_state(); - o->tag_state = allocate_alloc_state(); - o->object_state = allocate_alloc_state(); - + o->blob_state = alloc_state_alloc(); + o->tree_state = alloc_state_alloc(); + o->commit_state = alloc_state_alloc(); + o->tag_state = alloc_state_alloc(); + o->object_state = alloc_state_alloc(); o->is_shallow = -1; CALLOC_ARRAY(o->shallow_stat, 1); @@ -573,16 +572,11 @@ void parsed_object_pool_clear(struct parsed_object_pool *o) o->buffer_slab = NULL; parsed_object_pool_reset_commit_grafts(o); - clear_alloc_state(o->blob_state); - clear_alloc_state(o->tree_state); - clear_alloc_state(o->commit_state); - clear_alloc_state(o->tag_state); - clear_alloc_state(o->object_state); + alloc_state_free_and_null(&o->blob_state); + alloc_state_free_and_null(&o->tree_state); + alloc_state_free_and_null(&o->commit_state); + alloc_state_free_and_null(&o->tag_state); + alloc_state_free_and_null(&o->object_state); stat_validity_clear(o->shallow_stat); - FREE_AND_NULL(o->blob_state); - FREE_AND_NULL(o->tree_state); - FREE_AND_NULL(o->commit_state); - FREE_AND_NULL(o->tag_state); - FREE_AND_NULL(o->object_state); FREE_AND_NULL(o->shallow_stat); } diff --git a/object-store.c b/odb.c index 58cde0313a..75c443fe66 100644 --- a/object-store.c +++ b/odb.c @@ -1,5 +1,3 @@ -#define USE_THE_REPOSITORY_VARIABLE - #include "git-compat-util.h" #include "abspath.h" #include "commit-graph.h" @@ -13,7 +11,7 @@ #include "loose.h" #include "object-file-convert.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "packfile.h" #include "path.h" #include "promisor-remote.h" @@ -24,14 +22,15 @@ #include "strbuf.h" #include "strvec.h" #include "submodule.h" +#include "trace2.h" #include "write-or-die.h" KHASH_INIT(odb_path_map, const char * /* key: odb_path */, - struct object_directory *, 1, fspathhash, fspatheq) + struct odb_source *, 1, fspathhash, fspatheq) /* * This is meant to hold a *small* number of objects that you would - * want repo_read_object_file() to be able to return, but yet you do not want + * want odb_read_object() to be able to return, but yet you do not want * to write them into the object store (e.g. a browse-only * application). */ @@ -44,7 +43,7 @@ struct cached_object_entry { } value; }; -static const struct cached_object *find_cached_object(struct raw_object_store *object_store, +static const struct cached_object *find_cached_object(struct object_database *object_store, const struct object_id *oid) { static const struct cached_object empty_tree = { @@ -63,7 +62,8 @@ static const struct cached_object *find_cached_object(struct raw_object_store *o return NULL; } -int odb_mkstemp(struct strbuf *temp_filename, const char *pattern) +int odb_mkstemp(struct object_database *odb, + struct strbuf *temp_filename, const char *pattern) { int fd; /* @@ -71,22 +71,22 @@ int odb_mkstemp(struct strbuf *temp_filename, const char *pattern) * restrictive except to remove write permission. */ int mode = 0444; - repo_git_path_replace(the_repository, temp_filename, "objects/%s", pattern); + repo_git_path_replace(odb->repo, temp_filename, "objects/%s", pattern); fd = git_mkstemp_mode(temp_filename->buf, mode); if (0 <= fd) return fd; /* slow path */ /* some mkstemp implementations erase temp_filename on failure */ - repo_git_path_replace(the_repository, temp_filename, "objects/%s", pattern); - safe_create_leading_directories(the_repository, temp_filename->buf); + repo_git_path_replace(odb->repo, temp_filename, "objects/%s", pattern); + safe_create_leading_directories(odb->repo, temp_filename->buf); return xmkstemp_mode(temp_filename->buf, mode); } /* * Return non-zero iff the path is usable as an alternate object database. */ -static int alt_odb_usable(struct raw_object_store *o, +static int alt_odb_usable(struct object_database *o, struct strbuf *path, const char *normalized_objdir, khiter_t *pos) { @@ -104,18 +104,18 @@ static int alt_odb_usable(struct raw_object_store *o, * Prevent the common mistake of listing the same * thing twice, or object directory itself. */ - if (!o->odb_by_path) { + if (!o->source_by_path) { khiter_t p; - o->odb_by_path = kh_init_odb_path_map(); - assert(!o->odb->next); - p = kh_put_odb_path_map(o->odb_by_path, o->odb->path, &r); + o->source_by_path = kh_init_odb_path_map(); + assert(!o->sources->next); + p = kh_put_odb_path_map(o->source_by_path, o->sources->path, &r); assert(r == 1); /* never used */ - kh_value(o->odb_by_path, p) = o->odb; + kh_value(o->source_by_path, p) = o->sources; } if (fspatheq(path->buf, normalized_objdir)) return 0; - *pos = kh_put_odb_path_map(o->odb_by_path, path->buf, &r); + *pos = kh_put_odb_path_map(o->source_by_path, path->buf, &r); /* r: 0 = exists, 1 = never used, 2 = deleted */ return r == 0 ? 0 : 1; } @@ -124,7 +124,7 @@ static int alt_odb_usable(struct raw_object_store *o, * Prepare alternate object database registry. * * The variable alt_odb_list points at the list of struct - * object_directory. The elements on this list come from + * odb_source. The elements on this list come from * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates, * whose contents is similar to that environment variable but can be @@ -135,23 +135,25 @@ static int alt_odb_usable(struct raw_object_store *o, * of the object ID, an extra slash for the first level indirection, and * the terminating NUL. */ -static void read_info_alternates(struct repository *r, +static void read_info_alternates(struct object_database *odb, const char *relative_base, int depth); -static int link_alt_odb_entry(struct repository *r, const struct strbuf *entry, - const char *relative_base, int depth, const char *normalized_objdir) + +static struct odb_source *link_alt_odb_entry(struct object_database *odb, + const char *dir, + const char *relative_base, + int depth) { - struct object_directory *ent; + struct odb_source *alternate = NULL; struct strbuf pathbuf = STRBUF_INIT; struct strbuf tmp = STRBUF_INIT; khiter_t pos; - int ret = -1; - if (!is_absolute_path(entry->buf) && relative_base) { + if (!is_absolute_path(dir) && relative_base) { strbuf_realpath(&pathbuf, relative_base, 1); strbuf_addch(&pathbuf, '/'); } - strbuf_addbuf(&pathbuf, entry); + strbuf_addstr(&pathbuf, dir); if (!strbuf_realpath(&tmp, pathbuf.buf, 0)) { error(_("unable to normalize alternate object path: %s"), @@ -167,27 +169,32 @@ static int link_alt_odb_entry(struct repository *r, const struct strbuf *entry, while (pathbuf.len && pathbuf.buf[pathbuf.len - 1] == '/') strbuf_setlen(&pathbuf, pathbuf.len - 1); - if (!alt_odb_usable(r->objects, &pathbuf, normalized_objdir, &pos)) + strbuf_reset(&tmp); + strbuf_realpath(&tmp, odb->sources->path, 1); + + if (!alt_odb_usable(odb, &pathbuf, tmp.buf, &pos)) goto error; - CALLOC_ARRAY(ent, 1); - /* pathbuf.buf is already in r->objects->odb_by_path */ - ent->path = strbuf_detach(&pathbuf, NULL); + CALLOC_ARRAY(alternate, 1); + alternate->odb = odb; + alternate->local = false; + /* pathbuf.buf is already in r->objects->source_by_path */ + alternate->path = strbuf_detach(&pathbuf, NULL); /* add the alternate entry */ - *r->objects->odb_tail = ent; - r->objects->odb_tail = &(ent->next); - ent->next = NULL; - assert(r->objects->odb_by_path); - kh_value(r->objects->odb_by_path, pos) = ent; + *odb->sources_tail = alternate; + odb->sources_tail = &(alternate->next); + alternate->next = NULL; + assert(odb->source_by_path); + kh_value(odb->source_by_path, pos) = alternate; /* recursively add alternates */ - read_info_alternates(r, ent->path, depth + 1); - ret = 0; + read_info_alternates(odb, alternate->path, depth + 1); + error: strbuf_release(&tmp); strbuf_release(&pathbuf); - return ret; + return alternate; } static const char *parse_alt_odb_entry(const char *string, @@ -219,11 +226,10 @@ static const char *parse_alt_odb_entry(const char *string, return end; } -static void link_alt_odb_entries(struct repository *r, const char *alt, +static void link_alt_odb_entries(struct object_database *odb, const char *alt, int sep, const char *relative_base, int depth) { - struct strbuf objdirbuf = STRBUF_INIT; - struct strbuf entry = STRBUF_INIT; + struct strbuf dir = STRBUF_INIT; if (!alt || !*alt) return; @@ -234,20 +240,16 @@ static void link_alt_odb_entries(struct repository *r, const char *alt, return; } - strbuf_realpath(&objdirbuf, r->objects->odb->path, 1); - while (*alt) { - alt = parse_alt_odb_entry(alt, sep, &entry); - if (!entry.len) + alt = parse_alt_odb_entry(alt, sep, &dir); + if (!dir.len) continue; - link_alt_odb_entry(r, &entry, - relative_base, depth, objdirbuf.buf); + link_alt_odb_entry(odb, dir.buf, relative_base, depth); } - strbuf_release(&entry); - strbuf_release(&objdirbuf); + strbuf_release(&dir); } -static void read_info_alternates(struct repository *r, +static void read_info_alternates(struct object_database *odb, const char *relative_base, int depth) { @@ -261,15 +263,16 @@ static void read_info_alternates(struct repository *r, return; } - link_alt_odb_entries(r, buf.buf, '\n', relative_base, depth); + link_alt_odb_entries(odb, buf.buf, '\n', relative_base, depth); strbuf_release(&buf); free(path); } -void add_to_alternates_file(const char *reference) +void odb_add_to_alternates_file(struct object_database *odb, + const char *dir) { struct lock_file lock = LOCK_INIT; - char *alts = repo_git_path(the_repository, "objects/info/alternates"); + char *alts = repo_git_path(odb->repo, "objects/info/alternates"); FILE *in, *out; int found = 0; @@ -283,7 +286,7 @@ void add_to_alternates_file(const char *reference) struct strbuf line = STRBUF_INIT; while (strbuf_getline(&line, in) != EOF) { - if (!strcmp(reference, line.buf)) { + if (!strcmp(dir, line.buf)) { found = 1; break; } @@ -299,85 +302,81 @@ void add_to_alternates_file(const char *reference) if (found) { rollback_lock_file(&lock); } else { - fprintf_or_die(out, "%s\n", reference); + fprintf_or_die(out, "%s\n", dir); if (commit_lock_file(&lock)) die_errno(_("unable to move new alternates file into place")); - if (the_repository->objects->loaded_alternates) - link_alt_odb_entries(the_repository, reference, - '\n', NULL, 0); + if (odb->loaded_alternates) + link_alt_odb_entries(odb, dir, '\n', NULL, 0); } free(alts); } -void add_to_alternates_memory(const char *reference) +struct odb_source *odb_add_to_alternates_memory(struct object_database *odb, + const char *dir) { /* * Make sure alternates are initialized, or else our entry may be * overwritten when they are. */ - prepare_alt_odb(the_repository); - - link_alt_odb_entries(the_repository, reference, - '\n', NULL, 0); + odb_prepare_alternates(odb); + return link_alt_odb_entry(odb, dir, NULL, 0); } -struct object_directory *set_temporary_primary_odb(const char *dir, int will_destroy) +struct odb_source *odb_set_temporary_primary_source(struct object_database *odb, + const char *dir, int will_destroy) { - struct object_directory *new_odb; + struct odb_source *source; /* * Make sure alternates are initialized, or else our entry may be * overwritten when they are. */ - prepare_alt_odb(the_repository); + odb_prepare_alternates(odb); /* * Make a new primary odb and link the old primary ODB in as an * alternate */ - new_odb = xcalloc(1, sizeof(*new_odb)); - new_odb->path = xstrdup(dir); + source = xcalloc(1, sizeof(*source)); + source->odb = odb; + source->path = xstrdup(dir); /* * Disable ref updates while a temporary odb is active, since * the objects in the database may roll back. */ - new_odb->disable_ref_updates = 1; - new_odb->will_destroy = will_destroy; - new_odb->next = the_repository->objects->odb; - the_repository->objects->odb = new_odb; - return new_odb->next; + source->disable_ref_updates = 1; + source->will_destroy = will_destroy; + source->next = odb->sources; + odb->sources = source; + return source->next; } -static void free_object_directory(struct object_directory *odb) +static void free_object_directory(struct odb_source *source) { - free(odb->path); - odb_clear_loose_cache(odb); - loose_object_map_clear(&odb->loose_map); - free(odb); + free(source->path); + odb_clear_loose_cache(source); + loose_object_map_clear(&source->loose_map); + free(source); } -void restore_primary_odb(struct object_directory *restore_odb, const char *old_path) +void odb_restore_primary_source(struct object_database *odb, + struct odb_source *restore_source, + const char *old_path) { - struct object_directory *cur_odb = the_repository->objects->odb; + struct odb_source *cur_source = odb->sources; - if (strcmp(old_path, cur_odb->path)) + if (strcmp(old_path, cur_source->path)) BUG("expected %s as primary object store; found %s", - old_path, cur_odb->path); + old_path, cur_source->path); - if (cur_odb->next != restore_odb) + if (cur_source->next != restore_source) BUG("we expect the old primary object store to be the first alternate"); - the_repository->objects->odb = restore_odb; - free_object_directory(cur_odb); + odb->sources = restore_source; + free_object_directory(cur_source); } -/* - * Compute the exact path an alternate is at and returns it. In case of - * error NULL is returned and the human readable error is added to `err` - * `path` may be relative and should point to $GIT_DIR. - * `err` must not be null. - */ char *compute_alternate_path(const char *path, struct strbuf *err) { char *ref_git = NULL; @@ -442,15 +441,15 @@ out: return ref_git; } -struct object_directory *find_odb(struct repository *r, const char *obj_dir) +struct odb_source *odb_find_source(struct object_database *odb, const char *obj_dir) { - struct object_directory *odb; + struct odb_source *source; char *obj_dir_real = real_pathdup(obj_dir, 1); struct strbuf odb_path_real = STRBUF_INIT; - prepare_alt_odb(r); - for (odb = r->objects->odb; odb; odb = odb->next) { - strbuf_realpath(&odb_path_real, odb->path, 1); + odb_prepare_alternates(odb); + for (source = odb->sources; source; source = source->next) { + strbuf_realpath(&odb_path_real, source->path, 1); if (!strcmp(obj_dir_real, odb_path_real.buf)) break; } @@ -458,17 +457,30 @@ struct object_directory *find_odb(struct repository *r, const char *obj_dir) free(obj_dir_real); strbuf_release(&odb_path_real); - if (!odb) + return source; +} + +struct odb_source *odb_find_source_or_die(struct object_database *odb, const char *obj_dir) +{ + struct odb_source *source = odb_find_source(odb, obj_dir); + if (!source) die(_("could not find object directory matching %s"), obj_dir); - return odb; + return source; +} + +void odb_add_submodule_source_by_path(struct object_database *odb, + const char *path) +{ + string_list_insert(&odb->submodule_source_paths, path); } -static void fill_alternate_refs_command(struct child_process *cmd, +static void fill_alternate_refs_command(struct repository *repo, + struct child_process *cmd, const char *repo_path) { const char *value; - if (!git_config_get_value("core.alternateRefsCommand", &value)) { + if (!repo_config_get_value(repo, "core.alternateRefsCommand", &value)) { cmd->use_shell = 1; strvec_push(&cmd->args, value); @@ -480,7 +492,7 @@ static void fill_alternate_refs_command(struct child_process *cmd, strvec_push(&cmd->args, "for-each-ref"); strvec_push(&cmd->args, "--format=%(objectname)"); - if (!git_config_get_value("core.alternateRefsPrefixes", &value)) { + if (!repo_config_get_value(repo, "core.alternateRefsPrefixes", &value)) { strvec_push(&cmd->args, "--"); strvec_split(&cmd->args, value); } @@ -490,15 +502,16 @@ static void fill_alternate_refs_command(struct child_process *cmd, cmd->out = -1; } -static void read_alternate_refs(const char *path, - alternate_ref_fn *cb, - void *data) +static void read_alternate_refs(struct repository *repo, + const char *path, + odb_for_each_alternate_ref_fn *cb, + void *payload) { struct child_process cmd = CHILD_PROCESS_INIT; struct strbuf line = STRBUF_INIT; FILE *fh; - fill_alternate_refs_command(&cmd, path); + fill_alternate_refs_command(repo, &cmd, path); if (start_command(&cmd)) return; @@ -508,13 +521,13 @@ static void read_alternate_refs(const char *path, struct object_id oid; const char *p; - if (parse_oid_hex(line.buf, &oid, &p) || *p) { + if (parse_oid_hex_algop(line.buf, &oid, &p, repo->hash_algo) || *p) { warning(_("invalid line while parsing alternate refs: %s"), line.buf); break; } - cb(&oid, data); + cb(&oid, payload); } fclose(fh); @@ -523,18 +536,18 @@ static void read_alternate_refs(const char *path, } struct alternate_refs_data { - alternate_ref_fn *fn; - void *data; + odb_for_each_alternate_ref_fn *fn; + void *payload; }; -static int refs_from_alternate_cb(struct object_directory *e, - void *data) +static int refs_from_alternate_cb(struct odb_source *alternate, + void *payload) { struct strbuf path = STRBUF_INIT; size_t base_len; - struct alternate_refs_data *cb = data; + struct alternate_refs_data *cb = payload; - if (!strbuf_realpath(&path, e->path, 0)) + if (!strbuf_realpath(&path, alternate->path, 0)) goto out; if (!strbuf_strip_suffix(&path, "/objects")) goto out; @@ -546,50 +559,52 @@ static int refs_from_alternate_cb(struct object_directory *e, goto out; strbuf_setlen(&path, base_len); - read_alternate_refs(path.buf, cb->fn, cb->data); + read_alternate_refs(alternate->odb->repo, path.buf, cb->fn, cb->payload); out: strbuf_release(&path); return 0; } -void for_each_alternate_ref(alternate_ref_fn fn, void *data) +void odb_for_each_alternate_ref(struct object_database *odb, + odb_for_each_alternate_ref_fn cb, void *payload) { - struct alternate_refs_data cb; - cb.fn = fn; - cb.data = data; - foreach_alt_odb(refs_from_alternate_cb, &cb); + struct alternate_refs_data data; + data.fn = cb; + data.payload = payload; + odb_for_each_alternate(odb, refs_from_alternate_cb, &data); } -int foreach_alt_odb(alt_odb_fn fn, void *cb) +int odb_for_each_alternate(struct object_database *odb, + odb_for_each_alternate_fn cb, void *payload) { - struct object_directory *ent; + struct odb_source *alternate; int r = 0; - prepare_alt_odb(the_repository); - for (ent = the_repository->objects->odb->next; ent; ent = ent->next) { - r = fn(ent, cb); + odb_prepare_alternates(odb); + for (alternate = odb->sources->next; alternate; alternate = alternate->next) { + r = cb(alternate, payload); if (r) break; } return r; } -void prepare_alt_odb(struct repository *r) +void odb_prepare_alternates(struct object_database *odb) { - if (r->objects->loaded_alternates) + if (odb->loaded_alternates) return; - link_alt_odb_entries(r, r->objects->alternate_db, PATH_SEP, NULL, 0); + link_alt_odb_entries(odb, odb->alternate_db, PATH_SEP, NULL, 0); - read_info_alternates(r, r->objects->odb->path, 0); - r->objects->loaded_alternates = 1; + read_info_alternates(odb, odb->sources->path, 0); + odb->loaded_alternates = 1; } -int has_alt_odb(struct repository *r) +int odb_has_alternates(struct object_database *odb) { - prepare_alt_odb(r); - return !!r->objects->odb->next; + odb_prepare_alternates(odb); + return !!odb->sources->next; } int obj_read_use_lock = 0; @@ -615,7 +630,24 @@ void disable_obj_read_lock(void) int fetch_if_missing = 1; -static int do_oid_object_info_extended(struct repository *r, +static int register_all_submodule_sources(struct object_database *odb) +{ + int ret = odb->submodule_source_paths.nr; + + for (size_t i = 0; i < odb->submodule_source_paths.nr; i++) + odb_add_to_alternates_memory(odb, + odb->submodule_source_paths.items[i].string); + if (ret) { + string_list_clear(&odb->submodule_source_paths, 0); + trace2_data_intmax("submodule", odb->repo, + "register_all_submodule_sources/registered", ret); + if (git_env_bool("GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB", 0)) + BUG("register_all_submodule_sources() called"); + } + return ret; +} + +static int do_oid_object_info_extended(struct object_database *odb, const struct object_id *oid, struct object_info *oi, unsigned flags) { @@ -628,7 +660,7 @@ static int do_oid_object_info_extended(struct repository *r, if (flags & OBJECT_INFO_LOOKUP_REPLACE) - real = lookup_replace_object(r, oid); + real = lookup_replace_object(odb->repo, oid); if (is_null_oid(real)) return -1; @@ -636,7 +668,7 @@ static int do_oid_object_info_extended(struct repository *r, if (!oi) oi = &blank_oi; - co = find_cached_object(r->objects, real); + co = find_cached_object(odb, real); if (co) { if (oi->typep) *(oi->typep) = co->type; @@ -645,7 +677,7 @@ static int do_oid_object_info_extended(struct repository *r, if (oi->disk_sizep) *(oi->disk_sizep) = 0; if (oi->delta_base_oid) - oidclr(oi->delta_base_oid, the_repository->hash_algo); + oidclr(oi->delta_base_oid, odb->repo->hash_algo); if (oi->contentp) *oi->contentp = xmemdupz(co->buf, co->size); oi->whence = OI_CACHED; @@ -653,36 +685,35 @@ static int do_oid_object_info_extended(struct repository *r, } while (1) { - if (find_pack_entry(r, real, &e)) + if (find_pack_entry(odb->repo, real, &e)) break; /* Most likely it's a loose object. */ - if (!loose_object_info(r, real, oi, flags)) + if (!loose_object_info(odb->repo, real, oi, flags)) return 0; /* Not a loose object; someone else may have just packed it. */ if (!(flags & OBJECT_INFO_QUICK)) { - reprepare_packed_git(r); - if (find_pack_entry(r, real, &e)) + reprepare_packed_git(odb->repo); + if (find_pack_entry(odb->repo, real, &e)) break; } /* - * If r is the_repository, this might be an attempt at - * accessing a submodule object as if it were in the_repository - * (having called add_submodule_odb() on that submodule's ODB). - * If any such ODBs exist, register them and try again. + * This might be an attempt at accessing a submodule object as + * if it were in main object store (having called + * `odb_add_submodule_source_by_path()` on that submodule's + * ODB). If any such ODBs exist, register them and try again. */ - if (r == the_repository && - register_all_submodule_odb_as_alternates()) + if (register_all_submodule_sources(odb)) /* We added some alternates; retry */ continue; /* Check if it is a missing object */ - if (fetch_if_missing && repo_has_promisor_remote(r) && + if (fetch_if_missing && repo_has_promisor_remote(odb->repo) && !already_retried && !(flags & OBJECT_INFO_SKIP_FETCH_OBJECT)) { - promisor_remote_get_direct(r, real, 1); + promisor_remote_get_direct(odb->repo, real, 1); already_retried = 1; continue; } @@ -692,7 +723,7 @@ static int do_oid_object_info_extended(struct repository *r, if ((flags & OBJECT_INFO_LOOKUP_REPLACE) && !oideq(real, oid)) die(_("replacement %s not found for %s"), oid_to_hex(real), oid_to_hex(oid)); - if ((p = has_packed_and_bad(r, real))) + if ((p = has_packed_and_bad(odb->repo, real))) die(_("packed object %s (stored in %s) is corrupt"), oid_to_hex(real), p->pack_name); } @@ -705,10 +736,10 @@ static int do_oid_object_info_extended(struct repository *r, * information below, so return early. */ return 0; - rtype = packed_object_info(r, e.p, e.offset, oi); + rtype = packed_object_info(odb->repo, e.p, e.offset, oi); if (rtype < 0) { mark_bad_packed_object(e.p, real); - return do_oid_object_info_extended(r, real, oi, 0); + return do_oid_object_info_extended(odb, real, oi, 0); } else if (oi->whence == OI_PACKED) { oi->u.packed.offset = e.offset; oi->u.packed.pack = e.p; @@ -732,10 +763,10 @@ static int oid_object_info_convert(struct repository *r, void *content; int ret; - if (repo_oid_to_algop(r, input_oid, the_hash_algo, &oid)) { + if (repo_oid_to_algop(r, input_oid, r->hash_algo, &oid)) { if (do_die) die(_("missing mapping of %s to %s"), - oid_to_hex(input_oid), the_hash_algo->name); + oid_to_hex(input_oid), r->hash_algo->name); return -1; } @@ -756,7 +787,7 @@ static int oid_object_info_convert(struct repository *r, oi = &new_oi; } - ret = oid_object_info_extended(r, &oid, oi, flags); + ret = odb_read_object_info_extended(r->objects, &oid, oi, flags); if (ret) return -1; if (oi == input_oi) @@ -766,8 +797,8 @@ static int oid_object_info_convert(struct repository *r, struct strbuf outbuf = STRBUF_INIT; if (type != OBJ_BLOB) { - ret = convert_object_file(the_repository, &outbuf, - the_hash_algo, input_algo, + ret = convert_object_file(r, &outbuf, + r->hash_algo, input_algo, content, size, type, !do_die); free(content); if (ret == -1) @@ -799,52 +830,54 @@ static int oid_object_info_convert(struct repository *r, return ret; } -int oid_object_info_extended(struct repository *r, const struct object_id *oid, - struct object_info *oi, unsigned flags) +int odb_read_object_info_extended(struct object_database *odb, + const struct object_id *oid, + struct object_info *oi, + unsigned flags) { int ret; - if (oid->algo && (hash_algo_by_ptr(r->hash_algo) != oid->algo)) - return oid_object_info_convert(r, oid, oi, flags); + if (oid->algo && (hash_algo_by_ptr(odb->repo->hash_algo) != oid->algo)) + return oid_object_info_convert(odb->repo, oid, oi, flags); obj_read_lock(); - ret = do_oid_object_info_extended(r, oid, oi, flags); + ret = do_oid_object_info_extended(odb, oid, oi, flags); obj_read_unlock(); return ret; } /* returns enum object_type or negative */ -int oid_object_info(struct repository *r, - const struct object_id *oid, - unsigned long *sizep) +int odb_read_object_info(struct object_database *odb, + const struct object_id *oid, + unsigned long *sizep) { enum object_type type; struct object_info oi = OBJECT_INFO_INIT; oi.typep = &type; oi.sizep = sizep; - if (oid_object_info_extended(r, oid, &oi, - OBJECT_INFO_LOOKUP_REPLACE) < 0) + if (odb_read_object_info_extended(odb, oid, &oi, + OBJECT_INFO_LOOKUP_REPLACE) < 0) return -1; return type; } -int pretend_object_file(struct repository *repo, - void *buf, unsigned long len, enum object_type type, - struct object_id *oid) +int odb_pretend_object(struct object_database *odb, + void *buf, unsigned long len, enum object_type type, + struct object_id *oid) { struct cached_object_entry *co; char *co_buf; - hash_object_file(repo->hash_algo, buf, len, type, oid); - if (has_object(repo, oid, 0) || - find_cached_object(repo->objects, oid)) + hash_object_file(odb->repo->hash_algo, buf, len, type, oid); + if (odb_has_object(odb, oid, 0) || + find_cached_object(odb, oid)) return 0; - ALLOC_GROW(repo->objects->cached_objects, - repo->objects->cached_object_nr + 1, repo->objects->cached_object_alloc); - co = &repo->objects->cached_objects[repo->objects->cached_object_nr++]; + ALLOC_GROW(odb->cached_objects, + odb->cached_object_nr + 1, odb->cached_object_alloc); + co = &odb->cached_objects[odb->cached_object_nr++]; co->value.size = len; co->value.type = type; co_buf = xmalloc(len); @@ -854,15 +887,10 @@ int pretend_object_file(struct repository *repo, return 0; } -/* - * This function dies on corrupt objects; the callers who want to - * deal with them should arrange to call oid_object_info_extended() and give - * error messages themselves. - */ -void *repo_read_object_file(struct repository *r, - const struct object_id *oid, - enum object_type *type, - unsigned long *size) +void *odb_read_object(struct object_database *odb, + const struct object_id *oid, + enum object_type *type, + unsigned long *size) { struct object_info oi = OBJECT_INFO_INIT; unsigned flags = OBJECT_INFO_DIE_IF_CORRUPT | OBJECT_INFO_LOOKUP_REPLACE; @@ -871,17 +899,17 @@ void *repo_read_object_file(struct repository *r, oi.typep = type; oi.sizep = size; oi.contentp = &data; - if (oid_object_info_extended(r, oid, &oi, flags)) + if (odb_read_object_info_extended(odb, oid, &oi, flags)) return NULL; return data; } -void *read_object_with_reference(struct repository *r, - const struct object_id *oid, - enum object_type required_type, - unsigned long *size, - struct object_id *actual_oid_return) +void *odb_read_object_peeled(struct object_database *odb, + const struct object_id *oid, + enum object_type required_type, + unsigned long *size, + struct object_id *actual_oid_return) { enum object_type type; void *buffer; @@ -893,7 +921,7 @@ void *read_object_with_reference(struct repository *r, int ref_length = -1; const char *ref_type = NULL; - buffer = repo_read_object_file(r, &actual_oid, &type, &isize); + buffer = odb_read_object(odb, &actual_oid, &type, &isize); if (!buffer) return NULL; if (type == required_type) { @@ -913,9 +941,10 @@ void *read_object_with_reference(struct repository *r, } ref_length = strlen(ref_type); - if (ref_length + the_hash_algo->hexsz > isize || + if (ref_length + odb->repo->hash_algo->hexsz > isize || memcmp(buffer, ref_type, ref_length) || - get_oid_hex((char *) buffer + ref_length, &actual_oid)) { + get_oid_hex_algop((char *) buffer + ref_length, &actual_oid, + odb->repo->hash_algo)) { free(buffer); return NULL; } @@ -925,7 +954,7 @@ void *read_object_with_reference(struct repository *r, } } -int has_object(struct repository *r, const struct object_id *oid, +int odb_has_object(struct object_database *odb, const struct object_id *oid, unsigned flags) { unsigned object_info_flags = 0; @@ -937,12 +966,13 @@ int has_object(struct repository *r, const struct object_id *oid, if (!(flags & HAS_OBJECT_FETCH_PROMISOR)) object_info_flags |= OBJECT_INFO_SKIP_FETCH_OBJECT; - return oid_object_info_extended(r, oid, NULL, object_info_flags) >= 0; + return odb_read_object_info_extended(odb, oid, NULL, object_info_flags) >= 0; } -void assert_oid_type(const struct object_id *oid, enum object_type expect) +void odb_assert_oid_type(struct object_database *odb, + const struct object_id *oid, enum object_type expect) { - enum object_type type = oid_object_info(the_repository, oid, NULL); + enum object_type type = odb_read_object_info(odb, oid, NULL); if (type < 0) die(_("%s is not a valid object"), oid_to_hex(oid)); if (type != expect) @@ -950,31 +980,43 @@ void assert_oid_type(const struct object_id *oid, enum object_type expect) type_name(expect)); } -struct raw_object_store *raw_object_store_new(void) +int odb_write_object_ext(struct object_database *odb, + const void *buf, unsigned long len, + enum object_type type, + struct object_id *oid, + struct object_id *compat_oid, + unsigned flags) +{ + return write_object_file(odb->sources, buf, len, type, oid, compat_oid, flags); +} + +struct object_database *odb_new(struct repository *repo) { - struct raw_object_store *o = xmalloc(sizeof(*o)); + struct object_database *o = xmalloc(sizeof(*o)); memset(o, 0, sizeof(*o)); + o->repo = repo; INIT_LIST_HEAD(&o->packed_git_mru); hashmap_init(&o->pack_map, pack_map_entry_cmp, NULL, 0); pthread_mutex_init(&o->replace_mutex, NULL); + string_list_init_dup(&o->submodule_source_paths); return o; } -static void free_object_directories(struct raw_object_store *o) +static void free_object_directories(struct object_database *o) { - while (o->odb) { - struct object_directory *next; + while (o->sources) { + struct odb_source *next; - next = o->odb->next; - free_object_directory(o->odb); - o->odb = next; + next = o->sources->next; + free_object_directory(o->sources); + o->sources = next; } - kh_destroy_odb_path_map(o->odb_by_path); - o->odb_by_path = NULL; + kh_destroy_odb_path_map(o->source_by_path); + o->source_by_path = NULL; } -void raw_object_store_clear(struct raw_object_store *o) +void odb_clear(struct object_database *o) { FREE_AND_NULL(o->alternate_db); @@ -986,7 +1028,7 @@ void raw_object_store_clear(struct raw_object_store *o) o->commit_graph_attempted = 0; free_object_directories(o); - o->odb_tail = NULL; + o->sources_tail = NULL; o->loaded_alternates = 0; for (size_t i = 0; i < o->cached_object_nr; i++) @@ -1007,4 +1049,5 @@ void raw_object_store_clear(struct raw_object_store *o) o->packed_git = NULL; hashmap_clear(&o->pack_map); + string_list_clear(&o->submodule_source_paths, 0); } @@ -0,0 +1,530 @@ +#ifndef ODB_H +#define ODB_H + +#include "hashmap.h" +#include "object.h" +#include "list.h" +#include "oidset.h" +#include "oidmap.h" +#include "string-list.h" +#include "thread-utils.h" + +struct oidmap; +struct oidtree; +struct strbuf; +struct repository; +struct multi_pack_index; + +/* + * Compute the exact path an alternate is at and returns it. In case of + * error NULL is returned and the human readable error is added to `err` + * `path` may be relative and should point to $GIT_DIR. + * `err` must not be null. + */ +char *compute_alternate_path(const char *path, struct strbuf *err); + +/* + * 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: + * + * - The primary source, which is typically located in "$GIT_DIR/objects". + * This is where new objects are usually written to. + * + * - Alternate sources, which are configured via "objects/info/alternates" or + * via the GIT_ALTERNATE_OBJECT_DIRECTORIES environment variable. These + * alternate sources are only used to read objects. + */ +struct odb_source { + struct odb_source *next; + + /* Object database that owns this object source. */ + struct object_database *odb; + + /* + * Used to store the results of readdir(3) calls when we are OK + * sacrificing accuracy due to races for speed. That includes + * object existence with OBJECT_INFO_QUICK, as well as + * our search for unique abbreviated hashes. Don't use it for tasks + * requiring greater accuracy! + * + * Be sure to call odb_load_loose_cache() before using. + */ + uint32_t loose_objects_subdir_seen[8]; /* 256 bits */ + struct oidtree *loose_objects_cache; + + /* Map between object IDs for loose objects. */ + struct loose_object_map *loose_map; + + /* + * private data + * + * should only be accessed directly by packfile.c and midx.c + */ + struct multi_pack_index *midx; + + /* + * Figure out whether this is the local source of the owning + * repository, which would typically be its ".git/objects" directory. + * This local object directory is usually where objects would be + * written to. + */ + bool local; + + /* + * This is a temporary object store created by the tmp_objdir + * facility. Disable ref updates since the objects in the store + * might be discarded on rollback. + */ + int disable_ref_updates; + + /* + * This object store is ephemeral, so there is no need to fsync. + */ + int will_destroy; + + /* + * Path to the source. If this is a relative path, it is relative to + * the current working directory. + */ + char *path; +}; + +struct packed_git; +struct cached_object_entry; +struct odb_transaction; + +/* + * The object database encapsulates access to objects in a repository. It + * manages one or more sources that store the actual objects which are + * configured via alternates. + */ +struct object_database { + /* Repository that owns this database. */ + struct repository *repo; + + /* + * State of current current object database transaction. Only one + * transaction may be pending at a time. Is NULL when no transaction is + * configured. + */ + struct odb_transaction *transaction; + + /* + * Set of all object directories; the main directory is first (and + * cannot be NULL after initialization). Subsequent directories are + * alternates. + */ + struct odb_source *sources; + struct odb_source **sources_tail; + struct kh_odb_path_map *source_by_path; + + int loaded_alternates; + + /* + * A list of alternate object directories loaded from the environment; + * this should not generally need to be accessed directly, but will + * populate the "sources" list when odb_prepare_alternates() is run. + */ + char *alternate_db; + + /* + * Objects that should be substituted by other objects + * (see git-replace(1)). + */ + struct oidmap replace_map; + unsigned replace_map_initialized : 1; + pthread_mutex_t replace_mutex; /* protect object replace functions */ + + struct commit_graph *commit_graph; + unsigned commit_graph_attempted : 1; /* if loading has been attempted */ + + /* + * private data + * + * should only be accessed directly by packfile.c + */ + + struct packed_git *packed_git; + /* A most-recently-used ordered version of the packed_git list. */ + struct list_head packed_git_mru; + + struct { + struct packed_git **packs; + unsigned flags; + } kept_pack_cache; + + /* + * This is meant to hold a *small* number of objects that you would + * want odb_read_object() to be able to return, but yet you do not want + * to write them into the object store (e.g. a browse-only + * application). + */ + struct cached_object_entry *cached_objects; + size_t cached_object_nr, cached_object_alloc; + + /* + * A map of packfiles to packed_git structs for tracking which + * packs have been loaded already. + */ + struct hashmap pack_map; + + /* + * 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. + */ + unsigned long approximate_object_count; + unsigned approximate_object_count_valid : 1; + + /* + * Whether packed_git has already been populated with this repository's + * packs. + */ + unsigned packed_git_initialized : 1; + + /* + * Submodule source paths that will be added as additional sources to + * allow lookup of submodule objects via the main object database. + */ + struct string_list submodule_source_paths; +}; + +struct object_database *odb_new(struct repository *repo); +void odb_clear(struct object_database *o); + +/* + * Find source by its object directory path. Returns a `NULL` pointer in case + * the source could not be found. + */ +struct odb_source *odb_find_source(struct object_database *odb, const char *obj_dir); + +/* Same as `odb_find_source()`, but dies in case the source doesn't exist. */ +struct odb_source *odb_find_source_or_die(struct object_database *odb, const char *obj_dir); + +/* + * Replace the current writable object directory with the specified temporary + * object directory; returns the former primary source. + */ +struct odb_source *odb_set_temporary_primary_source(struct object_database *odb, + const char *dir, int will_destroy); + +/* + * Restore the primary source that was previously replaced by + * `odb_set_temporary_primary_source()`. + */ +void odb_restore_primary_source(struct object_database *odb, + struct odb_source *restore_source, + const char *old_path); + +/* + * Call odb_add_submodule_source_by_path() to add the submodule at the given + * path to a list. The object stores of all submodules in that list will be + * added as additional sources in the object store when looking up objects. + */ +void odb_add_submodule_source_by_path(struct object_database *odb, + const char *path); + +/* + * Iterate through all alternates of the database and execute the provided + * callback function for each of them. Stop iterating once the callback + * function returns a non-zero value, in which case the value is bubbled up + * from the callback. + */ +typedef int odb_for_each_alternate_fn(struct odb_source *, void *); +int odb_for_each_alternate(struct object_database *odb, + odb_for_each_alternate_fn cb, void *payload); + +/* + * Iterate through all alternates of the database and yield their respective + * references. + */ +typedef void odb_for_each_alternate_ref_fn(const struct object_id *oid, void *); +void odb_for_each_alternate_ref(struct object_database *odb, + odb_for_each_alternate_ref_fn cb, void *payload); + +/* + * Create a temporary file rooted in the primary alternate's directory, or die + * on failure. The filename is taken from "pattern", which should have the + * usual "XXXXXX" trailer, and the resulting filename is written into the + * "template" buffer. Returns the open descriptor. + */ +int odb_mkstemp(struct object_database *odb, + struct strbuf *temp_filename, const char *pattern); + +/* + * Prepare alternate object sources for the given database by reading + * "objects/info/alternates" and opening the respective sources. + */ +void odb_prepare_alternates(struct object_database *odb); + +/* + * Check whether the object database has any alternates. The primary object + * source does not count as alternate. + */ +int odb_has_alternates(struct object_database *odb); + +/* + * Add the directory to the on-disk alternates file; the new entry will also + * take effect in the current process. + */ +void odb_add_to_alternates_file(struct object_database *odb, + const char *dir); + +/* + * Add the directory to the in-memory list of alternate sources (along with any + * recursive alternates it points to), but do not modify the on-disk alternates + * file. + */ +struct odb_source *odb_add_to_alternates_memory(struct object_database *odb, + const char *dir); + +/* + * Read an object from the database. Returns the object data and assigns object + * type and size to the `type` and `size` pointers, if these pointers are + * non-NULL. Returns a `NULL` pointer in case the object does not exist. + * + * This function dies on corrupt objects; the callers who want to deal with + * them should arrange to call odb_read_object_info_extended() and give error + * messages themselves. + */ +void *odb_read_object(struct object_database *odb, + const struct object_id *oid, + enum object_type *type, + unsigned long *size); + +void *odb_read_object_peeled(struct object_database *odb, + const struct object_id *oid, + enum object_type required_type, + unsigned long *size, + struct object_id *oid_ret); + +/* + * Add an object file to the in-memory object store, without writing it + * to disk. + * + * Callers are responsible for calling write_object_file to record the + * object in persistent storage before writing any other new objects + * that reference it. + */ +int odb_pretend_object(struct object_database *odb, + void *buf, unsigned long len, enum object_type type, + struct object_id *oid); + +struct object_info { + /* Request */ + enum object_type *typep; + unsigned long *sizep; + off_t *disk_sizep; + struct object_id *delta_base_oid; + void **contentp; + + /* Response */ + enum { + OI_CACHED, + OI_LOOSE, + OI_PACKED, + OI_DBCACHED + } whence; + union { + /* + * struct { + * ... Nothing to expose in this case + * } cached; + * struct { + * ... Nothing to expose in this case + * } loose; + */ + struct { + struct packed_git *pack; + off_t offset; + unsigned int is_delta; + } packed; + } u; +}; + +/* + * Initializer for a "struct object_info" that wants no items. You may + * also memset() the memory to all-zeroes. + */ +#define OBJECT_INFO_INIT { 0 } + +/* Invoke lookup_replace_object() on the given hash */ +#define OBJECT_INFO_LOOKUP_REPLACE 1 +/* Do not retry packed storage after checking packed and loose storage */ +#define OBJECT_INFO_QUICK 8 +/* + * Do not attempt to fetch the object if missing (even if fetch_is_missing is + * nonzero). + */ +#define OBJECT_INFO_SKIP_FETCH_OBJECT 16 +/* + * This is meant for bulk prefetching of missing blobs in a partial + * clone. Implies OBJECT_INFO_SKIP_FETCH_OBJECT and OBJECT_INFO_QUICK + */ +#define OBJECT_INFO_FOR_PREFETCH (OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK) + +/* Die if object corruption (not just an object being missing) was detected. */ +#define OBJECT_INFO_DIE_IF_CORRUPT 32 + +/* + * Read object info from the object database and populate the `object_info` + * structure. Returns 0 on success, a negative error code otherwise. + */ +int odb_read_object_info_extended(struct object_database *odb, + const struct object_id *oid, + struct object_info *oi, + unsigned flags); + +/* + * Read a subset of object info for the given object ID. Returns an `enum + * object_type` on success, a negative error code otherwise. If successful and + * `sizep` is non-NULL, then the size of the object will be written to the + * pointer. + */ +int odb_read_object_info(struct object_database *odb, + const struct object_id *oid, + unsigned long *sizep); + +enum { + /* Retry packed storage after checking packed and loose storage */ + HAS_OBJECT_RECHECK_PACKED = (1 << 0), + /* Allow fetching the object in case the repository has a promisor remote. */ + HAS_OBJECT_FETCH_PROMISOR = (1 << 1), +}; + +/* + * Returns 1 if the object exists. This function will not lazily fetch objects + * in a partial clone by default. + */ +int odb_has_object(struct object_database *odb, + const struct object_id *oid, + unsigned flags); + +void odb_assert_oid_type(struct object_database *odb, + const struct object_id *oid, enum object_type expect); + +/* + * Enabling the object read lock allows multiple threads to safely call the + * following functions in parallel: odb_read_object(), + * odb_read_object_peeled(), odb_read_object_info() and odb(). + * + * obj_read_lock() and obj_read_unlock() may also be used to protect other + * section which cannot execute in parallel with object reading. Since the used + * lock is a recursive mutex, these sections can even contain calls to object + * reading functions. However, beware that in these cases zlib inflation won't + * be performed in parallel, losing performance. + * + * TODO: odb_read_object_info_extended()'s call stack has a recursive behavior. If + * any of its callees end up calling it, this recursive call won't benefit from + * parallel inflation. + */ +void enable_obj_read_lock(void); +void disable_obj_read_lock(void); + +extern int obj_read_use_lock; +extern pthread_mutex_t obj_read_mutex; + +static inline void obj_read_lock(void) +{ + if(obj_read_use_lock) + pthread_mutex_lock(&obj_read_mutex); +} + +static inline void obj_read_unlock(void) +{ + if(obj_read_use_lock) + pthread_mutex_unlock(&obj_read_mutex); +} +/* Flags for for_each_*_object(). */ +enum for_each_object_flags { + /* Iterate only over local objects, not alternates. */ + FOR_EACH_OBJECT_LOCAL_ONLY = (1<<0), + + /* Only iterate over packs obtained from the promisor remote. */ + FOR_EACH_OBJECT_PROMISOR_ONLY = (1<<1), + + /* + * Visit objects within a pack in packfile order rather than .idx order + */ + FOR_EACH_OBJECT_PACK_ORDER = (1<<2), + + /* Only iterate over packs that are not marked as kept in-core. */ + FOR_EACH_OBJECT_SKIP_IN_CORE_KEPT_PACKS = (1<<3), + + /* Only iterate over packs that do not have .keep files. */ + FOR_EACH_OBJECT_SKIP_ON_DISK_KEPT_PACKS = (1<<4), +}; + +enum { + /* + * By default, `odb_write_object()` does not actually write anything + * into the object store, but only computes the object ID. This flag + * changes that so that the object will be written as a loose object + * and persisted. + */ + WRITE_OBJECT_PERSIST = (1 << 0), + + /* + * Do not print an error in case something goes wrong. + */ + WRITE_OBJECT_SILENT = (1 << 1), +}; + +/* + * Write an object into the object database. The object is being written into + * the local alternate of the repository. If provided, the converted object ID + * as well as the compatibility object ID are written to the respective + * pointers. + * + * Returns 0 on success, a negative error code otherwise. + */ +int odb_write_object_ext(struct object_database *odb, + const void *buf, unsigned long len, + enum object_type type, + struct object_id *oid, + struct object_id *compat_oid, + unsigned flags); + +static inline int odb_write_object(struct object_database *odb, + const void *buf, unsigned long len, + enum object_type type, + struct object_id *oid) +{ + return odb_write_object_ext(odb, buf, len, type, oid, NULL, 0); +} + +/* Compatibility wrappers, to be removed once Git 2.51 has been released. */ +#include "repository.h" + +static inline int oid_object_info_extended(struct repository *r, + const struct object_id *oid, + struct object_info *oi, + unsigned flags) +{ + return odb_read_object_info_extended(r->objects, oid, oi, flags); +} + +static inline int oid_object_info(struct repository *r, + const struct object_id *oid, + unsigned long *sizep) +{ + return odb_read_object_info(r->objects, oid, sizep); +} + +static inline void *repo_read_object_file(struct repository *r, + const struct object_id *oid, + enum object_type *type, + unsigned long *size) +{ + return odb_read_object(r->objects, oid, type, size); +} + +static inline int has_object(struct repository *r, + const struct object_id *oid, + unsigned flags) +{ + return odb_has_object(r->objects, oid, flags); +} + +#endif /* ODB_H */ diff --git a/oss-fuzz/fuzz-commit-graph.c b/oss-fuzz/fuzz-commit-graph.c index fbb77fec19..fb8b8787a4 100644 --- a/oss-fuzz/fuzz-commit-graph.c +++ b/oss-fuzz/fuzz-commit-graph.c @@ -4,9 +4,6 @@ #include "commit-graph.h" #include "repository.h" -struct commit_graph *parse_commit_graph(struct repo_settings *s, - void *graph_map, size_t graph_size); - int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) @@ -22,9 +19,10 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) * possible. */ repo_set_hash_algo(the_repository, GIT_HASH_SHA1); + the_repository->settings.initialized = 1; the_repository->settings.commit_graph_generation_version = 2; the_repository->settings.commit_graph_changed_paths_version = 1; - g = parse_commit_graph(&the_repository->settings, (void *)data, size); + g = parse_commit_graph(the_repository, (void *)data, size); repo_clear(the_repository); free_commit_graph(g); diff --git a/oss-fuzz/fuzz-pack-idx.c b/oss-fuzz/fuzz-pack-idx.c index 609a343ee3..d2a92f34d9 100644 --- a/oss-fuzz/fuzz-pack-idx.c +++ b/oss-fuzz/fuzz-pack-idx.c @@ -1,5 +1,5 @@ #include "git-compat-util.h" -#include "object-store.h" +#include "odb.h" #include "packfile.h" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c index 7f400ee012..4404921521 100644 --- a/pack-bitmap-write.c +++ b/pack-bitmap-write.c @@ -4,7 +4,7 @@ #include "environment.h" #include "gettext.h" #include "hex.h" -#include "object-store.h" +#include "odb.h" #include "commit.h" #include "diff.h" #include "revision.h" @@ -144,8 +144,8 @@ void bitmap_writer_build_type_index(struct bitmap_writer *writer, break; default: - real_type = oid_object_info(writer->to_pack->repo, - &entry->idx.oid, NULL); + real_type = odb_read_object_info(writer->to_pack->repo->objects, + &entry->idx.oid, NULL); break; } @@ -1052,7 +1052,8 @@ void bitmap_writer_finish(struct bitmap_writer *writer, struct bitmap_disk_header header; - int fd = odb_mkstemp(&tmp_file, "pack/tmp_bitmap_XXXXXX"); + int fd = odb_mkstemp(writer->repo->objects, &tmp_file, + "pack/tmp_bitmap_XXXXXX"); if (writer->pseudo_merges_nr) options |= BITMAP_OPT_PSEUDO_MERGES; @@ -1087,7 +1088,7 @@ void bitmap_writer_finish(struct bitmap_writer *writer, oid_access); if (commit_pos < 0) - BUG(_("trying to write commit not in index")); + BUG("trying to write commit not in index"); stored->commit_pos = commit_pos + base_objects; } diff --git a/pack-bitmap.c b/pack-bitmap.c index ac6d62b980..058bdb5d7d 100644 --- a/pack-bitmap.c +++ b/pack-bitmap.c @@ -17,7 +17,7 @@ #include "packfile.h" #include "repository.h" #include "trace2.h" -#include "object-store.h" +#include "odb.h" #include "list-objects-filter-options.h" #include "midx.h" #include "config.h" @@ -31,6 +31,7 @@ struct stored_bitmap { struct object_id oid; struct ewah_bitmap *root; struct stored_bitmap *xor; + size_t map_pos; int flags; }; @@ -215,7 +216,7 @@ static uint32_t bitmap_num_objects(struct bitmap_index *index) static struct repository *bitmap_repo(struct bitmap_index *bitmap_git) { if (bitmap_is_midx(bitmap_git)) - return bitmap_git->midx->repo; + return bitmap_git->midx->source->odb->repo; return bitmap_git->pack->repo; } @@ -314,13 +315,14 @@ static struct stored_bitmap *store_bitmap(struct bitmap_index *index, struct ewah_bitmap *root, const struct object_id *oid, struct stored_bitmap *xor_with, - int flags) + int flags, size_t map_pos) { struct stored_bitmap *stored; khiter_t hash_pos; int ret; stored = xmalloc(sizeof(struct stored_bitmap)); + stored->map_pos = map_pos; stored->root = root; stored->xor = xor_with; stored->flags = flags; @@ -376,10 +378,12 @@ static int load_bitmap_entries_v1(struct bitmap_index *index) struct stored_bitmap *xor_bitmap = NULL; uint32_t commit_idx_pos; struct object_id oid; + size_t entry_map_pos; if (index->map_size - index->map_pos < 6) return error(_("corrupt ewah bitmap: truncated header for entry %d"), i); + entry_map_pos = index->map_pos; commit_idx_pos = read_be32(index->map, &index->map_pos); xor_offset = read_u8(index->map, &index->map_pos); flags = read_u8(index->map, &index->map_pos); @@ -402,8 +406,9 @@ static int load_bitmap_entries_v1(struct bitmap_index *index) if (!bitmap) return -1; - recent_bitmaps[i % MAX_XOR_OFFSET] = store_bitmap( - index, bitmap, &oid, xor_bitmap, flags); + recent_bitmaps[i % MAX_XOR_OFFSET] = + store_bitmap(index, bitmap, &oid, xor_bitmap, flags, + entry_map_pos); } return 0; @@ -413,13 +418,12 @@ char *midx_bitmap_filename(struct multi_pack_index *midx) { struct strbuf buf = STRBUF_INIT; if (midx->has_chain) - get_split_midx_filename_ext(midx->repo->hash_algo, &buf, - midx->object_dir, + get_split_midx_filename_ext(midx->source, &buf, get_midx_checksum(midx), MIDX_EXT_BITMAP); else - get_midx_filename_ext(midx->repo->hash_algo, &buf, - midx->object_dir, get_midx_checksum(midx), + get_midx_filename_ext(midx->source, &buf, + get_midx_checksum(midx), MIDX_EXT_BITMAP); return strbuf_detach(&buf, NULL); @@ -458,7 +462,7 @@ static int open_midx_bitmap_1(struct bitmap_index *bitmap_git, if (bitmap_git->pack || bitmap_git->midx) { struct strbuf buf = STRBUF_INIT; - get_midx_filename(midx->repo->hash_algo, &buf, midx->object_dir); + get_midx_filename(midx->source, &buf); trace2_data_string("bitmap", bitmap_repo(bitmap_git), "ignoring extra midx bitmap file", buf.buf); close(fd); @@ -488,7 +492,7 @@ static int open_midx_bitmap_1(struct bitmap_index *bitmap_git, } for (i = 0; i < bitmap_git->midx->num_packs + bitmap_git->midx->num_packs_in_base; i++) { - if (prepare_midx_pack(bitmap_repo(bitmap_git), bitmap_git->midx, i)) { + if (prepare_midx_pack(bitmap_git->midx, i)) { warning(_("could not open pack %s"), bitmap_git->midx->pack_names[i]); goto cleanup; @@ -630,41 +634,28 @@ static int load_bitmap(struct repository *r, struct bitmap_index *bitmap_git, bitmap_git->ext_index.positions = kh_init_oid_pos(); if (load_reverse_index(r, bitmap_git)) - goto failed; + return -1; if (!(bitmap_git->commits = read_bitmap_1(bitmap_git)) || !(bitmap_git->trees = read_bitmap_1(bitmap_git)) || !(bitmap_git->blobs = read_bitmap_1(bitmap_git)) || !(bitmap_git->tags = read_bitmap_1(bitmap_git))) - goto failed; + return -1; if (!bitmap_git->table_lookup && load_bitmap_entries_v1(bitmap_git) < 0) - goto failed; + return -1; if (bitmap_git->base) { if (!bitmap_is_midx(bitmap_git)) BUG("non-MIDX bitmap has non-NULL base bitmap index"); if (load_bitmap(r, bitmap_git->base, 1) < 0) - goto failed; + return -1; } if (!recursing) load_all_type_bitmaps(bitmap_git); return 0; - -failed: - munmap(bitmap_git->map, bitmap_git->map_size); - bitmap_git->map = NULL; - bitmap_git->map_size = 0; - - kh_destroy_oid_map(bitmap_git->bitmaps); - bitmap_git->bitmaps = NULL; - - kh_destroy_oid_pos(bitmap_git->ext_index.positions); - bitmap_git->ext_index.positions = NULL; - - return -1; } static int open_pack_bitmap(struct repository *r, @@ -691,13 +682,15 @@ static int open_pack_bitmap(struct repository *r, static int open_midx_bitmap(struct repository *r, struct bitmap_index *bitmap_git) { + struct odb_source *source; int ret = -1; - struct multi_pack_index *midx; assert(!bitmap_git->map); - for (midx = get_multi_pack_index(r); midx; midx = midx->next) { - if (!open_midx_bitmap_1(bitmap_git, midx)) + odb_prepare_alternates(r->objects); + for (source = r->objects->sources; source; source = source->next) { + struct multi_pack_index *midx = get_multi_pack_index(source); + if (midx && !open_midx_bitmap_1(bitmap_git, midx)) ret = 0; } return ret; @@ -882,6 +875,7 @@ static struct stored_bitmap *lazy_bitmap_for_commit(struct bitmap_index *bitmap_ int xor_flags; khiter_t hash_pos; struct bitmap_lookup_table_xor_item *xor_item; + size_t entry_map_pos; if (is_corrupt) return NULL; @@ -941,6 +935,7 @@ static struct stored_bitmap *lazy_bitmap_for_commit(struct bitmap_index *bitmap_ goto corrupt; } + entry_map_pos = bitmap_git->map_pos; bitmap_git->map_pos += sizeof(uint32_t) + sizeof(uint8_t); xor_flags = read_u8(bitmap_git->map, &bitmap_git->map_pos); bitmap = read_bitmap_1(bitmap_git); @@ -948,7 +943,8 @@ static struct stored_bitmap *lazy_bitmap_for_commit(struct bitmap_index *bitmap_ if (!bitmap) goto corrupt; - xor_bitmap = store_bitmap(bitmap_git, bitmap, &xor_item->oid, xor_bitmap, xor_flags); + xor_bitmap = store_bitmap(bitmap_git, bitmap, &xor_item->oid, + xor_bitmap, xor_flags, entry_map_pos); xor_items_nr--; } @@ -982,6 +978,7 @@ static struct stored_bitmap *lazy_bitmap_for_commit(struct bitmap_index *bitmap_ * Instead, we can skip ahead and immediately read the flags and * ewah bitmap. */ + entry_map_pos = bitmap_git->map_pos; bitmap_git->map_pos += sizeof(uint32_t) + sizeof(uint8_t); flags = read_u8(bitmap_git->map, &bitmap_git->map_pos); bitmap = read_bitmap_1(bitmap_git); @@ -989,7 +986,8 @@ static struct stored_bitmap *lazy_bitmap_for_commit(struct bitmap_index *bitmap_ if (!bitmap) goto corrupt; - return store_bitmap(bitmap_git, bitmap, oid, xor_bitmap, flags); + return store_bitmap(bitmap_git, bitmap, oid, xor_bitmap, flags, + entry_map_pos); corrupt: free(xor_items); @@ -1363,8 +1361,8 @@ static struct bitmap *find_boundary_objects(struct bitmap_index *bitmap_git, bitmap_set(roots_bitmap, pos); } - if (!cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap)) - bitmap_free(roots_bitmap); + cascade_pseudo_merges_1(bitmap_git, cb.base, roots_bitmap); + bitmap_free(roots_bitmap); } /* @@ -1868,8 +1866,8 @@ static unsigned long get_size_by_pos(struct bitmap_index *bitmap_git, size_t eindex_pos = pos - bitmap_num_objects_total(bitmap_git); struct eindex *eindex = &bitmap_git->ext_index; struct object *obj = eindex->objects[eindex_pos]; - if (oid_object_info_extended(bitmap_repo(bitmap_git), &obj->oid, - &oi, 0) < 0) + if (odb_read_object_info_extended(bitmap_repo(bitmap_git)->objects, &obj->oid, + &oi, 0) < 0) die(_("unable to get size of %s"), oid_to_hex(&obj->oid)); } @@ -2467,7 +2465,7 @@ void reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git, struct multi_pack_index *m = bitmap_git->midx; for (i = 0; i < m->num_packs + m->num_packs_in_base; i++) { struct bitmapped_pack pack; - if (nth_bitmapped_pack(r, bitmap_git->midx, &pack, i) < 0) { + if (nth_bitmapped_pack(bitmap_git->midx, &pack, i) < 0) { warning(_("unable to load pack: '%s', disabling pack-reuse"), bitmap_git->midx->pack_names[i]); free(packs); @@ -2852,8 +2850,9 @@ int test_bitmap_commits(struct repository *r) die(_("failed to load bitmap indexes")); /* - * As this function is only used to print bitmap selected - * commits, we don't have to read the commit table. + * Since this function needs to print the bitmapped + * commits, bypass the commit lookup table (if one exists) + * by forcing the bitmap to eagerly load its entries. */ if (bitmap_git->table_lookup) { if (load_bitmap_entries_v1(bitmap_git) < 0) @@ -2869,6 +2868,48 @@ int test_bitmap_commits(struct repository *r) return 0; } +int test_bitmap_commits_with_offset(struct repository *r) +{ + struct object_id oid; + struct stored_bitmap *stored; + struct bitmap_index *bitmap_git; + size_t commit_idx_pos_map_pos, xor_offset_map_pos, flag_map_pos, + ewah_bitmap_map_pos; + + bitmap_git = prepare_bitmap_git(r); + if (!bitmap_git) + die(_("failed to load bitmap indexes")); + + /* + * Since this function needs to know the position of each individual + * bitmap, bypass the commit lookup table (if one exists) by forcing + * the bitmap to eagerly load its entries. + */ + if (bitmap_git->table_lookup) { + if (load_bitmap_entries_v1(bitmap_git) < 0) + die(_("failed to load bitmap indexes")); + } + + kh_foreach (bitmap_git->bitmaps, oid, stored, { + commit_idx_pos_map_pos = stored->map_pos; + xor_offset_map_pos = stored->map_pos + sizeof(uint32_t); + flag_map_pos = xor_offset_map_pos + sizeof(uint8_t); + ewah_bitmap_map_pos = flag_map_pos + sizeof(uint8_t); + + printf_ln("%s %"PRIuMAX" %"PRIuMAX" %"PRIuMAX" %"PRIuMAX, + oid_to_hex(&oid), + (uintmax_t)commit_idx_pos_map_pos, + (uintmax_t)xor_offset_map_pos, + (uintmax_t)flag_map_pos, + (uintmax_t)ewah_bitmap_map_pos); + }) + ; + + free_bitmap_index(bitmap_git); + + return 0; +} + int test_bitmap_hashes(struct repository *r) { struct bitmap_index *bitmap_git = prepare_bitmap_git(r); @@ -3220,8 +3261,8 @@ static off_t get_disk_usage_for_extended(struct bitmap_index *bitmap_git) i))) continue; - if (oid_object_info_extended(bitmap_repo(bitmap_git), &obj->oid, - &oi, 0) < 0) + if (odb_read_object_info_extended(bitmap_repo(bitmap_git)->objects, + &obj->oid, &oi, 0) < 0) die(_("unable to get disk usage of '%s'"), oid_to_hex(&obj->oid)); @@ -3305,11 +3346,18 @@ static int verify_bitmap_file(const struct git_hash_algo *algop, int verify_bitmap_files(struct repository *r) { + struct odb_source *source; int res = 0; - for (struct multi_pack_index *m = get_multi_pack_index(r); - m; m = m->next) { - char *midx_bitmap_name = midx_bitmap_filename(m); + odb_prepare_alternates(r->objects); + for (source = r->objects->sources; source; source = source->next) { + struct multi_pack_index *m = get_multi_pack_index(source); + char *midx_bitmap_name; + + if (!m) + continue; + + midx_bitmap_name = midx_bitmap_filename(m); res |= verify_bitmap_file(r->hash_algo, midx_bitmap_name); free(midx_bitmap_name); } diff --git a/pack-bitmap.h b/pack-bitmap.h index 382d39499a..1bd7a791e2 100644 --- a/pack-bitmap.h +++ b/pack-bitmap.h @@ -81,6 +81,7 @@ void traverse_bitmap_commit_list(struct bitmap_index *, show_reachable_fn show_reachable); void test_bitmap_walk(struct rev_info *revs); int test_bitmap_commits(struct repository *r); +int test_bitmap_commits_with_offset(struct repository *r); int test_bitmap_hashes(struct repository *r); int test_bitmap_pseudo_merges(struct repository *r); int test_bitmap_pseudo_merge_commits(struct repository *r, uint32_t n); diff --git a/pack-check.c b/pack-check.c index 874897d6cb..67cb2cf72f 100644 --- a/pack-check.c +++ b/pack-check.c @@ -8,7 +8,7 @@ #include "progress.h" #include "packfile.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" struct idx_entry { off_t offset; diff --git a/pack-mtimes.c b/pack-mtimes.c index 20900ca88d..8e1f2dec0e 100644 --- a/pack-mtimes.c +++ b/pack-mtimes.c @@ -1,7 +1,7 @@ #include "git-compat-util.h" #include "gettext.h" #include "pack-mtimes.h" -#include "object-store.h" +#include "odb.h" #include "packfile.h" #include "strbuf.h" diff --git a/pack-objects.h b/pack-objects.h index 475a2d67ce..83299d4732 100644 --- a/pack-objects.h +++ b/pack-objects.h @@ -1,7 +1,7 @@ #ifndef PACK_OBJECTS_H #define PACK_OBJECTS_H -#include "object-store.h" +#include "odb.h" #include "thread-utils.h" #include "pack.h" #include "packfile.h" @@ -120,11 +120,23 @@ struct object_entry { unsigned ext_base:1; /* delta_idx points outside packlist */ }; +/** + * A packing region is a section of the packing_data.objects array + * as given by a starting index and a number of elements. + */ +struct packing_region { + size_t start; + size_t nr; +}; + struct packing_data { struct repository *repo; struct object_entry *objects; uint32_t nr_objects, nr_alloc; + struct packing_region *regions; + size_t nr_regions, nr_regions_alloc; + int32_t *index; uint32_t index_size; diff --git a/pack-revindex.c b/pack-revindex.c index ffcde48870..d0791cc493 100644 --- a/pack-revindex.c +++ b/pack-revindex.c @@ -1,7 +1,7 @@ #include "git-compat-util.h" #include "gettext.h" #include "pack-revindex.h" -#include "object-store.h" +#include "odb.h" #include "packfile.h" #include "strbuf.h" #include "trace2.h" @@ -379,25 +379,25 @@ int load_midx_revindex(struct multi_pack_index *m) * not want to accidentally call munmap() in the middle of the * MIDX. */ - trace2_data_string("load_midx_revindex", m->repo, + trace2_data_string("load_midx_revindex", m->source->odb->repo, "source", "midx"); m->revindex_data = (const uint32_t *)m->chunk_revindex; return 0; } - trace2_data_string("load_midx_revindex", m->repo, + trace2_data_string("load_midx_revindex", m->source->odb->repo, "source", "rev"); if (m->has_chain) - get_split_midx_filename_ext(m->repo->hash_algo, &revindex_name, - m->object_dir, get_midx_checksum(m), + get_split_midx_filename_ext(m->source, &revindex_name, + get_midx_checksum(m), MIDX_EXT_REV); else - get_midx_filename_ext(m->repo->hash_algo, &revindex_name, - m->object_dir, get_midx_checksum(m), + get_midx_filename_ext(m->source, &revindex_name, + get_midx_checksum(m), MIDX_EXT_REV); - ret = load_revindex_from_disk(m->repo->hash_algo, + ret = load_revindex_from_disk(m->source->odb->repo->hash_algo, revindex_name.buf, m->num_objects, &m->revindex_map, diff --git a/pack-write.c b/pack-write.c index 6b06315f80..83eaf88541 100644 --- a/pack-write.c +++ b/pack-write.c @@ -84,7 +84,8 @@ const char *write_idx_file(struct repository *repo, } else { if (!index_name) { struct strbuf tmp_file = STRBUF_INIT; - fd = odb_mkstemp(&tmp_file, "pack/tmp_idx_XXXXXX"); + fd = odb_mkstemp(repo->objects, &tmp_file, + "pack/tmp_idx_XXXXXX"); index_name = strbuf_detach(&tmp_file, NULL); } else { unlink(index_name); @@ -259,7 +260,8 @@ char *write_rev_file_order(struct repository *repo, if (flags & WRITE_REV) { if (!rev_name) { struct strbuf tmp_file = STRBUF_INIT; - fd = odb_mkstemp(&tmp_file, "pack/tmp_rev_XXXXXX"); + fd = odb_mkstemp(repo->objects, &tmp_file, + "pack/tmp_rev_XXXXXX"); path = strbuf_detach(&tmp_file, NULL); } else { unlink(rev_name); @@ -342,7 +344,7 @@ static char *write_mtimes_file(struct repository *repo, if (!to_pack) BUG("cannot call write_mtimes_file with NULL packing_data"); - fd = odb_mkstemp(&tmp_file, "pack/tmp_mtimes_XXXXXX"); + fd = odb_mkstemp(repo->objects, &tmp_file, "pack/tmp_mtimes_XXXXXX"); mtimes_name = strbuf_detach(&tmp_file, NULL); f = hashfd(repo->hash_algo, fd, mtimes_name); @@ -531,27 +533,29 @@ struct hashfile *create_tmp_packfile(struct repository *repo, struct strbuf tmpname = STRBUF_INIT; int fd; - fd = odb_mkstemp(&tmpname, "pack/tmp_pack_XXXXXX"); + fd = odb_mkstemp(repo->objects, &tmpname, "pack/tmp_pack_XXXXXX"); *pack_tmp_name = strbuf_detach(&tmpname, NULL); return hashfd(repo->hash_algo, fd, *pack_tmp_name); } -static void rename_tmp_packfile(struct strbuf *name_prefix, const char *source, +static void rename_tmp_packfile(struct repository *repo, + struct strbuf *name_prefix, const char *source, const char *ext) { size_t name_prefix_len = name_prefix->len; strbuf_addstr(name_prefix, ext); - if (finalize_object_file(source, name_prefix->buf)) + if (finalize_object_file(repo, source, name_prefix->buf)) die("unable to rename temporary file to '%s'", name_prefix->buf); strbuf_setlen(name_prefix, name_prefix_len); } -void rename_tmp_packfile_idx(struct strbuf *name_buffer, +void rename_tmp_packfile_idx(struct repository *repo, + struct strbuf *name_buffer, char **idx_tmp_name) { - rename_tmp_packfile(name_buffer, *idx_tmp_name, "idx"); + rename_tmp_packfile(repo, name_buffer, *idx_tmp_name, "idx"); } void stage_tmp_packfiles(struct repository *repo, @@ -584,11 +588,11 @@ void stage_tmp_packfiles(struct repository *repo, hash); } - rename_tmp_packfile(name_buffer, pack_tmp_name, "pack"); + rename_tmp_packfile(repo, name_buffer, pack_tmp_name, "pack"); if (rev_tmp_name) - rename_tmp_packfile(name_buffer, rev_tmp_name, "rev"); + rename_tmp_packfile(repo, name_buffer, rev_tmp_name, "rev"); if (mtimes_tmp_name) - rename_tmp_packfile(name_buffer, mtimes_tmp_name, "mtimes"); + rename_tmp_packfile(repo, name_buffer, mtimes_tmp_name, "mtimes"); free(rev_tmp_name); free(mtimes_tmp_name); @@ -145,7 +145,8 @@ void stage_tmp_packfiles(struct repository *repo, struct pack_idx_option *pack_idx_opts, unsigned char hash[], char **idx_tmp_name); -void rename_tmp_packfile_idx(struct strbuf *basename, +void rename_tmp_packfile_idx(struct repository *repo, + struct strbuf *basename, char **idx_tmp_name); #endif diff --git a/packfile.c b/packfile.c index 70c7208f02..acb680966d 100644 --- a/packfile.c +++ b/packfile.c @@ -19,7 +19,7 @@ #include "tree-walk.h" #include "tree.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "midx.h" #include "commit-graph.h" #include "pack-revindex.h" @@ -359,8 +359,9 @@ void close_pack(struct packed_git *p) oidset_clear(&p->bad_objects); } -void close_object_store(struct raw_object_store *o) +void close_object_store(struct object_database *o) { + struct odb_source *source; struct packed_git *p; for (p = o->packed_git; p; p = p->next) @@ -369,9 +370,10 @@ void close_object_store(struct raw_object_store *o) else close_pack(p); - if (o->multi_pack_index) { - close_midx(o->multi_pack_index); - o->multi_pack_index = NULL; + for (source = o->sources; source; source = source->next) { + if (source->midx) + close_midx(source->midx); + source->midx = NULL; } close_commit_graph(o); @@ -933,22 +935,17 @@ static void prepare_pack(const char *full_name, size_t full_name_len, report_garbage(PACKDIR_FILE_GARBAGE, full_name); } -static void prepare_packed_git_one(struct repository *r, char *objdir, int local) +static void prepare_packed_git_one(struct odb_source *source) { - struct prepare_pack_data data; struct string_list garbage = STRING_LIST_INIT_DUP; + struct prepare_pack_data data = { + .m = source->midx, + .r = source->odb->repo, + .garbage = &garbage, + .local = source->local, + }; - data.m = r->objects->multi_pack_index; - - /* look for the multi-pack-index for this object directory */ - while (data.m && strcmp(data.m->object_dir, objdir)) - data.m = data.m->next; - - data.r = r; - data.garbage = &garbage; - data.local = local; - - for_each_file_in_pack_dir(objdir, prepare_pack, &data); + for_each_file_in_pack_dir(source->path, prepare_pack, &data); report_pack_garbage(data.garbage); string_list_clear(data.garbage, 0); @@ -965,14 +962,18 @@ static void prepare_packed_git(struct repository *r); unsigned long repo_approximate_object_count(struct repository *r) { if (!r->objects->approximate_object_count_valid) { - unsigned long count; - struct multi_pack_index *m; + struct odb_source *source; + unsigned long count = 0; struct packed_git *p; prepare_packed_git(r); - count = 0; - for (m = get_multi_pack_index(r); m; m = m->next) - count += m->num_objects; + + 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; + } + for (p = r->objects->packed_git; p; p = p->next) { if (open_pack_index(p)) continue; @@ -1029,16 +1030,15 @@ static void prepare_packed_git_mru(struct repository *r) static void prepare_packed_git(struct repository *r) { - struct object_directory *odb; + struct odb_source *source; if (r->objects->packed_git_initialized) return; - prepare_alt_odb(r); - for (odb = r->objects->odb; odb; odb = odb->next) { - int local = (odb == r->objects->odb); - prepare_multi_pack_index_one(r, odb->path, local); - prepare_packed_git_one(r, odb->path, local); + odb_prepare_alternates(r->objects); + for (source = r->objects->sources; source; source = source->next) { + prepare_multi_pack_index_one(source); + prepare_packed_git_one(source); } rearrange_packed_git(r); @@ -1048,7 +1048,7 @@ static void prepare_packed_git(struct repository *r) void reprepare_packed_git(struct repository *r) { - struct object_directory *odb; + struct odb_source *source; obj_read_lock(); @@ -1059,10 +1059,10 @@ void reprepare_packed_git(struct repository *r) * the lifetime of the process. */ r->objects->loaded_alternates = 0; - prepare_alt_odb(r); + odb_prepare_alternates(r->objects); - for (odb = r->objects->odb; odb; odb = odb->next) - odb_clear_loose_cache(odb); + for (source = r->objects->sources; source; source = source->next) + odb_clear_loose_cache(source); r->objects->approximate_object_count_valid = 0; r->objects->packed_git_initialized = 0; @@ -1076,32 +1076,22 @@ struct packed_git *get_packed_git(struct repository *r) return r->objects->packed_git; } -struct multi_pack_index *get_multi_pack_index(struct repository *r) +struct multi_pack_index *get_multi_pack_index(struct odb_source *source) { - prepare_packed_git(r); - return r->objects->multi_pack_index; -} - -struct multi_pack_index *get_local_multi_pack_index(struct repository *r) -{ - struct multi_pack_index *m = get_multi_pack_index(r); - - /* no need to iterate; we always put the local one first (if any) */ - if (m && m->local) - return m; - - return NULL; + prepare_packed_git(source->odb->repo); + return source->midx; } struct packed_git *get_all_packs(struct repository *r) { - struct multi_pack_index *m; - prepare_packed_git(r); - for (m = r->objects->multi_pack_index; m; m = m->next) { - uint32_t i; - for (i = 0; i < m->num_packs + m->num_packs_in_base; i++) - prepare_midx_pack(r, m, i); + + for (struct odb_source *source = r->objects->sources; source; source = source->next) { + struct multi_pack_index *m = source->midx; + if (!m) + continue; + for (uint32_t i = 0; i < m->num_packs + m->num_packs_in_base; i++) + prepare_midx_pack(m, i); } return r->objects->packed_git; @@ -1321,7 +1311,7 @@ static int retry_bad_packed_offset(struct repository *r, return OBJ_BAD; nth_packed_object_id(&oid, p, pack_pos_to_index(p, pos)); mark_bad_packed_object(p, &oid); - type = oid_object_info(r, &oid, NULL); + type = odb_read_object_info(r->objects, &oid, NULL); if (type <= OBJ_NONE) return OBJ_BAD; return type; @@ -1849,7 +1839,8 @@ void *unpack_entry(struct repository *r, struct packed_git *p, off_t obj_offset, oi.typep = &type; oi.sizep = &base_size; oi.contentp = &base; - if (oid_object_info_extended(r, &base_oid, &oi, 0) < 0) + if (odb_read_object_info_extended(r->objects, &base_oid, + &oi, 0) < 0) base = NULL; external_base = base; @@ -2082,16 +2073,15 @@ static int fill_pack_entry(const struct object_id *oid, int find_pack_entry(struct repository *r, const struct object_id *oid, struct pack_entry *e) { struct list_head *pos; - struct multi_pack_index *m; prepare_packed_git(r); - if (!r->objects->packed_git && !r->objects->multi_pack_index) - return 0; - for (m = r->objects->multi_pack_index; m; m = m->next) { - if (fill_midx_entry(r, oid, e, m)) + for (struct odb_source *source = r->objects->sources; source; source = source->next) + if (source->midx && fill_midx_entry(source->midx, oid, e)) return 1; - } + + if (!r->objects->packed_git) + return 0; list_for_each(pos, &r->objects->packed_git_mru) { struct packed_git *p = list_entry(pos, struct packed_git, mru); diff --git a/packfile.h b/packfile.h index 3a3c77cf05..f16753f2a9 100644 --- a/packfile.h +++ b/packfile.h @@ -3,10 +3,10 @@ #include "list.h" #include "object.h" -#include "object-store.h" +#include "odb.h" #include "oidset.h" -/* in object-store.h */ +/* in odb.h */ struct object_info; struct packed_git { @@ -147,8 +147,7 @@ void install_packed_git(struct repository *r, struct packed_git *pack); struct packed_git *get_packed_git(struct repository *r); struct list_head *get_packed_git_mru(struct repository *r); -struct multi_pack_index *get_multi_pack_index(struct repository *r); -struct multi_pack_index *get_local_multi_pack_index(struct repository *r); +struct multi_pack_index *get_multi_pack_index(struct odb_source *source); struct packed_git *get_all_packs(struct repository *r); /* @@ -183,12 +182,12 @@ int close_pack_fd(struct packed_git *p); uint32_t get_pack_fanout(struct packed_git *p, uint32_t value); -struct raw_object_store; +struct object_database; unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *); void close_pack_windows(struct packed_git *); void close_pack(struct packed_git *); -void close_object_store(struct raw_object_store *o); +void close_object_store(struct object_database *o); void unuse_pack(struct pack_window **); void clear_delta_base_cache(void); struct packed_git *add_packed_git(struct repository *r, const char *path, diff --git a/parallel-checkout.c b/parallel-checkout.c index 57c2dcaa8f..fba6aa65a6 100644 --- a/parallel-checkout.c +++ b/parallel-checkout.c @@ -57,12 +57,12 @@ void get_parallel_checkout_configs(int *num_workers, int *threshold) return; } - if (git_config_get_int("checkout.workers", num_workers)) + if (repo_config_get_int(the_repository, "checkout.workers", num_workers)) *num_workers = DEFAULT_NUM_WORKERS; else if (*num_workers < 1) *num_workers = online_cpus(); - if (git_config_get_int("checkout.thresholdForParallelism", threshold)) + if (repo_config_get_int(the_repository, "checkout.thresholdForParallelism", threshold)) *threshold = DEFAULT_THRESHOLD_FOR_PARALLELISM; } diff --git a/parse-options.c b/parse-options.c index a9a39ecaef..992ec9631f 100644 --- a/parse-options.c +++ b/parse-options.c @@ -68,6 +68,64 @@ static char *fix_filename(const char *prefix, const char *file) return prefix_filename_except_for_dash(prefix, file); } +static int do_get_int_value(const void *value, size_t precision, intmax_t *ret) +{ + switch (precision) { + case sizeof(int8_t): + *ret = *(int8_t *)value; + return 0; + case sizeof(int16_t): + *ret = *(int16_t *)value; + return 0; + case sizeof(int32_t): + *ret = *(int32_t *)value; + return 0; + case sizeof(int64_t): + *ret = *(int64_t *)value; + return 0; + default: + return -1; + } +} + +static intmax_t get_int_value(const struct option *opt, enum opt_parsed flags) +{ + intmax_t ret; + if (do_get_int_value(opt->value, opt->precision, &ret)) + BUG("invalid precision for option %s", optname(opt, flags)); + return ret; +} + +static enum parse_opt_result set_int_value(const struct option *opt, + enum opt_parsed flags, + intmax_t value) +{ + switch (opt->precision) { + case sizeof(int8_t): + *(int8_t *)opt->value = value; + return 0; + case sizeof(int16_t): + *(int16_t *)opt->value = value; + return 0; + case sizeof(int32_t): + *(int32_t *)opt->value = value; + return 0; + case sizeof(int64_t): + *(int64_t *)opt->value = value; + return 0; + default: + BUG("invalid precision for option %s", optname(opt, flags)); + } +} + +static int signed_int_fits(intmax_t value, size_t precision) +{ + size_t bits = precision * CHAR_BIT; + intmax_t upper_bound = INTMAX_MAX >> (bitsizeof(intmax_t) - bits); + intmax_t lower_bound = -upper_bound - 1; + return lower_bound <= value && value <= upper_bound; +} + static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, const struct option *opt, enum opt_parsed flags, @@ -89,35 +147,55 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, return opt->ll_callback(p, opt, NULL, unset); case OPTION_BIT: + { + intmax_t value = get_int_value(opt, flags); if (unset) - *(int *)opt->value &= ~opt->defval; + value &= ~opt->defval; else - *(int *)opt->value |= opt->defval; - return 0; + value |= opt->defval; + return set_int_value(opt, flags, value); + } case OPTION_NEGBIT: + { + intmax_t value = get_int_value(opt, flags); if (unset) - *(int *)opt->value |= opt->defval; + value |= opt->defval; else - *(int *)opt->value &= ~opt->defval; - return 0; + value &= ~opt->defval; + return set_int_value(opt, flags, value); + } case OPTION_BITOP: + { + intmax_t value = get_int_value(opt, flags); if (unset) BUG("BITOP can't have unset form"); - *(int *)opt->value &= ~opt->extra; - *(int *)opt->value |= opt->defval; - return 0; + value &= ~opt->extra; + value |= opt->defval; + return set_int_value(opt, flags, value); + } case OPTION_COUNTUP: - if (*(int *)opt->value < 0) - *(int *)opt->value = 0; - *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; - return 0; + { + size_t bits = CHAR_BIT * opt->precision; + intmax_t upper_bound = INTMAX_MAX >> (bitsizeof(intmax_t) - bits); + intmax_t value = get_int_value(opt, flags); + + if (value < 0) + value = 0; + if (unset) + value = 0; + else if (value < upper_bound) + value++; + else + return error(_("value for %s exceeds %"PRIdMAX), + optname(opt, flags), upper_bound); + return set_int_value(opt, flags, value); + } case OPTION_SET_INT: - *(int *)opt->value = unset ? 0 : opt->defval; - return 0; + return set_int_value(opt, flags, unset ? 0 : opt->defval); case OPTION_STRING: if (unset) @@ -199,23 +277,7 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"), arg, optname(opt, flags), (intmax_t)lower_bound, (intmax_t)upper_bound); - switch (opt->precision) { - case 1: - *(int8_t *)opt->value = value; - return 0; - case 2: - *(int16_t *)opt->value = value; - return 0; - case 4: - *(int32_t *)opt->value = value; - return 0; - case 8: - *(int64_t *)opt->value = value; - return 0; - default: - BUG("invalid precision for option %s", - optname(opt, flags)); - } + return set_int_value(opt, flags, value); } case OPTION_UNSIGNED: { @@ -266,7 +328,9 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, } struct parse_opt_cmdmode_list { - int value, *value_ptr; + intmax_t value; + void *value_ptr; + size_t precision; const struct option *opt; const char *arg; enum opt_parsed flags; @@ -280,7 +344,7 @@ static void build_cmdmode_list(struct parse_opt_ctx_t *ctx, for (; opts->type != OPTION_END; opts++) { struct parse_opt_cmdmode_list *elem = ctx->cmdmode_list; - int *value_ptr = opts->value; + void *value_ptr = opts->value; if (!(opts->flags & PARSE_OPT_CMDMODE) || !value_ptr) continue; @@ -292,10 +356,13 @@ static void build_cmdmode_list(struct parse_opt_ctx_t *ctx, CALLOC_ARRAY(elem, 1); elem->value_ptr = value_ptr; - elem->value = *value_ptr; + elem->precision = opts->precision; + if (do_get_int_value(value_ptr, opts->precision, &elem->value)) + optbug(opts, "has invalid precision"); elem->next = ctx->cmdmode_list; ctx->cmdmode_list = elem; } + BUG_if_bug("invalid 'struct option'"); } static char *optnamearg(const struct option *opt, const char *arg, @@ -317,7 +384,13 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p, char *opt_name, *other_opt_name; for (; elem; elem = elem->next) { - if (*elem->value_ptr == elem->value) + intmax_t new_value; + + if (do_get_int_value(elem->value_ptr, elem->precision, + &new_value)) + BUG("impossible: invalid precision"); + + if (new_value == elem->value) continue; if (elem->opt && @@ -327,7 +400,7 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p, elem->opt = opt; elem->arg = arg; elem->flags = flags; - elem->value = *elem->value_ptr; + elem->value = new_value; } if (result || !elem) @@ -586,10 +659,14 @@ static void parse_options_check(const struct option *opts) opts->long_name && !(opts->flags & PARSE_OPT_NONEG)) optbug(opts, "OPTION_SET_INT 0 should not be negatable"); switch (opts->type) { - case OPTION_COUNTUP: + case OPTION_SET_INT: case OPTION_BIT: case OPTION_NEGBIT: - case OPTION_SET_INT: + case OPTION_BITOP: + case OPTION_COUNTUP: + if (!signed_int_fits(opts->defval, opts->precision)) + optbug(opts, "has invalid defval"); + /* fallthru */ case OPTION_NUMBER: if ((opts->flags & PARSE_OPT_OPTARG) || !(opts->flags & PARSE_OPT_NOARG)) @@ -876,10 +953,16 @@ static void free_preprocessed_options(struct option *options) free(options); } +#define USAGE_NORMAL 0 +#define USAGE_FULL 1 +#define USAGE_TO_STDOUT 0 +#define USAGE_TO_STDERR 1 + static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *, const char * const *, const struct option *, - int, int); + int full_usage, + int usage_to_stderr); enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx, const struct option *options, @@ -1011,7 +1094,8 @@ enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx, } if (internal_help && !strcmp(arg + 2, "help-all")) - return usage_with_options_internal(ctx, usagestr, options, 1, 0); + return usage_with_options_internal(ctx, usagestr, options, + USAGE_FULL, USAGE_TO_STDOUT); if (internal_help && !strcmp(arg + 2, "help")) goto show_usage; switch (parse_long_opt(ctx, arg + 2, options)) { @@ -1052,7 +1136,8 @@ unknown: return PARSE_OPT_DONE; show_usage: - return usage_with_options_internal(ctx, usagestr, options, 0, 0); + return usage_with_options_internal(ctx, usagestr, options, + USAGE_NORMAL, USAGE_TO_STDOUT); } int parse_options_end(struct parse_opt_ctx_t *ctx) @@ -1261,7 +1346,7 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t if (!saw_empty_line && !*str) saw_empty_line = 1; - string_list_split(&list, str, '\n', -1); + string_list_split(&list, str, "\n", -1); for (j = 0; j < list.nr; j++) { const char *line = list.items[j].string; @@ -1367,7 +1452,8 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t void NORETURN usage_with_options(const char * const *usagestr, const struct option *opts) { - usage_with_options_internal(NULL, usagestr, opts, 0, 1); + usage_with_options_internal(NULL, usagestr, opts, + USAGE_NORMAL, USAGE_TO_STDERR); exit(129); } @@ -1375,9 +1461,16 @@ void show_usage_with_options_if_asked(int ac, const char **av, const char * const *usagestr, const struct option *opts) { - if (ac == 2 && !strcmp(av[1], "-h")) { - usage_with_options_internal(NULL, usagestr, opts, 0, 0); - exit(129); + if (ac == 2) { + if (!strcmp(av[1], "-h")) { + usage_with_options_internal(NULL, usagestr, opts, + USAGE_NORMAL, USAGE_TO_STDOUT); + exit(129); + } else if (!strcmp(av[1], "--help-all")) { + usage_with_options_internal(NULL, usagestr, opts, + USAGE_FULL, USAGE_TO_STDOUT); + exit(129); + } } } diff --git a/parse-options.h b/parse-options.h index 91c3e3c29b..706de9729f 100644 --- a/parse-options.h +++ b/parse-options.h @@ -172,6 +172,7 @@ struct option { .short_name = (s), \ .long_name = (l), \ .value = (v), \ + .precision = sizeof(*v), \ .help = (h), \ .flags = PARSE_OPT_NOARG|(f), \ .callback = NULL, \ @@ -182,6 +183,7 @@ struct option { .short_name = (s), \ .long_name = (l), \ .value = (v), \ + .precision = sizeof(*v), \ .help = (h), \ .flags = PARSE_OPT_NOARG|(f), \ } @@ -190,6 +192,7 @@ struct option { .short_name = (s), \ .long_name = (l), \ .value = (v), \ + .precision = sizeof(*v), \ .help = (h), \ .flags = PARSE_OPT_NOARG | (f), \ .defval = (i), \ @@ -238,6 +241,7 @@ struct option { .short_name = (s), \ .long_name = (l), \ .value = (v), \ + .precision = sizeof(*v), \ .help = (h), \ .flags = PARSE_OPT_NOARG|PARSE_OPT_NONEG, \ .defval = (set), \ @@ -248,6 +252,7 @@ struct option { .short_name = (s), \ .long_name = (l), \ .value = (v), \ + .precision = sizeof(*v), \ .help = (h), \ .flags = PARSE_OPT_NOARG, \ .defval = (b), \ @@ -260,6 +265,7 @@ struct option { .short_name = (s), \ .long_name = (l), \ .value = (v), \ + .precision = sizeof(*v), \ .help = (h), \ .flags = PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, \ .defval = 1, \ @@ -269,6 +275,7 @@ struct option { .short_name = (s), \ .long_name = (l), \ .value = (v), \ + .precision = sizeof(*v), \ .help = (h), \ .flags = PARSE_OPT_CMDMODE|PARSE_OPT_NOARG|PARSE_OPT_NONEG | (f), \ .defval = (i), \ @@ -616,6 +623,8 @@ int parse_opt_tracking_mode(const struct option *, const char *, int); #define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, "pathspec-from-file", v, N_("read pathspec from file")) #define OPT_PATHSPEC_FILE_NUL(v) OPT_BOOL(0, "pathspec-file-nul", v, N_("with --pathspec-from-file, pathspec elements are separated with NUL character")) #define OPT_AUTOSTASH(v) OPT_BOOL(0, "autostash", v, N_("automatically stash/stash pop before and after")) +#define OPT_DIFF_UNIFIED(v) OPT_INTEGER_F('U', "unified", v, N_("generate diffs with <n> lines context"), PARSE_OPT_NONEG) +#define OPT_DIFF_INTERHUNK_CONTEXT(v) OPT_INTEGER_F(0, "inter-hunk-context", v, N_("show context between diff hunks up to the specified number of lines"), PARSE_OPT_NONEG) #define OPT_IPVERSION(v) \ OPT_SET_INT_F('4', "ipv4", (v), N_("use IPv4 addresses only"), \ diff --git a/path-walk.c b/path-walk.c index 341bdd2ba4..f1ceed99e9 100644 --- a/path-walk.c +++ b/path-walk.c @@ -105,6 +105,24 @@ static void push_to_stack(struct path_walk_context *ctx, prio_queue_put(&ctx->path_stack, xstrdup(path)); } +static void add_path_to_list(struct path_walk_context *ctx, + const char *path, + enum object_type type, + struct object_id *oid, + int interesting) +{ + struct type_and_oid_list *list = strmap_get(&ctx->paths_to_lists, path); + + if (!list) { + CALLOC_ARRAY(list, 1); + list->type = type; + strmap_put(&ctx->paths_to_lists, path, list); + } + + list->maybe_interesting |= interesting; + oid_array_append(&list->oids, oid); +} + static int add_tree_entries(struct path_walk_context *ctx, const char *base_path, struct object_id *oid) @@ -129,7 +147,6 @@ static int add_tree_entries(struct path_walk_context *ctx, init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size); while (tree_entry(&desc, &entry)) { - struct type_and_oid_list *list; struct object *o; /* Not actually true, but we will ignore submodules later. */ enum object_type type = S_ISDIR(entry.mode) ? OBJ_TREE : OBJ_BLOB; @@ -190,17 +207,10 @@ static int add_tree_entries(struct path_walk_context *ctx, continue; } - if (!(list = strmap_get(&ctx->paths_to_lists, path.buf))) { - CALLOC_ARRAY(list, 1); - list->type = type; - strmap_put(&ctx->paths_to_lists, path.buf, list); - } - push_to_stack(ctx, path.buf); - - if (!(o->flags & UNINTERESTING)) - list->maybe_interesting = 1; + add_path_to_list(ctx, path.buf, type, &entry.oid, + !(o->flags & UNINTERESTING)); - oid_array_append(&list->oids, &entry.oid); + push_to_stack(ctx, path.buf); } free_tree_buffer(tree); @@ -377,15 +387,9 @@ static int setup_pending_objects(struct path_walk_info *info, if (!info->trees) continue; if (pending->path) { - struct type_and_oid_list *list; char *path = *pending->path ? xstrfmt("%s/", pending->path) : xstrdup(""); - if (!(list = strmap_get(&ctx->paths_to_lists, path))) { - CALLOC_ARRAY(list, 1); - list->type = OBJ_TREE; - strmap_put(&ctx->paths_to_lists, path, list); - } - oid_array_append(&list->oids, &obj->oid); + add_path_to_list(ctx, path, OBJ_TREE, &obj->oid, 1); free(path); } else { /* assume a root tree, such as a lightweight tag. */ @@ -396,19 +400,10 @@ static int setup_pending_objects(struct path_walk_info *info, case OBJ_BLOB: if (!info->blobs) continue; - if (pending->path) { - struct type_and_oid_list *list; - char *path = pending->path; - if (!(list = strmap_get(&ctx->paths_to_lists, path))) { - CALLOC_ARRAY(list, 1); - list->type = OBJ_BLOB; - strmap_put(&ctx->paths_to_lists, path, list); - } - oid_array_append(&list->oids, &obj->oid); - } else { - /* assume a root tree, such as a lightweight tag. */ + if (pending->path) + add_path_to_list(ctx, pending->path, OBJ_BLOB, &obj->oid, 1); + else oid_array_append(&tagged_blobs->oids, &obj->oid); - } break; case OBJ_COMMIT: @@ -503,7 +498,11 @@ int walk_objects_by_path(struct path_walk_info *info) if (prepare_revision_walk(info->revs)) die(_("failed to setup revision walk")); - /* Walk trees to mark them as UNINTERESTING. */ + /* + * Walk trees to mark them as UNINTERESTING. + * This is particularly important when 'edge_aggressive' is set. + */ + info->revs->edge_hint_aggressive = info->edge_aggressive; edge_repo = info->revs->repo; edge_tree_list = root_tree_list; mark_edges_uninteresting(info->revs, show_edge, diff --git a/path-walk.h b/path-walk.h index 473ee9d361..5ef5a8440e 100644 --- a/path-walk.h +++ b/path-walk.h @@ -51,6 +51,13 @@ struct path_walk_info { int prune_all_uninteresting; /** + * When 'edge_aggressive' is set, then the revision walk will use + * the '--object-edge-aggressive' option to mark even more objects + * as uninteresting. + */ + int edge_aggressive; + + /** * Specify a sparse-checkout definition to match our paths to. Do not * walk outside of this sparse definition. If the patterns are in * cone mode, then the search may prune directories that are outside @@ -15,7 +15,7 @@ #include "submodule-config.h" #include "path.h" #include "packfile.h" -#include "object-store.h" +#include "odb.h" #include "lockfile.h" #include "exec-cmd.h" @@ -397,7 +397,7 @@ static void adjust_git_path(struct repository *repo, strbuf_splice(buf, 0, buf->len, repo->index_file, strlen(repo->index_file)); else if (dir_prefix(base, "objects")) - replace_dir(buf, git_dir_len + 7, repo->objects->odb->path); + replace_dir(buf, git_dir_len + 7, repo->objects->sources->path); else if (repo_settings_get_hooks_path(repo) && dir_prefix(base, "hooks")) replace_dir(buf, git_dir_len + 5, repo_settings_get_hooks_path(repo)); else if (repo->different_commondir) diff --git a/pathspec.c b/pathspec.c index 2b4e434bc0..5993c4afa0 100644 --- a/pathspec.c +++ b/pathspec.c @@ -201,8 +201,7 @@ static void parse_pathspec_attr_match(struct pathspec_item *item, const char *va if (!value || !*value) die(_("attr spec must not be empty")); - string_list_split(&list, value, ' ', -1); - string_list_remove_empty_items(&list, 0); + string_list_split_f(&list, value, " ", -1, STRING_LIST_SPLIT_NONEMPTY); item->attr_check = attr_check_alloc(); CALLOC_ARRAY(item->attr_match, list.nr); @@ -492,7 +491,7 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags, if (!match) { const char *hint_path; - if (!have_git_dir()) + if ((flags & PATHSPEC_NO_REPOSITORY) || !have_git_dir()) die(_("'%s' is outside the directory tree"), copyfrom); hint_path = repo_get_work_tree(the_repository); @@ -614,6 +613,10 @@ void parse_pathspec(struct pathspec *pathspec, (flags & PATHSPEC_PREFER_FULL)) BUG("PATHSPEC_PREFER_CWD and PATHSPEC_PREFER_FULL are incompatible"); + if ((flags & PATHSPEC_NO_REPOSITORY) && + (~magic_mask & (PATHSPEC_ATTR | PATHSPEC_FROMTOP))) + BUG("PATHSPEC_NO_REPOSITORY is incompatible with PATHSPEC_ATTR and PATHSPEC_FROMTOP"); + /* No arguments with prefix -> prefix pathspec */ if (!entry) { if (flags & PATHSPEC_PREFER_FULL) diff --git a/pathspec.h b/pathspec.h index de537cff3c..5e3a6f1fe7 100644 --- a/pathspec.h +++ b/pathspec.h @@ -76,6 +76,11 @@ struct pathspec { * allowed, then it will automatically set for every pathspec. */ #define PATHSPEC_LITERAL_PATH (1<<6) +/* + * For git diff --no-index, indicate that we are operating without + * a repository or index. + */ +#define PATHSPEC_NO_REPOSITORY (1<<7) /** * Given command line arguments and a prefix, convert the input to @@ -184,6 +189,12 @@ int match_pathspec(struct index_state *istate, const char *name, int namelen, int prefix, char *seen, int is_dir); +/* Set both DO_MATCH_DIRECTORY and DO_MATCH_LEADING_PATHSPEC if is_dir true */ +int match_leading_pathspec(struct index_state *istate, + const struct pathspec *ps, + const char *name, int namelen, + int prefix, char *seen, int is_dir); + /* * Determine whether a pathspec will match only entire index entries (non-sparse * files and/or entire sparse directories). If the pathspec has the potential to diff --git a/perl/Git.pm b/perl/Git.pm index 6f47d653ab..090cf77dab 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -1061,6 +1061,19 @@ sub _close_cat_blob { delete @$self{@vars}; } +# Given PORT, a port number or service name, return its numerical +# value else undef. +sub port_num { + my ($port) = @_; + + # Port can be either a positive integer within the 16-bit range... + if ($port =~ /^\d+$/ && $port > 0 && $port <= (2**16 - 1)) { + return $port; + } + + # ... or a symbolic port (service name). + return scalar getservbyname($port, ''); +} =item credential_read( FILEHANDLE ) diff --git a/pkt-line.c b/pkt-line.c index a5bcbc96fb..fc583feb26 100644 --- a/pkt-line.c +++ b/pkt-line.c @@ -617,7 +617,7 @@ void packet_reader_init(struct packet_reader *reader, int fd, reader->buffer_size = sizeof(packet_buffer); reader->options = options; reader->me = "git"; - reader->hash_algo = &hash_algos[GIT_HASH_SHA1]; + reader->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY]; strbuf_init(&reader->scratch, 0); } @@ -61,6 +61,7 @@ # dirty нечиÑÑ‚, мръÑен (файл, индекÑ) # fallback резервен вариант # pathspec magic Ð¾Ð¿Ñ†Ð¸Ñ Ð·Ð° магичеÑки пътища +# pathspec шаблон за пътища # bitmap index Ð¸Ð½Ð´ÐµÐºÑ Ð½Ð° база битови маÑки # multi-pack bitmap многопакетната битова маÑка # ewah bitmap битова маÑка във формат EWAH @@ -237,6 +238,7 @@ # exit code изходен код # score оценка за Ñъвпадение # raw необработен +# mbox файл Ñ Ð¿Ð¾Ñ‰Ð° # # # ------------------------ @@ -266,8 +268,8 @@ msgid "" msgstr "" "Project-Id-Version: git 2.48\n" "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n" -"POT-Creation-Date: 2025-05-29 13:50+0200\n" -"PO-Revision-Date: 2025-05-29 20:25+0200\n" +"POT-Creation-Date: 2025-08-13 22:06+0200\n" +"PO-Revision-Date: 2025-08-13 22:07+0200\n" "Last-Translator: Alexander Shopov <ash@kambanaria.org>\n" "Language-Team: Bulgarian <dict@fsa-bg.org>\n" "Language: bg\n" @@ -277,6 +279,10 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #, c-format +msgid "%s cannot be negative" +msgstr "%s трÑбва да е неотрицателно" + +#, c-format msgid "Huh (%s)?" msgstr "ÐеуÑпешен анализ — „%s“." @@ -980,8 +986,8 @@ msgid "" "outside of your sparse-checkout definition, so will not be\n" "updated in the index:\n" msgstr "" -"Следните пътища напаÑват Ñ Ð¿ÑŠÑ‚Ð¸Ñ‰Ð° извън дефинициÑта за чаÑтично\n" -"изтеглÑне и нÑма да Ñе обновÑÑ‚ в индекÑа:\n" +"Следните пътища и шаблони напаÑват Ñ Ð¿ÑŠÑ‚Ð¸Ñ‰Ð° извън дефинициÑта\n" +"за чаÑтично изтеглÑне и нÑма да Ñе обновÑÑ‚ в индекÑа:\n" msgid "" "If you intend to update such entries, try one of the following:\n" @@ -1644,11 +1650,11 @@ msgstr "файлът „%s“ не може да Ñе прочете" #, c-format msgid "pathspec '%s' matches files outside the current directory" -msgstr "пътÑÑ‚ „%s“ Ñъвпада Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²Ðµ извън текущата директориÑ" +msgstr "шаблонът за пътища „%s“ Ñъвпада Ñ Ñ„Ð°Ð¹Ð»Ð¾Ð²Ðµ извън текущата директориÑ" #, c-format msgid "pathspec '%s' did not match any files" -msgstr "пътÑÑ‚ „%s“ не Ñъвпада Ñ Ð½Ð¸ÐºÐ¾Ð¹ файл" +msgstr "шаблонът за пътища „%s“ не Ñъвпада Ñ Ð½Ð¸ÐºÐ¾Ð¹ файл" #, c-format msgid "no such ref: %.*s" @@ -2115,7 +2121,7 @@ msgid "'%s' is already used by worktree at '%s'" msgstr "„%s“ вече Ñе ползва от работното дърво в „%s“" msgid "git add [<options>] [--] <pathspec>..." -msgstr "git add [ОПЦИЯ…] [--] ПЪТ…" +msgstr "git add [ОПЦИЯ…] [--] ШÐБЛОÐ_ЗÐ_ПЪТИЩÐ…" #, c-format msgid "cannot chmod %cx '%s'" @@ -2242,12 +2248,16 @@ msgid "adding files failed" msgstr "неуÑпешно добавÑне на файлове" #, c-format +msgid "'%s' cannot be negative" +msgstr "„%s“ трÑбва да е неотрицателно" + +#, c-format msgid "--chmod param '%s' must be either -x or +x" msgstr "параметърът към „--chmod“ — „%s“ може да е или „-x“, или „+x“" #, c-format msgid "'%s' and pathspec arguments cannot be used together" -msgstr "опциÑта „%s“ и път Ñа неÑъвмеÑтими" +msgstr "опциÑта „%s“ и шаблони за пътища Ñа неÑъвмеÑтими" #, c-format msgid "Nothing specified, nothing added.\n" @@ -2678,7 +2688,7 @@ msgid "" msgstr "" "git bisect start [--term-(new,bad)=УПРÐВЛЯВÐЩÐ_ДУМР--term-" "(old,good)=УПРÐВЛЯВÐЩÐ_ДУМÐ] [--no-checkout] [--first-parent] [ЛОШО " -"[ДОБРО…]] [--] [ПЪТ…]" +"[ДОБРО…]] [--] [ШÐБЛОÐ_ЗÐ_ПЪТИЩÐ…]" msgid "git bisect (good|bad) [<rev>...]" msgstr "git bisect (good|bad) [ВЕРСИЯ…]" @@ -4251,7 +4261,7 @@ msgid "checkout their version for unmerged files" msgstr "изтеглÑне на чуждата верÑÐ¸Ñ Ð½Ð° неÑлетите файлове" msgid "do not limit pathspecs to sparse entries only" -msgstr "без ограничаване на изброените пътища Ñамо до чаÑтично изтеглените" +msgstr "без ограничаване на шаблоните за пътища Ñамо до чаÑтично изтеглените" #, c-format msgid "options '-%c', '-%c', and '%s' cannot be used together" @@ -4341,7 +4351,9 @@ msgstr "използване на припокриващ режим" msgid "" "git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] " "[<pathspec>...]" -msgstr "git clean [-d] [-f] [-i] [-n] [-q] [-e ШÐБЛОÐ] [-x|-X] [--] [ПЪТ…]" +msgstr "" +"git clean [-d] [-f] [-i] [-n] [-q] [-e ШÐБЛОÐ] [-x|-X] [--] " +"[ШÐБЛОÐ_ЗÐ_ПЪТИЩÐ…]" #, c-format msgid "Removing %s\n" @@ -4595,7 +4607,7 @@ msgid "setup as shared repository" msgstr "наÑтройване за Ñподелено хранилище" msgid "pathspec" -msgstr "път" +msgstr "ШÐБЛОÐ_ЗÐ_ПЪТИЩÐ" msgid "initialize submodules in the clone" msgstr "инициализиране на подмодулите при това клониране" @@ -5007,10 +5019,10 @@ msgstr "" " [-i|-o] [--pathspec-from-file=ФÐЙЛ [--pathspec-file-nul]]\n" " [(--trailer ЛЕКСЕМÐ[(=|:)СТОЙÐОСТ])…] [-" "S[ИДЕÐТИФИКÐТОР_ÐÐ_КЛЮЧ]]\n" -" [--] [ПЪТ…]" +" [--] [ШÐБЛОÐ_ЗÐ_ПЪТИЩÐ…]" msgid "git status [<options>] [--] [<pathspec>...]" -msgstr "git status [ОПЦИЯ…] [--] [ПЪТ…]" +msgstr "git status [ОПЦИЯ…] [--] [ШÐБЛОÐ_ЗÐ_ПЪТИЩÐ…]" msgid "" "You asked to amend the most recent commit, but doing so would make\n" @@ -5516,24 +5528,24 @@ msgstr "git config list [ОПЦИЯ_ЗÐ_ФÐЙЛ] [ОПЦИЯ_ЗÐ_ИЗВЕЖД msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp] [--value=<value>] [--fixed-value] [--default=<default>] <name>" +"regexp] [--value=<pattern>] [--fixed-value] [--default=<default>] [--" +"url=<url>] <name>" msgstr "" "git config get [ОПЦИЯ_ЗÐ_ФÐЙЛ] [ОПЦИЯ_ЗÐ_ИЗВЕЖДÐÐЕ] [--includes] [--all] [--" -"regexp] [--value=СТОЙÐОСТ] [--fixed-value] [--default=СТÐÐДÐРТÐО] ИМЕ" +"regexp] [--value=ШÐБЛОÐ] [--fixed-value] [--default=СТÐÐДÐРТÐО] ИМЕ" msgid "" -"git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--" -"fixed-value] <name> <value>" +"git config set [<file-option>] [--type=<type>] [--all] [--value=<pattern>] " +"[--fixed-value] <name> <value>" msgstr "" -"git config set [ОПЦИЯ_ЗÐ_ФÐЙЛ] [--type=ВИД] [--all] [--value=СТОЙÐОСТ] [--" +"git config set [ОПЦИЯ_ЗÐ_ФÐЙЛ] [--type=ВИД] [--all] [--value=ШÐБЛОÐ] [--" "fixed-value] ИМЕ СТОЙÐОСТ" msgid "" -"git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] " +"git config unset [<file-option>] [--all] [--value=<pattern>] [--fixed-value] " "<name>" msgstr "" -"git config unset [ОПЦИЯ_ЗÐ_ФÐЙЛ] [--all] [--value=СТОЙÐОСТ] [--fixed-value] " -"ИМЕ" +"git config unset [ОПЦИЯ_ЗÐ_ФÐЙЛ] [--all] [--value=ШÐБЛОÐ] [--fixed-value] ИМЕ" msgid "git config rename-section [<file-option>] <old-name> <new-name>" msgstr "git config rename-section [ОПЦИЯ_ЗÐ_ФÐЙЛ] СТÐРО_ИМЕ ÐОВО_ИМЕ" @@ -5550,18 +5562,18 @@ msgstr "" msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] " +"regexp=<regexp>] [--value=<pattern>] [--fixed-value] [--default=<default>] " "<name>" msgstr "" "git config get [ОПЦИЯ_ЗÐ_ФÐЙЛ] [ОПЦИЯ_ЗÐ_ИЗВЕЖДÐÐЕ] [--includes] [--all] [--" -"regexp=РЕГ_ИЗР][--value=СТОЙÐОСТ] [--fixed-value] [--default=СТÐÐДÐРТÐО] ИМЕ" +"regexp=РЕГ_ИЗР][--value=ШÐБЛОÐ] [--fixed-value] [--default=СТÐÐДÐРТÐО] ИМЕ" msgid "" "git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] " -"[--value=<value>] [--fixed-value] <name> <value>" +"[--value=<pattern>] [--fixed-value] <name> <value>" msgstr "" "git config set [ОПЦИЯ_ЗÐ_ФÐЙЛ] [--type=ВИД] [--comment=СЪОБЩЕÐИЕ] [--all] [--" -"value=СТОЙÐОСТ] [--fixed-value] ИМЕ СТОЙÐОСТ" +"value=ШÐБЛОÐ] [--fixed-value] ИМЕ СТОЙÐОСТ" msgid "Config file location" msgstr "МеÑтоположение на ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð¸Ñ Ñ„Ð°Ð¹Ð»" @@ -6087,7 +6099,7 @@ msgid "working without -z is not supported" msgstr "опциÑта „-z“ е задължителна" msgid "pathspec arguments not supported" -msgstr "не Ñе поддържат опции за пътища" +msgstr "не Ñе поддържат аргументи Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð¸ за пътища" msgid "revision arguments not allowed" msgstr "не Ñе поддържат опции за верÑии" @@ -6244,7 +6256,7 @@ msgstr "git fast-export [ОПЦИЯ_ЗÐ_СПИСЪКÐ_С_ВЕРСИИ…]" msgid "Error: Cannot export nested tags unless --mark-tags is specified." msgstr "" -"Грешка: непреките етикети не Ñе изнаÑÑÑ‚, оÑвен ако не зададете „--mark-tags“." +"ГРЕШКÐ: непреките етикети не Ñе изнаÑÑÑ‚, оÑвен ако не зададете „--mark-tags“." msgid "--anonymize-map token cannot be empty" msgstr "опциÑта „--anonymize-map“ изиÑква аргумент" @@ -6437,23 +6449,6 @@ msgid "rejected %s because shallow roots are not allowed to be updated" msgstr "" "отхвърлÑне на „%s“, защото плитките върхове не може да бъдат обновÑвани" -#, c-format -msgid "" -"some local refs could not be updated; try running\n" -" 'git remote prune %s' to remove any old, conflicting branches" -msgstr "" -"нÑкои локални указатели не може да бъдат обновени. Изпълнете командата\n" -"„git remote prune %s“, за да премахнете оÑтарелите клони, които\n" -"предизвикват конфликта" - -#, c-format -msgid " (%s will become dangling)" -msgstr " (обектът „%s“ ще Ñе окаже извън клон)" - -#, c-format -msgid " (%s has become dangling)" -msgstr " (обектът „%s“ вече е извън клон)" - msgid "[deleted]" msgstr "[изтрит]" @@ -6501,6 +6496,19 @@ msgstr "" "ще изключи предупреждението, докато отдалечениÑÑ‚ указател HEAD не\n" "започне да Ñочи нещо друго." +#, c-format +msgid "" +"some local refs could not be updated; try running\n" +" 'git remote prune %s' to remove any old, conflicting branches" +msgstr "" +"нÑкои локални указатели не може да бъдат обновени. Изпълнете командата\n" +"„git remote prune %s“, за да премахнете оÑтарелите клони, които\n" +"предизвикват конфликта" + +#, c-format +msgid "fetching ref %s failed: %s" +msgstr "неуÑпешно доÑтавÑне на ÑƒÐºÐ°Ð·Ð°Ñ‚ÐµÐ»Ñ %s: %s" + msgid "multiple branches detected, incompatible with --set-upstream" msgstr "" "заÑечени Ñа множеÑтво клони, това е неÑъвмеÑтимо Ñ Ð¾Ð¿Ñ†Ð¸Ñта „--set-upstream“" @@ -6747,6 +6755,9 @@ msgstr "git for-each-ref [--merged [ПОДÐÐ’ÐÐЕ]] [--no-merged [ПОДÐÐ’Ð msgid "git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]" msgstr "git for-each-ref [--contains [ПОДÐÐ’ÐÐЕ]] [--no-contains [ПОДÐÐ’ÐÐЕ]]" +msgid "git for-each-ref [--start-after <marker>]" +msgstr "git for-each-ref [--start-after МÐРКЕР]" + msgid "quote placeholders suitably for shells" msgstr "цитиране подходÑщо за командни интерпретатори на обвивката" @@ -6762,6 +6773,12 @@ msgstr "цитиране подходÑщо за tcl" msgid "show only <n> matched refs" msgstr "извеждане Ñамо на този БРОЙ напаÑнати указатели" +msgid "marker" +msgstr "МÐРКЕР" + +msgid "start iteration after the provided marker" +msgstr "начало на итерирането Ñлед ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð¼Ð°Ñ€ÐºÐµÑ€" + msgid "respect format colors" msgstr "Ñпазване на цветовете на форма̀та" @@ -6786,9 +6803,15 @@ msgstr "изчитане на шаблоните за указатели от Ñ msgid "also include HEAD ref and pseudorefs" msgstr "включване и на ÑƒÐºÐ°Ð·Ð°Ñ‚ÐµÐ»Ñ â€žHEAD“ както и пÑевдо указателите" +msgid "cannot use --start-after with custom sort options" +msgstr "опциÑта „--start-after“ е неÑъвмеÑтима Ñ Ð¸Ð·Ñ€Ð¸Ñ‡Ð½Ð° подредба" + msgid "unknown arguments supplied with --stdin" msgstr "непознат аргумент към опциÑта „--stdin“" +msgid "cannot use --start-after with patterns" +msgstr "опциÑта „--start-after“ е неÑъвмеÑтима ÑÑŠÑ Ð·Ð°Ð´Ð°Ð²Ð°Ð½ÐµÑ‚Ð¾ на шаблони" + msgid "git for-each-repo --config=<config> [--] <arguments>" msgstr "git for-each-repo --config=ÐÐСТРОЙКР[--] ÐРГУМЕÐТ…" @@ -7190,6 +7213,10 @@ msgstr "препакетиране на вÑичко без най-Ð³Ð¾Ð»ÐµÐ¼Ð¸Ñ msgid "pack prefix to store a pack containing pruned objects" msgstr "Ð¿Ñ€ÐµÑ„Ð¸ÐºÑ Ð½Ð° имената на пакетите за окаÑтрени обекти" +msgid "skip maintenance tasks typically done in the foreground" +msgstr "" +"преÑкачане на дейноÑтите по поддръжката — типично за работа на преден режим" + #, c-format msgid "failed to parse gc.logExpiry value %s" msgstr "неразпозната ÑтойноÑÑ‚ на „gc.logExpiry“ %s" @@ -7268,14 +7295,14 @@ msgstr "" "„core.multiPackIndex“ е изключена" #, c-format -msgid "lock file '%s' exists, skipping maintenance" -msgstr "заключващиÑÑ‚ файл „%s“ ÑъщеÑтвува. ДейÑтвието Ñе преÑкача" - -#, c-format msgid "task '%s' failed" msgstr "неуÑпешно изпълнение на задачата „%s“" #, c-format +msgid "lock file '%s' exists, skipping maintenance" +msgstr "заключващиÑÑ‚ файл „%s“ ÑъщеÑтвува. ДейÑтвието Ñе преÑкача" + +#, c-format msgid "'%s' is not a valid task" msgstr "„%s“ не е правилна задача" @@ -7304,9 +7331,6 @@ msgstr "задача" msgid "run a specific task" msgstr "изпълнение на определена задача" -msgid "use at most one of --auto and --schedule=<frequency>" -msgstr "опциите „--auto“ и „--schedule=ЧЕСТОТГ Ñа неÑъвмеÑтими" - #, c-format msgid "unable to add '%s' value of '%s'" msgstr "неуÑпешно добавÑне на ÑтойноÑÑ‚ на „%s“ за „%s“" @@ -8199,11 +8223,7 @@ msgstr "" "във ФÐЙЛа" msgid "-L<range>:<file> cannot be used with pathspec" -msgstr "опциÑта „-LДИÐПÐЗОÐ:ФÐЙЛ“ не може да Ñе ползва Ñ Ð¿ÑŠÑ‚" - -#, c-format -msgid "Final output: %d %s\n" -msgstr "Резултат: %d %s\n" +msgstr "опциÑта „-LДИÐПÐЗОÐ:ФÐЙЛ“ не може да Ñе ползва Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½ за пътища" #, c-format msgid "git show %s: bad file" @@ -8934,6 +8954,9 @@ msgstr "извеждане на ÑтатиÑтиката Ñлед завършв msgid "(synonym to --stat)" msgstr "(пÑевдоним на „--stat“)" +msgid "show a compact-summary at the end of the merge" +msgstr "извеждане на кратко-обобщение Ñлед завършване на Ñливане" + msgid "add (at most <n>) entries from shortlog to merge commit message" msgstr "" "добавÑне (на макÑимум такъв БРОЙ) запиÑи от ÑÑŠÐºÑ€Ð°Ñ‚ÐµÐ½Ð¸Ñ Ð¶ÑƒÑ€Ð½Ð°Ð» в Ñъобщението " @@ -9209,10 +9232,6 @@ msgid "error: tag input does not pass fsck: %s" msgstr "ГРЕШКÐ: аргументът-етикет не минава проверка Ñ â€žfsck“: %s" #, c-format -msgid "%d (FSCK_IGNORE?) should never trigger this callback" -msgstr "%d (FSCK_IGNORE?) никога не трÑбва да задейÑтва тази функциÑ" - -#, c-format msgid "could not read tagged object '%s'" msgstr "обектът Ñ ÐµÑ‚Ð¸ÐºÐµÑ‚ не може да Ñе прочете: %s" @@ -9746,15 +9765,26 @@ msgstr "да Ñе използва бележката Ñочена от този msgid "unknown subcommand: `%s'" msgstr "непозната подкоманда: „%s“" -msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]" -msgstr "" -"git pack-objects --stdout [ОПЦИЯ…] [< СПИСЪК_С_УКÐЗÐТЕЛИ|< СПИСЪК_С_ОБЕКТИ]" - msgid "" -"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<pack-name>]\n" +" [--cruft] [--cruft-expiration=<time>]\n" +" [--stdout [--filter=<filter-spec>] | <base-name>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=<n>] [--path-walk] < <object-list>" msgstr "" -"git pack-objects [ОПЦИЯ…] ПРЕФИКС_ÐÐ_ИМЕТО [< СПИСЪК_С_УКÐЗÐТЕЛИ|< " -"СПИСЪК_С_ОБЕКТИ]" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=БРОЙ] [--depth=БРОЙ]\n" +" [--revs [--unpacked | --all]] [--keep-pack=ИМЕ_ÐÐ_ПÐКЕТ]\n" +" [--cruft] [--cruft-expiration=ВРЕМЕ]\n" +" [--stdout [--filter=ФИЛТЪР] | ОСÐОВÐО_ИМЕ]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=ВЕРСИЯ] [--path-walk] < СПИСЪК_С_ОБЕКТИ" #, c-format msgid "invalid --name-hash-version option: %d" @@ -9863,6 +9893,15 @@ msgstr "" msgid "unable to get type of object %s" msgstr "видът на обекта „%s“ не може да Ñе определи" +msgid "Compressing objects by path" +msgstr "КомпреÑиране на обектите на база пътÑ" + +#, c-format +msgid "Path-based delta compression using up to %d thread" +msgid_plural "Path-based delta compression using up to %d threads" +msgstr[0] "Делта компреÑиÑта на база път ще използва до %d нишка" +msgstr[1] "Делта компреÑиÑта на база път ще използва до %d нишки" + msgid "Compressing objects" msgstr "КомпреÑиране на обектите" @@ -9941,6 +9980,9 @@ msgstr "непакетираниÑÑ‚ обект в „%s“ не може да Ñ msgid "unable to force loose object" msgstr "оÑтаването на обекта непакетиран не може да Ñе наложи" +msgid "failed to pack objects via path-walk" +msgstr "неуÑпешно пакетиране на обекти чрез обхождане на дървото" + #, c-format msgid "not a rev '%s'" msgstr "„%s“ не е верÑиÑ" @@ -10058,6 +10100,11 @@ msgstr "използване на алгоритъм за чаÑтична Ð´Ð¾Ñ msgid "create thin packs" msgstr "Ñъздаване на Ñъкратени пакети" +msgid "use the path-walk API to walk objects when possible" +msgstr "" +"когато е възможно, използване на Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð½Ð¸Ñ Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð·Ð° обхождане на " +"обектите" + msgid "create packs suitable for shallow fetches" msgstr "пакетиране подходÑщо за плитко доÑтавÑне" @@ -10120,6 +10167,10 @@ msgstr "" "ползва %d" #, c-format +msgid "cannot use %s with %s" +msgstr "„%s“ и „%s“ Ñа неÑъвмеÑтими" + +#, c-format msgid "bad pack compression level %d" msgstr "неправилно ниво на компреÑиране при пакетиране: %d" @@ -10136,9 +10187,6 @@ msgstr "" "опциÑта „--thin“не може да Ñе използва за Ñъздаване на пакетни файлове Ñ " "индекÑ" -msgid "cannot use --filter with --stdin-packs" -msgstr "опциите „--filter“ и „--stdin-packs“ Ñа неÑъвмеÑтими" - msgid "cannot use internal rev list with --stdin-packs" msgstr "" "вътрешниÑÑ‚ ÑпиÑък на указатели и опциÑта „--stdin-packs“ Ñа неÑъвмеÑтими" @@ -10146,9 +10194,6 @@ msgstr "" msgid "cannot use internal rev list with --cruft" msgstr "вътрешниÑÑ‚ ÑпиÑък на верÑии и опциÑта „--cruft“ Ñа неÑъвмеÑтими" -msgid "cannot use --stdin-packs with --cruft" -msgstr "опциите „--stdin-packs“ и „--cruft“ Ñа неÑъвмеÑтими" - msgid "Enumerating objects" msgstr "ИзброÑване на обектите" @@ -10161,22 +10206,6 @@ msgstr "" "%<PRIu32>), преизползвани при пакетиране: %<PRIu32> (от %<PRIuMAX>)" msgid "" -"'git pack-redundant' is nominated for removal.\n" -"If you still use this command, please add an extra\n" -"option, '--i-still-use-this', on the command line\n" -"and let us know you still use it by sending an e-mail\n" -"to <git@vger.kernel.org>. Thanks.\n" -msgstr "" -"Командата „git pack-redundant“ е оÑтарÑла и предÑтои\n" -"пълното Ñ Ð¿Ñ€ÐµÐ¼Ð°Ñ…Ð²Ð°Ð½Ðµ. Ðко вÑе още Ñ Ð¿Ð¾Ð»Ð·Ð²Ð°Ñ‚Ðµ, добавете\n" -"опциÑта „--i-still-use-this“ на ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð¸Ñ Ñ€ÐµÐ´ и молим да\n" -"ни извеÑтите Ñ Ðµ-пиÑмо до пощенÑÐºÐ¸Ñ ÑпиÑък:\n" -"<git@vger.kernel.org>.\n" - -msgid "refusing to run without --i-still-use-this" -msgstr "трÑбва да добавите и опциÑта „--i-still-use-this“" - -msgid "" "git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude " "<pattern>]" msgstr "" @@ -11511,6 +11540,18 @@ msgstr "" msgid "unknown --mirror argument: %s" msgstr "неправилна ÑтойноÑÑ‚ за „--mirror“: %s" +#, c-format +msgid "remote name '%s' is a subset of existing remote '%s'" +msgstr "" +"името на отдалеченото хранилище „%s“ Ñе Ñъдържа в името на ÑъщеÑтвуващо " +"отдалечено хранилище „%s“ като подниз" + +#, c-format +msgid "remote name '%s' is a superset of existing remote '%s'" +msgstr "" +"името на отдалеченото хранилище „%s“ Ñъдържа в името Ñи ÑъщеÑтвуващо " +"отдалечено хранилище „%s“ като подниз" + msgid "fetch the remote branches" msgstr "отдалечените клони не може да бъдат доÑтавени" @@ -11821,14 +11862,6 @@ msgid "Could not set up %s" msgstr "„%s“ не може да Ñе наÑтрои" #, c-format -msgid " %s will become dangling!" -msgstr "„%s“ ще Ñе превърне в обект извън клоните!" - -#, c-format -msgid " %s has become dangling!" -msgstr "„%s“ Ñе превърна в обект извън клоните!" - -#, c-format msgid "Pruning %s" msgstr "ОкаÑтрÑне на „%s“" @@ -11892,11 +11925,11 @@ msgstr "повече подробноÑти. ПоÑÑ‚Ð°Ð²Ñ Ñе пред поРmsgid "" "git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n" -"[--write-midx] [--name-hash-version=<n>]" +"[--write-midx] [--name-hash-version=<n>] [--path-walk]" msgstr "" "git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=БРОЙ] [--depth=БРОЙ] [--threads=БРОЙ] [--keep-pack=ИМЕ_ÐÐ_ПÐКЕТ]\n" -"[--write-midx] [--name-hash-version=БРОЙ]" +"[--write-midx] [--name-hash-version=ВЕРСИЯ] [--path-walk]" msgid "" "Incremental repacks are incompatible with bitmap indexes. Use\n" @@ -11994,6 +12027,9 @@ msgid "" msgstr "" "укажете Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð·Ð° контролни Ñуми за групиране на подобните обекти по път" +msgid "pass --path-walk to git-pack-objects" +msgstr "подаване на опциÑта „--path-walk“ на командата „git-pack-objects“" + msgid "do not run git-update-server-info" msgstr "без изпълнение на командата „git-update-server-info“" @@ -12328,7 +12364,7 @@ msgstr "не Ñе поддържа прилагане наново и на поРmsgid "" "git rerere [clear | forget <pathspec>... | diff | status | remaining | gc]" -msgstr "git rerere [clear|forget ПЪТ…|diff|status|remaining|gc]" +msgstr "git rerere [clear|forget ШÐБЛОÐ_ЗÐ_ПЪТИЩÐ…|diff|status|remaining|gc]" msgid "register clean resolutions in index" msgstr "региÑтриране на чиÑти корекции на конфликти в индекÑа" @@ -12345,7 +12381,7 @@ msgid "" msgstr "git reset [--mixed|--soft|--hard|--merge|--keep] [-q] [ПОДÐÐ’ÐÐЕ]" msgid "git reset [-q] [<tree-ish>] [--] <pathspec>..." -msgstr "git reset [-q] [УКÐЗÐТЕЛ_КЪМ_ДЪРВО] [--] ПЪТИЩÐ…" +msgstr "git reset [-q] [УКÐЗÐТЕЛ_КЪМ_ДЪРВО] [--] ШÐБЛОÐ_ЗÐ_ПЪТИЩÐ…" msgid "" "git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]" @@ -12354,7 +12390,7 @@ msgstr "" "[УКÐЗÐТЕЛ_КЪМ_ДЪРВО]" msgid "git reset --patch [<tree-ish>] [--] [<pathspec>...]" -msgstr "git reset --patch [УКÐЗÐТЕЛ_КЪМ_ДЪРВО] [--] [ПЪТИЩÐ…]" +msgstr "git reset --patch [УКÐЗÐТЕЛ_КЪМ_ДЪРВО] [--] [ШÐБЛОÐ_ЗÐ_ПЪТИЩÐ…]" msgid "mixed" msgstr "ÑмеÑено (mixed)" @@ -12639,7 +12675,7 @@ msgid "" msgstr "" "git rm [-f|--force] [-n] [-r] [--cached] [--ignore-unmatch]\n" " [--quiet] [--pathspec-from-file=ФÐЙЛ [--pathspec-file-nul]]\n" -" [--] [ПЪТ…]" +" [--] [ШÐБЛОÐ_ЗÐ_ПЪТИЩÐ…]" msgid "" "the following file has staged content different from both the\n" @@ -12699,7 +12735,7 @@ msgstr "" "изтриване" msgid "No pathspec was given. Which files should I remove?" -msgstr "Ðе Ñа зададени пътища. Кои файлове да Ñе изтриÑÑ‚?" +msgstr "Ðе Ñа зададени шаблони за пътища. Кои файлове да Ñе изтриÑÑ‚?" msgid "please stage your changes to .gitmodules or stash them to proceed" msgstr "" @@ -13148,7 +13184,7 @@ msgstr "" "quiet]\n" " [-u|--include-untracked] [-a|--all] [(-m|--message) СЪОБЩЕÐИЕ]\n" " [--pathspec-from-file=ФÐЙЛ [--pathspec-file-nul]]\n" -" [--] [ПЪТ…]]" +" [--] [ШÐБЛОÐ_ЗÐ_ПЪТИЩÐ…]]" msgid "" "git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | " @@ -13162,17 +13198,23 @@ msgstr "" msgid "git stash create [<message>]" msgstr "git stash create [СЪОБЩЕÐИЕ]" +msgid "git stash export (--print | --to-ref <ref>) [<stash>...]" +msgstr "git stash export (--print | --to-ref УКÐЗÐТЕЛ) [СКÐТÐÐО…]" + +msgid "git stash import <commit>" +msgstr "git bisect import ПОДÐÐ’ÐÐЕ" + #, c-format msgid "'%s' is not a stash-like commit" msgstr "„%s“ не е подаване, приличащо на нещо Ñкатано" +msgid "No stash entries found." +msgstr "Ðе е открито нищо Ñкатано." + #, c-format msgid "Too many revisions specified:%s" msgstr "Указани Ñа прекалено много верÑии:%s" -msgid "No stash entries found." -msgstr "Ðе е открито нищо Ñкатано." - #, c-format msgid "%s is not a valid reference" msgstr "„%s“ е неправилно име за указател" @@ -13327,19 +13369,74 @@ msgstr "Ñкатаване и на неÑледените файлове" msgid "include ignore files" msgstr "Ñкатаване и на игнорираните файлове" -msgid "skip and remove all lines starting with comment character" -msgstr "пропуÑкане на вÑички редове, които започват Ñ â€ž#“" +#, c-format +msgid "cannot parse commit %s" +msgstr "подаването „%s“ не може да Ñе анализира" -msgid "prepend comment character and space to each line" -msgstr "добавÑне на „# “ в началото на вÑеки ред" +#, c-format +msgid "invalid author or committer for %s" +msgstr "неправилен автор или подаващ на „%s“" + +msgid "could not write commit" +msgstr "подаването не може да Ñе запази" #, c-format -msgid "Expecting a full ref name, got %s" -msgstr "ИзиÑква Ñе пълно име на указател, а не „%s“" +msgid "not a valid revision: %s" +msgstr "не е верÑиÑ: %s" #, c-format -msgid "could not get a repository handle for submodule '%s'" -msgstr "не може да Ñе получи връзка към хранилище за подмодула „%s“" +msgid "not a commit: %s" +msgstr "не е подаване: „%s“" + +#, c-format +msgid "%s is not a valid exported stash commit" +msgstr "„%s“ не е изнеÑено Ñкатано подаване" + +#, c-format +msgid "found root commit %s with invalid data" +msgstr "открито е начално подаване „%s“ Ñ Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»Ð½Ð¸ данни" + +#, c-format +msgid "found stash commit %s without expected prefix" +msgstr "открито е Ñкатано подаване „%s“ без Ð¾Ñ‡Ð°ÐºÐ²Ð°Ð½Ð¸Ñ Ð¿Ñ€ÐµÑ„Ð¸ÐºÑ" + +#, c-format +msgid "cannot parse parents of commit: %s" +msgstr "не може да Ñе анализират родителÑките Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ Ð½Ð°: „%s“" + +#, c-format +msgid "%s does not look like a stash commit" +msgstr "„%s“ не изглежда да е Ñкатано подаване" + +#, c-format +msgid "cannot read commit buffer for %s" +msgstr "буферът за подаване на „%s“ не може да Ñе прочете" + +#, c-format +msgid "cannot save the stash for %s" +msgstr "Ñкатаното за „%s“ не може да Ñе запази" + +msgid "unable to write base commit" +msgstr "базовото подаване не може да Ñе запише" + +#, c-format +msgid "unable to find stash entry %s" +msgstr "ÑкатаниÑÑ‚ Ð·Ð°Ð¿Ð¸Ñ Ð·Ð° „%s“ не може да Ñе открие" + +msgid "print the object ID instead of writing it to a ref" +msgstr "извеждане на идентификатор на обект вмеÑто запазването му в указател" + +msgid "save the data to the given ref" +msgstr "запазване на данните в Ð´Ð°Ð´ÐµÐ½Ð¸Ñ ÑƒÐºÐ°Ð·Ð°Ñ‚ÐµÐ»" + +msgid "exactly one of --print and --to-ref is required" +msgstr "необходима е точно една от опциите „--print“ и „--to-ref“" + +msgid "skip and remove all lines starting with comment character" +msgstr "пропуÑкане на вÑички редове, които започват Ñ â€ž#“" + +msgid "prepend comment character and space to each line" +msgstr "добавÑне на „# “ в началото на вÑеки ред" #, c-format msgid "" @@ -13350,6 +13447,10 @@ msgstr "" "за Ñебе Ñи." #, c-format +msgid "could not get a repository handle for submodule '%s'" +msgstr "не може да Ñе получи връзка към хранилище за подмодула „%s“" + +#, c-format msgid "No url found for submodule path '%s' in .gitmodules" msgstr "Във файла „.gitmodules“ не е открит Ð°Ð´Ñ€ÐµÑ Ð·Ð° Ð¿ÑŠÑ‚Ñ ÐºÑŠÐ¼ подмодул „%s“" @@ -13709,6 +13810,10 @@ msgstr "" "но той не е на никой клон" #, c-format +msgid "Expecting a full ref name, got %s" +msgstr "ИзиÑква Ñе пълно име на указател, а не „%s“" + +#, c-format msgid "Unable to find current revision in submodule path '%s'" msgstr "Текущата верÑÐ¸Ñ Ð·Ð° подмодула в „%s“ липÑва" @@ -13914,6 +14019,10 @@ msgstr "" "или „../“" #, c-format +msgid "submodule name '%s' already used for path '%s'" +msgstr "името на подмодул „%s“ вече Ñе ползва за Ð¿ÑŠÑ‚Ñ â€ž%s“" + +#, c-format msgid "'%s' is not a valid submodule name" msgstr "„%s“ е неправилно име за подмодул" @@ -14568,10 +14677,6 @@ msgid "Preparing worktree (checking out '%s')" msgstr "ПриготвÑне на работното дърво (изтеглÑне на „%s“)" #, c-format -msgid "unreachable: invalid reference: %s" -msgstr "недоÑтижим обект: неправилен указател: %s" - -#, c-format msgid "Preparing worktree (detached HEAD %s)" msgstr "ПодготвÑне на работно дърво (указателÑÑ‚ „HEAD“ не Ñвързан: %s)" @@ -14752,7 +14857,7 @@ msgstr "поправÑне: %s: „%s“" #, c-format msgid "error: %s: %s" -msgstr "грешка: %s: „%s“" +msgstr "ГРЕШКÐ: %s: „%s“" msgid "git write-tree [--missing-ok] [--prefix=<prefix>/]" msgstr "git write-tree [--missing-ok] [--prefix=ПРЕФИКС/]" @@ -14876,8 +14981,9 @@ msgstr "Пратката изиÑква ÑÐ»ÐµÐ´Ð½Ð¸Ñ Ñ„Ð¸Ð»Ñ‚ÑŠÑ€: %s" msgid "unable to dup bundle descriptor" msgstr "неуÑпешно дублиране на деÑкриптора на пратката Ñ â€ždup“" +# заради git-po-helper не ползваме „git pack-objects“ msgid "Could not spawn pack-objects" -msgstr "Командата „git pack-objects“ не може да Ñе Ñтартира" +msgstr "ПроцеÑÑŠÑ‚ на git — „pack-objects“ не може да Ñе Ñтартира" msgid "pack-objects died" msgstr "Командата „git pack-objects“ не завърши уÑпешно" @@ -15684,7 +15790,7 @@ msgid "Finding commits for commit graph among packed objects" msgstr "Откриване на подаваниÑта в гра̀фа измежду пакетираните обекти" msgid "Finding extra edges in commit graph" -msgstr "Откриване на още върхове в гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта" +msgstr "Откриване на още ребра в гра̀фа Ñ Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñта" msgid "failed to write correct number of base graph ids" msgstr "" @@ -16238,14 +16344,6 @@ msgid "bad numeric config value '%s' for '%s' in %s: %s" msgstr "неправилна чиÑлова ÑтойноÑÑ‚ „%s“ за „%s“ в %s: %s" #, c-format -msgid "invalid value for variable %s" -msgstr "неправилна ÑтойноÑÑ‚ за променливата „%s“" - -#, c-format -msgid "ignoring unknown core.fsync component '%s'" -msgstr "преÑкачане на Ð½ÐµÐ¿Ð¾Ð·Ð½Ð°Ñ‚Ð¸Ñ ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ‚ в наÑтройката „core.fsync“: „%s“" - -#, c-format msgid "bad boolean config value '%s' for '%s'" msgstr "неправилна булева ÑтойноÑÑ‚ „%s“ за „%s“" @@ -16258,49 +16356,6 @@ msgid "'%s' for '%s' is not a valid timestamp" msgstr "„%s“ не е правилна ÑтойноÑÑ‚ за време за „%s“" #, c-format -msgid "abbrev length out of range: %d" -msgstr "дължината на Ñъкращаване е извън диапазона ([4; 40]): %d" - -#, c-format -msgid "bad zlib compression level %d" -msgstr "неправилно ниво на компреÑиране: %d" - -#, c-format -msgid "%s cannot contain newline" -msgstr "%s не може да Ñъдържа нови редове" - -#, c-format -msgid "%s must have at least one character" -msgstr "%s трÑбва да Ñъдържа поне един знак" - -#, c-format -msgid "ignoring unknown core.fsyncMethod value '%s'" -msgstr "непознатата ÑтойноÑÑ‚ за „core.fsyncMethod“ — „%s“ Ñе преÑкача" - -msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" -msgstr "" -"наÑтройката „core.fsyncObjectFiles“ е оÑтарÑла и е заменена Ñ â€žcore.fsync“" - -#, c-format -msgid "invalid mode for object creation: %s" -msgstr "неправилен режим за Ñъздаването на обекти: %s" - -#, c-format -msgid "malformed value for %s" -msgstr "неправилна ÑтойноÑÑ‚ за „%s“" - -#, c-format -msgid "malformed value for %s: %s" -msgstr "неправилна ÑтойноÑÑ‚ за „%s“: „%s“" - -msgid "must be one of nothing, matching, simple, upstream or current" -msgstr "" -"трÑбва да е една от Ñледните ÑтойноÑти: „nothing“ (без изтлаÑкване при липÑа " -"на указател), „matching“ (вÑички клони ÑÑŠÑ Ñъвпадащи имена), „simple“ " -"(клонът ÑÑŠÑ Ñъщото име, от който Ñе издърпва), „upstream“ (клонът, от който " -"Ñе издърпва) или „current“ (клонът ÑÑŠÑ Ñъщото име)" - -#, c-format msgid "unable to load config blob object '%s'" msgstr "обектът-BLOB „%s“ Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ð¸ не може да Ñе зареди" @@ -16833,8 +16888,8 @@ msgstr "ÑтандартниÑÑ‚ вход не може да Ñе Ñравни Ñ msgid "cannot compare a named pipe to a directory" msgstr "именован канал не може да Ñе Ñравни Ñ Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ" -msgid "git diff --no-index [<options>] <path> <path>" -msgstr "git diff --no-index [ОПЦИЯ…] ПЪТ ПЪТ" +msgid "git diff --no-index [<options>] <path> <path> [<pathspec>...]" +msgstr "git diff --no-index [ОПЦИЯ…] ПЪТ ПЪТ [ШÐБЛОÐ_ЗÐ_ПЪТИЩÐ…]" msgid "" "Not a git repository. Use --no-index to compare two paths outside a working " @@ -16843,6 +16898,13 @@ msgstr "" "Ðе е хранилище на git. Ползвайте опциÑта „--no-index“, за да Ñравните " "пътища извън работно дърво" +msgid "" +"Limiting comparison with pathspecs is only supported if both paths are " +"directories." +msgstr "" +"Ограничаването на Ñравнението чрез шаблони за пътища Ñе поддържа, Ñамо " +"когато и двата Ð¿ÑŠÑ‚Ñ Ñа директории." + #, c-format msgid " Failed to parse dirstat cut-off percentage '%s'\n" msgstr "" @@ -16902,7 +16964,7 @@ msgstr "" "външната програма за разлики завърши неуÑпешно. Спиране на работата при „%s“" msgid "--follow requires exactly one pathspec" -msgstr "опциÑта „--follow“ изиÑква точно един път" +msgstr "опциÑта „--follow“ изиÑква точно един шаблон за пътища" #, c-format msgid "pathspec magic not supported by --follow: %s" @@ -17365,7 +17427,7 @@ msgstr "ÐÑма път на име „%s“ в разликата" #, c-format msgid "pathspec '%s' did not match any file(s) known to git" -msgstr "пътÑÑ‚ „%s“ не Ñъвпада Ñ Ð½Ð¸ÐºÐ¾Ð¹ файл в git" +msgstr "шаблонът за пътища „%s“ не Ñъвпада Ñ Ð½Ð¸ÐºÐ¾Ð¹ файл в git" #, c-format msgid "unrecognized pattern: '%s'" @@ -17438,6 +17500,57 @@ msgid "bad git namespace path \"%s\"" msgstr "неправилен път към проÑтранÑтва от имена „%s“" #, c-format +msgid "invalid value for variable %s" +msgstr "неправилна ÑтойноÑÑ‚ за променливата „%s“" + +#, c-format +msgid "ignoring unknown core.fsync component '%s'" +msgstr "преÑкачане на Ð½ÐµÐ¿Ð¾Ð·Ð½Ð°Ñ‚Ð¸Ñ ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ‚ в наÑтройката „core.fsync“: „%s“" + +#, c-format +msgid "abbrev length out of range: %d" +msgstr "дължината на Ñъкращаване е извън диапазона ([4; 40]): %d" + +#, c-format +msgid "bad zlib compression level %d" +msgstr "неправилно ниво на компреÑиране: %d" + +#, c-format +msgid "%s cannot contain newline" +msgstr "%s не може да Ñъдържа нови редове" + +#, c-format +msgid "%s must have at least one character" +msgstr "%s трÑбва да Ñъдържа поне един знак" + +#, c-format +msgid "ignoring unknown core.fsyncMethod value '%s'" +msgstr "непознатата ÑтойноÑÑ‚ за „core.fsyncMethod“ — „%s“ Ñе преÑкача" + +msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" +msgstr "" +"наÑтройката „core.fsyncObjectFiles“ е оÑтарÑла и е заменена Ñ â€žcore.fsync“" + +#, c-format +msgid "invalid mode for object creation: %s" +msgstr "неправилен режим за Ñъздаването на обекти: %s" + +#, c-format +msgid "malformed value for %s" +msgstr "неправилна ÑтойноÑÑ‚ за „%s“" + +#, c-format +msgid "malformed value for %s: %s" +msgstr "неправилна ÑтойноÑÑ‚ за „%s“: „%s“" + +msgid "must be one of nothing, matching, simple, upstream or current" +msgstr "" +"трÑбва да е една от Ñледните ÑтойноÑти: „nothing“ (без изтлаÑкване при липÑа " +"на указател), „matching“ (вÑички клони ÑÑŠÑ Ñъвпадащи имена), „simple“ " +"(клонът ÑÑŠÑ Ñъщото име, от който Ñе издърпва), „upstream“ (клонът, от който " +"Ñе издърпва) или „current“ (клонът ÑÑŠÑ Ñъщото име)" + +#, c-format msgid "too many args to run %s" msgstr "прекалено много аргументи за изпълнение „%s“" @@ -18173,6 +18286,36 @@ msgstr "не може да Ñе ползва празно име като иде msgid "name consists only of disallowed characters: %s" msgstr "името Ñъдържа Ñамо непозволени знаци: „%s“" +msgid "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>" +msgstr "" +"git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) ПÐПКÐ] < ФÐЙЛ_С_ПОЩÐ" + +msgid "no IMAP host specified" +msgstr "не е указан хоÑÑ‚ за IMAP" + +msgid "" +"set the IMAP host with 'git config imap.host <host>'.\n" +"(e.g., 'git config imap.host imaps://imap.example.com')" +msgstr "" +"задайте хоÑта за IMAP Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð°Ñ‚Ð°:\n" +"\n" +" git config imap.host ХОСТ\n" +"\n" +"(например: „git config imap.host imaps://imap.example.com“)" + +msgid "no IMAP folder specified" +msgstr "не е указана папка за IMAP" + +msgid "" +"set the target folder with 'git config imap.folder <folder>'.\n" +"(e.g., 'git config imap.folder Drafts')" +msgstr "" +"задайте целевата папка Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð°Ñ‚Ð°:\n" +"\n" +" git config imap.folder ПÐПКÐ\n" +"\n" +"(например: „git config imap.folder Drafts“)" + msgid "expected 'tree:<depth>'" msgstr "очаква Ñе „tree:ДЪЛБОЧИÐГ" @@ -19174,6 +19317,26 @@ msgid "invalid object name '%.*s'." msgstr "неправилно име на обект: „%.*s“" #, c-format +msgid "invalid object type \"%s\"" +msgstr "неправилен вид обект: „%s“" + +#, c-format +msgid "object %s is a %s, not a %s" +msgstr "обектът „%s“ е %s, а не %s" + +#, c-format +msgid "object %s has unknown type id %d" +msgstr "обектът „%s“ е непознат вид: %d" + +#, c-format +msgid "unable to parse object: %s" +msgstr "обектът „%s“ не може да Ñе анализира" + +#, c-format +msgid "hash mismatch %s" +msgstr "разлика в контролната Ñума: „%s“" + +#, c-format msgid "object directory %s does not exist; check .git/objects/info/alternates" msgstr "" "директориÑта за обекти „%s“ не ÑъщеÑтвува, проверете „.git/objects/info/" @@ -19243,26 +19406,6 @@ msgid "%s is not a valid '%s' object" msgstr "„%s“ е неправилен обект от вид „%s“" #, c-format -msgid "invalid object type \"%s\"" -msgstr "неправилен вид обект: „%s“" - -#, c-format -msgid "object %s is a %s, not a %s" -msgstr "обектът „%s“ е %s, а не %s" - -#, c-format -msgid "object %s has unknown type id %d" -msgstr "обектът „%s“ е непознат вид: %d" - -#, c-format -msgid "unable to parse object: %s" -msgstr "обектът „%s“ не може да Ñе анализира" - -#, c-format -msgid "hash mismatch %s" -msgstr "разлика в контролната Ñума: „%s“" - -#, c-format msgid "duplicate entry when writing bitmap index: %s" msgstr "повтарÑщ Ñе Ð·Ð°Ð¿Ð¸Ñ Ð¿Ñ€Ð¸ запазване на Ð¸Ð½Ð´ÐµÐºÑ Ð½Ð° база битови маÑки: „%s“" @@ -19273,9 +19416,6 @@ msgstr "опит за ÑъхранÑване на подаване, което Ð msgid "too many pseudo-merges" msgstr "прекалено много пÑевдо ÑливаниÑ" -msgid "trying to write commit not in index" -msgstr "опит за запиÑване на обект за подаване извън индекÑа" - msgid "failed to load bitmap index (corrupted?)" msgstr "" "индекÑÑŠÑ‚ на база битови маÑки не може да Ñе зареди (възможно е да е повреден)" @@ -19556,6 +19696,10 @@ msgid "%s isn't available" msgstr "опциÑта „%s“ не е налична" #, c-format +msgid "value for %s exceeds %<PRIdMAX>" +msgstr "ÑтойноÑтта %s е над %<PRIdMAX>" + +#, c-format msgid "value %s for %s not in range [%<PRIdMAX>,%<PRIdMAX>]" msgstr "ÑтойноÑтта %s за „%s“ е извън диапазона [%<PRIdMAX>,%<PRIdMAX>]" @@ -19711,7 +19855,7 @@ msgid "how to strip spaces and #comments from message" msgstr "кои празни знаци и #коментари да Ñе махат от ÑъобщениÑта" msgid "read pathspec from file" -msgstr "изчитане на пътищата от ФÐЙЛ" +msgstr "изчитане на шаблоните за пътища от ФÐЙЛ" msgid "" "with --pathspec-from-file, pathspec elements are separated with NUL character" @@ -19757,14 +19901,15 @@ msgid "invalid attribute name %s" msgstr "неправилно име на атрибут: „%s“" msgid "global 'glob' and 'noglob' pathspec settings are incompatible" -msgstr "глобалните наÑтройки за пътища „glob“ и „noglob“ Ñа неÑъвмеÑтими" +msgstr "" +"глобалните наÑтройки за шаблони за пътища „glob“ и „noglob“ Ñа неÑъвмеÑтими" msgid "" "global 'literal' pathspec setting is incompatible with all other global " "pathspec settings" msgstr "" "глобалната наÑтройка за доÑловни пътища „literal“ е неÑъвмеÑтима Ñ Ð²Ñички " -"други глобални наÑтройки за пътища" +"други глобални наÑтройки за шаблони за пътища" msgid "invalid parameter for pathspec magic 'prefix'" msgstr "неправилен параметър за опциÑта за магичеÑки пътища „prefix“" @@ -19803,7 +19948,7 @@ msgstr "%s: магичеÑките пътища не Ñе поддържат Ð¾Ñ #, c-format msgid "pathspec '%s' is beyond a symbolic link" -msgstr "пътÑÑ‚ „%s“ е Ñлед Ñимволна връзка" +msgstr "шаблонът за пътища „%s“ е Ñлед Ñимволна връзка" #, c-format msgid "line is badly quoted: %s" @@ -20548,6 +20693,14 @@ msgid "%s does not point to a valid object!" msgstr "„%s“ не Ñочи към позволен обект!" #, c-format +msgid "%s%s will become dangling after %s is deleted\n" +msgstr "%s„%s“ ще оÑтане извън клоните Ñлед изтриването на „%s“\n" + +#, c-format +msgid "%s%s has become dangling after %s was deleted\n" +msgstr "%s„%s“ оÑтана извън клоните Ñлед изтриването на „%s“\n" + +#, c-format msgid "" "Using '%s' as the name for the initial branch. This default branch name\n" "is subject to change. To configure the initial branch name to use in all\n" @@ -21605,7 +21758,7 @@ msgid "" msgstr "" "След коригирането на конфликтите отбележете решаването им чрез:\n" "\n" -" git add/rm ПЪТ…\n" +" git add/rm ШÐБЛОÐ_ЗÐ_ПЪТИЩÐ…\n" "\n" "и изпълнете:\n" "\n" @@ -21630,7 +21783,7 @@ msgid "" msgstr "" "След коригирането на конфликтите отбележете решаването им чрез:\n" "\n" -" git add/rm ПЪТ…\n" +" git add/rm ШÐБЛОÐ_ЗÐ_ПЪТИЩÐ…\n" "\n" "и изпълнете:\n" "\n" @@ -22950,6 +23103,9 @@ msgid "toggle pruning of uninteresting paths" msgstr "" "превключване на окаÑтрÑнето на пътищата, които не предÑтавлÑват интереÑ" +msgid "toggle aggressive edge walk" +msgstr "превключване на агреÑивно обхождане на ребрата" + msgid "read a pattern list over stdin" msgstr "изчитане на ÑпиÑък Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð¸ от ÑÑ‚Ð°Ð½Ð´Ð°Ñ€Ñ‚Ð½Ð¸Ñ Ð²Ñ…Ð¾Ð´" @@ -23584,12 +23740,29 @@ msgid "fatal: " msgstr "фатална грешка: " msgid "error: " -msgstr "грешка: " +msgstr "ГРЕШКÐ: " msgid "warning: " msgstr "предупреждение: " #, c-format +msgid "" +"'%s' is nominated for removal.\n" +"If you still use this command, please add an extra\n" +"option, '--i-still-use-this', on the command line\n" +"and let us know you still use it by sending an e-mail\n" +"to <git@vger.kernel.org>. Thanks.\n" +msgstr "" +"ПредÑтои пълното премахване на „%s“.\n" +"Ðко вÑе още ползвате тази команда, добавете\n" +"опциÑта „--i-still-use-this“ на ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð¸Ñ Ñ€ÐµÐ´ и молим да\n" +"ни извеÑтите Ñ Ðµ-пиÑмо до пощенÑÐºÐ¸Ñ ÑпиÑък:\n" +"<git@vger.kernel.org>. Предварително благодарим.\n" + +msgid "refusing to run without --i-still-use-this" +msgstr "трÑбва да добавите и опциÑта „--i-still-use-this“" + +#, c-format msgid "uname() failed with error '%s' (%d)\n" msgstr "грешка при изпълнението на „uname()“ — „%s“ (%d)\n" @@ -24180,7 +24353,7 @@ msgstr "непознат Ñтил „%s“ за „%s“" msgid "" "Error: Your local changes to the following files would be overwritten by " "merge" -msgstr "Грешка: Сливането ще презапише локалните промѐни на тези файлове:" +msgstr "ГРЕШКÐ: Сливането ще презапише локалните промѐни на тези файлове:" msgid "Automated merge did not work." msgstr "Ðвтоматичното Ñливане не Ñработи." @@ -24541,6 +24714,10 @@ msgid "(body) Adding cc: %s from line '%s'\n" msgstr "(Ñ‚Ñло) ДобавÑне на „Ñк: %s“ от ред „%s“\n" #, perl-format +msgid "error: invalid SMTP port '%s'\n" +msgstr "ГРЕШКÐ: неправилен порт за SMTP: „%s“\n" + +#, perl-format msgid "(%s) Could not execute '%s'" msgstr "(%s) Ðе може да Ñе изпълни „%s“" @@ -2,6 +2,7 @@ # This file is distributed under the same license as the Git package. # Alex Henrie <alexhenrie24@gmail.com>, 2014-2016. # Jordi Mas i Hernà ndez <jmas@softcatala.org>, 2016-2024 +# Mikel Forcada <mikel.forcada@gmail.com> 2024- # # Terminologia # @@ -18,17 +19,19 @@ # cover letter | carta de presentació # cruft | superflu # delta | diferència -# deprecated | en desús +# deprecated | en desús / obsolet # detached | separat # dry-run | fer una prova # fatal | fatal # fetch | obtenir # flush | buidar / buidatge # graph | graf +# graft | empelt / empeltar # hash | resum # hint | consell # hook | lligam # hunk | tros +# lock | blocatge, blocar # multi-pack-index | Ãndex multipaquet # not supported | no està admès # pull | baixar @@ -46,7 +49,7 @@ # upstream | font # # Alguns termes que són ordres especÃfiques del git i d'à mbit molt tècnic -# hem decidit no traduir-los per facilitar-ne la compressió a l'usuari i perquè +# hem decidit no traduir-los per a facilitar-ne la compressió a l'usuari i perquè # no tenen una transcendència al gran públic. Es tracta de casos similars # a «ping» en l'à mbit de xarxes. # @@ -77,8 +80,8 @@ msgid "" msgstr "" "Project-Id-Version: Git\n" "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n" -"POT-Creation-Date: 2024-10-05 01:20+0000\n" -"PO-Revision-Date: 2024-10-05 09:03+0200\n" +"POT-Creation-Date: 2025-08-12 20:20-0400\n" +"PO-Revision-Date: 2025-08-15 22:35+0200\n" "Last-Translator: Mikel Forcada <mikel.forcada@gmail.com>\n" "Language-Team: Catalan\n" "Language: ca\n" @@ -90,6 +93,11 @@ msgstr "" #: add-interactive.c #, c-format +msgid "%s cannot be negative" +msgstr "%s no pot ser negatiu" + +#: add-interactive.c +#, c-format msgid "Huh (%s)?" msgstr "Perdó (%s)?" @@ -504,8 +512,8 @@ msgstr "" #, c-format msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? " msgstr "" -"Descarta el canvi de mode de l'Ãndex i de l'arbre de treball [y,n,q,a," -"d%s,?]? " +"Descarta el canvi de mode de l'Ãndex i de l'arbre de treball [y,n,q,a,d" +"%s,?]? " #: add-patch.c #, c-format @@ -832,10 +840,10 @@ msgstr "Només han canviat els fitxers binaris." #, c-format msgid "" "\n" -"Disable this message with \"git config advice.%s false\"" +"Disable this message with \"git config set advice.%s false\"" msgstr "" "\n" -"Desactiva aquest missatge amb «git config advice.%s false»" +"Desactiva aquest missatge amb «git config set advice.%s false»" #: advice.c #, c-format @@ -930,7 +938,7 @@ msgid "" "* Use the --sparse option.\n" "* Disable or modify the sparsity rules." msgstr "" -"Si voleu actualitzar aquestes entrades, proveu les següents solucions:\n" +"Si voleu actualitzar aquestes entrades, proveu les solucions següents:\n" "* Utilitzeu l'opció --sparse.\n" "* Inhabiliteu o modifiqueu les regles de dispersió." @@ -1024,11 +1032,10 @@ msgstr "opció ignora l'espai en blanc «%s» no reconeguda" #: builtin/describe.c builtin/diff-tree.c builtin/difftool.c #: builtin/fast-export.c builtin/fetch.c builtin/help.c builtin/index-pack.c #: builtin/init-db.c builtin/log.c builtin/ls-files.c builtin/merge-base.c -#: builtin/merge-tree.c builtin/merge.c builtin/pack-objects.c builtin/rebase.c -#: builtin/repack.c builtin/replay.c builtin/reset.c builtin/rev-list.c -#: builtin/rev-parse.c builtin/show-branch.c builtin/stash.c -#: builtin/submodule--helper.c builtin/tag.c builtin/worktree.c parse-options.c -#: range-diff.c revision.c +#: builtin/merge-tree.c builtin/merge.c builtin/rebase.c builtin/repack.c +#: builtin/replay.c builtin/reset.c builtin/rev-parse.c builtin/show-branch.c +#: builtin/stash.c builtin/submodule--helper.c builtin/tag.c builtin/worktree.c +#: parse-options.c range-diff.c revision.c #, c-format msgid "options '%s' and '%s' cannot be used together" msgstr "les opcions «%s» i «%s» no es poden usar juntes" @@ -1380,7 +1387,7 @@ msgstr "S'està comprovant el pedaç %s..." #: apply.c #, c-format msgid "sha1 information is lacking or useless for submodule %s" -msgstr "falta la informació sha1 o és inútil per al submòdul %s" +msgstr "hi manca la informació sha1 o és inútil per al submòdul %s" #: apply.c #, c-format @@ -1390,7 +1397,7 @@ msgstr "canvi de mode per a %s, el qual no està en la HEAD actual" #: apply.c #, c-format msgid "sha1 information is lacking or useless (%s)." -msgstr "falta informació sha1 o és inútil (%s)." +msgstr "hi manca informació sha1 o és inútil (%s)." #: apply.c #, c-format @@ -1779,7 +1786,7 @@ msgstr "cometes no tancades: «%s»" #: archive.c #, c-format msgid "missing colon: '%s'" -msgstr "falten els dos punts: «%s»" +msgstr "hi manquen els dos punts: «%s»" #: archive.c #, c-format @@ -1927,12 +1934,12 @@ msgstr "no es pot fer fstat gitattributes al fitxer «%s»" #: attr.c #, c-format msgid "ignoring overly large gitattributes file '%s'" -msgstr "s'ignorarà el fitxer «%s» gitattributes per a ser massa gran" +msgstr "s'ignorarà el fitxer «%s» gitattributes per ser massa gran" #: attr.c #, c-format msgid "ignoring overly large gitattributes blob '%s'" -msgstr "s'ignorarà el blob «%s» gitattributes per a ser massa gran" +msgstr "s'ignorarà el blob «%s» gitattributes per ser massa gran" #: attr.c msgid "cannot use --attr-source or GIT_ATTR_SOURCE without repo" @@ -1942,7 +1949,7 @@ msgstr "no es pot usar --attr-source o GIT_ATTR_SOURCE sense repository" msgid "bad --attr-source or GIT_ATTR_SOURCE" msgstr "--attr-source incorrecte o GIT_ATTR_SOURCE" -#: attr.c read-cache.c +#: attr.c read-cache.c refs/packed-backend.c #, c-format msgid "unable to stat '%s'" msgstr "no s'ha pogut fer «stat» a «%s»" @@ -2462,6 +2469,12 @@ msgstr "\"Useu -f si realment voleu afegir-los." msgid "adding files failed" msgstr "l'afegiment de fitxers ha fallat" +#: builtin/add.c builtin/checkout.c builtin/commit.c builtin/reset.c +#: builtin/stash.c +#, c-format +msgid "'%s' cannot be negative" +msgstr "'%s' no pot ser negatiu" + #: builtin/add.c #, c-format msgid "--chmod param '%s' must be either -x or +x" @@ -2500,7 +2513,7 @@ msgid "bad action '%s' for '%s'" msgstr "acció «%s» incorrecta per a «%s»" #: builtin/am.c builtin/blame.c builtin/fetch.c builtin/pack-objects.c -#: builtin/pull.c builtin/revert.c config.c diff-merges.c gpg-interface.c +#: builtin/pull.c builtin/revert.c diff-merges.c environment.c gpg-interface.c #: ls-refs.c parallel-checkout.c sequencer.c setup.c #, c-format msgid "invalid value for '%s': '%s'" @@ -2794,7 +2807,7 @@ msgstr "executa interactivament" msgid "bypass pre-applypatch and applypatch-msg hooks" msgstr "evita els lligams pre-applypatch i applypatch-msg" -#: builtin/am.c +#: builtin/am.c builtin/cat-file.c msgid "historical option -- no-op" msgstr "opció històrica -- no-op" @@ -2982,6 +2995,22 @@ msgstr "git archive: error de protocol" msgid "git archive: expected a flush" msgstr "git archive: s'esperava una neteja" +#: builtin/backfill.c +msgid "git backfill [--min-batch-size=<n>] [--[no-]sparse]" +msgstr "git backfill [--min-batch-size=<n>] [--[no-]sparse]" + +#: builtin/backfill.c +msgid "problem loading sparse-checkout" +msgstr "s'ha produït un problema en carregar sparse-checkout" + +#: builtin/backfill.c +msgid "Minimum number of objects to request at a time" +msgstr "Nombre mÃnim d'objectes a sol·licitar d'una vegada." + +#: builtin/backfill.c +msgid "Restrict the missing objects to the current sparse-checkout" +msgstr "Restringeix els objectes que manquen al sparse-checkout actual" + #: builtin/bisect.c msgid "" "git bisect start [--term-(new|bad)=<term> --term-(old|good)=<term>] [--no-" @@ -3420,7 +3449,7 @@ msgstr "" msgid "ignore whitespace differences" msgstr "ignora les diferències d'espai en blanc" -#: builtin/blame.c builtin/log.c +#: builtin/blame.c builtin/clone.c builtin/log.c msgid "rev" msgstr "rev" @@ -3773,7 +3802,7 @@ msgstr "mou/canvia de nom una branca i el seu registre de referències" #: builtin/branch.c msgid "move/rename a branch, even if target exists" -msgstr "mou/canvia de nom una branca, encara que el destà existeixi" +msgstr "mou/canvia de nom una branca, encara que la destinació existeixi" #: builtin/branch.c builtin/for-each-ref.c builtin/tag.c msgid "do not output a newline after empty formatted refs" @@ -3785,7 +3814,7 @@ msgstr "copia una branca i el seu registre de referències" #: builtin/branch.c msgid "copy a branch, even if target exists" -msgstr "copia una branca, encara que el destà existeixi" +msgstr "copia una branca, encara que la destinació existeixi" #: builtin/branch.c msgid "list branch names" @@ -3944,11 +3973,6 @@ msgid "git version:\n" msgstr "versió de git:\n" #: builtin/bugreport.c -#, c-format -msgid "uname() failed with error '%s' (%d)\n" -msgstr "uname() ha fallat amb l'error «%s» (%d)\n" - -#: builtin/bugreport.c msgid "compiler info: " msgstr "informació del compilador: " @@ -4005,8 +4029,8 @@ msgstr "" "Reviseu la resta de l'informe d'error de sota.\n" "Podeu eliminar qualsevol lÃnia que vulgueu.\n" -#: builtin/bugreport.c builtin/commit.c builtin/fast-export.c builtin/rebase.c -#: parse-options.h +#: builtin/bugreport.c builtin/commit.c builtin/fast-export.c +#: builtin/pack-objects.c builtin/rebase.c parse-options.h msgid "mode" msgstr "mode" @@ -4126,7 +4150,7 @@ msgstr "Cal un repositori per a desfer un farcell." msgid "Unbundling objects" msgstr "S'estan desagrupant objectes" -#: builtin/cat-file.c merge-recursive.c +#: builtin/cat-file.c #, c-format msgid "cannot read object %s '%s'" msgstr "no es pot llegir l'objecte %s «%s»" @@ -4163,12 +4187,8 @@ msgid "git cat-file <type> <object>" msgstr "git cat-file <tipus> <objecte>" #: builtin/cat-file.c -msgid "git cat-file (-e | -p) <object>" -msgstr "git cat-file (-e | -p) <objecte>" - -#: builtin/cat-file.c -msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>" -msgstr "git cat-file (-t | -s) [--allow-unknown-type] <objecte>" +msgid "git cat-file (-e | -p | -t | -s) <object>" +msgstr "git cat-file (-e | -p | -t | -s) <objecte>" #: builtin/cat-file.c msgid "" @@ -4216,10 +4236,6 @@ msgstr "" msgid "show object size" msgstr "mostra la mida de l'objecte" -#: builtin/cat-file.c -msgid "allow -s and -t to work with broken/corrupt objects" -msgstr "permet que -s i -t funcionin amb objectes trencats/malmesos" - #: builtin/cat-file.c builtin/log.c msgid "use mail map file" msgstr "usa el fitxer de mapa de correu" @@ -4293,6 +4309,15 @@ msgid "use a <path> for (--textconv | --filters); Not with 'batch'" msgstr "useu un <camÃ> per a (--textconv | --filters); no amb «batch»" #: builtin/cat-file.c +msgid "objects filter only supported in batch mode" +msgstr "el filtre d'objectes només s'admet en mode batch" + +#: builtin/cat-file.c +#, c-format +msgid "objects filter not supported: '%s'" +msgstr "filtre d'objectes no admès: %s" + +#: builtin/cat-file.c #, c-format msgid "'%s=<%s>' needs '%s' or '%s'" msgstr "«%s=<%s>» necessita «%s» o «%s»" @@ -4881,7 +4906,7 @@ msgstr "estil de conflicte desconegut «%s»" msgid "perform a 3-way merge with the new branch" msgstr "realitza una fusió de 3 vies amb la branca nova" -#: builtin/checkout.c builtin/log.c parse-options.h +#: builtin/checkout.c builtin/log.c builtin/range-diff.c parse-options.h msgid "style" msgstr "estil" @@ -4910,8 +4935,8 @@ msgid "update ignored files (default)" msgstr "actualitza els fitxers ignorats (per defecte)" #: builtin/checkout.c -msgid "do not check if another worktree is holding the given ref" -msgstr "no comprovis si un altre arbre de treball té la referència donada" +msgid "do not check if another worktree is using this branch" +msgstr "no comprovis si un altre arbre de treball usa aquesta branca" #: builtin/checkout.c msgid "checkout our version for unmerged files" @@ -4937,7 +4962,7 @@ msgstr "--track necessita un nom de branca" #: builtin/checkout.c #, c-format msgid "missing branch name; try -%c" -msgstr "falta el nom de la branca; proveu -%c" +msgstr "hi manca el nom de la branca; proveu -%c" #: builtin/checkout.c #, c-format @@ -5207,8 +5232,114 @@ msgstr "" "netejar" #: builtin/clone.c -msgid "git clone [<options>] [--] <repo> [<dir>]" -msgstr "git clone [<opcions>] [--] <repositori> [<directori>]" +#, c-format +msgid "info: Could not add alternate for '%s': %s\n" +msgstr "info: No s'ha pogut afegir un alternatiu per a «%s»: %s\n" + +#: builtin/clone.c builtin/diff.c builtin/rm.c grep.c setup.c +#, c-format +msgid "failed to stat '%s'" +msgstr "s'ha produït un error en fer stat a «%s»" + +#: builtin/clone.c +#, c-format +msgid "%s exists and is not a directory" +msgstr "%s existeix i no és directori" + +#: builtin/clone.c +#, c-format +msgid "'%s' is a symlink, refusing to clone with --local" +msgstr "«%s» és un enllaç simbòlic, es rebutja clonar amb --local" + +#: builtin/clone.c +#, c-format +msgid "failed to start iterator over '%s'" +msgstr "no s'ha pogut iniciar l'iterador sobre «%s»" + +#: builtin/clone.c +#, c-format +msgid "symlink '%s' exists, refusing to clone with --local" +msgstr "l'enllaç simbòlic «%s» existeix, es rebutja a clonar amb --local" + +#: builtin/clone.c compat/precompose_utf8.c +#, c-format +msgid "failed to unlink '%s'" +msgstr "s'ha produït un error en desenllaçar «%s»" + +#: builtin/clone.c +#, c-format +msgid "hardlink cannot be checked at '%s'" +msgstr "no es pot comprovar l'enllaç fÃsic en «%s»" + +#: builtin/clone.c +#, c-format +msgid "hardlink different from source at '%s'" +msgstr "l'enllaç fÃsic és diferent de la font en «%s»" + +#: builtin/clone.c +#, c-format +msgid "failed to create link '%s'" +msgstr "s'ha produït un error en crear l'enllaç «%s»" + +#: builtin/clone.c +#, c-format +msgid "failed to copy file to '%s'" +msgstr "s'ha produït un error en copiar el fitxer a «%s»" + +#: builtin/clone.c refs/files-backend.c +#, c-format +msgid "failed to iterate over '%s'" +msgstr "no s'ha pogut iterar sobre «%s»" + +#: builtin/clone.c +#, c-format +msgid "done.\n" +msgstr "fet.\n" + +#: builtin/clone.c +msgid "" +"Clone succeeded, but checkout failed.\n" +"You can inspect what was checked out with 'git status'\n" +"and retry with 'git restore --source=HEAD :/'\n" +msgstr "" +"El clonatge ha tingut èxit, però no s'ha pogut agafar.\n" +"Podeu inspeccionar el que s'ha agafat amb «git status»\n" +"i tornar-ho a provar amb «git restore --source=HEAD :/»\n" + +#: builtin/clone.c fetch-pack.c +msgid "remote did not send all necessary objects" +msgstr "el remot no ha enviat tots els objectes necessaris" + +#: builtin/clone.c +#, c-format +msgid "unable to update %s" +msgstr "no s'ha pogut actualitzar %s" + +#: builtin/clone.c +msgid "failed to initialize sparse-checkout" +msgstr "no s'ha pogut inicialitzar «sparse-checkout»" + +#: builtin/clone.c +msgid "remote HEAD refers to nonexistent ref, unable to checkout" +msgstr "" +"el HEAD remot es refereix a una referència que no existeix, no s'ha pogut " +"agafar" + +#: builtin/clone.c +msgid "unable to checkout working tree" +msgstr "no s'ha pogut agafar l'arbre de treball" + +#: builtin/clone.c +msgid "unable to write parameters to config file" +msgstr "no s'han pogut escriure els parà metres al fitxer de configuració" + +#: builtin/clone.c +msgid "cannot repack to clean up" +msgstr "no es pot reempaquetar per a netejar" + +#: builtin/clone.c +msgid "cannot unlink temporary alternates file" +msgstr "no es pot desenllaçar el fitxer d'alternatives temporal" #: builtin/clone.c msgid "don't clone shallow repository" @@ -5281,6 +5412,10 @@ msgid "checkout <branch> instead of the remote's HEAD" msgstr "agafa <branca> en lloc de la HEAD del remot" #: builtin/clone.c +msgid "clone single revision <rev> and check out" +msgstr "clona només la revisió <rev> i agafa" + +#: builtin/clone.c msgid "path to git-upload-pack on the remote" msgstr "camà a git-upload-pack en el remot" @@ -5296,23 +5431,21 @@ msgstr "crea un clon superficial d'aquesta profunditat" msgid "create a shallow clone since a specific time" msgstr "crea un clon superficial des d'una data especÃfica" -#: builtin/clone.c builtin/fetch.c builtin/pull.c builtin/rebase.c -#: builtin/replay.c -msgid "revision" -msgstr "revisió" +#: builtin/clone.c builtin/fetch.c builtin/pull.c +msgid "ref" +msgstr "ref" #: builtin/clone.c builtin/fetch.c builtin/pull.c -msgid "deepen history of shallow clone, excluding rev" -msgstr "aprofundeix la història d'un clon superficial, excloent una revisió" +msgid "deepen history of shallow clone, excluding ref" +msgstr "aprofundeix la història d'un clon superficial, excloent referències" #: builtin/clone.c builtin/submodule--helper.c msgid "clone only one branch, HEAD or --branch" msgstr "clona només una branca, HEAD o --branch" #: builtin/clone.c -msgid "don't clone any tags, and make later fetches not to follow them" -msgstr "" -"no cloneu cap etiqueta, i feu que els «fetch» següents no les segueixin" +msgid "clone tags, and make later fetches not to follow them" +msgstr "cloneu les etiquetes, i feu que els «fetch» següents no les segueixin" #: builtin/clone.c msgid "any cloned submodules will be shallow" @@ -5371,119 +5504,8 @@ msgid "a URI for downloading bundles before fetching from origin remote" msgstr "un URI per a baixar paquets abans d'obtenir des del remot origen" #: builtin/clone.c -#, c-format -msgid "info: Could not add alternate for '%s': %s\n" -msgstr "info: No s'ha pogut afegir un alternatiu per a «%s»: %s\n" - -#: builtin/clone.c builtin/diff.c builtin/rm.c grep.c setup.c -#, c-format -msgid "failed to stat '%s'" -msgstr "s'ha produït un error en fer stat a «%s»" - -#: builtin/clone.c -#, c-format -msgid "%s exists and is not a directory" -msgstr "%s existeix i no és directori" - -#: builtin/clone.c -#, c-format -msgid "'%s' is a symlink, refusing to clone with --local" -msgstr "«%s» és un enllaç simbòlic, es rebutja clonar amb --local" - -#: builtin/clone.c -#, c-format -msgid "failed to start iterator over '%s'" -msgstr "no s'ha pogut iniciar l'iterador sobre «%s»" - -#: builtin/clone.c -#, c-format -msgid "symlink '%s' exists, refusing to clone with --local" -msgstr "l'enllaç simbòlic «%s» existeix, es rebutja a clonar amb --local" - -#: builtin/clone.c compat/precompose_utf8.c -#, c-format -msgid "failed to unlink '%s'" -msgstr "s'ha produït un error en desenllaçar «%s»" - -#: builtin/clone.c -#, c-format -msgid "hardlink cannot be checked at '%s'" -msgstr "no es pot comprovar l'enllaç fÃsic en «%s»" - -#: builtin/clone.c -#, c-format -msgid "hardlink different from source at '%s'" -msgstr "l'enllaç fÃsic és diferent de la font en «%s»" - -#: builtin/clone.c -#, c-format -msgid "failed to create link '%s'" -msgstr "s'ha produït un error en crear l'enllaç «%s»" - -#: builtin/clone.c -#, c-format -msgid "failed to copy file to '%s'" -msgstr "s'ha produït un error en copiar el fitxer a «%s»" - -#: builtin/clone.c refs/files-backend.c -#, c-format -msgid "failed to iterate over '%s'" -msgstr "no s'ha pogut iterar sobre «%s»" - -#: builtin/clone.c -#, c-format -msgid "done.\n" -msgstr "fet.\n" - -#: builtin/clone.c -msgid "" -"Clone succeeded, but checkout failed.\n" -"You can inspect what was checked out with 'git status'\n" -"and retry with 'git restore --source=HEAD :/'\n" -msgstr "" -"El clonatge ha tingut èxit, però no s'ha pogut agafar.\n" -"Podeu inspeccionar el que s'ha agafat amb «git status»\n" -"i tornar-ho a provar amb «git restore --source=HEAD :/»\n" - -#: builtin/clone.c -#, c-format -msgid "Could not find remote branch %s to clone." -msgstr "No s'ha pogut trobar la branca remota %s per a clonar." - -#: builtin/clone.c fetch-pack.c -msgid "remote did not send all necessary objects" -msgstr "el remot no ha enviat tots els objectes necessaris" - -#: builtin/clone.c -#, c-format -msgid "unable to update %s" -msgstr "no s'ha pogut actualitzar %s" - -#: builtin/clone.c -msgid "failed to initialize sparse-checkout" -msgstr "no s'ha pogut inicialitzar «sparse-checkout»" - -#: builtin/clone.c -msgid "remote HEAD refers to nonexistent ref, unable to checkout" -msgstr "" -"el HEAD remot es refereix a una referència que no existeix, no s'ha pogut " -"agafar" - -#: builtin/clone.c -msgid "unable to checkout working tree" -msgstr "no s'ha pogut agafar l'arbre de treball" - -#: builtin/clone.c -msgid "unable to write parameters to config file" -msgstr "no s'han pogut escriure els parà metres al fitxer de configuració" - -#: builtin/clone.c -msgid "cannot repack to clean up" -msgstr "no es pot reempaquetar per a netejar" - -#: builtin/clone.c -msgid "cannot unlink temporary alternates file" -msgstr "no es pot desenllaçar el fitxer d'alternatives temporal" +msgid "git clone [<options>] [--] <repo> [<dir>]" +msgstr "git clone [<opcions>] [--] <repositori> [<directori>]" #: builtin/clone.c msgid "Too many arguments." @@ -5512,12 +5534,12 @@ msgstr "la profunditat %s no és un nombre positiu" #: builtin/clone.c #, c-format msgid "destination path '%s' already exists and is not an empty directory." -msgstr "el camà destà «%s» ja existeix i no és un directori buit." +msgstr "el camà de destinació «%s» ja existeix i no és un directori buit." #: builtin/clone.c #, c-format msgid "repository path '%s' already exists and is not an empty directory." -msgstr "el camà destà «%s» ja existeix i no és un directori buit." +msgstr "el camà de destinació «%s» ja existeix i no és un directori buit." #: builtin/clone.c #, c-format @@ -5614,6 +5636,11 @@ msgid "Remote branch %s not found in upstream %s" msgstr "La branca remota %s no es troba en la font %s" #: builtin/clone.c +#, c-format +msgid "Remote revision %s not found in upstream %s" +msgstr "La branca remota %s no es troba en la font %s " + +#: builtin/clone.c msgid "You appear to have cloned an empty repository." msgstr "Sembla que heu clonat un repositori buit." @@ -5676,7 +5703,8 @@ msgstr "" "[no-]progress]\n" " <split-options>" -#: builtin/commit-graph.c builtin/fetch.c builtin/log.c builtin/repack.c +#: builtin/commit-graph.c builtin/fetch.c builtin/gc.c builtin/log.c +#: builtin/repack.c msgid "dir" msgstr "directori" @@ -5836,7 +5864,7 @@ msgstr "git commit-tree: ha fallat en llegir" #: builtin/commit.c msgid "" -"git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]\n" +"git commit [-a | --interactive | --patch] [-s] [-v] [-u[<mode>]] [--amend]\n" " [--dry-run] [(-c | -C | --squash) <commit> | --fixup [(amend|" "reword):]<commit>]\n" " [-F <file> | -m <msg>] [--reset-author] [--allow-empty]\n" @@ -6466,28 +6494,30 @@ msgstr "git config list [<opció-fitxer>] [<opció-presentació>] [--includes]" #: builtin/config.c msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp] [--value=<value>] [--fixed-value] [--default=<default>] <name>" +"regexp] [--value=<pattern>] [--fixed-value] [--default=<default>] [--" +"url=<url>] <name>" msgstr "" "git config get [<opció-fitxer>] [<opció-presentació>] [--includes] [--all] " -"[--regexp] [--value=<valor>] [--fixed-value] [--default=<default>] <nom>" +"[--regexp] [--value=<patró>] [--fixed-value] [--default=<default>] [--" +"url=<url>] <nom>" # Cal traduir els parà metres amb <...>? #: builtin/config.c msgid "" -"git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--" -"fixed-value] <name> <value>" +"git config set [<file-option>] [--type=<type>] [--all] [--value=<pattern>] " +"[--fixed-value] <name> <value>" msgstr "" -"git config set [<opció-fitxer>] [--type=<tipus>] [--all] [--value=<valor>] " +"git config set [<opció-fitxer>] [--type=<tipus>] [--all] [--value=<patró>] " "[--fixed-value] <nom> <valor>" # Cal traduir els parà metres amb <...>? #: builtin/config.c msgid "" -"git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] " -"<name> <value>" +"git config unset [<file-option>] [--all] [--value=<pattern>] [--fixed-value] " +"<name>" msgstr "" -"git config unset [<opció-fitxer>] [--all] [--value=<valor>] [--fixed-value] " -"<name> <valor>" +"git config unset [<opció-fitxer>] [--all] [--value=<patró>] [--fixed-value] " +"<nom>" #: builtin/config.c msgid "git config rename-section [<file-option>] <old-name> <new-name>" @@ -6512,21 +6542,21 @@ msgstr "git config [<opció-fitxer>] --get-colorbool <nom> [<stdout-is-tty>]" #: builtin/config.c msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] " +"regexp=<regexp>] [--value=<pattern>] [--fixed-value] [--default=<default>] " "<name>" msgstr "" "git config get [<opció-fitxer>] [<opció-presentació>] [--includes] [--all] " -"[--regexp=<expr-reg>] [--value=<valor>] [--fixed-value] [--" +"[--regexp=<expr-reg>] [--value=<patró>] [--fixed-value] [--" "default=<default>] <nom>" # Cal traduir els parà metres amb <...>? #: builtin/config.c msgid "" "git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] " -"[--value=<value>] [--fixed-value] <name> <value>" +"[--value=<pattern>] [--fixed-value] <name> <value>" msgstr "" "git config set [<opció-fitxer>] [--type=<tipus>] [--comment=<missatge>] [--" -"all] [--value=<valor>] [--fixed-value] <nom> <valor>" +"all] [--value=<patró>] [--fixed-value] <nom> <valor>" #: builtin/config.c msgid "Config file location" @@ -6778,7 +6808,7 @@ msgstr "valor" #: builtin/config.c msgid "use default value when missing entry" -msgstr "utilitza el valor per defecte quan falti una entrada" +msgstr "utilitza el valor per defecte quan hi manqui una entrada" #: builtin/config.c msgid "--fixed-value only applies with 'value-pattern'" @@ -6916,7 +6946,7 @@ msgstr "troba la configuració de color: slot [<stdout-is-tty>]" #: builtin/config.c msgid "with --get, use default value when missing entry" -msgstr "amb --get utilitza el valor per defecte quan falti una entrada" +msgstr "amb --get utilitza el valor per defecte quan hi manqui una entrada" #: builtin/config.c msgid "--get-color and variable type are incoherent" @@ -6983,7 +7013,7 @@ msgstr "" #, c-format msgid "unable to get credential storage lock in %d ms" msgstr "" -"no s'ha pogut obtenir el bloqueig de l'emmagatzematge de credencials en %d ms" +"no s'ha pogut obtenir el blocatge de l'emmagatzematge de credencials en %d ms" #: builtin/describe.c msgid "" @@ -7065,12 +7095,8 @@ msgstr "%lu comissions recorregudes\n" #: builtin/describe.c #, c-format -msgid "" -"more than %i tags found; listed %i most recent\n" -"gave up search at %s\n" -msgstr "" -"s'han trobat més de %i etiquetes: s'han llistat les %i més recents\n" -"s'ha renunciat la cerca a %s\n" +msgid "found %i tags; gave up search at %s\n" +msgstr "trobades %i etiquetes; s'ha abandonat la recerca en %s\n" #: builtin/describe.c #, c-format @@ -7172,6 +7198,64 @@ msgstr "especifiqueu un sufix en format strftime per al nom de fitxer" msgid "specify the content of the diagnostic archive" msgstr "especifica el contingut de l'arxiu de diagnòstic" +#: builtin/diff-pairs.c +#, c-format +msgid "unable to parse mode: %s" +msgstr "no s'ha pogut analitzar el mode: %s" + +#: builtin/diff-pairs.c +#, c-format +msgid "unable to parse object id: %s" +msgstr "no s'ha pogut analitzar l'identificador d'objecte: %s" + +#: builtin/diff-pairs.c +msgid "git diff-pairs -z [<diff-options>]" +msgstr "git diff-pairs -z [<opcions-diferencia>]" + +#: builtin/diff-pairs.c builtin/log.c builtin/replay.c builtin/shortlog.c +#: bundle.c +#, c-format +msgid "unrecognized argument: %s" +msgstr "argument no reconegut: %s" + +#: builtin/diff-pairs.c +msgid "working without -z is not supported" +msgstr "no s'admet treballar sense -z" + +#: builtin/diff-pairs.c +msgid "pathspec arguments not supported" +msgstr "arguments de l'especificació de camà no admesos" + +#: builtin/diff-pairs.c +msgid "revision arguments not allowed" +msgstr "arguments de revisió no permesos" + +#: builtin/diff-pairs.c +msgid "invalid raw diff input" +msgstr "entrada de diff crua invà lida" + +#: builtin/diff-pairs.c +msgid "tree objects not supported" +msgstr "no s'admeten els objectes d'arbre" + +#: builtin/diff-pairs.c +msgid "got EOF while reading path" +msgstr "rebut un EOF durant la lectura del camÃ" + +#: builtin/diff-pairs.c +msgid "got EOF while reading destination path" +msgstr "rebut un EOF durant la lectura del camà de destinació" + +#: builtin/diff-pairs.c +#, c-format +msgid "unable to parse rename/copy score: %s" +msgstr "no s'ha pogut analitzar puntuació de canvi de nom/còpia: %s" + +#: builtin/diff-pairs.c +#, c-format +msgid "unknown diff status: %c" +msgstr "estat de diff desconegut: %c" + #: builtin/diff-tree.c msgid "--merge-base only works with two commits" msgstr "--merge-base només funciona amb dues comissions" @@ -7354,6 +7438,10 @@ msgid "select handling of signed tags" msgstr "selecciona la gestió de les etiquetes signades" #: builtin/fast-export.c +msgid "select handling of signed commits" +msgstr "selecciona la gestió de les etiquetes signades" + +#: builtin/fast-export.c msgid "select handling of tags that tag filtered objects" msgstr "selecciona la gestió de les etiquetes que etiquetin objectes filtrats" @@ -7460,7 +7548,7 @@ msgstr "" #: builtin/fetch-pack.c #, c-format msgid "Lockfile created but not reported: %s" -msgstr "S'ha creat el fitxer de bloqueig però no s'ha informat: %s" +msgstr "S'ha creat el fitxer de blocatge però no s'ha informat: %s" #: builtin/fetch.c msgid "git fetch [<options>] [<repository> [<refspec>...]]" @@ -7581,26 +7669,6 @@ msgstr "" "s'ha rebutjat %s perquè no es permeten actualitzar les arrels superficials" #: builtin/fetch.c -#, c-format -msgid "" -"some local refs could not be updated; try running\n" -" 'git remote prune %s' to remove any old, conflicting branches" -msgstr "" -"algunes referències locals no s'han pogut actualitzar;\n" -" intenteu executar «git remote prune %s» per a eliminar\n" -" qualsevol branca antiga o conflictiva" - -#: builtin/fetch.c -#, c-format -msgid " (%s will become dangling)" -msgstr " (%s es tornarà despenjat)" - -#: builtin/fetch.c -#, c-format -msgid " (%s has become dangling)" -msgstr " (%s s'ha quedat despenjat)" - -#: builtin/fetch.c msgid "[deleted]" msgstr "[suprimit]" @@ -7623,7 +7691,7 @@ msgstr "l'opció «%s» amb valor «%s» no és và lida per a %s" msgid "option \"%s\" is ignored for %s" msgstr "l'opció «%s» s'ignora per a «%s»" -#: builtin/fetch.c object-file.c +#: builtin/fetch.c odb.c #, c-format msgid "%s is not a valid object" msgstr "%s no és un objecte và lid" @@ -7634,6 +7702,36 @@ msgid "the object %s does not exist" msgstr "l'objecte %s no existeix" #: builtin/fetch.c +#, c-format +msgid "" +"Run 'git remote set-head %s %s' to follow the change, or set\n" +"'remote.%s.followRemoteHEAD' configuration option to a different value\n" +"if you do not want to see this message. Specifically running\n" +"'git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s'\n" +"will disable the warning until the remote changes HEAD to something else." +msgstr "" +"Executeu 'git remote set-head %s %s' per a seguir el canvi, o fixeu\n" +"l'opció de configuració 'remote.%s.followRemoteHEAD' a un valor diferent\n" +"si no voleu veure aquest missatge. EspecÃficament, si executeu\n" +"'git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s'\n" +"es deshabilitarà l'avÃs fins que el remot canviï HEAD a alguna altra cosa." + +#: builtin/fetch.c +#, c-format +msgid "" +"some local refs could not be updated; try running\n" +" 'git remote prune %s' to remove any old, conflicting branches" +msgstr "" +"algunes referències locals no s'han pogut actualitzar;\n" +" intenteu executar «git remote prune %s» per a eliminar\n" +" qualsevol branca antiga o conflictiva" + +#: builtin/fetch.c +#, c-format +msgid "fetching ref %s failed: %s" +msgstr "l'obtenció de la referència %s ha fallat: %s" + +#: builtin/fetch.c msgid "multiple branches detected, incompatible with --set-upstream" msgstr "s'han detectat múltiples branques, incompatible amb --set-upstream" @@ -7810,6 +7908,10 @@ msgstr "mapa de referències" msgid "specify fetch refmap" msgstr "especÃfica l'obtenció del mapa de referències" +#: builtin/fetch.c builtin/pull.c builtin/rebase.c builtin/replay.c +msgid "revision" +msgstr "revisió" + #: builtin/fetch.c builtin/pull.c msgid "report that we have only objects reachable from this object" msgstr "informa que només hi ha objectes abastables des d'aquest objecte" @@ -7919,7 +8021,7 @@ msgstr "usa <text> com a inici de missatge" #: builtin/fmt-merge-msg.c msgid "use <name> instead of the real target branch" -msgstr "usa <nom> en lloc de la branca de destà real" +msgstr "usa <nom> en lloc de la branca de destinació real" #: builtin/fmt-merge-msg.c msgid "file to read from" @@ -7943,6 +8045,10 @@ msgstr "" "git for-each-ref [--contains [<comissió>]] [--no-contains [<comissió>]]" #: builtin/for-each-ref.c +msgid "git for-each-ref [--start-after <marker>]" +msgstr "git for-each-ref [--start-after <marcador>]" + +#: builtin/for-each-ref.c msgid "quote placeholders suitably for shells" msgstr "" "posa els marcadors de posició entre cometes adequades per a intèrprets " @@ -7964,6 +8070,14 @@ msgstr "posa els marcadors de posició entre cometes adequades per al Tcl" msgid "show only <n> matched refs" msgstr "mostra només <n> referències coincidents" +#: builtin/for-each-ref.c +msgid "marker" +msgstr "marca" + +#: builtin/for-each-ref.c +msgid "start iteration after the provided marker" +msgstr "comença la iteració després del marcador proporcionat" + #: builtin/for-each-ref.c builtin/tag.c msgid "respect format colors" msgstr "respecta els colors del format" @@ -7997,9 +8111,18 @@ msgid "also include HEAD ref and pseudorefs" msgstr "inclou també la referència HEAD i les pseudoreferències" #: builtin/for-each-ref.c +msgid "cannot use --start-after with custom sort options" +msgstr "" +"no es pot utilitzar --start-after amb opcions personalitzades d'ordenació" + +#: builtin/for-each-ref.c msgid "unknown arguments supplied with --stdin" msgstr "s'han proporcionat arguments desconeguts amb --stdin" +#: builtin/for-each-ref.c +msgid "cannot use --start-after with patterns" +msgstr "no es pot utilitzar --start-after amb patrons" + #: builtin/for-each-repo.c msgid "git for-each-repo --config=<config> [--] <arguments>" msgstr "git for-each-repo --config=<config> [--] <arguments>" @@ -8018,7 +8141,7 @@ msgstr "continua fins i tot si l'ordre falla en un repositori" #: builtin/for-each-repo.c msgid "missing --config=<config>" -msgstr "falta --config=<config>" +msgstr "hi manca --config=<config>" #: builtin/for-each-repo.c #, c-format @@ -8163,11 +8286,6 @@ msgstr "%s: objecte corrupte o no trobat: %s" #: builtin/fsck.c #, c-format -msgid "%s: object is of unknown type '%s': %s" -msgstr "%s: l'objecte és de tipus desconegut «%s»: %s" - -#: builtin/fsck.c -#, c-format msgid "%s: object could not be parsed: %s" msgstr "%s: no s'ha pogut analitzar l'objecte: %s" @@ -8239,11 +8357,15 @@ msgid "invalid rev-index for pack '%s'" msgstr "rev-index no và lid per al paquet «%s»" #: builtin/fsck.c +msgid "Checking ref database" +msgstr "S'està comprovant la base de dades de referències" + +#: builtin/fsck.c msgid "" "git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n" " [--[no-]full] [--strict] [--verbose] [--lost-found]\n" " [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n" -" [--[no-]name-objects] [<object>...]" +" [--[no-]name-objects] [--[no-]references] [<object>...]" msgstr "" "git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n" " [--[no-]full] [--strict] [--verbose] [--lost-found]\n" @@ -8298,6 +8420,10 @@ msgstr "mostra el progrés" msgid "show verbose names for reachable objects" msgstr "mostra els noms detallats dels objectes abastables" +#: builtin/fsck.c +msgid "check reference database consistency" +msgstr "comprova la consistència de les base de dades de referències" + #: builtin/fsck.c builtin/index-pack.c msgid "Checking objects" msgstr "S'estan comprovant els objectes" @@ -8305,7 +8431,7 @@ msgstr "S'estan comprovant els objectes" #: builtin/fsck.c #, c-format msgid "%s: object missing" -msgstr "%s: falta l'objecte" +msgstr "%s: hi manca l'objecte" #: builtin/fsck.c #, c-format @@ -8496,6 +8622,15 @@ msgstr "" msgid "repack all other packs except the largest pack" msgstr "reempaqueta tots els altres paquets excepte el paquet més gran" +#: builtin/gc.c builtin/repack.c +msgid "pack prefix to store a pack containing pruned objects" +msgstr "" +"prefix del paquet per a emmagatzemar un paquet que contingui objectes podats" + +#: builtin/gc.c +msgid "skip maintenance tasks typically done in the foreground" +msgstr "omet les tasques de manteniment que es fan tÃpicament en primer pla" + #: builtin/gc.c #, c-format msgid "failed to parse gc.logExpiry value %s" @@ -8590,13 +8725,13 @@ msgstr "" #: builtin/gc.c #, c-format -msgid "lock file '%s' exists, skipping maintenance" -msgstr "el fitxer de bloqueig «%s» existeix, s'omet el manteniment" +msgid "task '%s' failed" +msgstr "la tasca «%s» ha fallat" #: builtin/gc.c #, c-format -msgid "task '%s' failed" -msgstr "la tasca «%s» ha fallat" +msgid "lock file '%s' exists, skipping maintenance" +msgstr "el fitxer de blocatge «%s» existeix, s'omet el manteniment" #: builtin/gc.c #, c-format @@ -8637,10 +8772,6 @@ msgid "run a specific task" msgstr "executa una tasca especÃfica" #: builtin/gc.c -msgid "use at most one of --auto and --schedule=<frequency>" -msgstr "usa com a mà xim un entre --auto i --schedule=<freqüència>" - -#: builtin/gc.c #, c-format msgid "unable to add '%s' value of '%s'" msgstr "no es pot afegir el valor «%s» de «%s»" @@ -8742,8 +8873,28 @@ msgid "%s scheduler is not available" msgstr "el planificador %s no està disponible" #: builtin/gc.c -msgid "another process is scheduling background maintenance" -msgstr "un altre procés està planificant un manteniment en segon pla" +#, c-format +msgid "" +"unable to create '%s.lock': %s.\n" +"\n" +"Another scheduled git-maintenance(1) process seems to be running in this\n" +"repository. Please make sure no other maintenance processes are running and\n" +"then try again. If it still fails, a git-maintenance(1) process may have\n" +"crashed in this repository earlier: remove the file manually to continue." +msgstr "" +"No s'ha pogut crear «%s.lock»: %s.\n" +"\n" +"Sembla que un altre procés git-maintenance(1) s'està executant en aquest\n" +"repositori. Assegureu-vos que no hi ha cap altre proés de manteniment en " +"execució\n" +"i llavors proveu de nou. Si encara falla, potser un procés git-" +"maintenance(1) \n" +"ha fallat en aquest repositori abans: elimineu el fitxer manualment per a " +"continuar." + +#: builtin/gc.c +msgid "cannot acquire lock for scheduled background maintenance" +msgstr "no puc adquirir un blocatge per al manteniment en segona mà planificat" #: builtin/gc.c msgid "git maintenance start [--scheduler=<scheduler>]" @@ -9483,9 +9634,36 @@ msgstr[0] "longitud de cadena = %d: %lu objecte" msgstr[1] "longitud de cadena = %d: %lu objectes" #: builtin/index-pack.c +msgid "could not start pack-objects to repack local links" +msgstr "" +"no s'ha pogut iniciar pack-objects per a tornar a empaquetar els enllaços " +"locals" + +#: builtin/index-pack.c +msgid "failed to feed local object to pack-objects" +msgstr "no s'ha pogut alimentar pack-objects amb l'objecte local" + +#: builtin/index-pack.c +msgid "index-pack: Expecting full hex object ID lines only from pack-objects." +msgstr "" +"index-pack: s'esperen lÃnies amb l'identificador d'objecte hexadecimal " +"complet des de pack-objects." + +#: builtin/index-pack.c +msgid "could not finish pack-objects to repack local links" +msgstr "" +"no s'ha pogut finalitzar pack-objects per a tornar a empaquetar els enllaços " +"locals" + +#: builtin/index-pack.c msgid "Cannot come back to cwd" msgstr "No es pot tornar al directori de treball actual" +#: builtin/index-pack.c builtin/unpack-objects.c +#, c-format +msgid "bad --pack_header: %s" +msgstr "--pack_header incorrecte: %s" + #: builtin/index-pack.c #, c-format msgid "bad %s" @@ -9497,6 +9675,10 @@ msgid "unknown hash algorithm '%s'" msgstr "algorisme de resum desconegut «%s»" #: builtin/index-pack.c +msgid "--promisor cannot be used with a pack name" +msgstr "--promisor no es pot usar amb un nom de paquet" + +#: builtin/index-pack.c msgid "--stdin requires a git repository" msgstr "--stdin requereix un repositori git" @@ -9636,7 +9818,7 @@ msgstr "acció si el «trailer» ja existeix" #: builtin/interpret-trailers.c msgid "action if trailer is missing" -msgstr "acció si el «trailer» falta" +msgstr "acció si el «trailer» hi manca" #: builtin/interpret-trailers.c msgid "output only the trailers" @@ -9716,22 +9898,12 @@ msgstr "" "traça l'evolució del rang de lÃnia <inici>,<final> o funcions :<nom-funció> " "a <fitxer>" -#: builtin/log.c builtin/replay.c builtin/shortlog.c bundle.c -#, c-format -msgid "unrecognized argument: %s" -msgstr "argument no reconegut: %s" - #: builtin/log.c msgid "-L<range>:<file> cannot be used with pathspec" msgstr "-L<rang>:<fitxer> no es pot usar amb una especificació de camÃ" #: builtin/log.c #, c-format -msgid "Final output: %d %s\n" -msgstr "Sortida final: %d %s\n" - -#: builtin/log.c -#, c-format msgid "git show %s: bad file" msgstr "git show %s: fitxer incorrecte" @@ -10572,6 +10744,10 @@ msgid "also show informational/conflict messages" msgstr "també mostra missatges informatius i de conflictes" #: builtin/merge-tree.c +msgid "suppress all output; only exit status wanted" +msgstr "suprimeix tota la sortida; només es vol l'estat de sortida" + +#: builtin/merge-tree.c msgid "list filenames without modes/oids/stages" msgstr "llista els noms de fitxer sense modes/oids/stages" @@ -10609,11 +10785,6 @@ msgstr "opció d'estratègia desconeguda: -X%s" msgid "malformed input line: '%s'." msgstr "lÃnia d'entrada mal formada: «%s»." -#: builtin/merge-tree.c -#, c-format -msgid "merging cannot continue; got unclean result of %d" -msgstr "la fusió no pot continuar; s'ha obtingut un resultat no net de %d" - #: builtin/merge.c msgid "git merge [<options>] [<commit>...]" msgstr "git merge [<opcions>] [<comissió>...]" @@ -10655,6 +10826,10 @@ msgid "(synonym to --stat)" msgstr "(sinònim de --stat)" #: builtin/merge.c builtin/pull.c +msgid "show a compact-summary at the end of the merge" +msgstr "mostra un compact-summary al final de la fusió" + +#: builtin/merge.c builtin/pull.c msgid "add (at most <n>) entries from shortlog to merge commit message" msgstr "" "afegeix (com a mà xim <n>) entrades del registre curt al missatge de comissió " @@ -10699,7 +10874,7 @@ msgstr "missatge de comissió de fusió (per a una fusió no d'avanç rà pid)" #: builtin/merge.c msgid "use <name> instead of the real target" -msgstr "usa <nom> en lloc de destà real" +msgstr "usa <nom> en lloc de destinació real" #: builtin/merge.c msgid "abort the current in-progress merge" @@ -10738,7 +10913,7 @@ msgstr "read-tree ha fallat" msgid "Already up to date. (nothing to squash)" msgstr "Ja està actualitzat. (res a fer «squash»)" -#: builtin/merge.c merge-ort-wrappers.c merge-recursive.c +#: builtin/merge.c merge-ort-wrappers.c msgid "Already up to date." msgstr "Ja està al dia." @@ -10762,7 +10937,7 @@ msgstr "«%s» no assenyala una comissió" msgid "Bad branch.%s.mergeoptions string: %s" msgstr "Cadena branch.%s.mergeoptions incorrecta: %s" -#: builtin/merge.c merge-recursive.c +#: builtin/merge.c merge-ort-wrappers.c msgid "Unable to write index." msgstr "No s'ha pogut escriure l'Ãndex." @@ -10793,7 +10968,8 @@ msgid "" "\n" msgstr "" "Introduïu un missatge de comissió per a explicar per què aquesta fusió és\n" -"necessà ria, especialment si es fusiona una branca amb funcionalitat nova.\n" +"necessà ria, especialment si es fusiona una font actualitzada amb una branca " +"de tòpic.\n" "\n" #: builtin/merge.c @@ -10926,7 +11102,7 @@ msgstr "Es pot fusionar només una comissió a una HEAD buida" msgid "Updating %s..%s\n" msgstr "S'estan actualitzant %s..%s\n" -#: builtin/merge.c merge-ort-wrappers.c merge-recursive.c +#: builtin/merge.c merge-ort-wrappers.c #, c-format msgid "" "Your local changes to the following files would be overwritten by merge:\n" @@ -10994,11 +11170,6 @@ msgstr "error: l'entrada d'etiqueta no passa fsck: %s" #: builtin/mktag.c #, c-format -msgid "%d (FSCK_IGNORE?) should never trigger this callback" -msgstr "%d (FSCK_IGNORE?) no hauria d'activar mai aquesta crida de retorn" - -#: builtin/mktag.c -#, c-format msgid "could not read tagged object '%s'" msgstr "no s'ha pogut llegir l'objecte etiquetat «%s»" @@ -11096,8 +11267,12 @@ msgstr "" "en un lot que és més gran que aquesta mida" #: builtin/mv.c -msgid "git mv [<options>] <source>... <destination>" -msgstr "git mv [<opcions>] <origen>... <destÃ>" +msgid "git mv [-v] [-f] [-n] [-k] <source> <destination>" +msgstr "git mv [-v] [-f] [-n] [-k] <origen> <destinacio>" + +#: builtin/mv.c +msgid "git mv [-v] [-f] [-n] [-k] <source>... <destination-directory>" +msgstr "git mv [-v] [-f] [-n] [-k] <origen>... <directori-destinacio>" #: builtin/mv.c #, c-format @@ -11117,7 +11292,7 @@ msgstr "%.*s és en l'Ãndex" #: builtin/mv.c msgid "force move/rename even if target exists" -msgstr "força el moviment / canvi de nom encara que el destà existeixi" +msgstr "força el moviment / canvi de nom encara que la destinació existeixi" #: builtin/mv.c msgid "skip move/rename errors" @@ -11126,7 +11301,7 @@ msgstr "omet els errors de moviment / canvi de nom" #: builtin/mv.c #, c-format msgid "destination '%s' is not a directory" -msgstr "el destà «%s» no és un directori" +msgstr "la destinació «%s» no és un directori" #: builtin/mv.c #, c-format @@ -11139,7 +11314,7 @@ msgstr "origen incorrecte" #: builtin/mv.c msgid "destination exists" -msgstr "el destà existeix" +msgstr "la destinació existeix" #: builtin/mv.c msgid "can not move directory into itself" @@ -11172,20 +11347,25 @@ msgstr "No es pot sobreescriure" #: builtin/mv.c msgid "multiple sources for the same target" -msgstr "múltiples orÃgens per al mateix destÃ" +msgstr "múltiples orÃgens per a la mateixa destinació" #: builtin/mv.c msgid "destination directory does not exist" -msgstr "el directori destà no existeix" +msgstr "el directori de destinació no existeix" #: builtin/mv.c msgid "destination exists in the index" -msgstr "el destà existeix a l'Ãndex" +msgstr "la destinació existeix a l'Ãndex" #: builtin/mv.c #, c-format msgid "%s, source=%s, destination=%s" -msgstr "%s, origen=%s, destÃ=%s" +msgstr "%s, origen=%s, destinació=%s" + +#: builtin/mv.c +#, c-format +msgid "cannot move both '%s' and its parent directory '%s'" +msgstr "no puc moure «%s» i el seu directori pare «%s» simultà niament" #: builtin/mv.c #, c-format @@ -11247,60 +11427,59 @@ msgstr "desreferencia les etiquetes en l'entrada (ús intern)" #: builtin/notes.c msgid "git notes [--ref <notes-ref>] [list [<object>]]" -msgstr "git notes [--ref <referència-de-notes>] [llista [<objecte>]]" +msgstr "git notes [--ref <referència-notes>] [llista [<objecte>]]" #: builtin/notes.c msgid "" "git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--" "separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c " -"| -C) <object>] [<object>]" +"| -C) <object>] [<object>] [-e]" msgstr "" -"git notes [--ref <notes-ref>] add [-f] [--allow-empty] [--[no-]separator|--" -"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <fitxer> | (-" -"c | -C) <object>] [<object>]" +"git notes [--ref <referència-notes>] add [-f] [--allow-empty] [--" +"[no-]separator|--separator=<salt-parà graf>] [--[no-]stripspace] [-m <msg> | -" +"F <fitxer> | (-c | -C) <objecte>] [<objecte>]" #: builtin/notes.c msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>" msgstr "" -"git notes [--ref <referència-de-notes>] copy [-f] <objecte-de> <objecte-a>" +"git notes [--ref <referència-notes>] copy [-f] <objecte-de> <objecte-a>" #: builtin/notes.c msgid "" "git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--" "separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <file> | (-c " -"| -C) <object>] [<object>]" +"| -C) <object>] [<object>] [-e]" msgstr "" -"git notes [--ref <notes-ref>] append [--allow-empty] [--[no-]separator|--" -"separator=<paragraph-break>] [--[no-]stripspace] [-m <msg> | -F <fitxer> | (-" -"c | -C) <objecte>] [<objecte>]" +"git notes [--ref <referència-notes>] append [--allow-empty] [--" +"[no-]separator|--separator=<salt-parà graf>] [--[no-]stripspace] [-m <msg> | -" +"F <fitxer> | (-c | -C) <objecte>] [<objecte>] [-e]" #: builtin/notes.c msgid "git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]" -msgstr "" -"git notes [--ref <referència-de-notes>] edit [--allow-empty] [<objecte>]" +msgstr "git notes [--ref <referència-notes>] edit [--allow-empty] [<objecte>]" #: builtin/notes.c msgid "git notes [--ref <notes-ref>] show [<object>]" -msgstr "git notes [--ref <referència-de-notes>] show [<objecte>]" +msgstr "git notes [--ref <referència-notes>] show [<objecte>]" #: builtin/notes.c msgid "" "git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>" msgstr "" -"git notes [--ref <referència-de-notes>] merge [-v | -q] [-s <estratègia>] " -"<referència-de-notes>" +"git notes [--ref <referència-notes>] merge [-v | -q] [-s <estratègia>] " +"<referència-notes>" #: builtin/notes.c msgid "git notes [--ref <notes-ref>] remove [<object>...]" -msgstr "git notes [--ref <referència-de-notes>] remove [<objecte>...]" +msgstr "git notes [--ref <referència-notes>] remove [<objecte>...]" #: builtin/notes.c msgid "git notes [--ref <notes-ref>] prune [-n] [-v]" -msgstr "git notes [--ref <referència-de-notes>] prune [-n] [-v]" +msgstr "git notes [--ref <referència-notes>] prune [-n] [-v]" #: builtin/notes.c msgid "git notes [--ref <notes-ref>] get-ref" -msgstr "git notes [--ref <referència-de-notes>] get-ref" +msgstr "git notes [--ref <referència-notes>] get-ref" #: builtin/notes.c msgid "git notes [list [<object>]]" @@ -11332,7 +11511,7 @@ msgstr "git notes show [<objecte>]" #: builtin/notes.c msgid "git notes merge [<options>] <notes-ref>" -msgstr "git notes merge [<opcions>] <referència-de-notes>" +msgstr "git notes merge [<opcions>] <referència-notes>" #: builtin/notes.c msgid "git notes merge --commit [<options>]" @@ -11428,6 +11607,10 @@ msgid "reuse and edit specified note object" msgstr "reusa i edita l'objecte de nota especificat" #: builtin/notes.c +msgid "edit note message in editor" +msgstr "edita el missatge d'anotació en l'editor" + +#: builtin/notes.c msgid "reuse specified note object" msgstr "reusa l'objecte de nota especificat" @@ -11644,7 +11827,7 @@ msgstr "referència de notes" #: builtin/notes.c msgid "use notes from <notes-ref>" -msgstr "usa les notes de <referència-de-notes>" +msgstr "usa les notes de <referència-notes>" #: builtin/notes.c builtin/remote.c parse-options.c #, c-format @@ -11652,14 +11835,35 @@ msgid "unknown subcommand: `%s'" msgstr "subordre desconeguda: «%s»" #: builtin/pack-objects.c -msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]" -msgstr "git pack-objects --stdout [<opcions>] [< <ref-list> | < <object-list>]" +msgid "" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<pack-name>]\n" +" [--cruft] [--cruft-expiration=<time>]\n" +" [--stdout [--filter=<filter-spec>] | <base-name>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=<n>] [--path-walk] < <object-list>" +msgstr "" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<nom-paquet>]\n" +" [--cruft] [--cruft-expiration=<data>]\n" +" [--stdout [--filter=<especificacio-filtre>] | <nom-base>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=<n>] [--path-walk] < <lista-objectes>" #: builtin/pack-objects.c -msgid "" -"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]" -msgstr "" -"git pack-objects [<opcions>] <base-name> [< <ref-list> | < <object-list>]" +#, c-format +msgid "invalid --name-hash-version option: %d" +msgstr "opció --name-hash-version no và lida: %d" + +#: builtin/pack-objects.c +msgid "currently, --write-bitmap-index requires --name-hash-version=1" +msgstr "actualment, --write-bitmap-index requereix --name-hash-version=1" #: builtin/pack-objects.c #, c-format @@ -11786,6 +11990,17 @@ msgid "unable to get type of object %s" msgstr "no s'ha pogut obtenir el tipus de l'objecte: %s" #: builtin/pack-objects.c +msgid "Compressing objects by path" +msgstr "S'estan comprimint els objectes per camÃ" + +#: builtin/pack-objects.c +#, c-format +msgid "Path-based delta compression using up to %d thread" +msgid_plural "Path-based delta compression using up to %d threads" +msgstr[0] "La compressió de diferències basada en camins usa fins a %d fil" +msgstr[1] "La compressió de diferències basada en camins usa fins a %d fils" + +#: builtin/pack-objects.c msgid "Compressing objects" msgstr "S'estan comprimint els objectes" @@ -11878,6 +12093,10 @@ msgid "unable to force loose object" msgstr "no s'ha pogut forçar l'objecte solt" #: builtin/pack-objects.c +msgid "failed to pack objects via path-walk" +msgstr "no s'ha pogut empaquetar els objectes via path-walk" + +#: builtin/pack-objects.c #, c-format msgid "not a rev '%s'" msgstr "«%s» no és una revisió" @@ -12030,6 +12249,10 @@ msgid "create thin packs" msgstr "crea paquets prims" #: builtin/pack-objects.c +msgid "use the path-walk API to walk objects when possible" +msgstr "useu l'API path-walk per a recórrer objectes sempre que sigui possible" + +#: builtin/pack-objects.c msgid "create packs suitable for shallow fetches" msgstr "crea paquets adequats per a les obtencions superficials" @@ -12072,6 +12295,10 @@ msgid "do not pack objects in promisor packfiles" msgstr "no empaquetis els objectes als fitxers de paquet «promisor»" #: builtin/pack-objects.c +msgid "implies --missing=allow-any" +msgstr "implica --missing=allow-any" + +#: builtin/pack-objects.c msgid "respect islands during delta compression" msgstr "respecta les illes durant la compressió delta" @@ -12085,6 +12312,10 @@ msgstr "" "exclou qualsevol uploadpack.blobpackfileuri configurat amb aquest protocol" #: builtin/pack-objects.c +msgid "use the specified name-hash function to group similar objects" +msgstr "usa la funció name-hash especificada per a agrupar objectes similars" + +#: builtin/pack-objects.c #, c-format msgid "delta chain depth %d is too deep, forcing %d" msgstr "la profunditat de la cadena delta %d és massa profunda, forçant %d" @@ -12094,7 +12325,12 @@ msgstr "la profunditat de la cadena delta %d és massa profunda, forçant %d" msgid "pack.deltaCacheLimit is too high, forcing %d" msgstr "pack.deltaCacheLimit és massa alt, forçant %d" -#: builtin/pack-objects.c config.c +#: builtin/pack-objects.c +#, c-format +msgid "cannot use %s with %s" +msgstr "no es pot usar %s amb %s" + +#: builtin/pack-objects.c environment.c #, c-format msgid "bad pack compression level %d" msgstr "nivell de compressió de paquet %d erroni" @@ -12114,10 +12350,6 @@ msgid "--thin cannot be used to build an indexable pack" msgstr "--thin no es pot utilitzar per a construir un paquet indexable" #: builtin/pack-objects.c -msgid "cannot use --filter with --stdin-packs" -msgstr "no es pot utilitzar --filter sense --stdin-packs" - -#: builtin/pack-objects.c msgid "cannot use internal rev list with --stdin-packs" msgstr "no es pot utilitzar la llista de revisió interna amb --stdin-packs" @@ -12126,10 +12358,6 @@ msgid "cannot use internal rev list with --cruft" msgstr "no es pot utilitzar la llista de revisió interna amb --cruft" #: builtin/pack-objects.c -msgid "cannot use --stdin-packs with --cruft" -msgstr "no es pot --stdin-packs amb --cruft" - -#: builtin/pack-objects.c msgid "Enumerating objects" msgstr "S'estan enumerant els objectes" @@ -12142,24 +12370,6 @@ msgstr "" "Total %<PRIu32> (%<PRIu32> diferències), reusats %<PRIu32> (%<PRIu32> " "diferències), paquets reusats %<PRIu32> (de %<PRIuMAX>)" -#: builtin/pack-redundant.c -msgid "" -"'git pack-redundant' is nominated for removal.\n" -"If you still use this command, please add an extra\n" -"option, '--i-still-use-this', on the command line\n" -"and let us know you still use it by sending an e-mail\n" -"to <git@vger.kernel.org>. Thanks.\n" -msgstr "" -"«git pack-redundant» està nominat per a la seva supressió.\n" -"Si encara feu servir aquesta ordre, afegiu-hi l'opció\n" -"addicional, «--i-still-use-this», a la lÃnia d'ordres\n" -"i feu-nos saber que encara l'useu enviant un correu electrònic\n" -"a <git@vger.kernel.org>. Grà cies.\n" - -#: builtin/pack-redundant.c -msgid "refusing to run without --i-still-use-this" -msgstr "es rebutja a executar sense --i-still-use-this" - #: builtin/pack-refs.c msgid "" "git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude " @@ -12355,6 +12565,11 @@ msgstr "" msgid "unable to access commit %s" msgstr "no s'ha pogut accedir a la comissió %s" +#: builtin/pull.c refspec.c +#, c-format +msgid "invalid refspec '%s'" +msgstr "refspec no và lida: «%s»" + #: builtin/pull.c msgid "ignoring --verify-signatures for rebase" msgstr "s'està ignorant --verify-signatures en fer «rebase»" @@ -12463,7 +12678,7 @@ msgstr "abreviatura d'etiqueta sense <etiqueta>" #: builtin/push.c msgid "--delete only accepts plain target ref names" -msgstr "--delete només accepta noms de referència de destà senzills" +msgstr "--delete només accepta noms de referència de destinació senzills" #: builtin/push.c msgid "" @@ -12533,9 +12748,8 @@ msgid "" "upstream, see 'push.autoSetupRemote' in 'git help config'.\n" msgstr "" "\n" -"Per fer que això succeeixi automà ticament per a les branques sense " -"seguiment\n" -"font, vegeu «push.autoSetupRemote» a «git help config».\n" +"Per fer que això succeeixi automà ticament per a les fonts sense seguiment,\n" +"vegeu «push.autoSetupRemote» a «git help config».\n" #: builtin/push.c #, c-format @@ -12555,8 +12769,7 @@ msgstr "" #: builtin/push.c #, c-format msgid "The current branch %s has multiple upstream branches, refusing to push." -msgstr "" -"La branca actual %s té múltiples branques fonts, s'està refusant pujar." +msgstr "La branca actual %s té múltiples branques font; s'està refusant pujar." #: builtin/push.c msgid "" @@ -12759,7 +12972,7 @@ msgid "" "\n" " git push <name>\n" msgstr "" -"No hi ha cap destà de pujada configurat.\n" +"No hi ha cap destinació de pujada configurat.\n" "Especifiqueu l'URL des de la lÃnia d'ordres o bé configureu un repositori " "remot fent servir\n" "\n" @@ -12922,7 +13135,7 @@ msgid "" "[<upstream> [<branch>]]" msgstr "" "git rebase [-i] [options] [--exec <ordre>] [--onto <newbase> | --keep-base] " -"[<upstream> [<branca>]]" +"[<font> [<branca>]]" #: builtin/rebase.c msgid "" @@ -13018,8 +13231,8 @@ msgstr "--empty=ask és obslolet; utilitzeu '--empty=stop' en el seu lloc." #: builtin/rebase.c #, c-format msgid "" -"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and " -"\"stop\"." +"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"stop" +"\"." msgstr "" "tipus buit «%s» no reconegut; els valors và lids són \"drop\", \"keep\" i " "\"stop\"." @@ -13510,6 +13723,10 @@ msgid "git reflog exists <ref>" msgstr "git reflog exists <referència>" #: builtin/reflog.c +msgid "git reflog drop [--all [--single-worktree] | <refs>...]" +msgstr "git reflog drop [--all [--single-worktree] | <refs>...]" + +#: builtin/reflog.c #, c-format msgid "invalid timestamp '%s' given to '--%s'" msgstr "marca de temps «%s» donada a «--%s» no és và lida" @@ -13577,8 +13794,8 @@ msgstr "S'estan marcant els objectes abastables..." #: builtin/reflog.c #, c-format -msgid "%s points nowhere!" -msgstr "%s no apunta a enlloc" +msgid "reflog could not be found: '%s'" +msgstr "no s'ha pogut trobar el reflog: «%s»" #: builtin/reflog.c msgid "no reflog specified to delete" @@ -13589,9 +13806,21 @@ msgstr "no s'ha especificat cap registre de referències per a suprimir" msgid "invalid ref format: %s" msgstr "format de referència no và lid: %s" +#: builtin/reflog.c +msgid "drop the reflogs of all references" +msgstr "descarta els reflogs de totes les referències" + +#: builtin/reflog.c +msgid "drop reflogs from the current worktree only" +msgstr "descarta només els reflogs de l'arbre de treball actual" + +#: builtin/reflog.c +msgid "references specified along with --all" +msgstr "referències especificades conjuntament amb --all" + #: builtin/refs.c -msgid "git refs migrate --ref-format=<format> [--dry-run]" -msgstr "git refs migrate --ref-format=<format> [--dry-run]" +msgid "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]" +msgstr "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]" #: builtin/refs.c msgid "git refs verify [--strict] [--verbose]" @@ -13606,8 +13835,12 @@ msgid "perform a non-destructive dry-run" msgstr "fes una prova no destructiva" #: builtin/refs.c +msgid "drop reflogs entirely during the migration" +msgstr "descarta completament els reflogs durant la migració" + +#: builtin/refs.c msgid "missing --ref-format=<format>" -msgstr "falta --ref-format=<format>" +msgstr "hi manca --ref-format=<format>" #: builtin/refs.c #, c-format @@ -13725,6 +13958,18 @@ msgid "unknown --mirror argument: %s" msgstr "argument de «--mirror» desconegut: %s" #: builtin/remote.c +#, c-format +msgid "remote name '%s' is a subset of existing remote '%s'" +msgstr "el nom remot «%s» és un subconjunt del remot existent «%s»" + +#: builtin/remote.c +#, c-format +msgid "remote name '%s' is a superset of existing remote '%s'" +msgstr "" +"el nom remot «%s» és un supeconjunt\n" +" del remot existent «%s»" + +#: builtin/remote.c msgid "fetch the remote branches" msgstr "obtén les branques remotes" @@ -14040,6 +14285,35 @@ msgstr[0] " Referència local configurada per a «git push»%s:" msgstr[1] " Referències locals configurades per a «git push»%s:" #: builtin/remote.c +#, c-format +msgid "'%s/HEAD' is unchanged and points to '%s'\n" +msgstr "«%s/HEAD» no ha canviat i apunta a «%s»\n" + +#: builtin/remote.c +#, c-format +msgid "'%s/HEAD' has changed from '%s' and now points to '%s'\n" +msgstr "«%s/HEAD» ha canviat des de «%s» i ara apunta a «%s»\n" + +#: builtin/remote.c +#, c-format +msgid "'%s/HEAD' is now created and points to '%s'\n" +msgstr "«%s/HEAD» s'ha creat ara i apunta a «%s»\n" + +#: builtin/remote.c +#, c-format +msgid "'%s/HEAD' was detached at '%s' and now points to '%s'\n" +msgstr "«%s/HEAD» s'ha separat en «%s» i ara apunta a «%s»\n" + +#: builtin/remote.c +#, c-format +msgid "" +"'%s/HEAD' used to point to '%s' (which is not a remote branch), but now " +"points to '%s'\n" +msgstr "" +"«%s/HEAD» solia apuntar a «%s» (que no és una branca remota), però ara " +"apunta a«%s»\n" + +#: builtin/remote.c msgid "set refs/remotes/<name>/HEAD according to remote" msgstr "estableix refs/remotes/<nom>/HEAD segons el remot" @@ -14067,21 +14341,11 @@ msgstr "No és una referència và lida: %s" #: builtin/remote.c #, c-format -msgid "Could not setup %s" +msgid "Could not set up %s" msgstr "No s'ha pogut configurar %s" #: builtin/remote.c #, c-format -msgid " %s will become dangling!" -msgstr " %s es quedara despenjat!" - -#: builtin/remote.c -#, c-format -msgid " %s has become dangling!" -msgstr " %s s'ha quedat despenjat!" - -#: builtin/remote.c -#, c-format msgid "Pruning %s" msgstr "S'està podant %s" @@ -14160,8 +14424,14 @@ msgid "be verbose; must be placed before a subcommand" msgstr "sigues detallat; s'ha de col·locar abans d'una subordre" #: builtin/repack.c -msgid "git repack [<options>]" -msgstr "git repack [<opcions>]" +msgid "" +"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" +"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n" +"[--write-midx] [--name-hash-version=<n>] [--path-walk]" +msgstr "" +"git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" +"[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<nom-paquet>]\n" +"[--write-midx] [--name-hash-version=<n>] [--path-walk]" #: builtin/repack.c msgid "" @@ -14254,6 +14524,10 @@ msgid "with --cruft, expire objects older than this" msgstr "amb --cruft, vencen els objectes més antics que aquest" #: builtin/repack.c +msgid "with --cruft, only repack cruft packs smaller than this" +msgstr "amb --cruft, empaqueta només els objectes més petits que aquest" + +#: builtin/repack.c msgid "remove redundant packs, and run git-prune-packed" msgstr "elimina els paquets redundants, i executeu git-prune-packed" @@ -14266,6 +14540,17 @@ msgid "pass --no-reuse-object to git-pack-objects" msgstr "passa --no-reuse-object a git-pack-objects" #: builtin/repack.c +msgid "" +"specify the name hash version to use for grouping similar objects by path" +msgstr "" +"especifica la versió de hash de noms que s'ha d'usar per a agrupar objectes " +"similars per camÃ" + +#: builtin/repack.c +msgid "pass --path-walk to git-pack-objects" +msgstr "passa --path-walk a git-pack-objects" + +#: builtin/repack.c msgid "do not run git-update-server-info" msgstr "no executis git-update-server-info" @@ -14332,11 +14617,6 @@ msgid "write a multi-pack index of the resulting packs" msgstr "escriu un Ãndex multipaquet dels paquets resultants" #: builtin/repack.c -msgid "pack prefix to store a pack containing pruned objects" -msgstr "" -"prefix del paquet per a emmagatzemar un paquet que contingui objectes podats" - -#: builtin/repack.c msgid "pack prefix to store a pack containing filtered out objects" msgstr "" "prefix del paquet per a emmagatzemar un paquet que contingui objectes " @@ -14525,7 +14805,7 @@ msgstr "no s'ha pogut escriure la comissió de reemplaçament per a: «%s»" #: builtin/replace.c #, c-format msgid "graft for '%s' unnecessary" -msgstr "«graft» per a «%s» innecessari" +msgstr "l'empelt per a «%s» és innecessari" #: builtin/replace.c #, c-format @@ -14538,7 +14818,7 @@ msgid "" "could not convert the following graft(s):\n" "%s" msgstr "" -"no s'han pogut convertir els següents «grafts»:\n" +"no s'han pogut convertir els empelts següents:\n" "%s" #: builtin/replace.c @@ -14559,7 +14839,7 @@ msgstr "canvia els pares d'una comissió" #: builtin/replace.c msgid "convert existing graft file" -msgstr "converteix el fitxer «graft» existent" +msgstr "converteix el fitxer d'empelts existent" #: builtin/replace.c msgid "replace the ref if it exists" @@ -14614,10 +14894,6 @@ msgid "need some commits to replay" msgstr "calen algunes comissions per tornar a reproduir" #: builtin/replay.c -msgid "--onto and --advance are incompatible" -msgstr "--onto i --advance són incompatibles" - -#: builtin/replay.c msgid "all positive revisions given must be references" msgstr "totes les revisions positives que s'han donat han de ser referències" @@ -14867,6 +15143,10 @@ msgid "invalid value for '%s': '%s', the only allowed format is '%s'" msgstr "valor no và lid per a «%s»: «%s», l'únic format permès és «%s»" #: builtin/rev-list.c +msgid "-z option used with unsupported option" +msgstr "l'opció -z s'ha usat amb una opció no admesa" + +#: builtin/rev-list.c msgid "rev-list does not support display of notes" msgstr "el rev-list no permet mostrar notes" @@ -15733,20 +16013,28 @@ msgid "git stash create [<message>]" msgstr "git stash create [<missatge>]" #: builtin/stash.c +msgid "git stash export (--print | --to-ref <ref>) [<stash>...]" +msgstr "git stash export (--print | --to-ref <referència>) [<stash>...]" + +#: builtin/stash.c +msgid "git stash import <commit>" +msgstr "git stash import [<comissió>]" + +#: builtin/stash.c #, c-format msgid "'%s' is not a stash-like commit" msgstr "«%s» no és una comissió de tipus «stash»" #: builtin/stash.c +msgid "No stash entries found." +msgstr "No s'ha trobat cap entrada «stash»." + +#: builtin/stash.c #, c-format msgid "Too many revisions specified:%s" msgstr "S'han especificat massa revisions:%s" #: builtin/stash.c -msgid "No stash entries found." -msgstr "No s'ha trobat cap entrada «stash»." - -#: builtin/stash.c #, c-format msgid "%s is not a valid reference" msgstr "«%s» no és una referència và lida" @@ -15942,6 +16230,88 @@ msgstr "inclou els fitxers no seguits a «stash»" msgid "include ignore files" msgstr "inclou els fitxers ignorats" +#: builtin/stash.c +#, c-format +msgid "cannot parse commit %s" +msgstr "no s'ha pogut analitzar la comissió %s" + +#: builtin/stash.c +#, c-format +msgid "invalid author or committer for %s" +msgstr "autor o comissor invà lid per a %s" + +#: builtin/stash.c +msgid "could not write commit" +msgstr "no s'ha pogut escriure la comissió" + +#: builtin/stash.c +#, c-format +msgid "not a valid revision: %s" +msgstr "«%s» no és una revisió và lida" + +#: builtin/stash.c +#, c-format +msgid "not a commit: %s" +msgstr "no és una comissió: «%s»" + +#: builtin/stash.c +#, c-format +msgid "%s is not a valid exported stash commit" +msgstr "«%s» no és una comissió de «stash» exportada và lida" + +#: builtin/stash.c +#, c-format +msgid "found root commit %s with invalid data" +msgstr "s'ha trobat la comissió %s amb dades invà lides" + +#: builtin/stash.c +#, c-format +msgid "found stash commit %s without expected prefix" +msgstr "s'ha trobat una comissió de «stash» %s sense el prefix esperat" + +#: builtin/stash.c +#, c-format +msgid "cannot parse parents of commit: %s" +msgstr "no es poden analitzar els pares de la comissió: %s" + +#: builtin/stash.c +#, c-format +msgid "%s does not look like a stash commit" +msgstr "«%s» no sembla una comissió de «stash»" + +#: builtin/stash.c +#, c-format +msgid "cannot read commit buffer for %s" +msgstr "no es pot llegir la memòria intermèdia de comissió per a %s" + +#: builtin/stash.c +#, c-format +msgid "cannot save the stash for %s" +msgstr "no es pot desar l'«stash» per a %s" + +#: builtin/stash.c +msgid "unable to write base commit" +msgstr "no s'ha pogut escriure la comissió base %s" + +#: builtin/stash.c +#, c-format +msgid "unable to find stash entry %s" +msgstr "no s'ha pogut trobar l'entrada de «stash» %s" + +#: builtin/stash.c +msgid "print the object ID instead of writing it to a ref" +msgstr "" +"imprimeix l'identificador de l'objecte en comptes d'escriure'l en una " +"referència" + +#: builtin/stash.c +msgid "save the data to the given ref" +msgstr "desa les dades en la referència donada" + +#: builtin/stash.c +msgid "exactly one of --print and --to-ref is required" +msgstr "es requereix exactament una de --print i --to-ref" + #: builtin/stripspace.c msgid "skip and remove all lines starting with comment character" msgstr "" @@ -15953,16 +16323,6 @@ msgstr "anteposa el carà cter de comentari i un espai a cada lÃnia" #: builtin/submodule--helper.c #, c-format -msgid "Expecting a full ref name, got %s" -msgstr "S'espera un nom de referència ple, s'ha rebut %s" - -#: builtin/submodule--helper.c -#, c-format -msgid "could not get a repository handle for submodule '%s'" -msgstr "no s'ha pogut obtenir el gestor del repositori pel submòdul «%s»" - -#: builtin/submodule--helper.c -#, c-format msgid "" "could not look up configuration '%s'. Assuming this repository is its own " "authoritative upstream." @@ -15972,6 +16332,11 @@ msgstr "" #: builtin/submodule--helper.c #, c-format +msgid "could not get a repository handle for submodule '%s'" +msgstr "no s'ha pogut obtenir el gestor del repositori pel submòdul «%s»" + +#: builtin/submodule--helper.c +#, c-format msgid "No url found for submodule path '%s' in .gitmodules" msgstr "No s'ha trobat cap url per al camà de submòdul «%s» a .gitmodules" @@ -16298,7 +16663,7 @@ msgid "" msgstr "" "git submodule--helper clone [--prefix=<camÃ>] [--quiet] [--reference " "<repositori>] [--name <nom>] [--depth <depth>] [--single-branch] [--filter " -"<filter-spec>] --url <url> --path <camÃ>" +"<especificacio-filtre>] --url <url> --path <camÃ>" #: builtin/submodule--helper.c #, c-format @@ -16412,6 +16777,11 @@ msgstr "" #: builtin/submodule--helper.c #, c-format +msgid "Expecting a full ref name, got %s" +msgstr "S'espera un nom de referència ple, s'ha rebut %s" + +#: builtin/submodule--helper.c +#, c-format msgid "Unable to find current revision in submodule path '%s'" msgstr "No s'ha pogut trobar la revisió actual al camà del submòdul «%s»" @@ -16490,10 +16860,10 @@ msgid "" "shallow] [--reference <repository>] [--recursive] [--[no-]single-branch] " "[--] [<path>...]" msgstr "" -"git submodule [--quiet] update [--init [--filter=<filter-spec>]] [--remote] " -"[-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--[no-]recommend-" -"shallow] [--reference <repositori>] [--recursive] [--[no-]single-branch] " -"[--] [<camÃ>...]" +"git submodule [--quiet] update [--init [--filter=<especificacio-filtre>]] [--" +"remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--" +"[no-]recommend-shallow] [--reference <repositori>] [--recursive] [--" +"[no-]single-branch] [--] [<camÃ>...]" #: builtin/submodule--helper.c submodule.c msgid "Failed to resolve HEAD as a valid ref." @@ -16665,6 +17035,11 @@ msgstr "URL de repositori: «%s» ha de ser absolut o començar amb ./|../" #: builtin/submodule--helper.c #, c-format +msgid "submodule name '%s' already used for path '%s'" +msgstr "el nom de submòdul «%s» ja s'usa per al camà «%s»" + +#: builtin/submodule--helper.c +#, c-format msgid "'%s' is not a valid submodule name" msgstr "«%s» no és un nom de submòdul và lid" @@ -16714,7 +17089,7 @@ msgid "" " [(--trailer <token>[(=|:)<value>])...]\n" " <tagname> [<commit> | <object>]" msgstr "" -"git tag [-a | -s | -u <id-clau>] [-f] [-m <missatge> | -F <fitxer>] [-e]\n" +"git tag [-a | -s | -u <id-clau>] [-f] [-m <msg> | -F <fitxer>] [-e]\n" " [(--trailer <token>[(=|:)<valor>])...]\n" " <nom-etiqueta> [<comissió> | <objecte>]" @@ -17219,8 +17594,8 @@ msgid "git update-ref [<options>] <refname> <new-oid> [<old-oid>]" msgstr "git update-ref [<opcions>] <nom-referència> <oid-nou> [<oid-vell>]" #: builtin/update-ref.c -msgid "git update-ref [<options>] --stdin [-z]" -msgstr "git update-ref [<opcions>] --stdin [-z]" +msgid "git update-ref [<options>] --stdin [-z] [--batch-updates]" +msgstr "git update-ref [<opcions>] --stdin [-z] [--batch-updates]" #: builtin/update-ref.c msgid "delete the reference" @@ -17238,6 +17613,10 @@ msgstr "stdin té arguments acabats amb NUL" msgid "read updates from stdin" msgstr "llegeix les actualitzacions des de stdin" +#: builtin/update-ref.c +msgid "batch reference updates" +msgstr "actualitzacions de les referències de batch" + #: builtin/update-server-info.c msgid "update the info files from scratch" msgstr "actualitza els fitxers d'informació des de zero" @@ -17464,11 +17843,6 @@ msgstr "S'està preparant l'arbre de treball (s'està agafant «%s»)" #: builtin/worktree.c #, c-format -msgid "unreachable: invalid reference: %s" -msgstr "no accessible: referència no và lida: %s" - -#: builtin/worktree.c -#, c-format msgid "Preparing worktree (detached HEAD %s)" msgstr "S'està preparant l'arbre de treball (HEAD %s separat)" @@ -17529,6 +17903,10 @@ msgstr "" "prova de fer coincidir el nom de la branca nova amb una branca amb seguiment " "remot" +#: builtin/worktree.c +msgid "use relative paths for worktrees" +msgstr "usa camins relatius per als arbres de treball" + #: builtin/worktree.c diff.c parse-options.c #, c-format msgid "options '%s', '%s', and '%s' cannot be used together" @@ -17602,7 +17980,7 @@ msgstr "«%s» és un arbre de treball principal" #: builtin/worktree.c #, c-format msgid "could not figure out destination name from '%s'" -msgstr "no s'ha pogut deduir el nom de destà des de «%s»" +msgstr "no s'ha pogut deduir el nom de destinació des de «%s»" #: builtin/worktree.c #, c-format @@ -17610,7 +17988,7 @@ msgid "" "cannot move a locked working tree, lock reason: %s\n" "use 'move -f -f' to override or unlock first" msgstr "" -"no es pot moure un arbre de treball bloquejat, raó del bloqueig: %s\n" +"no es pot moure un arbre de treball bloquejat, raó del blocatge: %s\n" "useu primer «move -f -f» per a sobreescriure'l o desbloquejar-lo primer" #: builtin/worktree.c @@ -17658,7 +18036,7 @@ msgid "" "cannot remove a locked working tree, lock reason: %s\n" "use 'remove -f -f' to override or unlock first" msgstr "" -"no es pot suprimir un arbre de treball bloquejat, raó del bloqueig: %s\n" +"no es pot suprimir un arbre de treball bloquejat, raó del blocatge: %s\n" "useu primer «remove -f -f» per a sobreescriure'l o desbloquejar-lo" #: builtin/worktree.c @@ -17873,6 +18251,32 @@ msgstr "no es pot crear «%s»" msgid "index-pack died" msgstr "l'index-pack s'ha mort" +#: cache-tree.c +#, c-format +msgid "directory '%s' is present in index, but not sparse" +msgstr "El directori %s és en l'Ãndex però no és dispers" + +#: cache-tree.c unpack-trees.c +msgid "corrupted cache-tree has entries not present in index" +msgstr "el cache-tree corromput té entrades que no apareixen en l'Ãndex" + +#: cache-tree.c +#, c-format +msgid "%s with flags 0x%x should not be in cache-tree" +msgstr "%s amb els indicadors 0x%x no hauria de ser en el cache-tree" + +#: cache-tree.c +#, c-format +msgid "bad subtree '%.*s'" +msgstr "subarbre incorrecte «%.*s»" + +#: cache-tree.c +#, c-format +msgid "cache-tree for path %.*s does not match. Expected %s got %s" +msgstr "" +"el cache-tree per al camà %.*s no coincideix. S'esperava %s i s'ha obtingut " +"%s" + #: chunk-format.c msgid "terminating chunk id appears earlier than expected" msgstr "" @@ -17932,6 +18336,10 @@ msgid "Create an archive of files from a named tree" msgstr "Crea un arxiu de fitxers des d'un arbre amb nom" #: command-list.h +msgid "Download missing objects in a partial clone" +msgstr "Descarrega els objectes que manquen en un clon parcial" + +#: command-list.h msgid "Use binary search to find the commit that introduced a bug" msgstr "Troba per cerca binà ria el canvi que hagi introduït un defecte" @@ -18076,6 +18484,10 @@ msgid "Compare a tree to the working tree or index" msgstr "Compara un arbre amb l'arbre de treball o l'Ãndex" #: command-list.h +msgid "Compare the content and mode of provided blob pairs" +msgstr "Compara el contingut i el mode dels blobs proporcionats" + +#: command-list.h msgid "Compares the content and mode of blobs found via two tree objects" msgstr "" "Compara el contingut i el mode dels blobs trobats a través de dos objectes " @@ -19048,13 +19460,13 @@ msgid "" "to convert the grafts into replace refs.\n" "\n" "Turn this message off by running\n" -"\"git config advice.graftFileDeprecated false\"" +"\"git config set advice.graftFileDeprecated false\"" msgstr "" "La compatibilitat amb <GIT_DIR>/info/grafts és obsoleta\n" "i s'eliminarà en una futura versió del Git.\n" "\n" "Useu «git replace --convert-graft-file»\n" -"per a convertir els grafs en referències de reemplaçament.\n" +"per a convertir els empelts en referències de reemplaçament.\n" "\n" "Desactiveu aquest missatge executant\n" "«git config advice.graftFileDeprecated false»" @@ -19395,12 +19807,12 @@ msgstr "format de configuració no và lid: %s" #: config.c #, c-format msgid "missing environment variable name for configuration '%.*s'" -msgstr "falta el nom de la variable d'entorn per a la configuració «%.*s»" +msgstr "manca el nom de la variable d'entorn per a la configuració «%.*s»" #: config.c #, c-format msgid "missing environment variable '%s' for configuration '%.*s'" -msgstr "falta la variable d'entorn «%s» per a la configuració «%.*s»" +msgstr "hi manca la variable d'entorn «%s» per a la configuració «%.*s»" #: config.c #, c-format @@ -19449,12 +19861,12 @@ msgstr "hi ha massa arguments a %s" #: config.c #, c-format msgid "missing config key %s" -msgstr "falta la clau de configuració %s" +msgstr "hi manca la clau de configuració %s" #: config.c #, c-format msgid "missing config value %s" -msgstr "falta el valor de configuració %s" +msgstr "hi manca el valor de configuració %s" #: config.c #, c-format @@ -19536,16 +19948,6 @@ msgstr "valor de configuració numèric incorrecte «%s» per «%s» en %s: %s" #: config.c #, c-format -msgid "invalid value for variable %s" -msgstr "valor no và lid per a la variable %s" - -#: config.c -#, c-format -msgid "ignoring unknown core.fsync component '%s'" -msgstr "s'ignora el component core.fsync «%s» desconegut" - -#: config.c -#, c-format msgid "bad boolean config value '%s' for '%s'" msgstr "valor de configuració booleà erroni «%s» per a «%s»" @@ -19561,57 +19963,6 @@ msgstr "«%s» per a «%s» no és una marca de temps và lida" #: config.c #, c-format -msgid "abbrev length out of range: %d" -msgstr "la longitud d'«abbrev» està fora de rang: %d" - -#: config.c -#, c-format -msgid "bad zlib compression level %d" -msgstr "nivell de compressió de zlib incorrecte %d" - -# newline → lÃnia nova o salt de lÃnia? -#: config.c -#, c-format -msgid "%s cannot contain newline" -msgstr "%s no pot contenir una nova lÃnia" - -#: config.c -#, c-format -msgid "%s must have at least one character" -msgstr "%s ha de tenir almenys un carà cter" - -#: config.c -#, c-format -msgid "ignoring unknown core.fsyncMethod value '%s'" -msgstr "s'ignora el valor desconegut «%s» de core.fsyncMethod" - -#: config.c -msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" -msgstr "core.fsyncObjectFiles és obsolet; useu core.fsync" - -#: config.c -#, c-format -msgid "invalid mode for object creation: %s" -msgstr "mode de creació d'objecte no và lid: %s" - -#: config.c -#, c-format -msgid "malformed value for %s" -msgstr "valor no và lid per a %s" - -#: config.c -#, c-format -msgid "malformed value for %s: %s" -msgstr "valor no và lid per a %s: %s" - -#: config.c -msgid "must be one of nothing, matching, simple, upstream or current" -msgstr "" -"ha de ser un dels elements següents: nothing, matching, simple, upstream o " -"current" - -#: config.c -#, c-format msgid "unable to load config blob object '%s'" msgstr "no s'ha pogut carregar l'objecte blob de configuració «%s»" @@ -19727,7 +20078,7 @@ msgstr "" #: config.c #, c-format msgid "missing value for '%s'" -msgstr "falta el valor per «%s»" +msgstr "hi manca el valor per a «%s»" #: connect.c msgid "the remote end hung up upon initial contact" @@ -20086,6 +20437,21 @@ msgstr "l'url no té esquema: %s" msgid "credential url cannot be parsed: %s" msgstr "no s'ha pogut analitzar l'URL de credencials: %s" +#: daemon.c +#, c-format +msgid "invalid timeout '%s', expecting a non-negative integer" +msgstr "el temps d'espera «%s» és invà lid; s'espera un enter no negatiu" + +#: daemon.c +#, c-format +msgid "invalid init-timeout '%s', expecting a non-negative integer" +msgstr "init-timeout invà lid «%s»; s'espera un enter no negatiu" + +#: daemon.c +#, c-format +msgid "invalid max-connections '%s', expecting an integer" +msgstr "max-connections «%s» invà lid; s'espera un enter" + #: date.c msgid "in the future" msgstr "en el futur" @@ -20189,7 +20555,7 @@ msgstr "no és và lid --%s amb valor «%s»" #: diagnose.c #, c-format msgid "could not archive missing directory '%s'" -msgstr "no s'ha pogut arxivar el directori que falta «%s»" +msgstr "no s'ha pogut arxivar el directori que hi manca «%s»" #: diagnose.c dir.c #, c-format @@ -20239,8 +20605,9 @@ msgid "cannot compare a named pipe to a directory" msgstr "no es pot comparar una canonada amb nom amb un directori" #: diff-no-index.c -msgid "git diff --no-index [<options>] <path> <path>" -msgstr "git diff --no-index [<opcions>] <camÃ> <camÃ>" +msgid "git diff --no-index [<options>] <path> <path> [<pathspec>...]" +msgstr "" +"git diff --no-index [<opcions>] <camÃ> <camÃ> [<especificacio-camÃ>...]" #: diff-no-index.c msgid "" @@ -20250,6 +20617,14 @@ msgstr "" "No és un repositori Git. Useu --no-index per a comparar dos camins fora del " "directori de treball" +#: diff-no-index.c +msgid "" +"Limiting comparison with pathspecs is only supported if both paths are " +"directories." +msgstr "" +"La comparació limitada entre especificacions de camà només s'admet si els " +"dos camins són directoris." + #: diff.c #, c-format msgid " Failed to parse dirstat cut-off percentage '%s'\n" @@ -20294,7 +20669,7 @@ msgid "Unknown value for 'diff.submodule' config variable: '%s'" msgstr "" "Valor desconegut de la variable de configuració de «diff.submodule»: «%s»" -#: diff.c merge-recursive.c transport.c +#: diff.c merge-ort.c transport.c #, c-format msgid "unknown value for config '%s': %s" msgstr "valor desconegut per al config «%s»': %s" @@ -20404,6 +20779,14 @@ msgid "invalid regex given to -I: '%s'" msgstr "expressió regular donada a -I: no và lida: «%s»" #: diff.c +msgid "-G requires a non-empty argument" +msgstr "-G requires a non-empty argument" + +#: diff.c +msgid "-S requires a non-empty argument" +msgstr "-S requereix un argument no buit" + +#: diff.c #, c-format msgid "failed to parse --submodule option parameter: '%s'" msgstr "" @@ -20426,7 +20809,7 @@ msgstr "genera el pedaç" msgid "<n>" msgstr "<n>" -#: diff.c +#: diff.c parse-options.h msgid "generate diffs with <n> lines context" msgstr "genera diffs amb <n> lÃnies de context" @@ -20574,13 +20957,13 @@ msgstr "afegir un prefix addicional per a cada lÃnia de sortida" #: diff.c msgid "do not show any source or destination prefix" -msgstr "no mostris cap prefix d'origen o destÃ" +msgstr "no mostris cap prefix d'origen o de destinació" #: diff.c msgid "use default prefixes a/ and b/" msgstr "utilitza els prefixos per defecte a/ i b/" -#: diff.c +#: diff.c parse-options.h msgid "show context between diff hunks up to the specified number of lines" msgstr "" "mostra el context entre trossos de diferència fins al nombre especificat de " @@ -20993,12 +21376,89 @@ msgstr "no s'ha pogut fer «stat» sobre el fitxer «%s»" msgid "bad git namespace path \"%s\"" msgstr "camà d'espai de noms git incorrecte «%s»" +#: environment.c +#, c-format +msgid "invalid value for variable %s" +msgstr "valor no và lid per a la variable %s" + +#: environment.c +#, c-format +msgid "ignoring unknown core.fsync component '%s'" +msgstr "s'ignora el component core.fsync «%s» desconegut" + +#: environment.c +#, c-format +msgid "abbrev length out of range: %d" +msgstr "la longitud d'«abbrev» està fora de rang: %d" + +#: environment.c +#, c-format +msgid "bad zlib compression level %d" +msgstr "nivell de compressió de zlib incorrecte %d" + +# newline → lÃnia nova o salt de lÃnia? +#: environment.c +#, c-format +msgid "%s cannot contain newline" +msgstr "%s no pot contenir una nova lÃnia" + +#: environment.c +#, c-format +msgid "%s must have at least one character" +msgstr "%s ha de tenir almenys un carà cter" + +#: environment.c +#, c-format +msgid "ignoring unknown core.fsyncMethod value '%s'" +msgstr "s'ignora el valor desconegut «%s» de core.fsyncMethod" + +#: environment.c +msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" +msgstr "core.fsyncObjectFiles és obsolet; useu core.fsync" + +#: environment.c +#, c-format +msgid "invalid mode for object creation: %s" +msgstr "mode de creació d'objecte no và lid: %s" + +#: environment.c +#, c-format +msgid "malformed value for %s" +msgstr "valor no và lid per a %s" + +#: environment.c +#, c-format +msgid "malformed value for %s: %s" +msgstr "valor no và lid per a %s: %s" + +#: environment.c +msgid "must be one of nothing, matching, simple, upstream or current" +msgstr "" +"ha de ser un dels elements següents: nothing, matching, simple, upstream o " +"current" + #: exec-cmd.c #, c-format msgid "too many args to run %s" msgstr "hi ha massa arguments per a executar %s" #: fetch-pack.c +#, c-format +msgid "" +"You are attempting to fetch %s, which is in the commit graph file but not in " +"the object database.\n" +"This is probably due to repo corruption.\n" +"If you are attempting to repair this repo corruption by refetching the " +"missing object, use 'git fetch --refetch' with the missing object." +msgstr "" +"Esteu intentant obtenir %s, el qual és en el graf de comissions però no en " +"la base de dades d'objectes\n" +"Això és degut probablement a la corrupció del repositori.\n" +"Si el que voleu és intentar reparar aquesta corrupció de repositori " +"reobtenint l'objecte que manca, useu 'git fetch --refetch' amb l'objecte que " +"manca" + +#: fetch-pack.c msgid "git fetch-pack: expected shallow list" msgstr "git fetch-pack: llista shallow esperada" @@ -21506,7 +21966,9 @@ msgstr "s'ha produït un error en escriure la clau de signatura ssh a «%s»" #: gpg-interface.c #, c-format msgid "failed writing ssh signing key buffer to '%s'" -msgstr "s'ha produït un error en escriure la clau de signatura ssh a «%s»" +msgstr "" +"s'ha produït un error en escriure la memòria intermèdia de la clau de " +"signatura ssh a «%s»" #: gpg-interface.c msgid "" @@ -21519,7 +21981,9 @@ msgstr "" #: gpg-interface.c #, c-format msgid "failed reading ssh signing data buffer from '%s'" -msgstr "s'ha produït un error en llegir la signatura ssh des de «%s»" +msgstr "" +"s'ha produït un error en llegir la memòria intermèdia de les dades de " +"signatura ssh des de «%s»" #: graph.c #, c-format @@ -21722,10 +22186,10 @@ msgstr[1] "" #, c-format msgid "" "The '%s' hook was ignored because it's not set as executable.\n" -"You can disable this warning with `git config advice.ignoredHook false`." +"You can disable this warning with `git config set advice.ignoredHook false`." msgstr "" "El lligam «%s» s'ha ignorat perquè no s'ha establert com a executable.\n" -"Podeu desactivar aquest avÃs amb «git config advice.ignoredHook false»." +"Podeu desactivar aquest avÃs amb «git config advice.ignoredHook false». " #: http-fetch.c msgid "not a git repository" @@ -21746,16 +22210,13 @@ msgid "Delegation control is not supported with cURL < 7.22.0" msgstr "No s'admet el control de delegació amb el cURL < 7.22.0" #: http.c -msgid "Public key pinning not supported with cURL < 7.39.0" -msgstr "No s'admet la fixació de clau pública amb cURL < 7.39.0" - -#: http.c msgid "Unknown value for http.proactiveauth" msgstr "Valor desconegut de http.proactiveauth" -#: http.c -msgid "CURLSSLOPT_NO_REVOKE not supported with cURL < 7.44.0" -msgstr "CURLSSLOPT_NO_REVOKE no està admès amb cURL < 7.44.0" +#: http.c parse.c +#, c-format +msgid "failed to parse %s" +msgstr "s'ha produït un error en analitzar %s" #: http.c #, c-format @@ -21859,6 +22320,35 @@ msgstr "nom d'identitat buit (per <%s>) no és permès" msgid "name consists only of disallowed characters: %s" msgstr "el nom conté només carà cters no permesos: %s" +#: imap-send.c +msgid "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>" +msgstr "" +"git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <carpeta>] < <bustia>" + +#: imap-send.c +msgid "no IMAP host specified" +msgstr "cap mà quina IMAP especificada" + +#: imap-send.c +msgid "" +"set the IMAP host with 'git config imap.host <host>'.\n" +"(e.g., 'git config imap.host imaps://imap.example.com')" +msgstr "" +"establiu la mà quina d'IMAP amb 'git config imap.host <maquina>'.\n" +"(p. ex., 'git config imap.host imaps://imap.example.com')" + +#: imap-send.c +msgid "no IMAP folder specified" +msgstr "cap carpeta IMAP especificada" + +#: imap-send.c +msgid "" +"set the target folder with 'git config imap.folder <folder>'.\n" +"(e.g., 'git config imap.folder Drafts')" +msgstr "" +"establiu la carpeta de destinació amb 'git config imap.folder <carpeta>'.\n" +"(p. ex., 'git config imap.folder Drafts')" + #: list-objects-filter-options.c msgid "expected 'tree:<depth>'" msgstr "s'esperava «tree:<profunditat>»" @@ -21986,7 +22476,17 @@ msgstr "CRLF entre cometes detectat" msgid "unable to format message: %s" msgstr "no es pot formatar el missatge: %s" -#: merge-ort.c merge-recursive.c +#: merge-ll.c +#, c-format +msgid "invalid marker-size '%s', expecting an integer" +msgstr "marker-size «%s» invà lid; s'esperava un enter" + +#: merge-ort-wrappers.c +#, c-format +msgid "Could not parse object '%s'" +msgstr "No s'ha pogut analitzar l'objecte «%s»" + +#: merge-ort.c #, c-format msgid "Failed to merge submodule %s (not checked out)" msgstr "S'ha produït un error en fusionar el submòdul %s (no està agafat)" @@ -21996,7 +22496,7 @@ msgstr "S'ha produït un error en fusionar el submòdul %s (no està agafat)" msgid "Failed to merge submodule %s (no merge base)" msgstr "S'ha produït un error en fusionar el submòdul %s (no hi ha fusió base)" -#: merge-ort.c merge-recursive.c +#: merge-ort.c #, c-format msgid "Failed to merge submodule %s (commits not present)" msgstr "S'ha produït un error en fusionar el submòdul %s (no hi ha comissions)" @@ -22007,7 +22507,7 @@ msgstr "S'ha produït un error en fusionar el submòdul %s (no hi ha comissions) msgid "error: failed to merge submodule %s (repository corrupt)" msgstr "error: no s'ha pogut fusionar el submòdul %s (repositori malmès)" -#: merge-ort.c merge-recursive.c +#: merge-ort.c #, c-format msgid "Failed to merge submodule %s (commits don't follow merge-base)" msgstr "" @@ -22052,12 +22552,12 @@ msgstr "no s'ha pogut executar la fusió interna per a %s" msgid "error: unable to add %s to database" msgstr "error: no es pot afegir %s a la base de dades" -#: merge-ort.c merge-recursive.c +#: merge-ort.c #, c-format msgid "Auto-merging %s" msgstr "S'està autofusionant %s" -#: merge-ort.c merge-recursive.c +#: merge-ort.c #, c-format msgid "" "CONFLICT (implicit dir rename): Existing file/dir at %s in the way of " @@ -22067,7 +22567,7 @@ msgstr "" "existent a %s en forma de canvi del nom del directori implÃcit, posant-hi " "els camins següents a: %s." -#: merge-ort.c merge-recursive.c +#: merge-ort.c #, c-format msgid "" "CONFLICT (implicit dir rename): Cannot map more than one path to %s; " @@ -22088,7 +22588,7 @@ msgstr "" "%s; s'han canviat de nom a múltiples altres directoris, sense una destinació " "per a la majoria dels fitxers." -#: merge-ort.c merge-recursive.c +#: merge-ort.c #, c-format msgid "" "WARNING: Avoiding applying %s -> %s rename to %s, because %s itself was " @@ -22097,7 +22597,7 @@ msgstr "" "AVÃS: S'està evitant aplicar el canvi de nom %s -> %s a %s, perquè %s ell " "mateix ja havia canviat de nom." -#: merge-ort.c merge-recursive.c +#: merge-ort.c #, c-format msgid "" "Path updated: %s added in %s inside a directory that was renamed in %s; " @@ -22106,7 +22606,7 @@ msgstr "" "Pedaç actualitzat: %s afegit a %s dins d'un directori que va canviar de nom " "a %s; movent-lo a %s." -#: merge-ort.c merge-recursive.c +#: merge-ort.c #, c-format msgid "" "Path updated: %s renamed to %s in %s, inside a directory that was renamed in " @@ -22115,7 +22615,7 @@ msgstr "" "Pedaç actualitzat: %s canviat al nom %s a %s, dins d'un directori que va " "canviar de nom a %s; movent-lo a %s." -#: merge-ort.c merge-recursive.c +#: merge-ort.c #, c-format msgid "" "CONFLICT (file location): %s added in %s inside a directory that was renamed " @@ -22124,7 +22624,7 @@ msgstr "" "CONFLICTE (ubicació del fitxer): %s afegit a %s dins d'un directori que va " "canviar de nom a %s suggerint que potser hauria de moure's a %s." -#: merge-ort.c merge-recursive.c +#: merge-ort.c #, c-format msgid "" "CONFLICT (file location): %s renamed to %s in %s, inside a directory that " @@ -22196,19 +22696,19 @@ msgstr "" "canviat el nom d'un d'ells per tal que cadascun pugui ser registrat en algun " "lloc." -#: merge-ort.c merge-recursive.c +#: merge-ort.c msgid "content" msgstr "contingut" -#: merge-ort.c merge-recursive.c +#: merge-ort.c msgid "add/add" msgstr "afegiment/afegiment" -#: merge-ort.c merge-recursive.c +#: merge-ort.c msgid "submodule" msgstr "submòdul" -#: merge-ort.c merge-recursive.c +#: merge-ort.c #, c-format msgid "CONFLICT (%s): Merge conflict in %s" msgstr "CONFLICTE (%s): Conflicte de fusió en %s" @@ -22271,329 +22771,6 @@ msgid "collecting merge info failed for trees %s, %s, %s" msgstr "" "ha fallat la recollida de la informació de fusió per als arbres %s, %s, %s" -#: merge-recursive.c -msgid "(bad commit)\n" -msgstr "(comissió errònia)\n" - -#: merge-recursive.c -#, c-format -msgid "add_cacheinfo failed for path '%s'; merge aborting." -msgstr "add_cacheinfo ha fallat per al camà «%s»; interrompent la fusió." - -#: merge-recursive.c -#, c-format -msgid "add_cacheinfo failed to refresh for path '%s'; merge aborting." -msgstr "" -"add_cacheinfo ha fallat al refrescar el camà «%s»; interrompent la fusió." - -#: merge-recursive.c -#, c-format -msgid "failed to create path '%s'%s" -msgstr "s'ha produït un error en crear el camà «%s»%s" - -#: merge-recursive.c -#, c-format -msgid "Removing %s to make room for subdirectory\n" -msgstr "S'està eliminant %s per a fer espai per al subdirectori\n" - -#: merge-recursive.c -msgid ": perhaps a D/F conflict?" -msgstr ": potser un conflicte D/F?" - -#: merge-recursive.c -#, c-format -msgid "refusing to lose untracked file at '%s'" -msgstr "s'està refusant perdre el fitxer no seguit a «%s»" - -#: merge-recursive.c -#, c-format -msgid "blob expected for %s '%s'" -msgstr "blob esperat per a %s «%s»" - -#: merge-recursive.c -#, c-format -msgid "failed to open '%s': %s" -msgstr "s'ha produït un error en obrir «%s»: %s" - -#: merge-recursive.c -#, c-format -msgid "failed to symlink '%s': %s" -msgstr "s'ha produït un error en fer l'enllaç simbòlic «%s»: %s" - -#: merge-recursive.c -#, c-format -msgid "do not know what to do with %06o %s '%s'" -msgstr "no se sap què fer amb %06o %s «%s»" - -#: merge-recursive.c -#, c-format -msgid "Failed to merge submodule %s (repository corrupt)" -msgstr "No s'ha pogut fusionar el submòdul %s (repositori malmès)" - -#: merge-recursive.c -#, c-format -msgid "Fast-forwarding submodule %s to the following commit:" -msgstr "Avançament rà pid del submòdul %s a la següent comissió:" - -#: merge-recursive.c -#, c-format -msgid "Fast-forwarding submodule %s" -msgstr "Avançament rà pid al submòdul %s" - -#: merge-recursive.c -#, c-format -msgid "Failed to merge submodule %s (merge following commits not found)" -msgstr "" -"Ha fallat en fusionar el submòdul %s (no s'ha trobat les comissions següents)" - -#: merge-recursive.c -#, c-format -msgid "Failed to merge submodule %s (not fast-forward)" -msgstr "" -"S'ha produït un error en fusionar el submòdul %s (sense avançament rà pid)" - -#: merge-recursive.c -msgid "Found a possible merge resolution for the submodule:\n" -msgstr "S'ha trobat una possible resolució de fusió pel submòdul:\n" - -#: merge-recursive.c -#, c-format -msgid "" -"If this is correct simply add it to the index for example\n" -"by using:\n" -"\n" -" git update-index --cacheinfo 160000 %s \"%s\"\n" -"\n" -"which will accept this suggestion.\n" -msgstr "" -"Si això és correcte simplement afegiu-ho a l'Ãndex per exemple\n" -"utilitzant:\n" -"\n" -" git update-index --cacheinfo 160000 %s «%s»\n" -"\n" -"que acceptarà aquest suggeriment.\n" - -#: merge-recursive.c -#, c-format -msgid "Failed to merge submodule %s (multiple merges found)" -msgstr "" -"S'ha produït un error en fusionar el submòdul %s (s'han trobat múltiples " -"fusions)" - -#: merge-recursive.c -msgid "failed to execute internal merge" -msgstr "no s'ha pogut executar la fusió interna" - -#: merge-recursive.c -#, c-format -msgid "unable to add %s to database" -msgstr "no s'ha pogut afegir %s a la base de dades" - -#: merge-recursive.c -#, c-format -msgid "Error: Refusing to lose untracked file at %s; writing to %s instead." -msgstr "" -"Error: s'està refusant perdre el fitxer no seguit a %s; en comptes s'ha " -"escrit a %s." - -#: merge-recursive.c -#, c-format -msgid "" -"CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left " -"in tree." -msgstr "" -"CONFLICTE: (%s/supressió): %s suprimit en %s i %s en %s. La versió %s de %s " -"s'ha deixat en l'arbre." - -#: merge-recursive.c -#, c-format -msgid "" -"CONFLICT (%s/delete): %s deleted in %s and %s to %s in %s. Version %s of %s " -"left in tree." -msgstr "" -"CONFLICTE: (%s/supressió): %s suprimit en %s i %s a %s en %s. La versió %s " -"de %s s'ha deixat en l'arbre." - -#: merge-recursive.c -#, c-format -msgid "" -"CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left " -"in tree at %s." -msgstr "" -"CONFLICTE: (%s/supressió): %s suprimit en %s i %s en %s. La versió %s de %s " -"s'ha deixat en l'arbre a %s." - -#: merge-recursive.c -#, c-format -msgid "" -"CONFLICT (%s/delete): %s deleted in %s and %s to %s in %s. Version %s of %s " -"left in tree at %s." -msgstr "" -"CONFLICTE: (%s/supressió): %s suprimit en %s i %s a %s en %s. La versió %s " -"de %s s'ha deixat en l'arbre a %s." - -#: merge-recursive.c -msgid "rename" -msgstr "canvi de nom" - -#: merge-recursive.c -msgid "renamed" -msgstr "canviat de nom" - -#: merge-recursive.c -#, c-format -msgid "Refusing to lose dirty file at %s" -msgstr "S'està refusant a perdre el fitxer brut a %s" - -#: merge-recursive.c -#, c-format -msgid "Refusing to lose untracked file at %s, even though it's in the way." -msgstr "" -"S'està refusant perdre el fitxer no seguit a «%s», malgrat que està en mig " -"de l'operació." - -#: merge-recursive.c -#, c-format -msgid "CONFLICT (rename/add): Rename %s->%s in %s. Added %s in %s" -msgstr "" -"CONFLICTE (canvi de nom/afegiment): Canvi de nom %s->%s a %s. S'ha afegit " -"%s a %s" - -#: merge-recursive.c -#, c-format -msgid "%s is a directory in %s adding as %s instead" -msgstr "%s és un directori en %s; s'està afegint com a %s en lloc d'això" - -#: merge-recursive.c -#, c-format -msgid "Refusing to lose untracked file at %s; adding as %s instead" -msgstr "" -"S'està refusant perdre el fitxer no seguit a %s; en comptes, s'està afegint " -"com a %s" - -#: merge-recursive.c -#, c-format -msgid "" -"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename " -"\"%s\"->\"%s\" in \"%s\"%s" -msgstr "" -"CONFLICTE (canvi de nom/canvi de nom): Canvi de nom «%s»->«%s» en la branca " -"«%s» canvi de nom «%s»->«%s» en «%s»%s" - -#: merge-recursive.c -msgid " (left unresolved)" -msgstr " (deixat sense resolució)" - -#: merge-recursive.c -#, c-format -msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s" -msgstr "" -"CONFLICTE (canvi de nom/canvi de nom): Canvi de nom %s->%s en %s. Canvi de " -"nom %s->%s en %s" - -#: merge-recursive.c -#, c-format -msgid "" -"CONFLICT (directory rename split): Unclear where to place %s because " -"directory %s was renamed to multiple other directories, with no destination " -"getting a majority of the files." -msgstr "" -"CONFLICTE (divisió de canvi de nom de directori): no està clar on col·locar " -"%s perquè el directori %s s'han canviat de nom a múltiples altres " -"directoris, sense una destinació per a la majoria dels fitxers." - -#: merge-recursive.c -#, c-format -msgid "" -"CONFLICT (rename/rename): Rename directory %s->%s in %s. Rename directory %s-" -">%s in %s" -msgstr "" -"CONFLICTE (canvi de nom/canvi de nom): canvi de nom %s->%s en %s. Canvi de " -"nom de directori %s->%s en %s" - -#: merge-recursive.c -#, c-format -msgid "cannot read object %s" -msgstr "no es pot llegir l'objecte %s" - -#: merge-recursive.c -#, c-format -msgid "object %s is not a blob" -msgstr "l'objecte %s no és un blob" - -#: merge-recursive.c -msgid "modify" -msgstr "modificació" - -#: merge-recursive.c -msgid "modified" -msgstr "modificat" - -#: merge-recursive.c -#, c-format -msgid "Skipped %s (merged same as existing)" -msgstr "S'ha omès %s (el fusionat és igual a l'existent)" - -#: merge-recursive.c -#, c-format -msgid "Adding as %s instead" -msgstr "S'està afegint com a %s en lloc d'això" - -#: merge-recursive.c -#, c-format -msgid "Removing %s" -msgstr "S'està eliminant %s" - -#: merge-recursive.c -msgid "file/directory" -msgstr "fitxer/directori" - -#: merge-recursive.c -msgid "directory/file" -msgstr "directori/fitxer" - -#: merge-recursive.c -#, c-format -msgid "CONFLICT (%s): There is a directory with name %s in %s. Adding %s as %s" -msgstr "" -"CONFLICTE (%s): Hi ha un directori amb nom %s en %s. S'està afegint %s com a " -"%s" - -#: merge-recursive.c -#, c-format -msgid "Adding %s" -msgstr "S'està afegint %s" - -#: merge-recursive.c -#, c-format -msgid "CONFLICT (add/add): Merge conflict in %s" -msgstr "CONFLICTE (afegiment/afegiment): Conflicte de fusió en %s" - -#: merge-recursive.c -#, c-format -msgid "merging of trees %s and %s failed" -msgstr "la fusió dels arbres %s i %s ha fallat" - -#: merge-recursive.c -msgid "Merging:" -msgstr "S'està fusionant:" - -#: merge-recursive.c -#, c-format -msgid "found %u common ancestor:" -msgid_plural "found %u common ancestors:" -msgstr[0] "s'ha trobat %u avantpassat en comú:" -msgstr[1] "s'han trobat %u avantpassats en comú:" - -#: merge-recursive.c -msgid "merge returned no commit" -msgstr "la fusió no ha retornat cap comissió" - -#: merge-recursive.c -#, c-format -msgid "Could not parse object '%s'" -msgstr "No s'ha pogut analitzar l'objecte «%s»" - #: merge.c msgid "failed to read the cache" msgstr "s'ha produït un error en llegir la memòria cau" @@ -22647,16 +22824,17 @@ msgid "failed to clear multi-pack-index at %s" msgstr "no s'ha pogut netejar l'Ãndex multipaquet a %s" #: midx-write.c -msgid "cannot write incremental MIDX with bitmap" -msgstr "no es pot escriure un MIDX incremental amb mapa de bits" - -#: midx-write.c msgid "ignoring existing multi-pack-index; checksum mismatch" msgstr "" "s'està ignorant l'Ãndex multipaquet existent; la suma de verificació no " "coincideix" #: midx-write.c +#, c-format +msgid "could not load reverse index for MIDX %s" +msgstr "no s'ha pogut carregar l'Ãndex invers per al MIDX %s" + +#: midx-write.c msgid "Adding packfiles to multi-pack-index" msgstr "S'estan afegint fitxers empaquetats a l'Ãndex multipaquet" @@ -22762,8 +22940,8 @@ msgstr "el fitxer de l'Ãndex multipaquet %s és massa petit" #, c-format msgid "multi-pack-index signature 0x%08x does not match signature 0x%08x" msgstr "" -"la signatura de l'Ãndex multipaquet 0x%08x no coincideix amb la signatura " -"0x%08x" +"la signatura de l'Ãndex multipaquet 0x%08x no coincideix amb la signatura 0x" +"%08x" #: midx.c #, c-format @@ -22995,83 +23173,6 @@ msgstr "No s'ha pogut convertir l'objecte de %s a %s" #: object-file.c #, c-format -msgid "object directory %s does not exist; check .git/objects/info/alternates" -msgstr "" -"no existeix el directori d'objecte %s; comproveu .git/objects/info/alternates" - -#: object-file.c -#, c-format -msgid "unable to normalize alternate object path: %s" -msgstr "no s'ha pogut normalitzar el camà a l'objecte alternatiu: %s" - -#: object-file.c -#, c-format -msgid "%s: ignoring alternate object stores, nesting too deep" -msgstr "" -"%s: s'estan ignorant els emmagatzematges alternatius d'objectes, imbricació " -"massa profunda" - -#: object-file.c -msgid "unable to fdopen alternates lockfile" -msgstr "no s'ha pogut fer «fdopen» al fitxer de bloqueig alternatiu" - -#: object-file.c -msgid "unable to read alternates file" -msgstr "no es pot llegir el fitxer «alternates»" - -#: object-file.c -msgid "unable to move new alternates file into place" -msgstr "no s'ha pogut moure el nou fitxer «alternates» al lloc" - -#: object-file.c -#, c-format -msgid "path '%s' does not exist" -msgstr "el camà «%s» no existeix" - -#: object-file.c -#, c-format -msgid "reference repository '%s' as a linked checkout is not supported yet." -msgstr "" -"encara no s'admet el repositori de referència «%s» com a agafament enllaçat." - -#: object-file.c -#, c-format -msgid "reference repository '%s' is not a local repository." -msgstr "el repositori de referència «%s» no és un repositori local." - -#: object-file.c -#, c-format -msgid "reference repository '%s' is shallow" -msgstr "el repositori de referència «%s» és superficial" - -#: object-file.c -#, c-format -msgid "reference repository '%s' is grafted" -msgstr "el repositori de referència «%s» és empeltat" - -#: object-file.c -#, c-format -msgid "could not find object directory matching %s" -msgstr "no s'ha pogut trobar el directori de l'objecte que coincideixi amb %s" - -#: object-file.c -#, c-format -msgid "invalid line while parsing alternate refs: %s" -msgstr "" -"lÃnia no và lida quan s'analitzaven les referències de l'«alternate»: %s" - -#: object-file.c -#, c-format -msgid "attempting to mmap %<PRIuMAX> over limit %<PRIuMAX>" -msgstr "s'està intentant fer mmap %<PRIuMAX> per sobre del lÃmit %<PRIuMAX>" - -#: object-file.c -#, c-format -msgid "mmap failed%s" -msgstr "mmap ha fallat%s" - -#: object-file.c -#, c-format msgid "object file %s is empty" msgstr "el tipus d'objecte %s és buit" @@ -23116,21 +23217,6 @@ msgstr "l'objecte solt %s (emmagatzemat a %s) és corrupte" #: object-file.c #, c-format -msgid "replacement %s not found for %s" -msgstr "no s'ha trobat el reemplaçament %s per a %s" - -#: object-file.c -#, c-format -msgid "packed object %s (stored in %s) is corrupt" -msgstr "l'objecte empaquetat %s (emmagatzemat a %s) és corrupte" - -#: object-file.c -#, c-format -msgid "missing mapping of %s to %s" -msgstr "manca el mapatge de %s a %s" - -#: object-file.c -#, c-format msgid "unable to open %s" msgstr "no s'ha pogut obrir %s" @@ -23146,6 +23232,11 @@ msgstr "no s'ha pogut escriure al fitxer %s" #: object-file.c #, c-format +msgid "unable to write repeatedly vanishing file %s" +msgstr "no puc escriure el fitxer %s que desapareix repetidament" + +#: object-file.c +#, c-format msgid "unable to set permission to '%s'" msgstr "no s'ha pogut establir el permÃs a «%s»" @@ -23244,11 +23335,6 @@ msgstr "%s: tipus de fitxer no suportat" #: object-file.c #, c-format -msgid "%s is not a valid '%s' object" -msgstr "%s no és un objecte de «%s» và lid" - -#: object-file.c -#, c-format msgid "hash mismatch for %s (expected %s)" msgstr "no coincideix el resum per a %s (s'esperava %s)" @@ -23269,6 +23355,11 @@ msgstr "no s'ha pogut analitzar la capçalera de %s" #: object-file.c #, c-format +msgid "unable to parse type from header '%s' of %s" +msgstr "no s'ha pogut analitzar el tipus de la capçalera «%s» de %s" + +#: object-file.c +#, c-format msgid "unable to unpack contents of %s" msgstr "no s'han pogut desempaquetar els continguts de %s" @@ -23362,7 +23453,7 @@ msgid "" "\n" "where \"$br\" is somehow empty and a 40-hex ref is created. Please\n" "examine these refs and maybe delete them. Turn this message off by\n" -"running \"git config advice.objectNameWarning false\"" +"running \"git config set advice.objectNameWarning false\"" msgstr "" "Git normalment mai crea una referència que acabi amb 40 carà cters\n" "hexadecimals perquè s'ignorarà quan només especifiqueu 40 carà cters\n" @@ -23473,6 +23564,93 @@ msgstr "no s'ha pogut analitzar l'objecte: %s" msgid "hash mismatch %s" msgstr "el resum no coincideix %s" +#: odb.c +#, c-format +msgid "object directory %s does not exist; check .git/objects/info/alternates" +msgstr "" +"no existeix el directori d'objecte %s; comproveu .git/objects/info/alternates" + +#: odb.c +#, c-format +msgid "unable to normalize alternate object path: %s" +msgstr "no s'ha pogut normalitzar el camà a l'objecte alternatiu: %s" + +#: odb.c +#, c-format +msgid "%s: ignoring alternate object stores, nesting too deep" +msgstr "" +"%s: s'estan ignorant els emmagatzematges alternatius d'objectes, imbricació " +"massa profunda" + +#: odb.c +msgid "unable to fdopen alternates lockfile" +msgstr "no s'ha pogut fer «fdopen» al fitxer de blocatge alternatiu" + +#: odb.c +msgid "unable to read alternates file" +msgstr "no es pot llegir el fitxer «alternates»" + +#: odb.c +msgid "unable to move new alternates file into place" +msgstr "no s'ha pogut moure el nou fitxer «alternates» al lloc" + +#: odb.c +#, c-format +msgid "path '%s' does not exist" +msgstr "el camà «%s» no existeix" + +#: odb.c +#, c-format +msgid "reference repository '%s' as a linked checkout is not supported yet." +msgstr "" +"encara no s'admet el repositori de referència «%s» com a agafament enllaçat." + +#: odb.c +#, c-format +msgid "reference repository '%s' is not a local repository." +msgstr "el repositori de referència «%s» no és un repositori local." + +#: odb.c +#, c-format +msgid "reference repository '%s' is shallow" +msgstr "el repositori de referència «%s» és superficial" + +#: odb.c +#, c-format +msgid "reference repository '%s' is grafted" +msgstr "el repositori de referència «%s» és empeltat" + +#: odb.c +#, c-format +msgid "could not find object directory matching %s" +msgstr "no s'ha pogut trobar el directori de l'objecte que coincideixi amb %s" + +#: odb.c +#, c-format +msgid "invalid line while parsing alternate refs: %s" +msgstr "" +"lÃnia no và lida quan s'analitzaven les referències de l'«alternate»: %s" + +#: odb.c +#, c-format +msgid "replacement %s not found for %s" +msgstr "no s'ha trobat el reemplaçament %s per a %s" + +#: odb.c +#, c-format +msgid "packed object %s (stored in %s) is corrupt" +msgstr "l'objecte empaquetat %s (emmagatzemat a %s) és corrupte" + +#: odb.c +#, c-format +msgid "missing mapping of %s to %s" +msgstr "manca el mapatge de %s a %s" + +#: odb.c +#, c-format +msgid "%s is not a valid '%s' object" +msgstr "%s no és un objecte de «%s» và lid" + #: pack-bitmap-write.c #, c-format msgid "duplicate entry when writing bitmap index: %s" @@ -23487,10 +23665,6 @@ msgstr "s'ha intentat emmagatzemar una comissió no seleccionada: «%s»" msgid "too many pseudo-merges" msgstr "massa pseudo-fusions" -#: pack-bitmap-write.c -msgid "trying to write commit not in index" -msgstr "s'està intentant no escriure la comissió a l'Ãndex" - #: pack-bitmap.c msgid "failed to load bitmap index (corrupted?)" msgstr "" @@ -23574,22 +23748,13 @@ msgstr "la suma de verificació no coincideix amb el MIDX i el mapa de bits" #: pack-bitmap.c msgid "multi-pack bitmap is missing required reverse index" -msgstr "falta l'Ãndex invers necessari al mapa de bits multipaquet" +msgstr "hi manca l'Ãndex invers necessari al mapa de bits multipaquet" #: pack-bitmap.c #, c-format msgid "could not open pack %s" msgstr "no s'ha pogut obrir el paquet %s" -#: pack-bitmap.c t/helper/test-read-midx.c -msgid "could not determine MIDX preferred pack" -msgstr "no s'ha pogut determinar el paquet preferit MIDX" - -#: pack-bitmap.c -#, c-format -msgid "preferred pack (%s) is invalid" -msgstr "el paquet preferit (%s) no és và lid" - #: pack-bitmap.c msgid "corrupt bitmap lookup table: triplet position out of index" msgstr "" @@ -23830,6 +23995,21 @@ msgstr "%s no és disponible" #: parse-options.c #, c-format +msgid "value for %s exceeds %<PRIdMAX>" +msgstr "el valor de %s supera %<PRIdMAX> " + +#: parse-options.c +#, c-format +msgid "value %s for %s not in range [%<PRIdMAX>,%<PRIdMAX>]" +msgstr "el valor %s de %s no està dins del rang [%<PRIdMAX>,%<PRIdMAX>]" + +#: parse-options.c +#, c-format +msgid "%s expects an integer value with an optional k/m/g suffix" +msgstr "%s espera un valor enter amb un sufix opcional k/m/g" + +#: parse-options.c +#, c-format msgid "%s expects a non-negative integer value with an optional k/m/g suffix" msgstr "%s espera un valor enter no negatiu amb un sufix opcional k/m/g" @@ -23867,6 +24047,55 @@ msgstr "opció «%c» desconeguda" msgid "unknown non-ascii option in string: `%s'" msgstr "opció no ascii desconeguda en la cadena: «%s»" +#. TRANSLATORS: The "<%s>" part of this string +#. stands for an optional value given to a command +#. line option in the long form, and "<>" is there +#. as a convention to signal that it is a +#. placeholder (i.e. the user should substitute it +#. with the real value). If your language uses a +#. different convention, you can change "<%s>" part +#. to match yours, e.g. it might use "|%s|" instead, +#. or if the alphabet is different enough it may use +#. "%s" without any placeholder signal. Most +#. translations leave this message as is. +#. +#: parse-options.c +#, c-format +msgid "[=<%s>]" +msgstr "[=<%s>]" + +#. TRANSLATORS: The "<%s>" part of this string +#. stands for an optional value given to a command +#. line option in the short form, and "<>" is there +#. as a convention to signal that it is a +#. placeholder (i.e. the user should substitute it +#. with the real value). If your language uses a +#. different convention, you can change "<%s>" part +#. to match yours, e.g. it might use "|%s|" instead, +#. or if the alphabet is different enough it may use +#. "%s" without any placeholder signal. Most +#. translations leave this message as is. +#. +#: parse-options.c +#, c-format +msgid "[<%s>]" +msgstr "[<%s>]" + +#. TRANSLATORS: The "<%s>" part of this string stands for a +#. value given to a command line option, and "<>" is there +#. as a convention to signal that it is a placeholder +#. (i.e. the user should substitute it with the real value). +#. If your language uses a different convention, you can +#. change "<%s>" part to match yours, e.g. it might use +#. "|%s|" instead, or if the alphabet is different enough it +#. may use "%s" without any placeholder signal. Most +#. translations leave this message as is. +#. +#: parse-options.c +#, c-format +msgid " <%s>" +msgstr " <%s>" + #: parse-options.c msgid "..." msgstr "..." @@ -23966,10 +24195,24 @@ msgstr "" msgid "bad boolean environment value '%s' for '%s'" msgstr "el valor «%s» booleà de l'entorn és incorrecte per a «%s»" -#: parse.c +#: path-walk.c #, c-format -msgid "failed to parse %s" -msgstr "s'ha produït un error en analitzar %s" +msgid "failed to walk children of tree %s: not found" +msgstr "no s'ha pogut recórrer els descendents de l'arbre %s: no s'han trobat" + +#: path-walk.c +#, c-format +msgid "failed to find object %s" +msgstr "no s'ha pogut trobar l'objecte %s" + +#: path-walk.c +#, c-format +msgid "failed to find tag %s" +msgstr "no s'ha pogut trobar l'etiqueta %s" + +#: path-walk.c +msgid "failed to setup revision walk" +msgstr "no s'ha pogut configurar un recorregut de revisió" #: path.c #, c-format @@ -24165,6 +24408,31 @@ msgstr "el nom remot «promisor» no pot començar amb «/»: %s" msgid "could not fetch %s from promisor remote" msgstr "no s'ha pogut obtenir «%s» del «promisor» remot" +#: promisor-remote.c +#, c-format +msgid "no or empty URL advertised for remote '%s'" +msgstr "no hi ha URL configurat per al remot «%s» o és buit" + +#: promisor-remote.c +#, c-format +msgid "known remote named '%s' but with URL '%s' instead of '%s'" +msgstr "remot conegut nomenat «%s» però amb URL «%s» en comptes de «%s»" + +#: promisor-remote.c +#, c-format +msgid "unknown '%s' value for '%s' config option" +msgstr "valor desconegut «%s» per a l'opció «%s» de configuració" + +#: promisor-remote.c +#, c-format +msgid "unknown element '%s' from remote info" +msgstr "valor desconegut «%s» en la informació del remot" + +#: promisor-remote.c +#, c-format +msgid "accepted promisor remote '%s' not found" +msgstr "no s'ha trobat el remot promisor acceptat «%s»" + #: protocol-caps.c msgid "object-info: expected flush after arguments" msgstr "object-info: s'esperava una neteja després dels arguments" @@ -24943,6 +25211,16 @@ msgstr "%s no apunta a un objecte và lid" #: refs.c #, c-format +msgid "%s%s will become dangling after %s is deleted\n" +msgstr " %s%s es quedarà despenjat després d'esborrar %s\n" + +#: refs.c +#, c-format +msgid "%s%s has become dangling after %s was deleted\n" +msgstr " %s%s s'ha quedat despenjat després d'esborrar %s\n" + +#: refs.c +#, c-format msgid "" "Using '%s' as the name for the initial branch. This default branch name\n" "is subject to change. To configure the initial branch name to use in all\n" @@ -25000,9 +25278,20 @@ msgid "log for %s is empty" msgstr "el registre per a %s és buit" #: refs.c -msgid "refusing to force and skip creation of reflog" +#, c-format +msgid "refusing to update reflog for pseudoref '%s'" msgstr "" -"s'ha rebutjat l'acció forçada i l'omissió de crear un registre de referències" +"s'ha rebutjat l'actualització del reflog per a la pseudoreferència «%s»" + +#: refs.c +#, c-format +msgid "refusing to update pseudoref '%s'" +msgstr "s'ha rebutjat l'actualització de la pseudoreferència «%s»" + +#: refs.c +#, c-format +msgid "refusing to update reflog with bad name '%s'" +msgstr "s'està refusant el reflog amb el nom incorrecte «%s»" #: refs.c #, c-format @@ -25010,9 +25299,9 @@ msgid "refusing to update ref with bad name '%s'" msgstr "s'està refusant la referència amb nom malmès «%s»" #: refs.c -#, c-format -msgid "refusing to update pseudoref '%s'" -msgstr "s'ha rebutjat l'actualització de la pseudoreferència «%s»" +msgid "refusing to force and skip creation of reflog" +msgstr "" +"s'ha rebutjat l'acció forçada i l'omissió de crear un registre de referències" #: refs.c #, c-format @@ -25082,6 +25371,11 @@ msgstr "" #: refs/files-backend.c #, c-format +msgid "cannot read ref file '%s'" +msgstr "no es pot llegir el fitxer de referències «%s»" + +#: refs/files-backend.c +#, c-format msgid "cannot open directory %s" msgstr "no es pot obrir el directori «%s»" @@ -25089,6 +25383,11 @@ msgstr "no es pot obrir el directori «%s»" msgid "Checking references consistency" msgstr "S'està comprovant la consistència de les referències" +#: refs/packed-backend.c +#, c-format +msgid "unable to open '%s'" +msgstr "no s'ha pogut obrir %s" + #: refs/reftable-backend.c #, c-format msgid "refname is dangerous: %s" @@ -25185,8 +25484,13 @@ msgstr "" #: refspec.c #, c-format -msgid "invalid refspec '%s'" -msgstr "refspec no và lida: «%s»" +msgid "pattern '%s' has no '*'" +msgstr "el patró «%s» no té cap «*»" + +#: refspec.c +#, c-format +msgid "replacement '%s' has no '*'" +msgstr "el reemplaçament «%s» no té cap «*»" #: remote-curl.c #, c-format @@ -25318,7 +25622,7 @@ msgstr "El transport http no admet %s" #: remote-curl.c msgid "protocol error: expected '<url> <path>', missing space" msgstr "" -"s'ha produït un error de protocol: s'esperava «<url> <camÃ>», falta espai" +"s'ha produït un error de protocol: s'esperava «<url> <camÃ>», hi manca espai" #: remote-curl.c #, c-format @@ -25348,6 +25652,31 @@ msgstr "remote-curl: ordre «%s» desconeguda del git" #: remote.c #, c-format +msgid "" +"reading remote from \"%s/%s\", which is nominated for removal.\n" +"\n" +"If you still use the \"remotes/\" directory it is recommended to\n" +"migrate to config-based remotes:\n" +"\n" +"\tgit remote rename %s %s\n" +"\n" +"If you cannot, please let us know why you still need to use it by\n" +"sending an e-mail to <git@vger.kernel.org>." +msgstr "" +"s'està llegint el remot des de «%s/%s», el qual està nominat per a la seva " +"supressió.\n" +"\n" +"Si encara feu servir el directori «remotes/», es recomana la migració a\n" +"remots basats en configuració\n" +"\n" +"\tgit remote rename %s %s\n" +"\n" +"Si no podeu, feu-nos saber, per favor, per què necessiteu fer-lo servir " +"encara\n" +"tot enviant correu electrònic a <git@vger.kernel.org>." + +#: remote.c +#, c-format msgid "config remote shorthand cannot begin with '/': %s" msgstr "" "l'abreviatura del fitxer de configuració remot no pot començar amb «/»: %s" @@ -25362,6 +25691,11 @@ msgstr "més d'un paquet de cà rrega donat, usant el primer" #: remote.c #, c-format +msgid "unrecognized followRemoteHEAD value '%s' ignored" +msgstr "valor de followRemoteHEAD no reconegut «%s» ignorat" + +#: remote.c +#, c-format msgid "unrecognized value transfer.credentialsInUrl: '%s'" msgstr "valor no conegut per a transfer.credentialsInUrl: «%s»" @@ -25387,16 +25721,6 @@ msgstr "%s segueix ambdós %s i %s" #: remote.c #, c-format -msgid "key '%s' of pattern had no '*'" -msgstr "la clau «%s» del patró no té «*»" - -#: remote.c -#, c-format -msgid "value '%s' of pattern has no '*'" -msgstr "el valor «%s» del patró no té «*»" - -#: remote.c -#, c-format msgid "src refspec %s does not match any" msgstr "l'especificació de referència font %s no coincideix amb cap referència" @@ -25431,7 +25755,8 @@ msgstr "" " és una referència «refs/{heads,tags}/». Si és aixÃ, afegim el prefix\n" " refs/{heads,tags}/ corresponent al costat remot.\n" "\n" -"Res d'això ha funcionat. Cal que proporcioneu una referència completa." +"Res d'això ha funcionat, i per això ho hem abandonat. Cal que proporcioneu " +"una referència completa." #: remote.c #, c-format @@ -25520,7 +25845,8 @@ msgstr "la branca font «%s» no s'emmagatzema com a branca amb seguiment remot" #, c-format msgid "push destination '%s' on remote '%s' has no local tracking branch" msgstr "" -"el destà de pujada «%s» en el remot «%s» no té cap branca amb seguiment remot" +"la destinació de pujada «%s» en el remot «%s» no té cap branca amb seguiment " +"remot" #: remote.c #, c-format @@ -25534,11 +25860,11 @@ msgstr "les especificacions de referència de pujada «%s» no inclouen «%s»" #: remote.c msgid "push has no destination (push.default is 'nothing')" -msgstr "push no té destà (push.default és «nothing»)" +msgstr "push no té destinació (push.default és «nothing»)" #: remote.c msgid "cannot resolve 'simple' push to a single destination" -msgstr "no es pot resoldre una pujada «simple» a un sol destÃ" +msgstr "no es pot resoldre una pujada «simple» a una sola destinació" #: remote.c #, c-format @@ -25838,8 +26164,8 @@ msgid "could not set recommended config" msgstr "no s'ha pogut establir la configuració recomanada" #: scalar.c -msgid "could not turn on maintenance" -msgstr "no s'ha pogut activar el manteniment" +msgid "could not toggle maintenance" +msgstr "no s'ha pogut commutar el manteniment" #: scalar.c msgid "could not start the FSMonitor daemon" @@ -25900,14 +26226,17 @@ msgstr "crea un repositori dins del directori «src»" msgid "specify if tags should be fetched during clone" msgstr "especifica si les etiquetes s'han d'obtenir durant el clon" -# Deixem <enlistment> sense traduir de moment +#: scalar.c +msgid "specify if background maintenance should be enabled" +msgstr "especifica si cal activar el manteniment en segon pla" + #: scalar.c msgid "" "scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n" -"\t[--[no-]src] [--[no-]tags] <url> [<enlistment>]" +"\t[--[no-]src] [--[no-]tags] [--[no-]maintenance] <url> [<enlistment>]" msgstr "" "scalar clone [--single-branch] [--branch <branca-principal>] [--full-clone]\n" -"\t[--[no-]src] [--[no-]tags] <url> [<enlistment>]" +"\t[--[no-]src] [--[no-]tags] <url> [<allistament>]" #: scalar.c #, c-format @@ -25949,27 +26278,44 @@ msgstr "no s'ha pogut configurar per a una clonació completa" #: scalar.c msgid "scalar diagnose [<enlistment>]" -msgstr "scalar diagnose [<enlistment>]" +msgstr "scalar diagnose [<allistament>]" #: scalar.c msgid "`scalar list` does not take arguments" msgstr "«scalar list» no accepta arguments" #: scalar.c -msgid "scalar register [<enlistment>]" -msgstr "scalar register [<enlistment>]" +msgid "scalar register [--[no-]maintenance] [<enlistment>]" +msgstr "scalar register [--[no-]maintenance] [<allistament>]" #: scalar.c msgid "reconfigure all registered enlistments" msgstr "reconfigura tots els allistaments registrats" #: scalar.c -msgid "scalar reconfigure [--all | <enlistment>]" -msgstr "scalar reconfigure [--all | <enlistment>]" +msgid "(enable|disable|keep)" +msgstr "(enable|disable|keep)" + +#: scalar.c +msgid "signal how to adjust background maintenance" +msgstr "assenyala com ajustar el manteniment en segon pla" + +#: scalar.c +msgid "" +"scalar reconfigure [--maintenance=(enable|disable|keep)] [--all | " +"<enlistment>]" +msgstr "" +"scalar reconfigure [--maintenance=(enable|disable|keep)] [--all | " +"<allistament>]" #: scalar.c msgid "--all or <enlistment>, but not both" -msgstr "--all o <enlistment>, però no ambdós" +msgstr "--all o <allistament>, però no ambdós" + +#: scalar.c +#, c-format +msgid "unknown mode for --maintenance option: %s" +msgstr "mode desconegut per a l'opció de --maintenance: %s" #: scalar.c #, c-format @@ -26010,7 +26356,7 @@ msgid "" "scalar run <task> [<enlistment>]\n" "Tasks:\n" msgstr "" -"scalar run <task> {<enlistment>]\n" +"scalar run <task> {<allistament>]\n" "Tasques:\n" #: scalar.c @@ -26020,11 +26366,11 @@ msgstr "no existeix la tasca: «%s»" #: scalar.c msgid "scalar unregister [<enlistment>]" -msgstr "scalar unregister [<enlistment>]" +msgstr "scalar unregister [<allistament>]" #: scalar.c msgid "scalar delete <enlistment>" -msgstr "supressió de l'escalar <enlistment>" +msgstr "supressió de l'escalar <allistament>" #: scalar.c msgid "refusing to delete current working directory" @@ -26097,27 +26443,27 @@ msgstr "" #: send-pack.c msgid "the receiving end does not support this repository's hash algorithm" -msgstr "el receptor de destà no admet l'algorisme de resum del repositori" +msgstr "el receptor de destinació no admet l'algorisme de resum del repositori" #: send-pack.c msgid "the receiving end does not support --signed push" -msgstr "el destà receptor no admet pujar --signed" +msgstr "la destinació receptora no admet pujar --signed" #: send-pack.c msgid "" "not sending a push certificate since the receiving end does not support --" "signed push" msgstr "" -"no s'està enviant una certificació de pujada perquè el destà receptor no " -"admet pujar --signed" +"no s'està enviant una certificació de pujada perquè la destinació " +"receptorano admet pujar --signed" #: send-pack.c msgid "the receiving end does not support --atomic push" -msgstr "el destà receptor no admet pujar --atomic" +msgstr "la destinació receptora no admet pujar --atomic" #: send-pack.c msgid "the receiving end does not support push options" -msgstr "el receptor al destà no admet opcions de pujada" +msgstr "el receptor a la destinació no admet opcions de pujada" #: sequencer.c #, c-format @@ -26271,15 +26617,15 @@ msgstr "variable «%s» desconeguda" #: sequencer.c msgid "missing 'GIT_AUTHOR_NAME'" -msgstr "falta «GIT_AUTHOR_NAME»" +msgstr "hi manca «GIT_AUTHOR_NAME»" #: sequencer.c msgid "missing 'GIT_AUTHOR_EMAIL'" -msgstr "falta «GIT_AUTHOR_EMAIL»" +msgstr "hi manca «GIT_AUTHOR_EMAIL»" #: sequencer.c msgid "missing 'GIT_AUTHOR_DATE'" -msgstr "falta «GIT_AUTHOR_DATE»" +msgstr "hi manca «GIT_AUTHOR_DATE»" #: sequencer.c #, c-format @@ -26413,7 +26759,7 @@ msgstr "identitat d'autor no và lida: «%s»" #: sequencer.c msgid "corrupt author: missing date information" -msgstr "autor malmès: falta la informació de la data" +msgstr "autor malmès: hi manca la informació de la data" #: sequencer.c #, c-format @@ -26602,7 +26948,7 @@ msgstr "ordre no và lida «%.*s»" #: sequencer.c #, c-format msgid "missing arguments for %s" -msgstr "falten els arguments per a %s" +msgstr "hi manquen els arguments per a %s" #: sequencer.c #, c-format @@ -26881,7 +27227,7 @@ msgid "" "Updated the following refs with %s:\n" "%s" msgstr "" -"S'han actualitzat els següents refs amb %s:\n" +"S'han actualitzat les referències següents amb %s:\n" "%s" #: sequencer.c @@ -27728,6 +28074,42 @@ msgid "number of entries in the cache tree to invalidate (default 0)" msgstr "" "nombre d'entrades a l'arbre de la memòria cau a invalidar (per defecte 0)" +#: t/helper/test-pack-deltas.c +msgid "the number of objects to write" +msgstr "el nombre d'objectes a escriure" + +#: t/helper/test-path-walk.c +msgid "test-tool path-walk <options> -- <revision-options>" +msgstr "test-tool path-walk <opcions> -- <opcions-revisio>" + +#: t/helper/test-path-walk.c +msgid "toggle inclusion of blob objects" +msgstr "commuta la inclusió d'objectes de blob" + +#: t/helper/test-path-walk.c +msgid "toggle inclusion of commit objects" +msgstr "commuta la inclusió d'objectes de comissió" + +#: t/helper/test-path-walk.c +msgid "toggle inclusion of tag objects" +msgstr "commuta la inclusió d'objectes d'etiqueta" + +#: t/helper/test-path-walk.c +msgid "toggle inclusion of tree objects" +msgstr "commuta la inclusió d'objectes d'arbre" + +#: t/helper/test-path-walk.c +msgid "toggle pruning of uninteresting paths" +msgstr "commuta la poda de camins no interessants" + +#: t/helper/test-path-walk.c +msgid "toggle aggressive edge walk" +msgstr "commuta el recorregut agressiu de vores" + +#: t/helper/test-path-walk.c +msgid "read a pattern list over stdin" +msgstr "llegeix una llista patrons de l'entrada està ndard" + #: t/helper/test-reach.c #, c-format msgid "commit %s is not marked reachable" @@ -27737,6 +28119,10 @@ msgstr "la comissió %s no està marcada com abastable" msgid "too many commits marked reachable" msgstr "hi ha massa comissions marcades com abastables" +#: t/helper/test-read-midx.c +msgid "could not determine MIDX preferred pack" +msgstr "no s'ha pogut determinar el paquet preferit MIDX" + #: t/helper/test-serve-v2.c msgid "test-tool serve-v2 [<options>]" msgstr "test-tool serve-v2 [<opcions>]" @@ -28498,6 +28884,30 @@ msgstr "error: " msgid "warning: " msgstr "avÃs: " +#: usage.c +#, c-format +msgid "" +"'%s' is nominated for removal.\n" +"If you still use this command, please add an extra\n" +"option, '--i-still-use-this', on the command line\n" +"and let us know you still use it by sending an e-mail\n" +"to <git@vger.kernel.org>. Thanks.\n" +msgstr "" +"«%s» està nominat per a la seva supressió.\n" +"Si encara feu servir aquesta ordre, afegiu-hi l'opció\n" +"addicional, «--i-still-use-this», a la lÃnia d'ordres\n" +"i feu-nos saber que encara l'useu enviant un correu electrònic\n" +"a <git@vger.kernel.org>. Grà cies.\n" + +#: usage.c +msgid "refusing to run without --i-still-use-this" +msgstr "es rebutja a executar sense --i-still-use-this" + +#: version.c +#, c-format +msgid "uname() failed with error '%s' (%d)\n" +msgstr "uname() ha fallat amb l'error «%s» (%d)\n" + #: walker.c msgid "Fetching objects" msgstr "S'estan obtenint objectes" @@ -28540,6 +28950,10 @@ msgid ".git file incorrect" msgstr "fitxer .git incorrecte" #: worktree.c +msgid ".git file absolute/relative path mismatch" +msgstr "el camins absoluts/relatius del fitxer .git no coincideixen " + +#: worktree.c msgid "not a valid path" msgstr "no és un camà và lid" @@ -28562,6 +28976,10 @@ msgid "gitdir unreadable" msgstr "gitdir illegible" #: worktree.c +msgid "gitdir absolute/relative path mismatch" +msgstr "el camins absoluts/relatius del directori git no coincideixen " + +#: worktree.c msgid "gitdir incorrect" msgstr "gitdir incorrecte" @@ -28605,6 +29023,16 @@ msgstr "no s'ha pogut desassignar %s en «%s»" msgid "failed to set extensions.worktreeConfig setting" msgstr "no s'ha pogut establir el parà metre extensions.worktreeConfig" +#: worktree.c +msgid "unable to upgrade repository format to support relative worktrees" +msgstr "" +"no s'ha pogut actualitzar el format del repositori perquè admeti arbres de " +"treball relatius" + +#: worktree.c +msgid "unable to set extensions.relativeWorktrees setting" +msgstr "no s'ha pogut establir el parà metre extensions.relativeWorktrees" + #: wrapper.c #, c-format msgid "could not setenv '%s'" @@ -28633,6 +29061,16 @@ msgstr "no s'ha pogut obtenir el directori de treball actual" msgid "unable to get random bytes" msgstr "no s'han pogut obtenir octets aleatoris" +#: wrapper.c +#, c-format +msgid "attempting to mmap %<PRIuMAX> over limit %<PRIuMAX>" +msgstr "s'està intentant fer mmap %<PRIuMAX> per sobre del lÃmit %<PRIuMAX>" + +#: wrapper.c +#, c-format +msgid "mmap failed%s" +msgstr "mmap ha fallat%s" + #: wt-status.c msgid "Unmerged paths:" msgstr "Camins sense fusionar:" @@ -29562,6 +30000,16 @@ msgstr "" #: git-send-email.perl #, perl-format +msgid "Outlook reassigned Message-ID to: %s\n" +msgstr "Outlook ha reassignat el Message-ID a %s\n" + +#: git-send-email.perl +msgid "Warning: Could not retrieve Message-ID from server response.\n" +msgstr "" +"AvÃs: no s'ha pogut recuperar el Message-ID de la resposta del servidor.\n" + +#: git-send-email.perl +#, perl-format msgid "Failed to send %s\n" msgstr "S'ha produït un error en enviar %s\n" @@ -29619,6 +30067,11 @@ msgstr "(cos) S'està afegint cc: %s des de la lÃnia «%s»\n" #: git-send-email.perl #, perl-format +msgid "error: invalid SMTP port '%s'\n" +msgstr "error: port SMTP invà lid: «%s»\n" + +#: git-send-email.perl +#, perl-format msgid "(%s) Could not execute '%s'" msgstr "(%s) no s'ha pogut executar «%s»" @@ -29681,6 +30134,367 @@ msgstr "S'està ometent %s amb el sufix de còpia de seguretat «%s».\n" msgid "Do you really want to send %s? [y|N]: " msgstr "Esteu segur que voleu enviar %s? [y|N]: " +#~ msgid "start-after" +#~ msgstr "start-after" + +#~ msgid "compact-summary" +#~ msgstr "compact-summary" + +#~ msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>" +#~ msgstr "git cat-file (-t | -s) [--allow-unknown-type] <objecte>" + +#~ msgid "allow -s and -t to work with broken/corrupt objects" +#~ msgstr "permet que -s i -t funcionin amb objectes trencats/malmesos" + +#, c-format +#~ msgid "Could not find remote branch %s to clone." +#~ msgstr "No s'ha pogut trobar la branca remota %s per a clonar." + +#, c-format +#~ msgid "" +#~ "more than %i tags found; listed %i most recent\n" +#~ "gave up search at %s\n" +#~ msgstr "" +#~ "s'han trobat més de %i etiquetes: s'han llistat les %i més recents\n" +#~ "s'ha renunciat la cerca a %s\n" + +#, c-format +#~ msgid " (%s will become dangling)" +#~ msgstr " (%s es tornarà despenjat)" + +#, c-format +#~ msgid " (%s has become dangling)" +#~ msgstr " (%s s'ha quedat despenjat)" + +#, c-format +#~ msgid "%s: object is of unknown type '%s': %s" +#~ msgstr "%s: l'objecte és de tipus desconegut «%s»: %s" + +#~ msgid "use at most one of --auto and --schedule=<frequency>" +#~ msgstr "usa com a mà xim un entre --auto i --schedule=<freqüència>" + +#, c-format +#~ msgid "Final output: %d %s\n" +#~ msgstr "Sortida final: %d %s\n" + +#, c-format +#~ msgid "merging cannot continue; got unclean result of %d" +#~ msgstr "la fusió no pot continuar; s'ha obtingut un resultat no net de %d" + +#, c-format +#~ msgid "%d (FSCK_IGNORE?) should never trigger this callback" +#~ msgstr "%d (FSCK_IGNORE?) no hauria d'activar mai aquesta crida de retorn" + +#~ msgid "" +#~ "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]" +#~ msgstr "" +#~ "git pack-objects --stdout [<opcions>] [< <ref-list> | < <object-list>]" + +#~ msgid "" +#~ "git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]" +#~ msgstr "" +#~ "git pack-objects [<opcions>] <base-name> [< <ref-list> | < <object-list>]" + +#~ msgid "cannot use --stdin-packs with --cruft" +#~ msgstr "no es pot --stdin-packs amb --cruft" + +#, c-format +#~ msgid "%s points nowhere!" +#~ msgstr "%s no apunta a enlloc" + +#~ msgid "--onto and --advance are incompatible" +#~ msgstr "--onto i --advance són incompatibles" + +#, c-format +#~ msgid "unreachable: invalid reference: %s" +#~ msgstr "no accessible: referència no và lida: %s" + +#~ msgid "Public key pinning not supported with cURL < 7.39.0" +#~ msgstr "No s'admet la fixació de clau pública amb cURL < 7.39.0" + +#~ msgid "CURLSSLOPT_NO_REVOKE not supported with cURL < 7.44.0" +#~ msgstr "CURLSSLOPT_NO_REVOKE no està admès amb cURL < 7.44.0" + +#~ msgid "(bad commit)\n" +#~ msgstr "(comissió errònia)\n" + +#, c-format +#~ msgid "add_cacheinfo failed for path '%s'; merge aborting." +#~ msgstr "add_cacheinfo ha fallat per al camà «%s»; interrompent la fusió." + +#, c-format +#~ msgid "add_cacheinfo failed to refresh for path '%s'; merge aborting." +#~ msgstr "" +#~ "add_cacheinfo ha fallat al refrescar el camà «%s»; interrompent la fusió." + +#, c-format +#~ msgid "failed to create path '%s'%s" +#~ msgstr "s'ha produït un error en crear el camà «%s»%s" + +#, c-format +#~ msgid "Removing %s to make room for subdirectory\n" +#~ msgstr "S'està eliminant %s per a fer espai per al subdirectori\n" + +#~ msgid ": perhaps a D/F conflict?" +#~ msgstr ": potser un conflicte D/F?" + +#, c-format +#~ msgid "refusing to lose untracked file at '%s'" +#~ msgstr "s'està refusant perdre el fitxer no seguit a «%s»" + +#, c-format +#~ msgid "blob expected for %s '%s'" +#~ msgstr "blob esperat per a %s «%s»" + +#, c-format +#~ msgid "failed to open '%s': %s" +#~ msgstr "s'ha produït un error en obrir «%s»: %s" + +#, c-format +#~ msgid "failed to symlink '%s': %s" +#~ msgstr "s'ha produït un error en fer l'enllaç simbòlic «%s»: %s" + +#, c-format +#~ msgid "do not know what to do with %06o %s '%s'" +#~ msgstr "no se sap què fer amb %06o %s «%s»" + +#, c-format +#~ msgid "Failed to merge submodule %s (repository corrupt)" +#~ msgstr "No s'ha pogut fusionar el submòdul %s (repositori malmès)" + +#, c-format +#~ msgid "Fast-forwarding submodule %s to the following commit:" +#~ msgstr "Avançament rà pid del submòdul %s a la següent comissió:" + +#, c-format +#~ msgid "Fast-forwarding submodule %s" +#~ msgstr "Avançament rà pid al submòdul %s" + +#, c-format +#~ msgid "Failed to merge submodule %s (merge following commits not found)" +#~ msgstr "" +#~ "Ha fallat en fusionar el submòdul %s (no s'ha trobat les comissions " +#~ "següents)" + +#, c-format +#~ msgid "Failed to merge submodule %s (not fast-forward)" +#~ msgstr "" +#~ "S'ha produït un error en fusionar el submòdul %s (sense avançament rà pid)" + +#~ msgid "Found a possible merge resolution for the submodule:\n" +#~ msgstr "S'ha trobat una possible resolució de fusió pel submòdul:\n" + +#, c-format +#~ msgid "" +#~ "If this is correct simply add it to the index for example\n" +#~ "by using:\n" +#~ "\n" +#~ " git update-index --cacheinfo 160000 %s \"%s\"\n" +#~ "\n" +#~ "which will accept this suggestion.\n" +#~ msgstr "" +#~ "Si això és correcte simplement afegiu-ho a l'Ãndex per exemple\n" +#~ "utilitzant:\n" +#~ "\n" +#~ " git update-index --cacheinfo 160000 %s «%s»\n" +#~ "\n" +#~ "que acceptarà aquest suggeriment.\n" + +#, c-format +#~ msgid "Failed to merge submodule %s (multiple merges found)" +#~ msgstr "" +#~ "S'ha produït un error en fusionar el submòdul %s (s'han trobat múltiples " +#~ "fusions)" + +#~ msgid "failed to execute internal merge" +#~ msgstr "no s'ha pogut executar la fusió interna" + +#, c-format +#~ msgid "unable to add %s to database" +#~ msgstr "no s'ha pogut afegir %s a la base de dades" + +#, c-format +#~ msgid "Error: Refusing to lose untracked file at %s; writing to %s instead." +#~ msgstr "" +#~ "Error: s'està refusant perdre el fitxer no seguit a %s; en comptes s'ha " +#~ "escrit a %s." + +#, c-format +#~ msgid "" +#~ "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s " +#~ "left in tree." +#~ msgstr "" +#~ "CONFLICTE: (%s/supressió): %s suprimit en %s i %s en %s. La versió %s de " +#~ "%s s'ha deixat en l'arbre." + +#, c-format +#~ msgid "" +#~ "CONFLICT (%s/delete): %s deleted in %s and %s to %s in %s. Version %s of " +#~ "%s left in tree." +#~ msgstr "" +#~ "CONFLICTE: (%s/supressió): %s suprimit en %s i %s a %s en %s. La versió " +#~ "%s de %s s'ha deixat en l'arbre." + +#, c-format +#~ msgid "" +#~ "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s " +#~ "left in tree at %s." +#~ msgstr "" +#~ "CONFLICTE: (%s/supressió): %s suprimit en %s i %s en %s. La versió %s de " +#~ "%s s'ha deixat en l'arbre a %s." + +#, c-format +#~ msgid "" +#~ "CONFLICT (%s/delete): %s deleted in %s and %s to %s in %s. Version %s of " +#~ "%s left in tree at %s." +#~ msgstr "" +#~ "CONFLICTE: (%s/supressió): %s suprimit en %s i %s a %s en %s. La versió " +#~ "%s de %s s'ha deixat en l'arbre a %s." + +#~ msgid "rename" +#~ msgstr "canvi de nom" + +#~ msgid "renamed" +#~ msgstr "canviat de nom" + +#, c-format +#~ msgid "Refusing to lose dirty file at %s" +#~ msgstr "S'està refusant a perdre el fitxer brut a %s" + +#, c-format +#~ msgid "Refusing to lose untracked file at %s, even though it's in the way." +#~ msgstr "" +#~ "S'està refusant perdre el fitxer no seguit a «%s», malgrat que està en " +#~ "mig de l'operació." + +#, c-format +#~ msgid "CONFLICT (rename/add): Rename %s->%s in %s. Added %s in %s" +#~ msgstr "" +#~ "CONFLICTE (canvi de nom/afegiment): Canvi de nom %s->%s a %s. S'ha " +#~ "afegit %s a %s" + +#, c-format +#~ msgid "%s is a directory in %s adding as %s instead" +#~ msgstr "%s és un directori en %s; s'està afegint com a %s en lloc d'això" + +#, c-format +#~ msgid "Refusing to lose untracked file at %s; adding as %s instead" +#~ msgstr "" +#~ "S'està refusant perdre el fitxer no seguit a %s; en comptes, s'està " +#~ "afegint com a %s" + +#, c-format +#~ msgid "" +#~ "CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename " +#~ "\"%s\"->\"%s\" in \"%s\"%s" +#~ msgstr "" +#~ "CONFLICTE (canvi de nom/canvi de nom): Canvi de nom «%s»->«%s» en la " +#~ "branca «%s» canvi de nom «%s»->«%s» en «%s»%s" + +#~ msgid " (left unresolved)" +#~ msgstr " (deixat sense resolució)" + +#, c-format +#~ msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s" +#~ msgstr "" +#~ "CONFLICTE (canvi de nom/canvi de nom): Canvi de nom %s->%s en %s. Canvi " +#~ "de nom %s->%s en %s" + +#, c-format +#~ msgid "" +#~ "CONFLICT (directory rename split): Unclear where to place %s because " +#~ "directory %s was renamed to multiple other directories, with no " +#~ "destination getting a majority of the files." +#~ msgstr "" +#~ "CONFLICTE (divisió de canvi de nom de directori): no està clar on " +#~ "col·locar %s perquè el directori %s s'han canviat de nom a múltiples " +#~ "altres directoris, sense una destinació per a la majoria dels fitxers." + +#, c-format +#~ msgid "" +#~ "CONFLICT (rename/rename): Rename directory %s->%s in %s. Rename directory " +#~ "%s->%s in %s" +#~ msgstr "" +#~ "CONFLICTE (canvi de nom/canvi de nom): canvi de nom %s->%s en %s. Canvi " +#~ "de nom de directori %s->%s en %s" + +#, c-format +#~ msgid "cannot read object %s" +#~ msgstr "no es pot llegir l'objecte %s" + +#, c-format +#~ msgid "object %s is not a blob" +#~ msgstr "l'objecte %s no és un blob" + +#~ msgid "modify" +#~ msgstr "modificació" + +#~ msgid "modified" +#~ msgstr "modificat" + +#, c-format +#~ msgid "Skipped %s (merged same as existing)" +#~ msgstr "S'ha omès %s (el fusionat és igual a l'existent)" + +#, c-format +#~ msgid "Adding as %s instead" +#~ msgstr "S'està afegint com a %s en lloc d'això" + +#, c-format +#~ msgid "Removing %s" +#~ msgstr "S'està eliminant %s" + +#~ msgid "file/directory" +#~ msgstr "fitxer/directori" + +#~ msgid "directory/file" +#~ msgstr "directori/fitxer" + +#, c-format +#~ msgid "" +#~ "CONFLICT (%s): There is a directory with name %s in %s. Adding %s as %s" +#~ msgstr "" +#~ "CONFLICTE (%s): Hi ha un directori amb nom %s en %s. S'està afegint %s " +#~ "com a %s" + +#, c-format +#~ msgid "Adding %s" +#~ msgstr "S'està afegint %s" + +#, c-format +#~ msgid "CONFLICT (add/add): Merge conflict in %s" +#~ msgstr "CONFLICTE (afegiment/afegiment): Conflicte de fusió en %s" + +#, c-format +#~ msgid "merging of trees %s and %s failed" +#~ msgstr "la fusió dels arbres %s i %s ha fallat" + +#~ msgid "Merging:" +#~ msgstr "S'està fusionant:" + +#, c-format +#~ msgid "found %u common ancestor:" +#~ msgid_plural "found %u common ancestors:" +#~ msgstr[0] "s'ha trobat %u avantpassat en comú:" +#~ msgstr[1] "s'han trobat %u avantpassats en comú:" + +#~ msgid "merge returned no commit" +#~ msgstr "la fusió no ha retornat cap comissió" + +#~ msgid "cannot write incremental MIDX with bitmap" +#~ msgstr "no es pot escriure un MIDX incremental amb mapa de bits" + +#~ msgid "trying to write commit not in index" +#~ msgstr "s'està intentant no escriure la comissió a l'Ãndex" + +#, c-format +#~ msgid "preferred pack (%s) is invalid" +#~ msgstr "el paquet preferit (%s) no és và lid" + +#, c-format +#~ msgid "key '%s' of pattern had no '*'" +#~ msgstr "la clau «%s» del patró no té «*»" + #~ msgid "revision walk setup failed\n" #~ msgstr "la configuració del recorregut de revisions ha fallat\n" @@ -29745,10 +30559,6 @@ msgstr "Esteu segur que voleu enviar %s? [y|N]: " #~ msgid "bad ls-files format: %%%.*s" #~ msgstr "format incorrecte de ls-files: %%%.*s" -#, c-format -#~ msgid "no URLs configured for remote '%s'" -#~ msgstr "cap URL configurat per al remot «%s»" - #~ msgid "keep redundant, empty commits" #~ msgstr "retén les comissions redundants i buides" @@ -87,8 +87,8 @@ msgid "" msgstr "" "Project-Id-Version: git\n" "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n" -"POT-Creation-Date: 2025-05-27 22:57+0000\n" -"PO-Revision-Date: 2025-05-29 12:54+0200\n" +"POT-Creation-Date: 2025-08-12 17:01+0000\n" +"PO-Revision-Date: 2025-08-14 19:13+0200\n" "Last-Translator: Cédric Malard <c.malard-git@valdun.net>\n" "Language-Team: Jean-Noël Avila <jn.avila@free.fr>\n" "Language: fr\n" @@ -99,6 +99,10 @@ msgstr "" "X-Generator: Poedit 3.0.1\n" #, c-format +msgid "%s cannot be negative" +msgstr "%s doit être non négatif" + +#, c-format msgid "Huh (%s)?" msgstr "Hein (%s) ?" @@ -2047,6 +2051,10 @@ msgid "adding files failed" msgstr "échec de l'ajout de fichiers" #, c-format +msgid "'%s' cannot be negative" +msgstr "'%s' doit être non négatif" + +#, c-format msgid "--chmod param '%s' must be either -x or +x" msgstr "Le paramètre '%s' de --chmod doit être soit -x soit +x" @@ -5265,24 +5273,25 @@ msgstr "" msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp] [--value=<value>] [--fixed-value] [--default=<default>] <name>" +"regexp] [--value=<pattern>] [--fixed-value] [--default=<default>] [--" +"url=<url>] <name>" msgstr "" "git config get [<option-de-fichier>] [<option-d-affichage>] [--includes] [--" -"all] [--regexp] [--value=<valeur>] [--fixed-value] [--default=<défaut>] " -"<name>" +"all] [--regexp] [--value=<motif>] [--fixed-value] [--default=<défaut>] [--" +"url=<url>] <nom>" msgid "" -"git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--" -"fixed-value] <name> <value>" +"git config set [<file-option>] [--type=<type>] [--all] [--value=<pattern>] " +"[--fixed-value] <name> <value>" msgstr "" "git config set [<option-de-fichier>] [--type=<type>] [--all] [--" -"value=<valeur>] [--fixed-value] <nom> <valeur>" +"value=<motif>] [--fixed-value] <nom> <valeur>" msgid "" -"git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] " +"git config unset [<file-option>] [--all] [--value=<pattern>] [--fixed-value] " "<name>" msgstr "" -"git config unset [<option-de-fichier>] [--all] [--value=<valeur>] [--fixed-" +"git config unset [<option-de-fichier>] [--all] [--value=<motif>] [--fixed-" "value] <nom>" msgid "git config rename-section [<file-option>] <old-name> <new-name>" @@ -5301,19 +5310,19 @@ msgstr "" msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] " +"regexp=<regexp>] [--value=<pattern>] [--fixed-value] [--default=<default>] " "<name>" msgstr "" "git config get [<option-de-fichier>] [<option-d-affichage>] [--includes] [--" -"all] [--regexp=<regexp>] [--value=<valeur>] [--fixed-value] [--" -"default=<défaut>] <name>" +"all] [--regexp=<regexp>] [--value=<motif>] [--fixed-value] [--" +"default=<défaut>] <nom>" msgid "" "git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] " -"[--value=<value>] [--fixed-value] <name> <value>" +"[--value=<pattern>] [--fixed-value] <name> <value>" msgstr "" "git config set [<option-de-fichier>] [--type=<type>] [--comment=<message>] " -"[--all] [--value=<valeur>] [--fixed-value] <nom> <valeur>" +"[--all] [--value=<motif>] [--fixed-value] <nom> <valeur>" msgid "Config file location" msgstr "Emplacement du fichier de configuration" @@ -6179,22 +6188,6 @@ msgstr "" "%s rejeté parce que les mises à jour de racines superficielles ne sont pas " "permises" -#, c-format -msgid "" -"some local refs could not be updated; try running\n" -" 'git remote prune %s' to remove any old, conflicting branches" -msgstr "" -"des références locales n'ont pas pu être mises à jour ; essayez de lancer\n" -" 'git remote prune %s' pour supprimer des branches anciennes en conflit" - -#, c-format -msgid " (%s will become dangling)" -msgstr " (%s sera en suspens)" - -#, c-format -msgid " (%s has become dangling)" -msgstr " (%s est devenu en suspens)" - msgid "[deleted]" msgstr "[supprimé]" @@ -6236,6 +6229,18 @@ msgstr "" "%s'\n" "va désactiver l'alerte jusqu'à ce que le distant change HEAD." +#, c-format +msgid "" +"some local refs could not be updated; try running\n" +" 'git remote prune %s' to remove any old, conflicting branches" +msgstr "" +"des références locales n'ont pas pu être mises à jour ; essayez de lancer\n" +" 'git remote prune %s' pour supprimer des branches anciennes en conflit" + +#, c-format +msgid "fetching ref %s failed: %s" +msgstr "échec de la récupération de %s : %s" + msgid "multiple branches detected, incompatible with --set-upstream" msgstr "branches multiples détectées, imcompatible avec --set-upstream" @@ -6483,6 +6488,9 @@ msgstr "git for-each-ref [--merged [<commit>]] [--no-merged [<commit>]]" msgid "git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]" msgstr "git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]" +msgid "git for-each-ref [--start-after <marker>]" +msgstr "git for-each-ref [--start-after <marqueur>]" + msgid "quote placeholders suitably for shells" msgstr "échapper les champs réservés pour les interpréteurs de commandes" @@ -6498,6 +6506,12 @@ msgstr "échapper les champs réservés pour compatibilité avec Tcl" msgid "show only <n> matched refs" msgstr "n'afficher que <n> références correspondant" +msgid "marker" +msgstr "marqueur" + +msgid "start iteration after the provided marker" +msgstr "commencer l'itération après le marqueur indiqué" + msgid "respect format colors" msgstr "respecter les couleurs de formatage" @@ -6522,9 +6536,16 @@ msgstr "lire les motifs de références depuis l'entrée standard" msgid "also include HEAD ref and pseudorefs" msgstr "inclure aussi la référence HEAD et les pseudo-réfs" +msgid "cannot use --start-after with custom sort options" +msgstr "" +"impossible d'utiliser --start-after avec des options de tri personnalisé" + msgid "unknown arguments supplied with --stdin" msgstr "arguments inconnus fournis avec l'option --stdin" +msgid "cannot use --start-after with patterns" +msgstr "impossible d'utiliser --start-after avec des motifs" + msgid "git for-each-repo --config=<config> [--] <arguments>" msgstr "git for-each-repo --config=<config> [--] <arguments>" @@ -6924,6 +6945,9 @@ msgstr "recompacter tous les autres paquets excepté le plus gros paquet" msgid "pack prefix to store a pack containing pruned objects" msgstr "préfixe de paquet pour stocker un paquet contenant les objets élagués" +msgid "skip maintenance tasks typically done in the foreground" +msgstr "passer les tâches de maintenance typiquement lancées en premier plan" + #, c-format msgid "failed to parse gc.logExpiry value %s" msgstr "impossible d'analyser gc.logExpiry %s" @@ -6998,14 +7022,14 @@ msgstr "" "tâche incremental-repack ignorée parce que core.multiPackIndex est désactivé" #, c-format -msgid "lock file '%s' exists, skipping maintenance" -msgstr "le fichier verrou '%s' existe, pas de maintenance" - -#, c-format msgid "task '%s' failed" msgstr "échec de la tâche '%s'" #, c-format +msgid "lock file '%s' exists, skipping maintenance" +msgstr "le fichier verrou '%s' existe, pas de maintenance" + +#, c-format msgid "'%s' is not a valid task" msgstr "'%s' n'est pas une tâche valide" @@ -7034,9 +7058,6 @@ msgstr "tâche" msgid "run a specific task" msgstr "lancer une tâche spécifique" -msgid "use at most one of --auto and --schedule=<frequency>" -msgstr "--auto et --schedule=<fréquence> sont mutuellement exclusifs" - #, c-format msgid "unable to add '%s' value of '%s'" msgstr "impossible d'ajouter la valeur '%s' de '%s'" @@ -7932,10 +7953,6 @@ msgstr "" "-L<plage>:<fichier> ne peut pas être utilisé avec une spécificateur de chemin" #, c-format -msgid "Final output: %d %s\n" -msgstr "Sortie finale : %d %s\n" - -#, c-format msgid "git show %s: bad file" msgstr "git show %s : fichier incorrect" @@ -8659,6 +8676,9 @@ msgstr "afficher un diffstat à la fin de la fusion" msgid "(synonym to --stat)" msgstr "(synonyme de --stat)" +msgid "show a compact-summary at the end of the merge" +msgstr "afficher un résumé rapide à la fin de la fusion" + msgid "add (at most <n>) entries from shortlog to merge commit message" msgstr "" "ajouter (au plus <n>) éléments du journal court au message de validation de " @@ -8932,10 +8952,6 @@ msgid "error: tag input does not pass fsck: %s" msgstr "erreur : l'entrée d'étiquette ne passe pas fsck : %s" #, c-format -msgid "%d (FSCK_IGNORE?) should never trigger this callback" -msgstr "%d (FSCK_IGNORE?) ne devrait jamais rappeler cette fonction" - -#, c-format msgid "could not read tagged object '%s'" msgstr "impossible de lire l'objet étiqueté '%s'" @@ -9456,16 +9472,27 @@ msgstr "utiliser les notes depuis <références-notes>" msgid "unknown subcommand: `%s'" msgstr "sous-commande inconnue : `%s'" -msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]" -msgstr "" -"git pack-objects --stdout [<options>] [< <liste-références> | < <liste-" -"objets>]" - msgid "" -"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]" -msgstr "" -"git pack-objects [<options>] <nom-de-base> [< <liste-références> | < <liste-" -"objets>]" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<pack-name>]\n" +" [--cruft] [--cruft-expiration=<time>]\n" +" [--stdout [--filter=<filter-spec>] | <base-name>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=<n>] [--path-walk] < <object-list>" +msgstr "" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<nom-de-" +"paquet>]\n" +" [--cruft] [--cruft-expiration=<date>]\n" +" [--stdout [--filter=<spéc-de-filtre>] | <nom-de-base>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=<n>] [--path-walk] < <liste-d-objets>" #, c-format msgid "invalid --name-hash-version option: %d" @@ -9573,6 +9600,19 @@ msgstr "impossible d'empaqueter les objets joignables depuis l'étiquette %s" msgid "unable to get type of object %s" msgstr "impossible d'obtenir le type de l'objet %s" +msgid "Compressing objects by path" +msgstr "Compression des objets par chemin" + +#, c-format +msgid "Path-based delta compression using up to %d thread" +msgid_plural "Path-based delta compression using up to %d threads" +msgstr[0] "" +"Compression par delta basé sur les chemins en utilisant jusqu'à %d fil " +"d'exécution" +msgstr[1] "" +"Compression par delta basé sur les chemins en utilisant jusqu'à %d fils " +"d'exécution" + msgid "Compressing objects" msgstr "Compression des objets" @@ -9648,6 +9688,9 @@ msgstr "l'objet libre à %s n'a pas pu être examiné" msgid "unable to force loose object" msgstr "impossible de forcer l'objet libre" +msgid "failed to pack objects via path-walk" +msgstr "échec du paquetage des objets par parcours de chemin" + #, c-format msgid "not a rev '%s'" msgstr "'%s' n'est pas une révision" @@ -9763,6 +9806,11 @@ msgstr "utiliser l'algorithme de joignabilité creuse" msgid "create thin packs" msgstr "créer des paquets légers" +msgid "use the path-walk API to walk objects when possible" +msgstr "" +"utiliser l'API de parcours de chemin pour parcourir les objets quand c'est " +"possible" + msgid "create packs suitable for shallow fetches" msgstr "créer des paquets permettant des récupérations superficielles" @@ -9821,6 +9869,10 @@ msgid "pack.deltaCacheLimit is too high, forcing %d" msgstr "pack.deltaCacheLimit est trop grand, forcé à %d" #, c-format +msgid "cannot use %s with %s" +msgstr "impossible d'utiliser %s avec %s" + +#, c-format msgid "bad pack compression level %d" msgstr "niveau de compression du paquet %d" @@ -9835,9 +9887,6 @@ msgstr "la taille limite minimale d'un paquet est 1 MiB" msgid "--thin cannot be used to build an indexable pack" msgstr "--thin ne peut pas être utilisé pour construire un paquet indexable" -msgid "cannot use --filter with --stdin-packs" -msgstr "impossible d'utiliser --filter avec --stdin-packs" - msgid "cannot use internal rev list with --stdin-packs" msgstr "" "impossible d'utiliser une liste interne de révisions avec --stdin-packs" @@ -9845,9 +9894,6 @@ msgstr "" msgid "cannot use internal rev list with --cruft" msgstr "impossible d'utiliser une liste interne de révisions avec --cruft" -msgid "cannot use --stdin-packs with --cruft" -msgstr "impossible d'utiliser --stdin-packs avec --cruft" - msgid "Enumerating objects" msgstr "Énumération des objets" @@ -9860,22 +9906,6 @@ msgstr "" "réutilisés du paquet %<PRIu32> (depuis %<PRIuMAX>)" msgid "" -"'git pack-redundant' is nominated for removal.\n" -"If you still use this command, please add an extra\n" -"option, '--i-still-use-this', on the command line\n" -"and let us know you still use it by sending an e-mail\n" -"to <git@vger.kernel.org>. Thanks.\n" -msgstr "" -"La suppression de 'git pack-redundant' est prévue.\n" -"Si vous utilisez cette commande, veuillez ajouter\n" -"une option supplémentaire, '--i-still-use-this',\n" -"sur la ligne de commande pour nous avertir par\n" -"un courriel à <git@vger.kernel.org>. Merci.\n" - -msgid "refusing to run without --i-still-use-this" -msgstr "refus de lancer sans --i-still-use-this" - -msgid "" "git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude " "<pattern>]" msgstr "" @@ -11198,6 +11228,14 @@ msgstr "" msgid "unknown --mirror argument: %s" msgstr "argument de --mirror inconnu : %s" +#, c-format +msgid "remote name '%s' is a subset of existing remote '%s'" +msgstr "le nom distance '%s' est un sous-ensemble du distant existant '%s'" + +#, c-format +msgid "remote name '%s' is a superset of existing remote '%s'" +msgstr "les nom distant '%s' est un sur-ensemble du distant existant '%s'" + msgid "fetch the remote branches" msgstr "rapatrier les branches distantes" @@ -11508,14 +11546,6 @@ msgid "Could not set up %s" msgstr "Impossible de paramétrer %s" #, c-format -msgid " %s will become dangling!" -msgstr " %s se retrouvera en suspens !" - -#, c-format -msgid " %s has become dangling!" -msgstr " %s se retrouve en suspens !" - -#, c-format msgid "Pruning %s" msgstr "Élimination de %s" @@ -11579,11 +11609,11 @@ msgstr "être verbeux : doit être placé avant une sous-commande" msgid "" "git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n" -"[--write-midx] [--name-hash-version=<n>]" +"[--write-midx] [--name-hash-version=<n>] [--path-walk]" msgstr "" "git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<nom-de-paquet>]\n" -"[--write-midx] [--name-hash-version=<n>]" +"[--write-midx] [--name-hash-version=<n>] [--path-walk]" msgid "" "Incremental repacks are incompatible with bitmap indexes. Use\n" @@ -11673,6 +11703,9 @@ msgstr "" "spécifier la verison d'empreinte de nom à utiliser pour grouper les objets " "similaires par chemin" +msgid "pass --path-walk to git-pack-objects" +msgstr "passer --path-walk à git-pack-objects" + msgid "do not run git-update-server-info" msgstr "ne pas lancer git-update-server-info" @@ -12849,17 +12882,23 @@ msgstr "" msgid "git stash create [<message>]" msgstr "git stash create [<message>]" +msgid "git stash export (--print | --to-ref <ref>) [<stash>...]" +msgstr "git stash export (--print | --to-ref <réf>) [<remise>...]" + +msgid "git stash import <commit>" +msgstr "git stash import <commit>" + #, c-format msgid "'%s' is not a stash-like commit" msgstr "'%s' n'est pas une validation de type remisage" +msgid "No stash entries found." +msgstr "Aucune entrée de remisage trouvée." + #, c-format msgid "Too many revisions specified:%s" msgstr "Trop de révisions spécifiées : %s" -msgid "No stash entries found." -msgstr "Aucune entrée de remisage trouvée." - #, c-format msgid "%s is not a valid reference" msgstr "%s n'est pas une référence valide" @@ -13014,6 +13053,69 @@ msgstr "inclure les fichiers non suivis dans la remise" msgid "include ignore files" msgstr "inclure les fichiers ignorés" +#, c-format +msgid "cannot parse commit %s" +msgstr "impossible d'analyser le commit %s" + +#, c-format +msgid "invalid author or committer for %s" +msgstr "identité d'auteur ou de valideur invalide pour %s" + +msgid "could not write commit" +msgstr "impossible d'écrire le commit" + +#, c-format +msgid "not a valid revision: %s" +msgstr "révision invalide : %s" + +#, c-format +msgid "not a commit: %s" +msgstr "pas un commit : %s" + +#, c-format +msgid "%s is not a valid exported stash commit" +msgstr "%s n'est pas un commit de remisage exporté valide" + +#, c-format +msgid "found root commit %s with invalid data" +msgstr "commit racine %s trouvé avec des données invalides" + +#, c-format +msgid "found stash commit %s without expected prefix" +msgstr "commit de remisage %s trouvé sans le préfixe attendu" + +#, c-format +msgid "cannot parse parents of commit: %s" +msgstr "impossible d'analyser les parents du commit : %s" + +#, c-format +msgid "%s does not look like a stash commit" +msgstr "%s ne ressemble pas à un commit de remisage" + +#, c-format +msgid "cannot read commit buffer for %s" +msgstr "impossible de lire le tampon de commit pour %s" + +#, c-format +msgid "cannot save the stash for %s" +msgstr "impossible de sauvegarder le remisage pour %s" + +msgid "unable to write base commit" +msgstr "impossible d'écrire le commit de base" + +#, c-format +msgid "unable to find stash entry %s" +msgstr "impossible de trouver l'entrée de remisage %s" + +msgid "print the object ID instead of writing it to a ref" +msgstr "afficher l'ID d'objet au lieu de l'écrire sur une réf" + +msgid "save the data to the given ref" +msgstr "sauvegarder la donnée sur la réf donnée" + +msgid "exactly one of --print and --to-ref is required" +msgstr "un seul des deux paramètres --print et --to-ref est requis" + msgid "skip and remove all lines starting with comment character" msgstr "" "sauter et supprimer toutes les lignes commençant par le caractère de " @@ -13023,14 +13125,6 @@ msgid "prepend comment character and space to each line" msgstr "ajouter devant chaque ligne le caractère de commentaire et un espace" #, c-format -msgid "Expecting a full ref name, got %s" -msgstr "Nom de référence complet attendu, %s obtenu" - -#, c-format -msgid "could not get a repository handle for submodule '%s'" -msgstr "impossible de trouver une poignée de dépôt pour le sous-module '%s'" - -#, c-format msgid "" "could not look up configuration '%s'. Assuming this repository is its own " "authoritative upstream." @@ -13039,6 +13133,10 @@ msgstr "" "son propre amont d'autorité." #, c-format +msgid "could not get a repository handle for submodule '%s'" +msgstr "impossible de trouver une poignée de dépôt pour le sous-module '%s'" + +#, c-format msgid "No url found for submodule path '%s' in .gitmodules" msgstr "URL non trouvée pour le chemin de sous-module '%s' dans .gitmodules" @@ -13400,6 +13498,10 @@ msgstr "" "super-projet, mais le super-projet n'est sur aucune branche" #, c-format +msgid "Expecting a full ref name, got %s" +msgstr "Nom de référence complet attendu, %s obtenu" + +#, c-format msgid "Unable to find current revision in submodule path '%s'" msgstr "" "Impossible de trouver la révision actuelle dans le chemin de sous-module '%s'" @@ -13605,6 +13707,10 @@ msgid "repo URL: '%s' must be absolute or begin with ./|../" msgstr "l'URL de dépôt : '%s' doit être absolu ou commencer par ./|../" #, c-format +msgid "submodule name '%s' already used for path '%s'" +msgstr "Sous-module '%s' déjà utilisé pour le chemin '%s'" + +#, c-format msgid "'%s' is not a valid submodule name" msgstr "'%s' n'est pas un nom valide de sous-module" @@ -14251,10 +14357,6 @@ msgid "Preparing worktree (checking out '%s')" msgstr "Préparation de l'arbre de travail (extraction de '%s')" #, c-format -msgid "unreachable: invalid reference: %s" -msgstr "non joignable : référence invalide : %s" - -#, c-format msgid "Preparing worktree (detached HEAD %s)" msgstr "Préparation de l'arbre de travail (HEAD détachée %s)" @@ -15930,14 +16032,6 @@ msgstr "" "valeur numérique de configuration incorrecte '%s' pour '%s' dans %s : %s" #, c-format -msgid "invalid value for variable %s" -msgstr "valeur invalide pour la variable %s" - -#, c-format -msgid "ignoring unknown core.fsync component '%s'" -msgstr "ignore le composant core.fsync inconne '%s'" - -#, c-format msgid "bad boolean config value '%s' for '%s'" msgstr "valeur booléenne de configuration invalide '%s' pour '%s'" @@ -15950,44 +16044,6 @@ msgid "'%s' for '%s' is not a valid timestamp" msgstr "'%s' pour '%s' n'est pas un horodatage valide" #, c-format -msgid "abbrev length out of range: %d" -msgstr "longueur d'abbrev hors plage : %d" - -#, c-format -msgid "bad zlib compression level %d" -msgstr "niveau de compression zlib incorrect %d" - -#, c-format -msgid "%s cannot contain newline" -msgstr "%s ne peut pas contenir de retour à la ligne" - -#, c-format -msgid "%s must have at least one character" -msgstr "%s doit contenir au moins un caractère" - -#, c-format -msgid "ignoring unknown core.fsyncMethod value '%s'" -msgstr "valeur inconnue '%s' de core.fsyncMethod" - -msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" -msgstr "core.fsyncObjectFiles est obsolète ; utilisez core.fsync à la place" - -#, c-format -msgid "invalid mode for object creation: %s" -msgstr "mode invalide pour la création d'objet : %s" - -#, c-format -msgid "malformed value for %s" -msgstr "valeur mal formée pour %s" - -#, c-format -msgid "malformed value for %s: %s" -msgstr "valeur mal formée pour %s : %s" - -msgid "must be one of nothing, matching, simple, upstream or current" -msgstr "doit être parmi nothing, matching, simple, upstream ou current" - -#, c-format msgid "unable to load config blob object '%s'" msgstr "impossible de charger l'objet blob de config '%s'" @@ -16512,8 +16568,9 @@ msgstr "impossible de comparer stdin à un répertoire" msgid "cannot compare a named pipe to a directory" msgstr "impossible de réparer un tuyau nommé à un répertoire" -msgid "git diff --no-index [<options>] <path> <path>" -msgstr "git diff --no-index [<options>] <chemin> <chemin>" +msgid "git diff --no-index [<options>] <path> <path> [<pathspec>...]" +msgstr "" +"git diff --no-index [<options>] <chemin> <chemin> [<spéc-de-chemin>...]" msgid "" "Not a git repository. Use --no-index to compare two paths outside a working " @@ -16522,6 +16579,13 @@ msgstr "" "Pas un dépôt git. Utilisez --no-index pour comparer deux chemins hors d'un " "arbre de travail" +msgid "" +"Limiting comparison with pathspecs is only supported if both paths are " +"directories." +msgstr "" +"La limitation de comparaison entre spécificateurs de chemins n'est prise en " +"charge que si les deux chemins sont des répertoires." + #, c-format msgid " Failed to parse dirstat cut-off percentage '%s'\n" msgstr "" @@ -17132,6 +17196,52 @@ msgid "bad git namespace path \"%s\"" msgstr "espaces de nom de Git \"%s\"" #, c-format +msgid "invalid value for variable %s" +msgstr "valeur invalide pour la variable %s" + +#, c-format +msgid "ignoring unknown core.fsync component '%s'" +msgstr "ignore le composant core.fsync inconne '%s'" + +#, c-format +msgid "abbrev length out of range: %d" +msgstr "longueur d'abbrev hors plage : %d" + +#, c-format +msgid "bad zlib compression level %d" +msgstr "niveau de compression zlib incorrect %d" + +#, c-format +msgid "%s cannot contain newline" +msgstr "%s ne peut pas contenir de retour à la ligne" + +#, c-format +msgid "%s must have at least one character" +msgstr "%s doit contenir au moins un caractère" + +#, c-format +msgid "ignoring unknown core.fsyncMethod value '%s'" +msgstr "valeur inconnue '%s' de core.fsyncMethod" + +msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" +msgstr "core.fsyncObjectFiles est obsolète ; utilisez core.fsync à la place" + +#, c-format +msgid "invalid mode for object creation: %s" +msgstr "mode invalide pour la création d'objet : %s" + +#, c-format +msgid "malformed value for %s" +msgstr "valeur mal formée pour %s" + +#, c-format +msgid "malformed value for %s: %s" +msgstr "valeur mal formée pour %s : %s" + +msgid "must be one of nothing, matching, simple, upstream or current" +msgstr "doit être parmi nothing, matching, simple, upstream ou current" + +#, c-format msgid "too many args to run %s" msgstr "trop d'arguments pour lancer %s" @@ -17849,6 +17959,30 @@ msgstr "nom d'identifiant vide (pour <%s>) non permis" msgid "name consists only of disallowed characters: %s" msgstr "le nom n'est constitué que de caractères interdits : %s" +msgid "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>" +msgstr "" +"git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <répertoire>] < <mbox>" + +msgid "no IMAP host specified" +msgstr "serveur IMAP non spécifié" + +msgid "" +"set the IMAP host with 'git config imap.host <host>'.\n" +"(e.g., 'git config imap.host imaps://imap.example.com')" +msgstr "" +"régler l'hôte IMAP avec 'git config imap.host <hôte>'\n" +"(par ex., 'git config imap.host imaps://imap.example.com')" + +msgid "no IMAP folder specified" +msgstr "dossier IMAP non spécifié" + +msgid "" +"set the target folder with 'git config imap.folder <folder>'.\n" +"(e.g., 'git config imap.folder Drafts')" +msgstr "" +"régler le dossier cible avec 'git config imap.folder <dossier>'\"\n" +"(par ex., 'git config imap.folder Drafts')" + msgid "expected 'tree:<depth>'" msgstr "attendu : 'tree:<profondeur>'" @@ -18656,7 +18790,7 @@ msgstr "%s : type de fichier non supporté" #, c-format msgid "hash mismatch for %s (expected %s)" -msgstr "incohérence de hachage pour %s (%s attendu)" +msgstr "incohérence d'empreinte pour %s (%s attendu)" #, c-format msgid "unable to mmap %s" @@ -18835,6 +18969,26 @@ msgid "invalid object name '%.*s'." msgstr "nom d'objet invalide : '%.*s'." #, c-format +msgid "invalid object type \"%s\"" +msgstr "type d'objet invalide \"%s\"" + +#, c-format +msgid "object %s is a %s, not a %s" +msgstr "l'objet %s est de type %s, pas de type %s" + +#, c-format +msgid "object %s has unknown type id %d" +msgstr "l'objet %s a un id de type inconnu %d" + +#, c-format +msgid "unable to parse object: %s" +msgstr "impossible d'analyser l'objet : %s" + +#, c-format +msgid "hash mismatch %s" +msgstr "incohérence d'empreinte %s" + +#, c-format msgid "object directory %s does not exist; check .git/objects/info/alternates" msgstr "" "le répertoire objet %s n'existe pas ; vérifiez .git/objects/info/alternates" @@ -18903,26 +19057,6 @@ msgid "%s is not a valid '%s' object" msgstr "%s n'est pas un objet '%s' valide" #, c-format -msgid "invalid object type \"%s\"" -msgstr "type d'objet invalide \"%s\"" - -#, c-format -msgid "object %s is a %s, not a %s" -msgstr "l'objet %s est de type %s, pas de type %s" - -#, c-format -msgid "object %s has unknown type id %d" -msgstr "l'objet %s a un id de type inconnu %d" - -#, c-format -msgid "unable to parse object: %s" -msgstr "impossible d'analyser l'objet : %s" - -#, c-format -msgid "hash mismatch %s" -msgstr "incohérence de hachage %s" - -#, c-format msgid "duplicate entry when writing bitmap index: %s" msgstr "entrée dupliquée dans l'index en bitmap : '%s'" @@ -18933,9 +19067,6 @@ msgstr "essai de stockage d'un commit non-sélectionné : '%s'" msgid "too many pseudo-merges" msgstr "trop de pseudo-fusions" -msgid "trying to write commit not in index" -msgstr "échec de l'écriture de l'objet commit absent de l'index" - msgid "failed to load bitmap index (corrupted?)" msgstr "échec du chargement de l'index en bitmap (corruption ?)" @@ -19187,6 +19318,10 @@ msgid "%s isn't available" msgstr "%s n'est pas disponible" #, c-format +msgid "value for %s exceeds %<PRIdMAX>" +msgstr "la valeur pour %s est supérieure à %<PRIdMAX>" + +#, c-format msgid "value %s for %s not in range [%<PRIdMAX>,%<PRIdMAX>]" msgstr "valeur %s pour %s pas dans la plage [%<PRIdMAX>,%<PRIdMAX>]" @@ -20178,6 +20313,14 @@ msgid "%s does not point to a valid object!" msgstr "%s ne pointe pas sur un objet valide!" #, c-format +msgid "%s%s will become dangling after %s is deleted\n" +msgstr "%s%s se retrouvera en suspens après que %s est effacé\n" + +#, c-format +msgid "%s%s has become dangling after %s was deleted\n" +msgstr "%s%s est en suspens depuis que %s a été effacé\n" + +#, c-format msgid "" "Using '%s' as the name for the initial branch. This default branch name\n" "is subject to change. To configure the initial branch name to use in all\n" @@ -22530,6 +22673,9 @@ msgstr "activer l'inclusion des objets arbre" msgid "toggle pruning of uninteresting paths" msgstr "activer l'élagage des chemins inintéressants" +msgid "toggle aggressive edge walk" +msgstr "activer le parcours agressif des arêtes" + msgid "read a pattern list over stdin" msgstr "lire les motifs depuis stdin" @@ -23180,6 +23326,23 @@ msgid "warning: " msgstr "avertissement : " #, c-format +msgid "" +"'%s' is nominated for removal.\n" +"If you still use this command, please add an extra\n" +"option, '--i-still-use-this', on the command line\n" +"and let us know you still use it by sending an e-mail\n" +"to <git@vger.kernel.org>. Thanks.\n" +msgstr "" +"La suppression de '%s' est prévue.\n" +"Si vous utilisez cette commande, veuillez ajouter\n" +"une option supplémentaire, '--i-still-use-this',\n" +"sur la ligne de commande pour nous avertir par\n" +"un courriel à <git@vger.kernel.org>. Merci.\n" + +msgid "refusing to run without --i-still-use-this" +msgstr "refus de lancer sans --i-still-use-this" + +#, c-format msgid "uname() failed with error '%s' (%d)\n" msgstr "échec de uname() avec l'erreur '%s' (%d)\n" @@ -24124,6 +24287,10 @@ msgid "(body) Adding cc: %s from line '%s'\n" msgstr "(corps) Ajout de cc: %s depuis la ligne '%s'\n" #, perl-format +msgid "error: invalid SMTP port '%s'\n" +msgstr "erreur : port SMTP invalide '%s'\n" + +#, perl-format msgid "(%s) Could not execute '%s'" msgstr "(%s) Impossible d'exécuter '%s'" @@ -24176,293 +24343,8 @@ msgstr "%s sauté avec un suffix de sauvegarde '%s'.\n" msgid "Do you really want to send %s? [y|N]: " msgstr "Souhaitez-vous réellement envoyer %s ?[y|N] : " -#~ msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>" -#~ msgstr "git cat-file (-t | -s) [--allow-unknown-type] <objet>" - -#~ msgid "allow -s and -t to work with broken/corrupt objects" -#~ msgstr "autoriser -s et -t à travailler sur des objets cassés/corrompus" +#~ msgid "start-after" +#~ msgstr "start-after" -#, c-format -#~ msgid "%s: object is of unknown type '%s': %s" -#~ msgstr "%s : l'objet a un type '%s' inconnu : %s" - -#, c-format -#~ msgid "%s points nowhere!" -#~ msgstr "%s ne pointe nulle part !" - -#~ msgid "(bad commit)\n" -#~ msgstr "(mauvais commit)\n" - -#, c-format -#~ msgid "add_cacheinfo failed for path '%s'; merge aborting." -#~ msgstr "échec de add_cacheinfo pour le chemin '%s' ; abandon de la fusion." - -#, c-format -#~ msgid "add_cacheinfo failed to refresh for path '%s'; merge aborting." -#~ msgstr "échec de add_cacheinfo pour le chemin '%s' ; abandon de la fusion." - -#, c-format -#~ msgid "failed to create path '%s'%s" -#~ msgstr "impossible de créer le chemin '%s' %s" - -#, c-format -#~ msgid "Removing %s to make room for subdirectory\n" -#~ msgstr "Suppression de %s pour faire de la place pour le sous-répertoire\n" - -#~ msgid ": perhaps a D/F conflict?" -#~ msgstr ": peut-être un conflit D/F ?" - -#, c-format -#~ msgid "refusing to lose untracked file at '%s'" -#~ msgstr "refus de perdre le fichier non suivi '%s'" - -#, c-format -#~ msgid "blob expected for %s '%s'" -#~ msgstr "blob attendu pour %s '%s'" - -#, c-format -#~ msgid "failed to open '%s': %s" -#~ msgstr "échec à l'ouverture de '%s' : %s" - -#, c-format -#~ msgid "failed to symlink '%s': %s" -#~ msgstr "échec à la création du lien symbolique '%s' : %s" - -#, c-format -#~ msgid "do not know what to do with %06o %s '%s'" -#~ msgstr "ne sait pas traiter %06o %s '%s'" - -#, c-format -#~ msgid "Failed to merge submodule %s (repository corrupt)" -#~ msgstr "Échec de la fusion du sous-module %s (dépôt corrompu)" - -#, c-format -#~ msgid "Fast-forwarding submodule %s to the following commit:" -#~ msgstr "Avance rapide du sous-module %s au commit suivant :" - -#, c-format -#~ msgid "Fast-forwarding submodule %s" -#~ msgstr "Avance rapide du sous-module %s" - -#, c-format -#~ msgid "Failed to merge submodule %s (merge following commits not found)" -#~ msgstr "" -#~ "Échec de fusion du sous-module %s (fusion suivant les commits non trouvée)" - -#, c-format -#~ msgid "Failed to merge submodule %s (not fast-forward)" -#~ msgstr "Échec de fusion du sous-module %s (pas en avance rapide)" - -#~ msgid "Found a possible merge resolution for the submodule:\n" -#~ msgstr "Résolution possible de fusion trouvée pour le sous-module :\n" - -#, c-format -#~ msgid "" -#~ "If this is correct simply add it to the index for example\n" -#~ "by using:\n" -#~ "\n" -#~ " git update-index --cacheinfo 160000 %s \"%s\"\n" -#~ "\n" -#~ "which will accept this suggestion.\n" -#~ msgstr "" -#~ "Si c'est correct, ajoutez le simplement à l'index\n" -#~ "en utilisant par exemple :\n" -#~ "\n" -#~ " git update-index --cacheinfo 160000 %s \"%s\"\n" -#~ "\n" -#~ "qui acceptera cette suggestion.\n" - -#, c-format -#~ msgid "Failed to merge submodule %s (multiple merges found)" -#~ msgstr "Échec de fusion du sous-module %s (plusieurs fusions trouvées)" - -#~ msgid "failed to execute internal merge" -#~ msgstr "échec à l'exécution de la fusion interne" - -#, c-format -#~ msgid "unable to add %s to database" -#~ msgstr "impossible d'ajouter %s à la base de données" - -#, c-format -#~ msgid "Error: Refusing to lose untracked file at %s; writing to %s instead." -#~ msgstr "" -#~ "Erreur : refus de perdre le fichier non suivi %s ; écriture dans %s à la " -#~ "place." - -#, c-format -#~ msgid "" -#~ "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s " -#~ "left in tree." -#~ msgstr "" -#~ "CONFLIT (%s/suppression) : %s supprimé dans %s et %s dans %s. Version %s " -#~ "de %s laissée dans l'arbre." - -#, c-format -#~ msgid "" -#~ "CONFLICT (%s/delete): %s deleted in %s and %s to %s in %s. Version %s of " -#~ "%s left in tree." -#~ msgstr "" -#~ "CONFLIT (%s/suppression) : %s supprimé dans %s et %s à %s dans %s. " -#~ "Version %s de %s laissée dans l'arbre." - -#, c-format -#~ msgid "" -#~ "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s " -#~ "left in tree at %s." -#~ msgstr "" -#~ "CONFLIT (%s/suppression) : %s supprimé dans %s et %s dans %s. Version %s " -#~ "de %s laissée dans l'arbre dans le fichier %s." - -#, c-format -#~ msgid "" -#~ "CONFLICT (%s/delete): %s deleted in %s and %s to %s in %s. Version %s of " -#~ "%s left in tree at %s." -#~ msgstr "" -#~ "CONFLIT (%s/suppression) : %s supprimé dans %s et %s à %s dans %s. " -#~ "Version %s de %s laissée dans l'arbre dans le fichier %s." - -#~ msgid "rename" -#~ msgstr "renommage" - -#~ msgid "renamed" -#~ msgstr "renommé" - -#, c-format -#~ msgid "Refusing to lose dirty file at %s" -#~ msgstr "Refus de perdre le fichier modifié %s" - -#, c-format -#~ msgid "Refusing to lose untracked file at %s, even though it's in the way." -#~ msgstr "Refus de perdre le fichier non suivi %s, même s'il gêne." - -#, c-format -#~ msgid "CONFLICT (rename/add): Rename %s->%s in %s. Added %s in %s" -#~ msgstr "" -#~ "CONFLIT (renommage/ajout) : Renommage de %s->%s dans %s. %s ajouté dans %s" - -#, c-format -#~ msgid "%s is a directory in %s adding as %s instead" -#~ msgstr "%s est un répertoire dans %s ajouté plutôt comme %s" - -#, c-format -#~ msgid "Refusing to lose untracked file at %s; adding as %s instead" -#~ msgstr "Refus de perdre le fichier non suivi %s ; ajout comme %s à la place" - -#, c-format -#~ msgid "" -#~ "CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename " -#~ "\"%s\"->\"%s\" in \"%s\"%s" -#~ msgstr "" -#~ "CONFLIT (renommage/renommage) : Renommage de \"%s\"->\"%s\" dans la " -#~ "branche \"%s\" et renommage \"%s\"->\"%s\" dans \"%s\"%s" - -#~ msgid " (left unresolved)" -#~ msgstr " (laissé non résolu)" - -#, c-format -#~ msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s" -#~ msgstr "" -#~ "CONFLIT (renommage/renommage) : renommage '%s'->'%s' dans %s. Renommage " -#~ "'%s'->'%s' dans %s" - -#, c-format -#~ msgid "" -#~ "CONFLICT (directory rename split): Unclear where to place %s because " -#~ "directory %s was renamed to multiple other directories, with no " -#~ "destination getting a majority of the files." -#~ msgstr "" -#~ "CONFLIT (renommage de répertoire coupé) : la place de %s n'est pas claire " -#~ "parce que le répertoire %s a été renommé en plusieurs autres répertoires, " -#~ "sans aucune destination récupérant la majorité des fichiers." - -#, c-format -#~ msgid "" -#~ "CONFLICT (rename/rename): Rename directory %s->%s in %s. Rename directory " -#~ "%s->%s in %s" -#~ msgstr "" -#~ "CONFLIT (renommage/renommage) : renommage du répertoire %s->%s dans %s. " -#~ "Renommage de répertoire %s->%s dans %s" - -#, c-format -#~ msgid "cannot read object %s" -#~ msgstr "impossible de lire l'objet %s" - -#, c-format -#~ msgid "object %s is not a blob" -#~ msgstr "l'objet %s n'est pas un blob" - -#~ msgid "modify" -#~ msgstr "modification" - -#~ msgid "modified" -#~ msgstr "modifié" - -#, c-format -#~ msgid "Skipped %s (merged same as existing)" -#~ msgstr "%s sauté (fusion identique à l'existant)" - -#, c-format -#~ msgid "Adding as %s instead" -#~ msgstr "Ajout plutôt comme %s" - -#, c-format -#~ msgid "Removing %s" -#~ msgstr "Suppression de %s" - -#~ msgid "file/directory" -#~ msgstr "fichier/répertoire" - -#~ msgid "directory/file" -#~ msgstr "répertoire/fichier" - -#, c-format -#~ msgid "" -#~ "CONFLICT (%s): There is a directory with name %s in %s. Adding %s as %s" -#~ msgstr "" -#~ "CONFLIT (%s) : Il y a un répertoire nommé %s dans %s. Ajout de %s comme %s" - -#, c-format -#~ msgid "Adding %s" -#~ msgstr "Ajout de %s" - -#, c-format -#~ msgid "CONFLICT (add/add): Merge conflict in %s" -#~ msgstr "CONFLIT (ajout/ajout) : Conflit de fusion dans %s" - -#, c-format -#~ msgid "merging of trees %s and %s failed" -#~ msgstr "échec de fusion des arbres %s et %s" - -#~ msgid "Merging:" -#~ msgstr "Fusion :" - -#, c-format -#~ msgid "found %u common ancestor:" -#~ msgid_plural "found %u common ancestors:" -#~ msgstr[0] "%u ancêtre commun trouvé :" -#~ msgstr[1] "%u ancêtres communs trouvés :" - -#~ msgid "merge returned no commit" -#~ msgstr "la fusion n'a pas retourné de commit" - -#~ msgid "cannot write incremental MIDX with bitmap" -#~ msgstr "impossible d'écrire un MIDX incrémental avec des bitmap" - -#, c-format -#~ msgid "Could not find remote branch %s to clone." -#~ msgstr "Impossible de trouver la branche distante '%s' à cloner." - -#, c-format -#~ msgid "merging cannot continue; got unclean result of %d" -#~ msgstr "la fusion ne peut pas continuer ; résultat non propre retourné %d" - -#~ msgid "--onto and --advance are incompatible" -#~ msgstr "--onto et --advance sont incompatibles" - -#, c-format -#~ msgid "key '%s' of pattern had no '*'" -#~ msgstr "la clé '%s' du modèle n'a pas de '*'" - -#, c-format -#~ msgid "preferred pack (%s) is invalid" -#~ msgstr "le paquet préféré (%s) est invalide" +#~ msgid "compact-summary" +#~ msgstr "résumé-compact" @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: Git\n" "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n" -"POT-Creation-Date: 2025-05-27 22:57+0000\n" -"PO-Revision-Date: 2025-06-07 08:26+0700\n" +"POT-Creation-Date: 2025-08-12 17:01+0000\n" +"PO-Revision-Date: 2025-08-15 17:33+0700\n" "Last-Translator: Bagas Sanjaya <bagasdotme@gmail.com>\n" "Language-Team: Indonesian\n" "Language: id\n" @@ -19,6 +19,11 @@ msgstr "" #: add-interactive.c #, c-format +msgid "%s cannot be negative" +msgstr "%s tidak boleh negatif" + +#: add-interactive.c +#, c-format msgid "Huh (%s)?" msgstr "Huh (%s)?" @@ -950,10 +955,10 @@ msgstr "opsi abai spasi putih tidak dikenal '%s'" #: builtin/describe.c builtin/diff-tree.c builtin/difftool.c #: builtin/fast-export.c builtin/fetch.c builtin/help.c builtin/index-pack.c #: builtin/init-db.c builtin/log.c builtin/ls-files.c builtin/merge-base.c -#: builtin/merge-tree.c builtin/merge.c builtin/pack-objects.c builtin/rebase.c -#: builtin/repack.c builtin/replay.c builtin/reset.c builtin/rev-parse.c -#: builtin/show-branch.c builtin/stash.c builtin/submodule--helper.c -#: builtin/tag.c builtin/worktree.c parse-options.c range-diff.c revision.c +#: builtin/merge-tree.c builtin/merge.c builtin/rebase.c builtin/repack.c +#: builtin/replay.c builtin/reset.c builtin/rev-parse.c builtin/show-branch.c +#: builtin/stash.c builtin/submodule--helper.c builtin/tag.c builtin/worktree.c +#: parse-options.c range-diff.c revision.c #, c-format msgid "options '%s' and '%s' cannot be used together" msgstr "Opsi '%s' dan '%s' tidak dapat digunakan bersamaan" @@ -2369,6 +2374,12 @@ msgstr "Gunakan -f jika Anda benar-benar ingin menambahkannya." msgid "adding files failed" msgstr "gagal menambahkan berkas" +#: builtin/add.c builtin/checkout.c builtin/commit.c builtin/reset.c +#: builtin/stash.c +#, c-format +msgid "'%s' cannot be negative" +msgstr "'%s' tidak boleh negatif" + #: builtin/add.c #, c-format msgid "--chmod param '%s' must be either -x or +x" @@ -2407,7 +2418,7 @@ msgid "bad action '%s' for '%s'" msgstr "tindakan jelek '%s' untuk '%s'" #: builtin/am.c builtin/blame.c builtin/fetch.c builtin/pack-objects.c -#: builtin/pull.c builtin/revert.c config.c diff-merges.c gpg-interface.c +#: builtin/pull.c builtin/revert.c diff-merges.c environment.c gpg-interface.c #: ls-refs.c parallel-checkout.c sequencer.c setup.c #, c-format msgid "invalid value for '%s': '%s'" @@ -3901,8 +3912,8 @@ msgstr "" "Mohon tinjau sisa laporan bug di bawah ini.\n" "Anda dapat menghapus baris-baris yang Anda tidak ingin dibagi.\n" -#: builtin/bugreport.c builtin/commit.c builtin/fast-export.c builtin/rebase.c -#: parse-options.h +#: builtin/bugreport.c builtin/commit.c builtin/fast-export.c +#: builtin/pack-objects.c builtin/rebase.c parse-options.h msgid "mode" msgstr "mode" @@ -6350,25 +6361,27 @@ msgstr "git config list [<opsi berkas>] [<opsi tampilan>] [--includes]" #: builtin/config.c msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp] [--value=<value>] [--fixed-value] [--default=<default>] <name>" +"regexp] [--value=<pattern>] [--fixed-value] [--default=<default>] [--" +"url=<url>] <name>" msgstr "" "git config get [<opsi berkas>] [<opsi tampilan>] [--includes] [--all] [--" -"regexp] [--value=<nilai>] [--fixed-value] [--default=<default>] <nama>" +"regexp] [--value=<nilai>] [--fixed-value] [--default=<default>] [--" +"url=<url>] <nama>" #: builtin/config.c msgid "" -"git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--" -"fixed-value] <name> <value>" +"git config set [<file-option>] [--type=<type>] [--all] [--value=<pattern>] " +"[--fixed-value] <name> <value>" msgstr "" -"git config set [<opsi berkas>] [--type=<tipe>] [--all] [--value=<nilai>] [--" +"git config set [<opsi berkas>] [--type=<tipe>] [--all] [--value=<pola>] [--" "fixed-value] <nama> <nilai>" #: builtin/config.c msgid "" -"git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] " +"git config unset [<file-option>] [--all] [--value=<pattern>] [--fixed-value] " "<name>" msgstr "" -"git config unset [<opsi berkas>] [--all] [--value=<nilai>] [--fixed-value] " +"git config unset [<opsi berkas>] [--all] [--value=<pola>] [--fixed-value] " "<nama>" #: builtin/config.c @@ -6390,20 +6403,19 @@ msgstr "git config [<opsi berkas>] --get-colorbool <nama> [<stdout-is-tty>]" #: builtin/config.c msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] " +"regexp=<regexp>] [--value=<pattern>] [--fixed-value] [--default=<default>] " "<name>" msgstr "" "git config get [<opsi berkas>] [<opsi tampilan] [--includes] [--all] [--" -"regexp=<regexp> [--value=<nilai>] [--fixed-value] [--default=<default>] " -"<nama>" +"regexp=<regexp> [--value=<pola>] [--fixed-value] [--default=<asali>] <nama>" #: builtin/config.c msgid "" "git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] " -"[--value=<value>] [--fixed-value] <name> <value>" +"[--value=<pattern>] [--fixed-value] <name> <value>" msgstr "" "git config set [<opsi berkas>] [--type=<tipe>] [--comment=<pesan>] [--all] " -"[--value=<nilai>] [--fixed-value] <nama> <nilai>" +"[--value=<pola>] [--fixed-value] <nama> <nilai>" #: builtin/config.c msgid "Config file location" @@ -7493,25 +7505,6 @@ msgid "rejected %s because shallow roots are not allowed to be updated" msgstr "tolak %s karena akar dangkal tidak diperkenankan untuk diperbarui" #: builtin/fetch.c -#, c-format -msgid "" -"some local refs could not be updated; try running\n" -" 'git remote prune %s' to remove any old, conflicting branches" -msgstr "" -"beberapa referensi lokal tidak dapat diperbarui; coba jalankan\n" -" 'git remote prune %s' untuk hapus cabang yang lama dan berkonflik" - -#: builtin/fetch.c -#, c-format -msgid " (%s will become dangling)" -msgstr " (%s akan menjadi terjuntai)" - -#: builtin/fetch.c -#, c-format -msgid " (%s has become dangling)" -msgstr " (%s telah menjadi terjuntai)" - -#: builtin/fetch.c msgid "[deleted]" msgstr "[dihapus]" @@ -7534,7 +7527,7 @@ msgstr "opsi \"%s\" nilai \"%s\" tidak valid untuk %s" msgid "option \"%s\" is ignored for %s" msgstr "opsi \"%s\" diabaikan untuk %s" -#: builtin/fetch.c object-store.c +#: builtin/fetch.c odb.c #, c-format msgid "%s is not a valid object" msgstr "%s bukan sebuah objek valid" @@ -7560,6 +7553,20 @@ msgstr "" "mematikan peringatan ini sampai remote mengubah HEAD ke yang lain." #: builtin/fetch.c +#, c-format +msgid "" +"some local refs could not be updated; try running\n" +" 'git remote prune %s' to remove any old, conflicting branches" +msgstr "" +"beberapa referensi lokal tidak dapat diperbarui; coba jalankan\n" +" 'git remote prune %s' untuk hapus cabang yang lama dan berkonflik" + +#: builtin/fetch.c +#, c-format +msgid "fetching ref %s failed: %s" +msgstr "gagal mengambil referensi %s: %s" + +#: builtin/fetch.c msgid "multiple branches detected, incompatible with --set-upstream" msgstr "banyak cabang terdeteksi, tidak kompatibel dengan --set-upstream" @@ -7868,6 +7875,10 @@ msgid "git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]" msgstr "git for-each-ref [--contains [<komit>]] [--no-contains [<komit>]]" #: builtin/for-each-ref.c +msgid "git for-each-ref [--start-after <marker>]" +msgstr "git for-each-ref [--start-after <penanda>]" + +#: builtin/for-each-ref.c msgid "quote placeholders suitably for shells" msgstr "kutip tempat penampung yang sesuai untuk cangkang" @@ -7887,6 +7898,14 @@ msgstr "kutip tempat penampung yang sesuai untuk Tcl" msgid "show only <n> matched refs" msgstr "hanya perlihatkan <n> referensi yang cocok" +#: builtin/for-each-ref.c +msgid "marker" +msgstr "penanda" + +#: builtin/for-each-ref.c +msgid "start iteration after the provided marker" +msgstr "mulai iterasi setelah penanda yang diberikan" + #: builtin/for-each-ref.c builtin/tag.c msgid "respect format colors" msgstr "hargai warna format" @@ -7920,9 +7939,17 @@ msgid "also include HEAD ref and pseudorefs" msgstr "juga termasuk referensi HEAD dan referensi semu" #: builtin/for-each-ref.c +msgid "cannot use --start-after with custom sort options" +msgstr "tidak dapat menggunakan --start-after dengan opsi urut kustom" + +#: builtin/for-each-ref.c msgid "unknown arguments supplied with --stdin" msgstr "argumen tidak dikenal diberikan dengan --stdin" +#: builtin/for-each-ref.c +msgid "cannot use --start-after with patterns" +msgstr "tidak dapat menggunakan --start-after dengan pola" + #: builtin/for-each-repo.c msgid "git for-each-repo --config=<config> [--] <arguments>" msgstr "git for-each-repo --config=<konfigurasi> [--] <argumen perintah>" @@ -8427,6 +8454,10 @@ msgid "pack prefix to store a pack containing pruned objects" msgstr "awalan pak untuk menyimpan pak berisi objek terpangkas" #: builtin/gc.c +msgid "skip maintenance tasks typically done in the foreground" +msgstr "lewati tugas pemeliharaan yang umumnya dilakukan di depan layar" + +#: builtin/gc.c #, c-format msgid "failed to parse gc.logExpiry value %s" msgstr "gagal menguraikan nilai gc.logExpiry %s" @@ -8516,13 +8547,13 @@ msgstr "" #: builtin/gc.c #, c-format -msgid "lock file '%s' exists, skipping maintenance" -msgstr "berkas kunci '%s' ada, melewatkan pemeliharaan" +msgid "task '%s' failed" +msgstr "tugas '%s' gagal" #: builtin/gc.c #, c-format -msgid "task '%s' failed" -msgstr "tugas '%s' gagal" +msgid "lock file '%s' exists, skipping maintenance" +msgstr "berkas kunci '%s' ada, melewatkan pemeliharaan" #: builtin/gc.c #, c-format @@ -8564,10 +8595,6 @@ msgid "run a specific task" msgstr "jalankan tugas spesifik" #: builtin/gc.c -msgid "use at most one of --auto and --schedule=<frequency>" -msgstr "gunakan paling banyak satu dari --auto dan --schedule=<frekuensi>" - -#: builtin/gc.c #, c-format msgid "unable to add '%s' value of '%s'" msgstr "tidak dapat menambahkan nilai '%s' dari '%s'" @@ -9683,11 +9710,6 @@ msgstr "-L<rentang>:<berkas> tidak dapat digunakan dengan spek jalur" #: builtin/log.c #, c-format -msgid "Final output: %d %s\n" -msgstr "Keluaran terakhir: %d %s\n" - -#: builtin/log.c -#, c-format msgid "git show %s: bad file" msgstr "git show %s: berkas jelek" @@ -10606,6 +10628,10 @@ msgid "(synonym to --stat)" msgstr "(sinonim untuk --stat)" #: builtin/merge.c builtin/pull.c +msgid "show a compact-summary at the end of the merge" +msgstr "perlihatkan ringkasan kecil di akhir penggabungan" + +#: builtin/merge.c builtin/pull.c msgid "add (at most <n>) entries from shortlog to merge commit message" msgstr "" "tambah (paling banyak <n>) entri dari log pendek ke pesan komit penggabungan" @@ -10942,11 +10968,6 @@ msgstr "kesalahan: masukan tag tidak lolos fsck: %s" #: builtin/mktag.c #, c-format -msgid "%d (FSCK_IGNORE?) should never trigger this callback" -msgstr "%d (FSCK_IGNORE?) seharusnya tidak pernah memicu pemanggilan balik ini" - -#: builtin/mktag.c -#, c-format msgid "could not read tagged object '%s'" msgstr "tidak dapat membaca objek tertag '%s'" @@ -11603,17 +11624,26 @@ msgid "unknown subcommand: `%s'" msgstr "subperintah tidak dikenal: `%s'" #: builtin/pack-objects.c -msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]" -msgstr "" -"git pack-objects --stdout [<opsi>...] [< <daftar referensi> | < <daftar-" -"objek>]" - -#: builtin/pack-objects.c msgid "" -"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]" -msgstr "" -"git pack-objects [<opsi>...] <nama dasar> [< <daftar referensi> | < <daftar-" -"objek>]" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<pack-name>]\n" +" [--cruft] [--cruft-expiration=<time>]\n" +" [--stdout [--filter=<filter-spec>] | <base-name>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=<n>] [--path-walk] < <object-list>" +msgstr "" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<nama pak>]\n" +" [--cruft] [--cruft-expiration=<waktu>]\n" +" [--stdout [--filter=<spek penyaring>] | <nama dasar>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=<n>] [--path-walk] < <daftar objek>" #: builtin/pack-objects.c #, c-format @@ -11743,6 +11773,17 @@ msgid "unable to get type of object %s" msgstr "tidak dapat mendapatkan tipe objek %s" #: builtin/pack-objects.c +msgid "Compressing objects by path" +msgstr "Memampatkan objek berdasarkan jalur" + +#: builtin/pack-objects.c +#, c-format +msgid "Path-based delta compression using up to %d thread" +msgid_plural "Path-based delta compression using up to %d threads" +msgstr[0] "Kompresi delta berdasarkan jalur menggunakan sampai %d utas" +msgstr[1] "Kompresi delta berdasarkan jalur menggunakan sampai %d utas" + +#: builtin/pack-objects.c msgid "Compressing objects" msgstr "Memampatkan objek" @@ -11835,6 +11876,10 @@ msgid "unable to force loose object" msgstr "tidak dapat memaksakan objek longgar" #: builtin/pack-objects.c +msgid "failed to pack objects via path-walk" +msgstr "gagal mempak objek lewat jalan jalur" + +#: builtin/pack-objects.c #, c-format msgid "not a rev '%s'" msgstr "bukan sebuah revisi '%s'" @@ -11979,6 +12024,10 @@ msgid "create thin packs" msgstr "buat pak tipis" #: builtin/pack-objects.c +msgid "use the path-walk API to walk objects when possible" +msgstr "gunakan API path-walk untuk melangkahi objek apabila dimungkinkan" + +#: builtin/pack-objects.c msgid "create packs suitable for shallow fetches" msgstr "buat pak yang cocok untuk pengambilan dangkal" @@ -12052,7 +12101,12 @@ msgstr "kedalaman rantai delta %d terlalu dalam, memaksakan %d" msgid "pack.deltaCacheLimit is too high, forcing %d" msgstr "pack.deltaCacheLimit terlalu tinggi, memaksakan %d" -#: builtin/pack-objects.c config.c +#: builtin/pack-objects.c +#, c-format +msgid "cannot use %s with %s" +msgstr "tidak dapat menggunakan %s dengan %s" + +#: builtin/pack-objects.c environment.c #, c-format msgid "bad pack compression level %d" msgstr "level kompresi pak jelek %d" @@ -12073,10 +12127,6 @@ msgstr "" "--thin tidak dapat digunakan untuk membangun sebuah pak yang dapat diindeks" #: builtin/pack-objects.c -msgid "cannot use --filter with --stdin-packs" -msgstr "tidak dapat menggunakan --filter dengan --stdin-packs" - -#: builtin/pack-objects.c msgid "cannot use internal rev list with --stdin-packs" msgstr "tidak dapat menggunakan daftar revisi internal dengan --stdin-packs" @@ -12085,10 +12135,6 @@ msgid "cannot use internal rev list with --cruft" msgstr "tidak dapat menggunakan daftar revisi internal dengan --cruft" #: builtin/pack-objects.c -msgid "cannot use --stdin-packs with --cruft" -msgstr "tidak dapat menggunakan --stdin-packs dengan --cruft" - -#: builtin/pack-objects.c msgid "Enumerating objects" msgstr "Menghitung objek" @@ -12101,24 +12147,6 @@ msgstr "" "Total %<PRIu32> (delta %<PRIu32>), digunakan ulang %<PRIu32> (delta " "%<PRIu32>), pak yang digunakan ulang %<PRIu32> (dari %<PRIuMAX>)" -#: builtin/pack-redundant.c -msgid "" -"'git pack-redundant' is nominated for removal.\n" -"If you still use this command, please add an extra\n" -"option, '--i-still-use-this', on the command line\n" -"and let us know you still use it by sending an e-mail\n" -"to <git@vger.kernel.org>. Thanks.\n" -msgstr "" -"'git pack-redundant' dinominasikan untuk dihapus.\n" -"Jika Anda masih menggunakan perintah ini, mohon tambahkan sebuah opsi\n" -"ekstra, '--i-still-use-this', pada baris perintah dan beri tahu kami jika\n" -"Anda masih menggunakannya dengan mengirimkan surel ke\n" -"<git@vger.kernel.org>. Terima kasih.\n" - -#: builtin/pack-redundant.c -msgid "refusing to run without --i-still-use-this" -msgstr "menolak menjalankan tanpa --i-still-use-this" - #: builtin/pack-refs.c msgid "" "git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude " @@ -13695,6 +13723,16 @@ msgid "unknown --mirror argument: %s" msgstr "argumen --mirror tidak dikenal: %s" #: builtin/remote.c +#, c-format +msgid "remote name '%s' is a subset of existing remote '%s'" +msgstr "nama remote '%s' adalah subset dari remote yang sudah ada '%s'" + +#: builtin/remote.c +#, c-format +msgid "remote name '%s' is a superset of existing remote '%s'" +msgstr "nama remote '%s' adalah superset dari remote yang sudah ada '%s'" + +#: builtin/remote.c msgid "fetch the remote branches" msgstr "ambil cabang remote" @@ -14069,16 +14107,6 @@ msgstr "Tidak dapat menyiapkan %s" #: builtin/remote.c #, c-format -msgid " %s will become dangling!" -msgstr " %s akan menjadi teruntai!" - -#: builtin/remote.c -#, c-format -msgid " %s has become dangling!" -msgstr " %s telah menjadi teruntai!" - -#: builtin/remote.c -#, c-format msgid "Pruning %s" msgstr "Memangkas %s" @@ -14160,11 +14188,11 @@ msgstr "jadi lebih bertele-tele; harus ditempatkan sebelum subperintah" msgid "" "git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n" -"[--write-midx] [--name-hash-version=<n>]" +"[--write-midx] [--name-hash-version=<n>] [--path-walk]" msgstr "" "git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<nama pak>]\n" -"[--write-midx] [--name-hash-version=<n>]" +"[--write-midx] [--name-hash-version=<n>] [--path-walk]" #: builtin/repack.c msgid "" @@ -14269,6 +14297,10 @@ msgstr "" "serupa berdasarkan jalur" #: builtin/repack.c +msgid "pass --path-walk to git-pack-objects" +msgstr "lewatkan --path-walk ke git-pack-objects" + +#: builtin/repack.c msgid "do not run git-update-server-info" msgstr "jangan jalankan git-update-server-info" @@ -15721,20 +15753,28 @@ msgid "git stash create [<message>]" msgstr "git stash create [<pesan>]" #: builtin/stash.c +msgid "git stash export (--print | --to-ref <ref>) [<stash>...]" +msgstr "git stash export (--print | --to-ref <ref>) [<stase>...]" + +#: builtin/stash.c +msgid "git stash import <commit>" +msgstr "git stash import <komit>" + +#: builtin/stash.c #, c-format msgid "'%s' is not a stash-like commit" msgstr "'%s' bukan komit mirip stase" #: builtin/stash.c +msgid "No stash entries found." +msgstr "Tidak ada entri stase ditemukan." + +#: builtin/stash.c #, c-format msgid "Too many revisions specified:%s" msgstr "Terlalu banyak revisi disebutkan:%s" #: builtin/stash.c -msgid "No stash entries found." -msgstr "Tidak ada entri stase ditemukan." - -#: builtin/stash.c #, c-format msgid "%s is not a valid reference" msgstr "%s bukan referensi valid" @@ -15934,6 +15974,86 @@ msgstr "masukkan berkas tak terlacak ke dalam stase" msgid "include ignore files" msgstr "masukkan berkas ignore" +#: builtin/stash.c +#, c-format +msgid "cannot parse commit %s" +msgstr "tidak dapat menguraikan komit %s" + +#: builtin/stash.c +#, c-format +msgid "invalid author or committer for %s" +msgstr "pengarang atau pengkomit tidak valid untuk %s" + +#: builtin/stash.c +msgid "could not write commit" +msgstr "tidak dapat menulis komit" + +#: builtin/stash.c +#, c-format +msgid "not a valid revision: %s" +msgstr "bukan revisi valid: %s" + +#: builtin/stash.c +#, c-format +msgid "not a commit: %s" +msgstr "bukan komit: %s" + +#: builtin/stash.c +#, c-format +msgid "%s is not a valid exported stash commit" +msgstr "%s bukan komit stase terekspor yang valid" + +#: builtin/stash.c +#, c-format +msgid "found root commit %s with invalid data" +msgstr "dapat komit akar %s dengan data invalid" + +#: builtin/stash.c +#, c-format +msgid "found stash commit %s without expected prefix" +msgstr "dapat komit stase %s tanpa prefiks yang diharapkan" + +#: builtin/stash.c +#, c-format +msgid "cannot parse parents of commit: %s" +msgstr "tidak dapat menguraikan induk komit: %s" + +#: builtin/stash.c +#, c-format +msgid "%s does not look like a stash commit" +msgstr "%s bukan terlihat seperti komit stase" + +#: builtin/stash.c +#, c-format +msgid "cannot read commit buffer for %s" +msgstr "tidak dapat membaca penyangga komit untuk %s" + +#: builtin/stash.c +#, c-format +msgid "cannot save the stash for %s" +msgstr "tidak dapat menyimpan stase untuk %s" + +#: builtin/stash.c +msgid "unable to write base commit" +msgstr "tidak dapat menulis komit dasar" + +#: builtin/stash.c +#, c-format +msgid "unable to find stash entry %s" +msgstr "tidak dapat menemukan entri stase %s" + +#: builtin/stash.c +msgid "print the object ID instead of writing it to a ref" +msgstr "cetak ID objek daripada menulisnya ke referensi" + +#: builtin/stash.c +msgid "save the data to the given ref" +msgstr "simpan data ke referensi yang diberikan" + +#: builtin/stash.c +msgid "exactly one of --print and --to-ref is required" +msgstr "tepatnya salah satu dari --print dan --to-ref diperlukan" + #: builtin/stripspace.c msgid "skip and remove all lines starting with comment character" msgstr "lewati dan hapus semua baris yang diawali dengan karakter komentar" @@ -15944,16 +16064,6 @@ msgstr "tambahkan karakter komentar dan spasi di awal setiap baris" #: builtin/submodule--helper.c #, c-format -msgid "Expecting a full ref name, got %s" -msgstr "Mengharapkan nama referensi penuh, dapat %s" - -#: builtin/submodule--helper.c -#, c-format -msgid "could not get a repository handle for submodule '%s'" -msgstr "tidak dapat mendapat pegangan repositori untuk submodul '%s'" - -#: builtin/submodule--helper.c -#, c-format msgid "" "could not look up configuration '%s'. Assuming this repository is its own " "authoritative upstream." @@ -15963,6 +16073,11 @@ msgstr "" #: builtin/submodule--helper.c #, c-format +msgid "could not get a repository handle for submodule '%s'" +msgstr "tidak dapat mendapat pegangan repositori untuk submodul '%s'" + +#: builtin/submodule--helper.c +#, c-format msgid "No url found for submodule path '%s' in .gitmodules" msgstr "Tidak ada url yang ditemukan untuk jalur submodul '%s' di .gitmodules" @@ -16393,6 +16508,11 @@ msgstr "" #: builtin/submodule--helper.c #, c-format +msgid "Expecting a full ref name, got %s" +msgstr "Mengharapkan nama referensi penuh, dapat %s" + +#: builtin/submodule--helper.c +#, c-format msgid "Unable to find current revision in submodule path '%s'" msgstr "Tidak dapat menemukan revisi saat ini pada jalur submodul '%s'" @@ -16642,6 +16762,11 @@ msgstr "URL repo: '%s' harus absolut atau diawali dengan ./|../" #: builtin/submodule--helper.c #, c-format +msgid "submodule name '%s' already used for path '%s'" +msgstr "nama submodul '%s' telah digunakan untuk jalur '%s'" + +#: builtin/submodule--helper.c +#, c-format msgid "'%s' is not a valid submodule name" msgstr "'%s' bukan nama submodul yang valid" @@ -17425,11 +17550,6 @@ msgstr "Menyiapkan pohon kerja (men-checkout '%s')" #: builtin/worktree.c #, c-format -msgid "unreachable: invalid reference: %s" -msgstr "tidak dapat dicapat: referensi tidak valid: %s" - -#: builtin/worktree.c -#, c-format msgid "Preparing worktree (detached HEAD %s)" msgstr "Menyiapkan pohon kerja (HEAD terpisah %s)" @@ -19479,16 +19599,6 @@ msgstr "nilai konfigurasi numerik '%s' jelek untuk '%s' dalam %s: %s" #: config.c #, c-format -msgid "invalid value for variable %s" -msgstr "nilai tidak valid untuk variabel %s" - -#: config.c -#, c-format -msgid "ignoring unknown core.fsync component '%s'" -msgstr "mengabaikan komponen core.fsync tidak dikenal '%s'" - -#: config.c -#, c-format msgid "bad boolean config value '%s' for '%s'" msgstr "nilai konfigurasi boolean '%s' jelek untuk '%s'" @@ -19504,54 +19614,6 @@ msgstr "'%s' untuk '%s' bukan stempel waktu valid" #: config.c #, c-format -msgid "abbrev length out of range: %d" -msgstr "panjang singkatan di luar rentang: %d" - -#: config.c -#, c-format -msgid "bad zlib compression level %d" -msgstr "level kompresi zlib jelek %d" - -#: config.c -#, c-format -msgid "%s cannot contain newline" -msgstr "%s tidak dapat berisi baris baru" - -#: config.c -#, c-format -msgid "%s must have at least one character" -msgstr "%s harus ada sedikitnya satu karakter" - -#: config.c -#, c-format -msgid "ignoring unknown core.fsyncMethod value '%s'" -msgstr "mengabaikan nilai core.fsyncMethod tidak dikenal '%s'" - -#: config.c -msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" -msgstr "core.fsyncObjectFiles usang; gunakan core.fsync sebagai gantinya" - -#: config.c -#, c-format -msgid "invalid mode for object creation: %s" -msgstr "mode tidak valid untuk pembuatan objek: %s" - -#: config.c -#, c-format -msgid "malformed value for %s" -msgstr "nilai rusak untuk %s" - -#: config.c -#, c-format -msgid "malformed value for %s: %s" -msgstr "nilai rusak untuk %s: %s" - -#: config.c -msgid "must be one of nothing, matching, simple, upstream or current" -msgstr "harus salah satu dari nothing, matching, simple, upstream atau current" - -#: config.c -#, c-format msgid "unable to load config blob object '%s'" msgstr "tidak dapat memuat objek blob konfigurasi '%s'" @@ -20185,8 +20247,8 @@ msgid "cannot compare a named pipe to a directory" msgstr "tidak dapat membandingkan pipa bernama dan sebuah direktori" #: diff-no-index.c -msgid "git diff --no-index [<options>] <path> <path>" -msgstr "git diff --no-index [<opsi>] <jalur> <jalur>" +msgid "git diff --no-index [<options>] <path> <path> [<pathspec>...]" +msgstr "git diff --no-index [<opsi>] <jalur> <jalur> [<spek jalur>...]" #: diff-no-index.c msgid "" @@ -20196,6 +20258,14 @@ msgstr "" "Bukan sebuah repositori git. Gunakan --no-index untuk membandingkan dua " "jalur di luar pohon kerja" +#: diff-no-index.c +msgid "" +"Limiting comparison with pathspecs is only supported if both paths are " +"directories." +msgstr "" +"Membatasi perbandingan dengan spek jalur hanya didukung jika kedua jalur " +"merupakan direktori." + #: diff.c #, c-format msgid " Failed to parse dirstat cut-off percentage '%s'\n" @@ -20378,7 +20448,7 @@ msgstr "buat tambalan" msgid "<n>" msgstr "<n>" -#: diff.c +#: diff.c parse-options.h msgid "generate diffs with <n> lines context" msgstr "buat diff dengan <n> baris konteks" @@ -20529,7 +20599,7 @@ msgstr "jangan perlihatkan prefiks sumber atau tujuan apapun" msgid "use default prefixes a/ and b/" msgstr "gunakan awalan asali a/ dan b/" -#: diff.c +#: diff.c parse-options.h msgid "show context between diff hunks up to the specified number of lines" msgstr "" "perlihatkan konteks diantara bingkah diff hingga jumlah baris yang disebutkan" @@ -20929,6 +20999,64 @@ msgstr "tidak dapat men-stat berkas '%s'" msgid "bad git namespace path \"%s\"" msgstr "jalur ruang nama git jelek \"%s\"" +#: environment.c +#, c-format +msgid "invalid value for variable %s" +msgstr "nilai tidak valid untuk variabel %s" + +#: environment.c +#, c-format +msgid "ignoring unknown core.fsync component '%s'" +msgstr "mengabaikan komponen core.fsync tidak dikenal '%s'" + +#: environment.c +#, c-format +msgid "abbrev length out of range: %d" +msgstr "panjang singkatan di luar rentang: %d" + +#: environment.c +#, c-format +msgid "bad zlib compression level %d" +msgstr "level kompresi zlib jelek %d" + +#: environment.c +#, c-format +msgid "%s cannot contain newline" +msgstr "%s tidak dapat berisi baris baru" + +#: environment.c +#, c-format +msgid "%s must have at least one character" +msgstr "%s harus ada sedikitnya satu karakter" + +#: environment.c +#, c-format +msgid "ignoring unknown core.fsyncMethod value '%s'" +msgstr "mengabaikan nilai core.fsyncMethod tidak dikenal '%s'" + +#: environment.c +msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" +msgstr "core.fsyncObjectFiles usang; gunakan core.fsync sebagai gantinya" + +#: environment.c +#, c-format +msgid "invalid mode for object creation: %s" +msgstr "mode tidak valid untuk pembuatan objek: %s" + +#: environment.c +#, c-format +msgid "malformed value for %s" +msgstr "nilai rusak untuk %s" + +#: environment.c +#, c-format +msgid "malformed value for %s: %s" +msgstr "nilai rusak untuk %s: %s" + +#: environment.c +msgid "must be one of nothing, matching, simple, upstream or current" +msgstr "harus salah satu dari nothing, matching, simple, upstream atau current" + #: exec-cmd.c #, c-format msgid "too many args to run %s" @@ -21800,6 +21928,35 @@ msgstr "nama identitas kosong (untuk <%s>) tidak diperbolehkan" msgid "name consists only of disallowed characters: %s" msgstr "nama hanya terdiri dari karakter yang tidak diperbolehkan: %s" +#: imap-send.c +msgid "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>" +msgstr "" +"git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>" + +#: imap-send.c +msgid "no IMAP host specified" +msgstr "tidak ada host IMAP yang dirincikan" + +#: imap-send.c +msgid "" +"set the IMAP host with 'git config imap.host <host>'.\n" +"(e.g., 'git config imap.host imaps://imap.example.com')" +msgstr "" +"setel host IMAP dengan 'git config imap.host <host>'.\n" +"(misalnya 'git config imap.host imaps://imap.example.com')" + +#: imap-send.c +msgid "no IMAP folder specified" +msgstr "tidak ada folder IMAP yang dirincikan" + +#: imap-send.c +msgid "" +"set the target folder with 'git config imap.folder <folder>'.\n" +"(e.g., 'git config imap.folder Drafts')" +msgstr "" +"setel folder target dengan 'git config imap.folder <folder>'.\n" +"(contohnya 'git config imap.folder Drafts')" + #: list-objects-filter-options.c msgid "expected 'tree:<depth>'" msgstr "'tree:<kedalaman> diharapkan'" @@ -22962,114 +23119,114 @@ msgstr "<objek>:<jalur> diperlukan, hanya <objek> '%s' diberikan" msgid "invalid object name '%.*s'." msgstr "nama objek tidak valid '%.*s'." -#: object-store.c +#: object.c +#, c-format +msgid "invalid object type \"%s\"" +msgstr "tipe objek tidak valid \"%s\"" + +#: object.c +#, c-format +msgid "object %s is a %s, not a %s" +msgstr "objek %s adalah %s, bukan %s" + +#: object.c +#, c-format +msgid "object %s has unknown type id %d" +msgstr "objek %s punya id tipe tidak dikenal %d" + +#: object.c +#, c-format +msgid "unable to parse object: %s" +msgstr "tidak dapat menguraikan objek: %s" + +#: object.c +#, c-format +msgid "hash mismatch %s" +msgstr "hash tidak cocok %s" + +#: odb.c #, c-format msgid "object directory %s does not exist; check .git/objects/info/alternates" msgstr "direktori objek %s tidak ada; periksa .git/objects/info/alternates" -#: object-store.c +#: odb.c #, c-format msgid "unable to normalize alternate object path: %s" msgstr "tidak dapat menormalisasikan jalur objek alternatif: %s" -#: object-store.c +#: odb.c #, c-format msgid "%s: ignoring alternate object stores, nesting too deep" msgstr "%s: mengabaikan penyimpanan objek alternatif, bersarang terlalu dalam" -#: object-store.c +#: odb.c msgid "unable to fdopen alternates lockfile" msgstr "tidak dapat men-fdopen berkas kunci alternatif" -#: object-store.c +#: odb.c msgid "unable to read alternates file" msgstr "tidak dapat membaca berkas alternatif" -#: object-store.c +#: odb.c msgid "unable to move new alternates file into place" msgstr "tidak dapat memindahkan berkas alternatif baru ke tempatnya" -#: object-store.c +#: odb.c #, c-format msgid "path '%s' does not exist" msgstr "jalur '%s' tidak ada" -#: object-store.c +#: odb.c #, c-format msgid "reference repository '%s' as a linked checkout is not supported yet." msgstr "" "repositori referensi '%s' sebagai sebuah checkout tertaut belum didukung." -#: object-store.c +#: odb.c #, c-format msgid "reference repository '%s' is not a local repository." msgstr "repositori referensi '%s' bukan sebuah repositori lokal" -#: object-store.c +#: odb.c #, c-format msgid "reference repository '%s' is shallow" msgstr "repositori referensi '%s' dangkal" -#: object-store.c +#: odb.c #, c-format msgid "reference repository '%s' is grafted" msgstr "repositori referensi '%s' cangkok" -#: object-store.c +#: odb.c #, c-format msgid "could not find object directory matching %s" msgstr "tidak dapat menemukan direktori objek yang cocok dengan %s" -#: object-store.c +#: odb.c #, c-format msgid "invalid line while parsing alternate refs: %s" msgstr "baris tidak valid saat menguraikan referensi alternatif: %s" -#: object-store.c +#: odb.c #, c-format msgid "replacement %s not found for %s" msgstr "pengganti %s tidak ditemukan untuk %s" -#: object-store.c +#: odb.c #, c-format msgid "packed object %s (stored in %s) is corrupt" msgstr "objek terpak %s (disimpan di %s) rusak" -#: object-store.c +#: odb.c #, c-format msgid "missing mapping of %s to %s" msgstr "pemetaan %s ke %s hilang" -#: object-store.c +#: odb.c #, c-format msgid "%s is not a valid '%s' object" msgstr "%s bukan sebuah objek '%s' valid" -#: object.c -#, c-format -msgid "invalid object type \"%s\"" -msgstr "tipe objek tidak valid \"%s\"" - -#: object.c -#, c-format -msgid "object %s is a %s, not a %s" -msgstr "objek %s adalah %s, bukan %s" - -#: object.c -#, c-format -msgid "object %s has unknown type id %d" -msgstr "objek %s punya id tipe tidak dikenal %d" - -#: object.c -#, c-format -msgid "unable to parse object: %s" -msgstr "tidak dapat menguraikan objek: %s" - -#: object.c -#, c-format -msgid "hash mismatch %s" -msgstr "hash tidak cocok %s" - #: pack-bitmap-write.c #, c-format msgid "duplicate entry when writing bitmap index: %s" @@ -23084,10 +23241,6 @@ msgstr "mencoba menyimpan komit yang tidak dipilih: '%s'" msgid "too many pseudo-merges" msgstr "terlalu banyak penggabungan semu" -#: pack-bitmap-write.c -msgid "trying to write commit not in index" -msgstr "mencoba menulis komit yang bukan di indeks" - #: pack-bitmap.c msgid "failed to load bitmap index (corrupted?)" msgstr "gagal menulis indeks bitmap (rusak?)" @@ -23398,6 +23551,11 @@ msgstr "%s tidak ada" #: parse-options.c #, c-format +msgid "value for %s exceeds %<PRIdMAX>" +msgstr "nilai %s melebihi %<PRIdMAX>" + +#: parse-options.c +#, c-format msgid "value %s for %s not in range [%<PRIdMAX>,%<PRIdMAX>]" msgstr "nilai %s untuk %s di luar rentang [%<PRIdMAX>,%<PRIdMAX>]" @@ -24584,6 +24742,16 @@ msgstr "%s tidak menunjuk ke sebuah objek valid!" #: refs.c #, c-format +msgid "%s%s will become dangling after %s is deleted\n" +msgstr "%s%s akan menjadi teruntai setelah %s dihapus\n" + +#: refs.c +#, c-format +msgid "%s%s has become dangling after %s was deleted\n" +msgstr "%s%s telah menjadi teruntai setelah %s dihapus\n" + +#: refs.c +#, c-format msgid "" "Using '%s' as the name for the initial branch. This default branch name\n" "is subject to change. To configure the initial branch name to use in all\n" @@ -27417,6 +27585,10 @@ msgid "toggle pruning of uninteresting paths" msgstr "pangkas jalur yang tidak menarik" #: t/helper/test-path-walk.c +msgid "toggle aggressive edge walk" +msgstr "nyalakan jalan tepian agresif" + +#: t/helper/test-path-walk.c msgid "read a pattern list over stdin" msgstr "baca daftar pola dari masukan standar" @@ -28179,6 +28351,25 @@ msgstr "kesalahan: " msgid "warning: " msgstr "peringatan: " +#: usage.c +#, c-format +msgid "" +"'%s' is nominated for removal.\n" +"If you still use this command, please add an extra\n" +"option, '--i-still-use-this', on the command line\n" +"and let us know you still use it by sending an e-mail\n" +"to <git@vger.kernel.org>. Thanks.\n" +msgstr "" +"'%s' dinominasikan untuk dihapus.\n" +"Jika Anda masih menggunakan perintah ini, mohon tambahkan sebuah opsi\n" +"tambahan, '--i-still-use-this', pada baris perintah dan beri tahu kami jika\n" +"Anda masih menggunakannya dengan mengirimkan surel ke\n" +"<git@vger.kernel.org>. Terima kasih.\n" + +#: usage.c +msgid "refusing to run without --i-still-use-this" +msgstr "menolak menjalankan tanpa --i-still-use-this" + #: version.c #, c-format msgid "uname() failed with error '%s' (%d)\n" @@ -29328,6 +29519,11 @@ msgstr "(body) Menambahkan cc: %s dari baris '%s'\n" #: git-send-email.perl #, perl-format +msgid "error: invalid SMTP port '%s'\n" +msgstr "galat: port SMTP tidak valid '%s'\n" + +#: git-send-email.perl +#, perl-format msgid "(%s) Could not execute '%s'" msgstr "(%s) Tidak dapat menjalankan '%s'" diff --git a/po/meson.build b/po/meson.build index d7154b6395..de3b4e2c38 100644 --- a/po/meson.build +++ b/po/meson.build @@ -8,6 +8,7 @@ translations = i18n.gettext('git', 'el', 'es', 'fr', + 'ga', 'id', 'is', 'it', @@ -5,10 +5,10 @@ # msgid "" msgstr "" -"Project-Id-Version: git 2.49.0\n" +"Project-Id-Version: git 2.51.0\n" "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n" -"POT-Creation-Date: 2025-03-10 17:45+0100\n" -"PO-Revision-Date: 2025-03-10 17:48+0100\n" +"POT-Creation-Date: 2025-08-14 09:52+0100\n" +"PO-Revision-Date: 2025-08-14 09:53+0100\n" "Last-Translator: Peter Krefting <peter@softwolves.pp.se>\n" "Language-Team: Svenska <tp-sv@listor.tp-sv.se>\n" "Language: sv\n" @@ -19,6 +19,10 @@ msgstr "" "X-Generator: Gtranslator 42.0\n" #, c-format +msgid "%s cannot be negative" +msgstr "%s kan inte vara negativt" + +#, c-format msgid "Huh (%s)?" msgstr "VadÃ¥ (%s)?" @@ -1923,6 +1927,10 @@ msgid "adding files failed" msgstr "misslyckades lägga till filer" #, c-format +msgid "'%s' cannot be negative" +msgstr "â€%s†kan inte vara negativt" + +#, c-format msgid "--chmod param '%s' must be either -x or +x" msgstr "â€--chmodâ€-parametern â€%s†mÃ¥ste antingen vara -x eller +x" @@ -3241,11 +3249,8 @@ msgstr "endast en buntflagga kan anges" msgid "git cat-file <type> <object>" msgstr "git cat-file <typ> <objekt>" -msgid "git cat-file (-e | -p) <object>" -msgstr "git cat-file (-e | -p) <objekt>" - -msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>" -msgstr "git cat-file (-t | -s) [--allow-unknown-type] <objekt>" +msgid "git cat-file (-e | -p | -t | -s) <object>" +msgstr "git cat-file (-e | -p | -t | -s) <objekt>" msgid "" "git cat-file (--textconv | --filters)\n" @@ -3284,9 +3289,6 @@ msgstr "visa objekttyp (en av: â€blobâ€, â€treeâ€, â€commitâ€, â€tagâ€, msgid "show object size" msgstr "visa objektstorlek" -msgid "allow -s and -t to work with broken/corrupt objects" -msgstr "lÃ¥ter -s och -t att fungera med trasiga/sönderskrivna objekt" - msgid "use mail map file" msgstr "använd e-postmappningsfil" @@ -3342,6 +3344,13 @@ msgstr "blob|träd" msgid "use a <path> for (--textconv | --filters); Not with 'batch'" msgstr "använd en <sökväg> för (--textconv | --filters): Inte med â€batchâ€" +msgid "objects filter only supported in batch mode" +msgstr "objektfilter stöds endast i buntläge" + +#, c-format +msgid "objects filter not supported: '%s'" +msgstr "objektfilter stöds inte: â€%sâ€" + #, c-format msgid "'%s=<%s>' needs '%s' or '%s'" msgstr "â€%s=<%s>†behöver â€%s†eller â€%sâ€" @@ -5054,23 +5063,25 @@ msgstr "git config list [<filflagga>] [<visningsflagga>] [--includes]" msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp] [--value=<value>] [--fixed-value] [--default=<default>] <name>" +"regexp] [--value=<pattern>] [--fixed-value] [--default=<default>] [--" +"url=<url>] <name>" msgstr "" "git config get [<filflagga>] [<visningsflagga>] [--includes] [--all] [--" -"regexp] [--value=<värde>] [--fixed-value] [--default=<förval>] <namn>" +"regexp] [--value=<mönster>] [--fixed-value] [--default=<förval>] [--" +"url=<url>] <namn>" msgid "" -"git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--" -"fixed-value] <name> <value>" +"git config set [<file-option>] [--type=<type>] [--all] [--value=<pattern>] " +"[--fixed-value] <name> <value>" msgstr "" -"git config set [<filflagga>] [--type=<typ>] [--all] [--value=<värde>] [--" +"git config set [<filflagga>] [--type=<typ>] [--all] [--value=<mönster>] [--" "fixed-value] <namn> <värde>" msgid "" -"git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] " +"git config unset [<file-option>] [--all] [--value=<pattern>] [--fixed-value] " "<name>" msgstr "" -"git config unset [<filflagga>] [--all] [--value=<värde>] [--fixed-value] " +"git config unset [<filflagga>] [--all] [--value=<mönster>] [--fixed-value] " "<namn>" msgid "git config rename-section [<file-option>] <old-name> <new-name>" @@ -5087,19 +5098,19 @@ msgstr "git config [<filflagga>] --get-colorbool <namn> [<stdout-är-tty>]" msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] " +"regexp=<regexp>] [--value=<pattern>] [--fixed-value] [--default=<default>] " "<name>" msgstr "" "git config get [<filflagga>] [<visningsflagga>] [--includes] [--all] [--" -"regexp=<reguttr>] [--value=<värde>] [--fixed-value] [--default=<förval>] " +"regexp=<reguttr>] [--value=<mönster>] [--fixed-value] [--default=<förval>] " "<namn>" msgid "" "git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] " -"[--value=<value>] [--fixed-value] <name> <value>" +"[--value=<pattern>] [--fixed-value] <name> <value>" msgstr "" "git config set [<filflagga>] [--type=<typ>] [--comment=<meddelande>] [--all] " -"[--value=<värde>] [--fixed-value] <namn> <värde>" +"[--value=<mönster>] [--fixed-value] <namn> <värde>" msgid "Config file location" msgstr "Konfigurationsfilens plats" @@ -5585,6 +5596,50 @@ msgstr "ange ett filändelse i strftime-format" msgid "specify the content of the diagnostic archive" msgstr "ange vilket innehÃ¥ll diagnostikarkivet ska ha" +#, c-format +msgid "unable to parse mode: %s" +msgstr "kan inte tolka läget: %s" + +#, c-format +msgid "unable to parse object id: %s" +msgstr "kan inte tolka objekt-id: %s" + +msgid "git diff-pairs -z [<diff-options>]" +msgstr "git diff-pairs -z [<diff-flaggor>]" + +#, c-format +msgid "unrecognized argument: %s" +msgstr "okänt argument: %s" + +msgid "working without -z is not supported" +msgstr "stöder inte att arbeta utan -z" + +msgid "pathspec arguments not supported" +msgstr "sökvägsangivelser stöds inte som argument" + +msgid "revision arguments not allowed" +msgstr "revisioner tillÃ¥ts inte som argument" + +msgid "invalid raw diff input" +msgstr "ogiltig indata i rÃ¥ diff" + +msgid "tree objects not supported" +msgstr "trädobjekt stöds inte" + +msgid "got EOF while reading path" +msgstr "fick filslut vid läsning av sökväg" + +msgid "got EOF while reading destination path" +msgstr "fick filslut vid läsning av destinationssökväg" + +#, c-format +msgid "unable to parse rename/copy score: %s" +msgstr "kan inte tolka namnbyte-/kopieringspoäng: %s" + +#, c-format +msgid "unknown diff status: %c" +msgstr "okänd diff-status: %c" + msgid "--merge-base only works with two commits" msgstr "--merge-base fungerar endast med tvÃ¥ incheckningar" @@ -5725,6 +5780,9 @@ msgstr "visa förlopp efter <n> objekt" msgid "select handling of signed tags" msgstr "välj hantering av signerade taggar" +msgid "select handling of signed commits" +msgstr "välj hantering av signerade incheckningar" + msgid "select handling of tags that tag filtered objects" msgstr "välj hantering av taggar som har taggfiltrerade objekt" @@ -5895,22 +5953,6 @@ msgstr "%s sände inte alla nödvändiga objekt" msgid "rejected %s because shallow roots are not allowed to be updated" msgstr "avvisade %s dÃ¥ grunda rötter inte tillÃ¥ts uppdateras" -#, c-format -msgid "" -"some local refs could not be updated; try running\n" -" 'git remote prune %s' to remove any old, conflicting branches" -msgstr "" -"vissa lokala referenser kunde inte uppdateras; testa att köra\n" -" â€git remote prune %s†för att ta bort gamla grenar som stÃ¥r i konflikt" - -#, c-format -msgid " (%s will become dangling)" -msgstr " (%s kommer bli dinglande)" - -#, c-format -msgid " (%s has become dangling)" -msgstr " (%s har blivit dinglande)" - msgid "[deleted]" msgstr "[borttagen]" @@ -5951,6 +5993,18 @@ msgstr "" "varningen till fjärren ändrar HEAD till nÃ¥got annat genom att köra\n" "â€git config set remote %s.followRemoteHEAD warn-if-not-branch-%sâ€." +#, c-format +msgid "" +"some local refs could not be updated; try running\n" +" 'git remote prune %s' to remove any old, conflicting branches" +msgstr "" +"vissa lokala referenser kunde inte uppdateras; testa att köra\n" +" â€git remote prune %s†för att ta bort gamla grenar som stÃ¥r i konflikt" + +#, c-format +msgid "fetching ref %s failed: %s" +msgstr "misslyckades hämta referensen %s: %s" + msgid "multiple branches detected, incompatible with --set-upstream" msgstr "flera grenar upptäcktes, inkompatibelt med --set-upstream" @@ -6191,6 +6245,9 @@ msgid "git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]" msgstr "" "git for-each-ref [--contains [<incheckning>]] [--no-contains [<incheckning>]]" +msgid "git for-each-ref [--start-after <marker>]" +msgstr "git for-each-ref [--start-after <markör>]" + msgid "quote placeholders suitably for shells" msgstr "citera platshÃ¥llare passande för skal" @@ -6206,6 +6263,12 @@ msgstr "citera platshÃ¥llare passande för Tcl" msgid "show only <n> matched refs" msgstr "visa endast <n> träffade refs" +msgid "marker" +msgstr "markör" + +msgid "start iteration after the provided marker" +msgstr "pÃ¥börjar iterationen efter angiven markör" + msgid "respect format colors" msgstr "använd formatfärger" @@ -6230,9 +6293,15 @@ msgstr "läs referensmönster frÃ¥n standard in" msgid "also include HEAD ref and pseudorefs" msgstr "ta ocksÃ¥ med HEAD- och pseudo-referenser" +msgid "cannot use --start-after with custom sort options" +msgstr "kan inte använda --start-after med skräddarsydda sorteringsalternativ" + msgid "unknown arguments supplied with --stdin" msgstr "okända argument angavs tillsammans med --stdin" +msgid "cannot use --start-after with patterns" +msgstr "kan inte använda --start-after med mönster" + msgid "git for-each-repo --config=<config> [--] <arguments>" msgstr "git for-each-repo --config=<konfig> [--] <argument>" @@ -6365,10 +6434,6 @@ msgid "%s: object corrupt or missing: %s" msgstr "%s: objektet trasigt eller saknas: %s" #, c-format -msgid "%s: object is of unknown type '%s': %s" -msgstr "%s: objektet har okänd typ â€%sâ€: %s" - -#, c-format msgid "%s: object could not be parsed: %s" msgstr "%s: objektet kunde inte tolkas: %s" @@ -6425,16 +6490,19 @@ msgstr "kan inte läsa rev-index för paketfil â€%sâ€" msgid "invalid rev-index for pack '%s'" msgstr "ogiltigt rev-index för paketet â€%sâ€" +msgid "Checking ref database" +msgstr "Kontrollerar referensdatabasen" + msgid "" "git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n" " [--[no-]full] [--strict] [--verbose] [--lost-found]\n" " [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n" -" [--[no-]name-objects] [<object>...]" +" [--[no-]name-objects] [--[no-]references] [<object>...]" msgstr "" "git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n" " [--[no-]full] [--strict] [--verbose] [--lost-found]\n" " [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n" -" [--[no-]name-objects] [<objekt>...]" +" [--[no-]name-objects] [--[no-]references] [<objekt>...]" msgid "show unreachable objects" msgstr "visa onÃ¥bara objekt" @@ -6474,6 +6542,9 @@ msgstr "visa förlopp" msgid "show verbose names for reachable objects" msgstr "visa ordrika namn för nÃ¥bara objekt" +msgid "check reference database consistency" +msgstr "kontrollerar referensdatabasens konsistens" + msgid "Checking objects" msgstr "Kontrollerar objekt" @@ -6632,6 +6703,9 @@ msgstr "packa om alla paket förutom det största paketet" msgid "pack prefix to store a pack containing pruned objects" msgstr "paketprefix att lagra ett paket som innehÃ¥ller bortrensade objekt" +msgid "skip maintenance tasks typically done in the foreground" +msgstr "hoppa över underhÃ¥llsfunktioner som vanligtvis körs i förgrunden" + #, c-format msgid "failed to parse gc.logExpiry value %s" msgstr "misslyckades tolka värdet %s för gc.logExpiry" @@ -6704,14 +6778,14 @@ msgstr "" "inaktiverat" #, c-format -msgid "lock file '%s' exists, skipping maintenance" -msgstr "lÃ¥sfilen â€%s†finns, hoppar över underhÃ¥ll" - -#, c-format msgid "task '%s' failed" msgstr "uppgiften â€%s†misslyckades" #, c-format +msgid "lock file '%s' exists, skipping maintenance" +msgstr "lÃ¥sfilen â€%s†finns, hoppar över underhÃ¥ll" + +#, c-format msgid "'%s' is not a valid task" msgstr "â€%s†är inte en giltig uppgift" @@ -6740,9 +6814,6 @@ msgstr "uppgift" msgid "run a specific task" msgstr "utför en specifik uppgift" -msgid "use at most one of --auto and --schedule=<frequency>" -msgstr "använd som mest en av --auto och --schedule=<frekvens>" - #, c-format msgid "unable to add '%s' value of '%s'" msgstr "kan inte lägga till â€%sâ€-värdet för â€%sâ€" @@ -7608,18 +7679,10 @@ msgstr "" "spÃ¥ra utvecklingen av radintervallet <start>,<slut> eller funktionen :" "<funknamn> i <fil>" -#, c-format -msgid "unrecognized argument: %s" -msgstr "okänt argument: %s" - msgid "-L<range>:<file> cannot be used with pathspec" msgstr "-L<intervall>:<fil> kan inte användas med sökvägsspecifikation" #, c-format -msgid "Final output: %d %s\n" -msgstr "Slututdata: %d %s\n" - -#, c-format msgid "git show %s: bad file" msgstr "git show %s: felaktig fil" @@ -8250,6 +8313,9 @@ msgstr "gör endast en enkel sammanslagning" msgid "also show informational/conflict messages" msgstr "visa även informations-/konfliktmeddelanden" +msgid "suppress all output; only exit status wanted" +msgstr "undertryck all utdata; önskar endast avslutningsstatus" + msgid "list filenames without modes/oids/stages" msgstr "lista filnamn utan lägen/oid/köer" @@ -8310,6 +8376,9 @@ msgstr "visa en diffstat när sammanslagningen är färdig" msgid "(synonym to --stat)" msgstr "(synonym till --stat)" +msgid "show a compact-summary at the end of the merge" +msgstr "visa en kompakt sammanfattning när sammanslagningen är färdig" + msgid "add (at most <n>) entries from shortlog to merge commit message" msgstr "" "lägg till (som mest <n>) poster frÃ¥n shortlog till incheckningsmeddelandet" @@ -8575,10 +8644,6 @@ msgid "error: tag input does not pass fsck: %s" msgstr "fel: taggindata godkänns inte av fsck: %s" #, c-format -msgid "%d (FSCK_IGNORE?) should never trigger this callback" -msgstr "%d (FSCK_IGNORE?) skulle aldrig utlösa detta Ã¥teranrop" - -#, c-format msgid "could not read tagged object '%s'" msgstr "kunde inte läsa det taggade objektet â€%sâ€" @@ -8651,8 +8716,11 @@ msgstr "" "vid ompackning, samla mindre paketfiler i en bunt som är större än denna " "storlek" -msgid "git mv [<options>] <source>... <destination>" -msgstr "git mv [<flaggor>] <källa>... <mÃ¥l>" +msgid "git mv [-v] [-f] [-n] [-k] <source> <destination>" +msgstr "git mv [-v] [-f] [-n] [-k] <källa> <mÃ¥l>" + +msgid "git mv [-v] [-f] [-n] [-k] <source>... <destination-directory>" +msgstr "git mv [-v] [-f] [-n] [-k] <källa> <mÃ¥lkatalog>" #, c-format msgid "Directory %s is in index and no submodule?" @@ -8722,6 +8790,10 @@ msgid "%s, source=%s, destination=%s" msgstr "%s, källa=%s, mÃ¥l=%s" #, c-format +msgid "cannot move both '%s' and its parent directory '%s'" +msgstr "kan inte flytta bÃ¥de â€%s†och dess föräldrakatalog â€%sâ€" + +#, c-format msgid "Renaming %s to %s\n" msgstr "Byter namn pÃ¥ %s till %s\n" @@ -9086,13 +9158,26 @@ msgstr "använd anteckningar frÃ¥n <anteckningsref>" msgid "unknown subcommand: `%s'" msgstr "okänt underkommando: â€%sâ€" -msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]" -msgstr "git pack-objects --stdout [<flaggor>] [< <reflista> | < <objektlista>]" - msgid "" -"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<pack-name>]\n" +" [--cruft] [--cruft-expiration=<time>]\n" +" [--stdout [--filter=<filter-spec>] | <base-name>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=<n>] [--path-walk] < <object-list>" msgstr "" -"git pack-objects [<flaggor>] <basnamn> [< <reflista> | < <objektlista>]" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<paketnamn>]\n" +" [--cruft] [--cruft-expiration=<tid>]\n" +" [--stdout [--filter=<filterspec>] | <basnamn>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=<n>] [--path-walk] < <objektlista>" #, c-format msgid "invalid --name-hash-version option: %d" @@ -9196,6 +9281,15 @@ msgstr "kan inte packa objekt nÃ¥bara frÃ¥n taggen %s" msgid "unable to get type of object %s" msgstr "kan inte hämta typ för objektet %s" +msgid "Compressing objects by path" +msgstr "komprimerar objekt efter sökväg" + +#, c-format +msgid "Path-based delta compression using up to %d thread" +msgid_plural "Path-based delta compression using up to %d threads" +msgstr[0] "Sökvägsbaserad deltakomprimering använder upp till %d trÃ¥d" +msgstr[1] "Sökvägsbaserad deltakomprimering använder upp till %d trÃ¥dar" + msgid "Compressing objects" msgstr "Komprimerar objekt" @@ -9270,6 +9364,9 @@ msgstr "lösa objekt pÃ¥ %s kunde inte undersökas" msgid "unable to force loose object" msgstr "kan inte tvinga lösa objekt" +msgid "failed to pack objects via path-walk" +msgstr "misslyckades packa objekt genom att gÃ¥ genom sökväg" + #, c-format msgid "not a rev '%s'" msgstr "inte en referens â€%sâ€" @@ -9379,6 +9476,9 @@ msgstr "använd gles-nÃ¥barhetsalgoritmen" msgid "create thin packs" msgstr "skapa tunna paket" +msgid "use the path-walk API to walk objects when possible" +msgstr "använd â€path-walkâ€-API:et för att gÃ¥ genom objekt om möjligt" + msgid "create packs suitable for shallow fetches" msgstr "skapa packfiler lämpade för grunda hämtningar" @@ -9435,6 +9535,10 @@ msgid "pack.deltaCacheLimit is too high, forcing %d" msgstr "pack.deltaCacheLimit är för högt, pÃ¥tvingar %d" #, c-format +msgid "cannot use %s with %s" +msgstr "kan inte använda %s med %s" + +#, c-format msgid "bad pack compression level %d" msgstr "felaktig paketkomprimeringsgrad %d" @@ -9448,18 +9552,12 @@ msgstr "minsta packstorlek är 1 MiB" msgid "--thin cannot be used to build an indexable pack" msgstr "--thin kan inte användas för att bygga ett indexerbart paket" -msgid "cannot use --filter with --stdin-packs" -msgstr "kan inte använda --filter med --stdin-packs" - msgid "cannot use internal rev list with --stdin-packs" msgstr "kan inte använda intern revisionslista med --stdin-packs" msgid "cannot use internal rev list with --cruft" msgstr "kan inte använda intern revisionslista med --cruft" -msgid "cannot use --stdin-packs with --cruft" -msgstr "kan inte använda --stdin-packs med --cruft" - msgid "Enumerating objects" msgstr "Räknar upp objekt" @@ -9472,22 +9570,6 @@ msgstr "" "paket-Ã¥teranvända %<PRIu32> (frÃ¥n %<PRIuMAX>)" msgid "" -"'git pack-redundant' is nominated for removal.\n" -"If you still use this command, please add an extra\n" -"option, '--i-still-use-this', on the command line\n" -"and let us know you still use it by sending an e-mail\n" -"to <git@vger.kernel.org>. Thanks.\n" -msgstr "" -"â€git pack-redundant†har nominerats för borttagning.\n" -"Om du fortfarande använder kommandot, lägg till flaggan\n" -"â€--i-still-use-this†pÃ¥ kommandoraden och berätta för\n" -"oss att du fortfarande använder det pÃ¥ e-post till\n" -"<git@vger.kernel.org>. Tack.\n" - -msgid "refusing to run without --i-still-use-this" -msgstr "vägrar köra utan --i-still-use-this" - -msgid "" "git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude " "<pattern>]" msgstr "" @@ -9639,6 +9721,10 @@ msgstr "" msgid "unable to access commit %s" msgstr "kan inte komma Ã¥t incheckningen %s" +#, c-format +msgid "invalid refspec '%s'" +msgstr "felaktig referensspecifikation: â€%sâ€" + msgid "ignoring --verify-signatures for rebase" msgstr "ignorera --verify-signatures för ombasering" @@ -10588,6 +10674,9 @@ msgstr "" msgid "git reflog exists <ref>" msgstr "git reflog exists <referens>" +msgid "git reflog drop [--all [--single-worktree] | <refs>...]" +msgstr "git reflog drop [--all [--single-worktree] | <referenser>...]" + #, c-format msgid "invalid timestamp '%s' given to '--%s'" msgstr "ogiltig tidsstämpel â€%s†given i â€--%sâ€" @@ -10636,8 +10725,8 @@ msgid "Marking reachable objects..." msgstr "Markerar nÃ¥bara objekt..." #, c-format -msgid "%s points nowhere!" -msgstr "%s pekar ingenstans!" +msgid "reflog could not be found: '%s'" +msgstr "refernsloggen hittades inte: â€%sâ€" msgid "no reflog specified to delete" msgstr "ingen referenslogg att ta bort angavs" @@ -10646,6 +10735,15 @@ msgstr "ingen referenslogg att ta bort angavs" msgid "invalid ref format: %s" msgstr "felaktigt referensformat: %s" +msgid "drop the reflogs of all references" +msgstr "kasta referensloggar för alla referenser" + +msgid "drop reflogs from the current worktree only" +msgstr "kasta referensloggar endast för aktuell arbetskatalog" + +msgid "references specified along with --all" +msgstr "referenser angivna tillsammans med --all" + msgid "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]" msgstr "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]" @@ -10753,6 +10851,14 @@ msgstr "" msgid "unknown --mirror argument: %s" msgstr "okänt argument till --mirror: %s" +#, c-format +msgid "remote name '%s' is a subset of existing remote '%s'" +msgstr "fjärrnamnet â€%s†är en delmängd av befintlig fjärr â€%sâ€" + +#, c-format +msgid "remote name '%s' is a superset of existing remote '%s'" +msgstr "fjärrnamnet â€%s†är en övermängd av befintlig fjärr â€%sâ€" + msgid "fetch the remote branches" msgstr "hämta fjärrgrenarna" @@ -11052,14 +11158,6 @@ msgid "Could not set up %s" msgstr "Kunde inte ställa in %s" #, c-format -msgid " %s will become dangling!" -msgstr " %s kommer bli dinglande!" - -#, c-format -msgid " %s has become dangling!" -msgstr " %s har blivit dinglande!" - -#, c-format msgid "Pruning %s" msgstr "Rensar %s" @@ -11123,11 +11221,11 @@ msgstr "var pratsam; mÃ¥ste skrivas före ett underkommando" msgid "" "git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n" -"[--write-midx] [--name-hash-version=<n>]" +"[--write-midx] [--name-hash-version=<n>] [--path-walk]" msgstr "" "git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<paket-namn>]\n" -"[--write-midx] [--name-hash-version=<n>]" +"[--write-midx] [--name-hash-version=<n>] [--path-walk]" msgid "" "Incremental repacks are incompatible with bitmap indexes. Use\n" @@ -11192,6 +11290,9 @@ msgstr "cirkadatum" msgid "with --cruft, expire objects older than this" msgstr "med --cruft, lÃ¥t tid gÃ¥ ut för objekt äldre än detta" +msgid "with --cruft, only repack cruft packs smaller than this" +msgstr "med --cruft, packa bara om onödiga paket mindre än detta" + msgid "remove redundant packs, and run git-prune-packed" msgstr "ta bort överflödiga paket, och kör git-prune-packed" @@ -11207,6 +11308,9 @@ msgstr "" "ange den namnhash-version som ska användas för att gruppera liknande objekt " "efter sökväg" +msgid "pass --path-walk to git-pack-objects" +msgstr "sänd --path-walk till git-pack-objects" + msgid "do not run git-update-server-info" msgstr "kör inte git-update-server-info" @@ -11666,6 +11770,9 @@ msgstr "kan inte hämta diskanvändning för %s" msgid "invalid value for '%s': '%s', the only allowed format is '%s'" msgstr "felaktigt värde för â€%sâ€: â€%sâ€, det enda tillÃ¥tna formatet är â€%sâ€" +msgid "-z option used with unsupported option" +msgstr "flaggan -z använd med en flagga som inte stöds" + msgid "rev-list does not support display of notes" msgstr "rev-list stöder inte visning av anteckningar" @@ -12351,17 +12458,23 @@ msgstr "" msgid "git stash create [<message>]" msgstr "git stash create [<meddelande>]" +msgid "git stash export (--print | --to-ref <ref>) [<stash>...]" +msgstr "git stash export (--print | --to-ref <referens>) [<stash>...]" + +msgid "git stash import <commit>" +msgstr "git stash import <incheckning>" + #, c-format msgid "'%s' is not a stash-like commit" msgstr "â€%s†är inte en â€stashâ€-liknande incheckning" +msgid "No stash entries found." +msgstr "Inga â€stashâ€-poster hittades." + #, c-format msgid "Too many revisions specified:%s" msgstr "För mÃ¥nga revisioner angivna:%s" -msgid "No stash entries found." -msgstr "Inga â€stashâ€-poster hittades." - #, c-format msgid "%s is not a valid reference" msgstr "%s är inte en giltig referens" @@ -12513,19 +12626,74 @@ msgstr "ta med ospÃ¥rade filer i â€stashâ€" msgid "include ignore files" msgstr "ta med ignorerade filer" -msgid "skip and remove all lines starting with comment character" -msgstr "hoppa över och ta bort alla rader som inleds med kommentarstecken" +#, c-format +msgid "cannot parse commit %s" +msgstr "kan inte tolka incheckningen %s" -msgid "prepend comment character and space to each line" -msgstr "lägg in kommentarstecken och blanksteg först pÃ¥ varje rad" +#, c-format +msgid "invalid author or committer for %s" +msgstr "ogiltig författare eller incheckare för %s" + +msgid "could not write commit" +msgstr "kunde inte skriva incheckning" #, c-format -msgid "Expecting a full ref name, got %s" -msgstr "Förväntade fullt referensnamn, fick %s" +msgid "not a valid revision: %s" +msgstr "inte en giltig revision: %s" #, c-format -msgid "could not get a repository handle for submodule '%s'" -msgstr "kunde inte fÃ¥ tag i arkivhandtag för undermodulen â€%sâ€" +msgid "not a commit: %s" +msgstr "inte en incheckning: â€%sâ€" + +#, c-format +msgid "%s is not a valid exported stash commit" +msgstr "%s är inte en giltig exporterad â€stashâ€-incheckning" + +#, c-format +msgid "found root commit %s with invalid data" +msgstr "hittade rotincheckningen %s med ogiltig data" + +#, c-format +msgid "found stash commit %s without expected prefix" +msgstr "hittade â€stashâ€-incheckningen %s utan förväntat prefix" + +#, c-format +msgid "cannot parse parents of commit: %s" +msgstr "kan inte tolka föräldrar för incheckningen: %s" + +#, c-format +msgid "%s does not look like a stash commit" +msgstr "%s ser inte ut som en â€stashâ€-incheckning" + +#, c-format +msgid "cannot read commit buffer for %s" +msgstr "kan inte läsa incheckningsbuffert för %s" + +#, c-format +msgid "cannot save the stash for %s" +msgstr "kan inte spara â€stash†för %s" + +msgid "unable to write base commit" +msgstr "kan inte skriva basincheckning" + +#, c-format +msgid "unable to find stash entry %s" +msgstr "kan inte hitta â€stashâ€-posten %s" + +msgid "print the object ID instead of writing it to a ref" +msgstr "skriv ut objekt-ID istället för skriva det till en referens" + +msgid "save the data to the given ref" +msgstr "spara data till given referens" + +msgid "exactly one of --print and --to-ref is required" +msgstr "exakt en av --print och --to-ref krävs" + +msgid "skip and remove all lines starting with comment character" +msgstr "hoppa över och ta bort alla rader som inleds med kommentarstecken" + +msgid "prepend comment character and space to each line" +msgstr "lägg in kommentarstecken och blanksteg först pÃ¥ varje rad" #, c-format msgid "" @@ -12536,6 +12704,10 @@ msgstr "" "officiella uppström." #, c-format +msgid "could not get a repository handle for submodule '%s'" +msgstr "kunde inte fÃ¥ tag i arkivhandtag för undermodulen â€%sâ€" + +#, c-format msgid "No url found for submodule path '%s' in .gitmodules" msgstr "Hittade ingen url för undermodulsökvägen â€%s†i .gitmodules" @@ -12881,6 +13053,10 @@ msgstr "" "huvudprojektet är inte pÃ¥ nÃ¥gon gren" #, c-format +msgid "Expecting a full ref name, got %s" +msgstr "Förväntade fullt referensnamn, fick %s" + +#, c-format msgid "Unable to find current revision in submodule path '%s'" msgstr "Kan inte hitta aktuell revision i undermodulsökvägen â€%sâ€" @@ -13080,6 +13256,10 @@ msgid "repo URL: '%s' must be absolute or begin with ./|../" msgstr "arkiv-URL: â€%s†mÃ¥ste vara absolut eller börja med ./|../" #, c-format +msgid "submodule name '%s' already used for path '%s'" +msgstr "undermodulnamnet â€%s†används redan för sökvägen â€%sâ€" + +#, c-format msgid "'%s' is not a valid submodule name" msgstr "â€%s†är inte ett giltigt namn pÃ¥ undermodul" @@ -13507,8 +13687,8 @@ msgstr "git update-ref [<flaggor>] -d <refnamn> [<gammalt-oid>]" msgid "git update-ref [<options>] <refname> <new-oid> [<old-oid>]" msgstr "git update-ref [<flaggor>] <refnamn> <gammalt-oid> [<nytt-oid>]" -msgid "git update-ref [<options>] --stdin [-z]" -msgstr "git update-ref [<flaggor>] --stdin [-z]" +msgid "git update-ref [<options>] --stdin [-z] [--batch-updates]" +msgstr "git update-ref [<flaggor>] --stdin [-z] [--batch-updates]" msgid "delete the reference" msgstr "ta bort referensen" @@ -13522,6 +13702,9 @@ msgstr "standard in har NUL-terminerade argument" msgid "read updates from stdin" msgstr "läs uppdateringar frÃ¥n standard in" +msgid "batch reference updates" +msgstr "bunta referensuppdateringar" + msgid "update the info files from scratch" msgstr "uppdatera informationsfilerna frÃ¥n grunden" @@ -13702,10 +13885,6 @@ msgid "Preparing worktree (checking out '%s')" msgstr "Förbereder arbetskatalog (checkar ut â€%sâ€)" #, c-format -msgid "unreachable: invalid reference: %s" -msgstr "onÃ¥bar: felaktig referens: %s" - -#, c-format msgid "Preparing worktree (detached HEAD %s)" msgstr "Förbereder arbetskatalog (frÃ¥nkopplat HEAD %s)" @@ -14201,6 +14380,9 @@ msgstr "Jämför filer i arbetskatalogen och indexet" msgid "Compare a tree to the working tree or index" msgstr "Jämför en träd med arbetskatalogen eller indexet" +msgid "Compare the content and mode of provided blob pairs" +msgstr "Jämför innehÃ¥ll och läge för det angivna blob-paret" + msgid "Compares the content and mode of blobs found via two tree objects" msgstr "Visar innehÃ¥ll och läge för blob:ar som hittats via tvÃ¥ trädobjekt" @@ -15306,14 +15488,6 @@ msgid "bad numeric config value '%s' for '%s' in %s: %s" msgstr "felaktigt numeriskt konfigurationsvärde â€%s†för â€%s†i %s: %s" #, c-format -msgid "invalid value for variable %s" -msgstr "ogiltigt värde för variabeln %s" - -#, c-format -msgid "ignoring unknown core.fsync component '%s'" -msgstr "ignorerar okänd core.fsync-komponent â€%sâ€" - -#, c-format msgid "bad boolean config value '%s' for '%s'" msgstr "felaktigt booleskt konfigurationsvärde â€%s†för â€%sâ€" @@ -15326,44 +15500,6 @@ msgid "'%s' for '%s' is not a valid timestamp" msgstr "â€%s†för â€%s†är inte en giltig tidsstämpel" #, c-format -msgid "abbrev length out of range: %d" -msgstr "förkortningslängd utanför intervallet: %d" - -#, c-format -msgid "bad zlib compression level %d" -msgstr "felaktigt zlib-komprimeringsgrad %d" - -#, c-format -msgid "%s cannot contain newline" -msgstr "%s kan inte innehÃ¥lla nyradstecken" - -#, c-format -msgid "%s must have at least one character" -msgstr "%s mÃ¥ste innehÃ¥lla minst ett tecken" - -#, c-format -msgid "ignoring unknown core.fsyncMethod value '%s'" -msgstr "ignorerar okänt core.fsyncMethod-värde â€%sâ€" - -msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" -msgstr "core.fsyncObjectFiles avrÃ¥ds frÃ¥n; använd core.fsync istället" - -#, c-format -msgid "invalid mode for object creation: %s" -msgstr "felaktigt läge för skapande av objekt: %s" - -#, c-format -msgid "malformed value for %s" -msgstr "felformat värde för %s" - -#, c-format -msgid "malformed value for %s: %s" -msgstr "felformat värde för %s: %s" - -msgid "must be one of nothing, matching, simple, upstream or current" -msgstr "mÃ¥ste vara en av nothing, matching, simple, upstream eller current" - -#, c-format msgid "unable to load config blob object '%s'" msgstr "kan inte läsa konfigurerings-blobobjektet â€%sâ€" @@ -15871,8 +16007,9 @@ msgstr "kan inte jämföra standard in med en katalog" msgid "cannot compare a named pipe to a directory" msgstr "kan inte jämföra ett namngivet rör med en katalog" -msgid "git diff --no-index [<options>] <path> <path>" -msgstr "git diff --no-index [<flaggor>] <sökväg> <sökväg>" +msgid "git diff --no-index [<options>] <path> <path> [<pathspec>...]" +msgstr "" +"git diff --no-index [<flaggor>] <sökväg> <sökväg> [<sökvägsangivelse>...]" msgid "" "Not a git repository. Use --no-index to compare two paths outside a working " @@ -15881,6 +16018,13 @@ msgstr "" "Inte ett git-arkiv. Använd --no-index för att jämföra tvÃ¥ sökvägar utanför " "en arbetskatalog." +msgid "" +"Limiting comparison with pathspecs is only supported if both paths are " +"directories." +msgstr "" +"Det är endast möjligt att begränsa jämförelsen med sökvägsangivelser om " +"bägge sökvägarna är kataloger." + #, c-format msgid " Failed to parse dirstat cut-off percentage '%s'\n" msgstr " Misslyckades tolka dirstat-avskärningsprocentandel â€%sâ€\n" @@ -16452,6 +16596,52 @@ msgid "bad git namespace path \"%s\"" msgstr "felaktig git-namnrymdssökväg â€%sâ€" #, c-format +msgid "invalid value for variable %s" +msgstr "ogiltigt värde för variabeln %s" + +#, c-format +msgid "ignoring unknown core.fsync component '%s'" +msgstr "ignorerar okänd core.fsync-komponent â€%sâ€" + +#, c-format +msgid "abbrev length out of range: %d" +msgstr "förkortningslängd utanför intervallet: %d" + +#, c-format +msgid "bad zlib compression level %d" +msgstr "felaktigt zlib-komprimeringsgrad %d" + +#, c-format +msgid "%s cannot contain newline" +msgstr "%s kan inte innehÃ¥lla nyradstecken" + +#, c-format +msgid "%s must have at least one character" +msgstr "%s mÃ¥ste innehÃ¥lla minst ett tecken" + +#, c-format +msgid "ignoring unknown core.fsyncMethod value '%s'" +msgstr "ignorerar okänt core.fsyncMethod-värde â€%sâ€" + +msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" +msgstr "core.fsyncObjectFiles avrÃ¥ds frÃ¥n; använd core.fsync istället" + +#, c-format +msgid "invalid mode for object creation: %s" +msgstr "felaktigt läge för skapande av objekt: %s" + +#, c-format +msgid "malformed value for %s" +msgstr "felformat värde för %s" + +#, c-format +msgid "malformed value for %s: %s" +msgstr "felformat värde för %s: %s" + +msgid "must be one of nothing, matching, simple, upstream or current" +msgstr "mÃ¥ste vara en av nothing, matching, simple, upstream eller current" + +#, c-format msgid "too many args to run %s" msgstr "för mÃ¥nga flaggor för att köra %s" @@ -17077,6 +17267,10 @@ msgid "Unknown value for http.proactiveauth" msgstr "Okänt värde för http.proactiveauth" #, c-format +msgid "failed to parse %s" +msgstr "misslyckades tolka %s" + +#, c-format msgid "Unsupported SSL backend '%s'. Supported SSL backends:" msgstr "SSL-bakändan â€%s†stöds inte. Dessa SSL-bakändor stöds:" @@ -17157,6 +17351,29 @@ msgstr "tomt ident-namn (för <%s>) ej tillÃ¥tet" msgid "name consists only of disallowed characters: %s" msgstr "namnet bestÃ¥r enbart av ej tillÃ¥tna tecken: %s" +msgid "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>" +msgstr "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <mapp>] < <mbox>" + +msgid "no IMAP host specified" +msgstr "ingen IMAP-värd angavs" + +msgid "" +"set the IMAP host with 'git config imap.host <host>'.\n" +"(e.g., 'git config imap.host imaps://imap.example.com')" +msgstr "" +"ställ in IMAP-värden med â€git config imap.host <värd>â€.\n" +"(t.ex., â€git config imap.host imaps://imap.example.comâ€)" + +msgid "no IMAP folder specified" +msgstr "ingen IMAP-mapp angavs" + +msgid "" +"set the target folder with 'git config imap.folder <folder>'.\n" +"(e.g., 'git config imap.folder Drafts')" +msgstr "" +"ställ in IMAP-mappen med â€git config imap.folder <mapp>â€.\n" +"(t.ex., â€git config imap.folder Utkastâ€)" + msgid "expected 'tree:<depth>'" msgstr "förväntade â€tree:<djup>â€" @@ -17263,6 +17480,10 @@ msgid "invalid marker-size '%s', expecting an integer" msgstr "felaktigt värde för marker-size â€%sâ€, förväntade ett heltal" #, c-format +msgid "Could not parse object '%s'" +msgstr "Kunde inte tolka objektet â€%sâ€" + +#, c-format msgid "Failed to merge submodule %s (not checked out)" msgstr "Misslyckades slÃ¥ ihop undermodulen %s (ej utcheckad)" @@ -17504,264 +17725,6 @@ msgstr "" msgid "collecting merge info failed for trees %s, %s, %s" msgstr "samling av sammanslagningsinfo misslyckades för träden %s, %s, %s" -msgid "(bad commit)\n" -msgstr "(felaktig incheckning)\n" - -#, c-format -msgid "add_cacheinfo failed for path '%s'; merge aborting." -msgstr "" -"add_cacheinfo misslyckades för sökvägen â€%sâ€; avslutar sammanslagningen." - -#, c-format -msgid "add_cacheinfo failed to refresh for path '%s'; merge aborting." -msgstr "" -"add_cacheinfo misslyckades uppdatera för sökvägen â€%sâ€; avslutar " -"sammanslagningen." - -#, c-format -msgid "failed to create path '%s'%s" -msgstr "misslyckades skapa sökvägen â€%sâ€%s" - -#, c-format -msgid "Removing %s to make room for subdirectory\n" -msgstr "Tar bort %s för att göra plats för underkatalog\n" - -msgid ": perhaps a D/F conflict?" -msgstr ": kanske en K/F-konflikt?" - -#, c-format -msgid "refusing to lose untracked file at '%s'" -msgstr "vägrar förlora ospÃ¥rad fil vid â€%sâ€" - -#, c-format -msgid "blob expected for %s '%s'" -msgstr "blob förväntades för %s â€%sâ€" - -#, c-format -msgid "failed to open '%s': %s" -msgstr "misslyckades öppna â€%sâ€: %s" - -#, c-format -msgid "failed to symlink '%s': %s" -msgstr "misslyckades skapa symboliska länken â€%sâ€: %s" - -#, c-format -msgid "do not know what to do with %06o %s '%s'" -msgstr "vet inte hur %06o %s â€%s†ska hanteras" - -#, c-format -msgid "Failed to merge submodule %s (repository corrupt)" -msgstr "Misslyckades slÃ¥ ihop undermodulen %s (arkivet är trasigt)" - -#, c-format -msgid "Fast-forwarding submodule %s to the following commit:" -msgstr "Snabbspolar undermodulen %s till följande incheckning:" - -#, c-format -msgid "Fast-forwarding submodule %s" -msgstr "Snabbspolar undermodulen %s" - -#, c-format -msgid "Failed to merge submodule %s (merge following commits not found)" -msgstr "" -"Misslyckades slÃ¥ ihop undermodulen %s (sammanslagning efter incheckningar " -"hittades inte)" - -#, c-format -msgid "Failed to merge submodule %s (not fast-forward)" -msgstr "Misslyckades slÃ¥ ihop undermodulen %s (ej snabbspolning)" - -msgid "Found a possible merge resolution for the submodule:\n" -msgstr "Hittade en möjlig lösning av sammanslagning för undermodulen:\n" - -#, c-format -msgid "" -"If this is correct simply add it to the index for example\n" -"by using:\n" -"\n" -" git update-index --cacheinfo 160000 %s \"%s\"\n" -"\n" -"which will accept this suggestion.\n" -msgstr "" -"Om detta är riktigt lägger du bara till det i indexet, till\n" -"exempel sÃ¥ här:\n" -"\n" -" git update-index --cacheinfo 160000 %s \"%s\"\n" -"\n" -"vilket godtar lösningen.\n" - -#, c-format -msgid "Failed to merge submodule %s (multiple merges found)" -msgstr "" -"Misslyckades slÃ¥ ihop undermodulen %s (flera sammanslagningar hittades)" - -msgid "failed to execute internal merge" -msgstr "misslyckades exekvera intern sammanslagning" - -#, c-format -msgid "unable to add %s to database" -msgstr "kan inte lägga till %s till databasen" - -#, c-format -msgid "Error: Refusing to lose untracked file at %s; writing to %s instead." -msgstr "Fel: Vägrar förlora ospÃ¥rad fil vid %s; skriver till %s istället." - -#, c-format -msgid "" -"CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left " -"in tree." -msgstr "" -"KONFLIKT (%s/radera): %s raderad i %s och %s i %s. Versionen %s av %s lämnad " -"i trädet." - -#, c-format -msgid "" -"CONFLICT (%s/delete): %s deleted in %s and %s to %s in %s. Version %s of %s " -"left in tree." -msgstr "" -"KONFLIKT (%s/radera): %s raderad i %s och %s till %s i %s. Versionen %s av " -"%s lämnad i trädet." - -#, c-format -msgid "" -"CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left " -"in tree at %s." -msgstr "" -"KONFLIKT (%s/radera): %s raderad i %s och %s i %s. Versionen %s av %s lämnad " -"i trädet vid %s." - -#, c-format -msgid "" -"CONFLICT (%s/delete): %s deleted in %s and %s to %s in %s. Version %s of %s " -"left in tree at %s." -msgstr "" -"KONFLIKT (%s/radera): %s raderad i %s och %s till %s i %s. Versionen %s av " -"%s lämnad i trädet vid %s." - -msgid "rename" -msgstr "namnbyte" - -msgid "renamed" -msgstr "namnbytt" - -#, c-format -msgid "Refusing to lose dirty file at %s" -msgstr "Vägrar förlora lortig fil vid â€%sâ€" - -#, c-format -msgid "Refusing to lose untracked file at %s, even though it's in the way." -msgstr "Vägrar förlora ospÃ¥rad fil vid %s, trots att den är i vägen." - -#, c-format -msgid "CONFLICT (rename/add): Rename %s->%s in %s. Added %s in %s" -msgstr "KONFLIKT (namnbyte/tillägg): Namnbyte %s→%s i %s. Lade till %s i %s" - -#, c-format -msgid "%s is a directory in %s adding as %s instead" -msgstr "%s är en katalog i %s lägger till som %s istället" - -#, c-format -msgid "Refusing to lose untracked file at %s; adding as %s instead" -msgstr "Vägrar förlora ospÃ¥rad fil vid %s; lägger till som %s istället" - -#, c-format -msgid "" -"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename " -"\"%s\"->\"%s\" in \"%s\"%s" -msgstr "" -"KONFLIKT (namnbyte/namnbyte): Namnbyte â€%sâ€â†’â€%s†pÃ¥ grenen â€%s†namnbyte " -"â€%sâ€â†’â€%s†i â€%sâ€%s" - -msgid " (left unresolved)" -msgstr " (lämnad olöst)" - -#, c-format -msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s" -msgstr "KONFLIKT (namnbyte/namnbyte): Namnbyte %s→%s i %s. Namnbyte %s→%s i %s" - -#, c-format -msgid "" -"CONFLICT (directory rename split): Unclear where to place %s because " -"directory %s was renamed to multiple other directories, with no destination " -"getting a majority of the files." -msgstr "" -"KONFLIKT (namnändrad delad katalog): Osäker pÃ¥ var %s ska placeras dÃ¥ " -"katalogen %s bytte namn till flera andra kataloger, utan att nÃ¥gon " -"destination fick en majoritet av filerna." - -#, c-format -msgid "" -"CONFLICT (rename/rename): Rename directory %s->%s in %s. Rename directory %s-" -">%s in %s" -msgstr "" -"KONFLIKT (namnbyte/namnbyte): Namnbytt katalog %s→%s i %s. Namnbytt katalog " -"%s→%s i %s" - -#, c-format -msgid "cannot read object %s" -msgstr "kan inte läsa objektet %s" - -#, c-format -msgid "object %s is not a blob" -msgstr "objektet %s är inte en blob" - -msgid "modify" -msgstr "ändra" - -msgid "modified" -msgstr "ändrad" - -#, c-format -msgid "Skipped %s (merged same as existing)" -msgstr "Hoppade över %s (sammanslagen samma som befintlig)" - -#, c-format -msgid "Adding as %s instead" -msgstr "Lägger till som %s istället" - -#, c-format -msgid "Removing %s" -msgstr "Tar bort %s" - -msgid "file/directory" -msgstr "fil/katalog" - -msgid "directory/file" -msgstr "katalog/fil" - -#, c-format -msgid "CONFLICT (%s): There is a directory with name %s in %s. Adding %s as %s" -msgstr "" -"KONFLIKT (%s): Det finns en katalog med namnet %s i %s. Lägger till %s som %s" - -#, c-format -msgid "Adding %s" -msgstr "Lägger till %s" - -#, c-format -msgid "CONFLICT (add/add): Merge conflict in %s" -msgstr "KONFLIKT (tillägg/tillägg): Sammanslagningskonflikt i %s" - -#, c-format -msgid "merging of trees %s and %s failed" -msgstr "sammanslagning av träden %s och %s misslyckades" - -msgid "Merging:" -msgstr "SlÃ¥r ihop:" - -#, c-format -msgid "found %u common ancestor:" -msgid_plural "found %u common ancestors:" -msgstr[0] "hittade %u gemensam förfader:" -msgstr[1] "hittade %u gemensamma förfäder:" - -msgid "merge returned no commit" -msgstr "sammanslagningen returnerade ingen incheckning" - -#, c-format -msgid "Could not parse object '%s'" -msgstr "Kunde inte tolka objektet â€%sâ€" - msgid "failed to read the cache" msgstr "misslyckades läsa cachen" @@ -17803,12 +17766,13 @@ msgstr "kan inte länka â€%s†till â€%sâ€" msgid "failed to clear multi-pack-index at %s" msgstr "misslyckades städa multi-pack-index pÃ¥ %s" -msgid "cannot write incremental MIDX with bitmap" -msgstr "kan inte skriva inkrementell MIDX med bitkarta" - msgid "ignoring existing multi-pack-index; checksum mismatch" msgstr "ignorerar befintlig multi-pack-index; felaktig kontrollsumma" +#, c-format +msgid "could not load reverse index for MIDX %s" +msgstr "kunde inte läsa in baklängesindexet för MIDX %s" + msgid "Adding packfiles to multi-pack-index" msgstr "Lägger till paketfiler till multi-pack-index" @@ -18063,63 +18027,6 @@ msgid "Failed to convert object from %s to %s" msgstr "Misslyckades konvertera objekt frÃ¥n %s till %s" #, c-format -msgid "object directory %s does not exist; check .git/objects/info/alternates" -msgstr "objektkatalogen %s finns inte; se .git/objects/info/alternates" - -#, c-format -msgid "unable to normalize alternate object path: %s" -msgstr "kan inte normalisera supplerande objektsökväg: %s" - -#, c-format -msgid "%s: ignoring alternate object stores, nesting too deep" -msgstr "%s: ignorerar supplerande objektlager, för djup nästling" - -msgid "unable to fdopen alternates lockfile" -msgstr "kan inte utföra â€fdopen†pÃ¥ suppleantlÃ¥sfil" - -msgid "unable to read alternates file" -msgstr "kan inte läsa â€alternatesâ€-filen" - -msgid "unable to move new alternates file into place" -msgstr "kan inte flytta ny â€alternatesâ€-fil pÃ¥ plats" - -#, c-format -msgid "path '%s' does not exist" -msgstr "sökvägen â€%s†finns inte" - -#, c-format -msgid "reference repository '%s' as a linked checkout is not supported yet." -msgstr "referensarkivet â€%s†som en länkad utcheckning stöds inte ännu." - -#, c-format -msgid "reference repository '%s' is not a local repository." -msgstr "referensarkivet â€%s†är inte ett lokalt arkiv." - -#, c-format -msgid "reference repository '%s' is shallow" -msgstr "referensarkivet â€%s†är grunt" - -#, c-format -msgid "reference repository '%s' is grafted" -msgstr "referensarkivet â€%s†är ympat" - -#, c-format -msgid "could not find object directory matching %s" -msgstr "kunde inte hitta objektkatalog för %s" - -#, c-format -msgid "invalid line while parsing alternate refs: %s" -msgstr "felaktig rad vid tolkning av supplerande referenser: %s" - -#, c-format -msgid "attempting to mmap %<PRIuMAX> over limit %<PRIuMAX>" -msgstr "försök att utföra â€mmap†pÃ¥ %<PRIuMAX> över gränsen %<PRIuMAX>" - -#, c-format -msgid "mmap failed%s" -msgstr "mmap misslyckades%s" - -#, c-format msgid "object file %s is empty" msgstr "objektfilen %s är tom" @@ -18155,18 +18062,6 @@ msgid "loose object %s (stored in %s) is corrupt" msgstr "löst objekt %s (lagrat i %s) är trasigt" #, c-format -msgid "replacement %s not found for %s" -msgstr "ersättningen %s hittades inte för %s" - -#, c-format -msgid "packed object %s (stored in %s) is corrupt" -msgstr "packat objekt %s (lagrat i %s) är trasigt" - -#, c-format -msgid "missing mapping of %s to %s" -msgstr "saknar koppling av %s till %s" - -#, c-format msgid "unable to open %s" msgstr "kan inte öppna %s" @@ -18260,10 +18155,6 @@ msgid "%s: unsupported file type" msgstr "%s: filtypen stöds ej" #, c-format -msgid "%s is not a valid '%s' object" -msgstr "%s är inte ett giltigt â€%sâ€-objekt" - -#, c-format msgid "hash mismatch for %s (expected %s)" msgstr "hash stämmer inte för %s (förväntade %s)" @@ -18280,6 +18171,10 @@ msgid "unable to parse header of %s" msgstr "kan inte tolka huvud för %s" #, c-format +msgid "unable to parse type from header '%s' of %s" +msgstr "kan inte tolka typen frÃ¥n huvudet â€%s†för %s" + +#, c-format msgid "unable to unpack contents of %s" msgstr "kan inte tolka innehÃ¥ll i %s" @@ -18456,6 +18351,71 @@ msgid "hash mismatch %s" msgstr "hashvärde stämmer inte överens %s" #, c-format +msgid "object directory %s does not exist; check .git/objects/info/alternates" +msgstr "objektkatalogen %s finns inte; se .git/objects/info/alternates" + +#, c-format +msgid "unable to normalize alternate object path: %s" +msgstr "kan inte normalisera supplerande objektsökväg: %s" + +#, c-format +msgid "%s: ignoring alternate object stores, nesting too deep" +msgstr "%s: ignorerar supplerande objektlager, för djup nästling" + +msgid "unable to fdopen alternates lockfile" +msgstr "kan inte utföra â€fdopen†pÃ¥ suppleantlÃ¥sfil" + +msgid "unable to read alternates file" +msgstr "kan inte läsa â€alternatesâ€-filen" + +msgid "unable to move new alternates file into place" +msgstr "kan inte flytta ny â€alternatesâ€-fil pÃ¥ plats" + +#, c-format +msgid "path '%s' does not exist" +msgstr "sökvägen â€%s†finns inte" + +#, c-format +msgid "reference repository '%s' as a linked checkout is not supported yet." +msgstr "referensarkivet â€%s†som en länkad utcheckning stöds inte ännu." + +#, c-format +msgid "reference repository '%s' is not a local repository." +msgstr "referensarkivet â€%s†är inte ett lokalt arkiv." + +#, c-format +msgid "reference repository '%s' is shallow" +msgstr "referensarkivet â€%s†är grunt" + +#, c-format +msgid "reference repository '%s' is grafted" +msgstr "referensarkivet â€%s†är ympat" + +#, c-format +msgid "could not find object directory matching %s" +msgstr "kunde inte hitta objektkatalog för %s" + +#, c-format +msgid "invalid line while parsing alternate refs: %s" +msgstr "felaktig rad vid tolkning av supplerande referenser: %s" + +#, c-format +msgid "replacement %s not found for %s" +msgstr "ersättningen %s hittades inte för %s" + +#, c-format +msgid "packed object %s (stored in %s) is corrupt" +msgstr "packat objekt %s (lagrat i %s) är trasigt" + +#, c-format +msgid "missing mapping of %s to %s" +msgstr "saknar koppling av %s till %s" + +#, c-format +msgid "%s is not a valid '%s' object" +msgstr "%s är inte ett giltigt â€%sâ€-objekt" + +#, c-format msgid "duplicate entry when writing bitmap index: %s" msgstr "duplicerad post när bitkarteindex skulle skrivas: %s" @@ -18466,9 +18426,6 @@ msgstr "försökta lagra icke-vald incheckning: â€%sâ€" msgid "too many pseudo-merges" msgstr "för mÃ¥nga pseudo-sammanslagningar" -msgid "trying to write commit not in index" -msgstr "försöker skriva incheckning som inte finns i indexet" - msgid "failed to load bitmap index (corrupted?)" msgstr "misslyckade läsa in bitkarteindex (trasigt?)" @@ -18713,6 +18670,18 @@ msgid "%s isn't available" msgstr "%s är inte tillgängligt" #, c-format +msgid "value for %s exceeds %<PRIdMAX>" +msgstr "värdet för %s överstiger %<PRIdMAX>" + +#, c-format +msgid "value %s for %s not in range [%<PRIdMAX>,%<PRIdMAX>]" +msgstr "värdet %s för %s inte i intervallet [%<PRIdMAX>,%<PRIdMAX>]" + +#, c-format +msgid "%s expects an integer value with an optional k/m/g suffix" +msgstr "%s förväntar ett heltalsvärde, med valfritt k/m/g-suffix" + +#, c-format msgid "%s expects a non-negative integer value with an optional k/m/g suffix" msgstr "%s förväntar ett icke-negativt heltalsvärde, med valfritt k/m/g-suffix" @@ -18870,10 +18839,6 @@ msgid "bad boolean environment value '%s' for '%s'" msgstr "felaktigt booleskt miljövariabelvärde â€%s†för â€%sâ€" #, c-format -msgid "failed to parse %s" -msgstr "misslyckades tolka %s" - -#, c-format msgid "failed to walk children of tree %s: not found" msgstr "misslyckades traversera löven i trädet %s: hittades inte" @@ -19034,8 +18999,12 @@ msgid "could not fetch %s from promisor remote" msgstr "kunde inte hämta %s frÃ¥n kontraktsfjärr" #, c-format -msgid "known remote named '%s' but with url '%s' instead of '%s'" -msgstr "känd fjärr som heter â€%s†med med url:en â€%s†istället för â€%sâ€" +msgid "no or empty URL advertised for remote '%s'" +msgstr "ingen eller tom URL tillkännagavs för fjärren â€%sâ€" + +#, c-format +msgid "known remote named '%s' but with URL '%s' instead of '%s'" +msgstr "känd fjärr som heter â€%s†men med URL:en â€%s†istället för â€%sâ€" #, c-format msgid "unknown '%s' value for '%s' config option" @@ -19678,6 +19647,14 @@ msgid "%s does not point to a valid object!" msgstr "â€%s†pekar inte pÃ¥ ett giltigt objekt!" #, c-format +msgid "%s%s will become dangling after %s is deleted\n" +msgstr "%s%s kommer bli dinglande efter att %s tagits bort\n" + +#, c-format +msgid "%s%s has become dangling after %s was deleted\n" +msgstr "%s%s har blivit dinglande efter att %s togs bort\n" + +#, c-format msgid "" "Using '%s' as the name for the initial branch. This default branch name\n" "is subject to change. To configure the initial branch name to use in all\n" @@ -19805,6 +19782,10 @@ msgid "Checking references consistency" msgstr "Kontrollerar konsistens för referenser" #, c-format +msgid "unable to open '%s'" +msgstr "kan inte öppna â€%sâ€" + +#, c-format msgid "refname is dangerous: %s" msgstr "refnamnet är farligt: %s" @@ -19871,10 +19852,6 @@ msgid "refname %s is a symbolic ref, copying it is not supported" msgstr "referensnamnet %s är en symbolisk referens, kopiering stöds inte" #, c-format -msgid "invalid refspec '%s'" -msgstr "felaktig referensspecifikation: â€%sâ€" - -#, c-format msgid "pattern '%s' has no '*'" msgstr "mönstret â€%s†innehÃ¥ller ingen â€*â€" @@ -20413,8 +20390,8 @@ msgstr "kunde inte lägga till enrollering" msgid "could not set recommended config" msgstr "kan inte ange rekommenderad konfiguration" -msgid "could not turn on maintenance" -msgstr "kunde inte aktivera underhÃ¥ll" +msgid "could not toggle maintenance" +msgstr "kunde inte växla underhÃ¥ll" msgid "could not start the FSMonitor daemon" msgstr "kunde inte starta FSMonitor-server" @@ -20460,12 +20437,15 @@ msgstr "skapa arkiv inuti katalogen â€srcâ€" msgid "specify if tags should be fetched during clone" msgstr "ange om taggar ska hämtas vid kloning" +msgid "specify if background maintenance should be enabled" +msgstr "ange om bakgrundsunderhÃ¥ll ska aktiveras" + msgid "" "scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n" -"\t[--[no-]src] [--[no-]tags] <url> [<enlistment>]" +"\t[--[no-]src] [--[no-]tags] [--[no-]maintenance] <url> [<enlistment>]" msgstr "" "scalar clone [--single-branch] [--branch <huvudgren>] [--full-clone]\n" -"\t[--[no-]src] [--[no-]tags] <url> [<enrollering>]" +"\t[--[no-]src] [--[no-]tags] [--[no-]maintenance] <url> [<enrollering>]" #, c-format msgid "cannot deduce worktree name from '%s'" @@ -20503,19 +20483,33 @@ msgstr "scalar diagnose [<enrollering>]" msgid "`scalar list` does not take arguments" msgstr "â€scalar list†tar inte argument" -msgid "scalar register [<enlistment>]" -msgstr "scalar register [<enrollering>]" +msgid "scalar register [--[no-]maintenance] [<enlistment>]" +msgstr "scalar register [--[no-]maintenance] [<enrollering>]" msgid "reconfigure all registered enlistments" msgstr "konfigurera alla registrerade enrolleringar pÃ¥ nytt" -msgid "scalar reconfigure [--all | <enlistment>]" -msgstr "scalar reconfigure [--all | <enrollering>]" +msgid "(enable|disable|keep)" +msgstr "(aktivera|inaktivera|behÃ¥ll)" + +msgid "signal how to adjust background maintenance" +msgstr "signallera hur bakgrundsunderhÃ¥ll ska justeras" + +msgid "" +"scalar reconfigure [--maintenance=(enable|disable|keep)] [--all | " +"<enlistment>]" +msgstr "" +"scalar reconfigure [--maintenance=(enable|disable|keep)] [--all | " +"<enrollering>]" msgid "--all or <enlistment>, but not both" msgstr "--all eller <enrollering>, men inte bägge" #, c-format +msgid "unknown mode for --maintenance option: %s" +msgstr "okänt läge för flaggan --maintenance: %s" + +#, c-format msgid "could not remove stale scalar.repo '%s'" msgstr "kunde inte ta bort gammal scalar.repo â€%sâ€" @@ -21946,6 +21940,9 @@ msgstr "töm cacheträdet före varje iteration" msgid "number of entries in the cache tree to invalidate (default 0)" msgstr "antal poster i cacheträdet att ogiltigförklara (förval är 0)" +msgid "the number of objects to write" +msgstr "antal objekt att skriva" + msgid "test-tool path-walk <options> -- <revision-options>" msgstr "test-tool path-walk <flaggor> -- <revision-flaggor>" @@ -21964,6 +21961,9 @@ msgstr "växla om trädobjekt ska vara med eller inte" msgid "toggle pruning of uninteresting paths" msgstr "växla bortrensning av ointressanta sökvägar" +msgid "toggle aggressive edge walk" +msgstr "växla aggressiv kantvandring" + msgid "read a pattern list over stdin" msgstr "läs en mönsterlista frÃ¥n standard in" @@ -22601,6 +22601,23 @@ msgid "warning: " msgstr "varning: " #, c-format +msgid "" +"'%s' is nominated for removal.\n" +"If you still use this command, please add an extra\n" +"option, '--i-still-use-this', on the command line\n" +"and let us know you still use it by sending an e-mail\n" +"to <git@vger.kernel.org>. Thanks.\n" +msgstr "" +"â€%s†har nominerats för borttagning.\n" +"Om du fortfarande använder kommandot, lägg till flaggan\n" +"â€--i-still-use-this†pÃ¥ kommandoraden och berätta för\n" +"oss att du fortfarande använder det pÃ¥ e-post till\n" +"<git@vger.kernel.org>. Tack.\n" + +msgid "refusing to run without --i-still-use-this" +msgstr "vägrar köra utan --i-still-use-this" + +#, c-format msgid "uname() failed with error '%s' (%d)\n" msgstr "uname() misslyckades med felet â€%s†(%d)\n" @@ -22719,6 +22736,14 @@ msgstr "kan inte hämta aktuell arbetskatalog" msgid "unable to get random bytes" msgstr "kan inte hämta slumpdata" +#, c-format +msgid "attempting to mmap %<PRIuMAX> over limit %<PRIuMAX>" +msgstr "försök att utföra â€mmap†pÃ¥ %<PRIuMAX> över gränsen %<PRIuMAX>" + +#, c-format +msgid "mmap failed%s" +msgstr "mmap misslyckades%s" + msgid "Unmerged paths:" msgstr "Ej sammanslagna sökvägar:" @@ -23450,6 +23475,13 @@ msgstr "" "smtp-debug." #, perl-format +msgid "Outlook reassigned Message-ID to: %s\n" +msgstr "Outlook gav nytt Message-ID till: %s\n" + +msgid "Warning: Could not retrieve Message-ID from server response.\n" +msgstr "Varning: Kunde inte hämta Message-ID frÃ¥n serversvaret.\n" + +#, perl-format msgid "Failed to send %s\n" msgstr "Misslyckades sända %s\n" @@ -23494,6 +23526,10 @@ msgid "(body) Adding cc: %s from line '%s'\n" msgstr "(kropp) Lägger till cc: %s frÃ¥n raden â€%sâ€\n" #, perl-format +msgid "error: invalid SMTP port '%s'\n" +msgstr "fel: ogiltig SMTP-port â€%sâ€\n" + +#, perl-format msgid "(%s) Could not execute '%s'" msgstr "(%s) Kunde inte köra â€%sâ€" @@ -96,8 +96,8 @@ msgid "" msgstr "" "Project-Id-Version: Git Turkish Localization Project\n" "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n" -"POT-Creation-Date: 2025-05-29 11:02+0300\n" -"PO-Revision-Date: 2025-05-29 12:00+0300\n" +"POT-Creation-Date: 2025-08-14 16:38+0300\n" +"PO-Revision-Date: 2025-08-14 16:45+0300\n" "Last-Translator: Emir SARI <emir_sari@icloud.com>\n" "Language-Team: Turkish (https://github.com/bitigchi/git-po/)\n" "Language: tr\n" @@ -107,6 +107,10 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #, c-format +msgid "%s cannot be negative" +msgstr "%s negatif olamaz" + +#, c-format msgid "Huh (%s)?" msgstr "Pardon (%s)?" @@ -2011,6 +2015,10 @@ msgid "adding files failed" msgstr "dosya ekleme baÅŸarısız" #, c-format +msgid "'%s' cannot be negative" +msgstr "'%s' negatif olamaz" + +#, c-format msgid "--chmod param '%s' must be either -x or +x" msgstr "--chmod param '%s' ya -x ya da +x olmalıdır" @@ -5163,23 +5171,25 @@ msgstr "" msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp] [--value=<value>] [--fixed-value] [--default=<default>] <name>" +"regexp] [--value=<pattern>] [--fixed-value] [--default=<default>] [--" +"url=<url>] <name>" msgstr "" -"git config get [<dosya-sçnÄŸi>] [<görüntü-sçnÄŸi>] [--includes] [--all] [--" -"regexp] [--value=<deÄŸer>] [--fixed-value] [--default=<öntanımlı>] <ad>" +"git config get [<dosya-seçeneÄŸi>] [<display-option>] [--includes] [--all] [--" +"regexp] [--value=<dizgi>] [--fixed-value] [--default=<öntanımlı>] [--" +"url=<url>] <ad>" msgid "" -"git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--" -"fixed-value] <name> <value>" +"git config set [<file-option>] [--type=<type>] [--all] [--value=<pattern>] " +"[--fixed-value] <name> <value>" msgstr "" -"git config set [<dosya-seçeneÄŸi>] [--type=<tür>] [--all] [--value=<deÄŸer>] " +"git config set [<dosya-seçeneÄŸi>] [--type=<tür>] [--all] [--value=<dizgi>] " "[--fixed-value] <ad> <deÄŸer>" msgid "" -"git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] " +"git config unset [<file-option>] [--all] [--value=<pattern>] [--fixed-value] " "<name>" msgstr "" -"git config unset [<dosya-seçeneÄŸi>] [--all] [--value=<deÄŸer>] [--fixed-" +"git config unset [<dosya-seçeneÄŸi>] [--all] [--value=<dizgi>] [--fixed-" "value] <ad>" msgid "git config rename-section [<file-option>] <old-name> <new-name>" @@ -5196,19 +5206,19 @@ msgstr "git config [<dosya-seçeneÄŸi>] --get-colorbool <ad> [<stdout-tty>]" msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] " +"regexp=<regexp>] [--value=<pattern>] [--fixed-value] [--default=<default>] " "<name>" msgstr "" -"git config get [<dosya-seçeneÄŸi>] [<görüntüleme-seçeneÄŸi>] [--includes] [--" -"all] [--regexp=<düzenli-ifade>] [--value=<deÄŸer>] [--fixed-value] [--" +"git config get [<dosya-seçeneÄŸi>] [<görüntü-seçeneÄŸi>] [--includes] [--all] " +"[--regexp=<düzenli-ifade>] [--value=<dizgi>] [--fixed-value] [--" "default=<öntanımlı>] <ad>" msgid "" "git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] " -"[--value=<value>] [--fixed-value] <name> <value>" +"[--value=<pattern>] [--fixed-value] <name> <value>" msgstr "" "git config set [<dosya-seçeneÄŸi>] [--type=<tür>] [--comment=<ileti>] [--all] " -"[--value=<deÄŸer>] [--fixed-value] <ad> <deÄŸer>" +"[--value=<dizgi>] [--fixed-value] <ad> <deÄŸer>" msgid "Config file location" msgstr "Yapılandırma dosyası konumu" @@ -6055,22 +6065,6 @@ msgstr "%s tüm gerekli nesneleri göndermedi" msgid "rejected %s because shallow roots are not allowed to be updated" msgstr "%s reddedildi; çünkü sığ köklerin güncellenmesine izin verilmiyor" -#, c-format -msgid "" -"some local refs could not be updated; try running\n" -" 'git remote prune %s' to remove any old, conflicting branches" -msgstr "" -"bazı yerel baÅŸvurular güncellenemedi; 'git remote prune %s'\n" -"kullanarak eski ve çakışan dalları kaldırmayı deneyin" - -#, c-format -msgid " (%s will become dangling)" -msgstr " (%s sarkacak)" - -#, c-format -msgid " (%s has become dangling)" -msgstr " (%s sarkmaya baÅŸladı)" - msgid "[deleted]" msgstr "[silindi]" @@ -6112,6 +6106,18 @@ msgstr "" "komutunu çalıştırmak, uyarıyı HEAD'e veya baÅŸka bir ÅŸeye uzaktan\n" "deÄŸiÅŸiklik olana dek devre dışı bırakır." +#, c-format +msgid "" +"some local refs could not be updated; try running\n" +" 'git remote prune %s' to remove any old, conflicting branches" +msgstr "" +"bazı yerel baÅŸvurular güncellenemedi; 'git remote prune %s'\n" +"kullanarak eski ve çakışan dalları kaldırmayı deneyin" + +#, c-format +msgid "fetching ref %s failed: %s" +msgstr "%s baÅŸvurusu getirilemedi: %s" + msgid "multiple branches detected, incompatible with --set-upstream" msgstr "birden çok dal algılandı, --set-upstream ile uyumsuz" @@ -6355,6 +6361,9 @@ msgstr "git for-each-ref [--merged [<iÅŸleme>]] [--no-merged [<iÅŸleme>]]" msgid "git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]" msgstr "git for-each-ref [--contains [<iÅŸleme>]] [--no-contains [<iÅŸleme>]]" +msgid "git for-each-ref [--start-after <marker>]" +msgstr "git for-each-ref [--start-after <imleyici>]" + msgid "quote placeholders suitably for shells" msgstr "yer tutucuları kabuÄŸun anlayabileceÄŸi biçimde tırnak içine al" @@ -6370,6 +6379,12 @@ msgstr "yer tutucuları Tcl'nin anlayabileceÄŸi biçimde tırnak içine al" msgid "show only <n> matched refs" msgstr "yalnızca <n> eÅŸleÅŸen baÅŸvuruyu göster" +msgid "marker" +msgstr "imleyici" + +msgid "start iteration after the provided marker" +msgstr "yinelemeyi belirtilen imleyiciden sonra baÅŸlat" + msgid "respect format colors" msgstr "biçim renklerine uy" @@ -6394,9 +6409,15 @@ msgstr "baÅŸvuru dizgilerini stdin'den oku" msgid "also include HEAD ref and pseudorefs" msgstr "ayrıca HEAD ve yalancı baÅŸvuruları içer" +msgid "cannot use --start-after with custom sort options" +msgstr "özel sıralama seçenekleriyle --start-after kullanılamıyor" + msgid "unknown arguments supplied with --stdin" msgstr "--stdin ile bilinmeyen argümanlar verilmiÅŸ" +msgid "cannot use --start-after with patterns" +msgstr "dizgilerle --start-after kullanılamıyor" + msgid "git for-each-repo --config=<config> [--] <arguments>" msgstr "git for-each-repo --config=<yapılandırma> [--] <argümanlar>" @@ -6792,6 +6813,9 @@ msgstr "en büyük paket dışındaki diÄŸer tüm paketleri yeniden paketle" msgid "pack prefix to store a pack containing pruned objects" msgstr "budanan nesneler içeren paketi depolamak için paket öneki" +msgid "skip maintenance tasks typically done in the foreground" +msgstr "genelde ön planda yapılan bakım görevlerini atla" + #, c-format msgid "failed to parse gc.logExpiry value %s" msgstr "gc.logExpiry deÄŸeri %s ayrıştırılamadı" @@ -6863,14 +6887,14 @@ msgstr "" "incremental-repack görevi atlanıyor; çünkü core.multiPackIndex devre dışı" #, c-format -msgid "lock file '%s' exists, skipping maintenance" -msgstr "kilit dosyası '%s' var, bakım atlanıyor" - -#, c-format msgid "task '%s' failed" msgstr "'%s' görevi baÅŸarısız oldu" #, c-format +msgid "lock file '%s' exists, skipping maintenance" +msgstr "kilit dosyası '%s' var, bakım atlanıyor" + +#, c-format msgid "'%s' is not a valid task" msgstr "'%s' geçerli bir görev deÄŸil" @@ -6899,9 +6923,6 @@ msgstr "görev" msgid "run a specific task" msgstr "belirli bir görevi çalıştır" -msgid "use at most one of --auto and --schedule=<frequency>" -msgstr "tek kezde --auto ve --schedule=<sıklık>'tan birini kullan" - #, c-format msgid "unable to add '%s' value of '%s'" msgstr "ÅŸunun için '%s' deÄŸeri eklenemiyor: '%s'" @@ -7779,10 +7800,6 @@ msgid "-L<range>:<file> cannot be used with pathspec" msgstr "-L<erim>:<dosya>, yol belirteci ile kullanılamıyor" #, c-format -msgid "Final output: %d %s\n" -msgstr "Son çıktı: %d %s\n" - -#, c-format msgid "git show %s: bad file" msgstr "git show %s: hatalı dosya" @@ -8481,6 +8498,9 @@ msgstr "birleÅŸtirmenin sonunda bir diffstat göster" msgid "(synonym to --stat)" msgstr "(--stat eÅŸanlamlısı)" +msgid "show a compact-summary at the end of the merge" +msgstr "birleÅŸtirmenin sonunda ufak bir özet göster" + msgid "add (at most <n>) entries from shortlog to merge commit message" msgstr "" "kısa günlükten birleÅŸtirme iÅŸlemesi iletisine girdiler (en çok <n>) ekle" @@ -8744,10 +8764,6 @@ msgid "error: tag input does not pass fsck: %s" msgstr "hata: etiket girdisi fsck'den geçemiyor: %s" #, c-format -msgid "%d (FSCK_IGNORE?) should never trigger this callback" -msgstr "%d (FSCK_IGNORE?) hiçbir zaman bu geri çağırmayı tetiklememeli" - -#, c-format msgid "could not read tagged object '%s'" msgstr "etiketlenmiÅŸ nesne '%s' okunamadı" @@ -9259,16 +9275,26 @@ msgstr "notları <not-bÅŸvr>'ndan kullan" msgid "unknown subcommand: `%s'" msgstr "bilinmeyen altkomut: '%s'" -msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]" -msgstr "" -"git pack-objects --stdout [<sçnklr>] [< <baÅŸvuru-listesi> | < <nesne-" -"listesi>]" - msgid "" -"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<pack-name>]\n" +" [--cruft] [--cruft-expiration=<time>]\n" +" [--stdout [--filter=<filter-spec>] | <base-name>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=<n>] [--path-walk] < <object-list>" msgstr "" -"git pack-objects [<sçnklr>] <temel-ad> [< <bÅŸvru-listesi> | < <nesne-" -"listesi>]" +"git pack-objects [-q | --progress | --all-progress]\n" +" [--all-progress-implied] [--no-reuse-delta]\n" +" [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<paket-adı>]\n" +" [--cruft] [--cruft-expiration=<zaman>]\n" +" [--stdout [--filter=<süzgeç-belirtimi>] | <taban-adı>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=<n>] [--path-walk] < <nesne-listesi>" #, c-format msgid "invalid --name-hash-version option: %d" @@ -9373,6 +9399,15 @@ msgstr "%s etiketinden ulaşılabilir nesneler paketlenemiyor" msgid "unable to get type of object %s" msgstr "%s nesnesinin türü alınamıyor" +msgid "Compressing objects by path" +msgstr "Nesneler yola göre sıkıştırılıyor" + +#, c-format +msgid "Path-based delta compression using up to %d thread" +msgid_plural "Path-based delta compression using up to %d threads" +msgstr[0] "Yol tabanlı delta sıkıştırması %d iÅŸ parçacığı kullanıyor" +msgstr[1] "Yol tabanlı delta sıkıştırması %d iÅŸ parçacığı kullanıyor" + msgid "Compressing objects" msgstr "Nesneler sıkıştırılıyor" @@ -9448,6 +9483,9 @@ msgstr "%s konumundaki gevÅŸek nesne incelenemedi" msgid "unable to force loose object" msgstr "gevÅŸek nesne zorlanamıyor" +msgid "failed to pack objects via path-walk" +msgstr "path-walk ile nesneler paketlenemedi" + #, c-format msgid "not a rev '%s'" msgstr "bir revizyon deÄŸil: '%s'" @@ -9557,6 +9595,9 @@ msgstr "aralıklı ulaşılabilirlik algoritmasını kullan" msgid "create thin packs" msgstr "ince paketler oluÅŸtur" +msgid "use the path-walk API to walk objects when possible" +msgstr "olursa nesneleri yürütmek için path-walk API'sini kullan" + msgid "create packs suitable for shallow fetches" msgstr "sığ getirmelere uygun paketler oluÅŸtur" @@ -9614,6 +9655,10 @@ msgid "pack.deltaCacheLimit is too high, forcing %d" msgstr "pack.deltaCacheLimit çok yüksek, %d zorlanıyor" #, c-format +msgid "cannot use %s with %s" +msgstr "%s, %s ile kullanılamıyor" + +#, c-format msgid "bad pack compression level %d" msgstr "hatalı paket sıkıştırma düzeyi %d" @@ -9626,18 +9671,12 @@ msgstr "olabilecek en küçük paket boyutu limiti 1 MiB'dır" msgid "--thin cannot be used to build an indexable pack" msgstr "--thin bir indekslenebilir paket yapımında kullanılamaz" -msgid "cannot use --filter with --stdin-packs" -msgstr "--filter, --stdin-packs ile birlikte kullanılamıyor" - msgid "cannot use internal rev list with --stdin-packs" msgstr "iç revizyon listeleri, --stdin-packs ile birlikte kullanılamıyor" msgid "cannot use internal rev list with --cruft" msgstr "iç revizyon listeleri, --cruft ile birlikte kullanılamıyor" -msgid "cannot use --stdin-packs with --cruft" -msgstr "--stdin-packs, --cruft ile birlikte kullanılamıyor" - msgid "Enumerating objects" msgstr "Nesneler ortaya dökülüyor" @@ -9650,23 +9689,6 @@ msgstr "" "%<PRIu32>), yeniden kullanılan paket %<PRIu32> (%<PRIuMAX> konumundan)" msgid "" -"'git pack-redundant' is nominated for removal.\n" -"If you still use this command, please add an extra\n" -"option, '--i-still-use-this', on the command line\n" -"and let us know you still use it by sending an e-mail\n" -"to <git@vger.kernel.org>. Thanks.\n" -msgstr "" -"'git pack-redundant' komutu kaldırma için aday\n" -"gösterildi. Bu komutu hâlâ kullanıyorsanız lütfen\n" -"komut satırında '--i-still-use-this' ek seçeneÄŸini\n" -"kullanın ve bunu hâlâ kullandığınızı\n" -"<git@vger.kernel.org> adresine bir e-posta atarak\n" -"bize haber verin. SaÄŸ olun.\n" - -msgid "refusing to run without --i-still-use-this" -msgstr "--i-still-use-this olmadan çalıştırma reddediliyor" - -msgid "" "git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude " "<pattern>]" msgstr "" @@ -10958,6 +10980,14 @@ msgstr "" msgid "unknown --mirror argument: %s" msgstr "bilinmeyen --mirror argümanı: %s" +#, c-format +msgid "remote name '%s' is a subset of existing remote '%s'" +msgstr "uzak konum adı '%s', var olan '%s' uzak konumunun bir alt kümesi" + +#, c-format +msgid "remote name '%s' is a superset of existing remote '%s'" +msgstr "uzak konum adı '%s', var olan '%s' uzak konumunun bir üst kümesi" + msgid "fetch the remote branches" msgstr "uzak konum dallarını getir" @@ -11264,14 +11294,6 @@ msgid "Could not set up %s" msgstr "%s ayarlanamadı" #, c-format -msgid " %s will become dangling!" -msgstr " %s sarkacak!" - -#, c-format -msgid " %s has become dangling!" -msgstr " %s sarkmaya baÅŸladı!" - -#, c-format msgid "Pruning %s" msgstr "%s budanıyor" @@ -11335,11 +11357,11 @@ msgstr "ayrıntılı anlat; bir altkomuttan önce yerleÅŸtirilmelidir" msgid "" "git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n" -"[--write-midx] [--name-hash-version=<n>]" +"[--write-midx] [--name-hash-version=<n>] [--path-walk]" msgstr "" "git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<paket-adı>]\n" -"[--write-midx] [--name-hash-version=<n>]" +"[--write-midx] [--name-hash-version=<n>] [--path-walk]" msgid "" "Incremental repacks are incompatible with bitmap indexes. Use\n" @@ -11421,6 +11443,9 @@ msgid "" msgstr "" "benzer nesneleri yola göre gruplamada kullanılacak name-hash sürümünü belirt" +msgid "pass --path-walk to git-pack-objects" +msgstr "--path-walk'ı 'git-pack-objects'e geçir" + msgid "do not run git-update-server-info" msgstr "'git-update-server-info' çalıştırma" @@ -12571,17 +12596,23 @@ msgstr "" msgid "git stash create [<message>]" msgstr "git stash create [<ileti>]" +msgid "git stash export (--print | --to-ref <ref>) [<stash>...]" +msgstr "git stash export (--print | --to-ref <baÅŸvuru>) [<zula>...]" + +msgid "git stash import <commit>" +msgstr "git stash import [<iÅŸleme>]" + #, c-format msgid "'%s' is not a stash-like commit" msgstr "'%s' zulaya benzer bir iÅŸleme deÄŸil" +msgid "No stash entries found." +msgstr "Zula girdisi bulunamadı." + #, c-format msgid "Too many revisions specified:%s" msgstr "Çok fazla revizyon belirtildi:%s" -msgid "No stash entries found." -msgstr "Zula girdisi bulunamadı." - #, c-format msgid "%s is not a valid reference" msgstr "%s geçerli bir baÅŸvuru deÄŸil" @@ -12733,19 +12764,74 @@ msgstr "zulada izlenmeyen dosyaları içer" msgid "include ignore files" msgstr "yok sayma dosyalarını içer" -msgid "skip and remove all lines starting with comment character" -msgstr "yorum karakteri ile baÅŸlayan tüm satırları atla ve kaldır" +#, c-format +msgid "cannot parse commit %s" +msgstr "%s iÅŸlemesi ayrıştırılamıyor" -msgid "prepend comment character and space to each line" -msgstr "her satırın başına yorum karakteri ve boÅŸluk koy" +#, c-format +msgid "invalid author or committer for %s" +msgstr "%s için geçersiz yazar veya iÅŸleyici" + +msgid "could not write commit" +msgstr "iÅŸleme yazılamadı" #, c-format -msgid "Expecting a full ref name, got %s" -msgstr "Tam bir baÅŸvuru adı bekleniyordu, %s alındı" +msgid "not a valid revision: %s" +msgstr "geçerli bir revizyon deÄŸil: %s" #, c-format -msgid "could not get a repository handle for submodule '%s'" -msgstr "'%s' altmodülü için depo tutacağı alınamadı" +msgid "not a commit: %s" +msgstr "bir iÅŸleme deÄŸil: %s" + +#, c-format +msgid "%s is not a valid exported stash commit" +msgstr "%s, geçerli bir dışa aktarılmış zula iÅŸlemesi deÄŸil" + +#, c-format +msgid "found root commit %s with invalid data" +msgstr "geçersiz veriyle %s kök iÅŸlemesi bulundu" + +#, c-format +msgid "found stash commit %s without expected prefix" +msgstr "beklenen öneksiz %s zula iÅŸlemesi bulundu" + +#, c-format +msgid "cannot parse parents of commit: %s" +msgstr "iÅŸlemenin üst ögesi ayrıştırılamıyor: %s" + +#, c-format +msgid "%s does not look like a stash commit" +msgstr "%s, bir zula iÅŸlemesi gibi görünmüyor" + +#, c-format +msgid "cannot read commit buffer for %s" +msgstr "%s için iÅŸleme arabelleÄŸi okunamıyor" + +#, c-format +msgid "cannot save the stash for %s" +msgstr "%s için zula kaydedilemiyor" + +msgid "unable to write base commit" +msgstr "taban iÅŸleme yazılamıyor" + +#, c-format +msgid "unable to find stash entry %s" +msgstr "zula girdisi %s bulunamıyor" + +msgid "print the object ID instead of writing it to a ref" +msgstr "baÅŸvuruya yazmak yerine nesne kimliÄŸini yazdır" + +msgid "save the data to the given ref" +msgstr "veriyi verilen baÅŸvuruya kaydet" + +msgid "exactly one of --print and --to-ref is required" +msgstr "tam olarak --print veya --to-ref arasından biri gerekiyor" + +msgid "skip and remove all lines starting with comment character" +msgstr "yorum karakteri ile baÅŸlayan tüm satırları atla ve kaldır" + +msgid "prepend comment character and space to each line" +msgstr "her satırın başına yorum karakteri ve boÅŸluk koy" #, c-format msgid "" @@ -12756,6 +12842,10 @@ msgstr "" "varsayılıyor." #, c-format +msgid "could not get a repository handle for submodule '%s'" +msgstr "'%s' altmodülü için depo tutacağı alınamadı" + +#, c-format msgid "No url found for submodule path '%s' in .gitmodules" msgstr ".gitmodules içinde '%s' altmodül yolu için url bulunamadı" @@ -13104,6 +13194,10 @@ msgstr "" "ancak üst proje, herhangi bir dalda deÄŸil" #, c-format +msgid "Expecting a full ref name, got %s" +msgstr "Tam bir baÅŸvuru adı bekleniyordu, %s alındı" + +#, c-format msgid "Unable to find current revision in submodule path '%s'" msgstr "'%s' altmodül yolunda geçerli revizyon bulunamadı" @@ -13302,6 +13396,10 @@ msgid "repo URL: '%s' must be absolute or begin with ./|../" msgstr "depo URL'si: '%s' mutlak olmalı veya ./|../ ile baÅŸlamalıdır" #, c-format +msgid "submodule name '%s' already used for path '%s'" +msgstr "altmodül adı '%s', halihazırda '%s' yolu için kullanılıyor" + +#, c-format msgid "'%s' is not a valid submodule name" msgstr "'%s' geçerli bir altmodül adı deÄŸil" @@ -13924,10 +14022,6 @@ msgid "Preparing worktree (checking out '%s')" msgstr "Çalışma aÄŸacı hazırlanıyor ('%s' çıkış yapılıyor)" #, c-format -msgid "unreachable: invalid reference: %s" -msgstr "eriÅŸilemiyor: geçersiz baÅŸvuru: %s" - -#, c-format msgid "Preparing worktree (detached HEAD %s)" msgstr "Çalışma aÄŸacı hazırlanıyor (ayrık HEAD %s)" @@ -15524,14 +15618,6 @@ msgid "bad numeric config value '%s' for '%s' in %s: %s" msgstr "hatalı sayısal yapılandırma deÄŸeri '%s', '%s' için (%s içinde): %s" #, c-format -msgid "invalid value for variable %s" -msgstr "%s deÄŸiÅŸkeni için geçersiz deÄŸer" - -#, c-format -msgid "ignoring unknown core.fsync component '%s'" -msgstr "bilinmeyen core.fsync bileÅŸeni '%s' yok sayılıyor" - -#, c-format msgid "bad boolean config value '%s' for '%s'" msgstr "hatalı Boole yapılandırma deÄŸeri '%s', '%s' için" @@ -15544,44 +15630,6 @@ msgid "'%s' for '%s' is not a valid timestamp" msgstr "%s', '%s' için geçerli bir zaman damgası deÄŸil" #, c-format -msgid "abbrev length out of range: %d" -msgstr "kısaltma uzunluÄŸu erim dışında: %d" - -#, c-format -msgid "bad zlib compression level %d" -msgstr "hatalı zlib sıkıştırma düzeyi %d" - -#, c-format -msgid "%s cannot contain newline" -msgstr "%s yenisatır içeremez" - -#, c-format -msgid "%s must have at least one character" -msgstr "%s, en az bir karaktere iye olmalı" - -#, c-format -msgid "ignoring unknown core.fsyncMethod value '%s'" -msgstr "bilinmeyen core.fsyncMethod deÄŸeri '%s' yok sayılıyor" - -msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" -msgstr "core.fsyncObjectFiles artık kullanılmıyor; yerine core.fsync kullanın" - -#, c-format -msgid "invalid mode for object creation: %s" -msgstr "nesne oluÅŸturma için geçersiz kip: %s" - -#, c-format -msgid "malformed value for %s" -msgstr "%s için hatalı oluÅŸturulmuÅŸ deÄŸer" - -#, c-format -msgid "malformed value for %s: %s" -msgstr "%s için hatalı oluÅŸturulmuÅŸ deÄŸer: %s" - -msgid "must be one of nothing, matching, simple, upstream or current" -msgstr "nothing, matching, simple, upstream veya current içinden biri olmalı" - -#, c-format msgid "unable to load config blob object '%s'" msgstr "'%s' yapılandırma ikili nesnesi yüklenemiyor" @@ -16092,8 +16140,8 @@ msgstr "stdin, bir dizinle karşılaÅŸtırılamıyor" msgid "cannot compare a named pipe to a directory" msgstr "adlandırılmış bir veriyolu bir dizinle karşılaÅŸtırılamıyor" -msgid "git diff --no-index [<options>] <path> <path>" -msgstr "git diff --no-index [<seçenekler>] <yol> <yol>" +msgid "git diff --no-index [<options>] <path> <path> [<pathspec>...]" +msgstr "git diff --no-index [<seçenekler>] <yol> <yol> [<yol-belirteci>...]" msgid "" "Not a git repository. Use --no-index to compare two paths outside a working " @@ -16102,6 +16150,13 @@ msgstr "" "Bir git deposu deÄŸil. Bir çalışma aÄŸacının dışındaki iki yolu karşılaÅŸtırmak " "için --no-index kullanın" +msgid "" +"Limiting comparison with pathspecs is only supported if both paths are " +"directories." +msgstr "" +"KarşılaÅŸtırmayı yol belirteçleriyle sınırlamak yalnızca her iki yol da " +"dizinse desteklenir." + #, c-format msgid " Failed to parse dirstat cut-off percentage '%s'\n" msgstr " dirstat kesim yüzdesi '%s' ayrıştırılamadı\n" @@ -16678,6 +16733,52 @@ msgid "bad git namespace path \"%s\"" msgstr "hatalı git ad alanı yolu \"%s\"" #, c-format +msgid "invalid value for variable %s" +msgstr "%s deÄŸiÅŸkeni için geçersiz deÄŸer" + +#, c-format +msgid "ignoring unknown core.fsync component '%s'" +msgstr "bilinmeyen core.fsync bileÅŸeni '%s' yok sayılıyor" + +#, c-format +msgid "abbrev length out of range: %d" +msgstr "kısaltma uzunluÄŸu erim dışında: %d" + +#, c-format +msgid "bad zlib compression level %d" +msgstr "hatalı zlib sıkıştırma düzeyi %d" + +#, c-format +msgid "%s cannot contain newline" +msgstr "%s yenisatır içeremez" + +#, c-format +msgid "%s must have at least one character" +msgstr "%s, en az bir karaktere iye olmalı" + +#, c-format +msgid "ignoring unknown core.fsyncMethod value '%s'" +msgstr "bilinmeyen core.fsyncMethod deÄŸeri '%s' yok sayılıyor" + +msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" +msgstr "core.fsyncObjectFiles artık kullanılmıyor; yerine core.fsync kullanın" + +#, c-format +msgid "invalid mode for object creation: %s" +msgstr "nesne oluÅŸturma için geçersiz kip: %s" + +#, c-format +msgid "malformed value for %s" +msgstr "%s için hatalı oluÅŸturulmuÅŸ deÄŸer" + +#, c-format +msgid "malformed value for %s: %s" +msgstr "%s için hatalı oluÅŸturulmuÅŸ deÄŸer: %s" + +msgid "must be one of nothing, matching, simple, upstream or current" +msgstr "nothing, matching, simple, upstream veya current içinden biri olmalı" + +#, c-format msgid "too many args to run %s" msgstr "%s çalıştırmak için pek fazla argüman" @@ -17387,6 +17488,30 @@ msgstr "boÅŸ tanımlayıcı adına (<%s> için) izin verilmiyor" msgid "name consists only of disallowed characters: %s" msgstr "ad yalnızca izin verilmeyen karakterlerden oluÅŸuyor: %s" +msgid "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>" +msgstr "" +"git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <klasör>] < <mbox>" + +msgid "no IMAP host specified" +msgstr "belirtilmiÅŸ IMAP makinesi yok" + +msgid "" +"set the IMAP host with 'git config imap.host <host>'.\n" +"(e.g., 'git config imap.host imaps://imap.example.com')" +msgstr "" +"IMAP makinesini 'git config imap.host <makine>' ile ayarlayın.\n" +"ÖrneÄŸin, 'git config imap.host imaps://imap.example.com'." + +msgid "no IMAP folder specified" +msgstr "belirtilmiÅŸ IMAP klasörü yok" + +msgid "" +"set the target folder with 'git config imap.folder <folder>'.\n" +"(e.g., 'git config imap.folder Drafts')" +msgstr "" +"Hedef klasörü 'git config imap.folder <klasör>' ile ayarlayın.\n" +"ÖrneÄŸin, 'git config imap.folder Taslaklar'." + msgid "expected 'tree:<depth>'" msgstr "'tree:<derinlik>' bekleniyordu" @@ -18345,6 +18470,26 @@ msgid "invalid object name '%.*s'." msgstr "geçersiz nesne adı: '%.*s'." #, c-format +msgid "invalid object type \"%s\"" +msgstr "geçersiz nesne türü \"%s\"" + +#, c-format +msgid "object %s is a %s, not a %s" +msgstr "%s nesnesi bir %s, %s deÄŸil" + +#, c-format +msgid "object %s has unknown type id %d" +msgstr "%s nesnesi %d bilinmeyen tür numarasına iye" + +#, c-format +msgid "unable to parse object: %s" +msgstr "nesne ayrıştırılamıyor: %s" + +#, c-format +msgid "hash mismatch %s" +msgstr "saÄŸlama uyuÅŸmazlığı %s" + +#, c-format msgid "object directory %s does not exist; check .git/objects/info/alternates" msgstr "nesne dizini %s yok; ÅŸurayı denetleyin: .git/objects/info/alternates" @@ -18411,26 +18556,6 @@ msgid "%s is not a valid '%s' object" msgstr "%s geçerli bir '%s' nesnesi deÄŸil" #, c-format -msgid "invalid object type \"%s\"" -msgstr "geçersiz nesne türü \"%s\"" - -#, c-format -msgid "object %s is a %s, not a %s" -msgstr "%s nesnesi bir %s, %s deÄŸil" - -#, c-format -msgid "object %s has unknown type id %d" -msgstr "%s nesnesi %d bilinmeyen tür numarasına iye" - -#, c-format -msgid "unable to parse object: %s" -msgstr "nesne ayrıştırılamıyor: %s" - -#, c-format -msgid "hash mismatch %s" -msgstr "saÄŸlama uyuÅŸmazlığı %s" - -#, c-format msgid "duplicate entry when writing bitmap index: %s" msgstr "biteÅŸlem indeksi yazılırken yinelenen girdi: %s" @@ -18441,9 +18566,6 @@ msgstr "seçili olmayan iÅŸleme yazılmaya çalışıldı: '%s'" msgid "too many pseudo-merges" msgstr "pek çok yalancı birleÅŸtirme" -msgid "trying to write commit not in index" -msgstr "indekste olmayan iÅŸleme yazılmaya çalışılıyor" - msgid "failed to load bitmap index (corrupted?)" msgstr "biteÅŸlem indeksi yüklenemedi (hasarlı mı?)" @@ -18689,6 +18811,10 @@ msgid "%s isn't available" msgstr "%s kullanılabilir deÄŸil" #, c-format +msgid "value for %s exceeds %<PRIdMAX>" +msgstr "%s için olan deÄŸer, %<PRIdMAX> deÄŸerini aşıyor" + +#, c-format msgid "value %s for %s not in range [%<PRIdMAX>,%<PRIdMAX>]" msgstr "%s deÄŸeri, %s için, [%<PRIdMAX>, %<PRIdMAX>] eriminde deÄŸil" @@ -19654,6 +19780,14 @@ msgid "%s does not point to a valid object!" msgstr "%s geçerli bir nesneye iÅŸaret etmiyor!" #, c-format +msgid "%s%s will become dangling after %s is deleted\n" +msgstr "%s%s, %s silindikten sonra sarkacak\n" + +#, c-format +msgid "%s%s has become dangling after %s was deleted\n" +msgstr "%s%s, %s silindikten sonra sarkmaya baÅŸladı\n" + +#, c-format msgid "" "Using '%s' as the name for the initial branch. This default branch name\n" "is subject to change. To configure the initial branch name to use in all\n" @@ -21964,6 +22098,9 @@ msgstr "aÄŸaç nesnelerinin içerilmesini aç/kapat" msgid "toggle pruning of uninteresting paths" msgstr "ilgisiz yolların budanmasını aç/kapat" +msgid "toggle aggressive edge walk" +msgstr "agresif kenar yürüyüşünü aç/kapat" + msgid "read a pattern list over stdin" msgstr "stdin'den bir dizgi listesi oku" @@ -22595,6 +22732,24 @@ msgid "warning: " msgstr "uyarı: " #, c-format +msgid "" +"'%s' is nominated for removal.\n" +"If you still use this command, please add an extra\n" +"option, '--i-still-use-this', on the command line\n" +"and let us know you still use it by sending an e-mail\n" +"to <git@vger.kernel.org>. Thanks.\n" +msgstr "" +"'%s' komutu, kaldırma için aday\n" +"gösterildi. Bu komutu hâlâ kullanıyorsanız lütfen\n" +"komut satırında '--i-still-use-this' ek seçeneÄŸini\n" +"kullanın ve bunu hâlâ kullandığınızı\n" +"<git@vger.kernel.org> adresine bir e-posta atarak\n" +"bize haber verin. SaÄŸ olun.\n" + +msgid "refusing to run without --i-still-use-this" +msgstr "--i-still-use-this olmadan çalıştırma reddediliyor" + +#, c-format msgid "uname() failed with error '%s' (%d)\n" msgstr "uname() '%s' hatasını verip çıktı (%d)\n" @@ -23506,6 +23661,10 @@ msgid "(body) Adding cc: %s from line '%s'\n" msgstr "(body) Cc: %s, '%s' satırından ekleniyor\n" #, perl-format +msgid "error: invalid SMTP port '%s'\n" +msgstr "hata: geçersiz SMTP kapısı '%s'\n" + +#, perl-format msgid "(%s) Could not execute '%s'" msgstr "(%s) '%s' yürütülemedi" @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: Git v2.46\n" "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n" -"POT-Creation-Date: 2025-06-07 16:24-0700\n" -"PO-Revision-Date: 2025-06-03 18:43-0700\n" +"POT-Creation-Date: 2025-08-14 15:16-0700\n" +"PO-Revision-Date: 2025-08-14 17:33-0700\n" "Last-Translator: Kateryna Golovanova <kate@kgthreads.com>\n" "Language-Team: Ukrainian <https://github.com/arkid15r/git-uk-l10n/>\n" "Language: uk\n" @@ -21,6 +21,10 @@ msgstr "" "X-Generator: Poedit 3.6\n" #, c-format +msgid "%s cannot be negative" +msgstr "%s не може бути відʼємним" + +#, c-format msgid "Huh (%s)?" msgstr "Га (%s)?" @@ -1957,6 +1961,10 @@ msgid "adding files failed" msgstr "Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñ–Ð² завершилоÑÑ Ð½ÐµÐ²Ð´Ð°Ð»Ð¾" #, c-format +msgid "'%s' cannot be negative" +msgstr "\"%s\" не може бути відʼємним" + +#, c-format msgid "--chmod param '%s' must be either -x or +x" msgstr "--chmod параметр \"%s\" має бути -x або +x" @@ -5155,25 +5163,26 @@ msgstr "git config list [<опціÑ-файлу>] [<опціÑ-відображРmsgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp] [--value=<value>] [--fixed-value] [--default=<default>] <name>" +"regexp] [--value=<pattern>] [--fixed-value] [--default=<default>] [--" +"url=<url>] <name>" msgstr "" "git config get [<опціÑ-файлу>] [<опціÑ-відображеннÑ>] [--includes] [--all] " -"[--regexp] [--value=<значеннÑ>] [--fixed-value] [--default=<за " -"замовчуваннÑм>] <назва>" +"[--regexp] [--value=<шаблон>] [--fixed-value] [--default=<Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð° " +"замовчуваннÑм>] [--url=<URL-адреÑа>] <назва>" msgid "" -"git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--" -"fixed-value] <name> <value>" +"git config set [<file-option>] [--type=<type>] [--all] [--value=<pattern>] " +"[--fixed-value] <name> <value>" msgstr "" -"git config set [<опціÑ-файлу>] [--type=<тип>] [--all] [--value=<значеннÑ>] " -"[--fixed-value] <назва> <значеннÑ>" +"git config set [<опціÑ-файлу>] [--type=<тип>] [--all] [--value=<шаблон>] [--" +"fixed-value] <назва> <значеннÑ>" msgid "" -"git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] " +"git config unset [<file-option>] [--all] [--value=<pattern>] [--fixed-value] " "<name>" msgstr "" -"git config unset [<опціÑ-файлу>] [--all] [--value=<значеннÑ>] [--fixed-" -"value] <назва>" +"git config unset [<опціÑ-файлу>] [--all] [--value=<шаблон>] [--fixed-value] " +"<назва>" msgid "git config rename-section [<file-option>] <old-name> <new-name>" msgstr "git config rename-section [<опціÑ-файлу>] <Ñтара-назва> <нова-назва>" @@ -5189,19 +5198,19 @@ msgstr "git config [<опціÑ-файлу>] --get-colorbool <назва> [<stdo msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] " +"regexp=<regexp>] [--value=<pattern>] [--fixed-value] [--default=<default>] " "<name>" msgstr "" "git config get [<опціÑ-файлу>] [<опціÑ-відображеннÑ>] [--includes] [--all] " -"[--regexp=<регвир>] [--value=<значеннÑ>] [--fixed-value] [--default=<за-" -"замовчуваннÑм>] <назва>" +"[--regexp=<регвир>] [--value=<шаблон>] [--fixed-value] [--default=<Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ " +"за замовчуваннÑм>] <назва>" msgid "" "git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] " -"[--value=<value>] [--fixed-value] <name> <value>" +"[--value=<pattern>] [--fixed-value] <name> <value>" msgstr "" -"git config set [<опціÑ-файлу>] [--type=<тип>] [--comment=<допиÑ>] [--all][--" -"value=<значеннÑ>] [--fixed-value] <назва> <значеннÑ>" +"git config set [<опціÑ-файлу>] [--type=<тип>] [--comment=<допиÑ>] [--all] [--" +"value=<шаблон>] [--fixed-value] <назва> <значеннÑ>" msgid "Config file location" msgstr "Ð Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ конфігурації" @@ -6061,22 +6070,6 @@ msgstr "%s не надіÑлав уÑÑ– необхідні обʼєкти" msgid "rejected %s because shallow roots are not allowed to be updated" msgstr "відхилено %s, оÑкільки неглибокі корені не можна оновлювати" -#, c-format -msgid "" -"some local refs could not be updated; try running\n" -" 'git remote prune %s' to remove any old, conflicting branches" -msgstr "" -"не вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ деÑкі локальні поÑиланнÑ; Ñпробуйте виконати\n" -" \"git remote prune %s\", щоб видалити вÑÑ– Ñтарі конфліктні гілки" - -#, c-format -msgid " (%s will become dangling)" -msgstr " (%s Ñтануть виÑÑчими)" - -#, c-format -msgid " (%s has become dangling)" -msgstr " (%s Ñтав виÑÑчим)" - msgid "[deleted]" msgstr "[видалено]" @@ -6118,6 +6111,18 @@ msgstr "" "\"git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s\"\n" "вимкне попередженнÑ, доки віддалене Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð½Ðµ змінить HEAD на щоÑÑŒ інше." +#, c-format +msgid "" +"some local refs could not be updated; try running\n" +" 'git remote prune %s' to remove any old, conflicting branches" +msgstr "" +"не вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ деÑкі локальні поÑиланнÑ; Ñпробуйте виконати\n" +" \"git remote prune %s\", щоб видалити вÑÑ– Ñтарі конфліктні гілки" + +#, c-format +msgid "fetching ref %s failed: %s" +msgstr "не вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ поÑÐ¸Ð»Ð°Ð½Ð½Ñ %s: %s" + msgid "multiple branches detected, incompatible with --set-upstream" msgstr "виÑвлено кілька гілок, неÑуміÑних з --set-upstream" @@ -6365,6 +6370,9 @@ msgstr "git for-each-ref [--merged [<коміт>]] [--no-merged [<коміт>]]" msgid "git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]" msgstr "git for-each-ref [--contains [<коміт>]] [--no-contains [<коміт>]]" +msgid "git for-each-ref [--start-after <marker>]" +msgstr "git for-each-ref [--start-after <маркер>]" + msgid "quote placeholders suitably for shells" msgstr "заповнювачі лапок Ð´Ð»Ñ shell" @@ -6380,6 +6388,12 @@ msgstr "заповнювачі лапок Ð´Ð»Ñ Tcl" msgid "show only <n> matched refs" msgstr "показати тільки <н> відповідних поÑилань" +msgid "marker" +msgstr "маркер" + +msgid "start iteration after the provided marker" +msgstr "почати ітерацію піÑÐ»Ñ Ð²ÐºÐ°Ð·Ð°Ð½Ð¾Ð³Ð¾ маркера" + msgid "respect format colors" msgstr "дотримуватиÑÑ ÐºÐ¾Ð»ÑŒÐ¾Ñ€Ñ–Ð² формату" @@ -6404,9 +6418,16 @@ msgstr "читати шаблони поÑилань з stdin" msgid "also include HEAD ref and pseudorefs" msgstr "також включити HEAD та пÑевдопоÑиланнÑ" +msgid "cannot use --start-after with custom sort options" +msgstr "" +"неможливо викориÑтовувати --start-after із влаÑними параметрами ÑортуваннÑ" + msgid "unknown arguments supplied with --stdin" msgstr "невідомі аргументи надані через --stdin" +msgid "cannot use --start-after with patterns" +msgstr "неможливо викориÑтовувати --start-after із шаблонами" + msgid "git for-each-repo --config=<config> [--] <arguments>" msgstr "git for-each-repo --config=<конфіг> [--] <аргументи>" @@ -6803,6 +6824,11 @@ msgstr "перепакувати вÑÑ– пакунки, крім Ð½Ð°Ð¹Ð±Ñ–Ð»ÑŒÑ msgid "pack prefix to store a pack containing pruned objects" msgstr "Ð¿Ñ€ÐµÑ„Ñ–ÐºÑ Ð´Ð»Ñ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Ð¿Ð°ÐºÑƒÐ½ÐºÐ° з обрізаними обʼєктами" +msgid "skip maintenance tasks typically done in the foreground" +msgstr "" +"пропуÑтити Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð¾Ð±ÑлуговуваннÑ, Ñкі зазвичай виконуютьÑÑ Ñƒ фоновому " +"режимі" + #, c-format msgid "failed to parse gc.logExpiry value %s" msgstr "не вдалоÑÑ Ñ€Ð¾Ð·Ñ–Ð±Ñ€Ð°Ñ‚Ð¸ gc.logExpiry Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ %s" @@ -6879,14 +6905,14 @@ msgstr "" "пропуÑк incremental-repack завданнÑ, оÑкільки core.multiPackIndex вимкнено" #, c-format -msgid "lock file '%s' exists, skipping maintenance" -msgstr "файл Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ \"%s\" Ñ–Ñнує, пропуÑк обÑлуговуваннÑ" - -#, c-format msgid "task '%s' failed" msgstr "Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ \"%s\" завершилоÑÑ Ð½ÐµÐ²Ð´Ð°Ð»Ð¾" #, c-format +msgid "lock file '%s' exists, skipping maintenance" +msgstr "файл Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ \"%s\" Ñ–Ñнує, пропуÑк обÑлуговуваннÑ" + +#, c-format msgid "'%s' is not a valid task" msgstr "\"%s\" не Ñ” припуÑтимим завданнÑм" @@ -6915,9 +6941,6 @@ msgstr "завданнÑ" msgid "run a specific task" msgstr "запуÑтити певне завданнÑ" -msgid "use at most one of --auto and --schedule=<frequency>" -msgstr "викориÑтовуйте щонайбільше одну з опцій --auto та --schedule=<чаÑтота>" - #, c-format msgid "unable to add '%s' value of '%s'" msgstr "не вдалоÑÑ Ð´Ð¾Ð´Ð°Ñ‚Ð¸ \"%s\" Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð¾ \"%s\"" @@ -7802,10 +7825,6 @@ msgid "-L<range>:<file> cannot be used with pathspec" msgstr "-L<діапазон>:<файл> не можна викориÑтовувати з визначником шлÑху" #, c-format -msgid "Final output: %d %s\n" -msgstr "Кінцевий результат: %d %s\n" - -#, c-format msgid "git show %s: bad file" msgstr "git show %s: невірний файл" @@ -8517,6 +8536,9 @@ msgstr "показувати diffstat наприкінці злиттÑ" msgid "(synonym to --stat)" msgstr "(Ñинонім до --stat)" +msgid "show a compact-summary at the end of the merge" +msgstr "показати ÑтиÑлий підÑумок наприкінці злиттÑ" + msgid "add (at most <n>) entries from shortlog to merge commit message" msgstr "" "додати (не більше <н>) запиÑів з короткого журналу у Ð´Ð¾Ð¿Ð¸Ñ Ð´Ð¾ коміту злиттÑ" @@ -8784,10 +8806,6 @@ msgid "error: tag input does not pass fsck: %s" msgstr "помилка: вхідний тег не пройшов fsck: %s" #, c-format -msgid "%d (FSCK_IGNORE?) should never trigger this callback" -msgstr "%d (FSCK_IGNORE?) ніколи не мав Ñпричинити цей зворотній виклик" - -#, c-format msgid "could not read tagged object '%s'" msgstr "не вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ тегований об’єкт \"%s\"" @@ -9304,16 +9322,27 @@ msgstr "викориÑтовувати нотатки з <поÑиланнÑ-нРmsgid "unknown subcommand: `%s'" msgstr "невідома підкоманда: \"%s\"" -msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]" -msgstr "" -"git pack-objects --stdout [<опції>] [< <ÑпиÑок-поÑилань> | < <ÑпиÑок-" -"обʼєктів>]" - msgid "" -"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]" -msgstr "" -"git pack-objects [<опції>] <базова-назва> [< <ÑпиÑок-поÑилань> | < <ÑпиÑок-" -"обʼєктів>]" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<pack-name>]\n" +" [--cruft] [--cruft-expiration=<time>]\n" +" [--stdout [--filter=<filter-spec>] | <base-name>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=<n>] [--path-walk] < <object-list>" +msgstr "" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<н>] [--depth=<н>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<назва-" +"пакунка>]\n" +" [--cruft] [--cruft-expiration=<чаÑ>]\n" +" [--stdout [--filter=<визначник-фільтру>] | <базова-назва>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=<н>] [--path-walk] < <ÑпиÑок-обʼєктів>" #, c-format msgid "invalid --name-hash-version option: %d" @@ -9418,6 +9447,16 @@ msgstr "не вдалоÑÑ Ð·Ð°Ð¿Ð°ÐºÑƒÐ²Ð°Ñ‚Ð¸ обʼєкти, доÑÑ‚ÑƒÐ¿Ð½Ñ msgid "unable to get type of object %s" msgstr "не вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ тип обʼєкта %s" +msgid "Compressing objects by path" +msgstr "СтиÑÐ½ÐµÐ½Ð½Ñ Ð¾Ð±Ê¼Ñ”ÐºÑ‚Ñ–Ð² за шлÑхом" + +#, c-format +msgid "Path-based delta compression using up to %d thread" +msgid_plural "Path-based delta compression using up to %d threads" +msgstr[0] "Дельта-ÑтиÑÐ½ÐµÐ½Ð½Ñ Ð½Ð° оÑнові шлÑху з викориÑтаннÑм до %d потоку" +msgstr[1] "Дельта-ÑтиÑÐ½ÐµÐ½Ð½Ñ Ð½Ð° оÑнові шлÑху з викориÑтаннÑм до %d потоків" +msgstr[2] "Дельта-ÑтиÑÐ½ÐµÐ½Ð½Ñ Ð½Ð° оÑнові шлÑху з викориÑтаннÑм до %d потоків" + msgid "Compressing objects" msgstr "КомпреÑÑ–Ñ Ð¾Ð±Ê¼Ñ”ÐºÑ‚Ñ–Ð²" @@ -9493,6 +9532,9 @@ msgstr "не вдалоÑÑ Ñ€Ð¾Ð·Ð³Ð»Ñнути вільний обʼєкт у msgid "unable to force loose object" msgstr "не вдалоÑÑ Ð¿Ñ€Ð¸Ð¼ÑƒÑово вивільнити обʼєкт" +msgid "failed to pack objects via path-walk" +msgstr "не вдалоÑÑ Ð·Ð°Ð¿Ð°ÐºÑƒÐ²Ð°Ñ‚Ð¸ обʼєкти за допомогою path-walk" + #, c-format msgid "not a rev '%s'" msgstr "не Ñ” ревізією \"%s\"" @@ -9603,6 +9645,10 @@ msgstr "викориÑтовувати алгоритм розрідженої Ð msgid "create thin packs" msgstr "Ñтворити тонкі пакунки" +msgid "use the path-walk API to walk objects when possible" +msgstr "" +"викориÑтовувати API path-walk Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñ…Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ Ð¾Ð±Ê¼Ñ”ÐºÑ‚Ñ–Ð², коли це можливо" + msgid "create packs suitable for shallow fetches" msgstr "Ñтворювати пакунки, придатні Ð´Ð»Ñ Ð½ÐµÐ³Ð»Ð¸Ð±Ð¾ÐºÐ¾Ð³Ð¾ отриманнÑ" @@ -9660,6 +9706,10 @@ msgid "pack.deltaCacheLimit is too high, forcing %d" msgstr "pack.deltaCacheLimit занадто великий, примуÑове %d" #, c-format +msgid "cannot use %s with %s" +msgstr "неможливо викориÑтовувати %s з %s" + +#, c-format msgid "bad pack compression level %d" msgstr "невірний рівень ÑтиÑÐ½ÐµÐ½Ð½Ñ Ð¿Ð°ÐºÑƒÐ½ÐºÐ° %d" @@ -9673,18 +9723,12 @@ msgstr "мінімальний розмір пакунка - 1 МіБ" msgid "--thin cannot be used to build an indexable pack" msgstr "--thin не можна викориÑтовувати Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ–Ð½Ð´ÐµÐºÑованого пакунка" -msgid "cannot use --filter with --stdin-packs" -msgstr "неможливо викориÑтовувати --filter з --stdin-packs" - msgid "cannot use internal rev list with --stdin-packs" msgstr "неможливо викориÑтовувати внутрішній ÑпиÑок ревізій з --stdin-packs" msgid "cannot use internal rev list with --cruft" msgstr "неможливо викориÑтовувати внутрішній ÑпиÑок ревізій з --cruft" -msgid "cannot use --stdin-packs with --cruft" -msgstr "неможливо викориÑтовувати --stdin-packs з --cruft" - msgid "Enumerating objects" msgstr "ÐŸÐµÑ€ÐµÑ€Ð°Ñ…ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð±Ê¼Ñ”ÐºÑ‚Ñ–Ð²" @@ -9697,23 +9741,6 @@ msgstr "" "%<PRIu32>), повторно викориÑтано пакунків %<PRIu32> (з %<PRIuMAX>)" msgid "" -"'git pack-redundant' is nominated for removal.\n" -"If you still use this command, please add an extra\n" -"option, '--i-still-use-this', on the command line\n" -"and let us know you still use it by sending an e-mail\n" -"to <git@vger.kernel.org>. Thanks.\n" -msgstr "" -"Команду \"git pack-redundant\" номіновано на вилученнÑ.\n" -"Якщо ви вÑе ще викориÑтовуєте цю команду, будь лаÑка, додайте додатковий " -"параметр\n" -"\"--i-still-use-this\" у командному Ñ€Ñдку\n" -"Ñ– повідомте нам, що ви вÑе ще викориÑтовуєте Ñ—Ñ—, надіÑлавши лиÑта\n" -"на адреÑу <git@vger.kernel.org>. ДÑкуємо.\n" - -msgid "refusing to run without --i-still-use-this" -msgstr "відмовлено в запуÑку без --i-still-use-this" - -msgid "" "git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude " "<pattern>]" msgstr "" @@ -11032,6 +11059,14 @@ msgstr "" msgid "unknown --mirror argument: %s" msgstr "невідомий --mirror аргумент: %s" +#, c-format +msgid "remote name '%s' is a subset of existing remote '%s'" +msgstr "віддалена назва \"%s\" Ñ” підмножиною Ñ–Ñнуючого віддаленого \"%s\"" + +#, c-format +msgid "remote name '%s' is a superset of existing remote '%s'" +msgstr "віддалена назва \"%s\" Ñ” надмножиною Ñ–Ñнуючого віддаленого \"%s\"" + msgid "fetch the remote branches" msgstr "отримати віддалені гілки" @@ -11344,14 +11379,6 @@ msgid "Could not set up %s" msgstr "Ðе вдалоÑÑ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ñ‚Ð¸ %s" #, c-format -msgid " %s will become dangling!" -msgstr " %s Ñтануть виÑÑчими!" - -#, c-format -msgid " %s has become dangling!" -msgstr " %s Ñтав виÑÑчим!" - -#, c-format msgid "Pruning %s" msgstr "Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ %s" @@ -11415,11 +11442,11 @@ msgstr "розгорнутий вивід; має ÑтоÑти перед під msgid "" "git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n" -"[--write-midx] [--name-hash-version=<n>]" +"[--write-midx] [--name-hash-version=<n>] [--path-walk]" msgstr "" "git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=<н>] [--depth=<н>] [--threads=<н>] [--keep-pack=<назва-пакунка>]\n" -"[--write-midx] [--name-hash-version=<н>]" +"[--write-midx] [--name-hash-version=<н>] [--path-walk]" msgid "" "Incremental repacks are incompatible with bitmap indexes. Use\n" @@ -11486,7 +11513,8 @@ msgid "with --cruft, expire objects older than this" msgstr "з --cruft видалити обʼєкти, Ñтаріші за цей термін" msgid "with --cruft, only repack cruft packs smaller than this" -msgstr "з параметром --cruft, перепакувати лише марні пакунки, менші за цей розмір" +msgstr "" +"з параметром --cruft, перепакувати лише марні пакунки, менші за цей розмір" msgid "remove redundant packs, and run git-prune-packed" msgstr "видалити зайві пакунки Ñ– запуÑтити git-prune-packed" @@ -11501,6 +11529,9 @@ msgid "" "specify the name hash version to use for grouping similar objects by path" msgstr "вказати верÑÑ–ÑŽ назви хешу Ð´Ð»Ñ Ð³Ñ€ÑƒÐ¿ÑƒÐ²Ð°Ð½Ð½Ñ Ñхожих обʼєктів за шлÑхом" +msgid "pass --path-walk to git-pack-objects" +msgstr "передати --path-walk до git-pack-objects" + msgid "do not run git-update-server-info" msgstr "не запуÑкати git-update-server-info" @@ -12669,17 +12700,23 @@ msgstr "" msgid "git stash create [<message>]" msgstr "git stash create [<допиÑ>]" +msgid "git stash export (--print | --to-ref <ref>) [<stash>...]" +msgstr "git stash export (--print | --to-ref <поÑиланнÑ>) [<Ñхов>...]" + +msgid "git stash import <commit>" +msgstr "git stash import <коміт>" + #, c-format msgid "'%s' is not a stash-like commit" msgstr "\"%s\" не Ñ” Ñховоподібним комітом" +msgid "No stash entries found." +msgstr "ЗапиÑи Ñхову не знайдені." + #, c-format msgid "Too many revisions specified:%s" msgstr "Вказано забагато ревізій:%s" -msgid "No stash entries found." -msgstr "ЗапиÑи Ñхова не знайдені." - #, c-format msgid "%s is not a valid reference" msgstr "%s не Ñ” припуÑтимим поÑиланнÑм" @@ -12832,19 +12869,74 @@ msgstr "в тому чиÑлі невідÑтежувані файли ÑÑ…Ð¾Ð²Ñ msgid "include ignore files" msgstr "в тому чиÑли файли ігноруваннÑ" -msgid "skip and remove all lines starting with comment character" -msgstr "пропуÑтити та видалити вÑÑ– Ñ€Ñдки, що починаютьÑÑ Ð· Ñимволу коментарÑ" +#, c-format +msgid "cannot parse commit %s" +msgstr "неможливо розібрати коміт %s" -msgid "prepend comment character and space to each line" -msgstr "додати Ñимвол ÐºÐ¾Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ñ Ñ‚Ð° пробіл до кожного Ñ€Ñдка" +#, c-format +msgid "invalid author or committer for %s" +msgstr "неприпуÑтимий автор або комітер Ð´Ð»Ñ %s" + +msgid "could not write commit" +msgstr "не вдалоÑÑ Ð·Ð°Ð¿Ð¸Ñати коміт" #, c-format -msgid "Expecting a full ref name, got %s" -msgstr "ОчікувалаÑÑŒ повна назва поÑиланнÑ, отримано %s" +msgid "not a valid revision: %s" +msgstr "неприпуÑтима редакціÑ: %s" #, c-format -msgid "could not get a repository handle for submodule '%s'" -msgstr "не вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ обʼєкт Ñховища Ð´Ð»Ñ Ð¿Ñ–Ð´Ð¼Ð¾Ð´ÑƒÐ»Ñ \"%s\"" +msgid "not a commit: %s" +msgstr "не Ñ” комітом: %s" + +#, c-format +msgid "%s is not a valid exported stash commit" +msgstr "%s не Ñ” припуÑтимим екÑпортованим комітом Ñхову" + +#, c-format +msgid "found root commit %s with invalid data" +msgstr "знайдено кореневий коміт %s з неприпуÑтими даними" + +#, c-format +msgid "found stash commit %s without expected prefix" +msgstr "знайдено коміт Ñхову %s без очікуваного префікÑа" + +#, c-format +msgid "cannot parse parents of commit: %s" +msgstr "неможливо розібрати батьків коміту: %s" + +#, c-format +msgid "%s does not look like a stash commit" +msgstr "%s не Ñхожий на коміт Ñхову" + +#, c-format +msgid "cannot read commit buffer for %s" +msgstr "неможливо прочитати буфер комітів Ð´Ð»Ñ %s" + +#, c-format +msgid "cannot save the stash for %s" +msgstr "неможливо зберегти Ñхов Ð´Ð»Ñ %s" + +msgid "unable to write base commit" +msgstr "не вдалоÑÑ Ð·Ð°Ð¿Ð¸Ñати базовий коміт" + +#, c-format +msgid "unable to find stash entry %s" +msgstr "не вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ Ð·Ð°Ð¿Ð¸Ñ Ñхову %s" + +msgid "print the object ID instead of writing it to a ref" +msgstr "виводити ідентифікатор обʼєкта заміÑть запиÑÑƒÐ²Ð°Ð½Ð½Ñ Ð¹Ð¾Ð³Ð¾ в поÑиланнÑ" + +msgid "save the data to the given ref" +msgstr "зберегти дані у вказаному поÑиланні" + +msgid "exactly one of --print and --to-ref is required" +msgstr "потрібно вказати лише один з параметрів --print та --to-ref" + +msgid "skip and remove all lines starting with comment character" +msgstr "пропуÑтити та видалити вÑÑ– Ñ€Ñдки, що починаютьÑÑ Ð· Ñимволу коментарÑ" + +msgid "prepend comment character and space to each line" +msgstr "додати Ñимвол ÐºÐ¾Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ñ Ñ‚Ð° пробіл до кожного Ñ€Ñдка" #, c-format msgid "" @@ -12855,6 +12947,10 @@ msgstr "" "першоджерельне Ñховище." #, c-format +msgid "could not get a repository handle for submodule '%s'" +msgstr "не вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ обʼєкт Ñховища Ð´Ð»Ñ Ð¿Ñ–Ð´Ð¼Ð¾Ð´ÑƒÐ»Ñ \"%s\"" + +#, c-format msgid "No url found for submodule path '%s' in .gitmodules" msgstr "Ðе знайдено URL Ð´Ð»Ñ ÑˆÐ»Ñху Ð¿Ñ–Ð´Ð¼Ð¾Ð´ÑƒÐ»Ñ \"%s\" у .gitmodules" @@ -13213,6 +13309,10 @@ msgstr "" "Ñуперпроект не знаходитьÑÑ Ñƒ жодній гілці" #, c-format +msgid "Expecting a full ref name, got %s" +msgstr "ОчікувалаÑÑŒ повна назва поÑиланнÑ, отримано %s" + +#, c-format msgid "Unable to find current revision in submodule path '%s'" msgstr "Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ поточну ревізію у шлÑху Ð¿Ñ–Ð´Ð¼Ð¾Ð´ÑƒÐ»Ñ \"%s\"" @@ -13416,6 +13516,10 @@ msgid "repo URL: '%s' must be absolute or begin with ./|../" msgstr "URL Ñховища: \"%s\" має бути абÑолютним або починатиÑÑ Ð· ./|../." #, c-format +msgid "submodule name '%s' already used for path '%s'" +msgstr "назва Ð¿Ñ–Ð´Ð¼Ð¾Ð´ÑƒÐ»Ñ \"%s\" вже викориÑтовуєтьÑÑ Ð´Ð»Ñ ÑˆÐ»Ñху \"%s\"" + +#, c-format msgid "'%s' is not a valid submodule name" msgstr "\"%s\" не Ñ” припуÑтимою назвою підмодулÑ" @@ -14049,10 +14153,6 @@ msgid "Preparing worktree (checking out '%s')" msgstr "Підготовка робочого дерева (перехід до \"%s\")" #, c-format -msgid "unreachable: invalid reference: %s" -msgstr "недоÑÑжне: неприпуÑтиме поÑиланнÑ: %s" - -#, c-format msgid "Preparing worktree (detached HEAD %s)" msgstr "Підготовка робочого дерева (відокремлений HEAD %s)" @@ -15660,14 +15760,6 @@ msgid "bad numeric config value '%s' for '%s' in %s: %s" msgstr "невірне чиÑлове Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ— \"%s\" Ð´Ð»Ñ \"%s\" у \"%s\": %s" #, c-format -msgid "invalid value for variable %s" -msgstr "неприпуÑтиме Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð·Ð¼Ñ–Ð½Ð½Ð¾Ñ— %s" - -#, c-format -msgid "ignoring unknown core.fsync component '%s'" -msgstr "Ñ–Ð³Ð½Ð¾Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð½ÐµÐ²Ñ–Ð´Ð¾Ð¼Ð¾Ð³Ð¾ компонента core.fsync \"%s\"" - -#, c-format msgid "bad boolean config value '%s' for '%s'" msgstr "невірне булеве Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ— \"%s\" Ð´Ð»Ñ \"%s\"" @@ -15680,44 +15772,6 @@ msgid "'%s' for '%s' is not a valid timestamp" msgstr "\"%s\" Ð´Ð»Ñ \"%s\" не Ñ” припуÑтимою міткою чаÑу" #, c-format -msgid "abbrev length out of range: %d" -msgstr "довжина ÑÐºÐ¾Ñ€Ð¾Ñ‡ÐµÐ½Ð½Ñ Ð¿Ð¾Ð·Ð° діапазоном: %d" - -#, c-format -msgid "bad zlib compression level %d" -msgstr "невірний рівень zlib компреÑÑ–Ñ— %d" - -#, c-format -msgid "%s cannot contain newline" -msgstr "%s не може міÑтити Ñимволи нового Ñ€Ñдка" - -#, c-format -msgid "%s must have at least one character" -msgstr "%s повинен мати принаймні один Ñимвол" - -#, c-format -msgid "ignoring unknown core.fsyncMethod value '%s'" -msgstr "Ñ–Ð³Ð½Ð¾Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð½ÐµÐ²Ñ–Ð´Ð¾Ð¼Ð¾Ð³Ð¾ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ core.fsyncMethod \"%s\"" - -msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" -msgstr "core.fsyncObjectFiles заÑтаріла; натоміÑть викориÑтовуйте core.fsync" - -#, c-format -msgid "invalid mode for object creation: %s" -msgstr "неприпуÑтимий режим ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¾Ð±Ê¼Ñ”ÐºÑ‚Ð°: %s" - -#, c-format -msgid "malformed value for %s" -msgstr "невірно Ñформоване Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ %s" - -#, c-format -msgid "malformed value for %s: %s" -msgstr "невірно Ñформоване Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ %s: %s" - -msgid "must be one of nothing, matching, simple, upstream or current" -msgstr "має бути одним з nothing, matching, simple, upstream або current" - -#, c-format msgid "unable to load config blob object '%s'" msgstr "не вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ config blob обʼєкт \"%s\"" @@ -16238,8 +16292,8 @@ msgstr "неможливо порівнÑти stdin з директорією" msgid "cannot compare a named pipe to a directory" msgstr "неможливо порівнÑти іменований канал з директорією" -msgid "git diff --no-index [<options>] <path> <path>" -msgstr "git diff --no-index [<опції>] <шлÑÑ…> <шлÑÑ…>" +msgid "git diff --no-index [<options>] <path> <path> [<pathspec>...]" +msgstr "git diff --no-index [<опції>] <шлÑÑ…> <шлÑÑ…> [<визначник шлÑху>...]" msgid "" "Not a git repository. Use --no-index to compare two paths outside a working " @@ -16248,6 +16302,13 @@ msgstr "" "Це не git Ñховище. СкориÑтайтеÑÑŒ --no-index Ð´Ð»Ñ Ð¿Ð¾Ñ€Ñ–Ð²Ð½ÑÐ½Ð½Ñ Ð´Ð²Ð¾Ñ… шлÑхів поза " "робочим деревом" +msgid "" +"Limiting comparison with pathspecs is only supported if both paths are " +"directories." +msgstr "" +"ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð¿Ð¾Ñ€Ñ–Ð²Ð½ÑÐ½Ð½Ñ Ð· pathspecs підтримуєтьÑÑ Ñ‚Ñ–Ð»ÑŒÐºÐ¸ в тому випадку, Ñкщо " +"обидва шлÑхи Ñ” директоріÑми." + #, c-format msgid " Failed to parse dirstat cut-off percentage '%s'\n" msgstr " Ðе вдалоÑÑ Ñ€Ð¾Ð·Ñ–Ð±Ñ€Ð°Ñ‚Ð¸ відÑоток відÑÑ–ÐºÐ°Ð½Ð½Ñ dirstat \"%s\"\n" @@ -16829,6 +16890,52 @@ msgid "bad git namespace path \"%s\"" msgstr "невірний шлÑÑ… до проÑтору імен git \"%s\"" #, c-format +msgid "invalid value for variable %s" +msgstr "неприпуÑтиме Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð·Ð¼Ñ–Ð½Ð½Ð¾Ñ— %s" + +#, c-format +msgid "ignoring unknown core.fsync component '%s'" +msgstr "Ñ–Ð³Ð½Ð¾Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð½ÐµÐ²Ñ–Ð´Ð¾Ð¼Ð¾Ð³Ð¾ компонента core.fsync \"%s\"" + +#, c-format +msgid "abbrev length out of range: %d" +msgstr "довжина ÑÐºÐ¾Ñ€Ð¾Ñ‡ÐµÐ½Ð½Ñ Ð¿Ð¾Ð·Ð° діапазоном: %d" + +#, c-format +msgid "bad zlib compression level %d" +msgstr "невірний рівень zlib компреÑÑ–Ñ— %d" + +#, c-format +msgid "%s cannot contain newline" +msgstr "%s не може міÑтити Ñимволи нового Ñ€Ñдка" + +#, c-format +msgid "%s must have at least one character" +msgstr "%s повинен мати принаймні один Ñимвол" + +#, c-format +msgid "ignoring unknown core.fsyncMethod value '%s'" +msgstr "Ñ–Ð³Ð½Ð¾Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð½ÐµÐ²Ñ–Ð´Ð¾Ð¼Ð¾Ð³Ð¾ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ core.fsyncMethod \"%s\"" + +msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" +msgstr "core.fsyncObjectFiles заÑтаріла; натоміÑть викориÑтовуйте core.fsync" + +#, c-format +msgid "invalid mode for object creation: %s" +msgstr "неприпуÑтимий режим ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¾Ð±Ê¼Ñ”ÐºÑ‚Ð°: %s" + +#, c-format +msgid "malformed value for %s" +msgstr "невірно Ñформоване Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ %s" + +#, c-format +msgid "malformed value for %s: %s" +msgstr "невірно Ñформоване Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ %s: %s" + +msgid "must be one of nothing, matching, simple, upstream or current" +msgstr "має бути одним з nothing, matching, simple, upstream або current" + +#, c-format msgid "too many args to run %s" msgstr "забагато аргументів Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑку %s" @@ -17547,6 +17654,31 @@ msgstr "порожнє Ñ–Ð¼Ê¼Ñ Ð¾ÑобиÑтоÑті (Ð´Ð»Ñ <%s>) заборРmsgid "name consists only of disallowed characters: %s" msgstr "Ñ–Ð¼Ê¼Ñ ÑкладаєтьÑÑ Ð»Ð¸ÑˆÐµ з заборонених Ñимволів: %s" +msgid "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>" +msgstr "" +"git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <директоріÑ>] < <mbox>" + +msgid "no IMAP host specified" +msgstr "не вказано хоÑÑ‚ IMAP" + +msgid "" +"set the IMAP host with 'git config imap.host <host>'.\n" +"(e.g., 'git config imap.host imaps://imap.example.com')" +msgstr "" +"вÑтановіть хоÑÑ‚ IMAP за допомогою команди \"git config imap.host <хоÑÑ‚>\".\n" +"(наприклад, \"git config imap.host imaps://imap.example.com\")" + +msgid "no IMAP folder specified" +msgstr "не вказано директорію IMAP" + +msgid "" +"set the target folder with 'git config imap.folder <folder>'.\n" +"(e.g., 'git config imap.folder Drafts')" +msgstr "" +"вÑтановіть цільову директорію за допомогою команди \"git config imap.folder " +"<директоріÑ>\".\n" +"(наприклад, \"git config imap.folder Drafts\")" + msgid "expected 'tree:<depth>'" msgstr "очікувалоÑÑŒ \"tree:<глибина>\"" @@ -18510,6 +18642,26 @@ msgid "invalid object name '%.*s'." msgstr "неприпуÑтима назва обʼєкта \"%.*s\"." #, c-format +msgid "invalid object type \"%s\"" +msgstr "неприпуÑтимий тип обʼєкту \"%s\"" + +#, c-format +msgid "object %s is a %s, not a %s" +msgstr "обʼєкт %s Ñ” %s, а не %s" + +#, c-format +msgid "object %s has unknown type id %d" +msgstr "обʼєкт %s має невідомий тип ідентифікатора %d" + +#, c-format +msgid "unable to parse object: %s" +msgstr "не вдалоÑÑ Ñ€Ð¾Ð·Ñ–Ð±Ñ€Ð°Ñ‚Ð¸ обʼєкт: %s" + +#, c-format +msgid "hash mismatch %s" +msgstr "невідповідніÑть хешу %s" + +#, c-format msgid "object directory %s does not exist; check .git/objects/info/alternates" msgstr "Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ñ–Ñ Ð¾Ð±â€™Ñ”ÐºÑ‚Ð° %s не Ñ–Ñнує; перевірте .git/objects/info/alternates" @@ -18577,26 +18729,6 @@ msgid "%s is not a valid '%s' object" msgstr "%s не Ñ” допуÑтимим \"%s\" обʼєктом" #, c-format -msgid "invalid object type \"%s\"" -msgstr "неприпуÑтимий тип обʼєкту \"%s\"" - -#, c-format -msgid "object %s is a %s, not a %s" -msgstr "обʼєкт %s Ñ” %s, а не %s" - -#, c-format -msgid "object %s has unknown type id %d" -msgstr "обʼєкт %s має невідомий тип ідентифікатора %d" - -#, c-format -msgid "unable to parse object: %s" -msgstr "не вдалоÑÑ Ñ€Ð¾Ð·Ñ–Ð±Ñ€Ð°Ñ‚Ð¸ обʼєкт: %s" - -#, c-format -msgid "hash mismatch %s" -msgstr "невідповідніÑть хешу %s" - -#, c-format msgid "duplicate entry when writing bitmap index: %s" msgstr "дубльований елемент під Ñ‡Ð°Ñ Ð·Ð°Ð¿Ð¸Ñу bitmap індекÑу: \"%s\"" @@ -18607,9 +18739,6 @@ msgstr "Ñпроба зберегти невибраний коміт: \"%s\"" msgid "too many pseudo-merges" msgstr "занадто багато пÑевдозлиттÑ" -msgid "trying to write commit not in index" -msgstr "Ñпроба запиÑати коміт, Ñкого немає в індекÑÑ–" - msgid "failed to load bitmap index (corrupted?)" msgstr "не вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ bitmap Ñ–Ð½Ð´ÐµÐºÑ (пошкоджено?)" @@ -18863,6 +18992,10 @@ msgid "%s isn't available" msgstr "%s недоÑтупний" #, c-format +msgid "value for %s exceeds %<PRIdMAX>" +msgstr "Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ %s перевищує %<PRIdMAX>" + +#, c-format msgid "value %s for %s not in range [%<PRIdMAX>,%<PRIdMAX>]" msgstr "Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ %s Ð´Ð»Ñ %s не в діапазоні [%<PRIdMAX>,%<PRIdMAX>]" @@ -19837,6 +19970,14 @@ msgid "%s does not point to a valid object!" msgstr "%s не вказує на допуÑтимий обʼєкт!" #, c-format +msgid "%s%s will become dangling after %s is deleted\n" +msgstr "%s%s Ñтане виÑÑчим піÑÐ»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ %s\n" + +#, c-format +msgid "%s%s has become dangling after %s was deleted\n" +msgstr "%s%s Ñтав виÑÑчим піÑÐ»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ %s\n" + +#, c-format msgid "" "Using '%s' as the name for the initial branch. This default branch name\n" "is subject to change. To configure the initial branch name to use in all\n" @@ -22201,6 +22342,9 @@ msgstr "перемикач Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð¾Ð±Ê¼Ñ”ÐºÑ‚Ñ–Ð² дерева" msgid "toggle pruning of uninteresting paths" msgstr "перемикач Ð¾Ð±Ñ€Ñ–Ð·Ð°Ð½Ð½Ñ Ð½ÐµÑ†Ñ–ÐºÐ°Ð²Ð¸Ñ… шлÑхів" +msgid "toggle aggressive edge walk" +msgstr "перемикач агреÑивного Ñ…Ð¾Ð´Ñ–Ð½Ð½Ñ Ð¿Ð¾ краю" + msgid "read a pattern list over stdin" msgstr "читати ÑпиÑок шаблонів через stdin" @@ -22845,6 +22989,24 @@ msgid "warning: " msgstr "попередженнÑ: " #, c-format +msgid "" +"'%s' is nominated for removal.\n" +"If you still use this command, please add an extra\n" +"option, '--i-still-use-this', on the command line\n" +"and let us know you still use it by sending an e-mail\n" +"to <git@vger.kernel.org>. Thanks.\n" +msgstr "" +"\"%s\" запропоновано Ð´Ð»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ.\n" +"Якщо ви вÑе ще викориÑтовуєте цю команду, додайте додаткову\n" +"опцію \"--i-still-use-this\" у командний Ñ€Ñдок\n" +"Ñ– повідомте Ð½Ð°Ñ Ð¿Ñ€Ð¾ те, що ви вÑе ще Ñ—Ñ— викориÑтовуєте, надіÑлавши " +"електронного лиÑта\n" +"на адреÑу <git@vger.kernel.org>. ДÑкуємо.\n" + +msgid "refusing to run without --i-still-use-this" +msgstr "відмовлено в запуÑку без --i-still-use-this" + +#, c-format msgid "uname() failed with error '%s' (%d)\n" msgstr "uname() завершивÑÑ Ð½ÐµÐ²Ð´Ð°Ð»Ð¾ з помилкою \"%s\" (%d)\n" @@ -23087,9 +23249,9 @@ msgstr "невідÑтежуваний контент, " #, c-format msgid "Your stash currently has %d entry" msgid_plural "Your stash currently has %d entries" -msgstr[0] "У вашій Ñхованці наразі Ñ” %d запиÑ" -msgstr[1] "У вашій Ñхованці наразі Ñ” %d запиÑи" -msgstr[2] "У вашій Ñхованці наразі Ñ” %d запиÑів" +msgstr[0] "У вашому Ñхові наразі Ñ” %d запиÑ" +msgstr[1] "У вашому Ñхові наразі Ñ” %d запиÑи" +msgstr[2] "У вашому Ñхові наразі Ñ” %d запиÑів" msgid "Submodules changed but not updated:" msgstr "Підмодулі змінено, але не оновлено:" @@ -23780,6 +23942,10 @@ msgid "(body) Adding cc: %s from line '%s'\n" msgstr "(тіло) Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ cc: %s з Ñ€Ñдка \"%s\"\n" #, perl-format +msgid "error: invalid SMTP port '%s'\n" +msgstr "помилка: неприпуÑтимий порт SMTP \"%s\"\n" + +#, perl-format msgid "(%s) Could not execute '%s'" msgstr "(%s) Ðе вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ \"%s\"" @@ -66,10 +66,10 @@ # +------------------------------------------------------------------+ msgid "" msgstr "" -"Project-Id-Version: git 2.49\n" +"Project-Id-Version: git 2.51\n" "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n" -"POT-Creation-Date: 2025-03-05 22:57+0000\n" -"PO-Revision-Date: 2025-03-06 08:20+0700\n" +"POT-Creation-Date: 2025-08-12 17:01+0000\n" +"PO-Revision-Date: 2025-08-14 16:16+0700\n" "Last-Translator: VÅ© Tiến Hưng <newcomerminecraft@gmail.com>\n" "Language-Team: Vietnamese <https://github.com/Nekosha/git-po>\n" "Language: vi\n" @@ -79,6 +79,10 @@ msgstr "" "Plural-Forms: nplurals=1; plural=0\n" #, c-format +msgid "%s cannot be negative" +msgstr "%s phải không âm" + +#, c-format msgid "Huh (%s)?" msgstr "Hả (%s)?" @@ -1978,6 +1982,10 @@ msgid "adding files failed" msgstr "thêm táºp tin gặp lá»—i" #, c-format +msgid "'%s' cannot be negative" +msgstr "'%s' phải không âm" + +#, c-format msgid "--chmod param '%s' must be either -x or +x" msgstr "--chmod tham số '%s' phải hoặc là -x hay +x" @@ -2952,7 +2960,7 @@ msgid "show hash and subject, give twice for upstream branch" msgstr "hiển thị mã băm và chá»§ Ä‘á», đưa ra hai lần cho nhánh thượng nguồn" msgid "suppress informational messages" -msgstr "không xuất các thông tin" +msgstr "không in ra các thông tin phản hồi" msgid "set branch tracking configuration" msgstr "đặt cấu hình thao dõi nhánh" @@ -3261,7 +3269,7 @@ msgid "do not show bundle details" msgstr "không hiển thị chi tiết bundle (bó)" msgid "need a repository to verify a bundle" -msgstr "cần má»™t kho chứa để thẩm tra má»™t bundle" +msgstr "cần má»™t kho chứa để xác minh má»™t bundle" #, c-format msgid "%s is okay\n" @@ -3301,11 +3309,8 @@ msgstr "chỉ má»™t tùy chá»n batch được chỉ ra" msgid "git cat-file <type> <object>" msgstr "git cat-file <kiểu> <đối tượng>" -msgid "git cat-file (-e | -p) <object>" -msgstr "git cat-file (-e | -p) <đối tượng>" - -msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>" -msgstr "git cat-file (-t | -s) [--allow-unknown-type] <đối_tượng>" +msgid "git cat-file (-e | -p | -t | -s) <object>" +msgstr "git cat-file (-e | -p | -t | -s) <đối tượng>" msgid "" "git cat-file (--textconv | --filters)\n" @@ -3345,9 +3350,6 @@ msgstr "" msgid "show object size" msgstr "hiển thị kÃch thước đối tượng" -msgid "allow -s and -t to work with broken/corrupt objects" -msgstr "cho phép -s và -t để là m việc vá»›i các đối tượng sai/há»ng" - msgid "use mail map file" msgstr "sá» dụng táºp tin ánh xạ thư" @@ -3404,6 +3406,13 @@ msgid "use a <path> for (--textconv | --filters); Not with 'batch'" msgstr "" "dùng má»™t </đưá»ng/dẫn/> rõ rà ng cho (--textconv/--filters); Không vá»›i 'batch'" +msgid "objects filter only supported in batch mode" +msgstr "chỉ há»— trợ lá»c đối tượng trong chế độ batch" + +#, c-format +msgid "objects filter not supported: '%s'" +msgstr "không há»— trợ lá»c đối tượng: '%s'" + #, c-format msgid "'%s=<%s>' needs '%s' or '%s'" msgstr "'%s=<%s>' cần '%s' hoặc '%s'" @@ -4500,7 +4509,7 @@ msgid "the object directory to store the graph" msgstr "thư mục đối tượng để lưu đồ thị" msgid "if the commit-graph is split, only verify the tip file" -msgstr "nếu đồ-thị-chuyển-giao bị chia cắt, thì chỉ thẩm tra táºp tin đỉnh" +msgstr "nếu đồ-thị-chuyển-giao bị chia cắt, thì chỉ xác minh táºp tin đỉnh" #, c-format msgid "Could not open commit-graph '%s'" @@ -4932,7 +4941,7 @@ msgid "paths '%s ...' with -a does not make sense" msgstr "các đưá»ng dẫn '%s ...' vá»›i tùy chá»n -a không hợp lý" msgid "show status concisely" -msgstr "hiển thị trạng thái ở dạng súc tÃch" +msgstr "hiển thị trạng thái ở dạng tóm lược" msgid "show branch information" msgstr "hiển thị thông tin nhánh" @@ -5135,58 +5144,61 @@ msgstr "" "và sau đó \"git restore --staged :/\" để khắc phục." msgid "git config list [<file-option>] [<display-option>] [--includes]" -msgstr "git config list [<tuỳ-chá»n>] [<tuỳ-chá»n-hiển-thị>] [--includes]" +msgstr "" +"git config list [<tuỳ-chá»n-táºp-tin>] [<tuỳ-chá»n-hiển-thị>] [--includes]" msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp] [--value=<value>] [--fixed-value] [--default=<default>] <name>" +"regexp] [--value=<pattern>] [--fixed-value] [--default=<default>] [--" +"url=<url>] <name>" msgstr "" -"git config get [<tuỳ-chá»n>] [<tuỳ-chá»n-hiển-thị>] [--includes] [--all] [--" -"regexp] [--value=<giá-trị>] [--fixed-value] [--default=<giá-trị-mặc-định>] " -"<tên>" +"git config get [<tuỳ-chá»n-táºp-tin>] [<tuỳ-chá»n-hiển-thị>] [--includes] [--" +"all] [--regexp] [--value=<giá-trị>] [--fixed-value] [--default=<giá-trị-mặc-" +"định>] [--url=<url>] <tên>" msgid "" -"git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--" -"fixed-value] <name> <value>" +"git config set [<file-option>] [--type=<type>] [--all] [--value=<pattern>] " +"[--fixed-value] <name> <value>" msgstr "" -"git config set [<tuỳ-chá»n>] [--type=<kiểu>] [--all] [--value=<giá-trị>] [--" -"fixed-value] <khoá> <giá-trị>" +"git config set [<tuỳ-chá»n-táºp-tin>] [--type=<kiểu>] [--all] [--value=<giá-" +"trị>] [--fixed-value] <khoá> <giá-trị>" msgid "" -"git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] " +"git config unset [<file-option>] [--all] [--value=<pattern>] [--fixed-value] " "<name>" msgstr "" -"git config unset [<tuỳ-chá»n>] [--all] [--value=<giá-trị>] [--fixed-value] " -"<khoá>" +"git config unset [<tuỳ-chá»n-táºp-tin>] [--all] [--value=<giá-trị>] [--fixed-" +"value] <khoá>" msgid "git config rename-section [<file-option>] <old-name> <new-name>" -msgstr "git config rename-section [<tuỳ-chá»n>] <tên-cÅ©> <tên-má»›i>" +msgstr "git config rename-section [<tuỳ-chá»n-táºp-tin>] <tên-cÅ©> <tên-má»›i>" msgid "git config remove-section [<file-option>] <name>" -msgstr "git config remove-section [<tuỳ-chá»n>] <tên>" +msgstr "git config remove-section [<tuỳ-chá»n-táºp-tin>] <tên>" msgid "git config edit [<file-option>]" -msgstr "git config edit [<tùy-chá»n>]" +msgstr "git config edit [<tùy-chá»n-táºp-tin>]" msgid "git config [<file-option>] --get-colorbool <name> [<stdout-is-tty>]" msgstr "" -"git config [<tuỳ-chá»n>] --get-colorbool <tên> [<stdout-là -tty-hay-không>]" +"git config [<tuỳ-chá»n-táºp-tin>] --get-colorbool <tên> [<stdout-là -tty-hay-" +"không>]" msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] " +"regexp=<regexp>] [--value=<pattern>] [--fixed-value] [--default=<default>] " "<name>" msgstr "" -"git config get [<tuỳ-chá»n>] [<tuỳ-chá»n-hiển-thị>] [--includes] [--all] [--" -"regexp=<biểu-thức-chÃnh-quy>] [--value=<giá-trị>] [--fixed-value] [--" +"git config get [<tuỳ-chá»n-táºp-tin>] [<tuỳ-chá»n-hiển-thị>] [--includes] [--" +"all] [--regexp=<biểu-thức-chÃnh-quy>] [--value=<giá-trị>] [--fixed-value] [--" "default=<giá-trị-mặc-định>] <khoá>" msgid "" "git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] " -"[--value=<value>] [--fixed-value] <name> <value>" +"[--value=<pattern>] [--fixed-value] <name> <value>" msgstr "" -"git config set [<tuỳ-chá»n>] [--type=<kiểu>] [--comment=<chú-thÃch>] [--all] " -"[--value=<giá-trị>] [--fixed-value] <khoá> <giá-trị>" +"git config set [<tuỳ-chá»n-táºp-tin>] [--type=<kiểu>] [--comment=<chú-thÃch>] " +"[--all] [--value=<giá-trị>] [--fixed-value] <khoá> <giá-trị>" msgid "Config file location" msgstr "Vị trà táºp tin cấu hình" @@ -5672,6 +5684,50 @@ msgstr "" msgid "specify the content of the diagnostic archive" msgstr "chỉ định ná»™i dung bản báo cáo" +#, c-format +msgid "unable to parse mode: %s" +msgstr "không thể Ä‘á»c chế độ: %s" + +#, c-format +msgid "unable to parse object id: %s" +msgstr "không thể Ä‘á»c đối tượng: %s" + +msgid "git diff-pairs -z [<diff-options>]" +msgstr "git diff-pairs -z [<các tùy chá»n>]" + +#, c-format +msgid "unrecognized argument: %s" +msgstr "đối số không được thừa nháºn: %s" + +msgid "working without -z is not supported" +msgstr "không há»— trợ bá» tuỳ chá»n -z" + +msgid "pathspec arguments not supported" +msgstr "đặc tả đưá»ng dẫn chưa được há»— trợ" + +msgid "revision arguments not allowed" +msgstr "không cho phép dùng tên lần cải biên" + +msgid "invalid raw diff input" +msgstr "dòng diff không hợp lệ" + +msgid "tree objects not supported" +msgstr "không há»— trợ đối tượng cây" + +msgid "got EOF while reading path" +msgstr "gặp EOF khi Ä‘á»c đưá»ng dẫn" + +msgid "got EOF while reading destination path" +msgstr "gặp EOF khi Ä‘á»c đưá»ng dẫn Ä‘Ãch" + +#, c-format +msgid "unable to parse rename/copy score: %s" +msgstr "không thể Ä‘á»c rename/copy score:%s" + +#, c-format +msgid "unknown diff status: %c" +msgstr "trạng thái diff không rõ: %c" + msgid "--merge-base only works with two commits" msgstr "--merge-base chỉ hoạt động vá»›i hai lần chuyển giao" @@ -5809,17 +5865,20 @@ msgid "show progress after <n> objects" msgstr "hiển thị tiến triển sau <n> đối tượng" msgid "select handling of signed tags" -msgstr "chá»n Ä‘iá»u khiển cá»§a thẻ đã ký" +msgstr "chá»n cách xá» lý thẻ đã ký" + +msgid "select handling of signed commits" +msgstr "chá»n cách xá» lý lần chuyển giao đã ký" msgid "select handling of tags that tag filtered objects" -msgstr "chá»n sá»± xá» lý cá»§a các thẻ, cái mà đánh thẻ các đối tượng được lá»c ra" +msgstr "chá»n cách xá» lý cá»§a các thẻ trên các đối tượng đã bị lá»c ra" msgid "select handling of commit messages in an alternate encoding" msgstr "" -"chá»n bá»™ xá» lý cho các ghi chú cá»§a lần chuyển giao theo má»™t bá»™ mã thay thế" +"chá»n cách xá» lý cho các ghi chú lần chuyển giao được mã hoá theo bá»™ mã khác" msgid "dump marks to this file" -msgstr "đổ các đánh dấu nà y và o táºp-tin" +msgstr "xuất các đánh dấu nà y và o táºp-tin" msgid "import marks from this file" msgstr "nháºp và o đánh dấu từ táºp tin nà y" @@ -5985,22 +6044,6 @@ msgstr "%s đã không gá»i tất cả các đối tượng cần thiết" msgid "rejected %s because shallow roots are not allowed to be updated" msgstr "từ chối %s bởi vì các gốc nông thì không được phép cáºp nháºt" -#, c-format -msgid "" -"some local refs could not be updated; try running\n" -" 'git remote prune %s' to remove any old, conflicting branches" -msgstr "" -"má»™t số tham chiếu ná»™i bá»™ không thể được cáºp nháºt; hãy thá» chạy\n" -" 'git remote prune %s' để bá» Ä‘i những nhánh cÅ©, hay bị xung đột" - -#, c-format -msgid " (%s will become dangling)" -msgstr " (%s sẽ trở thà nh không đầu (không được quản lý))" - -#, c-format -msgid " (%s has become dangling)" -msgstr " (%s đã trở thà nh không đầu (không được quản lý))" - msgid "[deleted]" msgstr "[đã xóa]" @@ -6040,6 +6083,18 @@ msgstr "" "thông báo nà y. Cụ thể, 'git config set remote.%s.followRemoteHEAD %s'\n" "sẽ vô hiệu cảnh báo nà y tá»›i khi máy chá»§ đổi HEAD vá» chá»— khác." +#, c-format +msgid "" +"some local refs could not be updated; try running\n" +" 'git remote prune %s' to remove any old, conflicting branches" +msgstr "" +"má»™t số tham chiếu ná»™i bá»™ không thể được cáºp nháºt; hãy thá» chạy\n" +" 'git remote prune %s' để bá» Ä‘i những nhánh cÅ©, hay bị xung đột" + +#, c-format +msgid "fetching ref %s failed: %s" +msgstr "gặp lá»—i khi tải gói %s: %s" + msgid "multiple branches detected, incompatible with --set-upstream" msgstr "phát hiện nhiá»u nhánh, không tương thÃch vá»›i --set-upstream" @@ -6284,6 +6339,9 @@ msgstr "" "git for-each-ref [--contains [<lần-chuyển-giao>]] [--no-contains [<lần-" "chuyển-giao>]]" +msgid "git for-each-ref [--start-after <marker>]" +msgstr "git for-each-ref [--start-after <dấu>]" + msgid "quote placeholders suitably for shells" msgstr "trÃch dẫn dạng phù hợp cho shell" @@ -6299,6 +6357,12 @@ msgstr "trÃch dẫn dạng phù hợp cho Tcl" msgid "show only <n> matched refs" msgstr "hiển thị chỉ <n> tham chiếu khá»›p" +msgid "marker" +msgstr "dấu" + +msgid "start iteration after the provided marker" +msgstr "bắt đầu duyệt sau dấu chỉ định" + msgid "respect format colors" msgstr "các mà u định dạng lưu tâm" @@ -6323,9 +6387,15 @@ msgstr "Ä‘á»c các mẫu tham chiếu từ stdin" msgid "also include HEAD ref and pseudorefs" msgstr "bao gồm tham chiếu HEAD và giả tham chiếu" +msgid "cannot use --start-after with custom sort options" +msgstr "không thể dùng tùy chá»n --start-after vá»›i tuỳ chá»n sắp xếp" + msgid "unknown arguments supplied with --stdin" msgstr "đối số không rõ được chỉ định cùng vá»›i --stdin" +msgid "cannot use --start-after with patterns" +msgstr "không thể dùng tùy chá»n --start-after vá»›i mẫu" + msgid "git for-each-repo --config=<config> [--] <arguments>" msgstr "git for-each-repo --config=<tùy chá»n> [--] <đối số>" @@ -6454,10 +6524,6 @@ msgid "%s: object corrupt or missing: %s" msgstr "%s: thiếu đối tượng hoặc há»ng: %s" #, c-format -msgid "%s: object is of unknown type '%s': %s" -msgstr "%s: đối tượng có kiểu chưa biết '%s': %s" - -#, c-format msgid "%s: object could not be parsed: %s" msgstr "%s: không thể Ä‘á»c cú đối tượng: %s" @@ -6514,11 +6580,14 @@ msgstr "không thể tải pack-index cho gói '%s'" msgid "invalid rev-index for pack '%s'" msgstr "giá trị rev-index cho gói '%s' không hợp lệ" +msgid "Checking ref database" +msgstr "Äang kiểm tra database tham chiếu" + msgid "" "git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n" " [--[no-]full] [--strict] [--verbose] [--lost-found]\n" " [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n" -" [--[no-]name-objects] [<object>...]" +" [--[no-]name-objects] [--[no-]references] [<object>...]" msgstr "" "git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n" " [--[no-]full] [--strict] [--verbose] [--lost-found]\n" @@ -6561,6 +6630,9 @@ msgstr "hiển thị quá trình" msgid "show verbose names for reachable objects" msgstr "hiển thị tên chi tiết cho các đối tượng Ä‘á»c được" +msgid "check reference database consistency" +msgstr "Äang kiểm tra tÃnh nhất quán database tham chiếu" + msgid "Checking objects" msgstr "Äang kiểm tra các đối tượng" @@ -6719,6 +6791,9 @@ msgstr "đóng gói lại tất cả các gói khác ngoại trừ gói lá»›n nh msgid "pack prefix to store a pack containing pruned objects" msgstr "tiá»n tố cá»§a gói để lưu gói gồm những đối tượng đã loại bá»" +msgid "skip maintenance tasks typically done in the foreground" +msgstr "bá» qua các tác vụ bảo trì trong ná»n" + #, c-format msgid "failed to parse gc.logExpiry value %s" msgstr "gặp lá»—i khi Ä‘á»c giá trị gc.logExpiry %s" @@ -6792,14 +6867,14 @@ msgid "" msgstr "bá» qua tác vụ incremental-repack vì core.multiPackIndex bị vô hiệu hóa" #, c-format -msgid "lock file '%s' exists, skipping maintenance" -msgstr "đã có khóa cá»§a táºp tin '%s', bá» qua bảo trì" - -#, c-format msgid "task '%s' failed" msgstr "gặp lá»—i khi thá»±c hiện nhiệm vụ '%s'" #, c-format +msgid "lock file '%s' exists, skipping maintenance" +msgstr "đã có khóa cá»§a táºp tin '%s', bá» qua bảo trì" + +#, c-format msgid "'%s' is not a valid task" msgstr "'%s' không phải má»™t nhiệm vụ hợp lệ" @@ -6828,9 +6903,6 @@ msgstr "tác vụ" msgid "run a specific task" msgstr "chạy má»™t nhiệm vụ cụ thể" -msgid "use at most one of --auto and --schedule=<frequency>" -msgstr "dùng nhiá»u nhất là má»™t trong --auto và --schedule=<frequency>" - #, c-format msgid "unable to add '%s' value of '%s'" msgstr "không thể thêm giá trị '%s' cá»§a '%s'" @@ -7667,7 +7739,7 @@ msgid "invalid --decorate option: %s" msgstr "tùy chá»n --decorate không hợp lệ: %s" msgid "suppress diff output" -msgstr "chặn má»i đầu ra từ diff" +msgstr "không in ra kết quả diff" msgid "show source" msgstr "hiển thị mã nguồn" @@ -7691,18 +7763,10 @@ msgstr "" "theo vết sá»± tiến hóa cá»§a phạm vi dòng <start>,<end>, hoặc hà m :<tên hà m> " "trong <táºp tin>" -#, c-format -msgid "unrecognized argument: %s" -msgstr "đối số không được thừa nháºn: %s" - msgid "-L<range>:<file> cannot be used with pathspec" msgstr "-L<vùng>:<táºp tin> không thể được sá» dụng vá»›i đặc tả đưá»ng dẫn" #, c-format -msgid "Final output: %d %s\n" -msgstr "Äầu ra cuối cùng: %d %s\n" - -#, c-format msgid "git show %s: bad file" msgstr "git show %s: sai táºp tin" @@ -8085,7 +8149,7 @@ msgid "show debugging data" msgstr "hiển thị dữ liệu gỡ lá»—i" msgid "suppress duplicate entries" -msgstr "chặn các mục tin trùng lặp" +msgstr "không in ra các mục tin trùng lặp" msgid "show sparse directories in the presence of a sparse index" msgstr "hiển thị thư mục thưa trong sá»± có mặt cá»§a chỉ mục thưa" @@ -8278,7 +8342,7 @@ msgid "choose a diff algorithm" msgstr "chá»n thuáºt toán diff" msgid "for conflicts, use this marker size" -msgstr "nếu xung đột, hãy sá» dụng kÃch thước bá»™ tạo nà y" +msgstr "nếu xung đột, hãy sá» dụng kÃch thước dấu nà y" msgid "do not warn about conflicts" msgstr "không cảnh báo vá» các xung đột xảy ra" @@ -8345,6 +8409,9 @@ msgstr "chỉ hoà trá»™n đơn giản" msgid "also show informational/conflict messages" msgstr "hiển thị thông báo chú thÃch/xung đột" +msgid "suppress all output; only exit status wanted" +msgstr "không in ra kết quả; chỉ trả vá» kết quả thá»±c thi" + msgid "list filenames without modes/oids/stages" msgstr "liệt kê tên táºp tin không kèm chế độ/oid/stage" @@ -8405,6 +8472,9 @@ msgstr "hiển thị diffstat (thống kê khác biệt) phÃa dưới hòa trá» msgid "(synonym to --stat)" msgstr "(đồng nghÄ©a vá»›i --stat)" +msgid "show a compact-summary at the end of the merge" +msgstr "hiển thị compact-summary (tổng hợp tóm lược) phÃa dưới hòa trá»™n" + msgid "add (at most <n>) entries from shortlog to merge commit message" msgstr "thêm (Ãt nhất <n>) mục từ shortlog cho ghi chú chuyển giao hòa trá»™n" @@ -8424,7 +8494,7 @@ msgid "abort if fast-forward is not possible" msgstr "huá»· lệnh nếu không thể chuyển-tiếp-nhanh" msgid "verify that the named commit has a valid GPG signature" -msgstr "thẩm tra xem lần chuyển giao có tên đó có chữ ký GPG hợp lệ hay không" +msgstr "xác minh xem lần chuyển giao có tên đó có chữ ký GPG hợp lệ hay không" msgid "strategy" msgstr "chiến lược" @@ -8677,10 +8747,6 @@ msgid "error: tag input does not pass fsck: %s" msgstr "lá»—i: đầu và o thẻ không qua kiểm tra fsck: %s" #, c-format -msgid "%d (FSCK_IGNORE?) should never trigger this callback" -msgstr "%d (FSCK_IGNORE?) không bao giá» nên kÃch hoạt callback nà y" - -#, c-format msgid "could not read tagged object '%s'" msgstr "không thể Ä‘á»c đối tượng được đánh thẻ %s" @@ -8754,12 +8820,15 @@ msgstr "" "trong suốt quá trình đóng gói lại, gom các táºp tin gói có kÃch cỡ nhá» hÆ¡n " "và o má»™t bó cái mà lá»›n hÆ¡n kÃch thước nà y" -msgid "git mv [<options>] <source>... <destination>" -msgstr "git mv [<các tùy chá»n>] <nguồn>... <Ä‘Ãch>" +msgid "git mv [-v] [-f] [-n] [-k] <source> <destination>" +msgstr "git mv [-v] [-f] [-n] [-k] <nguồn> <Ä‘Ãch>" + +msgid "git mv [-v] [-f] [-n] [-k] <source>... <destination-directory>" +msgstr "git mv [-v] [-f] [-n] [-k] <nguồn>... <thư-mục-Ä‘Ãch>" #, c-format msgid "Directory %s is in index and no submodule?" -msgstr "Thư mục '%s' có ở trong chỉ mục mà không có mô-Ä‘un con?" +msgstr "Thư mục %s có ở trong chỉ mục mà không có mô-Ä‘un con?" msgid "Please stage your changes to .gitmodules or stash them to proceed" msgstr "" @@ -8825,6 +8894,10 @@ msgid "%s, source=%s, destination=%s" msgstr "%s, nguồn=%s, Ä‘Ãch=%s" #, c-format +msgid "cannot move both '%s' and its parent directory '%s'" +msgstr "Không thể di chuyển cả %s và thư mục cha %s" + +#, c-format msgid "Renaming %s to %s\n" msgstr "Äổi tên %s thà nh %s\n" @@ -9189,16 +9262,26 @@ msgstr "dùng 'notes' từ <notes-ref>" msgid "unknown subcommand: `%s'" msgstr "không hiểu câu lệnh con: `%s'" -msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]" -msgstr "" -"git pack-objects --stdout [<các tùy chá»n>] [< <danh-sách-tham-chiếu> | < " -"<danh-sách-đối-tượng>]" - msgid "" -"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<pack-name>]\n" +" [--cruft] [--cruft-expiration=<time>]\n" +" [--stdout [--filter=<filter-spec>] | <base-name>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=<n>] [--path-walk] < <object-list>" msgstr "" -"git pack-objects [<các tùy chá»n>] <base-name> [< <danh-sách-ref> | < <danh-" -"sách-đối-tượng>]" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<pack-name>]\n" +" [--cruft] [--cruft-expiration=<time>]\n" +" [--stdout [--filter=<filter-spec>] | <base-name>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=<n>] [--path-walk] < <object-list>" #, c-format msgid "invalid --name-hash-version option: %d" @@ -9303,6 +9386,14 @@ msgstr "không thể đóng gói các đối tượng tiếp cáºn được từ msgid "unable to get type of object %s" msgstr "không thể lấy kiểu cá»§a đối tượng '%s'" +msgid "Compressing objects by path" +msgstr "Äang nén các đối tượng theo đưá»ng dẫn" + +#, c-format +msgid "Path-based delta compression using up to %d thread" +msgid_plural "Path-based delta compression using up to %d threads" +msgstr[0] "Nén delta đưá»ng dẫn dùng tá»›i %d tuyến trình" + msgid "Compressing objects" msgstr "Äang nén các đối tượng" @@ -9378,6 +9469,9 @@ msgstr "đối tượng mất tại %s không thể đã kiểm tra" msgid "unable to force loose object" msgstr "không thể buá»™c mất đối tượng" +msgid "failed to pack objects via path-walk" +msgstr "gặp lá»—i khi nén đối tượng theo thuáºt toán duyệt cây" + #, c-format msgid "not a rev '%s'" msgstr "không phải má»™t rev '%s'" @@ -9488,6 +9582,9 @@ msgstr "sá» dụng thuáºt toán 'sparse reachability'" msgid "create thin packs" msgstr "tạo gói nhẹ" +msgid "use the path-walk API to walk objects when possible" +msgstr "dùng API path-walk để duyệt đối tượng nếu có thể" + msgid "create packs suitable for shallow fetches" msgstr "tạo gói để phù hợp cho lấy vá» nông (shallow)" @@ -9542,6 +9639,10 @@ msgid "pack.deltaCacheLimit is too high, forcing %d" msgstr "pack.deltaCacheLimit là quá cao, ép dùng %d" #, c-format +msgid "cannot use %s with %s" +msgstr "Không thể dùng %s vá»›i %s" + +#, c-format msgid "bad pack compression level %d" msgstr "mức nén gói %d không hợp lệ" @@ -9555,18 +9656,12 @@ msgstr "giá»›i hạn kÃch thước tối thiểu cá»§a gói là 1 MiB" msgid "--thin cannot be used to build an indexable pack" msgstr "không thể dùng --thin để xây dá»±ng gói đánh chỉ mục được" -msgid "cannot use --filter with --stdin-packs" -msgstr "không thể dùng tùy chá»n --filter vá»›i --stdin-packs" - msgid "cannot use internal rev list with --stdin-packs" msgstr "không thể dùng danh sách rev ná»™i bá»™ vá»›i --stdin-packs" msgid "cannot use internal rev list with --cruft" msgstr "không thể dùng danh sách rev ná»™i bá»™ vá»›i --cruft" -msgid "cannot use --stdin-packs with --cruft" -msgstr "không thể dùng tùy chá»n --stdin-packs vá»›i --cruft" - msgid "Enumerating objects" msgstr "Duyệt các đối tượng" @@ -9579,22 +9674,6 @@ msgstr "" "lại pack %<PRIu32> (trong số %<PRIuMAX>)" msgid "" -"'git pack-redundant' is nominated for removal.\n" -"If you still use this command, please add an extra\n" -"option, '--i-still-use-this', on the command line\n" -"and let us know you still use it by sending an e-mail\n" -"to <git@vger.kernel.org>. Thanks.\n" -msgstr "" -"'git pack-redundant' đã được đỠcỠđể loại bá».\n" -"Nếu bạn vẫn còn sá» dụng lệnh nà y, vui lòng bổ sung\n" -"thêm má»™t tùy chá»n, '--i-still-use-this', trên dòng lệnh\n" -"và cho chúng tôi biết bạn vẫn sá» dụng nó bằng cách gá»i e-mail\n" -"đến <git@vger.kernel.org>. Xin cảm Æ¡n.\n" - -msgid "refusing to run without --i-still-use-this" -msgstr "từ chối chạy lệnh nà y mà không có --i-still-use-this" - -msgid "" "git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude " "<pattern>]" msgstr "" @@ -9751,6 +9830,10 @@ msgstr "" msgid "unable to access commit %s" msgstr "không thể truy cáºp lần chuyển giao '%s'" +#, c-format +msgid "invalid refspec '%s'" +msgstr "refspec không hợp lệ '%s'" + msgid "ignoring --verify-signatures for rebase" msgstr "bá» qua --verify-signatures khi cải tổ" @@ -10218,7 +10301,7 @@ msgid "debug unpack-trees" msgstr "gỡ lá»—i 'unpack-trees'" msgid "suppress feedback messages" -msgstr "không xuất các thông tin phản hồi" +msgstr "không in ra các thông tin phản hồi" msgid "You need to resolve your current index first" msgstr "Bạn cần phải giải quyết chỉ mục hiện tại cá»§a bạn trước đã" @@ -10716,6 +10799,9 @@ msgstr "" msgid "git reflog exists <ref>" msgstr "git reflog exists <tham_chiếu>" +msgid "git reflog drop [--all [--single-worktree] | <refs>...]" +msgstr "git reflog drop [--all [--single-worktree] | <tham_chiếu>...]" + #, c-format msgid "invalid timestamp '%s' given to '--%s'" msgstr "dấu vết thá»i gian không hợp lệ '%s' đưa cho '--%s'" @@ -10764,8 +10850,8 @@ msgid "Marking reachable objects..." msgstr "Äánh dấu các đối tượng tiếp cáºn được..." #, c-format -msgid "%s points nowhere!" -msgstr "%s chẳng chỉ đến đâu cả!" +msgid "reflog could not be found: '%s'" +msgstr "không tìm thấy reflog: '%s'" msgid "no reflog specified to delete" msgstr "chưa chỉ ra reflog để xóa" @@ -10774,6 +10860,15 @@ msgstr "chưa chỉ ra reflog để xóa" msgid "invalid ref format: %s" msgstr "định dạng tham chiếu không hợp lệ: %s" +msgid "drop the reflogs of all references" +msgstr "bá» các reflogs cá»§a má»i tham chiếu" + +msgid "drop reflogs from the current worktree only" +msgstr "bá» reflogs chỉ từ thư mục là m việc hiện tại" + +msgid "references specified along with --all" +msgstr "chỉ định tham chiếu vá»›i tùy chá»n --all" + msgid "git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]" msgstr "git refs migrate --ref-format=<định dạng> [--no-reflog] [--dry-run]" @@ -10881,6 +10976,14 @@ msgstr "" msgid "unknown --mirror argument: %s" msgstr "không hiểu tham số --mirror: %s" +#, c-format +msgid "remote name '%s' is a subset of existing remote '%s'" +msgstr "máy chá»§ '%s' là táºp con cá»§a máy chá»§ đã sẵn có '%s'" + +#, c-format +msgid "remote name '%s' is a superset of existing remote '%s'" +msgstr "máy chá»§ '%s' là táºp cha cá»§a máy chá»§ đã sẵn có '%s'" + msgid "fetch the remote branches" msgstr "lấy vá» các nhánh từ máy chá»§" @@ -11175,14 +11278,6 @@ msgid "Could not set up %s" msgstr "Không thể cà i đặt %s" #, c-format -msgid " %s will become dangling!" -msgstr " %s sẽ trở thà nh không đầu (không được quản lý)!" - -#, c-format -msgid " %s has become dangling!" -msgstr " %s đã trở thà nh không đầu (không được quản lý)!" - -#, c-format msgid "Pruning %s" msgstr "Äang xén bá»›t %s" @@ -11246,11 +11341,11 @@ msgstr "chi tiết; phải được đặt trước má»™t lệnh-con" msgid "" "git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n" -"[--write-midx] [--name-hash-version=<n>]" +"[--write-midx] [--name-hash-version=<n>] [--path-walk]" msgstr "" "git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<tên-pack>]\n" -"[--write-midx] [--name-hash-version=<n>]" +"[--write-midx] [--name-hash-version=<n>] [--path-walk]" msgid "" "Incremental repacks are incompatible with bitmap indexes. Use\n" @@ -11317,6 +11412,9 @@ msgstr "ngà y ước tÃnh" msgid "with --cruft, expire objects older than this" msgstr "vá»›i --cruft, đánh dấu hết hạn các đối tượng cÅ© hÆ¡n khoảng nà y" +msgid "with --cruft, only repack cruft packs smaller than this" +msgstr "vá»›i --cruft, chỉ nén các gói nhá» hÆ¡n khoảng nà y" + msgid "remove redundant packs, and run git-prune-packed" msgstr "xóa bá» các gói dư thừa, và chạy git-prune-packed" @@ -11328,7 +11426,10 @@ msgstr "chuyển --no-reuse-object cho git-pack-objects" msgid "" "specify the name hash version to use for grouping similar objects by path" -msgstr "phiên bản hà m băm để nhóm đối tượng giống nhau theo đưá»ng dẫn" +msgstr "chá»n phiên bản hà m băm để nhóm đối tượng giống nhau theo đưá»ng dẫn" + +msgid "pass --path-walk to git-pack-objects" +msgstr "chuyển --path-walk cho git-pack-objects" msgid "do not run git-update-server-info" msgstr "không chạy git-update-server-info" @@ -11786,6 +11887,9 @@ msgstr "không thể dung lượng đĩa đã dùng cá»§a %s" msgid "invalid value for '%s': '%s', the only allowed format is '%s'" msgstr "giá trị không hợp lệ cho '%s': '%s', chỉ cho phép định dạng là '%s'" +msgid "-z option used with unsupported option" +msgstr "tuỳ chá»n -z dùng vá»›i tuỳ chá»n không há»— trợ" + msgid "rev-list does not support display of notes" msgstr "rev-list không há»— trợ hiển thị các ghi chú" @@ -12080,7 +12184,7 @@ msgid "sort output according to the number of commits per author" msgstr "sắp xếp kết quả theo số lượng chuyển giao trên má»—i tác giả" msgid "suppress commit descriptions, only provides commit count" -msgstr "chặn má»i mô tả lần chuyển giao, chỉ đưa ra số lượng lần chuyển giao" +msgstr "không in ra mô tả, chỉ đưa ra số lượng lần chuyển giao" msgid "show the email address of each author" msgstr "hiển thị thư Ä‘iện tá» cho từng tác giả" @@ -12141,7 +12245,7 @@ msgid "synonym to more=-1" msgstr "đồng nghÄ©a vá»›i more=-1" msgid "suppress naming strings" -msgstr "chặn các chuá»—i đặt tên" +msgstr "không in ra các chuá»—i đặt tên" msgid "include the current branch" msgstr "bao gồm nhánh hiện hà nh" @@ -12463,17 +12567,23 @@ msgstr "" msgid "git stash create [<message>]" msgstr "git stash create [<ghi chú>]" +msgid "git stash export (--print | --to-ref <ref>) [<stash>...]" +msgstr "git stash export (--print | --to-ref <ref>) [<stash>...]" + +msgid "git stash import <commit>" +msgstr "git stash import <lần-chuyển-giao>" + #, c-format msgid "'%s' is not a stash-like commit" msgstr "'%s' không phải là lần chuyển giao kiểu-stash" +msgid "No stash entries found." +msgstr "Không tìm thấy các mục tạm cất (stash) nà o." + #, c-format msgid "Too many revisions specified:%s" msgstr "Chỉ ra quá nhiá»u lần cải biên: %s" -msgid "No stash entries found." -msgstr "Không tìm thấy các mục tạm cất (stash) nà o." - #, c-format msgid "%s is not a valid reference" msgstr "'%s' không phải má»™t tham chiếu hợp lệ" @@ -12625,19 +12735,74 @@ msgstr "bao gồm các táºp tin không được theo dõi trong stash" msgid "include ignore files" msgstr "bao gồm các táºp tin bị bá» qua" -msgid "skip and remove all lines starting with comment character" -msgstr "giữ và xóa bá» má»i dòng bắt đầu bằng ký tá»± ghi chú" +#, c-format +msgid "cannot parse commit %s" +msgstr "không thể Ä‘á»c lần chuyển giao %s" -msgid "prepend comment character and space to each line" -msgstr "treo trước ký tá»± ghi chú và ký tá»± khoảng trắng cho từng dòng" +#, c-format +msgid "invalid author or committer for %s" +msgstr "danh tÃnh tác giả/ngưá»i chuyển giao không hợp lệ cho %s" + +msgid "could not write commit" +msgstr "không thể ghi lần chuyển giao" #, c-format -msgid "Expecting a full ref name, got %s" -msgstr "Cần tên tham chiếu dạng đầy đủ, nhưng lại có %s" +msgid "not a valid revision: %s" +msgstr "không phải revision hợp lệ: %s" #, c-format -msgid "could not get a repository handle for submodule '%s'" -msgstr "không thể lấy thẻ quản kho cho mô-Ä‘un-con '%s'" +msgid "not a commit: %s" +msgstr "không phải là lần chuyển giao: %s" + +#, c-format +msgid "%s is not a valid exported stash commit" +msgstr "%s không phải má»™t lần chuyển giao tạm cất hợp lệ" + +#, c-format +msgid "found root commit %s with invalid data" +msgstr "lần chuyển giao gốc %s có dữ liệu không hợp lệ" + +#, c-format +msgid "found stash commit %s without expected prefix" +msgstr "lần chuyển giao tạm cất %s không có tiá»n tố" + +#, c-format +msgid "cannot parse parents of commit: %s" +msgstr "không thể Ä‘á»c lần chuyển giao cha: %s" + +#, c-format +msgid "%s does not look like a stash commit" +msgstr "'%s' không phải lần chuyển giao tạm cất" + +#, c-format +msgid "cannot read commit buffer for %s" +msgstr "không thể Ä‘á»c buffer chuyển giao cho %s" + +#, c-format +msgid "cannot save the stash for %s" +msgstr "không thể cất tạm cho %s" + +msgid "unable to write base commit" +msgstr "không thể ghi gốc chuyển giao" + +#, c-format +msgid "unable to find stash entry %s" +msgstr "không tìm thấy stash entry %s" + +msgid "print the object ID instead of writing it to a ref" +msgstr "in ID đối tượng thay vì ghi ra tham chiếu" + +msgid "save the data to the given ref" +msgstr "lưu dữ liệu vá» tham chiếu chỉ định" + +msgid "exactly one of --print and --to-ref is required" +msgstr "yêu cầu má»™t trong hai tuỳ chá»n --print và --to-ref" + +msgid "skip and remove all lines starting with comment character" +msgstr "giữ và xóa bá» má»i dòng bắt đầu bằng ký tá»± ghi chú" + +msgid "prepend comment character and space to each line" +msgstr "treo trước ký tá»± ghi chú và ký tá»± khoảng trắng cho từng dòng" #, c-format msgid "" @@ -12648,6 +12813,10 @@ msgstr "" "sở hữu chÃnh nó." #, c-format +msgid "could not get a repository handle for submodule '%s'" +msgstr "không thể lấy thẻ quản kho cho mô-Ä‘un-con '%s'" + +#, c-format msgid "No url found for submodule path '%s' in .gitmodules" msgstr "Không tìm thấy url cho đưá»ng dẫn mô-Ä‘un-con '%s' trong .gitmodules" @@ -12799,7 +12968,7 @@ msgid "" "with a .git file by using absorbgitdirs." msgstr "" "Cây là m việc mô-Ä‘un-con '%s' có chứa thư mục .git. Việc nà y sẽ được thay thế " -"vá»›i má»™t táºp tin .git bằng các sá» dụng absorbgitdirs." +"vá»›i má»™t táºp tin .git bằng cách sá» dụng absorbgitdirs." #, c-format msgid "" @@ -12829,7 +12998,7 @@ msgid "remove submodule working trees even if they contain local changes" msgstr "gỡ bá» cây là m việc cá»§a mô-Ä‘un-con ngay cả khi nó có thay đổi ná»™i bá»™" msgid "unregister all submodules" -msgstr "bỠđăng ký tất cả các trong mô-Ä‘un-con" +msgstr "bỠđăng ký tất cả các mô-Ä‘un-con" msgid "" "git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]" @@ -12887,7 +13056,7 @@ msgid "could not get submodule directory for '%s'" msgstr "không thể lấy thư mục mô-Ä‘un-con cho '%s'" msgid "alternative anchor for relative paths" -msgstr "Ä‘iểm neo thay thế cho các đưá»ng dẫn tương đối" +msgstr "Ä‘iểm gốc thay thế cho các đưá»ng dẫn tương đối" msgid "where the new submodule will be cloned to" msgstr "nhân bản mô-Ä‘un-con má»›i và o chá»— nà o" @@ -13007,6 +13176,10 @@ msgstr "" "project cha lại không trên bất kỳ nhánh nà o" #, c-format +msgid "Expecting a full ref name, got %s" +msgstr "Cần tên tham chiếu dạng đầy đủ, nhưng lại có %s" + +#, c-format msgid "Unable to find current revision in submodule path '%s'" msgstr "Không tìm thấy lần cải biên hiện hà nh trong đưá»ng dẫn mô-Ä‘un-con '%s'" @@ -13130,7 +13303,7 @@ msgstr "'%s' đã tồn tại từ trước và không phải là má»™t kho git #, c-format msgid "A git directory for '%s' is found locally with remote(s):\n" -msgstr "Thư mục git cho '%s' được tìm thấy má»™t cách cục bá»™ vá»›i các máy chá»§:\n" +msgstr "Thư mục git cho '%s' được tìm thấy tại cục bá»™ vá»›i các máy chá»§:\n" #, c-format msgid "" @@ -13208,6 +13381,10 @@ msgid "repo URL: '%s' must be absolute or begin with ./|../" msgstr "repo URL: '%s' phải là đưá»ng dẫn tuyệt đối hoặc là bắt đầu bằng ./|../" #, c-format +msgid "submodule name '%s' already used for path '%s'" +msgstr "Mô-Ä‘un-con '%s' đã dùng cho đưá»ng dẫn '%s'" + +#, c-format msgid "'%s' is not a valid submodule name" msgstr "'%s' không phải là má»™t tên mô-Ä‘un-con hợp lệ" @@ -13338,7 +13515,7 @@ msgid "delete tags" msgstr "xóa thẻ" msgid "verify tags" -msgstr "thẩm tra thẻ" +msgstr "xác minh thẻ" msgid "Tag creation options" msgstr "Tùy chá»n tạo thẻ" @@ -13356,7 +13533,7 @@ msgid "annotated and GPG-signed tag" msgstr "thẻ chú giải và ký kiểu GPG" msgid "use another key to sign the tag" -msgstr "dùng kháo khác để ký thẻ" +msgstr "dùng khoá khác để ký thẻ" msgid "replace the tag if exists" msgstr "thay thế nếu thẻ đó đã có trước" @@ -13368,7 +13545,7 @@ msgid "Tag listing options" msgstr "Các tùy chá»n liệt kê thẻ" msgid "show tag list in columns" -msgstr "hiển thị danh sách thẻ trong các cá»™t" +msgstr "hiển thị danh sách thẻ theo cá»™t" msgid "print only tags that contain the commit" msgstr "chỉ hiển thị những nhánh mà nó chứa lần chuyển giao" @@ -13503,7 +13680,7 @@ msgid "clear assumed-unchanged bit" msgstr "xóa bÃt assumed-unchanged (giả định là không thay đổi)" msgid "mark files as \"index-only\"" -msgstr "đánh dấu các táºp tin là 'chỉ-Ä‘á»c'" +msgstr "đánh dấu các táºp tin là \"chỉ-Ä‘á»c\"" msgid "clear skip-worktree bit" msgstr "xóa bÃt skip-worktree" @@ -13529,19 +13706,19 @@ msgid "add entries from standard input to the index" msgstr "không thể Ä‘á»c các mục từ stdin và o chỉ mục" msgid "repopulate stages #2 and #3 for the listed paths" -msgstr "phục hồi các trạng thái #2 và #3 cho các đưá»ng dẫn được liệt kê" +msgstr "phục hồi giai Ä‘oạn #2 và #3 cho các đưá»ng dẫn được liệt kê" msgid "only update entries that differ from HEAD" msgstr "chỉ cáºp nháºt các mục tin mà nó khác biệt so vá»›i HEAD" msgid "ignore files missing from worktree" -msgstr "bá» qua các táºp-tin thiếu trong thư-mục là m việc" +msgstr "bá» qua các táºp tin thiếu trong thư mục là m việc" msgid "report actions to standard output" msgstr "ghi các thao tác ra stdout" msgid "(for porcelains) forget saved unresolved conflicts" -msgstr "(cho 'porcelains') quên các xung đột chưa được giải quyết đã ghi" +msgstr "(cho lệnh porcelain) quên các xung đột chưa được giải quyết đã ghi" msgid "write index in this format" msgstr "ghi chỉ mục ở định dạng nà y" @@ -13603,7 +13780,7 @@ msgstr "" "sá»± muốn tắt bá»™ đệm chưa theo dõi" msgid "Untracked cache disabled" -msgstr "Nhá»› đệm không theo vết bị tắt" +msgstr "Bá»™ đệm không theo vết bị tắt" msgid "" "core.untrackedCache is set to false; remove or change it, if you really want " @@ -13639,8 +13816,8 @@ msgstr "git update-ref [<các tùy chá»n>] -d <refname> [<oid-cÅ©>]" msgid "git update-ref [<options>] <refname> <new-oid> [<old-oid>]" msgstr "git update-ref [<các tùy chá»n>] <refname> <oid-má»›i> [<oid-cÅ©>]" -msgid "git update-ref [<options>] --stdin [-z]" -msgstr "git update-ref [<các tùy chá»n>] --stdin [-z]" +msgid "git update-ref [<options>] --stdin [-z] [--batch-updates]" +msgstr "git update-ref [<các tùy chá»n>] --stdin [-z] [--batch-updates]" msgid "delete the reference" msgstr "xóa tham chiếu" @@ -13654,8 +13831,11 @@ msgstr "stdin có các đối số được kết thúc bởi NUL" msgid "read updates from stdin" msgstr "Ä‘á»c cáºp nháºt từ stdin" +msgid "batch reference updates" +msgstr "cáºp nháºt tham chiếu theo lô" + msgid "update the info files from scratch" -msgstr "cáºp nháºt các táºp tin thông tin từ Ä‘iểm xuất phát" +msgstr "cáºp nháºt các táºp tin thông tin lại từ đầu" msgid "" "git-upload-pack [--[no-]strict] [--timeout=<n>] [--stateless-rpc]\n" @@ -13831,10 +14011,6 @@ msgid "Preparing worktree (checking out '%s')" msgstr "Äang chuẩn bị cây là m việc (Ä‘ang checkout '%s')" #, c-format -msgid "unreachable: invalid reference: %s" -msgstr "không tham chiếu được: tham chiếu không hợp lệ: %s" - -#, c-format msgid "Preparing worktree (detached HEAD %s)" msgstr "Äang chuẩn bị cây là m việc (HEAD đã tách rá»i '%s')" @@ -13903,9 +14079,7 @@ msgid "show extended annotations and reasons, if available" msgstr "hiển thị chú thÃch và lý do mở rá»™ng, nếu có" msgid "add 'prunable' annotation to worktrees older than <time>" -msgstr "" -"thêm chú thÃch kiểu 'prunable' cho các cây là m việc hết hạn cÅ© hÆ¡n khoảng " -"<thá»i gian>" +msgstr "thêm chú thÃch 'prunable' cho các cây là m việc cÅ© hÆ¡n <thá»i gian>" msgid "terminate records with a NUL character" msgstr "kết thúc các bản ghi bằng ký tá»± NULL" @@ -13960,7 +14134,7 @@ msgstr "" #, c-format msgid "validation failed, cannot move working tree: %s" -msgstr "thẩm tra gặp lá»—i, không thể di chuyển má»™t cây-là m-việc: %s" +msgstr "xác minh gặp lá»—i, không thể di chuyển má»™t cây-là m-việc: %s" #, c-format msgid "failed to move '%s' to '%s'" @@ -14000,7 +14174,7 @@ msgstr "" #, c-format msgid "validation failed, cannot remove working tree: %s" -msgstr "thẩm tra gặp lá»—i, không thể gỡ bá» má»™t cây-là m-việc: %s" +msgstr "xác minh gặp lá»—i, không thể gỡ bá» má»™t cây-là m-việc: %s" #, c-format msgid "repair: %s: %s" @@ -14282,7 +14456,7 @@ msgid "Record changes to the repository" msgstr "Ghi các thay đổi và o kho chứa" msgid "Write and verify Git commit-graph files" -msgstr "Ghi và thẩm tra các táºp tin đồ há»a các lần chuyển giao Git" +msgstr "Ghi và xác minh các táºp tin đồ há»a các lần chuyển giao Git" msgid "Create a new commit object" msgstr "Tạo má»™t đối tượng chuyển giao" @@ -14334,6 +14508,9 @@ msgstr "So sánh các táºp tin trong cây là m việc và chỉ mục" msgid "Compare a tree to the working tree or index" msgstr "So sánh các cây trong cây là m việc hoặc chỉ mục" +msgid "Compare the content and mode of provided blob pairs" +msgstr "So sánh ná»™i dung và chế độ cá»§a hai blob" + msgid "Compares the content and mode of blobs found via two tree objects" msgstr "" "So sánh ná»™i dung và chế độ cá»§a các blob tìm thấy thông qua hai đối tượng cây" @@ -14370,7 +14547,7 @@ msgstr "Chuẩn bị các bản vá để gá»i qua thư Ä‘iện tá»" msgid "Verifies the connectivity and validity of the objects in the database" msgstr "" -"Thẩm tra lại tÃnh kết nối và tÃnh hiệu lá»±c cảu các đối tượng trong cÆ¡ sở dữ " +"xác minh lại tÃnh kết nối và tÃnh hiệu lá»±c cảu các đối tượng trong cÆ¡ sở dữ " "liệu" msgid "Cleanup unnecessary files and optimize the local repository" @@ -14469,7 +14646,7 @@ msgid "Build a tree-object from ls-tree formatted text" msgstr "Xây dá»±ng má»™t tree-object từ văn bản định dạng ls-tree" msgid "Write and verify multi-pack-indexes" -msgstr "Ghi và thẩm tra các multi-pack-indexes" +msgstr "Ghi và xác minh các multi-pack-indexes" msgid "Move or rename a file, a directory, or a symlink" msgstr "Di chuyển hay đổi tên má»™t táºp tin, thư mục hoặc liên kết má»m" @@ -14627,7 +14804,7 @@ msgid "Read, modify and delete symbolic refs" msgstr "Äá»c, sá»a và xóa tham chiếu má»m" msgid "Create, list, delete or verify a tag object signed with GPG" -msgstr "Tạo, liệt kê, xóa hay xác thá»±c má»™t đối tượng thẻ được ký bằng GPG" +msgstr "Tạo, liệt kê, xóa hay xác minh má»™t đối tượng thẻ được ký bằng GPG" msgid "Creates a temporary file with a blob's contents" msgstr "Tạo má»™t táºp tin tạm vá»›i ná»™i dung cá»§a blob" @@ -15041,7 +15218,7 @@ msgstr "" "'%s' và '%s')" msgid "Verifying commits in commit graph" -msgstr "Äang thẩm tra các lần chuyển giao trong đồ thị lần chuyển giao" +msgstr "Äang xác minh các lần chuyển giao trong đồ thị lần chuyển giao" #, c-format msgid "could not parse commit %s" @@ -15447,14 +15624,6 @@ msgid "bad numeric config value '%s' for '%s' in %s: %s" msgstr "sai giá trị bằng số cá»§a cấu hình '%s' cho '%s' trong %s: %s" #, c-format -msgid "invalid value for variable %s" -msgstr "giá trị không hợp lệ cho biến %s" - -#, c-format -msgid "ignoring unknown core.fsync component '%s'" -msgstr "bá» qua thà nh phần core.fsync chưa biết '%s'" - -#, c-format msgid "bad boolean config value '%s' for '%s'" msgstr "sai giá trị kiểu boolean cá»§a cấu hình '%s' cho '%s'" @@ -15467,44 +15636,6 @@ msgid "'%s' for '%s' is not a valid timestamp" msgstr "'%s' dà nh cho '%s' không phải là dấu vết thá»i gian hợp lệ" #, c-format -msgid "abbrev length out of range: %d" -msgstr "chiá»u dà i abbrev nằm ngoà i phạm vi: %d" - -#, c-format -msgid "bad zlib compression level %d" -msgstr "mức nén zlib %d là sai" - -#, c-format -msgid "%s cannot contain newline" -msgstr "%s không thể chứa ký tá»± xuống dòng" - -#, c-format -msgid "%s must have at least one character" -msgstr "%s phải có Ãt nhất má»™t ký tá»±" - -#, c-format -msgid "ignoring unknown core.fsyncMethod value '%s'" -msgstr "bá» qua giá trị core.fsyncMethod chưa biết '%s'" - -msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" -msgstr "core.fsyncObjectFiles đã lạc háºu; hãy dùng core.fsync để thay thế" - -#, c-format -msgid "invalid mode for object creation: %s" -msgstr "chế độ không hợp lệ đối vá»›i việc tạo đối tượng: %s" - -#, c-format -msgid "malformed value for %s" -msgstr "giá trị cho %s sai dạng" - -#, c-format -msgid "malformed value for %s: %s" -msgstr "giá trị cho %s sai dạng: %s" - -msgid "must be one of nothing, matching, simple, upstream or current" -msgstr "phải là má»™t trong số nothing, matching, simple, upstream hay current" - -#, c-format msgid "unable to load config blob object '%s'" msgstr "không thể tải đối tượng blob cấu hình '%s'" @@ -16004,8 +16135,10 @@ msgstr "không thể so sánh stdin và thư mục" msgid "cannot compare a named pipe to a directory" msgstr "không thể so sánh pipe có tên và thư mục" -msgid "git diff --no-index [<options>] <path> <path>" -msgstr "git diff --no-index [<các tùy chá»n>] </đưá»ng/dẫn> </đưá»ng/dẫn>" +msgid "git diff --no-index [<options>] <path> <path> [<pathspec>...]" +msgstr "" +"git diff --no-index [<các tùy chá»n>] </đưá»ng/dẫn> </đưá»ng/dẫn> [<đặc tả " +"đưá»ng dẫn>...]" msgid "" "Not a git repository. Use --no-index to compare two paths outside a working " @@ -16014,6 +16147,11 @@ msgstr "" "Không phải là má»™t thư mục git. Dùng --no-index để so sánh hai đưá»ng dẫn bên " "ngoà i cây là m việc" +msgid "" +"Limiting comparison with pathspecs is only supported if both paths are " +"directories." +msgstr "Giá»›i hạn so sánh theo đưá»ng dẫn chỉ há»— trợ vá»›i hai thư mục." + #, c-format msgid " Failed to parse dirstat cut-off percentage '%s'\n" msgstr " Gặp lá»—i khi Ä‘á»c phần trăm cắt bá» dirstat '%s'\n" @@ -16193,20 +16331,19 @@ msgid "synonym for --dirstat=files,<param1>,<param2>..." msgstr "đồng nghÄ©a vá»›i --dirstat=files,<tham_số_1>,<tham_số_2>..." msgid "warn if changes introduce conflict markers or whitespace errors" -msgstr "" -"cảnh báo nếu các thay đổi đưa ra các bá»™ tạo xung đột hay lá»—i khoảng trắng" +msgstr "cảnh báo nếu các thay đổi chứa dấu xung đột hay lá»—i khoảng trắng" msgid "condensed summary such as creations, renames and mode changes" -msgstr "tổng hợp dạng xúc tÃch như là tạo, đổi tên và các thay đổi chế độ" +msgstr "tổng hợp ngắn gá»n gồm việc tạo, đổi tên và thay đổi chế độ" msgid "show only names of changed files" -msgstr "chỉ hiển thị tên cá»§a các táºp tin đổi" +msgstr "chỉ hiển thị tên cá»§a các táºp tin thay đổi" msgid "show only names and status of changed files" -msgstr "chỉ hiển thị tên táºp tin và tình trạng cá»§a các táºp tin bị thay đổi" +msgstr "chỉ hiển thị tên táºp tin và trạng thái cá»§a các táºp tin thay đổi" msgid "<width>[,<name-width>[,<count>]]" -msgstr "<rá»™ng>[,<name-width>[,<số-lượng>]]" +msgstr "<độ-rá»™ng>[,<độ-rá»™ng-tên>[,<số-lượng>]]" msgid "generate diffstat" msgstr "tạo diffstat" @@ -16589,6 +16726,52 @@ msgid "bad git namespace path \"%s\"" msgstr "đưá»ng dẫn không gian tên git \"%s\" sai" #, c-format +msgid "invalid value for variable %s" +msgstr "giá trị không hợp lệ cho biến %s" + +#, c-format +msgid "ignoring unknown core.fsync component '%s'" +msgstr "bá» qua thà nh phần core.fsync chưa biết '%s'" + +#, c-format +msgid "abbrev length out of range: %d" +msgstr "chiá»u dà i abbrev nằm ngoà i phạm vi: %d" + +#, c-format +msgid "bad zlib compression level %d" +msgstr "mức nén zlib %d là sai" + +#, c-format +msgid "%s cannot contain newline" +msgstr "%s không thể chứa ký tá»± xuống dòng" + +#, c-format +msgid "%s must have at least one character" +msgstr "%s phải có Ãt nhất má»™t ký tá»±" + +#, c-format +msgid "ignoring unknown core.fsyncMethod value '%s'" +msgstr "bá» qua giá trị core.fsyncMethod chưa biết '%s'" + +msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" +msgstr "core.fsyncObjectFiles đã lạc háºu; hãy dùng core.fsync để thay thế" + +#, c-format +msgid "invalid mode for object creation: %s" +msgstr "chế độ không hợp lệ đối vá»›i việc tạo đối tượng: %s" + +#, c-format +msgid "malformed value for %s" +msgstr "giá trị cho %s sai dạng" + +#, c-format +msgid "malformed value for %s: %s" +msgstr "giá trị cho %s sai dạng: %s" + +msgid "must be one of nothing, matching, simple, upstream or current" +msgstr "phải là má»™t trong số nothing, matching, simple, upstream hay current" + +#, c-format msgid "too many args to run %s" msgstr "quá nhiá»u tham số để chạy %s" @@ -17207,6 +17390,10 @@ msgid "Unknown value for http.proactiveauth" msgstr "không hiểu giá trị cho http.proactiveauth" #, c-format +msgid "failed to parse %s" +msgstr "gặp lá»—i khi Ä‘á»c cú pháp %s" + +#, c-format msgid "Unsupported SSL backend '%s'. Supported SSL backends:" msgstr "" "Không há»— trợ ứng dụng SSL chạy phÃa sau '%s'. Há»— trợ ứng dụng SSL chạy phÃa " @@ -17291,6 +17478,30 @@ msgstr "không cho phép tên định danh là rá»—ng (cho <%s>)" msgid "name consists only of disallowed characters: %s" msgstr "tên chỉ được phép bao gồm các ký tá»± sau: %s" +msgid "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>" +msgstr "" +"git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>" + +msgid "no IMAP host specified" +msgstr "chưa chỉ ra máy chá»§ IMAP nà o" + +msgid "" +"set the IMAP host with 'git config imap.host <host>'.\n" +"(e.g., 'git config imap.host imaps://imap.example.com')" +msgstr "" +"chỉ định máy chá»§ IMAP vá»›i 'git config imap.host <host>'.\n" +"(v.d., 'git config imap.host imaps://imap.example.com')" + +msgid "no IMAP folder specified" +msgstr "chưa chỉ ra thư mục IMAP nà o" + +msgid "" +"set the target folder with 'git config imap.folder <folder>'.\n" +"(e.g., 'git config imap.folder Drafts')" +msgstr "" +"chỉ định thư mục vá»›i 'git config imap.folder <folder>'.\n" +"(v.d., 'git config imap.folder Drafts')" + msgid "expected 'tree:<depth>'" msgstr "cần 'tree:<depth>'" @@ -17396,6 +17607,10 @@ msgid "invalid marker-size '%s', expecting an integer" msgstr "marker-size không hợp lệ '%s', cần số nguyên" #, c-format +msgid "Could not parse object '%s'" +msgstr "Không thể Ä‘á»c đối tượng '%s'" + +#, c-format msgid "Failed to merge submodule %s (not checked out)" msgstr "Gặp lá»—i khi hòa trá»™n mô-Ä‘un-con %s (không checkout)" @@ -17519,7 +17734,8 @@ msgstr "" #, c-format msgid "CONFLICT (rename/rename): %s renamed to %s in %s and to %s in %s." msgstr "" -"XUNG ÄỘT (đổi-tên/đổi-tên): Äổi tên %s->%s trong %s và thà nh %s trong %s." +"XUNG ÄỘT (đổi tên/đổi tên): Äổi tên %s thà nh %s trong %s và thà nh %s trong " +"%s." #, c-format msgid "" @@ -17527,14 +17743,14 @@ msgid "" "conflicts AND collides with another path; this may result in nested conflict " "markers." msgstr "" -"XUNG ÄỘT (đổi tên liên quan đến va chạm): đổi tên %s -> %s xung đột ná»™i dung " -"VÀ va chạm vá»›i má»™t đưá»ng dẫn khác; Ä‘iá»u nà y có thể dẫn đến tạo ra các xung " -"đột lồng nhau." +"XUNG ÄỘT (đổi tên cùng va chạm): đổi tên %s -> %s xung đột ná»™i dung VÀ xung " +"đột vá»›i má»™t đưá»ng dẫn khác; Ä‘iá»u nà y có thể tạo ra các dấu xung đột lồng " +"nhau." #, c-format msgid "CONFLICT (rename/delete): %s renamed to %s in %s, but deleted in %s." msgstr "" -"XUNG ÄỘT (đổi-tên/xóa): Äổi tên %s->%s trong %s, nhưng lại bị xóa trong %s." +"XUNG ÄỘT (đổi tên/xóa): Äổi tên %s->%s trong %s, nhưng lại bị xóa trong %s." #, c-format msgid "error: cannot read object %s" @@ -17634,270 +17850,6 @@ msgstr "" msgid "collecting merge info failed for trees %s, %s, %s" msgstr "thu tháºp thông tin hòa trá»™n gặp lá»—i cho cây %s, %s, %s" -msgid "(bad commit)\n" -msgstr "(commit sai)\n" - -#, c-format -msgid "add_cacheinfo failed for path '%s'; merge aborting." -msgstr "add_cacheinfo gặp lá»—i đối vá»›i đưá»ng dẫn '%s'; huá»· bá» việc hòa trá»™n." - -#, c-format -msgid "add_cacheinfo failed to refresh for path '%s'; merge aborting." -msgstr "" -"add_cacheinfo gặp lá»—i khi là m má»›i đối vá»›i đưá»ng dẫn '%s'; huá»· bá» việc hòa " -"trá»™n." - -#, c-format -msgid "failed to create path '%s'%s" -msgstr "gặp lá»—i khi tạo đưá»ng dẫn '%s'%s" - -#, c-format -msgid "Removing %s to make room for subdirectory\n" -msgstr "Gỡ bá» %s để tạo chá»— (room) cho thư mục con\n" - -msgid ": perhaps a D/F conflict?" -msgstr ": có lẽ là xung đột D/F (táºp tin/thư mục)?" - -#, c-format -msgid "refusing to lose untracked file at '%s'" -msgstr "từ chối đóng táºp tin không được theo dõi tại '%s'" - -#, c-format -msgid "blob expected for %s '%s'" -msgstr "mong đợi đối tượng blob cho %s '%s'" - -#, c-format -msgid "failed to open '%s': %s" -msgstr "gặp lá»—i khi mở '%s': %s" - -#, c-format -msgid "failed to symlink '%s': %s" -msgstr "gặp lá»—i khi tạo liên kết má»m (symlink) '%s': %s" - -#, c-format -msgid "do not know what to do with %06o %s '%s'" -msgstr "không hiểu phải là m gì vá»›i %06o %s '%s'" - -#, c-format -msgid "Failed to merge submodule %s (repository corrupt)" -msgstr "Gặp lá»—i khi hòa trá»™n mô-Ä‘un-con %s (kho chứa há»ng)" - -#, c-format -msgid "Fast-forwarding submodule %s to the following commit:" -msgstr "Chuyển-tiếp-nhanh mô-Ä‘un-con '%s' đến lần chuyển giao sau đây:" - -#, c-format -msgid "Fast-forwarding submodule %s" -msgstr "Chuyển-tiếp-nhanh mô-Ä‘un-con '%s'" - -#, c-format -msgid "Failed to merge submodule %s (merge following commits not found)" -msgstr "" -"Gặp lá»—i khi hòa trá»™n mô-Ä‘un-con '%s' (không tìm thấy các lần chuyển giao " -"theo sau hòa trá»™n)" - -#, c-format -msgid "Failed to merge submodule %s (not fast-forward)" -msgstr "Gặp lá»—i khi hòa trá»™n mô-Ä‘un-con '%s' (không chuyển tiếp nhanh được)" - -msgid "Found a possible merge resolution for the submodule:\n" -msgstr "Tìm thấy má»™t giải pháp hòa trá»™n khả thi cho mô-Ä‘un-con:\n" - -#, c-format -msgid "" -"If this is correct simply add it to the index for example\n" -"by using:\n" -"\n" -" git update-index --cacheinfo 160000 %s \"%s\"\n" -"\n" -"which will accept this suggestion.\n" -msgstr "" -"Nếu đây là đúng đơn giản thêm nó và o chỉ mục và dụ\n" -"bằng cách dùng:\n" -"\n" -" git update-index --cacheinfo 160000 %s \"%s\"\n" -"\n" -"cái mà sẽ chấp nháºn gợi ý nà y.\n" - -#, c-format -msgid "Failed to merge submodule %s (multiple merges found)" -msgstr "Gặp lá»—i khi hòa trá»™n mô-Ä‘un-con '%s' (thấy nhiá»u hòa trá»™n Ä‘a trùng)" - -msgid "failed to execute internal merge" -msgstr "Gặp lá»—i khi thá»±c hiện trá»™n ná»™i bá»™" - -#, c-format -msgid "unable to add %s to database" -msgstr "Không thể thêm %s và o cÆ¡ sở dữ liệu" - -#, c-format -msgid "Error: Refusing to lose untracked file at %s; writing to %s instead." -msgstr "" -"Lá»—i: từ chối đóng táºp tin không được theo dõi tại '%s'; thay và o đó ghi và o " -"%s." - -#, c-format -msgid "" -"CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left " -"in tree." -msgstr "" -"XUNG ÄỘT (%s/xóa): %s bị xóa trong %s và %s trong %s. Phiên bản %s cá»§a %s " -"còn lại trong cây (tree)." - -#, c-format -msgid "" -"CONFLICT (%s/delete): %s deleted in %s and %s to %s in %s. Version %s of %s " -"left in tree." -msgstr "" -"XUNG ÄỘT (%s/xóa): %s bị xóa trong %s và %s đến %s trong %s. Phiên bản %s " -"cá»§a %s còn lại trong cây (tree)." - -#, c-format -msgid "" -"CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left " -"in tree at %s." -msgstr "" -"XUNG ÄỘT (%s/xóa): %s bị xóa trong %s và %s trong %s. Phiên bản %s cá»§a %s " -"còn lại trong cây (tree) tại %s." - -#, c-format -msgid "" -"CONFLICT (%s/delete): %s deleted in %s and %s to %s in %s. Version %s of %s " -"left in tree at %s." -msgstr "" -"XUNG ÄỘT (%s/xóa): %s bị xóa trong %s và %s đến %s trong %s. Phiên bản %s " -"cá»§a %s còn lại trong cây (tree) tại %s." - -msgid "rename" -msgstr "đổi tên" - -msgid "renamed" -msgstr "đã đổi tên" - -#, c-format -msgid "Refusing to lose dirty file at %s" -msgstr "Từ chối đóng táºp tin không được theo dõi tại '%s'" - -#, c-format -msgid "Refusing to lose untracked file at %s, even though it's in the way." -msgstr "" -"Từ chối đóng táºp tin không được theo dõi tại '%s', ngay cả khi nó ở trên " -"đưá»ng." - -#, c-format -msgid "CONFLICT (rename/add): Rename %s->%s in %s. Added %s in %s" -msgstr "" -"XUNG ÄỘT (đổi-tên/thêm): Äổi tên %s->%s trong %s. %s được thêm trong %s" - -#, c-format -msgid "%s is a directory in %s adding as %s instead" -msgstr "%s là má»™t thư mục trong %s thay và o đó thêm và o như là %s" - -#, c-format -msgid "Refusing to lose untracked file at %s; adding as %s instead" -msgstr "" -"Từ chối đóng táºp tin không được theo dõi tại '%s'; thay và o đó Ä‘ang thêm " -"thà nh %s" - -#, c-format -msgid "" -"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename " -"\"%s\"->\"%s\" in \"%s\"%s" -msgstr "" -"XUNG ÄỘT (đổi-tên/đổi-tên): Äổi tên \"%s\"->\"%s\" trong nhánh \"%s\" đổi " -"tên \"%s\"->\"%s\" trong \"%s\"%s" - -msgid " (left unresolved)" -msgstr " (cần giải quyết)" - -#, c-format -msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s" -msgstr "" -"XUNG ÄỘT (đổi-tên/đổi-tên): Äổi tên %s->%s trong %s. Äổi tên %s->%s trong %s" - -#, c-format -msgid "" -"CONFLICT (directory rename split): Unclear where to place %s because " -"directory %s was renamed to multiple other directories, with no destination " -"getting a majority of the files." -msgstr "" -"XUNG ÄỘT: (phân hoá đổi tên thư mục): Không rõ đặt %s ở đâu bởi vì thư mục " -"%s đã bị đổi tên thà nh nhiá»u thư mục khác, mà không bên nà o nháºn phần lá»›n " -"các táºp tin gốc." - -#, c-format -msgid "" -"CONFLICT (rename/rename): Rename directory %s->%s in %s. Rename directory %s-" -">%s in %s" -msgstr "" -"XUNG ÄỘT (đổi-tên/đổi-tên): Äổi tên thư mục %s->%s trong %s. Äổi tên thư mục " -"%s->%s trong %s" - -#, c-format -msgid "cannot read object %s" -msgstr "không thể Ä‘á»c đối tượng %s" - -#, c-format -msgid "object %s is not a blob" -msgstr "đối tượng %s không phải là má»™t blob" - -msgid "modify" -msgstr "sá»a đổi" - -msgid "modified" -msgstr "đã sá»a" - -#, c-format -msgid "Skipped %s (merged same as existing)" -msgstr "Äã bá» qua %s (đã có sẵn lần hòa trá»™n nà y)" - -#, c-format -msgid "Adding as %s instead" -msgstr "Thay và o đó thêm và o %s" - -#, c-format -msgid "Removing %s" -msgstr "Äang xóa %s" - -msgid "file/directory" -msgstr "táºp-tin/thư-mục" - -msgid "directory/file" -msgstr "thư-mục/táºp-tin" - -#, c-format -msgid "CONFLICT (%s): There is a directory with name %s in %s. Adding %s as %s" -msgstr "" -"XUNG ÄỘT (%s): Ở đây không có thư mục nà o có tên %s trong %s. Thêm %s như là " -"%s" - -#, c-format -msgid "Adding %s" -msgstr "Thêm \"%s\"" - -#, c-format -msgid "CONFLICT (add/add): Merge conflict in %s" -msgstr "XUNG ÄỘT (thêm/thêm): Xung đột hòa trá»™n trong %s" - -#, c-format -msgid "merging of trees %s and %s failed" -msgstr "hòa trá»™n các cây %s và %s gặp lá»—i" - -msgid "Merging:" -msgstr "Äang trá»™n:" - -#, c-format -msgid "found %u common ancestor:" -msgid_plural "found %u common ancestors:" -msgstr[0] "tìm thấy %u tổ tiên chung:" - -msgid "merge returned no commit" -msgstr "hòa trá»™n không trả vá» lần chuyển giao nà o" - -#, c-format -msgid "Could not parse object '%s'" -msgstr "Không thể Ä‘á»c đối tượng '%s'" - msgid "failed to read the cache" msgstr "gặp lá»—i khi Ä‘á»c bá»™ nhá»› đệm" @@ -17939,12 +17891,13 @@ msgstr "không thể liên kết '%s' và o '%s'" msgid "failed to clear multi-pack-index at %s" msgstr "gặp lá»—i khi xóa multi-pack-index tại %s" -msgid "cannot write incremental MIDX with bitmap" -msgstr "không thể ghi incremental MIDX vá»›i bitmap" - msgid "ignoring existing multi-pack-index; checksum mismatch" msgstr "bá» qua multi-pack-index sẵn có; tổng kiểm không khá»›p" +#, c-format +msgid "could not load reverse index for MIDX %s" +msgstr "không thể mở chỉ mục ngược cho MIDX %s" + msgid "Adding packfiles to multi-pack-index" msgstr "Äang thêm táºp tin gói từ multi-pack-index" @@ -18104,7 +18057,7 @@ msgid "the midx contains no oid" msgstr "midx chẳng chứa oid nà o" msgid "Verifying OID order in multi-pack-index" -msgstr "Thẩm tra thứ tá»± OID trong multi-pack-index" +msgstr "xác minh thứ tá»± OID trong multi-pack-index" #, c-format msgid "oid lookup out of order: oid[%d] = %s >= %s = oid[%d]" @@ -18114,7 +18067,7 @@ msgid "Sorting objects by packfile" msgstr "Äang sắp xếp các đối tượng theo táºp tin gói" msgid "Verifying object offsets" -msgstr "Äang thẩm tra các khoảng bù đối tượng" +msgstr "Äang xác minh các khoảng bù đối tượng" #, c-format msgid "failed to load pack entry for oid[%d] = %s" @@ -18196,64 +18149,6 @@ msgid "Failed to convert object from %s to %s" msgstr "Chuyển đổi đối tượng từ %s sang %s thất bại" #, c-format -msgid "object directory %s does not exist; check .git/objects/info/alternates" -msgstr "" -"thư mục đối tượng %s không tồn tại; kiểm tra .git/objects/info/alternates" - -#, c-format -msgid "unable to normalize alternate object path: %s" -msgstr "không thể thưá»ng hóa đưá»ng dẫn đối tượng thay thế: '%s'" - -#, c-format -msgid "%s: ignoring alternate object stores, nesting too deep" -msgstr "%s: Ä‘ang bá» qua kho đối tượng thay thế, lồng nhau quá sâu" - -msgid "unable to fdopen alternates lockfile" -msgstr "không thể fdopen táºp tin khóa thay thế" - -msgid "unable to read alternates file" -msgstr "không thể Ä‘á»c táºp tin thay thế" - -msgid "unable to move new alternates file into place" -msgstr "không thể di chuyển táºp tin thay thế và o chá»—" - -#, c-format -msgid "path '%s' does not exist" -msgstr "đưá»ng dẫn '%s' không tồn tại" - -#, c-format -msgid "reference repository '%s' as a linked checkout is not supported yet." -msgstr "kho tham chiếu '%s' như là checkout liên kết vẫn chưa được há»— trợ." - -#, c-format -msgid "reference repository '%s' is not a local repository." -msgstr "kho tham chiếu '%s' không phải là má»™t kho ná»™i bá»™." - -#, c-format -msgid "reference repository '%s' is shallow" -msgstr "kho tham chiếu '%s' là nông" - -#, c-format -msgid "reference repository '%s' is grafted" -msgstr "kho tham chiếu '%s' bị cấy ghép" - -#, c-format -msgid "could not find object directory matching %s" -msgstr "không thể tìm thấy thư mục đối tượng khá»›p vá»›i '%s'" - -#, c-format -msgid "invalid line while parsing alternate refs: %s" -msgstr "dòng không hợp lệ trong khi Ä‘á»c các tham chiếu thay thế: %s" - -#, c-format -msgid "attempting to mmap %<PRIuMAX> over limit %<PRIuMAX>" -msgstr "Ä‘ang cố để mmap %<PRIuMAX> vượt quá giá»›i hạn %<PRIuMAX>" - -#, c-format -msgid "mmap failed%s" -msgstr "mmap gặp lá»—i%s" - -#, c-format msgid "object file %s is empty" msgstr "táºp tin đối tượng %s trống rá»—ng" @@ -18289,18 +18184,6 @@ msgid "loose object %s (stored in %s) is corrupt" msgstr "đối tượng mất %s (được lưu trong %s) bị há»ng" #, c-format -msgid "replacement %s not found for %s" -msgstr "c%s thay thế không được tìm thấy cho %s" - -#, c-format -msgid "packed object %s (stored in %s) is corrupt" -msgstr "đối tượng đã đóng gói %s (được lưu trong %s) bị há»ng" - -#, c-format -msgid "missing mapping of %s to %s" -msgstr "thiếu ánh xạ %s sang %s" - -#, c-format msgid "unable to open %s" msgstr "không thể mở %s" @@ -18394,10 +18277,6 @@ msgid "%s: unsupported file type" msgstr "%s: kiểu táºp tin không được há»— trợ" #, c-format -msgid "%s is not a valid '%s' object" -msgstr "%s không phải là má»™t đối tượng '%s' hợp lệ" - -#, c-format msgid "hash mismatch for %s (expected %s)" msgstr "mã băm không khá»›p cho %s (cần %s)" @@ -18407,15 +18286,19 @@ msgstr "không thể mmap %s" #, c-format msgid "unable to unpack header of %s" -msgstr "không thể giải gói phần đầu cá»§a '%s'" +msgstr "không thể giải gói phần đầu cá»§a %s" #, c-format msgid "unable to parse header of %s" -msgstr "không thể Ä‘á»c phần đầu cá»§a '%s'" +msgstr "không thể Ä‘á»c phần đầu cá»§a %s" + +#, c-format +msgid "unable to parse type from header '%s' of %s" +msgstr "không thể Ä‘á»c phần đầu '%s' cá»§a %s" #, c-format msgid "unable to unpack contents of %s" -msgstr "không thể giải gói ná»™i dung cá»§a '%s'" +msgstr "không thể giải gói ná»™i dung cá»§a %s" #. TRANSLATORS: This is a line of ambiguous object #. output shown when we cannot look up or parse the @@ -18423,7 +18306,7 @@ msgstr "không thể giải gói ná»™i dung cá»§a '%s'" #. #, c-format msgid "%s [bad object]" -msgstr "%s [đối tượng sai.]" +msgstr "%s [đối tượng sai]" #. TRANSLATORS: This is a line of ambiguous commit #. object output. E.g.: @@ -18457,7 +18340,7 @@ msgstr "%s thẻ %s - %s" #. #, c-format msgid "%s [bad tag, could not parse it]" -msgstr "%s [thẻ sai, không hiểu cú pháp nó]" +msgstr "%s [thẻ sai, không hiểu cú pháp]" #. TRANSLATORS: This is a line of ambiguous <type> #. object output. E.g. "deadbeef tree". @@ -18591,6 +18474,72 @@ msgid "hash mismatch %s" msgstr "mã băm không khá»›p %s" #, c-format +msgid "object directory %s does not exist; check .git/objects/info/alternates" +msgstr "" +"thư mục đối tượng %s không tồn tại; kiểm tra .git/objects/info/alternates" + +#, c-format +msgid "unable to normalize alternate object path: %s" +msgstr "không thể thưá»ng hóa đưá»ng dẫn đối tượng thay thế: '%s'" + +#, c-format +msgid "%s: ignoring alternate object stores, nesting too deep" +msgstr "%s: Ä‘ang bá» qua kho đối tượng thay thế, lồng nhau quá sâu" + +msgid "unable to fdopen alternates lockfile" +msgstr "không thể fdopen táºp tin khóa thay thế" + +msgid "unable to read alternates file" +msgstr "không thể Ä‘á»c táºp tin thay thế" + +msgid "unable to move new alternates file into place" +msgstr "không thể di chuyển táºp tin thay thế và o chá»—" + +#, c-format +msgid "path '%s' does not exist" +msgstr "đưá»ng dẫn '%s' không tồn tại" + +#, c-format +msgid "reference repository '%s' as a linked checkout is not supported yet." +msgstr "kho tham chiếu '%s' như là checkout liên kết vẫn chưa được há»— trợ." + +#, c-format +msgid "reference repository '%s' is not a local repository." +msgstr "kho tham chiếu '%s' không phải là má»™t kho ná»™i bá»™." + +#, c-format +msgid "reference repository '%s' is shallow" +msgstr "kho tham chiếu '%s' là nông" + +#, c-format +msgid "reference repository '%s' is grafted" +msgstr "kho tham chiếu '%s' bị cấy ghép" + +#, c-format +msgid "could not find object directory matching %s" +msgstr "không thể tìm thấy thư mục đối tượng khá»›p vá»›i '%s'" + +#, c-format +msgid "invalid line while parsing alternate refs: %s" +msgstr "dòng không hợp lệ trong khi Ä‘á»c các tham chiếu thay thế: %s" + +#, c-format +msgid "replacement %s not found for %s" +msgstr "c%s thay thế không được tìm thấy cho %s" + +#, c-format +msgid "packed object %s (stored in %s) is corrupt" +msgstr "đối tượng đã đóng gói %s (được lưu trong %s) bị há»ng" + +#, c-format +msgid "missing mapping of %s to %s" +msgstr "thiếu ánh xạ %s sang %s" + +#, c-format +msgid "%s is not a valid '%s' object" +msgstr "%s không phải là má»™t đối tượng '%s' hợp lệ" + +#, c-format msgid "duplicate entry when writing bitmap index: %s" msgstr "đối tượng trùng lặp khi ghi chỉ mục bitmap: %s" @@ -18601,9 +18550,6 @@ msgstr "đã cố ghi lần chuyển giao không được chá»n: '%s'" msgid "too many pseudo-merges" msgstr "có quá nhiá»u lần pseudo-merge" -msgid "trying to write commit not in index" -msgstr "đã thá» ghi lần chuyển giao nằm ngoà i chỉ mục" - msgid "failed to load bitmap index (corrupted?)" msgstr "không thể Ä‘á»c chỉ mục bitmap (bị há»ng?)" @@ -18845,8 +18791,20 @@ msgid "%s isn't available" msgstr "%s không sẵn có" #, c-format +msgid "value for %s exceeds %<PRIdMAX>" +msgstr "giá trị %s vượt quá %<PRIdMAX>" + +#, c-format +msgid "value %s for %s not in range [%<PRIdMAX>,%<PRIdMAX>]" +msgstr "giá trị %s cho %s không nằm trong khoảng [%<PRIdMAX>, %<PRIdMAX>]" + +#, c-format +msgid "%s expects an integer value with an optional k/m/g suffix" +msgstr "%s cần má»™t giá trị dạng số nguyên vá»›i háºu tố tùy chá»n k/m/g" + +#, c-format msgid "%s expects a non-negative integer value with an optional k/m/g suffix" -msgstr "%s cần má»™t giá trị dạng số không âm vá»›i má»™t háºu tố tùy chá»n k/m/g" +msgstr "%s cần má»™t giá trị dạng số không âm vá»›i háºu tố tùy chá»n k/m/g" #, c-format msgid "ambiguous option: %s (could be --%s%s or --%s%s)" @@ -19004,10 +18962,6 @@ msgid "bad boolean environment value '%s' for '%s'" msgstr "sai giá trị kiểu boolean cá»§a cấu hình '%s' cho '%s'" #, c-format -msgid "failed to parse %s" -msgstr "gặp lá»—i khi Ä‘á»c cú pháp %s" - -#, c-format msgid "failed to walk children of tree %s: not found" msgstr "gặp lá»—i khi duyệt nhánh cá»§a cây %s: không tìm thấy" @@ -19167,12 +19121,16 @@ msgid "could not fetch %s from promisor remote" msgstr "không thể tải %s từ máy chá»§ promisor" #, c-format -msgid "known remote named '%s' but with url '%s' instead of '%s'" -msgstr "có máy chá»§ '%s' nhưng vá»›i url '%s' thay vì '%s'" +msgid "no or empty URL advertised for remote '%s'" +msgstr "URL máy chá»§ '%s' rá»—ng hoặc không có" + +#, c-format +msgid "known remote named '%s' but with URL '%s' instead of '%s'" +msgstr "có máy chá»§ '%s' nhưng vá»›i URL '%s' thay vì '%s'" #, c-format msgid "unknown '%s' value for '%s' config option" -msgstr "không hiểu giá trị '%s' cho cho cấu hình '%s'" +msgstr "không hiểu giá trị '%s' cho cấu hình '%s'" #, c-format msgid "unknown element '%s' from remote info" @@ -19805,6 +19763,14 @@ msgid "%s does not point to a valid object!" msgstr "'%s' không chỉ đến má»™t lần chuyển giao hợp lệ nà o cả!" #, c-format +msgid "%s%s will become dangling after %s is deleted\n" +msgstr " %s%s sẽ trở thà nh không đầu sau khi xoá %s!\n" + +#, c-format +msgid "%s%s has become dangling after %s was deleted\n" +msgstr "%s%s đã trở thà nh không đầu sau khi xoá %s!\n" + +#, c-format msgid "" "Using '%s' as the name for the initial branch. This default branch name\n" "is subject to change. To configure the initial branch name to use in all\n" @@ -19932,6 +19898,10 @@ msgid "Checking references consistency" msgstr "Äang kiểm tra tÃnh nhất quán các tham chiếu" #, c-format +msgid "unable to open '%s'" +msgstr "không thể mở '%s'" + +#, c-format msgid "refname is dangerous: %s" msgstr "tên tham chiếu không an toà n: %s" @@ -19999,10 +19969,6 @@ msgid "refname %s is a symbolic ref, copying it is not supported" msgstr "tên tham chiếu %s là tham chiếu biểu trưng, không há»— trợ sao chép" #, c-format -msgid "invalid refspec '%s'" -msgstr "refspec không hợp lệ '%s'" - -#, c-format msgid "pattern '%s' has no '*'" msgstr "giá trị '%s' không có '*'" @@ -20540,7 +20506,7 @@ msgstr "không thể thêm enlistment" msgid "could not set recommended config" msgstr "không thể đặt cấu hình đỠnghị" -msgid "could not turn on maintenance" +msgid "could not toggle maintenance" msgstr "không thể báºt chế độ bảo trì" msgid "could not start the FSMonitor daemon" @@ -20586,12 +20552,15 @@ msgstr "tạo kho chứa trong thư mục 'src'" msgid "specify if tags should be fetched during clone" msgstr "có nên lấy vá» thẻ khi nhân bản" +msgid "specify if background maintenance should be enabled" +msgstr "có nên báºt bảo trì ná»n" + msgid "" "scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n" -"\t[--[no-]src] [--[no-]tags] <url> [<enlistment>]" +"\t[--[no-]src] [--[no-]tags] [--[no-]maintenance] <url> [<enlistment>]" msgstr "" "scalar clone [--single-branch] [--branch <nhánh-chÃnh>] [--full-clone]\n" -"\t[--[no-]src] [--[no-]tags] <url> [<enlistment>]" +"\t[--[no-]src] [--[no-]tags] [--[no-]maintenance] <url> [<enlistment>]" #, c-format msgid "cannot deduce worktree name from '%s'" @@ -20629,19 +20598,33 @@ msgstr "scalar diagnose [<enlistment>]" msgid "`scalar list` does not take arguments" msgstr "`scalar list` không nháºn các tham số" -msgid "scalar register [<enlistment>]" -msgstr "scalar register [<enlistment>]" +msgid "scalar register [--[no-]maintenance] [<enlistment>]" +msgstr "scalar register [--[no-]maintenance] [<enlistment>]" msgid "reconfigure all registered enlistments" msgstr "cấu hình má»i enlistments đã đăng ký" -msgid "scalar reconfigure [--all | <enlistment>]" -msgstr "scalar reconfigure [--all | <enlistment>]" +msgid "(enable|disable|keep)" +msgstr "(enable|disable|keep)" + +msgid "signal how to adjust background maintenance" +msgstr "Ä‘iá»u chỉnh kế hoạch bảo trì ná»n" + +msgid "" +"scalar reconfigure [--maintenance=(enable|disable|keep)] [--all | " +"<enlistment>]" +msgstr "" +"scalar reconfigure [--maintenance=(enable|disable|keep)] [--all | " +"<enlistment>]" msgid "--all or <enlistment>, but not both" msgstr "--all hoặc <enlistment>, không thể là cả hai" #, c-format +msgid "unknown mode for --maintenance option: %s" +msgstr "không hiểu chế độ cho tuỳ chá»n --maintenance: %s" + +#, c-format msgid "could not remove stale scalar.repo '%s'" msgstr "không thể xoá scalar.repo đã cÅ© '%s'" @@ -21021,11 +21004,11 @@ msgstr "không thể cáºp nháºt %s" #, c-format msgid "could not parse parent commit %s" -msgstr "không thể Ä‘á»c lần chuyển giao mẹ '%s'" +msgstr "không thể Ä‘á»c lần chuyển giao cha %s" #, c-format msgid "unknown command: %d" -msgstr "không hiểu câu lệnh %d" +msgstr "không hiểu câu lệnh: %d" msgid "This is the 1st commit message:" msgstr "Äây là chú thÃch cho lần chuyển giao thứ nhất:" @@ -21085,7 +21068,7 @@ msgstr "không thể lấy ghi chú lần chuyển giao cho %s" #. "revert" or "pick", the second %s a SHA1. #, c-format msgid "%s: cannot parse parent commit %s" -msgstr "%s: không thể Ä‘á»c lần chuyển giao mẹ cá»§a %s" +msgstr "%s: không thể Ä‘á»c lần chuyển giao cha cá»§a %s" #, c-format msgid "could not revert %s... %s" @@ -22075,6 +22058,9 @@ msgstr "dá»n cây nhá»› tạm trước má»—i chu kỳ" msgid "number of entries in the cache tree to invalidate (default 0)" msgstr "số mục cần huá»· trong câu nhá»› tạm (mặc định 0)" +msgid "the number of objects to write" +msgstr "số đối tượng tối thiểu cần ghi" + msgid "test-tool path-walk <options> -- <revision-options>" msgstr "test-tool path-walk <tuỳ chá»n> -- <tuỳ chá»n cải biên>" @@ -22093,6 +22079,9 @@ msgstr "bao gồm các đối tượng cây" msgid "toggle pruning of uninteresting paths" msgstr "lược bá» những đưá»ng dẫn Ãt ý nghÄ©a" +msgid "toggle aggressive edge walk" +msgstr "duyệt cạnh tÃch cá»±c" + msgid "read a pattern list over stdin" msgstr "Ä‘á»c các mẫu từ stdin" @@ -22741,6 +22730,23 @@ msgid "warning: " msgstr "cảnh báo: " #, c-format +msgid "" +"'%s' is nominated for removal.\n" +"If you still use this command, please add an extra\n" +"option, '--i-still-use-this', on the command line\n" +"and let us know you still use it by sending an e-mail\n" +"to <git@vger.kernel.org>. Thanks.\n" +msgstr "" +"'%s' đã được đỠcỠđể loại bá».\n" +"Nếu bạn vẫn còn sá» dụng lệnh nà y, vui lòng bổ sung\n" +"thêm má»™t tùy chá»n, '--i-still-use-this', trên dòng lệnh\n" +"và cho chúng tôi biết bạn vẫn sá» dụng nó bằng cách gá»i e-mail\n" +"đến <git@vger.kernel.org>. Xin cảm Æ¡n.\n" + +msgid "refusing to run without --i-still-use-this" +msgstr "từ chối chạy lệnh nà y mà không có --i-still-use-this" + +#, c-format msgid "uname() failed with error '%s' (%d)\n" msgstr "uname() gặp lá»—i '%s' (%d)\n" @@ -22860,6 +22866,14 @@ msgstr "không thể lấy thư mục là m việc hiện hà nh" msgid "unable to get random bytes" msgstr "không thể lấy byte ngẫu nhiên" +#, c-format +msgid "attempting to mmap %<PRIuMAX> over limit %<PRIuMAX>" +msgstr "Ä‘ang cố để mmap %<PRIuMAX> vượt quá giá»›i hạn %<PRIuMAX>" + +#, c-format +msgid "mmap failed%s" +msgstr "mmap gặp lá»—i%s" + msgid "Unmerged paths:" msgstr "Những đưá»ng dẫn chưa được hòa trá»™n:" @@ -23606,6 +23620,13 @@ msgstr "" "Không thể khởi tạo SMTP đúng cách. Kiểm tra cấu hình và dùng --smtp-debug." #, perl-format +msgid "Outlook reassigned Message-ID to: %s\n" +msgstr "Outlook đổi Message-ID thà nh: %s\n" + +msgid "Warning: Could not retrieve Message-ID from server response.\n" +msgstr "Cảnh báo: Không thể lấy Message-ID từ máy chá»§.\n" + +#, perl-format msgid "Failed to send %s\n" msgstr "Gặp lá»—i khi gá»i %s\n" @@ -23650,6 +23671,10 @@ msgid "(body) Adding cc: %s from line '%s'\n" msgstr "(body) Thêm cc: %s từ dòng '%s'\n" #, perl-format +msgid "error: invalid SMTP port '%s'\n" +msgstr "lá»—i: SMTP port không hợp lệ '%s'\n" + +#, perl-format msgid "(%s) Could not execute '%s'" msgstr "(%s) Không thể thá»±c thi '%s'" @@ -23702,6 +23727,332 @@ msgstr "Bá» qua %s vá»›i háºu tố sao lưu '%s'.\n" msgid "Do you really want to send %s? [y|N]: " msgstr "Bạn có thá»±c sá»± muốn gá»i %s? [y|N](có/KHÔNG): " +#~ msgid "start-after" +#~ msgstr "start-after" + +#~ msgid "compact-summary" +#~ msgstr "tổng hợp tóm lược" + +#~ msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>" +#~ msgstr "git cat-file (-t | -s) [--allow-unknown-type] <đối_tượng>" + +#~ msgid "allow -s and -t to work with broken/corrupt objects" +#~ msgstr "cho phép -s và -t để là m việc vá»›i các đối tượng sai/há»ng" + +#, c-format +#~ msgid " (%s will become dangling)" +#~ msgstr " (%s sẽ trở thà nh không đầu (không được quản lý))" + +#, c-format +#~ msgid " (%s has become dangling)" +#~ msgstr " (%s đã trở thà nh không đầu (không được quản lý))" + +#, c-format +#~ msgid "%s: object is of unknown type '%s': %s" +#~ msgstr "%s: đối tượng có kiểu chưa biết '%s': %s" + +#~ msgid "use at most one of --auto and --schedule=<frequency>" +#~ msgstr "dùng nhiá»u nhất là má»™t trong --auto và --schedule=<frequency>" + +#, c-format +#~ msgid "Final output: %d %s\n" +#~ msgstr "Äầu ra cuối cùng: %d %s\n" + +#, c-format +#~ msgid "%d (FSCK_IGNORE?) should never trigger this callback" +#~ msgstr "%d (FSCK_IGNORE?) không bao giá» nên kÃch hoạt callback nà y" + +#~ msgid "" +#~ "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]" +#~ msgstr "" +#~ "git pack-objects --stdout [<các tùy chá»n>] [< <danh-sách-tham-chiếu> | < " +#~ "<danh-sách-đối-tượng>]" + +#~ msgid "" +#~ "git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]" +#~ msgstr "" +#~ "git pack-objects [<các tùy chá»n>] <base-name> [< <danh-sách-ref> | < " +#~ "<danh-sách-đối-tượng>]" + +#~ msgid "cannot use --stdin-packs with --cruft" +#~ msgstr "không thể dùng tùy chá»n --stdin-packs vá»›i --cruft" + +#, c-format +#~ msgid "%s points nowhere!" +#~ msgstr "%s chẳng chỉ đến đâu cả!" + +#, c-format +#~ msgid "unreachable: invalid reference: %s" +#~ msgstr "không tham chiếu được: tham chiếu không hợp lệ: %s" + +#~ msgid "(bad commit)\n" +#~ msgstr "(commit sai)\n" + +#, c-format +#~ msgid "add_cacheinfo failed for path '%s'; merge aborting." +#~ msgstr "add_cacheinfo gặp lá»—i đối vá»›i đưá»ng dẫn '%s'; huá»· bá» việc hòa trá»™n." + +#, c-format +#~ msgid "add_cacheinfo failed to refresh for path '%s'; merge aborting." +#~ msgstr "" +#~ "add_cacheinfo gặp lá»—i khi là m má»›i đối vá»›i đưá»ng dẫn '%s'; huá»· bá» việc hòa " +#~ "trá»™n." + +#, c-format +#~ msgid "failed to create path '%s'%s" +#~ msgstr "gặp lá»—i khi tạo đưá»ng dẫn '%s'%s" + +#, c-format +#~ msgid "Removing %s to make room for subdirectory\n" +#~ msgstr "Gỡ bá» %s để tạo chá»— (room) cho thư mục con\n" + +#~ msgid ": perhaps a D/F conflict?" +#~ msgstr ": có lẽ là xung đột D/F (táºp tin/thư mục)?" + +#, c-format +#~ msgid "refusing to lose untracked file at '%s'" +#~ msgstr "từ chối đóng táºp tin không được theo dõi tại '%s'" + +#, c-format +#~ msgid "blob expected for %s '%s'" +#~ msgstr "mong đợi đối tượng blob cho %s '%s'" + +#, c-format +#~ msgid "failed to open '%s': %s" +#~ msgstr "gặp lá»—i khi mở '%s': %s" + +#, c-format +#~ msgid "failed to symlink '%s': %s" +#~ msgstr "gặp lá»—i khi tạo liên kết má»m (symlink) '%s': %s" + +#, c-format +#~ msgid "do not know what to do with %06o %s '%s'" +#~ msgstr "không hiểu phải là m gì vá»›i %06o %s '%s'" + +#, c-format +#~ msgid "Failed to merge submodule %s (repository corrupt)" +#~ msgstr "Gặp lá»—i khi hòa trá»™n mô-Ä‘un-con %s (kho chứa há»ng)" + +#, c-format +#~ msgid "Fast-forwarding submodule %s to the following commit:" +#~ msgstr "Chuyển-tiếp-nhanh mô-Ä‘un-con '%s' đến lần chuyển giao sau đây:" + +#, c-format +#~ msgid "Fast-forwarding submodule %s" +#~ msgstr "Chuyển-tiếp-nhanh mô-Ä‘un-con '%s'" + +#, c-format +#~ msgid "Failed to merge submodule %s (merge following commits not found)" +#~ msgstr "" +#~ "Gặp lá»—i khi hòa trá»™n mô-Ä‘un-con '%s' (không tìm thấy các lần chuyển giao " +#~ "theo sau hòa trá»™n)" + +#, c-format +#~ msgid "Failed to merge submodule %s (not fast-forward)" +#~ msgstr "Gặp lá»—i khi hòa trá»™n mô-Ä‘un-con '%s' (không chuyển tiếp nhanh được)" + +#~ msgid "Found a possible merge resolution for the submodule:\n" +#~ msgstr "Tìm thấy má»™t giải pháp hòa trá»™n khả thi cho mô-Ä‘un-con:\n" + +#, c-format +#~ msgid "" +#~ "If this is correct simply add it to the index for example\n" +#~ "by using:\n" +#~ "\n" +#~ " git update-index --cacheinfo 160000 %s \"%s\"\n" +#~ "\n" +#~ "which will accept this suggestion.\n" +#~ msgstr "" +#~ "Nếu đây là đúng đơn giản thêm nó và o chỉ mục và dụ\n" +#~ "bằng cách dùng:\n" +#~ "\n" +#~ " git update-index --cacheinfo 160000 %s \"%s\"\n" +#~ "\n" +#~ "cái mà sẽ chấp nháºn gợi ý nà y.\n" + +#, c-format +#~ msgid "Failed to merge submodule %s (multiple merges found)" +#~ msgstr "Gặp lá»—i khi hòa trá»™n mô-Ä‘un-con '%s' (thấy nhiá»u hòa trá»™n Ä‘a trùng)" + +#~ msgid "failed to execute internal merge" +#~ msgstr "Gặp lá»—i khi thá»±c hiện trá»™n ná»™i bá»™" + +#, c-format +#~ msgid "unable to add %s to database" +#~ msgstr "Không thể thêm %s và o cÆ¡ sở dữ liệu" + +#, c-format +#~ msgid "Error: Refusing to lose untracked file at %s; writing to %s instead." +#~ msgstr "" +#~ "Lá»—i: từ chối đóng táºp tin không được theo dõi tại '%s'; thay và o đó ghi " +#~ "và o %s." + +#, c-format +#~ msgid "" +#~ "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s " +#~ "left in tree." +#~ msgstr "" +#~ "XUNG ÄỘT (%s/xóa): %s bị xóa trong %s và %s trong %s. Phiên bản %s cá»§a %s " +#~ "còn lại trong cây (tree)." + +#, c-format +#~ msgid "" +#~ "CONFLICT (%s/delete): %s deleted in %s and %s to %s in %s. Version %s of " +#~ "%s left in tree." +#~ msgstr "" +#~ "XUNG ÄỘT (%s/xóa): %s bị xóa trong %s và %s đến %s trong %s. Phiên bản %s " +#~ "cá»§a %s còn lại trong cây (tree)." + +#, c-format +#~ msgid "" +#~ "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s " +#~ "left in tree at %s." +#~ msgstr "" +#~ "XUNG ÄỘT (%s/xóa): %s bị xóa trong %s và %s trong %s. Phiên bản %s cá»§a %s " +#~ "còn lại trong cây (tree) tại %s." + +#, c-format +#~ msgid "" +#~ "CONFLICT (%s/delete): %s deleted in %s and %s to %s in %s. Version %s of " +#~ "%s left in tree at %s." +#~ msgstr "" +#~ "XUNG ÄỘT (%s/xóa): %s bị xóa trong %s và %s đến %s trong %s. Phiên bản %s " +#~ "cá»§a %s còn lại trong cây (tree) tại %s." + +#~ msgid "rename" +#~ msgstr "đổi tên" + +#~ msgid "renamed" +#~ msgstr "đã đổi tên" + +#, c-format +#~ msgid "Refusing to lose dirty file at %s" +#~ msgstr "Từ chối đóng táºp tin không được theo dõi tại '%s'" + +#, c-format +#~ msgid "Refusing to lose untracked file at %s, even though it's in the way." +#~ msgstr "" +#~ "Từ chối đóng táºp tin không được theo dõi tại '%s', ngay cả khi nó ở trên " +#~ "đưá»ng." + +#, c-format +#~ msgid "CONFLICT (rename/add): Rename %s->%s in %s. Added %s in %s" +#~ msgstr "" +#~ "XUNG ÄỘT (đổi-tên/thêm): Äổi tên %s->%s trong %s. %s được thêm trong %s" + +#, c-format +#~ msgid "%s is a directory in %s adding as %s instead" +#~ msgstr "%s là má»™t thư mục trong %s thay và o đó thêm và o như là %s" + +#, c-format +#~ msgid "Refusing to lose untracked file at %s; adding as %s instead" +#~ msgstr "" +#~ "Từ chối đóng táºp tin không được theo dõi tại '%s'; thay và o đó Ä‘ang thêm " +#~ "thà nh %s" + +#, c-format +#~ msgid "" +#~ "CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename " +#~ "\"%s\"->\"%s\" in \"%s\"%s" +#~ msgstr "" +#~ "XUNG ÄỘT (đổi-tên/đổi-tên): Äổi tên \"%s\"->\"%s\" trong nhánh \"%s\" đổi " +#~ "tên \"%s\"->\"%s\" trong \"%s\"%s" + +#~ msgid " (left unresolved)" +#~ msgstr " (cần giải quyết)" + +#, c-format +#~ msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s" +#~ msgstr "" +#~ "XUNG ÄỘT (đổi-tên/đổi-tên): Äổi tên %s->%s trong %s. Äổi tên %s->%s trong " +#~ "%s" + +#, c-format +#~ msgid "" +#~ "CONFLICT (directory rename split): Unclear where to place %s because " +#~ "directory %s was renamed to multiple other directories, with no " +#~ "destination getting a majority of the files." +#~ msgstr "" +#~ "XUNG ÄỘT: (phân hoá đổi tên thư mục): Không rõ đặt %s ở đâu bởi vì thư " +#~ "mục %s đã bị đổi tên thà nh nhiá»u thư mục khác, mà không bên nà o nháºn phần " +#~ "lá»›n các táºp tin gốc." + +#, c-format +#~ msgid "" +#~ "CONFLICT (rename/rename): Rename directory %s->%s in %s. Rename directory " +#~ "%s->%s in %s" +#~ msgstr "" +#~ "XUNG ÄỘT (đổi-tên/đổi-tên): Äổi tên thư mục %s->%s trong %s. Äổi tên thư " +#~ "mục %s->%s trong %s" + +#, c-format +#~ msgid "cannot read object %s" +#~ msgstr "không thể Ä‘á»c đối tượng %s" + +#, c-format +#~ msgid "object %s is not a blob" +#~ msgstr "đối tượng %s không phải là má»™t blob" + +#~ msgid "modify" +#~ msgstr "sá»a đổi" + +#~ msgid "modified" +#~ msgstr "đã sá»a" + +#, c-format +#~ msgid "Skipped %s (merged same as existing)" +#~ msgstr "Äã bá» qua %s (đã có sẵn lần hòa trá»™n nà y)" + +#, c-format +#~ msgid "Adding as %s instead" +#~ msgstr "Thay và o đó thêm và o %s" + +#, c-format +#~ msgid "Removing %s" +#~ msgstr "Äang xóa %s" + +#~ msgid "file/directory" +#~ msgstr "táºp-tin/thư-mục" + +#~ msgid "directory/file" +#~ msgstr "thư-mục/táºp-tin" + +#, c-format +#~ msgid "" +#~ "CONFLICT (%s): There is a directory with name %s in %s. Adding %s as %s" +#~ msgstr "" +#~ "XUNG ÄỘT (%s): Ở đây không có thư mục nà o có tên %s trong %s. Thêm %s như " +#~ "là %s" + +#, c-format +#~ msgid "Adding %s" +#~ msgstr "Thêm \"%s\"" + +#, c-format +#~ msgid "CONFLICT (add/add): Merge conflict in %s" +#~ msgstr "XUNG ÄỘT (thêm/thêm): Xung đột hòa trá»™n trong %s" + +#, c-format +#~ msgid "merging of trees %s and %s failed" +#~ msgstr "hòa trá»™n các cây %s và %s gặp lá»—i" + +#~ msgid "Merging:" +#~ msgstr "Äang trá»™n:" + +#, c-format +#~ msgid "found %u common ancestor:" +#~ msgid_plural "found %u common ancestors:" +#~ msgstr[0] "tìm thấy %u tổ tiên chung:" + +#~ msgid "merge returned no commit" +#~ msgstr "hòa trá»™n không trả vá» lần chuyển giao nà o" + +#~ msgid "cannot write incremental MIDX with bitmap" +#~ msgstr "không thể ghi incremental MIDX vá»›i bitmap" + +#~ msgid "trying to write commit not in index" +#~ msgstr "đã thá» ghi lần chuyển giao nằm ngoà i chỉ mục" + #, c-format #~ msgid "Could not find remote branch %s to clone." #~ msgstr "Không tìm thấy nhánh máy chá»§ %s để nhân bản (clone)." @@ -23710,9 +24061,6 @@ msgstr "Bạn có thá»±c sá»± muốn gá»i %s? [y|N](có/KHÔNG): " #~ msgid "merging cannot continue; got unclean result of %d" #~ msgstr "không thể tiếp tục hoà trá»™n; kết quả không hoà n toà n %d" -#~ msgid "git repack [<options>]" -#~ msgstr "git repack [<các tùy chá»n>]" - #~ msgid "--onto and --advance are incompatible" #~ msgstr "--onto và --advance xung khắc" diff --git a/po/zh_CN.po b/po/zh_CN.po index 139ae331dc..a0d43c5333 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -155,8 +155,8 @@ msgid "" msgstr "" "Project-Id-Version: Git\n" "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n" -"POT-Creation-Date: 2025-06-08 20:30+0800\n" -"PO-Revision-Date: 2025-06-12 14:50+0800\n" +"POT-Creation-Date: 2025-08-12 20:20-0400\n" +"PO-Revision-Date: 2025-08-17 08:14-0400\n" "Last-Translator: Teng Long <dyroneteng@gmail.com>\n" "Language-Team: GitHub <https://github.com/dyrone/git/>\n" "Language: zh_CN\n" @@ -168,6 +168,11 @@ msgstr "" #: add-interactive.c #, c-format +msgid "%s cannot be negative" +msgstr "%s ä¸èƒ½ä¸ºè´Ÿæ•°" + +#: add-interactive.c +#, c-format msgid "Huh (%s)?" msgstr "嗯(%s)?" @@ -1074,10 +1079,10 @@ msgstr "未能识别的空白å—符忽略选项 '%s'" #: builtin/describe.c builtin/diff-tree.c builtin/difftool.c #: builtin/fast-export.c builtin/fetch.c builtin/help.c builtin/index-pack.c #: builtin/init-db.c builtin/log.c builtin/ls-files.c builtin/merge-base.c -#: builtin/merge-tree.c builtin/merge.c builtin/pack-objects.c builtin/rebase.c -#: builtin/repack.c builtin/replay.c builtin/reset.c builtin/rev-parse.c -#: builtin/show-branch.c builtin/stash.c builtin/submodule--helper.c -#: builtin/tag.c builtin/worktree.c parse-options.c range-diff.c revision.c +#: builtin/merge-tree.c builtin/merge.c builtin/rebase.c builtin/repack.c +#: builtin/replay.c builtin/reset.c builtin/rev-parse.c builtin/show-branch.c +#: builtin/stash.c builtin/submodule--helper.c builtin/tag.c builtin/worktree.c +#: parse-options.c range-diff.c revision.c #, c-format msgid "options '%s' and '%s' cannot be used together" msgstr "选项 '%s' å’Œ '%s' ä¸èƒ½åŒæ—¶ä½¿ç”¨" @@ -2459,6 +2464,12 @@ msgstr "å¦‚æžœæ‚¨ç¡®å®žæƒ³æ·»åŠ å®ƒä»¬ï¼Œè¯·ä½¿ç”¨ -f 选项。" msgid "adding files failed" msgstr "æ·»åŠ æ–‡ä»¶å¤±è´¥" +#: builtin/add.c builtin/checkout.c builtin/commit.c builtin/reset.c +#: builtin/stash.c +#, c-format +msgid "'%s' cannot be negative" +msgstr "'%s' ä¸èƒ½ä¸ºè´Ÿæ•°" + #: builtin/add.c #, c-format msgid "--chmod param '%s' must be either -x or +x" @@ -2497,7 +2508,7 @@ msgid "bad action '%s' for '%s'" msgstr "'%2$s' 的错误动作 '%1$s'" #: builtin/am.c builtin/blame.c builtin/fetch.c builtin/pack-objects.c -#: builtin/pull.c builtin/revert.c config.c diff-merges.c gpg-interface.c +#: builtin/pull.c builtin/revert.c diff-merges.c environment.c gpg-interface.c #: ls-refs.c parallel-checkout.c sequencer.c setup.c #, c-format msgid "invalid value for '%s': '%s'" @@ -3819,8 +3830,8 @@ msgstr "HEAD 没有ä½äºŽ /refs/heads 之下ï¼" #: builtin/branch.c msgid "" -"branch with --recurse-submodules can only be used if " -"submodule.propagateBranches is enabled" +"branch with --recurse-submodules can only be used if submodule." +"propagateBranches is enabled" msgstr "" "带有 --recurse-submodules 的分支åªèƒ½åœ¨ submodule.propagateBranches å¯ç”¨æ—¶ä½¿ç”¨" @@ -3963,8 +3974,8 @@ msgstr "" "请检查下é¢é”™è¯¯æŠ¥å‘Šä¸ä½™ä¸‹çš„内容。\n" "您å¯ä»¥åˆ é™¤ä»»ä½•æ‚¨ä¸æƒ³å…±äº«çš„内容。\n" -#: builtin/bugreport.c builtin/commit.c builtin/fast-export.c builtin/rebase.c -#: parse-options.h +#: builtin/bugreport.c builtin/commit.c builtin/fast-export.c +#: builtin/pack-objects.c builtin/rebase.c parse-options.h msgid "mode" msgstr "模å¼" @@ -6373,25 +6384,26 @@ msgstr "git config list [<文件选项>] [<显示选项>] [--includes]" #: builtin/config.c msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp] [--value=<value>] [--fixed-value] [--default=<default>] <name>" +"regexp] [--value=<pattern>] [--fixed-value] [--default=<default>] [--" +"url=<url>] <name>" msgstr "" "git config get [<文件选项>] [<显示选项>] [--includes] [--all] [--regexp] [--" -"value=<值>] [--fixed-value] [--default=<默认值>] <åç§°>" +"value=<模å¼>] [--fixed-value] [--default=<默认值>] [--url=<url>] <åç§°>" #: builtin/config.c msgid "" -"git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--" -"fixed-value] <name> <value>" +"git config set [<file-option>] [--type=<type>] [--all] [--value=<pattern>] " +"[--fixed-value] <name> <value>" msgstr "" -"git config set [<文件选项>] [--type=<类型>] [--all] [--value=<值>] [--fixed-" -"value] <åç§°> <值>" +"git config set [<文件选项>] [--type=<类型>] [--all] [--value=<模å¼>] [--" +"fixed-value] <åç§°> <值>" #: builtin/config.c msgid "" -"git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] " +"git config unset [<file-option>] [--all] [--value=<pattern>] [--fixed-value] " "<name>" msgstr "" -"git config unset [<文件选项>] [--all] [--value=<值>] [--fixed-value] <åç§°>" +"git config unset [<文件选项>] [--all] [--value=<模å¼>] [--fixed-value] <åç§°>" #: builtin/config.c msgid "git config rename-section [<file-option>] <old-name> <new-name>" @@ -6412,19 +6424,19 @@ msgstr "git config [<文件选项>] --get-colorbool <åç§°> [<æ ‡å‡†è¾“å‡ºä¸ºtt #: builtin/config.c msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] " +"regexp=<regexp>] [--value=<pattern>] [--fixed-value] [--default=<default>] " "<name>" msgstr "" "git config get [<文件选项>] [<显示选项>] [--includes] [--all] [--regexp=<æ£åˆ™" -"表达å¼>] [--value=<值>] [--fixed-value] [--default=<默认值>] <åç§°>" +"表达å¼>] [--value=<模å¼>] [--fixed-value] [--default=<默认值>] <åç§°>" #: builtin/config.c msgid "" "git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] " -"[--value=<value>] [--fixed-value] <name> <value>" +"[--value=<pattern>] [--fixed-value] <name> <value>" msgstr "" "git config set [<文件选项>] [--type=<类型>] [--comment=<消æ¯>] [--all] [--" -"value=<值>] [--fixed-value] <åç§°> <值>" +"value=<模å¼>] [--fixed-value] <åç§°> <值>" #: builtin/config.c msgid "Config file location" @@ -7046,7 +7058,7 @@ msgstr "ä¸èƒ½è§£æžæ¨¡å¼ï¼š%s" #: builtin/diff-pairs.c #, c-format msgid "unable to parse object id: %s" -msgstr "ä¸èƒ½è§£æžå¯¹è±¡ID:%s" +msgstr "ä¸èƒ½è§£æžå¯¹è±¡ ID:%s" #: builtin/diff-pairs.c msgid "git diff-pairs -z [<diff-options>]" @@ -7493,27 +7505,6 @@ msgid "rejected %s because shallow roots are not allowed to be updated" msgstr "æ‹’ç» %s å› ä¸ºæµ…å…‹éš†çš„æ ¹ä¸å…许被更新" #: builtin/fetch.c -#, c-format -msgid "" -"some local refs could not be updated; try running\n" -" 'git remote prune %s' to remove any old, conflicting branches" -msgstr "" -"一些本地引用ä¸èƒ½è¢«æ›´æ–°ï¼›å°è¯•è¿è¡Œ\n" -" 'git remote prune %s' æ¥åˆ é™¤æ—§çš„ã€æœ‰å†²çªçš„分支" - -# 译者:注æ„ä¿æŒå‰å¯¼ç©ºæ ¼ -#: builtin/fetch.c -#, c-format -msgid " (%s will become dangling)" -msgstr " (%s å°†æˆä¸ºæ‚¬ç©ºçжæ€ï¼‰" - -# 译者:注æ„ä¿æŒå‰å¯¼ç©ºæ ¼ -#: builtin/fetch.c -#, c-format -msgid " (%s has become dangling)" -msgstr " (%s å·²æˆä¸ºæ‚¬ç©ºçжæ€ï¼‰" - -#: builtin/fetch.c msgid "[deleted]" msgstr "[å·²åˆ é™¤]" @@ -7536,7 +7527,7 @@ msgstr "选项 \"%s\" 的值 \"%s\" 对于 %s æ˜¯æ— æ•ˆçš„" msgid "option \"%s\" is ignored for %s" msgstr "选项 \"%s\" 为 %s 所忽略" -#: builtin/fetch.c object-store.c +#: builtin/fetch.c odb.c #, c-format msgid "%s is not a valid object" msgstr "%s 䏿˜¯ä¸€ä¸ªæœ‰æ•ˆçš„对象" @@ -7561,6 +7552,20 @@ msgstr "" "以ç¦ç”¨è¯¥è¦å‘Šï¼Œç›´åˆ°è¿œç¨‹å°† HEAD 更改为其他内容。" #: builtin/fetch.c +#, c-format +msgid "" +"some local refs could not be updated; try running\n" +" 'git remote prune %s' to remove any old, conflicting branches" +msgstr "" +"一些本地引用ä¸èƒ½è¢«æ›´æ–°ï¼›å°è¯•è¿è¡Œ\n" +" 'git remote prune %s' æ¥åˆ é™¤æ—§çš„ã€æœ‰å†²çªçš„分支" + +#: builtin/fetch.c +#, c-format +msgid "fetching ref %s failed: %s" +msgstr "获å–引用 %s 失败:%s" + +#: builtin/fetch.c msgid "multiple branches detected, incompatible with --set-upstream" msgstr "检测到多分支,和 --set-upstream ä¸å…¼å®¹" @@ -7799,8 +7804,8 @@ msgstr "åè®®ä¸æ”¯æŒ --negotiate-only,退出" #: builtin/fetch.c msgid "" -"--filter can only be used with the remote configured in " -"extensions.partialclone" +"--filter can only be used with the remote configured in extensions." +"partialclone" msgstr "åªå¯ä»¥å°† --filter 用于在 extensions.partialclone ä¸é…置的远程仓库" #: builtin/fetch.c @@ -7857,6 +7862,10 @@ msgid "git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]" msgstr "git for-each-ref [--contains [<æäº¤>]] [--no-contains [<æäº¤>]]" #: builtin/for-each-ref.c +msgid "git for-each-ref [--start-after <marker>]" +msgstr "git for-each-ref [--start-after <æ ‡è®°>]" + +#: builtin/for-each-ref.c msgid "quote placeholders suitably for shells" msgstr "引用å ä½ç¬¦é€‚用于 shells" @@ -7876,6 +7885,14 @@ msgstr "引用å ä½ç¬¦é€‚用于 Tcl" msgid "show only <n> matched refs" msgstr "åªæ˜¾ç¤º <n> 个匹é…的引用" +#: builtin/for-each-ref.c +msgid "marker" +msgstr "æ ‡è®°" + +#: builtin/for-each-ref.c +msgid "start iteration after the provided marker" +msgstr "ä»ŽæŒ‡å®šæ ‡è®°ä¹‹åŽå¼€å§‹è¿ä»£" + #: builtin/for-each-ref.c builtin/tag.c msgid "respect format colors" msgstr "éµç…§æ ¼å¼ä¸çš„颜色输出" @@ -7909,9 +7926,17 @@ msgid "also include HEAD ref and pseudorefs" msgstr "还包括 HEAD 引用和伪引用" #: builtin/for-each-ref.c +msgid "cannot use --start-after with custom sort options" +msgstr "ä¸èƒ½å°† --start-after 与自定义排åºé€‰é¡¹ä¸€åŒä½¿ç”¨" + +#: builtin/for-each-ref.c msgid "unknown arguments supplied with --stdin" msgstr "为 --stdin æä¾›äº†æœªçŸ¥çš„命令傿•°" +#: builtin/for-each-ref.c +msgid "cannot use --start-after with patterns" +msgstr "ä¸èƒ½å°† --start-after 与模å¼åŒ¹é…一åŒä½¿ç”¨" + #: builtin/for-each-repo.c msgid "git for-each-repo --config=<config> [--] <arguments>" msgstr "git for-each-repo --config=<é…ç½®> [--] <命令傿•°>" @@ -8414,6 +8439,10 @@ msgid "pack prefix to store a pack containing pruned objects" msgstr "用于å˜å‚¨ä¿®å‰ªå¯¹è±¡çš„包å‰ç¼€" #: builtin/gc.c +msgid "skip maintenance tasks typically done in the foreground" +msgstr "跳过通常在å‰å°æ‰§è¡Œçš„维护任务" + +#: builtin/gc.c #, c-format msgid "failed to parse gc.logExpiry value %s" msgstr "æ— æ³•è§£æž gc.logExpiry 的值 %s" @@ -8500,13 +8529,13 @@ msgstr "跳过增é‡é‡æ–°æ‰“åŒ…ä»»åŠ¡ï¼Œå› ä¸º core.multiPackIndex 被ç¦ç”¨" #: builtin/gc.c #, c-format -msgid "lock file '%s' exists, skipping maintenance" -msgstr "锿–‡ä»¶ '%s' å·²å˜åœ¨ï¼Œè·³è¿‡ç»´æŠ¤" +msgid "task '%s' failed" +msgstr "任务 '%s' 失败" #: builtin/gc.c #, c-format -msgid "task '%s' failed" -msgstr "任务 '%s' 失败" +msgid "lock file '%s' exists, skipping maintenance" +msgstr "锿–‡ä»¶ '%s' å·²å˜åœ¨ï¼Œè·³è¿‡ç»´æŠ¤" #: builtin/gc.c #, c-format @@ -8547,10 +8576,6 @@ msgid "run a specific task" msgstr "è¿è¡Œä¸€ä¸ªç‰¹å®šçš„任务" #: builtin/gc.c -msgid "use at most one of --auto and --schedule=<frequency>" -msgstr "最多使用 --auto å’Œ --schedule=<频率> å…¶ä¸ä¹‹ä¸€" - -#: builtin/gc.c #, c-format msgid "unable to add '%s' value of '%s'" msgstr "æ— æ³•æ·»åŠ '%2$s' çš„ '%1$s' 值" @@ -9651,11 +9676,6 @@ msgstr "-L<范围>:<文件> ä¸èƒ½å’Œè·¯å¾„表达å¼å…±ç”¨" #: builtin/log.c #, c-format -msgid "Final output: %d %s\n" -msgstr "最终输出:%d %s\n" - -#: builtin/log.c -#, c-format msgid "git show %s: bad file" msgstr "git show %s: æŸå的文件" @@ -10562,6 +10582,10 @@ msgid "(synonym to --stat)" msgstr "(和 --stat åŒä¹‰ï¼‰" #: builtin/merge.c builtin/pull.c +msgid "show a compact-summary at the end of the merge" +msgstr "在åˆå¹¶ç»“æŸæ—¶æ˜¾ç¤ºç®€æ´æ‘˜è¦" + +#: builtin/merge.c builtin/pull.c msgid "add (at most <n>) entries from shortlog to merge commit message" msgstr "在åˆå¹¶æäº¤ä¿¡æ¯ä¸æ·»åŠ ï¼ˆæœ€å¤š <n> æ¡ï¼‰ç²¾ç®€æäº¤è®°å½•" @@ -10890,11 +10914,6 @@ msgstr "é”™è¯¯ï¼šæ ‡ç¾è¾“入未通过 fsck:%s" #: builtin/mktag.c #, c-format -msgid "%d (FSCK_IGNORE?) should never trigger this callback" -msgstr "%d (FSCK_IGNORE?) 永远ä¸åº”该触å‘这个回调" - -#: builtin/mktag.c -#, c-format msgid "could not read tagged object '%s'" msgstr "ä¸èƒ½è¯»å–è¢«æ ‡è®°çš„å¯¹è±¡ '%s'" @@ -11537,13 +11556,26 @@ msgid "unknown subcommand: `%s'" msgstr "未知å命令:`%s'" #: builtin/pack-objects.c -msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]" -msgstr "git pack-objects --stdout [<选项>] [< <引用列表> | < <对象列表>]" - -#: builtin/pack-objects.c msgid "" -"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]" -msgstr "git pack-objects [<选项>] <å‰ç¼€åç§°> [< <引用列表> | < <对象列表>]" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<pack-name>]\n" +" [--cruft] [--cruft-expiration=<time>]\n" +" [--stdout [--filter=<filter-spec>] | <base-name>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=<n>] [--path-walk] < <object-list>" +msgstr "" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<包å>]\n" +" [--cruft] [--cruft-expiration=<æ—¶é—´>]\n" +" [--stdout [--filter=<è¿‡æ»¤è§„æ ¼>] | <基线åç§°>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\"\n" +" [--name-hash-version=<n>] [--path-walk] < <对象列表>" #: builtin/pack-objects.c #, c-format @@ -11672,6 +11704,17 @@ msgid "unable to get type of object %s" msgstr "æ— æ³•èŽ·å¾—å¯¹è±¡ %s 类型" #: builtin/pack-objects.c +msgid "Compressing objects by path" +msgstr "按路径压缩对象" + +#: builtin/pack-objects.c +#, c-format +msgid "Path-based delta compression using up to %d thread" +msgid_plural "Path-based delta compression using up to %d threads" +msgstr[0] "基于路径的差值压缩,最多使用 %d 个线程" +msgstr[1] "基于路径的差值压缩,最多使用 %d 个线程" + +#: builtin/pack-objects.c msgid "Compressing objects" msgstr "压缩对象ä¸" @@ -11762,6 +11805,10 @@ msgid "unable to force loose object" msgstr "æ— æ³•å¼ºåˆ¶æ¾æ•£å¯¹è±¡" #: builtin/pack-objects.c +msgid "failed to pack objects via path-walk" +msgstr "通过 path-walk 打包对象失败" + +#: builtin/pack-objects.c #, c-format msgid "not a rev '%s'" msgstr "䏿˜¯ä¸€ä¸ªç‰ˆæœ¬ '%s'" @@ -11906,6 +11953,10 @@ msgid "create thin packs" msgstr "创建精简包" #: builtin/pack-objects.c +msgid "use the path-walk API to walk objects when possible" +msgstr "在å¯èƒ½çš„æƒ…况下使用 path-walk API é历对象" + +#: builtin/pack-objects.c msgid "create packs suitable for shallow fetches" msgstr "åˆ›å»ºé€‚åˆæµ…克隆仓库获å–的包" @@ -11975,7 +12026,12 @@ msgstr "增é‡é“¾æ·±åº¦ %d 太深了,强制为 %d" msgid "pack.deltaCacheLimit is too high, forcing %d" msgstr "é…ç½® pack.deltaCacheLimit 太高了,强制为 %d" -#: builtin/pack-objects.c config.c +#: builtin/pack-objects.c +#, c-format +msgid "cannot use %s with %s" +msgstr "ä¸èƒ½å°† %s 与 %s 一åŒä½¿ç”¨" + +#: builtin/pack-objects.c environment.c #, c-format msgid "bad pack compression level %d" msgstr "错误的打包压缩级别 %d" @@ -11993,10 +12049,6 @@ msgid "--thin cannot be used to build an indexable pack" msgstr "--thin ä¸èƒ½ç”¨äºŽåˆ›å»ºä¸€ä¸ªå¯ç´¢å¼•包" #: builtin/pack-objects.c -msgid "cannot use --filter with --stdin-packs" -msgstr "ä¸èƒ½åŒæ—¶ä½¿ç”¨ --filter å’Œ --stdin-packs" - -#: builtin/pack-objects.c msgid "cannot use internal rev list with --stdin-packs" msgstr "ä¸èƒ½åŒæ—¶ä½¿ç”¨å†…部版本列表和 --stdin-packs" @@ -12005,10 +12057,6 @@ msgid "cannot use internal rev list with --cruft" msgstr "ä¸èƒ½åŒæ—¶ä½¿ç”¨å†…部版本列表和 --cruft" #: builtin/pack-objects.c -msgid "cannot use --stdin-packs with --cruft" -msgstr "ä¸èƒ½å°† --stdin-packs å’Œ --cruft åŒæ—¶ä½¿ç”¨" - -#: builtin/pack-objects.c msgid "Enumerating objects" msgstr "枚举对象ä¸" @@ -12021,23 +12069,6 @@ msgstr "" "总共 %<PRIu32>(差异 %<PRIu32>),å¤ç”¨ %<PRIu32>(差异 %<PRIu32>),包å¤ç”¨ " "%<PRIu32>(æ¥è‡ª %<PRIuMAX> 个包)" -#: builtin/pack-redundant.c -msgid "" -"'git pack-redundant' is nominated for removal.\n" -"If you still use this command, please add an extra\n" -"option, '--i-still-use-this', on the command line\n" -"and let us know you still use it by sending an e-mail\n" -"to <git@vger.kernel.org>. Thanks.\n" -msgstr "" -"准备移除 'git pack-redundant' å‘½ä»¤ã€‚å¦‚æžœæ‚¨ä»æ—§ä½¿ç”¨è¿™ä¸ª\n" -"å‘½ä»¤ï¼Œè¯·åœ¨å‘½ä»¤è¡Œä¸æ·»åŠ é¢å¤–傿•°ï¼š'--i-still-use-this',\n" -"并通过å‘é€é‚®ä»¶åˆ° <git@vger.kernel.org> è®©æˆ‘ä»¬çŸ¥é“æ‚¨ä»æ—§\n" -"使用它。 谢谢。\n" - -#: builtin/pack-redundant.c -msgid "refusing to run without --i-still-use-this" -msgstr "æ‹’ç»åœ¨æœªæŒ‡å®š --i-still-use-this 选项时è¿è¡Œ" - #: builtin/pack-refs.c msgid "" "git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude " @@ -12395,8 +12426,8 @@ msgid "" "upstream, see 'push.autoSetupRemote' in 'git help config'.\n" msgstr "" "\n" -"为了让没有追踪上游的分支自动é…置,å‚è§ 'git help config' ä¸çš„ " -"push.autoSetupRemote。\n" +"为了让没有追踪上游的分支自动é…置,å‚è§ 'git help config' ä¸çš„ 'push." +"autoSetupRemote'。\n" #: builtin/push.c #, c-format @@ -12862,8 +12893,8 @@ msgstr "--empty=ask 已弃用;请使用 '--empty=stop'。" #: builtin/rebase.c #, c-format msgid "" -"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and " -"\"stop\"." +"unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"stop" +"\"." msgstr "æ— æ³•è¯†åˆ«çš„ç©ºç±»åž‹ '%s';有效值有 \"drop\"ã€\"keep\" å’Œ \"stop\"。" #: builtin/rebase.c @@ -13558,6 +13589,16 @@ msgid "unknown --mirror argument: %s" msgstr "未知的 --mirror 傿•°ï¼š%s" #: builtin/remote.c +#, c-format +msgid "remote name '%s' is a subset of existing remote '%s'" +msgstr "远程åç§° '%s' 是现有远程 '%s' çš„å集" + +#: builtin/remote.c +#, c-format +msgid "remote name '%s' is a superset of existing remote '%s'" +msgstr "远程åç§° '%s' 是现有远程 '%s' 的超集" + +#: builtin/remote.c msgid "fetch the remote branches" msgstr "抓å–远程的分支" @@ -13920,18 +13961,6 @@ msgstr "䏿˜¯ä¸€ä¸ªæœ‰æ•ˆå¼•用:%s" msgid "Could not set up %s" msgstr "ä¸èƒ½è®¾ç½® %s" -# 译者:注æ„ä¿æŒå‰å¯¼ç©ºæ ¼ -#: builtin/remote.c -#, c-format -msgid " %s will become dangling!" -msgstr " %s å°†æˆä¸ºæ‚¬ç©ºçжæ€ï¼" - -# 译者:注æ„ä¿æŒå‰å¯¼ç©ºæ ¼ -#: builtin/remote.c -#, c-format -msgid " %s has become dangling!" -msgstr " %s å·²æˆä¸ºæ‚¬ç©ºçжæ€ï¼" - #: builtin/remote.c #, c-format msgid "Pruning %s" @@ -14015,11 +14044,11 @@ msgstr "冗长输出;必须置于å命令之å‰" msgid "" "git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n" -"[--write-midx] [--name-hash-version=<n>]" +"[--write-midx] [--name-hash-version=<n>] [--path-walk]" msgstr "" "git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<包å>]\n" -"[--write-midx] [--name-hash-version=<n>]" +"[--write-midx] [--name-hash-version=<n>] [--path-walk]" #: builtin/repack.c msgid "" @@ -14121,6 +14150,10 @@ msgid "" msgstr "指定è¦ä½¿ç”¨çš„å称哈希算法,以实现按照路径对相似对象分组" #: builtin/repack.c +msgid "pass --path-walk to git-pack-objects" +msgstr "å‘ git-pack-objects ä¼ é€’å‚æ•° --path-walk" + +#: builtin/repack.c msgid "do not run git-update-server-info" msgstr "ä¸è¿è¡Œ git-update-server-info" @@ -15537,20 +15570,28 @@ msgid "git stash create [<message>]" msgstr "git stash create [<消æ¯>]" #: builtin/stash.c +msgid "git stash export (--print | --to-ref <ref>) [<stash>...]" +msgstr "git stash export (--print | --to-ref <引用>) [<è´®è—>...]" + +#: builtin/stash.c +msgid "git stash import <commit>" +msgstr "git stash import <æäº¤>" + +#: builtin/stash.c #, c-format msgid "'%s' is not a stash-like commit" msgstr "'%s' ä¸åƒæ˜¯ä¸€ä¸ªè´®è—æäº¤" #: builtin/stash.c +msgid "No stash entries found." +msgstr "未å‘çŽ°è´®è—æ¡ç›®ã€‚" + +#: builtin/stash.c #, c-format msgid "Too many revisions specified:%s" msgstr "指定了太多的版本:%s" #: builtin/stash.c -msgid "No stash entries found." -msgstr "未å‘çŽ°è´®è—æ¡ç›®ã€‚" - -#: builtin/stash.c #, c-format msgid "%s is not a valid reference" msgstr "%s 䏿˜¯ä¸€ä¸ªæœ‰æ•ˆçš„引用å" @@ -15745,6 +15786,86 @@ msgstr "è´®è—ä¸åŒ…嫿œªè·Ÿè¸ªæ–‡ä»¶" msgid "include ignore files" msgstr "包å«å¿½ç•¥çš„æ–‡ä»¶" +#: builtin/stash.c +#, c-format +msgid "cannot parse commit %s" +msgstr "ä¸èƒ½è§£æžæäº¤ %s" + +#: builtin/stash.c +#, c-format +msgid "invalid author or committer for %s" +msgstr "%s 的作者或æäº¤è€…ä¿¡æ¯æ— 效" + +#: builtin/stash.c +msgid "could not write commit" +msgstr "æ— æ³•å†™å…¥æäº¤" + +#: builtin/stash.c +#, c-format +msgid "not a valid revision: %s" +msgstr "䏿˜¯æœ‰æ•ˆç‰ˆæœ¬ï¼š%s" + +#: builtin/stash.c +#, c-format +msgid "not a commit: %s" +msgstr "䏿˜¯æäº¤ï¼š%s" + +#: builtin/stash.c +#, c-format +msgid "%s is not a valid exported stash commit" +msgstr "%s 䏿˜¯ä¸€ä¸ªæœ‰æ•ˆçš„ä¸”å·²å¯¼å‡ºçš„è´®è—æäº¤" + +#: builtin/stash.c +#, c-format +msgid "found root commit %s with invalid data" +msgstr "å‘çŽ°æ ¹æäº¤ %s åŒ…å«æ— 效数æ®" + +#: builtin/stash.c +#, c-format +msgid "found stash commit %s without expected prefix" +msgstr "å‘çŽ°è´®è—æäº¤ %s 缺少预期å‰ç¼€" + +#: builtin/stash.c +#, c-format +msgid "cannot parse parents of commit: %s" +msgstr "æ— æ³•è§£æžè¯¥æäº¤çš„父æäº¤ï¼š%s" + +#: builtin/stash.c +#, c-format +msgid "%s does not look like a stash commit" +msgstr "%s 看起æ¥ä¸åƒæ˜¯ä¸€ä¸ªè´®è—æäº¤" + +#: builtin/stash.c +#, c-format +msgid "cannot read commit buffer for %s" +msgstr "æ— æ³•è¯»å–æäº¤çš„ç¼“å†²åŒºï¼š%s" + +#: builtin/stash.c +#, c-format +msgid "cannot save the stash for %s" +msgstr "æ— æ³•ä¿å˜ %s 的贮è—" + +#: builtin/stash.c +msgid "unable to write base commit" +msgstr "æ— æ³•å†™å…¥åŸºçº¿æäº¤" + +#: builtin/stash.c +#, c-format +msgid "unable to find stash entry %s" +msgstr "æ— æ³•æ‰¾åˆ°è´®è—æ¡ç›® %s" + +#: builtin/stash.c +msgid "print the object ID instead of writing it to a ref" +msgstr "打å°å¯¹è±¡ IDï¼Œè€Œä¸æ˜¯å†™å…¥å¼•用" + +#: builtin/stash.c +msgid "save the data to the given ref" +msgstr "将数æ®ä¿å˜åˆ°æŒ‡å®šå¼•用" + +#: builtin/stash.c +msgid "exactly one of --print and --to-ref is required" +msgstr "傿•° --print å’Œ --to-ref 必须二选一" + #: builtin/stripspace.c msgid "skip and remove all lines starting with comment character" msgstr "跳过和移除所有的注释行" @@ -15755,8 +15876,10 @@ msgstr "为æ¯ä¸€è¡Œçš„è¡Œé¦–æ·»åŠ æ³¨é‡Šç¬¦å’Œç©ºæ ¼" #: builtin/submodule--helper.c #, c-format -msgid "Expecting a full ref name, got %s" -msgstr "期望一个完整的引用å称,å´å¾—到 %s" +msgid "" +"could not look up configuration '%s'. Assuming this repository is its own " +"authoritative upstream." +msgstr "æ— æ³•æ‰¾åˆ°é…ç½® '%s'。å‡å®šè¿™ä¸ªä»“库是其自身的官方上游。" #: builtin/submodule--helper.c #, c-format @@ -15765,13 +15888,6 @@ msgstr "æ— æ³•èŽ·å¾—åæ¨¡ç»„ '%s' çš„ä»“åº“å¥æŸ„" #: builtin/submodule--helper.c #, c-format -msgid "" -"could not look up configuration '%s'. Assuming this repository is its own " -"authoritative upstream." -msgstr "æ— æ³•æ‰¾åˆ°é…ç½® '%s'。å‡å®šè¿™ä¸ªä»“库是其自身的官方上游。" - -#: builtin/submodule--helper.c -#, c-format msgid "No url found for submodule path '%s' in .gitmodules" msgstr "在 .gitmodules 䏿œªæ‰¾åˆ°å模组路径 '%s' çš„ url" @@ -16189,6 +16305,11 @@ msgstr "忍¡ç»„(%s)的分支é…置为继承上级项目的分支,但是ä #: builtin/submodule--helper.c #, c-format +msgid "Expecting a full ref name, got %s" +msgstr "期望一个完整的引用å称,å´å¾—到 %s" + +#: builtin/submodule--helper.c +#, c-format msgid "Unable to find current revision in submodule path '%s'" msgstr "æ— æ³•åœ¨åæ¨¡ç»„路径 '%s' 䏿‰¾åˆ°å½“å‰ç‰ˆæœ¬" @@ -16434,6 +16555,11 @@ msgstr "仓库 URL:'%s' 必须是ç»å¯¹è·¯å¾„或以 ./|../ èµ·å§‹" #: builtin/submodule--helper.c #, c-format +msgid "submodule name '%s' already used for path '%s'" +msgstr "忍¡ç»„åç§° '%s' 已被用于路径 '%s'" + +#: builtin/submodule--helper.c +#, c-format msgid "'%s' is not a valid submodule name" msgstr "'%s' 䏿˜¯ä¸€ä¸ªæœ‰æ•ˆçš„忍¡ç»„åç§°" @@ -16988,7 +17114,7 @@ msgstr "ä»Žæ ‡å‡†è¾“å…¥è¯»å–æ›´æ–°" #: builtin/update-ref.c msgid "batch reference updates" -msgstr "批é‡å¼•用更新" +msgstr "批é‡å¤„ç†å¼•用更新" #: builtin/update-server-info.c msgid "update the info files from scratch" @@ -17207,11 +17333,6 @@ msgstr "准备工作区(检出 '%s')" #: builtin/worktree.c #, c-format -msgid "unreachable: invalid reference: %s" -msgstr "ä¸å¯è¾¾ï¼šæ— 效引用:%s" - -#: builtin/worktree.c -#, c-format msgid "Preparing worktree (detached HEAD %s)" msgstr "准备工作区(分离头指针 %s)" @@ -18657,8 +18778,8 @@ msgstr "æ£å°è¯•写æäº¤å›¾ï¼Œä½†æ˜¯ 'core.commitGraph' 被ç¦ç”¨" #: commit-graph.c #, c-format msgid "" -"attempting to write a commit-graph, but 'commitGraph.changedPathsVersion' " -"(%d) is not supported" +"attempting to write a commit-graph, but 'commitGraph." +"changedPathsVersion' (%d) is not supported" msgstr "å°è¯•写入æäº¤å›¾ï¼Œä½†ä¸æ”¯æŒ 'commitGraph.changedPathsVersion' (%d)" #: commit-graph.c @@ -19081,8 +19202,8 @@ msgid "" "remote URLs cannot be configured in file directly or indirectly included by " "includeIf.hasconfig:remote.*.url" msgstr "" -"远程 URL ä¸èƒ½åœ¨æ–‡ä»¶ä¸é…置,ä¸ç®¡ç›´æŽ¥åœ°è¿˜æ˜¯é€šè¿‡ " -"includeIf.hasconfig:remote.*.url 间接地包å«ã€‚" +"远程 URL ä¸èƒ½åœ¨æ–‡ä»¶ä¸é…置,ä¸ç®¡ç›´æŽ¥åœ°è¿˜æ˜¯é€šè¿‡ includeIf.hasconfig:remote.*." +"url 间接地包å«" #: config.c #, c-format @@ -19228,16 +19349,6 @@ msgstr "在 %3$s ä¸é…ç½®å˜é‡ '%2$s' 错误的å–值 '%1$s':%4$s" #: config.c #, c-format -msgid "invalid value for variable %s" -msgstr "å˜é‡ %s çš„å€¼æ— æ•ˆ" - -#: config.c -#, c-format -msgid "ignoring unknown core.fsync component '%s'" -msgstr "忽略未知的 core.fsync 组件 '%s'" - -#: config.c -#, c-format msgid "bad boolean config value '%s' for '%s'" msgstr "'%2$s' 的错误的布尔å–值 '%1$s'" @@ -19253,54 +19364,6 @@ msgstr "'%2$s' 的值 '%1$s' 䏿˜¯ä¸€ä¸ªæœ‰æ•ˆçš„æ—¶é—´æˆ³" #: config.c #, c-format -msgid "abbrev length out of range: %d" -msgstr "缩写长度超出范围:%d" - -#: config.c -#, c-format -msgid "bad zlib compression level %d" -msgstr "错误的 zlib 压缩级别 %d" - -#: config.c -#, c-format -msgid "%s cannot contain newline" -msgstr "%s ä¸èƒ½åŒ…嫿¢è¡Œç¬¦" - -#: config.c -#, c-format -msgid "%s must have at least one character" -msgstr "%s 必须至少有一个å—符" - -#: config.c -#, c-format -msgid "ignoring unknown core.fsyncMethod value '%s'" -msgstr "忽略未知的 core.fsyncMethod 值 '%s'" - -#: config.c -msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" -msgstr "core.fsyncObjectFiles å·²ç»è¢«å¼ƒç”¨ï¼›å–而代之使用 core.fsync" - -#: config.c -#, c-format -msgid "invalid mode for object creation: %s" -msgstr "æ— æ•ˆçš„å¯¹è±¡åˆ›å»ºæ¨¡å¼ï¼š%s" - -#: config.c -#, c-format -msgid "malformed value for %s" -msgstr "%s çš„å–å€¼æ ¼å¼é”™è¯¯" - -#: config.c -#, c-format -msgid "malformed value for %s: %s" -msgstr "%s çš„å–å€¼æ ¼å¼é”™è¯¯ï¼š%s" - -#: config.c -msgid "must be one of nothing, matching, simple, upstream or current" -msgstr "必须是其ä¸ä¹‹ä¸€ï¼šnothingã€matchingã€simpleã€upstream 或 current" - -#: config.c -#, c-format msgid "unable to load config blob object '%s'" msgstr "æ— æ³•ä»Žæ•°æ®å¯¹è±¡ '%s' åŠ è½½é…ç½®" @@ -19922,8 +19985,8 @@ msgid "cannot compare a named pipe to a directory" msgstr "æ— æ³•å°†å‘½å管é“和目录进行比较" #: diff-no-index.c -msgid "git diff --no-index [<options>] <path> <path>" -msgstr "git diff --no-index [<选项>] <路径> <路径>" +msgid "git diff --no-index [<options>] <path> <path> [<pathspec>...]" +msgstr "git diff --no-index [<选项>] <路径> <路径> [<è·¯å¾„è§„æ ¼>...]" #: diff-no-index.c msgid "" @@ -19931,6 +19994,12 @@ msgid "" "tree" msgstr "䏿˜¯ git 仓库。使用 --no-index 比较工作区之外的两个路径" +#: diff-no-index.c +msgid "" +"Limiting comparison with pathspecs is only supported if both paths are " +"directories." +msgstr "仅当两个路径å‡ä¸ºç›®å½•æ—¶ï¼Œæ‰æ”¯æŒä½¿ç”¨è·¯å¾„表达å¼é™åˆ¶æ¯”较范围。" + # 译者:注æ„ä¿æŒå‰å¯¼ç©ºæ ¼ #: diff.c #, c-format @@ -20109,7 +20178,7 @@ msgstr "生æˆè¡¥ä¸" msgid "<n>" msgstr "<n>" -#: diff.c +#: diff.c parse-options.h msgid "generate diffs with <n> lines context" msgstr "生æˆå« <n> 行上下文的差异" @@ -20256,7 +20325,7 @@ msgstr "䏿˜¾ç¤ºä»»ä½•æºå’Œç›®æ ‡å‰ç¼€" msgid "use default prefixes a/ and b/" msgstr "使用 a/ å’Œ b/ 作为默认å‰ç¼€" -#: diff.c +#: diff.c parse-options.h msgid "show context between diff hunks up to the specified number of lines" msgstr "显示指定行数的差异å—间的上下文" @@ -20641,6 +20710,64 @@ msgstr "ä¸èƒ½å¯¹æ–‡ä»¶ '%s' 调用 stat" msgid "bad git namespace path \"%s\"" msgstr "错误的 git åå—空间路径 \"%s\"" +#: environment.c +#, c-format +msgid "invalid value for variable %s" +msgstr "å˜é‡ %s çš„å€¼æ— æ•ˆ" + +#: environment.c +#, c-format +msgid "ignoring unknown core.fsync component '%s'" +msgstr "忽略未知的 core.fsync 组件 '%s'" + +#: environment.c +#, c-format +msgid "abbrev length out of range: %d" +msgstr "缩写长度超出范围:%d" + +#: environment.c +#, c-format +msgid "bad zlib compression level %d" +msgstr "错误的 zlib 压缩级别 %d" + +#: environment.c +#, c-format +msgid "%s cannot contain newline" +msgstr "%s ä¸èƒ½åŒ…嫿¢è¡Œç¬¦" + +#: environment.c +#, c-format +msgid "%s must have at least one character" +msgstr "%s 必须至少有一个å—符" + +#: environment.c +#, c-format +msgid "ignoring unknown core.fsyncMethod value '%s'" +msgstr "忽略未知的 core.fsyncMethod 值 '%s'" + +#: environment.c +msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" +msgstr "core.fsyncObjectFiles å·²ç»è¢«å¼ƒç”¨ï¼›å–而代之使用 core.fsync" + +#: environment.c +#, c-format +msgid "invalid mode for object creation: %s" +msgstr "æ— æ•ˆçš„å¯¹è±¡åˆ›å»ºæ¨¡å¼ï¼š%s" + +#: environment.c +#, c-format +msgid "malformed value for %s" +msgstr "%s çš„å–å€¼æ ¼å¼é”™è¯¯" + +#: environment.c +#, c-format +msgid "malformed value for %s: %s" +msgstr "%s çš„å–å€¼æ ¼å¼é”™è¯¯ï¼š%s" + +#: environment.c +msgid "must be one of nothing, matching, simple, upstream or current" +msgstr "必须是其ä¸ä¹‹ä¸€ï¼šnothingã€matchingã€simpleã€upstream 或 current" + #: exec-cmd.c #, c-format msgid "too many args to run %s" @@ -21499,6 +21626,35 @@ msgstr "ä¸å…许空的姓å(对于 <%s>)" msgid "name consists only of disallowed characters: %s" msgstr "å§“åä¸ä»…包å«ç¦ç”¨å—符:%s" +#: imap-send.c +msgid "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>" +msgstr "" +"git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <文件夹>] < <mbox>" + +#: imap-send.c +msgid "no IMAP host specified" +msgstr "未指定 IMAP 主机" + +#: imap-send.c +msgid "" +"set the IMAP host with 'git config imap.host <host>'.\n" +"(e.g., 'git config imap.host imaps://imap.example.com')" +msgstr "" +"通过 'git config imap.host <主机>' 设置 IMAP 主机。\n" +"(例如:'git config imap.host imaps://imap.example.com')" + +#: imap-send.c +msgid "no IMAP folder specified" +msgstr "未指定 IMAP 文件夹" + +#: imap-send.c +msgid "" +"set the target folder with 'git config imap.folder <folder>'.\n" +"(e.g., 'git config imap.folder Drafts')" +msgstr "" +"通过 'git config imap.folder <文件夹>' è®¾ç½®ç›®æ ‡æ–‡ä»¶å¤¹ã€‚\n" +"(例如:'git config imap.folder Drafts')" + #: list-objects-filter-options.c msgid "expected 'tree:<depth>'" msgstr "期望 'tree:<深度>'" @@ -22455,7 +22611,7 @@ msgstr "æ— æ³•è§£æž %s 的头部" #: object-file.c #, c-format msgid "unable to parse type from header '%s' of %s" -msgstr "æ— æ³•ä»Ž %s 的包头 '%s' ä¸è§£æžç±»åž‹" +msgstr "æ— æ³•ä»Ž %2$s 的包头 '%1$s' ä¸è§£æžç±»åž‹" #: object-file.c #, c-format @@ -22634,113 +22790,113 @@ msgstr "éœ€è¦ <对象>:<路径>,åªç»™å‡ºäº† <对象> '%s'" msgid "invalid object name '%.*s'." msgstr "æ— æ•ˆçš„å¯¹è±¡å '%.*s'。" -#: object-store.c +#: object.c +#, c-format +msgid "invalid object type \"%s\"" +msgstr "æ— æ•ˆçš„å¯¹è±¡ç±»åž‹ \"%s\"" + +#: object.c +#, c-format +msgid "object %s is a %s, not a %s" +msgstr "对象 %s 是一个 %sï¼Œä¸æ˜¯ä¸€ä¸ª %s" + +#: object.c +#, c-format +msgid "object %s has unknown type id %d" +msgstr "对象 %s 有未知的类型 id %d" + +#: object.c +#, c-format +msgid "unable to parse object: %s" +msgstr "ä¸èƒ½è§£æžå¯¹è±¡ï¼š%s" + +#: object.c +#, c-format +msgid "hash mismatch %s" +msgstr "哈希值与 %s ä¸åŒ¹é…" + +#: odb.c #, c-format msgid "object directory %s does not exist; check .git/objects/info/alternates" msgstr "对象目录 %s ä¸å˜åœ¨ï¼Œæ£€æŸ¥ .git/objects/info/alternates" -#: object-store.c +#: odb.c #, c-format msgid "unable to normalize alternate object path: %s" msgstr "æ— æ³•è§„èŒƒåŒ–å¤‡ç”¨å¯¹è±¡è·¯å¾„ï¼š%s" -#: object-store.c +#: odb.c #, c-format msgid "%s: ignoring alternate object stores, nesting too deep" msgstr "%s:忽略备用对象库,嵌套太深" -#: object-store.c +#: odb.c msgid "unable to fdopen alternates lockfile" msgstr "æ— æ³• fdopen alternates çš„é”æ–‡ä»¶" -#: object-store.c +#: odb.c msgid "unable to read alternates file" msgstr "æ— æ³•è¯»å– alternates 文件" -#: object-store.c +#: odb.c msgid "unable to move new alternates file into place" msgstr "æ— æ³•å°†æ–°çš„ alternates 文件移动到ä½" -#: object-store.c +#: odb.c #, c-format msgid "path '%s' does not exist" msgstr "路径 '%s' ä¸å˜åœ¨" -#: object-store.c +#: odb.c #, c-format msgid "reference repository '%s' as a linked checkout is not supported yet." msgstr "å°šä¸æ”¯æŒå°†å‚考仓库 '%s' 作为链接检出。" -#: object-store.c +#: odb.c #, c-format msgid "reference repository '%s' is not a local repository." msgstr "å‚考仓库 '%s' 䏿˜¯æœ¬åœ°ä»“库。" -#: object-store.c +#: odb.c #, c-format msgid "reference repository '%s' is shallow" msgstr "å‚考仓库 '%s' 是浅克隆" -#: object-store.c +#: odb.c #, c-format msgid "reference repository '%s' is grafted" msgstr "å‚考仓库 '%s' 已被移æ¤" -#: object-store.c +#: odb.c #, c-format msgid "could not find object directory matching %s" msgstr "æ— æ³•æ‰¾åˆ°å’Œ %s 匹é…的对象目录" -#: object-store.c +#: odb.c #, c-format msgid "invalid line while parsing alternate refs: %s" msgstr "è§£æžå¤‡ç”¨å¼•用时é‡åˆ°æ— 效的行:%s" -#: object-store.c +#: odb.c #, c-format msgid "replacement %s not found for %s" msgstr "找ä¸åˆ° %2$s 的替代 %1$s" -#: object-store.c +#: odb.c #, c-format msgid "packed object %s (stored in %s) is corrupt" msgstr "打包对象 %s(ä¿å˜åœ¨ %s)已æŸå" -#: object-store.c +#: odb.c #, c-format msgid "missing mapping of %s to %s" msgstr "缺少 %s 到 %s çš„æ˜ å°„" -#: object-store.c +#: odb.c #, c-format msgid "%s is not a valid '%s' object" msgstr "%s 䏿˜¯æœ‰æ•ˆçš„ '%s' 对象" -#: object.c -#, c-format -msgid "invalid object type \"%s\"" -msgstr "æ— æ•ˆçš„å¯¹è±¡ç±»åž‹ \"%s\"" - -#: object.c -#, c-format -msgid "object %s is a %s, not a %s" -msgstr "对象 %s 是一个 %sï¼Œä¸æ˜¯ä¸€ä¸ª %s" - -#: object.c -#, c-format -msgid "object %s has unknown type id %d" -msgstr "对象 %s 有未知的类型 id %d" - -#: object.c -#, c-format -msgid "unable to parse object: %s" -msgstr "ä¸èƒ½è§£æžå¯¹è±¡ï¼š%s" - -#: object.c -#, c-format -msgid "hash mismatch %s" -msgstr "哈希值与 %s ä¸åŒ¹é…" - #: pack-bitmap-write.c #, c-format msgid "duplicate entry when writing bitmap index: %s" @@ -22755,10 +22911,6 @@ msgstr "å°è¯•å˜å‚¨æœªé€‰å®šçš„æäº¤ï¼š'%s'" msgid "too many pseudo-merges" msgstr "太多伪åˆå¹¶" -#: pack-bitmap-write.c -msgid "trying to write commit not in index" -msgstr "å°è¯•写入未在索引ä¸çš„æäº¤" - #: pack-bitmap.c msgid "failed to load bitmap index (corrupted?)" msgstr "æ— æ³•è½½å…¥ä½å›¾ç´¢å¼•(已æŸå?)" @@ -23063,6 +23215,11 @@ msgstr "%s ä¸å¯ç”¨" #: parse-options.c #, c-format +msgid "value for %s exceeds %<PRIdMAX>" +msgstr "%s 的值超出了 %<PRIdMAX>" + +#: parse-options.c +#, c-format msgid "value %s for %s not in range [%<PRIdMAX>,%<PRIdMAX>]" msgstr "%2$s 的值 %1$s ä¸åœ¨èŒƒå›´ [%3$<PRIdMAX>,%4$<PRIdMAX>] 内" @@ -24215,6 +24372,18 @@ msgstr "没有 '%s' 的引用日志" msgid "%s does not point to a valid object!" msgstr "%s 没有指å‘一个有效的对象ï¼" +# 译者:注æ„ä¿æŒå‰å¯¼ç©ºæ ¼ +#: refs.c +#, c-format +msgid "%s%s will become dangling after %s is deleted\n" +msgstr "%s%s 将在 %s è¢«åˆ é™¤åŽå˜ä¸ºæ‚¬ç©ºå¼•用\n" + +# 译者:注æ„ä¿æŒå‰å¯¼ç©ºæ ¼ +#: refs.c +#, c-format +msgid "%s%s has become dangling after %s was deleted\n" +msgstr "%s%s 已在 %s è¢«åˆ é™¤åŽå˜ä¸ºæ‚¬ç©ºå¼•用\n" + #: refs.c #, c-format msgid "" @@ -26992,6 +27161,10 @@ msgid "toggle pruning of uninteresting paths" msgstr "切æ¢å¯¹æ— 趣路径的修剪" #: t/helper/test-path-walk.c +msgid "toggle aggressive edge walk" +msgstr "åˆ‡æ¢æ¿€è¿›è¾¹é历模å¼" + +#: t/helper/test-path-walk.c msgid "read a pattern list over stdin" msgstr "ä»Žæ ‡å‡†è¾“å…¥è¯»å–æ¨¡å¼åˆ—表" @@ -27742,6 +27915,24 @@ msgstr "错误:" msgid "warning: " msgstr "è¦å‘Šï¼š" +#: usage.c +#, c-format +msgid "" +"'%s' is nominated for removal.\n" +"If you still use this command, please add an extra\n" +"option, '--i-still-use-this', on the command line\n" +"and let us know you still use it by sending an e-mail\n" +"to <git@vger.kernel.org>. Thanks.\n" +msgstr "" +"'%s' 命令已被æå移除。\n" +"如果您ä»åœ¨ä½¿ç”¨è¯¥å‘½ä»¤ï¼Œè¯·åœ¨å‘½ä»¤è¡Œä¸æ·»åŠ é¢å¤–选项\n" +"'--i-still-use-this',并通过å‘é€é‚®ä»¶è‡³ <git@vger.kernel.org> \n" +"通知我们您ä»åœ¨ä½¿ç”¨å®ƒã€‚谢谢。\n" + +#: usage.c +msgid "refusing to run without --i-still-use-this" +msgstr "æ‹’ç»åœ¨æœªæŒ‡å®š --i-still-use-this 选项时è¿è¡Œ" + #: version.c #, c-format msgid "uname() failed with error '%s' (%d)\n" @@ -28890,6 +29081,11 @@ msgstr "(body) æ·»åŠ cc: %s 自行 '%s'\n" #: git-send-email.perl #, perl-format +msgid "error: invalid SMTP port '%s'\n" +msgstr "é”™è¯¯ï¼šæ— æ•ˆçš„ SMTP ç«¯å£ '%s'\n" + +#: git-send-email.perl +#, perl-format msgid "(%s) Could not execute '%s'" msgstr "(%s) ä¸èƒ½æ‰§è¡Œ '%s'" diff --git a/po/zh_TW.po b/po/zh_TW.po index 4fcbb71f8b..6fe5dbccd2 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -19,22 +19,22 @@ # - Yichao Yu <yyc1992 AT gmail.com> # - Zhuang Ya <zhuangya AT me.com> # -# Yi-Jyun Pan <pan93412@gmail.com>, 2021, 2022, 2023, 2024. +# Yi-Jyun Pan <pan93412@gmail.com>, 2021, 2022, 2023, 2024, 2025. # Kaiyang Wu <self@origincode.me>, 2022. -# Lumynous <lumynou5.tw@gmail.com>, 2023, 2024. +# Lumynous <lumynou5.tw@gmail.com>, 2023, 2024, 2025. # Kisaragi Hiu <mail@kisaragi-hiu.com>, 2024. # Ngoo Ka-iu <willy04wu69@gmail.com>, 2024. # Nightfeather Chen <slat@nightfeather.me>, 2024. -# hms5232 <hms5232@hhming.moe>, 2024. +# hms5232 <hms5232@hhming.moe>, 2024, 2025. msgid "" msgstr "" "Project-Id-Version: Git\n" "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n" -"POT-Creation-Date: 2025-06-12 22:09+0800\n" -"PO-Revision-Date: 2025-06-12 22:25+0800\n" -"Last-Translator: Yi-Jyun Pan <pan93412@gmail.com>\n" -"Language-Team: Chinese (Traditional) <http://weblate.slat.org/projects/git-" -"po/git-cli/zh_Hant/>\n" +"POT-Creation-Date: 2025-08-16 12:10+0800\n" +"PO-Revision-Date: 2025-08-16 12:11+0800\n" +"Last-Translator: hms5232 <hms5232@hhming.moe>\n" +"Language-Team: Chinese (Traditional Han script) <https://weblate.slat.org/" +"projects/git-po/git-cli/zh_Hant/>\n" "Language: zh_TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -46,6 +46,11 @@ msgstr "" #: add-interactive.c #, c-format +msgid "%s cannot be negative" +msgstr "%s ä¸èƒ½æ˜¯è² 數" + +#: add-interactive.c +#, c-format msgid "Huh (%s)?" msgstr "嗯(%s)?" @@ -131,12 +136,12 @@ msgstr "忽略未åˆä½µé …目:%s" #: add-interactive.c #, c-format msgid "Only binary files changed.\n" -msgstr "åªæœ‰äºŒé€²ä½æª”案更動了。\n" +msgstr "åªæœ‰äºŒé€²ä½æª”案變更了。\n" #: add-interactive.c #, c-format msgid "No changes.\n" -msgstr "沒有更動。\n" +msgstr "沒有變更。\n" #: add-interactive.c msgid "Patch update" @@ -148,15 +153,15 @@ msgstr "檢閱差異" #: add-interactive.c msgid "show paths with changes" -msgstr "顯示有更動的路徑" +msgstr "顯示有變更的路徑" #: add-interactive.c msgid "add working tree state to the staged set of changes" -msgstr "將工作å€ç‹€æ…‹åŠ å…¥è‡³æš«å˜æ›´å‹•集" +msgstr "將工作å€ç‹€æ…‹åŠ å…¥è‡³æš«å˜è®Šæ›´æš«å˜é›†" #: add-interactive.c msgid "revert staged set of changes back to the HEAD version" -msgstr "將暫å˜çš„æ›´å‹•集還原回 HEAD 版本" +msgstr "將變更暫å˜é›†é‚„原至 HEAD 版本" #: add-interactive.c msgid "pick hunks and update selectively" @@ -168,7 +173,7 @@ msgstr "檢視 HEAD åŠç´¢å¼•之間的差異" #: add-interactive.c msgid "add contents of untracked files to the staged set of changes" -msgstr "å°‡æœªè¿½è¹¤æª”æ¡ˆçš„å…§å®¹åŠ å…¥è‡³æ›´å‹•æš«å˜é›†" +msgstr "å°‡æœªè¿½è¹¤æª”æ¡ˆçš„å…§å®¹åŠ å…¥è‡³è®Šæ›´æš«å˜é›†" #: add-interactive.c msgid "Prompt help:" @@ -244,7 +249,7 @@ msgstr "å†è¦‹ã€‚\n" #: add-patch.c #, c-format msgid "Stage mode change [y,n,q,a,d%s,?]? " -msgstr "æš«å˜æ¨¡å¼æ›´å‹• [y,n,q,a,d%s,?]? " +msgstr "æš«å˜æ¨¡å¼è®Šæ›´ [y,n,q,a,d%s,?]? " #: add-patch.c #, c-format @@ -284,7 +289,7 @@ msgstr "" #: add-patch.c #, c-format msgid "Stash mode change [y,n,q,a,d%s,?]? " -msgstr "è²¯å˜æ¨¡å¼æ›´å‹• [y,n,q,a,d%s,?]? " +msgstr "è²¯å˜æ¨¡å¼è®Šæ›´ [y,n,q,a,d%s,?]? " #: add-patch.c #, c-format @@ -324,7 +329,7 @@ msgstr "" #: add-patch.c #, c-format msgid "Unstage mode change [y,n,q,a,d%s,?]? " -msgstr "å–æ¶ˆæš«å˜æ¨¡å¼æ›´å‹• [y,n,q,a,d%s,?]? " +msgstr "å–æ¶ˆæš«å˜æ¨¡å¼è®Šæ›´ [y,n,q,a,d%s,?]? " #: add-patch.c #, c-format @@ -364,7 +369,7 @@ msgstr "" #: add-patch.c #, c-format msgid "Apply mode change to index [y,n,q,a,d%s,?]? " -msgstr "å°‡æ¨¡å¼æ›´å‹•套用到索引 [y,n,q,a,d%s,?]? " +msgstr "將模å¼è®Šæ›´å¥—ç”¨è‡³ç´¢å¼•å€ [y,n,q,a,d%s,?]? " #: add-patch.c #, c-format @@ -404,7 +409,7 @@ msgstr "" #: add-patch.c #, c-format msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? " -msgstr "å¾žå·¥ä½œå€æ¨æ£„æ¨¡å¼æ›´å‹• [y,n,q,a,d%s,?]? " +msgstr "å¾žå·¥ä½œå€æ¨æ£„模å¼è®Šæ›´ [y,n,q,a,d%s,?]? " #: add-patch.c #, c-format @@ -444,7 +449,7 @@ msgstr "" #: add-patch.c #, c-format msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? " -msgstr "å¾žç´¢å¼•å’Œå·¥ä½œå€æ¨æ£„æ¨¡å¼æ›´å‹• [y,n,q,a,d%s,?]? " +msgstr "從索引å€å’Œå·¥ä½œå€æ¨æ£„模å¼è®Šæ›´ [y,n,q,a,d%s,?]? " #: add-patch.c #, c-format @@ -478,7 +483,7 @@ msgstr "" #: add-patch.c #, c-format msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? " -msgstr "å°‡æ¨¡å¼æ›´å‹•å¥—ç”¨åˆ°ç´¢å¼•å’Œå·¥ä½œå€ [y,n,q,a,d%s,?]? " +msgstr "將模å¼è®Šæ›´å¥—用至索引å€å’Œå·¥ä½œå€ [y,n,q,a,d%s,?]? " #: add-patch.c #, c-format @@ -512,7 +517,7 @@ msgstr "" #: add-patch.c #, c-format msgid "Apply mode change to worktree [y,n,q,a,d%s,?]? " -msgstr "å°‡æ¨¡å¼æ›´å‹•å¥—ç”¨åˆ°å·¥ä½œå€ [y,n,q,a,d%s,?]? " +msgstr "將模å¼è®Šæ›´å¥—ç”¨è‡³å·¥ä½œå€ [y,n,q,a,d%s,?]? " #: add-patch.c #, c-format @@ -608,9 +613,9 @@ msgid "" "Lines starting with %s will be removed.\n" msgstr "" "---\n" -"è¦åˆªé™¤ã€Œ%cã€é–‹é 的列,請將列首(上下文)改æˆç©ºç™½ã€‚\n" -"è¦åˆªé™¤ã€Œ%cã€é–‹é 的列,請直接刪除。\n" -"é–‹é æ˜¯ %s 的列將會被移除。\n" +"è¦ç§»é™¤ã€Œ%cã€é–‹é 的列,請將列首(上下文)改æˆç©ºç™½ã€‚\n" +"è¦ç§»é™¤ã€Œ%cã€é–‹é 的列,請直接刪除。\n" +"é–‹é æ˜¯ %s 的列會被移除。\n" #: add-patch.c msgid "" @@ -754,11 +759,11 @@ msgstr "「git applyã€å¤±æ•—" #: add-patch.c msgid "No changes." -msgstr "沒有更動。" +msgstr "沒有變更。" #: add-patch.c msgid "Only binary files changed." -msgstr "åªæœ‰äºŒé€²ä½æª”案更動了。" +msgstr "åªæœ‰äºŒé€²ä½æª”案變更了。" #: advice.c #, c-format @@ -816,7 +821,7 @@ msgstr "åˆä½µå°šæœªçµæŸï¼ˆæœ‰ MERGE_HEAD)。" #: advice.c msgid "Please, commit your changes before merging." -msgstr "請在åˆä½µå‰å…ˆæäº¤æ‚¨çš„æ›´å‹•。" +msgstr "請在åˆä½µå‰å…ˆæäº¤æ‚¨çš„變更。" #: advice.c msgid "Exiting because of unfinished merge." @@ -912,8 +917,8 @@ msgid "" "sparse-checkout definition but are not sparse due to local\n" "modifications.\n" msgstr "" -"以下路徑已經移到稀ç–簽出定義之外的地方,\n" -"ä½†å› ç‚ºæœ‰æœ¬æ©Ÿæ›´å‹•ï¼Œå› æ¤è·¯å¾‘䏿˜¯ç¨€ç–的。\n" +"以下路徑已移動至稀ç–簽出定義以外的ä½ç½®ï¼Œ\n" +"ä½†å› æœ¬æ©Ÿä¿®æ”¹è€Œä¸æ˜¯ç¨€ç–的。\n" #: advice.c msgid "" @@ -941,22 +946,22 @@ msgstr "引數éŽå¤š" #: apply.c #, c-format msgid "unrecognized whitespace option '%s'" -msgstr "空白å—å…ƒé¸é …「%sã€ä¸èªè˜" +msgstr "無法è˜åˆ¥ç©ºç™½å—å…ƒé¸é …「%sã€" #: apply.c #, c-format msgid "unrecognized whitespace ignore option '%s'" -msgstr "空白å—元忽略é¸é …「%sã€ä¸èªè˜" +msgstr "無法辨è˜ç©ºç™½å—元忽略é¸é …「%sã€" #: apply.c archive.c builtin/add.c builtin/branch.c builtin/checkout-index.c #: builtin/checkout.c builtin/clean.c builtin/clone.c builtin/commit.c #: builtin/describe.c builtin/diff-tree.c builtin/difftool.c #: builtin/fast-export.c builtin/fetch.c builtin/help.c builtin/index-pack.c #: builtin/init-db.c builtin/log.c builtin/ls-files.c builtin/merge-base.c -#: builtin/merge-tree.c builtin/merge.c builtin/pack-objects.c builtin/rebase.c -#: builtin/repack.c builtin/replay.c builtin/reset.c builtin/rev-parse.c -#: builtin/show-branch.c builtin/stash.c builtin/submodule--helper.c -#: builtin/tag.c builtin/worktree.c parse-options.c range-diff.c revision.c +#: builtin/merge-tree.c builtin/merge.c builtin/rebase.c builtin/repack.c +#: builtin/replay.c builtin/reset.c builtin/rev-parse.c builtin/show-branch.c +#: builtin/stash.c builtin/submodule--helper.c builtin/tag.c builtin/worktree.c +#: parse-options.c range-diff.c revision.c #, c-format msgid "options '%s' and '%s' cannot be used together" msgstr "ç„¡æ³•åŒæ™‚使用「%sã€å’Œã€Œ%sã€é¸é …" @@ -1080,7 +1085,7 @@ msgstr "二進ä½ä¿®è£œæª”在第 %d 列æå£žï¼š%.*s" #: apply.c #, c-format msgid "unrecognized binary patch at line %d" -msgstr "第 %d 列的二進ä½ä¿®è£œæª”ä¸èªè˜" +msgstr "無法辨è˜ç¬¬ %d 列的二進ä½ä¿®è£œæª”" #: apply.c #, c-format @@ -1294,7 +1299,7 @@ msgstr "%s 忍¡çµ„缺少 sha1 資訊或沒有幫助" #: apply.c #, c-format msgid "mode change for %s, which is not in current HEAD" -msgstr "%s çš„æ¨¡å¼æœ‰æ›´å‹•,但其ä¸åœ¨ç›®å‰ HEAD ä¸" +msgstr "有 %s 的模å¼è®Šæ›´ï¼Œä½†å…¶ä¸åœ¨ç›®å‰çš„ HEAD ä¸" #: apply.c #, c-format @@ -1428,11 +1433,11 @@ msgstr "無法寫入新索引檔案" #: apply.c msgid "don't apply changes matching the given path" -msgstr "ä¸è¦å¥—ç”¨ç¬¦åˆæä¾›è·¯å¾‘çš„æ›´å‹•" +msgstr "ä¸å¥—ç”¨ç¬¦åˆæä¾›è·¯å¾‘çš„è®Šæ›´" #: apply.c msgid "apply changes matching the given path" -msgstr "å¥—ç”¨ç¬¦åˆæä¾›è·¯å¾‘çš„æ›´å‹•" +msgstr "å¥—ç”¨ç¬¦åˆæä¾›è·¯å¾‘çš„è®Šæ›´" #: apply.c builtin/am.c msgid "num" @@ -1488,15 +1493,15 @@ msgstr "嘗試三方åˆä½µï¼Œè‹¥å¤±æ•—則回到æ£å¸¸ä¿®è£œæ¨¡å¼" #: apply.c builtin/merge-file.c msgid "for conflicts, use our version" -msgstr "如果è¡çªï¼Œä½¿ç”¨æˆ‘們的版本" +msgstr "如有è¡çªï¼Œä½¿ç”¨æˆ‘方版本" #: apply.c builtin/merge-file.c msgid "for conflicts, use their version" -msgstr "如果è¡çªï¼Œä½¿ç”¨ä»–們的版本" +msgstr "如有è¡çªï¼Œä½¿ç”¨ä»–方版本" #: apply.c builtin/merge-file.c msgid "for conflicts, use a union version" -msgstr "如果è¡çªï¼Œä½¿ç”¨è¯åˆç‰ˆæœ¬" +msgstr "如有è¡çªï¼Œä½¿ç”¨è¯é›†ç‰ˆæœ¬" #: apply.c msgid "build a temporary index based on embedded index information" @@ -1521,7 +1526,7 @@ msgstr "檢查新增和修改的列ä¸é–“ï¼Œæ˜¯å¦æœ‰ç©ºç™½å—元誤用" #: apply.c msgid "ignore changes in whitespace when finding context" -msgstr "尋找上下文時忽略空白å—元更動" +msgstr "尋找上下文時忽略空白å—元變更" #: apply.c msgid "apply the patch in reverse" @@ -1831,7 +1836,7 @@ msgstr "忽略éŽå¤§çš„ gitattributes 資料物件「%sã€" #: attr.c msgid "cannot use --attr-source or GIT_ATTR_SOURCE without repo" -msgstr "沒有版本庫無法使用 --attr-source 或 GIT_ATTR_SOURCE" +msgstr "沒有版本庫ä¸èƒ½ä½¿ç”¨ --attr-source 或 GIT_ATTR_SOURCE" #: attr.c msgid "bad --attr-source or GIT_ATTR_SOURCE" @@ -1840,7 +1845,7 @@ msgstr "無效的 --attr-source 或 GIT_ATTR_SOURCE" #: attr.c read-cache.c refs/packed-backend.c #, c-format msgid "unable to stat '%s'" -msgstr "無法統計「%sã€" +msgstr "無法 stat「%sã€" #: bisect.c builtin/cat-file.c builtin/index-pack.c builtin/notes.c #: builtin/pack-objects.c combine-diff.c object-file.c rerere.c @@ -1930,7 +1935,7 @@ msgstr "無法建立「%sã€æª”案" #: bisect.c builtin/notes.c #, c-format msgid "unable to start 'show' for object '%s'" -msgstr "無法為物件「%sã€é–‹å§‹ã€Œshowã€" +msgstr "無法å°ç‰©ä»¶ã€Œ%sã€åŸ·è¡Œã€Œshowã€" #: bisect.c builtin/merge.c #, c-format @@ -2190,7 +2195,7 @@ msgstr "無法 chmod %cx '%s'" #: builtin/add.c msgid "Unstaged changes after refreshing the index:" -msgstr "釿–°æ•´ç†ç´¢å¼•之後,尚未被暫å˜çš„æ›´å‹•:" +msgstr "釿–°æ•´ç†ç´¢å¼•之後未暫å˜çš„變更:" #: builtin/add.c msgid "could not read the index" @@ -2203,7 +2208,7 @@ msgstr "編輯修補檔失敗" #: builtin/add.c read-cache.c #, c-format msgid "could not stat '%s'" -msgstr "無法統計「%sã€" +msgstr "無法 stat「%sã€" #: builtin/add.c msgid "empty patch. aborted" @@ -2260,7 +2265,7 @@ msgstr "åªè¨˜éŒ„「路徑ç¨å¾Œæœƒå†åŠ å…¥ã€" #: builtin/add.c msgid "add changes from all tracked and untracked files" -msgstr "å¾žæ‰€æœ‰å·²è¿½è¹¤å’Œæœªè¿½è¹¤æª”æ¡ˆåŠ å…¥æ›´å‹•" +msgstr "åŠ å…¥æ‰€æœ‰å·²è¿½è¹¤å’Œæœªè¿½è¹¤æª”æ¡ˆçš„è®Šæ›´" #: builtin/add.c msgid "ignore paths removed in the working tree (same as --no-all)" @@ -2334,6 +2339,12 @@ msgstr "如果您確定想è¦åŠ å…¥å®ƒå€‘ï¼Œè«‹ä½¿ç”¨ -f。" msgid "adding files failed" msgstr "åŠ å…¥æª”æ¡ˆå¤±æ•—" +#: builtin/add.c builtin/checkout.c builtin/commit.c builtin/reset.c +#: builtin/stash.c +#, c-format +msgid "'%s' cannot be negative" +msgstr "「%sã€ä¸èƒ½æ˜¯è² 數" + #: builtin/add.c #, c-format msgid "--chmod param '%s' must be either -x or +x" @@ -2372,7 +2383,7 @@ msgid "bad action '%s' for '%s'" msgstr "「%sã€å‹•作å°ã€Œ%sã€ç„¡æ•ˆ" #: builtin/am.c builtin/blame.c builtin/fetch.c builtin/pack-objects.c -#: builtin/pull.c builtin/revert.c config.c diff-merges.c gpg-interface.c +#: builtin/pull.c builtin/revert.c diff-merges.c environment.c gpg-interface.c #: ls-refs.c parallel-checkout.c sequencer.c setup.c #, c-format msgid "invalid value for '%s': '%s'" @@ -2463,7 +2474,7 @@ msgstr "解決æ¤å•題後,請執行「%s --continueã€ã€‚\n" #: builtin/am.c #, c-format msgid "If you prefer to skip this patch, run \"%s --skip\" instead.\n" -msgstr "è‹¥è¦ç•¥éŽæ¤ä¿®è£œï¼Œè«‹æ”¹åŸ·è¡Œã€Œ%s --skipã€ã€‚\n" +msgstr "è‹¥è¦ç•¥éŽæ¤ä¿®è£œæª”,請改執行「%s --skipã€ã€‚\n" #: builtin/am.c #, c-format @@ -2517,7 +2528,7 @@ msgstr "æ£åœ¨å›žå¾©åˆ°ä¿®è£œåŸºç¤Žè™•,並進行三方åˆä½µâ€¦â€¦" #: builtin/am.c msgid "Failed to merge in the changes." -msgstr "無法åˆä½µæ›´å‹•。" +msgstr "åˆä½µè®Šæ›´å¤±æ•—。" #: builtin/am.c builtin/merge.c sequencer.c msgid "git write-tree failed to write a tree" @@ -2952,7 +2963,7 @@ msgstr "無法開啟檔案「%sã€" #: builtin/bisect.c #, c-format msgid "Invalid command: you're currently in a %s/%s bisect" -msgstr "命令無效:您æ£è™•於二分æœå°‹ %s/%s 的狀態" +msgstr "命令無效:您æ£è™•於採用 %s/%s 術語的二分æœå°‹" #: builtin/bisect.c #, c-format @@ -3036,7 +3047,7 @@ msgstr "「 ã€ä¸æ˜¯æœ‰æ•ˆè¡“語" #: builtin/bisect.c #, c-format msgid "unrecognized option: '%s'" -msgstr "ä¸èªè˜é¸é …:「%sã€" +msgstr "無法辨è˜é¸é …:「%sã€" #: builtin/bisect.c #, c-format @@ -3129,7 +3140,7 @@ msgstr "好的修訂版回傳å½é€ 的錯誤碼 %d" #: builtin/bisect.c #, c-format msgid "bisect run failed: exit code %d from %s is < 0 or >= 128" -msgstr "二分æœå°‹åŸ·è¡Œå¤±æ•—:%2$s å›žå‚³çš„çµæŸä»£ç¢¼ %1$d å°æ–¼ 0 或大於 128" +msgstr "二分æœå°‹åŸ·è¡Œå¤±æ•—:%2$s çš„çµæŸä»£ç¢¼ %1$d å°æ–¼ 0 或大於 128" #: builtin/bisect.c #, c-format @@ -3316,11 +3327,11 @@ msgstr "score" #: builtin/blame.c msgid "find line copies within and across files" -msgstr "找到檔案內åŠè·¨æª”案的列拷è²å‹•作" +msgstr "尋找檔案內åŠè·¨æª”案拷è²çš„列" #: builtin/blame.c msgid "find line movements within and across files" -msgstr "找到檔案內åŠè·¨æª”案的列移動動作" +msgstr "尋找檔案內åŠè·¨æª”案移動的列" #: builtin/blame.c msgid "range" @@ -3399,7 +3410,7 @@ msgid "" "deleting branch '%s' that has been merged to\n" " '%s', but not yet merged to HEAD" msgstr "" -"å°‡è¦åˆªé™¤çš„分支「%sã€å·²ç¶“被åˆä½µåˆ°\n" +"è¦åˆªé™¤çš„分支「%sã€å·²ç¶“被åˆä½µåˆ°\n" " 「%sã€ï¼Œä½†å°šæœªåˆä½µåˆ° HEAD" # è¯è€…ï¼šä¿æŒåŽŸæ›è¡Œæ ¼å¼ï¼Œåœ¨è¼¸å‡ºæ™‚ %s 的替代內容會讓å—串變長 @@ -3475,7 +3486,7 @@ msgstr "ç„¡æ³•è§£æžæ ¼å¼åŒ–å—串" #: builtin/branch.c msgid "could not resolve HEAD" -msgstr "ç„¡æ³•è§£æž HEAD" +msgstr "ç„¡æ³•è§£æž HEAD 指標" #: builtin/branch.c #, c-format @@ -3514,34 +3525,34 @@ msgstr "沒有å為「%sã€çš„分支" #: builtin/branch.c msgid "branch rename failed" -msgstr "åˆ†æ”¯é‡æ–°å‘½å失敗" +msgstr "釿–°å‘½å分支失敗" #: builtin/branch.c msgid "branch copy failed" -msgstr "分支拷è²å¤±æ•—" +msgstr "æ‹·è²åˆ†æ”¯å¤±æ•—" #: builtin/branch.c #, c-format msgid "created a copy of a misnamed branch '%s'" -msgstr "已為誤命å的分支「%sã€å»ºç«‹æ‹·è²" +msgstr "已為誤命å的分支「%sã€å»ºç«‹è¤‡æœ¬" #: builtin/branch.c #, c-format msgid "renamed a misnamed branch '%s' away" -msgstr "已更改誤命å的分支「%sã€çš„å稱" +msgstr "已將誤命å的分支「%sã€é‡æ–°å‘½å" #: builtin/branch.c #, c-format msgid "branch renamed to %s, but HEAD is not updated" -msgstr "åˆ†æ”¯å·²é‡æ–°å‘½å為 %s,但 HEAD 指標尚未更新" +msgstr "åˆ†æ”¯å·²é‡æ–°å‘½å為 %s,但 HEAD 指標並未更新" #: builtin/branch.c msgid "branch is renamed, but update of config-file failed" -msgstr "åˆ†æ”¯å·²é‡æ–°å‘½å,但無法更新組態檔案" +msgstr "åˆ†æ”¯å·²é‡æ–°å‘½å,但組態檔案更新失敗" #: builtin/branch.c msgid "branch is copied, but update of config-file failed" -msgstr "分支已拷è²ï¼Œä½†ç„¡æ³•更新組態檔案" +msgstr "分支已拷è²ï¼Œä½†çµ„態檔案更新失敗" #: builtin/branch.c #, c-format @@ -3550,9 +3561,9 @@ msgid "" " %s\n" "Lines starting with '%s' will be stripped.\n" msgstr "" -"請編輯下述分支的æè¿°\n" +"請編輯以下分支的æè¿°\n" " %s\n" -"é–‹é æ˜¯ã€Œ%sã€çš„列將被刪除。\n" +"é–‹é æ˜¯ã€Œ%sã€çš„列將被濾除。\n" #: builtin/branch.c msgid "Generic options" @@ -3628,7 +3639,7 @@ msgstr "å³ä½¿ç›®æ¨™å·²å˜åœ¨ï¼Œä»ç§»å‹•æˆ–é‡æ–°å‘½å分支" #: builtin/branch.c builtin/for-each-ref.c builtin/tag.c msgid "do not output a newline after empty formatted refs" -msgstr "åœ¨æ ¼å¼åŒ–çµæžœç‚ºç©ºçš„引用之後,ä¸è¦è¼¸å‡ºæ›åˆ—符號" +msgstr "åœ¨æ ¼å¼åŒ–為空å—串的引用之後ä¸è¼¸å‡ºæ›åˆ—符號" #: builtin/branch.c msgid "copy a branch and its reflog" @@ -3724,11 +3735,11 @@ msgstr "無法編輯超éŽä¸€å€‹åˆ†æ”¯çš„æè¿°" #: builtin/branch.c msgid "cannot copy the current branch while not on any" -msgstr "ä¸åœ¨ä»»ä½•分支上,無法拷è²ç›®å‰åˆ†æ”¯" +msgstr "ä¸åœ¨ä»»ä½•分支上,ä¸èƒ½æ‹·è²ç›®å‰åˆ†æ”¯" #: builtin/branch.c msgid "cannot rename the current branch while not on any" -msgstr "ä¸åœ¨ä»»ä½•åˆ†æ”¯ä¸Šï¼Œç„¡æ³•é‡æ–°å‘½åç›®å‰åˆ†æ”¯" +msgstr "ä¸åœ¨ä»»ä½•分支上,ä¸èƒ½é‡æ–°å‘½åç›®å‰åˆ†æ”¯" #: builtin/branch.c msgid "too many branches for a copy operation" @@ -3746,7 +3757,7 @@ msgstr "è¦è¨å®šæ–°ä¸Šæ¸¸çš„引數太多" #, c-format msgid "" "could not set upstream of HEAD to %s when it does not point to any branch" -msgstr "無法將 HEAD 的上游è¨ç‚º %s:其未指å‘任何分支" +msgstr "HEAD 䏿Œ‡å‘任何分支時無法將其上游è¨ç‚º %s" #: builtin/branch.c #, c-format @@ -3764,7 +3775,7 @@ msgstr "è¦å–消è¨å®šä¸Šæ¸¸çš„引數太多" #: builtin/branch.c msgid "could not unset upstream of HEAD when it does not point to any branch" -msgstr "ç„¡æ³•å–æ¶ˆè¨å®š HEAD 的上游:其未指å‘任何分支" +msgstr "HEAD 䏿Œ‡å‘ä»»ä½•åˆ†æ”¯æ™‚ç„¡æ³•å–æ¶ˆè¨å®šå…¶ä¸Šæ¸¸" #: builtin/branch.c #, c-format @@ -3777,7 +3788,7 @@ msgid "" "Did you mean to use: -a|-r --list <pattern>?" msgstr "" "「git branchã€çš„ -a å’Œ -r é¸é …ä¸å–分支å稱。\n" -"您是想使用:-a|-r --list <pattern> 嗎?" +"您是想使用:-a|-r --list <模å¼> 嗎?" #: builtin/branch.c msgid "" @@ -3846,8 +3857,8 @@ msgstr "" "請檢閱è‡èŸ²å ±å‘Šä¸‹æ–¹çš„剩餘部分。\n" "您å¯åˆªé™¤ä»»ä½•æ‚¨ä¸æƒ³åˆ†äº«çš„地方。\n" -#: builtin/bugreport.c builtin/commit.c builtin/fast-export.c builtin/rebase.c -#: parse-options.h +#: builtin/bugreport.c builtin/commit.c builtin/fast-export.c +#: builtin/pack-objects.c builtin/rebase.c parse-options.h msgid "mode" msgstr "mode" @@ -4003,7 +4014,6 @@ msgid "git cat-file <type> <object>" msgstr "git cat-file <type> <object>" #: builtin/cat-file.c -#| msgid "git cat-file (-e | -p) <object>" msgid "git cat-file (-e | -p | -t | -s) <object>" msgstr "git cat-file (-e | -p | -t | -s) <object>" @@ -4124,7 +4134,7 @@ msgstr "請在 (--textconv | --filters) 使用 <path>,而éžã€Œbatchã€" #: builtin/cat-file.c msgid "objects filter only supported in batch mode" -msgstr "ç‰©ä»¶éŽæ¿¾å™¨åƒ…æ”¯æ´æ‰¹æ¬¡æ¨¡å¼" +msgstr "åƒ…æ‰¹æ¬¡æ¨¡å¼æ”¯æ´ç‰©ä»¶éŽæ¿¾å™¨" #: builtin/cat-file.c #, c-format @@ -4205,7 +4215,7 @@ msgstr "<tree-ish>" #: builtin/check-attr.c msgid "which tree-ish to check attributes at" -msgstr "è¦ç”¨å“ªä¸€å€‹æ¨¹ç‹€ç‰©ä»¶æª¢æŸ¥å±¬æ€§" +msgstr "è¦æª¢æŸ¥å±¬æ€§çš„æ¨¹ç‹€ç‰©ä»¶æŒ‡ç¤ºå…ƒ" #: builtin/check-ignore.c builtin/checkout.c builtin/gc.c builtin/worktree.c msgid "suppress progress reporting" @@ -4253,7 +4263,7 @@ msgstr "亦從 stdin 讀å–è¯çµ¡åœ°å€" #: builtin/check-mailmap.c msgid "read additional mailmap entries from file" -msgstr "從檔案讀å–å…¶ä»– mailmap é …ç›®" +msgstr "從檔案讀å–é¡å¤– mailmap é …ç›®" #: builtin/check-mailmap.c msgid "blob" @@ -4411,12 +4421,12 @@ msgstr "未指定「%2$sã€æ™‚ï¼Œå¿…é ˆä½¿ç”¨ã€Œ%1$sã€" #: builtin/checkout.c #, c-format msgid "'%s' or '%s' cannot be used with %s" -msgstr "「%sã€æˆ–「%sã€ç„¡æ³•與 %s 一起使用" +msgstr "「%sã€æˆ–「%sã€ä¸èƒ½èˆ‡ %s åŒæ™‚使用" #: builtin/checkout.c #, c-format msgid "'%s', '%s', or '%s' cannot be used when checking out of a tree" -msgstr "「%sã€ã€ã€Œ%sã€æˆ–「%sã€ç„¡æ³•在簽出樹狀物件時使用" +msgstr "「%sã€ã€ã€Œ%sã€æˆ–「%sã€ä¸èƒ½åœ¨ç°½å‡ºæ¨¹ç‹€ç‰©ä»¶æ™‚使用" #: builtin/checkout.c #, c-format @@ -4427,7 +4437,7 @@ msgstr "路徑「%sã€æœªåˆä½µ" #: merge-ort.c reset.c sequencer.c tree-walk.c #, c-format msgid "unable to read tree (%s)" -msgstr "ç„¡æ³•è®€å–æ¨¹ï¼ˆ%s)" +msgstr "ç„¡æ³•è®€å–æ¨¹ç‹€ç‰©ä»¶ï¼ˆ%s)" #: builtin/checkout.c msgid "you need to resolve your current index first" @@ -4439,7 +4449,7 @@ msgid "" "cannot continue with staged changes in the following files:\n" "%s" msgstr "" -"無法繼續,下列檔案有暫å˜çš„æ›´å‹•:\n" +"無法繼續,下列檔案有暫å˜çš„變更:\n" "%s" #: builtin/checkout.c @@ -4620,53 +4630,53 @@ msgid "" "cannot switch branch while merging\n" "Consider \"git merge --quit\" or \"git worktree add\"." msgstr "" -"無法在åˆä½µæ™‚切æ›åˆ†æ”¯\n" -"請試試「git merge --quitã€æˆ–「git worktree addã€ã€‚" +"ä¸èƒ½åœ¨åˆä½µæ™‚切æ›åˆ†æ”¯\n" +"請考慮使用「git merge --quitã€æˆ–「git worktree addã€ã€‚" #: builtin/checkout.c msgid "" "cannot switch branch in the middle of an am session\n" "Consider \"git am --quit\" or \"git worktree add\"." msgstr "" -"無法在 am 工作階段期間時切æ›åˆ†æ”¯\n" -"請試試「git am --quitã€æˆ–「git worktree addã€ã€‚" +"ä¸èƒ½åœ¨ am 工作階段ä¸åˆ‡æ›åˆ†æ”¯\n" +"請考慮使用「git am --quitã€æˆ–「git worktree addã€ã€‚" #: builtin/checkout.c msgid "" "cannot switch branch while rebasing\n" "Consider \"git rebase --quit\" or \"git worktree add\"." msgstr "" -"無法在é‡å®šåŸºåº•時切æ›åˆ†æ”¯\n" -"請試試「git rebase --quitã€æˆ–「git worktree addã€ã€‚" +"ä¸èƒ½åœ¨é‡å®šåŸºåº•時切æ›åˆ†æ”¯\n" +"請考慮使用「git rebase --quitã€æˆ–「git worktree addã€ã€‚" #: builtin/checkout.c msgid "" "cannot switch branch while cherry-picking\n" "Consider \"git cherry-pick --quit\" or \"git worktree add\"." msgstr "" -"無法在æ€é¸æ™‚切æ›åˆ†æ”¯\n" -"請試試「git cherry-pick --quitã€æˆ–「git worktree addã€ã€‚" +"ä¸èƒ½åœ¨æ€é¸æ™‚切æ›åˆ†æ”¯\n" +"請考慮使用「git cherry-pick --quitã€æˆ–「git worktree addã€ã€‚" #: builtin/checkout.c msgid "" "cannot switch branch while reverting\n" "Consider \"git revert --quit\" or \"git worktree add\"." msgstr "" -"無法在還原時切æ›åˆ†æ”¯\n" -"請試試「git revert --quitã€æˆ–「git worktree addã€ã€‚" +"ä¸èƒ½åœ¨é‚„原時切æ›åˆ†æ”¯\n" +"請考慮使用「git revert --quitã€æˆ–「git worktree addã€ã€‚" #: builtin/checkout.c msgid "you are switching branch while bisecting" -msgstr "您在進行二分æœå°‹æ™‚切æ›åˆ†æ”¯" +msgstr "在進行二分æœå°‹æ™‚切æ›åˆ†æ”¯" #: builtin/checkout.c msgid "paths cannot be used with switching branches" -msgstr "路徑ä¸èƒ½èˆ‡åˆ‡æ›åˆ†æ”¯åŒæ™‚使用" +msgstr "切æ›åˆ†æ”¯æ™‚ä¸èƒ½æŒ‡å®šè·¯å¾‘" #: builtin/checkout.c #, c-format msgid "'%s' cannot be used with switching branches" -msgstr "「%sã€ä¸èƒ½èˆ‡åˆ‡æ›åˆ†æ”¯åŒæ™‚使用" +msgstr "切æ›åˆ†æ”¯æ™‚ä¸èƒ½æŒ‡å®šã€Œ%sã€" #: builtin/checkout.c #, c-format @@ -4681,12 +4691,12 @@ msgstr "「%sã€ä¸èƒ½èˆ‡ã€Œ%sã€åŒæ™‚使用" #: builtin/checkout.c #, c-format msgid "'%s' cannot take <start-point>" -msgstr "「%sã€ä¸å– <start-point>" +msgstr "「%sã€ä¸èƒ½èˆ‡ <start-point> åŒæ™‚使用" #: builtin/checkout.c #, c-format msgid "Cannot switch branch to a non-commit '%s'" -msgstr "無法將分支切æ›è‡³éžæäº¤é …目「%sã€" +msgstr "ä¸èƒ½å°‡åˆ†æ”¯åˆ‡æ›è‡³éžæäº¤çš„「%sã€" #: builtin/checkout.c msgid "missing branch or commit argument" @@ -4711,7 +4721,7 @@ msgstr "è¡çªè¼¸å‡ºé¢¨æ ¼ï¼ˆmergeã€diff3 或 zdiff3)" #: builtin/checkout.c builtin/worktree.c msgid "detach HEAD at named commit" -msgstr "自指定æäº¤åˆ†é›¢ HEAD 指標" +msgstr "使 HEAD 指標在指定æäº¤åˆ†é›¢" #: builtin/checkout.c msgid "force checkout (throw away local modifications)" @@ -4731,24 +4741,24 @@ msgstr "更新忽略的檔案(é è¨å€¼ï¼‰" #: builtin/checkout.c msgid "do not check if another worktree is using this branch" -msgstr "䏿ª¢æŸ¥å…¶ä»–å·¥ä½œå€æ˜¯å¦æ£åœ¨ä½¿ç”¨æ¤åˆ†æ”¯" +msgstr "䏿ª¢æŸ¥æ˜¯å¦æœ‰å…¶ä»–工作å€åœ¨ä½¿ç”¨æ¤åˆ†æ”¯" #: builtin/checkout.c msgid "checkout our version for unmerged files" -msgstr "å°å°šæœªåˆä½µçš„æª”案簽出我們的版本" +msgstr "簽出未åˆä½µæª”案的我方版本" #: builtin/checkout.c msgid "checkout their version for unmerged files" -msgstr "å°å°šæœªåˆä½µçš„æª”案簽出他們的版本" +msgstr "簽出未åˆä½µæª”案的他方版本" #: builtin/checkout.c msgid "do not limit pathspecs to sparse entries only" -msgstr "å°è·¯å¾‘è¦æ ¼ä¸åšç¨€ç–簽出的é™åˆ¶" +msgstr "ä¸å°‡è·¯å¾‘è¦æ ¼é™åˆ¶ç‚ºåƒ…稀ç–é …ç›®" #: builtin/checkout.c #, c-format msgid "options '-%c', '-%c', and '%s' cannot be used together" -msgstr "「-%cã€ã€ã€Œ-%cã€å’Œã€Œ%sã€é¸é …ä¸å¾—åŒæ™‚使用" +msgstr "「-%cã€ã€ã€Œ-%cã€å’Œã€Œ%sã€é¸é …ä¸èƒ½åŒæ™‚使用" #: builtin/checkout.c msgid "--track needs a branch name" @@ -4757,7 +4767,7 @@ msgstr "--track 需è¦åˆ†æ”¯å稱" #: builtin/checkout.c #, c-format msgid "missing branch name; try -%c" -msgstr "缺少分支å稱,請嘗試 -%c" +msgstr "缺少分支å稱;請嘗試 -%c" #: builtin/checkout.c #, c-format @@ -4766,25 +4776,24 @@ msgstr "ç„¡æ³•è§£æž %s" #: builtin/checkout.c msgid "invalid path specification" -msgstr "ç„¡æ•ˆçš„è·¯å¾‘è¦æ ¼" +msgstr "è·¯å¾‘è¦æ ¼ç„¡æ•ˆ" #: builtin/checkout.c #, c-format msgid "'%s' is not a commit and a branch '%s' cannot be created from it" -msgstr "「%sã€ä¸æ˜¯æäº¤ï¼Œå› æ¤ä¸èƒ½ä»¥é€™ç‚ºåŸºç¤Žå»ºç«‹ã€Œ%sã€åˆ†æ”¯" +msgstr "「%sã€ä¸æ˜¯æäº¤ï¼Œä¸èƒ½å»ºç«‹ä»¥å…¶ç‚ºåŸºåº•的分支「%sã€" #: builtin/checkout.c #, c-format msgid "git checkout: --detach does not take a path argument '%s'" -msgstr "git checkout:--detach ä¸å–路徑引數「%sã€" +msgstr "git checkout: --detach ä¸å–路徑引數「%sã€" #: builtin/checkout.c msgid "" "git checkout: --ours/--theirs, --force and --merge are incompatible when\n" "checking out of the index." msgstr "" -"git checkout:在從索引簽出時,--ours/--theirsã€--force\n" -"å’Œ --merge ä¸ç›¸å®¹ã€‚" +"git checkout: 從索引簽出時,--ours/--theirsã€--force å’Œ --merge ä¸ç›¸å®¹ã€‚" #: builtin/checkout.c msgid "you must specify path(s) to restore" @@ -4809,7 +4818,7 @@ msgstr "為新分支建立引用日誌" #: builtin/checkout.c msgid "second guess 'git checkout <no-such-branch>' (default)" -msgstr "二次猜測「git checkout <ç„¡æ¤åˆ†æ”¯>ã€ï¼ˆé è¨å€¼ï¼‰" +msgstr "接著猜測「git checkout <ä¸å˜åœ¨çš„分支>ã€ï¼ˆé è¨å€¼ï¼‰" #: builtin/checkout.c msgid "use overlay mode (default)" @@ -5074,7 +5083,7 @@ msgstr "建立連çµã€Œ%sã€å¤±æ•—" #: builtin/clone.c #, c-format msgid "failed to copy file to '%s'" -msgstr "複製檔案至「%sã€å¤±æ•—" +msgstr "æ‹·è²æª”案至「%sã€å¤±æ•—" #: builtin/clone.c refs/files-backend.c #, c-format @@ -6255,25 +6264,27 @@ msgstr "git config list [<檔案é¸é …>] [<顯示é¸é …>] [--includes]" #: builtin/config.c msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp] [--value=<value>] [--fixed-value] [--default=<default>] <name>" +"regexp] [--value=<pattern>] [--fixed-value] [--default=<default>] [--" +"url=<url>] <name>" msgstr "" -"git config get [<檔案é¸é …>] [<顯示é¸é …>] [--includes] [--all] [--regexp] [--" -"value=<值>] [--fixed-value] [--default=<é è¨å€¼>] <å稱>" +"git config get [<file-option>] [<display-option>] [--includes] [--all] [--" +"regexp] [--value=<pattern>] [--fixed-value] [--default=<default>] [--" +"url=<url>] <name>" #: builtin/config.c msgid "" -"git config set [<file-option>] [--type=<type>] [--all] [--value=<value>] [--" -"fixed-value] <name> <value>" +"git config set [<file-option>] [--type=<type>] [--all] [--value=<pattern>] " +"[--fixed-value] <name> <value>" msgstr "" -"git config set [<檔案é¸é …>] [--type=<類型>] [--all] [--value=<值>] [--fixed-" -"value] <å稱> <值>" +"git config set [<file-option>] [--type=<type>] [--all] [--value=<pattern>] " +"[--fixed-value] <name> <value>" #: builtin/config.c msgid "" -"git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] " +"git config unset [<file-option>] [--all] [--value=<pattern>] [--fixed-value] " "<name>" msgstr "" -"git config unset [<file-option>] [--all] [--value=<value>] [--fixed-value] " +"git config unset [<file-option>] [--all] [--value=<pattern>] [--fixed-value] " "<name>" #: builtin/config.c @@ -6295,19 +6306,20 @@ msgstr "git config [<file-option>] --get-colorbool <name> [<stdout-is-tty>]" #: builtin/config.c msgid "" "git config get [<file-option>] [<display-option>] [--includes] [--all] [--" -"regexp=<regexp>] [--value=<value>] [--fixed-value] [--default=<default>] " +"regexp=<regexp>] [--value=<pattern>] [--fixed-value] [--default=<default>] " "<name>" msgstr "" -"git config get [<檔案é¸é …>] [<顯示é¸é …>] [--includes] [--all] [--regexp=<常è¦" -"表示å¼>] [--value=<值>] [--fixed-value] [--default=<é è¨å€¼>] <å稱>" +"git config get [<file-option>] [<display-option>] [--includes] [--all] [--" +"regexp=<regexp>] [--value=<pattern>] [--fixed-value] [--default=<default>] " +"<name>" #: builtin/config.c msgid "" "git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] " -"[--value=<value>] [--fixed-value] <name> <value>" +"[--value=<pattern>] [--fixed-value] <name> <value>" msgstr "" -"git config set [<檔案é¸é …>] [--type=<類型>] [--comment=<備註>] [--all] [--" -"value=<值>] [--fixed-value] <å稱> <值>" +"git config set [<file-option>] [--type=<type>] [--comment=<message>] [--all] " +"[--value=<pattern>] [--fixed-value] <name> <value>" #: builtin/config.c msgid "Config file location" @@ -6934,7 +6946,6 @@ msgid "unable to parse object id: %s" msgstr "無法解æžç‰©ä»¶ ID:%s" #: builtin/diff-pairs.c -#| msgid "git repack [<options>]" msgid "git diff-pairs -z [<diff-options>]" msgstr "git diff-pairs -z [<diff-options>]" @@ -6975,7 +6986,7 @@ msgstr "讀å–目的地路徑時é‡åˆ° EOF" #: builtin/diff-pairs.c #, c-format msgid "unable to parse rename/copy score: %s" -msgstr "無法解æžé‡æ–°å‘½å/複製分數:%s" +msgstr "無法解æžé‡æ–°å‘½å/æ‹·è²åˆ†æ•¸ï¼š%s" #: builtin/diff-pairs.c #, c-format @@ -7068,7 +7079,7 @@ msgstr "å·¥ä½œå€æª”案被留了下來。" #: builtin/difftool.c sequencer.c #, c-format msgid "could not copy '%s' to '%s'" -msgstr "無法將「%sã€è¤‡è£½åˆ°ã€Œ%sã€" +msgstr "無法將「%sã€æ‹·è²è‡³ã€Œ%sã€" #: builtin/difftool.c #, c-format @@ -7379,27 +7390,6 @@ msgid "rejected %s because shallow roots are not allowed to be updated" msgstr "已拒絕 %s,ä¸å…許更新淺複製" #: builtin/fetch.c -#, c-format -msgid "" -"some local refs could not be updated; try running\n" -" 'git remote prune %s' to remove any old, conflicting branches" -msgstr "" -"一些本機引用ä¸èƒ½è¢«æ›´æ–°ï¼›å˜—試執行\n" -" 'git remote prune %s' ä¾†åˆªé™¤èˆŠçš„ã€æœ‰è¡çªçš„分支" - -# è¯è€…ï¼šè«‹ç¶æŒå‰å°Žç©ºæ ¼ -#: builtin/fetch.c -#, c-format -msgid " (%s will become dangling)" -msgstr " (%s å°‡æˆç‚ºæ‡¸ç©ºç‹€æ…‹ï¼‰" - -# è¯è€…ï¼šè«‹ç¶æŒå‰å°Žç©ºæ ¼ -#: builtin/fetch.c -#, c-format -msgid " (%s has become dangling)" -msgstr " (%s å·²æˆç‚ºæ‡¸ç©ºç‹€æ…‹ï¼‰" - -#: builtin/fetch.c msgid "[deleted]" msgstr "[已刪除]" @@ -7422,7 +7412,7 @@ msgstr "é¸é …「%sã€çš„值「%sã€å° %s 無效" msgid "option \"%s\" is ignored for %s" msgstr "é¸é …「%sã€è¢« %s 忽略" -#: builtin/fetch.c object-store.c +#: builtin/fetch.c odb.c #, c-format msgid "%s is not a valid object" msgstr "%s 䏿˜¯ä¸€å€‹æœ‰æ•ˆçš„物件" @@ -7441,13 +7431,27 @@ msgid "" "'git config set remote.%s.followRemoteHEAD warn-if-not-branch-%s'\n" "will disable the warning until the remote changes HEAD to something else." msgstr "" -"執行「git remote set-head %s %sã€ä¾†è·Ÿé€²æ¤å·®ç•°ï¼Œæˆ–者\n" +"執行「git remote set-head %s %sã€ä¾†è¿½è¹¤æ¤å·®ç•°ï¼Œæˆ–者\n" "å¦‚æžœæ‚¨ä¸æƒ³çœ‹åˆ°é€™å‰‡è¨Šæ¯ï¼Œè«‹å°‡ã€Œremote.%s.followRemoteHEADã€çµ„æ…‹é¸é …\n" "è¨å®šæˆåˆ¥çš„值。更具體些來說,執行\n" "「git config set remote.%s.followRemoteHEAD warn-if-not-branch-%sã€\n" "會åœç”¨é€™å€‹è¦å‘Šï¼Œç›´åˆ°é 端將 HEAD 改為指å‘å…¶ä»–æ±è¥¿ã€‚" #: builtin/fetch.c +#, c-format +msgid "" +"some local refs could not be updated; try running\n" +" 'git remote prune %s' to remove any old, conflicting branches" +msgstr "" +"一些本機引用ä¸èƒ½è¢«æ›´æ–°ï¼›å˜—試執行\n" +" 'git remote prune %s' ä¾†åˆªé™¤èˆŠçš„ã€æœ‰è¡çªçš„分支" + +#: builtin/fetch.c +#, c-format +msgid "fetching ref %s failed: %s" +msgstr "å–回 %s 引用失敗:%s" + +#: builtin/fetch.c msgid "multiple branches detected, incompatible with --set-upstream" msgstr "檢測到多分支,和 --set-upstream ä¸ç›¸å®¹" @@ -7747,6 +7751,10 @@ msgid "git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]" msgstr "git for-each-ref [--contains [<æäº¤>]] [--no-contains [<æäº¤>]]" #: builtin/for-each-ref.c +msgid "git for-each-ref [--start-after <marker>]" +msgstr "git for-each-ref [--start-after <marker>]" + +#: builtin/for-each-ref.c msgid "quote placeholders suitably for shells" msgstr "引用å ä½ç¬¦é©ç”¨æ–¼ shells" @@ -7766,6 +7774,14 @@ msgstr "引用å ä½ç¬¦é©ç”¨æ–¼ Tcl" msgid "show only <n> matched refs" msgstr "åªé¡¯ç¤º <n> 個符åˆçš„引用" +#: builtin/for-each-ref.c +msgid "marker" +msgstr "標記點" + +#: builtin/for-each-ref.c +msgid "start iteration after the provided marker" +msgstr "在指定標記點後開始è¿ä»£" + #: builtin/for-each-ref.c builtin/tag.c msgid "respect format colors" msgstr "éµç…§æ ¼å¼ä¸çš„é¡è‰²è¼¸å‡º" @@ -7799,9 +7815,17 @@ msgid "also include HEAD ref and pseudorefs" msgstr "åŒ…å« HEAD 引用和å½å¼•用" #: builtin/for-each-ref.c +msgid "cannot use --start-after with custom sort options" +msgstr "無法將 --start-after 和自訂排åºé¸é …çµåˆä½¿ç”¨" + +#: builtin/for-each-ref.c msgid "unknown arguments supplied with --stdin" msgstr "為 --stdin æä¾›çš„引數未知" +#: builtin/for-each-ref.c +msgid "cannot use --start-after with patterns" +msgstr "--start-after 無法æé… pattern 使用" + #: builtin/for-each-repo.c msgid "git for-each-repo --config=<config> [--] <arguments>" msgstr "git for-each-repo --config=<config> [--] <arguments>" @@ -8040,11 +8064,6 @@ msgid "Checking ref database" msgstr "æ£åœ¨æª¢æŸ¥å¼•用資料庫" #: builtin/fsck.c -#| msgid "" -#| "git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n" -#| " [--[no-]full] [--strict] [--verbose] [--lost-found]\n" -#| " [--[no-]dangling] [--[no-]progress] [--connectivity-only]\n" -#| " [--[no-]name-objects] [<object>...]" msgid "" "git fsck [--tags] [--root] [--unreachable] [--cache] [--no-reflogs]\n" " [--[no-]full] [--strict] [--verbose] [--lost-found]\n" @@ -8309,6 +8328,10 @@ msgid "pack prefix to store a pack containing pruned objects" msgstr "å°‡å‰ç¶´æ‰“包並儲å˜ç‚ºåŒ…å«å·²å‰ªé™¤ç‰©ä»¶çš„包" #: builtin/gc.c +msgid "skip maintenance tasks typically done in the foreground" +msgstr "ç•¥éŽé€šå¸¸åœ¨å‰æ™¯å®Œæˆçš„ç¶è·ä½œæ¥" + +#: builtin/gc.c #, c-format msgid "failed to parse gc.logExpiry value %s" msgstr "ç„¡æ³•è§£æž gc.logExpiry 的值 %s" @@ -8394,13 +8417,13 @@ msgstr "ç•¥éŽå¢žé‡é‡æ–°æ‰“åŒ…å·¥ä½œï¼Œå› ç‚º core.multiPackIndex 已被åœç”¨ #: builtin/gc.c #, c-format -msgid "lock file '%s' exists, skipping maintenance" -msgstr "å·²å˜åœ¨ '%s' 鎖定檔案,略éŽç¶è·" +msgid "task '%s' failed" +msgstr "ä½œæ¥ '%s' 失敗" #: builtin/gc.c #, c-format -msgid "task '%s' failed" -msgstr "ä½œæ¥ '%s' 失敗" +msgid "lock file '%s' exists, skipping maintenance" +msgstr "å·²å˜åœ¨ '%s' 鎖定檔案,略éŽç¶è·" #: builtin/gc.c #, c-format @@ -8441,10 +8464,6 @@ msgid "run a specific task" msgstr "執行指定作æ¥" #: builtin/gc.c -msgid "use at most one of --auto and --schedule=<frequency>" -msgstr "--auto å’Œ --schedule=<é »çŽ‡> è«‹ä»»é¸ä¸€" - -#: builtin/gc.c #, c-format msgid "unable to add '%s' value of '%s'" msgstr "無法為「%2$sã€çš„å€¼åŠ ä¸Šã€Œ%1$sã€" @@ -9287,7 +9306,7 @@ msgstr "無法啟動 pack-objects 來釿–°æ‰“包本機連çµ" #: builtin/index-pack.c msgid "failed to feed local object to pack-objects" -msgstr "無法將本機物件喂給 pack-objects" +msgstr "無法將本機物件餵給 pack-objects" #: builtin/index-pack.c msgid "index-pack: Expecting full hex object ID lines only from pack-objects." @@ -9540,11 +9559,6 @@ msgstr "-L<範åœ>:<檔案> å’Œ pathspec ä¸èƒ½åŒæ™‚使用" #: builtin/log.c #, c-format -msgid "Final output: %d %s\n" -msgstr "最終輸出:%d %s\n" - -#: builtin/log.c -#, c-format msgid "git show %s: bad file" msgstr "git show %s: æå£žçš„æª”案" @@ -10155,7 +10169,7 @@ msgstr "ä¿ç•™ä¸»æ—¨ä¸ä¸æ˜¯ PATCH 的方括號" #: builtin/mailinfo.c msgid "copy Message-ID to the end of commit message" -msgstr "複製 Message-ID 至æäº¤èªªæ˜Žæœ«å°¾" +msgstr "æ‹·è² Message-ID 至æäº¤èªªæ˜Žæœ«å°¾" #: builtin/mailinfo.c msgid "re-code metadata to i18n.commitEncoding" @@ -10448,6 +10462,10 @@ msgid "(synonym to --stat)" msgstr "(和 --stat åŒç¾©ï¼‰" #: builtin/merge.c builtin/pull.c +msgid "show a compact-summary at the end of the merge" +msgstr "在åˆä½µçµå°¾é¡¯ç¤ºç²¾è¦æ‘˜è¦ (compact-summary)" + +#: builtin/merge.c builtin/pull.c msgid "add (at most <n>) entries from shortlog to merge commit message" msgstr "在åˆä½µæäº¤èªªæ˜Žä¸æ–°å¢žï¼ˆæœ€å¤š <n> æ¢ï¼‰ç²¾ç°¡æäº¤è¨˜éŒ„" @@ -10777,11 +10795,6 @@ msgstr "éŒ¯èª¤ï¼šæ¨™ç±¤è¼¸å…¥æœªé€šéŽ fsck:%s" #: builtin/mktag.c #, c-format -msgid "%d (FSCK_IGNORE?) should never trigger this callback" -msgstr "%d (FSCK_IGNORE?) 䏿‡‰è§¸ç™¼é€™å€‹å›žå‘¼å‡½å¼" - -#: builtin/mktag.c -#, c-format msgid "could not read tagged object '%s'" msgstr "ç„¡æ³•è®€å–æœ‰æ¨™ç±¤çš„物件「%sã€" @@ -10873,12 +10886,10 @@ msgid "" msgstr "在 repack 期間,將較å°å°ºå¯¸çš„包檔案收集到大於æ¤å¤§å°çš„æ‰¹æ¬¡ä¸" #: builtin/mv.c -#| msgid "git mv [<options>] <source>... <destination>" msgid "git mv [-v] [-f] [-n] [-k] <source> <destination>" msgstr "git mv [-v] [-f] [-n] [-k] <source> <destination>" #: builtin/mv.c -#| msgid "git mv [<options>] <source>... <destination>" msgid "git mv [-v] [-f] [-n] [-k] <source>... <destination-directory>" msgstr "git mv [-v] [-f] [-n] [-k] <source>... <destination-directory>" @@ -11047,7 +11058,7 @@ msgstr "" #: builtin/notes.c msgid "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>" -msgstr "git notes [--ref <註解引用>] copy [-f] <來æºç‰©ä»¶> <目標物件>" +msgstr "git notes [--ref <notes-ref>] copy [-f] <from-object> <to-object>" #: builtin/notes.c msgid "" @@ -11094,11 +11105,11 @@ msgstr "git notes add [<é¸é …>] [<物件>]" #: builtin/notes.c msgid "git notes copy [<options>] <from-object> <to-object>" -msgstr "git notes copy [<é¸é …>] <來æºç‰©ä»¶> <目標物件>" +msgstr "git notes copy [<options>] <from-object> <to-object>" #: builtin/notes.c msgid "git notes copy --stdin [<from-object> <to-object>]..." -msgstr "git notes copy --stdin [<來æºç‰©ä»¶> <目標物件>]..." +msgstr "git notes copy --stdin [<from-object> <to-object>]..." #: builtin/notes.c msgid "git notes append [<options>] [<object>]" @@ -11181,7 +11192,7 @@ msgstr "ä¸èƒ½å¾žéžè³‡æ–™ç‰©ä»¶ '%s' ä¸è®€å–註解資料。" #: builtin/notes.c #, c-format msgid "failed to copy notes from '%s' to '%s'" -msgstr "從 '%s' 複製註解到 '%s' 時失敗" +msgstr "將註解從「%sã€æ‹·è²è‡³ã€Œ%sã€å¤±æ•—" #. TRANSLATORS: the first %s will be replaced by a git #. notes command: 'add', 'merge', 'remove', etc. @@ -11270,12 +11281,12 @@ msgstr "å¤ªå°‘åƒæ•¸" msgid "" "Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite " "existing notes" -msgstr "ä¸èƒ½è¤‡è£½è¨»è§£ã€‚發ç¾ç‰©ä»¶ %s å·²å˜åœ¨è¨»è§£ã€‚使用 '-f' 覆蓋ç¾å˜è¨»è§£" +msgstr "無法拷è²è¨»è§£ã€‚物件 %s 已有註解。使用「-fã€è¦†å¯«ç¾æœ‰çš„註解" #: builtin/notes.c #, c-format msgid "missing notes on source object %s. Cannot copy." -msgstr "來æºç‰©ä»¶ %s 缺少註解。ä¸èƒ½è¤‡è£½ã€‚" +msgstr "來æºç‰©ä»¶ %s 缺少註解。無法拷è²ã€‚" #: builtin/notes.c #, c-format @@ -11426,13 +11437,26 @@ msgid "unknown subcommand: `%s'" msgstr "æœªçŸ¥åæŒ‡ä»¤ï¼šã€Œ%sã€" #: builtin/pack-objects.c -msgid "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]" -msgstr "git pack-objects --stdout [<é¸é …>] [< <引用列表> | < <物件列表>]" - -#: builtin/pack-objects.c msgid "" -"git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]" -msgstr "git pack-objects [<é¸é …>] <å‰ç¶´å稱> [< <引用列表> | < <物件列表>]" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<pack-name>]\n" +" [--cruft] [--cruft-expiration=<time>]\n" +" [--stdout [--filter=<filter-spec>] | <base-name>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=<n>] [--path-walk] < <object-list>" +msgstr "" +"git pack-objects [-q | --progress | --all-progress] [--all-progress-" +"implied]\n" +" [--no-reuse-delta] [--delta-base-offset] [--non-empty]\n" +" [--local] [--incremental] [--window=<n>] [--depth=<n>]\n" +" [--revs [--unpacked | --all]] [--keep-pack=<pack-name>]\n" +" [--cruft] [--cruft-expiration=<time>]\n" +" [--stdout [--filter=<filter-spec>] | <base-name>]\n" +" [--shallow] [--keep-true-parents] [--[no-]sparse]\n" +" [--name-hash-version=<n>] [--path-walk] < <object-list>" #: builtin/pack-objects.c #, c-format @@ -11561,6 +11585,16 @@ msgid "unable to get type of object %s" msgstr "無法ç²å¾—物件 %s 類型" #: builtin/pack-objects.c +msgid "Compressing objects by path" +msgstr "æ£åœ¨æ ¹æ“šè·¯å¾‘壓縮物件" + +#: builtin/pack-objects.c +#, c-format +msgid "Path-based delta compression using up to %d thread" +msgid_plural "Path-based delta compression using up to %d threads" +msgstr[0] "使用最多 %d 個執行緒進行路徑å¼å·®ç•°å£“縮" + +#: builtin/pack-objects.c msgid "Compressing objects" msgstr "壓縮物件ä¸" @@ -11651,6 +11685,10 @@ msgid "unable to force loose object" msgstr "無法強制鬆散物件" #: builtin/pack-objects.c +msgid "failed to pack objects via path-walk" +msgstr "無法使用 path-walk å°è£ç‰©ä»¶" + +#: builtin/pack-objects.c #, c-format msgid "not a rev '%s'" msgstr "䏿˜¯ä¸€å€‹ç‰ˆæœ¬ '%s'" @@ -11795,6 +11833,10 @@ msgid "create thin packs" msgstr "建立精簡包" #: builtin/pack-objects.c +msgid "use the path-walk API to walk objects when possible" +msgstr "å¯è¡Œæ™‚使用 path-walk API 走訪物件" + +#: builtin/pack-objects.c msgid "create packs suitable for shallow fetches" msgstr "建立é©åˆæ·ºè¤‡è£½ç‰ˆæœ¬åº«å–得的包" @@ -11864,7 +11906,12 @@ msgstr "增é‡éˆæ·±åº¦ %d 太深了,強制為 %d" msgid "pack.deltaCacheLimit is too high, forcing %d" msgstr "è¨å®š pack.deltaCacheLimit 太高了,強制為 %d" -#: builtin/pack-objects.c config.c +#: builtin/pack-objects.c +#, c-format +msgid "cannot use %s with %s" +msgstr "無法將 %s 與 %s 一起使用" + +#: builtin/pack-objects.c environment.c #, c-format msgid "bad pack compression level %d" msgstr "錯誤的打包壓縮級別 %d" @@ -11882,10 +11929,6 @@ msgid "--thin cannot be used to build an indexable pack" msgstr "--thin ä¸èƒ½ç”¨æ–¼å»ºç«‹ä¸€å€‹å¯ç´¢å¼•包" #: builtin/pack-objects.c -msgid "cannot use --filter with --stdin-packs" -msgstr "無法將 --filter åŠ --stdin-packs çµåˆä½¿ç”¨" - -#: builtin/pack-objects.c msgid "cannot use internal rev list with --stdin-packs" msgstr "無法將內部版本清單與 --stdin-packs çµåˆä½¿ç”¨" @@ -11894,10 +11937,6 @@ msgid "cannot use internal rev list with --cruft" msgstr "無法é€éŽ --cruft 使用內部修訂清單" #: builtin/pack-objects.c -msgid "cannot use --stdin-packs with --cruft" -msgstr "無法將 --stdin-packs 與 --cruft 組åˆä½¿ç”¨" - -#: builtin/pack-objects.c msgid "Enumerating objects" msgstr "枚舉物件" @@ -11910,24 +11949,6 @@ msgstr "" "總共 %<PRIu32> (差異 %<PRIu32>),復用 %<PRIu32> (差異 %<PRIu32>),é‡ç”¨åŒ… " "%<PRIu32> (總共 %<PRIuMAX>)" -#: builtin/pack-redundant.c -msgid "" -"'git pack-redundant' is nominated for removal.\n" -"If you still use this command, please add an extra\n" -"option, '--i-still-use-this', on the command line\n" -"and let us know you still use it by sending an e-mail\n" -"to <git@vger.kernel.org>. Thanks.\n" -msgstr "" -"「git pack-redundantã€å·²è¢«æå準備移除。\n" -"如果您ä»åœ¨ä½¿ç”¨é€™æ¢å‘½ä»¤ï¼Œè«‹åœ¨å‘½ä»¤åˆ—å¤šåŠ ä¸€å€‹é¸é …\n" -"「--i-still-use-thisã€ï¼Œç„¶å¾Œå¯„å°é›»å信到\n" -"<git@vger.kernel.org> è®“æˆ‘å€‘çŸ¥é“æ‚¨é‚„在使用。\n" -"感è¬ã€‚\n" - -#: builtin/pack-redundant.c -msgid "refusing to run without --i-still-use-this" -msgstr "傳入 --i-still-use-this 剿‹’絕執行" - #: builtin/pack-refs.c msgid "" "git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude " @@ -13451,6 +13472,16 @@ msgid "unknown --mirror argument: %s" msgstr "未知的 --mirror 引數:%s" #: builtin/remote.c +#, c-format +msgid "remote name '%s' is a subset of existing remote '%s'" +msgstr "å為「%sã€çš„é 端是「%sã€ç¾æœ‰é 端的å集" + +#: builtin/remote.c +#, c-format +msgid "remote name '%s' is a superset of existing remote '%s'" +msgstr "å為「%sã€çš„é 端是「%sã€ç¾æœ‰é 端的超集" + +#: builtin/remote.c msgid "fetch the remote branches" msgstr "抓å–é 端的分支" @@ -13812,18 +13843,6 @@ msgstr "䏿˜¯ä¸€å€‹æœ‰æ•ˆå¼•用:%s" msgid "Could not set up %s" msgstr "無法é…ç½® %s" -# è¯è€…ï¼šè«‹ç¶æŒå‰å°Žç©ºæ ¼ -#: builtin/remote.c -#, c-format -msgid " %s will become dangling!" -msgstr " %s å°‡æˆç‚ºæ‡¸ç©ºç‹€æ…‹ï¼" - -# è¯è€…ï¼šè«‹ç¶æŒå‰å°Žç©ºæ ¼ -#: builtin/remote.c -#, c-format -msgid " %s has become dangling!" -msgstr " %s å·²æˆç‚ºæ‡¸ç©ºç‹€æ…‹ï¼" - #: builtin/remote.c #, c-format msgid "Pruning %s" @@ -13907,11 +13926,11 @@ msgstr "è©³ç´°è¼¸å‡ºï¼›å¿…é ˆç½®æ–¼åæŒ‡ä»¤ä¹‹å‰" msgid "" "git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n" -"[--write-midx] [--name-hash-version=<n>]" +"[--write-midx] [--name-hash-version=<n>] [--path-walk]" msgstr "" "git repack [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m]\n" "[--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]\n" -"[--write-midx] [--name-hash-version=<n>]" +"[--write-midx] [--name-hash-version=<n>] [--path-walk]" #: builtin/repack.c msgid "" @@ -14013,6 +14032,10 @@ msgid "" msgstr "指定è¦ç”¨ä¾†ä»¥è·¯å¾‘為相似物件分組的å稱雜湊版本" #: builtin/repack.c +msgid "pass --path-walk to git-pack-objects" +msgstr "å°‡ --path-walk 傳éžçµ¦ git-pack-objects" + +#: builtin/repack.c msgid "do not run git-update-server-info" msgstr "ä¸åŸ·è¡Œ git-update-server-info" @@ -15429,20 +15452,28 @@ msgid "git stash create [<message>]" msgstr "git stash create [<message>]" #: builtin/stash.c +msgid "git stash export (--print | --to-ref <ref>) [<stash>...]" +msgstr "git stash export (--print | --to-ref <ref>) [<stash>...]" + +#: builtin/stash.c +msgid "git stash import <commit>" +msgstr "git stash import <commit>" + +#: builtin/stash.c #, c-format msgid "'%s' is not a stash-like commit" msgstr "'%s' ä¸åƒæ˜¯ä¸€å€‹è²¯å˜æäº¤" #: builtin/stash.c +msgid "No stash entries found." +msgstr "未發ç¾è²¯å˜æ¢ç›®ã€‚" + +#: builtin/stash.c #, c-format msgid "Too many revisions specified:%s" msgstr "指定了太多的版本:%s" #: builtin/stash.c -msgid "No stash entries found." -msgstr "未發ç¾è²¯å˜æ¢ç›®ã€‚" - -#: builtin/stash.c #, c-format msgid "%s is not a valid reference" msgstr "%s 䏿˜¯ä¸€å€‹æœ‰æ•ˆçš„引用å" @@ -15637,6 +15668,86 @@ msgstr "貯å˜ä¸åŒ…嫿œªè¿½è¹¤æª”案" msgid "include ignore files" msgstr "包å«å¿½ç•¥çš„æª”案" +#: builtin/stash.c +#, c-format +msgid "cannot parse commit %s" +msgstr "ç„¡æ³•è§£æžæäº¤ %s" + +#: builtin/stash.c +#, c-format +msgid "invalid author or committer for %s" +msgstr "%s 的作者或æäº¤è€…無效" + +#: builtin/stash.c +msgid "could not write commit" +msgstr "無法寫入æäº¤" + +#: builtin/stash.c +#, c-format +msgid "not a valid revision: %s" +msgstr "䏿˜¯æœ‰æ•ˆçš„修訂版:%s" + +#: builtin/stash.c +#, c-format +msgid "not a commit: %s" +msgstr "䏿˜¯æäº¤ï¼š%s" + +#: builtin/stash.c +#, c-format +msgid "%s is not a valid exported stash commit" +msgstr "%s 䏿˜¯æœ‰æ•ˆçš„åŒ¯å‡ºè²¯å˜æäº¤" + +#: builtin/stash.c +#, c-format +msgid "found root commit %s with invalid data" +msgstr "找到 %s æ ¹æäº¤ï¼Œå…§æœ‰ç„¡æ•ˆè³‡æ–™" + +#: builtin/stash.c +#, c-format +msgid "found stash commit %s without expected prefix" +msgstr "找到 %s è²¯å˜æäº¤ï¼Œè£¡é¢ç¼ºå°‘é æœŸçš„å‰ç¶´" + +#: builtin/stash.c +#, c-format +msgid "cannot parse parents of commit: %s" +msgstr "ç„¡æ³•è§£æžæäº¤çš„çˆ¶ç‰©ä»¶ï¼š%s" + +#: builtin/stash.c +#, c-format +msgid "%s does not look like a stash commit" +msgstr "%s ä¼¼ä¹Žä¸æ˜¯è²¯å˜æäº¤" + +#: builtin/stash.c +#, c-format +msgid "cannot read commit buffer for %s" +msgstr "ç„¡æ³•è®€å– %s çš„æäº¤ç·©è¡å€" + +#: builtin/stash.c +#, c-format +msgid "cannot save the stash for %s" +msgstr "ç„¡æ³•å„²å˜ %s 的貯å˜" + +#: builtin/stash.c +msgid "unable to write base commit" +msgstr "無法寫入基礎æäº¤" + +#: builtin/stash.c +#, c-format +msgid "unable to find stash entry %s" +msgstr "找ä¸åˆ°è²¯å˜é …ç›® %s" + +#: builtin/stash.c +msgid "print the object ID instead of writing it to a ref" +msgstr "輸出物件 ID 而éžå°‡å…¶å¯«å…¥å¼•用" + +#: builtin/stash.c +msgid "save the data to the given ref" +msgstr "將資料å˜å…¥æŒ‡å®šå¼•用" + +#: builtin/stash.c +msgid "exactly one of --print and --to-ref is required" +msgstr "éœ€è¦æŒ‡å®š --print 或 --to-ref å…¶ä¸ä¸€å€‹" + #: builtin/stripspace.c msgid "skip and remove all lines starting with comment character" msgstr "ç•¥éŽå’Œç§»é™¤æ‰€æœ‰çš„備註行" @@ -15647,8 +15758,10 @@ msgstr "為æ¯ä¸€è¡Œçš„è¡Œé¦–æ–°å¢žå‚™è¨»ç¬¦å’Œç©ºæ ¼" #: builtin/submodule--helper.c #, c-format -msgid "Expecting a full ref name, got %s" -msgstr "期望一個完整的引用å稱,å»å¾—到 %s" +msgid "" +"could not look up configuration '%s'. Assuming this repository is its own " +"authoritative upstream." +msgstr "找ä¸åˆ°ã€Œ%sã€çµ„æ…‹è¨å®šã€‚å‡å®šé€™å€‹ç‰ˆæœ¬åº«æ˜¯å…¶è‡ªèº«çš„官方上游。" #: builtin/submodule--helper.c #, c-format @@ -15657,13 +15770,6 @@ msgstr "無法å–得忍¡çµ„「%sã€çš„版本庫控制代碼" #: builtin/submodule--helper.c #, c-format -msgid "" -"could not look up configuration '%s'. Assuming this repository is its own " -"authoritative upstream." -msgstr "找ä¸åˆ°ã€Œ%sã€çµ„æ…‹è¨å®šã€‚å‡å®šé€™å€‹ç‰ˆæœ¬åº«æ˜¯å…¶è‡ªèº«çš„官方上游。" - -#: builtin/submodule--helper.c -#, c-format msgid "No url found for submodule path '%s' in .gitmodules" msgstr "在 .gitmodules 䏿œªæ‰¾åˆ°å模組 '%s' çš„ url" @@ -15920,7 +16026,7 @@ msgstr "ä¸èƒ½è˜åˆ¥ submodule.alternateLocation çš„å–值 '%s'" #: builtin/submodule--helper.c submodule.c #, c-format msgid "refusing to create/use '%s' in another submodule's git dir" -msgstr "æ‹’çµ•åœ¨å…¶ä»–åæ¨¡çµ„çš„ git 路徑建立ï¼ä½¿ç”¨ã€Œ%sã€" +msgstr "æ‹’çµ•åœ¨å…¶ä»–åæ¨¡çµ„çš„ git 目錄ä¸å»ºç«‹/使用「%sã€" #: builtin/submodule--helper.c #, c-format @@ -16080,6 +16186,11 @@ msgstr "忍¡çµ„(%s)的分支è¨å®šç‚ºç¹¼æ‰¿ä¸Šç´šå°ˆæ¡ˆçš„分支,但是ä #: builtin/submodule--helper.c #, c-format +msgid "Expecting a full ref name, got %s" +msgstr "期望一個完整的引用å稱,å»å¾—到 %s" + +#: builtin/submodule--helper.c +#, c-format msgid "Unable to find current revision in submodule path '%s'" msgstr "ç„¡æ³•åœ¨åæ¨¡çµ„路徑「%sã€ä¸å°‹æ‰¾ç›®å‰çš„修訂版本" @@ -16325,6 +16436,11 @@ msgstr "版本庫 URL:「%sã€å¿…é ˆæ˜¯çµ•å°è·¯å¾‘ï¼Œæˆ–é–‹é æ˜¯ ./|../" #: builtin/submodule--helper.c #, c-format +msgid "submodule name '%s' already used for path '%s'" +msgstr "「%sã€å模組å稱已被「%sã€è·¯å¾‘使用" + +#: builtin/submodule--helper.c +#, c-format msgid "'%s' is not a valid submodule name" msgstr "「%sã€ä¸æ˜¯æœ‰æ•ˆçš„忍¡çµ„å稱" @@ -16856,7 +16972,6 @@ msgid "git update-ref [<options>] <refname> <new-oid> [<old-oid>]" msgstr "git update-ref [<options>] <refname> <new-oid> [<old-oid>]" #: builtin/update-ref.c -#| msgid "git update-ref [<options>] --stdin [-z]" msgid "git update-ref [<options>] --stdin [-z] [--batch-updates]" msgstr "git update-ref [<options>] --stdin [-z] [--batch-updates]" @@ -17054,12 +17169,12 @@ msgstr "" #: builtin/worktree.c #, c-format msgid "failed to copy '%s' to '%s'; sparse-checkout may not work correctly" -msgstr "無法將「%sã€è¤‡è£½åˆ°ã€Œ%sã€ï¼›ç¨€ç–簽出å¯èƒ½ç„¡æ³•æ£å¸¸é‹ä½œ" +msgstr "將「%sã€æ‹·è²è‡³ã€Œ%sã€å¤±æ•—;稀ç–簽出å¯èƒ½ç„¡æ³•æ£å¸¸é‹ä½œ" #: builtin/worktree.c #, c-format msgid "failed to copy worktree config from '%s' to '%s'" -msgstr "無法將工作å€çµ„態從「%sã€è¤‡è£½åˆ°ã€Œ%sã€" +msgstr "將工作å€çµ„態從「%sã€æ‹·è²è‡³ã€Œ%sã€å¤±æ•—" #: builtin/worktree.c #, c-format @@ -17097,11 +17212,6 @@ msgstr "準備工作å€ï¼ˆç°½å‡º '%s')" #: builtin/worktree.c #, c-format -msgid "unreachable: invalid reference: %s" -msgstr "ä¸å¯é”:無效引用:%s" - -#: builtin/worktree.c -#, c-format msgid "Preparing worktree (detached HEAD %s)" msgstr "準備工作å€ï¼ˆåˆ†é›¢é–‹é 指標 %s)" @@ -17629,7 +17739,7 @@ msgstr "切æ›åˆ†æ”¯æˆ–å¾©åŽŸå·¥ä½œå€æª”案" #: command-list.h msgid "Copy files from the index to the working tree" -msgstr "從索引複製檔案到工作å€" +msgstr "å¾žç´¢å¼•å€æ‹·è²æª”案至工作å€" #: command-list.h msgid "Find commits yet to be applied to upstream" @@ -18804,7 +18914,7 @@ msgstr "[GLE %ld] 無法å–得「%lsã€çš„通訊å”定資訊" #: compat/mingw.c #, c-format msgid "failed to copy SID (%ld)" -msgstr "無法複製 SID (%ld)" +msgstr "æ‹·è² SID 失敗(%ld)" #: compat/mingw.c #, c-format @@ -19113,16 +19223,6 @@ msgstr "在 %3$s ä¸è¨å®šè®Šæ•¸ '%2$s' 錯誤的å–值 '%1$s':%4$s" #: config.c #, c-format -msgid "invalid value for variable %s" -msgstr "%s 變數的值無效" - -#: config.c -#, c-format -msgid "ignoring unknown core.fsync component '%s'" -msgstr "忽略未知的 core.fsync 元件「%sã€" - -#: config.c -#, c-format msgid "bad boolean config value '%s' for '%s'" msgstr "「%2$sã€çš„「%1$sã€å¸ƒæž—è¨å®šå€¼ç„¡æ•ˆ" @@ -19138,54 +19238,6 @@ msgstr "'%2$s' 的值 '%1$s' 䏿˜¯ä¸€å€‹æœ‰æ•ˆçš„æ™‚間戳" #: config.c #, c-format -msgid "abbrev length out of range: %d" -msgstr "縮寫長度超出範åœï¼š%d" - -#: config.c -#, c-format -msgid "bad zlib compression level %d" -msgstr "錯誤的 zlib 壓縮級別 %d" - -#: config.c -#, c-format -msgid "%s cannot contain newline" -msgstr "%s ä¸èƒ½åŒ…嫿›è¡Œç¬¦è™Ÿ" - -#: config.c -#, c-format -msgid "%s must have at least one character" -msgstr "%s 得有至少 1 個å—å…ƒ" - -#: config.c -#, c-format -msgid "ignoring unknown core.fsyncMethod value '%s'" -msgstr "忽略未知的 core.fsyncMethod 值「%sã€" - -#: config.c -msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" -msgstr "core.fsyncObjectFiles 已棄用。請改用 core.fsync" - -#: config.c -#, c-format -msgid "invalid mode for object creation: %s" -msgstr "無效的物件建立模å¼ï¼š%s" - -#: config.c -#, c-format -msgid "malformed value for %s" -msgstr "%s çš„å–å€¼æ ¼å¼éŒ¯èª¤" - -#: config.c -#, c-format -msgid "malformed value for %s: %s" -msgstr "%s çš„å–å€¼æ ¼å¼éŒ¯èª¤ï¼š%s" - -#: config.c -msgid "must be one of nothing, matching, simple, upstream or current" -msgstr "å¿…é ˆæ˜¯å…¶ä¸ä¹‹ä¸€ï¼šnothingã€matchingã€simpleã€upstream 或 current" - -#: config.c -#, c-format msgid "unable to load config blob object '%s'" msgstr "無法從資料物件 '%s' 載入è¨å®š" @@ -19509,7 +19561,7 @@ msgstr "%s ä¸çš„ CRLF 將被 LF å–代" msgid "" "in the working copy of '%s', CRLF will be replaced by LF the next time Git " "touches it" -msgstr "在「%sã€çš„工作複本ä¸ï¼Œä¸‹æ¬¡ Git 接觸到時會用 LF å–代 CRLF" +msgstr "下次 Git å˜å–「%sã€çš„工作複本時會以 LF å–代 CRLF" #: convert.c #, c-format @@ -19521,7 +19573,7 @@ msgstr "檔案 %s ä¸çš„ LF 將被 CRLF å–代" msgid "" "in the working copy of '%s', LF will be replaced by CRLF the next time Git " "touches it" -msgstr "在「%sã€çš„工作複本ä¸ï¼Œä¸‹æ¬¡ Git 接觸到時會用 CRLF å–代 LF" +msgstr "下次 Git å˜å–「%sã€çš„工作複本時會以 CRLF å–代 LF" #: convert.c #, c-format @@ -19797,8 +19849,8 @@ msgid "cannot compare a named pipe to a directory" msgstr "無法比å°å‘½å管線 (pipe) 和目錄" #: diff-no-index.c -msgid "git diff --no-index [<options>] <path> <path>" -msgstr "git diff --no-index [<é¸é …>] <路徑> <路徑>" +msgid "git diff --no-index [<options>] <path> <path> [<pathspec>...]" +msgstr "git diff --no-index [<options>] <path> <path> [<pathspec>...]" #: diff-no-index.c msgid "" @@ -19806,6 +19858,12 @@ msgid "" "tree" msgstr "䏿˜¯ä¸€å€‹ git 版本庫。使用 --no-index 比較工作å€ä¹‹å¤–的兩個路徑" +#: diff-no-index.c +msgid "" +"Limiting comparison with pathspecs is only supported if both paths are " +"directories." +msgstr "åªç•¶å…©å€‹è·¯å¾‘éƒ½æ˜¯ç›®éŒ„æ™‚ï¼Œæ‰æ”¯æ´ä½¿ç”¨è·¯å¾‘è¦æ ¼é™åˆ¶æ¯”è¼ƒé …ç›®ã€‚" + # è¯è€…ï¼šè«‹ç¶æŒå‰å°Žç©ºæ ¼ #: diff.c #, c-format @@ -19984,7 +20042,7 @@ msgstr "生æˆä¿®è£œæª”" msgid "<n>" msgstr "<n>" -#: diff.c +#: diff.c parse-options.h msgid "generate diffs with <n> lines context" msgstr "生æˆå« <n> 行上下文的差異" @@ -20131,7 +20189,7 @@ msgstr "ä¸é¡¯ç¤ºä»»ä½•來æºå’Œç›®çš„地å‰ç¶´" msgid "use default prefixes a/ and b/" msgstr "使用é è¨çš„å‰ç½®å稱 a/ å’Œ b/" -#: diff.c +#: diff.c parse-options.h msgid "show context between diff hunks up to the specified number of lines" msgstr "顯示指定行數的差異å€å¡Šé–“的上下文" @@ -20173,11 +20231,11 @@ msgstr "çœç•¥åˆªé™¤è®Šæ›´çš„差異輸出" #: diff.c msgid "detect copies" -msgstr "檢測複製" +msgstr "檢測拷è²" #: diff.c msgid "use unmodified files as source to find copies" -msgstr "使用未修改的檔案åšç‚ºç™¼ç¾æ‹·è²çš„來æº" +msgstr "使用未修改的檔案作為尋找拷è²çš„來æº" #: diff.c msgid "disable rename detection" @@ -20195,7 +20253,7 @@ msgstr "ç¹¼çºŒåˆ—å‡ºæª”æ¡ˆé‡æ–°å‘½å以外的æ·å²è¨˜éŒ„" msgid "" "prevent rename/copy detection if the number of rename/copy targets exceeds " "given limit" -msgstr "å¦‚æžœé‡æ–°å‘½å/è¤‡è£½ç›®æ¨™è¶…éŽæä¾›çš„é™åˆ¶ï¼Œç¦æ¢é‡æ–°å‘½å/複製檢測" +msgstr "å¦‚æžœé‡æ–°å‘½å/æ‹·è²çš„目標數é‡è¶…出æä¾›çš„é™åº¦ï¼Œå‰‡é˜²æ¢æª¢æ¸¬é‡æ–°å‘½å/æ‹·è²" #: diff.c msgid "Diff algorithm options" @@ -20405,7 +20463,7 @@ msgstr "å› ç‚ºæª”æ¡ˆå¤ªå¤šï¼Œå·²ç•¥éŽè©³ç´°é‡æ–°å‘½å嵿¸¬ã€‚" #: diff.c msgid "only found copies from modified paths due to too many files." -msgstr "å› ç‚ºæª”æ¡ˆå¤ªå¤šï¼Œåªåœ¨ä¿®æ”¹çš„路徑ä¸å°‹æ‰¾è¤‡è£½ã€‚" +msgstr "å› ç‚ºæª”æ¡ˆå¤ªå¤šï¼Œåªåœ¨æœ‰ä¿®æ”¹çš„路徑ä¸å°‹æ‰¾äº†è¤‡æœ¬ã€‚" #: diff.c #, c-format @@ -20516,6 +20574,64 @@ msgstr "無法 stat 檔案「%sã€" msgid "bad git namespace path \"%s\"" msgstr "錯誤的 git åå—空間路徑 \"%s\"" +#: environment.c +#, c-format +msgid "invalid value for variable %s" +msgstr "%s 變數的值無效" + +#: environment.c +#, c-format +msgid "ignoring unknown core.fsync component '%s'" +msgstr "忽略未知的 core.fsync 元件「%sã€" + +#: environment.c +#, c-format +msgid "abbrev length out of range: %d" +msgstr "縮寫長度超出範åœï¼š%d" + +#: environment.c +#, c-format +msgid "bad zlib compression level %d" +msgstr "錯誤的 zlib 壓縮級別 %d" + +#: environment.c +#, c-format +msgid "%s cannot contain newline" +msgstr "%s ä¸èƒ½åŒ…嫿›è¡Œç¬¦è™Ÿ" + +#: environment.c +#, c-format +msgid "%s must have at least one character" +msgstr "%s 得有至少 1 個å—å…ƒ" + +#: environment.c +#, c-format +msgid "ignoring unknown core.fsyncMethod value '%s'" +msgstr "忽略未知的 core.fsyncMethod 值「%sã€" + +#: environment.c +msgid "core.fsyncObjectFiles is deprecated; use core.fsync instead" +msgstr "core.fsyncObjectFiles 已棄用。請改用 core.fsync" + +#: environment.c +#, c-format +msgid "invalid mode for object creation: %s" +msgstr "無效的物件建立模å¼ï¼š%s" + +#: environment.c +#, c-format +msgid "malformed value for %s" +msgstr "%s çš„å–å€¼æ ¼å¼éŒ¯èª¤" + +#: environment.c +#, c-format +msgid "malformed value for %s: %s" +msgstr "%s çš„å–å€¼æ ¼å¼éŒ¯èª¤ï¼š%s" + +#: environment.c +msgid "must be one of nothing, matching, simple, upstream or current" +msgstr "å¿…é ˆæ˜¯å…¶ä¸ä¹‹ä¸€ï¼šnothingã€matchingã€simpleã€upstream 或 current" + #: exec-cmd.c #, c-format msgid "too many args to run %s" @@ -21372,6 +21488,35 @@ msgstr "ä¸å…許空的姓åï¼ˆå°æ–¼ <%s>)" msgid "name consists only of disallowed characters: %s" msgstr "å§“åä¸åƒ…包å«åœç”¨å—元:%s" +#: imap-send.c +msgid "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>" +msgstr "" +"git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>" + +#: imap-send.c +msgid "no IMAP host specified" +msgstr "沒有指定 IMAP 主機" + +#: imap-send.c +msgid "" +"set the IMAP host with 'git config imap.host <host>'.\n" +"(e.g., 'git config imap.host imaps://imap.example.com')" +msgstr "" +"使用「git config imap.host <主機>ã€ä¾†è¨å®š IMAP 主機。\n" +"(比如「git config imap.host imaps://imap.example.comã€ï¼‰" + +#: imap-send.c +msgid "no IMAP folder specified" +msgstr "沒有指定 IMAP 資料夾" + +#: imap-send.c +msgid "" +"set the target folder with 'git config imap.folder <folder>'.\n" +"(e.g., 'git config imap.folder Drafts')" +msgstr "" +"使用「git config imap.folder <資料夾>ã€æŒ‡å®šç›®çš„地資料夾。\n" +"(比如「git config imap.folder Draftsã€ï¼‰" + #: list-objects-filter-options.c msgid "expected 'tree:<depth>'" msgstr "期望 'tree:<深度>'" @@ -22511,113 +22656,113 @@ msgstr "éœ€è¦æŒ‡å®š <object>:<path>,å»åªæŒ‡å®š <object>「%sã€" msgid "invalid object name '%.*s'." msgstr "'%.*s' 物件å稱無效。" -#: object-store.c +#: object.c +#, c-format +msgid "invalid object type \"%s\"" +msgstr "無效的物件類型 \"%s\"" + +#: object.c +#, c-format +msgid "object %s is a %s, not a %s" +msgstr "物件 %s 是一個 %sï¼Œä¸æ˜¯ä¸€å€‹ %s" + +#: object.c +#, c-format +msgid "object %s has unknown type id %d" +msgstr "物件 %s 有未知的類型 id %d" + +#: object.c +#, c-format +msgid "unable to parse object: %s" +msgstr "ä¸èƒ½è§£æžç‰©ä»¶ï¼š%s" + +#: object.c +#, c-format +msgid "hash mismatch %s" +msgstr "雜湊值與 %s ä¸ç¬¦åˆ" + +#: odb.c #, c-format msgid "object directory %s does not exist; check .git/objects/info/alternates" msgstr "物件目錄 %s ä¸å˜åœ¨ï¼Œæª¢æŸ¥ .git/objects/info/alternates" -#: object-store.c +#: odb.c #, c-format msgid "unable to normalize alternate object path: %s" msgstr "無法è¦ç¯„化備用物件路徑:%s" -#: object-store.c +#: odb.c #, c-format msgid "%s: ignoring alternate object stores, nesting too deep" msgstr "%s:忽略備用物件庫,嵌套太深" -#: object-store.c +#: odb.c msgid "unable to fdopen alternates lockfile" msgstr "無法 fdopen å–代鎖檔案" -#: object-store.c +#: odb.c msgid "unable to read alternates file" msgstr "ç„¡æ³•è®€å–æ›¿ä»£æª”案" -#: object-store.c +#: odb.c msgid "unable to move new alternates file into place" msgstr "無法將新的替代檔案移動到ä½" -#: object-store.c +#: odb.c #, c-format msgid "path '%s' does not exist" msgstr "路徑 '%s' ä¸å˜åœ¨" -#: object-store.c +#: odb.c #, c-format msgid "reference repository '%s' as a linked checkout is not supported yet." msgstr "å°šä¸æ”¯æ´å°‡å¼•用版本庫 '%s' 作為一個連çµç°½å‡ºã€‚" -#: object-store.c +#: odb.c #, c-format msgid "reference repository '%s' is not a local repository." msgstr "引用版本庫 '%s' 䏿˜¯ä¸€å€‹æœ¬æ©Ÿç‰ˆæœ¬åº«ã€‚" -#: object-store.c +#: odb.c #, c-format msgid "reference repository '%s' is shallow" msgstr "引用版本庫 '%s' 是一個淺複製" -#: object-store.c +#: odb.c #, c-format msgid "reference repository '%s' is grafted" msgstr "引用版本庫 '%s' 已被移æ¤" -#: object-store.c +#: odb.c #, c-format msgid "could not find object directory matching %s" msgstr "找ä¸åˆ°ç¬¦åˆ %s 的物件目錄" -#: object-store.c +#: odb.c #, c-format msgid "invalid line while parsing alternate refs: %s" msgstr "è§£æžå‚™ç”¨å¼•用時無效的行:%s" -#: object-store.c +#: odb.c #, c-format msgid "replacement %s not found for %s" msgstr "找ä¸åˆ° %2$s 的替代 %1$s" -#: object-store.c +#: odb.c #, c-format msgid "packed object %s (stored in %s) is corrupt" msgstr "打包物件 %s(儲å˜åœ¨ %s)已æå£ž" -#: object-store.c +#: odb.c #, c-format msgid "missing mapping of %s to %s" msgstr "缺少 %s 到 %s çš„æ˜ å°„" -#: object-store.c +#: odb.c #, c-format msgid "%s is not a valid '%s' object" msgstr "%s 䏿˜¯ä¸€å€‹æœ‰æ•ˆçš„ '%s' 物件" -#: object.c -#, c-format -msgid "invalid object type \"%s\"" -msgstr "無效的物件類型 \"%s\"" - -#: object.c -#, c-format -msgid "object %s is a %s, not a %s" -msgstr "物件 %s 是一個 %sï¼Œä¸æ˜¯ä¸€å€‹ %s" - -#: object.c -#, c-format -msgid "object %s has unknown type id %d" -msgstr "物件 %s 有未知的類型 id %d" - -#: object.c -#, c-format -msgid "unable to parse object: %s" -msgstr "ä¸èƒ½è§£æžç‰©ä»¶ï¼š%s" - -#: object.c -#, c-format -msgid "hash mismatch %s" -msgstr "雜湊值與 %s ä¸ç¬¦åˆ" - #: pack-bitmap-write.c #, c-format msgid "duplicate entry when writing bitmap index: %s" @@ -22632,10 +22777,6 @@ msgstr "å˜—è©¦å„²å˜æœªé¸å–çš„æäº¤ï¼šã€Œ%sã€" msgid "too many pseudo-merges" msgstr "å½åˆä½µéŽå¤š" -#: pack-bitmap-write.c -msgid "trying to write commit not in index" -msgstr "嘗試寫入ä¸åœ¨ç´¢å¼•çš„æäº¤" - #: pack-bitmap.c msgid "failed to load bitmap index (corrupted?)" msgstr "無法載入ä½åœ–索引(æå£žï¼Ÿï¼‰" @@ -22940,6 +23081,11 @@ msgstr "%s ä¸å¯ç”¨" #: parse-options.c #, c-format +msgid "value for %s exceeds %<PRIdMAX>" +msgstr "%s çš„å€¼è¶…éŽ %<PRIdMAX>" + +#: parse-options.c +#, c-format msgid "value %s for %s not in range [%<PRIdMAX>,%<PRIdMAX>]" msgstr "%2$s 的數值 %1$s ä¸åœ¨ [%3$<PRIdMAX>,%4$<PRIdMAX>] 範åœå…§" @@ -24091,6 +24237,18 @@ msgstr "沒有 '%s' 的引用日誌" msgid "%s does not point to a valid object!" msgstr "%s 沒有指å‘一個有效的物件ï¼" +# è¯è€…ï¼šè«‹ç¶æŒå‰å°Žç©ºæ ¼ +#: refs.c +#, c-format +msgid "%s%s will become dangling after %s is deleted\n" +msgstr "在刪除 %3$s 之後,%1$s%2$s 將進入懸空狀態\n" + +# è¯è€…ï¼šè«‹ç¶æŒå‰å°Žç©ºæ ¼ +#: refs.c +#, c-format +msgid "%s%s has become dangling after %s was deleted\n" +msgstr "在刪除 %3$s 後,%1$s%2$s 已經是懸空狀態\n" + #: refs.c #, c-format msgid "" @@ -24324,7 +24482,7 @@ msgstr "找ä¸åˆ°å¼•用å稱 %s" #: refs/reftable-backend.c #, c-format msgid "refname %s is a symbolic ref, copying it is not supported" -msgstr "引用å稱 %s æ˜¯ç¬¦è™Ÿå¼•ç”¨ï¼Œä¸æ”¯æ´è¤‡è£½" +msgstr "引用å稱 %s 是象徵å¼å¼•ç”¨ï¼Œä¸æ”¯æ´æ‹·è²" #: refspec.c #, c-format @@ -25047,9 +25205,6 @@ msgid "specify if background maintenance should be enabled" msgstr "指定是å¦è¦å•Ÿç”¨èƒŒæ™¯ç¶è·æ¨¡å¼" #: scalar.c -#| msgid "" -#| "scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n" -#| "\t[--[no-]src] [--[no-]tags] <url> [<enlistment>]" msgid "" "scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n" "\t[--[no-]src] [--[no-]tags] [--[no-]maintenance] <url> [<enlistment>]" @@ -25104,7 +25259,6 @@ msgid "`scalar list` does not take arguments" msgstr "`scalar list` 未å–引數" #: scalar.c -#| msgid "scalar register [<enlistment>]" msgid "scalar register [--[no-]maintenance] [<enlistment>]" msgstr "scalar register [--[no-]maintenance] [<enlistment>]" @@ -25113,7 +25267,6 @@ msgid "reconfigure all registered enlistments" msgstr "釿–°è¨å®šæ‰€æœ‰è¨»å†Šçš„編列åå–®" #: scalar.c -#| msgid "enable/disable untracked cache" msgid "(enable|disable|keep)" msgstr "(enable|disable|keep)" @@ -25122,7 +25275,6 @@ msgid "signal how to adjust background maintenance" msgstr "指示調整背景ç¶è·æ¨¡å¼çš„æ–¹å¼" #: scalar.c -#| msgid "scalar reconfigure [--all | <enlistment>]" msgid "" "scalar reconfigure [--maintenance=(enable|disable|keep)] [--all | " "<enlistment>]" @@ -25137,7 +25289,7 @@ msgstr "--all 或 <enlistment> 但ä¸èƒ½å‚³å…¥å…©è€…" #: scalar.c #, c-format msgid "unknown mode for --maintenance option: %s" -msgstr "--maintenance é¸é …的值無效:%s" +msgstr "æä¾›çš„ --maintenance 模å¼ç„¡æ•ˆï¼š%s" #: scalar.c #, c-format @@ -25534,7 +25686,7 @@ msgstr "建立æäº¤å¾Œï¼Œä¸èƒ½è§£æž HEAD" #: sequencer.c msgid "detached HEAD" -msgstr "分離 HEAD" +msgstr "分離 HEAD 指標" # è¯è€…ï¼šä¸æ–‡å—串拼接,å¯åˆªé™¤å‰å°Žç©ºæ ¼ #: sequencer.c @@ -26460,7 +26612,7 @@ msgstr "ä¸èƒ½è‡ª '%s' 到 '%s' 建立符號連çµ" #: setup.c #, c-format msgid "cannot copy '%s' to '%s'" -msgstr "ä¸èƒ½è¤‡è£½ '%s' 至 '%s'" +msgstr "無法將「%sã€æ‹·è²è‡³ã€Œ%sã€" #: setup.c #, c-format @@ -26475,7 +26627,7 @@ msgstr "沒有在 %s 䏿‰¾åˆ°ç¯„本" #: setup.c #, c-format msgid "not copying templates from '%s': %s" -msgstr "沒有從 '%s' 複製範本:%s" +msgstr "未從「%sã€æ‹·è²ç¯„本:%s" #: setup.c #, c-format @@ -26877,8 +27029,12 @@ msgid "toggle pruning of uninteresting paths" msgstr "åˆ‡æ›æ˜¯å¦å‰ªé™¤ä¸é‡è¦è·¯å¾‘" #: t/helper/test-path-walk.c +msgid "toggle aggressive edge walk" +msgstr "åˆ‡æ›æ˜¯å¦æ›´æ¿€é€²åœ°èµ°è¨ªé‚Š" + +#: t/helper/test-path-walk.c msgid "read a pattern list over stdin" -msgstr "å¾žæ¨™æº–è¼¸å…¥è®€å–æ¨¡å¼æ¸…å–®" +msgstr "從 stdin è®€å–æ¨¡å¼æ¸…å–®" #: t/helper/test-reach.c #, c-format @@ -27184,7 +27340,7 @@ msgstr "%s 執行緒ç‰å¾…失敗:%s" #: transport-helper.c #, c-format msgid "can't start thread for copying data: %s" -msgstr "ä¸èƒ½å•Ÿå‹•執行緒來複製資料:%s" +msgstr "無法啟動執行緒來拷è²è³‡æ–™: %s" #: transport-helper.c #, c-format @@ -27198,7 +27354,7 @@ msgstr "%s 進程失敗" #: transport-helper.c msgid "can't start thread for copying data" -msgstr "ä¸èƒ½å•Ÿå‹•執行緒來複製資料" +msgstr "無法啟動執行緒來拷è²è³‡æ–™" #: transport.c #, c-format @@ -27627,6 +27783,24 @@ msgstr "錯誤: " msgid "warning: " msgstr "è¦å‘Š: " +#: usage.c +#, c-format +msgid "" +"'%s' is nominated for removal.\n" +"If you still use this command, please add an extra\n" +"option, '--i-still-use-this', on the command line\n" +"and let us know you still use it by sending an e-mail\n" +"to <git@vger.kernel.org>. Thanks.\n" +msgstr "" +"「%sã€å‘½ä»¤å·²è¢«æå移除。\n" +"如果您ä»åœ¨ä½¿ç”¨è©²å‘½ä»¤ï¼Œè«‹å¤šåŠ ä¸Šã€Œ--i-still-use-thisã€\n" +"é¸é …,然後寄å°é›»å郵件到 <git@vger.kernel.org>,\n" +"è®“æˆ‘å€‘çŸ¥é“æ‚¨é‚„在使用,è¬è¬ã€‚\n" + +#: usage.c +msgid "refusing to run without --i-still-use-this" +msgstr "傳入 --i-still-use-this 剿‹’絕執行" + #: version.c #, c-format msgid "uname() failed with error '%s' (%d)\n" @@ -27674,7 +27848,7 @@ msgstr ".git æª”æ¡ˆä¸æ£ç¢º" #: worktree.c msgid ".git file absolute/relative path mismatch" -msgstr ".git æª”æ¡ˆçš„çµ•å°æˆ–相å°è·¯å¾‘ä¸ä¸€è‡´" +msgstr ".git 檔案的絕å°/相å°è·¯å¾‘ä¸ä¸€è‡´" #: worktree.c msgid "not a valid path" @@ -27698,7 +27872,7 @@ msgstr "ç„¡æ³•è®€å– gitdir" #: worktree.c msgid "gitdir absolute/relative path mismatch" -msgstr "gitdir çš„çµ•å°æˆ–相å°è·¯å¾‘ä¸ä¸€è‡´" +msgstr "gitdir 的絕å°/相å°è·¯å¾‘ä¸ä¸€è‡´" #: worktree.c msgid "gitdir incorrect" @@ -28307,7 +28481,7 @@ msgstr "æäº¤ç‚ºç©ºï¼Œä½†æ˜¯å˜åœ¨å°šæœªè¿½è¹¤çš„æª”案\n" #: wt-status.c #, c-format msgid "nothing to commit (create/copy files and use \"git add\" to track)\n" -msgstr "ç„¡æª”æ¡ˆè¦æäº¤ï¼ˆå»ºç«‹/複製檔案並使用 \"git add\" 建立追蹤)\n" +msgstr "沒有æ±è¥¿æäº¤ï¼ˆå»ºç«‹/æ‹·è²æª”案並使用「git addã€è¿½è¹¤ä¹‹ï¼‰\n" #: wt-status.c #, c-format @@ -28688,7 +28862,7 @@ msgstr "傳é€é€™å°ä¿¡ä»¶ï¼Ÿ([y]es|[n]o|[e]dit|[q]uit|[a]ll): " #: git-send-email.perl msgid "Send this email reply required" -msgstr "傳é€è¦æ±‚的信件回復" +msgstr "傳é€è¦æ±‚的信件回覆" #: git-send-email.perl msgid "The required SMTP server is not properly defined." @@ -28775,6 +28949,11 @@ msgstr "(body) 新增 cc: %s 自行 '%s'\n" #: git-send-email.perl #, perl-format +msgid "error: invalid SMTP port '%s'\n" +msgstr "錯誤:SMTP é€£ç·šåŸ ã€Œ%sã€ç„¡æ•ˆ\n" + +#: git-send-email.perl +#, perl-format msgid "(%s) Could not execute '%s'" msgstr "(%s) ä¸èƒ½åŸ·è¡Œ '%s'" @@ -28815,7 +28994,7 @@ msgstr "" #: git-send-email.perl #, perl-format msgid "unable to open %s: %s\n" -msgstr "ä¸èƒ½é–‹å•Ÿ %s:%s\n" +msgstr "無法開啟 %s: %s\n" #: git-send-email.perl #, perl-format @@ -28829,7 +29008,7 @@ msgstr "" #: git-send-email.perl #, perl-format msgid "Skipping %s with backup suffix '%s'.\n" -msgstr "ç•¥éŽ %s å«å‚™ä»½å¾Œç¶´ '%s'。\n" +msgstr "ç•¥éŽå…·æœ‰å‚™ä»½å¾Œç¶´ã€Œ%2$sã€çš„ %1$s。\n" #. TRANSLATORS: please keep "[y|N]" as is. #: git-send-email.perl @@ -28837,6 +29016,51 @@ msgstr "ç•¥éŽ %s å«å‚™ä»½å¾Œç¶´ '%s'。\n" msgid "Do you really want to send %s? [y|N]: " msgstr "您真的è¦å‚³é€ %s?[y|N]: " +#~ msgid "start-after" +#~ msgstr "start-after" + +#~ msgid "compact-summary" +#~ msgstr "ç²¾è¦æ‘˜è¦ (compact-summary)" + +# è¯è€…ï¼šè«‹ç¶æŒå‰å°Žç©ºæ ¼ +#, c-format +#~ msgid " (%s will become dangling)" +#~ msgstr " (%s å°‡æˆç‚ºæ‡¸ç©ºç‹€æ…‹ï¼‰" + +# è¯è€…ï¼šè«‹ç¶æŒå‰å°Žç©ºæ ¼ +#, c-format +#~ msgid " (%s has become dangling)" +#~ msgstr " (%s å·²æˆç‚ºæ‡¸ç©ºç‹€æ…‹ï¼‰" + +#~ msgid "use at most one of --auto and --schedule=<frequency>" +#~ msgstr "--auto å’Œ --schedule=<é »çŽ‡> è«‹ä»»é¸ä¸€" + +#, c-format +#~ msgid "Final output: %d %s\n" +#~ msgstr "最終輸出:%d %s\n" + +#, c-format +#~ msgid "%d (FSCK_IGNORE?) should never trigger this callback" +#~ msgstr "%d (FSCK_IGNORE?) 䏿‡‰è§¸ç™¼é€™å€‹å›žå‘¼å‡½å¼" + +#~ msgid "" +#~ "git pack-objects --stdout [<options>] [< <ref-list> | < <object-list>]" +#~ msgstr "git pack-objects --stdout [<é¸é …>] [< <引用列表> | < <物件列表>]" + +#~ msgid "" +#~ "git pack-objects [<options>] <base-name> [< <ref-list> | < <object-list>]" +#~ msgstr "git pack-objects [<é¸é …>] <å‰ç¶´å稱> [< <引用列表> | < <物件列表>]" + +#~ msgid "cannot use --stdin-packs with --cruft" +#~ msgstr "無法將 --stdin-packs 與 --cruft 組åˆä½¿ç”¨" + +#, c-format +#~ msgid "unreachable: invalid reference: %s" +#~ msgstr "ä¸å¯é”:無效引用:%s" + +#~ msgid "trying to write commit not in index" +#~ msgstr "嘗試寫入ä¸åœ¨ç´¢å¼•çš„æäº¤" + #~ msgid "git cat-file (-t | -s) [--allow-unknown-type] <object>" #~ msgstr "git cat-file (-t | -s) [--allow-unknown-type] <object>" diff --git a/preload-index.c b/preload-index.c index 40ab2abafb..b222821b44 100644 --- a/preload-index.c +++ b/preload-index.c @@ -2,7 +2,6 @@ * Copyright (C) 2008 Linus Torvalds */ -#define USE_THE_REPOSITORY_VARIABLE #define DISABLE_SIGN_COMPARE_WARNINGS #include "git-compat-util.h" @@ -19,6 +18,7 @@ #include "repository.h" #include "symlinks.h" #include "trace2.h" +#include "config.h" /* * Mostly randomly chosen maximum thread counts: we @@ -111,6 +111,9 @@ void preload_index(struct index_state *index, struct thread_data data[MAX_PARALLEL]; struct progress_data pd; int t2_sum_lstat = 0; + int core_preload_index = 1; + + repo_config_get_bool(index->repo, "core.preloadindex", &core_preload_index); if (!HAVE_THREADS || !core_preload_index) return; @@ -132,7 +135,7 @@ void preload_index(struct index_state *index, memset(&pd, 0, sizeof(pd)); if (refresh_flags & REFRESH_PROGRESS && isatty(2)) { - pd.progress = start_delayed_progress(the_repository, + pd.progress = start_delayed_progress(index->repo, _("Refreshing index"), index->cache_nr); pthread_mutex_init(&pd.mutex, NULL); @@ -141,7 +141,7 @@ static void setup_commit_formats(void) COPY_ARRAY(commit_formats, builtin_formats, ARRAY_SIZE(builtin_formats)); - git_config(git_pretty_formats_config, NULL); + repo_config(the_repository, git_pretty_formats_config, NULL); } static struct cmt_fmt_map *find_commit_format_recursive(const char *sought, diff --git a/prio-queue.c b/prio-queue.c index ec33ac27db..9748528ce6 100644 --- a/prio-queue.c +++ b/prio-queue.c @@ -58,22 +58,10 @@ void prio_queue_put(struct prio_queue *queue, void *thing) } } -void *prio_queue_get(struct prio_queue *queue) +static void sift_down_root(struct prio_queue *queue) { - void *result; size_t ix, child; - if (!queue->nr) - return NULL; - if (!queue->compare) - return queue->array[--queue->nr].data; /* LIFO */ - - result = queue->array[0].data; - if (!--queue->nr) - return result; - - queue->array[0] = queue->array[queue->nr]; - /* Push down the one at the root */ for (ix = 0; ix * 2 + 1 < queue->nr; ix = child) { child = ix * 2 + 1; /* left */ @@ -86,6 +74,23 @@ void *prio_queue_get(struct prio_queue *queue) swap(queue, child, ix); } +} + +void *prio_queue_get(struct prio_queue *queue) +{ + void *result; + + if (!queue->nr) + return NULL; + if (!queue->compare) + return queue->array[--queue->nr].data; /* LIFO */ + + result = queue->array[0].data; + if (!--queue->nr) + return result; + + queue->array[0] = queue->array[queue->nr]; + sift_down_root(queue); return result; } @@ -97,3 +102,17 @@ void *prio_queue_peek(struct prio_queue *queue) return queue->array[queue->nr - 1].data; return queue->array[0].data; } + +void prio_queue_replace(struct prio_queue *queue, void *thing) +{ + if (!queue->nr) { + prio_queue_put(queue, thing); + } else if (!queue->compare) { + queue->array[queue->nr - 1].ctr = queue->insertion_ctr++; + queue->array[queue->nr - 1].data = thing; + } else { + queue->array[0].ctr = queue->insertion_ctr++; + queue->array[0].data = thing; + sift_down_root(queue); + } +} diff --git a/prio-queue.h b/prio-queue.h index 38d032636d..da7fad2f1f 100644 --- a/prio-queue.h +++ b/prio-queue.h @@ -52,6 +52,14 @@ void *prio_queue_get(struct prio_queue *); */ void *prio_queue_peek(struct prio_queue *); +/* + * Replace the "thing" that compares the smallest with a new "thing", + * like prio_queue_get()+prio_queue_put() would do, but in a more + * efficient way. Does the same as prio_queue_put() if the queue is + * empty. + */ +void prio_queue_replace(struct prio_queue *queue, void *thing); + void clear_prio_queue(struct prio_queue *); /* Reverse the LIFO elements */ diff --git a/progress.c b/progress.c index 8d5ae70f3a..8315bdc3d4 100644 --- a/progress.c +++ b/progress.c @@ -114,16 +114,19 @@ static void display(struct progress *progress, uint64_t n, const char *done) const char *tp; struct strbuf *counters_sb = &progress->counters_sb; int show_update = 0; + int update = !!progress_update; int last_count_len = counters_sb->len; - if (progress->delay && (!progress_update || --progress->delay)) + progress_update = 0; + + if (progress->delay && (!update || --progress->delay)) return; progress->last_value = n; tp = (progress->throughput) ? progress->throughput->display.buf : ""; if (progress->total) { unsigned percent = n * 100 / progress->total; - if (percent != progress->last_percent || progress_update) { + if (percent != progress->last_percent || update) { progress->last_percent = percent; strbuf_reset(counters_sb); @@ -133,7 +136,7 @@ static void display(struct progress *progress, uint64_t n, const char *done) tp); show_update = 1; } - } else if (progress_update) { + } else if (update) { strbuf_reset(counters_sb); strbuf_addf(counters_sb, "%"PRIuMAX"%s", (uintmax_t)n, tp); show_update = 1; @@ -166,7 +169,6 @@ static void display(struct progress *progress, uint64_t n, const char *done) } fflush(stderr); } - progress_update = 0; } } @@ -281,7 +283,7 @@ static int get_default_delay(void) static int delay_in_secs = -1; if (delay_in_secs < 0) - delay_in_secs = git_env_ulong("GIT_PROGRESS_DELAY", 2); + delay_in_secs = git_env_ulong("GIT_PROGRESS_DELAY", 1); return delay_in_secs; } diff --git a/promisor-remote.c b/promisor-remote.c index 9d058586df..77ebf537e2 100644 --- a/promisor-remote.c +++ b/promisor-remote.c @@ -3,7 +3,7 @@ #include "git-compat-util.h" #include "gettext.h" #include "hex.h" -#include "object-store.h" +#include "odb.h" #include "promisor-remote.h" #include "config.h" #include "trace2.h" @@ -46,7 +46,7 @@ static int fetch_objects(struct repository *repo, "fetch", remote_name, "--no-tags", "--no-write-fetch-head", "--recurse-submodules=no", "--filter=blob:none", "--stdin", NULL); - if (!git_config_get_bool("promisor.quiet", &quiet) && quiet) + if (!repo_config_get_bool(the_repository, "promisor.quiet", &quiet) && quiet) strvec_push(&child.args, "--quiet"); if (start_command(&child)) die(_("promisor-remote: unable to fork off fetch subprocess")); @@ -245,8 +245,8 @@ static int remove_fetched_oids(struct repository *repo, struct object_id *new_oids; for (i = 0; i < oid_nr; i++) - if (oid_object_info_extended(repo, &old_oids[i], NULL, - OBJECT_INFO_SKIP_FETCH_OBJECT)) { + if (odb_read_object_info_extended(repo->objects, &old_oids[i], NULL, + OBJECT_INFO_SKIP_FETCH_OBJECT)) { remaining[i] = 1; remaining_nr++; } @@ -314,9 +314,162 @@ static int allow_unsanitized(char ch) return ch > 32 && ch < 127; } -static void promisor_info_vecs(struct repository *repo, - struct strvec *names, - struct strvec *urls) +/* + * All the fields used in "promisor-remote" protocol capability, + * including the mandatory "name" and "url" ones. + */ +static const char promisor_field_name[] = "name"; +static const char promisor_field_url[] = "url"; +static const char promisor_field_filter[] = "partialCloneFilter"; +static const char promisor_field_token[] = "token"; + +/* + * List of optional field names that can be used in the + * "promisor-remote" protocol capability (others must be + * ignored). Each field should correspond to a configurable property + * of a remote that can be relevant for the client. + */ +static const char *known_fields[] = { + promisor_field_filter, /* Filter used for partial clone */ + promisor_field_token, /* Authentication token for the remote */ + NULL +}; + +/* + * Check if 'field' is in the list of the known field names for the + * "promisor-remote" protocol capability. + */ +static int is_known_field(const char *field) +{ + const char **p; + + for (p = known_fields; *p; p++) + if (!strcasecmp(*p, field)) + return 1; + return 0; +} + +static int is_valid_field(struct string_list_item *item, void *cb_data) +{ + const char *field = item->string; + const char *config_key = (const char *)cb_data; + + if (!is_known_field(field)) { + warning(_("unsupported field '%s' in '%s' config"), field, config_key); + return 0; + } + return 1; +} + +static char *fields_from_config(struct string_list *fields_list, const char *config_key) +{ + char *fields = NULL; + + if (!repo_config_get_string(the_repository, config_key, &fields) && *fields) { + string_list_split_in_place_f(fields_list, fields, ",", -1, + STRING_LIST_SPLIT_TRIM | + STRING_LIST_SPLIT_NONEMPTY); + filter_string_list(fields_list, 0, is_valid_field, (void *)config_key); + } + + return fields; +} + +static struct string_list *fields_sent(void) +{ + static struct string_list fields_list = STRING_LIST_INIT_NODUP; + static int initialized; + + if (!initialized) { + fields_list.cmp = strcasecmp; + fields_from_config(&fields_list, "promisor.sendFields"); + initialized = 1; + } + + return &fields_list; +} + +static struct string_list *fields_checked(void) +{ + static struct string_list fields_list = STRING_LIST_INIT_NODUP; + static int initialized; + + if (!initialized) { + fields_list.cmp = strcasecmp; + fields_from_config(&fields_list, "promisor.checkFields"); + initialized = 1; + } + + return &fields_list; +} + +/* + * Struct for promisor remotes involved in the "promisor-remote" + * protocol capability. + * + * Except for "name", each <member> in this struct and its <value> + * should correspond (either on the client side or on the server side) + * to a "remote.<name>.<member>" config variable set to <value> where + * "<name>" is a promisor remote name. + */ +struct promisor_info { + const char *name; + const char *url; + const char *filter; + const char *token; +}; + +static void promisor_info_free(struct promisor_info *p) +{ + free((char *)p->name); + free((char *)p->url); + free((char *)p->filter); + free((char *)p->token); + free(p); +} + +static void promisor_info_list_clear(struct string_list *list) +{ + for (size_t i = 0; i < list->nr; i++) + promisor_info_free(list->items[i].util); + string_list_clear(list, 0); +} + +static void set_one_field(struct promisor_info *p, + const char *field, const char *value) +{ + if (!strcasecmp(field, promisor_field_filter)) + p->filter = xstrdup(value); + else if (!strcasecmp(field, promisor_field_token)) + p->token = xstrdup(value); + else + BUG("invalid field '%s'", field); +} + +static void set_fields(struct promisor_info *p, + struct string_list *field_names) +{ + struct string_list_item *item; + + for_each_string_list_item(item, field_names) { + char *key = xstrfmt("remote.%s.%s", p->name, item->string); + const char *val; + if (!repo_config_get_string_tmp(the_repository, key, &val) && *val) + set_one_field(p, item->string, val); + free(key); + } +} + +/* + * Populate 'list' with promisor remote information from the config. + * The 'util' pointer of each list item will hold a 'struct + * promisor_info'. Except "name" and "url", only members of that + * struct specified by the 'field_names' list are set (using values + * from the configuration). + */ +static void promisor_config_info_list(struct repository *repo, + struct string_list *list, + struct string_list *field_names) { struct promisor_remote *r; @@ -327,9 +480,18 @@ static void promisor_info_vecs(struct repository *repo, char *url_key = xstrfmt("remote.%s.url", r->name); /* Only add remotes with a non empty URL */ - if (!git_config_get_string_tmp(url_key, &url) && *url) { - strvec_push(names, r->name); - strvec_push(urls, url); + if (!repo_config_get_string_tmp(the_repository, url_key, &url) && *url) { + struct promisor_info *new_info = xcalloc(1, sizeof(*new_info)); + struct string_list_item *item; + + new_info->name = xstrdup(r->name); + new_info->url = xstrdup(url); + + if (field_names) + set_fields(new_info, field_names); + + item = string_list_append(list, new_info->name); + item->util = new_info; } free(url_key); @@ -340,47 +502,45 @@ char *promisor_remote_info(struct repository *repo) { struct strbuf sb = STRBUF_INIT; int advertise_promisors = 0; - struct strvec names = STRVEC_INIT; - struct strvec urls = STRVEC_INIT; + struct string_list config_info = STRING_LIST_INIT_NODUP; + struct string_list_item *item; - git_config_get_bool("promisor.advertise", &advertise_promisors); + repo_config_get_bool(the_repository, "promisor.advertise", &advertise_promisors); if (!advertise_promisors) return NULL; - promisor_info_vecs(repo, &names, &urls); + promisor_config_info_list(repo, &config_info, fields_sent()); - if (!names.nr) + if (!config_info.nr) return NULL; - for (size_t i = 0; i < names.nr; i++) { - if (i) + for_each_string_list_item(item, &config_info) { + struct promisor_info *p = item->util; + + if (item != config_info.items) strbuf_addch(&sb, ';'); - strbuf_addstr(&sb, "name="); - strbuf_addstr_urlencode(&sb, names.v[i], allow_unsanitized); - strbuf_addstr(&sb, ",url="); - strbuf_addstr_urlencode(&sb, urls.v[i], allow_unsanitized); + + strbuf_addf(&sb, "%s=", promisor_field_name); + strbuf_addstr_urlencode(&sb, p->name, allow_unsanitized); + strbuf_addf(&sb, ",%s=", promisor_field_url); + strbuf_addstr_urlencode(&sb, p->url, allow_unsanitized); + + if (p->filter) { + strbuf_addf(&sb, ",%s=", promisor_field_filter); + strbuf_addstr_urlencode(&sb, p->filter, allow_unsanitized); + } + if (p->token) { + strbuf_addf(&sb, ",%s=", promisor_field_token); + strbuf_addstr_urlencode(&sb, p->token, allow_unsanitized); + } } - strvec_clear(&names); - strvec_clear(&urls); + promisor_info_list_clear(&config_info); return strbuf_detach(&sb, NULL); } -/* - * Find first index of 'nicks' where there is 'nick'. 'nick' is - * compared case sensitively to the strings in 'nicks'. If not found - * 'nicks->nr' is returned. - */ -static size_t remote_nick_find(struct strvec *nicks, const char *nick) -{ - for (size_t i = 0; i < nicks->nr; i++) - if (!strcmp(nicks->v[i], nick)) - return i; - return nicks->nr; -} - enum accept_promisor { ACCEPT_NONE = 0, ACCEPT_KNOWN_URL, @@ -388,23 +548,84 @@ enum accept_promisor { ACCEPT_ALL }; +static int match_field_against_config(const char *field, const char *value, + struct promisor_info *config_info) +{ + if (config_info->filter && !strcasecmp(field, promisor_field_filter)) + return !strcmp(config_info->filter, value); + else if (config_info->token && !strcasecmp(field, promisor_field_token)) + return !strcmp(config_info->token, value); + + return 0; +} + +static int all_fields_match(struct promisor_info *advertised, + struct string_list *config_info, + int in_list) +{ + struct string_list *fields = fields_checked(); + struct string_list_item *item_checked; + + for_each_string_list_item(item_checked, fields) { + int match = 0; + const char *field = item_checked->string; + const char *value = NULL; + struct string_list_item *item; + + if (!strcasecmp(field, promisor_field_filter)) + value = advertised->filter; + else if (!strcasecmp(field, promisor_field_token)) + value = advertised->token; + + if (!value) + return 0; + + if (in_list) { + for_each_string_list_item(item, config_info) { + struct promisor_info *p = item->util; + if (match_field_against_config(field, value, p)) { + match = 1; + break; + } + } + } else { + item = string_list_lookup(config_info, advertised->name); + if (item) { + struct promisor_info *p = item->util; + match = match_field_against_config(field, value, p); + } + } + + if (!match) + return 0; + } + + return 1; +} + static int should_accept_remote(enum accept_promisor accept, - const char *remote_name, const char *remote_url, - struct strvec *names, struct strvec *urls) + struct promisor_info *advertised, + struct string_list *config_info) { - size_t i; + struct promisor_info *p; + struct string_list_item *item; + const char *remote_name = advertised->name; + const char *remote_url = advertised->url; if (accept == ACCEPT_ALL) - return 1; + return all_fields_match(advertised, config_info, 1); - i = remote_nick_find(names, remote_name); + /* Get config info for that promisor remote */ + item = string_list_lookup(config_info, remote_name); - if (i >= names->nr) + if (!item) /* We don't know about that remote */ return 0; + p = item->util; + if (accept == ACCEPT_KNOWN_NAME) - return 1; + return all_fields_match(advertised, config_info, 0); if (accept != ACCEPT_KNOWN_URL) BUG("Unhandled 'enum accept_promisor' value '%d'", accept); @@ -414,26 +635,74 @@ static int should_accept_remote(enum accept_promisor accept, return 0; } - if (!strcmp(urls->v[i], remote_url)) - return 1; + if (!strcmp(p->url, remote_url)) + return all_fields_match(advertised, config_info, 0); warning(_("known remote named '%s' but with URL '%s' instead of '%s'"), - remote_name, urls->v[i], remote_url); + remote_name, p->url, remote_url); return 0; } +static int skip_field_name_prefix(const char *elem, const char *field_name, const char **value) +{ + const char *p; + if (!skip_prefix(elem, field_name, &p) || *p != '=') + return 0; + *value = p + 1; + return 1; +} + +static struct promisor_info *parse_one_advertised_remote(const char *remote_info) +{ + struct promisor_info *info = xcalloc(1, sizeof(*info)); + struct string_list elem_list = STRING_LIST_INIT_DUP; + struct string_list_item *item; + + string_list_split(&elem_list, remote_info, ",", -1); + + for_each_string_list_item(item, &elem_list) { + const char *elem = item->string; + const char *p = strchr(elem, '='); + + if (!p) { + warning(_("invalid element '%s' from remote info"), elem); + continue; + } + + if (skip_field_name_prefix(elem, promisor_field_name, &p)) + info->name = url_percent_decode(p); + else if (skip_field_name_prefix(elem, promisor_field_url, &p)) + info->url = url_percent_decode(p); + else if (skip_field_name_prefix(elem, promisor_field_filter, &p)) + info->filter = url_percent_decode(p); + else if (skip_field_name_prefix(elem, promisor_field_token, &p)) + info->token = url_percent_decode(p); + } + + string_list_clear(&elem_list, 0); + + if (!info->name || !info->url) { + warning(_("server advertised a promisor remote without a name or URL: %s"), + remote_info); + promisor_info_free(info); + return NULL; + } + + return info; +} + static void filter_promisor_remote(struct repository *repo, struct strvec *accepted, const char *info) { - struct strbuf **remotes; const char *accept_str; enum accept_promisor accept = ACCEPT_NONE; - struct strvec names = STRVEC_INIT; - struct strvec urls = STRVEC_INIT; + struct string_list config_info = STRING_LIST_INIT_NODUP; + struct string_list remote_info = STRING_LIST_INIT_DUP; + struct string_list_item *item; - if (!git_config_get_string_tmp("promisor.acceptfromserver", &accept_str)) { + if (!repo_config_get_string_tmp(the_repository, "promisor.acceptfromserver", &accept_str)) { if (!*accept_str || !strcasecmp("None", accept_str)) accept = ACCEPT_NONE; else if (!strcasecmp("KnownUrl", accept_str)) @@ -450,49 +719,31 @@ static void filter_promisor_remote(struct repository *repo, if (accept == ACCEPT_NONE) return; - if (accept != ACCEPT_ALL) - promisor_info_vecs(repo, &names, &urls); - /* Parse remote info received */ - remotes = strbuf_split_str(info, ';', 0); - - for (size_t i = 0; remotes[i]; i++) { - struct strbuf **elems; - const char *remote_name = NULL; - const char *remote_url = NULL; - char *decoded_name = NULL; - char *decoded_url = NULL; - - strbuf_strip_suffix(remotes[i], ";"); - elems = strbuf_split(remotes[i], ','); - - for (size_t j = 0; elems[j]; j++) { - int res; - strbuf_strip_suffix(elems[j], ","); - res = skip_prefix(elems[j]->buf, "name=", &remote_name) || - skip_prefix(elems[j]->buf, "url=", &remote_url); - if (!res) - warning(_("unknown element '%s' from remote info"), - elems[j]->buf); - } + string_list_split(&remote_info, info, ";", -1); + + for_each_string_list_item(item, &remote_info) { + struct promisor_info *advertised; - if (remote_name) - decoded_name = url_percent_decode(remote_name); - if (remote_url) - decoded_url = url_percent_decode(remote_url); + advertised = parse_one_advertised_remote(item->string); + + if (!advertised) + continue; + + if (!config_info.nr) { + promisor_config_info_list(repo, &config_info, fields_checked()); + string_list_sort(&config_info); + } - if (decoded_name && should_accept_remote(accept, decoded_name, decoded_url, &names, &urls)) - strvec_push(accepted, decoded_name); + if (should_accept_remote(accept, advertised, &config_info)) + strvec_push(accepted, advertised->name); - strbuf_list_free(elems); - free(decoded_name); - free(decoded_url); + promisor_info_free(advertised); } - strvec_clear(&names); - strvec_clear(&urls); - strbuf_list_free(remotes); + promisor_info_list_clear(&config_info); + string_list_clear(&remote_info, 0); } char *promisor_remote_reply(const char *info) @@ -518,16 +769,15 @@ char *promisor_remote_reply(const char *info) void mark_promisor_remotes_as_accepted(struct repository *r, const char *remotes) { - struct strbuf **accepted_remotes = strbuf_split_str(remotes, ';', 0); + struct string_list accepted_remotes = STRING_LIST_INIT_DUP; + struct string_list_item *item; - for (size_t i = 0; accepted_remotes[i]; i++) { - struct promisor_remote *p; - char *decoded_remote; + string_list_split(&accepted_remotes, remotes, ";", -1); - strbuf_strip_suffix(accepted_remotes[i], ";"); - decoded_remote = url_percent_decode(accepted_remotes[i]->buf); + for_each_string_list_item(item, &accepted_remotes) { + char *decoded_remote = url_percent_decode(item->string); + struct promisor_remote *p = repo_promisor_remote_find(r, decoded_remote); - p = repo_promisor_remote_find(r, decoded_remote); if (p) p->accepted = 1; else @@ -537,5 +787,5 @@ void mark_promisor_remotes_as_accepted(struct repository *r, const char *remotes free(decoded_remote); } - strbuf_list_free(accepted_remotes); + string_list_clear(&accepted_remotes, 0); } @@ -77,12 +77,6 @@ char *git_prompt(const char *prompt, int flags) int git_read_line_interactively(struct strbuf *line) { - int ret; - fflush(stdout); - ret = strbuf_getline_lf(line, stdin); - if (ret != EOF) - strbuf_trim_trailing_newline(line); - - return ret; + return strbuf_getline(line, stdin); } diff --git a/protocol-caps.c b/protocol-caps.c index 9b8db37a21..ecdd0dc58d 100644 --- a/protocol-caps.c +++ b/protocol-caps.c @@ -6,7 +6,7 @@ #include "hash.h" #include "hex.h" #include "object.h" -#include "object-store.h" +#include "odb.h" #include "repository.h" #include "string-list.h" #include "strbuf.h" @@ -64,7 +64,7 @@ static void send_info(struct repository *r, struct packet_writer *writer, strbuf_addstr(&send_buffer, oid_str); if (info->size) { - if (oid_object_info(r, &oid, &object_size) < 0) { + if (odb_read_object_info(r->objects, &oid, &object_size) < 0) { strbuf_addstr(&send_buffer, " "); } else { strbuf_addf(&send_buffer, " %lu", object_size); diff --git a/protocol.c b/protocol.c index bae7226ff4..a3e26a8dd3 100644 --- a/protocol.c +++ b/protocol.c @@ -24,7 +24,7 @@ enum protocol_version get_protocol_version_config(void) const char *git_test_k = "GIT_TEST_PROTOCOL_VERSION"; const char *git_test_v; - if (!git_config_get_string_tmp("protocol.version", &value)) { + if (!repo_config_get_string_tmp(the_repository, "protocol.version", &value)) { enum protocol_version version = parse_protocol_version(value); if (version == protocol_unknown_version) @@ -61,7 +61,7 @@ enum protocol_version determine_protocol_version_server(void) if (git_protocol) { struct string_list list = STRING_LIST_INIT_DUP; const struct string_list_item *item; - string_list_split(&list, git_protocol, ':', -1); + string_list_split(&list, git_protocol, ":", -1); for_each_string_list_item(item, &list) { const char *value; diff --git a/prune-packed.c b/prune-packed.c index 92fb4fbb0e..d49dc11957 100644 --- a/prune-packed.c +++ b/prune-packed.c @@ -40,7 +40,7 @@ void prune_packed_objects(int opts) progress = start_delayed_progress(the_repository, _("Removing duplicate objects"), 256); - for_each_loose_file_in_objdir(repo_get_object_directory(the_repository), + for_each_loose_file_in_source(the_repository->objects->sources, prune_object, NULL, prune_subdir, &opts); /* Ensure we show 100% before finishing progress */ diff --git a/range-diff.c b/range-diff.c index 8a2dcbee32..ca449a0769 100644 --- a/range-diff.c +++ b/range-diff.c @@ -325,13 +325,24 @@ static int diffsize(const char *a, const char *b) } static void get_correspondences(struct string_list *a, struct string_list *b, - int creation_factor) + int creation_factor, size_t max_memory) { int n = a->nr + b->nr; int *cost, c, *a2b, *b2a; int i, j; - - ALLOC_ARRAY(cost, st_mult(n, n)); + size_t cost_size = st_mult(n, n); + size_t cost_bytes = st_mult(sizeof(int), cost_size); + if (cost_bytes >= max_memory) { + struct strbuf cost_str = STRBUF_INIT; + struct strbuf max_str = STRBUF_INIT; + strbuf_humanise_bytes(&cost_str, cost_bytes); + strbuf_humanise_bytes(&max_str, max_memory); + die(_("range-diff: unable to compute the range-diff, since it " + "exceeds the maximum memory for the cost matrix: %s " + "(%"PRIuMAX" bytes) needed, limited to %s (%"PRIuMAX" bytes)"), + cost_str.buf, (uintmax_t)cost_bytes, max_str.buf, (uintmax_t)max_memory); + } + ALLOC_ARRAY(cost, cost_size); ALLOC_ARRAY(a2b, n); ALLOC_ARRAY(b2a, n); @@ -591,7 +602,8 @@ int show_range_diff(const char *range1, const char *range2, if (!res) { find_exact_matches(&branch1, &branch2); get_correspondences(&branch1, &branch2, - range_diff_opts->creation_factor); + range_diff_opts->creation_factor, + range_diff_opts->max_memory); output(&branch1, &branch2, range_diff_opts); } diff --git a/range-diff.h b/range-diff.h index cd85000b5a..9d39818e34 100644 --- a/range-diff.h +++ b/range-diff.h @@ -5,6 +5,10 @@ #include "strvec.h" #define RANGE_DIFF_CREATION_FACTOR_DEFAULT 60 +#define RANGE_DIFF_MAX_MEMORY_DEFAULT \ + (sizeof(void*) >= 8 ? \ + ((size_t)(1024L * 1024L) * (size_t)(4L * 1024L)) : /* 4GB on 64-bit */ \ + ((size_t)(1024L * 1024L) * (size_t)(2L * 1024L))) /* 2GB on 32-bit */ /* * A much higher value than the default, when we KNOW we are comparing @@ -17,6 +21,7 @@ struct range_diff_options { unsigned dual_color:1; unsigned left_only:1, right_only:1; unsigned include_merges:1; + size_t max_memory; const struct diff_options *diffopt; /* may be NULL */ const struct strvec *other_arg; /* may be NULL */ }; diff --git a/reachable.c b/reachable.c index 9dc748f0b9..22266db523 100644 --- a/reachable.c +++ b/reachable.c @@ -170,7 +170,7 @@ static void load_gc_recent_objects(struct recent_data *data) data->extra_recent_oids_loaded = 1; - if (git_config_get_string_multi("gc.recentobjectshook", &programs)) + if (repo_config_get_string_multi(the_repository, "gc.recentobjectshook", &programs)) return; for (i = 0; i < programs->nr; i++) { @@ -211,7 +211,7 @@ static void add_recent_object(const struct object_id *oid, * later processing, and the revision machinery expects * commits and tags to have been parsed. */ - type = oid_object_info(the_repository, oid, NULL); + type = odb_read_object_info(the_repository->objects, oid, NULL); if (type < 0) die("unable to get object info for %s", oid_to_hex(oid)); @@ -319,7 +319,7 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs, oidset_init(&data.extra_recent_oids, 0); data.extra_recent_oids_loaded = 0; - r = for_each_loose_object(add_recent_loose, &data, + r = for_each_loose_object(the_repository->objects, add_recent_loose, &data, FOR_EACH_OBJECT_LOCAL_ONLY); if (r) goto done; diff --git a/read-cache.c b/read-cache.c index c0bb760ad4..229b8ef11c 100644 --- a/read-cache.c +++ b/read-cache.c @@ -20,7 +20,7 @@ #include "refs.h" #include "dir.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "oid-array.h" #include "tree.h" #include "commit.h" @@ -254,7 +254,7 @@ static int ce_compare_link(const struct cache_entry *ce, size_t expected_size) if (strbuf_readlink(&sb, ce->name, expected_size)) return -1; - buffer = repo_read_object_file(the_repository, &ce->oid, &type, &size); + buffer = odb_read_object(the_repository->objects, &ce->oid, &type, &size); if (buffer) { if (size == sb.len) match = memcmp(buffer, sb.buf, size); @@ -690,7 +690,7 @@ static struct cache_entry *create_alias_ce(struct index_state *istate, void set_object_name_for_intent_to_add_entry(struct cache_entry *ce) { struct object_id oid; - if (write_object_file("", 0, OBJ_BLOB, &oid)) + if (odb_write_object(the_repository->objects, "", 0, OBJ_BLOB, &oid)) die(_("cannot create an empty blob in the object database")); oidcpy(&ce->oid, &oid); } @@ -1456,7 +1456,8 @@ int repo_refresh_and_write_index(struct repository *repo, struct lock_file lock_file = LOCK_INIT; int fd, ret = 0; - fd = repo_hold_locked_index(repo, &lock_file, 0); + fd = repo_hold_locked_index(repo, &lock_file, + gentle ? 0 : LOCK_REPORT_ON_ERROR); if (!gentle && fd < 0) return -1; if (refresh_index(repo->index, refresh_flags, pathspec, seen, header_msg)) @@ -2754,7 +2755,7 @@ static int record_eoie(void) { int val; - if (!git_config_get_bool("index.recordendofindexentries", &val)) + if (!repo_config_get_bool(the_repository, "index.recordendofindexentries", &val)) return val; /* @@ -2769,7 +2770,7 @@ static int record_ieot(void) { int val; - if (!git_config_get_bool("index.recordoffsettable", &val)) + if (!repo_config_get_bool(the_repository, "index.recordoffsettable", &val)) return val; /* @@ -3485,8 +3486,8 @@ void *read_blob_data_from_index(struct index_state *istate, } if (pos < 0) return NULL; - data = repo_read_object_file(the_repository, &istate->cache[pos]->oid, - &type, &sz); + data = odb_read_object(the_repository->objects, &istate->cache[pos]->oid, + &type, &sz); if (!data || type != OBJ_BLOB) { free(data); return NULL; @@ -3729,9 +3730,9 @@ void prefetch_cache_entries(const struct index_state *istate, if (S_ISGITLINK(ce->ce_mode) || !must_prefetch(ce)) continue; - if (!oid_object_info_extended(the_repository, &ce->oid, - NULL, - OBJECT_INFO_FOR_PREFETCH)) + if (!odb_read_object_info_extended(the_repository->objects, + &ce->oid, NULL, + OBJECT_INFO_FOR_PREFETCH)) continue; oid_array_append(&to_fetch, &ce->oid); } @@ -3946,6 +3947,7 @@ int add_files_to_cache(struct repository *repo, const char *prefix, const struct pathspec *pathspec, char *ps_matched, int include_sparse, int flags) { + struct odb_transaction *transaction; struct update_callback_data data; struct rev_info rev; @@ -3971,9 +3973,9 @@ int add_files_to_cache(struct repository *repo, const char *prefix, * This function is invoked from commands other than 'add', which * may not have their own transaction active. */ - begin_odb_transaction(); + transaction = begin_odb_transaction(repo->objects); run_diff_files(&rev, DIFF_RACY_IS_MODIFIED); - end_odb_transaction(); + end_odb_transaction(transaction); release_revisions(&rev); return !!data.add_errors; diff --git a/rebase-interactive.c b/rebase-interactive.c index cbeb864147..809f76a87b 100644 --- a/rebase-interactive.c +++ b/rebase-interactive.c @@ -30,7 +30,7 @@ static enum missing_commit_check_level get_missing_commit_check_level(void) { const char *value; - if (git_config_get_value("rebase.missingcommitscheck", &value) || + if (repo_config_get_value(the_repository, "rebase.missingcommitscheck", &value) || !strcasecmp("ignore", value)) return MISSING_COMMIT_CHECK_IGNORE; if (!strcasecmp("warn", value)) diff --git a/ref-filter.c b/ref-filter.c index 7a274633cf..520d2539c9 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -12,7 +12,7 @@ #include "refs.h" #include "wildmatch.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "oid-array.h" #include "repo-settings.h" #include "repository.h" @@ -435,7 +435,7 @@ static int remote_ref_atom_parser(struct ref_format *format UNUSED, } atom->u.remote_ref.nobracket = 0; - string_list_split(¶ms, arg, ',', -1); + string_list_split(¶ms, arg, ",", -1); for (i = 0; i < params.nr; i++) { const char *s = params.items[i].string; @@ -831,7 +831,7 @@ static int align_atom_parser(struct ref_format *format UNUSED, align->position = ALIGN_LEFT; - string_list_split(¶ms, arg, ',', -1); + string_list_split(¶ms, arg, ",", -1); for (i = 0; i < params.nr; i++) { const char *s = params.items[i].string; int position; @@ -2302,8 +2302,8 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj oi->info.sizep = &oi->size; oi->info.typep = &oi->type; } - if (oid_object_info_extended(the_repository, &oi->oid, &oi->info, - OBJECT_INFO_LOOKUP_REPLACE)) + if (odb_read_object_info_extended(the_repository->objects, &oi->oid, &oi->info, + OBJECT_INFO_LOOKUP_REPLACE)) return strbuf_addf_ret(err, -1, _("missing object %s for %s"), oid_to_hex(&oi->oid), ref->refname); if (oi->info.disk_sizep && oi->disk_size < 0) @@ -2684,6 +2684,41 @@ static int filter_exclude_match(struct ref_filter *filter, const char *refname) } /* + * We need to seek to the reference right after a given marker but excluding any + * matching references. So we seek to the lexicographically next reference. + */ +static int start_ref_iterator_after(struct ref_iterator *iter, const char *marker) +{ + struct strbuf sb = STRBUF_INIT; + int ret; + + strbuf_addstr(&sb, marker); + strbuf_addch(&sb, 1); + + ret = ref_iterator_seek(iter, sb.buf, 0); + + strbuf_release(&sb); + return ret; +} + +static int for_each_fullref_with_seek(struct ref_filter *filter, each_ref_fn cb, + void *cb_data, unsigned int flags) +{ + struct ref_iterator *iter; + int ret = 0; + + iter = refs_ref_iterator_begin(get_main_ref_store(the_repository), "", + NULL, 0, flags); + if (filter->start_after) + ret = start_ref_iterator_after(iter, filter->start_after); + + if (ret) + return ret; + + return do_for_each_ref_iterator(iter, cb, cb_data); +} + +/* * This is the same as for_each_fullref_in(), but it tries to iterate * only over the patterns we'll care about. Note that it _doesn't_ do a full * pattern match, so the callback still has to match each ref individually. @@ -2694,8 +2729,8 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter, { if (filter->kind & FILTER_REFS_ROOT_REFS) { /* In this case, we want to print all refs including root refs. */ - return refs_for_each_include_root_refs(get_main_ref_store(the_repository), - cb, cb_data); + return for_each_fullref_with_seek(filter, cb, cb_data, + DO_FOR_EACH_INCLUDE_ROOT_REFS); } if (!filter->match_as_path) { @@ -2704,8 +2739,7 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter, * prefixes like "refs/heads/" etc. are stripped off, * so we have to look at everything: */ - return refs_for_each_fullref_in(get_main_ref_store(the_repository), - "", NULL, cb, cb_data); + return for_each_fullref_with_seek(filter, cb, cb_data, 0); } if (filter->ignore_case) { @@ -2714,14 +2748,12 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter, * so just return everything and let the caller * sort it out. */ - return refs_for_each_fullref_in(get_main_ref_store(the_repository), - "", NULL, cb, cb_data); + return for_each_fullref_with_seek(filter, cb, cb_data, 0); } if (!filter->name_patterns[0]) { /* no patterns; we have to look at everything */ - return refs_for_each_fullref_in(get_main_ref_store(the_repository), - "", filter->exclude.v, cb, cb_data); + return for_each_fullref_with_seek(filter, cb, cb_data, 0); } return refs_for_each_fullref_in_prefixes(get_main_ref_store(the_repository), @@ -3189,6 +3221,7 @@ void filter_is_base(struct repository *r, static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref_fn fn, void *cb_data) { + const char *prefix = NULL; int ret = 0; filter->kind = type & FILTER_REFS_KIND_MASK; @@ -3199,38 +3232,48 @@ static int do_filter_refs(struct ref_filter *filter, unsigned int type, each_ref /* Simple per-ref filtering */ if (!filter->kind) die("filter_refs: invalid type"); - else { - /* - * For common cases where we need only branches or remotes or tags, - * we only iterate through those refs. If a mix of refs is needed, - * we iterate over all refs and filter out required refs with the help - * of filter_ref_kind(). - */ - if (filter->kind == FILTER_REFS_BRANCHES) - ret = refs_for_each_fullref_in(get_main_ref_store(the_repository), - "refs/heads/", NULL, - fn, cb_data); - else if (filter->kind == FILTER_REFS_REMOTES) - ret = refs_for_each_fullref_in(get_main_ref_store(the_repository), - "refs/remotes/", NULL, - fn, cb_data); - else if (filter->kind == FILTER_REFS_TAGS) - ret = refs_for_each_fullref_in(get_main_ref_store(the_repository), - "refs/tags/", NULL, fn, - cb_data); - else if (filter->kind & FILTER_REFS_REGULAR) - ret = for_each_fullref_in_pattern(filter, fn, cb_data); - /* - * When printing all ref types, HEAD is already included, - * so we don't want to print HEAD again. - */ - if (!ret && !(filter->kind & FILTER_REFS_ROOT_REFS) && - (filter->kind & FILTER_REFS_DETACHED_HEAD)) - refs_head_ref(get_main_ref_store(the_repository), fn, - cb_data); + /* + * For common cases where we need only branches or remotes or tags, + * we only iterate through those refs. If a mix of refs is needed, + * we iterate over all refs and filter out required refs with the help + * of filter_ref_kind(). + */ + if (filter->kind == FILTER_REFS_BRANCHES) + prefix = "refs/heads/"; + else if (filter->kind == FILTER_REFS_REMOTES) + prefix = "refs/remotes/"; + else if (filter->kind == FILTER_REFS_TAGS) + prefix = "refs/tags/"; + + if (prefix) { + struct ref_iterator *iter; + + iter = refs_ref_iterator_begin(get_main_ref_store(the_repository), + "", NULL, 0, 0); + + if (filter->start_after) + ret = start_ref_iterator_after(iter, filter->start_after); + else + ret = ref_iterator_seek(iter, prefix, + REF_ITERATOR_SEEK_SET_PREFIX); + + if (!ret) + ret = do_for_each_ref_iterator(iter, fn, cb_data); + } else if (filter->kind & FILTER_REFS_REGULAR) { + ret = for_each_fullref_in_pattern(filter, fn, cb_data); } + /* + * When printing all ref types, HEAD is already included, + * so we don't want to print HEAD again. + */ + if (!ret && !(filter->kind & FILTER_REFS_ROOT_REFS) && + (filter->kind & FILTER_REFS_DETACHED_HEAD)) + refs_head_ref(get_main_ref_store(the_repository), fn, + cb_data); + + clear_contains_cache(&filter->internal.contains_cache); clear_contains_cache(&filter->internal.no_contains_cache); diff --git a/ref-filter.h b/ref-filter.h index c98c4fbd4c..f22ca94b49 100644 --- a/ref-filter.h +++ b/ref-filter.h @@ -64,6 +64,7 @@ struct ref_array { struct ref_filter { const char **name_patterns; + const char *start_after; struct strvec exclude; struct oid_array points_at; struct commit_list *with_commit; diff --git a/reflog-walk.c b/reflog-walk.c index c7070b13b0..4f1ce04749 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -22,9 +22,10 @@ struct complete_reflogs { int nr, alloc; }; -static int read_one_reflog(struct object_id *ooid, struct object_id *noid, - const char *email, timestamp_t timestamp, int tz, - const char *message, void *cb_data) +static int read_one_reflog(const char *refname UNUSED, + struct object_id *ooid, struct object_id *noid, + const char *email, timestamp_t timestamp, int tz, + const char *message, void *cb_data) { struct complete_reflogs *array = cb_data; struct reflog_info *item; @@ -3,9 +3,10 @@ #include "git-compat-util.h" #include "config.h" +#include "environment.h" #include "gettext.h" #include "parse-options.h" -#include "object-store.h" +#include "odb.h" #include "reflog.h" #include "refs.h" #include "revision.h" @@ -81,6 +82,20 @@ int reflog_expire_config(const char *var, const char *value, return 0; } +void reflog_clear_expire_config(struct reflog_expire_options *opts) +{ + struct reflog_expire_entry_option *ent = opts->entries, *tmp; + + while (ent) { + tmp = ent; + ent = ent->next; + free(tmp); + } + + opts->entries = NULL; + opts->entries_tail = NULL; +} + void reflog_expire_options_set_refname(struct reflog_expire_options *cb, const char *ref) { @@ -140,8 +155,8 @@ static int tree_is_complete(const struct object_id *oid) if (!tree->buffer) { enum object_type type; unsigned long size; - void *data = repo_read_object_file(the_repository, oid, &type, - &size); + void *data = odb_read_object(the_repository->objects, oid, + &type, &size); if (!data) { tree->object.flags |= INCOMPLETE; return 0; @@ -152,7 +167,7 @@ static int tree_is_complete(const struct object_id *oid) init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size); complete = 1; while (tree_entry(&desc, &entry)) { - if (!has_object(the_repository, &entry.oid, + if (!odb_has_object(the_repository->objects, &entry.oid, HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR) || (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) { tree->object.flags |= INCOMPLETE; @@ -492,7 +507,8 @@ void reflog_expiry_cleanup(void *cb_data) free_commit_list(cb->mark_list); } -int count_reflog_ent(struct object_id *ooid UNUSED, +int count_reflog_ent(const char *refname UNUSED, + struct object_id *ooid UNUSED, struct object_id *noid UNUSED, const char *email UNUSED, timestamp_t timestamp, int tz UNUSED, @@ -34,6 +34,8 @@ struct reflog_expire_options { int reflog_expire_config(const char *var, const char *value, const struct config_context *ctx, void *cb); +void reflog_clear_expire_config(struct reflog_expire_options *opts); + /* * Adapt the options so that they apply to the given refname. This applies any * per-reference reflog expiry configuration that may exist to the options. @@ -63,7 +65,8 @@ void reflog_expiry_prepare(const char *refname, const struct object_id *oid, int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid, const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data); -int count_reflog_ent(struct object_id *ooid, struct object_id *noid, +int count_reflog_ent(const char *refname, + struct object_id *ooid, struct object_id *noid, const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data); int should_expire_reflog_ent_verbose(struct object_id *ooid, @@ -19,7 +19,7 @@ #include "run-command.h" #include "hook.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "object.h" #include "path.h" #include "submodule.h" @@ -376,7 +376,8 @@ int ref_resolves_to_object(const char *refname, { if (flags & REF_ISBROKEN) return 0; - if (!has_object(repo, oid, HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) { + if (!odb_has_object(repo->objects, oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) { error(_("%s does not point to a valid object!"), refname); return 0; } @@ -438,9 +439,9 @@ static int for_each_filter_refs(const char *refname, const char *referent, struct warn_if_dangling_data { struct ref_store *refs; FILE *fp; - const char *refname; const struct string_list *refnames; - const char *msg_fmt; + const char *indent; + int dry_run; }; static int warn_if_dangling_symref(const char *refname, const char *referent UNUSED, @@ -448,44 +449,34 @@ static int warn_if_dangling_symref(const char *refname, const char *referent UNU int flags, void *cb_data) { struct warn_if_dangling_data *d = cb_data; - const char *resolves_to; + const char *resolves_to, *msg; if (!(flags & REF_ISSYMREF)) return 0; resolves_to = refs_resolve_ref_unsafe(d->refs, refname, 0, NULL, NULL); if (!resolves_to - || (d->refname - ? strcmp(resolves_to, d->refname) - : !string_list_has_string(d->refnames, resolves_to))) { + || !string_list_has_string(d->refnames, resolves_to)) { return 0; } - fprintf(d->fp, d->msg_fmt, refname); - fputc('\n', d->fp); + msg = d->dry_run + ? _("%s%s will become dangling after %s is deleted\n") + : _("%s%s has become dangling after %s was deleted\n"); + fprintf(d->fp, msg, d->indent, refname, resolves_to); return 0; } -void refs_warn_dangling_symref(struct ref_store *refs, FILE *fp, - const char *msg_fmt, const char *refname) -{ - struct warn_if_dangling_data data = { - .refs = refs, - .fp = fp, - .refname = refname, - .msg_fmt = msg_fmt, - }; - refs_for_each_rawref(refs, warn_if_dangling_symref, &data); -} - void refs_warn_dangling_symrefs(struct ref_store *refs, FILE *fp, - const char *msg_fmt, const struct string_list *refnames) + const char *indent, int dry_run, + const struct string_list *refnames) { struct warn_if_dangling_data data = { .refs = refs, .fp = fp, .refnames = refnames, - .msg_fmt = msg_fmt, + .indent = indent, + .dry_run = dry_run, }; refs_for_each_rawref(refs, warn_if_dangling_symref, &data); } @@ -636,10 +627,12 @@ void expand_ref_prefix(struct strvec *prefixes, const char *prefix) strvec_pushf(prefixes, *p, len, prefix); } +#ifndef WITH_BREAKING_CHANGES static const char default_branch_name_advice[] = N_( "Using '%s' as the name for the initial branch. This default branch name\n" -"is subject to change. To configure the initial branch name to use in all\n" -"of your new repositories, which will suppress this warning, call:\n" +"will change to \"main\" in Git 3.0. To configure the initial branch name\n" +"to use in all of your new repositories, which will suppress this warning,\n" +"call:\n" "\n" "\tgit config --global init.defaultBranch <name>\n" "\n" @@ -648,6 +641,15 @@ static const char default_branch_name_advice[] = N_( "\n" "\tgit branch -m <name>\n" ); +#else +static const char default_branch_name_advice[] = N_( +"Using '%s' as the name for the initial branch since Git 3.0.\n" +"If you expected Git to create 'master', the just-created\n" +"branch can be renamed via this command:\n" +"\n" +"\tgit branch -m master\n" +); +#endif /* WITH_BREAKING_CHANGES */ char *repo_default_branch_name(struct repository *r, int quiet) { @@ -658,11 +660,15 @@ char *repo_default_branch_name(struct repository *r, int quiet) if (env && *env) ret = xstrdup(env); - else if (repo_config_get_string(r, config_key, &ret) < 0) + if (!ret && repo_config_get_string(r, config_key, &ret) < 0) die(_("could not retrieve `%s`"), config_display_key); if (!ret) { +#ifdef WITH_BREAKING_CHANGES + ret = xstrdup("main"); +#else ret = xstrdup("master"); +#endif /* WITH_BREAKING_CHANGES */ if (!quiet) advise_if_enabled(ADVICE_DEFAULT_BRANCH_NAME, _(default_branch_name_advice), ret); @@ -954,7 +960,7 @@ long get_files_ref_lock_timeout_ms(void) static int timeout_ms = 100; if (!configured) { - git_config_get_int("core.filesreflocktimeout", &timeout_ms); + repo_config_get_int(the_repository, "core.filesreflocktimeout", &timeout_ms); configured = 1; } @@ -1031,7 +1037,6 @@ int is_branch(const char *refname) } struct read_ref_at_cb { - const char *refname; timestamp_t at_time; int cnt; int reccnt; @@ -1061,7 +1066,8 @@ static void set_read_ref_cutoffs(struct read_ref_at_cb *cb, *cb->cutoff_cnt = cb->reccnt; } -static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid, +static int read_ref_at_ent(const char *refname, + struct object_id *ooid, struct object_id *noid, const char *email UNUSED, timestamp_t timestamp, int tz, const char *message, void *cb_data) @@ -1081,14 +1087,13 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid, oidcpy(cb->oid, noid); if (!oideq(&cb->ooid, noid)) warning(_("log for ref %s has gap after %s"), - cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822))); + refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822))); } else if (cb->date == cb->at_time) oidcpy(cb->oid, noid); else if (!oideq(noid, cb->oid)) warning(_("log for ref %s unexpectedly ended on %s"), - cb->refname, show_date(cb->date, cb->tz, - DATE_MODE(RFC2822))); + refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822))); cb->reccnt++; oidcpy(&cb->ooid, ooid); oidcpy(&cb->noid, noid); @@ -1103,7 +1108,8 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid, return 0; } -static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid, +static int read_ref_at_ent_oldest(const char *refname UNUSED, + struct object_id *ooid, struct object_id *noid, const char *email UNUSED, timestamp_t timestamp, int tz, const char *message, void *cb_data) @@ -1126,7 +1132,6 @@ int read_ref_at(struct ref_store *refs, const char *refname, struct read_ref_at_cb cb; memset(&cb, 0, sizeof(cb)); - cb.refname = refname; cb.at_time = at_time; cb.cnt = cnt; cb.msg = msg; @@ -1371,27 +1376,22 @@ int ref_transaction_update(struct ref_transaction *transaction, return 0; } -/* - * Similar to`ref_transaction_update`, but this function is only for adding - * a reflog update. Supports providing custom committer information. The index - * field can be utiltized to order updates as desired. When not used, the - * updates default to being ordered by refname. - */ -static int ref_transaction_update_reflog(struct ref_transaction *transaction, - const char *refname, - const struct object_id *new_oid, - const struct object_id *old_oid, - const char *committer_info, - unsigned int flags, - const char *msg, - uint64_t index, - struct strbuf *err) +int ref_transaction_update_reflog(struct ref_transaction *transaction, + const char *refname, + const struct object_id *new_oid, + const struct object_id *old_oid, + const char *committer_info, + const char *msg, + uint64_t index, + struct strbuf *err) { struct ref_update *update; + unsigned int flags; assert(err); - flags |= REF_LOG_ONLY | REF_FORCE_CREATE_REFLOG | REF_NO_DEREF; + flags = REF_HAVE_OLD | REF_HAVE_NEW | REF_LOG_ONLY | REF_FORCE_CREATE_REFLOG | REF_NO_DEREF | + REF_LOG_USE_PROVIDED_OIDS; if (!transaction_refname_valid(refname, new_oid, flags, err)) return -1; @@ -1399,11 +1399,6 @@ static int ref_transaction_update_reflog(struct ref_transaction *transaction, update = ref_transaction_add_update(transaction, refname, flags, new_oid, old_oid, NULL, NULL, committer_info, msg); - /* - * While we do set the old_oid value, we unset the flag to skip - * old_oid verification which only makes sense for refs. - */ - update->flags &= ~REF_HAVE_OLD; update->index = index; /* @@ -1859,7 +1854,13 @@ int refs_for_each_namespaced_ref(struct ref_store *refs, int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data) { - return do_for_each_ref(refs, "", NULL, fn, 0, + return refs_for_each_rawref_in(refs, "", fn, cb_data); +} + +int refs_for_each_rawref_in(struct ref_store *refs, const char *prefix, + each_ref_fn fn, void *cb_data) +{ + return do_for_each_ref(refs, prefix, NULL, fn, 0, DO_FOR_EACH_INCLUDE_BROKEN, cb_data); } @@ -2477,7 +2478,7 @@ int ref_transaction_prepare(struct ref_transaction *transaction, break; } - if (refs->repo->objects->odb->disable_ref_updates) { + if (refs->repo->objects->sources->disable_ref_updates) { strbuf_addstr(err, _("ref updates forbidden inside quarantine environment")); return -1; @@ -2666,12 +2667,12 @@ enum ref_transaction_error refs_verify_refnames_available(struct ref_store *refs if (!initial_transaction) { int ok; - if (!iter) { + if (!iter) iter = refs_ref_iterator_begin(refs, dirname.buf, NULL, 0, DO_FOR_EACH_INCLUDE_BROKEN); - } else if (ref_iterator_seek(iter, dirname.buf) < 0) { + else if (ref_iterator_seek(iter, dirname.buf, + REF_ITERATOR_SEEK_SET_PREFIX) < 0) goto cleanup; - } while ((ok = ref_iterator_advance(iter)) == ITER_OK) { if (skip && @@ -2960,7 +2961,8 @@ struct migration_data { struct ref_store *old_refs; struct ref_transaction *transaction; struct strbuf *errbuf; - struct strbuf sb; + struct strbuf sb, name, mail; + uint64_t index; }; static int migrate_one_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid, @@ -2993,50 +2995,41 @@ done: return ret; } -struct reflog_migration_data { - uint64_t index; - const char *refname; - struct ref_store *old_refs; - struct ref_transaction *transaction; - struct strbuf *errbuf; - struct strbuf *sb; -}; - -static int migrate_one_reflog_entry(struct object_id *old_oid, +static int migrate_one_reflog_entry(const char *refname, + struct object_id *old_oid, struct object_id *new_oid, const char *committer, timestamp_t timestamp, int tz, const char *msg, void *cb_data) { - struct reflog_migration_data *data = cb_data; + struct migration_data *data = cb_data; + struct ident_split ident; const char *date; int ret; + if (split_ident_line(&ident, committer, strlen(committer)) < 0) + return -1; + + strbuf_reset(&data->name); + strbuf_add(&data->name, ident.name_begin, ident.name_end - ident.name_begin); + strbuf_reset(&data->mail); + strbuf_add(&data->mail, ident.mail_begin, ident.mail_end - ident.mail_begin); + date = show_date(timestamp, tz, DATE_MODE(NORMAL)); - strbuf_reset(data->sb); - /* committer contains name and email */ - strbuf_addstr(data->sb, fmt_ident("", committer, WANT_BLANK_IDENT, date, 0)); - - ret = ref_transaction_update_reflog(data->transaction, data->refname, - new_oid, old_oid, data->sb->buf, - REF_HAVE_NEW | REF_HAVE_OLD, msg, - data->index++, data->errbuf); + strbuf_reset(&data->sb); + strbuf_addstr(&data->sb, fmt_ident(data->name.buf, data->mail.buf, WANT_BLANK_IDENT, date, 0)); + + ret = ref_transaction_update_reflog(data->transaction, refname, + new_oid, old_oid, data->sb.buf, + msg, data->index++, data->errbuf); return ret; } static int migrate_one_reflog(const char *refname, void *cb_data) { struct migration_data *migration_data = cb_data; - struct reflog_migration_data data = { - .refname = refname, - .old_refs = migration_data->old_refs, - .transaction = migration_data->transaction, - .errbuf = migration_data->errbuf, - .sb = &migration_data->sb, - }; - return refs_for_each_reflog_ent(migration_data->old_refs, refname, - migrate_one_reflog_entry, &data); + migrate_one_reflog_entry, migration_data); } static int move_files(const char *from_path, const char *to_path, struct strbuf *errbuf) @@ -3131,6 +3124,8 @@ int repo_migrate_ref_storage_format(struct repository *repo, struct strbuf new_gitdir = STRBUF_INIT; struct migration_data data = { .sb = STRBUF_INIT, + .name = STRBUF_INIT, + .mail = STRBUF_INIT, }; int did_migrate_refs = 0; int ret; @@ -3306,11 +3301,36 @@ done: ref_transaction_free(transaction); strbuf_release(&new_gitdir); strbuf_release(&data.sb); + strbuf_release(&data.name); + strbuf_release(&data.mail); return ret; } int ref_update_expects_existing_old_ref(struct ref_update *update) { + if (update->flags & REF_LOG_ONLY) + return 0; + return (update->flags & REF_HAVE_OLD) && (!is_null_oid(&update->old_oid) || update->old_target); } + +const char *ref_transaction_error_msg(enum ref_transaction_error err) +{ + switch (err) { + case REF_TRANSACTION_ERROR_NAME_CONFLICT: + return "refname conflict"; + case REF_TRANSACTION_ERROR_CREATE_EXISTS: + return "reference already exists"; + case REF_TRANSACTION_ERROR_NONEXISTENT_REF: + return "reference does not exist"; + case REF_TRANSACTION_ERROR_INCORRECT_OLD_VALUE: + return "incorrect old value provided"; + case REF_TRANSACTION_ERROR_INVALID_NEW_VALUE: + return "invalid new value provided"; + case REF_TRANSACTION_ERROR_EXPECTED_SYMREF: + return "expected symref but found regular ref"; + default: + return "unknown failure"; + } +} @@ -428,6 +428,8 @@ int refs_for_each_namespaced_ref(struct ref_store *refs, /* can be used to learn about broken ref and symref */ int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data); +int refs_for_each_rawref_in(struct ref_store *refs, const char *prefix, + each_ref_fn fn, void *cb_data); /* * Iterates over all refs including root refs, i.e. pseudorefs and HEAD. @@ -452,10 +454,9 @@ static inline const char *has_glob_specials(const char *pattern) return strpbrk(pattern, "?*["); } -void refs_warn_dangling_symref(struct ref_store *refs, FILE *fp, - const char *msg_fmt, const char *refname); void refs_warn_dangling_symrefs(struct ref_store *refs, FILE *fp, - const char *msg_fmt, const struct string_list *refnames); + const char *indent, int dry_run, + const struct string_list *refnames); /* * Flags for controlling behaviour of pack_refs() @@ -559,10 +560,13 @@ int refs_delete_reflog(struct ref_store *refs, const char *refname); * The cb_data is a caller-supplied pointer given to the iterator * functions. */ -typedef int each_reflog_ent_fn( - struct object_id *old_oid, struct object_id *new_oid, - const char *committer, timestamp_t timestamp, - int tz, const char *msg, void *cb_data); +typedef int each_reflog_ent_fn(const char *refname, + struct object_id *old_oid, + struct object_id *new_oid, + const char *committer, + timestamp_t timestamp, + int tz, const char *msg, + void *cb_data); /* Iterate over reflog entries in the log for `refname`. */ @@ -761,12 +765,19 @@ struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs, #define REF_SKIP_CREATE_REFLOG (1 << 12) /* + * When writing a REF_LOG_ONLY record, use the old and new object IDs provided + * in the update instead of resolving the old object ID. The caller must also + * set both REF_HAVE_OLD and REF_HAVE_NEW. + */ +#define REF_LOG_USE_PROVIDED_OIDS (1 << 13) + +/* * Bitmask of all of the flags that are allowed to be passed in to * ref_transaction_update() and friends: */ #define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS \ (REF_NO_DEREF | REF_FORCE_CREATE_REFLOG | REF_SKIP_OID_VERIFICATION | \ - REF_SKIP_REFNAME_VERIFICATION | REF_SKIP_CREATE_REFLOG) + REF_SKIP_REFNAME_VERIFICATION | REF_SKIP_CREATE_REFLOG | REF_LOG_USE_PROVIDED_OIDS) /* * Add a reference update to transaction. `new_oid` is the value that @@ -796,6 +807,21 @@ int ref_transaction_update(struct ref_transaction *transaction, struct strbuf *err); /* + * Similar to `ref_transaction_update`, but this function is only for adding + * a reflog update. Supports providing custom committer information. The index + * field can be utiltized to order updates as desired. When set to zero, the + * updates default to being ordered by refname. + */ +int ref_transaction_update_reflog(struct ref_transaction *transaction, + const char *refname, + const struct object_id *new_oid, + const struct object_id *old_oid, + const char *committer_info, + const char *msg, + uint64_t index, + struct strbuf *err); + +/* * Add a reference creation to transaction. new_oid is the value that * the reference should have after the update; it must not be * null_oid. It is verified that the reference does not exist @@ -908,6 +934,11 @@ void ref_transaction_for_each_rejected_update(struct ref_transaction *transactio void *cb_data); /* + * Translate errors to human readable error messages. + */ +const char *ref_transaction_error_msg(enum ref_transaction_error err); + +/* * Free `*transaction` and all associated data. */ void ref_transaction_free(struct ref_transaction *transaction); @@ -1190,4 +1221,159 @@ int repo_migrate_ref_storage_format(struct repository *repo, unsigned int flags, struct strbuf *err); +/* + * Reference iterators + * + * A reference iterator encapsulates the state of an in-progress + * iteration over references. Create an instance of `struct + * ref_iterator` via one of the functions in this module. + * + * A freshly-created ref_iterator doesn't yet point at a reference. To + * advance the iterator, call ref_iterator_advance(). If successful, + * this sets the iterator's refname, oid, and flags fields to describe + * the next reference and returns ITER_OK. The data pointed at by + * refname and oid belong to the iterator; if you want to retain them + * after calling ref_iterator_advance() again or calling + * ref_iterator_free(), you must make a copy. When the iteration has + * been exhausted, ref_iterator_advance() releases any resources + * associated with the iteration, frees the ref_iterator object, and + * returns ITER_DONE. If you want to abort the iteration early, call + * ref_iterator_free(), which also frees the ref_iterator object and + * any associated resources. If there was an internal error advancing + * to the next entry, ref_iterator_advance() aborts the iteration, + * frees the ref_iterator, and returns ITER_ERROR. + * + * The reference currently being looked at can be peeled by calling + * ref_iterator_peel(). This function is often faster than peel_ref(), + * so it should be preferred when iterating over references. + * + * Putting it all together, a typical iteration looks like this: + * + * int ok; + * struct ref_iterator *iter = ...; + * + * while ((ok = ref_iterator_advance(iter)) == ITER_OK) { + * if (want_to_stop_iteration()) { + * ok = ITER_DONE; + * break; + * } + * + * // Access information about the current reference: + * if (!(iter->flags & REF_ISSYMREF)) + * printf("%s is %s\n", iter->refname, oid_to_hex(iter->oid)); + * + * // If you need to peel the reference: + * ref_iterator_peel(iter, &oid); + * } + * + * if (ok != ITER_DONE) + * handle_error(); + * ref_iterator_free(iter); + */ +struct ref_iterator; + +/* + * These flags are passed to refs_ref_iterator_begin() (and do_for_each_ref(), + * which feeds it). + */ +enum do_for_each_ref_flags { + /* + * Include broken references in a do_for_each_ref*() iteration, which + * would normally be omitted. This includes both refs that point to + * missing objects (a true repository corruption), ones with illegal + * names (which we prefer not to expose to callers), as well as + * dangling symbolic refs (i.e., those that point to a non-existent + * ref; this is not a corruption, but as they have no valid oid, we + * omit them from normal iteration results). + */ + DO_FOR_EACH_INCLUDE_BROKEN = (1 << 0), + + /* + * Only include per-worktree refs in a do_for_each_ref*() iteration. + * Normally this will be used with a files ref_store, since that's + * where all reference backends will presumably store their + * per-worktree refs. + */ + DO_FOR_EACH_PER_WORKTREE_ONLY = (1 << 1), + + /* + * Omit dangling symrefs from output; this only has an effect with + * INCLUDE_BROKEN, since they are otherwise not included at all. + */ + DO_FOR_EACH_OMIT_DANGLING_SYMREFS = (1 << 2), + + /* + * Include root refs i.e. HEAD and pseudorefs along with the regular + * refs. + */ + DO_FOR_EACH_INCLUDE_ROOT_REFS = (1 << 3), +}; + +/* + * Return an iterator that goes over each reference in `refs` for + * which the refname begins with prefix. If trim is non-zero, then + * trim that many characters off the beginning of each refname. + * The output is ordered by refname. + */ +struct ref_iterator *refs_ref_iterator_begin( + struct ref_store *refs, + const char *prefix, const char **exclude_patterns, + int trim, enum do_for_each_ref_flags flags); + +/* + * Advance the iterator to the first or next item and return ITER_OK. + * If the iteration is exhausted, free the resources associated with + * the ref_iterator and return ITER_DONE. On errors, free the iterator + * resources and return ITER_ERROR. It is a bug to use ref_iterator or + * call this function again after it has returned ITER_DONE or + * ITER_ERROR. + */ +int ref_iterator_advance(struct ref_iterator *ref_iterator); + +enum ref_iterator_seek_flag { + /* + * When the REF_ITERATOR_SEEK_SET_PREFIX flag is set, the iterator's prefix is + * updated to match the provided string, affecting all subsequent iterations. If + * not, the iterator seeks to the specified reference and clears any previously + * set prefix. + */ + REF_ITERATOR_SEEK_SET_PREFIX = (1 << 0), +}; + +/* + * Seek the iterator to the first reference matching the given seek string. + * The seek string is matched as a literal string, without regard for path + * separators. If seek is NULL or the empty string, seek the iterator to the + * first reference again. + * + * This function is expected to behave as if a new ref iterator has been + * created, but allows reuse of existing iterators for optimization. + * + * Returns 0 on success, a negative error code otherwise. + */ +int ref_iterator_seek(struct ref_iterator *ref_iterator, const char *refname, + unsigned int flags); + +/* + * If possible, peel the reference currently being viewed by the + * iterator. Return 0 on success. + */ +int ref_iterator_peel(struct ref_iterator *ref_iterator, + struct object_id *peeled); + +/* Free the reference iterator and any associated resources. */ +void ref_iterator_free(struct ref_iterator *ref_iterator); + +/* + * The common backend for the for_each_*ref* functions. Call fn for + * each reference in iter. If the iterator itself ever returns + * ITER_ERROR, return -1. If fn ever returns a non-zero value, stop + * the iteration and return that value. Otherwise, return 0. In any + * case, free the iterator when done. This function is basically an + * adapter between the callback style of reference iteration and the + * iterator style. + */ +int do_for_each_ref_iterator(struct ref_iterator *iter, + each_ref_fn fn, void *cb_data); + #endif /* REFS_H */ diff --git a/refs/debug.c b/refs/debug.c index 485e3079d7..1cb955961e 100644 --- a/refs/debug.c +++ b/refs/debug.c @@ -170,12 +170,13 @@ static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator) } static int debug_ref_iterator_seek(struct ref_iterator *ref_iterator, - const char *prefix) + const char *refname, unsigned int flags) { struct debug_ref_iterator *diter = (struct debug_ref_iterator *)ref_iterator; - int res = diter->iter->vtable->seek(diter->iter, prefix); - trace_printf_key(&trace_refs, "iterator_seek: %s: %d\n", prefix ? prefix : "", res); + int res = diter->iter->vtable->seek(diter->iter, refname, flags); + trace_printf_key(&trace_refs, "iterator_seek: %s flags: %d: %d\n", + refname ? refname : "", flags, res); return res; } @@ -276,7 +277,8 @@ struct debug_reflog { void *cb_data; }; -static int debug_print_reflog_ent(struct object_id *old_oid, +static int debug_print_reflog_ent(const char *refname, + struct object_id *old_oid, struct object_id *new_oid, const char *committer, timestamp_t timestamp, int tz, const char *msg, void *cb_data) @@ -291,7 +293,7 @@ static int debug_print_reflog_ent(struct object_id *old_oid, if (new_oid) oid_to_hex_r(n, new_oid); - ret = dbg->fn(old_oid, new_oid, committer, timestamp, tz, msg, + ret = dbg->fn(refname, old_oid, new_oid, committer, timestamp, tz, msg, dbg->cb_data); trace_printf_key(&trace_refs, "reflog_ent %s (ret %d): %s -> %s, %s %ld \"%.*s\"\n", diff --git a/refs/files-backend.c b/refs/files-backend.c index bf6f89b1d1..1b3bf26add 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -68,6 +68,12 @@ */ #define REF_DELETED_RMDIR (1 << 9) +/* + * Used to indicate that the reflog-only update has been created via + * `split_head_update()`. + */ +#define REF_LOG_VIA_SPLIT (1 << 14) + struct ref_lock { char *ref_name; struct lock_file lk; @@ -929,11 +935,11 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator) } static int files_ref_iterator_seek(struct ref_iterator *ref_iterator, - const char *prefix) + const char *refname, unsigned int flags) { struct files_ref_iterator *iter = (struct files_ref_iterator *)ref_iterator; - return ref_iterator_seek(iter->iter0, prefix); + return ref_iterator_seek(iter->iter0, refname, flags); } static int files_ref_iterator_peel(struct ref_iterator *ref_iterator, @@ -2109,7 +2115,9 @@ static int files_delete_reflog(struct ref_store *ref_store, return ret; } -static int show_one_reflog_ent(struct files_ref_store *refs, struct strbuf *sb, +static int show_one_reflog_ent(struct files_ref_store *refs, + const char *refname, + struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data) { struct object_id ooid, noid; @@ -2136,7 +2144,7 @@ static int show_one_reflog_ent(struct files_ref_store *refs, struct strbuf *sb, message += 6; else message += 7; - return fn(&ooid, &noid, p, timestamp, tz, message, cb_data); + return fn(refname, &ooid, &noid, p, timestamp, tz, message, cb_data); } static char *find_beginning_of_line(char *bob, char *scan) @@ -2220,7 +2228,7 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store, strbuf_splice(&sb, 0, 0, bp + 1, endp - (bp + 1)); scanp = bp; endp = bp + 1; - ret = show_one_reflog_ent(refs, &sb, fn, cb_data); + ret = show_one_reflog_ent(refs, refname, &sb, fn, cb_data); strbuf_reset(&sb); if (ret) break; @@ -2232,7 +2240,7 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store, * Process it, and we can end the loop. */ strbuf_splice(&sb, 0, 0, buf, endp - buf); - ret = show_one_reflog_ent(refs, &sb, fn, cb_data); + ret = show_one_reflog_ent(refs, refname, &sb, fn, cb_data); strbuf_reset(&sb); break; } @@ -2282,7 +2290,7 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store, return -1; while (!ret && !strbuf_getwholeline(&sb, logfp, '\n')) - ret = show_one_reflog_ent(refs, &sb, fn, cb_data); + ret = show_one_reflog_ent(refs, refname, &sb, fn, cb_data); fclose(logfp); strbuf_release(&sb); return ret; @@ -2316,7 +2324,8 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator) } static int files_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED, - const char *prefix UNUSED) + const char *refname UNUSED, + unsigned int flags UNUSED) { BUG("ref_iterator_seek() called for reflog_iterator"); } @@ -2420,9 +2429,10 @@ static enum ref_transaction_error split_head_update(struct ref_update *update, new_update = ref_transaction_add_update( transaction, "HEAD", - update->flags | REF_LOG_ONLY | REF_NO_DEREF, + update->flags | REF_LOG_ONLY | REF_NO_DEREF | REF_LOG_VIA_SPLIT, &update->new_oid, &update->old_oid, NULL, NULL, update->committer_info, update->msg); + new_update->parent_update = update; /* * Add "HEAD". This insertion is O(N) in the transaction @@ -2493,7 +2503,6 @@ static enum ref_transaction_error split_symref_update(struct ref_update *update, * done when new_update is processed. */ update->flags |= REF_LOG_ONLY | REF_NO_DEREF; - update->flags &= ~REF_HAVE_OLD; return 0; } @@ -2506,12 +2515,37 @@ static enum ref_transaction_error split_symref_update(struct ref_update *update, */ static enum ref_transaction_error check_old_oid(struct ref_update *update, struct object_id *oid, + struct strbuf *referent, struct strbuf *err) { - if (!(update->flags & REF_HAVE_OLD) || - oideq(oid, &update->old_oid)) + if (update->flags & REF_LOG_ONLY || + !(update->flags & REF_HAVE_OLD)) return 0; + if (oideq(oid, &update->old_oid)) { + /* + * Normally matching the expected old oid is enough. Either we + * found the ref at the expected state, or we are creating and + * expect the null oid (and likewise found nothing). + * + * But there is one exception for the null oid: if we found a + * symref pointing to nothing we'll also get the null oid. In + * regular recursive mode, that's good (we'll write to what the + * symref points to, which doesn't exist). But in no-deref + * mode, it means we'll clobber the symref, even though the + * caller asked for this to be a creation event. So flag + * that case to preserve the dangling symref. + */ + if ((update->flags & REF_NO_DEREF) && referent->len && + is_null_oid(oid)) { + strbuf_addf(err, "cannot lock ref '%s': " + "dangling symref already exists", + ref_update_original_update_refname(update)); + return REF_TRANSACTION_ERROR_CREATE_EXISTS; + } + return 0; + } + if (is_null_oid(&update->old_oid)) { strbuf_addf(err, "cannot lock ref '%s': " "reference already exists", @@ -2600,7 +2634,36 @@ static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *re update->backend_data = lock; - if (update->type & REF_ISSYMREF) { + if (update->flags & REF_LOG_VIA_SPLIT) { + struct ref_lock *parent_lock; + + if (!update->parent_update) + BUG("split update without a parent"); + + parent_lock = update->parent_update->backend_data; + + /* + * Check that "HEAD" didn't racily change since we have looked + * it up. If it did we must refuse to write the reflog entry. + * + * Note that this does not catch all races: if "HEAD" was + * racily changed to point to one of the refs part of the + * transaction then we would miss writing the split reflog + * entry for "HEAD". + */ + if (!(update->type & REF_ISSYMREF) || + strcmp(update->parent_update->refname, referent.buf)) { + strbuf_addstr(err, "HEAD has been racily updated"); + ret = REF_TRANSACTION_ERROR_GENERIC; + goto out; + } + + if (update->flags & REF_HAVE_OLD) { + oidcpy(&lock->old_oid, &update->old_oid); + } else { + oidcpy(&lock->old_oid, &parent_lock->old_oid); + } + } else if (update->type & REF_ISSYMREF) { if (update->flags & REF_NO_DEREF) { /* * We won't be reading the referent as part of @@ -2622,7 +2685,8 @@ static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *re if (update->old_target) ret = ref_update_check_old_target(referent.buf, update, err); else - ret = check_old_oid(update, &lock->old_oid, err); + ret = check_old_oid(update, &lock->old_oid, + &referent, err); if (ret) goto out; } else { @@ -2654,7 +2718,8 @@ static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *re ret = REF_TRANSACTION_ERROR_EXPECTED_SYMREF; goto out; } else { - ret = check_old_oid(update, &lock->old_oid, err); + ret = check_old_oid(update, &lock->old_oid, + &referent, err); if (ret) { goto out; } @@ -2760,6 +2825,8 @@ static void files_transaction_cleanup(struct files_ref_store *refs, if (lock) { unlock_ref(lock); + try_remove_empty_parents(refs, update->refname, + REMOVE_EMPTY_PARENTS_REF); update->backend_data = NULL; } } @@ -2974,6 +3041,20 @@ static int parse_and_write_reflog(struct files_ref_store *refs, struct ref_lock *lock, struct strbuf *err) { + struct object_id *old_oid = &lock->old_oid; + + if (update->flags & REF_LOG_USE_PROVIDED_OIDS) { + if (!(update->flags & REF_HAVE_OLD) || + !(update->flags & REF_HAVE_NEW) || + !(update->flags & REF_LOG_ONLY)) { + strbuf_addf(err, _("trying to write reflog for '%s'" + "with incomplete values"), update->refname); + return REF_TRANSACTION_ERROR_GENERIC; + } + + old_oid = &update->old_oid; + } + if (update->new_target) { /* * We want to get the resolved OID for the target, to ensure @@ -2991,7 +3072,7 @@ static int parse_and_write_reflog(struct files_ref_store *refs, } } - if (files_log_ref_write(refs, lock->ref_name, &lock->old_oid, + if (files_log_ref_write(refs, lock->ref_name, old_oid, &update->new_oid, update->committer_info, update->msg, update->flags, err)) { char *old_msg = strbuf_detach(err, NULL); @@ -3059,7 +3140,8 @@ static int files_transaction_finish_initial(struct files_ref_store *refs, for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; - if ((update->flags & REF_HAVE_OLD) && + if (!(update->flags & REF_LOG_ONLY) && + (update->flags & REF_HAVE_OLD) && !is_null_oid(&update->old_oid)) BUG("initial ref transaction with old_sha1 set"); @@ -3208,6 +3290,10 @@ static int files_transaction_finish(struct ref_store *ref_store, */ for (i = 0; i < transaction->nr; i++) { struct ref_update *update = transaction->updates[i]; + + if (update->rejection_err) + continue; + if (update->flags & REF_DELETING && !(update->flags & REF_LOG_ONLY) && !(update->flags & REF_IS_PRUNING)) { @@ -3239,6 +3325,9 @@ static int files_transaction_finish(struct ref_store *ref_store, struct ref_update *update = transaction->updates[i]; struct ref_lock *lock = update->backend_data; + if (update->rejection_err) + continue; + if (update->flags & REF_DELETING && !(update->flags & REF_LOG_ONLY)) { update->flags |= REF_DELETED_RMDIR; @@ -3299,7 +3388,8 @@ struct expire_reflog_cb { dry_run:1; }; -static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid, +static int expire_reflog_ent(const char *refname UNUSED, + struct object_id *ooid, struct object_id *noid, const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { diff --git a/refs/iterator.c b/refs/iterator.c index 766d96e795..17ef841d8a 100644 --- a/refs/iterator.c +++ b/refs/iterator.c @@ -15,10 +15,10 @@ int ref_iterator_advance(struct ref_iterator *ref_iterator) return ref_iterator->vtable->advance(ref_iterator); } -int ref_iterator_seek(struct ref_iterator *ref_iterator, - const char *prefix) +int ref_iterator_seek(struct ref_iterator *ref_iterator, const char *refname, + unsigned int flags) { - return ref_iterator->vtable->seek(ref_iterator, prefix); + return ref_iterator->vtable->seek(ref_iterator, refname, flags); } int ref_iterator_peel(struct ref_iterator *ref_iterator, @@ -57,7 +57,8 @@ static int empty_ref_iterator_advance(struct ref_iterator *ref_iterator UNUSED) } static int empty_ref_iterator_seek(struct ref_iterator *ref_iterator UNUSED, - const char *prefix UNUSED) + const char *refname UNUSED, + unsigned int flags UNUSED) { return 0; } @@ -224,7 +225,7 @@ error: } static int merge_ref_iterator_seek(struct ref_iterator *ref_iterator, - const char *prefix) + const char *refname, unsigned int flags) { struct merge_ref_iterator *iter = (struct merge_ref_iterator *)ref_iterator; @@ -234,11 +235,11 @@ static int merge_ref_iterator_seek(struct ref_iterator *ref_iterator, iter->iter0 = iter->iter0_owned; iter->iter1 = iter->iter1_owned; - ret = ref_iterator_seek(iter->iter0, prefix); + ret = ref_iterator_seek(iter->iter0, refname, flags); if (ret < 0) return ret; - ret = ref_iterator_seek(iter->iter1, prefix); + ret = ref_iterator_seek(iter->iter1, refname, flags); if (ret < 0) return ret; @@ -407,13 +408,16 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator) } static int prefix_ref_iterator_seek(struct ref_iterator *ref_iterator, - const char *prefix) + const char *refname, unsigned int flags) { struct prefix_ref_iterator *iter = (struct prefix_ref_iterator *)ref_iterator; - free(iter->prefix); - iter->prefix = xstrdup_or_null(prefix); - return ref_iterator_seek(iter->iter0, prefix); + + if (flags & REF_ITERATOR_SEEK_SET_PREFIX) { + free(iter->prefix); + iter->prefix = xstrdup_or_null(refname); + } + return ref_iterator_seek(iter->iter0, refname, flags); } static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator, diff --git a/refs/packed-backend.c b/refs/packed-backend.c index 7fd73a0e6d..a8c22a0a7f 100644 --- a/refs/packed-backend.c +++ b/refs/packed-backend.c @@ -1004,19 +1004,23 @@ static int packed_ref_iterator_advance(struct ref_iterator *ref_iterator) } static int packed_ref_iterator_seek(struct ref_iterator *ref_iterator, - const char *prefix) + const char *refname, unsigned int flags) { struct packed_ref_iterator *iter = (struct packed_ref_iterator *)ref_iterator; const char *start; - if (prefix && *prefix) - start = find_reference_location(iter->snapshot, prefix, 0); + if (refname && *refname) + start = find_reference_location(iter->snapshot, refname, 0); else start = iter->snapshot->start; - free(iter->prefix); - iter->prefix = xstrdup_or_null(prefix); + /* Unset any previously set prefix */ + FREE_AND_NULL(iter->prefix); + + if (flags & REF_ITERATOR_SEEK_SET_PREFIX) + iter->prefix = xstrdup_or_null(refname); + iter->pos = start; iter->eof = iter->snapshot->eof; @@ -1194,7 +1198,8 @@ static struct ref_iterator *packed_ref_iterator_begin( iter->repo = ref_store->repo; iter->flags = flags; - if (packed_ref_iterator_seek(&iter->base, prefix) < 0) { + if (packed_ref_iterator_seek(&iter->base, prefix, + REF_ITERATOR_SEEK_SET_PREFIX) < 0) { ref_iterator_free(&iter->base); return NULL; } @@ -1228,7 +1233,7 @@ int packed_refs_lock(struct ref_store *ref_store, int flags, struct strbuf *err) static int timeout_value = 1000; if (!timeout_configured) { - git_config_get_int("core.packedrefstimeout", &timeout_value); + repo_config_get_int(the_repository, "core.packedrefstimeout", &timeout_value); timeout_configured = 1; } diff --git a/refs/ref-cache.c b/refs/ref-cache.c index c1f1bab1d5..c180e0aad7 100644 --- a/refs/ref-cache.c +++ b/refs/ref-cache.c @@ -194,20 +194,6 @@ static struct ref_dir *find_containing_dir(struct ref_dir *dir, return dir; } -struct ref_entry *find_ref_entry(struct ref_dir *dir, const char *refname) -{ - int entry_index; - struct ref_entry *entry; - dir = find_containing_dir(dir, refname); - if (!dir) - return NULL; - entry_index = search_ref_dir(dir, refname, strlen(refname)); - if (entry_index == -1) - return NULL; - entry = dir->entries[entry_index]; - return (entry->flag & REF_DIR) ? NULL : entry; -} - /* * Emit a warning and return true iff ref1 and ref2 have the same name * and the same oid. Die if they have the same name but different @@ -448,11 +434,9 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator) } } -static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator, - const char *prefix) +static int cache_ref_iterator_set_prefix(struct cache_ref_iterator *iter, + const char *prefix) { - struct cache_ref_iterator *iter = - (struct cache_ref_iterator *)ref_iterator; struct cache_ref_iterator_level *level; struct ref_dir *dir; @@ -483,6 +467,84 @@ static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator, return 0; } +static int cache_ref_iterator_seek(struct ref_iterator *ref_iterator, + const char *refname, unsigned int flags) +{ + struct cache_ref_iterator *iter = + (struct cache_ref_iterator *)ref_iterator; + + if (flags & REF_ITERATOR_SEEK_SET_PREFIX) { + return cache_ref_iterator_set_prefix(iter, refname); + } else if (refname && *refname) { + struct cache_ref_iterator_level *level; + const char *slash = refname; + struct ref_dir *dir; + + dir = get_ref_dir(iter->cache->root); + + if (iter->prime_dir) + prime_ref_dir(dir, refname); + + iter->levels_nr = 1; + level = &iter->levels[0]; + level->index = -1; + level->dir = dir; + + /* Unset any previously set prefix */ + FREE_AND_NULL(iter->prefix); + + /* + * Breakdown the provided seek path and assign the correct + * indexing to each level as needed. + */ + do { + int idx; + size_t len; + int cmp = 0; + + sort_ref_dir(dir); + + slash = strchr(slash, '/'); + len = slash ? (size_t)(slash - refname) : strlen(refname); + + for (idx = 0; idx < dir->nr; idx++) { + cmp = strncmp(refname, dir->entries[idx]->name, len); + if (cmp <= 0) + break; + } + /* don't overflow the index */ + idx = idx >= dir->nr ? dir->nr - 1 : idx; + + if (slash) + slash = slash + 1; + + level->index = idx; + if (dir->entries[idx]->flag & REF_DIR) { + /* push down a level */ + dir = get_ref_dir(dir->entries[idx]); + + ALLOC_GROW(iter->levels, iter->levels_nr + 1, + iter->levels_alloc); + level = &iter->levels[iter->levels_nr++]; + level->dir = dir; + level->index = -1; + level->prefix_state = PREFIX_CONTAINS_DIR; + } else { + /* reduce the index so the leaf node is iterated over */ + if (cmp <= 0 && !slash) + level->index = idx - 1; + /* + * while the seek path may not be exhausted, our + * match is exhausted at a leaf node. + */ + break; + } + } while (slash); + } + + return 0; +} + static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator, struct object_id *peeled) { @@ -523,7 +585,8 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache, iter->cache = cache; iter->prime_dir = prime_dir; - if (cache_ref_iterator_seek(&iter->base, prefix) < 0) { + if (cache_ref_iterator_seek(&iter->base, prefix, + REF_ITERATOR_SEEK_SET_PREFIX) < 0) { ref_iterator_free(&iter->base); return NULL; } diff --git a/refs/ref-cache.h b/refs/ref-cache.h index 5f04e518c3..f635d2d824 100644 --- a/refs/ref-cache.h +++ b/refs/ref-cache.h @@ -202,13 +202,6 @@ void free_ref_cache(struct ref_cache *cache); void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry); /* - * Find the value entry with the given name in dir, sorting ref_dirs - * and recursing into subdirectories as necessary. If the name is not - * found or it corresponds to a directory entry, return NULL. - */ -struct ref_entry *find_ref_entry(struct ref_dir *dir, const char *refname); - -/* * Start iterating over references in `cache`. If `prefix` is * specified, only include references whose names start with that * prefix. If `prime_dir` is true, then fill any incomplete diff --git a/refs/refs-internal.h b/refs/refs-internal.h index f868870851..54c2079c12 100644 --- a/refs/refs-internal.h +++ b/refs/refs-internal.h @@ -244,90 +244,8 @@ const char *find_descendant_ref(const char *dirname, #define SYMREF_MAXDEPTH 5 /* - * These flags are passed to refs_ref_iterator_begin() (and do_for_each_ref(), - * which feeds it). - */ -enum do_for_each_ref_flags { - /* - * Include broken references in a do_for_each_ref*() iteration, which - * would normally be omitted. This includes both refs that point to - * missing objects (a true repository corruption), ones with illegal - * names (which we prefer not to expose to callers), as well as - * dangling symbolic refs (i.e., those that point to a non-existent - * ref; this is not a corruption, but as they have no valid oid, we - * omit them from normal iteration results). - */ - DO_FOR_EACH_INCLUDE_BROKEN = (1 << 0), - - /* - * Only include per-worktree refs in a do_for_each_ref*() iteration. - * Normally this will be used with a files ref_store, since that's - * where all reference backends will presumably store their - * per-worktree refs. - */ - DO_FOR_EACH_PER_WORKTREE_ONLY = (1 << 1), - - /* - * Omit dangling symrefs from output; this only has an effect with - * INCLUDE_BROKEN, since they are otherwise not included at all. - */ - DO_FOR_EACH_OMIT_DANGLING_SYMREFS = (1 << 2), - - /* - * Include root refs i.e. HEAD and pseudorefs along with the regular - * refs. - */ - DO_FOR_EACH_INCLUDE_ROOT_REFS = (1 << 3), -}; - -/* - * Reference iterators - * - * A reference iterator encapsulates the state of an in-progress - * iteration over references. Create an instance of `struct - * ref_iterator` via one of the functions in this module. - * - * A freshly-created ref_iterator doesn't yet point at a reference. To - * advance the iterator, call ref_iterator_advance(). If successful, - * this sets the iterator's refname, oid, and flags fields to describe - * the next reference and returns ITER_OK. The data pointed at by - * refname and oid belong to the iterator; if you want to retain them - * after calling ref_iterator_advance() again or calling - * ref_iterator_free(), you must make a copy. When the iteration has - * been exhausted, ref_iterator_advance() releases any resources - * associated with the iteration, frees the ref_iterator object, and - * returns ITER_DONE. If you want to abort the iteration early, call - * ref_iterator_free(), which also frees the ref_iterator object and - * any associated resources. If there was an internal error advancing - * to the next entry, ref_iterator_advance() aborts the iteration, - * frees the ref_iterator, and returns ITER_ERROR. - * - * The reference currently being looked at can be peeled by calling - * ref_iterator_peel(). This function is often faster than peel_ref(), - * so it should be preferred when iterating over references. - * - * Putting it all together, a typical iteration looks like this: - * - * int ok; - * struct ref_iterator *iter = ...; - * - * while ((ok = ref_iterator_advance(iter)) == ITER_OK) { - * if (want_to_stop_iteration()) { - * ok = ITER_DONE; - * break; - * } - * - * // Access information about the current reference: - * if (!(iter->flags & REF_ISSYMREF)) - * printf("%s is %s\n", iter->refname, oid_to_hex(iter->oid)); - * - * // If you need to peel the reference: - * ref_iterator_peel(iter, &oid); - * } - * - * if (ok != ITER_DONE) - * handle_error(); - * ref_iterator_free(iter); + * Data structure for holding a reference iterator. See refs.h for + * more details and usage instructions. */ struct ref_iterator { struct ref_iterator_vtable *vtable; @@ -338,42 +256,6 @@ struct ref_iterator { }; /* - * Advance the iterator to the first or next item and return ITER_OK. - * If the iteration is exhausted, free the resources associated with - * the ref_iterator and return ITER_DONE. On errors, free the iterator - * resources and return ITER_ERROR. It is a bug to use ref_iterator or - * call this function again after it has returned ITER_DONE or - * ITER_ERROR. - */ -int ref_iterator_advance(struct ref_iterator *ref_iterator); - -/* - * Seek the iterator to the first reference with the given prefix. - * The prefix is matched as a literal string, without regard for path - * separators. If prefix is NULL or the empty string, seek the iterator to the - * first reference again. - * - * This function is expected to behave as if a new ref iterator with the same - * prefix had been created, but allows reuse of iterators and thus may allow - * the backend to optimize. Parameters other than the prefix that have been - * passed when creating the iterator will remain unchanged. - * - * Returns 0 on success, a negative error code otherwise. - */ -int ref_iterator_seek(struct ref_iterator *ref_iterator, - const char *prefix); - -/* - * If possible, peel the reference currently being viewed by the - * iterator. Return 0 on success. - */ -int ref_iterator_peel(struct ref_iterator *ref_iterator, - struct object_id *peeled); - -/* Free the reference iterator and any associated resources. */ -void ref_iterator_free(struct ref_iterator *ref_iterator); - -/* * An iterator over nothing (its first ref_iterator_advance() call * returns ITER_DONE). */ @@ -385,17 +267,6 @@ struct ref_iterator *empty_ref_iterator_begin(void); int is_empty_ref_iterator(struct ref_iterator *ref_iterator); /* - * Return an iterator that goes over each reference in `refs` for - * which the refname begins with prefix. If trim is non-zero, then - * trim that many characters off the beginning of each refname. - * The output is ordered by refname. - */ -struct ref_iterator *refs_ref_iterator_begin( - struct ref_store *refs, - const char *prefix, const char **exclude_patterns, - int trim, enum do_for_each_ref_flags flags); - -/* * A callback function used to instruct merge_ref_iterator how to * interleave the entries from iter0 and iter1. The function should * return one of the constants defined in enum iterator_selection. It @@ -482,11 +353,12 @@ void base_ref_iterator_init(struct ref_iterator *iter, typedef int ref_iterator_advance_fn(struct ref_iterator *ref_iterator); /* - * Seek the iterator to the first reference matching the given prefix. Should - * behave the same as if a new iterator was created with the same prefix. + * Seek the iterator to the first matching reference. If the + * REF_ITERATOR_SEEK_SET_PREFIX flag is set, it would behave the same as if a + * new iterator was created with the provided refname as prefix. */ typedef int ref_iterator_seek_fn(struct ref_iterator *ref_iterator, - const char *prefix); + const char *refname, unsigned int flags); /* * Peels the current ref, returning 0 for success or -1 for failure. @@ -520,18 +392,6 @@ struct ref_iterator_vtable { */ extern struct ref_iterator *current_ref_iter; -/* - * The common backend for the for_each_*ref* functions. Call fn for - * each reference in iter. If the iterator itself ever returns - * ITER_ERROR, return -1. If fn ever returns a non-zero value, stop - * the iteration and return that value. Otherwise, return 0. In any - * case, free the iterator when done. This function is basically an - * adapter between the callback style of reference iteration and the - * iterator style. - */ -int do_for_each_ref_iterator(struct ref_iterator *iter, - each_ref_fn fn, void *cb_data); - struct ref_store; /* refs backends */ @@ -802,7 +662,8 @@ enum ref_transaction_error ref_update_check_old_target(const char *referent, /* * Check if the ref must exist, this means that the old_oid or - * old_target is non NULL. + * old_target is non NULL. Log-only updates never require the old state to + * match. */ int ref_update_expects_existing_old_ref(struct ref_update *update); diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c index 4c3817f4ec..9e889da2ff 100644 --- a/refs/reftable-backend.c +++ b/refs/reftable-backend.c @@ -386,7 +386,7 @@ static struct ref_store *reftable_be_init(struct repository *repo, refs->write_options.lock_timeout_ms = 100; refs->write_options.fsync = reftable_be_fsync; - git_config(reftable_be_config, &refs->write_options); + repo_config(the_repository, reftable_be_config, &refs->write_options); /* * It is somewhat unfortunate that we have to mirror the default block @@ -719,15 +719,20 @@ static int reftable_ref_iterator_advance(struct ref_iterator *ref_iterator) } static int reftable_ref_iterator_seek(struct ref_iterator *ref_iterator, - const char *prefix) + const char *refname, unsigned int flags) { struct reftable_ref_iterator *iter = (struct reftable_ref_iterator *)ref_iterator; - free(iter->prefix); - iter->prefix = xstrdup_or_null(prefix); - iter->prefix_len = prefix ? strlen(prefix) : 0; - iter->err = reftable_iterator_seek_ref(&iter->iter, prefix); + /* Unset any previously set prefix */ + FREE_AND_NULL(iter->prefix); + iter->prefix_len = 0; + + if (flags & REF_ITERATOR_SEEK_SET_PREFIX) { + iter->prefix = xstrdup_or_null(refname); + iter->prefix_len = refname ? strlen(refname) : 0; + } + iter->err = reftable_iterator_seek_ref(&iter->iter, refname); return iter->err; } @@ -839,7 +844,8 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_ if (ret) goto done; - ret = reftable_ref_iterator_seek(&iter->base, prefix); + ret = reftable_ref_iterator_seek(&iter->base, prefix, + REF_ITERATOR_SEEK_SET_PREFIX); if (ret) goto done; @@ -1006,10 +1012,6 @@ static int prepare_transaction_update(struct write_transaction_table_arg **out, if (!arg) { struct reftable_addition *addition; - ret = reftable_stack_reload(be->stack); - if (ret) - return ret; - ret = reftable_stack_new_addition(&addition, be->stack, REFTABLE_STACK_NEW_ADDITION_RELOAD); if (ret) { @@ -1096,6 +1098,20 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor if (ret) return REF_TRANSACTION_ERROR_GENERIC; + if (u->flags & REF_LOG_USE_PROVIDED_OIDS) { + if (!(u->flags & REF_HAVE_OLD) || + !(u->flags & REF_HAVE_NEW) || + !(u->flags & REF_LOG_ONLY)) { + strbuf_addf(err, _("trying to write reflog for '%s'" + "with incomplete values"), u->refname); + return REF_TRANSACTION_ERROR_GENERIC; + } + + if (queue_transaction_update(refs, tx_data, u, &u->old_oid, err)) + return REF_TRANSACTION_ERROR_GENERIC; + return 0; + } + /* Verify that the new object ID is valid. */ if ((u->flags & REF_HAVE_NEW) && !is_null_oid(&u->new_oid) && !(u->flags & REF_SKIP_OID_VERIFICATION) && @@ -1180,8 +1196,6 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor if (ret > 0) { /* The reference does not exist, but we expected it to. */ strbuf_addf(err, _("cannot lock ref '%s': " - - "unable to resolve reference '%s'"), ref_update_original_update_refname(u), u->refname); return REF_TRANSACTION_ERROR_NONEXISTENT_REF; @@ -1235,13 +1249,8 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor new_update->parent_update = u; - /* - * Change the symbolic ref update to log only. Also, it - * doesn't need to check its old OID value, as that will be - * done when new_update is processed. - */ + /* Change the symbolic ref update to log only. */ u->flags |= REF_LOG_ONLY | REF_NO_DEREF; - u->flags &= ~REF_HAVE_OLD; } } @@ -1265,8 +1274,33 @@ static enum ref_transaction_error prepare_single_update(struct reftable_ref_stor ret = ref_update_check_old_target(referent->buf, u, err); if (ret) return ret; - } else if ((u->flags & REF_HAVE_OLD) && !oideq(¤t_oid, &u->old_oid)) { - if (is_null_oid(&u->old_oid)) { + } else if ((u->flags & (REF_LOG_ONLY | REF_HAVE_OLD)) == REF_HAVE_OLD) { + if (oideq(¤t_oid, &u->old_oid)) { + /* + * Normally matching the expected old oid is enough. Either we + * found the ref at the expected state, or we are creating and + * expect the null oid (and likewise found nothing). + * + * But there is one exception for the null oid: if we found a + * symref pointing to nothing we'll also get the null oid. In + * regular recursive mode, that's good (we'll write to what the + * symref points to, which doesn't exist). But in no-deref + * mode, it means we'll clobber the symref, even though the + * caller asked for this to be a creation event. So flag + * that case to preserve the dangling symref. + * + * Everything else is OK and we can fall through to the + * end of the conditional chain. + */ + if ((u->flags & REF_NO_DEREF) && + referent->len && + is_null_oid(&u->old_oid)) { + strbuf_addf(err, _("cannot lock ref '%s': " + "dangling symref already exists"), + ref_update_original_update_refname(u)); + return REF_TRANSACTION_ERROR_CREATE_EXISTS; + } + } else if (is_null_oid(&u->old_oid)) { strbuf_addf(err, _("cannot lock ref '%s': " "reference already exists"), ref_update_original_update_refname(u)); @@ -1960,7 +1994,8 @@ static int reftable_be_rename_ref(struct ref_store *ref_store, ret = backend_for(&arg.be, refs, newrefname, &newrefname, 1); if (ret) goto done; - ret = reftable_stack_add(arg.be->stack, &write_copy_table, &arg); + ret = reftable_stack_add(arg.be->stack, &write_copy_table, &arg, + REFTABLE_STACK_NEW_ADDITION_RELOAD); done: assert(ret != REFTABLE_API_ERROR); @@ -1989,7 +2024,8 @@ static int reftable_be_copy_ref(struct ref_store *ref_store, ret = backend_for(&arg.be, refs, newrefname, &newrefname, 1); if (ret) goto done; - ret = reftable_stack_add(arg.be->stack, &write_copy_table, &arg); + ret = reftable_stack_add(arg.be->stack, &write_copy_table, &arg, + REFTABLE_STACK_NEW_ADDITION_RELOAD); done: assert(ret != REFTABLE_API_ERROR); @@ -2042,7 +2078,8 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator) } static int reftable_reflog_iterator_seek(struct ref_iterator *ref_iterator UNUSED, - const char *prefix UNUSED) + const char *refname UNUSED, + unsigned int flags UNUSED) { BUG("reftable reflog iterator cannot be seeked"); return -1; @@ -2140,7 +2177,7 @@ static int yield_log_record(struct reftable_ref_store *refs, full_committer = fmt_ident(log->value.update.name, log->value.update.email, WANT_COMMITTER_IDENT, NULL, IDENT_NO_DATE); - return fn(&old_oid, &new_oid, full_committer, + return fn(log->refname, &old_oid, &new_oid, full_committer, log->value.update.time, log->value.update.tz_offset, log->value.update.message, cb_data); } @@ -2360,7 +2397,8 @@ static int reftable_be_create_reflog(struct ref_store *ref_store, goto done; arg.stack = be->stack; - ret = reftable_stack_add(be->stack, &write_reflog_existence_table, &arg); + ret = reftable_stack_add(be->stack, &write_reflog_existence_table, &arg, + REFTABLE_STACK_NEW_ADDITION_RELOAD); done: return ret; @@ -2431,7 +2469,8 @@ static int reftable_be_delete_reflog(struct ref_store *ref_store, return ret; arg.stack = be->stack; - ret = reftable_stack_add(be->stack, &write_reflog_delete_table, &arg); + ret = reftable_stack_add(be->stack, &write_reflog_delete_table, &arg, + REFTABLE_STACK_NEW_ADDITION_RELOAD); assert(ret != REFTABLE_API_ERROR); return ret; @@ -2552,15 +2591,16 @@ static int reftable_be_reflog_expire(struct ref_store *ref_store, if (ret < 0) goto done; - ret = reftable_stack_init_log_iterator(be->stack, &it); + ret = reftable_stack_new_addition(&add, be->stack, + REFTABLE_STACK_NEW_ADDITION_RELOAD); if (ret < 0) goto done; - ret = reftable_iterator_seek_log(&it, refname); + ret = reftable_stack_init_log_iterator(be->stack, &it); if (ret < 0) goto done; - ret = reftable_stack_new_addition(&add, be->stack, 0); + ret = reftable_iterator_seek_log(&it, refname); if (ret < 0) goto done; diff --git a/reftable/reftable-stack.h b/reftable/reftable-stack.h index 910ec6ef3a..d70fcb705d 100644 --- a/reftable/reftable-stack.h +++ b/reftable/reftable-stack.h @@ -68,12 +68,15 @@ int reftable_addition_commit(struct reftable_addition *add); * transaction. Releases the lock if held. */ void reftable_addition_destroy(struct reftable_addition *add); -/* add a new table to the stack. The write_table function must call - * reftable_writer_set_limits, add refs and return an error value. */ +/* + * Add a new table to the stack. The write_table function must call + * reftable_writer_set_limits, add refs and return an error value. + * The flags are passed through to `reftable_stack_new_addition()`. + */ int reftable_stack_add(struct reftable_stack *st, int (*write_table)(struct reftable_writer *wr, void *write_arg), - void *write_arg); + void *write_arg, unsigned flags); struct reftable_iterator; diff --git a/reftable/reftable-writer.h b/reftable/reftable-writer.h index 0fbeff17f4..1e7003cd69 100644 --- a/reftable/reftable-writer.h +++ b/reftable/reftable-writer.h @@ -156,7 +156,7 @@ int reftable_writer_add_ref(struct reftable_writer *w, the records before adding them, reordering the records array passed in. */ int reftable_writer_add_refs(struct reftable_writer *w, - struct reftable_ref_record *refs, int n); + struct reftable_ref_record *refs, size_t n); /* adds reftable_log_records. Log records are keyed by (refname, decreasing @@ -171,7 +171,7 @@ int reftable_writer_add_log(struct reftable_writer *w, the records before adding them, reordering records array passed in. */ int reftable_writer_add_logs(struct reftable_writer *w, - struct reftable_log_record *logs, int n); + struct reftable_log_record *logs, size_t n); /* reftable_writer_close finalizes the reftable. The writer is retained so * statistics can be inspected. */ diff --git a/reftable/stack.c b/reftable/stack.c index 4caf96aa1d..f91ce50bcd 100644 --- a/reftable/stack.c +++ b/reftable/stack.c @@ -17,18 +17,6 @@ #include "table.h" #include "writer.h" -static int stack_try_add(struct reftable_stack *st, - int (*write_table)(struct reftable_writer *wr, - void *arg), - void *arg); -static int stack_write_compact(struct reftable_stack *st, - struct reftable_writer *wr, - size_t first, size_t last, - struct reftable_log_expiry_config *config); -static void reftable_addition_close(struct reftable_addition *add); -static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st, - int reuse_open); - static int stack_filename(struct reftable_buf *dest, struct reftable_stack *st, const char *name) { @@ -84,54 +72,6 @@ static int fd_writer_flush(void *arg) return stack_fsync(writer->opts, writer->fd); } -int reftable_new_stack(struct reftable_stack **dest, const char *dir, - const struct reftable_write_options *_opts) -{ - struct reftable_buf list_file_name = REFTABLE_BUF_INIT; - struct reftable_write_options opts = { 0 }; - struct reftable_stack *p; - int err; - - p = reftable_calloc(1, sizeof(*p)); - if (!p) { - err = REFTABLE_OUT_OF_MEMORY_ERROR; - goto out; - } - - if (_opts) - opts = *_opts; - if (opts.hash_id == 0) - opts.hash_id = REFTABLE_HASH_SHA1; - - *dest = NULL; - - reftable_buf_reset(&list_file_name); - if ((err = reftable_buf_addstr(&list_file_name, dir)) < 0 || - (err = reftable_buf_addstr(&list_file_name, "/tables.list")) < 0) - goto out; - - p->list_file = reftable_buf_detach(&list_file_name); - p->list_fd = -1; - p->opts = opts; - p->reftable_dir = reftable_strdup(dir); - if (!p->reftable_dir) { - err = REFTABLE_OUT_OF_MEMORY_ERROR; - goto out; - } - - err = reftable_stack_reload_maybe_reuse(p, 1); - if (err < 0) - goto out; - - *dest = p; - err = 0; - -out: - if (err < 0) - reftable_stack_destroy(p); - return err; -} - static int fd_read_lines(int fd, char ***namesp) { char *buf = NULL; @@ -591,9 +531,59 @@ out: return err; } -/* -1 = error - 0 = up to date - 1 = changed. */ +int reftable_new_stack(struct reftable_stack **dest, const char *dir, + const struct reftable_write_options *_opts) +{ + struct reftable_buf list_file_name = REFTABLE_BUF_INIT; + struct reftable_write_options opts = { 0 }; + struct reftable_stack *p; + int err; + + p = reftable_calloc(1, sizeof(*p)); + if (!p) { + err = REFTABLE_OUT_OF_MEMORY_ERROR; + goto out; + } + + if (_opts) + opts = *_opts; + if (opts.hash_id == 0) + opts.hash_id = REFTABLE_HASH_SHA1; + + *dest = NULL; + + reftable_buf_reset(&list_file_name); + if ((err = reftable_buf_addstr(&list_file_name, dir)) < 0 || + (err = reftable_buf_addstr(&list_file_name, "/tables.list")) < 0) + goto out; + + p->list_file = reftable_buf_detach(&list_file_name); + p->list_fd = -1; + p->opts = opts; + p->reftable_dir = reftable_strdup(dir); + if (!p->reftable_dir) { + err = REFTABLE_OUT_OF_MEMORY_ERROR; + goto out; + } + + err = reftable_stack_reload_maybe_reuse(p, 1); + if (err < 0) + goto out; + + *dest = p; + err = 0; + +out: + if (err < 0) + reftable_stack_destroy(p); + return err; +} + +/* + * Check whether the given stack is up-to-date with what we have in memory. + * Returns 0 if so, 1 if the stack is out-of-date or a negative error code + * otherwise. + */ static int stack_uptodate(struct reftable_stack *st) { char **names = NULL; @@ -667,34 +657,6 @@ int reftable_stack_reload(struct reftable_stack *st) return err; } -int reftable_stack_add(struct reftable_stack *st, - int (*write)(struct reftable_writer *wr, void *arg), - void *arg) -{ - int err = stack_try_add(st, write, arg); - if (err < 0) { - if (err == REFTABLE_OUTDATED_ERROR) { - /* Ignore error return, we want to propagate - REFTABLE_OUTDATED_ERROR. - */ - reftable_stack_reload(st); - } - return err; - } - - return 0; -} - -static int format_name(struct reftable_buf *dest, uint64_t min, uint64_t max) -{ - char buf[100]; - uint32_t rnd = reftable_rand(); - snprintf(buf, sizeof(buf), "0x%012" PRIx64 "-0x%012" PRIx64 "-%08x", - min, max, rnd); - reftable_buf_reset(dest); - return reftable_buf_addstr(dest, buf); -} - struct reftable_addition { struct reftable_flock tables_list_lock; struct reftable_stack *stack; @@ -704,7 +666,25 @@ struct reftable_addition { uint64_t next_update_index; }; -#define REFTABLE_ADDITION_INIT {0} +static void reftable_addition_close(struct reftable_addition *add) +{ + struct reftable_buf nm = REFTABLE_BUF_INIT; + size_t i; + + for (i = 0; i < add->new_tables_len; i++) { + if (!stack_filename(&nm, add->stack, add->new_tables[i])) + unlink(nm.buf); + reftable_free(add->new_tables[i]); + add->new_tables[i] = NULL; + } + reftable_free(add->new_tables); + add->new_tables = NULL; + add->new_tables_len = 0; + add->new_tables_cap = 0; + + flock_release(&add->tables_list_lock); + reftable_buf_release(&nm); +} static int reftable_stack_init_addition(struct reftable_addition *add, struct reftable_stack *st, @@ -713,18 +693,14 @@ static int reftable_stack_init_addition(struct reftable_addition *add, struct reftable_buf lock_file_name = REFTABLE_BUF_INIT; int err; + memset(add, 0, sizeof(*add)); add->stack = st; err = flock_acquire(&add->tables_list_lock, st->list_file, st->opts.lock_timeout_ms); - if (err < 0) { - if (errno == EEXIST) { - err = REFTABLE_LOCK_ERROR; - } else { - err = REFTABLE_IO_ERROR; - } + if (err < 0) goto done; - } + if (st->opts.default_permissions) { if (chmod(add->tables_list_lock.path, st->opts.default_permissions) < 0) { @@ -754,24 +730,54 @@ done: return err; } -static void reftable_addition_close(struct reftable_addition *add) +static int stack_try_add(struct reftable_stack *st, + int (*write_table)(struct reftable_writer *wr, + void *arg), + void *arg, unsigned flags) { - struct reftable_buf nm = REFTABLE_BUF_INIT; - size_t i; + struct reftable_addition add; + int err; - for (i = 0; i < add->new_tables_len; i++) { - if (!stack_filename(&nm, add->stack, add->new_tables[i])) - unlink(nm.buf); - reftable_free(add->new_tables[i]); - add->new_tables[i] = NULL; + err = reftable_stack_init_addition(&add, st, flags); + if (err < 0) + goto done; + + err = reftable_addition_add(&add, write_table, arg); + if (err < 0) + goto done; + + err = reftable_addition_commit(&add); +done: + reftable_addition_close(&add); + return err; +} + +int reftable_stack_add(struct reftable_stack *st, + int (*write)(struct reftable_writer *wr, void *arg), + void *arg, unsigned flags) +{ + int err = stack_try_add(st, write, arg, flags); + if (err < 0) { + if (err == REFTABLE_OUTDATED_ERROR) { + /* Ignore error return, we want to propagate + REFTABLE_OUTDATED_ERROR. + */ + reftable_stack_reload(st); + } + return err; } - reftable_free(add->new_tables); - add->new_tables = NULL; - add->new_tables_len = 0; - add->new_tables_cap = 0; - flock_release(&add->tables_list_lock); - reftable_buf_release(&nm); + return 0; +} + +static int format_name(struct reftable_buf *dest, uint64_t min, uint64_t max) +{ + char buf[100]; + uint32_t rnd = reftable_rand(); + snprintf(buf, sizeof(buf), "0x%012" PRIx64 "-0x%012" PRIx64 "-%08x", + min, max, rnd); + reftable_buf_reset(dest); + return reftable_buf_addstr(dest, buf); } void reftable_addition_destroy(struct reftable_addition *add) @@ -841,10 +847,13 @@ int reftable_addition_commit(struct reftable_addition *add) * control. It is possible that a concurrent writer is already * trying to compact parts of the stack, which would lead to a * `REFTABLE_LOCK_ERROR` because parts of the stack are locked - * already. This is a benign error though, so we ignore it. + * already. Similarly, the stack may have been rewritten by a + * concurrent writer, which causes `REFTABLE_OUTDATED_ERROR`. + * Both of these errors are benign, so we simply ignore them. */ err = reftable_stack_auto_compact(add->stack); - if (err < 0 && err != REFTABLE_LOCK_ERROR) + if (err < 0 && err != REFTABLE_LOCK_ERROR && + err != REFTABLE_OUTDATED_ERROR) goto done; err = 0; } @@ -858,39 +867,18 @@ int reftable_stack_new_addition(struct reftable_addition **dest, struct reftable_stack *st, unsigned int flags) { - int err = 0; - struct reftable_addition empty = REFTABLE_ADDITION_INIT; + int err; REFTABLE_CALLOC_ARRAY(*dest, 1); if (!*dest) return REFTABLE_OUT_OF_MEMORY_ERROR; - **dest = empty; err = reftable_stack_init_addition(*dest, st, flags); if (err) { reftable_free(*dest); *dest = NULL; } - return err; -} -static int stack_try_add(struct reftable_stack *st, - int (*write_table)(struct reftable_writer *wr, - void *arg), - void *arg) -{ - struct reftable_addition add = REFTABLE_ADDITION_INIT; - int err = reftable_stack_init_addition(&add, st, 0); - if (err < 0) - goto done; - - err = reftable_addition_add(&add, write_table, arg); - if (err < 0) - goto done; - - err = reftable_addition_commit(&add); -done: - reftable_addition_close(&add); return err; } @@ -1007,72 +995,6 @@ uint64_t reftable_stack_next_update_index(struct reftable_stack *st) return 1; } -static int stack_compact_locked(struct reftable_stack *st, - size_t first, size_t last, - struct reftable_log_expiry_config *config, - struct reftable_tmpfile *tab_file_out) -{ - struct reftable_buf next_name = REFTABLE_BUF_INIT; - struct reftable_buf tab_file_path = REFTABLE_BUF_INIT; - struct reftable_writer *wr = NULL; - struct fd_writer writer= { - .opts = &st->opts, - }; - struct reftable_tmpfile tab_file = REFTABLE_TMPFILE_INIT; - int err = 0; - - err = format_name(&next_name, reftable_table_min_update_index(st->tables[first]), - reftable_table_max_update_index(st->tables[last])); - if (err < 0) - goto done; - - err = stack_filename(&tab_file_path, st, next_name.buf); - if (err < 0) - goto done; - - err = reftable_buf_addstr(&tab_file_path, ".temp.XXXXXX"); - if (err < 0) - goto done; - - err = tmpfile_from_pattern(&tab_file, tab_file_path.buf); - if (err < 0) - goto done; - - if (st->opts.default_permissions && - chmod(tab_file.path, st->opts.default_permissions) < 0) { - err = REFTABLE_IO_ERROR; - goto done; - } - - writer.fd = tab_file.fd; - err = reftable_writer_new(&wr, fd_writer_write, fd_writer_flush, - &writer, &st->opts); - if (err < 0) - goto done; - - err = stack_write_compact(st, wr, first, last, config); - if (err < 0) - goto done; - - err = reftable_writer_close(wr); - if (err < 0) - goto done; - - err = tmpfile_close(&tab_file); - if (err < 0) - goto done; - - *tab_file_out = tab_file; - tab_file = REFTABLE_TMPFILE_INIT; - -done: - tmpfile_delete(&tab_file); - reftable_writer_free(wr); - reftable_buf_release(&next_name); - reftable_buf_release(&tab_file_path); - return err; -} - static int stack_write_compact(struct reftable_stack *st, struct reftable_writer *wr, size_t first, size_t last, @@ -1172,6 +1094,72 @@ done: return err; } +static int stack_compact_locked(struct reftable_stack *st, + size_t first, size_t last, + struct reftable_log_expiry_config *config, + struct reftable_tmpfile *tab_file_out) +{ + struct reftable_buf next_name = REFTABLE_BUF_INIT; + struct reftable_buf tab_file_path = REFTABLE_BUF_INIT; + struct reftable_writer *wr = NULL; + struct fd_writer writer= { + .opts = &st->opts, + }; + struct reftable_tmpfile tab_file = REFTABLE_TMPFILE_INIT; + int err = 0; + + err = format_name(&next_name, reftable_table_min_update_index(st->tables[first]), + reftable_table_max_update_index(st->tables[last])); + if (err < 0) + goto done; + + err = stack_filename(&tab_file_path, st, next_name.buf); + if (err < 0) + goto done; + + err = reftable_buf_addstr(&tab_file_path, ".temp.XXXXXX"); + if (err < 0) + goto done; + + err = tmpfile_from_pattern(&tab_file, tab_file_path.buf); + if (err < 0) + goto done; + + if (st->opts.default_permissions && + chmod(tab_file.path, st->opts.default_permissions) < 0) { + err = REFTABLE_IO_ERROR; + goto done; + } + + writer.fd = tab_file.fd; + err = reftable_writer_new(&wr, fd_writer_write, fd_writer_flush, + &writer, &st->opts); + if (err < 0) + goto done; + + err = stack_write_compact(st, wr, first, last, config); + if (err < 0) + goto done; + + err = reftable_writer_close(wr); + if (err < 0) + goto done; + + err = tmpfile_close(&tab_file); + if (err < 0) + goto done; + + *tab_file_out = tab_file; + tab_file = REFTABLE_TMPFILE_INIT; + +done: + tmpfile_delete(&tab_file); + reftable_writer_free(wr); + reftable_buf_release(&next_name); + reftable_buf_release(&tab_file_path); + return err; +} + enum stack_compact_range_flags { /* * Perform a best-effort compaction. That is, even if we cannot lock @@ -1219,17 +1207,27 @@ static int stack_compact_range(struct reftable_stack *st, * which are part of the user-specified range. */ err = flock_acquire(&tables_list_lock, st->list_file, st->opts.lock_timeout_ms); - if (err < 0) { - if (errno == EEXIST) - err = REFTABLE_LOCK_ERROR; - else - err = REFTABLE_IO_ERROR; + if (err < 0) goto done; - } + /* + * Check whether the stack is up-to-date. We unfortunately cannot + * handle the situation gracefully in case it's _not_ up-to-date + * because the range of tables that the user has requested us to + * compact may have been changed. So instead we abort. + * + * We could in theory improve the situation by having the caller not + * pass in a range, but instead the list of tables to compact. If so, + * we could check that relevant tables still exist. But for now it's + * good enough to just abort. + */ err = stack_uptodate(st); - if (err) + if (err < 0) + goto done; + if (err > 0) { + err = REFTABLE_OUTDATED_ERROR; goto done; + } /* * Lock all tables in the user-provided range. This is the slice of our @@ -1264,7 +1262,7 @@ static int stack_compact_range(struct reftable_stack *st, * tables, otherwise there would be nothing to compact. * In that case, we return a lock error to our caller. */ - if (errno == EEXIST && last - (i - 1) >= 2 && + if (err == REFTABLE_LOCK_ERROR && last - (i - 1) >= 2 && flags & STACK_COMPACT_RANGE_BEST_EFFORT) { err = 0; /* @@ -1276,13 +1274,9 @@ static int stack_compact_range(struct reftable_stack *st, */ first = (i - 1) + 1; break; - } else if (errno == EEXIST) { - err = REFTABLE_LOCK_ERROR; - goto done; - } else { - err = REFTABLE_IO_ERROR; - goto done; } + + goto done; } /* @@ -1291,10 +1285,8 @@ static int stack_compact_range(struct reftable_stack *st, * of tables. */ err = flock_close(&table_locks[nlocks++]); - if (err < 0) { - err = REFTABLE_IO_ERROR; + if (err < 0) goto done; - } } /* @@ -1326,13 +1318,8 @@ static int stack_compact_range(struct reftable_stack *st, * the new table. */ err = flock_acquire(&tables_list_lock, st->list_file, st->opts.lock_timeout_ms); - if (err < 0) { - if (errno == EEXIST) - err = REFTABLE_LOCK_ERROR; - else - err = REFTABLE_IO_ERROR; + if (err < 0) goto done; - } if (st->opts.default_permissions) { if (chmod(tables_list_lock.path, diff --git a/reftable/system.c b/reftable/system.c index 1ee268b125..725a25844e 100644 --- a/reftable/system.c +++ b/reftable/system.c @@ -72,7 +72,7 @@ int flock_acquire(struct reftable_flock *l, const char *target_path, reftable_free(lockfile); if (errno == EEXIST) return REFTABLE_LOCK_ERROR; - return -1; + return REFTABLE_IO_ERROR; } l->fd = get_lock_file_fd(lockfile); diff --git a/reftable/system.h b/reftable/system.h index beb9d2431f..c54ed4cad6 100644 --- a/reftable/system.h +++ b/reftable/system.h @@ -81,7 +81,9 @@ struct reftable_flock { * to acquire the lock. If `timeout_ms` is 0 we don't wait, if it is negative * we block indefinitely. * - * Retrun 0 on success, a reftable error code on error. + * Retrun 0 on success, a reftable error code on error. Specifically, + * `REFTABLE_LOCK_ERROR` should be returned in case the target path is already + * locked. */ int flock_acquire(struct reftable_flock *l, const char *target_path, long timeout_ms); diff --git a/reftable/writer.c b/reftable/writer.c index 3b4ebdd6dc..0133b64975 100644 --- a/reftable/writer.c +++ b/reftable/writer.c @@ -395,14 +395,16 @@ out: } int reftable_writer_add_refs(struct reftable_writer *w, - struct reftable_ref_record *refs, int n) + struct reftable_ref_record *refs, size_t n) { int err = 0; - int i = 0; - QSORT(refs, n, reftable_ref_record_compare_name); - for (i = 0; err == 0 && i < n; i++) { + + if (n) + qsort(refs, n, sizeof(*refs), reftable_ref_record_compare_name); + + for (size_t i = 0; err == 0 && i < n; i++) err = reftable_writer_add_ref(w, &refs[i]); - } + return err; } @@ -486,15 +488,16 @@ done: } int reftable_writer_add_logs(struct reftable_writer *w, - struct reftable_log_record *logs, int n) + struct reftable_log_record *logs, size_t n) { int err = 0; - int i = 0; - QSORT(logs, n, reftable_log_record_compare_key); - for (i = 0; err == 0 && i < n; i++) { + if (n) + qsort(logs, n, sizeof(*logs), reftable_log_record_compare_key); + + for (size_t i = 0; err == 0 && i < n; i++) err = reftable_writer_add_log(w, &logs[i]); - } + return err; } diff --git a/remote-curl.c b/remote-curl.c index b8bc3a80cf..84f4694780 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -285,7 +285,7 @@ static const struct git_hash_algo *detect_hash_algo(struct discovery *heads) * back to SHA1, which may or may not be correct. */ if (!p) - return &hash_algos[GIT_HASH_SHA1]; + return &hash_algos[GIT_HASH_SHA1_LEGACY]; algo = hash_algo_by_length((p - heads->buf) / 2); if (algo == GIT_HASH_UNKNOWN) @@ -12,7 +12,7 @@ #include "refs.h" #include "refspec.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "path.h" #include "commit.h" #include "diff.h" @@ -165,6 +165,9 @@ static void remote_clear(struct remote *remote) strvec_clear(&remote->url); strvec_clear(&remote->pushurl); + refspec_clear(&remote->push); + refspec_clear(&remote->fetch); + free((char *)remote->receivepack); free((char *)remote->uploadpack); FREE_AND_NULL(remote->http_proxy); @@ -174,9 +177,15 @@ static void remote_clear(struct remote *remote) static void add_merge(struct branch *branch, const char *name) { - ALLOC_GROW(branch->merge_name, branch->merge_nr + 1, + struct refspec_item *merge; + + ALLOC_GROW(branch->merge, branch->merge_nr + 1, branch->merge_alloc); - branch->merge_name[branch->merge_nr++] = name; + + merge = xcalloc(1, sizeof(*merge)); + merge->src = xstrdup(name); + + branch->merge[branch->merge_nr++] = merge; } struct branches_hash_key { @@ -247,15 +256,23 @@ static struct branch *make_branch(struct remote_state *remote_state, return ret; } +static void merge_clear(struct branch *branch) +{ + for (int i = 0; i < branch->merge_nr; i++) { + refspec_item_clear(branch->merge[i]); + free(branch->merge[i]); + } + FREE_AND_NULL(branch->merge); + branch->merge_nr = 0; +} + static void branch_release(struct branch *branch) { free((char *)branch->name); free((char *)branch->refname); free(branch->remote_name); free(branch->pushremote_name); - for (int i = 0; i < branch->merge_nr; i++) - refspec_item_clear(branch->merge[i]); - free(branch->merge); + merge_clear(branch); } static struct rewrite *make_rewrite(struct rewrites *r, @@ -317,11 +334,10 @@ static void warn_about_deprecated_remote_type(const char *type, type, remote->name, remote->name, remote->name); } -static void read_remotes_file(struct remote_state *remote_state, - struct remote *remote) +static void read_remotes_file(struct repository *repo, struct remote *remote) { struct strbuf buf = STRBUF_INIT; - FILE *f = fopen_or_warn(repo_git_path_append(the_repository, &buf, + FILE *f = fopen_or_warn(repo_git_path_append(repo, &buf, "remotes/%s", remote->name), "r"); if (!f) @@ -337,7 +353,7 @@ static void read_remotes_file(struct remote_state *remote_state, strbuf_rtrim(&buf); if (skip_prefix(buf.buf, "URL:", &v)) - add_url_alias(remote_state, remote, + add_url_alias(repo->remote_state, remote, skip_spaces(v)); else if (skip_prefix(buf.buf, "Push:", &v)) refspec_append(&remote->push, skip_spaces(v)); @@ -350,12 +366,11 @@ out: strbuf_release(&buf); } -static void read_branches_file(struct remote_state *remote_state, - struct remote *remote) +static void read_branches_file(struct repository *repo, struct remote *remote) { char *frag, *to_free = NULL; struct strbuf buf = STRBUF_INIT; - FILE *f = fopen_or_warn(repo_git_path_append(the_repository, &buf, + FILE *f = fopen_or_warn(repo_git_path_append(repo, &buf, "branches/%s", remote->name), "r"); if (!f) @@ -382,9 +397,9 @@ static void read_branches_file(struct remote_state *remote_state, if (frag) *(frag++) = '\0'; else - frag = to_free = repo_default_branch_name(the_repository, 0); + frag = to_free = repo_default_branch_name(repo, 0); - add_url_alias(remote_state, remote, buf.buf); + add_url_alias(repo->remote_state, remote, buf.buf); refspec_appendf(&remote->fetch, "refs/heads/%s:refs/heads/%s", frag, remote->name); @@ -429,7 +444,7 @@ static int handle_config(const char *key, const char *value, } else if (!strcmp(subkey, "merge")) { if (!value) return config_error_nonbool(key); - add_merge(branch, xstrdup(value)); + add_merge(branch, value); } return 0; } @@ -681,7 +696,7 @@ const char *pushremote_for_branch(struct branch *branch, int *explicit) branch, explicit); } -static struct remote *remotes_remote_get(struct remote_state *remote_state, +static struct remote *remotes_remote_get(struct repository *repo, const char *name); char *remote_ref_for_branch(struct branch *branch, int for_push) @@ -692,7 +707,7 @@ char *remote_ref_for_branch(struct branch *branch, int for_push) if (branch) { if (!for_push) { if (branch->merge_nr) { - return xstrdup(branch->merge_name[0]); + return xstrdup(branch->merge[0]->src); } } else { char *dst; @@ -700,7 +715,7 @@ char *remote_ref_for_branch(struct branch *branch, int for_push) the_repository->remote_state, branch, NULL); struct remote *remote = remotes_remote_get( - the_repository->remote_state, remote_name); + the_repository, remote_name); if (remote && remote->push.nr && (dst = apply_refspecs(&remote->push, @@ -719,7 +734,7 @@ static void validate_remote_url(struct remote *remote) struct strbuf redacted = STRBUF_INIT; int warn_not_die; - if (git_config_get_string_tmp("transfer.credentialsinurl", &value)) + if (repo_config_get_string_tmp(the_repository, "transfer.credentialsinurl", &value)) return; if (!strcmp("warn", value)) @@ -757,10 +772,11 @@ loop_cleanup: } static struct remote * -remotes_remote_get_1(struct remote_state *remote_state, const char *name, +remotes_remote_get_1(struct repository *repo, const char *name, const char *(*get_default)(struct remote_state *, struct branch *, int *)) { + struct remote_state *remote_state = repo->remote_state; struct remote *ret; int name_given = 0; @@ -774,9 +790,9 @@ remotes_remote_get_1(struct remote_state *remote_state, const char *name, #ifndef WITH_BREAKING_CHANGES if (valid_remote_nick(name) && have_git_dir()) { if (!valid_remote(ret)) - read_remotes_file(remote_state, ret); + read_remotes_file(repo, ret); if (!valid_remote(ret)) - read_branches_file(remote_state, ret); + read_branches_file(repo, ret); } #endif /* WITH_BREAKING_CHANGES */ if (name_given && !valid_remote(ret)) @@ -790,35 +806,33 @@ remotes_remote_get_1(struct remote_state *remote_state, const char *name, } static inline struct remote * -remotes_remote_get(struct remote_state *remote_state, const char *name) +remotes_remote_get(struct repository *repo, const char *name) { - return remotes_remote_get_1(remote_state, name, - remotes_remote_for_branch); + return remotes_remote_get_1(repo, name, remotes_remote_for_branch); } struct remote *remote_get(const char *name) { read_config(the_repository, 0); - return remotes_remote_get(the_repository->remote_state, name); + return remotes_remote_get(the_repository, name); } struct remote *remote_get_early(const char *name) { read_config(the_repository, 1); - return remotes_remote_get(the_repository->remote_state, name); + return remotes_remote_get(the_repository, name); } static inline struct remote * -remotes_pushremote_get(struct remote_state *remote_state, const char *name) +remotes_pushremote_get(struct repository *repo, const char *name) { - return remotes_remote_get_1(remote_state, name, - remotes_pushremote_for_branch); + return remotes_remote_get_1(repo, name, remotes_pushremote_for_branch); } struct remote *pushremote_get(const char *name) { read_config(the_repository, 0); - return remotes_pushremote_get(the_repository->remote_state, name); + return remotes_pushremote_get(the_repository, name); } int remote_is_configured(struct remote *remote, int in_repo) @@ -1157,7 +1171,6 @@ static void show_push_unqualified_ref_name_error(const char *dst_value, const char *matched_src_name) { struct object_id oid; - enum object_type type; /* * TRANSLATORS: "matches '%s'%" is the <dst> part of "git push @@ -1182,30 +1195,37 @@ static void show_push_unqualified_ref_name_error(const char *dst_value, BUG("'%s' is not a valid object, " "match_explicit_lhs() should catch this!", matched_src_name); - type = oid_object_info(the_repository, &oid, NULL); - if (type == OBJ_COMMIT) { + + switch (odb_read_object_info(the_repository->objects, &oid, NULL)) { + case OBJ_COMMIT: advise(_("The <src> part of the refspec is a commit object.\n" "Did you mean to create a new branch by pushing to\n" "'%s:refs/heads/%s'?"), matched_src_name, dst_value); - } else if (type == OBJ_TAG) { + break; + case OBJ_TAG: advise(_("The <src> part of the refspec is a tag object.\n" "Did you mean to create a new tag by pushing to\n" "'%s:refs/tags/%s'?"), matched_src_name, dst_value); - } else if (type == OBJ_TREE) { + break; + case OBJ_TREE: advise(_("The <src> part of the refspec is a tree object.\n" "Did you mean to tag a new tree by pushing to\n" "'%s:refs/tags/%s'?"), matched_src_name, dst_value); - } else if (type == OBJ_BLOB) { + break; + case OBJ_BLOB: advise(_("The <src> part of the refspec is a blob object.\n" "Did you mean to tag a new blob by pushing to\n" "'%s:refs/tags/%s'?"), matched_src_name, dst_value); - } else { - BUG("'%s' should be commit/tag/tree/blob, is '%d'", - matched_src_name, type); + break; + default: + advise(_("The <src> part of the refspec ('%s') " + "is an object ID that doesn't exist.\n"), + matched_src_name); + break; } } @@ -1412,7 +1432,8 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds continue; /* not a tag */ if (string_list_has_string(&dst_tag, ref->name)) continue; /* they already have it */ - if (oid_object_info(the_repository, &ref->new_oid, NULL) != OBJ_TAG) + if (odb_read_object_info(the_repository->objects, + &ref->new_oid, NULL) != OBJ_TAG) continue; /* be conservative */ item = string_list_append(&src_tag, ref->name); item->util = ref; @@ -1702,7 +1723,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, if (!reject_reason && !ref->deletion && !is_null_oid(&ref->old_oid)) { if (starts_with(ref->name, "refs/tags/")) reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS; - else if (!has_object(the_repository, &ref->old_oid, HAS_OBJECT_RECHECK_PACKED)) + else if (!odb_has_object(the_repository->objects, &ref->old_oid, HAS_OBJECT_RECHECK_PACKED)) reject_reason = REF_STATUS_REJECT_FETCH_FIRST; else if (!lookup_commit_reference_gently(the_repository, &ref->old_oid, 1) || !lookup_commit_reference_gently(the_repository, &ref->new_oid, 1)) @@ -1722,7 +1743,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror, } } -static void set_merge(struct remote_state *remote_state, struct branch *ret) +static void set_merge(struct repository *repo, struct branch *ret) { struct remote *remote; char *ref; @@ -1731,52 +1752,80 @@ static void set_merge(struct remote_state *remote_state, struct branch *ret) if (!ret) return; /* no branch */ - if (ret->merge) + if (ret->set_merge) return; /* already run */ if (!ret->remote_name || !ret->merge_nr) { /* * no merge config; let's make sure we don't confuse callers * with a non-zero merge_nr but a NULL merge */ - ret->merge_nr = 0; + merge_clear(ret); return; } + ret->set_merge = 1; - remote = remotes_remote_get(remote_state, ret->remote_name); + remote = remotes_remote_get(repo, ret->remote_name); - CALLOC_ARRAY(ret->merge, ret->merge_nr); for (i = 0; i < ret->merge_nr; i++) { - ret->merge[i] = xcalloc(1, sizeof(**ret->merge)); - ret->merge[i]->src = xstrdup(ret->merge_name[i]); if (!remote_find_tracking(remote, ret->merge[i]) || strcmp(ret->remote_name, ".")) continue; - if (repo_dwim_ref(the_repository, ret->merge_name[i], - strlen(ret->merge_name[i]), &oid, &ref, + if (repo_dwim_ref(repo, ret->merge[i]->src, + strlen(ret->merge[i]->src), &oid, &ref, 0) == 1) ret->merge[i]->dst = ref; else - ret->merge[i]->dst = xstrdup(ret->merge_name[i]); + ret->merge[i]->dst = xstrdup(ret->merge[i]->src); } } -struct branch *branch_get(const char *name) +static struct branch *repo_branch_get(struct repository *repo, const char *name) { struct branch *ret; - read_config(the_repository, 0); + read_config(repo, 0); if (!name || !*name || !strcmp(name, "HEAD")) - ret = the_repository->remote_state->current_branch; + ret = repo->remote_state->current_branch; else - ret = make_branch(the_repository->remote_state, name, + ret = make_branch(repo->remote_state, name, strlen(name)); - set_merge(the_repository->remote_state, ret); + set_merge(repo, ret); return ret; } +struct branch *branch_get(const char *name) +{ + return repo_branch_get(the_repository, name); +} + +const char *repo_default_remote(struct repository *repo) +{ + struct branch *branch; + + read_config(repo, 0); + branch = repo_branch_get(repo, "HEAD"); + + return remotes_remote_for_branch(repo->remote_state, branch, NULL); +} + +const char *repo_remote_from_url(struct repository *repo, const char *url) +{ + read_config(repo, 0); + + for (int i = 0; i < repo->remote_state->remotes_nr; i++) { + struct remote *remote = repo->remote_state->remotes[i]; + if (!remote) + continue; + + if (remote_has_url(remote, url)) + return remote->name; + } + return NULL; +} + int branch_has_merge_config(struct branch *branch) { - return branch && !!branch->merge; + return branch && branch->set_merge; } int branch_merge_matches(struct branch *branch, @@ -1841,13 +1890,14 @@ static const char *tracking_for_push_dest(struct remote *remote, return ret; } -static const char *branch_get_push_1(struct remote_state *remote_state, +static const char *branch_get_push_1(struct repository *repo, struct branch *branch, struct strbuf *err) { + struct remote_state *remote_state = repo->remote_state; struct remote *remote; remote = remotes_remote_get( - remote_state, + repo, remotes_pushremote_for_branch(remote_state, branch, NULL)); if (!remote) return error_buf(err, @@ -1914,7 +1964,7 @@ const char *branch_get_push(struct branch *branch, struct strbuf *err) if (!branch->push_tracking_ref) branch->push_tracking_ref = branch_get_push_1( - the_repository->remote_state, branch, err); + the_repository, branch, err); return branch->push_tracking_ref; } @@ -2534,7 +2584,8 @@ struct check_and_collect_until_cb_data { }; /* Get the timestamp of the latest entry. */ -static int peek_reflog(struct object_id *o_oid UNUSED, +static int peek_reflog(const char *refname UNUSED, + struct object_id *o_oid UNUSED, struct object_id *n_oid UNUSED, const char *ident UNUSED, timestamp_t timestamp, int tz UNUSED, @@ -2545,7 +2596,8 @@ static int peek_reflog(struct object_id *o_oid UNUSED, return 1; } -static int check_and_collect_until(struct object_id *o_oid UNUSED, +static int check_and_collect_until(const char *refname UNUSED, + struct object_id *o_oid UNUSED, struct object_id *n_oid, const char *ident UNUSED, timestamp_t timestamp, int tz UNUSED, @@ -9,6 +9,7 @@ struct option; struct transport_ls_refs_options; +struct repository; /** * The API gives access to the configuration related to remotes. It handles @@ -315,8 +316,8 @@ struct branch { char *pushremote_name; - /* An array of the "merge" lines in the configuration. */ - const char **merge_name; + /* True if set_merge() has been called to finalize the merge array */ + int set_merge; /** * An array of the struct refspecs used for the merge lines. That is, @@ -338,6 +339,9 @@ const char *remote_for_branch(struct branch *branch, int *explicit); const char *pushremote_for_branch(struct branch *branch, int *explicit); char *remote_ref_for_branch(struct branch *branch, int for_push); +const char *repo_default_remote(struct repository *repo); +const char *repo_remote_from_url(struct repository *repo, const char *url); + /* returns true if the given branch has merge configuration given. */ int branch_has_merge_config(struct branch *branch); diff --git a/replace-object.c b/replace-object.c index f8c5f68837..3eae051074 100644 --- a/replace-object.c +++ b/replace-object.c @@ -2,7 +2,7 @@ #include "gettext.h" #include "hex.h" #include "oidmap.h" -#include "object-store.h" +#include "odb.h" #include "replace-object.h" #include "refs.h" #include "repository.h" diff --git a/replace-object.h b/replace-object.h index 3052e96a62..4c9f2a2383 100644 --- a/replace-object.h +++ b/replace-object.h @@ -3,7 +3,7 @@ #include "oidmap.h" #include "repository.h" -#include "object-store.h" +#include "odb.h" struct replace_object { struct oidmap_entry original; diff --git a/repo-settings.c b/repo-settings.c index 4129f8fb2b..195c24e9c0 100644 --- a/repo-settings.c +++ b/repo-settings.c @@ -54,11 +54,13 @@ void prepare_repo_settings(struct repository *r) r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_SKIPPING; r->settings.pack_use_bitmap_boundary_traversal = 1; r->settings.pack_use_multi_pack_reuse = 1; + r->settings.pack_use_path_walk = 1; } if (manyfiles) { r->settings.index_version = 4; r->settings.index_skip_hash = 1; r->settings.core_untracked_cache = UNTRACKED_CACHE_WRITE; + r->settings.pack_use_path_walk = 1; } /* Commit graph config or default, does not cascade (simple) */ @@ -73,6 +75,7 @@ void prepare_repo_settings(struct repository *r) /* Boolean config or default, does not cascade (simple) */ repo_cfg_bool(r, "pack.usesparse", &r->settings.pack_use_sparse, 1); + repo_cfg_bool(r, "pack.usepathwalk", &r->settings.pack_use_path_walk, 0); repo_cfg_bool(r, "core.multipackindex", &r->settings.core_multi_pack_index, 1); repo_cfg_bool(r, "index.sparse", &r->settings.sparse_index, 0); repo_cfg_bool(r, "index.skiphash", &r->settings.index_skip_hash, r->settings.index_skip_hash); diff --git a/repo-settings.h b/repo-settings.h index 2bf24b2597..d477885561 100644 --- a/repo-settings.h +++ b/repo-settings.h @@ -56,6 +56,7 @@ struct repo_settings { enum untracked_cache_setting core_untracked_cache; int pack_use_sparse; + int pack_use_path_walk; enum fetch_negotiation_setting fetch_negotiation_algorithm; int core_multi_pack_index; diff --git a/repository.c b/repository.c index 9b3d6665fc..6faf5c7398 100644 --- a/repository.c +++ b/repository.c @@ -1,7 +1,7 @@ #include "git-compat-util.h" #include "abspath.h" #include "repository.h" -#include "object-store.h" +#include "odb.h" #include "config.h" #include "object.h" #include "lockfile.h" @@ -52,11 +52,12 @@ static void set_default_hash_algo(struct repository *repo) void initialize_repository(struct repository *repo) { - repo->objects = raw_object_store_new(); + repo->objects = odb_new(repo); repo->remote_state = remote_state_new(); repo->parsed_objects = parsed_object_pool_new(repo); ALLOC_ARRAY(repo->index, 1); index_state_init(repo->index, repo); + repo->check_deprecated_config = true; /* * When a command runs inside a repository, it learns what @@ -107,9 +108,9 @@ const char *repo_get_common_dir(struct repository *repo) const char *repo_get_object_directory(struct repository *repo) { - if (!repo->objects->odb) + if (!repo->objects->sources) BUG("repository hasn't been set up"); - return repo->objects->odb->path; + return repo->objects->sources->path; } const char *repo_get_index_file(struct repository *repo) @@ -165,14 +166,16 @@ void repo_set_gitdir(struct repository *repo, repo_set_commondir(repo, o->commondir); - if (!repo->objects->odb) { - CALLOC_ARRAY(repo->objects->odb, 1); - repo->objects->odb_tail = &repo->objects->odb->next; + if (!repo->objects->sources) { + CALLOC_ARRAY(repo->objects->sources, 1); + repo->objects->sources->odb = repo->objects; + repo->objects->sources->local = true; + repo->objects->sources_tail = &repo->objects->sources->next; } - expand_base_dir(&repo->objects->odb->path, o->object_dir, + expand_base_dir(&repo->objects->sources->path, o->object_dir, repo->commondir, "objects"); - repo->objects->odb->disable_ref_updates = o->disable_ref_updates; + repo->objects->sources->disable_ref_updates = o->disable_ref_updates; free(repo->objects->alternate_db); repo->objects->alternate_db = xstrdup_or_null(o->alternate_db); @@ -284,6 +287,7 @@ int repo_init(struct repository *repo, repo_set_ref_storage_format(repo, format.ref_storage_format); repo->repository_format_worktree_config = format.worktree_config; repo->repository_format_relative_worktrees = format.relative_worktrees; + repo->repository_format_precious_objects = format.precious_objects; /* take ownership of format.partial_clone */ repo->repository_format_partial_clone = format.partial_clone; @@ -374,7 +378,7 @@ void repo_clear(struct repository *repo) FREE_AND_NULL(repo->worktree); FREE_AND_NULL(repo->submodule_prefix); - raw_object_store_clear(repo->objects); + odb_clear(repo->objects); FREE_AND_NULL(repo->objects); parsed_object_pool_clear(repo->parsed_objects); diff --git a/repository.h b/repository.h index c4c92b2ab9..5808a5d610 100644 --- a/repository.h +++ b/repository.h @@ -9,7 +9,7 @@ struct git_hash_algo; struct index_state; struct lock_file; struct pathspec; -struct raw_object_store; +struct object_database; struct submodule_cache; struct promisor_remote_config; struct remote_state; @@ -20,6 +20,12 @@ enum ref_storage_format { REF_STORAGE_FORMAT_REFTABLE, }; +#ifdef WITH_BREAKING_CHANGES /* Git 3.0 */ +# define REF_STORAGE_FORMAT_DEFAULT REF_STORAGE_FORMAT_REFTABLE +#else +# define REF_STORAGE_FORMAT_DEFAULT REF_STORAGE_FORMAT_FILES +#endif + struct repo_path_cache { char *squash_msg; char *merge_msg; @@ -47,7 +53,7 @@ struct repository { /* * Holds any information related to accessing the raw object content. */ - struct raw_object_store *objects; + struct object_database *objects; /* * All objects in this repository that have been parsed. This structure @@ -151,9 +157,13 @@ struct repository { /* Configurations */ int repository_format_worktree_config; int repository_format_relative_worktrees; + int repository_format_precious_objects; /* Indicate if a repository has a different 'commondir' from 'gitdir' */ unsigned different_commondir:1; + + /* Should repo_config() check for deprecated settings */ + bool check_deprecated_config; }; #ifdef USE_THE_REPOSITORY_VARIABLE @@ -5,6 +5,7 @@ #include "abspath.h" #include "config.h" #include "copy.h" +#include "environment.h" #include "gettext.h" #include "hex.h" #include "lockfile.h" @@ -18,7 +19,7 @@ #include "path.h" #include "pathspec.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "strmap.h" #define RESOLVED 0 @@ -877,9 +878,9 @@ static int do_plain_rerere(struct repository *r, static void git_rerere_config(void) { - git_config_get_bool("rerere.enabled", &rerere_enabled); - git_config_get_bool("rerere.autoupdate", &rerere_autoupdate); - git_config(git_default_config, NULL); + repo_config_get_bool(the_repository, "rerere.enabled", &rerere_enabled); + repo_config_get_bool(the_repository, "rerere.autoupdate", &rerere_autoupdate); + repo_config(the_repository, git_default_config, NULL); } static GIT_PATH_FUNC(git_path_rr_cache, "rr-cache") @@ -1000,9 +1001,8 @@ static int handle_cache(struct index_state *istate, break; i = ce_stage(ce) - 1; if (!mmfile[i].ptr) { - mmfile[i].ptr = repo_read_object_file(the_repository, - &ce->oid, &type, - &size); + mmfile[i].ptr = odb_read_object(the_repository->objects, + &ce->oid, &type, &size); if (!mmfile[i].ptr) die(_("unable to read %s"), oid_to_hex(&ce->oid)); @@ -1248,7 +1248,7 @@ void rerere_gc(struct repository *r, struct string_list *rr) &cutoff_resolve, now); repo_config_get_expiry_in_days(the_repository, "gc.rerereunresolved", &cutoff_noresolve, now); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); dir = opendir(repo_git_path_replace(the_repository, &buf, "rr-cache")); if (!dir) die_errno(_("unable to open rr-cache directory")); diff --git a/revision.c b/revision.c index 2c36a9c179..6ba8f67054 100644 --- a/revision.c +++ b/revision.c @@ -8,7 +8,7 @@ #include "hex.h" #include "object-name.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "oidset.h" #include "tag.h" #include "blob.h" @@ -50,8 +50,6 @@ #include "parse-options.h" #include "wildmatch.h" -volatile show_early_output_fn_t show_early_output; - static char *term_bad; static char *term_good; @@ -673,26 +671,66 @@ static void trace2_bloom_filter_statistics_atexit(void) static int forbid_bloom_filters(struct pathspec *spec) { - if (spec->has_wildcard) - return 1; - if (spec->nr > 1) - return 1; - if (spec->magic & ~PATHSPEC_LITERAL) - return 1; - if (spec->nr && (spec->items[0].magic & ~PATHSPEC_LITERAL)) + unsigned int allowed_magic = + PATHSPEC_FROMTOP | + PATHSPEC_MAXDEPTH | + PATHSPEC_LITERAL | + PATHSPEC_GLOB | + PATHSPEC_ATTR; + + if (spec->magic & ~allowed_magic) return 1; + for (size_t nr = 0; nr < spec->nr; nr++) + if (spec->items[nr].magic & ~allowed_magic) + return 1; return 0; } -static void prepare_to_use_bloom_filter(struct rev_info *revs) +static void release_revisions_bloom_keyvecs(struct rev_info *revs); + +static int convert_pathspec_to_bloom_keyvec(struct bloom_keyvec **out, + const struct pathspec_item *pi, + const struct bloom_filter_settings *settings) { - struct pathspec_item *pi; char *path_alloc = NULL; - const char *path, *p; + const char *path; size_t len; - int path_component_nr = 1; + int res = -1; + + len = pi->nowildcard_len; + if (len != pi->len) { + /* + * for path like "dir/file*", nowildcard part would be + * "dir/file", but only "dir" should be used for the + * bloom filter. + */ + while (len > 0 && pi->match[len - 1] != '/') + len--; + } + /* remove single trailing slash from path, if needed */ + if (len > 0 && pi->match[len - 1] == '/') + len--; + if (!len) + goto cleanup; + + if (len != pi->len) { + path_alloc = xmemdupz(pi->match, len); + path = path_alloc; + } else + path = pi->match; + + *out = bloom_keyvec_new(path, len, settings); + + res = 0; +cleanup: + free(path_alloc); + return res; +} + +static void prepare_to_use_bloom_filter(struct rev_info *revs) +{ if (!revs->commits) return; @@ -708,48 +746,14 @@ static void prepare_to_use_bloom_filter(struct rev_info *revs) if (!revs->pruning.pathspec.nr) return; - pi = &revs->pruning.pathspec.items[0]; + revs->bloom_keyvecs_nr = revs->pruning.pathspec.nr; + CALLOC_ARRAY(revs->bloom_keyvecs, revs->bloom_keyvecs_nr); - /* remove single trailing slash from path, if needed */ - if (pi->len > 0 && pi->match[pi->len - 1] == '/') { - path_alloc = xmemdupz(pi->match, pi->len - 1); - path = path_alloc; - } else - path = pi->match; - - len = strlen(path); - if (!len) { - revs->bloom_filter_settings = NULL; - free(path_alloc); - return; - } - - p = path; - while (*p) { - /* - * At this point, the path is normalized to use Unix-style - * path separators. This is required due to how the - * changed-path Bloom filters store the paths. - */ - if (*p == '/') - path_component_nr++; - p++; - } - - revs->bloom_keys_nr = path_component_nr; - ALLOC_ARRAY(revs->bloom_keys, revs->bloom_keys_nr); - - fill_bloom_key(path, len, &revs->bloom_keys[0], - revs->bloom_filter_settings); - path_component_nr = 1; - - p = path + len - 1; - while (p > path) { - if (*p == '/') - fill_bloom_key(path, p - path, - &revs->bloom_keys[path_component_nr++], - revs->bloom_filter_settings); - p--; + for (int i = 0; i < revs->pruning.pathspec.nr; i++) { + if (convert_pathspec_to_bloom_keyvec(&revs->bloom_keyvecs[i], + &revs->pruning.pathspec.items[i], + revs->bloom_filter_settings)) + goto fail; } if (trace2_is_enabled() && !bloom_filter_atexit_registered) { @@ -757,14 +761,18 @@ static void prepare_to_use_bloom_filter(struct rev_info *revs) bloom_filter_atexit_registered = 1; } - free(path_alloc); + return; + +fail: + revs->bloom_filter_settings = NULL; + release_revisions_bloom_keyvecs(revs); } static int check_maybe_different_in_bloom_filter(struct rev_info *revs, struct commit *commit) { struct bloom_filter *filter; - int result = 1, j; + int result = 0; if (!revs->repo->objects->commit_graph) return -1; @@ -779,10 +787,10 @@ static int check_maybe_different_in_bloom_filter(struct rev_info *revs, return -1; } - for (j = 0; result && j < revs->bloom_keys_nr; j++) { - result = bloom_filter_contains(filter, - &revs->bloom_keys[j], - revs->bloom_filter_settings); + for (size_t nr = 0; !result && nr < revs->bloom_keyvecs_nr; nr++) { + result = bloom_filter_contains_vec(filter, + revs->bloom_keyvecs[nr], + revs->bloom_filter_settings); } if (result) @@ -823,7 +831,7 @@ static int rev_compare_tree(struct rev_info *revs, return REV_TREE_SAME; } - if (revs->bloom_keys_nr && !nth_parent) { + if (revs->bloom_keyvecs_nr && !nth_parent) { bloom_ret = check_maybe_different_in_bloom_filter(revs, commit); if (bloom_ret == 0) @@ -850,7 +858,7 @@ static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit, if (!t1) return 0; - if (!nth_parent && revs->bloom_keys_nr) { + if (!nth_parent && revs->bloom_keyvecs_nr) { bloom_ret = check_maybe_different_in_bloom_filter(revs, commit); if (!bloom_ret) return 1; @@ -1479,7 +1487,6 @@ static int limit_list(struct rev_info *revs) while (original_list) { struct commit *commit = pop_commit(&original_list); struct object *obj = &commit->object; - show_early_output_fn_t show; if (commit == interesting_cache) interesting_cache = NULL; @@ -1503,13 +1510,6 @@ static int limit_list(struct rev_info *revs) continue; date = commit->date; p = &commit_list_insert(commit, p)->next; - - show = show_early_output; - if (!show) - continue; - - show(revs, newlist); - show_early_output = NULL; } if (revs->cherry_pick || revs->cherry_mark) cherry_pick_list(newlist, revs); @@ -1636,7 +1636,7 @@ void exclude_hidden_refs(struct ref_exclusions *exclusions, const char *section) cb.exclusions = exclusions; cb.section = section; - git_config(hide_refs_config, &cb); + repo_config(the_repository, hide_refs_config, &cb); } struct all_refs_cb { @@ -1705,7 +1705,8 @@ static void handle_one_reflog_commit(struct object_id *oid, void *cb_data) } } -static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid, +static int handle_one_reflog_ent(const char *refname UNUSED, + struct object_id *ooid, struct object_id *noid, const char *email UNUSED, timestamp_t timestamp UNUSED, int tz UNUSED, @@ -1907,7 +1908,8 @@ static void add_alternate_refs_to_pending(struct rev_info *revs, struct add_alternate_refs_data data; data.revs = revs; data.flags = flags; - for_each_alternate_ref(add_one_alternate_ref, &data); + odb_for_each_alternate_ref(the_repository->objects, + add_one_alternate_ref, &data); } static int add_parents_only(struct rev_info *revs, const char *arg_, int flags, @@ -2060,6 +2062,7 @@ static void prepare_show_merge(struct rev_info *revs) parse_pathspec(&revs->prune_data, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, PATHSPEC_PREFER_FULL | PATHSPEC_LITERAL_PATH, "", prune); revs->limited = 1; + free(prune); } static int dotdot_missing(const char *arg, char *dotdot, @@ -2441,13 +2444,6 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg } else if (!strcmp(arg, "--author-date-order")) { revs->sort_order = REV_SORT_BY_AUTHOR_DATE; revs->topo_order = 1; - } else if (!strcmp(arg, "--early-output")) { - revs->early_output = 100; - revs->topo_order = 1; - } else if (skip_prefix(arg, "--early-output=", &optarg)) { - if (strtoul_ui(optarg, 10, &revs->early_output) < 0) - die("'%s': not a non-negative integer", optarg); - revs->topo_order = 1; } else if (!strcmp(arg, "--parents")) { revs->rewrite_parents = 1; revs->print_parents = 1; @@ -3111,7 +3107,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s /* Pickaxe, diff-filter and rename following need diffs */ if ((revs->diffopt.pickaxe_opts & DIFF_PICKAXE_KINDS_MASK) || - revs->diffopt.filter || + revs->diffopt.filter || revs->diffopt.filter_not || revs->diffopt.flags.follow_renames) revs->diff = 1; @@ -3200,6 +3196,14 @@ static void release_revisions_mailmap(struct string_list *mailmap) static void release_revisions_topo_walk_info(struct topo_walk_info *info); +static void release_revisions_bloom_keyvecs(struct rev_info *revs) +{ + for (size_t nr = 0; nr < revs->bloom_keyvecs_nr; nr++) + bloom_keyvec_free(revs->bloom_keyvecs[nr]); + FREE_AND_NULL(revs->bloom_keyvecs); + revs->bloom_keyvecs_nr = 0; +} + static void free_void_commit_list(void *list) { free_commit_list(list); @@ -3228,11 +3232,7 @@ void release_revisions(struct rev_info *revs) clear_decoration(&revs->treesame, free); line_log_free(revs); oidset_clear(&revs->missing_commits); - - for (int i = 0; i < revs->bloom_keys_nr; i++) - clear_bloom_key(&revs->bloom_keys[i]); - FREE_AND_NULL(revs->bloom_keys); - revs->bloom_keys_nr = 0; + release_revisions_bloom_keyvecs(revs); } static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child) diff --git a/revision.h b/revision.h index 6d369cdad6..21e288c5ba 100644 --- a/revision.h +++ b/revision.h @@ -62,7 +62,7 @@ struct repository; struct rev_info; struct string_list; struct saved_parents; -struct bloom_key; +struct bloom_keyvec; struct bloom_filter_settings; struct option; struct parse_opt_ctx_t; @@ -160,8 +160,6 @@ struct rev_info { /* topo-sort */ enum rev_sort_order sort_order; - unsigned int early_output; - unsigned int ignore_missing:1, ignore_missing_links:1; @@ -360,8 +358,8 @@ struct rev_info { /* Commit graph bloom filter fields */ /* The bloom filter key(s) for the pathspec */ - struct bloom_key *bloom_keys; - int bloom_keys_nr; + struct bloom_keyvec **bloom_keyvecs; + int bloom_keyvecs_nr; /* * The bloom filter settings used to generate the key. @@ -553,10 +551,4 @@ int rewrite_parents(struct rev_info *revs, */ struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit); -/** - * Global for the (undocumented) "--early-output" flag for "git log". - */ -typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *); -extern volatile show_early_output_fn_t show_early_output; - #endif diff --git a/run-command.c b/run-command.c index 8833b23367..ed9575bd6a 100644 --- a/run-command.c +++ b/run-command.c @@ -1817,7 +1817,7 @@ int prepare_auto_maintenance(int quiet, struct child_process *maint) { int enabled, auto_detach; - if (!git_config_get_bool("maintenance.auto", &enabled) && + if (!repo_config_get_bool(the_repository, "maintenance.auto", &enabled) && !enabled) return 0; @@ -1826,8 +1826,8 @@ 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 (git_config_get_bool("maintenance.autodetach", &auto_detach) && - git_config_get_bool("gc.autodetach", &auto_detach)) + if (repo_config_get_bool(the_repository, "maintenance.autodetach", &auto_detach) && + repo_config_get_bool(the_repository, "gc.autodetach", &auto_detach)) auto_detach = 1; maint->git_cmd = 1; diff --git a/sane-ctype.h b/sane-ctype.h index cbea1b299b..4f476c4381 100644 --- a/sane-ctype.h +++ b/sane-ctype.h @@ -1,6 +1,15 @@ #ifndef SANE_CTYPE_H #define SANE_CTYPE_H +/* + * Explicitly include <ctype.h> so that its header guards kick in from here on. + * This ensures that the file won't get included after "sane-ctype.h", as that + * would otherwise lead to a compiler error because the function declarations + * for `int isascii(int c)` et al would be mangled by our macros with the same + * name. + */ +#include <ctype.h> + /* Sane ctype - no locale, and works with signed chars */ #undef isascii #undef isspace @@ -101,9 +101,9 @@ static int set_scalar_config(const struct scalar_config *config, int reconfigure int res; if ((reconfigure && config->overwrite_on_reconfigure) || - git_config_get_string(config->key, &value)) { + repo_config_get_string(the_repository, config->key, &value)) { trace2_data_string("scalar", the_repository, config->key, "created"); - res = git_config_set_gently(config->key, config->value); + res = repo_config_set_gently(the_repository, config->key, config->value); } else { trace2_data_string("scalar", the_repository, config->key, "exists"); res = 0; @@ -170,6 +170,7 @@ static int set_recommended_config(int reconfigure) { "core.autoCRLF", "false" }, { "core.safeCRLF", "false" }, { "fetch.showForcedUpdates", "false" }, + { "pack.usePathWalk", "true" }, { NULL, NULL }, }; int i; @@ -192,12 +193,12 @@ static int set_recommended_config(int reconfigure) * The `log.excludeDecoration` setting is special because it allows * for multiple values. */ - if (git_config_get_string("log.excludeDecoration", &value)) { + if (repo_config_get_string(the_repository, "log.excludeDecoration", &value)) { trace2_data_string("scalar", the_repository, "log.excludeDecoration", "created"); - if (git_config_set_multivar_gently("log.excludeDecoration", - "refs/prefetch/*", - CONFIG_REGEX_NONE, 0)) + if (repo_config_set_multivar_gently(the_repository, "log.excludeDecoration", + "refs/prefetch/*", + CONFIG_REGEX_NONE, 0)) return error(_("could not configure " "log.excludeDecoration")); } else { @@ -321,7 +322,7 @@ static int set_config(const char *fmt, ...) value = strchr(buf.buf, '='); if (value) *(value++) = '\0'; - res = git_config_set_gently(buf.buf, value); + res = repo_config_set_gently(the_repository, buf.buf, value); strbuf_release(&buf); return res; @@ -712,7 +713,7 @@ static int cmd_reconfigure(int argc, const char **argv) maintenance_str); } - git_config(get_scalar_repos, &scalar_repos); + repo_config(the_repository, get_scalar_repos, &scalar_repos); for (size_t i = 0; i < scalar_repos.nr; i++) { int succeeded = 0; @@ -762,7 +763,7 @@ static int cmd_reconfigure(int argc, const char **argv) break; } - git_config_clear(); + repo_config_clear(the_repository); if (repo_init(&r, gitdir.buf, commondir.buf)) goto loop_end; diff --git a/send-pack.c b/send-pack.c index 86592ce526..67d6987b1c 100644 --- a/send-pack.c +++ b/send-pack.c @@ -4,7 +4,7 @@ #include "date.h" #include "gettext.h" #include "hex.h" -#include "object-store.h" +#include "odb.h" #include "pkt-line.h" #include "sideband.h" #include "run-command.h" @@ -45,7 +45,7 @@ int option_parse_push_signed(const struct option *opt, static void feed_object(struct repository *r, const struct object_id *oid, FILE *fh, int negative) { - if (negative && !has_object(r, oid, 0)) + if (negative && !odb_has_object(r->objects, oid, 0)) return; if (negative) @@ -257,6 +257,13 @@ static int receive_status(struct repository *r, refname); continue; } + + /* + * Clients sending duplicate refs can cause the same value + * to be overridden, causing a memory leak. + */ + free(hint->remote_status); + if (!strcmp(head, "ng")) { hint->status = REF_STATUS_REMOTE_REJECT; if (p) diff --git a/sequencer.c b/sequencer.c index 97dfdcfc19..6d29a938aa 100644 --- a/sequencer.c +++ b/sequencer.c @@ -13,7 +13,7 @@ #include "dir.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "object.h" #include "pager.h" #include "commit.h" @@ -327,7 +327,7 @@ static int git_sequencer_config(const char *k, const char *v, void sequencer_init_config(struct replay_opts *opts) { opts->default_msg_cleanup = COMMIT_MSG_CLEANUP_NONE; - git_config(git_sequencer_config, opts); + repo_config(the_repository, git_sequencer_config, opts); } static inline int is_rebase_i(const struct replay_opts *opts) @@ -2056,6 +2056,9 @@ static int update_squash_messages(struct repository *r, const char *message, *body; const char *encoding = get_commit_output_encoding(); + if (!is_fixup(command)) + BUG("not a FIXUP or SQUASH %d", command); + if (ctx->current_fixup_count > 0) { struct strbuf header = STRBUF_INIT; char *eol; @@ -2123,8 +2126,7 @@ static int update_squash_messages(struct repository *r, strbuf_addstr(&buf, "\n\n"); strbuf_add_commented_lines(&buf, body, strlen(body), comment_line_str); - } else - return error(_("unknown command: %d"), command); + } repo_unuse_commit_buffer(r, commit, message); if (!res) @@ -2707,6 +2709,7 @@ static int check_merge_commit_insn(enum todo_command command) return error(_("cannot squash merge commit into another commit")); case TODO_MERGE: + case TODO_DROP: return 0; default: @@ -3636,57 +3639,57 @@ static int save_opts(struct replay_opts *opts) int res = 0; if (opts->no_commit) - res |= git_config_set_in_file_gently(opts_file, + res |= repo_config_set_in_file_gently(the_repository, opts_file, "options.no-commit", NULL, "true"); if (opts->edit >= 0) - res |= git_config_set_in_file_gently(opts_file, "options.edit", NULL, + res |= repo_config_set_in_file_gently(the_repository, opts_file, "options.edit", NULL, opts->edit ? "true" : "false"); if (opts->allow_empty) - res |= git_config_set_in_file_gently(opts_file, + res |= repo_config_set_in_file_gently(the_repository, opts_file, "options.allow-empty", NULL, "true"); if (opts->allow_empty_message) - res |= git_config_set_in_file_gently(opts_file, + res |= repo_config_set_in_file_gently(the_repository, opts_file, "options.allow-empty-message", NULL, "true"); if (opts->drop_redundant_commits) - res |= git_config_set_in_file_gently(opts_file, + res |= repo_config_set_in_file_gently(the_repository, opts_file, "options.drop-redundant-commits", NULL, "true"); if (opts->keep_redundant_commits) - res |= git_config_set_in_file_gently(opts_file, + res |= repo_config_set_in_file_gently(the_repository, opts_file, "options.keep-redundant-commits", NULL, "true"); if (opts->signoff) - res |= git_config_set_in_file_gently(opts_file, + res |= repo_config_set_in_file_gently(the_repository, opts_file, "options.signoff", NULL, "true"); if (opts->record_origin) - res |= git_config_set_in_file_gently(opts_file, + res |= repo_config_set_in_file_gently(the_repository, opts_file, "options.record-origin", NULL, "true"); if (opts->allow_ff) - res |= git_config_set_in_file_gently(opts_file, + res |= repo_config_set_in_file_gently(the_repository, opts_file, "options.allow-ff", NULL, "true"); if (opts->mainline) { struct strbuf buf = STRBUF_INIT; strbuf_addf(&buf, "%d", opts->mainline); - res |= git_config_set_in_file_gently(opts_file, + res |= repo_config_set_in_file_gently(the_repository, opts_file, "options.mainline", NULL, buf.buf); strbuf_release(&buf); } if (opts->strategy) - res |= git_config_set_in_file_gently(opts_file, + res |= repo_config_set_in_file_gently(the_repository, opts_file, "options.strategy", NULL, opts->strategy); if (opts->gpg_sign) - res |= git_config_set_in_file_gently(opts_file, + res |= repo_config_set_in_file_gently(the_repository, opts_file, "options.gpg-sign", NULL, opts->gpg_sign); for (size_t i = 0; i < opts->xopts.nr; i++) - res |= git_config_set_multivar_in_file_gently(opts_file, + res |= repo_config_set_multivar_in_file_gently(the_repository, opts_file, "options.strategy-option", opts->xopts.v[i], "^$", NULL, 0); if (opts->allow_rerere_auto) - res |= git_config_set_in_file_gently(opts_file, + res |= repo_config_set_in_file_gently(the_repository, opts_file, "options.allow-rerere-auto", NULL, opts->allow_rerere_auto == RERERE_AUTOUPDATE ? "true" : "false"); if (opts->explicit_cleanup) - res |= git_config_set_in_file_gently(opts_file, + res |= repo_config_set_in_file_gently(the_repository, opts_file, "options.default-msg-cleanup", NULL, describe_cleanup_mode(opts->default_msg_cleanup)); return res; @@ -5491,9 +5494,8 @@ int sequencer_pick_revisions(struct repository *r, if (!repo_get_oid(r, name, &oid)) { if (!lookup_commit_reference_gently(r, &oid, 1)) { - enum object_type type = oid_object_info(r, - &oid, - NULL); + enum object_type type = odb_read_object_info(r->objects, + &oid, NULL); res = error(_("%s: can't cherry-pick a %s"), name, type_name(type)); goto out; @@ -5821,7 +5823,7 @@ static int make_script_with_merges(struct pretty_print_context *pp, *cmd_reset = abbr ? "t" : "reset", *cmd_merge = abbr ? "m" : "merge"; - git_config_get_int("rebase.maxlabellength", &state.max_label_length); + repo_config_get_int(the_repository, "rebase.maxlabellength", &state.max_label_length); oidmap_init(&commit2todo, 0); oidmap_init(&state.commit2label, 0); @@ -6076,7 +6078,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc, revs.topo_order = 1; revs.pretty_given = 1; - git_config_get_string("rebase.instructionFormat", &format); + repo_config_get_string(the_repository, "rebase.instructionFormat", &format); if (!format || !*format) { free(format); format = xstrdup("# %s"); @@ -14,7 +14,7 @@ static int advertise_sid = -1; static int advertise_object_info = -1; -static int client_hash_algo = GIT_HASH_SHA1; +static int client_hash_algo = GIT_HASH_SHA1_LEGACY; static int always_advertise(struct repository *r UNUSED, struct strbuf *value UNUSED) diff --git a/server-info.c b/server-info.c index d6cd20a39d..9bb30d9ab7 100644 --- a/server-info.c +++ b/server-info.c @@ -11,7 +11,7 @@ #include "packfile.h" #include "path.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "server-info.h" #include "strbuf.h" #include "tempfile.h" @@ -753,7 +753,8 @@ static int check_repository_format_gently(const char *gitdir, struct repository_ die("%s", err.buf); } - repository_format_precious_objects = candidate->precious_objects; + the_repository->repository_format_precious_objects = candidate->precious_objects; + string_list_clear(&candidate->unknown_extensions, 0); string_list_clear(&candidate->v1_only_extensions, 0); @@ -814,7 +815,7 @@ int upgrade_repository_format(int target_version) } strbuf_addf(&repo_version, "%d", target_version); - git_config_set("core.repositoryformatversion", repo_version.buf); + repo_config_set(the_repository, "core.repositoryformatversion", repo_version.buf); ret = 1; @@ -835,9 +836,12 @@ static void init_repository_format(struct repository_format *format) int read_repository_format(struct repository_format *format, const char *path) { clear_repository_format(format); + format->hash_algo = GIT_HASH_SHA1_LEGACY; git_config_from_file(check_repo_format, path, format); - if (format->version == -1) + if (format->version == -1) { clear_repository_format(format); + format->hash_algo = GIT_HASH_SHA1_LEGACY; + } return format->version; } @@ -1456,8 +1460,9 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, if (env_ceiling_dirs) { int empty_entry_found = 0; + static const char path_sep[] = { PATH_SEP, '\0' }; - string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1); + string_list_split(&ceiling_dirs, env_ceiling_dirs, path_sep, -1); filter_string_list(&ceiling_dirs, 0, canonicalize_ceiling_entry, &empty_entry_found); ceil_offset = longest_ancestor_length(dir->buf, &ceiling_dirs); @@ -1737,7 +1742,7 @@ const char *setup_git_directory_gently(int *nongit_ok) * configuration (including the per-repo config file that we * ignored previously). */ - git_config_clear(); + repo_config_clear(the_repository); /* * Let's assume that we are in a git repository. @@ -1864,6 +1869,8 @@ const char *setup_git_directory_gently(int *nongit_ok) the_repository->repository_format_partial_clone = repo_fmt.partial_clone; repo_fmt.partial_clone = NULL; + the_repository->repository_format_precious_objects = + repo_fmt.precious_objects; } } /* @@ -1871,7 +1878,7 @@ const char *setup_git_directory_gently(int *nongit_ok) * the core.precomposeunicode configuration, this * has to happen after the above block that finds * out where the repository is, i.e. a preparation - * for calling git_config_get_bool(). + * for calling repo_config_get_bool(). */ if (prefix) { prefix = precompose_string_if_needed(prefix); @@ -2222,21 +2229,21 @@ void initialize_repository_version(int hash_algo, * version will get adjusted by git-clone(1) once it has learned about * the remote repository's format. */ - if (hash_algo != GIT_HASH_SHA1 || + if (hash_algo != GIT_HASH_SHA1_LEGACY || ref_storage_format != REF_STORAGE_FORMAT_FILES) target_version = GIT_REPO_VERSION_READ; - if (hash_algo != GIT_HASH_SHA1 && hash_algo != GIT_HASH_UNKNOWN) - git_config_set("extensions.objectformat", - hash_algos[hash_algo].name); + if (hash_algo != GIT_HASH_SHA1_LEGACY && hash_algo != GIT_HASH_UNKNOWN) + repo_config_set(the_repository, "extensions.objectformat", + hash_algos[hash_algo].name); else if (reinit) - git_config_set_gently("extensions.objectformat", NULL); + repo_config_set_gently(the_repository, "extensions.objectformat", NULL); if (ref_storage_format != REF_STORAGE_FORMAT_FILES) - git_config_set("extensions.refstorage", - ref_storage_format_to_name(ref_storage_format)); + repo_config_set(the_repository, "extensions.refstorage", + ref_storage_format_to_name(ref_storage_format)); else if (reinit) - git_config_set_gently("extensions.refstorage", NULL); + repo_config_set_gently(the_repository, "extensions.refstorage", NULL); if (reinit) { struct strbuf config = STRBUF_INIT; @@ -2253,7 +2260,7 @@ void initialize_repository_version(int hash_algo, } strbuf_addf(&repo_version, "%d", target_version); - git_config_set("core.repositoryformatversion", repo_version.buf); + repo_config_set(the_repository, "core.repositoryformatversion", repo_version.buf); strbuf_release(&repo_version); } @@ -2331,9 +2338,9 @@ static int create_default_files(const char *template_path, * disk). */ copy_templates(template_path); - git_config_clear(); + repo_config_clear(the_repository); repo_settings_reset_shared_repository(the_repository); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); reinit = is_reinit(); @@ -2369,17 +2376,17 @@ static int create_default_files(const char *template_path, if (filemode && !reinit && (st1.st_mode & S_IXUSR)) filemode = 0; } - git_config_set("core.filemode", filemode ? "true" : "false"); + repo_config_set(the_repository, "core.filemode", filemode ? "true" : "false"); if (is_bare_repository()) - git_config_set("core.bare", "true"); + repo_config_set(the_repository, "core.bare", "true"); else { - git_config_set("core.bare", "false"); + repo_config_set(the_repository, "core.bare", "false"); /* allow template config file to override the default */ if (repo_settings_get_log_all_ref_updates(the_repository) == LOG_REFS_UNSET) - git_config_set("core.logallrefupdates", "true"); + repo_config_set(the_repository, "core.logallrefupdates", "true"); if (needs_work_tree_config(original_git_dir, work_tree)) - git_config_set("core.worktree", work_tree); + repo_config_set(the_repository, "core.worktree", work_tree); } if (!reinit) { @@ -2392,12 +2399,12 @@ static int create_default_files(const char *template_path, S_ISLNK(st1.st_mode)) unlink(path.buf); /* good */ else - git_config_set("core.symlinks", "false"); + repo_config_set(the_repository, "core.symlinks", "false"); /* Check if the filesystem is case-insensitive */ repo_git_path_replace(the_repository, &path, "CoNfIg"); if (!access(path.buf, F_OK)) - git_config_set("core.ignorecase", "true"); + repo_config_set(the_repository, "core.ignorecase", "true"); probe_utf8_pathname_composition(); } @@ -2481,6 +2488,18 @@ static int read_default_format_config(const char *key, const char *value, goto out; } + /* + * Enable the reftable format when "features.experimental" is enabled. + * "init.defaultRefFormat" takes precedence over this setting. + */ + if (!strcmp(key, "feature.experimental") && + cfg->ref_format == REF_STORAGE_FORMAT_UNKNOWN && + git_config_bool(key, value)) { + cfg->ref_format = REF_STORAGE_FORMAT_REFTABLE; + ret = 0; + goto out; + } + ret = 0; out: free(str); @@ -2541,6 +2560,8 @@ static void repository_format_configure(struct repository_format *repo_fmt, repo_fmt->ref_storage_format = ref_format; } else if (cfg.ref_format != REF_STORAGE_FORMAT_UNKNOWN) { repo_fmt->ref_storage_format = cfg.ref_format; + } else { + repo_fmt->ref_storage_format = REF_STORAGE_FORMAT_DEFAULT; } repo_set_ref_storage_format(the_repository, repo_fmt->ref_storage_format); } @@ -2590,7 +2611,7 @@ int init_db(const char *git_dir, const char *real_git_dir, * have set up the repository format such that we can evaluate * includeIf conditions correctly in the case of re-initialization. */ - git_config(platform_core_config, NULL); + repo_config(the_repository, platform_core_config, NULL); safe_create_dir(the_repository, git_dir, 0); @@ -2619,8 +2640,8 @@ int init_db(const char *git_dir, const char *real_git_dir, xsnprintf(buf, sizeof(buf), "%d", OLD_PERM_EVERYBODY); else BUG("invalid value for shared_repository"); - git_config_set("core.sharedrepository", buf); - git_config_set("receive.denyNonFastforwards", "true"); + repo_config_set(the_repository, "core.sharedrepository", buf); + repo_config_set(the_repository, "receive.denyNonFastforwards", "true"); } if (!(flags & INIT_DB_QUIET)) { @@ -149,7 +149,7 @@ struct repository_format { { \ .version = -1, \ .is_bare = -1, \ - .hash_algo = GIT_HASH_SHA1, \ + .hash_algo = GIT_HASH_DEFAULT, \ .ref_storage_format = REF_STORAGE_FORMAT_FILES, \ .unknown_extensions = STRING_LIST_INIT_DUP, \ .v1_only_extensions = STRING_LIST_INIT_DUP, \ @@ -5,7 +5,7 @@ #include "repository.h" #include "tempfile.h" #include "lockfile.h" -#include "object-store.h" +#include "odb.h" #include "commit.h" #include "tag.h" #include "pkt-line.h" @@ -310,8 +310,8 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data) if (graft->nr_parent != -1) return 0; if (data->flags & QUICK) { - if (!has_object(the_repository, &graft->oid, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) + if (!odb_has_object(the_repository->objects, &graft->oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) return 0; } else if (data->flags & SEEN_ONLY) { struct commit *c = lookup_commit(the_repository, &graft->oid); @@ -477,8 +477,8 @@ void prepare_shallow_info(struct shallow_info *info, struct oid_array *sa) ALLOC_ARRAY(info->ours, sa->nr); ALLOC_ARRAY(info->theirs, sa->nr); for (size_t i = 0; i < sa->nr; i++) { - if (has_object(the_repository, sa->oid + i, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) { + if (odb_has_object(the_repository->objects, sa->oid + i, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) { struct commit_graft *graft; graft = lookup_commit_graft(the_repository, &sa->oid[i]); @@ -515,8 +515,8 @@ void remove_nonexistent_theirs_shallow(struct shallow_info *info) for (i = dst = 0; i < info->nr_theirs; i++) { if (i != dst) info->theirs[dst] = info->theirs[i]; - if (has_object(the_repository, oid + info->theirs[i], - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) + if (odb_has_object(the_repository->objects, oid + info->theirs[i], + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) dst++; } info->nr_theirs = dst; diff --git a/shared.mak b/shared.mak index 1a99848a95..5c7bc94785 100644 --- a/shared.mak +++ b/shared.mak @@ -88,6 +88,8 @@ ifndef V QUIET_LINT_GITLINK = @echo ' ' LINT GITLINK $<; QUIET_LINT_MANSEC = @echo ' ' LINT MAN SEC $<; + QUIET_LINT_DELIMSEC = @echo ' ' LINT DEL SEC $<; + QUIET_LINT_DOCSTYLE = @echo ' ' LINT DOCSTYLE $<; QUIET_LINT_MANEND = @echo ' ' LINT MAN END $<; export V diff --git a/sideband.c b/sideband.c index 251e9615ed..8f15b98a65 100644 --- a/sideband.c +++ b/sideband.c @@ -39,9 +39,9 @@ static int use_sideband_colors(void) if (use_sideband_colors_cached >= 0) return use_sideband_colors_cached; - if (!git_config_get_string_tmp(key, &value)) + if (!repo_config_get_string_tmp(the_repository, key, &value)) use_sideband_colors_cached = git_config_colorbool(key, value); - else if (!git_config_get_string_tmp("color.ui", &value)) + else if (!repo_config_get_string_tmp(the_repository, "color.ui", &value)) use_sideband_colors_cached = git_config_colorbool("color.ui", value); else use_sideband_colors_cached = GIT_COLOR_AUTO; @@ -49,7 +49,7 @@ static int use_sideband_colors(void) for (i = 0; i < ARRAY_SIZE(keywords); i++) { strbuf_reset(&sb); strbuf_addf(&sb, "%s.%s", key, keywords[i].keyword); - if (git_config_get_string_tmp(sb.buf, &value)) + if (repo_config_get_string_tmp(the_repository, sb.buf, &value)) continue; color_parse(value, keywords[i].color); } @@ -8,55 +8,55 @@ #include "utf8.h" #include "date.h" -int starts_with(const char *str, const char *prefix) +bool starts_with(const char *str, const char *prefix) { for (; ; str++, prefix++) if (!*prefix) - return 1; + return true; else if (*str != *prefix) - return 0; + return false; } -int istarts_with(const char *str, const char *prefix) +bool istarts_with(const char *str, const char *prefix) { for (; ; str++, prefix++) if (!*prefix) - return 1; + return true; else if (tolower(*str) != tolower(*prefix)) - return 0; + return false; } -int starts_with_mem(const char *str, size_t len, const char *prefix) +bool starts_with_mem(const char *str, size_t len, const char *prefix) { const char *end = str + len; for (; ; str++, prefix++) { if (!*prefix) - return 1; + return true; else if (str == end || *str != *prefix) - return 0; + return false; } } -int skip_to_optional_arg_default(const char *str, const char *prefix, +bool skip_to_optional_arg_default(const char *str, const char *prefix, const char **arg, const char *def) { const char *p; if (!skip_prefix(str, prefix, &p)) - return 0; + return false; if (!*p) { if (arg) *arg = def; - return 1; + return true; } if (*p != '=') - return 0; + return false; if (arg) *arg = p + 1; - return 1; + return true; } /* @@ -660,9 +660,9 @@ char *xstrvfmt(const char *fmt, va_list ap); __attribute__((format (printf, 1, 2))) char *xstrfmt(const char *fmt, ...); -int starts_with(const char *str, const char *prefix); -int istarts_with(const char *str, const char *prefix); -int starts_with_mem(const char *str, size_t len, const char *prefix); +bool starts_with(const char *str, const char *prefix); +bool istarts_with(const char *str, const char *prefix); +bool starts_with_mem(const char *str, size_t len, const char *prefix); /* * If the string "str" is the same as the string in "prefix", then the "arg" @@ -678,16 +678,16 @@ int starts_with_mem(const char *str, size_t len, const char *prefix); * can be used instead of !strcmp(arg, "--key") and then * skip_prefix(arg, "--key=", &arg) to parse such an option. */ -int skip_to_optional_arg_default(const char *str, const char *prefix, +bool skip_to_optional_arg_default(const char *str, const char *prefix, const char **arg, const char *def); -static inline int skip_to_optional_arg(const char *str, const char *prefix, +static inline bool skip_to_optional_arg(const char *str, const char *prefix, const char **arg) { return skip_to_optional_arg_default(str, prefix, arg, ""); } -static inline int ends_with(const char *str, const char *suffix) +static inline bool ends_with(const char *str, const char *suffix) { size_t len; return strip_suffix(str, suffix, &len); diff --git a/streaming.c b/streaming.c index 6d6512e2e0..4b13827668 100644 --- a/streaming.c +++ b/streaming.c @@ -10,7 +10,7 @@ #include "streaming.h" #include "repository.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "replace-object.h" #include "packfile.h" @@ -44,7 +44,7 @@ struct git_istream { union { struct { - char *buf; /* from oid_object_info_extended() */ + char *buf; /* from odb_read_object_info_extended() */ unsigned long read_ptr; } incore; @@ -403,8 +403,8 @@ static int open_istream_incore(struct git_istream *st, struct repository *r, oi.typep = type; oi.sizep = &st->size; oi.contentp = (void **)&st->u.incore.buf; - return oid_object_info_extended(r, oid, &oi, - OBJECT_INFO_DIE_IF_CORRUPT); + return odb_read_object_info_extended(r->objects, oid, &oi, + OBJECT_INFO_DIE_IF_CORRUPT); } /***************************************************************************** @@ -422,7 +422,7 @@ static int istream_source(struct git_istream *st, oi.typep = type; oi.sizep = &size; - status = oid_object_info_extended(r, oid, &oi, 0); + status = odb_read_object_info_extended(r->objects, oid, &oi, 0); if (status < 0) return status; diff --git a/string-list.c b/string-list.c index bf061fec56..343cf1ca90 100644 --- a/string-list.c +++ b/string-list.c @@ -1,5 +1,3 @@ -#define DISABLE_SIGN_COMPARE_WARNINGS - #include "git-compat-util.h" #include "string-list.h" @@ -17,19 +15,19 @@ void string_list_init_dup(struct string_list *list) /* if there is no exact match, point to the index where the entry could be * inserted */ -static int get_entry_index(const struct string_list *list, const char *string, - int *exact_match) +static size_t get_entry_index(const struct string_list *list, const char *string, + int *exact_match) { - int left = -1, right = list->nr; + size_t left = 0, right = list->nr; compare_strings_fn cmp = list->cmp ? list->cmp : strcmp; - while (left + 1 < right) { - int middle = left + (right - left) / 2; + while (left < right) { + size_t middle = left + (right - left) / 2; int compare = cmp(string, list->items[middle].string); if (compare < 0) right = middle; else if (compare > 0) - left = middle; + left = middle + 1; else { *exact_match = 1; return middle; @@ -40,14 +38,13 @@ static int get_entry_index(const struct string_list *list, const char *string, return right; } -/* returns -1-index if already exists */ -static int add_entry(int insert_at, struct string_list *list, const char *string) +static size_t add_entry(struct string_list *list, const char *string) { int exact_match = 0; - int index = insert_at != -1 ? insert_at : get_entry_index(list, string, &exact_match); + size_t index = get_entry_index(list, string, &exact_match); if (exact_match) - return -1 - index; + return index; ALLOC_GROW(list->items, list->nr+1, list->alloc); if (index < list->nr) @@ -63,10 +60,7 @@ static int add_entry(int insert_at, struct string_list *list, const char *string struct string_list_item *string_list_insert(struct string_list *list, const char *string) { - int index = add_entry(-1, list, string); - - if (index < 0) - index = -1 - index; + size_t index = add_entry(list, string); return list->items + index; } @@ -116,9 +110,9 @@ struct string_list_item *string_list_lookup(struct string_list *list, const char void string_list_remove_duplicates(struct string_list *list, int free_util) { if (list->nr > 1) { - int src, dst; + size_t dst = 1; compare_strings_fn cmp = list->cmp ? list->cmp : strcmp; - for (src = dst = 1; src < list->nr; src++) { + for (size_t src = 1; src < list->nr; src++) { if (!cmp(list->items[dst - 1].string, list->items[src].string)) { if (list->strdup_strings) free(list->items[src].string); @@ -134,8 +128,8 @@ void string_list_remove_duplicates(struct string_list *list, int free_util) int for_each_string_list(struct string_list *list, string_list_each_func_t fn, void *cb_data) { - int i, ret = 0; - for (i = 0; i < list->nr; i++) + int ret = 0; + for (size_t i = 0; i < list->nr; i++) if ((ret = fn(&list->items[i], cb_data))) break; return ret; @@ -144,8 +138,8 @@ int for_each_string_list(struct string_list *list, void filter_string_list(struct string_list *list, int free_util, string_list_each_func_t want, void *cb_data) { - int src, dst = 0; - for (src = 0; src < list->nr; src++) { + size_t dst = 0; + for (size_t src = 0; src < list->nr; src++) { if (want(&list->items[src], cb_data)) { list->items[dst++] = list->items[src]; } else { @@ -171,13 +165,12 @@ void string_list_remove_empty_items(struct string_list *list, int free_util) void string_list_clear(struct string_list *list, int free_util) { if (list->items) { - int i; if (list->strdup_strings) { - for (i = 0; i < list->nr; i++) + for (size_t i = 0; i < list->nr; i++) free(list->items[i].string); } if (free_util) { - for (i = 0; i < list->nr; i++) + for (size_t i = 0; i < list->nr; i++) free(list->items[i].util); } free(list->items); @@ -189,13 +182,12 @@ void string_list_clear(struct string_list *list, int free_util) void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc) { if (list->items) { - int i; if (clearfunc) { - for (i = 0; i < list->nr; i++) + for (size_t i = 0; i < list->nr; i++) clearfunc(list->items[i].util, list->items[i].string); } if (list->strdup_strings) { - for (i = 0; i < list->nr; i++) + for (size_t i = 0; i < list->nr; i++) free(list->items[i].string); } free(list->items); @@ -284,55 +276,99 @@ void unsorted_string_list_delete_item(struct string_list *list, int i, int free_ list->nr--; } -int string_list_split(struct string_list *list, const char *string, - int delim, int maxsplit) +/* + * append a substring [p..end] to list; return number of things it + * appended to the list. + */ +static int append_one(struct string_list *list, + const char *p, const char *end, + int in_place, unsigned flags) +{ + if (!end) + end = p + strlen(p); + + if ((flags & STRING_LIST_SPLIT_TRIM)) { + /* rtrim */ + for (; p < end; end--) + if (!isspace(end[-1])) + break; + } + + if ((flags & STRING_LIST_SPLIT_NONEMPTY) && (end <= p)) + return 0; + + if (in_place) { + *((char *)end) = '\0'; + string_list_append(list, p); + } else { + string_list_append_nodup(list, xmemdupz(p, end - p)); + } + return 1; +} + +/* + * Unfortunately this cannot become a public interface, as _in_place() + * wants to have "const char *string" while the other variant wants to + * have "char *string" for type safety. + * + * This accepts "const char *string" to allow both wrappers to use it; + * it internally casts away the constness when in_place is true by + * taking advantage of strpbrk() that takes a "const char *" arg and + * returns "char *" pointer into that const string. Yucky but works ;-). + */ +static int split_string(struct string_list *list, const char *string, const char *delim, + int maxsplit, int in_place, unsigned flags) { int count = 0; - const char *p = string, *end; + const char *p = string; + + if (in_place && list->strdup_strings) + BUG("string_list_split_in_place() called with strdup_strings"); + else if (!in_place && !list->strdup_strings) + BUG("string_list_split() called without strdup_strings"); - if (!list->strdup_strings) - die("internal error in string_list_split(): " - "list->strdup_strings must be set"); for (;;) { - count++; - if (maxsplit >= 0 && count > maxsplit) { - string_list_append(list, p); - return count; + char *end; + + if (flags & STRING_LIST_SPLIT_TRIM) { + /* ltrim */ + while (*p && isspace(*p)) + p++; } - end = strchr(p, delim); - if (end) { - string_list_append_nodup(list, xmemdupz(p, end - p)); - p = end + 1; - } else { - string_list_append(list, p); + + if (0 <= maxsplit && maxsplit <= count) + end = NULL; + else + end = strpbrk(p, delim); + + count += append_one(list, p, end, in_place, flags); + + if (!end) return count; - } + p = end + 1; } } +int string_list_split(struct string_list *list, const char *string, + const char *delim, int maxsplit) +{ + return split_string(list, string, delim, maxsplit, 0, 0); +} + int string_list_split_in_place(struct string_list *list, char *string, const char *delim, int maxsplit) { - int count = 0; - char *p = string, *end; + return split_string(list, string, delim, maxsplit, 1, 0); +} - if (list->strdup_strings) - die("internal error in string_list_split_in_place(): " - "list->strdup_strings must not be set"); - for (;;) { - count++; - if (maxsplit >= 0 && count > maxsplit) { - string_list_append(list, p); - return count; - } - end = strpbrk(p, delim); - if (end) { - *end = '\0'; - string_list_append(list, p); - p = end + 1; - } else { - string_list_append(list, p); - return count; - } - } +int string_list_split_f(struct string_list *list, const char *string, + const char *delim, int maxsplit, unsigned flags) +{ + return split_string(list, string, delim, maxsplit, 0, flags); +} + +int string_list_split_in_place_f(struct string_list *list, char *string, + const char *delim, int maxsplit, unsigned flags) +{ + return split_string(list, string, delim, maxsplit, 1, flags); } diff --git a/string-list.h b/string-list.h index 122b318641..2b438c7733 100644 --- a/string-list.h +++ b/string-list.h @@ -254,7 +254,7 @@ 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); /** - * Split string into substrings on character `delim` and append the + * 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 * allocated to hold the substrings. If maxsplit is non-negative, @@ -262,15 +262,15 @@ void unsorted_string_list_delete_item(struct string_list *list, int i, int free_ * appended to list. * * Examples: - * string_list_split(l, "foo:bar:baz", ':', -1) -> ["foo", "bar", "baz"] - * string_list_split(l, "foo:bar:baz", ':', 0) -> ["foo:bar:baz"] - * string_list_split(l, "foo:bar:baz", ':', 1) -> ["foo", "bar:baz"] - * string_list_split(l, "foo:bar:", ':', -1) -> ["foo", "bar", ""] - * string_list_split(l, "", ':', -1) -> [""] - * string_list_split(l, ":", ':', -1) -> ["", ""] + * string_list_split(l, "foo:bar:baz", ":", -1) -> ["foo", "bar", "baz"] + * string_list_split(l, "foo:bar:baz", ":", 0) -> ["foo:bar:baz"] + * string_list_split(l, "foo:bar:baz", ":", 1) -> ["foo", "bar:baz"] + * string_list_split(l, "foo:bar:", ":", -1) -> ["foo", "bar", ""] + * string_list_split(l, "", ":", -1) -> [""] + * string_list_split(l, ":", ":", -1) -> ["", ""] */ int string_list_split(struct string_list *list, const char *string, - int delim, int maxsplit); + const char *delim, int maxsplit); /* * Like string_list_split(), except that string is split in-place: the @@ -281,4 +281,21 @@ int string_list_split(struct string_list *list, const char *string, */ int string_list_split_in_place(struct string_list *list, char *string, const char *delim, int maxsplit); + +/* Flag bits for split_f and split_in_place_f functions */ +enum { + /* + * trim whitespaces around resulting string piece before adding + * it to the list + */ + STRING_LIST_SPLIT_TRIM = (1 << 0), + /* omit adding empty string piece to the resulting list */ + STRING_LIST_SPLIT_NONEMPTY = (1 << 1), +}; + +int string_list_split_f(struct string_list *, const char *string, + const char *delim, int maxsplit, unsigned flags); + +int string_list_split_in_place_f(struct string_list *, char *string, + const char *delim, int maxsplit, unsigned flags); #endif /* STRING_LIST_H */ diff --git a/sub-process.c b/sub-process.c index 1daf5a9752..83bf0a0e82 100644 --- a/sub-process.c +++ b/sub-process.c @@ -30,23 +30,20 @@ struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const ch int subprocess_read_status(int fd, struct strbuf *status) { - struct strbuf **pair; - char *line; int len; for (;;) { + char *line; + const char *value; + len = packet_read_line_gently(fd, NULL, &line); if ((len < 0) || !line) break; - pair = strbuf_split_str(line, '=', 2); - if (pair[0] && pair[0]->len && pair[1]) { + if (skip_prefix(line, "status=", &value)) { /* the last "status=<foo>" line wins */ - if (!strcmp(pair[0]->buf, "status=")) { - strbuf_reset(status); - strbuf_addbuf(status, pair[1]); - } + strbuf_reset(status); + strbuf_addstr(status, value); } - strbuf_list_free(pair); } return (len < 0) ? len : 0; diff --git a/sub-process.h b/sub-process.h index 6a61638a8a..bfc3959a1b 100644 --- a/sub-process.h +++ b/sub-process.h @@ -73,7 +73,7 @@ static inline struct child_process *subprocess_get_child_process( /* * Perform the version and capability negotiation as described in the - * "Handshake" section of long-running-process-protocol.txt using the + * "Handshake" section of long-running-process-protocol.adoc using the * given requested versions and capabilities. The "versions" and "capabilities" * parameters are arrays terminated by a 0 or blank struct. * diff --git a/submodule-config.c b/submodule-config.c index 8630e27947..1f19fe2077 100644 --- a/submodule-config.c +++ b/submodule-config.c @@ -13,7 +13,7 @@ #include "submodule.h" #include "strbuf.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "parse-options.h" #include "thread-utils.h" #include "tree-walk.h" @@ -235,18 +235,6 @@ in_component: return 0; } -static int starts_with_dot_slash(const char *const path) -{ - return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_SLASH | - PATH_MATCH_XPLATFORM); -} - -static int starts_with_dot_dot_slash(const char *const path) -{ - return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH | - PATH_MATCH_XPLATFORM); -} - static int submodule_url_is_relative(const char *url) { return starts_with_dot_slash(url) || starts_with_dot_dot_slash(url); @@ -743,8 +731,8 @@ static const struct submodule *config_from(struct submodule_cache *cache, if (submodule) goto out; - config = repo_read_object_file(the_repository, &oid, &type, - &config_size); + config = odb_read_object(the_repository->objects, &oid, + &type, &config_size); if (!config || type != OBJ_BLOB) goto out; @@ -810,7 +798,8 @@ static void config_from_gitmodules(config_fn_t fn, struct repository *repo, void repo_get_oid(repo, GITMODULES_HEAD, &oid) >= 0) { config_source.blob = oidstr = xstrdup(oid_to_hex(&oid)); if (repo != the_repository) - add_submodule_odb_by_path(repo->objects->odb->path); + odb_add_submodule_source_by_path(the_repository->objects, + repo->objects->sources->path); } else { goto out; } @@ -994,7 +983,7 @@ int config_set_in_gitmodules_file_gently(const char *key, const char *value) { int ret; - ret = git_config_set_in_file_gently(GITMODULES_FILE, key, NULL, value); + ret = repo_config_set_in_file_gently(the_repository, GITMODULES_FILE, key, NULL, value); if (ret < 0) /* Maybe the user already did that, don't error out here */ warning(_("Could not update .gitmodules entry %s"), key); diff --git a/submodule.c b/submodule.c index ead3fb5dad..fff3c75570 100644 --- a/submodule.c +++ b/submodule.c @@ -27,11 +27,10 @@ #include "parse-options.h" #include "object-file.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "commit-reach.h" #include "read-cache-ll.h" #include "setup.h" -#include "trace2.h" static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF; static int initialized_fetch_ref_tips; @@ -176,30 +175,6 @@ void stage_updated_gitmodules(struct index_state *istate) die(_("staging updated .gitmodules failed")); } -static struct string_list added_submodule_odb_paths = STRING_LIST_INIT_DUP; - -void add_submodule_odb_by_path(const char *path) -{ - string_list_insert(&added_submodule_odb_paths, path); -} - -int register_all_submodule_odb_as_alternates(void) -{ - int i; - int ret = added_submodule_odb_paths.nr; - - for (i = 0; i < added_submodule_odb_paths.nr; i++) - add_to_alternates_memory(added_submodule_odb_paths.items[i].string); - if (ret) { - string_list_clear(&added_submodule_odb_paths, 0); - trace2_data_intmax("submodule", the_repository, - "register_all_submodule_odb_as_alternates/registered", ret); - if (git_env_bool("GIT_TEST_FATAL_REGISTER_SUBMODULE_ODB", 0)) - BUG("register_all_submodule_odb_as_alternates() called"); - } - return ret; -} - void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt, const char *path) { @@ -993,7 +968,7 @@ static int check_has_commit(const struct object_id *oid, void *data) return 0; } - type = oid_object_info(&subrepo, oid, NULL); + type = odb_read_object_info(subrepo.objects, oid, NULL); switch (type) { case OBJ_COMMIT: @@ -1777,8 +1752,7 @@ static int fetch_start_failure(struct strbuf *err UNUSED, static int commit_missing_in_sub(const struct object_id *oid, void *data) { struct repository *subrepo = data; - - enum object_type type = oid_object_info(subrepo, oid, NULL); + enum object_type type = odb_read_object_info(subrepo->objects, oid, NULL); return type != OBJ_COMMIT; } @@ -2084,7 +2058,7 @@ void submodule_unset_core_worktree(const struct submodule *sub) submodule_name_to_gitdir(&config_path, the_repository, sub->name); strbuf_addstr(&config_path, "/config"); - if (git_config_set_in_file_gently(config_path.buf, "core.worktree", NULL, NULL)) + if (repo_config_set_in_file_gently(the_repository, config_path.buf, "core.worktree", NULL, NULL)) warning(_("Could not unset core.worktree setting in submodule '%s'"), sub->path); diff --git a/submodule.h b/submodule.h index db980c1d08..b10e16e6c0 100644 --- a/submodule.h +++ b/submodule.h @@ -105,15 +105,6 @@ int submodule_uses_gitfile(const char *path); int bad_to_remove_submodule(const char *path, unsigned flags); /* - * Call add_submodule_odb_by_path() to add the submodule at the given - * path to a list. When register_all_submodule_odb_as_alternates() is - * called, the object stores of all submodules in that list will be - * added as alternates in the_repository. - */ -void add_submodule_odb_by_path(const char *path); -int register_all_submodule_odb_as_alternates(void); - -/* * Checks if there are submodule changes in a..b. If a is the null OID, * checks b and all its ancestors instead. */ diff --git a/subprojects/expat.wrap b/subprojects/expat.wrap index 2e0427dcfd..0e9292f97b 100644 --- a/subprojects/expat.wrap +++ b/subprojects/expat.wrap @@ -1,13 +1,13 @@ [wrap-file] -directory = expat-2.6.3 -source_url = https://github.com/libexpat/libexpat/releases/download/R_2_6_3/expat-2.6.3.tar.xz -source_filename = expat-2.6.3.tar.bz2 -source_hash = 274db254a6979bde5aad404763a704956940e465843f2a9bd9ed7af22e2c0efc -patch_filename = expat_2.6.3-1_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/expat_2.6.3-1/get_patch -patch_hash = cf017fbe105e31428b2768360bd9be39094df4e948a1e8d1c54b6f7c76460cb1 -source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/expat_2.6.3-1/expat-2.6.3.tar.bz2 -wrapdb_version = 2.6.3-1 +directory = expat-2.7.1 +source_url = https://github.com/libexpat/libexpat/releases/download/R_2_7_1/expat-2.7.1.tar.xz +source_filename = expat-2.7.1.tar.bz2 +source_hash = 354552544b8f99012e5062f7d570ec77f14b412a3ff5c7d8d0dae62c0d217c30 +patch_filename = expat_2.7.1-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/expat_2.7.1-1/get_patch +patch_hash = fe28cbbc427a7c9787d08b969ad54d19f59d8dd18294b4a18651cecfc789d4ef +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/expat_2.7.1-1/expat-2.7.1.tar.bz2 +wrapdb_version = 2.7.1-1 [provide] expat = expat_dep diff --git a/subprojects/pcre2.wrap b/subprojects/pcre2.wrap index 7e18447254..f45c968e2f 100644 --- a/subprojects/pcre2.wrap +++ b/subprojects/pcre2.wrap @@ -1,13 +1,13 @@ [wrap-file] -directory = pcre2-10.44 -source_url = https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.44/pcre2-10.44.tar.bz2 -source_filename = pcre2-10.44.tar.bz2 -source_hash = d34f02e113cf7193a1ebf2770d3ac527088d485d4e047ed10e5d217c6ef5de96 -patch_filename = pcre2_10.44-2_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/pcre2_10.44-2/get_patch -patch_hash = 4336d422ee9043847e5e10dbbbd01940d4c9e5027f31ccdc33a7898a1ca94009 -source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/pcre2_10.44-2/pcre2-10.44.tar.bz2 -wrapdb_version = 10.44-2 +directory = pcre2-10.45 +source_url = https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.45/pcre2-10.45.tar.bz2 +source_filename = pcre2-10.45.tar.bz2 +source_hash = 21547f3516120c75597e5b30a992e27a592a31950b5140e7b8bfde3f192033c4 +patch_filename = pcre2_10.45-2_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/pcre2_10.45-2/get_patch +patch_hash = 7c6f34b703708652a404f9dc2769c67658c437b6043573295fa3428a9b7a6807 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/pcre2_10.45-2/pcre2-10.45.tar.bz2 +wrapdb_version = 10.45-2 [provide] libpcre2-8 = libpcre2_8 diff --git a/t/Makefile b/t/Makefile index 791e0a0978..ab8a5b54aa 100644 --- a/t/Makefile +++ b/t/Makefile @@ -125,7 +125,6 @@ check-meson: @mkdir -p mesontmp && \ printf "%s\n" \ "integration_tests t[0-9][0-9][0-9][0-9]-*.sh" \ - "unit_test_programs unit-tests/t-*.c" \ "clar_test_suites unit-tests/u-*.c" | \ while read -r variable pattern; do \ awk "/^$$variable = \[\$$/ {flag=1 ; next } /^]$$/ { flag=0 } flag { gsub(/^ \047/, \"\"); gsub(/\047,\$$/, \"\"); print }" meson.build >mesontmp/meson.txt && \ @@ -190,15 +189,9 @@ perf: .PHONY: libgit-sys-test libgit-rs-test libgit-sys-test: - $(QUIET)(\ - cd ../contrib/libgit-sys && \ - cargo test \ - ) -libgit-rs-test: - $(QUIET)(\ - cd ../contrib/libgit-rs && \ - cargo test \ - ) + $(QUIET)cargo test --manifest-path ../contrib/libgit-sys/Cargo.toml +libgit-rs-test: libgit-sys-test + $(QUIET)cargo test --manifest-path ../contrib/libgit-rs/Cargo.toml ifdef INCLUDE_LIBGIT_RS -all:: libgit-sys-test libgit-rs-test +all:: libgit-rs-test endif @@ -415,6 +415,10 @@ GIT_TEST_PACK_SPARSE=<boolean> if disabled will default the pack-objects builtin to use the non-sparse object walk. This can still be overridden by the --sparse command-line argument. +GIT_TEST_PACK_PATH_WALK=<boolean> if enabled will default the pack-objects +builtin to use the path-walk API for the object walk. This can still be +overridden by the --no-path-walk command-line argument. + GIT_TEST_PRELOAD_INDEX=<boolean> exercises the preload-index code path by overriding the minimum number of cache entries required per thread. diff --git a/t/for-each-ref-tests.sh b/t/for-each-ref-tests.sh new file mode 100644 index 0000000000..e3ad19298a --- /dev/null +++ b/t/for-each-ref-tests.sh @@ -0,0 +1,2141 @@ +git_for_each_ref=${git_for_each_ref:-git for-each-ref} +GNUPGHOME_NOT_USED=$GNUPGHOME +. "$TEST_DIRECTORY"/lib-gpg.sh +. "$TEST_DIRECTORY"/lib-terminal.sh + +# Mon Jul 3 23:18:43 2006 +0000 +datestamp=1151968723 +setdate_and_increment () { + GIT_COMMITTER_DATE="$datestamp +0200" + datestamp=$(expr "$datestamp" + 1) + GIT_AUTHOR_DATE="$datestamp +0200" + datestamp=$(expr "$datestamp" + 1) + export GIT_COMMITTER_DATE GIT_AUTHOR_DATE +} + +test_object_file_size () { + oid=$(git rev-parse "$1") + path=".git/objects/$(test_oid_to_path $oid)" + test_file_size "$path" +} + +test_expect_success setup ' + # setup .mailmap + cat >.mailmap <<-EOF && + A Thor <athor@example.com> A U Thor <author@example.com> + C Mitter <cmitter@example.com> C O Mitter <committer@example.com> + EOF + + setdate_and_increment && + echo "Using $datestamp" > one && + git add one && + git commit -m "Initial" && + git branch -M main && + setdate_and_increment && + git tag -a -m "Tagging at $datestamp" testtag && + git update-ref refs/remotes/origin/main main && + git remote add origin nowhere && + git config branch.main.remote origin && + git config branch.main.merge refs/heads/main && + git remote add myfork elsewhere && + git config remote.pushdefault myfork && + git config push.default current +' + +test_atom () { + case "$1" in + head) ref=refs/heads/main ;; + tag) ref=refs/tags/testtag ;; + sym) ref=refs/heads/sym ;; + *) ref=$1 ;; + esac + format=$2 + test_do=test_expect_${4:-success} + + printf '%s\n' "$3" >expected + $test_do $PREREQ "basic atom: $ref $format" ' + ${git_for_each_ref} --format="%($format)" "$ref" >actual && + sanitize_pgp <actual >actual.clean && + test_cmp expected actual.clean + ' + + # Automatically test "contents:size" atom after testing "contents" + if test "$format" = "contents" + then + # for commit leg, $3 is changed there + expect=$(printf '%s' "$3" | wc -c) + $test_do $PREREQ "basic atom: $ref contents:size" ' + type=$(git cat-file -t "$ref") && + case $type in + tag) + # We cannot use $3 as it expects sanitize_pgp to run + git cat-file tag $ref >out && + expect=$(tail -n +6 out | wc -c) && + rm -f out ;; + tree | blob) + expect="" ;; + commit) + : "use the calculated expect" ;; + *) + BUG "unknown object type" ;; + esac && + # Leave $expect unquoted to lose possible leading whitespaces + echo $expect >expected && + ${git_for_each_ref} --format="%(contents:size)" "$ref" >actual && + test_cmp expected actual + ' + fi +} + +hexlen=$(test_oid hexsz) + +test_atom head refname refs/heads/main +test_atom head refname: refs/heads/main +test_atom head refname:short main +test_atom head refname:lstrip=1 heads/main +test_atom head refname:lstrip=2 main +test_atom head refname:lstrip=-1 main +test_atom head refname:lstrip=-2 heads/main +test_atom head refname:rstrip=1 refs/heads +test_atom head refname:rstrip=2 refs +test_atom head refname:rstrip=-1 refs +test_atom head refname:rstrip=-2 refs/heads +test_atom head refname:strip=1 heads/main +test_atom head refname:strip=2 main +test_atom head refname:strip=-1 main +test_atom head refname:strip=-2 heads/main +test_atom head upstream refs/remotes/origin/main +test_atom head upstream:short origin/main +test_atom head upstream:lstrip=2 origin/main +test_atom head upstream:lstrip=-2 origin/main +test_atom head upstream:rstrip=2 refs/remotes +test_atom head upstream:rstrip=-2 refs/remotes +test_atom head upstream:strip=2 origin/main +test_atom head upstream:strip=-2 origin/main +test_atom head push refs/remotes/myfork/main +test_atom head push:short myfork/main +test_atom head push:lstrip=1 remotes/myfork/main +test_atom head push:lstrip=-1 main +test_atom head push:rstrip=1 refs/remotes/myfork +test_atom head push:rstrip=-1 refs +test_atom head push:strip=1 remotes/myfork/main +test_atom head push:strip=-1 main +test_atom head objecttype commit +test_atom head objectsize $((131 + hexlen)) +test_atom head objectsize:disk $(test_object_file_size refs/heads/main) +test_atom head deltabase $ZERO_OID +test_atom head objectname $(git rev-parse refs/heads/main) +test_atom head objectname:short $(git rev-parse --short refs/heads/main) +test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main) +test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main) +test_atom head tree $(git rev-parse refs/heads/main^{tree}) +test_atom head tree:short $(git rev-parse --short refs/heads/main^{tree}) +test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/main^{tree}) +test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/main^{tree}) +test_atom head parent '' +test_atom head parent:short '' +test_atom head parent:short=1 '' +test_atom head parent:short=10 '' +test_atom head numparent 0 +test_atom head object '' +test_atom head type '' +test_atom head raw "$(git cat-file commit refs/heads/main) +" +test_atom head '*objectname' '' +test_atom head '*objecttype' '' +test_atom head author 'A U Thor <author@example.com> 1151968724 +0200' +test_atom head authorname 'A U Thor' +test_atom head authorname:mailmap 'A Thor' +test_atom head authoremail '<author@example.com>' +test_atom head authoremail:trim 'author@example.com' +test_atom head authoremail:localpart 'author' +test_atom head authoremail:trim,localpart 'author' +test_atom head authoremail:mailmap '<athor@example.com>' +test_atom head authoremail:mailmap,trim 'athor@example.com' +test_atom head authoremail:trim,mailmap 'athor@example.com' +test_atom head authoremail:mailmap,localpart 'athor' +test_atom head authoremail:localpart,mailmap 'athor' +test_atom head authoremail:mailmap,trim,localpart,mailmap,trim 'athor' +test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200' +test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200' +test_atom head committername 'C O Mitter' +test_atom head committername:mailmap 'C Mitter' +test_atom head committeremail '<committer@example.com>' +test_atom head committeremail:trim 'committer@example.com' +test_atom head committeremail:localpart 'committer' +test_atom head committeremail:localpart,trim 'committer' +test_atom head committeremail:mailmap '<cmitter@example.com>' +test_atom head committeremail:mailmap,trim 'cmitter@example.com' +test_atom head committeremail:trim,mailmap 'cmitter@example.com' +test_atom head committeremail:mailmap,localpart 'cmitter' +test_atom head committeremail:localpart,mailmap 'cmitter' +test_atom head committeremail:trim,mailmap,trim,trim,localpart 'cmitter' +test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200' +test_atom head tag '' +test_atom head tagger '' +test_atom head taggername '' +test_atom head taggeremail '' +test_atom head taggeremail:trim '' +test_atom head taggeremail:localpart '' +test_atom head taggerdate '' +test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200' +test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200' +test_atom head subject 'Initial' +test_atom head subject:sanitize 'Initial' +test_atom head contents:subject 'Initial' +test_atom head body '' +test_atom head contents:body '' +test_atom head contents:signature '' +test_atom head contents 'Initial +' +test_atom head HEAD '*' + +test_atom tag refname refs/tags/testtag +test_atom tag refname:short testtag +test_atom tag upstream '' +test_atom tag push '' +test_atom tag objecttype tag +test_atom tag objectsize $((114 + hexlen)) +test_atom tag objectsize:disk $(test_object_file_size refs/tags/testtag) +test_atom tag '*objectsize:disk' $(test_object_file_size refs/heads/main) +test_atom tag deltabase $ZERO_OID +test_atom tag '*deltabase' $ZERO_OID +test_atom tag objectname $(git rev-parse refs/tags/testtag) +test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag) +test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main) +test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main) +test_atom tag tree '' +test_atom tag tree:short '' +test_atom tag tree:short=1 '' +test_atom tag tree:short=10 '' +test_atom tag parent '' +test_atom tag parent:short '' +test_atom tag parent:short=1 '' +test_atom tag parent:short=10 '' +test_atom tag numparent '' +test_atom tag object $(git rev-parse refs/tags/testtag^0) +test_atom tag type 'commit' +test_atom tag '*objectname' $(git rev-parse refs/tags/testtag^{}) +test_atom tag '*objecttype' 'commit' +test_atom tag author '' +test_atom tag authorname '' +test_atom tag authorname:mailmap '' +test_atom tag authoremail '' +test_atom tag authoremail:trim '' +test_atom tag authoremail:localpart '' +test_atom tag authoremail:trim,localpart '' +test_atom tag authoremail:mailmap '' +test_atom tag authoremail:mailmap,trim '' +test_atom tag authoremail:trim,mailmap '' +test_atom tag authoremail:mailmap,localpart '' +test_atom tag authoremail:localpart,mailmap '' +test_atom tag authoremail:mailmap,trim,localpart,mailmap,trim '' +test_atom tag authordate '' +test_atom tag committer '' +test_atom tag committername '' +test_atom tag committername:mailmap '' +test_atom tag committeremail '' +test_atom tag committeremail:trim '' +test_atom tag committeremail:localpart '' +test_atom tag committeremail:localpart,trim '' +test_atom tag committeremail:mailmap '' +test_atom tag committeremail:mailmap,trim '' +test_atom tag committeremail:trim,mailmap '' +test_atom tag committeremail:mailmap,localpart '' +test_atom tag committeremail:localpart,mailmap '' +test_atom tag committeremail:trim,mailmap,trim,trim,localpart '' +test_atom tag committerdate '' +test_atom tag tag 'testtag' +test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200' +test_atom tag taggername 'C O Mitter' +test_atom tag taggername:mailmap 'C Mitter' +test_atom tag taggeremail '<committer@example.com>' +test_atom tag taggeremail:trim 'committer@example.com' +test_atom tag taggeremail:localpart 'committer' +test_atom tag taggeremail:trim,localpart 'committer' +test_atom tag taggeremail:mailmap '<cmitter@example.com>' +test_atom tag taggeremail:mailmap,trim 'cmitter@example.com' +test_atom tag taggeremail:trim,mailmap 'cmitter@example.com' +test_atom tag taggeremail:mailmap,localpart 'cmitter' +test_atom tag taggeremail:localpart,mailmap 'cmitter' +test_atom tag taggeremail:trim,mailmap,trim,localpart,localpart 'cmitter' +test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200' +test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200' +test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200' +test_atom tag subject 'Tagging at 1151968727' +test_atom tag subject:sanitize 'Tagging-at-1151968727' +test_atom tag contents:subject 'Tagging at 1151968727' +test_atom tag body '' +test_atom tag contents:body '' +test_atom tag contents:signature '' +test_atom tag contents 'Tagging at 1151968727 +' +test_atom tag HEAD ' ' + +test_expect_success 'basic atom: refs/tags/testtag *raw' ' + git cat-file commit refs/tags/testtag^{} >expected && + ${git_for_each_ref} --format="%(*raw)" refs/tags/testtag >actual && + sanitize_pgp <expected >expected.clean && + echo >>expected.clean && + sanitize_pgp <actual >actual.clean && + test_cmp expected.clean actual.clean +' + +test_expect_success 'Check invalid atoms names are errors' ' + test_must_fail ${git_for_each_ref} --format="%(INVALID)" refs/heads +' + +test_expect_success 'Check format specifiers are ignored in naming date atoms' ' + ${git_for_each_ref} --format="%(authordate)" refs/heads && + ${git_for_each_ref} --format="%(authordate:default) %(authordate)" refs/heads && + ${git_for_each_ref} --format="%(authordate) %(authordate:default)" refs/heads && + ${git_for_each_ref} --format="%(authordate:default) %(authordate:default)" refs/heads +' + +test_expect_success 'Check valid format specifiers for date fields' ' + ${git_for_each_ref} --format="%(authordate:default)" refs/heads && + ${git_for_each_ref} --format="%(authordate:relative)" refs/heads && + ${git_for_each_ref} --format="%(authordate:short)" refs/heads && + ${git_for_each_ref} --format="%(authordate:local)" refs/heads && + ${git_for_each_ref} --format="%(authordate:iso8601)" refs/heads && + ${git_for_each_ref} --format="%(authordate:rfc2822)" refs/heads +' + +test_expect_success 'Check invalid format specifiers are errors' ' + test_must_fail ${git_for_each_ref} --format="%(authordate:INVALID)" refs/heads +' + +test_expect_success 'arguments to %(objectname:short=) must be positive integers' ' + test_must_fail ${git_for_each_ref} --format="%(objectname:short=0)" && + test_must_fail ${git_for_each_ref} --format="%(objectname:short=-1)" && + test_must_fail ${git_for_each_ref} --format="%(objectname:short=foo)" +' + +test_bad_atom () { + case "$1" in + head) ref=refs/heads/main ;; + tag) ref=refs/tags/testtag ;; + sym) ref=refs/heads/sym ;; + *) ref=$1 ;; + esac + format=$2 + test_do=test_expect_${4:-success} + + printf '%s\n' "$3" >expect + $test_do $PREREQ "err basic atom: $ref $format" ' + test_must_fail ${git_for_each_ref} \ + --format="%($format)" "$ref" 2>error && + test_cmp expect error + ' +} + +test_bad_atom head 'authoremail:foo' \ + 'fatal: unrecognized %(authoremail) argument: foo' + +test_bad_atom head 'authoremail:mailmap,trim,bar' \ + 'fatal: unrecognized %(authoremail) argument: bar' + +test_bad_atom head 'authoremail:trim,' \ + 'fatal: unrecognized %(authoremail) argument: ' + +test_bad_atom head 'authoremail:mailmaptrim' \ + 'fatal: unrecognized %(authoremail) argument: trim' + +test_bad_atom head 'committeremail: ' \ + 'fatal: unrecognized %(committeremail) argument: ' + +test_bad_atom head 'committeremail: trim,foo' \ + 'fatal: unrecognized %(committeremail) argument: trim,foo' + +test_bad_atom head 'committeremail:mailmap,localpart ' \ + 'fatal: unrecognized %(committeremail) argument: ' + +test_bad_atom head 'committeremail:trim_localpart' \ + 'fatal: unrecognized %(committeremail) argument: _localpart' + +test_bad_atom head 'committeremail:localpart,,,trim' \ + 'fatal: unrecognized %(committeremail) argument: ,,trim' + +test_bad_atom tag 'taggeremail:mailmap,trim, foo ' \ + 'fatal: unrecognized %(taggeremail) argument: foo ' + +test_bad_atom tag 'taggeremail:trim,localpart,' \ + 'fatal: unrecognized %(taggeremail) argument: ' + +test_bad_atom tag 'taggeremail:mailmap;localpart trim' \ + 'fatal: unrecognized %(taggeremail) argument: ;localpart trim' + +test_bad_atom tag 'taggeremail:localpart trim' \ + 'fatal: unrecognized %(taggeremail) argument: trim' + +test_bad_atom tag 'taggeremail:mailmap,mailmap,trim,qux,localpart,trim' \ + 'fatal: unrecognized %(taggeremail) argument: qux,localpart,trim' + +test_date () { + f=$1 && + committer_date=$2 && + author_date=$3 && + tagger_date=$4 && + cat >expected <<-EOF && + 'refs/heads/main' '$committer_date' '$author_date' + 'refs/tags/testtag' '$tagger_date' + EOF + ( + ${git_for_each_ref} --shell \ + --format="%(refname) %(committerdate${f:+:$f}) %(authordate${f:+:$f})" \ + refs/heads && + ${git_for_each_ref} --shell \ + --format="%(refname) %(taggerdate${f:+:$f})" \ + refs/tags + ) >actual && + test_cmp expected actual +} + +test_expect_success 'Check unformatted date fields output' ' + test_date "" \ + "Tue Jul 4 01:18:43 2006 +0200" \ + "Tue Jul 4 01:18:44 2006 +0200" \ + "Tue Jul 4 01:18:45 2006 +0200" +' + +test_expect_success 'Check format "default" formatted date fields output' ' + test_date default \ + "Tue Jul 4 01:18:43 2006 +0200" \ + "Tue Jul 4 01:18:44 2006 +0200" \ + "Tue Jul 4 01:18:45 2006 +0200" +' + +test_expect_success 'Check format "default-local" date fields output' ' + test_date default-local "Mon Jul 3 23:18:43 2006" "Mon Jul 3 23:18:44 2006" "Mon Jul 3 23:18:45 2006" +' + +# Don't know how to do relative check because I can't know when this script +# is going to be run and can't fake the current time to git, and hence can't +# provide expected output. Instead, I'll just make sure that "relative" +# doesn't exit in error +test_expect_success 'Check format "relative" date fields output' ' + f=relative && + (${git_for_each_ref} --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && + ${git_for_each_ref} --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual +' + +# We just check that this is the same as "relative" for now. +test_expect_success 'Check format "relative-local" date fields output' ' + test_date relative-local \ + "$(${git_for_each_ref} --format="%(committerdate:relative)" refs/heads)" \ + "$(${git_for_each_ref} --format="%(authordate:relative)" refs/heads)" \ + "$(${git_for_each_ref} --format="%(taggerdate:relative)" refs/tags)" +' + +test_expect_success 'Check format "short" date fields output' ' + test_date short 2006-07-04 2006-07-04 2006-07-04 +' + +test_expect_success 'Check format "short-local" date fields output' ' + test_date short-local 2006-07-03 2006-07-03 2006-07-03 +' + +test_expect_success 'Check format "local" date fields output' ' + test_date local \ + "Mon Jul 3 23:18:43 2006" \ + "Mon Jul 3 23:18:44 2006" \ + "Mon Jul 3 23:18:45 2006" +' + +test_expect_success 'Check format "iso8601" date fields output' ' + test_date iso8601 \ + "2006-07-04 01:18:43 +0200" \ + "2006-07-04 01:18:44 +0200" \ + "2006-07-04 01:18:45 +0200" +' + +test_expect_success 'Check format "iso8601-local" date fields output' ' + test_date iso8601-local "2006-07-03 23:18:43 +0000" "2006-07-03 23:18:44 +0000" "2006-07-03 23:18:45 +0000" +' + +test_expect_success 'Check format "rfc2822" date fields output' ' + test_date rfc2822 \ + "Tue, 4 Jul 2006 01:18:43 +0200" \ + "Tue, 4 Jul 2006 01:18:44 +0200" \ + "Tue, 4 Jul 2006 01:18:45 +0200" +' + +test_expect_success 'Check format "rfc2822-local" date fields output' ' + test_date rfc2822-local "Mon, 3 Jul 2006 23:18:43 +0000" "Mon, 3 Jul 2006 23:18:44 +0000" "Mon, 3 Jul 2006 23:18:45 +0000" +' + +test_expect_success 'Check format "raw" date fields output' ' + test_date raw "1151968723 +0200" "1151968724 +0200" "1151968725 +0200" +' + +test_expect_success 'Check format "raw-local" date fields output' ' + test_date raw-local "1151968723 +0000" "1151968724 +0000" "1151968725 +0000" +' + +test_expect_success 'Check format of strftime date fields' ' + echo "my date is 2006-07-04" >expected && + ${git_for_each_ref} \ + --format="%(authordate:format:my date is %Y-%m-%d)" \ + refs/heads >actual && + test_cmp expected actual +' + +test_expect_success 'Check format of strftime-local date fields' ' + echo "my date is 2006-07-03" >expected && + ${git_for_each_ref} \ + --format="%(authordate:format-local:my date is %Y-%m-%d)" \ + refs/heads >actual && + test_cmp expected actual +' + +test_expect_success 'exercise strftime with odd fields' ' + echo >expected && + ${git_for_each_ref} --format="%(authordate:format:)" refs/heads >actual && + test_cmp expected actual && + long="long format -- $ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID" && + echo $long >expected && + ${git_for_each_ref} --format="%(authordate:format:$long)" refs/heads >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +refs/heads/main +refs/remotes/origin/main +refs/tags/testtag +EOF + +test_expect_success 'Verify ascending sort' ' + ${git_for_each_ref} --format="%(refname)" --sort=refname >actual && + test_cmp expected actual +' + + +cat >expected <<\EOF +refs/tags/testtag +refs/remotes/origin/main +refs/heads/main +EOF + +test_expect_success 'Verify descending sort' ' + ${git_for_each_ref} --format="%(refname)" --sort=-refname >actual && + test_cmp expected actual +' + +test_expect_success 'Give help even with invalid sort atoms' ' + test_expect_code 129 ${git_for_each_ref} --sort=bogus -h >actual 2>&1 && + grep "^usage: ${git_for_each_ref}" actual +' + +cat >expected <<\EOF +refs/tags/testtag +refs/tags/testtag-2 +EOF + +test_expect_success 'exercise patterns with prefixes' ' + git tag testtag-2 && + test_when_finished "git tag -d testtag-2" && + ${git_for_each_ref} --format="%(refname)" \ + refs/tags/testtag refs/tags/testtag-2 >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +refs/tags/testtag +refs/tags/testtag-2 +EOF + +test_expect_success 'exercise glob patterns with prefixes' ' + git tag testtag-2 && + test_when_finished "git tag -d testtag-2" && + ${git_for_each_ref} --format="%(refname)" \ + refs/tags/testtag "refs/tags/testtag-*" >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +refs/tags/bar +refs/tags/baz +refs/tags/testtag +EOF + +test_expect_success 'exercise patterns with prefix exclusions' ' + for tag in foo/one foo/two foo/three bar baz + do + git tag "$tag" || return 1 + done && + test_when_finished "git tag -d foo/one foo/two foo/three bar baz" && + ${git_for_each_ref} --format="%(refname)" \ + refs/tags/ --exclude=refs/tags/foo >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +refs/tags/bar +refs/tags/baz +refs/tags/foo/one +refs/tags/testtag +EOF + +test_expect_success 'exercise patterns with pattern exclusions' ' + for tag in foo/one foo/two foo/three bar baz + do + git tag "$tag" || return 1 + done && + test_when_finished "git tag -d foo/one foo/two foo/three bar baz" && + ${git_for_each_ref} --format="%(refname)" \ + refs/tags/ --exclude="refs/tags/foo/t*" >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +'refs/heads/main' +'refs/remotes/origin/main' +'refs/tags/testtag' +EOF + +test_expect_success 'Quoting style: shell' ' + ${git_for_each_ref} --shell --format="%(refname)" >actual && + test_cmp expected actual +' + +test_expect_success 'Quoting style: perl' ' + ${git_for_each_ref} --perl --format="%(refname)" >actual && + test_cmp expected actual +' + +test_expect_success 'Quoting style: python' ' + ${git_for_each_ref} --python --format="%(refname)" >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +"refs/heads/main" +"refs/remotes/origin/main" +"refs/tags/testtag" +EOF + +test_expect_success 'Quoting style: tcl' ' + ${git_for_each_ref} --tcl --format="%(refname)" >actual && + test_cmp expected actual +' + +for i in "--perl --shell" "-s --python" "--python --tcl" "--tcl --perl"; do + test_expect_success "more than one quoting style: $i" " + test_must_fail ${git_for_each_ref} $i 2>err && + grep '^error: more than one quoting style' err + " +done + +test_expect_success 'setup for upstream:track[short]' ' + test_commit two +' + +test_atom head upstream:track '[ahead 1]' +test_atom head upstream:trackshort '>' +test_atom head upstream:track,nobracket 'ahead 1' +test_atom head upstream:nobracket,track 'ahead 1' + +test_expect_success 'setup for push:track[short]' ' + test_commit third && + git update-ref refs/remotes/myfork/main main && + git reset main~1 +' + +test_atom head push:track '[behind 1]' +test_atom head push:trackshort '<' + +test_expect_success 'Check that :track[short] cannot be used with other atoms' ' + test_must_fail ${git_for_each_ref} --format="%(refname:track)" 2>/dev/null && + test_must_fail ${git_for_each_ref} --format="%(refname:trackshort)" 2>/dev/null +' + +test_expect_success 'Check that :track[short] works when upstream is invalid' ' + cat >expected <<-\EOF && + [gone] + + EOF + test_when_finished "git config branch.main.merge refs/heads/main" && + git config branch.main.merge refs/heads/does-not-exist && + ${git_for_each_ref} \ + --format="%(upstream:track)$LF%(upstream:trackshort)" \ + refs/heads >actual && + test_cmp expected actual +' + +test_expect_success 'Check for invalid refname format' ' + test_must_fail ${git_for_each_ref} --format="%(refname:INVALID)" +' + +test_expect_success 'set up color tests' ' + cat >expected.color <<-EOF && + $(git rev-parse --short refs/heads/main) <GREEN>main<RESET> + $(git rev-parse --short refs/remotes/myfork/main) <GREEN>myfork/main<RESET> + $(git rev-parse --short refs/remotes/origin/main) <GREEN>origin/main<RESET> + $(git rev-parse --short refs/tags/testtag) <GREEN>testtag<RESET> + $(git rev-parse --short refs/tags/third) <GREEN>third<RESET> + $(git rev-parse --short refs/tags/two) <GREEN>two<RESET> + EOF + sed "s/<[^>]*>//g" <expected.color >expected.bare && + color_format="%(objectname:short) %(color:green)%(refname:short)" +' + +test_expect_success TTY '%(color) shows color with a tty' ' + test_terminal ${git_for_each_ref} --format="$color_format" >actual.raw && + test_decode_color <actual.raw >actual && + test_cmp expected.color actual +' + +test_expect_success '%(color) does not show color without tty' ' + TERM=vt100 ${git_for_each_ref} --format="$color_format" >actual && + test_cmp expected.bare actual +' + +test_expect_success '--color can override tty check' ' + ${git_for_each_ref} --color --format="$color_format" >actual.raw && + test_decode_color <actual.raw >actual && + test_cmp expected.color actual +' + +test_expect_success 'color.ui=always does not override tty check' ' + git -c color.ui=always ${git_for_each_ref#git} --format="$color_format" >actual && + test_cmp expected.bare actual +' + +test_expect_success 'setup for describe atom tests' ' + git init -b master describe-repo && + ( + cd describe-repo && + + test_commit --no-tag one && + git tag tagone && + + test_commit --no-tag two && + git tag -a -m "tag two" tagtwo + ) +' + +test_expect_success 'describe atom vs git describe' ' + ( + cd describe-repo && + + ${git_for_each_ref} --format="%(objectname)" \ + refs/tags/ >obj && + while read hash + do + if desc=$(git describe $hash) + then + : >expect-contains-good + else + : >expect-contains-bad + fi && + echo "$hash $desc" || return 1 + done <obj >expect && + test_path_exists expect-contains-good && + test_path_exists expect-contains-bad && + + ${git_for_each_ref} --format="%(objectname) %(describe)" \ + refs/tags/ >actual 2>err && + test_cmp expect actual && + test_must_be_empty err + ) +' + +test_expect_success 'describe:tags vs describe --tags' ' + ( + cd describe-repo && + git describe --tags >expect && + ${git_for_each_ref} --format="%(describe:tags)" \ + refs/heads/master >actual && + test_cmp expect actual + ) +' + +test_expect_success 'describe:abbrev=... vs describe --abbrev=...' ' + ( + cd describe-repo && + + # Case 1: We have commits between HEAD and the most + # recent tag reachable from it + test_commit --no-tag file && + git describe --abbrev=14 >expect && + ${git_for_each_ref} --format="%(describe:abbrev=14)" \ + refs/heads/master >actual && + test_cmp expect actual && + + # Make sure the hash used is at least 14 digits long + sed -e "s/^.*-g\([0-9a-f]*\)$/\1/" <actual >hexpart && + test 15 -le $(wc -c <hexpart) && + + # Case 2: We have a tag at HEAD, describe directly gives + # the name of the tag + git tag -a -m tagged tagname && + git describe --abbrev=14 >expect && + ${git_for_each_ref} --format="%(describe:abbrev=14)" \ + refs/heads/master >actual && + test_cmp expect actual && + test tagname = $(cat actual) + ) +' + +test_expect_success 'describe:match=... vs describe --match ...' ' + ( + cd describe-repo && + git tag -a -m "tag foo" tag-foo && + git describe --match "*-foo" >expect && + ${git_for_each_ref} --format="%(describe:match="*-foo")" \ + refs/heads/master >actual && + test_cmp expect actual + ) +' + +test_expect_success 'describe:exclude:... vs describe --exclude ...' ' + ( + cd describe-repo && + git tag -a -m "tag bar" tag-bar && + git describe --exclude "*-bar" >expect && + ${git_for_each_ref} --format="%(describe:exclude="*-bar")" \ + refs/heads/master >actual && + test_cmp expect actual + ) +' + +test_expect_success 'deref with describe atom' ' + ( + cd describe-repo && + cat >expect <<-\EOF && + + tagname + tagname + tagname + + tagtwo + EOF + ${git_for_each_ref} --format="%(*describe)" >actual && + test_cmp expect actual + ) +' + +test_expect_success 'err on bad describe atom arg' ' + ( + cd describe-repo && + + # The bad arg is the only arg passed to describe atom + cat >expect <<-\EOF && + fatal: unrecognized %(describe) argument: baz + EOF + test_must_fail ${git_for_each_ref} --format="%(describe:baz)" \ + refs/heads/master 2>actual && + test_cmp expect actual && + + # The bad arg is in the middle of the option string + # passed to the describe atom + cat >expect <<-\EOF && + fatal: unrecognized %(describe) argument: qux=1,abbrev=14 + EOF + test_must_fail ${git_for_each_ref} \ + --format="%(describe:tags,qux=1,abbrev=14)" \ + ref/heads/master 2>actual && + test_cmp expect actual + ) +' + +cat >expected <<\EOF +heads/main +tags/main +EOF + +test_expect_success 'Check ambiguous head and tag refs (strict)' ' + git config --bool core.warnambiguousrefs true && + git checkout -b newtag && + echo "Using $datestamp" > one && + git add one && + git commit -m "Branch" && + setdate_and_increment && + git tag -m "Tagging at $datestamp" main && + ${git_for_each_ref} --format "%(refname:short)" refs/heads/main refs/tags/main >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +heads/main +main +EOF + +test_expect_success 'Check ambiguous head and tag refs (loose)' ' + git config --bool core.warnambiguousrefs false && + ${git_for_each_ref} --format "%(refname:short)" refs/heads/main refs/tags/main >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +heads/ambiguous +ambiguous +EOF + +test_expect_success 'Check ambiguous head and tag refs II (loose)' ' + git checkout main && + git tag ambiguous testtag^0 && + git branch ambiguous testtag^0 && + ${git_for_each_ref} --format "%(refname:short)" refs/heads/ambiguous refs/tags/ambiguous >actual && + test_cmp expected actual +' + +test_expect_success 'create tag without tagger' ' + git tag -a -m "Broken tag" taggerless && + git tag -f taggerless $(git cat-file tag taggerless | + sed -e "/^tagger /d" | + git hash-object --literally --stdin -w -t tag) +' + +test_atom refs/tags/taggerless type 'commit' +test_atom refs/tags/taggerless tag 'taggerless' +test_atom refs/tags/taggerless tagger '' +test_atom refs/tags/taggerless taggername '' +test_atom refs/tags/taggerless taggeremail '' +test_atom refs/tags/taggerless taggeremail:trim '' +test_atom refs/tags/taggerless taggeremail:localpart '' +test_atom refs/tags/taggerless taggerdate '' +test_atom refs/tags/taggerless committer '' +test_atom refs/tags/taggerless committername '' +test_atom refs/tags/taggerless committeremail '' +test_atom refs/tags/taggerless committeremail:trim '' +test_atom refs/tags/taggerless committeremail:localpart '' +test_atom refs/tags/taggerless committerdate '' +test_atom refs/tags/taggerless subject 'Broken tag' + +test_expect_success 'an unusual tag with an incomplete line' ' + + git tag -m "bogo" bogo && + bogo=$(git cat-file tag bogo) && + bogo=$(printf "%s" "$bogo" | git mktag) && + git tag -f bogo "$bogo" && + ${git_for_each_ref} --format "%(body)" refs/tags/bogo + +' + +test_expect_success 'create tag with subject and body content' ' + cat >>msg <<-\EOF && + the subject line + + first body line + second body line + EOF + git tag -F msg subject-body +' +test_atom refs/tags/subject-body subject 'the subject line' +test_atom refs/tags/subject-body subject:sanitize 'the-subject-line' +test_atom refs/tags/subject-body body 'first body line +second body line +' +test_atom refs/tags/subject-body contents 'the subject line + +first body line +second body line +' + +test_expect_success 'create tag with multiline subject' ' + cat >msg <<-\EOF && + first subject line + second subject line + + first body line + second body line + EOF + git tag -F msg multiline +' +test_atom refs/tags/multiline subject 'first subject line second subject line' +test_atom refs/tags/multiline subject:sanitize 'first-subject-line-second-subject-line' +test_atom refs/tags/multiline contents:subject 'first subject line second subject line' +test_atom refs/tags/multiline body 'first body line +second body line +' +test_atom refs/tags/multiline contents:body 'first body line +second body line +' +test_atom refs/tags/multiline contents:signature '' +test_atom refs/tags/multiline contents 'first subject line +second subject line + +first body line +second body line +' + +test_expect_success GPG 'create signed tags' ' + git tag -s -m "" signed-empty && + git tag -s -m "subject line" signed-short && + cat >msg <<-\EOF && + subject line + + body contents + EOF + git tag -s -F msg signed-long +' + +sig='-----BEGIN PGP SIGNATURE----- +-----END PGP SIGNATURE----- +' + +PREREQ=GPG +test_atom refs/tags/signed-empty subject '' +test_atom refs/tags/signed-empty subject:sanitize '' +test_atom refs/tags/signed-empty contents:subject '' +test_atom refs/tags/signed-empty body "$sig" +test_atom refs/tags/signed-empty contents:body '' +test_atom refs/tags/signed-empty contents:signature "$sig" +test_atom refs/tags/signed-empty contents "$sig" + +test_expect_success GPG 'basic atom: refs/tags/signed-empty raw' ' + git cat-file tag refs/tags/signed-empty >expected && + ${git_for_each_ref} --format="%(raw)" refs/tags/signed-empty >actual && + sanitize_pgp <expected >expected.clean && + echo >>expected.clean && + sanitize_pgp <actual >actual.clean && + test_cmp expected.clean actual.clean +' + +test_atom refs/tags/signed-short subject 'subject line' +test_atom refs/tags/signed-short subject:sanitize 'subject-line' +test_atom refs/tags/signed-short contents:subject 'subject line' +test_atom refs/tags/signed-short body "$sig" +test_atom refs/tags/signed-short contents:body '' +test_atom refs/tags/signed-short contents:signature "$sig" +test_atom refs/tags/signed-short contents "subject line +$sig" + +test_expect_success GPG 'basic atom: refs/tags/signed-short raw' ' + git cat-file tag refs/tags/signed-short >expected && + ${git_for_each_ref} --format="%(raw)" refs/tags/signed-short >actual && + sanitize_pgp <expected >expected.clean && + echo >>expected.clean && + sanitize_pgp <actual >actual.clean && + test_cmp expected.clean actual.clean +' + +test_atom refs/tags/signed-long subject 'subject line' +test_atom refs/tags/signed-long subject:sanitize 'subject-line' +test_atom refs/tags/signed-long contents:subject 'subject line' +test_atom refs/tags/signed-long body "body contents +$sig" +test_atom refs/tags/signed-long contents:body 'body contents +' +test_atom refs/tags/signed-long contents:signature "$sig" +test_atom refs/tags/signed-long contents "subject line + +body contents +$sig" + +test_expect_success GPG 'basic atom: refs/tags/signed-long raw' ' + git cat-file tag refs/tags/signed-long >expected && + ${git_for_each_ref} --format="%(raw)" refs/tags/signed-long >actual && + sanitize_pgp <expected >expected.clean && + echo >>expected.clean && + sanitize_pgp <actual >actual.clean && + test_cmp expected.clean actual.clean +' + +test_expect_success 'set up refs pointing to tree and blob' ' + git update-ref refs/mytrees/first refs/heads/main^{tree} && + git update-ref refs/myblobs/first refs/heads/main:one +' + +test_atom refs/mytrees/first subject "" +test_atom refs/mytrees/first contents:subject "" +test_atom refs/mytrees/first body "" +test_atom refs/mytrees/first contents:body "" +test_atom refs/mytrees/first contents:signature "" +test_atom refs/mytrees/first contents "" + +test_expect_success 'basic atom: refs/mytrees/first raw' ' + git cat-file tree refs/mytrees/first >expected && + echo >>expected && + ${git_for_each_ref} --format="%(raw)" refs/mytrees/first >actual && + test_cmp expected actual && + git cat-file -s refs/mytrees/first >expected && + ${git_for_each_ref} --format="%(raw:size)" refs/mytrees/first >actual && + test_cmp expected actual +' + +test_atom refs/myblobs/first subject "" +test_atom refs/myblobs/first contents:subject "" +test_atom refs/myblobs/first body "" +test_atom refs/myblobs/first contents:body "" +test_atom refs/myblobs/first contents:signature "" +test_atom refs/myblobs/first contents "" + +test_expect_success 'basic atom: refs/myblobs/first raw' ' + git cat-file blob refs/myblobs/first >expected && + echo >>expected && + ${git_for_each_ref} --format="%(raw)" refs/myblobs/first >actual && + test_cmp expected actual && + git cat-file -s refs/myblobs/first >expected && + ${git_for_each_ref} --format="%(raw:size)" refs/myblobs/first >actual && + test_cmp expected actual +' + +test_expect_success 'set up refs pointing to binary blob' ' + printf "a\0b\0c" >blob1 && + printf "a\0c\0b" >blob2 && + printf "\0a\0b\0c" >blob3 && + printf "abc" >blob4 && + printf "\0 \0 \0 " >blob5 && + printf "\0 \0a\0 " >blob6 && + printf " " >blob7 && + >blob8 && + obj=$(git hash-object -w blob1) && + git update-ref refs/myblobs/blob1 "$obj" && + obj=$(git hash-object -w blob2) && + git update-ref refs/myblobs/blob2 "$obj" && + obj=$(git hash-object -w blob3) && + git update-ref refs/myblobs/blob3 "$obj" && + obj=$(git hash-object -w blob4) && + git update-ref refs/myblobs/blob4 "$obj" && + obj=$(git hash-object -w blob5) && + git update-ref refs/myblobs/blob5 "$obj" && + obj=$(git hash-object -w blob6) && + git update-ref refs/myblobs/blob6 "$obj" && + obj=$(git hash-object -w blob7) && + git update-ref refs/myblobs/blob7 "$obj" && + obj=$(git hash-object -w blob8) && + git update-ref refs/myblobs/blob8 "$obj" +' + +test_expect_success 'Verify sorts with raw' ' + cat >expected <<-EOF && + refs/myblobs/blob8 + refs/myblobs/blob5 + refs/myblobs/blob6 + refs/myblobs/blob3 + refs/myblobs/blob7 + refs/mytrees/first + refs/myblobs/first + refs/myblobs/blob1 + refs/myblobs/blob2 + refs/myblobs/blob4 + refs/heads/main + EOF + ${git_for_each_ref} --format="%(refname)" --sort=raw \ + refs/heads/main refs/myblobs/ refs/mytrees/first >actual && + test_cmp expected actual +' + +test_expect_success 'Verify sorts with raw:size' ' + cat >expected <<-EOF && + refs/myblobs/blob8 + refs/myblobs/blob7 + refs/myblobs/blob4 + refs/myblobs/blob1 + refs/myblobs/blob2 + refs/myblobs/blob3 + refs/myblobs/blob5 + refs/myblobs/blob6 + refs/myblobs/first + refs/mytrees/first + refs/heads/main + EOF + ${git_for_each_ref} --format="%(refname)" --sort=raw:size \ + refs/heads/main refs/myblobs/ refs/mytrees/first >actual && + test_cmp expected actual +' + +test_expect_success 'validate raw atom with %(if:equals)' ' + cat >expected <<-EOF && + not equals + not equals + not equals + not equals + not equals + not equals + refs/myblobs/blob4 + not equals + not equals + not equals + not equals + not equals + EOF + ${git_for_each_ref} --format="%(if:equals=abc)%(raw)%(then)%(refname)%(else)not equals%(end)" \ + refs/myblobs/ refs/heads/ >actual && + test_cmp expected actual +' + +test_expect_success 'validate raw atom with %(if:notequals)' ' + cat >expected <<-EOF && + refs/heads/ambiguous + refs/heads/main + refs/heads/newtag + refs/myblobs/blob1 + refs/myblobs/blob2 + refs/myblobs/blob3 + equals + refs/myblobs/blob5 + refs/myblobs/blob6 + refs/myblobs/blob7 + refs/myblobs/blob8 + refs/myblobs/first + EOF + ${git_for_each_ref} --format="%(if:notequals=abc)%(raw)%(then)%(refname)%(else)equals%(end)" \ + refs/myblobs/ refs/heads/ >actual && + test_cmp expected actual +' + +test_expect_success 'empty raw refs with %(if)' ' + cat >expected <<-EOF && + refs/myblobs/blob1 not empty + refs/myblobs/blob2 not empty + refs/myblobs/blob3 not empty + refs/myblobs/blob4 not empty + refs/myblobs/blob5 not empty + refs/myblobs/blob6 not empty + refs/myblobs/blob7 empty + refs/myblobs/blob8 empty + refs/myblobs/first not empty + EOF + ${git_for_each_ref} --format="%(refname) %(if)%(raw)%(then)not empty%(else)empty%(end)" \ + refs/myblobs/ >actual && + test_cmp expected actual +' + +test_expect_success '%(raw) with --python must fail' ' + test_must_fail ${git_for_each_ref} --format="%(raw)" --python +' + +test_expect_success '%(raw) with --tcl must fail' ' + test_must_fail ${git_for_each_ref} --format="%(raw)" --tcl +' + +test_expect_success PERL_TEST_HELPERS '%(raw) with --perl' ' + ${git_for_each_ref} --format="\$name= %(raw); +print \"\$name\"" refs/myblobs/blob1 --perl | perl >actual && + cmp blob1 actual && + ${git_for_each_ref} --format="\$name= %(raw); +print \"\$name\"" refs/myblobs/blob3 --perl | perl >actual && + cmp blob3 actual && + ${git_for_each_ref} --format="\$name= %(raw); +print \"\$name\"" refs/myblobs/blob8 --perl | perl >actual && + cmp blob8 actual && + ${git_for_each_ref} --format="\$name= %(raw); +print \"\$name\"" refs/myblobs/first --perl | perl >actual && + cmp one actual && + git cat-file tree refs/mytrees/first > expected && + ${git_for_each_ref} --format="\$name= %(raw); +print \"\$name\"" refs/mytrees/first --perl | perl >actual && + cmp expected actual +' + +test_expect_success '%(raw) with --shell must fail' ' + test_must_fail ${git_for_each_ref} --format="%(raw)" --shell +' + +test_expect_success '%(raw) with --shell and --sort=raw must fail' ' + test_must_fail ${git_for_each_ref} --format="%(raw)" --sort=raw --shell +' + +test_expect_success '%(raw:size) with --shell' ' + ${git_for_each_ref} --format="%(raw:size)" | sed "s/^/$SQ/;s/$/$SQ/" >expect && + ${git_for_each_ref} --format="%(raw:size)" --shell >actual && + test_cmp expect actual +' + +test_expect_success "${git_for_each_ref} --format compare with cat-file --batch" ' + git rev-parse refs/mytrees/first | git cat-file --batch >expected && + ${git_for_each_ref} --format="%(objectname) %(objecttype) %(objectsize) +%(raw)" refs/mytrees/first >actual && + test_cmp expected actual +' + +test_expect_success 'verify sorts with contents:size' ' + cat >expect <<-\EOF && + refs/heads/main + refs/heads/newtag + refs/heads/ambiguous + EOF + ${git_for_each_ref} --format="%(refname)" \ + --sort=contents:size refs/heads/ >actual && + test_cmp expect actual +' + +test_expect_success 'set up multiple-sort tags' ' + for when in 100000 200000 + do + for email in user1 user2 + do + for ref in ref1 ref2 + do + GIT_COMMITTER_DATE="@$when +0000" \ + GIT_COMMITTER_EMAIL="$email@example.com" \ + git tag -m "tag $ref-$when-$email" \ + multi-$ref-$when-$email || return 1 + done + done + done +' + +test_expect_success 'Verify sort with multiple keys' ' + cat >expected <<-\EOF && + 100000 <user1@example.com> refs/tags/multi-ref2-100000-user1 + 100000 <user1@example.com> refs/tags/multi-ref1-100000-user1 + 100000 <user2@example.com> refs/tags/multi-ref2-100000-user2 + 100000 <user2@example.com> refs/tags/multi-ref1-100000-user2 + 200000 <user1@example.com> refs/tags/multi-ref2-200000-user1 + 200000 <user1@example.com> refs/tags/multi-ref1-200000-user1 + 200000 <user2@example.com> refs/tags/multi-ref2-200000-user2 + 200000 <user2@example.com> refs/tags/multi-ref1-200000-user2 + EOF + ${git_for_each_ref} \ + --format="%(taggerdate:unix) %(taggeremail) %(refname)" \ + --sort=-refname \ + --sort=taggeremail \ + --sort=taggerdate \ + "refs/tags/multi-*" >actual && + test_cmp expected actual +' + +test_expect_success 'equivalent sorts fall back on refname' ' + cat >expected <<-\EOF && + 100000 <user1@example.com> refs/tags/multi-ref1-100000-user1 + 100000 <user2@example.com> refs/tags/multi-ref1-100000-user2 + 100000 <user1@example.com> refs/tags/multi-ref2-100000-user1 + 100000 <user2@example.com> refs/tags/multi-ref2-100000-user2 + 200000 <user1@example.com> refs/tags/multi-ref1-200000-user1 + 200000 <user2@example.com> refs/tags/multi-ref1-200000-user2 + 200000 <user1@example.com> refs/tags/multi-ref2-200000-user1 + 200000 <user2@example.com> refs/tags/multi-ref2-200000-user2 + EOF + ${git_for_each_ref} \ + --format="%(taggerdate:unix) %(taggeremail) %(refname)" \ + --sort=taggerdate \ + "refs/tags/multi-*" >actual && + test_cmp expected actual +' + +test_expect_success '--no-sort cancels the previous sort keys' ' + cat >expected <<-\EOF && + 100000 <user1@example.com> refs/tags/multi-ref1-100000-user1 + 100000 <user2@example.com> refs/tags/multi-ref1-100000-user2 + 100000 <user1@example.com> refs/tags/multi-ref2-100000-user1 + 100000 <user2@example.com> refs/tags/multi-ref2-100000-user2 + 200000 <user1@example.com> refs/tags/multi-ref1-200000-user1 + 200000 <user2@example.com> refs/tags/multi-ref1-200000-user2 + 200000 <user1@example.com> refs/tags/multi-ref2-200000-user1 + 200000 <user2@example.com> refs/tags/multi-ref2-200000-user2 + EOF + ${git_for_each_ref} \ + --format="%(taggerdate:unix) %(taggeremail) %(refname)" \ + --sort=-refname \ + --sort=taggeremail \ + --no-sort \ + --sort=taggerdate \ + "refs/tags/multi-*" >actual && + test_cmp expected actual +' + +test_expect_success '--no-sort without subsequent --sort prints expected refs' ' + cat >expected <<-\EOF && + refs/tags/multi-ref1-100000-user1 + refs/tags/multi-ref1-100000-user2 + refs/tags/multi-ref1-200000-user1 + refs/tags/multi-ref1-200000-user2 + refs/tags/multi-ref2-100000-user1 + refs/tags/multi-ref2-100000-user2 + refs/tags/multi-ref2-200000-user1 + refs/tags/multi-ref2-200000-user2 + EOF + + # Sort the results with `sort` for a consistent comparison against + # expected + ${git_for_each_ref} \ + --format="%(refname)" \ + --no-sort \ + "refs/tags/multi-*" | sort >actual && + test_cmp expected actual +' + +test_expect_success 'set up custom date sorting' ' + # Dates: + # - Wed Feb 07 2024 21:34:20 +0000 + # - Tue Dec 14 1999 00:05:22 +0000 + # - Fri Jun 04 2021 11:26:51 +0000 + # - Mon Jan 22 2007 16:44:01 GMT+0000 + i=1 && + for when in 1707341660 945129922 1622806011 1169484241 + do + GIT_COMMITTER_DATE="@$when +0000" \ + GIT_COMMITTER_EMAIL="user@example.com" \ + git tag -m "tag $when" custom-dates-$i && + i=$(($i+1)) || return 1 + done +' + +test_expect_success 'sort by date defaults to full timestamp' ' + cat >expected <<-\EOF && + 945129922 refs/tags/custom-dates-2 + 1169484241 refs/tags/custom-dates-4 + 1622806011 refs/tags/custom-dates-3 + 1707341660 refs/tags/custom-dates-1 + EOF + + ${git_for_each_ref} \ + --format="%(creatordate:unix) %(refname)" \ + --sort=creatordate \ + "refs/tags/custom-dates-*" >actual && + test_cmp expected actual +' + +test_expect_success 'sort by custom date format' ' + cat >expected <<-\EOF && + 00:05:22 refs/tags/custom-dates-2 + 11:26:51 refs/tags/custom-dates-3 + 16:44:01 refs/tags/custom-dates-4 + 21:34:20 refs/tags/custom-dates-1 + EOF + + ${git_for_each_ref} \ + --format="%(creatordate:format:%H:%M:%S) %(refname)" \ + --sort="creatordate:format:%H:%M:%S" \ + "refs/tags/custom-dates-*" >actual && + test_cmp expected actual +' + +test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' ' + test_when_finished "git checkout main" && + ${git_for_each_ref} --format="%(HEAD) %(refname:short)" refs/heads/ >actual && + sed -e "s/^\* / /" actual >expect && + git checkout --orphan orphaned-branch && + ${git_for_each_ref} --format="%(HEAD) %(refname:short)" refs/heads/ >actual && + test_cmp expect actual +' + +cat >trailers <<EOF +Reviewed-by: A U Thor <author@example.com> +Signed-off-by: A U Thor <author@example.com> +[ v2 updated patch description ] +Acked-by: A U Thor + <author@example.com> +EOF + +unfold () { + perl -0pe 's/\n\s+/ /g' +} + +test_expect_success 'set up trailers for next test' ' + echo "Some contents" > two && + git add two && + git commit -F - <<-EOF + trailers: this commit message has trailers + + Some message contents + + $(cat trailers) + EOF +' + +test_trailer_option () { + if test "$#" -eq 3 + then + prereq="$1" + shift + fi && + title=$1 option=$2 + cat >expect + test_expect_success $prereq "$title" ' + ${git_for_each_ref} --format="%($option)" refs/heads/main >actual && + test_cmp expect actual && + ${git_for_each_ref} --format="%(contents:$option)" refs/heads/main >actual && + test_cmp expect actual + ' +} + +test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) unfolds trailers' \ + 'trailers:unfold' <<-EOF + $(unfold <trailers) + + EOF + +test_trailer_option '%(trailers:only) shows only "key: value" trailers' \ + 'trailers:only' <<-EOF + $(grep -v patch.description <trailers) + + EOF + +test_trailer_option '%(trailers:only=no,only=true) shows only "key: value" trailers' \ + 'trailers:only=no,only=true' <<-EOF + $(grep -v patch.description <trailers) + + EOF + +test_trailer_option '%(trailers:only=yes) shows only "key: value" trailers' \ + 'trailers:only=yes' <<-EOF + $(grep -v patch.description <trailers) + + EOF + +test_trailer_option '%(trailers:only=no) shows all trailers' \ + 'trailers:only=no' <<-EOF + $(cat trailers) + + EOF + +test_trailer_option PERL_TEST_HELPERS '%(trailers:only) and %(trailers:unfold) work together' \ + 'trailers:only,unfold' <<-EOF + $(grep -v patch.description <trailers | unfold) + + EOF + +test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) and %(trailers:only) work together' \ + 'trailers:unfold,only' <<-EOF + $(grep -v patch.description <trailers | unfold) + + EOF + +test_trailer_option '%(trailers:key=foo) shows that trailer' \ + 'trailers:key=Signed-off-by' <<-EOF + Signed-off-by: A U Thor <author@example.com> + + EOF + +test_trailer_option '%(trailers:key=foo) is case insensitive' \ + 'trailers:key=SiGned-oFf-bY' <<-EOF + Signed-off-by: A U Thor <author@example.com> + + EOF + +test_trailer_option '%(trailers:key=foo:) trailing colon also works' \ + 'trailers:key=Signed-off-by:' <<-EOF + Signed-off-by: A U Thor <author@example.com> + + EOF + +test_trailer_option '%(trailers:key=foo) multiple keys' \ + 'trailers:key=Reviewed-by:,key=Signed-off-by' <<-EOF + Reviewed-by: A U Thor <author@example.com> + Signed-off-by: A U Thor <author@example.com> + + EOF + +test_trailer_option '%(trailers:key=nonexistent) becomes empty' \ + 'trailers:key=Shined-off-by:' <<-EOF + + EOF + +test_trailer_option '%(trailers:key=foo) handles multiple lines even if folded' \ + 'trailers:key=Acked-by' <<-EOF + $(grep -v patch.description <trailers | grep -v Signed-off-by | grep -v Reviewed-by) + + EOF + +test_trailer_option '%(trailers:key=foo,unfold) properly unfolds' \ + 'trailers:key=Signed-Off-by,unfold' <<-EOF + $(unfold <trailers | grep Signed-off-by) + + EOF + +test_trailer_option '%(trailers:key=foo,only=no) also includes nontrailer lines' \ + 'trailers:key=Signed-off-by,only=no' <<-EOF + Signed-off-by: A U Thor <author@example.com> + $(grep patch.description <trailers) + + EOF + +test_trailer_option '%(trailers:key=foo,valueonly) shows only value' \ + 'trailers:key=Signed-off-by,valueonly' <<-EOF + A U Thor <author@example.com> + + EOF + +test_trailer_option '%(trailers:separator) changes separator' \ + 'trailers:separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF + Reviewed-by: A U Thor <author@example.com>,Signed-off-by: A U Thor <author@example.com> + EOF + +test_trailer_option '%(trailers:key_value_separator) changes key-value separator' \ + 'trailers:key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF + Reviewed-by,A U Thor <author@example.com> + Signed-off-by,A U Thor <author@example.com> + + EOF + +test_trailer_option '%(trailers:separator,key_value_separator) changes both separators' \ + 'trailers:separator=%x2C,key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF + Reviewed-by,A U Thor <author@example.com>,Signed-off-by,A U Thor <author@example.com> + EOF + +test_expect_success 'multiple %(trailers) use their own options' ' + git tag -F - tag-with-trailers <<-\EOF && + body + + one: foo + one: bar + two: baz + two: qux + EOF + t1="%(trailers:key=one,key_value_separator=W,separator=X)" && + t2="%(trailers:key=two,key_value_separator=Y,separator=Z)" && + ${git_for_each_ref} --format="$t1%0a$t2" refs/tags/tag-with-trailers >actual && + cat >expect <<-\EOF && + oneWfooXoneWbar + twoYbazZtwoYqux + EOF + test_cmp expect actual +' + +test_failing_trailer_option () { + title=$1 option=$2 + cat >expect + test_expect_success "$title" ' + # error message cannot be checked under i18n + test_must_fail ${git_for_each_ref} --format="%($option)" refs/heads/main 2>actual && + test_cmp expect actual && + test_must_fail ${git_for_each_ref} --format="%(contents:$option)" refs/heads/main 2>actual && + test_cmp expect actual + ' +} + +test_failing_trailer_option '%(trailers) rejects unknown trailers arguments' \ + 'trailers:unsupported' <<-\EOF + fatal: unknown %(trailers) argument: unsupported + EOF + +test_failing_trailer_option '%(trailers:key) without value is error' \ + 'trailers:key' <<-\EOF + fatal: expected %(trailers:key=<value>) + EOF + +test_expect_success 'if arguments, %(contents:trailers) shows error if colon is missing' ' + cat >expect <<-EOF && + fatal: unrecognized %(contents) argument: trailersonly + EOF + test_must_fail ${git_for_each_ref} --format="%(contents:trailersonly)" 2>actual && + test_cmp expect actual +' + +test_expect_success 'basic atom: head contents:trailers' ' + ${git_for_each_ref} --format="%(contents:trailers)" refs/heads/main >actual && + sanitize_pgp <actual >actual.clean && + # ${git_for_each_ref} ends with a blank line + cat >expect <<-EOF && + $(cat trailers) + + EOF + test_cmp expect actual.clean +' + +test_expect_success 'basic atom: rest must fail' ' + test_must_fail ${git_for_each_ref} --format="%(rest)" refs/heads/main +' + +test_expect_success 'HEAD atom does not take arguments' ' + test_must_fail ${git_for_each_ref} --format="%(HEAD:foo)" 2>err && + echo "fatal: %(HEAD) does not take arguments" >expect && + test_cmp expect err +' + +test_expect_success 'subject atom rejects unknown arguments' ' + test_must_fail ${git_for_each_ref} --format="%(subject:foo)" 2>err && + echo "fatal: unrecognized %(subject) argument: foo" >expect && + test_cmp expect err +' + +test_expect_success 'refname atom rejects unknown arguments' ' + test_must_fail ${git_for_each_ref} --format="%(refname:foo)" 2>err && + echo "fatal: unrecognized %(refname) argument: foo" >expect && + test_cmp expect err +' + +test_expect_success 'trailer parsing not fooled by --- line' ' + git commit --allow-empty -F - <<-\EOF && + this is the subject + + This is the body. The message has a "---" line which would confuse a + message+patch parser. But here we know we have only a commit message, + so we get it right. + + trailer: wrong + --- + This is more body. + + trailer: right + EOF + + { + echo "trailer: right" && + echo + } >expect && + ${git_for_each_ref} --format="%(trailers)" refs/heads/main >actual && + test_cmp expect actual +' + +test_expect_success 'Add symbolic ref for the following tests' ' + git symbolic-ref refs/heads/sym refs/heads/main +' + +cat >expected <<EOF +refs/heads/main +EOF + +test_expect_success 'Verify usage of %(symref) atom' ' + ${git_for_each_ref} --format="%(symref)" refs/heads/sym >actual && + test_cmp expected actual +' + +cat >expected <<EOF +heads/main +EOF + +test_expect_success 'Verify usage of %(symref:short) atom' ' + ${git_for_each_ref} --format="%(symref:short)" refs/heads/sym >actual && + test_cmp expected actual +' + +cat >expected <<EOF +main +heads/main +EOF + +test_expect_success 'Verify usage of %(symref:lstrip) atom' ' + ${git_for_each_ref} --format="%(symref:lstrip=2)" refs/heads/sym > actual && + ${git_for_each_ref} --format="%(symref:lstrip=-2)" refs/heads/sym >> actual && + test_cmp expected actual && + + ${git_for_each_ref} --format="%(symref:strip=2)" refs/heads/sym > actual && + ${git_for_each_ref} --format="%(symref:strip=-2)" refs/heads/sym >> actual && + test_cmp expected actual +' + +cat >expected <<EOF +refs +refs/heads +EOF + +test_expect_success 'Verify usage of %(symref:rstrip) atom' ' + ${git_for_each_ref} --format="%(symref:rstrip=2)" refs/heads/sym > actual && + ${git_for_each_ref} --format="%(symref:rstrip=-2)" refs/heads/sym >> actual && + test_cmp expected actual +' + +test_expect_success ':remotename and :remoteref' ' + git init remote-tests && + ( + cd remote-tests && + test_commit initial && + git branch -M main && + git remote add from fifth.coffee:blub && + git config branch.main.remote from && + git config branch.main.merge refs/heads/stable && + git remote add to southridge.audio:repo && + git config remote.to.push "refs/heads/*:refs/heads/pushed/*" && + git config branch.main.pushRemote to && + for pair in "%(upstream)=refs/remotes/from/stable" \ + "%(upstream:remotename)=from" \ + "%(upstream:remoteref)=refs/heads/stable" \ + "%(push)=refs/remotes/to/pushed/main" \ + "%(push:remotename)=to" \ + "%(push:remoteref)=refs/heads/pushed/main" + do + echo "${pair#*=}" >expect && + ${git_for_each_ref} --format="${pair%=*}" \ + refs/heads/main >actual && + test_cmp expect actual || exit 1 + done && + git branch push-simple && + git config branch.push-simple.pushRemote from && + actual="$(${git_for_each_ref} \ + --format="%(push:remotename),%(push:remoteref)" \ + refs/heads/push-simple)" && + test from, = "$actual" + ) +' + +test_expect_success "${git_for_each_ref} --ignore-case ignores case" ' + ${git_for_each_ref} --format="%(refname)" refs/heads/MAIN >actual && + test_must_be_empty actual && + + echo refs/heads/main >expect && + ${git_for_each_ref} --format="%(refname)" --ignore-case \ + refs/heads/MAIN >actual && + test_cmp expect actual +' + +test_expect_success "${git_for_each_ref} --omit-empty works" ' + ${git_for_each_ref} --format="%(refname)" >actual && + test_line_count -gt 1 actual && + ${git_for_each_ref} --format="%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)" --omit-empty >actual && + echo refs/heads/main >expect && + test_cmp expect actual +' + +test_expect_success "${git_for_each_ref} --ignore-case works on multiple sort keys" ' + # name refs numerically to avoid case-insensitive filesystem conflicts + nr=0 && + for email in a A b B + do + for subject in a A b B + do + GIT_COMMITTER_EMAIL="$email@example.com" \ + git tag -m "tag $subject" icase-$(printf %02d $nr) && + nr=$((nr+1))|| + return 1 + done + done && + ${git_for_each_ref} --ignore-case \ + --format="%(taggeremail) %(subject) %(refname)" \ + --sort=refname \ + --sort=subject \ + --sort=taggeremail \ + refs/tags/icase-* >actual && + cat >expect <<-\EOF && + <a@example.com> tag a refs/tags/icase-00 + <a@example.com> tag A refs/tags/icase-01 + <A@example.com> tag a refs/tags/icase-04 + <A@example.com> tag A refs/tags/icase-05 + <a@example.com> tag b refs/tags/icase-02 + <a@example.com> tag B refs/tags/icase-03 + <A@example.com> tag b refs/tags/icase-06 + <A@example.com> tag B refs/tags/icase-07 + <b@example.com> tag a refs/tags/icase-08 + <b@example.com> tag A refs/tags/icase-09 + <B@example.com> tag a refs/tags/icase-12 + <B@example.com> tag A refs/tags/icase-13 + <b@example.com> tag b refs/tags/icase-10 + <b@example.com> tag B refs/tags/icase-11 + <B@example.com> tag b refs/tags/icase-14 + <B@example.com> tag B refs/tags/icase-15 + EOF + test_cmp expect actual +' + +test_expect_success "${git_for_each_ref} reports broken tags" ' + git tag -m "good tag" broken-tag-good HEAD && + git cat-file tag broken-tag-good >good && + sed s/commit/blob/ <good >bad && + bad=$(git hash-object -w -t tag bad) && + git update-ref refs/tags/broken-tag-bad $bad && + test_must_fail ${git_for_each_ref} --format="%(*objectname)" \ + refs/tags/broken-tag-* +' + +test_expect_success 'set up tag with signature and no blank lines' ' + git tag -F - fake-sig-no-blanks <<-\EOF + this is the subject + -----BEGIN PGP SIGNATURE----- + not a real signature, but we just care about the + subject/body parsing. It is important here that + there are no blank lines in the signature. + -----END PGP SIGNATURE----- + EOF +' + +test_atom refs/tags/fake-sig-no-blanks contents:subject 'this is the subject' +test_atom refs/tags/fake-sig-no-blanks contents:body '' +test_atom refs/tags/fake-sig-no-blanks contents:signature "$sig" + +test_expect_success 'set up tag with CRLF signature' ' + append_cr <<-\EOF | + this is the subject + -----BEGIN PGP SIGNATURE----- + + not a real signature, but we just care about + the subject/body parsing. It is important here + that there is a blank line separating this + from the signature header. + -----END PGP SIGNATURE----- + EOF + git tag -F - --cleanup=verbatim fake-sig-crlf +' + +test_atom refs/tags/fake-sig-crlf contents:subject 'this is the subject' +test_atom refs/tags/fake-sig-crlf contents:body '' + +# CRLF is retained in the signature, so we have to pass our expected value +# through append_cr. But test_atom requires a shell string, which means command +# substitution, and the shell will strip trailing newlines from the output of +# the substitution. Hack around it by adding and then removing a dummy line. +sig_crlf="$(printf "%s" "$sig" | append_cr; echo dummy)" +sig_crlf=${sig_crlf%dummy} +test_atom refs/tags/fake-sig-crlf contents:signature "$sig_crlf" + +test_expect_success 'set up tag with signature and trailers' ' + git tag -F - fake-sig-trailer <<-\EOF + this is the subject + + this is the body + + My-Trailer: foo + -----BEGIN PGP SIGNATURE----- + + not a real signature, but we just care about the + subject/body/trailer parsing. + -----END PGP SIGNATURE----- + EOF +' + +# use "separator=" here to suppress the terminating newline +test_atom refs/tags/fake-sig-trailer trailers:separator= 'My-Trailer: foo' + +test_expect_success "${git_for_each_ref} --stdin: empty" ' + >in && + ${git_for_each_ref} --format="%(refname)" --stdin <in >actual && + ${git_for_each_ref} --format="%(refname)" >expect && + test_cmp expect actual +' + +test_expect_success "${git_for_each_ref} --stdin: fails if extra args" ' + >in && + test_must_fail ${git_for_each_ref} --format="%(refname)" \ + --stdin refs/heads/extra <in 2>err && + grep "unknown arguments supplied with --stdin" err +' + +test_expect_success "${git_for_each_ref} --stdin: matches" ' + cat >in <<-EOF && + refs/tags/multi* + refs/heads/amb* + EOF + + cat >expect <<-EOF && + refs/heads/ambiguous + refs/tags/multi-ref1-100000-user1 + refs/tags/multi-ref1-100000-user2 + refs/tags/multi-ref1-200000-user1 + refs/tags/multi-ref1-200000-user2 + refs/tags/multi-ref2-100000-user1 + refs/tags/multi-ref2-100000-user2 + refs/tags/multi-ref2-200000-user1 + refs/tags/multi-ref2-200000-user2 + refs/tags/multiline + EOF + + ${git_for_each_ref} --format="%(refname)" --stdin <in >actual && + test_cmp expect actual +' + +test_expect_success "${git_for_each_ref} with non-existing refs" ' + cat >in <<-EOF && + refs/heads/this-ref-does-not-exist + refs/tags/bogus + EOF + + ${git_for_each_ref} --format="%(refname)" --stdin <in >actual && + test_must_be_empty actual && + + xargs ${git_for_each_ref} --format="%(refname)" <in >actual && + test_must_be_empty actual +' + +test_expect_success "${git_for_each_ref} with nested tags" ' + git tag -am "Normal tag" nested/base HEAD && + git tag -am "Nested tag" nested/nest1 refs/tags/nested/base && + git tag -am "Double nested tag" nested/nest2 refs/tags/nested/nest1 && + + head_oid="$(git rev-parse HEAD)" && + base_tag_oid="$(git rev-parse refs/tags/nested/base)" && + nest1_tag_oid="$(git rev-parse refs/tags/nested/nest1)" && + nest2_tag_oid="$(git rev-parse refs/tags/nested/nest2)" && + + cat >expect <<-EOF && + refs/tags/nested/base $base_tag_oid tag $head_oid commit + refs/tags/nested/nest1 $nest1_tag_oid tag $head_oid commit + refs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit + EOF + + ${git_for_each_ref} \ + --format="%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)" \ + refs/tags/nested/ >actual && + test_cmp expect actual +' + +test_expect_success 'is-base atom with non-commits' ' + ${git_for_each_ref} --format="%(is-base:HEAD) %(refname)" >out 2>err && + grep "(HEAD) refs/heads/main" out && + + test_line_count = 2 err && + grep "error: object .* is a commit, not a blob" err && + grep "error: bad tag pointer to" err +' + +GRADE_FORMAT="%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)" +TRUSTLEVEL_FORMAT="%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)" + +test_expect_success GPG 'setup for signature atom using gpg' ' + git checkout -b signed && + + test_when_finished "test_unconfig commit.gpgSign" && + + echo "1" >file && + git add file && + test_tick && + git commit -S -m "file: 1" && + git tag first-signed && + + echo "2" >file && + test_tick && + git commit -a -m "file: 2" && + git tag second-unsigned && + + git config commit.gpgSign 1 && + echo "3" >file && + test_tick && + git commit -a --no-gpg-sign -m "file: 3" && + git tag third-unsigned && + + test_tick && + git rebase -f HEAD^^ && git tag second-signed HEAD^ && + git tag third-signed && + + echo "4" >file && + test_tick && + git commit -a -SB7227189 -m "file: 4" && + git tag fourth-signed && + + echo "5" >file && + test_tick && + git commit -a --no-gpg-sign -m "file: 5" && + git tag fifth-unsigned && + + echo "6" >file && + test_tick && + git commit -a --no-gpg-sign -m "file: 6" && + + test_tick && + git rebase -f HEAD^^ && + git tag fifth-signed HEAD^ && + git tag sixth-signed && + + echo "7" >file && + test_tick && + git commit -a --no-gpg-sign -m "file: 7" && + git tag seventh-unsigned +' + +test_expect_success GPGSSH 'setup for signature atom using ssh' ' + test_when_finished "test_unconfig gpg.format user.signingkey" && + + test_config gpg.format ssh && + test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" && + echo "8" >file && + test_tick && + git add file && + git commit -S -m "file: 8" && + git tag eighth-signed-ssh +' + +test_expect_success GPG2 'bare signature atom' ' + git verify-commit first-signed 2>expect && + echo >>expect && + ${git_for_each_ref} refs/tags/first-signed \ + --format="%(signature)" >actual && + test_cmp expect actual +' + +test_expect_success GPG 'show good signature with custom format' ' + git verify-commit first-signed && + cat >expect <<-\EOF && + G + 13B6F51ECDDE430D + C O Mitter <committer@example.com> + 73D758744BE721698EC54E8713B6F51ECDDE430D + 73D758744BE721698EC54E8713B6F51ECDDE430D + EOF + ${git_for_each_ref} refs/tags/first-signed \ + --format="$GRADE_FORMAT" >actual && + test_cmp expect actual +' +test_expect_success GPGSSH 'show good signature with custom format with ssh' ' + test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && + FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") && + cat >expect.tmpl <<-\EOF && + G + FINGERPRINT + principal with number 1 + FINGERPRINT + + EOF + sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect && + ${git_for_each_ref} refs/tags/eighth-signed-ssh \ + --format="$GRADE_FORMAT" >actual && + test_cmp expect actual +' + +test_expect_success GPG 'signature atom with grade option and bad signature' ' + git cat-file commit third-signed >raw && + sed -e "s/^file: 3/file: 3 forged/" raw >forged1 && + FORGED1=$(git hash-object -w -t commit forged1) && + git update-ref refs/tags/third-signed "$FORGED1" && + test_must_fail git verify-commit "$FORGED1" && + + cat >expect <<-\EOF && + B + 13B6F51ECDDE430D + C O Mitter <committer@example.com> + + + EOF + ${git_for_each_ref} refs/tags/third-signed \ + --format="$GRADE_FORMAT" >actual && + test_cmp expect actual +' + +test_expect_success GPG 'show untrusted signature with custom format' ' + cat >expect <<-\EOF && + U + 65A0EEA02E30CAD7 + Eris Discordia <discord@example.net> + F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7 + D4BE22311AD3131E5EDA29A461092E85B7227189 + EOF + ${git_for_each_ref} refs/tags/fourth-signed \ + --format="$GRADE_FORMAT" >actual && + test_cmp expect actual +' + +test_expect_success GPG 'show untrusted signature with undefined trust level' ' + cat >expect <<-\EOF && + undefined + 65A0EEA02E30CAD7 + Eris Discordia <discord@example.net> + F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7 + D4BE22311AD3131E5EDA29A461092E85B7227189 + EOF + ${git_for_each_ref} refs/tags/fourth-signed \ + --format="$TRUSTLEVEL_FORMAT" >actual && + test_cmp expect actual +' + +test_expect_success GPG 'show untrusted signature with ultimate trust level' ' + cat >expect <<-\EOF && + ultimate + 13B6F51ECDDE430D + C O Mitter <committer@example.com> + 73D758744BE721698EC54E8713B6F51ECDDE430D + 73D758744BE721698EC54E8713B6F51ECDDE430D + EOF + ${git_for_each_ref} refs/tags/sixth-signed \ + --format="$TRUSTLEVEL_FORMAT" >actual && + test_cmp expect actual +' + +test_expect_success GPG 'show unknown signature with custom format' ' + cat >expect <<-\EOF && + E + 13B6F51ECDDE430D + + + + EOF + GNUPGHOME="$GNUPGHOME_NOT_USED" ${git_for_each_ref} \ + refs/tags/sixth-signed --format="$GRADE_FORMAT" >actual && + test_cmp expect actual +' + +test_expect_success GPG 'show lack of signature with custom format' ' + cat >expect <<-\EOF && + N + + + + + EOF + ${git_for_each_ref} refs/tags/seventh-unsigned \ + --format="$GRADE_FORMAT" >actual && + test_cmp expect actual +' + +test_done diff --git a/t/helper/test-advise.c b/t/helper/test-advise.c index 6967c8e25c..81ed93a05c 100644 --- a/t/helper/test-advise.c +++ b/t/helper/test-advise.c @@ -3,6 +3,7 @@ #include "test-tool.h" #include "advice.h" #include "config.h" +#include "environment.h" #include "setup.h" int cmd__advise_if_enabled(int argc, const char **argv) @@ -11,7 +12,7 @@ int cmd__advise_if_enabled(int argc, const char **argv) die("usage: %s <advice>", argv[0]); setup_git_directory(); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); /* * Any advice type can be used for testing, but NESTED_TAG was diff --git a/t/helper/test-bitmap.c b/t/helper/test-bitmap.c index 3f23f21072..16a01669e4 100644 --- a/t/helper/test-bitmap.c +++ b/t/helper/test-bitmap.c @@ -10,6 +10,11 @@ static int bitmap_list_commits(void) return test_bitmap_commits(the_repository); } +static int bitmap_list_commits_with_offset(void) +{ + return test_bitmap_commits_with_offset(the_repository); +} + static int bitmap_dump_hashes(void) { return test_bitmap_hashes(the_repository); @@ -36,6 +41,8 @@ int cmd__bitmap(int argc, const char **argv) if (argc == 2 && !strcmp(argv[1], "list-commits")) return bitmap_list_commits(); + if (argc == 2 && !strcmp(argv[1], "list-commits-with-offset")) + return bitmap_list_commits_with_offset(); if (argc == 2 && !strcmp(argv[1], "dump-hashes")) return bitmap_dump_hashes(); if (argc == 2 && !strcmp(argv[1], "dump-pseudo-merges")) @@ -46,6 +53,7 @@ int cmd__bitmap(int argc, const char **argv) return bitmap_dump_pseudo_merge_objects(atoi(argv[2])); usage("\ttest-tool bitmap list-commits\n" + "\ttest-tool bitmap list-commits-with-offset\n" "\ttest-tool bitmap dump-hashes\n" "\ttest-tool bitmap dump-pseudo-merges\n" "\ttest-tool bitmap dump-pseudo-merge-commits <n>\n" diff --git a/t/helper/test-bloom.c b/t/helper/test-bloom.c index 9aa2c5a592..3283544bd3 100644 --- a/t/helper/test-bloom.c +++ b/t/helper/test-bloom.c @@ -12,13 +12,13 @@ static struct bloom_filter_settings settings = DEFAULT_BLOOM_FILTER_SETTINGS; static void add_string_to_filter(const char *data, struct bloom_filter *filter) { struct bloom_key key; - fill_bloom_key(data, strlen(data), &key, &settings); + bloom_key_fill(&key, data, strlen(data), &settings); printf("Hashes:"); for (size_t i = 0; i < settings.num_hashes; i++) printf("0x%08x|", key.hashes[i]); printf("\n"); add_key_to_filter(&key, filter, &settings); - clear_bloom_key(&key); + bloom_key_clear(&key); } static void print_bloom_filter(struct bloom_filter *filter) { @@ -61,13 +61,13 @@ int cmd__bloom(int argc, const char **argv) uint32_t hashed; if (argc < 3) usage(bloom_usage); - hashed = murmur3_seeded_v2(0, argv[2], strlen(argv[2])); + hashed = test_bloom_murmur3_seeded(0, argv[2], strlen(argv[2]), 2); printf("Murmur3 Hash with seed=0:0x%08x\n", hashed); } if (!strcmp(argv[1], "get_murmur3_seven_highbit")) { uint32_t hashed; - hashed = murmur3_seeded_v2(0, "\x99\xaa\xbb\xcc\xdd\xee\xff", 7); + hashed = test_bloom_murmur3_seeded(0, "\x99\xaa\xbb\xcc\xdd\xee\xff", 7, 2); printf("Murmur3 Hash with seed=0:0x%08x\n", hashed); } diff --git a/t/helper/test-config.c b/t/helper/test-config.c index 75e028ab2a..9f8cca7c48 100644 --- a/t/helper/test-config.c +++ b/t/helper/test-config.c @@ -32,10 +32,10 @@ * ascending order of priority from a config_set * constructed from files entered as arguments. * - * iterate -> iterate over all values using git_config(), and print some + * iterate -> iterate over all values using repo_config(), and print some * data for each * - * git_config_int -> iterate over all values using git_config() and print the + * git_config_int -> iterate over all values using repo_config() and print the * integer value for the entered key or die * * Examples: @@ -110,7 +110,7 @@ int cmd__config(int argc, const char **argv) fprintf(stderr, "Please, provide a command name on the command-line\n"); goto exit1; } else if (argc == 3 && !strcmp(argv[1], "get_value")) { - if (!git_config_get_value(argv[2], &v)) { + if (!repo_config_get_value(the_repository, argv[2], &v)) { if (!v) printf("(NULL)\n"); else @@ -121,7 +121,7 @@ int cmd__config(int argc, const char **argv) goto exit1; } } else if (argc == 3 && !strcmp(argv[1], "get_value_multi")) { - if (!git_config_get_value_multi(argv[2], &strptr)) { + if (!repo_config_get_value_multi(the_repository, argv[2], &strptr)) { for (i = 0; i < strptr->nr; i++) { v = strptr->items[i].string; if (!v) @@ -137,7 +137,7 @@ int cmd__config(int argc, const char **argv) } else if (argc == 3 && !strcmp(argv[1], "get")) { int ret; - if (!(ret = git_config_get(argv[2]))) + if (!(ret = repo_config_get(the_repository, argv[2]))) goto exit0; else if (ret == 1) printf("Value not found for \"%s\"\n", argv[2]); @@ -155,7 +155,7 @@ int cmd__config(int argc, const char **argv) BUG("Key \"%s\" has unknown return %d", argv[2], ret); goto exit1; } else if (argc == 3 && !strcmp(argv[1], "get_int")) { - if (!git_config_get_int(argv[2], &val)) { + if (!repo_config_get_int(the_repository, argv[2], &val)) { printf("%d\n", val); goto exit0; } else { @@ -163,7 +163,7 @@ int cmd__config(int argc, const char **argv) goto exit1; } } else if (argc == 3 && !strcmp(argv[1], "get_bool")) { - if (!git_config_get_bool(argv[2], &val)) { + if (!repo_config_get_bool(the_repository, argv[2], &val)) { printf("%d\n", val); goto exit0; } else { @@ -171,7 +171,7 @@ int cmd__config(int argc, const char **argv) goto exit1; } } else if (argc == 3 && !strcmp(argv[1], "get_string")) { - if (!git_config_get_string_tmp(argv[2], &v)) { + if (!repo_config_get_string_tmp(the_repository, argv[2], &v)) { printf("%s\n", v); goto exit0; } else { @@ -218,10 +218,10 @@ int cmd__config(int argc, const char **argv) goto exit1; } } else if (!strcmp(argv[1], "iterate")) { - git_config(iterate_cb, NULL); + repo_config(the_repository, iterate_cb, NULL); goto exit0; } else if (argc == 3 && !strcmp(argv[1], "git_config_int")) { - git_config(parse_int_cb, (void *) argv[2]); + repo_config(the_repository, parse_int_cb, (void *) argv[2]); goto exit0; } diff --git a/t/helper/test-delta.c b/t/helper/test-delta.c index 6bc787a474..52ea00c937 100644 --- a/t/helper/test-delta.c +++ b/t/helper/test-delta.c @@ -11,6 +11,7 @@ #include "test-tool.h" #include "git-compat-util.h" #include "delta.h" +#include "strbuf.h" static const char usage_str[] = "test-tool delta (-d|-p) <from_file> <data_file> <out_file>"; @@ -18,68 +19,38 @@ static const char usage_str[] = int cmd__delta(int argc, const char **argv) { int fd; - struct stat st; - void *from_buf = NULL, *data_buf = NULL, *out_buf = NULL; - unsigned long from_size, data_size, out_size; - int ret = 1; + struct strbuf from = STRBUF_INIT, data = STRBUF_INIT; + char *out_buf; + unsigned long out_size; - if (argc != 5 || (strcmp(argv[1], "-d") && strcmp(argv[1], "-p"))) { - fprintf(stderr, "usage: %s\n", usage_str); - return 1; - } + if (argc != 5 || (strcmp(argv[1], "-d") && strcmp(argv[1], "-p"))) + usage(usage_str); - fd = open(argv[2], O_RDONLY); - if (fd < 0 || fstat(fd, &st)) { - perror(argv[2]); - return 1; - } - from_size = st.st_size; - from_buf = xmalloc(from_size); - if (read_in_full(fd, from_buf, from_size) < 0) { - perror(argv[2]); - close(fd); - goto cleanup; - } - close(fd); - - fd = open(argv[3], O_RDONLY); - if (fd < 0 || fstat(fd, &st)) { - perror(argv[3]); - goto cleanup; - } - data_size = st.st_size; - data_buf = xmalloc(data_size); - if (read_in_full(fd, data_buf, data_size) < 0) { - perror(argv[3]); - close(fd); - goto cleanup; - } - close(fd); + if (strbuf_read_file(&from, argv[2], 0) < 0) + die_errno("unable to read '%s'", argv[2]); + if (strbuf_read_file(&data, argv[3], 0) < 0) + die_errno("unable to read '%s'", argv[3]); if (argv[1][1] == 'd') - out_buf = diff_delta(from_buf, from_size, - data_buf, data_size, + out_buf = diff_delta(from.buf, from.len, + data.buf, data.len, &out_size, 0); else - out_buf = patch_delta(from_buf, from_size, - data_buf, data_size, + out_buf = patch_delta(from.buf, from.len, + data.buf, data.len, &out_size); - if (!out_buf) { - fprintf(stderr, "delta operation failed (returned NULL)\n"); - goto cleanup; - } + if (!out_buf) + die("delta operation failed (returned NULL)"); - fd = open (argv[4], O_WRONLY|O_CREAT|O_TRUNC, 0666); - if (fd < 0 || write_in_full(fd, out_buf, out_size) < 0) { - perror(argv[4]); - goto cleanup; - } + fd = xopen(argv[4], O_WRONLY|O_CREAT|O_TRUNC, 0666); + if (write_in_full(fd, out_buf, out_size) < 0) + die_errno("write(%s)", argv[4]); + if (close(fd) < 0) + die_errno("close(%s)", argv[4]); - ret = 0; -cleanup: - free(from_buf); - free(data_buf); + strbuf_release(&from); + strbuf_release(&data); free(out_buf); - return ret; + return 0; } diff --git a/t/helper/test-find-pack.c b/t/helper/test-find-pack.c index 76c2f4eba8..611a13a326 100644 --- a/t/helper/test-find-pack.c +++ b/t/helper/test-find-pack.c @@ -2,7 +2,7 @@ #include "test-tool.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "packfile.h" #include "parse-options.h" #include "setup.h" diff --git a/t/helper/test-hashmap.c b/t/helper/test-hashmap.c index 7782ae585e..56d223a283 100644 --- a/t/helper/test-hashmap.c +++ b/t/helper/test-hashmap.c @@ -137,6 +137,11 @@ static void perf_hashmap(unsigned int method, unsigned int rounds) * Read stdin line by line and print result of commands to stdout: * * perfhashmap method rounds -> test hashmap.[ch] performance + * + * NOTE: this is not used by any of our mechanized build & test + * procedure, after 3469a236 (t: port helper/test-hashmap.c to + * unit-tests/t-hashmap.c, 2024-08-03). See the log message of that + * commit for the reason why this is still here. */ int cmd__hashmap(int argc UNUSED, const char **argv UNUSED) { @@ -149,8 +154,8 @@ int cmd__hashmap(int argc UNUSED, const char **argv UNUSED) /* break line into command and up to two parameters */ string_list_setlen(&parts, 0); - string_list_split_in_place(&parts, line.buf, DELIM, 2); - string_list_remove_empty_items(&parts, 0); + string_list_split_in_place_f(&parts, line.buf, DELIM, 2, + STRING_LIST_SPLIT_NONEMPTY); /* ignore empty lines */ if (!parts.nr) diff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c index a288069b04..f8316a7d29 100644 --- a/t/helper/test-json-writer.c +++ b/t/helper/test-json-writer.c @@ -492,8 +492,8 @@ static int scripted(void) /* break line into command and zero or more tokens */ string_list_setlen(&parts, 0); - string_list_split_in_place(&parts, line, " ", -1); - string_list_remove_empty_items(&parts, 0); + string_list_split_in_place_f(&parts, line, " ", -1, + STRING_LIST_SPLIT_NONEMPTY); /* ignore empty lines */ if (!parts.nr || !*parts.items[0].string) diff --git a/t/helper/test-pack-mtimes.c b/t/helper/test-pack-mtimes.c index fdf1b13437..d51aaa3dc4 100644 --- a/t/helper/test-pack-mtimes.c +++ b/t/helper/test-pack-mtimes.c @@ -3,7 +3,7 @@ #include "test-tool.h" #include "hex.h" #include "strbuf.h" -#include "object-store.h" +#include "odb.h" #include "packfile.h" #include "pack-mtimes.h" #include "setup.h" diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c index f2663dd0c0..68579d83f3 100644 --- a/t/helper/test-parse-options.c +++ b/t/helper/test-parse-options.c @@ -131,6 +131,7 @@ int cmd__parse_options(int argc, const char **argv) .short_name = 'B', .long_name = "no-fear", .value = &boolean, + .precision = sizeof(boolean), .help = "be brave", .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, .defval = 1, @@ -148,9 +149,16 @@ int cmd__parse_options(int argc, const char **argv) OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23), OPT_CMDMODE(0, "mode1", &integer, "set integer to 1 (cmdmode option)", 1), OPT_CMDMODE(0, "mode2", &integer, "set integer to 2 (cmdmode option)", 2), - OPT_CALLBACK_F(0, "mode34", &integer, "(3|4)", - "set integer to 3 or 4 (cmdmode option)", - PARSE_OPT_CMDMODE, mode34_callback), + { + .type = OPTION_CALLBACK, + .long_name = "mode34", + .value = &integer, + .precision = sizeof(integer), + .argh = "(3|4)", + .help = "set integer to 3 or 4 (cmdmode option)", + .flags = PARSE_OPT_CMDMODE, + .callback = mode34_callback, + }, OPT_CALLBACK('L', "length", &integer, "str", "get length of <str>", length_callback), OPT_FILENAME('F', "file", &file, "set file to <file>"), @@ -170,6 +178,7 @@ int cmd__parse_options(int argc, const char **argv) .type = OPTION_COUNTUP, .short_name = '+', .value = &boolean, + .precision = sizeof(boolean), .help = "same as -b", .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH, }, @@ -177,6 +186,7 @@ int cmd__parse_options(int argc, const char **argv) .type = OPTION_COUNTUP, .long_name = "ambiguous", .value = &ambiguous, + .precision = sizeof(ambiguous), .help = "positive ambiguity", .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, }, @@ -184,6 +194,7 @@ int cmd__parse_options(int argc, const char **argv) .type = OPTION_COUNTUP, .long_name = "no-ambiguous", .value = &ambiguous, + .precision = sizeof(ambiguous), .help = "negative ambiguity", .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG, }, diff --git a/t/helper/test-partial-clone.c b/t/helper/test-partial-clone.c index 34f1aee558..d848800749 100644 --- a/t/helper/test-partial-clone.c +++ b/t/helper/test-partial-clone.c @@ -1,7 +1,7 @@ #include "test-tool.h" #include "hex.h" #include "repository.h" -#include "object-store.h" +#include "odb.h" #include "setup.h" /* @@ -23,7 +23,7 @@ static void object_info(const char *gitdir, const char *oid_hex) die("could not init repo"); if (parse_oid_hex_algop(oid_hex, &oid, &p, r.hash_algo)) die("could not parse oid"); - if (oid_object_info_extended(&r, &oid, &oi, 0)) + if (odb_read_object_info_extended(r.objects, &oid, &oi, 0)) die("could not obtain object info"); printf("%d\n", (int) size); diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c index 086238c826..f5f33751da 100644 --- a/t/helper/test-path-utils.c +++ b/t/helper/test-path-utils.c @@ -348,6 +348,7 @@ int cmd__path_utils(int argc, const char **argv) if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) { int len; struct string_list ceiling_dirs = STRING_LIST_INIT_DUP; + const char path_sep[] = { PATH_SEP, '\0' }; char *path = xstrdup(argv[2]); /* @@ -362,7 +363,7 @@ int cmd__path_utils(int argc, const char **argv) */ if (normalize_path_copy(path, path)) die("Path \"%s\" could not be normalized", argv[2]); - string_list_split(&ceiling_dirs, argv[3], PATH_SEP, -1); + string_list_split(&ceiling_dirs, argv[3], path_sep, -1); filter_string_list(&ceiling_dirs, 0, normalize_ceiling_entry, NULL); len = longest_ancestor_length(path, &ceiling_dirs); diff --git a/t/helper/test-path-walk.c b/t/helper/test-path-walk.c index 61e845e5ec..fe63002c2b 100644 --- a/t/helper/test-path-walk.c +++ b/t/helper/test-path-walk.c @@ -82,6 +82,8 @@ int cmd__path_walk(int argc, const char **argv) N_("toggle inclusion of tree objects")), OPT_BOOL(0, "prune", &info.prune_all_uninteresting, N_("toggle pruning of uninteresting paths")), + OPT_BOOL(0, "edge-aggressive", &info.edge_aggressive, + N_("toggle aggressive edge walk")), OPT_BOOL(0, "stdin-pl", &stdin_pl, N_("read a pattern list over stdin")), OPT_END(), diff --git a/t/helper/test-read-cache.c b/t/helper/test-read-cache.c index e277dde8e7..9ae71cefb3 100644 --- a/t/helper/test-read-cache.c +++ b/t/helper/test-read-cache.c @@ -2,6 +2,7 @@ #include "test-tool.h" #include "config.h" +#include "environment.h" #include "read-cache-ll.h" #include "repository.h" #include "setup.h" @@ -19,7 +20,7 @@ int cmd__read_cache(int argc, const char **argv) if (argc == 2) cnt = strtol(argv[1], NULL, 0); setup_git_directory(); - git_config(git_default_config, NULL); + repo_config(the_repository, git_default_config, NULL); for (i = 0; i < cnt; i++) { repo_read_index(the_repository); diff --git a/t/helper/test-read-graph.c b/t/helper/test-read-graph.c index 8b413b644b..6a5f64e473 100644 --- a/t/helper/test-read-graph.c +++ b/t/helper/test-read-graph.c @@ -3,7 +3,7 @@ #include "test-tool.h" #include "commit-graph.h" #include "repository.h" -#include "object-store.h" +#include "odb.h" #include "bloom.h" #include "setup.h" @@ -73,15 +73,15 @@ static void dump_graph_bloom_filters(struct commit_graph *graph) int cmd__read_graph(int argc, const char **argv) { struct commit_graph *graph = NULL; - struct object_directory *odb; + struct odb_source *source; int ret = 0; setup_git_directory(); - odb = the_repository->objects->odb; + source = the_repository->objects->sources; prepare_repo_settings(the_repository); - graph = read_commit_graph_one(the_repository, odb); + graph = read_commit_graph_one(source); if (!graph) { ret = 1; goto done; diff --git a/t/helper/test-read-midx.c b/t/helper/test-read-midx.c index ac81390899..6de5d1665a 100644 --- a/t/helper/test-read-midx.c +++ b/t/helper/test-read-midx.c @@ -4,21 +4,31 @@ #include "hex.h" #include "midx.h" #include "repository.h" -#include "object-store.h" +#include "odb.h" #include "pack-bitmap.h" #include "packfile.h" #include "setup.h" #include "gettext.h" #include "pack-revindex.h" +static struct multi_pack_index *setup_midx(const char *object_dir) +{ + struct odb_source *source; + setup_git_directory(); + source = odb_find_source(the_repository->objects, object_dir); + if (!source) + source = odb_add_to_alternates_memory(the_repository->objects, + object_dir); + return load_multi_pack_index(source); +} + static int read_midx_file(const char *object_dir, const char *checksum, int show_objects) { uint32_t i; struct multi_pack_index *m; - setup_git_directory(); - m = load_multi_pack_index(the_repository, object_dir, 1); + m = setup_midx(object_dir); if (!m) return 1; @@ -56,7 +66,7 @@ static int read_midx_file(const char *object_dir, const char *checksum, for (i = 0; i < m->num_packs; i++) printf("%s\n", m->pack_names[i]); - printf("object-dir: %s\n", m->object_dir); + printf("object-dir: %s\n", m->source->path); if (show_objects) { struct object_id oid; @@ -65,7 +75,7 @@ static int read_midx_file(const char *object_dir, const char *checksum, for (i = 0; i < m->num_objects; i++) { nth_midxed_object_oid(&oid, m, i + m->num_objects_in_base); - fill_midx_entry(the_repository, &oid, &e, m); + fill_midx_entry(m, &oid, &e); printf("%s %"PRIu64"\t%s\n", oid_to_hex(&oid), e.offset, e.p->pack_name); @@ -81,8 +91,7 @@ static int read_midx_checksum(const char *object_dir) { struct multi_pack_index *m; - setup_git_directory(); - m = load_multi_pack_index(the_repository, object_dir, 1); + m = setup_midx(object_dir); if (!m) return 1; printf("%s\n", hash_to_hex(get_midx_checksum(m))); @@ -96,9 +105,7 @@ static int read_midx_preferred_pack(const char *object_dir) struct multi_pack_index *midx = NULL; uint32_t preferred_pack; - setup_git_directory(); - - midx = load_multi_pack_index(the_repository, object_dir, 1); + midx = setup_midx(object_dir); if (!midx) return 1; @@ -119,14 +126,12 @@ static int read_midx_bitmapped_packs(const char *object_dir) struct bitmapped_pack pack; uint32_t i; - setup_git_directory(); - - midx = load_multi_pack_index(the_repository, object_dir, 1); + midx = setup_midx(object_dir); if (!midx) return 1; for (i = 0; i < midx->num_packs + midx->num_packs_in_base; i++) { - if (nth_bitmapped_pack(the_repository, midx, &pack, i) < 0) { + if (nth_bitmapped_pack(midx, &pack, i) < 0) { close_midx(midx); return 1; } diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c index 4cfc7c90b5..83b06d39a3 100644 --- a/t/helper/test-ref-store.c +++ b/t/helper/test-ref-store.c @@ -5,7 +5,7 @@ #include "refs.h" #include "setup.h" #include "worktree.h" -#include "object-store.h" +#include "odb.h" #include "path.h" #include "repository.h" #include "strbuf.h" @@ -29,7 +29,7 @@ static unsigned int parse_flags(const char *str, struct flag_definition *defs) if (!strcmp(str, "0")) return 0; - string_list_split(&masks, str, ',', 64); + string_list_split(&masks, str, ",", 64); for (size_t i = 0; i < masks.nr; i++) { const char *name = masks.items[i].string; struct flag_definition *def = defs; @@ -79,7 +79,7 @@ static const char **get_store(const char **argv, struct ref_store **refs) if (!repo_submodule_path_append(the_repository, &sb, gitdir, "objects/")) die("computing submodule path failed"); - add_to_alternates_memory(sb.buf); + odb_add_to_alternates_memory(the_repository->objects, sb.buf); strbuf_release(&sb); *refs = repo_get_submodule_ref_store(the_repository, gitdir); @@ -215,7 +215,8 @@ static int cmd_for_each_reflog(struct ref_store *refs, return refs_for_each_reflog(refs, each_reflog, NULL); } -static int each_reflog_ent(struct object_id *old_oid, struct object_id *new_oid, +static int each_reflog_ent(const char *refname UNUSED, + struct object_id *old_oid, struct object_id *new_oid, const char *committer, timestamp_t timestamp, int tz, const char *msg, void *cb_data UNUSED) { diff --git a/t/helper/test-string-list.c b/t/helper/test-string-list.c index 6f10c5a435..6be0cdb8e2 100644 --- a/t/helper/test-string-list.c +++ b/t/helper/test-string-list.c @@ -1,105 +1,9 @@ -#define DISABLE_SIGN_COMPARE_WARNINGS - #include "test-tool.h" #include "strbuf.h" #include "string-list.h" -/* - * Parse an argument into a string list. arg should either be a - * ':'-separated list of strings, or "-" to indicate an empty string - * list (as opposed to "", which indicates a string list containing a - * single empty string). list->strdup_strings must be set. - */ -static void parse_string_list(struct string_list *list, const char *arg) -{ - if (!strcmp(arg, "-")) - return; - - (void)string_list_split(list, arg, ':', -1); -} - -static void write_list(const struct string_list *list) -{ - int i; - for (i = 0; i < list->nr; i++) - printf("[%d]: \"%s\"\n", i, list->items[i].string); -} - -static void write_list_compact(const struct string_list *list) -{ - int i; - if (!list->nr) - printf("-\n"); - else { - printf("%s", list->items[0].string); - for (i = 1; i < list->nr; i++) - printf(":%s", list->items[i].string); - printf("\n"); - } -} - -static int prefix_cb(struct string_list_item *item, void *cb_data) -{ - const char *prefix = (const char *)cb_data; - return starts_with(item->string, prefix); -} - int cmd__string_list(int argc, const char **argv) { - if (argc == 5 && !strcmp(argv[1], "split")) { - struct string_list list = STRING_LIST_INIT_DUP; - int i; - const char *s = argv[2]; - int delim = *argv[3]; - int maxsplit = atoi(argv[4]); - - i = string_list_split(&list, s, delim, maxsplit); - printf("%d\n", i); - write_list(&list); - string_list_clear(&list, 0); - return 0; - } - - if (argc == 5 && !strcmp(argv[1], "split_in_place")) { - struct string_list list = STRING_LIST_INIT_NODUP; - int i; - char *s = xstrdup(argv[2]); - const char *delim = argv[3]; - int maxsplit = atoi(argv[4]); - - i = string_list_split_in_place(&list, s, delim, maxsplit); - printf("%d\n", i); - write_list(&list); - string_list_clear(&list, 0); - free(s); - return 0; - } - - if (argc == 4 && !strcmp(argv[1], "filter")) { - /* - * Retain only the items that have the specified prefix. - * Arguments: list|- prefix - */ - struct string_list list = STRING_LIST_INIT_DUP; - const char *prefix = argv[3]; - - parse_string_list(&list, argv[2]); - filter_string_list(&list, 0, prefix_cb, (void *)prefix); - write_list_compact(&list); - string_list_clear(&list, 0); - return 0; - } - - if (argc == 3 && !strcmp(argv[1], "remove_duplicates")) { - struct string_list list = STRING_LIST_INIT_DUP; - - parse_string_list(&list, argv[2]); - string_list_remove_duplicates(&list, 0); - write_list_compact(&list); - string_list_clear(&list, 0); - return 0; - } - if (argc == 2 && !strcmp(argv[1], "sort")) { struct string_list list = STRING_LIST_INIT_NODUP; struct strbuf sb = STRBUF_INIT; diff --git a/t/helper/test-truncate.c b/t/helper/test-truncate.c index 3931deaec7..2820cc7ed7 100644 --- a/t/helper/test-truncate.c +++ b/t/helper/test-truncate.c @@ -21,5 +21,8 @@ int cmd__truncate(int argc, const char **argv) if (ftruncate(fd, (off_t) sz) < 0) die_errno("failed to truncate file"); + + close(fd); + return 0; } diff --git a/t/helper/test-userdiff.c b/t/helper/test-userdiff.c index 94c48ababb..aa3a9894d2 100644 --- a/t/helper/test-userdiff.c +++ b/t/helper/test-userdiff.c @@ -41,7 +41,7 @@ int cmd__userdiff(int argc, const char **argv) if (want & USERDIFF_DRIVER_TYPE_CUSTOM) { setup_git_directory(); - git_config(cmd__userdiff_config, NULL); + repo_config(the_repository, cmd__userdiff_config, NULL); } for_each_userdiff_driver(driver_cb, &want); diff --git a/t/meson.build b/t/meson.build index d052fc3e23..7974795fe4 100644 --- a/t/meson.build +++ b/t/meson.build @@ -1,5 +1,6 @@ clar_test_suites = [ 'unit-tests/u-ctype.c', + 'unit-tests/u-dir.c', 'unit-tests/u-example-decorate.c', 'unit-tests/u-hash.c', 'unit-tests/u-hashmap.c', @@ -8,9 +9,18 @@ clar_test_suites = [ 'unit-tests/u-oidmap.c', 'unit-tests/u-oidtree.c', 'unit-tests/u-prio-queue.c', + 'unit-tests/u-reftable-basics.c', + 'unit-tests/u-reftable-block.c', + 'unit-tests/u-reftable-merged.c', + 'unit-tests/u-reftable-pq.c', + 'unit-tests/u-reftable-readwrite.c', + 'unit-tests/u-reftable-record.c', + 'unit-tests/u-reftable-stack.c', + 'unit-tests/u-reftable-table.c', 'unit-tests/u-reftable-tree.c', 'unit-tests/u-strbuf.c', 'unit-tests/u-strcmp-offset.c', + 'unit-tests/u-string-list.c', 'unit-tests/u-strvec.c', 'unit-tests/u-trailer.c', 'unit-tests/u-urlmatch-normalization.c', @@ -19,7 +29,8 @@ clar_test_suites = [ clar_sources = [ 'unit-tests/clar/clar.c', 'unit-tests/unit-test.c', - 'unit-tests/lib-oid.c' + 'unit-tests/lib-oid.c', + 'unit-tests/lib-reftable.c' ] clar_decls_h = custom_target( @@ -49,36 +60,12 @@ clar_sources += custom_target( clar_unit_tests = executable('unit-tests', sources: clar_sources + clar_test_suites, + c_args: [ + '-DGIT_CLAR_DECLS_H="' + clar_decls_h.full_path() + '"', + ], dependencies: [libgit_commonmain], ) -test('unit-tests', clar_unit_tests) - -unit_test_programs = [ - 'unit-tests/t-reftable-basics.c', - 'unit-tests/t-reftable-block.c', - 'unit-tests/t-reftable-merged.c', - 'unit-tests/t-reftable-pq.c', - 'unit-tests/t-reftable-readwrite.c', - 'unit-tests/t-reftable-record.c', - 'unit-tests/t-reftable-stack.c', - 'unit-tests/t-reftable-table.c', -] - -foreach unit_test_program : unit_test_programs - unit_test_name = fs.stem(unit_test_program) - unit_test = executable(unit_test_name, - sources: [ - 'unit-tests/test-lib.c', - 'unit-tests/lib-reftable.c', - unit_test_program, - ], - dependencies: [libgit_commonmain], - ) - test(unit_test_name, unit_test, - workdir: meson.current_source_dir(), - timeout: 0, - ) -endforeach +test('unit-tests', clar_unit_tests, kwargs: test_kwargs) subdir('helper') @@ -123,7 +110,6 @@ integration_tests = [ 't0060-path-utils.sh', 't0061-run-command.sh', 't0062-revision-walking.sh', - 't0063-string-list.sh', 't0066-dir-iterator.sh', 't0067-parse_pathspec_file.sh', 't0068-for-each-repo.sh', @@ -178,7 +164,6 @@ integration_tests = [ 't1015-read-index-unmerged.sh', 't1016-compatObjectFormat.sh', 't1020-subdirectory.sh', - 't1021-rerere-in-workdir.sh', 't1022-read-tree-partial-clone.sh', 't1050-large.sh', 't1051-large-conversion.sh', @@ -220,10 +205,14 @@ integration_tests = [ 't1418-reflog-exists.sh', 't1419-exclude-refs.sh', 't1420-lost-found.sh', + 't1421-reflog-write.sh', + 't1422-show-ref-exists.sh', 't1430-bad-ref-name.sh', 't1450-fsck.sh', 't1451-fsck-buffer.sh', 't1460-refs-migrate.sh', + 't1461-refs-list.sh', + 't1462-refs-exists.sh', 't1500-rev-parse.sh', 't1501-work-tree.sh', 't1502-rev-parse-parseopt.sh', @@ -246,6 +235,7 @@ integration_tests = [ 't1700-split-index.sh', 't1701-racy-split-index.sh', 't1800-hook.sh', + 't1900-repo.sh', 't2000-conflict-when-checking-files-out.sh', 't2002-checkout-cache-u.sh', 't2003-checkout-cache-mkdir.sh', @@ -502,6 +492,7 @@ integration_tests = [ 't4069-remerge-diff.sh', 't4070-diff-pairs.sh', 't4071-diff-minimal.sh', + 't4072-diff-max-depth.sh', 't4100-apply-stat.sh', 't4101-apply-nonl.sh', 't4102-apply-rename.sh', @@ -962,6 +953,7 @@ integration_tests = [ 't8012-blame-colors.sh', 't8013-blame-ignore-revs.sh', 't8014-blame-ignore-fuzzy.sh', + 't8020-last-modified.sh', 't9001-send-email.sh', 't9002-column.sh', 't9003-help-autocorrect.sh', @@ -1117,6 +1109,7 @@ benchmarks = [ 'perf/p1450-fsck.sh', 'perf/p1451-fsck-skip-list.sh', 'perf/p1500-graph-walks.sh', + 'perf/p1501-rev-parse-oneline.sh', 'perf/p2000-sparse-operations.sh', 'perf/p3400-rebase.sh', 'perf/p3404-rebase-interactive.sh', @@ -1154,6 +1147,7 @@ benchmarks = [ 'perf/p7820-grep-engines.sh', 'perf/p7821-grep-engines-fixed.sh', 'perf/p7822-grep-perl-character.sh', + 'perf/p8020-last-modified.sh', 'perf/p9210-scalar.sh', 'perf/p9300-fast-import-export.sh', ] @@ -1163,8 +1157,6 @@ benchmarks = [ # sufficient to catch missing test suites in our CI though. foreach glob, tests : { 't[0-9][0-9][0-9][0-9]-*.sh': integration_tests, - 'perf/p[0-9][0-9][0-9][0-9]-*.sh': benchmarks, - 'unit-tests/t-*.c': unit_test_programs, 'unit-tests/u-*.c': clar_test_suites, } actual_tests = run_command(shell, '-c', 'ls ' + glob, @@ -1212,7 +1204,7 @@ foreach integration_test : integration_tests workdir: meson.current_source_dir(), env: test_environment, depends: test_dependencies + bin_wrappers, - timeout: 0, + kwargs: test_kwargs, ) endforeach diff --git a/t/perf/p1501-rev-parse-oneline.sh b/t/perf/p1501-rev-parse-oneline.sh new file mode 100755 index 0000000000..538fa9c404 --- /dev/null +++ b/t/perf/p1501-rev-parse-oneline.sh @@ -0,0 +1,71 @@ +#!/bin/sh + +test_description='Test :/ object name notation' + +. ./perf-lib.sh + +test_perf_fresh_repo + +# +# Creates lots of merges to make history traversal costly. In +# particular it creates 2^($max_level-1)-1 2-way merges on top of +# 2^($max_level-1) root commits. E.g., the commit history looks like +# this for a $max_level of 3: +# +# _1_ +# / \ +# 2 3 +# / \ / \ +# 4 5 6 7 +# +# The numbers are the fast-import marks, which also are the commit +# messages. 1 is the HEAD commit and a merge, 2 and 3 are also merges, +# 4-7 are the root commits. +# +build_history () { + local max_level="$1" && + local level="${2:-1}" && + local mark="${3:-1}" && + if test $level -eq $max_level + then + echo "reset refs/heads/master" && + echo "from $ZERO_OID" && + echo "commit refs/heads/master" && + echo "mark :$mark" && + echo "committer C <c@example.com> 1234567890 +0000" && + echo "data <<EOF" && + echo "$mark" && + echo "EOF" + else + local level1=$((level+1)) && + local mark1=$((2*mark)) && + local mark2=$((2*mark+1)) && + build_history $max_level $level1 $mark1 && + build_history $max_level $level1 $mark2 && + echo "commit refs/heads/master" && + echo "mark :$mark" && + echo "committer C <c@example.com> 1234567890 +0000" && + echo "data <<EOF" && + echo "$mark" && + echo "EOF" && + echo "from :$mark1" && + echo "merge :$mark2" + fi +} + +test_expect_success 'setup' ' + build_history 16 | git fast-import && + git log --format="%H %s" --reverse >commits && + sed -n -e "s/ .*$//p" -e "q" <commits >expect && + sed -n -e "s/^.* //p" -e "q" <commits >needle +' + +test_perf "rev-parse :/$(cat needle)" ' + git rev-parse :/$(cat needle) >actual +' + +test_expect_success 'verify result' ' + test_cmp expect actual +' + +test_done diff --git a/t/perf/p5313-pack-objects.sh b/t/perf/p5313-pack-objects.sh index 786a2c1c6f..46a6cd32d2 100755 --- a/t/perf/p5313-pack-objects.sh +++ b/t/perf/p5313-pack-objects.sh @@ -22,46 +22,53 @@ test_expect_success 'create rev input' ' EOF ' -for version in 1 2 -do - export version +test_all_with_args () { + parameter=$1 + export parameter - test_perf "thin pack with version $version" ' + test_perf "thin pack with $parameter" ' git pack-objects --thin --stdout --revs --sparse \ - --name-hash-version=$version <in-thin >out + $parameter <in-thin >out ' - test_size "thin pack size with version $version" ' + test_size "thin pack size with $parameter" ' test_file_size out ' - test_perf "big pack with version $version" ' + test_perf "big pack with $parameter" ' git pack-objects --stdout --revs --sparse \ - --name-hash-version=$version <in-big >out + $parameter <in-big >out ' - test_size "big pack size with version $version" ' + test_size "big pack size with $parameter" ' test_file_size out ' - test_perf "shallow fetch pack with version $version" ' + test_perf "shallow fetch pack with $parameter" ' git pack-objects --stdout --revs --sparse --shallow \ - --name-hash-version=$version <in-shallow >out + $parameter <in-shallow >out ' - test_size "shallow pack size with version $version" ' + test_size "shallow pack size with $parameter" ' test_file_size out ' - test_perf "repack with version $version" ' - git repack -adf --name-hash-version=$version + test_perf "repack with $parameter" ' + git repack -adf $parameter ' - test_size "repack size with version $version" ' + test_size "repack size with $parameter" ' gitdir=$(git rev-parse --git-dir) && pack=$(ls $gitdir/objects/pack/pack-*.pack) && test_file_size "$pack" ' +} + +for version in 1 2 +do + test_all_with_args --name-hash-version=$version done +test_all_with_args --path-walk + test_done diff --git a/t/perf/p8020-last-modified.sh b/t/perf/p8020-last-modified.sh new file mode 100755 index 0000000000..cb1f98d3db --- /dev/null +++ b/t/perf/p8020-last-modified.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +test_description='last-modified perf tests' +. ./perf-lib.sh + +test_perf_default_repo + +test_perf 'top-level last-modified' ' + git last-modified HEAD +' + +test_perf 'top-level recursive last-modified' ' + git last-modified -r HEAD +' + +test_perf 'subdir last-modified' ' + git ls-tree -d HEAD >subtrees && + path="$(head -n 1 subtrees | cut -f2)" && + git last-modified -r HEAD -- "$path" +' + +test_done diff --git a/t/show-ref-exists-tests.sh b/t/show-ref-exists-tests.sh new file mode 100644 index 0000000000..36e8e9df33 --- /dev/null +++ b/t/show-ref-exists-tests.sh @@ -0,0 +1,77 @@ +git_show_ref_exists=${git_show_ref_exists:-git show-ref --exists} + +test_expect_success setup ' + test_commit --annotate A && + git checkout -b side && + test_commit --annotate B && + git checkout main && + test_commit C && + git branch B A^0 +' + +test_expect_success '--exists with existing reference' ' + ${git_show_ref_exists} refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME +' + +test_expect_success '--exists with missing reference' ' + test_expect_code 2 ${git_show_ref_exists} refs/heads/does-not-exist +' + +test_expect_success '--exists does not use DWIM' ' + test_expect_code 2 ${git_show_ref_exists} $GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 2>err && + grep "reference does not exist" err +' + +test_expect_success '--exists with HEAD' ' + ${git_show_ref_exists} HEAD +' + +test_expect_success '--exists with bad reference name' ' + test_when_finished "git update-ref -d refs/heads/bad...name" && + new_oid=$(git rev-parse HEAD) && + test-tool ref-store main update-ref msg refs/heads/bad...name $new_oid $ZERO_OID REF_SKIP_REFNAME_VERIFICATION && + ${git_show_ref_exists} refs/heads/bad...name +' + +test_expect_success '--exists with arbitrary symref' ' + test_when_finished "git symbolic-ref -d refs/symref" && + git symbolic-ref refs/symref refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME && + ${git_show_ref_exists} refs/symref +' + +test_expect_success '--exists with dangling symref' ' + test_when_finished "git symbolic-ref -d refs/heads/dangling" && + git symbolic-ref refs/heads/dangling refs/heads/does-not-exist && + ${git_show_ref_exists} refs/heads/dangling +' + +test_expect_success '--exists with nonexistent object ID' ' + test-tool ref-store main update-ref msg refs/heads/missing-oid $(test_oid 001) $ZERO_OID REF_SKIP_OID_VERIFICATION && + ${git_show_ref_exists} refs/heads/missing-oid +' + +test_expect_success '--exists with non-commit object' ' + tree_oid=$(git rev-parse HEAD^{tree}) && + test-tool ref-store main update-ref msg refs/heads/tree ${tree_oid} $ZERO_OID REF_SKIP_OID_VERIFICATION && + ${git_show_ref_exists} refs/heads/tree +' + +test_expect_success '--exists with directory fails with generic error' ' + cat >expect <<-EOF && + error: reference does not exist + EOF + test_expect_code 2 ${git_show_ref_exists} refs/heads 2>err && + test_cmp expect err +' + +test_expect_success '--exists with non-existent special ref' ' + test_expect_code 2 ${git_show_ref_exists} FETCH_HEAD +' + +test_expect_success '--exists with existing special ref' ' + test_when_finished "rm .git/FETCH_HEAD" && + git rev-parse HEAD >.git/FETCH_HEAD && + ${git_show_ref_exists} FETCH_HEAD +' + +test_done diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 35c5c2b4f9..2b63e1c86c 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -130,7 +130,7 @@ test_expect_success 'subtest: a failing TODO test' ' ' test_expect_success 'subtest: a passing TODO test' ' - write_and_run_sub_test_lib_test passing-todo <<-\EOF && + write_and_run_sub_test_lib_test_err passing-todo <<-\EOF && test_expect_failure "pretend we have fixed a known breakage" "true" test_done EOF @@ -142,7 +142,7 @@ test_expect_success 'subtest: a passing TODO test' ' ' test_expect_success 'subtest: 2 TODO tests, one passin' ' - write_and_run_sub_test_lib_test partially-passing-todos <<-\EOF && + write_and_run_sub_test_lib_test_err partially-passing-todos <<-\EOF && test_expect_failure "pretend we have a known breakage" "false" test_expect_success "pretend we have a passing test" "true" test_expect_failure "pretend we have fixed another known breakage" "true" @@ -219,41 +219,44 @@ test_expect_success 'subtest: --verbose option' ' test_expect_success "failing test" false test_done EOF - mv t1234-verbose/out t1234-verbose/out+ && - grep -v "^Initialized empty" t1234-verbose/out+ >t1234-verbose/out && - check_sub_test_lib_test t1234-verbose <<-\EOF - > expecting success of 1234.1 '\''passing test'\'': true + mv t1234-verbose/err t1234-verbose/err+ && + grep -v "^Initialized empty" t1234-verbose/err+ >t1234-verbose/err && + check_sub_test_lib_test_err t1234-verbose \ + <<-\EOF_OUT 3<<-\EOF_ERR > ok 1 - passing test + > ok 2 - test with output + > not ok 3 - failing test + > # false + > # failed 1 among 3 test(s) + > 1..3 + EOF_OUT + > expecting success of 1234.1 '\''passing test'\'': true > Z > expecting success of 1234.2 '\''test with output'\'': echo foo > foo - > ok 2 - test with output > Z > expecting success of 1234.3 '\''failing test'\'': false - > not ok 3 - failing test - > # false > Z - > # failed 1 among 3 test(s) - > 1..3 - EOF + EOF_ERR ' test_expect_success 'subtest: --verbose-only option' ' run_sub_test_lib_test_err \ t1234-verbose \ --verbose-only=2 && - check_sub_test_lib_test t1234-verbose <<-\EOF + check_sub_test_lib_test_err t1234-verbose <<-\EOF_OUT 3<<-\EOF_ERR > ok 1 - passing test - > Z - > expecting success of 1234.2 '\''test with output'\'': echo foo - > foo > ok 2 - test with output - > Z > not ok 3 - failing test > # false > # failed 1 among 3 test(s) > 1..3 - EOF + EOF_OUT + > Z + > expecting success of 1234.2 '\''test with output'\'': echo foo + > foo + > Z + EOF_ERR ' test_expect_success 'subtest: skip one with GIT_SKIP_TESTS' ' diff --git a/t/t0001-init.sh b/t/t0001-init.sh index f11a40811f..618da080dc 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -658,6 +658,17 @@ test_expect_success 'init warns about invalid init.defaultRefFormat' ' test_cmp expected actual ' +test_expect_success 'default ref format' ' + test_when_finished "rm -rf refformat" && + ( + sane_unset GIT_DEFAULT_REF_FORMAT && + git init refformat + ) && + git version --build-options | sed -ne "s/^default-ref-format: //p" >expect && + git -C refformat rev-parse --show-ref-format >actual && + test_cmp expect actual +' + backends="files reftable" for format in $backends do @@ -738,6 +749,40 @@ test_expect_success "GIT_DEFAULT_REF_FORMAT= overrides init.defaultRefFormat" ' test_cmp expect actual ' +test_expect_success "init with feature.experimental=true" ' + test_when_finished "rm -rf refformat" && + test_config_global feature.experimental true && + ( + sane_unset GIT_DEFAULT_REF_FORMAT && + git init refformat + ) && + echo reftable >expect && + git -C refformat rev-parse --show-ref-format >actual && + test_cmp expect actual +' + +test_expect_success "init.defaultRefFormat overrides feature.experimental=true" ' + test_when_finished "rm -rf refformat" && + test_config_global feature.experimental true && + test_config_global init.defaultRefFormat files && + ( + sane_unset GIT_DEFAULT_REF_FORMAT && + git init refformat + ) && + echo files >expect && + git -C refformat rev-parse --show-ref-format >actual && + test_cmp expect actual +' + +test_expect_success "GIT_DEFAULT_REF_FORMAT= overrides feature.experimental=true" ' + test_when_finished "rm -rf refformat" && + test_config_global feature.experimental true && + GIT_DEFAULT_REF_FORMAT=files git init refformat && + echo files >expect && + git -C refformat rev-parse --show-ref-format >actual && + test_cmp expect actual +' + for from_format in $backends do test_expect_success "re-init with same format ($from_format)" ' @@ -838,6 +883,22 @@ test_expect_success 'advice on unconfigured init.defaultBranch disabled' ' test_grep ! "hint: " err ' +test_expect_success 'default branch name' ' + if test_have_prereq WITH_BREAKING_CHANGES + then + expect=main + else + expect=master + fi && + echo "refs/heads/$expect" >expect && + ( + sane_unset GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME && + git init default-initial-branch-name + ) && + git -C default-initial-branch-name symbolic-ref HEAD >actual && + test_cmp expect actual +' + test_expect_success 'overridden default main branch name (env)' ' test_config_global init.defaultBranch nmb && GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=env git init main-branch-env && diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index bf10d253ec..f0d50d769e 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh @@ -281,7 +281,7 @@ test_expect_success 'required filter with absent smudge field' ' test_expect_success 'filtering large input to small output should use little memory' ' test_config filter.devnull.clean "cat >/dev/null" && test_config filter.devnull.required true && - for i in $(test_seq 1 30); do printf "%1048576d" 1 || return 1; done >30MB && + test_seq -f "%1048576d" 1 30 >30MB && echo "30MB filter=devnull" >.gitattributes && GIT_MMAP_LIMIT=1m GIT_ALLOC_LIMIT=1m git add 30MB ' @@ -299,7 +299,7 @@ test_expect_success 'filter that does not read is fine' ' test_expect_success EXPENSIVE 'filter large file' ' test_config filter.largefile.smudge cat && test_config filter.largefile.clean cat && - for i in $(test_seq 1 2048); do printf "%1048576d" 1 || return 1; done >2GB && + test_seq -f "%1048576d" 1 2048 >2GB && echo "2GB filter=largefile" >.gitattributes && git add 2GB 2>err && test_must_be_empty err && diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh index 5c9dc90d0b..ca8568067d 100755 --- a/t/t0050-filesystem.sh +++ b/t/t0050-filesystem.sh @@ -10,53 +10,35 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME auml=$(printf '\303\244') aumlcdiar=$(printf '\141\314\210') -if test_have_prereq CASE_INSENSITIVE_FS -then - say "will test on a case insensitive filesystem" - test_case=test_expect_failure -else - test_case=test_expect_success -fi - if test_have_prereq UTF8_NFD_TO_NFC then - say "will test on a unicode corrupting filesystem" test_unicode=test_expect_failure else test_unicode=test_expect_success fi -test_have_prereq SYMLINKS || - say "will test on a filesystem lacking symbolic links" - -if test_have_prereq CASE_INSENSITIVE_FS -then -test_expect_success "detection of case insensitive filesystem during repo init" ' +test_expect_success CASE_INSENSITIVE_FS "detection of case insensitive filesystem during repo init" ' test $(git config --bool core.ignorecase) = true ' -else -test_expect_success "detection of case insensitive filesystem during repo init" ' + +test_expect_success !CASE_INSENSITIVE_FS "detection of case insensitive filesystem during repo init" ' { test_must_fail git config --bool core.ignorecase >/dev/null || test $(git config --bool core.ignorecase) = false } ' -fi -if test_have_prereq SYMLINKS -then -test_expect_success "detection of filesystem w/o symlink support during repo init" ' +test_expect_success SYMLINKS "detection of filesystem w/o symlink support during repo init" ' { test_must_fail git config --bool core.symlinks || test "$(git config --bool core.symlinks)" = true } ' -else -test_expect_success "detection of filesystem w/o symlink support during repo init" ' + +test_expect_success !SYMLINKS "detection of filesystem w/o symlink support during repo init" ' v=$(git config --bool core.symlinks) && test "$v" = false ' -fi test_expect_success "setup case tests" ' git config core.ignorecase true && diff --git a/t/t0063-string-list.sh b/t/t0063-string-list.sh deleted file mode 100755 index aac63ba506..0000000000 --- a/t/t0063-string-list.sh +++ /dev/null @@ -1,142 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2012 Michael Haggerty -# - -test_description='Test string list functionality' - -. ./test-lib.sh - -test_split () { - cat >expected && - test_expect_success "split $1 at $2, max $3" " - test-tool string-list split '$1' '$2' '$3' >actual && - test_cmp expected actual && - test-tool string-list split_in_place '$1' '$2' '$3' >actual && - test_cmp expected actual - " -} - -test_split_in_place() { - cat >expected && - test_expect_success "split (in place) $1 at $2, max $3" " - test-tool string-list split_in_place '$1' '$2' '$3' >actual && - test_cmp expected actual - " -} - -test_split "foo:bar:baz" ":" "-1" <<EOF -3 -[0]: "foo" -[1]: "bar" -[2]: "baz" -EOF - -test_split "foo:bar:baz" ":" "0" <<EOF -1 -[0]: "foo:bar:baz" -EOF - -test_split "foo:bar:baz" ":" "1" <<EOF -2 -[0]: "foo" -[1]: "bar:baz" -EOF - -test_split "foo:bar:baz" ":" "2" <<EOF -3 -[0]: "foo" -[1]: "bar" -[2]: "baz" -EOF - -test_split "foo:bar:" ":" "-1" <<EOF -3 -[0]: "foo" -[1]: "bar" -[2]: "" -EOF - -test_split "" ":" "-1" <<EOF -1 -[0]: "" -EOF - -test_split ":" ":" "-1" <<EOF -2 -[0]: "" -[1]: "" -EOF - -test_split_in_place "foo:;:bar:;:baz:;:" ":;" "-1" <<EOF -10 -[0]: "foo" -[1]: "" -[2]: "" -[3]: "bar" -[4]: "" -[5]: "" -[6]: "baz" -[7]: "" -[8]: "" -[9]: "" -EOF - -test_split_in_place "foo:;:bar:;:baz" ":;" "0" <<EOF -1 -[0]: "foo:;:bar:;:baz" -EOF - -test_split_in_place "foo:;:bar:;:baz" ":;" "1" <<EOF -2 -[0]: "foo" -[1]: ";:bar:;:baz" -EOF - -test_split_in_place "foo:;:bar:;:baz" ":;" "2" <<EOF -3 -[0]: "foo" -[1]: "" -[2]: ":bar:;:baz" -EOF - -test_split_in_place "foo:;:bar:;:" ":;" "-1" <<EOF -7 -[0]: "foo" -[1]: "" -[2]: "" -[3]: "bar" -[4]: "" -[5]: "" -[6]: "" -EOF - -test_expect_success "test filter_string_list" ' - test "x-" = "x$(test-tool string-list filter - y)" && - test "x-" = "x$(test-tool string-list filter no y)" && - test yes = "$(test-tool string-list filter yes y)" && - test yes = "$(test-tool string-list filter no:yes y)" && - test yes = "$(test-tool string-list filter yes:no y)" && - test y1:y2 = "$(test-tool string-list filter y1:y2 y)" && - test y2:y1 = "$(test-tool string-list filter y2:y1 y)" && - test "x-" = "x$(test-tool string-list filter x1:x2 y)" -' - -test_expect_success "test remove_duplicates" ' - test "x-" = "x$(test-tool string-list remove_duplicates -)" && - test "x" = "x$(test-tool string-list remove_duplicates "")" && - test a = "$(test-tool string-list remove_duplicates a)" && - test a = "$(test-tool string-list remove_duplicates a:a)" && - test a = "$(test-tool string-list remove_duplicates a:a:a:a:a)" && - test a:b = "$(test-tool string-list remove_duplicates a:b)" && - test a:b = "$(test-tool string-list remove_duplicates a:a:b)" && - test a:b = "$(test-tool string-list remove_duplicates a:b:b)" && - test a:b:c = "$(test-tool string-list remove_duplicates a:b:c)" && - test a:b:c = "$(test-tool string-list remove_duplicates a:a:b:c)" && - test a:b:c = "$(test-tool string-list remove_duplicates a:b:b:c)" && - test a:b:c = "$(test-tool string-list remove_duplicates a:b:c:c)" && - test a:b:c = "$(test-tool string-list remove_duplicates a:a:b:b:c:c)" && - test a:b:c = "$(test-tool string-list remove_duplicates a:a:a:b:b:b:c:c:c)" -' - -test_done diff --git a/t/t0411-clone-from-partial.sh b/t/t0411-clone-from-partial.sh index 196fc61784..9e6bca5625 100755 --- a/t/t0411-clone-from-partial.sh +++ b/t/t0411-clone-from-partial.sh @@ -59,6 +59,12 @@ test_expect_success 'pack-objects should fetch from promisor remote and execute test_expect_success 'clone from promisor remote does not lazy-fetch by default' ' rm -f script-executed && + + # The --path-walk feature of "git pack-objects" is not + # compatible with this kind of fetch from an incomplete repo. + GIT_TEST_PACK_PATH_WALK=0 && + export GIT_TEST_PACK_PATH_WALK && + test_must_fail git clone evil no-lazy 2>err && test_grep "lazy fetching disabled" err && test_path_is_missing script-executed diff --git a/t/t0450-txt-doc-vs-help.sh b/t/t0450-txt-doc-vs-help.sh index 2f7504ae7e..e12e18f97f 100755 --- a/t/t0450-txt-doc-vs-help.sh +++ b/t/t0450-txt-doc-vs-help.sh @@ -41,7 +41,7 @@ help_to_synopsis () { } builtin_to_adoc () { - echo "$GIT_BUILD_DIR/Documentation/git-$1.adoc" + echo "$GIT_SOURCE_DIR/Documentation/git-$1.adoc" } adoc_to_synopsis () { @@ -112,10 +112,19 @@ do adoc="$(builtin_to_adoc "$builtin")" && preq="$(echo BUILTIN_ADOC_$builtin | tr '[:lower:]-' '[:upper:]_')" && - if test -f "$adoc" + # If and only if *.adoc is missing, builtin shall be listed in t0450/adoc-missing. + if grep -q "^$builtin$" "$TEST_DIRECTORY"/t0450/adoc-missing then + test_expect_success "$builtin appropriately marked as not having .adoc" ' + ! test -f "$adoc" + ' + else test_set_prereq "$preq" - fi && + + test_expect_success "$builtin appropriately marked as having .adoc" ' + test -f "$adoc" + ' + fi # *.adoc output assertions test_expect_success "$preq" "$builtin *.adoc SYNOPSIS has dashed labels" ' diff --git a/t/t0450/adoc-help-mismatches b/t/t0450/adoc-help-mismatches index c4a15fd0cb..2c6ecd5fc8 100644 --- a/t/t0450/adoc-help-mismatches +++ b/t/t0450/adoc-help-mismatches @@ -17,7 +17,6 @@ fast-export fast-import fetch-pack fmt-merge-msg -for-each-ref format-patch fsck-objects fsmonitor--daemon @@ -38,7 +37,6 @@ merge-one-file multi-pack-index name-rev notes -pack-objects push range-diff rebase diff --git a/t/t0450/adoc-missing b/t/t0450/adoc-missing new file mode 100644 index 0000000000..1ec9f8dcf3 --- /dev/null +++ b/t/t0450/adoc-missing @@ -0,0 +1,9 @@ +checkout--worker +merge-ours +merge-recursive +merge-recursive-ours +merge-recursive-theirs +merge-subtree +pickaxe +submodule--helper +upload-archive--writer diff --git a/t/t0610-reftable-basics.sh b/t/t0610-reftable-basics.sh index 1be534a895..3ea5d51532 100755 --- a/t/t0610-reftable-basics.sh +++ b/t/t0610-reftable-basics.sh @@ -477,11 +477,7 @@ test_expect_success !CYGWIN 'ref transaction: many concurrent writers' ' test_commit --no-tag initial && head=$(git rev-parse HEAD) && - for i in $(test_seq 100) - do - printf "%s commit\trefs/heads/branch-%s\n" "$head" "$i" || - return 1 - done >expect && + test_seq -f "$head commit\trefs/heads/branch-%d" 100 >expect && printf "%s commit\trefs/heads/main\n" "$head" >>expect && for i in $(test_seq 100) diff --git a/t/t0612-reftable-jgit-compatibility.sh b/t/t0612-reftable-jgit-compatibility.sh index d0d7e80b49..7df2ad5817 100755 --- a/t/t0612-reftable-jgit-compatibility.sh +++ b/t/t0612-reftable-jgit-compatibility.sh @@ -112,14 +112,11 @@ test_expect_success 'JGit can read multi-level index' ' cd repo && test_commit A && - awk " - BEGIN { - print \"start\"; - for (i = 0; i < 10000; i++) - printf \"create refs/heads/branch-%d HEAD\n\", i; - print \"commit\"; - } - " >input && + { + echo start && + test_seq -f "create refs/heads/branch-%d HEAD" 10000 && + echo commit + } >input && git update-ref --stdin <input && test_same_refs && diff --git a/t/t0613-reftable-write-options.sh b/t/t0613-reftable-write-options.sh index 6447920c9b..e334751759 100755 --- a/t/t0613-reftable-write-options.sh +++ b/t/t0613-reftable-write-options.sh @@ -11,16 +11,18 @@ export GIT_TEST_REFTABLE_AUTOCOMPACTION # Block sizes depend on the hash function, so we force SHA1 here. GIT_TEST_DEFAULT_HASH=sha1 export GIT_TEST_DEFAULT_HASH -# Block sizes also depend on the actual refs we write, so we force "master" to -# be the default initial branch name. -GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master -export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME . ./test-lib.sh +# Block sizes depend on the actual refs we write, so, for tests +# that check block size, we force the initial branch name to be "master". +init_repo () { + git init --initial-branch master repo +} + test_expect_success 'default write options' ' test_when_finished "rm -rf repo" && - git init repo && + init_repo && ( cd repo && test_commit initial && @@ -43,7 +45,7 @@ test_expect_success 'default write options' ' test_expect_success 'disabled reflog writes no log blocks' ' test_config_global core.logAllRefUpdates false && test_when_finished "rm -rf repo" && - git init repo && + init_repo && ( cd repo && test_commit initial && @@ -62,15 +64,11 @@ test_expect_success 'disabled reflog writes no log blocks' ' test_expect_success 'many refs results in multiple blocks' ' test_when_finished "rm -rf repo" && - git init repo && + init_repo && ( cd repo && test_commit initial && - for i in $(test_seq 200) - do - printf "update refs/heads/branch-%d HEAD\n" "$i" || - return 1 - done >input && + test_seq -f "update refs/heads/branch-%d HEAD" 200 >input && git update-ref --stdin <input && git pack-refs && @@ -119,7 +117,7 @@ test_expect_success 'tiny block size leads to error' ' test_expect_success 'small block size leads to multiple ref blocks' ' test_config_global core.logAllRefUpdates false && test_when_finished "rm -rf repo" && - git init repo && + init_repo && ( cd repo && test_commit A && @@ -176,15 +174,11 @@ test_expect_success 'block size exceeding maximum supported size' ' test_expect_success 'restart interval at every single record' ' test_when_finished "rm -rf repo" && - git init repo && + init_repo && ( cd repo && test_commit initial && - for i in $(test_seq 10) - do - printf "update refs/heads/branch-%d HEAD\n" "$i" || - return 1 - done >input && + test_seq -f "update refs/heads/branch-%d HEAD" 10 >input && git update-ref --stdin <input && git -c reftable.restartInterval=1 pack-refs && @@ -220,15 +214,11 @@ test_expect_success 'restart interval exceeding maximum supported interval' ' test_expect_success 'object index gets written by default with ref index' ' test_config_global core.logAllRefUpdates false && test_when_finished "rm -rf repo" && - git init repo && + init_repo && ( cd repo && test_commit initial && - for i in $(test_seq 5) - do - printf "update refs/heads/branch-%d HEAD\n" "$i" || - return 1 - done >input && + test_seq -f "update refs/heads/branch-%d HEAD" 5 >input && git update-ref --stdin <input && git -c reftable.blockSize=100 pack-refs && @@ -259,15 +249,11 @@ test_expect_success 'object index gets written by default with ref index' ' test_expect_success 'object index can be disabled' ' test_config_global core.logAllRefUpdates false && test_when_finished "rm -rf repo" && - git init repo && + init_repo && ( cd repo && test_commit initial && - for i in $(test_seq 5) - do - printf "update refs/heads/branch-%d HEAD\n" "$i" || - return 1 - done >input && + test_seq -f "update refs/heads/branch-%d HEAD" 5 >input && git update-ref --stdin <input && git -c reftable.blockSize=100 -c reftable.indexObjects=false pack-refs && diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh index 317da6869c..1f61b666a7 100755 --- a/t/t1006-cat-file.sh +++ b/t/t1006-cat-file.sh @@ -113,53 +113,55 @@ strlen () { run_tests () { type=$1 - oid=$2 - size=$3 - content=$4 - pretty_content=$5 + object_name="$2" + mode=$3 + size=$4 + content=$5 + pretty_content=$6 + oid=${7:-"$object_name"} batch_output="$oid $type $size $content" test_expect_success "$type exists" ' - git cat-file -e $oid + git cat-file -e "$object_name" ' test_expect_success "Type of $type is correct" ' echo $type >expect && - git cat-file -t $oid >actual && + git cat-file -t "$object_name" >actual && test_cmp expect actual ' test_expect_success "Size of $type is correct" ' echo $size >expect && - git cat-file -s $oid >actual && + git cat-file -s "$object_name" >actual && test_cmp expect actual ' test -z "$content" || test_expect_success "Content of $type is correct" ' echo_without_newline "$content" >expect && - git cat-file $type $oid >actual && + git cat-file $type "$object_name" >actual && test_cmp expect actual ' test_expect_success "Pretty content of $type is correct" ' echo_without_newline "$pretty_content" >expect && - git cat-file -p $oid >actual && + git cat-file -p "$object_name" >actual && test_cmp expect actual ' test -z "$content" || test_expect_success "--batch output of $type is correct" ' echo "$batch_output" >expect && - echo $oid | git cat-file --batch >actual && + echo "$object_name" | git cat-file --batch >actual && test_cmp expect actual ' test_expect_success "--batch-check output of $type is correct" ' echo "$oid $type $size" >expect && - echo_without_newline $oid | git cat-file --batch-check >actual && + echo_without_newline "$object_name" | git cat-file --batch-check >actual && test_cmp expect actual ' @@ -168,13 +170,13 @@ $content" test -z "$content" || test_expect_success "--batch-command $opt output of $type content is correct" ' echo "$batch_output" >expect && - test_write_lines "contents $oid" | git cat-file --batch-command $opt >actual && + test_write_lines "contents $object_name" | git cat-file --batch-command $opt >actual && test_cmp expect actual ' test_expect_success "--batch-command $opt output of $type info is correct" ' echo "$oid $type $size" >expect && - test_write_lines "info $oid" | + test_write_lines "info $object_name" | git cat-file --batch-command $opt >actual && test_cmp expect actual ' @@ -182,30 +184,45 @@ $content" test_expect_success "custom --batch-check format" ' echo "$type $oid" >expect && - echo $oid | git cat-file --batch-check="%(objecttype) %(objectname)" >actual && + echo "$object_name" | git cat-file --batch-check="%(objecttype) %(objectname)" >actual && test_cmp expect actual ' test_expect_success "custom --batch-command format" ' echo "$type $oid" >expect && - echo "info $oid" | git cat-file --batch-command="%(objecttype) %(objectname)" >actual && + echo "info $object_name" | git cat-file --batch-command="%(objecttype) %(objectname)" >actual && test_cmp expect actual ' - test_expect_success '--batch-check with %(rest)' ' + # FIXME: %(rest) is incompatible with object names that include whitespace, + # e.g. HEAD:path/to/a/file with spaces. Use the resolved OID as input to + # test this instead of the raw object name. + if echo "$object_name" | grep -q " "; then + test_rest=test_expect_failure + else + test_rest=test_expect_success + fi + + $test_rest '--batch-check with %(rest)' ' echo "$type this is some extra content" >expect && - echo "$oid this is some extra content" | + echo "$object_name this is some extra content" | git cat-file --batch-check="%(objecttype) %(rest)" >actual && test_cmp expect actual ' + test_expect_success '--batch-check with %(objectmode)' ' + echo "$mode $oid" >expect && + echo $object_name | git cat-file --batch-check="%(objectmode) %(objectname)" >actual && + test_cmp expect actual + ' + test -z "$content" || test_expect_success "--batch without type ($type)" ' { echo "$size" && echo "$content" } >expect && - echo $oid | git cat-file --batch="%(objectsize)" >actual && + echo "$object_name" | git cat-file --batch="%(objectsize)" >actual && test_cmp expect actual ' @@ -215,7 +232,7 @@ $content" echo "$type" && echo "$content" } >expect && - echo $oid | git cat-file --batch="%(objecttype)" >actual && + echo "$object_name" | git cat-file --batch="%(objecttype)" >actual && test_cmp expect actual ' } @@ -230,13 +247,14 @@ test_expect_success "setup" ' git config extensions.compatobjectformat $test_compat_hash_algo && echo_without_newline "$hello_content" > hello && git update-index --add hello && + echo_without_newline "$hello_content" > "path with spaces" && + git update-index --add --chmod=+x "path with spaces" && git commit -m "add hello file" ' run_blob_tests () { oid=$1 - - run_tests 'blob' $oid $hello_size "$hello_content" "$hello_content" + run_tests 'blob' $oid "" $hello_size "$hello_content" "$hello_content" test_expect_success '--batch-command --buffer with flush for blob info' ' echo "$oid blob $hello_size" >expect && @@ -269,13 +287,17 @@ test_expect_success '--batch-check without %(rest) considers whole line' ' tree_oid=$(git write-tree) tree_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tree_oid) -tree_size=$(($(test_oid rawsz) + 13)) -tree_compat_size=$(($(test_oid --hash=compat rawsz) + 13)) -tree_pretty_content="100644 blob $hello_oid hello${LF}" -tree_compat_pretty_content="100644 blob $hello_compat_oid hello${LF}" - -run_tests 'tree' $tree_oid $tree_size "" "$tree_pretty_content" -run_tests 'tree' $tree_compat_oid $tree_compat_size "" "$tree_compat_pretty_content" +tree_size=$((2 * $(test_oid rawsz) + 13 + 24)) +tree_compat_size=$((2 * $(test_oid --hash=compat rawsz) + 13 + 24)) +tree_pretty_content="100644 blob $hello_oid hello${LF}100755 blob $hello_oid path with spaces${LF}" +tree_compat_pretty_content="100644 blob $hello_compat_oid hello${LF}100755 blob $hello_compat_oid path with spaces${LF}" + +run_tests 'tree' $tree_oid "" $tree_size "" "$tree_pretty_content" +run_tests 'tree' $tree_compat_oid "" $tree_compat_size "" "$tree_compat_pretty_content" +run_tests 'blob' "$tree_oid:hello" "100644" $hello_size "" "$hello_content" $hello_oid +run_tests 'blob' "$tree_compat_oid:hello" "100644" $hello_size "" "$hello_content" $hello_compat_oid +run_tests 'blob' "$tree_oid:path with spaces" "100755" $hello_size "" "$hello_content" $hello_oid +run_tests 'blob' "$tree_compat_oid:path with spaces" "100755" $hello_size "" "$hello_content" $hello_compat_oid commit_message="Initial commit" commit_oid=$(echo_without_newline "$commit_message" | git commit-tree $tree_oid) @@ -294,8 +316,8 @@ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE $commit_message" -run_tests 'commit' $commit_oid $commit_size "$commit_content" "$commit_content" -run_tests 'commit' $commit_compat_oid $commit_compat_size "$commit_compat_content" "$commit_compat_content" +run_tests 'commit' $commit_oid "" $commit_size "$commit_content" "$commit_content" +run_tests 'commit' $commit_compat_oid "" $commit_compat_size "$commit_compat_content" "$commit_compat_content" tag_header_without_oid="type blob tag hellotag @@ -318,8 +340,8 @@ tag_size=$(strlen "$tag_content") tag_compat_oid=$(git rev-parse --output-object-format=$test_compat_hash_algo $tag_oid) tag_compat_size=$(strlen "$tag_compat_content") -run_tests 'tag' $tag_oid $tag_size "$tag_content" "$tag_content" -run_tests 'tag' $tag_compat_oid $tag_compat_size "$tag_compat_content" "$tag_compat_content" +run_tests 'tag' $tag_oid "" $tag_size "$tag_content" "$tag_content" +run_tests 'tag' $tag_compat_oid "" $tag_compat_size "$tag_compat_content" "$tag_compat_content" test_expect_success "Reach a blob from a tag pointing to it" ' echo_without_newline "$hello_content" >expect && @@ -1198,6 +1220,31 @@ test_expect_success 'cat-file --batch-check respects replace objects' ' test_cmp expect actual ' +test_expect_success 'batch-check with a submodule' ' + # FIXME: this call to mktree is incompatible with compatObjectFormat + # because the submodule OID cannot be mapped to the compat hash algo. + test_unconfig extensions.compatobjectformat && + printf "160000 commit $(test_oid deadbeef)\tsub\n" >tree-with-sub && + tree=$(git mktree <tree-with-sub) && + test_config extensions.compatobjectformat $test_compat_hash_algo && + + git cat-file --batch-check >actual <<-EOF && + $tree:sub + EOF + printf "$(test_oid deadbeef) submodule\n" >expect && + test_cmp expect actual +' + +test_expect_success 'batch-check with a submodule, object exists' ' + printf "160000 commit $commit_oid\tsub\n" >tree-with-sub && + tree=$(git mktree <tree-with-sub) && + git cat-file --batch-check >actual <<-EOF && + $tree:sub + EOF + printf "$commit_oid commit $commit_size\n" >expect && + test_cmp expect actual +' + # Pull the entry for object with oid "$1" out of the output of # "cat-file --batch", including its object content (which requires # parsing and reading a set amount of bytes, hence perl). diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh index dbbe9fb0d4..de076293b6 100755 --- a/t/t1007-hash-object.sh +++ b/t/t1007-hash-object.sh @@ -30,7 +30,7 @@ setup_repo() { test_repo=test push_repo() { - test_create_repo $test_repo + git init --quiet $test_repo cd $test_repo setup_repo @@ -252,9 +252,9 @@ test_expect_success '--literally complains about non-standard types' ' test_must_fail git hash-object -t bogus --literally --stdin ' -test_expect_success '--stdin outside of repository (uses SHA-1)' ' +test_expect_success '--stdin outside of repository (uses default hash)' ' nongit git hash-object --stdin <hello >actual && - echo "$(test_oid --hash=sha1 hello)" >expect && + echo "$(test_oid --hash=builtin hello)" >expect && test_cmp expect actual ' diff --git a/t/t1021-rerere-in-workdir.sh b/t/t1021-rerere-in-workdir.sh deleted file mode 100755 index 0b892894eb..0000000000 --- a/t/t1021-rerere-in-workdir.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/sh - -test_description='rerere run in a workdir' -GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main -export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME - -. ./test-lib.sh - -test_expect_success SYMLINKS setup ' - git config rerere.enabled true && - >world && - git add world && - test_tick && - git commit -m initial && - - echo hello >world && - test_tick && - git commit -a -m hello && - - git checkout -b side HEAD^ && - echo goodbye >world && - test_tick && - git commit -a -m goodbye && - - git checkout main -' - -test_expect_success SYMLINKS 'rerere in workdir' ' - rm -rf .git/rr-cache && - "$SHELL_PATH" "$TEST_DIRECTORY/../contrib/workdir/git-new-workdir" . work && - ( - cd work && - test_must_fail git merge side && - git rerere status >actual && - echo world >expect && - test_cmp expect actual - ) -' - -# This fails because we don't resolve relative symlink in mkdir_in_gitdir() -# For the purpose of helping contrib/workdir/git-new-workdir users, we do not -# have to support relative symlinks, but it might be nicer to make this work -# with a relative symbolic link someday. -test_expect_failure SYMLINKS 'rerere in workdir (relative)' ' - rm -rf .git/rr-cache && - "$SHELL_PATH" "$TEST_DIRECTORY/../contrib/workdir/git-new-workdir" . krow && - ( - cd krow && - rm -f .git/rr-cache && - ln -s ../.git/rr-cache .git/rr-cache && - test_must_fail git merge side && - git rerere status >actual && - echo world >expect && - test_cmp expect actual - ) -' - -test_done diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh index d8101139b4..b0f691c151 100755 --- a/t/t1092-sparse-checkout-compatibility.sh +++ b/t/t1092-sparse-checkout-compatibility.sh @@ -1506,6 +1506,8 @@ test_expect_success 'sparse-index is not expanded' ' ensure_not_expanded reset --hard && ensure_not_expanded restore -s rename-out-to-out -- deep/deeper1 && + ensure_not_expanded ls-files deep/deeper1 && + echo >>sparse-index/README.md && ensure_not_expanded add -A && echo >>sparse-index/extra.txt && @@ -1607,6 +1609,17 @@ test_expect_success 'describe tested on all' ' test_all_match git describe --dirty ' +test_expect_success 'ls-files filtering and expansion' ' + init_repos && + + # This filtering will hit a sparse directory midway + # through the iteration. + test_all_match git ls-files deep && + + # This pathspec will filter the index to only a sparse + # directory. + test_all_match git ls-files folder1 +' test_expect_success 'sparse-index is not expanded: describe' ' init_repos && diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index d29d23cb89..b7415ec9d5 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -1380,10 +1380,7 @@ test_expect_success 'fails with duplicate ref update via symref' ' test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction creating branches does not burst open file limit' ' ( - for i in $(test_seq 33) - do - echo "create refs/heads/$i HEAD" || exit 1 - done >large_input && + test_seq -f "create refs/heads/%d HEAD" 33 >large_input && run_with_limited_open_files git update-ref --stdin <large_input && git rev-parse --verify -q refs/heads/33 ) @@ -1391,10 +1388,7 @@ test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction creating branches test_expect_success ULIMIT_FILE_DESCRIPTORS 'large transaction deleting branches does not burst open file limit' ' ( - for i in $(test_seq 33) - do - echo "delete refs/heads/$i HEAD" || exit 1 - done >large_input && + test_seq -f "delete refs/heads/%d HEAD" 33 >large_input && run_with_limited_open_files git update-ref --stdin <large_input && test_must_fail git rev-parse --verify -q refs/heads/33 ) @@ -2299,6 +2293,51 @@ do test_grep -q "refname conflict" stdout ) ' + + test_expect_success "stdin $type batch-updates delete incorrect symbolic ref" ' + git init repo && + test_when_finished "rm -fr repo" && + ( + cd repo && + test_commit c1 && + head=$(git rev-parse HEAD) && + git symbolic-ref refs/heads/symbolic refs/heads/non-existent && + + format_command $type "delete refs/heads/symbolic" "$head" >stdin && + git update-ref $type --stdin --batch-updates <stdin >stdout && + test_grep "reference does not exist" stdout + ) + ' + + test_expect_success "stdin $type batch-updates delete with incorrect old_oid" ' + git init repo && + test_when_finished "rm -fr repo" && + ( + cd repo && + test_commit c1 && + git branch new-branch && + test_commit c2 && + head=$(git rev-parse HEAD) && + + format_command $type "delete refs/heads/new-branch" "$head" >stdin && + git update-ref $type --stdin --batch-updates <stdin >stdout && + test_grep "incorrect old value provided" stdout + ) + ' + + test_expect_success "stdin $type batch-updates delete non-existent ref" ' + git init repo && + test_when_finished "rm -fr repo" && + ( + cd repo && + test_commit commit && + head=$(git rev-parse HEAD) && + + format_command $type "delete refs/heads/non-existent" "$head" >stdin && + git update-ref $type --stdin --batch-updates <stdin >stdout && + test_grep "reference does not exist" stdout + ) + ' done test_expect_success 'update-ref should also create reflog for HEAD' ' @@ -2310,4 +2349,44 @@ test_expect_success 'update-ref should also create reflog for HEAD' ' test_cmp expect actual ' +test_expect_success REFFILES 'empty directories are pruned when aborting a transaction' ' + test_path_is_missing .git/refs/heads/nested && + git update-ref --stdin <<-EOF && + create refs/heads/nested/something HEAD + prepare + abort + EOF + test_path_is_missing .git/refs/heads/nested +' + +test_expect_success REFFILES 'empty directories are pruned when not committing' ' + test_path_is_missing .git/refs/heads/nested && + git update-ref --stdin <<-EOF && + create refs/heads/nested/something HEAD + prepare + EOF + test_path_is_missing .git/refs/heads/nested +' + +test_expect_success 'dangling symref not overwritten by creation' ' + test_when_finished "git update-ref -d refs/heads/dangling" && + git symbolic-ref refs/heads/dangling refs/heads/does-not-exist && + test_must_fail git update-ref --no-deref --stdin 2>err <<-\EOF && + create refs/heads/dangling HEAD + EOF + test_grep "cannot lock.*dangling symref already exists" err && + test_must_fail git rev-parse --verify refs/heads/dangling && + test_must_fail git rev-parse --verify refs/heads/does-not-exist +' + +test_expect_success 'dangling symref overwritten without old oid' ' + test_when_finished "git update-ref -d refs/heads/dangling" && + git symbolic-ref refs/heads/dangling refs/heads/does-not-exist && + git update-ref --no-deref --stdin <<-\EOF && + update refs/heads/dangling HEAD + EOF + git rev-parse --verify refs/heads/dangling && + test_must_fail git rev-parse --verify refs/heads/does-not-exist +' + test_done diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh index 9da3650e91..36c903ca19 100755 --- a/t/t1403-show-ref.sh +++ b/t/t1403-show-ref.sh @@ -228,69 +228,4 @@ test_expect_success 'show-ref sub-modes are mutually exclusive' ' grep "cannot be used together" err ' -test_expect_success '--exists with existing reference' ' - git show-ref --exists refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME -' - -test_expect_success '--exists with missing reference' ' - test_expect_code 2 git show-ref --exists refs/heads/does-not-exist -' - -test_expect_success '--exists does not use DWIM' ' - test_expect_code 2 git show-ref --exists $GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 2>err && - grep "reference does not exist" err -' - -test_expect_success '--exists with HEAD' ' - git show-ref --exists HEAD -' - -test_expect_success '--exists with bad reference name' ' - test_when_finished "git update-ref -d refs/heads/bad...name" && - new_oid=$(git rev-parse HEAD) && - test-tool ref-store main update-ref msg refs/heads/bad...name $new_oid $ZERO_OID REF_SKIP_REFNAME_VERIFICATION && - git show-ref --exists refs/heads/bad...name -' - -test_expect_success '--exists with arbitrary symref' ' - test_when_finished "git symbolic-ref -d refs/symref" && - git symbolic-ref refs/symref refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME && - git show-ref --exists refs/symref -' - -test_expect_success '--exists with dangling symref' ' - test_when_finished "git symbolic-ref -d refs/heads/dangling" && - git symbolic-ref refs/heads/dangling refs/heads/does-not-exist && - git show-ref --exists refs/heads/dangling -' - -test_expect_success '--exists with nonexistent object ID' ' - test-tool ref-store main update-ref msg refs/heads/missing-oid $(test_oid 001) $ZERO_OID REF_SKIP_OID_VERIFICATION && - git show-ref --exists refs/heads/missing-oid -' - -test_expect_success '--exists with non-commit object' ' - tree_oid=$(git rev-parse HEAD^{tree}) && - test-tool ref-store main update-ref msg refs/heads/tree ${tree_oid} $ZERO_OID REF_SKIP_OID_VERIFICATION && - git show-ref --exists refs/heads/tree -' - -test_expect_success '--exists with directory fails with generic error' ' - cat >expect <<-EOF && - error: reference does not exist - EOF - test_expect_code 2 git show-ref --exists refs/heads 2>err && - test_cmp expect err -' - -test_expect_success '--exists with non-existent special ref' ' - test_expect_code 2 git show-ref --exists FETCH_HEAD -' - -test_expect_success '--exists with existing special ref' ' - test_when_finished "rm .git/FETCH_HEAD" && - git rev-parse HEAD >.git/FETCH_HEAD && - git show-ref --exists FETCH_HEAD -' - test_done diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh index 42b501f163..e30f87a358 100755 --- a/t/t1410-reflog.sh +++ b/t/t1410-reflog.sh @@ -673,4 +673,32 @@ test_expect_success 'reflog drop --all with reference' ' ) ' +test_expect_success 'expire with pattern config' ' + # Split refs/heads/ into two roots so we can apply config to each. Make + # two branches per root to verify that config is applied correctly + # multiple times. + git branch root1/branch1 && + git branch root1/branch2 && + git branch root2/branch1 && + git branch root2/branch2 && + + test_config "gc.reflogexpire" "never" && + test_config "gc.refs/heads/root2/*.reflogExpire" "now" && + git reflog expire \ + root1/branch1 root1/branch2 \ + root2/branch1 root2/branch2 && + + cat >expect <<-\EOF && + root1/branch1@{0} + root1/branch2@{0} + EOF + git log -g --branches="root*" --format=%gD >actual.raw && + # The sole reflog entry of each branch points to the same commit, so + # the order in which they are shown is nondeterministic. We just care + # about the what was expired (and what was not), so sort to get a known + # order. + sort <actual.raw >actual.sorted && + test_cmp expect actual.sorted +' + test_done diff --git a/t/t1416-ref-transaction-hooks.sh b/t/t1416-ref-transaction-hooks.sh index 8c777f7cf8..d91dd3a3b5 100755 --- a/t/t1416-ref-transaction-hooks.sh +++ b/t/t1416-ref-transaction-hooks.sh @@ -120,8 +120,6 @@ test_expect_success 'interleaving hook calls succeed' ' cat >expect <<-EOF && hooks/update refs/tags/PRE $ZERO_OID $PRE_OID - hooks/reference-transaction prepared - hooks/reference-transaction committed hooks/update refs/tags/POST $ZERO_OID $POST_OID hooks/reference-transaction prepared hooks/reference-transaction committed diff --git a/t/t1421-reflog-write.sh b/t/t1421-reflog-write.sh new file mode 100755 index 0000000000..46df64c176 --- /dev/null +++ b/t/t1421-reflog-write.sh @@ -0,0 +1,126 @@ +#!/bin/sh + +test_description='Manually write reflog entries' + +. ./test-lib.sh + +SIGNATURE="C O Mitter <committer@example.com> 1112911993 -0700" + +test_reflog_matches () { + repo="$1" && + refname="$2" && + cat >actual && + test-tool -C "$repo" ref-store main for-each-reflog-ent "$refname" >expected && + test_cmp expected actual +} + +test_expect_success 'invalid number of arguments' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + for args in "" "1" "1 2" "1 2 3" "1 2 3 4 5" + do + test_must_fail git reflog write $args 2>err && + test_grep "usage: git reflog write" err || return 1 + done + ) +' + +test_expect_success 'invalid refname' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_must_fail git reflog write "refs/heads/ invalid" $ZERO_OID $ZERO_OID first 2>err && + test_grep "invalid reference name: " err + ) +' + +test_expect_success 'unqualified refname is rejected' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_must_fail git reflog write unqualified $ZERO_OID $ZERO_OID first 2>err && + test_grep "invalid reference name: " err + ) +' + +test_expect_success 'nonexistent object IDs' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_must_fail git reflog write refs/heads/something $(test_oid deadbeef) $ZERO_OID old-object-id 2>err && + test_grep "old object .* does not exist" err && + test_must_fail git reflog write refs/heads/something $ZERO_OID $(test_oid deadbeef) new-object-id 2>err && + test_grep "new object .* does not exist" err + ) +' + +test_expect_success 'abbreviated object IDs' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit initial && + abbreviated_oid=$(git rev-parse HEAD | test_copy_bytes 8) && + test_must_fail git reflog write refs/heads/something $abbreviated_oid $ZERO_OID old-object-id 2>err && + test_grep "invalid old object ID" err && + test_must_fail git reflog write refs/heads/something $ZERO_OID $abbreviated_oid new-object-id 2>err && + test_grep "invalid new object ID" err + ) +' + +test_expect_success 'reflog message gets normalized' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit initial && + COMMIT_OID=$(git rev-parse HEAD) && + git reflog write HEAD $COMMIT_OID $COMMIT_OID "$(printf "message\nwith\nnewlines")" && + git reflog show -1 --format=%gs HEAD >actual && + echo "message with newlines" >expected && + test_cmp expected actual + ) +' + +test_expect_success 'simple writes' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit initial && + COMMIT_OID=$(git rev-parse HEAD) && + + git reflog write refs/heads/something $ZERO_OID $COMMIT_OID first && + test_reflog_matches . refs/heads/something <<-EOF && + $ZERO_OID $COMMIT_OID $SIGNATURE first + EOF + + git reflog write refs/heads/something $COMMIT_OID $COMMIT_OID second && + test_reflog_matches . refs/heads/something <<-EOF + $ZERO_OID $COMMIT_OID $SIGNATURE first + $COMMIT_OID $COMMIT_OID $SIGNATURE second + EOF + ) +' + +test_expect_success 'can write to root ref' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + test_commit initial && + COMMIT_OID=$(git rev-parse HEAD) && + + git reflog write ROOT_REF_HEAD $ZERO_OID $COMMIT_OID first && + test_reflog_matches . ROOT_REF_HEAD <<-EOF + $ZERO_OID $COMMIT_OID $SIGNATURE first + EOF + ) +' + +test_done diff --git a/t/t1422-show-ref-exists.sh b/t/t1422-show-ref-exists.sh new file mode 100755 index 0000000000..fdca3f16c8 --- /dev/null +++ b/t/t1422-show-ref-exists.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +test_description='show-ref --exists' +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME + +. ./test-lib.sh + +. "$TEST_DIRECTORY"/show-ref-exists-tests.sh diff --git a/t/t1460-refs-migrate.sh b/t/t1460-refs-migrate.sh index 2ab97e1b7d..0e1116a319 100755 --- a/t/t1460-refs-migrate.sh +++ b/t/t1460-refs-migrate.sh @@ -7,6 +7,17 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME . ./test-lib.sh +print_all_reflog_entries () { + repo=$1 && + test-tool -C "$repo" ref-store main for-each-reflog >reflogs && + while read reflog + do + echo "REFLOG: $reflog" && + test-tool -C "$repo" ref-store main for-each-reflog-ent "$reflog" || + return 1 + done <reflogs +} + # Migrate the provided repository from one format to the other and # verify that the references and logs are migrated over correctly. # Usage: test_migration <repo> <format> [<skip_reflog_verify> [<options...>]] @@ -28,8 +39,7 @@ test_migration () { --format='%(refname) %(objectname) %(symref)' >expect && if ! $skip_reflog_verify then - git -C "$repo" reflog --all >expect_logs && - git -C "$repo" reflog list >expect_log_list + print_all_reflog_entries "$repo" >expect_logs fi && git -C "$repo" refs migrate --ref-format="$format" "$@" && @@ -39,10 +49,8 @@ test_migration () { test_cmp expect actual && if ! $skip_reflog_verify then - git -C "$repo" reflog --all >actual_logs && - git -C "$repo" reflog list >actual_log_list && - test_cmp expect_logs actual_logs && - test_cmp expect_log_list actual_log_list + print_all_reflog_entries "$repo" >actual_logs && + test_cmp expect_logs actual_logs fi && git -C "$repo" rev-parse --show-ref-format >actual && @@ -273,7 +281,7 @@ test_expect_success 'multiple reftable blocks with multiple entries' ' test_commit -C repo second && printf "update refs/heads/ref-%d HEAD\n" $(test_seq 3000) >stdin && git -C repo update-ref --stdin <stdin && - test_migration repo reftable + test_migration repo reftable true ' test_expect_success 'migrating from files format deletes backend files' ' diff --git a/t/t1461-refs-list.sh b/t/t1461-refs-list.sh new file mode 100755 index 0000000000..36e3d81e59 --- /dev/null +++ b/t/t1461-refs-list.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +test_description='git refs list tests' + +. ./test-lib.sh + +git_for_each_ref='git refs list' +. "$TEST_DIRECTORY"/for-each-ref-tests.sh diff --git a/t/t1462-refs-exists.sh b/t/t1462-refs-exists.sh new file mode 100755 index 0000000000..349453c4ca --- /dev/null +++ b/t/t1462-refs-exists.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +test_description='refs exists' +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main +export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME + +. ./test-lib.sh + +git_show_ref_exists='git refs exists' +. "$TEST_DIRECTORY"/show-ref-exists-tests.sh diff --git a/t/t1517-outside-repo.sh b/t/t1517-outside-repo.sh index 6824581317..c824c1a25c 100755 --- a/t/t1517-outside-repo.sh +++ b/t/t1517-outside-repo.sh @@ -107,11 +107,46 @@ test_expect_success LIBCURL 'remote-http outside repository' ' test_grep "^error: remote-curl" actual ' -test_expect_success 'update-server-info does not crash with -h' ' - test_expect_code 129 git update-server-info -h >usage && - test_grep "[Uu]sage: git update-server-info " usage && - test_expect_code 129 nongit git update-server-info -h >usage && - test_grep "[Uu]sage: git update-server-info " usage +for cmd in $(git --list-cmds=main) +do + cmd=${cmd%.*} # strip .sh, .perl, etc. + case "$cmd" in + archimport | citool | credential-netrc | credential-libsecret | \ + credential-osxkeychain | cvsexportcommit | cvsimport | cvsserver | \ + daemon | \ + difftool--helper | filter-branch | fsck-objects | get-tar-commit-id | \ + gui | gui--askpass | \ + http-backend | http-fetch | http-push | init-db | \ + merge-octopus | merge-one-file | merge-resolve | mergetool | \ + mktag | p4 | p4.py | pickaxe | remote-ftp | remote-ftps | \ + remote-http | remote-https | replay | send-email | \ + sh-i18n--envsubst | shell | show | stage | submodule | svn | \ + upload-archive--writer | upload-pack | web--browse | whatchanged) + expect_outcome=expect_failure ;; + *) + expect_outcome=expect_success ;; + esac + case "$cmd" in + instaweb) + prereq=PERL ;; + *) + prereq= ;; + esac + test_$expect_outcome $prereq "'git $cmd -h' outside a repository" ' + test_expect_code 129 nongit git $cmd -h >usage && + test_grep "[Uu]sage: git $cmd " usage + ' + test_$expect_outcome $prereq "'git $cmd --help-all' outside a repository" ' + test_expect_code 129 nongit git $cmd --help-all >usage && + test_grep "[Uu]sage: git $cmd " usage + ' +done + +test_expect_success 'fmt-merge-msg does not crash with -h' ' + test_expect_code 129 git fmt-merge-msg -h >usage && + test_grep "[Uu]sage: git fmt-merge-msg " usage && + test_expect_code 129 nongit git fmt-merge-msg -h >usage && + test_grep "[Uu]sage: git fmt-merge-msg " usage ' test_done diff --git a/t/t1900-repo.sh b/t/t1900-repo.sh new file mode 100755 index 0000000000..2beba67889 --- /dev/null +++ b/t/t1900-repo.sh @@ -0,0 +1,113 @@ +#!/bin/sh + +test_description='test git repo-info' + +. ./test-lib.sh + +# Test whether a key-value pair is correctly returned +# +# Usage: test_repo_info <label> <init command> <repo_name> <key> <expected value> +# +# Arguments: +# label: the label of the test +# init_command: a command which creates a repository +# repo_name: the name of the repository that will be created in init_command +# key: the key of the field that is being tested +# expected_value: the value that the field should contain +test_repo_info () { + label=$1 + init_command=$2 + repo_name=$3 + key=$4 + expected_value=$5 + + test_expect_success "setup: $label" ' + eval "$init_command $repo_name" + ' + + test_expect_success "keyvalue: $label" ' + echo "$key=$expected_value" > expect && + git -C "$repo_name" repo info "$key" >actual && + test_cmp expect actual + ' + + test_expect_success "nul: $label" ' + printf "%s\n%s\0" "$key" "$expected_value" >expect && + git -C "$repo_name" repo info --format=nul "$key" >actual && + test_cmp_bin expect actual + ' +} + +test_repo_info 'ref format files is retrieved correctly' \ + 'git init --ref-format=files' 'format-files' 'references.format' 'files' + +test_repo_info 'ref format reftable is retrieved correctly' \ + 'git init --ref-format=reftable' 'format-reftable' 'references.format' 'reftable' + +test_repo_info 'bare repository = false is retrieved correctly' \ + 'git init' 'nonbare' 'layout.bare' 'false' + +test_repo_info 'bare repository = true is retrieved correctly' \ + 'git init --bare' 'bare' 'layout.bare' 'true' + +test_repo_info 'shallow repository = false is retrieved correctly' \ + 'git init' 'nonshallow' 'layout.shallow' 'false' + +test_expect_success 'setup remote' ' + git init remote && + echo x >remote/x && + git -C remote add x && + git -C remote commit -m x +' + +test_repo_info 'shallow repository = true is retrieved correctly' \ + 'git clone --depth 1 "file://$PWD/remote"' 'shallow' 'layout.shallow' 'true' + +test_repo_info 'object.format = sha1 is retrieved correctly' \ + 'git init --object-format=sha1' 'sha1' 'object.format' 'sha1' + +test_repo_info 'object.format = sha256 is retrieved correctly' \ + 'git init --object-format=sha256' 'sha256' 'object.format' 'sha256' + +test_expect_success 'values returned in order requested' ' + cat >expect <<-\EOF && + layout.bare=false + references.format=files + layout.bare=false + EOF + git init --ref-format=files ordered && + git -C ordered repo info layout.bare references.format layout.bare >actual && + test_cmp expect actual +' + +test_expect_success 'git-repo-info fails if an invalid key is requested' ' + echo "error: key ${SQ}foo${SQ} not found" >expect && + test_must_fail git repo info foo 2>actual && + test_cmp expect actual +' + +test_expect_success 'git-repo-info outputs data even if there is an invalid field' ' + echo "references.format=$(test_detect_ref_format)" >expect && + test_must_fail git repo info foo references.format bar >actual && + test_cmp expect actual +' + +test_expect_success 'git-repo-info aborts when requesting an invalid format' ' + echo "fatal: invalid format ${SQ}foo${SQ}" >expect && + test_must_fail git repo info --format=foo 2>actual && + test_cmp expect actual +' + +test_expect_success '-z uses nul-terminated format' ' + printf "layout.bare\nfalse\0layout.shallow\nfalse\0" >expected && + git repo info -z layout.bare layout.shallow >actual && + test_cmp expected actual +' + +test_expect_success 'git repo info uses the last requested format' ' + echo "layout.bare=false" >expected && + git repo info --format=nul -z --format=keyvalue layout.bare >actual && + test_cmp expected actual +' + +test_done diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh index 90638fa886..023e1301c8 100755 --- a/t/t2400-worktree-add.sh +++ b/t/t2400-worktree-add.sh @@ -42,8 +42,8 @@ test_expect_success '"add" using - shorthand' ' test_expect_success '"add" refuses to checkout locked branch' ' test_must_fail git worktree add zere main && - ! test -d zere && - ! test -d .git/worktrees/zere + test_path_is_missing zere && + test_path_is_missing .git/worktrees/zere ' test_expect_success 'checking out paths not complaining about linked checkouts' ' @@ -70,14 +70,14 @@ test_expect_success '"add" worktree' ' test_expect_success '"add" worktree with lock' ' git worktree add --detach --lock here-with-lock main && test_when_finished "git worktree unlock here-with-lock || :" && - test -f .git/worktrees/here-with-lock/locked + test_path_is_file .git/worktrees/here-with-lock/locked ' test_expect_success '"add" worktree with lock and reason' ' lock_reason="why not" && git worktree add --detach --lock --reason "$lock_reason" here-with-lock-reason main && test_when_finished "git worktree unlock here-with-lock-reason || :" && - test -f .git/worktrees/here-with-lock-reason/locked && + test_path_is_file .git/worktrees/here-with-lock-reason/locked && echo "$lock_reason" >expect && test_cmp expect .git/worktrees/here-with-lock-reason/locked ' @@ -412,14 +412,14 @@ test_expect_success '"add --orphan" with empty repository' ' test_expect_success '"add" worktree with orphan branch and lock' ' git worktree add --lock --orphan -b orphanbr orphan-with-lock && test_when_finished "git worktree unlock orphan-with-lock || :" && - test -f .git/worktrees/orphan-with-lock/locked + test_path_is_file .git/worktrees/orphan-with-lock/locked ' test_expect_success '"add" worktree with orphan branch, lock, and reason' ' lock_reason="why not" && git worktree add --detach --lock --reason "$lock_reason" orphan-with-lock-reason main && test_when_finished "git worktree unlock orphan-with-lock-reason || :" && - test -f .git/worktrees/orphan-with-lock-reason/locked && + test_path_is_file .git/worktrees/orphan-with-lock-reason/locked && echo "$lock_reason" >expect && test_cmp expect .git/worktrees/orphan-with-lock-reason/locked ' @@ -474,7 +474,7 @@ test_expect_success 'local clone --shared from linked checkout' ' test_expect_success '"add" worktree with --no-checkout' ' git worktree add --no-checkout -b swamp swamp && - ! test -e swamp/init.t && + test_path_is_missing swamp/init.t && git -C swamp reset --hard && test_cmp init.t swamp/init.t ' @@ -497,7 +497,7 @@ test_expect_success 'put a worktree under rebase' ' test_expect_success 'add a worktree, checking out a rebased branch' ' test_must_fail git worktree add new-rebase under-rebase && - ! test -d new-rebase + test_path_is_missing new-rebase ' test_expect_success 'checking out a rebased branch from another worktree' ' @@ -535,7 +535,7 @@ test_expect_success 'checkout a branch under bisect' ' git worktree list >actual && grep "under-bisect.*detached HEAD" actual && test_must_fail git worktree add new-bisect under-bisect && - ! test -d new-bisect + test_path_is_missing new-bisect ) ' @@ -1165,7 +1165,7 @@ test_expect_success '"add" not tripped up by magic worktree matching"' ' test_expect_success FUNNYNAMES 'sanitize generated worktree name' ' git worktree add --detach ". weird*..?.lock.lock" && - test -d .git/worktrees/---weird-.- + test_path_is_dir .git/worktrees/---weird-.- ' test_expect_success '"add" should not fail because of another bad worktree' ' diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh index 13f66fd649..b41e7f0daa 100755 --- a/t/t3000-ls-files-others.sh +++ b/t/t3000-ls-files-others.sh @@ -73,25 +73,6 @@ test_expect_success 'ls-files --others handles non-submodule .git' ' test_cmp expected1 output ' -test_expect_success SYMLINKS 'ls-files --others with symlinked submodule' ' - git init super && - git init sub && - ( - cd sub && - >a && - git add a && - git commit -m sub && - git pack-refs --all - ) && - ( - cd super && - "$SHELL_PATH" "$TEST_DIRECTORY/../contrib/workdir/git-new-workdir" ../sub sub && - git ls-files --others --exclude-standard >../actual - ) && - echo sub/ >expect && - test_cmp expect actual -' - test_expect_success 'setup nested pathspec search' ' test_create_repo nested && ( diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 6bac217ed3..e778dd8ae4 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -1176,7 +1176,7 @@ test_expect_success 'rebase -i respects core.commentchar' ' test B = $(git cat-file commit HEAD^ | sed -ne \$p) ' -test_expect_success 'rebase -i respects core.commentchar=auto' ' +test_expect_success !WITH_BREAKING_CHANGES 'rebase -i respects core.commentchar=auto' ' test_config core.commentchar auto && write_script copy-edit-script.sh <<-\EOF && cp "$1" edit-script @@ -1184,8 +1184,23 @@ test_expect_success 'rebase -i respects core.commentchar=auto' ' test_when_finished "git rebase --abort || :" && ( test_set_editor "$(pwd)/copy-edit-script.sh" && - git rebase -i HEAD^ + git rebase -i HEAD^ 2>err ) && + sed -n "s/^hint: *\$//p; s/^hint: //p; s/^warning: //p" err >actual && + cat >expect <<-EOF && + Support for ${SQ}core.commentChar=auto${SQ} is deprecated and will be removed in Git 3.0 + + To use the default comment string (#) please run + + git config unset core.commentChar + + To set a custom comment string please run + + git config set core.commentChar <comment string> + + where ${SQ}<comment string>${SQ} is the string you wish to use. + EOF + test_cmp expect actual && test -z "$(grep -ve "^#" -e "^\$" -e "^pick" edit-script)" ' @@ -2263,6 +2278,7 @@ test_expect_success 'non-merge commands reject merge commits' ' edit $oid fixup $oid squash $oid + drop $oid # acceptable, no advice EOF ( set_replace_editor todo && diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh index 1e0b2ffb85..5033411a43 100755 --- a/t/t3415-rebase-autosquash.sh +++ b/t/t3415-rebase-autosquash.sh @@ -394,6 +394,16 @@ test_expect_success 'autosquash with empty custom instructionFormat' ' ) ' +test_expect_success 'autosquash with invalid custom instructionFormat' ' + git reset --hard base && + test_commit invalid-instructionFormat-test && + ( + test_must_fail git -c rebase.instructionFormat=blah \ + rebase --autosquash --force-rebase -i HEAD^ && + test_path_is_missing .git/rebase-merge + ) +' + set_backup_editor () { write_script backup-editor.sh <<-\EOF cp "$1" .git/backup-"$(basename "$1")" diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh index 127216f722..f9b8999db5 100755 --- a/t/t3418-rebase-continue.sh +++ b/t/t3418-rebase-continue.sh @@ -328,6 +328,19 @@ test_expect_success 'there is no --no-reschedule-failed-exec in an ongoing rebas test_expect_code 129 git rebase --edit-todo --no-reschedule-failed-exec ' +test_expect_success !WITH_BREAKING_CHANGES 'no change in comment character due to conflicts markers with core.commentChar=auto' ' + git checkout -b branch-a && + test_commit A F1 && + git checkout -b branch-b HEAD^ && + test_commit B F1 && + test_must_fail git rebase branch-a && + printf "B\nA\n" >F1 && + git add F1 && + GIT_EDITOR="cat >actual" git -c core.commentChar=auto rebase --continue && + # Check that "#" is still the comment character. + test_grep "^# Changes to be committed" actual +' + test_orig_head_helper () { test_when_finished 'git rebase --abort && git checkout topic && diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index 98259e2ada..1f16e6b522 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -17,11 +17,6 @@ test_expect_success 'Initialize test directory' ' git commit -m "add normal files" ' -if test_have_prereq !FUNNYNAMES -then - say 'Your filesystem does not allow tabs in filenames.' -fi - test_expect_success FUNNYNAMES 'add files with funny names' ' touch -- "tab embedded" "newline${LF}embedded" && git add -- "tab embedded" "newline${LF}embedded" && diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index b8a05d95f3..d9fe289a7a 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -63,7 +63,7 @@ test_expect_success 'setup (initial)' ' ' test_expect_success 'status works (initial)' ' git add -i </dev/null >output && - grep "+1/-0 *+2/-0 file" output + test_grep "+1/-0 *+2/-0 file" output ' test_expect_success 'setup expected' ' @@ -86,7 +86,7 @@ test_expect_success 'revert works (initial)' ' git add file && test_write_lines r 1 | git add -i && git ls-files >output && - ! grep . output + test_grep ! . output ' test_expect_success 'add untracked (multiple)' ' @@ -109,7 +109,7 @@ test_expect_success 'setup (commit)' ' ' test_expect_success 'status works (commit)' ' git add -i </dev/null >output && - grep "+1/-0 *+2/-0 file" output + test_grep "+1/-0 *+2/-0 file" output ' test_expect_success 'update can stage deletions' ' @@ -141,7 +141,7 @@ test_expect_success 'revert works (commit)' ' git add file && test_write_lines r 1 | git add -i && git add -i </dev/null >output && - grep "unchanged *+3/-0 file" output + test_grep "unchanged *+3/-0 file" output ' test_expect_success 'reject multi-key input' ' @@ -185,7 +185,7 @@ test_expect_success 'setup fake editor' ' test_expect_success 'bad edit rejected' ' git reset && test_write_lines e n d | git add -p >output && - grep "hunk does not apply" output + test_grep "hunk does not apply" output ' test_expect_success 'setup patch' ' @@ -198,7 +198,7 @@ test_expect_success 'setup patch' ' test_expect_success 'garbage edit rejected' ' git reset && test_write_lines e n d | git add -p >output && - grep "hunk does not apply" output + test_grep "hunk does not apply" output ' test_expect_success 'setup patch' ' @@ -313,8 +313,8 @@ test_expect_success FILEMODE 'stage mode and hunk' ' chmod +x file && printf "y\\ny\\n" | git add -p && git diff --cached file >out && - grep "new mode" out && - grep "+content" out && + test_grep "new mode" out && + test_grep "+content" out && git diff file >out && test_must_be_empty out ' @@ -636,7 +636,7 @@ test_expect_success 'split hunk "add -p (edit)"' ' printf "%s\n" s e q n q q | EDITOR=: git add -p && git diff >actual && - ! grep "^+15" actual + test_grep ! "^+15" actual ' test_expect_success 'split hunk "add -p (no, yes, edit)"' ' @@ -648,7 +648,7 @@ test_expect_success 'split hunk "add -p (no, yes, edit)"' ' EDITOR=: git add -p 2>error && test_must_be_empty error && git diff >actual && - ! grep "^+31" actual + test_grep ! "^+31" actual ' test_expect_success 'split hunk with incomplete line at end' ' @@ -682,7 +682,7 @@ test_expect_success 'edit, adding lines to the first hunk' ' EDITOR=./fake_editor.sh git add -p 2>error && test_must_be_empty error && git diff --cached >actual && - grep "^+22" actual + test_grep "^+22" actual ' test_expect_success 'patch mode ignores unmerged entries' ' @@ -696,7 +696,7 @@ test_expect_success 'patch mode ignores unmerged entries' ' test_must_fail git merge side && echo changed >non-conflict.t && echo y | git add -p >output && - ! grep a/conflict.t output && + test_grep ! a/conflict.t output && cat >expected <<-\EOF && * Unmerged path conflict.t diff --git a/non-conflict.t b/non-conflict.t @@ -728,7 +728,7 @@ test_expect_success 'diffs can be colorized' ' # We do not want to depend on the exact coloring scheme # git uses for diffs, so just check that we saw some kind of color. - grep "$(printf "\\033")" output + test_grep "$(printf "\\033")" output ' test_expect_success 'colors can be overridden' ' @@ -743,7 +743,7 @@ test_expect_success 'colors can be overridden' ' -c color.interactive.error=blue \ add -i 2>err.raw <input && test_decode_color <err.raw >err && - grep "<BLUE>Huh (trigger)?<RESET>" err && + test_grep "<BLUE>Huh (trigger)?<RESET>" err && test_write_lines help quit >input && force_color git \ @@ -863,7 +863,45 @@ test_expect_success 'colorized diffs respect diff.wsErrorHighlight' ' printf y >y && force_color git -c diff.wsErrorHighlight=all add -p >output.raw 2>&1 <y && test_decode_color <output.raw >output && - grep "old<" output + test_grep "old<" output +' + +test_expect_success 'diff color respects color.diff' ' + git reset --hard && + + echo old >test && + git add test && + echo new >test && + + printf n >n && + force_color git \ + -c color.interactive=auto \ + -c color.interactive.prompt=blue \ + -c color.diff=false \ + -c color.diff.old=red \ + add -p >output.raw 2>&1 <n && + test_decode_color <output.raw >output && + test_grep "BLUE.*Stage this hunk" output && + test_grep ! "RED" output +' + +test_expect_success 're-coloring diff without color.interactive' ' + git reset --hard && + + test_write_lines 1 2 3 >test && + git add test && + test_write_lines one 2 three >test && + + test_write_lines s n n | + force_color git \ + -c color.interactive=false \ + -c color.interactive.prompt=blue \ + -c color.diff=true \ + -c color.diff.frag="bold magenta" \ + add -p >output.raw 2>&1 && + test_decode_color <output.raw >output && + test_grep "<BOLD;MAGENTA>@@" output && + test_grep ! "BLUE" output ' test_expect_success 'diffFilter filters diff' ' @@ -876,7 +914,7 @@ test_expect_success 'diffFilter filters diff' ' # avoid depending on the exact coloring or content of the prompts, # and just make sure we saw our diff prefixed - grep foo:.*content output + test_grep foo:.*content output ' test_expect_success 'detect bogus diffFilter output' ' @@ -886,7 +924,7 @@ test_expect_success 'detect bogus diffFilter output' ' test_config interactive.diffFilter "sed 6d" && printf y >y && force_color test_must_fail git add -p <y >output 2>&1 && - grep "mismatched output" output + test_grep "mismatched output" output ' test_expect_success 'handle iffy colored hunk headers' ' @@ -896,7 +934,7 @@ test_expect_success 'handle iffy colored hunk headers' ' printf n >n && force_color git -c interactive.diffFilter="sed s/.*@@.*/XX/" \ add -p >output 2>&1 <n && - grep "^XX$" output + test_grep "^XX$" output ' test_expect_success 'handle very large filtered diff' ' @@ -1002,7 +1040,7 @@ test_expect_success 'add -p does not expand argument lists' ' # update it, but we want to be sure that our "." pathspec # was not expanded into the argument list of any command. # So look only for "not-changed". - ! grep -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out + test_grep ! -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out ' test_expect_success 'hunk-editing handles custom comment char' ' @@ -1072,21 +1110,21 @@ test_expect_success 'setup different kinds of dirty submodules' ' test_expect_success 'status ignores dirty submodules (except HEAD)' ' git -C for-submodules add -i </dev/null >output && - grep dirty-head output && - grep dirty-both-ways output && - ! grep dirty-otherwise output + test_grep dirty-head output && + test_grep dirty-both-ways output && + test_grep ! dirty-otherwise output ' test_expect_success 'handle submodules' ' echo 123 >>for-submodules/dirty-otherwise/initial.t && force_color git -C for-submodules add -p dirty-otherwise >output 2>&1 && - grep "No changes" output && + test_grep "No changes" output && force_color git -C for-submodules add -p dirty-head >output 2>&1 <y && git -C for-submodules ls-files --stage dirty-head >actual && rev="$(git -C for-submodules/dirty-head rev-parse HEAD)" && - grep "$rev" actual + test_grep "$rev" actual ' test_expect_success 'set up pathological context' ' @@ -1230,4 +1268,90 @@ test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' ' test_cmp expect actual ' +test_expect_success 'add -p respects diff.context' ' + test_write_lines a b c d e f g h i j k l m >file && + git add file && + test_write_lines a b c d e f G h i j k l m >file && + echo y | git -c diff.context=5 add -p >actual && + test_grep "@@ -2,11 +2,11 @@" actual +' + +test_expect_success 'add -p respects diff.interHunkContext' ' + test_write_lines a b c d e f g h i j k l m n o p q r s >file && + git add file && + test_write_lines a b c d E f g i i j k l m N o p q r s >file && + echo y | git -c diff.interhunkcontext=2 add -p >actual && + test_grep "@@ -2,16 +2,16 @@" actual +' + +test_expect_success 'add -p rejects negative diff.context' ' + test_config diff.context -1 && + test_must_fail git add -p 2>output && + test_grep "diff.context cannot be negative" output +' + +for cmd in add checkout restore 'commit -m file' +do + test_expect_success "${cmd%% *} accepts -U and --inter-hunk-context" ' + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && + git add file && + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ + $cmd -p -U 4 --inter-hunk-context 2 >actual && + test_grep "@@ -2,20 +2,20 @@" actual + ' +done + +test_expect_success 'reset accepts -U and --inter-hunk-context' ' + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && + git commit -m file file && + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && + git add file && + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ + reset -p -U 4 --inter-hunk-context 2 >actual && + test_grep "@@ -2,20 +2,20 @@" actual +' + +test_expect_success 'stash accepts -U and --inter-hunk-context' ' + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && + git commit -m file file && + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ + stash -p -U 4 --inter-hunk-context 2 >actual && + test_grep "@@ -2,20 +2,20 @@" actual +' + +test_expect_success 'set up base for -p color tests' ' + echo commit >file && + git commit -am "commit state" && + git tag patch-base +' + +for cmd in add checkout commit reset restore "stash save" "stash push" +do + test_expect_success "$cmd rejects invalid context options" ' + test_must_fail git $cmd -p -U -3 2>actual && + cat actual | echo && + test_grep -e ".--unified. cannot be negative" actual && + + test_must_fail git $cmd -p --inter-hunk-context -3 2>actual && + test_grep -e ".--inter-hunk-context. cannot be negative" actual && + + test_must_fail git $cmd -U 7 2>actual && + test_grep -E ".--unified. requires .(--interactive/)?--patch." actual && + + test_must_fail git $cmd --inter-hunk-context 2 2>actual && + test_grep -E ".--inter-hunk-context. requires .(--interactive/)?--patch." actual + ' + + test_expect_success "$cmd falls back to color.ui" ' + git reset --hard patch-base && + echo working-tree >file && + test_write_lines y | + force_color git -c color.ui=false $cmd -p >output.raw 2>&1 && + test_decode_color <output.raw >output && + test_cmp output.raw output + ' +done + test_done diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index 74666ff3e4..0bb4648e36 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -11,6 +11,13 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME . ./test-lib.sh . "$TEST_DIRECTORY"/lib-unique-files.sh +test_expect_success 'setup' ' + test_oid_cache <<-EOF + export_base sha1:73c9bab443d1f88ac61aa533d2eeaaa15451239c + export_base sha256:f210fa6346e3e2ce047bdb570426b17075980c1ac01fec8fc4b75bd3ab4bcfe4 + EOF +' + test_expect_success 'usage on cmd and subcommand invalid option' ' test_expect_code 129 git stash --invalid-option 2>usage && grep "or: git stash" usage && @@ -1177,6 +1184,28 @@ test_expect_success 'stash -- <pathspec> stashes and restores the file' ' test_path_is_file bar ' +test_expect_success 'stash --patch <pathspec> stash and restores the file' ' + test_write_lines b c >file && + git commit -m "add a few lines" file && + test_write_lines a b c d >file && + test_write_lines b c d >expect-file && + echo changed-other-file >other-file && + test_write_lines s y n | git stash -m "stash bar" --patch file && + test_cmp expect-file file && + echo changed-other-file >expect && + test_cmp expect other-file && + git checkout HEAD -- file && + git stash pop && + test_cmp expect other-file && + test_write_lines a b c >expect && + test_cmp expect file +' + +test_expect_success 'stash <pathspec> -p is rejected' ' + test_must_fail git stash file -p 2>err && + test_grep "subcommand wasn${SQ}t specified; ${SQ}push${SQ} can${SQ}t be assumed due to unexpected token ${SQ}file${SQ}" err +' + test_expect_success 'stash -- <pathspec> stashes in subdirectory' ' mkdir sub && >foo && @@ -1412,6 +1441,100 @@ test_expect_success 'stash --keep-index --include-untracked with empty tree' ' ) ' +test_expect_success 'stash export and import round-trip stashes' ' + git reset && + >untracked && + >tracked1 && + >tracked2 && + git add tracked* && + git stash -- && + >subdir/untracked && + >subdir/tracked1 && + >subdir/tracked2 && + git add subdir/tracked* && + git stash --include-untracked -- subdir/ && + git tag t-stash0 stash@{0} && + git tag t-stash1 stash@{1} && + simple=$(git stash export --print) && + git stash clear && + git stash import "$simple" && + test_cmp_rev stash@{0} t-stash0 && + test_cmp_rev stash@{1} t-stash1 && + git stash export --to-ref refs/heads/foo && + test_cmp_rev "$(test_oid empty_tree)" foo: && + test_cmp_rev "$(test_oid empty_tree)" foo^: && + test_cmp_rev t-stash0 foo^2 && + test_cmp_rev t-stash1 foo^^2 && + git log --first-parent --format="%s" refs/heads/foo >log && + grep "^git stash: " log >log2 && + test_line_count = 13 log2 && + git stash clear && + git stash import foo && + test_cmp_rev stash@{0} t-stash0 && + test_cmp_rev stash@{1} t-stash1 +' + +test_expect_success 'stash import appends commits' ' + git log --format=oneline -g refs/stash >out && + cat out out >out2 && + git stash import refs/heads/foo && + git log --format=oneline -g refs/stash >actual && + test_line_count = $(wc -l <out2) actual +' + +test_expect_success 'stash export can accept specified stashes' ' + git stash clear && + git stash import foo && + git stash export --to-ref refs/heads/bar stash@{1} stash@{0} && + git stash clear && + git stash import refs/heads/bar && + test_cmp_rev stash@{1} t-stash0 && + test_cmp_rev stash@{0} t-stash1 && + git log --format=oneline -g refs/stash >actual && + test_line_count = 2 actual +' + +test_expect_success 'stash export rejects invalid arguments' ' + test_must_fail git stash export --print --to-ref refs/heads/invalid 2>err && + grep "exactly one of --print and --to-ref is required" err && + test_must_fail git stash export 2>err2 && + grep "exactly one of --print and --to-ref is required" err2 +' + +test_expect_success 'stash can import and export zero stashes' ' + git stash clear && + git stash export --to-ref refs/heads/baz && + test_cmp_rev "$(test_oid empty_tree)" baz: && + test_cmp_rev "$(test_oid export_base)" baz && + test_must_fail git rev-parse baz^1 && + git stash import baz && + test_must_fail git rev-parse refs/stash +' + +test_expect_success 'stash rejects invalid attempts to import commits' ' + git stash import foo && + test_must_fail git stash import HEAD 2>output && + oid=$(git rev-parse HEAD) && + grep "$oid is not a valid exported stash commit" output && + test_cmp_rev stash@{0} t-stash0 && + + git checkout --orphan orphan && + git commit-tree $(test_oid empty_tree) -p "$oid" -p "$oid^" -m "" >fake-commit && + git update-ref refs/heads/orphan "$(cat fake-commit)" && + oid=$(git rev-parse HEAD) && + test_must_fail git stash import orphan 2>output && + grep "found stash commit $oid without expected prefix" output && + test_cmp_rev stash@{0} t-stash0 && + + git checkout --orphan orphan2 && + git commit-tree $(test_oid empty_tree) -m "" >fake-commit && + git update-ref refs/heads/orphan2 "$(cat fake-commit)" && + oid=$(git rev-parse HEAD) && + test_must_fail git stash import orphan2 2>output && + grep "found root commit $oid with invalid data" output && + test_cmp_rev stash@{0} t-stash0 +' + test_expect_success 'stash apply should succeed with unmodified file' ' echo base >file && git add file && @@ -1549,11 +1672,9 @@ test_expect_success 'stash create reports a locked index' ' echo change >A.file && touch .git/index.lock && - cat >expect <<-EOF && - error: could not write index - EOF test_must_fail git stash create 2>err && - test_cmp expect err + test_grep "error: could not write index" err && + test_grep "error: Unable to create '.*index.lock'" err ) ' @@ -1566,11 +1687,9 @@ test_expect_success 'stash push reports a locked index' ' echo change >A.file && touch .git/index.lock && - cat >expect <<-EOF && - error: could not write index - EOF test_must_fail git stash push 2>err && - test_cmp expect err + test_grep "error: could not write index" err && + test_grep "error: Unable to create '.*index.lock'" err ) ' @@ -1584,11 +1703,41 @@ test_expect_success 'stash apply reports a locked index' ' git stash push && touch .git/index.lock && - cat >expect <<-EOF && - error: could not write index - EOF test_must_fail git stash apply 2>err && - test_cmp expect err + test_grep "error: could not write index" err && + test_grep "error: Unable to create '.*index.lock'" err + ) +' + +test_expect_success 'submodules does not affect the branch recorded in stash message' ' + git init sub_project && + ( + cd sub_project && + echo "Initial content in sub_project" >sub_file.txt && + git add sub_file.txt && + git commit -m "Initial commit in sub_project" + ) && + + git init main_project && + ( + cd main_project && + echo "Initial content in main_project" >main_file.txt && + git add main_file.txt && + git commit -m "Initial commit in main_project" && + + git -c protocol.file.allow=always submodule add ../sub_project sub && + git commit -m "Added submodule sub_project" && + + git checkout -b feature_main && + git -C sub checkout -b feature_sub && + + git checkout -b work_branch && + echo "Important work to be stashed" >work_item.txt && + git add work_item.txt && + git stash push -m "custom stash for work_branch" && + + git stash list >../actual_stash_list.txt && + grep "On work_branch: custom stash for work_branch" ../actual_stash_list.txt ) ' diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh index ae313e3c70..90a4ff2c10 100755 --- a/t/t3904-stash-patch.sh +++ b/t/t3904-stash-patch.sh @@ -107,4 +107,23 @@ test_expect_success 'stash -p with split hunk' ' ! grep "added line 2" test ' +test_expect_success 'stash -p not confused by GIT_PAGER_IN_USE' ' + echo to-stash >test && + # Set both GIT_PAGER_IN_USE and TERM. Our goal is to entice any + # diff subprocesses into thinking that they could output + # color, even though their stdout is not going into a tty. + echo y | + GIT_PAGER_IN_USE=1 TERM=vt100 git stash -p && + git diff --exit-code +' + +test_expect_success 'index push not confused by GIT_PAGER_IN_USE' ' + echo index >test && + git add test && + echo working-tree >test && + # As above, we try to entice the child diff into using color. + GIT_PAGER_IN_USE=1 TERM=vt100 git stash push test && + git diff --exit-code +' + test_done diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh index a51f881b1c..32b14e3a71 100755 --- a/t/t4000-diff-format.sh +++ b/t/t4000-diff-format.sh @@ -36,7 +36,7 @@ test_expect_success 'git diff-files -p after editing work tree.' ' # that's as far as it comes if [ "$(git config --get core.filemode)" = false ] then - say 'filemode disabled on the filesystem' + skip_all='filemode disabled on the filesystem' test_done fi diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index 782d97fb7d..55a06eadb3 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -5,7 +5,7 @@ test_description='Various diff formatting options' -GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME . ./test-lib.sh @@ -70,7 +70,7 @@ test_expect_success setup ' GIT_COMMITTER_DATE="2006-06-26 00:04:00 +0000" && export GIT_AUTHOR_DATE GIT_COMMITTER_DATE && - git checkout master && + git checkout main && git pull -s ours --no-rebase . side && GIT_AUTHOR_DATE="2006-06-26 00:05:00 +0000" && @@ -95,7 +95,7 @@ test_expect_success setup ' test_write_lines B A >dir/sub && git add dir/sub && git commit -m "Rearranged lines in dir/sub" && - git checkout master && + git checkout main && GIT_AUTHOR_DATE="2006-06-26 00:06:00 +0000" && GIT_COMMITTER_DATE="2006-06-26 00:06:00 +0000" && @@ -103,7 +103,7 @@ test_expect_success setup ' git checkout -b mode initial && git update-index --chmod=+x file0 && git commit -m "update mode" && - git checkout -f master && + git checkout -f main && GIT_AUTHOR_DATE="2006-06-26 00:06:00 +0000" && GIT_COMMITTER_DATE="2006-06-26 00:06:00 +0000" && @@ -112,12 +112,12 @@ test_expect_success setup ' git update-index --chmod=+x file2 && git commit -m "update mode (file2)" && git notes add -m "note" && - git checkout -f master && + git checkout -f main && - # Same merge as master, but with parents reversed. Hide it in a + # Same merge as main, but with parents reversed. Hide it in a # pseudo-ref to avoid impacting tests with --all. commit=$(echo reverse | - git commit-tree -p master^2 -p master^1 master^{tree}) && + git commit-tree -p main^2 -p main^1 main^{tree}) && git update-ref REVERSE $commit && git config diff.renames false && @@ -127,15 +127,15 @@ test_expect_success setup ' : <<\EOF ! [initial] Initial - * [master] Merge branch 'side' + * [main] Merge branch 'side' ! [rearrange] Rearranged lines in dir/sub ! [side] Side ---- + [rearrange] Rearranged lines in dir/sub - - [master] Merge branch 'side' + - [main] Merge branch 'side' * + [side] Side - * [master^] Third - * [master~2] Second + * [main^] Third + * [main~2] Second +*++ [initial] Initial EOF @@ -206,14 +206,30 @@ do expect="$TEST_DIRECTORY/t4013/diff.$test" actual="$pfx-diff.$test" - test_expect_success "git $cmd # magic is ${magic:-(not used)}" ' + case "$cmd" in + whatchanged | whatchanged" "*) + prereq=!WITH_BREAKING_CHANGES + ;; + *) + prereq=;; + esac + + test_expect_success $prereq "git $cmd # magic is ${magic:-(not used)}" ' { echo "$ git $cmd" + + case "$cmd" in + whatchanged | whatchanged" "*) + run="whatchanged --i-still-use-this" + run="$run ${cmd#whatchanged}" ;; + *) + run=$cmd ;; + esac && case "$magic" in "") - GIT_PRINT_SHA1_ELLIPSIS=yes git $cmd ;; + GIT_PRINT_SHA1_ELLIPSIS=yes git $run ;; noellipses) - git $cmd ;; + git $run ;; esac | sed -e "s/^\\(-*\\)$V\\(-*\\)\$/\\1g-i-t--v-e-r-s-i-o-n\2/" \ -e "s/^\\(.*mixed; boundary=\"-*\\)$V\\(-*\\)\"\$/\\1g-i-t--v-e-r-s-i-o-n\2\"/" @@ -295,64 +311,64 @@ diff-tree initial mode diff-tree --stat initial mode diff-tree --summary initial mode -diff-tree master -diff-tree -m master -diff-tree -p master -diff-tree -p -m master -diff-tree -c master -diff-tree -c --abbrev master -:noellipses diff-tree -c --abbrev master -diff-tree --cc master +diff-tree main +diff-tree -m main +diff-tree -p main +diff-tree -p -m main +diff-tree -c main +diff-tree -c --abbrev main +:noellipses diff-tree -c --abbrev main +diff-tree --cc main # stat only should show the diffstat with the first parent -diff-tree -c --stat master -diff-tree --cc --stat master -diff-tree -c --stat --summary master -diff-tree --cc --stat --summary master +diff-tree -c --stat main +diff-tree --cc --stat main +diff-tree -c --stat --summary main +diff-tree --cc --stat --summary main # stat summary should show the diffstat and summary with the first parent diff-tree -c --stat --summary side diff-tree --cc --stat --summary side -diff-tree --cc --shortstat master +diff-tree --cc --shortstat main diff-tree --cc --summary REVERSE # improved by Timo's patch -diff-tree --cc --patch-with-stat master +diff-tree --cc --patch-with-stat main # improved by Timo's patch -diff-tree --cc --patch-with-stat --summary master +diff-tree --cc --patch-with-stat --summary main # this is correct diff-tree --cc --patch-with-stat --summary side -log master -log -p master -log --root master -log --root -p master -log --patch-with-stat master -log --root --patch-with-stat master -log --root --patch-with-stat --summary master +log main +log -p main +log --root main +log --root -p main +log --patch-with-stat main +log --root --patch-with-stat main +log --root --patch-with-stat --summary main # improved by Timo's patch -log --root -c --patch-with-stat --summary master +log --root -c --patch-with-stat --summary main # improved by Timo's patch -log --root --cc --patch-with-stat --summary master -log --no-diff-merges -p --first-parent master -log --diff-merges=off -p --first-parent master -log --first-parent --diff-merges=off -p master -log -p --first-parent master -log -p --diff-merges=first-parent master -log --diff-merges=first-parent master -log -m -p --first-parent master -log -m -p master -log --cc -m -p master -log -c -m -p master -log -m --raw master -log -m --stat master -log -SF master -log -S F master -log -SF -p master -log -SF master --max-count=0 -log -SF master --max-count=1 -log -SF master --max-count=2 -log -GF master -log -GF -p master -log -GF -p --pickaxe-all master -log -IA -IB -I1 -I2 -p master +log --root --cc --patch-with-stat --summary main +log --no-diff-merges -p --first-parent main +log --diff-merges=off -p --first-parent main +log --first-parent --diff-merges=off -p main +log -p --first-parent main +log -p --diff-merges=first-parent main +log --diff-merges=first-parent main +log -m -p --first-parent main +log -m -p main +log --cc -m -p main +log -c -m -p main +log -m --raw main +log -m --stat main +log -SF main +log -S F main +log -SF -p main +log -SF main --max-count=0 +log -SF main --max-count=1 +log -SF main --max-count=2 +log -GF main +log -GF -p main +log -GF -p --pickaxe-all main +log -IA -IB -I1 -I2 -p main log --decorate --all log --decorate=full --all log --decorate --clear-decorations --all @@ -361,35 +377,35 @@ log --decorate=full --clear-decorations --all rev-list --parents HEAD rev-list --children HEAD -whatchanged master -:noellipses whatchanged master -whatchanged -p master -whatchanged --root master -:noellipses whatchanged --root master -whatchanged --root -p master -whatchanged --patch-with-stat master -whatchanged --root --patch-with-stat master -whatchanged --root --patch-with-stat --summary master +whatchanged main +:noellipses whatchanged main +whatchanged -p main +whatchanged --root main +:noellipses whatchanged --root main +whatchanged --root -p main +whatchanged --patch-with-stat main +whatchanged --root --patch-with-stat main +whatchanged --root --patch-with-stat --summary main # improved by Timo's patch -whatchanged --root -c --patch-with-stat --summary master +whatchanged --root -c --patch-with-stat --summary main # improved by Timo's patch -whatchanged --root --cc --patch-with-stat --summary master -whatchanged -SF master -:noellipses whatchanged -SF master -whatchanged -SF -p master +whatchanged --root --cc --patch-with-stat --summary main +whatchanged -SF main +:noellipses whatchanged -SF main +whatchanged -SF -p main -log --patch-with-stat master -- dir/ -whatchanged --patch-with-stat master -- dir/ -log --patch-with-stat --summary master -- dir/ -whatchanged --patch-with-stat --summary master -- dir/ +log --patch-with-stat main -- dir/ +whatchanged --patch-with-stat main -- dir/ +log --patch-with-stat --summary main -- dir/ +whatchanged --patch-with-stat --summary main -- dir/ show initial show --root initial show side -show master -show -c master -show -m master -show --first-parent master +show main +show -c main +show -m main +show --first-parent main show --stat side show --stat --summary side show --patch-with-stat side @@ -398,22 +414,22 @@ show --patch-with-raw side show --patch-with-stat --summary side format-patch --stdout initial..side -format-patch --stdout initial..master^ -format-patch --stdout initial..master -format-patch --stdout --no-numbered initial..master -format-patch --stdout --numbered initial..master +format-patch --stdout initial..main^ +format-patch --stdout initial..main +format-patch --stdout --no-numbered initial..main +format-patch --stdout --numbered initial..main format-patch --attach --stdout initial..side format-patch --attach --stdout --suffix=.diff initial..side -format-patch --attach --stdout initial..master^ -format-patch --attach --stdout initial..master +format-patch --attach --stdout initial..main^ +format-patch --attach --stdout initial..main format-patch --inline --stdout initial..side -format-patch --inline --stdout initial..master^ -format-patch --inline --stdout --numbered-files initial..master -format-patch --inline --stdout initial..master -format-patch --inline --stdout --subject-prefix=TESTCASE initial..master +format-patch --inline --stdout initial..main^ +format-patch --inline --stdout --numbered-files initial..main +format-patch --inline --stdout initial..main +format-patch --inline --stdout --subject-prefix=TESTCASE initial..main config format.subjectprefix DIFFERENT_PREFIX -format-patch --inline --stdout initial..master^^ -format-patch --stdout --cover-letter -n initial..master^ +format-patch --inline --stdout initial..main^^ +format-patch --stdout --cover-letter -n initial..main^ diff --abbrev initial..side diff -U initial..side @@ -432,13 +448,13 @@ diff --name-status dir2 dir diff --no-index --name-status dir2 dir diff --no-index --name-status -- dir2 dir diff --no-index dir dir3 -diff master master^ side +diff main main^ side # Can't use spaces... -diff --line-prefix=abc master master^ side -diff --dirstat master~1 master~2 +diff --line-prefix=abc main main^ side +diff --dirstat main~1 main~2 diff --dirstat initial rearrange diff --dirstat-by-file initial rearrange -diff --dirstat --cc master~1 master +diff --dirstat --cc main~1 main # No-index --abbrev and --no-abbrev diff --raw initial :noellipses diff --raw initial @@ -460,8 +476,13 @@ diff-tree --stat --compact-summary initial mode diff-tree -R --stat --compact-summary initial mode EOF +test_expect_success !WITH_BREAKING_CHANGES 'whatchanged needs --i-still-use-this' ' + test_must_fail git whatchanged >message 2>&1 && + test_grep "nominated for removal" message +' + test_expect_success 'log -m matches pure log' ' - git log master >result && + git log main >result && process_diffs result >expected && git log -m >result && process_diffs result >actual && @@ -469,17 +490,17 @@ test_expect_success 'log -m matches pure log' ' ' test_expect_success 'log --diff-merges=on matches --diff-merges=separate' ' - git log -p --diff-merges=separate master >result && + git log -p --diff-merges=separate main >result && process_diffs result >expected && - git log -p --diff-merges=on master >result && + git log -p --diff-merges=on main >result && process_diffs result >actual && test_cmp expected actual ' test_expect_success 'log --dd matches --diff-merges=1 -p' ' - git log --diff-merges=1 -p master >result && + git log --diff-merges=1 -p main >result && process_diffs result >expected && - git log --dd master >result && + git log --dd main >result && process_diffs result >actual && test_cmp expected actual ' @@ -490,19 +511,19 @@ test_expect_success 'deny wrong log.diffMerges config' ' ' test_expect_success 'git config log.diffMerges first-parent' ' - git log -p --diff-merges=first-parent master >result && + git log -p --diff-merges=first-parent main >result && process_diffs result >expected && test_config log.diffMerges first-parent && - git log -p --diff-merges=on master >result && + git log -p --diff-merges=on main >result && process_diffs result >actual && test_cmp expected actual ' test_expect_success 'git config log.diffMerges first-parent vs -m' ' - git log -p --diff-merges=first-parent master >result && + git log -p --diff-merges=first-parent main >result && process_diffs result >expected && test_config log.diffMerges first-parent && - git log -p -m master >result && + git log -p -m main >result && process_diffs result >actual && test_cmp expected actual ' @@ -551,7 +572,7 @@ test_expect_success 'diff-tree --stdin with log formatting' ' Third Second EOF - git rev-list master | git diff-tree --stdin --format=%s -s >actual && + git rev-list main | git diff-tree --stdin --format=%s -s >actual && test_cmp expect actual ' @@ -564,16 +585,16 @@ test_expect_success 'diff-tree --stdin with pathspec' ' dir/sub EOF - git rev-list master^ | + git rev-list main^ | git diff-tree -r --stdin --name-only --format=%s dir >actual && test_cmp expect actual ' test_expect_success 'show A B ... -- <pathspec>' ' # side touches dir/sub, file0, and file3 - # master^ touches dir/sub, and file1 - # master^^ touches dir/sub, file0, and file2 - git show --name-only --format="<%s>" side master^ master^^ -- dir >actual && + # main^ touches dir/sub, and file1 + # main^^ touches dir/sub, file0, and file2 + git show --name-only --format="<%s>" side main^ main^^ -- dir >actual && cat >expect <<-\EOF && <Side> @@ -589,7 +610,7 @@ test_expect_success 'show A B ... -- <pathspec>' ' ' test_expect_success 'diff -I<regex>: setup' ' - git checkout master && + git checkout main && test_seq 50 >file0 && git commit -m "Set up -I<regex> test file" file0 && test_seq 50 | sed -e "s/13/ten and three/" -e "/7\$/d" >file0 && @@ -627,6 +648,19 @@ test_expect_success 'diff -I<regex>: detect malformed regex' ' test_grep "invalid regex given to -I: " error ' +test_expect_success 'diff -I<regex>: ignore matching file' ' + test_when_finished "git rm -f file1" && + test_seq 50 >file1 && + git add file1 && + test_seq 50 | sed -e "s/13/ten and three/" -e "s/^[124-9].*/& /" >file1 && + + : >actual && + git diff --raw --ignore-blank-lines -I"ten.*e" -I"^[124-9]" >>actual && + git diff --name-only --ignore-blank-lines -I"ten.*e" -I"^[124-9]" >>actual && + git diff --name-status --ignore-blank-lines -I"ten.*e" -I"^[124-9]" >>actual && + test_grep ! "file1" actual +' + # check_prefix <patch> <src> <dst> # check only lines with paths to avoid dependency on exact oid/contents check_prefix () { diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_main index 9951e3677d..af1cf20f4c 100644 --- a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master +++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_main @@ -1,4 +1,4 @@ -$ git diff-tree --cc --patch-with-stat --summary master +$ git diff-tree --cc --patch-with-stat --summary main 59d314ad6f356dd08601a4cd5e530381da3e3c64 dir/sub | 2 ++ file0 | 3 +++ diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_main index db3c0a7b2c..0ec6042097 100644 --- a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master +++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_main @@ -1,4 +1,4 @@ -$ git diff-tree --cc --patch-with-stat master +$ git diff-tree --cc --patch-with-stat main 59d314ad6f356dd08601a4cd5e530381da3e3c64 dir/sub | 2 ++ file0 | 3 +++ diff --git a/t/t4013/diff.diff-tree_--cc_--shortstat_master b/t/t4013/diff.diff-tree_--cc_--shortstat_main index a4ca42df2a..9a4ef03197 100644 --- a/t/t4013/diff.diff-tree_--cc_--shortstat_master +++ b/t/t4013/diff.diff-tree_--cc_--shortstat_main @@ -1,4 +1,4 @@ -$ git diff-tree --cc --shortstat master +$ git diff-tree --cc --shortstat main 59d314ad6f356dd08601a4cd5e530381da3e3c64 2 files changed, 5 insertions(+) $ diff --git a/t/t4013/diff.diff-tree_-c_--stat_--summary_master b/t/t4013/diff.diff-tree_--cc_--stat_--summary_main index 81c3021541..9db08a4aa9 100644 --- a/t/t4013/diff.diff-tree_-c_--stat_--summary_master +++ b/t/t4013/diff.diff-tree_--cc_--stat_--summary_main @@ -1,4 +1,4 @@ -$ git diff-tree -c --stat --summary master +$ git diff-tree --cc --stat --summary main 59d314ad6f356dd08601a4cd5e530381da3e3c64 dir/sub | 2 ++ file0 | 3 +++ diff --git a/t/t4013/diff.diff-tree_-c_--stat_master b/t/t4013/diff.diff-tree_--cc_--stat_main index 89d59b1548..7ecc67a524 100644 --- a/t/t4013/diff.diff-tree_-c_--stat_master +++ b/t/t4013/diff.diff-tree_--cc_--stat_main @@ -1,4 +1,4 @@ -$ git diff-tree -c --stat master +$ git diff-tree --cc --stat main 59d314ad6f356dd08601a4cd5e530381da3e3c64 dir/sub | 2 ++ file0 | 3 +++ diff --git a/t/t4013/diff.diff-tree_--cc_master b/t/t4013/diff.diff-tree_--cc_main index 5ecb4e14ae..1a962856ad 100644 --- a/t/t4013/diff.diff-tree_--cc_master +++ b/t/t4013/diff.diff-tree_--cc_main @@ -1,4 +1,4 @@ -$ git diff-tree --cc master +$ git diff-tree --cc main 59d314ad6f356dd08601a4cd5e530381da3e3c64 diff --cc dir/sub index cead32e,7289e35..992913c diff --git a/t/t4013/diff.diff-tree_-c_--abbrev_master b/t/t4013/diff.diff-tree_-c_--abbrev_main index b8e4aa2530..039d127cf2 100644 --- a/t/t4013/diff.diff-tree_-c_--abbrev_master +++ b/t/t4013/diff.diff-tree_-c_--abbrev_main @@ -1,4 +1,4 @@ -$ git diff-tree -c --abbrev master +$ git diff-tree -c --abbrev main 59d314ad6f356dd08601a4cd5e530381da3e3c64 ::100644 100644 100644 cead32e... 7289e35... 992913c... MM dir/sub ::100644 100644 100644 b414108... f4615da... 10a8a9f... MM file0 diff --git a/t/t4013/diff.diff-tree_--cc_--stat_--summary_master b/t/t4013/diff.diff-tree_-c_--stat_--summary_main index d019867dd9..05a8d16ba7 100644 --- a/t/t4013/diff.diff-tree_--cc_--stat_--summary_master +++ b/t/t4013/diff.diff-tree_-c_--stat_--summary_main @@ -1,4 +1,4 @@ -$ git diff-tree --cc --stat --summary master +$ git diff-tree -c --stat --summary main 59d314ad6f356dd08601a4cd5e530381da3e3c64 dir/sub | 2 ++ file0 | 3 +++ diff --git a/t/t4013/diff.diff-tree_--cc_--stat_master b/t/t4013/diff.diff-tree_-c_--stat_main index 40b91796b3..61d9f450df 100644 --- a/t/t4013/diff.diff-tree_--cc_--stat_master +++ b/t/t4013/diff.diff-tree_-c_--stat_main @@ -1,4 +1,4 @@ -$ git diff-tree --cc --stat master +$ git diff-tree -c --stat main 59d314ad6f356dd08601a4cd5e530381da3e3c64 dir/sub | 2 ++ file0 | 3 +++ diff --git a/t/t4013/diff.diff-tree_-c_master b/t/t4013/diff.diff-tree_-c_main index e2d2bb2611..a84e1185cf 100644 --- a/t/t4013/diff.diff-tree_-c_master +++ b/t/t4013/diff.diff-tree_-c_main @@ -1,4 +1,4 @@ -$ git diff-tree -c master +$ git diff-tree -c main 59d314ad6f356dd08601a4cd5e530381da3e3c64 ::100644 100644 100644 cead32e925b1420c84c14cbf7cf755e7e45af8ad 7289e35bff32727c08dda207511bec138fdb9ea5 992913c5aa0a5476d10c49ed0f21fc0c6d1aedf3 MM dir/sub ::100644 100644 100644 b414108e81e5091fe0974a1858b4d0d22b107f70 f4615da674c09df322d6ba8d6b21ecfb1b1ba510 10a8a9f3657f91a156b9f0184ed79a20adef9f7f MM file0 diff --git a/t/t4013/diff.diff-tree_-m_master b/t/t4013/diff.diff-tree_-m_main index 6d0a2207fb..5da1f7f525 100644 --- a/t/t4013/diff.diff-tree_-m_master +++ b/t/t4013/diff.diff-tree_-m_main @@ -1,4 +1,4 @@ -$ git diff-tree -m master +$ git diff-tree -m main 59d314ad6f356dd08601a4cd5e530381da3e3c64 :040000 040000 65f5c9dd60ce3b2b3324b618ac7accf8d912c113 0564e026437809817a64fff393079714b6dd4628 M dir :100644 100644 b414108e81e5091fe0974a1858b4d0d22b107f70 10a8a9f3657f91a156b9f0184ed79a20adef9f7f M file0 diff --git a/t/t4013/diff.diff-tree_-p_-m_master b/t/t4013/diff.diff-tree_-p_-m_main index b60bea039d..29c9fc20b8 100644 --- a/t/t4013/diff.diff-tree_-p_-m_master +++ b/t/t4013/diff.diff-tree_-p_-m_main @@ -1,4 +1,4 @@ -$ git diff-tree -p -m master +$ git diff-tree -p -m main 59d314ad6f356dd08601a4cd5e530381da3e3c64 diff --git a/dir/sub b/dir/sub index cead32e..992913c 100644 diff --git a/t/t4013/diff.diff-tree_-p_main b/t/t4013/diff.diff-tree_-p_main new file mode 100644 index 0000000000..c658062422 --- /dev/null +++ b/t/t4013/diff.diff-tree_-p_main @@ -0,0 +1,2 @@ +$ git diff-tree -p main +$ diff --git a/t/t4013/diff.diff-tree_-p_master b/t/t4013/diff.diff-tree_-p_master deleted file mode 100644 index b182875fb2..0000000000 --- a/t/t4013/diff.diff-tree_-p_master +++ /dev/null @@ -1,2 +0,0 @@ -$ git diff-tree -p master -$ diff --git a/t/t4013/diff.diff-tree_main b/t/t4013/diff.diff-tree_main new file mode 100644 index 0000000000..dc5b9fdeb6 --- /dev/null +++ b/t/t4013/diff.diff-tree_main @@ -0,0 +1,2 @@ +$ git diff-tree main +$ diff --git a/t/t4013/diff.diff-tree_master b/t/t4013/diff.diff-tree_master deleted file mode 100644 index fe9226f8a1..0000000000 --- a/t/t4013/diff.diff-tree_master +++ /dev/null @@ -1,2 +0,0 @@ -$ git diff-tree master -$ diff --git a/t/t4013/diff.diff_--dirstat_--cc_main~1_main b/t/t4013/diff.diff_--dirstat_--cc_main~1_main new file mode 100644 index 0000000000..168a357a02 --- /dev/null +++ b/t/t4013/diff.diff_--dirstat_--cc_main~1_main @@ -0,0 +1,3 @@ +$ git diff --dirstat --cc main~1 main + 40.0% dir/ +$ diff --git a/t/t4013/diff.diff_--dirstat_--cc_master~1_master b/t/t4013/diff.diff_--dirstat_--cc_master~1_master deleted file mode 100644 index fba4e34175..0000000000 --- a/t/t4013/diff.diff_--dirstat_--cc_master~1_master +++ /dev/null @@ -1,3 +0,0 @@ -$ git diff --dirstat --cc master~1 master - 40.0% dir/ -$ diff --git a/t/t4013/diff.diff_--dirstat_main~1_main~2 b/t/t4013/diff.diff_--dirstat_main~1_main~2 new file mode 100644 index 0000000000..6809733708 --- /dev/null +++ b/t/t4013/diff.diff_--dirstat_main~1_main~2 @@ -0,0 +1,3 @@ +$ git diff --dirstat main~1 main~2 + 40.0% dir/ +$ diff --git a/t/t4013/diff.diff_--dirstat_master~1_master~2 b/t/t4013/diff.diff_--dirstat_master~1_master~2 deleted file mode 100644 index b672e1ca63..0000000000 --- a/t/t4013/diff.diff_--dirstat_master~1_master~2 +++ /dev/null @@ -1,3 +0,0 @@ -$ git diff --dirstat master~1 master~2 - 40.0% dir/ -$ diff --git a/t/t4013/diff.diff_--line-prefix=abc_master_master^_side b/t/t4013/diff.diff_--line-prefix=abc_main_main^_side index 99f91e7f0e..67a2145a36 100644 --- a/t/t4013/diff.diff_--line-prefix=abc_master_master^_side +++ b/t/t4013/diff.diff_--line-prefix=abc_main_main^_side @@ -1,4 +1,4 @@ -$ git diff --line-prefix=abc master master^ side +$ git diff --line-prefix=abc main main^ side abcdiff --cc dir/sub abcindex cead32e,7289e35..992913c abc--- a/dir/sub diff --git a/t/t4013/diff.diff_master_master^_side b/t/t4013/diff.diff_main_main^_side index 50ec9cadd6..ab81ec9e47 100644 --- a/t/t4013/diff.diff_master_master^_side +++ b/t/t4013/diff.diff_main_main^_side @@ -1,4 +1,4 @@ -$ git diff master master^ side +$ git diff main main^ side diff --cc dir/sub index cead32e,7289e35..992913c --- a/dir/sub diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master b/t/t4013/diff.format-patch_--attach_--stdout_initial..main index 52fedc179e..9f56380350 100644 --- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master +++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..main @@ -1,4 +1,4 @@ -$ git format-patch --attach --stdout initial..master +$ git format-patch --attach --stdout initial..main From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001 From: A U Thor <author@example.com> Date: Mon, 26 Jun 2006 00:01:00 +0000 diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ b/t/t4013/diff.format-patch_--attach_--stdout_initial..main^ index 1c3cde251b..80132ea99e 100644 --- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ +++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..main^ @@ -1,4 +1,4 @@ -$ git format-patch --attach --stdout initial..master^ +$ git format-patch --attach --stdout initial..main^ From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001 From: A U Thor <author@example.com> Date: Mon, 26 Jun 2006 00:01:00 +0000 diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..main index 02c4db7ec5..8e889090fb 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master +++ b/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..main @@ -1,4 +1,4 @@ -$ git format-patch --inline --stdout --numbered-files initial..master +$ git format-patch --inline --stdout --numbered-files initial..main From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001 From: A U Thor <author@example.com> Date: Mon, 26 Jun 2006 00:01:00 +0000 diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..main index c7677c5951..d7d2b12d15 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master +++ b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..main @@ -1,4 +1,4 @@ -$ git format-patch --inline --stdout --subject-prefix=TESTCASE initial..master +$ git format-patch --inline --stdout --subject-prefix=TESTCASE initial..main From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001 From: A U Thor <author@example.com> Date: Mon, 26 Jun 2006 00:01:00 +0000 diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_initial..main index 5b3e34e2c0..c49c423f82 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master +++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..main @@ -1,4 +1,4 @@ -$ git format-patch --inline --stdout initial..master +$ git format-patch --inline --stdout initial..main From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001 From: A U Thor <author@example.com> Date: Mon, 26 Jun 2006 00:01:00 +0000 diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..main^ index d13f8a8128..8669dbfe6c 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ +++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..main^ @@ -1,4 +1,4 @@ -$ git format-patch --inline --stdout initial..master^ +$ git format-patch --inline --stdout initial..main^ From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001 From: A U Thor <author@example.com> Date: Mon, 26 Jun 2006 00:01:00 +0000 diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..main^^ index caec5537de..b749be58b0 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ +++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..main^^ @@ -1,4 +1,4 @@ -$ git format-patch --inline --stdout initial..master^^ +$ git format-patch --inline --stdout initial..main^^ From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001 From: A U Thor <author@example.com> Date: Mon, 26 Jun 2006 00:01:00 +0000 diff --git a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..main^ index 244d964fc6..567f222198 100644 --- a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ +++ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..main^ @@ -1,4 +1,4 @@ -$ git format-patch --stdout --cover-letter -n initial..master^ +$ git format-patch --stdout --cover-letter -n initial..main^ From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001 From: C O Mitter <committer@example.com> Date: Mon, 26 Jun 2006 00:06:00 +0000 diff --git a/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master b/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..main index bfc287a147..195b62ea4f 100644 --- a/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master +++ b/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..main @@ -1,4 +1,4 @@ -$ git format-patch --stdout --no-numbered initial..master +$ git format-patch --stdout --no-numbered initial..main From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001 From: A U Thor <author@example.com> Date: Mon, 26 Jun 2006 00:01:00 +0000 diff --git a/t/t4013/diff.format-patch_--stdout_--numbered_initial..master b/t/t4013/diff.format-patch_--stdout_--numbered_initial..main index 568f6f584e..0678a38515 100644 --- a/t/t4013/diff.format-patch_--stdout_--numbered_initial..master +++ b/t/t4013/diff.format-patch_--stdout_--numbered_initial..main @@ -1,4 +1,4 @@ -$ git format-patch --stdout --numbered initial..master +$ git format-patch --stdout --numbered initial..main From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001 From: A U Thor <author@example.com> Date: Mon, 26 Jun 2006 00:01:00 +0000 diff --git a/t/t4013/diff.format-patch_--stdout_initial..master b/t/t4013/diff.format-patch_--stdout_initial..main index 5f0352f9f7..b4a6302e7c 100644 --- a/t/t4013/diff.format-patch_--stdout_initial..master +++ b/t/t4013/diff.format-patch_--stdout_initial..main @@ -1,4 +1,4 @@ -$ git format-patch --stdout initial..master +$ git format-patch --stdout initial..main From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001 From: A U Thor <author@example.com> Date: Mon, 26 Jun 2006 00:01:00 +0000 diff --git a/t/t4013/diff.format-patch_--stdout_initial..master^ b/t/t4013/diff.format-patch_--stdout_initial..main^ index 2ae454d807..36b3221582 100644 --- a/t/t4013/diff.format-patch_--stdout_initial..master^ +++ b/t/t4013/diff.format-patch_--stdout_initial..main^ @@ -1,4 +1,4 @@ -$ git format-patch --stdout initial..master^ +$ git format-patch --stdout initial..main^ From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001 From: A U Thor <author@example.com> Date: Mon, 26 Jun 2006 00:01:00 +0000 diff --git a/t/t4013/diff.log_-c_-m_-p_master b/t/t4013/diff.log_--cc_-m_-p_main index b660f3d5f2..f32746ea3e 100644 --- a/t/t4013/diff.log_-c_-m_-p_master +++ b/t/t4013/diff.log_--cc_-m_-p_main @@ -1,4 +1,4 @@ -$ git log -c -m -p master +$ git log --cc -m -p main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0) Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_--decorate=full_--all b/t/t4013/diff.log_--decorate=full_--all index 6b0b334a5d..c099399525 100644 --- a/t/t4013/diff.log_--decorate=full_--all +++ b/t/t4013/diff.log_--decorate=full_--all @@ -26,7 +26,7 @@ Date: Mon Jun 26 00:06:00 2006 +0000 Notes added by 'git notes add' -commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> refs/heads/master) +commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> refs/heads/main) Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--decorate=full_--clear-decorations_--all b/t/t4013/diff.log_--decorate=full_--clear-decorations_--all index 1c030a6554..c43684e536 100644 --- a/t/t4013/diff.log_--decorate=full_--clear-decorations_--all +++ b/t/t4013/diff.log_--decorate=full_--clear-decorations_--all @@ -26,7 +26,7 @@ Date: Mon Jun 26 00:06:00 2006 +0000 Notes added by 'git notes add' -commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> refs/heads/master) +commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> refs/heads/main) Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--decorate=full_--decorate-all_--all b/t/t4013/diff.log_--decorate=full_--decorate-all_--all index d6e7928784..48dca61681 100644 --- a/t/t4013/diff.log_--decorate=full_--decorate-all_--all +++ b/t/t4013/diff.log_--decorate=full_--decorate-all_--all @@ -26,7 +26,7 @@ Date: Mon Jun 26 00:06:00 2006 +0000 Notes added by 'git notes add' -commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> refs/heads/master) +commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> refs/heads/main) Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--decorate_--all b/t/t4013/diff.log_--decorate_--all index c7df1f5814..8bbf891f65 100644 --- a/t/t4013/diff.log_--decorate_--all +++ b/t/t4013/diff.log_--decorate_--all @@ -26,7 +26,7 @@ Date: Mon Jun 26 00:06:00 2006 +0000 Notes added by 'git notes add' -commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> master) +commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> main) Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--decorate_--clear-decorations_--all b/t/t4013/diff.log_--decorate_--clear-decorations_--all index 88be82cce3..86b1353f51 100644 --- a/t/t4013/diff.log_--decorate_--clear-decorations_--all +++ b/t/t4013/diff.log_--decorate_--clear-decorations_--all @@ -26,7 +26,7 @@ Date: Mon Jun 26 00:06:00 2006 +0000 Notes added by 'git notes add' -commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> master) +commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> main) Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--decorate_--decorate-all_--all b/t/t4013/diff.log_--decorate_--decorate-all_--all index 5d22618bb6..59fb17bfc5 100644 --- a/t/t4013/diff.log_--decorate_--decorate-all_--all +++ b/t/t4013/diff.log_--decorate_--decorate-all_--all @@ -26,7 +26,7 @@ Date: Mon Jun 26 00:06:00 2006 +0000 Notes added by 'git notes add' -commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> master) +commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD -> main) Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:04:00 2006 +0000 diff --git a/t/t4013/diff.log_--diff-merges=first-parent_master b/t/t4013/diff.log_--diff-merges=first-parent_main index fa63a557dd..bacee621e5 100644 --- a/t/t4013/diff.log_--diff-merges=first-parent_master +++ b/t/t4013/diff.log_--diff-merges=first-parent_main @@ -1,4 +1,4 @@ -$ git log --diff-merges=first-parent master +$ git log --diff-merges=first-parent main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_--no-diff-merges_-p_--first-parent_master b/t/t4013/diff.log_--diff-merges=off_-p_--first-parent_main index 597002232e..fe180fda3b 100644 --- a/t/t4013/diff.log_--no-diff-merges_-p_--first-parent_master +++ b/t/t4013/diff.log_--diff-merges=off_-p_--first-parent_main @@ -1,4 +1,4 @@ -$ git log --no-diff-merges -p --first-parent master +$ git log --diff-merges=off -p --first-parent main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_--first-parent_--diff-merges=off_-p_master b/t/t4013/diff.log_--first-parent_--diff-merges=off_-p_main index 5d7461a167..dca62d4e60 100644 --- a/t/t4013/diff.log_--first-parent_--diff-merges=off_-p_master +++ b/t/t4013/diff.log_--first-parent_--diff-merges=off_-p_main @@ -1,4 +1,4 @@ -$ git log --first-parent --diff-merges=off -p master +$ git log --first-parent --diff-merges=off -p main commit 80e25ffa65bcdbe82ef654b4d06dbbde7945c37f Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_--diff-merges=off_-p_--first-parent_master b/t/t4013/diff.log_--no-diff-merges_-p_--first-parent_main index 194e893c94..0b54118088 100644 --- a/t/t4013/diff.log_--diff-merges=off_-p_--first-parent_master +++ b/t/t4013/diff.log_--no-diff-merges_-p_--first-parent_main @@ -1,4 +1,4 @@ -$ git log --diff-merges=off -p --first-parent master +$ git log --no-diff-merges -p --first-parent main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_--summary_main_--_dir_ index a18f1472a9..3ed46cc867 100644 --- a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ +++ b/t/t4013/diff.log_--patch-with-stat_--summary_main_--_dir_ @@ -1,4 +1,4 @@ -$ git log --patch-with-stat --summary master -- dir/ +$ git log --patch-with-stat --summary main -- dir/ commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_--patch-with-stat_master b/t/t4013/diff.log_--patch-with-stat_main index ae425c4672..2e12b557cb 100644 --- a/t/t4013/diff.log_--patch-with-stat_master +++ b/t/t4013/diff.log_--patch-with-stat_main @@ -1,4 +1,4 @@ -$ git log --patch-with-stat master +$ git log --patch-with-stat main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_--patch-with-stat_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_main_--_dir_ index d5207cadf4..d511ea7f6b 100644 --- a/t/t4013/diff.log_--patch-with-stat_master_--_dir_ +++ b/t/t4013/diff.log_--patch-with-stat_main_--_dir_ @@ -1,4 +1,4 @@ -$ git log --patch-with-stat master -- dir/ +$ git log --patch-with-stat main -- dir/ commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_main index 0fc1e8cd71..3cfd0e6422 100644 --- a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master +++ b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_main @@ -1,4 +1,4 @@ -$ git log --root --cc --patch-with-stat --summary master +$ git log --root --cc --patch-with-stat --summary main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--patch-with-stat_--summary_main index dffc09dde9..9f4d6dfa44 100644 --- a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master +++ b/t/t4013/diff.log_--root_--patch-with-stat_--summary_main @@ -1,4 +1,4 @@ -$ git log --root --patch-with-stat --summary master +$ git log --root --patch-with-stat --summary main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_--root_--patch-with-stat_master b/t/t4013/diff.log_--root_--patch-with-stat_main index 55aa98012d..0d69ae2e11 100644 --- a/t/t4013/diff.log_--root_--patch-with-stat_master +++ b/t/t4013/diff.log_--root_--patch-with-stat_main @@ -1,4 +1,4 @@ -$ git log --root --patch-with-stat master +$ git log --root --patch-with-stat main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_main index 019d85f7de..1b71add9de 100644 --- a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master +++ b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_main @@ -1,4 +1,4 @@ -$ git log --root -c --patch-with-stat --summary master +$ git log --root -c --patch-with-stat --summary main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_--root_-p_master b/t/t4013/diff.log_--root_-p_main index b42c334439..04581296ed 100644 --- a/t/t4013/diff.log_--root_-p_master +++ b/t/t4013/diff.log_--root_-p_main @@ -1,4 +1,4 @@ -$ git log --root -p master +$ git log --root -p main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_--root_master b/t/t4013/diff.log_--root_main index e8f46159da..d5e90fd6b4 100644 --- a/t/t4013/diff.log_--root_master +++ b/t/t4013/diff.log_--root_main @@ -1,4 +1,4 @@ -$ git log --root master +$ git log --root main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_-GF_-p_--pickaxe-all_master b/t/t4013/diff.log_-GF_-p_--pickaxe-all_main index d36f88098b..1f7a497c2d 100644 --- a/t/t4013/diff.log_-GF_-p_--pickaxe-all_master +++ b/t/t4013/diff.log_-GF_-p_--pickaxe-all_main @@ -1,4 +1,4 @@ -$ git log -GF -p --pickaxe-all master +$ git log -GF -p --pickaxe-all main commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:02:00 2006 +0000 diff --git a/t/t4013/diff.log_-GF_-p_master b/t/t4013/diff.log_-GF_-p_main index 9d93f2c23a..c80dda41e9 100644 --- a/t/t4013/diff.log_-GF_-p_master +++ b/t/t4013/diff.log_-GF_-p_main @@ -1,4 +1,4 @@ -$ git log -GF -p master +$ git log -GF -p main commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:02:00 2006 +0000 diff --git a/t/t4013/diff.log_-SF_master b/t/t4013/diff.log_-GF_main index c1599f2f52..b94a7f7839 100644 --- a/t/t4013/diff.log_-SF_master +++ b/t/t4013/diff.log_-GF_main @@ -1,4 +1,4 @@ -$ git log -SF master +$ git log -GF main commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:02:00 2006 +0000 diff --git a/t/t4013/diff.log_-IA_-IB_-I1_-I2_-p_master b/t/t4013/diff.log_-IA_-IB_-I1_-I2_-p_main index 929f35a05b..67e26b4e54 100644 --- a/t/t4013/diff.log_-IA_-IB_-I1_-I2_-p_master +++ b/t/t4013/diff.log_-IA_-IB_-I1_-I2_-p_main @@ -1,4 +1,4 @@ -$ git log -IA -IB -I1 -I2 -p master +$ git log -IA -IB -I1 -I2 -p main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_-SF_-p_master b/t/t4013/diff.log_-SF_-p_main index 5e32438972..fa82ac1490 100644 --- a/t/t4013/diff.log_-SF_-p_master +++ b/t/t4013/diff.log_-SF_-p_main @@ -1,4 +1,4 @@ -$ git log -SF -p master +$ git log -SF -p main commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:02:00 2006 +0000 diff --git a/t/t4013/diff.log_-GF_master b/t/t4013/diff.log_-SF_main index 4c6708d2d0..dbf770db49 100644 --- a/t/t4013/diff.log_-GF_master +++ b/t/t4013/diff.log_-SF_main @@ -1,4 +1,4 @@ -$ git log -GF master +$ git log -SF main commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:02:00 2006 +0000 diff --git a/t/t4013/diff.log_-SF_main_--max-count=0 b/t/t4013/diff.log_-SF_main_--max-count=0 new file mode 100644 index 0000000000..683b17eb99 --- /dev/null +++ b/t/t4013/diff.log_-SF_main_--max-count=0 @@ -0,0 +1,2 @@ +$ git log -SF main --max-count=0 +$ diff --git a/t/t4013/diff.log_-SF_master_--max-count=2 b/t/t4013/diff.log_-SF_main_--max-count=1 index a6c55fd482..2102426f8c 100644 --- a/t/t4013/diff.log_-SF_master_--max-count=2 +++ b/t/t4013/diff.log_-SF_main_--max-count=1 @@ -1,4 +1,4 @@ -$ git log -SF master --max-count=2 +$ git log -SF main --max-count=1 commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:02:00 2006 +0000 diff --git a/t/t4013/diff.log_-SF_main_--max-count=2 b/t/t4013/diff.log_-SF_main_--max-count=2 new file mode 100644 index 0000000000..23e12a4cbb --- /dev/null +++ b/t/t4013/diff.log_-SF_main_--max-count=2 @@ -0,0 +1,7 @@ +$ git log -SF main --max-count=2 +commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 +Author: A U Thor <author@example.com> +Date: Mon Jun 26 00:02:00 2006 +0000 + + Third +$ diff --git a/t/t4013/diff.log_-SF_master_--max-count=0 b/t/t4013/diff.log_-SF_master_--max-count=0 deleted file mode 100644 index c1fc6c8731..0000000000 --- a/t/t4013/diff.log_-SF_master_--max-count=0 +++ /dev/null @@ -1,2 +0,0 @@ -$ git log -SF master --max-count=0 -$ diff --git a/t/t4013/diff.log_-SF_master_--max-count=1 b/t/t4013/diff.log_-SF_master_--max-count=1 deleted file mode 100644 index c981a03814..0000000000 --- a/t/t4013/diff.log_-SF_master_--max-count=1 +++ /dev/null @@ -1,7 +0,0 @@ -$ git log -SF master --max-count=1 -commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 -Author: A U Thor <author@example.com> -Date: Mon Jun 26 00:02:00 2006 +0000 - - Third -$ diff --git a/t/t4013/diff.log_-S_F_master b/t/t4013/diff.log_-S_F_main index 978d2b4118..a75a42e143 100644 --- a/t/t4013/diff.log_-S_F_master +++ b/t/t4013/diff.log_-S_F_main @@ -1,4 +1,4 @@ -$ git log -S F master +$ git log -S F main commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:02:00 2006 +0000 diff --git a/t/t4013/diff.log_--cc_-m_-p_master b/t/t4013/diff.log_-c_-m_-p_main index 7c217cf348..427f732456 100644 --- a/t/t4013/diff.log_--cc_-m_-p_master +++ b/t/t4013/diff.log_-c_-m_-p_main @@ -1,4 +1,4 @@ -$ git log --cc -m -p master +$ git log -c -m -p main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0) Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_-m_--raw_master b/t/t4013/diff.log_-m_--raw_main index cd2ecc4628..31d9bc7707 100644 --- a/t/t4013/diff.log_-m_--raw_master +++ b/t/t4013/diff.log_-m_--raw_main @@ -1,4 +1,4 @@ -$ git log -m --raw master +$ git log -m --raw main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0) Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_-m_--stat_master b/t/t4013/diff.log_-m_--stat_main index c7db084fd9..4c8909229b 100644 --- a/t/t4013/diff.log_-m_--stat_master +++ b/t/t4013/diff.log_-m_--stat_main @@ -1,4 +1,4 @@ -$ git log -m --stat master +$ git log -m --stat main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0) Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_-m_-p_--first-parent_master b/t/t4013/diff.log_-m_-p_--first-parent_main index 7a0073f529..459e10786b 100644 --- a/t/t4013/diff.log_-m_-p_--first-parent_master +++ b/t/t4013/diff.log_-m_-p_--first-parent_main @@ -1,4 +1,4 @@ -$ git log -m -p --first-parent master +$ git log -m -p --first-parent main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_-m_-p_master b/t/t4013/diff.log_-m_-p_main index 9ca62a01ed..07453c5698 100644 --- a/t/t4013/diff.log_-m_-p_master +++ b/t/t4013/diff.log_-m_-p_main @@ -1,4 +1,4 @@ -$ git log -m -p master +$ git log -m -p main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0) Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_-p_--diff-merges=first-parent_master b/t/t4013/diff.log_-p_--diff-merges=first-parent_main index 9538a27511..264a2f373e 100644 --- a/t/t4013/diff.log_-p_--diff-merges=first-parent_master +++ b/t/t4013/diff.log_-p_--diff-merges=first-parent_main @@ -1,4 +1,4 @@ -$ git log -p --diff-merges=first-parent master +$ git log -p --diff-merges=first-parent main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_-p_--first-parent_master b/t/t4013/diff.log_-p_--first-parent_main index 28840ebea1..247980817b 100644 --- a/t/t4013/diff.log_-p_--first-parent_master +++ b/t/t4013/diff.log_-p_--first-parent_main @@ -1,4 +1,4 @@ -$ git log -p --first-parent master +$ git log -p --first-parent main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_-p_master b/t/t4013/diff.log_-p_main index bf1326dc36..c82b4dbf16 100644 --- a/t/t4013/diff.log_-p_master +++ b/t/t4013/diff.log_-p_main @@ -1,4 +1,4 @@ -$ git log -p master +$ git log -p main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.log_master b/t/t4013/diff.log_main index a8f6ce5abd..50401f73e6 100644 --- a/t/t4013/diff.log_master +++ b/t/t4013/diff.log_main @@ -1,4 +1,4 @@ -$ git log master +$ git log main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.noellipses-diff-tree_-c_--abbrev_master b/t/t4013/diff.noellipses-diff-tree_-c_--abbrev_main index bb80f013b3..3aa1f80af3 100644 --- a/t/t4013/diff.noellipses-diff-tree_-c_--abbrev_master +++ b/t/t4013/diff.noellipses-diff-tree_-c_--abbrev_main @@ -1,4 +1,4 @@ -$ git diff-tree -c --abbrev master +$ git diff-tree -c --abbrev main 59d314ad6f356dd08601a4cd5e530381da3e3c64 ::100644 100644 100644 cead32e 7289e35 992913c MM dir/sub ::100644 100644 100644 b414108 f4615da 10a8a9f MM file0 diff --git a/t/t4013/diff.noellipses-whatchanged_--root_master b/t/t4013/diff.noellipses-whatchanged_--root_main index c2cfd4e729..2bec055835 100644 --- a/t/t4013/diff.noellipses-whatchanged_--root_master +++ b/t/t4013/diff.noellipses-whatchanged_--root_main @@ -1,4 +1,4 @@ -$ git whatchanged --root master +$ git whatchanged --root main commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Author: A U Thor <author@example.com> Date: Mon Jun 26 00:03:00 2006 +0000 diff --git a/t/t4013/diff.noellipses-whatchanged_-SF_master b/t/t4013/diff.noellipses-whatchanged_-SF_main index b36ce5886e..0c1476d19e 100644 --- a/t/t4013/diff.noellipses-whatchanged_-SF_master +++ b/t/t4013/diff.noellipses-whatchanged_-SF_main @@ -1,4 +1,4 @@ -$ git whatchanged -SF master +$ git whatchanged -SF main commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:02:00 2006 +0000 diff --git a/t/t4013/diff.noellipses-whatchanged_master b/t/t4013/diff.noellipses-whatchanged_main index 55e500f2ed..c48d2851aa 100644 --- a/t/t4013/diff.noellipses-whatchanged_master +++ b/t/t4013/diff.noellipses-whatchanged_main @@ -1,4 +1,4 @@ -$ git whatchanged master +$ git whatchanged main commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Author: A U Thor <author@example.com> Date: Mon Jun 26 00:03:00 2006 +0000 diff --git a/t/t4013/diff.show_--first-parent_master b/t/t4013/diff.show_--first-parent_main index 3dcbe473a0..480502d65c 100644 --- a/t/t4013/diff.show_--first-parent_master +++ b/t/t4013/diff.show_--first-parent_main @@ -1,4 +1,4 @@ -$ git show --first-parent master +$ git show --first-parent main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.show_-c_master b/t/t4013/diff.show_-c_main index 81aba8da96..74ef8bc96b 100644 --- a/t/t4013/diff.show_-c_master +++ b/t/t4013/diff.show_-c_main @@ -1,4 +1,4 @@ -$ git show -c master +$ git show -c main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.show_-m_master b/t/t4013/diff.show_-m_main index 4ea2ee453d..8fd56736d9 100644 --- a/t/t4013/diff.show_-m_master +++ b/t/t4013/diff.show_-m_main @@ -1,4 +1,4 @@ -$ git show -m master +$ git show -m main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0) Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.show_master b/t/t4013/diff.show_main index fb08ce0e46..630b52a95e 100644 --- a/t/t4013/diff.show_master +++ b/t/t4013/diff.show_main @@ -1,4 +1,4 @@ -$ git show master +$ git show main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.whatchanged_--patch-with-stat_--summary_main_--_dir_ index c8b6af2f43..ce0754d556 100644 --- a/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_ +++ b/t/t4013/diff.whatchanged_--patch-with-stat_--summary_main_--_dir_ @@ -1,4 +1,4 @@ -$ git whatchanged --patch-with-stat --summary master -- dir/ +$ git whatchanged --patch-with-stat --summary main -- dir/ commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Author: A U Thor <author@example.com> Date: Mon Jun 26 00:03:00 2006 +0000 diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_master b/t/t4013/diff.whatchanged_--patch-with-stat_main index 1ac431ba92..aabccf39a5 100644 --- a/t/t4013/diff.whatchanged_--patch-with-stat_master +++ b/t/t4013/diff.whatchanged_--patch-with-stat_main @@ -1,4 +1,4 @@ -$ git whatchanged --patch-with-stat master +$ git whatchanged --patch-with-stat main commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Author: A U Thor <author@example.com> Date: Mon Jun 26 00:03:00 2006 +0000 diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_ b/t/t4013/diff.whatchanged_--patch-with-stat_main_--_dir_ index b30c28588f..c05a0e8149 100644 --- a/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_ +++ b/t/t4013/diff.whatchanged_--patch-with-stat_main_--_dir_ @@ -1,4 +1,4 @@ -$ git whatchanged --patch-with-stat master -- dir/ +$ git whatchanged --patch-with-stat main -- dir/ commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Author: A U Thor <author@example.com> Date: Mon Jun 26 00:03:00 2006 +0000 diff --git a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_main index 30aae7817b..1f74b1b548 100644 --- a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master +++ b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_main @@ -1,4 +1,4 @@ -$ git whatchanged --root --cc --patch-with-stat --summary master +$ git whatchanged --root --cc --patch-with-stat --summary main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_main index db90e51525..80d9812151 100644 --- a/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master +++ b/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_main @@ -1,4 +1,4 @@ -$ git whatchanged --root --patch-with-stat --summary master +$ git whatchanged --root --patch-with-stat --summary main commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Author: A U Thor <author@example.com> Date: Mon Jun 26 00:03:00 2006 +0000 diff --git a/t/t4013/diff.whatchanged_--root_--patch-with-stat_master b/t/t4013/diff.whatchanged_--root_--patch-with-stat_main index 9a6cc92ce7..c0b9082a2c 100644 --- a/t/t4013/diff.whatchanged_--root_--patch-with-stat_master +++ b/t/t4013/diff.whatchanged_--root_--patch-with-stat_main @@ -1,4 +1,4 @@ -$ git whatchanged --root --patch-with-stat master +$ git whatchanged --root --patch-with-stat main commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Author: A U Thor <author@example.com> Date: Mon Jun 26 00:03:00 2006 +0000 diff --git a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_main index d1d32bd34c..0002c6912e 100644 --- a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master +++ b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_main @@ -1,4 +1,4 @@ -$ git whatchanged --root -c --patch-with-stat --summary master +$ git whatchanged --root -c --patch-with-stat --summary main commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 Merge: 9a6d494 c7a2ab9 Author: A U Thor <author@example.com> diff --git a/t/t4013/diff.whatchanged_--root_-p_master b/t/t4013/diff.whatchanged_--root_-p_main index ebf1f0661e..39f3e2be74 100644 --- a/t/t4013/diff.whatchanged_--root_-p_master +++ b/t/t4013/diff.whatchanged_--root_-p_main @@ -1,4 +1,4 @@ -$ git whatchanged --root -p master +$ git whatchanged --root -p main commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Author: A U Thor <author@example.com> Date: Mon Jun 26 00:03:00 2006 +0000 diff --git a/t/t4013/diff.whatchanged_--root_master b/t/t4013/diff.whatchanged_--root_main index a405cb6138..36f4d6697f 100644 --- a/t/t4013/diff.whatchanged_--root_master +++ b/t/t4013/diff.whatchanged_--root_main @@ -1,4 +1,4 @@ -$ git whatchanged --root master +$ git whatchanged --root main commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Author: A U Thor <author@example.com> Date: Mon Jun 26 00:03:00 2006 +0000 diff --git a/t/t4013/diff.whatchanged_-SF_-p_master b/t/t4013/diff.whatchanged_-SF_-p_main index f39da84822..0e2e67c0df 100644 --- a/t/t4013/diff.whatchanged_-SF_-p_master +++ b/t/t4013/diff.whatchanged_-SF_-p_main @@ -1,4 +1,4 @@ -$ git whatchanged -SF -p master +$ git whatchanged -SF -p main commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:02:00 2006 +0000 diff --git a/t/t4013/diff.whatchanged_-SF_master b/t/t4013/diff.whatchanged_-SF_main index 0499321d0e..34c6bf6b95 100644 --- a/t/t4013/diff.whatchanged_-SF_master +++ b/t/t4013/diff.whatchanged_-SF_main @@ -1,4 +1,4 @@ -$ git whatchanged -SF master +$ git whatchanged -SF main commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Author: A U Thor <author@example.com> Date: Mon Jun 26 00:02:00 2006 +0000 diff --git a/t/t4013/diff.whatchanged_-p_master b/t/t4013/diff.whatchanged_-p_main index f18d43209c..18f3bdeef4 100644 --- a/t/t4013/diff.whatchanged_-p_master +++ b/t/t4013/diff.whatchanged_-p_main @@ -1,4 +1,4 @@ -$ git whatchanged -p master +$ git whatchanged -p main commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Author: A U Thor <author@example.com> Date: Mon Jun 26 00:03:00 2006 +0000 diff --git a/t/t4013/diff.whatchanged_master b/t/t4013/diff.whatchanged_main index cd3bcc2c72..d6c83ed8b0 100644 --- a/t/t4013/diff.whatchanged_master +++ b/t/t4013/diff.whatchanged_main @@ -1,4 +1,4 @@ -$ git whatchanged master +$ git whatchanged main commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Author: A U Thor <author@example.com> Date: Mon Jun 26 00:03:00 2006 +0000 diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh index 52e3e476ff..9de7f73f42 100755 --- a/t/t4015-diff-whitespace.sh +++ b/t/t4015-diff-whitespace.sh @@ -11,12 +11,8 @@ test_description='Test special whitespace in diff engine. . "$TEST_DIRECTORY"/lib-diff.sh for opt_res in --patch --quiet -s --stat --shortstat --dirstat=lines \ - --raw! --name-only! --name-status! + --raw --name-only --name-status do - opts=${opt_res%!} expect_failure= - test "$opts" = "$opt_res" || - expect_failure="test_expect_code 1" - test_expect_success "status with $opts (different)" ' echo foo >x && git add x && @@ -43,7 +39,7 @@ do echo foo >x && git add x && echo " foo" >x && - $expect_failure git diff -w $opts --exit-code x + git diff -w $opts --exit-code x ' done diff --git a/t/t4018/r-indent b/t/t4018/r-indent new file mode 100644 index 0000000000..9df440f2a4 --- /dev/null +++ b/t/t4018/r-indent @@ -0,0 +1,6 @@ +RIGHT <- function(a, b) { + c = mean(a, b) + d = c + 2 + ChangeMe() + return (d) +} diff --git a/t/t4018/r-indent-nested b/t/t4018/r-indent-nested new file mode 100644 index 0000000000..30412e6c79 --- /dev/null +++ b/t/t4018/r-indent-nested @@ -0,0 +1,10 @@ +LEFT = function(a, b) { + c = mean(a, b) + RIGHT = function(d, e) { + f = var(d, e) + g = f + 1 + ChangeMe() + return (g) + } + return (RIGHT(2, 3)) +} diff --git a/t/t4018/r-noindent b/t/t4018/r-noindent new file mode 100644 index 0000000000..6d9b01ffe3 --- /dev/null +++ b/t/t4018/r-noindent @@ -0,0 +1,6 @@ +RIGHT <- function(a, b) { +c = mean(a, b) +d = c + 2 +ChangeMe() +return (c) +} diff --git a/t/t4041-diff-submodule-option.sh b/t/t4041-diff-submodule-option.sh index 28f9d83d4c..4d4aa1650f 100755 --- a/t/t4041-diff-submodule-option.sh +++ b/t/t4041-diff-submodule-option.sh @@ -48,11 +48,12 @@ commit_file () { git commit "$@" -m "Commit $*" >/dev/null } -test_create_repo sm1 && -add_file . foo >/dev/null - -head1=$(add_file sm1 foo1 foo2) -fullhead1=$(cd sm1; git rev-parse --verify HEAD) +test_expect_success 'setup submodule' ' + git init sm1 && + add_file . foo && + head1=$(add_file sm1 foo1 foo2) && + fullhead1=$(cd sm1 && git rev-parse --verify HEAD) +' test_expect_success 'added submodule' ' git add sm1 && @@ -235,10 +236,13 @@ test_expect_success 'typechanged submodule(submodule->blob)' ' test_cmp expected actual ' -rm -f sm1 && -test_create_repo sm1 && -head6=$(add_file sm1 foo6 foo7) -fullhead6=$(cd sm1; git rev-parse --verify HEAD) +test_expect_success 'setup submodule anew' ' + rm -f sm1 && + git init sm1 && + head6=$(add_file sm1 foo6 foo7) && + fullhead6=$(cd sm1 && git rev-parse --verify HEAD) +' + test_expect_success 'nonexistent commit' ' git diff-index -p --submodule=log HEAD >actual && cat >expected <<-EOF && diff --git a/t/t4042-diff-textconv-caching.sh b/t/t4042-diff-textconv-caching.sh index ff0e73531b..31018ceba2 100755 --- a/t/t4042-diff-textconv-caching.sh +++ b/t/t4042-diff-textconv-caching.sh @@ -120,6 +120,14 @@ test_expect_success 'log notes cache and still use cache for -p' ' ' test_expect_success 'caching is silently ignored outside repo' ' + test_oid_cache <<-\EOM && + oid1 sha1:5626abf + oid1 sha256:a4ed1f3 + oid2 sha1:f719efd + oid2 sha256:aa9e7dc + EOM + oid1=$(test_oid --hash=builtin oid1) && + oid2=$(test_oid --hash=builtin oid2) && mkdir -p non-repo && echo one >non-repo/one && echo two >non-repo/two && @@ -129,9 +137,9 @@ test_expect_success 'caching is silently ignored outside repo' ' -c diff.test.textconv="tr a-z A-Z <" \ -c diff.test.cachetextconv=true \ diff --no-index one two >actual && - cat >expect <<-\EOF && + cat >expect <<-EOF && diff --git a/one b/two - index 5626abf..f719efd 100644 + index $oid1..$oid2 100644 --- a/one +++ b/two @@ -1 +1 @@ diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh index 5e5bad61ca..44b4b13f5d 100755 --- a/t/t4053-diff-no-index.sh +++ b/t/t4053-diff-no-index.sh @@ -26,6 +26,23 @@ test_expect_success 'git diff --no-index directories' ' test_line_count = 14 cnt ' +test_expect_success 'git diff --no-index with -' ' + cat >expect <<-\EOF && + diff --git a/- b/- + new file mode 100644 + --- /dev/null + +++ b/- + @@ -0,0 +1 @@ + +frotz + EOF + ( + cd a && + echo frotz | + test_expect_code 1 git diff --no-index /dev/null - >../actual + ) && + test_cmp expect actual +' + test_expect_success 'git diff --no-index relative path outside repo' ' ( cd repo && @@ -295,4 +312,79 @@ test_expect_success PIPE,SYMLINKS 'diff --no-index reads from pipes' ' test_cmp expect actual ' +test_expect_success 'diff --no-index F F rejects pathspecs' ' + test_must_fail git diff --no-index -- a/1 a/2 a 2>actual.err && + test_grep "usage: git diff --no-index" actual.err +' + +test_expect_success 'diff --no-index D F rejects pathspecs' ' + test_must_fail git diff --no-index -- a a/2 a 2>actual.err && + test_grep "usage: git diff --no-index" actual.err +' + +test_expect_success 'diff --no-index F D rejects pathspecs' ' + test_must_fail git diff --no-index -- a/1 b b 2>actual.err && + test_grep "usage: git diff --no-index" actual.err +' + +test_expect_success 'diff --no-index rejects absolute pathspec' ' + test_must_fail git diff --no-index -- a b $(pwd)/a/1 +' + +test_expect_success 'diff --no-index with pathspec' ' + test_expect_code 1 git diff --name-status --no-index a b 1 >actual && + cat >expect <<-EOF && + D a/1 + EOF + test_cmp expect actual +' + +test_expect_success 'diff --no-index with pathspec no matches' ' + test_expect_code 0 git diff --name-status --no-index a b missing +' + +test_expect_success 'diff --no-index with negative pathspec' ' + test_expect_code 1 git diff --name-status --no-index a b ":!2" >actual && + cat >expect <<-EOF && + D a/1 + EOF + test_cmp expect actual +' + +test_expect_success 'setup nested' ' + mkdir -p c/1/2 && + mkdir -p d/1/2 && + echo 1 >c/1/2/a && + echo 2 >c/1/2/b +' + +test_expect_success 'diff --no-index with pathspec nested negative pathspec' ' + test_expect_code 0 git diff --no-index c d ":!1" +' + +test_expect_success 'diff --no-index with pathspec nested pathspec' ' + test_expect_code 1 git diff --name-status --no-index c d 1/2 >actual && + cat >expect <<-EOF && + D c/1/2/a + D c/1/2/b + EOF + test_cmp expect actual +' + +test_expect_success 'diff --no-index with pathspec glob' ' + test_expect_code 1 git diff --name-status --no-index c d ":(glob)**/a" >actual && + cat >expect <<-EOF && + D c/1/2/a + EOF + test_cmp expect actual +' + +test_expect_success 'diff --no-index with pathspec glob and exclude' ' + test_expect_code 1 git diff --name-status --no-index c d ":(glob,exclude)**/a" >actual && + cat >expect <<-EOF && + D c/1/2/b + EOF + test_cmp expect actual +' + test_done diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index ec2804eea6..1384a81957 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -38,55 +38,55 @@ test_expect_success 'setup' ' test_expect_success 'the default number of context lines is 3' ' git diff >output && - ! grep "^ d" output && - grep "^ e" output && - grep "^ j" output && - ! grep "^ k" output + test_grep ! "^ d" output && + test_grep "^ e" output && + test_grep "^ j" output && + test_grep ! "^ k" output ' test_expect_success 'diff.context honored by "log"' ' git log -1 -p >output && - ! grep firstline output && - git config diff.context 8 && + test_grep ! firstline output && + test_config diff.context 8 && git log -1 -p >output && - grep "^ firstline" output + test_grep "^ firstline" output ' test_expect_success 'The -U option overrides diff.context' ' - git config diff.context 8 && + test_config diff.context 8 && git log -U4 -1 >output && - ! grep "^ firstline" output + test_grep ! "^ firstline" output ' test_expect_success 'diff.context honored by "diff"' ' - git config diff.context 8 && + test_config diff.context 8 && git diff >output && - grep "^ firstline" output + test_grep "^ firstline" output ' test_expect_success 'plumbing not affected' ' - git config diff.context 8 && + test_config diff.context 8 && git diff-files -p >output && - ! grep "^ firstline" output + test_grep ! "^ firstline" output ' test_expect_success 'non-integer config parsing' ' - git config diff.context no && + test_config diff.context no && test_must_fail git diff 2>output && test_grep "bad numeric config value" output ' test_expect_success 'negative integer config parsing' ' - git config diff.context -1 && + test_config diff.context -1 && test_must_fail git diff 2>output && test_grep "bad config variable" output ' test_expect_success '-U0 is valid, so is diff.context=0' ' - git config diff.context 0 && + test_config diff.context 0 && git diff >output && - grep "^-ADDED" output && - grep "^+MODIFIED" output + test_grep "^-ADDED" output && + test_grep "^+MODIFIED" output ' test_expect_success '-U2147483647 works' ' @@ -94,9 +94,9 @@ test_expect_success '-U2147483647 works' ' test_line_count = 16 x && git diff -U2147483647 >output && test_line_count = 22 output && - grep "^-ADDED" output && - grep "^+MODIFIED" output && - grep "^+APPENDED" output + test_grep "^-ADDED" output && + test_grep "^+MODIFIED" output && + test_grep "^+APPENDED" output ' test_done diff --git a/t/t4060-diff-submodule-option-diff-format.sh b/t/t4060-diff-submodule-option-diff-format.sh index 76b83101d3..dbfeb7470b 100755 --- a/t/t4060-diff-submodule-option-diff-format.sh +++ b/t/t4060-diff-submodule-option-diff-format.sh @@ -363,9 +363,12 @@ test_expect_success 'typechanged submodule(submodule->blob)' ' diff_cmp expected actual ' -rm -f sm1 && -test_create_repo sm1 && -head6=$(add_file sm1 foo6 foo7) +test_expect_success 'setup' ' + rm -f sm1 && + git init sm1 && + head6=$(add_file sm1 foo6 foo7) +' + test_expect_success 'nonexistent commit' ' git diff-index -p --submodule=diff HEAD >actual && cat >expected <<-EOF && diff --git a/t/t4072-diff-max-depth.sh b/t/t4072-diff-max-depth.sh new file mode 100755 index 0000000000..0fbf1321f7 --- /dev/null +++ b/t/t4072-diff-max-depth.sh @@ -0,0 +1,116 @@ +#!/bin/sh + +test_description='check that diff --max-depth will limit recursion' +. ./test-lib.sh + +make_dir() { + mkdir -p "$1" && + echo "$2" >"$1/file" +} + +make_files() { + echo "$1" >file && + make_dir one "$1" && + make_dir one/two "$1" && + make_dir one/two/three "$1" +} + +test_expect_success 'setup' ' + git commit --allow-empty -m empty && + git tag empty && + make_files added && + git add . && + git commit -m added && + make_files modified && + git add . && + git commit -m modified && + make_files index && + git add . && + make_files worktree +' + +test_expect_success '--max-depth is disallowed with wildcard pathspecs' ' + test_must_fail git diff-tree --max-depth=0 HEAD^ HEAD -- "f*" +' + +check_one() { + type=$1; shift + args=$1; shift + path=$1; shift + depth=$1; shift + test_expect_${expect:-success} "diff-$type $args, path=$path, depth=$depth" " + for i in $*; do echo \$i; done >expect && + git diff-$type --max-depth=$depth --name-only $args -- $path >actual && + test_cmp expect actual + " +} + +# For tree comparisons, we expect to see subtrees at the boundary +# get their own entry. +check_trees() { + check_one tree "$*" '' 0 file one + check_one tree "$*" '' 1 file one/file one/two + check_one tree "$*" '' 2 file one/file one/two/file one/two/three + check_one tree "$*" '' 3 file one/file one/two/file one/two/three/file + check_one tree "$*" '' -1 file one/file one/two/file one/two/three/file + check_one tree "$*" one 0 one + check_one tree "$*" one 1 one/file one/two + check_one tree "$*" one 2 one/file one/two/file one/two/three + check_one tree "$*" one 3 one/file one/two/file one/two/three/file + check_one tree "$*" one/two 0 one/two + check_one tree "$*" one/two 1 one/two/file one/two/three + check_one tree "$*" one/two 2 one/two/file one/two/three/file + check_one tree "$*" one/two 2 one/two/file one/two/three/file + check_one tree "$*" one/two/three 0 one/two/three + check_one tree "$*" one/two/three 1 one/two/three/file +} + +# But for index comparisons, we do not store subtrees at all, so we do not +# expect them. +check_index() { + check_one "$@" '' 0 file + check_one "$@" '' 1 file one/file + check_one "$@" '' 2 file one/file one/two/file + check_one "$@" '' 3 file one/file one/two/file one/two/three/file + check_one "$@" one 0 + check_one "$@" one 1 one/file + check_one "$@" one 2 one/file one/two/file + check_one "$@" one 3 one/file one/two/file one/two/three/file + check_one "$@" one/two 0 + check_one "$@" one/two 1 one/two/file + check_one "$@" one/two 2 one/two/file one/two/three/file + check_one "$@" one/two/three 0 + check_one "$@" one/two/three 1 one/two/three/file + + # Value '-1' for '--max-depth is the same as recursion without limit, + # and thus should always succeed. + local expect= + check_one "$@" '' -1 file one/file one/two/file one/two/three/file +} + +# Check as a modification... +check_trees HEAD^ HEAD +# ...and as an addition... +check_trees empty HEAD +# ...and as a deletion. +check_trees HEAD empty + +# We currently only implement max-depth for trees. +expect=failure +# Check index against a tree +check_index index "--cached HEAD" +# and index against the worktree +check_index files "" +expect= + +test_expect_success 'find shortest path within embedded pathspecs' ' + cat >expect <<-\EOF && + one/file + one/two/file + one/two/three/file + EOF + git diff-tree --max-depth=2 --name-only HEAD^ HEAD -- one one/two >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t4140-apply-ita.sh b/t/t4140-apply-ita.sh index c614eaf04c..0b11a8aef4 100755 --- a/t/t4140-apply-ita.sh +++ b/t/t4140-apply-ita.sh @@ -7,6 +7,10 @@ test_description='git apply of i-t-a file' test_expect_success setup ' test_write_lines 1 2 3 4 5 >blueprint && + cat blueprint >committed-file && + git add committed-file && + git commit -m "commit" && + cat blueprint >test-file && git add -N test-file && git diff >creation-patch && @@ -14,7 +18,14 @@ test_expect_success setup ' rm -f test-file && git diff >deletion-patch && - grep "deleted file mode 100644" deletion-patch + grep "deleted file mode 100644" deletion-patch && + + git rm -f test-file && + test_write_lines 6 >>committed-file && + cat blueprint >test-file && + git add -N test-file && + git diff >complex-patch && + git restore committed-file ' test_expect_success 'apply creation patch to ita path (--cached)' ' @@ -53,4 +64,22 @@ test_expect_success 'apply deletion patch to ita path (--index)' ' git ls-files --stage --error-unmatch test-file ' +test_expect_success 'apply creation patch to existing index with -N' ' + git rm -f test-file && + cat blueprint >index-file && + git add index-file && + git apply -N creation-patch && + + git ls-files --stage --error-unmatch index-file && + git ls-files --stage --error-unmatch test-file +' + +test_expect_success 'apply complex patch with -N' ' + git rm -f test-file index-file && + git apply -N complex-patch && + + git ls-files --stage --error-unmatch test-file && + git diff | grep "a/committed-file" +' + test_done diff --git a/t/t4150-am.sh b/t/t4150-am.sh index 2ae93d3c96..699a81ab5c 100755 --- a/t/t4150-am.sh +++ b/t/t4150-am.sh @@ -1086,7 +1086,7 @@ test_expect_success 'am works with multi-line in-body headers' ' # bump from, date, and subject down to in-body header awk " /^From:/{ - print \"From: x <x\@example.com>\"; + print \"From: x <x@example.com>\"; print \"Date: Sat, 1 Jan 2000 00:00:00 +0000\"; print \"Subject: x\n\"; }; 1 diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 51f7beb59f..05cee9e41b 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -134,6 +134,12 @@ test_expect_success 'diff-filter=D' ' ' +test_expect_success 'all-negative filter' ' + git log --no-renames --format=%s --diff-filter=d HEAD >actual && + printf "%s\n" fifth fourth third second initial >expect && + test_cmp expect actual +' + test_expect_success 'diff-filter=R' ' git log -M --pretty="format:%s" --diff-filter=R HEAD >actual && @@ -486,10 +492,16 @@ test_expect_success !FAIL_PREREQS 'log with various grep.patternType configurati ) ' -for cmd in show whatchanged reflog format-patch +cmds="show reflog format-patch" +if test_have_prereq !WITH_BREAKING_CHANGES +then + cmds="$cmds whatchanged" +fi +for cmd in $cmds do case "$cmd" in format-patch) myarg="HEAD~.." ;; + whatchanged) myarg=--i-still-use-this ;; *) myarg= ;; esac @@ -1201,20 +1213,27 @@ test_expect_success 'reflog is expected format' ' test_cmp expect actual ' -test_expect_success 'whatchanged is expected format' ' +test_expect_success !WITH_BREAKING_CHANGES 'whatchanged is expected format' ' + whatchanged="whatchanged --i-still-use-this" && git log --no-merges --raw >expect && - git whatchanged >actual && + git $whatchanged >actual && test_cmp expect actual ' test_expect_success 'log.abbrevCommit configuration' ' + whatchanged="whatchanged --i-still-use-this" && + git log --abbrev-commit >expect.log.abbrev && git log --no-abbrev-commit >expect.log.full && git log --pretty=raw >expect.log.raw && git reflog --abbrev-commit >expect.reflog.abbrev && git reflog --no-abbrev-commit >expect.reflog.full && - git whatchanged --abbrev-commit >expect.whatchanged.abbrev && - git whatchanged --no-abbrev-commit >expect.whatchanged.full && + + if test_have_prereq !WITH_BREAKING_CHANGES + then + git $whatchanged --abbrev-commit >expect.whatchanged.abbrev && + git $whatchanged --no-abbrev-commit >expect.whatchanged.full + fi && test_config log.abbrevCommit true && @@ -1231,10 +1250,13 @@ test_expect_success 'log.abbrevCommit configuration' ' git reflog --no-abbrev-commit >actual && test_cmp expect.reflog.full actual && - git whatchanged >actual && - test_cmp expect.whatchanged.abbrev actual && - git whatchanged --no-abbrev-commit >actual && - test_cmp expect.whatchanged.full actual + if test_have_prereq !WITH_BREAKING_CHANGES + then + git $whatchanged >actual && + test_cmp expect.whatchanged.abbrev actual && + git $whatchanged --no-abbrev-commit >actual && + test_cmp expect.whatchanged.full actual + fi ' test_expect_success '--abbrev-commit with core.abbrev=false' ' diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh index 4a6242ff99..74b7ddccb2 100755 --- a/t/t4203-mailmap.sh +++ b/t/t4203-mailmap.sh @@ -1133,4 +1133,37 @@ test_expect_success 'git cat-file --batch-command returns correct size with --us test_cmp expect actual ' +test_expect_success 'git cat-file --mailmap works with different author and committer' ' + test_when_finished "rm .mailmap" && + cat >.mailmap <<-\EOF && + Mailmapped User <mailmapped-user@gitlab.com> C O Mitter <committer@example.com> + EOF + git commit --allow-empty -m "different author/committer" \ + --author="Different Author <different@example.com>" && + cat >expect <<-\EOF && + author Different Author <different@example.com> + committer Mailmapped User <mailmapped-user@gitlab.com> + EOF + git cat-file --mailmap commit HEAD >log && + sed -n -e "/^author /s/>.*/>/p" -e "/^committer /s/>.*/>/p" log >actual && + test_cmp expect actual +' + +test_expect_success 'git cat-file --mailmap maps both author and committer when both need mapping' ' + test_when_finished "rm .mailmap" && + cat >.mailmap <<-\EOF && + Mapped Author <mapped-author@example.com> <different@example.com> + Mapped Committer <mapped-committer@example.com> C O Mitter <committer@example.com> + EOF + git commit --allow-empty -m "both author and committer mapped" \ + --author="Different Author <different@example.com>" && + cat >expect <<-\EOF && + author Mapped Author <mapped-author@example.com> + committer Mapped Committer <mapped-committer@example.com> + EOF + git cat-file --mailmap commit HEAD >log && + sed -n -e "/^author /s/>.*/>/p" -e "/^committer /s/>.*/>/p" log >actual && + test_cmp expect actual +' + test_done diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh index 950451cf6a..0a7c3ca42f 100755 --- a/t/t4211-line-log.sh +++ b/t/t4211-line-log.sh @@ -78,6 +78,8 @@ canned_test "-L :main:a.c -L 4,18:a.c simple" multiple-overlapping canned_test "-L 4:a.c -L 8,12:a.c simple" multiple-superset canned_test "-L 8,12:a.c -L 4:a.c simple" multiple-superset +canned_test "-L 10,16:b.c -L 18,26:b.c main" no-assertion-error + test_bad_opts "-L" "switch.*requires a value" test_bad_opts "-L b.c" "argument not .start,end:file" test_bad_opts "-L 1:" "argument not .start,end:file" diff --git a/t/t4211/sha1/expect.multiple b/t/t4211/sha1/expect.multiple index 76ad5b598c..1eee8a7801 100644 --- a/t/t4211/sha1/expect.multiple +++ b/t/t4211/sha1/expect.multiple @@ -102,3 +102,9 @@ diff --git a/a.c b/a.c + s++; + } +} +@@ -0,0 +16,5 @@ ++int main () ++{ ++ printf("%d\n", f(15)); ++ return 0; ++} diff --git a/t/t4211/sha1/expect.no-assertion-error b/t/t4211/sha1/expect.no-assertion-error new file mode 100644 index 0000000000..994c37db1e --- /dev/null +++ b/t/t4211/sha1/expect.no-assertion-error @@ -0,0 +1,90 @@ +commit 0d8dcfc6b968e06a27d5215bad1fdde3de9d6235 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:50:24 2013 +0100 + + move within the file + +diff --git a/b.c b/b.c +--- a/b.c ++++ b/b.c +@@ -25,0 +18,9 @@ ++long f(long x) ++{ ++ int s = 0; ++ while (x) { ++ x /= 2; ++ s++; ++ } ++ return s; ++} + +commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:43 2013 +0100 + + change back to complete line + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -18,5 +18,7 @@ + int main () + { + printf("%ld\n", f(15)); + return 0; +-} +\ No newline at end of file ++} ++ ++/* incomplete lines are bad! */ + +commit 100b61a6f2f720f812620a9d10afb3a960ccb73c +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:10 2013 +0100 + + change to an incomplete line at end + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -18,5 +18,5 @@ + int main () + { + printf("%ld\n", f(15)); + return 0; +-} ++} +\ No newline at end of file + +commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:16 2013 +0100 + + touch both functions + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -17,5 +17,5 @@ + int main () + { +- printf("%d\n", f(15)); ++ printf("%ld\n", f(15)); + return 0; + } + +commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:48 2013 +0100 + + initial + +diff --git a/a.c b/a.c +--- /dev/null ++++ b/a.c +@@ -0,0 +16,5 @@ ++int main () ++{ ++ printf("%d\n", f(15)); ++ return 0; ++} diff --git a/t/t4211/sha1/expect.two-ranges b/t/t4211/sha1/expect.two-ranges index 6109aa0dce..c5164f3be3 100644 --- a/t/t4211/sha1/expect.two-ranges +++ b/t/t4211/sha1/expect.two-ranges @@ -100,3 +100,9 @@ diff --git a/a.c b/a.c + s++; + } +} +@@ -0,0 +16,5 @@ ++int main () ++{ ++ printf("%d\n", f(15)); ++ return 0; ++} diff --git a/t/t4211/sha256/expect.multiple b/t/t4211/sha256/expect.multiple index ca00409b9a..dbd987b74a 100644 --- a/t/t4211/sha256/expect.multiple +++ b/t/t4211/sha256/expect.multiple @@ -102,3 +102,9 @@ diff --git a/a.c b/a.c + s++; + } +} +@@ -0,0 +16,5 @@ ++int main () ++{ ++ printf("%d\n", f(15)); ++ return 0; ++} diff --git a/t/t4211/sha256/expect.no-assertion-error b/t/t4211/sha256/expect.no-assertion-error new file mode 100644 index 0000000000..36ed12aa9c --- /dev/null +++ b/t/t4211/sha256/expect.no-assertion-error @@ -0,0 +1,90 @@ +commit eb871b8aa9aff323e484723039c9a92ab0266e060bc0ef2afb08fadda25c5ace +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:50:24 2013 +0100 + + move within the file + +diff --git a/b.c b/b.c +--- a/b.c ++++ b/b.c +@@ -25,0 +18,9 @@ ++long f(long x) ++{ ++ int s = 0; ++ while (x) { ++ x /= 2; ++ s++; ++ } ++ return s; ++} + +commit 5526ed05c2476b56af8b7be499e8f78bd50f490740733a9ca7e1f55878fa90a9 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:43 2013 +0100 + + change back to complete line + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -18,5 +18,7 @@ + int main () + { + printf("%ld\n", f(15)); + return 0; +-} +\ No newline at end of file ++} ++ ++/* incomplete lines are bad! */ + +commit 29f32ac3141c48b22803e5c4127b719917b67d0f8ca8c5248bebfa2a19f7da10 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:10 2013 +0100 + + change to an incomplete line at end + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -18,5 +18,5 @@ + int main () + { + printf("%ld\n", f(15)); + return 0; +-} ++} +\ No newline at end of file + +commit ccf97b9878189c40a981da50b15713bb80a35755326320ec80900caf22ced46f +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:16 2013 +0100 + + touch both functions + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -17,5 +17,5 @@ + int main () + { +- printf("%d\n", f(15)); ++ printf("%ld\n", f(15)); + return 0; + } + +commit 1dd7e9b2b1699324b53b341e728653b913bc192a14dfea168c5b51f2b3d03592 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:48 2013 +0100 + + initial + +diff --git a/a.c b/a.c +--- /dev/null ++++ b/a.c +@@ -0,0 +16,5 @@ ++int main () ++{ ++ printf("%d\n", f(15)); ++ return 0; ++} diff --git a/t/t4211/sha256/expect.two-ranges b/t/t4211/sha256/expect.two-ranges index af57c8b997..6a94d3b9cb 100644 --- a/t/t4211/sha256/expect.two-ranges +++ b/t/t4211/sha256/expect.two-ranges @@ -100,3 +100,9 @@ diff --git a/a.c b/a.c + s++; + } +} +@@ -0,0 +16,5 @@ ++int main () ++{ ++ printf("%d\n", f(15)); ++ return 0; ++} diff --git a/t/t4216-log-bloom.sh b/t/t4216-log-bloom.sh index 8910d53cac..1064990de3 100755 --- a/t/t4216-log-bloom.sh +++ b/t/t4216-log-bloom.sh @@ -66,8 +66,9 @@ sane_unset GIT_TRACE2_CONFIG_PARAMS setup () { rm -f "$TRASH_DIRECTORY/trace.perf" && - git -c core.commitGraph=false log --pretty="format:%s" $1 >log_wo_bloom && - GIT_TRACE2_PERF="$TRASH_DIRECTORY/trace.perf" git -c core.commitGraph=true log --pretty="format:%s" $1 >log_w_bloom + eval git -c core.commitGraph=false log --pretty="format:%s" "$1" >log_wo_bloom && + eval "GIT_TRACE2_PERF=\"$TRASH_DIRECTORY/trace.perf\"" \ + git -c core.commitGraph=true log --pretty="format:%s" "$1" >log_w_bloom } test_bloom_filters_used () { @@ -138,10 +139,6 @@ test_expect_success 'git log with --walk-reflogs does not use Bloom filters' ' test_bloom_filters_not_used "--walk-reflogs -- A" ' -test_expect_success 'git log -- multiple path specs does not use Bloom filters' ' - test_bloom_filters_not_used "-- file4 A/file1" -' - test_expect_success 'git log -- "." pathspec at root does not use Bloom filters' ' test_bloom_filters_not_used "-- ." ' @@ -151,9 +148,40 @@ test_expect_success 'git log with wildcard that resolves to a single path uses B test_bloom_filters_used "-- *renamed" ' -test_expect_success 'git log with wildcard that resolves to a multiple paths does not uses Bloom filters' ' - test_bloom_filters_not_used "-- *" && - test_bloom_filters_not_used "-- file*" +test_expect_success 'git log with multiple literal paths uses Bloom filter' ' + test_bloom_filters_used "-- file4 A/file1" && + test_bloom_filters_used "-- *" && + test_bloom_filters_used "-- file*" +' + +test_expect_success 'git log with paths all contain non-wildcard part uses Bloom filter' ' + test_bloom_filters_used "-- A/\* file4" && + test_bloom_filters_used "-- A/file\*" && + test_bloom_filters_used "-- * A/\*" +' + +test_expect_success 'git log with path only contains wildcard part does not use Bloom filter' ' + test_bloom_filters_not_used "-- file\*" && + test_bloom_filters_not_used "-- file\* A/\*" && + test_bloom_filters_not_used "-- file\* *" && + test_bloom_filters_not_used "-- \*" +' + +test_expect_success 'git log with path contains various magic signatures' ' + cd A && + test_bloom_filters_used "-- \:\(top\)B" && + cd .. && + + test_bloom_filters_used "-- \:\(glob\)A/\*\*/C" && + test_bloom_filters_not_used "-- \:\(icase\)FILE4" && + test_bloom_filters_not_used "-- \:\(exclude\)A/B/C" && + + test_when_finished "rm -f .gitattributes" && + cat >.gitattributes <<-EOF && + A/file1 text + A/B/file2 -text + EOF + test_bloom_filters_used "-- \:\(attr\:text\)A" ' test_expect_success 'setup - add commit-graph to the chain without Bloom filters' ' diff --git a/t/t4256/1/mailinfo.c b/t/t4256/1/mailinfo.c index b395adbdf2..39caeba865 100644 --- a/t/t4256/1/mailinfo.c +++ b/t/t4256/1/mailinfo.c @@ -1214,7 +1214,7 @@ void setup_mailinfo(struct mailinfo *mi) mi->header_stage = 1; mi->use_inbody_headers = 1; mi->content_top = mi->content; - git_config(git_mailinfo_config, mi); + repo_config(the_repository, git_mailinfo_config, mi); } void clear_mailinfo(struct mailinfo *mi) diff --git a/t/t4256/1/mailinfo.c.orig b/t/t4256/1/mailinfo.c.orig index 3281a37d51..b76eb866aa 100644 --- a/t/t4256/1/mailinfo.c.orig +++ b/t/t4256/1/mailinfo.c.orig @@ -1154,7 +1154,7 @@ void setup_mailinfo(struct mailinfo *mi) mi->header_stage = 1; mi->use_inbody_headers = 1; mi->content_top = mi->content; - git_config(git_mailinfo_config, mi); + repo_config(the_repository, git_mailinfo_config, mi); } void clear_mailinfo(struct mailinfo *mi) diff --git a/t/t5004-archive-corner-cases.sh b/t/t5004-archive-corner-cases.sh index 5174995191..027dedd976 100755 --- a/t/t5004-archive-corner-cases.sh +++ b/t/t5004-archive-corner-cases.sh @@ -176,10 +176,7 @@ test_expect_success EXPENSIVE,UNZIP,UNZIP_ZIP64_SUPPORT \ blob=$(echo $s | git hash-object -w --stdin) && # create tree containing 65500 entries of that blob - for i in $(test_seq 1 65500) - do - echo "100644 blob $blob $i" || return 1 - done >tree && + test_seq -f "100644 blob $blob\t%d" 1 65500 >tree && tree=$(git mktree <tree) && # zip it, creating an archive a bit bigger than 4GB diff --git a/t/t5200-update-server-info.sh b/t/t5200-update-server-info.sh index 8365907055..a551e955b5 100755 --- a/t/t5200-update-server-info.sh +++ b/t/t5200-update-server-info.sh @@ -46,4 +46,9 @@ test_expect_success 'midx does not create duplicate pack entries' ' test_must_be_empty dups ' +test_expect_success 'update-server-info does not crash with -h' ' + test_expect_code 129 git update-server-info -h >usage && + test_grep "[Uu]sage: git update-server-info " usage +' + test_done diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index a5932b6a8b..73445782e7 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -525,7 +525,7 @@ test_expect_success 'index-pack --strict <pack> works in non-repo' ' test_path_is_file foo.idx ' -test_expect_success SHA1 'show-index works OK outside a repository' ' +test_expect_success DEFAULT_HASH_ALGORITHM 'show-index works OK outside a repository' ' nongit git show-index <foo.idx ' @@ -658,7 +658,7 @@ do test_commit -C repo initial && git -C repo repack -ad && git -C repo verify-pack "$(pwd)"/repo/.git/objects/pack/*.idx && - if test $hash = sha1 + if test $hash = $GIT_TEST_BUILTIN_HASH then nongit git verify-pack "$(pwd)"/repo/.git/objects/pack/*.idx else @@ -676,7 +676,7 @@ do test_commit -C repo initial && git -C repo repack -ad && git -C repo index-pack --verify "$(pwd)"/repo/.git/objects/pack/*.pack && - if test $hash = sha1 + if test $hash = $GIT_TEST_BUILTIN_HASH then nongit git index-pack --verify "$(pwd)"/repo/.git/objects/pack/*.pack else @@ -723,4 +723,23 @@ test_expect_success '--name-hash-version=2 and --write-bitmap-index are incompat ! test_grep "currently, --write-bitmap-index requires --name-hash-version=1" err ' +test_expect_success '--path-walk pack everything' ' + git -C server rev-parse HEAD >in && + GIT_PROGRESS_DELAY=0 git -C server pack-objects \ + --stdout --revs --path-walk --progress <in >out.pack 2>err && + grep "Compressing objects by path" err && + git -C server index-pack --stdin <out.pack +' + +test_expect_success '--path-walk thin pack' ' + cat >in <<-EOF && + $(git -C server rev-parse HEAD) + ^$(git -C server rev-parse HEAD~2) + EOF + GIT_PROGRESS_DELAY=0 git -C server pack-objects \ + --thin --stdout --revs --path-walk --progress <in >out.pack 2>err && + grep "Compressing objects by path" err && + git -C server index-pack --fix-thin --stdin <out.pack +' + test_done diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh index 1f1f664871..2be7cd30de 100755 --- a/t/t5304-prune.sh +++ b/t/t5304-prune.sh @@ -364,4 +364,9 @@ test_expect_success 'gc.recentObjectsHook' ' git cat-file -p $BLOB ' +test_expect_success 'prune does not crash with -h' ' + test_expect_code 129 git prune -h >usage && + test_grep "[Uu]sage: git prune " usage +' + test_done diff --git a/t/t5306-pack-nobase.sh b/t/t5306-pack-nobase.sh index 805d60ff31..609399d54f 100755 --- a/t/t5306-pack-nobase.sh +++ b/t/t5306-pack-nobase.sh @@ -59,6 +59,11 @@ test_expect_success 'indirectly clone patch_clone' ' git pull ../.git && test $(git rev-parse HEAD) = $B && + # The --path-walk feature of "git pack-objects" is not + # compatible with this kind of fetch from an incomplete repo. + GIT_TEST_PACK_PATH_WALK=0 && + export GIT_TEST_PACK_PATH_WALK && + git pull ../patch_clone/.git && test $(git rev-parse HEAD) = $C ) diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh index a62b463eaf..6718fb98c0 100755 --- a/t/t5310-pack-bitmaps.sh +++ b/t/t5310-pack-bitmaps.sh @@ -158,8 +158,9 @@ test_bitmap_cases () { ls .git/objects/pack/ | grep bitmap >output && test_line_count = 1 output && # verify equivalent packs are generated with/without using bitmap index - packasha1=$(git pack-objects --no-use-bitmap-index --all packa </dev/null) && - packbsha1=$(git pack-objects --use-bitmap-index --all packb </dev/null) && + # Be careful to not use the path-walk option in either case. + packasha1=$(git pack-objects --no-use-bitmap-index --no-path-walk --all packa </dev/null) && + packbsha1=$(git pack-objects --use-bitmap-index --no-path-walk --all packb </dev/null) && list_packed_objects packa-$packasha1.idx >packa.objects && list_packed_objects packb-$packbsha1.idx >packb.objects && test_cmp packa.objects packb.objects @@ -388,6 +389,14 @@ test_bitmap_cases () { git init --bare client.git && ( cd client.git && + + # This test relies on reusing a delta, but if the + # path-walk machinery is engaged, the base object + # is considered too small to use during the + # dynamic computation, so is not used. + GIT_TEST_PACK_PATH_WALK=0 && + export GIT_TEST_PACK_PATH_WALK && + git config transfer.unpackLimit 1 && git fetch .. delta-reuse-old:delta-reuse-old && git fetch .. delta-reuse-new:delta-reuse-new && @@ -486,6 +495,36 @@ test_bitmap_cases () { grep "ignoring extra bitmap" trace2.txt ) ' + + test_expect_success 'load corrupt bitmap' ' + rm -fr repo && + git init repo && + test_when_finished "rm -fr repo" && + ( + cd repo && + git config pack.writeBitmapLookupTable '"$writeLookupTable"' && + + test_commit base && + + git repack -adb && + bitmap="$(ls .git/objects/pack/pack-*.bitmap)" && + chmod +w $bitmap && + + test-tool bitmap list-commits-with-offset >offsets && + xor_off=$(head -n1 offsets | awk "{print \$3}") && + printf '\161' | + dd of=$bitmap count=1 bs=1 conv=notrunc seek=$xor_off && + + git rev-list --objects --no-object-names HEAD >expect.raw && + git rev-list --objects --use-bitmap-index --no-object-names HEAD \ + >actual.raw && + + sort expect.raw >expect && + sort actual.raw >actual && + + test_cmp expect actual + ) + ' } test_bitmap_cases diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh index defaa06d65..03dfb7a61e 100755 --- a/t/t5316-pack-delta-depth.sh +++ b/t/t5316-pack-delta-depth.sh @@ -89,15 +89,18 @@ max_chain() { # adjusted (or scrapped if the heuristics have become too unreliable) test_expect_success 'packing produces a long delta' ' # Use --window=0 to make sure we are seeing reused deltas, - # not computing a new long chain. - pack=$(git pack-objects --all --window=0 </dev/null pack) && + # not computing a new long chain. (Also avoid the --path-walk + # option as it may break delta chains.) + pack=$(git pack-objects --all --window=0 --no-path-walk </dev/null pack) && echo 9 >expect && max_chain pack-$pack.pack >actual && test_cmp expect actual ' test_expect_success '--depth limits depth' ' - pack=$(git pack-objects --all --depth=5 </dev/null pack) && + # Avoid --path-walk to avoid breaking delta chains across path + # boundaries. + pack=$(git pack-objects --all --depth=5 --no-path-walk </dev/null pack) && echo 5 >expect && max_chain pack-$pack.pack >actual && test_cmp expect actual diff --git a/t/t5319-multi-pack-index.sh b/t/t5319-multi-pack-index.sh index bd75dea950..93f319a4b2 100755 --- a/t/t5319-multi-pack-index.sh +++ b/t/t5319-multi-pack-index.sh @@ -28,11 +28,11 @@ midx_read_expect () { EOF if test $NUM_PACKS -ge 1 then - ls $OBJECT_DIR/pack/ | grep idx | sort + ls "$OBJECT_DIR"/pack/ | grep idx | sort fi && printf "object-dir: $OBJECT_DIR\n" } >expect && - test-tool read-midx $OBJECT_DIR >actual && + test-tool read-midx "$OBJECT_DIR" >actual && test_cmp expect actual } @@ -305,7 +305,7 @@ test_expect_success 'midx picks objects from preferred pack' ' ofs=$(git show-index <objects/pack/test-BC-$bc.idx | grep $b | cut -d" " -f1) && - printf "%s %s\tobjects/pack/test-BC-%s.pack\n" \ + printf "%s %s\t./objects/pack/test-BC-%s.pack\n" \ "$b" "$ofs" "$bc" >expect && grep ^$b out >actual && @@ -639,7 +639,7 @@ test_expect_success 'force some 64-bit offsets with pack-objects' ' ( cd ../objects64 && pwd ) >.git/objects/info/alternates && midx64=$(git multi-pack-index --object-dir=../objects64 write) ) && - midx_read_expect 1 63 5 objects64 " large-offsets" + midx_read_expect 1 63 5 "$(pwd)/objects64" " large-offsets" ' test_expect_success 'verify multi-pack-index with 64-bit offsets' ' @@ -989,6 +989,23 @@ test_expect_success 'repack --batch-size=0 repacks everything' ' ) ' +test_expect_success EXPENSIVE 'repack/expire with many packs' ' + cp -r dup many && + ( + cd many && + + for i in $(test_seq 1 100) + do + test_commit extra$i && + git maintenance run --task=loose-objects || return 1 + done && + + git multi-pack-index write && + git multi-pack-index repack && + git multi-pack-index expire + ) +' + test_expect_success 'repack --batch-size=<large> repacks everything' ' ( cd dup2 && @@ -1083,7 +1100,10 @@ test_expect_success 'load reverse index when missing .idx, .pack' ' mv $idx.bak $idx && mv $pack $pack.bak && - git cat-file --batch-check="%(objectsize:disk)" <tip + git cat-file --batch-check="%(objectsize:disk)" <tip && + + test_must_fail git multi-pack-index write 2>err && + test_grep "could not load pack" err ) ' diff --git a/t/t5323-pack-redundant.sh b/t/t5323-pack-redundant.sh index bc30bc9652..2d96afd6f7 100755 --- a/t/t5323-pack-redundant.sh +++ b/t/t5323-pack-redundant.sh @@ -45,6 +45,11 @@ fi main_repo=main.git shared_repo=shared.git +test_expect_success 'pack-redundant needs --i-still-use-this' ' + test_must_fail git pack-redundant >message 2>&1 && + test_grep "nominated for removal" message +' + git_pack_redundant='git pack-redundant --i-still-use-this' # Create commits in <repo> and assign each commit's oid to shell variables diff --git a/t/t5331-pack-objects-stdin.sh b/t/t5331-pack-objects-stdin.sh index b48c0cbe8f..4a8df5a389 100755 --- a/t/t5331-pack-objects-stdin.sh +++ b/t/t5331-pack-objects-stdin.sh @@ -64,7 +64,7 @@ test_expect_success '--stdin-packs is incompatible with --filter' ' cd stdin-packs && test_must_fail git pack-objects --stdin-packs --stdout \ --filter=blob:none </dev/null 2>err && - test_grep "cannot use --filter with --stdin-packs" err + test_grep "options .--stdin-packs. and .--filter. cannot be used together" err ) ' @@ -236,4 +236,124 @@ test_expect_success 'pack-objects --stdin with packfiles from main and alternate test_cmp expected-objects actual-objects ' +objdir=.git/objects +packdir=$objdir/pack + +objects_in_packs () { + for p in "$@" + do + git show-index <"$packdir/pack-$p.idx" || return 1 + done >objects.raw && + + cut -d' ' -f2 objects.raw | sort && + rm -f objects.raw +} + +test_expect_success '--stdin-packs=follow walks into unknown packs' ' + test_when_finished "rm -fr repo" && + + git init repo && + ( + cd repo && + + for c in A B C D + do + test_commit "$c" || return 1 + done && + + 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 C..D | git pack-objects --revs $packdir/pack)" && + test_commit E && + + git prune-packed && + + cat >in <<-EOF && + pack-$B.pack + ^pack-$C.pack + pack-$D.pack + EOF + + # With just --stdin-packs, pack "A" is unknown to us, so + # only objects from packs "B" and "D" are included in + # the output pack. + P=$(git pack-objects --stdin-packs $packdir/pack <in) && + objects_in_packs $B $D >expect && + objects_in_packs $P >actual && + test_cmp expect actual && + + # But with --stdin-packs=follow, objects from both + # included packs reach objects from the unknown pack, so + # objects from pack "A" is included in the output pack + # in addition to the above. + P=$(git pack-objects --stdin-packs=follow $packdir/pack <in) && + objects_in_packs $A $B $D >expect && + objects_in_packs $P >actual && + test_cmp expect actual && + + # And with --unpacked, we will pick up objects from unknown + # packs that are reachable from loose objects. Loose object E + # reaches objects in pack A, but there are three excluded packs + # in between. + # + # The resulting pack should include objects reachable from E + # that are not present in packs B, C, or D, along with those + # present in pack A. + cat >in <<-EOF && + ^pack-$B.pack + ^pack-$C.pack + ^pack-$D.pack + EOF + + P=$(git pack-objects --stdin-packs=follow --unpacked \ + $packdir/pack <in) && + + { + objects_in_packs $A && + git rev-list --objects --no-object-names D..E + }>expect.raw && + sort expect.raw >expect && + objects_in_packs $P >actual && + test_cmp expect actual + ) +' + +stdin_packs__follow_with_only () { + rm -fr stdin_packs__follow_with_only && + git init stdin_packs__follow_with_only && + ( + cd stdin_packs__follow_with_only && + + test_commit A && + test_commit B && + + git rev-parse "$@" >B.objects && + + echo A | git pack-objects --revs $packdir/pack && + B="$(git pack-objects $packdir/pack <B.objects)" && + + git cat-file --batch-check="%(objectname)" --batch-all-objects >objs && + for obj in $(cat objs) + do + rm -f $objdir/$(test_oid_to_path $obj) || return 1 + done && + + ( cd $packdir && ls pack-*.pack ) >in && + git pack-objects --stdin-packs=follow --stdout >/dev/null <in + ) +} + +test_expect_success '--stdin-packs=follow tolerates missing blobs' ' + stdin_packs__follow_with_only HEAD HEAD^{tree} +' + +test_expect_success '--stdin-packs=follow tolerates missing trees' ' + stdin_packs__follow_with_only HEAD HEAD:B.t +' + +test_expect_success '--stdin-packs=follow tolerates missing commits' ' + stdin_packs__follow_with_only HEAD HEAD^{tree} +' + test_done diff --git a/t/t5332-multi-pack-reuse.sh b/t/t5332-multi-pack-reuse.sh index 57cad7708f..395d09444c 100755 --- a/t/t5332-multi-pack-reuse.sh +++ b/t/t5332-multi-pack-reuse.sh @@ -7,6 +7,13 @@ test_description='pack-objects multi-pack reuse' GIT_TEST_MULTI_PACK_INDEX=0 GIT_TEST_MULTI_PACK_INDEX_WRITE_INCREMENTAL=0 + +# The --path-walk option does not consider the preferred pack +# at all for reusing deltas, so this variable changes the +# behavior of this test, if enabled. +GIT_TEST_PACK_PATH_WALK=0 +export GIT_TEST_PACK_PATH_WALK + objdir=.git/objects packdir=$objdir/pack diff --git a/t/t5333-pseudo-merge-bitmaps.sh b/t/t5333-pseudo-merge-bitmaps.sh index 56674db562..1f7a5d82ee 100755 --- a/t/t5333-pseudo-merge-bitmaps.sh +++ b/t/t5333-pseudo-merge-bitmaps.sh @@ -234,8 +234,8 @@ test_expect_success 'pseudo-merge pattern with capture groups' ' test_commit_bulk 16 && git rev-list HEAD~16.. >in && - sed "s|\(.*\)|create refs/remotes/$r/tags/\1 \1" in | - git update-ref --stdin || return 1 + sed "s|\(.*\)|create refs/remotes/$r/tags/\1 \1|" in >refs && + git update-ref --stdin <refs || return 1 done && git \ @@ -445,4 +445,21 @@ test_expect_success 'pseudo-merge closure' ' ) ' +test_expect_success 'use pseudo-merge in boundary traversal' ' + git init pseudo-merge-boundary-traversal && + ( + cd pseudo-merge-boundary-traversal && + + git config bitmapPseudoMerge.test.pattern refs/ && + git config pack.useBitmapBoundaryTraversal true && + + test_commit A && + git repack -adb && + test_commit B && + + nr=$(git rev-list --count --use-bitmap-index HEAD~1..HEAD) && + test 1 -eq "$nr" + ) +' + test_done diff --git a/t/t5408-send-pack-stdin.sh b/t/t5408-send-pack-stdin.sh index 526a675045..ec339761c2 100755 --- a/t/t5408-send-pack-stdin.sh +++ b/t/t5408-send-pack-stdin.sh @@ -69,15 +69,24 @@ test_expect_success 'stdin mixed with cmdline' ' test_expect_success 'cmdline refs written in order' ' clear_remote && - test_must_fail git send-pack remote.git A:foo B:foo && - verify_push A foo + test_must_fail git send-pack remote.git A:foo B:foo 2>err && + test_grep "multiple updates for ref ${SQ}refs/heads/foo${SQ} not allowed" err && + test_must_fail git --git-dir=remote.git rev-parse foo +' + +test_expect_success 'cmdline refs with multiple duplicates' ' + clear_remote && + test_must_fail git send-pack remote.git A:foo B:foo C:foo 2>err && + test_grep "multiple updates for ref ${SQ}refs/heads/foo${SQ} not allowed" err && + test_must_fail git --git-dir=remote.git rev-parse foo ' test_expect_success '--stdin refs come after cmdline' ' clear_remote && echo A:foo >input && test_must_fail git send-pack remote.git --stdin B:foo <input && - verify_push B foo + test_grep "multiple updates for ref ${SQ}refs/heads/foo${SQ} not allowed" err && + test_must_fail git --git-dir=remote.git rev-parse foo ' test_expect_success 'refspecs and --mirror do not mix (cmdline)' ' diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index bef0250e89..e592c0bcde 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -1644,4 +1644,91 @@ test_expect_success 'empty config clears remote.*.pushurl list' ' test_cmp expect actual ' +test_expect_success 'forbid adding subset of existing remote' ' + test_when_finished "git remote rm outer" && + git remote add outer url && + test_must_fail git remote add outer/inner url 2>err && + test_grep ".outer/inner. is a subset of existing remote .outer." err +' + +test_expect_success 'forbid adding superset of existing remote' ' + test_when_finished "git remote rm outer/inner" && + git remote add outer/inner url && + test_must_fail git remote add outer url 2>err && + test_grep ".outer. is a superset of existing remote .outer/inner." err +' + +test_expect_success 'rename handles unborn HEAD' ' + test_when_finished "git remote remove unborn-renamed" && + git remote add unborn url && + git symbolic-ref refs/remotes/unborn/HEAD refs/remotes/unborn/nonexistent && + git remote rename unborn unborn-renamed && + git symbolic-ref refs/remotes/unborn-renamed/HEAD >actual && + echo refs/remotes/unborn-renamed/nonexistent >expected && + test_cmp expected actual +' + +test_expect_success 'rename can nest a remote into itself' ' + test_commit parent-commit && + COMMIT_ID=$(git rev-parse HEAD) && + test_when_finished "git remote remove parent || true" && + git remote add parent url && + git update-ref refs/remotes/parent/branch $COMMIT_ID && + test_when_finished "git remote remove parent/child" && + git remote rename parent parent/child && + git for-each-ref refs/remotes/ >actual && + printf "$COMMIT_ID commit\trefs/remotes/parent/child/branch\n" >expected && + test_cmp expected actual +' + +test_expect_success 'rename can nest a remote into itself with a conflicting branch name' ' + test_commit parent-conflict && + COMMIT_ID=$(git rev-parse HEAD) && + test_when_finished "git remote remove parent || true" && + git remote add parent url && + git update-ref refs/remotes/parent/child $COMMIT_ID && + test_when_finished "git remote remove parent/child" && + test_must_fail git remote rename parent parent/child 2>err && + test_grep "renaming remote references failed" err && + test_grep "The remote you are trying to rename has conflicting references" err && + git for-each-ref refs/remotes/ >actual && + printf "$COMMIT_ID commit\trefs/remotes/parent/child\n" >expected && + test_cmp expected actual +' + +test_expect_success 'rename can unnest a remote' ' + test_commit parent-child-commit && + COMMIT_ID=$(git rev-parse HEAD) && + test_when_finished "git remote remove parent/child || true" && + git remote add parent/child url && + git update-ref refs/remotes/parent/child/branch $COMMIT_ID && + git remote rename parent/child parent && + git for-each-ref refs/remotes/ >actual && + printf "$COMMIT_ID commit\trefs/remotes/parent/branch\n" >expected && + test_cmp expected actual +' + +test_expect_success 'rename moves around the reflog' ' + test_commit reflog-old && + COMMIT_ID=$(git rev-parse HEAD) && + test_config core.logAllRefUpdates true && + test_when_finished "git remote remove reflog-old || true" && + git remote add reflog-old url && + git update-ref refs/remotes/reflog-old/branch $COMMIT_ID && + test-tool ref-store main for-each-reflog >actual && + test_grep refs/remotes/reflog-old/branch actual && + test-tool ref-store main for-each-reflog-ent refs/remotes/reflog-old/branch >reflog-entries-old && + test_line_count = 1 reflog-entries-old && + git remote rename reflog-old reflog-new && + test-tool ref-store main for-each-reflog >actual && + test_grep ! refs/remotes/reflog-old actual && + test_grep refs/remotes/reflog-new/branch actual && + test-tool ref-store main for-each-reflog-ent refs/remotes/reflog-new/branch >reflog-entries-new && + cat >expect <<-EOF && + $(cat reflog-entries-old) + $COMMIT_ID $COMMIT_ID $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1112912173 -0700 remote: renamed refs/remotes/reflog-old/branch to refs/remotes/reflog-new/branch + EOF + test_cmp expect reflog-entries-new +' + test_done diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index ebc696546b..83d1aadf9f 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -14,8 +14,6 @@ then test_done fi -D=$(pwd) - test_expect_success setup ' echo >file original && git add file && @@ -51,46 +49,50 @@ test_expect_success "clone and setup child repos" ' ' test_expect_success "fetch test" ' - cd "$D" && echo >file updated by origin && git commit -a -m "updated by origin" && - cd two && - git fetch && - git rev-parse --verify refs/heads/one && - mine=$(git rev-parse refs/heads/one) && - his=$(cd ../one && git rev-parse refs/heads/main) && - test "z$mine" = "z$his" + ( + cd two && + git fetch && + git rev-parse --verify refs/heads/one && + mine=$(git rev-parse refs/heads/one) && + his=$(cd ../one && git rev-parse refs/heads/main) && + test "z$mine" = "z$his" + ) ' test_expect_success "fetch test for-merge" ' - cd "$D" && - cd three && - git fetch && - git rev-parse --verify refs/heads/two && - git rev-parse --verify refs/heads/one && - main_in_two=$(cd ../two && git rev-parse main) && - one_in_two=$(cd ../two && git rev-parse one) && - { - echo "$one_in_two " && - echo "$main_in_two not-for-merge" - } >expected && - cut -f -2 .git/FETCH_HEAD >actual && - test_cmp expected actual' + ( + cd three && + git fetch && + git rev-parse --verify refs/heads/two && + git rev-parse --verify refs/heads/one && + main_in_two=$(cd ../two && git rev-parse main) && + one_in_two=$(cd ../two && git rev-parse one) && + { + echo "$one_in_two " && + echo "$main_in_two not-for-merge" + } >expected && + cut -f -2 .git/FETCH_HEAD >actual && + test_cmp expected actual + ) +' test_expect_success "fetch test remote HEAD" ' - cd "$D" && - cd two && - git fetch && - git rev-parse --verify refs/remotes/origin/HEAD && - git rev-parse --verify refs/remotes/origin/main && - head=$(git rev-parse refs/remotes/origin/HEAD) && - branch=$(git rev-parse refs/remotes/origin/main) && - test "z$head" = "z$branch"' + ( + cd two && + git fetch && + git rev-parse --verify refs/remotes/origin/HEAD && + git rev-parse --verify refs/remotes/origin/main && + head=$(git rev-parse refs/remotes/origin/HEAD) && + branch=$(git rev-parse refs/remotes/origin/main) && + test "z$head" = "z$branch" + ) +' test_expect_success "fetch test remote HEAD in bare repository" ' test_when_finished rm -rf barerepo && ( - cd "$D" && git init --bare barerepo && cd barerepo && git remote add upstream ../two && @@ -105,262 +107,235 @@ test_expect_success "fetch test remote HEAD in bare repository" ' test_expect_success "fetch test remote HEAD change" ' - cd "$D" && - cd two && - git switch -c other && - git push -u origin other && - git rev-parse --verify refs/remotes/origin/HEAD && - git rev-parse --verify refs/remotes/origin/main && - git rev-parse --verify refs/remotes/origin/other && - git remote set-head origin other && - git fetch && - head=$(git rev-parse refs/remotes/origin/HEAD) && - branch=$(git rev-parse refs/remotes/origin/other) && - test "z$head" = "z$branch"' - -test_expect_success "fetch test followRemoteHEAD never" ' - test_when_finished "git config unset remote.origin.followRemoteHEAD" && - ( - cd "$D" && - cd two && - git update-ref --no-deref -d refs/remotes/origin/HEAD && - git config set remote.origin.followRemoteHEAD "never" && - GIT_TRACE_PACKET=$PWD/trace.out git fetch && - # Confirm that we do not even ask for HEAD when we are - # not going to act on it. - test_grep ! "ref-prefix HEAD" trace.out && - test_must_fail git rev-parse --verify refs/remotes/origin/HEAD - ) -' - -test_expect_success "fetch test followRemoteHEAD warn no change" ' - test_when_finished "git config unset remote.origin.followRemoteHEAD" && ( - cd "$D" && cd two && - git rev-parse --verify refs/remotes/origin/other && - git remote set-head origin other && + git switch -c other && + git push -u origin other && git rev-parse --verify refs/remotes/origin/HEAD && git rev-parse --verify refs/remotes/origin/main && - git config set remote.origin.followRemoteHEAD "warn" && - git fetch >output && - echo "${SQ}HEAD${SQ} at ${SQ}origin${SQ} is ${SQ}main${SQ}," \ - "but we have ${SQ}other${SQ} locally." >expect && - test_cmp expect output && + git rev-parse --verify refs/remotes/origin/other && + git remote set-head origin other && + git fetch && head=$(git rev-parse refs/remotes/origin/HEAD) && branch=$(git rev-parse refs/remotes/origin/other) && test "z$head" = "z$branch" ) ' +test_expect_success "fetch test followRemoteHEAD never" ' + git -C two update-ref --no-deref -d refs/remotes/origin/HEAD && + test_config -C two remote.origin.followRemoteHEAD "never" && + GIT_TRACE_PACKET=$PWD/trace.out git -C two fetch && + # Confirm that we do not even ask for HEAD when we are + # not going to act on it. + test_grep ! "ref-prefix HEAD" trace.out && + test_must_fail git -C two rev-parse --verify refs/remotes/origin/HEAD +' + +test_expect_success "fetch test followRemoteHEAD warn no change" ' + git -C two rev-parse --verify refs/remotes/origin/other && + git -C two remote set-head origin other && + git -C two rev-parse --verify refs/remotes/origin/HEAD && + git -C two rev-parse --verify refs/remotes/origin/main && + test_config -C two remote.origin.followRemoteHEAD "warn" && + git -C two fetch >output && + echo "${SQ}HEAD${SQ} at ${SQ}origin${SQ} is ${SQ}main${SQ}," \ + "but we have ${SQ}other${SQ} locally." >expect && + test_cmp expect output && + head=$(git -C two rev-parse refs/remotes/origin/HEAD) && + branch=$(git -C two rev-parse refs/remotes/origin/other) && + test "z$head" = "z$branch" +' + test_expect_success "fetch test followRemoteHEAD warn create" ' - test_when_finished "git config unset remote.origin.followRemoteHEAD" && - ( - cd "$D" && - cd two && - git update-ref --no-deref -d refs/remotes/origin/HEAD && - git config set remote.origin.followRemoteHEAD "warn" && - git rev-parse --verify refs/remotes/origin/main && - output=$(git fetch) && - test "z" = "z$output" && - head=$(git rev-parse refs/remotes/origin/HEAD) && - branch=$(git rev-parse refs/remotes/origin/main) && - test "z$head" = "z$branch" - ) + git -C two update-ref --no-deref -d refs/remotes/origin/HEAD && + test_config -C two remote.origin.followRemoteHEAD "warn" && + git -C two rev-parse --verify refs/remotes/origin/main && + output=$(git -C two fetch) && + test "z" = "z$output" && + head=$(git -C two rev-parse refs/remotes/origin/HEAD) && + branch=$(git -C two rev-parse refs/remotes/origin/main) && + test "z$head" = "z$branch" ' test_expect_success "fetch test followRemoteHEAD warn detached" ' - test_when_finished "git config unset remote.origin.followRemoteHEAD" && - ( - cd "$D" && - cd two && - git update-ref --no-deref -d refs/remotes/origin/HEAD && - git update-ref refs/remotes/origin/HEAD HEAD && - HEAD=$(git log --pretty="%H") && - git config set remote.origin.followRemoteHEAD "warn" && - git fetch >output && - echo "${SQ}HEAD${SQ} at ${SQ}origin${SQ} is ${SQ}main${SQ}," \ - "but we have a detached HEAD pointing to" \ - "${SQ}${HEAD}${SQ} locally." >expect && - test_cmp expect output - ) + git -C two update-ref --no-deref -d refs/remotes/origin/HEAD && + git -C two update-ref refs/remotes/origin/HEAD HEAD && + HEAD=$(git -C two log --pretty="%H") && + test_config -C two remote.origin.followRemoteHEAD "warn" && + git -C two fetch >output && + echo "${SQ}HEAD${SQ} at ${SQ}origin${SQ} is ${SQ}main${SQ}," \ + "but we have a detached HEAD pointing to" \ + "${SQ}${HEAD}${SQ} locally." >expect && + test_cmp expect output ' test_expect_success "fetch test followRemoteHEAD warn quiet" ' - test_when_finished "git config unset remote.origin.followRemoteHEAD" && - ( - cd "$D" && - cd two && - git rev-parse --verify refs/remotes/origin/other && - git remote set-head origin other && - git rev-parse --verify refs/remotes/origin/HEAD && - git rev-parse --verify refs/remotes/origin/main && - git config set remote.origin.followRemoteHEAD "warn" && - output=$(git fetch --quiet) && - test "z" = "z$output" && - head=$(git rev-parse refs/remotes/origin/HEAD) && - branch=$(git rev-parse refs/remotes/origin/other) && - test "z$head" = "z$branch" - ) + git -C two rev-parse --verify refs/remotes/origin/other && + git -C two remote set-head origin other && + git -C two rev-parse --verify refs/remotes/origin/HEAD && + git -C two rev-parse --verify refs/remotes/origin/main && + test_config -C two remote.origin.followRemoteHEAD "warn" && + output=$(git -C two fetch --quiet) && + test "z" = "z$output" && + head=$(git -C two rev-parse refs/remotes/origin/HEAD) && + branch=$(git -C two rev-parse refs/remotes/origin/other) && + test "z$head" = "z$branch" ' test_expect_success "fetch test followRemoteHEAD warn-if-not-branch branch is same" ' - test_when_finished "git config unset remote.origin.followRemoteHEAD" && - ( - cd "$D" && - cd two && - git rev-parse --verify refs/remotes/origin/other && - git remote set-head origin other && - git rev-parse --verify refs/remotes/origin/HEAD && - git rev-parse --verify refs/remotes/origin/main && - git config set remote.origin.followRemoteHEAD "warn-if-not-main" && - actual=$(git fetch) && - test "z" = "z$actual" && - head=$(git rev-parse refs/remotes/origin/HEAD) && - branch=$(git rev-parse refs/remotes/origin/other) && - test "z$head" = "z$branch" - ) + git -C two rev-parse --verify refs/remotes/origin/other && + git -C two remote set-head origin other && + git -C two rev-parse --verify refs/remotes/origin/HEAD && + git -C two rev-parse --verify refs/remotes/origin/main && + test_config -C two remote.origin.followRemoteHEAD "warn-if-not-main" && + actual=$(git -C two fetch) && + test "z" = "z$actual" && + head=$(git -C two rev-parse refs/remotes/origin/HEAD) && + branch=$(git -C two rev-parse refs/remotes/origin/other) && + test "z$head" = "z$branch" ' test_expect_success "fetch test followRemoteHEAD warn-if-not-branch branch is different" ' - test_when_finished "git config unset remote.origin.followRemoteHEAD" && - ( - cd "$D" && - cd two && - git rev-parse --verify refs/remotes/origin/other && - git remote set-head origin other && - git rev-parse --verify refs/remotes/origin/HEAD && - git rev-parse --verify refs/remotes/origin/main && - git config set remote.origin.followRemoteHEAD "warn-if-not-some/different-branch" && - git fetch >actual && - echo "${SQ}HEAD${SQ} at ${SQ}origin${SQ} is ${SQ}main${SQ}," \ - "but we have ${SQ}other${SQ} locally." >expect && - test_cmp expect actual && - head=$(git rev-parse refs/remotes/origin/HEAD) && - branch=$(git rev-parse refs/remotes/origin/other) && - test "z$head" = "z$branch" - ) + git -C two rev-parse --verify refs/remotes/origin/other && + git -C two remote set-head origin other && + git -C two rev-parse --verify refs/remotes/origin/HEAD && + git -C two rev-parse --verify refs/remotes/origin/main && + test_config -C two remote.origin.followRemoteHEAD "warn-if-not-some/different-branch" && + git -C two fetch >actual && + echo "${SQ}HEAD${SQ} at ${SQ}origin${SQ} is ${SQ}main${SQ}," \ + "but we have ${SQ}other${SQ} locally." >expect && + test_cmp expect actual && + head=$(git -C two rev-parse refs/remotes/origin/HEAD) && + branch=$(git -C two rev-parse refs/remotes/origin/other) && + test "z$head" = "z$branch" ' test_expect_success "fetch test followRemoteHEAD always" ' - test_when_finished "git config unset remote.origin.followRemoteHEAD" && - ( - cd "$D" && - cd two && - git rev-parse --verify refs/remotes/origin/other && - git remote set-head origin other && - git rev-parse --verify refs/remotes/origin/HEAD && - git rev-parse --verify refs/remotes/origin/main && - git config set remote.origin.followRemoteHEAD "always" && - git fetch && - head=$(git rev-parse refs/remotes/origin/HEAD) && - branch=$(git rev-parse refs/remotes/origin/main) && - test "z$head" = "z$branch" - ) + git -C two rev-parse --verify refs/remotes/origin/other && + git -C two remote set-head origin other && + git -C two rev-parse --verify refs/remotes/origin/HEAD && + git -C two rev-parse --verify refs/remotes/origin/main && + test_config -C two remote.origin.followRemoteHEAD "always" && + git -C two fetch && + head=$(git -C two rev-parse refs/remotes/origin/HEAD) && + branch=$(git -C two rev-parse refs/remotes/origin/main) && + test "z$head" = "z$branch" ' test_expect_success 'followRemoteHEAD does not kick in with refspecs' ' - test_when_finished "git config unset remote.origin.followRemoteHEAD" && - ( - cd "$D" && - cd two && - git remote set-head origin other && - git config set remote.origin.followRemoteHEAD always && - git fetch origin refs/heads/main:refs/remotes/origin/main && - echo refs/remotes/origin/other >expect && - git symbolic-ref refs/remotes/origin/HEAD >actual && - test_cmp expect actual - ) + git -C two remote set-head origin other && + test_config -C two remote.origin.followRemoteHEAD always && + git -C two fetch origin refs/heads/main:refs/remotes/origin/main && + echo refs/remotes/origin/other >expect && + git -C two symbolic-ref refs/remotes/origin/HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'followRemoteHEAD create does not overwrite dangling symref' ' + git -C two remote add -m does-not-exist custom-head ../one && + test_config -C two remote.custom-head.followRemoteHEAD create && + git -C two fetch custom-head && + echo refs/remotes/custom-head/does-not-exist >expect && + git -C two symbolic-ref refs/remotes/custom-head/HEAD >actual && + test_cmp expect actual ' test_expect_success 'fetch --prune on its own works as expected' ' - cd "$D" && git clone . prune && - cd prune && - git update-ref refs/remotes/origin/extrabranch main && + ( + cd prune && + git update-ref refs/remotes/origin/extrabranch main && - git fetch --prune origin && - test_must_fail git rev-parse origin/extrabranch + git fetch --prune origin && + test_must_fail git rev-parse origin/extrabranch + ) ' test_expect_success 'fetch --prune with a branch name keeps branches' ' - cd "$D" && git clone . prune-branch && - cd prune-branch && - git update-ref refs/remotes/origin/extrabranch main && + ( + cd prune-branch && + git update-ref refs/remotes/origin/extrabranch main && - git fetch --prune origin main && - git rev-parse origin/extrabranch + git fetch --prune origin main && + git rev-parse origin/extrabranch + ) ' test_expect_success 'fetch --prune with a namespace keeps other namespaces' ' - cd "$D" && git clone . prune-namespace && - cd prune-namespace && + ( + cd prune-namespace && - git fetch --prune origin refs/heads/a/*:refs/remotes/origin/a/* && - git rev-parse origin/main + git fetch --prune origin refs/heads/a/*:refs/remotes/origin/a/* && + git rev-parse origin/main + ) ' test_expect_success 'fetch --prune handles overlapping refspecs' ' - cd "$D" && git update-ref refs/pull/42/head main && git clone . prune-overlapping && - cd prune-overlapping && - git config --add remote.origin.fetch refs/pull/*/head:refs/remotes/origin/pr/* && + ( + cd prune-overlapping && + git config --add remote.origin.fetch refs/pull/*/head:refs/remotes/origin/pr/* && - git fetch --prune origin && - git rev-parse origin/main && - git rev-parse origin/pr/42 && + git fetch --prune origin && + git rev-parse origin/main && + git rev-parse origin/pr/42 && - git config --unset-all remote.origin.fetch && - git config remote.origin.fetch refs/pull/*/head:refs/remotes/origin/pr/* && - git config --add remote.origin.fetch refs/heads/*:refs/remotes/origin/* && + git config --unset-all remote.origin.fetch && + git config remote.origin.fetch refs/pull/*/head:refs/remotes/origin/pr/* && + git config --add remote.origin.fetch refs/heads/*:refs/remotes/origin/* && - git fetch --prune origin && - git rev-parse origin/main && - git rev-parse origin/pr/42 + git fetch --prune origin && + git rev-parse origin/main && + git rev-parse origin/pr/42 + ) ' test_expect_success 'fetch --prune --tags prunes branches but not tags' ' - cd "$D" && git clone . prune-tags && - cd prune-tags && - git tag sometag main && - # Create what looks like a remote-tracking branch from an earlier - # fetch that has since been deleted from the remote: - git update-ref refs/remotes/origin/fake-remote main && - - git fetch --prune --tags origin && - git rev-parse origin/main && - test_must_fail git rev-parse origin/fake-remote && - git rev-parse sometag + ( + cd prune-tags && + git tag sometag main && + # Create what looks like a remote-tracking branch from an earlier + # fetch that has since been deleted from the remote: + git update-ref refs/remotes/origin/fake-remote main && + + git fetch --prune --tags origin && + git rev-parse origin/main && + test_must_fail git rev-parse origin/fake-remote && + git rev-parse sometag + ) ' test_expect_success 'fetch --prune --tags with branch does not prune other things' ' - cd "$D" && git clone . prune-tags-branch && - cd prune-tags-branch && - git tag sometag main && - git update-ref refs/remotes/origin/extrabranch main && + ( + cd prune-tags-branch && + git tag sometag main && + git update-ref refs/remotes/origin/extrabranch main && - git fetch --prune --tags origin main && - git rev-parse origin/extrabranch && - git rev-parse sometag + git fetch --prune --tags origin main && + git rev-parse origin/extrabranch && + git rev-parse sometag + ) ' test_expect_success 'fetch --prune --tags with refspec prunes based on refspec' ' - cd "$D" && git clone . prune-tags-refspec && - cd prune-tags-refspec && - git tag sometag main && - git update-ref refs/remotes/origin/foo/otherbranch main && - git update-ref refs/remotes/origin/extrabranch main && - - git fetch --prune --tags origin refs/heads/foo/*:refs/remotes/origin/foo/* && - test_must_fail git rev-parse refs/remotes/origin/foo/otherbranch && - git rev-parse origin/extrabranch && - git rev-parse sometag + ( + cd prune-tags-refspec && + git tag sometag main && + git update-ref refs/remotes/origin/foo/otherbranch main && + git update-ref refs/remotes/origin/extrabranch main && + + git fetch --prune --tags origin refs/heads/foo/*:refs/remotes/origin/foo/* && + test_must_fail git rev-parse refs/remotes/origin/foo/otherbranch && + git rev-parse origin/extrabranch && + git rev-parse sometag + ) ' test_expect_success 'fetch --tags gets tags even without a configured remote' ' @@ -381,21 +356,21 @@ test_expect_success 'fetch --tags gets tags even without a configured remote' ' ' test_expect_success REFFILES 'fetch --prune fails to delete branches' ' - cd "$D" && git clone . prune-fail && - cd prune-fail && - git update-ref refs/remotes/origin/extrabranch main && - git pack-refs --all && - : this will prevent --prune from locking packed-refs for deleting refs, but adding loose refs still succeeds && - >.git/packed-refs.new && + ( + cd prune-fail && + git update-ref refs/remotes/origin/extrabranch main && + git pack-refs --all && + : this will prevent --prune from locking packed-refs for deleting refs, but adding loose refs still succeeds && + >.git/packed-refs.new && - test_must_fail git fetch --prune origin + test_must_fail git fetch --prune origin + ) ' test_expect_success 'fetch --atomic works with a single branch' ' - test_when_finished "rm -rf \"$D\"/atomic" && + test_when_finished "rm -rf atomic" && - cd "$D" && git clone . atomic && git branch atomic-branch && oid=$(git rev-parse atomic-branch) && @@ -408,9 +383,8 @@ test_expect_success 'fetch --atomic works with a single branch' ' ' test_expect_success 'fetch --atomic works with multiple branches' ' - test_when_finished "rm -rf \"$D\"/atomic" && + test_when_finished "rm -rf atomic" && - cd "$D" && git clone . atomic && git branch atomic-branch-1 && git branch atomic-branch-2 && @@ -423,9 +397,8 @@ test_expect_success 'fetch --atomic works with multiple branches' ' ' test_expect_success 'fetch --atomic works with mixed branches and tags' ' - test_when_finished "rm -rf \"$D\"/atomic" && + test_when_finished "rm -rf atomic" && - cd "$D" && git clone . atomic && git branch atomic-mixed-branch && git tag atomic-mixed-tag && @@ -437,9 +410,8 @@ test_expect_success 'fetch --atomic works with mixed branches and tags' ' ' test_expect_success 'fetch --atomic prunes references' ' - test_when_finished "rm -rf \"$D\"/atomic" && + test_when_finished "rm -rf atomic" && - cd "$D" && git branch atomic-prune-delete && git clone . atomic && git branch --delete atomic-prune-delete && @@ -453,9 +425,8 @@ test_expect_success 'fetch --atomic prunes references' ' ' test_expect_success 'fetch --atomic aborts with non-fast-forward update' ' - test_when_finished "rm -rf \"$D\"/atomic" && + test_when_finished "rm -rf atomic" && - cd "$D" && git branch atomic-non-ff && git clone . atomic && git rev-parse HEAD >actual && @@ -472,9 +443,8 @@ test_expect_success 'fetch --atomic aborts with non-fast-forward update' ' ' test_expect_success 'fetch --atomic executes a single reference transaction only' ' - test_when_finished "rm -rf \"$D\"/atomic" && + test_when_finished "rm -rf atomic" && - cd "$D" && git clone . atomic && git branch atomic-hooks-1 && git branch atomic-hooks-2 && @@ -499,9 +469,8 @@ test_expect_success 'fetch --atomic executes a single reference transaction only ' test_expect_success 'fetch --atomic aborts all reference updates if hook aborts' ' - test_when_finished "rm -rf \"$D\"/atomic" && + test_when_finished "rm -rf atomic" && - cd "$D" && git clone . atomic && git branch atomic-hooks-abort-1 && git branch atomic-hooks-abort-2 && @@ -536,9 +505,8 @@ test_expect_success 'fetch --atomic aborts all reference updates if hook aborts' ' test_expect_success 'fetch --atomic --append appends to FETCH_HEAD' ' - test_when_finished "rm -rf \"$D\"/atomic" && + test_when_finished "rm -rf atomic" && - cd "$D" && git clone . atomic && oid=$(git rev-parse HEAD) && @@ -574,8 +542,7 @@ test_expect_success REFFILES 'fetch --atomic fails transaction if reference lock ' test_expect_success '--refmap="" ignores configured refspec' ' - cd "$TRASH_DIRECTORY" && - git clone "$D" remote-refs && + git clone . remote-refs && git -C remote-refs rev-parse remotes/origin/main >old && git -C remote-refs update-ref refs/remotes/origin/main main~1 && git -C remote-refs rev-parse remotes/origin/main >new && @@ -599,34 +566,26 @@ test_expect_success '--refmap="" and --prune' ' test_expect_success 'fetch tags when there is no tags' ' - cd "$D" && - - mkdir notags && - cd notags && - git init && - - git fetch -t .. + git init notags && + git -C notags fetch -t .. ' test_expect_success 'fetch following tags' ' - cd "$D" && git tag -a -m "annotated" anno HEAD && git tag light HEAD && - mkdir four && - cd four && - git init && - - git fetch .. :track && - git show-ref --verify refs/tags/anno && - git show-ref --verify refs/tags/light - + git init four && + ( + cd four && + git fetch .. :track && + git show-ref --verify refs/tags/anno && + git show-ref --verify refs/tags/light + ) ' test_expect_success 'fetch uses remote ref names to describe new refs' ' - cd "$D" && git init descriptive && ( cd descriptive && @@ -654,30 +613,20 @@ test_expect_success 'fetch uses remote ref names to describe new refs' ' test_expect_success 'fetch must not resolve short tag name' ' - cd "$D" && - - mkdir five && - cd five && - git init && - - test_must_fail git fetch .. anno:five + git init five && + test_must_fail git -C five fetch .. anno:five ' test_expect_success 'fetch can now resolve short remote name' ' - cd "$D" && git update-ref refs/remotes/six/HEAD HEAD && - mkdir six && - cd six && - git init && - - git fetch .. six:six + git init six && + git -C six fetch .. six:six ' test_expect_success 'create bundle 1' ' - cd "$D" && echo >file updated again by origin && git commit -a -m "tip" && git bundle create --version=3 bundle1 main^..main @@ -691,35 +640,36 @@ test_expect_success 'header of bundle looks right' ' OID refs/heads/main EOF - sed -e "s/$OID_REGEX/OID/g" -e "5q" "$D"/bundle1 >actual && + sed -e "s/$OID_REGEX/OID/g" -e "5q" bundle1 >actual && test_cmp expect actual ' test_expect_success 'create bundle 2' ' - cd "$D" && git bundle create bundle2 main~2..main ' test_expect_success 'unbundle 1' ' - cd "$D/bundle" && - git checkout -b some-branch && - test_must_fail git fetch "$D/bundle1" main:main + ( + cd bundle && + git checkout -b some-branch && + test_must_fail git fetch bundle1 main:main + ) ' test_expect_success 'bundle 1 has only 3 files ' ' - cd "$D" && test_bundle_object_count bundle1 3 ' test_expect_success 'unbundle 2' ' - cd "$D/bundle" && - git fetch ../bundle2 main:main && - test "tip" = "$(git log -1 --pretty=oneline main | cut -d" " -f2)" + ( + cd bundle && + git fetch ../bundle2 main:main && + test "tip" = "$(git log -1 --pretty=oneline main | cut -d" " -f2)" + ) ' test_expect_success 'bundle does not prerequisite objects' ' - cd "$D" && touch file2 && git add file2 && git commit -m add.file2 file2 && @@ -729,7 +679,6 @@ test_expect_success 'bundle does not prerequisite objects' ' test_expect_success 'bundle should be able to create a full history' ' - cd "$D" && git tag -a -m "1.0" v1.0 main && git bundle create bundle4 v1.0 @@ -783,7 +732,6 @@ test_expect_success 'quoting of a strangely named repo' ' test_expect_success 'bundle should record HEAD correctly' ' - cd "$D" && git bundle create bundle5 HEAD main && git bundle list-heads bundle5 >actual && for h in HEAD refs/heads/main @@ -803,7 +751,6 @@ test_expect_success 'mark initial state of origin/main' ' test_expect_success 'explicit fetch should update tracking' ' - cd "$D" && git branch -f side && ( cd three && @@ -818,7 +765,6 @@ test_expect_success 'explicit fetch should update tracking' ' test_expect_success 'explicit pull should update tracking' ' - cd "$D" && git branch -f side && ( cd three && @@ -832,7 +778,6 @@ test_expect_success 'explicit pull should update tracking' ' ' test_expect_success 'explicit --refmap is allowed only with command-line refspec' ' - cd "$D" && ( cd three && test_must_fail git fetch --refmap="*:refs/remotes/none/*" @@ -840,7 +785,6 @@ test_expect_success 'explicit --refmap is allowed only with command-line refspec ' test_expect_success 'explicit --refmap option overrides remote.*.fetch' ' - cd "$D" && git branch -f side && ( cd three && @@ -855,7 +799,6 @@ test_expect_success 'explicit --refmap option overrides remote.*.fetch' ' ' test_expect_success 'explicitly empty --refmap option disables remote.*.fetch' ' - cd "$D" && git branch -f side && ( cd three && @@ -870,7 +813,6 @@ test_expect_success 'explicitly empty --refmap option disables remote.*.fetch' ' test_expect_success 'configured fetch updates tracking' ' - cd "$D" && git branch -f side && ( cd three && @@ -884,7 +826,6 @@ test_expect_success 'configured fetch updates tracking' ' ' test_expect_success 'non-matching refspecs do not confuse tracking update' ' - cd "$D" && git update-ref refs/odd/location HEAD && ( cd three && @@ -901,14 +842,12 @@ test_expect_success 'non-matching refspecs do not confuse tracking update' ' test_expect_success 'pushing nonexistent branch by mistake should not segv' ' - cd "$D" && test_must_fail git push seven no:no ' test_expect_success 'auto tag following fetches minimum' ' - cd "$D" && git clone .git follow && git checkout HEAD^0 && ( @@ -1307,7 +1246,7 @@ test_expect_success 'fetch --prune prints the remotes url' ' cd only-prunes && git fetch --prune origin 2>&1 | head -n1 >../actual ) && - echo "From ${D}/." >expect && + echo "From $(pwd)/." >expect && test_cmp expect actual ' @@ -1357,14 +1296,14 @@ test_expect_success 'fetching with auto-gc does not lock up' ' echo "$*" && false EOF - git clone "file://$D" auto-gc && + git clone "file://$PWD" auto-gc && test_commit test2 && ( cd auto-gc && git config fetch.unpackLimit 1 && git config gc.autoPackLimit 1 && git config gc.autoDetach false && - GIT_ASK_YESNO="$D/askyesno" git fetch --verbose >fetch.out 2>&1 && + GIT_ASK_YESNO="$TRASH_DIRECTORY/askyesno" git fetch --verbose >fetch.out 2>&1 && test_grep "Auto packing the repository" fetch.out && ! grep "Should I try again" fetch.out ) diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index dabcc5f811..46926e7bbd 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -105,7 +105,6 @@ check_push_result () { } test_expect_success setup ' - >path1 && git add path1 && test_tick && @@ -117,7 +116,6 @@ test_expect_success setup ' test_tick && git commit -a -m second && the_commit=$(git show-ref -s --verify refs/heads/main) - ' for cmd in push fetch @@ -322,104 +320,82 @@ test_expect_success 'push with pushInsteadOf and explicit pushurl (pushInsteadOf ' test_expect_success 'push with matching heads' ' - mk_test testrepo heads/main && git push testrepo : && check_push_result testrepo $the_commit heads/main - ' test_expect_success 'push with matching heads on the command line' ' - mk_test testrepo heads/main && git push testrepo : && check_push_result testrepo $the_commit heads/main - ' test_expect_success 'failed (non-fast-forward) push with matching heads' ' - mk_test testrepo heads/main && git push testrepo : && git commit --amend -massaged && test_must_fail git push testrepo && check_push_result testrepo $the_commit heads/main && git reset --hard $the_commit - ' test_expect_success 'push --force with matching heads' ' - mk_test testrepo heads/main && git push testrepo : && git commit --amend -massaged && git push --force testrepo : && ! check_push_result testrepo $the_commit heads/main && git reset --hard $the_commit - ' test_expect_success 'push with matching heads and forced update' ' - mk_test testrepo heads/main && git push testrepo : && git commit --amend -massaged && git push testrepo +: && ! check_push_result testrepo $the_commit heads/main && git reset --hard $the_commit - ' test_expect_success 'push with no ambiguity (1)' ' - mk_test testrepo heads/main && git push testrepo main:main && check_push_result testrepo $the_commit heads/main - ' test_expect_success 'push with no ambiguity (2)' ' - mk_test testrepo remotes/origin/main && git push testrepo main:origin/main && check_push_result testrepo $the_commit remotes/origin/main - ' test_expect_success 'push with colon-less refspec, no ambiguity' ' - mk_test testrepo heads/main heads/t/main && git branch -f t/main main && git push testrepo main && check_push_result testrepo $the_commit heads/main && check_push_result testrepo $the_first_commit heads/t/main - ' test_expect_success 'push with weak ambiguity (1)' ' - mk_test testrepo heads/main remotes/origin/main && git push testrepo main:main && check_push_result testrepo $the_commit heads/main && check_push_result testrepo $the_first_commit remotes/origin/main - ' test_expect_success 'push with weak ambiguity (2)' ' - mk_test testrepo heads/main remotes/origin/main remotes/another/main && git push testrepo main:main && check_push_result testrepo $the_commit heads/main && check_push_result testrepo $the_first_commit remotes/origin/main remotes/another/main - ' test_expect_success 'push with ambiguity' ' - mk_test testrepo heads/frotz tags/frotz && test_must_fail git push testrepo main:frotz && check_push_result testrepo $the_first_commit heads/frotz tags/frotz - ' test_expect_success 'push with onelevel ref' ' @@ -428,17 +404,14 @@ test_expect_success 'push with onelevel ref' ' ' test_expect_success 'push with colon-less refspec (1)' ' - mk_test testrepo heads/frotz tags/frotz && git branch -f frotz main && git push testrepo frotz && check_push_result testrepo $the_commit heads/frotz && check_push_result testrepo $the_first_commit tags/frotz - ' test_expect_success 'push with colon-less refspec (2)' ' - mk_test testrepo heads/frotz tags/frotz && if git show-ref --verify -q refs/heads/frotz then @@ -448,7 +421,6 @@ test_expect_success 'push with colon-less refspec (2)' ' git push -f testrepo frotz && check_push_result testrepo $the_commit tags/frotz && check_push_result testrepo $the_first_commit heads/frotz - ' test_expect_success 'push with colon-less refspec (3)' ' @@ -465,7 +437,6 @@ test_expect_success 'push with colon-less refspec (3)' ' ' test_expect_success 'push with colon-less refspec (4)' ' - mk_test testrepo && if git show-ref --verify -q refs/heads/frotz then @@ -475,38 +446,34 @@ test_expect_success 'push with colon-less refspec (4)' ' git push testrepo frotz && check_push_result testrepo $the_commit tags/frotz && test 1 = $( cd testrepo && git show-ref | wc -l ) - ' test_expect_success 'push head with non-existent, incomplete dest' ' - mk_test testrepo && git push testrepo main:branch && check_push_result testrepo $the_commit heads/branch - ' test_expect_success 'push tag with non-existent, incomplete dest' ' - mk_test testrepo && git tag -f v1.0 && git push testrepo v1.0:tag && check_push_result testrepo $the_commit tags/tag - ' test_expect_success 'push oid with non-existent, incomplete dest' ' - mk_test testrepo && test_must_fail git push testrepo $(git rev-parse main):foo - ' test_expect_success 'push ref expression with non-existent, incomplete dest' ' - mk_test testrepo && test_must_fail git push testrepo main^:branch +' +test_expect_success 'push ref expression with non-existent oid src' ' + mk_test testrepo && + test_must_fail git push testrepo $(test_oid 001):branch ' for head in HEAD @ @@ -550,7 +517,6 @@ do git checkout main && git push testrepo $head:branch && check_push_result testrepo $the_commit heads/branch - ' test_expect_success "push with config remote.*.push = $head" ' @@ -596,7 +562,6 @@ test_expect_success 'push with remote.pushdefault' ' ' test_expect_success 'push with config remote.*.pushurl' ' - mk_test testrepo heads/main && git checkout main && test_config remote.there.url test2repo && @@ -655,7 +620,6 @@ test_expect_success 'push ignores "branch." config without subsection' ' ' test_expect_success 'push with dry-run' ' - mk_test testrepo heads/main && old_commit=$(git -C testrepo show-ref -s --verify refs/heads/main) && git push --dry-run testrepo : && @@ -663,7 +627,6 @@ test_expect_success 'push with dry-run' ' ' test_expect_success 'push updates local refs' ' - mk_test testrepo heads/main && mk_child testrepo child && ( @@ -673,11 +636,9 @@ test_expect_success 'push updates local refs' ' test $(git rev-parse main) = \ $(git rev-parse remotes/origin/main) ) - ' test_expect_success 'push updates up-to-date local refs' ' - mk_test testrepo heads/main && mk_child testrepo child1 && mk_child testrepo child2 && @@ -689,11 +650,9 @@ test_expect_success 'push updates up-to-date local refs' ' test $(git rev-parse main) = \ $(git rev-parse remotes/origin/main) ) - ' test_expect_success 'push preserves up-to-date packed refs' ' - mk_test testrepo heads/main && mk_child testrepo child && ( @@ -701,11 +660,9 @@ test_expect_success 'push preserves up-to-date packed refs' ' git push && ! test -f .git/refs/remotes/origin/main ) - ' test_expect_success 'push does not update local refs on failure' ' - mk_test testrepo heads/main && mk_child testrepo child && echo "#!/no/frobnication/today" >testrepo/.git/hooks/pre-receive && @@ -717,16 +674,13 @@ test_expect_success 'push does not update local refs on failure' ' test $(git rev-parse main) != \ $(git rev-parse remotes/origin/main) ) - ' test_expect_success 'allow deleting an invalid remote ref' ' - mk_test testrepo heads/branch && rm -f testrepo/.git/objects/??/* && git push testrepo :refs/heads/branch && (cd testrepo && test_must_fail git rev-parse --verify refs/heads/branch) - ' test_expect_success 'pushing valid refs triggers post-receive and post-update hooks' ' @@ -744,8 +698,8 @@ test_expect_success 'pushing valid refs triggers post-receive and post-update ho EOF cat >update.expect <<-EOF && - refs/heads/main $orgmain $newmain refs/heads/next $orgnext $newnext + refs/heads/main $orgmain $newmain EOF cat >post-receive.expect <<-EOF && @@ -808,8 +762,8 @@ test_expect_success 'deletion of a non-existent ref is not fed to post-receive a EOF cat >update.expect <<-EOF && - refs/heads/main $orgmain $newmain refs/heads/nonexistent $ZERO_OID $ZERO_OID + refs/heads/main $orgmain $newmain EOF cat >post-receive.expect <<-EOF && @@ -868,10 +822,10 @@ test_expect_success 'mixed ref updates, deletes, invalid deletes trigger hooks w EOF cat >update.expect <<-EOF && - refs/heads/main $orgmain $newmain refs/heads/next $orgnext $newnext - refs/heads/seen $orgseen $newseen refs/heads/nonexistent $ZERO_OID $ZERO_OID + refs/heads/main $orgmain $newmain + refs/heads/seen $orgseen $newseen EOF cat >post-receive.expect <<-EOF && @@ -1909,4 +1863,23 @@ test_expect_success 'push with config push.useBitmaps' ' --thin --delta-base-offset -q --no-use-bitmap-index <false ' +test_expect_success 'push with config pack.usePathWalk=true' ' + mk_test testrepo heads/main && + git checkout main && + test_config pack.usePathWalk true && + GIT_TRACE2_EVENT="$(pwd)/path-walk.txt" \ + git push --quiet testrepo main:test && + + test_region pack-objects path-walk path-walk.txt +' + +test_expect_success 'push with F/D conflict with deletion and creation' ' + test_when_finished "git branch -D branch" && + git branch branch/conflict && + mk_test testrepo heads/branch/conflict && + git branch -D branch/conflict && + git branch branch && + git push testrepo :refs/heads/branch/conflict refs/heads/branch +' + test_done diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index 63c9a8f04b..0e0019347e 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -472,6 +472,66 @@ test_expect_success 'pull --no-autostash & merge.autostash unset' ' test_pull_autostash_fail --no-autostash --no-rebase ' +test_expect_success 'pull succeeds with dirty working directory and pull.autostash=true' ' + test_config pull.autostash true && + test_pull_autostash 1 --rebase && + test_pull_autostash 2 --no-rebase && + test_pull_autostash 1 --autostash --rebase && + test_pull_autostash 2 --autostash --no-rebase +' + +test_expect_success 'pull fails with dirty working directory and pull.autostash=false' ' + test_config pull.autostash false && + test_pull_autostash_fail --rebase && + test_pull_autostash_fail --no-rebase && + test_pull_autostash_fail --no-autostash --rebase && + test_pull_autostash_fail --no-autostash --no-rebase +' + +test_expect_success 'pull --autostash overrides pull.autostash=false' ' + test_config pull.autostash false && + test_pull_autostash 1 --autostash --rebase && + test_pull_autostash 2 --autostash --no-rebase +' + +test_expect_success 'pull --no-autostash overrides pull.autostash=true' ' + test_config pull.autostash true && + test_pull_autostash_fail --no-autostash --rebase && + test_pull_autostash_fail --no-autostash --no-rebase +' + +test_expect_success 'pull.autostash=true overrides rebase.autostash' ' + test_config pull.autostash true && + test_config rebase.autostash true && + test_pull_autostash 1 --rebase && + test_config rebase.autostash false && + test_pull_autostash 1 --rebase +' + +test_expect_success 'pull.autostash=false overrides rebase.autostash' ' + test_config pull.autostash false && + test_config rebase.autostash true && + test_pull_autostash_fail --rebase && + test_config rebase.autostash false && + test_pull_autostash_fail --rebase +' + +test_expect_success 'pull.autostash=true overrides merge.autostash' ' + test_config pull.autostash true && + test_config merge.autostash true && + test_pull_autostash 2 --no-rebase && + test_config merge.autostash false && + test_pull_autostash 2 --no-rebase +' + +test_expect_success 'pull.autostash=false overrides merge.autostash' ' + test_config pull.autostash false && + test_config merge.autostash true && + test_pull_autostash_fail --no-rebase && + test_config merge.autostash false && + test_pull_autostash_fail --no-rebase +' + test_expect_success 'pull.rebase' ' git reset --hard before-rebase && test_config pull.rebase true && diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh index 558eedf25a..d40292cfb7 100755 --- a/t/t5530-upload-pack-error.sh +++ b/t/t5530-upload-pack-error.sh @@ -4,8 +4,6 @@ test_description='errors in upload-pack' . ./test-lib.sh -D=$(pwd) - corrupt_repo () { object_sha1=$(git rev-parse "$1") && ob=$(expr "$object_sha1" : "\(..\)") && @@ -21,11 +19,7 @@ test_expect_success 'setup and corrupt repository' ' test_tick && echo changed >file && git commit -a -m changed && - corrupt_repo HEAD:file - -' - -test_expect_success 'fsck fails' ' + corrupt_repo HEAD:file && test_must_fail git fsck ' @@ -40,17 +34,12 @@ test_expect_success 'upload-pack fails due to error in pack-objects packing' ' ' test_expect_success 'corrupt repo differently' ' - git hash-object -w file && - corrupt_repo HEAD^^{tree} - -' - -test_expect_success 'fsck fails' ' + corrupt_repo HEAD^^{tree} && test_must_fail git fsck ' -test_expect_success 'upload-pack fails due to error in rev-list' ' +test_expect_success 'upload-pack fails due to error in rev-list' ' printf "%04xwant %s\n%04xshallow %s00000009done\n0000" \ $(($hexsz + 10)) $(git rev-parse HEAD) \ $(($hexsz + 12)) $(git rev-parse HEAD^) >input && @@ -59,7 +48,6 @@ test_expect_success 'upload-pack fails due to error in rev-list' ' ' test_expect_success 'upload-pack fails due to bad want (no object)' ' - printf "%04xwant %s multi_ack_detailed\n00000009done\n0000" \ $(($hexsz + 29)) $(test_oid deadbeef) >input && test_must_fail git upload-pack . <input >output 2>output.err && @@ -69,7 +57,6 @@ test_expect_success 'upload-pack fails due to bad want (no object)' ' ' test_expect_success 'upload-pack fails due to bad want (not tip)' ' - oid=$(echo an object we have | git hash-object -w --stdin) && printf "%04xwant %s multi_ack_detailed\n00000009done\n0000" \ $(($hexsz + 29)) "$oid" >input && @@ -80,7 +67,6 @@ test_expect_success 'upload-pack fails due to bad want (not tip)' ' ' test_expect_success 'upload-pack fails due to error in pack-objects enumeration' ' - printf "%04xwant %s\n00000009done\n0000" \ $((hexsz + 10)) $(git rev-parse HEAD) >input && test_must_fail git upload-pack . <input >/dev/null 2>output.err && @@ -105,18 +91,48 @@ test_expect_success 'upload-pack tolerates EOF just after stateless client wants test_cmp expect actual ' -test_expect_success 'create empty repository' ' - - mkdir foo && - cd foo && - git init - -' - test_expect_success 'fetch fails' ' + git init foo && + test_must_fail git -C foo fetch .. main +' - test_must_fail git fetch .. main +test_expect_success 'upload-pack ACKs repeated non-commit objects repeatedly (protocol v0)' ' + commit_id=$(git rev-parse HEAD) && + tree_id=$(git rev-parse HEAD^{tree}) && + test-tool pkt-line pack >request <<-EOF && + want $commit_id + 0000 + have $tree_id + have $tree_id + 0000 + EOF + git upload-pack --stateless-rpc . <request >actual && + depacketize <actual >actual.raw && + grep ^ACK actual.raw >actual.acks && + cat >expect <<-EOF && + ACK $tree_id + ACK $tree_id + EOF + test_cmp expect actual.acks +' +test_expect_success 'upload-pack ACKs repeated non-commit objects once only (protocol v2)' ' + commit_id=$(git rev-parse HEAD) && + tree_id=$(git rev-parse HEAD^{tree}) && + test-tool pkt-line pack >request <<-EOF && + command=fetch + object-format=$(test_oid algo) + 0001 + want $commit_id + have $tree_id + have $tree_id + 0000 + EOF + GIT_PROTOCOL=version=2 git upload-pack . <request >actual && + depacketize <actual >actual.raw && + grep ^ACK actual.raw >actual.acks && + echo "ACK $tree_id" >expect && + test_cmp expect actual.acks ' test_done diff --git a/t/t5538-push-shallow.sh b/t/t5538-push-shallow.sh index e91fcc173e..dc0e972943 100755 --- a/t/t5538-push-shallow.sh +++ b/t/t5538-push-shallow.sh @@ -123,4 +123,45 @@ EOF git cat-file blob $(echo 1|git hash-object --stdin) >/dev/null ) ' + +test_expect_success 'push new commit from shallow clone has correct object count' ' + git init origin && + test_commit -C origin a && + test_commit -C origin b && + + git clone --depth=1 "file://$(pwd)/origin" client && + git -C client checkout -b topic && + git -C client commit --allow-empty -m "empty" && + GIT_PROGRESS_DELAY=0 git -C client push --progress origin topic 2>err && + test_grep "Enumerating objects: 1, done." err +' + +test_expect_success 'push new commit from shallow clone has good deltas' ' + git init base && + test_seq 1 999 >base/a && + test_commit -C base initial && + git -C base add a && + git -C base commit -m "big a" && + + git clone --depth=1 "file://$(pwd)/base" deltas && + git -C deltas checkout -b deltas && + test_seq 1 1000 >deltas/a && + git -C deltas commit -a -m "bigger a" && + GIT_PROGRESS_DELAY=0 git -C deltas push --progress origin deltas 2>err && + + test_grep "Enumerating objects: 5, done" err && + + # If the delta base is found, then this message uses "bytes". + # If the delta base is not found, then this message uses "KiB". + test_grep "Writing objects: .* bytes" err && + + git -C deltas commit --amend -m "changed message" && + GIT_TRACE2_EVENT="$(pwd)/config-push.txt" \ + GIT_PROGRESS_DELAY=0 git -C deltas -c pack.usePathWalk=true \ + push --progress -f origin deltas 2>err && + + test_grep "Enumerating objects: 1, done" err && + test_region pack-objects path-walk config-push.txt +' + test_done diff --git a/t/t5564-http-proxy.sh b/t/t5564-http-proxy.sh index b27e481f95..c3903faf2d 100755 --- a/t/t5564-http-proxy.sh +++ b/t/t5564-http-proxy.sh @@ -72,7 +72,9 @@ test_expect_success SOCKS_PROXY 'clone via Unix socket' ' test_when_finished "rm -rf clone" && test_config_global http.proxy "socks4://localhost$PWD/%2530.sock" && { { - GIT_TRACE_CURL=$PWD/trace git clone "$HTTPD_URL/smart/repo.git" clone 2>err && + GIT_TRACE_CURL=$PWD/trace \ + GIT_TRACE_CURL_COMPONENTS=socks \ + git clone "$HTTPD_URL/smart/repo.git" clone 2>err && grep -i "SOCKS4 request granted" trace } || old_libcurl_error err diff --git a/t/t5710-promisor-remote-capability.sh b/t/t5710-promisor-remote-capability.sh index cb061b1f35..023735d6a8 100755 --- a/t/t5710-promisor-remote-capability.sh +++ b/t/t5710-promisor-remote-capability.sh @@ -295,6 +295,71 @@ test_expect_success "clone with 'KnownUrl' and empty url, so not advertised" ' check_missing_objects server 1 "$oid" ' +test_expect_success "clone with promisor.sendFields" ' + git -C server config promisor.advertise true && + test_when_finished "rm -rf client" && + + git -C server remote add otherLop "https://invalid.invalid" && + git -C server config remote.otherLop.token "fooBar" && + git -C server config remote.otherLop.stuff "baz" && + git -C server config remote.otherLop.partialCloneFilter "blob:limit=10k" && + test_when_finished "git -C server remote remove otherLop" && + test_config -C server promisor.sendFields "partialCloneFilter, token" && + test_when_finished "rm trace" && + + # Clone from server to create a client + GIT_TRACE_PACKET="$(pwd)/trace" GIT_NO_LAZY_FETCH=0 git clone \ + -c remote.lop.promisor=true \ + -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \ + -c remote.lop.url="file://$(pwd)/lop" \ + -c promisor.acceptfromserver=All \ + --no-local --filter="blob:limit=5k" server client && + + # Check that fields are properly transmitted + ENCODED_URL=$(echo "file://$(pwd)/lop" | sed -e "s/ /%20/g") && + PR1="name=lop,url=$ENCODED_URL,partialCloneFilter=blob:none" && + PR2="name=otherLop,url=https://invalid.invalid,partialCloneFilter=blob:limit=10k,token=fooBar" && + test_grep "clone< promisor-remote=$PR1;$PR2" trace && + test_grep "clone> promisor-remote=lop;otherLop" trace && + + # Check that the largest object is still missing on the server + check_missing_objects server 1 "$oid" +' + +test_expect_success "clone with promisor.checkFields" ' + git -C server config promisor.advertise true && + test_when_finished "rm -rf client" && + + git -C server remote add otherLop "https://invalid.invalid" && + git -C server config remote.otherLop.token "fooBar" && + git -C server config remote.otherLop.stuff "baz" && + git -C server config remote.otherLop.partialCloneFilter "blob:limit=10k" && + test_when_finished "git -C server remote remove otherLop" && + test_config -C server promisor.sendFields "partialCloneFilter, token" && + test_when_finished "rm trace" && + + # Clone from server to create a client + GIT_TRACE_PACKET="$(pwd)/trace" GIT_NO_LAZY_FETCH=0 git clone \ + -c remote.lop.promisor=true \ + -c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \ + -c remote.lop.url="file://$(pwd)/lop" \ + -c remote.lop.partialCloneFilter="blob:none" \ + -c promisor.acceptfromserver=All \ + -c promisor.checkFields=partialcloneFilter \ + --no-local --filter="blob:limit=5k" server client && + + # Check that fields are properly transmitted + ENCODED_URL=$(echo "file://$(pwd)/lop" | sed -e "s/ /%20/g") && + PR1="name=lop,url=$ENCODED_URL,partialCloneFilter=blob:none" && + PR2="name=otherLop,url=https://invalid.invalid,partialCloneFilter=blob:limit=10k,token=fooBar" && + test_grep "clone< promisor-remote=$PR1;$PR2" trace && + test_grep "clone> promisor-remote=lop" trace && + test_grep ! "clone> promisor-remote=lop;otherLop" trace && + + # Check that the largest object is still missing on the server + check_missing_objects server 1 "$oid" +' + test_expect_success "clone with promisor.advertise set to 'true' but don't delete the client" ' git -C server config promisor.advertise true && diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index 256ccaefb7..2c70cc561a 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh @@ -409,6 +409,36 @@ test_expect_success 'describe tag object' ' test_grep "fatal: test-blob-1 is neither a commit nor blob" actual ' +test_expect_success 'describe an unreachable blob' ' + blob=$(echo not-found-anywhere | git hash-object -w --stdin) && + test_must_fail git describe $blob 2>actual && + test_grep "blob .$blob. not reachable from HEAD" actual +' + +test_expect_success 'describe blob on an unborn branch' ' + oldbranch=$(git symbolic-ref HEAD) && + test_when_finished "git symbolic-ref HEAD $oldbranch" && + git symbolic-ref HEAD refs/heads/does-not-exist && + test_must_fail git describe test-blob 2>actual && + test_grep "cannot search .* on an unborn branch" actual +' + +# This test creates a repository state that we generally try to disallow: HEAD +# is pointing to an object that is not a commit. The ref update code forbids +# non-commit writes directly to HEAD or to any branch in refs/heads/. But we +# can use the loophole of pointing HEAD to another non-branch ref (something we +# should forbid, but don't for historical reasons). +# +# Do not take this test as an endorsement of the loophole! If we ever tighten +# it, it is reasonable to just drop this test entirely. +test_expect_success 'describe blob on a non-commit HEAD' ' + oldbranch=$(git symbolic-ref HEAD) && + test_when_finished "git symbolic-ref HEAD $oldbranch" && + git symbolic-ref HEAD refs/tags/test-blob && + test_must_fail git describe test-blob 2>actual && + test_grep "blob .* not reachable from HEAD" actual +' + test_expect_success ULIMIT_STACK_SIZE 'name-rev works in a deep repo' ' i=1 && while test $i -lt 8000 diff --git a/t/t6137-pathspec-wildcards-literal.sh b/t/t6137-pathspec-wildcards-literal.sh index 20abad5667..17a03085ef 100755 --- a/t/t6137-pathspec-wildcards-literal.sh +++ b/t/t6137-pathspec-wildcards-literal.sh @@ -3,8 +3,8 @@ test_description='test wildcards and literals with git add/commit (subshell styl . ./test-lib.sh -test_have_prereq FUNNYNAMES || { - skip_all='skipping: needs FUNNYNAMES (non-Windows only)' +test_have_prereq BSLASHPSPEC || { + skip_all='skipping: needs BSLASHPSPEC (backslashes in pathspecs)' test_done } @@ -184,7 +184,7 @@ test_expect_success 'add wildcard f?z' ' ) ' -test_expect_success 'add literal \? literal' ' +test_expect_success 'add literal \?' ' git init test-q-lit && ( cd test-q-lit && @@ -241,7 +241,7 @@ test_expect_success 'add literal hello\?world' ' ) ' -test_expect_success 'add literal [abc]' ' +test_expect_success 'add literal \[abc\]' ' git init test-brackets-lit && ( cd test-brackets-lit && @@ -280,7 +280,7 @@ test_expect_success 'commit: wildcard *' ' ) ' -test_expect_success 'commit: literal *' ' +test_expect_success 'commit: literal \*' ' git init test-c-asterisk-lit && ( cd test-c-asterisk-lit && @@ -328,7 +328,7 @@ test_expect_success 'commit: literal f\*' ' ) ' -test_expect_success 'commit: wildcard pathspec limits commit' ' +test_expect_success 'commit: wildcard f**' ' git init test-c-pathlimit && ( cd test-c-pathlimit && diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index ce9af79ab1..1d9809114d 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -6,2150 +6,14 @@ test_description='for-each-ref test' . ./test-lib.sh -GNUPGHOME_NOT_USED=$GNUPGHOME -. "$TEST_DIRECTORY"/lib-gpg.sh -. "$TEST_DIRECTORY"/lib-terminal.sh -# Mon Jul 3 23:18:43 2006 +0000 -datestamp=1151968723 -setdate_and_increment () { - GIT_COMMITTER_DATE="$datestamp +0200" - datestamp=$(expr "$datestamp" + 1) - GIT_AUTHOR_DATE="$datestamp +0200" - datestamp=$(expr "$datestamp" + 1) - export GIT_COMMITTER_DATE GIT_AUTHOR_DATE -} - -test_object_file_size () { - oid=$(git rev-parse "$1") - path=".git/objects/$(test_oid_to_path $oid)" - test_file_size "$path" -} - -test_expect_success setup ' - # setup .mailmap - cat >.mailmap <<-EOF && - A Thor <athor@example.com> A U Thor <author@example.com> - C Mitter <cmitter@example.com> C O Mitter <committer@example.com> - EOF - - setdate_and_increment && - echo "Using $datestamp" > one && - git add one && - git commit -m "Initial" && - git branch -M main && - setdate_and_increment && - git tag -a -m "Tagging at $datestamp" testtag && - git update-ref refs/remotes/origin/main main && - git remote add origin nowhere && - git config branch.main.remote origin && - git config branch.main.merge refs/heads/main && - git remote add myfork elsewhere && - git config remote.pushdefault myfork && - git config push.default current -' - -test_atom () { - case "$1" in - head) ref=refs/heads/main ;; - tag) ref=refs/tags/testtag ;; - sym) ref=refs/heads/sym ;; - *) ref=$1 ;; - esac - format=$2 - test_do=test_expect_${4:-success} - - printf '%s\n' "$3" >expected - $test_do $PREREQ "basic atom: $ref $format" ' - git for-each-ref --format="%($format)" "$ref" >actual && - sanitize_pgp <actual >actual.clean && - test_cmp expected actual.clean - ' - - # Automatically test "contents:size" atom after testing "contents" - if test "$format" = "contents" - then - # for commit leg, $3 is changed there - expect=$(printf '%s' "$3" | wc -c) - $test_do $PREREQ "basic atom: $ref contents:size" ' - type=$(git cat-file -t "$ref") && - case $type in - tag) - # We cannot use $3 as it expects sanitize_pgp to run - git cat-file tag $ref >out && - expect=$(tail -n +6 out | wc -c) && - rm -f out ;; - tree | blob) - expect="" ;; - commit) - : "use the calculated expect" ;; - *) - BUG "unknown object type" ;; - esac && - # Leave $expect unquoted to lose possible leading whitespaces - echo $expect >expected && - git for-each-ref --format="%(contents:size)" "$ref" >actual && - test_cmp expected actual - ' - fi -} - -hexlen=$(test_oid hexsz) - -test_atom head refname refs/heads/main -test_atom head refname: refs/heads/main -test_atom head refname:short main -test_atom head refname:lstrip=1 heads/main -test_atom head refname:lstrip=2 main -test_atom head refname:lstrip=-1 main -test_atom head refname:lstrip=-2 heads/main -test_atom head refname:rstrip=1 refs/heads -test_atom head refname:rstrip=2 refs -test_atom head refname:rstrip=-1 refs -test_atom head refname:rstrip=-2 refs/heads -test_atom head refname:strip=1 heads/main -test_atom head refname:strip=2 main -test_atom head refname:strip=-1 main -test_atom head refname:strip=-2 heads/main -test_atom head upstream refs/remotes/origin/main -test_atom head upstream:short origin/main -test_atom head upstream:lstrip=2 origin/main -test_atom head upstream:lstrip=-2 origin/main -test_atom head upstream:rstrip=2 refs/remotes -test_atom head upstream:rstrip=-2 refs/remotes -test_atom head upstream:strip=2 origin/main -test_atom head upstream:strip=-2 origin/main -test_atom head push refs/remotes/myfork/main -test_atom head push:short myfork/main -test_atom head push:lstrip=1 remotes/myfork/main -test_atom head push:lstrip=-1 main -test_atom head push:rstrip=1 refs/remotes/myfork -test_atom head push:rstrip=-1 refs -test_atom head push:strip=1 remotes/myfork/main -test_atom head push:strip=-1 main -test_atom head objecttype commit -test_atom head objectsize $((131 + hexlen)) -test_atom head objectsize:disk $(test_object_file_size refs/heads/main) -test_atom head deltabase $ZERO_OID -test_atom head objectname $(git rev-parse refs/heads/main) -test_atom head objectname:short $(git rev-parse --short refs/heads/main) -test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main) -test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main) -test_atom head tree $(git rev-parse refs/heads/main^{tree}) -test_atom head tree:short $(git rev-parse --short refs/heads/main^{tree}) -test_atom head tree:short=1 $(git rev-parse --short=1 refs/heads/main^{tree}) -test_atom head tree:short=10 $(git rev-parse --short=10 refs/heads/main^{tree}) -test_atom head parent '' -test_atom head parent:short '' -test_atom head parent:short=1 '' -test_atom head parent:short=10 '' -test_atom head numparent 0 -test_atom head object '' -test_atom head type '' -test_atom head raw "$(git cat-file commit refs/heads/main) -" -test_atom head '*objectname' '' -test_atom head '*objecttype' '' -test_atom head author 'A U Thor <author@example.com> 1151968724 +0200' -test_atom head authorname 'A U Thor' -test_atom head authorname:mailmap 'A Thor' -test_atom head authoremail '<author@example.com>' -test_atom head authoremail:trim 'author@example.com' -test_atom head authoremail:localpart 'author' -test_atom head authoremail:trim,localpart 'author' -test_atom head authoremail:mailmap '<athor@example.com>' -test_atom head authoremail:mailmap,trim 'athor@example.com' -test_atom head authoremail:trim,mailmap 'athor@example.com' -test_atom head authoremail:mailmap,localpart 'athor' -test_atom head authoremail:localpart,mailmap 'athor' -test_atom head authoremail:mailmap,trim,localpart,mailmap,trim 'athor' -test_atom head authordate 'Tue Jul 4 01:18:44 2006 +0200' -test_atom head committer 'C O Mitter <committer@example.com> 1151968723 +0200' -test_atom head committername 'C O Mitter' -test_atom head committername:mailmap 'C Mitter' -test_atom head committeremail '<committer@example.com>' -test_atom head committeremail:trim 'committer@example.com' -test_atom head committeremail:localpart 'committer' -test_atom head committeremail:localpart,trim 'committer' -test_atom head committeremail:mailmap '<cmitter@example.com>' -test_atom head committeremail:mailmap,trim 'cmitter@example.com' -test_atom head committeremail:trim,mailmap 'cmitter@example.com' -test_atom head committeremail:mailmap,localpart 'cmitter' -test_atom head committeremail:localpart,mailmap 'cmitter' -test_atom head committeremail:trim,mailmap,trim,trim,localpart 'cmitter' -test_atom head committerdate 'Tue Jul 4 01:18:43 2006 +0200' -test_atom head tag '' -test_atom head tagger '' -test_atom head taggername '' -test_atom head taggeremail '' -test_atom head taggeremail:trim '' -test_atom head taggeremail:localpart '' -test_atom head taggerdate '' -test_atom head creator 'C O Mitter <committer@example.com> 1151968723 +0200' -test_atom head creatordate 'Tue Jul 4 01:18:43 2006 +0200' -test_atom head subject 'Initial' -test_atom head subject:sanitize 'Initial' -test_atom head contents:subject 'Initial' -test_atom head body '' -test_atom head contents:body '' -test_atom head contents:signature '' -test_atom head contents 'Initial -' -test_atom head HEAD '*' - -test_atom tag refname refs/tags/testtag -test_atom tag refname:short testtag -test_atom tag upstream '' -test_atom tag push '' -test_atom tag objecttype tag -test_atom tag objectsize $((114 + hexlen)) -test_atom tag objectsize:disk $(test_object_file_size refs/tags/testtag) -test_atom tag '*objectsize:disk' $(test_object_file_size refs/heads/main) -test_atom tag deltabase $ZERO_OID -test_atom tag '*deltabase' $ZERO_OID -test_atom tag objectname $(git rev-parse refs/tags/testtag) -test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag) -test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/main) -test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/main) -test_atom tag tree '' -test_atom tag tree:short '' -test_atom tag tree:short=1 '' -test_atom tag tree:short=10 '' -test_atom tag parent '' -test_atom tag parent:short '' -test_atom tag parent:short=1 '' -test_atom tag parent:short=10 '' -test_atom tag numparent '' -test_atom tag object $(git rev-parse refs/tags/testtag^0) -test_atom tag type 'commit' -test_atom tag '*objectname' $(git rev-parse refs/tags/testtag^{}) -test_atom tag '*objecttype' 'commit' -test_atom tag author '' -test_atom tag authorname '' -test_atom tag authorname:mailmap '' -test_atom tag authoremail '' -test_atom tag authoremail:trim '' -test_atom tag authoremail:localpart '' -test_atom tag authoremail:trim,localpart '' -test_atom tag authoremail:mailmap '' -test_atom tag authoremail:mailmap,trim '' -test_atom tag authoremail:trim,mailmap '' -test_atom tag authoremail:mailmap,localpart '' -test_atom tag authoremail:localpart,mailmap '' -test_atom tag authoremail:mailmap,trim,localpart,mailmap,trim '' -test_atom tag authordate '' -test_atom tag committer '' -test_atom tag committername '' -test_atom tag committername:mailmap '' -test_atom tag committeremail '' -test_atom tag committeremail:trim '' -test_atom tag committeremail:localpart '' -test_atom tag committeremail:localpart,trim '' -test_atom tag committeremail:mailmap '' -test_atom tag committeremail:mailmap,trim '' -test_atom tag committeremail:trim,mailmap '' -test_atom tag committeremail:mailmap,localpart '' -test_atom tag committeremail:localpart,mailmap '' -test_atom tag committeremail:trim,mailmap,trim,trim,localpart '' -test_atom tag committerdate '' -test_atom tag tag 'testtag' -test_atom tag tagger 'C O Mitter <committer@example.com> 1151968725 +0200' -test_atom tag taggername 'C O Mitter' -test_atom tag taggername:mailmap 'C Mitter' -test_atom tag taggeremail '<committer@example.com>' -test_atom tag taggeremail:trim 'committer@example.com' -test_atom tag taggeremail:localpart 'committer' -test_atom tag taggeremail:trim,localpart 'committer' -test_atom tag taggeremail:mailmap '<cmitter@example.com>' -test_atom tag taggeremail:mailmap,trim 'cmitter@example.com' -test_atom tag taggeremail:trim,mailmap 'cmitter@example.com' -test_atom tag taggeremail:mailmap,localpart 'cmitter' -test_atom tag taggeremail:localpart,mailmap 'cmitter' -test_atom tag taggeremail:trim,mailmap,trim,localpart,localpart 'cmitter' -test_atom tag taggerdate 'Tue Jul 4 01:18:45 2006 +0200' -test_atom tag creator 'C O Mitter <committer@example.com> 1151968725 +0200' -test_atom tag creatordate 'Tue Jul 4 01:18:45 2006 +0200' -test_atom tag subject 'Tagging at 1151968727' -test_atom tag subject:sanitize 'Tagging-at-1151968727' -test_atom tag contents:subject 'Tagging at 1151968727' -test_atom tag body '' -test_atom tag contents:body '' -test_atom tag contents:signature '' -test_atom tag contents 'Tagging at 1151968727 -' -test_atom tag HEAD ' ' - -test_expect_success 'basic atom: refs/tags/testtag *raw' ' - git cat-file commit refs/tags/testtag^{} >expected && - git for-each-ref --format="%(*raw)" refs/tags/testtag >actual && - sanitize_pgp <expected >expected.clean && - echo >>expected.clean && - sanitize_pgp <actual >actual.clean && - test_cmp expected.clean actual.clean -' - -test_expect_success 'Check invalid atoms names are errors' ' - test_must_fail git for-each-ref --format="%(INVALID)" refs/heads -' - -test_expect_success 'for-each-ref does not crash with -h' ' +test_expect_success "for-each-ref does not crash with -h" ' test_expect_code 129 git for-each-ref -h >usage && test_grep "[Uu]sage: git for-each-ref " usage && test_expect_code 129 nongit git for-each-ref -h >usage && test_grep "[Uu]sage: git for-each-ref " usage ' -test_expect_success 'Check format specifiers are ignored in naming date atoms' ' - git for-each-ref --format="%(authordate)" refs/heads && - git for-each-ref --format="%(authordate:default) %(authordate)" refs/heads && - git for-each-ref --format="%(authordate) %(authordate:default)" refs/heads && - git for-each-ref --format="%(authordate:default) %(authordate:default)" refs/heads -' - -test_expect_success 'Check valid format specifiers for date fields' ' - git for-each-ref --format="%(authordate:default)" refs/heads && - git for-each-ref --format="%(authordate:relative)" refs/heads && - git for-each-ref --format="%(authordate:short)" refs/heads && - git for-each-ref --format="%(authordate:local)" refs/heads && - git for-each-ref --format="%(authordate:iso8601)" refs/heads && - git for-each-ref --format="%(authordate:rfc2822)" refs/heads -' - -test_expect_success 'Check invalid format specifiers are errors' ' - test_must_fail git for-each-ref --format="%(authordate:INVALID)" refs/heads -' - -test_expect_success 'arguments to %(objectname:short=) must be positive integers' ' - test_must_fail git for-each-ref --format="%(objectname:short=0)" && - test_must_fail git for-each-ref --format="%(objectname:short=-1)" && - test_must_fail git for-each-ref --format="%(objectname:short=foo)" -' - -test_bad_atom () { - case "$1" in - head) ref=refs/heads/main ;; - tag) ref=refs/tags/testtag ;; - sym) ref=refs/heads/sym ;; - *) ref=$1 ;; - esac - format=$2 - test_do=test_expect_${4:-success} - - printf '%s\n' "$3" >expect - $test_do $PREREQ "err basic atom: $ref $format" ' - test_must_fail git for-each-ref \ - --format="%($format)" "$ref" 2>error && - test_cmp expect error - ' -} - -test_bad_atom head 'authoremail:foo' \ - 'fatal: unrecognized %(authoremail) argument: foo' - -test_bad_atom head 'authoremail:mailmap,trim,bar' \ - 'fatal: unrecognized %(authoremail) argument: bar' - -test_bad_atom head 'authoremail:trim,' \ - 'fatal: unrecognized %(authoremail) argument: ' - -test_bad_atom head 'authoremail:mailmaptrim' \ - 'fatal: unrecognized %(authoremail) argument: trim' - -test_bad_atom head 'committeremail: ' \ - 'fatal: unrecognized %(committeremail) argument: ' - -test_bad_atom head 'committeremail: trim,foo' \ - 'fatal: unrecognized %(committeremail) argument: trim,foo' - -test_bad_atom head 'committeremail:mailmap,localpart ' \ - 'fatal: unrecognized %(committeremail) argument: ' - -test_bad_atom head 'committeremail:trim_localpart' \ - 'fatal: unrecognized %(committeremail) argument: _localpart' - -test_bad_atom head 'committeremail:localpart,,,trim' \ - 'fatal: unrecognized %(committeremail) argument: ,,trim' - -test_bad_atom tag 'taggeremail:mailmap,trim, foo ' \ - 'fatal: unrecognized %(taggeremail) argument: foo ' - -test_bad_atom tag 'taggeremail:trim,localpart,' \ - 'fatal: unrecognized %(taggeremail) argument: ' - -test_bad_atom tag 'taggeremail:mailmap;localpart trim' \ - 'fatal: unrecognized %(taggeremail) argument: ;localpart trim' - -test_bad_atom tag 'taggeremail:localpart trim' \ - 'fatal: unrecognized %(taggeremail) argument: trim' - -test_bad_atom tag 'taggeremail:mailmap,mailmap,trim,qux,localpart,trim' \ - 'fatal: unrecognized %(taggeremail) argument: qux,localpart,trim' - -test_date () { - f=$1 && - committer_date=$2 && - author_date=$3 && - tagger_date=$4 && - cat >expected <<-EOF && - 'refs/heads/main' '$committer_date' '$author_date' - 'refs/tags/testtag' '$tagger_date' - EOF - ( - git for-each-ref --shell \ - --format="%(refname) %(committerdate${f:+:$f}) %(authordate${f:+:$f})" \ - refs/heads && - git for-each-ref --shell \ - --format="%(refname) %(taggerdate${f:+:$f})" \ - refs/tags - ) >actual && - test_cmp expected actual -} - -test_expect_success 'Check unformatted date fields output' ' - test_date "" \ - "Tue Jul 4 01:18:43 2006 +0200" \ - "Tue Jul 4 01:18:44 2006 +0200" \ - "Tue Jul 4 01:18:45 2006 +0200" -' - -test_expect_success 'Check format "default" formatted date fields output' ' - test_date default \ - "Tue Jul 4 01:18:43 2006 +0200" \ - "Tue Jul 4 01:18:44 2006 +0200" \ - "Tue Jul 4 01:18:45 2006 +0200" -' - -test_expect_success 'Check format "default-local" date fields output' ' - test_date default-local "Mon Jul 3 23:18:43 2006" "Mon Jul 3 23:18:44 2006" "Mon Jul 3 23:18:45 2006" -' - -# Don't know how to do relative check because I can't know when this script -# is going to be run and can't fake the current time to git, and hence can't -# provide expected output. Instead, I'll just make sure that "relative" -# doesn't exit in error -test_expect_success 'Check format "relative" date fields output' ' - f=relative && - (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads && - git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual -' - -# We just check that this is the same as "relative" for now. -test_expect_success 'Check format "relative-local" date fields output' ' - test_date relative-local \ - "$(git for-each-ref --format="%(committerdate:relative)" refs/heads)" \ - "$(git for-each-ref --format="%(authordate:relative)" refs/heads)" \ - "$(git for-each-ref --format="%(taggerdate:relative)" refs/tags)" -' - -test_expect_success 'Check format "short" date fields output' ' - test_date short 2006-07-04 2006-07-04 2006-07-04 -' - -test_expect_success 'Check format "short-local" date fields output' ' - test_date short-local 2006-07-03 2006-07-03 2006-07-03 -' - -test_expect_success 'Check format "local" date fields output' ' - test_date local \ - "Mon Jul 3 23:18:43 2006" \ - "Mon Jul 3 23:18:44 2006" \ - "Mon Jul 3 23:18:45 2006" -' - -test_expect_success 'Check format "iso8601" date fields output' ' - test_date iso8601 \ - "2006-07-04 01:18:43 +0200" \ - "2006-07-04 01:18:44 +0200" \ - "2006-07-04 01:18:45 +0200" -' - -test_expect_success 'Check format "iso8601-local" date fields output' ' - test_date iso8601-local "2006-07-03 23:18:43 +0000" "2006-07-03 23:18:44 +0000" "2006-07-03 23:18:45 +0000" -' - -test_expect_success 'Check format "rfc2822" date fields output' ' - test_date rfc2822 \ - "Tue, 4 Jul 2006 01:18:43 +0200" \ - "Tue, 4 Jul 2006 01:18:44 +0200" \ - "Tue, 4 Jul 2006 01:18:45 +0200" -' - -test_expect_success 'Check format "rfc2822-local" date fields output' ' - test_date rfc2822-local "Mon, 3 Jul 2006 23:18:43 +0000" "Mon, 3 Jul 2006 23:18:44 +0000" "Mon, 3 Jul 2006 23:18:45 +0000" -' - -test_expect_success 'Check format "raw" date fields output' ' - test_date raw "1151968723 +0200" "1151968724 +0200" "1151968725 +0200" -' - -test_expect_success 'Check format "raw-local" date fields output' ' - test_date raw-local "1151968723 +0000" "1151968724 +0000" "1151968725 +0000" -' - -test_expect_success 'Check format of strftime date fields' ' - echo "my date is 2006-07-04" >expected && - git for-each-ref \ - --format="%(authordate:format:my date is %Y-%m-%d)" \ - refs/heads >actual && - test_cmp expected actual -' - -test_expect_success 'Check format of strftime-local date fields' ' - echo "my date is 2006-07-03" >expected && - git for-each-ref \ - --format="%(authordate:format-local:my date is %Y-%m-%d)" \ - refs/heads >actual && - test_cmp expected actual -' - -test_expect_success 'exercise strftime with odd fields' ' - echo >expected && - git for-each-ref --format="%(authordate:format:)" refs/heads >actual && - test_cmp expected actual && - long="long format -- $ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID$ZERO_OID" && - echo $long >expected && - git for-each-ref --format="%(authordate:format:$long)" refs/heads >actual && - test_cmp expected actual -' - -cat >expected <<\EOF -refs/heads/main -refs/remotes/origin/main -refs/tags/testtag -EOF - -test_expect_success 'Verify ascending sort' ' - git for-each-ref --format="%(refname)" --sort=refname >actual && - test_cmp expected actual -' - - -cat >expected <<\EOF -refs/tags/testtag -refs/remotes/origin/main -refs/heads/main -EOF - -test_expect_success 'Verify descending sort' ' - git for-each-ref --format="%(refname)" --sort=-refname >actual && - test_cmp expected actual -' - -test_expect_success 'Give help even with invalid sort atoms' ' - test_expect_code 129 git for-each-ref --sort=bogus -h >actual 2>&1 && - grep "^usage: git for-each-ref" actual -' - -cat >expected <<\EOF -refs/tags/testtag -refs/tags/testtag-2 -EOF - -test_expect_success 'exercise patterns with prefixes' ' - git tag testtag-2 && - test_when_finished "git tag -d testtag-2" && - git for-each-ref --format="%(refname)" \ - refs/tags/testtag refs/tags/testtag-2 >actual && - test_cmp expected actual -' - -cat >expected <<\EOF -refs/tags/testtag -refs/tags/testtag-2 -EOF - -test_expect_success 'exercise glob patterns with prefixes' ' - git tag testtag-2 && - test_when_finished "git tag -d testtag-2" && - git for-each-ref --format="%(refname)" \ - refs/tags/testtag "refs/tags/testtag-*" >actual && - test_cmp expected actual -' - -cat >expected <<\EOF -refs/tags/bar -refs/tags/baz -refs/tags/testtag -EOF - -test_expect_success 'exercise patterns with prefix exclusions' ' - for tag in foo/one foo/two foo/three bar baz - do - git tag "$tag" || return 1 - done && - test_when_finished "git tag -d foo/one foo/two foo/three bar baz" && - git for-each-ref --format="%(refname)" \ - refs/tags/ --exclude=refs/tags/foo >actual && - test_cmp expected actual -' - -cat >expected <<\EOF -refs/tags/bar -refs/tags/baz -refs/tags/foo/one -refs/tags/testtag -EOF - -test_expect_success 'exercise patterns with pattern exclusions' ' - for tag in foo/one foo/two foo/three bar baz - do - git tag "$tag" || return 1 - done && - test_when_finished "git tag -d foo/one foo/two foo/three bar baz" && - git for-each-ref --format="%(refname)" \ - refs/tags/ --exclude="refs/tags/foo/t*" >actual && - test_cmp expected actual -' - -cat >expected <<\EOF -'refs/heads/main' -'refs/remotes/origin/main' -'refs/tags/testtag' -EOF - -test_expect_success 'Quoting style: shell' ' - git for-each-ref --shell --format="%(refname)" >actual && - test_cmp expected actual -' - -test_expect_success 'Quoting style: perl' ' - git for-each-ref --perl --format="%(refname)" >actual && - test_cmp expected actual -' - -test_expect_success 'Quoting style: python' ' - git for-each-ref --python --format="%(refname)" >actual && - test_cmp expected actual -' - -cat >expected <<\EOF -"refs/heads/main" -"refs/remotes/origin/main" -"refs/tags/testtag" -EOF - -test_expect_success 'Quoting style: tcl' ' - git for-each-ref --tcl --format="%(refname)" >actual && - test_cmp expected actual -' - -for i in "--perl --shell" "-s --python" "--python --tcl" "--tcl --perl"; do - test_expect_success "more than one quoting style: $i" " - test_must_fail git for-each-ref $i 2>err && - grep '^error: more than one quoting style' err - " -done - -test_expect_success 'setup for upstream:track[short]' ' - test_commit two -' - -test_atom head upstream:track '[ahead 1]' -test_atom head upstream:trackshort '>' -test_atom head upstream:track,nobracket 'ahead 1' -test_atom head upstream:nobracket,track 'ahead 1' - -test_expect_success 'setup for push:track[short]' ' - test_commit third && - git update-ref refs/remotes/myfork/main main && - git reset main~1 -' - -test_atom head push:track '[behind 1]' -test_atom head push:trackshort '<' - -test_expect_success 'Check that :track[short] cannot be used with other atoms' ' - test_must_fail git for-each-ref --format="%(refname:track)" 2>/dev/null && - test_must_fail git for-each-ref --format="%(refname:trackshort)" 2>/dev/null -' - -test_expect_success 'Check that :track[short] works when upstream is invalid' ' - cat >expected <<-\EOF && - [gone] - - EOF - test_when_finished "git config branch.main.merge refs/heads/main" && - git config branch.main.merge refs/heads/does-not-exist && - git for-each-ref \ - --format="%(upstream:track)$LF%(upstream:trackshort)" \ - refs/heads >actual && - test_cmp expected actual -' - -test_expect_success 'Check for invalid refname format' ' - test_must_fail git for-each-ref --format="%(refname:INVALID)" -' - -test_expect_success 'set up color tests' ' - cat >expected.color <<-EOF && - $(git rev-parse --short refs/heads/main) <GREEN>main<RESET> - $(git rev-parse --short refs/remotes/myfork/main) <GREEN>myfork/main<RESET> - $(git rev-parse --short refs/remotes/origin/main) <GREEN>origin/main<RESET> - $(git rev-parse --short refs/tags/testtag) <GREEN>testtag<RESET> - $(git rev-parse --short refs/tags/third) <GREEN>third<RESET> - $(git rev-parse --short refs/tags/two) <GREEN>two<RESET> - EOF - sed "s/<[^>]*>//g" <expected.color >expected.bare && - color_format="%(objectname:short) %(color:green)%(refname:short)" -' - -test_expect_success TTY '%(color) shows color with a tty' ' - test_terminal git for-each-ref --format="$color_format" >actual.raw && - test_decode_color <actual.raw >actual && - test_cmp expected.color actual -' - -test_expect_success '%(color) does not show color without tty' ' - TERM=vt100 git for-each-ref --format="$color_format" >actual && - test_cmp expected.bare actual -' - -test_expect_success '--color can override tty check' ' - git for-each-ref --color --format="$color_format" >actual.raw && - test_decode_color <actual.raw >actual && - test_cmp expected.color actual -' - -test_expect_success 'color.ui=always does not override tty check' ' - git -c color.ui=always for-each-ref --format="$color_format" >actual && - test_cmp expected.bare actual -' - -test_expect_success 'setup for describe atom tests' ' - git init -b master describe-repo && - ( - cd describe-repo && - - test_commit --no-tag one && - git tag tagone && - - test_commit --no-tag two && - git tag -a -m "tag two" tagtwo - ) -' - -test_expect_success 'describe atom vs git describe' ' - ( - cd describe-repo && - - git for-each-ref --format="%(objectname)" \ - refs/tags/ >obj && - while read hash - do - if desc=$(git describe $hash) - then - : >expect-contains-good - else - : >expect-contains-bad - fi && - echo "$hash $desc" || return 1 - done <obj >expect && - test_path_exists expect-contains-good && - test_path_exists expect-contains-bad && - - git for-each-ref --format="%(objectname) %(describe)" \ - refs/tags/ >actual 2>err && - test_cmp expect actual && - test_must_be_empty err - ) -' - -test_expect_success 'describe:tags vs describe --tags' ' - ( - cd describe-repo && - git describe --tags >expect && - git for-each-ref --format="%(describe:tags)" \ - refs/heads/master >actual && - test_cmp expect actual - ) -' - -test_expect_success 'describe:abbrev=... vs describe --abbrev=...' ' - ( - cd describe-repo && - - # Case 1: We have commits between HEAD and the most - # recent tag reachable from it - test_commit --no-tag file && - git describe --abbrev=14 >expect && - git for-each-ref --format="%(describe:abbrev=14)" \ - refs/heads/master >actual && - test_cmp expect actual && - - # Make sure the hash used is at least 14 digits long - sed -e "s/^.*-g\([0-9a-f]*\)$/\1/" <actual >hexpart && - test 15 -le $(wc -c <hexpart) && - - # Case 2: We have a tag at HEAD, describe directly gives - # the name of the tag - git tag -a -m tagged tagname && - git describe --abbrev=14 >expect && - git for-each-ref --format="%(describe:abbrev=14)" \ - refs/heads/master >actual && - test_cmp expect actual && - test tagname = $(cat actual) - ) -' - -test_expect_success 'describe:match=... vs describe --match ...' ' - ( - cd describe-repo && - git tag -a -m "tag foo" tag-foo && - git describe --match "*-foo" >expect && - git for-each-ref --format="%(describe:match="*-foo")" \ - refs/heads/master >actual && - test_cmp expect actual - ) -' - -test_expect_success 'describe:exclude:... vs describe --exclude ...' ' - ( - cd describe-repo && - git tag -a -m "tag bar" tag-bar && - git describe --exclude "*-bar" >expect && - git for-each-ref --format="%(describe:exclude="*-bar")" \ - refs/heads/master >actual && - test_cmp expect actual - ) -' - -test_expect_success 'deref with describe atom' ' - ( - cd describe-repo && - cat >expect <<-\EOF && - - tagname - tagname - tagname - - tagtwo - EOF - git for-each-ref --format="%(*describe)" >actual && - test_cmp expect actual - ) -' - -test_expect_success 'err on bad describe atom arg' ' - ( - cd describe-repo && - - # The bad arg is the only arg passed to describe atom - cat >expect <<-\EOF && - fatal: unrecognized %(describe) argument: baz - EOF - test_must_fail git for-each-ref --format="%(describe:baz)" \ - refs/heads/master 2>actual && - test_cmp expect actual && - - # The bad arg is in the middle of the option string - # passed to the describe atom - cat >expect <<-\EOF && - fatal: unrecognized %(describe) argument: qux=1,abbrev=14 - EOF - test_must_fail git for-each-ref \ - --format="%(describe:tags,qux=1,abbrev=14)" \ - ref/heads/master 2>actual && - test_cmp expect actual - ) -' - -cat >expected <<\EOF -heads/main -tags/main -EOF - -test_expect_success 'Check ambiguous head and tag refs (strict)' ' - git config --bool core.warnambiguousrefs true && - git checkout -b newtag && - echo "Using $datestamp" > one && - git add one && - git commit -m "Branch" && - setdate_and_increment && - git tag -m "Tagging at $datestamp" main && - git for-each-ref --format "%(refname:short)" refs/heads/main refs/tags/main >actual && - test_cmp expected actual -' - -cat >expected <<\EOF -heads/main -main -EOF - -test_expect_success 'Check ambiguous head and tag refs (loose)' ' - git config --bool core.warnambiguousrefs false && - git for-each-ref --format "%(refname:short)" refs/heads/main refs/tags/main >actual && - test_cmp expected actual -' - -cat >expected <<\EOF -heads/ambiguous -ambiguous -EOF - -test_expect_success 'Check ambiguous head and tag refs II (loose)' ' - git checkout main && - git tag ambiguous testtag^0 && - git branch ambiguous testtag^0 && - git for-each-ref --format "%(refname:short)" refs/heads/ambiguous refs/tags/ambiguous >actual && - test_cmp expected actual -' - -test_expect_success 'create tag without tagger' ' - git tag -a -m "Broken tag" taggerless && - git tag -f taggerless $(git cat-file tag taggerless | - sed -e "/^tagger /d" | - git hash-object --literally --stdin -w -t tag) -' - -test_atom refs/tags/taggerless type 'commit' -test_atom refs/tags/taggerless tag 'taggerless' -test_atom refs/tags/taggerless tagger '' -test_atom refs/tags/taggerless taggername '' -test_atom refs/tags/taggerless taggeremail '' -test_atom refs/tags/taggerless taggeremail:trim '' -test_atom refs/tags/taggerless taggeremail:localpart '' -test_atom refs/tags/taggerless taggerdate '' -test_atom refs/tags/taggerless committer '' -test_atom refs/tags/taggerless committername '' -test_atom refs/tags/taggerless committeremail '' -test_atom refs/tags/taggerless committeremail:trim '' -test_atom refs/tags/taggerless committeremail:localpart '' -test_atom refs/tags/taggerless committerdate '' -test_atom refs/tags/taggerless subject 'Broken tag' - -test_expect_success 'an unusual tag with an incomplete line' ' - - git tag -m "bogo" bogo && - bogo=$(git cat-file tag bogo) && - bogo=$(printf "%s" "$bogo" | git mktag) && - git tag -f bogo "$bogo" && - git for-each-ref --format "%(body)" refs/tags/bogo - -' - -test_expect_success 'create tag with subject and body content' ' - cat >>msg <<-\EOF && - the subject line - - first body line - second body line - EOF - git tag -F msg subject-body -' -test_atom refs/tags/subject-body subject 'the subject line' -test_atom refs/tags/subject-body subject:sanitize 'the-subject-line' -test_atom refs/tags/subject-body body 'first body line -second body line -' -test_atom refs/tags/subject-body contents 'the subject line - -first body line -second body line -' - -test_expect_success 'create tag with multiline subject' ' - cat >msg <<-\EOF && - first subject line - second subject line - - first body line - second body line - EOF - git tag -F msg multiline -' -test_atom refs/tags/multiline subject 'first subject line second subject line' -test_atom refs/tags/multiline subject:sanitize 'first-subject-line-second-subject-line' -test_atom refs/tags/multiline contents:subject 'first subject line second subject line' -test_atom refs/tags/multiline body 'first body line -second body line -' -test_atom refs/tags/multiline contents:body 'first body line -second body line -' -test_atom refs/tags/multiline contents:signature '' -test_atom refs/tags/multiline contents 'first subject line -second subject line - -first body line -second body line -' - -test_expect_success GPG 'create signed tags' ' - git tag -s -m "" signed-empty && - git tag -s -m "subject line" signed-short && - cat >msg <<-\EOF && - subject line - - body contents - EOF - git tag -s -F msg signed-long -' - -sig='-----BEGIN PGP SIGNATURE----- ------END PGP SIGNATURE----- -' - -PREREQ=GPG -test_atom refs/tags/signed-empty subject '' -test_atom refs/tags/signed-empty subject:sanitize '' -test_atom refs/tags/signed-empty contents:subject '' -test_atom refs/tags/signed-empty body "$sig" -test_atom refs/tags/signed-empty contents:body '' -test_atom refs/tags/signed-empty contents:signature "$sig" -test_atom refs/tags/signed-empty contents "$sig" - -test_expect_success GPG 'basic atom: refs/tags/signed-empty raw' ' - git cat-file tag refs/tags/signed-empty >expected && - git for-each-ref --format="%(raw)" refs/tags/signed-empty >actual && - sanitize_pgp <expected >expected.clean && - echo >>expected.clean && - sanitize_pgp <actual >actual.clean && - test_cmp expected.clean actual.clean -' - -test_atom refs/tags/signed-short subject 'subject line' -test_atom refs/tags/signed-short subject:sanitize 'subject-line' -test_atom refs/tags/signed-short contents:subject 'subject line' -test_atom refs/tags/signed-short body "$sig" -test_atom refs/tags/signed-short contents:body '' -test_atom refs/tags/signed-short contents:signature "$sig" -test_atom refs/tags/signed-short contents "subject line -$sig" - -test_expect_success GPG 'basic atom: refs/tags/signed-short raw' ' - git cat-file tag refs/tags/signed-short >expected && - git for-each-ref --format="%(raw)" refs/tags/signed-short >actual && - sanitize_pgp <expected >expected.clean && - echo >>expected.clean && - sanitize_pgp <actual >actual.clean && - test_cmp expected.clean actual.clean -' - -test_atom refs/tags/signed-long subject 'subject line' -test_atom refs/tags/signed-long subject:sanitize 'subject-line' -test_atom refs/tags/signed-long contents:subject 'subject line' -test_atom refs/tags/signed-long body "body contents -$sig" -test_atom refs/tags/signed-long contents:body 'body contents -' -test_atom refs/tags/signed-long contents:signature "$sig" -test_atom refs/tags/signed-long contents "subject line - -body contents -$sig" - -test_expect_success GPG 'basic atom: refs/tags/signed-long raw' ' - git cat-file tag refs/tags/signed-long >expected && - git for-each-ref --format="%(raw)" refs/tags/signed-long >actual && - sanitize_pgp <expected >expected.clean && - echo >>expected.clean && - sanitize_pgp <actual >actual.clean && - test_cmp expected.clean actual.clean -' - -test_expect_success 'set up refs pointing to tree and blob' ' - git update-ref refs/mytrees/first refs/heads/main^{tree} && - git update-ref refs/myblobs/first refs/heads/main:one -' - -test_atom refs/mytrees/first subject "" -test_atom refs/mytrees/first contents:subject "" -test_atom refs/mytrees/first body "" -test_atom refs/mytrees/first contents:body "" -test_atom refs/mytrees/first contents:signature "" -test_atom refs/mytrees/first contents "" - -test_expect_success 'basic atom: refs/mytrees/first raw' ' - git cat-file tree refs/mytrees/first >expected && - echo >>expected && - git for-each-ref --format="%(raw)" refs/mytrees/first >actual && - test_cmp expected actual && - git cat-file -s refs/mytrees/first >expected && - git for-each-ref --format="%(raw:size)" refs/mytrees/first >actual && - test_cmp expected actual -' - -test_atom refs/myblobs/first subject "" -test_atom refs/myblobs/first contents:subject "" -test_atom refs/myblobs/first body "" -test_atom refs/myblobs/first contents:body "" -test_atom refs/myblobs/first contents:signature "" -test_atom refs/myblobs/first contents "" - -test_expect_success 'basic atom: refs/myblobs/first raw' ' - git cat-file blob refs/myblobs/first >expected && - echo >>expected && - git for-each-ref --format="%(raw)" refs/myblobs/first >actual && - test_cmp expected actual && - git cat-file -s refs/myblobs/first >expected && - git for-each-ref --format="%(raw:size)" refs/myblobs/first >actual && - test_cmp expected actual -' - -test_expect_success 'set up refs pointing to binary blob' ' - printf "a\0b\0c" >blob1 && - printf "a\0c\0b" >blob2 && - printf "\0a\0b\0c" >blob3 && - printf "abc" >blob4 && - printf "\0 \0 \0 " >blob5 && - printf "\0 \0a\0 " >blob6 && - printf " " >blob7 && - >blob8 && - obj=$(git hash-object -w blob1) && - git update-ref refs/myblobs/blob1 "$obj" && - obj=$(git hash-object -w blob2) && - git update-ref refs/myblobs/blob2 "$obj" && - obj=$(git hash-object -w blob3) && - git update-ref refs/myblobs/blob3 "$obj" && - obj=$(git hash-object -w blob4) && - git update-ref refs/myblobs/blob4 "$obj" && - obj=$(git hash-object -w blob5) && - git update-ref refs/myblobs/blob5 "$obj" && - obj=$(git hash-object -w blob6) && - git update-ref refs/myblobs/blob6 "$obj" && - obj=$(git hash-object -w blob7) && - git update-ref refs/myblobs/blob7 "$obj" && - obj=$(git hash-object -w blob8) && - git update-ref refs/myblobs/blob8 "$obj" -' - -test_expect_success 'Verify sorts with raw' ' - cat >expected <<-EOF && - refs/myblobs/blob8 - refs/myblobs/blob5 - refs/myblobs/blob6 - refs/myblobs/blob3 - refs/myblobs/blob7 - refs/mytrees/first - refs/myblobs/first - refs/myblobs/blob1 - refs/myblobs/blob2 - refs/myblobs/blob4 - refs/heads/main - EOF - git for-each-ref --format="%(refname)" --sort=raw \ - refs/heads/main refs/myblobs/ refs/mytrees/first >actual && - test_cmp expected actual -' - -test_expect_success 'Verify sorts with raw:size' ' - cat >expected <<-EOF && - refs/myblobs/blob8 - refs/myblobs/blob7 - refs/myblobs/blob4 - refs/myblobs/blob1 - refs/myblobs/blob2 - refs/myblobs/blob3 - refs/myblobs/blob5 - refs/myblobs/blob6 - refs/myblobs/first - refs/mytrees/first - refs/heads/main - EOF - git for-each-ref --format="%(refname)" --sort=raw:size \ - refs/heads/main refs/myblobs/ refs/mytrees/first >actual && - test_cmp expected actual -' - -test_expect_success 'validate raw atom with %(if:equals)' ' - cat >expected <<-EOF && - not equals - not equals - not equals - not equals - not equals - not equals - refs/myblobs/blob4 - not equals - not equals - not equals - not equals - not equals - EOF - git for-each-ref --format="%(if:equals=abc)%(raw)%(then)%(refname)%(else)not equals%(end)" \ - refs/myblobs/ refs/heads/ >actual && - test_cmp expected actual -' - -test_expect_success 'validate raw atom with %(if:notequals)' ' - cat >expected <<-EOF && - refs/heads/ambiguous - refs/heads/main - refs/heads/newtag - refs/myblobs/blob1 - refs/myblobs/blob2 - refs/myblobs/blob3 - equals - refs/myblobs/blob5 - refs/myblobs/blob6 - refs/myblobs/blob7 - refs/myblobs/blob8 - refs/myblobs/first - EOF - git for-each-ref --format="%(if:notequals=abc)%(raw)%(then)%(refname)%(else)equals%(end)" \ - refs/myblobs/ refs/heads/ >actual && - test_cmp expected actual -' - -test_expect_success 'empty raw refs with %(if)' ' - cat >expected <<-EOF && - refs/myblobs/blob1 not empty - refs/myblobs/blob2 not empty - refs/myblobs/blob3 not empty - refs/myblobs/blob4 not empty - refs/myblobs/blob5 not empty - refs/myblobs/blob6 not empty - refs/myblobs/blob7 empty - refs/myblobs/blob8 empty - refs/myblobs/first not empty - EOF - git for-each-ref --format="%(refname) %(if)%(raw)%(then)not empty%(else)empty%(end)" \ - refs/myblobs/ >actual && - test_cmp expected actual -' - -test_expect_success '%(raw) with --python must fail' ' - test_must_fail git for-each-ref --format="%(raw)" --python -' - -test_expect_success '%(raw) with --tcl must fail' ' - test_must_fail git for-each-ref --format="%(raw)" --tcl -' - -test_expect_success PERL_TEST_HELPERS '%(raw) with --perl' ' - git for-each-ref --format="\$name= %(raw); -print \"\$name\"" refs/myblobs/blob1 --perl | perl >actual && - cmp blob1 actual && - git for-each-ref --format="\$name= %(raw); -print \"\$name\"" refs/myblobs/blob3 --perl | perl >actual && - cmp blob3 actual && - git for-each-ref --format="\$name= %(raw); -print \"\$name\"" refs/myblobs/blob8 --perl | perl >actual && - cmp blob8 actual && - git for-each-ref --format="\$name= %(raw); -print \"\$name\"" refs/myblobs/first --perl | perl >actual && - cmp one actual && - git cat-file tree refs/mytrees/first > expected && - git for-each-ref --format="\$name= %(raw); -print \"\$name\"" refs/mytrees/first --perl | perl >actual && - cmp expected actual -' - -test_expect_success '%(raw) with --shell must fail' ' - test_must_fail git for-each-ref --format="%(raw)" --shell -' - -test_expect_success '%(raw) with --shell and --sort=raw must fail' ' - test_must_fail git for-each-ref --format="%(raw)" --sort=raw --shell -' - -test_expect_success '%(raw:size) with --shell' ' - git for-each-ref --format="%(raw:size)" | sed "s/^/$SQ/;s/$/$SQ/" >expect && - git for-each-ref --format="%(raw:size)" --shell >actual && - test_cmp expect actual -' - -test_expect_success 'for-each-ref --format compare with cat-file --batch' ' - git rev-parse refs/mytrees/first | git cat-file --batch >expected && - git for-each-ref --format="%(objectname) %(objecttype) %(objectsize) -%(raw)" refs/mytrees/first >actual && - test_cmp expected actual -' - -test_expect_success 'verify sorts with contents:size' ' - cat >expect <<-\EOF && - refs/heads/main - refs/heads/newtag - refs/heads/ambiguous - EOF - git for-each-ref --format="%(refname)" \ - --sort=contents:size refs/heads/ >actual && - test_cmp expect actual -' - -test_expect_success 'set up multiple-sort tags' ' - for when in 100000 200000 - do - for email in user1 user2 - do - for ref in ref1 ref2 - do - GIT_COMMITTER_DATE="@$when +0000" \ - GIT_COMMITTER_EMAIL="$email@example.com" \ - git tag -m "tag $ref-$when-$email" \ - multi-$ref-$when-$email || return 1 - done - done - done -' - -test_expect_success 'Verify sort with multiple keys' ' - cat >expected <<-\EOF && - 100000 <user1@example.com> refs/tags/multi-ref2-100000-user1 - 100000 <user1@example.com> refs/tags/multi-ref1-100000-user1 - 100000 <user2@example.com> refs/tags/multi-ref2-100000-user2 - 100000 <user2@example.com> refs/tags/multi-ref1-100000-user2 - 200000 <user1@example.com> refs/tags/multi-ref2-200000-user1 - 200000 <user1@example.com> refs/tags/multi-ref1-200000-user1 - 200000 <user2@example.com> refs/tags/multi-ref2-200000-user2 - 200000 <user2@example.com> refs/tags/multi-ref1-200000-user2 - EOF - git for-each-ref \ - --format="%(taggerdate:unix) %(taggeremail) %(refname)" \ - --sort=-refname \ - --sort=taggeremail \ - --sort=taggerdate \ - "refs/tags/multi-*" >actual && - test_cmp expected actual -' - -test_expect_success 'equivalent sorts fall back on refname' ' - cat >expected <<-\EOF && - 100000 <user1@example.com> refs/tags/multi-ref1-100000-user1 - 100000 <user2@example.com> refs/tags/multi-ref1-100000-user2 - 100000 <user1@example.com> refs/tags/multi-ref2-100000-user1 - 100000 <user2@example.com> refs/tags/multi-ref2-100000-user2 - 200000 <user1@example.com> refs/tags/multi-ref1-200000-user1 - 200000 <user2@example.com> refs/tags/multi-ref1-200000-user2 - 200000 <user1@example.com> refs/tags/multi-ref2-200000-user1 - 200000 <user2@example.com> refs/tags/multi-ref2-200000-user2 - EOF - git for-each-ref \ - --format="%(taggerdate:unix) %(taggeremail) %(refname)" \ - --sort=taggerdate \ - "refs/tags/multi-*" >actual && - test_cmp expected actual -' - -test_expect_success '--no-sort cancels the previous sort keys' ' - cat >expected <<-\EOF && - 100000 <user1@example.com> refs/tags/multi-ref1-100000-user1 - 100000 <user2@example.com> refs/tags/multi-ref1-100000-user2 - 100000 <user1@example.com> refs/tags/multi-ref2-100000-user1 - 100000 <user2@example.com> refs/tags/multi-ref2-100000-user2 - 200000 <user1@example.com> refs/tags/multi-ref1-200000-user1 - 200000 <user2@example.com> refs/tags/multi-ref1-200000-user2 - 200000 <user1@example.com> refs/tags/multi-ref2-200000-user1 - 200000 <user2@example.com> refs/tags/multi-ref2-200000-user2 - EOF - git for-each-ref \ - --format="%(taggerdate:unix) %(taggeremail) %(refname)" \ - --sort=-refname \ - --sort=taggeremail \ - --no-sort \ - --sort=taggerdate \ - "refs/tags/multi-*" >actual && - test_cmp expected actual -' - -test_expect_success '--no-sort without subsequent --sort prints expected refs' ' - cat >expected <<-\EOF && - refs/tags/multi-ref1-100000-user1 - refs/tags/multi-ref1-100000-user2 - refs/tags/multi-ref1-200000-user1 - refs/tags/multi-ref1-200000-user2 - refs/tags/multi-ref2-100000-user1 - refs/tags/multi-ref2-100000-user2 - refs/tags/multi-ref2-200000-user1 - refs/tags/multi-ref2-200000-user2 - EOF - - # Sort the results with `sort` for a consistent comparison against - # expected - git for-each-ref \ - --format="%(refname)" \ - --no-sort \ - "refs/tags/multi-*" | sort >actual && - test_cmp expected actual -' - -test_expect_success 'set up custom date sorting' ' - # Dates: - # - Wed Feb 07 2024 21:34:20 +0000 - # - Tue Dec 14 1999 00:05:22 +0000 - # - Fri Jun 04 2021 11:26:51 +0000 - # - Mon Jan 22 2007 16:44:01 GMT+0000 - i=1 && - for when in 1707341660 945129922 1622806011 1169484241 - do - GIT_COMMITTER_DATE="@$when +0000" \ - GIT_COMMITTER_EMAIL="user@example.com" \ - git tag -m "tag $when" custom-dates-$i && - i=$(($i+1)) || return 1 - done -' - -test_expect_success 'sort by date defaults to full timestamp' ' - cat >expected <<-\EOF && - 945129922 refs/tags/custom-dates-2 - 1169484241 refs/tags/custom-dates-4 - 1622806011 refs/tags/custom-dates-3 - 1707341660 refs/tags/custom-dates-1 - EOF - - git for-each-ref \ - --format="%(creatordate:unix) %(refname)" \ - --sort=creatordate \ - "refs/tags/custom-dates-*" >actual && - test_cmp expected actual -' - -test_expect_success 'sort by custom date format' ' - cat >expected <<-\EOF && - 00:05:22 refs/tags/custom-dates-2 - 11:26:51 refs/tags/custom-dates-3 - 16:44:01 refs/tags/custom-dates-4 - 21:34:20 refs/tags/custom-dates-1 - EOF - - git for-each-ref \ - --format="%(creatordate:format:%H:%M:%S) %(refname)" \ - --sort="creatordate:format:%H:%M:%S" \ - "refs/tags/custom-dates-*" >actual && - test_cmp expected actual -' - -test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' ' - test_when_finished "git checkout main" && - git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual && - sed -e "s/^\* / /" actual >expect && - git checkout --orphan orphaned-branch && - git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual && - test_cmp expect actual -' - -cat >trailers <<EOF -Reviewed-by: A U Thor <author@example.com> -Signed-off-by: A U Thor <author@example.com> -[ v2 updated patch description ] -Acked-by: A U Thor - <author@example.com> -EOF - -unfold () { - perl -0pe 's/\n\s+/ /g' -} - -test_expect_success 'set up trailers for next test' ' - echo "Some contents" > two && - git add two && - git commit -F - <<-EOF - trailers: this commit message has trailers - - Some message contents - - $(cat trailers) - EOF -' - -test_trailer_option () { - if test "$#" -eq 3 - then - prereq="$1" - shift - fi && - title=$1 option=$2 - cat >expect - test_expect_success $prereq "$title" ' - git for-each-ref --format="%($option)" refs/heads/main >actual && - test_cmp expect actual && - git for-each-ref --format="%(contents:$option)" refs/heads/main >actual && - test_cmp expect actual - ' -} - -test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) unfolds trailers' \ - 'trailers:unfold' <<-EOF - $(unfold <trailers) - - EOF - -test_trailer_option '%(trailers:only) shows only "key: value" trailers' \ - 'trailers:only' <<-EOF - $(grep -v patch.description <trailers) - - EOF - -test_trailer_option '%(trailers:only=no,only=true) shows only "key: value" trailers' \ - 'trailers:only=no,only=true' <<-EOF - $(grep -v patch.description <trailers) - - EOF - -test_trailer_option '%(trailers:only=yes) shows only "key: value" trailers' \ - 'trailers:only=yes' <<-EOF - $(grep -v patch.description <trailers) - - EOF - -test_trailer_option '%(trailers:only=no) shows all trailers' \ - 'trailers:only=no' <<-EOF - $(cat trailers) - - EOF - -test_trailer_option PERL_TEST_HELPERS '%(trailers:only) and %(trailers:unfold) work together' \ - 'trailers:only,unfold' <<-EOF - $(grep -v patch.description <trailers | unfold) - - EOF - -test_trailer_option PERL_TEST_HELPERS '%(trailers:unfold) and %(trailers:only) work together' \ - 'trailers:unfold,only' <<-EOF - $(grep -v patch.description <trailers | unfold) - - EOF - -test_trailer_option '%(trailers:key=foo) shows that trailer' \ - 'trailers:key=Signed-off-by' <<-EOF - Signed-off-by: A U Thor <author@example.com> - - EOF - -test_trailer_option '%(trailers:key=foo) is case insensitive' \ - 'trailers:key=SiGned-oFf-bY' <<-EOF - Signed-off-by: A U Thor <author@example.com> - - EOF - -test_trailer_option '%(trailers:key=foo:) trailing colon also works' \ - 'trailers:key=Signed-off-by:' <<-EOF - Signed-off-by: A U Thor <author@example.com> - - EOF - -test_trailer_option '%(trailers:key=foo) multiple keys' \ - 'trailers:key=Reviewed-by:,key=Signed-off-by' <<-EOF - Reviewed-by: A U Thor <author@example.com> - Signed-off-by: A U Thor <author@example.com> - - EOF - -test_trailer_option '%(trailers:key=nonexistent) becomes empty' \ - 'trailers:key=Shined-off-by:' <<-EOF - - EOF - -test_trailer_option '%(trailers:key=foo) handles multiple lines even if folded' \ - 'trailers:key=Acked-by' <<-EOF - $(grep -v patch.description <trailers | grep -v Signed-off-by | grep -v Reviewed-by) - - EOF - -test_trailer_option '%(trailers:key=foo,unfold) properly unfolds' \ - 'trailers:key=Signed-Off-by,unfold' <<-EOF - $(unfold <trailers | grep Signed-off-by) - - EOF - -test_trailer_option '%(trailers:key=foo,only=no) also includes nontrailer lines' \ - 'trailers:key=Signed-off-by,only=no' <<-EOF - Signed-off-by: A U Thor <author@example.com> - $(grep patch.description <trailers) - - EOF - -test_trailer_option '%(trailers:key=foo,valueonly) shows only value' \ - 'trailers:key=Signed-off-by,valueonly' <<-EOF - A U Thor <author@example.com> - - EOF - -test_trailer_option '%(trailers:separator) changes separator' \ - 'trailers:separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF - Reviewed-by: A U Thor <author@example.com>,Signed-off-by: A U Thor <author@example.com> - EOF - -test_trailer_option '%(trailers:key_value_separator) changes key-value separator' \ - 'trailers:key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF - Reviewed-by,A U Thor <author@example.com> - Signed-off-by,A U Thor <author@example.com> - - EOF - -test_trailer_option '%(trailers:separator,key_value_separator) changes both separators' \ - 'trailers:separator=%x2C,key_value_separator=%x2C,key=Reviewed-by,key=Signed-off-by:' <<-EOF - Reviewed-by,A U Thor <author@example.com>,Signed-off-by,A U Thor <author@example.com> - EOF - -test_expect_success 'multiple %(trailers) use their own options' ' - git tag -F - tag-with-trailers <<-\EOF && - body - - one: foo - one: bar - two: baz - two: qux - EOF - t1="%(trailers:key=one,key_value_separator=W,separator=X)" && - t2="%(trailers:key=two,key_value_separator=Y,separator=Z)" && - git for-each-ref --format="$t1%0a$t2" refs/tags/tag-with-trailers >actual && - cat >expect <<-\EOF && - oneWfooXoneWbar - twoYbazZtwoYqux - EOF - test_cmp expect actual -' - -test_failing_trailer_option () { - title=$1 option=$2 - cat >expect - test_expect_success "$title" ' - # error message cannot be checked under i18n - test_must_fail git for-each-ref --format="%($option)" refs/heads/main 2>actual && - test_cmp expect actual && - test_must_fail git for-each-ref --format="%(contents:$option)" refs/heads/main 2>actual && - test_cmp expect actual - ' -} - -test_failing_trailer_option '%(trailers) rejects unknown trailers arguments' \ - 'trailers:unsupported' <<-\EOF - fatal: unknown %(trailers) argument: unsupported - EOF - -test_failing_trailer_option '%(trailers:key) without value is error' \ - 'trailers:key' <<-\EOF - fatal: expected %(trailers:key=<value>) - EOF - -test_expect_success 'if arguments, %(contents:trailers) shows error if colon is missing' ' - cat >expect <<-EOF && - fatal: unrecognized %(contents) argument: trailersonly - EOF - test_must_fail git for-each-ref --format="%(contents:trailersonly)" 2>actual && - test_cmp expect actual -' - -test_expect_success 'basic atom: head contents:trailers' ' - git for-each-ref --format="%(contents:trailers)" refs/heads/main >actual && - sanitize_pgp <actual >actual.clean && - # git for-each-ref ends with a blank line - cat >expect <<-EOF && - $(cat trailers) - - EOF - test_cmp expect actual.clean -' - -test_expect_success 'basic atom: rest must fail' ' - test_must_fail git for-each-ref --format="%(rest)" refs/heads/main -' - -test_expect_success 'HEAD atom does not take arguments' ' - test_must_fail git for-each-ref --format="%(HEAD:foo)" 2>err && - echo "fatal: %(HEAD) does not take arguments" >expect && - test_cmp expect err -' - -test_expect_success 'subject atom rejects unknown arguments' ' - test_must_fail git for-each-ref --format="%(subject:foo)" 2>err && - echo "fatal: unrecognized %(subject) argument: foo" >expect && - test_cmp expect err -' - -test_expect_success 'refname atom rejects unknown arguments' ' - test_must_fail git for-each-ref --format="%(refname:foo)" 2>err && - echo "fatal: unrecognized %(refname) argument: foo" >expect && - test_cmp expect err -' - -test_expect_success 'trailer parsing not fooled by --- line' ' - git commit --allow-empty -F - <<-\EOF && - this is the subject - - This is the body. The message has a "---" line which would confuse a - message+patch parser. But here we know we have only a commit message, - so we get it right. - - trailer: wrong - --- - This is more body. - - trailer: right - EOF - - { - echo "trailer: right" && - echo - } >expect && - git for-each-ref --format="%(trailers)" refs/heads/main >actual && - test_cmp expect actual -' - -test_expect_success 'Add symbolic ref for the following tests' ' - git symbolic-ref refs/heads/sym refs/heads/main -' - -cat >expected <<EOF -refs/heads/main -EOF - -test_expect_success 'Verify usage of %(symref) atom' ' - git for-each-ref --format="%(symref)" refs/heads/sym >actual && - test_cmp expected actual -' - -cat >expected <<EOF -heads/main -EOF - -test_expect_success 'Verify usage of %(symref:short) atom' ' - git for-each-ref --format="%(symref:short)" refs/heads/sym >actual && - test_cmp expected actual -' - -cat >expected <<EOF -main -heads/main -EOF - -test_expect_success 'Verify usage of %(symref:lstrip) atom' ' - git for-each-ref --format="%(symref:lstrip=2)" refs/heads/sym > actual && - git for-each-ref --format="%(symref:lstrip=-2)" refs/heads/sym >> actual && - test_cmp expected actual && - - git for-each-ref --format="%(symref:strip=2)" refs/heads/sym > actual && - git for-each-ref --format="%(symref:strip=-2)" refs/heads/sym >> actual && - test_cmp expected actual -' - -cat >expected <<EOF -refs -refs/heads -EOF - -test_expect_success 'Verify usage of %(symref:rstrip) atom' ' - git for-each-ref --format="%(symref:rstrip=2)" refs/heads/sym > actual && - git for-each-ref --format="%(symref:rstrip=-2)" refs/heads/sym >> actual && - test_cmp expected actual -' - -test_expect_success ':remotename and :remoteref' ' - git init remote-tests && - ( - cd remote-tests && - test_commit initial && - git branch -M main && - git remote add from fifth.coffee:blub && - git config branch.main.remote from && - git config branch.main.merge refs/heads/stable && - git remote add to southridge.audio:repo && - git config remote.to.push "refs/heads/*:refs/heads/pushed/*" && - git config branch.main.pushRemote to && - for pair in "%(upstream)=refs/remotes/from/stable" \ - "%(upstream:remotename)=from" \ - "%(upstream:remoteref)=refs/heads/stable" \ - "%(push)=refs/remotes/to/pushed/main" \ - "%(push:remotename)=to" \ - "%(push:remoteref)=refs/heads/pushed/main" - do - echo "${pair#*=}" >expect && - git for-each-ref --format="${pair%=*}" \ - refs/heads/main >actual && - test_cmp expect actual || exit 1 - done && - git branch push-simple && - git config branch.push-simple.pushRemote from && - actual="$(git for-each-ref \ - --format="%(push:remotename),%(push:remoteref)" \ - refs/heads/push-simple)" && - test from, = "$actual" - ) -' - -test_expect_success 'for-each-ref --ignore-case ignores case' ' - git for-each-ref --format="%(refname)" refs/heads/MAIN >actual && - test_must_be_empty actual && - - echo refs/heads/main >expect && - git for-each-ref --format="%(refname)" --ignore-case \ - refs/heads/MAIN >actual && - test_cmp expect actual -' - -test_expect_success 'for-each-ref --omit-empty works' ' - git for-each-ref --format="%(refname)" >actual && - test_line_count -gt 1 actual && - git for-each-ref --format="%(if:equals=refs/heads/main)%(refname)%(then)%(refname)%(end)" --omit-empty >actual && - echo refs/heads/main >expect && - test_cmp expect actual -' - -test_expect_success 'for-each-ref --ignore-case works on multiple sort keys' ' - # name refs numerically to avoid case-insensitive filesystem conflicts - nr=0 && - for email in a A b B - do - for subject in a A b B - do - GIT_COMMITTER_EMAIL="$email@example.com" \ - git tag -m "tag $subject" icase-$(printf %02d $nr) && - nr=$((nr+1))|| - return 1 - done - done && - git for-each-ref --ignore-case \ - --format="%(taggeremail) %(subject) %(refname)" \ - --sort=refname \ - --sort=subject \ - --sort=taggeremail \ - refs/tags/icase-* >actual && - cat >expect <<-\EOF && - <a@example.com> tag a refs/tags/icase-00 - <a@example.com> tag A refs/tags/icase-01 - <A@example.com> tag a refs/tags/icase-04 - <A@example.com> tag A refs/tags/icase-05 - <a@example.com> tag b refs/tags/icase-02 - <a@example.com> tag B refs/tags/icase-03 - <A@example.com> tag b refs/tags/icase-06 - <A@example.com> tag B refs/tags/icase-07 - <b@example.com> tag a refs/tags/icase-08 - <b@example.com> tag A refs/tags/icase-09 - <B@example.com> tag a refs/tags/icase-12 - <B@example.com> tag A refs/tags/icase-13 - <b@example.com> tag b refs/tags/icase-10 - <b@example.com> tag B refs/tags/icase-11 - <B@example.com> tag b refs/tags/icase-14 - <B@example.com> tag B refs/tags/icase-15 - EOF - test_cmp expect actual -' - -test_expect_success 'for-each-ref reports broken tags' ' - git tag -m "good tag" broken-tag-good HEAD && - git cat-file tag broken-tag-good >good && - sed s/commit/blob/ <good >bad && - bad=$(git hash-object -w -t tag bad) && - git update-ref refs/tags/broken-tag-bad $bad && - test_must_fail git for-each-ref --format="%(*objectname)" \ - refs/tags/broken-tag-* -' - -test_expect_success 'set up tag with signature and no blank lines' ' - git tag -F - fake-sig-no-blanks <<-\EOF - this is the subject - -----BEGIN PGP SIGNATURE----- - not a real signature, but we just care about the - subject/body parsing. It is important here that - there are no blank lines in the signature. - -----END PGP SIGNATURE----- - EOF -' - -test_atom refs/tags/fake-sig-no-blanks contents:subject 'this is the subject' -test_atom refs/tags/fake-sig-no-blanks contents:body '' -test_atom refs/tags/fake-sig-no-blanks contents:signature "$sig" - -test_expect_success 'set up tag with CRLF signature' ' - append_cr <<-\EOF | - this is the subject - -----BEGIN PGP SIGNATURE----- - - not a real signature, but we just care about - the subject/body parsing. It is important here - that there is a blank line separating this - from the signature header. - -----END PGP SIGNATURE----- - EOF - git tag -F - --cleanup=verbatim fake-sig-crlf -' - -test_atom refs/tags/fake-sig-crlf contents:subject 'this is the subject' -test_atom refs/tags/fake-sig-crlf contents:body '' - -# CRLF is retained in the signature, so we have to pass our expected value -# through append_cr. But test_atom requires a shell string, which means command -# substitution, and the shell will strip trailing newlines from the output of -# the substitution. Hack around it by adding and then removing a dummy line. -sig_crlf="$(printf "%s" "$sig" | append_cr; echo dummy)" -sig_crlf=${sig_crlf%dummy} -test_atom refs/tags/fake-sig-crlf contents:signature "$sig_crlf" - -test_expect_success 'set up tag with signature and trailers' ' - git tag -F - fake-sig-trailer <<-\EOF - this is the subject - - this is the body - - My-Trailer: foo - -----BEGIN PGP SIGNATURE----- - - not a real signature, but we just care about the - subject/body/trailer parsing. - -----END PGP SIGNATURE----- - EOF -' - -# use "separator=" here to suppress the terminating newline -test_atom refs/tags/fake-sig-trailer trailers:separator= 'My-Trailer: foo' - -test_expect_success 'git for-each-ref --stdin: empty' ' - >in && - git for-each-ref --format="%(refname)" --stdin <in >actual && - git for-each-ref --format="%(refname)" >expect && - test_cmp expect actual -' - -test_expect_success 'git for-each-ref --stdin: fails if extra args' ' - >in && - test_must_fail git for-each-ref --format="%(refname)" \ - --stdin refs/heads/extra <in 2>err && - grep "unknown arguments supplied with --stdin" err -' - -test_expect_success 'git for-each-ref --stdin: matches' ' - cat >in <<-EOF && - refs/tags/multi* - refs/heads/amb* - EOF - - cat >expect <<-EOF && - refs/heads/ambiguous - refs/tags/multi-ref1-100000-user1 - refs/tags/multi-ref1-100000-user2 - refs/tags/multi-ref1-200000-user1 - refs/tags/multi-ref1-200000-user2 - refs/tags/multi-ref2-100000-user1 - refs/tags/multi-ref2-100000-user2 - refs/tags/multi-ref2-200000-user1 - refs/tags/multi-ref2-200000-user2 - refs/tags/multiline - EOF - - git for-each-ref --format="%(refname)" --stdin <in >actual && - test_cmp expect actual -' - -test_expect_success 'git for-each-ref with non-existing refs' ' - cat >in <<-EOF && - refs/heads/this-ref-does-not-exist - refs/tags/bogus - EOF - - git for-each-ref --format="%(refname)" --stdin <in >actual && - test_must_be_empty actual && - - xargs git for-each-ref --format="%(refname)" <in >actual && - test_must_be_empty actual -' - -test_expect_success 'git for-each-ref with nested tags' ' - git tag -am "Normal tag" nested/base HEAD && - git tag -am "Nested tag" nested/nest1 refs/tags/nested/base && - git tag -am "Double nested tag" nested/nest2 refs/tags/nested/nest1 && - - head_oid="$(git rev-parse HEAD)" && - base_tag_oid="$(git rev-parse refs/tags/nested/base)" && - nest1_tag_oid="$(git rev-parse refs/tags/nested/nest1)" && - nest2_tag_oid="$(git rev-parse refs/tags/nested/nest2)" && - - cat >expect <<-EOF && - refs/tags/nested/base $base_tag_oid tag $head_oid commit - refs/tags/nested/nest1 $nest1_tag_oid tag $head_oid commit - refs/tags/nested/nest2 $nest2_tag_oid tag $head_oid commit - EOF - - git for-each-ref \ - --format="%(refname) %(objectname) %(objecttype) %(*objectname) %(*objecttype)" \ - refs/tags/nested/ >actual && - test_cmp expect actual -' - -test_expect_success 'is-base atom with non-commits' ' - git for-each-ref --format="%(is-base:HEAD) %(refname)" >out 2>err && - grep "(HEAD) refs/heads/main" out && - - test_line_count = 2 err && - grep "error: object .* is a commit, not a blob" err && - grep "error: bad tag pointer to" err -' - -GRADE_FORMAT="%(signature:grade)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)" -TRUSTLEVEL_FORMAT="%(signature:trustlevel)%0a%(signature:key)%0a%(signature:signer)%0a%(signature:fingerprint)%0a%(signature:primarykeyfingerprint)" - -test_expect_success GPG 'setup for signature atom using gpg' ' - git checkout -b signed && - - test_when_finished "test_unconfig commit.gpgSign" && - - echo "1" >file && - git add file && - test_tick && - git commit -S -m "file: 1" && - git tag first-signed && - - echo "2" >file && - test_tick && - git commit -a -m "file: 2" && - git tag second-unsigned && - - git config commit.gpgSign 1 && - echo "3" >file && - test_tick && - git commit -a --no-gpg-sign -m "file: 3" && - git tag third-unsigned && - - test_tick && - git rebase -f HEAD^^ && git tag second-signed HEAD^ && - git tag third-signed && - - echo "4" >file && - test_tick && - git commit -a -SB7227189 -m "file: 4" && - git tag fourth-signed && - - echo "5" >file && - test_tick && - git commit -a --no-gpg-sign -m "file: 5" && - git tag fifth-unsigned && - - echo "6" >file && - test_tick && - git commit -a --no-gpg-sign -m "file: 6" && - - test_tick && - git rebase -f HEAD^^ && - git tag fifth-signed HEAD^ && - git tag sixth-signed && - - echo "7" >file && - test_tick && - git commit -a --no-gpg-sign -m "file: 7" && - git tag seventh-unsigned -' - -test_expect_success GPGSSH 'setup for signature atom using ssh' ' - test_when_finished "test_unconfig gpg.format user.signingkey" && - - test_config gpg.format ssh && - test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" && - echo "8" >file && - test_tick && - git add file && - git commit -S -m "file: 8" && - git tag eighth-signed-ssh -' - -test_expect_success GPG2 'bare signature atom' ' - git verify-commit first-signed 2>expect && - echo >>expect && - git for-each-ref refs/tags/first-signed \ - --format="%(signature)" >actual && - test_cmp expect actual -' - -test_expect_success GPG 'show good signature with custom format' ' - git verify-commit first-signed && - cat >expect <<-\EOF && - G - 13B6F51ECDDE430D - C O Mitter <committer@example.com> - 73D758744BE721698EC54E8713B6F51ECDDE430D - 73D758744BE721698EC54E8713B6F51ECDDE430D - EOF - git for-each-ref refs/tags/first-signed \ - --format="$GRADE_FORMAT" >actual && - test_cmp expect actual -' -test_expect_success GPGSSH 'show good signature with custom format with ssh' ' - test_config gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && - FINGERPRINT=$(ssh-keygen -lf "${GPGSSH_KEY_PRIMARY}" | awk "{print \$2;}") && - cat >expect.tmpl <<-\EOF && - G - FINGERPRINT - principal with number 1 - FINGERPRINT - - EOF - sed "s|FINGERPRINT|$FINGERPRINT|g" expect.tmpl >expect && - git for-each-ref refs/tags/eighth-signed-ssh \ - --format="$GRADE_FORMAT" >actual && - test_cmp expect actual -' - -test_expect_success GPG 'signature atom with grade option and bad signature' ' - git cat-file commit third-signed >raw && - sed -e "s/^file: 3/file: 3 forged/" raw >forged1 && - FORGED1=$(git hash-object -w -t commit forged1) && - git update-ref refs/tags/third-signed "$FORGED1" && - test_must_fail git verify-commit "$FORGED1" && - - cat >expect <<-\EOF && - B - 13B6F51ECDDE430D - C O Mitter <committer@example.com> - - - EOF - git for-each-ref refs/tags/third-signed \ - --format="$GRADE_FORMAT" >actual && - test_cmp expect actual -' - -test_expect_success GPG 'show untrusted signature with custom format' ' - cat >expect <<-\EOF && - U - 65A0EEA02E30CAD7 - Eris Discordia <discord@example.net> - F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7 - D4BE22311AD3131E5EDA29A461092E85B7227189 - EOF - git for-each-ref refs/tags/fourth-signed \ - --format="$GRADE_FORMAT" >actual && - test_cmp expect actual -' - -test_expect_success GPG 'show untrusted signature with undefined trust level' ' - cat >expect <<-\EOF && - undefined - 65A0EEA02E30CAD7 - Eris Discordia <discord@example.net> - F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7 - D4BE22311AD3131E5EDA29A461092E85B7227189 - EOF - git for-each-ref refs/tags/fourth-signed \ - --format="$TRUSTLEVEL_FORMAT" >actual && - test_cmp expect actual -' - -test_expect_success GPG 'show untrusted signature with ultimate trust level' ' - cat >expect <<-\EOF && - ultimate - 13B6F51ECDDE430D - C O Mitter <committer@example.com> - 73D758744BE721698EC54E8713B6F51ECDDE430D - 73D758744BE721698EC54E8713B6F51ECDDE430D - EOF - git for-each-ref refs/tags/sixth-signed \ - --format="$TRUSTLEVEL_FORMAT" >actual && - test_cmp expect actual -' - -test_expect_success GPG 'show unknown signature with custom format' ' - cat >expect <<-\EOF && - E - 13B6F51ECDDE430D - - - - EOF - GNUPGHOME="$GNUPGHOME_NOT_USED" git for-each-ref \ - refs/tags/sixth-signed --format="$GRADE_FORMAT" >actual && - test_cmp expect actual -' - -test_expect_success GPG 'show lack of signature with custom format' ' - cat >expect <<-\EOF && - N - - - - - EOF - git for-each-ref refs/tags/seventh-unsigned \ - --format="$GRADE_FORMAT" >actual && - test_cmp expect actual -' +. "$TEST_DIRECTORY"/for-each-ref-tests.sh test_done diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh index bb02b86c16..9b80ea1e3b 100755 --- a/t/t6302-for-each-ref-filter.sh +++ b/t/t6302-for-each-ref-filter.sh @@ -541,4 +541,217 @@ test_expect_success 'validate worktree atom' ' test_cmp expect actual ' +test_expect_success 'start after with empty value' ' + cat >expect <<-\EOF && + refs/heads/main + refs/heads/main_worktree + refs/heads/side + refs/odd/spot + refs/tags/annotated-tag + refs/tags/doubly-annotated-tag + refs/tags/doubly-signed-tag + refs/tags/foo1.10 + refs/tags/foo1.3 + refs/tags/foo1.6 + refs/tags/four + refs/tags/one + refs/tags/signed-tag + refs/tags/three + refs/tags/two + EOF + git for-each-ref --format="%(refname)" --start-after="" >actual && + test_cmp expect actual +' + +test_expect_success 'start after a specific reference' ' + cat >expect <<-\EOF && + refs/tags/annotated-tag + refs/tags/doubly-annotated-tag + refs/tags/doubly-signed-tag + refs/tags/foo1.10 + refs/tags/foo1.3 + refs/tags/foo1.6 + refs/tags/four + refs/tags/one + refs/tags/signed-tag + refs/tags/three + refs/tags/two + EOF + git for-each-ref --format="%(refname)" --start-after=refs/odd/spot >actual && + test_cmp expect actual +' + +test_expect_success 'start after a specific reference with partial match' ' + cat >expect <<-\EOF && + refs/odd/spot + refs/tags/annotated-tag + refs/tags/doubly-annotated-tag + refs/tags/doubly-signed-tag + refs/tags/foo1.10 + refs/tags/foo1.3 + refs/tags/foo1.6 + refs/tags/four + refs/tags/one + refs/tags/signed-tag + refs/tags/three + refs/tags/two + EOF + git for-each-ref --format="%(refname)" --start-after=refs/odd/sp >actual && + test_cmp expect actual +' + +test_expect_success 'start after, just behind a specific reference' ' + cat >expect <<-\EOF && + refs/odd/spot + refs/tags/annotated-tag + refs/tags/doubly-annotated-tag + refs/tags/doubly-signed-tag + refs/tags/foo1.10 + refs/tags/foo1.3 + refs/tags/foo1.6 + refs/tags/four + refs/tags/one + refs/tags/signed-tag + refs/tags/three + refs/tags/two + EOF + git for-each-ref --format="%(refname)" --start-after=refs/odd/parrot >actual && + test_cmp expect actual +' + +test_expect_success 'start after with specific directory match' ' + cat >expect <<-\EOF && + refs/odd/spot + refs/tags/annotated-tag + refs/tags/doubly-annotated-tag + refs/tags/doubly-signed-tag + refs/tags/foo1.10 + refs/tags/foo1.3 + refs/tags/foo1.6 + refs/tags/four + refs/tags/one + refs/tags/signed-tag + refs/tags/three + refs/tags/two + EOF + git for-each-ref --format="%(refname)" --start-after=refs/odd >actual && + test_cmp expect actual +' + +test_expect_success 'start after with specific directory and trailing slash' ' + cat >expect <<-\EOF && + refs/odd/spot + refs/tags/annotated-tag + refs/tags/doubly-annotated-tag + refs/tags/doubly-signed-tag + refs/tags/foo1.10 + refs/tags/foo1.3 + refs/tags/foo1.6 + refs/tags/four + refs/tags/one + refs/tags/signed-tag + refs/tags/three + refs/tags/two + EOF + git for-each-ref --format="%(refname)" --start-after=refs/odd/ >actual && + test_cmp expect actual +' + +test_expect_success 'start after, just behind a specific directory' ' + cat >expect <<-\EOF && + refs/odd/spot + refs/tags/annotated-tag + refs/tags/doubly-annotated-tag + refs/tags/doubly-signed-tag + refs/tags/foo1.10 + refs/tags/foo1.3 + refs/tags/foo1.6 + refs/tags/four + refs/tags/one + refs/tags/signed-tag + refs/tags/three + refs/tags/two + EOF + git for-each-ref --format="%(refname)" --start-after=refs/lost >actual && + test_cmp expect actual +' + +test_expect_success 'start after, overflow specific reference length' ' + cat >expect <<-\EOF && + refs/tags/annotated-tag + refs/tags/doubly-annotated-tag + refs/tags/doubly-signed-tag + refs/tags/foo1.10 + refs/tags/foo1.3 + refs/tags/foo1.6 + refs/tags/four + refs/tags/one + refs/tags/signed-tag + refs/tags/three + refs/tags/two + EOF + git for-each-ref --format="%(refname)" --start-after=refs/odd/spotnew >actual && + test_cmp expect actual +' + +test_expect_success 'start after, overflow specific reference path' ' + cat >expect <<-\EOF && + refs/tags/annotated-tag + refs/tags/doubly-annotated-tag + refs/tags/doubly-signed-tag + refs/tags/foo1.10 + refs/tags/foo1.3 + refs/tags/foo1.6 + refs/tags/four + refs/tags/one + refs/tags/signed-tag + refs/tags/three + refs/tags/two + EOF + git for-each-ref --format="%(refname)" --start-after=refs/odd/spot/new >actual && + test_cmp expect actual +' + +test_expect_success 'start after, with exclude pattern' ' + cat >expect <<-\EOF && + refs/tags/annotated-tag + refs/tags/doubly-annotated-tag + refs/tags/doubly-signed-tag + refs/tags/foo1.10 + refs/tags/foo1.3 + refs/tags/foo1.6 + refs/tags/four + refs/tags/one + refs/tags/signed-tag + refs/tags/three + refs/tags/two + EOF + git for-each-ref --format="%(refname)" --start-after=refs/odd/spot \ + --exclude=refs/tags/foo >actual && + test_cmp expect actual +' + +test_expect_success 'start after, last reference' ' + cat >expect <<-\EOF && + EOF + git for-each-ref --format="%(refname)" --start-after=refs/tags/two >actual && + test_cmp expect actual +' + +test_expect_success 'start after used with a pattern' ' + cat >expect <<-\EOF && + fatal: cannot use --start-after with patterns + EOF + test_must_fail git for-each-ref --format="%(refname)" --start-after=refs/odd/spot refs/tags 2>actual && + test_cmp expect actual +' + +test_expect_success 'start after used with custom sort order' ' + cat >expect <<-\EOF && + fatal: cannot use --start-after with custom sort options + EOF + test_must_fail git for-each-ref --format="%(refname)" --start-after=refs/odd/spot --sort=author 2>actual && + test_cmp expect actual +' + test_done diff --git a/t/t6422-merge-rename-corner-cases.sh b/t/t6422-merge-rename-corner-cases.sh index 9cbe7ca782..f14c0fb30e 100755 --- a/t/t6422-merge-rename-corner-cases.sh +++ b/t/t6422-merge-rename-corner-cases.sh @@ -1146,10 +1146,7 @@ test_conflicts_with_adds_and_renames() { cd simple_${sideL}_${sideR} && # Create some related files now - for i in $(test_seq 1 10) - do - echo Random base content line $i - done >file_v1 && + test_seq -f "Random base content line %d" 1 10 >file_v1 && cp file_v1 file_v2 && echo modification >>file_v2 && @@ -1293,10 +1290,7 @@ test_setup_nested_conflicts_from_rename_rename () { cd nested_conflicts_from_rename_rename && # Create some related files now - for i in $(test_seq 1 10) - do - echo Random base content line $i - done >file_v1 && + test_seq -f "Random base content line %d" 1 10 >file_v1 && cp file_v1 file_v2 && cp file_v1 file_v3 && diff --git a/t/t6423-merge-rename-directories.sh b/t/t6423-merge-rename-directories.sh index f48ed6d035..533ac85dc8 100755 --- a/t/t6423-merge-rename-directories.sh +++ b/t/t6423-merge-rename-directories.sh @@ -4731,7 +4731,7 @@ test_setup_12i () { mkdir -p source/subdir && echo foo >source/subdir/foo && - echo bar >source/bar && + printf "%d\n" 1 2 3 4 5 6 7 >source/bar && echo baz >source/baz && git add source && git commit -m orig && @@ -4747,6 +4747,7 @@ test_setup_12i () { git switch B && git mv source/bar source/subdir/bar && echo more baz >>source/baz && + git add source/baz && git commit -m B ) } @@ -4758,6 +4759,88 @@ test_expect_success '12i: Directory rename causes rename-to-self' ' git checkout A^0 && + # NOTE: A potentially better resolution would be for + # source/bar -> source/subdir/bar + # to use the directory rename to become + # source/bar -> source/bar + # (a rename to self), and thus we end up with bar with + # a path conflict (given merge.directoryRenames=conflict). + # However, since the relevant renames optimization + # prevents us from noticing + # source/bar -> source/subdir/bar + # as a rename and looking at it just as + # delete source/bar + # add source/subdir/bar + # the directory rename of source/subdir/bar -> source/bar does + # not look like a rename-to-self situation but a + # rename-on-top-of-other-file situation. We do not want + # stage 1 entries from an unrelated file, so we expect an + # error about there being a file in the way. + + test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 >out && + + grep "CONFLICT (implicit dir rename).*source/bar in the way" out && + test_path_is_missing source/bar && + test_path_is_file source/subdir/bar && + test_path_is_file source/baz && + + git ls-files >actual && + uniq <actual >tracked && + test_line_count = 3 tracked && + + git status --porcelain -uno >actual && + cat >expect <<-\EOF && + M source/baz + R source/bar -> source/subdir/bar + EOF + test_cmp expect actual + ) +' + +# Testcase 12i2, Identical to 12i except that source/subdir/bar modified on unrenamed side +# Commit O: source/{subdir/foo, bar, baz_1} +# Commit A: source/{foo, bar_2, baz_1} +# Commit B: source/{subdir/{foo, bar}, baz_2} +# Expected: source/{foo, bar, baz_2}, with conflicts on +# source/bar vs. source/subdir/bar + +test_setup_12i2 () { + git init 12i2 && + ( + cd 12i2 && + + mkdir -p source/subdir && + echo foo >source/subdir/foo && + printf "%d\n" 1 2 3 4 5 6 7 >source/bar && + echo baz >source/baz && + git add source && + git commit -m orig && + + git branch O && + git branch A && + git branch B && + + git switch A && + git mv source/subdir/foo source/foo && + echo 8 >> source/bar && + git add source/bar && + git commit -m A && + + git switch B && + git mv source/bar source/subdir/bar && + echo more baz >>source/baz && + git add source/baz && + git commit -m B + ) +} + +test_expect_success '12i2: Directory rename causes rename-to-self' ' + test_setup_12i2 && + ( + cd 12i2 && + + git checkout A^0 && + test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 && test_path_is_missing source/subdir && @@ -4771,7 +4854,7 @@ test_expect_success '12i: Directory rename causes rename-to-self' ' git status --porcelain -uno >actual && cat >expect <<-\EOF && UU source/bar - M source/baz + M source/baz EOF test_cmp expect actual ) @@ -4806,6 +4889,7 @@ test_setup_12j () { git switch B && git mv bar subdir/bar && echo more baz >>baz && + git add baz && git commit -m B ) } @@ -4817,10 +4901,29 @@ test_expect_success '12j: Directory rename to root causes rename-to-self' ' git checkout A^0 && - test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 && - - test_path_is_missing subdir && - test_path_is_file bar && + # NOTE: A potentially better resolution would be for + # bar -> subdir/bar + # to use the directory rename to become + # bar -> bar + # (a rename to self), and thus we end up with bar with + # a path conflict (given merge.directoryRenames=conflict). + # However, since the relevant renames optimization + # prevents us from noticing + # bar -> subdir/bar + # as a rename and looking at it just as + # delete bar + # add subdir/bar + # the directory rename of subdir/bar -> bar does not look + # like a rename-to-self situation but a + # rename-on-top-of-other-file situation. We do not want + # stage 1 entries from an unrelated file, so we expect an + # error about there being a file in the way. + + test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 >out && + grep "CONFLICT (implicit dir rename).*bar in the way" out && + + test_path_is_missing bar && + test_path_is_file subdir/bar && test_path_is_file baz && git ls-files >actual && @@ -4829,8 +4932,8 @@ test_expect_success '12j: Directory rename to root causes rename-to-self' ' git status --porcelain -uno >actual && cat >expect <<-\EOF && - UU bar - M baz + M baz + R bar -> subdir/bar EOF test_cmp expect actual ) @@ -4865,6 +4968,7 @@ test_setup_12k () { git switch B && git mv dirA/bar dirB/bar && echo more baz >>dirA/baz && + git add dirA/baz && git commit -m B ) } @@ -4876,10 +4980,29 @@ test_expect_success '12k: Directory rename with sibling causes rename-to-self' ' git checkout A^0 && - test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 && - - test_path_is_missing dirB && - test_path_is_file dirA/bar && + # NOTE: A potentially better resolution would be for + # dirA/bar -> dirB/bar + # to use the directory rename (dirB/ -> dirA/) to become + # dirA/bar -> dirA/bar + # (a rename to self), and thus we end up with bar with + # a path conflict (given merge.directoryRenames=conflict). + # However, since the relevant renames optimization + # prevents us from noticing + # dirA/bar -> dirB/bar + # as a rename and looking at it just as + # delete dirA/bar + # add dirB/bar + # the directory rename of dirA/bar -> dirB/bar does + # not look like a rename-to-self situation but a + # rename-on-top-of-other-file situation. We do not want + # stage 1 entries from an unrelated file, so we expect an + # error about there being a file in the way. + + test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 >out && + grep "CONFLICT (implicit dir rename).*dirA/bar in the way" out && + + test_path_is_missing dirA/bar && + test_path_is_file dirB/bar && test_path_is_file dirA/baz && git ls-files >actual && @@ -4888,8 +5011,8 @@ test_expect_success '12k: Directory rename with sibling causes rename-to-self' ' git status --porcelain -uno >actual && cat >expect <<-\EOF && - UU dirA/bar - M dirA/baz + M dirA/baz + R dirA/bar -> dirB/bar EOF test_cmp expect actual ) @@ -5056,6 +5179,25 @@ test_expect_success '12m: Change parent of renamed-dir to symlink on other side' ) ' +# Testcase 12n, Directory rename transitively makes rename back to self +# +# (Since this is a cherry-pick instead of merge, the labels are a bit weird. +# O, the original commit, is A~1 rather than what branch O points to.) +# +# Commit O: tools/hello +# world +# Commit A: tools/hello +# tools/world +# Commit B: hello +# In words: +# A: world -> tools/world +# B: tools/ -> /, i.e. rename all of tools to toplevel directory +# delete world +# +# Expected: +# CONFLICT (file location): tools/world vs. world +# + test_setup_12n () { git init 12n && ( @@ -5092,10 +5234,357 @@ test_expect_success '12n: Directory rename transitively makes rename back to sel git checkout -q B^0 && test_must_fail git cherry-pick A^0 >out && - grep "CONFLICT (file location).*should perhaps be moved" out + test_grep "CONFLICT (file location).*should perhaps be moved" out && + + # Should have 1 entry for hello, and 2 for world + test_stdout_line_count = 3 git ls-files -s && + test_stdout_line_count = 1 git ls-files -s hello && + test_stdout_line_count = 2 git ls-files -s world + ) +' + +# Testcase 12n2, Directory rename transitively makes rename back to self +# +# Commit O: tools/hello +# world +# Commit A: tools/hello +# tools/world +# Commit B: hello +# In words: +# A: world -> tools/world +# B: tools/ -> /, i.e. rename all of tools to toplevel directory +# delete world +# +# Expected: +# CONFLICT (file location): tools/world vs. world +# + +test_setup_12n2 () { + git init 12n2 && + ( + cd 12n2 && + + mkdir tools && + echo hello >tools/hello && + git add tools/hello && + echo world >world && + git add world && + git commit -m "O" && + + git branch O && + git branch A && + git branch B && + + git switch A && + git mv world tools/world && + git commit -m "Move world into tools/" && + + git switch B && + git mv tools/hello hello && + git rm world && + git commit -m "Move hello from tools/ to toplevel" + ) +} + +test_expect_success '12n2: Directory rename transitively makes rename back to self' ' + test_setup_12n2 && + ( + cd 12n2 && + + git checkout -q B^0 && + + test_might_fail git -c merge.directoryRenames=true merge A^0 >out && + + # Should have 1 entry for hello, and either 0 or 2 for world + # + # NOTE: Since merge.directoryRenames=true, there is no path + # conflict for world vs. tools/world; it should end up at + # world. The fact that world was unmodified on side A, means + # there was no content conflict; we should just take the + # content from side B -- i.e. delete the file. So merging + # could just delete world. + # + # However, rename-to-self-via-directory-rename is a bit more + # challenging. Relax this test to allow world to be treated + # as a modify/delete conflict as well, meaning it will have + # two higher order stages, that just so happen to match. + # + test_stdout_line_count = 1 git ls-files -s hello && + test_stdout_line_count = 2 git ls-files -s world && + test_grep "CONFLICT (modify/delete).*world deleted in HEAD" out ) ' +# Testcase 12o, Directory rename hits other rename source; file still in way on same side +# Commit O: A/file1_1 +# A/stuff +# B/file1_2 +# B/stuff +# C/other +# Commit A: A/file1_1 +# A/stuff +# B/stuff +# C/file1_2 +# C/other +# Commit B: D/file2_1 +# A/stuff +# B/file1_2 +# B/stuff +# A/other +# In words: +# A: rename B/file1_2 -> C/file1_2 +# B: rename C/ -> A/ +# rename A/file1_1 -> D/file2_1 +# Rationale: +# A/stuff is unmodified, it shows up in final output +# B/stuff is unmodified, it shows up in final output +# C/other touched on one side (rename to A), so A/other shows up in output +# A/file1 is renamed to D/file2 +# B/file1 -> C/file1 and even though C/ -> A/, A/file1 is +# "in the way" so we don't do the directory rename +# Expected: A/stuff +# B/stuff +# A/other +# D/file2 +# C/file1 +# + CONFLICT (implicit dir rename): A/file1 in way of C/file1 +# + +test_setup_12o () { + git init 12o && + ( + cd 12o && + + mkdir -p A B C && + echo 1 >A/file1 && + echo 2 >B/file1 && + echo other >C/other && + echo Astuff >A/stuff && + echo Bstuff >B/stuff && + git add . && + git commit -m "O" && + + git branch O && + git branch A && + git branch B && + + git switch A && + git mv B/file1 C/ && + git add . && + git commit -m "A" && + + git switch B && + mkdir -p D && + git mv A/file1 D/file2 && + git mv C/other A/other && + git add . && + git commit -m "B" + ) +} + +test_expect_success '12o: Directory rename hits other rename source; file still in way on same side' ' + test_setup_12o && + ( + cd 12o && + + git checkout -q A^0 && + + test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 >out && + + test_stdout_line_count = 5 git ls-files -s && + test_stdout_line_count = 1 git ls-files -s A/other && + test_stdout_line_count = 1 git ls-files -s A/stuff && + test_stdout_line_count = 1 git ls-files -s B/stuff && + test_stdout_line_count = 1 git ls-files -s D/file2 && + + grep "CONFLICT (implicit dir rename).*Existing file/dir at A/file1 in the way" out && + test_stdout_line_count = 1 git ls-files -s C/file1 + ) +' + +# Testcase 12p, Directory rename hits other rename source; file still in way on other side +# Commit O: A/file1_1 +# A/stuff +# B/file1_2 +# B/stuff +# C/other +# Commit A: D/file2_1 +# A/stuff +# B/stuff +# C/file1_2 +# C/other +# Commit B: A/file1_1 +# A/stuff +# B/file1_2 +# B/stuff +# A/other +# Short version: +# A: rename A/file1_1 -> D/file2_1 +# rename B/file1_2 -> C/file1_2 +# B: Rename C/ -> A/ +# Rationale: +# A/stuff is unmodified, it shows up in final output +# B/stuff is unmodified, it shows up in final output +# C/other touched on one side (rename to A), so A/other shows up in output +# A/file1 is renamed to D/file2 +# B/file1 -> C/file1 and even though C/ -> A/, A/file1 is +# "in the way" so we don't do the directory rename +# Expected: A/stuff +# B/stuff +# A/other +# D/file2 +# C/file1 +# + CONFLICT (implicit dir rename): A/file1 in way of C/file1 +# + +test_setup_12p () { + git init 12p && + ( + cd 12p && + + mkdir -p A B C && + echo 1 >A/file1 && + echo 2 >B/file1 && + echo other >C/other && + echo Astuff >A/stuff && + echo Bstuff >B/stuff && + git add . && + git commit -m "O" && + + git branch O && + git branch A && + git branch B && + + git switch A && + mkdir -p D && + git mv A/file1 D/file2 && + git mv B/file1 C/ && + git add . && + git commit -m "A" && + + git switch B && + git mv C/other A/other && + git add . && + git commit -m "B" + ) +} + +test_expect_success '12p: Directory rename hits other rename source; file still in way on other side' ' + test_setup_12p && + ( + cd 12p && + + git checkout -q A^0 && + + test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 >out && + + test_stdout_line_count = 5 git ls-files -s && + test_stdout_line_count = 1 git ls-files -s A/other && + test_stdout_line_count = 1 git ls-files -s A/stuff && + test_stdout_line_count = 1 git ls-files -s B/stuff && + test_stdout_line_count = 1 git ls-files -s D/file2 && + + grep "CONFLICT (implicit dir rename).*Existing file/dir at A/file1 in the way" out && + test_stdout_line_count = 1 git ls-files -s C/file1 + ) +' + +# Testcase 12q, Directory rename hits other rename source; file removed though +# Commit O: A/file1_1 +# A/stuff +# B/file1_2 +# B/stuff +# C/other +# Commit A: A/stuff +# B/stuff +# C/file1_2 +# C/other +# Commit B: D/file2_1 +# A/stuff +# B/file1_2 +# B/stuff +# A/other +# In words: +# A: delete A/file1_1, rename B/file1_2 -> C/file1_2 +# B: Rename C/ -> A/, rename A/file1_1 -> D/file2_1 +# Rationale: +# A/stuff is unmodified, it shows up in final output +# B/stuff is unmodified, it shows up in final output +# C/other touched on one side (rename to A), so A/other shows up in output +# A/file1 is rename/delete to D/file2, so two stages for D/file2 +# B/file1 -> C/file1 and even though C/ -> A/, A/file1 as a source was +# "in the way" (ish) so we don't do the directory rename +# Expected: A/stuff +# B/stuff +# A/other +# D/file2 (two stages) +# C/file1 +# + CONFLICT (implicit dir rename): A/file1 in way of C/file1 +# + CONFLICT (rename/delete): D/file2 +# + +test_setup_12q () { + git init 12q && + ( + cd 12q && + + mkdir -p A B C && + echo 1 >A/file1 && + echo 2 >B/file1 && + echo other >C/other && + echo Astuff >A/stuff && + echo Bstuff >B/stuff && + git add . && + git commit -m "O" && + + git branch O && + git branch A && + git branch B && + + git switch A && + git rm A/file1 && + git mv B/file1 C/ && + git add . && + git commit -m "A" && + + git switch B && + mkdir -p D && + git mv A/file1 D/file2 && + git mv C/other A/other && + git add . && + git commit -m "B" + ) +} + +test_expect_success '12q: Directory rename hits other rename source; file removed though' ' + test_setup_12q && + ( + cd 12q && + + git checkout -q A^0 && + + test_must_fail git -c merge.directoryRenames=conflict merge -s recursive B^0 >out && + + grep "CONFLICT (rename/delete).*A/file1.*D/file2" out && + grep "CONFLICT (implicit dir rename).*Existing file/dir at A/file1 in the way" out && + + test_stdout_line_count = 6 git ls-files -s && + test_stdout_line_count = 1 git ls-files -s A/other && + test_stdout_line_count = 1 git ls-files -s A/stuff && + test_stdout_line_count = 1 git ls-files -s B/stuff && + test_stdout_line_count = 2 git ls-files -s D/file2 && + + # This is a slightly suboptimal resolution; allowing the + # rename of C/ -> A/ to affect C/file1 and further rename + # it to A/file1 would probably be preferable, but since + # A/file1 existed as the source of another rename, allowing + # the dir rename of C/file1 -> A/file1 would mean modifying + # the code so that renames do not adjust both their source + # and target paths in all cases. + ! grep "CONFLICT (file location)" out && + test_stdout_line_count = 1 git ls-files -s C/file1 + ) +' ########################################################################### # SECTION 13: Checking informational and conflict messages diff --git a/t/t6601-path-walk.sh b/t/t6601-path-walk.sh index 8d187f7279..56bd1e3c5b 100755 --- a/t/t6601-path-walk.sh +++ b/t/t6601-path-walk.sh @@ -376,6 +376,26 @@ test_expect_success 'topic, not base, boundary with pruning' ' test_cmp_sorted expect out ' +test_expect_success 'topic, not base, --edge-aggressive with pruning' ' + test-tool path-walk --prune --edge-aggressive -- topic --not base >out && + + cat >expect <<-EOF && + 0:commit::$(git rev-parse topic) + 1:tree::$(git rev-parse topic^{tree}) + 1:tree::$(git rev-parse base^{tree}):UNINTERESTING + 2:tree:right/:$(git rev-parse topic:right) + 2:tree:right/:$(git rev-parse base:right):UNINTERESTING + 3:blob:right/c:$(git rev-parse base:right/c):UNINTERESTING + 3:blob:right/c:$(git rev-parse topic:right/c) + blobs:2 + commits:1 + tags:0 + trees:4 + EOF + + test_cmp_sorted expect out +' + test_expect_success 'trees are reported exactly once' ' test_when_finished "rm -rf unique-trees" && test_create_repo unique-trees && diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh index 5fcf281dfb..c490e5707a 100755 --- a/t/t7005-editor.sh +++ b/t/t7005-editor.sh @@ -7,125 +7,96 @@ test_description='GIT_EDITOR, core.editor, and stuff' unset EDITOR VISUAL GIT_EDITOR test_expect_success 'determine default editor' ' - vi=$(TERM=vt100 git var GIT_EDITOR) && test -n "$vi" - ' -if ! expr "$vi" : '[a-z]*$' >/dev/null -then - vi= -fi - -for i in GIT_EDITOR core_editor EDITOR VISUAL $vi -do - cat >e-$i.sh <<-EOF - #!$SHELL_PATH - echo "Edited by $i" >"\$1" - EOF - chmod +x e-$i.sh -done +test_expect_success setup ' + if ! expr "$vi" : "[a-z]*$" >/dev/null + then + vi= + fi && -if ! test -z "$vi" -then - mv e-$vi.sh $vi -fi + for i in GIT_EDITOR core_editor EDITOR VISUAL $vi + do + write_script e-$i.sh <<-EOF || return 1 + echo "Edited by $i" >"\$1" + EOF + done && -test_expect_success setup ' + if ! test -z "$vi" + then + mv e-$vi.sh $vi + fi && msg="Hand-edited" && test_commit "$msg" && - echo "$msg" >expect && - git show -s --format=%s > actual && - test_cmp expect actual - + test_commit_message HEAD -m "$msg" ' -TERM=dumb -export TERM test_expect_success 'dumb should error out when falling back on vi' ' - - if git commit --amend - then - echo "Oops?" - false - else - : happy - fi + test_must_fail env TERM=dumb git commit --amend ' test_expect_success 'dumb should prefer EDITOR to VISUAL' ' - - EDITOR=./e-EDITOR.sh && - VISUAL=./e-VISUAL.sh && - export EDITOR VISUAL && - git commit --amend && - test "$(git show -s --format=%s)" = "Edited by EDITOR" - + TERM=dumb EDITOR=./e-EDITOR.sh VISUAL=./e-VISUAL.sh \ + git commit --amend && + test_commit_message HEAD -m "Edited by EDITOR" ' -TERM=vt100 -export TERM for i in $vi EDITOR VISUAL core_editor GIT_EDITOR do - echo "Edited by $i" >expect - unset EDITOR VISUAL GIT_EDITOR - git config --unset-all core.editor - case "$i" in - core_editor) - git config core.editor ./e-core_editor.sh - ;; - [A-Z]*) - eval "$i=./e-$i.sh" - export $i - ;; - esac test_expect_success "Using $i" ' - git --exec-path=. commit --amend && - git show -s --pretty=oneline | - sed -e "s/^[0-9a-f]* //" >actual && - test_cmp expect actual + if test "$i" = core_editor + then + test_config core.editor ./e-core_editor.sh + fi && + ( + case "$i" in + [A-Z]*) + eval "$i=./e-$i.sh" && + export $i + ;; + esac && + PATH="$PWD:$PATH" TERM=vt100 git commit --amend + ) && + test_commit_message HEAD -m "Edited by $i" ' done -unset EDITOR VISUAL GIT_EDITOR -git config --unset-all core.editor -for i in $vi EDITOR VISUAL core_editor GIT_EDITOR -do - echo "Edited by $i" >expect - case "$i" in - core_editor) - git config core.editor ./e-core_editor.sh - ;; - [A-Z]*) - eval "$i=./e-$i.sh" - export $i - ;; - esac - test_expect_success "Using $i (override)" ' - git --exec-path=. commit --amend && - git show -s --pretty=oneline | - sed -e "s/^[0-9a-f]* //" >actual && - test_cmp expect actual - ' -done +test_expect_success 'Using editors with overrides' ' + ( + TERM=vt100 && + export TERM && + for i in $vi EDITOR VISUAL core_editor GIT_EDITOR + do + echo "Edited by $i" >expect && + case "$i" in + core_editor) + git config core.editor ./e-core_editor.sh + ;; + [A-Z]*) + eval "$i=./e-$i.sh" && + export $i + ;; + esac && + PATH="$PWD:$PATH" git commit --amend && + test_commit_message HEAD expect || exit 1 + done + ) +' test_expect_success 'editor with a space' ' echo "echo space >\"\$1\"" >"e space.sh" && chmod a+x "e space.sh" && GIT_EDITOR="./e\ space.sh" git commit --amend && - test space = "$(git show -s --pretty=format:%s)" - + test_commit_message HEAD -m space ' -unset GIT_EDITOR test_expect_success 'core.editor with a space' ' - - git config core.editor \"./e\ space.sh\" && + test_config core.editor \"./e\ space.sh\" && git commit --amend && - test space = "$(git show -s --pretty=format:%s)" - + test_commit_message HEAD -m space ' test_done diff --git a/t/t7007-show.sh b/t/t7007-show.sh index d6cc69e0f2..2d322b53d1 100755 --- a/t/t7007-show.sh +++ b/t/t7007-show.sh @@ -167,4 +167,28 @@ test_expect_success 'show --graph is forbidden' ' test_must_fail git show --graph HEAD ' +test_expect_success 'show unmerged index' ' + git reset --hard && + + git switch -C base && + echo "base" >conflicting && + git add conflicting && + git commit -m "base" && + + git branch hello && + git branch goodbye && + + git switch hello && + echo "hello" >conflicting && + git commit -am "hello" && + + git switch goodbye && + echo "goodbye" >conflicting && + git commit -am "goodbye" && + + git switch hello && + test_must_fail git merge goodbye && + git show --merge HEAD +' + test_done diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index d6a501d453..fd3e7e355e 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -1482,4 +1482,27 @@ test_expect_success '`submodule init` and `init.templateDir`' ' ) ' +test_expect_success 'submodule add fails when name is reused' ' + git init test-submodule && + ( + cd test-submodule && + git commit --allow-empty -m init && + + git init ../child-origin && + git -C ../child-origin commit --allow-empty -m init && + + git submodule add ../child-origin child && + git commit -m "Add submodule child" && + + git mv child child_old && + git commit -m "Move child to child_old" && + + # Now adding a *new* repo at the old name must fail + git init ../child2-origin && + git -C ../child2-origin commit --allow-empty -m init && + test_must_fail git submodule add ../child2-origin child 2>err && + test_grep "already used for" err + ) +' + test_done diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh index 9c3cc4cf40..66c3ec2da2 100755 --- a/t/t7401-submodule-summary.sh +++ b/t/t7401-submodule-summary.sh @@ -38,10 +38,11 @@ commit_file () { git commit "$@" -m "Commit $*" >/dev/null } -test_create_repo sm1 && -add_file . foo >/dev/null - -head1=$(add_file sm1 foo1 foo2) +test_expect_success 'setup submodule' ' + git init sm1 && + add_file . foo && + head1=$(add_file sm1 foo1 foo2) +' test_expect_success 'added submodule' " git add sm1 && @@ -214,9 +215,12 @@ test_expect_success 'typechanged submodule(submodule->blob)' " test_cmp expected actual " -rm -f sm1 && -test_create_repo sm1 && -head6=$(add_file sm1 foo6 foo7) +test_expect_success 'setup submodule' ' + rm -f sm1 && + git init sm1 && + head6=$(add_file sm1 foo6 foo7) +' + test_expect_success 'nonexistent commit' " git submodule summary >actual && cat >expected <<-EOF && diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index c562bad042..3adab12091 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -1095,12 +1095,15 @@ test_expect_success 'submodule update --quiet passes quietness to fetch with a s (cd super5 && # This test var can mess with the stderr output checked in this test. GIT_TEST_NAME_HASH_VERSION=1 \ + GIT_TEST_PACK_PATH_WALK=0 \ git submodule update --quiet --init --depth=1 submodule3 >out 2>err && test_must_be_empty out && test_must_be_empty err ) && git clone super4 super6 && (cd super6 && + # This test variable will create a "warning" message to stderr + GIT_TEST_PACK_PATH_WALK=0 \ git submodule update --init --depth=1 submodule3 >out 2>err && test_file_not_empty out && test_file_not_empty err @@ -1134,6 +1137,67 @@ test_expect_success 'setup clean recursive superproject' ' git clone --recurse-submodules top top-clean ' +test_expect_success 'submodule update with multiple remotes' ' + test_when_finished "rm -fr top-cloned" && + cp -r top-clean top-cloned && + + # Create a commit in each repo, starting with bottom + test_commit -C bottom multiple_remote_commit && + # Create middle commit + git -C middle/bottom fetch && + git -C middle/bottom checkout -f FETCH_HEAD && + git -C middle add bottom && + git -C middle commit -m "multiple_remote_commit" && + # Create top commit + git -C top/middle fetch && + git -C top/middle checkout -f FETCH_HEAD && + git -C top add middle && + git -C top commit -m "multiple_remote_commit" && + + # rename the submodule remote + git -C top-cloned/middle remote rename origin upstream && + + # Add another remote + git -C top-cloned/middle remote add other bogus && + + # Make the update of "middle" a no-op, otherwise we error out + # because of its unmerged state + test_config -C top-cloned submodule.middle.update !true && + git -C top-cloned submodule update --recursive 2>actual.err && + cat >expect.err <<-\EOF && + EOF + test_cmp expect.err actual.err +' + +test_expect_success 'submodule update with renamed remote' ' + test_when_finished "rm -fr top-cloned" && + cp -r top-clean top-cloned && + + # Create a commit in each repo, starting with bottom + test_commit -C bottom rename_commit && + # Create middle commit + git -C middle/bottom fetch && + git -C middle/bottom checkout -f FETCH_HEAD && + git -C middle add bottom && + git -C middle commit -m "rename_commit" && + # Create top commit + git -C top/middle fetch && + git -C top/middle checkout -f FETCH_HEAD && + git -C top add middle && + git -C top commit -m "rename_commit" && + + # rename the submodule remote + git -C top-cloned/middle remote rename origin upstream && + + # Make the update of "middle" a no-op, otherwise we error out + # because of its unmerged state + test_config -C top-cloned submodule.middle.update !true && + git -C top-cloned submodule update --recursive 2>actual.err && + cat >expect.err <<-\EOF && + EOF + test_cmp expect.err actual.err +' + test_expect_success 'submodule update should skip unmerged submodules' ' test_when_finished "rm -fr top-cloned" && cp -r top-clean top-cloned && diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh index 9509dc18fd..6fd3b870de 100755 --- a/t/t7413-submodule-is-active.sh +++ b/t/t7413-submodule-is-active.sh @@ -124,4 +124,19 @@ test_expect_success 'is-active, submodule.active and submodule add' ' git -C super2 config --get submodule.mod.active ' +test_expect_success 'submodule add skips redundant active entry' ' + git init repo && + ( + cd repo && + git config submodule.active "lib/*" && + git commit --allow-empty -m init && + + git init ../lib-origin && + git -C ../lib-origin commit --allow-empty -m init && + + git submodule add ../lib-origin lib/foo && + test_must_fail git config --get submodule.lib/foo.active + ) +' + test_done diff --git a/t/t7422-submodule-output.sh b/t/t7422-submodule-output.sh index 023a5cbdc4..aea1ddf117 100755 --- a/t/t7422-submodule-output.sh +++ b/t/t7422-submodule-output.sh @@ -180,17 +180,14 @@ test_expect_success !MINGW 'git submodule status --recursive propagates SIGPIPE' COMMIT=$(git rev-parse HEAD) && for i in $(test_seq 2000) do - printf "[submodule \"sm-$i\"]\npath = recursive-submodule-path-$i\n" "$i" || + echo "[submodule \"sm-$i\"]" && + echo "path = recursive-submodule-path-$i" || return 1 done >gitmodules && BLOB=$(git hash-object -w --stdin <gitmodules) && printf "100644 blob $BLOB\t.gitmodules\n" >tree && - for i in $(test_seq 2000) - do - printf "160000 commit $COMMIT\trecursive-submodule-path-%d\n" "$i" || - return 1 - done >>tree && + test_seq -f "160000 commit $COMMIT\trecursive-submodule-path-%d" 2000 >>tree && TREE=$(git mktree <tree) && COMMIT=$(git commit-tree "$TREE") && diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh index 14b5743b96..f512eed278 100755 --- a/t/t7450-bad-git-dotfiles.sh +++ b/t/t7450-bad-git-dotfiles.sh @@ -401,7 +401,7 @@ test_expect_success SYMLINKS,!WINDOWS,!MINGW 'submodule must not checkout into d git -C repo commit -m submodule && git -c protocol.file.allow=always clone --recurse-submodules repo bad-clone && - ! test -f "$PWD/foo" && + ! test -f "$PWD/bad-clone/sub/foo" && test -f $(printf "bad-clone/sub\r/post-checkout") ' diff --git a/t/t7502-commit-porcelain.sh b/t/t7502-commit-porcelain.sh index b37e2018a7..05f6da4ad9 100755 --- a/t/t7502-commit-porcelain.sh +++ b/t/t7502-commit-porcelain.sh @@ -956,13 +956,39 @@ test_expect_success 'commit --status with custom comment character' ' test_grep "^; Changes to be committed:" .git/COMMIT_EDITMSG ' -test_expect_success 'switch core.commentchar' ' +test_expect_success !WITH_BREAKING_CHANGES 'switch core.commentchar' ' test_commit "#foo" foo && - GIT_EDITOR=.git/FAKE_EDITOR git -c core.commentChar=auto commit --amend && + cat >config-include <<-\EOF && + [core] + commentString=: + commentString=% + commentChar=auto + EOF + test_when_finished "rm config-include" && + test_config include.path "$(pwd)/config-include" && + test_config core.commentChar ! && + GIT_EDITOR=.git/FAKE_EDITOR git commit --amend 2>err && + sed -n "s/^hint: *\$//p; s/^hint: //p; s/^warning: //p" err >actual && + cat >expect <<-EOF && + Support for ${SQ}core.commentChar=auto${SQ} is deprecated and will be removed in Git 3.0 + + To use the default comment string (#) please run + + git config unset core.commentChar + git config unset --file ~/config-include --all core.commentString + git config unset --file ~/config-include core.commentChar + + To set a custom comment string please run + + git config set --file ~/config-include core.commentChar <comment string> + + where ${SQ}<comment string>${SQ} is the string you wish to use. + EOF + test_cmp expect actual && test_grep "^; Changes to be committed:" .git/COMMIT_EDITMSG ' -test_expect_success 'switch core.commentchar but out of options' ' +test_expect_success !WITH_BREAKING_CHANGES 'switch core.commentchar but out of options' ' cat >text <<\EOF && # 1 ; 2 @@ -982,4 +1008,24 @@ EOF ) ' +test_expect_success WITH_BREAKING_CHANGES 'core.commentChar=auto is rejected' ' + test_config core.commentChar auto && + test_must_fail git rev-parse --git-dir 2>err && + sed -n "s/^hint: *\$//p; s/^hint: //p; s/^fatal: //p" err >actual && + cat >expect <<-EOF && + Support for ${SQ}core.commentChar=auto${SQ} has been removed in Git 3.0 + + To use the default comment string (#) please run + + git config unset core.commentChar + + To set a custom comment string please run + + git config set core.commentChar <comment string> + + where ${SQ}<comment string>${SQ} is the string you wish to use. + EOF + test_cmp expect actual +' + test_done diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh index 39677e859a..1201c85ba6 100755 --- a/t/t7510-signed-commit.sh +++ b/t/t7510-signed-commit.sh @@ -449,7 +449,17 @@ test_expect_success 'custom `gpg.program`' ' test_must_fail env LET_GPG_PROGRAM_FAIL=1 \ git commit -S --allow-empty -m must-fail 2>err && - grep zOMG err + grep zOMG err && + + # `gpg.program` starts with `~`, the path should be interpreted to be relative to `$HOME` + test_config gpg.program "~/fake-gpg" && + env HOME="$(pwd)" \ + git commit -S --allow-empty -m signed-commit && + + # `gpg.program` does not specify an absolute path, it should find a program in `$PATH` + test_config gpg.program "fake-gpg" && + env PATH="$PWD:$PATH" \ + git commit -S --allow-empty -m signed-commit ' test_done diff --git a/t/t7528-signed-commit-ssh.sh b/t/t7528-signed-commit-ssh.sh index 065f780636..0f887a3ebe 100755 --- a/t/t7528-signed-commit-ssh.sh +++ b/t/t7528-signed-commit-ssh.sh @@ -84,18 +84,26 @@ test_expect_success GPGSSH 'sign commits using literal public keys with ssh-agen test_config gpg.format ssh && eval $(ssh-agent) && test_when_finished "kill ${SSH_AGENT_PID}" && - ssh-add "${GPGSSH_KEY_PRIMARY}" && - echo 1 >file && git add file && - git commit -a -m rsa-inline -S"$(cat "${GPGSSH_KEY_PRIMARY}.pub")" && - echo 2 >file && - test_config user.signingkey "$(cat "${GPGSSH_KEY_PRIMARY}.pub")" && - git commit -a -m rsa-config -S && - ssh-add "${GPGSSH_KEY_ECDSA}" && - echo 3 >file && - git commit -a -m ecdsa-inline -S"key::$(cat "${GPGSSH_KEY_ECDSA}.pub")" && - echo 4 >file && - test_config user.signingkey "key::$(cat "${GPGSSH_KEY_ECDSA}.pub")" && - git commit -a -m ecdsa-config -S + test_when_finished "test_unconfig user.signingkey" && + mkdir tmpdir && + TMPDIR="$(pwd)/tmpdir" && + ( + export TMPDIR && + ssh-add "${GPGSSH_KEY_PRIMARY}" && + echo 1 >file && git add file && + git commit -a -m rsa-inline -S"$(cat "${GPGSSH_KEY_PRIMARY}.pub")" && + echo 2 >file && + git config user.signingkey "$(cat "${GPGSSH_KEY_PRIMARY}.pub")" && + git commit -a -m rsa-config -S && + ssh-add "${GPGSSH_KEY_ECDSA}" && + echo 3 >file && + git commit -a -m ecdsa-inline -S"key::$(cat "${GPGSSH_KEY_ECDSA}.pub")" && + echo 4 >file && + git config user.signingkey "key::$(cat "${GPGSSH_KEY_ECDSA}.pub")" && + git commit -a -m ecdsa-config -S + ) && + find tmpdir -type f >tmpfiles && + test_must_be_empty tmpfiles ' test_expect_success GPGSSH,GPGSSH_VERIFYTIME 'create signed commits with keys having defined lifetimes' ' diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 2a8df29219..9838094b66 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -185,8 +185,19 @@ test_expect_success 'reject non-strategy with a git-merge-foo name' ' test_expect_success 'merge c0 with c1' ' echo "OBJID HEAD@{0}: merge c1: Fast-forward" >reflog.expected && + cat >expect <<-\EOF && + Updating FROM..TO + Fast-forward + file | 2 +- + other | 9 +++++++++ + 2 files changed, 10 insertions(+), 1 deletion(-) + create mode 100644 other + EOF + git reset --hard c0 && - git merge c1 && + git merge c1 >out && + sed -e "1s/^Updating [0-9a-f.]*/Updating FROM..TO/" out >actual && + test_cmp expect actual && verify_merge file result.1 && verify_head "$c1" && @@ -205,6 +216,67 @@ test_expect_success 'merge c0 with c1 with --ff-only' ' verify_head "$c1" ' +test_expect_success 'the same merge with merge.stat=diffstat' ' + cat >expect <<-\EOF && + Updating FROM..TO + Fast-forward + file | 2 +- + other | 9 +++++++++ + 2 files changed, 10 insertions(+), 1 deletion(-) + create mode 100644 other + EOF + + git reset --hard c0 && + git -c merge.stat=diffstat merge c1 >out && + sed -e "1s/^Updating [0-9a-f.]*/Updating FROM..TO/" out >actual && + test_cmp expect actual +' + +test_expect_success 'the same merge with compact summary' ' + cat >expect <<-\EOF && + Updating FROM..TO + Fast-forward + file | 2 +- + other (new) | 9 +++++++++ + 2 files changed, 10 insertions(+), 1 deletion(-) + EOF + + git reset --hard c0 && + git merge --compact-summary c1 >out && + sed -e "1s/^Updating [0-9a-f.]*/Updating FROM..TO/" out >actual && + test_cmp expect actual +' + +test_expect_success 'the same merge with compact summary' ' + cat >expect <<-\EOF && + Updating FROM..TO + Fast-forward + file | 2 +- + other (new) | 9 +++++++++ + 2 files changed, 10 insertions(+), 1 deletion(-) + EOF + + git reset --hard c0 && + git merge --compact-summary c1 >out && + sed -e "1s/^Updating [0-9a-f.]*/Updating FROM..TO/" out >actual && + test_cmp expect actual +' + +test_expect_success 'the same merge with merge.stat=compact' ' + cat >expect <<-\EOF && + Updating FROM..TO + Fast-forward + file | 2 +- + other (new) | 9 +++++++++ + 2 files changed, 10 insertions(+), 1 deletion(-) + EOF + + git reset --hard c0 && + git -c merge.stat=compact merge c1 >out && + sed -e "1s/^Updating [0-9a-f.]*/Updating FROM..TO/" out >actual && + test_cmp expect actual +' + test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge from unborn branch' ' diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh index 611755cc13..73b78bdd88 100755 --- a/t/t7700-repack.sh +++ b/t/t7700-repack.sh @@ -838,4 +838,67 @@ test_expect_success '-n overrides repack.updateServerInfo=true' ' test_server_info_missing ' +test_expect_success 'pending objects are repacked appropriately' ' + test_when_finished rm -rf pending && + git init pending && + + ( + cd pending && + + # Commit file, a/b/c and never change them. + mkdir -p a/b && + echo singleton >file && + echo stuff >a/b/c && + echo more >a/d && + git add file a && + git commit -m "single blobs" && + + # Files a/d and a/e will not be singletons. + echo d >a/d && + echo e >a/e && + git add a && + git commit -m "more blobs" && + + # This use of a sparse index helps to force + # test that the cache-tree is walked, too. + git sparse-checkout set --sparse-index a x && + + # Create staged changes: + # * a/e now has multiple versions. + # * a/i now has only one version. + echo f >a/d && + echo h >a/e && + echo i >a/i && + git add a && + + # Stage and unstage a change to make use of + # resolve-undo cache and how that impacts fsck. + mkdir x && + echo y >x/y && + git add x && + xy=$(git rev-parse :x/y) && + git rm --cached x/y && + + # The blob for x/y must persist through repacks, + # but fsck currently ignores the REUC extension + # for finding links to the blob. + cat >expect <<-EOF && + dangling blob $xy + EOF + + # Bring the loose objects into a packfile to avoid + # leftovers in next test. Without this, the loose + # objects persist and the test succeeds for other + # reasons. + git repack -adf && + git fsck >out && + test_cmp expect out && + + # Test path walk version with pack.useSparse. + git -c pack.useSparse=true repack -adf --path-walk && + git fsck >out && + test_cmp expect out + ) +' + test_done diff --git a/t/t7704-repack-cruft.sh b/t/t7704-repack-cruft.sh index 8aebfb45f5..aa2e2e6ad8 100755 --- a/t/t7704-repack-cruft.sh +++ b/t/t7704-repack-cruft.sh @@ -724,4 +724,149 @@ test_expect_success 'cruft repack respects --quiet' ' ) ' +setup_cruft_exclude_tests() { + git init "$1" && + ( + cd "$1" && + + git config repack.midxMustContainCruft false && + + test_commit one && + + test_commit --no-tag two && + two="$(git rev-parse HEAD)" && + test_commit --no-tag three && + three="$(git rev-parse HEAD)" && + git reset --hard one && + git reflog expire --all --expire=all && + + GIT_TEST_MULTI_PACK_INDEX=0 git repack --cruft -d && + + git merge $two && + test_commit four + ) +} + +test_expect_success 'repack --write-midx excludes cruft where possible' ' + setup_cruft_exclude_tests exclude-cruft-when-possible && + ( + cd exclude-cruft-when-possible && + + GIT_TEST_MULTI_PACK_INDEX=0 \ + git repack -d --geometric=2 --write-midx --write-bitmap-index && + + test-tool read-midx --show-objects $objdir >midx && + cruft="$(ls $packdir/*.mtimes)" && + test_grep ! "$(basename "$cruft" .mtimes).idx" midx && + + git rev-list --all --objects --no-object-names >reachable.raw && + sort reachable.raw >reachable.objects && + awk "/\.pack$/ { print \$1 }" <midx | sort >midx.objects && + + test_cmp reachable.objects midx.objects + ) +' + +test_expect_success 'repack --write-midx includes cruft when instructed' ' + setup_cruft_exclude_tests exclude-cruft-when-instructed && + ( + cd exclude-cruft-when-instructed && + + GIT_TEST_MULTI_PACK_INDEX=0 \ + git -c repack.midxMustContainCruft=true repack \ + -d --geometric=2 --write-midx --write-bitmap-index && + + test-tool read-midx --show-objects $objdir >midx && + cruft="$(ls $packdir/*.mtimes)" && + test_grep "$(basename "$cruft" .mtimes).idx" midx && + + git cat-file --batch-check="%(objectname)" --batch-all-objects \ + >all.objects && + awk "/\.pack$/ { print \$1 }" <midx | sort >midx.objects && + + test_cmp all.objects midx.objects + ) +' + +test_expect_success 'repack --write-midx includes cruft when necessary' ' + setup_cruft_exclude_tests exclude-cruft-when-necessary && + ( + cd exclude-cruft-when-necessary && + + test_path_is_file $(ls $packdir/pack-*.mtimes) && + ( cd $packdir && ls pack-*.idx ) | sort >packs.all && + git multi-pack-index write --stdin-packs --bitmap <packs.all && + + test_commit five && + GIT_TEST_MULTI_PACK_INDEX=0 \ + git repack -d --geometric=2 --write-midx --write-bitmap-index && + + test-tool read-midx --show-objects $objdir >midx && + awk "/\.pack$/ { print \$1 }" <midx | sort >midx.objects && + git cat-file --batch-all-objects --batch-check="%(objectname)" \ + >expect.objects && + test_cmp expect.objects midx.objects && + + grep "^pack-" midx >midx.packs && + test_line_count = "$(($(wc -l <packs.all) + 1))" midx.packs + ) +' + +test_expect_success 'repack --write-midx includes cruft when already geometric' ' + git init repack--write-midx-geometric-noop && + ( + cd repack--write-midx-geometric-noop && + + git branch -M main && + test_commit A && + test_commit B && + + git checkout -B side && + test_commit --no-tag C && + C="$(git rev-parse HEAD)" && + + git checkout main && + git branch -D side && + git reflog expire --all --expire=all && + + # At this point we have two packs: one containing the + # objects belonging to commits A and B, and another + # (cruft) pack containing the objects belonging to + # commit C. + git repack --cruft -d && + + # Create a third pack which contains a merge commit + # making commit C reachable again. + # + # --no-ff is important here, as it ensures that we + # actually write a new object and subsequently a new + # pack to contain it. + git merge --no-ff $C && + git repack -d && + + ls $packdir/pack-*.idx | sort >packs.all && + cruft="$(ls $packdir/pack-*.mtimes)" && + cruft="${cruft%.mtimes}.idx" && + + for idx in $(grep -v $cruft <packs.all) + do + git show-index <$idx >out && + wc -l <out || return 1 + done >sizes.raw && + + # Make sure that there are two non-cruft packs, and + # that one of them contains at least twice as many + # objects as the other, ensuring that they are already + # in a geometric progression. + sort -n sizes.raw >sizes && + test_line_count = 2 sizes && + s1=$(head -n 1 sizes) && + s2=$(tail -n 1 sizes) && + test "$s2" -gt "$((2 * $s1))" && + + git -c repack.midxMustContainCruft=false repack --geometric=2 \ + --write-midx --write-bitmap-index + ) +' + test_done diff --git a/t/t7815-grep-binary.sh b/t/t7815-grep-binary.sh index b7d83f9a5d..55d5e6de17 100755 --- a/t/t7815-grep-binary.sh +++ b/t/t7815-grep-binary.sh @@ -63,7 +63,7 @@ test_expect_success 'git grep ile a' ' git grep ile a ' -test_expect_failure !CYGWIN 'git grep .fi a' ' +test_expect_failure !CYGWIN,!MACOS 'git grep .fi a' ' git grep .fi a ' diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh index 8cf89e285f..ddd273d8dc 100755 --- a/t/t7900-maintenance.sh +++ b/t/t7900-maintenance.sh @@ -49,9 +49,9 @@ test_expect_success 'run [--auto|--quiet]' ' git maintenance run --auto 2>/dev/null && GIT_TRACE2_EVENT="$(pwd)/run-no-quiet.txt" \ git maintenance run --no-quiet 2>/dev/null && - test_subcommand git gc --quiet --no-detach <run-no-auto.txt && - test_subcommand ! git gc --auto --quiet --no-detach <run-auto.txt && - test_subcommand git gc --no-quiet --no-detach <run-no-quiet.txt + test_subcommand git gc --quiet --no-detach --skip-foreground-tasks <run-no-auto.txt && + test_subcommand ! git gc --auto --quiet --no-detach --skip-foreground-tasks <run-auto.txt && + test_subcommand git gc --no-quiet --no-detach --skip-foreground-tasks <run-no-quiet.txt ' test_expect_success 'maintenance.auto config option' ' @@ -154,9 +154,9 @@ test_expect_success 'run --task=<task>' ' git maintenance run --task=commit-graph 2>/dev/null && GIT_TRACE2_EVENT="$(pwd)/run-both.txt" \ git maintenance run --task=commit-graph --task=gc 2>/dev/null && - test_subcommand ! git gc --quiet --no-detach <run-commit-graph.txt && - test_subcommand git gc --quiet --no-detach <run-gc.txt && - test_subcommand git gc --quiet --no-detach <run-both.txt && + test_subcommand ! git gc --quiet --no-detach --skip-foreground-tasks <run-commit-graph.txt && + test_subcommand git gc --quiet --no-detach --skip-foreground-tasks <run-gc.txt && + test_subcommand git gc --quiet --no-detach --skip-foreground-tasks <run-both.txt && test_subcommand git commit-graph write --split --reachable --no-progress <run-commit-graph.txt && test_subcommand ! git commit-graph write --split --reachable --no-progress <run-gc.txt && test_subcommand git commit-graph write --split --reachable --no-progress <run-both.txt @@ -610,7 +610,12 @@ test_expect_success 'rerere-gc task with --auto honors maintenance.rerere-gc.aut test_expect_success '--auto and --schedule incompatible' ' test_must_fail git maintenance run --auto --schedule=daily 2>err && - test_grep "at most one" err + test_grep "cannot be used together" err +' + +test_expect_success '--task and --schedule incompatible' ' + test_must_fail git maintenance run --task=pack-refs --schedule=daily 2>err && + test_grep "cannot be used together" err ' test_expect_success 'invalid --schedule value' ' diff --git a/t/t8020-last-modified.sh b/t/t8020-last-modified.sh new file mode 100755 index 0000000000..5eb4cef035 --- /dev/null +++ b/t/t8020-last-modified.sh @@ -0,0 +1,210 @@ +#!/bin/sh + +test_description='last-modified tests' + +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit 1 file && + mkdir a && + test_commit 2 a/file && + mkdir a/b && + test_commit 3 a/b/file +' + +test_expect_success 'cannot run last-modified on two trees' ' + test_must_fail git last-modified HEAD HEAD~1 +' + +check_last_modified() { + local indir= && + while test $# != 0 + do + case "$1" in + -C) + indir="$2" + shift + ;; + *) + break + ;; + esac && + shift + done && + + cat >expect && + test_when_finished "rm -f tmp.*" && + git ${indir:+-C "$indir"} last-modified "$@" >tmp.1 && + git name-rev --annotate-stdin --name-only --tags \ + <tmp.1 >tmp.2 && + tr '\t' ' ' <tmp.2 >actual && + test_cmp expect actual +} + +test_expect_success 'last-modified non-recursive' ' + check_last_modified <<-\EOF + 3 a + 1 file + EOF +' + +test_expect_success 'last-modified recursive' ' + check_last_modified -r <<-\EOF + 3 a/b/file + 2 a/file + 1 file + EOF +' + +test_expect_success 'last-modified recursive with show-trees' ' + check_last_modified -r -t <<-\EOF + 3 a + 3 a/b + 3 a/b/file + 2 a/file + 1 file + EOF +' + +test_expect_success 'last-modified non-recursive with show-trees' ' + check_last_modified -t <<-\EOF + 3 a + 1 file + EOF +' + +test_expect_success 'last-modified subdir' ' + check_last_modified a <<-\EOF + 3 a + EOF +' + +test_expect_success 'last-modified subdir recursive' ' + check_last_modified -r a <<-\EOF + 3 a/b/file + 2 a/file + EOF +' + +test_expect_success 'last-modified from non-HEAD commit' ' + check_last_modified HEAD^ <<-\EOF + 2 a + 1 file + EOF +' + +test_expect_success 'last-modified from subdir defaults to root' ' + check_last_modified -C a <<-\EOF + 3 a + 1 file + EOF +' + +test_expect_success 'last-modified from subdir uses relative pathspecs' ' + check_last_modified -C a -r b <<-\EOF + 3 a/b/file + EOF +' + +test_expect_success 'limit last-modified traversal by count' ' + check_last_modified -1 <<-\EOF + 3 a + ^2 file + EOF +' + +test_expect_success 'limit last-modified traversal by commit' ' + check_last_modified HEAD~2..HEAD <<-\EOF + 3 a + ^1 file + EOF +' + +test_expect_success 'only last-modified files in the current tree' ' + git rm -rf a && + git commit -m "remove a" && + check_last_modified <<-\EOF + 1 file + EOF +' + +test_expect_success 'cross merge boundaries in blaming' ' + git checkout HEAD^0 && + git rm -rf . && + test_commit m1 && + git checkout HEAD^ && + git rm -rf . && + test_commit m2 && + git merge m1 && + check_last_modified <<-\EOF + m2 m2.t + m1 m1.t + EOF +' + +test_expect_success 'last-modified merge for resolved conflicts' ' + git checkout HEAD^0 && + git rm -rf . && + test_commit c1 conflict && + git checkout HEAD^ && + git rm -rf . && + test_commit c2 conflict && + test_must_fail git merge c1 && + test_commit resolved conflict && + check_last_modified conflict <<-\EOF + resolved conflict + EOF +' + + +# Consider `file` with this content through history: +# +# A---B---B-------B---B +# \ / +# C---D +test_expect_success 'last-modified merge ignores content from branch' ' + git checkout HEAD^0 && + git rm -rf . && + test_commit a1 file A && + test_commit a2 file B && + test_commit a3 file C && + test_commit a4 file D && + git checkout a2 && + git merge --no-commit --no-ff a4 && + git checkout a2 -- file && + git merge --continue && + check_last_modified <<-\EOF + a2 file + EOF +' + +# Consider `file` with this content through history: +# +# A---B---B---C---D---B---B +# \ / +# B-------B +test_expect_success 'last-modified merge undoes changes' ' + git checkout HEAD^0 && + git rm -rf . && + test_commit b1 file A && + test_commit b2 file B && + test_commit b3 file C && + test_commit b4 file D && + git checkout b2 && + test_commit b5 file2 2 && + git checkout b4 && + git merge --no-commit --no-ff b5 && + git checkout b2 -- file && + git merge --continue && + check_last_modified <<-\EOF + b5 file2 + b2 file + EOF +' + +test_expect_success 'last-modified complains about unknown arguments' ' + test_must_fail git last-modified --foo 2>err && + grep "unknown last-modified argument: --foo" err +' + +test_done diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 0c1af43f6f..e56e0c8d77 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -201,6 +201,13 @@ test_expect_success $PREREQ 'cc trailer with get_maintainer.pl output' ' test_cmp expected-cc commandline1 ' +test_expect_failure $PREREQ 'invalid smtp server port value' ' + clean_fake_sendmail && + git send-email -1 --to=recipient@example.com \ + --smtp-server-port=bogus-symbolic-name \ + --smtp-server="$(pwd)/fake.sendmail" +' + test_expect_success $PREREQ 'setup expect' " cat >expected-show-all-headers <<\EOF 0001-Second.patch diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index b258dbf1df..4dc3d645bf 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -120,7 +120,7 @@ test_expect_success 'A: create pack from stdin' ' INPUT_END git fast-import --export-marks=marks.out <input && - git whatchanged main + git log --raw main ' test_expect_success 'A: verify pack' ' @@ -279,7 +279,7 @@ test_expect_success 'A: verify marks import does not crash' ' INPUT_END git fast-import --import-marks=marks.out <input && - git whatchanged verify--import-marks + git log --raw verify--import-marks ' test_expect_success 'A: verify pack' ' @@ -652,7 +652,7 @@ test_expect_success 'C: incremental import create pack from stdin' ' INPUT_END git fast-import <input && - git whatchanged branch + git log --raw branch ' test_expect_success 'C: verify pack' ' @@ -715,7 +715,7 @@ test_expect_success 'D: inline data in commit' ' INPUT_END git fast-import <input && - git whatchanged branch + git log --raw branch ' test_expect_success 'D: verify pack' ' @@ -882,7 +882,7 @@ test_expect_success 'H: deletall, add 1' ' INPUT_END git fast-import <input && - git whatchanged H + git log --raw H ' test_expect_success 'H: verify pack' ' @@ -2066,7 +2066,7 @@ test_expect_success 'Q: commit notes' ' INPUT_END git fast-import <input && - git whatchanged notes-test + git log --raw notes-test ' test_expect_success 'Q: verify pack' ' diff --git a/t/t9301-fast-import-notes.sh b/t/t9301-fast-import-notes.sh index 1ae4d7c0d3..e62173cf1f 100755 --- a/t/t9301-fast-import-notes.sh +++ b/t/t9301-fast-import-notes.sh @@ -76,7 +76,7 @@ INPUT_END test_expect_success 'set up main branch' ' git fast-import <input && - git whatchanged main + git log --raw main ' commit4=$(git rev-parse refs/heads/main) diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh index 76619765fc..8f85c69d62 100755 --- a/t/t9350-fast-export.sh +++ b/t/t9350-fast-export.sh @@ -48,12 +48,11 @@ test_expect_success 'fast-export | fast-import' ' mkdir new && git --git-dir=new/.git init && git fast-export --all >actual && - (cd new && - git fast-import && - test $MAIN = $(git rev-parse --verify refs/heads/main) && - test $REIN = $(git rev-parse --verify refs/tags/rein) && - test $WER = $(git rev-parse --verify refs/heads/wer) && - test $MUSS = $(git rev-parse --verify refs/tags/muss)) <actual + git -C new fast-import <actual && + test $MAIN = $(git -C new rev-parse --verify refs/heads/main) && + test $REIN = $(git -C new rev-parse --verify refs/tags/rein) && + test $WER = $(git -C new rev-parse --verify refs/heads/wer) && + test $MUSS = $(git -C new rev-parse --verify refs/tags/muss) ' @@ -87,13 +86,11 @@ test_expect_success 'fast-export --mark-tags ^muss^{commit} muss' ' test_expect_success 'fast-export main~2..main' ' git fast-export main~2..main >actual && - sed "s/main/partial/" actual | - (cd new && - git fast-import && - test $MAIN != $(git rev-parse --verify refs/heads/partial) && - git diff --exit-code main partial && - git diff --exit-code main^ partial^ && - test_must_fail git rev-parse partial~2) + sed "s/main/partial/" actual | git -C new fast-import && + test $MAIN != $(git -C new rev-parse --verify refs/heads/partial) && + git -C new diff --exit-code main partial && + git -C new diff --exit-code main^ partial^ && + test_must_fail git -C new rev-parse partial~2 ' @@ -102,10 +99,8 @@ test_expect_success 'fast-export --reference-excluded-parents main~2..main' ' git fast-export --reference-excluded-parents main~2..main >actual && grep commit.refs/heads/main actual >commit-count && test_line_count = 2 commit-count && - sed "s/main/rewrite/" actual | - (cd new && - git fast-import && - test $MAIN = $(git rev-parse --verify refs/heads/rewrite)) + sed "s/main/rewrite/" actual | git -C new fast-import && + test $MAIN = $(git -C new rev-parse --verify refs/heads/rewrite) ' test_expect_success 'fast-export --show-original-ids' ' @@ -133,20 +128,19 @@ test_expect_success ICONV 'reencoding iso-8859-7' ' echo rosten >file && git commit -s -F "$TEST_DIRECTORY/t9350/simple-iso-8859-7-commit-message.txt" file && git fast-export --reencode=yes wer^..wer >iso-8859-7.fi && - sed "s/wer/i18n/" iso-8859-7.fi | - (cd new && - git fast-import && - # The commit object, if not re-encoded, would be 200 bytes plus hash. - # Removing the "encoding iso-8859-7\n" header drops 20 bytes. - # Re-encoding the Pi character from \xF0 (\360) in iso-8859-7 - # to \xCF\x80 (\317\200) in UTF-8 adds a byte. Check for - # the expected size. - test $(($(test_oid hexsz) + 181)) -eq "$(git cat-file -s i18n)" && - # ...and for the expected translation of bytes. - git cat-file commit i18n >actual && - grep $(printf "\317\200") actual && - # Also make sure the commit does not have the "encoding" header - ! grep ^encoding actual) + sed "s/wer/i18n/" iso-8859-7.fi | git -C new fast-import && + + # The commit object, if not re-encoded, would be 200 bytes plus hash. + # Removing the "encoding iso-8859-7\n" header drops 20 bytes. + # Re-encoding the Pi character from \xF0 (\360) in iso-8859-7 + # to \xCF\x80 (\317\200) in UTF-8 adds a byte. Check for + # the expected size. + test $(($(test_oid hexsz) + 181)) -eq "$(git -C new cat-file -s i18n)" && + # ...and for the expected translation of bytes. + git -C new cat-file commit i18n >actual && + grep $(printf "\317\200") actual && + # Also make sure the commit does not have the "encoding" header + ! grep ^encoding actual ' test_expect_success 'aborting on iso-8859-7' ' @@ -165,20 +159,19 @@ test_expect_success 'preserving iso-8859-7' ' echo rosten >file && git commit -s -F "$TEST_DIRECTORY/t9350/simple-iso-8859-7-commit-message.txt" file && git fast-export --reencode=no wer^..wer >iso-8859-7.fi && - sed "s/wer/i18n-no-recoding/" iso-8859-7.fi | - (cd new && - git fast-import && - # The commit object, if not re-encoded, is 200 bytes plus hash. - # Removing the "encoding iso-8859-7\n" header would drops 20 - # bytes. Re-encoding the Pi character from \xF0 (\360) in - # iso-8859-7 to \xCF\x80 (\317\200) in UTF-8 adds a byte. - # Check for the expected size... - test $(($(test_oid hexsz) + 200)) -eq "$(git cat-file -s i18n-no-recoding)" && - # ...as well as the expected byte. - git cat-file commit i18n-no-recoding >actual && - grep $(printf "\360") actual && - # Also make sure the commit has the "encoding" header - grep ^encoding actual) + sed "s/wer/i18n-no-recoding/" iso-8859-7.fi | git -C new fast-import && + + # The commit object, if not re-encoded, is 200 bytes plus hash. + # Removing the "encoding iso-8859-7\n" header would drops 20 + # bytes. Re-encoding the Pi character from \xF0 (\360) in + # iso-8859-7 to \xCF\x80 (\317\200) in UTF-8 adds a byte. + # Check for the expected size... + test $(($(test_oid hexsz) + 200)) -eq "$(git -C new cat-file -s i18n-no-recoding)" && + # ...as well as the expected byte. + git -C new cat-file commit i18n-no-recoding >actual && + grep $(printf "\360") actual && + # Also make sure the commit has the "encoding" header + grep ^encoding actual ' test_expect_success 'encoding preserved if reencoding fails' ' @@ -188,18 +181,17 @@ test_expect_success 'encoding preserved if reencoding fails' ' echo rosten >file && git commit -s -F "$TEST_DIRECTORY/t9350/broken-iso-8859-7-commit-message.txt" file && git fast-export --reencode=yes wer^..wer >iso-8859-7.fi && - sed "s/wer/i18n-invalid/" iso-8859-7.fi | - (cd new && - git fast-import && - git cat-file commit i18n-invalid >actual && - # Make sure the commit still has the encoding header - grep ^encoding actual && - # Verify that the commit has the expected size; i.e. - # that no bytes were re-encoded to a different encoding. - test $(($(test_oid hexsz) + 212)) -eq "$(git cat-file -s i18n-invalid)" && - # ...and check for the original special bytes - grep $(printf "\360") actual && - grep $(printf "\377") actual) + sed "s/wer/i18n-invalid/" iso-8859-7.fi | git -C new fast-import && + git -C new cat-file commit i18n-invalid >actual && + + # Make sure the commit still has the encoding header + grep ^encoding actual && + # Verify that the commit has the expected size; i.e. + # that no bytes were re-encoded to a different encoding. + test $(($(test_oid hexsz) + 212)) -eq "$(git -C new cat-file -s i18n-invalid)" && + # ...and check for the original special bytes + grep $(printf "\360") actual && + grep $(printf "\377") actual ' test_expect_success 'import/export-marks' ' @@ -314,29 +306,23 @@ test_expect_success GPG 'signed-commits=abort' ' test_expect_success GPG 'signed-commits=verbatim' ' git fast-export --signed-commits=verbatim --reencode=no commit-signing >output && - grep "^gpgsig sha" output && + test_grep -E "^gpgsig $GIT_DEFAULT_HASH openpgp" output && grep "encoding ISO-8859-1" output && - ( - cd new && - git fast-import && - STRIPPED=$(git rev-parse --verify refs/heads/commit-signing) && - test $COMMIT_SIGNING = $STRIPPED - ) <output + git -C new fast-import <output && + STRIPPED=$(git -C new rev-parse --verify refs/heads/commit-signing) && + test $COMMIT_SIGNING = $STRIPPED ' test_expect_success GPG 'signed-commits=warn-verbatim' ' git fast-export --signed-commits=warn-verbatim --reencode=no commit-signing >output 2>err && - grep "^gpgsig sha" output && + test_grep -E "^gpgsig $GIT_DEFAULT_HASH openpgp" output && grep "encoding ISO-8859-1" output && test -s err && - ( - cd new && - git fast-import && - STRIPPED=$(git rev-parse --verify refs/heads/commit-signing) && - test $COMMIT_SIGNING = $STRIPPED - ) <output + git -C new fast-import <output && + STRIPPED=$(git -C new rev-parse --verify refs/heads/commit-signing) && + test $COMMIT_SIGNING = $STRIPPED ' @@ -345,12 +331,9 @@ test_expect_success GPG 'signed-commits=strip' ' git fast-export --signed-commits=strip --reencode=no commit-signing >output && ! grep ^gpgsig output && grep "^encoding ISO-8859-1" output && - sed "s/commit-signing/commit-strip-signing/" output | ( - cd new && - git fast-import && - STRIPPED=$(git rev-parse --verify refs/heads/commit-strip-signing) && - test $COMMIT_SIGNING != $STRIPPED - ) + sed "s/commit-signing/commit-strip-signing/" output | git -C new fast-import && + STRIPPED=$(git -C new rev-parse --verify refs/heads/commit-strip-signing) && + test $COMMIT_SIGNING != $STRIPPED ' @@ -360,12 +343,59 @@ test_expect_success GPG 'signed-commits=warn-strip' ' ! grep ^gpgsig output && grep "^encoding ISO-8859-1" output && test -s err && - sed "s/commit-signing/commit-strip-signing/" output | ( - cd new && - git fast-import && - STRIPPED=$(git rev-parse --verify refs/heads/commit-strip-signing) && - test $COMMIT_SIGNING != $STRIPPED - ) + sed "s/commit-signing/commit-strip-signing/" output | git -C new fast-import && + STRIPPED=$(git -C new rev-parse --verify refs/heads/commit-strip-signing) && + test $COMMIT_SIGNING != $STRIPPED + +' + +test_expect_success GPGSM 'setup X.509 signed commit' ' + + git checkout -b x509-signing main && + test_config gpg.format x509 && + test_config user.signingkey $GIT_COMMITTER_EMAIL && + echo "X.509 content" >file && + git add file && + git commit -S -m "X.509 signed commit" && + X509_COMMIT=$(git rev-parse HEAD) && + git checkout main + +' + +test_expect_success GPGSM 'round-trip X.509 signed commit' ' + + git fast-export --signed-commits=verbatim x509-signing >output && + test_grep -E "^gpgsig $GIT_DEFAULT_HASH x509" output && + git -C new fast-import <output && + git -C new cat-file commit refs/heads/x509-signing >actual && + grep "^gpgsig" actual && + IMPORTED=$(git -C new rev-parse refs/heads/x509-signing) && + test $X509_COMMIT = $IMPORTED + +' + +test_expect_success GPGSSH 'setup SSH signed commit' ' + + git checkout -b ssh-signing main && + test_config gpg.format ssh && + test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" && + echo "SSH content" >file && + git add file && + git commit -S -m "SSH signed commit" && + SSH_COMMIT=$(git rev-parse HEAD) && + git checkout main + +' + +test_expect_success GPGSSH 'round-trip SSH signed commit' ' + + git fast-export --signed-commits=verbatim ssh-signing >output && + test_grep -E "^gpgsig $GIT_DEFAULT_HASH ssh" output && + git -C new fast-import <output && + git -C new cat-file commit refs/heads/ssh-signing >actual && + grep "^gpgsig" actual && + IMPORTED=$(git -C new rev-parse refs/heads/ssh-signing) && + test $SSH_COMMIT = $IMPORTED ' @@ -405,14 +435,13 @@ test_expect_success 'submodule fast-export | fast-import' ' mkdir new && git --git-dir=new/.git init && git fast-export --signed-tags=strip --all >actual && - (cd new && - git fast-import && - test "$SUBENT1" = "$(git ls-tree refs/heads/main^ sub)" && - test "$SUBENT2" = "$(git ls-tree refs/heads/main sub)" && - git checkout main && - git submodule init && - git submodule update && - cmp sub/file ../sub/file) <actual + git -C new fast-import <actual && + test "$SUBENT1" = "$(git -C new ls-tree refs/heads/main^ sub)" && + test "$SUBENT2" = "$(git -C new ls-tree refs/heads/main sub)" && + git -C new checkout main && + git -C new submodule init && + git -C new submodule update && + cmp new/sub/file sub/file ' @@ -454,10 +483,8 @@ test_expect_success 'fast-export -C -C | fast-import' ' git --git-dir=new/.git init && git fast-export -C -C --signed-tags=strip --all > output && grep "^C file2 file4\$" output && - cat output | - (cd new && - git fast-import && - test $ENTRY = $(git rev-parse --verify refs/heads/copy)) + git -C new fast-import <output && + test $ENTRY = $(git -C new rev-parse --verify refs/heads/copy) ' @@ -905,4 +932,42 @@ test_expect_success 'fast-export handles --end-of-options' ' test_cmp expect actual ' +test_expect_success GPG 'setup a commit with dual signatures on its SHA-1 and SHA-256 formats' ' + # Create a signed SHA-256 commit + git init --object-format=sha256 explicit-sha256 && + git -C explicit-sha256 config extensions.compatObjectFormat sha1 && + git -C explicit-sha256 checkout -b dual-signed && + test_commit -C explicit-sha256 A && + echo B >explicit-sha256/B && + git -C explicit-sha256 add B && + test_tick && + git -C explicit-sha256 commit -S -m "signed" B && + SHA256_B=$(git -C explicit-sha256 rev-parse dual-signed) && + + # Create the corresponding SHA-1 commit + SHA1_B=$(git -C explicit-sha256 rev-parse --output-object-format=sha1 dual-signed) && + + # Check that the resulting SHA-1 commit has both signatures + echo $SHA1_B | git -C explicit-sha256 cat-file --batch >out && + test_grep -E "^gpgsig " out && + test_grep -E "^gpgsig-sha256 " out +' + +test_expect_success GPG 'export and import of doubly signed commit' ' + git -C explicit-sha256 fast-export --signed-commits=verbatim dual-signed >output && + test_grep -E "^gpgsig sha1 openpgp" output && + test_grep -E "^gpgsig sha256 openpgp" output && + git -C new fast-import <output && + git -C new cat-file commit refs/heads/dual-signed >actual && + test_grep -E "^gpgsig " actual && + test_grep -E "^gpgsig-sha256 " actual && + IMPORTED=$(git -C new rev-parse refs/heads/dual-signed) && + if test "$GIT_DEFAULT_HASH" = "sha1" + then + test $SHA1_B = $IMPORTED + else + test $SHA256_B = $IMPORTED + fi +' + test_done diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index 7679780fb8..578d6c8b32 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -700,19 +700,17 @@ test_expect_success \ # ---------------------------------------------------------------------- # syntax highlighting +test_lazy_prereq HIGHLIGHT ' + highlight_version=$(highlight --version </dev/null 2>/dev/null) && + test -n "$highlight_version" +' -highlight_version=$(highlight --version </dev/null 2>/dev/null) -if [ $? -eq 127 ]; then - say "Skipping syntax highlighting tests: 'highlight' not found" -elif test -z "$highlight_version"; then - say "Skipping syntax highlighting tests: incorrect 'highlight' found" -else - test_set_prereq HIGHLIGHT +test_expect_success HIGHLIGHT ' cat >>gitweb_config.perl <<-\EOF our $highlight_bin = "highlight"; - $feature{'highlight'}{'override'} = 1; + $feature{"highlight"}{"override"} = 1; EOF -fi +' test_expect_success HIGHLIGHT \ 'syntax highlighting (no highlight, unknown syntax)' \ diff --git a/t/t9822-git-p4-path-encoding.sh b/t/t9822-git-p4-path-encoding.sh index 572d395498..e6e07facd4 100755 --- a/t/t9822-git-p4-path-encoding.sh +++ b/t/t9822-git-p4-path-encoding.sh @@ -7,12 +7,17 @@ test_description='Clone repositories with non ASCII paths' UTF8_ESCAPED="a-\303\244_o-\303\266_u-\303\274.txt" ISO8859_ESCAPED="a-\344_o-\366_u-\374.txt" -ISO8859="$(printf "$ISO8859_ESCAPED")" && -echo content123 >"$ISO8859" && -rm "$ISO8859" || { +test_lazy_prereq FS_ACCEPTS_ISO_8859_1 ' + ISO8859="$(printf "$ISO8859_ESCAPED")" && + echo content123 >"$ISO8859" && + rm "$ISO8859" +' + +if ! test_have_prereq FS_ACCEPTS_ISO_8859_1 +then skip_all="fs does not accept ISO-8859-1 filenames" test_done -} +fi test_expect_success 'start p4d' ' start_p4d diff --git a/t/t9835-git-p4-metadata-encoding-python2.sh b/t/t9835-git-p4-metadata-encoding-python2.sh index 6116f806f6..b969c7e0d5 100755 --- a/t/t9835-git-p4-metadata-encoding-python2.sh +++ b/t/t9835-git-p4-metadata-encoding-python2.sh @@ -12,23 +12,25 @@ failing, and produces maximally sane output in git.' ## SECTION REPEATED IN t9836 ## ############################### +EXTRA_PATH="$(pwd)/temp_python" +mkdir "$EXTRA_PATH" +PATH="$EXTRA_PATH:$PATH" +export PATH + # These tests are specific to Python 2. Write a custom script that executes # git-p4 directly with the Python 2 interpreter to ensure that we use that # version even if Git was compiled with Python 3. -python_target_binary=$(which python2) -if test -n "$python_target_binary" -then - mkdir temp_python - PATH="$(pwd)/temp_python:$PATH" - export PATH - - write_script temp_python/git-p4-python2 <<-EOF +test_lazy_prereq P4_PYTHON2 ' + python_target_binary=$(which python2) && + test -n "$python_target_binary" && + write_script "$EXTRA_PATH"/git-p4-python2 <<-EOF && exec "$python_target_binary" "$(git --exec-path)/git-p4" "\$@" EOF -fi + ( git p4-python2 || true ) >err && + test_grep "valid commands" err +' -git p4-python2 >err -if ! grep 'valid commands' err +if ! test_have_prereq P4_PYTHON2 then skip_all="skipping python2 git p4 tests; python2 not available" test_done diff --git a/t/t9836-git-p4-metadata-encoding-python3.sh b/t/t9836-git-p4-metadata-encoding-python3.sh index 5e5217a66b..da6669bf71 100755 --- a/t/t9836-git-p4-metadata-encoding-python3.sh +++ b/t/t9836-git-p4-metadata-encoding-python3.sh @@ -12,23 +12,25 @@ failing, and produces maximally sane output in git.' ## SECTION REPEATED IN t9835 ## ############################### +EXTRA_PATH="$(pwd)/temp_python" +mkdir "$EXTRA_PATH" +PATH="$EXTRA_PATH:$PATH" +export PATH + # These tests are specific to Python 3. Write a custom script that executes # git-p4 directly with the Python 3 interpreter to ensure that we use that # version even if Git was compiled with Python 2. -python_target_binary=$(which python3) -if test -n "$python_target_binary" -then - mkdir temp_python - PATH="$(pwd)/temp_python:$PATH" - export PATH - - write_script temp_python/git-p4-python3 <<-EOF +test_lazy_prereq P4_PYTHON3 ' + python_target_binary=$(which python3) && + test -n "$python_target_binary" && + write_script "$EXTRA_PATH"/git-p4-python3 <<-EOF && exec "$python_target_binary" "$(git --exec-path)/git-p4" "\$@" EOF -fi + ( git p4-python3 || true ) >err && + test_grep "valid commands" err +' -git p4-python3 >err -if ! grep 'valid commands' err +if ! test_have_prereq P4_PYTHON3 then skip_all="skipping python3 git p4 tests; python3 not available" test_done diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 343b8cd191..964e1f1569 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -11,9 +11,9 @@ test_description='test bash completion' # untraceable with such ancient Bash versions. test_untraceable=UnfortunatelyYes -# Override environment and always use master for the default initial branch +# Override environment and always use main for the default initial branch # name for these tests, so that rev completion candidates are as expected. -GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master +GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME . ./lib-bash.sh @@ -1453,7 +1453,7 @@ test_expect_success 'git bisect - start subcommand arguments before double-dash HEAD Z final Z initial Z - master Z + main Z EOF ) ' @@ -2596,6 +2596,8 @@ test_expect_success 'double dash "git checkout"' ' --merge Z --conflict=Z --patch Z + --unified=Z + --inter-hunk-context=Z --ignore-skip-worktree-bits Z --ignore-other-worktrees Z --recurse-submodules Z diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh index d667dda654..637a6f13a6 100755 --- a/t/t9903-bash-prompt.sh +++ b/t/t9903-bash-prompt.sh @@ -66,10 +66,6 @@ test_expect_success 'prompt - unborn branch' ' test_cmp expected "$actual" ' -if test_have_prereq !FUNNYNAMES; then - say 'Your filesystem does not allow newlines in filenames.' -fi - test_expect_success FUNNYNAMES 'prompt - with newline in path' ' repo_with_newline="repo with diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index bee4a2ca34..a28de7b19b 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -1451,9 +1451,21 @@ test_cmp_fspath () { # test_seq 1 5 -- outputs 1 2 3 4 5 one line at a time # # or with one argument (end), in which case it starts counting -# from 1. +# from 1. In addition to the start/end arguments, you can pass an optional +# printf format. For example: +# +# test_seq -f "line %d" 1 5 +# +# would print 5 lines, "line 1" through "line 5". test_seq () { + local fmt="%d" + case "$1" in + -f) + fmt="$2" + shift 2 + ;; + esac case $# in 1) set 1 "$@" ;; 2) ;; @@ -1462,7 +1474,7 @@ test_seq () { test_seq_counter__=$1 while test "$test_seq_counter__" -le "$2" do - echo "$test_seq_counter__" + printf "$fmt\n" "$test_seq_counter__" test_seq_counter__=$(( $test_seq_counter__ + 1 )) done } @@ -1695,7 +1707,7 @@ test_set_hash () { # Detect the hash algorithm in use. test_detect_hash () { - case "$GIT_TEST_DEFAULT_HASH" in + case "${GIT_TEST_DEFAULT_HASH:-$GIT_TEST_BUILTIN_HASH}" in "sha256") test_hash_algo=sha256 test_compat_hash_algo=sha1 @@ -1767,6 +1779,9 @@ test_oid () { --hash=compat) algo="$test_compat_hash_algo" && shift;; + --hash=builtin) + algo="$GIT_TEST_BUILTIN_HASH" && + shift;; --hash=*) algo="${1#--hash=}" && shift;; diff --git a/t/test-lib.sh b/t/test-lib.sh index 92d0db13d7..562f950fb0 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -127,14 +127,22 @@ then export GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS fi -# Explicitly set the default branch name for testing, to avoid the -# transitory "git init" warning under --verbose. -: ${GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME:=master} +# Explicitly set the default branch name for testing, to squelch hints +# from "git init" during the transition period. Should be removed +# after we decide to remove ADVICE_DEFAULT_BRANCH_NAME +if test -z "$WITH_BREAKING_CHANGES" +then + : ${GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME:=master} +else + : ${GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME:=main} +fi export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME + ################################################################ # It appears that people try to run tests without building... -"${GIT_TEST_INSTALLED:-$GIT_BUILD_DIR}/git$X" >/dev/null +GIT_BINARY="${GIT_TEST_INSTALLED:-$GIT_BUILD_DIR}/git$X" +"$GIT_BINARY" >/dev/null if test $? != 1 then if test -n "$GIT_TEST_INSTALLED" @@ -470,7 +478,7 @@ then then : Executed by a Bash version supporting BASH_XTRACEFD. Good. else - echo >&2 "warning: ignoring -x; '$0' is untraceable without BASH_XTRACEFD" + echo >&2 "# warning: ignoring -x; '$0' is untraceable without BASH_XTRACEFD" trace= fi fi @@ -536,7 +544,8 @@ export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME export GIT_COMMITTER_DATE GIT_AUTHOR_DATE export EDITOR -GIT_DEFAULT_HASH="${GIT_TEST_DEFAULT_HASH:-sha1}" +GIT_TEST_BUILTIN_HASH=$("$GIT_BINARY" version --build-options | sed -ne 's/^default-hash: //p') +GIT_DEFAULT_HASH="${GIT_TEST_DEFAULT_HASH:-$GIT_TEST_BUILTIN_HASH}" export GIT_DEFAULT_HASH GIT_DEFAULT_REF_FORMAT="${GIT_TEST_DEFAULT_REF_FORMAT:-files}" export GIT_DEFAULT_REF_FORMAT @@ -707,7 +716,7 @@ then exec 3>>"$GIT_TEST_TEE_OUTPUT_FILE" 4>&3 elif test "$verbose" = "t" then - exec 4>&2 3>&1 + exec 4>&2 3>&2 else exec 4>/dev/null 3>/dev/null fi @@ -949,7 +958,7 @@ maybe_setup_verbose () { test -z "$verbose_only" && return if match_pattern_list $test_count "$verbose_only" then - exec 4>&2 3>&1 + exec 4>&2 3>&2 # Emit a delimiting blank line when going from # non-verbose to verbose. Within verbose mode the # delimiter is printed by test_expect_*. The choice @@ -1272,7 +1281,14 @@ test_done () { check_test_results_san_file_ "$test_failure" - if test -z "$skip_all" && test -n "$invert_exit_code" + if test "$test_fixed" != 0 + then + if test -z "$invert_exit_code" + then + GIT_EXIT_OK=t + exit 1 + fi + elif test -z "$skip_all" && test -n "$invert_exit_code" then say_color warn "# faking up non-zero exit with --invert-exit-code" GIT_EXIT_OK=t @@ -1638,6 +1654,12 @@ fi # Fix some commands on Windows, and other OS-specific things uname_s=$(uname -s) case $uname_s in +Darwin) + test_set_prereq MACOS + test_set_prereq POSIXPERM + test_set_prereq BSLASHPSPEC + test_set_prereq EXECKEEPSPID + ;; *MINGW*) # Windows has its own (incompatible) sort and find sort () { @@ -1895,6 +1917,10 @@ test_lazy_prereq SHA1 ' esac ' +test_lazy_prereq DEFAULT_HASH_ALGORITHM ' + test "$GIT_TEST_BUILTIN_HASH" = "$GIT_DEFAULT_HASH" +' + test_lazy_prereq DEFAULT_REPO_FORMAT ' test_have_prereq SHA1,REFFILES ' diff --git a/t/unit-tests/clar/.github/workflows/ci.yml b/t/unit-tests/clar/.github/workflows/ci.yml index 0065843d17..4d4724222c 100644 --- a/t/unit-tests/clar/.github/workflows/ci.yml +++ b/t/unit-tests/clar/.github/workflows/ci.yml @@ -13,23 +13,56 @@ jobs: platform: - os: ubuntu-latest generator: Unix Makefiles + env: + CFLAGS: "-Werror -Wall -Wextra" + - os: ubuntu-latest + generator: Unix Makefiles + env: + CC: "clang" + CFLAGS: "-Werror -Wall -Wextra -fsanitize=leak" + - os: ubuntu-latest + generator: Unix Makefiles + image: i386/debian:latest + env: + CFLAGS: "-Werror -Wall -Wextra" - os: macos-latest generator: Unix Makefiles + env: + CFLAGS: "-Werror -Wall -Wextra" - os: windows-latest generator: Visual Studio 17 2022 - os: windows-latest generator: MSYS Makefiles + env: + CFLAGS: "-Werror -Wall -Wextra" - os: windows-latest generator: MinGW Makefiles + env: + CFLAGS: "-Werror -Wall -Wextra" + fail-fast: false runs-on: ${{ matrix.platform.os }} + container: ${{matrix.platform.image}} + + env: + CC: ${{matrix.platform.env.CC}} + CFLAGS: ${{matrix.platform.env.CFLAGS}} steps: + - name: Prepare 32 bit container image + if: matrix.platform.image == 'i386/debian:latest' + run: apt -q update && apt -q -y install cmake gcc libc6-amd64 lib64stdc++6 make python3 - name: Check out - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Build + shell: bash run: | mkdir build cd build cmake .. -G "${{matrix.platform.generator}}" - cmake --build . + cmake --build . --verbose + - name: Test + shell: bash + run: | + cd build + CTEST_OUTPUT_ON_FAILURE=1 ctest --build-config Debug diff --git a/t/unit-tests/clar/CMakeLists.txt b/t/unit-tests/clar/CMakeLists.txt index 12d4af114f..125db05bc1 100644 --- a/t/unit-tests/clar/CMakeLists.txt +++ b/t/unit-tests/clar/CMakeLists.txt @@ -1,8 +1,15 @@ +include(CheckFunctionExists) + cmake_minimum_required(VERSION 3.16..3.29) project(clar LANGUAGES C) -option(BUILD_TESTS "Build test executable" ON) +option(BUILD_EXAMPLE "Build the example." ON) + +check_function_exists(realpath CLAR_HAS_REALPATH) +if(CLAR_HAS_REALPATH) + add_compile_definitions(-DCLAR_HAS_REALPATH) +endif() add_library(clar INTERFACE) target_sources(clar INTERFACE @@ -25,4 +32,8 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) if(BUILD_TESTING) add_subdirectory(test) endif() + + if(BUILD_EXAMPLE) + add_subdirectory(example) + endif() endif() diff --git a/t/unit-tests/clar/README.md b/t/unit-tests/clar/README.md index a8961c5f10..41595989ca 100644 --- a/t/unit-tests/clar/README.md +++ b/t/unit-tests/clar/README.md @@ -26,8 +26,7 @@ Can you count to funk? ~~~~ sh $ mkdir tests $ cp -r $CLAR_ROOT/clar* tests - $ cp $CLAR_ROOT/test/clar_test.h tests - $ cp $CLAR_ROOT/test/main.c.sample tests/main.c + $ cp $CLAR_ROOT/example/*.c tests ~~~~ - **One: Write some tests** @@ -147,7 +146,7 @@ To use Clar: 1. copy the Clar boilerplate to your test directory 2. copy (and probably modify) the sample `main.c` (from - `$CLAR_PATH/test/main.c.sample`) + `$CLAR_PATH/example/main.c`) 3. run the Clar mixer (a.k.a. `generate.py`) to scan your test directory and write out the test suite metadata. 4. compile your test files and the Clar boilerplate into a single test @@ -159,7 +158,7 @@ The Clar boilerplate gives you a set of useful test assertions and features the `clar.c` and `clar.h` files, plus the code in the `clar/` subdirectory. You should not need to edit these files. -The sample `main.c` (i.e. `$CLAR_PATH/test/main.c.sample`) file invokes +The sample `main.c` (i.e. `$CLAR_PATH/example/main.c`) file invokes `clar_test(argc, argv)` to run the tests. Usually, you will edit this file to perform any framework specific initialization and teardown that you need. @@ -251,11 +250,16 @@ suite. - `cl_fixture(const char *)`: Gets the full path to a fixture file. -Please do note that these methods are *always* available whilst running a -test, even when calling auxiliary/static functions inside the same file. +### Auxiliary / helper functions -It's strongly encouraged to perform test assertions in auxiliary methods, -instead of returning error values. This is considered good Clar style. +The clar API is always available while running a test, even when calling +"auxiliary" (helper) functions. + +You're encouraged to perform test assertions in those auxiliary +methods, instead of returning error values. This is considered good +Clar style. _However_, when you do this, you need to call `cl_invoke` +to preserve the current state; this ensures that failures are reported +as coming from the actual test, instead of the auxiliary method. Style Example: @@ -310,20 +314,19 @@ static void check_string(const char *str) void test_example__a_test_with_auxiliary_methods(void) { - check_string("foo"); - check_string("bar"); + cl_invoke(check_string("foo")); + cl_invoke(check_string("bar")); } ~~~~ About Clar ========== -Clar has been written from scratch by [Vicent MartÃ](https://github.com/vmg), -to replace the old testing framework in [libgit2][libgit2]. - -Do you know what languages are *in* on the SF startup scene? Node.js *and* -Latin. Follow [@vmg](https://www.twitter.com/vmg) on Twitter to -receive more lessons on word etymology. You can be hip too. - +Clar was originally written by [Vicent MartÃ](https://github.com/vmg), +to replace the old testing framework in [libgit2][libgit2]. It is +currently maintained by [Edward Thomson](https://github.com/ethomson), +and used by the [libgit2][libgit2] and [git][git] projects, amongst +others. [libgit2]: https://github.com/libgit2/libgit2 +[git]: https://github.com/git/git diff --git a/t/unit-tests/clar/clar.c b/t/unit-tests/clar/clar.c index d54e455367..d6176e50b2 100644 --- a/t/unit-tests/clar/clar.c +++ b/t/unit-tests/clar/clar.c @@ -79,6 +79,8 @@ # else # define p_snprintf snprintf # endif + +# define localtime_r(timer, buf) (localtime_s(buf, timer) == 0 ? buf : NULL) #else # include <sys/wait.h> /* waitpid(2) */ # include <unistd.h> @@ -150,7 +152,6 @@ static struct { enum cl_output_format output_format; - int report_errors_only; int exit_on_error; int verbosity; @@ -164,6 +165,10 @@ static struct { struct clar_report *reports; struct clar_report *last_report; + const char *invoke_file; + const char *invoke_func; + size_t invoke_line; + void (*local_cleanup)(void *); void *local_cleanup_payload; @@ -190,7 +195,7 @@ struct clar_suite { }; /* From clar_print_*.c */ -static void clar_print_init(int test_count, int suite_count, const char *suite_names); +static void clar_print_init(int test_count, int suite_count); static void clar_print_shutdown(int test_count, int suite_count, int error_count); static void clar_print_error(int num, const struct clar_report *report, const struct clar_error *error); static void clar_print_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status failed); @@ -199,8 +204,10 @@ static void clar_print_onabortv(const char *msg, va_list argp); static void clar_print_onabort(const char *msg, ...); /* From clar_sandbox.c */ -static void clar_unsandbox(void); -static void clar_sandbox(void); +static void clar_tempdir_init(void); +static void clar_tempdir_shutdown(void); +static int clar_sandbox_create(const char *suite_name, const char *test_name); +static int clar_sandbox_cleanup(void); /* From summary.h */ static struct clar_summary *clar_summary_init(const char *filename); @@ -304,6 +311,8 @@ clar_run_test( CL_TRACE(CL_TRACE__TEST__BEGIN); + clar_sandbox_create(suite->name, test->name); + _clar.last_report->start = time(NULL); clar_time_now(&start); @@ -328,9 +337,13 @@ clar_run_test( if (_clar.local_cleanup != NULL) _clar.local_cleanup(_clar.local_cleanup_payload); + clar__clear_invokepoint(); + if (cleanup->ptr != NULL) cleanup->ptr(); + clar_sandbox_cleanup(); + CL_TRACE(CL_TRACE__TEST__END); _clar.tests_ran++; @@ -339,18 +352,14 @@ clar_run_test( _clar.local_cleanup = NULL; _clar.local_cleanup_payload = NULL; - if (_clar.report_errors_only) { - clar_report_errors(_clar.last_report); - } else { - clar_print_ontest(suite->name, test->name, _clar.tests_ran, _clar.last_report->status); - } + clar_print_ontest(suite->name, test->name, _clar.tests_ran, _clar.last_report->status); } static void clar_run_suite(const struct clar_suite *suite, const char *filter) { const struct clar_func *test = suite->tests; - size_t i, matchlen; + size_t i, matchlen = 0; struct clar_report *report; int exact = 0; @@ -360,8 +369,7 @@ clar_run_suite(const struct clar_suite *suite, const char *filter) if (_clar.exit_on_error && _clar.total_errors) return; - if (!_clar.report_errors_only) - clar_print_onsuite(suite->name, ++_clar.suites_ran); + clar_print_onsuite(suite->name, ++_clar.suites_ran); _clar.active_suite = suite->name; _clar.active_test = NULL; @@ -428,12 +436,12 @@ clar_usage(const char *arg) printf(" -iname Include the suite with `name`\n"); printf(" -xname Exclude the suite with `name`\n"); printf(" -v Increase verbosity (show suite names)\n"); - printf(" -q Only report tests that had an error\n"); + printf(" -q Decrease verbosity, inverse to -v\n"); printf(" -Q Quit as soon as a test fails\n"); printf(" -t Display results in tap format\n"); printf(" -l Print suite names\n"); printf(" -r[filename] Write summary file (to the optional filename)\n"); - exit(-1); + exit(1); } static void @@ -441,18 +449,11 @@ clar_parse_args(int argc, char **argv) { int i; - /* Verify options before execute */ for (i = 1; i < argc; ++i) { char *argument = argv[i]; - if (argument[0] != '-' || argument[1] == '\0' - || strchr("sixvqQtlr", argument[1]) == NULL) { + if (argument[0] != '-' || argument[1] == '\0') clar_usage(argv[0]); - } - } - - for (i = 1; i < argc; ++i) { - char *argument = argv[i]; switch (argument[1]) { case 's': @@ -465,8 +466,13 @@ clar_parse_args(int argc, char **argv) argument += offset; arglen = strlen(argument); - if (arglen == 0) - clar_usage(argv[0]); + if (arglen == 0) { + if (i + 1 == argc) + clar_usage(argv[0]); + + argument = argv[++i]; + arglen = strlen(argument); + } for (j = 0; j < _clar_suite_count; ++j) { suitelen = strlen(_clar_suites[j].name); @@ -483,9 +489,6 @@ clar_parse_args(int argc, char **argv) ++found; - if (!exact) - _clar.verbosity = MAX(_clar.verbosity, 1); - switch (action) { case 's': { struct clar_explicit *explicit; @@ -517,23 +520,37 @@ clar_parse_args(int argc, char **argv) if (!found) clar_abort("No suite matching '%s' found.\n", argument); + break; } case 'q': - _clar.report_errors_only = 1; + if (argument[2] != '\0') + clar_usage(argv[0]); + + _clar.verbosity--; break; case 'Q': + if (argument[2] != '\0') + clar_usage(argv[0]); + _clar.exit_on_error = 1; break; case 't': + if (argument[2] != '\0') + clar_usage(argv[0]); + _clar.output_format = CL_OUTPUT_TAP; break; case 'l': { size_t j; + + if (argument[2] != '\0') + clar_usage(argv[0]); + printf("Test suites (use -s<name> to run just one):\n"); for (j = 0; j < _clar_suite_count; ++j) printf(" %3d: %s\n", (int)j, _clar_suites[j].name); @@ -542,23 +559,27 @@ clar_parse_args(int argc, char **argv) } case 'v': + if (argument[2] != '\0') + clar_usage(argv[0]); + _clar.verbosity++; break; case 'r': _clar.write_summary = 1; free(_clar.summary_filename); + if (*(argument + 2)) { if ((_clar.summary_filename = strdup(argument + 2)) == NULL) clar_abort("Failed to allocate summary filename.\n"); } else { _clar.summary_filename = NULL; } + break; default: - clar_abort("Unexpected commandline argument '%s'.\n", - argument[1]); + clar_usage(argv[0]); } } } @@ -571,11 +592,7 @@ clar_test_init(int argc, char **argv) if (argc > 1) clar_parse_args(argc, argv); - clar_print_init( - (int)_clar_callback_count, - (int)_clar_suite_count, - "" - ); + clar_print_init((int)_clar_callback_count, (int)_clar_suite_count); if (!_clar.summary_filename && (summary_env = getenv("CLAR_SUMMARY")) != NULL) { @@ -591,7 +608,7 @@ clar_test_init(int argc, char **argv) if (_clar.write_summary) _clar.summary = clar_summary_init(_clar.summary_filename); - clar_sandbox(); + clar_tempdir_init(); } int @@ -623,7 +640,7 @@ clar_test_shutdown(void) _clar.total_errors ); - clar_unsandbox(); + clar_tempdir_shutdown(); if (_clar.write_summary && clar_summary_shutdown(_clar.summary) < 0) clar_abort("Failed to write the summary file '%s: %s.\n", @@ -635,6 +652,14 @@ clar_test_shutdown(void) } for (report = _clar.reports; report; report = report_next) { + struct clar_error *error, *error_next; + + for (error = report->errors; error; error = error_next) { + free(error->description); + error_next = error->next; + free(error); + } + report_next = report->next; free(report); } @@ -660,7 +685,7 @@ static void abort_test(void) clar_print_onabort( "Fatal error: a cleanup method raised an exception.\n"); clar_report_errors(_clar.last_report); - exit(-1); + exit(1); } CL_TRACE(CL_TRACE__TEST__LONGJMP); @@ -695,9 +720,9 @@ void clar__fail( _clar.last_report->last_error = error; - error->file = file; - error->function = function; - error->line_number = line; + error->file = _clar.invoke_file ? _clar.invoke_file : file; + error->function = _clar.invoke_func ? _clar.invoke_func : function; + error->line_number = _clar.invoke_line ? _clar.invoke_line : line; error->error_msg = error_msg; if (description != NULL && @@ -754,7 +779,12 @@ void clar__assert_equal( p_snprintf(buf, sizeof(buf), "'%s' != '%s' (at byte %d)", s1, s2, pos); } else { - p_snprintf(buf, sizeof(buf), "'%s' != '%s'", s1, s2); + const char *q1 = s1 ? "'" : ""; + const char *q2 = s2 ? "'" : ""; + s1 = s1 ? s1 : "NULL"; + s2 = s2 ? s2 : "NULL"; + p_snprintf(buf, sizeof(buf), "%s%s%s != %s%s%s", + q1, s1, q1, q2, s2, q2); } } } @@ -767,12 +797,17 @@ void clar__assert_equal( if (!is_equal) { if (s1 && s2) { int pos; - for (pos = 0; s1[pos] == s2[pos] && pos < len; ++pos) + for (pos = 0; pos < len && s1[pos] == s2[pos]; ++pos) /* find differing byte offset */; p_snprintf(buf, sizeof(buf), "'%.*s' != '%.*s' (at byte %d)", len, s1, len, s2, pos); } else { - p_snprintf(buf, sizeof(buf), "'%.*s' != '%.*s'", len, s1, len, s2); + const char *q1 = s1 ? "'" : ""; + const char *q2 = s2 ? "'" : ""; + s1 = s1 ? s1 : "NULL"; + s2 = s2 ? s2 : "NULL"; + p_snprintf(buf, sizeof(buf), "%s%.*s%s != %s%.*s%s", + q1, len, s1, q1, q2, len, s2, q2); } } } @@ -790,7 +825,12 @@ void clar__assert_equal( p_snprintf(buf, sizeof(buf), "'%ls' != '%ls' (at byte %d)", wcs1, wcs2, pos); } else { - p_snprintf(buf, sizeof(buf), "'%ls' != '%ls'", wcs1, wcs2); + const char *q1 = wcs1 ? "'" : ""; + const char *q2 = wcs2 ? "'" : ""; + wcs1 = wcs1 ? wcs1 : L"NULL"; + wcs2 = wcs2 ? wcs2 : L"NULL"; + p_snprintf(buf, sizeof(buf), "%s%ls%s != %s%ls%s", + q1, wcs1, q1, q2, wcs2, q2); } } } @@ -803,12 +843,17 @@ void clar__assert_equal( if (!is_equal) { if (wcs1 && wcs2) { int pos; - for (pos = 0; wcs1[pos] == wcs2[pos] && pos < len; ++pos) + for (pos = 0; pos < len && wcs1[pos] == wcs2[pos]; ++pos) /* find differing byte offset */; p_snprintf(buf, sizeof(buf), "'%.*ls' != '%.*ls' (at byte %d)", len, wcs1, len, wcs2, pos); } else { - p_snprintf(buf, sizeof(buf), "'%.*ls' != '%.*ls'", len, wcs1, len, wcs2); + const char *q1 = wcs1 ? "'" : ""; + const char *q2 = wcs2 ? "'" : ""; + wcs1 = wcs1 ? wcs1 : L"NULL"; + wcs2 = wcs2 ? wcs2 : L"NULL"; + p_snprintf(buf, sizeof(buf), "%s%.*ls%s != %s%.*ls%s", + q1, len, wcs1, q1, q2, len, wcs2, q2); } } } @@ -850,6 +895,23 @@ void cl_set_cleanup(void (*cleanup)(void *), void *opaque) _clar.local_cleanup_payload = opaque; } +void clar__set_invokepoint( + const char *file, + const char *func, + size_t line) +{ + _clar.invoke_file = file; + _clar.invoke_func = func; + _clar.invoke_line = line; +} + +void clar__clear_invokepoint(void) +{ + _clar.invoke_file = NULL; + _clar.invoke_func = NULL; + _clar.invoke_line = 0; +} + #include "clar/sandbox.h" #include "clar/fixtures.h" #include "clar/fs.h" diff --git a/t/unit-tests/clar/clar.h b/t/unit-tests/clar/clar.h index 8c22382bd5..ca72292ae9 100644 --- a/t/unit-tests/clar/clar.h +++ b/t/unit-tests/clar/clar.h @@ -8,6 +8,25 @@ #define __CLAR_TEST_H__ #include <stdlib.h> +#include <limits.h> + +#if defined(_WIN32) && defined(CLAR_WIN32_LONGPATHS) +# define CLAR_MAX_PATH 4096 +#elif defined(_WIN32) +# define CLAR_MAX_PATH MAX_PATH +#else +# define CLAR_MAX_PATH PATH_MAX +#endif + +#ifndef CLAR_SELFTEST +# define CLAR_CURRENT_FILE __FILE__ +# define CLAR_CURRENT_LINE __LINE__ +# define CLAR_CURRENT_FUNC __func__ +#else +# define CLAR_CURRENT_FILE "file" +# define CLAR_CURRENT_LINE 42 +# define CLAR_CURRENT_FUNC "func" +#endif enum cl_test_status { CL_TEST_OK, @@ -30,6 +49,7 @@ void clar_test_shutdown(void); int clar_test(int argc, char *argv[]); const char *clar_sandbox_path(void); +const char *clar_tempdir_path(void); void cl_set_cleanup(void (*cleanup)(void *), void *opaque); void cl_fs_cleanup(void); @@ -84,18 +104,32 @@ const char *cl_fixture_basename(const char *fixture_name); #endif /** + * Invoke a helper function, which itself will use `cl_assert` + * constructs. This will preserve the stack information of the + * current call point, so that function name and line number + * information is shown from the line of the test, instead of + * the helper function. + */ +#define cl_invoke(expr) \ + do { \ + clar__set_invokepoint(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE); \ + expr; \ + clar__clear_invokepoint(); \ + } while(0) + +/** * Assertion macros with explicit error message */ -#define cl_must_pass_(expr, desc) clar__assert((expr) >= 0, __FILE__, __func__, __LINE__, "Function call failed: " #expr, desc, 1) -#define cl_must_fail_(expr, desc) clar__assert((expr) < 0, __FILE__, __func__, __LINE__, "Expected function call to fail: " #expr, desc, 1) -#define cl_assert_(expr, desc) clar__assert((expr) != 0, __FILE__, __func__, __LINE__, "Expression is not true: " #expr, desc, 1) +#define cl_must_pass_(expr, desc) clar__assert((expr) >= 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Function call failed: " #expr, desc, 1) +#define cl_must_fail_(expr, desc) clar__assert((expr) < 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Expected function call to fail: " #expr, desc, 1) +#define cl_assert_(expr, desc) clar__assert((expr) != 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Expression is not true: " #expr, desc, 1) /** * Check macros with explicit error message */ -#define cl_check_pass_(expr, desc) clar__assert((expr) >= 0, __FILE__, __func__, __LINE__, "Function call failed: " #expr, desc, 0) -#define cl_check_fail_(expr, desc) clar__assert((expr) < 0, __FILE__, __func__, __LINE__, "Expected function call to fail: " #expr, desc, 0) -#define cl_check_(expr, desc) clar__assert((expr) != 0, __FILE__, __func__, __LINE__, "Expression is not true: " #expr, desc, 0) +#define cl_check_pass_(expr, desc) clar__assert((expr) >= 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Function call failed: " #expr, desc, 0) +#define cl_check_fail_(expr, desc) clar__assert((expr) < 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Expected function call to fail: " #expr, desc, 0) +#define cl_check_(expr, desc) clar__assert((expr) != 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Expression is not true: " #expr, desc, 0) /** * Assertion macros with no error message @@ -114,33 +148,33 @@ const char *cl_fixture_basename(const char *fixture_name); /** * Forced failure/warning */ -#define cl_fail(desc) clar__fail(__FILE__, __func__, __LINE__, "Test failed.", desc, 1) -#define cl_warning(desc) clar__fail(__FILE__, __func__, __LINE__, "Warning during test execution:", desc, 0) +#define cl_fail(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Test failed.", desc, 1) +#define cl_warning(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Warning during test execution:", desc, 0) #define cl_skip() clar__skip() /** * Typed assertion macros */ -#define cl_assert_equal_s(s1,s2) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2)) -#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2)) +#define cl_assert_equal_s(s1,s2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2)) +#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2)) -#define cl_assert_equal_wcs(wcs1,wcs2) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%ls", (wcs1), (wcs2)) -#define cl_assert_equal_wcs_(wcs1,wcs2,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%ls", (wcs1), (wcs2)) +#define cl_assert_equal_wcs(wcs1,wcs2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2, 1, "%ls", (wcs1), (wcs2)) +#define cl_assert_equal_wcs_(wcs1,wcs2,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%ls", (wcs1), (wcs2)) -#define cl_assert_equal_strn(s1,s2,len) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%.*s", (s1), (s2), (int)(len)) -#define cl_assert_equal_strn_(s1,s2,len,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%.*s", (s1), (s2), (int)(len)) +#define cl_assert_equal_strn(s1,s2,len) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #s1 " != " #s2, 1, "%.*s", (s1), (s2), (int)(len)) +#define cl_assert_equal_strn_(s1,s2,len,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%.*s", (s1), (s2), (int)(len)) -#define cl_assert_equal_wcsn(wcs1,wcs2,len) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%.*ls", (wcs1), (wcs2), (int)(len)) -#define cl_assert_equal_wcsn_(wcs1,wcs2,len,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%.*ls", (wcs1), (wcs2), (int)(len)) +#define cl_assert_equal_wcsn(wcs1,wcs2,len) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2, 1, "%.*ls", (wcs1), (wcs2), (int)(len)) +#define cl_assert_equal_wcsn_(wcs1,wcs2,len,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%.*ls", (wcs1), (wcs2), (int)(len)) -#define cl_assert_equal_i(i1,i2) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2)) -#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2)) -#define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2)) +#define cl_assert_equal_i(i1,i2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2)) +#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2)) +#define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2)) -#define cl_assert_equal_b(b1,b2) clar__assert_equal(__FILE__,__func__,__LINE__,#b1 " != " #b2, 1, "%d", (int)((b1) != 0),(int)((b2) != 0)) +#define cl_assert_equal_b(b1,b2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#b1 " != " #b2, 1, "%d", (int)((b1) != 0),(int)((b2) != 0)) -#define cl_assert_equal_p(p1,p2) clar__assert_equal(__FILE__,__func__,__LINE__,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2)) +#define cl_assert_equal_p(p1,p2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2)) void clar__skip(void); @@ -170,4 +204,11 @@ void clar__assert_equal( const char *fmt, ...); +void clar__set_invokepoint( + const char *file, + const char *func, + size_t line); + +void clar__clear_invokepoint(void); + #endif diff --git a/t/unit-tests/clar/clar/fixtures.h b/t/unit-tests/clar/clar/fixtures.h index 6ec6423484..9f1023df59 100644 --- a/t/unit-tests/clar/clar/fixtures.h +++ b/t/unit-tests/clar/clar/fixtures.h @@ -2,7 +2,7 @@ static const char * fixture_path(const char *base, const char *fixture_name) { - static char _path[4096]; + static char _path[CLAR_MAX_PATH]; size_t root_len; root_len = strlen(base); @@ -28,7 +28,7 @@ const char *cl_fixture(const char *fixture_name) void cl_fixture_sandbox(const char *fixture_name) { - fs_copy(cl_fixture(fixture_name), _clar_path); + fs_copy(cl_fixture(fixture_name), clar_sandbox_path()); } const char *cl_fixture_basename(const char *fixture_name) @@ -45,6 +45,6 @@ const char *cl_fixture_basename(const char *fixture_name) void cl_fixture_cleanup(const char *fixture_name) { - fs_rm(fixture_path(_clar_path, cl_fixture_basename(fixture_name))); + fs_rm(fixture_path(clar_sandbox_path(), cl_fixture_basename(fixture_name))); } #endif diff --git a/t/unit-tests/clar/clar/fs.h b/t/unit-tests/clar/clar/fs.h index 2203743fb4..f1311d91e8 100644 --- a/t/unit-tests/clar/clar/fs.h +++ b/t/unit-tests/clar/clar/fs.h @@ -8,12 +8,6 @@ #ifdef _WIN32 -#ifdef CLAR_WIN32_LONGPATHS -# define CLAR_MAX_PATH 4096 -#else -# define CLAR_MAX_PATH MAX_PATH -#endif - #define RM_RETRY_COUNT 5 #define RM_RETRY_DELAY 10 @@ -296,7 +290,7 @@ void cl_fs_cleanup(void) { #ifdef CLAR_FIXTURE_PATH - fs_rm(fixture_path(_clar_path, "*")); + fs_rm(fixture_path(clar_tempdir_path(), "*")); #else ((void)fs_copy); /* unused */ #endif @@ -371,17 +365,19 @@ static void fs_copydir_helper(const char *source, const char *dest, int dest_mode) { DIR *source_dir; - struct dirent *d; mkdir(dest, dest_mode); cl_assert_(source_dir = opendir(source), "Could not open source dir"); - for (;;) { + while (1) { + struct dirent *d; char *child; errno = 0; - if ((d = readdir(source_dir)) == NULL) + d = readdir(source_dir); + if (!d) break; + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) continue; @@ -479,15 +475,18 @@ static void fs_rmdir_helper(const char *path) { DIR *dir; - struct dirent *d; cl_assert_(dir = opendir(path), "Could not open dir"); - for (;;) { + + while (1) { + struct dirent *d; char *child; errno = 0; - if ((d = readdir(dir)) == NULL) + d = readdir(dir); + if (!d) break; + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) continue; @@ -524,7 +523,7 @@ fs_rm(const char *path) void cl_fs_cleanup(void) { - clar_unsandbox(); - clar_sandbox(); + clar_tempdir_shutdown(); + clar_tempdir_init(); } #endif diff --git a/t/unit-tests/clar/clar/print.h b/t/unit-tests/clar/clar/print.h index 69d0ee967e..89b66591d7 100644 --- a/t/unit-tests/clar/clar/print.h +++ b/t/unit-tests/clar/clar/print.h @@ -1,9 +1,13 @@ /* clap: clar protocol, the traditional clar output format */ -static void clar_print_clap_init(int test_count, int suite_count, const char *suite_names) +static void clar_print_clap_init(int test_count, int suite_count) { (void)test_count; - printf("Loaded %d suites: %s\n", (int)suite_count, suite_names); + + if (_clar.verbosity < 0) + return; + + printf("Loaded %d suites:\n", (int)suite_count); printf("Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n"); } @@ -13,10 +17,27 @@ static void clar_print_clap_shutdown(int test_count, int suite_count, int error_ (void)suite_count; (void)error_count; - printf("\n\n"); + if (_clar.verbosity >= 0) + printf("\n\n"); clar_report_all(); } + +static void clar_print_indented(const char *str, int indent) +{ + const char *bol, *eol; + + for (bol = str; *bol; bol = eol) { + eol = strchr(bol, '\n'); + if (eol) + eol++; + else + eol = bol + strlen(bol); + printf("%*s%.*s", indent, "", (int)(eol - bol), bol); + } + putc('\n', stdout); +} + static void clar_print_clap_error(int num, const struct clar_report *report, const struct clar_error *error) { printf(" %d) Failure:\n", num); @@ -27,10 +48,10 @@ static void clar_print_clap_error(int num, const struct clar_report *report, con error->file, error->line_number); - printf(" %s\n", error->error_msg); + clar_print_indented(error->error_msg, 2); if (error->description != NULL) - printf(" %s\n", error->description); + clar_print_indented(error->description, 2); printf("\n"); fflush(stdout); @@ -41,14 +62,17 @@ static void clar_print_clap_ontest(const char *suite_name, const char *test_name (void)test_name; (void)test_number; + if (_clar.verbosity < 0) + return; + if (_clar.verbosity > 1) { printf("%s::%s: ", suite_name, test_name); switch (status) { case CL_TEST_OK: printf("ok\n"); break; case CL_TEST_FAILURE: printf("fail\n"); break; - case CL_TEST_SKIP: printf("skipped"); break; - case CL_TEST_NOTRUN: printf("notrun"); break; + case CL_TEST_SKIP: printf("skipped\n"); break; + case CL_TEST_NOTRUN: printf("notrun\n"); break; } } else { switch (status) { @@ -64,6 +88,8 @@ static void clar_print_clap_ontest(const char *suite_name, const char *test_name static void clar_print_clap_onsuite(const char *suite_name, int suite_index) { + if (_clar.verbosity < 0) + return; if (_clar.verbosity == 1) printf("\n%s", suite_name); @@ -77,11 +103,10 @@ static void clar_print_clap_onabort(const char *fmt, va_list arg) /* tap: test anywhere protocol format */ -static void clar_print_tap_init(int test_count, int suite_count, const char *suite_names) +static void clar_print_tap_init(int test_count, int suite_count) { (void)test_count; (void)suite_count; - (void)suite_names; printf("TAP version 13\n"); } @@ -127,18 +152,20 @@ static void clar_print_tap_ontest(const char *suite_name, const char *test_name, case CL_TEST_FAILURE: printf("not ok %d - %s::%s\n", test_number, suite_name, test_name); - printf(" ---\n"); - printf(" reason: |\n"); - printf(" %s\n", error->error_msg); + if (_clar.verbosity >= 0) { + printf(" ---\n"); + printf(" reason: |\n"); + clar_print_indented(error->error_msg, 6); - if (error->description) - printf(" %s\n", error->description); + if (error->description) + clar_print_indented(error->description, 6); - printf(" at:\n"); - printf(" file: '"); print_escaped(error->file); printf("'\n"); - printf(" line: %" PRIuMAX "\n", error->line_number); - printf(" function: '%s'\n", error->function); - printf(" ---\n"); + printf(" at:\n"); + printf(" file: '"); print_escaped(error->file); printf("'\n"); + printf(" line: %" PRIuMAX "\n", error->line_number); + printf(" function: '%s'\n", error->function); + printf(" ---\n"); + } break; case CL_TEST_SKIP: @@ -152,6 +179,8 @@ static void clar_print_tap_ontest(const char *suite_name, const char *test_name, static void clar_print_tap_onsuite(const char *suite_name, int suite_index) { + if (_clar.verbosity < 0) + return; printf("# start of suite %d: %s\n", suite_index, suite_name); } @@ -177,9 +206,9 @@ static void clar_print_tap_onabort(const char *fmt, va_list arg) } \ } while (0) -static void clar_print_init(int test_count, int suite_count, const char *suite_names) +static void clar_print_init(int test_count, int suite_count) { - PRINT(init, test_count, suite_count, suite_names); + PRINT(init, test_count, suite_count); } static void clar_print_shutdown(int test_count, int suite_count, int error_count) diff --git a/t/unit-tests/clar/clar/sandbox.h b/t/unit-tests/clar/clar/sandbox.h index bc960f50e0..52add8aceb 100644 --- a/t/unit-tests/clar/clar/sandbox.h +++ b/t/unit-tests/clar/clar/sandbox.h @@ -2,7 +2,17 @@ #include <sys/syslimits.h> #endif -static char _clar_path[4096 + 1]; +/* + * The tempdir is the temporary directory for the entirety of the clar + * process execution. The sandbox is an individual temporary directory + * for the execution of an individual test. Sandboxes are deleted + * entirely after test execution to avoid pollution across tests. + */ + +static char _clar_tempdir[CLAR_MAX_PATH]; +static size_t _clar_tempdir_len; + +static char _clar_sandbox[CLAR_MAX_PATH]; static int is_valid_tmp_path(const char *path) @@ -15,7 +25,10 @@ is_valid_tmp_path(const char *path) if (!S_ISDIR(st.st_mode)) return 0; - return (access(path, W_OK) == 0); + if (access(path, W_OK) != 0) + return 0; + + return (strlen(path) < CLAR_MAX_PATH); } static int @@ -31,14 +44,11 @@ find_tmp_path(char *buffer, size_t length) for (i = 0; i < var_count; ++i) { const char *env = getenv(env_vars[i]); + if (!env) continue; if (is_valid_tmp_path(env)) { -#ifdef __APPLE__ - if (length >= PATH_MAX && realpath(env, buffer) != NULL) - return 0; -#endif strncpy(buffer, env, length - 1); buffer[length - 1] = '\0'; return 0; @@ -47,21 +57,18 @@ find_tmp_path(char *buffer, size_t length) /* If the environment doesn't say anything, try to use /tmp */ if (is_valid_tmp_path("/tmp")) { -#ifdef __APPLE__ - if (length >= PATH_MAX && realpath("/tmp", buffer) != NULL) - return 0; -#endif strncpy(buffer, "/tmp", length - 1); buffer[length - 1] = '\0'; return 0; } #else - DWORD env_len = GetEnvironmentVariable("CLAR_TMP", buffer, (DWORD)length); - if (env_len > 0 && env_len < (DWORD)length) + DWORD len = GetEnvironmentVariable("CLAR_TMP", buffer, (DWORD)length); + if (len > 0 && len < (DWORD)length) return 0; - if (GetTempPath((DWORD)length, buffer)) + len = GetTempPath((DWORD)length, buffer); + if (len > 0 && len < (DWORD)length) return 0; #endif @@ -75,17 +82,53 @@ find_tmp_path(char *buffer, size_t length) return -1; } -static void clar_unsandbox(void) +static int canonicalize_tmp_path(char *buffer) +{ +#ifdef _WIN32 + char tmp[CLAR_MAX_PATH], *p; + DWORD ret; + + ret = GetFullPathName(buffer, CLAR_MAX_PATH, tmp, NULL); + + if (ret == 0 || ret > CLAR_MAX_PATH) + return -1; + + ret = GetLongPathName(tmp, buffer, CLAR_MAX_PATH); + + if (ret == 0 || ret > CLAR_MAX_PATH) + return -1; + + /* normalize path to POSIX forward slashes */ + for (p = buffer; *p; p++) + if (*p == '\\') + *p = '/'; + + return 0; +#elif defined(CLAR_HAS_REALPATH) + char tmp[CLAR_MAX_PATH]; + + if (realpath(buffer, tmp) == NULL) + return -1; + + strcpy(buffer, tmp); + return 0; +#else + (void)buffer; + return 0; +#endif +} + +static void clar_tempdir_shutdown(void) { - if (_clar_path[0] == '\0') + if (_clar_tempdir[0] == '\0') return; cl_must_pass(chdir("..")); - fs_rm(_clar_path); + fs_rm(_clar_tempdir); } -static int build_sandbox_path(void) +static int build_tempdir_path(void) { #ifdef CLAR_TMPDIR const char path_tail[] = CLAR_TMPDIR "_XXXXXX"; @@ -95,64 +138,153 @@ static int build_sandbox_path(void) size_t len; - if (find_tmp_path(_clar_path, sizeof(_clar_path)) < 0) + if (find_tmp_path(_clar_tempdir, sizeof(_clar_tempdir)) < 0 || + canonicalize_tmp_path(_clar_tempdir) < 0) return -1; - len = strlen(_clar_path); + len = strlen(_clar_tempdir); -#ifdef _WIN32 - { /* normalize path to POSIX forward slashes */ - size_t i; - for (i = 0; i < len; ++i) { - if (_clar_path[i] == '\\') - _clar_path[i] = '/'; - } - } -#endif + if (len + strlen(path_tail) + 2 > CLAR_MAX_PATH) + return -1; - if (_clar_path[len - 1] != '/') { - _clar_path[len++] = '/'; - } + if (_clar_tempdir[len - 1] != '/') + _clar_tempdir[len++] = '/'; - strncpy(_clar_path + len, path_tail, sizeof(_clar_path) - len); + strncpy(_clar_tempdir + len, path_tail, sizeof(_clar_tempdir) - len); #if defined(__MINGW32__) - if (_mktemp(_clar_path) == NULL) + if (_mktemp(_clar_tempdir) == NULL) return -1; - if (mkdir(_clar_path, 0700) != 0) + if (mkdir(_clar_tempdir, 0700) != 0) return -1; #elif defined(_WIN32) - if (_mktemp_s(_clar_path, sizeof(_clar_path)) != 0) + if (_mktemp_s(_clar_tempdir, sizeof(_clar_tempdir)) != 0) return -1; - if (mkdir(_clar_path, 0700) != 0) + if (mkdir(_clar_tempdir, 0700) != 0) return -1; -#elif defined(__sun) || defined(__TANDEM) - if (mktemp(_clar_path) == NULL) +#elif defined(__sun) || defined(__TANDEM) || defined(__hpux) + if (mktemp(_clar_tempdir) == NULL) return -1; - if (mkdir(_clar_path, 0700) != 0) + if (mkdir(_clar_tempdir, 0700) != 0) return -1; #else - if (mkdtemp(_clar_path) == NULL) + if (mkdtemp(_clar_tempdir) == NULL) return -1; #endif + _clar_tempdir_len = strlen(_clar_tempdir); return 0; } -static void clar_sandbox(void) +static void clar_tempdir_init(void) { - if (_clar_path[0] == '\0' && build_sandbox_path() < 0) - clar_abort("Failed to build sandbox path.\n"); + if (_clar_tempdir[0] == '\0' && build_tempdir_path() < 0) + clar_abort("Failed to build tempdir path.\n"); - if (chdir(_clar_path) != 0) - clar_abort("Failed to change into sandbox directory '%s': %s.\n", - _clar_path, strerror(errno)); + if (chdir(_clar_tempdir) != 0) + clar_abort("Failed to change into tempdir '%s': %s.\n", + _clar_tempdir, strerror(errno)); + +#if !defined(CLAR_SANDBOX_TEST_NAMES) && defined(_WIN32) + srand(clock() ^ (unsigned int)time(NULL) ^ GetCurrentProcessId() ^ GetCurrentThreadId()); +#elif !defined(CLAR_SANDBOX_TEST_NAMES) + srand(clock() ^ time(NULL) ^ ((unsigned)getpid() << 16)); +#endif +} + +static void append(char *dst, const char *src) +{ + char *d; + const char *s; + + for (d = dst; *d; d++) + ; + + for (s = src; *s; d++, s++) + if (*s == ':') + *d = '_'; + else + *d = *s; + + *d = '\0'; +} + +static int clar_sandbox_create(const char *suite_name, const char *test_name) +{ +#ifndef CLAR_SANDBOX_TEST_NAMES + char alpha[] = "0123456789abcdef"; + int num = rand(); +#endif + + cl_assert(_clar_sandbox[0] == '\0'); + + /* + * We may want to use test names as sandbox directory names for + * readability, _however_ on platforms with restrictions for short + * file / folder names (eg, Windows), this may be too long. + */ +#ifdef CLAR_SANDBOX_TEST_NAMES + cl_assert(strlen(_clar_tempdir) + strlen(suite_name) + strlen(test_name) + 3 < CLAR_MAX_PATH); + + strcpy(_clar_sandbox, _clar_tempdir); + _clar_sandbox[_clar_tempdir_len] = '/'; + _clar_sandbox[_clar_tempdir_len + 1] = '\0'; + + append(_clar_sandbox, suite_name); + append(_clar_sandbox, "__"); + append(_clar_sandbox, test_name); +#else + ((void)suite_name); + ((void)test_name); + ((void)append); + + cl_assert(strlen(_clar_tempdir) + 9 < CLAR_MAX_PATH); + + strcpy(_clar_sandbox, _clar_tempdir); + _clar_sandbox[_clar_tempdir_len] = '/'; + + _clar_sandbox[_clar_tempdir_len + 1] = alpha[(num & 0xf0000000) >> 28]; + _clar_sandbox[_clar_tempdir_len + 2] = alpha[(num & 0x0f000000) >> 24]; + _clar_sandbox[_clar_tempdir_len + 3] = alpha[(num & 0x00f00000) >> 20]; + _clar_sandbox[_clar_tempdir_len + 4] = alpha[(num & 0x000f0000) >> 16]; + _clar_sandbox[_clar_tempdir_len + 5] = alpha[(num & 0x0000f000) >> 12]; + _clar_sandbox[_clar_tempdir_len + 6] = alpha[(num & 0x00000f00) >> 8]; + _clar_sandbox[_clar_tempdir_len + 7] = alpha[(num & 0x000000f0) >> 4]; + _clar_sandbox[_clar_tempdir_len + 8] = alpha[(num & 0x0000000f) >> 0]; + _clar_sandbox[_clar_tempdir_len + 9] = '\0'; +#endif + + if (mkdir(_clar_sandbox, 0700) != 0) + return -1; + + if (chdir(_clar_sandbox) != 0) + return -1; + + return 0; +} + +static int clar_sandbox_cleanup(void) +{ + cl_assert(_clar_sandbox[0] != '\0'); + + if (chdir(_clar_tempdir) != 0) + return -1; + + fs_rm(_clar_sandbox); + _clar_sandbox[0] = '\0'; + + return 0; +} + +const char *clar_tempdir_path(void) +{ + return _clar_tempdir; } const char *clar_sandbox_path(void) { - return _clar_path; + return _clar_sandbox; } diff --git a/t/unit-tests/clar/clar/summary.h b/t/unit-tests/clar/clar/summary.h index 0d0b646fe7..7b85f162d8 100644 --- a/t/unit-tests/clar/clar/summary.h +++ b/t/unit-tests/clar/clar/summary.h @@ -23,10 +23,11 @@ static int clar_summary_testsuite(struct clar_summary *summary, int idn, const char *name, time_t timestamp, int test_count, int fail_count, int error_count) { - struct tm *tm = localtime(×tamp); + struct tm tm; char iso_dt[20]; - if (strftime(iso_dt, sizeof(iso_dt), "%Y-%m-%dT%H:%M:%S", tm) == 0) + localtime_r(×tamp, &tm); + if (strftime(iso_dt, sizeof(iso_dt), "%Y-%m-%dT%H:%M:%S", &tm) == 0) return -1; return fprintf(summary->fp, "\t<testsuite" diff --git a/t/unit-tests/clar/example/CMakeLists.txt b/t/unit-tests/clar/example/CMakeLists.txt new file mode 100644 index 0000000000..b72f187523 --- /dev/null +++ b/t/unit-tests/clar/example/CMakeLists.txt @@ -0,0 +1,28 @@ +find_package(Python COMPONENTS Interpreter REQUIRED) + +add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/clar.suite" + COMMAND "${Python_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/generate.py" --output "${CMAKE_CURRENT_BINARY_DIR}" + DEPENDS main.c example.c + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" +) + +add_executable(example) +set_target_properties(example PROPERTIES + C_STANDARD 90 + C_STANDARD_REQUIRED ON + C_EXTENSIONS OFF +) +target_sources(example PRIVATE + main.c + example.c + "${CMAKE_CURRENT_BINARY_DIR}/clar.suite" +) +target_compile_definitions(example PRIVATE) +target_compile_options(example PRIVATE + $<IF:$<CXX_COMPILER_ID:MSVC>,/W4,-Wall> +) +target_include_directories(example PRIVATE + "${CMAKE_SOURCE_DIR}" + "${CMAKE_CURRENT_BINARY_DIR}" +) +target_link_libraries(example clar) diff --git a/t/unit-tests/clar/example/example.c b/t/unit-tests/clar/example/example.c new file mode 100644 index 0000000000..c07d6bf68e --- /dev/null +++ b/t/unit-tests/clar/example/example.c @@ -0,0 +1,6 @@ +#include "clar.h" + +void test_example__simple_assert(void) +{ + cl_assert_equal_i(1, 1); +} diff --git a/t/unit-tests/clar/test/main.c.sample b/t/unit-tests/clar/example/main.c index a4d91b72fa..f8def7fa6e 100644 --- a/t/unit-tests/clar/test/main.c.sample +++ b/t/unit-tests/clar/example/main.c @@ -5,7 +5,7 @@ * For full terms see the included COPYING file. */ -#include "clar_test.h" +#include "clar.h" /* * Minimal main() for clar tests. diff --git a/t/unit-tests/clar/generate.py b/t/unit-tests/clar/generate.py index 80996ac3e7..fd2f0ee83b 100755 --- a/t/unit-tests/clar/generate.py +++ b/t/unit-tests/clar/generate.py @@ -158,17 +158,24 @@ class TestSuite(object): def find_modules(self): modules = [] - for root, _, files in os.walk(self.path): - module_root = root[len(self.path):] - module_root = [c for c in module_root.split(os.sep) if c] - tests_in_module = fnmatch.filter(files, "*.c") + if os.path.isfile(self.path): + full_path = os.path.abspath(self.path) + module_name = os.path.basename(self.path) + module_name = os.path.splitext(module_name)[0] + modules.append((full_path, module_name)) + else: + for root, _, files in os.walk(self.path): + module_root = root[len(self.path):] + module_root = [c for c in module_root.split(os.sep) if c] - for test_file in tests_in_module: - full_path = os.path.join(root, test_file) - module_name = "_".join(module_root + [test_file[:-2]]).replace("-", "_") + tests_in_module = fnmatch.filter(files, "*.c") - modules.append((full_path, module_name)) + for test_file in tests_in_module: + full_path = os.path.join(root, test_file) + module_name = "_".join(module_root + [test_file[:-2]]).replace("-", "_") + + modules.append((full_path, module_name)) return modules @@ -217,6 +224,7 @@ class TestSuite(object): def write(self): output = os.path.join(self.output, 'clar.suite') + os.makedirs(self.output, exist_ok=True) if not self.should_generate(output): return False @@ -258,7 +266,11 @@ if __name__ == '__main__': sys.exit(1) path = args.pop() if args else '.' + if os.path.isfile(path) and not options.output: + print("Must provide --output when specifying a file") + sys.exit(1) output = options.output or path + suite = TestSuite(path, output) suite.load(options.force) suite.disable(options.excluded) diff --git a/t/unit-tests/clar/test/CMakeLists.txt b/t/unit-tests/clar/test/CMakeLists.txt index 7f2c1dc17a..f240166439 100644 --- a/t/unit-tests/clar/test/CMakeLists.txt +++ b/t/unit-tests/clar/test/CMakeLists.txt @@ -2,12 +2,12 @@ find_package(Python COMPONENTS Interpreter REQUIRED) add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/clar.suite" COMMAND "${Python_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/generate.py" --output "${CMAKE_CURRENT_BINARY_DIR}" - DEPENDS main.c sample.c clar_test.h + DEPENDS main.c selftest.c WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" ) -add_executable(clar_test) -set_target_properties(clar_test PROPERTIES +add_executable(selftest) +set_target_properties(selftest PROPERTIES C_STANDARD 90 C_STANDARD_REQUIRED ON C_EXTENSIONS OFF @@ -15,25 +15,35 @@ set_target_properties(clar_test PROPERTIES # MSVC generates all kinds of warnings. We may want to fix these in the future # and then unconditionally treat warnings as errors. -if(NOT MSVC) - set_target_properties(clar_test PROPERTIES +if (NOT MSVC) + set_target_properties(selftest PROPERTIES COMPILE_WARNING_AS_ERROR ON ) endif() -target_sources(clar_test PRIVATE +target_sources(selftest PRIVATE main.c - sample.c + selftest.c "${CMAKE_CURRENT_BINARY_DIR}/clar.suite" ) -target_compile_definitions(clar_test PRIVATE - CLAR_FIXTURE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/resources/" +target_compile_definitions(selftest PRIVATE + CLAR_FIXTURE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/expected/" ) -target_compile_options(clar_test PRIVATE +target_compile_options(selftest PRIVATE $<IF:$<CXX_COMPILER_ID:MSVC>,/W4,-Wall> ) -target_include_directories(clar_test PRIVATE +target_include_directories(selftest PRIVATE "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}" ) -target_link_libraries(clar_test clar) +target_link_libraries(selftest clar) + +add_test(NAME build_selftest + COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --config "$<CONFIG>" --target selftest +) +set_tests_properties(build_selftest PROPERTIES FIXTURES_SETUP clar_test_fixture) + +add_subdirectory(suites) + +add_test(NAME selftest COMMAND "${CMAKE_CURRENT_BINARY_DIR}/selftest" $<TARGET_FILE_DIR:combined_suite>) +set_tests_properties(selftest PROPERTIES FIXTURES_REQUIRED clar_test_fixture) diff --git a/t/unit-tests/clar/test/clar_test.h b/t/unit-tests/clar/test/clar_test.h deleted file mode 100644 index 0fcaa639aa..0000000000 --- a/t/unit-tests/clar/test/clar_test.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (c) Vicent Marti. All rights reserved. - * - * This file is part of clar, distributed under the ISC license. - * For full terms see the included COPYING file. - */ -#ifndef __CLAR_TEST__ -#define __CLAR_TEST__ - -/* Import the standard clar helper functions */ -#include "clar.h" - -/* Your custom shared includes / defines here */ -extern int global_test_counter; - -#endif diff --git a/t/unit-tests/clar/test/expected/help b/t/unit-tests/clar/test/expected/help new file mode 100644 index 0000000000..9428def2d7 --- /dev/null +++ b/t/unit-tests/clar/test/expected/help @@ -0,0 +1,12 @@ +Usage: combined [options] + +Options: + -sname Run only the suite with `name` (can go to individual test name) + -iname Include the suite with `name` + -xname Exclude the suite with `name` + -v Increase verbosity (show suite names) + -q Decrease verbosity, inverse to -v + -Q Quit as soon as a test fails + -t Display results in tap format + -l Print suite names + -r[filename] Write summary file (to the optional filename) diff --git a/t/unit-tests/clar/test/expected/quiet b/t/unit-tests/clar/test/expected/quiet new file mode 100644 index 0000000000..280c99d8ad --- /dev/null +++ b/t/unit-tests/clar/test/expected/quiet @@ -0,0 +1,44 @@ + 1) Failure: +combined::1 [file:42] + Function call failed: -1 + + 2) Failure: +combined::2 [file:42] + Expression is not true: 100 == 101 + + 3) Failure: +combined::strings [file:42] + String mismatch: "mismatched" != actual ("this one fails") + 'mismatched' != 'expected' (at byte 0) + + 4) Failure: +combined::strings_with_length [file:42] + String mismatch: "exactly" != actual ("this one fails") + 'exa' != 'exp' (at byte 2) + + 5) Failure: +combined::int [file:42] + 101 != value ("extra note on failing test") + 101 != 100 + + 6) Failure: +combined::int_fmt [file:42] + 022 != value + 0022 != 0144 + + 7) Failure: +combined::bool [file:42] + 0 != value + 0 != 1 + + 8) Failure: +combined::multiline_description [file:42] + Function call failed: -1 + description line 1 + description line 2 + + 9) Failure: +combined::null_string [file:42] + String mismatch: "expected" != actual ("this one fails") + 'expected' != NULL + diff --git a/t/unit-tests/clar/test/expected/specific_test b/t/unit-tests/clar/test/expected/specific_test new file mode 100644 index 0000000000..6c22e9f507 --- /dev/null +++ b/t/unit-tests/clar/test/expected/specific_test @@ -0,0 +1,9 @@ +Loaded 1 suites: +Started (test status codes: OK='.' FAILURE='F' SKIPPED='S') +F + + 1) Failure: +combined::bool [file:42] + 0 != value + 0 != 1 + diff --git a/t/unit-tests/clar/test/expected/stop_on_failure b/t/unit-tests/clar/test/expected/stop_on_failure new file mode 100644 index 0000000000..c23610754f --- /dev/null +++ b/t/unit-tests/clar/test/expected/stop_on_failure @@ -0,0 +1,8 @@ +Loaded 1 suites: +Started (test status codes: OK='.' FAILURE='F' SKIPPED='S') +F + + 1) Failure: +combined::1 [file:42] + Function call failed: -1 + diff --git a/t/unit-tests/clar/test/expected/suite_names b/t/unit-tests/clar/test/expected/suite_names new file mode 100644 index 0000000000..10d1538427 --- /dev/null +++ b/t/unit-tests/clar/test/expected/suite_names @@ -0,0 +1,2 @@ +Test suites (use -s<name> to run just one): + 0: combined diff --git a/t/unit-tests/clar/test/expected/summary.xml b/t/unit-tests/clar/test/expected/summary.xml new file mode 100644 index 0000000000..9a89d43a59 --- /dev/null +++ b/t/unit-tests/clar/test/expected/summary.xml @@ -0,0 +1,41 @@ +<testsuites> + <testsuite id="0" name="selftest" hostname="localhost" timestamp="2024-09-06T10:04:08" tests="8" failures="8" errors="0"> + <testcase name="1" classname="selftest" time="0.00"> + <failure type="assert"><![CDATA[Function call failed: -1 +(null)]]></failure> + </testcase> + <testcase name="2" classname="selftest" time="0.00"> + <failure type="assert"><![CDATA[Expression is not true: 100 == 101 +(null)]]></failure> + </testcase> + <testcase name="strings" classname="selftest" time="0.00"> + <failure type="assert"><![CDATA[String mismatch: "mismatched" != actual ("this one fails") +'mismatched' != 'expected' (at byte 0)]]></failure> + </testcase> + <testcase name="strings_with_length" classname="selftest" time="0.00"> + <failure type="assert"><![CDATA[String mismatch: "exactly" != actual ("this one fails") +'exa' != 'exp' (at byte 2)]]></failure> + </testcase> + <testcase name="int" classname="selftest" time="0.00"> + <failure type="assert"><![CDATA[101 != value ("extra note on failing test") +101 != 100]]></failure> + </testcase> + <testcase name="int_fmt" classname="selftest" time="0.00"> + <failure type="assert"><![CDATA[022 != value +0022 != 0144]]></failure> + </testcase> + <testcase name="bool" classname="selftest" time="0.00"> + <failure type="assert"><![CDATA[0 != value +0 != 1]]></failure> + </testcase> + <testcase name="multiline_description" classname="selftest" time="0.00"> + <failure type="assert"><![CDATA[Function call failed: −1 +description line 1 +description line 2]]></failure> + </testcase> + <testcase name="null_string" classname="selftest" time="0.00"> + <failure type="assert"><![CDATA[String mismatch: "expected" != actual ("this one fails") +'expected' != NULL]]></failure> + </testcase> + </testsuite> +</testsuites> diff --git a/t/unit-tests/clar/test/expected/summary_with_filename b/t/unit-tests/clar/test/expected/summary_with_filename new file mode 100644 index 0000000000..460160791d --- /dev/null +++ b/t/unit-tests/clar/test/expected/summary_with_filename @@ -0,0 +1,49 @@ +Loaded 1 suites: +Started (test status codes: OK='.' FAILURE='F' SKIPPED='S') +FFFFFFFFF + + 1) Failure: +combined::1 [file:42] + Function call failed: -1 + + 2) Failure: +combined::2 [file:42] + Expression is not true: 100 == 101 + + 3) Failure: +combined::strings [file:42] + String mismatch: "mismatched" != actual ("this one fails") + 'mismatched' != 'expected' (at byte 0) + + 4) Failure: +combined::strings_with_length [file:42] + String mismatch: "exactly" != actual ("this one fails") + 'exa' != 'exp' (at byte 2) + + 5) Failure: +combined::int [file:42] + 101 != value ("extra note on failing test") + 101 != 100 + + 6) Failure: +combined::int_fmt [file:42] + 022 != value + 0022 != 0144 + + 7) Failure: +combined::bool [file:42] + 0 != value + 0 != 1 + + 8) Failure: +combined::multiline_description [file:42] + Function call failed: -1 + description line 1 + description line 2 + + 9) Failure: +combined::null_string [file:42] + String mismatch: "expected" != actual ("this one fails") + 'expected' != NULL + +written summary file to different.xml diff --git a/t/unit-tests/clar/test/expected/summary_without_filename b/t/unit-tests/clar/test/expected/summary_without_filename new file mode 100644 index 0000000000..7874c1d98b --- /dev/null +++ b/t/unit-tests/clar/test/expected/summary_without_filename @@ -0,0 +1,49 @@ +Loaded 1 suites: +Started (test status codes: OK='.' FAILURE='F' SKIPPED='S') +FFFFFFFFF + + 1) Failure: +combined::1 [file:42] + Function call failed: -1 + + 2) Failure: +combined::2 [file:42] + Expression is not true: 100 == 101 + + 3) Failure: +combined::strings [file:42] + String mismatch: "mismatched" != actual ("this one fails") + 'mismatched' != 'expected' (at byte 0) + + 4) Failure: +combined::strings_with_length [file:42] + String mismatch: "exactly" != actual ("this one fails") + 'exa' != 'exp' (at byte 2) + + 5) Failure: +combined::int [file:42] + 101 != value ("extra note on failing test") + 101 != 100 + + 6) Failure: +combined::int_fmt [file:42] + 022 != value + 0022 != 0144 + + 7) Failure: +combined::bool [file:42] + 0 != value + 0 != 1 + + 8) Failure: +combined::multiline_description [file:42] + Function call failed: -1 + description line 1 + description line 2 + + 9) Failure: +combined::null_string [file:42] + String mismatch: "expected" != actual ("this one fails") + 'expected' != NULL + +written summary file to summary.xml diff --git a/t/unit-tests/clar/test/expected/tap b/t/unit-tests/clar/test/expected/tap new file mode 100644 index 0000000000..bddbd5dfe9 --- /dev/null +++ b/t/unit-tests/clar/test/expected/tap @@ -0,0 +1,92 @@ +TAP version 13 +# start of suite 1: combined +not ok 1 - combined::1 + --- + reason: | + Function call failed: -1 + at: + file: 'file' + line: 42 + function: 'func' + --- +not ok 2 - combined::2 + --- + reason: | + Expression is not true: 100 == 101 + at: + file: 'file' + line: 42 + function: 'func' + --- +not ok 3 - combined::strings + --- + reason: | + String mismatch: "mismatched" != actual ("this one fails") + 'mismatched' != 'expected' (at byte 0) + at: + file: 'file' + line: 42 + function: 'func' + --- +not ok 4 - combined::strings_with_length + --- + reason: | + String mismatch: "exactly" != actual ("this one fails") + 'exa' != 'exp' (at byte 2) + at: + file: 'file' + line: 42 + function: 'func' + --- +not ok 5 - combined::int + --- + reason: | + 101 != value ("extra note on failing test") + 101 != 100 + at: + file: 'file' + line: 42 + function: 'func' + --- +not ok 6 - combined::int_fmt + --- + reason: | + 022 != value + 0022 != 0144 + at: + file: 'file' + line: 42 + function: 'func' + --- +not ok 7 - combined::bool + --- + reason: | + 0 != value + 0 != 1 + at: + file: 'file' + line: 42 + function: 'func' + --- +not ok 8 - combined::multiline_description + --- + reason: | + Function call failed: -1 + description line 1 + description line 2 + at: + file: 'file' + line: 42 + function: 'func' + --- +not ok 9 - combined::null_string + --- + reason: | + String mismatch: "expected" != actual ("this one fails") + 'expected' != NULL + at: + file: 'file' + line: 42 + function: 'func' + --- +1..9 diff --git a/t/unit-tests/clar/test/expected/without_arguments b/t/unit-tests/clar/test/expected/without_arguments new file mode 100644 index 0000000000..1111d418a0 --- /dev/null +++ b/t/unit-tests/clar/test/expected/without_arguments @@ -0,0 +1,48 @@ +Loaded 1 suites: +Started (test status codes: OK='.' FAILURE='F' SKIPPED='S') +FFFFFFFFF + + 1) Failure: +combined::1 [file:42] + Function call failed: -1 + + 2) Failure: +combined::2 [file:42] + Expression is not true: 100 == 101 + + 3) Failure: +combined::strings [file:42] + String mismatch: "mismatched" != actual ("this one fails") + 'mismatched' != 'expected' (at byte 0) + + 4) Failure: +combined::strings_with_length [file:42] + String mismatch: "exactly" != actual ("this one fails") + 'exa' != 'exp' (at byte 2) + + 5) Failure: +combined::int [file:42] + 101 != value ("extra note on failing test") + 101 != 100 + + 6) Failure: +combined::int_fmt [file:42] + 022 != value + 0022 != 0144 + + 7) Failure: +combined::bool [file:42] + 0 != value + 0 != 1 + + 8) Failure: +combined::multiline_description [file:42] + Function call failed: -1 + description line 1 + description line 2 + + 9) Failure: +combined::null_string [file:42] + String mismatch: "expected" != actual ("this one fails") + 'expected' != NULL + diff --git a/t/unit-tests/clar/test/main.c b/t/unit-tests/clar/test/main.c index 59e56ad255..94af440643 100644 --- a/t/unit-tests/clar/test/main.c +++ b/t/unit-tests/clar/test/main.c @@ -1,23 +1,9 @@ -/* - * Copyright (c) Vicent Marti. All rights reserved. - * - * This file is part of clar, distributed under the ISC license. - * For full terms see the included COPYING file. - */ +#include <stdio.h> +#include <string.h> -#include "clar_test.h" +#include "selftest.h" -/* - * Sample main() for clar tests. - * - * You should write your own main routine for clar tests that does specific - * setup and teardown as necessary for your application. The only required - * line is the call to `clar_test(argc, argv)`, which will execute the test - * suite. If you want to check the return value of the test application, - * your main() should return the same value returned by clar_test(). - */ - -int global_test_counter = 0; +const char *selftest_suite_directory; #ifdef _WIN32 int __cdecl main(int argc, char *argv[]) @@ -25,16 +11,15 @@ int __cdecl main(int argc, char *argv[]) int main(int argc, char *argv[]) #endif { - int ret; - - /* Your custom initialization here */ - global_test_counter = 0; - - /* Run the test suite */ - ret = clar_test(argc, argv); + if (argc < 2) { + fprintf(stderr, "usage: %s <selftest-suite-directory> <options>\n", + argv[0]); + exit(1); + } - /* Your custom cleanup here */ - cl_assert_equal_i(8, global_test_counter); + selftest_suite_directory = argv[1]; + memmove(argv + 1, argv + 2, argc - 1); + argc -= 1; - return ret; + return clar_test(argc, argv); } diff --git a/t/unit-tests/clar/test/selftest.c b/t/unit-tests/clar/test/selftest.c new file mode 100644 index 0000000000..eed83e4512 --- /dev/null +++ b/t/unit-tests/clar/test/selftest.c @@ -0,0 +1,370 @@ +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> + +#include "selftest.h" + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include <windows.h> + +static char *read_full(HANDLE h, int is_pipe) +{ + char *data = NULL; + size_t data_size = 0; + + while (1) { + CHAR buf[4096]; + DWORD bytes_read; + + if (!ReadFile(h, buf, sizeof(buf), &bytes_read, NULL)) { + if (!is_pipe) + cl_fail("Failed reading file handle."); + cl_assert_equal_i(GetLastError(), ERROR_BROKEN_PIPE); + break; + } + if (!bytes_read) + break; + + data = realloc(data, data_size + bytes_read); + cl_assert(data); + memcpy(data + data_size, buf, bytes_read); + data_size += bytes_read; + } + + data = realloc(data, data_size + 1); + cl_assert(data); + data[data_size] = '\0'; + + while (strstr(data, "\r\n")) { + char *ptr = strstr(data, "\r\n"); + memmove(ptr, ptr + 1, strlen(ptr)); + } + + return data; +} + +static char *read_file(const char *path) +{ + char *content; + HANDLE file; + + file = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + cl_assert(file != INVALID_HANDLE_VALUE); + content = read_full(file, 0); + cl_assert_equal_b(1, CloseHandle(file)); + + return content; +} + +static char *execute(const char *suite, int expected_error_code, const char **args, size_t nargs) +{ + SECURITY_ATTRIBUTES security_attributes = { 0 }; + PROCESS_INFORMATION process_info = { 0 }; + STARTUPINFO startup_info = { 0 }; + char binary_path[4096] = { 0 }; + char cmdline[4096] = { 0 }; + char *output = NULL; + HANDLE stdout_write; + HANDLE stdout_read; + DWORD exit_code; + size_t i; + + snprintf(binary_path, sizeof(binary_path), "%s/%s_suite.exe", + selftest_suite_directory, suite); + + /* + * Assemble command line arguments. In theory we'd have to properly + * quote them. In practice none of our tests actually care. + */ + snprintf(cmdline, sizeof(cmdline), suite); + for (i = 0; i < nargs; i++) { + size_t cmdline_len = strlen(cmdline); + const char *arg = args[i]; + cl_assert(cmdline_len + strlen(arg) < sizeof(cmdline)); + snprintf(cmdline + cmdline_len, sizeof(cmdline) - cmdline_len, + " %s", arg); + } + + /* + * Create a pipe that we will use to read data from the child process. + * The writing side needs to be inheritable such that the child can use + * it as stdout and stderr. The reading side should only be used by the + * parent. + */ + security_attributes.nLength = sizeof(security_attributes); + security_attributes.bInheritHandle = TRUE; + cl_assert_equal_b(1, CreatePipe(&stdout_read, &stdout_write, &security_attributes, 0)); + cl_assert_equal_b(1, SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0)); + + /* + * Create the child process with our pipe. + */ + startup_info.cb = sizeof(startup_info); + startup_info.hStdError = stdout_write; + startup_info.hStdOutput = stdout_write; + startup_info.dwFlags |= STARTF_USESTDHANDLES; + cl_assert_equal_b(1, CreateProcess(binary_path, cmdline, NULL, NULL, TRUE, + 0, NULL, NULL, &startup_info, &process_info)); + cl_assert_equal_b(1, CloseHandle(stdout_write)); + + output = read_full(stdout_read, 1); + cl_assert_equal_b(1, CloseHandle(stdout_read)); + cl_assert_equal_b(1, GetExitCodeProcess(process_info.hProcess, &exit_code)); + cl_assert_equal_i(exit_code, expected_error_code); + + return output; +} + +static void assert_output(const char *suite, const char *expected_output_file, int expected_error_code, ...) +{ + char *expected_output = NULL; + char *output = NULL; + const char *args[16]; + va_list ap; + size_t i; + + va_start(ap, expected_error_code); + for (i = 0; ; i++) { + const char *arg = va_arg(ap, const char *); + if (!arg) + break; + cl_assert(i < sizeof(args) / sizeof(*args)); + args[i] = arg; + } + va_end(ap); + + output = execute(suite, expected_error_code, args, i); + expected_output = read_file(cl_fixture(expected_output_file)); + cl_assert_equal_s(output, expected_output); + + free(expected_output); + free(output); +} + +#else +# include <errno.h> +# include <fcntl.h> +# include <limits.h> +# include <unistd.h> +# include <sys/wait.h> + +static char *read_full(int fd) +{ + size_t data_bytes = 0; + char *data = NULL; + + while (1) { + char buf[4096]; + ssize_t n; + + n = read(fd, buf, sizeof(buf)); + if (n < 0) { + if (errno == EAGAIN || errno == EINTR) + continue; + cl_fail("Failed reading from child process."); + } + if (!n) + break; + + data = realloc(data, data_bytes + n); + cl_assert(data); + + memcpy(data + data_bytes, buf, n); + data_bytes += n; + } + + data = realloc(data, data_bytes + 1); + cl_assert(data); + data[data_bytes] = '\0'; + + return data; +} + +static char *read_file(const char *path) +{ + char *data; + int fd; + + fd = open(path, O_RDONLY); + if (fd < 0) + cl_fail("Failed reading expected file."); + + data = read_full(fd); + cl_must_pass(close(fd)); + + return data; +} + +static char *execute(const char *suite, int expected_error_code, const char **args, size_t nargs) +{ + int pipe_fds[2]; + pid_t pid; + + cl_must_pass(pipe(pipe_fds)); + + pid = fork(); + if (!pid) { + const char *final_args[17] = { NULL }; + char binary_path[4096]; + size_t len = 0; + size_t i; + + cl_assert(nargs < sizeof(final_args) / sizeof(*final_args)); + final_args[0] = suite; + for (i = 0; i < nargs; i++) + final_args[i + 1] = args[i]; + + if (dup2(pipe_fds[1], STDOUT_FILENO) < 0 || + dup2(pipe_fds[1], STDERR_FILENO) < 0 || + close(0) < 0 || + close(pipe_fds[0]) < 0 || + close(pipe_fds[1]) < 0) + exit(1); + + cl_assert(len + strlen(selftest_suite_directory) < sizeof(binary_path)); + strcpy(binary_path, selftest_suite_directory); + len += strlen(selftest_suite_directory); + + cl_assert(len + 1 < sizeof(binary_path)); + binary_path[len] = '/'; + len += 1; + + cl_assert(len + strlen(suite) < sizeof(binary_path)); + strcpy(binary_path + len, suite); + len += strlen(suite); + + cl_assert(len + strlen("_suite") < sizeof(binary_path)); + strcpy(binary_path + len, "_suite"); + len += strlen("_suite"); + + binary_path[len] = '\0'; + + execv(binary_path, (char **) final_args); + exit(1); + } else if (pid > 0) { + pid_t waited_pid; + char *output; + int stat; + + cl_must_pass(close(pipe_fds[1])); + + output = read_full(pipe_fds[0]); + + waited_pid = waitpid(pid, &stat, 0); + cl_assert_equal_i(pid, waited_pid); + cl_assert(WIFEXITED(stat)); + cl_assert_equal_i(WEXITSTATUS(stat), expected_error_code); + + return output; + } else { + cl_fail("Fork failed."); + } + + return NULL; +} + +static void assert_output(const char *suite, const char *expected_output_file, int expected_error_code, ...) +{ + char *expected_output, *output; + const char *args[16]; + va_list ap; + size_t i; + + va_start(ap, expected_error_code); + for (i = 0; ; i++) { + cl_assert(i < sizeof(args) / sizeof(*args)); + args[i] = va_arg(ap, const char *); + if (!args[i]) + break; + } + va_end(ap); + + output = execute(suite, expected_error_code, args, i); + expected_output = read_file(cl_fixture(expected_output_file)); + cl_assert_equal_s(output, expected_output); + + free(expected_output); + free(output); +} +#endif + +void test_selftest__help(void) +{ + cl_invoke(assert_output("combined", "help", 1, "-h", NULL)); +} + +void test_selftest__without_arguments(void) +{ + cl_invoke(assert_output("combined", "without_arguments", 9, NULL)); +} + +void test_selftest__specific_test(void) +{ + cl_invoke(assert_output("combined", "specific_test", 1, "-scombined::bool", NULL)); +} + +void test_selftest__stop_on_failure(void) +{ + cl_invoke(assert_output("combined", "stop_on_failure", 1, "-Q", NULL)); +} + +void test_selftest__quiet(void) +{ + cl_invoke(assert_output("combined", "quiet", 9, "-q", NULL)); +} + +void test_selftest__tap(void) +{ + cl_invoke(assert_output("combined", "tap", 9, "-t", NULL)); +} + +void test_selftest__suite_names(void) +{ + cl_invoke(assert_output("combined", "suite_names", 0, "-l", NULL)); +} + +void test_selftest__summary_without_filename(void) +{ + struct stat st; + cl_invoke(assert_output("combined", "summary_without_filename", 9, "-r", NULL)); + /* The summary contains timestamps, so we cannot verify its contents. */ + cl_must_pass(stat("summary.xml", &st)); +} + +void test_selftest__summary_with_filename(void) +{ + struct stat st; + cl_invoke(assert_output("combined", "summary_with_filename", 9, "-rdifferent.xml", NULL)); + /* The summary contains timestamps, so we cannot verify its contents. */ + cl_must_pass(stat("different.xml", &st)); +} + +void test_selftest__pointer_equal(void) +{ + const char *args[] = { + "-spointer::equal", + "-t" + }; + char *output = execute("pointer", 0, args, 2); + cl_assert_equal_s(output, + "TAP version 13\n" + "# start of suite 1: pointer\n" + "ok 1 - pointer::equal\n" + "1..1\n" + ); + free(output); +} + +void test_selftest__pointer_unequal(void) +{ + const char *args[] = { + "-spointer::unequal", + }; + char *output = execute("pointer", 1, args, 1); + cl_assert(output); + cl_assert(strstr(output, "Pointer mismatch: ")); + free(output); +} diff --git a/t/unit-tests/clar/test/selftest.h b/t/unit-tests/clar/test/selftest.h new file mode 100644 index 0000000000..c24e0c5af4 --- /dev/null +++ b/t/unit-tests/clar/test/selftest.h @@ -0,0 +1,3 @@ +#include "clar.h" + +extern const char *selftest_suite_directory; diff --git a/t/unit-tests/clar/test/suites/CMakeLists.txt b/t/unit-tests/clar/test/suites/CMakeLists.txt new file mode 100644 index 0000000000..fa8ab9416a --- /dev/null +++ b/t/unit-tests/clar/test/suites/CMakeLists.txt @@ -0,0 +1,53 @@ +list(APPEND suites + "combined" + "pointer" +) + +foreach(suite IN LISTS suites) + add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${suite}/clar.suite" + COMMAND "${Python_EXECUTABLE}" + "${CMAKE_SOURCE_DIR}/generate.py" + "${CMAKE_CURRENT_SOURCE_DIR}/${suite}.c" + --output "${CMAKE_CURRENT_BINARY_DIR}/${suite}" + DEPENDS ${suite}.c + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + ) + + add_executable(${suite}_suite) + set_target_properties(${suite}_suite PROPERTIES + C_STANDARD 90 + C_STANDARD_REQUIRED ON + C_EXTENSIONS OFF + ) + + # MSVC generates all kinds of warnings. We may want to fix these in the future + # and then unconditionally treat warnings as errors. + if(NOT MSVC) + set_target_properties(${suite}_suite PROPERTIES + COMPILE_WARNING_AS_ERROR ON + ) + endif() + + target_sources(${suite}_suite PRIVATE + main.c + ${suite}.c + "${CMAKE_CURRENT_BINARY_DIR}/${suite}/clar.suite" + ) + target_compile_definitions(${suite}_suite PRIVATE + CLAR_FIXTURE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/resources/" + CLAR_SELFTEST + ) + target_compile_options(${suite}_suite PRIVATE + $<IF:$<CXX_COMPILER_ID:MSVC>,/W4,-Wall> + ) + target_include_directories(${suite}_suite PRIVATE + "${CMAKE_SOURCE_DIR}" + "${CMAKE_CURRENT_BINARY_DIR}/${suite}" + ) + target_link_libraries(${suite}_suite clar) + + add_test(NAME build_${suite}_suite + COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --config "$<CONFIG>" --target selftest + ) + set_tests_properties(build_${suite}_suite PROPERTIES FIXTURES_SETUP clar_test_fixture) +endforeach() diff --git a/t/unit-tests/clar/test/sample.c b/t/unit-tests/clar/test/suites/combined.c index faa1209262..e8b41c98c3 100644 --- a/t/unit-tests/clar/test/sample.c +++ b/t/unit-tests/clar/test/suites/combined.c @@ -1,6 +1,7 @@ -#include "clar_test.h" #include <sys/stat.h> +#include "clar.h" + static int file_size(const char *filename) { struct stat st; @@ -10,19 +11,14 @@ static int file_size(const char *filename) return -1; } -void test_sample__initialize(void) -{ - global_test_counter++; -} - -void test_sample__cleanup(void) +void test_combined__cleanup(void) { cl_fixture_cleanup("test"); cl_assert(file_size("test/file") == -1); } -void test_sample__1(void) +void test_combined__1(void) { cl_assert(1); cl_must_pass(0); /* 0 == success */ @@ -30,7 +26,7 @@ void test_sample__1(void) cl_must_pass(-1); /* demonstrate a failing call */ } -void test_sample__2(void) +void test_combined__2(void) { cl_fixture_sandbox("test"); @@ -39,7 +35,7 @@ void test_sample__2(void) cl_assert(100 == 101); } -void test_sample__strings(void) +void test_combined__strings(void) { const char *actual = "expected"; cl_assert_equal_s("expected", actual); @@ -47,7 +43,7 @@ void test_sample__strings(void) cl_assert_equal_s_("mismatched", actual, "this one fails"); } -void test_sample__strings_with_length(void) +void test_combined__strings_with_length(void) { const char *actual = "expected"; cl_assert_equal_strn("expected_", actual, 8); @@ -56,29 +52,34 @@ void test_sample__strings_with_length(void) cl_assert_equal_strn_("exactly", actual, 3, "this one fails"); } -void test_sample__int(void) +void test_combined__int(void) { int value = 100; cl_assert_equal_i(100, value); cl_assert_equal_i_(101, value, "extra note on failing test"); } -void test_sample__int_fmt(void) +void test_combined__int_fmt(void) { int value = 100; cl_assert_equal_i_fmt(022, value, "%04o"); } -void test_sample__bool(void) +void test_combined__bool(void) { int value = 100; cl_assert_equal_b(1, value); /* test equality as booleans */ cl_assert_equal_b(0, value); } -void test_sample__ptr(void) +void test_combined__multiline_description(void) { - const char *actual = "expected"; - cl_assert_equal_p(actual, actual); /* pointers to same object */ - cl_assert_equal_p(&actual, actual); + cl_must_pass_(-1, "description line 1\ndescription line 2"); +} + +void test_combined__null_string(void) +{ + const char *actual = NULL; + cl_assert_equal_s(actual, actual); + cl_assert_equal_s_("expected", actual, "this one fails"); } diff --git a/t/unit-tests/clar/test/suites/main.c b/t/unit-tests/clar/test/suites/main.c new file mode 100644 index 0000000000..3ab581d390 --- /dev/null +++ b/t/unit-tests/clar/test/suites/main.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) Vicent Marti. All rights reserved. + * + * This file is part of clar, distributed under the ISC license. + * For full terms see the included COPYING file. + */ + +#include "clar.h" + +/* + * Selftest main() for clar tests. + * + * You should write your own main routine for clar tests that does specific + * setup and teardown as necessary for your application. The only required + * line is the call to `clar_test(argc, argv)`, which will execute the test + * suite. If you want to check the return value of the test application, + * your main() should return the same value returned by clar_test(). + */ + +#ifdef _WIN32 +int __cdecl main(int argc, char *argv[]) +#else +int main(int argc, char *argv[]) +#endif +{ + return clar_test(argc, argv); +} diff --git a/t/unit-tests/clar/test/suites/pointer.c b/t/unit-tests/clar/test/suites/pointer.c new file mode 100644 index 0000000000..20535b159e --- /dev/null +++ b/t/unit-tests/clar/test/suites/pointer.c @@ -0,0 +1,13 @@ +#include "clar.h" + +void test_pointer__equal(void) +{ + void *p1 = (void *)0x1; + cl_assert_equal_p(p1, p1); +} + +void test_pointer__unequal(void) +{ + void *p1 = (void *)0x1, *p2 = (void *)0x2; + cl_assert_equal_p(p1, p2); +} diff --git a/t/unit-tests/clar/test/resources/test/file b/t/unit-tests/clar/test/suites/resources/test/file index 220f4aa98a..220f4aa98a 100644 --- a/t/unit-tests/clar/test/resources/test/file +++ b/t/unit-tests/clar/test/suites/resources/test/file diff --git a/t/unit-tests/lib-reftable.c b/t/unit-tests/lib-reftable.c index 8a69612266..fdb5b11a20 100644 --- a/t/unit-tests/lib-reftable.c +++ b/t/unit-tests/lib-reftable.c @@ -1,12 +1,14 @@ -#define DISABLE_SIGN_COMPARE_WARNINGS - +#include "unit-test.h" #include "lib-reftable.h" -#include "test-lib.h" +#include "hex.h" +#include "parse-options.h" #include "reftable/constants.h" #include "reftable/writer.h" #include "strbuf.h" +#include "string-list.h" +#include "strvec.h" -void t_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id) +void cl_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id) { memset(p, (uint8_t)i, hash_size(id)); } @@ -22,17 +24,17 @@ static int strbuf_writer_flush(void *arg UNUSED) return 0; } -struct reftable_writer *t_reftable_strbuf_writer(struct reftable_buf *buf, +struct reftable_writer *cl_reftable_strbuf_writer(struct reftable_buf *buf, struct reftable_write_options *opts) { struct reftable_writer *writer; int ret = reftable_writer_new(&writer, &strbuf_writer_write, &strbuf_writer_flush, buf, opts); - check(!ret); + cl_assert(!ret); return writer; } -void t_reftable_write_to_buf(struct reftable_buf *buf, +void cl_reftable_write_to_buf(struct reftable_buf *buf, struct reftable_ref_record *refs, size_t nrefs, struct reftable_log_record *logs, @@ -64,35 +66,36 @@ void t_reftable_write_to_buf(struct reftable_buf *buf, min = ui; } - writer = t_reftable_strbuf_writer(buf, &opts); - reftable_writer_set_limits(writer, min, max); + writer = cl_reftable_strbuf_writer(buf, &opts); + ret = reftable_writer_set_limits(writer, min, max); + cl_assert(!ret); if (nrefs) { ret = reftable_writer_add_refs(writer, refs, nrefs); - check_int(ret, ==, 0); + cl_assert_equal_i(ret, 0); } if (nlogs) { ret = reftable_writer_add_logs(writer, logs, nlogs); - check_int(ret, ==, 0); + cl_assert_equal_i(ret, 0); } ret = reftable_writer_close(writer); - check_int(ret, ==, 0); + cl_assert_equal_i(ret, 0); stats = reftable_writer_stats(writer); - for (size_t i = 0; i < stats->ref_stats.blocks; i++) { + for (size_t i = 0; i < (size_t)stats->ref_stats.blocks; i++) { size_t off = i * (opts.block_size ? opts.block_size : DEFAULT_BLOCK_SIZE); if (!off) off = header_size(opts.hash_id == REFTABLE_HASH_SHA256 ? 2 : 1); - check_char(buf->buf[off], ==, 'r'); + cl_assert(buf->buf[off] == 'r'); } if (nrefs) - check_int(stats->ref_stats.blocks, >, 0); + cl_assert(stats->ref_stats.blocks > 0); if (nlogs) - check_int(stats->log_stats.blocks, >, 0); + cl_assert(stats->log_stats.blocks > 0); reftable_writer_free(writer); } diff --git a/t/unit-tests/lib-reftable.h b/t/unit-tests/lib-reftable.h index e4c360fa7e..d7e6d3136f 100644 --- a/t/unit-tests/lib-reftable.h +++ b/t/unit-tests/lib-reftable.h @@ -1,21 +1,20 @@ -#ifndef LIB_REFTABLE_H -#define LIB_REFTABLE_H - +#include "git-compat-util.h" +#include "clar/clar.h" +#include "clar-decls.h" #include "git-compat-util.h" #include "reftable/reftable-writer.h" +#include "strbuf.h" struct reftable_buf; -void t_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id); +void cl_reftable_set_hash(uint8_t *p, int i, enum reftable_hash id); -struct reftable_writer *t_reftable_strbuf_writer(struct reftable_buf *buf, +struct reftable_writer *cl_reftable_strbuf_writer(struct reftable_buf *buf, struct reftable_write_options *opts); -void t_reftable_write_to_buf(struct reftable_buf *buf, +void cl_reftable_write_to_buf(struct reftable_buf *buf, struct reftable_ref_record *refs, size_t nrecords, struct reftable_log_record *logs, size_t nlogs, struct reftable_write_options *opts); - -#endif diff --git a/t/unit-tests/t-reftable-basics.c b/t/unit-tests/t-reftable-basics.c deleted file mode 100644 index c9e751e49e..0000000000 --- a/t/unit-tests/t-reftable-basics.c +++ /dev/null @@ -1,219 +0,0 @@ -/* -Copyright 2020 Google LLC - -Use of this source code is governed by a BSD-style -license that can be found in the LICENSE file or at -https://developers.google.com/open-source/licenses/bsd -*/ - -#include "test-lib.h" -#include "reftable/basics.h" - -struct integer_needle_lesseq_args { - int needle; - int *haystack; -}; - -static int integer_needle_lesseq(size_t i, void *_args) -{ - struct integer_needle_lesseq_args *args = _args; - return args->needle <= args->haystack[i]; -} - -static void *realloc_stub(void *p UNUSED, size_t size UNUSED) -{ - return NULL; -} - -int cmd_main(int argc UNUSED, const char *argv[] UNUSED) -{ - if_test ("binary search with binsearch works") { - int haystack[] = { 2, 4, 6, 8, 10 }; - struct { - int needle; - size_t expected_idx; - } testcases[] = { - {-9000, 0}, - {-1, 0}, - {0, 0}, - {2, 0}, - {3, 1}, - {4, 1}, - {7, 3}, - {9, 4}, - {10, 4}, - {11, 5}, - {9000, 5}, - }; - - for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) { - struct integer_needle_lesseq_args args = { - .haystack = haystack, - .needle = testcases[i].needle, - }; - size_t idx; - - idx = binsearch(ARRAY_SIZE(haystack), - &integer_needle_lesseq, &args); - check_int(idx, ==, testcases[i].expected_idx); - } - } - - if_test ("names_length returns size of a NULL-terminated string array") { - const char *a[] = { "a", "b", NULL }; - check_int(names_length(a), ==, 2); - } - - if_test ("names_equal compares NULL-terminated string arrays") { - const char *a[] = { "a", "b", "c", NULL }; - const char *b[] = { "a", "b", "d", NULL }; - const char *c[] = { "a", "b", NULL }; - - check(names_equal(a, a)); - check(!names_equal(a, b)); - check(!names_equal(a, c)); - } - - if_test ("parse_names works for basic input") { - char in1[] = "line\n"; - char in2[] = "a\nb\nc"; - char **out = parse_names(in1, strlen(in1)); - check(out != NULL); - check_str(out[0], "line"); - check(!out[1]); - free_names(out); - - out = parse_names(in2, strlen(in2)); - check(out != NULL); - check_str(out[0], "a"); - check_str(out[1], "b"); - check_str(out[2], "c"); - check(!out[3]); - free_names(out); - } - - if_test ("parse_names drops empty string") { - char in[] = "a\n\nb\n"; - char **out = parse_names(in, strlen(in)); - check(out != NULL); - check_str(out[0], "a"); - /* simply '\n' should be dropped as empty string */ - check_str(out[1], "b"); - check(!out[2]); - free_names(out); - } - - if_test ("common_prefix_size works") { - struct reftable_buf a = REFTABLE_BUF_INIT; - struct reftable_buf b = REFTABLE_BUF_INIT; - struct { - const char *a, *b; - int want; - } cases[] = { - {"abcdef", "abc", 3}, - { "abc", "ab", 2 }, - { "", "abc", 0 }, - { "abc", "abd", 2 }, - { "abc", "pqr", 0 }, - }; - - for (size_t i = 0; i < ARRAY_SIZE(cases); i++) { - check(!reftable_buf_addstr(&a, cases[i].a)); - check(!reftable_buf_addstr(&b, cases[i].b)); - check_uint(common_prefix_size(&a, &b), ==, cases[i].want); - reftable_buf_reset(&a); - reftable_buf_reset(&b); - } - reftable_buf_release(&a); - reftable_buf_release(&b); - } - - if_test ("reftable_put_be64 and reftable_get_be64 work") { - uint64_t in = 0x1122334455667788; - uint8_t dest[8]; - uint64_t out; - reftable_put_be64(dest, in); - out = reftable_get_be64(dest); - check_int(in, ==, out); - } - - if_test ("reftable_put_be32 and reftable_get_be32 work") { - uint32_t in = 0x11223344; - uint8_t dest[4]; - uint32_t out; - reftable_put_be32(dest, in); - out = reftable_get_be32(dest); - check_int(in, ==, out); - } - - if_test ("reftable_put_be24 and reftable_get_be24 work") { - uint32_t in = 0x112233; - uint8_t dest[3]; - uint32_t out; - reftable_put_be24(dest, in); - out = reftable_get_be24(dest); - check_int(in, ==, out); - } - - if_test ("put_be16 and get_be16 work") { - uint32_t in = 0xfef1; - uint8_t dest[3]; - uint32_t out; - reftable_put_be16(dest, in); - out = reftable_get_be16(dest); - check_int(in, ==, out); - } - - if_test ("REFTABLE_ALLOC_GROW works") { - int *arr = NULL, *old_arr; - size_t alloc = 0, old_alloc; - - check(!REFTABLE_ALLOC_GROW(arr, 1, alloc)); - check(arr != NULL); - check_uint(alloc, >=, 1); - arr[0] = 42; - - old_alloc = alloc; - old_arr = arr; - reftable_set_alloc(NULL, realloc_stub, NULL); - check(REFTABLE_ALLOC_GROW(arr, old_alloc + 1, alloc)); - check(arr == old_arr); - check_uint(alloc, ==, old_alloc); - - old_alloc = alloc; - reftable_set_alloc(NULL, NULL, NULL); - check(!REFTABLE_ALLOC_GROW(arr, old_alloc + 1, alloc)); - check(arr != NULL); - check_uint(alloc, >, old_alloc); - arr[alloc - 1] = 42; - - reftable_free(arr); - } - - if_test ("REFTABLE_ALLOC_GROW_OR_NULL works") { - int *arr = NULL; - size_t alloc = 0, old_alloc; - - REFTABLE_ALLOC_GROW_OR_NULL(arr, 1, alloc); - check(arr != NULL); - check_uint(alloc, >=, 1); - arr[0] = 42; - - old_alloc = alloc; - REFTABLE_ALLOC_GROW_OR_NULL(arr, old_alloc + 1, alloc); - check(arr != NULL); - check_uint(alloc, >, old_alloc); - arr[alloc - 1] = 42; - - old_alloc = alloc; - reftable_set_alloc(NULL, realloc_stub, NULL); - REFTABLE_ALLOC_GROW_OR_NULL(arr, old_alloc + 1, alloc); - check(arr == NULL); - check_uint(alloc, ==, 0); - reftable_set_alloc(NULL, NULL, NULL); - - reftable_free(arr); - } - - return test_done(); -} diff --git a/t/unit-tests/u-dir.c b/t/unit-tests/u-dir.c new file mode 100644 index 0000000000..2d0adaa39e --- /dev/null +++ b/t/unit-tests/u-dir.c @@ -0,0 +1,47 @@ +#include "unit-test.h" +#include "dir.h" + +#define TEST_WITHIN_DEPTH(path, depth, max_depth, expect) do { \ + int actual = within_depth(path, strlen(path), \ + depth, max_depth); \ + if (actual != expect) \ + cl_failf("path '%s' with depth '%d' and max-depth '%d': expected %d, got %d", \ + path, depth, max_depth, expect, actual); \ + } while (0) + +void test_dir__within_depth(void) +{ + /* depth = 0; max_depth = 0 */ + TEST_WITHIN_DEPTH("", 0, 0, 1); + TEST_WITHIN_DEPTH("file", 0, 0, 1); + TEST_WITHIN_DEPTH("a", 0, 0, 1); + TEST_WITHIN_DEPTH("a/file", 0, 0, 0); + TEST_WITHIN_DEPTH("a/b", 0, 0, 0); + TEST_WITHIN_DEPTH("a/b/file", 0, 0, 0); + + /* depth = 0; max_depth = 1 */ + TEST_WITHIN_DEPTH("", 0, 1, 1); + TEST_WITHIN_DEPTH("file", 0, 1, 1); + TEST_WITHIN_DEPTH("a", 0, 1, 1); + TEST_WITHIN_DEPTH("a/file", 0, 1, 1); + TEST_WITHIN_DEPTH("a/b", 0, 1, 1); + TEST_WITHIN_DEPTH("a/b/file", 0, 1, 0); + + /* depth = 1; max_depth = 1 */ + TEST_WITHIN_DEPTH("", 1, 1, 1); + TEST_WITHIN_DEPTH("file", 1, 1, 1); + TEST_WITHIN_DEPTH("a", 1, 1, 1); + TEST_WITHIN_DEPTH("a/file", 1, 1, 0); + TEST_WITHIN_DEPTH("a/b", 1, 1, 0); + TEST_WITHIN_DEPTH("a/b/file", 1, 1, 0); + + /* depth = 1; max_depth = 0 */ + TEST_WITHIN_DEPTH("", 1, 0, 0); + TEST_WITHIN_DEPTH("file", 1, 0, 0); + TEST_WITHIN_DEPTH("a", 1, 0, 0); + TEST_WITHIN_DEPTH("a/file", 1, 0, 0); + TEST_WITHIN_DEPTH("a/b", 1, 0, 0); + TEST_WITHIN_DEPTH("a/b/file", 1, 0, 0); + + +} diff --git a/t/unit-tests/u-prio-queue.c b/t/unit-tests/u-prio-queue.c index 145e689c9c..63e58114ae 100644 --- a/t/unit-tests/u-prio-queue.c +++ b/t/unit-tests/u-prio-queue.c @@ -13,6 +13,7 @@ static int intcmp(const void *va, const void *vb, void *data UNUSED) #define STACK -3 #define GET -4 #define REVERSE -5 +#define REPLACE -6 static int show(int *v) { @@ -51,6 +52,15 @@ static void test_prio_queue(int *input, size_t input_size, case REVERSE: prio_queue_reverse(&pq); break; + case REPLACE: + peek = prio_queue_peek(&pq); + cl_assert(i + 1 < input_size); + cl_assert(input[i + 1] >= 0); + cl_assert(j < result_size); + cl_assert_equal_i(result[j], show(peek)); + j++; + prio_queue_replace(&pq, &input[++i]); + break; default: prio_queue_put(&pq, &input[i]); break; @@ -81,6 +91,13 @@ void test_prio_queue__empty(void) ((int []){ 1, 2, MISSING, 1, 2, MISSING })); } +void test_prio_queue__replace(void) +{ + TEST_INPUT(((int []){ REPLACE, 6, 2, 4, REPLACE, 5, 7, GET, + REPLACE, 1, DUMP }), + ((int []){ MISSING, 2, 4, 5, 1, 6, 7 })); +} + void test_prio_queue__stack(void) { TEST_INPUT(((int []){ STACK, 8, 1, 5, 4, 6, 2, 3, DUMP }), @@ -92,3 +109,9 @@ void test_prio_queue__reverse_stack(void) TEST_INPUT(((int []){ STACK, 1, 2, 3, 4, 5, 6, REVERSE, DUMP }), ((int []){ 1, 2, 3, 4, 5, 6 })); } + +void test_prio_queue__replace_stack(void) +{ + TEST_INPUT(((int []){ STACK, 8, 1, 5, REPLACE, 4, 6, 2, 3, DUMP }), + ((int []){ 5, 3, 2, 6, 4, 1, 8 })); +} diff --git a/t/unit-tests/u-reftable-basics.c b/t/unit-tests/u-reftable-basics.c new file mode 100644 index 0000000000..a0471083e7 --- /dev/null +++ b/t/unit-tests/u-reftable-basics.c @@ -0,0 +1,227 @@ +/* +Copyright 2020 Google LLC + +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file or at +https://developers.google.com/open-source/licenses/bsd +*/ + +#include "unit-test.h" +#include "lib-reftable.h" +#include "reftable/basics.h" + +struct integer_needle_lesseq_args { + int needle; + int *haystack; +}; + +static int integer_needle_lesseq(size_t i, void *_args) +{ + struct integer_needle_lesseq_args *args = _args; + return args->needle <= args->haystack[i]; +} + +static void *realloc_stub(void *p UNUSED, size_t size UNUSED) +{ + return NULL; +} + +void test_reftable_basics__binsearch(void) +{ + int haystack[] = { 2, 4, 6, 8, 10 }; + struct { + int needle; + size_t expected_idx; + } testcases[] = { + {-9000, 0}, + {-1, 0}, + {0, 0}, + {2, 0}, + {3, 1}, + {4, 1}, + {7, 3}, + {9, 4}, + {10, 4}, + {11, 5}, + {9000, 5}, + }; + + for (size_t i = 0; i < ARRAY_SIZE(testcases); i++) { + struct integer_needle_lesseq_args args = { + .haystack = haystack, + .needle = testcases[i].needle, + }; + size_t idx; + + idx = binsearch(ARRAY_SIZE(haystack), + &integer_needle_lesseq, &args); + cl_assert_equal_i(idx, testcases[i].expected_idx); + } +} + +void test_reftable_basics__names_length(void) +{ + const char *a[] = { "a", "b", NULL }; + cl_assert_equal_i(names_length(a), 2); +} + +void test_reftable_basics__names_equal(void) +{ + const char *a[] = { "a", "b", "c", NULL }; + const char *b[] = { "a", "b", "d", NULL }; + const char *c[] = { "a", "b", NULL }; + + cl_assert(names_equal(a, a)); + cl_assert(!names_equal(a, b)); + cl_assert(!names_equal(a, c)); +} + +void test_reftable_basics__parse_names(void) +{ + char in1[] = "line\n"; + char in2[] = "a\nb\nc"; + char **out = parse_names(in1, strlen(in1)); + cl_assert(out != NULL); + cl_assert_equal_s(out[0], "line"); + cl_assert(!out[1]); + free_names(out); + + out = parse_names(in2, strlen(in2)); + cl_assert(out != NULL); + cl_assert_equal_s(out[0], "a"); + cl_assert_equal_s(out[1], "b"); + cl_assert_equal_s(out[2], "c"); + cl_assert(!out[3]); + free_names(out); +} + +void test_reftable_basics__parse_names_drop_empty_string(void) +{ + char in[] = "a\n\nb\n"; + char **out = parse_names(in, strlen(in)); + cl_assert(out != NULL); + cl_assert_equal_s(out[0], "a"); + /* simply '\n' should be dropped as empty string */ + cl_assert_equal_s(out[1], "b"); + cl_assert(out[2] == NULL); + free_names(out); +} + +void test_reftable_basics__common_prefix_size(void) +{ + struct reftable_buf a = REFTABLE_BUF_INIT; + struct reftable_buf b = REFTABLE_BUF_INIT; + struct { + const char *a, *b; + int want; + } cases[] = { + {"abcdef", "abc", 3}, + { "abc", "ab", 2 }, + { "", "abc", 0 }, + { "abc", "abd", 2 }, + { "abc", "pqr", 0 }, + }; + + for (size_t i = 0; i < ARRAY_SIZE(cases); i++) { + cl_assert_equal_i(reftable_buf_addstr(&a, cases[i].a), 0); + cl_assert_equal_i(reftable_buf_addstr(&b, cases[i].b), 0); + cl_assert_equal_i(common_prefix_size(&a, &b), cases[i].want); + reftable_buf_reset(&a); + reftable_buf_reset(&b); + } + reftable_buf_release(&a); + reftable_buf_release(&b); +} + +void test_reftable_basics__put_get_be64(void) +{ + uint64_t in = 0x1122334455667788; + uint8_t dest[8]; + uint64_t out; + reftable_put_be64(dest, in); + out = reftable_get_be64(dest); + cl_assert(in == out); +} + +void test_reftable_basics__put_get_be32(void) +{ + uint32_t in = 0x11223344; + uint8_t dest[4]; + uint32_t out; + reftable_put_be32(dest, in); + out = reftable_get_be32(dest); + cl_assert_equal_i(in, out); +} + +void test_reftable_basics__put_get_be24(void) +{ + uint32_t in = 0x112233; + uint8_t dest[3]; + uint32_t out; + reftable_put_be24(dest, in); + out = reftable_get_be24(dest); + cl_assert_equal_i(in, out); +} + +void test_reftable_basics__put_get_be16(void) +{ + uint32_t in = 0xfef1; + uint8_t dest[3]; + uint32_t out; + reftable_put_be16(dest, in); + out = reftable_get_be16(dest); + cl_assert_equal_i(in, out); +} + +void test_reftable_basics__alloc_grow(void) +{ + int *arr = NULL, *old_arr; + size_t alloc = 0, old_alloc; + + cl_assert_equal_i(REFTABLE_ALLOC_GROW(arr, 1, alloc), 0); + cl_assert(arr != NULL); + cl_assert(alloc >= 1); + arr[0] = 42; + + old_alloc = alloc; + old_arr = arr; + reftable_set_alloc(NULL, realloc_stub, NULL); + cl_assert(REFTABLE_ALLOC_GROW(arr, old_alloc + 1, alloc)); + cl_assert(arr == old_arr); + cl_assert_equal_i(alloc, old_alloc); + + old_alloc = alloc; + reftable_set_alloc(NULL, NULL, NULL); + cl_assert_equal_i(REFTABLE_ALLOC_GROW(arr, old_alloc + 1, alloc), 0); + cl_assert(arr != NULL); + cl_assert(alloc > old_alloc); + arr[alloc - 1] = 42; + + reftable_free(arr); +} + +void test_reftable_basics__alloc_grow_or_null(void) +{ + int *arr = NULL; + size_t alloc = 0, old_alloc; + + REFTABLE_ALLOC_GROW_OR_NULL(arr, 1, alloc); + cl_assert(arr != NULL); + cl_assert(alloc >= 1); + arr[0] = 42; + + old_alloc = alloc; + REFTABLE_ALLOC_GROW_OR_NULL(arr, old_alloc + 1, alloc); + cl_assert(arr != NULL); + cl_assert(alloc > old_alloc); + arr[alloc - 1] = 42; + + old_alloc = alloc; + reftable_set_alloc(NULL, realloc_stub, NULL); + REFTABLE_ALLOC_GROW_OR_NULL(arr, old_alloc + 1, alloc); + cl_assert(arr == NULL); + cl_assert_equal_i(alloc, 0); + reftable_set_alloc(NULL, NULL, NULL); + + reftable_free(arr); +} diff --git a/t/unit-tests/t-reftable-block.c b/t/unit-tests/u-reftable-block.c index 52f1dae1c9..f4bded7d26 100644 --- a/t/unit-tests/t-reftable-block.c +++ b/t/unit-tests/u-reftable-block.c @@ -6,14 +6,15 @@ license that can be found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd */ -#include "test-lib.h" +#include "unit-test.h" +#include "lib-reftable.h" #include "reftable/block.h" #include "reftable/blocksource.h" #include "reftable/constants.h" #include "reftable/reftable-error.h" #include "strbuf.h" -static void t_ref_block_read_write(void) +void test_reftable_block__read_write(void) { const int header_off = 21; /* random */ struct reftable_record recs[30]; @@ -34,17 +35,18 @@ static void t_ref_block_read_write(void) struct reftable_buf block_data = REFTABLE_BUF_INIT; REFTABLE_CALLOC_ARRAY(block_data.buf, block_size); - check(block_data.buf != NULL); + cl_assert(block_data.buf != NULL); block_data.len = block_size; - ret = block_writer_init(&bw, REFTABLE_BLOCK_TYPE_REF, (uint8_t *) block_data.buf, block_size, + ret = block_writer_init(&bw, REFTABLE_BLOCK_TYPE_REF, + (uint8_t *) block_data.buf, block_size, header_off, hash_size(REFTABLE_HASH_SHA1)); - check(!ret); + cl_assert(!ret); rec.u.ref.refname = (char *) ""; rec.u.ref.value_type = REFTABLE_REF_DELETION; ret = block_writer_add(&bw, &rec); - check_int(ret, ==, REFTABLE_API_ERROR); + cl_assert_equal_i(ret, REFTABLE_API_ERROR); for (i = 0; i < N; i++) { rec.u.ref.refname = xstrfmt("branch%02"PRIuMAX, (uintmax_t)i); @@ -55,11 +57,11 @@ static void t_ref_block_read_write(void) ret = block_writer_add(&bw, &rec); rec.u.ref.refname = NULL; rec.u.ref.value_type = REFTABLE_REF_DELETION; - check_int(ret, ==, 0); + cl_assert_equal_i(ret, 0); } ret = block_writer_finish(&bw); - check_int(ret, >, 0); + cl_assert(ret > 0); block_writer_release(&bw); @@ -71,32 +73,32 @@ static void t_ref_block_read_write(void) for (i = 0; ; i++) { ret = block_iter_next(&it, &rec); - check_int(ret, >=, 0); + cl_assert(ret >= 0); if (ret > 0) { - check_int(i, ==, N); + cl_assert_equal_i(i, N); break; } - check(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1)); + cl_assert_equal_i(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1), 1); } for (i = 0; i < N; i++) { reftable_record_key(&recs[i], &want); ret = block_iter_seek_key(&it, &want); - check_int(ret, ==, 0); + cl_assert_equal_i(ret, 0); ret = block_iter_next(&it, &rec); - check_int(ret, ==, 0); + cl_assert_equal_i(ret, 0); - check(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1)); + cl_assert_equal_i(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1), 1); want.len--; ret = block_iter_seek_key(&it, &want); - check_int(ret, ==, 0); + cl_assert_equal_i(ret, 0); ret = block_iter_next(&it, &rec); - check_int(ret, ==, 0); - check(reftable_record_equal(&recs[10 * (i / 10)], &rec, REFTABLE_HASH_SIZE_SHA1)); + cl_assert_equal_i(ret, 0); + cl_assert_equal_i(reftable_record_equal(&recs[10 * (i / 10)], &rec, REFTABLE_HASH_SIZE_SHA1), 1); } reftable_block_release(&block); @@ -108,7 +110,7 @@ static void t_ref_block_read_write(void) reftable_record_release(&recs[i]); } -static void t_log_block_read_write(void) +void test_reftable_block__log_read_write(void) { const int header_off = 21; struct reftable_record recs[30]; @@ -129,12 +131,12 @@ static void t_log_block_read_write(void) struct reftable_buf block_data = REFTABLE_BUF_INIT; REFTABLE_CALLOC_ARRAY(block_data.buf, block_size); - check(block_data.buf != NULL); + cl_assert(block_data.buf != NULL); block_data.len = block_size; ret = block_writer_init(&bw, REFTABLE_BLOCK_TYPE_LOG, (uint8_t *) block_data.buf, block_size, header_off, hash_size(REFTABLE_HASH_SHA1)); - check(!ret); + cl_assert(!ret); for (i = 0; i < N; i++) { rec.u.log.refname = xstrfmt("branch%02"PRIuMAX , (uintmax_t)i); @@ -145,11 +147,11 @@ static void t_log_block_read_write(void) ret = block_writer_add(&bw, &rec); rec.u.log.refname = NULL; rec.u.log.value_type = REFTABLE_LOG_DELETION; - check_int(ret, ==, 0); + cl_assert_equal_i(ret, 0); } ret = block_writer_finish(&bw); - check_int(ret, >, 0); + cl_assert(ret > 0); block_writer_release(&bw); @@ -161,33 +163,33 @@ static void t_log_block_read_write(void) for (i = 0; ; i++) { ret = block_iter_next(&it, &rec); - check_int(ret, >=, 0); + cl_assert(ret >= 0); if (ret > 0) { - check_int(i, ==, N); + cl_assert_equal_i(i, N); break; } - check(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1)); + cl_assert_equal_i(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1), 1); } for (i = 0; i < N; i++) { reftable_buf_reset(&want); - check(!reftable_buf_addstr(&want, recs[i].u.log.refname)); + cl_assert(reftable_buf_addstr(&want, recs[i].u.log.refname) == 0); ret = block_iter_seek_key(&it, &want); - check_int(ret, ==, 0); + cl_assert_equal_i(ret, 0); ret = block_iter_next(&it, &rec); - check_int(ret, ==, 0); + cl_assert_equal_i(ret, 0); - check(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1)); + cl_assert_equal_i(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1), 1); want.len--; ret = block_iter_seek_key(&it, &want); - check_int(ret, ==, 0); + cl_assert_equal_i(ret, 0); ret = block_iter_next(&it, &rec); - check_int(ret, ==, 0); - check(reftable_record_equal(&recs[10 * (i / 10)], &rec, REFTABLE_HASH_SIZE_SHA1)); + cl_assert_equal_i(ret, 0); + cl_assert_equal_i(reftable_record_equal(&recs[10 * (i / 10)], &rec, REFTABLE_HASH_SIZE_SHA1), 1); } reftable_block_release(&block); @@ -199,7 +201,7 @@ static void t_log_block_read_write(void) reftable_record_release(&recs[i]); } -static void t_obj_block_read_write(void) +void test_reftable_block__obj_read_write(void) { const int header_off = 21; struct reftable_record recs[30]; @@ -220,12 +222,12 @@ static void t_obj_block_read_write(void) struct reftable_buf block_data = REFTABLE_BUF_INIT; REFTABLE_CALLOC_ARRAY(block_data.buf, block_size); - check(block_data.buf != NULL); + cl_assert(block_data.buf != NULL); block_data.len = block_size; ret = block_writer_init(&bw, REFTABLE_BLOCK_TYPE_OBJ, (uint8_t *) block_data.buf, block_size, header_off, hash_size(REFTABLE_HASH_SHA1)); - check(!ret); + cl_assert(!ret); for (i = 0; i < N; i++) { uint8_t bytes[] = { i, i + 1, i + 2, i + 3, i + 5 }, *allocated; @@ -238,11 +240,11 @@ static void t_obj_block_read_write(void) ret = block_writer_add(&bw, &rec); rec.u.obj.hash_prefix = NULL; rec.u.obj.hash_prefix_len = 0; - check_int(ret, ==, 0); + cl_assert_equal_i(ret, 0); } ret = block_writer_finish(&bw); - check_int(ret, >, 0); + cl_assert(ret > 0); block_writer_release(&bw); @@ -254,24 +256,24 @@ static void t_obj_block_read_write(void) for (i = 0; ; i++) { ret = block_iter_next(&it, &rec); - check_int(ret, >=, 0); + cl_assert(ret >= 0); if (ret > 0) { - check_int(i, ==, N); + cl_assert_equal_i(i, N); break; } - check(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1)); + cl_assert_equal_i(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1), 1); } for (i = 0; i < N; i++) { reftable_record_key(&recs[i], &want); ret = block_iter_seek_key(&it, &want); - check_int(ret, ==, 0); + cl_assert_equal_i(ret, 0); ret = block_iter_next(&it, &rec); - check_int(ret, ==, 0); + cl_assert_equal_i(ret, 0); - check(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1)); + cl_assert_equal_i(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1), 1); } reftable_block_release(&block); @@ -283,7 +285,7 @@ static void t_obj_block_read_write(void) reftable_record_release(&recs[i]); } -static void t_index_block_read_write(void) +void test_reftable_block__ref_read_write(void) { const int header_off = 21; struct reftable_record recs[30]; @@ -305,12 +307,12 @@ static void t_index_block_read_write(void) struct reftable_buf block_data = REFTABLE_BUF_INIT; REFTABLE_CALLOC_ARRAY(block_data.buf, block_size); - check(block_data.buf != NULL); + cl_assert(block_data.buf != NULL); block_data.len = block_size; ret = block_writer_init(&bw, REFTABLE_BLOCK_TYPE_INDEX, (uint8_t *) block_data.buf, block_size, header_off, hash_size(REFTABLE_HASH_SHA1)); - check(!ret); + cl_assert(!ret); for (i = 0; i < N; i++) { char buf[128]; @@ -319,15 +321,15 @@ static void t_index_block_read_write(void) reftable_buf_init(&recs[i].u.idx.last_key); recs[i].type = REFTABLE_BLOCK_TYPE_INDEX; - check(!reftable_buf_addstr(&recs[i].u.idx.last_key, buf)); + cl_assert(!reftable_buf_addstr(&recs[i].u.idx.last_key, buf)); recs[i].u.idx.offset = i; ret = block_writer_add(&bw, &recs[i]); - check_int(ret, ==, 0); + cl_assert_equal_i(ret, 0); } ret = block_writer_finish(&bw); - check_int(ret, >, 0); + cl_assert(ret > 0); block_writer_release(&bw); @@ -339,32 +341,32 @@ static void t_index_block_read_write(void) for (i = 0; ; i++) { ret = block_iter_next(&it, &rec); - check_int(ret, >=, 0); + cl_assert(ret >= 0); if (ret > 0) { - check_int(i, ==, N); + cl_assert_equal_i(i, N); break; } - check(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1)); + cl_assert_equal_i(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1), 1); } for (i = 0; i < N; i++) { reftable_record_key(&recs[i], &want); ret = block_iter_seek_key(&it, &want); - check_int(ret, ==, 0); + cl_assert_equal_i(ret, 0); ret = block_iter_next(&it, &rec); - check_int(ret, ==, 0); + cl_assert_equal_i(ret, 0); - check(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1)); + cl_assert_equal_i(reftable_record_equal(&recs[i], &rec, REFTABLE_HASH_SIZE_SHA1), 1); want.len--; ret = block_iter_seek_key(&it, &want); - check_int(ret, ==, 0); + cl_assert_equal_i(ret, 0); ret = block_iter_next(&it, &rec); - check_int(ret, ==, 0); - check(reftable_record_equal(&recs[10 * (i / 10)], &rec, REFTABLE_HASH_SIZE_SHA1)); + cl_assert_equal_i(ret, 0); + cl_assert_equal_i(reftable_record_equal(&recs[10 * (i / 10)], &rec, REFTABLE_HASH_SIZE_SHA1), 1); } reftable_block_release(&block); @@ -376,7 +378,7 @@ static void t_index_block_read_write(void) reftable_record_release(&recs[i]); } -static void t_block_iterator(void) +void test_reftable_block__iterator(void) { struct reftable_block_source source = { 0 }; struct block_writer writer = { @@ -391,11 +393,12 @@ static void t_block_iterator(void) data.len = 1024; REFTABLE_CALLOC_ARRAY(data.buf, data.len); - check(data.buf != NULL); + cl_assert(data.buf != NULL); - err = block_writer_init(&writer, REFTABLE_BLOCK_TYPE_REF, (uint8_t *) data.buf, data.len, + err = block_writer_init(&writer, REFTABLE_BLOCK_TYPE_REF, + (uint8_t *) data.buf, data.len, 0, hash_size(REFTABLE_HASH_SHA1)); - check(!err); + cl_assert(!err); for (size_t i = 0; i < ARRAY_SIZE(expected_refs); i++) { expected_refs[i] = (struct reftable_record) { @@ -408,42 +411,42 @@ static void t_block_iterator(void) memset(expected_refs[i].u.ref.value.val1, i, REFTABLE_HASH_SIZE_SHA1); err = block_writer_add(&writer, &expected_refs[i]); - check_int(err, ==, 0); + cl_assert_equal_i(err, 0); } err = block_writer_finish(&writer); - check_int(err, >, 0); + cl_assert(err > 0); block_source_from_buf(&source, &data); reftable_block_init(&block, &source, 0, 0, data.len, REFTABLE_HASH_SIZE_SHA1, REFTABLE_BLOCK_TYPE_REF); err = reftable_block_init_iterator(&block, &it); - check_int(err, ==, 0); + cl_assert_equal_i(err, 0); for (size_t i = 0; ; i++) { err = reftable_iterator_next_ref(&it, &ref); if (err > 0) { - check_int(i, ==, ARRAY_SIZE(expected_refs)); + cl_assert_equal_i(i, ARRAY_SIZE(expected_refs)); break; } - check_int(err, ==, 0); + cl_assert_equal_i(err, 0); - check(reftable_ref_record_equal(&ref, &expected_refs[i].u.ref, - REFTABLE_HASH_SIZE_SHA1)); + cl_assert(reftable_ref_record_equal(&ref, + &expected_refs[i].u.ref, REFTABLE_HASH_SIZE_SHA1)); } err = reftable_iterator_seek_ref(&it, "refs/heads/does-not-exist"); - check_int(err, ==, 0); + cl_assert_equal_i(err, 0); err = reftable_iterator_next_ref(&it, &ref); - check_int(err, ==, 1); + cl_assert_equal_i(err, 1); err = reftable_iterator_seek_ref(&it, "refs/heads/branch-13"); - check_int(err, ==, 0); + cl_assert_equal_i(err, 0); err = reftable_iterator_next_ref(&it, &ref); - check_int(err, ==, 0); - check(reftable_ref_record_equal(&ref, &expected_refs[13].u.ref, - REFTABLE_HASH_SIZE_SHA1)); + cl_assert_equal_i(err, 0); + cl_assert(reftable_ref_record_equal(&ref, + &expected_refs[13].u.ref,REFTABLE_HASH_SIZE_SHA1)); for (size_t i = 0; i < ARRAY_SIZE(expected_refs); i++) reftable_free(expected_refs[i].u.ref.refname); @@ -453,14 +456,3 @@ static void t_block_iterator(void) block_writer_release(&writer); reftable_buf_release(&data); } - -int cmd_main(int argc UNUSED, const char *argv[] UNUSED) -{ - TEST(t_index_block_read_write(), "read-write operations on index blocks work"); - TEST(t_log_block_read_write(), "read-write operations on log blocks work"); - TEST(t_obj_block_read_write(), "read-write operations on obj blocks work"); - TEST(t_ref_block_read_write(), "read-write operations on ref blocks work"); - TEST(t_block_iterator(), "block iterator works"); - - return test_done(); -} diff --git a/t/unit-tests/t-reftable-merged.c b/t/unit-tests/u-reftable-merged.c index 18c3251a56..54cb7fc2a7 100644 --- a/t/unit-tests/t-reftable-merged.c +++ b/t/unit-tests/u-reftable-merged.c @@ -6,7 +6,7 @@ license that can be found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd */ -#include "test-lib.h" +#include "unit-test.h" #include "lib-reftable.h" #include "reftable/blocksource.h" #include "reftable/constants.h" @@ -29,21 +29,21 @@ merged_table_from_records(struct reftable_ref_record **refs, int err; REFTABLE_CALLOC_ARRAY(*tables, n); - check(*tables != NULL); + cl_assert(*tables != NULL); REFTABLE_CALLOC_ARRAY(*source, n); - check(*source != NULL); + cl_assert(*source != NULL); for (size_t i = 0; i < n; i++) { - t_reftable_write_to_buf(&buf[i], refs[i], sizes[i], NULL, 0, &opts); + cl_reftable_write_to_buf(&buf[i], refs[i], sizes[i], NULL, 0, &opts); block_source_from_buf(&(*source)[i], &buf[i]); err = reftable_table_new(&(*tables)[i], &(*source)[i], "name"); - check(!err); + cl_assert(!err); } err = reftable_merged_table_new(&mt, *tables, n, REFTABLE_HASH_SHA1); - check(!err); + cl_assert(!err); return mt; } @@ -54,7 +54,7 @@ static void tables_destroy(struct reftable_table **tables, const size_t n) reftable_free(tables); } -static void t_merged_single_record(void) +void test_reftable_merged__single_record(void) { struct reftable_ref_record r1[] = { { .refname = (char *) "b", @@ -85,13 +85,14 @@ static void t_merged_single_record(void) int err; err = merged_table_init_iter(mt, &it, REFTABLE_BLOCK_TYPE_REF); - check(!err); + cl_assert(!err); err = reftable_iterator_seek_ref(&it, "a"); - check(!err); + cl_assert(!err); err = reftable_iterator_next_ref(&it, &ref); - check(!err); - check(reftable_ref_record_equal(&r2[0], &ref, REFTABLE_HASH_SIZE_SHA1)); + cl_assert(!err); + cl_assert(reftable_ref_record_equal(&r2[0], &ref, + REFTABLE_HASH_SIZE_SHA1) != 0); reftable_ref_record_release(&ref); reftable_iterator_destroy(&it); tables_destroy(tables, 3); @@ -101,7 +102,7 @@ static void t_merged_single_record(void) reftable_free(bs); } -static void t_merged_refs(void) +void test_reftable_merged__refs(void) { struct reftable_ref_record r1[] = { { @@ -165,12 +166,12 @@ static void t_merged_refs(void) size_t i; err = merged_table_init_iter(mt, &it, REFTABLE_BLOCK_TYPE_REF); - check(!err); + cl_assert(!err); err = reftable_iterator_seek_ref(&it, "a"); - check(!err); - check_int(reftable_merged_table_hash_id(mt), ==, REFTABLE_HASH_SHA1); - check_int(reftable_merged_table_min_update_index(mt), ==, 1); - check_int(reftable_merged_table_max_update_index(mt), ==, 3); + cl_assert(err == 0); + cl_assert_equal_i(reftable_merged_table_hash_id(mt), REFTABLE_HASH_SHA1); + cl_assert_equal_i(reftable_merged_table_min_update_index(mt), 1); + cl_assert_equal_i(reftable_merged_table_max_update_index(mt), 3); while (len < 100) { /* cap loops/recursion. */ struct reftable_ref_record ref = { 0 }; @@ -178,15 +179,15 @@ static void t_merged_refs(void) if (err > 0) break; - check(!REFTABLE_ALLOC_GROW(out, len + 1, cap)); + cl_assert(REFTABLE_ALLOC_GROW(out, len + 1, cap) == 0); out[len++] = ref; } reftable_iterator_destroy(&it); - check_int(ARRAY_SIZE(want), ==, len); + cl_assert_equal_i(ARRAY_SIZE(want), len); for (i = 0; i < len; i++) - check(reftable_ref_record_equal(want[i], &out[i], - REFTABLE_HASH_SIZE_SHA1)); + cl_assert(reftable_ref_record_equal(want[i], &out[i], + REFTABLE_HASH_SIZE_SHA1) != 0); for (i = 0; i < len; i++) reftable_ref_record_release(&out[i]); reftable_free(out); @@ -198,7 +199,7 @@ static void t_merged_refs(void) reftable_free(bs); } -static void t_merged_seek_multiple_times(void) +void test_reftable_merged__seek_multiple_times(void) { struct reftable_ref_record r1[] = { { @@ -248,20 +249,17 @@ static void t_merged_seek_multiple_times(void) for (size_t i = 0; i < 5; i++) { int err = reftable_iterator_seek_ref(&it, "c"); - check(!err); + cl_assert(!err); - err = reftable_iterator_next_ref(&it, &rec); - check(!err); - err = reftable_ref_record_equal(&rec, &r1[1], REFTABLE_HASH_SIZE_SHA1); - check(err == 1); + cl_assert(reftable_iterator_next_ref(&it, &rec) == 0); + cl_assert_equal_i(reftable_ref_record_equal(&rec, &r1[1], + REFTABLE_HASH_SIZE_SHA1), 1); - err = reftable_iterator_next_ref(&it, &rec); - check(!err); - err = reftable_ref_record_equal(&rec, &r2[1], REFTABLE_HASH_SIZE_SHA1); - check(err == 1); + cl_assert(reftable_iterator_next_ref(&it, &rec) == 0); + cl_assert_equal_i(reftable_ref_record_equal(&rec, &r2[1], + REFTABLE_HASH_SIZE_SHA1), 1); - err = reftable_iterator_next_ref(&it, &rec); - check(err > 0); + cl_assert(reftable_iterator_next_ref(&it, &rec) > 0); } for (size_t i = 0; i < ARRAY_SIZE(bufs); i++) @@ -273,7 +271,7 @@ static void t_merged_seek_multiple_times(void) reftable_free(sources); } -static void t_merged_seek_multiple_times_without_draining(void) +void test_reftable_merged__seek_multiple_times_no_drain(void) { struct reftable_ref_record r1[] = { { @@ -317,24 +315,19 @@ static void t_merged_seek_multiple_times_without_draining(void) struct reftable_ref_record rec = { 0 }; struct reftable_iterator it = { 0 }; struct reftable_merged_table *mt; - int err; mt = merged_table_from_records(refs, &sources, &tables, sizes, bufs, 2); merged_table_init_iter(mt, &it, REFTABLE_BLOCK_TYPE_REF); - err = reftable_iterator_seek_ref(&it, "b"); - check(!err); - err = reftable_iterator_next_ref(&it, &rec); - check(!err); - err = reftable_ref_record_equal(&rec, &r2[0], REFTABLE_HASH_SIZE_SHA1); - check(err == 1); + cl_assert(reftable_iterator_seek_ref(&it, "b") == 0); + cl_assert(reftable_iterator_next_ref(&it, &rec) == 0); + cl_assert_equal_i(reftable_ref_record_equal(&rec, &r2[0], + REFTABLE_HASH_SIZE_SHA1), 1); - err = reftable_iterator_seek_ref(&it, "a"); - check(!err); - err = reftable_iterator_next_ref(&it, &rec); - check(!err); - err = reftable_ref_record_equal(&rec, &r1[0], REFTABLE_HASH_SIZE_SHA1); - check(err == 1); + cl_assert(reftable_iterator_seek_ref(&it, "a") == 0); + cl_assert(reftable_iterator_next_ref(&it, &rec) == 0); + cl_assert_equal_i(reftable_ref_record_equal(&rec, &r1[0], + REFTABLE_HASH_SIZE_SHA1), 1); for (size_t i = 0; i < ARRAY_SIZE(bufs); i++) reftable_buf_release(&bufs[i]); @@ -359,25 +352,25 @@ merged_table_from_log_records(struct reftable_log_record **logs, int err; REFTABLE_CALLOC_ARRAY(*tables, n); - check(*tables != NULL); + cl_assert(*tables != NULL); REFTABLE_CALLOC_ARRAY(*source, n); - check(*source != NULL); + cl_assert(*source != NULL); for (size_t i = 0; i < n; i++) { - t_reftable_write_to_buf(&buf[i], NULL, 0, logs[i], sizes[i], &opts); + cl_reftable_write_to_buf(&buf[i], NULL, 0, logs[i], sizes[i], &opts); block_source_from_buf(&(*source)[i], &buf[i]); err = reftable_table_new(&(*tables)[i], &(*source)[i], "name"); - check(!err); + cl_assert(!err); } err = reftable_merged_table_new(&mt, *tables, n, REFTABLE_HASH_SHA1); - check(!err); + cl_assert(!err); return mt; } -static void t_merged_logs(void) +void test_reftable_merged__logs(void) { struct reftable_log_record r1[] = { { @@ -439,19 +432,19 @@ static void t_merged_logs(void) struct reftable_merged_table *mt = merged_table_from_log_records( logs, &bs, &tables, sizes, bufs, 3); struct reftable_iterator it = { 0 }; - int err; struct reftable_log_record *out = NULL; size_t len = 0; size_t cap = 0; size_t i; + int err; err = merged_table_init_iter(mt, &it, REFTABLE_BLOCK_TYPE_LOG); - check(!err); + cl_assert(!err); err = reftable_iterator_seek_log(&it, "a"); - check(!err); - check_int(reftable_merged_table_hash_id(mt), ==, REFTABLE_HASH_SHA1); - check_int(reftable_merged_table_min_update_index(mt), ==, 1); - check_int(reftable_merged_table_max_update_index(mt), ==, 3); + cl_assert(!err); + cl_assert_equal_i(reftable_merged_table_hash_id(mt), REFTABLE_HASH_SHA1); + cl_assert_equal_i(reftable_merged_table_min_update_index(mt), 1); + cl_assert_equal_i(reftable_merged_table_max_update_index(mt), 3); while (len < 100) { /* cap loops/recursion. */ struct reftable_log_record log = { 0 }; @@ -459,24 +452,24 @@ static void t_merged_logs(void) if (err > 0) break; - check(!REFTABLE_ALLOC_GROW(out, len + 1, cap)); + cl_assert(REFTABLE_ALLOC_GROW(out, len + 1, cap) == 0); out[len++] = log; } reftable_iterator_destroy(&it); - check_int(ARRAY_SIZE(want), ==, len); + cl_assert_equal_i(ARRAY_SIZE(want), len); for (i = 0; i < len; i++) - check(reftable_log_record_equal(want[i], &out[i], - REFTABLE_HASH_SIZE_SHA1)); + cl_assert(reftable_log_record_equal(want[i], &out[i], + REFTABLE_HASH_SIZE_SHA1) != 0); err = merged_table_init_iter(mt, &it, REFTABLE_BLOCK_TYPE_LOG); - check(!err); + cl_assert(!err); err = reftable_iterator_seek_log_at(&it, "a", 2); - check(!err); + cl_assert(!err); reftable_log_record_release(&out[0]); - err = reftable_iterator_next_log(&it, &out[0]); - check(!err); - check(reftable_log_record_equal(&out[0], &r3[0], REFTABLE_HASH_SIZE_SHA1)); + cl_assert(reftable_iterator_next_log(&it, &out[0]) == 0); + cl_assert(reftable_log_record_equal(&out[0], &r3[0], + REFTABLE_HASH_SIZE_SHA1) != 0); reftable_iterator_destroy(&it); for (i = 0; i < len; i++) @@ -490,11 +483,11 @@ static void t_merged_logs(void) reftable_free(bs); } -static void t_default_write_opts(void) +void test_reftable_merged__default_write_opts(void) { struct reftable_write_options opts = { 0 }; struct reftable_buf buf = REFTABLE_BUF_INIT; - struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts); struct reftable_ref_record rec = { .refname = (char *) "master", .update_index = 1, @@ -507,40 +500,25 @@ static void t_default_write_opts(void) reftable_writer_set_limits(w, 1, 1); - err = reftable_writer_add_ref(w, &rec); - check(!err); + cl_assert_equal_i(reftable_writer_add_ref(w, &rec), 0); - err = reftable_writer_close(w); - check(!err); + cl_assert_equal_i(reftable_writer_close(w), 0); reftable_writer_free(w); block_source_from_buf(&source, &buf); err = reftable_table_new(&table, &source, "filename"); - check(!err); + cl_assert(!err); hash_id = reftable_table_hash_id(table); - check_int(hash_id, ==, REFTABLE_HASH_SHA1); + cl_assert_equal_i(hash_id, REFTABLE_HASH_SHA1); err = reftable_merged_table_new(&merged, &table, 1, REFTABLE_HASH_SHA256); - check_int(err, ==, REFTABLE_FORMAT_ERROR); + cl_assert_equal_i(err, REFTABLE_FORMAT_ERROR); err = reftable_merged_table_new(&merged, &table, 1, REFTABLE_HASH_SHA1); - check(!err); + cl_assert(!err); reftable_table_decref(table); reftable_merged_table_free(merged); reftable_buf_release(&buf); } - - -int cmd_main(int argc UNUSED, const char *argv[] UNUSED) -{ - TEST(t_default_write_opts(), "merged table with default write opts"); - TEST(t_merged_logs(), "merged table with multiple log updates for same ref"); - TEST(t_merged_refs(), "merged table with multiple updates to same ref"); - TEST(t_merged_seek_multiple_times(), "merged table can seek multiple times"); - TEST(t_merged_seek_multiple_times_without_draining(), "merged table can seek multiple times without draining"); - TEST(t_merged_single_record(), "ref occurring in only one record can be fetched"); - - return test_done(); -} diff --git a/t/unit-tests/t-reftable-pq.c b/t/unit-tests/u-reftable-pq.c index fb5a4eb187..f8a28f6e07 100644 --- a/t/unit-tests/t-reftable-pq.c +++ b/t/unit-tests/u-reftable-pq.c @@ -6,7 +6,8 @@ license that can be found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd */ -#include "test-lib.h" +#include "unit-test.h" +#include "lib-reftable.h" #include "reftable/constants.h" #include "reftable/pq.h" #include "strbuf.h" @@ -15,18 +16,18 @@ static void merged_iter_pqueue_check(const struct merged_iter_pqueue *pq) { for (size_t i = 1; i < pq->len; i++) { size_t parent = (i - 1) / 2; - check(pq_less(&pq->heap[parent], &pq->heap[i])); + cl_assert(pq_less(&pq->heap[parent], &pq->heap[i]) != 0); } } static int pq_entry_equal(struct pq_entry *a, struct pq_entry *b) { int cmp; - check(!reftable_record_cmp(a->rec, b->rec, &cmp)); + cl_assert_equal_i(reftable_record_cmp(a->rec, b->rec, &cmp), 0); return !cmp && (a->index == b->index); } -static void t_pq_record(void) +void test_reftable_pq__record(void) { struct merged_iter_pqueue pq = { 0 }; struct reftable_record recs[54]; @@ -34,7 +35,8 @@ static void t_pq_record(void) char *last = NULL; for (i = 0; i < N; i++) { - check(!reftable_record_init(&recs[i], REFTABLE_BLOCK_TYPE_REF)); + cl_assert(!reftable_record_init(&recs[i], + REFTABLE_BLOCK_TYPE_REF)); recs[i].u.ref.refname = xstrfmt("%02"PRIuMAX, (uintmax_t)i); } @@ -53,13 +55,13 @@ static void t_pq_record(void) struct pq_entry top = merged_iter_pqueue_top(pq); struct pq_entry e; - check(!merged_iter_pqueue_remove(&pq, &e)); + cl_assert_equal_i(merged_iter_pqueue_remove(&pq, &e), 0); merged_iter_pqueue_check(&pq); - check(pq_entry_equal(&top, &e)); - check(reftable_record_type(e.rec) == REFTABLE_BLOCK_TYPE_REF); + cl_assert(pq_entry_equal(&top, &e)); + cl_assert(reftable_record_type(e.rec) == REFTABLE_BLOCK_TYPE_REF); if (last) - check_int(strcmp(last, e.rec->u.ref.refname), <, 0); + cl_assert(strcmp(last, e.rec->u.ref.refname) < 0); last = e.rec->u.ref.refname; } @@ -68,7 +70,7 @@ static void t_pq_record(void) merged_iter_pqueue_release(&pq); } -static void t_pq_index(void) +void test_reftable_pq__index(void) { struct merged_iter_pqueue pq = { 0 }; struct reftable_record recs[13]; @@ -76,7 +78,8 @@ static void t_pq_index(void) size_t N = ARRAY_SIZE(recs), i; for (i = 0; i < N; i++) { - check(!reftable_record_init(&recs[i], REFTABLE_BLOCK_TYPE_REF)); + cl_assert(!reftable_record_init(&recs[i], + REFTABLE_BLOCK_TYPE_REF)); recs[i].u.ref.refname = (char *) "refs/heads/master"; } @@ -96,28 +99,29 @@ static void t_pq_index(void) struct pq_entry top = merged_iter_pqueue_top(pq); struct pq_entry e; - check(!merged_iter_pqueue_remove(&pq, &e)); + cl_assert_equal_i(merged_iter_pqueue_remove(&pq, &e), 0); merged_iter_pqueue_check(&pq); - check(pq_entry_equal(&top, &e)); - check(reftable_record_type(e.rec) == REFTABLE_BLOCK_TYPE_REF); - check_int(e.index, ==, i); + cl_assert(pq_entry_equal(&top, &e)); + cl_assert(reftable_record_type(e.rec) == REFTABLE_BLOCK_TYPE_REF); + cl_assert_equal_i(e.index, i); if (last) - check_str(last, e.rec->u.ref.refname); + cl_assert_equal_s(last, e.rec->u.ref.refname); last = e.rec->u.ref.refname; } merged_iter_pqueue_release(&pq); } -static void t_merged_iter_pqueue_top(void) +void test_reftable_pq__merged_iter_pqueue_top(void) { struct merged_iter_pqueue pq = { 0 }; struct reftable_record recs[13]; size_t N = ARRAY_SIZE(recs), i; for (i = 0; i < N; i++) { - check(!reftable_record_init(&recs[i], REFTABLE_BLOCK_TYPE_REF)); + cl_assert(!reftable_record_init(&recs[i], + REFTABLE_BLOCK_TYPE_REF)); recs[i].u.ref.refname = (char *) "refs/heads/master"; } @@ -137,25 +141,16 @@ static void t_merged_iter_pqueue_top(void) struct pq_entry top = merged_iter_pqueue_top(pq); struct pq_entry e; - check(!merged_iter_pqueue_remove(&pq, &e)); + cl_assert_equal_i(merged_iter_pqueue_remove(&pq, &e), 0); merged_iter_pqueue_check(&pq); - check(pq_entry_equal(&top, &e)); - check(reftable_record_equal(top.rec, &recs[i], REFTABLE_HASH_SIZE_SHA1)); + cl_assert(pq_entry_equal(&top, &e) != 0); + cl_assert(reftable_record_equal(top.rec, &recs[i], REFTABLE_HASH_SIZE_SHA1) != 0); for (size_t j = 0; i < pq.len; j++) { - check(pq_less(&top, &pq.heap[j])); - check_int(top.index, >, j); + cl_assert(pq_less(&top, &pq.heap[j]) != 0); + cl_assert(top.index > j); } } merged_iter_pqueue_release(&pq); } - -int cmd_main(int argc UNUSED, const char *argv[] UNUSED) -{ - TEST(t_pq_record(), "pq works with record-based comparison"); - TEST(t_pq_index(), "pq works with index-based comparison"); - TEST(t_merged_iter_pqueue_top(), "merged_iter_pqueue_top works"); - - return test_done(); -} diff --git a/t/unit-tests/t-reftable-readwrite.c b/t/unit-tests/u-reftable-readwrite.c index 4c49129439..4d8c4be5f1 100644 --- a/t/unit-tests/t-reftable-readwrite.c +++ b/t/unit-tests/u-reftable-readwrite.c @@ -8,7 +8,7 @@ https://developers.google.com/open-source/licenses/bsd #define DISABLE_SIGN_COMPARE_WARNINGS -#include "test-lib.h" +#include "unit-test.h" #include "lib-reftable.h" #include "reftable/basics.h" #include "reftable/blocksource.h" @@ -19,24 +19,24 @@ https://developers.google.com/open-source/licenses/bsd static const int update_index = 5; -static void t_buffer(void) +void test_reftable_readwrite__buffer(void) { struct reftable_buf buf = REFTABLE_BUF_INIT; struct reftable_block_source source = { 0 }; struct reftable_block_data out = { 0 }; int n; uint8_t in[] = "hello"; - check(!reftable_buf_add(&buf, in, sizeof(in))); + cl_assert_equal_i(reftable_buf_add(&buf, in, sizeof(in)), 0); block_source_from_buf(&source, &buf); - check_int(block_source_size(&source), ==, 6); + cl_assert_equal_i(block_source_size(&source), 6); n = block_source_read_data(&source, &out, 0, sizeof(in)); - check_int(n, ==, sizeof(in)); - check(!memcmp(in, out.data, n)); + cl_assert_equal_i(n, sizeof(in)); + cl_assert(!memcmp(in, out.data, n)); block_source_release_data(&out); n = block_source_read_data(&source, &out, 1, 2); - check_int(n, ==, 2); - check(!memcmp(out.data, "el", 2)); + cl_assert_equal_i(n, 2); + cl_assert(!memcmp(out.data, "el", 2)); block_source_release_data(&out); block_source_close(&source); @@ -55,41 +55,41 @@ static void write_table(char ***names, struct reftable_buf *buf, int N, int i; REFTABLE_CALLOC_ARRAY(*names, N + 1); - check(*names != NULL); + cl_assert(*names != NULL); REFTABLE_CALLOC_ARRAY(refs, N); - check(refs != NULL); + cl_assert(refs != NULL); REFTABLE_CALLOC_ARRAY(logs, N); - check(logs != NULL); + cl_assert(logs != NULL); for (i = 0; i < N; i++) { refs[i].refname = (*names)[i] = xstrfmt("refs/heads/branch%02d", i); refs[i].update_index = update_index; refs[i].value_type = REFTABLE_REF_VAL1; - t_reftable_set_hash(refs[i].value.val1, i, REFTABLE_HASH_SHA1); + cl_reftable_set_hash(refs[i].value.val1, i, + REFTABLE_HASH_SHA1); } for (i = 0; i < N; i++) { logs[i].refname = (*names)[i]; logs[i].update_index = update_index; logs[i].value_type = REFTABLE_LOG_UPDATE; - t_reftable_set_hash(logs[i].value.update.new_hash, i, - REFTABLE_HASH_SHA1); + cl_reftable_set_hash(logs[i].value.update.new_hash, i, + REFTABLE_HASH_SHA1); logs[i].value.update.message = (char *) "message"; } - t_reftable_write_to_buf(buf, refs, N, logs, N, &opts); + cl_reftable_write_to_buf(buf, refs, N, logs, N, &opts); reftable_free(refs); reftable_free(logs); } -static void t_log_buffer_size(void) +void test_reftable_readwrite__log_buffer_size(void) { struct reftable_buf buf = REFTABLE_BUF_INIT; struct reftable_write_options opts = { .block_size = 4096, }; - int err; int i; struct reftable_log_record log = { .refname = (char *) "refs/heads/master", @@ -102,7 +102,8 @@ static void t_log_buffer_size(void) .time = 0x5e430672, .message = (char *) "commit: 9\n", } } }; - struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, + &opts); /* This tests buffer extension for log compression. Must use a random hash, to ensure that the compressed part is larger than the original. @@ -112,22 +113,19 @@ static void t_log_buffer_size(void) log.value.update.new_hash[i] = (uint8_t)(git_rand(0) % 256); } reftable_writer_set_limits(w, update_index, update_index); - err = reftable_writer_add_log(w, &log); - check(!err); - err = reftable_writer_close(w); - check(!err); + cl_assert_equal_i(reftable_writer_add_log(w, &log), 0); + cl_assert_equal_i(reftable_writer_close(w), 0); reftable_writer_free(w); reftable_buf_release(&buf); } -static void t_log_overflow(void) +void test_reftable_readwrite__log_overflow(void) { struct reftable_buf buf = REFTABLE_BUF_INIT; char msg[256] = { 0 }; struct reftable_write_options opts = { .block_size = ARRAY_SIZE(msg), }; - int err; struct reftable_log_record log = { .refname = (char *) "refs/heads/master", .update_index = update_index, @@ -144,21 +142,22 @@ static void t_log_overflow(void) }, }, }; - struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, + &opts); memset(msg, 'x', sizeof(msg) - 1); reftable_writer_set_limits(w, update_index, update_index); - err = reftable_writer_add_log(w, &log); - check_int(err, ==, REFTABLE_ENTRY_TOO_BIG_ERROR); + cl_assert_equal_i(reftable_writer_add_log(w, &log), REFTABLE_ENTRY_TOO_BIG_ERROR); reftable_writer_free(w); reftable_buf_release(&buf); } -static void t_log_write_limits(void) +void test_reftable_readwrite__log_write_limits(void) { struct reftable_write_options opts = { 0 }; struct reftable_buf buf = REFTABLE_BUF_INIT; - struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, + &opts); struct reftable_log_record log = { .refname = (char *)"refs/head/master", .update_index = 0, @@ -174,29 +173,25 @@ static void t_log_write_limits(void) }, }, }; - int err; reftable_writer_set_limits(w, 1, 1); /* write with update_index (0) below set limits (1, 1) */ - err = reftable_writer_add_log(w, &log); - check_int(err, ==, 0); + cl_assert_equal_i(reftable_writer_add_log(w, &log), 0); /* write with update_index (1) in the set limits (1, 1) */ log.update_index = 1; - err = reftable_writer_add_log(w, &log); - check_int(err, ==, 0); + cl_assert_equal_i(reftable_writer_add_log(w, &log), 0); /* write with update_index (3) above set limits (1, 1) */ log.update_index = 3; - err = reftable_writer_add_log(w, &log); - check_int(err, ==, REFTABLE_API_ERROR); + cl_assert_equal_i(reftable_writer_add_log(w, &log), REFTABLE_API_ERROR); reftable_writer_free(w); reftable_buf_release(&buf); } -static void t_log_write_read(void) +void test_reftable_readwrite__log_write_read(void) { struct reftable_write_options opts = { .block_size = 256, @@ -207,13 +202,14 @@ static void t_log_write_read(void) struct reftable_table *table; struct reftable_block_source source = { 0 }; struct reftable_buf buf = REFTABLE_BUF_INIT; - struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts); const struct reftable_stats *stats = NULL; - int N = 2, err, i, n; + int N = 2, i; char **names; + int err; names = reftable_calloc(N + 1, sizeof(*names)); - check(names != NULL); + cl_assert(names != NULL); reftable_writer_set_limits(w, 0, N); @@ -225,8 +221,7 @@ static void t_log_write_read(void) ref.refname = name; ref.update_index = i; - err = reftable_writer_add_ref(w, &ref); - check(!err); + cl_assert_equal_i(reftable_writer_add_ref(w, &ref), 0); } for (i = 0; i < N; i++) { @@ -235,60 +230,57 @@ static void t_log_write_read(void) log.refname = names[i]; log.update_index = i; log.value_type = REFTABLE_LOG_UPDATE; - t_reftable_set_hash(log.value.update.old_hash, i, - REFTABLE_HASH_SHA1); - t_reftable_set_hash(log.value.update.new_hash, i + 1, - REFTABLE_HASH_SHA1); + cl_reftable_set_hash(log.value.update.old_hash, i, + REFTABLE_HASH_SHA1); + cl_reftable_set_hash(log.value.update.new_hash, i + 1, + REFTABLE_HASH_SHA1); - err = reftable_writer_add_log(w, &log); - check(!err); + cl_assert_equal_i(reftable_writer_add_log(w, &log), 0); } - n = reftable_writer_close(w); - check_int(n, ==, 0); + cl_assert_equal_i(reftable_writer_close(w), 0); stats = reftable_writer_stats(w); - check_int(stats->log_stats.blocks, >, 0); + cl_assert(stats->log_stats.blocks > 0); reftable_writer_free(w); w = NULL; block_source_from_buf(&source, &buf); err = reftable_table_new(&table, &source, "file.log"); - check(!err); + cl_assert(!err); err = reftable_table_init_ref_iterator(table, &it); - check(!err); + cl_assert(!err); err = reftable_iterator_seek_ref(&it, names[N - 1]); - check(!err); + cl_assert(!err); err = reftable_iterator_next_ref(&it, &ref); - check(!err); + cl_assert(!err); /* end of iteration. */ - err = reftable_iterator_next_ref(&it, &ref); - check_int(err, >, 0); + cl_assert(reftable_iterator_next_ref(&it, &ref) > 0); reftable_iterator_destroy(&it); reftable_ref_record_release(&ref); err = reftable_table_init_log_iterator(table, &it); - check(!err); + cl_assert(!err); err = reftable_iterator_seek_log(&it, ""); - check(!err); + cl_assert(!err); for (i = 0; ; i++) { int err = reftable_iterator_next_log(&it, &log); if (err > 0) break; - check(!err); - check_str(names[i], log.refname); - check_int(i, ==, log.update_index); + cl_assert(!err); + cl_assert_equal_s(names[i], log.refname); + cl_assert_equal_i(i, log.update_index); reftable_log_record_release(&log); } - check_int(i, ==, N); + cl_assert_equal_i(i, N); reftable_iterator_destroy(&it); /* cleanup. */ @@ -297,7 +289,7 @@ static void t_log_write_read(void) reftable_table_decref(table); } -static void t_log_zlib_corruption(void) +void test_reftable_readwrite__log_zlib_corruption(void) { struct reftable_write_options opts = { .block_size = 256, @@ -306,10 +298,12 @@ static void t_log_zlib_corruption(void) struct reftable_table *table; struct reftable_block_source source = { 0 }; struct reftable_buf buf = REFTABLE_BUF_INIT; - struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, + &opts); const struct reftable_stats *stats = NULL; char message[100] = { 0 }; - int err, i, n; + int i; + int err; struct reftable_log_record log = { .refname = (char *) "refname", .value_type = REFTABLE_LOG_UPDATE, @@ -329,14 +323,11 @@ static void t_log_zlib_corruption(void) reftable_writer_set_limits(w, 1, 1); - err = reftable_writer_add_log(w, &log); - check(!err); - - n = reftable_writer_close(w); - check_int(n, ==, 0); + cl_assert_equal_i(reftable_writer_add_log(w, &log), 0); + cl_assert_equal_i(reftable_writer_close(w), 0); stats = reftable_writer_stats(w); - check_int(stats->log_stats.blocks, >, 0); + cl_assert(stats->log_stats.blocks > 0); reftable_writer_free(w); w = NULL; @@ -346,12 +337,12 @@ static void t_log_zlib_corruption(void) block_source_from_buf(&source, &buf); err = reftable_table_new(&table, &source, "file.log"); - check(!err); + cl_assert(!err); err = reftable_table_init_log_iterator(table, &it); - check(!err); + cl_assert(!err); err = reftable_iterator_seek_log(&it, "refname"); - check_int(err, ==, REFTABLE_ZLIB_ERROR); + cl_assert_equal_i(err, REFTABLE_ZLIB_ERROR); reftable_iterator_destroy(&it); @@ -360,7 +351,7 @@ static void t_log_zlib_corruption(void) reftable_buf_release(&buf); } -static void t_table_read_write_sequential(void) +void test_reftable_readwrite__table_read_write_sequential(void) { char **names; struct reftable_buf buf = REFTABLE_BUF_INIT; @@ -376,24 +367,24 @@ static void t_table_read_write_sequential(void) block_source_from_buf(&source, &buf); err = reftable_table_new(&table, &source, "file.ref"); - check(!err); + cl_assert(!err); err = reftable_table_init_ref_iterator(table, &it); - check(!err); + cl_assert(!err); err = reftable_iterator_seek_ref(&it, ""); - check(!err); + cl_assert(!err); for (j = 0; ; j++) { struct reftable_ref_record ref = { 0 }; int r = reftable_iterator_next_ref(&it, &ref); - check_int(r, >=, 0); + cl_assert(r >= 0); if (r > 0) break; - check_str(names[j], ref.refname); - check_int(update_index, ==, ref.update_index); + cl_assert_equal_s(names[j], ref.refname); + cl_assert_equal_i(update_index, ref.update_index); reftable_ref_record_release(&ref); } - check_int(j, ==, N); + cl_assert_equal_i(j, N); reftable_iterator_destroy(&it); reftable_table_decref(table); @@ -401,42 +392,42 @@ static void t_table_read_write_sequential(void) free_names(names); } -static void t_table_write_small_table(void) +void test_reftable_readwrite__table_write_small_table(void) { char **names; struct reftable_buf buf = REFTABLE_BUF_INIT; int N = 1; write_table(&names, &buf, N, 4096, REFTABLE_HASH_SHA1); - check_int(buf.len, <, 200); + cl_assert(buf.len < 200); reftable_buf_release(&buf); free_names(names); } -static void t_table_read_api(void) +void test_reftable_readwrite__table_read_api(void) { char **names; struct reftable_buf buf = REFTABLE_BUF_INIT; int N = 50; struct reftable_table *table; struct reftable_block_source source = { 0 }; - int err; struct reftable_log_record log = { 0 }; struct reftable_iterator it = { 0 }; + int err; write_table(&names, &buf, N, 256, REFTABLE_HASH_SHA1); block_source_from_buf(&source, &buf); err = reftable_table_new(&table, &source, "file.ref"); - check(!err); + cl_assert(!err); err = reftable_table_init_ref_iterator(table, &it); - check(!err); + cl_assert(!err); err = reftable_iterator_seek_ref(&it, names[0]); - check(!err); + cl_assert(!err); err = reftable_iterator_next_log(&it, &log); - check_int(err, ==, REFTABLE_API_ERROR); + cl_assert_equal_i(err, REFTABLE_API_ERROR); reftable_buf_release(&buf); free_names(names); @@ -464,42 +455,43 @@ static void t_table_read_write_seek(int index, enum reftable_hash hash_id) block_source_from_buf(&source, &buf); err = reftable_table_new(&table, &source, "file.ref"); - check(!err); - check_int(hash_id, ==, reftable_table_hash_id(table)); + cl_assert(!err); + cl_assert_equal_i(hash_id, reftable_table_hash_id(table)); if (!index) { table->ref_offsets.index_offset = 0; } else { - check_int(table->ref_offsets.index_offset, >, 0); + cl_assert(table->ref_offsets.index_offset > 0); } for (i = 1; i < N; i++) { err = reftable_table_init_ref_iterator(table, &it); - check(!err); + cl_assert(!err); err = reftable_iterator_seek_ref(&it, names[i]); - check(!err); + cl_assert(!err); err = reftable_iterator_next_ref(&it, &ref); - check(!err); - check_str(names[i], ref.refname); - check_int(REFTABLE_REF_VAL1, ==, ref.value_type); - check_int(i, ==, ref.value.val1[0]); + cl_assert(!err); + cl_assert_equal_s(names[i], ref.refname); + cl_assert_equal_i(REFTABLE_REF_VAL1, ref.value_type); + cl_assert_equal_i(i, ref.value.val1[0]); reftable_ref_record_release(&ref); reftable_iterator_destroy(&it); } - check(!reftable_buf_addstr(&pastLast, names[N - 1])); - check(!reftable_buf_addstr(&pastLast, "/")); + cl_assert_equal_i(reftable_buf_addstr(&pastLast, names[N - 1]), + 0); + cl_assert_equal_i(reftable_buf_addstr(&pastLast, "/"), 0); err = reftable_table_init_ref_iterator(table, &it); - check(!err); + cl_assert(!err); err = reftable_iterator_seek_ref(&it, pastLast.buf); if (err == 0) { struct reftable_ref_record ref = { 0 }; int err = reftable_iterator_next_ref(&it, &ref); - check_int(err, >, 0); + cl_assert(err > 0); } else { - check_int(err, >, 0); + cl_assert(err > 0); } reftable_buf_release(&pastLast); @@ -510,17 +502,17 @@ static void t_table_read_write_seek(int index, enum reftable_hash hash_id) reftable_table_decref(table); } -static void t_table_read_write_seek_linear(void) +void test_reftable_readwrite__table_read_write_seek_linear(void) { t_table_read_write_seek(0, REFTABLE_HASH_SHA1); } -static void t_table_read_write_seek_linear_sha256(void) +void test_reftable_readwrite__table_read_write_seek_linear_sha256(void) { t_table_read_write_seek(0, REFTABLE_HASH_SHA256); } -static void t_table_read_write_seek_index(void) +void test_reftable_readwrite__table_read_write_seek_index(void) { t_table_read_write_seek(1, REFTABLE_HASH_SHA1); } @@ -538,14 +530,16 @@ static void t_table_refs_for(int indexed) struct reftable_table *table; struct reftable_block_source source = { 0 }; struct reftable_buf buf = REFTABLE_BUF_INIT; - struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, + &opts); struct reftable_iterator it = { 0 }; - int N = 50, n, j, err, i; + int N = 50, j, i; + int err; want_names = reftable_calloc(N + 1, sizeof(*want_names)); - check(want_names != NULL); + cl_assert(want_names != NULL); - t_reftable_set_hash(want_hash, 4, REFTABLE_HASH_SHA1); + cl_reftable_set_hash(want_hash, 4, REFTABLE_HASH_SHA1); for (i = 0; i < N; i++) { uint8_t hash[REFTABLE_HASH_SIZE_SHA1]; @@ -561,24 +555,22 @@ static void t_table_refs_for(int indexed) ref.refname = name; ref.value_type = REFTABLE_REF_VAL2; - t_reftable_set_hash(ref.value.val2.value, i / 4, - REFTABLE_HASH_SHA1); - t_reftable_set_hash(ref.value.val2.target_value, 3 + i / 4, - REFTABLE_HASH_SHA1); + cl_reftable_set_hash(ref.value.val2.value, i / 4, + REFTABLE_HASH_SHA1); + cl_reftable_set_hash(ref.value.val2.target_value, + 3 + i / 4, REFTABLE_HASH_SHA1); /* 80 bytes / entry, so 3 entries per block. Yields 17 */ /* blocks. */ - n = reftable_writer_add_ref(w, &ref); - check_int(n, ==, 0); + cl_assert_equal_i(reftable_writer_add_ref(w, &ref), 0); if (!memcmp(ref.value.val2.value, want_hash, REFTABLE_HASH_SIZE_SHA1) || !memcmp(ref.value.val2.target_value, want_hash, REFTABLE_HASH_SIZE_SHA1)) want_names[want_names_len++] = xstrdup(name); } - n = reftable_writer_close(w); - check_int(n, ==, 0); + cl_assert_equal_i(reftable_writer_close(w), 0); reftable_writer_free(w); w = NULL; @@ -586,29 +578,29 @@ static void t_table_refs_for(int indexed) block_source_from_buf(&source, &buf); err = reftable_table_new(&table, &source, "file.ref"); - check(!err); + cl_assert(!err); if (!indexed) table->obj_offsets.is_present = 0; err = reftable_table_init_ref_iterator(table, &it); - check(!err); + cl_assert(!err); err = reftable_iterator_seek_ref(&it, ""); - check(!err); + cl_assert(!err); reftable_iterator_destroy(&it); err = reftable_table_refs_for(table, &it, want_hash); - check(!err); + cl_assert(!err); for (j = 0; ; j++) { int err = reftable_iterator_next_ref(&it, &ref); - check_int(err, >=, 0); + cl_assert(err >= 0); if (err > 0) break; - check_int(j, <, want_names_len); - check_str(ref.refname, want_names[j]); + cl_assert(j < want_names_len); + cl_assert_equal_s(ref.refname, want_names[j]); reftable_ref_record_release(&ref); } - check_int(j, ==, want_names_len); + cl_assert_equal_i(j, want_names_len); reftable_buf_release(&buf); free_names(want_names); @@ -616,21 +608,21 @@ static void t_table_refs_for(int indexed) reftable_table_decref(table); } -static void t_table_refs_for_no_index(void) +void test_reftable_readwrite__table_refs_for_no_index(void) { t_table_refs_for(0); } -static void t_table_refs_for_obj_index(void) +void test_reftable_readwrite__table_refs_for_obj_index(void) { t_table_refs_for(1); } -static void t_write_empty_table(void) +void test_reftable_readwrite__write_empty_table(void) { struct reftable_write_options opts = { 0 }; struct reftable_buf buf = REFTABLE_BUF_INIT; - struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts); struct reftable_block_source source = { 0 }; struct reftable_table *table = NULL; struct reftable_ref_record rec = { 0 }; @@ -639,43 +631,41 @@ static void t_write_empty_table(void) reftable_writer_set_limits(w, 1, 1); - err = reftable_writer_close(w); - check_int(err, ==, REFTABLE_EMPTY_TABLE_ERROR); + cl_assert_equal_i(reftable_writer_close(w), REFTABLE_EMPTY_TABLE_ERROR); reftable_writer_free(w); - check_uint(buf.len, ==, header_size(1) + footer_size(1)); + cl_assert_equal_i(buf.len, header_size(1) + footer_size(1)); block_source_from_buf(&source, &buf); err = reftable_table_new(&table, &source, "filename"); - check(!err); + cl_assert(!err); err = reftable_table_init_ref_iterator(table, &it); - check(!err); + cl_assert(!err); err = reftable_iterator_seek_ref(&it, ""); - check(!err); + cl_assert(!err); err = reftable_iterator_next_ref(&it, &rec); - check_int(err, >, 0); + cl_assert(err > 0); reftable_iterator_destroy(&it); reftable_table_decref(table); reftable_buf_release(&buf); } -static void t_write_object_id_min_length(void) +void test_reftable_readwrite__write_object_id_min_length(void) { struct reftable_write_options opts = { .block_size = 75, }; struct reftable_buf buf = REFTABLE_BUF_INIT; - struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts); struct reftable_ref_record ref = { .update_index = 1, .value_type = REFTABLE_REF_VAL1, .value.val1 = {42}, }; - int err; int i; reftable_writer_set_limits(w, 1, 1); @@ -686,30 +676,27 @@ static void t_write_object_id_min_length(void) char name[256]; snprintf(name, sizeof(name), "ref%05d", i); ref.refname = name; - err = reftable_writer_add_ref(w, &ref); - check(!err); + cl_assert_equal_i(reftable_writer_add_ref(w, &ref), 0); } - err = reftable_writer_close(w); - check(!err); - check_int(reftable_writer_stats(w)->object_id_len, ==, 2); + cl_assert_equal_i(reftable_writer_close(w), 0); + cl_assert_equal_i(reftable_writer_stats(w)->object_id_len, 2); reftable_writer_free(w); reftable_buf_release(&buf); } -static void t_write_object_id_length(void) +void test_reftable_readwrite__write_object_id_length(void) { struct reftable_write_options opts = { .block_size = 75, }; struct reftable_buf buf = REFTABLE_BUF_INIT; - struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts); struct reftable_ref_record ref = { .update_index = 1, .value_type = REFTABLE_REF_VAL1, .value.val1 = {42}, }; - int err; int i; reftable_writer_set_limits(w, 1, 1); @@ -721,44 +708,39 @@ static void t_write_object_id_length(void) snprintf(name, sizeof(name), "ref%05d", i); ref.refname = name; ref.value.val1[15] = i; - err = reftable_writer_add_ref(w, &ref); - check(!err); + cl_assert(reftable_writer_add_ref(w, &ref) == 0); } - err = reftable_writer_close(w); - check(!err); - check_int(reftable_writer_stats(w)->object_id_len, ==, 16); + cl_assert_equal_i(reftable_writer_close(w), 0); + cl_assert_equal_i(reftable_writer_stats(w)->object_id_len, 16); reftable_writer_free(w); reftable_buf_release(&buf); } -static void t_write_empty_key(void) +void test_reftable_readwrite__write_empty_key(void) { struct reftable_write_options opts = { 0 }; struct reftable_buf buf = REFTABLE_BUF_INIT; - struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts); struct reftable_ref_record ref = { .refname = (char *) "", .update_index = 1, .value_type = REFTABLE_REF_DELETION, }; - int err; reftable_writer_set_limits(w, 1, 1); - err = reftable_writer_add_ref(w, &ref); - check_int(err, ==, REFTABLE_API_ERROR); - - err = reftable_writer_close(w); - check_int(err, ==, REFTABLE_EMPTY_TABLE_ERROR); + cl_assert_equal_i(reftable_writer_add_ref(w, &ref), REFTABLE_API_ERROR); + cl_assert_equal_i(reftable_writer_close(w), + REFTABLE_EMPTY_TABLE_ERROR); reftable_writer_free(w); reftable_buf_release(&buf); } -static void t_write_key_order(void) +void test_reftable_readwrite__write_key_order(void) { struct reftable_write_options opts = { 0 }; struct reftable_buf buf = REFTABLE_BUF_INIT; - struct reftable_writer *w = t_reftable_strbuf_writer(&buf, &opts); + struct reftable_writer *w = cl_reftable_strbuf_writer(&buf, &opts); struct reftable_ref_record refs[2] = { { .refname = (char *) "b", @@ -776,24 +758,21 @@ static void t_write_key_order(void) }, } }; - int err; reftable_writer_set_limits(w, 1, 1); - err = reftable_writer_add_ref(w, &refs[0]); - check(!err); - err = reftable_writer_add_ref(w, &refs[1]); - check_int(err, ==, REFTABLE_API_ERROR); + cl_assert_equal_i(reftable_writer_add_ref(w, &refs[0]), 0); + cl_assert_equal_i(reftable_writer_add_ref(w, &refs[1]), + REFTABLE_API_ERROR); refs[0].update_index = 2; - err = reftable_writer_add_ref(w, &refs[0]); - check_int(err, ==, REFTABLE_API_ERROR); + cl_assert_equal_i(reftable_writer_add_ref(w, &refs[0]), REFTABLE_API_ERROR); reftable_writer_close(w); reftable_writer_free(w); reftable_buf_release(&buf); } -static void t_write_multiple_indices(void) +void test_reftable_readwrite__write_multiple_indices(void) { struct reftable_write_options opts = { .block_size = 100, @@ -805,9 +784,10 @@ static void t_write_multiple_indices(void) struct reftable_writer *writer; struct reftable_table *table; char buf[128]; - int err, i; + int i; + int err; - writer = t_reftable_strbuf_writer(&writer_buf, &opts); + writer = cl_reftable_strbuf_writer(&writer_buf, &opts); reftable_writer_set_limits(writer, 1, 1); for (i = 0; i < 100; i++) { struct reftable_ref_record ref = { @@ -819,8 +799,7 @@ static void t_write_multiple_indices(void) snprintf(buf, sizeof(buf), "refs/heads/%04d", i); ref.refname = buf; - err = reftable_writer_add_ref(writer, &ref); - check(!err); + cl_assert_equal_i(reftable_writer_add_ref(writer, &ref), 0); } for (i = 0; i < 100; i++) { @@ -836,8 +815,7 @@ static void t_write_multiple_indices(void) snprintf(buf, sizeof(buf), "refs/heads/%04d", i); log.refname = buf; - err = reftable_writer_add_log(writer, &log); - check(!err); + cl_assert_equal_i(reftable_writer_add_log(writer, &log), 0); } reftable_writer_close(writer); @@ -847,22 +825,22 @@ static void t_write_multiple_indices(void) * for each of the block types. */ stats = reftable_writer_stats(writer); - check_int(stats->ref_stats.index_offset, >, 0); - check_int(stats->obj_stats.index_offset, >, 0); - check_int(stats->log_stats.index_offset, >, 0); + cl_assert(stats->ref_stats.index_offset > 0); + cl_assert(stats->obj_stats.index_offset > 0); + cl_assert(stats->log_stats.index_offset > 0); block_source_from_buf(&source, &writer_buf); err = reftable_table_new(&table, &source, "filename"); - check(!err); + cl_assert(!err); /* * Seeking the log uses the log index now. In case there is any * confusion regarding indices we would notice here. */ err = reftable_table_init_log_iterator(table, &it); - check(!err); + cl_assert(!err); err = reftable_iterator_seek_log(&it, ""); - check(!err); + cl_assert(!err); reftable_iterator_destroy(&it); reftable_writer_free(writer); @@ -870,7 +848,7 @@ static void t_write_multiple_indices(void) reftable_buf_release(&writer_buf); } -static void t_write_multi_level_index(void) +void test_reftable_readwrite__write_multi_level_index(void) { struct reftable_write_options opts = { .block_size = 100, @@ -883,7 +861,7 @@ static void t_write_multi_level_index(void) struct reftable_table *table; int err; - writer = t_reftable_strbuf_writer(&writer_buf, &opts); + writer = cl_reftable_strbuf_writer(&writer_buf, &opts); reftable_writer_set_limits(writer, 1, 1); for (size_t i = 0; i < 200; i++) { struct reftable_ref_record ref = { @@ -896,8 +874,7 @@ static void t_write_multi_level_index(void) snprintf(buf, sizeof(buf), "refs/heads/%03" PRIuMAX, (uintmax_t)i); ref.refname = buf; - err = reftable_writer_add_ref(writer, &ref); - check(!err); + cl_assert_equal_i(reftable_writer_add_ref(writer, &ref), 0); } reftable_writer_close(writer); @@ -906,19 +883,19 @@ static void t_write_multi_level_index(void) * multi-level index. */ stats = reftable_writer_stats(writer); - check_int(stats->ref_stats.max_index_level, ==, 2); + cl_assert_equal_i(stats->ref_stats.max_index_level, 2); block_source_from_buf(&source, &writer_buf); err = reftable_table_new(&table, &source, "filename"); - check(!err); + cl_assert(!err); /* * Seeking the last ref should work as expected. */ err = reftable_table_init_ref_iterator(table, &it); - check(!err); + cl_assert(!err); err = reftable_iterator_seek_ref(&it, "refs/heads/199"); - check(!err); + cl_assert(!err); reftable_iterator_destroy(&it); reftable_writer_free(writer); @@ -927,7 +904,7 @@ static void t_write_multi_level_index(void) reftable_buf_release(&buf); } -static void t_corrupt_table_empty(void) +void test_reftable_readwrite__corrupt_table_empty(void) { struct reftable_buf buf = REFTABLE_BUF_INIT; struct reftable_block_source source = { 0 }; @@ -936,50 +913,22 @@ static void t_corrupt_table_empty(void) block_source_from_buf(&source, &buf); err = reftable_table_new(&table, &source, "file.log"); - check_int(err, ==, REFTABLE_FORMAT_ERROR); + cl_assert_equal_i(err, REFTABLE_FORMAT_ERROR); } -static void t_corrupt_table(void) +void test_reftable_readwrite__corrupt_table(void) { uint8_t zeros[1024] = { 0 }; struct reftable_buf buf = REFTABLE_BUF_INIT; struct reftable_block_source source = { 0 }; struct reftable_table *table; int err; - check(!reftable_buf_add(&buf, zeros, sizeof(zeros))); + + cl_assert(!reftable_buf_add(&buf, zeros, sizeof(zeros))); block_source_from_buf(&source, &buf); err = reftable_table_new(&table, &source, "file.log"); - check_int(err, ==, REFTABLE_FORMAT_ERROR); + cl_assert_equal_i(err, REFTABLE_FORMAT_ERROR); reftable_buf_release(&buf); } - -int cmd_main(int argc UNUSED, const char *argv[] UNUSED) -{ - TEST(t_buffer(), "strbuf works as blocksource"); - TEST(t_corrupt_table(), "read-write on corrupted table"); - TEST(t_corrupt_table_empty(), "read-write on an empty table"); - TEST(t_log_buffer_size(), "buffer extension for log compression"); - TEST(t_log_overflow(), "log overflow returns expected error"); - TEST(t_log_write_limits(), "writer limits for writing log records"); - TEST(t_log_write_read(), "read-write on log records"); - TEST(t_log_zlib_corruption(), "reading corrupted log record returns expected error"); - TEST(t_table_read_api(), "read on a table"); - TEST(t_table_read_write_seek_index(), "read-write on a table with index"); - TEST(t_table_read_write_seek_linear(), "read-write on a table without index (SHA1)"); - TEST(t_table_read_write_seek_linear_sha256(), "read-write on a table without index (SHA256)"); - TEST(t_table_read_write_sequential(), "sequential read-write on a table"); - TEST(t_table_refs_for_no_index(), "refs-only table with no index"); - TEST(t_table_refs_for_obj_index(), "refs-only table with index"); - TEST(t_table_write_small_table(), "write_table works"); - TEST(t_write_empty_key(), "write on refs with empty keys"); - TEST(t_write_empty_table(), "read-write on empty tables"); - TEST(t_write_key_order(), "refs must be written in increasing order"); - TEST(t_write_multi_level_index(), "table with multi-level index"); - TEST(t_write_multiple_indices(), "table with indices for multiple block types"); - TEST(t_write_object_id_length(), "prefix compression on writing refs"); - TEST(t_write_object_id_min_length(), "prefix compression on writing refs"); - - return test_done(); -} diff --git a/t/unit-tests/t-reftable-record.c b/t/unit-tests/u-reftable-record.c index 553a007664..6c8c0d5374 100644 --- a/t/unit-tests/t-reftable-record.c +++ b/t/unit-tests/u-reftable-record.c @@ -6,7 +6,8 @@ https://developers.google.com/open-source/licenses/bsd */ -#include "test-lib.h" +#include "unit-test.h" +#include "lib-reftable.h" #include "reftable/basics.h" #include "reftable/constants.h" #include "reftable/record.h" @@ -17,16 +18,17 @@ static void t_copy(struct reftable_record *rec) uint8_t typ; typ = reftable_record_type(rec); - check(!reftable_record_init(©, typ)); + cl_assert_equal_i(reftable_record_init(©, typ), 0); reftable_record_copy_from(©, rec, REFTABLE_HASH_SIZE_SHA1); /* do it twice to catch memory leaks */ reftable_record_copy_from(©, rec, REFTABLE_HASH_SIZE_SHA1); - check(reftable_record_equal(rec, ©, REFTABLE_HASH_SIZE_SHA1)); + cl_assert(reftable_record_equal(rec, ©, + REFTABLE_HASH_SIZE_SHA1) != 0); reftable_record_release(©); } -static void t_varint_roundtrip(void) +void test_reftable_record__varint_roundtrip(void) { uint64_t inputs[] = { 0, 1, @@ -49,16 +51,16 @@ static void t_varint_roundtrip(void) int n = put_var_int(&out, in); uint64_t got = 0; - check_int(n, >, 0); + cl_assert(n > 0); out.len = n; n = get_var_int(&got, &out); - check_int(n, >, 0); + cl_assert(n > 0); - check_int(got, ==, in); + cl_assert_equal_i(got, in); } } -static void t_varint_overflow(void) +void test_reftable_record__varint_overflow(void) { unsigned char buf[] = { 0xFF, 0xFF, 0xFF, 0xFF, @@ -70,8 +72,7 @@ static void t_varint_overflow(void) .len = sizeof(buf), }; uint64_t value; - int err = get_var_int(&value, &view); - check_int(err, ==, -1); + cl_assert_equal_i(get_var_int(&value, &view), -1); } static void set_hash(uint8_t *h, int j) @@ -80,7 +81,7 @@ static void set_hash(uint8_t *h, int j) h[i] = (j >> i) & 0xff; } -static void t_reftable_ref_record_comparison(void) +void test_reftable_record__ref_record_comparison(void) { struct reftable_record in[3] = { { @@ -102,21 +103,23 @@ static void t_reftable_ref_record_comparison(void) }; int cmp; - check(!reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1)); - check(!reftable_record_cmp(&in[0], &in[1], &cmp)); - check(!cmp); + cl_assert(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1) == 0); + cl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0); + cl_assert(!cmp); - check(!reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1)); - check(!reftable_record_cmp(&in[1], &in[2], &cmp)); - check_int(cmp, >, 0); + cl_assert(reftable_record_equal(&in[1], &in[2], + REFTABLE_HASH_SIZE_SHA1) == 0); + cl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0); + cl_assert(cmp > 0); in[1].u.ref.value_type = in[0].u.ref.value_type; - check(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1)); - check(!reftable_record_cmp(&in[0], &in[1], &cmp)); - check(!cmp); + cl_assert(reftable_record_equal(&in[0], &in[1], + REFTABLE_HASH_SIZE_SHA1) != 0); + cl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0); + cl_assert(!cmp); } -static void t_reftable_ref_record_compare_name(void) +void test_reftable_record__ref_record_compare_name(void) { struct reftable_ref_record recs[3] = { { @@ -130,12 +133,15 @@ static void t_reftable_ref_record_compare_name(void) }, }; - check_int(reftable_ref_record_compare_name(&recs[0], &recs[1]), <, 0); - check_int(reftable_ref_record_compare_name(&recs[1], &recs[0]), >, 0); - check_int(reftable_ref_record_compare_name(&recs[0], &recs[2]), ==, 0); + cl_assert(reftable_ref_record_compare_name(&recs[0], + &recs[1]) < 0); + cl_assert(reftable_ref_record_compare_name(&recs[1], + &recs[0]) > 0); + cl_assert_equal_i(reftable_ref_record_compare_name(&recs[0], + &recs[2]), 0); } -static void t_reftable_ref_record_roundtrip(void) +void test_reftable_record__ref_record_roundtrip(void) { struct reftable_buf scratch = REFTABLE_BUF_INIT; @@ -172,19 +178,21 @@ static void t_reftable_ref_record_roundtrip(void) t_copy(&in); - check_int(reftable_record_val_type(&in), ==, i); - check_int(reftable_record_is_deletion(&in), ==, i == REFTABLE_REF_DELETION); + cl_assert_equal_i(reftable_record_val_type(&in), i); + cl_assert_equal_i(reftable_record_is_deletion(&in), + i == REFTABLE_REF_DELETION); reftable_record_key(&in, &key); n = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1); - check_int(n, >, 0); + cl_assert(n > 0); /* decode into a non-zero reftable_record to test for leaks. */ m = reftable_record_decode(&out, key, i, dest, REFTABLE_HASH_SIZE_SHA1, &scratch); - check_int(n, ==, m); + cl_assert_equal_i(n, m); - check(reftable_ref_record_equal(&in.u.ref, &out.u.ref, - REFTABLE_HASH_SIZE_SHA1)); + cl_assert(reftable_ref_record_equal(&in.u.ref, + &out.u.ref, + REFTABLE_HASH_SIZE_SHA1) != 0); reftable_record_release(&in); reftable_buf_release(&key); @@ -194,7 +202,7 @@ static void t_reftable_ref_record_roundtrip(void) reftable_buf_release(&scratch); } -static void t_reftable_log_record_comparison(void) +void test_reftable_record__log_record_comparison(void) { struct reftable_record in[3] = { { @@ -215,21 +223,24 @@ static void t_reftable_log_record_comparison(void) }; int cmp; - check(!reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1)); - check(!reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1)); - check(!reftable_record_cmp(&in[1], &in[2], &cmp)); - check_int(cmp, >, 0); + cl_assert_equal_i(reftable_record_equal(&in[0], &in[1], + REFTABLE_HASH_SIZE_SHA1), 0); + cl_assert_equal_i(reftable_record_equal(&in[1], &in[2], + REFTABLE_HASH_SIZE_SHA1), 0); + cl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0); + cl_assert(cmp > 0); /* comparison should be reversed for equal keys, because * comparison is now performed on the basis of update indices */ - check(!reftable_record_cmp(&in[0], &in[1], &cmp)); - check_int(cmp, <, 0); + cl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0); + cl_assert(cmp < 0); in[1].u.log.update_index = in[0].u.log.update_index; - check(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1)); - check(!reftable_record_cmp(&in[0], &in[1], &cmp)); + cl_assert(reftable_record_equal(&in[0], &in[1], + REFTABLE_HASH_SIZE_SHA1) != 0); + cl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0); } -static void t_reftable_log_record_compare_key(void) +void test_reftable_record__log_record_compare_key(void) { struct reftable_log_record logs[3] = { { @@ -246,19 +257,24 @@ static void t_reftable_log_record_compare_key(void) }, }; - check_int(reftable_log_record_compare_key(&logs[0], &logs[1]), <, 0); - check_int(reftable_log_record_compare_key(&logs[1], &logs[0]), >, 0); + cl_assert(reftable_log_record_compare_key(&logs[0], + &logs[1]) < 0); + cl_assert(reftable_log_record_compare_key(&logs[1], + &logs[0]) > 0); logs[1].update_index = logs[0].update_index; - check_int(reftable_log_record_compare_key(&logs[0], &logs[1]), <, 0); + cl_assert(reftable_log_record_compare_key(&logs[0], + &logs[1]) < 0); - check_int(reftable_log_record_compare_key(&logs[0], &logs[2]), >, 0); - check_int(reftable_log_record_compare_key(&logs[2], &logs[0]), <, 0); + cl_assert(reftable_log_record_compare_key(&logs[0], + &logs[2]) > 0); + cl_assert(reftable_log_record_compare_key(&logs[2], + &logs[0]) < 0); logs[2].update_index = logs[0].update_index; - check_int(reftable_log_record_compare_key(&logs[0], &logs[2]), ==, 0); + cl_assert_equal_i(reftable_log_record_compare_key(&logs[0], &logs[2]), 0); } -static void t_reftable_log_record_roundtrip(void) +void test_reftable_record__log_record_roundtrip(void) { struct reftable_log_record in[] = { { @@ -292,9 +308,9 @@ static void t_reftable_log_record_roundtrip(void) set_hash(in[2].value.update.new_hash, 3); set_hash(in[2].value.update.old_hash, 4); - check(!reftable_log_record_is_deletion(&in[0])); - check(reftable_log_record_is_deletion(&in[1])); - check(!reftable_log_record_is_deletion(&in[2])); + cl_assert_equal_i(reftable_log_record_is_deletion(&in[0]), 0); + cl_assert(reftable_log_record_is_deletion(&in[1]) != 0); + cl_assert_equal_i(reftable_log_record_is_deletion(&in[2]), 0); for (size_t i = 0; i < ARRAY_SIZE(in); i++) { struct reftable_record rec = { .type = REFTABLE_BLOCK_TYPE_LOG }; @@ -328,14 +344,14 @@ static void t_reftable_log_record_roundtrip(void) reftable_record_key(&rec, &key); n = reftable_record_encode(&rec, dest, REFTABLE_HASH_SIZE_SHA1); - check_int(n, >=, 0); + cl_assert(n >= 0); valtype = reftable_record_val_type(&rec); m = reftable_record_decode(&out, key, valtype, dest, REFTABLE_HASH_SIZE_SHA1, &scratch); - check_int(n, ==, m); + cl_assert_equal_i(n, m); - check(reftable_log_record_equal(&in[i], &out.u.log, - REFTABLE_HASH_SIZE_SHA1)); + cl_assert(reftable_log_record_equal(&in[i], &out.u.log, + REFTABLE_HASH_SIZE_SHA1) != 0); reftable_log_record_release(&in[i]); reftable_buf_release(&key); reftable_record_release(&out); @@ -344,7 +360,7 @@ static void t_reftable_log_record_roundtrip(void) reftable_buf_release(&scratch); } -static void t_key_roundtrip(void) +void test_reftable_record__key_roundtrip(void) { uint8_t buffer[1024] = { 0 }; struct string_view dest = { @@ -359,25 +375,28 @@ static void t_key_roundtrip(void) int n, m; uint8_t rt_extra; - check(!reftable_buf_addstr(&last_key, "refs/heads/master")); - check(!reftable_buf_addstr(&key, "refs/tags/bla")); + cl_assert_equal_i(reftable_buf_addstr(&last_key, + "refs/heads/master"), 0); + cl_assert_equal_i(reftable_buf_addstr(&key, + "refs/tags/bla"), 0); extra = 6; n = reftable_encode_key(&restart, dest, last_key, key, extra); - check(!restart); - check_int(n, >, 0); + cl_assert(!restart); + cl_assert(n > 0); - check(!reftable_buf_addstr(&roundtrip, "refs/heads/master")); + cl_assert_equal_i(reftable_buf_addstr(&roundtrip, + "refs/heads/master"), 0); m = reftable_decode_key(&roundtrip, &rt_extra, dest); - check_int(n, ==, m); - check(!reftable_buf_cmp(&key, &roundtrip)); - check_int(rt_extra, ==, extra); + cl_assert_equal_i(n, m); + cl_assert_equal_i(reftable_buf_cmp(&key, &roundtrip), 0); + cl_assert_equal_i(rt_extra, extra); reftable_buf_release(&last_key); reftable_buf_release(&key); reftable_buf_release(&roundtrip); } -static void t_reftable_obj_record_comparison(void) +void test_reftable_record__obj_record_comparison(void) { uint8_t id_bytes[] = { 0, 1, 2, 3, 4, 5, 6 }; @@ -405,21 +424,23 @@ static void t_reftable_obj_record_comparison(void) }; int cmp; - check(!reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1)); - check(!reftable_record_cmp(&in[0], &in[1], &cmp)); - check(!cmp); + cl_assert_equal_i(reftable_record_equal(&in[0], &in[1], + REFTABLE_HASH_SIZE_SHA1), 0); + cl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0); + cl_assert(!cmp); - check(!reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1)); - check(!reftable_record_cmp(&in[1], &in[2], &cmp)); - check_int(cmp, >, 0); + cl_assert_equal_i(reftable_record_equal(&in[1], &in[2], + REFTABLE_HASH_SIZE_SHA1), 0); + cl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0); + cl_assert(cmp > 0); in[1].u.obj.offset_len = in[0].u.obj.offset_len; - check(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1)); - check(!reftable_record_cmp(&in[0], &in[1], &cmp)); - check(!cmp); + cl_assert(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1) != 0); + cl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0); + cl_assert(!cmp); } -static void t_reftable_obj_record_roundtrip(void) +void test_reftable_record__obj_record_roundtrip(void) { uint8_t testHash1[REFTABLE_HASH_SIZE_SHA1] = { 1, 2, 3, 4, 0 }; uint64_t till9[] = { 1, 2, 3, 4, 500, 600, 700, 800, 9000 }; @@ -460,17 +481,18 @@ static void t_reftable_obj_record_roundtrip(void) int n, m; uint8_t extra; - check(!reftable_record_is_deletion(&in)); + cl_assert_equal_i(reftable_record_is_deletion(&in), 0); t_copy(&in); reftable_record_key(&in, &key); n = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1); - check_int(n, >, 0); + cl_assert(n > 0); extra = reftable_record_val_type(&in); m = reftable_record_decode(&out, key, extra, dest, REFTABLE_HASH_SIZE_SHA1, &scratch); - check_int(n, ==, m); + cl_assert_equal_i(n, m); - check(reftable_record_equal(&in, &out, REFTABLE_HASH_SIZE_SHA1)); + cl_assert(reftable_record_equal(&in, &out, + REFTABLE_HASH_SIZE_SHA1) != 0); reftable_buf_release(&key); reftable_record_release(&out); } @@ -478,7 +500,7 @@ static void t_reftable_obj_record_roundtrip(void) reftable_buf_release(&scratch); } -static void t_reftable_index_record_comparison(void) +void test_reftable_record__index_record_comparison(void) { struct reftable_record in[3] = { { @@ -499,28 +521,33 @@ static void t_reftable_index_record_comparison(void) }; int cmp; - check(!reftable_buf_addstr(&in[0].u.idx.last_key, "refs/heads/master")); - check(!reftable_buf_addstr(&in[1].u.idx.last_key, "refs/heads/master")); - check(!reftable_buf_addstr(&in[2].u.idx.last_key, "refs/heads/branch")); + cl_assert_equal_i(reftable_buf_addstr(&in[0].u.idx.last_key, + "refs/heads/master"), 0); + cl_assert_equal_i(reftable_buf_addstr(&in[1].u.idx.last_key, "refs/heads/master"), 0); + cl_assert(reftable_buf_addstr(&in[2].u.idx.last_key, + "refs/heads/branch") == 0); - check(!reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1)); - check(!reftable_record_cmp(&in[0], &in[1], &cmp)); - check(!cmp); + cl_assert_equal_i(reftable_record_equal(&in[0], &in[1], + REFTABLE_HASH_SIZE_SHA1), 0); + cl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0); + cl_assert(!cmp); - check(!reftable_record_equal(&in[1], &in[2], REFTABLE_HASH_SIZE_SHA1)); - check(!reftable_record_cmp(&in[1], &in[2], &cmp)); - check_int(cmp, >, 0); + cl_assert_equal_i(reftable_record_equal(&in[1], &in[2], + REFTABLE_HASH_SIZE_SHA1), 0); + cl_assert_equal_i(reftable_record_cmp(&in[1], &in[2], &cmp), 0); + cl_assert(cmp > 0); in[1].u.idx.offset = in[0].u.idx.offset; - check(reftable_record_equal(&in[0], &in[1], REFTABLE_HASH_SIZE_SHA1)); - check(!reftable_record_cmp(&in[0], &in[1], &cmp)); - check(!cmp); + cl_assert(reftable_record_equal(&in[0], &in[1], + REFTABLE_HASH_SIZE_SHA1) != 0); + cl_assert_equal_i(reftable_record_cmp(&in[0], &in[1], &cmp), 0); + cl_assert(!cmp); for (size_t i = 0; i < ARRAY_SIZE(in); i++) reftable_record_release(&in[i]); } -static void t_reftable_index_record_roundtrip(void) +void test_reftable_record__index_record_roundtrip(void) { struct reftable_record in = { .type = REFTABLE_BLOCK_TYPE_INDEX, @@ -543,43 +570,26 @@ static void t_reftable_index_record_roundtrip(void) int n, m; uint8_t extra; - check(!reftable_buf_addstr(&in.u.idx.last_key, "refs/heads/master")); + cl_assert_equal_i(reftable_buf_addstr(&in.u.idx.last_key, + "refs/heads/master"), 0); reftable_record_key(&in, &key); t_copy(&in); - check(!reftable_record_is_deletion(&in)); - check(!reftable_buf_cmp(&key, &in.u.idx.last_key)); + cl_assert_equal_i(reftable_record_is_deletion(&in), 0); + cl_assert_equal_i(reftable_buf_cmp(&key, &in.u.idx.last_key), 0); n = reftable_record_encode(&in, dest, REFTABLE_HASH_SIZE_SHA1); - check_int(n, >, 0); + cl_assert(n > 0); extra = reftable_record_val_type(&in); - m = reftable_record_decode(&out, key, extra, dest, REFTABLE_HASH_SIZE_SHA1, - &scratch); - check_int(m, ==, n); + m = reftable_record_decode(&out, key, extra, dest, + REFTABLE_HASH_SIZE_SHA1, &scratch); + cl_assert_equal_i(m, n); - check(reftable_record_equal(&in, &out, REFTABLE_HASH_SIZE_SHA1)); + cl_assert(reftable_record_equal(&in, &out, + REFTABLE_HASH_SIZE_SHA1) != 0); reftable_record_release(&out); reftable_buf_release(&key); reftable_buf_release(&scratch); reftable_buf_release(&in.u.idx.last_key); } - -int cmd_main(int argc UNUSED, const char *argv[] UNUSED) -{ - TEST(t_reftable_ref_record_comparison(), "comparison operations work on ref record"); - TEST(t_reftable_log_record_comparison(), "comparison operations work on log record"); - TEST(t_reftable_index_record_comparison(), "comparison operations work on index record"); - TEST(t_reftable_obj_record_comparison(), "comparison operations work on obj record"); - TEST(t_reftable_ref_record_compare_name(), "reftable_ref_record_compare_name works"); - TEST(t_reftable_log_record_compare_key(), "reftable_log_record_compare_key works"); - TEST(t_reftable_log_record_roundtrip(), "record operations work on log record"); - TEST(t_reftable_ref_record_roundtrip(), "record operations work on ref record"); - TEST(t_varint_roundtrip(), "put_var_int and get_var_int work"); - TEST(t_varint_overflow(), "get_var_int notices an integer overflow"); - TEST(t_key_roundtrip(), "reftable_encode_key and reftable_decode_key work"); - TEST(t_reftable_obj_record_roundtrip(), "record operations work on obj record"); - TEST(t_reftable_index_record_roundtrip(), "record operations work on index record"); - - return test_done(); -} diff --git a/t/unit-tests/t-reftable-stack.c b/t/unit-tests/u-reftable-stack.c index 2f49c97519..a8b91812e8 100644 --- a/t/unit-tests/t-reftable-stack.c +++ b/t/unit-tests/u-reftable-stack.c @@ -8,9 +8,9 @@ https://developers.google.com/open-source/licenses/bsd #define DISABLE_SIGN_COMPARE_WARNINGS -#include "test-lib.h" -#include "lib-reftable.h" +#include "unit-test.h" #include "dir.h" +#include "lib-reftable.h" #include "reftable/merged.h" #include "reftable/reftable-error.h" #include "reftable/stack.h" @@ -70,11 +70,11 @@ static char *get_tmp_template(int linenumber) static char *get_tmp_dir(int linenumber) { char *dir = get_tmp_template(linenumber); - check(mkdtemp(dir) != NULL); + cl_assert(mkdtemp(dir) != NULL); return dir; } -static void t_read_file(void) +void test_reftable_stack__read_file(void) { char *fn = get_tmp_template(__LINE__); struct tempfile *tmp = mks_tempfile(fn); @@ -84,17 +84,17 @@ static void t_read_file(void) char **names = NULL; const char *want[] = { "line1", "line2", "line3" }; - check_int(fd, >, 0); + cl_assert(fd > 0); n = write_in_full(fd, out, strlen(out)); - check_int(n, ==, strlen(out)); + cl_assert_equal_i(n, strlen(out)); err = close(fd); - check_int(err, >=, 0); + cl_assert(err >= 0); err = read_lines(fn, &names); - check(!err); + cl_assert(!err); for (size_t i = 0; names[i]; i++) - check_str(want[i], names[i]); + cl_assert_equal_s(want[i], names[i]); free_names(names); (void) remove(fn); delete_tempfile(&tmp); @@ -103,8 +103,8 @@ static void t_read_file(void) static int write_test_ref(struct reftable_writer *wr, void *arg) { struct reftable_ref_record *ref = arg; - check(!reftable_writer_set_limits(wr, ref->update_index, - ref->update_index)); + cl_assert_equal_i(reftable_writer_set_limits(wr, + ref->update_index, ref->update_index), 0); return reftable_writer_add_ref(wr, ref); } @@ -112,7 +112,6 @@ static void write_n_ref_tables(struct reftable_stack *st, size_t n) { int disable_auto_compact; - int err; disable_auto_compact = st->opts.disable_auto_compact; st->opts.disable_auto_compact = 1; @@ -126,10 +125,10 @@ static void write_n_ref_tables(struct reftable_stack *st, snprintf(buf, sizeof(buf), "refs/heads/branch-%04"PRIuMAX, (uintmax_t)i); ref.refname = buf; - t_reftable_set_hash(ref.value.val1, i, REFTABLE_HASH_SHA1); + cl_reftable_set_hash(ref.value.val1, i, REFTABLE_HASH_SHA1); - err = reftable_stack_add(st, &write_test_ref, &ref); - check(!err); + cl_assert_equal_i(reftable_stack_add(st, + &write_test_ref, &ref, 0), 0); } st->opts.disable_auto_compact = disable_auto_compact; @@ -144,12 +143,13 @@ static int write_test_log(struct reftable_writer *wr, void *arg) { struct write_log_arg *wla = arg; - check(!reftable_writer_set_limits(wr, wla->update_index, - wla->update_index)); + cl_assert_equal_i(reftable_writer_set_limits(wr, + wla->update_index, + wla->update_index), 0); return reftable_writer_add_log(wr, wla->log); } -static void t_reftable_stack_add_one(void) +void test_reftable_stack__add_one(void) { char *dir = get_tmp_dir(__LINE__); struct reftable_buf scratch = REFTABLE_BUF_INIT; @@ -158,7 +158,6 @@ static void t_reftable_stack_add_one(void) .default_permissions = 0660, }; struct reftable_stack *st = NULL; - int err; struct reftable_ref_record ref = { .refname = (char *) "HEAD", .update_index = 1, @@ -167,32 +166,37 @@ static void t_reftable_stack_add_one(void) }; struct reftable_ref_record dest = { 0 }; struct stat stat_result = { 0 }; + int err; + err = reftable_new_stack(&st, dir, &opts); - check(!err); + cl_assert(!err); - err = reftable_stack_add(st, write_test_ref, &ref); - check(!err); + err = reftable_stack_add(st, write_test_ref, &ref, 0); + cl_assert(!err); err = reftable_stack_read_ref(st, ref.refname, &dest); - check(!err); - check(reftable_ref_record_equal(&ref, &dest, REFTABLE_HASH_SIZE_SHA1)); - check_int(st->tables_len, >, 0); + cl_assert(!err); + cl_assert(reftable_ref_record_equal(&ref, &dest, + REFTABLE_HASH_SIZE_SHA1)); + cl_assert(st->tables_len > 0); #ifndef GIT_WINDOWS_NATIVE - check(!reftable_buf_addstr(&scratch, dir)); - check(!reftable_buf_addstr(&scratch, "/tables.list")); - err = stat(scratch.buf, &stat_result); - check(!err); - check_int((stat_result.st_mode & 0777), ==, opts.default_permissions); + cl_assert_equal_i(reftable_buf_addstr(&scratch, dir), 0); + cl_assert_equal_i(reftable_buf_addstr(&scratch, + "/tables.list"), 0); + cl_assert_equal_i(stat(scratch.buf, &stat_result), 0); + cl_assert_equal_i((stat_result.st_mode & 0777), + opts.default_permissions); reftable_buf_reset(&scratch); - check(!reftable_buf_addstr(&scratch, dir)); - check(!reftable_buf_addstr(&scratch, "/")); + cl_assert_equal_i(reftable_buf_addstr(&scratch, dir), 0); + cl_assert_equal_i(reftable_buf_addstr(&scratch, "/"), 0); /* do not try at home; not an external API for reftable. */ - check(!reftable_buf_addstr(&scratch, st->tables[0]->name)); + cl_assert(!reftable_buf_addstr(&scratch, st->tables[0]->name)); err = stat(scratch.buf, &stat_result); - check(!err); - check_int((stat_result.st_mode & 0777), ==, opts.default_permissions); + cl_assert(!err); + cl_assert_equal_i((stat_result.st_mode & 0777), + opts.default_permissions); #else (void) stat_result; #endif @@ -204,14 +208,13 @@ static void t_reftable_stack_add_one(void) umask(mask); } -static void t_reftable_stack_uptodate(void) +void test_reftable_stack__uptodate(void) { struct reftable_write_options opts = { 0 }; struct reftable_stack *st1 = NULL; struct reftable_stack *st2 = NULL; char *dir = get_tmp_dir(__LINE__); - int err; struct reftable_ref_record ref1 = { .refname = (char *) "HEAD", .update_index = 1, @@ -229,34 +232,25 @@ static void t_reftable_stack_uptodate(void) /* simulate multi-process access to the same stack by creating two stacks for the same directory. */ - err = reftable_new_stack(&st1, dir, &opts); - check(!err); - - err = reftable_new_stack(&st2, dir, &opts); - check(!err); - - err = reftable_stack_add(st1, write_test_ref, &ref1); - check(!err); - - err = reftable_stack_add(st2, write_test_ref, &ref2); - check_int(err, ==, REFTABLE_OUTDATED_ERROR); - - err = reftable_stack_reload(st2); - check(!err); - - err = reftable_stack_add(st2, write_test_ref, &ref2); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st1, dir, &opts), 0); + cl_assert_equal_i(reftable_new_stack(&st2, dir, &opts), 0); + cl_assert_equal_i(reftable_stack_add(st1, write_test_ref, + &ref1, 0), 0); + cl_assert_equal_i(reftable_stack_add(st2, write_test_ref, + &ref2, 0), REFTABLE_OUTDATED_ERROR); + cl_assert_equal_i(reftable_stack_reload(st2), 0); + cl_assert_equal_i(reftable_stack_add(st2, write_test_ref, + &ref2, 0), 0); reftable_stack_destroy(st1); reftable_stack_destroy(st2); clear_dir(dir); } -static void t_reftable_stack_transaction_api(void) +void test_reftable_stack__transaction_api(void) { char *dir = get_tmp_dir(__LINE__); struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; - int err; struct reftable_addition *add = NULL; struct reftable_ref_record ref = { @@ -267,37 +261,32 @@ static void t_reftable_stack_transaction_api(void) }; struct reftable_ref_record dest = { 0 }; - err = reftable_new_stack(&st, dir, &opts); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); reftable_addition_destroy(add); - err = reftable_stack_new_addition(&add, st, 0); - check(!err); - - err = reftable_addition_add(add, write_test_ref, &ref); - check(!err); - - err = reftable_addition_commit(add); - check(!err); + cl_assert_equal_i(reftable_stack_new_addition(&add, st, 0), 0); + cl_assert_equal_i(reftable_addition_add(add, write_test_ref, + &ref), 0); + cl_assert_equal_i(reftable_addition_commit(add), 0); reftable_addition_destroy(add); - err = reftable_stack_read_ref(st, ref.refname, &dest); - check(!err); - check_int(REFTABLE_REF_SYMREF, ==, dest.value_type); - check(reftable_ref_record_equal(&ref, &dest, REFTABLE_HASH_SIZE_SHA1)); + cl_assert_equal_i(reftable_stack_read_ref(st, ref.refname, + &dest), 0); + cl_assert_equal_i(REFTABLE_REF_SYMREF, dest.value_type); + cl_assert(reftable_ref_record_equal(&ref, &dest, + REFTABLE_HASH_SIZE_SHA1) != 0); reftable_ref_record_release(&dest); reftable_stack_destroy(st); clear_dir(dir); } -static void t_reftable_stack_transaction_with_reload(void) +void test_reftable_stack__transaction_with_reload(void) { char *dir = get_tmp_dir(__LINE__); struct reftable_stack *st1 = NULL, *st2 = NULL; - int err; struct reftable_addition *add = NULL; struct reftable_ref_record refs[2] = { { @@ -315,17 +304,12 @@ static void t_reftable_stack_transaction_with_reload(void) }; struct reftable_ref_record ref = { 0 }; - err = reftable_new_stack(&st1, dir, NULL); - check(!err); - err = reftable_new_stack(&st2, dir, NULL); - check(!err); - - err = reftable_stack_new_addition(&add, st1, 0); - check(!err); - err = reftable_addition_add(add, write_test_ref, &refs[0]); - check(!err); - err = reftable_addition_commit(add); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st1, dir, NULL), 0); + cl_assert_equal_i(reftable_new_stack(&st2, dir, NULL), 0); + cl_assert_equal_i(reftable_stack_new_addition(&add, st1, 0), 0); + cl_assert_equal_i(reftable_addition_add(add, write_test_ref, + &refs[0]), 0); + cl_assert_equal_i(reftable_addition_commit(add), 0); reftable_addition_destroy(add); /* @@ -333,20 +317,20 @@ static void t_reftable_stack_transaction_with_reload(void) * create the addition and lock the stack by default, but allow the * reload to happen when REFTABLE_STACK_NEW_ADDITION_RELOAD is set. */ - err = reftable_stack_new_addition(&add, st2, 0); - check_int(err, ==, REFTABLE_OUTDATED_ERROR); - err = reftable_stack_new_addition(&add, st2, REFTABLE_STACK_NEW_ADDITION_RELOAD); - check(!err); - err = reftable_addition_add(add, write_test_ref, &refs[1]); - check(!err); - err = reftable_addition_commit(add); - check(!err); + cl_assert_equal_i(reftable_stack_new_addition(&add, st2, 0), + REFTABLE_OUTDATED_ERROR); + cl_assert_equal_i(reftable_stack_new_addition(&add, st2, + REFTABLE_STACK_NEW_ADDITION_RELOAD), 0); + cl_assert_equal_i(reftable_addition_add(add, write_test_ref, + &refs[1]), 0); + cl_assert_equal_i(reftable_addition_commit(add), 0); reftable_addition_destroy(add); for (size_t i = 0; i < ARRAY_SIZE(refs); i++) { - err = reftable_stack_read_ref(st2, refs[i].refname, &ref); - check(!err); - check(reftable_ref_record_equal(&refs[i], &ref, REFTABLE_HASH_SIZE_SHA1)); + cl_assert_equal_i(reftable_stack_read_ref(st2, + refs[i].refname, &ref) , 0); + cl_assert(reftable_ref_record_equal(&refs[i], &ref, + REFTABLE_HASH_SIZE_SHA1) != 0); } reftable_ref_record_release(&ref); @@ -355,17 +339,15 @@ static void t_reftable_stack_transaction_with_reload(void) clear_dir(dir); } -static void t_reftable_stack_transaction_api_performs_auto_compaction(void) +void test_reftable_stack__transaction_api_performs_auto_compaction(void) { char *dir = get_tmp_dir(__LINE__); struct reftable_write_options opts = {0}; struct reftable_addition *add = NULL; struct reftable_stack *st = NULL; size_t n = 20; - int err; - err = reftable_new_stack(&st, dir, &opts); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); for (size_t i = 0; i <= n; i++) { struct reftable_ref_record ref = { @@ -385,14 +367,11 @@ static void t_reftable_stack_transaction_api_performs_auto_compaction(void) */ st->opts.disable_auto_compact = i != n; - err = reftable_stack_new_addition(&add, st, 0); - check(!err); - - err = reftable_addition_add(add, write_test_ref, &ref); - check(!err); - - err = reftable_addition_commit(add); - check(!err); + cl_assert_equal_i(reftable_stack_new_addition(&add, + st, 0), 0); + cl_assert_equal_i(reftable_addition_add(add, + write_test_ref, &ref), 0); + cl_assert_equal_i(reftable_addition_commit(add), 0); reftable_addition_destroy(add); @@ -402,16 +381,16 @@ static void t_reftable_stack_transaction_api_performs_auto_compaction(void) * all tables in the stack. */ if (i != n) - check_int(st->merged->tables_len, ==, i + 1); + cl_assert_equal_i(st->merged->tables_len, i + 1); else - check_int(st->merged->tables_len, ==, 1); + cl_assert_equal_i(st->merged->tables_len, 1); } reftable_stack_destroy(st); clear_dir(dir); } -static void t_reftable_stack_auto_compaction_fails_gracefully(void) +void test_reftable_stack__auto_compaction_fails_gracefully(void) { struct reftable_ref_record ref = { .refname = (char *) "refs/heads/master", @@ -425,32 +404,31 @@ static void t_reftable_stack_auto_compaction_fails_gracefully(void) char *dir = get_tmp_dir(__LINE__); int err; - err = reftable_new_stack(&st, dir, &opts); - check(!err); - - err = reftable_stack_add(st, write_test_ref, &ref); - check(!err); - check_int(st->merged->tables_len, ==, 1); - check_int(st->stats.attempts, ==, 0); - check_int(st->stats.failures, ==, 0); + cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_stack_add(st, write_test_ref, + &ref, 0), 0); + cl_assert_equal_i(st->merged->tables_len, 1); + cl_assert_equal_i(st->stats.attempts, 0); + cl_assert_equal_i(st->stats.failures, 0); /* * Lock the newly written table such that it cannot be compacted. * Adding a new table to the stack should not be impacted by this, even * though auto-compaction will now fail. */ - check(!reftable_buf_addstr(&table_path, dir)); - check(!reftable_buf_addstr(&table_path, "/")); - check(!reftable_buf_addstr(&table_path, st->tables[0]->name)); - check(!reftable_buf_addstr(&table_path, ".lock")); + cl_assert(!reftable_buf_addstr(&table_path, dir)); + cl_assert(!reftable_buf_addstr(&table_path, "/")); + cl_assert(!reftable_buf_addstr(&table_path, + st->tables[0]->name)); + cl_assert(!reftable_buf_addstr(&table_path, ".lock")); write_file_buf(table_path.buf, "", 0); ref.update_index = 2; - err = reftable_stack_add(st, write_test_ref, &ref); - check(!err); - check_int(st->merged->tables_len, ==, 2); - check_int(st->stats.attempts, ==, 1); - check_int(st->stats.failures, ==, 1); + err = reftable_stack_add(st, write_test_ref, &ref, 0); + cl_assert(!err); + cl_assert_equal_i(st->merged->tables_len, 2); + cl_assert_equal_i(st->stats.attempts, 1); + cl_assert_equal_i(st->stats.failures, 1); reftable_stack_destroy(st); reftable_buf_release(&table_path); @@ -462,12 +440,11 @@ static int write_error(struct reftable_writer *wr UNUSED, void *arg) return *((int *)arg); } -static void t_reftable_stack_update_index_check(void) +void test_reftable_stack__update_index_check(void) { char *dir = get_tmp_dir(__LINE__); struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; - int err; struct reftable_ref_record ref1 = { .refname = (char *) "name1", .update_index = 1, @@ -481,39 +458,33 @@ static void t_reftable_stack_update_index_check(void) .value.symref = (char *) "master", }; - err = reftable_new_stack(&st, dir, &opts); - check(!err); - - err = reftable_stack_add(st, write_test_ref, &ref1); - check(!err); - - err = reftable_stack_add(st, write_test_ref, &ref2); - check_int(err, ==, REFTABLE_API_ERROR); + cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_stack_add(st, write_test_ref, + &ref1, 0), 0); + cl_assert_equal_i(reftable_stack_add(st, write_test_ref, + &ref2, 0), REFTABLE_API_ERROR); reftable_stack_destroy(st); clear_dir(dir); } -static void t_reftable_stack_lock_failure(void) +void test_reftable_stack__lock_failure(void) { char *dir = get_tmp_dir(__LINE__); struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; - int err, i; + int i; - err = reftable_new_stack(&st, dir, &opts); - check(!err); - for (i = -1; i != REFTABLE_EMPTY_TABLE_ERROR; i--) { - err = reftable_stack_add(st, write_error, &i); - check_int(err, ==, i); - } + cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + for (i = -1; i != REFTABLE_EMPTY_TABLE_ERROR; i--) + cl_assert_equal_i(reftable_stack_add(st, write_error, + &i, 0), i); reftable_stack_destroy(st); clear_dir(dir); } -static void t_reftable_stack_add(void) +void test_reftable_stack__add(void) { - int err = 0; struct reftable_write_options opts = { .exact_log_message = 1, .default_permissions = 0660, @@ -526,9 +497,10 @@ static void t_reftable_stack_add(void) struct reftable_buf path = REFTABLE_BUF_INIT; struct stat stat_result; size_t i, N = ARRAY_SIZE(refs); + int err = 0; err = reftable_new_stack(&st, dir, &opts); - check(!err); + cl_assert(!err); for (i = 0; i < N; i++) { char buf[256]; @@ -536,66 +508,66 @@ static void t_reftable_stack_add(void) refs[i].refname = xstrdup(buf); refs[i].update_index = i + 1; refs[i].value_type = REFTABLE_REF_VAL1; - t_reftable_set_hash(refs[i].value.val1, i, REFTABLE_HASH_SHA1); + cl_reftable_set_hash(refs[i].value.val1, i, + REFTABLE_HASH_SHA1); logs[i].refname = xstrdup(buf); logs[i].update_index = N + i + 1; logs[i].value_type = REFTABLE_LOG_UPDATE; logs[i].value.update.email = xstrdup("identity@invalid"); - t_reftable_set_hash(logs[i].value.update.new_hash, i, REFTABLE_HASH_SHA1); + cl_reftable_set_hash(logs[i].value.update.new_hash, i, + REFTABLE_HASH_SHA1); } - for (i = 0; i < N; i++) { - int err = reftable_stack_add(st, write_test_ref, &refs[i]); - check(!err); - } + for (i = 0; i < N; i++) + cl_assert_equal_i(reftable_stack_add(st, write_test_ref, + &refs[i], 0), 0); for (i = 0; i < N; i++) { struct write_log_arg arg = { .log = &logs[i], .update_index = reftable_stack_next_update_index(st), }; - int err = reftable_stack_add(st, write_test_log, &arg); - check(!err); + cl_assert_equal_i(reftable_stack_add(st, write_test_log, + &arg, 0), 0); } - err = reftable_stack_compact_all(st, NULL); - check(!err); + cl_assert_equal_i(reftable_stack_compact_all(st, NULL), 0); for (i = 0; i < N; i++) { struct reftable_ref_record dest = { 0 }; - int err = reftable_stack_read_ref(st, refs[i].refname, &dest); - check(!err); - check(reftable_ref_record_equal(&dest, refs + i, - REFTABLE_HASH_SIZE_SHA1)); + cl_assert_equal_i(reftable_stack_read_ref(st, + refs[i].refname, &dest), 0); + cl_assert(reftable_ref_record_equal(&dest, refs + i, + REFTABLE_HASH_SIZE_SHA1) != 0); reftable_ref_record_release(&dest); } for (i = 0; i < N; i++) { struct reftable_log_record dest = { 0 }; - int err = reftable_stack_read_log(st, refs[i].refname, &dest); - check(!err); - check(reftable_log_record_equal(&dest, logs + i, - REFTABLE_HASH_SIZE_SHA1)); + cl_assert_equal_i(reftable_stack_read_log(st, + refs[i].refname, &dest), 0); + cl_assert(reftable_log_record_equal(&dest, logs + i, + REFTABLE_HASH_SIZE_SHA1) != 0); reftable_log_record_release(&dest); } #ifndef GIT_WINDOWS_NATIVE - check(!reftable_buf_addstr(&path, dir)); - check(!reftable_buf_addstr(&path, "/tables.list")); - err = stat(path.buf, &stat_result); - check(!err); - check_int((stat_result.st_mode & 0777), ==, opts.default_permissions); + cl_assert_equal_i(reftable_buf_addstr(&path, dir), 0); + cl_assert_equal_i(reftable_buf_addstr(&path, "/tables.list"), 0); + cl_assert_equal_i(stat(path.buf, &stat_result), 0); + cl_assert_equal_i((stat_result.st_mode & 0777), opts.default_permissions); reftable_buf_reset(&path); - check(!reftable_buf_addstr(&path, dir)); - check(!reftable_buf_addstr(&path, "/")); + cl_assert_equal_i(reftable_buf_addstr(&path, dir), 0); + cl_assert_equal_i(reftable_buf_addstr(&path, "/"), 0); /* do not try at home; not an external API for reftable. */ - check(!reftable_buf_addstr(&path, st->tables[0]->name)); + cl_assert(!reftable_buf_addstr(&path, st->tables[0]->name)); err = stat(path.buf, &stat_result); - check(!err); - check_int((stat_result.st_mode & 0777), ==, opts.default_permissions); + cl_assert(!err); + cl_assert_equal_i((stat_result.st_mode & 0777), + opts.default_permissions); #else (void) stat_result; #endif @@ -610,7 +582,7 @@ static void t_reftable_stack_add(void) clear_dir(dir); } -static void t_reftable_stack_iterator(void) +void test_reftable_stack__iterator(void) { struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; @@ -621,27 +593,27 @@ static void t_reftable_stack_iterator(void) size_t N = ARRAY_SIZE(refs), i; int err; - err = reftable_new_stack(&st, dir, &opts); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); for (i = 0; i < N; i++) { refs[i].refname = xstrfmt("branch%02"PRIuMAX, (uintmax_t)i); refs[i].update_index = i + 1; refs[i].value_type = REFTABLE_REF_VAL1; - t_reftable_set_hash(refs[i].value.val1, i, REFTABLE_HASH_SHA1); + cl_reftable_set_hash(refs[i].value.val1, i, + REFTABLE_HASH_SHA1); logs[i].refname = xstrfmt("branch%02"PRIuMAX, (uintmax_t)i); logs[i].update_index = i + 1; logs[i].value_type = REFTABLE_LOG_UPDATE; logs[i].value.update.email = xstrdup("johndoe@invalid"); logs[i].value.update.message = xstrdup("commit\n"); - t_reftable_set_hash(logs[i].value.update.new_hash, i, REFTABLE_HASH_SHA1); + cl_reftable_set_hash(logs[i].value.update.new_hash, i, + REFTABLE_HASH_SHA1); } - for (i = 0; i < N; i++) { - err = reftable_stack_add(st, write_test_ref, &refs[i]); - check(!err); - } + for (i = 0; i < N; i++) + cl_assert_equal_i(reftable_stack_add(st, write_test_ref, + &refs[i], 0), 0); for (i = 0; i < N; i++) { struct write_log_arg arg = { @@ -649,8 +621,8 @@ static void t_reftable_stack_iterator(void) .update_index = reftable_stack_next_update_index(st), }; - err = reftable_stack_add(st, write_test_log, &arg); - check(!err); + cl_assert_equal_i(reftable_stack_add(st, write_test_log, + &arg, 0), 0); } reftable_stack_init_ref_iterator(st, &it); @@ -661,16 +633,16 @@ static void t_reftable_stack_iterator(void) err = reftable_iterator_next_ref(&it, &ref); if (err > 0) break; - check(!err); - check(reftable_ref_record_equal(&ref, &refs[i], REFTABLE_HASH_SIZE_SHA1)); + cl_assert(!err); + cl_assert(reftable_ref_record_equal(&ref, &refs[i], + REFTABLE_HASH_SIZE_SHA1) != 0); reftable_ref_record_release(&ref); } - check_int(i, ==, N); + cl_assert_equal_i(i, N); reftable_iterator_destroy(&it); - err = reftable_stack_init_log_iterator(st, &it); - check(!err); + cl_assert_equal_i(reftable_stack_init_log_iterator(st, &it), 0); reftable_iterator_seek_log(&it, logs[0].refname); for (i = 0; ; i++) { @@ -679,11 +651,12 @@ static void t_reftable_stack_iterator(void) err = reftable_iterator_next_log(&it, &log); if (err > 0) break; - check(!err); - check(reftable_log_record_equal(&log, &logs[i], REFTABLE_HASH_SIZE_SHA1)); + cl_assert(!err); + cl_assert(reftable_log_record_equal(&log, &logs[i], + REFTABLE_HASH_SIZE_SHA1) != 0); reftable_log_record_release(&log); } - check_int(i, ==, N); + cl_assert_equal_i(i, N); reftable_stack_destroy(st); reftable_iterator_destroy(&it); @@ -694,9 +667,8 @@ static void t_reftable_stack_iterator(void) clear_dir(dir); } -static void t_reftable_stack_log_normalize(void) +void test_reftable_stack__log_normalize(void) { - int err = 0; struct reftable_write_options opts = { 0, }; @@ -721,28 +693,26 @@ static void t_reftable_stack_log_normalize(void) .update_index = 1, }; - err = reftable_new_stack(&st, dir, &opts); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); input.value.update.message = (char *) "one\ntwo"; - err = reftable_stack_add(st, write_test_log, &arg); - check_int(err, ==, REFTABLE_API_ERROR); + cl_assert_equal_i(reftable_stack_add(st, write_test_log, + &arg, 0), REFTABLE_API_ERROR); input.value.update.message = (char *) "one"; - err = reftable_stack_add(st, write_test_log, &arg); - check(!err); - - err = reftable_stack_read_log(st, input.refname, &dest); - check(!err); - check_str(dest.value.update.message, "one\n"); + cl_assert_equal_i(reftable_stack_add(st, write_test_log, + &arg, 0), 0); + cl_assert_equal_i(reftable_stack_read_log(st, input.refname, + &dest), 0); + cl_assert_equal_s(dest.value.update.message, "one\n"); input.value.update.message = (char *) "two\n"; arg.update_index = 2; - err = reftable_stack_add(st, write_test_log, &arg); - check(!err); - err = reftable_stack_read_log(st, input.refname, &dest); - check(!err); - check_str(dest.value.update.message, "two\n"); + cl_assert_equal_i(reftable_stack_add(st, write_test_log, + &arg, 0), 0); + cl_assert_equal_i(reftable_stack_read_log(st, input.refname, + &dest), 0); + cl_assert_equal_s(dest.value.update.message, "two\n"); /* cleanup */ reftable_stack_destroy(st); @@ -750,20 +720,18 @@ static void t_reftable_stack_log_normalize(void) clear_dir(dir); } -static void t_reftable_stack_tombstone(void) +void test_reftable_stack__tombstone(void) { char *dir = get_tmp_dir(__LINE__); struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; - int err; struct reftable_ref_record refs[2] = { 0 }; struct reftable_log_record logs[2] = { 0 }; size_t i, N = ARRAY_SIZE(refs); struct reftable_ref_record dest = { 0 }; struct reftable_log_record log_dest = { 0 }; - err = reftable_new_stack(&st, dir, &opts); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); /* even entries add the refs, odd entries delete them. */ for (i = 0; i < N; i++) { @@ -772,8 +740,8 @@ static void t_reftable_stack_tombstone(void) refs[i].update_index = i + 1; if (i % 2 == 0) { refs[i].value_type = REFTABLE_REF_VAL1; - t_reftable_set_hash(refs[i].value.val1, i, - REFTABLE_HASH_SHA1); + cl_reftable_set_hash(refs[i].value.val1, i, + REFTABLE_HASH_SHA1); } logs[i].refname = xstrdup(buf); @@ -785,42 +753,37 @@ static void t_reftable_stack_tombstone(void) logs[i].update_index = 1; if (i % 2 == 0) { logs[i].value_type = REFTABLE_LOG_UPDATE; - t_reftable_set_hash(logs[i].value.update.new_hash, i, - REFTABLE_HASH_SHA1); + cl_reftable_set_hash(logs[i].value.update.new_hash, i, REFTABLE_HASH_SHA1); logs[i].value.update.email = xstrdup("identity@invalid"); } } - for (i = 0; i < N; i++) { - int err = reftable_stack_add(st, write_test_ref, &refs[i]); - check(!err); - } + for (i = 0; i < N; i++) + cl_assert_equal_i(reftable_stack_add(st, write_test_ref, + &refs[i], 0), 0); for (i = 0; i < N; i++) { struct write_log_arg arg = { .log = &logs[i], .update_index = reftable_stack_next_update_index(st), }; - int err = reftable_stack_add(st, write_test_log, &arg); - check(!err); + cl_assert_equal_i(reftable_stack_add(st, write_test_log, + &arg, 0), 0); } - err = reftable_stack_read_ref(st, "branch", &dest); - check_int(err, ==, 1); + cl_assert_equal_i(reftable_stack_read_ref(st, "branch", + &dest), 1); reftable_ref_record_release(&dest); - err = reftable_stack_read_log(st, "branch", &log_dest); - check_int(err, ==, 1); + cl_assert_equal_i(reftable_stack_read_log(st, "branch", + &log_dest), 1); reftable_log_record_release(&log_dest); - err = reftable_stack_compact_all(st, NULL); - check(!err); - - err = reftable_stack_read_ref(st, "branch", &dest); - check_int(err, ==, 1); - - err = reftable_stack_read_log(st, "branch", &log_dest); - check_int(err, ==, 1); + cl_assert_equal_i(reftable_stack_compact_all(st, NULL), 0); + cl_assert_equal_i(reftable_stack_read_ref(st, "branch", + &dest), 1); + cl_assert_equal_i(reftable_stack_read_log(st, "branch", + &log_dest), 1); reftable_ref_record_release(&dest); reftable_log_record_release(&log_dest); @@ -833,12 +796,11 @@ static void t_reftable_stack_tombstone(void) clear_dir(dir); } -static void t_reftable_stack_hash_id(void) +void test_reftable_stack__hash_id(void) { char *dir = get_tmp_dir(__LINE__); struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; - int err; struct reftable_ref_record ref = { .refname = (char *) "master", @@ -852,62 +814,57 @@ static void t_reftable_stack_hash_id(void) struct reftable_stack *st_default = NULL; struct reftable_ref_record dest = { 0 }; - err = reftable_new_stack(&st, dir, &opts); - check(!err); - - err = reftable_stack_add(st, write_test_ref, &ref); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_stack_add(st, write_test_ref, + &ref, 0), 0); /* can't read it with the wrong hash ID. */ - err = reftable_new_stack(&st32, dir, &opts32); - check_int(err, ==, REFTABLE_FORMAT_ERROR); + cl_assert_equal_i(reftable_new_stack(&st32, dir, + &opts32), REFTABLE_FORMAT_ERROR); /* check that we can read it back with default opts too. */ - err = reftable_new_stack(&st_default, dir, &opts_default); - check(!err); - - err = reftable_stack_read_ref(st_default, "master", &dest); - check(!err); - - check(reftable_ref_record_equal(&ref, &dest, REFTABLE_HASH_SIZE_SHA1)); + cl_assert_equal_i(reftable_new_stack(&st_default, dir, + &opts_default), 0); + cl_assert_equal_i(reftable_stack_read_ref(st_default, "master", + &dest), 0); + cl_assert(reftable_ref_record_equal(&ref, &dest, + REFTABLE_HASH_SIZE_SHA1) != 0); reftable_ref_record_release(&dest); reftable_stack_destroy(st); reftable_stack_destroy(st_default); clear_dir(dir); } -static void t_suggest_compaction_segment(void) +void test_reftable_stack__suggest_compaction_segment(void) { uint64_t sizes[] = { 512, 64, 17, 16, 9, 9, 9, 16, 2, 16 }; struct segment min = suggest_compaction_segment(sizes, ARRAY_SIZE(sizes), 2); - check_int(min.start, ==, 1); - check_int(min.end, ==, 10); + cl_assert_equal_i(min.start, 1); + cl_assert_equal_i(min.end, 10); } -static void t_suggest_compaction_segment_nothing(void) +void test_reftable_stack__suggest_compaction_segment_nothing(void) { uint64_t sizes[] = { 64, 32, 16, 8, 4, 2 }; struct segment result = suggest_compaction_segment(sizes, ARRAY_SIZE(sizes), 2); - check_int(result.start, ==, result.end); + cl_assert_equal_i(result.start, result.end); } -static void t_reflog_expire(void) +void test_reftable_stack__reflog_expire(void) { char *dir = get_tmp_dir(__LINE__); struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; struct reftable_log_record logs[20] = { 0 }; size_t i, N = ARRAY_SIZE(logs) - 1; - int err; struct reftable_log_expiry_config expiry = { .time = 10, }; struct reftable_log_record log = { 0 }; - err = reftable_new_stack(&st, dir, &opts); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); for (i = 1; i <= N; i++) { char buf[256]; @@ -918,8 +875,8 @@ static void t_reflog_expire(void) logs[i].value_type = REFTABLE_LOG_UPDATE; logs[i].value.update.time = i; logs[i].value.update.email = xstrdup("identity@invalid"); - t_reftable_set_hash(logs[i].value.update.new_hash, i, - REFTABLE_HASH_SHA1); + cl_reftable_set_hash(logs[i].value.update.new_hash, i, + REFTABLE_HASH_SHA1); } for (i = 1; i <= N; i++) { @@ -927,31 +884,23 @@ static void t_reflog_expire(void) .log = &logs[i], .update_index = reftable_stack_next_update_index(st), }; - int err = reftable_stack_add(st, write_test_log, &arg); - check(!err); + cl_assert_equal_i(reftable_stack_add(st, write_test_log, + &arg, 0), 0); } - err = reftable_stack_compact_all(st, NULL); - check(!err); - - err = reftable_stack_compact_all(st, &expiry); - check(!err); - - err = reftable_stack_read_log(st, logs[9].refname, &log); - check_int(err, ==, 1); - - err = reftable_stack_read_log(st, logs[11].refname, &log); - check(!err); + cl_assert_equal_i(reftable_stack_compact_all(st, NULL), 0); + cl_assert_equal_i(reftable_stack_compact_all(st, &expiry), 0); + cl_assert_equal_i(reftable_stack_read_log(st, logs[9].refname, + &log), 1); + cl_assert_equal_i(reftable_stack_read_log(st, logs[11].refname, + &log), 0); expiry.min_update_index = 15; - err = reftable_stack_compact_all(st, &expiry); - check(!err); - - err = reftable_stack_read_log(st, logs[14].refname, &log); - check_int(err, ==, 1); - - err = reftable_stack_read_log(st, logs[16].refname, &log); - check(!err); + cl_assert_equal_i(reftable_stack_compact_all(st, &expiry), 0); + cl_assert_equal_i(reftable_stack_read_log(st, logs[14].refname, + &log), 1); + cl_assert_equal_i(reftable_stack_read_log(st, logs[16].refname, + &log), 0); /* cleanup */ reftable_stack_destroy(st); @@ -963,26 +912,21 @@ static void t_reflog_expire(void) static int write_nothing(struct reftable_writer *wr, void *arg UNUSED) { - check(!reftable_writer_set_limits(wr, 1, 1)); + cl_assert_equal_i(reftable_writer_set_limits(wr, 1, 1), 0); return 0; } -static void t_empty_add(void) +void test_reftable_stack__empty_add(void) { struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; - int err; char *dir = get_tmp_dir(__LINE__); struct reftable_stack *st2 = NULL; - err = reftable_new_stack(&st, dir, &opts); - check(!err); - - err = reftable_stack_add(st, write_nothing, NULL); - check(!err); - - err = reftable_new_stack(&st2, dir, &opts); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); + cl_assert_equal_i(reftable_stack_add(st, write_nothing, + NULL, 0), 0); + cl_assert_equal_i(reftable_new_stack(&st2, dir, &opts), 0); clear_dir(dir); reftable_stack_destroy(st); reftable_stack_destroy(st2); @@ -998,18 +942,17 @@ static int fastlogN(uint64_t sz, uint64_t N) return l - 1; } -static void t_reftable_stack_auto_compaction(void) +void test_reftable_stack__auto_compaction(void) { struct reftable_write_options opts = { .disable_auto_compact = 1, }; struct reftable_stack *st = NULL; char *dir = get_tmp_dir(__LINE__); - int err; size_t i, N = 100; + int err; - err = reftable_new_stack(&st, dir, &opts); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); for (i = 0; i < N; i++) { char name[100]; @@ -1021,33 +964,32 @@ static void t_reftable_stack_auto_compaction(void) }; snprintf(name, sizeof(name), "branch%04"PRIuMAX, (uintmax_t)i); - err = reftable_stack_add(st, write_test_ref, &ref); - check(!err); + err = reftable_stack_add(st, write_test_ref, &ref, 0); + cl_assert(!err); err = reftable_stack_auto_compact(st); - check(!err); - check(i < 2 || st->merged->tables_len < 2 * fastlogN(i, 2)); + cl_assert(!err); + cl_assert(i < 2 || st->merged->tables_len < 2 * fastlogN(i, 2)); } - check_int(reftable_stack_compaction_stats(st)->entries_written, <, - (uint64_t)(N * fastlogN(N, 2))); + cl_assert(reftable_stack_compaction_stats(st)->entries_written < + (uint64_t)(N * fastlogN(N, 2))); reftable_stack_destroy(st); clear_dir(dir); } -static void t_reftable_stack_auto_compaction_factor(void) +void test_reftable_stack__auto_compaction_factor(void) { struct reftable_write_options opts = { .auto_compaction_factor = 5, }; struct reftable_stack *st = NULL; char *dir = get_tmp_dir(__LINE__); - int err; size_t N = 100; + int err; - err = reftable_new_stack(&st, dir, &opts); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); for (size_t i = 0; i < N; i++) { char name[20]; @@ -1058,17 +1000,17 @@ static void t_reftable_stack_auto_compaction_factor(void) }; xsnprintf(name, sizeof(name), "branch%04"PRIuMAX, (uintmax_t)i); - err = reftable_stack_add(st, &write_test_ref, &ref); - check(!err); + err = reftable_stack_add(st, &write_test_ref, &ref, 0); + cl_assert(!err); - check(i < 5 || st->merged->tables_len < 5 * fastlogN(i, 5)); + cl_assert(i < 5 || st->merged->tables_len < 5 * fastlogN(i, 5)); } reftable_stack_destroy(st); clear_dir(dir); } -static void t_reftable_stack_auto_compaction_with_locked_tables(void) +void test_reftable_stack__auto_compaction_with_locked_tables(void) { struct reftable_write_options opts = { .disable_auto_compact = 1, @@ -1078,21 +1020,20 @@ static void t_reftable_stack_auto_compaction_with_locked_tables(void) char *dir = get_tmp_dir(__LINE__); int err; - err = reftable_new_stack(&st, dir, &opts); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); write_n_ref_tables(st, 5); - check_int(st->merged->tables_len, ==, 5); + cl_assert_equal_i(st->merged->tables_len, 5); /* * Given that all tables we have written should be roughly the same * size, we expect that auto-compaction will want to compact all of the * tables. Locking any of the tables will keep it from doing so. */ - check(!reftable_buf_addstr(&buf, dir)); - check(!reftable_buf_addstr(&buf, "/")); - check(!reftable_buf_addstr(&buf, st->tables[2]->name)); - check(!reftable_buf_addstr(&buf, ".lock")); + cl_assert(!reftable_buf_addstr(&buf, dir)); + cl_assert(!reftable_buf_addstr(&buf, "/")); + cl_assert(!reftable_buf_addstr(&buf, st->tables[2]->name)); + cl_assert(!reftable_buf_addstr(&buf, ".lock")); write_file_buf(buf.buf, "", 0); /* @@ -1102,25 +1043,23 @@ static void t_reftable_stack_auto_compaction_with_locked_tables(void) * only compact the newest two tables. */ err = reftable_stack_auto_compact(st); - check(!err); - check_int(st->stats.failures, ==, 0); - check_int(st->merged->tables_len, ==, 4); + cl_assert(!err); + cl_assert_equal_i(st->stats.failures, 0); + cl_assert_equal_i(st->merged->tables_len, 4); reftable_stack_destroy(st); reftable_buf_release(&buf); clear_dir(dir); } -static void t_reftable_stack_add_performs_auto_compaction(void) +void test_reftable_stack__add_performs_auto_compaction(void) { struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; char *dir = get_tmp_dir(__LINE__); - int err; size_t i, n = 20; - err = reftable_new_stack(&st, dir, &opts); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); for (i = 0; i <= n; i++) { struct reftable_ref_record ref = { @@ -1140,8 +1079,8 @@ static void t_reftable_stack_add_performs_auto_compaction(void) snprintf(buf, sizeof(buf), "branch-%04"PRIuMAX, (uintmax_t)i); ref.refname = buf; - err = reftable_stack_add(st, write_test_ref, &ref); - check(!err); + cl_assert_equal_i(reftable_stack_add(st, write_test_ref, + &ref, 0), 0); /* * The stack length should grow continuously for all runs where @@ -1149,16 +1088,16 @@ static void t_reftable_stack_add_performs_auto_compaction(void) * all tables in the stack. */ if (i != n) - check_int(st->merged->tables_len, ==, i + 1); + cl_assert_equal_i(st->merged->tables_len, i + 1); else - check_int(st->merged->tables_len, ==, 1); + cl_assert_equal_i(st->merged->tables_len, 1); } reftable_stack_destroy(st); clear_dir(dir); } -static void t_reftable_stack_compaction_with_locked_tables(void) +void test_reftable_stack__compaction_with_locked_tables(void) { struct reftable_write_options opts = { .disable_auto_compact = 1, @@ -1168,17 +1107,16 @@ static void t_reftable_stack_compaction_with_locked_tables(void) char *dir = get_tmp_dir(__LINE__); int err; - err = reftable_new_stack(&st, dir, &opts); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); write_n_ref_tables(st, 3); - check_int(st->merged->tables_len, ==, 3); + cl_assert_equal_i(st->merged->tables_len, 3); /* Lock one of the tables that we're about to compact. */ - check(!reftable_buf_addstr(&buf, dir)); - check(!reftable_buf_addstr(&buf, "/")); - check(!reftable_buf_addstr(&buf, st->tables[1]->name)); - check(!reftable_buf_addstr(&buf, ".lock")); + cl_assert(!reftable_buf_addstr(&buf, dir)); + cl_assert(!reftable_buf_addstr(&buf, "/")); + cl_assert(!reftable_buf_addstr(&buf, st->tables[1]->name)); + cl_assert(!reftable_buf_addstr(&buf, ".lock")); write_file_buf(buf.buf, "", 0); /* @@ -1186,36 +1124,31 @@ static void t_reftable_stack_compaction_with_locked_tables(void) * compact all tables. */ err = reftable_stack_compact_all(st, NULL); - check_int(err, ==, REFTABLE_LOCK_ERROR); - check_int(st->stats.failures, ==, 1); - check_int(st->merged->tables_len, ==, 3); + cl_assert_equal_i(err, REFTABLE_LOCK_ERROR); + cl_assert_equal_i(st->stats.failures, 1); + cl_assert_equal_i(st->merged->tables_len, 3); reftable_stack_destroy(st); reftable_buf_release(&buf); clear_dir(dir); } -static void t_reftable_stack_compaction_concurrent(void) +void test_reftable_stack__compaction_concurrent(void) { struct reftable_write_options opts = { 0 }; struct reftable_stack *st1 = NULL, *st2 = NULL; char *dir = get_tmp_dir(__LINE__); - int err; - err = reftable_new_stack(&st1, dir, &opts); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st1, dir, &opts), 0); write_n_ref_tables(st1, 3); - err = reftable_new_stack(&st2, dir, &opts); - check(!err); - - err = reftable_stack_compact_all(st1, NULL); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st2, dir, &opts), 0); + cl_assert_equal_i(reftable_stack_compact_all(st1, NULL), 0); reftable_stack_destroy(st1); reftable_stack_destroy(st2); - check_int(count_dir_entries(dir), ==, 2); + cl_assert_equal_i(count_dir_entries(dir), 2); clear_dir(dir); } @@ -1228,32 +1161,24 @@ static void unclean_stack_close(struct reftable_stack *st) REFTABLE_FREE_AND_NULL(st->tables); } -static void t_reftable_stack_compaction_concurrent_clean(void) +void test_reftable_stack__compaction_concurrent_clean(void) { struct reftable_write_options opts = { 0 }; struct reftable_stack *st1 = NULL, *st2 = NULL, *st3 = NULL; char *dir = get_tmp_dir(__LINE__); - int err; - err = reftable_new_stack(&st1, dir, &opts); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st1, dir, &opts), 0); write_n_ref_tables(st1, 3); - err = reftable_new_stack(&st2, dir, &opts); - check(!err); - - err = reftable_stack_compact_all(st1, NULL); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st2, dir, &opts), 0); + cl_assert_equal_i(reftable_stack_compact_all(st1, NULL), 0); unclean_stack_close(st1); unclean_stack_close(st2); - err = reftable_new_stack(&st3, dir, &opts); - check(!err); - - err = reftable_stack_clean(st3); - check(!err); - check_int(count_dir_entries(dir), ==, 2); + cl_assert_equal_i(reftable_new_stack(&st3, dir, &opts), 0); + cl_assert_equal_i(reftable_stack_clean(st3), 0); + cl_assert_equal_i(count_dir_entries(dir), 2); reftable_stack_destroy(st1); reftable_stack_destroy(st2); @@ -1262,7 +1187,7 @@ static void t_reftable_stack_compaction_concurrent_clean(void) clear_dir(dir); } -static void t_reftable_stack_read_across_reload(void) +void test_reftable_stack__read_across_reload(void) { struct reftable_write_options opts = { 0 }; struct reftable_stack *st1 = NULL, *st2 = NULL; @@ -1272,37 +1197,35 @@ static void t_reftable_stack_read_across_reload(void) int err; /* Create a first stack and set up an iterator for it. */ - err = reftable_new_stack(&st1, dir, &opts); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st1, dir, &opts), 0); write_n_ref_tables(st1, 2); - check_int(st1->merged->tables_len, ==, 2); + cl_assert_equal_i(st1->merged->tables_len, 2); reftable_stack_init_ref_iterator(st1, &it); - err = reftable_iterator_seek_ref(&it, ""); - check(!err); + cl_assert_equal_i(reftable_iterator_seek_ref(&it, ""), 0); /* Set up a second stack for the same directory and compact it. */ err = reftable_new_stack(&st2, dir, &opts); - check(!err); - check_int(st2->merged->tables_len, ==, 2); + cl_assert(!err); + cl_assert_equal_i(st2->merged->tables_len, 2); err = reftable_stack_compact_all(st2, NULL); - check(!err); - check_int(st2->merged->tables_len, ==, 1); + cl_assert(!err); + cl_assert_equal_i(st2->merged->tables_len, 1); /* * Verify that we can continue to use the old iterator even after we * have reloaded its stack. */ err = reftable_stack_reload(st1); - check(!err); - check_int(st1->merged->tables_len, ==, 1); + cl_assert(!err); + cl_assert_equal_i(st1->merged->tables_len, 1); err = reftable_iterator_next_ref(&it, &rec); - check(!err); - check_str(rec.refname, "refs/heads/branch-0000"); + cl_assert(!err); + cl_assert_equal_s(rec.refname, "refs/heads/branch-0000"); err = reftable_iterator_next_ref(&it, &rec); - check(!err); - check_str(rec.refname, "refs/heads/branch-0001"); + cl_assert(!err); + cl_assert_equal_s(rec.refname, "refs/heads/branch-0001"); err = reftable_iterator_next_ref(&it, &rec); - check_int(err, >, 0); + cl_assert(err > 0); reftable_ref_record_release(&rec); reftable_iterator_destroy(&it); @@ -1311,7 +1234,7 @@ static void t_reftable_stack_read_across_reload(void) clear_dir(dir); } -static void t_reftable_stack_reload_with_missing_table(void) +void test_reftable_stack__reload_with_missing_table(void) { struct reftable_write_options opts = { 0 }; struct reftable_stack *st = NULL; @@ -1322,46 +1245,40 @@ static void t_reftable_stack_reload_with_missing_table(void) int err; /* Create a first stack and set up an iterator for it. */ - err = reftable_new_stack(&st, dir, &opts); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); write_n_ref_tables(st, 2); - check_int(st->merged->tables_len, ==, 2); + cl_assert_equal_i(st->merged->tables_len, 2); reftable_stack_init_ref_iterator(st, &it); - err = reftable_iterator_seek_ref(&it, ""); - check(!err); + cl_assert_equal_i(reftable_iterator_seek_ref(&it, ""), 0); /* * Update the tables.list file with some garbage data, while reusing * our old tables. This should trigger a partial reload of the stack, * where we try to reuse our old tables. */ - check(!reftable_buf_addstr(&content, st->tables[0]->name)); - check(!reftable_buf_addstr(&content, "\n")); - check(!reftable_buf_addstr(&content, st->tables[1]->name)); - check(!reftable_buf_addstr(&content, "\n")); - check(!reftable_buf_addstr(&content, "garbage\n")); - check(!reftable_buf_addstr(&table_path, st->list_file)); - check(!reftable_buf_addstr(&table_path, ".lock")); + cl_assert(!reftable_buf_addstr(&content, st->tables[0]->name)); + cl_assert(!reftable_buf_addstr(&content, "\n")); + cl_assert(!reftable_buf_addstr(&content, st->tables[1]->name)); + cl_assert(!reftable_buf_addstr(&content, "\n")); + cl_assert(!reftable_buf_addstr(&content, "garbage\n")); + cl_assert(!reftable_buf_addstr(&table_path, st->list_file)); + cl_assert(!reftable_buf_addstr(&table_path, ".lock")); write_file_buf(table_path.buf, content.buf, content.len); - err = rename(table_path.buf, st->list_file); - check(!err); + cl_assert_equal_i(rename(table_path.buf, st->list_file), 0); err = reftable_stack_reload(st); - check_int(err, ==, -4); - check_int(st->merged->tables_len, ==, 2); + cl_assert_equal_i(err, -4); + cl_assert_equal_i(st->merged->tables_len, 2); /* * Even though the reload has failed, we should be able to continue * using the iterator. */ - err = reftable_iterator_next_ref(&it, &rec); - check(!err); - check_str(rec.refname, "refs/heads/branch-0000"); - err = reftable_iterator_next_ref(&it, &rec); - check(!err); - check_str(rec.refname, "refs/heads/branch-0001"); - err = reftable_iterator_next_ref(&it, &rec); - check_int(err, >, 0); + cl_assert_equal_i(reftable_iterator_next_ref(&it, &rec), 0); + cl_assert_equal_s(rec.refname, "refs/heads/branch-0000"); + cl_assert_equal_i(reftable_iterator_next_ref(&it, &rec), 0); + cl_assert_equal_s(rec.refname, "refs/heads/branch-0001"); + cl_assert(reftable_iterator_next_ref(&it, &rec) > 0); reftable_ref_record_release(&rec); reftable_iterator_destroy(&it); @@ -1374,12 +1291,13 @@ static void t_reftable_stack_reload_with_missing_table(void) static int write_limits_after_ref(struct reftable_writer *wr, void *arg) { struct reftable_ref_record *ref = arg; - check(!reftable_writer_set_limits(wr, ref->update_index, ref->update_index)); - check(!reftable_writer_add_ref(wr, ref)); + cl_assert_equal_i(reftable_writer_set_limits(wr, + ref->update_index, ref->update_index), 0); + cl_assert_equal_i(reftable_writer_add_ref(wr, ref), 0); return reftable_writer_set_limits(wr, ref->update_index, ref->update_index); } -static void t_reftable_invalid_limit_updates(void) +void test_reftable_stack__invalid_limit_updates(void) { struct reftable_ref_record ref = { .refname = (char *) "HEAD", @@ -1393,59 +1311,22 @@ static void t_reftable_invalid_limit_updates(void) struct reftable_addition *add = NULL; char *dir = get_tmp_dir(__LINE__); struct reftable_stack *st = NULL; - int err; - err = reftable_new_stack(&st, dir, &opts); - check(!err); + cl_assert_equal_i(reftable_new_stack(&st, dir, &opts), 0); reftable_addition_destroy(add); - err = reftable_stack_new_addition(&add, st, 0); - check(!err); + cl_assert_equal_i(reftable_stack_new_addition(&add, st, 0), 0); /* * write_limits_after_ref also updates the update indexes after adding * the record. This should cause an err to be returned, since the limits * must be set at the start. */ - err = reftable_addition_add(add, write_limits_after_ref, &ref); - check_int(err, ==, REFTABLE_API_ERROR); + cl_assert_equal_i(reftable_addition_add(add, + write_limits_after_ref, &ref), REFTABLE_API_ERROR); reftable_addition_destroy(add); reftable_stack_destroy(st); clear_dir(dir); } - -int cmd_main(int argc UNUSED, const char *argv[] UNUSED) -{ - TEST(t_empty_add(), "empty addition to stack"); - TEST(t_read_file(), "read_lines works"); - TEST(t_reflog_expire(), "expire reflog entries"); - TEST(t_reftable_invalid_limit_updates(), "prevent limit updates after adding records"); - TEST(t_reftable_stack_add(), "add multiple refs and logs to stack"); - TEST(t_reftable_stack_add_one(), "add a single ref record to stack"); - TEST(t_reftable_stack_add_performs_auto_compaction(), "addition to stack triggers auto-compaction"); - TEST(t_reftable_stack_auto_compaction(), "stack must form geometric sequence after compaction"); - TEST(t_reftable_stack_auto_compaction_factor(), "auto-compaction with non-default geometric factor"); - TEST(t_reftable_stack_auto_compaction_fails_gracefully(), "failure on auto-compaction"); - TEST(t_reftable_stack_auto_compaction_with_locked_tables(), "auto compaction with locked tables"); - TEST(t_reftable_stack_compaction_concurrent(), "compaction with concurrent stack"); - TEST(t_reftable_stack_compaction_concurrent_clean(), "compaction with unclean stack shutdown"); - TEST(t_reftable_stack_compaction_with_locked_tables(), "compaction with locked tables"); - TEST(t_reftable_stack_hash_id(), "read stack with wrong hash ID"); - TEST(t_reftable_stack_iterator(), "log and ref iterator for reftable stack"); - TEST(t_reftable_stack_lock_failure(), "stack addition with lockfile failure"); - TEST(t_reftable_stack_log_normalize(), "log messages should be normalized"); - TEST(t_reftable_stack_read_across_reload(), "stack iterators work across reloads"); - TEST(t_reftable_stack_reload_with_missing_table(), "stack iteration with garbage tables"); - TEST(t_reftable_stack_tombstone(), "'tombstone' refs in stack"); - TEST(t_reftable_stack_transaction_api(), "update transaction to stack"); - TEST(t_reftable_stack_transaction_with_reload(), "transaction with reload"); - TEST(t_reftable_stack_transaction_api_performs_auto_compaction(), "update transaction triggers auto-compaction"); - TEST(t_reftable_stack_update_index_check(), "update transactions with equal update indices"); - TEST(t_reftable_stack_uptodate(), "stack must be reloaded before ref update"); - TEST(t_suggest_compaction_segment(), "suggest_compaction_segment with basic input"); - TEST(t_suggest_compaction_segment_nothing(), "suggest_compaction_segment with pre-compacted input"); - - return test_done(); -} diff --git a/t/unit-tests/t-reftable-table.c b/t/unit-tests/u-reftable-table.c index 7e1eb533d0..14fae8b199 100644 --- a/t/unit-tests/t-reftable-table.c +++ b/t/unit-tests/u-reftable-table.c @@ -1,4 +1,4 @@ -#include "test-lib.h" +#include "unit-test.h" #include "lib-reftable.h" #include "reftable/blocksource.h" #include "reftable/constants.h" @@ -6,7 +6,7 @@ #include "reftable/table.h" #include "strbuf.h" -static int t_table_seek_once(void) +void test_reftable_table__seek_once(void) { struct reftable_ref_record records[] = { { @@ -22,32 +22,32 @@ static int t_table_seek_once(void) struct reftable_buf buf = REFTABLE_BUF_INIT; int ret; - t_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), NULL, 0, NULL); + cl_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), NULL, 0, NULL); block_source_from_buf(&source, &buf); ret = reftable_table_new(&table, &source, "name"); - check(!ret); + cl_assert(!ret); reftable_table_init_ref_iterator(table, &it); ret = reftable_iterator_seek_ref(&it, ""); - check(!ret); + cl_assert(!ret); ret = reftable_iterator_next_ref(&it, &ref); - check(!ret); + cl_assert(!ret); - ret = reftable_ref_record_equal(&ref, &records[0], REFTABLE_HASH_SIZE_SHA1); - check_int(ret, ==, 1); + ret = reftable_ref_record_equal(&ref, &records[0], + REFTABLE_HASH_SIZE_SHA1); + cl_assert_equal_i(ret, 1); ret = reftable_iterator_next_ref(&it, &ref); - check_int(ret, ==, 1); + cl_assert_equal_i(ret, 1); reftable_ref_record_release(&ref); reftable_iterator_destroy(&it); reftable_table_decref(table); reftable_buf_release(&buf); - return 0; } -static int t_table_reseek(void) +void test_reftable_table__reseek(void) { struct reftable_ref_record records[] = { { @@ -63,35 +63,35 @@ static int t_table_reseek(void) struct reftable_buf buf = REFTABLE_BUF_INIT; int ret; - t_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), NULL, 0, NULL); + cl_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), + NULL, 0, NULL); block_source_from_buf(&source, &buf); ret = reftable_table_new(&table, &source, "name"); - check(!ret); + cl_assert(!ret); reftable_table_init_ref_iterator(table, &it); for (size_t i = 0; i < 5; i++) { ret = reftable_iterator_seek_ref(&it, ""); - check(!ret); + cl_assert(!ret); ret = reftable_iterator_next_ref(&it, &ref); - check(!ret); + cl_assert(!ret); ret = reftable_ref_record_equal(&ref, &records[0], REFTABLE_HASH_SIZE_SHA1); - check_int(ret, ==, 1); + cl_assert_equal_i(ret, 1); ret = reftable_iterator_next_ref(&it, &ref); - check_int(ret, ==, 1); + cl_assert_equal_i(ret, 1); } reftable_ref_record_release(&ref); reftable_iterator_destroy(&it); reftable_table_decref(table); reftable_buf_release(&buf); - return 0; } -static int t_table_block_iterator(void) +void test_reftable_table__block_iterator(void) { struct reftable_block_source source = { 0 }; struct reftable_table_iterator it = { 0 }; @@ -147,14 +147,14 @@ static int t_table_block_iterator(void) (uintmax_t) i); } - t_reftable_write_to_buf(&buf, records, nrecords, NULL, 0, NULL); + cl_reftable_write_to_buf(&buf, records, nrecords, NULL, 0, NULL); block_source_from_buf(&source, &buf); ret = reftable_table_new(&table, &source, "name"); - check(!ret); + cl_assert(!ret); ret = reftable_table_iterator_init(&it, table); - check(!ret); + cl_assert(!ret); for (size_t i = 0; i < ARRAY_SIZE(expected_blocks); i++) { struct reftable_iterator record_it = { 0 }; @@ -163,22 +163,26 @@ static int t_table_block_iterator(void) }; ret = reftable_table_iterator_next(&it, &block); - check(!ret); + cl_assert(!ret); - check_int(block->block_type, ==, expected_blocks[i].block_type); - check_int(block->header_off, ==, expected_blocks[i].header_off); - check_int(block->restart_count, ==, expected_blocks[i].restart_count); + cl_assert_equal_i(block->block_type, + expected_blocks[i].block_type); + cl_assert_equal_i(block->header_off, + expected_blocks[i].header_off); + cl_assert_equal_i(block->restart_count, + expected_blocks[i].restart_count); ret = reftable_block_init_iterator(block, &record_it); - check(!ret); + cl_assert(!ret); for (size_t j = 0; ; j++) { ret = iterator_next(&record_it, &record); if (ret > 0) { - check_int(j, ==, expected_blocks[i].record_count); + cl_assert_equal_i(j, + expected_blocks[i].record_count); break; } - check(!ret); + cl_assert(!ret); } reftable_iterator_destroy(&record_it); @@ -186,7 +190,7 @@ static int t_table_block_iterator(void) } ret = reftable_table_iterator_next(&it, &block); - check_int(ret, ==, 1); + cl_assert_equal_i(ret, 1); for (size_t i = 0; i < nrecords; i++) reftable_free(records[i].refname); @@ -194,13 +198,4 @@ static int t_table_block_iterator(void) reftable_table_decref(table); reftable_buf_release(&buf); reftable_free(records); - return 0; -} - -int cmd_main(int argc UNUSED, const char *argv[] UNUSED) -{ - TEST(t_table_seek_once(), "table can seek once"); - TEST(t_table_reseek(), "table can reseek multiple times"); - TEST(t_table_block_iterator(), "table can iterate through blocks"); - return test_done(); } diff --git a/t/unit-tests/u-string-list.c b/t/unit-tests/u-string-list.c new file mode 100644 index 0000000000..a2457d7b1e --- /dev/null +++ b/t/unit-tests/u-string-list.c @@ -0,0 +1,306 @@ +#include "unit-test.h" +#include "string-list.h" + +static void t_vcreate_string_list_dup(struct string_list *list, + int free_util, va_list ap) +{ + const char *arg; + + cl_assert(list->strdup_strings); + + string_list_clear(list, free_util); + while ((arg = va_arg(ap, const char *))) + string_list_append(list, arg); +} + +static void t_create_string_list_dup(struct string_list *list, int free_util, ...) +{ + va_list ap; + + cl_assert(list->strdup_strings); + + string_list_clear(list, free_util); + va_start(ap, free_util); + t_vcreate_string_list_dup(list, free_util, ap); + va_end(ap); +} + +static void t_string_list_clear(struct string_list *list, int free_util) +{ + string_list_clear(list, free_util); + cl_assert_equal_p(list->items, NULL); + cl_assert_equal_i(list->nr, 0); + cl_assert_equal_i(list->alloc, 0); +} + +static void t_string_list_equal(struct string_list *list, + struct string_list *expected_strings) +{ + cl_assert_equal_i(list->nr, expected_strings->nr); + cl_assert(list->nr <= list->alloc); + for (size_t i = 0; i < expected_strings->nr; i++) + cl_assert_equal_s(list->items[i].string, + expected_strings->items[i].string); +} + +static void t_string_list_split(const char *data, const char *delim, int maxsplit, ...) +{ + struct string_list expected_strings = STRING_LIST_INIT_DUP; + struct string_list list = STRING_LIST_INIT_DUP; + va_list ap; + int len; + + va_start(ap, maxsplit); + t_vcreate_string_list_dup(&expected_strings, 0, ap); + va_end(ap); + + string_list_clear(&list, 0); + len = string_list_split(&list, data, delim, maxsplit); + cl_assert_equal_i(len, expected_strings.nr); + t_string_list_equal(&list, &expected_strings); + + string_list_clear(&expected_strings, 0); + string_list_clear(&list, 0); +} + +static void t_string_list_split_f(const char *data, const char *delim, + int maxsplit, unsigned flags, ...) +{ + struct string_list expected_strings = STRING_LIST_INIT_DUP; + struct string_list list = STRING_LIST_INIT_DUP; + va_list ap; + int len; + + va_start(ap, flags); + t_vcreate_string_list_dup(&expected_strings, 0, ap); + va_end(ap); + + string_list_clear(&list, 0); + len = string_list_split_f(&list, data, delim, maxsplit, flags); + cl_assert_equal_i(len, expected_strings.nr); + t_string_list_equal(&list, &expected_strings); + + string_list_clear(&expected_strings, 0); + string_list_clear(&list, 0); +} + +void test_string_list__split_f(void) +{ + t_string_list_split_f("::foo:bar:baz:", ":", -1, 0, + "", "", "foo", "bar", "baz", "", NULL); + t_string_list_split_f(" foo:bar : baz", ":", -1, STRING_LIST_SPLIT_TRIM, + "foo", "bar", "baz", NULL); + t_string_list_split_f(" a b c ", " ", 1, STRING_LIST_SPLIT_TRIM, + "a", "b c", NULL); + t_string_list_split_f("::foo::bar:baz:", ":", -1, STRING_LIST_SPLIT_NONEMPTY, + "foo", "bar", "baz", NULL); + t_string_list_split_f("foo:baz", ":", -1, STRING_LIST_SPLIT_NONEMPTY, + "foo", "baz", NULL); + t_string_list_split_f("foo :: : baz", ":", -1, + STRING_LIST_SPLIT_NONEMPTY | STRING_LIST_SPLIT_TRIM, + "foo", "baz", NULL); +} + +static void t_string_list_split_in_place_f(const char *data_, const char *delim, + int maxsplit, unsigned flags, ...) +{ + struct string_list expected_strings = STRING_LIST_INIT_DUP; + struct string_list list = STRING_LIST_INIT_NODUP; + char *data = xstrdup(data_); + va_list ap; + int len; + + va_start(ap, flags); + t_vcreate_string_list_dup(&expected_strings, 0, ap); + va_end(ap); + + string_list_clear(&list, 0); + len = string_list_split_in_place_f(&list, data, delim, maxsplit, flags); + cl_assert_equal_i(len, expected_strings.nr); + t_string_list_equal(&list, &expected_strings); + + free(data); + string_list_clear(&expected_strings, 0); + string_list_clear(&list, 0); +} + +void test_string_list__split_in_place_f(void) +{ + t_string_list_split_in_place_f("::foo:bar:baz:", ":", -1, 0, + "", "", "foo", "bar", "baz", "", NULL); + t_string_list_split_in_place_f(" foo:bar : baz", ":", -1, STRING_LIST_SPLIT_TRIM, + "foo", "bar", "baz", NULL); + t_string_list_split_in_place_f(" a b c ", " ", 1, STRING_LIST_SPLIT_TRIM, + "a", "b c", NULL); + t_string_list_split_in_place_f("::foo::bar:baz:", ":", -1, + STRING_LIST_SPLIT_NONEMPTY, + "foo", "bar", "baz", NULL); + t_string_list_split_in_place_f("foo:baz", ":", -1, STRING_LIST_SPLIT_NONEMPTY, + "foo", "baz", NULL); + t_string_list_split_in_place_f("foo :: : baz", ":", -1, + STRING_LIST_SPLIT_NONEMPTY | STRING_LIST_SPLIT_TRIM, + "foo", "baz", NULL); +} + +void test_string_list__split(void) +{ + t_string_list_split("foo:bar:baz", ":", -1, "foo", "bar", "baz", NULL); + t_string_list_split("foo:bar:baz", ":", 0, "foo:bar:baz", NULL); + t_string_list_split("foo:bar:baz", ":", 1, "foo", "bar:baz", NULL); + t_string_list_split("foo:bar:baz", ":", 2, "foo", "bar", "baz", NULL); + t_string_list_split("foo:bar:", ":", -1, "foo", "bar", "", NULL); + t_string_list_split("", ":", -1, "", NULL); + t_string_list_split(":", ":", -1, "", "", NULL); +} + +static void t_string_list_split_in_place(const char *data, const char *delim, + int maxsplit, ...) +{ + struct string_list expected_strings = STRING_LIST_INIT_DUP; + struct string_list list = STRING_LIST_INIT_NODUP; + char *string = xstrdup(data); + va_list ap; + int len; + + va_start(ap, maxsplit); + t_vcreate_string_list_dup(&expected_strings, 0, ap); + va_end(ap); + + string_list_clear(&list, 0); + len = string_list_split_in_place(&list, string, delim, maxsplit); + cl_assert_equal_i(len, expected_strings.nr); + t_string_list_equal(&list, &expected_strings); + + free(string); + string_list_clear(&expected_strings, 0); + string_list_clear(&list, 0); +} + +void test_string_list__split_in_place(void) +{ + t_string_list_split_in_place("foo:;:bar:;:baz:;:", ":;", -1, + "foo", "", "", "bar", "", "", "baz", "", "", "", NULL); + t_string_list_split_in_place("foo:;:bar:;:baz", ":;", 0, + "foo:;:bar:;:baz", NULL); + t_string_list_split_in_place("foo:;:bar:;:baz", ":;", 1, + "foo", ";:bar:;:baz", NULL); + t_string_list_split_in_place("foo:;:bar:;:baz", ":;", 2, + "foo", "", ":bar:;:baz", NULL); + t_string_list_split_in_place("foo:;:bar:;:", ":;", -1, + "foo", "", "", "bar", "", "", "", NULL); +} + +static int prefix_cb(struct string_list_item *item, void *cb_data) +{ + const char *prefix = (const char *)cb_data; + return starts_with(item->string, prefix); +} + +static void t_string_list_filter(struct string_list *list, ...) +{ + struct string_list expected_strings = STRING_LIST_INIT_DUP; + const char *prefix = "y"; + va_list ap; + + va_start(ap, list); + t_vcreate_string_list_dup(&expected_strings, 0, ap); + va_end(ap); + + filter_string_list(list, 0, prefix_cb, (void *)prefix); + t_string_list_equal(list, &expected_strings); + + string_list_clear(&expected_strings, 0); +} + +void test_string_list__filter(void) +{ + struct string_list list = STRING_LIST_INIT_DUP; + + t_create_string_list_dup(&list, 0, NULL); + t_string_list_filter(&list, NULL); + + t_create_string_list_dup(&list, 0, "no", NULL); + t_string_list_filter(&list, NULL); + + t_create_string_list_dup(&list, 0, "yes", NULL); + t_string_list_filter(&list, "yes", NULL); + + t_create_string_list_dup(&list, 0, "no", "yes", NULL); + t_string_list_filter(&list, "yes", NULL); + + t_create_string_list_dup(&list, 0, "yes", "no", NULL); + t_string_list_filter(&list, "yes", NULL); + + t_create_string_list_dup(&list, 0, "y1", "y2", NULL); + t_string_list_filter(&list, "y1", "y2", NULL); + + t_create_string_list_dup(&list, 0, "y2", "y1", NULL); + t_string_list_filter(&list, "y2", "y1", NULL); + + t_create_string_list_dup(&list, 0, "x1", "x2", NULL); + t_string_list_filter(&list, NULL); + + t_string_list_clear(&list, 0); +} + +static void t_string_list_remove_duplicates(struct string_list *list, ...) +{ + struct string_list expected_strings = STRING_LIST_INIT_DUP; + va_list ap; + + va_start(ap, list); + t_vcreate_string_list_dup(&expected_strings, 0, ap); + va_end(ap); + + string_list_remove_duplicates(list, 0); + t_string_list_equal(list, &expected_strings); + + string_list_clear(&expected_strings, 0); +} + +void test_string_list__remove_duplicates(void) +{ + struct string_list list = STRING_LIST_INIT_DUP; + + t_create_string_list_dup(&list, 0, NULL); + t_string_list_remove_duplicates(&list, NULL); + + t_create_string_list_dup(&list, 0, "", NULL); + t_string_list_remove_duplicates(&list, "", NULL); + + t_create_string_list_dup(&list, 0, "a", NULL); + t_string_list_remove_duplicates(&list, "a", NULL); + + t_create_string_list_dup(&list, 0, "a", "a", NULL); + t_string_list_remove_duplicates(&list, "a", NULL); + + t_create_string_list_dup(&list, 0, "a", "a", "a", NULL); + t_string_list_remove_duplicates(&list, "a", NULL); + + t_create_string_list_dup(&list, 0, "a", "a", "b", NULL); + t_string_list_remove_duplicates(&list, "a", "b", NULL); + + t_create_string_list_dup(&list, 0, "a", "b", "b", NULL); + t_string_list_remove_duplicates(&list, "a", "b", NULL); + + t_create_string_list_dup(&list, 0, "a", "b", "c", NULL); + t_string_list_remove_duplicates(&list, "a", "b", "c", NULL); + + t_create_string_list_dup(&list, 0, "a", "a", "b", "c", NULL); + t_string_list_remove_duplicates(&list, "a", "b", "c", NULL); + + t_create_string_list_dup(&list, 0, "a", "b", "b", "c", NULL); + t_string_list_remove_duplicates(&list, "a", "b", "c", NULL); + + t_create_string_list_dup(&list, 0, "a", "b", "c", "c", NULL); + t_string_list_remove_duplicates(&list, "a", "b", "c", NULL); + + t_create_string_list_dup(&list, 0, "a", "a", "b", "b", "c", "c", NULL); + t_string_list_remove_duplicates(&list, "a", "b", "c", NULL); + + t_create_string_list_dup(&list, 0, "a", "a", "a", "b", "b", "b", + "c", "c", "c", NULL); + t_string_list_remove_duplicates(&list, "a", "b", "c", NULL); + + t_string_list_clear(&list, 0); +} diff --git a/t/unit-tests/unit-test.h b/t/unit-tests/unit-test.h index 85e5d6a948..39a0b72a05 100644 --- a/t/unit-tests/unit-test.h +++ b/t/unit-tests/unit-test.h @@ -1,8 +1,13 @@ #include "git-compat-util.h" #include "clar/clar.h" -#include "clar-decls.h" #include "strbuf.h" +#ifndef GIT_CLAR_DECLS_H +# include "clar-decls.h" +#else +# include GIT_CLAR_DECLS_H +#endif + #define cl_failf(fmt, ...) do { \ char desc[4096]; \ snprintf(desc, sizeof(desc), fmt, __VA_ARGS__); \ @@ -5,7 +5,7 @@ #include "environment.h" #include "tag.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "commit.h" #include "tree.h" #include "blob.h" @@ -52,7 +52,7 @@ int gpg_verify_tag(const struct object_id *oid, const char *name_to_report, unsigned long size; int ret; - type = oid_object_info(the_repository, oid, NULL); + type = odb_read_object_info(the_repository->objects, oid, NULL); if (type != OBJ_TAG) return error("%s: cannot verify a non-tag object of type %s.", name_to_report ? @@ -60,7 +60,7 @@ int gpg_verify_tag(const struct object_id *oid, const char *name_to_report, repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV), type_name(type)); - buf = repo_read_object_file(the_repository, oid, &type, &size); + buf = odb_read_object(the_repository->objects, oid, &type, &size); if (!buf) return error("%s: unable to read file.", name_to_report ? @@ -222,8 +222,8 @@ int parse_tag(struct tag *item) if (item->object.parsed) return 0; - data = repo_read_object_file(the_repository, &item->object.oid, &type, - &size); + data = odb_read_object(the_repository->objects, &item->object.oid, + &type, &size); if (!data) return error("Could not read %s", oid_to_hex(&item->object.oid)); diff --git a/tmp-objdir.c b/tmp-objdir.c index c38fbeb5e8..9f5a1788cd 100644 --- a/tmp-objdir.c +++ b/tmp-objdir.c @@ -10,14 +10,14 @@ #include "strbuf.h" #include "strvec.h" #include "quote.h" -#include "object-store.h" +#include "odb.h" #include "repository.h" struct tmp_objdir { struct repository *repo; struct strbuf path; struct strvec env; - struct object_directory *prev_odb; + struct odb_source *prev_source; int will_destroy; }; @@ -46,8 +46,8 @@ int tmp_objdir_destroy(struct tmp_objdir *t) if (t == the_tmp_objdir) the_tmp_objdir = NULL; - if (t->prev_odb) - restore_primary_odb(t->prev_odb, t->path.buf); + if (t->prev_source) + odb_restore_primary_source(t->repo->objects, t->prev_source, t->path.buf); err = remove_dir_recursively(&t->path, 0); @@ -227,7 +227,7 @@ static int migrate_one(struct tmp_objdir *t, return -1; return migrate_paths(t, src, dst, flags); } - return finalize_object_file_flags(src->buf, dst->buf, flags); + return finalize_object_file_flags(t->repo, src->buf, dst->buf, flags); } static int is_loose_object_shard(const char *name) @@ -276,11 +276,11 @@ int tmp_objdir_migrate(struct tmp_objdir *t) if (!t) return 0; - if (t->prev_odb) { - if (t->repo->objects->odb->will_destroy) + if (t->prev_source) { + if (t->repo->objects->sources->will_destroy) BUG("migrating an ODB that was marked for destruction"); - restore_primary_odb(t->prev_odb, t->path.buf); - t->prev_odb = NULL; + odb_restore_primary_source(t->repo->objects, t->prev_source, t->path.buf); + t->prev_source = NULL; } strbuf_addbuf(&src, &t->path); @@ -304,24 +304,26 @@ const char **tmp_objdir_env(const struct tmp_objdir *t) void tmp_objdir_add_as_alternate(const struct tmp_objdir *t) { - add_to_alternates_memory(t->path.buf); + odb_add_to_alternates_memory(t->repo->objects, t->path.buf); } void tmp_objdir_replace_primary_odb(struct tmp_objdir *t, int will_destroy) { - if (t->prev_odb) + if (t->prev_source) BUG("the primary object database is already replaced"); - t->prev_odb = set_temporary_primary_odb(t->path.buf, will_destroy); + t->prev_source = odb_set_temporary_primary_source(t->repo->objects, + t->path.buf, will_destroy); t->will_destroy = will_destroy; } struct tmp_objdir *tmp_objdir_unapply_primary_odb(void) { - if (!the_tmp_objdir || !the_tmp_objdir->prev_odb) + if (!the_tmp_objdir || !the_tmp_objdir->prev_source) return NULL; - restore_primary_odb(the_tmp_objdir->prev_odb, the_tmp_objdir->path.buf); - the_tmp_objdir->prev_odb = NULL; + odb_restore_primary_source(the_tmp_objdir->repo->objects, + the_tmp_objdir->prev_source, the_tmp_objdir->path.buf); + the_tmp_objdir->prev_source = NULL; return the_tmp_objdir; } diff --git a/trace2/tr2_cfg.c b/trace2/tr2_cfg.c index 22a99a0682..bbcfeda60a 100644 --- a/trace2/tr2_cfg.c +++ b/trace2/tr2_cfg.c @@ -8,89 +8,65 @@ #include "trace2/tr2_sysenv.h" #include "wildmatch.h" -static struct strbuf **tr2_cfg_patterns; -static int tr2_cfg_count_patterns; +static struct string_list tr2_cfg_patterns = STRING_LIST_INIT_DUP; static int tr2_cfg_loaded; -static struct strbuf **tr2_cfg_env_vars; -static int tr2_cfg_env_vars_count; +static struct string_list tr2_cfg_env_vars = STRING_LIST_INIT_DUP; static int tr2_cfg_env_vars_loaded; /* * Parse a string containing a comma-delimited list of config keys - * or wildcard patterns into a list of strbufs. + * or wildcard patterns into a string list. */ -static int tr2_cfg_load_patterns(void) +static size_t tr2_cfg_load_patterns(void) { - struct strbuf **s; const char *envvar; if (tr2_cfg_loaded) - return tr2_cfg_count_patterns; + return tr2_cfg_patterns.nr; tr2_cfg_loaded = 1; envvar = tr2_sysenv_get(TR2_SYSENV_CFG_PARAM); if (!envvar || !*envvar) - return tr2_cfg_count_patterns; + return tr2_cfg_patterns.nr; - tr2_cfg_patterns = strbuf_split_buf(envvar, strlen(envvar), ',', -1); - for (s = tr2_cfg_patterns; *s; s++) { - struct strbuf *buf = *s; - - if (buf->len && buf->buf[buf->len - 1] == ',') - strbuf_setlen(buf, buf->len - 1); - strbuf_trim_trailing_newline(*s); - strbuf_trim(*s); - } - - tr2_cfg_count_patterns = s - tr2_cfg_patterns; - return tr2_cfg_count_patterns; + string_list_split_f(&tr2_cfg_patterns, envvar, ",", -1, + STRING_LIST_SPLIT_TRIM); + return tr2_cfg_patterns.nr; } void tr2_cfg_free_patterns(void) { - if (tr2_cfg_patterns) - strbuf_list_free(tr2_cfg_patterns); - tr2_cfg_count_patterns = 0; + if (tr2_cfg_patterns.nr) + string_list_clear(&tr2_cfg_patterns, 0); tr2_cfg_loaded = 0; } /* * Parse a string containing a comma-delimited list of environment variable - * names into a list of strbufs. + * names into a string list. */ -static int tr2_load_env_vars(void) +static size_t tr2_load_env_vars(void) { - struct strbuf **s; const char *varlist; if (tr2_cfg_env_vars_loaded) - return tr2_cfg_env_vars_count; + return tr2_cfg_env_vars.nr; tr2_cfg_env_vars_loaded = 1; varlist = tr2_sysenv_get(TR2_SYSENV_ENV_VARS); if (!varlist || !*varlist) - return tr2_cfg_env_vars_count; - - tr2_cfg_env_vars = strbuf_split_buf(varlist, strlen(varlist), ',', -1); - for (s = tr2_cfg_env_vars; *s; s++) { - struct strbuf *buf = *s; - - if (buf->len && buf->buf[buf->len - 1] == ',') - strbuf_setlen(buf, buf->len - 1); - strbuf_trim_trailing_newline(*s); - strbuf_trim(*s); - } + return tr2_cfg_env_vars.nr; - tr2_cfg_env_vars_count = s - tr2_cfg_env_vars; - return tr2_cfg_env_vars_count; + string_list_split_f(&tr2_cfg_env_vars, varlist, ",", -1, + STRING_LIST_SPLIT_TRIM); + return tr2_cfg_env_vars.nr; } void tr2_cfg_free_env_vars(void) { - if (tr2_cfg_env_vars) - strbuf_list_free(tr2_cfg_env_vars); - tr2_cfg_env_vars_count = 0; + if (tr2_cfg_env_vars.nr) + string_list_clear(&tr2_cfg_env_vars, 0); tr2_cfg_env_vars_loaded = 0; } @@ -105,12 +81,11 @@ struct tr2_cfg_data { static int tr2_cfg_cb(const char *key, const char *value, const struct config_context *ctx, void *d) { - struct strbuf **s; + struct string_list_item *item; struct tr2_cfg_data *data = (struct tr2_cfg_data *)d; - for (s = tr2_cfg_patterns; *s; s++) { - struct strbuf *buf = *s; - int wm = wildmatch(buf->buf, key, WM_CASEFOLD); + for_each_string_list_item(item, &tr2_cfg_patterns) { + int wm = wildmatch(item->string, key, WM_CASEFOLD); if (wm == WM_MATCH) { trace2_def_param_fl(data->file, data->line, key, value, ctx->kvi); @@ -132,17 +107,16 @@ void tr2_cfg_list_config_fl(const char *file, int line) void tr2_list_env_vars_fl(const char *file, int line) { struct key_value_info kvi = KVI_INIT; - struct strbuf **s; + struct string_list_item *item; kvi_from_param(&kvi); if (tr2_load_env_vars() <= 0) return; - for (s = tr2_cfg_env_vars; *s; s++) { - struct strbuf *buf = *s; - const char *val = getenv(buf->buf); + for_each_string_list_item(item, &tr2_cfg_env_vars) { + const char *val = getenv(item->string); if (val && *val) - trace2_def_param_fl(file, line, buf->buf, val, &kvi); + trace2_def_param_fl(file, line, item->string, val, &kvi); } } @@ -595,8 +595,8 @@ void trailer_config_init(void) default_conf_info.where = WHERE_END; default_conf_info.if_exists = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR; default_conf_info.if_missing = MISSING_ADD; - git_config(git_trailer_default_config, NULL); - git_config(git_trailer_config, NULL); + repo_config(the_repository, git_trailer_default_config, NULL); + repo_config(the_repository, git_trailer_config, NULL); configured = 1; } diff --git a/transport.c b/transport.c index 6c2801bcbd..6ac8aa402b 100644 --- a/transport.c +++ b/transport.c @@ -54,14 +54,14 @@ static int transport_color_config(void) return 0; initialized = 1; - if (!git_config_get_string(key, &value)) + if (!repo_config_get_string(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 (!git_config_get_string(keys[i], &value)) { + if (!repo_config_get_string(the_repository, keys[i], &value)) { if (!value) return config_error_nonbool(keys[i]); if (color_parse(value, transport_colors[i]) < 0) @@ -202,7 +202,7 @@ static int fetch_refs_from_bundle(struct transport *transport, if (!data->get_refs_from_bundle_called) get_refs_from_bundle_inner(transport); - git_config(fetch_fsck_config_cb, &msg_types); + repo_config(the_repository, fetch_fsck_config_cb, &msg_types); opts.fsck_msg_types = msg_types.buf; ret = unbundle(the_repository, &data->header, data->fd, @@ -1042,7 +1042,7 @@ static const struct string_list *protocol_allow_list(void) if (enabled < 0) { const char *v = getenv("GIT_ALLOW_PROTOCOL"); if (v) { - string_list_split(&allowed, v, ':', -1); + string_list_split(&allowed, v, ":", -1); string_list_sort(&allowed); enabled = 1; } else { @@ -1078,7 +1078,7 @@ static enum protocol_allow_config get_protocol_config(const char *type) char *value; /* first check the per-protocol config */ - if (!git_config_get_string(key, &value)) { + if (!repo_config_get_string(the_repository, key, &value)) { enum protocol_allow_config ret = parse_protocol_config(key, value); free(key); @@ -1088,7 +1088,7 @@ static enum protocol_allow_config get_protocol_config(const char *type) free(key); /* if defined, fallback to user-defined default for unknown protocols */ - if (!git_config_get_string("protocol.allow", &value)) { + if (!repo_config_get_string(the_repository, "protocol.allow", &value)) { enum protocol_allow_config ret = parse_protocol_config("protocol.allow", value); free(value); @@ -1243,7 +1243,7 @@ struct transport *transport_get(struct remote *remote, const char *url) ret->smart_options->receivepack = remote->receivepack; } - ret->hash_algo = &hash_algos[GIT_HASH_SHA1]; + ret->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY]; return ret; } @@ -1602,7 +1602,7 @@ int transport_get_remote_bundle_uri(struct transport *transport) * Don't request bundle-uri from the server unless configured to * do so by the transfer.bundleURI=true config option. */ - if (git_config_get_bool("transfer.bundleuri", &value) || !value) + if (repo_config_get_bool(the_repository, "transfer.bundleuri", &value) || !value) return 0; if (!transport->bundles->baseURI) diff --git a/tree-diff.c b/tree-diff.c index e00fc2f450..5988148b60 100644 --- a/tree-diff.c +++ b/tree-diff.c @@ -13,6 +13,7 @@ #include "tree-walk.h" #include "environment.h" #include "repository.h" +#include "dir.h" /* * Some mode bits are also used internally for computations. @@ -48,6 +49,73 @@ free((x)); \ } while(0) +/* Returns true if and only if "dir" is a leading directory of "path" */ +static int is_dir_prefix(const char *path, const char *dir, int dirlen) +{ + return !strncmp(path, dir, dirlen) && + (!path[dirlen] || path[dirlen] == '/'); +} + +static int check_recursion_depth(const struct strbuf *name, + const struct pathspec *ps, + int max_depth) +{ + int i; + + if (!ps->nr) + return within_depth(name->buf, name->len, 1, max_depth); + + /* + * We look through the pathspecs in reverse-sorted order, because we + * want to find the longest match first (e.g., "a/b" is better for + * checking depth than "a/b/c"). + */ + for (i = ps->nr - 1; i >= 0; i--) { + const struct pathspec_item *item = ps->items+i; + + /* + * If the name to match is longer than the pathspec, then we + * are only interested if the pathspec matches and we are + * within the allowed depth. + */ + if (name->len >= item->len) { + if (!is_dir_prefix(name->buf, item->match, item->len)) + continue; + return within_depth(name->buf + item->len, + name->len - item->len, + 1, max_depth); + } + + /* + * Otherwise, our name is shorter than the pathspec. We need to + * check if it is a prefix of the pathspec; if so, we must + * always recurse in order to process further (the resulting + * paths we find might or might not match our pathspec, but we + * cannot know until we recurse). + */ + if (is_dir_prefix(item->match, name->buf, name->len)) + return 1; + } + return 0; +} + +static int should_recurse(const struct strbuf *name, struct diff_options *opt) +{ + if (!opt->flags.recursive) + return 0; + if (!opt->max_depth_valid) + return 1; + + /* + * We catch this during diff_setup_done, but let's double-check + * against any internal munging. + */ + if (opt->pathspec.has_wildcard) + BUG("wildcard pathspecs are incompatible with max-depth"); + + return check_recursion_depth(name, &opt->pathspec, opt->max_depth); +} + static void ll_diff_tree_paths( struct combine_diff_path ***tail, const struct object_id *oid, const struct object_id **parents_oid, int nparent, @@ -170,9 +238,13 @@ static void emit_path(struct combine_diff_path ***tail, mode = 0; } - if (opt->flags.recursive && isdir) { - recurse = 1; - emitthis = opt->flags.tree_in_recursive; + if (isdir) { + strbuf_add(base, path, pathlen); + if (should_recurse(base, opt)) { + recurse = 1; + emitthis = opt->flags.tree_in_recursive; + } + strbuf_setlen(base, old_baselen); } if (emitthis) { diff --git a/tree-walk.c b/tree-walk.c index 90655d5237..e449a1320e 100644 --- a/tree-walk.c +++ b/tree-walk.c @@ -6,7 +6,7 @@ #include "gettext.h" #include "hex.h" #include "object-file.h" -#include "object-store.h" +#include "odb.h" #include "trace2.h" #include "tree.h" #include "pathspec.h" @@ -90,7 +90,7 @@ void *fill_tree_descriptor(struct repository *r, void *buf = NULL; if (oid) { - buf = read_object_with_reference(r, oid, OBJ_TREE, &size, NULL); + buf = odb_read_object_peeled(r->objects, oid, OBJ_TREE, &size, NULL); if (!buf) die(_("unable to read tree (%s)"), oid_to_hex(oid)); } @@ -611,7 +611,7 @@ int get_tree_entry(struct repository *r, unsigned long size; struct object_id root; - tree = read_object_with_reference(r, tree_oid, OBJ_TREE, &size, &root); + tree = odb_read_object_peeled(r->objects, tree_oid, OBJ_TREE, &size, &root); if (!tree) return -1; @@ -681,10 +681,8 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r, void *tree; struct object_id root; unsigned long size; - tree = read_object_with_reference(r, - ¤t_tree_oid, - OBJ_TREE, &size, - &root); + tree = odb_read_object_peeled(r->objects, ¤t_tree_oid, + OBJ_TREE, &size, &root); if (!tree) goto done; @@ -795,9 +793,9 @@ enum get_oid_result get_tree_entry_follow_symlinks(struct repository *r, */ retval = DANGLING_SYMLINK; - contents = repo_read_object_file(r, - ¤t_tree_oid, &type, - &link_len); + contents = odb_read_object(r->objects, + ¤t_tree_oid, &type, + &link_len); if (!contents) goto done; @@ -4,7 +4,7 @@ #include "hex.h" #include "tree.h" #include "object-name.h" -#include "object-store.h" +#include "odb.h" #include "commit.h" #include "alloc.h" #include "tree-walk.h" @@ -193,8 +193,8 @@ int parse_tree_gently(struct tree *item, int quiet_on_missing) if (item->object.parsed) return 0; - buffer = repo_read_object_file(the_repository, &item->object.oid, - &type, &size); + buffer = odb_read_object(the_repository->objects, &item->object.oid, + &type, &size); if (!buffer) return quiet_on_missing ? -1 : error("Could not read %s", diff --git a/unpack-trees.c b/unpack-trees.c index 471837f032..f38c761ab9 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -26,7 +26,7 @@ #include "symlinks.h" #include "trace2.h" #include "fsmonitor.h" -#include "object-store.h" +#include "odb.h" #include "promisor-remote.h" #include "entry.h" #include "parallel-checkout.h" diff --git a/upload-pack.c b/upload-pack.c index 26f29b85b5..f78fabc1e1 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -10,7 +10,7 @@ #include "pkt-line.h" #include "sideband.h" #include "repository.h" -#include "object-store.h" +#include "odb.h" #include "oid-array.h" #include "object.h" #include "commit.h" @@ -476,20 +476,17 @@ static void create_pack_file(struct upload_pack_data *pack_data, static int do_got_oid(struct upload_pack_data *data, const struct object_id *oid) { - int we_knew_they_have = 0; struct object *o = parse_object_with_flags(the_repository, oid, PARSE_OBJECT_SKIP_HASH_CHECK | PARSE_OBJECT_DISCARD_TREE); if (!o) die("oops (%s)", oid_to_hex(oid)); + if (o->type == OBJ_COMMIT) { struct commit_list *parents; struct commit *commit = (struct commit *)o; - if (o->flags & THEY_HAVE) - we_knew_they_have = 1; - else - o->flags |= THEY_HAVE; + if (!data->oldest_have || (commit->date < data->oldest_have)) data->oldest_have = commit->date; for (parents = commit->parents; @@ -497,11 +494,13 @@ static int do_got_oid(struct upload_pack_data *data, const struct object_id *oid parents = parents->next) parents->item->object.flags |= THEY_HAVE; } - if (!we_knew_they_have) { - add_object_array(o, NULL, &data->have_obj); - return 1; - } - return 0; + + if (o->flags & THEY_HAVE) + return 0; + o->flags |= THEY_HAVE; + + add_object_array(o, NULL, &data->have_obj); + return 1; } static int got_oid(struct upload_pack_data *data, @@ -509,7 +508,7 @@ static int got_oid(struct upload_pack_data *data, { if (get_oid_hex(hex, oid)) die("git upload-pack: expected SHA1 object, got '%s'", hex); - if (!has_object(the_repository, oid, 0)) + if (!odb_has_object(the_repository->objects, oid, 0)) return -1; return do_got_oid(data, oid); } @@ -1685,7 +1684,7 @@ static void process_args(struct packet_reader *request, if (data->uri_protocols.nr) send_err_and_die(data, "multiple packfile-uris lines forbidden"); - string_list_split(&data->uri_protocols, p, ',', -1); + string_list_split(&data->uri_protocols, p, ",", -1); continue; } @@ -67,6 +67,8 @@ static NORETURN void usage_builtin(const char *err, va_list params) static void die_message_builtin(const char *err, va_list params) { + if (!err) + return; trace2_cmd_error_va(err, params); vreportf(_("fatal: "), err, params); } @@ -190,7 +192,8 @@ static void show_usage_if_asked_helper(const char *err, ...) void show_usage_if_asked(int ac, const char **av, const char *err) { - if (ac == 2 && !strcmp(av[1], "-h")) + if (ac == 2 && (!strcmp(av[1], "-h") || + !strcmp(av[1], "--help-all"))) show_usage_if_asked_helper(err); } @@ -372,3 +375,15 @@ void bug_fl(const char *file, int line, const char *fmt, ...) trace2_cmd_error_va(fmt, ap); va_end(ap); } + +NORETURN void you_still_use_that(const char *command_name) +{ + fprintf(stderr, + _("'%s' is nominated for removal.\n" + "If you still use this command, please add an extra\n" + "option, '--i-still-use-this', on the command line\n" + "and let us know you still use it by sending an e-mail\n" + "to <git@vger.kernel.org>. Thanks.\n"), + command_name); + die(_("refusing to run without --i-still-use-this")); +} diff --git a/userdiff.c b/userdiff.c index 05776ccd10..fe710a68bf 100644 --- a/userdiff.c +++ b/userdiff.c @@ -327,6 +327,10 @@ PATTERNS("python", "|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?" "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"), /* -- */ +PATTERNS("r", + "^[ \t]*([a-zA-z][a-zA-Z0-9_.]*[ \t]*(<-|=)[ \t]*function.*)$", + /* -- */ + "[^ \t]+"), PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$", /* -- */ diff --git a/versioncmp.c b/versioncmp.c index b6eebdb989..3a81b17bc1 100644 --- a/versioncmp.c +++ b/versioncmp.c @@ -167,8 +167,8 @@ int versioncmp(const char *s1, const char *s2) const char *const oldk = "versionsort.prereleasesuffix"; const struct string_list *newl; const struct string_list *oldl; - int new = git_config_get_string_multi(newk, &newl); - int old = git_config_get_string_multi(oldk, &oldl); + int new = repo_config_get_string_multi(the_repository, newk, &newl); + int old = repo_config_get_string_multi(the_repository, oldk, &oldl); if (!new && !old) warning("ignoring %s because %s is set", oldk, newk); @@ -5,7 +5,7 @@ #include "hex.h" #include "walker.h" #include "repository.h" -#include "object-store.h" +#include "odb.h" #include "commit.h" #include "strbuf.h" #include "tree.h" @@ -14,6 +14,7 @@ #include "blob.h" #include "refs.h" #include "progress.h" +#include "prio-queue.h" static struct object_id current_commit_oid; @@ -78,7 +79,7 @@ static int process_tree(struct walker *walker, struct tree *tree) #define SEEN (1U << 1) #define TO_SCAN (1U << 2) -static struct commit_list *complete = NULL; +static struct prio_queue complete = { compare_commits_by_commit_date }; static int process_commit(struct walker *walker, struct commit *commit) { @@ -87,7 +88,10 @@ static int process_commit(struct walker *walker, struct commit *commit) if (repo_parse_commit(the_repository, commit)) return -1; - while (complete && complete->item->date >= commit->date) { + while (complete.nr) { + struct commit *item = prio_queue_peek(&complete); + if (item->date < commit->date) + break; pop_most_recent_commit(&complete, COMPLETE); } @@ -150,8 +154,8 @@ static int process(struct walker *walker, struct object *obj) return 0; obj->flags |= SEEN; - if (has_object(the_repository, &obj->oid, - HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) { + if (odb_has_object(the_repository->objects, &obj->oid, + HAS_OBJECT_RECHECK_PACKED | HAS_OBJECT_FETCH_PROMISOR)) { /* We already have it, so we should scan it now. */ obj->flags |= TO_SCAN; } @@ -233,7 +237,7 @@ static int mark_complete(const char *path UNUSED, if (commit) { commit->object.flags |= COMPLETE; - commit_list_insert(commit, &complete); + prio_queue_put(&complete, commit); } return 0; } @@ -302,7 +306,6 @@ int walker_fetch(struct walker *walker, int targets, char **target, if (!walker->get_recover) { refs_for_each_ref(get_main_ref_store(the_repository), mark_complete, NULL); - commit_list_sort_by_date(&complete); } for (i = 0; i < targets; i++) { diff --git a/worktree.c b/worktree.c index c34b9eb74e..a2a5f51f29 100644 --- a/worktree.c +++ b/worktree.c @@ -991,9 +991,9 @@ done: static int move_config_setting(const char *key, const char *value, const char *from_file, const char *to_file) { - if (git_config_set_in_file_gently(to_file, key, NULL, value)) + if (repo_config_set_in_file_gently(the_repository, to_file, key, NULL, value)) return error(_("unable to set %s in '%s'"), key, to_file); - if (git_config_set_in_file_gently(from_file, key, NULL, NULL)) + if (repo_config_set_in_file_gently(the_repository, from_file, key, NULL, NULL)) return error(_("unable to unset %s in '%s'"), key, from_file); return 0; } @@ -1013,7 +1013,7 @@ int init_worktree_config(struct repository *r) */ if (r->repository_format_worktree_config) return 0; - if ((res = git_config_set_gently("extensions.worktreeConfig", "true"))) + if ((res = repo_config_set_gently(the_repository, "extensions.worktreeConfig", "true"))) return error(_("failed to set extensions.worktreeConfig setting")); common_config_file = xstrfmt("%s/config", r->commondir); @@ -1077,7 +1077,7 @@ void write_worktree_linking_files(struct strbuf dotgit, struct strbuf gitdir, if (use_relative_paths && !the_repository->repository_format_relative_worktrees) { if (upgrade_repository_format(1) < 0) die(_("unable to upgrade repository format to support relative worktrees")); - if (git_config_set_gently("extensions.relativeWorktrees", "true")) + if (repo_config_set_gently(the_repository, "extensions.relativeWorktrees", "true")) die(_("unable to set extensions.relativeWorktrees setting")); the_repository->repository_format_relative_worktrees = 1; } diff --git a/wt-status.c b/wt-status.c index 454601afa1..21dabab77d 100644 --- a/wt-status.c +++ b/wt-status.c @@ -972,7 +972,8 @@ static void wt_longstatus_print_changed(struct wt_status *s) wt_longstatus_print_trailer(s); } -static int stash_count_refs(struct object_id *ooid UNUSED, +static int stash_count_refs(const char *refname UNUSED, + struct object_id *ooid UNUSED, struct object_id *noid UNUSED, const char *email UNUSED, timestamp_t timestamp UNUSED, int tz UNUSED, @@ -1351,8 +1352,8 @@ static int split_commit_in_progress(struct wt_status *s) */ static void abbrev_oid_in_line(struct strbuf *line) { - struct strbuf **split; - int i; + struct string_list split = STRING_LIST_INIT_DUP; + struct object_id oid; if (starts_with(line->buf, "exec ") || starts_with(line->buf, "x ") || @@ -1360,26 +1361,15 @@ static void abbrev_oid_in_line(struct strbuf *line) starts_with(line->buf, "l ")) return; - split = strbuf_split_max(line, ' ', 3); - if (split[0] && split[1]) { - struct object_id oid; - - /* - * strbuf_split_max left a space. Trim it and re-add - * it after abbreviation. - */ - strbuf_trim(split[1]); - if (!repo_get_oid(the_repository, split[1]->buf, &oid)) { - strbuf_reset(split[1]); - strbuf_add_unique_abbrev(split[1], &oid, - DEFAULT_ABBREV); - strbuf_addch(split[1], ' '); - strbuf_reset(line); - for (i = 0; split[i]; i++) - strbuf_addbuf(line, split[i]); - } + if ((2 <= string_list_split(&split, line->buf, " ", 2)) && + !repo_get_oid(the_repository, split.items[1].string, &oid)) { + strbuf_reset(line); + strbuf_addf(line, "%s ", split.items[0].string); + strbuf_add_unique_abbrev(line, &oid, DEFAULT_ABBREV); + for (size_t i = 2; i < split.nr; i++) + strbuf_addf(line, " %s", split.items[i].string); } - strbuf_list_free(split); + string_list_clear(&split, 0); } static int read_rebase_todolist(const char *fname, struct string_list *lines) @@ -1664,7 +1654,8 @@ struct grab_1st_switch_cbdata { struct object_id noid; }; -static int grab_1st_switch(struct object_id *ooid UNUSED, +static int grab_1st_switch(const char *refname UNUSED, + struct object_id *ooid UNUSED, struct object_id *noid, const char *email UNUSED, timestamp_t timestamp UNUSED, int tz UNUSED, diff --git a/xdiff-interface.c b/xdiff-interface.c index 1edcd319e6..4971f722b3 100644 --- a/xdiff-interface.c +++ b/xdiff-interface.c @@ -2,10 +2,11 @@ #define DISABLE_SIGN_COMPARE_WARNINGS #include "git-compat-util.h" +#include "environment.h" #include "gettext.h" #include "config.h" #include "hex.h" -#include "object-store.h" +#include "odb.h" #include "strbuf.h" #include "xdiff-interface.h" #include "xdiff/xtypes.h" @@ -187,7 +188,7 @@ void read_mmblob(mmfile_t *ptr, const struct object_id *oid) return; } - ptr->ptr = repo_read_object_file(the_repository, oid, &type, &size); + ptr->ptr = odb_read_object(the_repository->objects, oid, &type, &size); if (!ptr->ptr || type != OBJ_BLOB) die("unable to read blob object %s", oid_to_hex(oid)); ptr->size = size; diff --git a/xdiff-interface.h b/xdiff-interface.h index 1ed430b622..dfc55daddf 100644 --- a/xdiff-interface.h +++ b/xdiff-interface.h @@ -28,9 +28,9 @@ * from an error internal to xdiff, xdiff itself will see that * non-zero return and translate it to -1. * - * See "diff_grep" in diffcore-pickaxe.c for a trick to work around - * this, i.e. using the "consume_callback_data" to note the desired - * early return. + * See "diff_grep" in diffcore-pickaxe.c and "quick_consume" in diff.c + * for a trick to work around this, i.e. using the "consume_callback_data" + * to note the desired early return. */ typedef int (*xdiff_emit_line_fn)(void *, char *, unsigned long); typedef void (*xdiff_emit_hunk_fn)(void *data, diff --git a/xdiff/xutils.c b/xdiff/xutils.c index 444a108f87..78d1cf74b1 100644 --- a/xdiff/xutils.c +++ b/xdiff/xutils.c @@ -249,7 +249,7 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags) return 1; } -static unsigned long xdl_hash_record_with_whitespace(char const **data, +unsigned long xdl_hash_record_with_whitespace(char const **data, char const *top, long flags) { unsigned long ha = 5381; char const *ptr = *data; @@ -294,19 +294,67 @@ static unsigned long xdl_hash_record_with_whitespace(char const **data, return ha; } -unsigned long xdl_hash_record(char const **data, char const *top, long flags) { - unsigned long ha = 5381; +/* + * Compiler reassociation barrier: pretend to modify X and Y to disallow + * changing evaluation order with respect to following uses of X and Y. + */ +#ifdef __GNUC__ +#define REASSOC_FENCE(x, y) __asm__("" : "+r"(x), "+r"(y)) +#else +#define REASSOC_FENCE(x, y) +#endif + +unsigned long xdl_hash_record_verbatim(char const **data, char const *top) { + unsigned long ha = 5381, c0, c1; char const *ptr = *data; - - if (flags & XDF_WHITESPACE_FLAGS) - return xdl_hash_record_with_whitespace(data, top, flags); - +#if 0 + /* + * The baseline form of the optimized loop below. This is the djb2 + * hash (the above function uses a variant with XOR instead of ADD). + */ for (; ptr < top && *ptr != '\n'; ptr++) { ha += (ha << 5); - ha ^= (unsigned long) *ptr; + ha += (unsigned long) *ptr; } *data = ptr < top ? ptr + 1: ptr; - +#else + /* Process two characters per iteration. */ + if (top - ptr >= 2) do { + if ((c0 = ptr[0]) == '\n') { + *data = ptr + 1; + return ha; + } + if ((c1 = ptr[1]) == '\n') { + *data = ptr + 2; + c0 += ha; + REASSOC_FENCE(c0, ha); + ha = ha * 32 + c0; + return ha; + } + /* + * Combine characters C0 and C1 into the hash HA. We have + * HA = (HA * 33 + C0) * 33 + C1, and we want to ensure + * that dependency chain over HA is just one multiplication + * and one addition, i.e. we want to evaluate this as + * HA = HA * 33 * 33 + (C0 * 33 + C1), and likewise prefer + * (C0 * 32 + (C0 + C1)) for the expression in parenthesis. + */ + ha *= 33 * 33; + c1 += c0; + REASSOC_FENCE(c1, c0); + c1 += c0 * 32; + REASSOC_FENCE(c1, ha); + ha += c1; + + ptr += 2; + } while (ptr < top - 1); + *data = top; + if (ptr < top && (c0 = ptr[0]) != '\n') { + c0 += ha; + REASSOC_FENCE(c0, ha); + ha = ha * 32 + c0; + } +#endif return ha; } diff --git a/xdiff/xutils.h b/xdiff/xutils.h index fd0bba94e8..13f6831047 100644 --- a/xdiff/xutils.h +++ b/xdiff/xutils.h @@ -34,7 +34,15 @@ void *xdl_cha_alloc(chastore_t *cha); long xdl_guess_lines(mmfile_t *mf, long sample); int xdl_blankline(const char *line, long size, long flags); int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags); -unsigned long xdl_hash_record(char const **data, char const *top, long flags); +unsigned long xdl_hash_record_verbatim(char const **data, char const *top); +unsigned long xdl_hash_record_with_whitespace(char const **data, char const *top, long flags); +static inline unsigned long xdl_hash_record(char const **data, char const *top, long flags) +{ + if (flags & XDF_WHITESPACE_FLAGS) + return xdl_hash_record_with_whitespace(data, top, flags); + else + return xdl_hash_record_verbatim(data, top); +} unsigned int xdl_hashbits(unsigned int size); int xdl_num_out(char *out, long val); int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2, |
