summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/l10n.yml105
-rw-r--r--.github/workflows/main.yml10
-rw-r--r--.gitignore3
-rw-r--r--Documentation/.gitignore1
-rw-r--r--Documentation/Makefile68
-rw-r--r--Documentation/MyFirstContribution.txt83
-rw-r--r--Documentation/RelNotes/2.33.1.txt138
-rw-r--r--Documentation/RelNotes/2.34.0.txt338
-rw-r--r--Documentation/RelNotes/2.34.1.txt23
-rw-r--r--Documentation/RelNotes/2.35.0.txt65
-rw-r--r--Documentation/SubmittingPatches4
-rw-r--r--Documentation/blame-options.txt11
-rw-r--r--Documentation/config.txt2
-rw-r--r--Documentation/config/branch.txt4
-rw-r--r--Documentation/config/color.txt38
-rw-r--r--Documentation/config/gpg.txt43
-rw-r--r--Documentation/config/pack.txt4
-rw-r--r--Documentation/config/pull.txt4
-rw-r--r--Documentation/config/user.txt7
-rw-r--r--Documentation/diff-format.txt2
-rw-r--r--Documentation/git-add.txt9
-rw-r--r--Documentation/git-am.txt2
-rw-r--r--Documentation/git-archive.txt17
-rw-r--r--Documentation/git-blame.txt17
-rw-r--r--Documentation/git-branch.txt4
-rw-r--r--Documentation/git-bundle.txt10
-rw-r--r--Documentation/git-cat-file.txt6
-rw-r--r--Documentation/git-checkout.txt5
-rw-r--r--Documentation/git-commit.txt5
-rw-r--r--Documentation/git-config.txt3
-rw-r--r--Documentation/git-cvsserver.txt27
-rw-r--r--Documentation/git-format-patch.txt6
-rw-r--r--Documentation/git-help.txt9
-rw-r--r--Documentation/git-http-backend.txt26
-rw-r--r--Documentation/git-index-pack.txt6
-rw-r--r--Documentation/git-maintenance.txt57
-rw-r--r--Documentation/git-multi-pack-index.txt47
-rw-r--r--Documentation/git-pull.txt6
-rw-r--r--Documentation/git-read-tree.txt23
-rw-r--r--Documentation/git-rebase.txt54
-rw-r--r--Documentation/git-receive-pack.txt5
-rw-r--r--Documentation/git-repack.txt18
-rw-r--r--Documentation/git-reset.txt3
-rw-r--r--Documentation/git-rm.txt6
-rw-r--r--Documentation/git-send-email.txt6
-rw-r--r--Documentation/git-send-pack.txt4
-rw-r--r--Documentation/git-sparse-checkout.txt10
-rw-r--r--Documentation/git-stash.txt34
-rw-r--r--Documentation/git-status.txt33
-rw-r--r--Documentation/git-svn.txt1
-rw-r--r--Documentation/git-upload-pack.txt20
-rw-r--r--Documentation/git-version.txt28
-rw-r--r--Documentation/git.txt38
-rw-r--r--Documentation/gitignore.txt2
-rw-r--r--Documentation/gitweb.txt2
-rwxr-xr-xDocumentation/lint-gitlink.perl10
-rwxr-xr-xDocumentation/lint-man-end-blurb.perl2
-rwxr-xr-xDocumentation/lint-man-section-order.perl2
-rw-r--r--Documentation/merge-options.txt5
-rw-r--r--Documentation/pretty-options.txt4
-rw-r--r--Documentation/rev-list-options.txt8
-rw-r--r--Documentation/technical/api-parse-options.txt5
-rw-r--r--Documentation/technical/api-trace2.txt44
-rw-r--r--Documentation/technical/bitmap-format.txt71
-rw-r--r--Documentation/technical/http-protocol.txt3
-rw-r--r--Documentation/technical/multi-pack-index.txt14
-rw-r--r--Documentation/technical/protocol-v2.txt17
-rw-r--r--Documentation/technical/signature-format.txt24
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--INSTALL15
-rw-r--r--Makefile138
l---------RelNotes2
-rw-r--r--add-interactive.c8
-rw-r--r--advice.c11
-rw-r--r--attr.c15
-rw-r--r--builtin.h1
-rw-r--r--builtin/add.c39
-rw-r--r--builtin/am.c8
-rw-r--r--builtin/bisect--helper.c160
-rw-r--r--builtin/blame.c33
-rw-r--r--builtin/branch.c23
-rw-r--r--builtin/bugreport.c46
-rw-r--r--builtin/bundle.c14
-rw-r--r--builtin/cat-file.c54
-rw-r--r--builtin/checkout--worker.c4
-rw-r--r--builtin/checkout.c10
-rw-r--r--builtin/clone.c164
-rw-r--r--builtin/commit-graph.c34
-rw-r--r--builtin/commit.c3
-rw-r--r--builtin/config.c2
-rw-r--r--builtin/credential-cache.c30
-rw-r--r--builtin/difftool.c159
-rw-r--r--builtin/fast-export.c2
-rw-r--r--builtin/fetch.c76
-rw-r--r--builtin/for-each-ref.c12
-rw-r--r--builtin/fsck.c52
-rw-r--r--builtin/gc.c629
-rw-r--r--builtin/grep.c81
-rw-r--r--builtin/help.c140
-rw-r--r--builtin/index-pack.c60
-rw-r--r--builtin/ls-files.c22
-rw-r--r--builtin/ls-remote.c17
-rw-r--r--builtin/merge.c10
-rw-r--r--builtin/mktag.c3
-rw-r--r--builtin/multi-pack-index.c94
-rw-r--r--builtin/mv.c52
-rw-r--r--builtin/pack-objects.c34
-rw-r--r--builtin/prune.c1
-rw-r--r--builtin/pull.c51
-rw-r--r--builtin/read-tree.c26
-rw-r--r--builtin/rebase.c388
-rw-r--r--builtin/receive-pack.c30
-rw-r--r--builtin/reflog.c14
-rw-r--r--builtin/remote.c114
-rw-r--r--builtin/repack.c301
-rw-r--r--builtin/reset.c10
-rw-r--r--builtin/rev-parse.c4
-rw-r--r--builtin/revert.c3
-rw-r--r--builtin/rm.c10
-rw-r--r--builtin/send-pack.c12
-rw-r--r--builtin/shortlog.c3
-rw-r--r--builtin/show-branch.c6
-rw-r--r--builtin/sparse-checkout.c94
-rw-r--r--builtin/stash.c107
-rw-r--r--builtin/submodule--helper.c669
-rw-r--r--builtin/tag.c46
-rw-r--r--builtin/update-ref.c14
-rw-r--r--builtin/upload-pack.c28
-rw-r--r--builtin/worktree.c1
-rw-r--r--bulk-checkin.c31
-rw-r--r--bundle.c12
-rw-r--r--bundle.h14
-rw-r--r--cache-tree.c42
-rw-r--r--cache.h118
-rw-r--r--cbtree.h5
-rw-r--r--checkout.c2
-rwxr-xr-xci/install-dependencies.sh2
-rwxr-xr-xci/lib.sh9
-rwxr-xr-xci/run-build-and-tests.sh1
-rw-r--r--command-list.txt2
-rw-r--r--commit-graph.c3
-rw-r--r--compat/linux/procinfo.c169
-rw-r--r--compat/nedmalloc/nedmalloc.c2
-rw-r--r--compat/simple-ipc/ipc-unix-socket.c36
-rw-r--r--compat/simple-ipc/ipc-win32.c179
-rw-r--r--compat/terminal.c75
-rw-r--r--compat/terminal.h3
-rw-r--r--compat/unsetenv.c4
-rw-r--r--compat/vcbuild/README2
-rw-r--r--compat/win32/lazyload.h14
-rw-r--r--config.c64
-rw-r--r--config.h5
-rw-r--r--config.mak.dev23
-rw-r--r--config.mak.uname13
-rw-r--r--configure.ac8
-rw-r--r--connect.c2
-rw-r--r--connected.c16
-rw-r--r--connected.h2
-rw-r--r--contrib/buildsystems/CMakeLists.txt8
-rw-r--r--contrib/coccinelle/xopen.cocci13
-rw-r--r--contrib/completion/git-completion.bash36
-rw-r--r--contrib/credential/gnome-keyring/git-credential-gnome-keyring.c2
-rw-r--r--contrib/credential/libsecret/git-credential-libsecret.c2
-rwxr-xr-xcontrib/rerere-train.sh2
-rw-r--r--convert.c2
-rw-r--r--credential.c2
-rw-r--r--daemon.c21
-rw-r--r--delta.h6
-rw-r--r--diff.c12
-rw-r--r--dir.c185
-rw-r--r--dir.h19
-rw-r--r--entry.c20
-rw-r--r--entry.h4
-rw-r--r--environment.c12
-rw-r--r--fetch-negotiator.c1
-rw-r--r--fetch-pack.c12
-rw-r--r--fmt-merge-msg.c12
-rwxr-xr-xgenerate-hooklist.sh20
-rw-r--r--gettext.h24
-rwxr-xr-xgit-bisect.sh89
-rw-r--r--git-compat-util.h35
-rw-r--r--git-curl-compat.h129
-rwxr-xr-xgit-cvsserver.perl5
-rwxr-xr-xgit-filter-branch.sh2
-rwxr-xr-xgit-instaweb.sh9
-rw-r--r--git-rebase--preserve-merges.sh1057
-rwxr-xr-xgit-send-email.perl56
-rw-r--r--git-sh-i18n.sh12
-rw-r--r--git-sh-setup.sh26
-rwxr-xr-xgit-submodule.sh233
-rwxr-xr-xgit-svn.perl1
-rw-r--r--git.c3
-rw-r--r--gpg-interface.c587
-rw-r--r--gpg-interface.h8
-rw-r--r--grep.c205
-rw-r--r--grep.h36
-rw-r--r--help.c16
-rw-r--r--help.h2
-rw-r--r--hook.c42
-rw-r--r--hook.h16
-rw-r--r--http-backend.c10
-rw-r--r--http.c47
-rw-r--r--http.h1
-rw-r--r--imap-send.c2
-rw-r--r--list.h5
-rw-r--r--lockfile.h2
-rw-r--r--log-tree.c8
-rw-r--r--log-tree.h2
-rw-r--r--ls-refs.c31
-rw-r--r--ls-refs.h4
-rw-r--r--merge-ort.c80
-rw-r--r--merge-recursive.c82
-rw-r--r--merge.c8
-rw-r--r--mergesort.c121
-rw-r--r--mergetools/xxdiff7
-rw-r--r--midx.c532
-rw-r--r--midx.h23
-rw-r--r--object-file.c308
-rw-r--r--object-store.h24
-rw-r--r--object.c4
-rw-r--r--object.h2
-rw-r--r--oid-array.h2
-rw-r--r--oidset.c5
-rw-r--r--oidset.h5
-rw-r--r--pack-bitmap-write.c79
-rw-r--r--pack-bitmap.c547
-rw-r--r--pack-bitmap.h10
-rw-r--r--pack-check.c3
-rw-r--r--pack-revindex.c8
-rw-r--r--pack-revindex.h4
-rw-r--r--pack-write.c57
-rw-r--r--pack.h10
-rw-r--r--packfile.c47
-rw-r--r--packfile.h4
-rw-r--r--parallel-checkout.c5
-rw-r--r--parse-options.c176
-rw-r--r--parse-options.h30
-rw-r--r--path.c13
-rw-r--r--path.h5
-rw-r--r--pathspec.c5
-rw-r--r--pkt-line.c80
-rw-r--r--pkt-line.h21
-rw-r--r--po/TEAMS2
-rw-r--r--po/bg.po8686
-rw-r--r--po/ca.po11997
-rw-r--r--po/de.po8383
-rw-r--r--po/es.po9583
-rw-r--r--po/fr.po8349
-rw-r--r--po/git.pot8101
-rw-r--r--po/id.po10276
-rw-r--r--po/it.po6
-rw-r--r--po/ko.po10
-rw-r--r--po/pl.po11575
-rw-r--r--po/pt_PT.po3725
-rw-r--r--po/sv.po8884
-rw-r--r--po/tr.po8372
-rw-r--r--po/vi.po8937
-rw-r--r--po/zh_CN.po8833
-rw-r--r--po/zh_TW.po9052
-rw-r--r--pretty.c111
-rw-r--r--protocol-caps.c3
-rw-r--r--protocol-caps.h4
-rw-r--r--read-cache.c107
-rw-r--r--rebase-interactive.c29
-rw-r--r--rebase-interactive.h2
-rw-r--r--rebase.c5
-rw-r--r--rebase.h1
-rw-r--r--ref-filter.c67
-rw-r--r--ref-filter.h31
-rw-r--r--reflog-walk.c3
-rw-r--r--refs.c215
-rw-r--r--refs.h30
-rw-r--r--refs/debug.c15
-rw-r--r--refs/files-backend.c415
-rw-r--r--refs/packed-backend.c35
-rw-r--r--refs/packed-backend.h4
-rw-r--r--refs/ref-cache.c97
-rw-r--r--refs/ref-cache.h27
-rw-r--r--refs/refs-internal.h113
-rw-r--r--remote-curl.c10
-rw-r--r--remote.c2
-rw-r--r--repo-settings.c115
-rw-r--r--repository.c24
-rw-r--r--repository.h35
-rw-r--r--reset.c3
-rw-r--r--revision.c29
-rw-r--r--revision.h1
-rw-r--r--run-command.c176
-rw-r--r--run-command.h99
-rw-r--r--send-pack.c8
-rw-r--r--sequencer.c114
-rw-r--r--sequencer.h7
-rw-r--r--serve.c198
-rw-r--r--serve.h12
-rw-r--r--setup.c8
-rw-r--r--shallow.h4
-rw-r--r--simple-ipc.h27
-rw-r--r--sparse-index.c76
-rw-r--r--sparse-index.h3
-rw-r--r--strbuf.c12
-rw-r--r--strbuf.h8
-rw-r--r--streaming.c27
-rw-r--r--string-list.c8
-rw-r--r--string-list.h5
-rw-r--r--strvec.h8
-rw-r--r--submodule-config.c5
-rw-r--r--submodule-config.h4
-rw-r--r--submodule.c172
-rw-r--r--submodule.h19
-rw-r--r--t/.gitattributes1
-rw-r--r--t/README18
-rw-r--r--t/helper/test-bitmap.c10
-rw-r--r--t/helper/test-dump-untracked-cache.c6
-rw-r--r--t/helper/test-genzeros.c21
-rw-r--r--t/helper/test-mergesort.c368
-rw-r--r--t/helper/test-oid-array.c4
-rw-r--r--t/helper/test-oidtree.c3
-rw-r--r--t/helper/test-parse-options.c8
-rw-r--r--t/helper/test-prio-queue.c2
-rw-r--r--t/helper/test-read-midx.c46
-rw-r--r--t/helper/test-ref-store.c3
-rw-r--r--t/helper/test-run-command.c9
-rw-r--r--t/helper/test-serve-v2.c14
-rw-r--r--t/helper/test-simple-ipc.c233
-rw-r--r--t/helper/test-submodule-nested-repo-config.c4
-rw-r--r--t/lib-bitmap.sh240
-rw-r--r--t/lib-diff-data.sh22
-rw-r--r--t/lib-diff.sh2
-rw-r--r--t/lib-diff/COPYING361
-rw-r--r--t/lib-diff/README46
-rw-r--r--t/lib-gpg.sh35
-rw-r--r--t/lib-httpd.sh1
-rw-r--r--t/lib-httpd/apache.conf11
-rw-r--r--t/lib-httpd/error-no-report.sh6
-rw-r--r--t/lib-midx.sh8
-rw-r--r--t/lib-subtest.sh95
-rw-r--r--t/oid-info/oid2
-rwxr-xr-xt/perf/aggregate.perl4
-rw-r--r--t/perf/config2
-rw-r--r--t/perf/lib-bitmap.sh69
-rwxr-xr-xt/perf/p0071-sort.sh40
-rwxr-xr-xt/perf/p3400-rebase.sh2
-rwxr-xr-xt/perf/p5310-pack-bitmaps.sh65
-rwxr-xr-xt/perf/p5326-multi-pack-bitmaps.sh52
-rw-r--r--t/perf/perf-lib.sh5
-rwxr-xr-xt/perf/run2
-rwxr-xr-xt/t0000-basic.sh471
-rwxr-xr-xt/t0001-init.sh1
-rwxr-xr-xt/t0002-gitfile.sh1
-rwxr-xr-xt/t0003-attributes.sh1
-rwxr-xr-xt/t0004-unwritable.sh50
-rwxr-xr-xt/t0005-signals.sh2
-rwxr-xr-xt/t0007-git-var.sh2
-rwxr-xr-xt/t0008-ignores.sh44
-rwxr-xr-xt/t0009-prio-queue.sh2
-rwxr-xr-xt/t0010-racy-git.sh1
-rwxr-xr-xt/t0011-hashmap.sh2
-rwxr-xr-xt/t0012-help.sh65
-rwxr-xr-xt/t0013-sha1dc.sh2
-rwxr-xr-xt/t0016-oidmap.sh2
-rwxr-xr-xt/t0017-env-helper.sh1
-rwxr-xr-xt/t0018-advice.sh1
-rwxr-xr-xt/t0022-crlf-rename.sh1
-rwxr-xr-xt/t0024-crlf-archive.sh1
-rwxr-xr-xt/t0025-crlf-renormalize.sh1
-rwxr-xr-xt/t0026-eol-config.sh1
-rwxr-xr-xt/t0029-core-unsetenvvars.sh1
-rwxr-xr-xt/t0030-stripspace.sh1
-rwxr-xr-xt/t0040-parse-options.sh48
-rwxr-xr-xt/t0052-simple-ipc.sh1
-rwxr-xr-xt/t0055-beyond-symlinks.sh1
-rwxr-xr-xt/t0061-run-command.sh1
-rwxr-xr-xt/t0063-string-list.sh1
-rwxr-xr-xt/t0064-oid-array.sh2
-rwxr-xr-xt/t0065-strcmp-offset.sh1
-rwxr-xr-xt/t0066-dir-iterator.sh1
-rwxr-xr-xt/t0067-parse_pathspec_file.sh1
-rwxr-xr-xt/t0069-oidtree.sh1
-rwxr-xr-xt/t0071-sort.sh11
-rwxr-xr-xt/t0090-cache-tree.sh1
-rwxr-xr-xt/t0091-bugreport.sh1
-rwxr-xr-xt/t0110-urlmatch-normalization.sh2
-rwxr-xr-xt/t0210-trace2-normal.sh2
-rwxr-xr-xt/t0211-trace2-perf.sh2
-rwxr-xr-xt/t0212-trace2-event.sh2
-rwxr-xr-xt/t0301-credential-cache.sh32
-rwxr-xr-xt/t0410-partial-clone.sh18
-rwxr-xr-xt/t1000-read-tree-m-3way.sh2
-rwxr-xr-xt/t1001-read-tree-m-2way.sh2
-rwxr-xr-xt/t1003-read-tree-prefix.sh1
-rwxr-xr-xt/t1006-cat-file.sh297
-rwxr-xr-xt/t1009-read-tree-new-index.sh1
-rwxr-xr-xt/t1010-mktree.sh1
-rwxr-xr-xt/t1012-read-tree-df.sh1
-rwxr-xr-xt/t1013-read-tree-submodule.sh1
-rwxr-xr-xt/t1014-read-tree-confusing.sh2
-rwxr-xr-xt/t1050-large.sh8
-rwxr-xr-xt/t1051-large-conversion.sh26
-rwxr-xr-xt/t1091-sparse-checkout-builtin.sh88
-rwxr-xr-xt/t1092-sparse-checkout-compatibility.sh163
-rwxr-xr-xt/t1100-commit-tree-options.sh1
-rwxr-xr-xt/t1305-config-include.sh1
-rwxr-xr-xt/t1400-update-ref.sh34
-rwxr-xr-xt/t1417-reflog-updateref.sh65
-rwxr-xr-xt/t1430-bad-ref-name.sh3
-rwxr-xr-xt/t1450-fsck.sh97
-rwxr-xr-xt/t1502-rev-parse-parseopt.sh54
-rwxr-xr-xt/t1504-ceiling-dirs.sh2
-rwxr-xr-xt/t1510-repo-setup.sh1
-rwxr-xr-xt/t1600-index.sh13
-rwxr-xr-xt/t1700-split-index.sh34
-rwxr-xr-xt/t2002-checkout-cache-u.sh1
-rwxr-xr-xt/t2003-checkout-cache-mkdir.sh1
-rwxr-xr-xt/t2004-checkout-cache-temp.sh1
-rwxr-xr-xt/t2005-checkout-index-symlinks.sh1
-rwxr-xr-xt/t2021-checkout-overwrite.sh1
-rwxr-xr-xt/t2050-git-dir-relative.sh1
-rwxr-xr-xt/t2081-parallel-checkout-collisions.sh1
-rwxr-xr-xt/t2200-add-update.sh3
-rwxr-xr-xt/t2300-cd-to-toplevel.sh1
-rwxr-xr-xt/t2500-untracked-overwriting.sh244
-rwxr-xr-xt/t3000-ls-files-others.sh2
-rwxr-xr-xt/t3001-ls-files-others-exclude.sh5
-rwxr-xr-xt/t3002-ls-files-dashpath.sh2
-rwxr-xr-xt/t3003-ls-files-exclude.sh2
-rwxr-xr-xt/t3004-ls-files-basic.sh1
-rwxr-xr-xt/t3005-ls-files-relative.sh1
-rwxr-xr-xt/t3006-ls-files-long.sh2
-rwxr-xr-xt/t3008-ls-files-lazy-init-name-hash.sh1
-rwxr-xr-xt/t3020-ls-files-error-unmatch.sh2
-rwxr-xr-xt/t3070-wildmatch.sh1
-rwxr-xr-xt/t3100-ls-tree-restrict.sh2
-rwxr-xr-xt/t3101-ls-tree-dirname.sh2
-rwxr-xr-xt/t3102-ls-tree-wildcards.sh1
-rwxr-xr-xt/t3103-ls-tree-misc.sh1
-rwxr-xr-xt/t3200-branch.sh34
-rwxr-xr-xt/t3205-branch-color.sh1
-rwxr-xr-xt/t3211-peel-ref.sh1
-rwxr-xr-xt/t3300-funny-names.sh1
-rwxr-xr-xt/t3404-rebase-interactive.sh77
-rwxr-xr-xt/t3407-rebase-abort.sh105
-rwxr-xr-xt/t3408-rebase-multi-line.sh10
-rwxr-xr-xt/t3409-rebase-preserve-merges.sh130
-rwxr-xr-xt/t3410-rebase-preserve-dropped-merges.sh90
-rwxr-xr-xt/t3411-rebase-preserve-around-merges.sh80
-rwxr-xr-xt/t3412-rebase-root.sh37
-rwxr-xr-xt/t3414-rebase-preserve-onto.sh85
-rwxr-xr-xt/t3418-rebase-continue.sh15
-rwxr-xr-xt/t3421-rebase-topology-linear.sh19
-rwxr-xr-xt/t3422-rebase-incompatible-options.sh11
-rwxr-xr-xt/t3425-rebase-topology-merges.sh151
-rwxr-xr-xt/t3427-rebase-subtree.sh26
-rwxr-xr-xt/t3435-rebase-gpg-sign.sh1
-rwxr-xr-xt/t3436-rebase-more-options.sh29
-rwxr-xr-xt/t3510-cherry-pick-sequence.sh1
-rwxr-xr-xt/t3601-rm-pathspec-file.sh1
-rwxr-xr-xt/t3602-rm-sparse-checkout.sh69
-rwxr-xr-xt/t3700-add.sh1
-rwxr-xr-xt/t3705-add-sparse-checkout.sh85
-rwxr-xr-xt/t3902-quoted.sh1
-rwxr-xr-xt/t3903-stash.sh69
-rwxr-xr-xt/t3905-stash-include-untracked.sh6
-rwxr-xr-xt/t4002-diff-basic.sh2
-rwxr-xr-xt/t4003-diff-rename-1.sh4
-rwxr-xr-xt/t4005-diff-rename-2.sh4
-rwxr-xr-xt/t4007-rename-3.sh5
-rwxr-xr-xt/t4008-diff-break-rewrite.sh4
-rwxr-xr-xt/t4009-diff-rename-4.sh4
-rwxr-xr-xt/t4016-diff-quote.sh1
-rwxr-xr-xt/t4019-diff-wserror.sh1
-rwxr-xr-xt/t4022-diff-rewrite.sh6
-rwxr-xr-xt/t4023-diff-rename-typechange.sh11
-rwxr-xr-xt/t4025-hunk-header.sh1
-rwxr-xr-xt/t4026-color.sh2
-rw-r--r--t/t4034/cpp/expect63
-rw-r--r--t/t4034/cpp/post47
-rw-r--r--t/t4034/cpp/pre41
-rwxr-xr-xt/t4060-diff-submodule-option-diff-format.sh159
-rwxr-xr-xt/t4151-am-abort.sh39
-rwxr-xr-xt/t4202-log.sh74
-rwxr-xr-xt/t4210-log-i18n.sh7
-rwxr-xr-xt/t4300-merge-tree.sh2
-rwxr-xr-xt/t5304-prune.sh1
-rwxr-xr-xt/t5310-pack-bitmaps.sh234
-rwxr-xr-xt/t5312-prune-corruption.sh48
-rwxr-xr-xt/t5318-commit-graph.sh28
-rwxr-xr-xt/t5319-multi-pack-index.sh95
-rwxr-xr-xt/t5324-split-commit-graph.sh20
-rwxr-xr-xt/t5326-multi-pack-bitmaps.sh398
-rwxr-xr-xt/t5516-fetch-push.sh19
-rwxr-xr-xt/t5520-pull.sh24
-rwxr-xr-xt/t5521-pull-options.sh24
-rwxr-xr-xt/t5526-fetch-submodules.sh3
-rwxr-xr-xt/t5531-deep-submodule-push.sh3
-rwxr-xr-xt/t5534-push-signed.sh101
-rwxr-xr-xt/t5541-http-push-smart.sh16
-rwxr-xr-xt/t5545-push-options.sh3
-rwxr-xr-xt/t5551-http-fetch-smart.sh33
-rwxr-xr-xt/t5555-http-smart-common.sh161
-rw-r--r--t/t5562/invoke-with-content-length.pl16
-rwxr-xr-xt/t5572-pull-submodule.sh3
-rwxr-xr-xt/t5580-unc-paths.sh1
-rwxr-xr-xt/t5600-clone-fail-cleanup.sh4
-rwxr-xr-xt/t5615-alternate-env.sh2
-rwxr-xr-xt/t5701-git-serve.sh75
-rwxr-xr-xt/t5702-protocol-v2.sh13
-rwxr-xr-xt/t5704-protocol-violations.sh15
-rwxr-xr-xt/t6000-rev-list-misc.sh31
-rwxr-xr-xt/t6030-bisect-porcelain.sh18
-rwxr-xr-xt/t6200-fmt-merge-msg.sh28
-rwxr-xr-xt/t6300-for-each-ref.sh26
-rwxr-xr-xt/t6415-merge-dir-to-symlink.sh6
-rwxr-xr-xt/t6424-merge-unrelated-index-changes.sh1
-rwxr-xr-xt/t6430-merge-recursive.sh4
-rwxr-xr-xt/t6436-merge-overwrite.sh3
-rwxr-xr-xt/t6437-submodule-merge.sh3
-rwxr-xr-xt/t7001-mv.sh5
-rwxr-xr-xt/t7002-mv-sparse-checkout.sh209
-rwxr-xr-xt/t7031-verify-tag-signed-ssh.sh161
-rwxr-xr-xt/t7064-wtstatus-pv2.sh15
-rwxr-xr-xt/t7101-reset-empty-subdirs.sh11
-rwxr-xr-xt/t7104-reset-hard.sh1
-rwxr-xr-xt/t7112-reset-submodule.sh1
-rwxr-xr-xt/t7201-co.sh1
-rwxr-xr-xt/t7418-submodule-sparse-gitmodules.sh3
-rwxr-xr-xt/t7504-commit-msg-hook.sh8
-rwxr-xr-xt/t7505-prepare-commit-msg-hook.sh1
-rwxr-xr-xt/t7510-signed-commit.sh29
-rwxr-xr-xt/t7517-per-repo-email.sh13
-rwxr-xr-xt/t7518-ident-corner-cases.sh2
-rwxr-xr-xt/t7519-status-fsmonitor.sh70
-rwxr-xr-xt/t7528-signed-commit-ssh.sh398
-rwxr-xr-xt/t7600-merge.sh1
-rwxr-xr-xt/t7601-merge-pull-config.sh22
-rwxr-xr-xt/t7604-merge-custom-message.sh1
-rwxr-xr-xt/t7700-repack.sh156
-rwxr-xr-xt/t7703-repack-geometric.sh24
-rwxr-xr-xt/t7800-difftool.sh74
-rwxr-xr-xt/t7811-grep-open.sh3
-rwxr-xr-xt/t7813-grep-icase-iso.sh1
-rwxr-xr-xt/t7814-grep-recurse-submodules.sh106
-rwxr-xr-xt/t7816-grep-binary-pattern.sh1
-rwxr-xr-xt/t7900-maintenance.sh147
-rwxr-xr-xt/t9400-git-cvsserver-server.sh9
-rwxr-xr-xt/t9902-completion.sh3
-rw-r--r--t/test-lib.sh75
-rw-r--r--trace.h2
-rw-r--r--trace2.c31
-rw-r--r--trace2.h27
-rw-r--r--trace2/tr2_tgt.h5
-rw-r--r--trace2/tr2_tgt_event.c24
-rw-r--r--trace2/tr2_tgt_normal.c14
-rw-r--r--trace2/tr2_tgt_perf.c15
-rw-r--r--trace2/tr2_tls.c1
-rw-r--r--transport-helper.c22
-rw-r--r--transport-internal.h2
-rw-r--r--transport.c40
-rw-r--r--transport.h4
-rw-r--r--tree-diff.c4
-rw-r--r--unicode-width.h44
-rw-r--r--unpack-trees.c72
-rw-r--r--unpack-trees.h14
-rw-r--r--upload-pack.c32
-rw-r--r--upload-pack.h14
-rw-r--r--urlmatch.c2
-rw-r--r--urlmatch.h4
-rw-r--r--userdiff.c10
-rw-r--r--worktree.c17
-rw-r--r--wrapper.c6
-rw-r--r--write-or-die.c12
-rw-r--r--wt-status.c25
571 files changed, 79192 insertions, 69123 deletions
diff --git a/.github/workflows/l10n.yml b/.github/workflows/l10n.yml
new file mode 100644
index 0000000..27f72f0
--- /dev/null
+++ b/.github/workflows/l10n.yml
@@ -0,0 +1,105 @@
+name: git-l10n
+
+on: [push, pull_request_target]
+
+jobs:
+ git-po-helper:
+ if: >-
+ endsWith(github.repository, '/git-po') ||
+ contains(github.head_ref, 'l10n') ||
+ contains(github.ref, 'l10n')
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ steps:
+ - name: Setup base and head objects
+ id: setup-tips
+ run: |
+ if test "${{ github.event_name }}" = "pull_request_target"
+ then
+ base=${{ github.event.pull_request.base.sha }}
+ head=${{ github.event.pull_request.head.sha }}
+ else
+ base=${{ github.event.before }}
+ head=${{ github.event.after }}
+ fi
+ echo "::set-output name=base::$base"
+ echo "::set-output name=head::$head"
+ - name: Run partial clone
+ run: |
+ git -c init.defaultBranch=master init --bare .
+ git remote add \
+ --mirror=fetch \
+ origin \
+ https://github.com/${{ github.repository }}
+ # Fetch tips that may be unreachable from github.ref:
+ # - For a forced push, "$base" may be unreachable.
+ # - For a "pull_request_target" event, "$head" may be unreachable.
+ args=
+ for commit in \
+ ${{ steps.setup-tips.outputs.base }} \
+ ${{ steps.setup-tips.outputs.head }}
+ do
+ case $commit in
+ *[^0]*)
+ args="$args $commit"
+ ;;
+ *)
+ # Should not fetch ZERO-OID.
+ ;;
+ esac
+ done
+ git -c protocol.version=2 fetch \
+ --progress \
+ --no-tags \
+ --no-write-fetch-head \
+ --filter=blob:none \
+ origin \
+ ${{ github.ref }} \
+ $args
+ - uses: actions/setup-go@v2
+ with:
+ go-version: '>=1.16'
+ - name: Install git-po-helper
+ run: go install github.com/git-l10n/git-po-helper@main
+ - name: Install other dependencies
+ run: |
+ sudo apt-get update -q &&
+ sudo apt-get install -q -y gettext
+ - name: Run git-po-helper
+ id: check-commits
+ run: |
+ exit_code=0
+ git-po-helper check-commits \
+ --github-action-event="${{ github.event_name }}" -- \
+ ${{ steps.setup-tips.outputs.base }}..${{ steps.setup-tips.outputs.head }} \
+ >git-po-helper.out 2>&1 || exit_code=$?
+ if test $exit_code -ne 0 || grep -q WARNING git-po-helper.out
+ then
+ # Remove ANSI colors which are proper for console logs but not
+ # proper for PR comment.
+ echo "COMMENT_BODY<<EOF" >>$GITHUB_ENV
+ perl -pe 's/\e\[[0-9;]*m//g; s/\bEOF$//g' git-po-helper.out >>$GITHUB_ENV
+ echo "EOF" >>$GITHUB_ENV
+ fi
+ cat git-po-helper.out
+ exit $exit_code
+ - name: Create comment in pull request for report
+ uses: mshick/add-pr-comment@v1
+ if: >-
+ always() &&
+ github.event_name == 'pull_request_target' &&
+ env.COMMENT_BODY != ''
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ repo-token-user-login: 'github-actions[bot]'
+ message: >
+ ${{ steps.check-commits.outcome == 'failure' && 'Errors and warnings' || 'Warnings' }}
+ found by [git-po-helper](https://github.com/git-l10n/git-po-helper#readme) in workflow
+ [#${{ github.run_number }}](${{ env.GITHUB_SERVER_URL }}/${{ github.repository }}/actions/runs/${{ github.run_id }}):
+
+ ```
+
+ ${{ env.COMMENT_BODY }}
+
+ ```
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index b053b01..6ed6a9e 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -88,7 +88,7 @@ jobs:
env:
HOME: ${{runner.workspace}}
NO_PERL: 1
- run: ci/make-test-artifacts.sh artifacts
+ run: . /etc/profile && ci/make-test-artifacts.sh artifacts
- name: zip up tracked files
run: git archive -o artifacts/tracked.tar.gz HEAD
- name: upload tracked files and build artifacts
@@ -115,7 +115,7 @@ jobs:
- uses: git-for-windows/setup-git-for-windows-sdk@v1
- name: test
shell: bash
- run: ci/run-test-slice.sh ${{matrix.nr}} 10
+ run: . /etc/profile && ci/run-test-slice.sh ${{matrix.nr}} 10
- name: ci/print-test-failures.sh
if: failure()
shell: bash
@@ -198,8 +198,7 @@ jobs:
shell: bash
env:
NO_SVN_TESTS: 1
- GIT_TEST_SKIP_REBASE_P: 1
- run: ci/run-test-slice.sh ${{matrix.nr}} 10
+ run: . /etc/profile && ci/run-test-slice.sh ${{matrix.nr}} 10
- name: ci/print-test-failures.sh
if: failure()
shell: bash
@@ -232,6 +231,9 @@ jobs:
- jobname: linux-gcc-default
cc: gcc
pool: ubuntu-latest
+ - jobname: linux-leaks
+ cc: gcc
+ pool: ubuntu-latest
env:
CC: ${{matrix.vector.cc}}
jobname: ${{matrix.vector.jobname}}
diff --git a/.gitignore b/.gitignore
index 311841f..054249b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -125,7 +125,6 @@
/git-range-diff
/git-read-tree
/git-rebase
-/git-rebase--preserve-merges
/git-receive-pack
/git-reflog
/git-remote
@@ -190,6 +189,7 @@
/gitweb/static/gitweb.min.*
/config-list.h
/command-list.h
+/hook-list.h
*.tar.gz
*.dsc
*.deb
@@ -224,6 +224,7 @@
*.lib
*.res
*.sln
+*.sp
*.suo
*.ncb
*.vcproj
diff --git a/Documentation/.gitignore b/Documentation/.gitignore
index 9022d48..1c3771e 100644
--- a/Documentation/.gitignore
+++ b/Documentation/.gitignore
@@ -14,4 +14,5 @@ manpage-base-url.xsl
SubmittingPatches.txt
tmp-doc-diff/
GIT-ASCIIDOCFLAGS
+/.build/
/GIT-EXCLUDED-PROGRAMS
diff --git a/Documentation/Makefile b/Documentation/Makefile
index f5605b7..ed656db 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -90,6 +90,7 @@ SP_ARTICLES += $(API_DOCS)
TECH_DOCS += MyFirstContribution
TECH_DOCS += MyFirstObjectWalk
TECH_DOCS += SubmittingPatches
+TECH_DOCS += technical/bundle-format
TECH_DOCS += technical/hash-function-transition
TECH_DOCS += technical/http-protocol
TECH_DOCS += technical/index-format
@@ -225,6 +226,7 @@ endif
ifneq ($(findstring $(MAKEFLAGS),s),s)
ifndef V
+ QUIET = @
QUIET_ASCIIDOC = @echo ' ' ASCIIDOC $@;
QUIET_XMLTO = @echo ' ' XMLTO $@;
QUIET_DB2TEXI = @echo ' ' DB2TEXI $@;
@@ -232,11 +234,15 @@ ifndef V
QUIET_DBLATEX = @echo ' ' DBLATEX $@;
QUIET_XSLTPROC = @echo ' ' XSLTPROC $@;
QUIET_GEN = @echo ' ' GEN $@;
- QUIET_LINT = @echo ' ' LINT $@;
QUIET_STDERR = 2> /dev/null
QUIET_SUBDIR0 = +@subdir=
QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
$(MAKE) $(PRINT_DIR) -C $$subdir
+
+ QUIET_LINT_GITLINK = @echo ' ' LINT GITLINK $<;
+ QUIET_LINT_MANSEC = @echo ' ' LINT MAN SEC $<;
+ QUIET_LINT_MANEND = @echo ' ' LINT MAN END $<;
+
export V
endif
endif
@@ -284,7 +290,7 @@ install-html: html
../GIT-VERSION-FILE: FORCE
$(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) GIT-VERSION-FILE
-ifneq ($(MAKECMDGOALS),clean)
+ifneq ($(filter-out lint-docs clean,$(MAKECMDGOALS)),)
-include ../GIT-VERSION-FILE
endif
@@ -343,6 +349,7 @@ GIT-ASCIIDOCFLAGS: FORCE
fi
clean:
+ $(RM) -rf .build/
$(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7
$(RM) *.texi *.texi+ *.texi++ git.info gitman.info
$(RM) *.pdf
@@ -456,14 +463,61 @@ quick-install-html: require-htmlrepo
print-man1:
@for i in $(MAN1_TXT); do echo $$i; done
-lint-docs::
- $(QUIET_LINT)$(PERL_PATH) lint-gitlink.perl \
+## Lint: Common
+.build:
+ $(QUIET)mkdir $@
+.build/lint-docs: | .build
+ $(QUIET)mkdir $@
+
+## Lint: gitlink
+.build/lint-docs/gitlink: | .build/lint-docs
+ $(QUIET)mkdir $@
+.build/lint-docs/gitlink/howto: | .build/lint-docs/gitlink
+ $(QUIET)mkdir $@
+.build/lint-docs/gitlink/config: | .build/lint-docs/gitlink
+ $(QUIET)mkdir $@
+LINT_DOCS_GITLINK = $(patsubst %.txt,.build/lint-docs/gitlink/%.ok,$(HOWTO_TXT) $(DOC_DEP_TXT))
+$(LINT_DOCS_GITLINK): | .build/lint-docs/gitlink
+$(LINT_DOCS_GITLINK): | .build/lint-docs/gitlink/howto
+$(LINT_DOCS_GITLINK): | .build/lint-docs/gitlink/config
+$(LINT_DOCS_GITLINK): lint-gitlink.perl
+$(LINT_DOCS_GITLINK): .build/lint-docs/gitlink/%.ok: %.txt
+ $(QUIET_LINT_GITLINK)$(PERL_PATH) lint-gitlink.perl \
+ $< \
$(HOWTO_TXT) $(DOC_DEP_TXT) \
--section=1 $(MAN1_TXT) \
--section=5 $(MAN5_TXT) \
- --section=7 $(MAN7_TXT); \
- $(PERL_PATH) lint-man-end-blurb.perl $(MAN_TXT); \
- $(PERL_PATH) lint-man-section-order.perl $(MAN_TXT);
+ --section=7 $(MAN7_TXT) >$@
+.PHONY: lint-docs-gitlink
+lint-docs-gitlink: $(LINT_DOCS_GITLINK)
+
+## Lint: man-end-blurb
+.build/lint-docs/man-end-blurb: | .build/lint-docs
+ $(QUIET)mkdir $@
+LINT_DOCS_MAN_END_BLURB = $(patsubst %.txt,.build/lint-docs/man-end-blurb/%.ok,$(MAN_TXT))
+$(LINT_DOCS_MAN_END_BLURB): | .build/lint-docs/man-end-blurb
+$(LINT_DOCS_MAN_END_BLURB): lint-man-end-blurb.perl
+$(LINT_DOCS_MAN_END_BLURB): .build/lint-docs/man-end-blurb/%.ok: %.txt
+ $(QUIET_LINT_MANEND)$(PERL_PATH) lint-man-end-blurb.perl $< >$@
+.PHONY: lint-docs-man-end-blurb
+lint-docs-man-end-blurb: $(LINT_DOCS_MAN_END_BLURB)
+
+## Lint: man-section-order
+.build/lint-docs/man-section-order: | .build/lint-docs
+ $(QUIET)mkdir $@
+LINT_DOCS_MAN_SECTION_ORDER = $(patsubst %.txt,.build/lint-docs/man-section-order/%.ok,$(MAN_TXT))
+$(LINT_DOCS_MAN_SECTION_ORDER): | .build/lint-docs/man-section-order
+$(LINT_DOCS_MAN_SECTION_ORDER): lint-man-section-order.perl
+$(LINT_DOCS_MAN_SECTION_ORDER): .build/lint-docs/man-section-order/%.ok: %.txt
+ $(QUIET_LINT_MANSEC)$(PERL_PATH) lint-man-section-order.perl $< >$@
+.PHONY: lint-docs-man-section-order
+lint-docs-man-section-order: $(LINT_DOCS_MAN_SECTION_ORDER)
+
+## Lint: list of targets above
+.PHONY: lint-docs
+lint-docs: lint-docs-gitlink
+lint-docs: lint-docs-man-end-blurb
+lint-docs: lint-docs-man-section-order
ifeq ($(wildcard po/Makefile),po/Makefile)
doc-l10n install-l10n::
diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
index 015cf24..63a2ef5 100644
--- a/Documentation/MyFirstContribution.txt
+++ b/Documentation/MyFirstContribution.txt
@@ -905,19 +905,34 @@ Sending emails with Git is a two-part process; before you can prepare the emails
themselves, you'll need to prepare the patches. Luckily, this is pretty simple:
----
-$ git format-patch --cover-letter -o psuh/ master..psuh
-----
-
-The `--cover-letter` parameter tells `format-patch` to create a cover letter
-template for you. You will need to fill in the template before you're ready
-to send - but for now, the template will be next to your other patches.
-
-The `-o psuh/` parameter tells `format-patch` to place the patch files into a
-directory. This is useful because `git send-email` can take a directory and
-send out all the patches from there.
-
-`master..psuh` tells `format-patch` to generate patches for the difference
-between `master` and `psuh`. It will make one patch file per commit. After you
+$ git format-patch --cover-letter -o psuh/ --base=auto psuh@{u}..psuh
+----
+
+ . The `--cover-letter` option tells `format-patch` to create a
+ cover letter template for you. You will need to fill in the
+ template before you're ready to send - but for now, the template
+ will be next to your other patches.
+
+ . The `-o psuh/` option tells `format-patch` to place the patch
+ files into a directory. This is useful because `git send-email`
+ can take a directory and send out all the patches from there.
+
+ . The `--base=auto` option tells the command to record the "base
+ commit", on which the recipient is expected to apply the patch
+ series. The `auto` value will cause `format-patch` to compute
+ the base commit automatically, which is the merge base of tip
+ commit of the remote-tracking branch and the specified revision
+ range.
+
+ . The `psuh@{u}..psuh` option tells `format-patch` to generate
+ patches for the commits you created on the `psuh` branch since it
+ forked from its upstream (which is `origin/master` if you
+ followed the example in the "Set up your workspace" section). If
+ you are already on the `psuh` branch, you can just say `@{u}`,
+ which means "commits on the current branch since it forked from
+ its upstream", which is the same thing.
+
+The command will make one patch file per commit. After you
run, you can go have a look at each of the patches with your favorite text
editor and make sure everything looks alright; however, it's not recommended to
make code fixups via the patch file. It's a better idea to make the change the
@@ -1029,22 +1044,42 @@ kidding - be patient!)
[[v2-git-send-email]]
=== Sending v2
-Skip ahead to <<reviewing,Responding to Reviews>> for information on how to
-handle comments from reviewers. Continue this section when your topic branch is
-shaped the way you want it to look for your patchset v2.
+This section will focus on how to send a v2 of your patchset. To learn what
+should go into v2, skip ahead to <<reviewing,Responding to Reviews>> for
+information on how to handle comments from reviewers.
-When you're ready with the next iteration of your patch, the process is fairly
-similar.
+We'll reuse our `psuh` topic branch for v2. Before we make any changes, we'll
+mark the tip of our v1 branch for easy reference:
-First, generate your v2 patches again:
+----
+$ git checkout psuh
+$ git branch psuh-v1
+----
+
+Refine your patch series by using `git rebase -i` to adjust commits based upon
+reviewer comments. Once the patch series is ready for submission, generate your
+patches again, but with some new flags:
----
-$ git format-patch -v2 --cover-letter -o psuh/ master..psuh
+$ git format-patch -v2 --cover-letter -o psuh/ --range-diff master..psuh-v1 master..
----
-This will add your v2 patches, all named like `v2-000n-my-commit-subject.patch`,
-to the `psuh/` directory. You may notice that they are sitting alongside the v1
-patches; that's fine, but be careful when you are ready to send them.
+The `--range-diff master..psuh-v1` parameter tells `format-patch` to include a
+range-diff between `psuh-v1` and `psuh` in the cover letter (see
+linkgit:git-range-diff[1]). This helps tell reviewers about the differences
+between your v1 and v2 patches.
+
+The `-v2` parameter tells `format-patch` to output your patches
+as version "2". For instance, you may notice that your v2 patches are
+all named like `v2-000n-my-commit-subject.patch`. `-v2` will also format
+your patches by prefixing them with "[PATCH v2]" instead of "[PATCH]",
+and your range-diff will be prefaced with "Range-diff against v1".
+
+Afer you run this command, `format-patch` will output the patches to the `psuh/`
+directory, alongside the v1 patches. Using a single directory makes it easy to
+refer to the old v1 patches while proofreading the v2 patches, but you will need
+to be careful to send out only the v2 patches. We will use a pattern like
+"psuh/v2-*.patch" (not "psuh/*.patch", which would match v1 and v2 patches).
Edit your cover letter again. Now is a good time to mention what's different
between your last version and now, if it's something significant. You do not
@@ -1082,7 +1117,7 @@ to the command:
----
$ git send-email --to=target@example.com
--in-reply-to="<foo.12345.author@example.com>"
- psuh/v2*
+ psuh/v2-*.patch
----
[[single-patch]]
diff --git a/Documentation/RelNotes/2.33.1.txt b/Documentation/RelNotes/2.33.1.txt
new file mode 100644
index 0000000..b71738e
--- /dev/null
+++ b/Documentation/RelNotes/2.33.1.txt
@@ -0,0 +1,138 @@
+Git 2.33.1 Release Notes
+========================
+
+This primarily is to backport various fixes accumulated during the
+development towards Git 2.34, the next feature release.
+
+
+Fixes since v2.33
+-----------------
+
+ * The unicode character width table (used for output alignment) has
+ been updated.
+
+ * Input validation of "git pack-objects --stdin-packs" has been
+ corrected.
+
+ * Bugfix for common ancestor negotiation recently introduced in "git
+ push" codepath.
+
+ * "git pull" had various corner cases that were not well thought out
+ around its --rebase backend, e.g. "git pull --ff-only" did not stop
+ but went ahead and rebased when the history on other side is not a
+ descendant of our history. The series tries to fix them up.
+
+ * "git apply" miscounted the bytes and failed to read to the end of
+ binary hunks.
+
+ * "git range-diff" code clean-up.
+
+ * "git commit --fixup" now works with "--edit" again, after it was
+ broken in v2.32.
+
+ * Use upload-artifacts v1 (instead of v2) for 32-bit linux, as the
+ new version has a blocker bug for that architecture.
+
+ * Checking out all the paths from HEAD during the last conflicted
+ step in "git rebase" and continuing would cause the step to be
+ skipped (which is expected), but leaves MERGE_MSG file behind in
+ $GIT_DIR and confuses the next "git commit", which has been
+ corrected.
+
+ * Various bugs in "git rebase -r" have been fixed.
+
+ * mmap() imitation used to call xmalloc() that dies upon malloc()
+ failure, which has been corrected to just return an error to the
+ caller to be handled.
+
+ * "git diff --relative" segfaulted and/or produced incorrect result
+ when there are unmerged paths.
+
+ * The delayed checkout code path in "git checkout" etc. were chatty
+ even when --quiet and/or --no-progress options were given.
+
+ * "git branch -D <branch>" used to refuse to remove a broken branch
+ ref that points at a missing commit, which has been corrected.
+
+ * Build update for Apple clang.
+
+ * The parser for the "--nl" option of "git column" has been
+ corrected.
+
+ * "git upload-pack" which runs on the other side of "git fetch"
+ forgot to take the ref namespaces into account when handling
+ want-ref requests.
+
+ * The sparse-index support can corrupt the index structure by storing
+ a stale and/or uninitialized data, which has been corrected.
+
+ * Buggy tests could damage repositories outside the throw-away test
+ area we created. We now by default export GIT_CEILING_DIRECTORIES
+ to limit the damage from such a stray test.
+
+ * Even when running "git send-email" without its own threaded
+ discussion support, a threading related header in one message is
+ carried over to the subsequent message to result in an unwanted
+ threading, which has been corrected.
+
+ * The output from "git fast-export", when its anonymization feature
+ is in use, showed an annotated tag incorrectly.
+
+ * Recent "diff -m" changes broke "gitk", which has been corrected.
+
+ * "git maintenance" scheduler fix for macOS.
+
+ * A pathname in an advice message has been made cut-and-paste ready.
+
+ * The "git apply -3" code path learned not to bother the lower level
+ merge machinery when the three-way merge can be trivially resolved
+ without the content level merge.
+
+ * The code that optionally creates the *.rev reverse index file has
+ been optimized to avoid needless computation when it is not writing
+ the file out.
+
+ * "git range-diff -I... <range> <range>" segfaulted, which has been
+ corrected.
+
+ * The order in which various files that make up a single (conceptual)
+ packfile has been reevaluated and straightened up. This matters in
+ correctness, as an incomplete set of files must not be shown to a
+ running Git.
+
+ * The "mode" word is useless in a call to open(2) that does not
+ create a new file. Such a call in the files backend of the ref
+ subsystem has been cleaned up.
+
+ * "git update-ref --stdin" failed to flush its output as needed,
+ which potentially led the conversation to a deadlock.
+
+ * When "git am --abort" fails to abort correctly, it still exited
+ with exit status of 0, which has been corrected.
+
+ * Correct nr and alloc members of strvec struct to be of type size_t.
+
+ * "git stash", where the tentative change involves changing a
+ directory to a file (or vice versa), was confused, which has been
+ corrected.
+
+ * "git clone" from a repository whose HEAD is unborn into a bare
+ repository didn't follow the branch name the other side used, which
+ is corrected.
+
+ * "git cvsserver" had a long-standing bug in its authentication code,
+ which has finally been corrected (it is unclear and is a separate
+ question if anybody is seriously using it, though).
+
+ * "git difftool --dir-diff" mishandled symbolic links.
+
+ * Sensitive data in the HTTP trace were supposed to be redacted, but
+ we failed to do so in HTTP/2 requests.
+
+ * "make clean" has been updated to remove leftover .depend/
+ directories, even when it is not told to use them to compute header
+ dependencies.
+
+ * Protocol v0 clients can get stuck parsing a malformed feature line.
+
+Also contains various documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.34.0.txt b/Documentation/RelNotes/2.34.0.txt
index d21a63e..75d4fdf 100644
--- a/Documentation/RelNotes/2.34.0.txt
+++ b/Documentation/RelNotes/2.34.0.txt
@@ -4,13 +4,19 @@ Git 2.34 Release Notes
Updates since Git 2.33
----------------------
+Backward compatibility notes
+
+ * The "--preserve-merges" option of "git rebase" has been removed.
+
+
UI, Workflows & Features
* Pathname expansion (like "~username/") learned a way to specify a
location relative to Git installation (e.g. its $sharedir which is
$(prefix)/share), with "%(prefix)".
- * Use `ort` instead of `recursive` as the default merge strategy.
+ * The `ort` strategy is used instead of `recursive` as the default
+ merge strategy.
* The userdiff pattern for "java" language has been updated.
@@ -34,6 +40,53 @@ UI, Workflows & Features
interactive when the help.autocorrect configuration variable is set
to 'prompt'.
+ * "git maintenance" scheduler learned to use systemd timers as a
+ possible backend.
+
+ * "git diff --submodule=diff" showed failure from run_command() when
+ trying to run diff inside a submodule, when the user manually
+ removes the submodule directory.
+
+ * "git bundle unbundle" learned to show progress display.
+
+ * In cone mode, the sparse-index code path learned to remove ignored
+ files (like build artifacts) outside the sparse cone, allowing the
+ entire directory outside the sparse cone to be removed, which is
+ especially useful when the sparse patterns change.
+
+ * Taking advantage of the CGI interface, http-backend has been
+ updated to enable protocol v2 automatically when the other side
+ asks for it.
+
+ * The credential-cache helper has been adjusted to Windows.
+
+ * The error in "git help no-such-git-command" is handled better.
+
+ * The unicode character width table (used for output alignment) has
+ been updated.
+
+ * The ref iteration code used to optionally allow dangling refs to be
+ shown, which has been tightened up.
+
+ * "git add", "git mv", and "git rm" have been adjusted to avoid
+ updating paths outside of the sparse-checkout definition unless
+ the user specifies a "--sparse" option.
+
+ * "git repack" has been taught to generate multi-pack reachability
+ bitmaps.
+
+ * "git fsck" has been taught to report mismatch between expected and
+ actual types of an object better.
+
+ * In addition to GnuPG, ssh public crypto can be used for object and
+ push-cert signing. Note that this feature cannot be used with
+ ssh-keygen from OpenSSH 8.7, whose support for it is broken. Avoid
+ using it unless you update to OpenSSH 8.8.
+
+ * "git log --grep=string --author=name" learns to highlight hits just
+ like "git grep string" does.
+
+
Performance, Internal Implementation, Development Support etc.
@@ -74,123 +127,312 @@ Performance, Internal Implementation, Development Support etc.
* Callers from older advice_config[] based API has been updated to
use the newer advice_if_enabled() and advice_enabled() API.
+ * Teach "test_pause" and "debug" helpers to allow using the HOME and
+ TERM environment variables the user usually uses.
+
+ * "make INSTALL_STRIP=-s install" allows the installation step to use
+ "install -s" to strip the binaries as they get installed.
+
+ * Code that handles large number of refs in the "git fetch" code
+ path has been optimized.
+
+ * The reachability bitmap file used to be generated only for a single
+ pack, but now we've learned to generate bitmaps for history that
+ span across multiple packfiles.
+
+ * The code to make "git grep" recurse into submodules has been
+ updated to migrate away from the "add submodule's object store as
+ an alternate object store" mechanism (which is suboptimal).
+
+ * The tracing of process ancestry information has been enhanced.
+
+ * Reduce number of write(2) system calls while sending the
+ ref advertisement.
+
+ * Update the build procedure to use the "-pedantic" build when
+ DEVELOPER makefile macro is in effect.
+
+ * Large part of "git submodule add" gets rewritten in C.
+
+ * The run-command API has been updated so that the callers can easily
+ ask the file descriptors open for packfiles to be closed immediately
+ before spawning commands that may trigger auto-gc.
+
+ * An oddball OPTION_ARGUMENT feature has been removed from the
+ parse-options API.
+
+ * The mergesort implementation used to sort linked list has been
+ optimized.
+
+ * Remove external declaration of functions that no longer exist.
+
+ * "git multi-pack-index write --bitmap" learns to propagate the
+ hashcache from original bitmap to resulting bitmap.
+
+ * CI learns to run the leak sanitizer builds.
+
+ * "git grep --recurse-submodules" takes trees and blobs from the
+ submodule repository, but the textconv settings when processing a
+ blob from the submodule is not taken from the submodule repository.
+ A test is added to demonstrate the issue, without fixing it.
+
+ * Teach "git help -c" into helping the command line completion of
+ configuration variables.
+
+ * When "git cmd -h" shows more than one line of usage text (e.g.
+ the cmd subcommand may take sub-sub-command), parse-options API
+ learned to align these lines, even across i18n/l10n.
+
+ * Prevent "make sparse" from running for the source files that
+ haven't been modified.
+
+ * The code path to write a new version of .midx multi-pack index files
+ has learned to release the mmaped memory holding the current
+ version of .midx before removing them from the disk, as some
+ platforms do not allow removal of a file that still has mapping.
+
+ * A new feature has been added to abort early in the test framework.
+
Fixes since v2.33
-----------------
* Input validation of "git pack-objects --stdin-packs" has been
corrected.
- (merge 561fa03529 ab/pack-stdin-packs-fix later to maint).
* Bugfix for common ancestor negotiation recently introduced in "git
push" code path.
- (merge 82823118b9 jt/push-negotiation-fixes later to maint).
* "git pull" had various corner cases that were not well thought out
around its --rebase backend, e.g. "git pull --ff-only" did not stop
but went ahead and rebased when the history on other side is not a
descendant of our history. The series tries to fix them up.
- (merge 6f843a3355 en/pull-conflicting-options later to maint).
* "git apply" miscounted the bytes and failed to read to the end of
binary hunks.
- (merge 46d723ce57 jk/apply-binary-hunk-parsing-fix later to maint).
* "git range-diff" code clean-up.
- (merge c4d5907324 jk/range-diff-fixes later to maint).
* "git commit --fixup" now works with "--edit" again, after it was
broken in v2.32.
- (merge 8ef6aad664 jk/commit-edit-fixup-fix later to maint).
* Use upload-artifacts v1 (instead of v2) for 32-bit linux, as the
new version has a blocker bug for that architecture.
- (merge 3cf9bb36bf cb/ci-use-upload-artifacts-v1 later to maint).
* Checking out all the paths from HEAD during the last conflicted
step in "git rebase" and continuing would cause the step to be
skipped (which is expected), but leaves MERGE_MSG file behind in
$GIT_DIR and confuses the next "git commit", which has been
corrected.
- (merge e5ee33e855 pw/rebase-skip-final-fix later to maint).
* Various bugs in "git rebase -r" have been fixed.
- (merge f2563c9ef3 pw/rebase-r-fixes later to maint).
* mmap() imitation used to call xmalloc() that dies upon malloc()
failure, which has been corrected to just return an error to the
caller to be handled.
- (merge 95b4ff3931 rs/git-mmap-uses-malloc later to maint).
* "git diff --relative" segfaulted and/or produced incorrect result
when there are unmerged paths.
- (merge 8174627b3d dd/diff-files-unmerged-fix later to maint).
* The delayed checkout code path in "git checkout" etc. were chatty
even when --quiet and/or --no-progress options were given.
- (merge 7a132c628e mt/quiet-with-delayed-checkout later to maint).
* "git branch -D <branch>" used to refuse to remove a broken branch
ref that points at a missing commit, which has been corrected.
- (merge 597a977489 rs/branch-allow-deleting-dangling later to maint).
* Build update for Apple clang.
- (merge f32c5d3716 cb/makefile-apple-clang later to maint).
* The parser for the "--nl" option of "git column" has been
corrected.
- (merge c93ca46cf5 sg/column-nl later to maint).
* "git upload-pack" which runs on the other side of "git fetch"
forgot to take the ref namespaces into account when handling
want-ref requests.
- (merge 53a66ec37c ka/want-ref-in-namespace later to maint).
* The sparse-index support can corrupt the index structure by storing
a stale and/or uninitialized data, which has been corrected.
- (merge d9e9b44d7a jh/sparse-index-resize-fix later to maint).
* Buggy tests could damage repositories outside the throw-away test
area we created. We now by default export GIT_CEILING_DIRECTORIES
to limit the damage from such a stray test.
- (merge 614c3d8f2e sg/set-ceiling-during-tests later to maint).
* Even when running "git send-email" without its own threaded
discussion support, a threading related header in one message is
carried over to the subsequent message to result in an unwanted
threading, which has been corrected.
- (merge e082113484 mh/send-email-reset-in-reply-to later to maint).
* The output from "git fast-export", when its anonymization feature
is in use, showed an annotated tag incorrectly.
- (merge 2f040a9671 tk/fast-export-anonymized-tag-fix later to maint).
- * Doc update plus improved error reporting.
- (merge 1e93770888 jk/log-warn-on-bogus-encoding later to maint).
+ * Recent "diff -m" changes broke "gitk", which has been corrected.
+
+ * The "git apply -3" code path learned not to bother the lower level
+ merge machinery when the three-way merge can be trivially resolved
+ without the content level merge. This fixes a regression caused by
+ recent "-3way first and fall back to direct application" change.
+
+ * The code that optionally creates the *.rev reverse index file has
+ been optimized to avoid needless computation when it is not writing
+ the file out.
+
+ * "git range-diff -I... <range> <range>" segfaulted, which has been
+ corrected.
+
+ * The order in which various files that make up a single (conceptual)
+ packfile has been reevaluated and straightened up. This matters in
+ correctness, as an incomplete set of files must not be shown to a
+ running Git.
+
+ * The "mode" word is useless in a call to open(2) that does not
+ create a new file. Such a call in the files backend of the ref
+ subsystem has been cleaned up.
+
+ * "git update-ref --stdin" failed to flush its output as needed,
+ which potentially led the conversation to a deadlock.
+
+ * When "git am --abort" fails to abort correctly, it still exited
+ with exit status of 0, which has been corrected.
+
+ * Correct nr and alloc members of strvec struct to be of type size_t.
+
+ * "git stash", where the tentative change involves changing a
+ directory to a file (or vice versa), was confused, which has been
+ corrected.
+
+ * "git clone" from a repository whose HEAD is unborn into a bare
+ repository didn't follow the branch name the other side used, which
+ is corrected.
+
+ * "git cvsserver" had a long-standing bug in its authentication code,
+ which has finally been corrected (it is unclear and is a separate
+ question if anybody is seriously using it, though).
+
+ * "git difftool --dir-diff" mishandled symbolic links.
+
+ * Sensitive data in the HTTP trace were supposed to be redacted, but
+ we failed to do so in HTTP/2 requests.
+
+ * "make clean" has been updated to remove leftover .depend/
+ directories, even when it is not told to use them to compute header
+ dependencies.
+
+ * Protocol v0 clients can get stuck parsing a malformed feature line.
+
+ * A few kinds of changes "git status" can show were not documented.
+ (merge d2a534c515 ja/doc-status-types-and-copies later to maint).
+
+ * The mergesort implementation used to sort linked list has been
+ optimized.
+ (merge c90cfc225b rs/mergesort later to maint).
+
+ * An editor session launched during a Git operation (e.g. during 'git
+ commit') can leave the terminal in a funny state. The code path
+ has updated to save the terminal state before, and restore it
+ after, it spawns an editor.
+ (merge 3d411afabc cm/save-restore-terminal later to maint).
+
+ * "git cat-file --batch" with the "--batch-all-objects" option is
+ supposed to iterate over all the objects found in a repository, but
+ it used to translate these object names using the replace mechanism,
+ which defeats the point of enumerating all objects in the repository.
+ This has been corrected.
+ (merge bf972896d7 jk/cat-file-batch-all-wo-replace later to maint).
+
+ * Recent sparse-index work broke safety against attempts to add paths
+ with trailing slashes to the index, which has been corrected.
+ (merge c8ad9d04c6 rs/make-verify-path-really-verify-again later to maint).
+
+ * The "--color-lines" and "--color-by-age" options of "git blame"
+ have been missing, which are now documented.
+ (merge 8c32856133 bs/doc-blame-color-lines later to maint).
+
+ * The PATH used in CI job may be too wide and let incompatible dlls
+ to be grabbed, which can cause the build&test to fail. Tighten it.
+ (merge 7491ef6198 js/windows-ci-path-fix later to maint).
+
+ * Avoid performance measurements from getting ruined by gc and other
+ housekeeping pauses interfering in the middle.
+ (merge be79131a53 rs/disable-gc-during-perf-tests later to maint).
+
+ * Stop "git add --dry-run" from creating new blob and tree objects.
+ (merge e578d0311d rs/add-dry-run-without-objects later to maint).
+
+ * "git commit" gave duplicated error message when the object store
+ was unwritable, which has been corrected.
+ (merge 4ef91a2d79 ab/fix-commit-error-message-upon-unwritable-object-store later to maint).
+
+ * Recent sparse-index addition, namely any use of index_name_pos(),
+ can expand sparse index entries and breaks any code that walks
+ cache-tree or existing index entries. One such instance of such a
+ breakage has been corrected.
+
+ * The xxdiff difftool backend can exit with status 128, which the
+ difftool-helper that launches the backend takes as a significant
+ failure, when it is not significant at all. Work it around.
+ (merge 571f4348dd da/mergetools-special-case-xxdiff-exit-128 later to maint).
+
+ * Improve test framework around unwritable directories.
+ (merge 5d22e18965 ab/test-cleanly-recreate-trash-directory later to maint).
+
+ * "git push" client talking to an HTTP server did not diagnose the
+ lack of the final status report from the other side correctly,
+ which has been corrected.
+ (merge c5c3486f38 jk/http-push-status-fix later to maint).
+
+ * Update "git archive" documentation and give explicit mention on the
+ compression level for both zip and tar.gz format.
+ (merge c4b208c309 bs/archive-doc-compression-level later to maint).
+
+ * Drop "git sparse-checkout" from the list of common commands.
+ (merge 6a9a50a8af sg/sparse-index-not-that-common-a-command later to maint).
+
+ * "git branch -c/-m new old" was not described to copy config, which
+ has been corrected.
+ (merge 8252ec300e jc/branch-copy-doc later to maint).
+
+ * Squelch over-eager warning message added during this cycle.
+
+ * Fix long-standing shell syntax error in the completion script.
+ (merge 46b0585286 re/completion-fix-test-equality later to maint).
+
+ * Teach "git commit-graph" command not to allow using replace objects
+ at all, as we do not use the commit-graph at runtime when we see
+ object replacement.
+ (merge 095d112f8c ab/ignore-replace-while-working-on-commit-graph later to maint).
+
+ * "git pull --no-verify" did not affect the underlying "git merge".
+ (merge 47bfdfb3fd ar/fix-git-pull-no-verify later to maint).
+
+ * One CI task based on Fedora image noticed a not-quite-kosher
+ construct recently, which has been corrected.
+
+ * "git pull --ff-only" and "git pull --rebase --ff-only" should make
+ it a no-op to attempt pulling from a remote that is behind us, but
+ instead the command errored out by saying it was impossible to
+ fast-forward, which may technically be true, but not a useful thing
+ to diagnose as an error. This has been corrected.
+ (merge 361cb52383 jc/fix-pull-ff-only-when-already-up-to-date later to maint).
+
+ * The way Cygwin emulates a unix-domain socket, on top of which the
+ simple-ipc mechanism is implemented, can race with the program on
+ the other side that wants to use the socket, and briefly make it
+ appear as a regular file before lstat(2) starts reporting it as a
+ socket. We now have a workaround on the side that connects to a
+ unix domain socket.
* Other code cleanup, docfix, build fix, etc.
- (merge 1d9c8daef8 ab/bundle-doc later to maint).
- (merge 81483fe613 en/merge-strategy-docs later to maint).
- (merge 626beebdf8 js/log-protocol-version later to maint).
- (merge 00e302da76 cb/builtin-merge-format-string-fix later to maint).
- (merge ad51ae4dc0 cb/ci-freebsd-update later to maint).
- (merge be6444d1ca fc/completion-updates later to maint).
- (merge ff7b83f562 ti/tcsh-completion-regression-fix later to maint).
- (merge 325b06deda sg/make-fix-ar-invocation later to maint).
- (merge bd72824c60 me/t5582-cleanup later to maint).
- (merge f6a5af0f62 ga/send-email-sendmail-cmd later to maint).
- (merge f58c7468cd ab/ls-remote-packet-trace later to maint).
- (merge 0160f7e725 ab/rebase-fatal-fatal-fix later to maint).
- (merge a16eb6b1ff js/maintenance-launchctl-fix later to maint).
- (merge c21b2511c2 jk/t5323-no-pack-test-fix later to maint).
- (merge 5146c2f148 mh/credential-leakfix later to maint).
- (merge 1549577338 dd/t6300-wo-gpg-fix later to maint).
- (merge 66e905b7dd rs/xopen-reports-open-failures later to maint).
- (merge 469888e6a5 es/walken-tutorial-fix later to maint).
- (merge 88682b016d ba/object-info later to maint).
- (merge b45c172e51 ab/gc-log-rephrase later to maint).
- (merge ccdd5d1eb1 ab/mailmap-leakfix later to maint).
- (merge 6540b71614 cb/remote-ndebug-fix later to maint).
- (merge e4f8d27585 rs/show-branch-simplify later to maint).
- (merge e124ecf7f7 rs/archive-use-object-id later to maint).
- (merge cebead1ebf cb/ci-build-pedantic later to maint).
+ (merge f188160be9 ab/bundle-remove-verbose-option later to maint).
+ (merge 8c6b4332b4 rs/close-pack-leakfix later to maint).
+ (merge 51b04c05b7 bs/difftool-msg-tweak later to maint).
+ (merge dd20e4a6db ab/make-compdb-fix later to maint).
+ (merge 6ffb990dc4 os/status-docfix later to maint).
+ (merge 100c2da2d3 rs/p3400-lose-tac later to maint).
+ (merge 76f3b69896 tb/aggregate-ignore-leading-whitespaces later to maint).
+ (merge 6e4fd8bfcd tz/doc-link-to-bundle-format-fix later to maint).
+ (merge f6c013dfa1 jc/doc-commit-header-continuation-line later to maint).
+ (merge ec9a37d69b ab/pkt-line-cleanup later to maint).
+ (merge 8650c6298c ab/fix-make-lint-docs later to maint).
+ (merge 1c720357ce ab/test-lib-diff-cleanup later to maint).
+ (merge 6b615dbece ks/submodule-add-message-fix later to maint).
+ (merge 203eb8381a jc/doc-format-patch-clarify-auto-base later to maint).
+ (merge 559664c792 ab/test-lib later to maint).
diff --git a/Documentation/RelNotes/2.34.1.txt b/Documentation/RelNotes/2.34.1.txt
new file mode 100644
index 0000000..ad404e9
--- /dev/null
+++ b/Documentation/RelNotes/2.34.1.txt
@@ -0,0 +1,23 @@
+Git v2.34.1 Release Notes
+=========================
+
+This release is primarily to fix a handful of regressions in Git 2.34.
+
+Fixes since v2.34
+-----------------
+
+ * "git grep" looking in a blob that has non-UTF8 payload was
+ completely broken when linked with certain versions of PCREv2
+ library in the latest release.
+
+ * "git pull" with any strategy when the other side is behind us
+ should succeed as it is a no-op, but doesn't.
+
+ * An earlier change in 2.34.0 caused JGit application (that abused
+ GIT_EDITOR mechanism when invoking "git config") to get stuck with
+ a SIGTTOU signal; it has been reverted.
+
+ * An earlier change that broke .gitignore matching has been reverted.
+
+ * SubmittingPatches document gained a syntactically incorrect mark-up,
+ which has been corrected.
diff --git a/Documentation/RelNotes/2.35.0.txt b/Documentation/RelNotes/2.35.0.txt
new file mode 100644
index 0000000..120fac5
--- /dev/null
+++ b/Documentation/RelNotes/2.35.0.txt
@@ -0,0 +1,65 @@
+Git 2.35 Release Notes
+======================
+
+Updates since Git 2.34
+----------------------
+
+Backward compatibility warts
+
+ * "_" is now treated as any other URL-valid characters in an URL when
+ matching the per-URL configuration variable names.
+
+
+UI, Workflows & Features
+
+ * "git status --porcelain=v2" now show the number of stash entries
+ with --show-stash like the normal output does.
+
+ * "git stash" learned the "--staged" option to stash away what has
+ been added to the index (and nothing else).
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * The use of errno as a means to carry the nature of error in the ref
+ API implementation has been reworked and reduced.
+
+ * Teach and encourage first-time contributors to this project to
+ state the base commit when they submit their topic.
+
+ * The command line complation for "git send-email" options have been
+ tweaked to make it easier to keep it in sync with the command itself.
+
+
+Fixes since v2.34
+-----------------
+
+ * "git grep" looking in a blob that has non-UTF8 payload was
+ completely broken when linked with certain versions of PCREv2
+ library in the latest release.
+
+ * Other code cleanup, docfix, build fix, etc.
+
+ * "git pull" with any strategy when the other side is behind us
+ should succeed as it is a no-op, but doesn't.
+
+ * An earlier change in 2.34.0 caused JGit application (that abused
+ GIT_EDITOR mechanism when invoking "git config") to get stuck with
+ a SIGTTOU signal; it has been reverted.
+
+ * An earlier change that broke .gitignore matching has been reverted.
+
+ * Things like "git -c branch.sort=bogus branch new HEAD", i.e. the
+ operation modes of the "git branch" command that do not need the
+ sort key information, no longer errors out by seeing a bogus sort
+ key.
+ (merge 98e7ab6d42 jc/fix-ref-sorting-parse later to maint).
+
+ * The compatibility implementation for unsetenv(3) were written to
+ mimic ancient, non-POSIX, variant seen in an old glibc; it has been
+ changed to return an integer to match the more modern era.
+ (merge a38989bd5b jc/unsetenv-returns-an-int later to maint).
+
+ * The clean/smudge conversion code path has been prepared to better
+ work on platforms where ulong is narrower than size_t.
+ (merge 596b5e77c9 mc/clean-smudge-with-llp64 later to maint).
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index e409022..11e0305 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -448,7 +448,7 @@ their trees themselves.
entitled "What's cooking in git.git" and "What's in git.git" giving
the status of various proposed changes.
-== GitHub CI[[GHCI]]]
+== GitHub CI[[GHCI]]
With an account at GitHub, you can use GitHub CI to test your changes
on Linux, Mac and Windows. See
@@ -463,7 +463,7 @@ Follow these steps for the initial setup:
After the initial setup, CI will run whenever you push new changes
to your fork of Git on GitHub. You can monitor the test state of all your
-branches here: https://github.com/<Your GitHub handle>/git/actions/workflows/main.yml
+branches here: `https://github.com/<Your GitHub handle>/git/actions/workflows/main.yml`
If a branch did not pass all test cases then it is marked with a red
cross. In that case you can click on the failing job and navigate to
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index 117f4cf..9a66353 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -136,5 +136,16 @@ take effect.
option. An empty file name, `""`, will clear the list of revs from
previously processed files.
+--color-lines::
+ Color line annotations in the default format differently if they come from
+ the same commit as the preceding line. This makes it easier to distinguish
+ code blocks introduced by different commits. The color defaults to cyan and
+ can be adjusted using the `color.blame.repeatedLines` config option.
+
+--color-by-age::
+ Color line annotations depending on the age of the line in the default format.
+ The `color.blame.highlightRecent` config option controls what color is used for
+ each range of age.
+
-h::
Show help message.
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 0c0e6b8..1167e88 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -304,7 +304,7 @@ path relative to Git's "runtime prefix", i.e. relative to the location
where Git itself was installed. For example, `%(prefix)/bin/` refers to
the directory in which the Git executable itself lives. If Git was
compiled without runtime prefix support, the compiled-in prefix will be
-subsituted instead. In the unlikely event that a literal path needs to
+substituted instead. In the unlikely event that a literal path needs to
be specified that should _not_ be expanded, it needs to be prefixed by
`./`, like so: `./%(prefix)/bin`.
diff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt
index cc5f324..d323d73 100644
--- a/Documentation/config/branch.txt
+++ b/Documentation/config/branch.txt
@@ -85,10 +85,6 @@ When `merges` (or just 'm'), pass the `--rebase-merges` option to 'git rebase'
so that the local merge commits are included in the rebase (see
linkgit:git-rebase[1] for details).
+
-When `preserve` (or just 'p', deprecated in favor of `merges`), also pass
-`--preserve-merges` along to 'git rebase' so that locally committed merge
-commits will not be flattened by running 'git pull'.
-+
When the value is `interactive` (or just 'i'), the rebase is run in interactive
mode.
+
diff --git a/Documentation/config/color.txt b/Documentation/config/color.txt
index e05d520..1795b2d 100644
--- a/Documentation/config/color.txt
+++ b/Documentation/config/color.txt
@@ -9,26 +9,27 @@ color.advice.hint::
Use customized color for hints.
color.blame.highlightRecent::
- This can be used to color the metadata of a blame line depending
- on age of the line.
+ Specify the line annotation color for `git blame --color-by-age`
+ depending upon the age of the line.
+
-This setting should be set to a comma-separated list of color and date settings,
-starting and ending with a color, the dates should be set from oldest to newest.
-The metadata will be colored given the colors if the line was introduced
-before the given timestamp, overwriting older timestamped colors.
+This setting should be set to a comma-separated list of color and
+date settings, starting and ending with a color, the dates should be
+set from oldest to newest. The metadata will be colored with the
+specified colors if the line was introduced before the given
+timestamp, overwriting older timestamped colors.
+
-Instead of an absolute timestamp relative timestamps work as well, e.g.
-2.weeks.ago is valid to address anything older than 2 weeks.
+Instead of an absolute timestamp relative timestamps work as well,
+e.g. `2.weeks.ago` is valid to address anything older than 2 weeks.
+
-It defaults to 'blue,12 month ago,white,1 month ago,red', which colors
-everything older than one year blue, recent changes between one month and
-one year old are kept white, and lines introduced within the last month are
-colored red.
+It defaults to `blue,12 month ago,white,1 month ago,red`, which
+colors everything older than one year blue, recent changes between
+one month and one year old are kept white, and lines introduced
+within the last month are colored red.
color.blame.repeatedLines::
- Use the customized color for the part of git-blame output that
- is repeated meta information per line (such as commit id,
- author name, date and timezone). Defaults to cyan.
+ Use the specified color to colorize line annotations for
+ `git blame --color-lines`, if they come from the same commit as the
+ preceding line. Defaults to cyan.
color.branch::
A boolean to enable/disable color in the output of
@@ -104,9 +105,12 @@ color.grep.<slot>::
`matchContext`;;
matching text in context lines
`matchSelected`;;
- matching text in selected lines
+ matching text in selected lines. Also, used to customize the following
+ linkgit:git-log[1] subcommands: `--grep`, `--author` and `--committer`.
`selected`;;
- non-matching text in selected lines
+ non-matching text in selected lines. Also, used to customize the
+ following linkgit:git-log[1] subcommands: `--grep`, `--author` and
+ `--committer`.
`separator`;;
separators between fields on a line (`:`, `-`, and `=`)
and between hunks (`--`)
diff --git a/Documentation/config/gpg.txt b/Documentation/config/gpg.txt
index d94025c..4f30c7d 100644
--- a/Documentation/config/gpg.txt
+++ b/Documentation/config/gpg.txt
@@ -11,13 +11,13 @@ gpg.program::
gpg.format::
Specifies which key format to use when signing with `--gpg-sign`.
- Default is "openpgp" and another possible value is "x509".
+ Default is "openpgp". Other possible values are "x509", "ssh".
gpg.<format>.program::
Use this to customize the program used for the signing format you
chose. (see `gpg.program` and `gpg.format`) `gpg.program` can still
be used as a legacy synonym for `gpg.openpgp.program`. The default
- value for `gpg.x509.program` is "gpgsm".
+ value for `gpg.x509.program` is "gpgsm" and `gpg.ssh.program` is "ssh-keygen".
gpg.minTrustLevel::
Specifies a minimum trust level for signature verification. If
@@ -33,3 +33,42 @@ gpg.minTrustLevel::
* `marginal`
* `fully`
* `ultimate`
+
+gpg.ssh.defaultKeyCommand:
+ This command that will be run when user.signingkey is not set and a ssh
+ signature is requested. On successful exit a valid ssh public key is
+ expected in the first line of its output. To automatically use the first
+ available key from your ssh-agent set this to "ssh-add -L".
+
+gpg.ssh.allowedSignersFile::
+ A file containing ssh public keys which you are willing to trust.
+ The file consists of one or more lines of principals followed by an ssh
+ public key.
+ e.g.: user1@example.com,user2@example.com ssh-rsa AAAAX1...
+ See ssh-keygen(1) "ALLOWED SIGNERS" for details.
+ The principal is only used to identify the key and is available when
+ verifying a signature.
++
+SSH has no concept of trust levels like gpg does. To be able to differentiate
+between valid signatures and trusted signatures the trust level of a signature
+verification is set to `fully` when the public key is present in the allowedSignersFile.
+Otherwise the trust level is `undefined` and git verify-commit/tag will fail.
++
+This file can be set to a location outside of the repository and every developer
+maintains their own trust store. A central repository server could generate this
+file automatically from ssh keys with push access to verify the code against.
+In a corporate setting this file is probably generated at a global location
+from automation that already handles developer ssh keys.
++
+A repository that only allows signed commits can store the file
+in the repository itself using a path relative to the top-level of the working tree.
+This way only committers with an already valid key can add or change keys in the keyring.
++
+Using a SSH CA key with the cert-authority option
+(see ssh-keygen(1) "CERTIFICATES") is also valid.
+
+gpg.ssh.revocationFile::
+ Either a SSH KRL or a list of revoked public keys (without the principal prefix).
+ See ssh-keygen(1) for details.
+ If a public key is found in this file then it will always be treated
+ as having trust level "never" and signatures will show as invalid.
diff --git a/Documentation/config/pack.txt b/Documentation/config/pack.txt
index 763f7af..ad7f73a 100644
--- a/Documentation/config/pack.txt
+++ b/Documentation/config/pack.txt
@@ -159,6 +159,10 @@ pack.writeBitmapHashCache::
between an older, bitmapped pack and objects that have been
pushed since the last gc). The downside is that it consumes 4
bytes per object of disk space. Defaults to true.
++
+When writing a multi-pack reachability bitmap, no new namehashes are
+computed; instead, any namehashes stored in an existing bitmap are
+permuted into their appropriate location when writing a new bitmap.
pack.writeReverseIndex::
When true, git will write a corresponding .rev file (see:
diff --git a/Documentation/config/pull.txt b/Documentation/config/pull.txt
index 5404830..9349e09 100644
--- a/Documentation/config/pull.txt
+++ b/Documentation/config/pull.txt
@@ -18,10 +18,6 @@ When `merges` (or just 'm'), pass the `--rebase-merges` option to 'git rebase'
so that the local merge commits are included in the rebase (see
linkgit:git-rebase[1] for details).
+
-When `preserve` (or just 'p', deprecated in favor of `merges`), also pass
-`--preserve-merges` along to 'git rebase' so that locally committed merge
-commits will not be flattened by running 'git pull'.
-+
When the value is `interactive` (or just 'i'), the rebase is run in interactive
mode.
+
diff --git a/Documentation/config/user.txt b/Documentation/config/user.txt
index 59aec7c..ad78dce 100644
--- a/Documentation/config/user.txt
+++ b/Documentation/config/user.txt
@@ -36,3 +36,10 @@ user.signingKey::
commit, you can override the default selection with this variable.
This option is passed unchanged to gpg's --local-user parameter,
so you may specify a key using any method that gpg supports.
+ If gpg.format is set to "ssh" this can contain the literal ssh public
+ key (e.g.: "ssh-rsa XXXXXX identifier") or a file which contains it and
+ corresponds to the private key used for signing. The private key
+ needs to be available via ssh-agent. Alternatively it can be set to
+ a file containing a private key directly. If not set git will call
+ gpg.ssh.defaultKeyCommand (e.g.: "ssh-add -L") and try to use the first
+ key available.
diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt
index fbbd410..7a9c3b6 100644
--- a/Documentation/diff-format.txt
+++ b/Documentation/diff-format.txt
@@ -59,7 +59,7 @@ Possible status letters are:
- D: deletion of a file
- M: modification of the contents or mode of a file
- R: renaming of a file
-- T: change in the type of the file
+- T: change in the type of the file (regular file, symbolic link or submodule)
- U: file is unmerged (you must complete the merge before it can
be committed)
- X: "unknown" change type (most probably a bug, please report it)
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index be5e3ac..11eb70f 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -9,7 +9,7 @@ SYNOPSIS
--------
[verse]
'git add' [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p]
- [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]]
+ [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]] [--sparse]
[--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [--renormalize]
[--chmod=(+|-)x] [--pathspec-from-file=<file> [--pathspec-file-nul]]
[--] [<pathspec>...]
@@ -79,6 +79,13 @@ in linkgit:gitglossary[7].
--force::
Allow adding otherwise ignored files.
+--sparse::
+ Allow updating index entries outside of the sparse-checkout cone.
+ Normally, `git add` refuses to update index entries whose paths do
+ not fit within the sparse-checkout cone, since those files might
+ be removed from the working tree without warning. See
+ linkgit:git-sparse-checkout[1] for more details.
+
-i::
--interactive::
Add modified contents in the working tree interactively to
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 8714dfc..0a4a984 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -178,6 +178,8 @@ default. You can use `--no-utf8` to override this.
--abort::
Restore the original branch and abort the patching operation.
+ Revert contents of files involved in the am operation to their
+ pre-am state.
--quit::
Abort the patching operation but keep HEAD and the index
diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt
index 9f81728..bc4e76a 100644
--- a/Documentation/git-archive.txt
+++ b/Documentation/git-archive.txt
@@ -93,12 +93,19 @@ BACKEND EXTRA OPTIONS
zip
~~~
--0::
- Store the files instead of deflating them.
--9::
- Highest and slowest compression level. You can specify any
- number from 1 to 9 to adjust compression speed and ratio.
+-<digit>::
+ Specify compression level. Larger values allow the command
+ to spend more time to compress to smaller size. Supported
+ values are from `-0` (store-only) to `-9` (best ratio).
+ Default is `-6` if not given.
+tar
+~~~
+-<number>::
+ Specify compression level. The value will be passed to the
+ compression command configured in `tar.<format>.command`. See
+ manual page of the configured command for the list of supported
+ levels and the default level if this option isn't specified.
CONFIGURATION
-------------
diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt
index 3bf5d5d..d7a46cc 100644
--- a/Documentation/git-blame.txt
+++ b/Documentation/git-blame.txt
@@ -11,8 +11,8 @@ SYNOPSIS
'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental]
[-L <range>] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
[--ignore-rev <rev>] [--ignore-revs-file <file>]
- [--progress] [--abbrev=<n>] [<rev> | --contents <file> | --reverse <rev>..<rev>]
- [--] <file>
+ [--color-lines] [--color-by-age] [--progress] [--abbrev=<n>]
+ [<rev> | --contents <file> | --reverse <rev>..<rev>] [--] <file>
DESCRIPTION
-----------
@@ -93,6 +93,19 @@ include::blame-options.txt[]
is used for a caret to mark the boundary commit.
+THE DEFAULT FORMAT
+------------------
+
+When neither `--porcelain` nor `--incremental` option is specified,
+`git blame` will output annotation for each line with:
+
+- abbreviated object name for the commit the line came from;
+- author ident (by default author name and date, unless `-s` or `-e`
+ is specified); and
+- line number
+
+before the line contents.
+
THE PORCELAIN FORMAT
--------------------
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 5449767..8af42ef 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -125,14 +125,14 @@ OPTIONS
-m::
--move::
- Move/rename a branch and the corresponding reflog.
+ Move/rename a branch, together with its config and reflog.
-M::
Shortcut for `--move --force`.
-c::
--copy::
- Copy a branch and the corresponding reflog.
+ Copy a branch, together with its config and reflog.
-C::
Shortcut for `--copy --force`.
diff --git a/Documentation/git-bundle.txt b/Documentation/git-bundle.txt
index ac0d003..72ab813 100644
--- a/Documentation/git-bundle.txt
+++ b/Documentation/git-bundle.txt
@@ -13,7 +13,7 @@ SYNOPSIS
[--version=<version>] <file> <git-rev-list-args>
'git bundle' verify [-q | --quiet] <file>
'git bundle' list-heads <file> [<refname>...]
-'git bundle' unbundle <file> [<refname>...]
+'git bundle' unbundle [--progress] <file> [<refname>...]
DESCRIPTION
-----------
@@ -51,10 +51,10 @@ using the `--thin` option to linkgit:git-pack-objects[1], and
unbundled using the `--fix-thin` option to linkgit:git-index-pack[1].
There is no option to create a "thick pack" when using revision
-exclusions, users should not be concerned about the difference. By
-using "thin packs" bundles created using exclusions are smaller in
+exclusions, and users should not be concerned about the difference. By
+using "thin packs", bundles created using exclusions are smaller in
size. That they're "thin" under the hood is merely noted here as a
-curiosity, and as a reference to other documentation
+curiosity, and as a reference to other documentation.
See link:technical/bundle-format.html[the `bundle-format`
documentation] for more details and the discussion of "thin pack" in
@@ -144,7 +144,7 @@ unbundle <file>::
SPECIFYING REFERENCES
---------------------
-Revisions must accompanied by reference names to be packaged in a
+Revisions must be accompanied by reference names to be packaged in a
bundle.
More than one reference may be packaged, and more than one set of prerequisite objects can
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
index 4eb0421..27b27e2 100644
--- a/Documentation/git-cat-file.txt
+++ b/Documentation/git-cat-file.txt
@@ -94,8 +94,10 @@ OPTIONS
Instead of reading a list of objects on stdin, perform the
requested batch operation on all objects in the repository and
any alternate object stores (not just reachable objects).
- Requires `--batch` or `--batch-check` be specified. Note that
- the objects are visited in order sorted by their hashes.
+ Requires `--batch` or `--batch-check` be specified. By default,
+ the objects are visited in order sorted by their hashes; see
+ also `--unordered` below. Objects are presented as-is, without
+ respecting the "replace" mechanism of linkgit:git-replace[1].
--buffer::
Normally batch output is flushed after each object is output, so
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index b1a6fe4..d473c9b 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -118,8 +118,9 @@ OPTIONS
-f::
--force::
When switching branches, proceed even if the index or the
- working tree differs from `HEAD`. This is used to throw away
- local changes.
+ working tree differs from `HEAD`, and even if there are untracked
+ files in the way. This is used to throw away local changes and
+ any untracked files or directories that are in the way.
+
When checking out paths from the index, do not fail upon unmerged
entries; instead, unmerged entries are ignored.
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 95fec5f..6c60bf9 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -212,8 +212,9 @@ include::signoff-option.txt[]
each trailer would appear, and other details.
-n::
---no-verify::
- This option bypasses the pre-commit and commit-msg hooks.
+--[no-]verify::
+ By default, the pre-commit and commit-msg hooks are run.
+ When any of `--no-verify` or `-n` is given, these are bypassed.
See also linkgit:githooks[5].
--allow-empty::
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 2dc4bae..992225f 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -71,6 +71,9 @@ codes are:
On success, the command returns the exit code 0.
+A list of all available configuration variables can be obtained using the
+`git help --config` command.
+
[[OPTIONS]]
OPTIONS
-------
diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt
index f2e4a47..4dc57ed 100644
--- a/Documentation/git-cvsserver.txt
+++ b/Documentation/git-cvsserver.txt
@@ -99,7 +99,7 @@ looks like
------
-Only anonymous access is provided by pserve by default. To commit you
+Only anonymous access is provided by pserver by default. To commit you
will have to create pserver accounts, simply add a gitcvs.authdb
setting in the config file of the repositories you want the cvsserver
to allow writes to, for example:
@@ -114,21 +114,20 @@ The format of these files is username followed by the encrypted password,
for example:
------
- myuser:$1Oyx5r9mdGZ2
- myuser:$1$BA)@$vbnMJMDym7tA32AamXrm./
+ myuser:sqkNi8zPf01HI
+ myuser:$1$9K7FzU28$VfF6EoPYCJEYcVQwATgOP/
+ myuser:$5$.NqmNH1vwfzGpV8B$znZIcumu1tNLATgV2l6e1/mY8RzhUDHMOaVOeL1cxV3
------
You can use the 'htpasswd' facility that comes with Apache to make these
-files, but Apache's MD5 crypt method differs from the one used by most C
-library's crypt() function, so don't use the -m option.
+files, but only with the -d option (or -B if your system suports it).
-Alternatively you can produce the password with perl's crypt() operator:
------
- perl -e 'my ($user, $pass) = @ARGV; printf "%s:%s\n", $user, crypt($user, $pass)' $USER password
------
+Preferably use the system specific utility that manages password hash
+creation in your platform (e.g. mkpasswd in Linux, encrypt in OpenBSD or
+pwhash in NetBSD) and paste it in the right location.
Then provide your password via the pserver method, for example:
------
- cvs -d:pserver:someuser:somepassword <at> server/path/repo.git co <HEAD_name>
+ cvs -d:pserver:someuser:somepassword@server:/path/repo.git co <HEAD_name>
------
No special setup is needed for SSH access, other than having Git tools
in the PATH. If you have clients that do not accept the CVS_SERVER
@@ -138,7 +137,7 @@ Note: Newer CVS versions (>= 1.12.11) also support specifying
CVS_SERVER directly in CVSROOT like
------
-cvs -d ":ext;CVS_SERVER=git cvsserver:user@server/path/repo.git" co <HEAD_name>
+ cvs -d ":ext;CVS_SERVER=git cvsserver:user@server/path/repo.git" co <HEAD_name>
------
This has the advantage that it will be saved in your 'CVS/Root' files and
you don't need to worry about always setting the correct environment
@@ -186,8 +185,8 @@ allowing access over SSH.
+
--
------
- export CVSROOT=:ext:user@server:/var/git/project.git
- export CVS_SERVER="git cvsserver"
+ export CVSROOT=:ext:user@server:/var/git/project.git
+ export CVS_SERVER="git cvsserver"
------
--
4. For SSH clients that will make commits, make sure their server-side
@@ -203,7 +202,7 @@ allowing access over SSH.
`project-master` directory:
+
------
- cvs co -d project-master master
+ cvs co -d project-master master
------
[[dbbackend]]
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index fe2f69d..113eabc 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -689,10 +689,10 @@ You can also use `git format-patch --base=P -3 C` to generate patches
for A, B and C, and the identifiers for P, X, Y, Z are appended at the
end of the first message.
-If set `--base=auto` in cmdline, it will track base commit automatically,
-the base commit will be the merge base of tip commit of the remote-tracking
+If set `--base=auto` in cmdline, it will automatically compute
+the base commit as the merge base of tip commit of the remote-tracking
branch and revision-range specified in cmdline.
-For a local branch, you need to track a remote branch by `git branch
+For a local branch, you need to make it to track a remote branch by `git branch
--set-upstream-to` before using this option.
EXAMPLES
diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt
index 44fe886..96d5f59 100644
--- a/Documentation/git-help.txt
+++ b/Documentation/git-help.txt
@@ -8,8 +8,10 @@ git-help - Display help information about Git
SYNOPSIS
--------
[verse]
-'git help' [-a|--all [--[no-]verbose]] [-g|--guides]
- [-i|--info|-m|--man|-w|--web] [COMMAND|GUIDE]
+'git help' [-a|--all [--[no-]verbose]]
+ [[-i|--info] [-m|--man] [-w|--web]] [COMMAND|GUIDE]
+'git help' [-g|--guides]
+'git help' [-c|--config]
DESCRIPTION
-----------
@@ -58,8 +60,7 @@ OPTIONS
-g::
--guides::
- Prints a list of the Git concept guides on the standard output. This
- option overrides any given command or guide name.
+ Prints a list of the Git concept guides on the standard output.
-i::
--info::
diff --git a/Documentation/git-http-backend.txt b/Documentation/git-http-backend.txt
index 558966a..0c5c0dd 100644
--- a/Documentation/git-http-backend.txt
+++ b/Documentation/git-http-backend.txt
@@ -16,7 +16,9 @@ A simple CGI program to serve the contents of a Git repository to Git
clients accessing the repository over http:// and https:// protocols.
The program supports clients fetching using both the smart HTTP protocol
and the backwards-compatible dumb HTTP protocol, as well as clients
-pushing using the smart HTTP protocol.
+pushing using the smart HTTP protocol. It also supports Git's
+more-efficient "v2" protocol if properly configured; see the
+discussion of `GIT_PROTOCOL` in the ENVIRONMENT section below.
It verifies that the directory has the magic file
"git-daemon-export-ok", and it will refuse to export any Git directory
@@ -77,6 +79,18 @@ Apache 2.x::
SetEnv GIT_PROJECT_ROOT /var/www/git
SetEnv GIT_HTTP_EXPORT_ALL
ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
+
+# This is not strictly necessary using Apache and a modern version of
+# git-http-backend, as the webserver will pass along the header in the
+# environment as HTTP_GIT_PROTOCOL, and http-backend will copy that into
+# GIT_PROTOCOL. But you may need this line (or something similar if you
+# are using a different webserver), or if you want to support older Git
+# versions that did not do that copying.
+#
+# Having the webserver set up GIT_PROTOCOL is perfectly fine even with
+# modern versions (and will take precedence over HTTP_GIT_PROTOCOL,
+# which means it can be used to override the client's request).
+SetEnvIf Git-Protocol ".*" GIT_PROTOCOL=$0
----------------------------------------------------------------
+
To enable anonymous read access but authenticated write access,
@@ -264,6 +278,16 @@ a repository with an extremely large number of refs. The value can be
specified with a unit (e.g., `100M` for 100 megabytes). The default is
10 megabytes.
+Clients may probe for optional protocol capabilities (like the v2
+protocol) using the `Git-Protocol` HTTP header. In order to support
+these, the contents of that header must appear in the `GIT_PROTOCOL`
+environment variable. Most webservers will pass this header to the CGI
+via the `HTTP_GIT_PROTOCOL` variable, and `git-http-backend` will
+automatically copy that to `GIT_PROTOCOL`. However, some webservers may
+be more selective about which headers they'll pass, in which case they
+need to be configured explicitly (see the mention of `Git-Protocol` in
+the Apache config from the earlier EXAMPLES section).
+
The backend process sets GIT_COMMITTER_NAME to '$REMOTE_USER' and
GIT_COMMITTER_EMAIL to '$\{REMOTE_USER}@http.$\{REMOTE_ADDR\}',
ensuring that any reflogs created by 'git-receive-pack' contain some
diff --git a/Documentation/git-index-pack.txt b/Documentation/git-index-pack.txt
index 7fa74b9..1f1e359 100644
--- a/Documentation/git-index-pack.txt
+++ b/Documentation/git-index-pack.txt
@@ -82,6 +82,12 @@ OPTIONS
--strict::
Die, if the pack contains broken objects or links.
+--progress-title::
+ For internal use only.
++
+Set the title of the progress bar. The title is "Receiving objects" by
+default and "Indexing objects" when `--stdin` is specified.
+
--check-self-contained-and-connected::
Die if the pack contains broken links. For internal use only.
diff --git a/Documentation/git-maintenance.txt b/Documentation/git-maintenance.txt
index 1e738ad..e2cfb68 100644
--- a/Documentation/git-maintenance.txt
+++ b/Documentation/git-maintenance.txt
@@ -179,6 +179,17 @@ OPTIONS
`maintenance.<task>.enabled` configured as `true` are considered.
See the 'TASKS' section for the list of accepted `<task>` values.
+--scheduler=auto|crontab|systemd-timer|launchctl|schtasks::
+ When combined with the `start` subcommand, specify the scheduler
+ for running the hourly, daily and weekly executions of
+ `git maintenance run`.
+ Possible values for `<scheduler>` are `auto`, `crontab`
+ (POSIX), `systemd-timer` (Linux), `launchctl` (macOS), and
+ `schtasks` (Windows). When `auto` is specified, the
+ appropriate platform-specific scheduler is used; on Linux,
+ `systemd-timer` is used if available, otherwise
+ `crontab`. Default is `auto`.
+
TROUBLESHOOTING
---------------
@@ -277,6 +288,52 @@ schedule to ensure you are executing the correct binaries in your
schedule.
+BACKGROUND MAINTENANCE ON LINUX SYSTEMD SYSTEMS
+-----------------------------------------------
+
+While Linux supports `cron`, depending on the distribution, `cron` may
+be an optional package not necessarily installed. On modern Linux
+distributions, systemd timers are superseding it.
+
+If user systemd timers are available, they will be used as a replacement
+of `cron`.
+
+In this case, `git maintenance start` will create user systemd timer units
+and start the timers. The current list of user-scheduled tasks can be found
+by running `systemctl --user list-timers`. The timers written by `git
+maintenance start` are similar to this:
+
+-----------------------------------------------------------------------
+$ systemctl --user list-timers
+NEXT LEFT LAST PASSED UNIT ACTIVATES
+Thu 2021-04-29 19:00:00 CEST 42min left Thu 2021-04-29 18:00:11 CEST 17min ago git-maintenance@hourly.timer git-maintenance@hourly.service
+Fri 2021-04-30 00:00:00 CEST 5h 42min left Thu 2021-04-29 00:00:11 CEST 18h ago git-maintenance@daily.timer git-maintenance@daily.service
+Mon 2021-05-03 00:00:00 CEST 3 days left Mon 2021-04-26 00:00:11 CEST 3 days ago git-maintenance@weekly.timer git-maintenance@weekly.service
+-----------------------------------------------------------------------
+
+One timer is registered for each `--schedule=<frequency>` option.
+
+The definition of the systemd units can be inspected in the following files:
+
+-----------------------------------------------------------------------
+~/.config/systemd/user/git-maintenance@.timer
+~/.config/systemd/user/git-maintenance@.service
+~/.config/systemd/user/timers.target.wants/git-maintenance@hourly.timer
+~/.config/systemd/user/timers.target.wants/git-maintenance@daily.timer
+~/.config/systemd/user/timers.target.wants/git-maintenance@weekly.timer
+-----------------------------------------------------------------------
+
+`git maintenance start` will overwrite these files and start the timer
+again with `systemctl --user`, so any customization should be done by
+creating a drop-in file, i.e. a `.conf` suffixed file in the
+`~/.config/systemd/user/git-maintenance@.service.d` directory.
+
+`git maintenance stop` will stop the user systemd timers and delete
+the above mentioned files.
+
+For more details, see `systemd.timer(5)`.
+
+
BACKGROUND MAINTENANCE ON MACOS SYSTEMS
---------------------------------------
diff --git a/Documentation/git-multi-pack-index.txt b/Documentation/git-multi-pack-index.txt
index ffd601b..c588fb9 100644
--- a/Documentation/git-multi-pack-index.txt
+++ b/Documentation/git-multi-pack-index.txt
@@ -9,8 +9,7 @@ git-multi-pack-index - Write and verify multi-pack-indexes
SYNOPSIS
--------
[verse]
-'git multi-pack-index' [--object-dir=<dir>] [--[no-]progress]
- [--preferred-pack=<pack>] <subcommand>
+'git multi-pack-index' [--object-dir=<dir>] [--[no-]bitmap] <sub-command>
DESCRIPTION
-----------
@@ -23,10 +22,13 @@ OPTIONS
Use given directory for the location of Git objects. We check
`<dir>/packs/multi-pack-index` for the current MIDX file, and
`<dir>/packs` for the pack-files to index.
++
+`<dir>` must be an alternate of the current repository.
--[no-]progress::
Turn progress on/off explicitly. If neither is specified, progress is
- shown if standard error is connected to a terminal.
+ shown if standard error is connected to a terminal. Supported by
+ sub-commands `write`, `verify`, `expire`, and `repack.
The following subcommands are available:
@@ -37,9 +39,31 @@ write::
--
--preferred-pack=<pack>::
Optionally specify the tie-breaking pack used when
- multiple packs contain the same object. If not given,
- ties are broken in favor of the pack with the lowest
- mtime.
+ multiple packs contain the same object. `<pack>` must
+ contain at least one object. If not given, ties are
+ broken in favor of the pack with the lowest mtime.
+
+ --[no-]bitmap::
+ Control whether or not a multi-pack bitmap is written.
+
+ --stdin-packs::
+ Write a multi-pack index containing only the set of
+ line-delimited pack index basenames provided over stdin.
+
+ --refs-snapshot=<path>::
+ With `--bitmap`, optionally specify a file which
+ contains a "refs snapshot" taken prior to repacking.
++
+A reference snapshot is composed of line-delimited OIDs corresponding to
+the reference tips, usually taken by `git repack` prior to generating a
+new pack. A line may optionally start with a `+` character to indicate
+that the reference which corresponds to that OID is "preferred" (see
+linkgit:git-config[1]'s `pack.preferBitmapTips`.)
++
+The file given at `<path>` is expected to be readable, and can contain
+duplicates. (If a given OID is given more than once, it is marked as
+preferred if at least one instance of it begins with the special `+`
+marker).
--
verify::
@@ -75,19 +99,26 @@ associated `.keep` file will not be selected for the batch to repack.
EXAMPLES
--------
-* Write a MIDX file for the packfiles in the current .git folder.
+* Write a MIDX file for the packfiles in the current `.git` directory.
+
-----------------------------------------------
$ git multi-pack-index write
-----------------------------------------------
+* Write a MIDX file for the packfiles in the current `.git` directory with a
+corresponding bitmap.
++
+-------------------------------------------------------------
+$ git multi-pack-index write --preferred-pack=<pack> --bitmap
+-------------------------------------------------------------
+
* Write a MIDX file for the packfiles in an alternate object store.
+
-----------------------------------------------
$ git multi-pack-index --object-dir <alt> write
-----------------------------------------------
-* Verify the MIDX file for the packfiles in the current .git folder.
+* Verify the MIDX file for the packfiles in the current `.git` directory.
+
-----------------------------------------------
$ git multi-pack-index verify
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index aef757e..0e14f8b 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -105,7 +105,7 @@ Options related to merging
include::merge-options.txt[]
-r::
---rebase[=false|true|merges|preserve|interactive]::
+--rebase[=false|true|merges|interactive]::
When true, rebase the current branch on top of the upstream
branch after fetching. If there is a remote-tracking branch
corresponding to the upstream branch and the upstream branch
@@ -116,10 +116,6 @@ When set to `merges`, rebase using `git rebase --rebase-merges` so that
the local merge commits are included in the rebase (see
linkgit:git-rebase[1] for details).
+
-When set to `preserve` (deprecated in favor of `merges`), rebase with the
-`--preserve-merges` option passed to `git rebase` so that locally created
-merge commits will not be flattened.
-+
When false, merge the upstream branch into the current branch.
+
When `interactive`, enable the interactive mode of rebase.
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index 5fa8bab..8c3aceb 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -10,8 +10,7 @@ SYNOPSIS
--------
[verse]
'git read-tree' [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>]
- [-u [--exclude-per-directory=<gitignore>] | -i]]
- [--index-output=<file>] [--no-sparse-checkout]
+ [-u | -i]] [--index-output=<file>] [--no-sparse-checkout]
(--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])
@@ -39,8 +38,9 @@ OPTIONS
--reset::
Same as -m, except that unmerged entries are discarded instead
- of failing. When used with `-u`, updates leading to loss of
- working tree changes will not abort the operation.
+ of failing. When used with `-u`, updates leading to loss of
+ working tree changes or untracked files or directories will not
+ abort the operation.
-u::
After a successful merge, update the files in the work
@@ -88,21 +88,6 @@ OPTIONS
The command will refuse to overwrite entries that already
existed in the original index file.
---exclude-per-directory=<gitignore>::
- When running the command with `-u` and `-m` options, the
- merge result may need to overwrite paths that are not
- tracked in the current branch. The command usually
- refuses to proceed with the merge to avoid losing such a
- path. However this safety valve sometimes gets in the
- way. For example, it often happens that the other
- branch added a file that used to be a generated file in
- your branch, and the safety valve triggers when you try
- to switch to that branch after you ran `make` but before
- running `make clean` to remove the generated file. This
- option tells the command to read per-directory exclude
- file (usually '.gitignore') and allows such an untracked
- but explicitly ignored file to be overwritten.
-
--index-output=<file>::
Instead of writing the results out to `$GIT_INDEX_FILE`,
write the resulting index in the named file. While the
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 506345c..a1af21f 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -446,7 +446,8 @@ When --fork-point is active, 'fork_point' will be used instead of
ends up being empty, the <upstream> will be used as a fallback.
+
If <upstream> is given on the command line, then the default is
-`--no-fork-point`, otherwise the default is `--fork-point`.
+`--no-fork-point`, otherwise the default is `--fork-point`. See also
+`rebase.forkpoint` in linkgit:git-config[1].
+
If your branch was based on <upstream> but <upstream> was rewound and
your branch contains commits which were dropped, this option can be used
@@ -526,29 +527,12 @@ i.e. commits that would be excluded by linkgit:git-log[1]'s
the `rebase-cousins` mode is turned on, such commits are instead rebased
onto `<upstream>` (or `<onto>`, if specified).
+
-The `--rebase-merges` mode is similar in spirit to the deprecated
-`--preserve-merges` but works with interactive rebases,
-where commits can be reordered, inserted and dropped at will.
-+
It is currently only possible to recreate the merge commits using the
`ort` merge strategy; different merge strategies can be used only via
explicit `exec git merge -s <strategy> [...]` commands.
+
See also REBASING MERGES and INCOMPATIBLE OPTIONS below.
--p::
---preserve-merges::
- [DEPRECATED: use `--rebase-merges` instead] Recreate merge commits
- instead of flattening the history by replaying commits a merge commit
- introduces. Merge conflict resolutions or manual amendments to merge
- commits are not preserved.
-+
-This uses the `--interactive` machinery internally, but combining it
-with the `--interactive` option explicitly is generally not a good
-idea unless you know what you are doing (see BUGS below).
-+
-See also INCOMPATIBLE OPTIONS below.
-
-x <cmd>::
--exec <cmd>::
Append "exec <cmd>" after each line creating a commit in the
@@ -580,9 +564,6 @@ See also INCOMPATIBLE OPTIONS below.
the root commit(s) on a branch. When used with --onto, it
will skip changes already contained in <newbase> (instead of
<upstream>) whereas without --onto it will operate on every change.
- When used together with both --onto and --preserve-merges,
- 'all' root commits will be rewritten to have <newbase> as parent
- instead.
+
See also INCOMPATIBLE OPTIONS below.
@@ -644,7 +625,6 @@ are incompatible with the following options:
* --allow-empty-message
* --[no-]autosquash
* --rebase-merges
- * --preserve-merges
* --interactive
* --exec
* --no-keep-empty
@@ -655,13 +635,6 @@ are incompatible with the following options:
In addition, the following pairs of options are incompatible:
- * --preserve-merges and --interactive
- * --preserve-merges and --signoff
- * --preserve-merges and --rebase-merges
- * --preserve-merges and --empty=
- * --preserve-merges and --ignore-whitespace
- * --preserve-merges and --committer-date-is-author-date
- * --preserve-merges and --ignore-date
* --keep-base and --onto
* --keep-base and --root
* --fork-point and --root
@@ -1279,29 +1252,6 @@ CONFIGURATION
include::config/rebase.txt[]
include::config/sequencer.txt[]
-BUGS
-----
-The todo list presented by the deprecated `--preserve-merges --interactive`
-does not represent the topology of the revision graph (use `--rebase-merges`
-instead). Editing commits and rewording their commit messages should work
-fine, but attempts to reorder commits tend to produce counterintuitive results.
-Use `--rebase-merges` in such scenarios instead.
-
-For example, an attempt to rearrange
-------------
-1 --- 2 --- 3 --- 4 --- 5
-------------
-to
-------------
-1 --- 2 --- 4 --- 3 --- 5
-------------
-by moving the "pick 4" line will result in the following history:
-------------
- 3
- /
-1 --- 2 --- 4 --- 5
-------------
-
GIT
---
Part of the linkgit:git[1] suite
diff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt
index 25702ed..014a784 100644
--- a/Documentation/git-receive-pack.txt
+++ b/Documentation/git-receive-pack.txt
@@ -41,6 +41,11 @@ OPTIONS
<directory>::
The repository to sync into.
+--http-backend-info-refs::
+ Used by linkgit:git-http-backend[1] to serve up
+ `$GIT_URL/info/refs?service=git-receive-pack` requests. See
+ `--http-backend-info-refs` in linkgit:git-upload-pack[1].
+
PRE-RECEIVE HOOK
----------------
Before any ref is updated, if $GIT_DIR/hooks/pre-receive file exists
diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
index 24c00c9..7183fb4 100644
--- a/Documentation/git-repack.txt
+++ b/Documentation/git-repack.txt
@@ -9,7 +9,7 @@ git-repack - Pack unpacked objects in a repository
SYNOPSIS
--------
[verse]
-'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]
+'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [-m] [--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>] [--write-midx]
DESCRIPTION
-----------
@@ -128,10 +128,11 @@ depth is 4095.
-b::
--write-bitmap-index::
Write a reachability bitmap index as part of the repack. This
- only makes sense when used with `-a` or `-A`, as the bitmaps
+ only makes sense when used with `-a`, `-A` or `-m`, as the bitmaps
must be able to refer to all reachable objects. This option
- overrides the setting of `repack.writeBitmaps`. This option
- has no effect if multiple packfiles are created.
+ overrides the setting of `repack.writeBitmaps`. This option
+ has no effect if multiple packfiles are created, unless writing a
+ MIDX (in which case a multi-pack bitmap is created).
--pack-kept-objects::
Include objects in `.keep` files when repacking. Note that we
@@ -189,6 +190,15 @@ this "roll-up", without respect to their reachability. This is subject
to change in the future. This option (implying a drastically different
repack mode) is not guaranteed to work with all other combinations of
option to `git repack`.
++
+When writing a multi-pack bitmap, `git repack` selects the largest resulting
+pack as the preferred pack for object selection by the MIDX (see
+linkgit:git-multi-pack-index[1]).
+
+-m::
+--write-midx::
+ Write a multi-pack index (see linkgit:git-multi-pack-index[1])
+ containing the non-redundant packs.
CONFIGURATION
-------------
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 252e2d4..6f7685f 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -69,7 +69,8 @@ linkgit:git-add[1]).
--hard::
Resets the index and working tree. Any changes to tracked files in the
- working tree since `<commit>` are discarded.
+ working tree since `<commit>` are discarded. Any untracked files or
+ directories in the way of writing any tracked files are simply deleted.
--merge::
Resets the index and updates the files in the working tree that are
diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index 26e9b28..81bc23f 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -72,6 +72,12 @@ For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
--ignore-unmatch::
Exit with a zero status even if no files matched.
+--sparse::
+ Allow updating index entries outside of the sparse-checkout cone.
+ Normally, `git rm` refuses to update index entries whose paths do
+ not fit within the sparse-checkout cone. See
+ linkgit:git-sparse-checkout[1] for more.
+
-q::
--quiet::
`git rm` normally outputs one line (in the form of an `rm` command)
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 3db4eab..41cd8cb 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -9,7 +9,8 @@ git-send-email - Send a collection of patches as emails
SYNOPSIS
--------
[verse]
-'git send-email' [<options>] <file|directory|rev-list options>...
+'git send-email' [<options>] <file|directory>...
+'git send-email' [<options>] <format-patch options>
'git send-email' --dump-aliases
@@ -19,7 +20,8 @@ Takes the patches given on the command line and emails them out.
Patches can be specified as files, directories (which will send all
files in the directory), or directly as a revision list. In the
last case, any format accepted by linkgit:git-format-patch[1] can
-be passed to git send-email.
+be passed to git send-email, as well as options understood by
+linkgit:git-format-patch[1].
The header of the email is configurable via command-line options. If not
specified on the command line, the user will be prompted with a ReadLine
diff --git a/Documentation/git-send-pack.txt b/Documentation/git-send-pack.txt
index 44fd146..be41f11 100644
--- a/Documentation/git-send-pack.txt
+++ b/Documentation/git-send-pack.txt
@@ -9,10 +9,10 @@ git-send-pack - Push objects over Git protocol to another repository
SYNOPSIS
--------
[verse]
-'git send-pack' [--all] [--dry-run] [--force] [--receive-pack=<git-receive-pack>]
+'git send-pack' [--dry-run] [--force] [--receive-pack=<git-receive-pack>]
[--verbose] [--thin] [--atomic]
[--[no-]signed|--signed=(true|false|if-asked)]
- [<host>:]<directory> [<ref>...]
+ [<host>:]<directory> (--all | <ref>...)
DESCRIPTION
-----------
diff --git a/Documentation/git-sparse-checkout.txt b/Documentation/git-sparse-checkout.txt
index fdcf43f..42056ee 100644
--- a/Documentation/git-sparse-checkout.txt
+++ b/Documentation/git-sparse-checkout.txt
@@ -210,6 +210,16 @@ case-insensitive check. This corrects for case mismatched filenames in the
'git sparse-checkout set' command to reflect the expected cone in the working
directory.
+When changing the sparse-checkout patterns in cone mode, Git will inspect each
+tracked directory that is not within the sparse-checkout cone to see if it
+contains any untracked files. If all of those files are ignored due to the
+`.gitignore` patterns, then the directory will be deleted. If any of the
+untracked files within that directory is not ignored, then no deletions will
+occur within that directory and a warning message will appear. If these files
+are important, then reset your sparse-checkout definition so they are included,
+use `git add` and `git commit` to store them, then remove any remaining files
+manually to ensure Git can behave optimally.
+
SUBMODULES
----------
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index be6084c..6e15f47 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -13,7 +13,7 @@ SYNOPSIS
'git stash' drop [-q|--quiet] [<stash>]
'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
'git stash' branch <branchname> [<stash>]
-'git stash' [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
+'git stash' [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]
[-u|--include-untracked] [-a|--all] [-m|--message <message>]
[--pathspec-from-file=<file> [--pathspec-file-nul]]
[--] [<pathspec>...]]
@@ -47,7 +47,7 @@ stash index (e.g. the integer `n` is equivalent to `stash@{n}`).
COMMANDS
--------
-push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--pathspec-from-file=<file> [--pathspec-file-nul]] [--] [<pathspec>...]::
+push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--pathspec-from-file=<file> [--pathspec-file-nul]] [--] [<pathspec>...]::
Save your local modifications to a new 'stash entry' and roll them
back to HEAD (in the working tree and in the index).
@@ -60,7 +60,7 @@ subcommand from making an unwanted stash entry. The two exceptions to this
are `stash -p` which acts as alias for `stash push -p` and pathspec elements,
which are allowed after a double hyphen `--` for disambiguation.
-save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
+save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
This option is deprecated in favour of 'git stash push'. It
differs from "stash push" in that it cannot take pathspec.
@@ -205,6 +205,16 @@ to learn how to operate the `--patch` mode.
The `--patch` option implies `--keep-index`. You can use
`--no-keep-index` to override this.
+-S::
+--staged::
+ This option is only valid for `push` and `save` commands.
++
+Stash only the changes that are currently staged. This is similar to
+basic `git commit` except the state is committed to the stash instead
+of current branch.
++
+The `--patch` option has priority over this one.
+
--pathspec-from-file=<file>::
This option is only valid for `push` command.
+
@@ -341,6 +351,24 @@ $ edit/build/test remaining parts
$ git commit foo -m 'Remaining parts'
----------------------------------------------------------------
+Saving unrelated changes for future use::
+
+When you are in the middle of massive changes and you find some
+unrelated issue that you don't want to forget to fix, you can do the
+change(s), stage them, and use `git stash push --staged` to stash them
+out for future use. This is similar to committing the staged changes,
+only the commit ends-up being in the stash and not on the current branch.
++
+----------------------------------------------------------------
+# ... hack hack hack ...
+$ git add --patch foo # add unrelated changes to the index
+$ git stash push --staged # save these changes to the stash
+# ... hack hack hack, finish curent changes ...
+$ git commit -m 'Massive' # commit fully tested changes
+$ git switch fixup-branch # switch to another branch
+$ git stash pop # to finish work on the saved changes
+----------------------------------------------------------------
+
Recovering stash entries that were cleared/dropped erroneously::
If you mistakenly drop or clear stash entries, they cannot be recovered
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index 83f38e3..54a4b29 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -207,26 +207,29 @@ show tracked paths:
* ' ' = unmodified
* 'M' = modified
+* 'T' = file type changed (regular file, symbolic link or submodule)
* 'A' = added
* 'D' = deleted
* 'R' = renamed
-* 'C' = copied
+* 'C' = copied (if config option status.renames is set to "copies")
* 'U' = updated but unmerged
....
X Y Meaning
-------------------------------------------------
[AMD] not updated
-M [ MD] updated in index
-A [ MD] added to index
+M [ MTD] updated in index
+T [ MTD] type changed in index
+A [ MTD] added to index
D deleted from index
-R [ MD] renamed in index
-C [ MD] copied in index
-[MARC] index and work tree matches
-[ MARC] M work tree changed since index
-[ MARC] D deleted in work tree
-[ D] R renamed in work tree
-[ D] C copied in work tree
+R [ MTD] renamed in index
+C [ MTD] copied in index
+[MTARC] index and work tree matches
+[ MTARC] M work tree changed since index
+[ MTARC] T type changed in work tree since index
+[ MTARC] D deleted in work tree
+ R renamed in work tree
+ C copied in work tree
-------------------------------------------------
D D unmerged, both deleted
A U unmerged, added by us
@@ -311,6 +314,14 @@ Line Notes
------------------------------------------------------------
....
+Stash Information
+^^^^^^^^^^^^^^^^^
+
+If `--show-stash` is given, one line is printed showing the number of stash
+entries if non-zero:
+
+ # stash <N>
+
Changed Tracked Entries
^^^^^^^^^^^^^^^^^^^^^^^
@@ -363,7 +374,7 @@ Field Meaning
Unmerged entries have the following format; the first character is
a "u" to distinguish from ordinary changed entries.
- u <xy> <sub> <m1> <m2> <m3> <mW> <h1> <h2> <h3> <path>
+ u <XY> <sub> <m1> <m2> <m3> <mW> <h1> <h2> <h3> <path>
....
Field Meaning
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index d5776ff..222b556 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -678,7 +678,6 @@ config key: svn.authorsProg
--strategy=<strategy>::
-p::
--rebase-merges::
---preserve-merges (DEPRECATED)::
These are only used with the 'dcommit' and 'rebase' commands.
+
Passed directly to 'git rebase' when using 'dcommit' if a
diff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt
index 9822c1e..8f87b23 100644
--- a/Documentation/git-upload-pack.txt
+++ b/Documentation/git-upload-pack.txt
@@ -36,14 +36,26 @@ OPTIONS
This fits with the HTTP POST request processing model where
a program may read the request, write a response, and must exit.
---advertise-refs::
- Only the initial ref advertisement is output, and the program exits
- immediately. This fits with the HTTP GET request model, where
- no request content is received but a response must be produced.
+--http-backend-info-refs::
+ Used by linkgit:git-http-backend[1] to serve up
+ `$GIT_URL/info/refs?service=git-upload-pack` requests. See
+ "Smart Clients" in link:technical/http-protocol.html[the HTTP
+ transfer protocols] documentation and "HTTP Transport" in
+ link:technical/protocol-v2.html[the Git Wire Protocol, Version
+ 2] documentation. Also understood by
+ linkgit:git-receive-pack[1].
<directory>::
The repository to sync from.
+ENVIRONMENT
+-----------
+
+`GIT_PROTOCOL`::
+ Internal variable used for handshaking the wire protocol. Server
+ admins may need to configure some transports to allow this
+ variable to be passed. See the discussion in linkgit:git[1].
+
SEE ALSO
--------
linkgit:gitnamespaces[7]
diff --git a/Documentation/git-version.txt b/Documentation/git-version.txt
new file mode 100644
index 0000000..80fa775
--- /dev/null
+++ b/Documentation/git-version.txt
@@ -0,0 +1,28 @@
+git-version(1)
+==============
+
+NAME
+----
+git-version - Display version information about Git
+
+SYNOPSIS
+--------
+[verse]
+'git version' [--build-options]
+
+DESCRIPTION
+-----------
+With no options given, the version of 'git' is printed on the standard output.
+
+Note that `git --version` is identical to `git version` because the
+former is internally converted into the latter.
+
+OPTIONS
+-------
+--build-options::
+ Include additional information about how git was built for diagnostic
+ purposes.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 6dd241e..281c5f8 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -41,6 +41,10 @@ OPTIONS
-------
--version::
Prints the Git suite version that the 'git' program came from.
++
+This option is internally converted to `git version ...` and accepts
+the same options as the linkgit:git-version[1] command. If `--help` is
+also given, it takes precedence over `--version`.
--help::
Prints the synopsis and a list of the most commonly used
@@ -863,15 +867,16 @@ for full details.
end user, to be recorded in the body of the reflog.
`GIT_REF_PARANOIA`::
- If set to `1`, include broken or badly named refs when iterating
- over lists of refs. In a normal, non-corrupted repository, this
- does nothing. However, enabling it may help git to detect and
- abort some operations in the presence of broken refs. Git sets
- this variable automatically when performing destructive
- operations like linkgit:git-prune[1]. You should not need to set
- it yourself unless you want to be paranoid about making sure
- an operation has touched every ref (e.g., because you are
- cloning a repository to make a backup).
+ If set to `0`, ignore broken or badly named refs when iterating
+ over lists of refs. Normally Git will try to include any such
+ refs, which may cause some operations to fail. This is usually
+ preferable, as potentially destructive operations (e.g.,
+ linkgit:git-prune[1]) are better off aborting rather than
+ ignoring broken refs (and thus considering the history they
+ point to as not worth saving). The default value is `1` (i.e.,
+ be paranoid about detecting and aborting all operations). You
+ should not normally need to set this to `0`, but it may be
+ useful when trying to salvage data from a corrupted repository.
`GIT_ALLOW_PROTOCOL`::
If set to a colon-separated list of protocols, behave as if
@@ -894,6 +899,21 @@ for full details.
Contains a colon ':' separated list of keys with optional values
'key[=value]'. Presence of unknown keys and values must be
ignored.
++
+Note that servers may need to be configured to allow this variable to
+pass over some transports. It will be propagated automatically when
+accessing local repositories (i.e., `file://` or a filesystem path), as
+well as over the `git://` protocol. For git-over-http, it should work
+automatically in most configurations, but see the discussion in
+linkgit:git-http-backend[1]. For git-over-ssh, the ssh server may need
+to be configured to allow clients to pass this variable (e.g., by using
+`AcceptEnv GIT_PROTOCOL` with OpenSSH).
++
+This configuration is optional. If the variable is not propagated, then
+clients will fall back to the original "v0" protocol (but may miss out
+on some performance improvements or features). This variable currently
+only affects clones and fetches; it is not yet used for pushes (but may
+be in the future).
`GIT_OPTIONAL_LOCKS`::
If set to `0`, Git will complete any requested operation without
diff --git a/Documentation/gitignore.txt b/Documentation/gitignore.txt
index f8a1fc2..f2738b1 100644
--- a/Documentation/gitignore.txt
+++ b/Documentation/gitignore.txt
@@ -155,7 +155,7 @@ accessed from the index or a tree versus from the filesystem.
EXAMPLES
--------
- - The pattern `hello.*` matches any file or folder
+ - The pattern `hello.*` matches any file or directory
whose name begins with `hello.`. If one wants to restrict
this only to the directory and not in its subdirectories,
one can prepend the pattern with a slash, i.e. `/hello.*`;
diff --git a/Documentation/gitweb.txt b/Documentation/gitweb.txt
index 3cc9b03..7cee9d3 100644
--- a/Documentation/gitweb.txt
+++ b/Documentation/gitweb.txt
@@ -547,7 +547,7 @@ like this:
# make the front page an internal rewrite to the gitweb script
RewriteRule ^/$ /cgi-bin/gitweb.cgi [QSA,L,PT]
- # look for a public_git folder in unix users' home
+ # look for a public_git directory in unix users' home
# http://git.example.org/~<user>/
RewriteRule ^/\~([^\/]+)(/|/gitweb.cgi)?$ /cgi-bin/gitweb.cgi \
[QSA,E=GITWEB_PROJECTROOT:/home/$1/public_git/,L,PT]
diff --git a/Documentation/lint-gitlink.perl b/Documentation/lint-gitlink.perl
index b22a367..1c61dd9 100755
--- a/Documentation/lint-gitlink.perl
+++ b/Documentation/lint-gitlink.perl
@@ -5,11 +5,12 @@ use warnings;
# Parse arguments, a simple state machine for input like:
#
-# howto/*.txt config/*.txt --section=1 git.txt git-add.txt [...] --to-lint git-add.txt a-file.txt [...]
+# <file-to-check.txt> <valid-files-to-link-to> --section=1 git.txt git-add.txt [...] --to-lint git-add.txt a-file.txt [...]
my %TXT;
my %SECTION;
my $section;
my $lint_these = 0;
+my $to_check = shift @ARGV;
for my $arg (@ARGV) {
if (my ($sec) = $arg =~ /^--section=(\d+)$/s) {
$section = $sec;
@@ -30,13 +31,14 @@ sub report {
my ($pos, $line, $target, $msg) = @_;
substr($line, $pos) = "' <-- HERE";
$line =~ s/^\s+//;
- print "$ARGV:$.: error: $target: $msg, shown with 'HERE' below:\n";
- print "$ARGV:$.:\t'$line\n";
+ print STDERR "$ARGV:$.: error: $target: $msg, shown with 'HERE' below:\n";
+ print STDERR "$ARGV:$.:\t'$line\n";
$exit_code = 1;
}
@ARGV = sort values %TXT;
-die "BUG: Nothing to process!" unless @ARGV;
+die "BUG: No list of valid linkgit:* files given" unless @ARGV;
+@ARGV = $to_check;
while (<>) {
my $line = $_;
while ($line =~ m/linkgit:((.*?)\[(\d)\])/g) {
diff --git a/Documentation/lint-man-end-blurb.perl b/Documentation/lint-man-end-blurb.perl
index d69312e..6bdb13a 100755
--- a/Documentation/lint-man-end-blurb.perl
+++ b/Documentation/lint-man-end-blurb.perl
@@ -6,7 +6,7 @@ use warnings;
my $exit_code = 0;
sub report {
my ($target, $msg) = @_;
- print "error: $target: $msg\n";
+ print STDERR "error: $target: $msg\n";
$exit_code = 1;
}
diff --git a/Documentation/lint-man-section-order.perl b/Documentation/lint-man-section-order.perl
index b05f915..425377d 100755
--- a/Documentation/lint-man-section-order.perl
+++ b/Documentation/lint-man-section-order.perl
@@ -46,7 +46,7 @@ my $SECTION_RX = do {
my $exit_code = 0;
sub report {
my ($msg) = @_;
- print "$ARGV:$.: $msg\n";
+ print STDERR "$ARGV:$.: $msg\n";
$exit_code = 1;
}
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index 61ec157..d8f7cd7 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -132,8 +132,9 @@ ifdef::git-pull[]
Only useful when merging.
endif::git-pull[]
---no-verify::
- This option bypasses the pre-merge and commit-msg hooks.
+--[no-]verify::
+ By default, the pre-merge and commit-msg hooks are run.
+ When `--no-verify` is given, these are bypassed.
See also linkgit:githooks[5].
ifdef::git-pull[]
Only useful when merging.
diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt
index b3af850..dc685be 100644
--- a/Documentation/pretty-options.txt
+++ b/Documentation/pretty-options.txt
@@ -41,8 +41,8 @@ people using 80-column terminals.
in `X` and we are outputting in `X`, we will output the object
verbatim; this means that invalid sequences in the original
commit may be copied to the output. Likewise, if iconv(3) fails
- to convert the commit, we will output the original object
- verbatim, along with a warning.
+ to convert the commit, we will quietly output the original
+ object verbatim.
--expand-tabs=<n>::
--expand-tabs::
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index b7bd27e..24569b0 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -968,11 +968,6 @@ list of the missing objects. Object IDs are prefixed with a ``?'' character.
objects.
endif::git-rev-list[]
---unsorted-input::
- Show commits in the order they were given on the command line instead
- of sorting them in reverse chronological order by commit time. Cannot
- be combined with `--no-walk` or `--no-walk=sorted`.
-
--no-walk[=(sorted|unsorted)]::
Only show the given commits, but do not traverse their ancestors.
This has no effect if a range is specified. If the argument
@@ -980,8 +975,7 @@ endif::git-rev-list[]
given on the command line. Otherwise (if `sorted` or no argument
was given), the commits are shown in reverse chronological order
by commit time.
- Cannot be combined with `--graph`. Cannot be combined with
- `--unsorted-input` if `sorted` or no argument was given.
+ Cannot be combined with `--graph`.
--do-walk::
Overrides a previous `--no-walk`.
diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 5a60bbf..acfd5dc 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -198,11 +198,6 @@ There are some macros to easily define options:
The filename will be prefixed by passing the filename along with
the prefix argument of `parse_options()` to `prefix_filename()`.
-`OPT_ARGUMENT(long, &int_var, description)`::
- Introduce a long-option argument that will be kept in `argv[]`.
- If this option was seen, `int_var` will be set to one (except
- if a `NULL` pointer was passed).
-
`OPT_NUMBER_CALLBACK(&var, description, func_ptr)`::
Recognize numerical options like -123 and feed the integer as
if it was an argument to the function given by `func_ptr`.
diff --git a/Documentation/technical/api-trace2.txt b/Documentation/technical/api-trace2.txt
index b9f3198..bb13ca3 100644
--- a/Documentation/technical/api-trace2.txt
+++ b/Documentation/technical/api-trace2.txt
@@ -128,7 +128,7 @@ yields
------------
$ cat ~/log.event
-{"event":"version","sid":"sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.620713Z","file":"common-main.c","line":38,"evt":"2","exe":"2.20.1.155.g426c96fcdb"}
+{"event":"version","sid":"sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.620713Z","file":"common-main.c","line":38,"evt":"3","exe":"2.20.1.155.g426c96fcdb"}
{"event":"start","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.621027Z","file":"common-main.c","line":39,"t_abs":0.001173,"argv":["git","version"]}
{"event":"cmd_name","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.621122Z","file":"git.c","line":432,"name":"version","hierarchy":"version"}
{"event":"exit","sid":"20190408T191610.507018Z-H9b68c35f-P000059a8","thread":"main","time":"2019-01-16T17:28:42.621236Z","file":"git.c","line":662,"t_abs":0.001227,"code":0}
@@ -391,7 +391,7 @@ only present on the "start" and "atexit" events.
{
"event":"version",
...
- "evt":"2", # EVENT format version
+ "evt":"3", # EVENT format version
"exe":"2.20.1.155.g426c96fcdb" # git version
}
------------
@@ -613,6 +613,46 @@ stopping after the waitpid() and includes OS process creation overhead).
So this time will be slightly larger than the atexit time reported by
the child process itself.
+`"child_ready"`::
+ This event is generated after the current process has started
+ a background process and released all handles to it.
++
+------------
+{
+ "event":"child_ready",
+ ...
+ "child_id":2,
+ "pid":14708, # child PID
+ "ready":"ready", # child ready state
+ "t_rel":0.110605 # observed run-time of child process
+}
+------------
++
+Note that the session-id of the child process is not available to
+the current/spawning process, so the child's PID is reported here as
+a hint for post-processing. (But it is only a hint because the child
+process may be a shell script which doesn't have a session-id.)
++
+This event is generated after the child is started in the background
+and given a little time to boot up and start working. If the child
+startups normally and while the parent is still waiting, the "ready"
+field will have the value "ready".
+If the child is too slow to start and the parent times out, the field
+will have the value "timeout".
+If the child starts but the parent is unable to probe it, the field
+will have the value "error".
++
+After the parent process emits this event, it will release all of its
+handles to the child process and treat the child as a background
+daemon. So even if the child does eventually finish booting up,
+the parent will not emit an updated event.
++
+Note that the `t_rel` field contains the observed run time in seconds
+when the parent released the child process into the background.
+The child is assumed to be a long-running daemon process and may
+outlive the parent process. So the parent's child event times should
+not be compared to the child's atexit times.
+
`"exec"`::
This event is generated before git attempts to `exec()`
another command rather than starting a child process.
diff --git a/Documentation/technical/bitmap-format.txt b/Documentation/technical/bitmap-format.txt
index f8c18a0..04b3ec2 100644
--- a/Documentation/technical/bitmap-format.txt
+++ b/Documentation/technical/bitmap-format.txt
@@ -1,6 +1,44 @@
GIT bitmap v1 format
====================
+== Pack and multi-pack bitmaps
+
+Bitmaps store reachability information about the set of objects in a packfile,
+or a multi-pack index (MIDX). The former is defined obviously, and the latter is
+defined as the union of objects in packs contained in the MIDX.
+
+A bitmap may belong to either one pack, or the repository's multi-pack index (if
+it exists). A repository may have at most one bitmap.
+
+An object is uniquely described by its bit position within a bitmap:
+
+ - If the bitmap belongs to a packfile, the __n__th bit corresponds to
+ the __n__th object in pack order. For a function `offset` which maps
+ objects to their byte offset within a pack, pack order is defined as
+ follows:
+
+ o1 <= o2 <==> offset(o1) <= offset(o2)
+
+ - If the bitmap belongs to a MIDX, the __n__th bit corresponds to the
+ __n__th object in MIDX order. With an additional function `pack` which
+ maps objects to the pack they were selected from by the MIDX, MIDX order
+ is defined as follows:
+
+ o1 <= o2 <==> pack(o1) <= pack(o2) /\ offset(o1) <= offset(o2)
+
+ The ordering between packs is done according to the MIDX's .rev file.
+ Notably, the preferred pack sorts ahead of all other packs.
+
+The on-disk representation (described below) of a bitmap is the same regardless
+of whether or not that bitmap belongs to a packfile or a MIDX. The only
+difference is the interpretation of the bits, which is described above.
+
+Certain bitmap extensions are supported (see: Appendix B). No extensions are
+required for bitmaps corresponding to packfiles. For bitmaps that correspond to
+MIDXs, both the bit-cache and rev-cache extensions are required.
+
+== On-disk format
+
- A header appears at the beginning:
4-byte signature: {'B', 'I', 'T', 'M'}
@@ -14,17 +52,19 @@ GIT bitmap v1 format
The following flags are supported:
- BITMAP_OPT_FULL_DAG (0x1) REQUIRED
- This flag must always be present. It implies that the bitmap
- index has been generated for a packfile with full closure
- (i.e. where every single object in the packfile can find
- its parent links inside the same packfile). This is a
- requirement for the bitmap index format, also present in JGit,
- that greatly reduces the complexity of the implementation.
+ This flag must always be present. It implies that the
+ bitmap index has been generated for a packfile or
+ multi-pack index (MIDX) with full closure (i.e. where
+ every single object in the packfile/MIDX can find its
+ parent links inside the same packfile/MIDX). This is a
+ requirement for the bitmap index format, also present in
+ JGit, that greatly reduces the complexity of the
+ implementation.
- BITMAP_OPT_HASH_CACHE (0x4)
If present, the end of the bitmap file contains
`N` 32-bit name-hash values, one per object in the
- pack. The format and meaning of the name-hash is
+ pack/MIDX. The format and meaning of the name-hash is
described below.
4-byte entry count (network byte order)
@@ -33,7 +73,8 @@ GIT bitmap v1 format
20-byte checksum
- The SHA1 checksum of the pack this bitmap index belongs to.
+ The SHA1 checksum of the pack/MIDX this bitmap index
+ belongs to.
- 4 EWAH bitmaps that act as type indexes
@@ -50,7 +91,7 @@ GIT bitmap v1 format
- Tags
In each bitmap, the `n`th bit is set to true if the `n`th object
- in the packfile is of that type.
+ in the packfile or multi-pack index is of that type.
The obvious consequence is that the OR of all 4 bitmaps will result
in a full set (all bits set), and the AND of all 4 bitmaps will
@@ -62,8 +103,9 @@ GIT bitmap v1 format
Each entry contains the following:
- 4-byte object position (network byte order)
- The position **in the index for the packfile** where the
- bitmap for this commit is found.
+ The position **in the index for the packfile or
+ multi-pack index** where the bitmap for this commit is
+ found.
- 1-byte XOR-offset
The xor offset used to compress this bitmap. For an entry
@@ -146,10 +188,11 @@ Name-hash cache
---------------
If the BITMAP_OPT_HASH_CACHE flag is set, the end of the bitmap contains
-a cache of 32-bit values, one per object in the pack. The value at
+a cache of 32-bit values, one per object in the pack/MIDX. The value at
position `i` is the hash of the pathname at which the `i`th object
-(counting in index order) in the pack can be found. This can be fed
-into the delta heuristics to compare objects with similar pathnames.
+(counting in index or multi-pack index order) in the pack/MIDX can be found.
+This can be fed into the delta heuristics to compare objects with similar
+pathnames.
The hash algorithm used is:
diff --git a/Documentation/technical/http-protocol.txt b/Documentation/technical/http-protocol.txt
index 96d89ea..cc5126c 100644
--- a/Documentation/technical/http-protocol.txt
+++ b/Documentation/technical/http-protocol.txt
@@ -225,6 +225,9 @@ The client may send Extra Parameters (see
Documentation/technical/pack-protocol.txt) as a colon-separated string
in the Git-Protocol HTTP header.
+Uses the `--http-backend-info-refs` option to
+linkgit:git-upload-pack[1].
+
Dumb Server Response
^^^^^^^^^^^^^^^^^^^^
Dumb servers MUST respond with the dumb server reply format.
diff --git a/Documentation/technical/multi-pack-index.txt b/Documentation/technical/multi-pack-index.txt
index fb68897..86f40f2 100644
--- a/Documentation/technical/multi-pack-index.txt
+++ b/Documentation/technical/multi-pack-index.txt
@@ -36,7 +36,9 @@ Design Details
directory of an alternate. It refers only to packfiles in that
same directory.
-- The core.multiPackIndex config setting must be on to consume MIDX files.
+- The core.multiPackIndex config setting must be on (which is the
+ default) to consume MIDX files. Setting it to `false` prevents
+ Git from reading a MIDX file, even if one exists.
- The file format includes parameters for the object ID hash
function, so a future change of hash algorithm does not require
@@ -71,14 +73,10 @@ Future Work
still reducing the number of binary searches required for object
lookups.
-- The reachability bitmap is currently paired directly with a single
- packfile, using the pack-order as the object order to hopefully
- compress the bitmaps well using run-length encoding. This could be
- extended to pair a reachability bitmap with a multi-pack-index. If
- the multi-pack-index is extended to store a "stable object order"
+- If the multi-pack-index is extended to store a "stable object order"
(a function Order(hash) = integer that is constant for a given hash,
- even as the multi-pack-index is updated) then a reachability bitmap
- could point to a multi-pack-index and be updated independently.
+ even as the multi-pack-index is updated) then MIDX bitmaps could be
+ updated independently of the MIDX.
- Packfiles can be marked as "special" using empty files that share
the initial name but replace ".pack" with ".keep" or ".promisor".
diff --git a/Documentation/technical/protocol-v2.txt b/Documentation/technical/protocol-v2.txt
index 1040d85..21e8258 100644
--- a/Documentation/technical/protocol-v2.txt
+++ b/Documentation/technical/protocol-v2.txt
@@ -42,7 +42,8 @@ Initial Client Request
In general a client can request to speak protocol v2 by sending
`version=2` through the respective side-channel for the transport being
used which inevitably sets `GIT_PROTOCOL`. More information can be
-found in `pack-protocol.txt` and `http-protocol.txt`. In all cases the
+found in `pack-protocol.txt` and `http-protocol.txt`, as well as the
+`GIT_PROTOCOL` definition in `git.txt`. In all cases the
response from the server is the capability advertisement.
Git Transport
@@ -58,6 +59,8 @@ SSH and File Transport
When using either the ssh:// or file:// transport, the GIT_PROTOCOL
environment variable must be set explicitly to include "version=2".
+The server may need to be configured to allow this environment variable
+to pass.
HTTP Transport
~~~~~~~~~~~~~~
@@ -81,6 +84,12 @@ A v2 server would reply:
Subsequent requests are then made directly to the service
`$GIT_URL/git-upload-pack`. (This works the same for git-receive-pack).
+Uses the `--http-backend-info-refs` option to
+linkgit:git-upload-pack[1].
+
+The server may need to be configured to pass this header's contents via
+the `GIT_PROTOCOL` variable. See the discussion in `git-http-backend.txt`.
+
Capability Advertisement
------------------------
@@ -190,7 +199,11 @@ ls-refs takes in the following arguments:
Show peeled tags.
ref-prefix <prefix>
When specified, only references having a prefix matching one of
- the provided prefixes are displayed.
+ the provided prefixes are displayed. Multiple instances may be
+ given, in which case references matching any prefix will be
+ shown. Note that this is purely for optimization; a server MAY
+ show refs not matching the prefix if it chooses, and clients
+ should filter the result themselves.
If the 'unborn' feature is advertised the following argument can be
included in the client's request.
diff --git a/Documentation/technical/signature-format.txt b/Documentation/technical/signature-format.txt
index 2c9406a..166721b 100644
--- a/Documentation/technical/signature-format.txt
+++ b/Documentation/technical/signature-format.txt
@@ -13,6 +13,22 @@ Signatures always begin with `-----BEGIN PGP SIGNATURE-----`
and end with `-----END PGP SIGNATURE-----`, unless gpg is told to
produce RFC1991 signatures which use `MESSAGE` instead of `SIGNATURE`.
+Signatures sometimes appear as a part of the normal payload
+(e.g. a signed tag has the signature block appended after the payload
+that the signature applies to), and sometimes appear in the value of
+an object header (e.g. a merge commit that merged a signed tag would
+have the entire tag contents on its "mergetag" header). In the case
+of the latter, the usual multi-line formatting rule for object
+headers applies. I.e. the second and subsequent lines are prefixed
+with a SP to signal that the line is continued from the previous
+line.
+
+This is even true for an originally empty line. In the following
+examples, the end of line that ends with a whitespace letter is
+highlighted with a `$` sign; if you are trying to recreate these
+example by hand, do not cut and paste them---they are there
+primarily to highlight extra whitespace at the end of some lines.
+
The signed payload and the way the signature is embedded depends
on the type of the object resp. transaction.
@@ -78,7 +94,7 @@ author A U Thor <author@example.com> 1465981137 +0000
committer C O Mitter <committer@example.com> 1465981137 +0000
gpgsig -----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
-
+ $
iQEcBAABAgAGBQJXYRjRAAoJEGEJLoW3InGJ3IwIAIY4SA6GxY3BjL60YyvsJPh/
HRCJwH+w7wt3Yc/9/bW2F+gF72kdHOOs2jfv+OZhq0q4OAN6fvVSczISY/82LpS7
DVdMQj2/YcHDT4xrDNBnXnviDO9G7am/9OE77kEbXrp7QPxvhjkicHNwy2rEflAA
@@ -128,13 +144,13 @@ mergetag object 04b871796dc0420f8e7561a895b52484b701d51a
type commit
tag signedtag
tagger C O Mitter <committer@example.com> 1465981006 +0000
-
+ $
signed tag
-
+ $
signed tag message body
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
-
+ $
iQEcBAABAgAGBQJXYRhOAAoJEGEJLoW3InGJklkIAIcnhL7RwEb/+QeX9enkXhxn
rxfdqrvWd1K80sl2TOt8Bg/NYwrUBw/RWJ+sg/hhHp4WtvE1HDGHlkEz3y11Lkuh
8tSxS3qKTxXUGozyPGuE90sJfExhZlW4knIQ1wt/yWqM+33E9pN4hzPqLwyrdods
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index b48559d..8d2297c 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.33.GIT
+DEF_VER=v2.34.GIT
LF='
'
diff --git a/INSTALL b/INSTALL
index 66389ce..4140a3f 100644
--- a/INSTALL
+++ b/INSTALL
@@ -138,12 +138,15 @@ Issues of note:
BLK_SHA1. Also included is a version optimized for PowerPC
(PPC_SHA1).
- - "libcurl" library is used by git-http-fetch, git-fetch, and, if
- the curl version >= 7.34.0, for git-imap-send. You might also
- want the "curl" executable for debugging purposes. If you do not
- use http:// or https:// repositories, and do not want to put
- patches into an IMAP mailbox, you do not have to have them
- (use NO_CURL).
+ - "libcurl" library is used for fetching and pushing
+ repositories over http:// or https://, as well as by
+ git-imap-send if the curl version is >= 7.34.0. If you do
+ not need that functionality, use NO_CURL to build without
+ it.
+
+ Git requires version "7.19.4" or later of "libcurl" to build
+ without NO_CURL. This version requirement may be bumped in
+ the future.
- "expat" library; git-http-push uses it for remote lock
management over DAV. Similar to "curl" above, this is optional
diff --git a/Makefile b/Makefile
index b90af71..d56c0e4 100644
--- a/Makefile
+++ b/Makefile
@@ -305,9 +305,6 @@ all::
#
# Define NO_TCLTK if you do not want Tcl/Tk GUI.
#
-# Define SANE_TEXT_GREP to "-a" if you use recent versions of GNU grep
-# and egrep that are pickier when their input contains non-ASCII data.
-#
# The TCL_PATH variable governs the location of the Tcl interpreter
# used to optimize git-gui for your system. Only used if NO_TCLTK
# is not set. Defaults to the bare 'tclsh'.
@@ -409,15 +406,6 @@ all::
# Define NEEDS_LIBRT if your platform requires linking with librt (glibc version
# before 2.17) for clock_gettime and CLOCK_MONOTONIC.
#
-# Define USE_PARENS_AROUND_GETTEXT_N to "yes" if your compiler happily
-# compiles the following initialization:
-#
-# static const char s[] = ("FOO");
-#
-# and define it to "no" if you need to remove the parentheses () around the
-# constant. The default is "auto", which means to use parentheses if your
-# compiler is detected to support it.
-#
# Define HAVE_BSD_SYSCTL if your platform has a BSD-compatible sysctl function.
#
# Define HAVE_GETDELIM if your system has the getdelim() function.
@@ -498,10 +486,9 @@ all::
# setting this flag the exceptions are removed, and all of
# -Wextra is used.
#
-# pedantic:
+# no-pedantic:
#
-# Enable -pedantic compilation. This also disables
-# USE_PARENS_AROUND_GETTEXT_N to produce only relevant warnings.
+# Disable -pedantic compilation.
GIT-VERSION-FILE: FORCE
@$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -619,7 +606,6 @@ SCRIPT_SH += git-submodule.sh
SCRIPT_SH += git-web--browse.sh
SCRIPT_LIB += git-mergetool--lib
-SCRIPT_LIB += git-rebase--preserve-merges
SCRIPT_LIB += git-sh-i18n
SCRIPT_LIB += git-sh-setup
@@ -827,6 +813,10 @@ XDIFF_LIB = xdiff/lib.a
GENERATED_H += command-list.h
GENERATED_H += config-list.h
+GENERATED_H += hook-list.h
+
+.PHONY: generated-hdrs
+generated-hdrs: $(GENERATED_H)
LIB_H := $(sort $(patsubst ./%,%,$(shell git ls-files '*.h' ':!t/' ':!Documentation/' 2>/dev/null || \
$(FIND) . \
@@ -912,6 +902,7 @@ LIB_OBJS += hash-lookup.o
LIB_OBJS += hashmap.o
LIB_OBJS += help.o
LIB_OBJS += hex.o
+LIB_OBJS += hook.o
LIB_OBJS += ident.o
LIB_OBJS += json-writer.o
LIB_OBJS += kwset.o
@@ -1224,6 +1215,9 @@ PTHREAD_CFLAGS =
SPARSE_FLAGS ?=
SP_EXTRA_FLAGS = -Wno-universal-initializer
+# For informing GIT-BUILD-OPTIONS of the SANITIZE=leak target
+SANITIZE_LEAK =
+
# For the 'coccicheck' target; setting SPATCH_BATCH_SIZE higher will
# usually result in less CPU usage at the cost of higher peak memory.
# Setting it to 0 will feed all files in a single spatch invocation.
@@ -1268,6 +1262,7 @@ BASIC_CFLAGS += -DSHA1DC_FORCE_ALIGNED_ACCESS
endif
ifneq ($(filter leak,$(SANITIZERS)),)
BASIC_CFLAGS += -DSUPPRESS_ANNOTATED_LEAKS
+SANITIZE_LEAK = YesCompiledWithIt
endif
ifneq ($(filter address,$(SANITIZERS)),)
NO_REGEX = NeededForASAN
@@ -1288,6 +1283,7 @@ endif
ifeq ($(COMPUTE_HEADER_DEPENDENCIES),auto)
dep_check = $(shell $(CC) $(ALL_CFLAGS) \
+ -Wno-pedantic \
-c -MF /dev/null -MQ /dev/null -MMD -MP \
-x c /dev/null -o /dev/null 2>&1; \
echo $$?)
@@ -1313,6 +1309,7 @@ endif
ifeq ($(GENERATE_COMPILATION_DATABASE),yes)
compdb_check = $(shell $(CC) $(ALL_CFLAGS) \
+ -Wno-pedantic \
-c -MJ /dev/null \
-x c /dev/null -o /dev/null 2>&1; \
echo $$?)
@@ -1350,14 +1347,6 @@ ifneq (,$(SOCKLEN_T))
BASIC_CFLAGS += -Dsocklen_t=$(SOCKLEN_T)
endif
-ifeq (yes,$(USE_PARENS_AROUND_GETTEXT_N))
- BASIC_CFLAGS += -DUSE_PARENS_AROUND_GETTEXT_N=1
-else
-ifeq (no,$(USE_PARENS_AROUND_GETTEXT_N))
- BASIC_CFLAGS += -DUSE_PARENS_AROUND_GETTEXT_N=0
-endif
-endif
-
ifeq ($(uname_S),Darwin)
ifndef NO_FINK
ifeq ($(shell test -d /sw/lib && echo y),y)
@@ -1439,15 +1428,8 @@ else
REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES)
PROGRAM_OBJS += http-fetch.o
PROGRAMS += $(REMOTE_CURL_NAMES)
- curl_check := $(shell (echo 070908; $(CURL_CONFIG) --vernum | sed -e '/^70[BC]/s/^/0/') 2>/dev/null | sort -r | sed -ne 2p)
- ifeq "$(curl_check)" "070908"
- ifndef NO_EXPAT
- PROGRAM_OBJS += http-push.o
- else
- EXCLUDED_PROGRAMS += git-http-push
- endif
- else
- EXCLUDED_PROGRAMS += git-http-push
+ ifndef NO_EXPAT
+ PROGRAM_OBJS += http-push.o
endif
curl_check := $(shell (echo 072200; $(CURL_CONFIG) --vernum | sed -e '/^70[BC]/s/^/0/') 2>/dev/null | sort -r | sed -ne 2p)
ifeq "$(curl_check)" "072200"
@@ -2230,8 +2212,9 @@ git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)
$(filter %.o,$^) $(LIBS)
help.sp help.s help.o: command-list.h
+hook.sp hook.s hook.o: hook-list.h
-builtin/help.sp builtin/help.s builtin/help.o: config-list.h GIT-PREFIX
+builtin/help.sp builtin/help.s builtin/help.o: config-list.h hook-list.h GIT-PREFIX
builtin/help.sp builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \
'-DGIT_HTML_PATH="$(htmldir_relative_SQ)"' \
'-DGIT_MAN_PATH="$(mandir_relative_SQ)"' \
@@ -2254,43 +2237,42 @@ $(BUILT_INS): git$X
config-list.h: generate-configlist.sh
config-list.h: Documentation/*config.txt Documentation/config/*.txt
- $(QUIET_GEN)$(SHELL_PATH) ./generate-configlist.sh \
- >$@+ && mv $@+ $@
+ $(QUIET_GEN)$(SHELL_PATH) ./generate-configlist.sh >$@
command-list.h: generate-cmdlist.sh command-list.txt
command-list.h: $(wildcard Documentation/git*.txt)
$(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh \
$(patsubst %,--exclude-program %,$(EXCLUDED_PROGRAMS)) \
- command-list.txt >$@+ && mv $@+ $@
+ command-list.txt >$@
+
+hook-list.h: generate-hooklist.sh Documentation/githooks.txt
+ $(QUIET_GEN)$(SHELL_PATH) ./generate-hooklist.sh >$@
-SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
- $(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
- $(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP):$(PAGER_ENV):\
+SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):\
+ $(localedir_SQ):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
+ $(gitwebdir_SQ):$(PERL_PATH_SQ):$(PAGER_ENV):\
$(perllibdir_SQ)
+GIT-SCRIPT-DEFINES: FORCE
+ @FLAGS='$(SCRIPT_DEFINES)'; \
+ if test x"$$FLAGS" != x"`cat $@ 2>/dev/null`" ; then \
+ echo >&2 " * new script parameters"; \
+ echo "$$FLAGS" >$@; \
+ fi
+
define cmd_munge_script
sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
-e 's|@@DIFF@@|$(DIFF_SQ)|' \
-e 's|@@LOCALEDIR@@|$(localedir_SQ)|g' \
- -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-e 's/@@USE_GETTEXT_SCHEME@@/$(USE_GETTEXT_SCHEME)/g' \
-e $(BROKEN_PATH_FIX) \
-e 's|@@GITWEBDIR@@|$(gitwebdir_SQ)|g' \
-e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
- -e 's|@@SANE_TEXT_GREP@@|$(SANE_TEXT_GREP)|g' \
-e 's|@@PAGER_ENV@@|$(PAGER_ENV_SQ)|g' \
$@.sh >$@+
endef
-GIT-SCRIPT-DEFINES: FORCE
- @FLAGS='$(SCRIPT_DEFINES)'; \
- if test x"$$FLAGS" != x"`cat $@ 2>/dev/null`" ; then \
- echo >&2 " * new script parameters"; \
- echo "$$FLAGS" >$@; \
- fi
-
-
$(SCRIPT_SH_GEN) : % : %.sh GIT-SCRIPT-DEFINES
$(QUIET_GEN)$(cmd_munge_script) && \
chmod +x $@+ && \
@@ -2484,7 +2466,6 @@ dep_args = -MF $(dep_file) -MQ $@ -MMD -MP
endif
ifneq ($(COMPUTE_HEADER_DEPENDENCIES),yes)
-dep_dirs =
missing_dep_dirs =
dep_args =
endif
@@ -2525,13 +2506,6 @@ ifneq ($(dep_files_present),)
include $(dep_files_present)
endif
else
-# Dependencies on header files, for platforms that do not support
-# the gcc -MMD option.
-#
-# Dependencies on automatically generated headers such as command-list.h
-# should _not_ be included here, since they are necessary even when
-# building an object for the first time.
-
$(OBJECTS): $(LIB_H) $(GENERATED_H)
endif
@@ -2656,7 +2630,6 @@ XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \
--keyword=__ --keyword=N__ --keyword="__n:1,2"
LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
LOCALIZED_SH = $(SCRIPT_SH)
-LOCALIZED_SH += git-rebase--preserve-merges.sh
LOCALIZED_SH += git-sh-setup.sh
LOCALIZED_PERL = $(SCRIPT_PERL)
@@ -2753,19 +2726,25 @@ FIND_SOURCE_FILES = ( \
| sed -e 's|^\./||' \
)
-$(ETAGS_TARGET): FORCE
- $(QUIET_GEN)$(RM) "$(ETAGS_TARGET)+" && \
- $(FIND_SOURCE_FILES) | xargs etags -a -o "$(ETAGS_TARGET)+" && \
- mv "$(ETAGS_TARGET)+" "$(ETAGS_TARGET)"
+FOUND_SOURCE_FILES = $(shell $(FIND_SOURCE_FILES))
-tags: FORCE
- $(QUIET_GEN)$(RM) tags+ && \
- $(FIND_SOURCE_FILES) | xargs ctags -a -o tags+ && \
- mv tags+ tags
+$(ETAGS_TARGET): $(FOUND_SOURCE_FILES)
+ $(QUIET_GEN)$(RM) $@+ && \
+ echo $(FOUND_SOURCE_FILES) | xargs etags -a -o $@+ && \
+ mv $@+ $@
+
+tags: $(FOUND_SOURCE_FILES)
+ $(QUIET_GEN)$(RM) $@+ && \
+ echo $(FOUND_SOURCE_FILES) | xargs ctags -a -o $@+ && \
+ mv $@+ $@
+
+cscope.out: $(FOUND_SOURCE_FILES)
+ $(QUIET_GEN)$(RM) $@+ && \
+ echo $(FOUND_SOURCE_FILES) | xargs cscope -f$@+ -b && \
+ mv $@+ $@
-cscope:
- $(RM) cscope*
- $(FIND_SOURCE_FILES) | xargs cscope -b
+.PHONY: cscope
+cscope: cscope.out
### Detect prefix changes
TRACK_PREFIX = $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ):\
@@ -2815,6 +2794,7 @@ GIT-BUILD-OPTIONS: FORCE
@echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+
@echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+
@echo DC_SHA1=\''$(subst ','\'',$(subst ','\'',$(DC_SHA1)))'\' >>$@+
+ @echo SANITIZE_LEAK=\''$(subst ','\'',$(subst ','\'',$(SANITIZE_LEAK)))'\' >>$@+
@echo X=\'$(X)\' >>$@+
ifdef TEST_OUTPUT_DIRECTORY
@echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+
@@ -2915,14 +2895,16 @@ check-sha1:: t/helper/test-tool$X
SP_OBJ = $(patsubst %.o,%.sp,$(C_OBJ))
-$(SP_OBJ): %.sp: %.c GIT-CFLAGS FORCE
+$(SP_OBJ): %.sp: %.c %.o
$(QUIET_SP)cgcc -no-compile $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) \
- $(SPARSE_FLAGS) $(SP_EXTRA_FLAGS) $<
+ -Wsparse-error \
+ $(SPARSE_FLAGS) $(SP_EXTRA_FLAGS) $< && \
+ >$@
-.PHONY: sparse $(SP_OBJ)
+.PHONY: sparse
sparse: $(SP_OBJ)
-EXCEPT_HDRS := command-list.h config-list.h unicode-width.h compat/% xdiff/%
+EXCEPT_HDRS := $(GENERATED_H) unicode-width.h compat/% xdiff/%
ifndef GCRYPT_SHA256
EXCEPT_HDRS += sha256/gcrypt.h
endif
@@ -2944,7 +2926,8 @@ hdr-check: $(HCO)
style:
git clang-format --style file --diff --extensions c,h
-check: config-list.h command-list.h
+.PHONY: check
+check: $(GENERATED_H)
@if sparse; \
then \
echo >&2 "Use 'make sparse' instead"; \
@@ -2954,7 +2937,7 @@ check: config-list.h command-list.h
exit 1; \
fi
-FOUND_C_SOURCES = $(filter %.c,$(shell $(FIND_SOURCE_FILES)))
+FOUND_C_SOURCES = $(filter %.c,$(FOUND_SOURCE_FILES))
COCCI_SOURCES = $(filter-out $(THIRD_PARTY_SOURCES),$(FOUND_C_SOURCES))
%.cocci.patch: %.cocci $(COCCI_SOURCES)
@@ -3246,6 +3229,7 @@ clean: profile-clean coverage-clean cocciclean
$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
$(RM) $(TEST_PROGRAMS)
$(RM) $(FUZZ_PROGRAMS)
+ $(RM) $(SP_OBJ)
$(RM) $(HCC)
$(RM) -r bin-wrappers $(dep_dirs) $(compdb_dir) compile_commands.json
$(RM) -r po/build/
@@ -3284,7 +3268,7 @@ endif
.PHONY: all install profile-clean cocciclean clean strip
.PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
-.PHONY: FORCE cscope
+.PHONY: FORCE
### Check documentation
#
diff --git a/RelNotes b/RelNotes
index 6c94469..c27c205 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.34.0.txt \ No newline at end of file
+Documentation/RelNotes/2.35.0.txt \ No newline at end of file
diff --git a/add-interactive.c b/add-interactive.c
index 36ebdbd..6498ae1 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -102,8 +102,12 @@ struct prefix_item_list {
int *selected; /* for multi-selections */
size_t min_length, max_length;
};
-#define PREFIX_ITEM_LIST_INIT \
- { STRING_LIST_INIT_DUP, STRING_LIST_INIT_NODUP, NULL, 1, 4 }
+#define PREFIX_ITEM_LIST_INIT { \
+ .items = STRING_LIST_INIT_DUP, \
+ .sorted = STRING_LIST_INIT_NODUP, \
+ .min_length = 1, \
+ .max_length = 4, \
+}
static void prefix_item_list_clear(struct prefix_item_list *list)
{
diff --git a/advice.c b/advice.c
index 2f5499a..1dfc91d 100644
--- a/advice.c
+++ b/advice.c
@@ -224,15 +224,16 @@ void advise_on_updating_sparse_paths(struct string_list *pathspec_list)
if (!pathspec_list->nr)
return;
- fprintf(stderr, _("The following pathspecs didn't match any"
- " eligible path, but they do match index\n"
- "entries outside the current sparse checkout:\n"));
+ fprintf(stderr, _("The following paths and/or pathspecs matched paths that exist\n"
+ "outside of your sparse-checkout definition, so will not be\n"
+ "updated in the index:\n"));
for_each_string_list_item(item, pathspec_list)
fprintf(stderr, "%s\n", item->string);
advise_if_enabled(ADVICE_UPDATE_SPARSE_PATH,
- _("Disable or modify the sparsity rules if you intend"
- " to update such entries."));
+ _("If you intend to update such entries, try one of the following:\n"
+ "* Use the --sparse option.\n"
+ "* Disable or modify the sparsity rules."));
}
void detach_advice(const char *new_name)
diff --git a/attr.c b/attr.c
index d029e68..79adaa5 100644
--- a/attr.c
+++ b/attr.c
@@ -14,6 +14,7 @@
#include "utf8.h"
#include "quote.h"
#include "thread-utils.h"
+#include "dir.h"
const char git_attr__true[] = "(builtin)true";
const char git_attr__false[] = "\0(builtin)false";
@@ -744,6 +745,20 @@ static struct attr_stack *read_attr_from_index(struct index_state *istate,
if (!istate)
return NULL;
+ /*
+ * The .gitattributes file only applies to files within its
+ * parent directory. In the case of cone-mode sparse-checkout,
+ * the .gitattributes file is sparse if and only if all paths
+ * within that directory are also sparse. Thus, don't load the
+ * .gitattributes file since it will not matter.
+ *
+ * In the case of a sparse index, it is critical that we don't go
+ * looking for a .gitattributes file, as doing so would cause the
+ * index to expand.
+ */
+ if (!path_in_cone_mode_sparse_checkout(path, istate))
+ return NULL;
+
buf = read_blob_data_from_index(istate, path, NULL);
if (!buf)
return NULL;
diff --git a/builtin.h b/builtin.h
index 16ecd55..8a58743 100644
--- a/builtin.h
+++ b/builtin.h
@@ -225,7 +225,6 @@ int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
int cmd_switch(int argc, const char **argv, const char *prefix);
int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
int cmd_tag(int argc, const char **argv, const char *prefix);
-int cmd_tar_tree(int argc, const char **argv, const char *prefix);
int cmd_unpack_file(int argc, const char **argv, const char *prefix);
int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
int cmd_update_index(int argc, const char **argv, const char *prefix);
diff --git a/builtin/add.c b/builtin/add.c
index 2244311..ef6b619 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -30,6 +30,7 @@ static int patch_interactive, add_interactive, edit_interactive;
static int take_worktree_changes;
static int add_renormalize;
static int pathspec_file_nul;
+static int include_sparse;
static const char *pathspec_from_file;
static int legacy_stash_p; /* support for the scripted `git stash` */
@@ -46,7 +47,9 @@ static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
struct cache_entry *ce = active_cache[i];
int err;
- if (ce_skip_worktree(ce))
+ if (!include_sparse &&
+ (ce_skip_worktree(ce) ||
+ !path_in_sparse_checkout(ce->name, &the_index)))
continue;
if (pathspec && !ce_path_match(&the_index, ce, pathspec, NULL))
@@ -94,6 +97,10 @@ static void update_callback(struct diff_queue_struct *q,
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
const char *path = p->one->path;
+
+ if (!include_sparse && !path_in_sparse_checkout(path, &the_index))
+ continue;
+
switch (fix_unmerged_status(p, data)) {
default:
die(_("unexpected diff status %c"), p->status);
@@ -147,7 +154,9 @@ static int renormalize_tracked_files(const struct pathspec *pathspec, int flags)
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
- if (ce_skip_worktree(ce))
+ if (!include_sparse &&
+ (ce_skip_worktree(ce) ||
+ !path_in_sparse_checkout(ce->name, &the_index)))
continue;
if (ce_stage(ce))
continue; /* do not touch unmerged paths */
@@ -190,8 +199,6 @@ static int refresh(int verbose, const struct pathspec *pathspec)
struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP;
int flags = REFRESH_IGNORE_SKIP_WORKTREE |
(verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET);
- struct pattern_list pl = { 0 };
- int sparse_checkout_enabled = !get_sparse_checkout_patterns(&pl);
seen = xcalloc(pathspec->nr, 1);
refresh_index(&the_index, flags, pathspec, seen,
@@ -199,12 +206,9 @@ static int refresh(int verbose, const struct pathspec *pathspec)
for (i = 0; i < pathspec->nr; i++) {
if (!seen[i]) {
const char *path = pathspec->items[i].original;
- int dtype = DT_REG;
if (matches_skip_worktree(pathspec, i, &skip_worktree_seen) ||
- (sparse_checkout_enabled &&
- !path_matches_pattern_list(path, strlen(path), NULL,
- &dtype, &pl, &the_index))) {
+ !path_in_sparse_checkout(path, &the_index)) {
string_list_append(&only_match_skip_worktree,
pathspec->items[i].original);
} else {
@@ -382,6 +386,7 @@ static struct option builtin_add_options[] = {
OPT_BOOL( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")),
OPT_BOOL( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")),
OPT_BOOL( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")),
+ OPT_BOOL(0, "sparse", &include_sparse, N_("allow updating entries outside of the sparse-checkout cone")),
OPT_STRING(0, "chmod", &chmod_arg, "(+|-)x",
N_("override the executable bit of the listed files")),
OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo,
@@ -447,6 +452,7 @@ static void check_embedded_repo(const char *path)
static int add_files(struct dir_struct *dir, int flags)
{
int i, exit_status = 0;
+ struct string_list matched_sparse_paths = STRING_LIST_INIT_NODUP;
if (dir->ignored_nr) {
fprintf(stderr, _(ignore_error));
@@ -460,6 +466,12 @@ static int add_files(struct dir_struct *dir, int flags)
}
for (i = 0; i < dir->nr; i++) {
+ if (!include_sparse &&
+ !path_in_sparse_checkout(dir->entries[i]->name, &the_index)) {
+ string_list_append(&matched_sparse_paths,
+ dir->entries[i]->name);
+ continue;
+ }
if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) {
if (!ignore_add_errors)
die(_("adding files failed"));
@@ -468,6 +480,14 @@ static int add_files(struct dir_struct *dir, int flags)
check_embedded_repo(dir->entries[i]->name);
}
}
+
+ if (matched_sparse_paths.nr) {
+ advise_on_updating_sparse_paths(&matched_sparse_paths);
+ exit_status = 1;
+ }
+
+ string_list_clear(&matched_sparse_paths, 0);
+
return exit_status;
}
@@ -632,7 +652,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (seen[i])
continue;
- if (matches_skip_worktree(&pathspec, i, &skip_worktree_seen)) {
+ if (!include_sparse &&
+ matches_skip_worktree(&pathspec, i, &skip_worktree_seen)) {
string_list_append(&only_match_skip_worktree,
pathspec.items[i].original);
continue;
diff --git a/builtin/am.c b/builtin/am.c
index ff7dd33..8677ea2 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -11,6 +11,7 @@
#include "parse-options.h"
#include "dir.h"
#include "run-command.h"
+#include "hook.h"
#include "quote.h"
#include "tempfile.h"
#include "lockfile.h"
@@ -1848,7 +1849,6 @@ next:
*/
if (!state->rebasing) {
am_destroy(state);
- close_object_store(the_repository->objects);
run_auto_maintenance(state->quiet);
}
}
@@ -1918,7 +1918,8 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
opts.dst_index = &the_index;
opts.update = 1;
opts.merge = 1;
- opts.reset = reset;
+ opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
+ opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
opts.fn = twoway_merge;
init_tree_desc(&t[0], head->buffer, head->size);
init_tree_desc(&t[1], remote->buffer, remote->size);
@@ -2106,7 +2107,8 @@ static void am_abort(struct am_state *state)
if (!has_orig_head)
oidcpy(&orig_head, the_hash_algo->empty_tree);
- clean_index(&curr_head, &orig_head);
+ if (clean_index(&curr_head, &orig_head))
+ die(_("failed to clean index"));
if (has_orig_head)
update_ref("am --abort", "HEAD", &orig_head,
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index f184eae..28a2e6a 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -18,10 +18,10 @@ 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")
static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
+static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
static const char * const git_bisect_helper_usage[] = {
N_("git bisect--helper --bisect-reset [<commit>]"),
- N_("git bisect--helper --bisect-next-check <good_term> <bad_term> [<term>]"),
N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"),
N_("git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]"
" [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"),
@@ -30,6 +30,8 @@ static const char * const git_bisect_helper_usage[] = {
N_("git bisect--helper --bisect-state (good|old) [<rev>...]"),
N_("git bisect--helper --bisect-replay <filename>"),
N_("git bisect--helper --bisect-skip [(<rev>|<range>)...]"),
+ N_("git bisect--helper --bisect-visualize"),
+ N_("git bisect--helper --bisect-run <cmd>..."),
NULL
};
@@ -143,6 +145,19 @@ static int append_to_file(const char *path, const char *format, ...)
return res;
}
+static int print_file_to_stdout(const char *path)
+{
+ int fd = open(path, O_RDONLY);
+ int ret = 0;
+
+ if (fd < 0)
+ return error_errno(_("cannot open file '%s' for reading"), path);
+ if (copy_fd(fd, 1) < 0)
+ ret = error_errno(_("failed to read '%s'"), path);
+ close(fd);
+ return ret;
+}
+
static int check_term_format(const char *term, const char *orig_term)
{
int res;
@@ -1036,6 +1051,125 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar
return res;
}
+static int bisect_visualize(struct bisect_terms *terms, const char **argv, int argc)
+{
+ struct strvec args = STRVEC_INIT;
+ int flags = RUN_COMMAND_NO_STDIN, res = 0;
+ struct strbuf sb = STRBUF_INIT;
+
+ if (bisect_next_check(terms, NULL) != 0)
+ return BISECT_FAILED;
+
+ if (!argc) {
+ if ((getenv("DISPLAY") || getenv("SESSIONNAME") || getenv("MSYSTEM") ||
+ getenv("SECURITYSESSIONID")) && exists_in_PATH("gitk")) {
+ strvec_push(&args, "gitk");
+ } else {
+ strvec_push(&args, "log");
+ flags |= RUN_GIT_CMD;
+ }
+ } else {
+ if (argv[0][0] == '-') {
+ strvec_push(&args, "log");
+ flags |= RUN_GIT_CMD;
+ } else if (strcmp(argv[0], "tig") && !starts_with(argv[0], "git"))
+ flags |= RUN_GIT_CMD;
+
+ strvec_pushv(&args, argv);
+ }
+
+ strvec_pushl(&args, "--bisect", "--", NULL);
+
+ strbuf_read_file(&sb, git_path_bisect_names(), 0);
+ sq_dequote_to_strvec(sb.buf, &args);
+ strbuf_release(&sb);
+
+ res = run_command_v_opt(args.v, flags);
+ strvec_clear(&args);
+ return res;
+}
+
+static int bisect_run(struct bisect_terms *terms, const char **argv, int argc)
+{
+ int res = BISECT_OK;
+ struct strbuf command = STRBUF_INIT;
+ struct strvec args = STRVEC_INIT;
+ struct strvec run_args = STRVEC_INIT;
+ const char *new_state;
+ int temporary_stdout_fd, saved_stdout;
+
+ if (bisect_next_check(terms, NULL))
+ return BISECT_FAILED;
+
+ if (argc)
+ sq_quote_argv(&command, argv);
+ else {
+ error(_("bisect run failed: no command provided."));
+ return BISECT_FAILED;
+ }
+
+ strvec_push(&run_args, command.buf);
+
+ while (1) {
+ strvec_clear(&args);
+
+ printf(_("running %s\n"), command.buf);
+ res = run_command_v_opt(run_args.v, RUN_USING_SHELL);
+
+ if (res < 0 || 128 <= res) {
+ error(_("bisect run failed: exit code %d from"
+ " '%s' is < 0 or >= 128"), res, command.buf);
+ strbuf_release(&command);
+ return res;
+ }
+
+ if (res == 125)
+ new_state = "skip";
+ else if (!res)
+ new_state = terms->term_good;
+ else
+ new_state = terms->term_bad;
+
+ temporary_stdout_fd = open(git_path_bisect_run(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
+
+ if (temporary_stdout_fd < 0)
+ return error_errno(_("cannot open file '%s' for writing"), git_path_bisect_run());
+
+ fflush(stdout);
+ saved_stdout = dup(1);
+ dup2(temporary_stdout_fd, 1);
+
+ res = bisect_state(terms, &new_state, 1);
+
+ fflush(stdout);
+ dup2(saved_stdout, 1);
+ close(saved_stdout);
+ close(temporary_stdout_fd);
+
+ print_file_to_stdout(git_path_bisect_run());
+
+ if (res == BISECT_ONLY_SKIPPED_LEFT)
+ error(_("bisect run cannot continue any more"));
+ else if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE) {
+ printf(_("bisect run success"));
+ res = BISECT_OK;
+ } else if (res == BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND) {
+ printf(_("bisect found first bad commit"));
+ res = BISECT_OK;
+ } else if (res) {
+ error(_("bisect run failed: 'git bisect--helper --bisect-state"
+ " %s' exited with error code %d"), args.v[0], res);
+ } else {
+ continue;
+ }
+
+ strbuf_release(&command);
+ strvec_clear(&args);
+ strvec_clear(&run_args);
+ return res;
+ }
+}
+
int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
{
enum {
@@ -1048,7 +1182,9 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
BISECT_STATE,
BISECT_LOG,
BISECT_REPLAY,
- BISECT_SKIP
+ BISECT_SKIP,
+ BISECT_VISUALIZE,
+ BISECT_RUN,
} cmdmode = 0;
int res = 0, nolog = 0;
struct option options[] = {
@@ -1070,6 +1206,10 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
N_("replay the bisection process from the given file"), BISECT_REPLAY),
OPT_CMDMODE(0, "bisect-skip", &cmdmode,
N_("skip some commits for checkout"), BISECT_SKIP),
+ OPT_CMDMODE(0, "bisect-visualize", &cmdmode,
+ N_("visualize the bisection"), BISECT_VISUALIZE),
+ OPT_CMDMODE(0, "bisect-run", &cmdmode,
+ N_("use <cmd>... to automatically bisect."), BISECT_RUN),
OPT_BOOL(0, "no-log", &nolog,
N_("no log for BISECT_WRITE")),
OPT_END()
@@ -1089,12 +1229,6 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
return error(_("--bisect-reset requires either no argument or a commit"));
res = bisect_reset(argc ? argv[0] : NULL);
break;
- case BISECT_NEXT_CHECK:
- if (argc != 2 && argc != 3)
- return error(_("--bisect-next-check requires 2 or 3 arguments"));
- set_terms(&terms, argv[1], argv[0]);
- res = bisect_next_check(&terms, argc == 3 ? argv[2] : NULL);
- break;
case BISECT_TERMS:
if (argc > 1)
return error(_("--bisect-terms requires 0 or 1 argument"));
@@ -1131,6 +1265,16 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
get_terms(&terms);
res = bisect_skip(&terms, argv, argc);
break;
+ case BISECT_VISUALIZE:
+ get_terms(&terms);
+ res = bisect_visualize(&terms, argv, argc);
+ break;
+ case BISECT_RUN:
+ if (!argc)
+ return error(_("bisect run failed: no command provided."));
+ get_terms(&terms);
+ res = bisect_run(&terms, argv, argc);
+ break;
default:
BUG("unknown subcommand %d", cmdmode);
}
diff --git a/builtin/blame.c b/builtin/blame.c
index 641523f..f9ee3f8 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -101,6 +101,16 @@ struct commit_info {
struct strbuf summary;
};
+#define COMMIT_INFO_INIT { \
+ .author = STRBUF_INIT, \
+ .author_mail = STRBUF_INIT, \
+ .author_tz = STRBUF_INIT, \
+ .committer = STRBUF_INIT, \
+ .committer_mail = STRBUF_INIT, \
+ .committer_tz = STRBUF_INIT, \
+ .summary = STRBUF_INIT, \
+}
+
/*
* Parse author/committer line in the commit object buffer
*/
@@ -160,18 +170,6 @@ static void get_ac_line(const char *inbuf, const char *what,
strbuf_add(name, namebuf, namelen);
}
-static void commit_info_init(struct commit_info *ci)
-{
-
- strbuf_init(&ci->author, 0);
- strbuf_init(&ci->author_mail, 0);
- strbuf_init(&ci->author_tz, 0);
- strbuf_init(&ci->committer, 0);
- strbuf_init(&ci->committer_mail, 0);
- strbuf_init(&ci->committer_tz, 0);
- strbuf_init(&ci->summary, 0);
-}
-
static void commit_info_destroy(struct commit_info *ci)
{
@@ -192,8 +190,6 @@ static void get_commit_info(struct commit *commit,
const char *subject, *encoding;
const char *message;
- commit_info_init(ret);
-
encoding = get_log_output_encoding();
message = logmsg_reencode(commit, NULL, encoding);
get_ac_line(message, "\nauthor ",
@@ -246,7 +242,7 @@ static void write_filename_info(struct blame_origin *suspect)
*/
static int emit_one_suspect_detail(struct blame_origin *suspect, int repeat)
{
- struct commit_info ci;
+ struct commit_info ci = COMMIT_INFO_INIT;
if (!repeat && (suspect->commit->object.flags & METAINFO_SHOWN))
return 0;
@@ -440,7 +436,7 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int
int cnt;
const char *cp;
struct blame_origin *suspect = ent->suspect;
- struct commit_info ci;
+ struct commit_info ci = COMMIT_INFO_INIT;
char hex[GIT_MAX_HEXSZ + 1];
int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
const char *default_color = NULL, *color = NULL, *reset = NULL;
@@ -630,7 +626,7 @@ static void find_alignment(struct blame_scoreboard *sb, int *option)
if (longest_file < num)
longest_file = num;
if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
- struct commit_info ci;
+ struct commit_info ci = COMMIT_INFO_INIT;
suspect->commit->object.flags |= METAINFO_SHOWN;
get_commit_info(suspect->commit, &ci, 1);
if (*option & OUTPUT_SHOW_EMAIL)
@@ -917,6 +913,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
for (;;) {
switch (parse_options_step(&ctx, options, blame_opt_usage)) {
+ case PARSE_OPT_NON_OPTION:
+ case PARSE_OPT_UNKNOWN:
+ break;
case PARSE_OPT_HELP:
case PARSE_OPT_ERROR:
exit(129);
diff --git a/builtin/branch.c b/builtin/branch.c
index 03c7b72..81b5c11 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -77,12 +77,11 @@ define_list_config_array(color_branch_slots);
static int git_branch_config(const char *var, const char *value, void *cb)
{
const char *slot_name;
- struct ref_sorting **sorting_tail = (struct ref_sorting **)cb;
if (!strcmp(var, "branch.sort")) {
if (!value)
return config_error_nonbool(var);
- parse_ref_sorting(sorting_tail, value);
+ string_list_append(cb, value);
return 0;
}
@@ -407,7 +406,8 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
return strbuf_detach(&fmt, NULL);
}
-static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, struct ref_format *format)
+static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting,
+ struct ref_format *format, struct string_list *output)
{
int i;
struct ref_array array;
@@ -427,7 +427,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
memset(&array, 0, sizeof(array));
- filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN);
+ filter_refs(&array, filter, filter->kind);
if (filter->verbose)
maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
@@ -449,7 +449,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
if (column_active(colopts)) {
assert(!filter->verbose && "--column and --verbose are incompatible");
/* format to a string_list to let print_columns() do its job */
- string_list_append(&output, out.buf);
+ string_list_append(output, out.buf);
} else {
fwrite(out.buf, 1, out.len, stdout);
putchar('\n');
@@ -624,7 +624,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
enum branch_track track;
struct ref_filter filter;
int icase = 0;
- static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+ static struct ref_sorting *sorting;
+ struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct ref_format format = REF_FORMAT_INIT;
struct option options[] = {
@@ -665,7 +666,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_MERGED(&filter, N_("print only branches that are merged")),
OPT_NO_MERGED(&filter, N_("print only branches that are not merged")),
OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")),
- OPT_REF_SORT(sorting_tail),
+ OPT_REF_SORT(&sorting_options),
OPT_CALLBACK(0, "points-at", &filter.points_at, N_("object"),
N_("print only branches of the object"), parse_opt_object_name),
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
@@ -682,7 +683,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_branch_usage, options);
- git_config(git_branch_config, sorting_tail);
+ git_config(git_branch_config, &sorting_options);
track = git_branch_track;
@@ -748,14 +749,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
* local branches 'refs/heads/...' and finally remote-tracking
* branches 'refs/remotes/...'.
*/
- if (!sorting)
- sorting = ref_default_sorting();
+ sorting = ref_sorting_options(&sorting_options);
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
ref_sorting_set_sort_flags_all(
sorting, REF_SORTING_DETACHED_HEAD_FIRST, 1);
- print_ref_list(&filter, sorting, &format);
+ print_ref_list(&filter, sorting, &format, &output);
print_columns(&output, colopts, NULL);
string_list_clear(&output, 0);
+ ref_sorting_release(sorting);
return 0;
} else if (edit_description) {
const char *branch_name;
diff --git a/builtin/bugreport.c b/builtin/bugreport.c
index 06ed10d..9de32bc 100644
--- a/builtin/bugreport.c
+++ b/builtin/bugreport.c
@@ -3,7 +3,8 @@
#include "strbuf.h"
#include "help.h"
#include "compat/compiler.h"
-#include "run-command.h"
+#include "hook.h"
+#include "hook-list.h"
static void get_system_info(struct strbuf *sys_info)
@@ -41,39 +42,7 @@ static void get_system_info(struct strbuf *sys_info)
static void get_populated_hooks(struct strbuf *hook_info, int nongit)
{
- /*
- * NEEDSWORK: Doesn't look like there is a list of all possible hooks;
- * so below is a transcription of `git help hooks`. Later, this should
- * be replaced with some programmatically generated list (generated from
- * doc or else taken from some library which tells us about all the
- * hooks)
- */
- static const char *hook[] = {
- "applypatch-msg",
- "pre-applypatch",
- "post-applypatch",
- "pre-commit",
- "pre-merge-commit",
- "prepare-commit-msg",
- "commit-msg",
- "post-commit",
- "pre-rebase",
- "post-checkout",
- "post-merge",
- "pre-push",
- "pre-receive",
- "update",
- "post-receive",
- "post-update",
- "push-to-checkout",
- "pre-auto-gc",
- "post-rewrite",
- "sendemail-validate",
- "fsmonitor-watchman",
- "p4-pre-submit",
- "post-index-change",
- };
- int i;
+ const char **p;
if (nongit) {
strbuf_addstr(hook_info,
@@ -81,9 +50,12 @@ static void get_populated_hooks(struct strbuf *hook_info, int nongit)
return;
}
- for (i = 0; i < ARRAY_SIZE(hook); i++)
- if (find_hook(hook[i]))
- strbuf_addf(hook_info, "%s\n", hook[i]);
+ for (p = hook_name_list; *p; p++) {
+ const char *hook = *p;
+
+ if (hook_exists(hook))
+ strbuf_addf(hook_info, "%s\n", hook);
+ }
}
static const char * const bugreport_usage[] = {
diff --git a/builtin/bundle.c b/builtin/bundle.c
index 053a51b..5a85d7c 100644
--- a/builtin/bundle.c
+++ b/builtin/bundle.c
@@ -39,8 +39,6 @@ static const char * const builtin_bundle_unbundle_usage[] = {
NULL
};
-static int verbose;
-
static int parse_options_cmd_bundle(int argc,
const char **argv,
const char* prefix,
@@ -162,10 +160,15 @@ static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix)
struct bundle_header header = BUNDLE_HEADER_INIT;
int bundle_fd = -1;
int ret;
+ int progress = isatty(2);
+
struct option options[] = {
+ OPT_BOOL(0, "progress", &progress,
+ N_("show progress meter")),
OPT_END()
};
char *bundle_file;
+ struct strvec extra_index_pack_args = STRVEC_INIT;
argc = parse_options_cmd_bundle(argc, argv, prefix,
builtin_bundle_unbundle_usage, options, &bundle_file);
@@ -177,7 +180,11 @@ static int cmd_bundle_unbundle(int argc, const char **argv, const char *prefix)
}
if (!startup_info->have_repository)
die(_("Need a repository to unbundle."));
- ret = !!unbundle(the_repository, &header, bundle_fd, 0) ||
+ if (progress)
+ strvec_pushl(&extra_index_pack_args, "-v", "--progress-title",
+ _("Unbundling objects"), NULL);
+ ret = !!unbundle(the_repository, &header, bundle_fd,
+ &extra_index_pack_args) ||
list_bundle_refs(&header, argc, argv);
bundle_header_release(&header);
cleanup:
@@ -188,7 +195,6 @@ cleanup:
int cmd_bundle(int argc, const char **argv, const char *prefix)
{
struct option options[] = {
- OPT__VERBOSE(&verbose, N_("be verbose; must be placed before a subcommand")),
OPT_END()
};
int result;
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 243fe68..86fc032 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -355,18 +355,34 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
}
}
+/*
+ * If "pack" is non-NULL, then "offset" is the byte offset within the pack from
+ * which the object may be accessed (though note that we may also rely on
+ * data->oid, too). If "pack" is NULL, then offset is ignored.
+ */
static void batch_object_write(const char *obj_name,
struct strbuf *scratch,
struct batch_options *opt,
- struct expand_data *data)
+ struct expand_data *data,
+ struct packed_git *pack,
+ off_t offset)
{
- if (!data->skip_object_info &&
- oid_object_info_extended(the_repository, &data->oid, &data->info,
- OBJECT_INFO_LOOKUP_REPLACE) < 0) {
- printf("%s missing\n",
- obj_name ? obj_name : oid_to_hex(&data->oid));
- fflush(stdout);
- return;
+ if (!data->skip_object_info) {
+ int ret;
+
+ if (pack)
+ ret = packed_object_info(the_repository, pack, offset,
+ &data->info);
+ else
+ ret = oid_object_info_extended(the_repository,
+ &data->oid, &data->info,
+ OBJECT_INFO_LOOKUP_REPLACE);
+ if (ret < 0) {
+ printf("%s missing\n",
+ obj_name ? obj_name : oid_to_hex(&data->oid));
+ fflush(stdout);
+ return;
+ }
}
strbuf_reset(scratch);
@@ -428,7 +444,7 @@ static void batch_one_object(const char *obj_name,
return;
}
- batch_object_write(obj_name, scratch, opt, data);
+ batch_object_write(obj_name, scratch, opt, data, NULL, 0);
}
struct object_cb_data {
@@ -442,7 +458,8 @@ static int batch_object_cb(const struct object_id *oid, void *vdata)
{
struct object_cb_data *data = vdata;
oidcpy(&data->expand->oid, oid);
- batch_object_write(NULL, data->scratch, data->opt, data->expand);
+ batch_object_write(NULL, data->scratch, data->opt, data->expand,
+ NULL, 0);
return 0;
}
@@ -463,21 +480,26 @@ static int collect_packed_object(const struct object_id *oid,
return 0;
}
-static int batch_unordered_object(const struct object_id *oid, void *vdata)
+static int batch_unordered_object(const struct object_id *oid,
+ struct packed_git *pack, off_t offset,
+ void *vdata)
{
struct object_cb_data *data = vdata;
if (oidset_insert(data->seen, oid))
return 0;
- return batch_object_cb(oid, data);
+ oidcpy(&data->expand->oid, oid);
+ batch_object_write(NULL, data->scratch, data->opt, data->expand,
+ pack, offset);
+ return 0;
}
static int batch_unordered_loose(const struct object_id *oid,
const char *path,
void *data)
{
- return batch_unordered_object(oid, data);
+ return batch_unordered_object(oid, NULL, 0, data);
}
static int batch_unordered_packed(const struct object_id *oid,
@@ -485,7 +507,9 @@ static int batch_unordered_packed(const struct object_id *oid,
uint32_t pos,
void *data)
{
- return batch_unordered_object(oid, data);
+ return batch_unordered_object(oid, pack,
+ nth_packed_object_offset(pack, pos),
+ data);
}
static int batch_objects(struct batch_options *opt)
@@ -529,6 +553,8 @@ static int batch_objects(struct batch_options *opt)
if (has_promisor_remote())
warning("This repository uses promisor remotes. Some objects may not be loaded.");
+ read_replace_refs = 0;
+
cb.opt = opt;
cb.expand = &data;
cb.scratch = &output;
diff --git a/builtin/checkout--worker.c b/builtin/checkout--worker.c
index fb9fd13..ede7dc3 100644
--- a/builtin/checkout--worker.c
+++ b/builtin/checkout--worker.c
@@ -82,8 +82,8 @@ static void worker_loop(struct checkout *state)
size_t i, nr = 0, alloc = 0;
while (1) {
- int len = packet_read(0, NULL, NULL, packet_buffer,
- sizeof(packet_buffer), 0);
+ int len = packet_read(0, packet_buffer, sizeof(packet_buffer),
+ 0);
if (len < 0)
BUG("packet_read() returned negative value");
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 8c69dcd..cbf73b8 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -646,7 +646,9 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,
opts.head_idx = -1;
opts.update = worktree;
opts.skip_unmerged = !worktree;
- opts.reset = 1;
+ opts.reset = o->force ? UNPACK_RESET_OVERWRITE_UNTRACKED :
+ UNPACK_RESET_PROTECT_UNTRACKED;
+ opts.preserve_ignored = (!o->force && !o->overwrite_ignore);
opts.merge = 1;
opts.fn = oneway_merge;
opts.verbose_update = o->show_progress;
@@ -746,11 +748,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
new_branch_info->commit ?
&new_branch_info->commit->object.oid :
&new_branch_info->oid, NULL);
- if (opts->overwrite_ignore) {
- topts.dir = xcalloc(1, sizeof(*topts.dir));
- topts.dir->flags |= DIR_SHOW_IGNORED;
- setup_standard_excludes(topts.dir);
- }
+ topts.preserve_ignored = !opts->overwrite_ignore;
tree = parse_tree_indirect(old_branch_info->commit ?
&old_branch_info->commit->object.oid :
the_hash_algo->empty_tree);
diff --git a/builtin/clone.c b/builtin/clone.c
index b93bcd4..fb377b2 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -217,120 +217,6 @@ static char *get_repo_path(const char *repo, int *is_bundle)
return canon;
}
-static char *guess_dir_name(const char *repo, int is_bundle, int is_bare)
-{
- const char *end = repo + strlen(repo), *start, *ptr;
- size_t len;
- char *dir;
-
- /*
- * Skip scheme.
- */
- start = strstr(repo, "://");
- if (start == NULL)
- start = repo;
- else
- start += 3;
-
- /*
- * Skip authentication data. The stripping does happen
- * greedily, such that we strip up to the last '@' inside
- * the host part.
- */
- for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) {
- if (*ptr == '@')
- start = ptr + 1;
- }
-
- /*
- * Strip trailing spaces, slashes and /.git
- */
- while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
- end--;
- if (end - start > 5 && is_dir_sep(end[-5]) &&
- !strncmp(end - 4, ".git", 4)) {
- end -= 5;
- while (start < end && is_dir_sep(end[-1]))
- end--;
- }
-
- /*
- * Strip trailing port number if we've got only a
- * hostname (that is, there is no dir separator but a
- * colon). This check is required such that we do not
- * strip URI's like '/foo/bar:2222.git', which should
- * result in a dir '2222' being guessed due to backwards
- * compatibility.
- */
- if (memchr(start, '/', end - start) == NULL
- && memchr(start, ':', end - start) != NULL) {
- ptr = end;
- while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':')
- ptr--;
- if (start < ptr && ptr[-1] == ':')
- end = ptr - 1;
- }
-
- /*
- * Find last component. To remain backwards compatible we
- * also regard colons as path separators, such that
- * cloning a repository 'foo:bar.git' would result in a
- * directory 'bar' being guessed.
- */
- ptr = end;
- while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':')
- ptr--;
- start = ptr;
-
- /*
- * Strip .{bundle,git}.
- */
- len = end - start;
- strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
-
- if (!len || (len == 1 && *start == '/'))
- die(_("No directory name could be guessed.\n"
- "Please specify a directory on the command line"));
-
- if (is_bare)
- dir = xstrfmt("%.*s.git", (int)len, start);
- else
- dir = xstrndup(start, len);
- /*
- * Replace sequences of 'control' characters and whitespace
- * with one ascii space, remove leading and trailing spaces.
- */
- if (*dir) {
- char *out = dir;
- int prev_space = 1 /* strip leading whitespace */;
- for (end = dir; *end; ++end) {
- char ch = *end;
- if ((unsigned char)ch < '\x20')
- ch = '\x20';
- if (isspace(ch)) {
- if (prev_space)
- continue;
- prev_space = 1;
- } else
- prev_space = 0;
- *out++ = ch;
- }
- *out = '\0';
- if (out > dir && prev_space)
- out[-1] = '\0';
- }
- return dir;
-}
-
-static void strip_trailing_slashes(char *dir)
-{
- char *end = dir + strlen(dir);
-
- while (dir < end - 1 && is_dir_sep(end[-1]))
- end--;
- *end = '\0';
-}
-
static int add_one_reference(struct string_list_item *item, void *cb_data)
{
struct strbuf err = STRBUF_INIT;
@@ -657,7 +543,7 @@ static void write_followtags(const struct ref *refs, const char *msg)
}
}
-static int iterate_ref_map(void *cb_data, struct object_id *oid)
+static const struct object_id *iterate_ref_map(void *cb_data)
{
struct ref **rm = cb_data;
struct ref *ref = *rm;
@@ -668,13 +554,11 @@ static int iterate_ref_map(void *cb_data, struct object_id *oid)
*/
while (ref && !ref->peer_ref)
ref = ref->next;
- /* Returning -1 notes "end of list" to the caller. */
if (!ref)
- return -1;
+ return NULL;
- oidcpy(oid, &ref->old_oid);
*rm = ref->next;
- return 0;
+ return &ref->old_oid;
}
static void update_remote_refs(const struct ref *refs,
@@ -803,6 +687,7 @@ static int checkout(int submodule_progress)
opts.update = 1;
opts.merge = 1;
opts.clone = 1;
+ opts.preserve_ignored = 0;
opts.fn = oneway_merge;
opts.verbose_update = (option_verbosity >= 0);
opts.src_index = &the_index;
@@ -1041,8 +926,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
if (argc == 2)
dir = xstrdup(argv[1]);
else
- dir = guess_dir_name(repo_name, is_bundle, option_bare);
- strip_trailing_slashes(dir);
+ dir = git_url_basename(repo_name, is_bundle, option_bare);
+ strip_dir_trailing_slashes(dir);
dest_exists = path_exists(dir);
if (dest_exists && !is_empty_dir(dir))
@@ -1155,8 +1040,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, NULL,
INIT_DB_QUIET);
- if (real_git_dir)
+ if (real_git_dir) {
+ free((char *)git_dir);
git_dir = real_git_dir;
+ }
/*
* additional config can be injected with -c, make sure it's included
@@ -1345,6 +1232,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
our_head_points_at = remote_head_points_at;
}
else {
+ const char *branch;
+ char *ref;
+
if (option_branch)
die(_("Remote branch %s not found in upstream %s"),
option_branch, remote_name);
@@ -1355,24 +1245,22 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
remote_head_points_at = NULL;
remote_head = NULL;
option_no_checkout = 1;
- if (!option_bare) {
- const char *branch;
- char *ref;
-
- if (transport_ls_refs_options.unborn_head_target &&
- skip_prefix(transport_ls_refs_options.unborn_head_target,
- "refs/heads/", &branch)) {
- ref = transport_ls_refs_options.unborn_head_target;
- transport_ls_refs_options.unborn_head_target = NULL;
- create_symref("HEAD", ref, reflog_msg.buf);
- } else {
- branch = git_default_branch_name(0);
- ref = xstrfmt("refs/heads/%s", branch);
- }
- install_branch_config(0, branch, remote_name, ref);
- free(ref);
+ if (transport_ls_refs_options.unborn_head_target &&
+ skip_prefix(transport_ls_refs_options.unborn_head_target,
+ "refs/heads/", &branch)) {
+ ref = transport_ls_refs_options.unborn_head_target;
+ transport_ls_refs_options.unborn_head_target = NULL;
+ create_symref("HEAD", ref, reflog_msg.buf);
+ } else {
+ branch = git_default_branch_name(0);
+ ref = xstrfmt("refs/heads/%s", branch);
}
+
+ if (!option_bare)
+ install_branch_config(0, branch, remote_name, ref);
+
+ free(ref);
}
write_refspec_config(src_ref_prefix, our_head_points_at,
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index 21fc6e9..4247fbd 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -50,8 +50,6 @@ static struct option common_opts[] = {
OPT_STRING(0, "object-dir", &opts.obj_dir,
N_("dir"),
N_("the object directory to store the graph")),
- OPT_BOOL(0, "progress", &opts.progress,
- N_("force progress reporting")),
OPT_END()
};
@@ -60,28 +58,6 @@ static struct option *add_common_options(struct option *to)
return parse_options_concat(common_opts, to);
}
-static struct object_directory *find_odb(struct repository *r,
- const char *obj_dir)
-{
- struct object_directory *odb;
- char *obj_dir_real = real_pathdup(obj_dir, 1);
- struct strbuf odb_path_real = STRBUF_INIT;
-
- prepare_alt_odb(r);
- for (odb = r->objects->odb; odb; odb = odb->next) {
- strbuf_realpath(&odb_path_real, odb->path, 1);
- if (!strcmp(obj_dir_real, odb_path_real.buf))
- break;
- }
-
- free(obj_dir_real);
- strbuf_release(&odb_path_real);
-
- if (!odb)
- die(_("could not find object directory matching %s"), obj_dir);
- return odb;
-}
-
static int graph_verify(int argc, const char **argv)
{
struct commit_graph *graph = NULL;
@@ -95,6 +71,8 @@ static int graph_verify(int argc, const char **argv)
static struct option builtin_commit_graph_verify_options[] = {
OPT_BOOL(0, "shallow", &opts.shallow,
N_("if the commit-graph is split, only verify the tip file")),
+ OPT_BOOL(0, "progress", &opts.progress,
+ N_("force progress reporting")),
OPT_END(),
};
struct option *options = add_common_options(builtin_commit_graph_verify_options);
@@ -194,8 +172,8 @@ static int write_option_max_new_filters(const struct option *opt,
const char *s;
*to = strtol(arg, (char **)&s, 10);
if (*s)
- return error(_("%s expects a numerical value"),
- optname(opt, opt->flags));
+ return error(_("option `%s' expects a numerical value"),
+ "max-new-filters");
}
return 0;
}
@@ -246,6 +224,8 @@ static int graph_write(int argc, const char **argv)
OPT_CALLBACK_F(0, "max-new-filters", &write_opts.max_new_filters,
NULL, N_("maximum number of changed-path Bloom filters to compute"),
0, write_option_max_new_filters),
+ OPT_BOOL(0, "progress", &opts.progress,
+ N_("force progress reporting")),
OPT_END(),
};
struct option *options = add_common_options(builtin_commit_graph_write_options);
@@ -283,7 +263,6 @@ static int graph_write(int argc, const char **argv)
git_env_bool(GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS, 0))
flags |= COMMIT_GRAPH_WRITE_BLOOM_FILTERS;
- read_replace_refs = 0;
odb = find_odb(the_repository, opts.obj_dir);
if (opts.reachable) {
@@ -338,6 +317,7 @@ int cmd_commit_graph(int argc, const char **argv, const char *prefix)
if (!argc)
goto usage;
+ read_replace_refs = 0;
save_commit_buffer = 0;
if (!strcmp(argv[0], "verify"))
diff --git a/builtin/commit.c b/builtin/commit.c
index e7320f6..883c162 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -19,6 +19,7 @@
#include "revision.h"
#include "wt-status.h"
#include "run-command.h"
+#include "hook.h"
#include "refs.h"
#include "log-tree.h"
#include "strbuf.h"
@@ -1051,7 +1052,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
return 0;
}
- if (!no_verify && find_hook("pre-commit")) {
+ if (!no_verify && hook_exists("pre-commit")) {
/*
* Re-read the index as pre-commit hook could have updated it,
* and write it out as a tree. We must do this before we invoke
diff --git a/builtin/config.c b/builtin/config.c
index 865fddd..542d8d0 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -575,7 +575,7 @@ static int get_urlmatch(const char *var, const char *url)
int ret;
char *section_tail;
struct string_list_item *item;
- struct urlmatch_config config = { STRING_LIST_INIT_DUP };
+ struct urlmatch_config config = URLMATCH_CONFIG_INIT;
struct string_list values = STRING_LIST_INIT_DUP;
config.collect_fn = urlmatch_collect_fn;
diff --git a/builtin/credential-cache.c b/builtin/credential-cache.c
index e8a7415..78c02ad 100644
--- a/builtin/credential-cache.c
+++ b/builtin/credential-cache.c
@@ -11,6 +11,32 @@
#define FLAG_SPAWN 0x1
#define FLAG_RELAY 0x2
+#ifdef GIT_WINDOWS_NATIVE
+
+static int connection_closed(int error)
+{
+ return (error == EINVAL);
+}
+
+static int connection_fatally_broken(int error)
+{
+ return (error != ENOENT) && (error != ENETDOWN);
+}
+
+#else
+
+static int connection_closed(int error)
+{
+ return (error == ECONNRESET);
+}
+
+static int connection_fatally_broken(int error)
+{
+ return (error != ENOENT) && (error != ECONNREFUSED);
+}
+
+#endif
+
static int send_request(const char *socket, const struct strbuf *out)
{
int got_data = 0;
@@ -28,7 +54,7 @@ static int send_request(const char *socket, const struct strbuf *out)
int r;
r = read_in_full(fd, in, sizeof(in));
- if (r == 0 || (r < 0 && errno == ECONNRESET))
+ if (r == 0 || (r < 0 && connection_closed(errno)))
break;
if (r < 0)
die_errno("read error from cache daemon");
@@ -75,7 +101,7 @@ static void do_cache(const char *socket, const char *action, int timeout,
}
if (send_request(socket, &buf) < 0) {
- if (errno != ENOENT && errno != ECONNREFUSED)
+ if (connection_fatally_broken(errno))
die_errno("unable to connect to cache daemon");
if (flags & FLAG_SPAWN) {
spawn_daemon(socket);
diff --git a/builtin/difftool.c b/builtin/difftool.c
index 6a9242a..4931c10 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -252,16 +252,6 @@ static void changed_files(struct hashmap *result, const char *index_path,
strbuf_release(&buf);
}
-static NORETURN void exit_cleanup(const char *tmpdir, int exit_code)
-{
- struct strbuf buf = STRBUF_INIT;
- strbuf_addstr(&buf, tmpdir);
- remove_dir_recursively(&buf, 0);
- if (exit_code)
- warning(_("failed: %d"), exit_code);
- exit(exit_code);
-}
-
static int ensure_leading_directories(char *path)
{
switch (safe_create_leading_directories(path)) {
@@ -330,19 +320,44 @@ static int checkout_path(unsigned mode, struct object_id *oid,
return ret;
}
+static void write_file_in_directory(struct strbuf *dir, size_t dir_len,
+ const char *path, const char *content)
+{
+ add_path(dir, dir_len, path);
+ ensure_leading_directories(dir->buf);
+ unlink(dir->buf);
+ write_file(dir->buf, "%s", content);
+}
+
+/* Write the file contents for the left and right sides of the difftool
+ * dir-diff representation for submodules and symlinks. Symlinks and submodules
+ * are written as regular text files so that external diff tools can diff them
+ * as text files, resulting in behavior that is analogous to to what "git diff"
+ * displays for symlink and submodule diffs.
+ */
+static void write_standin_files(struct pair_entry *entry,
+ struct strbuf *ldir, size_t ldir_len,
+ struct strbuf *rdir, size_t rdir_len)
+{
+ if (*entry->left)
+ write_file_in_directory(ldir, ldir_len, entry->path, entry->left);
+ if (*entry->right)
+ write_file_in_directory(rdir, rdir_len, entry->path, entry->right);
+}
+
static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
- int argc, const char **argv)
+ struct child_process *child)
{
- char tmpdir[PATH_MAX];
struct strbuf info = STRBUF_INIT, lpath = STRBUF_INIT;
struct strbuf rpath = STRBUF_INIT, buf = STRBUF_INIT;
struct strbuf ldir = STRBUF_INIT, rdir = STRBUF_INIT;
struct strbuf wtdir = STRBUF_INIT;
- char *lbase_dir, *rbase_dir;
+ struct strbuf tmpdir = STRBUF_INIT;
+ char *lbase_dir = NULL, *rbase_dir = NULL;
size_t ldir_len, rdir_len, wtdir_len;
const char *workdir, *tmp;
int ret = 0, i;
- FILE *fp;
+ FILE *fp = NULL;
struct hashmap working_tree_dups = HASHMAP_INIT(working_tree_entry_cmp,
NULL);
struct hashmap submodules = HASHMAP_INIT(pair_cmp, NULL);
@@ -351,8 +366,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
struct pair_entry *entry;
struct index_state wtindex;
struct checkout lstate, rstate;
- int rc, flags = RUN_GIT_CMD, err = 0;
- struct child_process child = CHILD_PROCESS_INIT;
+ int flags = RUN_GIT_CMD, err = 0;
const char *helper_argv[] = { "difftool--helper", NULL, NULL, NULL };
struct hashmap wt_modified, tmp_modified;
int indices_loaded = 0;
@@ -361,11 +375,15 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
/* Setup temp directories */
tmp = getenv("TMPDIR");
- xsnprintf(tmpdir, sizeof(tmpdir), "%s/git-difftool.XXXXXX", tmp ? tmp : "/tmp");
- if (!mkdtemp(tmpdir))
- return error("could not create '%s'", tmpdir);
- strbuf_addf(&ldir, "%s/left/", tmpdir);
- strbuf_addf(&rdir, "%s/right/", tmpdir);
+ strbuf_add_absolute_path(&tmpdir, tmp ? tmp : "/tmp");
+ strbuf_trim_trailing_dir_sep(&tmpdir);
+ strbuf_addstr(&tmpdir, "/git-difftool.XXXXXX");
+ if (!mkdtemp(tmpdir.buf)) {
+ ret = error("could not create '%s'", tmpdir.buf);
+ goto finish;
+ }
+ strbuf_addf(&ldir, "%s/left/", tmpdir.buf);
+ strbuf_addf(&rdir, "%s/right/", tmpdir.buf);
strbuf_addstr(&wtdir, workdir);
if (!wtdir.len || !is_dir_sep(wtdir.buf[wtdir.len - 1]))
strbuf_addch(&wtdir, '/');
@@ -387,19 +405,15 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
rdir_len = rdir.len;
wtdir_len = wtdir.len;
- child.no_stdin = 1;
- child.git_cmd = 1;
- child.use_shell = 0;
- child.clean_on_exit = 1;
- child.dir = prefix;
- child.out = -1;
- strvec_pushl(&child.args, "diff", "--raw", "--no-abbrev", "-z",
- NULL);
- for (i = 0; i < argc; i++)
- strvec_push(&child.args, argv[i]);
- if (start_command(&child))
+ child->no_stdin = 1;
+ child->git_cmd = 1;
+ child->use_shell = 0;
+ child->clean_on_exit = 1;
+ child->dir = prefix;
+ child->out = -1;
+ if (start_command(child))
die("could not obtain raw diff");
- fp = xfdopen(child.out, "r");
+ fp = xfdopen(child->out, "r");
/* Build index info for left and right sides of the diff */
i = 0;
@@ -410,9 +424,9 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
const char *src_path, *dst_path;
if (starts_with(info.buf, "::"))
- die(N_("combined diff formats('-c' and '--cc') are "
+ die(N_("combined diff formats ('-c' and '--cc') are "
"not supported in\n"
- "directory diff mode('-d' and '--dir-diff')."));
+ "directory diff mode ('-d' and '--dir-diff')."));
if (parse_index_info(info.buf, &lmode, &rmode, &loid, &roid,
&status))
@@ -525,7 +539,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
fclose(fp);
fp = NULL;
- if (finish_command(&child)) {
+ if (finish_command(child)) {
ret = error("error occurred running diff --raw");
goto finish;
}
@@ -540,38 +554,19 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
*/
hashmap_for_each_entry(&submodules, &iter, entry,
entry /* member name */) {
- if (*entry->left) {
- add_path(&ldir, ldir_len, entry->path);
- ensure_leading_directories(ldir.buf);
- write_file(ldir.buf, "%s", entry->left);
- }
- if (*entry->right) {
- add_path(&rdir, rdir_len, entry->path);
- ensure_leading_directories(rdir.buf);
- write_file(rdir.buf, "%s", entry->right);
- }
+ write_standin_files(entry, &ldir, ldir_len, &rdir, rdir_len);
}
/*
- * Symbolic links require special treatment.The standard "git diff"
+ * Symbolic links require special treatment. The standard "git diff"
* shows only the link itself, not the contents of the link target.
* This loop replicates that behavior.
*/
hashmap_for_each_entry(&symlinks2, &iter, entry,
entry /* member name */) {
- if (*entry->left) {
- add_path(&ldir, ldir_len, entry->path);
- ensure_leading_directories(ldir.buf);
- write_file(ldir.buf, "%s", entry->left);
- }
- if (*entry->right) {
- add_path(&rdir, rdir_len, entry->path);
- ensure_leading_directories(rdir.buf);
- write_file(rdir.buf, "%s", entry->right);
- }
- }
- strbuf_release(&buf);
+ write_standin_files(entry, &ldir, ldir_len, &rdir, rdir_len);
+ }
strbuf_setlen(&ldir, ldir_len);
helper_argv[1] = ldir.buf;
@@ -583,7 +578,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
flags = 0;
} else
setenv("GIT_DIFFTOOL_DIRDIFF", "true", 1);
- rc = run_command_v_opt(helper_argv, flags);
+ ret = run_command_v_opt(helper_argv, flags);
/* TODO: audit for interaction with sparse-index. */
ensure_full_index(&wtindex);
@@ -617,7 +612,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
if (!indices_loaded) {
struct lock_file lock = LOCK_INIT;
strbuf_reset(&buf);
- strbuf_addf(&buf, "%s/wtindex", tmpdir);
+ strbuf_addf(&buf, "%s/wtindex", tmpdir.buf);
if (hold_lock_file_for_update(&lock, buf.buf, 0) < 0 ||
write_locked_index(&wtindex, &lock, COMMIT_LOCK)) {
ret = error("could not write %s", buf.buf);
@@ -647,11 +642,14 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
}
if (err) {
- warning(_("temporary files exist in '%s'."), tmpdir);
+ warning(_("temporary files exist in '%s'."), tmpdir.buf);
warning(_("you may want to cleanup or recover these."));
- exit(1);
- } else
- exit_cleanup(tmpdir, rc);
+ ret = 1;
+ } else {
+ remove_dir_recursively(&tmpdir, 0);
+ if (ret)
+ warning(_("failed: %d"), ret);
+ }
finish:
if (fp)
@@ -663,30 +661,29 @@ finish:
strbuf_release(&rdir);
strbuf_release(&wtdir);
strbuf_release(&buf);
+ strbuf_release(&tmpdir);
- return ret;
+ return (ret < 0) ? 1 : ret;
}
static int run_file_diff(int prompt, const char *prefix,
- int argc, const char **argv)
+ struct child_process *child)
{
- struct strvec args = STRVEC_INIT;
const char *env[] = {
"GIT_PAGER=", "GIT_EXTERNAL_DIFF=git-difftool--helper", NULL,
NULL
};
- int i;
if (prompt > 0)
env[2] = "GIT_DIFFTOOL_PROMPT=true";
else if (!prompt)
env[2] = "GIT_DIFFTOOL_NO_PROMPT=true";
+ child->git_cmd = 1;
+ child->dir = prefix;
+ strvec_pushv(&child->env_array, env);
- strvec_push(&args, "diff");
- for (i = 0; i < argc; i++)
- strvec_push(&args, argv[i]);
- return run_command_v_opt_cd_env(args.v, RUN_GIT_CMD, prefix, env);
+ return run_command(child);
}
int cmd_difftool(int argc, const char **argv, const char *prefix)
@@ -713,12 +710,13 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
"`--tool`")),
OPT_BOOL(0, "trust-exit-code", &trust_exit_code,
N_("make 'git-difftool' exit when an invoked diff "
- "tool returns a non - zero exit code")),
+ "tool returns a non-zero exit code")),
OPT_STRING('x', "extcmd", &extcmd, N_("command"),
N_("specify a custom command for viewing diffs")),
- OPT_ARGUMENT("no-index", &no_index, N_("passed to `diff`")),
+ OPT_BOOL(0, "no-index", &no_index, N_("passed to `diff`")),
OPT_END()
};
+ struct child_process child = CHILD_PROCESS_INIT;
git_config(difftool_config, NULL);
symlinks = has_symlinks;
@@ -768,7 +766,14 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)
* will invoke a separate instance of 'git-difftool--helper' for
* each file that changed.
*/
+ strvec_push(&child.args, "diff");
+ if (no_index)
+ strvec_push(&child.args, "--no-index");
+ if (dir_diff)
+ strvec_pushl(&child.args, "--raw", "--no-abbrev", "-z", NULL);
+ strvec_pushv(&child.args, argv);
+
if (dir_diff)
- return run_dir_diff(extcmd, symlinks, prefix, argc, argv);
- return run_file_diff(prompt, prefix, argc, argv);
+ return run_dir_diff(extcmd, symlinks, prefix, &child);
+ return run_file_diff(prompt, prefix, &child);
}
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 95e8e89..8e2caf7 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -312,7 +312,7 @@ static void export_blob(const struct object_id *oid)
if (!buf)
die("could not read blob %s", oid_to_hex(oid));
if (check_object_signature(the_repository, oid, buf, size,
- type_name(type)) < 0)
+ type_name(type), NULL) < 0)
die("oid mismatch in blob %s", oid_to_hex(oid));
object = parse_object_buffer(the_repository, oid, type,
size, buf, &eaten);
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 38fab03..f7abbc3 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -854,13 +854,11 @@ static int update_local_ref(struct ref *ref,
int summary_width)
{
struct commit *current = NULL, *updated;
- enum object_type type;
struct branch *current_branch = branch_get(NULL);
const char *pretty_ref = prettify_refname(ref->name);
int fast_forward = 0;
- type = oid_object_info(the_repository, &ref->new_oid, NULL);
- if (type < 0)
+ if (!repo_has_object_file(the_repository, &ref->new_oid))
die(_("object %s not found"), oid_to_hex(&ref->new_oid));
if (oideq(&ref->old_oid, &ref->new_oid)) {
@@ -972,7 +970,7 @@ static int update_local_ref(struct ref *ref,
}
}
-static int iterate_ref_map(void *cb_data, struct object_id *oid)
+static const struct object_id *iterate_ref_map(void *cb_data)
{
struct ref **rm = cb_data;
struct ref *ref = *rm;
@@ -980,10 +978,9 @@ static int iterate_ref_map(void *cb_data, struct object_id *oid)
while (ref && ref->status == REF_STATUS_REJECT_SHALLOW)
ref = ref->next;
if (!ref)
- return -1; /* end of the list */
+ return NULL;
*rm = ref->next;
- oidcpy(oid, &ref->old_oid);
- return 0;
+ return &ref->old_oid;
}
struct fetch_head {
@@ -1082,7 +1079,6 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
int connectivity_checked, struct ref *ref_map)
{
struct fetch_head fetch_head;
- struct commit *commit;
int url_len, i, rc = 0;
struct strbuf note = STRBUF_INIT, err = STRBUF_INIT;
struct ref_transaction *transaction = NULL;
@@ -1130,6 +1126,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
want_status <= FETCH_HEAD_IGNORE;
want_status++) {
for (rm = ref_map; rm; rm = rm->next) {
+ struct commit *commit = NULL;
struct ref *ref = NULL;
if (rm->status == REF_STATUS_REJECT_SHALLOW) {
@@ -1139,11 +1136,23 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
continue;
}
- commit = lookup_commit_reference_gently(the_repository,
- &rm->old_oid,
- 1);
- if (!commit)
- rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
+ /*
+ * References in "refs/tags/" are often going to point
+ * to annotated tags, which are not part of the
+ * commit-graph. We thus only try to look up refs in
+ * the graph which are not in that namespace to not
+ * regress performance in repositories with many
+ * annotated tags.
+ */
+ if (!starts_with(rm->name, "refs/tags/"))
+ commit = lookup_commit_in_graph(the_repository, &rm->old_oid);
+ if (!commit) {
+ commit = lookup_commit_reference_gently(the_repository,
+ &rm->old_oid,
+ 1);
+ if (!commit)
+ rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
+ }
if (rm->fetch_head_status != want_status)
continue;
@@ -1289,37 +1298,35 @@ static int check_exist_and_connected(struct ref *ref_map)
return check_connected(iterate_ref_map, &rm, &opt);
}
-static int fetch_refs(struct transport *transport, struct ref *ref_map)
+static int fetch_and_consume_refs(struct transport *transport, struct ref *ref_map)
{
- int ret = check_exist_and_connected(ref_map);
+ int connectivity_checked = 1;
+ int ret;
+
+ /*
+ * We don't need to perform a fetch in case we can already satisfy all
+ * refs.
+ */
+ ret = check_exist_and_connected(ref_map);
if (ret) {
trace2_region_enter("fetch", "fetch_refs", the_repository);
ret = transport_fetch_refs(transport, ref_map);
trace2_region_leave("fetch", "fetch_refs", the_repository);
+ if (ret)
+ goto out;
+ connectivity_checked = transport->smart_options ?
+ transport->smart_options->connectivity_checked : 0;
}
- if (!ret)
- /*
- * Keep the new pack's ".keep" file around to allow the caller
- * time to update refs to reference the new objects.
- */
- return 0;
- transport_unlock_pack(transport);
- return ret;
-}
-/* Update local refs based on the ref values fetched from a remote */
-static int consume_refs(struct transport *transport, struct ref *ref_map)
-{
- int connectivity_checked = transport->smart_options
- ? transport->smart_options->connectivity_checked : 0;
- int ret;
trace2_region_enter("fetch", "consume_refs", the_repository);
ret = store_updated_refs(transport->url,
transport->remote->name,
connectivity_checked,
ref_map);
- transport_unlock_pack(transport);
trace2_region_leave("fetch", "consume_refs", the_repository);
+
+out:
+ transport_unlock_pack(transport);
return ret;
}
@@ -1508,8 +1515,7 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
- if (!fetch_refs(transport, ref_map))
- consume_refs(transport, ref_map);
+ fetch_and_consume_refs(transport, ref_map);
if (gsecondary) {
transport_disconnect(gsecondary);
@@ -1600,7 +1606,7 @@ static int do_fetch(struct transport *transport,
transport->url);
}
}
- if (fetch_refs(transport, ref_map) || consume_refs(transport, ref_map)) {
+ if (fetch_and_consume_refs(transport, ref_map)) {
free_refs(ref_map);
retcode = 1;
goto cleanup;
@@ -2142,8 +2148,6 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
NULL);
}
- close_object_store(the_repository->objects);
-
if (enable_auto_gc)
run_auto_maintenance(verbosity < 0);
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 89cb630..6f62f40 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -17,7 +17,8 @@ static char const * const for_each_ref_usage[] = {
int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
{
int i;
- struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+ struct ref_sorting *sorting;
+ struct string_list sorting_options = STRING_LIST_INIT_DUP;
int maxcount = 0, icase = 0;
struct ref_array array;
struct ref_filter filter;
@@ -39,7 +40,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
OPT_STRING( 0 , "format", &format.format, N_("format"), N_("format to use for the output")),
OPT__COLOR(&format.use_color, N_("respect format colors")),
- OPT_REF_SORT(sorting_tail),
+ OPT_REF_SORT(&sorting_options),
OPT_CALLBACK(0, "points-at", &filter.points_at,
N_("object"), N_("print only refs which points at the given object"),
parse_opt_object_name),
@@ -70,14 +71,13 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
if (verify_ref_format(&format))
usage_with_options(for_each_ref_usage, opts);
- if (!sorting)
- sorting = ref_default_sorting();
+ sorting = ref_sorting_options(&sorting_options);
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
filter.ignore_case = icase;
filter.name_patterns = argv;
filter.match_as_path = 1;
- filter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN);
+ filter_refs(&array, &filter, FILTER_REFS_ALL);
ref_array_sort(sorting, &array);
if (!maxcount || array.nr < maxcount)
@@ -96,6 +96,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
ref_array_clear(&array);
free_commit_list(filter.with_commit);
free_commit_list(filter.no_commit);
- UNLEAK(sorting);
+ ref_sorting_release(sorting);
return 0;
}
diff --git a/builtin/fsck.c b/builtin/fsck.c
index b42b6fe..27b9e78 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -593,18 +593,44 @@ static void get_default_heads(void)
}
}
+struct for_each_loose_cb
+{
+ struct progress *progress;
+ struct strbuf obj_type;
+};
+
static int fsck_loose(const struct object_id *oid, const char *path, void *data)
{
+ struct for_each_loose_cb *cb_data = data;
struct object *obj;
- enum object_type type;
+ enum object_type type = OBJ_NONE;
unsigned long size;
- void *contents;
+ void *contents = NULL;
int eaten;
+ struct object_info oi = OBJECT_INFO_INIT;
+ struct object_id real_oid = *null_oid();
+ int err = 0;
- if (read_loose_object(path, oid, &type, &size, &contents) < 0) {
+ strbuf_reset(&cb_data->obj_type);
+ oi.type_name = &cb_data->obj_type;
+ oi.sizep = &size;
+ oi.typep = &type;
+
+ if (read_loose_object(path, oid, &real_oid, &contents, &oi) < 0) {
+ if (contents && !oideq(&real_oid, oid))
+ err = error(_("%s: hash-path mismatch, found at: %s"),
+ oid_to_hex(&real_oid), path);
+ else
+ err = error(_("%s: object corrupt or missing: %s"),
+ oid_to_hex(oid), path);
+ }
+ if (type != OBJ_NONE && type < 0)
+ err = error(_("%s: object is of unknown type '%s': %s"),
+ oid_to_hex(&real_oid), cb_data->obj_type.buf,
+ path);
+ if (err < 0) {
errors_found |= ERROR_OBJECT;
- error(_("%s: object corrupt or missing: %s"),
- oid_to_hex(oid), path);
+ free(contents);
return 0; /* keep checking other objects */
}
@@ -640,8 +666,10 @@ static int fsck_cruft(const char *basename, const char *path, void *data)
return 0;
}
-static int fsck_subdir(unsigned int nr, const char *path, void *progress)
+static int fsck_subdir(unsigned int nr, const char *path, void *data)
{
+ struct for_each_loose_cb *cb_data = data;
+ struct progress *progress = cb_data->progress;
display_progress(progress, nr + 1);
return 0;
}
@@ -649,6 +677,10 @@ static int fsck_subdir(unsigned int nr, const char *path, void *progress)
static void fsck_object_dir(const char *path)
{
struct progress *progress = NULL;
+ struct for_each_loose_cb cb_data = {
+ .obj_type = STRBUF_INIT,
+ .progress = progress,
+ };
if (verbose)
fprintf_ln(stderr, _("Checking object directory"));
@@ -657,9 +689,10 @@ static void fsck_object_dir(const char *path)
progress = start_progress(_("Checking object directories"), 256);
for_each_loose_file_in_objdir(path, fsck_loose, fsck_cruft, fsck_subdir,
- progress);
+ &cb_data);
display_progress(progress, 256);
stop_progress(&progress);
+ strbuf_release(&cb_data.obj_type);
}
static int fsck_head_link(const char *head_ref_name,
@@ -803,6 +836,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
fsck_enable_object_names(&fsck_walk_options);
git_config(git_fsck_config, &fsck_obj_options);
+ prepare_repo_settings(the_repository);
if (connectivity_only) {
for_each_loose_object(mark_loose_for_connectivity, NULL, 0);
@@ -908,7 +942,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
check_connectivity();
- if (!git_config_get_bool("core.commitgraph", &i) && i) {
+ if (the_repository->settings.core_commit_graph) {
struct child_process commit_graph_verify = CHILD_PROCESS_INIT;
const char *verify_argv[] = { "commit-graph", "verify", NULL, NULL, NULL };
@@ -924,7 +958,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
}
}
- if (!git_config_get_bool("core.multipackindex", &i) && i) {
+ if (the_repository->settings.core_multi_pack_index) {
struct child_process midx_verify = CHILD_PROCESS_INIT;
const char *midx_argv[] = { "multi-pack-index", "verify", NULL, NULL, NULL };
diff --git a/builtin/gc.c b/builtin/gc.c
index 43c3602..bcef6a4 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -663,8 +663,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
gc_before_repack();
if (!repository_format_precious_objects) {
- close_object_store(the_repository->objects);
- if (run_command_v_opt(repack.v, RUN_GIT_CMD))
+ if (run_command_v_opt(repack.v,
+ RUN_GIT_CMD | RUN_CLOSE_OBJECT_STORE))
die(FAILED_RUN, repack.v[0]);
if (prune_expire) {
@@ -848,7 +848,7 @@ static int run_write_commit_graph(struct maintenance_run_opts *opts)
{
struct child_process child = CHILD_PROCESS_INIT;
- child.git_cmd = 1;
+ child.git_cmd = child.close_object_store = 1;
strvec_pushl(&child.args, "commit-graph", "write",
"--split", "--reachable", NULL);
@@ -864,7 +864,6 @@ static int maintenance_task_commit_graph(struct maintenance_run_opts *opts)
if (!the_repository->settings.core_commit_graph)
return 0;
- close_object_store(the_repository->objects);
if (run_write_commit_graph(opts)) {
error(_("failed to write commit-graph"));
return 1;
@@ -913,7 +912,7 @@ static int maintenance_task_gc(struct maintenance_run_opts *opts)
{
struct child_process child = CHILD_PROCESS_INIT;
- child.git_cmd = 1;
+ child.git_cmd = child.close_object_store = 1;
strvec_push(&child.args, "gc");
if (opts->auto_flag)
@@ -923,7 +922,6 @@ static int maintenance_task_gc(struct maintenance_run_opts *opts)
else
strvec_push(&child.args, "--no-quiet");
- close_object_store(the_repository->objects);
return run_command(&child);
}
@@ -1051,12 +1049,11 @@ static int maintenance_task_loose_objects(struct maintenance_run_opts *opts)
static int incremental_repack_auto_condition(void)
{
struct packed_git *p;
- int enabled;
int incremental_repack_auto_limit = 10;
int count = 0;
- if (git_config_get_bool("core.multiPackIndex", &enabled) ||
- !enabled)
+ prepare_repo_settings(the_repository);
+ if (!the_repository->settings.core_multi_pack_index)
return 0;
git_config_get_int("maintenance.incremental-repack.auto",
@@ -1097,14 +1094,12 @@ static int multi_pack_index_expire(struct maintenance_run_opts *opts)
{
struct child_process child = CHILD_PROCESS_INIT;
- child.git_cmd = 1;
+ child.git_cmd = child.close_object_store = 1;
strvec_pushl(&child.args, "multi-pack-index", "expire", NULL);
if (opts->quiet)
strvec_push(&child.args, "--no-progress");
- close_object_store(the_repository->objects);
-
if (run_command(&child))
return error(_("'git multi-pack-index expire' failed"));
@@ -1155,7 +1150,7 @@ static int multi_pack_index_repack(struct maintenance_run_opts *opts)
{
struct child_process child = CHILD_PROCESS_INIT;
- child.git_cmd = 1;
+ child.git_cmd = child.close_object_store = 1;
strvec_pushl(&child.args, "multi-pack-index", "repack", NULL);
if (opts->quiet)
@@ -1164,8 +1159,6 @@ static int multi_pack_index_repack(struct maintenance_run_opts *opts)
strvec_pushf(&child.args, "--batch-size=%"PRIuMAX,
(uintmax_t)get_auto_pack_size());
- close_object_store(the_repository->objects);
-
if (run_command(&child))
return error(_("'git multi-pack-index repack' failed"));
@@ -1529,6 +1522,93 @@ static const char *get_frequency(enum schedule_priority schedule)
}
}
+/*
+ * get_schedule_cmd` reads the GIT_TEST_MAINT_SCHEDULER environment variable
+ * to mock the schedulers that `git maintenance start` rely on.
+ *
+ * For test purpose, GIT_TEST_MAINT_SCHEDULER can be set to a comma-separated
+ * list of colon-separated key/value pairs where each pair contains a scheduler
+ * and its corresponding mock.
+ *
+ * * If $GIT_TEST_MAINT_SCHEDULER is not set, return false and leave the
+ * arguments unmodified.
+ *
+ * * If $GIT_TEST_MAINT_SCHEDULER is set, return true.
+ * In this case, the *cmd value is read as input.
+ *
+ * * if the input value *cmd is the key of one of the comma-separated list
+ * item, then *is_available is set to true and *cmd is modified and becomes
+ * the mock command.
+ *
+ * * if the input value *cmd isn’t the key of any of the comma-separated list
+ * item, then *is_available is set to false.
+ *
+ * Ex.:
+ * GIT_TEST_MAINT_SCHEDULER not set
+ * +-------+-------------------------------------------------+
+ * | Input | Output |
+ * | *cmd | return code | *cmd | *is_available |
+ * +-------+-------------+-------------------+---------------+
+ * | "foo" | false | "foo" (unchanged) | (unchanged) |
+ * +-------+-------------+-------------------+---------------+
+ *
+ * GIT_TEST_MAINT_SCHEDULER set to “foo:./mock_foo.sh,bar:./mock_bar.sh”
+ * +-------+-------------------------------------------------+
+ * | Input | Output |
+ * | *cmd | return code | *cmd | *is_available |
+ * +-------+-------------+-------------------+---------------+
+ * | "foo" | true | "./mock.foo.sh" | true |
+ * | "qux" | true | "qux" (unchanged) | false |
+ * +-------+-------------+-------------------+---------------+
+ */
+static int get_schedule_cmd(const char **cmd, int *is_available)
+{
+ char *testing = xstrdup_or_null(getenv("GIT_TEST_MAINT_SCHEDULER"));
+ struct string_list_item *item;
+ struct string_list list = STRING_LIST_INIT_NODUP;
+
+ if (!testing)
+ return 0;
+
+ if (is_available)
+ *is_available = 0;
+
+ string_list_split_in_place(&list, testing, ',', -1);
+ for_each_string_list_item(item, &list) {
+ struct string_list pair = STRING_LIST_INIT_NODUP;
+
+ if (string_list_split_in_place(&pair, item->string, ':', 2) != 2)
+ continue;
+
+ if (!strcmp(*cmd, pair.items[0].string)) {
+ *cmd = pair.items[1].string;
+ if (is_available)
+ *is_available = 1;
+ string_list_clear(&list, 0);
+ UNLEAK(testing);
+ return 1;
+ }
+ }
+
+ string_list_clear(&list, 0);
+ free(testing);
+ return 1;
+}
+
+static int is_launchctl_available(void)
+{
+ const char *cmd = "launchctl";
+ int is_available;
+ if (get_schedule_cmd(&cmd, &is_available))
+ return is_available;
+
+#ifdef __APPLE__
+ return 1;
+#else
+ return 0;
+#endif
+}
+
static char *launchctl_service_name(const char *frequency)
{
struct strbuf label = STRBUF_INIT;
@@ -1555,19 +1635,17 @@ static char *launchctl_get_uid(void)
return xstrfmt("gui/%d", getuid());
}
-static int launchctl_boot_plist(int enable, const char *filename, const char *cmd)
+static int launchctl_boot_plist(int enable, const char *filename)
{
+ const char *cmd = "launchctl";
int result;
struct child_process child = CHILD_PROCESS_INIT;
char *uid = launchctl_get_uid();
+ get_schedule_cmd(&cmd, NULL);
strvec_split(&child.args, cmd);
- if (enable)
- strvec_push(&child.args, "bootstrap");
- else
- strvec_push(&child.args, "bootout");
- strvec_push(&child.args, uid);
- strvec_push(&child.args, filename);
+ strvec_pushl(&child.args, enable ? "bootstrap" : "bootout", uid,
+ filename, NULL);
child.no_stderr = 1;
child.no_stdout = 1;
@@ -1581,30 +1659,28 @@ static int launchctl_boot_plist(int enable, const char *filename, const char *cm
return result;
}
-static int launchctl_remove_plist(enum schedule_priority schedule, const char *cmd)
+static int launchctl_remove_plist(enum schedule_priority schedule)
{
const char *frequency = get_frequency(schedule);
char *name = launchctl_service_name(frequency);
char *filename = launchctl_service_filename(name);
- int result = launchctl_boot_plist(0, filename, cmd);
+ int result = launchctl_boot_plist(0, filename);
unlink(filename);
free(filename);
free(name);
return result;
}
-static int launchctl_remove_plists(const char *cmd)
+static int launchctl_remove_plists(void)
{
- return launchctl_remove_plist(SCHEDULE_HOURLY, cmd) ||
- launchctl_remove_plist(SCHEDULE_DAILY, cmd) ||
- launchctl_remove_plist(SCHEDULE_WEEKLY, cmd);
+ return launchctl_remove_plist(SCHEDULE_HOURLY) ||
+ launchctl_remove_plist(SCHEDULE_DAILY) ||
+ launchctl_remove_plist(SCHEDULE_WEEKLY);
}
static int launchctl_list_contains_plist(const char *name, const char *cmd)
{
- int result;
struct child_process child = CHILD_PROCESS_INIT;
- char *uid = launchctl_get_uid();
strvec_split(&child.args, cmd);
strvec_pushl(&child.args, "list", name, NULL);
@@ -1615,15 +1691,11 @@ static int launchctl_list_contains_plist(const char *name, const char *cmd)
if (start_command(&child))
die(_("failed to start launchctl"));
- result = finish_command(&child);
-
- free(uid);
-
/* Returns failure if 'name' doesn't exist. */
- return !result;
+ return !finish_command(&child);
}
-static int launchctl_schedule_plist(const char *exec_path, enum schedule_priority schedule, const char *cmd)
+static int launchctl_schedule_plist(const char *exec_path, enum schedule_priority schedule)
{
int i, fd;
const char *preamble, *repeat;
@@ -1634,7 +1706,9 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
static unsigned long lock_file_timeout_ms = ULONG_MAX;
struct strbuf plist = STRBUF_INIT, plist2 = STRBUF_INIT;
struct stat st;
+ const char *cmd = "launchctl";
+ get_schedule_cmd(&cmd, NULL);
preamble = "<?xml version=\"1.0\"?>\n"
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
"<plist version=\"1.0\">"
@@ -1715,8 +1789,8 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
die_errno(_("could not write '%s'"), filename);
/* bootout might fail if not already running, so ignore */
- launchctl_boot_plist(0, filename, cmd);
- if (launchctl_boot_plist(1, filename, cmd))
+ launchctl_boot_plist(0, filename);
+ if (launchctl_boot_plist(1, filename))
die(_("failed to bootstrap service %s"), filename);
}
@@ -1727,21 +1801,35 @@ static int launchctl_schedule_plist(const char *exec_path, enum schedule_priorit
return 0;
}
-static int launchctl_add_plists(const char *cmd)
+static int launchctl_add_plists(void)
{
const char *exec_path = git_exec_path();
- return launchctl_schedule_plist(exec_path, SCHEDULE_HOURLY, cmd) ||
- launchctl_schedule_plist(exec_path, SCHEDULE_DAILY, cmd) ||
- launchctl_schedule_plist(exec_path, SCHEDULE_WEEKLY, cmd);
+ return launchctl_schedule_plist(exec_path, SCHEDULE_HOURLY) ||
+ launchctl_schedule_plist(exec_path, SCHEDULE_DAILY) ||
+ launchctl_schedule_plist(exec_path, SCHEDULE_WEEKLY);
}
-static int launchctl_update_schedule(int run_maintenance, int fd, const char *cmd)
+static int launchctl_update_schedule(int run_maintenance, int fd)
{
if (run_maintenance)
- return launchctl_add_plists(cmd);
+ return launchctl_add_plists();
else
- return launchctl_remove_plists(cmd);
+ return launchctl_remove_plists();
+}
+
+static int is_schtasks_available(void)
+{
+ const char *cmd = "schtasks";
+ int is_available;
+ if (get_schedule_cmd(&cmd, &is_available))
+ return is_available;
+
+#ifdef GIT_WINDOWS_NATIVE
+ return 1;
+#else
+ return 0;
+#endif
}
static char *schtasks_task_name(const char *frequency)
@@ -1751,13 +1839,15 @@ static char *schtasks_task_name(const char *frequency)
return strbuf_detach(&label, NULL);
}
-static int schtasks_remove_task(enum schedule_priority schedule, const char *cmd)
+static int schtasks_remove_task(enum schedule_priority schedule)
{
+ const char *cmd = "schtasks";
int result;
struct strvec args = STRVEC_INIT;
const char *frequency = get_frequency(schedule);
char *name = schtasks_task_name(frequency);
+ get_schedule_cmd(&cmd, NULL);
strvec_split(&args, cmd);
strvec_pushl(&args, "/delete", "/tn", name, "/f", NULL);
@@ -1768,15 +1858,16 @@ static int schtasks_remove_task(enum schedule_priority schedule, const char *cmd
return result;
}
-static int schtasks_remove_tasks(const char *cmd)
+static int schtasks_remove_tasks(void)
{
- return schtasks_remove_task(SCHEDULE_HOURLY, cmd) ||
- schtasks_remove_task(SCHEDULE_DAILY, cmd) ||
- schtasks_remove_task(SCHEDULE_WEEKLY, cmd);
+ return schtasks_remove_task(SCHEDULE_HOURLY) ||
+ schtasks_remove_task(SCHEDULE_DAILY) ||
+ schtasks_remove_task(SCHEDULE_WEEKLY);
}
-static int schtasks_schedule_task(const char *exec_path, enum schedule_priority schedule, const char *cmd)
+static int schtasks_schedule_task(const char *exec_path, enum schedule_priority schedule)
{
+ const char *cmd = "schtasks";
int result;
struct child_process child = CHILD_PROCESS_INIT;
const char *xml;
@@ -1785,6 +1876,8 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority
char *name = schtasks_task_name(frequency);
struct strbuf tfilename = STRBUF_INIT;
+ get_schedule_cmd(&cmd, NULL);
+
strbuf_addf(&tfilename, "%s/schedule_%s_XXXXXX",
get_git_common_dir(), frequency);
tfile = xmks_tempfile(tfilename.buf);
@@ -1889,28 +1982,67 @@ static int schtasks_schedule_task(const char *exec_path, enum schedule_priority
return result;
}
-static int schtasks_schedule_tasks(const char *cmd)
+static int schtasks_schedule_tasks(void)
{
const char *exec_path = git_exec_path();
- return schtasks_schedule_task(exec_path, SCHEDULE_HOURLY, cmd) ||
- schtasks_schedule_task(exec_path, SCHEDULE_DAILY, cmd) ||
- schtasks_schedule_task(exec_path, SCHEDULE_WEEKLY, cmd);
+ return schtasks_schedule_task(exec_path, SCHEDULE_HOURLY) ||
+ schtasks_schedule_task(exec_path, SCHEDULE_DAILY) ||
+ schtasks_schedule_task(exec_path, SCHEDULE_WEEKLY);
}
-static int schtasks_update_schedule(int run_maintenance, int fd, const char *cmd)
+static int schtasks_update_schedule(int run_maintenance, int fd)
{
if (run_maintenance)
- return schtasks_schedule_tasks(cmd);
+ return schtasks_schedule_tasks();
else
- return schtasks_remove_tasks(cmd);
+ return schtasks_remove_tasks();
+}
+
+MAYBE_UNUSED
+static int check_crontab_process(const char *cmd)
+{
+ struct child_process child = CHILD_PROCESS_INIT;
+
+ strvec_split(&child.args, cmd);
+ strvec_push(&child.args, "-l");
+ child.no_stdin = 1;
+ child.no_stdout = 1;
+ child.no_stderr = 1;
+ child.silent_exec_failure = 1;
+
+ if (start_command(&child))
+ return 0;
+ /* Ignore exit code, as an empty crontab will return error. */
+ finish_command(&child);
+ return 1;
+}
+
+static int is_crontab_available(void)
+{
+ const char *cmd = "crontab";
+ int is_available;
+
+ if (get_schedule_cmd(&cmd, &is_available))
+ return is_available;
+
+#ifdef __APPLE__
+ /*
+ * macOS has cron, but it requires special permissions and will
+ * create a UI alert when attempting to run this command.
+ */
+ return 0;
+#else
+ return check_crontab_process(cmd);
+#endif
}
#define BEGIN_LINE "# BEGIN GIT MAINTENANCE SCHEDULE"
#define END_LINE "# END GIT MAINTENANCE SCHEDULE"
-static int crontab_update_schedule(int run_maintenance, int fd, const char *cmd)
+static int crontab_update_schedule(int run_maintenance, int fd)
{
+ const char *cmd = "crontab";
int result = 0;
int in_old_region = 0;
struct child_process crontab_list = CHILD_PROCESS_INIT;
@@ -1918,6 +2050,7 @@ static int crontab_update_schedule(int run_maintenance, int fd, const char *cmd)
FILE *cron_list, *cron_in;
struct strbuf line = STRBUF_INIT;
+ get_schedule_cmd(&cmd, NULL);
strvec_split(&crontab_list.args, cmd);
strvec_push(&crontab_list.args, "-l");
crontab_list.in = -1;
@@ -1994,66 +2127,376 @@ done_editing:
return result;
}
+static int real_is_systemd_timer_available(void)
+{
+ struct child_process child = CHILD_PROCESS_INIT;
+
+ strvec_pushl(&child.args, "systemctl", "--user", "list-timers", NULL);
+ child.no_stdin = 1;
+ child.no_stdout = 1;
+ child.no_stderr = 1;
+ child.silent_exec_failure = 1;
+
+ if (start_command(&child))
+ return 0;
+ if (finish_command(&child))
+ return 0;
+ return 1;
+}
+
+static int is_systemd_timer_available(void)
+{
+ const char *cmd = "systemctl";
+ int is_available;
+
+ if (get_schedule_cmd(&cmd, &is_available))
+ return is_available;
+
+ return real_is_systemd_timer_available();
+}
+
+static char *xdg_config_home_systemd(const char *filename)
+{
+ return xdg_config_home_for("systemd/user", filename);
+}
+
+static int systemd_timer_enable_unit(int enable,
+ enum schedule_priority schedule)
+{
+ const char *cmd = "systemctl";
+ struct child_process child = CHILD_PROCESS_INIT;
+ const char *frequency = get_frequency(schedule);
+
+ /*
+ * Disabling the systemd unit while it is already disabled makes
+ * systemctl print an error.
+ * Let's ignore it since it means we already are in the expected state:
+ * the unit is disabled.
+ *
+ * On the other hand, enabling a systemd unit which is already enabled
+ * produces no error.
+ */
+ if (!enable)
+ child.no_stderr = 1;
+
+ get_schedule_cmd(&cmd, NULL);
+ strvec_split(&child.args, cmd);
+ strvec_pushl(&child.args, "--user", enable ? "enable" : "disable",
+ "--now", NULL);
+ strvec_pushf(&child.args, "git-maintenance@%s.timer", frequency);
+
+ if (start_command(&child))
+ return error(_("failed to start systemctl"));
+ if (finish_command(&child))
+ /*
+ * Disabling an already disabled systemd unit makes
+ * systemctl fail.
+ * Let's ignore this failure.
+ *
+ * Enabling an enabled systemd unit doesn't fail.
+ */
+ if (enable)
+ return error(_("failed to run systemctl"));
+ return 0;
+}
+
+static int systemd_timer_delete_unit_templates(void)
+{
+ int ret = 0;
+ char *filename = xdg_config_home_systemd("git-maintenance@.timer");
+ if (unlink(filename) && !is_missing_file_error(errno))
+ ret = error_errno(_("failed to delete '%s'"), filename);
+ FREE_AND_NULL(filename);
+
+ filename = xdg_config_home_systemd("git-maintenance@.service");
+ if (unlink(filename) && !is_missing_file_error(errno))
+ ret = error_errno(_("failed to delete '%s'"), filename);
+
+ free(filename);
+ return ret;
+}
+
+static int systemd_timer_delete_units(void)
+{
+ return systemd_timer_enable_unit(0, SCHEDULE_HOURLY) ||
+ systemd_timer_enable_unit(0, SCHEDULE_DAILY) ||
+ systemd_timer_enable_unit(0, SCHEDULE_WEEKLY) ||
+ systemd_timer_delete_unit_templates();
+}
+
+static int systemd_timer_write_unit_templates(const char *exec_path)
+{
+ char *filename;
+ FILE *file;
+ const char *unit;
+
+ filename = xdg_config_home_systemd("git-maintenance@.timer");
+ if (safe_create_leading_directories(filename)) {
+ error(_("failed to create directories for '%s'"), filename);
+ goto error;
+ }
+ file = fopen_or_warn(filename, "w");
+ if (file == NULL)
+ goto error;
+
+ unit = "# This file was created and is maintained by Git.\n"
+ "# Any edits made in this file might be replaced in the future\n"
+ "# by a Git command.\n"
+ "\n"
+ "[Unit]\n"
+ "Description=Optimize Git repositories data\n"
+ "\n"
+ "[Timer]\n"
+ "OnCalendar=%i\n"
+ "Persistent=true\n"
+ "\n"
+ "[Install]\n"
+ "WantedBy=timers.target\n";
+ if (fputs(unit, file) == EOF) {
+ error(_("failed to write to '%s'"), filename);
+ fclose(file);
+ goto error;
+ }
+ if (fclose(file) == EOF) {
+ error_errno(_("failed to flush '%s'"), filename);
+ goto error;
+ }
+ free(filename);
+
+ filename = xdg_config_home_systemd("git-maintenance@.service");
+ file = fopen_or_warn(filename, "w");
+ if (file == NULL)
+ goto error;
+
+ unit = "# This file was created and is maintained by Git.\n"
+ "# Any edits made in this file might be replaced in the future\n"
+ "# by a Git command.\n"
+ "\n"
+ "[Unit]\n"
+ "Description=Optimize Git repositories data\n"
+ "\n"
+ "[Service]\n"
+ "Type=oneshot\n"
+ "ExecStart=\"%s/git\" --exec-path=\"%s\" for-each-repo --config=maintenance.repo maintenance run --schedule=%%i\n"
+ "LockPersonality=yes\n"
+ "MemoryDenyWriteExecute=yes\n"
+ "NoNewPrivileges=yes\n"
+ "RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6\n"
+ "RestrictNamespaces=yes\n"
+ "RestrictRealtime=yes\n"
+ "RestrictSUIDSGID=yes\n"
+ "SystemCallArchitectures=native\n"
+ "SystemCallFilter=@system-service\n";
+ if (fprintf(file, unit, exec_path, exec_path) < 0) {
+ error(_("failed to write to '%s'"), filename);
+ fclose(file);
+ goto error;
+ }
+ if (fclose(file) == EOF) {
+ error_errno(_("failed to flush '%s'"), filename);
+ goto error;
+ }
+ free(filename);
+ return 0;
+
+error:
+ free(filename);
+ systemd_timer_delete_unit_templates();
+ return -1;
+}
+
+static int systemd_timer_setup_units(void)
+{
+ const char *exec_path = git_exec_path();
+
+ int ret = systemd_timer_write_unit_templates(exec_path) ||
+ systemd_timer_enable_unit(1, SCHEDULE_HOURLY) ||
+ systemd_timer_enable_unit(1, SCHEDULE_DAILY) ||
+ systemd_timer_enable_unit(1, SCHEDULE_WEEKLY);
+ if (ret)
+ systemd_timer_delete_units();
+ return ret;
+}
+
+static int systemd_timer_update_schedule(int run_maintenance, int fd)
+{
+ if (run_maintenance)
+ return systemd_timer_setup_units();
+ else
+ return systemd_timer_delete_units();
+}
+
+enum scheduler {
+ SCHEDULER_INVALID = -1,
+ SCHEDULER_AUTO,
+ SCHEDULER_CRON,
+ SCHEDULER_SYSTEMD,
+ SCHEDULER_LAUNCHCTL,
+ SCHEDULER_SCHTASKS,
+};
+
+static const struct {
+ const char *name;
+ int (*is_available)(void);
+ int (*update_schedule)(int run_maintenance, int fd);
+} scheduler_fn[] = {
+ [SCHEDULER_CRON] = {
+ .name = "crontab",
+ .is_available = is_crontab_available,
+ .update_schedule = crontab_update_schedule,
+ },
+ [SCHEDULER_SYSTEMD] = {
+ .name = "systemctl",
+ .is_available = is_systemd_timer_available,
+ .update_schedule = systemd_timer_update_schedule,
+ },
+ [SCHEDULER_LAUNCHCTL] = {
+ .name = "launchctl",
+ .is_available = is_launchctl_available,
+ .update_schedule = launchctl_update_schedule,
+ },
+ [SCHEDULER_SCHTASKS] = {
+ .name = "schtasks",
+ .is_available = is_schtasks_available,
+ .update_schedule = schtasks_update_schedule,
+ },
+};
+
+static enum scheduler parse_scheduler(const char *value)
+{
+ if (!value)
+ return SCHEDULER_INVALID;
+ else if (!strcasecmp(value, "auto"))
+ return SCHEDULER_AUTO;
+ else if (!strcasecmp(value, "cron") || !strcasecmp(value, "crontab"))
+ return SCHEDULER_CRON;
+ else if (!strcasecmp(value, "systemd") ||
+ !strcasecmp(value, "systemd-timer"))
+ return SCHEDULER_SYSTEMD;
+ else if (!strcasecmp(value, "launchctl"))
+ return SCHEDULER_LAUNCHCTL;
+ else if (!strcasecmp(value, "schtasks"))
+ return SCHEDULER_SCHTASKS;
+ else
+ return SCHEDULER_INVALID;
+}
+
+static int maintenance_opt_scheduler(const struct option *opt, const char *arg,
+ int unset)
+{
+ enum scheduler *scheduler = opt->value;
+
+ BUG_ON_OPT_NEG(unset);
+
+ *scheduler = parse_scheduler(arg);
+ if (*scheduler == SCHEDULER_INVALID)
+ return error(_("unrecognized --scheduler argument '%s'"), arg);
+ return 0;
+}
+
+struct maintenance_start_opts {
+ enum scheduler scheduler;
+};
+
+static enum scheduler resolve_scheduler(enum scheduler scheduler)
+{
+ if (scheduler != SCHEDULER_AUTO)
+ return scheduler;
+
#if defined(__APPLE__)
-static const char platform_scheduler[] = "launchctl";
+ return SCHEDULER_LAUNCHCTL;
+
#elif defined(GIT_WINDOWS_NATIVE)
-static const char platform_scheduler[] = "schtasks";
+ return SCHEDULER_SCHTASKS;
+
+#elif defined(__linux__)
+ if (is_systemd_timer_available())
+ return SCHEDULER_SYSTEMD;
+ else if (is_crontab_available())
+ return SCHEDULER_CRON;
+ else
+ die(_("neither systemd timers nor crontab are available"));
+
#else
-static const char platform_scheduler[] = "crontab";
+ return SCHEDULER_CRON;
#endif
+}
-static int update_background_schedule(int enable)
+static void validate_scheduler(enum scheduler scheduler)
{
- int result;
- const char *scheduler = platform_scheduler;
- const char *cmd = scheduler;
- char *testing;
+ if (scheduler == SCHEDULER_INVALID)
+ BUG("invalid scheduler");
+ if (scheduler == SCHEDULER_AUTO)
+ BUG("resolve_scheduler should have been called before");
+
+ if (!scheduler_fn[scheduler].is_available())
+ die(_("%s scheduler is not available"),
+ scheduler_fn[scheduler].name);
+}
+
+static int update_background_schedule(const struct maintenance_start_opts *opts,
+ int enable)
+{
+ unsigned int i;
+ int result = 0;
struct lock_file lk;
char *lock_path = xstrfmt("%s/schedule", the_repository->objects->odb->path);
- testing = xstrdup_or_null(getenv("GIT_TEST_MAINT_SCHEDULER"));
- if (testing) {
- char *sep = strchr(testing, ':');
- if (!sep)
- die("GIT_TEST_MAINT_SCHEDULER unparseable: %s", testing);
- *sep = '\0';
- scheduler = testing;
- cmd = sep + 1;
+ if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0) {
+ free(lock_path);
+ return error(_("another process is scheduling background maintenance"));
}
- if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0) {
- result = error(_("another process is scheduling background maintenance"));
- goto cleanup;
+ for (i = 1; i < ARRAY_SIZE(scheduler_fn); i++) {
+ if (enable && opts->scheduler == i)
+ continue;
+ if (!scheduler_fn[i].is_available())
+ continue;
+ scheduler_fn[i].update_schedule(0, get_lock_file_fd(&lk));
}
- if (!strcmp(scheduler, "launchctl"))
- result = launchctl_update_schedule(enable, get_lock_file_fd(&lk), cmd);
- else if (!strcmp(scheduler, "schtasks"))
- result = schtasks_update_schedule(enable, get_lock_file_fd(&lk), cmd);
- else if (!strcmp(scheduler, "crontab"))
- result = crontab_update_schedule(enable, get_lock_file_fd(&lk), cmd);
- else
- die("unknown background scheduler: %s", scheduler);
+ if (enable)
+ result = scheduler_fn[opts->scheduler].update_schedule(
+ 1, get_lock_file_fd(&lk));
rollback_lock_file(&lk);
-cleanup:
free(lock_path);
- free(testing);
return result;
}
-static int maintenance_start(void)
+static const char *const builtin_maintenance_start_usage[] = {
+ N_("git maintenance start [--scheduler=<scheduler>]"),
+ NULL
+};
+
+static int maintenance_start(int argc, const char **argv, const char *prefix)
{
+ struct maintenance_start_opts opts = { 0 };
+ struct option options[] = {
+ OPT_CALLBACK_F(
+ 0, "scheduler", &opts.scheduler, N_("scheduler"),
+ N_("scheduler to trigger git maintenance run"),
+ PARSE_OPT_NONEG, maintenance_opt_scheduler),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options,
+ builtin_maintenance_start_usage, 0);
+ if (argc)
+ usage_with_options(builtin_maintenance_start_usage, options);
+
+ opts.scheduler = resolve_scheduler(opts.scheduler);
+ validate_scheduler(opts.scheduler);
+
if (maintenance_register())
warning(_("failed to add repo to global config"));
-
- return update_background_schedule(1);
+ return update_background_schedule(&opts, 1);
}
static int maintenance_stop(void)
{
- return update_background_schedule(0);
+ return update_background_schedule(NULL, 0);
}
static const char builtin_maintenance_usage[] = N_("git maintenance <subcommand> [<options>]");
@@ -2067,7 +2510,7 @@ int cmd_maintenance(int argc, const char **argv, const char *prefix)
if (!strcmp(argv[1], "run"))
return maintenance_run(argc - 1, argv + 1, prefix);
if (!strcmp(argv[1], "start"))
- return maintenance_start();
+ return maintenance_start(argc - 1, argv + 1, prefix);
if (!strcmp(argv[1], "stop"))
return maintenance_stop();
if (!strcmp(argv[1], "register"))
diff --git a/builtin/grep.c b/builtin/grep.c
index 7d2f8e5..9e34a82 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -65,6 +65,9 @@ static int todo_done;
/* Has all work items been added? */
static int all_work_added;
+static struct repository **repos_to_free;
+static size_t repos_to_free_nr, repos_to_free_alloc;
+
/* This lock protects all the variables above. */
static pthread_mutex_t grep_mutex;
@@ -168,6 +171,19 @@ static void work_done(struct work_item *w)
grep_unlock();
}
+static void free_repos(void)
+{
+ int i;
+
+ for (i = 0; i < repos_to_free_nr; i++) {
+ repo_clear(repos_to_free[i]);
+ free(repos_to_free[i]);
+ }
+ FREE_AND_NULL(repos_to_free);
+ repos_to_free_nr = 0;
+ repos_to_free_alloc = 0;
+}
+
static void *run(void *arg)
{
int hit = 0;
@@ -183,8 +199,8 @@ static void *run(void *arg)
grep_source_clear_data(&w->source);
work_done(w);
}
- free_grep_patterns(arg);
- free(arg);
+ free_grep_patterns(opt);
+ free(opt);
return (void*) (intptr_t) hit;
}
@@ -333,7 +349,7 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid,
struct grep_source gs;
grep_source_name(opt, filename, tree_name_len, &pathbuf);
- grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid);
+ grep_source_init_oid(&gs, pathbuf.buf, path, oid, opt->repo);
strbuf_release(&pathbuf);
if (num_threads > 1) {
@@ -359,7 +375,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)
struct grep_source gs;
grep_source_name(opt, filename, 0, &buf);
- grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename);
+ grep_source_init_file(&gs, buf.buf, filename);
strbuf_release(&buf);
if (num_threads > 1) {
@@ -385,7 +401,7 @@ static void append_path(struct grep_opt *opt, const void *data, size_t len)
if (len == 1 && *(const char *)data == '\0')
return;
- string_list_append(path_list, xstrndup(data, len));
+ string_list_append_nodup(path_list, xstrndup(data, len));
}
static void run_pager(struct grep_opt *opt, const char *prefix)
@@ -415,19 +431,21 @@ static int grep_submodule(struct grep_opt *opt,
const struct object_id *oid,
const char *filename, const char *path, int cached)
{
- struct repository subrepo;
+ struct repository *subrepo;
struct repository *superproject = opt->repo;
- const struct submodule *sub;
struct grep_opt subopt;
- int hit;
-
- sub = submodule_from_path(superproject, null_oid(), path);
+ int hit = 0;
if (!is_submodule_active(superproject, path))
return 0;
- if (repo_submodule_init(&subrepo, superproject, sub))
+ subrepo = xmalloc(sizeof(*subrepo));
+ if (repo_submodule_init(subrepo, superproject, path, null_oid())) {
+ free(subrepo);
return 0;
+ }
+ ALLOC_GROW(repos_to_free, repos_to_free_nr + 1, repos_to_free_alloc);
+ repos_to_free[repos_to_free_nr++] = subrepo;
/*
* NEEDSWORK: repo_read_gitmodules() might call
@@ -438,53 +456,49 @@ static int grep_submodule(struct grep_opt *opt,
* subrepo's odbs to the in-memory alternates list.
*/
obj_read_lock();
- repo_read_gitmodules(&subrepo, 0);
+ repo_read_gitmodules(subrepo, 0);
/*
- * NEEDSWORK: This adds the submodule's object directory to the list of
- * alternates for the single in-memory object store. This has some bad
- * consequences for memory (processed objects will never be freed) and
- * performance (this increases the number of pack files git has to pay
- * attention to, to the sum of the number of pack files in all the
- * repositories processed so far). This can be removed once the object
- * store is no longer global and instead is a member of the repository
- * object.
+ * All code paths tested by test code no longer need submodule ODBs to
+ * be added as alternates, but add it to the list just in case.
+ * Submodule ODBs added through add_submodule_odb_by_path() will be
+ * lazily registered as alternates when needed (and except in an
+ * unexpected code interaction, it won't be needed).
*/
- add_to_alternates_memory(subrepo.objects->odb->path);
+ add_submodule_odb_by_path(subrepo->objects->odb->path);
obj_read_unlock();
memcpy(&subopt, opt, sizeof(subopt));
- subopt.repo = &subrepo;
+ subopt.repo = subrepo;
if (oid) {
- struct object *object;
+ enum object_type object_type;
struct tree_desc tree;
void *data;
unsigned long size;
struct strbuf base = STRBUF_INIT;
obj_read_lock();
- object = parse_object_or_die(oid, NULL);
+ object_type = oid_object_info(subrepo, oid, NULL);
obj_read_unlock();
- data = read_object_with_reference(&subrepo,
- &object->oid, tree_type,
+ data = read_object_with_reference(subrepo,
+ oid, tree_type,
&size, NULL);
if (!data)
- die(_("unable to read tree (%s)"), oid_to_hex(&object->oid));
+ die(_("unable to read tree (%s)"), oid_to_hex(oid));
strbuf_addstr(&base, filename);
strbuf_addch(&base, '/');
init_tree_desc(&tree, data, size);
hit = grep_tree(&subopt, pathspec, &tree, &base, base.len,
- object->type == OBJ_COMMIT);
+ object_type == OBJ_COMMIT);
strbuf_release(&base);
free(data);
} else {
hit = grep_cache(&subopt, pathspec, cached);
}
- repo_clear(&subrepo);
return hit;
}
@@ -825,7 +839,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
struct grep_opt opt;
struct object_array list = OBJECT_ARRAY_INIT;
struct pathspec pathspec;
- struct string_list path_list = STRING_LIST_INIT_NODUP;
+ struct string_list path_list = STRING_LIST_INIT_DUP;
int i;
int dummy;
int use_index = 1;
@@ -1145,8 +1159,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
strbuf_addf(&buf, "+/%s%s",
strcmp("less", pager) ? "" : "*",
opt.pattern_list->pattern);
- string_list_append(&path_list,
- strbuf_detach(&buf, NULL));
+ string_list_append_nodup(&path_list,
+ strbuf_detach(&buf, NULL));
}
}
@@ -1181,6 +1195,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
if (hit && show_in_pager)
run_pager(&opt, prefix);
clear_pathspec(&pathspec);
+ string_list_clear(&path_list, 0);
free_grep_patterns(&opt);
+ object_array_clear(&list);
+ free_repos();
return !hit;
}
diff --git a/builtin/help.c b/builtin/help.c
index b7eec06..75cd2fb 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -7,7 +7,6 @@
#include "exec-cmd.h"
#include "parse-options.h"
#include "run-command.h"
-#include "column.h"
#include "config-list.h"
#include "help.h"
#include "alias.h"
@@ -34,32 +33,52 @@ enum help_format {
HELP_FORMAT_WEB
};
-static const char *html_path;
+enum show_config_type {
+ SHOW_CONFIG_HUMAN,
+ SHOW_CONFIG_VARS,
+ SHOW_CONFIG_SECTIONS,
+};
+
+static enum help_action {
+ HELP_ACTION_ALL = 1,
+ HELP_ACTION_GUIDES,
+ HELP_ACTION_CONFIG,
+ HELP_ACTION_CONFIG_FOR_COMPLETION,
+ HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION,
+} cmd_mode;
-static int show_all = 0;
-static int show_guides = 0;
-static int show_config;
+static const char *html_path;
static int verbose = 1;
-static unsigned int colopts;
static enum help_format help_format = HELP_FORMAT_NONE;
static int exclude_guides;
static struct option builtin_help_options[] = {
- OPT_BOOL('a', "all", &show_all, N_("print all available commands")),
+ OPT_CMDMODE('a', "all", &cmd_mode, N_("print all available commands"),
+ HELP_ACTION_ALL),
OPT_HIDDEN_BOOL(0, "exclude-guides", &exclude_guides, N_("exclude guides")),
- OPT_BOOL('g', "guides", &show_guides, N_("print list of useful guides")),
- OPT_BOOL('c', "config", &show_config, N_("print all configuration variable names")),
- OPT_SET_INT_F(0, "config-for-completion", &show_config, "", 2, PARSE_OPT_HIDDEN),
OPT_SET_INT('m', "man", &help_format, N_("show man page"), HELP_FORMAT_MAN),
OPT_SET_INT('w', "web", &help_format, N_("show manual in web browser"),
HELP_FORMAT_WEB),
OPT_SET_INT('i', "info", &help_format, N_("show info page"),
HELP_FORMAT_INFO),
OPT__VERBOSE(&verbose, N_("print command description")),
+
+ OPT_CMDMODE('g', "guides", &cmd_mode, N_("print list of useful guides"),
+ HELP_ACTION_GUIDES),
+ OPT_CMDMODE('c', "config", &cmd_mode, N_("print all configuration variable names"),
+ HELP_ACTION_CONFIG),
+ OPT_CMDMODE_F(0, "config-for-completion", &cmd_mode, "",
+ HELP_ACTION_CONFIG_FOR_COMPLETION, PARSE_OPT_HIDDEN),
+ OPT_CMDMODE_F(0, "config-sections-for-completion", &cmd_mode, "",
+ HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION, PARSE_OPT_HIDDEN),
+
OPT_END(),
};
static const char * const builtin_help_usage[] = {
- N_("git help [--all] [--guides] [--man | --web | --info] [<command>]"),
+ N_("git help [-a|--all] [--[no-]verbose]]\n"
+ " [[-i|--info] [-m|--man] [-w|--web]] [<command>]"),
+ N_("git help [-g|--guides]"),
+ N_("git help [-c|--config]"),
NULL
};
@@ -70,7 +89,7 @@ struct slot_expansion {
int found;
};
-static void list_config_help(int for_human)
+static void list_config_help(enum show_config_type type)
{
struct slot_expansion slot_expansions[] = {
{ "advice", "*", list_config_advices },
@@ -88,6 +107,8 @@ static void list_config_help(int for_human)
const char **p;
struct slot_expansion *e;
struct string_list keys = STRING_LIST_INIT_DUP;
+ struct string_list keys_uniq = STRING_LIST_INIT_DUP;
+ struct string_list_item *item;
int i;
for (p = config_name_list; *p; p++) {
@@ -118,34 +139,46 @@ static void list_config_help(int for_human)
for (i = 0; i < keys.nr; i++) {
const char *var = keys.items[i].string;
const char *wildcard, *tag, *cut;
+ const char *dot = NULL;
+ struct strbuf sb = STRBUF_INIT;
- if (for_human) {
+ switch (type) {
+ case SHOW_CONFIG_HUMAN:
puts(var);
continue;
+ case SHOW_CONFIG_SECTIONS:
+ dot = strchr(var, '.');
+ break;
+ case SHOW_CONFIG_VARS:
+ break;
}
-
wildcard = strchr(var, '*');
tag = strchr(var, '<');
- if (!wildcard && !tag) {
- puts(var);
+ if (!dot && !wildcard && !tag) {
+ string_list_append(&keys_uniq, var);
continue;
}
- if (wildcard && !tag)
+ if (dot)
+ cut = dot;
+ else if (wildcard && !tag)
cut = wildcard;
else if (!wildcard && tag)
cut = tag;
else
cut = wildcard < tag ? wildcard : tag;
- /*
- * We may produce duplicates, but that's up to
- * git-completion.bash to handle
- */
- printf("%.*s\n", (int)(cut - var), var);
+ strbuf_add(&sb, var, cut - var);
+ string_list_append(&keys_uniq, sb.buf);
+ strbuf_release(&sb);
+
}
string_list_clear(&keys, 0);
+ string_list_remove_duplicates(&keys_uniq, 0);
+ for_each_string_list_item(item, &keys_uniq)
+ puts(item->string);
+ string_list_clear(&keys_uniq, 0);
}
static enum help_format parse_help_format(const char *format)
@@ -349,8 +382,6 @@ static int add_man_viewer_info(const char *var, const char *value)
static int git_help_config(const char *var, const char *value, void *cb)
{
- if (starts_with(var, "column."))
- return git_column_config(var, value, "help", &colopts);
if (!strcmp(var, "help.format")) {
if (!value)
return config_error_nonbool(var);
@@ -467,11 +498,14 @@ static void get_html_page_path(struct strbuf *page_path, const char *page)
if (!html_path)
html_path = to_free = system_path(GIT_HTML_PATH);
- /* Check that we have a git documentation directory. */
+ /*
+ * Check that the page we're looking for exists.
+ */
if (!strstr(html_path, "://")) {
- if (stat(mkpath("%s/git.html", html_path), &st)
+ if (stat(mkpath("%s/%s.html", html_path, page), &st)
|| !S_ISREG(st.st_mode))
- die("'%s': not a documentation directory.", html_path);
+ die("'%s/%s.html': documentation file not found.",
+ html_path, page);
}
strbuf_init(page_path, 0);
@@ -541,6 +575,13 @@ static const char *check_git_cmd(const char* cmd)
return cmd;
}
+static void no_extra_argc(int argc)
+{
+ if (argc)
+ usage_msg_opt(_("this option doesn't take any other arguments"),
+ builtin_help_usage, builtin_help_options);
+}
+
int cmd_help(int argc, const char **argv, const char *prefix)
{
int nongit;
@@ -551,8 +592,8 @@ int cmd_help(int argc, const char **argv, const char *prefix)
builtin_help_usage, 0);
parsed_help_format = help_format;
- if (show_all) {
- git_config(git_help_config, NULL);
+ switch (cmd_mode) {
+ case HELP_ACTION_ALL:
if (verbose) {
setup_pager();
list_all_cmds_help();
@@ -560,30 +601,27 @@ int cmd_help(int argc, const char **argv, const char *prefix)
}
printf(_("usage: %s%s"), _(git_usage_string), "\n\n");
load_command_list("git-", &main_cmds, &other_cmds);
- list_commands(colopts, &main_cmds, &other_cmds);
- }
-
- if (show_config) {
- int for_human = show_config == 1;
-
- if (!for_human) {
- list_config_help(for_human);
- return 0;
- }
- setup_pager();
- list_config_help(for_human);
- printf("\n%s\n", _("'git help config' for more information"));
- return 0;
- }
-
- if (show_guides)
+ list_commands(&main_cmds, &other_cmds);
+ printf("%s\n", _(git_more_info_string));
+ break;
+ case HELP_ACTION_GUIDES:
+ no_extra_argc(argc);
list_guides_help();
-
- if (show_all || show_guides) {
printf("%s\n", _(git_more_info_string));
- /*
- * We're done. Ignore any remaining args
- */
+ return 0;
+ case HELP_ACTION_CONFIG_FOR_COMPLETION:
+ no_extra_argc(argc);
+ list_config_help(SHOW_CONFIG_VARS);
+ return 0;
+ case HELP_ACTION_CONFIG_SECTIONS_FOR_COMPLETION:
+ no_extra_argc(argc);
+ list_config_help(SHOW_CONFIG_SECTIONS);
+ return 0;
+ case HELP_ACTION_CONFIG:
+ no_extra_argc(argc);
+ setup_pager();
+ list_config_help(SHOW_CONFIG_HUMAN);
+ printf("\n%s\n", _("'git help config' for more information"));
return 0;
}
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 6cc4890..c23d01d 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -122,6 +122,7 @@ static int strict;
static int do_fsck_object;
static struct fsck_options fsck_options = FSCK_OPTIONS_MISSING_GITMODULES;
static int verbose;
+static const char *progress_title;
static int show_resolving_progress;
static int show_stat;
static int check_self_contained_and_connected;
@@ -187,9 +188,7 @@ static void init_thread(void)
pthread_key_create(&key, NULL);
CALLOC_ARRAY(thread_data, nr_threads);
for (i = 0; i < nr_threads; i++) {
- thread_data[i].pack_fd = open(curr_pack, O_RDONLY);
- if (thread_data[i].pack_fd == -1)
- die_errno(_("unable to open %s"), curr_pack);
+ thread_data[i].pack_fd = xopen(curr_pack, O_RDONLY);
}
threads_active = 1;
@@ -1153,6 +1152,7 @@ static void parse_pack_objects(unsigned char *hash)
if (verbose)
progress = start_progress(
+ progress_title ? progress_title :
from_stdin ? _("Receiving objects") : _("Indexing objects"),
nr_objects);
for (i = 0; i < nr_objects; i++) {
@@ -1415,7 +1415,7 @@ static void fix_unresolved_deltas(struct hashfile *f)
if (check_object_signature(the_repository, &d->oid,
data, size,
- type_name(type)))
+ type_name(type), NULL))
die(_("local object %s is corrupt"), oid_to_hex(&d->oid));
/*
@@ -1477,6 +1477,22 @@ static void write_special_file(const char *suffix, const char *msg,
strbuf_release(&name_buf);
}
+static void rename_tmp_packfile(const char **final_name,
+ const char *curr_name,
+ struct strbuf *name, unsigned char *hash,
+ const char *ext, int make_read_only_if_same)
+{
+ if (*final_name != curr_name) {
+ if (!*final_name)
+ *final_name = odb_pack_name(name, hash, ext);
+ if (finalize_object_file(curr_name, *final_name))
+ die(_("unable to rename temporary '*.%s' file to '%s'"),
+ ext, *final_name);
+ } else if (make_read_only_if_same) {
+ chmod(*final_name, 0444);
+ }
+}
+
static void final(const char *final_pack_name, const char *curr_pack_name,
const char *final_index_name, const char *curr_index_name,
const char *final_rev_index_name, const char *curr_rev_index_name,
@@ -1505,31 +1521,13 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
write_special_file("promisor", promisor_msg, final_pack_name,
hash, NULL);
- if (final_pack_name != curr_pack_name) {
- if (!final_pack_name)
- final_pack_name = odb_pack_name(&pack_name, hash, "pack");
- if (finalize_object_file(curr_pack_name, final_pack_name))
- die(_("cannot store pack file"));
- } else if (from_stdin)
- chmod(final_pack_name, 0444);
-
- if (final_index_name != curr_index_name) {
- if (!final_index_name)
- final_index_name = odb_pack_name(&index_name, hash, "idx");
- if (finalize_object_file(curr_index_name, final_index_name))
- die(_("cannot store index file"));
- } else
- chmod(final_index_name, 0444);
-
- if (curr_rev_index_name) {
- if (final_rev_index_name != curr_rev_index_name) {
- if (!final_rev_index_name)
- final_rev_index_name = odb_pack_name(&rev_index_name, hash, "rev");
- if (finalize_object_file(curr_rev_index_name, final_rev_index_name))
- die(_("cannot store reverse index file"));
- } else
- chmod(final_rev_index_name, 0444);
- }
+ rename_tmp_packfile(&final_pack_name, curr_pack_name, &pack_name,
+ hash, "pack", from_stdin);
+ if (curr_rev_index_name)
+ rename_tmp_packfile(&final_rev_index_name, curr_rev_index_name,
+ &rev_index_name, hash, "rev", 1);
+ rename_tmp_packfile(&final_index_name, curr_index_name, &index_name,
+ hash, "idx", 1);
if (do_fsck_object) {
struct packed_git *p;
@@ -1802,6 +1800,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
input_len = sizeof(*hdr);
} else if (!strcmp(arg, "-v")) {
verbose = 1;
+ } else if (!strcmp(arg, "--progress-title")) {
+ if (progress_title || (i+1) >= argc)
+ usage(index_pack_usage);
+ progress_title = argv[++i];
} else if (!strcmp(arg, "--show-resolving-progress")) {
show_resolving_progress = 1;
} else if (!strcmp(arg, "--report-end-of-input")) {
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 29a26ad..031fef1 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -209,10 +209,8 @@ static void show_submodule(struct repository *superproject,
struct dir_struct *dir, const char *path)
{
struct repository subrepo;
- const struct submodule *sub = submodule_from_path(superproject,
- null_oid(), path);
- if (repo_submodule_init(&subrepo, superproject, sub))
+ if (repo_submodule_init(&subrepo, superproject, path, null_oid()))
return;
if (repo_read_index(&subrepo) < 0)
@@ -614,7 +612,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
struct option builtin_ls_files_options[] = {
/* Think twice before adding "--nul" synonym to this */
OPT_SET_INT('z', NULL, &line_terminator,
- N_("paths are separated with NUL character"), '\0'),
+ N_("separate paths with the NUL character"), '\0'),
OPT_BOOL('t', NULL, &show_tag,
N_("identify the file status with tags")),
OPT_BOOL('v', NULL, &show_valid_bit,
@@ -651,7 +649,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
N_("skip files matching pattern"),
PARSE_OPT_NONEG, option_parse_exclude),
OPT_CALLBACK_F('X', "exclude-from", &dir, N_("file"),
- N_("exclude patterns are read from <file>"),
+ N_("read exclude patterns from <file>"),
PARSE_OPT_NONEG, option_parse_exclude_from),
OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, N_("file"),
N_("read additional per-directory exclude patterns in <file>")),
@@ -674,6 +672,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
N_("suppress duplicate entries")),
OPT_END()
};
+ int ret = 0;
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(ls_files_usage, builtin_ls_files_options);
@@ -777,16 +776,13 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
if (show_resolve_undo)
show_ru_info(the_repository->index);
- if (ps_matched) {
- int bad;
- bad = report_path_error(ps_matched, &pathspec);
- if (bad)
- fprintf(stderr, "Did you forget to 'git add'?\n");
-
- return bad ? 1 : 0;
+ if (ps_matched && report_path_error(ps_matched, &pathspec)) {
+ fprintf(stderr, "Did you forget to 'git add'?\n");
+ ret = 1;
}
+ string_list_clear(&exclude_list, 0);
dir_clear(&dir);
free(max_prefix);
- return 0;
+ return ret;
}
diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c
index f4fd823..44448fa 100644
--- a/builtin/ls-remote.c
+++ b/builtin/ls-remote.c
@@ -7,8 +7,8 @@
static const char * const ls_remote_usage[] = {
N_("git ls-remote [--heads] [--tags] [--refs] [--upload-pack=<exec>]\n"
- " [-q | --quiet] [--exit-code] [--get-url]\n"
- " [--symref] [<repository> [<refs>...]]"),
+ " [-q | --quiet] [--exit-code] [--get-url]\n"
+ " [--symref] [<repository> [<refs>...]]"),
NULL
};
@@ -54,7 +54,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
struct transport *transport;
const struct ref *ref;
struct ref_array ref_array;
- static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+ struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct option options[] = {
OPT__QUIET(&quiet, N_("do not print remote URL")),
@@ -68,7 +68,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
OPT_BIT(0, "refs", &flags, N_("do not show peeled tags"), REF_NORMAL),
OPT_BOOL(0, "get-url", &get_url,
N_("take url.<base>.insteadOf into account")),
- OPT_REF_SORT(sorting_tail),
+ OPT_REF_SORT(&sorting_options),
OPT_SET_INT_F(0, "exit-code", &status,
N_("exit with exit code 2 if no matching refs are found"),
2, PARSE_OPT_NOCOMPLETE),
@@ -86,8 +86,6 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
packet_trace_identity("ls-remote");
- UNLEAK(sorting);
-
if (argc > 1) {
int i;
CALLOC_ARRAY(pattern, argc);
@@ -139,8 +137,13 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
item->symref = xstrdup_or_null(ref->symref);
}
- if (sorting)
+ if (sorting_options.nr) {
+ struct ref_sorting *sorting;
+
+ sorting = ref_sorting_options(&sorting_options);
ref_array_sort(sorting, &ref_array);
+ ref_sorting_release(sorting);
+ }
for (i = 0; i < ref_array.nr; i++) {
const struct ref_array_item *ref = ref_array.items[i];
diff --git a/builtin/merge.c b/builtin/merge.c
index d2c52b6..ea3112e 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -13,6 +13,7 @@
#include "builtin.h"
#include "lockfile.h"
#include "run-command.h"
+#include "hook.h"
#include "diff.h"
#include "diff-merges.h"
#include "refs.h"
@@ -469,7 +470,6 @@ static void finish(struct commit *head_commit,
* We ignore errors in 'gc --auto', since the
* user should see them.
*/
- close_object_store(the_repository->objects);
run_auto_maintenance(verbosity < 0);
}
}
@@ -681,6 +681,7 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
opts.verbose_update = 1;
opts.trivial_merges_only = 1;
opts.merge = 1;
+ opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
trees[nr_trees] = parse_tree_indirect(common);
if (!trees[nr_trees++])
return -1;
@@ -849,7 +850,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)
* and write it out as a tree. We must do this before we invoke
* the editor and after we invoke run_status above.
*/
- if (find_hook("pre-merge-commit"))
+ if (hook_exists("pre-merge-commit"))
discard_cache();
read_cache_from(index_file);
strbuf_addbuf(&msg, &merge_msg);
@@ -1276,6 +1277,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (argc == 2 && !strcmp(argv[1], "-h"))
usage_with_options(builtin_merge_usage, builtin_merge_options);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
/*
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.
@@ -1574,6 +1578,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
finish(head_commit, remoteheads, &commit->object.oid, msg.buf);
remove_merge_branch_state(the_repository);
+ strbuf_release(&msg);
goto done;
} else if (!remoteheads->next && common->next)
;
@@ -1744,6 +1749,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
ret = suggest_conflicts();
done:
+ strbuf_release(&buf);
free(branch_to_free);
return ret;
}
diff --git a/builtin/mktag.c b/builtin/mktag.c
index dddcccd..3b2dbbb 100644
--- a/builtin/mktag.c
+++ b/builtin/mktag.c
@@ -62,7 +62,8 @@ static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type)
repl = lookup_replace_object(the_repository, tagged_oid);
ret = check_object_signature(the_repository, repl,
- buffer, size, type_name(*tagged_type));
+ buffer, size, type_name(*tagged_type),
+ NULL);
free(buffer);
return ret;
diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c
index 649aa5f..4480ba3 100644
--- a/builtin/multi-pack-index.c
+++ b/builtin/multi-pack-index.c
@@ -7,7 +7,8 @@
#include "object-store.h"
#define BUILTIN_MIDX_WRITE_USAGE \
- N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]")
+ N_("git multi-pack-index [<options>] write [--preferred-pack=<pack>]" \
+ "[--refs-snapshot=<path>]")
#define BUILTIN_MIDX_VERIFY_USAGE \
N_("git multi-pack-index [<options>] verify")
@@ -45,14 +46,15 @@ static char const * const builtin_multi_pack_index_usage[] = {
static struct opts_multi_pack_index {
const char *object_dir;
const char *preferred_pack;
+ const char *refs_snapshot;
unsigned long batch_size;
unsigned flags;
+ int stdin_packs;
} opts;
static struct option common_opts[] = {
OPT_FILENAME(0, "object-dir", &opts.object_dir,
N_("object directory containing set of packfile and pack-index pairs")),
- OPT_BIT(0, "progress", &opts.flags, N_("force progress reporting"), MIDX_PROGRESS),
OPT_END(),
};
@@ -61,6 +63,33 @@ static struct option *add_common_options(struct option *prev)
return parse_options_concat(common_opts, prev);
}
+static int git_multi_pack_index_write_config(const char *var, const char *value,
+ void *cb)
+{
+ if (!strcmp(var, "pack.writebitmaphashcache")) {
+ if (git_config_bool(var, value))
+ opts.flags |= MIDX_WRITE_BITMAP_HASH_CACHE;
+ else
+ opts.flags &= ~MIDX_WRITE_BITMAP_HASH_CACHE;
+ }
+
+ /*
+ * We should never make a fall-back call to 'git_default_config', since
+ * this was already called in 'cmd_multi_pack_index()'.
+ */
+ return 0;
+}
+
+static void read_packs_from_stdin(struct string_list *to)
+{
+ struct strbuf buf = STRBUF_INIT;
+ while (strbuf_getline(&buf, stdin) != EOF)
+ string_list_append(to, buf.buf);
+ string_list_sort(to);
+
+ strbuf_release(&buf);
+}
+
static int cmd_multi_pack_index_write(int argc, const char **argv)
{
struct option *options;
@@ -68,13 +97,27 @@ static int cmd_multi_pack_index_write(int argc, const char **argv)
OPT_STRING(0, "preferred-pack", &opts.preferred_pack,
N_("preferred-pack"),
N_("pack for reuse when computing a multi-pack bitmap")),
+ OPT_BIT(0, "bitmap", &opts.flags, N_("write multi-pack bitmap"),
+ MIDX_WRITE_BITMAP | MIDX_WRITE_REV_INDEX),
+ OPT_BIT(0, "progress", &opts.flags,
+ N_("force progress reporting"), MIDX_PROGRESS),
+ OPT_BOOL(0, "stdin-packs", &opts.stdin_packs,
+ N_("write multi-pack index containing only given indexes")),
+ OPT_FILENAME(0, "refs-snapshot", &opts.refs_snapshot,
+ N_("refs snapshot for selecting bitmap commits")),
OPT_END(),
};
+ opts.flags |= MIDX_WRITE_BITMAP_HASH_CACHE;
+
+ git_config(git_multi_pack_index_write_config, NULL);
+
options = add_common_options(builtin_multi_pack_index_write_options);
trace2_cmd_mode(argv[0]);
+ if (isatty(2))
+ opts.flags |= MIDX_PROGRESS;
argc = parse_options(argc, argv, NULL,
options, builtin_multi_pack_index_write_usage,
PARSE_OPT_KEEP_UNKNOWN);
@@ -84,16 +127,39 @@ static int cmd_multi_pack_index_write(int argc, const char **argv)
FREE_AND_NULL(options);
+ if (opts.stdin_packs) {
+ struct string_list packs = STRING_LIST_INIT_DUP;
+ int ret;
+
+ read_packs_from_stdin(&packs);
+
+ ret = write_midx_file_only(opts.object_dir, &packs,
+ opts.preferred_pack,
+ opts.refs_snapshot, opts.flags);
+
+ string_list_clear(&packs, 0);
+
+ return ret;
+
+ }
return write_midx_file(opts.object_dir, opts.preferred_pack,
- opts.flags);
+ opts.refs_snapshot, opts.flags);
}
static int cmd_multi_pack_index_verify(int argc, const char **argv)
{
- struct option *options = common_opts;
+ struct option *options;
+ static struct option builtin_multi_pack_index_verify_options[] = {
+ OPT_BIT(0, "progress", &opts.flags,
+ N_("force progress reporting"), MIDX_PROGRESS),
+ OPT_END(),
+ };
+ options = add_common_options(builtin_multi_pack_index_verify_options);
trace2_cmd_mode(argv[0]);
+ if (isatty(2))
+ opts.flags |= MIDX_PROGRESS;
argc = parse_options(argc, argv, NULL,
options, builtin_multi_pack_index_verify_usage,
PARSE_OPT_KEEP_UNKNOWN);
@@ -101,15 +167,25 @@ static int cmd_multi_pack_index_verify(int argc, const char **argv)
usage_with_options(builtin_multi_pack_index_verify_usage,
options);
+ FREE_AND_NULL(options);
+
return verify_midx_file(the_repository, opts.object_dir, opts.flags);
}
static int cmd_multi_pack_index_expire(int argc, const char **argv)
{
- struct option *options = common_opts;
+ struct option *options;
+ static struct option builtin_multi_pack_index_expire_options[] = {
+ OPT_BIT(0, "progress", &opts.flags,
+ N_("force progress reporting"), MIDX_PROGRESS),
+ OPT_END(),
+ };
+ options = add_common_options(builtin_multi_pack_index_expire_options);
trace2_cmd_mode(argv[0]);
+ if (isatty(2))
+ opts.flags |= MIDX_PROGRESS;
argc = parse_options(argc, argv, NULL,
options, builtin_multi_pack_index_expire_usage,
PARSE_OPT_KEEP_UNKNOWN);
@@ -117,6 +193,8 @@ static int cmd_multi_pack_index_expire(int argc, const char **argv)
usage_with_options(builtin_multi_pack_index_expire_usage,
options);
+ FREE_AND_NULL(options);
+
return expire_midx_packs(the_repository, opts.object_dir, opts.flags);
}
@@ -126,6 +204,8 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv)
static struct option builtin_multi_pack_index_repack_options[] = {
OPT_MAGNITUDE(0, "batch-size", &opts.batch_size,
N_("during repack, collect pack-files of smaller size into a batch that is larger than this size")),
+ OPT_BIT(0, "progress", &opts.flags,
+ N_("force progress reporting"), MIDX_PROGRESS),
OPT_END(),
};
@@ -133,6 +213,8 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv)
trace2_cmd_mode(argv[0]);
+ if (isatty(2))
+ opts.flags |= MIDX_PROGRESS;
argc = parse_options(argc, argv, NULL,
options,
builtin_multi_pack_index_repack_usage,
@@ -154,8 +236,6 @@ int cmd_multi_pack_index(int argc, const char **argv,
git_config(git_default_config, NULL);
- if (isatty(2))
- opts.flags |= MIDX_PROGRESS;
argc = parse_options(argc, argv, prefix,
builtin_multi_pack_index_options,
builtin_multi_pack_index_usage,
diff --git a/builtin/mv.c b/builtin/mv.c
index c2f96c8..83a465b 100644
--- a/builtin/mv.c
+++ b/builtin/mv.c
@@ -118,21 +118,23 @@ static int index_range_of_same_dir(const char *src, int length,
int cmd_mv(int argc, const char **argv, const char *prefix)
{
int i, flags, gitmodules_modified = 0;
- int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
+ int verbose = 0, show_only = 0, force = 0, ignore_errors = 0, ignore_sparse = 0;
struct option builtin_mv_options[] = {
OPT__VERBOSE(&verbose, N_("be verbose")),
OPT__DRY_RUN(&show_only, N_("dry run")),
OPT__FORCE(&force, N_("force move/rename even if target exists"),
PARSE_OPT_NOCOMPLETE),
OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")),
+ OPT_BOOL(0, "sparse", &ignore_sparse, N_("allow updating entries outside of the sparse-checkout cone")),
OPT_END(),
};
const char **source, **destination, **dest_path, **submodule_gitfile;
- enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
+ enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX, SPARSE } *modes;
struct stat st;
struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
struct lock_file lock_file = LOCK_INIT;
struct cache_entry *ce;
+ struct string_list only_match_skip_worktree = STRING_LIST_INIT_NODUP;
git_config(git_default_config, NULL);
@@ -176,14 +178,17 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
const char *src = source[i], *dst = destination[i];
int length, src_is_dir;
const char *bad = NULL;
+ int skip_sparse = 0;
if (show_only)
printf(_("Checking rename of '%s' to '%s'\n"), src, dst);
length = strlen(src);
- if (lstat(src, &st) < 0)
- bad = _("bad source");
- else if (!strncmp(src, dst, length) &&
+ if (lstat(src, &st) < 0) {
+ /* only error if existence is expected. */
+ if (modes[i] != SPARSE)
+ bad = _("bad source");
+ } else if (!strncmp(src, dst, length) &&
(dst[length] == 0 || dst[length] == '/')) {
bad = _("can not move directory into itself");
} else if ((src_is_dir = S_ISDIR(st.st_mode))
@@ -212,11 +217,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
dst_len = strlen(dst);
for (j = 0; j < last - first; j++) {
- const char *path = active_cache[first + j]->name;
+ const struct cache_entry *ce = active_cache[first + j];
+ const char *path = ce->name;
source[argc + j] = path;
destination[argc + j] =
prefix_path(dst, dst_len, path + length + 1);
- modes[argc + j] = INDEX;
+ modes[argc + j] = ce_skip_worktree(ce) ? SPARSE : INDEX;
submodule_gitfile[argc + j] = NULL;
}
argc += last - first;
@@ -244,14 +250,36 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
bad = _("multiple sources for the same target");
else if (is_dir_sep(dst[strlen(dst) - 1]))
bad = _("destination directory does not exist");
- else
+ else {
+ /*
+ * We check if the paths are in the sparse-checkout
+ * definition as a very final check, since that
+ * allows us to point the user to the --sparse
+ * option as a way to have a successful run.
+ */
+ if (!ignore_sparse &&
+ !path_in_sparse_checkout(src, &the_index)) {
+ string_list_append(&only_match_skip_worktree, src);
+ skip_sparse = 1;
+ }
+ if (!ignore_sparse &&
+ !path_in_sparse_checkout(dst, &the_index)) {
+ string_list_append(&only_match_skip_worktree, dst);
+ skip_sparse = 1;
+ }
+
+ if (skip_sparse)
+ goto remove_entry;
+
string_list_insert(&src_for_dst, dst);
+ }
if (!bad)
continue;
if (!ignore_errors)
die(_("%s, source=%s, destination=%s"),
bad, src, dst);
+remove_entry:
if (--argc > 0) {
int n = argc - i;
memmove(source + i, source + i + 1,
@@ -266,6 +294,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
}
}
+ if (only_match_skip_worktree.nr) {
+ advise_on_updating_sparse_paths(&only_match_skip_worktree);
+ if (!ignore_errors)
+ return 1;
+ }
+
for (i = 0; i < argc; i++) {
const char *src = source[i], *dst = destination[i];
enum update_mode mode = modes[i];
@@ -274,7 +308,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
printf(_("Renaming %s to %s\n"), src, dst);
if (show_only)
continue;
- if (mode != INDEX && rename(src, dst) < 0) {
+ if (mode != INDEX && mode != SPARSE && rename(src, dst) < 0) {
if (ignore_errors)
continue;
die_errno(_("renaming '%s' failed"), src);
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index ec85035..857be78 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -1124,6 +1124,11 @@ static void write_reused_pack(struct hashfile *f)
break;
offset += ewah_bit_ctz64(word >> offset);
+ /*
+ * Can use bit positions directly, even for MIDX
+ * bitmaps. See comment in try_partial_reuse()
+ * for why.
+ */
write_reused_pack_one(pos + offset, f, &w_curs);
display_progress(progress_state, ++written);
}
@@ -1217,6 +1222,7 @@ static void write_pack_file(void)
if (!pack_to_stdout) {
struct stat st;
struct strbuf tmpname = STRBUF_INIT;
+ char *idx_tmp_name = NULL;
/*
* Packs are runtime accessed in their mtime
@@ -1237,7 +1243,8 @@ static void write_pack_file(void)
warning_errno(_("failed utime() on %s"), pack_tmp_name);
}
- strbuf_addf(&tmpname, "%s-", base_name);
+ strbuf_addf(&tmpname, "%s-%s.", base_name,
+ hash_to_hex(hash));
if (write_bitmap_index) {
bitmap_writer_set_checksum(hash);
@@ -1245,23 +1252,29 @@ static void write_pack_file(void)
&to_pack, written_list, nr_written);
}
- finish_tmp_packfile(&tmpname, pack_tmp_name,
+ stage_tmp_packfiles(&tmpname, pack_tmp_name,
written_list, nr_written,
- &pack_idx_opts, hash);
+ &pack_idx_opts, hash, &idx_tmp_name);
if (write_bitmap_index) {
- strbuf_addf(&tmpname, "%s.bitmap", hash_to_hex(hash));
+ size_t tmpname_len = tmpname.len;
+ strbuf_addstr(&tmpname, "bitmap");
stop_progress(&progress_state);
bitmap_writer_show_progress(progress);
bitmap_writer_select_commits(indexed_commits, indexed_commits_nr, -1);
- bitmap_writer_build(&to_pack);
+ if (bitmap_writer_build(&to_pack) < 0)
+ die(_("failed to write bitmap index"));
bitmap_writer_finish(written_list, nr_written,
tmpname.buf, write_bitmap_options);
write_bitmap_index = 0;
+ strbuf_setlen(&tmpname, tmpname_len);
}
+ rename_tmp_packfile_idx(&tmpname, &idx_tmp_name);
+
+ free(idx_tmp_name);
strbuf_release(&tmpname);
free(pack_tmp_name);
puts(hash_to_hex(hash));
@@ -4135,11 +4148,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
read_packs_list_from_stdin();
if (rev_list_unpacked)
add_unreachable_loose_objects();
- } else if (!use_internal_rev_list)
+ } else if (!use_internal_rev_list) {
read_object_list_from_stdin();
- else {
+ } else {
get_object_list(rp.nr, rp.v);
- strvec_clear(&rp);
}
cleanup_preferred_base();
if (include_tag && nr_result)
@@ -4149,7 +4161,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
the_repository);
if (non_empty && !nr_result)
- return 0;
+ goto cleanup;
if (nr_result) {
trace2_region_enter("pack-objects", "prepare-pack",
the_repository);
@@ -4170,5 +4182,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
" pack-reused %"PRIu32),
written, written_delta, reused, reused_delta,
reuse_packfile_objects);
+
+cleanup:
+ strvec_clear(&rp);
+
return 0;
}
diff --git a/builtin/prune.c b/builtin/prune.c
index 02c6ab7..485c9a3 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -143,7 +143,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
expire = TIME_MAX;
save_commit_buffer = 0;
read_replace_refs = 0;
- ref_paranoia = 1;
repo_init_revisions(the_repository, &revs, prefix);
argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
diff --git a/builtin/pull.c b/builtin/pull.c
index b311ea6..1cfaf9f 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -26,13 +26,13 @@
#include "wt-status.h"
#include "commit-reach.h"
#include "sequencer.h"
+#include "packfile.h"
/**
* Parses the value of --rebase. If value is a false value, returns
* REBASE_FALSE. If value is a true value, returns REBASE_TRUE. If value is
- * "merges", returns REBASE_MERGES. If value is "preserve", returns
- * REBASE_PRESERVE. If value is a invalid value, dies with a fatal error if
- * fatal is true, otherwise returns REBASE_INVALID.
+ * "merges", returns REBASE_MERGES. If value is a invalid value, dies with
+ * a fatal error if fatal is true, otherwise returns REBASE_INVALID.
*/
static enum rebase_type parse_config_rebase(const char *key, const char *value,
int fatal)
@@ -84,6 +84,7 @@ static char *opt_edit;
static char *cleanup_arg;
static char *opt_ff;
static char *opt_verify_signatures;
+static char *opt_verify;
static int opt_autostash = -1;
static int config_autostash;
static int check_trust_level = 1;
@@ -126,7 +127,7 @@ static struct option pull_options[] = {
/* Options passed to git-merge or git-rebase */
OPT_GROUP(N_("Options related to merging")),
OPT_CALLBACK_F('r', "rebase", &opt_rebase,
- "(false|true|merges|preserve|interactive)",
+ "(false|true|merges|interactive)",
N_("incorporate changes by rebasing rather than merging"),
PARSE_OPT_OPTARG, parse_opt_rebase),
OPT_PASSTHRU('n', NULL, &opt_diffstat, NULL,
@@ -160,6 +161,9 @@ static struct option pull_options[] = {
OPT_PASSTHRU(0, "ff-only", &opt_ff, NULL,
N_("abort if fast-forward is not possible"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG),
+ OPT_PASSTHRU(0, "verify", &opt_verify, NULL,
+ N_("control use of pre-merge-commit and commit-msg hooks"),
+ PARSE_OPT_NOARG),
OPT_PASSTHRU(0, "verify-signatures", &opt_verify_signatures, NULL,
N_("verify that the named commit has a valid GPG signature"),
PARSE_OPT_NOARG),
@@ -577,7 +581,7 @@ static int run_fetch(const char *repo, const char **refspecs)
strvec_pushv(&args, refspecs);
} else if (*refspecs)
BUG("refspecs without repo?");
- ret = run_command_v_opt(args.v, RUN_GIT_CMD);
+ ret = run_command_v_opt(args.v, RUN_GIT_CMD | RUN_CLOSE_OBJECT_STORE);
strvec_clear(&args);
return ret;
}
@@ -675,6 +679,8 @@ static int run_merge(void)
strvec_pushf(&args, "--cleanup=%s", cleanup_arg);
if (opt_ff)
strvec_push(&args, opt_ff);
+ if (opt_verify)
+ strvec_push(&args, opt_verify);
if (opt_verify_signatures)
strvec_push(&args, opt_verify_signatures);
strvec_pushv(&args, opt_strategies.v);
@@ -883,8 +889,6 @@ static int run_rebase(const struct object_id *newbase,
/* Options passed to git-rebase */
if (opt_rebase == REBASE_MERGES)
strvec_push(&args, "--rebase-merges");
- else if (opt_rebase == REBASE_PRESERVE)
- strvec_push(&args, "--preserve-merges");
else if (opt_rebase == REBASE_INTERACTIVE)
strvec_push(&args, "--interactive");
if (opt_diffstat)
@@ -933,6 +937,33 @@ static int get_can_ff(struct object_id *orig_head,
return ret;
}
+/*
+ * Is orig_head a descendant of _all_ merge_heads?
+ * Unfortunately is_descendant_of() cannot be used as it asks
+ * if orig_head is a descendant of at least one of them.
+ */
+static int already_up_to_date(struct object_id *orig_head,
+ struct oid_array *merge_heads)
+{
+ int i;
+ struct commit *ours;
+
+ ours = lookup_commit_reference(the_repository, orig_head);
+ for (i = 0; i < merge_heads->nr; i++) {
+ struct commit_list *list = NULL;
+ struct commit *theirs;
+ int ok;
+
+ theirs = lookup_commit_reference(the_repository, &merge_heads->oid[i]);
+ commit_list_insert(theirs, &list);
+ ok = repo_is_descendant_of(the_repository, ours, list);
+ free_commit_list(list);
+ if (!ok)
+ return 0;
+ }
+ return 1;
+}
+
static void show_advice_pull_non_ff(void)
{
advise(_("You have divergent branches and need to specify how to reconcile them.\n"
@@ -957,6 +988,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
struct object_id rebase_fork_point;
int rebase_unspecified = 0;
int can_ff;
+ int divergent;
if (!getenv("GIT_REFLOG_ACTION"))
set_reflog_message(argc, argv);
@@ -1071,15 +1103,16 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
}
can_ff = get_can_ff(&orig_head, &merge_heads);
+ divergent = !can_ff && !already_up_to_date(&orig_head, &merge_heads);
/* ff-only takes precedence over rebase */
if (opt_ff && !strcmp(opt_ff, "--ff-only")) {
- if (!can_ff)
+ if (divergent)
die_ff_impossible();
opt_rebase = REBASE_FALSE;
}
/* If no action specified and we can't fast forward, then warn. */
- if (!opt_ff && rebase_unspecified && !can_ff) {
+ if (!opt_ff && rebase_unspecified && divergent) {
show_advice_pull_non_ff();
die(_("Need to specify how to reconcile divergent branches."));
}
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index 485e7b0..2109c4c 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -38,7 +38,7 @@ static int list_tree(struct object_id *oid)
}
static const char * const read_tree_usage[] = {
- N_("git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"),
+ N_("git read-tree [(-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>) [-u | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"),
NULL
};
@@ -53,24 +53,16 @@ static int index_output_cb(const struct option *opt, const char *arg,
static int exclude_per_directory_cb(const struct option *opt, const char *arg,
int unset)
{
- struct dir_struct *dir;
struct unpack_trees_options *opts;
BUG_ON_OPT_NEG(unset);
opts = (struct unpack_trees_options *)opt->value;
- if (opts->dir)
- die("more than one --exclude-per-directory given.");
-
- dir = xcalloc(1, sizeof(*opts->dir));
- dir->flags |= DIR_SHOW_IGNORED;
- dir->exclude_per_dir = arg;
- opts->dir = dir;
- /* We do not need to nor want to do read-directory
- * here; we are merely interested in reusing the
- * per directory ignore stack mechanism.
- */
+ if (!opts->update)
+ die("--exclude-per-directory is meaningless unless -u");
+ if (strcmp(arg, ".gitignore"))
+ die("--exclude-per-directory argument must be .gitignore");
return 0;
}
@@ -174,6 +166,9 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
if (1 < opts.merge + opts.reset + prefix_set)
die("Which one? -m, --reset, or --prefix?");
+ if (opts.reset)
+ opts.reset = UNPACK_RESET_OVERWRITE_UNTRACKED;
+
/*
* NEEDSWORK
*
@@ -209,8 +204,9 @@ int cmd_read_tree(int argc, const char **argv, const char *cmd_prefix)
if ((opts.update || opts.index_only) && !opts.merge)
die("%s is meaningless without -m, --reset, or --prefix",
opts.update ? "-u" : "-i");
- if ((opts.dir && !opts.update))
- die("--exclude-per-directory is meaningless unless -u");
+ if (opts.update && !opts.reset)
+ opts.preserve_ignored = 0;
+ /* otherwise, opts.preserve_ignored is irrelevant */
if (opts.merge && !opts.index_only)
setup_work_tree();
diff --git a/builtin/rebase.c b/builtin/rebase.c
index eb01f4d..34b4744 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -48,8 +48,7 @@ static GIT_PATH_FUNC(merge_dir, "rebase-merge")
enum rebase_type {
REBASE_UNSPECIFIED = -1,
REBASE_APPLY,
- REBASE_MERGE,
- REBASE_PRESERVE_MERGES
+ REBASE_MERGE
};
enum empty_type {
@@ -163,12 +162,7 @@ enum action {
ACTION_ABORT,
ACTION_QUIT,
ACTION_EDIT_TODO,
- ACTION_SHOW_CURRENT_PATCH,
- ACTION_SHORTEN_OIDS,
- ACTION_EXPAND_OIDS,
- ACTION_CHECK_TODO_LIST,
- ACTION_REARRANGE_SQUASH,
- ACTION_ADD_EXEC
+ ACTION_SHOW_CURRENT_PATCH
};
static const char *action_names[] = { "undefined",
@@ -179,81 +173,6 @@ static const char *action_names[] = { "undefined",
"edit_todo",
"show_current_patch" };
-static int add_exec_commands(struct string_list *commands)
-{
- const char *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
- int res;
-
- if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
- return error_errno(_("could not read '%s'."), todo_file);
-
- if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
- &todo_list)) {
- todo_list_release(&todo_list);
- return error(_("unusable todo list: '%s'"), todo_file);
- }
-
- todo_list_add_exec_commands(&todo_list, commands);
- res = todo_list_write_to_file(the_repository, &todo_list,
- todo_file, NULL, NULL, -1, 0);
- todo_list_release(&todo_list);
-
- if (res)
- return error_errno(_("could not write '%s'."), todo_file);
- return 0;
-}
-
-static int rearrange_squash_in_todo_file(void)
-{
- const char *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
- int res = 0;
-
- if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
- return error_errno(_("could not read '%s'."), todo_file);
- if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
- &todo_list)) {
- todo_list_release(&todo_list);
- return error(_("unusable todo list: '%s'"), todo_file);
- }
-
- res = todo_list_rearrange_squash(&todo_list);
- if (!res)
- res = todo_list_write_to_file(the_repository, &todo_list,
- todo_file, NULL, NULL, -1, 0);
-
- todo_list_release(&todo_list);
-
- if (res)
- return error_errno(_("could not write '%s'."), todo_file);
- return 0;
-}
-
-static int transform_todo_file(unsigned flags)
-{
- const char *todo_file = rebase_path_todo();
- struct todo_list todo_list = TODO_LIST_INIT;
- int res;
-
- if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
- return error_errno(_("could not read '%s'."), todo_file);
-
- if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
- &todo_list)) {
- todo_list_release(&todo_list);
- return error(_("unusable todo list: '%s'"), todo_file);
- }
-
- res = todo_list_write_to_file(the_repository, &todo_list, todo_file,
- NULL, NULL, -1, flags);
- todo_list_release(&todo_list);
-
- if (res)
- return error_errno(_("could not write '%s'."), todo_file);
- return 0;
-}
-
static int edit_todo_file(unsigned flags)
{
const char *todo_file = rebase_path_todo();
@@ -403,7 +322,6 @@ static int run_sequencer_rebase(struct rebase_options *opts,
flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
flags |= opts->root_with_onto ? TODO_LIST_ROOT_WITH_ONTO : 0;
- flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
flags |= opts->reapply_cherry_picks ? TODO_LIST_REAPPLY_CHERRY_PICKS : 0;
flags |= opts->flags & REBASE_NO_QUIET ? TODO_LIST_WARN_SKIPPED_CHERRY_PICKS : 0;
@@ -439,24 +357,6 @@ static int run_sequencer_rebase(struct rebase_options *opts,
break;
}
- case ACTION_SHORTEN_OIDS:
- case ACTION_EXPAND_OIDS:
- ret = transform_todo_file(flags);
- break;
- case ACTION_CHECK_TODO_LIST:
- ret = check_todo_list_from_file(the_repository);
- break;
- case ACTION_REARRANGE_SQUASH:
- ret = rearrange_squash_in_todo_file();
- break;
- case ACTION_ADD_EXEC: {
- struct string_list commands = STRING_LIST_INIT_DUP;
-
- split_exec_commands(opts->cmd, &commands);
- ret = add_exec_commands(&commands);
- string_list_clear(&commands, 0);
- break;
- }
default:
BUG("invalid command '%d'", command);
}
@@ -478,102 +378,9 @@ static int parse_opt_keep_empty(const struct option *opt, const char *arg,
return 0;
}
-static const char * const builtin_rebase_interactive_usage[] = {
- N_("git rebase--interactive [<options>]"),
- NULL
-};
-
-int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
-{
- struct rebase_options opts = REBASE_OPTIONS_INIT;
- struct object_id squash_onto = *null_oid();
- enum action command = ACTION_NONE;
- struct option options[] = {
- OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
- REBASE_FORCE),
- OPT_CALLBACK_F('k', "keep-empty", &options, NULL,
- N_("keep commits which start empty"),
- PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
- parse_opt_keep_empty),
- OPT_BOOL_F(0, "allow-empty-message", &opts.allow_empty_message,
- N_("allow commits with empty messages"),
- PARSE_OPT_HIDDEN),
- OPT_BOOL(0, "rebase-merges", &opts.rebase_merges, N_("rebase merge commits")),
- OPT_BOOL(0, "rebase-cousins", &opts.rebase_cousins,
- N_("keep original branch points of cousins")),
- OPT_BOOL(0, "autosquash", &opts.autosquash,
- N_("move commits that begin with squash!/fixup!")),
- OPT_BOOL(0, "signoff", &opts.signoff, N_("sign commits")),
- OPT_BIT('v', "verbose", &opts.flags,
- N_("display a diffstat of what changed upstream"),
- REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
- OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
- ACTION_CONTINUE),
- OPT_CMDMODE(0, "skip", &command, N_("skip commit"), ACTION_SKIP),
- OPT_CMDMODE(0, "edit-todo", &command, N_("edit the todo list"),
- ACTION_EDIT_TODO),
- OPT_CMDMODE(0, "show-current-patch", &command, N_("show the current patch"),
- ACTION_SHOW_CURRENT_PATCH),
- OPT_CMDMODE(0, "shorten-ids", &command,
- N_("shorten commit ids in the todo list"), ACTION_SHORTEN_OIDS),
- OPT_CMDMODE(0, "expand-ids", &command,
- N_("expand commit ids in the todo list"), ACTION_EXPAND_OIDS),
- OPT_CMDMODE(0, "check-todo-list", &command,
- N_("check the todo list"), ACTION_CHECK_TODO_LIST),
- OPT_CMDMODE(0, "rearrange-squash", &command,
- N_("rearrange fixup/squash lines"), ACTION_REARRANGE_SQUASH),
- OPT_CMDMODE(0, "add-exec-commands", &command,
- N_("insert exec commands in todo list"), ACTION_ADD_EXEC),
- { OPTION_CALLBACK, 0, "onto", &opts.onto, N_("onto"), N_("onto"),
- PARSE_OPT_NONEG, parse_opt_commit, 0 },
- { OPTION_CALLBACK, 0, "restrict-revision", &opts.restrict_revision,
- N_("restrict-revision"), N_("restrict revision"),
- PARSE_OPT_NONEG, parse_opt_commit, 0 },
- { OPTION_CALLBACK, 0, "squash-onto", &squash_onto, N_("squash-onto"),
- N_("squash onto"), PARSE_OPT_NONEG, parse_opt_object_id, 0 },
- { OPTION_CALLBACK, 0, "upstream", &opts.upstream, N_("upstream"),
- N_("the upstream commit"), PARSE_OPT_NONEG, parse_opt_commit,
- 0 },
- OPT_STRING(0, "head-name", &opts.head_name, N_("head-name"), N_("head name")),
- { OPTION_STRING, 'S', "gpg-sign", &opts.gpg_sign_opt, N_("key-id"),
- N_("GPG-sign commits"),
- PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
- OPT_STRING(0, "strategy", &opts.strategy, N_("strategy"),
- N_("rebase strategy")),
- OPT_STRING(0, "strategy-opts", &opts.strategy_opts, N_("strategy-opts"),
- N_("strategy options")),
- OPT_STRING(0, "switch-to", &opts.switch_to, N_("switch-to"),
- N_("the branch or commit to checkout")),
- OPT_STRING(0, "onto-name", &opts.onto_name, N_("onto-name"), N_("onto name")),
- OPT_STRING(0, "cmd", &opts.cmd, N_("cmd"), N_("the command to run")),
- OPT_RERERE_AUTOUPDATE(&opts.allow_rerere_autoupdate),
- OPT_BOOL(0, "reschedule-failed-exec", &opts.reschedule_failed_exec,
- N_("automatically re-schedule any `exec` that fails")),
- OPT_END()
- };
-
- opts.rebase_cousins = -1;
-
- if (argc == 1)
- usage_with_options(builtin_rebase_interactive_usage, options);
-
- argc = parse_options(argc, argv, prefix, options,
- builtin_rebase_interactive_usage, PARSE_OPT_KEEP_ARGV0);
-
- if (!is_null_oid(&squash_onto))
- opts.squash_onto = &squash_onto;
-
- if (opts.rebase_cousins >= 0 && !opts.rebase_merges)
- warning(_("--[no-]rebase-cousins has no effect without "
- "--rebase-merges"));
-
- return !!run_sequencer_rebase(&opts, command);
-}
-
static int is_merge(struct rebase_options *opts)
{
- return opts->type == REBASE_MERGE ||
- opts->type == REBASE_PRESERVE_MERGES;
+ return opts->type == REBASE_MERGE;
}
static void imply_merge(struct rebase_options *opts, const char *option)
@@ -583,7 +390,6 @@ static void imply_merge(struct rebase_options *opts, const char *option)
die(_("%s requires the merge backend"), option);
break;
case REBASE_MERGE:
- case REBASE_PRESERVE_MERGES:
break;
default:
opts->type = REBASE_MERGE; /* implied */
@@ -741,7 +547,6 @@ static int finish_rebase(struct rebase_options *opts)
delete_ref(NULL, "REBASE_HEAD", NULL, REF_NO_DEREF);
unlink(git_path_auto_merge(the_repository));
apply_autostash(state_dir_path("autostash", opts));
- close_object_store(the_repository->objects);
/*
* We ignore errors in 'git maintenance run --auto', since the
* user should see them.
@@ -763,28 +568,6 @@ static int finish_rebase(struct rebase_options *opts)
return ret;
}
-static struct commit *peel_committish(const char *name)
-{
- struct object *obj;
- struct object_id oid;
-
- if (get_oid(name, &oid))
- return NULL;
- obj = parse_object(the_repository, &oid);
- return (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
-}
-
-static void add_var(struct strbuf *buf, const char *name, const char *value)
-{
- if (!value)
- strbuf_addf(buf, "unset %s; ", name);
- else {
- strbuf_addf(buf, "%s=", name);
- sq_quote_buf(buf, value);
- strbuf_addstr(buf, "; ");
- }
-}
-
static int move_to_original_branch(struct rebase_options *opts)
{
struct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;
@@ -941,10 +724,7 @@ static int run_am(struct rebase_options *opts)
static int run_specific_rebase(struct rebase_options *opts, enum action action)
{
- const char *argv[] = { NULL, NULL };
- struct strbuf script_snippet = STRBUF_INIT, buf = STRBUF_INIT;
int status;
- const char *backend, *backend_func;
if (opts->type == REBASE_MERGE) {
/* Run sequencer-based rebase */
@@ -961,87 +741,11 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
}
status = run_sequencer_rebase(opts, action);
- goto finished_rebase;
- }
-
- if (opts->type == REBASE_APPLY) {
+ } else if (opts->type == REBASE_APPLY)
status = run_am(opts);
- goto finished_rebase;
- }
-
- add_var(&script_snippet, "GIT_DIR", absolute_path(get_git_dir()));
- add_var(&script_snippet, "state_dir", opts->state_dir);
-
- add_var(&script_snippet, "upstream_name", opts->upstream_name);
- add_var(&script_snippet, "upstream", opts->upstream ?
- oid_to_hex(&opts->upstream->object.oid) : NULL);
- add_var(&script_snippet, "head_name",
- opts->head_name ? opts->head_name : "detached HEAD");
- add_var(&script_snippet, "orig_head", oid_to_hex(&opts->orig_head));
- add_var(&script_snippet, "onto", opts->onto ?
- oid_to_hex(&opts->onto->object.oid) : NULL);
- add_var(&script_snippet, "onto_name", opts->onto_name);
- add_var(&script_snippet, "revisions", opts->revisions);
- add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
- oid_to_hex(&opts->restrict_revision->object.oid) : NULL);
- sq_quote_argv_pretty(&buf, opts->git_am_opts.v);
- add_var(&script_snippet, "git_am_opt", buf.buf);
- strbuf_release(&buf);
- add_var(&script_snippet, "verbose",
- opts->flags & REBASE_VERBOSE ? "t" : "");
- add_var(&script_snippet, "diffstat",
- opts->flags & REBASE_DIFFSTAT ? "t" : "");
- add_var(&script_snippet, "force_rebase",
- opts->flags & REBASE_FORCE ? "t" : "");
- if (opts->switch_to)
- add_var(&script_snippet, "switch_to", opts->switch_to);
- add_var(&script_snippet, "action", opts->action ? opts->action : "");
- add_var(&script_snippet, "signoff", opts->signoff ? "--signoff" : "");
- add_var(&script_snippet, "allow_rerere_autoupdate",
- opts->allow_rerere_autoupdate ?
- opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
- "--rerere-autoupdate" : "--no-rerere-autoupdate" : "");
- add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
- add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : "");
- add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
- add_var(&script_snippet, "cmd", opts->cmd);
- add_var(&script_snippet, "allow_empty_message",
- opts->allow_empty_message ? "--allow-empty-message" : "");
- add_var(&script_snippet, "rebase_merges",
- opts->rebase_merges ? "t" : "");
- add_var(&script_snippet, "rebase_cousins",
- opts->rebase_cousins ? "t" : "");
- add_var(&script_snippet, "strategy", opts->strategy);
- add_var(&script_snippet, "strategy_opts", opts->strategy_opts);
- add_var(&script_snippet, "rebase_root", opts->root ? "t" : "");
- add_var(&script_snippet, "squash_onto",
- opts->squash_onto ? oid_to_hex(opts->squash_onto) : "");
- add_var(&script_snippet, "git_format_patch_opt",
- opts->git_format_patch_opt.buf);
-
- if (is_merge(opts) &&
- !(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
- strbuf_addstr(&script_snippet,
- "GIT_SEQUENCE_EDITOR=:; export GIT_SEQUENCE_EDITOR; ");
- opts->autosquash = 0;
- }
-
- switch (opts->type) {
- case REBASE_PRESERVE_MERGES:
- backend = "git-rebase--preserve-merges";
- backend_func = "git_rebase__preserve_merges";
- break;
- default:
+ else
BUG("Unhandled rebase type %d", opts->type);
- break;
- }
-
- strbuf_addf(&script_snippet,
- ". git-sh-setup && . %s && %s", backend, backend_func);
- argv[0] = script_snippet.buf;
- status = run_command_v_opt(argv, RUN_USING_SHELL);
-finished_rebase:
if (opts->dont_finish_rebase)
; /* do nothing */
else if (opts->type == REBASE_MERGE)
@@ -1059,8 +763,6 @@ finished_rebase:
die("Nothing to do");
}
- strbuf_release(&script_snippet);
-
return status ? -1 : 0;
}
@@ -1196,7 +898,7 @@ static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
return 0;
}
-/* -i followed by -p is still explicitly interactive, but -p alone is not */
+/* -i followed by -r is still explicitly interactive, but -r alone is not */
static int parse_opt_interactive(const struct option *opt, const char *arg,
int unset)
{
@@ -1314,6 +1016,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
char *squash_onto_name = NULL;
int reschedule_failed_exec = -1;
int allow_preemptive_ff = 1;
+ int preserve_merges_selected = 0;
struct option builtin_rebase_options[] = {
OPT_STRING(0, "onto", &options.onto_name,
N_("revision"),
@@ -1378,10 +1081,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
N_("let the user edit the list of commits to rebase"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG,
parse_opt_interactive),
- OPT_SET_INT_F('p', "preserve-merges", &options.type,
+ OPT_SET_INT_F('p', "preserve-merges", &preserve_merges_selected,
N_("(DEPRECATED) try to recreate merges instead of "
"ignoring them"),
- REBASE_PRESERVE_MERGES, PARSE_OPT_HIDDEN),
+ 1, PARSE_OPT_HIDDEN),
OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
OPT_CALLBACK_F(0, "empty", &options, "{drop,keep,ask}",
N_("how to handle commits that become empty"),
@@ -1431,6 +1134,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
usage_with_options(builtin_rebase_usage,
builtin_rebase_options);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
options.allow_empty_message = 1;
git_config(rebase_config, &options);
/* options.gpg_sign_opt will be either "-S" or NULL */
@@ -1449,8 +1155,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
strbuf_reset(&buf);
strbuf_addf(&buf, "%s/rewritten", merge_dir());
if (is_directory(buf.buf)) {
- options.type = REBASE_PRESERVE_MERGES;
- options.flags |= REBASE_INTERACTIVE_EXPLICIT;
+ die("`rebase -p` is no longer supported");
} else {
strbuf_reset(&buf);
strbuf_addf(&buf, "%s/interactive", merge_dir());
@@ -1471,6 +1176,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
builtin_rebase_options,
builtin_rebase_usage, 0);
+ if (preserve_merges_selected)
+ die(_("--preserve-merges was replaced by --rebase-merges"));
+
if (action != ACTION_NONE && total_argc != 2) {
usage_with_options(builtin_rebase_usage,
builtin_rebase_options);
@@ -1480,10 +1188,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
usage_with_options(builtin_rebase_usage,
builtin_rebase_options);
- if (options.type == REBASE_PRESERVE_MERGES)
- warning(_("git rebase --preserve-merges is deprecated. "
- "Use --rebase-merges instead."));
-
if (keep_base) {
if (options.onto_name)
die(_("cannot combine '--keep-base' with '--onto'"));
@@ -1575,7 +1279,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
die(_("could not move back to %s"),
oid_to_hex(&options.orig_head));
remove_branch_state(the_repository, 0);
- ret = !!finish_rebase(&options);
+ ret = finish_rebase(&options);
goto cleanup;
}
case ACTION_QUIT: {
@@ -1584,11 +1288,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
struct replay_opts replay = REPLAY_OPTS_INIT;
replay.action = REPLAY_INTERACTIVE_REBASE;
- ret = !!sequencer_remove_state(&replay);
+ ret = sequencer_remove_state(&replay);
} else {
strbuf_reset(&buf);
strbuf_addstr(&buf, options.state_dir);
- ret = !!remove_dir_recursively(&buf, 0);
+ ret = remove_dir_recursively(&buf, 0);
if (ret)
error(_("could not remove '%s'"),
options.state_dir);
@@ -1703,7 +1407,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (options.ignore_date)
strvec_push(&options.git_am_opts, "--ignore-date");
} else {
- /* REBASE_MERGE and PRESERVE_MERGES */
+ /* REBASE_MERGE */
if (ignore_whitespace) {
string_list_append(&strategy_options,
"ignore-space-change");
@@ -1729,7 +1433,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
case REBASE_APPLY:
die(_("--strategy requires --merge or --interactive"));
case REBASE_MERGE:
- case REBASE_PRESERVE_MERGES:
/* compatible */
break;
case REBASE_UNSPECIFIED:
@@ -1781,7 +1484,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
switch (options.type) {
case REBASE_MERGE:
- case REBASE_PRESERVE_MERGES:
options.state_dir = merge_dir();
break;
case REBASE_APPLY:
@@ -1806,28 +1508,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.reschedule_failed_exec = reschedule_failed_exec;
if (options.signoff) {
- if (options.type == REBASE_PRESERVE_MERGES)
- die("cannot combine '--signoff' with "
- "'--preserve-merges'");
strvec_push(&options.git_am_opts, "--signoff");
options.flags |= REBASE_FORCE;
}
- if (options.type == REBASE_PRESERVE_MERGES) {
- /*
- * Note: incompatibility with --signoff handled in signoff block above
- * Note: incompatibility with --interactive is just a strong warning;
- * git-rebase.txt caveats with "unless you know what you are doing"
- */
- if (options.rebase_merges)
- die(_("cannot combine '--preserve-merges' with "
- "'--rebase-merges'"));
-
- if (options.reschedule_failed_exec)
- die(_("error: cannot combine '--preserve-merges' with "
- "'--reschedule-failed-exec'"));
- }
-
if (!options.root) {
if (argc < 1) {
struct branch *branch;
@@ -1846,7 +1530,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (!strcmp(options.upstream_name, "-"))
options.upstream_name = "@{-1}";
}
- options.upstream = peel_committish(options.upstream_name);
+ options.upstream =
+ lookup_commit_reference_by_name(options.upstream_name);
if (!options.upstream)
die(_("invalid upstream '%s'"), options.upstream_name);
options.upstream_arg = options.upstream_name;
@@ -1889,7 +1574,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.onto = lookup_commit_or_die(&merge_base,
options.onto_name);
} else {
- options.onto = peel_committish(options.onto_name);
+ options.onto =
+ lookup_commit_reference_by_name(options.onto_name);
if (!options.onto)
die(_("Does not point to a valid commit '%s'"),
options.onto_name);
@@ -1914,13 +1600,15 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
die_if_checked_out(buf.buf, 1);
options.head_name = xstrdup(buf.buf);
/* If not is it a valid ref (branch or commit)? */
- } else if (!get_oid(branch_name, &options.orig_head) &&
- lookup_commit_reference(the_repository,
- &options.orig_head))
+ } else {
+ struct commit *commit =
+ lookup_commit_reference_by_name(branch_name);
+ if (!commit)
+ die(_("no such branch/commit '%s'"),
+ branch_name);
+ oidcpy(&options.orig_head, &commit->object.oid);
options.head_name = NULL;
- else
- die(_("no such branch/commit '%s'"),
- branch_name);
+ }
} else if (argc == 0) {
/* Do not need to switch branches, we are already on it. */
options.head_name =
@@ -1960,7 +1648,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
if (require_clean_work_tree(the_repository, "rebase",
_("Please commit or stash them."), 1, 1)) {
- ret = 1;
+ ret = -1;
goto cleanup;
}
@@ -1995,7 +1683,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
RESET_HEAD_RUN_POST_CHECKOUT_HOOK,
NULL, buf.buf,
DEFAULT_REFLOG_ACTION) < 0) {
- ret = !!error(_("could not switch to "
+ ret = error(_("could not switch to "
"%s"),
options.switch_to);
goto cleanup;
@@ -2010,7 +1698,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
else
printf(_("Current branch %s is up to date.\n"),
branch_name);
- ret = !!finish_rebase(&options);
+ ret = finish_rebase(&options);
goto cleanup;
} else if (!(options.flags & REBASE_NO_QUIET))
; /* be quiet */
@@ -2088,7 +1776,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
RESET_HEAD_REFS_ONLY, "HEAD", msg.buf,
DEFAULT_REFLOG_ACTION);
strbuf_release(&msg);
- ret = !!finish_rebase(&options);
+ ret = finish_rebase(&options);
goto cleanup;
}
@@ -2102,7 +1790,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.revisions = revisions.buf;
run_rebase:
- ret = !!run_specific_rebase(&options, action);
+ ret = run_specific_rebase(&options, action);
cleanup:
strbuf_release(&buf);
@@ -2113,5 +1801,5 @@ cleanup:
free(options.strategy);
strbuf_release(&options.git_format_patch_opt);
free(squash_onto_name);
- return ret;
+ return !!ret;
}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 2d1f97e..49b846d 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -7,6 +7,7 @@
#include "pkt-line.h"
#include "sideband.h"
#include "run-command.h"
+#include "hook.h"
#include "exec-cmd.h"
#include "commit.h"
#include "object.h"
@@ -134,6 +135,10 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
if (status)
return status;
+ status = git_gpg_config(var, value, NULL);
+ if (status)
+ return status;
+
if (strcmp(var, "receive.denydeletes") == 0) {
deny_deletes = git_config_bool(var, value);
return 0;
@@ -1306,7 +1311,7 @@ static void refuse_unconfigured_deny_delete_current(void)
rp_error("%s", _(refuse_unconfigured_deny_delete_current_msg));
}
-static int command_singleton_iterator(void *cb_data, struct object_id *oid);
+static const struct object_id *command_singleton_iterator(void *cb_data);
static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
{
struct shallow_lock shallow_lock = SHALLOW_LOCK_INIT;
@@ -1463,7 +1468,7 @@ static const char *update_worktree(unsigned char *sha1, const struct worktree *w
strvec_pushf(&env, "GIT_DIR=%s", absolute_path(git_dir));
- if (!find_hook(push_to_checkout_hook))
+ if (!hook_exists(push_to_checkout_hook))
retval = push_to_deploy(sha1, &env, work_tree);
else
retval = push_to_checkout(sha1, &env, work_tree);
@@ -1731,16 +1736,15 @@ static void check_aliased_updates(struct command *commands)
string_list_clear(&ref_list, 0);
}
-static int command_singleton_iterator(void *cb_data, struct object_id *oid)
+static const struct object_id *command_singleton_iterator(void *cb_data)
{
struct command **cmd_list = cb_data;
struct command *cmd = *cmd_list;
if (!cmd || is_null_oid(&cmd->new_oid))
- return -1; /* end of list */
+ return NULL;
*cmd_list = NULL; /* this returns only one */
- oidcpy(oid, &cmd->new_oid);
- return 0;
+ return &cmd->new_oid;
}
static void set_connectivity_errors(struct command *commands,
@@ -1770,7 +1774,7 @@ struct iterate_data {
struct shallow_info *si;
};
-static int iterate_receive_command_list(void *cb_data, struct object_id *oid)
+static const struct object_id *iterate_receive_command_list(void *cb_data)
{
struct iterate_data *data = cb_data;
struct command **cmd_list = &data->cmds;
@@ -1781,13 +1785,11 @@ static int iterate_receive_command_list(void *cb_data, struct object_id *oid)
/* to be checked in update_shallow_ref() */
continue;
if (!is_null_oid(&cmd->new_oid) && !cmd->skip_update) {
- oidcpy(oid, &cmd->new_oid);
*cmd_list = cmd->next;
- return 0;
+ return &cmd->new_oid;
}
}
- *cmd_list = NULL;
- return -1; /* end of list */
+ return NULL;
}
static void reject_updates_to_hidden(struct command *commands)
@@ -2477,7 +2479,8 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT__QUIET(&quiet, N_("quiet")),
OPT_HIDDEN_BOOL(0, "stateless-rpc", &stateless_rpc, NULL),
- OPT_HIDDEN_BOOL(0, "advertise-refs", &advertise_refs, NULL),
+ OPT_HIDDEN_BOOL(0, "http-backend-info-refs", &advertise_refs, NULL),
+ OPT_ALIAS(0, "advertise-refs", "http-backend-info-refs"),
OPT_HIDDEN_BOOL(0, "reject-thin-pack-for-testing", &reject_thin, NULL),
OPT_END()
};
@@ -2580,10 +2583,9 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
proc.no_stdin = 1;
proc.stdout_to_stderr = 1;
proc.err = use_sideband ? -1 : 0;
- proc.git_cmd = 1;
+ proc.git_cmd = proc.close_object_store = 1;
proc.argv = argv_gc_auto;
- close_object_store(the_repository->objects);
if (!start_command(&proc)) {
if (use_sideband)
copy_to_sideband(proc.err, -1, NULL);
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 09541d1..175c83e 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -629,8 +629,9 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
free_worktrees(worktrees);
for (i = 0; i < collected.nr; i++) {
struct collected_reflog *e = collected.e[i];
+
set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog);
- status |= reflog_expire(e->reflog, &e->oid, flags,
+ status |= reflog_expire(e->reflog, flags,
reflog_expiry_prepare,
should_expire_reflog_ent,
reflog_expiry_cleanup,
@@ -642,17 +643,17 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
for (; i < argc; i++) {
char *ref;
- struct object_id oid;
- if (!dwim_log(argv[i], strlen(argv[i]), &oid, &ref)) {
+ if (!dwim_log(argv[i], strlen(argv[i]), NULL, &ref)) {
status |= error(_("%s points nowhere!"), argv[i]);
continue;
}
set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref);
- status |= reflog_expire(ref, &oid, flags,
+ status |= reflog_expire(ref, flags,
reflog_expiry_prepare,
should_expire_reflog_ent,
reflog_expiry_cleanup,
&cb);
+ free(ref);
}
return status;
}
@@ -700,7 +701,6 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
for ( ; i < argc; i++) {
const char *spec = strstr(argv[i], "@{");
- struct object_id oid;
char *ep, *ref;
int recno;
@@ -709,7 +709,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
continue;
}
- if (!dwim_log(argv[i], spec - argv[i], &oid, &ref)) {
+ if (!dwim_log(argv[i], spec - argv[i], NULL, &ref)) {
status |= error(_("no reflog for '%s'"), argv[i]);
continue;
}
@@ -724,7 +724,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
cb.cmd.expire_total = 0;
}
- status |= reflog_expire(ref, &oid, flags,
+ status |= reflog_expire(ref, flags,
reflog_expiry_prepare,
should_expire_reflog_ent,
reflog_expiry_cleanup,
diff --git a/builtin/remote.c b/builtin/remote.c
index 7f88e6c..299c466 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -318,6 +318,9 @@ static int config_read_branches(const char *key, const char *value, void *cb)
* truth value with >= REBASE_TRUE.
*/
info->rebase = rebase_parse_value(value);
+ if (info->rebase == REBASE_INVALID)
+ warning(_("unhandled branch.%s.rebase=%s; assuming "
+ "'true'"), name, value);
break;
case PUSH_REMOTE:
if (info->push_remote_name)
@@ -344,6 +347,14 @@ struct ref_states {
int queried;
};
+#define REF_STATES_INIT { \
+ .new_refs = STRING_LIST_INIT_DUP, \
+ .stale = STRING_LIST_INIT_DUP, \
+ .tracked = STRING_LIST_INIT_DUP, \
+ .heads = STRING_LIST_INIT_DUP, \
+ .push = STRING_LIST_INIT_DUP, \
+}
+
static int get_ref_states(const struct ref *remote_refs, struct ref_states *states)
{
struct ref *fetch_map = NULL, **tail = &fetch_map;
@@ -355,9 +366,6 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
die(_("Could not get fetch map for refspec %s"),
states->remote->fetch.raw[i]);
- states->new_refs.strdup_strings = 1;
- states->tracked.strdup_strings = 1;
- states->stale.strdup_strings = 1;
for (ref = fetch_map; ref; ref = ref->next) {
if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
string_list_append(&states->new_refs, abbrev_branch(ref->name));
@@ -406,7 +414,6 @@ static int get_push_ref_states(const struct ref *remote_refs,
match_push_refs(local_refs, &push_map, &remote->push, MATCH_REFS_NONE);
- states->push.strdup_strings = 1;
for (ref = push_map; ref; ref = ref->next) {
struct string_list_item *item;
struct push_info *info;
@@ -449,7 +456,6 @@ static int get_push_ref_states_noquery(struct ref_states *states)
if (remote->mirror)
return 0;
- states->push.strdup_strings = 1;
if (!remote->push.nr) {
item = string_list_append(&states->push, _("(matching)"));
info = item->util = xcalloc(1, sizeof(struct push_info));
@@ -483,7 +489,6 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
refspec.force = 0;
refspec.pattern = 1;
refspec.src = refspec.dst = "refs/heads/*";
- states->heads.strdup_strings = 1;
get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
fetch_map, 1);
@@ -970,26 +975,31 @@ static int get_remote_ref_states(const char *name,
}
struct show_info {
- struct string_list *list;
- struct ref_states *states;
+ struct string_list list;
+ struct ref_states states;
int width, width2;
int any_rebase;
};
+#define SHOW_INFO_INIT { \
+ .list = STRING_LIST_INIT_DUP, \
+ .states = REF_STATES_INIT, \
+}
+
static int add_remote_to_show_info(struct string_list_item *item, void *cb_data)
{
struct show_info *info = cb_data;
int n = strlen(item->string);
if (n > info->width)
info->width = n;
- string_list_insert(info->list, item->string);
+ string_list_insert(&info->list, item->string);
return 0;
}
static int show_remote_info_item(struct string_list_item *item, void *cb_data)
{
struct show_info *info = cb_data;
- struct ref_states *states = info->states;
+ struct ref_states *states = &info->states;
const char *name = item->string;
if (states->queried) {
@@ -1016,7 +1026,7 @@ static int show_remote_info_item(struct string_list_item *item, void *cb_data)
static int add_local_to_show_info(struct string_list_item *branch_item, void *cb_data)
{
struct show_info *show_info = cb_data;
- struct ref_states *states = show_info->states;
+ struct ref_states *states = &show_info->states;
struct branch_info *branch_info = branch_item->util;
struct string_list_item *item;
int n;
@@ -1029,7 +1039,7 @@ static int add_local_to_show_info(struct string_list_item *branch_item, void *cb
if (branch_info->rebase >= REBASE_TRUE)
show_info->any_rebase = 1;
- item = string_list_insert(show_info->list, branch_item->string);
+ item = string_list_insert(&show_info->list, branch_item->string);
item->util = branch_info;
return 0;
@@ -1084,7 +1094,7 @@ static int add_push_to_show_info(struct string_list_item *push_item, void *cb_da
show_info->width = n;
if ((n = strlen(push_info->dest)) > show_info->width2)
show_info->width2 = n;
- item = string_list_append(show_info->list, push_item->string);
+ item = string_list_append(&show_info->list, push_item->string);
item->util = push_item->util;
return 0;
}
@@ -1212,9 +1222,7 @@ static int show(int argc, const char **argv)
OPT_BOOL('n', NULL, &no_query, N_("do not query remotes")),
OPT_END()
};
- struct ref_states states;
- struct string_list info_list = STRING_LIST_INIT_NODUP;
- struct show_info info;
+ struct show_info info = SHOW_INFO_INIT;
argc = parse_options(argc, argv, NULL, options, builtin_remote_show_usage,
0);
@@ -1225,26 +1233,22 @@ static int show(int argc, const char **argv)
if (!no_query)
query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES);
- memset(&states, 0, sizeof(states));
- memset(&info, 0, sizeof(info));
- info.states = &states;
- info.list = &info_list;
for (; argc; argc--, argv++) {
int i;
const char **url;
int url_nr;
- get_remote_ref_states(*argv, &states, query_flag);
+ get_remote_ref_states(*argv, &info.states, query_flag);
printf_ln(_("* remote %s"), *argv);
- printf_ln(_(" Fetch URL: %s"), states.remote->url_nr > 0 ?
- states.remote->url[0] : _("(no URL)"));
- if (states.remote->pushurl_nr) {
- url = states.remote->pushurl;
- url_nr = states.remote->pushurl_nr;
+ printf_ln(_(" Fetch URL: %s"), info.states.remote->url_nr > 0 ?
+ info.states.remote->url[0] : _("(no URL)"));
+ if (info.states.remote->pushurl_nr) {
+ url = info.states.remote->pushurl;
+ url_nr = info.states.remote->pushurl_nr;
} else {
- url = states.remote->url;
- url_nr = states.remote->url_nr;
+ url = info.states.remote->url;
+ url_nr = info.states.remote->url_nr;
}
for (i = 0; i < url_nr; i++)
/*
@@ -1257,57 +1261,57 @@ static int show(int argc, const char **argv)
printf_ln(_(" Push URL: %s"), _("(no URL)"));
if (no_query)
printf_ln(_(" HEAD branch: %s"), _("(not queried)"));
- else if (!states.heads.nr)
+ else if (!info.states.heads.nr)
printf_ln(_(" HEAD branch: %s"), _("(unknown)"));
- else if (states.heads.nr == 1)
- printf_ln(_(" HEAD branch: %s"), states.heads.items[0].string);
+ else if (info.states.heads.nr == 1)
+ printf_ln(_(" HEAD branch: %s"), info.states.heads.items[0].string);
else {
printf(_(" HEAD branch (remote HEAD is ambiguous,"
" may be one of the following):\n"));
- for (i = 0; i < states.heads.nr; i++)
- printf(" %s\n", states.heads.items[i].string);
+ for (i = 0; i < info.states.heads.nr; i++)
+ printf(" %s\n", info.states.heads.items[i].string);
}
/* remote branch info */
info.width = 0;
- for_each_string_list(&states.new_refs, add_remote_to_show_info, &info);
- for_each_string_list(&states.tracked, add_remote_to_show_info, &info);
- for_each_string_list(&states.stale, add_remote_to_show_info, &info);
- if (info.list->nr)
+ for_each_string_list(&info.states.new_refs, add_remote_to_show_info, &info);
+ for_each_string_list(&info.states.tracked, add_remote_to_show_info, &info);
+ for_each_string_list(&info.states.stale, add_remote_to_show_info, &info);
+ if (info.list.nr)
printf_ln(Q_(" Remote branch:%s",
" Remote branches:%s",
- info.list->nr),
+ info.list.nr),
no_query ? _(" (status not queried)") : "");
- for_each_string_list(info.list, show_remote_info_item, &info);
- string_list_clear(info.list, 0);
+ for_each_string_list(&info.list, show_remote_info_item, &info);
+ string_list_clear(&info.list, 0);
/* git pull info */
info.width = 0;
info.any_rebase = 0;
for_each_string_list(&branch_list, add_local_to_show_info, &info);
- if (info.list->nr)
+ if (info.list.nr)
printf_ln(Q_(" Local branch configured for 'git pull':",
" Local branches configured for 'git pull':",
- info.list->nr));
- for_each_string_list(info.list, show_local_info_item, &info);
- string_list_clear(info.list, 0);
+ info.list.nr));
+ for_each_string_list(&info.list, show_local_info_item, &info);
+ string_list_clear(&info.list, 0);
/* git push info */
- if (states.remote->mirror)
+ if (info.states.remote->mirror)
printf_ln(_(" Local refs will be mirrored by 'git push'"));
info.width = info.width2 = 0;
- for_each_string_list(&states.push, add_push_to_show_info, &info);
- QSORT(info.list->items, info.list->nr, cmp_string_with_push);
- if (info.list->nr)
+ for_each_string_list(&info.states.push, add_push_to_show_info, &info);
+ QSORT(info.list.items, info.list.nr, cmp_string_with_push);
+ if (info.list.nr)
printf_ln(Q_(" Local ref configured for 'git push'%s:",
" Local refs configured for 'git push'%s:",
- info.list->nr),
+ info.list.nr),
no_query ? _(" (status not queried)") : "");
- for_each_string_list(info.list, show_push_info_item, &info);
- string_list_clear(info.list, 0);
+ for_each_string_list(&info.list, show_push_info_item, &info);
+ string_list_clear(&info.list, 0);
- free_remote_ref_states(&states);
+ free_remote_ref_states(&info.states);
}
return result;
@@ -1334,8 +1338,7 @@ static int set_head(int argc, const char **argv)
if (!opt_a && !opt_d && argc == 2) {
head_name = xstrdup(argv[1]);
} else if (opt_a && !opt_d && argc == 1) {
- struct ref_states states;
- memset(&states, 0, sizeof(states));
+ struct ref_states states = REF_STATES_INIT;
get_remote_ref_states(argv[0], &states, GET_HEAD_NAMES);
if (!states.heads.nr)
result |= error(_("Cannot determine remote HEAD"));
@@ -1374,14 +1377,13 @@ static int set_head(int argc, const char **argv)
static int prune_remote(const char *remote, int dry_run)
{
int result = 0;
- struct ref_states states;
+ struct ref_states states = REF_STATES_INIT;
struct string_list refs_to_prune = STRING_LIST_INIT_NODUP;
struct string_list_item *item;
const char *dangling_msg = dry_run
? _(" %s will become dangling!")
: _(" %s has become dangling!");
- memset(&states, 0, sizeof(states));
get_remote_ref_states(remote, &states, GET_REF_STATES);
if (!states.stale.nr) {
diff --git a/builtin/repack.c b/builtin/repack.c
index 5f9bc74..9b74e0d 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -15,6 +15,8 @@
#include "promisor-remote.h"
#include "shallow.h"
#include "pack.h"
+#include "pack-bitmap.h"
+#include "refs.h"
static int delta_base_offset = 1;
static int pack_kept_objects = -1;
@@ -94,12 +96,14 @@ static void remove_pack_on_signal(int signo)
}
/*
- * Adds all packs hex strings to the fname list, which do not
- * have a corresponding .keep file. These packs are not to
- * be kept if we are going to pack everything into one file.
+ * Adds all packs hex strings to either fname_nonkept_list or
+ * fname_kept_list based on whether each pack has a corresponding
+ * .keep file or not. Packs without a .keep file are not to be kept
+ * if we are going to pack everything into one file.
*/
-static void get_non_kept_pack_filenames(struct string_list *fname_list,
- const struct string_list *extra_keep)
+static void collect_pack_filenames(struct string_list *fname_nonkept_list,
+ struct string_list *fname_kept_list,
+ const struct string_list *extra_keep)
{
DIR *dir;
struct dirent *e;
@@ -112,21 +116,20 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list,
size_t len;
int i;
+ if (!strip_suffix(e->d_name, ".pack", &len))
+ continue;
+
for (i = 0; i < extra_keep->nr; i++)
if (!fspathcmp(e->d_name, extra_keep->items[i].string))
break;
- if (extra_keep->nr > 0 && i < extra_keep->nr)
- continue;
-
- if (!strip_suffix(e->d_name, ".pack", &len))
- continue;
fname = xmemdupz(e->d_name, len);
- if (!file_exists(mkpath("%s/%s.keep", packdir, fname)))
- string_list_append_nodup(fname_list, fname);
+ if ((extra_keep->nr > 0 && i < extra_keep->nr) ||
+ (file_exists(mkpath("%s/%s.keep", packdir, fname))))
+ string_list_append_nodup(fname_kept_list, fname);
else
- free(fname);
+ string_list_append_nodup(fname_nonkept_list, fname);
}
closedir(dir);
}
@@ -208,10 +211,10 @@ static struct {
unsigned optional:1;
} exts[] = {
{".pack"},
- {".idx"},
{".rev", 1},
{".bitmap", 1},
{".promisor", 1},
+ {".idx"},
};
static unsigned populate_pack_exts(char *name)
@@ -255,9 +258,11 @@ static void repack_promisor_objects(const struct pack_objects_args *args,
for_each_packed_object(write_oid, &cmd,
FOR_EACH_OBJECT_PROMISOR_ONLY);
- if (cmd.in == -1)
+ if (cmd.in == -1) {
/* No packed objects; cmd was never started */
+ child_process_clear(&cmd);
return;
+ }
close(cmd.in);
@@ -422,6 +427,25 @@ static void split_pack_geometry(struct pack_geometry *geometry, int factor)
geometry->split = split;
}
+static struct packed_git *get_largest_active_pack(struct pack_geometry *geometry)
+{
+ if (!geometry) {
+ /*
+ * No geometry means either an all-into-one repack (in which
+ * case there is only one pack left and it is the largest) or an
+ * incremental one.
+ *
+ * If repacking incrementally, then we could check the size of
+ * all packs to determine which should be preferred, but leave
+ * this for later.
+ */
+ return NULL;
+ }
+ if (geometry->split == geometry->pack_nr)
+ return NULL;
+ return geometry->pack[geometry->pack_nr - 1];
+}
+
static void clear_pack_geometry(struct pack_geometry *geometry)
{
if (!geometry)
@@ -433,17 +457,162 @@ static void clear_pack_geometry(struct pack_geometry *geometry)
geometry->split = 0;
}
+struct midx_snapshot_ref_data {
+ struct tempfile *f;
+ struct oidset seen;
+ int preferred;
+};
+
+static int midx_snapshot_ref_one(const char *refname,
+ const struct object_id *oid,
+ int flag, void *_data)
+{
+ struct midx_snapshot_ref_data *data = _data;
+ struct object_id peeled;
+
+ if (!peel_iterated_oid(oid, &peeled))
+ oid = &peeled;
+
+ if (oidset_insert(&data->seen, oid))
+ return 0; /* already seen */
+
+ if (oid_object_info(the_repository, oid, NULL) != OBJ_COMMIT)
+ return 0;
+
+ fprintf(data->f->fp, "%s%s\n", data->preferred ? "+" : "",
+ oid_to_hex(oid));
+
+ return 0;
+}
+
+static void midx_snapshot_refs(struct tempfile *f)
+{
+ struct midx_snapshot_ref_data data;
+ const struct string_list *preferred = bitmap_preferred_tips(the_repository);
+
+ data.f = f;
+ data.preferred = 0;
+ oidset_init(&data.seen, 0);
+
+ if (!fdopen_tempfile(f, "w"))
+ die(_("could not open tempfile %s for writing"),
+ get_tempfile_path(f));
+
+ if (preferred) {
+ struct string_list_item *item;
+
+ data.preferred = 1;
+ for_each_string_list_item(item, preferred)
+ for_each_ref_in(item->string, midx_snapshot_ref_one, &data);
+ data.preferred = 0;
+ }
+
+ for_each_ref(midx_snapshot_ref_one, &data);
+
+ if (close_tempfile_gently(f)) {
+ int save_errno = errno;
+ delete_tempfile(&f);
+ errno = save_errno;
+ die_errno(_("could not close refs snapshot tempfile"));
+ }
+
+ oidset_clear(&data.seen);
+}
+
+static void midx_included_packs(struct string_list *include,
+ struct string_list *existing_nonkept_packs,
+ struct string_list *existing_kept_packs,
+ struct string_list *names,
+ struct pack_geometry *geometry)
+{
+ struct string_list_item *item;
+
+ for_each_string_list_item(item, existing_kept_packs)
+ string_list_insert(include, xstrfmt("%s.idx", item->string));
+ for_each_string_list_item(item, names)
+ string_list_insert(include, xstrfmt("pack-%s.idx", item->string));
+ if (geometry) {
+ struct strbuf buf = STRBUF_INIT;
+ uint32_t i;
+ for (i = geometry->split; i < geometry->pack_nr; i++) {
+ struct packed_git *p = geometry->pack[i];
+
+ strbuf_addstr(&buf, pack_basename(p));
+ strbuf_strip_suffix(&buf, ".pack");
+ strbuf_addstr(&buf, ".idx");
+
+ string_list_insert(include, strbuf_detach(&buf, NULL));
+ }
+ } else {
+ for_each_string_list_item(item, existing_nonkept_packs) {
+ if (item->util)
+ continue;
+ string_list_insert(include, xstrfmt("%s.idx", item->string));
+ }
+ }
+}
+
+static int write_midx_included_packs(struct string_list *include,
+ struct pack_geometry *geometry,
+ const char *refs_snapshot,
+ int show_progress, int write_bitmaps)
+{
+ struct child_process cmd = CHILD_PROCESS_INIT;
+ struct string_list_item *item;
+ struct packed_git *largest = get_largest_active_pack(geometry);
+ FILE *in;
+ int ret;
+
+ if (!include->nr)
+ return 0;
+
+ cmd.in = -1;
+ cmd.git_cmd = 1;
+
+ strvec_push(&cmd.args, "multi-pack-index");
+ strvec_pushl(&cmd.args, "write", "--stdin-packs", NULL);
+
+ if (show_progress)
+ strvec_push(&cmd.args, "--progress");
+ else
+ strvec_push(&cmd.args, "--no-progress");
+
+ if (write_bitmaps)
+ strvec_push(&cmd.args, "--bitmap");
+
+ if (largest)
+ strvec_pushf(&cmd.args, "--preferred-pack=%s",
+ pack_basename(largest));
+
+ if (refs_snapshot)
+ strvec_pushf(&cmd.args, "--refs-snapshot=%s", refs_snapshot);
+
+ ret = start_command(&cmd);
+ if (ret)
+ return ret;
+
+ in = xfdopen(cmd.in, "w");
+ for_each_string_list_item(item, include)
+ fprintf(in, "%s\n", item->string);
+ fclose(in);
+
+ return finish_command(&cmd);
+}
+
int cmd_repack(int argc, const char **argv, const char *prefix)
{
struct child_process cmd = CHILD_PROCESS_INIT;
struct string_list_item *item;
struct string_list names = STRING_LIST_INIT_DUP;
struct string_list rollback = STRING_LIST_INIT_NODUP;
- struct string_list existing_packs = STRING_LIST_INIT_DUP;
+ struct string_list existing_nonkept_packs = STRING_LIST_INIT_DUP;
+ struct string_list existing_kept_packs = STRING_LIST_INIT_DUP;
struct pack_geometry *geometry = NULL;
struct strbuf line = STRBUF_INIT;
+ struct tempfile *refs_snapshot = NULL;
int i, ext, ret;
FILE *out;
+ int show_progress = isatty(2);
/* variables to be filled by option parsing */
int pack_everything = 0;
@@ -454,6 +623,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
int no_update_server_info = 0;
struct pack_objects_args po_args = {NULL};
int geometric_factor = 0;
+ int write_midx = 0;
struct option builtin_repack_options[] = {
OPT_BIT('a', NULL, &pack_everything,
@@ -496,6 +666,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
N_("do not repack this pack")),
OPT_INTEGER('g', "geometric", &geometric_factor,
N_("find a geometric progression with factor <N>")),
+ OPT_BOOL('m', "write-midx", &write_midx,
+ N_("write a multi-pack index of the resulting packs")),
OPT_END()
};
@@ -512,16 +684,32 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
die(_("--keep-unreachable and -A are incompatible"));
if (write_bitmaps < 0) {
- if (!(pack_everything & ALL_INTO_ONE) ||
- !is_bare_repository())
+ if (!write_midx &&
+ (!(pack_everything & ALL_INTO_ONE) || !is_bare_repository()))
write_bitmaps = 0;
+ } else if (write_bitmaps &&
+ git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0) &&
+ git_env_bool(GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP, 0)) {
+ write_bitmaps = 0;
}
if (pack_kept_objects < 0)
pack_kept_objects = write_bitmaps > 0;
- if (write_bitmaps && !(pack_everything & ALL_INTO_ONE))
+ if (write_bitmaps && !(pack_everything & ALL_INTO_ONE) && !write_midx)
die(_(incremental_bitmap_conflict_error));
+ if (write_midx && write_bitmaps) {
+ struct strbuf path = STRBUF_INIT;
+
+ strbuf_addf(&path, "%s/%s_XXXXXX", get_object_directory(),
+ "bitmap-ref-tips");
+
+ refs_snapshot = xmks_tempfile(path.buf);
+ midx_snapshot_refs(refs_snapshot);
+
+ strbuf_release(&path);
+ }
+
if (geometric_factor) {
if (pack_everything)
die(_("--geometric is incompatible with -A, -a"));
@@ -561,19 +749,22 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
}
if (has_promisor_remote())
strvec_push(&cmd.args, "--exclude-promisor-objects");
- if (write_bitmaps > 0)
- strvec_push(&cmd.args, "--write-bitmap-index");
- else if (write_bitmaps < 0)
- strvec_push(&cmd.args, "--write-bitmap-index-quiet");
+ if (!write_midx) {
+ if (write_bitmaps > 0)
+ strvec_push(&cmd.args, "--write-bitmap-index");
+ else if (write_bitmaps < 0)
+ strvec_push(&cmd.args, "--write-bitmap-index-quiet");
+ }
if (use_delta_islands)
strvec_push(&cmd.args, "--delta-islands");
- if (pack_everything & ALL_INTO_ONE) {
- get_non_kept_pack_filenames(&existing_packs, &keep_pack_list);
+ collect_pack_filenames(&existing_nonkept_packs, &existing_kept_packs,
+ &keep_pack_list);
+ if (pack_everything & ALL_INTO_ONE) {
repack_promisor_objects(&po_args, &names);
- if (existing_packs.nr && delete_redundant) {
+ if (existing_nonkept_packs.nr && delete_redundant) {
for_each_string_list_item(item, &names) {
strvec_pushf(&cmd.args, "--keep-pack=%s-%s.pack",
packtmp_name, item->string);
@@ -582,15 +773,12 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
strvec_pushf(&cmd.args,
"--unpack-unreachable=%s",
unpack_unreachable);
- strvec_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
} else if (pack_everything & LOOSEN_UNREACHABLE) {
strvec_push(&cmd.args,
"--unpack-unreachable");
} else if (keep_unreachable) {
strvec_push(&cmd.args, "--keep-unreachable");
strvec_push(&cmd.args, "--pack-loose-unreachable");
- } else {
- strvec_push(&cmd.env_array, "GIT_REF_PARANOIA=1");
}
}
} else if (geometry) {
@@ -676,20 +864,48 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
}
/* End of pack replacement. */
- reprepare_packed_git(the_repository);
-
- if (delete_redundant) {
+ if (delete_redundant && pack_everything & ALL_INTO_ONE) {
const int hexsz = the_hash_algo->hexsz;
- int opts = 0;
string_list_sort(&names);
- for_each_string_list_item(item, &existing_packs) {
+ for_each_string_list_item(item, &existing_nonkept_packs) {
char *sha1;
size_t len = strlen(item->string);
if (len < hexsz)
continue;
sha1 = item->string + len - hexsz;
- if (!string_list_has_string(&names, sha1))
- remove_redundant_pack(packdir, item->string);
+ /*
+ * Mark this pack for deletion, which ensures that this
+ * pack won't be included in a MIDX (if `--write-midx`
+ * was given) and that we will actually delete this pack
+ * (if `-d` was given).
+ */
+ item->util = (void*)(intptr_t)!string_list_has_string(&names, sha1);
+ }
+ }
+
+ if (write_midx) {
+ struct string_list include = STRING_LIST_INIT_NODUP;
+ midx_included_packs(&include, &existing_nonkept_packs,
+ &existing_kept_packs, &names, geometry);
+
+ ret = write_midx_included_packs(&include, geometry,
+ refs_snapshot ? get_tempfile_path(refs_snapshot) : NULL,
+ show_progress, write_bitmaps > 0);
+
+ string_list_clear(&include, 0);
+
+ if (ret)
+ return ret;
+ }
+
+ reprepare_packed_git(the_repository);
+
+ if (delete_redundant) {
+ int opts = 0;
+ for_each_string_list_item(item, &existing_nonkept_packs) {
+ if (!item->util)
+ continue;
+ remove_redundant_pack(packdir, item->string);
}
if (geometry) {
@@ -710,7 +926,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
}
strbuf_release(&buf);
}
- if (!po_args.quiet && isatty(2))
+ if (!po_args.quiet && show_progress)
opts |= PRUNE_PACKED_VERBOSE;
prune_packed_objects(opts);
@@ -725,12 +941,17 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
update_server_info(0);
remove_temporary_files();
- if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0))
- write_midx_file(get_object_directory(), NULL, 0);
+ if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0)) {
+ unsigned flags = 0;
+ if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP, 0))
+ flags |= MIDX_WRITE_BITMAP | MIDX_WRITE_REV_INDEX;
+ write_midx_file(get_object_directory(), NULL, NULL, flags);
+ }
string_list_clear(&names, 0);
string_list_clear(&rollback, 0);
- string_list_clear(&existing_packs, 0);
+ string_list_clear(&existing_nonkept_packs, 0);
+ string_list_clear(&existing_kept_packs, 0);
clear_pack_geometry(geometry);
strbuf_release(&line);
diff --git a/builtin/reset.c b/builtin/reset.c
index 51c9e2f..7393595 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -67,12 +67,18 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t
case KEEP:
case MERGE:
opts.update = 1;
+ opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
break;
case HARD:
opts.update = 1;
- /* fallthrough */
+ opts.reset = UNPACK_RESET_OVERWRITE_UNTRACKED;
+ break;
+ case MIXED:
+ opts.reset = UNPACK_RESET_PROTECT_UNTRACKED;
+ /* but opts.update=0, so working tree not updated */
+ break;
default:
- opts.reset = 1;
+ BUG("invalid reset_type passed to reset_index");
}
read_cache_unmerged();
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 22c4e1a..8480a59 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -863,8 +863,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (!strcmp(arg, "--bisect")) {
- for_each_fullref_in("refs/bisect/bad", show_reference, NULL, 0);
- for_each_fullref_in("refs/bisect/good", anti_reference, NULL, 0);
+ for_each_fullref_in("refs/bisect/bad", show_reference, NULL);
+ for_each_fullref_in("refs/bisect/good", anti_reference, NULL);
continue;
}
if (opt_with_value(arg, "--branches", &arg)) {
diff --git a/builtin/revert.c b/builtin/revert.c
index 2e13660..51776ab 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -136,6 +136,9 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
PARSE_OPT_KEEP_ARGV0 |
PARSE_OPT_KEEP_UNKNOWN);
+ prepare_repo_settings(the_repository);
+ the_repository->settings.command_requires_full_index = 0;
+
/* implies allow_empty */
if (opts->keep_redundant_commits)
opts->allow_empty = 1;
diff --git a/builtin/rm.c b/builtin/rm.c
index 3b44b80..3d0967c 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -237,6 +237,7 @@ static int check_local_mod(struct object_id *head, int index_only)
static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
static int ignore_unmatch = 0, pathspec_file_nul;
+static int include_sparse;
static char *pathspec_from_file;
static struct option builtin_rm_options[] = {
@@ -247,6 +248,7 @@ static struct option builtin_rm_options[] = {
OPT_BOOL('r', NULL, &recursive, N_("allow recursive removal")),
OPT_BOOL( 0 , "ignore-unmatch", &ignore_unmatch,
N_("exit with a zero status even if nothing matched")),
+ OPT_BOOL(0, "sparse", &include_sparse, N_("allow updating entries outside of the sparse-checkout cone")),
OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
OPT_END(),
@@ -298,7 +300,10 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
ensure_full_index(&the_index);
for (i = 0; i < active_nr; i++) {
const struct cache_entry *ce = active_cache[i];
- if (ce_skip_worktree(ce))
+
+ if (!include_sparse &&
+ (ce_skip_worktree(ce) ||
+ !path_in_sparse_checkout(ce->name, &the_index)))
continue;
if (!ce_path_match(&the_index, ce, &pathspec, seen))
continue;
@@ -322,7 +327,8 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
seen_any = 1;
else if (ignore_unmatch)
continue;
- else if (matches_skip_worktree(&pathspec, i, &skip_worktree_seen))
+ else if (!include_sparse &&
+ matches_skip_worktree(&pathspec, i, &skip_worktree_seen))
string_list_append(&only_match_skip_worktree, original);
else
die(_("pathspec '%s' did not match any files"), original);
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index 729dea1..69c432e 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -17,10 +17,10 @@
#include "protocol.h"
static const char * const send_pack_usage[] = {
- N_("git send-pack [--all | --mirror] [--dry-run] [--force] "
- "[--receive-pack=<git-receive-pack>] [--verbose] [--thin] [--atomic] "
- "[<host>:]<directory> [<ref>...]\n"
- " --all and explicit <ref> specification are mutually exclusive."),
+ N_("git send-pack [--mirror] [--dry-run] [--force]\n"
+ " [--receive-pack=<git-receive-pack>]\n"
+ " [--verbose] [--thin] [--atomic]\n"
+ " [<host>:]<directory> (--all | <ref>...)"),
NULL,
};
@@ -87,6 +87,10 @@ static void print_helper_status(struct ref *ref)
break;
case REF_STATUS_EXPECTING_REPORT:
+ res = "error";
+ msg = "expecting report";
+ break;
+
default:
continue;
}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 3e7ab1c..e7f7af5 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -374,6 +374,9 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
for (;;) {
switch (parse_options_step(&ctx, options, shortlog_usage)) {
+ case PARSE_OPT_NON_OPTION:
+ case PARSE_OPT_UNKNOWN:
+ break;
case PARSE_OPT_HELP:
case PARSE_OPT_ERROR:
exit(129);
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index bea4bbf..0824492 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -11,9 +11,9 @@
static const char* show_branch_usage[] = {
N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n"
- " [--current] [--color[=<when>] | --no-color] [--sparse]\n"
- " [--more=<n> | --list | --independent | --merge-base]\n"
- " [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"),
+ " [--current] [--color[=<when>] | --no-color] [--sparse]\n"
+ " [--more=<n> | --list | --independent | --merge-base]\n"
+ " [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"),
N_("git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"),
NULL
};
diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c
index 8ba9f13..d0f5c47 100644
--- a/builtin/sparse-checkout.c
+++ b/builtin/sparse-checkout.c
@@ -100,6 +100,98 @@ static int sparse_checkout_list(int argc, const char **argv)
return 0;
}
+static void clean_tracked_sparse_directories(struct repository *r)
+{
+ int i, was_full = 0;
+ struct strbuf path = STRBUF_INIT;
+ size_t pathlen;
+ struct string_list_item *item;
+ struct string_list sparse_dirs = STRING_LIST_INIT_DUP;
+
+ /*
+ * If we are not using cone mode patterns, then we cannot
+ * delete directories outside of the sparse cone.
+ */
+ if (!r || !r->index || !r->worktree)
+ return;
+ if (init_sparse_checkout_patterns(r->index) ||
+ !r->index->sparse_checkout_patterns->use_cone_patterns)
+ return;
+
+ /*
+ * Use the sparse index as a data structure to assist finding
+ * directories that are safe to delete. This conversion to a
+ * sparse index will not delete directories that contain
+ * conflicted entries or submodules.
+ */
+ if (!r->index->sparse_index) {
+ /*
+ * If something, such as a merge conflict or other concern,
+ * prevents us from converting to a sparse index, then do
+ * not try deleting files.
+ */
+ if (convert_to_sparse(r->index, SPARSE_INDEX_MEMORY_ONLY))
+ return;
+ was_full = 1;
+ }
+
+ strbuf_addstr(&path, r->worktree);
+ strbuf_complete(&path, '/');
+ pathlen = path.len;
+
+ /*
+ * Collect directories that have gone out of scope but also
+ * exist on disk, so there is some work to be done. We need to
+ * store the entries in a list before exploring, since that might
+ * expand the sparse-index again.
+ */
+ for (i = 0; i < r->index->cache_nr; i++) {
+ struct cache_entry *ce = r->index->cache[i];
+
+ if (S_ISSPARSEDIR(ce->ce_mode) &&
+ repo_file_exists(r, ce->name))
+ string_list_append(&sparse_dirs, ce->name);
+ }
+
+ for_each_string_list_item(item, &sparse_dirs) {
+ struct dir_struct dir = DIR_INIT;
+ struct pathspec p = { 0 };
+ struct strvec s = STRVEC_INIT;
+
+ strbuf_setlen(&path, pathlen);
+ strbuf_addstr(&path, item->string);
+
+ dir.flags |= DIR_SHOW_IGNORED_TOO;
+
+ setup_standard_excludes(&dir);
+ strvec_push(&s, path.buf);
+
+ parse_pathspec(&p, PATHSPEC_GLOB, 0, NULL, s.v);
+ fill_directory(&dir, r->index, &p);
+
+ if (dir.nr) {
+ warning(_("directory '%s' contains untracked files,"
+ " but is not in the sparse-checkout cone"),
+ item->string);
+ } else if (remove_dir_recursively(&path, 0)) {
+ /*
+ * Removal is "best effort". If something blocks
+ * the deletion, then continue with a warning.
+ */
+ warning(_("failed to remove directory '%s'"),
+ item->string);
+ }
+
+ dir_clear(&dir);
+ }
+
+ string_list_clear(&sparse_dirs, 0);
+ strbuf_release(&path);
+
+ if (was_full)
+ ensure_full_index(r->index);
+}
+
static int update_working_directory(struct pattern_list *pl)
{
enum update_sparsity_result result;
@@ -141,6 +233,8 @@ static int update_working_directory(struct pattern_list *pl)
else
rollback_lock_file(&lock_file);
+ clean_tracked_sparse_directories(r);
+
r->index->sparse_checkout_patterns = NULL;
return result;
}
diff --git a/builtin/stash.c b/builtin/stash.c
index 8f42360..18c812b 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -27,11 +27,11 @@ static const char * const git_stash_usage[] = {
N_("git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"),
N_("git stash branch <branchname> [<stash>]"),
"git stash clear",
- N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+ N_("git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]\n"
" [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
" [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
" [--] [<pathspec>...]]"),
- N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+ N_("git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]\n"
" [-u|--include-untracked] [-a|--all] [<message>]"),
NULL
};
@@ -85,7 +85,7 @@ static const char * const git_stash_push_usage[] = {
static const char * const git_stash_save_usage[] = {
N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
- " [-u|--include-untracked] [-a|--all] [<message>]"),
+ " [-u|--include-untracked] [-a|--all] [<message>]"),
NULL
};
@@ -256,8 +256,10 @@ static int reset_tree(struct object_id *i_tree, int update, int reset)
opts.src_index = &the_index;
opts.dst_index = &the_index;
opts.merge = 1;
- opts.reset = reset;
+ opts.reset = reset ? UNPACK_RESET_PROTECT_UNTRACKED : 0;
opts.update = update;
+ if (update)
+ opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
opts.fn = oneway_merge;
if (unpack_trees(nr_trees, t, &opts))
@@ -313,6 +315,17 @@ static int reset_head(void)
return run_command(&cp);
}
+static int is_path_a_directory(const char *path)
+{
+ /*
+ * This function differs from abspath.c:is_directory() in that
+ * here we use lstat() instead of stat(); we do not want to
+ * follow symbolic links here.
+ */
+ struct stat st;
+ return (!lstat(path, &st) && S_ISDIR(st.st_mode));
+}
+
static void add_diff_to_buf(struct diff_queue_struct *q,
struct diff_options *options,
void *data)
@@ -320,6 +333,9 @@ static void add_diff_to_buf(struct diff_queue_struct *q,
int i;
for (i = 0; i < q->nr; i++) {
+ if (is_path_a_directory(q->queue[i]->one->path))
+ continue;
+
strbuf_addstr(data, q->queue[i]->one->path);
/* NUL-terminate: will be fed to update-index -z */
@@ -521,9 +537,6 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
}
}
- if (info->has_u && restore_untracked(&info->u_tree))
- return error(_("could not restore untracked files from stash"));
-
init_merge_options(&o, the_repository);
o.branch1 = "Updated upstream";
@@ -558,6 +571,9 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
unstage_changes_unless_new(&c_tree);
}
+ if (info->has_u && restore_untracked(&info->u_tree))
+ return error(_("could not restore untracked files from stash"));
+
if (!quiet) {
struct child_process cp = CHILD_PROCESS_INIT;
@@ -1116,6 +1132,38 @@ done:
return ret;
}
+static int stash_staged(struct stash_info *info, struct strbuf *out_patch,
+ int quiet)
+{
+ int ret = 0;
+ struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
+ struct index_state istate = { NULL };
+
+ if (write_index_as_tree(&info->w_tree, &istate, the_repository->index_file,
+ 0, NULL)) {
+ ret = -1;
+ goto done;
+ }
+
+ cp_diff_tree.git_cmd = 1;
+ strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "-U1", "HEAD",
+ oid_to_hex(&info->w_tree), "--", NULL);
+ if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) {
+ ret = -1;
+ goto done;
+ }
+
+ if (!out_patch->len) {
+ if (!quiet)
+ fprintf_ln(stderr, _("No staged changes"));
+ ret = 1;
+ }
+
+done:
+ discard_index(&istate);
+ return ret;
+}
+
static int stash_patch(struct stash_info *info, const struct pathspec *ps,
struct strbuf *out_patch, int quiet)
{
@@ -1242,7 +1290,7 @@ done:
}
static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf,
- int include_untracked, int patch_mode,
+ int include_untracked, int patch_mode, int only_staged,
struct stash_info *info, struct strbuf *patch,
int quiet)
{
@@ -1321,6 +1369,16 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
} else if (ret > 0) {
goto done;
}
+ } else if (only_staged) {
+ ret = stash_staged(info, patch, quiet);
+ if (ret < 0) {
+ if (!quiet)
+ fprintf_ln(stderr, _("Cannot save the current "
+ "staged state"));
+ goto done;
+ } else if (ret > 0) {
+ goto done;
+ }
} else {
if (stash_working_tree(info, ps)) {
if (!quiet)
@@ -1379,7 +1437,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)
if (!check_changes_tracked_files(&ps))
return 0;
- ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, &info,
+ ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, 0, &info,
NULL, 0);
if (!ret)
printf_ln("%s", oid_to_hex(&info.w_commit));
@@ -1389,7 +1447,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)
}
static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet,
- int keep_index, int patch_mode, int include_untracked)
+ int keep_index, int patch_mode, int include_untracked, int only_staged)
{
int ret = 0;
struct stash_info info;
@@ -1407,6 +1465,17 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
goto done;
}
+ /* --patch overrides --staged */
+ if (patch_mode)
+ only_staged = 0;
+
+ if (only_staged && include_untracked) {
+ fprintf_ln(stderr, _("Can't use --staged and --include-untracked"
+ " or --all at the same time"));
+ ret = -1;
+ goto done;
+ }
+
read_cache_preload(NULL);
if (!include_untracked && ps->nr) {
int i;
@@ -1447,7 +1516,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
if (stash_msg)
strbuf_addstr(&stash_msg_buf, stash_msg);
- if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode,
+ if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, only_staged,
&info, &patch, quiet)) {
ret = -1;
goto done;
@@ -1464,7 +1533,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
printf_ln(_("Saved working directory and index state %s"),
stash_msg_buf.buf);
- if (!patch_mode) {
+ if (!(patch_mode || only_staged)) {
if (include_untracked && !ps->nr) {
struct child_process cp = CHILD_PROCESS_INIT;
@@ -1519,6 +1588,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
} else {
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
+ /* BUG: this nukes untracked files in the way */
strvec_pushl(&cp.args, "reset", "--hard", "-q",
"--no-recurse-submodules", NULL);
if (run_command(&cp)) {
@@ -1581,6 +1651,7 @@ static int push_stash(int argc, const char **argv, const char *prefix,
{
int force_assume = 0;
int keep_index = -1;
+ int only_staged = 0;
int patch_mode = 0;
int include_untracked = 0;
int quiet = 0;
@@ -1591,6 +1662,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,
struct option options[] = {
OPT_BOOL('k', "keep-index", &keep_index,
N_("keep index")),
+ OPT_BOOL('S', "staged", &only_staged,
+ N_("stash staged changes only")),
OPT_BOOL('p', "patch", &patch_mode,
N_("stash in patch mode")),
OPT__QUIET(&quiet, N_("quiet mode")),
@@ -1629,6 +1702,9 @@ static int push_stash(int argc, const char **argv, const char *prefix,
if (patch_mode)
die(_("--pathspec-from-file is incompatible with --patch"));
+ if (only_staged)
+ die(_("--pathspec-from-file is incompatible with --staged"));
+
if (ps.nr)
die(_("--pathspec-from-file is incompatible with pathspec arguments"));
@@ -1640,12 +1716,13 @@ static int push_stash(int argc, const char **argv, const char *prefix,
}
return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
- include_untracked);
+ include_untracked, only_staged);
}
static int save_stash(int argc, const char **argv, const char *prefix)
{
int keep_index = -1;
+ int only_staged = 0;
int patch_mode = 0;
int include_untracked = 0;
int quiet = 0;
@@ -1656,6 +1733,8 @@ static int save_stash(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT_BOOL('k', "keep-index", &keep_index,
N_("keep index")),
+ OPT_BOOL('S', "staged", &only_staged,
+ N_("stash staged changes only")),
OPT_BOOL('p', "patch", &patch_mode,
N_("stash in patch mode")),
OPT__QUIET(&quiet, N_("quiet mode")),
@@ -1677,7 +1756,7 @@ static int save_stash(int argc, const char **argv, const char *prefix)
memset(&ps, 0, sizeof(ps));
ret = do_push_stash(&ps, stash_msg, quiet, keep_index,
- patch_mode, include_untracked);
+ patch_mode, include_untracked, only_staged);
strbuf_release(&stash_msg_buf);
return ret;
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 4da9781..e630f0c 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -199,34 +199,28 @@ static char *relative_url(const char *remote_url,
return strbuf_detach(&sb, NULL);
}
-static int resolve_relative_url(int argc, const char **argv, const char *prefix)
+static char *resolve_relative_url(const char *rel_url, const char *up_path, int quiet)
{
- char *remoteurl = NULL;
+ char *remoteurl, *resolved_url;
char *remote = get_default_remote();
- const char *up_path = NULL;
- char *res;
- const char *url;
- struct strbuf sb = STRBUF_INIT;
-
- if (argc != 2 && argc != 3)
- die("resolve-relative-url only accepts one or two arguments");
-
- url = argv[1];
- strbuf_addf(&sb, "remote.%s.url", remote);
- free(remote);
+ struct strbuf remotesb = STRBUF_INIT;
- if (git_config_get_string(sb.buf, &remoteurl))
- /* the repository is its own authoritative upstream */
+ strbuf_addf(&remotesb, "remote.%s.url", remote);
+ if (git_config_get_string(remotesb.buf, &remoteurl)) {
+ if (!quiet)
+ warning(_("could not look up configuration '%s'. "
+ "Assuming this repository is its own "
+ "authoritative upstream."),
+ remotesb.buf);
remoteurl = xgetcwd();
+ }
+ resolved_url = relative_url(remoteurl, rel_url, up_path);
- if (argc == 3)
- up_path = argv[2];
-
- res = relative_url(remoteurl, url, up_path);
- puts(res);
- free(res);
+ free(remote);
free(remoteurl);
- return 0;
+ strbuf_release(&remotesb);
+
+ return resolved_url;
}
static int resolve_relative_url_test(int argc, const char **argv, const char *prefix)
@@ -313,7 +307,7 @@ struct module_list {
const struct cache_entry **entries;
int alloc, nr;
};
-#define MODULE_LIST_INIT { NULL, 0, 0 }
+#define MODULE_LIST_INIT { 0 }
static int module_list_compute(int argc, const char **argv,
const char *prefix,
@@ -590,31 +584,11 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
return 0;
}
-static char *compute_submodule_clone_url(const char *rel_url)
-{
- char *remoteurl, *relurl;
- char *remote = get_default_remote();
- struct strbuf remotesb = STRBUF_INIT;
-
- strbuf_addf(&remotesb, "remote.%s.url", remote);
- if (git_config_get_string(remotesb.buf, &remoteurl)) {
- warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
- remoteurl = xgetcwd();
- }
- relurl = relative_url(remoteurl, rel_url, NULL);
-
- free(remote);
- free(remoteurl);
- strbuf_release(&remotesb);
-
- return relurl;
-}
-
struct init_cb {
const char *prefix;
unsigned int flags;
};
-#define INIT_CB_INIT { NULL, 0 }
+#define INIT_CB_INIT { 0 }
static void init_submodule(const char *path, const char *prefix,
unsigned int flags)
@@ -660,7 +634,7 @@ static void init_submodule(const char *path, const char *prefix,
if (starts_with_dot_dot_slash(url) ||
starts_with_dot_slash(url)) {
char *oldurl = url;
- url = compute_submodule_clone_url(oldurl);
+ url = resolve_relative_url(oldurl, NULL, 0);
free(oldurl);
}
@@ -743,7 +717,7 @@ struct status_cb {
const char *prefix;
unsigned int flags;
};
-#define STATUS_CB_INIT { NULL, 0 }
+#define STATUS_CB_INIT { 0 }
static void print_status(unsigned int flags, char state, const char *path,
const struct object_id *oid, const char *displaypath)
@@ -937,13 +911,13 @@ struct module_cb {
char status;
const char *sm_path;
};
-#define MODULE_CB_INIT { 0, 0, NULL, NULL, '\0', NULL }
+#define MODULE_CB_INIT { 0 }
struct module_cb_list {
struct module_cb **entries;
int alloc, nr;
};
-#define MODULE_CB_LIST_INIT { NULL, 0, 0 }
+#define MODULE_CB_LIST_INIT { 0 }
struct summary_cb {
int argc;
@@ -954,7 +928,7 @@ struct summary_cb {
unsigned int files: 1;
int summary_limit;
};
-#define SUMMARY_CB_INIT { 0, NULL, NULL, 0, 0, 0, 0 }
+#define SUMMARY_CB_INIT { 0 }
enum diff_cmd {
DIFF_INDEX,
@@ -1360,7 +1334,7 @@ struct sync_cb {
const char *prefix;
unsigned int flags;
};
-#define SYNC_CB_INIT { NULL, 0 }
+#define SYNC_CB_INIT { 0 }
static void sync_submodule(const char *path, const char *prefix,
unsigned int flags)
@@ -1380,20 +1354,10 @@ static void sync_submodule(const char *path, const char *prefix,
if (sub && sub->url) {
if (starts_with_dot_dot_slash(sub->url) ||
starts_with_dot_slash(sub->url)) {
- char *remote_url, *up_path;
- char *remote = get_default_remote();
- strbuf_addf(&sb, "remote.%s.url", remote);
-
- if (git_config_get_string(sb.buf, &remote_url))
- remote_url = xgetcwd();
-
- up_path = get_up_path(path);
- sub_origin_url = relative_url(remote_url, sub->url, up_path);
- super_config_url = relative_url(remote_url, sub->url, NULL);
-
- free(remote);
+ char *up_path = get_up_path(path);
+ sub_origin_url = resolve_relative_url(sub->url, up_path, 1);
+ super_config_url = resolve_relative_url(sub->url, NULL, 1);
free(up_path);
- free(remote_url);
} else {
sub_origin_url = xstrdup(sub->url);
super_config_url = xstrdup(sub->url);
@@ -1516,7 +1480,7 @@ struct deinit_cb {
const char *prefix;
unsigned int flags;
};
-#define DEINIT_CB_INIT { NULL, 0 }
+#define DEINIT_CB_INIT { 0 }
static void deinit_submodule(const char *path, const char *prefix,
unsigned int flags)
@@ -1683,8 +1647,9 @@ struct submodule_alternate_setup {
} error_mode;
struct string_list *reference;
};
-#define SUBMODULE_ALTERNATE_SETUP_INIT { NULL, \
- SUBMODULE_ALTERNATE_ERROR_IGNORE, NULL }
+#define SUBMODULE_ALTERNATE_SETUP_INIT { \
+ .error_mode = SUBMODULE_ALTERNATE_ERROR_IGNORE, \
+}
static const char alternate_error_advice[] = N_(
"An alternate computed from a superproject's alternate is invalid.\n"
@@ -1704,18 +1669,24 @@ static int add_possible_reference_from_superproject(
* standard layout with .git/(modules/<name>)+/objects
*/
if (strip_suffix(odb->path, "/objects", &len)) {
+ struct repository alternate;
char *sm_alternate;
struct strbuf sb = STRBUF_INIT;
struct strbuf err = STRBUF_INIT;
strbuf_add(&sb, odb->path, len);
+ repo_init(&alternate, sb.buf, NULL);
+
/*
* We need to end the new path with '/' to mark it as a dir,
* otherwise a submodule name containing '/' will be broken
* as the last part of a missing submodule reference would
* be taken as a file name.
*/
- strbuf_addf(&sb, "/modules/%s/", sas->submodule_name);
+ strbuf_reset(&sb);
+ submodule_name_to_gitdir(&sb, &alternate, sas->submodule_name);
+ strbuf_addch(&sb, '/');
+ repo_clear(&alternate);
sm_alternate = compute_alternate_path(sb.buf, &err);
if (sm_alternate) {
@@ -1785,7 +1756,7 @@ static int clone_submodule(struct module_clone_data *clone_data)
struct strbuf sb = STRBUF_INIT;
struct child_process cp = CHILD_PROCESS_INIT;
- strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), clone_data->name);
+ submodule_name_to_gitdir(&sb, the_repository, clone_data->name);
sm_gitdir = absolute_pathdup(sb.buf);
strbuf_reset(&sb);
@@ -2045,6 +2016,20 @@ struct submodule_update_clone {
.max_jobs = 1, \
}
+struct update_data {
+ const char *recursive_prefix;
+ const char *sm_path;
+ const char *displaypath;
+ struct object_id oid;
+ struct object_id suboid;
+ struct submodule_update_strategy update_strategy;
+ int depth;
+ unsigned int force: 1;
+ unsigned int quiet: 1;
+ unsigned int nofetch: 1;
+ unsigned int just_cloned: 1;
+};
+#define UPDATE_DATA_INIT { .update_strategy = SUBMODULE_UPDATE_STRATEGY_INIT }
static void next_submodule_warn_missing(struct submodule_update_clone *suc,
struct strbuf *out, const char *displaypath)
@@ -2134,7 +2119,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
if (repo_config_get_string_tmp(the_repository, sb.buf, &url)) {
if (starts_with_dot_slash(sub->url) ||
starts_with_dot_dot_slash(sub->url)) {
- url = compute_submodule_clone_url(sub->url);
+ url = resolve_relative_url(sub->url, NULL, 0);
need_free_url = 1;
} else
url = sub->url;
@@ -2298,6 +2283,181 @@ static int git_update_clone_config(const char *var, const char *value,
return 0;
}
+static int is_tip_reachable(const char *path, struct object_id *oid)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+ struct strbuf rev = STRBUF_INIT;
+ char *hex = oid_to_hex(oid);
+
+ cp.git_cmd = 1;
+ cp.dir = xstrdup(path);
+ cp.no_stderr = 1;
+ strvec_pushl(&cp.args, "rev-list", "-n", "1", hex, "--not", "--all", NULL);
+
+ prepare_submodule_repo_env(&cp.env_array);
+
+ if (capture_command(&cp, &rev, GIT_MAX_HEXSZ + 1) || rev.len)
+ return 0;
+
+ return 1;
+}
+
+static int fetch_in_submodule(const char *module_path, int depth, int quiet, struct object_id *oid)
+{
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ prepare_submodule_repo_env(&cp.env_array);
+ cp.git_cmd = 1;
+ cp.dir = xstrdup(module_path);
+
+ strvec_push(&cp.args, "fetch");
+ if (quiet)
+ strvec_push(&cp.args, "--quiet");
+ if (depth)
+ strvec_pushf(&cp.args, "--depth=%d", depth);
+ if (oid) {
+ char *hex = oid_to_hex(oid);
+ char *remote = get_default_remote();
+ strvec_pushl(&cp.args, remote, hex, NULL);
+ }
+
+ return run_command(&cp);
+}
+
+static int run_update_command(struct update_data *ud, int subforce)
+{
+ struct strvec args = STRVEC_INIT;
+ struct strvec child_env = STRVEC_INIT;
+ char *oid = oid_to_hex(&ud->oid);
+ int must_die_on_failure = 0;
+ int git_cmd;
+
+ switch (ud->update_strategy.type) {
+ case SM_UPDATE_CHECKOUT:
+ git_cmd = 1;
+ strvec_pushl(&args, "checkout", "-q", NULL);
+ if (subforce)
+ strvec_push(&args, "-f");
+ break;
+ case SM_UPDATE_REBASE:
+ git_cmd = 1;
+ strvec_push(&args, "rebase");
+ if (ud->quiet)
+ strvec_push(&args, "--quiet");
+ must_die_on_failure = 1;
+ break;
+ case SM_UPDATE_MERGE:
+ git_cmd = 1;
+ strvec_push(&args, "merge");
+ if (ud->quiet)
+ strvec_push(&args, "--quiet");
+ must_die_on_failure = 1;
+ break;
+ case SM_UPDATE_COMMAND:
+ git_cmd = 0;
+ strvec_push(&args, ud->update_strategy.command);
+ must_die_on_failure = 1;
+ break;
+ default:
+ BUG("unexpected update strategy type: %s",
+ submodule_strategy_to_string(&ud->update_strategy));
+ }
+ strvec_push(&args, oid);
+
+ prepare_submodule_repo_env(&child_env);
+ if (run_command_v_opt_cd_env(args.v, git_cmd ? RUN_GIT_CMD : RUN_USING_SHELL,
+ ud->sm_path, child_env.v)) {
+ switch (ud->update_strategy.type) {
+ case SM_UPDATE_CHECKOUT:
+ printf(_("Unable to checkout '%s' in submodule path '%s'"),
+ oid, ud->displaypath);
+ break;
+ case SM_UPDATE_REBASE:
+ printf(_("Unable to rebase '%s' in submodule path '%s'"),
+ oid, ud->displaypath);
+ break;
+ case SM_UPDATE_MERGE:
+ printf(_("Unable to merge '%s' in submodule path '%s'"),
+ oid, ud->displaypath);
+ break;
+ case SM_UPDATE_COMMAND:
+ printf(_("Execution of '%s %s' failed in submodule path '%s'"),
+ ud->update_strategy.command, oid, ud->displaypath);
+ break;
+ default:
+ BUG("unexpected update strategy type: %s",
+ submodule_strategy_to_string(&ud->update_strategy));
+ }
+ /*
+ * NEEDSWORK: We are currently printing to stdout with error
+ * return so that the shell caller handles the error output
+ * properly. Once we start handling the error messages within
+ * C, we should use die() instead.
+ */
+ if (must_die_on_failure)
+ return 2;
+ /*
+ * This signifies to the caller in shell that the command
+ * failed without dying
+ */
+ return 1;
+ }
+
+ switch (ud->update_strategy.type) {
+ case SM_UPDATE_CHECKOUT:
+ printf(_("Submodule path '%s': checked out '%s'\n"),
+ ud->displaypath, oid);
+ break;
+ case SM_UPDATE_REBASE:
+ printf(_("Submodule path '%s': rebased into '%s'\n"),
+ ud->displaypath, oid);
+ break;
+ case SM_UPDATE_MERGE:
+ printf(_("Submodule path '%s': merged in '%s'\n"),
+ ud->displaypath, oid);
+ break;
+ case SM_UPDATE_COMMAND:
+ printf(_("Submodule path '%s': '%s %s'\n"),
+ ud->displaypath, ud->update_strategy.command, oid);
+ break;
+ default:
+ BUG("unexpected update strategy type: %s",
+ submodule_strategy_to_string(&ud->update_strategy));
+ }
+
+ return 0;
+}
+
+static int do_run_update_procedure(struct update_data *ud)
+{
+ int subforce = is_null_oid(&ud->suboid) || ud->force;
+
+ if (!ud->nofetch) {
+ /*
+ * Run fetch only if `oid` isn't present or it
+ * is not reachable from a ref.
+ */
+ if (!is_tip_reachable(ud->sm_path, &ud->oid) &&
+ fetch_in_submodule(ud->sm_path, ud->depth, ud->quiet, NULL) &&
+ !ud->quiet)
+ fprintf_ln(stderr,
+ _("Unable to fetch in submodule path '%s'; "
+ "trying to directly fetch %s:"),
+ ud->displaypath, oid_to_hex(&ud->oid));
+ /*
+ * Now we tried the usual fetch, but `oid` may
+ * not be reachable from any of the refs.
+ */
+ if (!is_tip_reachable(ud->sm_path, &ud->oid) &&
+ fetch_in_submodule(ud->sm_path, ud->depth, ud->quiet, &ud->oid))
+ die(_("Fetched in submodule path '%s', but it did not "
+ "contain %s. Direct fetching of that commit failed."),
+ ud->displaypath, oid_to_hex(&ud->oid));
+ }
+
+ return run_update_command(ud, subforce);
+}
+
static void update_submodule(struct update_clone_data *ucd)
{
fprintf(stdout, "dummy %s %d\t%s\n",
@@ -2395,6 +2555,73 @@ static int update_clone(int argc, const char **argv, const char *prefix)
return update_submodules(&suc);
}
+static int run_update_procedure(int argc, const char **argv, const char *prefix)
+{
+ int force = 0, quiet = 0, nofetch = 0, just_cloned = 0;
+ char *prefixed_path, *update = NULL;
+ struct update_data update_data = UPDATE_DATA_INIT;
+
+ struct option options[] = {
+ OPT__QUIET(&quiet, N_("suppress output for update by rebase or merge")),
+ OPT__FORCE(&force, N_("force checkout updates"), 0),
+ OPT_BOOL('N', "no-fetch", &nofetch,
+ N_("don't fetch new objects from the remote site")),
+ OPT_BOOL(0, "just-cloned", &just_cloned,
+ N_("overrides update mode in case the repository is a fresh clone")),
+ OPT_INTEGER(0, "depth", &update_data.depth, N_("depth for shallow fetch")),
+ OPT_STRING(0, "prefix", &prefix,
+ N_("path"),
+ N_("path into the working tree")),
+ OPT_STRING(0, "update", &update,
+ N_("string"),
+ N_("rebase, merge, checkout or none")),
+ OPT_STRING(0, "recursive-prefix", &update_data.recursive_prefix, N_("path"),
+ N_("path into the working tree, across nested "
+ "submodule boundaries")),
+ OPT_CALLBACK_F(0, "oid", &update_data.oid, N_("sha1"),
+ N_("SHA1 expected by superproject"), PARSE_OPT_NONEG,
+ parse_opt_object_id),
+ OPT_CALLBACK_F(0, "suboid", &update_data.suboid, N_("subsha1"),
+ N_("SHA1 of submodule's HEAD"), PARSE_OPT_NONEG,
+ parse_opt_object_id),
+ OPT_END()
+ };
+
+ const char *const usage[] = {
+ N_("git submodule--helper run-update-procedure [<options>] <path>"),
+ NULL
+ };
+
+ argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+ if (argc != 1)
+ usage_with_options(usage, options);
+
+ update_data.force = !!force;
+ update_data.quiet = !!quiet;
+ update_data.nofetch = !!nofetch;
+ update_data.just_cloned = !!just_cloned;
+ update_data.sm_path = argv[0];
+
+ if (update_data.recursive_prefix)
+ prefixed_path = xstrfmt("%s%s", update_data.recursive_prefix, update_data.sm_path);
+ else
+ prefixed_path = xstrdup(update_data.sm_path);
+
+ update_data.displaypath = get_submodule_displaypath(prefixed_path, prefix);
+
+ determine_submodule_update_strategy(the_repository, update_data.just_cloned,
+ update_data.sm_path, update,
+ &update_data.update_strategy);
+
+ free(prefixed_path);
+
+ if (!oideq(&update_data.oid, &update_data.suboid) || update_data.force)
+ return do_run_update_procedure(&update_data);
+
+ return 3;
+}
+
static int resolve_relative_path(int argc, const char **argv, const char *prefix)
{
struct strbuf sb = STRBUF_INIT;
@@ -2540,7 +2767,6 @@ static int push_check(int argc, const char **argv, const char *prefix)
static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
{
- const struct submodule *sub;
const char *path;
const char *cw;
struct repository subrepo;
@@ -2550,11 +2776,7 @@ static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
path = argv[1];
- sub = submodule_from_path(the_repository, null_oid(), path);
- if (!sub)
- BUG("We could get the submodule handle before?");
-
- if (repo_submodule_init(&subrepo, the_repository, sub))
+ if (repo_submodule_init(&subrepo, the_repository, path, null_oid()))
die(_("could not get a repository handle for submodule '%s'"), path);
if (!repo_config_get_string_tmp(&subrepo, "core.worktree", &cw)) {
@@ -2765,7 +2987,7 @@ struct add_data {
const char *prefix;
const char *branch;
const char *reference_path;
- const char *sm_path;
+ char *sm_path;
const char *sm_name;
const char *repo;
const char *realrepo;
@@ -2777,7 +2999,7 @@ struct add_data {
};
#define ADD_DATA_INIT { .depth = -1 }
-static void show_fetch_remotes(FILE *output, const char *git_dir_path)
+static void append_fetch_remotes(struct strbuf *msg, const char *git_dir_path)
{
struct child_process cp_remote = CHILD_PROCESS_INIT;
struct strbuf sb_remote_out = STRBUF_INIT;
@@ -2793,7 +3015,7 @@ static void show_fetch_remotes(FILE *output, const char *git_dir_path)
while ((next_line = strchr(line, '\n')) != NULL) {
size_t len = next_line - line;
if (strip_suffix_mem(line, &len, " (fetch)"))
- fprintf(output, " %.*s\n", (int)len, line);
+ strbuf_addf(msg, " %.*s\n", (int)len, line);
line = next_line + 1;
}
}
@@ -2825,19 +3047,27 @@ static int add_submodule(const struct add_data *add_data)
if (is_directory(submod_gitdir_path)) {
if (!add_data->force) {
- fprintf(stderr, _("A git directory for '%s' is found "
- "locally with remote(s):"),
- add_data->sm_name);
- show_fetch_remotes(stderr, submod_gitdir_path);
+ struct strbuf msg = STRBUF_INIT;
+ char *die_msg;
+
+ strbuf_addf(&msg, _("A git directory for '%s' is found "
+ "locally with remote(s):\n"),
+ add_data->sm_name);
+
+ append_fetch_remotes(&msg, submod_gitdir_path);
free(submod_gitdir_path);
- die(_("If you want to reuse this local git "
- "directory instead of cloning again from\n"
- " %s\n"
- "use the '--force' option. If the local git "
- "directory is not the correct repo\n"
- "or if you are unsure what this means, choose "
- "another name with the '--name' option.\n"),
- add_data->realrepo);
+
+ strbuf_addf(&msg, _("If you want to reuse this local git "
+ "directory instead of cloning again from\n"
+ " %s\n"
+ "use the '--force' option. If the local git "
+ "directory is not the correct repo\n"
+ "or you are unsure what this means choose "
+ "another name with the '--name' option."),
+ add_data->realrepo);
+
+ die_msg = strbuf_detach(&msg, NULL);
+ die("%s", die_msg);
} else {
printf(_("Reactivating local git directory for "
"submodule '%s'\n"), add_data->sm_name);
@@ -2864,6 +3094,10 @@ static int add_submodule(const struct add_data *add_data)
prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
cp.dir = add_data->sm_path;
+ /*
+ * NOTE: we only get here if add_data->force is true, so
+ * passing --force to checkout is reasonable.
+ */
strvec_pushl(&cp.args, "checkout", "-f", "-q", NULL);
if (add_data->branch) {
@@ -2877,61 +3111,246 @@ static int add_submodule(const struct add_data *add_data)
return 0;
}
-static int add_clone(int argc, const char **argv, const char *prefix)
+static int config_submodule_in_gitmodules(const char *name, const char *var, const char *value)
+{
+ char *key;
+ int ret;
+
+ if (!is_writing_gitmodules_ok())
+ die(_("please make sure that the .gitmodules file is in the working tree"));
+
+ key = xstrfmt("submodule.%s.%s", name, var);
+ ret = config_set_in_gitmodules_file_gently(key, value);
+ free(key);
+
+ return ret;
+}
+
+static void configure_added_submodule(struct add_data *add_data)
+{
+ char *key;
+ char *val = NULL;
+ struct child_process add_submod = CHILD_PROCESS_INIT;
+ struct child_process add_gitmodules = CHILD_PROCESS_INIT;
+
+ key = xstrfmt("submodule.%s.url", add_data->sm_name);
+ git_config_set_gently(key, add_data->realrepo);
+ free(key);
+
+ add_submod.git_cmd = 1;
+ strvec_pushl(&add_submod.args, "add",
+ "--no-warn-embedded-repo", NULL);
+ if (add_data->force)
+ strvec_push(&add_submod.args, "--force");
+ strvec_pushl(&add_submod.args, "--", add_data->sm_path, NULL);
+
+ if (run_command(&add_submod))
+ die(_("Failed to add submodule '%s'"), add_data->sm_path);
+
+ if (config_submodule_in_gitmodules(add_data->sm_name, "path", add_data->sm_path) ||
+ config_submodule_in_gitmodules(add_data->sm_name, "url", add_data->repo))
+ die(_("Failed to register submodule '%s'"), add_data->sm_path);
+
+ if (add_data->branch) {
+ if (config_submodule_in_gitmodules(add_data->sm_name,
+ "branch", add_data->branch))
+ die(_("Failed to register submodule '%s'"), add_data->sm_path);
+ }
+
+ add_gitmodules.git_cmd = 1;
+ strvec_pushl(&add_gitmodules.args,
+ "add", "--force", "--", ".gitmodules", NULL);
+
+ if (run_command(&add_gitmodules))
+ die(_("Failed to register submodule '%s'"), add_data->sm_path);
+
+ /*
+ * NEEDSWORK: In a multi-working-tree world this needs to be
+ * set in the per-worktree config.
+ */
+ /*
+ * NEEDSWORK: In the longer run, we need to get rid of this
+ * pattern of querying "submodule.active" before calling
+ * is_submodule_active(), since that function needs to find
+ * out the value of "submodule.active" again anyway.
+ */
+ if (!git_config_get_string("submodule.active", &val) && val) {
+ /*
+ * If the submodule being added isn't already covered by the
+ * current configured pathspec, set the submodule's active flag
+ */
+ if (!is_submodule_active(the_repository, add_data->sm_path)) {
+ key = xstrfmt("submodule.%s.active", add_data->sm_name);
+ git_config_set_gently(key, "true");
+ free(key);
+ }
+ } else {
+ key = xstrfmt("submodule.%s.active", add_data->sm_name);
+ git_config_set_gently(key, "true");
+ free(key);
+ }
+}
+
+static void die_on_index_match(const char *path, int force)
+{
+ struct pathspec ps;
+ const char *args[] = { path, NULL };
+ parse_pathspec(&ps, 0, PATHSPEC_PREFER_CWD, NULL, args);
+
+ if (read_cache_preload(NULL) < 0)
+ die(_("index file corrupt"));
+
+ if (ps.nr) {
+ int i;
+ char *ps_matched = xcalloc(ps.nr, 1);
+
+ /* TODO: audit for interaction with sparse-index. */
+ ensure_full_index(&the_index);
+
+ /*
+ * Since there is only one pathspec, we just need
+ * need to check ps_matched[0] to know if a cache
+ * entry matched.
+ */
+ for (i = 0; i < active_nr; i++) {
+ ce_path_match(&the_index, active_cache[i], &ps,
+ ps_matched);
+
+ if (ps_matched[0]) {
+ if (!force)
+ die(_("'%s' already exists in the index"),
+ path);
+ if (!S_ISGITLINK(active_cache[i]->ce_mode))
+ die(_("'%s' already exists in the index "
+ "and is not a submodule"), path);
+ break;
+ }
+ }
+ free(ps_matched);
+ }
+ clear_pathspec(&ps);
+}
+
+static void die_on_repo_without_commits(const char *path)
{
- int force = 0, quiet = 0, dissociate = 0, progress = 0;
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_addstr(&sb, path);
+ if (is_nonbare_repository_dir(&sb)) {
+ struct object_id oid;
+ if (resolve_gitlink_ref(path, "HEAD", &oid) < 0)
+ die(_("'%s' does not have a commit checked out"), path);
+ }
+ strbuf_release(&sb);
+}
+
+static int module_add(int argc, const char **argv, const char *prefix)
+{
+ int force = 0, quiet = 0, progress = 0, dissociate = 0;
struct add_data add_data = ADD_DATA_INIT;
struct option options[] = {
- OPT_STRING('b', "branch", &add_data.branch,
- N_("branch"),
- N_("branch of repository to checkout on cloning")),
- OPT_STRING(0, "prefix", &prefix,
- N_("path"),
- N_("alternative anchor for relative paths")),
- OPT_STRING(0, "path", &add_data.sm_path,
- N_("path"),
- N_("where the new submodule will be cloned to")),
- OPT_STRING(0, "name", &add_data.sm_name,
- N_("string"),
- N_("name of the new submodule")),
- OPT_STRING(0, "url", &add_data.realrepo,
- N_("string"),
- N_("url where to clone the submodule from")),
- OPT_STRING(0, "reference", &add_data.reference_path,
- N_("repo"),
- N_("reference repository")),
- OPT_BOOL(0, "dissociate", &dissociate,
- N_("use --reference only while cloning")),
- OPT_INTEGER(0, "depth", &add_data.depth,
- N_("depth for shallow clones")),
- OPT_BOOL(0, "progress", &progress,
- N_("force cloning progress")),
+ OPT_STRING('b', "branch", &add_data.branch, N_("branch"),
+ N_("branch of repository to add as submodule")),
OPT__FORCE(&force, N_("allow adding an otherwise ignored submodule path"),
PARSE_OPT_NOCOMPLETE),
- OPT__QUIET(&quiet, "suppress output for cloning a submodule"),
+ OPT__QUIET(&quiet, N_("print only error messages")),
+ OPT_BOOL(0, "progress", &progress, N_("force cloning progress")),
+ OPT_STRING(0, "reference", &add_data.reference_path, N_("repository"),
+ N_("reference repository")),
+ OPT_BOOL(0, "dissociate", &dissociate, N_("borrow the objects from reference repositories")),
+ OPT_STRING(0, "name", &add_data.sm_name, N_("name"),
+ N_("sets the submodule’s name to the given string "
+ "instead of defaulting to its path")),
+ OPT_INTEGER(0, "depth", &add_data.depth, N_("depth for shallow clones")),
OPT_END()
};
const char *const usage[] = {
- N_("git submodule--helper add-clone [<options>...] "
- "--url <url> --path <path> --name <name>"),
+ N_("git submodule--helper add [<options>] [--] <repository> [<path>]"),
NULL
};
argc = parse_options(argc, argv, prefix, options, usage, 0);
- if (argc != 0)
+ if (!is_writing_gitmodules_ok())
+ die(_("please make sure that the .gitmodules file is in the working tree"));
+
+ if (prefix && *prefix &&
+ add_data.reference_path && !is_absolute_path(add_data.reference_path))
+ add_data.reference_path = xstrfmt("%s%s", prefix, add_data.reference_path);
+
+ if (argc == 0 || argc > 2)
usage_with_options(usage, options);
+ add_data.repo = argv[0];
+ if (argc == 1)
+ add_data.sm_path = git_url_basename(add_data.repo, 0, 0);
+ else
+ add_data.sm_path = xstrdup(argv[1]);
+
+ if (prefix && *prefix && !is_absolute_path(add_data.sm_path))
+ add_data.sm_path = xstrfmt("%s%s", prefix, add_data.sm_path);
+
+ if (starts_with_dot_dot_slash(add_data.repo) ||
+ starts_with_dot_slash(add_data.repo)) {
+ if (prefix)
+ die(_("Relative path can only be used from the toplevel "
+ "of the working tree"));
+
+ /* dereference source url relative to parent's url */
+ add_data.realrepo = resolve_relative_url(add_data.repo, NULL, 1);
+ } else if (is_dir_sep(add_data.repo[0]) || strchr(add_data.repo, ':')) {
+ add_data.realrepo = add_data.repo;
+ } else {
+ die(_("repo URL: '%s' must be absolute or begin with ./|../"),
+ add_data.repo);
+ }
+
+ /*
+ * normalize path:
+ * multiple //; leading ./; /./; /../;
+ */
+ normalize_path_copy(add_data.sm_path, add_data.sm_path);
+ strip_dir_trailing_slashes(add_data.sm_path);
+
+ die_on_index_match(add_data.sm_path, force);
+ die_on_repo_without_commits(add_data.sm_path);
+
+ if (!force) {
+ int exit_code = -1;
+ struct strbuf sb = STRBUF_INIT;
+ struct child_process cp = CHILD_PROCESS_INIT;
+ cp.git_cmd = 1;
+ cp.no_stdout = 1;
+ strvec_pushl(&cp.args, "add", "--dry-run", "--ignore-missing",
+ "--no-warn-embedded-repo", add_data.sm_path, NULL);
+ if ((exit_code = pipe_command(&cp, NULL, 0, NULL, 0, &sb, 0))) {
+ strbuf_complete_line(&sb);
+ fputs(sb.buf, stderr);
+ free(add_data.sm_path);
+ return exit_code;
+ }
+ strbuf_release(&sb);
+ }
+
+ if(!add_data.sm_name)
+ add_data.sm_name = add_data.sm_path;
+
+ if (check_submodule_name(add_data.sm_name))
+ die(_("'%s' is not a valid submodule name"), add_data.sm_name);
+
add_data.prefix = prefix;
- add_data.progress = !!progress;
- add_data.dissociate = !!dissociate;
add_data.force = !!force;
add_data.quiet = !!quiet;
+ add_data.progress = !!progress;
+ add_data.dissociate = !!dissociate;
- if (add_submodule(&add_data))
+ if (add_submodule(&add_data)) {
+ free(add_data.sm_path);
return 1;
+ }
+ configure_added_submodule(&add_data);
+ free(add_data.sm_path);
return 0;
}
@@ -2948,12 +3367,12 @@ static struct cmd_struct commands[] = {
{"list", module_list, 0},
{"name", module_name, 0},
{"clone", module_clone, 0},
- {"add-clone", add_clone, 0},
+ {"add", module_add, SUPPORT_SUPER_PREFIX},
{"update-module-mode", module_update_module_mode, 0},
{"update-clone", update_clone, 0},
+ {"run-update-procedure", run_update_procedure, 0},
{"ensure-core-worktree", ensure_core_worktree, 0},
{"relative-path", resolve_relative_path, 0},
- {"resolve-relative-url", resolve_relative_url, 0},
{"resolve-relative-url-test", resolve_relative_url_test, 0},
{"foreach", module_foreach, SUPPORT_SUPER_PREFIX},
{"init", module_init, SUPPORT_SUPER_PREFIX},
diff --git a/builtin/tag.c b/builtin/tag.c
index 065b6bf..41863c5 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -23,10 +23,10 @@
static const char * const git_tag_usage[] = {
N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]\n"
- "\t\t<tagname> [<head>]"),
+ " <tagname> [<head>]"),
N_("git tag -d <tagname>..."),
N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]\n"
- "\t\t[--format=<format>] [--merged <commit>] [--no-merged <commit>] [<pattern>...]"),
+ " [--format=<format>] [--merged <commit>] [--no-merged <commit>] [<pattern>...]"),
N_("git tag -v [--format=<format>] <tagname>..."),
NULL
};
@@ -178,7 +178,6 @@ static const char tag_template_nocleanup[] =
static int git_tag_config(const char *var, const char *value, void *cb)
{
int status;
- struct ref_sorting **sorting_tail = (struct ref_sorting **)cb;
if (!strcmp(var, "tag.gpgsign")) {
config_sign_tag = git_config_bool(var, value);
@@ -188,7 +187,7 @@ static int git_tag_config(const char *var, const char *value, void *cb)
if (!strcmp(var, "tag.sort")) {
if (!value)
return config_error_nonbool(var);
- parse_ref_sorting(sorting_tail, value);
+ string_list_append(cb, value);
return 0;
}
@@ -432,11 +431,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
int annotate = 0, force = 0;
int cmdmode = 0, create_tag_object = 0;
const char *msgfile = NULL, *keyid = NULL;
- struct msg_arg msg = { 0, STRBUF_INIT };
+ struct msg_arg msg = { .buf = STRBUF_INIT };
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
struct ref_filter filter;
- static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+ struct ref_sorting *sorting;
+ struct string_list sorting_options = STRING_LIST_INIT_DUP;
struct ref_format format = REF_FORMAT_INIT;
int icase = 0;
int edit_flag = 0;
@@ -470,7 +470,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")),
OPT_MERGED(&filter, N_("print only tags that are merged")),
OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
- OPT_REF_SORT(sorting_tail),
+ OPT_REF_SORT(&sorting_options),
{
OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT,
@@ -482,10 +482,11 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
OPT_END()
};
+ int ret = 0;
setup_ref_filter_porcelain_msg();
- git_config(git_tag_config, sorting_tail);
+ git_config(git_tag_config, &sorting_options);
memset(&opt, 0, sizeof(opt));
memset(&filter, 0, sizeof(filter));
@@ -524,12 +525,10 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
die(_("--column and -n are incompatible"));
colopts = 0;
}
- if (!sorting)
- sorting = ref_default_sorting();
+ sorting = ref_sorting_options(&sorting_options);
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
filter.ignore_case = icase;
if (cmdmode == 'l') {
- int ret;
if (column_active(colopts)) {
struct column_options copts;
memset(&copts, 0, sizeof(copts));
@@ -540,7 +539,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
ret = list_tags(&filter, sorting, &format);
if (column_active(colopts))
stop_column_filter();
- return ret;
+ goto cleanup;
}
if (filter.lines != -1)
die(_("-n option is only allowed in list mode"));
@@ -552,12 +551,15 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
die(_("--points-at option is only allowed in list mode"));
if (filter.reachable_from || filter.unreachable_from)
die(_("--merged and --no-merged options are only allowed in list mode"));
- if (cmdmode == 'd')
- return delete_tags(argv);
+ if (cmdmode == 'd') {
+ ret = delete_tags(argv);
+ goto cleanup;
+ }
if (cmdmode == 'v') {
if (format.format && verify_ref_format(&format))
usage_with_options(git_tag_usage, options);
- return for_each_tag_name(argv, verify_tag, &format);
+ ret = for_each_tag_name(argv, verify_tag, &format);
+ goto cleanup;
}
if (msg.given || msgfile) {
@@ -626,10 +628,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
printf(_("Updated tag '%s' (was %s)\n"), tag,
find_unique_abbrev(&prev, DEFAULT_ABBREV));
- UNLEAK(buf);
- UNLEAK(ref);
- UNLEAK(reflog_msg);
- UNLEAK(msg);
- UNLEAK(err);
- return 0;
+cleanup:
+ ref_sorting_release(sorting);
+ strbuf_release(&buf);
+ strbuf_release(&ref);
+ strbuf_release(&reflog_msg);
+ strbuf_release(&msg.buf);
+ strbuf_release(&err);
+ return ret;
}
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 6029a80..a84e7b4 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -302,6 +302,12 @@ static void parse_cmd_verify(struct ref_transaction *transaction,
strbuf_release(&err);
}
+static void report_ok(const char *command)
+{
+ fprintf(stdout, "%s: ok\n", command);
+ fflush(stdout);
+}
+
static void parse_cmd_option(struct ref_transaction *transaction,
const char *next, const char *end)
{
@@ -317,7 +323,7 @@ static void parse_cmd_start(struct ref_transaction *transaction,
{
if (*next != line_termination)
die("start: extra input: %s", next);
- puts("start: ok");
+ report_ok("start");
}
static void parse_cmd_prepare(struct ref_transaction *transaction,
@@ -328,7 +334,7 @@ static void parse_cmd_prepare(struct ref_transaction *transaction,
die("prepare: extra input: %s", next);
if (ref_transaction_prepare(transaction, &error))
die("prepare: %s", error.buf);
- puts("prepare: ok");
+ report_ok("prepare");
}
static void parse_cmd_abort(struct ref_transaction *transaction,
@@ -339,7 +345,7 @@ static void parse_cmd_abort(struct ref_transaction *transaction,
die("abort: extra input: %s", next);
if (ref_transaction_abort(transaction, &error))
die("abort: %s", error.buf);
- puts("abort: ok");
+ report_ok("abort");
}
static void parse_cmd_commit(struct ref_transaction *transaction,
@@ -350,7 +356,7 @@ static void parse_cmd_commit(struct ref_transaction *transaction,
die("commit: extra input: %s", next);
if (ref_transaction_commit(transaction, &error))
die("commit: %s", error.buf);
- puts("commit: ok");
+ report_ok("commit");
ref_transaction_free(transaction);
}
diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c
index 6da8fa2..125af53 100644
--- a/builtin/upload-pack.c
+++ b/builtin/upload-pack.c
@@ -16,16 +16,18 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
{
const char *dir;
int strict = 0;
- struct upload_pack_options opts = { 0 };
- struct serve_options serve_opts = SERVE_OPTIONS_INIT;
+ int advertise_refs = 0;
+ int stateless_rpc = 0;
+ int timeout = 0;
struct option options[] = {
- OPT_BOOL(0, "stateless-rpc", &opts.stateless_rpc,
+ OPT_BOOL(0, "stateless-rpc", &stateless_rpc,
N_("quit after a single request/response exchange")),
- OPT_BOOL(0, "advertise-refs", &opts.advertise_refs,
- N_("exit immediately after initial ref advertisement")),
+ OPT_HIDDEN_BOOL(0, "http-backend-info-refs", &advertise_refs,
+ N_("serve up the info/refs for git-http-backend")),
+ OPT_ALIAS(0, "advertise-refs", "http-backend-info-refs"),
OPT_BOOL(0, "strict", &strict,
N_("do not try <directory>/.git/ if <directory> is no Git directory")),
- OPT_INTEGER(0, "timeout", &opts.timeout,
+ OPT_INTEGER(0, "timeout", &timeout,
N_("interrupt transfer after <n> seconds of inactivity")),
OPT_END()
};
@@ -38,9 +40,6 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
if (argc != 1)
usage_with_options(upload_pack_usage, options);
- if (opts.timeout)
- opts.daemon_mode = 1;
-
setup_path();
dir = argv[0];
@@ -50,21 +49,22 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
switch (determine_protocol_version_server()) {
case protocol_v2:
- serve_opts.advertise_capabilities = opts.advertise_refs;
- serve_opts.stateless_rpc = opts.stateless_rpc;
- serve(&serve_opts);
+ if (advertise_refs)
+ protocol_v2_advertise_capabilities();
+ else
+ protocol_v2_serve_loop(stateless_rpc);
break;
case protocol_v1:
/*
* v1 is just the original protocol with a version string,
* so just fall through after writing the version string.
*/
- if (opts.advertise_refs || !opts.stateless_rpc)
+ if (advertise_refs || !stateless_rpc)
packet_write_fmt(1, "version 1\n");
/* fallthrough */
case protocol_v0:
- upload_pack(&opts);
+ upload_pack(advertise_refs, stateless_rpc, timeout);
break;
case protocol_unknown_version:
BUG("unknown protocol version");
diff --git a/builtin/worktree.c b/builtin/worktree.c
index 0d0a80d..d22ece9 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -8,6 +8,7 @@
#include "branch.h"
#include "refs.h"
#include "run-command.h"
+#include "hook.h"
#include "sigchain.h"
#include "submodule.h"
#include "utf8.h"
diff --git a/bulk-checkin.c b/bulk-checkin.c
index b023d99..8785b2a 100644
--- a/bulk-checkin.c
+++ b/bulk-checkin.c
@@ -23,9 +23,25 @@ static struct bulk_checkin_state {
uint32_t nr_written;
} state;
+static void finish_tmp_packfile(struct strbuf *basename,
+ const char *pack_tmp_name,
+ struct pack_idx_entry **written_list,
+ uint32_t nr_written,
+ struct pack_idx_option *pack_idx_opts,
+ unsigned char hash[])
+{
+ char *idx_tmp_name = NULL;
+
+ stage_tmp_packfiles(basename, pack_tmp_name, written_list, nr_written,
+ pack_idx_opts, hash, &idx_tmp_name);
+ rename_tmp_packfile_idx(basename, &idx_tmp_name);
+
+ free(idx_tmp_name);
+}
+
static void finish_bulk_checkin(struct bulk_checkin_state *state)
{
- struct object_id oid;
+ unsigned char hash[GIT_MAX_RAWSZ];
struct strbuf packname = STRBUF_INIT;
int i;
@@ -37,19 +53,20 @@ static void finish_bulk_checkin(struct bulk_checkin_state *state)
unlink(state->pack_tmp_name);
goto clear_exit;
} else if (state->nr_written == 1) {
- finalize_hashfile(state->f, oid.hash, CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
+ finalize_hashfile(state->f, hash, CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
} else {
- int fd = finalize_hashfile(state->f, oid.hash, 0);
- fixup_pack_header_footer(fd, oid.hash, state->pack_tmp_name,
- state->nr_written, oid.hash,
+ int fd = finalize_hashfile(state->f, hash, 0);
+ fixup_pack_header_footer(fd, hash, state->pack_tmp_name,
+ state->nr_written, hash,
state->offset);
close(fd);
}
- strbuf_addf(&packname, "%s/pack/pack-", get_object_directory());
+ strbuf_addf(&packname, "%s/pack/pack-%s.", get_object_directory(),
+ hash_to_hex(hash));
finish_tmp_packfile(&packname, state->pack_tmp_name,
state->written, state->nr_written,
- &state->pack_idx_opts, oid.hash);
+ &state->pack_idx_opts, hash);
for (i = 0; i < state->nr_written; i++)
free(state->written[i]);
diff --git a/bundle.c b/bundle.c
index ab63f40..a0bb687 100644
--- a/bundle.c
+++ b/bundle.c
@@ -569,18 +569,18 @@ err:
}
int unbundle(struct repository *r, struct bundle_header *header,
- int bundle_fd, int flags)
+ int bundle_fd, struct strvec *extra_index_pack_args)
{
- const char *argv_index_pack[] = {"index-pack",
- "--fix-thin", "--stdin", NULL, NULL};
struct child_process ip = CHILD_PROCESS_INIT;
+ strvec_pushl(&ip.args, "index-pack", "--fix-thin", "--stdin", NULL);
- if (flags & BUNDLE_VERBOSE)
- argv_index_pack[3] = "-v";
+ if (extra_index_pack_args) {
+ strvec_pushv(&ip.args, extra_index_pack_args->v);
+ strvec_clear(extra_index_pack_args);
+ }
if (verify_bundle(r, header, 0))
return -1;
- ip.argv = argv_index_pack;
ip.in = bundle_fd;
ip.no_stdout = 1;
ip.git_cmd = 1;
diff --git a/bundle.h b/bundle.h
index 1927d8c..06009fe 100644
--- a/bundle.h
+++ b/bundle.h
@@ -26,9 +26,19 @@ int create_bundle(struct repository *r, const char *path,
int argc, const char **argv, struct strvec *pack_options,
int version);
int verify_bundle(struct repository *r, struct bundle_header *header, int verbose);
-#define BUNDLE_VERBOSE 1
+
+/**
+ * Unbundle after reading the header with read_bundle_header().
+ *
+ * We'll invoke "git index-pack --stdin --fix-thin" for you on the
+ * provided `bundle_fd` from read_bundle_header().
+ *
+ * Provide "extra_index_pack_args" to pass any extra arguments
+ * (e.g. "-v" for verbose/progress), NULL otherwise. The provided
+ * "extra_index_pack_args" (if any) will be strvec_clear()'d for you.
+ */
int unbundle(struct repository *r, struct bundle_header *header,
- int bundle_fd, int flags);
+ int bundle_fd, struct strvec *extra_index_pack_args);
int list_bundle_refs(struct bundle_header *header,
int argc, const char **argv);
diff --git a/cache-tree.c b/cache-tree.c
index 90919f9..79d1681 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -440,8 +440,9 @@ static int update_one(struct cache_tree *it,
} else if (dryrun) {
hash_object_file(the_hash_algo, buffer.buf, buffer.len,
tree_type, &it->oid);
- } else if (write_object_file(buffer.buf, buffer.len, tree_type,
- &it->oid)) {
+ } else if (write_object_file_flags(buffer.buf, buffer.len, tree_type,
+ &it->oid, flags & WRITE_TREE_SILENT
+ ? HASH_SILENT : 0)) {
strbuf_release(&buffer);
return -1;
}
@@ -826,10 +827,17 @@ static void verify_one_sparse(struct repository *r,
path->buf);
}
-static void verify_one(struct repository *r,
- struct index_state *istate,
- struct cache_tree *it,
- struct strbuf *path)
+/*
+ * Returns:
+ * 0 - Verification completed.
+ * 1 - Restart verification - a call to ensure_full_index() freed the cache
+ * tree that is being verified and verification needs to be restarted from
+ * the new toplevel cache tree.
+ */
+static int verify_one(struct repository *r,
+ struct index_state *istate,
+ struct cache_tree *it,
+ struct strbuf *path)
{
int i, pos, len = path->len;
struct strbuf tree_buf = STRBUF_INIT;
@@ -837,21 +845,30 @@ static void verify_one(struct repository *r,
for (i = 0; i < it->subtree_nr; i++) {
strbuf_addf(path, "%s/", it->down[i]->name);
- verify_one(r, istate, it->down[i]->cache_tree, path);
+ if (verify_one(r, istate, it->down[i]->cache_tree, path))
+ return 1;
strbuf_setlen(path, len);
}
if (it->entry_count < 0 ||
/* no verification on tests (t7003) that replace trees */
lookup_replace_object(r, &it->oid) != &it->oid)
- return;
+ return 0;
if (path->len) {
+ /*
+ * If the index is sparse and the cache tree is not
+ * index_name_pos() may trigger ensure_full_index() which will
+ * free the tree that is being verified.
+ */
+ int is_sparse = istate->sparse_index;
pos = index_name_pos(istate, path->buf, path->len);
+ if (is_sparse && !istate->sparse_index)
+ return 1;
if (pos >= 0) {
verify_one_sparse(r, istate, it, path, pos);
- return;
+ return 0;
}
pos = -pos - 1;
@@ -899,6 +916,7 @@ static void verify_one(struct repository *r,
oid_to_hex(&new_oid), oid_to_hex(&it->oid));
strbuf_setlen(path, len);
strbuf_release(&tree_buf);
+ return 0;
}
void cache_tree_verify(struct repository *r, struct index_state *istate)
@@ -907,6 +925,10 @@ void cache_tree_verify(struct repository *r, struct index_state *istate)
if (!istate->cache_tree)
return;
- verify_one(r, istate, istate->cache_tree, &path);
+ if (verify_one(r, istate, istate->cache_tree, &path)) {
+ strbuf_reset(&path);
+ if (verify_one(r, istate, istate->cache_tree, &path))
+ BUG("ensure_full_index() called twice while verifying cache tree");
+ }
strbuf_release(&path);
}
diff --git a/cache.h b/cache.h
index d23de69..eba1248 100644
--- a/cache.h
+++ b/cache.h
@@ -887,6 +887,7 @@ int ie_modified(struct index_state *, const struct cache_entry *, struct stat *,
#define HASH_WRITE_OBJECT 1
#define HASH_FORMAT_CHECK 2
#define HASH_RENORMALIZE 4
+#define HASH_SILENT 8
int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);
@@ -958,7 +959,6 @@ extern char *apply_default_ignorewhitespace;
extern const char *git_attributes_file;
extern const char *git_hooks_path;
extern int zlib_compression_level;
-extern int core_compression_level;
extern int pack_compression_level;
extern size_t packed_git_window_size;
extern size_t packed_git_limit;
@@ -996,14 +996,6 @@ extern int core_apply_sparse_checkout;
extern int core_sparse_checkout_cone;
/*
- * Include broken refs in all ref iterations, which will
- * generally choke dangerous operations rather than letting
- * them silently proceed without taking the broken ref into
- * account.
- */
-extern int ref_paranoia;
-
-/*
* Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value).
*/
int use_optional_locks(void);
@@ -1211,49 +1203,6 @@ enum scld_error safe_create_leading_directories(char *path);
enum scld_error safe_create_leading_directories_const(const char *path);
enum scld_error safe_create_leading_directories_no_share(char *path);
-/*
- * Callback function for raceproof_create_file(). This function is
- * expected to do something that makes dirname(path) permanent despite
- * the fact that other processes might be cleaning up empty
- * directories at the same time. Usually it will create a file named
- * path, but alternatively it could create another file in that
- * directory, or even chdir() into that directory. The function should
- * return 0 if the action was completed successfully. On error, it
- * should return a nonzero result and set errno.
- * raceproof_create_file() treats two errno values specially:
- *
- * - ENOENT -- dirname(path) does not exist. In this case,
- * raceproof_create_file() tries creating dirname(path)
- * (and any parent directories, if necessary) and calls
- * the function again.
- *
- * - EISDIR -- the file already exists and is a directory. In this
- * case, raceproof_create_file() removes the directory if
- * it is empty (and recursively any empty directories that
- * it contains) and calls the function again.
- *
- * Any other errno causes raceproof_create_file() to fail with the
- * callback's return value and errno.
- *
- * Obviously, this function should be OK with being called again if it
- * fails with ENOENT or EISDIR. In other scenarios it will not be
- * called again.
- */
-typedef int create_file_fn(const char *path, void *cb);
-
-/*
- * Create a file in dirname(path) by calling fn, creating leading
- * directories if necessary. Retry a few times in case we are racing
- * with another process that is trying to clean up the directory that
- * contains path. See the documentation for create_file_fn for more
- * details.
- *
- * Return the value and set the errno that resulted from the most
- * recent call of fn. fn is always called at least once, and will be
- * called more than once if it returns ENOENT or EISDIR.
- */
-int raceproof_create_file(const char *path, create_file_fn fn, void *cb);
-
int mkdir_in_gitdir(const char *path);
char *interpolate_path(const char *path, int real_home);
/* NEEDSWORK: remove this synonym once in-flight topics have migrated */
@@ -1299,6 +1248,13 @@ int looks_like_command_line_option(const char *str);
/**
* Return a newly allocated string with the evaluation of
+ * "$XDG_CONFIG_HOME/$subdir/$filename" if $XDG_CONFIG_HOME is non-empty, otherwise
+ * "$HOME/.config/$subdir/$filename". Return NULL upon error.
+ */
+char *xdg_config_home_for(const char *subdir, const char *filename);
+
+/**
+ * Return a newly allocated string with the evaluation of
* "$XDG_CONFIG_HOME/git/$filename" if $XDG_CONFIG_HOME is non-empty, otherwise
* "$HOME/.config/git/$filename". Return NULL upon error.
*/
@@ -1313,11 +1269,50 @@ char *xdg_cache_home(const char *filename);
int git_open_cloexec(const char *name, int flags);
#define git_open(name) git_open_cloexec(name, O_RDONLY)
-int unpack_loose_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
-int parse_loose_header(const char *hdr, unsigned long *sizep);
+
+/**
+ * unpack_loose_header() initializes the data stream needed to unpack
+ * a loose object header.
+ *
+ * Returns:
+ *
+ * - ULHR_OK on success
+ * - ULHR_BAD on error
+ * - ULHR_TOO_LONG if the header was too long
+ *
+ * It will only parse up to MAX_HEADER_LEN bytes unless an optional
+ * "hdrbuf" argument is non-NULL. This is intended for use with
+ * OBJECT_INFO_ALLOW_UNKNOWN_TYPE to extract the bad type for (error)
+ * reporting. The full header will be extracted to "hdrbuf" for use
+ * with parse_loose_header(), ULHR_TOO_LONG will still be returned
+ * from this function to indicate that the header was too long.
+ */
+enum unpack_loose_header_result {
+ ULHR_OK,
+ ULHR_BAD,
+ ULHR_TOO_LONG,
+};
+enum unpack_loose_header_result unpack_loose_header(git_zstream *stream,
+ unsigned char *map,
+ unsigned long mapsize,
+ void *buffer,
+ unsigned long bufsiz,
+ struct strbuf *hdrbuf);
+
+/**
+ * parse_loose_header() parses the starting "<type> <len>\0" of an
+ * object. If it doesn't follow that format -1 is returned. To check
+ * the validity of the <type> populate the "typep" in the "struct
+ * object_info". It will be OBJ_BAD if the object type is unknown. The
+ * parsed <len> can be retrieved via "oi->sizep", and from there
+ * passed to unpack_loose_rest().
+ */
+struct object_info;
+int parse_loose_header(const char *hdr, struct object_info *oi);
int check_object_signature(struct repository *r, const struct object_id *oid,
- void *buf, unsigned long size, const char *type);
+ void *buf, unsigned long size, const char *type,
+ struct object_id *real_oidp);
int finalize_object_file(const char *tmpfile, const char *filename);
@@ -1662,7 +1657,9 @@ struct cache_def {
int track_flags;
int prefix_len_stat_func;
};
-#define CACHE_DEF_INIT { STRBUF_INIT, 0, 0, 0 }
+#define CACHE_DEF_INIT { \
+ .path = STRBUF_INIT, \
+}
static inline void cache_def_clear(struct cache_def *cache)
{
strbuf_release(&cache->path);
@@ -1719,13 +1716,6 @@ int update_server_info(int);
const char *get_log_output_encoding(void);
const char *get_commit_output_encoding(void);
-/*
- * This is a hack for test programs like test-dump-untracked-cache to
- * ensure that they do not modify the untracked cache when reading it.
- * Do not use it otherwise!
- */
-extern int ignore_untracked_cache_config;
-
int committer_ident_sufficiently_given(void);
int author_ident_sufficiently_given(void);
@@ -1738,6 +1728,8 @@ extern const char *git_mailmap_blob;
void maybe_flush_or_die(FILE *, const char *);
__attribute__((format (printf, 2, 3)))
void fprintf_or_die(FILE *, const char *fmt, ...);
+void fwrite_or_die(FILE *f, const void *buf, size_t count);
+void fflush_or_die(FILE *f);
#define COPY_READ_ERROR (-2)
#define COPY_WRITE_ERROR (-3)
diff --git a/cbtree.h b/cbtree.h
index a04a312..dedbb8e 100644
--- a/cbtree.h
+++ b/cbtree.h
@@ -37,11 +37,12 @@ enum cb_next {
CB_BREAK = 1
};
-#define CBTREE_INIT { .root = NULL }
+#define CBTREE_INIT { 0 }
static inline void cb_init(struct cb_tree *t)
{
- t->root = NULL;
+ struct cb_tree blank = CBTREE_INIT;
+ memcpy(t, &blank, sizeof(*t));
}
struct cb_node *cb_lookup(struct cb_tree *, const uint8_t *k, size_t klen);
diff --git a/checkout.c b/checkout.c
index 6586e30..2e39dae 100644
--- a/checkout.c
+++ b/checkout.c
@@ -14,7 +14,7 @@ struct tracking_name_data {
struct object_id *default_dst_oid;
};
-#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0, NULL, NULL, NULL }
+#define TRACKING_NAME_DATA_INIT { 0 }
static int check_tracking_name(struct remote *remote, void *cb_data)
{
diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh
index 5772081..1d0e48f 100755
--- a/ci/install-dependencies.sh
+++ b/ci/install-dependencies.sh
@@ -12,7 +12,7 @@ UBUNTU_COMMON_PKGS="make libssl-dev libcurl4-openssl-dev libexpat-dev
libemail-valid-perl libio-socket-ssl-perl libnet-smtp-ssl-perl"
case "$jobname" in
-linux-clang|linux-gcc)
+linux-clang|linux-gcc|linux-leaks)
sudo apt-add-repository -y "ppa:ubuntu-toolchain-r/test"
sudo apt-get -q update
sudo apt-get -q -y install language-pack-is libsvn-perl apache2 \
diff --git a/ci/lib.sh b/ci/lib.sh
index 476c3f3..82cb17f 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -183,7 +183,7 @@ export GIT_TEST_CLONE_2GB=true
export SKIP_DASHED_BUILT_INS=YesPlease
case "$jobname" in
-linux-clang|linux-gcc)
+linux-clang|linux-gcc|linux-leaks)
if [ "$jobname" = linux-gcc ]
then
export CC=gcc-8
@@ -233,4 +233,11 @@ linux-musl)
;;
esac
+case "$jobname" in
+linux-leaks)
+ export SANITIZE=leak
+ export GIT_TEST_PASSING_SANITIZE_LEAK=true
+ ;;
+esac
+
MAKEFLAGS="$MAKEFLAGS CC=${CC:-cc}"
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index f3aba5d..cc62616 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -28,6 +28,7 @@ linux-gcc)
export GIT_TEST_COMMIT_GRAPH=1
export GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1
export GIT_TEST_MULTI_PACK_INDEX=1
+ export GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=1
export GIT_TEST_ADD_I_USE_BUILTIN=1
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
export GIT_TEST_WRITE_REV_INDEX=1
diff --git a/command-list.txt b/command-list.txt
index a289f09..eb9cee8 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -168,7 +168,7 @@ git-show-index plumbinginterrogators
git-show-ref plumbinginterrogators
git-sh-i18n purehelpers
git-sh-setup purehelpers
-git-sparse-checkout mainporcelain worktree
+git-sparse-checkout mainporcelain
git-stash mainporcelain
git-stage complete
git-status mainporcelain info
diff --git a/commit-graph.c b/commit-graph.c
index 00614ac..2706683 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -713,6 +713,7 @@ static void close_commit_graph_one(struct commit_graph *g)
if (!g)
return;
+ clear_commit_graph_data_slab(&commit_graph_data_slab);
close_commit_graph_one(g->base_graph);
free_commit_graph(g);
}
@@ -2125,7 +2126,7 @@ static void sort_and_scan_merged_commits(struct write_commit_graph_context *ctx)
ctx->num_extra_edges = 0;
for (i = 0; i < ctx->commits.nr; i++) {
- display_progress(ctx->progress, i);
+ display_progress(ctx->progress, i + 1);
if (i && oideq(&ctx->commits.list[i - 1]->object.oid,
&ctx->commits.list[i]->object.oid)) {
diff --git a/compat/linux/procinfo.c b/compat/linux/procinfo.c
index 578fed4..bc2f938 100644
--- a/compat/linux/procinfo.c
+++ b/compat/linux/procinfo.c
@@ -4,51 +4,172 @@
#include "strvec.h"
#include "trace2.h"
-static void get_ancestry_names(struct strvec *names)
+/*
+ * We need more complex parsing in stat_parent_pid() and
+ * parse_proc_stat() below than a dumb fscanf(). That's because while
+ * the statcomm field is surrounded by parentheses, the process itself
+ * is free to insert any arbitrary byte sequence its its name. That
+ * can include newlines, spaces, closing parentheses etc.
+ *
+ * See do_task_stat() in fs/proc/array.c in linux.git, this is in
+ * contrast with the escaped version of the name found in
+ * /proc/%d/status.
+ *
+ * So instead of using fscanf() we'll read N bytes from it, look for
+ * the first "(", and then the last ")", anything in-between is our
+ * process name.
+ *
+ * How much N do we need? On Linux /proc/sys/kernel/pid_max is 2^15 by
+ * default, but it can be raised set to values of up to 2^22. So
+ * that's 7 digits for a PID. We have 2 PIDs in the first four fields
+ * we're interested in, so 2 * 7 = 14.
+ *
+ * We then have 3 spaces between those four values, and we'd like to
+ * get to the space between the 4th and the 5th (the "pgrp" field) to
+ * make sure we read the entire "ppid" field. So that brings us up to
+ * 14 + 3 + 1 = 18. Add the two parentheses around the "comm" value
+ * and it's 20. The "state" value itself is then one character (now at
+ * 21).
+ *
+ * Finally the maximum length of the "comm" name itself is 15
+ * characters, e.g. a setting of "123456789abcdefg" will be truncated
+ * to "123456789abcdef". See PR_SET_NAME in prctl(2). So all in all
+ * we'd need to read 21 + 15 = 36 bytes.
+ *
+ * Let's just read 2^6 (64) instead for good measure. If PID_MAX ever
+ * grows past 2^22 we'll be future-proof. We'll then anchor at the
+ * last ")" we find to locate the parent PID.
+ */
+#define STAT_PARENT_PID_READ_N 64
+
+static int parse_proc_stat(struct strbuf *sb, struct strbuf *name,
+ int *statppid)
{
+ const char *comm_lhs = strchr(sb->buf, '(');
+ const char *comm_rhs = strrchr(sb->buf, ')');
+ const char *ppid_lhs, *ppid_rhs;
+ char *p;
+ pid_t ppid;
+
+ if (!comm_lhs || !comm_rhs)
+ goto bad_kernel;
+
+ /*
+ * We're at the ")", that's followed by " X ", where X is a
+ * single "state" character. So advance by 4 bytes.
+ */
+ ppid_lhs = comm_rhs + 4;
+
+ /*
+ * Read until the space between the "ppid" and "pgrp" fields
+ * to make sure we're anchored after the untruncated "ppid"
+ * field..
+ */
+ ppid_rhs = strchr(ppid_lhs, ' ');
+ if (!ppid_rhs)
+ goto bad_kernel;
+
+ ppid = strtol(ppid_lhs, &p, 10);
+ if (ppid_rhs == p) {
+ const char *comm = comm_lhs + 1;
+ size_t commlen = comm_rhs - comm;
+
+ strbuf_add(name, comm, commlen);
+ *statppid = ppid;
+
+ return 0;
+ }
+
+bad_kernel:
/*
- * NEEDSWORK: We could gather the entire pstree into an array to match
- * functionality with compat/win32/trace2_win32_process_info.c.
- * To do so, we may want to examine /proc/<pid>/stat. For now, just
- * gather the immediate parent name which is readily accessible from
- * /proc/$(getppid())/comm.
+ * We were able to read our STAT_PARENT_PID_READ_N bytes from
+ * /proc/%d/stat, but the content is bad. Broken kernel?
+ * Should not happen, but handle it gracefully.
*/
+ return -1;
+}
+
+static int stat_parent_pid(pid_t pid, struct strbuf *name, int *statppid)
+{
struct strbuf procfs_path = STRBUF_INIT;
- struct strbuf name = STRBUF_INIT;
+ struct strbuf sb = STRBUF_INIT;
+ FILE *fp;
+ int ret = -1;
/* try to use procfs if it's present. */
- strbuf_addf(&procfs_path, "/proc/%d/comm", getppid());
- if (strbuf_read_file(&name, procfs_path.buf, 0)) {
- strbuf_release(&procfs_path);
- strbuf_trim_trailing_newline(&name);
- strvec_push(names, strbuf_detach(&name, NULL));
- }
+ strbuf_addf(&procfs_path, "/proc/%d/stat", pid);
+ fp = fopen(procfs_path.buf, "r");
+ if (!fp)
+ goto cleanup;
+
+ /*
+ * We could be more strict here and assert that we read at
+ * least STAT_PARENT_PID_READ_N. My reading of procfs(5) is
+ * that on any modern kernel (at least since 2.6.0 released in
+ * 2003) even if all the mandatory numeric fields were zero'd
+ * out we'd get at least 100 bytes, but let's just check that
+ * we got anything at all and trust the parse_proc_stat()
+ * function to handle its "Bad Kernel?" error checking.
+ */
+ if (!strbuf_fread(&sb, STAT_PARENT_PID_READ_N, fp))
+ goto cleanup;
+ if (parse_proc_stat(&sb, name, statppid) < 0)
+ goto cleanup;
+
+ ret = 0;
+cleanup:
+ if (fp)
+ fclose(fp);
+ strbuf_release(&procfs_path);
+ strbuf_release(&sb);
+
+ return ret;
+}
+
+static void push_ancestry_name(struct strvec *names, pid_t pid)
+{
+ struct strbuf name = STRBUF_INIT;
+ int ppid;
+
+ if (stat_parent_pid(pid, &name, &ppid) < 0)
+ goto cleanup;
+
+ strvec_push(names, name.buf);
+
+ /*
+ * Both errors and reaching the end of the process chain are
+ * reported as fields of 0 by proc(5)
+ */
+ if (ppid)
+ push_ancestry_name(names, ppid);
+cleanup:
+ strbuf_release(&name);
return;
- /* NEEDSWORK: add non-procfs-linux implementations here */
}
void trace2_collect_process_info(enum trace2_process_info_reason reason)
{
- if (!trace2_is_enabled())
- return;
+ struct strvec names = STRVEC_INIT;
- /* someday we may want to write something extra here, but not today */
- if (reason == TRACE2_PROCESS_INFO_EXIT)
+ if (!trace2_is_enabled())
return;
- if (reason == TRACE2_PROCESS_INFO_STARTUP) {
+ switch (reason) {
+ case TRACE2_PROCESS_INFO_EXIT:
/*
- * NEEDSWORK: we could do the entire ptree in an array instead,
- * see compat/win32/trace2_win32_process_info.c.
+ * The Windows version of this calls its
+ * get_peak_memory_info() here. We may want to insert
+ * similar process-end statistics here in the future.
*/
- struct strvec names = STRVEC_INIT;
-
- get_ancestry_names(&names);
+ break;
+ case TRACE2_PROCESS_INFO_STARTUP:
+ push_ancestry_name(&names, getppid());
if (names.nr)
trace2_cmd_ancestry(names.v);
strvec_clear(&names);
+ break;
}
return;
diff --git a/compat/nedmalloc/nedmalloc.c b/compat/nedmalloc/nedmalloc.c
index 1cc31c3..edb438a 100644
--- a/compat/nedmalloc/nedmalloc.c
+++ b/compat/nedmalloc/nedmalloc.c
@@ -510,7 +510,7 @@ static void threadcache_free(nedpool *p, threadcache *tc, int mymspace, void *me
assert(idx<=THREADCACHEMAXBINS);
if(tck==*binsptr)
{
- fprintf(stderr, "Attempt to free already freed memory block %p - aborting!\n", tck);
+ fprintf(stderr, "Attempt to free already freed memory block %p - aborting!\n", (void *)tck);
abort();
}
#ifdef FULLSANITYCHECKS
diff --git a/compat/simple-ipc/ipc-unix-socket.c b/compat/simple-ipc/ipc-unix-socket.c
index 1927e6e..28a7928 100644
--- a/compat/simple-ipc/ipc-unix-socket.c
+++ b/compat/simple-ipc/ipc-unix-socket.c
@@ -35,6 +35,28 @@ enum ipc_active_state ipc_get_active_state(const char *path)
}
}
+#ifdef __CYGWIN__
+ /*
+ * Cygwin emulates Unix sockets by writing special-crafted files whose
+ * `system` bit is set.
+ *
+ * If we are too fast, Cygwin might still be in the process of marking
+ * the underlying file as a system file. Until then, we will not see a
+ * Unix socket here, but a plain file instead. Just in case that this
+ * is happening, wait a little and try again.
+ */
+ {
+ static const int delay[] = { 1, 10, 20, 40, -1 };
+ int i;
+
+ for (i = 0; S_ISREG(st.st_mode) && delay[i] > 0; i++) {
+ sleep_millisec(delay[i]);
+ if (lstat(path, &st) == -1)
+ return IPC_STATE__INVALID_PATH;
+ }
+ }
+#endif
+
/* also complain if a plain file is in the way */
if ((st.st_mode & S_IFMT) != S_IFSOCK)
return IPC_STATE__INVALID_PATH;
@@ -168,7 +190,8 @@ void ipc_client_close_connection(struct ipc_client_connection *connection)
int ipc_client_send_command_to_connection(
struct ipc_client_connection *connection,
- const char *message, struct strbuf *answer)
+ const char *message, size_t message_len,
+ struct strbuf *answer)
{
int ret = 0;
@@ -176,7 +199,7 @@ int ipc_client_send_command_to_connection(
trace2_region_enter("ipc-client", "send-command", NULL);
- if (write_packetized_from_buf_no_flush(message, strlen(message),
+ if (write_packetized_from_buf_no_flush(message, message_len,
connection->fd) < 0 ||
packet_flush_gently(connection->fd) < 0) {
ret = error(_("could not send IPC command"));
@@ -197,7 +220,8 @@ done:
int ipc_client_send_command(const char *path,
const struct ipc_client_connect_options *options,
- const char *message, struct strbuf *answer)
+ const char *message, size_t message_len,
+ struct strbuf *answer)
{
int ret = -1;
enum ipc_active_state state;
@@ -208,7 +232,9 @@ int ipc_client_send_command(const char *path,
if (state != IPC_STATE__LISTENING)
return ret;
- ret = ipc_client_send_command_to_connection(connection, message, answer);
+ ret = ipc_client_send_command_to_connection(connection,
+ message, message_len,
+ answer);
ipc_client_close_connection(connection);
@@ -503,7 +529,7 @@ static int worker_thread__do_io(
if (ret >= 0) {
ret = worker_thread_data->server_data->application_cb(
worker_thread_data->server_data->application_data,
- buf.buf, do_io_reply_callback, &reply_data);
+ buf.buf, buf.len, do_io_reply_callback, &reply_data);
packet_flush_gently(reply_data.fd);
}
diff --git a/compat/simple-ipc/ipc-win32.c b/compat/simple-ipc/ipc-win32.c
index 8dc7bda..20ea7b6 100644
--- a/compat/simple-ipc/ipc-win32.c
+++ b/compat/simple-ipc/ipc-win32.c
@@ -3,6 +3,8 @@
#include "strbuf.h"
#include "pkt-line.h"
#include "thread-utils.h"
+#include "accctrl.h"
+#include "aclapi.h"
#ifndef SUPPORTS_SIMPLE_IPC
/*
@@ -49,6 +51,9 @@ static enum ipc_active_state get_active_state(wchar_t *pipe_path)
if (GetLastError() == ERROR_FILE_NOT_FOUND)
return IPC_STATE__PATH_NOT_FOUND;
+ trace2_data_intmax("ipc-debug", NULL, "getstate/waitpipe/gle",
+ (intmax_t)GetLastError());
+
return IPC_STATE__OTHER_ERROR;
}
@@ -109,9 +114,15 @@ static enum ipc_active_state connect_to_server(
t_start_ms = (DWORD)(getnanotime() / 1000000);
if (!WaitNamedPipeW(wpath, timeout_ms)) {
- if (GetLastError() == ERROR_SEM_TIMEOUT)
+ DWORD gleWait = GetLastError();
+
+ if (gleWait == ERROR_SEM_TIMEOUT)
return IPC_STATE__NOT_LISTENING;
+ trace2_data_intmax("ipc-debug", NULL,
+ "connect/waitpipe/gle",
+ (intmax_t)gleWait);
+
return IPC_STATE__OTHER_ERROR;
}
@@ -133,17 +144,31 @@ static enum ipc_active_state connect_to_server(
break; /* try again */
default:
+ trace2_data_intmax("ipc-debug", NULL,
+ "connect/createfile/gle",
+ (intmax_t)gle);
+
return IPC_STATE__OTHER_ERROR;
}
}
if (!SetNamedPipeHandleState(hPipe, &mode, NULL, NULL)) {
+ gle = GetLastError();
+ trace2_data_intmax("ipc-debug", NULL,
+ "connect/setpipestate/gle",
+ (intmax_t)gle);
+
CloseHandle(hPipe);
return IPC_STATE__OTHER_ERROR;
}
*pfd = _open_osfhandle((intptr_t)hPipe, O_RDWR|O_BINARY);
if (*pfd < 0) {
+ gle = GetLastError();
+ trace2_data_intmax("ipc-debug", NULL,
+ "connect/openosfhandle/gle",
+ (intmax_t)gle);
+
CloseHandle(hPipe);
return IPC_STATE__OTHER_ERROR;
}
@@ -208,7 +233,8 @@ void ipc_client_close_connection(struct ipc_client_connection *connection)
int ipc_client_send_command_to_connection(
struct ipc_client_connection *connection,
- const char *message, struct strbuf *answer)
+ const char *message, size_t message_len,
+ struct strbuf *answer)
{
int ret = 0;
@@ -216,7 +242,7 @@ int ipc_client_send_command_to_connection(
trace2_region_enter("ipc-client", "send-command", NULL);
- if (write_packetized_from_buf_no_flush(message, strlen(message),
+ if (write_packetized_from_buf_no_flush(message, message_len,
connection->fd) < 0 ||
packet_flush_gently(connection->fd) < 0) {
ret = error(_("could not send IPC command"));
@@ -239,7 +265,8 @@ done:
int ipc_client_send_command(const char *path,
const struct ipc_client_connect_options *options,
- const char *message, struct strbuf *response)
+ const char *message, size_t message_len,
+ struct strbuf *response)
{
int ret = -1;
enum ipc_active_state state;
@@ -250,7 +277,9 @@ int ipc_client_send_command(const char *path,
if (state != IPC_STATE__LISTENING)
return ret;
- ret = ipc_client_send_command_to_connection(connection, message, response);
+ ret = ipc_client_send_command_to_connection(connection,
+ message, message_len,
+ response);
ipc_client_close_connection(connection);
@@ -458,7 +487,7 @@ static int do_io(struct ipc_server_thread_data *server_thread_data)
if (ret >= 0) {
ret = server_thread_data->server_data->application_cb(
server_thread_data->server_data->application_data,
- buf.buf, do_io_reply_callback, &reply_data);
+ buf.buf, buf.len, do_io_reply_callback, &reply_data);
packet_flush_gently(reply_data.fd);
@@ -565,11 +594,132 @@ finished:
return NULL;
}
+/*
+ * We need to build a Windows "SECURITY_ATTRIBUTES" object and use it
+ * to apply an ACL when we create the initial instance of the Named
+ * Pipe. The construction is somewhat involved and consists of
+ * several sequential steps and intermediate objects.
+ *
+ * We use this structure to hold these intermediate pointers so that
+ * we can free them as a group. (It is unclear from the docs whether
+ * some of these intermediate pointers can be freed before we are
+ * finished using the "lpSA" member.)
+ */
+struct my_sa_data
+{
+ PSID pEveryoneSID;
+ PACL pACL;
+ PSECURITY_DESCRIPTOR pSD;
+ LPSECURITY_ATTRIBUTES lpSA;
+};
+
+static void init_sa(struct my_sa_data *d)
+{
+ memset(d, 0, sizeof(*d));
+}
+
+static void release_sa(struct my_sa_data *d)
+{
+ if (d->pEveryoneSID)
+ FreeSid(d->pEveryoneSID);
+ if (d->pACL)
+ LocalFree(d->pACL);
+ if (d->pSD)
+ LocalFree(d->pSD);
+ if (d->lpSA)
+ LocalFree(d->lpSA);
+
+ memset(d, 0, sizeof(*d));
+}
+
+/*
+ * Create SECURITY_ATTRIBUTES to apply to the initial named pipe. The
+ * creator of the first server instance gets to set the ACLs on it.
+ *
+ * We allow the well-known group `EVERYONE` to have read+write access
+ * to the named pipe so that clients can send queries to the daemon
+ * and receive the response.
+ *
+ * Normally, this is not necessary since the daemon is usually
+ * automatically started by a foreground command like `git status`,
+ * but in those cases where an elevated Git command started the daemon
+ * (such that the daemon itself runs with elevation), we need to add
+ * the ACL so that non-elevated commands can write to it.
+ *
+ * The following document was helpful:
+ * https://docs.microsoft.com/en-us/windows/win32/secauthz/creating-a-security-descriptor-for-a-new-object-in-c--
+ *
+ * Returns d->lpSA set to a SA or NULL.
+ */
+static LPSECURITY_ATTRIBUTES get_sa(struct my_sa_data *d)
+{
+ SID_IDENTIFIER_AUTHORITY sid_auth_world = SECURITY_WORLD_SID_AUTHORITY;
+#define NR_EA (1)
+ EXPLICIT_ACCESS ea[NR_EA];
+ DWORD dwResult;
+
+ if (!AllocateAndInitializeSid(&sid_auth_world, 1,
+ SECURITY_WORLD_RID, 0,0,0,0,0,0,0,
+ &d->pEveryoneSID)) {
+ DWORD gle = GetLastError();
+ trace2_data_intmax("ipc-debug", NULL, "alloc-world-sid/gle",
+ (intmax_t)gle);
+ goto fail;
+ }
+
+ memset(ea, 0, NR_EA * sizeof(EXPLICIT_ACCESS));
+
+ ea[0].grfAccessPermissions = GENERIC_READ | GENERIC_WRITE;
+ ea[0].grfAccessMode = SET_ACCESS;
+ ea[0].grfInheritance = NO_INHERITANCE;
+ ea[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+ ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
+ ea[0].Trustee.ptstrName = (LPTSTR)d->pEveryoneSID;
+
+ dwResult = SetEntriesInAcl(NR_EA, ea, NULL, &d->pACL);
+ if (dwResult != ERROR_SUCCESS) {
+ DWORD gle = GetLastError();
+ trace2_data_intmax("ipc-debug", NULL, "set-acl-entry/gle",
+ (intmax_t)gle);
+ trace2_data_intmax("ipc-debug", NULL, "set-acl-entry/dw",
+ (intmax_t)dwResult);
+ goto fail;
+ }
+
+ d->pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(
+ LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
+ if (!InitializeSecurityDescriptor(d->pSD, SECURITY_DESCRIPTOR_REVISION)) {
+ DWORD gle = GetLastError();
+ trace2_data_intmax("ipc-debug", NULL, "init-sd/gle", (intmax_t)gle);
+ goto fail;
+ }
+
+ if (!SetSecurityDescriptorDacl(d->pSD, TRUE, d->pACL, FALSE)) {
+ DWORD gle = GetLastError();
+ trace2_data_intmax("ipc-debug", NULL, "set-sd-dacl/gle", (intmax_t)gle);
+ goto fail;
+ }
+
+ d->lpSA = (LPSECURITY_ATTRIBUTES)LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES));
+ d->lpSA->nLength = sizeof(SECURITY_ATTRIBUTES);
+ d->lpSA->lpSecurityDescriptor = d->pSD;
+ d->lpSA->bInheritHandle = FALSE;
+
+ return d->lpSA;
+
+fail:
+ release_sa(d);
+ return NULL;
+}
+
static HANDLE create_new_pipe(wchar_t *wpath, int is_first)
{
HANDLE hPipe;
DWORD dwOpenMode, dwPipeMode;
- LPSECURITY_ATTRIBUTES lpsa = NULL;
+ struct my_sa_data my_sa_data;
+
+ init_sa(&my_sa_data);
dwOpenMode = PIPE_ACCESS_INBOUND | PIPE_ACCESS_OUTBOUND |
FILE_FLAG_OVERLAPPED;
@@ -585,20 +735,15 @@ static HANDLE create_new_pipe(wchar_t *wpath, int is_first)
* set the ACL / Security Attributes on the named
* pipe; subsequent instances inherit and cannot
* change them.
- *
- * TODO Should we allow the application layer to
- * specify security attributes, such as `LocalService`
- * or `LocalSystem`, when we create the named pipe?
- * This question is probably not important when the
- * daemon is started by a foreground user process and
- * only needs to talk to the current user, but may be
- * if the daemon is run via the Control Panel as a
- * System Service.
*/
+ get_sa(&my_sa_data);
}
hPipe = CreateNamedPipeW(wpath, dwOpenMode, dwPipeMode,
- PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, lpsa);
+ PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0,
+ my_sa_data.lpSA);
+
+ release_sa(&my_sa_data);
return hPipe;
}
diff --git a/compat/terminal.c b/compat/terminal.c
index 43b73dd..5b903e7 100644
--- a/compat/terminal.c
+++ b/compat/terminal.c
@@ -8,8 +8,6 @@
#if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE)
-static void restore_term(void);
-
static void restore_term_on_signal(int sig)
{
restore_term();
@@ -25,7 +23,7 @@ static void restore_term_on_signal(int sig)
static int term_fd = -1;
static struct termios old_term;
-static void restore_term(void)
+void restore_term(void)
{
if (term_fd < 0)
return;
@@ -35,15 +33,22 @@ static void restore_term(void)
term_fd = -1;
}
+int save_term(int full_duplex)
+{
+ if (term_fd < 0)
+ term_fd = open("/dev/tty", O_RDWR);
+
+ return (term_fd < 0) ? -1 : tcgetattr(term_fd, &old_term);
+}
+
static int disable_bits(tcflag_t bits)
{
struct termios t;
- term_fd = open("/dev/tty", O_RDWR);
- if (tcgetattr(term_fd, &t) < 0)
+ if (save_term(0) < 0)
goto error;
- old_term = t;
+ t = old_term;
sigchain_push_common(restore_term_on_signal);
t.c_lflag &= ~bits;
@@ -75,9 +80,10 @@ static int enable_non_canonical(void)
static int use_stty = 1;
static struct string_list stty_restore = STRING_LIST_INIT_DUP;
static HANDLE hconin = INVALID_HANDLE_VALUE;
-static DWORD cmode;
+static HANDLE hconout = INVALID_HANDLE_VALUE;
+static DWORD cmode_in, cmode_out;
-static void restore_term(void)
+void restore_term(void)
{
if (use_stty) {
int i;
@@ -97,9 +103,42 @@ static void restore_term(void)
if (hconin == INVALID_HANDLE_VALUE)
return;
- SetConsoleMode(hconin, cmode);
+ SetConsoleMode(hconin, cmode_in);
+ CloseHandle(hconin);
+ if (cmode_out) {
+ assert(hconout != INVALID_HANDLE_VALUE);
+ SetConsoleMode(hconout, cmode_out);
+ CloseHandle(hconout);
+ }
+
+ hconin = hconout = INVALID_HANDLE_VALUE;
+}
+
+int save_term(int full_duplex)
+{
+ hconin = CreateFileA("CONIN$", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hconin == INVALID_HANDLE_VALUE)
+ return -1;
+
+ if (full_duplex) {
+ hconout = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hconout == INVALID_HANDLE_VALUE)
+ goto error;
+
+ GetConsoleMode(hconout, &cmode_out);
+ }
+
+ GetConsoleMode(hconin, &cmode_in);
+ use_stty = 0;
+ return 0;
+error:
CloseHandle(hconin);
hconin = INVALID_HANDLE_VALUE;
+ return -1;
}
static int disable_bits(DWORD bits)
@@ -135,15 +174,11 @@ static int disable_bits(DWORD bits)
use_stty = 0;
}
- hconin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
- FILE_SHARE_READ, NULL, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL, NULL);
- if (hconin == INVALID_HANDLE_VALUE)
+ if (save_term(0) < 0)
return -1;
- GetConsoleMode(hconin, &cmode);
sigchain_push_common(restore_term_on_signal);
- if (!SetConsoleMode(hconin, cmode & ~bits)) {
+ if (!SetConsoleMode(hconin, cmode_in & ~bits)) {
CloseHandle(hconin);
hconin = INVALID_HANDLE_VALUE;
return -1;
@@ -361,6 +396,16 @@ int read_key_without_echo(struct strbuf *buf)
#else
+int save_term(int full_duplex)
+{
+ /* full_duplex == 1, but no support available */
+ return -full_duplex;
+}
+
+void restore_term(void)
+{
+}
+
char *git_terminal_prompt(const char *prompt, int echo)
{
return getpass(prompt);
diff --git a/compat/terminal.h b/compat/terminal.h
index a9d52b8..e1770c5 100644
--- a/compat/terminal.h
+++ b/compat/terminal.h
@@ -1,6 +1,9 @@
#ifndef COMPAT_TERMINAL_H
#define COMPAT_TERMINAL_H
+int save_term(int full_duplex);
+void restore_term(void);
+
char *git_terminal_prompt(const char *prompt, int echo);
/* Read a single keystroke, without echoing it to the terminal */
diff --git a/compat/unsetenv.c b/compat/unsetenv.c
index bf5fd70..b9d34af 100644
--- a/compat/unsetenv.c
+++ b/compat/unsetenv.c
@@ -1,6 +1,6 @@
#include "../git-compat-util.h"
-void gitunsetenv (const char *name)
+int gitunsetenv(const char *name)
{
#if !defined(__MINGW32__)
extern char **environ;
@@ -24,4 +24,6 @@ void gitunsetenv (const char *name)
++dst;
}
environ[dst] = NULL;
+
+ return 0;
}
diff --git a/compat/vcbuild/README b/compat/vcbuild/README
index 51fb083..29ec1d0 100644
--- a/compat/vcbuild/README
+++ b/compat/vcbuild/README
@@ -92,7 +92,7 @@ The Steps of Build Git with VS2008
the git operations.
3. Inside Git's directory run the command:
- make command-list.h config-list.h
+ make generated-hdrs
to generate the header file needed to compile git.
4. Then either build Git with the GNU Make Makefile in the Git projects
diff --git a/compat/win32/lazyload.h b/compat/win32/lazyload.h
index 9e631c8..2b36371 100644
--- a/compat/win32/lazyload.h
+++ b/compat/win32/lazyload.h
@@ -15,10 +15,12 @@
* source, target);
*/
+typedef void (*FARVOIDPROC)(void);
+
struct proc_addr {
const char *const dll;
const char *const function;
- FARPROC pfunction;
+ FARVOIDPROC pfunction;
unsigned initialized : 1;
};
@@ -26,7 +28,8 @@ struct proc_addr {
#define DECLARE_PROC_ADDR(dll, rettype, function, ...) \
static struct proc_addr proc_addr_##function = \
{ #dll, #function, NULL, 0 }; \
- static rettype (WINAPI *function)(__VA_ARGS__)
+ typedef rettype (WINAPI *proc_type_##function)(__VA_ARGS__); \
+ static proc_type_##function function
/*
* Loads a function from a DLL (once-only).
@@ -35,9 +38,9 @@ struct proc_addr {
* This function is not thread-safe.
*/
#define INIT_PROC_ADDR(function) \
- (function = get_proc_addr(&proc_addr_##function))
+ (function = (proc_type_##function)get_proc_addr(&proc_addr_##function))
-static inline void *get_proc_addr(struct proc_addr *proc)
+static inline FARVOIDPROC get_proc_addr(struct proc_addr *proc)
{
/* only do this once */
if (!proc->initialized) {
@@ -46,7 +49,8 @@ static inline void *get_proc_addr(struct proc_addr *proc)
hnd = LoadLibraryExA(proc->dll, NULL,
LOAD_LIBRARY_SEARCH_SYSTEM32);
if (hnd)
- proc->pfunction = GetProcAddress(hnd, proc->function);
+ proc->pfunction = (FARVOIDPROC)GetProcAddress(hnd,
+ proc->function);
}
/* set ENOSYS if DLL or function was not found */
if (!proc->pfunction)
diff --git a/config.c b/config.c
index cb4a805..c5873f3 100644
--- a/config.c
+++ b/config.c
@@ -76,7 +76,6 @@ static struct key_value_info *current_config_kvi;
*/
static enum config_scope current_parsing_scope;
-static int core_compression_seen;
static int pack_compression_seen;
static int zlib_compression_seen;
@@ -149,8 +148,10 @@ static int handle_path_include(const char *path, struct config_include_data *inc
if (!is_absolute_path(path)) {
char *slash;
- if (!cf || !cf->path)
- return error(_("relative config includes must come from files"));
+ if (!cf || !cf->path) {
+ ret = error(_("relative config includes must come from files"));
+ goto cleanup;
+ }
slash = find_last_dir_sep(cf->path);
if (slash)
@@ -168,6 +169,7 @@ static int handle_path_include(const char *path, struct config_include_data *inc
ret = git_config_from_file(git_config_include, path, inc);
inc->depth--;
}
+cleanup:
strbuf_release(&buf);
free(expanded);
return ret;
@@ -426,7 +428,7 @@ static inline int iskeychar(int c)
* baselen - pointer to size_t which will hold the length of the
* section + subsection part, can be NULL
*/
-static int git_config_parse_key_1(const char *key, char **store_key, size_t *baselen_, int quiet)
+int git_config_parse_key(const char *key, char **store_key, size_t *baselen_)
{
size_t i, baselen;
int dot;
@@ -438,14 +440,12 @@ static int git_config_parse_key_1(const char *key, char **store_key, size_t *bas
*/
if (last_dot == NULL || last_dot == key) {
- if (!quiet)
- error(_("key does not contain a section: %s"), key);
+ error(_("key does not contain a section: %s"), key);
return -CONFIG_NO_SECTION_OR_NAME;
}
if (!last_dot[1]) {
- if (!quiet)
- error(_("key does not contain variable name: %s"), key);
+ error(_("key does not contain variable name: %s"), key);
return -CONFIG_NO_SECTION_OR_NAME;
}
@@ -456,8 +456,7 @@ static int git_config_parse_key_1(const char *key, char **store_key, size_t *bas
/*
* Validate the key and while at it, lower case it for matching.
*/
- if (store_key)
- *store_key = xmallocz(strlen(key));
+ *store_key = xmallocz(strlen(key));
dot = 0;
for (i = 0; key[i]; i++) {
@@ -468,39 +467,24 @@ static int git_config_parse_key_1(const char *key, char **store_key, size_t *bas
if (!dot || i > baselen) {
if (!iskeychar(c) ||
(i == baselen + 1 && !isalpha(c))) {
- if (!quiet)
- error(_("invalid key: %s"), key);
+ error(_("invalid key: %s"), key);
goto out_free_ret_1;
}
c = tolower(c);
} else if (c == '\n') {
- if (!quiet)
- error(_("invalid key (newline): %s"), key);
+ error(_("invalid key (newline): %s"), key);
goto out_free_ret_1;
}
- if (store_key)
- (*store_key)[i] = c;
+ (*store_key)[i] = c;
}
return 0;
out_free_ret_1:
- if (store_key) {
- FREE_AND_NULL(*store_key);
- }
+ FREE_AND_NULL(*store_key);
return -CONFIG_INVALID_KEY;
}
-int git_config_parse_key(const char *key, char **store_key, size_t *baselen)
-{
- return git_config_parse_key_1(key, store_key, baselen, 0);
-}
-
-int git_config_key_is_valid(const char *key)
-{
- return !git_config_parse_key_1(key, NULL, NULL, 1);
-}
-
static int config_parse_pair(const char *key, const char *value,
config_fn_t fn, void *data)
{
@@ -1400,8 +1384,6 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
level = Z_DEFAULT_COMPRESSION;
else if (level < 0 || level > Z_BEST_COMPRESSION)
die(_("bad zlib compression level %d"), level);
- core_compression_level = level;
- core_compression_seen = 1;
if (!zlib_compression_seen)
zlib_compression_level = level;
if (!pack_compression_seen)
@@ -1796,6 +1778,7 @@ int git_config_from_mem(config_fn_t fn,
int git_config_from_blob_oid(config_fn_t fn,
const char *name,
+ struct repository *repo,
const struct object_id *oid,
void *data)
{
@@ -1804,7 +1787,7 @@ int git_config_from_blob_oid(config_fn_t fn,
unsigned long size;
int ret;
- buf = read_object_file(oid, &type, &size);
+ buf = repo_read_object_file(repo, oid, &type, &size);
if (!buf)
return error(_("unable to load config blob object '%s'"), name);
if (type != OBJ_BLOB) {
@@ -1820,14 +1803,15 @@ int git_config_from_blob_oid(config_fn_t fn,
}
static int git_config_from_blob_ref(config_fn_t fn,
+ struct repository *repo,
const char *name,
void *data)
{
struct object_id oid;
- if (get_oid(name, &oid) < 0)
+ if (repo_get_oid(repo, name, &oid) < 0)
return error(_("unable to resolve config blob '%s'"), name);
- return git_config_from_blob_oid(fn, name, &oid, data);
+ return git_config_from_blob_oid(fn, name, repo, &oid, data);
}
char *git_system_config(void)
@@ -1958,12 +1942,16 @@ int config_with_options(config_fn_t fn, void *data,
* If we have a specific filename, use it. Otherwise, follow the
* regular lookup sequence.
*/
- if (config_source && config_source->use_stdin)
+ if (config_source && config_source->use_stdin) {
return git_config_from_stdin(fn, data);
- else if (config_source && config_source->file)
+ } else if (config_source && config_source->file) {
return git_config_from_file(fn, config_source->file, data);
- else if (config_source && config_source->blob)
- return git_config_from_blob_ref(fn, config_source->blob, data);
+ } else if (config_source && config_source->blob) {
+ struct repository *repo = config_source->repo ?
+ config_source->repo : the_repository;
+ return git_config_from_blob_ref(fn, repo, config_source->blob,
+ data);
+ }
return do_git_config_sequence(opts, fn, data);
}
diff --git a/config.h b/config.h
index a2200f3..f119de0 100644
--- a/config.h
+++ b/config.h
@@ -49,6 +49,8 @@ const char *config_scope_name(enum config_scope scope);
struct git_config_source {
unsigned int use_stdin:1;
const char *file;
+ /* The repository if blob is not NULL; leave blank for the_repository */
+ struct repository *repo;
const char *blob;
enum config_scope scope;
};
@@ -136,6 +138,7 @@ int git_config_from_mem(config_fn_t fn,
const char *buf, size_t len,
void *data, const struct config_options *opts);
int git_config_from_blob_oid(config_fn_t fn, const char *name,
+ struct repository *repo,
const struct object_id *oid, void *data);
void git_config_push_parameter(const char *text);
void git_config_push_env(const char *spec);
@@ -256,7 +259,6 @@ int git_config_set_gently(const char *, const char *);
void git_config_set(const char *, const char *);
int git_config_parse_key(const char *, char **, size_t *);
-int git_config_key_is_valid(const char *key);
/*
* The following macros specify flag bits that alter the behavior
@@ -606,7 +608,6 @@ int git_config_get_maybe_bool(const char *key, int *dest);
int git_config_get_pathname(const char *key, const char **dest);
int git_config_get_index_threads(int *dest);
-int git_config_get_untracked_cache(void);
int git_config_get_split_index(void);
int git_config_get_max_percent_split_change(void);
int git_config_get_fsmonitor(void);
diff --git a/config.mak.dev b/config.mak.dev
index 022fb58..7673fed 100644
--- a/config.mak.dev
+++ b/config.mak.dev
@@ -1,13 +1,24 @@
+ifndef COMPILER_FEATURES
+COMPILER_FEATURES := $(shell ./detect-compiler $(CC))
+endif
+
ifeq ($(filter no-error,$(DEVOPTS)),)
DEVELOPER_CFLAGS += -Werror
SPARSE_FLAGS += -Wsparse-error
endif
-ifneq ($(filter pedantic,$(DEVOPTS)),)
+
+DEVELOPER_CFLAGS += -Wall
+ifeq ($(filter no-pedantic,$(DEVOPTS)),)
DEVELOPER_CFLAGS += -pedantic
-# don't warn for each N_ use
-DEVELOPER_CFLAGS += -DUSE_PARENS_AROUND_GETTEXT_N=0
+ifneq (($or $(filter gcc5,$(COMPILER_FEATURES)),$(filter clang4,$(COMPILER_FEATURES))),)
+DEVELOPER_CFLAGS += -Wpedantic
+ifneq ($(filter gcc10,$(COMPILER_FEATURES)),)
+ifeq ($(uname_S),MINGW)
+DEVELOPER_CFLAGS += -Wno-pedantic-ms-format
+endif
+endif
+endif
endif
-DEVELOPER_CFLAGS += -Wall
DEVELOPER_CFLAGS += -Wdeclaration-after-statement
DEVELOPER_CFLAGS += -Wformat-security
DEVELOPER_CFLAGS += -Wold-style-definition
@@ -18,10 +29,6 @@ DEVELOPER_CFLAGS += -Wunused
DEVELOPER_CFLAGS += -Wvla
DEVELOPER_CFLAGS += -fno-common
-ifndef COMPILER_FEATURES
-COMPILER_FEATURES := $(shell ./detect-compiler $(CC))
-endif
-
ifneq ($(filter clang4,$(COMPILER_FEATURES)),)
DEVELOPER_CFLAGS += -Wtautological-constant-out-of-range-compare
endif
diff --git a/config.mak.uname b/config.mak.uname
index 76516aa..d0701f9 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -11,6 +11,10 @@ uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not')
uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not')
uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not')
+ifneq ($(findstring MINGW,$(uname_S)),)
+ uname_S := MINGW
+endif
+
ifdef MSVC
# avoid the MingW and Cygwin configuration sections
uname_S := Windows
@@ -54,7 +58,6 @@ ifeq ($(uname_S),Linux)
# -lrt is needed for clock_gettime on glibc <= 2.16
NEEDS_LIBRT = YesPlease
HAVE_GETDELIM = YesPlease
- SANE_TEXT_GREP=-a
FREAD_READS_DIRECTORIES = UnfortunatelyYes
BASIC_CFLAGS += -DHAVE_SYSINFO
PROCFS_EXECUTABLE_PATH = /proc/self/exe
@@ -588,7 +591,7 @@ ifeq ($(uname_S),NONSTOP_KERNEL)
SANE_TOOL_PATH = /usr/coreutils/bin:/usr/local/bin
SHELL_PATH = /usr/coreutils/bin/bash
endif
-ifneq (,$(findstring MINGW,$(uname_S)))
+ifeq ($(uname_S),MINGW)
pathsep = ;
HAVE_ALLOCA_H = YesPlease
NO_PREAD = YesPlease
@@ -735,9 +738,9 @@ vcxproj:
echo '</Project>') >git-remote-http/LinkOrCopyRemoteHttp.targets
git add -f git/LinkOrCopyBuiltins.targets git-remote-http/LinkOrCopyRemoteHttp.targets
- # Add command-list.h and config-list.h
- $(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 config-list.h command-list.h
- git add -f config-list.h command-list.h
+ # Add generated headers
+ $(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 $(GENERATED_H)
+ git add -f $(GENERATED_H)
# Add scripts
rm -f perl/perl.mak
diff --git a/configure.ac b/configure.ac
index 031e8d3..5ee25ec 100644
--- a/configure.ac
+++ b/configure.ac
@@ -507,14 +507,6 @@ if test -n "$ASCIIDOC"; then
esac
fi
-if grep -a ascii configure.ac >/dev/null; then
- AC_MSG_RESULT([Using 'grep -a' for sane_grep])
- SANE_TEXT_GREP=-a
-else
- SANE_TEXT_GREP=
-fi
-GIT_CONF_SUBST([SANE_TEXT_GREP])
-
## Checks for libraries.
AC_MSG_NOTICE([CHECKS for libraries])
#
diff --git a/connect.c b/connect.c
index aff13a2..eaf7d6d 100644
--- a/connect.c
+++ b/connect.c
@@ -557,6 +557,8 @@ const char *parse_feature_value(const char *feature_list, const char *feature, i
if (!*value || isspace(*value)) {
if (lenp)
*lenp = 0;
+ if (offset)
+ *offset = found + len - feature_list;
return value;
}
/* feature with a value (e.g., "agent=git/1.2.3") */
diff --git a/connected.c b/connected.c
index b5f9523..35bd4a2 100644
--- a/connected.c
+++ b/connected.c
@@ -24,7 +24,7 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
struct child_process rev_list = CHILD_PROCESS_INIT;
FILE *rev_list_in;
struct check_connected_options defaults = CHECK_CONNECTED_INIT;
- struct object_id oid;
+ const struct object_id *oid;
int err = 0;
struct packed_git *new_pack = NULL;
struct transport *transport;
@@ -34,7 +34,8 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
opt = &defaults;
transport = opt->transport;
- if (fn(cb_data, &oid)) {
+ oid = fn(cb_data);
+ if (!oid) {
if (opt->err_fd)
close(opt->err_fd);
return err;
@@ -73,7 +74,7 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
for (p = get_all_packs(the_repository); p; p = p->next) {
if (!p->pack_promisor)
continue;
- if (find_pack_entry_one(oid.hash, p))
+ if (find_pack_entry_one(oid->hash, p))
goto promisor_pack_found;
}
/*
@@ -83,7 +84,7 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
goto no_promisor_pack_found;
promisor_pack_found:
;
- } while (!fn(cb_data, &oid));
+ } while ((oid = fn(cb_data)) != NULL);
return 0;
}
@@ -106,7 +107,6 @@ no_promisor_pack_found:
if (opt->progress)
strvec_pushf(&rev_list.args, "--progress=%s",
_("Checking connectivity"));
- strvec_push(&rev_list.args, "--unsorted-input");
rev_list.git_cmd = 1;
rev_list.env = opt->env;
@@ -133,12 +133,12 @@ no_promisor_pack_found:
* are sure the ref is good and not sending it to
* rev-list for verification.
*/
- if (new_pack && find_pack_entry_one(oid.hash, new_pack))
+ if (new_pack && find_pack_entry_one(oid->hash, new_pack))
continue;
- if (fprintf(rev_list_in, "%s\n", oid_to_hex(&oid)) < 0)
+ if (fprintf(rev_list_in, "%s\n", oid_to_hex(oid)) < 0)
break;
- } while (!fn(cb_data, &oid));
+ } while ((oid = fn(cb_data)) != NULL);
if (ferror(rev_list_in) || fflush(rev_list_in)) {
if (errno != EPIPE && errno != EINVAL)
diff --git a/connected.h b/connected.h
index 8d5a6b3..6e59c92 100644
--- a/connected.h
+++ b/connected.h
@@ -9,7 +9,7 @@ struct transport;
* When called after returning the name for the last object, return -1
* to signal EOF, otherwise return 0.
*/
-typedef int (*oid_iterate_fn)(void *, struct object_id *oid);
+typedef const struct object_id *(*oid_iterate_fn)(void *);
/*
* Named-arguments struct for check_connected. All arguments are
diff --git a/contrib/buildsystems/CMakeLists.txt b/contrib/buildsystems/CMakeLists.txt
index 171b412..86b4611 100644
--- a/contrib/buildsystems/CMakeLists.txt
+++ b/contrib/buildsystems/CMakeLists.txt
@@ -624,6 +624,13 @@ if(NOT EXISTS ${CMAKE_BINARY_DIR}/config-list.h)
OUTPUT_FILE ${CMAKE_BINARY_DIR}/config-list.h)
endif()
+if(NOT EXISTS ${CMAKE_BINARY_DIR}/hook-list.h)
+ message("Generating hook-list.h")
+ execute_process(COMMAND ${SH_EXE} ${CMAKE_SOURCE_DIR}/generate-hooklist.sh
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ OUTPUT_FILE ${CMAKE_BINARY_DIR}/hook-list.h)
+endif()
+
include_directories(${CMAKE_BINARY_DIR})
#build
@@ -774,7 +781,6 @@ foreach(script ${git_shell_scripts})
string(REPLACE "@@USE_GETTEXT_SCHEME@@" "" content "${content}")
string(REPLACE "# @@BROKEN_PATH_FIX@@" "" content "${content}")
string(REPLACE "@@PERL@@" "${PERL_PATH}" content "${content}")
- string(REPLACE "@@SANE_TEXT_GREP@@" "-a" content "${content}")
string(REPLACE "@@PAGER_ENV@@" "LESS=FRX LV=-c" content "${content}")
file(WRITE ${CMAKE_BINARY_DIR}/${script} ${content})
endforeach()
diff --git a/contrib/coccinelle/xopen.cocci b/contrib/coccinelle/xopen.cocci
index 814d7b8..b71db67 100644
--- a/contrib/coccinelle/xopen.cocci
+++ b/contrib/coccinelle/xopen.cocci
@@ -2,15 +2,18 @@
identifier fd;
identifier die_fn =~ "^(die|die_errno)$";
@@
-(
- fd =
+ int fd =
- open
+ xopen
(...);
-|
- int fd =
+- if ( \( fd < 0 \| fd == -1 \) ) { die_fn(...); }
+
+@@
+expression fd;
+identifier die_fn =~ "^(die|die_errno)$";
+@@
+ fd =
- open
+ xopen
(...);
-)
- if ( \( fd < 0 \| fd == -1 \) ) { die_fn(...); }
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 8108eda..c82ccae 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -515,7 +515,7 @@ __gitcomp_file ()
# argument, and using the options specified in the second argument.
__git_ls_files_helper ()
{
- if [ "$2" == "--committable" ]; then
+ if [ "$2" = "--committable" ]; then
__git -C "$1" -c core.quotePath=false diff-index \
--name-only --relative HEAD -- "${3//\\/\\\\}*"
else
@@ -2359,16 +2359,7 @@ _git_send_email ()
return
;;
--*)
- __gitcomp_builtin send-email "--annotate --bcc --cc --cc-cmd --chain-reply-to
- --compose --confirm= --dry-run --envelope-sender
- --from --identity
- --in-reply-to --no-chain-reply-to --no-signed-off-by-cc
- --no-suppress-from --no-thread --quiet --reply-to
- --signed-off-by-cc --smtp-pass --smtp-server
- --smtp-server-port --smtp-encryption= --smtp-user
- --subject --suppress-cc= --suppress-from --thread --to
- --validate --no-validate
- $__git_format_patch_extra_options"
+ __gitcomp_builtin send-email "$__git_format_patch_extra_options"
return
;;
esac
@@ -2503,7 +2494,14 @@ __git_config_vars=
__git_compute_config_vars ()
{
test -n "$__git_config_vars" ||
- __git_config_vars="$(git help --config-for-completion | sort -u)"
+ __git_config_vars="$(git help --config-for-completion)"
+}
+
+__git_config_sections=
+__git_compute_config_sections ()
+{
+ test -n "$__git_config_sections" ||
+ __git_config_sections="$(git help --config-sections-for-completion)"
}
# Completes possible values of various configuration variables.
@@ -2543,7 +2541,7 @@ __git_complete_config_variable_value ()
return
;;
branch.*.rebase)
- __gitcomp "false true merges preserve interactive" "" "$cur_"
+ __gitcomp "false true merges interactive" "" "$cur_"
return
;;
remote.pushdefault)
@@ -2717,16 +2715,8 @@ __git_complete_config_variable_name ()
__gitcomp "$__git_config_vars" "" "$cur_" "$sfx"
;;
*)
- __git_compute_config_vars
- __gitcomp "$(echo "$__git_config_vars" |
- awk -F . '{
- sections[$1] = 1
- }
- END {
- for (s in sections)
- print s "."
- }
- ')" "" "$cur_"
+ __git_compute_config_sections
+ __gitcomp "$__git_config_sections" "" "$cur_" "."
;;
esac
}
diff --git a/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c b/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
index d389bfa..5927e27 100644
--- a/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
+++ b/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
@@ -138,7 +138,7 @@ struct credential {
char *password;
};
-#define CREDENTIAL_INIT { NULL, NULL, 0, NULL, NULL, NULL }
+#define CREDENTIAL_INIT { 0 }
typedef int (*credential_op_cb)(struct credential *);
diff --git a/contrib/credential/libsecret/git-credential-libsecret.c b/contrib/credential/libsecret/git-credential-libsecret.c
index e6598b6..2c5d76d 100644
--- a/contrib/credential/libsecret/git-credential-libsecret.c
+++ b/contrib/credential/libsecret/git-credential-libsecret.c
@@ -41,7 +41,7 @@ struct credential {
char *password;
};
-#define CREDENTIAL_INIT { NULL, NULL, 0, NULL, NULL, NULL }
+#define CREDENTIAL_INIT { 0 }
typedef int (*credential_op_cb)(struct credential *);
diff --git a/contrib/rerere-train.sh b/contrib/rerere-train.sh
index eeee45d..75125d6 100755
--- a/contrib/rerere-train.sh
+++ b/contrib/rerere-train.sh
@@ -91,7 +91,7 @@ do
git checkout -q $commit -- .
git rerere
fi
- git reset -q --hard
+ git reset -q --hard # Might nuke untracked files...
done
if test -z "$branch"
diff --git a/convert.c b/convert.c
index 0d6fb34..df7186b 100644
--- a/convert.c
+++ b/convert.c
@@ -613,7 +613,7 @@ static int crlf_to_worktree(const char *src, size_t len, struct strbuf *buf,
struct filter_params {
const char *src;
- unsigned long size;
+ size_t size;
int fd;
const char *cmd;
const char *path;
diff --git a/credential.c b/credential.c
index 000ac7a..e7240f3 100644
--- a/credential.c
+++ b/credential.c
@@ -105,7 +105,7 @@ static int match_partial_url(const char *url, void *cb)
static void credential_apply_config(struct credential *c)
{
char *normalized_url;
- struct urlmatch_config config = { STRING_LIST_INIT_DUP };
+ struct urlmatch_config config = URLMATCH_CONFIG_INIT;
struct strbuf url = STRBUF_INIT;
if (!c->host)
diff --git a/daemon.c b/daemon.c
index 5c4cbad..b1fcbe0 100644
--- a/daemon.c
+++ b/daemon.c
@@ -63,6 +63,12 @@ struct hostinfo {
unsigned int hostname_lookup_done:1;
unsigned int saw_extended_args:1;
};
+#define HOSTINFO_INIT { \
+ .hostname = STRBUF_INIT, \
+ .canon_hostname = STRBUF_INIT, \
+ .ip_address = STRBUF_INIT, \
+ .tcp_port = STRBUF_INIT, \
+}
static void lookup_hostname(struct hostinfo *hi);
@@ -727,15 +733,6 @@ static void lookup_hostname(struct hostinfo *hi)
}
}
-static void hostinfo_init(struct hostinfo *hi)
-{
- memset(hi, 0, sizeof(*hi));
- strbuf_init(&hi->hostname, 0);
- strbuf_init(&hi->canon_hostname, 0);
- strbuf_init(&hi->ip_address, 0);
- strbuf_init(&hi->tcp_port, 0);
-}
-
static void hostinfo_clear(struct hostinfo *hi)
{
strbuf_release(&hi->hostname);
@@ -760,17 +757,15 @@ static int execute(void)
char *line = packet_buffer;
int pktlen, len, i;
char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT");
- struct hostinfo hi;
+ struct hostinfo hi = HOSTINFO_INIT;
struct strvec env = STRVEC_INIT;
- hostinfo_init(&hi);
-
if (addr)
loginfo("Connection from %s:%s", addr, port);
set_keep_alive(0);
alarm(init_timeout ? init_timeout : timeout);
- pktlen = packet_read(0, NULL, NULL, packet_buffer, sizeof(packet_buffer), 0);
+ pktlen = packet_read(0, packet_buffer, sizeof(packet_buffer), 0);
alarm(0);
len = strlen(line);
diff --git a/delta.h b/delta.h
index 2df5fe1..8a56ec0 100644
--- a/delta.h
+++ b/delta.h
@@ -90,15 +90,15 @@ static inline unsigned long get_delta_hdr_size(const unsigned char **datap,
const unsigned char *top)
{
const unsigned char *data = *datap;
- unsigned long cmd, size = 0;
+ size_t cmd, size = 0;
int i = 0;
do {
cmd = *data++;
- size |= (cmd & 0x7f) << i;
+ size |= st_left_shift(cmd & 0x7f, i);
i += 7;
} while (cmd & 0x80 && data < top);
*datap = data;
- return size;
+ return cast_size_t_to_ulong(size);
}
#endif
diff --git a/diff.c b/diff.c
index a8113f1..861282d 100644
--- a/diff.c
+++ b/diff.c
@@ -26,6 +26,7 @@
#include "parse-options.h"
#include "help.h"
#include "promisor-remote.h"
+#include "dir.h"
#ifdef NO_FAST_WORKING_DIRECTORY
#define FAST_WORKING_DIRECTORY 0
@@ -774,13 +775,13 @@ struct emitted_diff_symbol {
int indent_width; /* The visual width of the indentation */
enum diff_symbol s;
};
-#define EMITTED_DIFF_SYMBOL_INIT {NULL}
+#define EMITTED_DIFF_SYMBOL_INIT { 0 }
struct emitted_diff_symbols {
struct emitted_diff_symbol *buf;
int nr, alloc;
};
-#define EMITTED_DIFF_SYMBOLS_INIT {NULL, 0, 0}
+#define EMITTED_DIFF_SYMBOLS_INIT { 0 }
static void append_emitted_diff_symbol(struct diff_options *o,
struct emitted_diff_symbol *e)
@@ -3907,6 +3908,13 @@ static int reuse_worktree_file(struct index_state *istate,
if (!want_file && would_convert_to_git(istate, name))
return 0;
+ /*
+ * If this path does not match our sparse-checkout definition,
+ * then the file will not be in the working directory.
+ */
+ if (!path_in_sparse_checkout(name, istate))
+ return 0;
+
len = strlen(name);
pos = index_name_pos(istate, name, len);
if (pos < 0)
diff --git a/dir.c b/dir.c
index 03c4d21..5aa6fba 100644
--- a/dir.c
+++ b/dir.c
@@ -1439,6 +1439,75 @@ done:
return result;
}
+int init_sparse_checkout_patterns(struct index_state *istate)
+{
+ if (!core_apply_sparse_checkout)
+ return 1;
+ if (istate->sparse_checkout_patterns)
+ return 0;
+
+ CALLOC_ARRAY(istate->sparse_checkout_patterns, 1);
+
+ if (get_sparse_checkout_patterns(istate->sparse_checkout_patterns) < 0) {
+ FREE_AND_NULL(istate->sparse_checkout_patterns);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int path_in_sparse_checkout_1(const char *path,
+ struct index_state *istate,
+ int require_cone_mode)
+{
+ int dtype = DT_REG;
+ enum pattern_match_result match = UNDECIDED;
+ const char *end, *slash;
+
+ /*
+ * We default to accepting a path if there are no patterns or
+ * they are of the wrong type.
+ */
+ if (init_sparse_checkout_patterns(istate) ||
+ (require_cone_mode &&
+ !istate->sparse_checkout_patterns->use_cone_patterns))
+ return 1;
+
+ /*
+ * If UNDECIDED, use the match from the parent dir (recursively), or
+ * fall back to NOT_MATCHED at the topmost level. Note that cone mode
+ * never returns UNDECIDED, so we will execute only one iteration in
+ * this case.
+ */
+ for (end = path + strlen(path);
+ end > path && match == UNDECIDED;
+ end = slash) {
+
+ for (slash = end - 1; slash > path && *slash != '/'; slash--)
+ ; /* do nothing */
+
+ match = path_matches_pattern_list(path, end - path,
+ slash > path ? slash + 1 : path, &dtype,
+ istate->sparse_checkout_patterns, istate);
+
+ /* We are going to match the parent dir now */
+ dtype = DT_DIR;
+ }
+ return match > 0;
+}
+
+int path_in_sparse_checkout(const char *path,
+ struct index_state *istate)
+{
+ return path_in_sparse_checkout_1(path, istate, 0);
+}
+
+int path_in_cone_mode_sparse_checkout(const char *path,
+ struct index_state *istate)
+{
+ return path_in_sparse_checkout_1(path, istate, 1);
+}
+
static struct path_pattern *last_matching_pattern_from_lists(
struct dir_struct *dir, struct index_state *istate,
const char *pathname, int pathlen,
@@ -2970,6 +3039,120 @@ int is_empty_dir(const char *path)
return ret;
}
+char *git_url_basename(const char *repo, int is_bundle, int is_bare)
+{
+ const char *end = repo + strlen(repo), *start, *ptr;
+ size_t len;
+ char *dir;
+
+ /*
+ * Skip scheme.
+ */
+ start = strstr(repo, "://");
+ if (start == NULL)
+ start = repo;
+ else
+ start += 3;
+
+ /*
+ * Skip authentication data. The stripping does happen
+ * greedily, such that we strip up to the last '@' inside
+ * the host part.
+ */
+ for (ptr = start; ptr < end && !is_dir_sep(*ptr); ptr++) {
+ if (*ptr == '@')
+ start = ptr + 1;
+ }
+
+ /*
+ * Strip trailing spaces, slashes and /.git
+ */
+ while (start < end && (is_dir_sep(end[-1]) || isspace(end[-1])))
+ end--;
+ if (end - start > 5 && is_dir_sep(end[-5]) &&
+ !strncmp(end - 4, ".git", 4)) {
+ end -= 5;
+ while (start < end && is_dir_sep(end[-1]))
+ end--;
+ }
+
+ /*
+ * Strip trailing port number if we've got only a
+ * hostname (that is, there is no dir separator but a
+ * colon). This check is required such that we do not
+ * strip URI's like '/foo/bar:2222.git', which should
+ * result in a dir '2222' being guessed due to backwards
+ * compatibility.
+ */
+ if (memchr(start, '/', end - start) == NULL
+ && memchr(start, ':', end - start) != NULL) {
+ ptr = end;
+ while (start < ptr && isdigit(ptr[-1]) && ptr[-1] != ':')
+ ptr--;
+ if (start < ptr && ptr[-1] == ':')
+ end = ptr - 1;
+ }
+
+ /*
+ * Find last component. To remain backwards compatible we
+ * also regard colons as path separators, such that
+ * cloning a repository 'foo:bar.git' would result in a
+ * directory 'bar' being guessed.
+ */
+ ptr = end;
+ while (start < ptr && !is_dir_sep(ptr[-1]) && ptr[-1] != ':')
+ ptr--;
+ start = ptr;
+
+ /*
+ * Strip .{bundle,git}.
+ */
+ len = end - start;
+ strip_suffix_mem(start, &len, is_bundle ? ".bundle" : ".git");
+
+ if (!len || (len == 1 && *start == '/'))
+ die(_("No directory name could be guessed.\n"
+ "Please specify a directory on the command line"));
+
+ if (is_bare)
+ dir = xstrfmt("%.*s.git", (int)len, start);
+ else
+ dir = xstrndup(start, len);
+ /*
+ * Replace sequences of 'control' characters and whitespace
+ * with one ascii space, remove leading and trailing spaces.
+ */
+ if (*dir) {
+ char *out = dir;
+ int prev_space = 1 /* strip leading whitespace */;
+ for (end = dir; *end; ++end) {
+ char ch = *end;
+ if ((unsigned char)ch < '\x20')
+ ch = '\x20';
+ if (isspace(ch)) {
+ if (prev_space)
+ continue;
+ prev_space = 1;
+ } else
+ prev_space = 0;
+ *out++ = ch;
+ }
+ *out = '\0';
+ if (out > dir && prev_space)
+ out[-1] = '\0';
+ }
+ return dir;
+}
+
+void strip_dir_trailing_slashes(char *dir)
+{
+ char *end = dir + strlen(dir);
+
+ while (dir < end - 1 && is_dir_sep(end[-1]))
+ end--;
+ *end = '\0';
+}
+
static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
{
DIR *dir;
@@ -3633,7 +3816,7 @@ static void connect_wt_gitdir_in_nested(const char *sub_worktree,
strbuf_reset(&sub_wt);
strbuf_reset(&sub_gd);
strbuf_addf(&sub_wt, "%s/%s", sub_worktree, sub->path);
- strbuf_addf(&sub_gd, "%s/modules/%s", sub_gitdir, sub->name);
+ submodule_name_to_gitdir(&sub_gd, &subrepo, sub->name);
connect_work_tree_and_git_dir(sub_wt.buf, sub_gd.buf, 1);
}
diff --git a/dir.h b/dir.h
index b3e1a54a..83f46c0 100644
--- a/dir.h
+++ b/dir.h
@@ -394,6 +394,14 @@ enum pattern_match_result path_matches_pattern_list(const char *pathname,
const char *basename, int *dtype,
struct pattern_list *pl,
struct index_state *istate);
+
+int init_sparse_checkout_patterns(struct index_state *state);
+
+int path_in_sparse_checkout(const char *path,
+ struct index_state *istate);
+int path_in_cone_mode_sparse_checkout(const char *path,
+ struct index_state *istate);
+
struct dir_entry *dir_add_ignored(struct dir_struct *dir,
struct index_state *istate,
const char *pathname, int len);
@@ -453,6 +461,17 @@ static inline int is_dot_or_dotdot(const char *name)
int is_empty_dir(const char *dir);
+/*
+ * Retrieve the "humanish" basename of the given Git URL.
+ *
+ * For example:
+ * /path/to/repo.git => "repo"
+ * host.xz:foo/.git => "foo"
+ * http://example.com/user/bar.baz => "bar.baz"
+ */
+char *git_url_basename(const char *repo, int is_bundle, int is_bare);
+void strip_dir_trailing_slashes(char *dir);
+
void setup_standard_excludes(struct dir_struct *dir);
char *get_sparse_checkout_filename(void);
diff --git a/entry.c b/entry.c
index 044e8ec..1c9df62 100644
--- a/entry.c
+++ b/entry.c
@@ -82,11 +82,13 @@ static int create_file(const char *path, unsigned int mode)
return open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
}
-void *read_blob_entry(const struct cache_entry *ce, unsigned long *size)
+void *read_blob_entry(const struct cache_entry *ce, size_t *size)
{
enum object_type type;
- void *blob_data = read_object_file(&ce->oid, &type, size);
+ unsigned long ul;
+ void *blob_data = read_object_file(&ce->oid, &type, &ul);
+ *size = ul;
if (blob_data) {
if (type == OBJ_BLOB)
return blob_data;
@@ -163,24 +165,21 @@ int finish_delayed_checkout(struct checkout *state, int *nr_checkouts,
int show_progress)
{
int errs = 0;
- unsigned delayed_object_count;
+ unsigned processed_paths = 0;
off_t filtered_bytes = 0;
struct string_list_item *filter, *path;
- struct progress *progress;
+ struct progress *progress = NULL;
struct delayed_checkout *dco = state->delayed_checkout;
if (!state->delayed_checkout)
return errs;
dco->state = CE_RETRY;
- delayed_object_count = dco->paths.nr;
- progress = show_progress
- ? start_delayed_progress(_("Filtering content"), delayed_object_count)
- : NULL;
+ if (show_progress)
+ progress = start_delayed_progress(_("Filtering content"), dco->paths.nr);
while (dco->filters.nr > 0) {
for_each_string_list_item(filter, &dco->filters) {
struct string_list available_paths = STRING_LIST_INIT_NODUP;
- display_progress(progress, delayed_object_count - dco->paths.nr);
if (!async_query_available_blobs(filter->string, &available_paths)) {
/* Filter reported an error */
@@ -227,6 +226,7 @@ int finish_delayed_checkout(struct checkout *state, int *nr_checkouts,
ce = index_file_exists(state->istate, path->string,
strlen(path->string), 0);
if (ce) {
+ display_progress(progress, ++processed_paths);
errs |= checkout_entry(ce, state, NULL, nr_checkouts);
filtered_bytes += ce->ce_stat_data.sd_size;
display_throughput(progress, filtered_bytes);
@@ -273,7 +273,7 @@ static int write_entry(struct cache_entry *ce, char *path, struct conv_attrs *ca
int fd, ret, fstat_done = 0;
char *new_blob;
struct strbuf buf = STRBUF_INIT;
- unsigned long size;
+ size_t size;
ssize_t wrote;
size_t newsize = 0;
struct stat st;
diff --git a/entry.h b/entry.h
index 7c889e5..252fd24 100644
--- a/entry.h
+++ b/entry.h
@@ -16,7 +16,7 @@ struct checkout {
clone:1,
refresh_cache:1;
};
-#define CHECKOUT_INIT { NULL, "" }
+#define CHECKOUT_INIT { .base_dir = "" }
#define TEMPORARY_FILENAME_LENGTH 25
/*
@@ -52,7 +52,7 @@ int finish_delayed_checkout(struct checkout *state, int *nr_checkouts,
*/
void unlink_entry(const struct cache_entry *ce);
-void *read_blob_entry(const struct cache_entry *ce, unsigned long *size);
+void *read_blob_entry(const struct cache_entry *ce, size_t *size);
int fstat_checkout_output(int fd, const struct checkout *state, struct stat *st);
void update_ce_after_write(const struct checkout *state, struct cache_entry *ce,
struct stat *st);
diff --git a/environment.c b/environment.c
index d6b22ed..9da7f3c 100644
--- a/environment.c
+++ b/environment.c
@@ -31,7 +31,6 @@ int prefer_symlink_refs;
int is_bare_repository_cfg = -1; /* unspecified */
int warn_ambiguous_refs = 1;
int warn_on_object_refname_ambiguity = 1;
-int ref_paranoia = -1;
int repository_format_precious_objects;
int repository_format_worktree_config;
const char *git_commit_encoding;
@@ -41,7 +40,6 @@ char *apply_default_ignorewhitespace;
const char *git_attributes_file;
const char *git_hooks_path;
int zlib_compression_level = Z_BEST_SPEED;
-int core_compression_level;
int pack_compression_level = Z_DEFAULT_COMPRESSION;
int fsync_object_files;
size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
@@ -96,13 +94,6 @@ int auto_comment_line_char;
/* Parallel index stat data preload? */
int core_preload_index = 1;
-/*
- * This is a hack for test programs like test-dump-untracked-cache to
- * ensure that they do not modify the untracked cache when reading it.
- * Do not use it otherwise!
- */
-int ignore_untracked_cache_config;
-
/* This is set by setup_git_dir_gently() and/or git_default_config() */
char *git_work_tree_cfg;
@@ -330,8 +321,7 @@ char *get_graft_file(struct repository *r)
static void set_git_dir_1(const char *path)
{
- if (setenv(GIT_DIR_ENVIRONMENT, path, 1))
- die(_("could not set GIT_DIR to '%s'"), path);
+ xsetenv(GIT_DIR_ENVIRONMENT, path, 1);
setup_git_env(path);
}
diff --git a/fetch-negotiator.c b/fetch-negotiator.c
index 57ed578..2733902 100644
--- a/fetch-negotiator.c
+++ b/fetch-negotiator.c
@@ -19,7 +19,6 @@ void fetch_negotiator_init(struct repository *r,
return;
case FETCH_NEGOTIATION_DEFAULT:
- default:
default_negotiator_init(negotiator);
return;
}
diff --git a/fetch-pack.c b/fetch-pack.c
index 0bf7ed7..a9604f3 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -119,6 +119,11 @@ static struct commit *deref_without_lazy_fetch(const struct object_id *oid,
{
enum object_type type;
struct object_info info = { .typep = &type };
+ struct commit *commit;
+
+ commit = lookup_commit_in_graph(the_repository, oid);
+ if (commit)
+ return commit;
while (1) {
if (oid_object_info_extended(the_repository, oid, &info,
@@ -1912,16 +1917,15 @@ static void update_shallow(struct fetch_pack_args *args,
oid_array_clear(&ref);
}
-static int iterate_ref_map(void *cb_data, struct object_id *oid)
+static const struct object_id *iterate_ref_map(void *cb_data)
{
struct ref **rm = cb_data;
struct ref *ref = *rm;
if (!ref)
- return -1; /* end of the list */
+ return NULL;
*rm = ref->next;
- oidcpy(oid, &ref->old_oid);
- return 0;
+ return &ref->old_oid;
}
struct ref *fetch_pack(struct fetch_pack_args *args,
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index b969dc6..5216191 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -9,6 +9,7 @@
#include "branch.h"
#include "fmt-merge-msg.h"
#include "commit-reach.h"
+#include "gpg-interface.h"
static int use_branch_desc;
static int suppress_dest_pattern_seen;
@@ -16,6 +17,8 @@ static struct string_list suppress_dest_patterns = STRING_LIST_INIT_DUP;
int fmt_merge_msg_config(const char *key, const char *value, void *cb)
{
+ int status = 0;
+
if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
int is_bool;
merge_log_config = git_config_bool_or_int(key, value, &is_bool);
@@ -34,6 +37,9 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb)
string_list_append(&suppress_dest_patterns, value);
suppress_dest_pattern_seen = 1;
} else {
+ status = git_gpg_config(key, value, NULL);
+ if (status)
+ return status;
return git_default_config(key, value, cb);
}
return 0;
@@ -528,11 +534,11 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
buf = payload.buf;
len = payload.len;
if (check_signature(payload.buf, payload.len, sig.buf,
- sig.len, &sigc) &&
- !sigc.gpg_output)
+ sig.len, &sigc) &&
+ !sigc.output)
strbuf_addstr(&sig, "gpg verification failed.\n");
else
- strbuf_addstr(&sig, sigc.gpg_output);
+ strbuf_addstr(&sig, sigc.output);
}
signature_check_clear(&sigc);
diff --git a/generate-hooklist.sh b/generate-hooklist.sh
new file mode 100755
index 0000000..2f9f54e
--- /dev/null
+++ b/generate-hooklist.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+# Usage: ./generate-hooklist.sh >hook-list.h
+
+cat <<EOF
+/* Automatically generated by generate-hooklist.sh */
+
+static const char *hook_name_list[] = {
+EOF
+
+sed -n \
+ -e '/^~~~~*$/ {x; s/^.*$/ "&",/; p;}' \
+ -e 'x' \
+ <Documentation/githooks.txt |
+ LC_ALL=C sort
+
+cat <<EOF
+ NULL,
+};
+EOF
diff --git a/gettext.h b/gettext.h
index c8b34fd..d209911 100644
--- a/gettext.h
+++ b/gettext.h
@@ -55,31 +55,7 @@ const char *Q_(const char *msgid, const char *plu, unsigned long n)
}
/* Mark msgid for translation but do not translate it. */
-#if !USE_PARENS_AROUND_GETTEXT_N
#define N_(msgid) msgid
-#else
-/*
- * Strictly speaking, this will lead to invalid C when
- * used this way:
- * static const char s[] = N_("FOO");
- * which will expand to
- * static const char s[] = ("FOO");
- * and in valid C, the initializer on the right hand side must
- * be without the parentheses. But many compilers do accept it
- * as a language extension and it will allow us to catch mistakes
- * like:
- * static const char *msgs[] = {
- * N_("one")
- * N_("two"),
- * N_("three"),
- * NULL
- * };
- * (notice the missing comma on one of the lines) by forcing
- * a compilation error, because parenthesised ("one") ("two")
- * will not get silently turned into ("onetwo").
- */
-#define N_(msgid) (msgid)
-#endif
const char *get_preferred_languages(void);
int is_utf8_locale(void);
diff --git a/git-bisect.sh b/git-bisect.sh
index 6a7afae..405cf76 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -34,94 +34,9 @@ Please use "git help bisect" to get the full man page.'
OPTIONS_SPEC=
. git-sh-setup
-_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
-_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
TERM_BAD=bad
TERM_GOOD=good
-bisect_visualize() {
- git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
-
- if test $# = 0
- then
- if test -n "${DISPLAY+set}${SESSIONNAME+set}${MSYSTEM+set}${SECURITYSESSIONID+set}" &&
- type gitk >/dev/null 2>&1
- then
- set gitk
- else
- set git log
- fi
- else
- case "$1" in
- git*|tig) ;;
- -*) set git log "$@" ;;
- *) set git "$@" ;;
- esac
- fi