summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/CONTRIBUTING.md3
-rw-r--r--Documentation/CodingGuidelines7
-rw-r--r--Documentation/Makefile1
-rw-r--r--Documentation/MyFirstContribution.txt4
-rw-r--r--Documentation/RelNotes/2.27.0.txt4
-rw-r--r--Documentation/RelNotes/2.28.0.txt236
-rw-r--r--Documentation/RelNotes/2.29.0.txt91
-rw-r--r--Documentation/SubmittingPatches15
-rw-r--r--Documentation/config/diff.txt4
-rw-r--r--Documentation/config/feature.txt10
-rw-r--r--Documentation/config/fetch.txt3
-rw-r--r--Documentation/config/fmt-merge-msg.txt12
-rw-r--r--Documentation/config/init.txt4
-rw-r--r--Documentation/config/protocol.txt3
-rw-r--r--Documentation/diff-options.txt5
-rw-r--r--Documentation/git-branch.txt2
-rw-r--r--Documentation/git-bugreport.txt1
-rw-r--r--Documentation/git-cat-file.txt2
-rw-r--r--Documentation/git-clone.txt2
-rw-r--r--Documentation/git-commit-graph.txt11
-rw-r--r--Documentation/git-diff.txt23
-rw-r--r--Documentation/git-fast-export.txt29
-rw-r--r--Documentation/git-fast-import.txt9
-rw-r--r--Documentation/git-fetch.txt8
-rw-r--r--Documentation/git-for-each-ref.txt27
-rw-r--r--Documentation/git-help.txt4
-rw-r--r--Documentation/git-http-fetch.txt9
-rw-r--r--Documentation/git-index-pack.txt12
-rw-r--r--Documentation/git-init.txt9
-rw-r--r--Documentation/git-log.txt7
-rw-r--r--Documentation/git-ls-remote.txt4
-rw-r--r--Documentation/git-rebase.txt2
-rw-r--r--Documentation/git-rev-list.txt40
-rw-r--r--Documentation/git-show-index.txt11
-rw-r--r--Documentation/git-sparse-checkout.txt30
-rw-r--r--Documentation/git-submodule.txt12
-rw-r--r--Documentation/git-worktree.txt4
-rw-r--r--Documentation/git.txt13
-rw-r--r--Documentation/giteveryday.txt10
-rw-r--r--Documentation/githooks.txt29
-rw-r--r--Documentation/gitmodules.txt6
-rw-r--r--Documentation/gitremote-helpers.txt37
-rw-r--r--Documentation/gitworkflows.txt18
-rw-r--r--Documentation/howto/maintain-git.txt52
-rw-r--r--Documentation/howto/rebase-from-internal-branch.txt32
-rw-r--r--Documentation/howto/revert-branch-rebase.txt32
-rw-r--r--Documentation/howto/update-hook-example.txt6
-rw-r--r--Documentation/pretty-formats.txt4
-rw-r--r--Documentation/rev-list-description.txt61
-rw-r--r--Documentation/rev-list-options.txt35
-rw-r--r--Documentation/revisions.txt3
-rw-r--r--Documentation/technical/commit-graph-format.txt2
-rw-r--r--Documentation/technical/http-protocol.txt2
-rw-r--r--Documentation/technical/pack-protocol.txt6
-rw-r--r--Documentation/technical/packfile-uri.txt78
-rw-r--r--Documentation/technical/protocol-capabilities.txt15
-rw-r--r--Documentation/technical/protocol-v2.txt59
-rw-r--r--Documentation/technical/reftable.txt1083
-rw-r--r--Documentation/user-manual.txt2
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile2
l---------RelNotes2
-rw-r--r--add-patch.c32
-rw-r--r--alloc.c18
-rw-r--r--alloc.h2
-rw-r--r--blame.c2
-rw-r--r--blob.c2
-rw-r--r--bloom.c26
-rw-r--r--bloom.h1
-rw-r--r--branch.c2
-rw-r--r--bugreport.c10
-rw-r--r--builtin/bisect--helper.c16
-rw-r--r--builtin/branch.c4
-rw-r--r--builtin/cat-file.c2
-rw-r--r--builtin/checkout.c6
-rw-r--r--builtin/clean.c49
-rw-r--r--builtin/clone.c47
-rw-r--r--builtin/commit-graph.c78
-rw-r--r--builtin/config.c2
-rw-r--r--builtin/diff-files.c7
-rw-r--r--builtin/diff.c147
-rw-r--r--builtin/fast-export.c162
-rw-r--r--builtin/fetch-pack.c19
-rw-r--r--builtin/fetch.c12
-rw-r--r--builtin/fsck.c4
-rw-r--r--builtin/grep.c2
-rw-r--r--builtin/index-pack.c14
-rw-r--r--builtin/init-db.c33
-rw-r--r--builtin/ls-remote.c4
-rw-r--r--builtin/merge.c2
-rw-r--r--builtin/mv.c7
-rw-r--r--builtin/pack-objects.c116
-rw-r--r--builtin/pull.c4
-rw-r--r--builtin/receive-pack.c10
-rw-r--r--builtin/reflog.c2
-rw-r--r--builtin/show-index.c29
-rw-r--r--builtin/sparse-checkout.c6
-rw-r--r--builtin/submodule--helper.c46
-rw-r--r--builtin/worktree.c158
-rw-r--r--bundle.c22
-rw-r--r--bundle.h1
-rw-r--r--cache.h4
-rwxr-xr-xci/config/allow-ref.sample (renamed from ci/config/allow-refs.sample)5
-rwxr-xr-xci/lib.sh4
-rw-r--r--column.c2
-rw-r--r--command-list.txt2
-rw-r--r--commit-graph.c472
-rw-r--r--commit-graph.h23
-rw-r--r--commit-reach.c93
-rw-r--r--commit-reach.h4
-rw-r--r--commit-slab-decl.h1
-rw-r--r--commit-slab-impl.h13
-rw-r--r--commit-slab.h10
-rw-r--r--commit.c14
-rw-r--r--commit.h2
-rwxr-xr-xcompat/vcbuild/scripts/clink.pl4
-rw-r--r--connect.c156
-rw-r--r--connect.h7
-rw-r--r--connected.c8
-rw-r--r--contrib/coccinelle/commit.cocci18
-rw-r--r--contrib/completion/git-completion.bash278
-rw-r--r--contrib/completion/git-prompt.sh26
-rwxr-xr-xcontrib/subtree/t/t7900-subtree.sh6
-rw-r--r--diff-lib.c3
-rw-r--r--diff.c16
-rw-r--r--diff.h10
-rw-r--r--dir.c69
-rw-r--r--entry.c3
-rw-r--r--fast-import.c25
-rw-r--r--fetch-pack.c164
-rw-r--r--fetch-pack.h2
-rw-r--r--fmt-merge-msg.c38
-rw-r--r--fsck.c4
-rw-r--r--fsmonitor.c2
-rw-r--r--fuzz-commit-graph.c2
-rwxr-xr-xgit-add--interactive.perl21
-rwxr-xr-xgit-bisect.sh4
-rw-r--r--git-compat-util.h6
-rwxr-xr-xgit-cvsexportcommit.perl14
-rwxr-xr-xgit-cvsimport.perl8
-rwxr-xr-xgit-cvsserver.perl37
-rw-r--r--git-gui/lib/choose_repository.tcl27
-rwxr-xr-xgit-p4.py9
-rwxr-xr-xgit-send-email.perl8
-rwxr-xr-xgit-submodule.sh32
-rwxr-xr-xgit-svn.perl25
-rw-r--r--git.c6
-rw-r--r--grep.c2
-rw-r--r--hashmap.h2
-rw-r--r--help.c1
-rw-r--r--http-fetch.c126
-rw-r--r--http-push.c16
-rw-r--r--http-walker.c5
-rw-r--r--http.c125
-rw-r--r--http.h31
-rw-r--r--imap-send.c2
-rw-r--r--line-log.c43
-rw-r--r--line-log.h5
-rw-r--r--list-objects-filter-options.c3
-rw-r--r--object-store.h1
-rw-r--r--object.c4
-rw-r--r--object.h10
-rw-r--r--pack-write.c5
-rw-r--r--packfile.c1
-rw-r--r--perl/Git/IndexInfo.pm6
-rw-r--r--perl/Git/SVN.pm83
-rw-r--r--perl/Git/SVN/Editor.pm8
-rw-r--r--perl/Git/SVN/Fetcher.pm6
-rw-r--r--perl/Git/SVN/Log.pm2
-rw-r--r--perl/Git/SVN/Ra.pm4
-rw-r--r--pkt-line.c18
-rw-r--r--pkt-line.h14
-rw-r--r--po/bg.po7130
-rw-r--r--po/ca.po12136
-rw-r--r--po/de.po8080
-rw-r--r--po/es.po8112
-rw-r--r--po/fr.po7992
-rw-r--r--po/git.pot7761
-rw-r--r--po/it.po8140
-rw-r--r--po/sv.po8093
-rw-r--r--po/tr.po8013
-rw-r--r--po/vi.po8168
-rw-r--r--po/zh_CN.po7918
-rw-r--r--po/zh_TW.po8769
-rw-r--r--protocol.c4
-rw-r--r--read-cache.c14
-rw-r--r--ref-filter.c11
-rw-r--r--refs.c159
-rw-r--r--refs.h27
-rw-r--r--refs/files-backend.c6
-rw-r--r--refs/refs-internal.h24
-rw-r--r--remote-curl.c122
-rw-r--r--remote-testsvn.c10
-rw-r--r--remote.c14
-rw-r--r--remote.h3
-rw-r--r--repo-settings.c8
-rw-r--r--repository.h6
-rw-r--r--revision.c142
-rw-r--r--revision.h13
-rw-r--r--run-command.c1
-rw-r--r--run-command.h1
-rw-r--r--send-pack.c8
-rw-r--r--serve.c29
-rw-r--r--setup.c128
-rw-r--r--sha1-file.c3
-rw-r--r--shallow.c14
-rw-r--r--strbuf.c5
-rw-r--r--strbuf.h1
-rw-r--r--t/README14
-rw-r--r--t/helper/test-oid-array.c3
-rw-r--r--t/helper/test-pkt-line.c4
-rw-r--r--t/helper/test-reach.c4
-rw-r--r--t/helper/test-ref-store.c2
-rw-r--r--t/helper/test-regex.c94
-rw-r--r--t/lib-git-svn.sh17
-rw-r--r--t/lib-httpd.sh2
-rw-r--r--t/lib-httpd/apache.conf8
-rw-r--r--t/lib-httpd/incomplete-body-upload-pack-v2-http.sh3
-rw-r--r--t/lib-httpd/incomplete-length-upload-pack-v2-http.sh3
-rw-r--r--t/lib-submodule-update.sh75
-rw-r--r--t/lib-t6000.sh5
-rwxr-xr-xt/perf/p1400-update-ref.sh32
-rwxr-xr-xt/t0000-basic.sh18
-rwxr-xr-xt/t0001-init.sh26
-rwxr-xr-xt/t0002-gitfile.sh2
-rwxr-xr-xt/t0410-partial-clone.sh34
-rwxr-xr-xt/t1013-read-tree-submodule.sh4
-rwxr-xr-xt/t1050-large.sh6
-rwxr-xr-xt/t1090-sparse-checkout-scope.sh1
-rwxr-xr-xt/t1091-sparse-checkout-builtin.sh22
-rwxr-xr-xt/t1302-repo-version.sh9
-rwxr-xr-xt/t1400-update-ref.sh32
-rwxr-xr-xt/t1416-ref-transaction-hooks.sh109
-rwxr-xr-xt/t1450-fsck.sh43
-rwxr-xr-xt/t1506-rev-parse-diagnosis.sh2
-rwxr-xr-xt/t2013-checkout-submodule.sh4
-rwxr-xr-xt/t2018-checkout-branch.sh10
-rwxr-xr-xt/t2027-checkout-track.sh24
-rwxr-xr-xt/t2060-switch.sh8
-rwxr-xr-xt/t2203-add-intent.sh52
-rwxr-xr-xt/t2401-worktree-prune.sh24
-rwxr-xr-xt/t2403-worktree-move.sh21
-rwxr-xr-xt/t2404-worktree-config.sh4
-rwxr-xr-xt/t3200-branch.sh69
-rwxr-xr-xt/t3426-rebase-submodule.sh10
-rwxr-xr-xt/t3430-rebase-merges.sh2
-rwxr-xr-xt/t3432-rebase-fast-forward.sh7
-rwxr-xr-xt/t3512-cherry-pick-submodule.sh2
-rwxr-xr-xt/t3513-revert-submodule.sh9
-rwxr-xr-xt/t3701-add-interactive.sh32
-rwxr-xr-xt/t3906-stash-submodule.sh9
-rwxr-xr-xt/t4010-diff-pathspec.sh4
-rwxr-xr-xt/t4014-format-patch.sh21
-rwxr-xr-xt/t4045-diff-relative.sh82
-rwxr-xr-xt/t4068-diff-symmetric.sh91
-rwxr-xr-xt/t4137-apply-submodule.sh12
-rwxr-xr-xt/t4202-log.sh2
-rwxr-xr-xt/t4210-log-i18n.sh77
-rwxr-xr-xt/t4211-line-log.sh76
-rwxr-xr-xt/t4216-log-bloom.sh45
-rwxr-xr-xt/t4255-am-submodule.sh12
-rwxr-xr-xt/t5300-pack-object.sh45
-rwxr-xr-xt/t5302-pack-index.sh356
-rwxr-xr-xt/t5318-commit-graph.sh61
-rwxr-xr-xt/t5324-split-commit-graph.sh2
-rwxr-xr-xt/t5500-fetch-pack.sh6
-rwxr-xr-xt/t5505-remote.sh8
-rwxr-xr-xt/t5510-fetch.sh2
-rwxr-xr-xt/t5516-fetch-push.sh16
-rwxr-xr-xt/t5528-push-default.sh6
-rwxr-xr-xt/t5537-fetch-shallow.sh14
-rwxr-xr-xt/t5539-fetch-http-shallow.sh4
-rwxr-xr-xt/t5540-http-push-webdav.sh16
-rwxr-xr-xt/t5541-http-push-smart.sh30
-rwxr-xr-xt/t5550-http-fetch-dumb.sh48
-rwxr-xr-xt/t5551-http-fetch-smart.sh56
-rwxr-xr-xt/t5562-http-backend-content-length.sh5
-rwxr-xr-xt/t5572-pull-submodule.sh20
-rwxr-xr-xt/t5581-http-curl-verbose.sh2
-rwxr-xr-xt/t5601-clone.sh4
-rwxr-xr-xt/t5606-clone-options.sh24
-rwxr-xr-xt/t5608-clone-2gb.sh11
-rwxr-xr-xt/t5616-partial-clone.sh38
-rwxr-xr-xt/t5701-git-serve.sh25
-rwxr-xr-xt/t5702-protocol-v2.sh138
-rwxr-xr-xt/t5703-upload-pack-ref-in-want.sh19
-rwxr-xr-xt/t5704-protocol-violations.sh2
-rwxr-xr-xt/t5801/git-remote-testgit6
-rwxr-xr-xt/t6000-rev-list-misc.sh7
-rwxr-xr-xt/t6030-bisect-porcelain.sh2
-rwxr-xr-xt/t6041-bisect-submodule.sh9
-rwxr-xr-xt/t6046-merge-skip-unneeded-updates.sh2
-rwxr-xr-xt/t6050-replace.sh2
-rwxr-xr-xt/t6132-pathspec-exclude.sh33
-rwxr-xr-xt/t6200-fmt-merge-msg.sh22
-rwxr-xr-xt/t6300-for-each-ref.sh38
-rwxr-xr-xt/t7001-mv.sh17
-rwxr-xr-xt/t7061-wtstatus-ignore.sh25
-rwxr-xr-xt/t7107-reset-pathspec-file.sh9
-rwxr-xr-xt/t7112-reset-submodule.sh6
-rwxr-xr-xt/t7406-submodule-update.sh16
-rwxr-xr-xt/t7419-submodule-set-branch.sh7
-rwxr-xr-xt/t7613-merge-submodule.sh8
-rwxr-xr-xt/t7800-difftool.sh8
-rwxr-xr-xt/t8002-blame.sh11
-rwxr-xr-xt/t8014-blame-ignore-fuzzy.sh2
-rwxr-xr-xt/t9001-send-email.sh14
-rwxr-xr-xt/t9020-remote-svn.sh10
-rwxr-xr-xt/t9100-git-svn-basic.sh24
-rwxr-xr-xt/t9101-git-svn-props.sh12
-rwxr-xr-xt/t9104-git-svn-follow-parent.sh3
-rwxr-xr-xt/t9108-git-svn-glob.sh4
-rwxr-xr-xt/t9109-git-svn-multi-glob.sh6
-rwxr-xr-xt/t9168-git-svn-partially-globbed-names.sh8
-rwxr-xr-xt/t9300-fast-import.sh28
-rwxr-xr-xt/t9351-fast-export-anonymize.sh54
-rwxr-xr-xt/t9400-git-cvsserver-server.sh2
-rwxr-xr-xt/t9700/test.pl6
-rwxr-xr-xt/t9834-git-p4-file-dir-bug.sh2
-rwxr-xr-xt/t9902-completion.sh459
-rw-r--r--t/test-lib-functions.sh47
-rw-r--r--t/test-lib.sh10
-rw-r--r--tag.c2
-rw-r--r--trace.c20
-rw-r--r--trace.h6
-rw-r--r--transport-helper.c31
-rw-r--r--transport.c56
-rw-r--r--transport.h14
-rw-r--r--tree-diff.c30
-rw-r--r--tree-walk.c9
-rw-r--r--tree.c2
-rw-r--r--upload-pack.c869
-rw-r--r--worktree.c26
-rw-r--r--worktree.h11
-rw-r--r--wrapper.c8
-rw-r--r--wt-status.c43
-rw-r--r--wt-status.h2
337 files changed, 61966 insertions, 48955 deletions
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index e7b4e2f..c8755e3 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -16,4 +16,7 @@ If you prefer video, then [this talk](https://www.youtube.com/watch?v=Q7i_qQW__q
might be useful to you as the presenter walks you through the contribution
process by example.
+Or, you can follow the ["My First Contribution"](https://git-scm.com/docs/MyFirstContribution)
+tutorial for another example of the contribution process.
+
Your friendly Git community!
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index 227f46a..45465bc 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -489,16 +489,11 @@ For Python scripts:
- We follow PEP-8 (http://www.python.org/dev/peps/pep-0008/).
- - As a minimum, we aim to be compatible with Python 2.6 and 2.7.
+ - As a minimum, we aim to be compatible with Python 2.7.
- Where required libraries do not restrict us to Python 2, we try to
also be compatible with Python 3.1 and later.
- - When you must differentiate between Unicode literals and byte string
- literals, it is OK to use the 'b' prefix. Even though the Python
- documentation for version 2.6 does not mention this prefix, it has
- been supported since version 2.6.0.
-
Error Messages
- Do not end error messages with a full stop.
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 15d9d04..ecd0b34 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -93,6 +93,7 @@ TECH_DOCS += technical/protocol-capabilities
TECH_DOCS += technical/protocol-common
TECH_DOCS += technical/protocol-v2
TECH_DOCS += technical/racy-git
+TECH_DOCS += technical/reftable
TECH_DOCS += technical/send-pack-pipeline
TECH_DOCS += technical/shallow
TECH_DOCS += technical/signature-format
diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
index 427274d..d85c9b5 100644
--- a/Documentation/MyFirstContribution.txt
+++ b/Documentation/MyFirstContribution.txt
@@ -1179,8 +1179,8 @@ look at the section below this one for some context.)
[[after-approval]]
=== After Review Approval
-The Git project has four integration branches: `pu`, `next`, `master`, and
-`maint`. Your change will be placed into `pu` fairly early on by the maintainer
+The Git project has four integration branches: `seen`, `next`, `master`, and
+`maint`. Your change will be placed into `seen` fairly early on by the maintainer
while it is still in the review process; from there, when it is ready for wider
testing, it will be merged into `next`. Plenty of early testers use `next` and
may report issues. Eventually, changes in `next` will make it to `master`,
diff --git a/Documentation/RelNotes/2.27.0.txt b/Documentation/RelNotes/2.27.0.txt
index 44b4b61..15518d0 100644
--- a/Documentation/RelNotes/2.27.0.txt
+++ b/Documentation/RelNotes/2.27.0.txt
@@ -24,6 +24,10 @@ Backward compatibility notes
users may find annoying---those who prefer not to rebase need to
set the variable to false to squelch the warning.
+ * The transport protocol version 2, which was promoted to the default
+ in Git 2.26 release, turned out to have some remaining rough edges,
+ so it has been demoted from the default.
+
UI, Workflows & Features
diff --git a/Documentation/RelNotes/2.28.0.txt b/Documentation/RelNotes/2.28.0.txt
new file mode 100644
index 0000000..6baf781
--- /dev/null
+++ b/Documentation/RelNotes/2.28.0.txt
@@ -0,0 +1,236 @@
+Git 2.28 Release Notes
+======================
+
+Updates since v2.27
+-------------------
+
+Backward compatibility notes
+
+ * "fetch.writeCommitGraph" is deemed to be still a bit too risky and
+ is no longer part of the "feature.experimental" set.
+
+
+UI, Workflows & Features
+
+ * The commands in the "diff" family learned to honor "diff.relative"
+ configuration variable.
+
+ * The check in "git fsck" to ensure that the tree objects are sorted
+ still had corner cases it missed unsorted entries.
+
+ * The interface to redact sensitive information in the trace output
+ has been simplified.
+
+ * The command line completion (in contrib/) learned to complete
+ options that the "git switch" command takes.
+
+ * "git diff" used to take arguments in random and nonsense range
+ notation, e.g. "git diff A..B C", "git diff A..B C...D", etc.,
+ which has been cleaned up.
+
+ * "git diff-files" has been taught to say paths that are marked as
+ intent-to-add are new files, not modified from an empty blob.
+
+ * "git status" learned to report the status of sparse checkout.
+
+ * "git difftool" has trouble dealing with paths added to the index
+ with the intent-to-add bit.
+
+ * "git fast-export --anonymize" learned to take customized mapping to
+ allow its users to tweak its output more usable for debugging.
+
+ * The command line completion support (in contrib/) used to be
+ prepared to work with "set -u" but recent changes got a bit more
+ sloppy. This has been corrected.
+
+ * "git gui" now allows opening work trees from the start-up dialog.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * Code optimization for a common case.
+ (merge 8777616e4d an/merge-single-strategy-optim later to maint).
+
+ * We've adopted a convention that any on-stack structure can be
+ initialized to have zero values in all fields with "= { 0 }",
+ even when the first field happens to be a pointer, but sparse
+ complained that a null pointer should be spelled NULL for a long
+ time. Start using -Wno-universal-initializer option to squelch
+ it (the latest sparse has it on by default).
+
+ * "git log -L..." now takes advantage of the "which paths are touched
+ by this commit?" info stored in the commit-graph system.
+
+ * As FreeBSD is not the only platform whose regexp library reports
+ a REG_ILLSEQ error when fed invalid UTF-8, add logic to detect that
+ automatically and skip the affected tests.
+
+ * "git bugreport" learns to report what shell is in use.
+
+ * Support for GIT_CURL_VERBOSE has been rewritten in terms of
+ GIT_TRACE_CURL.
+
+ * Preliminary clean-ups around refs API, plus file format
+ specification documentation for the reftable backend.
+
+ * Workaround breakage in MSVC build, where "curl-config --cflags"
+ gives settings appropriate for GCC build.
+
+ * Code clean-up of "git clean" resulted in a fix of recent
+ performance regression.
+
+ * Code clean-up in the codepath that serves "git fetch" continues.
+
+ * "git merge-base --is-ancestor" is taught to take advantage of the
+ commit graph.
+
+ * Rewrite of parts of the scripted "git submodule" Porcelain command
+ continues; this time it is "git submodule set-branch" subcommand's
+ turn.
+
+ * The "fetch/clone" protocol has been updated to allow the server to
+ instruct the clients to grab pre-packaged packfile(s) in addition
+ to the packed object data coming over the wire.
+
+ * A misdesigned strbuf_write_fd() function has been retired.
+
+ * SHA-256 migration work continues, including CVS/SVN interface.
+
+ * A few fields in "struct commit" that do not have to always be
+ present have been moved to commit slabs.
+
+ * API cleanup for get_worktrees()
+
+ * By renumbering object flag bits, "struct object" managed to lose
+ bloated inter-field padding.
+
+ * The name of the primary branch in existing repositories, and the
+ default name used for the first branch in newly created
+ repositories, is made configurable, so that we can eventually wean
+ ourselves off of the hardcoded 'master'.
+
+ * The effort to avoid using test_must_fail on non-git command continues.
+
+ * In 2.28-rc0, we corrected a bug that some repository extensions are
+ honored by mistake even in a version 0 repositories (these
+ configuration variables in extensions.* namespace were supposed to
+ have special meaning in repositories whose version numbers are 1 or
+ higher), but this was a bit too big a change. The behaviour in
+ recent versions of Git where certain extensions.* were honored by
+ mistake even in version 0 repositories has been restored.
+
+
+Fixes since v2.27
+-----------------
+
+ * The "--prepare-p4-only" option of "git p4" is supposed to stop
+ after replaying one changeset, but kept going (by mistake?)
+
+ * The error message from "git checkout -b foo -t bar baz" was
+ confusing.
+
+ * Some repositories in the wild have commits that record nonsense
+ committer timezone (e.g. rails.git); "git fast-import" learned an
+ option to pass these nonsense timestamps intact to allow recreating
+ existing repositories as-is.
+ (merge d42a2fb72f en/fast-import-looser-date later to maint).
+
+ * The command line completion script (in contrib/) tried to complete
+ "git stash -p" as if it were "git stash push -p", but it was too
+ aggressive and also affected "git stash show -p", which has been
+ corrected.
+ (merge fffd0cf520 vs/complete-stash-show-p-fix later to maint).
+
+ * On-the-wire protocol v2 easily falls into a deadlock between the
+ remote-curl helper and the fetch-pack process when the server side
+ prematurely throws an error and disconnects. The communication has
+ been updated to make it more robust.
+
+ * "git checkout -p" did not handle a newly added path at all.
+ (merge 2c8bd8471a js/checkout-p-new-file later to maint).
+
+ * The code to parse "git bisect start" command line was lax in
+ validating the arguments.
+ (merge 4d9005ff5d cb/bisect-helper-parser-fix later to maint).
+
+ * Reduce memory usage during "diff --quiet" in a worktree with too
+ many stat-unmatched paths.
+ (merge d2d7fbe129 jk/diff-memuse-optim-with-stat-unmatch later to maint).
+
+ * The reflog entries for "git clone" and "git fetch" did not
+ anonymize the URL they operated on.
+ (merge 46da295a77 js/reflog-anonymize-for-clone-and-fetch later to maint).
+
+ * The behaviour of "sparse-checkout" in the state "git clone
+ --no-checkout" left was changed accidentally in 2.27, which has
+ been corrected.
+
+ * Use of negative pathspec, while collecting paths including
+ untracked ones in the working tree, was broken.
+
+ * The same worktree directory must be registered only once, but
+ "git worktree move" allowed this invariant to be violated, which
+ has been corrected.
+ (merge 810382ed37 es/worktree-duplicate-paths later to maint).
+
+ * The effect of sparse checkout settings on submodules is documented.
+ (merge e7d7c73249 en/sparse-with-submodule-doc later to maint).
+
+ * Code clean-up around "git branch" with a minor bugfix.
+ (merge dc44639904 dl/branch-cleanup later to maint).
+
+ * A branch name used in a test has been clarified to match what is
+ going on.
+ (merge 08dc26061f pb/t4014-unslave later to maint).
+
+ * An in-code comment in "git diff" has been updated.
+ (merge c592fd4c83 dl/diff-usage-comment-update later to maint).
+
+ * The documentation and some tests have been adjusted for the recent
+ renaming of "pu" branch to "seen".
+ (merge 6dca5dbf93 js/pu-to-seen later to maint).
+
+ * The code to push changes over "dumb" HTTP had a bad interaction
+ with the commit reachability code due to incorrect allocation of
+ object flag bits, which has been corrected.
+ (merge 64472d15e9 bc/http-push-flagsfix later to maint).
+
+ * "git send-email --in-reply-to=<msg>" did not use the In-Reply-To:
+ header with the value given from the command line, and let it be
+ overridden by the value on In-Reply-To: header in the messages
+ being sent out (if exists).
+ (merge f9f60d7066 ra/send-email-in-reply-to-from-command-line-wins later to maint).
+
+ * "git log -Lx,y:path --before=date" lost track of where the range
+ should be because it didn't take the changes made by the youngest
+ commits that are omitted from the output into account.
+
+ * When "fetch.writeCommitGraph" configuration is set in a shallow
+ repository and a fetch moves the shallow boundary, we wrote out
+ broken commit-graph files that do not match the reality, which has
+ been corrected.
+
+ * "git checkout" failed to catch an error from fstat() after updating
+ a path in the working tree.
+ (merge 35e6e212fd mt/entry-fstat-fallback-fix later to maint).
+
+ * When an aliased command, whose output is piped to a pager by git,
+ gets killed by a signal, the pager got into a funny state, which
+ has been corrected (again).
+ (merge c0d73a59c9 ta/wait-on-aliased-commands-upon-signal later to maint).
+
+ * The code to produce progress output from "git commit-graph --write"
+ had a few breakages, which have been fixed.
+
+ * Other code cleanup, docfix, build fix, etc.
+ (merge 2c31a7aa44 jx/pkt-line-doc-count-fix later to maint).
+ (merge d63ae31962 cb/t5608-cleanup later to maint).
+ (merge 788db145c7 dl/t-readme-spell-git-correctly later to maint).
+ (merge 45a87a83bb dl/python-2.7-is-the-floor-version later to maint).
+ (merge b75a219904 es/advertise-contribution-doc later to maint).
+ (merge 0c9a4f638a rs/pull-leakfix later to maint).
+ (merge d546fe2874 rs/commit-reach-leakfix later to maint).
+ (merge 087bf5409c mk/pb-pretty-email-without-domain-part-fix later to maint).
+ (merge 5f4ee57ad9 es/worktree-code-cleanup later to maint).
+ (merge 0172f7834a cc/cat-file-usage-update later to maint).
+ (merge 81de0c01cf ma/rebase-doc-typofix later to maint).
diff --git a/Documentation/RelNotes/2.29.0.txt b/Documentation/RelNotes/2.29.0.txt
new file mode 100644
index 0000000..41a2348
--- /dev/null
+++ b/Documentation/RelNotes/2.29.0.txt
@@ -0,0 +1,91 @@
+Git 2.29 Release Notes
+======================
+
+Updates since v2.28
+-------------------
+
+UI, Workflows & Features
+
+ * "git help log" has been enhanced by sharing more material from the
+ documentation for the underlying "git rev-list" command.
+
+ * "git for-each-ref --format=<>" learned %(contents:size).
+
+ * "git merge" learned to selectively omit " into <branch>" at the end
+ of the title of default merge message with merge.suppressDest
+ configuration.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * The changed-path Bloom filter is improved using ideas from an
+ independent implementation.
+
+ * Updates to the changed-paths bloom filter.
+
+ * The test framework has been updated so that most tests will run
+ with predictable (artificial) timestamps.
+
+ * Preliminary clean-up of the refs API in preparation for adding a
+ new refs backend "reftable".
+
+ * Dev support to limit the use of test_must_fail to only git commands.
+
+ * While packing many objects in a repository with a promissor remote,
+ lazily fetching missing objects from the promissor remote one by
+ one may be inefficient---the code now attempts to fetch all the
+ missing objects in batch (obviously this won't work for a lazy
+ clone that lazily fetches tree objects as you cannot even enumerate
+ what blobs are missing until you learn which trees are missing).
+
+ * The pretend-object mechanism checks if the given object already
+ exists in the object store before deciding to keep the data
+ in-core, but the check would have triggered lazy fetching of such
+ an object from a promissor remote.
+
+
+Fixes since v2.28
+-----------------
+
+ * "git clone --separate-git-dir=$elsewhere" used to stomp on the
+ contents of the existing directory $elsewhere, which has been
+ taught to fail when $elsewhere is not an empty directory.
+ (merge dfaa209a79 bw/fail-cloning-into-non-empty later to maint).
+
+ * With the base fix to 2.27 regresion, any new extensions in a v0
+ repository would still be silently honored, which is not quite
+ right. Instead, complain and die loudly.
+ (merge ec91ffca04 jk/reject-newer-extensions-in-v0 later to maint).
+
+ * Fetching from a lazily cloned repository resulted at the server
+ side in attempts to lazy fetch objects that the client side has,
+ many of which will not be available from the third-party anyway.
+ (merge 77aa0941ce jt/avoid-lazy-fetching-upon-have-check later to maint).
+
+ * Fix to an ancient bug caused by an over-eager attempt for
+ optimization.
+ (merge a98f7fb366 rs/add-index-entry-optim-fix later to maint).
+
+ * Pushing a ref whose name contains non-ASCII character with the
+ "--force-with-lease" option did not work over smart HTTP protocol,
+ which has been corrected.
+ (merge cd85b447bf bc/push-cas-cquoted-refname later to maint).
+
+ * "git mv src dst", when src is an unmerged path, errored out
+ correctly but with an incorrect error message to claim that src is
+ not tracked, which has been clarified.
+ (merge 9b906af657 ct/mv-unmerged-path-error later to maint).
+
+ * Fix to a regression introduced during 2.27 cycle.
+ (merge cada7308ad en/fill-directory-exponential later to maint).
+
+ * Command line completion (in contrib/) update.
+ (merge 688b87c81b mp/complete-show-color-moved later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+ (merge 84544f2ea3 sk/typofixes later to maint).
+ (merge b17f411ab5 ar/help-guides-doc later to maint).
+ (merge 98c6871fad rs/grep-simpler-parse-object-or-die-call later to maint).
+ (merge 861c4ce141 en/typofixes later to maint).
+ (merge 60e47f6773 sg/ci-git-path-fix-with-pyenv later to maint).
+ (merge e2bfa50ac3 jb/doc-packfile-name later to maint).
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index 4515cab..291b61e 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -3,8 +3,9 @@ Submitting Patches
== Guidelines
-Here are some guidelines for people who want to contribute their code
-to this software.
+Here are some guidelines for people who want to contribute their code to this
+software. There is also a link:MyFirstContribution.html[step-by-step tutorial]
+available which covers many of these same guidelines.
[[base-branch]]
=== Decide what to base your work on.
@@ -18,7 +19,7 @@ change is relevant to.
base your work on the tip of the topic.
* A new feature should be based on `master` in general. If the new
- feature depends on a topic that is in `pu`, but not in `master`,
+ feature depends on a topic that is in `seen`, but not in `master`,
base your work on the tip of that topic.
* Corrections and enhancements to a topic not yet in `master` should
@@ -27,7 +28,7 @@ change is relevant to.
into the series.
* In the exceptional case that a new feature depends on several topics
- not in `master`, start working on `next` or `pu` privately and send
+ not in `master`, start working on `next` or `seen` privately and send
out patches for discussion. Before the final merge, you may have to
wait until some of the dependent topics graduate to `master`, and
rebase your work.
@@ -37,7 +38,7 @@ change is relevant to.
these parts should be based on their trees.
To find the tip of a topic branch, run `git log --first-parent
-master..pu` and look for the merge commit. The second parent of this
+master..seen` and look for the merge commit. The second parent of this
commit is the tip of the topic branch.
[[separate-commits]]
@@ -423,7 +424,7 @@ help you find out who they are.
and cooked further and eventually graduates to `master`.
In any time between the (2)-(3) cycle, the maintainer may pick it up
-from the list and queue it to `pu`, in order to make it easier for
+from the list and queue it to `seen`, in order to make it easier for
people play with it without having to pick up and apply the patch to
their trees themselves.
@@ -434,7 +435,7 @@ their trees themselves.
master. `git pull --rebase` will automatically skip already-applied
patches, and will let you know. This works only if you rebase on top
of the branch in which your patch has been merged (i.e. it will not
- tell you if your patch is merged in pu if you rebase on top of
+ tell you if your patch is merged in `seen` if you rebase on top of
master).
* Read the Git mailing list, the maintainer regularly posts messages
diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
index ff09f1c..c3ae136 100644
--- a/Documentation/config/diff.txt
+++ b/Documentation/config/diff.txt
@@ -105,6 +105,10 @@ diff.mnemonicPrefix::
diff.noprefix::
If set, 'git diff' does not show any source or destination prefix.
+diff.relative::
+ If set to 'true', 'git diff' does not show changes outside of the directory
+ and show pathnames relative to the current directory.
+
diff.orderFile::
File indicating how to order files within a diff.
See the '-O' option to linkgit:git-diff[1] for details.
diff --git a/Documentation/config/feature.txt b/Documentation/config/feature.txt
index 4e3a5c0..c0cbf2b 100644
--- a/Documentation/config/feature.txt
+++ b/Documentation/config/feature.txt
@@ -15,13 +15,9 @@ feature.experimental::
* `fetch.negotiationAlgorithm=skipping` may improve fetch negotiation times by
skipping more commits at a time, reducing the number of round trips.
+
-* `fetch.writeCommitGraph=true` writes a commit-graph after every `git fetch`
-command that downloads a pack-file from a remote. Using the `--split` option,
-most executions will create a very small commit-graph file on top of the
-existing commit-graph file(s). Occasionally, these files will merge and the
-write may take longer. Having an updated commit-graph file helps performance
-of many Git commands, including `git merge-base`, `git push -f`, and
-`git log --graph`.
+* `protocol.version=2` speeds up fetches from repositories with many refs by
+allowing the client to specify which refs to list before the server lists
+them.
feature.manyFiles::
Enable config options that optimize for repos with many files in the
diff --git a/Documentation/config/fetch.txt b/Documentation/config/fetch.txt
index b1a9b14..b203940 100644
--- a/Documentation/config/fetch.txt
+++ b/Documentation/config/fetch.txt
@@ -90,5 +90,4 @@ fetch.writeCommitGraph::
the existing commit-graph file(s). Occasionally, these files will
merge and the write may take longer. Having an updated commit-graph
file helps performance of many Git commands, including `git merge-base`,
- `git push -f`, and `git log --graph`. Defaults to false, unless
- `feature.experimental` is true.
+ `git push -f`, and `git log --graph`. Defaults to false.
diff --git a/Documentation/config/fmt-merge-msg.txt b/Documentation/config/fmt-merge-msg.txt
index c73cfa9..a8e8f74 100644
--- a/Documentation/config/fmt-merge-msg.txt
+++ b/Documentation/config/fmt-merge-msg.txt
@@ -8,3 +8,15 @@ merge.log::
most the specified number of one-line descriptions from the
actual commits that are being merged. Defaults to false, and
true is a synonym for 20.
+
+merge.suppressDest::
+ By adding a glob that matches the names of integration
+ branches to this multi-valued configuration variable, the
+ default merge message computed for merges into these
+ integration branches will omit " into <branch name>" from
+ its title.
++
+An element with an empty value can be used to clear the list
+of globs accumulated from previous configuration entries.
+When there is no `merge.suppressDest` variable defined, the
+default value of `master` is used for backward compatibility.
diff --git a/Documentation/config/init.txt b/Documentation/config/init.txt
index 46fa8c6..dc77f8c 100644
--- a/Documentation/config/init.txt
+++ b/Documentation/config/init.txt
@@ -1,3 +1,7 @@
init.templateDir::
Specify the directory from which templates will be copied.
(See the "TEMPLATE DIRECTORY" section of linkgit:git-init[1].)
+
+init.defaultBranch::
+ Allows overriding the default branch name e.g. when initializing
+ a new repository or when cloning an empty repository.
diff --git a/Documentation/config/protocol.txt b/Documentation/config/protocol.txt
index 0b40141..c46e9b3 100644
--- a/Documentation/config/protocol.txt
+++ b/Documentation/config/protocol.txt
@@ -48,7 +48,8 @@ protocol.version::
If set, clients will attempt to communicate with a server
using the specified protocol version. If the server does
not support it, communication falls back to version 0.
- If unset, the default is `0`.
+ If unset, the default is `0`, unless `feature.experimental`
+ is enabled, in which case the default is `2`.
Supported versions:
+
--
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index bb31f0c..7987d72 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -643,15 +643,18 @@ ifndef::git-format-patch[]
-R::
Swap two inputs; that is, show differences from index or
on-disk file to tree contents.
+endif::git-format-patch[]
--relative[=<path>]::
+--no-relative::
When run from a subdirectory of the project, it can be
told to exclude changes outside the directory and show
pathnames relative to it with this option. When you are
not in a subdirectory (e.g. in a bare repository), you
can name which subdirectory to make the output relative
to by giving a <path> as an argument.
-endif::git-format-patch[]
+ `--no-relative` can be used to countermand both `diff.relative` config
+ option and previous `--relative`.
-a::
--text::
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 135206f..03c0824 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -12,7 +12,7 @@ SYNOPSIS
[-v [--abbrev=<length> | --no-abbrev]]
[--column[=<options>] | --no-column] [--sort=<key>]
[(--merged | --no-merged) [<commit>]]
- [--contains [<commit]] [--no-contains [<commit>]]
+ [--contains [<commit>]] [--no-contains [<commit>]]
[--points-at <object>] [--format=<format>]
[(-r | --remotes) | (-a | --all)]
[--list] [<pattern>...]
diff --git a/Documentation/git-bugreport.txt b/Documentation/git-bugreport.txt
index 9edad66..66e88c2 100644
--- a/Documentation/git-bugreport.txt
+++ b/Documentation/git-bugreport.txt
@@ -29,6 +29,7 @@ The following information is captured automatically:
- uname sysname, release, version, and machine strings
- Compiler-specific info string
- A list of enabled hooks
+ - $SHELL
This tool is invoked via the typical Git setup process, which means that in some
cases, it might not be able to launch - for example, if a relevant config file
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index 8eca671..8e192d8 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
'git cat-file' (-t [--allow-unknown-type]| -s [--allow-unknown-type]| -e | -p | <type> | --textconv | --filters ) [--path=<path>] <object>
-'git cat-file' (--batch | --batch-check) [ --textconv | --filters ] [--follow-symlinks]
+'git cat-file' (--batch[=<format>] | --batch-check[=<format>]) [ --textconv | --filters ] [--follow-symlinks]
DESCRIPTION
-----------
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 08d6045..c898310 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -259,7 +259,7 @@ maintain a branch with no references other than a single cloned
branch. This is useful e.g. to maintain minimal clones of the default
branch of some repository for search indexing.
---recurse-submodules[=<pathspec]::
+--recurse-submodules[=<pathspec>]::
After the clone is created, initialize and clone submodules
within based on the provided pathspec. If no pathspec is
provided, all submodules are initialized and cloned.
diff --git a/Documentation/git-commit-graph.txt b/Documentation/git-commit-graph.txt
index a3d9967..17405c7 100644
--- a/Documentation/git-commit-graph.txt
+++ b/Documentation/git-commit-graph.txt
@@ -47,8 +47,10 @@ with `--stdin-commits` or `--reachable`.)
+
With the `--stdin-commits` option, generate the new commit graph by
walking commits starting at the commits specified in stdin as a list
-of OIDs in hex, one OID per line. (Cannot be combined with
-`--stdin-packs` or `--reachable`.)
+of OIDs in hex, one OID per line. OIDs that resolve to non-commits
+(either directly, or by peeling tags) are silently ignored. OIDs that
+are malformed, or do not exist generate an error. (Cannot be combined
+with `--stdin-packs` or `--reachable`.)
+
With the `--reachable` option, generate the new commit graph by walking
commits starting at all refs. (Cannot be combined with `--stdin-commits`
@@ -60,7 +62,10 @@ existing commit-graph file.
With the `--changed-paths` option, compute and write information about the
paths changed between a commit and its first parent. This operation can
take a while on large repositories. It provides significant performance gains
-for getting history of a directory or a file with `git log -- <path>`.
+for getting history of a directory or a file with `git log -- <path>`. If
+this option is given, future commit-graph writes will automatically assume
+that this option was intended. Use `--no-changed-paths` to stop storing this
+data.
+
With the `--split[=<strategy>]` option, write the commit-graph as a
chain of multiple commit-graph files stored in
diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index 37781cf..727f24d 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -11,15 +11,17 @@ SYNOPSIS
[verse]
'git diff' [<options>] [<commit>] [--] [<path>...]
'git diff' [<options>] --cached [<commit>] [--] [<path>...]
-'git diff' [<options>] <commit> <commit> [--] [<path>...]
+'git diff' [<options>] <commit> [<commit>...] <commit> [--] [<path>...]
+'git diff' [<options>] <commit>...<commit> [--] [<path>...]
'git diff' [<options>] <blob> <blob>
'git diff' [<options>] --no-index [--] <path> <path>
DESCRIPTION
-----------
Show changes between the working tree and the index or a tree, changes
-between the index and a tree, changes between two trees, changes between
-two blob objects, or changes between two files on disk.
+between the index and a tree, changes between two trees, changes resulting
+from a merge, changes between two blob objects, or changes between two
+files on disk.
'git diff' [<options>] [--] [<path>...]::
@@ -61,9 +63,19 @@ two blob objects, or changes between two files on disk.
This is to view the changes between two arbitrary
<commit>.
+'git diff' [<options>] <commit> <commit>... <commit> [--] [<path>...]::
+
+ This form is to view the results of a merge commit. The first
+ listed <commit> must be the merge itself; the remaining two or
+ more commits should be its parents. A convenient way to produce
+ the desired set of revisions is to use the {caret}@ suffix.
+ For instance, if `master` names a merge commit, `git diff master
+ master^@` gives the same combined diff as `git show master`.
+
'git diff' [<options>] <commit>..<commit> [--] [<path>...]::
- This is synonymous to the previous form. If <commit> on
+ This is synonymous to the earlier form (without the "..") for
+ viewing the changes between two arbitrary <commit>. If <commit> on
one side is omitted, it will have the same effect as
using HEAD instead.
@@ -196,7 +208,8 @@ linkgit:git-difftool[1],
linkgit:git-log[1],
linkgit:gitdiffcore[7],
linkgit:git-format-patch[1],
-linkgit:git-apply[1]
+linkgit:git-apply[1],
+linkgit:git-show[1]
GIT
---
diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt
index e8950de..1978dbd 100644
--- a/Documentation/git-fast-export.txt
+++ b/Documentation/git-fast-export.txt
@@ -119,6 +119,11 @@ by keeping the marks the same across runs.
the shape of the history and stored tree. See the section on
`ANONYMIZING` below.
+--anonymize-map=<from>[:<to>]::
+ Convert token `<from>` to `<to>` in the anonymized output. If
+ `<to>` is omitted, map `<from>` to itself (i.e., do not
+ anonymize it). See the section on `ANONYMIZING` below.
+
--reference-excluded-parents::
By default, running a command such as `git fast-export
master~5..master` will not include the commit master{tilde}5
@@ -238,6 +243,30 @@ collapse "User 0", "User 1", etc into "User X"). This produces a much
smaller output, and it is usually easy to quickly confirm that there is
no private data in the stream.
+Reproducing some bugs may require referencing particular commits or
+paths, which becomes challenging after refnames and paths have been
+anonymized. You can ask for a particular token to be left as-is or
+mapped to a new value. For example, if you have a bug which reproduces
+with `git rev-list sensitive -- secret.c`, you can run:
+
+---------------------------------------------------
+$ git fast-export --anonymize --all \
+ --anonymize-map=sensitive:foo \
+ --anonymize-map=secret.c:bar.c \
+ >stream
+---------------------------------------------------
+
+After importing the stream, you can then run `git rev-list foo -- bar.c`
+in the anonymized repository.
+
+Note that paths and refnames are split into tokens at slash boundaries.
+The command above would anonymize `subdir/secret.c` as something like
+`path123/bar.c`; you could then search for `bar.c` in the anonymized
+repository to determine the final pathname.
+
+To make referencing the final pathname simpler, you can map each path
+component; so if you also anonymize `subdir` to `publicdir`, then the
+final pathname would be `publicdir/bar.c`.
LIMITATIONS
-----------
diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt
index 77c6b3d..7d9aad2 100644
--- a/Documentation/git-fast-import.txt
+++ b/Documentation/git-fast-import.txt
@@ -293,7 +293,14 @@ by users who are located in the same location and time zone. In this
case a reasonable offset from UTC could be assumed.
+
Unlike the `rfc2822` format, this format is very strict. Any
-variation in formatting will cause fast-import to reject the value.
+variation in formatting will cause fast-import to reject the value,
+and some sanity checks on the numeric values may also be performed.
+
+`raw-permissive`::
+ This is the same as `raw` except that no sanity checks on
+ the numeric epoch and local offset are performed. This can
+ be useful when trying to filter or import an existing history
+ with e.g. bogus timezone values.
`rfc2822`::
This is the standard email format as described by RFC 2822.
diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt
index 5b1909f..45b6d8e 100644
--- a/Documentation/git-fetch.txt
+++ b/Documentation/git-fetch.txt
@@ -255,14 +255,14 @@ refspec.
* Using refspecs explicitly:
+
------------------------------------------------
-$ git fetch origin +pu:pu maint:tmp
+$ git fetch origin +seen:seen maint:tmp
------------------------------------------------
+
-This updates (or creates, as necessary) branches `pu` and `tmp` in
+This updates (or creates, as necessary) branches `seen` and `tmp` in
the local repository by fetching from the branches (respectively)
-`pu` and `maint` from the remote repository.
+`seen` and `maint` from the remote repository.
+
-The `pu` branch will be updated even if it does not fast-forward,
+The `seen` branch will be updated even if it does not fast-forward,
because it is prefixed with a plus sign; `tmp` will not be.
* Peek at a remote's branch, without configuring the remote in your local
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 6dcd39f..2ea71c5 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -232,12 +232,27 @@ Fields that have name-email-date tuple as its value (`author`,
`committer`, and `tagger`) can be suffixed with `name`, `email`,
and `date` to extract the named component.
-The complete message in a commit and tag object is `contents`.
-Its first line is `contents:subject`, where subject is the concatenation
-of all lines of the commit message up to the first blank line. The next
-line is `contents:body`, where body is all of the lines after the first
-blank line. The optional GPG signature is `contents:signature`. The
-first `N` lines of the message is obtained using `contents:lines=N`.
+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::
+ The size in bytes of the commit or tag message.
+
+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.
+
+contents:body::
+ The remainder of the commit or the tag message that follows
+ the "subject".
+
+contents:signature::
+ The optional GPG signature of the tag.
+
+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` (or by using the historical alias
`contents:trailers`). Non-trailer lines from the trailer block can be omitted
diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt
index f71db0d..69c0c5c 100644
--- a/Documentation/git-help.txt
+++ b/Documentation/git-help.txt
@@ -8,7 +8,7 @@ git-help - Display help information about Git
SYNOPSIS
--------
[verse]
-'git help' [-a|--all [--[no-]verbose]] [-g|--guide]
+'git help' [-a|--all [--[no-]verbose]] [-g|--guides]
[-i|--info|-m|--man|-w|--web] [COMMAND|GUIDE]
DESCRIPTION
@@ -21,7 +21,7 @@ on the standard output.
If the option `--all` or `-a` is given, all available commands are
printed on the standard output.
-If the option `--guide` or `-g` is given, a list of the useful
+If the option `--guides` or `-g` is given, a list of the useful
Git guides is also printed on the standard output.
If a command, or a guide, is given, a manual page for that command or
diff --git a/Documentation/git-http-fetch.txt b/Documentation/git-http-fetch.txt
index 666b042..4deb489 100644
--- a/Documentation/git-http-fetch.txt
+++ b/Documentation/git-http-fetch.txt
@@ -9,7 +9,7 @@ git-http-fetch - Download from a remote Git repository via HTTP
SYNOPSIS
--------
[verse]
-'git http-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] [--stdin] <commit> <url>
+'git http-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] [--stdin | --packfile=<hash> | <commit>] <url>
DESCRIPTION
-----------
@@ -40,6 +40,13 @@ commit-id::
<commit-id>['\t'<filename-as-in--w>]
+--packfile=<hash>::
+ Instead of a commit id on the command line (which is not expected in
+ this case), 'git http-fetch' fetches the packfile directly at the given
+ URL and uses index-pack to generate corresponding .idx and .keep files.
+ The hash is used to determine the name of the temporary file and is
+ arbitrary. The output of index-pack is printed to stdout.
+
--recover::
Verify that everything reachable from target is fetched. Used after
an earlier fetch is interrupted.
diff --git a/Documentation/git-index-pack.txt b/Documentation/git-index-pack.txt
index d5b7560..ac74d05 100644
--- a/Documentation/git-index-pack.txt
+++ b/Documentation/git-index-pack.txt
@@ -93,11 +93,19 @@ OPTIONS
--max-input-size=<size>::
Die, if the pack is larger than <size>.
+--object-format=<hash-algorithm>::
+ Specify the given object format (hash algorithm) for the pack. The valid
+ values are 'sha1' and (if enabled) 'sha256'. The default is the algorithm for
+ the current repository (set by `extensions.objectFormat`), or 'sha1' if no
+ value is set or outside a repository.
++
+This option cannot be used with --stdin.
+
NOTES
-----
-Once the index has been created, the list of object names is sorted
-and the SHA-1 hash of that list is printed to stdout. If --stdin was
+Once the index has been created, the hash that goes into the name of
+the pack/idx file is printed to stdout. If --stdin was
also used then this is prefixed by either "pack\t", or "keep\t" if a
new .keep file was successfully created. This is useful to remove a
.keep file used as a lock to prevent the race with 'git repack'
diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt
index adc6adf..ddfe265 100644
--- a/Documentation/git-init.txt
+++ b/Documentation/git-init.txt
@@ -10,7 +10,8 @@ SYNOPSIS
--------
[verse]
'git init' [-q | --quiet] [--bare] [--template=<template_directory>]
- [--separate-git-dir <git dir>] [--object-format=<format]
+ [--separate-git-dir <git dir>] [--object-format=<format>]
+ [-b <branch-name> | --initial-branch=<branch-name>]
[--shared[=<permissions>]] [directory]
@@ -67,6 +68,12 @@ repository.
+
If this is reinitialization, the repository will be moved to the specified path.
+-b <branch-name::
+--initial-branch=<branch-name>::
+
+Use the specified name for the initial branch in the newly created repository.
+If not specified, fall back to the default name: `master`.
+
--shared[=(false|true|umask|group|all|world|everybody|0xxx)]::
Specify that the Git repository is to be shared amongst several users. This
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 20e6d21..3fd26d5 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -15,9 +15,12 @@ DESCRIPTION
-----------
Shows the commit logs.
-The command takes options applicable to the `git rev-list`
+:git-log: 1
+include::rev-list-description.txt[]
+
+The command takes options applicable to the linkgit:git-rev-list[1]
command to control what is shown and how, and options applicable to
-the `git diff-*` commands to control how the changes
+the linkgit:git-diff[1] command to control how the changes
each commit introduces are shown.
diff --git a/Documentation/git-ls-remote.txt b/Documentation/git-ls-remote.txt
index 0a5c8b7..492e573 100644
--- a/Documentation/git-ls-remote.txt
+++ b/Documentation/git-ls-remote.txt
@@ -101,9 +101,9 @@ f25a265a342aed6041ab0cc484224d9ca54b6f41 refs/tags/v0.99.1
7ceca275d047c90c0c7d5afb13ab97efdf51bd6e refs/tags/v0.99.3
c5db5456ae3b0873fc659c19fafdde22313cc441 refs/tags/v0.99.2
0918385dbd9656cab0d1d81ba7453d49bbc16250 refs/tags/junio-gpg-pub
-$ git ls-remote http://www.kernel.org/pub/scm/git/git.git master pu rc
+$ git ls-remote http://www.kernel.org/pub/scm/git/git.git master seen rc
5fe978a5381f1fbad26a80e682ddd2a401966740 refs/heads/master
-c781a84b5204fb294c9ccc79f8b3baceeb32c061 refs/heads/pu
+c781a84b5204fb294c9ccc79f8b3baceeb32c061 refs/heads/seen
$ git remote add korg http://www.kernel.org/pub/scm/git/git.git
$ git ls-remote --tags korg v\*
d6602ec5194c87b0fc87103ca4d67251c76f233a refs/tags/v0.99
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 4624cfd..374d248 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -259,7 +259,7 @@ See also INCOMPATIBLE OPTIONS below.
unchanged as a result. If a temporary stash entry was created
using --autostash, it will be saved to the stash list.
---apply:
+--apply::
Use applying strategies to rebase (calling `git-am`
internally). This option may become a no-op in the future
once the merge backend handles everything the apply one does.
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index 025c911..5da6623 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -14,44 +14,8 @@ SYNOPSIS
DESCRIPTION
-----------
-List commits that are reachable by following the `parent` links from the
-given commit(s), but exclude commits that are reachable from the one(s)
-given with a '{caret}' in front of them. The output is given in reverse
-chronological order by default.
-
-You can think of this as a set operation. Commits given on the command
-line form a set of commits that are reachable from any of them, and then
-commits reachable from any of the ones given with '{caret}' in front are
-subtracted from that set. The remaining commits are what comes out in the
-command's output. Various other options and paths parameters can be used
-to further limit the result.
-
-Thus, the following command:
-
------------------------------------------------------------------------
- $ git rev-list foo bar ^baz
------------------------------------------------------------------------
-
-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 "{caret}'<commit1>' '<commit2>'". For example, either of
-the following may be used interchangeably:
-
------------------------------------------------------------------------
- $ git rev-list origin..HEAD
- $ git rev-list HEAD ^origin
------------------------------------------------------------------------
-
-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:
-
------------------------------------------------------------------------
- $ git rev-list A B --not $(git merge-base --all A B)
- $ git rev-list A...B
------------------------------------------------------------------------
+:git-rev-list: 1
+include::rev-list-description.txt[]
'rev-list' is a very essential Git command, since it
provides the ability to build and traverse commit ancestry graphs. For
diff --git a/Documentation/git-show-index.txt b/Documentation/git-show-index.txt
index 424e4ba..39b1d8e 100644
--- a/Documentation/git-show-index.txt
+++ b/Documentation/git-show-index.txt
@@ -9,7 +9,7 @@ git-show-index - Show packed archive index
SYNOPSIS
--------
[verse]
-'git show-index'
+'git show-index' [--object-format=<hash-algorithm>]
DESCRIPTION
@@ -36,6 +36,15 @@ Note that you can get more information on a packfile by calling
linkgit:git-verify-pack[1]. However, as this command considers only the
index file itself, it's both faster and more flexible.
+OPTIONS
+-------
+
+--object-format=<hash-algorithm>::
+ Specify the given object format (hash algorithm) for the index file. The
+ valid values are 'sha1' and (if enabled) 'sha256'. The default is the
+ algorithm for the current repository (set by `extensions.objectFormat`), or
+ 'sha1' if no value is set or outside a repository..
+
GIT
---
Part of the linkgit:git[1] suite
diff --git a/Documentation/git-sparse-checkout.txt b/Documentation/git-sparse-checkout.txt
index 7c8943a..a0eeaeb 100644
--- a/Documentation/git-sparse-checkout.txt
+++ b/Documentation/git-sparse-checkout.txt
@@ -200,10 +200,32 @@ directory.
SUBMODULES
----------
-If your repository contains one or more submodules, then those submodules will
-appear based on which you initialized with the `git submodule` command. If
-your sparse-checkout patterns exclude an initialized submodule, then that
-submodule will still appear in your working directory.
+If your repository contains one or more submodules, then submodules
+are populated based on interactions with the `git submodule` command.
+Specifically, `git submodule init -- <path>` will ensure the submodule
+at `<path>` is present, while `git submodule deinit [-f] -- <path>`
+will remove the files for the submodule at `<path>` (including any
+untracked files, uncommitted changes, and unpushed history). Similar
+to how sparse-checkout removes files from the working tree but still
+leaves entries in the index, deinitialized submodules are removed from
+the working directory but still have an entry in the index.
+
+Since submodules may have unpushed changes or untracked files,
+removing them could result in data loss. Thus, changing sparse
+inclusion/exclusion rules will not cause an already checked out
+submodule to be removed from the working copy. Said another way, just
+as `checkout` will not cause submodules to be automatically removed or
+initialized even when switching between branches that remove or add
+submodules, using `sparse-checkout` to reduce or expand the scope of
+"interesting" files will not cause submodules to be automatically
+deinitialized or initialized either.
+
+Further, the above facts mean that there are multiple reasons that
+"tracked" files might not be present in the working copy: sparsity
+pattern application from sparse-checkout, and submodule initialization
+state. Thus, commands like `git grep` that work on tracked files in
+the working copy may return results that are limited by either or both
+of these restrictions.
SEE ALSO
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index c9ed2bf..7e5f995 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -183,7 +183,7 @@ set-branch (-d|--default) [--] <path>::
Sets the default remote tracking branch for the submodule. The
`--branch` option allows the remote branch to be specified. The
`--default` option removes the submodule.<name>.branch configuration
- key, which causes the tracking branch to default to 'master'.
+ key, which causes the tracking branch to default to the remote 'HEAD'.
set-url [--] <path> <newurl>::
Sets the URL of the specified submodule to <newurl>. Then, it will
@@ -284,7 +284,7 @@ OPTIONS
`.gitmodules` for `update --remote`. A special value of `.` is used to
indicate that the name of the branch in the submodule should be the
same name as the current branch in the current repository. If the
- option is not specified, it defaults to 'master'.
+ option is not specified, it defaults to the remote 'HEAD'.
-f::
--force::
@@ -322,10 +322,10 @@ OPTIONS
the superproject's recorded SHA-1 to update the submodule, use the
status of the submodule's remote-tracking branch. The remote used
is branch's remote (`branch.<name>.remote`), defaulting to `origin`.
- The remote branch used defaults to `master`, but the branch name may
- be overridden by setting the `submodule.<name>.branch` option in
- either `.gitmodules` or `.git/config` (with `.git/config` taking
- precedence).
+ The remote branch used defaults to the remote `HEAD`, but the branch
+ name may be overridden by setting the `submodule.<name>.branch`
+ option in either `.gitmodules` or `.git/config` (with `.git/config`
+ taking precedence).
+
This works for any of the supported update procedures (`--checkout`,
`--rebase`, etc.). The only change is the source of the target SHA-1.
diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt
index 85d92c9..4796c3c 100644
--- a/Documentation/git-worktree.txt
+++ b/Documentation/git-worktree.txt
@@ -126,7 +126,9 @@ OPTIONS
locked working tree path, specify `--force` twice.
+
`move` refuses to move a locked working tree unless `--force` is specified
-twice.
+twice. If the destination is already assigned to some other working tree but is
+missing (for instance, if `<new-path>` was deleted manually), then `--force`
+allows the move to proceed; use --force twice if the destination is locked.
+
`remove` refuses to remove an unclean working tree unless `--force` is used.
To remove a locked working tree, specify `--force` twice.
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 9d6769e..3e50065 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -493,7 +493,7 @@ double-quotes and respecting backslash escapes. E.g., the value
details. This variable has lower precedence than other path
variables such as GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY...
-`GIT_DEFAULT_HASH_ALGORITHM`::
+`GIT_DEFAULT_HASH`::
If this variable is set, the default hash algorithm for new
repositories will be set to this value. This value is currently
ignored when cloning; the setting of the remote repository
@@ -721,8 +721,6 @@ of clones and fetches.
Enables a curl full trace dump of all incoming and outgoing data,
including descriptive information, of the git transport protocol.
This is similar to doing curl `--trace-ascii` on the command line.
- This option overrides setting the `GIT_CURL_VERBOSE` environment
- variable.
See `GIT_TRACE` for available trace output options.
`GIT_TRACE_CURL_NO_DATA`::
@@ -777,11 +775,10 @@ for full details.
See `GIT_TRACE2` for available trace output options and
link:technical/api-trace2.html[Trace2 documentation] for full details.
-`GIT_REDACT_COOKIES`::
- This can be set to a comma-separated list of strings. When a curl trace
- is enabled (see `GIT_TRACE_CURL` above), whenever a "Cookies:" header
- sent by the client is dumped, values of cookies whose key is in that
- list (case-sensitive) are redacted.
+`GIT_TRACE_REDACT`::
+ By default, when tracing is activated, Git redacts the values of
+ cookies, the "Authorization:" header, and the "Proxy-Authorization:"
+ header. Set this variable to `0` to prevent this redaction.
`GIT_LITERAL_PATHSPECS`::
Setting this variable to `1` will cause Git to treat all
diff --git a/Documentation/giteveryday.txt b/Documentation/giteveryday.txt
index 1bd919f..faba2ef 100644
--- a/Documentation/giteveryday.txt
+++ b/Documentation/giteveryday.txt
@@ -278,13 +278,13 @@ $ git am -3 -i -s ./+to-apply <4>
$ compile/test
$ git switch -c hold/linus && git am -3 -i -s ./+hold-linus <5>
$ git switch topic/one && git rebase master <6>
-$ git switch -C pu next <7>
+$ git switch -C seen next <7>
$ git merge topic/one topic/two && git merge hold/linus <8>
$ git switch maint
$ git cherry-pick master~4 <9>
$ compile/test
$ git tag -s -m "GIT 0.99.9x" v0.99.9x <10>
-$ git fetch ko && for branch in master maint next pu <11>
+$ git fetch ko && for branch in master maint next seen <11>
do
git show-branch ko/$branch $branch <12>
done
@@ -294,14 +294,14 @@ $ git push --follow-tags ko <13>
<1> see what you were in the middle of doing, if anything.
<2> see which branches haven't been merged into `master` yet.
Likewise for any other integration branches e.g. `maint`, `next`
-and `pu` (potential updates).
+and `seen`.
<3> read mails, save ones that are applicable, and save others
that are not quite ready (other mail readers are available).
<4> apply them, interactively, with your sign-offs.
<5> create topic branch as needed and apply, again with sign-offs.
<6> rebase internal topic branch that has not been merged to the
master or exposed as a part of a stable branch.
-<7> restart `pu` every time from the next.
+<7> restart `seen` every time from the next.
<8> and bundle topic branches still cooking.
<9> backport a critical fix.
<10> create a signed tag.
@@ -323,7 +323,7 @@ repository at kernel.org, and looks like this:
fetch = refs/heads/*:refs/remotes/ko/*
push = refs/heads/master
push = refs/heads/next
- push = +refs/heads/pu
+ push = +refs/heads/seen
push = refs/heads/maint
------------
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 81f2a87..31b601e 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -404,6 +404,35 @@ Both standard output and standard error output are forwarded to
`git send-pack` on the other end, so you can simply `echo` messages
for the user.
+reference-transaction
+~~~~~~~~~~~~~~~~~~~~~
+
+This hook is invoked by any Git command that performs reference
+updates. It executes whenever a reference transaction is prepared,
+committed or aborted and may thus get called multiple times.
+
+The hook takes exactly one argument, which is the current state the
+given reference transaction is in:
+
+ - "prepared": All reference updates have been queued to the
+ transaction and references were locked on disk.
+
+ - "committed": The reference transaction was committed and all
+ references now have their respective new value.
+
+ - "aborted": The reference transaction was aborted, no changes
+ were performed and the locks have been released.
+
+For each reference update that was added to the transaction, the hook
+receives on standard input a line of the format:
+
+ <old-value> SP <new-value> SP <ref-name> LF
+
+The exit status of the hook is ignored for any state except for the
+"prepared" state. In the "prepared" state, a non-zero exit status will
+cause the transaction to be aborted. The hook will not be called with
+"aborted" state in that case.
+
push-to-checkout
~~~~~~~~~~~~~~~~
diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt
index 67275fd..539b4e1 100644
--- a/Documentation/gitmodules.txt
+++ b/Documentation/gitmodules.txt
@@ -49,9 +49,9 @@ submodule.<name>.update::
submodule.<name>.branch::
A remote branch name for tracking updates in the upstream submodule.
- If the option is not specified, it defaults to 'master'. A special
- value of `.` is used to indicate that the name of the branch in the
- submodule should be the same name as the current branch in the
+ If the option is not specified, it defaults to the remote 'HEAD'.
+ A special value of `.` is used to indicate that the name of the branch
+ in the submodule should be the same name as the current branch in the
current repository. See the `--remote` documentation in
linkgit:git-submodule[1] for details.
diff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt
index f48a031..6f1e269 100644
--- a/Documentation/gitremote-helpers.txt
+++ b/Documentation/gitremote-helpers.txt
@@ -238,6 +238,9 @@ the remote repository.
`--signed-tags=verbatim` to linkgit:git-fast-export[1]. In the
absence of this capability, Git will use `--signed-tags=warn-strip`.
+'object-format'::
+ This indicates that the helper is able to interact with the remote
+ side using an explicit hash algorithm extension.
COMMANDS
@@ -257,12 +260,14 @@ Support for this command is mandatory.
'list'::
Lists the refs, one per line, in the format "<value> <name>
[<attr> ...]". The value may be a hex sha1 hash, "@<dest>" for
- a symref, or "?" to indicate that the helper could not get the
- value of the ref. A space-separated list of attributes follows
- the name; unrecognized attributes are ignored. The list ends
- with a blank line.
+ a symref, ":<keyword> <value>" for a key-value pair, or
+ "?" to indicate that the helper could not get the value of the
+ ref. A space-separated list of attributes follows the name;
+ unrecognized attributes are ignored. The list ends with a
+ blank line.
+
See REF LIST ATTRIBUTES for a list of currently defined attributes.
+See REF LIST KEYWORDS for a list of currently defined keywords.
+
Supported if the helper has the "fetch" or "import" capability.
@@ -405,7 +410,9 @@ Supported if the helper has the "connect" capability.
trying to fall back). After line feed terminating the positive
(empty) response, the output of the service starts. Messages
(both request and response) must consist of zero or more
- PKT-LINEs, terminating in a flush packet. The client must not
+ PKT-LINEs, terminating in a flush packet. Response messages will
+ then have a response end packet after the flush packet to
+ indicate the end of a response. The client must not
expect the server to store any state in between request-response
pairs. After the connection ends, the remote helper exits.
+
@@ -430,6 +437,18 @@ attributes are defined.
This ref is unchanged since the last import or fetch, although
the helper cannot necessarily determine what value that produced.
+REF LIST KEYWORDS
+-----------------
+
+The 'list' command may produce a list of key-value pairs.
+The following keys are defined.
+
+'object-format'::
+ The refs are using the given hash algorithm. This keyword is only
+ used if the server and client both support the object-format
+ extension.
+
+
OPTIONS
-------
@@ -514,6 +533,14 @@ set by Git if the remote helper has the 'option' capability.
transaction. If successful, all refs will be updated, or none will. If the
remote side does not support this capability, the push will fail.
+'option object-format' {'true'|algorithm}::
+ If 'true', indicate that the caller wants hash algorithm information
+ to be passed back from the remote. This mode is used when fetching
+ refs.
++
+If set to an algorithm, indicate that the caller wants to interact with
+the remote side using that algorithm.
+
SEE ALSO
--------
linkgit:git-remote[1]
diff --git a/Documentation/gitworkflows.txt b/Documentation/gitworkflows.txt
index abc0dc6..47cf97f 100644
--- a/Documentation/gitworkflows.txt
+++ b/Documentation/gitworkflows.txt
@@ -85,15 +85,15 @@ As a given feature goes from experimental to stable, it also
There is a fourth official branch that is used slightly differently:
-* 'pu' (proposed updates) is an integration branch for things that are
- not quite ready for inclusion yet (see "Integration Branches"
- below).
+* 'seen' (patches seen by the maintainer) is an integration branch for
+ things that are not quite ready for inclusion yet (see "Integration
+ Branches" below).
Each of the four branches is usually a direct descendant of the one
above it.
Conceptually, the feature enters at an unstable branch (usually 'next'
-or 'pu'), and "graduates" to 'master' for the next release once it is
+or 'seen'), and "graduates" to 'master' for the next release once it is
considered stable enough.
@@ -207,7 +207,7 @@ If you make it (very) clear that this branch is going to be deleted
right after the testing, you can even publish this branch, for example
to give the testers a chance to work with it, or other developers a
chance to see if their in-progress work will be compatible. `git.git`
-has such an official throw-away integration branch called 'pu'.
+has such an official throw-away integration branch called 'seen'.
Branch management for a release
@@ -291,8 +291,8 @@ This will not happen if the content of the branches was verified as
described in the previous section.
-Branch management for next and pu after a feature release
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Branch management for next and seen after a feature release
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
After a feature release, the integration branch 'next' may optionally be
rewound and rebuilt from the tip of 'master' using the surviving
@@ -319,8 +319,8 @@ so.
If you do this, then you should make a public announcement indicating
that 'next' was rewound and rebuilt.
-The same rewind and rebuild process may be followed for 'pu'. A public
-announcement is not necessary since 'pu' is a throw-away branch, as
+The same rewind and rebuild process may be followed for 'seen'. A public
+announcement is not necessary since 'seen' is a throw-away branch, as
described above.
diff --git a/Documentation/howto/maintain-git.txt b/Documentation/howto/maintain-git.txt
index 73be8b4..a67130d 100644
--- a/Documentation/howto/maintain-git.txt
+++ b/Documentation/howto/maintain-git.txt
@@ -66,7 +66,7 @@ this mailing list after each feature release is made.
demonstrated to be regression free. New changes are tested
in 'next' before merged to 'master'.
- - 'pu' branch is used to publish other proposed changes that do
+ - 'seen' branch is used to publish other proposed changes that do
not yet pass the criteria set for 'next'.
- The tips of 'master' and 'maint' branches will not be rewound to
@@ -76,7 +76,7 @@ this mailing list after each feature release is made.
of the cycle.
- Usually 'master' contains all of 'maint' and 'next' contains all
- of 'master'. 'pu' contains all the topics merged to 'next', but
+ of 'master'. 'seen' contains all the topics merged to 'next', but
is rebuilt directly on 'master'.
- The tip of 'master' is meant to be more stable than any
@@ -229,12 +229,12 @@ by doing the following:
series?)
- Prepare 'jch' branch, which is used to represent somewhere
- between 'master' and 'pu' and often is slightly ahead of 'next'.
+ between 'master' and 'seen' and often is slightly ahead of 'next'.
- $ Meta/Reintegrate master..pu >Meta/redo-jch.sh
+ $ Meta/Reintegrate master..seen >Meta/redo-jch.sh
The result is a script that lists topics to be merged in order to
- rebuild 'pu' as the input to Meta/Reintegrate script. Remove
+ rebuild 'seen' as the input to Meta/Reintegrate script. Remove
later topics that should not be in 'jch' yet. Add a line that
consists of '### match next' before the name of the first topic
in the output that should be in 'jch' but not in 'next' yet.
@@ -291,29 +291,29 @@ by doing the following:
merged to 'master'. This may lose '### match next' marker;
add it again to the appropriate place when it happens.
- - Rebuild 'pu'.
+ - Rebuild 'seen'.
- $ Meta/Reintegrate master..pu >Meta/redo-pu.sh
+ $ Meta/Reintegrate master..seen >Meta/redo-seen.sh
- Edit the result by adding new topics that are not still in 'pu'
+ Edit the result by adding new topics that are not still in 'seen'
in the script. Then
- $ git checkout -B pu jch
- $ sh Meta/redo-pu.sh
+ $ git checkout -B seen jch
+ $ sh Meta/redo-seen.sh
- When all is well, clean up the redo-pu.sh script with
+ When all is well, clean up the redo-seen.sh script with
- $ sh Meta/redo-pu.sh -u
+ $ sh Meta/redo-seen.sh -u
Double check by running
- $ git branch --no-merged pu
+ $ git branch --no-merged seen
to see there is no unexpected leftover topics.
At this point, build-test the result for semantic conflicts, and
if there are, prepare an appropriate merge-fix first (see
- appendix), and rebuild the 'pu' branch from scratch, starting at
+ appendix), and rebuild the 'seen' branch from scratch, starting at
the tip of 'jch'.
- Update "What's cooking" message to review the updates to
@@ -323,14 +323,14 @@ by doing the following:
$ Meta/cook
- This script inspects the history between master..pu, finds tips
+ This script inspects the history between master..seen, finds tips
of topic branches, compares what it found with the current
contents in Meta/whats-cooking.txt, and updates that file.
- Topics not listed in the file but are found in master..pu are
+ Topics not listed in the file but are found in master..seen are
added to the "New topics" section, topics listed in the file that
- are no longer found in master..pu are moved to the "Graduated to
+ are no longer found in master..seen are moved to the "Graduated to
master" section, and topics whose commits changed their states
- (e.g. used to be only in 'pu', now merged to 'next') are updated
+ (e.g. used to be only in 'seen', now merged to 'next') are updated
with change markers "<<" and ">>".
Look for lines enclosed in "<<" and ">>"; they hold contents from
@@ -360,7 +360,7 @@ Observations
Some observations to be made.
* Each topic is tested individually, and also together with other
- topics cooking first in 'pu', then in 'jch' and then in 'next'.
+ topics cooking first in 'seen', then in 'jch' and then in 'next'.
Until it matures, no part of it is merged to 'master'.
* A topic already in 'next' can get fixes while still in
@@ -411,7 +411,7 @@ new use of the variable under its old name. When these two topics
are merged together, the reference to the variable newly added by
the latter topic will still use the old name in the result.
-The Meta/Reintegrate script that is used by redo-jch and redo-pu
+The Meta/Reintegrate script that is used by redo-jch and redo-seen
scripts implements a crude but usable way to work this issue around.
When the script merges branch $X, it checks if "refs/merge-fix/$X"
exists, and if so, the effect of it is squashed into the result of
@@ -431,14 +431,14 @@ commit that can be squashed into a result of mechanical merge to
correct semantic conflicts.
After finding that the result of merging branch "ai/topic" to an
-integration branch had such a semantic conflict, say pu~4, check the
+integration branch had such a semantic conflict, say seen~4, check the
problematic merge out on a detached HEAD, edit the working tree to
fix the semantic conflict, and make a separate commit to record the
fix-up:
- $ git checkout pu~4
+ $ git checkout seen~4
$ git show -s --pretty=%s ;# double check
- Merge branch 'ai/topic' to pu
+ Merge branch 'ai/topic' to seen
$ edit
$ git commit -m 'merge-fix/ai/topic' -a
@@ -450,9 +450,9 @@ result:
Then double check the result by asking Meta/Reintegrate to redo the
merge:
- $ git checkout pu~5 ;# the parent of the problem merge
+ $ git checkout seen~5 ;# the parent of the problem merge
$ echo ai/topic | Meta/Reintegrate
- $ git diff pu~4
+ $ git diff seen~4
This time, because you prepared refs/merge-fix/ai/topic, the
resulting merge should have been tweaked to include the fix for the
@@ -464,7 +464,7 @@ branch needs this merge-fix is because another branch merged earlier
to the integration branch changed the underlying assumption ai/topic
branch made (e.g. ai/topic branch added a site to refer to a
variable, while the other branch renamed that variable and adjusted
-existing use sites), and if you changed redo-jch (or redo-pu) script
+existing use sites), and if you changed redo-jch (or redo-seen) script
to merge ai/topic branch before the other branch, then the above
merge-fix should not be applied while merging ai/topic, but should
instead be applied while merging the other branch. You would need
diff --git a/Documentation/howto/rebase-from-internal-branch.txt b/Documentation/howto/rebase-from-internal-branch.txt
index 02cb5f7..f2e10a7 100644
--- a/Documentation/howto/rebase-from-internal-branch.txt
+++ b/Documentation/howto/rebase-from-internal-branch.txt
@@ -4,7 +4,7 @@ Cc: Petr Baudis <pasky@suse.cz>, Linus Torvalds <torvalds@osdl.org>
Subject: Re: sending changesets from the middle of a git tree
Date: Sun, 14 Aug 2005 18:37:39 -0700
Abstract: In this article, JC talks about how he rebases the
- public "pu" branch using the core Git tools when he updates
+ public "seen" branch using the core Git tools when he updates
the "master" branch, and how "rebase" works. Also discussed
is how this applies to individual developers who sends patches
upstream.
@@ -20,8 +20,8 @@ Petr Baudis <pasky@suse.cz> writes:
> where Junio C Hamano <junkio@cox.net> told me that...
>> Linus Torvalds <torvalds@osdl.org> writes:
>>
->> > Junio, maybe you want to talk about how you move patches from your "pu"
->> > branch to the real branches.
+>> > Junio, maybe you want to talk about how you move patches from your
+>> > "seen" branch to the real branches.
>>
> Actually, wouldn't this be also precisely for what StGIT is intended to?
--------------------------------------
@@ -33,12 +33,12 @@ the kind of task StGIT is designed to do.
I just have done a simpler one, this time using only the core
Git tools.
-I had a handful of commits that were ahead of master in pu, and I
+I had a handful of commits that were ahead of master in 'seen', and I
wanted to add some documentation bypassing my usual habit of
-placing new things in pu first. At the beginning, the commit
+placing new things in 'seen' first. At the beginning, the commit
ancestry graph looked like this:
- *"pu" head
+ *"seen" head
master --> #1 --> #2 --> #3
So I started from master, made a bunch of edits, and committed:
@@ -50,7 +50,7 @@ So I started from master, made a bunch of edits, and committed:
After the commit, the ancestry graph would look like this:
- *"pu" head
+ *"seen" head
master^ --> #1 --> #2 --> #3
\
\---> master
@@ -58,31 +58,31 @@ After the commit, the ancestry graph would look like this:
The old master is now master^ (the first parent of the master).
The new master commit holds my documentation updates.
-Now I have to deal with "pu" branch.
+Now I have to deal with "seen" branch.
This is the kind of situation I used to have all the time when
Linus was the maintainer and I was a contributor, when you look
-at "master" branch being the "maintainer" branch, and "pu"
+at "master" branch being the "maintainer" branch, and "seen"
branch being the "contributor" branch. Your work started at the
tip of the "maintainer" branch some time ago, you made a lot of
progress in the meantime, and now the maintainer branch has some
other commits you do not have yet. And "git rebase" was written
with the explicit purpose of helping to maintain branches like
-"pu". You _could_ merge master to pu and keep going, but if you
+"seen". You _could_ merge master to 'seen' and keep going, but if you
eventually want to cherrypick and merge some but not necessarily
all changes back to the master branch, it often makes later
operations for _you_ easier if you rebase (i.e. carry forward
-your changes) "pu" rather than merge. So I ran "git rebase":
+your changes) "seen" rather than merge. So I ran "git rebase":
- $ git checkout pu
- $ git rebase master pu
+ $ git checkout seen
+ $ git rebase master seen
What this does is to pick all the commits since the current
-branch (note that I now am on "pu" branch) forked from the
+branch (note that I now am on "seen" branch) forked from the
master branch, and forward port these changes.
master^ --> #1 --> #2 --> #3
- \ *"pu" head
+ \ *"seen" head
\---> master --> #1' --> #2' --> #3'
The diff between master^ and #1 is applied to master and
@@ -92,7 +92,7 @@ commits are made similarly out of #2 and #3 commits.
Old #3 is not recorded in any of the .git/refs/heads/ file
anymore, so after doing this you will have dangling commit if
-you ran fsck-cache, which is normal. After testing "pu", you
+you ran fsck-cache, which is normal. After testing "seen", you
can run "git prune" to get rid of those original three commits.
While I am talking about "git rebase", I should talk about how
diff --git a/Documentation/howto/revert-branch-rebase.txt b/Documentation/howto/revert-branch-rebase.txt
index 149508e..a3e5595 100644
--- a/Documentation/howto/revert-branch-rebase.txt
+++ b/Documentation/howto/revert-branch-rebase.txt
@@ -15,7 +15,7 @@ One of the changes I pulled into the 'master' branch turns out to
break building Git with GCC 2.95. While they were well-intentioned
portability fixes, keeping things working with gcc-2.95 was also
important. Here is what I did to revert the change in the 'master'
-branch and to adjust the 'pu' branch, using core Git tools and
+branch and to adjust the 'seen' branch, using core Git tools and
barebone Porcelain.
First, prepare a throw-away branch in case I screw things up.
@@ -104,11 +104,11 @@ $ git diff master..revert-c99
says nothing.
-Then we rebase the 'pu' branch as usual.
+Then we rebase the 'seen' branch as usual.
------------------------------------------------
-$ git checkout pu
-$ git tag pu-anchor pu
+$ git checkout seen
+$ git tag seen-anchor seen
$ git rebase master
* Applying: Redo "revert" using three-way merge machinery.
First trying simple merge strategy to cherry-pick.
@@ -127,11 +127,11 @@ First trying simple merge strategy to cherry-pick.
First trying simple merge strategy to cherry-pick.
------------------------------------------------
-The temporary tag 'pu-anchor' is me just being careful, in case 'git
+The temporary tag 'seen-anchor' is me just being careful, in case 'git
rebase' screws up. After this, I can do these for sanity check:
------------------------------------------------
-$ git diff pu-anchor..pu ;# make sure we got the master fix.
+$ git diff seen-anchor..seen ;# make sure we got the master fix.
$ make CC=gcc-2.95 clean test ;# make sure it fixed the breakage.
$ make clean test ;# make sure it did not cause other breakage.
------------------------------------------------
@@ -140,7 +140,7 @@ Everything is in the good order. I do not need the temporary branch
or tag anymore, so remove them:
------------------------------------------------
-$ rm -f .git/refs/tags/pu-anchor
+$ rm -f .git/refs/tags/seen-anchor
$ git branch -d revert-c99
------------------------------------------------
@@ -168,18 +168,18 @@ Committed merge 7fb9b7262a1d1e0a47bbfdcbbcf50ce0635d3f8f
And the final repository status looks like this:
------------------------------------------------
-$ git show-branch --more=1 master pu rc
+$ git show-branch --more=1 master seen rc
! [master] Revert "Replace zero-length array decls with []."
- ! [pu] git-repack: Add option to repack all objects.
+ ! [seen] git-repack: Add option to repack all objects.
* [rc] Merge refs/heads/master from .
---
- + [pu] git-repack: Add option to repack all objects.
- + [pu~1] More documentation updates.
- + [pu~2] Show commits in topo order and name all commits.
- + [pu~3] mailinfo and applymbox updates
- + [pu~4] Document "git cherry-pick" and "git revert"
- + [pu~5] Remove git-apply-patch-script.
- + [pu~6] Redo "revert" using three-way merge machinery.
+ + [seen] git-repack: Add option to repack all objects.
+ + [seen~1] More documentation updates.
+ + [seen~2] Show commits in topo order and name all commits.
+ + [seen~3] mailinfo and applymbox updates
+ + [seen~4] Document "git cherry-pick" and "git revert"
+ + [seen~5] Remove git-apply-patch-script.
+ + [seen~6] Redo "revert" using three-way merge machinery.
- [rc] Merge refs/heads/master from .
++* [master] Revert "Replace zero-length array decls with []."
- [rc~1] Merge refs/heads/master from .
diff --git a/Documentation/howto/update-hook-example.txt b/Documentation/howto/update-hook-example.txt
index 89821ec..151ee84 100644
--- a/Documentation/howto/update-hook-example.txt
+++ b/Documentation/howto/update-hook-example.txt
@@ -179,7 +179,7 @@ allowed-groups, to describe which heads can be pushed into by
whom. The format of each file would look like this:
refs/heads/master junio
- +refs/heads/pu junio
+ +refs/heads/seen junio
refs/heads/cogito$ pasky
refs/heads/bw/.* linus
refs/heads/tmp/.* .*
@@ -187,6 +187,6 @@ whom. The format of each file would look like this:
With this, Linus can push or create "bw/penguin" or "bw/zebra"
or "bw/panda" branches, Pasky can do only "cogito", and JC can
-do master and pu branches and make versioned tags. And anybody
-can do tmp/blah branches. The '+' sign at the pu record means
+do master and "seen" branches and make versioned tags. And anybody
+can do tmp/blah branches. The '+' sign at the "seen" record means
that JC can make non-fast-forward pushes on it.
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index 547a552..84bbc74 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -196,8 +196,8 @@ The placeholders are:
'%ce':: committer email
'%cE':: committer email (respecting .mailmap, see
linkgit:git-shortlog[1] or linkgit:git-blame[1])
-'%cl':: author email local-part (the part before the '@' sign)
-'%cL':: author 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
diff --git a/Documentation/rev-list-description.txt b/Documentation/rev-list-description.txt
new file mode 100644
index 0000000..a9efa7f
--- /dev/null
+++ b/Documentation/rev-list-description.txt
@@ -0,0 +1,61 @@
+List commits that are reachable by following the `parent` links from the
+given commit(s), but exclude commits that are reachable from the one(s)
+given with a '{caret}' in front of them. The output is given in reverse
+chronological order by default.
+
+You can think of this as a set operation. Commits reachable from any of
+the commits given on the command line form a set, and then commits reachable
+from any of the ones given with '{caret}' in front are subtracted from that
+set. The remaining commits are what comes out in the command's output.
+Various other options and paths parameters can be used to further limit the
+result.
+
+Thus, the following command:
+
+ifdef::git-rev-list[]
+-----------------------------------------------------------------------
+$ git rev-list foo bar ^baz
+-----------------------------------------------------------------------
+endif::git-rev-list[]
+ifdef::git-log[]
+-----------------------------------------------------------------------
+$ git log foo bar ^baz
+-----------------------------------------------------------------------
+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
+the following may be used interchangeably:
+
+ifdef::git-rev-list[]
+-----------------------------------------------------------------------
+$ git rev-list origin..HEAD
+$ git rev-list HEAD ^origin
+-----------------------------------------------------------------------
+endif::git-rev-list[]
+ifdef::git-log[]
+-----------------------------------------------------------------------
+$ git log origin..HEAD
+$ git log HEAD ^origin
+-----------------------------------------------------------------------
+endif::git-log[]
+
+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:
+
+ifdef::git-rev-list[]
+-----------------------------------------------------------------------
+$ git rev-list A B --not $(git merge-base --all A B)
+$ git rev-list A...B
+-----------------------------------------------------------------------
+endif::git-rev-list[]
+ifdef::git-log[]
+-----------------------------------------------------------------------
+$ git log A B --not $(git merge-base --all A B)
+$ git log A...B
+-----------------------------------------------------------------------
+endif::git-log[]
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index 04ad7dd..b01b2b6 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -581,12 +581,12 @@ option does. Applied to the 'D..M' range, it results in:
Before discussing another option, `--show-pulls`, we need to
create a new example history.
-+
+
A common problem users face when looking at simplified history is that a
commit they know changed a file somehow does not appear in the file's
simplified history. Let's demonstrate a new example and show how options
such as `--full-history` and `--simplify-merges` works in that case:
-+
+
-----------------------------------------------------------------------
.-A---M-----C--N---O---P
/ / \ \ \/ / /
@@ -595,7 +595,7 @@ such as `--full-history` and `--simplify-merges` works in that case:
\ / /\ /
`---X--' `---Y--'
-----------------------------------------------------------------------
-+
+
For this example, suppose `I` created `file.txt` which was modified by
`A`, `B`, and `X` in different ways. The single-parent commits `C`, `Z`,
and `Y` do not change `file.txt`. The merge commit `M` was created by
@@ -607,19 +607,19 @@ the contents of `file.txt` at `X`. Hence, `R` is TREESAME to `X` but not
contents of `file.txt` at `R`, so `N` is TREESAME to `R` but not `C`.
The merge commits `O` and `P` are TREESAME to their first parents, but
not to their second parents, `Z` and `Y` respectively.
-+
+
When using the default mode, `N` and `R` both have a TREESAME parent, so
those edges are walked and the others are ignored. The resulting history
graph is:
-+
+
-----------------------------------------------------------------------
I---X
-----------------------------------------------------------------------
-+
+
When using `--full-history`, Git walks every edge. This will discover
the commits `A` and `B` and the merge `M`, but also will reveal the
merge commits `O` and `P`. With parent rewriting, the resulting graph is:
-+
+
-----------------------------------------------------------------------
.-A---M--------N---O---P
/ / \ \ \/ / /
@@ -628,21 +628,21 @@ merge commits `O` and `P`. With parent rewriting, the resulting graph is:
\ / /\ /
`---X--' `------'
-----------------------------------------------------------------------
-+
+
Here, the merge commits `O` and `P` contribute extra noise, as they did
not actually contribute a change to `file.txt`. They only merged a topic
that was based on an older version of `file.txt`. This is a common
issue in repositories using a workflow where many contributors work in
parallel and merge their topic branches along a single trunk: manu
unrelated merges appear in the `--full-history` results.
-+
+
When using the `--simplify-merges` option, the commits `O` and `P`
disappear from the results. This is because the rewritten second parents
of `O` and `P` are reachable from their first parents. Those edges are
removed and then the commits look like single-parent commits that are
TREESAME to their parent. This also happens to the commit `N`, resulting
in a history view as follows:
-+
+
-----------------------------------------------------------------------
.-A---M--.
/ / \
@@ -651,18 +651,18 @@ in a history view as follows:
\ / /
`---X--'
-----------------------------------------------------------------------
-+
+
In this view, we see all of the important single-parent changes from
`A`, `B`, and `X`. We also see the carefully-resolved merge `M` and the
not-so-carefully-resolved merge `R`. This is usually enough information
to determine why the commits `A` and `B` "disappeared" from history in
the default view. However, there are a few issues with this approach.
-+
+
The first issue is performance. Unlike any previous option, the
`--simplify-merges` option requires walking the entire commit history
before returning a single result. This can make the option difficult to
use for very large repositories.
-+
+
The second issue is one of auditing. When many contributors are working
on the same repository, it is important which merge commits introduced
a change into an important branch. The problematic merge `R` above is
@@ -671,10 +671,13 @@ important branch. Instead, the merge `N` was used to merge `R` and `X`
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::
+ 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.
+
-The `--show-pulls` option helps with both of these issues by adding more
-merge commits to the history results. If a merge is not TREESAME to its
-first parent but is TREESAME to a later parent, then that merge is
+When a merge commit is included by `--show-pulls`, the merge is
treated as if it "pulled" the change from another branch. When using
`--show-pulls` on this example (and no other options) the resulting
graph is:
diff --git a/Documentation/revisions.txt b/Documentation/revisions.txt
index 1ad9506..d9169c0 100644
--- a/Documentation/revisions.txt
+++ b/Documentation/revisions.txt
@@ -254,6 +254,9 @@ specifying a single revision, using the notation described in the
previous section, means the set of commits `reachable` from the given
commit.
+Specifying several revisions means the set of commits reachable from
+any of the given commits.
+
A commit's reachable set is the commit itself and the commits in
its ancestry chain.
diff --git a/Documentation/technical/commit-graph-format.txt b/Documentation/technical/commit-graph-format.txt
index 1beef17..4405410 100644
--- a/Documentation/technical/commit-graph-format.txt
+++ b/Documentation/technical/commit-graph-format.txt
@@ -32,7 +32,7 @@ the body into "chunks" and provide a binary lookup table at the beginning
of the body. The header includes certain values, such as number of chunks
and hash type.
-All 4-byte numbers are in network order.
+All multi-byte numbers are in network byte order.
HEADER:
diff --git a/Documentation/technical/http-protocol.txt b/Documentation/technical/http-protocol.txt
index 9c5b6f0..51a79e6 100644
--- a/Documentation/technical/http-protocol.txt
+++ b/Documentation/technical/http-protocol.txt
@@ -216,7 +216,7 @@ smart server reply:
S: 001e# service=git-upload-pack\n
S: 0000
S: 004895dcfa3633004da0049d3d0fa03f80589cbcaf31 refs/heads/maint\0multi_ack\n
- S: 0042d049f6c27a2244e12041955e262a404c7faba355 refs/heads/master\n
+ S: 003fd049f6c27a2244e12041955e262a404c7faba355 refs/heads/master\n
S: 003c2cb58b79488a98d2721cea644875a8dd0026b115 refs/tags/v1.0\n
S: 003fa3c2e2402b99163d1d59756e5f207ae21cccba4c refs/tags/v1.0^{}\n
S: 0000
diff --git a/Documentation/technical/pack-protocol.txt b/Documentation/technical/pack-protocol.txt
index d5ce4ee..a4573d1 100644
--- a/Documentation/technical/pack-protocol.txt
+++ b/Documentation/technical/pack-protocol.txt
@@ -96,7 +96,7 @@ Basically what the Git client is doing to connect to an 'upload-pack'
process on the server side over the Git protocol is this:
$ echo -e -n \
- "0039git-upload-pack /schacon/gitbook.git\0host=example.com\0" |
+ "003agit-upload-pack /schacon/gitbook.git\0host=example.com\0" |
nc -v example.com 9418
@@ -171,9 +171,9 @@ with a version number (if "version=1" is sent as an Extra Parameter),
and a listing of each reference it has (all branches and tags) along
with the object name that each reference currently points to.
- $ echo -e -n "0044git-upload-pack /schacon/gitbook.git\0host=example.com\0\0version=1\0" |
+ $ echo -e -n "0045git-upload-pack /schacon/gitbook.git\0host=example.com\0\0version=1\0" |
nc -v example.com 9418
- 000aversion 1
+ 000eversion 1
00887217a7c7e582c46cec22a130adf4b9d7d950fba0 HEAD\0multi_ack thin-pack
side-band side-band-64k ofs-delta shallow no-progress include-tag
00441d3fcd5ced445d1abc402225c0b8a1299641f497 refs/heads/integration
diff --git a/Documentation/technical/packfile-uri.txt b/Documentation/technical/packfile-uri.txt
new file mode 100644
index 0000000..318713a
--- /dev/null
+++ b/Documentation/technical/packfile-uri.txt
@@ -0,0 +1,78 @@
+Packfile URIs
+=============
+
+This feature allows servers to serve part of their packfile response as URIs.
+This allows server designs that improve scalability in bandwidth and CPU usage
+(for example, by serving some data through a CDN), and (in the future) provides
+some measure of resumability to clients.
+
+This feature is available only in protocol version 2.
+
+Protocol
+--------
+
+The server advertises the `packfile-uris` capability.
+
+If the client then communicates which protocols (HTTPS, etc.) it supports with
+a `packfile-uris` argument, the server MAY send a `packfile-uris` section
+directly before the `packfile` section (right after `wanted-refs` if it is
+sent) containing URIs of any of the given protocols. The URIs point to
+packfiles that use only features that the client has declared that it supports
+(e.g. ofs-delta and thin-pack). See protocol-v2.txt for the documentation of
+this section.
+
+Clients should then download and index all the given URIs (in addition to
+downloading and indexing the packfile given in the `packfile` section of the
+response) before performing the connectivity check.
+
+Server design
+-------------
+
+The server can be trivially made compatible with the proposed protocol by
+having it advertise `packfile-uris`, tolerating the client sending
+`packfile-uris`, and never sending any `packfile-uris` section. But we should
+include some sort of non-trivial implementation in the Minimum Viable Product,
+at least so that we can test the client.
+
+This is the implementation: a feature, marked experimental, that allows the
+server to be configured by one or more `uploadpack.blobPackfileUri=<sha1>
+<uri>` entries. Whenever the list of objects to be sent is assembled, all such
+blobs are excluded, replaced with URIs. The client will download those URIs,
+expecting them to each point to packfiles containing single blobs.
+
+Client design
+-------------
+
+The client has a config variable `fetch.uriprotocols` that determines which
+protocols the end user is willing to use. By default, this is empty.
+
+When the client downloads the given URIs, it should store them with "keep"
+files, just like it does with the packfile in the `packfile` section. These
+additional "keep" files can only be removed after the refs have been updated -
+just like the "keep" file for the packfile in the `packfile` section.
+
+The division of work (initial fetch + additional URIs) introduces convenient
+points for resumption of an interrupted clone - such resumption can be done
+after the Minimum Viable Product (see "Future work").
+
+Future work
+-----------
+
+The protocol design allows some evolution of the server and client without any
+need for protocol changes, so only a small-scoped design is included here to
+form the MVP. For example, the following can be done:
+
+ * On the server, more sophisticated means of excluding objects (e.g. by
+ specifying a commit to represent that commit and all objects that it
+ references).
+ * On the client, resumption of clone. If a clone is interrupted, information
+ could be recorded in the repository's config and a "clone-resume" command
+ can resume the clone in progress. (Resumption of subsequent fetches is more
+ difficult because that must deal with the user wanting to use the repository
+ even after the fetch was interrupted.)
+
+There are some possible features that will require a change in protocol:
+
+ * Additional HTTP headers (e.g. authentication)
+ * Byte range support
+ * Different file formats referenced by URIs (e.g. raw object)
diff --git a/Documentation/technical/protocol-capabilities.txt b/Documentation/technical/protocol-capabilities.txt
index 2b267c0..36ccd14 100644
--- a/Documentation/technical/protocol-capabilities.txt
+++ b/Documentation/technical/protocol-capabilities.txt
@@ -176,6 +176,21 @@ agent strings are purely informative for statistics and debugging
purposes, and MUST NOT be used to programmatically assume the presence
or absence of particular features.
+object-format
+-------------
+
+This capability, which takes a hash algorithm as an argument, indicates
+that the server supports the given hash algorithms. It may be sent
+multiple times; if so, the first one given is the one used in the ref
+advertisement.
+
+When provided by the client, this indicates that it intends to use the
+given hash algorithm to communicate. The algorithm provided must be one
+that the server supports.
+
+If this capability is not provided, it is assumed that the only
+supported algorithm is SHA-1.
+
symref
------
diff --git a/Documentation/technical/protocol-v2.txt b/Documentation/technical/protocol-v2.txt
index 7e3766c..e597b74 100644
--- a/Documentation/technical/protocol-v2.txt
+++ b/Documentation/technical/protocol-v2.txt
@@ -33,6 +33,8 @@ In protocol v2 these special packets will have the following semantics:
* '0000' Flush Packet (flush-pkt) - indicates the end of a message
* '0001' Delimiter Packet (delim-pkt) - separates sections of a message
+ * '0002' Message Packet (response-end-pkt) - indicates the end of a response
+ for stateless connections
Initial Client Request
----------------------
@@ -323,13 +325,26 @@ included in the client's request:
indicating its sideband (1, 2, or 3), and the server may send "0005\2"
(a PKT-LINE of sideband 2 with no payload) as a keepalive packet.
+If the 'packfile-uris' feature is advertised, the following argument
+can be included in the client's request as well as the potential
+addition of the 'packfile-uris' section in the server's response as
+explained below.
+
+ packfile-uris <comma-separated list of protocols>
+ Indicates to the server that the client is willing to receive
+ URIs of any of the given protocols in place of objects in the
+ sent packfile. Before performing the connectivity check, the
+ client should download from all given URIs. Currently, the
+ protocols supported are "http" and "https".
+
The response of `fetch` is broken into a number of sections separated by
delimiter packets (0001), with each section beginning with its section
-header.
+header. Most sections are sent only when the packfile is sent.
- output = *section
- section = (acknowledgments | shallow-info | wanted-refs | packfile)
- (flush-pkt | delim-pkt)
+ output = acknowledgements flush-pkt |
+ [acknowledgments delim-pkt] [shallow-info delim-pkt]
+ [wanted-refs delim-pkt] [packfile-uris delim-pkt]
+ packfile flush-pkt
acknowledgments = PKT-LINE("acknowledgments" LF)
(nak | *ack)
@@ -347,13 +362,17 @@ header.
*PKT-LINE(wanted-ref LF)
wanted-ref = obj-id SP refname
+ packfile-uris = PKT-LINE("packfile-uris" LF) *packfile-uri
+ packfile-uri = PKT-LINE(40*(HEXDIGIT) SP *%x20-ff LF)
+
packfile = PKT-LINE("packfile" LF)
*PKT-LINE(%x01-03 *%x00-ff)
acknowledgments section
- * If the client determines that it is finished with negotiations
- by sending a "done" line, the acknowledgments sections MUST be
- omitted from the server's response.
+ * If the client determines that it is finished with negotiations by
+ sending a "done" line (thus requiring the server to send a packfile),
+ the acknowledgments sections MUST be omitted from the server's
+ response.
* Always begins with the section header "acknowledgments"
@@ -404,9 +423,6 @@ header.
which the client has not indicated was shallow as a part of
its request.
- * This section is only included if a packfile section is also
- included in the response.
-
wanted-refs section
* This section is only included if the client has requested a
ref using a 'want-ref' line and if a packfile section is also
@@ -420,6 +436,20 @@ header.
* The server MUST NOT send any refs which were not requested
using 'want-ref' lines.
+ packfile-uris section
+ * This section is only included if the client sent
+ 'packfile-uris' and the server has at least one such URI to
+ send.
+
+ * Always begins with the section header "packfile-uris".
+
+ * For each URI the server sends, it sends a hash of the pack's
+ contents (as output by git index-pack) followed by the URI.
+
+ * The hashes are 40 hex characters long. When Git upgrades to a new
+ hash algorithm, this might need to be updated. (It should match
+ whatever index-pack outputs after "pack\t" or "keep\t".
+
packfile section
* This section is only included if the client has sent 'want'
lines in its request and either requested that no more
@@ -453,3 +483,12 @@ included in a request. This is done by sending each option as a
a request.
The provided options must not contain a NUL or LF character.
+
+ object-format
+~~~~~~~~~~~~~~~
+
+The server can advertise the `object-format` capability with a value `X` (in the
+form `object-format=X`) to notify the client that the server is able to deal
+with objects using hash algorithm X. If not specified, the server is assumed to
+only handle SHA-1. If the client would like to use a hash algorithm other than
+SHA-1, it should specify its object-format string.
diff --git a/Documentation/technical/reftable.txt b/Documentation/technical/reftable.txt
new file mode 100644
index 0000000..2951840
--- /dev/null
+++ b/Documentation/technical/reftable.txt
@@ -0,0 +1,1083 @@
+reftable
+--------
+
+Overview
+~~~~~~~~
+
+Problem statement
+^^^^^^^^^^^^^^^^^
+
+Some repositories contain a lot of references (e.g. android at 866k,
+rails at 31k). The existing packed-refs format takes up a lot of space
+(e.g. 62M), and does not scale with additional references. Lookup of a
+single reference requires linearly scanning the file.
+
+Atomic pushes modifying multiple references require copying the entire
+packed-refs file, which can be a considerable amount of data moved
+(e.g. 62M in, 62M out) for even small transactions (2 refs modified).
+
+Repositories with many loose references occupy a large number of disk
+blocks from the local file system, as each reference is its own file
+storing 41 bytes (and another file for the corresponding reflog). This
+negatively affects the number of inodes available when a large number of
+repositories are stored on the same filesystem. Readers can be penalized
+due to the larger number of syscalls required to traverse and read the
+`$GIT_DIR/refs` directory.
+
+
+Objectives
+^^^^^^^^^^
+
+* Near constant time lookup for any single reference, even when the
+repository is cold and not in process or kernel cache.
+* Near constant time verification if an object name is referred to by at least
+one reference (for allow-tip-sha1-in-want).
+* Efficient enumeration of an entire namespace, such as `refs/tags/`.
+* Support atomic push with `O(size_of_update)` operations.
+* Combine reflog storage with ref storage for small transactions.
+* Separate reflog storage for base refs and historical logs.
+
+Description
+^^^^^^^^^^^
+
+A reftable file is a portable binary file format customized for
+reference storage. References are sorted, enabling linear scans, binary
+search lookup, and range scans.
+
+Storage in the file is organized into variable sized blocks. Prefix
+compression is used within a single block to reduce disk space. Block
+size and alignment is tunable by the writer.
+
+Performance
+^^^^^^^^^^^
+
+Space used, packed-refs vs. reftable:
+
+[cols=",>,>,>,>,>",options="header",]
+|===============================================================
+|repository |packed-refs |reftable |% original |avg ref |avg obj
+|android |62.2 M |36.1 M |58.0% |33 bytes |5 bytes
+|rails |1.8 M |1.1 M |57.7% |29 bytes |4 bytes
+|git |78.7 K |48.1 K |61.0% |50 bytes |4 bytes
+|git (heads) |332 b |269 b |81.0% |33 bytes |0 bytes
+|===============================================================
+
+Scan (read 866k refs), by reference name lookup (single ref from 866k
+refs), and by SHA-1 lookup (refs with that SHA-1, from 866k refs):
+
+[cols=",>,>,>,>",options="header",]
+|=========================================================
+|format |cache |scan |by name |by SHA-1
+|packed-refs |cold |402 ms |409,660.1 usec |412,535.8 usec
+|packed-refs |hot | |6,844.6 usec |20,110.1 usec
+|reftable |cold |112 ms |33.9 usec |323.2 usec
+|reftable |hot | |20.2 usec |320.8 usec
+|=========================================================
+
+Space used for 149,932 log entries for 43,061 refs, reflog vs. reftable:
+
+[cols=",>,>",options="header",]
+|================================
+|format |size |avg entry
+|$GIT_DIR/logs |173 M |1209 bytes
+|reftable |5 M |37 bytes
+|================================
+
+Details
+~~~~~~~
+
+Peeling
+^^^^^^^
+
+References stored in a reftable are peeled, a record for an annotated
+(or signed) tag records both the tag object, and the object it refers
+to. This is analogous to storage in the packed-refs format.
+
+Reference name encoding
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Reference names are an uninterpreted sequence of bytes that must pass
+linkgit:git-check-ref-format[1] as a valid reference name.
+
+Key unicity
+^^^^^^^^^^^
+
+Each entry must have a unique key; repeated keys are disallowed.
+
+Network byte order
+^^^^^^^^^^^^^^^^^^
+
+All multi-byte, fixed width fields are in network byte order.
+
+Varint encoding
+^^^^^^^^^^^^^^^
+
+Varint encoding is identical to the ofs-delta encoding method used
+within pack files.
+
+Decoder works such as:
+
+....
+val = buf[ptr] & 0x7f
+while (buf[ptr] & 0x80) {
+ ptr++
+ val = ((val + 1) << 7) | (buf[ptr] & 0x7f)
+}
+....
+
+Ordering
+^^^^^^^^
+
+Blocks are lexicographically ordered by their first reference.
+
+Directory/file conflicts
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+The reftable format accepts both `refs/heads/foo` and
+`refs/heads/foo/bar` as distinct references.
+
+This property is useful for retaining log records in reftable, but may
+confuse versions of Git using `$GIT_DIR/refs` directory tree to maintain
+references. Users of reftable may choose to continue to reject `foo` and
+`foo/bar` type conflicts to prevent problems for peers.
+
+File format
+~~~~~~~~~~~
+
+Structure
+^^^^^^^^^
+
+A reftable file has the following high-level structure:
+
+....
+first_block {
+ header
+ first_ref_block
+}
+ref_block*
+ref_index*
+obj_block*
+obj_index*
+log_block*
+log_index*
+footer
+....
+
+A log-only file omits the `ref_block`, `ref_index`, `obj_block` and
+`obj_index` sections, containing only the file header and log block:
+
+....
+first_block {
+ header
+}
+log_block*
+log_index*
+footer
+....
+
+in a log-only file the first log block immediately follows the file
+header, without padding to block alignment.
+
+Block size
+^^^^^^^^^^
+
+The file's block size is arbitrarily determined by the writer, and does
+not have to be a power of 2. The block size must be larger than the
+longest reference name or log entry used in the repository, as
+references cannot span blocks.
+
+Powers of two that are friendly to the virtual memory system or
+filesystem (such as 4k or 8k) are recommended. Larger sizes (64k) can
+yield better compression, with a possible increased cost incurred by
+readers during access.
+
+The largest block size is `16777215` bytes (15.99 MiB).
+
+Block alignment
+^^^^^^^^^^^^^^^
+
+Writers may choose to align blocks at multiples of the block size by
+including `padding` filled with NUL bytes at the end of a block to round
+out to the chosen alignment. When alignment is used, writers must
+specify the alignment with the file header's `block_size` field.
+
+Block alignment is not required by the file format. Unaligned files must
+set `block_size = 0` in the file header, and omit `padding`. Unaligned
+files with more than one ref block must include the link:#Ref-index[ref
+index] to support fast lookup. Readers must be able to read both aligned
+and non-aligned files.
+
+Very small files (e.g. a single ref block) may omit `padding` and the ref
+index to reduce total file size.
+
+Header (version 1)
+^^^^^^^^^^^^^^^^^^
+
+A 24-byte header appears at the beginning of the file:
+
+....
+'REFT'
+uint8( version_number = 1 )
+uint24( block_size )
+uint64( min_update_index )
+uint64( max_update_index )
+....
+
+Aligned files must specify `block_size` to configure readers with the
+expected block alignment. Unaligned files must set `block_size = 0`.
+
+The `min_update_index` and `max_update_index` describe bounds for the
+`update_index` field of all log records in this file. When reftables are
+used in a stack for link:#Update-transactions[transactions], these
+fields can order the files such that the prior file's
+`max_update_index + 1` is the next file's `min_update_index`.
+
+Header (version 2)
+^^^^^^^^^^^^^^^^^^
+
+A 28-byte header appears at the beginning of the file:
+
+....
+'REFT'
+uint8( version_number = 2 )
+uint24( block_size )
+uint64( min_update_index )
+uint64( max_update_index )
+uint32( hash_id )
+....
+
+The header is identical to `version_number=1`, with the 4-byte hash ID
+("sha1" for SHA1 and "s256" for SHA-256) append to the header.
+
+For maximum backward compatibility, it is recommended to use version 1 when
+writing SHA1 reftables.
+
+First ref block
+^^^^^^^^^^^^^^^
+
+The first ref block shares the same block as the file header, and is 24
+bytes smaller than all other blocks in the file. The first block
+immediately begins after the file header, at position 24.
+
+If the first block is a log block (a log-only file), its block header
+begins immediately at position 24.
+
+Ref block format
+^^^^^^^^^^^^^^^^
+
+A ref block is written as:
+
+....
+'r'
+uint24( block_len )
+ref_record+
+uint24( restart_offset )+
+uint16( restart_count )
+
+padding?
+....
+
+Blocks begin with `block_type = 'r'` and a 3-byte `block_len` which
+encodes the number of bytes in the block up to, but not including the
+optional `padding`. This is always less than or equal to the file's
+block size. In the first ref block, `block_len` includes 24 bytes for
+the file header.
+
+The 2-byte `restart_count` stores the number of entries in the
+`restart_offset` list, which must not be empty. Readers can use
+`restart_count` to binary search between restarts before starting a
+linear scan.
+
+Exactly `restart_count` 3-byte `restart_offset` values precedes the
+`restart_count`. Offsets are relative to the start of the block and
+refer to the first byte of any `ref_record` whose name has not been
+prefix compressed. Entries in the `restart_offset` list must be sorted,
+ascending. Readers can start linear scans from any of these records.
+
+A variable number of `ref_record` fill the middle of the block,
+describing reference names and values. The format is described below.
+
+As the first ref block shares the first file block with the file header,
+all `restart_offset` in the first block are relative to the start of the
+file (position 0), and include the file header. This forces the first
+`restart_offset` to be `28`.
+
+ref record
+++++++++++
+
+A `ref_record` describes a single reference, storing both the name and
+its value(s). Records are formatted as:
+
+....
+varint( prefix_length )
+varint( (suffix_length << 3) | value_type )
+suffix
+varint( update_index_delta )
+value?
+....
+
+The `prefix_length` field specifies how many leading bytes of the prior
+reference record's name should be copied to obtain this reference's
+name. This must be 0 for the first reference in any block, and also must
+be 0 for any `ref_record` whose offset is listed in the `restart_offset`
+table at the end of the block.
+
+Recovering a reference name from any `ref_record` is a simple concat:
+
+....
+this_name = prior_name[0..prefix_length] + suffix
+....
+
+The `suffix_length` value provides the number of bytes available in
+`suffix` to copy from `suffix` to complete the reference name.
+
+The `update_index` that last modified the reference can be obtained by
+adding `update_index_delta` to the `min_update_index` from the file
+header: `min_update_index + update_index_delta`.
+
+The `value` follows. Its format is determined by `value_type`, one of
+the following:
+
+* `0x0`: deletion; no value data (see transactions, below)
+* `0x1`: one object name; value of the ref
+* `0x2`: two object names; value of the ref, peeled target
+* `0x3`: symbolic reference: `varint( target_len ) target`
+
+Symbolic references use `0x3`, followed by the complete name of the
+reference target. No compression is applied to the target name.
+
+Types `0x4..0x7` are reserved for future use.
+
+Ref index
+^^^^^^^^^
+
+The ref index stores the name of the last reference from every ref block
+in the file, enabling reduced disk seeks for lookups. Any reference can
+be found by searching the index, identifying the containing block, and
+searching within that block.
+
+The index may be organized into a multi-level index, where the 1st level
+index block points to additional ref index blocks (2nd level), which may
+in turn point to either additional index blocks (e.g. 3rd level) or ref
+blocks (leaf level). Disk reads required to access a ref go up with
+higher index levels. Multi-level indexes may be required to ensure no
+single index block exceeds the file format's max block size of
+`16777215` bytes (15.99 MiB). To achieve constant O(1) disk seeks for
+lookups the index must be a single level, which is permitted to exceed
+the file's configured block size, but not the format's max block size of
+15.99 MiB.
+
+If present, the ref index block(s) appears after the last ref block.
+
+If there are at least 4 ref blocks, a ref index block should be written
+to improve lookup times. Cold reads using the index require 2 disk reads
+(read index, read block), and binary searching < 4 blocks also requires
+<= 2 reads. Omitting the index block from smaller files saves space.
+
+If the file is unaligned and contains more than one ref block, the ref
+index must be written.
+
+Index block format:
+
+....
+'i'
+uint24( block_len )
+index_record+
+uint24( restart_offset )+
+uint16( restart_count )
+
+padding?
+....
+
+The index blocks begin with `block_type = 'i'` and a 3-byte `block_len`
+which encodes the number of bytes in the block, up to but not including
+the optional `padding`.
+
+The `restart_offset` and `restart_count` fields are identical in format,
+meaning and usage as in ref blocks.
+
+To reduce the number of reads required for random access in very large
+files the index block may be larger than other blocks. However, readers
+must hold the entire index in memory to benefit from this, so it's a
+time-space tradeoff in both file size and reader memory.
+
+Increasing the file's block size decreases the index size. Alternatively
+a multi-level index may be used, keeping index blocks within the file's
+block size, but increasing the number of blocks that need to be
+accessed.
+
+index record
+++++++++++++
+
+An index record describes the last entry in another block. Index records
+are written as:
+
+....
+varint( prefix_length )
+varint( (suffix_length << 3) | 0 )
+suffix
+varint( block_position )
+....
+
+Index records use prefix compression exactly like `ref_record`.
+
+Index records store `block_position` after the suffix, specifying the
+absolute position in bytes (from the start of the file) of the block
+that ends with this reference. Readers can seek to `block_position` to
+begin reading the block header.
+
+Readers must examine the block header at `block_position` to determine
+if the next block is another level index block, or the leaf-level ref
+block.
+
+Reading the index
++++++++++++++++++
+
+Readers loading the ref index must first read the footer (below) to
+obtain `ref_index_position`. If not present, the position will be 0. The
+`ref_index_position` is for the 1st level root of the ref index.
+
+Obj block format
+^^^^^^^^^^^^^^^^
+
+Object blocks are optional. Writers may choose to omit object blocks,
+especially if readers will not use the object name to ref mapping.
+
+Object blocks use unique, abbreviated 2-32 object name keys, mapping to
+ref blocks containing references pointing to that object directly, or as
+the peeled value of an annotated tag. Like ref blocks, object blocks use
+the file's standard block size. The abbrevation length is available in
+the footer as `obj_id_len`.
+
+To save space in small files, object blocks may be omitted if the ref
+index is not present, as brute force search will only need to read a few
+ref blocks. When missing, readers should brute force a linear search of
+all references to lookup by object name.
+
+An object block is written as:
+
+....
+'o'
+uint24( block_len )
+obj_record+
+uint24( restart_offset )+
+uint16( restart_count )
+
+padding?
+....
+
+Fields are identical to ref block. Binary search using the restart table
+works the same as in reference blocks.
+
+Because object names are abbreviated by writers to the shortest unique
+abbreviation within the reftable, obj key lengths have a variable length. Their
+length must be at least 2 bytes. Readers must compare only for common prefix
+match within an obj block or obj index.
+
+obj record
+++++++++++
+
+An `obj_record` describes a single object abbreviation, and the blocks
+containing references using that unique abbreviation:
+
+....
+varint( prefix_length )
+varint( (suffix_length << 3) | cnt_3 )
+suffix
+varint( cnt_large )?
+varint( position_delta )*
+....
+
+Like in reference blocks, abbreviations are prefix compressed within an
+obj block. On large reftables with many unique objects, higher block
+sizes (64k), and higher restart interval (128), a `prefix_length` of 2
+or 3 and `suffix_length` of 3 may be common in obj records (unique
+abbreviation of 5-6 raw bytes, 10-12 hex digits).
+
+Each record contains `position_count` number of positions for matching
+ref blocks. For 1-7 positions the count is stored in `cnt_3`. When
+`cnt_3 = 0` the actual count follows in a varint, `cnt_large`.
+
+The use of `cnt_3` bets most objects are pointed to by only a single
+reference, some may be pointed to by a couple of references, and very
+few (if any) are pointed to by more than 7 references.
+
+A special case exists when `cnt_3 = 0` and `cnt_large = 0`: there are no
+`position_delta`, but at least one reference starts with this
+abbreviation. A reader that needs exact reference names must scan all
+references to find which specific references have the desired object.
+Writers should use this format when the `position_delta` list would have
+overflowed the file's block size due to a high number of references
+pointing to the same object.
+
+The first `position_delta` is the position from the start of the file.
+Additional `position_delta` entries are sorted ascending and relative to
+the prior entry, e.g. a reader would perform:
+
+....
+pos = position_delta[0]
+prior = pos
+for (j = 1; j < position_count; j++) {
+ pos = prior + position_delta[j]
+ prior = pos
+}
+....
+
+With a position in hand, a reader must linearly scan the ref block,
+starting from the first `ref_record`, testing each reference's object names
+(for `value_type = 0x1` or `0x2`) for full equality. Faster searching by
+object name within a single ref block is not supported by the reftable format.
+Smaller block sizes reduce the number of candidates this step must
+consider.
+
+Obj index
+^^^^^^^^^
+
+The obj index stores the abbreviation from the last entry for every obj
+block in the file, enabling reduced disk seeks for all lookups. It is
+formatted exactly the same as the ref index, but refers to obj blocks.
+
+The obj index should be present if obj blocks are present, as obj blocks
+should only be written in larger files.
+
+Readers loading the obj index must first read the footer (below) to
+obtain `obj_index_position`. If not present, the position will be 0.
+
+Log block format
+^^^^^^^^^^^^^^^^
+
+Unlike ref and obj blocks, log blocks are always unaligned.
+
+Log blocks are variable in size, and do not match the `block_size`
+specified in the file header or footer. Writers should choose an
+appropriate buffer size to prepare a log block for deflation, such as
+`2 * block_size`.
+
+A log block is written as:
+
+....
+'g'
+uint24( block_len )
+zlib_deflate {
+ log_record+
+ uint24( restart_offset )+
+ uint16( restart_count )
+}
+....
+
+Log blocks look similar to ref blocks, except `block_type = 'g'`.
+
+The 4-byte block header is followed by the deflated block contents using
+zlib deflate. The `block_len` in the header is the inflated size
+(including 4-byte block header), and should be used by readers to
+preallocate the inflation output buffer. A log block's `block_len` may
+exceed the file's block size.
+
+Offsets within the log block (e.g. `restart_offset`) still include the
+4-byte header. Readers may prefer prefixing the inflation output buffer
+with the 4-byte header.
+
+Within the deflate container, a variable number of `log_record` describe
+reference changes. The log record format is described below. See ref
+block format (above) for a description of `restart_offset` and
+`restart_count`.
+
+Because log blocks have no alignment or padding between blocks, readers
+must keep track of the bytes consumed by the inflater to know where the
+next log block begins.
+
+log record
+++++++++++
+
+Log record keys are structured as:
+
+....
+ref_name '\0' reverse_int64( update_index )
+....
+
+where `update_index` is the unique transaction identifier. The
+`update_index` field must be unique within the scope of a `ref_name`.
+See the update transactions section below for further details.
+
+The `reverse_int64` function inverses the value so lexicographical
+ordering the network byte order encoding sorts the more recent records
+with higher `update_index` values first:
+
+....
+reverse_int64(int64 t) {
+ return 0xffffffffffffffff - t;
+}
+....
+
+Log records have a similar starting structure to ref and index records,
+utilizing the same prefix compression scheme applied to the log record
+key described above.
+
+....
+ varint( prefix_length )
+ varint( (suffix_length << 3) | log_type )
+ suffix
+ log_data {
+ old_id
+ new_id
+ varint( name_length ) name
+ varint( email_length ) email
+ varint( time_seconds )
+ sint16( tz_offset )
+ varint( message_length ) message
+ }?
+....
+
+Log record entries use `log_type` to indicate what follows:
+
+* `0x0`: deletion; no log data.
+* `0x1`: standard git reflog data using `log_data` above.
+
+The `log_type = 0x0` is mostly useful for `git stash drop`, removing an
+entry from the reflog of `refs/stash` in a transaction file (below),
+without needing to rewrite larger files. Readers reading a stack of
+reflogs must treat this as a deletion.
+
+For `log_type = 0x1`, the `log_data` section follows
+linkgit:git-update-ref[1] logging and includes:
+
+* two object names (old id, new id)
+* varint string of committer's name
+* varint string of committer's email
+* varint time in seconds since epoch (Jan 1, 1970)
+* 2-byte timezone offset in minutes (signed)
+* varint string of message
+
+`tz_offset` is the absolute number of minutes from GMT the committer was
+at the time of the update. For example `GMT-0800` is encoded in reftable
+as `sint16(-480)` and `GMT+0230` is `sint16(150)`.
+
+The committer email does not contain `<` or `>`, it's the value normally
+found between the `<>` in a git commit object header.
+
+The `message_length` may be 0, in which case there was no message
+supplied for the update.
+
+Contrary to traditional reflog (which is a file), renames are encoded as
+a combination of ref deletion and ref creation. A deletion is a log
+record with a zero new_id, and a creation is a log record with a zero old_id.
+
+Reading the log
++++++++++++++++
+
+Readers accessing the log must first read the footer (below) to
+determine the `log_position`. The first block of the log begins at
+`log_position` bytes since the start of the file. The `log_position` is
+not block aligned.
+
+Importing logs
+++++++++++++++
+
+When importing from `$GIT_DIR/logs` writers should globally order all
+log records roughly by timestamp while preserving file order, and assign
+unique, increasing `update_index` values for each log line. Newer log
+records get higher `update_index` values.
+
+Although an import may write only a single reftable file, the reftable
+file must span many unique `update_index`, as each log line requires its
+own `update_index` to preserve semantics.
+
+Log index
+^^^^^^^^^
+
+The log index stores the log key
+(`refname \0 reverse_int64(update_index)`) for the last log record of
+every log block in the file, supporting bounded-time lookup.
+
+A log index block must be written if 2 or more log blocks are written to
+the file. If present, the log index appears after the last log block.
+There is no padding used to align the log index to block alignment.
+
+Log index format is identical to ref index, except the keys are 9 bytes
+longer to include `'\0'` and the 8-byte `reverse_int64(update_index)`.
+Records use `block_position` to refer to the start of a log block.
+
+Reading the index
++++++++++++++++++
+
+Readers loading the log index must first read the footer (below) to
+obtain `log_index_position`. If not present, the position will be 0.
+
+Footer
+^^^^^^
+
+After the last block of the file, a file footer is written. It begins
+like the file header, but is extended with additional data.
+
+....
+ HEADER
+
+ uint64( ref_index_position )
+ uint64( (obj_position << 5) | obj_id_len )
+ uint64( obj_index_position )
+
+ uint64( log_position )
+ uint64( log_index_position )
+
+ uint32( CRC-32 of above )
+....
+
+If a section is missing (e.g. ref index) the corresponding position
+field (e.g. `ref_index_position`) will be 0.
+
+* `obj_position`: byte position for the first obj block.
+* `obj_id_len`: number of bytes used to abbreviate object names in
+obj blocks.
+* `log_position`: byte position for the first log block.
+* `ref_index_position`: byte position for the start of the ref index.
+* `obj_index_position`: byte position for the start of the obj index.
+* `log_index_position`: byte position for the start of the log index.
+
+The size of the footer is 68 bytes for version 1, and 72 bytes for
+version 2.
+
+Reading the footer
+++++++++++++++++++
+
+Readers must first read the file start to determine the version
+number. Then they seek to `file_length - FOOTER_LENGTH` to access the
+footer. A trusted external source (such as `stat(2)`) is necessary to
+obtain `file_length`. When reading the footer, readers must verify:
+
+* 4-byte magic is correct
+* 1-byte version number is recognized
+* 4-byte CRC-32 matches the other 64 bytes (including magic, and
+version)
+
+Once verified, the other fields of the footer can be accessed.
+
+Empty tables
+++++++++++++
+
+A reftable may be empty. In this case, the file starts with a header
+and is immediately followed by a footer.
+
+Binary search
+^^^^^^^^^^^^^
+
+Binary search within a block is supported by the `restart_offset` fields
+at the end of the block. Readers can binary search through the restart
+table to locate between which two restart points the sought reference or
+key should appear.
+
+Each record identified by a `restart_offset` stores the complete key in
+the `suffix` field of the record, making the compare operation during
+binary search straightforward.
+
+Once a restart point lexicographically before the sought reference has
+been identified, readers can linearly scan through the following record
+entries to locate the sought record, terminating if the current record
+sorts after (and therefore the sought key is not present).
+
+Restart point selection
++++++++++++++++++++++++
+
+Writers determine the restart points at file creation. The process is
+arbitrary, but every 16 or 64 records is recommended. Every 16 may be
+more suitable for smaller block sizes (4k or 8k), every 64 for larger
+block sizes (64k).
+
+More frequent restart points reduces prefix compression and increases
+space consumed by the restart table, both of which increase file size.
+
+Less frequent restart points makes prefix compression more effective,
+decreasing overall file size, with increased penalties for readers
+walking through more records after the binary search step.
+
+A maximum of `65535` restart points per block is supported.
+
+Considerations
+~~~~~~~~~~~~~~
+
+Lightweight refs dominate
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The reftable format assumes the vast majority of references are single
+object names valued with common prefixes, such as Gerrit Code Review's
+`refs/changes/` namespace, GitHub's `refs/pulls/` namespace, or many
+lightweight tags in the `refs/tags/` namespace.
+
+Annotated tags storing the peeled object cost an additional object name per
+reference.
+
+Low overhead
+^^^^^^^^^^^^
+
+A reftable with very few references (e.g. git.git with 5 heads) is 269
+bytes for reftable, vs. 332 bytes for packed-refs. This supports
+reftable scaling down for transaction logs (below).
+
+Block size
+^^^^^^^^^^
+
+For a Gerrit Code Review type repository with many change refs, larger
+block sizes (64 KiB) and less frequent restart points (every 64) yield
+better compression due to more references within the block compressing
+against the prior reference.
+
+Larger block sizes reduce the index size, as the reftable will require
+fewer blocks to store the same number of references.
+
+Minimal disk seeks
+^^^^^^^^^^^^^^^^^^
+
+Assuming the index block has been loaded into memory, binary searching
+for any single reference requires exactly 1 disk seek to load the
+containing block.
+
+Scans and lookups dominate
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Scanning all references and lookup by name (or namespace such as
+`refs/heads/`) are the most common activities performed on repositories.
+Object names are stored directly with references to optimize this use case.
+
+Logs are infrequently read
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Logs are infrequently accessed, but can be large. Deflating log blocks
+saves disk space, with some increased penalty at read time.
+
+Logs are stored in an isolated section from refs, reducing the burden on
+reference readers that want to ignore logs. Further, historical logs can
+be isolated into log-only files.
+
+Logs are read backwards
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Logs are frequently accessed backwards (most recent N records for master
+to answer `master@{4}`), so log records are grouped by reference, and
+sorted descending by update index.
+
+Repository format
+~~~~~~~~~~~~~~~~~
+
+Version 1
+^^^^^^^^^
+
+A repository must set its `$GIT_DIR/config` to configure reftable:
+
+....
+[core]
+ repositoryformatversion = 1
+[extensions]
+ refStorage = reftable
+....
+
+Layout
+^^^^^^
+
+A collection of reftable files are stored in the `$GIT_DIR/reftable/`
+directory:
+
+....
+00000001-00000001.log
+00000002-00000002.ref
+00000003-00000003.ref
+....
+
+where reftable files are named by a unique name such as produced by the
+function `${min_update_index}-${max_update_index}.ref`.
+
+Log-only files use the `.log` extension, while ref-only and mixed ref
+and log files use `.ref`. extension.
+
+The stack ordering file is `$GIT_DIR/reftable/tables.list` and lists the
+current files, one per line, in order, from oldest (base) to newest
+(most recent):
+
+....
+$ cat .git/reftable/tables.list
+00000001-00000001.log
+00000002-00000002.ref
+00000003-00000003.ref
+....
+
+Readers must read `$GIT_DIR/reftable/tables.list` to determine which
+files are relevant right now, and search through the stack in reverse
+order (last reftable is examined first).
+
+Reftable files not listed in `tables.list` may be new (and about to be
+added to the stack by the active writer), or ancient and ready to be
+pruned.
+
+Backward compatibility
+^^^^^^^^^^^^^^^^^^^^^^
+
+Older clients should continue to recognize the directory as a git
+repository so they don't look for an enclosing repository in parent
+directories. To this end, a reftable-enabled repository must contain the
+following dummy files
+
+* `.git/HEAD`, a regular file containing `ref: refs/heads/.invalid`.
+* `.git/refs/`, a directory
+* `.git/refs/heads`, a regular file
+
+Readers
+^^^^^^^
+
+Readers can obtain a consistent snapshot of the reference space by
+following:
+
+1. Open and read the `tables.list` file.
+2. Open each of the reftable files that it mentions.
+3. If any of the files is missing, goto 1.
+4. Read from the now-open files as long as necessary.
+
+Update transactions
+^^^^^^^^^^^^^^^^^^^
+
+Although reftables are immutable, mutations are supported by writing a
+new reftable and atomically appending it to the stack:
+
+1. Acquire `tables.list.lock`.
+2. Read `tables.list` to determine current reftables.
+3. Select `update_index` to be most recent file's
+`max_update_index + 1`.
+4. Prepare temp reftable `tmp_XXXXXX`, including log entries.
+5. Rename `tmp_XXXXXX` to `${update_index}-${update_index}.ref`.
+6. Copy `tables.list` to `tables.list.lock`, appending file from (5).
+7. Rename `tables.list.lock` to `tables.list`.
+
+During step 4 the new file's `min_update_index` and `max_update_index`
+are both set to the `update_index` selected by step 3. All log records
+for the transaction use the same `update_index` in their keys. This
+enables later correlation of which references were updated by the same
+transaction.
+
+Because a single `tables.list.lock` file is used to manage locking, the
+repository is single-threaded for writers. Writers may have to busy-spin
+(with backoff) around creating `tables.list.lock`, for up to an
+acceptable wait period, aborting if the repository is too busy to
+mutate. Application servers wrapped around repositories (e.g. Gerrit
+Code Review) can layer their own lock/wait queue to improve fairness to
+writers.
+
+Reference deletions
+^^^^^^^^^^^^^^^^^^^
+
+Deletion of any reference can be explicitly stored by setting the `type`
+to `0x0` and omitting the `value` field of the `ref_record`. This serves
+as a tombstone, overriding any assertions about the existence of the
+reference from earlier files in the stack.
+
+Compaction
+^^^^^^^^^^
+
+A partial stack of reftables can be compacted by merging references
+using a straightforward merge join across reftables, selecting the most
+recent value for output, and omitting deleted references that do not
+appear in remaining, lower reftables.
+
+A compacted reftable should set its `min_update_index` to the smallest
+of the input files' `min_update_index`, and its `max_update_index`
+likewise to the largest input `max_update_index`.
+
+For sake of illustration, assume the stack currently consists of
+reftable files (from oldest to newest): A, B, C, and D. The compactor is
+going to compact B and C, leaving A and D alone.
+
+1. Obtain lock `tables.list.lock` and read the `tables.list` file.
+2. Obtain locks `B.lock` and `C.lock`. Ownership of these locks
+prevents other processes from trying to compact these files.
+3. Release `tables.list.lock`.
+4. Compact `B` and `C` into a temp file
+`${min_update_index}-${max_update_index}_XXXXXX`.
+5. Reacquire lock `tables.list.lock`.
+6. Verify that `B` and `C` are still in the stack, in that order. This
+should always be the case, assuming that other processes are adhering to
+the locking protocol.
+7. Rename `${min_update_index}-${max_update_index}_XXXXXX` to
+`${min_update_index}-${max_update_index}.ref`.
+8. Write the new stack to `tables.list.lock`, replacing `B` and `C`
+with the file from (4).
+9. Rename `tables.list.lock` to `tables.list`.
+10. Delete `B` and `C`, perhaps after a short sleep to avoid forcing
+readers to backtrack.
+
+This strategy permits compactions to proceed independently of updates.
+
+Each reftable (compacted or not) is uniquely identified by its name, so
+open reftables can be cached by their name.
+
+Alternatives considered
+~~~~~~~~~~~~~~~~~~~~~~~
+
+bzip packed-refs
+^^^^^^^^^^^^^^^^
+
+`bzip2` can significantly shrink a large packed-refs file (e.g. 62 MiB
+compresses to 23 MiB, 37%). However the bzip format does not support
+random access to a single reference. Readers must inflate and discard
+while performing a linear scan.
+
+Breaking packed-refs into chunks (individually compressing each chunk)
+would reduce the amount of data a reader must inflate, but still leaves
+the problem of indexing chunks to support readers efficiently locating
+the correct chunk.
+
+Given the compression achieved by reftable's encoding, it does not seem
+necessary to add the complexity of bzip/gzip/zlib.
+
+Michael Haggerty's alternate format
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Michael Haggerty proposed
+link:https://lore.kernel.org/git/CAMy9T_HCnyc1g8XWOOWhe7nN0aEFyyBskV2aOMb_fe%2BwGvEJ7A%40mail.gmail.com/[an
+alternate] format to reftable on the Git mailing list. This format uses
+smaller chunks, without the restart table, and avoids block alignment
+with padding. Reflog entries immediately follow each ref, and are thus
+interleaved between refs.
+
+Performance testing indicates reftable is faster for lookups (51%
+faster, 11.2 usec vs. 5.4 usec), although reftable produces a slightly
+larger file (+ ~3.2%, 28.3M vs 29.2M):
+
+[cols=">,>,>,>",options="header",]
+|=====================================
+|format |size |seek cold |seek hot
+|mh-alt |28.3 M |23.4 usec |11.2 usec
+|reftable |29.2 M |19.9 usec |5.4 usec
+|=====================================
+
+JGit Ketch RefTree
+^^^^^^^^^^^^^^^^^^
+
+https://dev.eclipse.org/mhonarc/lists/jgit-dev/msg03073.html[JGit Ketch]
+proposed
+link:https://lore.kernel.org/git/CAJo%3DhJvnAPNAdDcAAwAvU9C4RVeQdoS3Ev9WTguHx4fD0V_nOg%40mail.gmail.com/[RefTree],
+an encoding of references inside Git tree objects stored as part of the
+repository's object database.
+
+The RefTree format adds additional load on the object database storage
+layer (more loose objects, more objects in packs), and relies heavily on
+the packer's delta compression to save space. Namespaces which are flat
+(e.g. thousands of tags in refs/tags) initially create very large loose
+objects, and so RefTree does not address the problem of copying many
+references to modify a handful.
+
+Flat namespaces are not efficiently searchable in RefTree, as tree
+objects in canonical formatting cannot be binary searched. This fails
+the need to handle a large number of references in a single namespace,
+such as GitHub's `refs/pulls`, or a project with many tags.
+
+LMDB
+^^^^
+
+David Turner proposed
+https://lore.kernel.org/git/1455772670-21142-26-git-send-email-dturner@twopensource.com/[using
+LMDB], as LMDB is lightweight (64k of runtime code) and GPL-compatible
+license.
+
+A downside of LMDB is its reliance on a single C implementation. This
+makes embedding inside JGit (a popular reimplementation of Git)
+difficult, and hoisting onto virtual storage (for JGit DFS) virtually
+impossible.
+
+A common format that can be supported by all major Git implementations
+(git-core, JGit, libgit2) is strongly preferred.
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 8336529..fd480b8 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -347,7 +347,7 @@ $ git branch -r
origin/man
origin/master
origin/next
- origin/pu
+ origin/seen
origin/todo
------------------------------------------------
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 165ce7a..9db2f4f 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.27.0-rc1
+DEF_VER=v2.28.0
LF='
'
diff --git a/Makefile b/Makefile
index 90aa329..372139f 100644
--- a/Makefile
+++ b/Makefile
@@ -1186,7 +1186,7 @@ PTHREAD_CFLAGS =
# For the 'sparse' target
SPARSE_FLAGS ?=
-SP_EXTRA_FLAGS =
+SP_EXTRA_FLAGS = -Wno-universal-initializer
# For the 'coccicheck' target; setting SPATCH_BATCH_SIZE higher will
# usually result in less CPU usage at the cost of higher peak memory.
diff --git a/RelNotes b/RelNotes
index f3d8527..9143650 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.27.0.txt \ No newline at end of file
+Documentation/RelNotes/2.29.0.txt \ No newline at end of file
diff --git a/add-patch.c b/add-patch.c
index d8bfe37..a1d66c1 100644
--- a/add-patch.c
+++ b/add-patch.c
@@ -10,7 +10,7 @@
#include "prompt.h"
enum prompt_mode_type {
- PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK,
+ PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_ADDITION, PROMPT_HUNK,
PROMPT_MODE_MAX, /* must be last */
};
@@ -33,6 +33,7 @@ static struct patch_mode patch_mode_add = {
.prompt_mode = {
N_("Stage mode change [y,n,q,a,d%s,?]? "),
N_("Stage deletion [y,n,q,a,d%s,?]? "),
+ N_("Stage addition [y,n,q,a,d%s,?]? "),
N_("Stage this hunk [y,n,q,a,d%s,?]? ")
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@@ -54,6 +55,7 @@ static struct patch_mode patch_mode_stash = {
.prompt_mode = {
N_("Stash mode change [y,n,q,a,d%s,?]? "),
N_("Stash deletion [y,n,q,a,d%s,?]? "),
+ N_("Stash addition [y,n,q,a,d%s,?]? "),
N_("Stash this hunk [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@@ -77,6 +79,7 @@ static struct patch_mode patch_mode_reset_head = {
.prompt_mode = {
N_("Unstage mode change [y,n,q,a,d%s,?]? "),
N_("Unstage deletion [y,n,q,a,d%s,?]? "),
+ N_("Unstage addition [y,n,q,a,d%s,?]? "),
N_("Unstage this hunk [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@@ -99,6 +102,7 @@ static struct patch_mode patch_mode_reset_nothead = {
.prompt_mode = {
N_("Apply mode change to index [y,n,q,a,d%s,?]? "),
N_("Apply deletion to index [y,n,q,a,d%s,?]? "),
+ N_("Apply addition to index [y,n,q,a,d%s,?]? "),
N_("Apply this hunk to index [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@@ -121,6 +125,7 @@ static struct patch_mode patch_mode_checkout_index = {
.prompt_mode = {
N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
+ N_("Discard addition from worktree [y,n,q,a,d%s,?]? "),
N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@@ -143,6 +148,7 @@ static struct patch_mode patch_mode_checkout_head = {
.prompt_mode = {
N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
+ N_("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@@ -164,6 +170,7 @@ static struct patch_mode patch_mode_checkout_nothead = {
.prompt_mode = {
N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
+ N_("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@@ -186,6 +193,7 @@ static struct patch_mode patch_mode_worktree_head = {
.prompt_mode = {
N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
+ N_("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@@ -207,6 +215,7 @@ static struct patch_mode patch_mode_worktree_nothead = {
.prompt_mode = {
N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
+ N_("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
},
.edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
@@ -248,7 +257,7 @@ struct add_p_state {
struct hunk head;
struct hunk *hunk;
size_t hunk_nr, hunk_alloc;
- unsigned deleted:1, mode_change:1,binary:1;
+ unsigned deleted:1, added:1, mode_change:1,binary:1;
} *file_diff;
size_t file_diff_nr;
@@ -442,7 +451,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
pend = p + plain->len;
while (p != pend) {
char *eol = memchr(p, '\n', pend - p);
- const char *deleted = NULL, *mode_change = NULL;
+ const char *deleted = NULL, *added = NULL, *mode_change = NULL;
if (!eol)
eol = pend;
@@ -461,11 +470,12 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
} else if (p == plain->buf)
BUG("diff starts with unexpected line:\n"
"%.*s\n", (int)(eol - p), p);
- else if (file_diff->deleted)
+ else if (file_diff->deleted || file_diff->added)
; /* keep the rest of the file in a single "hunk" */
else if (starts_with(p, "@@ ") ||
(hunk == &file_diff->head &&
- skip_prefix(p, "deleted file", &deleted))) {
+ (skip_prefix(p, "deleted file", &deleted) ||
+ skip_prefix(p, "new file", &added)))) {
if (marker == '-' || marker == '+')
/*
* Should not happen; previous hunk did not end
@@ -485,6 +495,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
if (deleted)
file_diff->deleted = 1;
+ else if (added)
+ file_diff->added = 1;
else if (parse_hunk_header(s, hunk) < 0)
return -1;
@@ -537,8 +549,10 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
starts_with(p, "Binary files "))
file_diff->binary = 1;
- if (file_diff->deleted && file_diff->mode_change)
- BUG("diff contains delete *and* a mode change?!?\n%.*s",
+ if (!!file_diff->deleted + !!file_diff->added +
+ !!file_diff->mode_change > 1)
+ BUG("diff can only contain delete *or* add *or* a "
+ "mode change?!?\n%.*s",
(int)(eol - (plain->buf + file_diff->head.start)),
plain->buf + file_diff->head.start);
@@ -1189,7 +1203,7 @@ static int edit_hunk_loop(struct add_p_state *s,
for (;;) {
int res = edit_hunk_manually(s, hunk);
if (res == 0) {
- /* abandonded */
+ /* abandoned */
*hunk = backup;
return -1;
}
@@ -1397,6 +1411,8 @@ static int patch_update_file(struct add_p_state *s,
if (file_diff->deleted)
prompt_mode_type = PROMPT_DELETION;
+ else if (file_diff->added)
+ prompt_mode_type = PROMPT_ADDITION;
else if (file_diff->mode_change && !hunk_index)
prompt_mode_type = PROMPT_MODE_CHANGE;
else
diff --git a/alloc.c b/alloc.c
index 1c64c4d..957a0af 100644
--- a/alloc.c
+++ b/alloc.c
@@ -99,23 +99,27 @@ void *alloc_object_node(struct repository *r)
return obj;
}
-static unsigned int alloc_commit_index(struct repository *r)
+/*
+ * The returned count is to be used as an index into commit slabs,
+ * that are *NOT* maintained per repository, and that is why a single
+ * global counter is used.
+ */
+static unsigned int alloc_commit_index(void)
{
- return r->parsed_objects->commit_count++;
+ static unsigned int parsed_commits_count;
+ return parsed_commits_count++;
}
-void init_commit_node(struct repository *r, struct commit *c)
+void init_commit_node(struct commit *c)
{
c->object.type = OBJ_COMMIT;
- c->index = alloc_commit_index(r);
- c->graph_pos = COMMIT_NOT_FROM_GRAPH;
- c->generation = GENERATION_NUMBER_INFINITY;
+ c->index = alloc_commit_index();
}
void *alloc_commit_node(struct repository *r)
{
struct commit *c = alloc_node(r->parsed_objects->commit_state, sizeof(struct commit));
- init_commit_node(r, c);
+ init_commit_node(c);
return c;
}
diff --git a/alloc.h b/alloc.h
index ed1071c..371d388 100644
--- a/alloc.h
+++ b/alloc.h
@@ -9,7 +9,7 @@ struct repository;
void *alloc_blob_node(struct repository *r);
void *alloc_tree_node(struct repository *r);
-void init_commit_node(struct repository *r, struct commit *c);
+void init_commit_node(struct commit *c);
void *alloc_commit_node(struct repository *r);
void *alloc_tag_node(struct repository *r);
void *alloc_object_node(struct repository *r);
diff --git a/blame.c b/blame.c
index da7e288..82fa16d 100644
--- a/blame.c
+++ b/blame.c
@@ -1272,7 +1272,7 @@ static int maybe_changed_path(struct repository *r,
if (!bd)
return 1;
- if (origin->commit->generation == GENERATION_NUMBER_INFINITY)
+ if (commit_graph_generation(origin->commit) == GENERATION_NUMBER_INFINITY)
return 1;
filter = get_bloom_filter(r, origin->commit, 0);
diff --git a/blob.c b/blob.c
index 36f9abd..182718a 100644
--- a/blob.c
+++ b/blob.c
@@ -10,7 +10,7 @@ struct blob *lookup_blob(struct repository *r, const struct object_id *oid)
struct object *obj = lookup_object(r, oid);
if (!obj)
return create_object(r, oid, alloc_blob_node(r));
- return object_as_type(r, obj, OBJ_BLOB, 0);
+ return object_as_type(obj, OBJ_BLOB, 0);
}
int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size)
diff --git a/bloom.c b/bloom.c
index 9b86aa3..1a57322 100644
--- a/bloom.c
+++ b/bloom.c
@@ -33,15 +33,16 @@ static int load_bloom_filter_from_graph(struct commit_graph *g,
struct commit *c)
{
uint32_t lex_pos, start_index, end_index;
+ uint32_t graph_pos = commit_graph_position(c);
- while (c->graph_pos < g->num_commits_in_base)
+ while (graph_pos < g->num_commits_in_base)
g = g->base_graph;
/* The commit graph commit 'c' lives in doesn't carry bloom filters. */
if (!g->chunk_bloom_indexes)
return 0;
- lex_pos = c->graph_pos - g->num_commits_in_base;
+ lex_pos = graph_pos - g->num_commits_in_base;
end_index = get_be32(g->chunk_bloom_indexes + 4 * lex_pos);
@@ -138,6 +139,11 @@ void fill_bloom_key(const char *data,
key->hashes[i] = hash0 + i * hash1;
}
+void clear_bloom_key(struct bloom_key *key)
+{
+ FREE_AND_NULL(key->hashes);
+}
+
void add_key_to_filter(const struct bloom_key *key,
struct bloom_filter *filter,
const struct bloom_filter_settings *settings)
@@ -181,24 +187,22 @@ struct bloom_filter *get_bloom_filter(struct repository *r,
struct diff_options diffopt;
int max_changes = 512;
- if (bloom_filters.slab_size == 0)
+ if (!bloom_filters.slab_size)
return NULL;
filter = bloom_filter_slab_at(&bloom_filters, c);
if (!filter->data) {
load_commit_graph_info(r, c);
- if (c->graph_pos != COMMIT_NOT_FROM_GRAPH &&
- r->objects->commit_graph->chunk_bloom_indexes) {
- if (load_bloom_filter_from_graph(r->objects->commit_graph, filter, c))
- return filter;
- else
- return NULL;
- }
+ if (commit_graph_position(c) != COMMIT_NOT_FROM_GRAPH &&
+ r->objects->commit_graph->chunk_bloom_indexes)
+ load_bloom_filter_from_graph(r->objects->commit_graph, filter, c);
}
- if (filter->data || !compute_if_not_present)
+ if (filter->data)
return filter;
+ if (!compute_if_not_present)
+ return NULL;
repo_diff_setup(r, &diffopt);
diffopt.flags.recursive = 1;
diff --git a/bloom.h b/bloom.h
index b2a8379..d8fbb0f 100644
--- a/bloom.h
+++ b/bloom.h
@@ -72,6 +72,7 @@ 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);
void add_key_to_filter(const struct bloom_key *key,
struct bloom_filter *filter,
diff --git a/branch.c b/branch.c
index 2d9e767..7095f78 100644
--- a/branch.c
+++ b/branch.c
@@ -370,7 +370,7 @@ int replace_each_worktree_head_symref(const char *oldref, const char *newref,
const char *logmsg)
{
int ret = 0;
- struct worktree **worktrees = get_worktrees(0);
+ struct worktree **worktrees = get_worktrees();
int i;
for (i = 0; worktrees[i]; i++) {
diff --git a/bugreport.c b/bugreport.c
index aa8a489..09579e2 100644
--- a/bugreport.c
+++ b/bugreport.c
@@ -9,6 +9,7 @@
static void get_system_info(struct strbuf *sys_info)
{
struct utsname uname_info;
+ char *shell = NULL;
/* get git version from native cmd */
strbuf_addstr(sys_info, _("git version:\n"));
@@ -29,8 +30,13 @@ static void get_system_info(struct strbuf *sys_info)
strbuf_addstr(sys_info, _("compiler info: "));
get_compiler_info(sys_info);
+
strbuf_addstr(sys_info, _("libc info: "));
get_libc_info(sys_info);
+
+ shell = getenv("SHELL");
+ strbuf_addf(sys_info, "$SHELL (typically, interactive shell): %s\n",
+ shell ? shell : "<unset>");
}
static void get_populated_hooks(struct strbuf *hook_info, int nongit)
@@ -174,7 +180,9 @@ int cmd_main(int argc, const char **argv)
die(_("couldn't create a new file at '%s'"), report_path.buf);
}
- strbuf_write_fd(&buffer, report);
+ if (write_in_full(report, buffer.buf, buffer.len) < 0)
+ die_errno(_("unable to write to %s"), report_path.buf);
+
close(report);
/*
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index c1c40b5..73f9324 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -13,7 +13,6 @@ static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
-static GIT_PATH_FUNC(git_path_bisect_head, "BISECT_HEAD")
static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
static GIT_PATH_FUNC(git_path_head_name, "head-name")
static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
@@ -164,7 +163,7 @@ static int bisect_reset(const char *commit)
strbuf_addstr(&branch, commit);
}
- if (!file_exists(git_path_bisect_head())) {
+ if (!ref_exists("BISECT_HEAD")) {
struct argv_array argv = ARGV_ARRAY_INIT;
argv_array_pushl(&argv, "checkout", branch.buf, "--", NULL);
@@ -455,9 +454,12 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
no_checkout = 1;
} else if (!strcmp(arg, "--term-good") ||
!strcmp(arg, "--term-old")) {
+ i++;
+ if (argc <= i)
+ return error(_("'' is not a valid term"));
must_write_terms = 1;
free((void *) terms->term_good);
- terms->term_good = xstrdup(argv[++i]);
+ terms->term_good = xstrdup(argv[i]);
} else if (skip_prefix(arg, "--term-good=", &arg) ||
skip_prefix(arg, "--term-old=", &arg)) {
must_write_terms = 1;
@@ -465,16 +467,18 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
terms->term_good = xstrdup(arg);
} else if (!strcmp(arg, "--term-bad") ||
!strcmp(arg, "--term-new")) {
+ i++;
+ if (argc <= i)
+ return error(_("'' is not a valid term"));
must_write_terms = 1;
free((void *) terms->term_bad);
- terms->term_bad = xstrdup(argv[++i]);
+ terms->term_bad = xstrdup(argv[i]);
} else if (skip_prefix(arg, "--term-bad=", &arg) ||
skip_prefix(arg, "--term-new=", &arg)) {
must_write_terms = 1;
free((void *) terms->term_bad);
terms->term_bad = xstrdup(arg);
- } else if (starts_with(arg, "--") &&
- !one_of(arg, "--term-good", "--term-bad", NULL)) {
+ } else if (starts_with(arg, "--")) {
return error(_("unrecognized option: '%s'"), arg);
} else {
char *commit_id = xstrfmt("%s^{commit}", arg);
diff --git a/builtin/branch.c b/builtin/branch.c
index accb61b..e82301f 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -468,7 +468,7 @@ static void print_current_branch_name(void)
static void reject_rebase_or_bisect_branch(const char *target)
{
- struct worktree **worktrees = get_worktrees(0);
+ struct worktree **worktrees = get_worktrees();
int i;
for (i = 0; worktrees[i]; i++) {
@@ -693,7 +693,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
list = 1;
if (!!delete + !!rename + !!copy + !!new_upstream + !!show_current +
- list + unset_upstream > 1)
+ list + edit_description + unset_upstream > 1)
usage_with_options(builtin_branch_usage, options);
if (filter.abbrev == -1)
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index ae18e20..5ebf133 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -596,7 +596,7 @@ static int batch_objects(struct batch_options *opt)
static const char * const cat_file_usage[] = {
N_("git cat-file (-t [--allow-unknown-type] | -s [--allow-unknown-type] | -e | -p | <type> | --textconv | --filters) [--path=<path>] <object>"),
- N_("git cat-file (--batch | --batch-check) [--follow-symlinks] [--textconv | --filters]"),
+ N_("git cat-file (--batch[=<format>] | --batch-check[=<format>]) [--follow-symlinks] [--textconv | --filters]"),
NULL
};
diff --git a/builtin/checkout.c b/builtin/checkout.c
index e9d111b..af849c6 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -621,9 +621,7 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
opts.src_index = &the_index;
opts.dst_index = &the_index;
init_checkout_metadata(&opts.meta, info->refname,
- info->commit ? &info->commit->object.oid :
- is_null_oid(&info->oid) ? &tree->object.oid :
- &info->oid,
+ info->commit ? &info->commit->object.oid : &null_oid,
NULL);
parse_tree(tree);
init_tree_desc(&tree_desc, tree->buffer, tree->size);
@@ -1689,7 +1687,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
* Try to give more helpful suggestion.
* new_branch && argc > 1 will be caught later.
*/
- if (opts->new_branch && argc == 1)
+ if (opts->new_branch && argc == 1 && !new_branch_info.commit)
die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
argv[0], opts->new_branch);
diff --git a/builtin/clean.c b/builtin/clean.c
index 4ca12bc..5a9c29a 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -924,12 +924,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
0);
memset(&dir, 0, sizeof(dir));
- if (ignored_only)
- dir.flags |= DIR_SHOW_IGNORED;
-
- if (ignored && ignored_only)
- die(_("-x and -X cannot be used together"));
-
if (!interactive && !dry_run && !force) {
if (config_set)
die(_("clean.requireForce set to true and neither -i, -n, nor -f given; "
@@ -946,6 +940,13 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
+ if (ignored && ignored_only)
+ die(_("-x and -X cannot be used together"));
+ if (!ignored)
+ setup_standard_excludes(&dir);
+ if (ignored_only)
+ dir.flags |= DIR_SHOW_IGNORED;
+
if (argc) {
/*
* Remaining args implies pathspecs specified, and we should
@@ -954,15 +955,41 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
remove_directories = 1;
}
- if (remove_directories)
- dir.flags |= DIR_SHOW_IGNORED_TOO | DIR_KEEP_UNTRACKED_CONTENTS;
+ if (remove_directories && !ignored_only) {
+ /*
+ * We need to know about ignored files too:
+ *
+ * If (ignored), then we will delete ignored files as well.
+ *
+ * If (!ignored), then even though we not are doing
+ * anything with ignored files, we need to know about them
+ * so that we can avoid deleting a directory of untracked
+ * files that also contains an ignored file within it.
+ *
+ * For the (!ignored) case, since we only need to avoid
+ * deleting ignored files, we can set
+ * DIR_SHOW_IGNORED_TOO_MODE_MATCHING in order to avoid
+ * recursing into a directory which is itself ignored.
+ */
+ dir.flags |= DIR_SHOW_IGNORED_TOO;
+ if (!ignored)
+ dir.flags |= DIR_SHOW_IGNORED_TOO_MODE_MATCHING;
+
+ /*
+ * Let the fill_directory() machinery know that we aren't
+ * just recursing to collect the ignored files; we want all
+ * the untracked ones so that we can delete them. (Note:
+ * we could also set DIR_KEEP_UNTRACKED_CONTENTS when
+ * ignored_only is true, since DIR_KEEP_UNTRACKED_CONTENTS
+ * only has effect in combination with DIR_SHOW_IGNORED_TOO. It makes
+ * the code clearer to exclude it, though.
+ */
+ dir.flags |= DIR_KEEP_UNTRACKED_CONTENTS;
+ }
if (read_cache() < 0)
die(_("index file corrupt"));
- if (!ignored)
- setup_standard_excludes(&dir);
-
pl = add_pattern_list(&dir, EXC_CMDL, "--exclude option");
for (i = 0; i < exclude_list.nr; i++)
add_pattern(exclude_list.items[i].string, "", 0, pl, -(i+1));
diff --git a/builtin/clone.c b/builtin/clone.c
index cb48a29..a9f3312 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -945,8 +945,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
{
int is_bundle = 0, is_local;
const char *repo_name, *repo, *work_tree, *git_dir;
- char *path, *dir;
- int dest_exists;
+ char *path, *dir, *display_repo = NULL;
+ int dest_exists, real_dest_exists = 0;
const struct ref *refs, *remote_head;
const struct ref *remote_head_points_at;
const struct ref *our_head_points_at;
@@ -1000,10 +1000,11 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
path = get_repo_path(repo_name, &is_bundle);
if (path)
repo = absolute_pathdup(repo_name);
- else if (!strchr(repo_name, ':'))
- die(_("repository '%s' does not exist"), repo_name);
- else
+ else if (strchr(repo_name, ':')) {
repo = repo_name;
+ display_repo = transport_anonymize_url(repo);
+ } else
+ die(_("repository '%s' does not exist"), repo_name);
/* no need to be strict, transport_set_option() will validate it again */
if (option_depth && atoi(option_depth) < 1)
@@ -1020,7 +1021,17 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
die(_("destination path '%s' already exists and is not "
"an empty directory."), dir);
- strbuf_addf(&reflog_msg, "clone: from %s", repo);
+ if (real_git_dir) {
+ real_dest_exists = path_exists(real_git_dir);
+ if (real_dest_exists && !is_empty_dir(real_git_dir))
+ die(_("repository path '%s' already exists and is not "
+ "an empty directory."), real_git_dir);
+ }
+
+
+ strbuf_addf(&reflog_msg, "clone: from %s",
+ display_repo ? display_repo : repo);
+ free(display_repo);
if (option_bare)
work_tree = NULL;
@@ -1054,7 +1065,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
}
if (real_git_dir) {
- if (path_exists(real_git_dir))
+ if (real_dest_exists)
junk_git_dir_flags |= REMOVE_DIR_KEEP_TOPLEVEL;
junk_git_dir = real_git_dir;
} else {
@@ -1108,7 +1119,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
}
}
- init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, INIT_DB_QUIET);
+ init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, NULL,
+ INIT_DB_QUIET);
if (real_git_dir)
git_dir = real_git_dir;
@@ -1217,6 +1229,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
refs = transport_get_remote_refs(transport, &ref_prefixes);
if (refs) {
+ int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
+
+ /*
+ * Now that we know what algorithm the remote side is using,
+ * let's set ours to the same thing.
+ */
+ initialize_repository_version(hash_algo);
+ repo_set_hash_algo(the_repository, hash_algo);
+
mapped_refs = wanted_peer_refs(refs, &remote->fetch);
/*
* transport_get_remote_refs() may return refs with null sha-1
@@ -1263,9 +1284,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
remote_head_points_at = NULL;
remote_head = NULL;
option_no_checkout = 1;
- if (!option_bare)
- install_branch_config(0, "master", option_origin,
- "refs/heads/master");
+ if (!option_bare) {
+ const char *branch = git_default_branch_name();
+ char *ref = xstrfmt("refs/heads/%s", branch);
+
+ install_branch_config(0, branch, option_origin, ref);
+ free(ref);
+ }
}
write_refspec_config(src_ref_prefix, our_head_points_at,
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index 15fe603..523501f 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -6,6 +6,8 @@
#include "repository.h"
#include "commit-graph.h"
#include "object-store.h"
+#include "progress.h"
+#include "tag.h"
static char const * const builtin_commit_graph_usage[] = {
N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"),
@@ -138,14 +140,37 @@ static int write_option_parse_split(const struct option *opt, const char *arg,
return 0;
}
+static int read_one_commit(struct oidset *commits, struct progress *progress,
+ const char *hash)
+{
+ struct object *result;
+ struct object_id oid;
+ const char *end;
+
+ if (parse_oid_hex(hash, &oid, &end))
+ return error(_("unexpected non-hex object ID: %s"), hash);
+
+ result = deref_tag(the_repository, parse_object(the_repository, &oid),
+ NULL, 0);
+ if (!result)
+ return error(_("invalid object: %s"), hash);
+ else if (object_as_type(result, OBJ_COMMIT, 1))
+ oidset_insert(commits, &result->oid);
+
+ display_progress(progress, oidset_size(commits));
+
+ return 0;
+}
+
static int graph_write(int argc, const char **argv)
{
- struct string_list *pack_indexes = NULL;
+ struct string_list pack_indexes = STRING_LIST_INIT_NODUP;
+ struct strbuf buf = STRBUF_INIT;
struct oidset commits = OIDSET_INIT;
struct object_directory *odb = NULL;
- struct string_list lines;
int result = 0;
enum commit_graph_write_flags flags = 0;
+ struct progress *progress = NULL;
static struct option builtin_commit_graph_write_options[] = {
OPT_STRING(0, "object-dir", &opts.obj_dir,
@@ -176,6 +201,7 @@ static int graph_write(int argc, const char **argv)
};
opts.progress = isatty(2);
+ opts.enable_changed_paths = -1;
split_opts.size_multiple = 2;
split_opts.max_commits = 0;
split_opts.expire_time = 0;
@@ -196,7 +222,9 @@ static int graph_write(int argc, const char **argv)
flags |= COMMIT_GRAPH_WRITE_SPLIT;
if (opts.progress)
flags |= COMMIT_GRAPH_WRITE_PROGRESS;
- if (opts.enable_changed_paths ||
+ if (!opts.enable_changed_paths)
+ flags |= COMMIT_GRAPH_NO_WRITE_BLOOM_FILTERS;
+ if (opts.enable_changed_paths == 1 ||
git_env_bool(GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS, 0))
flags |= COMMIT_GRAPH_WRITE_BLOOM_FILTERS;
@@ -209,44 +237,36 @@ static int graph_write(int argc, const char **argv)
return 0;
}
- string_list_init(&lines, 0);
- if (opts.stdin_packs || opts.stdin_commits) {
- struct strbuf buf = STRBUF_INIT;
-
+ if (opts.stdin_packs) {
while (strbuf_getline(&buf, stdin) != EOF)
- string_list_append(&lines, strbuf_detach(&buf, NULL));
-
- if (opts.stdin_packs)
- pack_indexes = &lines;
- if (opts.stdin_commits) {
- struct string_list_item *item;
- oidset_init(&commits, lines.nr);
- for_each_string_list_item(item, &lines) {
- struct object_id oid;
- const char *end;
-
- if (parse_oid_hex(item->string, &oid, &end)) {
- error(_("unexpected non-hex object ID: "
- "%s"), item->string);
- return 1;
- }
-
- oidset_insert(&commits, &oid);
+ string_list_append(&pack_indexes,
+ strbuf_detach(&buf, NULL));
+ } else if (opts.stdin_commits) {
+ oidset_init(&commits, 0);
+ if (opts.progress)
+ progress = start_delayed_progress(
+ _("Collecting commits from input"), 0);
+
+ while (strbuf_getline(&buf, stdin) != EOF) {
+ if (read_one_commit(&commits, progress, buf.buf)) {
+ result = 1;
+ goto cleanup;
}
- flags |= COMMIT_GRAPH_WRITE_CHECK_OIDS;
}
- UNLEAK(buf);
+ stop_progress(&progress);
}
if (write_commit_graph(odb,
- pack_indexes,
+ opts.stdin_packs ? &pack_indexes : NULL,
opts.stdin_commits ? &commits : NULL,
flags,
&split_opts))
result = 1;
- UNLEAK(lines);
+cleanup:
+ string_list_clear(&pack_indexes, 0);
+ strbuf_release(&buf);
return result;
}
diff --git a/builtin/config.c b/builtin/config.c
index ee4aef6..5e39f61 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -672,7 +672,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
given_config_source.file = git_pathdup("config");
given_config_source.scope = CONFIG_SCOPE_LOCAL;
} else if (use_worktree_config) {
- struct worktree **worktrees = get_worktrees(0);
+ struct worktree **worktrees = get_worktrees();
if (repository_format_worktree_config)
given_config_source.file = git_pathdup("config.worktree");
else if (worktrees[0] && worktrees[1])
diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index 86ae474..1e352dd 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -28,6 +28,13 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
repo_init_revisions(the_repository, &rev, prefix);
rev.abbrev = 0;
+
+ /*
+ * Consider "intent-to-add" files as new by default, unless
+ * explicitly specified in the command line or anywhere else.
+ */
+ rev.diffopt.ita_invisible_in_index = 1;
+
precompose_argv(argc, argv);
argc = setup_revisions(argc, argv, &rev, NULL);
diff --git a/builtin/diff.c b/builtin/diff.c
index 8537b17..cb98811 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -6,6 +6,7 @@
#define USE_THE_INDEX_COMPATIBILITY_MACROS
#include "cache.h"
#include "config.h"
+#include "ewah/ewok.h"
#include "lockfile.h"
#include "color.h"
#include "commit.h"
@@ -23,7 +24,13 @@
#define DIFF_NO_INDEX_IMPLICIT 2
static const char builtin_diff_usage[] =
-"git diff [<options>] [<commit> [<commit>]] [--] [<path>...]";
+"git diff [<options>] [<commit>] [--] [<path>...]\n"
+" or: git diff [<options>] --cached [<commit>] [--] [<path>...]\n"
+" or: git diff [<options>] <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>]\n"
+COMMON_DIFF_OPTIONS_HELP;
static const char *blob_path(struct object_array_entry *entry)
{
@@ -254,6 +261,108 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv
return run_diff_files(revs, options);
}
+struct symdiff {
+ struct bitmap *skip;
+ int warn;
+ const char *base, *left, *right;
+};
+
+/*
+ * Check for symmetric-difference arguments, and if present, arrange
+ * everything we need to know to handle them correctly. As a bonus,
+ * weed out all bogus range-based revision specifications, e.g.,
+ * "git diff A..B C..D" or "git diff A..B C" get rejected.
+ *
+ * For an actual symmetric diff, *symdiff is set this way:
+ *
+ * - its skip is non-NULL and marks *all* rev->pending.objects[i]
+ * indices that the caller should ignore (extra merge bases, of
+ * which there might be many, and A in A...B). Note that the
+ * chosen merge base and right side are NOT marked.
+ * - warn is set if there are multiple merge bases.
+ * - base, left, and right point to the names to use in a
+ * warning about multiple merge bases.
+ *
+ * If there is no symmetric diff argument, sym->skip is NULL and
+ * sym->warn is cleared. The remaining fields are not set.
+ */
+static void symdiff_prepare(struct rev_info *rev, struct symdiff *sym)
+{
+ int i, is_symdiff = 0, basecount = 0, othercount = 0;
+ int lpos = -1, rpos = -1, basepos = -1;
+ struct bitmap *map = NULL;
+
+ /*
+ * Use the whence fields to find merge bases and left and
+ * right parts of symmetric difference, so that we do not
+ * depend on the order that revisions are parsed. If there
+ * are any revs that aren't from these sources, we have a
+ * "git diff C A...B" or "git diff A...B C" case. Or we
+ * could even get "git diff A...B C...E", for instance.
+ *
+ * If we don't have just one merge base, we pick one
+ * at random.
+ *
+ * NB: REV_CMD_LEFT, REV_CMD_RIGHT are also used for A..B,
+ * so we must check for SYMMETRIC_LEFT too. The two arrays
+ * rev->pending.objects and rev->cmdline.rev are parallel.
+ */
+ for (i = 0; i < rev->cmdline.nr; i++) {
+ struct object *obj = rev->pending.objects[i].item;
+ switch (rev->cmdline.rev[i].whence) {
+ case REV_CMD_MERGE_BASE:
+ if (basepos < 0)
+ basepos = i;
+ basecount++;
+ break; /* do mark all bases */
+ case REV_CMD_LEFT:
+ if (lpos >= 0)
+ usage(builtin_diff_usage);
+ lpos = i;
+ if (obj->flags & SYMMETRIC_LEFT) {
+ is_symdiff = 1;
+ break; /* do mark A */
+ }
+ continue;
+ case REV_CMD_RIGHT:
+ if (rpos >= 0)
+ usage(builtin_diff_usage);
+ rpos = i;
+ continue; /* don't mark B */
+ case REV_CMD_PARENTS_ONLY:
+ case REV_CMD_REF:
+ case REV_CMD_REV:
+ othercount++;
+ continue;
+ }
+ if (map == NULL)
+ map = bitmap_new();
+ bitmap_set(map, i);
+ }
+
+ /*
+ * Forbid any additional revs for both A...B and A..B.
+ */
+ if (lpos >= 0 && othercount > 0)
+ usage(builtin_diff_usage);
+
+ if (!is_symdiff) {
+ bitmap_free(map);
+ sym->warn = 0;
+ sym->skip = NULL;
+ return;
+ }
+
+ sym->left = rev->pending.objects[lpos].name;
+ sym->right = rev->pending.objects[rpos].name;
+ if (basecount == 0)
+ die(_("%s...%s: no merge base"), sym->left, sym->right);
+ sym->base = rev->pending.objects[basepos].name;
+ bitmap_unset(map, basepos); /* unmark the base we want */
+ sym->warn = basecount > 1;
+ sym->skip = map;
+}
+
int cmd_diff(int argc, const char **argv, const char *prefix)
{
int i;
@@ -263,19 +372,29 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
struct object_array_entry *blob[2];
int nongit = 0, no_index = 0;
int result = 0;
+ struct symdiff sdiff;
/*
* We could get N tree-ish in the rev.pending_objects list.
- * Also there could be M blobs there, and P pathspecs.
+ * Also there could be M blobs there, and P pathspecs. --cached may
+ * also be present.
*
* N=0, M=0:
- * cache vs files (diff-files)
+ * cache vs files (diff-files)
+ *
+ * N=0, M=0, --cached:
+ * HEAD vs cache (diff-index --cached)
+ *
* N=0, M=2:
* compare two random blobs. P must be zero.
+ *
* N=0, M=1, P=1:
- * compare a blob with a working tree file.
+ * compare a blob with a working tree file.
*
* N=1, M=0:
+ * tree vs files (diff-index)
+ *
+ * N=1, M=0, --cached:
* tree vs cache (diff-index --cached)
*
* N=2, M=0:
@@ -382,6 +501,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
}
}
+ symdiff_prepare(&rev, &sdiff);
for (i = 0; i < rev.pending.nr; i++) {
struct object_array_entry *entry = &rev.pending.objects[i];
struct object *obj = entry->item;
@@ -396,6 +516,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
obj = &get_commit_tree(((struct commit *)obj))->object;
if (obj->type == OBJ_TREE) {
+ if (sdiff.skip && bitmap_get(sdiff.skip, i))
+ continue;
obj->flags |= flags;
add_object_array(obj, name, &ent);
} else if (obj->type == OBJ_BLOB) {
@@ -437,21 +559,12 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
usage(builtin_diff_usage);
else if (ent.nr == 1)
result = builtin_diff_index(&rev, argc, argv);
- else if (ent.nr == 2)
+ else if (ent.nr == 2) {
+ if (sdiff.warn)
+ warning(_("%s...%s: multiple merge bases, using %s"),
+ sdiff.left, sdiff.right, sdiff.base);
result = builtin_diff_tree(&rev, argc, argv,
&ent.objects[0], &ent.objects[1]);
- else if (ent.objects[0].item->flags & UNINTERESTING) {
- /*
- * diff A...B where there is at least one merge base
- * between A and B. We have ent.objects[0] ==
- * merge-base, ent.objects[ents-2] == A, and
- * ent.objects[ents-1] == B. Show diff between the
- * base and B. Note that we pick one merge base at
- * random if there are more than one.
- */
- result = builtin_diff_tree(&rev, argc, argv,
- &ent.objects[0],
- &ent.objects[ent.nr-1]);
} else
result = builtin_diff_combined(&rev, argc, argv,
ent.objects, ent.nr);
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 8586816..9f37895 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -45,6 +45,7 @@ static struct string_list extra_refs = STRING_LIST_INIT_NODUP;
static struct string_list tag_refs = STRING_LIST_INIT_NODUP;
static struct refspec refspecs = REFSPEC_INIT_FETCH;
static int anonymize;
+static struct hashmap anonymized_seeds;
static struct revision_sources revision_sources;
static int parse_opt_signed_tag_mode(const struct option *opt,
@@ -120,24 +121,33 @@ static int has_unshown_parent(struct commit *commit)
struct anonymized_entry {
struct hashmap_entry hash;
+ const char *anon;
+ const char orig[FLEX_ARRAY];
+};
+
+struct anonymized_entry_key {
+ struct hashmap_entry hash;
const char *orig;
size_t orig_len;
- const char *anon;
- size_t anon_len;
};
static int anonymized_entry_cmp(const void *unused_cmp_data,
const struct hashmap_entry *eptr,
const struct hashmap_entry *entry_or_key,
- const void *unused_keydata)
+ const void *keydata)
{
const struct anonymized_entry *a, *b;
a = container_of(eptr, const struct anonymized_entry, hash);
- b = container_of(entry_or_key, const struct anonymized_entry, hash);
+ if (keydata) {
+ const struct anonymized_entry_key *key = keydata;
+ int equal = !strncmp(a->orig, key->orig, key->orig_len) &&
+ !a->orig[key->orig_len];
+ return !equal;
+ }
- return a->orig_len != b->orig_len ||
- memcmp(a->orig, b->orig, a->orig_len);
+ b = container_of(entry_or_key, const struct anonymized_entry, hash);
+ return strcmp(a->orig, b->orig);
}
/*
@@ -145,31 +155,39 @@ static int anonymized_entry_cmp(const void *unused_cmp_data,
* the same anonymized string with another. The actual generation
* is farmed out to the generate function.
*/
-static const void *anonymize_mem(struct hashmap *map,
- void *(*generate)(const void *, size_t *),
- const void *orig, size_t *len)
+static const char *anonymize_str(struct hashmap *map,
+ char *(*generate)(void *),
+ const char *orig, size_t len,
+ void *data)
{
- struct anonymized_entry key, *ret;
+ struct anonymized_entry_key key;
+ struct anonymized_entry *ret;
if (!map->cmpfn)
hashmap_init(map, anonymized_entry_cmp, NULL, 0);
- hashmap_entry_init(&key.hash, memhash(orig, *len));
+ hashmap_entry_init(&key.hash, memhash(orig, len));
key.orig = orig;
- key.orig_len = *len;
- ret = hashmap_get_entry(map, &key, hash, NULL);
+ key.orig_len = len;
+
+ /* First check if it's a token the user configured manually... */
+ if (anonymized_seeds.cmpfn)
+ ret = hashmap_get_entry(&anonymized_seeds, &key, hash, &key);
+ else
+ ret = NULL;
+
+ /* ...otherwise check if we've already seen it in this context... */
+ if (!ret)
+ ret = hashmap_get_entry(map, &key, hash, &key);
+ /* ...and finally generate a new mapping if necessary */
if (!ret) {
- ret = xmalloc(sizeof(*ret));
+ FLEX_ALLOC_MEM(ret, orig, orig, len);
hashmap_entry_init(&ret->hash, key.hash.hash);
- ret->orig = xstrdup(orig);
- ret->orig_len = *len;
- ret->anon = generate(orig, len);
- ret->anon_len = *len;
+ ret->anon = generate(data);
hashmap_put(map, &ret->hash);
}
- *len = ret->anon_len;
return ret->anon;
}
@@ -181,13 +199,13 @@ static const void *anonymize_mem(struct hashmap *map,
*/
static void anonymize_path(struct strbuf *out, const char *path,
struct hashmap *map,
- void *(*generate)(const void *, size_t *))
+ char *(*generate)(void *))
{
while (*path) {
const char *end_of_component = strchrnul(path, '/');
size_t len = end_of_component - path;
- const char *c = anonymize_mem(map, generate, path, &len);
- strbuf_add(out, c, len);
+ const char *c = anonymize_str(map, generate, path, len, NULL);
+ strbuf_addstr(out, c);
path = end_of_component;
if (*path)
strbuf_addch(out, *path++);
@@ -361,12 +379,12 @@ static void print_path_1(const char *path)
printf("%s", path);
}
-static void *anonymize_path_component(const void *path, size_t *len)
+static char *anonymize_path_component(void *data)
{
static int counter;
struct strbuf out = STRBUF_INIT;
strbuf_addf(&out, "path%d", counter++);
- return strbuf_detach(&out, len);
+ return strbuf_detach(&out, NULL);
}
static void print_path(const char *path)
@@ -383,20 +401,23 @@ static void print_path(const char *path)
}
}
-static void *generate_fake_oid(const void *old, size_t *len)
+static char *generate_fake_oid(void *data)
{
static uint32_t counter = 1; /* avoid null oid */
const unsigned hashsz = the_hash_algo->rawsz;
- unsigned char *out = xcalloc(hashsz, 1);
+ unsigned char out[GIT_MAX_RAWSZ];
+ char *hex = xmallocz(GIT_MAX_HEXSZ);
+
+ hashclr(out);
put_be32(out + hashsz - 4, counter++);
- return out;
+ return hash_to_hex_algop_r(hex, out, the_hash_algo);
}
-static const struct object_id *anonymize_oid(const struct object_id *oid)
+static const char *anonymize_oid(const char *oid_hex)
{
static struct hashmap objs;
- size_t len = the_hash_algo->rawsz;
- return anonymize_mem(&objs, generate_fake_oid, oid, &len);
+ size_t len = strlen(oid_hex);
+ return anonymize_str(&objs, generate_fake_oid, oid_hex, len, NULL);
}
static void show_filemodify(struct diff_queue_struct *q,
@@ -455,9 +476,9 @@ static void show_filemodify(struct diff_queue_struct *q,
*/
if (no_data || S_ISGITLINK(spec->mode))
printf("M %06o %s ", spec->mode,
- oid_to_hex(anonymize ?
- anonymize_oid(&spec->oid) :
- &spec->oid));
+ anonymize ?
+ anonymize_oid(oid_to_hex(&spec->oid)) :
+ oid_to_hex(&spec->oid));
else {
struct object *object = lookup_object(the_repository,
&spec->oid);
@@ -493,12 +514,12 @@ static const char *find_encoding(const char *begin, const char *end)
return bol;
}
-static void *anonymize_ref_component(const void *old, size_t *len)
+static char *anonymize_ref_component(void *data)
{
static int counter;
struct strbuf out = STRBUF_INIT;
strbuf_addf(&out, "ref%d", counter++);
- return strbuf_detach(&out, len);
+ return strbuf_detach(&out, NULL);
}
static const char *anonymize_refname(const char *refname)
@@ -517,13 +538,6 @@ static const char *anonymize_refname(const char *refname)
static struct strbuf anon = STRBUF_INIT;
int i;
- /*
- * We also leave "master" as a special case, since it does not reveal
- * anything interesting.
- */
- if (!strcmp(refname, "refs/heads/master"))
- return refname;
-
strbuf_reset(&anon);
for (i = 0; i < ARRAY_SIZE(prefixes); i++) {
if (skip_prefix(refname, prefixes[i], &refname)) {
@@ -546,14 +560,13 @@ static char *anonymize_commit_message(const char *old)
return xstrfmt("subject %d\n\nbody\n", counter++);
}
-static struct hashmap idents;
-static void *anonymize_ident(const void *old, size_t *len)
+static char *anonymize_ident(void *data)
{
static int counter;
struct strbuf out = STRBUF_INIT;
strbuf_addf(&out, "User %d <user%d@example.com>", counter, counter);
counter++;
- return strbuf_detach(&out, len);
+ return strbuf_detach(&out, NULL);
}
/*
@@ -563,6 +576,7 @@ static void *anonymize_ident(const void *old, size_t *len)
*/
static void anonymize_ident_line(const char **beg, const char **end)
{
+ static struct hashmap idents;
static struct strbuf buffers[] = { STRBUF_INIT, STRBUF_INIT };
static unsigned which_buffer;
@@ -588,9 +602,9 @@ static void anonymize_ident_line(const char **beg, const char **end)
size_t len;
len = split.mail_end - split.name_begin;
- ident = anonymize_mem(&idents, anonymize_ident,
- split.name_begin, &len);
- strbuf_add(out, ident, len);
+ ident = anonymize_str(&idents, anonymize_ident,
+ split.name_begin, len, NULL);
+ strbuf_addstr(out, ident);
strbuf_addch(out, ' ');
strbuf_add(out, split.date_begin, split.tz_end - split.date_begin);
} else {
@@ -712,9 +726,10 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
if (mark)
printf(":%d\n", mark);
else
- printf("%s\n", oid_to_hex(anonymize ?
- anonymize_oid(&obj->oid) :
- &obj->oid));
+ printf("%s\n",
+ anonymize ?
+ anonymize_oid(oid_to_hex(&obj->oid)) :
+ oid_to_hex(&obj->oid));
i++;
}
@@ -729,12 +744,12 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
show_progress();
}
-static void *anonymize_tag(const void *old, size_t *len)
+static char *anonymize_tag(void *data)
{
static int counter;
struct strbuf out = STRBUF_INIT;
strbuf_addf(&out, "tag message %d", counter++);
- return strbuf_detach(&out, len);
+ return strbuf_detach(&out, NULL);
}
static void handle_tail(struct object_array *commits, struct rev_info *revs,
@@ -804,8 +819,8 @@ static void handle_tag(const char *name, struct tag *tag)
name = anonymize_refname(name);
if (message) {
static struct hashmap tags;
- message = anonymize_mem(&tags, anonymize_tag,
- message, &message_size);
+ message = anonymize_str(&tags, anonymize_tag,
+ message, message_size, NULL);
}
}
@@ -1136,6 +1151,37 @@ static void handle_deletes(void)
}
}
+static char *anonymize_seed(void *data)
+{
+ return xstrdup(data);
+}
+
+static int parse_opt_anonymize_map(const struct option *opt,
+ const char *arg, int unset)
+{
+ struct hashmap *map = opt->value;
+ const char *delim, *value;
+ size_t keylen;
+
+ BUG_ON_OPT_NEG(unset);
+
+ delim = strchr(arg, ':');
+ if (delim) {
+ keylen = delim - arg;
+ value = delim + 1;
+ } else {
+ keylen = strlen(arg);
+ value = arg;
+ }
+
+ if (!keylen || !*value)
+ return error(_("--anonymize-map token cannot be empty"));
+
+ anonymize_str(map, anonymize_seed, arg, keylen, (void *)value);
+
+ return 0;
+}
+
int cmd_fast_export(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
@@ -1177,6 +1223,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
OPT_STRING_LIST(0, "refspec", &refspecs_list, N_("refspec"),
N_("Apply refspec to exported refs")),
OPT_BOOL(0, "anonymize", &anonymize, N_("anonymize output")),
+ OPT_CALLBACK_F(0, "anonymize-map", &anonymized_seeds, N_("from:to"),
+ N_("convert <from> to <to> in anonymized output"),
+ PARSE_OPT_NONEG, parse_opt_anonymize_map),
OPT_BOOL(0, "reference-excluded-parents",
&reference_excluded_commits, N_("Reference parents which are not in fast-export stream by object id")),
OPT_BOOL(0, "show-original-ids", &show_original_ids,
@@ -1204,6 +1253,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
if (argc > 1)
usage_with_options (fast_export_usage, options);
+ if (anonymized_seeds.cmpfn && !anonymize)
+ die(_("--anonymize-map without --anonymize does not make sense"));
+
if (refspecs_list.nr) {
int i;
diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c
index 4771100..bbb5c96 100644
--- a/builtin/fetch-pack.c
+++ b/builtin/fetch-pack.c
@@ -48,8 +48,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
struct ref **sought = NULL;
int nr_sought = 0, alloc_sought = 0;
int fd[2];
- char *pack_lockfile = NULL;
- char **pack_lockfile_ptr = NULL;
+ struct string_list pack_lockfiles = STRING_LIST_INIT_DUP;
+ struct string_list *pack_lockfiles_ptr = NULL;
struct child_process *conn;
struct fetch_pack_args args;
struct oid_array shallow = OID_ARRAY_INIT;
@@ -134,7 +134,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
}
if (!strcmp("--lock-pack", arg)) {
args.lock_pack = 1;
- pack_lockfile_ptr = &pack_lockfile;
+ pack_lockfiles_ptr = &pack_lockfiles;
continue;
}
if (!strcmp("--check-self-contained-and-connected", arg)) {
@@ -224,7 +224,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
version = discover_version(&reader);
switch (version) {
case protocol_v2:
- get_remote_refs(fd[1], &reader, &ref, 0, NULL, NULL);
+ get_remote_refs(fd[1], &reader, &ref, 0, NULL, NULL, args.stateless_rpc);
break;
case protocol_v1:
case protocol_v0:
@@ -235,10 +235,15 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
}
ref = fetch_pack(&args, fd, ref, sought, nr_sought,
- &shallow, pack_lockfile_ptr, version);
- if (pack_lockfile) {
- printf("lock %s\n", pack_lockfile);
+ &shallow, pack_lockfiles_ptr, version);
+ if (pack_lockfiles.nr) {
+ int i;
+
+ printf("lock %s\n", pack_lockfiles.items[0].string);
fflush(stdout);
+ for (i = 1; i < pack_lockfiles.nr; i++)
+ warning(_("Lockfile created but not reported: %s"),
+ pack_lockfiles.items[i].string);
}
if (args.check_self_contained_and_connected &&
args.self_contained_and_connected) {
diff --git a/builtin/fetch.c b/builtin/fetch.c
index b5788c1..82ac4be 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -1758,8 +1758,13 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
/* Record the command line for the reflog */
strbuf_addstr(&default_rla, "fetch");
- for (i = 1; i < argc; i++)
- strbuf_addf(&default_rla, " %s", argv[i]);
+ for (i = 1; i < argc; i++) {
+ /* This handles non-URLs gracefully */
+ char *anon = transport_anonymize_url(argv[i]);
+
+ strbuf_addf(&default_rla, " %s", anon);
+ free(anon);
+ }
fetch_config_from_gitmodules(&submodule_fetch_jobs_config,
&recurse_submodules);
@@ -1790,9 +1795,6 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
if (depth || deepen_since || deepen_not.nr)
deepen = 1;
- if (filter_options.choice && !has_promisor_remote())
- die("--filter can only be used when extensions.partialClone is set");
-
if (all) {
if (argc == 1)
die(_("fetch --all does not take a repository argument"));
diff --git a/builtin/fsck.c b/builtin/fsck.c
index f02cbdb..37aa07d 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -241,7 +241,7 @@ static void mark_unreachable_referents(const struct object_id *oid)
enum object_type type = oid_object_info(the_repository,
&obj->oid, NULL);
if (type > 0)
- object_as_type(the_repository, obj, type, 0);
+ object_as_type(obj, type, 0);
}
options.walk = mark_used;
@@ -577,7 +577,7 @@ static void get_default_heads(void)
for_each_rawref(fsck_handle_ref, NULL);
- worktrees = get_worktrees(0);
+ worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
struct worktree *wt = *p;
struct strbuf ref = STRBUF_INIT;
diff --git a/builtin/grep.c b/builtin/grep.c
index a5056f3..5975cf5 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -466,7 +466,7 @@ static int grep_submodule(struct grep_opt *opt,
struct strbuf base = STRBUF_INIT;
obj_read_lock();
- object = parse_object_or_die(oid, oid_to_hex(oid));
+ object = parse_object_or_die(oid, NULL);
obj_read_unlock();
data = read_object_with_reference(&subrepo,
&object->oid, tree_type,
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index f176dd2..f865666 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -1555,13 +1555,9 @@ static void read_v2_anomalous_offsets(struct packed_git *p,
{
const uint32_t *idx1, *idx2;
uint32_t i;
- const uint32_t hashwords = the_hash_algo->rawsz / sizeof(uint32_t);
/* The address of the 4-byte offset table */
- idx1 = (((const uint32_t *)p->index_data)
- + 2 /* 8-byte header */
- + 256 /* fan out */
- + hashwords * p->num_objects /* object ID table */
+ idx1 = (((const uint32_t *)((const uint8_t *)p->index_data + p->crc_offset))
+ p->num_objects /* CRC32 table */
);
@@ -1671,6 +1667,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
unsigned char pack_hash[GIT_MAX_RAWSZ];
unsigned foreign_nr = 1; /* zero is a "good" value, assume bad */
int report_end_of_input = 0;
+ int hash_algo = 0;
/*
* index-pack never needs to fetch missing objects except when
@@ -1764,6 +1761,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
die(_("bad %s"), arg);
} else if (skip_prefix(arg, "--max-input-size=", &arg)) {
max_input_size = strtoumax(arg, NULL, 10);
+ } else if (skip_prefix(arg, "--object-format=", &arg)) {
+ hash_algo = hash_algo_by_name(arg);
+ if (hash_algo == GIT_HASH_UNKNOWN)
+ die(_("unknown hash algorithm '%s'"), arg);
+ repo_set_hash_algo(the_repository, hash_algo);
} else
usage(index_pack_usage);
continue;
@@ -1780,6 +1782,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
die(_("--fix-thin cannot be used without --stdin"));
if (from_stdin && !startup_info->have_repository)
die(_("--stdin requires a git repository"));
+ if (from_stdin && hash_algo)
+ die(_("--object-format cannot be used with --stdin"));
if (!index_name && pack_name)
index_name = derive_filename(pack_name, "idx", &index_name_buf);
diff --git a/builtin/init-db.c b/builtin/init-db.c
index 0b7222e..cee6482 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -203,6 +203,7 @@ void initialize_repository_version(int hash_algo)
static int create_default_files(const char *template_path,
const char *original_git_dir,
+ const char *initial_branch,
const struct repository_format *fmt)
{
struct stat st1;
@@ -258,15 +259,26 @@ static int create_default_files(const char *template_path,
die("failed to set up refs db: %s", err.buf);
/*
- * Create the default symlink from ".git/HEAD" to the "master"
- * branch, if it does not exist yet.
+ * Point the HEAD symref to the initial branch with if HEAD does
+ * not yet exist.
*/
path = git_path_buf(&buf, "HEAD");
reinit = (!access(path, R_OK)
|| readlink(path, junk, sizeof(junk)-1) != -1);
if (!reinit) {
- if (create_symref("HEAD", "refs/heads/master", NULL) < 0)
+ char *ref;
+
+ if (!initial_branch)
+ initial_branch = git_default_branch_name();
+
+ ref = xstrfmt("refs/heads/%s", initial_branch);
+ if (check_refname_format(ref, 0) < 0)
+ die(_("invalid initial branch name: '%s'"),
+ initial_branch);
+
+ if (create_symref("HEAD", ref, NULL) < 0)
exit(1);
+ free(ref);
}
initialize_repository_version(fmt->hash_algo);
@@ -383,7 +395,8 @@ static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash
}
int init_db(const char *git_dir, const char *real_git_dir,
- const char *template_dir, int hash, unsigned int flags)
+ const char *template_dir, int hash, const char *initial_branch,
+ unsigned int flags)
{
int reinit;
int exist_ok = flags & INIT_DB_EXIST_OK;
@@ -425,7 +438,11 @@ int init_db(const char *git_dir, const char *real_git_dir,
validate_hash_algorithm(&repo_fmt, hash);
- reinit = create_default_files(template_dir, original_git_dir, &repo_fmt);
+ reinit = create_default_files(template_dir, original_git_dir,
+ initial_branch, &repo_fmt);
+ if (reinit && initial_branch)
+ warning(_("re-init: ignored --initial-branch=%s"),
+ initial_branch);
create_object_directory();
@@ -528,6 +545,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
const char *template_dir = NULL;
unsigned int flags = 0;
const char *object_format = NULL;
+ const char *initial_branch = NULL;
int hash_algo = GIT_HASH_UNKNOWN;
const struct option init_db_options[] = {
OPT_STRING(0, "template", &template_dir, N_("template-directory"),
@@ -541,6 +559,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
OPT_BIT('q', "quiet", &flags, N_("be quiet"), INIT_DB_QUIET),
OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
N_("separate git dir from working tree")),
+ OPT_STRING('b', "initial-branch", &initial_branch, N_("name"),
+ N_("override the name of the initial branch")),
OPT_STRING(0, "object-format", &object_format, N_("hash"),
N_("specify the hash algorithm to use")),
OPT_END()
@@ -652,5 +672,6 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
UNLEAK(work_tree);
flags |= INIT_DB_EXIST_OK;
- return init_db(git_dir, real_git_dir, template_dir, hash_algo, flags);
+ return init_db(git_dir, real_git_dir, template_dir, hash_algo,
+ initial_branch, flags);
}
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index 6ef5195..3a4dd12 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -118,6 +118,10 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
transport->server_options = &server_options;
ref = transport_get_remote_refs(transport, &ref_prefixes);
+ if (ref) {
+ int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
+ repo_set_hash_algo(the_repository, hash_algo);
+ }
if (transport_disconnect(transport)) {
UNLEAK(sorting);
return 1;
diff --git a/builtin/merge.c b/builtin/merge.c
index ca6a5dc..7da707b 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -1656,7 +1656,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
}
merge_was_ok = 1;
}
- cnt = evaluate_result();
+ cnt = (use_strategies_nr > 1) ? evaluate_result() : 0;
if (best_cnt <= 0 || cnt <= best_cnt) {
best_strategy = use_strategies[i]->name;
best_cnt = cnt;
diff --git a/builtin/mv.c b/builtin/mv.c
index be15ba7..7dac714 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -132,6 +132,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
struct stat st;
struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
struct lock_file lock_file = LOCK_INIT;
+ struct cache_entry *ce;
git_config(git_default_config, NULL);
@@ -220,9 +221,11 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
}
argc += last - first;
}
- } else if (cache_name_pos(src, length) < 0)
+ } else if (!(ce = cache_file_exists(src, length, ignore_case))) {
bad = _("not under version control");
- else if (lstat(dst, &st) == 0 &&
+ } else if (ce_stage(ce)) {
+ bad = _("conflicted");
+ } else if (lstat(dst, &st) == 0 &&
(!ignore_case || strcasecmp(src, dst))) {
bad = _("destination exists");
if (force) {
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index c5b433a..ecef5cd 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -35,6 +35,7 @@
#include "midx.h"
#include "trace2.h"
#include "shallow.h"
+#include "promisor-remote.h"
#define IN_PACK(obj) oe_in_pack(&to_pack, obj)
#define SIZE(obj) oe_size(&to_pack, obj)
@@ -117,6 +118,8 @@ static unsigned long window_memory_limit = 0;
static struct list_objects_filter_options filter_options;
+static struct string_list uri_protocols = STRING_LIST_INIT_NODUP;
+
enum missing_action {
MA_ERROR = 0, /* fail if any missing objects are encountered */
MA_ALLOW_ANY, /* silently allow ALL missing objects */
@@ -125,6 +128,15 @@ enum missing_action {
static enum missing_action arg_missing_action;
static show_object_fn fn_show_object;
+struct configured_exclusion {
+ struct oidmap_entry e;
+ char *pack_hash_hex;
+ char *uri;
+};
+static struct oidmap configured_exclusions;
+
+static struct oidset excluded_by_config;
+
/*
* stats
*/
@@ -969,6 +981,25 @@ static void write_reused_pack(struct hashfile *f)
unuse_pack(&w_curs);
}
+static void write_excluded_by_configs(void)
+{
+ struct oidset_iter iter;
+ const struct object_id *oid;
+
+ oidset_iter_init(&excluded_by_config, &iter);
+ while ((oid = oidset_iter_next(&iter))) {
+ struct configured_exclusion *ex =
+ oidmap_get(&configured_exclusions, oid);
+
+ if (!ex)
+ BUG("configured exclusion wasn't configured");
+ write_in_full(1, ex->pack_hash_hex, strlen(ex->pack_hash_hex));
+ write_in_full(1, " ", 1);
+ write_in_full(1, ex->uri, strlen(ex->uri));
+ write_in_full(1, "\n", 1);
+ }
+}
+
static const char no_split_warning[] = N_(
"disabling bitmap writing, packs are split due to pack.packSizeLimit"
);
@@ -1266,6 +1297,25 @@ static int want_object_in_pack(const struct object_id *oid,
}
}
+ if (uri_protocols.nr) {
+ struct configured_exclusion *ex =
+ oidmap_get(&configured_exclusions, oid);
+ int i;
+ const char *p;
+
+ if (ex) {
+ for (i = 0; i < uri_protocols.nr; i++) {
+ if (skip_prefix(ex->uri,
+ uri_protocols.items[i].string,
+ &p) &&
+ *p == ':') {
+ oidset_insert(&excluded_by_config, oid);
+ return 0;
+ }
+ }
+ }
+ }
+
return 1;
}
@@ -1655,9 +1705,30 @@ static int can_reuse_delta(const struct object_id *base_oid,
return 0;
}
-static void check_object(struct object_entry *entry)
+static void prefetch_to_pack(uint32_t object_index_start) {
+ struct oid_array to_fetch = OID_ARRAY_INIT;
+ uint32_t i;
+
+ 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))
+ continue;
+ oid_array_append(&to_fetch, &entry->idx.oid);
+ }
+ promisor_remote_get_direct(the_repository,
+ to_fetch.oid, to_fetch.nr);
+ oid_array_clear(&to_fetch);
+}
+
+static void check_object(struct object_entry *entry, uint32_t object_index)
{
unsigned long canonical_size;
+ enum object_type type;
+ struct object_info oi = {.typep = &type, .sizep = &canonical_size};
if (IN_PACK(entry)) {
struct packed_git *p = IN_PACK(entry);
@@ -1791,8 +1862,18 @@ static void check_object(struct object_entry *entry)
unuse_pack(&w_curs);
}
- oe_set_type(entry,
- oid_object_info(the_repository, &entry->idx.oid, &canonical_size));
+ if (oid_object_info_extended(the_repository, &entry->idx.oid, &oi,
+ OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_LOOKUP_REPLACE) < 0) {
+ if (has_promisor_remote()) {
+ 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)
+ type = -1;
+ } else {
+ type = -1;
+ }
+ }
+ oe_set_type(entry, type);
if (entry->type_valid) {
SET_SIZE(entry, canonical_size);
} else {
@@ -2012,7 +2093,7 @@ static void get_object_details(void)
for (i = 0; i < to_pack.nr_objects; i++) {
struct object_entry *entry = sorted_by_offset[i];
- check_object(entry);
+ check_object(entry, i);
if (entry->type_valid &&
oe_size_greater_than(&to_pack, entry, big_file_threshold))
entry->no_try_delta = 1;
@@ -2864,6 +2945,29 @@ static int git_pack_config(const char *k, const char *v, void *cb)
pack_idx_opts.version);
return 0;
}
+ if (!strcmp(k, "uploadpack.blobpackfileuri")) {
+ struct configured_exclusion *ex = xmalloc(sizeof(*ex));
+ const char *oid_end, *pack_end;
+ /*
+ * Stores the pack hash. This is not a true object ID, but is
+ * of the same form.
+ */
+ struct object_id pack_hash;
+
+ if (parse_oid_hex(v, &ex->e.oid, &oid_end) ||
+ *oid_end != ' ' ||
+ parse_oid_hex(oid_end + 1, &pack_hash, &pack_end) ||
+ *pack_end != ' ')
+ die(_("value of uploadpack.blobpackfileuri must be "
+ "of the form '<object-hash> <pack-hash> <uri>' (got '%s')"), v);
+ if (oidmap_get(&configured_exclusions, &ex->e.oid))
+ die(_("object already configured in another "
+ "uploadpack.blobpackfileuri (got '%s')"), v);
+ ex->pack_hash_hex = xcalloc(1, pack_end - oid_end);
+ memcpy(ex->pack_hash_hex, oid_end + 1, pack_end - oid_end - 1);
+ ex->uri = xstrdup(pack_end + 1);
+ oidmap_put(&configured_exclusions, ex);
+ }
return git_default_config(k, v, cb);
}
@@ -3462,6 +3566,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
N_("do not pack objects in promisor packfiles")),
OPT_BOOL(0, "delta-islands", &use_delta_islands,
N_("respect islands during delta compression")),
+ OPT_STRING_LIST(0, "uri-protocol", &uri_protocols,
+ N_("protocol"),
+ N_("exclude any configured uploadpack.blobpackfileuri with this protocol")),
OPT_END(),
};
@@ -3650,6 +3757,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
}
trace2_region_enter("pack-objects", "write-pack-file", the_repository);
+ write_excluded_by_configs();
write_pack_file();
trace2_region_leave("pack-objects", "write-pack-file", the_repository);
diff --git a/builtin/pull.c b/builtin/pull.c
index 00e5857..8159c5d 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -1025,12 +1025,14 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
commit_list_insert(head, &list);
merge_head = lookup_commit_reference(the_repository,
&merge_heads.oid[0]);
- if (is_descendant_of(merge_head, list)) {
+ if (repo_is_descendant_of(the_repository,
+ merge_head, list)) {
/* we can fast-forward this without invoking rebase */
opt_ff = "--ff-only";
ran_ff = 1;
ret = run_merge();
}
+ free_commit_list(list);
}
if (!ran_ff)
ret = run_rebase(&curr_head, merge_heads.oid, &rebase_fork_point);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index ea3d0f0..d43663b 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -249,6 +249,7 @@ static void show_ref(const char *path, const struct object_id *oid)
strbuf_addf(&cap, " push-cert=%s", push_cert_nonce);
if (advertise_push_options)
strbuf_addstr(&cap, " push-options");
+ strbuf_addf(&cap, " object-format=%s", the_hash_algo->name);
strbuf_addf(&cap, " agent=%s", git_user_agent_sanitized());
packet_write_fmt(1, "%s %s%c%s\n",
oid_to_hex(oid), path, 0, cap.buf);
@@ -1624,6 +1625,8 @@ static struct command *read_head_info(struct packet_reader *reader,
linelen = strlen(reader->line);
if (linelen < reader->pktlen) {
const char *feature_list = reader->line + linelen + 1;
+ const char *hash = NULL;
+ int len = 0;
if (parse_feature_request(feature_list, "report-status"))
report_status = 1;
if (parse_feature_request(feature_list, "side-band-64k"))
@@ -1636,6 +1639,13 @@ static struct command *read_head_info(struct packet_reader *reader,
if (advertise_push_options
&& parse_feature_request(feature_list, "push-options"))
use_push_options = 1;
+ hash = parse_feature_value(feature_list, "object-format", &len, NULL);
+ if (!hash) {
+ hash = hash_algos[GIT_HASH_SHA1].name;
+ len = strlen(hash);
+ }
+ if (xstrncmpz(the_hash_algo->name, hash, len))
+ die("error: unsupported object format '%s'", hash);
}
if (!strcmp(reader->line, "push-cert")) {
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 52ecf6d..ca1d807 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -615,7 +615,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
int i;
memset(&collected, 0, sizeof(collected));
- worktrees = get_worktrees(0);
+ worktrees = get_worktrees();
for (p = worktrees; *p; p++) {
if (!all_worktrees && !(*p)->is_current)
continue;
diff --git a/builtin/show-index.c b/builtin/show-index.c
index 0826f6a..8106b03 100644
--- a/builtin/show-index.c
+++ b/builtin/show-index.c
@@ -1,9 +1,12 @@
#include "builtin.h"
#include "cache.h"
#include "pack.h"
+#include "parse-options.h"
-static const char show_index_usage[] =
-"git show-index";
+static const char *const show_index_usage[] = {
+ "git show-index [--object-format=<hash-algorithm>]",
+ NULL
+};
int cmd_show_index(int argc, const char **argv, const char *prefix)
{
@@ -11,10 +14,26 @@ int cmd_show_index(int argc, const char **argv, const char *prefix)
unsigned nr;
unsigned int version;
static unsigned int top_index[256];
- const unsigned hashsz = the_hash_algo->rawsz;
+ unsigned hashsz;
+ const char *hash_name = NULL;
+ int hash_algo;
+ const struct option show_index_options[] = {
+ OPT_STRING(0, "object-format", &hash_name, N_("hash-algorithm"),
+ N_("specify the hash algorithm to use")),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, show_index_options, show_index_usage, 0);
+
+ if (hash_name) {
+ hash_algo = hash_algo_by_name(hash_name);
+ if (hash_algo == GIT_HASH_UNKNOWN)
+ die(_("Unknown hash algorithm"));
+ repo_set_hash_algo(the_repository, hash_algo);
+ }
+
+ hashsz = the_hash_algo->rawsz;
- if (argc != 1)
- usage(show_index_usage);
if (fread(top_index, 2 * 4, 1, stdin) != 1)
die("unable to read header");
if (top_index[0] == htonl(PACK_IDX_SIGNATURE)) {
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 95d0882..4003f4d 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -99,6 +99,10 @@ static int update_working_directory(struct pattern_list *pl)
struct lock_file lock_file = LOCK_INIT;
struct repository *r = the_repository;
+ /* If no branch has been checked out, there are no updates to make. */
+ if (is_index_unborn(r->index))
+ return UPDATE_SPARSITY_SUCCESS;
+
memset(&o, 0, sizeof(o));
o.verbose_update = isatty(2);
o.update = 1;
@@ -249,6 +253,8 @@ static int set_config(enum sparse_checkout_mode mode)
{
const char *config_path;
+ if (upgrade_repository_format(1) < 0)
+ die(_("unable to upgrade repository format to enable worktreeConfig"));
if (git_config_set_gently("extensions.worktreeConfig", "true")) {
error(_("failed to set extensions.worktreeConfig setting"));
return 1;
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 46c03d2..a1c7560 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1981,7 +1981,7 @@ static const char *remote_submodule_branch(const char *path)
free(key);
if (!branch)
- return "master";
+ return "HEAD";
if (!strcmp(branch, ".")) {
const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
@@ -2277,6 +2277,49 @@ static int module_set_url(int argc, const char **argv, const char *prefix)
return 0;
}
+static int module_set_branch(int argc, const char **argv, const char *prefix)
+{
+ int opt_default = 0, ret;
+ const char *opt_branch = NULL;
+ const char *path;
+ char *config_name;
+
+ /*
+ * We accept the `quiet` option for uniformity across subcommands,
+ * though there is nothing to make less verbose in this subcommand.
+ */
+ struct option options[] = {
+ OPT_NOOP_NOARG('q', "quiet"),
+ OPT_BOOL('d', "default", &opt_default,
+ N_("set the default tracking branch to master")),
+ OPT_STRING('b', "branch", &opt_branch, N_("branch"),
+ N_("set the default tracking branch")),
+ OPT_END()
+ };
+ const char *const usage[] = {
+ N_("git submodule--helper set-branch [-q|--quiet] (-d|--default) <path>"),
+ N_("git submodule--helper set-branch [-q|--quiet] (-b|--branch) <branch> <path>"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+ if (!opt_branch && !opt_default)
+ die(_("--branch or --default required"));
+
+ if (opt_branch && opt_default)
+ die(_("--branch and --default are mutually exclusive"));
+
+ if (argc != 1 || !(path = argv[0]))
+ usage_with_options(usage, options);
+
+ config_name = xstrfmt("submodule.%s.branch", path);
+ ret = config_set_in_gitmodules_file_gently(config_name, opt_branch);
+
+ free(config_name);
+ return !!ret;
+}
+
#define SUPPORT_SUPER_PREFIX (1<<0)
struct cmd_struct {
@@ -2308,6 +2351,7 @@ static struct cmd_struct commands[] = {
{"check-name", check_name, 0},
{"config", module_config, 0},
{"set-url", module_set_url, 0},
+ {"set-branch", module_set_branch, 0},
};
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
diff --git a/builtin/worktree.c b/builtin/worktree.c
index d99db35..f0cbdef 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -67,7 +67,12 @@ static void delete_worktrees_dir_if_empty(void)
rmdir(git_path("worktrees")); /* ignore failed removal */
}
-static int prune_worktree(const char *id, struct strbuf *reason)
+/*
+ * Return true if worktree entry should be pruned, along with the reason for
+ * pruning. Otherwise, return false and the worktree's path, or NULL if it
+ * cannot be determined. Caller is responsible for freeing returned path.
+ */
+static int should_prune_worktree(const char *id, struct strbuf *reason, char **wtpath)
{
struct stat st;
char *path;
@@ -75,20 +80,21 @@ static int prune_worktree(const char *id, struct strbuf *reason)
size_t len;
ssize_t read_result;
+ *wtpath = NULL;
if (!is_directory(git_path("worktrees/%s", id))) {
- strbuf_addf(reason, _("Removing worktrees/%s: not a valid directory"), id);
+ strbuf_addstr(reason, _("not a valid directory"));
return 1;
}
if (file_exists(git_path("worktrees/%s/locked", id)))
return 0;
if (stat(git_path("worktrees/%s/gitdir", id), &st)) {
- strbuf_addf(reason, _("Removing worktrees/%s: gitdir file does not exist"), id);
+ strbuf_addstr(reason, _("gitdir file does not exist"));
return 1;
}
fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
if (fd < 0) {
- strbuf_addf(reason, _("Removing worktrees/%s: unable to read gitdir file (%s)"),
- id, strerror(errno));
+ strbuf_addf(reason, _("unable to read gitdir file (%s)"),
+ strerror(errno));
return 1;
}
len = xsize_t(st.st_size);
@@ -96,8 +102,8 @@ static int prune_worktree(const char *id, struct strbuf *reason)
read_result = read_in_full(fd, path, len);
if (read_result < 0) {
- strbuf_addf(reason, _("Removing worktrees/%s: unable to read gitdir file (%s)"),
- id, strerror(errno));
+ strbuf_addf(reason, _("unable to read gitdir file (%s)"),
+ strerror(errno));
close(fd);
free(path);
return 1;
@@ -106,53 +112,103 @@ static int prune_worktree(const char *id, struct strbuf *reason)
if (read_result != len) {
strbuf_addf(reason,
- _("Removing worktrees/%s: short read (expected %"PRIuMAX" bytes, read %"PRIuMAX")"),
- id, (uintmax_t)len, (uintmax_t)read_result);
+ _("short read (expected %"PRIuMAX" bytes, read %"PRIuMAX")"),
+ (uintmax_t)len, (uintmax_t)read_result);
free(path);
return 1;
}
while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
len--;
if (!len) {
- strbuf_addf(reason, _("Removing worktrees/%s: invalid gitdir file"), id);
+ strbuf_addstr(reason, _("invalid gitdir file"));
free(path);
return 1;
}
path[len] = '\0';
if (!file_exists(path)) {
- free(path);
if (stat(git_path("worktrees/%s/index", id), &st) ||
st.st_mtime <= expire) {
- strbuf_addf(reason, _("Removing worktrees/%s: gitdir file points to non-existent location"), id);
+ strbuf_addstr(reason, _("gitdir file points to non-existent location"));
+ free(path);
return 1;
} else {
+ *wtpath = path;
return 0;
}
}
- free(path);
+ *wtpath = path;
return 0;
}
+static void prune_worktree(const char *id, const char *reason)
+{
+ if (show_only || verbose)
+ printf_ln(_("Removing %s/%s: %s"), "worktrees", id, reason);
+ if (!show_only)
+ delete_git_dir(id);
+}
+
+static int prune_cmp(const void *a, const void *b)
+{
+ const struct string_list_item *x = a;
+ const struct string_list_item *y = b;
+ int c;
+
+ if ((c = fspathcmp(x->string, y->string)))
+ return c;
+ /*
+ * paths same; prune_dupes() removes all but the first worktree entry
+ * having the same path, so sort main worktree ('util' is NULL) above
+ * linked worktrees ('util' not NULL) since main worktree can't be
+ * removed
+ */
+ if (!x->util)
+ return -1;
+ if (!y->util)
+ return 1;
+ /* paths same; sort by .git/worktrees/<id> */
+ return strcmp(x->util, y->util);
+}
+
+static void prune_dups(struct string_list *l)
+{
+ int i;
+
+ QSORT(l->items, l->nr, prune_cmp);
+ for (i = 1; i < l->nr; i++) {
+ if (!fspathcmp(l->items[i].string, l->items[i - 1].string))
+ prune_worktree(l->items[i].util, "duplicate entry");
+ }
+}
+
static void prune_worktrees(void)
{
struct strbuf reason = STRBUF_INIT;
+ struct strbuf main_path = STRBUF_INIT;
+ struct string_list kept = STRING_LIST_INIT_NODUP;
DIR *dir = opendir(git_path("worktrees"));
struct dirent *d;
if (!dir)
return;
while ((d = readdir(dir)) != NULL) {
+ char *path;
if (is_dot_or_dotdot(d->d_name))
continue;
strbuf_reset(&reason);
- if (!prune_worktree(d->d_name, &reason))
- continue;
- if (show_only || verbose)
- printf("%s\n", reason.buf);
- if (show_only)
- continue;
- delete_git_dir(d->d_name);
+ if (should_prune_worktree(d->d_name, &reason, &path))
+ prune_worktree(d->d_name, reason.buf);
+ else if (path)
+ string_list_append(&kept, path)->util = xstrdup(d->d_name);
}
closedir(dir);
+
+ strbuf_add_absolute_path(&main_path, get_git_common_dir());
+ /* massage main worktree absolute path to match 'gitdir' content */
+ strbuf_strip_suffix(&main_path, "/.");
+ string_list_append(&kept, strbuf_detach(&main_path, NULL));
+ prune_dups(&kept);
+ string_list_clear(&kept, 1);
+
if (!show_only)
delete_worktrees_dir_if_empty();
strbuf_release(&reason);
@@ -224,34 +280,33 @@ static const char *worktree_basename(const char *path, int *olen)
return name;
}
-static void validate_worktree_add(const char *path, const struct add_opts *opts)
+/* check that path is viable location for worktree */
+static void check_candidate_path(const char *path,
+ int force,
+ struct worktree **worktrees,
+ const char *cmd)
{
- struct worktree **worktrees;
struct worktree *wt;
int locked;
if (file_exists(path) && !is_empty_dir(path))
die(_("'%s' already exists"), path);
- worktrees = get_worktrees(0);
wt = find_worktree_by_path(worktrees, path);
if (!wt)
- goto done;
+ return;
locked = !!worktree_lock_reason(wt);
- if ((!locked && opts->force) || (locked && opts->force > 1)) {
+ if ((!locked && force) || (locked && force > 1)) {
if (delete_git_dir(wt->id))
- die(_("unable to re-add worktree '%s'"), path);
- goto done;
+ die(_("unusable worktree destination '%s'"), path);
+ return;
}
if (locked)
- die(_("'%s' is a missing but locked worktree;\nuse 'add -f -f' to override, or 'unlock' and 'prune' or 'remove' to clear"), path);
+ die(_("'%s' is a missing but locked worktree;\nuse '%s -f -f' to override, or 'unlock' and 'prune' or 'remove' to clear"), cmd, path);
else
- die(_("'%s' is a missing but already registered worktree;\nuse 'add -f' to override, or 'prune' or 'remove' to clear"), path);
-
-done:
- free_worktrees(worktrees);
+ die(_("'%s' is a missing but already registered worktree;\nuse '%s -f' to override, or 'prune' or 'remove' to clear"), cmd, path);
}
static int add_worktree(const char *path, const char *refname,
@@ -268,8 +323,12 @@ static int add_worktree(const char *path, const char *refname,
struct commit *commit = NULL;
int is_branch = 0;
struct strbuf sb_name = STRBUF_INIT;
+ struct worktree **worktrees;
- validate_worktree_add(path, opts);
+ worktrees = get_worktrees();
+ check_candidate_path(path, opts->force, worktrees, "add");
+ free_worktrees(worktrees);
+ worktrees = NULL;
/* is 'refname' a branch or commit? */
if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
@@ -638,6 +697,23 @@ static void measure_widths(struct worktree **wt, int *abbrev, int *maxlen)
}
}
+static int pathcmp(const void *a_, const void *b_)
+{
+ const struct worktree *const *a = a_;
+ const struct worktree *const *b = b_;
+ return fspathcmp((*a)->path, (*b)->path);
+}
+
+static void pathsort(struct worktree **wt)
+{
+ int n = 0;
+ struct worktree **p = wt;
+
+ while (*p++)
+ n++;
+ QSORT(wt, n, pathcmp);
+}
+
static int list(int ac, const char **av, const char *prefix)
{
int porcelain = 0;
@@ -651,9 +727,12 @@ static int list(int ac, const char **av, const char *prefix)
if (ac)
usage_with_options(worktree_usage, options);
else {
- struct worktree **worktrees = get_worktrees(GWT_SORT_LINKED);
+ struct worktree **worktrees = get_worktrees();
int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;
+ /* sort worktrees by path but keep main worktree at top */
+ pathsort(worktrees + 1);
+
if (!porcelain)
measure_widths(worktrees, &abbrev, &path_maxlen);
@@ -682,7 +761,7 @@ static int lock_worktree(int ac, const char **av, const char *prefix)
if (ac != 1)
usage_with_options(worktree_usage, options);
- worktrees = get_worktrees(0);
+ worktrees = get_worktrees();
wt = find_worktree(worktrees, prefix, av[0]);
if (!wt)
die(_("'%s' is not a working tree"), av[0]);
@@ -715,7 +794,7 @@ static int unlock_worktree(int ac, const char **av, const char *prefix)
if (ac != 1)
usage_with_options(worktree_usage, options);
- worktrees = get_worktrees(0);
+ worktrees = get_worktrees();
wt = find_worktree(worktrees, prefix, av[0]);
if (!wt)
die(_("'%s' is not a working tree"), av[0]);
@@ -789,7 +868,7 @@ static int move_worktree(int ac, const char **av, const char *prefix)
strbuf_addstr(&dst, path);
free(path);
- worktrees = get_worktrees(0);
+ worktrees = get_worktrees();
wt = find_worktree(worktrees, prefix, av[0]);
if (!wt)
die(_("'%s' is not a working tree"), av[0]);
@@ -804,8 +883,7 @@ static int move_worktree(int ac, const char **av, const char *prefix)
strbuf_trim_trailing_dir_sep(&dst);
strbuf_addstr(&dst, sep);
}
- if (file_exists(dst.buf))
- die(_("target '%s' already exists"), dst.buf);
+ check_candidate_path(dst.buf, force, worktrees, "move");
validate_no_submodules(wt);
@@ -916,7 +994,7 @@ static int remove_worktree(int ac, const char **av, const char *prefix)
if (ac != 1)
usage_with_options(worktree_usage, options);
- worktrees = get_worktrees(0);
+ worktrees = get_worktrees();
wt = find_worktree(worktrees, prefix, av[0]);
if (!wt)
die(_("'%s' is not a working tree"), av[0]);
diff --git a/bundle.c b/bundle.c
index 99439e0..2a0d744 100644
--- a/bundle.c
+++ b/bundle.c
@@ -23,6 +23,17 @@ static void add_to_ref_list(const struct object_id *oid, const char *name,
list->nr++;
}
+static const struct git_hash_algo *detect_hash_algo(struct strbuf *buf)
+{
+ size_t len = strcspn(buf->buf, " \n");
+ int algo;
+
+ algo = hash_algo_by_length(len / 2);
+ if (algo == GIT_HASH_UNKNOWN)
+ return NULL;
+ return &hash_algos[algo];
+}
+
static int parse_bundle_header(int fd, struct bundle_header *header,
const char *report_path)
{
@@ -52,12 +63,21 @@ static int parse_bundle_header(int fd, struct bundle_header *header,
}
strbuf_rtrim(&buf);
+ if (!header->hash_algo) {
+ header->hash_algo = detect_hash_algo(&buf);
+ if (!header->hash_algo) {
+ error(_("unknown hash algorithm length"));
+ status = -1;
+ break;
+ }
+ }
+
/*
* Tip lines have object name, SP, and refname.
* Prerequisites have object name that is optionally
* followed by SP and subject line.
*/
- if (parse_oid_hex(buf.buf, &oid, &p) ||
+ if (parse_oid_hex_algop(buf.buf, &oid, &p, header->hash_algo) ||
(*p && !isspace(*p)) ||
(!is_prereq && !*p)) {
if (report_path)
diff --git a/bundle.h b/bundle.h
index ceab0c7..2dc9442 100644
--- a/bundle.h
+++ b/bundle.h
@@ -15,6 +15,7 @@ struct ref_list {
struct bundle_header {
struct ref_list prerequisites;
struct ref_list references;
+ const struct git_hash_algo *hash_algo;
};
int is_bundle(const char *path, int quiet);
diff --git a/cache.h b/cache.h
index 0f0485e..0290849 100644
--- a/cache.h
+++ b/cache.h
@@ -628,7 +628,7 @@ int path_inside_repo(const char *prefix, const char *path);
int init_db(const char *git_dir, const char *real_git_dir,
const char *template_dir, int hash_algo,
- unsigned int flags);
+ const char *initial_branch, unsigned int flags);
void initialize_repository_version(int hash_algo);
void sanitize_stdfds(void);
@@ -1044,6 +1044,7 @@ struct repository_format {
int hash_algo;
char *work_tree;
struct string_list unknown_extensions;
+ struct string_list v1_only_extensions;
};
/*
@@ -1057,6 +1058,7 @@ struct repository_format {
.is_bare = -1, \
.hash_algo = GIT_HASH_SHA1, \
.unknown_extensions = STRING_LIST_INIT_DUP, \
+ .v1_only_extensions = STRING_LIST_INIT_DUP, \
}
/*
diff --git a/ci/config/allow-refs.sample b/ci/config/allow-ref.sample
index f157f19..af0e076 100755
--- a/ci/config/allow-refs.sample
+++ b/ci/config/allow-ref.sample
@@ -7,8 +7,9 @@
# your repository:
#
# git checkout -b ci-config
-# cp allow-refs.sample allow-refs
-# $EDITOR allow-refs
+# cp allow-ref.sample allow-ref
+# $EDITOR allow-ref
+# git add allow-ref
# git commit -am "implement my ci preferences"
# git push
#
diff --git a/ci/lib.sh b/ci/lib.sh
index ff24c54..3eefec5 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -184,9 +184,9 @@ linux-clang|linux-gcc)
if [ "$jobname" = linux-gcc ]
then
export CC=gcc-8
- MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python3)"
+ MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=/usr/bin/python3"
else
- MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python2)"
+ MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=/usr/bin/python2"
fi
export GIT_TEST_HTTPD=true
diff --git a/column.c b/column.c
index 4a38eed..a58969b 100644
--- a/column.c
+++ b/column.c
@@ -107,7 +107,7 @@ static void display_plain(const struct string_list *list,
printf("%s%s%s", indent, list->items[i].string, nl);
}
-/* Print a cell to stdout with all necessary leading/traling space */
+/* Print a cell to stdout with all necessary leading/trailing space */
static int display_cell(struct column_data *data, int initial_width,
const char *empty_cell, int x, int y)
{
diff --git a/command-list.txt b/command-list.txt
index cbb960c..89aa60c 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -136,7 +136,7 @@ git-pack-redundant plumbinginterrogators
git-pack-refs ancillarymanipulators
git-parse-remote synchelpers
git-patch-id purehelpers
-git-prune ancillarymanipulators
+git-prune ancillarymanipulators complete
git-prune-packed plumbingmanipulators
git-pull mainporcelain remote
git-push mainporcelain remote
diff --git a/commit-graph.c b/commit-graph.c
index e3420dd..e51c91d 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -1,7 +1,5 @@
-#include "cache.h"
-#include "config.h"
-#include "dir.h"
#include "git-compat-util.h"
+#include "config.h"
#include "lockfile.h"
#include "pack.h"
#include "packfile.h"
@@ -19,6 +17,8 @@
#include "bloom.h"
#include "commit-slab.h"
#include "shallow.h"
+#include "json-writer.h"
+#include "trace2.h"
void git_test_write_commit_graph_or_die(void)
{
@@ -87,15 +87,69 @@ static int commit_pos_cmp(const void *va, const void *vb)
commit_pos_at(&commit_pos, b);
}
+define_commit_slab(commit_graph_data_slab, struct commit_graph_data);
+static struct commit_graph_data_slab commit_graph_data_slab =
+ COMMIT_SLAB_INIT(1, commit_graph_data_slab);
+
+uint32_t commit_graph_position(const struct commit *c)
+{
+ struct commit_graph_data *data =
+ commit_graph_data_slab_peek(&commit_graph_data_slab, c);
+
+ return data ? data->graph_pos : COMMIT_NOT_FROM_GRAPH;
+}
+
+uint32_t commit_graph_generation(const struct commit *c)
+{
+ struct commit_graph_data *data =
+ commit_graph_data_slab_peek(&commit_graph_data_slab, c);
+
+ if (!data)
+ return GENERATION_NUMBER_INFINITY;
+ else if (data->graph_pos == COMMIT_NOT_FROM_GRAPH)
+ return GENERATION_NUMBER_INFINITY;
+
+ return data->generation;
+}
+
+static struct commit_graph_data *commit_graph_data_at(const struct commit *c)
+{
+ unsigned int i, nth_slab;
+ struct commit_graph_data *data =
+ commit_graph_data_slab_peek(&commit_graph_data_slab, c);
+
+ if (data)
+ return data;
+
+ nth_slab = c->index / commit_graph_data_slab.slab_size;
+ data = commit_graph_data_slab_at(&commit_graph_data_slab, c);
+
+ /*
+ * commit-slab initializes elements with zero, overwrite this with
+ * COMMIT_NOT_FROM_GRAPH for graph_pos.
+ *
+ * We avoid initializing generation with checking if graph position
+ * is not COMMIT_NOT_FROM_GRAPH.
+ */
+ for (i = 0; i < commit_graph_data_slab.slab_size; i++) {
+ commit_graph_data_slab.slab[nth_slab][i].graph_pos =
+ COMMIT_NOT_FROM_GRAPH;
+ }
+
+ return data;
+}
+
static int commit_gen_cmp(const void *va, const void *vb)
{
const struct commit *a = *(const struct commit **)va;
const struct commit *b = *(const struct commit **)vb;
+ uint32_t generation_a = commit_graph_generation(a);
+ uint32_t generation_b = commit_graph_generation(b);
/* lower generation commits first */
- if (a->generation < b->generation)
+ if (generation_a < generation_b)
return -1;
- else if (a->generation > b->generation)
+ else if (generation_a > generation_b)
return 1;
/* use date as a heuristic when generations are equal */
@@ -149,7 +203,8 @@ static int commit_graph_compatible(struct repository *r)
}
prepare_commit_graft(r);
- if (r->parsed_objects && r->parsed_objects->grafts_nr)
+ if (r->parsed_objects &&
+ (r->parsed_objects->grafts_nr || r->parsed_objects->substituted_parent))
return 0;
if (is_repository_shallow(r))
return 0;
@@ -230,8 +285,7 @@ struct commit_graph *parse_commit_graph(void *graph_map, size_t graph_size)
const unsigned char *data, *chunk_lookup;
uint32_t i;
struct commit_graph *graph;
- uint64_t last_chunk_offset;
- uint32_t last_chunk_id;
+ uint64_t next_chunk_offset;
uint32_t graph_signature;
unsigned char graph_version, hash_version;
@@ -271,24 +325,26 @@ struct commit_graph *parse_commit_graph(void *graph_map, size_t graph_size)
graph->data = graph_map;
graph->data_len = graph_size;
- last_chunk_id = 0;
- last_chunk_offset = 8;
+ if (graph_size < GRAPH_HEADER_SIZE +
+ (graph->num_chunks + 1) * GRAPH_CHUNKLOOKUP_WIDTH +
+ GRAPH_FANOUT_SIZE + the_hash_algo->rawsz) {
+ error(_("commit-graph file is too small to hold %u chunks"),
+ graph->num_chunks);
+ free(graph);
+ return NULL;
+ }
+
chunk_lookup = data + 8;
+ next_chunk_offset = get_be64(chunk_lookup + 4);
for (i = 0; i < graph->num_chunks; i++) {
uint32_t chunk_id;
- uint64_t chunk_offset;
+ uint64_t chunk_offset = next_chunk_offset;
int chunk_repeated = 0;
- if (data + graph_size - chunk_lookup <
- GRAPH_CHUNKLOOKUP_WIDTH) {
- error(_("commit-graph chunk lookup table entry missing; file may be incomplete"));
- goto free_and_return;
- }
-
chunk_id = get_be32(chunk_lookup + 0);
- chunk_offset = get_be64(chunk_lookup + 4);
chunk_lookup += GRAPH_CHUNKLOOKUP_WIDTH;
+ next_chunk_offset = get_be64(chunk_lookup + 4);
if (chunk_offset > graph_size - the_hash_algo->rawsz) {
error(_("commit-graph improper chunk offset %08x%08x"), (uint32_t)(chunk_offset >> 32),
@@ -307,8 +363,11 @@ struct commit_graph *parse_commit_graph(void *graph_map, size_t graph_size)
case GRAPH_CHUNKID_OIDLOOKUP:
if (graph->chunk_oid_lookup)
chunk_repeated = 1;
- else
+ else {
graph->chunk_oid_lookup = data + chunk_offset;
+ graph->num_commits = (next_chunk_offset - chunk_offset)
+ / graph->hash_len;
+ }
break;
case GRAPH_CHUNKID_DATA:
@@ -362,15 +421,6 @@ struct commit_graph *parse_commit_graph(void *graph_map, size_t graph_size)
error(_("commit-graph chunk id %08x appears multiple times"), chunk_id);
goto free_and_return;
}
-
- if (last_chunk_id == GRAPH_CHUNKID_OIDLOOKUP)
- {
- graph->num_commits = (chunk_offset - last_chunk_offset)
- / graph->hash_len;
- }
-
- last_chunk_id = chunk_id;
- last_chunk_offset = chunk_offset;
}
if (graph->chunk_bloom_indexes && graph->chunk_bloom_data) {
@@ -569,10 +619,6 @@ static int prepare_commit_graph(struct repository *r)
return !!r->objects->commit_graph;
r->objects->commit_graph_attempted = 1;
- if (git_env_bool(GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD, 0))
- die("dying as requested by the '%s' variable on commit-graph load!",
- GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD);
-
prepare_repo_settings(r);
if (!git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
@@ -670,13 +716,14 @@ static struct commit_list **insert_parent_or_die(struct repository *r,
c = lookup_commit(r, &oid);
if (!c)
die(_("could not find commit %s"), oid_to_hex(&oid));
- c->graph_pos = pos;
+ commit_graph_data_at(c)->graph_pos = pos;
return &commit_list_insert(c, pptr)->next;
}
static void fill_commit_graph_info(struct commit *item, struct commit_graph *g, uint32_t pos)
{
const unsigned char *commit_data;
+ struct commit_graph_data *graph_data;
uint32_t lex_index;
while (pos < g->num_commits_in_base)
@@ -684,8 +731,10 @@ static void fill_commit_graph_info(struct commit *item, struct commit_graph *g,
lex_index = pos - g->num_commits_in_base;
commit_data = g->chunk_commit_data + GRAPH_DATA_WIDTH * lex_index;
- item->graph_pos = pos;
- item->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
+
+ graph_data = commit_graph_data_at(item);
+ graph_data->graph_pos = pos;
+ graph_data->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
}
static inline void set_commit_tree(struct commit *c, struct tree *t)
@@ -701,6 +750,7 @@ static int fill_commit_in_graph(struct repository *r,
uint32_t *parent_data_ptr;
uint64_t date_low, date_high;
struct commit_list **pptr;
+ struct commit_graph_data *graph_data;
const unsigned char *commit_data;
uint32_t lex_index;
@@ -714,7 +764,8 @@ static int fill_commit_in_graph(struct repository *r,
* Store the "full" position, but then use the
* "local" position for the rest of the calculation.
*/
- item->graph_pos = pos;
+ graph_data = commit_graph_data_at(item);
+ graph_data->graph_pos = pos;
lex_index = pos - g->num_commits_in_base;
commit_data = g->chunk_commit_data + (g->hash_len + 16) * lex_index;
@@ -727,7 +778,7 @@ static int fill_commit_in_graph(struct repository *r,
date_low = get_be32(commit_data + g->hash_len + 12);
item->date = (timestamp_t)((date_high << 32) | date_low);
- item->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
+ graph_data->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
pptr = &item->parents;
@@ -759,8 +810,9 @@ static int fill_commit_in_graph(struct repository *r,
static int find_commit_in_graph(struct commit *item, struct commit_graph *g, uint32_t *pos)
{
- if (item->graph_pos != COMMIT_NOT_FROM_GRAPH) {
- *pos = item->graph_pos;
+ uint32_t graph_pos = commit_graph_position(item);
+ if (graph_pos != COMMIT_NOT_FROM_GRAPH) {
+ *pos = graph_pos;
return 1;
} else {
struct commit_graph *cur_g = g;
@@ -795,6 +847,14 @@ static int parse_commit_in_graph_one(struct repository *r,
int parse_commit_in_graph(struct repository *r, struct commit *item)
{
+ static int checked_env = 0;
+
+ if (!checked_env &&
+ git_env_bool(GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE, 0))
+ die("dying as requested by the '%s' variable on commit-graph parse!",
+ GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE);
+ checked_env = 1;
+
if (!prepare_commit_graph(r))
return 0;
return parse_commit_in_graph_one(r, r->objects->commit_graph, item);
@@ -815,12 +875,13 @@ static struct tree *load_tree_for_commit(struct repository *r,
{
struct object_id oid;
const unsigned char *commit_data;
+ uint32_t graph_pos = commit_graph_position(c);
- while (c->graph_pos < g->num_commits_in_base)
+ while (graph_pos < g->num_commits_in_base)
g = g->base_graph;
commit_data = g->chunk_commit_data +
- GRAPH_DATA_WIDTH * (c->graph_pos - g->num_commits_in_base);
+ GRAPH_DATA_WIDTH * (graph_pos - g->num_commits_in_base);
hashcpy(oid.hash, commit_data);
set_commit_tree(c, lookup_tree(r, &oid));
@@ -834,7 +895,7 @@ static struct tree *get_commit_tree_in_graph_one(struct repository *r,
{
if (c->maybe_tree)
return c->maybe_tree;
- if (c->graph_pos == COMMIT_NOT_FROM_GRAPH)
+ 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);
@@ -881,16 +942,16 @@ struct write_commit_graph_context {
unsigned append:1,
report_progress:1,
split:1,
- check_oids:1,
changed_paths:1,
order_by_pack:1;
const struct split_commit_graph_opts *split_opts;
size_t total_bloom_filter_data_size;
+ const struct bloom_filter_settings *bloom_settings;
};
-static void write_graph_chunk_fanout(struct hashfile *f,
- struct write_commit_graph_context *ctx)
+static int write_graph_chunk_fanout(struct hashfile *f,
+ struct write_commit_graph_context *ctx)
{
int i, count = 0;
struct commit **list = ctx->commits.list;
@@ -911,17 +972,21 @@ static void write_graph_chunk_fanout(struct hashfile *f,
hashwrite_be32(f, count);
}
+
+ return 0;
}
-static void write_graph_chunk_oids(struct hashfile *f, int hash_len,
- struct write_commit_graph_context *ctx)
+static int write_graph_chunk_oids(struct hashfile *f,
+ struct write_commit_graph_context *ctx)
{
struct commit **list = ctx->commits.list;
int count;
for (count = 0; count < ctx->commits.nr; count++, list++) {
display_progress(ctx->progress, ++ctx->progress_cnt);
- hashwrite(f, (*list)->object.oid.hash, (int)hash_len);
+ hashwrite(f, (*list)->object.oid.hash, the_hash_algo->rawsz);
}
+
+ return 0;
}
static const unsigned char *commit_to_sha1(size_t index, void *table)
@@ -930,8 +995,8 @@ static const unsigned char *commit_to_sha1(size_t index, void *table)
return commits[index]->object.oid.hash;
}
-static void write_graph_chunk_data(struct hashfile *f, int hash_len,
- struct write_commit_graph_context *ctx)
+static int write_graph_chunk_data(struct hashfile *f,
+ struct write_commit_graph_context *ctx)
{
struct commit **list = ctx->commits.list;
struct commit **last = ctx->commits.list + ctx->commits.nr;
@@ -948,7 +1013,7 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
die(_("unable to parse commit %s"),
oid_to_hex(&(*list)->object.oid));
tree = get_commit_tree_oid(*list);
- hashwrite(f, tree->hash, hash_len);
+ hashwrite(f, tree->hash, the_hash_algo->rawsz);
parent = (*list)->parents;
@@ -1021,17 +1086,19 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
else
packedDate[0] = 0;
- packedDate[0] |= htonl((*list)->generation << 2);
+ packedDate[0] |= htonl(commit_graph_data_at(*list)->generation << 2);
packedDate[1] = htonl((*list)->date);
hashwrite(f, packedDate, 8);
list++;
}
+
+ return 0;
}
-static void write_graph_chunk_extra_edges(struct hashfile *f,
- struct write_commit_graph_context *ctx)
+static int write_graph_chunk_extra_edges(struct hashfile *f,
+ struct write_commit_graph_context *ctx)
{
struct commit **list = ctx->commits.list;
struct commit **last = ctx->commits.list + ctx->commits.nr;
@@ -1080,59 +1147,67 @@ static void write_graph_chunk_extra_edges(struct hashfile *f,
list++;
}
+
+ return 0;
}
-static void write_graph_chunk_bloom_indexes(struct hashfile *f,
- struct write_commit_graph_context *ctx)
+static int write_graph_chunk_bloom_indexes(struct hashfile *f,
+ struct write_commit_graph_context *ctx)
{
struct commit **list = ctx->commits.list;
struct commit **last = ctx->commits.list + ctx->commits.nr;
uint32_t cur_pos = 0;
- struct progress *progress = NULL;
- int i = 0;
-
- if (ctx->report_progress)
- progress = start_delayed_progress(
- _("Writing changed paths Bloom filters index"),
- ctx->commits.nr);
while (list < last) {
struct bloom_filter *filter = get_bloom_filter(ctx->r, *list, 0);
- cur_pos += filter->len;
- display_progress(progress, ++i);
+ size_t len = filter ? filter->len : 0;
+ cur_pos += len;
+ display_progress(ctx->progress, ++ctx->progress_cnt);
hashwrite_be32(f, cur_pos);
list++;
}
- stop_progress(&progress);
+ return 0;
}
-static void write_graph_chunk_bloom_data(struct hashfile *f,
- struct write_commit_graph_context *ctx,
- const struct bloom_filter_settings *settings)
+static void trace2_bloom_filter_settings(struct write_commit_graph_context *ctx)
+{
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_object_begin(&jw, 0);
+ jw_object_intmax(&jw, "hash_version", ctx->bloom_settings->hash_version);
+ jw_object_intmax(&jw, "num_hashes", ctx->bloom_settings->num_hashes);
+ jw_object_intmax(&jw, "bits_per_entry", ctx->bloom_settings->bits_per_entry);
+ jw_end(&jw);
+
+ trace2_data_json("bloom", ctx->r, "settings", &jw);
+
+ jw_release(&jw);
+}
+
+static int write_graph_chunk_bloom_data(struct hashfile *f,
+ struct write_commit_graph_context *ctx)
{
struct commit **list = ctx->commits.list;
struct commit **last = ctx->commits.list + ctx->commits.nr;
- struct progress *progress = NULL;
- int i = 0;
- if (ctx->report_progress)
- progress = start_delayed_progress(
- _("Writing changed paths Bloom filters data"),
- ctx->commits.nr);
+ trace2_bloom_filter_settings(ctx);
- hashwrite_be32(f, settings->hash_version);
- hashwrite_be32(f, settings->num_hashes);
- hashwrite_be32(f, settings->bits_per_entry);
+ hashwrite_be32(f, ctx->bloom_settings->hash_version);
+ hashwrite_be32(f, ctx->bloom_settings->num_hashes);
+ hashwrite_be32(f, ctx->bloom_settings->bits_per_entry);
while (list < last) {
struct bloom_filter *filter = get_bloom_filter(ctx->r, *list, 0);
- display_progress(progress, ++i);
- hashwrite(f, filter->data, filter->len * sizeof(unsigned char));
+ size_t len = filter ? filter->len : 0;
+
+ display_progress(ctx->progress, ++ctx->progress_cnt);
+ if (len)
+ hashwrite(f, filter->data, len * sizeof(unsigned char));
list++;
}
- stop_progress(&progress);
+ return 0;
}
static int oid_compare(const void *_a, const void *_b)
@@ -1220,7 +1295,7 @@ static void close_reachable(struct write_commit_graph_context *ctx)
continue;
if (ctx->split) {
if ((!parse_commit(commit) &&
- commit->graph_pos == COMMIT_NOT_FROM_GRAPH) ||
+ commit_graph_position(commit) == COMMIT_NOT_FROM_GRAPH) ||
flags == COMMIT_GRAPH_SPLIT_REPLACE)
add_missing_parents(ctx, commit);
} else if (!parse_commit_no_graph(commit))
@@ -1252,9 +1327,11 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
_("Computing commit graph generation numbers"),
ctx->commits.nr);
for (i = 0; i < ctx->commits.nr; i++) {
+ uint32_t generation = commit_graph_data_at(ctx->commits.list[i])->generation;
+
display_progress(ctx->progress, i + 1);
- if (ctx->commits.list[i]->generation != GENERATION_NUMBER_INFINITY &&
- ctx->commits.list[i]->generation != GENERATION_NUMBER_ZERO)
+ if (generation != GENERATION_NUMBER_INFINITY &&
+ generation != GENERATION_NUMBER_ZERO)
continue;
commit_list_insert(ctx->commits.list[i], &list);
@@ -1265,22 +1342,26 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
uint32_t max_generation = 0;
for (parent = current->parents; parent; parent = parent->next) {
- if (parent->item->generation == GENERATION_NUMBER_INFINITY ||
- parent->item->generation == GENERATION_NUMBER_ZERO) {
+ generation = commit_graph_data_at(parent->item)->generation;
+
+ if (generation == GENERATION_NUMBER_INFINITY ||
+ generation == GENERATION_NUMBER_ZERO) {
all_parents_computed = 0;
commit_list_insert(parent->item, &list);
break;
- } else if (parent->item->generation > max_generation) {
- max_generation = parent->item->generation;
+ } else if (generation > max_generation) {
+ max_generation = generation;
}
}
if (all_parents_computed) {
- current->generation = max_generation + 1;
+ struct commit_graph_data *data = commit_graph_data_at(current);
+
+ data->generation = max_generation + 1;
pop_commit(&list);
- if (current->generation > GENERATION_NUMBER_MAX)
- current->generation = GENERATION_NUMBER_MAX;
+ if (data->generation > GENERATION_NUMBER_MAX)
+ data->generation = GENERATION_NUMBER_MAX;
}
}
}
@@ -1319,13 +1400,25 @@ static void compute_bloom_filters(struct write_commit_graph_context *ctx)
stop_progress(&progress);
}
+struct refs_cb_data {
+ struct oidset *commits;
+ struct progress *progress;
+};
+
static int add_ref_to_set(const char *refname,
const struct object_id *oid,
int flags, void *cb_data)
{
- struct oidset *commits = (struct oidset *)cb_data;
+ struct object_id peeled;
+ struct refs_cb_data *data = (struct refs_cb_data *)cb_data;
+
+ if (!peel_ref(refname, &peeled))
+ oid = &peeled;
+ if (oid_object_info(the_repository, oid, NULL) == OBJ_COMMIT)
+ oidset_insert(data->commits, oid);
+
+ display_progress(data->progress, oidset_size(data->commits));
- oidset_insert(commits, oid);
return 0;
}
@@ -1334,9 +1427,19 @@ int write_commit_graph_reachable(struct object_directory *odb,
const struct split_commit_graph_opts *split_opts)
{
struct oidset commits = OIDSET_INIT;
+ struct refs_cb_data data;
int result;
- for_each_ref(add_ref_to_set, &commits);
+ memset(&data, 0, sizeof(data));
+ data.commits = &commits;
+ if (flags & COMMIT_GRAPH_WRITE_PROGRESS)
+ data.progress = start_delayed_progress(
+ _("Collecting referenced commits"), 0);
+
+ for_each_ref(add_ref_to_set, &data);
+
+ stop_progress(&data.progress);
+
result = write_commit_graph(odb, NULL, &commits,
flags, split_opts);
@@ -1392,46 +1495,19 @@ static int fill_oids_from_packs(struct write_commit_graph_context *ctx,
static int fill_oids_from_commits(struct write_commit_graph_context *ctx,
struct oidset *commits)
{
- uint32_t i = 0;
- struct strbuf progress_title = STRBUF_INIT;
struct oidset_iter iter;
struct object_id *oid;
if (!oidset_size(commits))
return 0;
- if (ctx->report_progress) {
- strbuf_addf(&progress_title,
- Q_("Finding commits for commit graph from %d ref",
- "Finding commits for commit graph from %d refs",
- oidset_size(commits)),
- oidset_size(commits));
- ctx->progress = start_delayed_progress(
- progress_title.buf,
- oidset_size(commits));
- }
-
oidset_iter_init(commits, &iter);
while ((oid = oidset_iter_next(&iter))) {
- struct commit *result;
-
- display_progress(ctx->progress, ++i);
-
- result = lookup_commit_reference_gently(ctx->r, oid, 1);
- if (result) {
- ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
- oidcpy(&ctx->oids.list[ctx->oids.nr], &(result->object.oid));
- ctx->oids.nr++;
- } else if (ctx->check_oids) {
- error(_("invalid commit object id: %s"),
- oid_to_hex(oid));
- return -1;
- }
+ ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
+ oidcpy(&ctx->oids.list[ctx->oids.nr], oid);
+ ctx->oids.nr++;
}
- stop_progress(&ctx->progress);
- strbuf_release(&progress_title);
-
return 0;
}
@@ -1465,7 +1541,7 @@ static uint32_t count_distinct_commits(struct write_commit_graph_context *ctx)
if (ctx->split) {
struct commit *c = lookup_commit(ctx->r, &ctx->oids.list[i]);
- if (!c || c->graph_pos != COMMIT_NOT_FROM_GRAPH)
+ if (!c || commit_graph_position(c) != COMMIT_NOT_FROM_GRAPH)
continue;
}
@@ -1499,7 +1575,7 @@ static void copy_oids_to_commits(struct write_commit_graph_context *ctx)
ctx->commits.list[ctx->commits.nr] = lookup_commit(ctx->r, &ctx->oids.list[i]);
if (ctx->split && flags != COMMIT_GRAPH_SPLIT_REPLACE &&
- ctx->commits.list[ctx->commits.nr]->graph_pos != COMMIT_NOT_FROM_GRAPH)
+ commit_graph_position(ctx->commits.list[ctx->commits.nr]) != COMMIT_NOT_FROM_GRAPH)
continue;
if (ctx->split && flags == COMMIT_GRAPH_SPLIT_REPLACE)
@@ -1542,19 +1618,36 @@ static int write_graph_chunk_base(struct hashfile *f,
return 0;
}
+typedef int (*chunk_write_fn)(struct hashfile *f,
+ struct write_commit_graph_context *ctx);
+
+struct chunk_info {
+ uint32_t id;
+ uint64_t size;
+ chunk_write_fn write_fn;
+};
+
static int write_commit_graph_file(struct write_commit_graph_context *ctx)
{
uint32_t i;
int fd;
struct hashfile *f;
struct lock_file lk = LOCK_INIT;
- uint32_t chunk_ids[MAX_NUM_CHUNKS + 1];
- uint64_t chunk_offsets[MAX_NUM_CHUNKS + 1];
+ struct chunk_info chunks[MAX_NUM_CHUNKS + 1];
const unsigned hashsz = the_hash_algo->rawsz;
struct strbuf progress_title = STRBUF_INIT;
int num_chunks = 3;
+ uint64_t chunk_offset;
struct object_id file_hash;
- const struct bloom_filter_settings bloom_settings = DEFAULT_BLOOM_FILTER_SETTINGS;
+ struct bloom_filter_settings bloom_settings = DEFAULT_BLOOM_FILTER_SETTINGS;
+
+ if (!ctx->bloom_settings) {
+ bloom_settings.bits_per_entry = git_env_ulong("GIT_TEST_BLOOM_SETTINGS_BITS_PER_ENTRY",
+ bloom_settings.bits_per_entry);
+ bloom_settings.num_hashes = git_env_ulong("GIT_TEST_BLOOM_SETTINGS_NUM_HASHES",
+ bloom_settings.num_hashes);
+ ctx->bloom_settings = &bloom_settings;
+ }
if (ctx->split) {
struct strbuf tmp_file = STRBUF_INIT;
@@ -1600,51 +1693,41 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
}
- chunk_ids[0] = GRAPH_CHUNKID_OIDFANOUT;
- chunk_ids[1] = GRAPH_CHUNKID_OIDLOOKUP;
- chunk_ids[2] = GRAPH_CHUNKID_DATA;
+ chunks[0].id = GRAPH_CHUNKID_OIDFANOUT;
+ chunks[0].size = GRAPH_FANOUT_SIZE;
+ chunks[0].write_fn = write_graph_chunk_fanout;
+ chunks[1].id = GRAPH_CHUNKID_OIDLOOKUP;
+ chunks[1].size = hashsz * ctx->commits.nr;
+ chunks[1].write_fn = write_graph_chunk_oids;
+ chunks[2].id = GRAPH_CHUNKID_DATA;
+ chunks[2].size = (hashsz + 16) * ctx->commits.nr;
+ chunks[2].write_fn = write_graph_chunk_data;
if (ctx->num_extra_edges) {
- chunk_ids[num_chunks] = GRAPH_CHUNKID_EXTRAEDGES;
+ chunks[num_chunks].id = GRAPH_CHUNKID_EXTRAEDGES;
+ chunks[num_chunks].size = 4 * ctx->num_extra_edges;
+ chunks[num_chunks].write_fn = write_graph_chunk_extra_edges;
num_chunks++;
}
if (ctx->changed_paths) {
- chunk_ids[num_chunks] = GRAPH_CHUNKID_BLOOMINDEXES;
+ chunks[num_chunks].id = GRAPH_CHUNKID_BLOOMINDEXES;
+ chunks[num_chunks].size = sizeof(uint32_t) * ctx->commits.nr;
+ chunks[num_chunks].write_fn = write_graph_chunk_bloom_indexes;
num_chunks++;
- chunk_ids[num_chunks] = GRAPH_CHUNKID_BLOOMDATA;
+ chunks[num_chunks].id = GRAPH_CHUNKID_BLOOMDATA;
+ chunks[num_chunks].size = sizeof(uint32_t) * 3
+ + ctx->total_bloom_filter_data_size;
+ chunks[num_chunks].write_fn = write_graph_chunk_bloom_data;
num_chunks++;
}
if (ctx->num_commit_graphs_after > 1) {
- chunk_ids[num_chunks] = GRAPH_CHUNKID_BASE;
+ chunks[num_chunks].id = GRAPH_CHUNKID_BASE;
+ chunks[num_chunks].size = hashsz * (ctx->num_commit_graphs_after - 1);
+ chunks[num_chunks].write_fn = write_graph_chunk_base;
num_chunks++;
}
- chunk_ids[num_chunks] = 0;
-
- chunk_offsets[0] = 8 + (num_chunks + 1) * GRAPH_CHUNKLOOKUP_WIDTH;
- chunk_offsets[1] = chunk_offsets[0] + GRAPH_FANOUT_SIZE;
- chunk_offsets[2] = chunk_offsets[1] + hashsz * ctx->commits.nr;
- chunk_offsets[3] = chunk_offsets[2] + (hashsz + 16) * ctx->commits.nr;
-
- num_chunks = 3;
- if (ctx->num_extra_edges) {
- chunk_offsets[num_chunks + 1] = chunk_offsets[num_chunks] +
- 4 * ctx->num_extra_edges;
- num_chunks++;
- }
- if (ctx->changed_paths) {
- chunk_offsets[num_chunks + 1] = chunk_offsets[num_chunks] +
- sizeof(uint32_t) * ctx->commits.nr;
- num_chunks++;
-
- chunk_offsets[num_chunks + 1] = chunk_offsets[num_chunks] +
- sizeof(uint32_t) * 3 + ctx->total_bloom_filter_data_size;
- num_chunks++;
- }
- if (ctx->num_commit_graphs_after > 1) {
- chunk_offsets[num_chunks + 1] = chunk_offsets[num_chunks] +
- hashsz * (ctx->num_commit_graphs_after - 1);
- num_chunks++;
- }
+ chunks[num_chunks].id = 0;
+ chunks[num_chunks].size = 0;
hashwrite_be32(f, GRAPH_SIGNATURE);
@@ -1653,13 +1736,16 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
hashwrite_u8(f, num_chunks);
hashwrite_u8(f, ctx->num_commit_graphs_after - 1);
+ chunk_offset = 8 + (num_chunks + 1) * GRAPH_CHUNKLOOKUP_WIDTH;
for (i = 0; i <= num_chunks; i++) {
uint32_t chunk_write[3];
- chunk_write[0] = htonl(chunk_ids[i]);
- chunk_write[1] = htonl(chunk_offsets[i] >> 32);
- chunk_write[2] = htonl(chunk_offsets[i] & 0xffffffff);
+ chunk_write[0] = htonl(chunks[i].id);
+ chunk_write[1] = htonl(chunk_offset >> 32);
+ chunk_write[2] = htonl(chunk_offset & 0xffffffff);
hashwrite(f, chunk_write, 12);
+
+ chunk_offset += chunks[i].size;
}
if (ctx->report_progress) {
@@ -1672,19 +1758,19 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
progress_title.buf,
num_chunks * ctx->commits.nr);
}
- write_graph_chunk_fanout(f, ctx);
- write_graph_chunk_oids(f, hashsz, ctx);
- write_graph_chunk_data(f, hashsz, ctx);
- if (ctx->num_extra_edges)
- write_graph_chunk_extra_edges(f, ctx);
- if (ctx->changed_paths) {
- write_graph_chunk_bloom_indexes(f, ctx);
- write_graph_chunk_bloom_data(f, ctx, &bloom_settings);
- }
- if (ctx->num_commit_graphs_after > 1 &&
- write_graph_chunk_base(f, ctx)) {
- return -1;
+
+ for (i = 0; i < num_chunks; i++) {
+ uint64_t start_offset = f->total + f->offset;
+
+ if (chunks[i].write_fn(f, ctx))
+ return -1;
+
+ if (f->total + f->offset != start_offset + chunks[i].size)
+ BUG("expected to write %"PRId64" bytes to chunk %"PRIx32", but wrote %"PRId64" instead",
+ chunks[i].size, chunks[i].id,
+ f->total + f->offset - start_offset);
}
+
stop_progress(&ctx->progress);
strbuf_release(&progress_title);
@@ -2017,11 +2103,24 @@ int write_commit_graph(struct object_directory *odb,
ctx->append = flags & COMMIT_GRAPH_WRITE_APPEND ? 1 : 0;
ctx->report_progress = flags & COMMIT_GRAPH_WRITE_PROGRESS ? 1 : 0;
ctx->split = flags & COMMIT_GRAPH_WRITE_SPLIT ? 1 : 0;
- ctx->check_oids = flags & COMMIT_GRAPH_WRITE_CHECK_OIDS ? 1 : 0;
ctx->split_opts = split_opts;
- ctx->changed_paths = flags & COMMIT_GRAPH_WRITE_BLOOM_FILTERS ? 1 : 0;
ctx->total_bloom_filter_data_size = 0;
+ if (flags & COMMIT_GRAPH_WRITE_BLOOM_FILTERS)
+ ctx->changed_paths = 1;
+ if (!(flags & COMMIT_GRAPH_NO_WRITE_BLOOM_FILTERS)) {
+ struct commit_graph *g;
+ prepare_commit_graph_one(ctx->r, ctx->odb);
+
+ g = ctx->r->objects->commit_graph;
+
+ /* We have changed-paths already. Keep them in the next graph */
+ if (g && g->chunk_bloom_data) {
+ ctx->changed_paths = 1;
+ ctx->bloom_settings = g->bloom_filter_settings;
+ }
+ }
+
if (ctx->split) {
struct commit_graph *g;
prepare_commit_graph(ctx->r);
@@ -2249,6 +2348,7 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
struct commit *graph_commit, *odb_commit;
struct commit_list *graph_parents, *odb_parents;
uint32_t max_generation = 0;
+ uint32_t generation;
display_progress(progress, i + 1);
hashcpy(cur_oid.hash, g->chunk_oid_lookup + g->hash_len * i);
@@ -2287,8 +2387,9 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
oid_to_hex(&graph_parents->item->object.oid),
oid_to_hex(&odb_parents->item->object.oid));
- if (graph_parents->item->generation > max_generation)
- max_generation = graph_parents->item->generation;
+ generation = commit_graph_generation(graph_parents->item);
+ if (generation > max_generation)
+ max_generation = generation;
graph_parents = graph_parents->next;
odb_parents = odb_parents->next;
@@ -2298,7 +2399,7 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
graph_report(_("commit-graph parent list for commit %s terminates early"),
oid_to_hex(&cur_oid));
- if (!graph_commit->generation) {
+ if (!commit_graph_generation(graph_commit)) {
if (generation_zero == GENERATION_NUMBER_EXISTS)
graph_report(_("commit-graph has generation number zero for commit %s, but non-zero elsewhere"),
oid_to_hex(&cur_oid));
@@ -2318,10 +2419,11 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
if (max_generation == GENERATION_NUMBER_MAX)
max_generation--;
- if (graph_commit->generation != max_generation + 1)
+ generation = commit_graph_generation(graph_commit);
+ if (generation != max_generation + 1)
graph_report(_("commit-graph generation for commit %s is %u != %u"),
oid_to_hex(&cur_oid),
- graph_commit->generation,
+ generation,
max_generation + 1);
if (graph_commit->date != odb_commit->date)
diff --git a/commit-graph.h b/commit-graph.h
index 4212766..09a9703 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -2,14 +2,11 @@
#define COMMIT_GRAPH_H
#include "git-compat-util.h"
-#include "repository.h"
-#include "string-list.h"
-#include "cache.h"
#include "object-store.h"
#include "oidset.h"
#define GIT_TEST_COMMIT_GRAPH "GIT_TEST_COMMIT_GRAPH"
-#define GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD "GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD"
+#define GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE "GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE"
#define GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS "GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS"
/*
@@ -23,6 +20,9 @@ void git_test_write_commit_graph_or_die(void);
struct commit;
struct bloom_filter_settings;
+struct repository;
+struct raw_object_store;
+struct string_list;
char *get_commit_graph_filename(struct object_directory *odb);
int open_commit_graph(const char *graph_file, int *fd, struct stat *st);
@@ -91,9 +91,8 @@ enum commit_graph_write_flags {
COMMIT_GRAPH_WRITE_APPEND = (1 << 0),
COMMIT_GRAPH_WRITE_PROGRESS = (1 << 1),
COMMIT_GRAPH_WRITE_SPLIT = (1 << 2),
- /* Make sure that each OID in the input is a valid commit OID. */
- COMMIT_GRAPH_WRITE_CHECK_OIDS = (1 << 3),
- COMMIT_GRAPH_WRITE_BLOOM_FILTERS = (1 << 4),
+ COMMIT_GRAPH_WRITE_BLOOM_FILTERS = (1 << 3),
+ COMMIT_GRAPH_NO_WRITE_BLOOM_FILTERS = (1 << 4),
};
enum commit_graph_split_flags {
@@ -137,4 +136,14 @@ void free_commit_graph(struct commit_graph *);
*/
void disable_commit_graph(struct repository *r);
+struct commit_graph_data {
+ uint32_t graph_pos;
+ uint32_t generation;
+};
+
+/*
+ * Commits should be parsed before accessing generation, graph positions.
+ */
+uint32_t commit_graph_generation(const struct commit *);
+uint32_t commit_graph_position(const struct commit *);
#endif
diff --git a/commit-reach.c b/commit-reach.c
index 4ca7e70..efd5925 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -58,14 +58,15 @@ static struct commit_list *paint_down_to_common(struct repository *r,
struct commit *commit = prio_queue_get(&queue);
struct commit_list *parents;
int flags;
+ uint32_t generation = commit_graph_generation(commit);
- if (min_generation && commit->generation > last_gen)
+ if (min_generation && generation > last_gen)
BUG("bad generation skip %8x > %8x at %s",
- commit->generation, last_gen,
+ generation, last_gen,
oid_to_hex(&commit->object.oid));
- last_gen = commit->generation;
+ last_gen = generation;
- if (commit->generation < min_generation)
+ if (generation < min_generation)
break;
flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
@@ -176,18 +177,20 @@ static int remove_redundant(struct repository *r, struct commit **array, int cnt
repo_parse_commit(r, array[i]);
for (i = 0; i < cnt; i++) {
struct commit_list *common;
- uint32_t min_generation = array[i]->generation;
+ uint32_t min_generation = commit_graph_generation(array[i]);
if (redundant[i])
continue;
for (j = filled = 0; j < cnt; j++) {
+ uint32_t curr_generation;
if (i == j || redundant[j])
continue;
filled_index[filled] = j;
work[filled++] = array[j];
- if (array[j]->generation < min_generation)
- min_generation = array[j]->generation;
+ curr_generation = commit_graph_generation(array[j]);
+ if (curr_generation < min_generation)
+ min_generation = curr_generation;
}
common = paint_down_to_common(r, array[i], filled,
work, min_generation);
@@ -283,7 +286,9 @@ struct commit_list *repo_get_merge_bases(struct repository *r,
/*
* Is "commit" a descendant of one of the elements on the "with_commit" list?
*/
-int is_descendant_of(struct commit *commit, struct commit_list *with_commit)
+int repo_is_descendant_of(struct repository *r,
+ struct commit *commit,
+ struct commit_list *with_commit)
{
if (!with_commit)
return 1;
@@ -301,7 +306,7 @@ int is_descendant_of(struct commit *commit, struct commit_list *with_commit)
other = with_commit->item;
with_commit = with_commit->next;
- if (in_merge_bases(other, commit))
+ if (repo_in_merge_bases_many(r, other, 1, &commit))
return 1;
}
return 0;
@@ -316,23 +321,26 @@ int repo_in_merge_bases_many(struct repository *r, struct commit *commit,
{
struct commit_list *bases;
int ret = 0, i;
- uint32_t min_generation = GENERATION_NUMBER_INFINITY;
+ uint32_t generation, min_generation = GENERATION_NUMBER_INFINITY;
if (repo_parse_commit(r, commit))
return ret;
for (i = 0; i < nr_reference; i++) {
if (repo_parse_commit(r, reference[i]))
return ret;
- if (reference[i]->generation < min_generation)
- min_generation = reference[i]->generation;
+
+ generation = commit_graph_generation(reference[i]);
+ if (generation < min_generation)
+ min_generation = generation;
}
- if (commit->generation > min_generation)
+ generation = commit_graph_generation(commit);
+ if (generation > min_generation)
return ret;
bases = paint_down_to_common(r, commit,
nr_reference, reference,
- commit->generation);
+ generation);
if (commit->object.flags & PARENT2)
ret = 1;
clear_commit_marks(commit, all_flags);
@@ -348,7 +356,15 @@ int repo_in_merge_bases(struct repository *r,
struct commit *commit,
struct commit *reference)
{
- return repo_in_merge_bases_many(r, commit, 1, &reference);
+ int res;
+ struct commit_list *list = NULL;
+ struct commit_list **next = &list;
+
+ next = commit_list_append(commit, next);
+ res = repo_is_descendant_of(r, reference, list);
+ free_commit_list(list);
+
+ return res;
}
struct commit_list *reduce_heads(struct commit_list *heads)
@@ -396,6 +412,7 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid)
struct object *o;
struct commit *old_commit, *new_commit;
struct commit_list *old_commit_list = NULL;
+ int ret;
/*
* Both new_commit and old_commit must be commit-ish and new_commit is descendant of
@@ -417,7 +434,10 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid)
return 0;
commit_list_insert(old_commit, &old_commit_list);
- return is_descendant_of(new_commit, old_commit_list);
+ ret = repo_is_descendant_of(the_repository,
+ new_commit, old_commit_list);
+ free_commit_list(old_commit_list);
+ return ret;
}
/*
@@ -467,7 +487,7 @@ static enum contains_result contains_test(struct commit *candidate,
/* Otherwise, we don't know; prepare to recurse */
parse_commit_or_die(candidate);
- if (candidate->generation < cutoff)
+ if (commit_graph_generation(candidate) < cutoff)
return CONTAINS_NO;
return CONTAINS_UNKNOWN;
@@ -490,10 +510,12 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
const struct commit_list *p;
for (p = want; p; p = p->next) {
+ uint32_t generation;
struct commit *c = p->item;
load_commit_graph_info(the_repository, c);
- if (c->generation < cutoff)
- cutoff = c->generation;
+ generation = commit_graph_generation(c);
+ if (generation < cutoff)
+ cutoff = generation;
}
result = contains_test(candidate, want, cache, cutoff);
@@ -536,7 +558,7 @@ int commit_contains(struct ref_filter *filter, struct commit *commit,
{
if (filter->with_commit_tag_algo)
return contains_tag_algo(commit, list, cache) == CONTAINS_YES;
- return is_descendant_of(commit, list);
+ return repo_is_descendant_of(the_repository, commit, list);
}
static int compare_commits_by_gen(const void *_a, const void *_b)
@@ -544,9 +566,12 @@ static int compare_commits_by_gen(const void *_a, const void *_b)
const struct commit *a = *(const struct commit * const *)_a;
const struct commit *b = *(const struct commit * const *)_b;
- if (a->generation < b->generation)
+ uint32_t generation_a = commit_graph_generation(a);
+ uint32_t generation_b = commit_graph_generation(b);
+
+ if (generation_a < generation_b)
return -1;
- if (a->generation > b->generation)
+ if (generation_a > generation_b)
return 1;
return 0;
}
@@ -585,7 +610,7 @@ int can_all_from_reach_with_flag(struct object_array *from,
list[nr_commits] = (struct commit *)from_one;
if (parse_commit(list[nr_commits]) ||
- list[nr_commits]->generation < min_generation) {
+ commit_graph_generation(list[nr_commits]) < min_generation) {
result = 0;
goto cleanup;
}
@@ -621,7 +646,7 @@ int can_all_from_reach_with_flag(struct object_array *from,
if (parse_commit(parent->item) ||
parent->item->date < min_commit_date ||
- parent->item->generation < min_generation)
+ commit_graph_generation(parent->item) < min_generation)
continue;
commit_list_insert(parent->item, &stack);
@@ -662,11 +687,13 @@ int can_all_from_reach(struct commit_list *from, struct commit_list *to,
add_object_array(&from_iter->item->object, NULL, &from_objs);
if (!parse_commit(from_iter->item)) {
+ uint32_t generation;
if (from_iter->item->date < min_commit_date)
min_commit_date = from_iter->item->date;
- if (from_iter->item->generation < min_generation)
- min_generation = from_iter->item->generation;
+ generation = commit_graph_generation(from_iter->item);
+ if (generation < min_generation)
+ min_generation = generation;
}
from_iter = from_iter->next;
@@ -674,11 +701,13 @@ int can_all_from_reach(struct commit_list *from, struct commit_list *to,
while (to_iter) {
if (!parse_commit(to_iter->item)) {
+ uint32_t generation;
if (to_iter->item->date < min_commit_date)
min_commit_date = to_iter->item->date;
- if (to_iter->item->generation < min_generation)
- min_generation = to_iter->item->generation;
+ generation = commit_graph_generation(to_iter->item);
+ if (generation < min_generation)
+ min_generation = generation;
}
to_iter->item->object.flags |= PARENT2;
@@ -718,11 +747,13 @@ struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
for (item = to; item < to_last; item++) {
+ uint32_t generation;
struct commit *c = *item;
parse_commit(c);
- if (c->generation < min_generation)
- min_generation = c->generation;
+ generation = commit_graph_generation(c);
+ if (generation < min_generation)
+ min_generation = generation;
if (!(c->object.flags & PARENT1)) {
c->object.flags |= PARENT1;
@@ -755,7 +786,7 @@ struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
parse_commit(p);
- if (p->generation < min_generation)
+ if (commit_graph_generation(p) < min_generation)
continue;
if (p->object.flags & PARENT2)
diff --git a/commit-reach.h b/commit-reach.h
index 99a43e8..b49ad71 100644
--- a/commit-reach.h
+++ b/commit-reach.h
@@ -27,7 +27,9 @@ struct commit_list *repo_get_merge_bases_many_dirty(struct repository *r,
struct commit_list *get_octopus_merge_bases(struct commit_list *in);
-int is_descendant_of(struct commit *commit, struct commit_list *with_commit);
+int repo_is_descendant_of(struct repository *r,
+ struct commit *commit,
+ struct commit_list *with_commit);
int repo_in_merge_bases(struct repository *r,
struct commit *commit,
struct commit *reference);
diff --git a/commit-slab-decl.h b/commit-slab-decl.h
index bfbed15..98de2c9 100644
--- a/commit-slab-decl.h
+++ b/commit-slab-decl.h
@@ -32,6 +32,7 @@ struct slabname { \
void init_ ##slabname## _with_stride(struct slabname *s, unsigned stride); \
void init_ ##slabname(struct slabname *s); \
void clear_ ##slabname(struct slabname *s); \
+void deep_clear_ ##slabname(struct slabname *s, void (*free_fn)(elemtype *ptr)); \
elemtype *slabname## _at_peek(struct slabname *s, const struct commit *c, int add_if_missing); \
elemtype *slabname## _at(struct slabname *s, const struct commit *c); \
elemtype *slabname## _peek(struct slabname *s, const struct commit *c)
diff --git a/commit-slab-impl.h b/commit-slab-impl.h
index 5c0eb91..557738d 100644
--- a/commit-slab-impl.h
+++ b/commit-slab-impl.h
@@ -38,6 +38,19 @@ scope void clear_ ##slabname(struct slabname *s) \
FREE_AND_NULL(s->slab); \
} \
\
+scope void deep_clear_ ##slabname(struct slabname *s, void (*free_fn)(elemtype *)) \
+{ \
+ unsigned int i; \
+ for (i = 0; i < s->slab_count; i++) { \
+ unsigned int j; \
+ if (!s->slab[i]) \
+ continue; \
+ for (j = 0; j < s->slab_size; j++) \
+ free_fn(&s->slab[i][j * s->stride]); \
+ } \
+ clear_ ##slabname(s); \
+} \
+ \
scope elemtype *slabname## _at_peek(struct slabname *s, \
const struct commit *c, \
int add_if_missing) \
diff --git a/commit-slab.h b/commit-slab.h
index 05b3f28..8e72a30 100644
--- a/commit-slab.h
+++ b/commit-slab.h
@@ -47,6 +47,16 @@
*
* Call this function before the slab falls out of scope to avoid
* leaking memory.
+ *
+ * - void deep_clear_indegree(struct indegree *, void (*free_fn)(int*))
+ *
+ * Empties the slab, similar to clear_indegree(), but in addition it
+ * calls the given 'free_fn' for each slab entry to release any
+ * additional memory that might be owned by the entry (but not the
+ * entry itself!).
+ * Note that 'free_fn' might be called even for entries for which no
+ * indegree_at() call has been made; in this case 'free_fn' is invoked
+ * with a pointer to a zero-initialized location.
*/
#define define_commit_slab(slabname, elemtype) \
diff --git a/commit.c b/commit.c
index 87686a7..7128895 100644
--- a/commit.c
+++ b/commit.c
@@ -37,7 +37,7 @@ struct commit *lookup_commit_reference_gently(struct repository *r,
if (!obj)
return NULL;
- return object_as_type(r, obj, OBJ_COMMIT, quiet);
+ return object_as_type(obj, OBJ_COMMIT, quiet);
}
struct commit *lookup_commit_reference(struct repository *r, const struct object_id *oid)
@@ -62,7 +62,7 @@ struct commit *lookup_commit(struct repository *r, const struct object_id *oid)
struct object *obj = lookup_object(r, oid);
if (!obj)
return create_object(r, oid, alloc_commit_node(r));
- return object_as_type(r, obj, OBJ_COMMIT, 0);
+ return object_as_type(obj, OBJ_COMMIT, 0);
}
struct commit *lookup_commit_reference_by_name(const char *name)
@@ -339,7 +339,7 @@ struct tree *repo_get_commit_tree(struct repository *r,
if (commit->maybe_tree || !commit->object.parsed)
return commit->maybe_tree;
- if (commit->graph_pos != COMMIT_NOT_FROM_GRAPH)
+ if (commit_graph_position(commit) != COMMIT_NOT_FROM_GRAPH)
return get_commit_tree_in_graph(r, commit);
return NULL;
@@ -423,6 +423,8 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
pptr = &item->parents;
graft = lookup_commit_graft(r, &item->object.oid);
+ if (graft)
+ r->parsed_objects->substituted_parent = 1;
while (bufptr + parent_entry_len < tail && !memcmp(bufptr, "parent ", 7)) {
struct commit *new_parent;
@@ -729,11 +731,13 @@ int compare_commits_by_author_date(const void *a_, const void *b_,
int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused)
{
const struct commit *a = a_, *b = b_;
+ const uint32_t generation_a = commit_graph_generation(a),
+ generation_b = commit_graph_generation(b);
/* newer commits first */
- if (a->generation < b->generation)
+ if (generation_a < generation_b)
return 1;
- else if (a->generation > b->generation)
+ else if (generation_a > generation_b)
return -1;
/* use date as a heuristic when generations are equal */
diff --git a/commit.h b/commit.h
index 1b2dea5..e901538 100644
--- a/commit.h
+++ b/commit.h
@@ -36,8 +36,6 @@ struct commit {
* or get_commit_tree_oid().
*/
struct tree *maybe_tree;
- uint32_t graph_pos;
- uint32_t generation;
unsigned int index;
};
diff --git a/compat/vcbuild/scripts/clink.pl b/compat/vcbuild/scripts/clink.pl
index d9f71b7..61ad084 100755
--- a/compat/vcbuild/scripts/clink.pl
+++ b/compat/vcbuild/scripts/clink.pl
@@ -23,7 +23,9 @@ while (@ARGV) {
# before any "-l*" flags.
$is_debug = 1;
}
- if ("$arg" =~ /^-[DIMGOZ]/) {
+ if ("$arg" =~ /^-I\/mingw(32|64)/) {
+ # eat
+ } elsif ("$arg" =~ /^-[DIMGOZ]/) {
push(@cflags, $arg);
} elsif ("$arg" eq "-o") {
my $file_out = shift @ARGV;
diff --git a/connect.c b/connect.c
index 23013c6..e0d5b9f 100644
--- a/connect.c
+++ b/connect.c
@@ -18,7 +18,7 @@
static char *server_capabilities_v1;
static struct argv_array server_capabilities_v2 = ARGV_ARRAY_INIT;
-static const char *parse_feature_value(const char *, const char *, int *);
+static const char *next_server_feature_value(const char *feature, int *len, int *offset);
static int check_ref(const char *name, unsigned int flags)
{
@@ -83,6 +83,21 @@ int server_supports_v2(const char *c, int die_on_error)
return 0;
}
+int server_feature_v2(const char *c, const char **v)
+{
+ int i;
+
+ for (i = 0; i < server_capabilities_v2.argc; i++) {
+ const char *out;
+ if (skip_prefix(server_capabilities_v2.argv[i], c, &out) &&
+ (*out == '=')) {
+ *v = out + 1;
+ return 1;
+ }
+ }
+ return 0;
+}
+
int server_supports_feature(const char *c, const char *feature,
int die_on_error)
{
@@ -127,6 +142,7 @@ enum protocol_version discover_version(struct packet_reader *reader)
die_initial_contact(0);
case PACKET_READ_FLUSH:
case PACKET_READ_DELIM:
+ case PACKET_READ_RESPONSE_END:
version = protocol_v0;
break;
case PACKET_READ_NORMAL:
@@ -180,17 +196,16 @@ reject:
static void annotate_refs_with_symref_info(struct ref *ref)
{
struct string_list symref = STRING_LIST_INIT_DUP;
- const char *feature_list = server_capabilities_v1;
+ int offset = 0;
- while (feature_list) {
+ while (1) {
int len;
const char *val;
- val = parse_feature_value(feature_list, "symref", &len);
+ val = next_server_feature_value("symref", &len, &offset);
if (!val)
break;
parse_one_symref_info(&symref, val, len);
- feature_list = val + 1;
}
string_list_sort(&symref);
@@ -204,21 +219,36 @@ static void annotate_refs_with_symref_info(struct ref *ref)
string_list_clear(&symref, 0);
}
-static void process_capabilities(const char *line, int *len)
+static void process_capabilities(struct packet_reader *reader, int *linelen)
{
+ const char *feat_val;
+ int feat_len;
+ const char *line = reader->line;
int nul_location = strlen(line);
- if (nul_location == *len)
+ if (nul_location == *linelen)
return;
server_capabilities_v1 = xstrdup(line + nul_location + 1);
- *len = nul_location;
+ *linelen = nul_location;
+
+ feat_val = server_feature_value("object-format", &feat_len);
+ if (feat_val) {
+ char *hash_name = xstrndup(feat_val, feat_len);
+ int hash_algo = hash_algo_by_name(hash_name);
+ if (hash_algo != GIT_HASH_UNKNOWN)
+ reader->hash_algo = &hash_algos[hash_algo];
+ free(hash_name);
+ } else {
+ reader->hash_algo = &hash_algos[GIT_HASH_SHA1];
+ }
}
-static int process_dummy_ref(const char *line)
+static int process_dummy_ref(const struct packet_reader *reader)
{
+ const char *line = reader->line;
struct object_id oid;
const char *name;
- if (parse_oid_hex(line, &oid, &name))
+ if (parse_oid_hex_algop(line, &oid, &name, reader->hash_algo))
return 0;
if (*name != ' ')
return 0;
@@ -234,13 +264,15 @@ static void check_no_capabilities(const char *line, int len)
line + strlen(line));
}
-static int process_ref(const char *line, int len, struct ref ***list,
- unsigned int flags, struct oid_array *extra_have)
+static int process_ref(const struct packet_reader *reader, int len,
+ struct ref ***list, unsigned int flags,
+ struct oid_array *extra_have)
{
+ const char *line = reader->line;
struct object_id old_oid;
const char *name;
- if (parse_oid_hex(line, &old_oid, &name))
+ if (parse_oid_hex_algop(line, &old_oid, &name, reader->hash_algo))
return 0;
if (*name != ' ')
return 0;
@@ -260,16 +292,17 @@ static int process_ref(const char *line, int len, struct ref ***list,
return 1;
}
-static int process_shallow(const char *line, int len,
+static int process_shallow(const struct packet_reader *reader, int len,
struct oid_array *shallow_points)
{
+ const char *line = reader->line;
const char *arg;
struct object_id old_oid;
if (!skip_prefix(line, "shallow ", &arg))
return 0;
- if (get_oid_hex(arg, &old_oid))
+ if (get_oid_hex_algop(arg, &old_oid, reader->hash_algo))
die(_("protocol error: expected shallow sha-1, got '%s'"), arg);
if (!shallow_points)
die(_("repository on the other end cannot be shallow"));
@@ -310,25 +343,26 @@ struct ref **get_remote_heads(struct packet_reader *reader,
state = EXPECTING_DONE;
break;
case PACKET_READ_DELIM:
+ case PACKET_READ_RESPONSE_END:
die(_("invalid packet"));
}
switch (state) {
case EXPECTING_FIRST_REF:
- process_capabilities(reader->line, &len);
- if (process_dummy_ref(reader->line)) {
+ process_capabilities(reader, &len);
+ if (process_dummy_ref(reader)) {
state = EXPECTING_SHALLOW;
break;
}
state = EXPECTING_REF;
/* fallthrough */
case EXPECTING_REF:
- if (process_ref(reader->line, len, &list, flags, extra_have))
+ if (process_ref(reader, len, &list, flags, extra_have))
break;
state = EXPECTING_SHALLOW;
/* fallthrough */
case EXPECTING_SHALLOW:
- if (process_shallow(reader->line, len, shallow_points))
+ if (process_shallow(reader, len, shallow_points))
break;
die(_("protocol error: unexpected '%s'"), reader->line);
case EXPECTING_DONE:
@@ -342,7 +376,7 @@ struct ref **get_remote_heads(struct packet_reader *reader,
}
/* Returns 1 when a valid ref has been added to `list`, 0 otherwise */
-static int process_ref_v2(const char *line, struct ref ***list)
+static int process_ref_v2(struct packet_reader *reader, struct ref ***list)
{
int ret = 1;
int i = 0;
@@ -350,6 +384,7 @@ static int process_ref_v2(const char *line, struct ref ***list)
struct ref *ref;
struct string_list line_sections = STRING_LIST_INIT_DUP;
const char *end;
+ const char *line = reader->line;
/*
* Ref lines have a number of fields which are space deliminated. The
@@ -362,7 +397,7 @@ static int process_ref_v2(const char *line, struct ref ***list)
goto out;
}
- if (parse_oid_hex(line_sections.items[i++].string, &old_oid, &end) ||
+ if (parse_oid_hex_algop(line_sections.items[i++].string, &old_oid, &end, reader->hash_algo) ||
*end) {
ret = 0;
goto out;
@@ -370,7 +405,7 @@ static int process_ref_v2(const char *line, struct ref ***list)
ref = alloc_ref(line_sections.items[i++].string);
- oidcpy(&ref->old_oid, &old_oid);
+ memcpy(ref->old_oid.hash, old_oid.hash, reader->hash_algo->rawsz);
**list = ref;
*list = &ref->next;
@@ -383,7 +418,8 @@ static int process_ref_v2(const char *line, struct ref ***list)
struct object_id peeled_oid;
char *peeled_name;
struct ref *peeled;
- if (parse_oid_hex(arg, &peeled_oid, &end) || *end) {
+ if (parse_oid_hex_algop(arg, &peeled_oid, &end,
+ reader->hash_algo) || *end) {
ret = 0;
goto out;
}
@@ -391,7 +427,8 @@ static int process_ref_v2(const char *line, struct ref ***list)
peeled_name = xstrfmt("%s^{}", ref->name);
peeled = alloc_ref(peeled_name);
- oidcpy(&peeled->old_oid, &peeled_oid);
+ memcpy(peeled->old_oid.hash, peeled_oid.hash,
+ reader->hash_algo->rawsz);
**list = peeled;
*list = &peeled->next;
@@ -404,12 +441,24 @@ out:
return ret;
}
+void check_stateless_delimiter(int stateless_rpc,
+ struct packet_reader *reader,
+ const char *error)
+{
+ if (!stateless_rpc)
+ return; /* not in stateless mode, no delimiter expected */
+ if (packet_reader_read(reader) != PACKET_READ_RESPONSE_END)
+ die("%s", error);
+}
+
struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
struct ref **list, int for_push,
const struct argv_array *ref_prefixes,
- const struct string_list *server_options)
+ const struct string_list *server_options,
+ int stateless_rpc)
{
int i;
+ const char *hash_name;
*list = NULL;
if (server_supports_v2("ls-refs", 1))
@@ -418,6 +467,16 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
if (server_supports_v2("agent", 0))
packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized());
+ if (server_feature_v2("object-format", &hash_name)) {
+ int hash_algo = hash_algo_by_name(hash_name);
+ if (hash_algo == GIT_HASH_UNKNOWN)
+ die(_("unknown object format '%s' specified by server"), hash_name);
+ 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];
+ }
+
if (server_options && server_options->nr &&
server_supports_v2("server-option", 1))
for (i = 0; i < server_options->nr; i++)
@@ -437,17 +496,20 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
/* Process response from server */
while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
- if (!process_ref_v2(reader->line, &list))
+ if (!process_ref_v2(reader, &list))
die(_("invalid ls-refs response: %s"), reader->line);
}
if (reader->status != PACKET_READ_FLUSH)
die(_("expected flush after ref listing"));
+ check_stateless_delimiter(stateless_rpc, reader,
+ _("expected response end packet after ref listing"));
+
return list;
}
-static const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp)
+const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp, int *offset)
{
int len;
@@ -455,6 +517,8 @@ static const char *parse_feature_value(const char *feature_list, const char *fea
return NULL;
len = strlen(feature);
+ if (offset)
+ feature_list += *offset;
while (*feature_list) {
const char *found = strstr(feature_list, feature);
if (!found)
@@ -469,9 +533,14 @@ static const char *parse_feature_value(const char *feature_list, const char *fea
}
/* feature with a value (e.g., "agent=git/1.2.3") */
else if (*value == '=') {
+ int end;
+
value++;
+ end = strcspn(value, " \t\n");
if (lenp)
- *lenp = strcspn(value, " \t\n");
+ *lenp = end;
+ if (offset)
+ *offset = value + end - feature_list;
return value;
}
/*
@@ -484,14 +553,41 @@ static const char *parse_feature_value(const char *feature_list, const char *fea
return NULL;
}
+int server_supports_hash(const char *desired, int *feature_supported)
+{
+ int offset = 0;
+ int len;
+ const char *hash;
+
+ hash = next_server_feature_value("object-format", &len, &offset);
+ if (feature_supported)
+ *feature_supported = !!hash;
+ if (!hash) {
+ hash = hash_algos[GIT_HASH_SHA1].name;
+ len = strlen(hash);
+ }
+ while (hash) {
+ if (!xstrncmpz(desired, hash, len))
+ return 1;
+
+ hash = next_server_feature_value("object-format", &len, &offset);
+ }
+ return 0;
+}
+
int parse_feature_request(const char *feature_list, const char *feature)
{
- return !!parse_feature_value(feature_list, feature, NULL);
+ return !!parse_feature_value(feature_list, feature, NULL, NULL);
+}
+
+static const char *next_server_feature_value(const char *feature, int *len, int *offset)
+{
+ return parse_feature_value(server_capabilities_v1, feature, len, offset);
}
const char *server_feature_value(const char *feature, int *len)
{
- return parse_feature_value(server_capabilities_v1, feature, len);
+ return parse_feature_value(server_capabilities_v1, feature, len, NULL);
}
int server_supports(const char *feature)
diff --git a/connect.h b/connect.h
index 5f2382e..c53586e 100644
--- a/connect.h
+++ b/connect.h
@@ -18,8 +18,15 @@ int url_is_local_not_ssh(const char *url);
struct packet_reader;
enum protocol_version discover_version(struct packet_reader *reader);
+int server_supports_hash(const char *desired, int *feature_supported);
+const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp, int *offset);
int server_supports_v2(const char *c, int die_on_error);
+int server_feature_v2(const char *c, const char **v);
int server_supports_feature(const char *c, const char *feature,
int die_on_error);
+void check_stateless_delimiter(int stateless_rpc,
+ struct packet_reader *reader,
+ const char *error);
+
#endif
diff --git a/connected.c b/connected.c
index 3135b71..937b4ba 100644
--- a/connected.c
+++ b/connected.c
@@ -43,10 +43,12 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
if (transport && transport->smart_options &&
transport->smart_options->self_contained_and_connected &&
- transport->pack_lockfile &&
- strip_suffix(transport->pack_lockfile, ".keep", &base_len)) {
+ transport->pack_lockfiles.nr == 1 &&
+ strip_suffix(transport->pack_lockfiles.items[0].string,
+ ".keep", &base_len)) {
struct strbuf idx_file = STRBUF_INIT;
- strbuf_add(&idx_file, transport->pack_lockfile, base_len);
+ strbuf_add(&idx_file, transport->pack_lockfiles.items[0].string,
+ base_len);
strbuf_addstr(&idx_file, ".idx");
new_pack = add_packed_git(idx_file.buf, idx_file.len, 1);
strbuf_release(&idx_file);
diff --git a/contrib/coccinelle/commit.cocci b/contrib/coccinelle/commit.cocci
index 778e470..af6dd4c 100644
--- a/contrib/coccinelle/commit.cocci
+++ b/contrib/coccinelle/commit.cocci
@@ -32,3 +32,21 @@ expression c;
- c->maybe_tree
+ repo_get_commit_tree(specify_the_right_repo_here, c)
...>}
+
+@@
+struct commit *c;
+expression E;
+@@
+(
+- c->generation = E;
++ commit_graph_data_at(c)->generation = E;
+|
+- c->graph_pos = E;
++ commit_graph_data_at(c)->graph_pos = E;
+|
+- c->generation
++ commit_graph_generation(c)
+|
+- c->graph_pos
++ commit_graph_position(c)
+)
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 70ad04e..0fdb5da 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -50,7 +50,7 @@ esac
# variable.
__git_find_repo_path ()
{
- if [ -n "$__git_repo_path" ]; then
+ if [ -n "${__git_repo_path-}" ]; then
# we already know where it is
return
fi
@@ -301,6 +301,19 @@ __gitcomp_direct ()
COMPREPLY=($1)
}
+# Similar to __gitcomp_direct, but appends to COMPREPLY instead.
+# Callers must take care of providing only words that match the current word
+# to be completed and adding any prefix and/or suffix (trailing space!), if
+# necessary.
+# 1: List of newline-separated matching completion words, complete with
+# prefix and suffix.
+__gitcomp_direct_append ()
+{
+ local IFS=$'\n'
+
+ COMPREPLY+=($1)
+}
+
__gitcompappend ()
{
local x i=${#COMPREPLY[@]}
@@ -373,7 +386,7 @@ __gitcomp ()
# Clear the variables caching builtins' options when (re-)sourcing
# the completion script.
if [[ -n ${ZSH_VERSION-} ]]; then
- unset $(set |sed -ne 's/^\(__gitcomp_builtin_[a-zA-Z0-9_][a-zA-Z0-9_]*\)=.*/\1/p') 2>/dev/null
+ unset ${(M)${(k)parameters[@]}:#__gitcomp_builtin_*} 2>/dev/null
else
unset $(compgen -v __gitcomp_builtin_)
fi
@@ -391,12 +404,12 @@ __gitcomp_builtin ()
# spaces must be replaced with underscore for multi-word
# commands, e.g. "git remote add" becomes remote_add.
local cmd="$1"
- local incl="$2"
- local excl="$3"
+ local incl="${2-}"
+ local excl="${3-}"
local var=__gitcomp_builtin_"${cmd/-/_}"
local options
- eval "options=\$$var"
+ eval "options=\${$var-}"
if [ -z "$options" ]; then
# leading and trailing spaces are significant to make
@@ -611,6 +624,19 @@ __git_heads ()
"refs/heads/$cur_*" "refs/heads/$cur_*/**"
}
+# Lists branches from remote repositories.
+# 1: A prefix to be added to each listed branch (optional).
+# 2: List only branches matching this word (optional; list all branches if
+# unset or empty).
+# 3: A suffix to be appended to each listed branch (optional).
+__git_remote_heads ()
+{
+ local pfx="${1-}" cur_="${2-}" sfx="${3-}"
+
+ __git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \
+ "refs/remotes/$cur_*" "refs/remotes/$cur_*/**"
+}
+
# Lists tags from the local repository.
# Accepts the same positional parameters as __git_heads() above.
__git_tags ()
@@ -621,6 +647,26 @@ __git_tags ()
"refs/tags/$cur_*" "refs/tags/$cur_*/**"
}
+# List unique branches from refs/remotes used for 'git checkout' and 'git
+# switch' tracking DWIMery.
+# 1: A prefix to be added to each listed branch (optional)
+# 2: List only branches matching this word (optional; list all branches if
+# unset or empty).
+# 3: A suffix to be appended to each listed branch (optional).
+__git_dwim_remote_heads ()
+{
+ local pfx="${1-}" cur_="${2-}" sfx="${3-}"
+ local fer_pfx="${pfx//\%/%%}" # "escape" for-each-ref format specifiers
+
+ # employ the heuristic used by git checkout and git switch
+ # Try to find a remote branch that cur_es the completion word
+ # but only output if the branch name is unique
+ __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \
+ --sort="refname:strip=3" \
+ "refs/remotes/*/$cur_*" "refs/remotes/*/$cur_*/**" | \
+ uniq -u
+}
+
# Lists refs from the local (by default) or from a remote repository.
# It accepts 0, 1 or 2 arguments:
# 1: The remote to list refs from (optional; ignored, if set but empty).
@@ -696,13 +742,7 @@ __git_refs ()
__git_dir="$dir" __git for-each-ref --format="$fer_pfx%($format)$sfx" \
"${refs[@]}"
if [ -n "$track" ]; then
- # employ the heuristic used by git checkout
- # Try to find a remote branch that matches the completion word
- # but only output if the branch name is unique
- __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \
- --sort="refname:strip=3" \
- "refs/remotes/*/$match*" "refs/remotes/*/$match*/**" | \
- uniq -u
+ __git_dwim_remote_heads "$pfx" "$match" "$sfx"
fi
return
fi
@@ -749,29 +789,51 @@ __git_refs ()
# Usage: __git_complete_refs [<option>]...
# --remote=<remote>: The remote to list refs from, can be the name of a
# configured remote, a path, or a URL.
-# --track: List unique remote branches for 'git checkout's tracking DWIMery.
+# --dwim: List unique remote branches for 'git switch's tracking DWIMery.
# --pfx=<prefix>: A prefix to be added to each ref.
# --cur=<word>: The current ref to be completed. Defaults to the current
# word to be completed.
# --sfx=<suffix>: A suffix to be appended to each ref instead of the default
# space.
+# --mode=<mode>: What set of refs to complete, one of 'refs' (the default) to
+# complete all refs, 'heads' to complete only branches, or
+# 'remote-heads' to complete only remote branches. Note that
+# --remote is only compatible with --mode=refs.
__git_complete_refs ()
{
- local remote track pfx cur_="$cur" sfx=" "
+ local remote= dwim= pfx= cur_="$cur" sfx=" " mode="refs"
while test $# != 0; do
case "$1" in
--remote=*) remote="${1##--remote=}" ;;
- --track) track="yes" ;;
+ --dwim) dwim="yes" ;;
+ # --track is an old spelling of --dwim
+ --track) dwim="yes" ;;
--pfx=*) pfx="${1##--pfx=}" ;;
--cur=*) cur_="${1##--cur=}" ;;
--sfx=*) sfx="${1##--sfx=}" ;;
+ --mode=*) mode="${1##--mode=}" ;;
*) return 1 ;;
esac
shift
done
- __gitcomp_direct "$(__git_refs "$remote" "$track" "$pfx" "$cur_" "$sfx")"
+ # complete references based on the specified mode
+ case "$mode" in
+ refs)
+ __gitcomp_direct "$(__git_refs "$remote" "" "$pfx" "$cur_" "$sfx")" ;;
+ heads)
+ __gitcomp_direct "$(__git_heads "$pfx" "$cur_" "$sfx")" ;;
+ remote-heads)
+ __gitcomp_direct "$(__git_remote_heads "$pfx" "$cur_" "$sfx")" ;;
+ *)
+ return 1 ;;
+ esac
+
+ # Append DWIM remote branch names if requested
+ if [ "$dwim" = "yes" ]; then
+ __gitcomp_direct_append "$(__git_dwim_remote_heads "$pfx" "$cur_" "$sfx")"
+ fi
}
# __git_refs2 requires 1 argument (to pass to __git_refs)
@@ -1090,7 +1152,7 @@ __git_find_on_cmdline ()
while [ $c -lt $cword ]; do
for word in $wordlist; do
if [ "$word" = "${words[c]}" ]; then
- if [ -n "$show_idx" ]; then
+ if [ -n "${show_idx-}" ]; then
echo "$c $word"
else
echo "$word"
@@ -1102,6 +1164,40 @@ __git_find_on_cmdline ()
done
}
+# Similar to __git_find_on_cmdline, except that it loops backwards and thus
+# prints the *last* word found. Useful for finding which of two options that
+# supersede each other came last, such as "--guess" and "--no-guess".
+#
+# Usage: __git_find_last_on_cmdline [<option>]... "<wordlist>"
+# --show-idx: Optionally show the index of the found word in the $words array.
+__git_find_last_on_cmdline ()
+{
+ local word c=$cword show_idx
+
+ while test $# -gt 1; do
+ case "$1" in
+ --show-idx) show_idx=y ;;
+ *) return 1 ;;
+ esac
+ shift
+ done
+ local wordlist="$1"
+
+ while [ $c -gt 1 ]; do
+ ((c--))
+ for word in $wordlist; do
+ if [ "$word" = "${words[c]}" ]; then
+ if [ -n "$show_idx" ]; then
+ echo "$c $word"
+ else
+ echo "$word"
+ fi
+ return
+ fi
+ done
+ done
+}
+
# Echo the value of an option set on the command line or config
#
# $1: short option name
@@ -1356,6 +1452,46 @@ _git_bundle ()
esac
}
+# Helper function to decide whether or not we should enable DWIM logic for
+# git-switch and git-checkout.
+#
+# To decide between the following rules in priority order
+# 1) the last provided of "--guess" or "--no-guess" explicitly enable or
+# disable completion of DWIM logic respectively.
+# 2) If the --no-track option is provided, take this as a hint to disable the
+# DWIM completion logic
+# 3) If GIT_COMPLETION_CHECKOUT_NO_GUESS is set, disable the DWIM completion
+# logic, as requested by the user.
+# 4) Enable DWIM logic otherwise.
+#
+__git_checkout_default_dwim_mode ()
+{
+ local last_option dwim_opt="--dwim"
+
+ if [ "${GIT_COMPLETION_CHECKOUT_NO_GUESS-}" = "1" ]; then
+ dwim_opt=""
+ fi
+
+ # --no-track disables DWIM, but with lower priority than
+ # --guess/--no-guess
+ if [ -n "$(__git_find_on_cmdline "--no-track")" ]; then
+ dwim_opt=""
+ fi
+
+ # Find the last provided --guess or --no-guess
+ last_option="$(__git_find_last_on_cmdline "--guess --no-guess")"
+ case "$last_option" in
+ --guess)
+ dwim_opt="--dwim"
+ ;;
+ --no-guess)
+ dwim_opt=""
+ ;;
+ esac
+
+ echo "$dwim_opt"
+}
+
_git_checkout ()
{
__git_has_doubledash && return
@@ -1368,14 +1504,38 @@ _git_checkout ()
__gitcomp_builtin checkout
;;
*)
- # check if --track, --no-track, or --no-guess was specified
- # if so, disable DWIM mode
- local flags="--track --no-track --no-guess" track_opt="--track"
- if [ "$GIT_COMPLETION_CHECKOUT_NO_GUESS" = "1" ] ||
- [ -n "$(__git_find_on_cmdline "$flags")" ]; then
- track_opt=''
+ local dwim_opt="$(__git_checkout_default_dwim_mode)"
+ local prevword prevword="${words[cword-1]}"
+
+ case "$prevword" in
+ -b|-B|--orphan)
+ # Complete local branches (and DWIM branch
+ # remote branch names) for an option argument
+ # specifying a new branch name. This is for
+ # convenience, assuming new branches are
+ # possibly based on pre-existing branch names.
+ __git_complete_refs $dwim_opt --mode="heads"
+ return
+ ;;
+ *)
+ ;;
+ esac
+
+ # At this point, we've already handled special completion for
+ # the arguments to -b/-B, and --orphan. There are 3 main
+ # things left we can possibly complete:
+ # 1) a start-point for -b/-B, -d/--detach, or --orphan
+ # 2) a remote head, for --track
+ # 3) an arbitrary reference, possibly including DWIM names
+ #
+
+ if [ -n "$(__git_find_on_cmdline "-b -B -d --detach --orphan")" ]; then
+ __git_complete_refs --mode="refs"
+ elif [ -n "$(__git_find_on_cmdline "--track")" ]; then
+ __git_complete_refs --mode="remote-heads"
+ else
+ __git_complete_refs $dwim_opt --mode="refs"
fi
- __git_complete_refs $track_opt
;;
esac
}
@@ -2224,29 +2384,43 @@ _git_switch ()
__gitcomp_builtin switch
;;
*)
- # check if --track, --no-track, or --no-guess was specified
- # if so, disable DWIM mode
- local track_opt="--track" only_local_ref=n
- if [ "$GIT_COMPLETION_CHECKOUT_NO_GUESS" = "1" ] ||
- [ -n "$(__git_find_on_cmdline "--track --no-track --no-guess")" ]; then
- track_opt=''
- fi
- # explicit --guess enables DWIM mode regardless of
- # $GIT_COMPLETION_CHECKOUT_NO_GUESS
- if [ -n "$(__git_find_on_cmdline "--guess")" ]; then
- track_opt='--track'
- fi
- if [ -z "$(__git_find_on_cmdline "-d --detach")" ]; then
- only_local_ref=y
- else
- # --guess --detach is invalid combination, no
- # dwim will be done when --detach is specified
- track_opt=
+ local dwim_opt="$(__git_checkout_default_dwim_mode)"
+ local prevword prevword="${words[cword-1]}"
+
+ case "$prevword" in
+ -c|-C|--orphan)
+ # Complete local branches (and DWIM branch
+ # remote branch names) for an option argument
+ # specifying a new branch name. This is for
+ # convenience, assuming new branches are
+ # possibly based on pre-existing branch names.
+ __git_complete_refs $dwim_opt --mode="heads"
+ return
+ ;;
+ *)
+ ;;
+ esac
+
+ # Unlike in git checkout, git switch --orphan does not take
+ # a start point. Thus we really have nothing to complete after
+ # the branch name.
+ if [ -n "$(__git_find_on_cmdline "--orphan")" ]; then
+ return
fi
- if [ $only_local_ref = y -a -z "$track_opt" ]; then
- __gitcomp_direct "$(__git_heads "" "$cur" " ")"
+
+ # At this point, we've already handled special completion for
+ # -c/-C, and --orphan. There are 3 main things left to
+ # complete:
+ # 1) a start-point for -c/-C or -d/--detach
+ # 2) a remote head, for --track
+ # 3) a branch name, possibly including DWIM remote branches
+
+ if [ -n "$(__git_find_on_cmdline "-c -C -d --detach")" ]; then
+ __git_complete_refs --mode="refs"
+ elif [ -n "$(__git_find_on_cmdline "--track")" ]; then
+ __git_complete_refs --mode="remote-heads"
else
- __git_complete_refs $track_opt
+ __git_complete_refs $dwim_opt --mode="heads"
fi
;;
esac
@@ -2733,6 +2907,14 @@ _git_show ()
__gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}"
return
;;
+ --color-moved=*)
+ __gitcomp "$__git_color_moved_opts" "" "${cur##--color-moved=}"
+ return
+ ;;
+ --color-moved-ws=*)
+ __gitcomp "$__git_color_moved_ws_opts" "" "${cur##--color-moved-ws=}"
+ return
+ ;;
--*)
__gitcomp "--pretty= --format= --abbrev-commit --no-abbrev-commit
--oneline --show-signature --patch
@@ -2782,7 +2964,7 @@ _git_stash ()
local save_opts='--all --keep-index --no-keep-index --quiet --patch --include-untracked'
local subcommands='push list show apply clear drop pop create branch'
local subcommand="$(__git_find_on_cmdline "$subcommands save")"
- if [ -n "$(__git_find_on_cmdline "-p")" ]; then
+ if [ -z "$subcommand" -a -n "$(__git_find_on_cmdline "-p")" ]; then
subcommand="push"
fi
if [ -z "$subcommand" ]; then
@@ -3176,7 +3358,7 @@ __git_main ()
((c++))
done
- if [ -z "$command" ]; then
+ if [ -z "${command-}" ]; then
case "$prev" in
--git-dir|-C|--work-tree)
# these need a path argument, let's fall back to
@@ -3211,7 +3393,7 @@ __git_main ()
"
;;
*)
- if test -n "$GIT_TESTING_PORCELAIN_COMMAND_LIST"
+ if test -n "${GIT_TESTING_PORCELAIN_COMMAND_LIST-}"
then
__gitcomp "$GIT_TESTING_PORCELAIN_COMMAND_LIST"
else
diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index 014cd7c..16260ba 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -70,6 +70,15 @@
# state symbols by setting GIT_PS1_STATESEPARATOR. The default separator
# is SP.
#
+# When there is an in-progress operation such as a merge, rebase,
+# revert, cherry-pick, or bisect, the prompt will include information
+# related to the operation, often in the form "|<OPERATION-NAME>".
+#
+# When the repository has a sparse-checkout, a notification of the form
+# "|SPARSE" will be included in the prompt. This can be shortened to a
+# single '?' character by setting GIT_PS1_COMPRESSSPARSESTATE, or omitted
+# by setting GIT_PS1_OMITSPARSESTATE.
+#
# By default, __git_ps1 will compare HEAD to your SVN upstream if it can
# find one, or @{upstream} otherwise. Once you have set
# GIT_PS1_SHOWUPSTREAM, you can override it on a per-repository basis by
@@ -421,6 +430,13 @@ __git_ps1 ()
return $exit
fi
+ local sparse=""
+ if [ -z "${GIT_PS1_COMPRESSSPARSESTATE}" ] &&
+ [ -z "${GIT_PS1_OMITSPARSESTATE}" ] &&
+ [ "$(git config --bool core.sparseCheckout)" = "true" ]; then
+ sparse="|SPARSE"
+ fi
+
local r=""
local b=""
local step=""
@@ -492,6 +508,7 @@ __git_ps1 ()
local i=""
local s=""
local u=""
+ local h=""
local c=""
local p=""
@@ -524,6 +541,11 @@ __git_ps1 ()
u="%${ZSH_VERSION+%}"
fi
+ if [ -n "${GIT_PS1_COMPRESSSPARSESTATE}" ] &&
+ [ "$(git config --bool core.sparseCheckout)" = "true" ]; then
+ h="?"
+ fi
+
if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
__git_ps1_show_upstream
fi
@@ -542,8 +564,8 @@ __git_ps1 ()
b="\${__git_ps1_branch_name}"
fi
- local f="$w$i$s$u"
- local gitstring="$c$b${f:+$z$f}$r$p"
+ local f="$h$w$i$s$u"
+ local gitstring="$c$b${f:+$z$f}${sparse}$r$p"
if [ $pcmode = yes ]; then
if [ "${__git_printf_supports_v-}" != yes ]; then
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
index 57ff4b2..53d7acc 100755
--- a/contrib/subtree/t/t7900-subtree.sh
+++ b/contrib/subtree/t/t7900-subtree.sh
@@ -196,7 +196,8 @@ test_expect_success 'merge new subproj history into sub dir/ with --prefix' '
cd "$subtree_test_count" &&
git fetch ./"sub proj" master &&
git subtree merge --prefix="sub dir" FETCH_HEAD &&
- check_equal "$(last_commit_message)" "Merge commit '\''$(git rev-parse FETCH_HEAD)'\''"
+ check_equal "$(last_commit_message)" \
+ "Merge commit '\''$(git rev-parse FETCH_HEAD)'\'' into master"
)
'
@@ -273,7 +274,8 @@ test_expect_success 'merge new subproj history into subdir/ with a slash appende
cd "$test_count" &&
git fetch ./subproj master &&
git subtree merge --prefix=subdir/ FETCH_HEAD &&
- check_equal "$(last_commit_message)" "Merge commit '\''$(git rev-parse FETCH_HEAD)'\''"
+ check_equal "$(last_commit_message)" \
+ "Merge commit '\''$(git rev-parse FETCH_HEAD)'\'' into master"
)
'
diff --git a/diff-lib.c b/diff-lib.c
index 61812f4..25fd2de 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -220,8 +220,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
} else if (revs->diffopt.ita_invisible_in_index &&
ce_intent_to_add(ce)) {
diff_addremove(&revs->diffopt, '+', ce->ce_mode,
- the_hash_algo->empty_tree, 0,
- ce->name, 0);
+ &null_oid, 0, ce->name, 0);
continue;
}
diff --git a/diff.c b/diff.c
index d1ad6a3..d24aaa3 100644
--- a/diff.c
+++ b/diff.c
@@ -48,6 +48,7 @@ static const char *diff_order_file_cfg;
int diff_auto_refresh_index = 1;
static int diff_mnemonic_prefix;
static int diff_no_prefix;
+static int diff_relative;
static int diff_stat_graph_width;
static int diff_dirstat_permille_default = 30;
static struct diff_options default_diff_options;
@@ -386,6 +387,10 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
diff_no_prefix = git_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "diff.relative")) {
+ diff_relative = git_config_bool(var, value);
+ return 0;
+ }
if (!strcmp(var, "diff.statgraphwidth")) {
diff_stat_graph_width = git_config_int(var, value);
return 0;
@@ -4538,6 +4543,7 @@ void repo_diff_setup(struct repository *r, struct diff_options *options)
options->interhunkcontext = diff_interhunk_context_default;
options->ws_error_highlight = ws_error_highlight_default;
options->flags.rename_empty = 1;
+ options->flags.relative_name = diff_relative;
options->objfind = NULL;
/* pathchange left =NULL by default */
@@ -5195,8 +5201,7 @@ static int diff_opt_relative(const struct option *opt,
{
struct diff_options *options = opt->value;
- BUG_ON_OPT_NEG(unset);
- options->flags.relative_name = 1;
+ options->flags.relative_name = !unset;
if (arg)
options->prefix = arg;
return 0;
@@ -5492,7 +5497,7 @@ static void prep_parse_options(struct diff_options *options)
OPT_GROUP(N_("Other diff options")),
OPT_CALLBACK_F(0, "relative", options, N_("<prefix>"),
N_("when run from subdir, exclude changes outside and show relative paths"),
- PARSE_OPT_NONEG | PARSE_OPT_OPTARG,
+ PARSE_OPT_OPTARG,
diff_opt_relative),
OPT_BOOL('a', "text", &options->flags.text,
N_("treat all files as text")),
@@ -6758,8 +6763,11 @@ void diff_change(struct diff_options *options,
return;
if (options->flags.quick && options->skip_stat_unmatch &&
- !diff_filespec_check_stat_unmatch(options->repo, p))
+ !diff_filespec_check_stat_unmatch(options->repo, p)) {
+ diff_free_filespec_data(p->one);
+ diff_free_filespec_data(p->two);
return;
+ }
options->flags.has_changes = 1;
}
diff --git a/diff.h b/diff.h
index 9443dc1..e0c0af6 100644
--- a/diff.h
+++ b/diff.h
@@ -431,11 +431,11 @@ struct combine_diff_path *diff_tree_paths(
struct combine_diff_path *p, const struct object_id *oid,
const struct object_id **parents_oid, int nparent,
struct strbuf *base, struct diff_options *opt);
-int diff_tree_oid(const struct object_id *old_oid,
- const struct object_id *new_oid,
- const char *base, struct diff_options *opt);
-int diff_root_tree_oid(const struct object_id *new_oid, const char *base,
- struct diff_options *opt);
+void diff_tree_oid(const struct object_id *old_oid,
+ const struct object_id *new_oid,
+ const char *base, struct diff_options *opt);
+void diff_root_tree_oid(const struct object_id *new_oid, const char *base,
+ struct diff_options *opt);
struct combine_diff_path {
struct combine_diff_path *next;
diff --git a/dir.c b/dir.c
index d97e955..fe64be3 100644
--- a/dir.c
+++ b/dir.c
@@ -193,6 +193,10 @@ int fill_directory(struct dir_struct *dir,
const char *prefix;
size_t prefix_len;
+ unsigned exclusive_flags = DIR_SHOW_IGNORED | DIR_SHOW_IGNORED_TOO;
+ if ((dir->flags & exclusive_flags) == exclusive_flags)
+ BUG("DIR_SHOW_IGNORED and DIR_SHOW_IGNORED_TOO are exclusive");
+
/*
* Calculate common prefix for the pathspec, and
* use that to optimize the directory walk
@@ -364,7 +368,8 @@ static int match_pathspec_item(const struct index_state *istate,
return MATCHED_FNMATCH;
/* Perform checks to see if "name" is a leading string of the pathspec */
- if (flags & DO_MATCH_LEADING_PATHSPEC) {
+ if ( (flags & DO_MATCH_LEADING_PATHSPEC) &&
+ !(flags & DO_MATCH_EXCLUDE)) {
/* name is a literal prefix of the pathspec */
int offset = name[namelen-1] == '/' ? 1 : 0;
if ((namelen < matchlen) &&
@@ -401,6 +406,10 @@ static int match_pathspec_item(const struct index_state *istate,
}
/*
+ * do_match_pathspec() is meant to ONLY be called by
+ * match_pathspec_with_flags(); calling it directly risks pathspecs
+ * like ':!unwanted_path' being ignored.
+ *
* Given a name and a list of pathspecs, returns the nature of the
* closest (i.e. most specific) match of the name to any of the
* pathspecs.
@@ -486,13 +495,12 @@ static int do_match_pathspec(const struct index_state *istate,
return retval;
}
-int match_pathspec(const struct index_state *istate,
- const struct pathspec *ps,
- const char *name, int namelen,
- int prefix, char *seen, int is_dir)
+static int match_pathspec_with_flags(const struct index_state *istate,
+ const struct pathspec *ps,
+ const char *name, int namelen,
+ int prefix, char *seen, unsigned flags)
{
int positive, negative;
- unsigned flags = is_dir ? DO_MATCH_DIRECTORY : 0;
positive = do_match_pathspec(istate, ps, name, namelen,
prefix, seen, flags);
if (!(ps->magic & PATHSPEC_EXCLUDE) || !positive)
@@ -503,6 +511,16 @@ int match_pathspec(const struct index_state *istate,
return negative ? 0 : positive;
}
+int match_pathspec(const 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 : 0;
+ return match_pathspec_with_flags(istate, ps, name, namelen,
+ prefix, seen, flags);
+}
+
/**
* Check if a submodule is a superset of the pathspec
*/
@@ -511,11 +529,11 @@ int submodule_path_match(const struct index_state *istate,
const char *submodule_name,
char *seen)
{
- int matched = do_match_pathspec(istate, ps, submodule_name,
- strlen(submodule_name),
- 0, seen,
- DO_MATCH_DIRECTORY |
- DO_MATCH_LEADING_PATHSPEC);
+ int matched = match_pathspec_with_flags(istate, ps, submodule_name,
+ strlen(submodule_name),
+ 0, seen,
+ DO_MATCH_DIRECTORY |
+ DO_MATCH_LEADING_PATHSPEC);
return matched;
}
@@ -1757,9 +1775,11 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
* for matching patterns.
*/
if (pathspec && !excluded) {
- matches_how = do_match_pathspec(istate, pathspec, dirname, len,
- 0 /* prefix */, NULL /* seen */,
- DO_MATCH_LEADING_PATHSPEC);
+ matches_how = match_pathspec_with_flags(istate, pathspec,
+ dirname, len,
+ 0 /* prefix */,
+ NULL /* seen */,
+ DO_MATCH_LEADING_PATHSPEC);
if (!matches_how)
return path_none;
}
@@ -1820,7 +1840,7 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
* to recurse into untracked/ignored directories if either of the
* following bits is set:
* - DIR_SHOW_IGNORED_TOO (because then we need to determine if
- * there are ignored directories below)
+ * there are ignored entries below)
* - DIR_HIDE_EMPTY_DIRECTORIES (because we have to determine if
* the directory is empty)
*/
@@ -1838,10 +1858,11 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
return path_excluded;
/*
- * If we have we don't want to know the all the paths under an
- * untracked or ignored directory, we still need to go into the
- * directory to determine if it is empty (because an empty directory
- * should be path_none instead of path_excluded or path_untracked).
+ * Even if we don't want to know all the paths under an untracked or
+ * ignored directory, we may still need to go into the directory to
+ * determine if it is empty (because with DIR_HIDE_EMPTY_DIRECTORIES,
+ * an empty directory should be path_none instead of path_excluded or
+ * path_untracked).
*/
check_only = ((dir->flags & DIR_HIDE_EMPTY_DIRECTORIES) &&
!(dir->flags & DIR_SHOW_IGNORED_TOO));
@@ -2188,13 +2209,13 @@ static enum path_treatment treat_path(struct dir_struct *dir,
baselen, excluded, pathspec);
case DT_REG:
case DT_LNK:
- if (excluded)
- return path_excluded;
if (pathspec &&
- !do_match_pathspec(istate, pathspec, path->buf, path->len,
- 0 /* prefix */, NULL /* seen */,
- 0 /* flags */))
+ !match_pathspec(istate, pathspec, path->buf, path->len,
+ 0 /* prefix */, NULL /* seen */,
+ 0 /* is_dir */))
return path_none;
+ if (excluded)
+ return path_excluded;
return path_untracked;
}
}
diff --git a/entry.c b/entry.c
index 00b4903..449bd32 100644
--- a/entry.c
+++ b/entry.c
@@ -113,8 +113,7 @@ static int fstat_output(int fd, const struct checkout *state, struct stat *st)
/* use fstat() only when path == ce->name */
if (fstat_is_reliable() &&
state->refresh_cache && !state->base_dir_len) {
- fstat(fd, st);
- return 1;
+ return !fstat(fd, st);
}
return 0;
}
diff --git a/fast-import.c b/fast-import.c
index c989702..0dfa14d 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -139,6 +139,7 @@ struct hash_list {
typedef enum {
WHENSPEC_RAW = 1,
+ WHENSPEC_RAW_PERMISSIVE,
WHENSPEC_RFC2822,
WHENSPEC_NOW
} whenspec_type;
@@ -1911,7 +1912,7 @@ static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
return 1;
}
-static int validate_raw_date(const char *src, struct strbuf *result)
+static int validate_raw_date(const char *src, struct strbuf *result, int strict)
{
const char *orig_src = src;
char *endp;
@@ -1920,7 +1921,11 @@ static int validate_raw_date(const char *src, struct strbuf *result)
errno = 0;
num = strtoul(src, &endp, 10);
- /* NEEDSWORK: perhaps check for reasonable values? */
+ /*
+ * NEEDSWORK: perhaps check for reasonable values? For example, we
+ * could error on values representing times more than a
+ * day in the future.
+ */
if (errno || endp == src || *endp != ' ')
return -1;
@@ -1929,7 +1934,13 @@ static int validate_raw_date(const char *src, struct strbuf *result)
return -1;
num = strtoul(src + 1, &endp, 10);
- if (errno || endp == src + 1 || *endp || 1400 < num)
+ /*
+ * NEEDSWORK: check for brokenness other than num > 1400, such as
+ * (num % 100) >= 60, or ((num % 100) % 15) != 0 ?
+ */
+ if (errno || endp == src + 1 || *endp || /* did not parse */
+ (strict && (1400 < num)) /* parsed a broken timezone */
+ )
return -1;
strbuf_addstr(result, orig_src);
@@ -1963,7 +1974,11 @@ static char *parse_ident(const char *buf)
switch (whenspec) {
case WHENSPEC_RAW:
- if (validate_raw_date(ltgt, &ident) < 0)
+ if (validate_raw_date(ltgt, &ident, 1) < 0)
+ die("Invalid raw date \"%s\" in ident: %s", ltgt, buf);
+ break;
+ case WHENSPEC_RAW_PERMISSIVE:
+ if (validate_raw_date(ltgt, &ident, 0) < 0)
die("Invalid raw date \"%s\" in ident: %s", ltgt, buf);
break;
case WHENSPEC_RFC2822:
@@ -3258,6 +3273,8 @@ static void option_date_format(const char *fmt)
{
if (!strcmp(fmt, "raw"))
whenspec = WHENSPEC_RAW;
+ else if (!strcmp(fmt, "raw-permissive"))
+ whenspec = WHENSPEC_RAW_PERMISSIVE;
else if (!strcmp(fmt, "rfc2822"))
whenspec = WHENSPEC_RFC2822;
else if (!strcmp(fmt, "now"))
diff --git a/fetch-pack.c b/fetch-pack.c
index 7eaa19d..80fb3bd 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -38,6 +38,7 @@ static int server_supports_filtering;
static struct shallow_lock shallow_lock;
static const char *alternate_shallow_file;
static struct strbuf fsck_msg_types = STRBUF_INIT;
+static struct string_list uri_protocols = STRING_LIST_INIT_DUP;
/* Remember to update object flag allocation in object.h */
#define COMPLETE (1U << 0)
@@ -794,7 +795,8 @@ static void write_promisor_file(const char *keep_name,
}
static int get_pack(struct fetch_pack_args *args,
- int xd[2], char **pack_lockfile,
+ int xd[2], struct string_list *pack_lockfiles,
+ int only_packfile,
struct ref **sought, int nr_sought)
{
struct async demux;
@@ -838,7 +840,7 @@ static int get_pack(struct fetch_pack_args *args,
}
if (do_keep || args->from_promisor) {
- if (pack_lockfile)
+ if (pack_lockfiles)
cmd.out = -1;
cmd_name = "index-pack";
argv_array_push(&cmd.args, cmd_name);
@@ -855,15 +857,22 @@ static int get_pack(struct fetch_pack_args *args,
"--keep=fetch-pack %"PRIuMAX " on %s",
(uintmax_t)getpid(), hostname);
}
- if (args->check_self_contained_and_connected)
+ if (only_packfile && args->check_self_contained_and_connected)
argv_array_push(&cmd.args, "--check-self-contained-and-connected");
+ else
+ /*
+ * We cannot perform any connectivity checks because
+ * not all packs have been downloaded; let the caller
+ * have this responsibility.
+ */
+ args->check_self_contained_and_connected = 0;
/*
* If we're obtaining the filename of a lockfile, we'll use
* that filename to write a .promisor file with more
* information below. If not, we need index-pack to do it for
* us.
*/
- if (!(do_keep && pack_lockfile) && args->from_promisor)
+ if (!(do_keep && pack_lockfiles) && args->from_promisor)
argv_array_push(&cmd.args, "--promisor");
}
else {
@@ -899,8 +908,9 @@ static int get_pack(struct fetch_pack_args *args,
cmd.git_cmd = 1;
if (start_command(&cmd))
die(_("fetch-pack: unable to fork off %s"), cmd_name);
- if (do_keep && pack_lockfile) {
- *pack_lockfile = index_pack_lockfile(cmd.out);
+ if (do_keep && pack_lockfiles) {
+ string_list_append_nodup(pack_lockfiles,
+ index_pack_lockfile(cmd.out));
close(cmd.out);
}
@@ -922,8 +932,8 @@ static int get_pack(struct fetch_pack_args *args,
* Now that index-pack has succeeded, write the promisor file using the
* obtained .keep filename if necessary
*/
- if (do_keep && pack_lockfile && args->from_promisor)
- write_promisor_file(*pack_lockfile, sought, nr_sought);
+ if (do_keep && pack_lockfiles && pack_lockfiles->nr && args->from_promisor)
+ write_promisor_file(pack_lockfiles->items[0].string, sought, nr_sought);
return 0;
}
@@ -940,7 +950,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
const struct ref *orig_ref,
struct ref **sought, int nr_sought,
struct shallow_info *si,
- char **pack_lockfile)
+ struct string_list *pack_lockfiles)
{
struct repository *r = the_repository;
struct ref *ref = copy_ref_list(orig_ref);
@@ -1040,6 +1050,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
print_verbose(args, _("Server supports %s"), "deepen-relative");
else if (args->deepen_relative)
die(_("Server does not support --deepen"));
+ if (!server_supports_hash(the_hash_algo->name, NULL))
+ die(_("Server does not support this repository's object format"));
if (!args->no_dependents) {
mark_complete_and_common_ref(negotiator, args, &ref);
@@ -1067,7 +1079,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
alternate_shallow_file = setup_temporary_shallow(si->shallow);
else
alternate_shallow_file = NULL;
- if (get_pack(args, fd, pack_lockfile, sought, nr_sought))
+ if (get_pack(args, fd, pack_lockfiles, 1, sought, nr_sought))
die(_("git fetch-pack: fetch failed."));
all_done:
@@ -1178,6 +1190,7 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
int sideband_all, int seen_ack)
{
int ret = 0;
+ const char *hash_name;
struct strbuf req_buf = STRBUF_INIT;
if (server_supports_v2("fetch", 1))
@@ -1192,6 +1205,17 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
args->server_options->items[i].string);
}
+ if (server_feature_v2("object-format", &hash_name)) {
+ int hash_algo = hash_algo_by_name(hash_name);
+ if (hash_algo_by_ptr(the_hash_algo) != hash_algo)
+ die(_("mismatched algorithms: client %s; server %s"),
+ the_hash_algo->name, hash_name);
+ packet_write_fmt(fd_out, "object-format=%s", the_hash_algo->name);
+ } else if (hash_algo_by_ptr(the_hash_algo) != GIT_HASH_SHA1) {
+ die(_("the server does not support algorithm '%s'"),
+ the_hash_algo->name);
+ }
+
packet_buf_delim(&req_buf);
if (args->use_thin_pack)
packet_buf_write(&req_buf, "thin-pack");
@@ -1221,6 +1245,26 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
warning("filtering not recognized by server, ignoring");
}
+ if (server_supports_feature("fetch", "packfile-uris", 0)) {
+ int i;
+ struct strbuf to_send = STRBUF_INIT;
+
+ for (i = 0; i < uri_protocols.nr; i++) {
+ const char *s = uri_protocols.items[i].string;
+
+ if (!strcmp(s, "https") || !strcmp(s, "http")) {
+ if (to_send.len)
+ strbuf_addch(&to_send, ',');
+ strbuf_addstr(&to_send, s);
+ }
+ }
+ if (to_send.len) {
+ packet_buf_write(&req_buf, "packfile-uris %s",
+ to_send.buf);
+ strbuf_release(&to_send);
+ }
+ }
+
/* add wants */
add_wants(args->no_dependents, wants, &req_buf);
@@ -1443,6 +1487,21 @@ static void receive_wanted_refs(struct packet_reader *reader,
die(_("error processing wanted refs: %d"), reader->status);
}
+static void receive_packfile_uris(struct packet_reader *reader,
+ struct string_list *uris)
+{
+ process_section_header(reader, "packfile-uris", 0);
+ while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
+ if (reader->pktlen < the_hash_algo->hexsz ||
+ reader->line[the_hash_algo->hexsz] != ' ')
+ die("expected '<hash> <uri>', got: %s\n", reader->line);
+
+ string_list_append(uris, reader->line);
+ }
+ if (reader->status != PACKET_READ_DELIM)
+ die("expected DELIM");
+}
+
enum fetch_state {
FETCH_CHECK_LOCAL = 0,
FETCH_SEND_REQUEST,
@@ -1451,13 +1510,20 @@ enum fetch_state {
FETCH_DONE,
};
+static void do_check_stateless_delimiter(const struct fetch_pack_args *args,
+ struct packet_reader *reader)
+{
+ check_stateless_delimiter(args->stateless_rpc, reader,
+ _("git fetch-pack: expected response end packet"));
+}
+
static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
int fd[2],
const struct ref *orig_ref,
struct ref **sought, int nr_sought,
struct oid_array *shallows,
struct shallow_info *si,
- char **pack_lockfile)
+ struct string_list *pack_lockfiles)
{
struct repository *r = the_repository;
struct ref *ref = copy_ref_list(orig_ref);
@@ -1469,6 +1535,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
struct fetch_negotiator negotiator_alloc;
struct fetch_negotiator *negotiator;
int seen_ack = 0;
+ struct string_list packfile_uris = STRING_LIST_INIT_DUP;
+ int i;
if (args->no_dependents) {
negotiator = NULL;
@@ -1535,6 +1603,10 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
/* Process ACKs/NAKs */
switch (process_acks(negotiator, &reader, &common)) {
case READY:
+ /*
+ * Don't check for response delimiter; get_pack() will
+ * read the rest of this response.
+ */
state = FETCH_GET_PACK;
break;
case COMMON_FOUND:
@@ -1542,6 +1614,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
seen_ack = 1;
/* fallthrough */
case NO_COMMON_FOUND:
+ do_check_stateless_delimiter(args, &reader);
state = FETCH_SEND_REQUEST;
break;
}
@@ -1557,10 +1630,14 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
if (process_section_header(&reader, "wanted-refs", 1))
receive_wanted_refs(&reader, sought, nr_sought);
- /* get the pack */
+ /* get the pack(s) */
+ if (process_section_header(&reader, "packfile-uris", 1))
+ receive_packfile_uris(&reader, &packfile_uris);
process_section_header(&reader, "packfile", 0);
- if (get_pack(args, fd, pack_lockfile, sought, nr_sought))
+ if (get_pack(args, fd, pack_lockfiles,
+ !packfile_uris.nr, sought, nr_sought))
die(_("git fetch-pack: fetch failed."));
+ do_check_stateless_delimiter(args, &reader);
state = FETCH_DONE;
break;
@@ -1569,8 +1646,55 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
}
}
+ for (i = 0; i < packfile_uris.nr; i++) {
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ char packname[GIT_MAX_HEXSZ + 1];
+ const char *uri = packfile_uris.items[i].string +
+ the_hash_algo->hexsz + 1;
+
+ argv_array_push(&cmd.args, "http-fetch");
+ argv_array_pushf(&cmd.args, "--packfile=%.*s",
+ (int) the_hash_algo->hexsz,
+ packfile_uris.items[i].string);
+ argv_array_push(&cmd.args, uri);
+ cmd.git_cmd = 1;
+ cmd.no_stdin = 1;
+ cmd.out = -1;
+ if (start_command(&cmd))
+ die("fetch-pack: unable to spawn http-fetch");
+
+ if (read_in_full(cmd.out, packname, 5) < 0 ||
+ memcmp(packname, "keep\t", 5))
+ die("fetch-pack: expected keep then TAB at start of http-fetch output");
+
+ if (read_in_full(cmd.out, packname,
+ the_hash_algo->hexsz + 1) < 0 ||
+ packname[the_hash_algo->hexsz] != '\n')
+ die("fetch-pack: expected hash then LF at end of http-fetch output");
+
+ packname[the_hash_algo->hexsz] = '\0';
+
+ close(cmd.out);
+
+ if (finish_command(&cmd))
+ die("fetch-pack: unable to finish http-fetch");
+
+ if (memcmp(packfile_uris.items[i].string, packname,
+ the_hash_algo->hexsz))
+ die("fetch-pack: pack downloaded from %s does not match expected hash %.*s",
+ uri, (int) the_hash_algo->hexsz,
+ packfile_uris.items[i].string);
+
+ string_list_append_nodup(pack_lockfiles,
+ xstrfmt("%s/pack/pack-%s.keep",
+ get_object_directory(),
+ packname));
+ }
+ string_list_clear(&packfile_uris, 0);
+
if (negotiator)
negotiator->release(negotiator);
+
oidset_clear(&common);
return ref;
}
@@ -1607,6 +1731,14 @@ static void fetch_pack_config(void)
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);
+ if (!uri_protocols.nr) {
+ char *str;
+
+ if (!git_config_get_string("fetch.uriprotocols", &str) && str) {
+ string_list_split(&uri_protocols, str, ',', -1);
+ free(str);
+ }
+ }
git_config(fetch_pack_config_cb, NULL);
}
@@ -1759,7 +1891,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
const struct ref *ref,
struct ref **sought, int nr_sought,
struct oid_array *shallow,
- char **pack_lockfile,
+ struct string_list *pack_lockfiles,
enum protocol_version version)
{
struct ref *ref_cpy;
@@ -1794,11 +1926,11 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
memset(&si, 0, sizeof(si));
ref_cpy = do_fetch_pack_v2(args, fd, ref, sought, nr_sought,
&shallows_scratch, &si,
- pack_lockfile);
+ pack_lockfiles);
} else {
prepare_shallow_info(&si, shallow);
ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
- &si, pack_lockfile);
+ &si, pack_lockfiles);
}
reprepare_packed_git(the_repository);
diff --git a/fetch-pack.h b/fetch-pack.h
index 67f6842..85d1e39 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -83,7 +83,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
struct ref **sought,
int nr_sought,
struct oid_array *shallow,
- char **pack_lockfile,
+ struct string_list *pack_lockfiles,
enum protocol_version version);
/*
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index 72d32bd..bd22e1e 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -10,6 +10,8 @@
#include "commit-reach.h"
static int use_branch_desc;
+static int suppress_dest_pattern_seen;
+static struct string_list suppress_dest_patterns = STRING_LIST_INIT_DUP;
int fmt_merge_msg_config(const char *key, const char *value, void *cb)
{
@@ -22,6 +24,14 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb)
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")) {
+ if (!value)
+ return config_error_nonbool(key);
+ if (!*value)
+ string_list_clear(&suppress_dest_patterns, 0);
+ else
+ string_list_append(&suppress_dest_patterns, value);
+ suppress_dest_pattern_seen = 1;
} else {
return git_default_config(key, value, cb);
}
@@ -403,6 +413,24 @@ static void shortlog(const char *name,
string_list_clear(&subjects, 0);
}
+/*
+ * See if dest_branch matches with any glob pattern on the
+ * suppress_dest_patterns list.
+ *
+ * We may want to also allow negative matches e.g. ":!glob" like we do
+ * for pathspec, but for now, let's keep it simple and stupid.
+ */
+static int dest_suppressed(const char *dest_branch)
+{
+ struct string_list_item *item;
+
+ for_each_string_list_item(item, &suppress_dest_patterns) {
+ if (!wildmatch(item->string, dest_branch, WM_PATHNAME))
+ return 1;
+ }
+ return 0;
+}
+
static void fmt_merge_msg_title(struct strbuf *out,
const char *current_branch)
{
@@ -451,10 +479,9 @@ static void fmt_merge_msg_title(struct strbuf *out,
strbuf_addf(out, " of %s", srcs.items[i].string);
}
- if (!strcmp("master", current_branch))
- strbuf_addch(out, '\n');
- else
- strbuf_addf(out, " into %s\n", current_branch);
+ if (!dest_suppressed(current_branch))
+ strbuf_addf(out, " into %s", current_branch);
+ strbuf_addch(out, '\n');
}
static void fmt_tag_signature(struct strbuf *tagbuf,
@@ -599,6 +626,9 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
void *current_branch_to_free;
struct merge_parents merge_parents;
+ if (!suppress_dest_pattern_seen)
+ string_list_append(&suppress_dest_patterns, "master");
+
memset(&merge_parents, 0, sizeof(merge_parents));
/* get current branch */
diff --git a/fsck.c b/fsck.c
index 8bb3ecf..f82e2fe 100644
--- a/fsck.c
+++ b/fsck.c
@@ -598,7 +598,7 @@ static int verify_ordered(unsigned mode1, const char *name1,
/*
* There can be non-consecutive duplicates due to the implicitly
- * add slash, e.g.:
+ * added slash, e.g.:
*
* foo
* foo.bar
@@ -620,7 +620,7 @@ static int verify_ordered(unsigned mode1, const char *name1,
if (!f_name)
break;
if (!skip_prefix(name2, f_name, &p))
- break;
+ continue;
if (!*p)
return TREE_HAS_DUPS;
if (is_less_than_slash(*p)) {
diff --git a/fsmonitor.c b/fsmonitor.c
index 932bd90..ed796e3 100644
--- a/fsmonitor.c
+++ b/fsmonitor.c
@@ -217,7 +217,7 @@ void refresh_fsmonitor(struct index_state *istate)
* Need to use a char * variable because static
* analysis was suggesting to use strbuf_addbuf
* but we don't want to copy the entire strbuf
- * only the the chars up to the first NUL
+ * only the chars up to the first NUL
*/
buf = query_result.buf;
strbuf_addstr(&last_update_token, buf);
diff --git a/fuzz-commit-graph.c b/fuzz-commit-graph.c
index 9fd1c04..4308172 100644
--- a/fuzz-commit-graph.c
+++ b/fuzz-commit-graph.c
@@ -12,7 +12,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
initialize_the_repository();
g = parse_commit_graph((void *)data, size);
repo_clear(the_repository);
- free(g);
+ free_commit_graph(g);
return 0;
}
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 10fd30a..f36c007 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -754,16 +754,18 @@ sub parse_diff_header {
my $head = { TEXT => [], DISPLAY => [], TYPE => 'header' };
my $mode = { TEXT => [], DISPLAY => [], TYPE => 'mode' };
my $deletion = { TEXT => [], DISPLAY => [], TYPE => 'deletion' };
+ my $addition = { TEXT => [], DISPLAY => [], TYPE => 'addition' };
for (my $i = 0; $i < @{$src->{TEXT}}; $i++) {
my $dest =
$src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ? $mode :
$src->{TEXT}->[$i] =~ /^deleted file/ ? $deletion :
+ $src->{TEXT}->[$i] =~ /^new file/ ? $addition :
$head;
push @{$dest->{TEXT}}, $src->{TEXT}->[$i];
push @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i];
}
- return ($head, $mode, $deletion);
+ return ($head, $mode, $deletion, $addition);
}
sub hunk_splittable {
@@ -1427,46 +1429,55 @@ my %patch_update_prompt_modes = (
stage => {
mode => N__("Stage mode change [y,n,q,a,d%s,?]? "),
deletion => N__("Stage deletion [y,n,q,a,d%s,?]? "),
+ addition => N__("Stage addition [y,n,q,a,d%s,?]? "),
hunk => N__("Stage this hunk [y,n,q,a,d%s,?]? "),
},
stash => {
mode => N__("Stash mode change [y,n,q,a,d%s,?]? "),
deletion => N__("Stash deletion [y,n,q,a,d%s,?]? "),
+ addition => N__("Stash addition [y,n,q,a,d%s,?]? "),
hunk => N__("Stash this hunk [y,n,q,a,d%s,?]? "),
},
reset_head => {
mode => N__("Unstage mode change [y,n,q,a,d%s,?]? "),
deletion => N__("Unstage deletion [y,n,q,a,d%s,?]? "),
+ addition => N__("Unstage addition [y,n,q,a,d%s,?]? "),
hunk => N__("Unstage this hunk [y,n,q,a,d%s,?]? "),
},
reset_nothead => {
mode => N__("Apply mode change to index [y,n,q,a,d%s,?]? "),
deletion => N__("Apply deletion to index [y,n,q,a,d%s,?]? "),
+ addition => N__("Apply addition to index [y,n,q,a,d%s,?]? "),
hunk => N__("Apply this hunk to index [y,n,q,a,d%s,?]? "),
},
checkout_index => {
mode => N__("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
deletion => N__("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
+ addition => N__("Discard addition from worktree [y,n,q,a,d%s,?]? "),
hunk => N__("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
},
checkout_head => {
mode => N__("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
deletion => N__("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
+ addition => N__("Discard addition from index and worktree [y,n,q,a,d%s,?]? "),
hunk => N__("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
},
checkout_nothead => {
mode => N__("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
deletion => N__("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
+ addition => N__("Apply addition to index and worktree [y,n,q,a,d%s,?]? "),
hunk => N__("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
},
worktree_head => {
mode => N__("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
deletion => N__("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
+ addition => N__("Discard addition from worktree [y,n,q,a,d%s,?]? "),
hunk => N__("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
},
worktree_nothead => {
mode => N__("Apply mode change to worktree [y,n,q,a,d%s,?]? "),
deletion => N__("Apply deletion to worktree [y,n,q,a,d%s,?]? "),
+ addition => N__("Apply addition to worktree [y,n,q,a,d%s,?]? "),
hunk => N__("Apply this hunk to worktree [y,n,q,a,d%s,?]? "),
},
);
@@ -1476,7 +1487,7 @@ sub patch_update_file {
my ($ix, $num);
my $path = shift;
my ($head, @hunk) = parse_diff($path);
- ($head, my $mode, my $deletion) = parse_diff_header($head);
+ ($head, my $mode, my $deletion, my $addition) = parse_diff_header($head);
for (@{$head->{DISPLAY}}) {
print;
}
@@ -1490,6 +1501,12 @@ sub patch_update_file {
push @{$deletion->{DISPLAY}}, @{$hunk->{DISPLAY}};
}
@hunk = ($deletion);
+ } elsif (@{$addition->{TEXT}}) {
+ foreach my $hunk (@hunk) {
+ push @{$addition->{TEXT}}, @{$hunk->{TEXT}};
+ push @{$addition->{DISPLAY}}, @{$hunk->{DISPLAY}};
+ }
+ @hunk = ($addition);
}
$num = scalar @hunk;
diff --git a/git-bisect.sh b/git-bisect.sh
index 08a6ed5..f03fbb1 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -41,7 +41,7 @@ TERM_GOOD=good
bisect_head()
{
- if test -f "$GIT_DIR/BISECT_HEAD"
+ if git rev-parse --verify -q BISECT_HEAD > /dev/null
then
echo BISECT_HEAD
else
@@ -153,7 +153,7 @@ bisect_next() {
git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD $TERM_GOOD|| exit
# Perform all bisection computation, display and checkout
- git bisect--helper --next-all $(test -f "$GIT_DIR/BISECT_HEAD" && echo --no-checkout)
+ git bisect--helper --next-all $(git rev-parse --verify -q BISECT_HEAD > /dev/null && echo --no-checkout)
res=$?
# Check if we should exit because bisection is finished
diff --git a/git-compat-util.h b/git-compat-util.h
index a73632e..5637114 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -869,6 +869,12 @@ FILE *fopen_for_writing(const char *path);
FILE *fopen_or_warn(const char *path, const char *mode);
/*
+ * Like strncmp, but only return zero if s is NUL-terminated and exactly len
+ * characters long. If it is not, consider it greater than t.
+ */
+int xstrncmpz(const char *s, const char *t, size_t len);
+
+/*
* FREE_AND_NULL(ptr) is like free(ptr) followed by ptr = NULL. Note
* that ptr is used twice, so don't pass e.g. ptr++.
*/
diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl
index fc00d59..6483d79 100755
--- a/git-cvsexportcommit.perl
+++ b/git-cvsexportcommit.perl
@@ -22,6 +22,10 @@ die "Need at least one commit identifier!" unless @ARGV;
my $repo = Git->repository();
$opt_w = $repo->config('cvsexportcommit.cvsdir') unless defined $opt_w;
+my $tmpdir = File::Temp->newdir;
+my $hash_algo = $repo->config('extensions.objectformat') || 'sha1';
+my $hexsz = $hash_algo eq 'sha256' ? 64 : 40;
+
if ($opt_w || $opt_W) {
# Remember where GIT_DIR is before changing to CVS checkout
unless ($ENV{GIT_DIR}) {
@@ -96,7 +100,7 @@ foreach my $line (@commit) {
}
if ($stage eq 'headers') {
- if ($line =~ m/^parent (\w{40})$/) { # found a parent
+ if ($line =~ m/^parent ([0-9a-f]{$hexsz})$/) { # found a parent
push @parents, $1;
} elsif ($line =~ m/^author (.+) \d+ [-+]\d+$/) {
$author = $1;
@@ -111,7 +115,7 @@ foreach my $line (@commit) {
}
}
-my $noparent = "0000000000000000000000000000000000000000";
+my $noparent = "0" x $hexsz;
if ($parent) {
my $found;
# double check that it's a valid parent
@@ -174,7 +178,7 @@ my $context = $opt_p ? '' : '-C1';
print "Checking if patch will apply\n";
my @stat;
-open APPLY, "GIT_DIR= git-apply $context --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch";
+open APPLY, "GIT_INDEX_FILE=$tmpdir/index git-apply $context --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch";
@stat=<APPLY>;
close APPLY || die "Cannot patch";
my (@bfiles,@files,@afiles,@dfiles);
@@ -329,7 +333,7 @@ print "Applying\n";
if ($opt_W) {
system("git checkout -q $commit^0") && die "cannot patch";
} else {
- `GIT_DIR= git-apply $context --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch";
+ `GIT_INDEX_FILE=$tmpdir/index git-apply $context --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch";
}
print "Patch applied successfully. Adding new files and directories to CVS\n";
@@ -407,7 +411,7 @@ unlink(".cvsexportcommit.diff");
if ($opt_W) {
system("git checkout $go_back_to") && die "cannot move back to $go_back_to";
- if (!($go_back_to =~ /^[0-9a-fA-F]{40}$/)) {
+ if (!($go_back_to =~ /^[0-9a-fA-F]{$hexsz}$/)) {
system("git symbolic-ref HEAD $go_back_to") &&
die "cannot move back to $go_back_to";
}
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index 1057f38..7bf3c12 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -637,9 +637,9 @@ sub getwd() {
return $pwd;
}
-sub is_sha1 {
+sub is_oid {
my $s = shift;
- return $s =~ /^[a-f0-9]{40}$/;
+ return $s =~ /^[a-f0-9]{40}(?:[a-f0-9]{24})?$/;
}
sub get_headref ($) {
@@ -810,7 +810,7 @@ sub write_tree () {
open(my $fh, '-|', qw(git write-tree))
or die "unable to open git write-tree: $!";
chomp(my $tree = <$fh>);
- is_sha1($tree)
+ is_oid($tree)
or die "Cannot get tree id ($tree): $!";
close($fh)
or die "Error running git write-tree: $?\n";
@@ -896,7 +896,7 @@ sub commit {
print "Committed patch $patchset ($branch $commit_date)\n" if $opt_v;
chomp(my $cid = <$commit_read>);
- is_sha1($cid) or die "Cannot get commit id ($cid): $!\n";
+ is_oid($cid) or die "Cannot get commit id ($cid): $!\n";
print "Commit ID $cid\n" if $opt_v;
close($commit_read);
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index ae10442..f6f3fc1 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -365,7 +365,7 @@ sub req_Root
}
foreach my $line ( @gitvars )
{
- next unless ( $line =~ /^(gitcvs)\.(?:(ext|pserver)\.)?([\w-]+)=(.*)$/ );
+ next unless ( $line =~ /^(gitcvs|extensions)\.(?:(ext|pserver)\.)?([\w-]+)=(.*)$/ );
unless ($2) {
$cfg->{$1}{$3} = $4;
} else {
@@ -392,6 +392,9 @@ sub req_Root
$log->nofile();
}
+ $state->{rawsz} = ($cfg->{'extensions'}{'objectformat'} || 'sha1') eq 'sha256' ? 32 : 20;
+ $state->{hexsz} = $state->{rawsz} * 2;
+
return 1;
}
@@ -1581,7 +1584,7 @@ sub req_ci
$parenthash = safe_pipe_capture('git', 'show-ref', '-s', $branchRef);
chomp $parenthash;
- if ($parenthash !~ /^[0-9a-f]{40}$/)
+ if ($parenthash !~ /^[0-9a-f]{$state->{hexsz}}$/)
{
if ( defined($stickyInfo) && defined($stickyInfo->{tag}) )
{
@@ -1708,7 +1711,7 @@ sub req_ci
chomp($commithash);
$log->info("Commit hash : $commithash");
- unless ( $commithash =~ /[a-zA-Z0-9]{40}/ )
+ unless ( $commithash =~ /[a-zA-Z0-9]{$state->{hexsz}}/ )
{
$log->warn("Commit failed (Invalid commit hash)");
print "error 1 Commit failed (unknown reason)\n";
@@ -2375,7 +2378,7 @@ sub req_annotate
print "E ***************\n";
while ( <ANNOTATE> )
{
- if (m/^([a-zA-Z0-9]{40})\t\([^\)]*\)(.*)$/i)
+ if (m/^([a-zA-Z0-9]{$state->{hexsz}})\t\([^\)]*\)(.*)$/i)
{
my $commithash = $1;
my $data = $2;
@@ -2852,7 +2855,7 @@ sub transmitfile
return;
}
- die "Need filehash" unless ( defined ( $filehash ) and $filehash =~ /^[a-zA-Z0-9]{40}$/ );
+ die "Need filehash" unless ( defined ( $filehash ) and $filehash =~ /^[a-zA-Z0-9]{$state->{hexsz}}$/ );
my $type = safe_pipe_capture('git', 'cat-file', '-t', $filehash);
chomp $type;
@@ -3042,7 +3045,7 @@ sub ensureWorkTree
my $ver = safe_pipe_capture('git', 'show-ref', '-s', "refs/heads/$state->{module}");
chomp $ver;
- if ($ver !~ /^[0-9a-f]{40}$/)
+ if ($ver !~ /^[0-9a-f]{$state->{hexsz}}$/)
{
$log->warn("Error from git show-ref -s refs/head$state->{module}");
print "error 1 cannot find the current HEAD of module";
@@ -3281,7 +3284,7 @@ sub open_blob_or_die
}
elsif( $srcType eq "sha1" )
{
- unless ( defined ( $name ) and $name =~ /^[a-zA-Z0-9]{40}$/ )
+ unless ( defined ( $name ) and $name =~ /^[a-zA-Z0-9]{$state->{hexsz}}$/ )
{
$log->warn("Need filehash");
die "Need filehash\n";
@@ -3817,7 +3820,7 @@ sub update
chomp $commitsha1;
my $commitinfo = ::safe_pipe_capture('git', 'cat-file', 'commit', $self->{module});
- unless ( $commitinfo =~ /tree\s+[a-zA-Z0-9]{40}/ )
+ unless ( $commitinfo =~ /tree\s+[a-zA-Z0-9]{$state->{hexsz}}/ )
{
die("Invalid module '$self->{module}'");
}
@@ -3957,7 +3960,7 @@ sub update
while ( <FILELIST> )
{
chomp;
- unless ( /^:\d{6}\s+([0-7]{6})\s+[a-f0-9]{40}\s+([a-f0-9]{40})\s+(\w)$/o )
+ unless ( /^:\d{6}\s+([0-7]{6})\s+[a-f0-9]{$state->{hexsz}}\s+([a-f0-9]{$state->{hexsz}})\s+(\w)$/o )
{
die("Couldn't process git-diff-tree line : $_");
}
@@ -4625,11 +4628,11 @@ sub getmeta
$db_query->execute($filename, $intRev);
$meta = $db_query->fetchrow_hashref;
}
- elsif ( $revision =~ /^2\.1\.1\.2000(\.[1-3][0-9][0-9]){20}$/ )
+ elsif ( $revision =~ /^2\.1\.1\.2000(\.[1-3][0-9][0-9]){$state->{rawsz}}$/ )
{
my ($commitHash)=($revision=~/^2\.1\.1\.2000(.*)$/);
$commitHash=~s/\.([0-9]+)/sprintf("%02x",$1-100)/eg;
- if($commitHash=~/^[0-9a-f]{40}$/)
+ if($commitHash=~/^[0-9a-f]{$state->{hexsz}}$/)
{
return $self->getMetaFromCommithash($filename,$commitHash);
}
@@ -4639,7 +4642,7 @@ sub getmeta
$log->warning("failed get $revision with commithash=$commitHash");
undef $revision;
}
- elsif ( $revision =~ /^[0-9a-f]{40}$/ )
+ elsif ( $revision =~ /^[0-9a-f]{$state->{hexsz}}$/ )
{
# Try DB first. This is mostly only useful for req_annotate(),
# which only calls this for stuff that should already be in
@@ -4658,7 +4661,7 @@ sub getmeta
if(! $meta)
{
my($revCommit)=$self->lookupCommitRef($revision);
- if($revCommit=~/^[0-9a-f]{40}$/)
+ if($revCommit=~/^[0-9a-f]{$state->{hexsz}}$/)
{
return $self->getMetaFromCommithash($filename,$revCommit);
}
@@ -4672,7 +4675,7 @@ sub getmeta
else
{
my($revCommit)=$self->lookupCommitRef($revision);
- if($revCommit=~/^[0-9a-f]{40}$/)
+ if($revCommit=~/^[0-9a-f]{$state->{hexsz}}$/)
{
return $self->getMetaFromCommithash($filename,$revCommit);
}
@@ -4767,7 +4770,7 @@ sub getMetaFromCommithash
my($fileHash) = ::safe_pipe_capture("git","rev-parse","$revCommit:$filename");
chomp $fileHash;
- if(!($fileHash=~/^[0-9a-f]{40}$/))
+ if(!($fileHash=~/^[0-9a-f]{$state->{hexsz}}$/))
{
die "Invalid fileHash '$fileHash' looking up"
." '$revCommit:$filename'\n";
@@ -4863,7 +4866,7 @@ sub lookupCommitRef
$commitHash = ::safe_pipe_capture("git","rev-parse","--verify","--quiet",
$self->unescapeRefName($ref));
$commitHash=~s/\s*$//;
- if(!($commitHash=~/^[0-9a-f]{40}$/))
+ if(!($commitHash=~/^[0-9a-f]{$state->{hexsz}}$/))
{
$commitHash=undef;
}
@@ -4909,7 +4912,7 @@ sub commitmessage
my $commithash = shift;
my $tablename = $self->tablename("commitmsgs");
- die("Need commithash") unless ( defined($commithash) and $commithash =~ /^[a-zA-Z0-9]{40}$/ );
+ die("Need commithash") unless ( defined($commithash) and $commithash =~ /^[a-zA-Z0-9]{$state->{hexsz}}$/ );
my $db_query;
$db_query = $self->{dbh}->prepare_cached("SELECT value FROM $tablename WHERE key=?",{},1);
diff --git a/git-gui/lib/choose_repository.tcl b/git-gui/lib/choose_repository.tcl
index e54f3e6..af1fee7 100644
--- a/git-gui/lib/choose_repository.tcl
+++ b/git-gui/lib/choose_repository.tcl
@@ -357,31 +357,10 @@ proc _is_git {path {outdir_var ""}} {
if {$outdir_var ne ""} {
upvar 1 $outdir_var outdir
}
- if {[file isfile $path]} {
- set fp [open $path r]
- gets $fp line
- close $fp
- if {[regexp "^gitdir: (.+)$" $line line link_target]} {
- set path [file join [file dirname $path] $link_target]
- set path [file normalize $path]
- }
- }
-
- if {[file exists [file join $path HEAD]]
- && [file exists [file join $path objects]]
- && [file exists [file join $path config]]} {
- set outdir $path
- return 1
- }
- if {[is_Cygwin]} {
- if {[file exists [file join $path HEAD]]
- && [file exists [file join $path objects.lnk]]
- && [file exists [file join $path config.lnk]]} {
- set outdir $path
- return 1
- }
+ if {[catch {set outdir [git rev-parse --resolve-git-dir $path]}]} {
+ return 0
}
- return 0
+ return 1
}
proc _objdir {path} {
diff --git a/git-p4.py b/git-p4.py
index d551efb..ca79dc0 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -2537,11 +2537,12 @@ class P4Submit(Command, P4UserMap):
ok = self.applyCommit(commit)
if ok:
applied.append(commit)
- else:
- if self.prepare_p4_only and i < last:
- print("Processing only the first commit due to option" \
- " --prepare-p4-only")
+ if self.prepare_p4_only:
+ if i < last:
+ print("Processing only the first commit due to option" \
+ " --prepare-p4-only")
break
+ else:
if i < last:
# prompt for what to do, or use the option/variable
if self.conflict_behavior == "ask":
diff --git a/git-send-email.perl b/git-send-email.perl
index dc95656..36c47ba 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -1699,10 +1699,14 @@ sub process_file {
$xfer_encoding = $1 if not defined $xfer_encoding;
}
elsif (/^In-Reply-To: (.*)/i) {
- $in_reply_to = $1;
+ if (!$initial_in_reply_to || $thread) {
+ $in_reply_to = $1;
+ }
}
elsif (/^References: (.*)/i) {
- $references = $1;
+ if (!$initial_in_reply_to || $thread) {
+ $references = $1;
+ }
}
elsif (!/^Date:\s/i && /^[-A-Za-z]+:\s+\S/) {
push @xh, $_;
diff --git a/git-submodule.sh b/git-submodule.sh
index 39ebdf2..43eb605 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -719,7 +719,7 @@ cmd_update()
# $@ = requested path
#
cmd_set_branch() {
- unset_branch=false
+ default=
branch=
while test $# -ne 0
@@ -729,7 +729,7 @@ cmd_set_branch() {
# we don't do anything with this but we need to accept it
;;
-d|--default)
- unset_branch=true
+ default=1
;;
-b|--branch)
case "$2" in '') usage ;; esac
@@ -750,33 +750,7 @@ cmd_set_branch() {
shift
done
- if test $# -ne 1
- then
- usage
- fi
-
- # we can't use `git submodule--helper name` here because internally, it
- # hashes the path so a trailing slash could lead to an unintentional no match
- name="$(git submodule--helper list "$1" | cut -f2)"
- if test -z "$name"
- then
- exit 1
- fi
-
- test -n "$branch"; has_branch=$?
- test "$unset_branch" = true; has_unset_branch=$?
-
- if test $((!$has_branch != !$has_unset_branch)) -eq 0
- then
- usage
- fi
-
- if test $has_branch -eq 0
- then
- git submodule--helper config submodule."$name".branch "$branch"
- else
- git submodule--helper config --unset submodule."$name".branch
- fi
+ git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper set-branch ${GIT_QUIET:+--quiet} ${branch:+--branch "$branch"} ${default:+--default} -- "$@"
}
#
diff --git a/git-svn.perl b/git-svn.perl
index 4aa208f..58f5a7a 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -5,7 +5,8 @@ use 5.008;
use warnings;
use strict;
use vars qw/ $AUTHOR $VERSION
- $sha1 $sha1_short $_revision $_repository
+ $oid $oid_short $oid_length
+ $_revision $_repository
$_q $_authors $_authors_prog %users/;
$AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
$VERSION = '@@GIT_VERSION@@';
@@ -103,8 +104,9 @@ sub _req_svn {
}
}
-$sha1 = qr/[a-f\d]{40}/;
-$sha1_short = qr/[a-f\d]{4,40}/;
+$oid = qr/(?:[a-f\d]{40}(?:[a-f\d]{24})?)/;
+$oid_short = qr/[a-f\d]{4,64}/;
+$oid_length = 40;
my ($_stdin, $_help, $_edit,
$_message, $_file, $_branch_dest,
$_template, $_shared,
@@ -498,6 +500,7 @@ sub do_git_init_db {
command_noisy('config', "$pfx.preserve-empty-dirs", 'true');
command_noisy('config', "$pfx.placeholder-filename", $$fname);
}
+ load_object_format();
}
sub init_subdir {
@@ -582,7 +585,7 @@ sub cmd_set_tree {
print "Reading from stdin...\n";
@commits = ();
while (<STDIN>) {
- if (/\b($sha1_short)\b/o) {
+ if (/\b($oid_short)\b/o) {
unshift @commits, $1;
}
}
@@ -1831,7 +1834,7 @@ sub get_tree_from_treeish {
if ($type eq 'commit') {
$expected = (grep /^tree /, command(qw/cat-file commit/,
$treeish))[0];
- ($expected) = ($expected =~ /^tree ($sha1)$/o);
+ ($expected) = ($expected =~ /^tree ($oid)$/o);
die "Unable to get tree from $treeish\n" unless $expected;
} elsif ($type eq 'tree') {
$expected = $treeish;
@@ -1975,9 +1978,15 @@ sub read_git_config {
}
}
}
+ load_object_format();
delete @$opts{@config_only} if @config_only;
}
+sub load_object_format {
+ chomp(my $hash = `git config --get extensions.objectformat`);
+ $::oid_length = 64 if $hash eq 'sha256';
+}
+
sub extract_metadata {
my $id = shift or return (undef, undef, undef);
my ($url, $rev, $uuid) = ($id =~ /^\s*git-svn-id:\s+(.*)\@(\d+)
@@ -2006,10 +2015,10 @@ sub cmt_sha2rev_batch {
print $out $sha, "\n";
while (my $line = <$in>) {
- if ($first && $line =~ /^[[:xdigit:]]{40}\smissing$/) {
+ if ($first && $line =~ /^$::oid\smissing$/) {
last;
} elsif ($first &&
- $line =~ /^[[:xdigit:]]{40}\scommit\s(\d+)$/) {
+ $line =~ /^$::oid\scommit\s(\d+)$/) {
$first = 0;
$size = $1;
next;
@@ -2036,7 +2045,7 @@ sub working_head_info {
my $hash;
my %max;
while (<$fh>) {
- if ( m{^commit ($::sha1)$} ) {
+ if ( m{^commit ($::oid)$} ) {
unshift @$refs, $hash if $hash and $refs;
$hash = $1;
next;
diff --git a/git.c b/git.c
index a2d337e..6cd887b 100644
--- a/git.c
+++ b/git.c
@@ -346,6 +346,8 @@ static int handle_alias(int *argcp, const char ***argv)
commit_pager_choice();
child.use_shell = 1;
+ child.clean_on_exit = 1;
+ child.wait_after_clean = 1;
child.trace2_child_class = "shell_alias";
argv_array_push(&child.args, alias_string + 1);
argv_array_pushv(&child.args, (*argv) + 1);
@@ -574,7 +576,7 @@ static struct cmd_struct commands[] = {
{ "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
{ "show", cmd_show, RUN_SETUP },
{ "show-branch", cmd_show_branch, RUN_SETUP },
- { "show-index", cmd_show_index },
+ { "show-index", cmd_show_index, RUN_SETUP_GENTLY },
{ "show-ref", cmd_show_ref, RUN_SETUP },
{ "sparse-checkout", cmd_sparse_checkout, RUN_SETUP | NEED_WORK_TREE },
{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
@@ -767,7 +769,7 @@ static int run_argv(int *argcp, const char ***argv)
* OK to return. Otherwise, we just pass along the status code.
*/
i = run_command_v_opt_tr2(args.argv, RUN_SILENT_EXEC_FAILURE |
- RUN_CLEAN_ON_EXIT, "git_alias");
+ RUN_CLEAN_ON_EXIT | RUN_WAIT_AFTER_CLEAN, "git_alias");
if (i >= 0 || errno != ENOENT)
exit(i);
die("could not execute builtin %s", **argv);
diff --git a/grep.c b/grep.c
index 13232a9..54af9f8 100644
--- a/grep.c
+++ b/grep.c
@@ -1817,7 +1817,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
* We might set up the shared textconv cache data here, which
* is not thread-safe. Also, get_oid_with_context() and
* parse_object() might be internally called. As they are not
- * currenty thread-safe and might be racy with object reading,
+ * currently thread-safe and might be racy with object reading,
* obj_read_lock() must be called.
*/
grep_attr_lock();
diff --git a/hashmap.h b/hashmap.h
index 79ae9f8..ef220de 100644
--- a/hashmap.h
+++ b/hashmap.h
@@ -168,7 +168,7 @@ struct hashmap_entry {
* argument `keydata`, respectively. Otherwise, `keydata` is NULL.
*
* When it is too expensive to allocate a user entry (either because it is
- * large or varialbe sized, such that it is not on the stack), then the
+ * large or variable sized, such that it is not on the stack), then the
* relevant data to check for equality should be passed via `keydata`.
* In this case `key` can be a stripped down version of the user key data
* or even just a hashmap_entry having the correct hash.
diff --git a/help.c b/help.c
index 1de9c0d..44cee69 100644
--- a/help.c
+++ b/help.c
@@ -641,6 +641,7 @@ void get_version_info(struct strbuf *buf, int show_build_options)
strbuf_addstr(buf, "no commit associated with this build\n");
strbuf_addf(buf, "sizeof-long: %d\n", (int)sizeof(long));
strbuf_addf(buf, "sizeof-size_t: %d\n", (int)sizeof(size_t));
+ strbuf_addf(buf, "shell-path: %s\n", SHELL_PATH);
/* NEEDSWORK: also save and output GIT-BUILD_OPTIONS? */
}
}
diff --git a/http-fetch.c b/http-fetch.c
index a32ac11..1df376e 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -5,22 +5,90 @@
#include "walker.h"
static const char http_fetch_usage[] = "git http-fetch "
-"[-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url";
+"[-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin | --packfile=hash | commit-id] url";
-int cmd_main(int argc, const char **argv)
+static int fetch_using_walker(const char *raw_url, int get_verbosely,
+ int get_recover, int commits, char **commit_id,
+ const char **write_ref, int commits_on_stdin)
{
+ char *url = NULL;
struct walker *walker;
+ int rc;
+
+ str_end_url_with_slash(raw_url, &url);
+
+ http_init(NULL, url, 0);
+
+ walker = get_http_walker(url);
+ walker->get_verbosely = get_verbosely;
+ walker->get_recover = get_recover;
+ walker->get_progress = 0;
+
+ rc = walker_fetch(walker, commits, commit_id, write_ref, url);
+
+ if (commits_on_stdin)
+ walker_targets_free(commits, commit_id, write_ref);
+
+ if (walker->corrupt_object_found) {
+ fprintf(stderr,
+"Some loose object were found to be corrupt, but they might be just\n"
+"a false '404 Not Found' error message sent with incorrect HTTP\n"
+"status code. Suggest running 'git fsck'.\n");
+ }
+
+ walker_free(walker);
+ http_cleanup();
+ free(url);
+
+ return rc;
+}
+
+static void fetch_single_packfile(struct object_id *packfile_hash,
+ const char *url) {
+ struct http_pack_request *preq;
+ struct slot_results results;
+ int ret;
+
+ http_init(NULL, url, 0);
+
+ preq = new_direct_http_pack_request(packfile_hash->hash, xstrdup(url));
+ if (preq == NULL)
+ die("couldn't create http pack request");
+ preq->slot->results = &results;
+ preq->generate_keep = 1;
+
+ if (start_active_slot(preq->slot)) {
+ run_active_slot(preq->slot);
+ if (results.curl_result != CURLE_OK) {
+ die("Unable to get pack file %s\n%s", preq->url,
+ curl_errorstr);
+ }
+ } else {
+ die("Unable to start request");
+ }
+
+ if ((ret = finish_http_pack_request(preq)))
+ die("finish_http_pack_request gave result %d", ret);
+
+ release_http_pack_request(preq);
+ http_cleanup();
+}
+
+int cmd_main(int argc, const char **argv)
+{
int commits_on_stdin = 0;
int commits;
const char **write_ref = NULL;
char **commit_id;
- char *url = NULL;
int arg = 1;
- int rc = 0;
int get_verbosely = 0;
int get_recover = 0;
+ int packfile = 0;
+ struct object_id packfile_hash;
while (arg < argc && argv[arg][0] == '-') {
+ const char *p;
+
if (argv[arg][1] == 't') {
} else if (argv[arg][1] == 'c') {
} else if (argv[arg][1] == 'a') {
@@ -35,46 +103,34 @@ int cmd_main(int argc, const char **argv)
get_recover = 1;
} else if (!strcmp(argv[arg], "--stdin")) {
commits_on_stdin = 1;
+ } else if (skip_prefix(argv[arg], "--packfile=", &p)) {
+ const char *end;
+
+ packfile = 1;
+ if (parse_oid_hex(p, &packfile_hash, &end) || *end)
+ die(_("argument to --packfile must be a valid hash (got '%s')"), p);
}
arg++;
}
- if (argc != arg + 2 - commits_on_stdin)
+ if (argc != arg + 2 - (commits_on_stdin || packfile))
usage(http_fetch_usage);
- if (commits_on_stdin) {
- commits = walker_targets_stdin(&commit_id, &write_ref);
- } else {
- commit_id = (char **) &argv[arg++];
- commits = 1;
- }
-
- if (argv[arg])
- str_end_url_with_slash(argv[arg], &url);
setup_git_directory();
git_config(git_default_config, NULL);
- http_init(NULL, url, 0);
- walker = get_http_walker(url);
- walker->get_verbosely = get_verbosely;
- walker->get_recover = get_recover;
-
- rc = walker_fetch(walker, commits, commit_id, write_ref, url);
-
- if (commits_on_stdin)
- walker_targets_free(commits, commit_id, write_ref);
-
- if (walker->corrupt_object_found) {
- fprintf(stderr,
-"Some loose object were found to be corrupt, but they might be just\n"
-"a false '404 Not Found' error message sent with incorrect HTTP\n"
-"status code. Suggest running 'git fsck'.\n");
+ if (packfile) {
+ fetch_single_packfile(&packfile_hash, argv[arg]);
+ return 0;
}
- walker_free(walker);
- http_cleanup();
-
- free(url);
-
- return rc;
+ if (commits_on_stdin) {
+ commits = walker_targets_stdin(&commit_id, &write_ref);
+ } else {
+ commit_id = (char **) &argv[arg++];
+ commits = 1;
+ }
+ return fetch_using_walker(argv[arg], get_verbosely, get_recover,
+ commits, commit_id, write_ref,
+ commits_on_stdin);
}
diff --git a/http-push.c b/http-push.c
index 822f326..1ff1883 100644
--- a/http-push.c
+++ b/http-push.c
@@ -70,10 +70,10 @@ enum XML_Status {
#define LOCK_REFRESH 30
/* Remember to update object flag allocation in object.h */
-#define LOCAL (1u<<16)
-#define REMOTE (1u<<17)
-#define FETCHING (1u<<18)
-#define PUSHING (1u<<19)
+#define LOCAL (1u<<11)
+#define REMOTE (1u<<12)
+#define FETCHING (1u<<13)
+#define PUSHING (1u<<14)
/* We allow "recursive" symbolic refs. Only within reason, though */
#define MAXDEPTH 5
@@ -117,6 +117,7 @@ enum transfer_state {
struct transfer_request {
struct object *obj;
+ struct packed_git *target;
char *url;
char *dest;
struct remote_lock *lock;
@@ -314,17 +315,18 @@ static void start_fetch_packed(struct transfer_request *request)
release_request(request);
return;
}
+ close_pack_index(target);
+ request->target = target;
fprintf(stderr, "Fetching pack %s\n",
hash_to_hex(target->hash));
fprintf(stderr, " which contains %s\n", oid_to_hex(&request->obj->oid));
- preq = new_http_pack_request(target, repo->url);
+ preq = new_http_pack_request(target->hash, repo->url);
if (preq == NULL) {
repo->can_update_info_refs = 0;
return;
}
- preq->lst = &repo->packs;
/* Make sure there isn't another open request for this pack */
while (check_request) {
@@ -597,6 +599,8 @@ static void finish_request(struct transfer_request *request)
}
if (fail)
repo->can_update_info_refs = 0;
+ else
+ http_install_packfile(request->target, &repo->packs);
release_request(request);
}
}
diff --git a/http-walker.c b/http-walker.c
index fe15e32..4fb1235 100644
--- a/http-walker.c
+++ b/http-walker.c
@@ -439,6 +439,7 @@ static int http_fetch_pack(struct walker *walker, struct alt_base *repo, unsigne
target = find_sha1_pack(sha1, repo->packs);
if (!target)
return -1;
+ close_pack_index(target);
if (walker->get_verbosely) {
fprintf(stderr, "Getting pack %s\n",
@@ -447,10 +448,9 @@ static int http_fetch_pack(struct walker *walker, struct alt_base *repo, unsigne
hash_to_hex(sha1));
}
- preq = new_http_pack_request(target, repo->base);
+ preq = new_http_pack_request(target->hash, repo->base);
if (preq == NULL)
goto abort;
- preq->lst = &repo->packs;
preq->slot->results = &results;
if (start_active_slot(preq->slot)) {
@@ -469,6 +469,7 @@ static int http_fetch_pack(struct walker *walker, struct alt_base *repo, unsigne
release_http_pack_request(preq);
if (ret)
return ret;
+ http_install_packfile(target, &repo->packs);
return 0;
diff --git a/http.c b/http.c
index 62aa995..3b12843 100644
--- a/http.c
+++ b/http.c
@@ -18,7 +18,7 @@
static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);
static int trace_curl_data = 1;
-static struct string_list cookies_to_redact = STRING_LIST_INIT_DUP;
+static int trace_curl_redact = 1;
#if LIBCURL_VERSION_NUM >= 0x070a08
long int git_curl_ipresolve = CURL_IPRESOLVE_WHATEVER;
#else
@@ -642,8 +642,9 @@ static void redact_sensitive_header(struct strbuf *header)
{
const char *sensitive_header;
- if (skip_prefix(header->buf, "Authorization:", &sensitive_header) ||
- skip_prefix(header->buf, "Proxy-Authorization:", &sensitive_header)) {
+ if (trace_curl_redact &&
+ (skip_prefix(header->buf, "Authorization:", &sensitive_header) ||
+ skip_prefix(header->buf, "Proxy-Authorization:", &sensitive_header))) {
/* The first token is the type, which is OK to log */
while (isspace(*sensitive_header))
sensitive_header++;
@@ -652,20 +653,15 @@ static void redact_sensitive_header(struct strbuf *header)
/* Everything else is opaque and possibly sensitive */
strbuf_setlen(header, sensitive_header - header->buf);
strbuf_addstr(header, " <redacted>");
- } else if (cookies_to_redact.nr &&
+ } else if (trace_curl_redact &&
skip_prefix(header->buf, "Cookie:", &sensitive_header)) {
struct strbuf redacted_header = STRBUF_INIT;
- char *cookie;
+ const char *cookie;
while (isspace(*sensitive_header))
sensitive_header++;
- /*
- * The contents of header starting from sensitive_header will
- * subsequently be overridden, so it is fine to mutate this
- * string (hence the assignment to "char *").
- */
- cookie = (char *) sensitive_header;
+ cookie = sensitive_header;
while (cookie) {
char *equals;
@@ -678,14 +674,8 @@ static void redact_sensitive_header(struct strbuf *header)
strbuf_addstr(&redacted_header, cookie);
continue;
}
- *equals = 0; /* temporarily set to NUL for lookup */
- if (string_list_lookup(&cookies_to_redact, cookie)) {
- strbuf_addstr(&redacted_header, cookie);
- strbuf_addstr(&redacted_header, "=<redacted>");
- } else {
- *equals = '=';
- strbuf_addstr(&redacted_header, cookie);
- }
+ strbuf_add(&redacted_header, cookie, equals - cookie);
+ strbuf_addstr(&redacted_header, "=<redacted>");
if (semicolon) {
/*
* There are more cookies. (Or, for some
@@ -804,6 +794,12 @@ static int curl_trace(CURL *handle, curl_infotype type, char *data, size_t size,
return 0;
}
+void http_trace_curl_no_data(void)
+{
+ trace_override_envvar(&trace_curl, "1");
+ trace_curl_data = 0;
+}
+
void setup_curl_trace(CURL *handle)
{
if (!trace_want(&trace_curl))
@@ -993,15 +989,12 @@ static CURL *get_curl_handle(void)
warning(_("Protocol restrictions not supported with cURL < 7.19.4"));
#endif
if (getenv("GIT_CURL_VERBOSE"))
- curl_easy_setopt(result, CURLOPT_VERBOSE, 1L);
+ http_trace_curl_no_data();
setup_curl_trace(result);
if (getenv("GIT_TRACE_CURL_NO_DATA"))
trace_curl_data = 0;
- if (getenv("GIT_REDACT_COOKIES")) {
- string_list_split(&cookies_to_redact,
- getenv("GIT_REDACT_COOKIES"), ',', -1);
- string_list_sort(&cookies_to_redact);
- }
+ if (!git_env_bool("GIT_TRACE_REDACT", 1))
+ trace_curl_redact = 0;
curl_easy_setopt(result, CURLOPT_USERAGENT,
user_agent ? user_agent : git_user_agent());
@@ -2268,70 +2261,74 @@ void release_http_pack_request(struct http_pack_request *preq)
int finish_http_pack_request(struct http_pack_request *preq)
{
- struct packed_git **lst;
- struct packed_git *p = preq->target;
- char *tmp_idx;
- size_t len;
struct child_process ip = CHILD_PROCESS_INIT;
-
- close_pack_index(p);
+ int tmpfile_fd;
+ int ret = 0;
fclose(preq->packfile);
preq->packfile = NULL;
- lst = preq->lst;
- while (*lst != p)
- lst = &((*lst)->next);
- *lst = (*lst)->next;
-
- if (!strip_suffix(preq->tmpfile.buf, ".pack.temp", &len))
- BUG("pack tmpfile does not end in .pack.temp?");
- tmp_idx = xstrfmt("%.*s.idx.temp", (int)len, preq->tmpfile.buf);
+ tmpfile_fd = xopen(preq->tmpfile.buf, O_RDONLY);
argv_array_push(&ip.args, "index-pack");
- argv_array_pushl(&ip.args, "-o", tmp_idx, NULL);
- argv_array_push(&ip.args, preq->tmpfile.buf);
+ argv_array_push(&ip.args, "--stdin");
ip.git_cmd = 1;
- ip.no_stdin = 1;
- ip.no_stdout = 1;
+ ip.in = tmpfile_fd;
+ if (preq->generate_keep) {
+ argv_array_pushf(&ip.args, "--keep=git %"PRIuMAX,
+ (uintmax_t)getpid());
+ ip.out = 0;
+ } else {
+ ip.no_stdout = 1;
+ }
if (run_command(&ip)) {
- unlink(preq->tmpfile.buf);
- unlink(tmp_idx);
- free(tmp_idx);
- return -1;
+ ret = -1;
+ goto cleanup;
}
- unlink(sha1_pack_index_name(p->hash));
+cleanup:
+ close(tmpfile_fd);
+ unlink(preq->tmpfile.buf);
+ return ret;
+}
- if (finalize_object_file(preq->tmpfile.buf, sha1_pack_name(p->hash))
- || finalize_object_file(tmp_idx, sha1_pack_index_name(p->hash))) {
- free(tmp_idx);
- return -1;
- }
+void http_install_packfile(struct packed_git *p,
+ struct packed_git **list_to_remove_from)
+{
+ struct packed_git **lst = list_to_remove_from;
+
+ while (*lst != p)
+ lst = &((*lst)->next);
+ *lst = (*lst)->next;
install_packed_git(the_repository, p);
- free(tmp_idx);
- return 0;
}
struct http_pack_request *new_http_pack_request(
- struct packed_git *target, const char *base_url)
+ const unsigned char *packed_git_hash, const char *base_url) {
+
+ struct strbuf buf = STRBUF_INIT;
+
+ end_url_with_slash(&buf, base_url);
+ strbuf_addf(&buf, "objects/pack/pack-%s.pack",
+ hash_to_hex(packed_git_hash));
+ return new_direct_http_pack_request(packed_git_hash,
+ strbuf_detach(&buf, NULL));
+}
+
+struct http_pack_request *new_direct_http_pack_request(
+ const unsigned char *packed_git_hash, char *url)
{
off_t prev_posn = 0;
- struct strbuf buf = STRBUF_INIT;
struct http_pack_request *preq;
preq = xcalloc(1, sizeof(*preq));
strbuf_init(&preq->tmpfile, 0);
- preq->target = target;
- end_url_with_slash(&buf, base_url);
- strbuf_addf(&buf, "objects/pack/pack-%s.pack",
- hash_to_hex(target->hash));
- preq->url = strbuf_detach(&buf, NULL);
+ preq->url = url;
- strbuf_addf(&preq->tmpfile, "%s.temp", sha1_pack_name(target->hash));
+ strbuf_addf(&preq->tmpfile, "%s.temp", sha1_pack_name(packed_git_hash));
preq->packfile = fopen(preq->tmpfile.buf, "a");
if (!preq->packfile) {
error("Unable to open local file %s for pack",
@@ -2355,7 +2352,7 @@ struct http_pack_request *new_http_pack_request(
if (http_is_verbose)
fprintf(stderr,
"Resuming fetch of pack %s at byte %"PRIuMAX"\n",
- hash_to_hex(target->hash),
+ hash_to_hex(packed_git_hash),
(uintmax_t)prev_posn);
http_opt_request_remainder(preq->slot->curl, prev_posn);
}
diff --git a/http.h b/http.h
index 5e0ad72..5de792e 100644
--- a/http.h
+++ b/http.h
@@ -216,18 +216,36 @@ int http_get_info_packs(const char *base_url,
struct http_pack_request {
char *url;
- struct packed_git *target;
- struct packed_git **lst;
+
+ /*
+ * If this is true, finish_http_pack_request() will pass "--keep" to
+ * index-pack, resulting in the creation of a keep file, and will not
+ * suppress its stdout (that is, the "keep\t<hash>\n" line will be
+ * printed to stdout).
+ */
+ unsigned generate_keep : 1;
+
FILE *packfile;
struct strbuf tmpfile;
struct active_request_slot *slot;
};
struct http_pack_request *new_http_pack_request(
- struct packed_git *target, const char *base_url);
+ const unsigned char *packed_git_hash, const char *base_url);
+struct http_pack_request *new_direct_http_pack_request(
+ const unsigned char *packed_git_hash, char *url);
int finish_http_pack_request(struct http_pack_request *preq);
void release_http_pack_request(struct http_pack_request *preq);
+/*
+ * Remove p from the given list, and invoke install_packed_git() on it.
+ *
+ * This is a convenience function for users that have obtained a list of packs
+ * from http_get_info_packs() and have chosen a specific pack to fetch.
+ */
+void http_install_packfile(struct packed_git *p,
+ struct packed_git **list_to_remove_from);
+
/* Helpers for fetching object */
struct http_object_request {
char *url;
@@ -252,6 +270,13 @@ int finish_http_object_request(struct http_object_request *freq);
void abort_http_object_request(struct http_object_request *freq);
void release_http_object_request(struct http_object_request *freq);
+/*
+ * Instead of using environment variables to determine if curl tracing happens,
+ * behave as if GIT_TRACE_CURL=1 and GIT_TRACE_CURL_NO_DATA=1 is set. Call this
+ * before calling setup_curl_trace().
+ */
+void http_trace_curl_no_data(void);
+
/* setup routine for curl_easy_setopt CURLOPT_DEBUGFUNCTION */
void setup_curl_trace(CURL *handle);
#endif /* HTTP_H */
diff --git a/imap-send.c b/imap-send.c
index 6c54d8c..5273754 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1464,7 +1464,7 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
- curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+ http_trace_curl_no_data();
setup_curl_trace(curl);
return curl;
diff --git a/line-log.c b/line-log.c
index 40e1738..c536928 100644
--- a/line-log.c
+++ b/line-log.c
@@ -15,6 +15,7 @@
#include "userdiff.h"
#include "line-log.h"
#include "argv-array.h"
+#include "bloom.h"
static void range_set_grow(struct range_set *rs, size_t extra)
{
@@ -1146,6 +1147,37 @@ int line_log_print(struct rev_info *rev, struct commit *commit)
return 1;
}
+static int bloom_filter_check(struct rev_info *rev,
+ struct commit *commit,
+ struct line_log_data *range)
+{
+ struct bloom_filter *filter;
+ struct bloom_key key;
+ int result = 0;
+
+ if (!commit->parents)
+ return 1;
+
+ if (!rev->bloom_filter_settings ||
+ !(filter = get_bloom_filter(rev->repo, commit, 0)))
+ return 1;
+
+ if (!range)
+ return 0;
+
+ while (!result && range) {
+ fill_bloom_key(range->path, strlen(range->path), &key, rev->bloom_filter_settings);
+
+ if (bloom_filter_contains(filter, &key, rev->bloom_filter_settings))
+ result = 1;
+
+ clear_bloom_key(&key);
+ range = range->next;
+ }
+
+ return result;
+}
+
static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *commit,
struct line_log_data *range)
{
@@ -1159,6 +1191,7 @@ static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *c
queue_diffs(range, &rev->diffopt, &queue, commit, parent);
changed = process_all_files(&parent_range, rev, &queue, range);
+
if (parent)
add_line_range(rev, parent, parent_range);
free_line_log_data(parent_range);
@@ -1227,13 +1260,17 @@ static int process_ranges_merge_commit(struct rev_info *rev, struct commit *comm
/* NEEDSWORK leaking like a sieve */
}
-static int process_ranges_arbitrary_commit(struct rev_info *rev, struct commit *commit)
+int line_log_process_ranges_arbitrary_commit(struct rev_info *rev, struct commit *commit)
{
struct line_log_data *range = lookup_line_range(rev, commit);
int changed = 0;
if (range) {
- if (!commit->parents || !commit->parents->next)
+ if (commit->parents && !bloom_filter_check(rev, commit, range)) {
+ 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
changed = process_ranges_merge_commit(rev, commit, range);
@@ -1270,7 +1307,7 @@ int line_log_filter(struct rev_info *rev)
while (list) {
struct commit_list *to_free = NULL;
commit = list->item;
- if (process_ranges_arbitrary_commit(rev, commit)) {
+ if (line_log_process_ranges_arbitrary_commit(rev, commit)) {
*pp = list;
pp = &list->next;
} else
diff --git a/line-log.h b/line-log.h
index 8ee7a2b..82ae8d9 100644
--- a/line-log.h
+++ b/line-log.h
@@ -46,10 +46,7 @@ void sort_and_merge_range_set(struct range_set *);
struct line_log_data {
struct line_log_data *next;
char *path;
- char status;
struct range_set ranges;
- int arg_alloc, arg_nr;
- const char **args;
struct diff_filepair *pair;
struct diff_ranges diff;
};
@@ -57,6 +54,8 @@ struct line_log_data {
void line_log_init(struct rev_info *rev, const char *prefix, struct string_list *args);
int line_log_filter(struct rev_info *rev);
+int line_log_process_ranges_arbitrary_commit(struct rev_info *rev,
+ struct commit *commit);
int line_log_print(struct rev_info *rev, struct commit *commit);
diff --git a/list-objects-filter-options.c b/list-objects-filter-options.c
index 256bcfb..3553ad7 100644
--- a/list-objects-filter-options.c
+++ b/list-objects-filter-options.c
@@ -326,7 +326,8 @@ void partial_clone_register(
/* Check if it is already registered */
if (!promisor_remote_find(remote)) {
- git_config_set("core.repositoryformatversion", "1");
+ if (upgrade_repository_format(1) < 0)
+ die(_("unable to upgrade repository format to support partial clone"));
/* Add promisor config for the remote */
cfg_name = xstrfmt("remote.%s.promisor", remote);
diff --git a/object-store.h b/object-store.h
index d1e490f..f439d47 100644
--- a/object-store.h
+++ b/object-store.h
@@ -70,6 +70,7 @@ struct packed_git {
size_t index_size;
uint32_t num_objects;
uint32_t num_bad_objects;
+ uint32_t crc_offset;
unsigned char *bad_object_sha1;
int index_version;
time_t mtime;
diff --git a/object.c b/object.c
index 794c866..3257518 100644
--- a/object.c
+++ b/object.c
@@ -157,13 +157,13 @@ void *create_object(struct repository *r, const struct object_id *oid, void *o)
return obj;
}
-void *object_as_type(struct repository *r, struct object *obj, enum object_type type, int quiet)
+void *object_as_type(struct object *obj, enum object_type type, int quiet)
{
if (obj->type == type)
return obj;
else if (obj->type == OBJ_NONE) {
if (type == OBJ_COMMIT)
- init_commit_node(r, (struct commit *) obj);
+ init_commit_node((struct commit *) obj);
else
obj->type = type;
return obj;
diff --git a/object.h b/object.h
index b22328b..96a2105 100644
--- a/object.h
+++ b/object.h
@@ -15,7 +15,6 @@ struct parsed_object_pool {
struct alloc_state *commit_state;
struct alloc_state *tag_state;
struct alloc_state *object_state;
- unsigned commit_count;
/* parent substitutions from .git/info/grafts and .git/shallow */
struct commit_graft **grafts;
@@ -26,6 +25,7 @@ struct parsed_object_pool {
char *alternate_shallow_file;
int commit_graft_prepared;
+ int substituted_parent;
struct buffer_slab *buffer_slab;
};
@@ -59,7 +59,7 @@ struct object_array {
/*
* object flag allocation:
- * revision.h: 0---------10 15 25----28
+ * revision.h: 0---------10 15 23------26
* fetch-pack.c: 01
* negotiator/default.c: 2--5
* walker.c: 0-2
@@ -67,7 +67,7 @@ struct object_array {
* builtin/blame.c: 12-13
* bisect.c: 16
* bundle.c: 16
- * http-push.c: 16-----19
+ * http-push.c: 11-----14
* commit-graph.c: 15
* commit-reach.c: 16-----19
* sha1-name.c: 20
@@ -79,7 +79,7 @@ struct object_array {
* builtin/show-branch.c: 0-------------------------------------------26
* builtin/unpack-objects.c: 2021
*/
-#define FLAG_BITS 29
+#define FLAG_BITS 28
/*
* The object type is stored in 3 bits.
@@ -121,7 +121,7 @@ struct object *lookup_object(struct repository *r, const struct object_id *oid);
void *create_object(struct repository *r, const struct object_id *oid, void *obj);
-void *object_as_type(struct repository *r, struct object *obj, enum object_type type, int quiet);
+void *object_as_type(struct object *obj, enum object_type type, int quiet);
/*
* Returns the object, having parsed it to find out what it is.
diff --git a/pack-write.c b/pack-write.c
index f0017be..685d327 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -38,9 +38,8 @@ static int need_large_offset(off_t offset, const struct pack_idx_option *opts)
}
/*
- * On entry *sha1 contains the pack content SHA1 hash, on exit it is
- * the SHA1 hash of sorted object names. The objects array passed in
- * will be sorted by SHA1 on exit.
+ * The *sha1 contains the pack content SHA1 hash.
+ * The objects array passed in will be sorted by SHA1 on exit.
*/
const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects,
int nr_objects, const struct pack_idx_option *opts,
diff --git a/packfile.c b/packfile.c
index f4e7529..6ab5233 100644
--- a/packfile.c
+++ b/packfile.c
@@ -178,6 +178,7 @@ int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
*/
(sizeof(off_t) <= 4))
return error("pack too large for current definition of off_t in %s", path);
+ p->crc_offset = 8 + 4 * 256 + nr * hashsz;
}
p->index_version = version;
diff --git a/perl/Git/IndexInfo.pm b/perl/Git/IndexInfo.pm
index a43108c..2a7b490 100644
--- a/perl/Git/IndexInfo.pm
+++ b/perl/Git/IndexInfo.pm
@@ -5,13 +5,15 @@ use Git qw/command_input_pipe command_close_pipe/;
sub new {
my ($class) = @_;
+ my $hash_algo = Git::config('extensions.objectformat') || 'sha1';
my ($gui, $ctx) = command_input_pipe(qw/update-index -z --index-info/);
- bless { gui => $gui, ctx => $ctx, nr => 0}, $class;
+ bless { gui => $gui, ctx => $ctx, nr => 0, hash_algo => $hash_algo}, $class;
}
sub remove {
my ($self, $path) = @_;
- if (print { $self->{gui} } '0 ', 0 x 40, "\t", $path, "\0") {
+ my $length = $self->{hash_algo} eq 'sha256' ? 64 : 40;
+ if (print { $self->{gui} } '0 ', 0 x $length, "\t", $path, "\0") {
return ++$self->{nr};
}
undef;
diff --git a/perl/Git/SVN.pm b/perl/Git/SVN.pm
index 4b28b87..d1c352f 100644
--- a/perl/Git/SVN.pm
+++ b/perl/Git/SVN.pm
@@ -2,7 +2,7 @@ package Git::SVN;
use strict;
use warnings;
use Fcntl qw/:DEFAULT :seek/;
-use constant rev_map_fmt => 'NH40';
+use constant rev_map_fmt => 'NH*';
use vars qw/$_no_metadata
$_repack $_repack_flags $_use_svm_props $_head
$_use_svnsync_props $no_reuse_existing
@@ -874,7 +874,7 @@ sub assert_index_clean {
command_noisy('read-tree', $treeish) unless -e $self->{index};
my $x = command_oneline('write-tree');
my ($y) = (command(qw/cat-file commit/, $treeish) =~
- /^tree ($::sha1)/mo);
+ /^tree ($::oid)/mo);
return if $y eq $x;
warn "Index mismatch: $y != $x\nrereading $treeish\n";
@@ -1020,7 +1020,7 @@ sub do_git_commit {
$tree = $self->tmp_index_do(sub {
command_oneline('write-tree') });
}
- die "Tree is not a valid sha1: $tree\n" if $tree !~ /^$::sha1$/o;
+ die "Tree is not a valid oid $tree\n" if $tree !~ /^$::oid$/o;
my @exec = ('git', 'commit-tree', $tree);
foreach ($self->get_commit_parents($log_entry)) {
@@ -1048,8 +1048,8 @@ sub do_git_commit {
close $out_fh or croak $!;
waitpid $pid, 0;
croak $? if $?;
- if ($commit !~ /^$::sha1$/o) {
- die "Failed to commit, invalid sha1: $commit\n";
+ if ($commit !~ /^$::oid$/o) {
+ die "Failed to commit, invalid oid: $commit\n";
}
$self->rev_map_set($log_entry->{revision}, $commit, 1);
@@ -2087,10 +2087,10 @@ sub rebuild_from_rev_db {
open my $fh, '<', $path or croak "open: $!";
binmode $fh or croak "binmode: $!";
while (<$fh>) {
- length($_) == 41 or croak "inconsistent size in ($_) != 41";
+ length($_) == $::oid_length + 1 or croak "inconsistent size in ($_)";
chomp($_);
++$r;
- next if $_ eq ('0' x 40);
+ next if $_ eq ('0' x $::oid_length);
$self->rev_map_set($r, $_);
print "r$r = $_\n";
}
@@ -2150,7 +2150,7 @@ sub rebuild {
my $svn_uuid = $self->rewrite_uuid || $self->ra_uuid;
my $c;
while (<$log>) {
- if ( m{^commit ($::sha1)$} ) {
+ if ( m{^commit ($::oid)$} ) {
$c = $1;
next;
}
@@ -2196,9 +2196,9 @@ sub rebuild {
# (mainly tags)
#
# The format is this:
-# - 24 bytes for every record,
+# - 24 or 36 bytes for every record,
# * 4 bytes for the integer representing an SVN revision number
-# * 20 bytes representing the sha1 of a git commit
+# * 20 or 32 bytes representing the oid of a git commit
# - No empty padding records like the old format
# (except the last record, which can be overwritten)
# - new records are written append-only since SVN revision numbers
@@ -2207,7 +2207,7 @@ sub rebuild {
# - Piping the file to xxd -c24 is a good way of dumping it for
# viewing or editing (piped back through xxd -r), should the need
# ever arise.
-# - The last record can be padding revision with an all-zero sha1
+# - The last record can be padding revision with an all-zero oid
# This is used to optimize fetch performance when using multiple
# "fetch" directives in .git/config
#
@@ -2215,38 +2215,39 @@ sub rebuild {
sub _rev_map_set {
my ($fh, $rev, $commit) = @_;
+ my $record_size = ($::oid_length / 2) + 4;
binmode $fh or croak "binmode: $!";
my $size = (stat($fh))[7];
- ($size % 24) == 0 or croak "inconsistent size: $size";
+ ($size % $record_size) == 0 or croak "inconsistent size: $size";
my $wr_offset = 0;
if ($size > 0) {
- sysseek($fh, -24, SEEK_END) or croak "seek: $!";
- my $read = sysread($fh, my $buf, 24) or croak "read: $!";
- $read == 24 or croak "read only $read bytes (!= 24)";
+ sysseek($fh, -$record_size, SEEK_END) or croak "seek: $!";
+ my $read = sysread($fh, my $buf, $record_size) or croak "read: $!";
+ $read == $record_size or croak "read only $read bytes (!= $record_size)";
my ($last_rev, $last_commit) = unpack(rev_map_fmt, $buf);
- if ($last_commit eq ('0' x40)) {
- if ($size >= 48) {
- sysseek($fh, -48, SEEK_END) or croak "seek: $!";
- $read = sysread($fh, $buf, 24) or
+ if ($last_commit eq ('0' x $::oid_length)) {
+ if ($size >= ($record_size * 2)) {
+ sysseek($fh, -($record_size * 2), SEEK_END) or croak "seek: $!";
+ $read = sysread($fh, $buf, $record_size) or
croak "read: $!";
- $read == 24 or
- croak "read only $read bytes (!= 24)";
+ $read == $record_size or
+ croak "read only $read bytes (!= $record_size)";
($last_rev, $last_commit) =
unpack(rev_map_fmt, $buf);
- if ($last_commit eq ('0' x40)) {
+ if ($last_commit eq ('0' x $::oid_length)) {
croak "inconsistent .rev_map\n";
}
}
if ($last_rev >= $rev) {
croak "last_rev is higher!: $last_rev >= $rev";
}
- $wr_offset = -24;
+ $wr_offset = -$record_size;
}
}
sysseek($fh, $wr_offset, SEEK_END) or croak "seek: $!";
- syswrite($fh, pack(rev_map_fmt, $rev, $commit), 24) == 24 or
+ syswrite($fh, pack(rev_map_fmt, $rev, $commit), $record_size) == $record_size or
croak "write: $!";
}
@@ -2271,7 +2272,7 @@ sub mkfile {
sub rev_map_set {
my ($self, $rev, $commit, $update_ref, $uuid) = @_;
defined $commit or die "missing arg3\n";
- length $commit == 40 or die "arg3 must be a full SHA1 hexsum\n";
+ $commit =~ /^$::oid$/ or die "arg3 must be a full hex object ID\n";
my $db = $self->map_path($uuid);
my $db_lock = "$db.lock";
my $sigmask;
@@ -2344,29 +2345,30 @@ sub rev_map_max {
sub rev_map_max_norebuild {
my ($self, $want_commit) = @_;
+ my $record_size = ($::oid_length / 2) + 4;
my $map_path = $self->map_path;
stat $map_path or return $want_commit ? (0, undef) : 0;
sysopen(my $fh, $map_path, O_RDONLY) or croak "open: $!";
binmode $fh or croak "binmode: $!";
my $size = (stat($fh))[7];
- ($size % 24) == 0 or croak "inconsistent size: $size";
+ ($size % $record_size) == 0 or croak "inconsistent size: $size";
if ($size == 0) {
close $fh or croak "close: $!";
return $want_commit ? (0, undef) : 0;
}
- sysseek($fh, -24, SEEK_END) or croak "seek: $!";
- sysread($fh, my $buf, 24) == 24 or croak "read: $!";
+ sysseek($fh, -$record_size, SEEK_END) or croak "seek: $!";
+ sysread($fh, my $buf, $record_size) == $record_size or croak "read: $!";
my ($r, $c) = unpack(rev_map_fmt, $buf);
- if ($want_commit && $c eq ('0' x40)) {
- if ($size < 48) {
+ if ($want_commit && $c eq ('0' x $::oid_length)) {
+ if ($size < $record_size * 2) {
return $want_commit ? (0, undef) : 0;
}
- sysseek($fh, -48, SEEK_END) or croak "seek: $!";
- sysread($fh, $buf, 24) == 24 or croak "read: $!";
+ sysseek($fh, -($record_size * 2), SEEK_END) or croak "seek: $!";
+ sysread($fh, $buf, $record_size) == $record_size or croak "read: $!";
($r, $c) = unpack(rev_map_fmt, $buf);
- if ($c eq ('0'x40)) {
+ if ($c eq ('0' x $::oid_length)) {
croak "Penultimate record is all-zeroes in $map_path";
}
}
@@ -2387,30 +2389,31 @@ sub rev_map_get {
sub _rev_map_get {
my ($fh, $rev) = @_;
+ my $record_size = ($::oid_length / 2) + 4;
binmode $fh or croak "binmode: $!";
my $size = (stat($fh))[7];
- ($size % 24) == 0 or croak "inconsistent size: $size";
+ ($size % $record_size) == 0 or croak "inconsistent size: $size";
if ($size == 0) {
return undef;
}
- my ($l, $u) = (0, $size - 24);
+ my ($l, $u) = (0, $size - $record_size);
my ($r, $c, $buf);
while ($l <= $u) {
- my $i = int(($l/24 + $u/24) / 2) * 24;
+ my $i = int(($l/$record_size + $u/$record_size) / 2) * $record_size;
sysseek($fh, $i, SEEK_SET) or croak "seek: $!";
- sysread($fh, my $buf, 24) == 24 or croak "read: $!";
+ sysread($fh, my $buf, $record_size) == $record_size or croak "read: $!";
my ($r, $c) = unpack(rev_map_fmt, $buf);
if ($r < $rev) {
- $l = $i + 24;
+ $l = $i + $record_size;
} elsif ($r > $rev) {
- $u = $i - 24;
+ $u = $i - $record_size;
} else { # $r == $rev
- return $c eq ('0' x 40) ? undef : $c;
+ return $c eq ('0' x $::oid_length) ? undef : $c;
}
}
undef;
diff --git a/perl/Git/SVN/Editor.pm b/perl/Git/SVN/Editor.pm
index 0df16ed..c961444 100644
--- a/perl/Git/SVN/Editor.pm
+++ b/perl/Git/SVN/Editor.pm
@@ -63,7 +63,7 @@ sub generate_diff {
my @mods;
while (defined($_ = get_record($diff_fh, "\0"))) {
if ($state eq 'meta' && /^:(\d{6})\s(\d{6})\s
- ($::sha1)\s($::sha1)\s
+ ($::oid)\s($::oid)\s
([MTCRAD])\d*$/xo) {
push @mods, { mode_a => $1, mode_b => $2,
sha1_a => $3, sha1_b => $4,
@@ -400,12 +400,12 @@ sub T {
($m->{mode_b} !~ /^120/ && $m->{mode_a} =~ /^120/)) {
$self->D({
mode_a => $m->{mode_a}, mode_b => '000000',
- sha1_a => $m->{sha1_a}, sha1_b => '0' x 40,
+ sha1_a => $m->{sha1_a}, sha1_b => '0' x $::oid_length,
chg => 'D', file_b => $m->{file_b}
}, $deletions);
$self->A({
mode_a => '000000', mode_b => $m->{mode_b},
- sha1_a => '0' x 40, sha1_b => $m->{sha1_b},
+ sha1_a => '0' x $::oid_length, sha1_b => $m->{sha1_b},
chg => 'A', file_b => $m->{file_b}
}, $deletions);
return;
@@ -434,7 +434,7 @@ sub _chg_file_get_blob ($$$$) {
$self->change_file_prop($fbat,'svn:special',undef);
}
my $blob = $m->{"sha1_$which"};
- return ($fh,) if ($blob =~ /^0{40}$/);
+ return ($fh,) if ($blob =~ /^0+$/);
my $size = $::_repository->cat_blob($blob, $fh);
croak "Failed to read object $blob" if ($size < 0);
$fh->flush == 0 or croak $!;
diff --git a/perl/Git/SVN/Fetcher.pm b/perl/Git/SVN/Fetcher.pm
index 64e900a..729e533 100644
--- a/perl/Git/SVN/Fetcher.pm
+++ b/perl/Git/SVN/Fetcher.pm
@@ -173,7 +173,7 @@ sub delete_entry {
# remove entire directories.
my ($tree) = (command('ls-tree', '-z', $self->{c}, "./$gpath")
- =~ /\A040000 tree ([a-f\d]{40})\t\Q$gpath\E\0/);
+ =~ /\A040000 tree ($::oid)\t\Q$gpath\E\0/);
if ($tree) {
my ($ls, $ctx) = command_output_pipe(qw/ls-tree
-r --name-only -z/,
@@ -203,7 +203,7 @@ sub open_file {
my $gpath = $self->git_path($path);
($mode, $blob) = (command('ls-tree', '-z', $self->{c}, "./$gpath")
- =~ /\A(\d{6}) blob ([a-f\d]{40})\t\Q$gpath\E\0/);
+ =~ /\A(\d{6}) blob ($::oid)\t\Q$gpath\E\0/);
unless (defined $mode && defined $blob) {
die "$path was not found in commit $self->{c} (r$rev)\n";
}
@@ -413,7 +413,7 @@ sub close_file {
$hash = $::_repository->hash_and_insert_object(
Git::temp_path($fh));
- $hash =~ /^[a-f\d]{40}$/ or die "not a sha1: $hash\n";
+ $hash =~ /^$::oid$/ or die "not an object ID: $hash\n";
Git::temp_release($fb->{base}, 1);
Git::temp_release($fh, 1);
diff --git a/perl/Git/SVN/Log.pm b/perl/Git/SVN/Log.pm
index 6641053..3858fcf 100644
--- a/perl/Git/SVN/Log.pm
+++ b/perl/Git/SVN/Log.pm
@@ -285,7 +285,7 @@ sub cmd_show_log {
my (@k, $c, $d, $stat);
my $esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/;
while (<$log>) {
- if (/^${esc_color}commit (?:- )?($::sha1_short)/o) {
+ if (/^${esc_color}commit (?:- )?($::oid_short)/o) {
my $cmt = $1;
if ($c && cmt_showable($c) && $c->{r} != $r_last) {
$r_last = $c->{r};
diff --git a/perl/Git/SVN/Ra.pm b/perl/Git/SVN/Ra.pm
index 56ad987..2cfe055 100644
--- a/perl/Git/SVN/Ra.pm
+++ b/perl/Git/SVN/Ra.pm
@@ -486,11 +486,11 @@ sub gs_fetch_loop_common {
$reload_ra->() if $ra_invalid;
}
# pre-fill the .rev_db since it'll eventually get filled in
- # with '0' x40 if something new gets committed
+ # with '0' x $oid_length if something new gets committed
foreach my $gs (@$gsv) {
next if $gs->rev_map_max >= $max;
next if defined $gs->rev_map_get($max);
- $gs->rev_map_set($max, 0 x40);
+ $gs->rev_map_set($max, 0 x $::oid_length);
}
foreach my $g (@$globs) {
my $k = "svn-remote.$g->{remote}.$g->{t}-maxRev";
diff --git a/pkt-line.c b/pkt-line.c
index a0e87b1..844c253 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -99,6 +99,13 @@ void packet_delim(int fd)
die_errno(_("unable to write delim packet"));
}
+void packet_response_end(int fd)
+{
+ packet_trace("0002", 4, 1);
+ if (write_in_full(fd, "0002", 4) < 0)
+ die_errno(_("unable to write stateless separator packet"));
+}
+
int packet_flush_gently(int fd)
{
packet_trace("0000", 4, 1);
@@ -306,10 +313,10 @@ static int get_packet_data(int fd, char **src_buf, size_t *src_size,
return ret;
}
-static int packet_length(const char *linelen)
+int packet_length(const char lenbuf_hex[4])
{
- int val = hex2chr(linelen);
- return (val < 0) ? val : (val << 8) | hex2chr(linelen + 2);
+ int val = hex2chr(lenbuf_hex);
+ return (val < 0) ? val : (val << 8) | hex2chr(lenbuf_hex + 2);
}
enum packet_read_status packet_read_with_status(int fd, char **src_buffer,
@@ -337,6 +344,10 @@ enum packet_read_status packet_read_with_status(int fd, char **src_buffer,
packet_trace("0001", 4, 0);
*pktlen = 0;
return PACKET_READ_DELIM;
+ } else if (len == 2) {
+ packet_trace("0002", 4, 0);
+ *pktlen = 0;
+ return PACKET_READ_RESPONSE_END;
} else if (len < 4) {
die(_("protocol error: bad line length %d"), len);
}
@@ -479,6 +490,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];
}
enum packet_read_status packet_reader_read(struct packet_reader *reader)
diff --git a/pkt-line.h b/pkt-line.h
index fef3a0d..8c90daa 100644
--- a/pkt-line.h
+++ b/pkt-line.h
@@ -22,6 +22,7 @@
*/
void packet_flush(int fd);
void packet_delim(int fd);
+void packet_response_end(int fd);
void packet_write_fmt(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
void packet_buf_flush(struct strbuf *buf);
void packet_buf_delim(struct strbuf *buf);
@@ -75,6 +76,15 @@ int packet_read(int fd, char **src_buffer, size_t *src_len, char
*buffer, unsigned size, int options);
/*
+ * Convert a four hex digit packet line length header into its numeric
+ * representation.
+ *
+ * If lenbuf_hex contains non-hex characters, return -1. Otherwise, return the
+ * numeric value of the length header.
+ */
+int packet_length(const char lenbuf_hex[4]);
+
+/*
* Read a packetized line into a buffer like the 'packet_read()' function but
* returns an 'enum packet_read_status' which indicates the status of the read.
* The number of bytes read will be assigned to *pktlen if the status of the
@@ -85,6 +95,7 @@ enum packet_read_status {
PACKET_READ_NORMAL,
PACKET_READ_FLUSH,
PACKET_READ_DELIM,
+ PACKET_READ_RESPONSE_END,
};
enum packet_read_status packet_read_with_status(int fd, char **src_buffer,
size_t *src_len, char *buffer,
@@ -166,6 +177,9 @@ struct packet_reader {
unsigned use_sideband : 1;
const char *me;
+
+ /* hash algorithm in use */
+ const struct git_hash_algo *hash_algo;
};
/*
diff --git a/po/bg.po b/po/bg.po
index 7990313..a189b16 100644
--- a/po/bg.po
+++ b/po/bg.po
@@ -133,7 +133,7 @@
# hash контролна сума, изчисляване на контролна сума
# fanout откъс (разперване???)
# idx - index of pack file, 1 index per 1 packfile
-# midx - multi-pack index, for objects across multiple packfiles
+# midx, multi-pack index - файл с индекса за множество пакети
# overlay mode - припокриващ режим (при изтеглянe)
# incremental file нарастващ файл
# split (commit-graphr) раздробен (граф с подавания)
@@ -143,6 +143,7 @@
# cone pattern matching пътеводно напасване
# negative pattern отрицателен шаблон
# colored hunk/diff оцветено парче/разлика
+# up to date обновен, освен като самостотелва дума - актуален или completely up to date - напълно актуален
# ------------------------
# „$var“ - може да не сработва за shell има gettext и eval_gettext - проверка - намират се лесно по „$
# ------------------------
@@ -159,10 +160,10 @@
# for i in `sort -u FILES`; do cnt=`grep $i FILES | wc -l`; echo $cnt $i ;done | sort -n
msgid ""
msgstr ""
-"Project-Id-Version: git 2.25\n"
+"Project-Id-Version: git 2.27\n"
"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2020-03-11 15:26+0800\n"
-"PO-Revision-Date: 2020-03-12 00:03+0100\n"
+"POT-Creation-Date: 2020-05-27 09:34+0200\n"
+"PO-Revision-Date: 2020-05-27 09:43+0200\n"
"Last-Translator: Alexander Shopov <ash@kambanaria.org>\n"
"Language-Team: Bulgarian <dict@fsa-bg.org>\n"
"Language: bg\n"
@@ -176,9 +177,9 @@ msgstr ""
msgid "Huh (%s)?"
msgstr "Неуспешен анализ — „%s“."
-#: add-interactive.c:521 add-interactive.c:822 sequencer.c:3124
-#: sequencer.c:3562 builtin/rebase.c:875 builtin/rebase.c:1687
-#: builtin/rebase.c:2086 builtin/rebase.c:2130
+#: add-interactive.c:521 add-interactive.c:822 reset.c:65 sequencer.c:3142
+#: sequencer.c:3581 sequencer.c:3723 builtin/rebase.c:1518
+#: builtin/rebase.c:1919
msgid "could not read index"
msgstr "индексът не може да бъде прочетен"
@@ -206,8 +207,7 @@ msgstr "Обновяване"
msgid "could not stage '%s'"
msgstr "неуспешно добавяне в индекса на „%s“"
-#: add-interactive.c:695 add-interactive.c:884 sequencer.c:3317
-#: builtin/rebase.c:899
+#: add-interactive.c:695 add-interactive.c:884 reset.c:89 sequencer.c:3336
msgid "could not write index"
msgstr "индексът не може да бъде записан"
@@ -223,8 +223,8 @@ msgstr[1] "%d файла обновени\n"
msgid "note: %s is untracked now.\n"
msgstr "БЕЛЕЖКА: „%s“ вече не се следи.\n"
-#: add-interactive.c:721 apply.c:4110 builtin/checkout.c:281
-#: builtin/reset.c:144
+#: add-interactive.c:721 apply.c:4110 builtin/checkout.c:294
+#: builtin/reset.c:145
#, c-format
msgid "make_cache_entry failed for path '%s'"
msgstr "неуспешно създаване на запис в кеша чрез „make_cache_entry“ за „%s“"
@@ -347,11 +347,11 @@ msgstr "избор на номериран елемент"
msgid "(empty) select nothing"
msgstr "(празно) без избор на нищо"
-#: add-interactive.c:1083 builtin/clean.c:822 git-add--interactive.perl:1851
+#: add-interactive.c:1083 builtin/clean.c:816 git-add--interactive.perl:1851
msgid "*** Commands ***"
msgstr "●●● Команди ●●●"
-#: add-interactive.c:1084 builtin/clean.c:823 git-add--interactive.perl:1848
+#: add-interactive.c:1084 builtin/clean.c:817 git-add--interactive.perl:1848
msgid "What now"
msgstr "Избор на следващо действие"
@@ -363,12 +363,13 @@ msgstr "в индекса"
msgid "unstaged"
msgstr "извън индекса"
-#: add-interactive.c:1136 apply.c:4967 apply.c:4970 builtin/am.c:2251
-#: builtin/am.c:2254 builtin/clone.c:123 builtin/fetch.c:144
-#: builtin/merge.c:274 builtin/pull.c:189 builtin/submodule--helper.c:409
+#: add-interactive.c:1136 apply.c:4967 apply.c:4970 builtin/am.c:2250
+#: builtin/am.c:2253 builtin/clone.c:123 builtin/fetch.c:145
+#: builtin/merge.c:276 builtin/pull.c:190 builtin/submodule--helper.c:409
#: builtin/submodule--helper.c:1394 builtin/submodule--helper.c:1397
#: builtin/submodule--helper.c:1902 builtin/submodule--helper.c:1905
-#: builtin/submodule--helper.c:2148 git-add--interactive.perl:213
+#: builtin/submodule--helper.c:2148 bugreport.c:129
+#: git-add--interactive.perl:213
msgid "path"
msgstr "път"
@@ -377,27 +378,27 @@ msgid "could not refresh index"
msgstr "индексът не може да бъде обновен"
#
-#: add-interactive.c:1157 builtin/clean.c:787 git-add--interactive.perl:1765
+#: add-interactive.c:1157 builtin/clean.c:781 git-add--interactive.perl:1765
#, c-format
msgid "Bye.\n"
msgstr "Изход.\n"
-#: add-patch.c:33 git-add--interactive.perl:1428
+#: add-patch.c:34 git-add--interactive.perl:1428
#, c-format, perl-format
msgid "Stage mode change [y,n,q,a,d%s,?]? "
msgstr "Добавяне на промяната на правата за достъп [y,n,q,a,d%s,?]? "
-#: add-patch.c:34 git-add--interactive.perl:1429
+#: add-patch.c:35 git-add--interactive.perl:1429
#, c-format, perl-format
msgid "Stage deletion [y,n,q,a,d%s,?]? "
msgstr "Добавяне на изтриването [y,n,q,a,d%s,?]? "
-#: add-patch.c:35 git-add--interactive.perl:1430
+#: add-patch.c:36 git-add--interactive.perl:1430
#, c-format, perl-format
msgid "Stage this hunk [y,n,q,a,d%s,?]? "
msgstr "Добавяне на това парче [y,n,q,a,d%s,?]? "
-#: add-patch.c:37
+#: add-patch.c:38
msgid ""
"If the patch applies cleanly, the edited hunk will immediately be marked for "
"staging."
@@ -405,7 +406,7 @@ msgstr ""
"Ако кръпката може да се приложи чисто, редактираното парче ще бъде незабавно "
"добавено към индекса."
-#: add-patch.c:40
+#: add-patch.c:41
msgid ""
"y - stage this hunk\n"
"n - do not stage this hunk\n"
@@ -419,22 +420,22 @@ msgstr ""
"a — добавяне на това и всички следващи парчета от файла в индекса\n"
"d — без добавяне на това и всички следващи парчета от файла в индекса\n"
-#: add-patch.c:54 git-add--interactive.perl:1433
+#: add-patch.c:55 git-add--interactive.perl:1433
#, c-format, perl-format
msgid "Stash mode change [y,n,q,a,d%s,?]? "
msgstr "Скатаване на промяната на правата за достъп [y,n,q,a,d%s,?]? "
-#: add-patch.c:55 git-add--interactive.perl:1434
+#: add-patch.c:56 git-add--interactive.perl:1434
#, c-format, perl-format
msgid "Stash deletion [y,n,q,a,d%s,?]? "
msgstr "Скатаване на изтриването [y,n,q,a,d%s,?]? "
-#: add-patch.c:56 git-add--interactive.perl:1435
+#: add-patch.c:57 git-add--interactive.perl:1435
#, c-format, perl-format
msgid "Stash this hunk [y,n,q,a,d%s,?]? "
msgstr "Скатаване на това парче [y,n,q,a,d%s,?]? "
-#: add-patch.c:58
+#: add-patch.c:59
msgid ""
"If the patch applies cleanly, the edited hunk will immediately be marked for "
"stashing."
@@ -442,7 +443,7 @@ msgstr ""
"Ако кръпката може да се приложи чисто, редактираното парче ще бъде незабавно "
"скътано."
-#: add-patch.c:61
+#: add-patch.c:62
msgid ""
"y - stash this hunk\n"
"n - do not stash this hunk\n"
@@ -456,22 +457,22 @@ msgstr ""
"a — скатаване на това и всички следващи парчета от файла\n"
"d — без скатаване на това и всички следващи парчета от файла\n"
-#: add-patch.c:77 git-add--interactive.perl:1438
+#: add-patch.c:78 git-add--interactive.perl:1438
#, c-format, perl-format
msgid "Unstage mode change [y,n,q,a,d%s,?]? "
msgstr "Изваждане на промяната на правата за достъп [y,n,q,a,d%s,?]? "
-#: add-patch.c:78 git-add--interactive.perl:1439
+#: add-patch.c:79 git-add--interactive.perl:1439
#, c-format, perl-format
msgid "Unstage deletion [y,n,q,a,d%s,?]? "
msgstr "Изваждане на изтриването [y,n,q,a,d%s,?]? "
-#: add-patch.c:79 git-add--interactive.perl:1440
+#: add-patch.c:80 git-add--interactive.perl:1440
#, c-format, perl-format
msgid "Unstage this hunk [y,n,q,a,d%s,?]? "
msgstr "Изваждане на това парче [y,n,q,a,d%s,?]? "
-#: add-patch.c:81
+#: add-patch.c:82
msgid ""
"If the patch applies cleanly, the edited hunk will immediately be marked for "
"unstaging."
@@ -479,7 +480,7 @@ msgstr ""
"Ако кръпката може да се приложи чисто, редактираното парче ще бъде незабавно "
"извадено от индекса."
-#: add-patch.c:84
+#: add-patch.c:85
msgid ""
"y - unstage this hunk\n"
"n - do not unstage this hunk\n"
@@ -493,23 +494,23 @@ msgstr ""
"a — изваждане на това и всички следващи парчета от файла от индекса\n"
"d — без изваждане на това и всички следващи парчета от файла от индекса\n"
-#: add-patch.c:99 git-add--interactive.perl:1443
+#: add-patch.c:100 git-add--interactive.perl:1443
#, c-format, perl-format
msgid "Apply mode change to index [y,n,q,a,d%s,?]? "
msgstr ""
"Прилагане на промяната на правата за достъп към индекса [y,n,q,a,d%s,?]? "
-#: add-patch.c:100 git-add--interactive.perl:1444
+#: add-patch.c:101 git-add--interactive.perl:1444
#, c-format, perl-format
msgid "Apply deletion to index [y,n,q,a,d%s,?]? "
msgstr "Прилагане на изтриването към индекса [y,n,q,a,d%s,?]? "
-#: add-patch.c:101 git-add--interactive.perl:1445
+#: add-patch.c:102 git-add--interactive.perl:1445
#, c-format, perl-format
msgid "Apply this hunk to index [y,n,q,a,d%s,?]? "
msgstr "Прилагане на това парче към индекса [y,n,q,a,d%s,?]? "
-#: add-patch.c:103 add-patch.c:168 add-patch.c:211
+#: add-patch.c:104 add-patch.c:169 add-patch.c:212
msgid ""
"If the patch applies cleanly, the edited hunk will immediately be marked for "
"applying."
@@ -517,7 +518,7 @@ msgstr ""
"Ако кръпката може да се приложи чисто, редактираното парче ще бъде незабавно "
"набелязано за прилагане."
-#: add-patch.c:106
+#: add-patch.c:107
msgid ""
"y - apply this hunk to index\n"
"n - do not apply this hunk to index\n"
@@ -531,7 +532,7 @@ msgstr ""
"a — прилагане на това и всички следващи парчета от файла към индекса\n"
"d — без прилагане на това и всички следващи парчета от файла към индекса\n"
-#: add-patch.c:121 git-add--interactive.perl:1448
+#: add-patch.c:122 git-add--interactive.perl:1448
#: git-add--interactive.perl:1463
#, c-format, perl-format
msgid "Discard mode change from worktree [y,n,q,a,d%s,?]? "
@@ -539,19 +540,19 @@ msgstr ""
"Премахване на промяната в правата за достъп от работното дърво [y,n,q,a,d"
"%s,?]? "
-#: add-patch.c:122 git-add--interactive.perl:1449
+#: add-patch.c:123 git-add--interactive.perl:1449
#: git-add--interactive.perl:1464
#, c-format, perl-format
msgid "Discard deletion from worktree [y,n,q,a,d%s,?]? "
msgstr "Премахване на изтриването от работното дърво [y,n,q,a,d%s,?]? "
-#: add-patch.c:123 git-add--interactive.perl:1450
+#: add-patch.c:124 git-add--interactive.perl:1450
#: git-add--interactive.perl:1465
#, c-format, perl-format
msgid "Discard this hunk from worktree [y,n,q,a,d%s,?]? "
msgstr "Премахване на парчето от работното дърво [y,n,q,a,d%s,?]? "
-#: add-patch.c:125 add-patch.c:147 add-patch.c:190
+#: add-patch.c:126 add-patch.c:148 add-patch.c:191
msgid ""
"If the patch applies cleanly, the edited hunk will immediately be marked for "
"discarding."
@@ -559,7 +560,7 @@ msgstr ""
"Ако кръпката може да се приложи чисто, редактираното парче ще бъде незабавно "
"набелязано за зануляване."
-#: add-patch.c:128 add-patch.c:193
+#: add-patch.c:129 add-patch.c:194
msgid ""
"y - discard this hunk from worktree\n"
"n - do not discard this hunk from worktree\n"
@@ -576,25 +577,25 @@ msgstr ""
"d — без премахване на това и всички следващи парчета от файла от работното "
"дърво\n"
-#: add-patch.c:143 add-patch.c:186 git-add--interactive.perl:1453
+#: add-patch.c:144 add-patch.c:187 git-add--interactive.perl:1453
#, c-format, perl-format
msgid "Discard mode change from index and worktree [y,n,q,a,d%s,?]? "
msgstr ""
"Премахване на промяната в правата за достъп от индекса и работното дърво [y,"
"n,q,a,d%s,?]? "
-#: add-patch.c:144 add-patch.c:187 git-add--interactive.perl:1454
+#: add-patch.c:145 add-patch.c:188 git-add--interactive.perl:1454
#, c-format, perl-format
msgid "Discard deletion from index and worktree [y,n,q,a,d%s,?]? "
msgstr ""
"Премахване на изтриването от индекса и работното дърво [y,n,q,a,d%s,?]? "
-#: add-patch.c:145 add-patch.c:188 git-add--interactive.perl:1455
+#: add-patch.c:146 add-patch.c:189 git-add--interactive.perl:1455
#, c-format, perl-format
msgid "Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "
msgstr "Премахване на парчето от индекса и работното дърво [y,n,q,a,d%s,?]? "
-#: add-patch.c:150
+#: add-patch.c:151
msgid ""
"y - discard this hunk from index and worktree\n"
"n - do not discard this hunk from index and worktree\n"
@@ -611,25 +612,25 @@ msgstr ""
"d — без премахване на това и всички следващи парчета от файла от индекса и "
"работното дърво\n"
-#: add-patch.c:164 add-patch.c:207 git-add--interactive.perl:1458
+#: add-patch.c:165 add-patch.c:208 git-add--interactive.perl:1458
#, c-format, perl-format
msgid "Apply mode change to index and worktree [y,n,q,a,d%s,?]? "
msgstr ""
"Прилагане на промяната в правата за достъп от индекса и работното дърво [y,n,"
"q,a,d%s,?]? "
-#: add-patch.c:165 add-patch.c:208 git-add--interactive.perl:1459
+#: add-patch.c:166 add-patch.c:209 git-add--interactive.perl:1459
#, c-format, perl-format
msgid "Apply deletion to index and worktree [y,n,q,a,d%s,?]? "
msgstr ""
"Прилагане на изтриването от индекса и работното дърво [y,n,q,a,d%s,?]? "
-#: add-patch.c:166 add-patch.c:209 git-add--interactive.perl:1460
+#: add-patch.c:167 add-patch.c:210 git-add--interactive.perl:1460
#, c-format, perl-format
msgid "Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "
msgstr "Прилагане на парчето от индекса и работното дърво [y,n,q,a,d%s,?]? "
-#: add-patch.c:171
+#: add-patch.c:172
msgid ""
"y - apply this hunk to index and worktree\n"
"n - do not apply this hunk to index and worktree\n"
@@ -646,7 +647,7 @@ msgstr ""
"d — без прилагане на това и всички следващи парчета от файла от индекса и "
"работното дърво\n"
-#: add-patch.c:214
+#: add-patch.c:215
msgid ""
"y - apply this hunk to worktree\n"
"n - do not apply this hunk to worktree\n"
@@ -660,34 +661,34 @@ msgstr ""
"a — прилагане на това и всички следващи парчета от файла\n"
"d — без прилагане на това и всички следващи парчета от файла\n"
-#: add-patch.c:318
+#: add-patch.c:319
#, c-format
msgid "could not parse hunk header '%.*s'"
msgstr "заглавната част на парчето „%.*s“ не може да се анализира"
-#: add-patch.c:337 add-patch.c:341
+#: add-patch.c:338 add-patch.c:342
#, c-format
msgid "could not parse colored hunk header '%.*s'"
msgstr "заглавната част на оцветеното парче „%.*s“ не може да се анализира"
-#: add-patch.c:395
+#: add-patch.c:396
msgid "could not parse diff"
msgstr "разликата не може да се анализира"
-#: add-patch.c:414
+#: add-patch.c:415
msgid "could not parse colored diff"
msgstr "оцветената разлика не може да се анализира"
-#: add-patch.c:428
+#: add-patch.c:429
#, c-format
msgid "failed to run '%s'"
msgstr "неуспешно изпълнение на „%s“"
-#: add-patch.c:587
+#: add-patch.c:588
msgid "mismatched output from interactive.diffFilter"
msgstr "изходът от „interactive.diffFilter“ не напасва"
-#: add-patch.c:588
+#: add-patch.c:589
msgid ""
"Your filter must maintain a one-to-one correspondence\n"
"between its input and output lines."
@@ -695,7 +696,7 @@ msgstr ""
"Обработените редове на изхода на ползвания филтър, трябва\n"
"да се отнасят едно към едно спрямо редовете на входа."
-#: add-patch.c:761
+#: add-patch.c:762
#, c-format
msgid ""
"expected context line #%d in\n"
@@ -704,7 +705,7 @@ msgstr ""
"очаква се ред №%d от контекста в\n"
"%.*s"
-#: add-patch.c:776
+#: add-patch.c:777
#, c-format
msgid ""
"hunks do not overlap:\n"
@@ -717,11 +718,11 @@ msgstr ""
" не завършва с:\n"
"%.*s"
-#: add-patch.c:1052 git-add--interactive.perl:1112
+#: add-patch.c:1053 git-add--interactive.perl:1112
msgid "Manual hunk edit mode -- see bottom for a quick guide.\n"
msgstr "Ръчно редактиране на парчета код — отдолу има подсказка.\n"
-#: add-patch.c:1056
+#: add-patch.c:1057
#, c-format
msgid ""
"---\n"
@@ -736,7 +737,7 @@ msgstr ""
"Редовете, които започват с „%c“ ще бъдат пропуснати.\n"
#. TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
-#: add-patch.c:1070 git-add--interactive.perl:1126
+#: add-patch.c:1071 git-add--interactive.perl:1126
msgid ""
"If it does not apply cleanly, you will be given an opportunity to\n"
"edit again. If all lines of the hunk are removed, then the edit is\n"
@@ -746,11 +747,11 @@ msgstr ""
"изтриете всички редове от парчето код, то ще бъде оставено непроменено, а\n"
"редактирането — отказано.\n"
-#: add-patch.c:1103
+#: add-patch.c:1104
msgid "could not parse hunk header"
msgstr "заглавната част парчето не може да се анализира"
-#: add-patch.c:1148
+#: add-patch.c:1149
msgid "'git apply --cached' failed"
msgstr "неуспешно изпълнение на „git apply --cached“"
@@ -874,37 +875,47 @@ msgstr "Това парче не може да бъде редактирано"
msgid "'git apply' failed"
msgstr "неуспешно изпълнение на „git apply“"
-#: advice.c:115
+#: advice.c:140
+#, c-format
+msgid ""
+"\n"
+"Disable this message with \"git config advice.%s false\""
+msgstr ""
+"\n"
+"За да изключите това предупреждение, изпълнете:\n"
+" git config advice.%s false"
+
+#: advice.c:156
#, c-format
msgid "%shint: %.*s%s\n"
msgstr "%sподсказка: %.*s%s\n"
-#: advice.c:168
+#: advice.c:247
msgid "Cherry-picking is not possible because you have unmerged files."
msgstr "Отбирането на подавания е блокирано от неслети файлове."
-#: advice.c:170
+#: advice.c:249
msgid "Committing is not possible because you have unmerged files."
msgstr "Подаването е блокирано от неслети файлове."
-#: advice.c:172
+#: advice.c:251
msgid "Merging is not possible because you have unmerged files."
msgstr "Сливането е блокирано от неслети файлове."
-#: advice.c:174
+#: advice.c:253
msgid "Pulling is not possible because you have unmerged files."
msgstr "Издърпването е блокирано от неслети файлове."
-#: advice.c:176
+#: advice.c:255
msgid "Reverting is not possible because you have unmerged files."
msgstr "Отмяната е блокирана от неслети файлове."
-#: advice.c:178
+#: advice.c:257
#, c-format
msgid "It is not possible to %s because you have unmerged files."
msgstr "Действието „%s“ е блокирано от неслети файлове."
-#: advice.c:186
+#: advice.c:265
msgid ""
"Fix them up in the work tree, and then use 'git add/rm <file>'\n"
"as appropriate to mark resolution and make a commit."
@@ -912,23 +923,23 @@ msgstr ""
"Редактирайте ги в работното дърво, и тогава ползвайте „git add/rm ФАЙЛ“,\n"
"за да отбележите коригирането им. След това извършете подаването."
-#: advice.c:194
+#: advice.c:273
msgid "Exiting because of an unresolved conflict."
msgstr "Изход от програмата заради некоригиран конфликт."
-#: advice.c:199 builtin/merge.c:1335
+#: advice.c:278 builtin/merge.c:1353
msgid "You have not concluded your merge (MERGE_HEAD exists)."
msgstr "Не сте завършили сливане. (Указателят „MERGE_HEAD“ съществува)."
-#: advice.c:201
+#: advice.c:280
msgid "Please, commit your changes before merging."
msgstr "Промените трябва да се подадат преди сливане."
-#: advice.c:202
+#: advice.c:281
msgid "Exiting because of unfinished merge."
msgstr "Изход от програмата заради незавършено сливане."
-#: advice.c:208
+#: advice.c:287
#, c-format
msgid ""
"Note: switching to '%s'.\n"
@@ -1232,7 +1243,7 @@ msgstr "неуспешно прилагане на кръпка: „%s:%ld“"
msgid "cannot checkout %s"
msgstr "„%s“ не може да се изтегли"
-#: apply.c:3405 apply.c:3416 apply.c:3462 midx.c:61 setup.c:298
+#: apply.c:3405 apply.c:3416 apply.c:3462 midx.c:61 setup.c:308
#, c-format
msgid "failed to read %s"
msgstr "файлът „%s“ не може да бъде прочетен"
@@ -1433,7 +1444,7 @@ msgstr[1] "Прилагане на кръпката „%%s“ с %d отхвър
msgid "truncating .rej filename to %.*s.rej"
msgstr "съкращаване на името на файла с отхвърлените парчета на „ %.*s.rej“"
-#: apply.c:4561 builtin/fetch.c:901 builtin/fetch.c:1201
+#: apply.c:4561 builtin/fetch.c:902 builtin/fetch.c:1195
#, c-format
msgid "cannot open %s"
msgstr "„%s“ не може да бъде отворен"
@@ -1501,7 +1512,7 @@ msgstr "без прилагане на промените напасващи н
msgid "apply changes matching the given path"
msgstr "прилагане на промените напасващи на дадения път"
-#: apply.c:4973 builtin/am.c:2260
+#: apply.c:4973 builtin/am.c:2259
msgid "num"
msgstr "БРОЙ"
@@ -1560,7 +1571,7 @@ msgid "build a temporary index based on embedded index information"
msgstr ""
"създаване на временен индекс на база на включената информация за индекса"
-#: apply.c:5005 builtin/checkout-index.c:173 builtin/ls-files.c:524
+#: apply.c:5005 builtin/checkout-index.c:173 builtin/ls-files.c:525
msgid "paths are separated with NUL character"
msgstr "разделяне на пътищата с нулевия знак „NUL“"
@@ -1568,9 +1579,9 @@ msgstr "разделяне на пътищата с нулевия знак „N
msgid "ensure at least <n> lines of context match"
msgstr "да се осигури контекст от поне такъв БРОЙ съвпадащи редове"
-#: apply.c:5008 builtin/am.c:2239 builtin/interpret-trailers.c:98
+#: apply.c:5008 builtin/am.c:2238 builtin/interpret-trailers.c:98
#: builtin/interpret-trailers.c:100 builtin/interpret-trailers.c:102
-#: builtin/pack-objects.c:3457 builtin/rebase.c:1508
+#: builtin/pack-objects.c:3458 builtin/rebase.c:1332
msgid "action"
msgstr "действие"
@@ -1600,8 +1611,8 @@ msgid "allow overlapping hunks"
msgstr "позволяване на застъпващи се парчета"
#: apply.c:5025 builtin/add.c:323 builtin/check-ignore.c:22
-#: builtin/commit.c:1360 builtin/count-objects.c:98 builtin/fsck.c:774
-#: builtin/log.c:2166 builtin/mv.c:123 builtin/read-tree.c:128
+#: builtin/commit.c:1366 builtin/count-objects.c:98 builtin/fsck.c:775
+#: builtin/log.c:2186 builtin/mv.c:123 builtin/read-tree.c:128
msgid "be verbose"
msgstr "повече подробности"
@@ -1613,7 +1624,7 @@ msgstr "пренебрегване на неправилно липсващ зн
msgid "do not trust the line counts in the hunk headers"
msgstr "без доверяване на номерата на редовете в заглавните части на парчетата"
-#: apply.c:5032 builtin/am.c:2248
+#: apply.c:5032 builtin/am.c:2247
msgid "root"
msgstr "НАЧАЛНА_ДИРЕКТОРИЯ"
@@ -1621,6 +1632,55 @@ msgstr "НАЧАЛНА_ДИРЕКТОРИЯ"
msgid "prepend <root> to all filenames"
msgstr "добавяне на тази НАЧАЛНА_ДИРЕКТОРИЯ към имената на всички файлове"
+#: archive-tar.c:125 archive-zip.c:351
+#, c-format
+msgid "cannot stream blob %s"
+msgstr "обектът-BLOB „%s“ не може да бъде обработен"
+
+#: archive-tar.c:266 archive-zip.c:369
+#, c-format
+msgid "unsupported file mode: 0%o (SHA1: %s)"
+msgstr "неподдържани права за достъп до файл: 0%o (SHA1: %s)"
+
+#: archive-tar.c:293 archive-zip.c:359
+#, c-format
+msgid "cannot read %s"
+msgstr "обектът „%s“ не може да бъде прочетен"
+
+#: archive-tar.c:465
+#, c-format
+msgid "unable to start '%s' filter"
+msgstr "филтърът „%s“ не може да бъде стартиран"
+
+#: archive-tar.c:468
+msgid "unable to redirect descriptor"
+msgstr "дескрипторът не може да бъде пренасочен"
+
+#: archive-tar.c:475
+#, c-format
+msgid "'%s' filter reported error"
+msgstr "филтърът „%s“ върна грешка"
+
+#: archive-zip.c:319
+#, c-format
+msgid "path is not valid UTF-8: %s"
+msgstr "пътят не е правилно кодиран в UTF-8: %s"
+
+#: archive-zip.c:323
+#, c-format
+msgid "path too long (%d chars, SHA1: %s): %s"
+msgstr "твърде дълъг път (%d знака, SHA1: %s): %s"
+
+#: archive-zip.c:480 builtin/pack-objects.c:232 builtin/pack-objects.c:235
+#, c-format
+msgid "deflate error (%d)"
+msgstr "грешка при декомпресиране с „deflate“ (%d)"
+
+#: archive-zip.c:615
+#, c-format
+msgid "timestamp too large for this system: %<PRIuMAX>"
+msgstr "времевата стойност е твърде голяма за тази система: %<PRIuMAX>"
+
#: archive.c:14
msgid "git archive [<options>] <tree-ish> [<path>...]"
msgstr "git archive [ОПЦИЯ…] УКАЗАТЕЛ_КЪМ_ДЪРВО [ПЪТ…]"
@@ -1640,172 +1700,123 @@ msgstr ""
msgid "git archive --remote <repo> [--exec <cmd>] --list"
msgstr "git archive --remote ХРАНИЛИЩЕ [--exec КОМАНДА] --list"
-#: archive.c:372 builtin/add.c:181 builtin/add.c:588 builtin/rm.c:315
+#: archive.c:377 builtin/add.c:181 builtin/add.c:588 builtin/rm.c:315
#, c-format
msgid "pathspec '%s' did not match any files"
msgstr "пътят „%s“ не съвпада с никой файл"
-#: archive.c:396
+#: archive.c:401
#, c-format
msgid "no such ref: %.*s"
msgstr "такъв указател няма: %.*s"
-#: archive.c:401
+#: archive.c:407
#, c-format
msgid "not a valid object name: %s"
msgstr "неправилно име на обект: „%s“"
-#: archive.c:414
+#: archive.c:420
#, c-format
msgid "not a tree object: %s"
msgstr "не е обект-дърво: %s"
-#: archive.c:426
+#: archive.c:432
msgid "current working directory is untracked"
msgstr "текущата работна директория не е следена"
-#: archive.c:457
+#: archive.c:464
msgid "fmt"
msgstr "ФОРМАТ"
-#: archive.c:457
+#: archive.c:464
msgid "archive format"
msgstr "ФОРМАТ на архива"
-#: archive.c:458 builtin/log.c:1653
+#: archive.c:465 builtin/log.c:1674
msgid "prefix"
msgstr "ПРЕФИКС"
-#: archive.c:459
+#: archive.c:466
msgid "prepend prefix to each pathname in the archive"
msgstr "добавяне на този ПРЕФИКС към всеки път в архива"
-#: archive.c:460 builtin/blame.c:861 builtin/blame.c:865 builtin/blame.c:866
+#: archive.c:467 builtin/blame.c:861 builtin/blame.c:865 builtin/blame.c:866
#: builtin/commit-tree.c:117 builtin/config.c:130 builtin/fast-export.c:1162
-#: builtin/fast-export.c:1164 builtin/fast-export.c:1168 builtin/grep.c:887
-#: builtin/hash-object.c:105 builtin/ls-files.c:560 builtin/ls-files.c:563
+#: builtin/fast-export.c:1164 builtin/fast-export.c:1168 builtin/grep.c:907
+#: builtin/hash-object.c:105 builtin/ls-files.c:561 builtin/ls-files.c:564
#: builtin/notes.c:412 builtin/notes.c:578 builtin/read-tree.c:123
#: parse-options.h:190
msgid "file"
msgstr "ФАЙЛ"
-#: archive.c:461 builtin/archive.c:90
+#: archive.c:468 builtin/archive.c:90
msgid "write the archive to this file"
msgstr "запазване на архива в този ФАЙЛ"
-#: archive.c:463
+#: archive.c:470
msgid "read .gitattributes in working directory"
msgstr "изчитане на „.gitattributes“ в работната директория"
-#: archive.c:464
+#: archive.c:471
msgid "report archived files on stderr"
msgstr "извеждане на архивираните файлове на стандартната грешка"
-#: archive.c:465
+#: archive.c:472
msgid "store only"
msgstr "съхранение без компресиране"
-#: archive.c:466
+#: archive.c:473
msgid "compress faster"
msgstr "бързо компресиране"
-#: archive.c:474
+#: archive.c:481
msgid "compress better"
msgstr "добро компресиране"
-#: archive.c:477
+#: archive.c:484
msgid "list supported archive formats"
msgstr "извеждане на списъка с поддържаните формати"
-#: archive.c:479 builtin/archive.c:91 builtin/clone.c:113 builtin/clone.c:116
+#: archive.c:486 builtin/archive.c:91 builtin/clone.c:113 builtin/clone.c:116
#: builtin/submodule--helper.c:1406 builtin/submodule--helper.c:1911
msgid "repo"
msgstr "хранилище"
-#: archive.c:480 builtin/archive.c:92
+#: archive.c:487 builtin/archive.c:92
msgid "retrieve the archive from remote repository <repo>"
msgstr "изтегляне на архива от отдалеченото ХРАНИЛИЩЕ"
-#: archive.c:481 builtin/archive.c:93 builtin/difftool.c:715
+#: archive.c:488 builtin/archive.c:93 builtin/difftool.c:715
#: builtin/notes.c:498
msgid "command"
msgstr "команда"
-#: archive.c:482 builtin/archive.c:94
+#: archive.c:489 builtin/archive.c:94
msgid "path to the remote git-upload-archive command"
msgstr "път към отдалечената команда „git-upload-archive“"
-#: archive.c:489
+#: archive.c:496
msgid "Unexpected option --remote"
msgstr "Неочаквана опция „--remote“"
-#: archive.c:491
+#: archive.c:498
msgid "Option --exec can only be used together with --remote"
msgstr "Опцията „--exec“ изисква „--remote“"
-#: archive.c:493
+#: archive.c:500
msgid "Unexpected option --output"
msgstr "Неочаквана опция „--output“"
-#: archive.c:515
+#: archive.c:522
#, c-format
msgid "Unknown archive format '%s'"
msgstr "Непознат формат на архив: „%s“"
-#: archive.c:522
+#: archive.c:529
#, c-format
msgid "Argument not supported for format '%s': -%d"
msgstr "Аргументът не се поддържа за форма̀та „%s“: -%d"
-#: archive-tar.c:125 archive-zip.c:351
-#, c-format
-msgid "cannot stream blob %s"
-msgstr "обектът-BLOB „%s“ не може да бъде обработен"
-
-#: archive-tar.c:266 archive-zip.c:369
-#, c-format
-msgid "unsupported file mode: 0%o (SHA1: %s)"
-msgstr "неподдържани права за достъп до файл: 0%o (SHA1: %s)"
-
-#: archive-tar.c:293 archive-zip.c:359
-#, c-format
-msgid "cannot read %s"
-msgstr "обектът „%s“ не може да бъде прочетен"
-
-#: archive-tar.c:465
-#, c-format
-msgid "unable to start '%s' filter"
-msgstr "филтърът „%s“ не може да бъде стартиран"
-
-#: archive-tar.c:468
-msgid "unable to redirect descriptor"
-msgstr "дескрипторът не може да бъде пренасочен"
-
-#: archive-tar.c:475
-#, c-format
-msgid "'%s' filter reported error"
-msgstr "филтърът „%s“ върна грешка"
-
-#: archive-zip.c:319
-#, c-format
-msgid "path is not valid UTF-8: %s"
-msgstr "пътят не е правилно кодиран в UTF-8: %s"
-
-#: archive-zip.c:323
-#, c-format
-msgid "path too long (%d chars, SHA1: %s): %s"
-msgstr "твърде дълъг път (%d знака, SHA1: %s): %s"
-
-#: archive-zip.c:480 builtin/pack-objects.c:231 builtin/pack-objects.c:234
-#, c-format
-msgid "deflate error (%d)"
-msgstr "грешка при декомпресиране с „deflate“ (%d)"
-
-#: archive-zip.c:615
-#, c-format
-msgid "timestamp too large for this system: %<PRIuMAX>"
-msgstr "времевата стойност е твърде голяма за тази система: %<PRIuMAX>"
-
#: attr.c:212
#, c-format
msgid "%.*s is not a valid attribute name"
@@ -1898,12 +1909,12 @@ msgstr "Двоично търсене: трябва да се провери б
msgid "a %s revision is needed"
msgstr "необходима е версия „%s“"
-#: bisect.c:920 builtin/notes.c:177 builtin/tag.c:254
+#: bisect.c:920 builtin/notes.c:177 builtin/tag.c:255
#, c-format
msgid "could not create file '%s'"
msgstr "файлът „%s“ не може да бъде създаден"
-#: bisect.c:966 builtin/merge.c:149
+#: bisect.c:966 builtin/merge.c:151
#, c-format
msgid "could not read file '%s'"
msgstr "файлът „%s“ не може да бъде прочетен"
@@ -1943,41 +1954,41 @@ msgid_plural "Bisecting: %d revisions left to test after this %s\n"
msgstr[0] "Двоично търсене: остава %d версия след тази %s\n"
msgstr[1] "Двоично търсене: остават %d версии след тази %s\n"
-#: blame.c:2700
+#: blame.c:2777
msgid "--contents and --reverse do not blend well."
msgstr "Опциите „--contents“ и „--reverse“ са несъвместими"
-#: blame.c:2714
+#: blame.c:2791
msgid "cannot use --contents with final commit object name"
msgstr "Опцията „--contents“ е несъвместима с име на обект от крайно подаване"
-#: blame.c:2735
+#: blame.c:2812
msgid "--reverse and --first-parent together require specified latest commit"
msgstr ""
"Едновременното задаване на опциите „--reverse“ и „--first-parent“ изисква "
"указването на крайно подаване"
-#: blame.c:2744 bundle.c:167 ref-filter.c:2203 remote.c:1942 sequencer.c:2006
-#: sequencer.c:4358 submodule.c:847 builtin/commit.c:1045 builtin/log.c:388
-#: builtin/log.c:991 builtin/log.c:1520 builtin/log.c:1925 builtin/log.c:2215
-#: builtin/merge.c:412 builtin/pack-objects.c:3275 builtin/pack-objects.c:3290
+#: blame.c:2821 bundle.c:167 ref-filter.c:2200 remote.c:1924 sequencer.c:2018
+#: sequencer.c:4466 submodule.c:847 builtin/commit.c:1047 builtin/log.c:405
+#: builtin/log.c:1012 builtin/log.c:1541 builtin/log.c:1945 builtin/log.c:2235
+#: builtin/merge.c:415 builtin/pack-objects.c:3276 builtin/pack-objects.c:3291
#: builtin/shortlog.c:192
msgid "revision walk setup failed"
msgstr "неуспешно настройване на обхождането на версиите"
-#: blame.c:2762
+#: blame.c:2839
msgid ""
"--reverse --first-parent together require range along first-parent chain"
msgstr ""
"Едновременното задаване на опциите „--reverse“ и „--first-parent“ изисква "
"указването на диапазон по веригата на първите наследници"
-#: blame.c:2773
+#: blame.c:2850
#, c-format
msgid "no such path %s in %s"
msgstr "няма път на име „%s“ в „%s“"
-#: blame.c:2784
+#: blame.c:2861
#, c-format
msgid "cannot read blob %s for path %s"
msgstr "обектът-BLOB „%s“ в пътя %s не може да бъде прочетен"
@@ -2110,12 +2121,12 @@ msgstr "Името на обект не е еднозначно: „%s“"
msgid "Not a valid branch point: '%s'."
msgstr "Неправилно място за начало на клон: „%s“"
-#: branch.c:364
+#: branch.c:365
#, c-format
msgid "'%s' is already checked out at '%s'"
msgstr "„%s“ вече е изтеглен в „%s“"
-#: branch.c:387
+#: branch.c:388
#, c-format
msgid "HEAD of working tree %s is not updated"
msgstr "Указателят „HEAD“ на работното дърво „%s“ не е обновен"
@@ -2130,8 +2141,8 @@ msgstr "Файлът „%s“ не изглежда да е пратка на gi
msgid "unrecognized header: %s%s (%d)"
msgstr "непозната заглавна част: %s%s (%d)"
-#: bundle.c:90 rerere.c:480 rerere.c:690 sequencer.c:2258 sequencer.c:3016
-#: builtin/commit.c:815
+#: bundle.c:90 rerere.c:480 rerere.c:690 sequencer.c:2270 sequencer.c:3034
+#: builtin/commit.c:814
#, c-format
msgid "could not open '%s'"
msgstr "„%s“ не може да се отвори"
@@ -2184,7 +2195,7 @@ msgid "ref '%s' is excluded by the rev-list options"
msgstr ""
"указателят „%s“ не е бил включен поради опциите зададени на „git rev-list“"
-#: bundle.c:461 builtin/log.c:203 builtin/log.c:1814 builtin/shortlog.c:306
+#: bundle.c:461 builtin/log.c:208 builtin/log.c:1834 builtin/shortlog.c:306
#, c-format
msgid "unrecognized argument: %s"
msgstr "непознат аргумент: %s"
@@ -2207,313 +2218,263 @@ msgstr "Командата „git index-pack“ не завърши успешн
msgid "invalid color value: %.*s"
msgstr "неправилна стойност за цвят: %.*s"
-#: commit.c:51 sequencer.c:2719 builtin/am.c:359 builtin/am.c:403
-#: builtin/am.c:1371 builtin/am.c:2014 builtin/replace.c:457
-#, c-format
-msgid "could not parse %s"
-msgstr "„%s“ не може да се анализира"
-
-#: commit.c:53
-#, c-format
-msgid "%s %s is not a commit!"
-msgstr "%s %s не е подаване!"
-
-#: commit.c:193
-msgid ""
-"Support for <GIT_DIR>/info/grafts is deprecated\n"
-"and will be removed in a future Git version.\n"
-"\n"
-"Please use \"git replace --convert-graft-file\"\n"
-"to convert the grafts into replace refs.\n"
-"\n"
-"Turn this message off by running\n"
-"\"git config advice.graftFileDeprecated false\""
-msgstr ""
-"Поддръжката на „<GIT_DIR>/info/grafts“ е остаряла.\n"
-"В бъдеща версия на Git ще бъде премахната.\n"
-"\n"
-"Може да преобразувате присадките в заместващи\n"
-"указатели с командата:\n"
-"\n"
-" git replace --convert-graft-file\n"
-"\n"
-"За да изключите това съобщение, изпълнете:\n"
-"\n"
-" git config advice.graftFileDeprecated false"
-
-#: commit.c:1153
-#, c-format
-msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
-msgstr ""
-"Подаването „%s“ е с недоверен подпис от GPG, който твърди, че е на „%s“."
-
-#: commit.c:1157
-#, c-format
-msgid "Commit %s has a bad GPG signature allegedly by %s."
-msgstr ""
-"Подаването „%s“ е с неправилен подпис от GPG, който твърди, че е на „%s“."
-
-#: commit.c:1160
-#, c-format
-msgid "Commit %s does not have a GPG signature."
-msgstr "Подаването „%s“ е без подпис от GPG."
-
-#: commit.c:1163
-#, c-format
-msgid "Commit %s has a good GPG signature by %s\n"
-msgstr "Подаването „%s“ е с коректен подпис от GPG на „%s“.\n"
-
-#: commit.c:1417
-msgid ""
-"Warning: commit message did not conform to UTF-8.\n"
-"You may want to amend it after fixing the message, or set the config\n"
-"variable i18n.commitencoding to the encoding your project uses.\n"
-msgstr ""
-"ПРЕДУПРЕЖДЕНИЕ: съобщението при подаване не е кодирано с UTF-8.\n"
-"Може да поправите подаването заедно със съобщението или може да\n"
-"зададете ползваното кодиране в настройката „i18n.commitencoding“.\n"
-
-#: commit-graph.c:122
+#: commit-graph.c:183
msgid "commit-graph file is too small"
msgstr "файлът с гра̀фа на подаванията е твърде малък"
-#: commit-graph.c:189
+#: commit-graph.c:248
#, c-format
msgid "commit-graph signature %X does not match signature %X"
msgstr "отпечатъкът на гра̀фа с подаванията %X не съвпада с %X"
-#: commit-graph.c:196
+#: commit-graph.c:255
#, c-format
msgid "commit-graph version %X does not match version %X"
msgstr "версията на гра̀фа с подаванията %X не съвпада с %X"
-#: commit-graph.c:203
+#: commit-graph.c:262
#, c-format
msgid "commit-graph hash version %X does not match version %X"
msgstr "версията на контролната сума на гра̀фа с подаванията %X не съвпада с %X"
-#: commit-graph.c:226
+#: commit-graph.c:284
msgid "commit-graph chunk lookup table entry missing; file may be incomplete"
msgstr ""
"записът в таблицата за откъси липсва, файлът за гра̀фа може да е непълен"
-#: commit-graph.c:237
+#: commit-graph.c:294
#, c-format
msgid "commit-graph improper chunk offset %08x%08x"
msgstr "неправилно отместване на откъс: %08x%08x"
-#: commit-graph.c:280
+#: commit-graph.c:362
#, c-format
msgid "commit-graph chunk id %08x appears multiple times"
msgstr "откъсът %08x се явява многократно"
-#: commit-graph.c:343
+#: commit-graph.c:436
msgid "commit-graph has no base graphs chunk"
msgstr "базовият откъс липсва в гра̀фа с подаванията"
-#: commit-graph.c:353
+#: commit-graph.c:446
msgid "commit-graph chain does not match"
msgstr "веригата на гра̀фа с подаванията не съвпада"
-#: commit-graph.c:401
+#: commit-graph.c:494
#, c-format
msgid "invalid commit-graph chain: line '%s' not a hash"
msgstr ""
"грешка във веригата на гра̀фа с подаванията: ред „%s“ не е контролна сума"
-#: commit-graph.c:425
+#: commit-graph.c:518
msgid "unable to find all commit-graph files"
msgstr "някои файлове на гра̀фа с подаванията не могат да бъдат открити"
-#: commit-graph.c:558 commit-graph.c:618
+#: commit-graph.c:651 commit-graph.c:711
msgid "invalid commit position. commit-graph is likely corrupt"
msgstr ""
"неправилна позиция на подаването. Вероятно графът с подаванията е повреден"
-#: commit-graph.c:579
+#: commit-graph.c:672
#, c-format
msgid "could not find commit %s"
msgstr "подаването „%s“ не може да бъде открито"
-#: commit-graph.c:852 builtin/am.c:1292
+#: commit-graph.c:948 builtin/am.c:1292
#, c-format
msgid "unable to parse commit %s"
msgstr "подаването не може да бъде анализирано: %s"
-#: commit-graph.c:1011 builtin/pack-objects.c:2782
+#: commit-graph.c:1096
+msgid "Writing changed paths Bloom filters index"
+msgstr "Запис на индекса за промените в пътищата следени от филтрите на Блум"
+
+#: commit-graph.c:1121
+msgid "Writing changed paths Bloom filters data"
+msgstr "Запис на данните за промените в пътищата следени от филтрите на Блум"
+
+#: commit-graph.c:1160 builtin/pack-objects.c:2783
#, c-format
msgid "unable to get type of object %s"
msgstr "видът на обекта „%s“ не може да бъде определен"
-#: commit-graph.c:1043
+#: commit-graph.c:1196
msgid "Loading known commits in commit graph"
msgstr "Зареждане на познатите подавания в гра̀фа с подаванията"
-#: commit-graph.c:1060
+#: commit-graph.c:1213
msgid "Expanding reachable commits in commit graph"
msgstr "Разширяване на достижимите подавания в гра̀фа"
-#: commit-graph.c:1079
+#: commit-graph.c:1233
msgid "Clearing commit marks in commit graph"
msgstr "Изчистване на отбелязванията на подаванията в гра̀фа с подаванията"
-#: commit-graph.c:1098
+#: commit-graph.c:1252
msgid "Computing commit graph generation numbers"
msgstr "Изчисляване на номерата на поколенията в гра̀фа с подаванията"
-#: commit-graph.c:1173
+#: commit-graph.c:1300
+msgid "Computing commit changed paths Bloom filters"
+msgstr "Изчисляване на филтрите на Блум на пътищата с промяна при подаването"
+
+#: commit-graph.c:1359
#, c-format
msgid "Finding commits for commit graph in %d pack"
msgid_plural "Finding commits for commit graph in %d packs"
msgstr[0] "Откриване на подаванията в гра̀фа в %d пакетен файл"
msgstr[1] "Откриване на подаванията в гра̀фа в %d пакетни файла"
-#: commit-graph.c:1186
+#: commit-graph.c:1372
#, c-format
msgid "error adding pack %s"
msgstr "грешка при добавяне на пакетен файл „%s“"
-#: commit-graph.c:1190
+#: commit-graph.c:1376
#, c-format
msgid "error opening index for %s"
msgstr "грешка при отваряне на индекса на „%s“"
-#: commit-graph.c:1214
+#: commit-graph.c:1405
#, c-format
msgid "Finding commits for commit graph from %d ref"
msgid_plural "Finding commits for commit graph from %d refs"
msgstr[0] "Откриване на подаванията в гра̀фа измежду %d указател"
msgstr[1] "Откриване на подаванията в гра̀фа измежду %d указатели"
-#: commit-graph.c:1234
+#: commit-graph.c:1426
#, c-format
msgid "invalid commit object id: %s"
msgstr "неправилен идентификатор на обект-подаване: „%s“"
-#: commit-graph.c:1249
+#: commit-graph.c:1442
msgid "Finding commits for commit graph among packed objects"
msgstr "Откриване на подаванията в гра̀фа измежду пакетираните обекти"
-#: commit-graph.c:1264
+#: commit-graph.c:1457
msgid "Counting distinct commits in commit graph"
msgstr "Преброяване на различните подавания в гра̀фа"
-#: commit-graph.c:1294
+#: commit-graph.c:1489
msgid "Finding extra edges in commit graph"
msgstr "Откриване на още върхове в гра̀фа с подаванията"
-#: commit-graph.c:1340
+#: commit-graph.c:1538
msgid "failed to write correct number of base graph ids"
msgstr "правилният брой на базовите идентификатори не може да се запише"
-#: commit-graph.c:1373 midx.c:814
+#: commit-graph.c:1572 midx.c:812
#, c-format
msgid "unable to create leading directories of %s"
msgstr "родителските директории на „%s“ не могат да бъдат създадени"
-#: commit-graph.c:1385 builtin/index-pack.c:306 builtin/repack.c:248
+#: commit-graph.c:1585
+msgid "unable to create temporary graph layer"
+msgstr "не може да бъде създаден временен слой за гра̀фа с подаванията"
+
+#: commit-graph.c:1590
#, c-format
-msgid "unable to create '%s'"
-msgstr "пакетният файл „%s“ не може да бъде създаден"
+msgid "unable to adjust shared permissions for '%s'"
+msgstr "правата за споделен достъп до „%s“ не могат да бъдат зададени"
-#: commit-graph.c:1445
+#: commit-graph.c:1667
#, c-format
msgid "Writing out commit graph in %d pass"
msgid_plural "Writing out commit graph in %d passes"
msgstr[0] "Запазване на гра̀фа с подаванията в %d пас"
msgstr[1] "Запазване на гра̀фа с подаванията в %d паса"
-#: commit-graph.c:1486
+#: commit-graph.c:1712
msgid "unable to open commit-graph chain file"
msgstr "файлът с веригата на гра̀фа с подаванията не може да се отвори"
-#: commit-graph.c:1498
+#: commit-graph.c:1728
msgid "failed to rename base commit-graph file"
msgstr "основният файл на гра̀фа с подаванията не може да бъде преименуван"
-#: commit-graph.c:1518
+#: commit-graph.c:1748
msgid "failed to rename temporary commit-graph file"
msgstr "временният файл на гра̀фа с подаванията не може да бъде преименуван"
-#: commit-graph.c:1631
+#: commit-graph.c:1874
msgid "Scanning merged commits"
msgstr "Търсене на подаванията със сливания"
-#: commit-graph.c:1642
+#: commit-graph.c:1885
#, c-format
msgid "unexpected duplicate commit id %s"
msgstr "неочакван, повтарящ се идентификатор на подаване: %s"
-#: commit-graph.c:1665
+#: commit-graph.c:1908
msgid "Merging commit-graph"
msgstr "Сливане на гра̀фа с подаванията"
-#: commit-graph.c:1844
+#: commit-graph.c:2096
#, c-format
msgid "the commit graph format cannot write %d commits"
msgstr "форматът на гра̀фа с подаванията не може да запише %d подавания"
-#: commit-graph.c:1855
+#: commit-graph.c:2107
msgid "too many commits to write graph"
msgstr "прекалено много подавания за записване на гра̀фа"
-#: commit-graph.c:1944
+#: commit-graph.c:2200
msgid "the commit-graph file has incorrect checksum and is likely corrupt"
msgstr "графът с подаванията е с грешна сума за проверка — вероятно е повреден"
-#: commit-graph.c:1954
+#: commit-graph.c:2210
#, c-format
msgid "commit-graph has incorrect OID order: %s then %s"
msgstr ""
"неправилна подредба на обектите по идентификатор в гра̀фа с подаванията: „%s“ "
"е преди „%s“, а не трябва"
-#: commit-graph.c:1964 commit-graph.c:1979
+#: commit-graph.c:2220 commit-graph.c:2235
#, c-format
msgid "commit-graph has incorrect fanout value: fanout[%d] = %u != %u"
msgstr ""
"неправилна стойност за откъс в гра̀фа с подаванията: fanout[%d] = %u, а "
"трябва да е %u"
-#: commit-graph.c:1971
+#: commit-graph.c:2227
#, c-format
msgid "failed to parse commit %s from commit-graph"
msgstr "подаване „%s“ в гра̀фа с подаванията не може да се анализира"
-#: commit-graph.c:1989
+#: commit-graph.c:2245
msgid "Verifying commits in commit graph"
msgstr "Проверка на подаванията в гра̀фа"
-#: commit-graph.c:2003
+#: commit-graph.c:2259
#, c-format
msgid "failed to parse commit %s from object database for commit-graph"
msgstr ""
"подаване „%s“ в базата от данни към гра̀фа с подаванията не може да се "
"анализира"
-#: commit-graph.c:2010
+#: commit-graph.c:2266
#, c-format
msgid "root tree OID for commit %s in commit-graph is %s != %s"
msgstr ""
"идентификаторът на обект за кореновото дърво за подаване „%s“ в гра̀фа с "
"подаванията е „%s“, а трябва да е „%s“"
-#: commit-graph.c:2020
+#: commit-graph.c:2276
#, c-format
msgid "commit-graph parent list for commit %s is too long"
msgstr "списъкът с родители на „%s“ в гра̀фа с подаванията е прекалено дълъг"
-#: commit-graph.c:2029
+#: commit-graph.c:2285
#, c-format
msgid "commit-graph parent for %s is %s != %s"
msgstr "родителят на „%s“ в гра̀фа с подаванията е „%s“, а трябва да е „%s“"
-#: commit-graph.c:2042
+#: commit-graph.c:2298
#, c-format
msgid "commit-graph parent list for commit %s terminates early"
msgstr "списъкът с родители на „%s“ в гра̀фа с подаванията е прекалено къс"
-#: commit-graph.c:2047
+#: commit-graph.c:2303
#, c-format
msgid ""
"commit-graph has generation number zero for commit %s, but non-zero elsewhere"
@@ -2521,7 +2482,7 @@ msgstr ""
"номерът на поколението на подаване „%s“ в гра̀фа с подаванията е 0, а другаде "
"не е"
-#: commit-graph.c:2051
+#: commit-graph.c:2307
#, c-format
msgid ""
"commit-graph has non-zero generation number for commit %s, but zero elsewhere"
@@ -2529,25 +2490,91 @@ msgstr ""
"номерът на поколението на подаване „%s“ в гра̀фа с подаванията не е 0, а "
"другаде е"
-#: commit-graph.c:2066
+#: commit-graph.c:2322
#, c-format
msgid "commit-graph generation for commit %s is %u != %u"
msgstr ""
"номерът на поколението на подаване „%s“ в гра̀фа с подаванията е %u, а "
"другаде е %u"
-#: commit-graph.c:2072
+#: commit-graph.c:2328
#, c-format
msgid "commit date for commit %s in commit-graph is %<PRIuMAX> != %<PRIuMAX>"
msgstr ""
"датата на подаване на „%s“ в гра̀фа с подаванията е %<PRIuMAX>, а трябва да е "
"%<PRIuMAX>"
+#: commit.c:52 sequencer.c:2739 builtin/am.c:359 builtin/am.c:403
+#: builtin/am.c:1371 builtin/am.c:2013 builtin/replace.c:457
+#, c-format
+msgid "could not parse %s"
+msgstr "„%s“ не може да се анализира"
+
+#: commit.c:54
+#, c-format
+msgid "%s %s is not a commit!"
+msgstr "%s %s не е подаване!"
+
+#: commit.c:194
+msgid ""
+"Support for <GIT_DIR>/info/grafts is deprecated\n"
+"and will be removed in a future Git version.\n"
+"\n"
+"Please use \"git replace --convert-graft-file\"\n"
+"to convert the grafts into replace refs.\n"
+"\n"
+"Turn this message off by running\n"
+"\"git config advice.graftFileDeprecated false\""
+msgstr ""
+"Поддръжката на „<GIT_DIR>/info/grafts“ е остаряла.\n"
+"В бъдеща версия на Git ще бъде премахната.\n"
+"\n"
+"Може да преобразувате присадките в заместващи\n"
+"указатели с командата:\n"
+"\n"
+" git replace --convert-graft-file\n"
+"\n"
+"За да изключите това съобщение, изпълнете:\n"
+"\n"
+" git config advice.graftFileDeprecated false"
+
+#: commit.c:1168
+#, c-format
+msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
+msgstr ""
+"Подаването „%s“ е с недоверен подпис от GPG, който твърди, че е на „%s“."
+
+#: commit.c:1172
+#, c-format
+msgid "Commit %s has a bad GPG signature allegedly by %s."
+msgstr ""
+"Подаването „%s“ е с неправилен подпис от GPG, който твърди, че е на „%s“."
+
+#: commit.c:1175
+#, c-format
+msgid "Commit %s does not have a GPG signature."
+msgstr "Подаването „%s“ е без подпис от GPG."
+
+#: commit.c:1178
+#, c-format
+msgid "Commit %s has a good GPG signature by %s\n"
+msgstr "Подаването „%s“ е с коректен подпис от GPG на „%s“.\n"
+
+#: commit.c:1432
+msgid ""
+"Warning: commit message did not conform to UTF-8.\n"
+"You may want to amend it after fixing the message, or set the config\n"
+"variable i18n.commitencoding to the encoding your project uses.\n"
+msgstr ""
+"ПРЕДУПРЕЖДЕНИЕ: съобщението при подаване не е кодирано с UTF-8.\n"
+"Може да поправите подаването заедно със съобщението или може да\n"
+"зададете ползваното кодиране в настройката „i18n.commitencoding“.\n"
+
#: compat/obstack.c:406 compat/obstack.c:408
msgid "memory exhausted"
msgstr "паметта свърши"
-#: config.c:124
+#: config.c:125
#, c-format
msgid ""
"exceeded maximum include depth (%d) while including\n"
@@ -2562,162 +2589,162 @@ msgstr ""
" %s\n"
"Това може да се дължи на зацикляне при вмъкването."
-#: config.c:140
+#: config.c:141
#, c-format
msgid "could not expand include path '%s'"
msgstr "пътят за вмъкване „%s“не може да бъде разширен"
-#: config.c:151
+#: config.c:152
msgid "relative config includes must come from files"
msgstr "относителните вмъквания на конфигурации трябва да идват от файлове"
-#: config.c:197
+#: config.c:198
msgid "relative config include conditionals must come from files"
msgstr "относителните условни изрази за вмъкване трябва да идват от файлове"
-#: config.c:376
+#: config.c:378
#, c-format
msgid "key does not contain a section: %s"
msgstr "ключът не съдържа раздел: „%s“"
-#: config.c:382
+#: config.c:384
#, c-format
msgid "key does not contain variable name: %s"
msgstr "ключът не съдържа име на променлива: „%s“"
-#: config.c:406 sequencer.c:2444
+#: config.c:408 sequencer.c:2456
#, c-format
msgid "invalid key: %s"
msgstr "неправилен ключ: „%s“"
-#: config.c:412
+#: config.c:414
#, c-format
msgid "invalid key (newline): %s"
msgstr "неправилен ключ (нов ред): „%s“"
-#: config.c:448 config.c:460
+#: config.c:450 config.c:462
#, c-format
msgid "bogus config parameter: %s"
msgstr "неправилен конфигурационен параметър: „%s“"
-#: config.c:495
+#: config.c:497
#, c-format
msgid "bogus format in %s"
msgstr "неправилен формат в „%s“"
-#: config.c:821
+#: config.c:836
#, c-format
msgid "bad config line %d in blob %s"
msgstr "неправилен ред за настройки %d в BLOB „%s“"
-#: config.c:825
+#: config.c:840
#, c-format
msgid "bad config line %d in file %s"
msgstr "неправилен ред за настройки %d във файла „%s“"
-#: config.c:829
+#: config.c:844
#, c-format
msgid "bad config line %d in standard input"
msgstr "неправилен ред за настройки %d на стандартния вход"
-#: config.c:833
+#: config.c:848
#, c-format
msgid "bad config line %d in submodule-blob %s"
msgstr "неправилен ред за настройки %d в BLOB за подмодул „%s“"
-#: config.c:837
+#: config.c:852
#, c-format
msgid "bad config line %d in command line %s"
msgstr "неправилен ред за настройки %d на командния ред „%s“"
-#: config.c:841
+#: config.c:856
#, c-format
msgid "bad config line %d in %s"
msgstr "неправилен ред за настройки %d в „%s“"
-#: config.c:978
+#: config.c:993
msgid "out of range"
msgstr "извън диапазона"
-#: config.c:978
+#: config.c:993
msgid "invalid unit"
msgstr "неправилна мерна единица"
-#: config.c:979
+#: config.c:994
#, c-format
msgid "bad numeric config value '%s' for '%s': %s"
msgstr "неправилна числова стойност „%s“ за „%s“: %s"
-#: config.c:998
+#: config.c:1013
#, c-format
msgid "bad numeric config value '%s' for '%s' in blob %s: %s"
msgstr "неправилна числова стойност „%s“ за „%s“ в BLOB „%s“: %s"
-#: config.c:1001
+#: config.c:1016
#, c-format
msgid "bad numeric config value '%s' for '%s' in file %s: %s"
msgstr "неправилна числова стойност „%s“ за „%s“ във файла „%s“: %s"
-#: config.c:1004
+#: config.c:1019
#, c-format
msgid "bad numeric config value '%s' for '%s' in standard input: %s"
msgstr "неправилна числова стойност „%s“ за „%s“ на стандартния вход: %s"
-#: config.c:1007
+#: config.c:1022
#, c-format
msgid "bad numeric config value '%s' for '%s' in submodule-blob %s: %s"
msgstr "неправилна числова стойност „%s“ за „%s“ в BLOB от подмодул „%s“: %s"
-#: config.c:1010
+#: config.c:1025
#, c-format
msgid "bad numeric config value '%s' for '%s' in command line %s: %s"
msgstr "неправилна числова стойност „%s“ за „%s“ на командния ред „%s“: %s"
-#: config.c:1013
+#: config.c:1028
#, c-format
msgid "bad numeric config value '%s' for '%s' in %s: %s"
msgstr "неправилна числова стойност „%s“ за „%s“ в %s: %s"
-#: config.c:1108
+#: config.c:1123
#, c-format
msgid "failed to expand user dir in: '%s'"
msgstr "домашната папка на потребителя не може да бъде открита: „%s“"
-#: config.c:1117
+#: config.c:1132
#, c-format
msgid "'%s' for '%s' is not a valid timestamp"
msgstr "„%s“ не е правилна стойност за време за „%s“"
-#: config.c:1208
+#: config.c:1223
#, c-format
msgid "abbrev length out of range: %d"
msgstr "дължината на съкращаване е извън интервала ([4; 40]): %d"
-#: config.c:1222 config.c:1233
+#: config.c:1237 config.c:1248
#, c-format
msgid "bad zlib compression level %d"
msgstr "неправилно ниво на компресиране: %d"
-#: config.c:1325
+#: config.c:1340
msgid "core.commentChar should only be one character"
msgstr "настройката „core.commentChar“ трябва да е само един знак"
-#: config.c:1358
+#: config.c:1373
#, c-format
msgid "invalid mode for object creation: %s"
msgstr "неправилен режим за създаването на обекти: %s"
-#: config.c:1430
+#: config.c:1445
#, c-format
msgid "malformed value for %s"
msgstr "неправилна стойност за „%s“"
-#: config.c:1456
+#: config.c:1471
#, c-format
msgid "malformed value for %s: %s"
msgstr "неправилна стойност за „%s“: „%s“"
-#: config.c:1457
+#: config.c:1472
msgid "must be one of nothing, matching, simple, upstream or current"
msgstr ""
"трябва да е една от следните стойности: „nothing“ (без изтласкване при липса "
@@ -2725,132 +2752,132 @@ msgstr ""
"„simple“ (клонът със същото име, от който се издърпва), „upstream“ (клонът, "
"от който се издърпва) или „current“ (клонът със същото име)"
-#: config.c:1518 builtin/pack-objects.c:3541
+#: config.c:1533 builtin/pack-objects.c:3542
#, c-format
msgid "bad pack compression level %d"
msgstr "неправилно ниво на компресиране при пакетиране: %d"
-#: config.c:1639
+#: config.c:1655
#, c-format
msgid "unable to load config blob object '%s'"
msgstr "обектът-BLOB „%s“ с конфигурации не може да се зареди"
-#: config.c:1642
+#: config.c:1658
#, c-format
msgid "reference '%s' does not point to a blob"
msgstr "указателят „%s“ не сочи към обект-BLOB"
-#: config.c:1659
+#: config.c:1675
#, c-format
msgid "unable to resolve config blob '%s'"
msgstr "обектът-BLOB „%s“ с конфигурации не може да бъде открит"
-#: config.c:1689
+#: config.c:1705
#, c-format
msgid "failed to parse %s"
msgstr "„%s“ не може да бъде анализиран"
-#: config.c:1743
+#: config.c:1759
msgid "unable to parse command-line config"
msgstr "неправилни настройки от командния ред"
-#: config.c:2097
+#: config.c:2113
msgid "unknown error occurred while reading the configuration files"
msgstr "неочаквана грешка при изчитането на конфигурационните файлове"
-#: config.c:2267
+#: config.c:2283
#, c-format
msgid "Invalid %s: '%s'"
msgstr "Неправилен %s: „%s“"
-#: config.c:2312
+#: config.c:2328
#, c-format
msgid "splitIndex.maxPercentChange value '%d' should be between 0 and 100"
msgstr ""
"стойността на „splitIndex.maxPercentChange“ трябва да е между 1 и 100, а не "
"%d"
-#: config.c:2358
+#: config.c:2374
#, c-format
msgid "unable to parse '%s' from command-line config"
msgstr "неразпозната стойност „%s“ от командния ред"
-#: config.c:2360
+#: config.c:2376
#, c-format
msgid "bad config variable '%s' in file '%s' at line %d"
msgstr "неправилна настройка „%s“ във файла „%s“ на ред №%d"
-#: config.c:2441
+#: config.c:2457
#, c-format
msgid "invalid section name '%s'"
msgstr "неправилно име на раздел: „%s“"
-#: config.c:2473
+#: config.c:2489
#, c-format
msgid "%s has multiple values"
msgstr "зададени са няколко стойности за „%s“"
-#: config.c:2502
+#: config.c:2518
#, c-format
msgid "failed to write new configuration file %s"
msgstr "новият конфигурационен файл „%s“ не може да бъде запазен"
-#: config.c:2754 config.c:3078
+#: config.c:2770 config.c:3094
#, c-format
msgid "could not lock config file %s"
msgstr "конфигурационният файл „%s“ не може да бъде заключен"
-#: config.c:2765
+#: config.c:2781
#, c-format
msgid "opening %s"
msgstr "отваряне на „%s“"
-#: config.c:2800 builtin/config.c:344
+#: config.c:2816 builtin/config.c:344
#, c-format
msgid "invalid pattern: %s"
msgstr "неправилен шаблон: %s"
-#: config.c:2825
+#: config.c:2841
#, c-format
msgid "invalid config file %s"
msgstr "неправилен конфигурационен файл: „%s“"
-#: config.c:2838 config.c:3091
+#: config.c:2854 config.c:3107
#, c-format
msgid "fstat on %s failed"
msgstr "неуспешно изпълнение на „fstat“ върху „%s“"
-#: config.c:2849
+#: config.c:2865
#, c-format
msgid "unable to mmap '%s'"
msgstr "неуспешно изпълнение на „mmap“ върху „%s“"
-#: config.c:2858 config.c:3096
+#: config.c:2874 config.c:3112
#, c-format
msgid "chmod on %s failed"
msgstr "неуспешна смяна на права с „chmod“ върху „%s“"
-#: config.c:2943 config.c:3193
+#: config.c:2959 config.c:3209
#, c-format
msgid "could not write config file %s"
msgstr "конфигурационният файл „%s“ не може да бъде записан"
-#: config.c:2977
+#: config.c:2993
#, c-format
msgid "could not set '%s' to '%s'"
msgstr "„%s“ не може да се зададе да е „%s“"
-#: config.c:2979 builtin/remote.c:655 builtin/remote.c:849 builtin/remote.c:857
+#: config.c:2995 builtin/remote.c:655 builtin/remote.c:849 builtin/remote.c:857
#, c-format
msgid "could not unset '%s'"
msgstr "„%s“ не може да се премахне"
-#: config.c:3069
+#: config.c:3085
#, c-format
msgid "invalid section name: %s"
msgstr "неправилно име на раздел: %s"
-#: config.c:3236
+#: config.c:3252
#, c-format
msgid "missing value for '%s'"
msgstr "липсва стойност за „%s“"
@@ -2920,7 +2947,7 @@ msgstr "неправилен отговор на „ls-refs“: „%s“"
#: connect.c:445
msgid "expected flush after ref listing"
-msgstr "очакваше се изчистване на буферите след изброяването на указателите"
+msgstr "след изброяването на указателите се очаква изчистване на буферите"
#: connect.c:544
#, c-format
@@ -3019,19 +3046,19 @@ msgstr "необичайният път „%s“ е блокиран"
msgid "unable to fork"
msgstr "неуспешно създаване на процес"
-#: connected.c:98 builtin/fsck.c:208 builtin/prune.c:43
+#: connected.c:107 builtin/fsck.c:209 builtin/prune.c:45
msgid "Checking connectivity"
msgstr "Проверка на свързаността"
-#: connected.c:110
+#: connected.c:119
msgid "Could not run 'git rev-list'"
msgstr "Командата „git rev-list“ не може да бъде изпълнена."
-#: connected.c:130
+#: connected.c:139
msgid "failed write to rev-list"
msgstr "неуспешен запис на списъка с версиите"
-#: connected.c:137
+#: connected.c:146
msgid "failed to close rev-list's stdin"
msgstr "стандартният вход на списъка с версиите не може да бъде затворен"
@@ -3140,15 +3167,15 @@ msgstr "неуспешно четене от външния филтър „%s
msgid "external filter '%s' failed"
msgstr "неуспешно изпълнение на външния филтър „%s“"
-#: convert.c:839
+#: convert.c:840
msgid "unexpected filter type"
msgstr "неочакван вид филтър"
-#: convert.c:850
+#: convert.c:851
msgid "path name too long for external filter"
msgstr "пътят е прекалено дълъг за външен филтър"
-#: convert.c:924
+#: convert.c:943
#, c-format
msgid ""
"external filter '%s' is not available anymore although not all paths have "
@@ -3157,20 +3184,48 @@ msgstr ""
"външният филтър „%s“ вече не е наличен, въпреки че не всички пътища са "
"филтрирани"
-#: convert.c:1223
+#: convert.c:1243
msgid "true/false are no valid working-tree-encodings"
msgstr "„true“/„false“ не може да са кодирания на работното дърво"
-#: convert.c:1411 convert.c:1445
+#: convert.c:1431 convert.c:1465
#, c-format
msgid "%s: clean filter '%s' failed"
msgstr "%s: неуспешно изпълнение на декодиращ филтър „%s“"
-#: convert.c:1489
+#: convert.c:1511
#, c-format
msgid "%s: smudge filter %s failed"
msgstr "%s: неуспешно изпълнение на кодиращ филтър „%s“"
+#: credential.c:96
+#, c-format
+msgid "skipping credential lookup for key: credential.%s"
+msgstr "прескачане на търсенето на идентификация според ключа: „credential.%s“"
+
+#: credential.c:112
+msgid "refusing to work with credential missing host field"
+msgstr "адресът трябва задължително да съдържа хост"
+
+#: credential.c:114
+msgid "refusing to work with credential missing protocol field"
+msgstr "адресът трябва задължително да съдържа протокол"
+
+#: credential.c:396
+#, c-format
+msgid "url contains a newline in its %s component: %s"
+msgstr "адресът съдържа нов ред в частта за %s: %s"
+
+#: credential.c:440
+#, c-format
+msgid "url has no scheme: %s"
+msgstr "адресът е без схема: %s"
+
+#: credential.c:513
+#, c-format
+msgid "credential url cannot be parsed: %s"
+msgstr "адресът за идентификация не може да се анализира: „%s“"
+
#: date.c:138
msgid "in the future"
msgstr "в бъдещето"
@@ -3265,15 +3320,6 @@ msgstr ""
msgid "Marked %d islands, done.\n"
msgstr "Отбелязани са %d групи, работата приключи.\n"
-#: diffcore-order.c:24
-#, c-format
-msgid "failed to read orderfile '%s'"
-msgstr "файлът с подредбата на съответствията „%s“ не може да бъде прочетен"
-
-#: diffcore-rename.c:543
-msgid "Performing inexact rename detection"
-msgstr "Търсене на преименувания на обекти съчетани с промени"
-
#: diff-no-index.c:238
msgid "git diff --no-index [<options>] <path> <path>"
msgstr "git diff --no-index [ОПЦИЯ…] ПЪТ ПЪТ"
@@ -3342,38 +3388,38 @@ msgstr ""
"Грешки в настройката „diff.dirstat“:\n"
"%s"
-#: diff.c:4202
+#: diff.c:4238
#, c-format
msgid "external diff died, stopping at %s"
msgstr ""
"външната програма за разлики завърши неуспешно. Спиране на работата при „%s“"
-#: diff.c:4547
+#: diff.c:4583
msgid "--name-only, --name-status, --check and -s are mutually exclusive"
msgstr ""
"Опциите „--name-only“, „--name-status“, „--check“ и „-s“ са несъвместими "
"една с друга"
-#: diff.c:4550
+#: diff.c:4586
msgid "-G, -S and --find-object are mutually exclusive"
msgstr "Опциите „-G“, „-S“ и „--find-object“ са несъвместими една с друга"
-#: diff.c:4628
+#: diff.c:4664
msgid "--follow requires exactly one pathspec"
msgstr "Опцията „--follow“ изисква точно един път"
-#: diff.c:4676
+#: diff.c:4712
#, c-format
msgid "invalid --stat value: %s"
msgstr "неправилна стойност за „--stat“: %s"
-#: diff.c:4681 diff.c:4686 diff.c:4691 diff.c:4696 diff.c:5209
+#: diff.c:4717 diff.c:4722 diff.c:4727 diff.c:4732 diff.c:5245
#: parse-options.c:197 parse-options.c:201
#, c-format
msgid "%s expects a numerical value"
msgstr "опцията „%s“ очаква число за аргумент"
-#: diff.c:4713
+#: diff.c:4749
#, c-format
msgid ""
"Failed to parse --dirstat/-X option parameter:\n"
@@ -3382,44 +3428,44 @@ msgstr ""
"Неразпознат параметър към опцията „--dirstat/-X“:\n"
"%s"
-#: diff.c:4798
+#: diff.c:4834
#, c-format
msgid "unknown change class '%c' in --diff-filter=%s"
msgstr "непознат вид промяна: „%c“ в „--diff-filter=%s“"
-#: diff.c:4822
+#: diff.c:4858
#, c-format
msgid "unknown value after ws-error-highlight=%.*s"
msgstr "непозната стойност след „ws-error-highlight=%.*s“"
-#: diff.c:4836
+#: diff.c:4872
#, c-format
msgid "unable to resolve '%s'"
msgstr "„%s“ не може да се открие"
-#: diff.c:4886 diff.c:4892
+#: diff.c:4922 diff.c:4928
#, c-format
msgid "%s expects <n>/<m> form"
msgstr ""
"опцията „%s“ изисква стойности за МИНИМАЛЕН_%%_ПРОМЯНА_ЗА_ИЗТОЧНИК_/"
"МАКСИМАЛЕН_%%_ПРОМЯНА_ЗА_ЗАМЯНА от"
-#: diff.c:4904
+#: diff.c:4940
#, c-format
msgid "%s expects a character, got '%s'"
msgstr "опцията „%s“ изисква знак, а не: „%s“"
-#: diff.c:4925
+#: diff.c:4961
#, c-format
msgid "bad --color-moved argument: %s"
msgstr "неправилен аргумент за „--color-moved“: „%s“"
-#: diff.c:4944
+#: diff.c:4980
#, c-format
msgid "invalid mode '%s' in --color-moved-ws"
msgstr "неправилен режим „%s“ за „ --color-moved-ws“"
-#: diff.c:4984
+#: diff.c:5020
msgid ""
"option diff-algorithm accepts \"myers\", \"minimal\", \"patience\" and "
"\"histogram\""
@@ -3428,153 +3474,153 @@ msgstr ""
"Майерс), „minimal“ (минимизиране на разликите), „patience“ (пасианс) и "
"„histogram“ (хистограмен)"
-#: diff.c:5020 diff.c:5040
+#: diff.c:5056 diff.c:5076
#, c-format
msgid "invalid argument to %s"
msgstr "неправилен аргумент към „%s“"
-#: diff.c:5178
+#: diff.c:5214
#, c-format
msgid "failed to parse --submodule option parameter: '%s'"
msgstr "неразпознат параметър към опцията „--submodule“: „%s“"
-#: diff.c:5234
+#: diff.c:5270
#, c-format
msgid "bad --word-diff argument: %s"
msgstr "неправилен аргумент към „--word-diff“: „%s“"
-#: diff.c:5257
+#: diff.c:5293
msgid "Diff output format options"
msgstr "Формат на изхода за разликите"
-#: diff.c:5259 diff.c:5265
+#: diff.c:5295 diff.c:5301
msgid "generate patch"
msgstr "създаване на кръпки"
-#: diff.c:5262 builtin/log.c:173
+#: diff.c:5298 builtin/log.c:177
msgid "suppress diff output"
msgstr "без извеждане на разликите"
-#: diff.c:5267 diff.c:5381 diff.c:5388
+#: diff.c:5303 diff.c:5417 diff.c:5424
msgid "<n>"
msgstr "БРОЙ"
-#: diff.c:5268 diff.c:5271
+#: diff.c:5304 diff.c:5307
msgid "generate diffs with <n> lines context"
msgstr "файловете с разлики да са с контекст с такъв БРОЙ редове"
-#: diff.c:5273
+#: diff.c:5309
msgid "generate the diff in raw format"
msgstr "файловете с разлики да са в суров формат"
-#: diff.c:5276
+#: diff.c:5312
msgid "synonym for '-p --raw'"
msgstr "псевдоним на „-p --stat“"
-#: diff.c:5280
+#: diff.c:5316
msgid "synonym for '-p --stat'"
msgstr "псевдоним на „-p --stat“"
-#: diff.c:5284
+#: diff.c:5320
msgid "machine friendly --stat"
msgstr "„--stat“ във формат за четене от програма"
-#: diff.c:5287
+#: diff.c:5323
msgid "output only the last line of --stat"
msgstr "извеждане само на последния ред на „--stat“"
-#: diff.c:5289 diff.c:5297
+#: diff.c:5325 diff.c:5333
msgid "<param1,param2>..."
msgstr "ПАРАМЕТЪР_1, ПАРАМЕТЪР_2, …"
-#: diff.c:5290
+#: diff.c:5326
msgid ""
"output the distribution of relative amount of changes for each sub-directory"
msgstr "извеждане на разпределението на промените за всяка поддиректория"
-#: diff.c:5294
+#: diff.c:5330
msgid "synonym for --dirstat=cumulative"
msgstr "псевдоним на „--dirstat=cumulative“"
-#: diff.c:5298
+#: diff.c:5334
msgid "synonym for --dirstat=files,param1,param2..."
msgstr "псевдоним на „--dirstat=ФАЙЛОВЕ,ПАРАМЕТЪР_1,ПАРАМЕТЪР_2,…“"
-#: diff.c:5302
+#: diff.c:5338
msgid "warn if changes introduce conflict markers or whitespace errors"
msgstr ""
"предупреждаване, ако промените водят до маркери за конфликт или грешки в "
"празните знаци"
-#: diff.c:5305
+#: diff.c:5341
msgid "condensed summary such as creations, renames and mode changes"
msgstr ""
"съкратено резюме на създадените, преименуваните и файловете с промяна на "
"режима на достъп"
-#: diff.c:5308
+#: diff.c:5344
msgid "show only names of changed files"
msgstr "извеждане само на имената на променените файлове"
-#: diff.c:5311
+#: diff.c:5347
msgid "show only names and status of changed files"
msgstr "извеждане само на имената и статистиката за променените файлове"
-#: diff.c:5313
+#: diff.c:5349
msgid "<width>[,<name-width>[,<count>]]"
msgstr "ШИРОЧИНА[,ИМЕ-ШИРОЧИНА[,БРОЙ]]"
-#: diff.c:5314
+#: diff.c:5350
msgid "generate diffstat"
msgstr "извеждане на статистика за промените"
-#: diff.c:5316 diff.c:5319 diff.c:5322
+#: diff.c:5352 diff.c:5355 diff.c:5358
msgid "<width>"
msgstr "ШИРОЧИНА"
-#: diff.c:5317
+#: diff.c:5353
msgid "generate diffstat with a given width"
msgstr "статистика с такава ШИРОЧИНА за промените"
-#: diff.c:5320
+#: diff.c:5356
msgid "generate diffstat with a given name width"
msgstr "статистика за промените с такава ШИРОЧИНА на имената"
-#: diff.c:5323
+#: diff.c:5359
msgid "generate diffstat with a given graph width"
msgstr "статистика за промените с такава ШИРОЧИНА на гра̀фа"
-#: diff.c:5325
+#: diff.c:5361
msgid "<count>"
msgstr "БРОЙ"
-#: diff.c:5326
+#: diff.c:5362
msgid "generate diffstat with limited lines"
msgstr "ограничаване на БРОя на редовете в статистиката за промените"
-#: diff.c:5329
+#: diff.c:5365
msgid "generate compact summary in diffstat"
msgstr "кратко резюме в статистиката за промените"
-#: diff.c:5332
+#: diff.c:5368
msgid "output a binary diff that can be applied"
msgstr "извеждане на двоична разлика във вид за прилагане"
-#: diff.c:5335
+#: diff.c:5371
msgid "show full pre- and post-image object names on the \"index\" lines"
msgstr ""
"показване на пълните имена на обекти в редовете за индекса при вариантите "
"преди и след промяната"
-#: diff.c:5337
+#: diff.c:5373
msgid "show colored diff"
msgstr "разлики в цвят"
-#: diff.c:5338
+#: diff.c:5374
msgid "<kind>"
msgstr "ВИД"
-#: diff.c:5339
+#: diff.c:5375
msgid ""
"highlight whitespace errors in the 'context', 'old' or 'new' lines in the "
"diff"
@@ -3582,7 +3628,7 @@ msgstr ""
"грешките в празните знаци да се указват в редовете за контекста, вариантите "
"преди и след разликата,"
-#: diff.c:5342
+#: diff.c:5378
msgid ""
"do not munge pathnames and use NULs as output field terminators in --raw or "
"--numstat"
@@ -3590,257 +3636,257 @@ msgstr ""
"без преименуване на пътищата. Да се използват нулеви байтове за разделители "
"на полета в изхода при ползване на опцията „--raw“ или „--numstat“"
-#: diff.c:5345 diff.c:5348 diff.c:5351 diff.c:5457
+#: diff.c:5381 diff.c:5384 diff.c:5387 diff.c:5493
msgid "<prefix>"
msgstr "ПРЕФИКС"
-#: diff.c:5346
+#: diff.c:5382
msgid "show the given source prefix instead of \"a/\""
msgstr "префикс вместо „a/“ за източник"
-#: diff.c:5349
+#: diff.c:5385
msgid "show the given destination prefix instead of \"b/\""
msgstr "префикс вместо „b/“ за цел"
-#: diff.c:5352
+#: diff.c:5388
msgid "prepend an additional prefix to every line of output"
msgstr "добавяне на допълнителен префикс за всеки ред на изхода"
-#: diff.c:5355
+#: diff.c:5391
msgid "do not show any source or destination prefix"
msgstr "без префикс за източника и целта"
-#: diff.c:5358
+#: diff.c:5394
msgid "show context between diff hunks up to the specified number of lines"
msgstr ""
"извеждане на контекст между последователните парчета с разлики от указания "
"БРОЙ редове"
-#: diff.c:5362 diff.c:5367 diff.c:5372
+#: diff.c:5398 diff.c:5403 diff.c:5408
msgid "<char>"
msgstr "ЗНАК"
-#: diff.c:5363
+#: diff.c:5399
msgid "specify the character to indicate a new line instead of '+'"
msgstr "знак вместо „+“ за нов вариант на ред"
-#: diff.c:5368
+#: diff.c:5404
msgid "specify the character to indicate an old line instead of '-'"
msgstr "знак вместо „-“ за стар вариант на ред"
-#: diff.c:5373
+#: diff.c:5409
msgid "specify the character to indicate a context instead of ' '"
msgstr "знак вместо „ “ за контекст"
-#: diff.c:5376
+#: diff.c:5412
msgid "Diff rename options"
msgstr "Настройки за разлики с преименуване"
-#: diff.c:5377
+#: diff.c:5413
msgid "<n>[/<m>]"
msgstr "МИНИМАЛЕН_%_ПРОМЯНА_ЗА_ИЗТОЧНИК[/МАКСИМАЛEН_%_ПРОМЯНА_ЗА_ЗАМЯНА]"
-#: diff.c:5378
+#: diff.c:5414
msgid "break complete rewrite changes into pairs of delete and create"
msgstr ""
"заместване на пълните промени с последователност от изтриване и създаване"
-#: diff.c:5382
+#: diff.c:5418
msgid "detect renames"
msgstr "засичане на преименуванията"
-#: diff.c:5386
+#: diff.c:5422
msgid "omit the preimage for deletes"
msgstr "без предварителен вариант при изтриване"
-#: diff.c:5389
+#: diff.c:5425
msgid "detect copies"
msgstr "засичане на копиранията"
-#: diff.c:5393
+#: diff.c:5429
msgid "use unmodified files as source to find copies"
msgstr "търсене на копирано и от непроменените файлове"
-#: diff.c:5395
+#: diff.c:5431
msgid "disable rename detection"
msgstr "без търсене на преименувания"
-#: diff.c:5398
+#: diff.c:5434
msgid "use empty blobs as rename source"
msgstr "празни обекти като източник при преименувания"
-#: diff.c:5400
+#: diff.c:5436
msgid "continue listing the history of a file beyond renames"
msgstr ""
"продължаване на извеждането на историята — без отрязването при преименувания "
"на файл"
-#: diff.c:5403
+#: diff.c:5439
msgid ""
"prevent rename/copy detection if the number of rename/copy targets exceeds "
"given limit"
msgstr ""
"без засичане на преименувания/копирания, ако броят им надвишава тази стойност"
-#: diff.c:5405
+#: diff.c:5441
msgid "Diff algorithm options"
msgstr "Опции към алгоритъма за разлики"
-#: diff.c:5407
+#: diff.c:5443
msgid "produce the smallest possible diff"
msgstr "търсене на възможно най-малка разлика"
-#: diff.c:5410
+#: diff.c:5446
msgid "ignore whitespace when comparing lines"
msgstr "без промени в празните знаци при сравняване на редове"
-#: diff.c:5413
+#: diff.c:5449
msgid "ignore changes in amount of whitespace"
msgstr "без промени в празните знаци"
-#: diff.c:5416
+#: diff.c:5452
msgid "ignore changes in whitespace at EOL"
msgstr "без промени в празните знаци в края на редовете"
-#: diff.c:5419
+#: diff.c:5455
msgid "ignore carrier-return at the end of line"
msgstr "без промени в знаците за край на ред"
-#: diff.c:5422
+#: diff.c:5458
msgid "ignore changes whose lines are all blank"
msgstr "без промени в редовете, които са изцяло от празни знаци"
-#: diff.c:5425
+#: diff.c:5461
msgid "heuristic to shift diff hunk boundaries for easy reading"
msgstr ""
"евристика за преместване на границите на парчетата за улесняване на четенето"
-#: diff.c:5428
+#: diff.c:5464
msgid "generate diff using the \"patience diff\" algorithm"
msgstr "разлика чрез алгоритъм за подредба като пасианс"
-#: diff.c:5432
+#: diff.c:5468
msgid "generate diff using the \"histogram diff\" algorithm"
msgstr "разлика по хистограмния алгоритъм"
-#: diff.c:5434
+#: diff.c:5470
msgid "<algorithm>"
msgstr "АЛГОРИТЪМ"
-#: diff.c:5435
+#: diff.c:5471
msgid "choose a diff algorithm"
msgstr "избор на АЛГОРИТЪМа за разлики"
-#: diff.c:5437
+#: diff.c:5473
msgid "<text>"
msgstr "ТЕКСТ"
-#: diff.c:5438
+#: diff.c:5474
msgid "generate diff using the \"anchored diff\" algorithm"
msgstr "разлика чрез алгоритъма със закотвяне"
-#: diff.c:5440 diff.c:5449 diff.c:5452
+#: diff.c:5476 diff.c:5485 diff.c:5488
msgid "<mode>"
msgstr "РЕЖИМ"
-#: diff.c:5441
+#: diff.c:5477
msgid "show word diff, using <mode> to delimit changed words"
msgstr ""
"разлика по думи, като се ползва този РЕЖИМ за отделянето на променените думи"
-#: diff.c:5443 diff.c:5446 diff.c:5491
+#: diff.c:5479 diff.c:5482 diff.c:5527
msgid "<regex>"
msgstr "РЕГУЛЯРЕН_ИЗРАЗ"
-#: diff.c:5444
+#: diff.c:5480
msgid "use <regex> to decide what a word is"
msgstr "РЕГУЛЯРЕН_ИЗРАЗ за разделяне по думи"
-#: diff.c:5447
+#: diff.c:5483
msgid "equivalent to --word-diff=color --word-diff-regex=<regex>"
msgstr "псевдоним на „--word-diff=color --word-diff-regex=РЕГУЛЯРЕН_ИЗРАЗ“"
-#: diff.c:5450
+#: diff.c:5486
msgid "moved lines of code are colored differently"
msgstr "различен цвят за извеждане на преместените редове"
-#: diff.c:5453
+#: diff.c:5489
msgid "how white spaces are ignored in --color-moved"
msgstr ""
"режим за прескачането на празните знаци при задаването на „--color-moved“"
-#: diff.c:5456
+#: diff.c:5492
msgid "Other diff options"
msgstr "Други опции за разлики"
-#: diff.c:5458
+#: diff.c:5494
msgid "when run from subdir, exclude changes outside and show relative paths"
msgstr ""
"при изпълнение от поддиректория да се пренебрегват разликите извън нея и да "
"се ползват относителни пътища"
-#: diff.c:5462
+#: diff.c:5498
msgid "treat all files as text"
msgstr "обработка на всички файлове като текстови"
-#: diff.c:5464
+#: diff.c:5500
msgid "swap two inputs, reverse the diff"
msgstr "размяна на двата входа — обръщане на разликата"
-#: diff.c:5466
+#: diff.c:5502
msgid "exit with 1 if there were differences, 0 otherwise"
msgstr ""
"завършване с код за състояние 1 при наличието на разлики, а в противен "
"случай — с 0"
-#: diff.c:5468
+#: diff.c:5504
msgid "disable all output of the program"
msgstr "без всякакъв изход от програмата"
-#: diff.c:5470
+#: diff.c:5506
msgid "allow an external diff helper to be executed"
msgstr "позволяване на изпълнение на външна помощна програма за разлики"
-#: diff.c:5472
+#: diff.c:5508
msgid "run external text conversion filters when comparing binary files"
msgstr ""
"изпълнение на външни програми-филтри при сравнението на двоични файлове"
-#: diff.c:5474
+#: diff.c:5510
msgid "<when>"
msgstr "КОГА"
-#: diff.c:5475
+#: diff.c:5511
msgid "ignore changes to submodules in the diff generation"
msgstr "игнориране на промените в подмодулите при извеждането на разликите"
-#: diff.c:5478
+#: diff.c:5514
msgid "<format>"
msgstr "ФОРМАТ"
-#: diff.c:5479
+#: diff.c:5515
msgid "specify how differences in submodules are shown"
msgstr "начин за извеждане на промените в подмодулите"
-#: diff.c:5483
+#: diff.c:5519
msgid "hide 'git add -N' entries from the index"
msgstr "без включване в индекса на записите, добавени с „git add -N“"
-#: diff.c:5486
+#: diff.c:5522
msgid "treat 'git add -N' entries as real in the index"
msgstr "включване в индекса на записите, добавени с „git add -N“"
-#: diff.c:5488
+#: diff.c:5524
msgid "<string>"
msgstr "НИЗ"
-#: diff.c:5489
+#: diff.c:5525
msgid ""
"look for differences that change the number of occurrences of the specified "
"string"
msgstr "търсене на разлики, които променят броя на поява на указаните низове"
-#: diff.c:5492
+#: diff.c:5528
msgid ""
"look for differences that change the number of occurrences of the specified "
"regex"
@@ -3848,62 +3894,71 @@ msgstr ""
"търсене на разлики, които променят броя на поява на низовете, които напасват "
"на регулярния израз"
-#: diff.c:5495
+#: diff.c:5531
msgid "show all changes in the changeset with -S or -G"
msgstr "извеждане на всички промени с „-G“/„-S“"
-#: diff.c:5498
+#: diff.c:5534
msgid "treat <string> in -S as extended POSIX regular expression"
msgstr "НИЗът към „-S“ да се тълкува като разширен регулярен израз по POSIX"
-#: diff.c:5501
+#: diff.c:5537
msgid "control the order in which files appear in the output"
msgstr "управление на подредбата на файловете в изхода"
-#: diff.c:5502
+#: diff.c:5538
msgid "<object-id>"
msgstr "ИДЕНТИФИКАТОР_НА_ОБЕКТ"
-#: diff.c:5503
+#: diff.c:5539
msgid ""
"look for differences that change the number of occurrences of the specified "
"object"
msgstr "търсене на разлики, които променят броя на поява на указания обект"
-#: diff.c:5505
+#: diff.c:5541
msgid "[(A|C|D|M|R|T|U|X|B)...[*]]"
msgstr "[(A|C|D|M|R|T|U|X|B)…[*]]"
-#: diff.c:5506
+#: diff.c:5542
msgid "select files by diff type"
msgstr "избор на файловете по вид разлика"
-#: diff.c:5508
+#: diff.c:5544
msgid "<file>"
msgstr "ФАЙЛ"
-#: diff.c:5509
+#: diff.c:5545
msgid "Output to a specific file"
msgstr "Изход към указания файл"
-#: diff.c:6164
+#: diff.c:6200
msgid "inexact rename detection was skipped due to too many files."
msgstr ""
"търсенето на преименувания на обекти съчетани с промени се прескача поради "
"многото файлове."
-#: diff.c:6167
+#: diff.c:6203
msgid "only found copies from modified paths due to too many files."
msgstr ""
"установени са само точните копия на променените пътища поради многото "
"файлове."
-#: diff.c:6170
+#: diff.c:6206
#, c-format
msgid ""
"you may want to set your %s variable to at least %d and retry the command."
msgstr "задайте променливата „%s“ да е поне %d и отново изпълнете командата."
+#: diffcore-order.c:24
+#, c-format
+msgid "failed to read orderfile '%s'"
+msgstr "файлът с подредбата на съответствията „%s“ не може да бъде прочетен"
+
+#: diffcore-rename.c:592
+msgid "Performing inexact rename detection"
+msgstr "Търсене на преименувания на обекти съчетани с промени"
+
#: dir.c:555
#, c-format
msgid "pathspec '%s' did not match any file(s) known to git"
@@ -3935,36 +3990,36 @@ msgstr "изключване на пътеводното напасване"
msgid "cannot use %s as an exclude file"
msgstr "„%s“ не може да се ползва за игнорираните файлове (като gitignore)"
-#: dir.c:2144
+#: dir.c:2275
#, c-format
msgid "could not open directory '%s'"
msgstr "директорията „%s“ не може да бъде отворена"
-#: dir.c:2479
+#: dir.c:2575
msgid "failed to get kernel name and information"
msgstr "името и версията на ядрото не бяха получени"
-#: dir.c:2603
+#: dir.c:2699
msgid "untracked cache is disabled on this system or location"
msgstr ""
"кешът за неследените файлове е изключен на тази система или местоположение"
-#: dir.c:3407
+#: dir.c:3481
#, c-format
msgid "index file corrupt in repo %s"
msgstr "файлът с индекса е повреден в хранилището „%s“"
-#: dir.c:3452 dir.c:3457
+#: dir.c:3526 dir.c:3531
#, c-format
msgid "could not create directories for %s"
msgstr "директориите за „%s“ не може да бъдат създадени"
-#: dir.c:3486
+#: dir.c:3560
#, c-format
msgid "could not migrate git directory from '%s' to '%s'"
msgstr "директорията на git не може да се мигрира от „%s“ до „%s“"
-#: editor.c:73
+#: editor.c:74
#, c-format
msgid "hint: Waiting for your editor to close the file...%c"
msgstr "Подсказка: чака се редакторът ви да затвори файла …%c"
@@ -3973,17 +4028,17 @@ msgstr "Подсказка: чака се редакторът ви да зат
msgid "Filtering content"
msgstr "Филтриране на съдържанието"
-#: entry.c:476
+#: entry.c:479
#, c-format
msgid "could not stat file '%s'"
msgstr "неуспешно изпълнение на „stat“ върху файла „%s“"
-#: environment.c:149
+#: environment.c:150
#, c-format
msgid "bad git namespace path \"%s\""
msgstr "неправилен път към пространства от имена „%s“"
-#: environment.c:331
+#: environment.c:337
#, c-format
msgid "could not set GIT_DIR to '%s'"
msgstr "GIT_DIR не може да се зададе да е „%s“"
@@ -3993,248 +4048,248 @@ msgstr "GIT_DIR не може да се зададе да е „%s“"
msgid "too many args to run %s"
msgstr "прекалено много аргументи за изпълнение „%s“"
-#: fetch-pack.c:150
+#: fetch-pack.c:151
msgid "git fetch-pack: expected shallow list"
msgstr "git fetch-pack: очаква се плитък списък"
-#: fetch-pack.c:153
+#: fetch-pack.c:154
msgid "git fetch-pack: expected a flush packet after shallow list"
msgstr "git fetch-pack: след плитък списък се очаква изчистващ пакет „flush“"
-#: fetch-pack.c:164
+#: fetch-pack.c:165
msgid "git fetch-pack: expected ACK/NAK, got a flush packet"
msgstr ""
"git fetch-pack: очаква се „ACK“/„NAK“, а бе получен изчистващ пакет „flush“"
-#: fetch-pack.c:184
+#: fetch-pack.c:185
#, c-format
msgid "git fetch-pack: expected ACK/NAK, got '%s'"
msgstr "git fetch-pack: очаква се „ACK“/„NAK“, а бе получено „%s“"
-#: fetch-pack.c:195
+#: fetch-pack.c:196
msgid "unable to write to remote"
msgstr "невъзможно писане към отдалечено хранилище"
-#: fetch-pack.c:257
+#: fetch-pack.c:258
msgid "--stateless-rpc requires multi_ack_detailed"
msgstr "опцията „--stateless-rpc“ изисква „multi_ack_detailed“"
-#: fetch-pack.c:356 fetch-pack.c:1340
+#: fetch-pack.c:357 fetch-pack.c:1364
#, c-format
msgid "invalid shallow line: %s"
msgstr "неправилен плитък ред: „%s“"
-#: fetch-pack.c:362 fetch-pack.c:1346
+#: fetch-pack.c:363 fetch-pack.c:1370
#, c-format
msgid "invalid unshallow line: %s"
msgstr "неправилен неплитък ред: „%s“"
-#: fetch-pack.c:364 fetch-pack.c:1348
+#: fetch-pack.c:365 fetch-pack.c:1372
#, c-format
msgid "object not found: %s"
msgstr "обектът „%s“ липсва"
-#: fetch-pack.c:367 fetch-pack.c:1351
+#: fetch-pack.c:368 fetch-pack.c:1375
#, c-format
msgid "error in object: %s"
msgstr "грешка в обекта: „%s“"
-#: fetch-pack.c:369 fetch-pack.c:1353
+#: fetch-pack.c:370 fetch-pack.c:1377
#, c-format
msgid "no shallow found: %s"
msgstr "не е открит плитък обект: %s"
-#: fetch-pack.c:372 fetch-pack.c:1357
+#: fetch-pack.c:373 fetch-pack.c:1381
#, c-format
msgid "expected shallow/unshallow, got %s"
msgstr "очаква се плитък или не обект, а бе получено: „%s“"
-#: fetch-pack.c:414
+#: fetch-pack.c:415
#, c-format
msgid "got %s %d %s"
msgstr "получено бе %s %d %s"
-#: fetch-pack.c:431
+#: fetch-pack.c:432
#, c-format
msgid "invalid commit %s"
msgstr "неправилно подаване: „%s“"
-#: fetch-pack.c:462
+#: fetch-pack.c:463
msgid "giving up"
msgstr "преустановяване"
-#: fetch-pack.c:475 progress.c:323
+#: fetch-pack.c:476 progress.c:336
msgid "done"
msgstr "действието завърши"
-#: fetch-pack.c:487
+#: fetch-pack.c:488
#, c-format
msgid "got %s (%d) %s"
msgstr "получено бе %s (%d) %s"
-#: fetch-pack.c:533
+#: fetch-pack.c:534
#, c-format
msgid "Marking %s as complete"
msgstr "Отбелязване на „%s“ като пълно"
-#: fetch-pack.c:754
+#: fetch-pack.c:755
#, c-format
msgid "already have %s (%s)"
msgstr "вече има „%s“ (%s)"
-#: fetch-pack.c:818
+#: fetch-pack.c:819
msgid "fetch-pack: unable to fork off sideband demultiplexer"
msgstr "fetch-pack: не може да се създаде процес за демултиплексора"
-#: fetch-pack.c:826
+#: fetch-pack.c:827
msgid "protocol error: bad pack header"
msgstr "протоколна грешка: неправилна заглавна част на пакет"
-#: fetch-pack.c:900
+#: fetch-pack.c:901
#, c-format
msgid "fetch-pack: unable to fork off %s"
msgstr "fetch-pack: не може да се създаде процес за „%s“"
-#: fetch-pack.c:916
+#: fetch-pack.c:917
#, c-format
msgid "%s failed"
msgstr "неуспешно изпълнение на „%s“"
-#: fetch-pack.c:918
+#: fetch-pack.c:919
msgid "error in sideband demultiplexer"
msgstr "грешка в демултиплексора"
-#: fetch-pack.c:965
+#: fetch-pack.c:966
#, c-format
msgid "Server version is %.*s"
msgstr "Версията на сървъра е: %.*s"
-#: fetch-pack.c:970 fetch-pack.c:976 fetch-pack.c:979 fetch-pack.c:985
-#: fetch-pack.c:989 fetch-pack.c:993 fetch-pack.c:997 fetch-pack.c:1001
-#: fetch-pack.c:1005 fetch-pack.c:1009 fetch-pack.c:1013 fetch-pack.c:1017
-#: fetch-pack.c:1023 fetch-pack.c:1029 fetch-pack.c:1034 fetch-pack.c:1039
+#: fetch-pack.c:971 fetch-pack.c:977 fetch-pack.c:980 fetch-pack.c:986
+#: fetch-pack.c:990 fetch-pack.c:994 fetch-pack.c:998 fetch-pack.c:1002
+#: fetch-pack.c:1006 fetch-pack.c:1010 fetch-pack.c:1014 fetch-pack.c:1018
+#: fetch-pack.c:1024 fetch-pack.c:1030 fetch-pack.c:1035 fetch-pack.c:1040
#, c-format
msgid "Server supports %s"
msgstr "Сървърът поддържа „%s“"
-#: fetch-pack.c:972
+#: fetch-pack.c:973
msgid "Server does not support shallow clients"
msgstr "Сървърът не поддържа плитки клиенти"
-#: fetch-pack.c:1032
+#: fetch-pack.c:1033
msgid "Server does not support --shallow-since"
msgstr "Сървърът не поддържа опцията „--shallow-since“"
-#: fetch-pack.c:1037
+#: fetch-pack.c:1038
msgid "Server does not support --shallow-exclude"
msgstr "Сървърът не поддържа опцията „--shallow-exclude“"
-#: fetch-pack.c:1041
+#: fetch-pack.c:1042
msgid "Server does not support --deepen"
msgstr "Сървърът не поддържа опцията „--deepen“"
-#: fetch-pack.c:1058
+#: fetch-pack.c:1059
msgid "no common commits"
msgstr "няма общи подавания"
-#: fetch-pack.c:1070 fetch-pack.c:1536
+#: fetch-pack.c:1071 fetch-pack.c:1563
msgid "git fetch-pack: fetch failed."
msgstr "git fetch-pack: неуспешно доставяне."
-#: fetch-pack.c:1209
+#: fetch-pack.c:1211
msgid "Server does not support shallow requests"
msgstr "Сървърът не поддържа плитки заявки"
-#: fetch-pack.c:1216
+#: fetch-pack.c:1218
msgid "Server supports filter"
msgstr "Сървърът поддържа филтри"
-#: fetch-pack.c:1239
+#: fetch-pack.c:1242
msgid "unable to write request to remote"
msgstr "невъзможно писане към отдалечено хранилище"
-#: fetch-pack.c:1257
+#: fetch-pack.c:1260
#, c-format
msgid "error reading section header '%s'"
msgstr "грешка при прочитане на заглавната част на раздел „%s“"
-#: fetch-pack.c:1263
+#: fetch-pack.c:1266
#, c-format
msgid "expected '%s', received '%s'"
msgstr "очаква се „%s“, а бе получено „%s“"
-#: fetch-pack.c:1303
+#: fetch-pack.c:1327
#, c-format
msgid "unexpected acknowledgment line: '%s'"
msgstr "неочакван ред за потвърждение: „%s“"
-#: fetch-pack.c:1308
+#: fetch-pack.c:1332
#, c-format
msgid "error processing acks: %d"
msgstr "грешка при обработка на потвържденията: %d"
-#: fetch-pack.c:1318
+#: fetch-pack.c:1342
msgid "expected packfile to be sent after 'ready'"
msgstr ""
"очакваше се пакетният файл да бъде изпратен след отговор за готовност (ready)"
-#: fetch-pack.c:1320
+#: fetch-pack.c:1344
msgid "expected no other sections to be sent after no 'ready'"
msgstr ""
"очакваше се след липса на отговор за готовност (ready) да не се се пращат "
"други раздели"
-#: fetch-pack.c:1362
+#: fetch-pack.c:1386
#, c-format
msgid "error processing shallow info: %d"
msgstr "грешка при обработка на информация за дълбочината/плиткостта: %d"
-#: fetch-pack.c:1409
+#: fetch-pack.c:1433
#, c-format
msgid "expected wanted-ref, got '%s'"
msgstr "очаква се искан указател, а бе получено: „%s“"
-#: fetch-pack.c:1414
+#: fetch-pack.c:1438
#, c-format
msgid "unexpected wanted-ref: '%s'"
msgstr "неочакван искан указател: „%s“"
-#: fetch-pack.c:1419
+#: fetch-pack.c:1443
#, c-format
msgid "error processing wanted refs: %d"
msgstr "грешка при обработката на исканите указатели: %d"
-#: fetch-pack.c:1762
+#: fetch-pack.c:1789
msgid "no matching remote head"
msgstr "не може да бъде открит подходящ връх от отдалеченото хранилище"
-#: fetch-pack.c:1785 builtin/clone.c:689
+#: fetch-pack.c:1812 builtin/clone.c:692
msgid "remote did not send all necessary objects"
msgstr "отдалеченото хранилище не изпрати всички необходими обекти."
-#: fetch-pack.c:1812
+#: fetch-pack.c:1839
#, c-format
msgid "no such remote ref %s"
msgstr "такъв отдалечен указател няма: %s"
-#: fetch-pack.c:1815
+#: fetch-pack.c:1842
#, c-format
msgid "Server does not allow request for unadvertised object %s"
msgstr "Сървърът не позволява заявка за необявен обект „%s“"
-#: gpg-interface.c:408
-msgid "gpg failed to sign the data"
-msgstr "Програмата „gpg“ не подписа данните."
-
-#: gpg-interface.c:434
+#: gpg-interface.c:272
msgid "could not create temporary file"
msgstr "не може да се създаде временен файл"
-#: gpg-interface.c:437
+#: gpg-interface.c:275
#, c-format
msgid "failed writing detached signature to '%s'"
msgstr "Програмата не успя да запише самостоятелния подпис в „%s“"
+#: gpg-interface.c:457
+msgid "gpg failed to sign the data"
+msgstr "Програмата „gpg“ не подписа данните."
+
#: graph.c:98
#, c-format
msgid "ignore invalid color '%.*s' in log.graphColors"
@@ -4253,7 +4308,7 @@ msgstr ""
msgid "'%s': unable to read %s"
msgstr "„%s“: файлът сочен от „%s“ не може да бъде прочетен"
-#: grep.c:2145 setup.c:166 builtin/clone.c:411 builtin/diff.c:82
+#: grep.c:2145 setup.c:176 builtin/clone.c:411 builtin/diff.c:82
#: builtin/rm.c:135
#, c-format
msgid "failed to stat '%s'"
@@ -4329,7 +4384,7 @@ msgstr "команди на git от други директории от „$PA
msgid "These are common Git commands used in various situations:"
msgstr "Това са най-често използваните команди на Git:"
-#: help.c:363 git.c:98
+#: help.c:363 git.c:99
#, c-format
msgid "unsupported command listing type '%s'"
msgstr "неподдържан списък от команди „%s“"
@@ -4338,19 +4393,19 @@ msgstr "неподдържан списък от команди „%s“"
msgid "The common Git guides are:"
msgstr "Популярните въведения в Git са:"
-#: help.c:512
+#: help.c:427
msgid "See 'git help <command>' to read about a specific subcommand"
msgstr "За повече информация за КОМАНДА изпълнете „git help КОМАНДА“"
-#: help.c:517
+#: help.c:432
msgid "External commands"
msgstr "Външни команди"
-#: help.c:532
+#: help.c:447
msgid "Command aliases"
msgstr "Псевдоними на командите"
-#: help.c:596
+#: help.c:511
#, c-format
msgid ""
"'%s' appears to be a git command, but we were not\n"
@@ -4359,36 +4414,36 @@ msgstr ""
"Изглежда, че „%s“ е команда на git, но тя не може да\n"
"бъде изпълнена. Вероятно пакетът „git-%s“ е повреден."
-#: help.c:655
+#: help.c:570
msgid "Uh oh. Your system reports no Git commands at all."
msgstr "Странно, изглежда, че на системата ви няма нито една команда на git."
-#: help.c:677
+#: help.c:592
#, c-format
msgid "WARNING: You called a Git command named '%s', which does not exist."
msgstr ""
"ПРЕДУПРЕЖДЕНИЕ: Пробвахте да изпълните команда на Git на име „%s“, а такава "
"не съществува."
-#: help.c:682
+#: help.c:597
#, c-format
msgid "Continuing under the assumption that you meant '%s'."
msgstr ""
"Изпълнението автоматично продължава, като се счита, че имате предвид „%s“."
-#: help.c:687
+#: help.c:602
#, c-format
msgid "Continuing in %0.1f seconds, assuming that you meant '%s'."
msgstr ""
"Изпълнението автоматично ще продължи след %0.1f сек., като се счита, че "
"имате предвид „%s“."
-#: help.c:695
+#: help.c:610
#, c-format
msgid "git: '%s' is not a git command. See 'git --help'."
msgstr "git: „%s“ не е команда на git. Погледнете изхода от „git --help“."
-#: help.c:699
+#: help.c:614
msgid ""
"\n"
"The most similar command is"
@@ -4402,16 +4457,16 @@ msgstr[1] ""
"\n"
"Най-близките команди са"
-#: help.c:714
+#: help.c:653
msgid "git version [<options>]"
msgstr "git version [ОПЦИЯ…]"
-#: help.c:783
+#: help.c:708
#, c-format
msgid "%s: %s - %s"
msgstr "%s: %s — %s"
-#: help.c:787
+#: help.c:712
msgid ""
"\n"
"Did you mean this?"
@@ -4482,37 +4537,11 @@ msgstr "не може да се ползва празно име като иде
msgid "name consists only of disallowed characters: %s"
msgstr "името съдържа само непозволени знаци: „%s“"
-#: ident.c:436 builtin/commit.c:635
+#: ident.c:436 builtin/commit.c:634
#, c-format
msgid "invalid date format: %s"
msgstr "неправилен формат на дата: %s"
-#: list-objects.c:127
-#, c-format
-msgid "entry '%s' in tree %s has tree mode, but is not a tree"
-msgstr "обект „%s“ в дървото „%s“ е отбелязан като дърво, но не е"
-
-#: list-objects.c:140
-#, c-format
-msgid "entry '%s' in tree %s has blob mode, but is not a blob"
-msgstr "обект „%s“ в дървото „%s“ е отбелязан като BLOB, но не е"
-
-#: list-objects.c:375
-#, c-format
-msgid "unable to load root tree for commit %s"
-msgstr "кореновото дърво за подаване „%s“ не може да се зареди"
-
-#: list-objects-filter.c:492
-#, c-format
-msgid "unable to access sparse blob in '%s'"
-msgstr "няма достъп до частично изтегления обект-BLOB в „%s“"
-
-#: list-objects-filter.c:495
-#, c-format
-msgid "unable to parse sparse filter data in %s"
-msgstr ""
-"данните от филтъра за частични изтегляния в „%s“ не може да бъдат анализирани"
-
#: list-objects-filter-options.c:58
msgid "expected 'tree:<depth>'"
msgstr "очаква се „tree:ДЪЛБОЧИНА“"
@@ -4539,7 +4568,33 @@ msgstr "добавете нещо след комбинирането:"
msgid "multiple filter-specs cannot be combined"
msgstr "не може да комбинирате множество филтри"
-#: lockfile.c:151
+#: list-objects-filter.c:492
+#, c-format
+msgid "unable to access sparse blob in '%s'"
+msgstr "няма достъп до частично изтегления обект-BLOB в „%s“"
+
+#: list-objects-filter.c:495
+#, c-format
+msgid "unable to parse sparse filter data in %s"
+msgstr ""
+"данните от филтъра за частични изтегляния в „%s“ не може да бъдат анализирани"
+
+#: list-objects.c:127
+#, c-format
+msgid "entry '%s' in tree %s has tree mode, but is not a tree"
+msgstr "обект „%s“ в дървото „%s“ е отбелязан като дърво, но не е"
+
+#: list-objects.c:140
+#, c-format
+msgid "entry '%s' in tree %s has blob mode, but is not a blob"
+msgstr "обект „%s“ в дървото „%s“ е отбелязан като BLOB, но не е"
+
+#: list-objects.c:375
+#, c-format
+msgid "unable to load root tree for commit %s"
+msgstr "кореновото дърво за подаване „%s“ не може да се зареди"
+
+#: lockfile.c:152
#, c-format
msgid ""
"Unable to create '%s.lock': %s.\n"
@@ -4558,20 +4613,14 @@ msgstr ""
"причина е, че някой процес на git в това хранилище е забил. За да\n"
"продължите работа, ще трябва ръчно да изтриете файла:"
-#: lockfile.c:159
+#: lockfile.c:160
#, c-format
msgid "Unable to create '%s.lock': %s"
msgstr "Файлът-ключалка „%s.lock“ не може да бъде създаден: %s"
-#: merge.c:41
-msgid "failed to read the cache"
-msgstr "кешът не може да бъде прочетен"
-
-#: merge.c:107 rerere.c:720 builtin/am.c:1879 builtin/am.c:1913
-#: builtin/checkout.c:541 builtin/checkout.c:800 builtin/clone.c:810
-#: builtin/stash.c:265
-msgid "unable to write new index file"
-msgstr "неуспешно записване на новия индекс"
+#: ls-refs.c:109
+msgid "expected flush after ls-refs arguments"
+msgstr "след аргументите към „ls-refs“ се очаква изчистване на буферите"
#: merge-recursive.c:356
msgid "(bad commit)\n"
@@ -4766,7 +4815,7 @@ msgstr "преименуване"
msgid "renamed"
msgstr "преименуван"
-#: merge-recursive.c:1577 merge-recursive.c:2472 merge-recursive.c:3117
+#: merge-recursive.c:1577 merge-recursive.c:2484 merge-recursive.c:3129
#, c-format
msgid "Refusing to lose dirty file at %s"
msgstr "Преустановяване на действието, за да не се изгуби промененият „%s“"
@@ -4809,14 +4858,14 @@ msgstr ""
msgid " (left unresolved)"
msgstr " (некоригиран конфликт)"
-#: merge-recursive.c:1793
+#: merge-recursive.c:1805
#, c-format
msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s"
msgstr ""
"КОНФЛИКТ (преименуване/преименуване): „%s“ е преименуван на „%s“ в клон "
"„%s“, а „%s“ е преименуван на „%s“ в „%s“"
-#: merge-recursive.c:2056
+#: merge-recursive.c:2068
#, c-format
msgid ""
"CONFLICT (directory rename split): Unclear where to place %s because "
@@ -4827,7 +4876,7 @@ msgstr ""
"постави „%s“, защото няколко нови директории поделят съдържанието на "
"директория „%s“, като никоя не съдържа мнозинство от файловете ѝ."
-#: merge-recursive.c:2088
+#: merge-recursive.c:2100
#, c-format
msgid ""
"CONFLICT (implicit dir rename): Existing file/dir at %s in the way of "
@@ -4836,7 +4885,7 @@ msgstr ""
"КОНФЛИКТ (косвено преименуване на директория): следният файл или директория "
"„%s“ не позволяват косвеното преименуване на следния път/ища: %s."
-#: merge-recursive.c:2098
+#: merge-recursive.c:2110
#, c-format
msgid ""
"CONFLICT (implicit dir rename): Cannot map more than one path to %s; "
@@ -4846,7 +4895,7 @@ msgstr ""
"съответства на „%s“. Косвено преименуване на директория води до поставянето "
"на тези пътища там: %s."
-#: merge-recursive.c:2190
+#: merge-recursive.c:2202
#, c-format
msgid ""
"CONFLICT (rename/rename): Rename directory %s->%s in %s. Rename directory %s-"
@@ -4855,7 +4904,7 @@ msgstr ""
"КОНФЛИКТ (преименуване/преименуване): „%s“ е преименуван на „%s“ в клон "
"„%s“, а „%s“ е преименуван на „%s“ в „%s“"
-#: merge-recursive.c:2435
+#: merge-recursive.c:2447
#, c-format
msgid ""
"WARNING: Avoiding applying %s -> %s rename to %s, because %s itself was "
@@ -4864,52 +4913,52 @@ msgstr ""
"ПРЕДУПРЕЖДЕНИЕ: прескачане на преименуването на „%s“ на „%s“ в „%s“, защото "
"„%s“ също е с променено име."
-#: merge-recursive.c:2961
+#: merge-recursive.c:2973
#, c-format
msgid "cannot read object %s"
msgstr "обектът „%s“ не може да се прочете"
-#: merge-recursive.c:2964
+#: merge-recursive.c:2976
#, c-format
msgid "object %s is not a blob"
msgstr "обектът „%s“ не е BLOB"
-#: merge-recursive.c:3028
+#: merge-recursive.c:3040
msgid "modify"
msgstr "промяна"
-#: merge-recursive.c:3028
+#: merge-recursive.c:3040
msgid "modified"
msgstr "променен"
-#: merge-recursive.c:3040
+#: merge-recursive.c:3052
msgid "content"
msgstr "съдържание"
-#: merge-recursive.c:3044
+#: merge-recursive.c:3056
msgid "add/add"
msgstr "добавяне/добавяне"
-#: merge-recursive.c:3067
+#: merge-recursive.c:3079
#, c-format
msgid "Skipped %s (merged same as existing)"
msgstr "Прескачане на „%s“ (слетият резултат е идентичен със сегашния)"
-#: merge-recursive.c:3089 git-submodule.sh:1003
+#: merge-recursive.c:3101 git-submodule.sh:985
msgid "submodule"
msgstr "ПОДМОДУЛ"
-#: merge-recursive.c:3090
+#: merge-recursive.c:3102
#, c-format
msgid "CONFLICT (%s): Merge conflict in %s"
msgstr "КОНФЛИКТ (%s): Конфликт при сливане на „%s“"
-#: merge-recursive.c:3120
+#: merge-recursive.c:3132
#, c-format
msgid "Adding as %s instead"
msgstr "Добавяне като „%s“"
-#: merge-recursive.c:3203
+#: merge-recursive.c:3215
#, c-format
msgid ""
"Path updated: %s added in %s inside a directory that was renamed in %s; "
@@ -4918,7 +4967,7 @@ msgstr ""
"Обновен път: „%s“ е добавен в „%s“ в директория, която е преименувана в "
"„%s“. Обектът се мести в „%s“."
-#: merge-recursive.c:3206
+#: merge-recursive.c:3218
#, c-format
msgid ""
"CONFLICT (file location): %s added in %s inside a directory that was renamed "
@@ -4927,7 +4976,7 @@ msgstr ""
"КОНФЛИКТ (места на файлове): „%s“ е добавен в „%s“ в директория, която е "
"преименувана в „%s“. Предложението е да преместите обекта в „%s“."
-#: merge-recursive.c:3210
+#: merge-recursive.c:3222
#, c-format
msgid ""
"Path updated: %s renamed to %s in %s, inside a directory that was renamed in "
@@ -4936,7 +4985,7 @@ msgstr ""
"Обновен път: „%s“ е преименуван на „%s“ в „%s“ в директория, която е "
"преименувана в „%s“. Обектът се мести в „%s“."
-#: merge-recursive.c:3213
+#: merge-recursive.c:3225
#, c-format
msgid ""
"CONFLICT (file location): %s renamed to %s in %s, inside a directory that "
@@ -4945,61 +4994,61 @@ msgstr ""
"КОНФЛИКТ (места на файлове): „%s“ е преименуван на „%s“ в „%s“ в директория, "
"която е преименувана в „%s“. Предложението е да преместите обекта в „%s“."
-#: merge-recursive.c:3327
+#: merge-recursive.c:3339
#, c-format
msgid "Removing %s"
msgstr "Изтриване на „%s“"
-#: merge-recursive.c:3350
+#: merge-recursive.c:3362
msgid "file/directory"
msgstr "файл/директория"
-#: merge-recursive.c:3355
+#: merge-recursive.c:3367
msgid "directory/file"
msgstr "директория/файл"
-#: merge-recursive.c:3362
+#: merge-recursive.c:3374
#, c-format
msgid "CONFLICT (%s): There is a directory with name %s in %s. Adding %s as %s"
msgstr ""
"КОНФЛИКТ (%s): Съществува директория на име „%s“ в „%s“. Добавяне на „%s“ "
"като „%s“"
-#: merge-recursive.c:3371
+#: merge-recursive.c:3383
#, c-format
msgid "Adding %s"
msgstr "Добавяне на „%s“"
-#: merge-recursive.c:3380
+#: merge-recursive.c:3392
#, c-format
msgid "CONFLICT (add/add): Merge conflict in %s"
msgstr "КОНФЛИКТ (добавяне/добавяне): Конфликт при сливане на „%s“"
-#: merge-recursive.c:3424
+#: merge-recursive.c:3436
msgid "Already up to date!"
msgstr "Вече е обновено!"
-#: merge-recursive.c:3433
+#: merge-recursive.c:3445
#, c-format
msgid "merging of trees %s and %s failed"
msgstr "неуспешно сливане на дърветата „%s“ и „%s“"
-#: merge-recursive.c:3537
+#: merge-recursive.c:3549
msgid "Merging:"
msgstr "Сливане:"
-#: merge-recursive.c:3550
+#: merge-recursive.c:3562
#, c-format
msgid "found %u common ancestor:"
msgid_plural "found %u common ancestors:"
msgstr[0] "открит е %u общ предшественик:"
msgstr[1] "открити са %u общи предшественици:"
-#: merge-recursive.c:3600
+#: merge-recursive.c:3612
msgid "merge returned no commit"
msgstr "сливането не върна подаване"
-#: merge-recursive.c:3659
+#: merge-recursive.c:3671
#, c-format
msgid ""
"Your local changes to the following files would be overwritten by merge:\n"
@@ -5008,15 +5057,25 @@ msgstr ""
"Сливането ще презапише локалните промени на тези файлове:\n"
" %s"
-#: merge-recursive.c:3756
+#: merge-recursive.c:3768
#, c-format
msgid "Could not parse object '%s'"
msgstr "Неуспешен анализ на обекта „%s“"
-#: merge-recursive.c:3774 builtin/merge.c:697 builtin/merge.c:877
+#: merge-recursive.c:3786 builtin/merge.c:705 builtin/merge.c:885
msgid "Unable to write index."
msgstr "Индексът не може да бъде прочетен"
+#: merge.c:41
+msgid "failed to read the cache"
+msgstr "кешът не може да бъде прочетен"
+
+#: merge.c:108 rerere.c:720 builtin/am.c:1878 builtin/am.c:1912
+#: builtin/checkout.c:559 builtin/checkout.c:824 builtin/clone.c:816
+#: builtin/stash.c:265
+msgid "unable to write new index file"
+msgstr "неуспешно записване на новия индекс"
+
#: midx.c:68
#, c-format
msgid "multi-pack-index file %s is too small"
@@ -5070,62 +5129,66 @@ msgstr ""
"неправилна подредба на имената в индекс за множество пакети: „%s“ се появи "
"преди „%s“"
-#: midx.c:210
+#: midx.c:208
#, c-format
msgid "bad pack-int-id: %u (%u total packs)"
msgstr ""
"неправилен идентификатор на пакет (pack-int-id): %u (от общо %u пакети)"
-#: midx.c:260
+#: midx.c:258
msgid "multi-pack-index stores a 64-bit offset, but off_t is too small"
msgstr ""
"индексът за множество пакети съдържа 64-битови отмествания, но размерът на "
"„off_t“ е недостатъчен"
-#: midx.c:288
+#: midx.c:286
msgid "error preparing packfile from multi-pack-index"
msgstr ""
"грешка при създаването на пакетен файл на базата на индекс за множество "
"пакети"
-#: midx.c:472
+#: midx.c:470
#, c-format
msgid "failed to add packfile '%s'"
msgstr "пакетният файл „%s“ не може да бъде добавен"
-#: midx.c:478
+#: midx.c:476
#, c-format
msgid "failed to open pack-index '%s'"
msgstr "индексът за пакети „%s“ не може да бъде отворен"
-#: midx.c:538
+#: midx.c:536
#, c-format
msgid "failed to locate object %d in packfile"
msgstr "обект %d в пакетния файл липсва"
-#: midx.c:842
+#: midx.c:840
msgid "Adding packfiles to multi-pack-index"
msgstr "Добавяне на пакетни файлове към индекс за множество пакети"
-#: midx.c:875
+#: midx.c:873
#, c-format
msgid "did not see pack-file %s to drop"
msgstr "пакетният файл за триене „%s“ не може да се открие"
-#: midx.c:973
+#: midx.c:925
+msgid "no pack files to index."
+msgstr "няма пакетни файлове за индексиране"
+
+#: midx.c:977
msgid "Writing chunks to multi-pack-index"
msgstr "Запис на откъси към индекс за множество пакети"
-#: midx.c:1052
+#: midx.c:1056
#, c-format
msgid "failed to clear multi-pack-index at %s"
msgstr "индексът за множество пакети не може да бъде изчистен при „%s“"
-#: midx.c:1108
+#: midx.c:1112
msgid "Looking for referenced packfiles"
msgstr "Търсене на указаните пакетни файлове"
-#: midx.c:1123
+#: midx.c:1127
#, c-format
msgid ""
"oid fanout out of order: fanout[%d] = %<PRIx32> > %<PRIx32> = fanout[%d]"
@@ -5133,54 +5196,58 @@ msgstr ""
"неправилна подредба на откъси (OID fanout): fanout[%d] = %<PRIx32> > "
"%<PRIx32> = fanout[%d]"
-#: midx.c:1128
+#: midx.c:1132
+msgid "the midx contains no oid"
+msgstr "във файла с индекса за множество пакети няма идентификатори на обекти"
+
+#: midx.c:1141
msgid "Verifying OID order in multi-pack-index"
msgstr ""
"Проверка на подредбата на идентификатори на обекти във файл с индекс към "
"множество пакетни файлове"
-#: midx.c:1137
+#: midx.c:1150
#, c-format
msgid "oid lookup out of order: oid[%d] = %s >= %s = oid[%d]"
msgstr ""
"неправилна подредба на откъси (OID lookup): oid[%d] = %s >= %s = oid[%d]"
-#: midx.c:1157
+#: midx.c:1170
msgid "Sorting objects by packfile"
msgstr "Подредба на обектите по пакетни файлове"
-#: midx.c:1164
+#: midx.c:1177
msgid "Verifying object offsets"
msgstr "Проверка на отместването на обекти"
-#: midx.c:1180
+#: midx.c:1193
#, c-format
msgid "failed to load pack entry for oid[%d] = %s"
msgstr "записът в пакета за обекта oid[%d] = %s не може да бъде зареден"
-#: midx.c:1186
+#: midx.c:1199
#, c-format
msgid "failed to load pack-index for packfile %s"
msgstr "индексът на пакета „%s“ не може да бъде зареден"
-#: midx.c:1195
+#: midx.c:1208
#, c-format
msgid "incorrect object offset for oid[%d] = %s: %<PRIx64> != %<PRIx64>"
msgstr "неправилно отместване на обект за oid[%d] = %s: %<PRIx64> != %<PRIx64>"
-#: midx.c:1220
+#: midx.c:1233
msgid "Counting referenced objects"
msgstr "Преброяване на свързаните обекти"
-#: midx.c:1230
+#: midx.c:1243
msgid "Finding and deleting unreferenced packfiles"
msgstr "Търсене и изтриване на несвързаните пакетни файлове"
-#: midx.c:1388
+#: midx.c:1433
msgid "could not start pack-objects"
msgstr "командата „pack-objects“ не може да бъде стартирана"
-#: midx.c:1407
+#: midx.c:1452
msgid "could not finish pack-objects"
msgstr "командата „pack-objects“ не може да бъде завършена"
@@ -5272,6 +5339,11 @@ msgstr "обектът „%s“ не може да бъде анализиран
msgid "hash mismatch %s"
msgstr "разлика в контролната сума: „%s“"
+#: pack-bitmap.c:815 pack-bitmap.c:821 builtin/pack-objects.c:2135
+#, c-format
+msgid "unable to get size of %s"
+msgstr "размерът на „%s“ не може да бъде получен"
+
#: packfile.c:629
msgid "offset before end of packfile (broken .idx?)"
msgstr ""
@@ -5291,10 +5363,27 @@ msgstr ""
"отместване преди края на индекса на пакетния файл „%s“ (възможно е индексът "
"да е отрязан)"
-#: pack-bitmap.c:800 pack-bitmap.c:806 builtin/pack-objects.c:2134
+#: parse-options-cb.c:20 parse-options-cb.c:24
#, c-format
-msgid "unable to get size of %s"
-msgstr "размерът на „%s“ не може да бъде получен"
+msgid "option `%s' expects a numerical value"
+msgstr "опцията „%s“ очаква число за аргумент"
+
+#: parse-options-cb.c:41
+#, c-format
+msgid "malformed expiration date '%s'"
+msgstr "неправилна дата на срок: „%s“"
+
+#: parse-options-cb.c:54
+#, c-format
+msgid "option `%s' expects \"always\", \"auto\", or \"never\""
+msgstr ""
+"опцията „%s“ изисква някоя от стойностите: „always“ (винаги), "
+"„auto“ (автоматично) или „never“ (никога)"
+
+#: parse-options-cb.c:130 parse-options-cb.c:147
+#, c-format
+msgid "malformed object name '%s'"
+msgstr "неправилно име на обект „%s“"
#: parse-options.c:38
#, c-format
@@ -5337,26 +5426,31 @@ msgstr "нееднозначна опция: „%s“ (може да е „--%s%
msgid "did you mean `--%s` (with two dashes)?"
msgstr "„--%s“ (с 2 тирета) ли имахте предвид?"
-#: parse-options.c:857
+#: parse-options.c:663 parse-options.c:963
+#, c-format
+msgid "alias of --%s"
+msgstr "псевдоним на „--%s“"
+
+#: parse-options.c:854
#, c-format
msgid "unknown option `%s'"
msgstr "непозната опция: „%s“"
-#: parse-options.c:859
+#: parse-options.c:856
#, c-format
msgid "unknown switch `%c'"
msgstr "непознат флаг „%c“"
-#: parse-options.c:861
+#: parse-options.c:858
#, c-format
msgid "unknown non-ascii option in string: `%s'"
msgstr "непозната стойност извън „ascii“ в низа: „%s“"
-#: parse-options.c:885
+#: parse-options.c:882
msgid "..."
msgstr "…"
-#: parse-options.c:904
+#: parse-options.c:901
#, c-format
msgid "usage: %s"
msgstr "употреба: %s"
@@ -5364,47 +5458,20 @@ msgstr "употреба: %s"
#. TRANSLATORS: the colon here should align with the
#. one in "usage: %s" translation.
#.
-#: parse-options.c:910
+#: parse-options.c:907
#, c-format
msgid " or: %s"
msgstr " или: %s"
-#: parse-options.c:913
+#: parse-options.c:910
#, c-format
msgid " %s"
msgstr " %s"
-#: parse-options.c:952
+#: parse-options.c:949
msgid "-NUM"
msgstr "-ЧИСЛО"
-#: parse-options.c:966
-#, c-format
-msgid "alias of --%s"
-msgstr "псевдоним на „--%s“"
-
-#: parse-options-cb.c:20 parse-options-cb.c:24
-#, c-format
-msgid "option `%s' expects a numerical value"
-msgstr "опцията „%s“ очаква число за аргумент"
-
-#: parse-options-cb.c:41
-#, c-format
-msgid "malformed expiration date '%s'"
-msgstr "неправилна дата на срок: „%s“"
-
-#: parse-options-cb.c:54
-#, c-format
-msgid "option `%s' expects \"always\", \"auto\", or \"never\""
-msgstr ""
-"опцията „%s“ изисква някоя от стойностите: „always“ (винаги), "
-"„auto“ (автоматично) или „never“ (никога)"
-
-#: parse-options-cb.c:130 parse-options-cb.c:147
-#, c-format
-msgid "malformed object name '%s'"
-msgstr "неправилно име на обект „%s“"
-
#: path.c:915
#, c-format
msgid "Could not make %s writable by group"
@@ -5464,27 +5531,27 @@ msgstr "Магическите пътища „%c“ са без реализа
msgid "%s: 'literal' and 'glob' are incompatible"
msgstr "%s: опциите „literal“ и „glob“ са несъвместими"
-#: pathspec.c:442
+#: pathspec.c:445
#, c-format
msgid "%s: '%s' is outside repository at '%s'"
msgstr "%s: „%s“ е извън хранилището при „%s“"
-#: pathspec.c:517
+#: pathspec.c:521
#, c-format
msgid "'%s' (mnemonic: '%c')"
msgstr "„%s“ (клавиш: „%c“)"
-#: pathspec.c:527
+#: pathspec.c:531
#, c-format
msgid "%s: pathspec magic not supported by this command: %s"
msgstr "%s: магическите пътища не се поддържат от командата „%s“"
-#: pathspec.c:594
+#: pathspec.c:598
#, c-format
msgid "pathspec '%s' is beyond a symbolic link"
msgstr "пътят „%s“ е след символна връзка"
-#: pathspec.c:639
+#: pathspec.c:643
#, c-format
msgid "line is badly quoted: %s"
msgstr "неправилно цитиран ред: %s"
@@ -5551,7 +5618,7 @@ msgstr "Обновяване на индекса"
msgid "unable to create threaded lstat: %s"
msgstr "не може да се създаде нишка за изпълнението на „lstat“: %s"
-#: pretty.c:981
+#: pretty.c:982
msgid "unable to parse --pretty format"
msgstr "аргументът към опцията „--pretty“ не може да се анализира"
@@ -5565,30 +5632,43 @@ msgid "promisor remote name cannot begin with '/': %s"
msgstr ""
"името отдалеченото хранилище-гарант не може за започва със знака „/“: %s"
-#: range-diff.c:75
+#: prune-packed.c:35
+msgid "Removing duplicate objects"
+msgstr "Изтриване на повтарящите се обекти"
+
+#: range-diff.c:77
msgid "could not start `log`"
msgstr "командата за журнала с подавания „log“ не може да се стартира"
-#: range-diff.c:77
+#: range-diff.c:79
msgid "could not read `log` output"
msgstr ""
"изходът от командата за журнала с подавания „log“ не може да се прочете"
-#: range-diff.c:96 sequencer.c:5020
+#: range-diff.c:98 sequencer.c:5143
#, c-format
msgid "could not parse commit '%s'"
msgstr "подаването „%s“ не може да бъде анализирано"
-#: range-diff.c:122
+#: range-diff.c:112
+#, c-format
+msgid ""
+"could not parse first line of `log` output: did not start with 'commit ': "
+"'%s'"
+msgstr ""
+"първият ред от изхода на командата „log“ не може да се анализира, защото не "
+"започва с „commit “: „%s“"
+
+#: range-diff.c:137
#, c-format
msgid "could not parse git header '%.*s'"
msgstr "заглавната част на git „%.*s“ не може да се анализира"
-#: range-diff.c:285
+#: range-diff.c:301
msgid "failed to generate diff"
msgstr "неуспешно търсене на разлика"
-#: range-diff.c:518 range-diff.c:520
+#: range-diff.c:534 range-diff.c:536
#, c-format
msgid "could not parse log for '%s'"
msgstr "журналът с подаванията на „%s“ не може да бъде анализиран"
@@ -5709,9 +5789,9 @@ msgstr "неподредени записи за „%s“"
#: read-cache.c:1983 read-cache.c:2271 rerere.c:565 rerere.c:599 rerere.c:1111
#: submodule.c:1619 builtin/add.c:532 builtin/check-ignore.c:181
-#: builtin/checkout.c:470 builtin/checkout.c:656 builtin/clean.c:967
-#: builtin/commit.c:367 builtin/diff-tree.c:120 builtin/grep.c:485
-#: builtin/mv.c:145 builtin/reset.c:246 builtin/rm.c:290
+#: builtin/checkout.c:488 builtin/checkout.c:676 builtin/clean.c:961
+#: builtin/commit.c:364 builtin/diff-tree.c:121 builtin/grep.c:507
+#: builtin/mv.c:145 builtin/reset.c:247 builtin/rm.c:290
#: builtin/submodule--helper.c:332
msgid "index file corrupt"
msgstr "файлът с индекса е повреден"
@@ -5768,12 +5848,12 @@ msgstr "споделеният индекс „%s“ не може да се о
msgid "broken index, expect %s in %s, got %s"
msgstr "грешки в индекса — в „%2$s“ се очаква „%1$s“, а бе получено „%3$s“"
-#: read-cache.c:3026 strbuf.c:1160 wrapper.c:622 builtin/merge.c:1122
+#: read-cache.c:3026 strbuf.c:1176 wrapper.c:622 builtin/merge.c:1130
#, c-format
msgid "could not close '%s'"
msgstr "„%s“ не може да се затвори"
-#: read-cache.c:3129 sequencer.c:2343 sequencer.c:3959
+#: read-cache.c:3129 sequencer.c:2355 sequencer.c:4066
#, c-format
msgid "could not stat '%s'"
msgstr "неуспешно изпълнение на „stat“ върху „%s“"
@@ -5912,14 +5992,14 @@ msgstr ""
"Ако изтриете всичко, пребазирането ще бъде преустановено.\n"
"\n"
-#: rebase-interactive.c:110 rerere.c:485 rerere.c:692 sequencer.c:3444
-#: sequencer.c:3470 sequencer.c:5125 builtin/fsck.c:346 builtin/rebase.c:252
+#: rebase-interactive.c:110 rerere.c:485 rerere.c:692 sequencer.c:3463
+#: sequencer.c:3489 sequencer.c:5248 builtin/fsck.c:347 builtin/rebase.c:258
#, c-format
msgid "could not write '%s'"
msgstr "„%s“ не може да се запише"
-#: rebase-interactive.c:116 builtin/rebase.c:184 builtin/rebase.c:210
-#: builtin/rebase.c:234
+#: rebase-interactive.c:116 builtin/rebase.c:190 builtin/rebase.c:216
+#: builtin/rebase.c:240
#, c-format
msgid "could not write '%s'."
msgstr "„%s“ не може да се запише."
@@ -5950,132 +6030,14 @@ msgstr ""
"предупреждение)\n"
"или „error“ (считане за грешка).\n"
-#: rebase-interactive.c:233 rebase-interactive.c:238 sequencer.c:2262
-#: builtin/rebase.c:170 builtin/rebase.c:195 builtin/rebase.c:221
-#: builtin/rebase.c:246
+#: rebase-interactive.c:233 rebase-interactive.c:238 sequencer.c:2274
+#: builtin/rebase.c:176 builtin/rebase.c:201 builtin/rebase.c:227
+#: builtin/rebase.c:252
#, c-format
msgid "could not read '%s'."
msgstr "от „%s“ не може да се чете."
-#: refs.c:262
-#, c-format
-msgid "%s does not point to a valid object!"
-msgstr "„%s“ не сочи към позволен обект!"
-
-#: refs.c:667
-#, c-format
-msgid "ignoring dangling symref %s"
-msgstr "игнориране на указател на обект извън клон „%s“"
-
-#: refs.c:669 ref-filter.c:2098
-#, c-format
-msgid "ignoring broken ref %s"
-msgstr "игнориране на повредения указател „%s“"
-
-#: refs.c:804
-#, c-format
-msgid "could not open '%s' for writing: %s"
-msgstr "„%s“ не може да бъде отворен за запис: %s"
-
-#: refs.c:814 refs.c:865
-#, c-format
-msgid "could not read ref '%s'"
-msgstr "указателят „%s“ не може да се прочете"
-
-#: refs.c:820
-#, c-format
-msgid "ref '%s' already exists"
-msgstr "указателят „%s“ вече съществува"
-
-#: refs.c:825
-#, c-format
-msgid "unexpected object ID when writing '%s'"
-msgstr "неочакван идентификатор на обект при записването на „%s“"
-
-#: refs.c:833 sequencer.c:407 sequencer.c:2701 sequencer.c:2905
-#: sequencer.c:2919 sequencer.c:3177 sequencer.c:5036 strbuf.c:1157
-#: wrapper.c:620
-#, c-format
-msgid "could not write to '%s'"
-msgstr "в „%s“ не може да се пише"
-
-#: refs.c:860 strbuf.c:1155 wrapper.c:188 wrapper.c:358 builtin/am.c:719
-#: builtin/rebase.c:1029
-#,