summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--.mailmap1
-rw-r--r--CODE_OF_CONDUCT.md93
-rw-r--r--Documentation/Makefile8
-rw-r--r--Documentation/MyFirstContribution.txt10
-rw-r--r--Documentation/MyFirstObjectWalk.txt906
-rw-r--r--Documentation/RelNotes/2.24.0.txt398
-rw-r--r--Documentation/RelNotes/2.7.1.txt2
-rw-r--r--Documentation/RelNotes/2.8.0.txt2
-rw-r--r--Documentation/SubmittingPatches4
-rw-r--r--Documentation/asciidoc.conf6
-rw-r--r--Documentation/asciidoctor-extensions.rb24
-rw-r--r--Documentation/config.txt86
-rw-r--r--Documentation/config/core.txt6
-rw-r--r--Documentation/config/diff.txt2
-rw-r--r--Documentation/config/feature.txt37
-rw-r--r--Documentation/config/fetch.txt23
-rw-r--r--Documentation/config/format.txt9
-rw-r--r--Documentation/config/gc.txt2
-rw-r--r--Documentation/config/index.txt1
-rw-r--r--Documentation/config/pack.txt3
-rw-r--r--Documentation/config/remote.txt8
-rw-r--r--Documentation/config/trace2.txt6
-rw-r--r--Documentation/diff-generate-patch.txt32
-rwxr-xr-xDocumentation/doc-diff17
-rw-r--r--Documentation/fetch-options.txt20
-rw-r--r--Documentation/git-am.txt2
-rw-r--r--Documentation/git-clean.txt16
-rw-r--r--Documentation/git-commit-graph.txt7
-rw-r--r--Documentation/git-commit.txt8
-rw-r--r--Documentation/git-config.txt56
-rw-r--r--Documentation/git-fast-export.txt23
-rw-r--r--Documentation/git-fast-import.txt25
-rw-r--r--Documentation/git-filter-branch.txt273
-rw-r--r--Documentation/git-format-patch.txt48
-rw-r--r--Documentation/git-gc.txt17
-rw-r--r--Documentation/git-grep.txt17
-rw-r--r--Documentation/git-gui.txt10
-rw-r--r--Documentation/git-ls-remote.txt32
-rw-r--r--Documentation/git-merge-base.txt98
-rw-r--r--Documentation/git-merge-index.txt26
-rw-r--r--Documentation/git-merge.txt2
-rw-r--r--Documentation/git-multi-pack-index.txt6
-rw-r--r--Documentation/git-notes.txt6
-rw-r--r--Documentation/git-rebase.txt35
-rw-r--r--Documentation/git-receive-pack.txt52
-rw-r--r--Documentation/git-replace.txt10
-rw-r--r--Documentation/git-rev-list.txt54
-rw-r--r--Documentation/git-send-email.txt12
-rw-r--r--Documentation/git-stash.txt5
-rw-r--r--Documentation/git-status.txt18
-rw-r--r--Documentation/git-submodule.txt3
-rw-r--r--Documentation/git-svn.txt10
-rw-r--r--Documentation/gitattributes.txt2
-rw-r--r--Documentation/gitcli.txt6
-rw-r--r--Documentation/githooks.txt32
-rw-r--r--Documentation/gitmodules.txt17
-rw-r--r--Documentation/gitremote-helpers.txt15
-rw-r--r--Documentation/gitrepository-layout.txt12
-rw-r--r--Documentation/gitweb.conf.txt6
-rw-r--r--Documentation/manpage-bold-literal.xsl3
-rw-r--r--Documentation/manpage.xsl3
-rw-r--r--Documentation/merge-options.txt32
-rw-r--r--Documentation/pretty-formats.txt8
-rw-r--r--Documentation/rev-list-options.txt16
-rw-r--r--Documentation/technical/api-directory-listing.txt6
-rw-r--r--Documentation/technical/api-grep.txt8
-rw-r--r--Documentation/technical/api-object-access.txt15
-rw-r--r--Documentation/technical/api-quote.txt10
-rw-r--r--Documentation/technical/api-trace2.txt31
-rw-r--r--Documentation/technical/api-tree-walking.txt8
-rw-r--r--Documentation/technical/api-xdiff-interface.txt7
-rw-r--r--Documentation/technical/partial-clone.txt117
-rw-r--r--Documentation/trace2-target-values.txt4
-rw-r--r--Documentation/user-manual.txt377
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile64
l---------RelNotes2
-rw-r--r--apply.c56
-rw-r--r--apply.h1
-rw-r--r--archive-tar.c14
-rw-r--r--attr.c34
-rw-r--r--azure-pipelines.yml168
-rw-r--r--banned.h2
-rw-r--r--bisect.c2
-rw-r--r--blame.c27
-rw-r--r--builtin/am.c23
-rw-r--r--builtin/blame.c32
-rw-r--r--builtin/cat-file.c5
-rw-r--r--builtin/check-ignore.c34
-rw-r--r--builtin/checkout.c31
-rw-r--r--builtin/clean.c27
-rw-r--r--builtin/clone.c11
-rw-r--r--builtin/commit-graph.c36
-rw-r--r--builtin/commit.c26
-rw-r--r--builtin/describe.c24
-rw-r--r--builtin/difftool.c56
-rw-r--r--builtin/fast-export.c82
-rw-r--r--builtin/fetch.c304
-rw-r--r--builtin/gc.c15
-rw-r--r--builtin/grep.c15
-rw-r--r--builtin/index-pack.c12
-rw-r--r--builtin/log.c146
-rw-r--r--builtin/ls-files.c8
-rw-r--r--builtin/merge-recursive.c4
-rw-r--r--builtin/merge-tree.c5
-rw-r--r--builtin/merge.c31
-rw-r--r--builtin/multi-pack-index.c18
-rw-r--r--builtin/name-rev.c15
-rw-r--r--builtin/notes.c2
-rw-r--r--builtin/pack-objects.c52
-rw-r--r--builtin/patch-id.c11
-rw-r--r--builtin/pull.c6
-rw-r--r--builtin/push.c75
-rw-r--r--builtin/rebase.c120
-rw-r--r--builtin/receive-pack.c50
-rw-r--r--builtin/repack.c14
-rw-r--r--builtin/replace.c9
-rw-r--r--builtin/rev-list.c5
-rw-r--r--builtin/rev-parse.c5
-rw-r--r--builtin/show-index.c13
-rw-r--r--builtin/stash.c19
-rw-r--r--builtin/submodule--helper.c4
-rw-r--r--builtin/update-index.c6
-rw-r--r--builtin/worktree.c5
-rw-r--r--bulk-checkin.c2
-rw-r--r--bundle.c4
-rw-r--r--cache-tree.c92
-rw-r--r--cache-tree.h3
-rw-r--r--cache.h68
-rwxr-xr-xci/install-dependencies.sh11
-rwxr-xr-xci/lib.sh7
-rwxr-xr-xci/run-static-analysis.sh3
-rwxr-xr-xci/test-documentation.sh2
-rw-r--r--column.c13
-rw-r--r--combine-diff.c2
-rw-r--r--commit-graph.c116
-rw-r--r--commit-graph.h21
-rw-r--r--commit-reach.c1
-rw-r--r--commit.c29
-rw-r--r--common-main.c8
-rw-r--r--compat/mingw.c34
-rw-r--r--compat/mingw.h6
-rw-r--r--compat/nedmalloc/malloc.c.h6
-rwxr-xr-xcompat/vcbuild/scripts/clink.pl48
-rw-r--r--compat/win32/path-utils.h5
-rw-r--r--compat/win32/pthread.h8
-rw-r--r--compat/winansi.c2
-rw-r--r--config.c57
-rw-r--r--config.mak.uname19
-rw-r--r--connect.c4
-rw-r--r--connected.c10
-rw-r--r--contrib/buildsystems/Generators/Vcxproj.pm18
-rw-r--r--contrib/coccinelle/hashmap.cocci16
-rw-r--r--contrib/completion/git-completion.bash295
-rw-r--r--contrib/completion/git-completion.zsh5
-rw-r--r--contrib/diff-highlight/DiffHighlight.pm2
-rwxr-xr-xcontrib/hg-to-git/hg-to-git.py50
-rw-r--r--contrib/svn-fe/svn-fe.txt4
-rwxr-xr-xcontrib/svn-fe/svnrdump_sim.py2
-rw-r--r--convert.c29
-rw-r--r--convert.h6
-rw-r--r--credential-store.c9
-rw-r--r--date.c27
-rw-r--r--diff-delta.c2
-rw-r--r--diff.c87
-rw-r--r--diff.h2
-rw-r--r--diffcore-break.c12
-rw-r--r--diffcore-rename.c17
-rw-r--r--dir.c349
-rw-r--r--dir.h79
-rw-r--r--environment.c2
-rw-r--r--fast-import.c103
-rw-r--r--fetch-negotiator.c25
-rw-r--r--fetch-negotiator.h5
-rw-r--r--fetch-object.c40
-rw-r--r--fetch-object.h9
-rw-r--r--fetch-pack.c103
-rw-r--r--fsmonitor.c29
-rwxr-xr-xgit-add--interactive.perl2
-rw-r--r--git-compat-util.h53
-rwxr-xr-xgit-filter-branch.sh14
-rw-r--r--git-gui/README.md174
-rwxr-xr-xgit-gui/git-gui.sh174
-rw-r--r--git-gui/lib/checkout_op.tcl6
-rw-r--r--git-gui/lib/commit.tcl4
-rw-r--r--git-gui/lib/diff.tcl129
-rw-r--r--git-gui/lib/index.tcl8
-rw-r--r--git-gui/po/ja.po9
-rwxr-xr-xgit-legacy-stash.sh2
-rwxr-xr-xgit-p4.py13
-rw-r--r--git.c5
-rwxr-xr-xgitk-git/gitk64
-rw-r--r--gitk-git/po/zh_cn.po1367
-rw-r--r--gitweb/static/js/blame_incremental.js2
-rw-r--r--grep.c243
-rw-r--r--grep.h27
-rw-r--r--hashmap.c58
-rw-r--r--hashmap.h176
-rw-r--r--help.c3
-rw-r--r--help.h2
-rw-r--r--http-push.c8
-rw-r--r--http.c7
-rw-r--r--http.h4
-rw-r--r--line-log.c71
-rw-r--r--list-objects-filter-options.c324
-rw-r--r--list-objects-filter-options.h62
-rw-r--r--list-objects-filter.c382
-rw-r--r--list-objects-filter.h40
-rw-r--r--list-objects.c59
-rw-r--r--ll-merge.c19
-rw-r--r--ll-merge.h1
-rw-r--r--log-tree.c5
-rw-r--r--merge-recursive.c760
-rw-r--r--merge-recursive.h164
-rw-r--r--midx.c82
-rw-r--r--midx.h10
-rw-r--r--name-hash.c57
-rw-r--r--notes.c6
-rw-r--r--object.c1
-rw-r--r--object.h3
-rw-r--r--oidmap.c20
-rw-r--r--oidmap.h6
-rw-r--r--pack-bitmap-write.c2
-rw-r--r--pack-bitmap.c6
-rw-r--r--pack-bitmap.h6
-rw-r--r--pack-objects.c22
-rw-r--r--pack-objects.h6
-rw-r--r--pack-write.c8
-rw-r--r--packfile.c50
-rw-r--r--packfile.h2
-rw-r--r--parse-options.c3
-rw-r--r--parse-options.h18
-rw-r--r--patch-ids.c18
-rw-r--r--path.c161
-rw-r--r--path.h3
-rw-r--r--perl/Git/SVN.pm4
-rw-r--r--po/bg.po4987
-rw-r--r--po/ca.po60
-rw-r--r--po/de.po5044
-rw-r--r--po/es.po5220
-rw-r--r--po/fr.po4994
-rw-r--r--po/git.pot4924
-rw-r--r--po/it.po5034
-rw-r--r--po/sv.po5240
-rw-r--r--po/vi.po5040
-rw-r--r--po/zh_CN.po4978
-rw-r--r--pretty.c11
-rw-r--r--progress.c61
-rw-r--r--promisor-remote.c268
-rw-r--r--promisor-remote.h33
-rw-r--r--quote.c24
-rw-r--r--quote.h1
-rw-r--r--range-diff.c13
-rw-r--r--read-cache.c55
-rw-r--r--ref-filter.c43
-rw-r--r--refs.c25
-rw-r--r--refs/packed-backend.c23
-rw-r--r--remote-curl.c35
-rw-r--r--remote.c21
-rw-r--r--remote.h2
-rw-r--r--repo-settings.c68
-rw-r--r--repository.h34
-rw-r--r--rerere.c8
-rw-r--r--revision.c44
-rw-r--r--send-pack.c3
-rw-r--r--sequencer.c227
-rw-r--r--sequencer.h9
-rw-r--r--setup.c13
-rw-r--r--sha1-file.c24
-rw-r--r--sha1-lookup.c12
-rw-r--r--sha1-name.c35
-rw-r--r--shallow.c5
-rw-r--r--stable-qsort.c (renamed from compat/qsort.c)6
-rw-r--r--strbuf.c15
-rw-r--r--strbuf.h7
-rw-r--r--sub-process.c20
-rw-r--r--sub-process.h6
-rw-r--r--submodule-config.c52
-rw-r--r--t/helper/.gitignore9
-rw-r--r--t/helper/test-date.c27
-rw-r--r--t/helper/test-hashmap.c50
-rw-r--r--t/helper/test-lazy-init-name-hash.c12
-rw-r--r--t/helper/test-progress.c81
-rw-r--r--t/helper/test-read-cache.c5
-rw-r--r--t/helper/test-run-command.c153
-rw-r--r--t/helper/test-tool.c1
-rw-r--r--t/helper/test-tool.h1
-rw-r--r--t/lib-git-svn.sh2
-rw-r--r--t/lib-rebase.sh37
-rwxr-xr-xt/perf/p5601-clone-reference.sh (renamed from t/perf/p5600-clone-reference.sh)0
-rwxr-xr-xt/t0000-basic.sh58
-rwxr-xr-xt/t0014-alias.sh7
-rwxr-xr-xt/t0021-conversion.sh3
-rwxr-xr-xt/t0028-working-tree-encoding.sh41
-rwxr-xr-xt/t0040-parse-options.sh7
-rwxr-xr-xt/t0050-filesystem.sh20
-rwxr-xr-xt/t0060-path-utils.sh2
-rwxr-xr-xt/t0061-run-command.sh21
-rwxr-xr-xt/t0211-trace2-perf.sh4
-rwxr-xr-xt/t0212-trace2-event.sh19
-rwxr-xr-xt/t0410-partial-clone.sh93
-rwxr-xr-xt/t0500-progress-display.sh286
-rwxr-xr-xt/t1300-config.sh9
-rwxr-xr-xt/t1308-config-set.sh8
-rwxr-xr-xt/t1309-early-config.sh7
-rwxr-xr-xt/t1404-update-ref-errors.sh64
-rwxr-xr-xt/t1414-reflog-walk.sh3
-rwxr-xr-xt/t1450-fsck.sh16
-rwxr-xr-xt/t1506-rev-parse-diagnosis.sh13
-rwxr-xr-xt/t1507-rev-parse-upstream.sh12
-rwxr-xr-xt/t1600-index.sh35
-rwxr-xr-xt/t2022-checkout-paths.sh11
-rwxr-xr-xt/t2070-restore.sh11
-rwxr-xr-xt/t3005-ls-files-relative.sh12
-rwxr-xr-xt/t3030-merge-recursive.sh37
-rwxr-xr-xt/t3201-branch-contains.sh8
-rwxr-xr-xt/t3206-range-diff.sh271
-rw-r--r--t/t3206/history.export31
-rwxr-xr-xt/t3301-notes.sh186
-rwxr-xr-xt/t3305-notes-fanout.sh22
-rwxr-xr-xt/t3306-notes-prune.sh45
-rwxr-xr-xt/t3400-rebase.sh38
-rwxr-xr-xt/t3404-rebase-interactive.sh631
-rwxr-xr-xt/t3416-rebase-onto-threedots.sh57
-rwxr-xr-xt/t3418-rebase-continue.sh14
-rwxr-xr-xt/t3420-rebase-autostash.sh12
-rwxr-xr-xt/t3421-rebase-topology-linear.sh29
-rwxr-xr-xt/t3422-rebase-incompatible-options.sh10
-rwxr-xr-xt/t3427-rebase-subtree.sh160
-rwxr-xr-xt/t3429-rebase-edit-todo.sh21
-rwxr-xr-xt/t3430-rebase-merges.sh45
-rwxr-xr-xt/t3431-rebase-fork-point.sh57
-rwxr-xr-xt/t3432-rebase-fast-forward.sh125
-rwxr-xr-xt/t3506-cherry-pick-ff.sh8
-rwxr-xr-xt/t3600-rm.sh4
-rwxr-xr-xt/t3701-add-interactive.sh2
-rwxr-xr-xt/t3800-mktag.sh49
-rwxr-xr-xt/t3903-stash.sh48
-rwxr-xr-xt/t3906-stash-submodule.sh42
-rwxr-xr-xt/t3908-stash-in-worktree.sh27
-rwxr-xr-xt/t4000-diff-format.sh2
-rwxr-xr-xt/t4002-diff-basic.sh367
-rwxr-xr-xt/t4009-diff-rename-4.sh19
-rwxr-xr-xt/t4013-diff-various.sh3
-rwxr-xr-xt/t4014-format-patch.sh1013
-rwxr-xr-xt/t4018-diff-funcname.sh1
-rw-r--r--t/t4018/dts-labels9
-rw-r--r--t/t4018/dts-node-unitless8
-rw-r--r--t/t4018/dts-nodes8
-rw-r--r--t/t4018/dts-nodes-boolean-prop9
-rw-r--r--t/t4018/dts-nodes-comment18
-rw-r--r--t/t4018/dts-nodes-comment28
-rw-r--r--t/t4018/dts-nodes-multiline-prop13
-rw-r--r--t/t4018/dts-reference9
-rw-r--r--t/t4018/dts-root5
-rw-r--r--t/t4018/dts-root-comment8
-rwxr-xr-xt/t4034-diff-words.sh1
-rw-r--r--t/t4034/dts/expect37
-rw-r--r--t/t4034/dts/post32
-rw-r--r--t/t4034/dts/pre32
-rwxr-xr-xt/t4038-diff-combined.sh2
-rwxr-xr-xt/t4067-diff-partial-clone.sh31
-rwxr-xr-xt/t4108-apply-threeway.sh55
-rwxr-xr-xt/t4150-am.sh52
-rwxr-xr-xt/t4202-log.sh22
-rwxr-xr-xt/t4203-mailmap.sh122
-rwxr-xr-xt/t4210-log-i18n.sh41
-rwxr-xr-xt/t4211-line-log.sh82
-rwxr-xr-xt/t4214-log-graph-octopus.sh329
-rwxr-xr-xt/t5004-archive-corner-cases.sh19
-rwxr-xr-xt/t5307-pack-missing-commit.sh4
-rwxr-xr-xt/t5318-commit-graph.sh93
-rwxr-xr-xt/t5319-multi-pack-index.sh69
-rwxr-xr-xt/t5324-split-commit-graph.sh4
-rwxr-xr-xt/t5500-fetch-pack.sh54
-rwxr-xr-xt/t5510-fetch.sh76
-rwxr-xr-xt/t5514-fetch-multiple.sh11
-rwxr-xr-xt/t5515-fetch-merge-logic.sh3
-rwxr-xr-xt/t5517-push-mirror.sh10
-rwxr-xr-xt/t5537-fetch-shallow.sh3
-rwxr-xr-xt/t5541-http-push-smart.sh46
-rwxr-xr-xt/t5545-push-options.sh3
-rwxr-xr-xt/t5552-skipping-fetch-negotiator.sh23
-rwxr-xr-xt/t5553-set-upstream.sh178
-rwxr-xr-xt/t5601-clone.sh7
-rwxr-xr-xt/t5607-clone-bundle.sh11
-rwxr-xr-xt/t5616-partial-clone.sh70
-rwxr-xr-xt/t5700-protocol-v1.sh3
-rwxr-xr-xt/t5702-protocol-v2.sh16
-rwxr-xr-xt/t5703-upload-pack-ref-in-want.sh204
-rwxr-xr-xt/t5801-remote-helpers.sh1
-rwxr-xr-xt/t6000-rev-list-misc.sh8
-rwxr-xr-xt/t6006-rev-list-format.sh33
-rwxr-xr-xt/t6011-rev-list-with-bad-commit.sh2
-rwxr-xr-xt/t6036-recursive-corner-cases.sh8
-rwxr-xr-xt/t6042-merge-rename-corner-cases.sh111
-rwxr-xr-xt/t6043-merge-rename-directories.sh568
-rwxr-xr-xt/t6046-merge-skip-unneeded-updates.sh135
-rwxr-xr-xt/t6047-diff3-conflict-markers.sh202
-rwxr-xr-xt/t6112-rev-list-filters-objects.sh194
-rwxr-xr-xt/t6120-describe.sh15
-rwxr-xr-xt/t6300-for-each-ref.sh19
-rwxr-xr-xt/t6501-freshen-objects.sh6
-rwxr-xr-xt/t7008-filter-branch-null-sha1.sh (renamed from t/t7009-filter-branch-null-sha1.sh)0
-rwxr-xr-xt/t7300-clean.sh64
-rwxr-xr-xt/t7400-submodule-basic.sh2
-rwxr-xr-xt/t7406-submodule-update.sh3
-rwxr-xr-xt/t7419-submodule-set-branch.sh6
-rwxr-xr-xt/t7503-pre-commit-and-pre-merge-commit-hooks.sh281
-rwxr-xr-xt/t7503-pre-commit-hook.sh139
-rwxr-xr-xt/t7505-prepare-commit-msg-hook.sh8
-rw-r--r--t/t7505/expected-rebase-i3
-rwxr-xr-xt/t7512-status-help.sh6
-rwxr-xr-xt/t7519-status-fsmonitor.sh19
-rwxr-xr-xt/t7519/fsmonitor-env24
-rwxr-xr-xt/t7812-grep-icase-non-ascii.sh28
-rwxr-xr-xt/t7814-grep-recurse-submodules.sh21
-rwxr-xr-xt/t7815-grep-binary.sh (renamed from t/t7008-grep-binary.sh)101
-rwxr-xr-xt/t7816-grep-binary-pattern.sh127
-rwxr-xr-xt/t9300-fast-import.sh50
-rwxr-xr-xt/t9350-fast-export.sh68
-rwxr-xr-xt/t9902-completion.sh80
-rw-r--r--t/test-lib-functions.sh10
-rw-r--r--t/test-lib.sh51
-rw-r--r--tag.c7
-rw-r--r--tag.h1
-rwxr-xr-xtemplates/hooks--pre-merge-commit.sample13
-rw-r--r--trace.c2
-rw-r--r--trace2/tr2_dst.c120
-rw-r--r--trace2/tr2_dst.h1
-rw-r--r--trace2/tr2_sysenv.c3
-rw-r--r--trace2/tr2_sysenv.h2
-rw-r--r--trace2/tr2_tgt_event.c36
-rw-r--r--trace2/tr2_tgt_normal.c37
-rw-r--r--trace2/tr2_tgt_perf.c91
-rw-r--r--transport-helper.c52
-rw-r--r--transport-internal.h6
-rw-r--r--transport.c33
-rw-r--r--transport.h3
-rw-r--r--tree-walk.c64
-rw-r--r--tree-walk.h18
-rw-r--r--tree.c18
-rw-r--r--unpack-trees.c150
-rw-r--r--unpack-trees.h4
-rw-r--r--upload-pack.c15
-rw-r--r--url.c6
-rw-r--r--url.h8
-rw-r--r--userdiff.c9
-rw-r--r--utf8.c6
-rw-r--r--wrapper.c84
-rw-r--r--wt-status.c6
-rw-r--r--wt-status.h2
-rw-r--r--xdiff/xdiffi.c99
454 files changed, 40020 insertions, 28232 deletions
diff --git a/.gitignore b/.gitignore
index 521d8f4..89b3b79 100644
--- a/.gitignore
+++ b/.gitignore
@@ -216,6 +216,7 @@
/tags
/TAGS
/cscope*
+*.hcc
*.obj
*.lib
*.res
@@ -231,7 +232,6 @@
*.ipdb
*.dll
.vs/
-*.manifest
Debug/
Release/
/UpgradeLog*.htm
diff --git a/.mailmap b/.mailmap
index 9a5ff04..14fa041 100644
--- a/.mailmap
+++ b/.mailmap
@@ -18,6 +18,7 @@ Alexey Shumkin <alex.crezoff@gmail.com> <zapped@mail.ru>
Alexey Shumkin <alex.crezoff@gmail.com> <Alex.Crezoff@gmail.com>
Anders Kaseorg <andersk@MIT.EDU> <andersk@ksplice.com>
Anders Kaseorg <andersk@MIT.EDU> <andersk@mit.edu>
+Andrey Mazo <ahippo@yandex.com> Mazo, Andrey <amazo@checkvideo.com>
Aneesh Kumar K.V <aneesh.kumar@gmail.com>
Amos Waterland <apw@debian.org> <apw@rossby.metr.ou.edu>
Amos Waterland <apw@debian.org> <apw@us.ibm.com>
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..fc4645d
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,93 @@
+# Git Code of Conduct
+
+This code of conduct outlines our expectations for participants within
+the Git community, as well as steps for reporting unacceptable behavior.
+We are committed to providing a welcoming and inspiring community for
+all and expect our code of conduct to be honored. Anyone who violates
+this code of conduct may be banned from the community.
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to make participation in our project and
+our community a harassment-free experience for everyone, regardless of age,
+body size, disability, ethnicity, sex characteristics, gender identity and
+expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity and
+orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies within all project spaces, and it also applies
+when an individual is representing the project or its community in public
+spaces. Examples of representing a project or community include using an
+official project e-mail address, posting via an official social media account,
+or acting as an appointed representative at an online or offline event.
+Representation of a project may be further defined and clarified by project
+maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at git@sfconservancy.org. All
+complaints will be reviewed and investigated and will result in a response
+that is deemed necessary and appropriate to the circumstances. The project
+team is obligated to maintain confidentiality with regard to the reporter of
+an incident. Further details of specific enforcement policies may be posted
+separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+The project leadership team can be contacted by email as a whole at
+git@sfconservancy.org, or individually:
+
+ - Ævar Arnfjörð Bjarmason <avarab@gmail.com>
+ - Christian Couder <christian.couder@gmail.com>
+ - Jeff King <peff@peff.net>
+ - Junio C Hamano <gitster@pobox.com>
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 76f2ecf..8fe829c 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -77,6 +77,7 @@ API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technica
SP_ARTICLES += $(API_DOCS)
TECH_DOCS += MyFirstContribution
+TECH_DOCS += MyFirstObjectWalk
TECH_DOCS += SubmittingPatches
TECH_DOCS += technical/hash-function-transition
TECH_DOCS += technical/http-protocol
@@ -123,7 +124,8 @@ ASCIIDOC_HTML = xhtml11
ASCIIDOC_DOCBOOK = docbook
ASCIIDOC_CONF = -f asciidoc.conf
ASCIIDOC_COMMON = $(ASCIIDOC) $(ASCIIDOC_EXTRA) $(ASCIIDOC_CONF) \
- -agit_version=$(GIT_VERSION)
+ -amanversion=$(GIT_VERSION) \
+ -amanmanual='Git Manual' -amansource='Git'
TXT_TO_HTML = $(ASCIIDOC_COMMON) -b $(ASCIIDOC_HTML)
TXT_TO_XML = $(ASCIIDOC_COMMON) -b $(ASCIIDOC_DOCBOOK)
MANPAGE_XSL = manpage-normal.xsl
@@ -197,11 +199,13 @@ ifdef USE_ASCIIDOCTOR
ASCIIDOC = asciidoctor
ASCIIDOC_CONF =
ASCIIDOC_HTML = xhtml5
-ASCIIDOC_DOCBOOK = docbook45
+ASCIIDOC_DOCBOOK = docbook5
ASCIIDOC_EXTRA += -acompat-mode -atabsize=8
ASCIIDOC_EXTRA += -I. -rasciidoctor-extensions
ASCIIDOC_EXTRA += -alitdd='&\#x2d;&\#x2d;'
DBLATEX_COMMON =
+XMLTO_EXTRA += --skip-validation
+XMLTO_EXTRA += -x manpage.xsl
endif
SHELL_PATH ?= $(SHELL)
diff --git a/Documentation/MyFirstContribution.txt b/Documentation/MyFirstContribution.txt
index f867037..5e9b808 100644
--- a/Documentation/MyFirstContribution.txt
+++ b/Documentation/MyFirstContribution.txt
@@ -97,8 +97,8 @@ int cmd_psuh(int argc, const char **argv, const char *prefix)
----
We'll also need to add the declaration of psuh; open up `builtin.h`, find the
-declaration for `cmd_push`, and add a new line for `psuh` immediately before it,
-in order to keep the declarations sorted:
+declaration for `cmd_pull`, and add a new line for `psuh` immediately before it,
+in order to keep the declarations alphabetically sorted:
----
int cmd_psuh(int argc, const char **argv, const char *prefix);
@@ -123,7 +123,7 @@ int cmd_psuh(int argc, const char **argv, const char *prefix)
}
----
-Let's try to build it. Open `Makefile`, find where `builtin/push.o` is added
+Let's try to build it. Open `Makefile`, find where `builtin/pull.o` is added
to `BUILTIN_OBJS`, and add `builtin/psuh.o` in the same way next to it in
alphabetical order. Once you've done so, move to the top-level directory and
build simply with `make`. Also add the `DEVELOPER=1` variable to turn on
@@ -149,7 +149,7 @@ a `cmd_struct` to the `commands[]` array. `struct cmd_struct` takes a string
with the command name, a function pointer to the command implementation, and a
setup option flag. For now, let's keep mimicking `push`. Find the line where
`cmd_push` is registered, copy it, and modify it for `cmd_psuh`, placing the new
-line in alphabetical order.
+line in alphabetical order (immediately before `cmd_pull`).
The options are documented in `builtin.h` under "Adding a new built-in." Since
we hope to print some data about the user's current workspace context later,
@@ -167,7 +167,7 @@ Check it out! You've got a command! Nice work! Let's commit this.
`git status` reveals modified `Makefile`, `builtin.h`, and `git.c` as well as
untracked `builtin/psuh.c` and `git-psuh`. First, let's take care of the binary,
-which should be ignored. Open `.gitignore` in your editor, find `/git-push`, and
+which should be ignored. Open `.gitignore` in your editor, find `/git-pull`, and
add an entry for your new command in alphabetical order:
----
diff --git a/Documentation/MyFirstObjectWalk.txt b/Documentation/MyFirstObjectWalk.txt
new file mode 100644
index 0000000..4d24dae
--- /dev/null
+++ b/Documentation/MyFirstObjectWalk.txt
@@ -0,0 +1,906 @@
+= My First Object Walk
+
+== What's an Object Walk?
+
+The object walk is a key concept in Git - this is the process that underpins
+operations like object transfer and fsck. Beginning from a given commit, the
+list of objects is found by walking parent relationships between commits (commit
+X based on commit W) and containment relationships between objects (tree Y is
+contained within commit X, and blob Z is located within tree Y, giving our
+working tree for commit X something like `y/z.txt`).
+
+A related concept is the revision walk, which is focused on commit objects and
+their parent relationships and does not delve into other object types. The
+revision walk is used for operations like `git log`.
+
+=== Related Reading
+
+- `Documentation/user-manual.txt` under "Hacking Git" contains some coverage of
+ the revision walker in its various incarnations.
+- `Documentation/technical/api-revision-walking.txt`
+- https://eagain.net/articles/git-for-computer-scientists/[Git for Computer Scientists]
+ gives a good overview of the types of objects in Git and what your object
+ walk is really describing.
+
+== Setting Up
+
+Create a new branch from `master`.
+
+----
+git checkout -b revwalk origin/master
+----
+
+We'll put our fiddling into a new command. For fun, let's name it `git walken`.
+Open up a new file `builtin/walken.c` and set up the command handler:
+
+----
+/*
+ * "git walken"
+ *
+ * Part of the "My First Object Walk" tutorial.
+ */
+
+#include "builtin.h"
+
+int cmd_walken(int argc, const char **argv, const char *prefix)
+{
+ trace_printf(_("cmd_walken incoming...\n"));
+ return 0;
+}
+----
+
+NOTE: `trace_printf()` differs from `printf()` in that it can be turned on or
+off at runtime. For the purposes of this tutorial, we will write `walken` as
+though it is intended for use as a "plumbing" command: that is, a command which
+is used primarily in scripts, rather than interactively by humans (a "porcelain"
+command). So we will send our debug output to `trace_printf()` instead. When
+running, enable trace output by setting the environment variable `GIT_TRACE`.
+
+Add usage text and `-h` handling, like all subcommands should consistently do
+(our test suite will notice and complain if you fail to do so).
+
+----
+int cmd_walken(int argc, const char **argv, const char *prefix)
+{
+ const char * const walken_usage[] = {
+ N_("git walken"),
+ NULL,
+ }
+ struct option options[] = {
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options, walken_usage, 0);
+
+ ...
+}
+----
+
+Also add the relevant line in `builtin.h` near `cmd_whatchanged()`:
+
+----
+int cmd_walken(int argc, const char **argv, const char *prefix);
+----
+
+Include the command in `git.c` in `commands[]` near the entry for `whatchanged`,
+maintaining alphabetical ordering:
+
+----
+{ "walken", cmd_walken, RUN_SETUP },
+----
+
+Add it to the `Makefile` near the line for `builtin/worktree.o`:
+
+----
+BUILTIN_OBJS += builtin/walken.o
+----
+
+Build and test out your command, without forgetting to ensure the `DEVELOPER`
+flag is set, and with `GIT_TRACE` enabled so the debug output can be seen:
+
+----
+$ echo DEVELOPER=1 >>config.mak
+$ make
+$ GIT_TRACE=1 ./bin-wrappers/git walken
+----
+
+NOTE: For a more exhaustive overview of the new command process, take a look at
+`Documentation/MyFirstContribution.txt`.
+
+NOTE: A reference implementation can be found at
+https://github.com/nasamuffin/git/tree/revwalk.
+
+=== `struct rev_cmdline_info`
+
+The definition of `struct rev_cmdline_info` can be found in `revision.h`.
+
+This struct is contained within the `rev_info` struct and is used to reflect
+parameters provided by the user over the CLI.
+
+`nr` represents the number of `rev_cmdline_entry` present in the array.
+
+`alloc` is used by the `ALLOC_GROW` macro. Check
+`Documentation/technical/api-allocation-growing.txt` - this variable is used to
+track the allocated size of the list.
+
+Per entry, we find:
+
+`item` is the object provided upon which to base the object walk. Items in Git
+can be blobs, trees, commits, or tags. (See `Documentation/gittutorial-2.txt`.)
+
+`name` is the object ID (OID) of the object - a hex string you may be familiar
+with from using Git to organize your source in the past. Check the tutorial
+mentioned above towards the top for a discussion of where the OID can come
+from.
+
+`whence` indicates some information about what to do with the parents of the
+specified object. We'll explore this flag more later on; take a look at
+`Documentation/revisions.txt` to get an idea of what could set the `whence`
+value.
+
+`flags` are used to hint the beginning of the revision walk and are the first
+block under the `#include`s in `revision.h`. The most likely ones to be set in
+the `rev_cmdline_info` are `UNINTERESTING` and `BOTTOM`, but these same flags
+can be used during the walk, as well.
+
+=== `struct rev_info`
+
+This one is quite a bit longer, and many fields are only used during the walk
+by `revision.c` - not configuration options. Most of the configurable flags in
+`struct rev_info` have a mirror in `Documentation/rev-list-options.txt`. It's a
+good idea to take some time and read through that document.
+
+== Basic Commit Walk
+
+First, let's see if we can replicate the output of `git log --oneline`. We'll
+refer back to the implementation frequently to discover norms when performing
+an object walk of our own.
+
+To do so, we'll first find all the commits, in order, which preceded the current
+commit. We'll extract the name and subject of the commit from each.
+
+Ideally, we will also be able to find out which ones are currently at the tip of
+various branches.
+
+=== Setting Up
+
+Preparing for your object walk has some distinct stages.
+
+1. Perform default setup for this mode, and others which may be invoked.
+2. Check configuration files for relevant settings.
+3. Set up the `rev_info` struct.
+4. Tweak the initialized `rev_info` to suit the current walk.
+5. Prepare the `rev_info` for the walk.
+6. Iterate over the objects, processing each one.
+
+==== Default Setups
+
+Before examining configuration files which may modify command behavior, set up
+default state for switches or options your command may have. If your command
+utilizes other Git components, ask them to set up their default states as well.
+For instance, `git log` takes advantage of `grep` and `diff` functionality, so
+its `init_log_defaults()` sets its own state (`decoration_style`) and asks
+`grep` and `diff` to initialize themselves by calling each of their
+initialization functions.
+
+For our first example within `git walken`, we don't intend to use any other
+components within Git, and we don't have any configuration to do. However, we
+may want to add some later, so for now, we can add an empty placeholder. Create
+a new function in `builtin/walken.c`:
+
+----
+static void init_walken_defaults(void)
+{
+ /*
+ * We don't actually need the same components `git log` does; leave this
+ * empty for now.
+ */
+}
+----
+
+Make sure to add a line invoking it inside of `cmd_walken()`.
+
+----
+int cmd_walken(int argc, const char **argv, const char *prefix)
+{
+ init_walken_defaults();
+}
+----
+
+==== Configuring From `.gitconfig`
+
+Next, we should have a look at any relevant configuration settings (i.e.,
+settings readable and settable from `git config`). This is done by providing a
+callback to `git_config()`; within that callback, you can also invoke methods
+from other components you may need that need to intercept these options. Your
+callback will be invoked once per each configuration value which Git knows about
+(global, local, worktree, etc.).
+
+Similarly to the default values, we don't have anything to do here yet
+ourselves; however, we should call `git_default_config()` if we aren't calling
+any other existing config callbacks.
+
+Add a new function to `builtin/walken.c`:
+
+----
+static int git_walken_config(const char *var, const char *value, void *cb)
+{
+ /*
+ * For now, we don't have any custom configuration, so fall back to
+ * the default config.
+ */
+ return git_default_config(var, value, cb);
+}
+----
+
+Make sure to invoke `git_config()` with it in your `cmd_walken()`:
+
+----
+int cmd_walken(int argc, const char **argv, const char *prefix)
+{
+ ...
+
+ git_config(git_walken_config, NULL);
+
+ ...
+}
+----
+
+==== Setting Up `rev_info`
+
+Now that we've gathered external configuration and options, it's time to
+initialize the `rev_info` object which we will use to perform the walk. This is
+typically done by calling `repo_init_revisions()` with the repository you intend
+to target, as well as the `prefix` argument of `cmd_walken` and your `rev_info`
+struct.
+
+Add the `struct rev_info` and the `repo_init_revisions()` call:
+----
+int cmd_walken(int argc, const char **argv, const char *prefix)
+{
+ /* This can go wherever you like in your declarations.*/
+ struct rev_info rev;
+ ...
+
+ /* This should go after the git_config() call. */
+ repo_init_revisions(the_repository, &rev, prefix);
+
+ ...
+}
+----
+
+==== Tweaking `rev_info` For the Walk
+
+We're getting close, but we're still not quite ready to go. Now that `rev` is
+initialized, we can modify it to fit our needs. This is usually done within a
+helper for clarity, so let's add one:
+
+----
+static void final_rev_info_setup(struct rev_info *rev)
+{
+ /*
+ * We want to mimic the appearance of `git log --oneline`, so let's
+ * force oneline format.
+ */
+ get_commit_format("oneline", rev);
+
+ /* Start our object walk at HEAD. */
+ add_head_to_pending(rev);
+}
+----
+
+[NOTE]
+====
+Instead of using the shorthand `add_head_to_pending()`, you could do
+something like this:
+----
+ struct setup_revision_opt opt;
+
+ memset(&opt, 0, sizeof(opt));
+ opt.def = "HEAD";
+ opt.revarg_opt = REVARG_COMMITTISH;
+ setup_revisions(argc, argv, rev, &opt);
+----
+Using a `setup_revision_opt` gives you finer control over your walk's starting
+point.
+====
+
+Then let's invoke `final_rev_info_setup()` after the call to
+`repo_init_revisions()`:
+
+----
+int cmd_walken(int argc, const char **argv, const char *prefix)
+{
+ ...
+
+ final_rev_info_setup(&rev);
+
+ ...
+}
+----
+
+Later, we may wish to add more arguments to `final_rev_info_setup()`. But for
+now, this is all we need.
+
+==== Preparing `rev_info` For the Walk
+
+Now that `rev` is all initialized and configured, we've got one more setup step
+before we get rolling. We can do this in a helper, which will both prepare the
+`rev_info` for the walk, and perform the walk itself. Let's start the helper
+with the call to `prepare_revision_walk()`, which can return an error without
+dying on its own:
+
+----
+static void walken_commit_walk(struct rev_info *rev)
+{
+ if (prepare_revision_walk(rev))
+ die(_("revision walk setup failed"));
+}
+----
+
+NOTE: `die()` prints to `stderr` and exits the program. Since it will print to
+`stderr` it's likely to be seen by a human, so we will localize it.
+
+==== Performing the Walk!
+
+Finally! We are ready to begin the walk itself. Now we can see that `rev_info`
+can also be used as an iterator; we move to the next item in the walk by using
+`get_revision()` repeatedly. Add the listed variable declarations at the top and
+the walk loop below the `prepare_revision_walk()` call within your
+`walken_commit_walk()`:
+
+----
+static void walken_commit_walk(struct rev_info *rev)
+{
+ struct commit *commit;
+ struct strbuf prettybuf = STRBUF_INIT;
+
+ ...
+
+ while ((commit = get_revision(rev))) {
+ if (!commit)
+ continue;
+
+ strbuf_reset(&prettybuf);
+ pp_commit_easy(CMIT_FMT_ONELINE, commit, &prettybuf);
+ puts(prettybuf.buf);
+ }
+ strbuf_release(&prettybuf);
+}
+----
+
+NOTE: `puts()` prints a `char*` to `stdout`. Since this is the part of the
+command we expect to be machine-parsed, we're sending it directly to stdout.
+
+Give it a shot.
+
+----
+$ make
+$ ./bin-wrappers/git walken
+----
+
+You should see all of the subject lines of all the commits in
+your tree's history, in order, ending with the initial commit, "Initial revision
+of "git", the information manager from hell". Congratulations! You've written
+your first revision walk. You can play with printing some additional fields
+from each commit if you're curious; have a look at the functions available in
+`commit.h`.
+
+=== Adding a Filter
+
+Next, let's try to filter the commits we see based on their author. This is
+equivalent to running `git log --author=<pattern>`. We can add a filter by
+modifying `rev_info.grep_filter`, which is a `struct grep_opt`.
+
+First some setup. Add `init_grep_defaults()` to `init_walken_defaults()` and add
+`grep_config()` to `git_walken_config()`:
+
+----
+static void init_walken_defaults(void)
+{
+ init_grep_defaults(the_repository);
+}
+
+...
+
+static int git_walken_config(const char *var, const char *value, void *cb)
+{
+ grep_config(var, value, cb);
+ return git_default_config(var, value, cb);
+}
+----
+
+Next, we can modify the `grep_filter`. This is done with convenience functions
+found in `grep.h`. For fun, we're filtering to only commits from folks using a
+`gmail.com` email address - a not-very-precise guess at who may be working on
+Git as a hobby. Since we're checking the author, which is a specific line in the
+header, we'll use the `append_header_grep_pattern()` helper. We can use
+the `enum grep_header_field` to indicate which part of the commit header we want
+to search.
+
+In `final_rev_info_setup()`, add your filter line:
+
+----
+static void final_rev_info_setup(int argc, const char **argv,
+ const char *prefix, struct rev_info *rev)
+{
+ ...
+
+ append_header_grep_pattern(&rev->grep_filter, GREP_HEADER_AUTHOR,
+ "gmail");
+ compile_grep_patterns(&rev->grep_filter);
+
+ ...
+}
+----
+
+`append_header_grep_pattern()` adds your new "gmail" pattern to `rev_info`, but
+it won't work unless we compile it with `compile_grep_patterns()`.
+
+NOTE: If you are using `setup_revisions()` (for example, if you are passing a
+`setup_revision_opt` instead of using `add_head_to_pending()`), you don't need
+to call `compile_grep_patterns()` because `setup_revisions()` calls it for you.
+
+NOTE: We could add the same filter via the `append_grep_pattern()` helper if we
+wanted to, but `append_header_grep_pattern()` adds the `enum grep_context` and
+`enum grep_pat_token` for us.
+
+=== Changing the Order
+
+There are a few ways that we can change the order of the commits during a
+revision walk. Firstly, we can use the `enum rev_sort_order` to choose from some
+typical orderings.
+
+`topo_order` is the same as `git log --topo-order`: we avoid showing a parent
+before all of its children have been shown, and we avoid mixing commits which
+are in different lines of history. (`git help log`'s section on `--topo-order`
+has a very nice diagram to illustrate this.)
+
+Let's see what happens when we run with `REV_SORT_BY_COMMIT_DATE` as opposed to
+`REV_SORT_BY_AUTHOR_DATE`. Add the following:
+
+----
+static void final_rev_info_setup(int argc, const char **argv,
+ const char *prefix, struct rev_info *rev)
+{
+ ...
+
+ rev->topo_order = 1;
+ rev->sort_order = REV_SORT_BY_COMMIT_DATE;
+
+ ...
+}
+----
+
+Let's output this into a file so we can easily diff it with the walk sorted by
+author date.
+
+----
+$ make
+$ ./bin-wrappers/git walken > commit-date.txt
+----
+
+Then, let's sort by author date and run it again.
+
+----
+static void final_rev_info_setup(int argc, const char **argv,
+ const char *prefix, struct rev_info *rev)
+{
+ ...
+
+ rev->topo_order = 1;
+ rev->sort_order = REV_SORT_BY_AUTHOR_DATE;
+
+ ...
+}
+----
+
+----
+$ make
+$ ./bin-wrappers/git walken > author-date.txt
+----
+
+Finally, compare the two. This is a little less helpful without object names or
+dates, but hopefully we get the idea.
+
+----
+$ diff -u commit-date.txt author-date.txt
+----
+
+This display indicates that commits can be reordered after they're written, for
+example with `git rebase`.
+
+Let's try one more reordering of commits. `rev_info` exposes a `reverse` flag.
+Set that flag somewhere inside of `final_rev_info_setup()`:
+
+----
+static void final_rev_info_setup(int argc, const char **argv, const char *prefix,
+ struct rev_info *rev)
+{
+ ...
+
+ rev->reverse = 1;
+
+ ...
+}
+----
+
+Run your walk again and note the difference in order. (If you remove the grep
+pattern, you should see the last commit this call gives you as your current
+HEAD.)
+
+== Basic Object Walk
+
+So far we've been walking only commits. But Git has more types of objects than
+that! Let's see if we can walk _all_ objects, and find out some information
+about each one.
+
+We can base our work on an example. `git pack-objects` prepares all kinds of
+objects for packing into a bitmap or packfile. The work we are interested in
+resides in `builtins/pack-objects.c:get_object_list()`; examination of that
+function shows that the all-object walk is being performed by
+`traverse_commit_list()` or `traverse_commit_list_filtered()`. Those two
+functions reside in `list-objects.c`; examining the source shows that, despite
+the name, these functions traverse all kinds of objects. Let's have a look at
+the arguments to `traverse_commit_list_filtered()`, which are a superset of the
+arguments to the unfiltered version.
+
+- `struct list_objects_filter_options *filter_options`: This is a struct which
+ stores a filter-spec as outlined in `Documentation/rev-list-options.txt`.
+- `struct rev_info *revs`: This is the `rev_info` used for the walk.
+- `show_commit_fn show_commit`: A callback which will be used to handle each
+ individual commit object.
+- `show_object_fn show_object`: A callback which will be used to handle each
+ non-commit object (so each blob, tree, or tag).
+- `void *show_data`: A context buffer which is passed in turn to `show_commit`
+ and `show_object`.
+- `struct oidset *omitted`: A linked-list of object IDs which the provided
+ filter caused to be omitted.
+
+It looks like this `traverse_commit_list_filtered()` uses callbacks we provide
+instead of needing us to call it repeatedly ourselves. Cool! Let's add the
+callbacks first.
+
+For the sake of this tutorial, we'll simply keep track of how many of each kind
+of object we find. At file scope in `builtin/walken.c` add the following
+tracking variables:
+
+----
+static int commit_count;
+static int tag_count;
+static int blob_count;
+static int tree_count;
+----
+
+Commits are handled by a different callback than other objects; let's do that
+one first:
+
+----
+static void walken_show_commit(struct commit *cmt, void *buf)
+{
+ commit_count++;
+}
+----
+
+The `cmt` argument is fairly self-explanatory. But it's worth mentioning that
+the `buf` argument is actually the context buffer that we can provide to the
+traversal calls - `show_data`, which we mentioned a moment ago.
+
+Since we have the `struct commit` object, we can look at all the same parts that
+we looked at in our earlier commit-only walk. For the sake of this tutorial,
+though, we'll just increment the commit counter and move on.
+
+The callback for non-commits is a little different, as we'll need to check
+which kind of object we're dealing with:
+
+----
+static void walken_show_object(struct object *obj, const char *str, void *buf)
+{
+ switch (obj->type) {
+ case OBJ_TREE:
+ tree_count++;
+ break;
+ case OBJ_BLOB:
+ blob_count++;
+ break;
+ case OBJ_TAG:
+ tag_count++;
+ break;
+ case OBJ_COMMIT:
+ BUG("unexpected commit object in walken_show_object\n");
+ default:
+ BUG("unexpected object type %s in walken_show_object\n",
+ type_name(obj->type));
+ }
+}
+----
+
+Again, `obj` is fairly self-explanatory, and we can guess that `buf` is the same
+context pointer that `walken_show_commit()` receives: the `show_data` argument
+to `traverse_commit_list()` and `traverse_commit_list_filtered()`. Finally,
+`str` contains the name of the object, which ends up being something like
+`foo.txt` (blob), `bar/baz` (tree), or `v1.2.3` (tag).
+
+To help assure us that we aren't double-counting commits, we'll include some
+complaining if a commit object is routed through our non-commit callback; we'll
+also complain if we see an invalid object type. Since those two cases should be
+unreachable, and would only change in the event of a semantic change to the Git
+codebase, we complain by using `BUG()` - which is a signal to a developer that
+the change they made caused unintended consequences, and the rest of the
+codebase needs to be updated to understand that change. `BUG()` is not intended
+to be seen by the public, so it is not localized.
+
+Our main object walk implementation is substantially different from our commit
+walk implementation, so let's make a new function to perform the object walk. We
+can perform setup which is applicable to all objects here, too, to keep separate
+from setup which is applicable to commit-only walks.
+
+We'll start by enabling all types of objects in the `struct rev_info`. We'll
+also turn on `tree_blobs_in_commit_order`, which means that we will walk a
+commit's tree and everything it points to immediately after we find each commit,
+as opposed to waiting for the end and walking through all trees after the commit
+history has been discovered. With the appropriate settings configured, we are
+ready to call `prepare_revision_walk()`.
+
+----
+static void walken_object_walk(struct rev_info *rev)
+{
+ rev->tree_objects = 1;
+ rev->blob_objects = 1;
+ rev->tag_objects = 1;
+ rev->tree_blobs_in_commit_order = 1;
+
+ if (prepare_revision_walk(rev))
+ die(_("revision walk setup failed"));
+
+ commit_count = 0;
+ tag_count = 0;
+ blob_count = 0;
+ tree_count = 0;
+----
+
+Let's start by calling just the unfiltered walk and reporting our counts.
+Complete your implementation of `walken_object_walk()`:
+
+----
+ traverse_commit_list(rev, walken_show_commit, walken_show_object, NULL);
+
+ printf("commits %d\nblobs %d\ntags %d\ntrees %d\n", commit_count,
+ blob_count, tag_count, tree_count);
+}
+----
+
+NOTE: This output is intended to be machine-parsed. Therefore, we are not
+sending it to `trace_printf()`, and we are not localizing it - we need scripts
+to be able to count on the formatting to be exactly the way it is shown here.
+If we were intending this output to be read by humans, we would need to localize
+it with `_()`.
+
+Finally, we'll ask `cmd_walken()` to use the object walk instead. Discussing
+command line options is out of scope for this tutorial, so we'll just hardcode
+a branch we can change at compile time. Where you call `final_rev_info_setup()`
+and `walken_commit_walk()`, instead branch like so:
+
+----
+ if (1) {
+ add_head_to_pending(&rev);
+ walken_object_walk(&rev);
+ } else {
+ final_rev_info_setup(argc, argv, prefix, &rev);
+ walken_commit_walk(&rev);
+ }
+----
+
+NOTE: For simplicity, we've avoided all the filters and sorts we applied in
+`final_rev_info_setup()` and simply added `HEAD` to our pending queue. If you
+want, you can certainly use the filters we added before by moving
+`final_rev_info_setup()` out of the conditional and removing the call to
+`add_head_to_pending()`.
+
+Now we can try to run our command! It should take noticeably longer than the
+commit walk, but an examination of the output will give you an idea why. Your
+output should look similar to this example, but with different counts:
+
+----
+Object walk completed. Found 55733 commits, 100274 blobs, 0 tags, and 104210 trees.
+----
+
+This makes sense. We have more trees than commits because the Git project has
+lots of subdirectories which can change, plus at least one tree per commit. We
+have no tags because we started on a commit (`HEAD`) and while tags can point to
+commits, commits can't point to tags.
+
+NOTE: You will have different counts when you run this yourself! The number of
+objects grows along with the Git project.
+
+=== Adding a Filter
+
+There are a handful of filters that we can apply to the object walk laid out in
+`Documentation/rev-list-options.txt`. These filters are typically useful for
+operations such as creating packfiles or performing a partial clone. They are
+defined in `list-objects-filter-options.h`. For the purposes of this tutorial we
+will use the "tree:1" filter, which causes the walk to omit all trees and blobs
+which are not directly referenced by commits reachable from the commit in
+`pending` when the walk begins. (`pending` is the list of objects which need to
+be traversed during a walk; you can imagine a breadth-first tree traversal to
+help understand. In our case, that means we omit trees and blobs not directly
+referenced by `HEAD` or `HEAD`'s history, because we begin the walk with only
+`HEAD` in the `pending` list.)
+
+First, we'll need to `#include "list-objects-filter-options.h`" and set up the
+`struct list_objects_filter_options` at the top of the function.
+
+----
+static void walken_object_walk(struct rev_info *rev)
+{
+ struct list_objects_filter_options filter_options = {};
+
+ ...
+----
+
+For now, we are not going to track the omitted objects, so we'll replace those
+parameters with `NULL`. For the sake of simplicity, we'll add a simple
+build-time branch to use our filter or not. Replace the line calling
+`traverse_commit_list()` with the following, which will remind us which kind of
+walk we've just performed:
+
+----
+ if (0) {
+ /* Unfiltered: */
+ trace_printf(_("Unfiltered object walk.\n"));
+ traverse_commit_list(rev, walken_show_commit,
+ walken_show_object, NULL);
+ } else {
+ trace_printf(
+ _("Filtered object walk with filterspec 'tree:1'.\n"));
+ parse_list_objects_filter(&filter_options, "tree:1");
+
+ traverse_commit_list_filtered(&filter_options, rev,
+ walken_show_commit, walken_show_object, NULL, NULL);
+ }
+----
+
+`struct list_objects_filter_options` is usually built directly from a command
+line argument, so the module provides an easy way to build one from a string.
+Even though we aren't taking user input right now, we can still build one with
+a hardcoded string using `parse_list_objects_filter()`.
+
+With the filter spec "tree:1", we are expecting to see _only_ the root tree for
+each commit; therefore, the tree object count should be less than or equal to
+the number of commits. (For an example of why that's true: `git commit --revert`
+points to the same tree object as its grandparent.)
+
+=== Counting Omitted Objects
+
+We also have the capability to enumerate all objects which were omitted by a
+filter, like with `git log --filter=<spec> --filter-print-omitted`. Asking
+`traverse_commit_list_filtered()` to populate the `omitted` list means that our
+object walk does not perform any better than an unfiltered object walk; all
+reachable objects are walked in order to populate the list.
+
+First, add the `struct oidset` and related items we will use to iterate it:
+
+----
+static void walken_object_walk(
+ ...
+
+ struct oidset omitted;
+ struct oidset_iter oit;
+ struct object_id *oid = NULL;
+ int omitted_count = 0;
+ oidset_init(&omitted, 0);
+
+ ...
+----
+
+Modify the call to `traverse_commit_list_filtered()` to include your `omitted`
+object:
+
+----
+ ...
+
+ traverse_commit_list_filtered(&filter_options, rev,
+ walken_show_commit, walken_show_object, NULL, &omitted);
+
+ ...
+----
+
+Then, after your traversal, the `oidset` traversal is pretty straightforward.
+Count all the objects within and modify the print statement:
+
+----
+ /* Count the omitted objects. */
+ oidset_iter_init(&omitted, &oit);
+
+ while ((oid = oidset_iter_next(&oit)))
+ omitted_count++;
+
+ printf("commits %d\nblobs %d\ntags %d\ntrees%d\nomitted %d\n",
+ commit_count, blob_count, tag_count, tree_count, omitted_count);
+----
+
+By running your walk with and without the filter, you should find that the total
+object count in each case is identical. You can also time each invocation of
+the `walken` subcommand, with and without `omitted` being passed in, to confirm
+to yourself the runtime impact of tracking all omitted objects.
+
+=== Changing the Order
+
+Finally, let's demonstrate that you can also reorder walks of all objects, not
+just walks of commits. First, we'll make our handlers chattier - modify
+`walken_show_commit()` and `walken_show_object()` to print the object as they
+go:
+
+----
+static void walken_show_commit(struct commit *cmt, void *buf)
+{
+ trace_printf("commit: %s\n", oid_to_hex(&cmt->object.oid));
+ commit_count++;
+}
+
+static void walken_show_object(struct object *obj, const char *str, void *buf)
+{
+ trace_printf("%s: %s\n", type_name(obj->type), oid_to_hex(&obj->oid));
+
+ ...
+}
+----
+
+NOTE: Since we will be examining this output directly as humans, we'll use
+`trace_printf()` here. Additionally, since this change introduces a significant
+number of printed lines, using `trace_printf()` will allow us to easily silence
+those lines without having to recompile.
+
+(Leave the counter increment logic in place.)
+
+With only that change, run again (but save yourself some scrollback):
+
+----
+$ GIT_TRACE=1 ./bin-wrappers/git walken | head -n 10
+----
+
+Take a look at the top commit with `git show` and the object ID you printed; it
+should be the same as the output of `git show HEAD`.
+
+Next, let's change a setting on our `struct rev_info` within
+`walken_object_walk()`. Find where you're changing the other settings on `rev`,
+such as `rev->tree_objects` and `rev->tree_blobs_in_commit_order`, and add the
+`reverse` setting at the bottom:
+
+----
+ ...
+
+ rev->tree_objects = 1;
+ rev->blob_objects = 1;
+ rev->tag_objects = 1;
+ rev->tree_blobs_in_commit_order = 1;
+ rev->reverse = 1;
+
+ ...
+----
+
+Now, run again, but this time, let's grab the last handful of objects instead
+of the first handful:
+
+----
+$ make
+$ GIT_TRACE=1 ./bin-wrappers git walken | tail -n 10
+----
+
+The last commit object given should have the same OID as the one we saw at the
+top before, and running `git show <oid>` with that OID should give you again
+the same results as `git show HEAD`. Furthermore, if you run and examine the
+first ten lines again (with `head` instead of `tail` like we did before applying
+the `reverse` setting), you should see that now the first commit printed is the
+initial commit, `e83c5163`.
+
+== Wrapping Up
+
+Let's review. In this tutorial, we:
+
+- Built a commit walk from the ground up
+- Enabled a grep filter for that commit walk
+- Changed the sort order of that filtered commit walk
+- Built an object walk (tags, commits, trees, and blobs) from the ground up
+- Learned how to add a filter-spec to an object walk
+- Changed the display order of the filtered object walk
diff --git a/Documentation/RelNotes/2.24.0.txt b/Documentation/RelNotes/2.24.0.txt
new file mode 100644
index 0000000..bde1541
--- /dev/null
+++ b/Documentation/RelNotes/2.24.0.txt
@@ -0,0 +1,398 @@
+Git 2.24 Release Notes
+======================
+
+Updates since v2.23
+-------------------
+
+Backward compatibility note
+
+ * "filter-branch" is showing its age and alternatives are available.
+ From this release, we started to discourage its use and hint
+ people about filter-repo.
+
+UI, Workflows & Features
+
+ * We now have an active interim maintainer for the Git-Gui part of
+ the system. Praise and thank Pratyush Yadav for volunteering.
+
+ * The command line parser learned "--end-of-options" notation; the
+ standard convention for scripters to have hardcoded set of options
+ first on the command line, and force the command to treat end-user
+ input as non-options, has been to use "--" as the delimiter, but
+ that would not work for commands that use "--" as a delimiter
+ between revs and pathspec.
+
+ * A mechanism to affect the default setting for a (related) group of
+ configuration variables is introduced.
+
+ * "git fetch" learned "--set-upstream" option to help those who first
+ clone from their private fork they intend to push to, add the true
+ upstream via "git remote add" and then "git fetch" from it.
+
+ * Device-tree files learned their own userdiff patterns.
+ (merge 3c81760bc6 sb/userdiff-dts later to maint).
+
+ * "git rebase --rebase-merges" learned to drive different merge
+ strategies and pass strategy specific options to them.
+
+ * A new "pre-merge-commit" hook has been introduced.
+
+ * Command line completion updates for "git -c var.name=val" have been
+ added.
+
+ * The lazy clone machinery has been taught that there can be more
+ than one promisor remote and consult them in order when downloading
+ missing objects on demand.
+
+ * The list-objects-filter API (used to create a sparse/lazy clone)
+ learned to take a combined filter specification.
+
+ * The documentation and tests for "git format-patch" have been
+ cleaned up.
+
+ * On Windows, the root level of UNC share is now allowed to be used
+ just like any other directory.
+
+ * The command line completion support (in contrib/) learned about the
+ "--skip" option of "git revert" and "git cherry-pick".
+
+ * "git rebase --keep-base <upstream>" tries to find the original base
+ of the topic being rebased and rebase on top of that same base,
+ which is useful when running the "git rebase -i" (and its limited
+ variant "git rebase -x").
+
+ The command also has learned to fast-forward in more cases where it
+ can instead of replaying to recreate identical commits.
+
+ * A configuration variable tells "git fetch" to write the commit
+ graph after finishing.
+
+ * "git add -i" has been taught to show the total number of hunks and
+ the hunks that has been processed so far when showing prompts.
+
+ * "git fetch --jobs=<n>" allowed <n> parallel jobs when fetching
+ submodules, but this did not apply to "git fetch --multiple" that
+ fetches from multiple remote repositories. It now does.
+
+ * The installation instruction for zsh completion script (in
+ contrib/) has been a bit improved.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * The code to write commit-graph over given commit object names has
+ been made a bit more robust.
+
+ * The first line of verbose output from each test piece now carries
+ the test name and number to help scanning with eyeballs.
+
+ * Further clean-up of the initialization code.
+
+ * xmalloc() used to have a mechanism to ditch memory and address
+ space resources as the last resort upon seeing an allocation
+ failure from the underlying malloc(), which made the code complex
+ and thread-unsafe with dubious benefit, as major memory resource
+ users already do limit their uses with various other mechanisms.
+ It has been simplified away.
+
+ * Unnecessary full-tree diff in "git log -L" machinery has been
+ optimized away.
+
+ * The http transport lacked some optimization the native transports
+ learned to avoid unnecessary ref advertisement, which has been
+ corrected.
+
+ * Preparation for SHA-256 upgrade continues in the test department.
+ (merge 0c37c41d13 bc/hash-independent-tests-part-5 later to maint).
+
+ * The memory ownership model of the "git fast-import" got
+ straightened out.
+
+ * Output from trace2 subsystem is formatted more prettily now.
+
+ * The internal code originally invented for ".gitignore" processing
+ got reshuffled and renamed to make it less tied to "excluding" and
+ stress more that it is about "matching", as it has been reused for
+ things like sparse checkout specification that want to check if a
+ path is "included".
+
+ * "git stash" learned to write refreshed index back to disk.
+
+ * Coccinelle checks are done on more source files than before now.
+
+ * The cache-tree code has been taught to be less aggressive in
+ attempting to see if a tree object it computed already exists in
+ the repository.
+
+ * The code to parse and use the commit-graph file has been made more
+ robust against corrupted input.
+
+ * The hg-to-git script (in contrib/) has been updated to work with
+ Python 3.
+
+ * Update the way build artifacts in t/helper/ directory are ignored.
+
+ * Preparation for SHA-256 upgrade continues.
+
+ * "git log --graph" for an octopus merge is sometimes colored
+ incorrectly, which is demonstrated and documented but not yet
+ fixed.
+
+ * The trace2 output, when sending them to files in a designated
+ directory, can populate the directory with too many files; a
+ mechanism is introduced to set the maximum number of files and
+ discard further logs when the maximum is reached.
+
+ * We have adopted a Code-of-conduct document.
+ (merge 3f9ef874a7 jk/coc later to maint).
+
+
+Fixes since v2.23
+-----------------
+
+ * "git grep --recurse-submodules" that looks at the working tree
+ files looked at the contents in the index in submodules, instead of
+ files in the working tree.
+ (merge 6a289d45c0 mt/grep-submodules-working-tree later to maint).
+
+ * Codepaths to walk tree objects have been audited for integer
+ overflows and hardened.
+ (merge 5aa02f9868 jk/tree-walk-overflow later to maint).
+
+ * "git pack-refs" can lose refs that are created while running, which
+ is getting corrected.
+ (merge a613d4f817 sc/pack-refs-deletion-racefix later to maint).
+
+ * "git checkout" and "git restore" to re-populate the index from a
+ tree-ish (typically HEAD) did not work correctly for a path that
+ was removed and then added again with the intent-to-add bit, when
+ the corresponding working tree file was empty. This has been
+ corrected.
+
+ * Compilation fix.
+ (merge 70597e8386 rs/nedalloc-fixlets later to maint).
+
+ * "git gui" learned to call the clean-up procedure before exiting.
+ (merge 0d88f3d2c5 py/git-gui-do-quit later to maint).
+
+ * We promoted the "indent heuristics" that decides where to split
+ diff hunks from experimental to the default a few years ago, but
+ some stale documentation still marked it as experimental, which has
+ been corrected.
+ (merge 64e5e1fba1 sg/diff-indent-heuristic-non-experimental later to maint).
+
+ * Fix a mismerge that happened in 2.22 timeframe.
+ (merge acb7da05ac en/checkout-mismerge-fix later to maint).
+
+ * "git archive" recorded incorrect length in extended pax header in
+ some corner cases, which has been corrected.
+ (merge 71d41ff651 rs/pax-extended-header-length-fix later to maint).
+
+ * On-demand object fetching in lazy clone incorrectly tried to fetch
+ commits from submodule projects, while still working in the
+ superproject, which has been corrected.
+ (merge a63694f523 jt/diff-lazy-fetch-submodule-fix later to maint).
+
+ * Prepare get_short_oid() codepath to be thread-safe.
+ (merge 7cfcb16b0e rs/sort-oid-array-thread-safe later to maint).
+
+ * "for-each-ref" and friends that show refs did not protect themselves
+ against ancient tags that did not record tagger names when asked to
+ show "%(taggername)", which have been corrected.
+ (merge 8b3f33ef11 mp/for-each-ref-missing-name-or-email later to maint).
+
+ * The "git am" based backend of "git rebase" ignored the result of
+ updating ".gitattributes" done in one step when replaying
+ subsequent steps.
+ (merge 2c65d90f75 bc/reread-attributes-during-rebase later to maint).
+
+ * Tell cURL library to use the same malloc() implementation, with the
+ xmalloc() wrapper, as the rest of the system, for consistency.
+ (merge 93b980e58f cb/curl-use-xmalloc later to maint).
+
+ * Build fix to adjust .gitignore to unignore a path that we started to track.
+ (merge aac6ff7b5b js/visual-studio later to maint).
+
+ * A few implementation fixes in the notes API.
+ (merge 60fe477a0b mh/notes-duplicate-entries later to maint).
+
+ * Fix an earlier regression to "git push --all" which should have
+ been forbidden when the target remote repository is set to be a
+ mirror.
+ (merge 8e4c8af058 tg/push-all-in-mirror-forbidden later to maint).
+
+ * Fix an earlier regression in the test suite, which mistakenly
+ stopped running HTTPD tests.
+ (merge 3960290675 sg/git-test-boolean later to maint).
+
+ * "git rebase --autostash <upstream> <branch>", when <branch> is
+ different from the current branch, incorrectly moved the tip of the
+ current branch, which has been corrected.
+ (merge bf1e28e0ad bw/rebase-autostash-keep-current-branch later to maint).
+
+ * Update support for Asciidoctor documentation toolchain.
+ (merge 83b0b8953e ma/asciidoctor-refmiscinfo later to maint).
+
+ * Start using DocBook 5 (instead of DocBook 4.5) as Asciidoctor 2.0
+ no longer works with the older one.
+ (merge f6461b82b9 bc/doc-use-docbook-5 later to maint).
+
+ * The markup used in user-manual has been updated to work better with
+ asciidoctor.
+ (merge c4d2f6143a ma/user-manual-markup-update later to maint).
+
+ * Make sure the grep machinery does not abort when seeing a payload
+ that is not UTF-8 even when JIT is not in use with PCRE1.
+ (merge ad7c543e3b cb/skip-utf8-check-with-pcre1 later to maint).
+
+ * The name of the blob object that stores the filter specification
+ for sparse cloning/fetching was interpreted in a wrong place in the
+ code, causing Git to abort.
+
+ * "git log --decorate-refs-exclude=<pattern>" was incorrectly
+ overruled when the "--simplify-by-decoration" option is used, which
+ has been corrected.
+ (merge 0cc7380d88 rs/simplify-by-deco-with-deco-refs-exclude later to maint).
+
+ * The "upload-pack" (the counterpart of "git fetch") needs to disable
+ commit-graph when responding to a shallow clone/fetch request, but
+ the way this was done made Git panic, which has been corrected.
+
+ * The object traversal machinery has been optimized not to load tree
+ objects when we are only interested in commit history.
+ (merge 72ed80c784 jk/list-objects-optim-wo-trees later to maint).
+
+ * The object name parser for "Nth parent" syntax has been made more
+ robust against integer overflows.
+ (merge 59fa5f5a25 rs/nth-parent-parse later to maint).
+
+ * The code used in following tags in "git fetch" has been optimized.
+ (merge b7e2d8bca5 ms/fetch-follow-tag-optim later to maint).
+
+ * Regression fix for progress output.
+ (merge 2bb74b53a4 sg/progress-fix later to maint).
+
+ * A bug in merge-recursive code that triggers when a branch with a
+ symbolic link is merged with a branch that replaces it with a
+ directory has been fixed.
+ (merge 83e3ad3b12 jt/merge-recursive-symlink-is-not-a-dir-in-way later to maint).
+
+ * The rename detection logic sorts a list of rename source candidates
+ by similarity to pick the best candidate, which means that a tie
+ between sources with the same similarity is broken by the original
+ location in the original candidate list (which is sorted by path).
+ Force the sorting by similarity done with a stable sort, which is
+ not promised by system supplied qsort(3), to ensure consistent
+ results across platforms.
+ (merge 2049b8dc65 js/diff-rename-force-stable-sort later to maint).
+
+ * The code to skip "UTF" and "UTF-" prefix, when computing an advice
+ message, did not work correctly when the prefix was "UTF", which
+ has been fixed.
+ (merge b181676ce9 rs/convert-fix-utf-without-dash later to maint).
+
+ * The author names taken from SVN repositories may have extra leading
+ or trailing whitespaces, which are now munged away.
+ (merge 4ddd4bddb1 tk/git-svn-trim-author-name later to maint).
+
+ * "git rebase -i" showed a wrong HEAD while "reword" open the editor.
+ (merge b0a3186140 pw/rebase-i-show-HEAD-to-reword later to maint).
+
+ * A few simplification and bugfixes to PCRE interface.
+ (merge c581e4a749 ab/pcre-jit-fixes later to maint).
+
+ * PCRE fixes.
+ (merge ff61681b46 cb/pcre1-cleanup later to maint).
+
+ * "git range-diff" segfaulted when diff.noprefix configuration was
+ used, as it blindly expected the patch it internally generates to
+ have the standard a/ and b/ prefixes. The command now forces the
+ internal patch to be built without any prefix, not to be affected
+ by any end-user configuration.
+ (merge 937b76ed49 js/range-diff-noprefix later to maint).
+
+ * "git stash apply" in a subdirectory of a secondary worktree failed
+ to access the worktree correctly, which has been corrected.
+ (merge dfd557c978 js/stash-apply-in-secondary-worktree later to maint).
+
+ * The merge-recursive machinery is one of the most complex parts of
+ the system that accumulated cruft over time. This large series
+ cleans up the implementation quite a bit.
+ (merge b657047719 en/merge-recursive-cleanup later to maint).
+
+ * Pretty-printed command line formatter (used in e.g. reporting the
+ command being run by the tracing API) had a bug that lost an
+ argument that is an empty string, which has been corrected.
+ (merge ce2d7ed2fd gs/sq-quote-buf-pretty later to maint).
+
+ * "git range-diff" failed to handle mode-only change, which has been
+ corrected.
+ (merge 2b6a9b13ca tg/range-diff-output-update later to maint).
+
+ * Dev support update.
+ (merge 4f3c1dc5d6 dl/allow-running-cocci-verbosely later to maint).
+
+ * "git format-patch -o <outdir>" did an equivalent of "mkdir <outdir>"
+ not "mkdir -p <outdir>", which was corrected.
+
+ * "git stash save" lost local changes to submodules, which has been
+ corrected.
+ (merge 556895d0c8 jj/stash-reset-only-toplevel later to maint).
+
+ * The atomic push over smart HTTP transport did not work, which has
+ been corrected.
+ (merge 6f1194246a bc/smart-http-atomic-push later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+ (merge d1387d3895 en/fast-import-merge-doc later to maint).
+ (merge 1c24a54ea4 bm/repository-layout-typofix later to maint).
+ (merge 415b770b88 ds/midx-expire-repack later to maint).
+ (merge 19800bdc3f nd/diff-parseopt later to maint).
+ (merge 58166c2e9d tg/t0021-racefix later to maint).
+ (merge 7027f508c7 dl/compat-cleanup later to maint).
+ (merge e770fbfeff jc/test-cleanup later to maint).
+ (merge 1fd881d404 rs/trace2-dst-warning later to maint).
+ (merge 7e92756751 mh/http-urlmatch-cleanup later to maint).
+ (merge 9784f97321 mh/release-commit-memory-fix later to maint).
+ (merge 60d198d022 tb/banned-vsprintf-namefix later to maint).
+ (merge 80e3658647 rs/help-unknown-ref-does-not-return later to maint).
+ (merge 0a8bc7068f dt/remote-helper-doc-re-lock-option later to maint).
+ (merge 27fd1e4ea7 en/merge-options-ff-and-friends later to maint).
+ (merge 502c386ff9 sg/clean-nested-repo-with-ignored later to maint).
+ (merge 26e3d1cbea am/mailmap-andrey-mazo later to maint).
+ (merge 47b27c96fa ss/get-time-cleanup later to maint).
+ (merge dd2e50a84e jk/commit-graph-cleanup later to maint).
+ (merge 4fd39c76e6 cs/pretty-formats-doc-typofix later to maint).
+ (merge 40e747e89d dl/submodule-set-branch later to maint).
+ (merge 689a146c91 rs/commit-graph-use-list-count later to maint).
+ (merge 0eb7c37a8a js/doc-patch-text later to maint).
+ (merge 4b3aa170d1 rs/nth-switch-code-simplification later to maint).
+ (merge 0d4304c124 ah/doc-submodule-ignore-submodules later to maint).
+ (merge af78249463 cc/svn-fe-py-shebang later to maint).
+ (merge 7bd97d6dff rs/alias-use-copy-array later to maint).
+ (merge c46ebc2496 sg/travis-help-debug later to maint).
+ (merge 24c681794f ps/my-first-contribution-alphasort later to maint).
+ (merge 75b2c15435 cb/do-not-use-test-cmp-with-a later to maint).
+ (merge cda0d497e3 bw/submodule-helper-usage-fix later to maint).
+ (merge fe0ed5d5e9 am/visual-studio-config-fix later to maint).
+ (merge 2e09c01232 sg/name-rev-cutoff-underflow-fix later to maint).
+ (merge ddb3c856f3 as/shallow-slab-use-fix later to maint).
+ (merge 71f4960b91 js/mingw-spawn-with-spaces-in-path later to maint).
+ (merge 53d687bf5f ah/cleanups later to maint).
+ (merge f537485fa5 rs/test-remove-useless-debugging-cat later to maint).
+ (merge 11a3d3aadd dl/rev-list-doc-cleanup later to maint).
+ (merge d928a8388a am/t0028-utf16-tests later to maint).
+ (merge b05b40930e dl/t0000-skip-test-test later to maint).
+ (merge 03d3b1297c js/xdiffi-comment-updates later to maint).
+ (merge 57d8f4b4c7 js/doc-stash-save later to maint).
+ (merge 8c1cfd58e3 ta/t1308-typofix later to maint).
+ (merge fa364ad790 bb/utf8-wcwidth-cleanup later to maint).
+ (merge 68b69211b2 bb/compat-util-comment-fix later to maint).
+ (merge 5cc6a4be11 rs/http-push-simplify later to maint).
+ (merge a81e42d235 rs/column-use-utf8-strnwidth later to maint).
+ (merge 062a309d36 rs/remote-curl-use-argv-array later to maint).
+ (merge 3b3c79f6c9 nr/diff-highlight-indent-fix later to maint).
+ (merge 3444ec2eb2 wb/fsmonitor-bitmap-fix later to maint).
+ (merge 10da030ab7 cb/pcre2-chartables-leakfix later to maint).
+ (merge 60e6569a12 js/mingw-needs-hiding-fix later to maint).
+ (merge 52bd3e4657 rl/gitweb-blame-prev-fix later to maint).
diff --git a/Documentation/RelNotes/2.7.1.txt b/Documentation/RelNotes/2.7.1.txt
index 6553d69..6323fea 100644
--- a/Documentation/RelNotes/2.7.1.txt
+++ b/Documentation/RelNotes/2.7.1.txt
@@ -10,7 +10,7 @@ Fixes since v2.7
setting GIT_WORK_TREE environment themselves.
* The "exclude_list" structure has the usual "alloc, nr" pair of
- fields to be used by ALLOC_GROW(), but clear_exclude_list() forgot
+ fields to be used by ALLOC_GROW(), but clear_pattern_list() forgot
to reset 'alloc' to 0 when it cleared 'nr' to discard the managed
array.
diff --git a/Documentation/RelNotes/2.8.0.txt b/Documentation/RelNotes/2.8.0.txt
index 2507971..5fbe1b8 100644
--- a/Documentation/RelNotes/2.8.0.txt
+++ b/Documentation/RelNotes/2.8.0.txt
@@ -270,7 +270,7 @@ notes for details).
setting GIT_WORK_TREE environment themselves.
* The "exclude_list" structure has the usual "alloc, nr" pair of
- fields to be used by ALLOC_GROW(), but clear_exclude_list() forgot
+ fields to be used by ALLOC_GROW(), but clear_pattern_list() forgot
to reset 'alloc' to 0 when it cleared 'nr' to discard the managed
array.
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index 6d589e1..1a60cc1 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -372,9 +372,9 @@ such as "Thanks-to:", "Based-on-patch-by:", or "Mentored-by:".
Some parts of the system have dedicated maintainers with their own
repositories.
-- `git-gui/` comes from git-gui project, maintained by Pat Thoyts:
+- `git-gui/` comes from git-gui project, maintained by Pratyush Yadav:
- git://repo.or.cz/git-gui.git
+ https://github.com/prati0100/git-gui.git
- `gitk-git/` comes from Paul Mackerras's gitk project:
diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf
index 2c16c53..8fc4b67 100644
--- a/Documentation/asciidoc.conf
+++ b/Documentation/asciidoc.conf
@@ -78,9 +78,9 @@ template::[header-declarations]
<refmeta>
<refentrytitle>{mantitle}</refentrytitle>
<manvolnum>{manvolnum}</manvolnum>
-<refmiscinfo class="source">Git</refmiscinfo>
-<refmiscinfo class="version">{git_version}</refmiscinfo>
-<refmiscinfo class="manual">Git Manual</refmiscinfo>
+<refmiscinfo class="source">{mansource}</refmiscinfo>
+<refmiscinfo class="version">{manversion}</refmiscinfo>
+<refmiscinfo class="manual">{manmanual}</refmiscinfo>
</refmeta>
<refnamediv>
<refname>{manname}</refname>
diff --git a/Documentation/asciidoctor-extensions.rb b/Documentation/asciidoctor-extensions.rb
index 0089e0c..d906a00 100644
--- a/Documentation/asciidoctor-extensions.rb
+++ b/Documentation/asciidoctor-extensions.rb
@@ -9,8 +9,11 @@ module Git
named :chrome
def process(parent, target, attrs)
- if parent.document.basebackend? 'html'
- prefix = parent.document.attr('git-relative-html-prefix')
+ prefix = parent.document.attr('git-relative-html-prefix')
+ if parent.document.doctype == 'book'
+ "<ulink url=\"#{prefix}#{target}.html\">" \
+ "#{target}(#{attrs[1]})</ulink>"
+ elsif parent.document.basebackend? 'html'
%(<a href="#{prefix}#{target}.html">#{target}(#{attrs[1]})</a>)
elsif parent.document.basebackend? 'docbook'
"<citerefentry>\n" \
@@ -20,9 +23,26 @@ module Git
end
end
end
+
+ class DocumentPostProcessor < Asciidoctor::Extensions::Postprocessor
+ def process document, output
+ if document.basebackend? 'docbook'
+ mansource = document.attributes['mansource']
+ manversion = document.attributes['manversion']
+ manmanual = document.attributes['manmanual']
+ new_tags = "" \
+ "<refmiscinfo class=\"source\">#{mansource}</refmiscinfo>\n" \
+ "<refmiscinfo class=\"version\">#{manversion}</refmiscinfo>\n" \
+ "<refmiscinfo class=\"manual\">#{manmanual}</refmiscinfo>\n"
+ output = output.sub(/<\/refmeta>/, new_tags + "</refmeta>")
+ end
+ output
+ end
+ end
end
end
Asciidoctor::Extensions.register do
inline_macro Git::Documentation::LinkGitProcessor, :linkgit
+ postprocessor Git::Documentation::DocumentPostProcessor
end
diff --git a/Documentation/config.txt b/Documentation/config.txt
index e3f5bc3..f50f1b4 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -178,47 +178,49 @@ to either specify only the realpath version, or both versions.
Example
~~~~~~~
- # Core variables
- [core]
- ; Don't trust file modes
- filemode = false
-
- # Our diff algorithm
- [diff]
- external = /usr/local/bin/diff-wrapper
- renames = true
-
- [branch "devel"]
- remote = origin
- merge = refs/heads/devel
-
- # Proxy settings
- [core]
- gitProxy="ssh" for "kernel.org"
- gitProxy=default-proxy ; for the rest
-
- [include]
- path = /path/to/foo.inc ; include by absolute path
- path = foo.inc ; find "foo.inc" relative to the current file
- path = ~/foo.inc ; find "foo.inc" in your `$HOME` directory
-
- ; include if $GIT_DIR is /path/to/foo/.git
- [includeIf "gitdir:/path/to/foo/.git"]
- path = /path/to/foo.inc
-
- ; include for all repositories inside /path/to/group
- [includeIf "gitdir:/path/to/group/"]
- path = /path/to/foo.inc
-
- ; include for all repositories inside $HOME/to/group
- [includeIf "gitdir:~/to/group/"]
- path = /path/to/foo.inc
-
- ; relative paths are always relative to the including
- ; file (if the condition is true); their location is not
- ; affected by the condition
- [includeIf "gitdir:/path/to/group/"]
- path = foo.inc
+----
+# Core variables
+[core]
+ ; Don't trust file modes
+ filemode = false
+
+# Our diff algorithm
+[diff]
+ external = /usr/local/bin/diff-wrapper
+ renames = true
+
+[branch "devel"]
+ remote = origin
+ merge = refs/heads/devel
+
+# Proxy settings
+[core]
+ gitProxy="ssh" for "kernel.org"
+ gitProxy=default-proxy ; for the rest
+
+[include]
+ path = /path/to/foo.inc ; include by absolute path
+ path = foo.inc ; find "foo.inc" relative to the current file
+ path = ~/foo.inc ; find "foo.inc" in your `$HOME` directory
+
+; include if $GIT_DIR is /path/to/foo/.git
+[includeIf "gitdir:/path/to/foo/.git"]
+ path = /path/to/foo.inc
+
+; include for all repositories inside /path/to/group
+[includeIf "gitdir:/path/to/group/"]
+ path = /path/to/foo.inc
+
+; include for all repositories inside $HOME/to/group
+[includeIf "gitdir:~/to/group/"]
+ path = /path/to/foo.inc
+
+; relative paths are always relative to the including
+; file (if the condition is true); their location is not
+; affected by the condition
+[includeIf "gitdir:/path/to/group/"]
+ path = foo.inc
+----
; include only if we are in a worktree where foo-branch is
; currently checked out
@@ -345,6 +347,8 @@ include::config/difftool.txt[]
include::config/fastimport.txt[]
+include::config/feature.txt[]
+
include::config/fetch.txt[]
include::config/format.txt[]
diff --git a/Documentation/config/core.txt b/Documentation/config/core.txt
index 75538d2..852d2ba 100644
--- a/Documentation/config/core.txt
+++ b/Documentation/config/core.txt
@@ -86,7 +86,9 @@ core.untrackedCache::
it will automatically be removed, if set to `false`. Before
setting it to `true`, you should check that mtime is working
properly on your system.
- See linkgit:git-update-index[1]. `keep` by default.
+ See linkgit:git-update-index[1]. `keep` by default, unless
+ `feature.manyFiles` is enabled which sets this setting to
+ `true` by default.
core.checkStat::
When missing or is set to `default`, many fields in the stat
@@ -577,7 +579,7 @@ the `GIT_NOTES_REF` environment variable. See linkgit:git-notes[1].
core.commitGraph::
If true, then git will read the commit-graph file (if it exists)
- to parse the graph structure of commits. Defaults to false. See
+ to parse the graph structure of commits. Defaults to true. See
linkgit:git-commit-graph[1] for more information.
core.useReplaceRefs::
diff --git a/Documentation/config/diff.txt b/Documentation/config/diff.txt
index 5afb5a2..ff09f1c 100644
--- a/Documentation/config/diff.txt
+++ b/Documentation/config/diff.txt
@@ -189,7 +189,7 @@ diff.guitool::
include::../mergetools-diff.txt[]
diff.indentHeuristic::
- Set this option to `true` to enable experimental heuristics
+ Set this option to `false` to disable the default heuristics
that shift diff hunk boundaries to make patches easier to read.
diff.algorithm::
diff --git a/Documentation/config/feature.txt b/Documentation/config/feature.txt
new file mode 100644
index 0000000..875f8c8
--- /dev/null
+++ b/Documentation/config/feature.txt
@@ -0,0 +1,37 @@
+feature.*::
+ The config settings that start with `feature.` modify the defaults of
+ a group of other config settings. These groups are created by the Git
+ developer community as recommended defaults and are subject to change.
+ In particular, new config options may be added with different defaults.
+
+feature.experimental::
+ Enable config options that are new to Git, and are being considered for
+ future defaults. Config settings included here may be added or removed
+ with each release, including minor version updates. These settings may
+ have unintended interactions since they are so new. Please enable this
+ setting if you are interested in providing feedback on experimental
+ features. The new default values are:
++
+* `pack.useSparse=true` uses a new algorithm when constructing a pack-file
+which can improve `git push` performance in repos with many files.
++
+* `fetch.negotiationAlgorithm=skipping` may improve fetch negotiation times by
+skipping more commits at a time, reducing the number of round trips.
++
+* `fetch.writeCommitGraph=true` writes a commit-graph after every `git fetch`
+command that downloads a pack-file from a remote. Using the `--split` option,
+most executions will create a very small commit-graph file on top of the
+existing commit-graph file(s). Occasionally, these files will merge and the
+write may take longer. Having an updated commit-graph file helps performance
+of many Git commands, including `git merge-base`, `git push -f`, and
+`git log --graph`.
+
+feature.manyFiles::
+ Enable config options that optimize for repos with many files in the
+ working directory. With many files, commands such as `git status` and
+ `git checkout` may be slow and these new defaults improve performance:
++
+* `index.version=4` enables path-prefix compression in the index.
++
+* `core.untrackedCache=true` enables the untracked cache. This setting assumes
+that mtime is working on your machine.
diff --git a/Documentation/config/fetch.txt b/Documentation/config/fetch.txt
index ba890b5..f119402 100644
--- a/Documentation/config/fetch.txt
+++ b/Documentation/config/fetch.txt
@@ -59,7 +59,8 @@ fetch.negotiationAlgorithm::
effort to converge faster, but may result in a larger-than-necessary
packfile; The default is "default" which instructs Git to use the default algorithm
that never skips commits (unless the server has acknowledged it or one
- of its descendants).
+ of its descendants). If `feature.experimental` is enabled, then this
+ setting defaults to "skipping".
Unknown values will cause 'git fetch' to error out.
+
See also the `--negotiation-tip` option for linkgit:git-fetch[1].
@@ -68,3 +69,23 @@ fetch.showForcedUpdates::
Set to false to enable `--no-show-forced-updates` in
linkgit:git-fetch[1] and linkgit:git-pull[1] commands.
Defaults to true.
+
+fetch.parallel::
+ Specifies the maximal number of fetch operations to be run in parallel
+ at a time (submodules, or remotes when the `--multiple` option of
+ linkgit:git-fetch[1] is in effect).
++
+A value of 0 will give some reasonable default. If unset, it defaults to 1.
++
+For submodules, this setting can be overridden using the `submodule.fetchJobs`
+config setting.
+
+fetch.writeCommitGraph::
+ Set to true to write a commit-graph after every `git fetch` command
+ that downloads a pack-file from a remote. Using the `--split` option,
+ most executions will create a very small commit-graph file on top of
+ the existing commit-graph file(s). Occasionally, these files will
+ merge and the write may take longer. Having an updated commit-graph
+ file helps performance of many Git commands, including `git merge-base`,
+ `git push -f`, and `git log --graph`. Defaults to false, unless
+ `feature.experimental` is true.
diff --git a/Documentation/config/format.txt b/Documentation/config/format.txt
index 414a5a8..513fcd8 100644
--- a/Documentation/config/format.txt
+++ b/Documentation/config/format.txt
@@ -36,6 +36,12 @@ format.subjectPrefix::
The default for format-patch is to output files with the '[PATCH]'
subject prefix. Use this variable to change that prefix.
+format.coverFromDescription::
+ The default mode for format-patch to determine which parts of
+ the cover letter will be populated using the branch's
+ description. See the `--cover-from-description` option in
+ linkgit:git-format-patch[1].
+
format.signature::
The default for format-patch is to output a signature containing
the Git version number. Use this variable to change that default.
@@ -77,10 +83,11 @@ format.coverLetter::
A boolean that controls whether to generate a cover-letter when
format-patch is invoked, but in addition can be set to "auto", to
generate a cover-letter only when there's more than one patch.
+ Default is false.
format.outputDirectory::
Set a custom directory to store the resulting files instead of the
- current working directory.
+ current working directory. All directory components will be created.
format.useAutoBase::
A boolean value which lets you enable the `--base=auto` option of
diff --git a/Documentation/config/gc.txt b/Documentation/config/gc.txt
index 02b92b1..00ea0a6 100644
--- a/Documentation/config/gc.txt
+++ b/Documentation/config/gc.txt
@@ -63,7 +63,7 @@ gc.writeCommitGraph::
If true, then gc will rewrite the commit-graph file when
linkgit:git-gc[1] is run. When using `git gc --auto`
the commit-graph will be updated if housekeeping is
- required. Default is false. See linkgit:git-commit-graph[1]
+ required. Default is true. See linkgit:git-commit-graph[1]
for details.
gc.logExpiry::
diff --git a/Documentation/config/index.txt b/Documentation/config/index.txt
index f181503..7cb50b3 100644
--- a/Documentation/config/index.txt
+++ b/Documentation/config/index.txt
@@ -24,3 +24,4 @@ index.threads::
index.version::
Specify the version with which new index files should be
initialized. This does not affect existing repositories.
+ If `feature.manyFiles` is enabled, then the default is 4.
diff --git a/Documentation/config/pack.txt b/Documentation/config/pack.txt
index 9cdcfa7..1d66f0c 100644
--- a/Documentation/config/pack.txt
+++ b/Documentation/config/pack.txt
@@ -112,7 +112,8 @@ pack.useSparse::
objects. This can have significant performance benefits when
computing a pack to send a small change. However, it is possible
that extra objects are added to the pack-file if the included
- commits contain certain types of direct renames.
+ commits contain certain types of direct renames. Default is `false`
+ unless `feature.experimental` is enabled.
pack.writeBitmaps (deprecated)::
This is a deprecated synonym for `repack.writeBitmaps`.
diff --git a/Documentation/config/remote.txt b/Documentation/config/remote.txt
index 6c4cad8..a8e6437 100644
--- a/Documentation/config/remote.txt
+++ b/Documentation/config/remote.txt
@@ -76,3 +76,11 @@ remote.<name>.pruneTags::
+
See also `remote.<name>.prune` and the PRUNING section of
linkgit:git-fetch[1].
+
+remote.<name>.promisor::
+ When set to true, this remote will be used to fetch promisor
+ objects.
+
+remote.<name>.partialclonefilter::
+ The filter that will be applied when fetching from this
+ promisor remote.
diff --git a/Documentation/config/trace2.txt b/Documentation/config/trace2.txt
index 2edbfb0..4ce0b9a 100644
--- a/Documentation/config/trace2.txt
+++ b/Documentation/config/trace2.txt
@@ -54,3 +54,9 @@ trace2.destinationDebug::
By default, these errors are suppressed and tracing is
silently disabled. May be overridden by the
`GIT_TRACE2_DST_DEBUG` environment variable.
+
+trace2.maxFiles::
+ Integer. When writing trace files to a target directory, do not
+ write additional traces if we would exceed this many files. Instead,
+ write a sentinel file that will block further tracing to this
+ directory. Defaults to 0, which disables this check.
diff --git a/Documentation/diff-generate-patch.txt b/Documentation/diff-generate-patch.txt
index f10ca41..e8ed647 100644
--- a/Documentation/diff-generate-patch.txt
+++ b/Documentation/diff-generate-patch.txt
@@ -1,11 +1,15 @@
-Generating patches with -p
---------------------------
-
-When "git-diff-index", "git-diff-tree", or "git-diff-files" are run
-with a `-p` option, "git diff" without the `--raw` option, or
-"git log" with the "-p" option, they
-do not produce the output described above; instead they produce a
-patch file. You can customize the creation of such patches via the
+Generating patch text with -p
+-----------------------------
+
+Running
+linkgit:git-diff[1],
+linkgit:git-log[1],
+linkgit:git-show[1],
+linkgit:git-diff-index[1],
+linkgit:git-diff-tree[1], or
+linkgit:git-diff-files[1]
+with the `-p` option produces patch text.
+You can customize the creation of patch text via the
`GIT_EXTERNAL_DIFF` and the `GIT_DIFF_OPTS` environment variables.
What the -p option produces is slightly different from the traditional
@@ -49,7 +53,7 @@ similarity index value of 100% is thus reserved for two equal
files, while 100% dissimilarity means that no line from the old
file made it into the new one.
+
-The index line includes the SHA-1 checksum before and after the change.
+The index line includes the blob object names before and after the change.
The <mode> is included if the file mode does not change; otherwise,
separate lines indicate the old and the new mode.
@@ -70,7 +74,7 @@ separate lines indicate the old and the new mode.
rename to a
-combined diff format
+Combined diff format
--------------------
Any diff-generating command can take the `-c` or `--cc` option to
@@ -80,7 +84,7 @@ linkgit:git-show[1]. Note also that you can give the `-m` option to any
of these commands to force generation of diffs with individual parents
of a merge.
-A 'combined diff' format looks like this:
+A "combined diff" format looks like this:
------------
diff --combined describe.c
@@ -113,11 +117,11 @@ index fabadb8,cc95eb0..4866510
------------
1. It is preceded with a "git diff" header, that looks like
- this (when `-c` option is used):
+ this (when the `-c` option is used):
diff --combined file
+
-or like this (when `--cc` option is used):
+or like this (when the `--cc` option is used):
diff --cc file
@@ -160,7 +164,7 @@ parents.
4. Chunk header format is modified to prevent people from
accidentally feeding it to `patch -p1`. Combined diff format
was created for review of merge commit changes, and was not
- meant for apply. The change is similar to the change in the
+ meant to be applied. The change is similar to the change in the
extended 'index' header:
@@@ <from-file-range> <from-file-range> <to-file-range> @@@
diff --git a/Documentation/doc-diff b/Documentation/doc-diff
index 3355be4..88a9b20 100755
--- a/Documentation/doc-diff
+++ b/Documentation/doc-diff
@@ -21,7 +21,7 @@ asciidoc use asciidoc with both commits
to-asciidoc use asciidoc with the 'to'-commit
to-asciidoctor use asciidoctor with the 'to'-commit
asciidoctor use asciidoctor with both commits
-cut-header-footer cut away header and footer
+cut-footer cut away footer
"
SUBDIRECTORY_OK=1
. "$(git --exec-path)/git-sh-setup"
@@ -31,7 +31,7 @@ force=
clean=
from_program=
to_program=
-cut_header_footer=
+cut_footer=
while test $# -gt 0
do
case "$1" in
@@ -55,8 +55,8 @@ do
--asciidoc)
from_program=-asciidoc
to_program=-asciidoc ;;
- --cut-header-footer)
- cut_header_footer=-cut-header-footer ;;
+ --cut-footer)
+ cut_footer=-cut-footer ;;
--)
shift; break ;;
*)
@@ -118,8 +118,8 @@ construct_makemanflags () {
from_makemanflags=$(construct_makemanflags "$from_program") &&
to_makemanflags=$(construct_makemanflags "$to_program") &&
-from_dir=$from_oid$from_program$cut_header_footer &&
-to_dir=$to_oid$to_program$cut_header_footer &&
+from_dir=$from_oid$from_program$cut_footer &&
+to_dir=$to_oid$to_program$cut_footer &&
# generate_render_makefile <srcdir> <dstdir>
generate_render_makefile () {
@@ -169,12 +169,11 @@ render_tree () {
make -j$parallel -f - &&
mv "$tmp/rendered/$dname+" "$tmp/rendered/$dname"
- if test "$cut_header_footer" = "-cut-header-footer"
+ if test "$cut_footer" = "-cut-footer"
then
for f in $(find "$tmp/rendered/$dname" -type f)
do
- tail -n +3 "$f" | head -n -2 |
- sed -e '1{/^$/d}' -e '${/^$/d}' >"$f+" &&
+ head -n -2 "$f" | sed -e '${/^$/d}' >"$f+" &&
mv "$f+" "$f" ||
return 1
done
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index 3c9b4f9..43b9ff3 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -160,15 +160,27 @@ ifndef::git-pull[]
-j::
--jobs=<n>::
- Number of parallel children to be used for fetching submodules.
- Each will fetch from different submodules, such that fetching many
- submodules will be faster. By default submodules will be fetched
- one at a time.
+ Number of parallel children to be used for all forms of fetching.
++
+If the `--multiple` option was specified, the different remotes will be fetched
+in parallel. If multiple submodules are fetched, they will be fetched in
+parallel. To control them independently, use the config settings
+`fetch.parallel` and `submodule.fetchJobs` (see linkgit:git-config[1]).
++
+Typically, parallel recursive and multi-remote fetches will be faster. By
+default fetches are performed sequentially, not in parallel.
--no-recurse-submodules::
Disable recursive fetching of submodules (this has the same effect as
using the `--recurse-submodules=no` option).
+--set-upstream::
+ If the remote is fetched successfully, pull and add upstream
+ (tracking) reference, used by argument-less
+ linkgit:git-pull[1] and other commands. For more information,
+ see `branch.<name>.merge` and `branch.<name>.remote` in
+ linkgit:git-config[1].
+
--submodule-prefix=<path>::
Prepend <path> to paths printed in informative messages
such as "Fetching submodule foo". This option is used
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index fc3b993..fc5750b 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -177,7 +177,7 @@ default. You can use `--no-utf8` to override this.
untouched.
--show-current-patch::
- Show the patch being applied when "git am" is stopped because
+ Show the entire e-mail message "git am" has stopped at, because
of conflicts.
DISCUSSION
diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt
index 0028ff1..a7f309d 100644
--- a/Documentation/git-clean.txt
+++ b/Documentation/git-clean.txt
@@ -26,18 +26,20 @@ are affected.
OPTIONS
-------
-d::
- Remove untracked directories in addition to untracked files.
- If an untracked directory is managed by a different Git
- repository, it is not removed by default. Use -f option twice
- if you really want to remove such a directory.
+ Normally, when no <path> is specified, git clean will not
+ recurse into untracked directories to avoid removing too much.
+ Specify -d to have it recurse into such directories as well.
+ If any paths are specified, -d is irrelevant; all untracked
+ files matching the specified paths (with exceptions for nested
+ git directories mentioned under `--force`) will be removed.
-f::
--force::
If the Git configuration variable clean.requireForce is not set
to false, 'git clean' will refuse to delete files or directories
- unless given -f, -n or -i. Git will refuse to delete directories
- with .git sub directory or file unless a second -f
- is given.
+ unless given -f or -i. Git will refuse to modify untracked
+ nested git repositories (directories with a .git subdirectory)
+ unless a second -f is given.
-i::
--interactive::
diff --git a/Documentation/git-commit-graph.txt b/Documentation/git-commit-graph.txt
index eb5e786..8c708a7 100644
--- a/Documentation/git-commit-graph.txt
+++ b/Documentation/git-commit-graph.txt
@@ -10,8 +10,8 @@ SYNOPSIS
--------
[verse]
'git commit-graph read' [--object-dir <dir>]
-'git commit-graph verify' [--object-dir <dir>] [--shallow]
-'git commit-graph write' <options> [--object-dir <dir>]
+'git commit-graph verify' [--object-dir <dir>] [--shallow] [--[no-]progress]
+'git commit-graph write' <options> [--object-dir <dir>] [--[no-]progress]
DESCRIPTION
@@ -29,6 +29,9 @@ OPTIONS
commit-graph file is expected to be in the `<dir>/info` directory and
the packfiles are expected to be in `<dir>/pack`.
+--[no-]progress::
+ Turn progress on/off explicitly. If neither is specified, progress is
+ shown if standard error is connected to a terminal.
COMMANDS
--------
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 7628193..afa7b75 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -282,18 +282,20 @@ FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].)
--untracked-files[=<mode>]::
Show untracked files.
+
+--
The mode parameter is optional (defaults to 'all'), and is used to
specify the handling of untracked files; when -u is not used, the
default is 'normal', i.e. show untracked files and directories.
-+
+
The possible options are:
-+
+
- 'no' - Show no untracked files
- 'normal' - Shows untracked files and directories
- 'all' - Also shows individual files in untracked directories.
-+
+
The default can be changed using the status.showUntrackedFiles
configuration variable documented in linkgit:git-config[1].
+--
-v::
--verbose::
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index ff9310f..899e92a 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -339,33 +339,35 @@ EXAMPLES
Given a .git/config like this:
- #
- # This is the config file, and
- # a '#' or ';' character indicates
- # a comment
- #
-
- ; core variables
- [core]
- ; Don't trust file modes
- filemode = false
-
- ; Our diff algorithm
- [diff]
- external = /usr/local/bin/diff-wrapper
- renames = true
-
- ; Proxy settings
- [core]
- gitproxy=proxy-command for kernel.org
- gitproxy=default-proxy ; for all the rest
-
- ; HTTP
- [http]
- sslVerify
- [http "https://weak.example.com"]
- sslVerify = false
- cookieFile = /tmp/cookie.txt
+------------
+#
+# This is the config file, and
+# a '#' or ';' character indicates
+# a comment
+#
+
+; core variables
+[core]
+ ; Don't trust file modes
+ filemode = false
+
+; Our diff algorithm
+[diff]
+ external = /usr/local/bin/diff-wrapper
+ renames = true
+
+; Proxy settings
+[core]
+ gitproxy=proxy-command for kernel.org
+ gitproxy=default-proxy ; for all the rest
+
+; HTTP
+[http]
+ sslVerify
+[http "https://weak.example.com"]
+ sslVerify = false
+ cookieFile = /tmp/cookie.txt
+------------
you can set the filemode to true with
diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt
index cc940eb..37634bf 100644
--- a/Documentation/git-fast-export.txt
+++ b/Documentation/git-fast-export.txt
@@ -17,9 +17,9 @@ This program dumps the given revisions in a form suitable to be piped
into 'git fast-import'.
You can use it as a human-readable bundle replacement (see
-linkgit:git-bundle[1]), or as a kind of an interactive
-'git filter-branch'.
-
+linkgit:git-bundle[1]), or as a format that can be edited before being
+fed to 'git fast-import' in order to do history rewrites (an ability
+relied on by tools like 'git filter-repo').
OPTIONS
-------
@@ -75,11 +75,20 @@ produced incorrect results if you gave these options.
Before processing any input, load the marks specified in
<file>. The input file must exist, must be readable, and
must use the same format as produced by --export-marks.
+
+--mark-tags::
+ In addition to labelling blobs and commits with mark ids, also
+ label tags. This is useful in conjunction with
+ `--export-marks` and `--import-marks`, and is also useful (and
+ necessary) for exporting of nested tags. It does not hurt
+ other cases and would be the default, but many fast-import
+ frontends are not prepared to accept tags with mark
+ identifiers.
+
-Any commits that have already been marked will not be exported again.
-If the backend uses a similar --import-marks file, this allows for
-incremental bidirectional exporting of the repository by keeping the
-marks the same across runs.
+Any commits (or tags) that have already been marked will not be
+exported again. If the backend uses a similar --import-marks file,
+this allows for incremental bidirectional exporting of the repository
+by keeping the marks the same across runs.
--fake-missing-tagger::
Some old repositories have tags without a tagger. The
diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt
index fad327a..a3f1e0c 100644
--- a/Documentation/git-fast-import.txt
+++ b/Documentation/git-fast-import.txt
@@ -337,6 +337,13 @@ and control the current import process. More detailed discussion
`commit` command. This command is optional and is not
needed to perform an import.
+`alias`::
+ Record that a mark refers to a given object without first
+ creating any new object. Using --import-marks and referring
+ to missing marks will cause fast-import to fail, so aliases
+ can provide a way to set otherwise pruned commits to a valid
+ value (e.g. the nearest non-pruned ancestor).
+
`checkpoint`::
Forces fast-import to close the current packfile, generate its
unique SHA-1 checksum and index, and start a new packfile.
@@ -391,7 +398,7 @@ change to the project.
('encoding' SP <encoding>)?
data
('from' SP <commit-ish> LF)?
- ('merge' SP <commit-ish> LF)?
+ ('merge' SP <commit-ish> LF)*
(filemodify | filedelete | filecopy | filerename | filedeleteall | notemodify)*
LF?
....
@@ -774,6 +781,7 @@ lightweight (non-annotated) tags see the `reset` command below.
....
'tag' SP <name> LF
+ mark?
'from' SP <commit-ish> LF
original-oid?
'tagger' (SP <name>)? SP LT <email> GT SP <when> LF
@@ -913,6 +921,21 @@ a data chunk which does not have an LF as its last byte.
+
The `LF` after `<delim> LF` is optional (it used to be required).
+`alias`
+~~~~~~~
+Record that a mark refers to a given object without first creating any
+new object.
+
+....
+ 'alias' LF
+ mark
+ 'to' SP <commit-ish> LF
+ LF?
+....
+
+For a detailed description of `<commit-ish>` see above under `from`.
+
+
`checkpoint`
~~~~~~~~~~~~
Forces fast-import to close the current packfile, start a new one, and to
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index 6b53dd7..5876598 100644
--- a/Documentation/git-filter-branch.txt
+++ b/Documentation/git-filter-branch.txt
@@ -16,6 +16,19 @@ SYNOPSIS
[--original <namespace>] [-d <directory>] [-f | --force]
[--state-branch <branch>] [--] [<rev-list options>...]
+WARNING
+-------
+'git filter-branch' has a plethora of pitfalls that can produce non-obvious
+manglings of the intended history rewrite (and can leave you with little
+time to investigate such problems since it has such abysmal performance).
+These safety and performance issues cannot be backward compatibly fixed and
+as such, its use is not recommended. Please use an alternative history
+filtering tool such as https://github.com/newren/git-filter-repo/[git
+filter-repo]. If you still need to use 'git filter-branch', please
+carefully read <<SAFETY>> (and <<PERFORMANCE>>) to learn about the land
+mines of filter-branch, and then vigilantly avoid as many of the hazards
+listed there as reasonably possible.
+
DESCRIPTION
-----------
Lets you rewrite Git revision history by rewriting the branches mentioned
@@ -445,36 +458,236 @@ warned.
(or if your git-gc is not new enough to support arguments to
`--prune`, use `git repack -ad; git prune` instead).
-NOTES
------
-
-git-filter-branch allows you to make complex shell-scripted rewrites
-of your Git history, but you probably don't need this flexibility if
-you're simply _removing unwanted data_ like large files or passwords.
-For those operations you may want to consider
-http://rtyley.github.io/bfg-repo-cleaner/[The BFG Repo-Cleaner],
-a JVM-based alternative to git-filter-branch, typically at least
-10-50x faster for those use-cases, and with quite different
-characteristics:
-
-* Any particular version of a file is cleaned exactly _once_. The BFG,
- unlike git-filter-branch, does not give you the opportunity to
- handle a file differently based on where or when it was committed
- within your history. This constraint gives the core performance
- benefit of The BFG, and is well-suited to the task of cleansing bad
- data - you don't care _where_ the bad data is, you just want it
- _gone_.
-
-* By default The BFG takes full advantage of multi-core machines,
- cleansing commit file-trees in parallel. git-filter-branch cleans
- commits sequentially (i.e. in a single-threaded manner), though it
- _is_ possible to write filters that include their own parallelism,
- in the scripts executed against each commit.
-
-* The http://rtyley.github.io/bfg-repo-cleaner/#examples[command options]
- are much more restrictive than git-filter branch, and dedicated just
- to the tasks of removing unwanted data- e.g:
- `--strip-blobs-bigger-than 1M`.
+[[PERFORMANCE]]
+PERFORMANCE
+-----------
+
+The performance of git-filter-branch is glacially slow; its design makes it
+impossible for a backward-compatible implementation to ever be fast:
+
+* In editing files, git-filter-branch by design checks out each and
+every commit as it existed in the original repo. If your repo has 10\^5
+files and 10\^5 commits, but each commit only modifies 5 files, then
+git-filter-branch will make you do 10\^10 modifications, despite only
+having (at most) 5*10^5 unique blobs.
+
+* If you try and cheat and try to make git-filter-branch only work on
+files modified in a commit, then two things happen
+
+ ** you run into problems with deletions whenever the user is simply
+ trying to rename files (because attempting to delete files that
+ don't exist looks like a no-op; it takes some chicanery to remap
+ deletes across file renames when the renames happen via arbitrary
+ user-provided shell)
+
+ ** even if you succeed at the map-deletes-for-renames chicanery, you
+ still technically violate backward compatibility because users are
+ allowed to filter files in ways that depend upon topology of
+ commits instead of filtering solely based on file contents or names
+ (though this has not been observed in the wild).
+
+* Even if you don't need to edit files but only want to e.g. rename or
+remove some and thus can avoid checking out each file (i.e. you can use
+--index-filter), you still are passing shell snippets for your filters.
+This means that for every commit, you have to have a prepared git repo
+where those filters can be run. That's a significant setup.
+
+* Further, several additional files are created or updated per commit by
+git-filter-branch. Some of these are for supporting the convenience
+functions provided by git-filter-branch (such as map()), while others
+are for keeping track of internal state (but could have also been
+accessed by user filters; one of git-filter-branch's regression tests
+does so). This essentially amounts to using the filesystem as an IPC
+mechanism between git-filter-branch and the user-provided filters.
+Disks tend to be a slow IPC mechanism, and writing these files also
+effectively represents a forced synchronization point between separate
+processes that we hit with every commit.
+
+* The user-provided shell commands will likely involve a pipeline of
+commands, resulting in the creation of many processes per commit.
+Creating and running another process takes a widely varying amount of
+time between operating systems, but on any platform it is very slow
+relative to invoking a function.
+
+* git-filter-branch itself is written in shell, which is kind of slow.
+This is the one performance issue that could be backward-compatibly
+fixed, but compared to the above problems that are intrinsic to the
+design of git-filter-branch, the language of the tool itself is a
+relatively minor issue.
+
+ ** Side note: Unfortunately, people tend to fixate on the
+ written-in-shell aspect and periodically ask if git-filter-branch
+ could be rewritten in another language to fix the performance
+ issues. Not only does that ignore the bigger intrinsic problems
+ with the design, it'd help less than you'd expect: if
+ git-filter-branch itself were not shell, then the convenience
+ functions (map(), skip_commit(), etc) and the `--setup` argument
+ could no longer be executed once at the beginning of the program
+ but would instead need to be prepended to every user filter (and
+ thus re-executed with every commit).
+
+The https://github.com/newren/git-filter-repo/[git filter-repo] tool is
+an alternative to git-filter-branch which does not suffer from these
+performance problems or the safety problems (mentioned below). For those
+with existing tooling which relies upon git-filter-branch, 'git
+repo-filter' also provides
+https://github.com/newren/git-filter-repo/blob/master/contrib/filter-repo-demos/filter-lamely[filter-lamely],
+a drop-in git-filter-branch replacement (with a few caveats). While
+filter-lamely suffers from all the same safety issues as
+git-filter-branch, it at least ameloriates the performance issues a
+little.
+
+[[SAFETY]]
+SAFETY
+------
+
+git-filter-branch is riddled with gotchas resulting in various ways to
+easily corrupt repos or end up with a mess worse than what you started
+with:
+
+* Someone can have a set of "working and tested filters" which they
+document or provide to a coworker, who then runs them on a different OS
+where the same commands are not working/tested (some examples in the
+git-filter-branch manpage are also affected by this). BSD vs. GNU
+userland differences can really bite. If lucky, error messages are
+spewed. But just as likely, the commands either don't do the filtering
+requested, or silently corrupt by making some unwanted change. The
+unwanted change may only affect a few commits, so it's not necessarily
+obvious either. (The fact that problems won't necessarily be obvious
+means they are likely to go unnoticed until the rewritten history is in
+use for quite a while, at which point it's really hard to justify
+another flag-day for another rewrite.)
+
+* Filenames with spaces are often mishandled by shell snippets since
+they cause problems for shell pipelines. Not everyone is familiar with
+find -print0, xargs -0, git-ls-files -z, etc. Even people who are
+familiar with these may assume such flags are not relevant because
+someone else renamed any such files in their repo back before the person
+doing the filtering joined the project. And often, even those familiar
+with handling arguments with spaces may not do so just because they
+aren't in the mindset of thinking about everything that could possibly
+go wrong.
+
+* Non-ascii filenames can be silently removed despite being in a desired
+directory. Keeping only wanted paths is often done using pipelines like
+`git ls-files | grep -v ^WANTED_DIR/ | xargs git rm`. ls-files will
+only quote filenames if needed, so folks may not notice that one of the
+files didn't match the regex (at least not until it's much too late).
+Yes, someone who knows about core.quotePath can avoid this (unless they
+have other special characters like \t, \n, or "), and people who use
+ls-files -z with something other than grep can avoid this, but that
+doesn't mean they will.
+
+* Similarly, when moving files around, one can find that filenames with
+non-ascii or special characters end up in a different directory, one
+that includes a double quote character. (This is technically the same
+issue as above with quoting, but perhaps an interesting different way
+that it can and has manifested as a problem.)
+
+* It's far too easy to accidentally mix up old and new history. It's
+still possible with any tool, but git-filter-branch almost invites it.
+If lucky, the only downside is users getting frustrated that they don't
+know how to shrink their repo and remove the old stuff. If unlucky,
+they merge old and new history and end up with multiple "copies" of each
+commit, some of which have unwanted or sensitive files and others which
+don't. This comes about in multiple different ways:
+
+ ** the default to only doing a partial history rewrite ('--all' is not
+ the default and few examples show it)
+
+ ** the fact that there's no automatic post-run cleanup
+
+ ** the fact that --tag-name-filter (when used to rename tags) doesn't
+ remove the old tags but just adds new ones with the new name
+
+ ** the fact that little educational information is provided to inform
+ users of the ramifications of a rewrite and how to avoid mixing old
+ and new history. For example, this man page discusses how users
+ need to understand that they need to rebase their changes for all
+ their branches on top of new history (or delete and reclone), but
+ that's only one of multiple concerns to consider. See the
+ "DISCUSSION" section of the git filter-repo manual page for more
+ details.
+
+* Annotated tags can be accidentally converted to lightweight tags, due
+to either of two issues:
+
+ ** Someone can do a history rewrite, realize they messed up, restore
+ from the backups in refs/original/, and then redo their
+ git-filter-branch command. (The backup in refs/original/ is not a
+ real backup; it dereferences tags first.)
+
+ ** Running git-filter-branch with either --tags or --all in your
+ <rev-list options>. In order to retain annotated tags as
+ annotated, you must use --tag-name-filter (and must not have
+ restored from refs/original/ in a previously botched rewrite).
+
+* Any commit messages that specify an encoding will become corrupted
+by the rewrite; git-filter-branch ignores the encoding, takes the original
+bytes, and feeds it to commit-tree without telling it the proper
+encoding. (This happens whether or not --msg-filter is used.)
+
+* Commit messages (even if they are all UTF-8) by default become
+corrupted due to not being updated -- any references to other commit
+hashes in commit messages will now refer to no-longer-extant commits.
+
+* There are no facilities for helping users find what unwanted crud they
+should delete, which means they are much more likely to have incomplete
+or partial cleanups that sometimes result in confusion and people
+wasting time trying to understand. (For example, folks tend to just
+look for big files to delete instead of big directories or extensions,
+and once they do so, then sometime later folks using the new repository
+who are going through history will notice a build artifact directory
+that has some files but not others, or a cache of dependencies
+(node_modules or similar) which couldn't have ever been functional since
+it's missing some files.)
+
+* If --prune-empty isn't specified, then the filtering process can
+create hoards of confusing empty commits
+
+* If --prune-empty is specified, then intentionally placed empty
+commits from before the filtering operation are also pruned instead of
+just pruning commits that became empty due to filtering rules.
+
+* If --prune empty is specified, sometimes empty commits are missed
+and left around anyway (a somewhat rare bug, but it happens...)
+
+* A minor issue, but users who have a goal to update all names and
+emails in a repository may be led to --env-filter which will only update
+authors and committers, missing taggers.
+
+* If the user provides a --tag-name-filter that maps multiple tags to
+the same name, no warning or error is provided; git-filter-branch simply
+overwrites each tag in some undocumented pre-defined order resulting in
+only one tag at the end. (A git-filter-branch regression test requires
+this surprising behavior.)
+
+Also, the poor performance of git-filter-branch often leads to safety
+issues:
+
+* Coming up with the correct shell snippet to do the filtering you want
+is sometimes difficult unless you're just doing a trivial modification
+such as deleting a couple files. Unfortunately, people often learn if
+the snippet is right or wrong by trying it out, but the rightness or
+wrongness can vary depending on special circumstances (spaces in
+filenames, non-ascii filenames, funny author names or emails, invalid
+timezones, presence of grafts or replace objects, etc.), meaning they
+may have to wait a long time, hit an error, then restart. The
+performance of git-filter-branch is so bad that this cycle is painful,
+reducing the time available to carefully re-check (to say nothing about
+what it does to the patience of the person doing the rewrite even if
+they do technically have more time available). This problem is extra
+compounded because errors from broken filters may not be shown for a
+long time and/or get lost in a sea of output. Even worse, broken
+filters often just result in silent incorrect rewrites.
+
+* To top it all off, even when users finally find working commands, they
+naturally want to share them. But they may be unaware that their repo
+didn't have some special cases that someone else's does. So, when
+someone else with a different repository runs the same commands, they
+get hit by the problems above. Or, the user just runs commands that
+really were vetted for special cases, but they run it on a different OS
+where it doesn't work, as noted above.
GIT
---
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index b9b97e6..00bdf9b 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -17,9 +17,10 @@ SYNOPSIS
[--signature-file=<file>]
[-n | --numbered | -N | --no-numbered]
[--start-number <n>] [--numbered-files]
- [--in-reply-to=Message-Id] [--suffix=.<sfx>]
+ [--in-reply-to=<message id>] [--suffix=.<sfx>]
[--ignore-if-in-upstream]
- [--rfc] [--subject-prefix=Subject-Prefix]
+ [--cover-from-description=<mode>]
+ [--rfc] [--subject-prefix=<subject prefix>]
[(--reroll-count|-v) <n>]
[--to=<email>] [--cc=<email>]
[--[no-]cover-letter] [--quiet]
@@ -66,7 +67,8 @@ they are created in the current working directory. The default path
can be set with the `format.outputDirectory` configuration option.
The `-o` option takes precedence over `format.outputDirectory`.
To store patches in the current working directory even when
-`format.outputDirectory` points elsewhere, use `-o .`.
+`format.outputDirectory` points elsewhere, use `-o .`. All directory
+components will be created.
By default, the subject of a single patch is "[PATCH] " followed by
the concatenation of lines from the commit message up to the first blank
@@ -159,9 +161,9 @@ Beware that the default for 'git send-email' is to thread emails
itself. If you want `git format-patch` to take care of threading, you
will want to ensure that threading is disabled for `git send-email`.
---in-reply-to=Message-Id::
+--in-reply-to=<message id>::
Make the first mail (or all the mails with `--no-thread`) appear as a
- reply to the given Message-Id, which avoids breaking threads to
+ reply to the given <message id>, which avoids breaking threads to
provide a new patch series.
--ignore-if-in-upstream::
@@ -171,9 +173,29 @@ will want to ensure that threading is disabled for `git send-email`.
patches being generated, and any patch that matches is
ignored.
---subject-prefix=<Subject-Prefix>::
+--cover-from-description=<mode>::
+ Controls which parts of the cover letter will be automatically
+ populated using the branch's description.
++
+If `<mode>` is `message` or `default`, the cover letter subject will be
+populated with placeholder text. The body of the cover letter will be
+populated with the branch's description. This is the default mode when
+no configuration nor command line option is specified.
++
+If `<mode>` is `subject`, the first paragraph of the branch description will
+populate the cover letter subject. The remainder of the description will
+populate the body of the cover letter.
++
+If `<mode>` is `auto`, if the first paragraph of the branch description
+is greater than 100 bytes, then the mode will be `message`, otherwise
+`subject` will be used.
++
+If `<mode>` is `none`, both the cover letter subject and body will be
+populated with placeholder text.
+
+--subject-prefix=<subject prefix>::
Instead of the standard '[PATCH]' prefix in the subject
- line, instead use '[<Subject-Prefix>]'. This
+ line, instead use '[<subject prefix>]'. This
allows for useful naming of a patch series, and can be
combined with the `--numbered` option.
@@ -314,7 +336,8 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.
--base=<commit>::
Record the base tree information to identify the state the
patch series applies to. See the BASE TREE INFORMATION section
- below for details.
+ below for details. If <commit> is "auto", a base commit is
+ automatically chosen.
--root::
Treat the revision argument as a <revision range>, even if it
@@ -330,8 +353,9 @@ CONFIGURATION
-------------
You can specify extra mail header lines to be added to each message,
defaults for the subject prefix and file suffix, number patches when
-outputting more than one patch, add "To" or "Cc:" headers, configure
-attachments, and sign off patches with configuration variables.
+outputting more than one patch, add "To:" or "Cc:" headers, configure
+attachments, change the patch output directory, and sign off patches
+with configuration variables.
------------
[format]
@@ -343,7 +367,9 @@ attachments, and sign off patches with configuration variables.
cc = <email>
attach [ = mime-boundary-string ]
signOff = true
- coverletter = auto
+ outputDirectory = <directory>
+ coverLetter = auto
+ coverFromDescription = auto
------------
diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt
index 247f765..0c114ad 100644
--- a/Documentation/git-gc.txt
+++ b/Documentation/git-gc.txt
@@ -115,15 +115,14 @@ NOTES
-----
'git gc' tries very hard not to delete objects that are referenced
-anywhere in your repository. In
-particular, it will keep not only objects referenced by your current set
-of branches and tags, but also objects referenced by the index,
-remote-tracking branches, refs saved by 'git filter-branch' in
-refs/original/, reflogs (which may reference commits in branches
-that were later amended or rewound), and anything else in the refs/* namespace.
-If you are expecting some objects to be deleted and they aren't, check
-all of those locations and decide whether it makes sense in your case to
-remove those references.
+anywhere in your repository. In particular, it will keep not only
+objects referenced by your current set of branches and tags, but also
+objects referenced by the index, remote-tracking branches, notes saved
+by 'git notes' under refs/notes/, reflogs (which may reference commits
+in branches that were later amended or rewound), and anything else in
+the refs/* namespace. If you are expecting some objects to be deleted
+and they aren't, check all of those locations and decide whether it
+makes sense in your case to remove those references.
On the other hand, when 'git gc' runs concurrently with another process,
there is a risk of it deleting an object that the other process is using
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index 2d27969..c89fb56 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -271,6 +271,23 @@ providing this option will cause it to die.
-f <file>::
Read patterns from <file>, one per line.
++
+Passing the pattern via <file> allows for providing a search pattern
+containing a \0.
++
+Not all pattern types support patterns containing \0. Git will error
+out if a given pattern type can't support such a pattern. The
+`--perl-regexp` pattern type when compiled against the PCRE v2 backend
+has the widest support for these types of patterns.
++
+In versions of Git before 2.23.0 patterns containing \0 would be
+silently considered fixed. This was never documented, there were also
+odd and undocumented interactions between e.g. non-ASCII patterns
+containing \0 and `--ignore-case`.
++
+In future versions we may learn to support patterns containing \0 for
+more search backends, until then we'll die when the pattern type in
+question doesn't support them.
-e::
The next parameter is the pattern. This option has to be
diff --git a/Documentation/git-gui.txt b/Documentation/git-gui.txt
index 5f93f80..c9d7e96 100644
--- a/Documentation/git-gui.txt
+++ b/Documentation/git-gui.txt
@@ -112,15 +112,9 @@ Other
versions are distributed as part of the Git suite for the convenience
of end users.
-A 'git gui' development repository can be obtained from:
+The official repository of the 'git gui' project can be found at:
- git clone git://repo.or.cz/git-gui.git
-
-or
-
- git clone http://repo.or.cz/r/git-gui.git
-
-or browsed online at http://repo.or.cz/w/git-gui.git/[].
+ https://github.com/prati0100/git-gui.git/
GIT
---
diff --git a/Documentation/git-ls-remote.txt b/Documentation/git-ls-remote.txt
index 0b057cb..a2ea1fd 100644
--- a/Documentation/git-ls-remote.txt
+++ b/Documentation/git-ls-remote.txt
@@ -92,21 +92,23 @@ OPTIONS
EXAMPLES
--------
- $ git ls-remote --tags ./.
- d6602ec5194c87b0fc87103ca4d67251c76f233a refs/tags/v0.99
- f25a265a342aed6041ab0cc484224d9ca54b6f41 refs/tags/v0.99.1
- 7ceca275d047c90c0c7d5afb13ab97efdf51bd6e refs/tags/v0.99.3
- c5db5456ae3b0873fc659c19fafdde22313cc441 refs/tags/v0.99.2
- 0918385dbd9656cab0d1d81ba7453d49bbc16250 refs/tags/junio-gpg-pub
- $ git ls-remote http://www.kernel.org/pub/scm/git/git.git master pu rc
- 5fe978a5381f1fbad26a80e682ddd2a401966740 refs/heads/master
- c781a84b5204fb294c9ccc79f8b3baceeb32c061 refs/heads/pu
- $ git remote add korg http://www.kernel.org/pub/scm/git/git.git
- $ git ls-remote --tags korg v\*
- d6602ec5194c87b0fc87103ca4d67251c76f233a refs/tags/v0.99
- f25a265a342aed6041ab0cc484224d9ca54b6f41 refs/tags/v0.99.1
- c5db5456ae3b0873fc659c19fafdde22313cc441 refs/tags/v0.99.2
- 7ceca275d047c90c0c7d5afb13ab97efdf51bd6e refs/tags/v0.99.3
+----
+$ git ls-remote --tags ./.
+d6602ec5194c87b0fc87103ca4d67251c76f233a refs/tags/v0.99
+f25a265a342aed6041ab0cc484224d9ca54b6f41 refs/tags/v0.99.1
+7ceca275d047c90c0c7d5afb13ab97efdf51bd6e refs/tags/v0.99.3
+c5db5456ae3b0873fc659c19fafdde22313cc441 refs/tags/v0.99.2
+0918385dbd9656cab0d1d81ba7453d49bbc16250 refs/tags/junio-gpg-pub
+$ git ls-remote http://www.kernel.org/pub/scm/git/git.git master pu rc
+5fe978a5381f1fbad26a80e682ddd2a401966740 refs/heads/master
+c781a84b5204fb294c9ccc79f8b3baceeb32c061 refs/heads/pu
+$ git remote add korg http://www.kernel.org/pub/scm/git/git.git
+$ git ls-remote --tags korg v\*
+d6602ec5194c87b0fc87103ca4d67251c76f233a refs/tags/v0.99
+f25a265a342aed6041ab0cc484224d9ca54b6f41 refs/tags/v0.99.1
+c5db5456ae3b0873fc659c19fafdde22313cc441 refs/tags/v0.99.2
+7ceca275d047c90c0c7d5afb13ab97efdf51bd6e refs/tags/v0.99.3
+----
SEE ALSO
--------
diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt
index 261d5c1..2d944e0 100644
--- a/Documentation/git-merge-base.txt
+++ b/Documentation/git-merge-base.txt
@@ -80,9 +80,11 @@ which is reachable from both 'A' and 'B' through the parent relationship.
For example, with this topology:
- o---o---o---B
- /
- ---o---1---o---o---o---A
+....
+ o---o---o---B
+ /
+---o---1---o---o---o---A
+....
the merge base between 'A' and 'B' is '1'.
@@ -90,21 +92,25 @@ Given three commits 'A', 'B' and 'C', `git merge-base A B C` will compute the
merge base between 'A' and a hypothetical commit 'M', which is a merge
between 'B' and 'C'. For example, with this topology:
- o---o---o---o---C
- /
- / o---o---o---B
- / /
- ---2---1---o---o---o---A
+....
+ o---o---o---o---C
+ /
+ / o---o---o---B
+ / /
+---2---1---o---o---o---A
+....
the result of `git merge-base A B C` is '1'. This is because the
equivalent topology with a merge commit 'M' between 'B' and 'C' is:
- o---o---o---o---o
- / \
- / o---o---o---o---M
- / /
- ---2---1---o---o---o---A
+....
+ o---o---o---o---o
+ / \
+ / o---o---o---o---M
+ / /
+---2---1---o---o---o---A
+....
and the result of `git merge-base A M` is '1'. Commit '2' is also a
common ancestor between 'A' and 'M', but '1' is a better common ancestor,
@@ -116,11 +122,13 @@ the best common ancestor of all commits.
When the history involves criss-cross merges, there can be more than one
'best' common ancestor for two commits. For example, with this topology:
- ---1---o---A
- \ /
- X
- / \
- ---2---o---o---B
+....
+---1---o---A
+ \ /
+ X
+ / \
+---2---o---o---B
+....
both '1' and '2' are merge-bases of A and B. Neither one is better than
the other (both are 'best' merge bases). When the `--all` option is not given,
@@ -131,18 +139,22 @@ and B is (or at least used to be) to compute the merge base between
A and B, and check if it is the same as A, in which case, A is an
ancestor of B. You will see this idiom used often in older scripts.
- A=$(git rev-parse --verify A)
- if test "$A" = "$(git merge-base A B)"
- then
- ... A is an ancestor of B ...
- fi
+....
+A=$(git rev-parse --verify A)
+if test "$A" = "$(git merge-base A B)"
+then
+ ... A is an ancestor of B ...
+fi
+....
In modern git, you can say this in a more direct way:
- if git merge-base --is-ancestor A B
- then
- ... A is an ancestor of B ...
- fi
+....
+if git merge-base --is-ancestor A B
+then
+ ... A is an ancestor of B ...
+fi
+....
instead.
@@ -154,13 +166,15 @@ topic origin/master`, the history of remote-tracking branch
`origin/master` may have been rewound and rebuilt, leading to a
history of this shape:
- o---B2
- /
- ---o---o---B1--o---o---o---B (origin/master)
- \
- B0
- \
- D0---D1---D (topic)
+....
+ o---B2
+ /
+---o---o---B1--o---o---o---B (origin/master)
+ \
+ B0
+ \
+ D0---D1---D (topic)
+....
where `origin/master` used to point at commits B0, B1, B2 and now it
points at B, and your `topic` branch was started on top of it back
@@ -193,13 +207,15 @@ will find B0, and
will replay D0, D1 and D on top of B to create a new history of this
shape:
- o---B2
- /
- ---o---o---B1--o---o---o---B (origin/master)
- \ \
- B0 D0'--D1'--D' (topic - updated)
- \
- D0---D1---D (topic - old)
+....
+ o---B2
+ /
+---o---o---B1--o---o---o---B (origin/master)
+ \ \
+ B0 D0'--D1'--D' (topic - updated)
+ \
+ D0---D1---D (topic - old)
+....
A caveat is that older reflog entries in your repository may be
expired by `git gc`. If B0 no longer appears in the reflog of the
diff --git a/Documentation/git-merge-index.txt b/Documentation/git-merge-index.txt
index 02676fb..2ab84a9 100644
--- a/Documentation/git-merge-index.txt
+++ b/Documentation/git-merge-index.txt
@@ -54,20 +54,24 @@ original is first. But the argument order to the 3-way merge program
Examples:
- torvalds@ppc970:~/merge-test> git merge-index cat MM
- This is MM from the original tree. # original
- This is modified MM in the branch A. # merge1
- This is modified MM in the branch B. # merge2
- This is modified MM in the branch B. # current contents
+----
+torvalds@ppc970:~/merge-test> git merge-index cat MM
+This is MM from the original tree. # original
+This is modified MM in the branch A. # merge1
+This is modified MM in the branch B. # merge2
+This is modified MM in the branch B. # current contents
+----
or
- torvalds@ppc970:~/merge-test> git merge-index cat AA MM
- cat: : No such file or directory
- This is added AA in the branch A.
- This is added AA in the branch B.
- This is added AA in the branch B.
- fatal: merge program failed
+----
+torvalds@ppc970:~/merge-test> git merge-index cat AA MM
+cat: : No such file or directory
+This is added AA in the branch A.
+This is added AA in the branch B.
+This is added AA in the branch B.
+fatal: merge program failed
+----
where the latter example shows how 'git merge-index' will stop trying to
merge once anything has returned an error (i.e., `cat` returned an error
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 01fd52d..092529c 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
'git merge' [-n] [--stat] [--no-commit] [--squash] [--[no-]edit]
- [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
+ [--no-verify] [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
[--[no-]allow-unrelated-histories]
[--[no-]rerere-autoupdate] [-m <msg>] [-F <file>] [<commit>...]
'git merge' (--continue | --abort | --quit)
diff --git a/Documentation/git-multi-pack-index.txt b/Documentation/git-multi-pack-index.txt
index 233b2b7..642d9ac 100644
--- a/Documentation/git-multi-pack-index.txt
+++ b/Documentation/git-multi-pack-index.txt
@@ -9,7 +9,7 @@ git-multi-pack-index - Write and verify multi-pack-indexes
SYNOPSIS
--------
[verse]
-'git multi-pack-index' [--object-dir=<dir>] <subcommand>
+'git multi-pack-index' [--object-dir=<dir>] [--[no-]progress] <subcommand>
DESCRIPTION
-----------
@@ -23,6 +23,10 @@ OPTIONS
`<dir>/packs/multi-pack-index` for the current MIDX file, and
`<dir>/packs` for the pack-files to index.
+--[no-]progress::
+ Turn progress on/off explicitly. If neither is specified, progress is
+ shown if standard error is connected to a terminal.
+
The following subcommands are available:
write::
diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
index f56a5a9..ced2e82 100644
--- a/Documentation/git-notes.txt
+++ b/Documentation/git-notes.txt
@@ -10,7 +10,7 @@ SYNOPSIS
[verse]
'git notes' [list [<object>]]
'git notes' add [-f] [--allow-empty] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
-'git notes' copy [-f] ( --stdin | <from-object> <to-object> )
+'git notes' copy [-f] ( --stdin | <from-object> [<to-object>] )
'git notes' append [--allow-empty] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
'git notes' edit [--allow-empty] [<object>]
'git notes' show [<object>]
@@ -68,8 +68,8 @@ add::
subcommand).
copy::
- Copy the notes for the first object onto the second object.
- Abort if the second object already has notes, or if the first
+ Copy the notes for the first object onto the second object (defaults to
+ HEAD). Abort if the second object already has notes, or if the first
object has none (use -f to overwrite existing notes to the
second object). This subcommand is equivalent to:
`git notes add [-f] -C $(git notes list <from-object>) <to-object>`
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 6156609..639a417 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -8,8 +8,8 @@ git-rebase - Reapply commits on top of another base tip
SYNOPSIS
--------
[verse]
-'git rebase' [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>]
- [<upstream> [<branch>]]
+'git rebase' [-i | --interactive] [<options>] [--exec <cmd>]
+ [--onto <newbase> | --keep-base] [<upstream> [<branch>]]
'git rebase' [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>]
--root [<branch>]
'git rebase' (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)
@@ -217,6 +217,24 @@ As a special case, you may use "A\...B" as a shortcut for the
merge base of A and B if there is exactly one merge base. You can
leave out at most one of A and B, in which case it defaults to HEAD.
+--keep-base::
+ Set the starting point at which to create the new commits to the
+ merge base of <upstream> <branch>. Running
+ 'git rebase --keep-base <upstream> <branch>' is equivalent to
+ running 'git rebase --onto <upstream>... <upstream>'.
++
+This option is useful in the case where one is developing a feature on
+top of an upstream branch. While the feature is being worked on, the
+upstream branch may advance and it may not be the best idea to keep
+rebasing on top of the upstream but to keep the base commit as-is.
++
+Although both this option and --fork-point find the merge base between
+<upstream> and <branch>, this option uses the merge base as the _starting
+point_ on which new commits will be created, whereas --fork-point uses
+the merge base to determine the _set of commits_ which will be rebased.
++
+See also INCOMPATIBLE OPTIONS below.
+
<upstream>::
Upstream branch to compare against. May be any valid commit,
not just an existing branch name. Defaults to the configured
@@ -369,6 +387,10 @@ ends up being empty, the <upstream> will be used as a fallback.
+
If either <upstream> or --root is given on the command line, then the
default is `--no-fork-point`, otherwise the default is `--fork-point`.
++
+If your branch was based on <upstream> but <upstream> was rewound and
+your branch contains commits which were dropped, this option can be used
+with `--keep-base` in order to drop those commits from your branch.
--ignore-whitespace::
--whitespace=<option>::
@@ -543,8 +565,8 @@ In addition, the following pairs of options are incompatible:
* --preserve-merges and --interactive
* --preserve-merges and --signoff
* --preserve-merges and --rebase-merges
- * --rebase-merges and --strategy
- * --rebase-merges and --strategy-option
+ * --keep-base and --onto
+ * --keep-base and --root
BEHAVIORAL DIFFERENCES
-----------------------
@@ -832,7 +854,8 @@ Hard case: The changes are not the same.::
This happens if the 'subsystem' rebase had conflicts, or used
`--interactive` to omit, edit, squash, or fixup commits; or
if the upstream used one of `commit --amend`, `reset`, or
- `filter-branch`.
+ a full history rewriting command like
+ https://github.com/newren/git-filter-repo[`filter-repo`].
The easy case
@@ -870,7 +893,7 @@ NOTE: While an "easy case recovery" sometimes appears to be successful
--interactive` will be **resurrected**!
The idea is to manually tell 'git rebase' "where the old 'subsystem'
-ended and your 'topic' began", that is, what the old merge-base
+ended and your 'topic' began", that is, what the old merge base
between them was. You will have to find a way to name the last commit
of the old 'subsystem', for example:
diff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt
index dedf97e..25702ed 100644
--- a/Documentation/git-receive-pack.txt
+++ b/Documentation/git-receive-pack.txt
@@ -165,29 +165,31 @@ ref listing the commits pushed to the repository, and logs the push
certificates of signed pushes with good signatures to a logger
service:
- #!/bin/sh
- # mail out commit update information.
- while read oval nval ref
- do
- if expr "$oval" : '0*$' >/dev/null
- then
- echo "Created a new ref, with the following commits:"
- git rev-list --pretty "$nval"
- else
- echo "New commits:"
- git rev-list --pretty "$nval" "^$oval"
- fi |
- mail -s "Changes to ref $ref" commit-list@mydomain
- done
- # log signed push certificate, if any
- if test -n "${GIT_PUSH_CERT-}" && test ${GIT_PUSH_CERT_STATUS} = G
+----
+#!/bin/sh
+# mail out commit update information.
+while read oval nval ref
+do
+ if expr "$oval" : '0*$' >/dev/null
then
- (
- echo expected nonce is ${GIT_PUSH_NONCE}
- git cat-file blob ${GIT_PUSH_CERT}
- ) | mail -s "push certificate from $GIT_PUSH_CERT_SIGNER" push-log@mydomain
- fi
- exit 0
+ echo "Created a new ref, with the following commits:"
+ git rev-list --pretty "$nval"
+ else
+ echo "New commits:"
+ git rev-list --pretty "$nval" "^$oval"
+ fi |
+ mail -s "Changes to ref $ref" commit-list@mydomain
+done
+# log signed push certificate, if any
+if test -n "${GIT_PUSH_CERT-}" && test ${GIT_PUSH_CERT_STATUS} = G
+then
+ (
+ echo expected nonce is ${GIT_PUSH_NONCE}
+ git cat-file blob ${GIT_PUSH_CERT}
+ ) | mail -s "push certificate from $GIT_PUSH_CERT_SIGNER" push-log@mydomain
+fi
+exit 0
+----
The exit code from this hook invocation is ignored, however a
non-zero exit code will generate an error message.
@@ -212,8 +214,10 @@ anyway.
This hook can be used, for example, to run `git update-server-info`
if the repository is packed and is served via a dumb transport.
- #!/bin/sh
- exec git update-server-info
+----
+#!/bin/sh
+exec git update-server-info
+----
QUARANTINE ENVIRONMENT
diff --git a/Documentation/git-replace.txt b/Documentation/git-replace.txt
index 246dc99..f271d75 100644
--- a/Documentation/git-replace.txt
+++ b/Documentation/git-replace.txt
@@ -123,10 +123,10 @@ The following format are available:
CREATING REPLACEMENT OBJECTS
----------------------------
-linkgit:git-filter-branch[1], linkgit:git-hash-object[1] and
-linkgit:git-rebase[1], among other git commands, can be used to create
-replacement objects from existing objects. The `--edit` option can
-also be used with 'git replace' to create a replacement object by
+linkgit:git-hash-object[1], linkgit:git-rebase[1], and
+https://github.com/newren/git-filter-repo[git-filter-repo], among other git commands, can be used to
+create replacement objects from existing objects. The `--edit` option
+can also be used with 'git replace' to create a replacement object by
editing an existing object.
If you want to replace many blobs, trees or commits that are part of a
@@ -148,13 +148,13 @@ pending objects.
SEE ALSO
--------
linkgit:git-hash-object[1]
-linkgit:git-filter-branch[1]
linkgit:git-rebase[1]
linkgit:git-tag[1]
linkgit:git-branch[1]
linkgit:git-commit[1]
linkgit:git-var[1]
linkgit:git[1]
+https://github.com/newren/git-filter-repo[git-filter-repo]
GIT
---
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index 9392760..025c911 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -9,59 +9,7 @@ git-rev-list - Lists commit objects in reverse chronological order
SYNOPSIS
--------
[verse]
-'git rev-list' [ --max-count=<number> ]
- [ --skip=<number> ]
- [ --max-age=<timestamp> ]
- [ --min-age=<timestamp> ]
- [ --sparse ]
- [ --merges ]
- [ --no-merges ]
- [ --min-parents=<number> ]
- [ --no-min-parents ]
- [ --max-parents=<number> ]
- [ --no-max-parents ]
- [ --first-parent ]
- [ --remove-empty ]
- [ --full-history ]
- [ --not ]
- [ --all ]
- [ --branches[=<pattern>] ]
- [ --tags[=<pattern>] ]
- [ --remotes[=<pattern>] ]
- [ --glob=<glob-pattern> ]
- [ --ignore-missing ]
- [ --stdin ]
- [ --quiet ]
- [ --topo-order ]
- [ --parents ]
- [ --timestamp ]
- [ --left-right ]
- [ --left-only ]
- [ --right-only ]
- [ --cherry-mark ]
- [ --cherry-pick ]
- [ --encoding=<encoding> ]
- [ --(author|committer|grep)=<pattern> ]
- [ --regexp-ignore-case | -i ]
- [ --extended-regexp | -E ]
- [ --fixed-strings | -F ]
- [ --date=<format>]
- [ [ --objects | --objects-edge | --objects-edge-aggressive ]
- [ --unpacked ]
- [ --object-names | --no-object-names ]
- [ --filter=<filter-spec> [ --filter-print-omitted ] ] ]
- [ --missing=<missing-action> ]
- [ --pretty | --header ]
- [ --bisect ]
- [ --bisect-vars ]
- [ --bisect-all ]
- [ --merge ]
- [ --reverse ]
- [ --walk-reflogs ]
- [ --no-walk ] [ --do-walk ]
- [ --count ]
- [ --use-bitmap-index ]
- <commit>... [ \-- <paths>... ]
+'git rev-list' [<options>] <commit>... [[--] <path>...]
DESCRIPTION
-----------
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index d93e5d0..0a69810 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -486,11 +486,13 @@ Use gmail as the smtp server
To use 'git send-email' to send your patches through the GMail SMTP server,
edit ~/.gitconfig to specify your account settings:
- [sendemail]
- smtpEncryption = tls
- smtpServer = smtp.gmail.com
- smtpUser = yourname@gmail.com
- smtpServerPort = 587
+----
+[sendemail]
+ smtpEncryption = tls
+ smtpServer = smtp.gmail.com
+ smtpUser = yourname@gmail.com
+ smtpServerPort = 587
+----
If you have multifactor authentication setup on your gmail account, you will
need to generate an app-specific password for use with 'git send-email'. Visit
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 8fbe12c..53e1a12 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -87,8 +87,9 @@ The `--patch` option implies `--keep-index`. You can use
save [-p|--patch] [-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 pathspecs,
- and any non-option arguments form the message.
+ differs from "stash push" in that it cannot take pathspecs.
+ Instead, all non-option arguments are concatenated to form the stash
+ message.
list [<options>]::
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index d4e8f24..7731b45 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -59,16 +59,17 @@ This is optional and defaults to the original version 'v1' format.
--untracked-files[=<mode>]::
Show untracked files.
+
+--
The mode parameter is used to specify the handling of untracked files.
It is optional: it defaults to 'all', and if specified, it must be
stuck to the option (e.g. `-uno`, but not `-u no`).
-+
+
The possible options are:
-+
+
- 'no' - Show no untracked files.
- 'normal' - Shows untracked files and directories.
- 'all' - Also shows individual files in untracked directories.
-+
+
When `-u` option is not used, untracked files and directories are
shown (i.e. the same as specifying `normal`), to help you avoid
forgetting to add newly created files. Because it takes extra work
@@ -78,9 +79,10 @@ Consider enabling untracked cache and split index if supported (see
`git update-index --untracked-cache` and `git update-index
--split-index`), Otherwise you can use `no` to have `git status`
return more quickly without showing untracked files.
-+
+
The default can be changed using the status.showUntrackedFiles
configuration variable documented in linkgit:git-config[1].
+--
--ignore-submodules[=<when>]::
Ignore changes to submodules when looking for changes. <when> can be
@@ -100,11 +102,12 @@ configuration variable documented in linkgit:git-config[1].
--ignored[=<mode>]::
Show ignored files as well.
+
+--
The mode parameter is used to specify the handling of ignored files.
It is optional: it defaults to 'traditional'.
-+
+
The possible options are:
-+
+
- 'traditional' - Shows ignored files and directories, unless
--untracked-files=all is specified, in which case
individual files in ignored directories are
@@ -112,12 +115,13 @@ The possible options are:
- 'no' - Show no ignored files.
- 'matching' - Shows ignored files and directories matching an
ignore pattern.
-+
+
When 'matching' mode is specified, paths that explicitly match an
ignored pattern are shown. If a directory matches an ignore pattern,
then it is shown, but not paths contained in the ignored directory. If
a directory does not match an ignore pattern, but all contents are
ignored, then the directory is not shown, but all contents are shown.
+--
-z::
Terminate entries with NUL, instead of LF. This implies
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index 0ed5c24..1f46380 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -173,7 +173,8 @@ submodule with the `--init` option.
If `--recursive` is specified, this command will recurse into the
registered submodules, and update any nested submodules within.
--
-set-branch ((-d|--default)|(-b|--branch <branch>)) [--] <path>::
+set-branch (-b|--branch) <branch> [--] <path>::
+set-branch (-d|--default) [--] <path>::
Sets the default remote tracking branch for the submodule. The
`--branch` option allows the remote branch to be specified. The
`--default` option removes the submodule.<name>.branch configuration
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 3071162..53774f5 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -769,11 +769,11 @@ option for (hopefully) obvious reasons.
+
This option is NOT recommended as it makes it difficult to track down
old references to SVN revision numbers in existing documentation, bug
-reports and archives. If you plan to eventually migrate from SVN to Git
-and are certain about dropping SVN history, consider
-linkgit:git-filter-branch[1] instead. filter-branch also allows
-reformatting of metadata for ease-of-reading and rewriting authorship
-info for non-"svn.authorsFile" users.
+reports, and archives. If you plan to eventually migrate from SVN to
+Git and are certain about dropping SVN history, consider
+https://github.com/newren/git-filter-repo[git-filter-repo] instead.
+filter-repo also allows reformatting of metadata for ease-of-reading
+and rewriting authorship info for non-"svn.authorsFile" users.
svn.useSvmProps::
svn-remote.<name>.useSvmProps::
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index fb1d188..c5a528c 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -810,6 +810,8 @@ patterns are available:
- `css` suitable for cascading style sheets.
+- `dts` suitable for devicetree (DTS) files.
+
- `fortran` suitable for source code in the Fortran language.
- `fountain` suitable for Fountain documents.
diff --git a/Documentation/gitcli.txt b/Documentation/gitcli.txt
index 1ed3ca3..4b32876 100644
--- a/Documentation/gitcli.txt
+++ b/Documentation/gitcli.txt
@@ -37,6 +37,12 @@ arguments. Here are the rules:
file called HEAD in your work tree, `git diff HEAD` is ambiguous, and
you have to say either `git diff HEAD --` or `git diff -- HEAD` to
disambiguate.
+
+ * Because `--` disambiguates revisions and paths in some commands, it
+ cannot be used for those commands to separate options and revisions.
+ You can use `--end-of-options` for this (it also works for commands
+ that do not distinguish between revisions in paths, in which case it
+ is simply an alias for `--`).
+
When writing a script that is expected to handle random user-input, it is
a good practice to make it explicit which arguments are which by placing
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 82cd573..50365f2 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -103,6 +103,28 @@ The default 'pre-commit' hook, when enabled--and with the
`hooks.allownonascii` config option unset or set to false--prevents
the use of non-ASCII filenames.
+pre-merge-commit
+~~~~~~~~~~~~~~~~
+
+This hook is invoked by linkgit:git-merge[1], and can be bypassed
+with the `--no-verify` option. It takes no parameters, and is
+invoked after the merge has been carried out successfully and before
+obtaining the proposed commit log message to
+make a commit. Exiting with a non-zero status from this script
+causes the `git merge` command to abort before creating a commit.
+
+The default 'pre-merge-commit' hook, when enabled, runs the
+'pre-commit' hook, if the latter is enabled.
+
+This hook is invoked with the environment variable
+`GIT_EDITOR=:` if the command will not bring up an editor
+to modify the commit message.
+
+If the merge cannot be carried out automatically, the conflicts
+need to be resolved and the result committed separately (see
+linkgit:git-merge[1]). At that point, this hook will not be executed,
+but the 'pre-commit' hook will, if it is enabled.
+
prepare-commit-msg
~~~~~~~~~~~~~~~~~~
@@ -425,10 +447,12 @@ post-rewrite
This hook is invoked by commands that rewrite commits
(linkgit:git-commit[1] when called with `--amend` and
-linkgit:git-rebase[1]; currently `git filter-branch` does 'not' call
-it!). Its first argument denotes the command it was invoked by:
-currently one of `amend` or `rebase`. Further command-dependent
-arguments may be passed in the future.
+linkgit:git-rebase[1]; however, full-history (re)writing tools like
+linkgit:git-fast-import[1] or
+https://github.com/newren/git-filter-repo[git-filter-repo] typically
+do not call it!). Its first argument denotes the command it was
+invoked by: currently one of `amend` or `rebase`. Further
+command-dependent arguments may be passed in the future.
The hook receives a list of the rewritten commits on stdin, in the
format
diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt
index a66e95b..f2a65ba 100644
--- a/Documentation/gitmodules.txt
+++ b/Documentation/gitmodules.txt
@@ -90,7 +90,7 @@ of the superproject, the setting there will override the one found in
.gitmodules.
Both settings can be overridden on the command line by using the
-"--ignore-submodule" option. The 'git submodule' commands are not
+"--ignore-submodules" option. The 'git submodule' commands are not
affected by this setting.
--
@@ -105,14 +105,15 @@ EXAMPLES
Consider the following .gitmodules file:
- [submodule "libfoo"]
- path = include/foo
- url = git://foo.com/git/lib.git
-
- [submodule "libbar"]
- path = include/bar
- url = git://bar.com/git/lib.git
+----
+[submodule "libfoo"]
+ path = include/foo
+ url = git://foo.com/git/lib.git
+[submodule "libbar"]
+ path = include/bar
+ url = git://bar.com/git/lib.git
+----
This defines two submodules, `libfoo` and `libbar`. These are expected to
be checked out in the paths `include/foo` and `include/bar`, and for both
diff --git a/Documentation/gitremote-helpers.txt b/Documentation/gitremote-helpers.txt
index 43f80c8..f48a031 100644
--- a/Documentation/gitremote-helpers.txt
+++ b/Documentation/gitremote-helpers.txt
@@ -297,9 +297,13 @@ Supported if the helper has the "option" capability.
same batch are complete. Only objects which were reported
in the output of 'list' with a sha1 may be fetched this way.
+
-Optionally may output a 'lock <file>' line indicating a file under
-GIT_DIR/objects/pack which is keeping a pack until refs can be
-suitably updated.
+Optionally may output a 'lock <file>' line indicating the full path of
+a file under `$GIT_DIR/objects/pack` which is keeping a pack until
+refs can be suitably updated. The path must end with `.keep`. This is
+a mechanism to name a <pack,idx,keep> tuple by giving only the keep
+component. The kept pack will not be deleted by a concurrent repack,
+even though its objects may not be referenced until the fetch completes.
+The `.keep` file will be deleted at the conclusion of the fetch.
+
If option 'check-connectivity' is requested, the helper must output
'connectivity-ok' if the clone is self-contained and connected.
@@ -505,6 +509,11 @@ set by Git if the remote helper has the 'option' capability.
Indicate that only the objects wanted need to be fetched, not
their dependents.
+'option atomic' {'true'|'false'}::
+ When pushing, request the remote server to update refs in a single atomic
+ transaction. If successful, all refs will be updated, or none will. If the
+ remote side does not support this capability, the push will fail.
+
SEE ALSO
--------
linkgit:git-remote[1]
diff --git a/Documentation/gitrepository-layout.txt b/Documentation/gitrepository-layout.txt
index 216b11e..1a2ef4c 100644
--- a/Documentation/gitrepository-layout.txt
+++ b/Documentation/gitrepository-layout.txt
@@ -59,7 +59,7 @@ objects/[0-9a-f][0-9a-f]::
here are often called 'unpacked' (or 'loose') objects.
objects/pack::
- Packs (files that store many object in compressed form,
+ Packs (files that store many objects in compressed form,
along with index files to allow them to be randomly
accessed) are found in this directory.
@@ -96,9 +96,9 @@ refs::
directory. The 'git prune' command knows to preserve
objects reachable from refs found in this directory and
its subdirectories.
- This directory is ignored (except refs/bisect and
- refs/worktree) if $GIT_COMMON_DIR is set and
- "$GIT_COMMON_DIR/refs" will be used instead.
+ This directory is ignored (except refs/bisect,
+ refs/rewritten and refs/worktree) if $GIT_COMMON_DIR is
+ set and "$GIT_COMMON_DIR/refs" will be used instead.
refs/heads/`name`::
records tip-of-the-tree commit objects of branch `name`
@@ -240,8 +240,8 @@ remotes::
logs::
Records of changes made to refs are stored in this directory.
See linkgit:git-update-ref[1] for more information. This
- directory is ignored if $GIT_COMMON_DIR is set and
- "$GIT_COMMON_DIR/logs" will be used instead.
+ directory is ignored (except logs/HEAD) if $GIT_COMMON_DIR is
+ set and "$GIT_COMMON_DIR/logs" will be used instead.
logs/refs/heads/`name`::
Records all changes made to the branch tip named `name`.
diff --git a/Documentation/gitweb.conf.txt b/Documentation/gitweb.conf.txt
index 35317e7..7963a79 100644
--- a/Documentation/gitweb.conf.txt
+++ b/Documentation/gitweb.conf.txt
@@ -786,9 +786,9 @@ forks::
subdirectories of project root (basename) to be forks of existing
projects. For each project +$projname.git+, projects in the
+$projname/+ directory and its subdirectories will not be
- shown in the main projects list. Instead, a \'\+' mark is shown
- next to +$projname+, which links to a "forks" view that lists all
- the forks (all projects in +$projname/+ subdirectory). Additionally
+ shown in the main projects list. Instead, a \'+' mark is shown
+ next to `$projname`, which links to a "forks" view that lists all
+ the forks (all projects in `$projname/` subdirectory). Additionally
a "forks" view for a project is linked from project summary page.
+
If the project list is taken from a file (+$projects_list+ points to a
diff --git a/Documentation/manpage-bold-literal.xsl b/Documentation/manpage-bold-literal.xsl
index 608eb5d..94d6c1b 100644
--- a/Documentation/manpage-bold-literal.xsl
+++ b/Documentation/manpage-bold-literal.xsl
@@ -1,12 +1,13 @@
<!-- manpage-bold-literal.xsl:
special formatting for manpages rendered from asciidoc+docbook -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:d="http://docbook.org/ns/docbook"
version="1.0">
<!-- render literal text as bold (instead of plain or monospace);
this makes literal text easier to distinguish in manpages
viewed on a tty -->
-<xsl:template match="literal">
+<xsl:template match="literal|d:literal">
<xsl:value-of select="$git.docbook.backslash"/>
<xsl:text>fB</xsl:text>
<xsl:apply-templates/>
diff --git a/Documentation/manpage.xsl b/Documentation/manpage.xsl
new file mode 100644
index 0000000..ef64bab
--- /dev/null
+++ b/Documentation/manpage.xsl
@@ -0,0 +1,3 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:import href="http://docbook.sourceforge.net/release/xsl-ns/current/manpages/docbook.xsl" />
+</xsl:stylesheet>
diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt
index 79a00d2..59b8ff1 100644
--- a/Documentation/merge-options.txt
+++ b/Documentation/merge-options.txt
@@ -40,20 +40,24 @@ set to `no` at the beginning of them.
case of a merge conflict.
--ff::
- When the merge resolves as a fast-forward, only update the branch
- pointer, without creating a merge commit. This is the default
- behavior.
-
--no-ff::
- Create a merge commit even when the merge resolves as a
- fast-forward. This is the default behaviour when merging an
- annotated (and possibly signed) tag that is not stored in
- its natural place in 'refs/tags/' hierarchy.
-
--ff-only::
- Refuse to merge and exit with a non-zero status unless the
- current `HEAD` is already up to date or the merge can be
- resolved as a fast-forward.
+ Specifies how a merge is handled when the merged-in history is
+ already a descendant of the current history. `--ff` is the
+ default unless merging an annotated (and possibly signed) tag
+ that is not stored in its natural place in the `refs/tags/`
+ hierarchy, in which case `--no-ff` is assumed.
++
+With `--ff`, when possible resolve the merge as a fast-forward (only
+update the branch pointer to match the merged branch; do not create a
+merge commit). When not possible (when the merged-in history is not a
+descendant of the current history), create a merge commit.
++
+With `--no-ff`, create a merge commit in all cases, even when the merge
+could instead be resolved as a fast-forward.
++
+With `--ff-only`, resolve the merge as a fast-forward when possible.
+When not possible, refuse to merge and exit with a non-zero status.
-S[<keyid>]::
--gpg-sign[=<keyid>]::
@@ -105,6 +109,10 @@ option can be used to override --squash.
+
With --squash, --commit is not allowed, and will fail.
+--no-verify::
+ This option bypasses the pre-merge and commit-msg hooks.
+ See also linkgit:githooks[5].
+
-s <strategy>::
--strategy=<strategy>::
Use the given merge strategy; can be supplied more than
diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt
index 0795983..31c6e8d 100644
--- a/Documentation/pretty-formats.txt
+++ b/Documentation/pretty-formats.txt
@@ -163,6 +163,9 @@ The placeholders are:
'%ae':: author email
'%aE':: author email (respecting .mailmap, see linkgit:git-shortlog[1]
or linkgit:git-blame[1])
+'%al':: author email local-part (the part before the '@' sign)
+'%aL':: author local-part (see '%al') respecting .mailmap, see
+ linkgit:git-shortlog[1] or linkgit:git-blame[1])
'%ad':: author date (format respects --date= option)
'%aD':: author date, RFC2822 style
'%ar':: author date, relative
@@ -175,6 +178,9 @@ The placeholders are:
'%ce':: committer email
'%cE':: committer email (respecting .mailmap, see
linkgit:git-shortlog[1] or linkgit:git-blame[1])
+'%cl':: author email local-part (the part before the '@' sign)
+'%cL':: author local-part (see '%cl') respecting .mailmap, see
+ linkgit:git-shortlog[1] or linkgit:git-blame[1])
'%cd':: committer date (format respects --date= option)
'%cD':: committer date, RFC2822 style
'%cr':: committer date, relative
@@ -208,7 +214,7 @@ endif::git-rev-list[]
'%GP':: show the fingerprint of the primary key whose subkey was used
to sign a signed commit
'%gD':: reflog selector, e.g., `refs/stash@{1}` or `refs/stash@{2
- minutes ago`}; the format follows the rules described for the
+ minutes ago}`; the format follows the rules described for the
`-g` option. The portion before the `@` is the refname as
given on the command line (so `git log -g refs/heads/master`
would yield `refs/heads/master@{0}`).
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index bb1251c..90ff9e2 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -756,6 +756,22 @@ explicitly-given commit or tree.
Note that the form '--filter=sparse:path=<path>' that wants to read
from an arbitrary path on the filesystem has been dropped for security
reasons.
++
+Multiple '--filter=' flags can be specified to combine filters. Only
+objects which are accepted by every filter are included.
++
+The form '--filter=combine:<filter1>+<filter2>+...<filterN>' can also be
+used to combined several filters, but this is harder than just repeating
+the '--filter' flag and is usually not necessary. Filters are joined by
+'{plus}' and individual filters are %-encoded (i.e. URL-encoded).
+Besides the '{plus}' and '%' characters, the following characters are
+reserved and also must be encoded: `~!@#$^&*()[]{}\;",<>?`+&#39;&#96;+
+as well as all characters with ASCII code &lt;= `0x20`, which includes
+space and newline.
++
+Other arbitrary characters can also be encoded. For instance,
+'combine:tree:3+blob:none' and 'combine:tree%3A3+blob%3Anone' are
+equivalent.
--no-filter::
Turn off any previous `--filter=` argument.
diff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt
index 5abb8e8..76b6e4f 100644
--- a/Documentation/technical/api-directory-listing.txt
+++ b/Documentation/technical/api-directory-listing.txt
@@ -111,11 +111,11 @@ marked. If you to exclude files, make sure you have loaded index first.
* Prepare `struct dir_struct dir` and clear it with `memset(&dir, 0,
sizeof(dir))`.
-* To add single exclude pattern, call `add_exclude_list()` and then
- `add_exclude()`.
+* To add single exclude pattern, call `add_pattern_list()` and then
+ `add_pattern()`.
* To add patterns from a file (e.g. `.git/info/exclude`), call
- `add_excludes_from_file()` , and/or set `dir.exclude_per_dir`. A
+ `add_patterns_from_file()` , and/or set `dir.exclude_per_dir`. A
short-hand function `setup_standard_excludes()` can be used to set
up the standard set of exclude settings.
diff --git a/Documentation/technical/api-grep.txt b/Documentation/technical/api-grep.txt
deleted file mode 100644
index a69cc89..0000000
--- a/Documentation/technical/api-grep.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-grep API
-========
-
-Talk about <grep.h>, things like:
-
-* grep_buffer()
-
-(JC)
diff --git a/Documentation/technical/api-object-access.txt b/Documentation/technical/api-object-access.txt
deleted file mode 100644
index 5b29622..0000000
--- a/Documentation/technical/api-object-access.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-object access API
-=================
-
-Talk about <sha1-file.c> and <object.h> family, things like
-
-* read_sha1_file()
-* read_object_with_reference()
-* has_sha1_file()
-* write_sha1_file()
-* pretend_object_file()
-* lookup_{object,commit,tag,blob,tree}
-* parse_{object,commit,tag,blob,tree}
-* Use of object flags
-
-(JC, Shawn, Daniel, Dscho, Linus)
diff --git a/Documentation/technical/api-quote.txt b/Documentation/technical/api-quote.txt
deleted file mode 100644
index e8a1bce..0000000
--- a/Documentation/technical/api-quote.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-quote API
-=========
-
-Talk about <quote.h>, things like
-
-* sq_quote and unquote
-* c_style quote and unquote
-* quoting for foreign languages
-
-(JC)
diff --git a/Documentation/technical/api-trace2.txt b/Documentation/technical/api-trace2.txt
index 71eb081..a045dbe 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":"1","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":"2","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}
@@ -142,10 +142,9 @@ system or global config value to one of the following:
include::../trace2-target-values.txt[]
-If the target already exists and is a directory, the traces will be
-written to files (one per process) underneath the given directory. They
-will be named according to the last component of the SID (optionally
-followed by a counter to avoid filename collisions).
+When trace files are written to a target directory, they will be named according
+to the last component of the SID (optionally followed by a counter to avoid
+filename collisions).
== Trace2 API
@@ -605,17 +604,35 @@ only present on the "start" and "atexit" events.
==== Event-Specific Key/Value Pairs
`"version"`::
- This event gives the version of the executable and the EVENT format.
+ This event gives the version of the executable and the EVENT format. It
+ should always be the first event in a trace session. The EVENT format
+ version will be incremented if new event types are added, if existing
+ fields are removed, or if there are significant changes in
+ interpretation of existing events or fields. Smaller changes, such as
+ adding a new field to an existing event, will not require an increment
+ to the EVENT format version.
+
------------
{
"event":"version",
...
- "evt":"1", # EVENT format version
+ "evt":"2", # EVENT format version
"exe":"2.20.1.155.g426c96fcdb" # git version
}
------------
+`"discard"`::
+ This event is written to the git-trace2-discard sentinel file if there
+ are too many files in the target trace directory (see the
+ trace2.maxFiles config option).
++
+------------
+{
+ "event":"discard",
+ ...
+}
+------------
+
`"start"`::
This event contains the complete argv received by main().
+
diff --git a/Documentation/technical/api-tree-walking.txt b/Documentation/technical/api-tree-walking.txt
index bde1862..7962e32 100644
--- a/Documentation/technical/api-tree-walking.txt
+++ b/Documentation/technical/api-tree-walking.txt
@@ -62,9 +62,7 @@ Initializing
`setup_traverse_info`::
Initialize a `traverse_info` given the pathname of the tree to start
- traversing from. The `base` argument is assumed to be the `path`
- member of the `name_entry` being recursed into unless the tree is a
- top-level tree in which case the empty string ("") is used.
+ traversing from.
Walking
-------
@@ -140,6 +138,10 @@ same in the next callback invocation.
This utilizes the memory structure of a tree entry to avoid the
overhead of using a generic strlen().
+`strbuf_make_traverse_path`::
+
+ Convenience wrapper to `make_traverse_path` into a strbuf.
+
Authors
-------
diff --git a/Documentation/technical/api-xdiff-interface.txt b/Documentation/technical/api-xdiff-interface.txt
deleted file mode 100644
index 6296eca..0000000
--- a/Documentation/technical/api-xdiff-interface.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-xdiff interface API
-===================
-
-Talk about our calling convention to xdiff library, including
-xdiff_emit_consume_fn.
-
-(Dscho, JC)
diff --git a/Documentation/technical/partial-clone.txt b/Documentation/technical/partial-clone.txt
index 896c7b3..210373e 100644
--- a/Documentation/technical/partial-clone.txt
+++ b/Documentation/technical/partial-clone.txt
@@ -30,12 +30,20 @@ advance* during clone and fetch operations and thereby reduce download
times and disk usage. Missing objects can later be "demand fetched"
if/when needed.
+A remote that can later provide the missing objects is called a
+promisor remote, as it promises to send the objects when
+requested. Initialy Git supported only one promisor remote, the origin
+remote from which the user cloned and that was configured in the
+"extensions.partialClone" config option. Later support for more than
+one promisor remote has been implemented.
+
Use of partial clone requires that the user be online and the origin
-remote be available for on-demand fetching of missing objects. This may
-or may not be problematic for the user. For example, if the user can
-stay within the pre-selected subset of the source tree, they may not
-encounter any missing objects. Alternatively, the user could try to
-pre-fetch various objects if they know that they are going offline.
+remote or other promisor remotes be available for on-demand fetching
+of missing objects. This may or may not be problematic for the user.
+For example, if the user can stay within the pre-selected subset of
+the source tree, they may not encounter any missing objects.
+Alternatively, the user could try to pre-fetch various objects if they
+know that they are going offline.
Non-Goals
@@ -100,18 +108,18 @@ or commits that reference missing trees.
Handling Missing Objects
------------------------
-- An object may be missing due to a partial clone or fetch, or missing due
- to repository corruption. To differentiate these cases, the local
- repository specially indicates such filtered packfiles obtained from the
- promisor remote as "promisor packfiles".
+- An object may be missing due to a partial clone or fetch, or missing
+ due to repository corruption. To differentiate these cases, the
+ local repository specially indicates such filtered packfiles
+ obtained from promisor remotes as "promisor packfiles".
+
These promisor packfiles consist of a "<name>.promisor" file with
arbitrary contents (like the "<name>.keep" files), in addition to
their "<name>.pack" and "<name>.idx" files.
- The local repository considers a "promisor object" to be an object that
- it knows (to the best of its ability) that the promisor remote has promised
- that it has, either because the local repository has that object in one of
+ it knows (to the best of its ability) that promisor remotes have promised
+ that they have, either because the local repository has that object in one of
its promisor packfiles, or because another promisor object refers to it.
+
When Git encounters a missing object, Git can see if it is a promisor object
@@ -123,12 +131,12 @@ expensive-to-modify list of missing objects.[a]
- Since almost all Git code currently expects any referenced object to be
present locally and because we do not want to force every command to do
a dry-run first, a fallback mechanism is added to allow Git to attempt
- to dynamically fetch missing objects from the promisor remote.
+ to dynamically fetch missing objects from promisor remotes.
+
When the normal object lookup fails to find an object, Git invokes
-fetch-object to try to get the object from the server and then retry
-the object lookup. This allows objects to be "faulted in" without
-complicated prediction algorithms.
+promisor_remote_get_direct() to try to get the object from a promisor
+remote and then retry the object lookup. This allows objects to be
+"faulted in" without complicated prediction algorithms.
+
For efficiency reasons, no check as to whether the missing object is
actually a promisor object is performed.
@@ -157,8 +165,7 @@ and prefetch those objects in bulk.
+
We are not happy with this global variable and would like to remove it,
but that requires significant refactoring of the object code to pass an
-additional flag. We hope that concurrent efforts to add an ODB API can
-encompass this.
+additional flag.
Fetching Missing Objects
@@ -182,21 +189,63 @@ has been updated to not use any object flags when the corresponding argument
though they are not necessary.
+Using many promisor remotes
+---------------------------
+
+Many promisor remotes can be configured and used.
+
+This allows for example a user to have multiple geographically-close
+cache servers for fetching missing blobs while continuing to do
+filtered `git-fetch` commands from the central server.
+
+When fetching objects, promisor remotes are tried one after the other
+until all the objects have been fetched.
+
+Remotes that are considered "promisor" remotes are those specified by
+the following configuration variables:
+
+- `extensions.partialClone = <name>`
+
+- `remote.<name>.promisor = true`
+
+- `remote.<name>.partialCloneFilter = ...`
+
+Only one promisor remote can be configured using the
+`extensions.partialClone` config variable. This promisor remote will
+be the last one tried when fetching objects.
+
+We decided to make it the last one we try, because it is likely that
+someone using many promisor remotes is doing so because the other
+promisor remotes are better for some reason (maybe they are closer or
+faster for some kind of objects) than the origin, and the origin is
+likely to be the remote specified by extensions.partialClone.
+
+This justification is not very strong, but one choice had to be made,
+and anyway the long term plan should be to make the order somehow
+fully configurable.
+
+For now though the other promisor remotes will be tried in the order
+they appear in the config file.
+
Current Limitations
-------------------
-- The remote used for a partial clone (or the first partial fetch
- following a regular clone) is marked as the "promisor remote".
+- It is not possible to specify the order in which the promisor
+ remotes are tried in other ways than the order in which they appear
+ in the config file.
+
-We are currently limited to a single promisor remote and only that
-remote may be used for subsequent partial fetches.
+It is also not possible to specify an order to be used when fetching
+from one remote and a different order when fetching from another
+remote.
+
+- It is not possible to push only specific objects to a promisor
+ remote.
+
-We accept this limitation because we believe initial users of this
-feature will be using it on repositories with a strong single central
-server.
+It is not possible to push at the same time to multiple promisor
+remote in a specific order.
-- Dynamic object fetching will only ask the promisor remote for missing
- objects. We assume that the promisor remote has a complete view of the
+- Dynamic object fetching will only ask promisor remotes for missing
+ objects. We assume that promisor remotes have a complete view of the
repository and can satisfy all such requests.
- Repack essentially treats promisor and non-promisor packfiles as 2
@@ -218,15 +267,17 @@ server.
Future Work
-----------
-- Allow more than one promisor remote and define a strategy for fetching
- missing objects from specific promisor remotes or of iterating over the
- set of promisor remotes until a missing object is found.
+- Improve the way to specify the order in which promisor remotes are
+ tried.
+
-A user might want to have multiple geographically-close cache servers
-for fetching missing blobs while continuing to do filtered `git-fetch`
-commands from the central server, for example.
+For example this could allow to specify explicitly something like:
+"When fetching from this remote, I want to use these promisor remotes
+in this order, though, when pushing or fetching to that remote, I want
+to use those promisor remotes in that order."
+
+- Allow pushing to promisor remotes.
+
-Or the user might want to work in a triangular work flow with multiple
+The user might want to work in a triangular work flow with multiple
promisor remotes that each have an incomplete view of the repository.
- Allow repack to work on promisor packfiles (while keeping them distinct
diff --git a/Documentation/trace2-target-values.txt b/Documentation/trace2-target-values.txt
index 27d3c64..3985b6d 100644
--- a/Documentation/trace2-target-values.txt
+++ b/Documentation/trace2-target-values.txt
@@ -2,7 +2,9 @@
* `0` or `false` - Disables the target.
* `1` or `true` - Writes to `STDERR`.
* `[2-9]` - Writes to the already opened file descriptor.
-* `<absolute-pathname>` - Writes to the file in append mode.
+* `<absolute-pathname>` - Writes to the file in append mode. If the target
+already exists and is a directory, the traces will be written to files (one
+per process) underneath the given directory.
* `af_unix:[<socket_type>:]<absolute-pathname>` - Write to a
Unix DomainSocket (on platforms that support them). Socket
type can be either `stream` or `dgram`; if omitted Git will
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 8bce75b..06bd899 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1,5 +1,4 @@
-Git User Manual
-===============
+= Git User Manual
Git is a fast distributed revision control system.
@@ -41,12 +40,10 @@ complete.
[[repositories-and-branches]]
-Repositories and Branches
-=========================
+== Repositories and Branches
[[how-to-get-a-git-repository]]
-How to get a Git repository
----------------------------
+=== How to get a Git repository
It will be useful to have a Git repository to experiment with as you
read this manual.
@@ -73,8 +70,7 @@ top-level directory named `.git`, which contains all the information
about the history of the project.
[[how-to-check-out]]
-How to check out a different version of a project
--------------------------------------------------
+=== How to check out a different version of a project
Git is best thought of as a tool for storing the history of a collection
of files. It stores the history as a compressed collection of
@@ -151,8 +147,7 @@ with no way to find the history it used to point to; so use this command
carefully.
[[understanding-commits]]
-Understanding History: Commits
-------------------------------
+=== Understanding History: Commits
Every change in the history of a project is represented by a commit.
The linkgit:git-show[1] command shows the most recent commit on the
@@ -202,8 +197,7 @@ history, including file data and directory contents, is stored in an object
with a name that is a hash of its contents.
[[understanding-reachability]]
-Understanding history: commits, parents, and reachability
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Understanding history: commits, parents, and reachability
Every commit (except the very first commit in a project) also has a
parent commit which shows what happened before this commit.
@@ -227,8 +221,7 @@ that Y is a descendant of X, or that there is a chain of parents
leading from commit Y to commit X.
[[history-diagrams]]
-Understanding history: History diagrams
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Understanding history: History diagrams
We will sometimes represent Git history using diagrams like the one
below. Commits are shown as "o", and the links between them with
@@ -247,8 +240,7 @@ If we need to talk about a particular commit, the character "o" may
be replaced with another letter or number.
[[what-is-a-branch]]
-Understanding history: What is a branch?
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Understanding history: What is a branch?
When we need to be precise, we will use the word "branch" to mean a line
of development, and "branch head" (or just "head") to mean a reference
@@ -261,8 +253,7 @@ However, when no confusion will result, we often just use the term
"branch" both for branches and for branch heads.
[[manipulating-branches]]
-Manipulating branches
----------------------
+=== Manipulating branches
Creating, deleting, and modifying branches is quick and easy; here's
a summary of the commands:
@@ -299,8 +290,7 @@ ref: refs/heads/master
------------------------------------------------
[[detached-head]]
-Examining an old version without creating a new branch
-------------------------------------------------------
+=== Examining an old version without creating a new branch
The `git switch` command normally expects a branch head, but will also
accept an arbitrary commit when invoked with --detach; for example,
@@ -340,8 +330,7 @@ make up a name for the new branch. You can still create a new branch
(or tag) for this version later if you decide to.
[[examining-remote-branches]]
-Examining branches from a remote repository
--------------------------------------------
+=== Examining branches from a remote repository
The "master" branch that was created at the time you cloned is a copy
of the HEAD in the repository that you cloned from. That repository
@@ -383,8 +372,7 @@ Note that the name "origin" is just the name that Git uses by default
to refer to the repository that you cloned from.
[[how-git-stores-references]]
-Naming branches, tags, and other references
--------------------------------------------
+=== Naming branches, tags, and other references
Branches, remote-tracking branches, and tags are all references to
commits. All references are named with a slash-separated path name
@@ -413,8 +401,7 @@ references with the same shorthand name, see the "SPECIFYING
REVISIONS" section of linkgit:gitrevisions[7].
[[Updating-a-repository-With-git-fetch]]
-Updating a repository with git fetch
-------------------------------------
+=== Updating a repository with git fetch
After you clone a repository and commit a few changes of your own, you
may wish to check the original repository for updates.
@@ -425,8 +412,7 @@ repository. It will not touch any of your own branches--not even the
"master" branch that was created for you on clone.
[[fetching-branches]]
-Fetching branches from other repositories
------------------------------------------
+=== Fetching branches from other repositories
You can also track branches from repositories other than the one you
cloned from, using linkgit:git-remote[1]:
@@ -474,8 +460,7 @@ text editor. (See the "CONFIGURATION FILE" section of
linkgit:git-config[1] for details.)
[[exploring-git-history]]
-Exploring Git history
-=====================
+== Exploring Git history
Git is best thought of as a tool for storing the history of a
collection of files. It does this by storing compressed snapshots of
@@ -489,8 +474,7 @@ We start with one specialized tool that is useful for finding the
commit that introduced a bug into a project.
[[using-bisect]]
-How to use bisect to find a regression
---------------------------------------
+=== How to use bisect to find a regression
Suppose version 2.6.18 of your project worked, but the version at
"master" crashes. Sometimes the best way to find the cause of such a
@@ -572,8 +556,7 @@ linkgit:git-bisect[1] for more information about this and other `git
bisect` features.
[[naming-commits]]
-Naming commits
---------------
+=== Naming commits
We have seen several ways of naming commits already:
@@ -637,8 +620,7 @@ e05db0fd4f31dde7005f075a84f96b360d05984b
-------------------------------------------------
[[creating-tags]]
-Creating tags
--------------
+=== Creating tags
We can also create a tag to refer to a particular commit; after
running
@@ -655,8 +637,7 @@ should create a tag object instead; see the linkgit:git-tag[1] man page
for details.
[[browsing-revisions]]
-Browsing revisions
-------------------
+=== Browsing revisions
The linkgit:git-log[1] command can show lists of commits. On its
own, it shows all commits reachable from the parent commit; but you
@@ -697,8 +678,7 @@ multiple independent lines of development, the particular order that
commits are listed in may be somewhat arbitrary.
[[generating-diffs]]
-Generating diffs
-----------------
+=== Generating diffs
You can generate diffs between any two versions using
linkgit:git-diff[1]:
@@ -726,8 +706,7 @@ will generate a file with a patch for each commit reachable from test
but not from master.
[[viewing-old-file-versions]]
-Viewing old file versions
--------------------------
+=== Viewing old file versions
You can always view an old version of a file by just checking out the
correct revision first. But sometimes it is more convenient to be
@@ -742,12 +721,10 @@ Before the colon may be anything that names a commit, and after it
may be any path to a file tracked by Git.
[[history-examples]]
-Examples
---------
+=== Examples
[[counting-commits-on-a-branch]]
-Counting the number of commits on a branch
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Counting the number of commits on a branch
Suppose you want to know how many commits you've made on `mybranch`
since it diverged from `origin`:
@@ -765,8 +742,7 @@ $ git rev-list origin..mybranch | wc -l
-------------------------------------------------
[[checking-for-equal-branches]]
-Check whether two branches point at the same history
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Check whether two branches point at the same history
Suppose you want to check whether two branches point at the same point
in history.
@@ -798,8 +774,7 @@ $ git log origin...master
will return no commits when the two branches are equal.
[[finding-tagged-descendants]]
-Find first tagged version including a given fix
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Find first tagged version including a given fix
Suppose you know that the commit e05db0fd fixed a certain problem.
You'd like to find the earliest tagged release that contains that
@@ -883,8 +858,7 @@ shows that e05db0fd is reachable from itself, from v1.5.0-rc1,
and from v1.5.0-rc2, and not from v1.5.0-rc0.
[[showing-commits-unique-to-a-branch]]
-Showing commits unique to a given branch
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Showing commits unique to a given branch
Suppose you would like to see all the commits reachable from the branch
head named `master` but not from any other head in your repository.
@@ -931,8 +905,7 @@ $ gitk $( git show-ref --heads ) --not $( git show-ref --tags )
syntax such as `--not`.)
[[making-a-release]]
-Creating a changelog and tarball for a software release
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Creating a changelog and tarball for a software release
The linkgit:git-archive[1] command can create a tar or zip archive from
any version of a project; for example:
@@ -983,8 +956,7 @@ and then he just cut-and-pastes the output commands after verifying that
they look OK.
[[Finding-commits-With-given-Content]]
-Finding commits referencing a file with given content
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Finding commits referencing a file with given content
Somebody hands you a copy of a file, and asks which commits modified a
file such that it contained the given content either before or after the
@@ -1000,12 +972,10 @@ student. The linkgit:git-log[1], linkgit:git-diff-tree[1], and
linkgit:git-hash-object[1] man pages may prove helpful.
[[Developing-With-git]]
-Developing with Git
-===================
+== Developing with Git
[[telling-git-your-name]]
-Telling Git your name
----------------------
+=== Telling Git your name
Before creating any commits, you should introduce yourself to Git.
The easiest way to do so is to use linkgit:git-config[1]:
@@ -1030,8 +1000,7 @@ also edit it with your favorite editor.
[[creating-a-new-repository]]
-Creating a new repository
--------------------------
+=== Creating a new repository
Creating a new repository from scratch is very easy:
@@ -1052,8 +1021,7 @@ $ git commit
-------------------------------------------------
[[how-to-make-a-commit]]
-How to make a commit
---------------------
+=== How to make a commit
Creating a new commit takes three steps:
@@ -1148,8 +1116,7 @@ for inclusion in the index (by right-clicking on the diff hunk and
choosing "Stage Hunk For Commit").
[[creating-good-commit-messages]]
-Creating good commit messages
------------------------------
+=== Creating good commit messages
Though not required, it's a good idea to begin the commit message
with a single short (less than 50 character) line summarizing the
@@ -1162,8 +1129,7 @@ rest of the commit in the body.
[[ignoring-files]]
-Ignoring files
---------------
+=== Ignoring files
A project will often generate files that you do 'not' want to track with Git.
This typically includes files generated by a build process or temporary
@@ -1205,8 +1171,7 @@ Some Git commands can also take exclude patterns directly on the
command line. See linkgit:gitignore[5] for the details.
[[how-to-merge]]
-How to merge
-------------
+=== How to merge
You can rejoin two diverging branches of development using
linkgit:git-merge[1]:
@@ -1254,8 +1219,7 @@ has two parents, one pointing to the top of the current branch, and
one to the top of the other branch.
[[resolving-a-merge]]
-Resolving a merge
------------------
+=== Resolving a merge
When a merge isn't resolved automatically, Git leaves the index and
the working tree in a special state that gives you all the
@@ -1297,8 +1261,7 @@ The above is all you need to know to resolve a simple merge. But Git
also provides more information to help resolve conflicts:
[[conflict-resolution]]
-Getting conflict-resolution help during a merge
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Getting conflict-resolution help during a merge
All of the changes that Git was able to merge automatically are
already added to the index file, so linkgit:git-diff[1] shows only
@@ -1401,8 +1364,7 @@ the different stages of that file will be "collapsed", after which
`git diff` will (by default) no longer show diffs for that file.
[[undoing-a-merge]]
-Undoing a merge
----------------
+=== Undoing a merge
If you get stuck and decide to just give up and throw the whole mess
away, you can always return to the pre-merge state with
@@ -1423,8 +1385,7 @@ itself have been merged into another branch, as doing so may confuse
further merges.
[[fast-forwards]]
-Fast-forward merges
--------------------
+=== Fast-forward merges
There is one special case not mentioned above, which is treated
differently. Normally, a merge results in a merge commit, with two
@@ -1438,8 +1399,7 @@ to point at the head of the merged-in branch, without any new commits being
created.
[[fixing-mistakes]]
-Fixing mistakes
----------------
+=== Fixing mistakes
If you've messed up the working tree, but haven't yet committed your
mistake, you can return the entire working tree to the last committed
@@ -1463,8 +1423,7 @@ fundamentally different ways to fix the problem:
a branch that has had its history changed.
[[reverting-a-commit]]
-Fixing a mistake with a new commit
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Fixing a mistake with a new commit
Creating a new commit that reverts an earlier change is very easy;
just pass the linkgit:git-revert[1] command a reference to the bad
@@ -1490,8 +1449,7 @@ conflicts manually, just as in the case of <<resolving-a-merge,
resolving a merge>>.
[[fixing-a-mistake-by-rewriting-history]]
-Fixing a mistake by rewriting history
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Fixing a mistake by rewriting history
If the problematic commit is the most recent commit, and you have not
yet made that commit public, then you may just
@@ -1518,8 +1476,7 @@ this is an advanced topic to be left for
<<cleaning-up-history,another chapter>>.
[[checkout-of-path]]
-Checking out an old version of a file
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Checking out an old version of a file
In the process of undoing a previous bad change, you may find it
useful to check out an older version of a particular file using
@@ -1543,8 +1500,7 @@ $ git show HEAD^:path/to/file
which will display the given version of the file.
[[interrupted-work]]
-Temporarily setting aside work in progress
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Temporarily setting aside work in progress
While you are in the middle of working on something complicated, you
find an unrelated but obvious and trivial bug. You would like to fix it
@@ -1575,8 +1531,7 @@ $ git stash pop
[[ensuring-good-performance]]
-Ensuring good performance
--------------------------
+=== Ensuring good performance
On large repositories, Git depends on compression to keep the history
information from taking up too much space on disk or in memory. Some
@@ -1587,12 +1542,10 @@ to avoid automatic compression kicking in when it is not convenient.
[[ensuring-reliability]]
-Ensuring reliability
---------------------
+=== Ensuring reliability
[[checking-for-corruption]]
-Checking the repository for corruption
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Checking the repository for corruption
The linkgit:git-fsck[1] command runs a number of self-consistency checks
on the repository, and reports on any problems. This may take some
@@ -1618,12 +1571,10 @@ You can run `git fsck --no-dangling` to suppress these messages, and still
view real errors.
[[recovering-lost-changes]]
-Recovering lost changes
-~~~~~~~~~~~~~~~~~~~~~~~
+==== Recovering lost changes
[[reflogs]]
-Reflogs
-^^^^^^^
+===== Reflogs
Say you modify a branch with <<fixing-mistakes,`git reset --hard`>>,
and then realize that the branch was the only reference you had to
@@ -1670,8 +1621,7 @@ same project, the reflog history is not shared: it tells you only about
how the branches in your local repository have changed over time.
[[dangling-object-recovery]]
-Examining dangling objects
-^^^^^^^^^^^^^^^^^^^^^^^^^^
+===== Examining dangling objects
In some situations the reflog may not be able to save you. For example,
suppose you delete a branch, then realize you need the history it
@@ -1715,12 +1665,10 @@ dangling objects can arise in other situations.
[[sharing-development]]
-Sharing development with others
-===============================
+== Sharing development with others
[[getting-updates-With-git-pull]]
-Getting updates with git pull
------------------------------
+=== Getting updates with git pull
After you clone a repository and commit a few changes of your own, you
may wish to check the original repository for updates and merge them
@@ -1783,8 +1731,7 @@ $ git merge branch
are roughly equivalent.
[[submitting-patches]]
-Submitting patches to a project
--------------------------------
+=== Submitting patches to a project
If you just have a few changes, the simplest way to submit them may
just be to send them as patches in email:
@@ -1812,8 +1759,7 @@ Consult the mailing list for your project first to determine
their requirements for submitting patches.
[[importing-patches]]
-Importing patches to a project
-------------------------------
+=== Importing patches to a project
Git also provides a tool called linkgit:git-am[1] (am stands for
"apply mailbox"), for importing such an emailed series of patches.
@@ -1845,8 +1791,7 @@ the original mailbox, with authorship and commit log message each
taken from the message containing each patch.
[[public-repositories]]
-Public Git repositories
------------------------
+=== Public Git repositories
Another way to submit changes to a project is to tell the maintainer
of that project to pull the changes from your repository using
@@ -1886,21 +1831,22 @@ pull from that repository. So the flow of changes, in a situation
where there is one other developer with a public repository, looks
like this:
- you push
- your personal repo ------------------> your public repo
- ^ |
- | |
- | you pull | they pull
- | |
- | |
- | they push V
- their public repo <------------------- their repo
+....
+ you push
+your personal repo ------------------> your public repo
+ ^ |
+ | |
+ | you pull | they pull
+ | |
+ | |
+ | they push V
+their public repo <------------------- their repo
+....
We explain how to do this in the following sections.
[[setting-up-a-public-repository]]
-Setting up a public repository
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Setting up a public repository
Assume your personal repository is in the directory `~/proj`. We
first create a new clone of the repository and tell `git daemon` that it
@@ -1920,8 +1866,7 @@ public repository. You can use scp, rsync, or whatever is most
convenient.
[[exporting-via-git]]
-Exporting a Git repository via the Git protocol
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Exporting a Git repository via the Git protocol
This is the preferred method.
@@ -1942,8 +1887,7 @@ linkgit:git-daemon[1] man page for details. (See especially the
examples section.)
[[exporting-via-http]]
-Exporting a git repository via HTTP
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Exporting a git repository via HTTP
The Git protocol gives better performance and reliability, but on a
host with a web server set up, HTTP exports may be simpler to set up.
@@ -1975,8 +1919,7 @@ for a slightly more sophisticated setup using WebDAV which also
allows pushing over HTTP.)
[[pushing-changes-to-a-public-repository]]
-Pushing changes to a public repository
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Pushing changes to a public repository
Note that the two techniques outlined above (exporting via
<<exporting-via-http,http>> or <<exporting-via-git,git>>) allow other
@@ -2035,8 +1978,7 @@ See the explanations of the `remote.<name>.url`,
linkgit:git-config[1] for details.
[[forcing-push]]
-What to do when a push fails
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== What to do when a push fails
If a push would not result in a <<fast-forwards,fast-forward>> of the
remote branch, then it will fail with an error like:
@@ -2090,8 +2032,7 @@ pull, or by a fetch followed by a rebase; see the
linkgit:gitcvs-migration[7] for more.
[[setting-up-a-shared-repository]]
-Setting up a shared repository
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Setting up a shared repository
Another way to collaborate is by using a model similar to that
commonly used in CVS, where several developers with special rights
@@ -2121,8 +2062,7 @@ advantages over the central shared repository:
"out".
[[setting-up-gitweb]]
-Allowing web browsing of a repository
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Allowing web browsing of a repository
The gitweb cgi script provides users an easy way to browse your
project's revisions, file contents and logs without having to install
@@ -2138,8 +2078,7 @@ linkgit:gitweb[1] for instructions on details setting up a permanent
installation with a CGI or Perl capable server.
[[how-to-get-a-git-repository-with-minimal-history]]
-How to get a Git repository with minimal history
-------------------------------------------------
+=== How to get a Git repository with minimal history
A <<def_shallow_clone,shallow clone>>, with its truncated
history, is useful when one is interested only in recent history
@@ -2158,12 +2097,10 @@ have to result in huge conflicts. This limitation may make such
a repository unsuitable to be used in merge based workflows.
[[sharing-development-examples]]
-Examples
---------
+=== Examples
[[maintaining-topic-branches]]
-Maintaining topic branches for a Linux subsystem maintainer
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Maintaining topic branches for a Linux subsystem maintainer
This describes how Tony Luck uses Git in his role as maintainer of the
IA64 architecture for the Linux kernel.
@@ -2459,8 +2396,7 @@ done
[[cleaning-up-history]]
-Rewriting history and maintaining patch series
-==============================================
+== Rewriting history and maintaining patch series
Normally commits are only added to a project, never taken away or
replaced. Git is designed with this assumption, and violating it will
@@ -2470,8 +2406,7 @@ However, there is a situation in which it can be useful to violate this
assumption.
[[patch-series]]
-Creating the perfect patch series
----------------------------------
+=== Creating the perfect patch series
Suppose you are a contributor to a large project, and you want to add a
complicated feature, and to present it to the other developers in a way
@@ -2503,8 +2438,7 @@ use them, and then explain some of the problems that can arise because
you are rewriting history.
[[using-git-rebase]]
-Keeping a patch series up to date using git rebase
---------------------------------------------------
+=== Keeping a patch series up to date using git rebase
Suppose that you create a branch `mywork` on a remote-tracking branch
`origin`, and create some commits on top of it:
@@ -2591,8 +2525,7 @@ the rebase. See <<interactive-rebase>> for details, and
<<reordering-patch-series>> for alternatives.
[[rewriting-one-commit]]
-Rewriting a single commit
--------------------------
+=== Rewriting a single commit
We saw in <<fixing-a-mistake-by-rewriting-history>> that you can replace the
most recent commit using
@@ -2610,8 +2543,7 @@ If you need to amend commits from deeper in your history, you can
use <<interactive-rebase,interactive rebase's `edit` instruction>>.
[[reordering-patch-series]]
-Reordering or selecting from a patch series
--------------------------------------------
+=== Reordering or selecting from a patch series
Sometimes you want to edit a commit deeper in your history. One
approach is to use `git format-patch` to create a series of patches
@@ -2630,8 +2562,7 @@ $ git am *.patch
-------------------------------------------------
[[interactive-rebase]]
-Using interactive rebases
--------------------------
+=== Using interactive rebases
You can also edit a patch series with an interactive rebase. This is
the same as <<reordering-patch-series,reordering a patch series using
@@ -2688,16 +2619,14 @@ For a more detailed discussion of the procedure and additional tips,
see the "INTERACTIVE MODE" section of linkgit:git-rebase[1].
[[patch-series-tools]]
-Other tools
------------
+=== Other tools
There are numerous other tools, such as StGit, which exist for the
purpose of maintaining a patch series. These are outside of the scope of
this manual.
[[problems-With-rewriting-history]]
-Problems with rewriting history
--------------------------------
+=== Problems with rewriting history
The primary problem with rewriting the history of a branch has to do
with merging. Suppose somebody fetches your branch and merges it into
@@ -2745,8 +2674,7 @@ For true distributed development that supports proper merging,
published branches should never be rewritten.
[[bisect-merges]]
-Why bisecting merge commits can be harder than bisecting linear history
------------------------------------------------------------------------
+=== Why bisecting merge commits can be harder than bisecting linear history
The linkgit:git-bisect[1] command correctly handles history that
includes merge commits. However, when the commit that it finds is a
@@ -2811,12 +2739,10 @@ linear by rebasing against the latest upstream version before
publishing.
[[advanced-branch-management]]
-Advanced branch management
-==========================
+== Advanced branch management
[[fetching-individual-branches]]
-Fetching individual branches
-----------------------------
+=== Fetching individual branches
Instead of using linkgit:git-remote[1], you can also choose just
to update one branch at a time, and to store it locally under an
@@ -2844,8 +2770,7 @@ already have a branch named example-master, it will attempt to
master branch. In more detail:
[[fetch-fast-forwards]]
-git fetch and fast-forwards
----------------------------
+=== git fetch and fast-forwards
In the previous example, when updating an existing branch, `git fetch`
checks to make sure that the most recent commit on the remote
@@ -2882,8 +2807,7 @@ unless you've already created a reference of your own pointing to
them.
[[forcing-fetch]]
-Forcing git fetch to do non-fast-forward updates
-------------------------------------------------
+=== Forcing git fetch to do non-fast-forward updates
If git fetch fails because the new head of a branch is not a
descendant of the old head, you may force the update with:
@@ -2903,8 +2827,7 @@ Be aware that commits that the old version of example/master pointed at
may be lost, as we saw in the previous section.
[[remote-branch-configuration]]
-Configuring remote-tracking branches
-------------------------------------
+=== Configuring remote-tracking branches
We saw above that `origin` is just a shortcut to refer to the
repository that you originally cloned from. This information is
@@ -2955,8 +2878,7 @@ the refspec syntax.
[[git-concepts]]
-Git concepts
-============
+== Git concepts
Git is built on a small number of simple but powerful ideas. While it
is possible to get things done without understanding them, you will find
@@ -2966,8 +2888,7 @@ We start with the most important, the <<def_object_database,object
database>> and the <<def_index,index>>.
[[the-object-database]]
-The Object Database
--------------------
+=== The Object Database
We already saw in <<understanding-commits>> that all commits are stored
@@ -3011,8 +2932,7 @@ There are four different types of objects: "blob", "tree", "commit", and
The object types in some more detail:
[[commit-object]]
-Commit Object
-~~~~~~~~~~~~~
+==== Commit Object
The "commit" object links a physical state of a tree with a description
of how we got there and why. Use the `--pretty=raw` option to
@@ -3064,8 +2984,7 @@ commit whose parent is normally the current HEAD, and whose tree is
taken from the content currently stored in the index.
[[tree-object]]
-Tree Object
-~~~~~~~~~~~
+==== Tree Object
The ever-versatile linkgit:git-show[1] command can also be used to
examine tree objects, but linkgit:git-ls-tree[1] will give you more
@@ -3104,8 +3023,7 @@ Note that the files all have mode 644 or 755: Git actually only pays
attention to the executable bit.
[[blob-object]]
-Blob Object
-~~~~~~~~~~~
+==== Blob Object
You can use linkgit:git-show[1] to examine the contents of a blob; take,
for example, the blob in the entry for `COPYING` from the tree above:
@@ -3134,8 +3052,7 @@ sometimes be useful for browsing the contents of a tree that is not
currently checked out.
[[trust]]
-Trust
-~~~~~
+==== Trust
If you receive the SHA-1 name of a blob from one source, and its contents
from another (possibly untrusted) source, you can still trust that those
@@ -3164,8 +3081,7 @@ like GPG/PGP.
To assist in this, Git also provides the tag object...
[[tag-object]]
-Tag Object
-~~~~~~~~~~
+==== Tag Object
A tag object contains an object, object type, tag name, the name of the
person ("tagger") who created the tag, and a message, which may contain
@@ -3194,8 +3110,7 @@ objects. (Note that linkgit:git-tag[1] can also be used to create
references whose names begin with `refs/tags/`).
[[pack-files]]
-How Git stores objects efficiently: pack files
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== How Git stores objects efficiently: pack files
Newly created objects are initially created in a file named after the
object's SHA-1 hash (stored in `.git/objects`).
@@ -3253,8 +3168,7 @@ The linkgit:git-gc[1] command performs packing, pruning, and more for
you, so is normally the only high-level command you need.
[[dangling-objects]]
-Dangling objects
-~~~~~~~~~~~~~~~~
+==== Dangling objects
The linkgit:git-fsck[1] command will sometimes complain about dangling
objects. They are not a problem.
@@ -3334,8 +3248,7 @@ don't want to do that while the filesystem is mounted.
accesses to a repository but you might receive confusing or scary messages.)
[[recovering-from-repository-corruption]]
-Recovering from repository corruption
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== Recovering from repository corruption
By design, Git treats data trusted to it with caution. However, even in
the absence of bugs in Git itself, it is still possible that hardware or
@@ -3452,8 +3365,7 @@ whole thing. It's up to you--Git does *have* a lot of information, it is
just missing one particular blob version.
[[the-index]]
-The index
----------
+=== The index
The index is a binary file (generally kept in `.git/index`) containing a
sorted list of path names, each with permissions and the SHA-1 of a blob
@@ -3511,8 +3423,7 @@ If you blow the index away entirely, you generally haven't lost any
information as long as you have the name of the tree that it described.
[[submodules]]
-Submodules
-==========
+== Submodules
Large projects are often composed of smaller, self-contained modules. For
example, an embedded Linux distribution's source tree would include every
@@ -3698,8 +3609,8 @@ $ git push
You have to run `git submodule update` after `git pull` if you want to update
submodules, too.
-Pitfalls with submodules
-------------------------
+[[pitfalls-with-submodules]]
+=== Pitfalls with submodules
Always publish the submodule change before publishing the change to the
superproject that references it. If you forget to publish the submodule change,
@@ -3768,8 +3679,7 @@ submodule update` will not overwrite them. Instead, you get the usual
warning about not being able switch from a dirty branch.
[[low-level-operations]]
-Low-level Git operations
-========================
+== Low-level Git operations
Many of the higher-level commands were originally implemented as shell
scripts using a smaller core of low-level Git commands. These can still
@@ -3777,8 +3687,7 @@ be useful when doing unusual things with Git, or just as a way to
understand its inner workings.
[[object-manipulation]]
-Object access and manipulation
-------------------------------
+=== Object access and manipulation
The linkgit:git-cat-file[1] command can show the contents of any object,
though the higher-level linkgit:git-show[1] is usually more useful.
@@ -3795,8 +3704,7 @@ verified by linkgit:git-verify-tag[1], though it is normally simpler to
use linkgit:git-tag[1] for both.
[[the-workflow]]
-The Workflow
-------------
+=== The Workflow
High-level operations such as linkgit:git-commit[1] and
linkgit:git-restore[1] work by moving data
@@ -3811,8 +3719,7 @@ the database or the working directory. Thus there are four main
combinations:
[[working-directory-to-index]]
-working directory -> index
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== working directory -> index
The linkgit:git-update-index[1] command updates the index with
information from the working directory. You generally update the
@@ -3848,8 +3755,7 @@ The previously introduced linkgit:git-add[1] is just a wrapper for
linkgit:git-update-index[1].
[[index-to-object-database]]
-index -> object database
-~~~~~~~~~~~~~~~~~~~~~~~~
+==== index -> object database
You write your current index file to a "tree" object with the program
@@ -3864,8 +3770,7 @@ use that tree to re-generate the index at any time by going in the
other direction:
[[object-database-to-index]]
-object database -> index
-~~~~~~~~~~~~~~~~~~~~~~~~
+==== object database -> index
You read a "tree" file from the object database, and use that to
populate (and overwrite--don't do this if your index contains any
@@ -3881,8 +3786,7 @@ earlier. However, that is only your 'index' file: your working
directory contents have not been modified.
[[index-to-working-directory]]
-index -> working directory
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+==== index -> working directory
You update your working directory from the index by "checking out"
files. This is not a very common operation, since normally you'd just
@@ -3911,8 +3815,7 @@ Finally, there are a few odds and ends which are not purely moving
from one representation to the other:
[[tying-it-all-together]]
-Tying it all together
-~~~~~~~~~~~~~~~~~~~~~
+==== Tying it all together
To commit a tree you have instantiated with `git write-tree`, you'd
create a "commit" object that refers to that tree and the history
@@ -3986,8 +3889,7 @@ Here is a picture that illustrates how various pieces fit together:
[[examining-the-data]]
-Examining the data
-------------------
+=== Examining the data
You can examine the data represented in the object database and the
index with various helper tools. For every object, you can use
@@ -4022,8 +3924,7 @@ $ git cat-file commit HEAD
to see what the top commit was.
[[merging-multiple-trees]]
-Merging multiple trees
-----------------------
+=== Merging multiple trees
Git can help you perform a three-way merge, which can in turn be
used for a many-way merge by repeating the merge procedure several
@@ -4073,8 +3974,7 @@ index file, and you can just write the result out with
[[merging-multiple-trees-2]]
-Merging multiple trees, continued
----------------------------------
+=== Merging multiple trees, continued
Sadly, many merges aren't trivial. If there are files that have
been added, moved or removed, or if both branches have modified the
@@ -4144,15 +4044,13 @@ $ git merge-index git-merge-one-file hello.c
and that is what higher level `git merge -s resolve` is implemented with.
[[hacking-git]]
-Hacking Git
-===========
+== Hacking Git
This chapter covers internal details of the Git implementation which
probably only Git developers need to understand.
[[object-details]]
-Object storage format
----------------------
+=== Object storage format
All objects have a statically determined "type" which identifies the
format of the object (i.e. how it is used, and how it can refer to other
@@ -4182,8 +4080,7 @@ of all objects, and verifies their internal consistency (in addition
to just verifying their superficial consistency through the hash).
[[birdview-on-the-source-code]]
-A birds-eye view of Git's source code
--------------------------------------
+=== A birds-eye view of Git's source code
It is not always easy for new developers to find their way through Git's
source code. This section gives you a little guidance to show where to
@@ -4392,25 +4289,22 @@ You see, Git is actually the best tool to find out about the source of Git
itself!
[[glossary]]
-Git Glossary
-============
+== Git Glossary
[[git-explained]]
-Git explained
--------------
+=== Git explained
include::glossary-content.txt[]
[[git-quick-start]]
-Appendix A: Git Quick Reference
-===============================
+[appendix]
+== Git Quick Reference
This is a quick summary of the major commands; the previous chapters
explain how these work in more detail.
[[quick-creating-a-new-repository]]
-Creating a new repository
--------------------------
+=== Creating a new repository
From a tarball:
@@ -4431,8 +4325,7 @@ $ cd project
-----------------------------------------------
[[managing-branches]]
-Managing branches
------------------
+=== Managing branches
-----------------------------------------------
$ git branch # list all local branches in this repo
@@ -4496,8 +4389,7 @@ $ git branch -r # list all remote branches
[[exploring-history]]
-Exploring history
------------------
+=== Exploring history
-----------------------------------------------
$ gitk # visualize and browse history
@@ -4532,8 +4424,7 @@ $ git bisect bad # if this revision is bad.
-----------------------------------------------
[[making-changes]]
-Making changes
---------------
+=== Making changes
Make sure Git knows who to blame:
@@ -4563,8 +4454,7 @@ $ git commit -a # use latest content of all tracked files
-----------------------------------------------
[[merging]]
-Merging
--------
+=== Merging
-----------------------------------------------
$ git merge test # merge branch "test" into the current branch
@@ -4574,8 +4464,7 @@ $ git pull . test # equivalent to git merge test
-----------------------------------------------
[[sharing-your-changes]]
-Sharing your changes
---------------------
+=== Sharing your changes
Importing or exporting patches:
@@ -4620,8 +4509,7 @@ $ git push example test
-----------------------------------------------
[[repository-maintenance]]
-Repository maintenance
-----------------------
+=== Repository maintenance
Check for corruption:
@@ -4637,12 +4525,11 @@ $ git gc
[[todo]]
-Appendix B: Notes and todo list for this manual
-===============================================
+[appendix]
+== Notes and todo list for this manual
[[todo-list]]
-Todo list
----------
+=== Todo list
This is a work in progress.
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index a1539a7..5048d9b 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.23.0
+DEF_VER=v2.24.0
LF='
'
diff --git a/Makefile b/Makefile
index f925534..58b92af 100644
--- a/Makefile
+++ b/Makefile
@@ -34,13 +34,8 @@ all::
# library. Support for version 1 will likely be removed in some future
# release of Git, as upstream has all but abandoned it.
#
-# When using USE_LIBPCRE1, define NO_LIBPCRE1_JIT if the PCRE v1
-# library is compiled without --enable-jit. We will auto-detect
-# whether the version of the PCRE v1 library in use has JIT support at
-# all, but we unfortunately can't auto-detect whether JIT support
-# hasn't been compiled in in an otherwise JIT-supporting version. If
-# you have link-time errors about a missing `pcre_jit_exec` define
-# this, or recompile PCRE v1 with --enable-jit.
+# When using USE_LIBPCRE1, define NO_LIBPCRE1_JIT if you want to
+# disable JIT even if supported by your library.
#
# Define LIBPCREDIR=/foo/bar if your PCRE header and library files are
# in /foo/bar/include and /foo/bar/lib directories. Which version of
@@ -598,6 +593,7 @@ SCRIPT_SH =
SCRIPT_LIB =
TEST_BUILTINS_OBJS =
TEST_PROGRAMS_NEED_X =
+THIRD_PARTY_SOURCES =
# Having this variable in your environment would break pipelines because
# you cause "cd" to echo its destination to stdout. It can also take
@@ -728,6 +724,7 @@ TEST_BUILTINS_OBJS += test-parse-options.o
TEST_BUILTINS_OBJS += test-path-utils.o
TEST_BUILTINS_OBJS += test-pkt-line.o
TEST_BUILTINS_OBJS += test-prio-queue.o
+TEST_BUILTINS_OBJS += test-progress.o
TEST_BUILTINS_OBJS += test-reach.o
TEST_BUILTINS_OBJS += test-read-cache.o
TEST_BUILTINS_OBJS += test-read-midx.o
@@ -818,12 +815,12 @@ VCSSVN_LIB = vcs-svn/lib.a
GENERATED_H += command-list.h
-LIB_H := $(sort $(shell git ls-files '*.h' ':!t/' ':!Documentation/' 2>/dev/null || \
+LIB_H := $(sort $(patsubst ./%,%,$(shell git ls-files '*.h' ':!t/' ':!Documentation/' 2>/dev/null || \
$(FIND) . \
-name .git -prune -o \
-name t -prune -o \
-name Documentation -prune -o \
- -name '*.h' -print))
+ -name '*.h' -print)))
LIB_OBJS += abspath.o
LIB_OBJS += advice.o
@@ -884,7 +881,6 @@ LIB_OBJS += ewah/ewah_io.o
LIB_OBJS += ewah/ewah_rlw.o
LIB_OBJS += exec-cmd.o
LIB_OBJS += fetch-negotiator.o
-LIB_OBJS += fetch-object.o
LIB_OBJS += fetch-pack.o
LIB_OBJS += fsck.o
LIB_OBJS += fsmonitor.o
@@ -948,6 +944,7 @@ LIB_OBJS += preload-index.o
LIB_OBJS += pretty.o
LIB_OBJS += prio-queue.o
LIB_OBJS += progress.o
+LIB_OBJS += promisor-remote.o
LIB_OBJS += prompt.o
LIB_OBJS += protocol.o
LIB_OBJS += quote.o
@@ -965,6 +962,7 @@ LIB_OBJS += refspec.o
LIB_OBJS += ref-filter.o
LIB_OBJS += remote.o
LIB_OBJS += replace-object.o
+LIB_OBJS += repo-settings.o
LIB_OBJS += repository.o
LIB_OBJS += rerere.o
LIB_OBJS += resolve-undo.o
@@ -983,6 +981,7 @@ LIB_OBJS += shallow.o
LIB_OBJS += sideband.o
LIB_OBJS += sigchain.o
LIB_OBJS += split-index.o
+LIB_OBJS += stable-qsort.o
LIB_OBJS += strbuf.o
LIB_OBJS += streaming.o
LIB_OBJS += string-list.o
@@ -1145,6 +1144,20 @@ BUILTIN_OBJS += builtin/verify-tag.o
BUILTIN_OBJS += builtin/worktree.o
BUILTIN_OBJS += builtin/write-tree.o
+# THIRD_PARTY_SOURCES is a list of patterns compatible with the
+# $(filter) and $(filter-out) family of functions. They specify source
+# files which are taken from some third-party source where we want to be
+# less strict about issues such as coding style so we don't diverge from
+# upstream unnecessarily (making merging in future changes easier).
+THIRD_PARTY_SOURCES += compat/inet_ntop.c
+THIRD_PARTY_SOURCES += compat/inet_pton.c
+THIRD_PARTY_SOURCES += compat/nedmalloc/%
+THIRD_PARTY_SOURCES += compat/obstack.%
+THIRD_PARTY_SOURCES += compat/poll/%
+THIRD_PARTY_SOURCES += compat/regex/%
+THIRD_PARTY_SOURCES += sha1collisiondetection/%
+THIRD_PARTY_SOURCES += sha1dc/%
+
GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB)
EXTLIBS =
@@ -1714,7 +1727,6 @@ ifdef NO_GETPAGESIZE
endif
ifdef INTERNAL_QSORT
COMPAT_CFLAGS += -DINTERNAL_QSORT
- COMPAT_OBJS += compat/qsort.o
endif
ifdef HAVE_ISO_QSORT_S
COMPAT_CFLAGS += -DHAVE_ISO_QSORT_S
@@ -1871,8 +1883,9 @@ ifndef V
QUIET_MSGFMT = @echo ' ' MSGFMT $@;
QUIET_GCOV = @echo ' ' GCOV $@;
QUIET_SP = @echo ' ' SP $<;
- QUIET_HDR = @echo ' ' HDR $<;
+ QUIET_HDR = @echo ' ' HDR $(<:hcc=h);
QUIET_RC = @echo ' ' RC $@;
+ QUIET_SPATCH = @echo ' ' SPATCH $<;
QUIET_SUBDIR0 = +@subdir=
QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
$(MAKE) $(PRINT_DIR) -C $$subdir
@@ -2599,6 +2612,7 @@ FIND_SOURCE_FILES = ( \
-o \( -name 'trash*' -type d -prune \) \
-o \( -name '*.[hcS]' -type f -print \) \
-o \( -name '*.sh' -type f -print \) \
+ | sed -e 's|^\./||' \
)
$(ETAGS_TARGET): FORCE
@@ -2768,11 +2782,16 @@ EXCEPT_HDRS := $(GEN_HDRS) compat/% xdiff/%
ifndef GCRYPT_SHA256
EXCEPT_HDRS += sha256/gcrypt.h
endif
-CHK_HDRS = $(filter-out $(EXCEPT_HDRS),$(patsubst ./%,%,$(LIB_H)))
+CHK_HDRS = $(filter-out $(EXCEPT_HDRS),$(LIB_H))
HCO = $(patsubst %.h,%.hco,$(CHK_HDRS))
+HCC = $(HCO:hco=hcc)
+
+%.hcc: %.h
+ @echo '#include "git-compat-util.h"' >$@
+ @echo '#include "$<"' >>$@
-$(HCO): %.hco: %.h FORCE
- $(QUIET_HDR)$(CC) -include git-compat-util.h -I. -o /dev/null -c -xc $<
+$(HCO): %.hco: %.hcc FORCE
+ $(QUIET_HDR)$(CC) $(ALL_CFLAGS) -o /dev/null -c -xc $<
.PHONY: hdr-check $(HCO)
hdr-check: $(HCO)
@@ -2791,15 +2810,11 @@ check: command-list.h
exit 1; \
fi
-C_SOURCES = $(patsubst %.o,%.c,$(C_OBJ))
-ifdef DC_SHA1_SUBMODULE
-COCCI_SOURCES = $(filter-out sha1collisiondetection/%,$(C_SOURCES))
-else
-COCCI_SOURCES = $(filter-out sha1dc/%,$(C_SOURCES))
-endif
+FOUND_C_SOURCES = $(filter %.c,$(shell $(FIND_SOURCE_FILES)))
+COCCI_SOURCES = $(filter-out $(THIRD_PARTY_SOURCES),$(FOUND_C_SOURCES))
%.cocci.patch: %.cocci $(COCCI_SOURCES)
- @echo ' ' SPATCH $<; \
+ $(QUIET_SPATCH) \
if test $(SPATCH_BATCH_SIZE) = 0; then \
limit=; \
else \
@@ -3028,6 +3043,10 @@ rpm::
@false
.PHONY: rpm
+ifneq ($(INCLUDE_DLLS_IN_ARTIFACTS),)
+OTHER_PROGRAMS += $(shell echo *.dll t/helper/*.dll)
+endif
+
artifacts-tar:: $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS) \
GIT-BUILD-OPTIONS $(TEST_PROGRAMS) $(test_bindir_programs) \
$(MOFILES)
@@ -3081,6 +3100,7 @@ clean: profile-clean coverage-clean cocciclean
$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
$(RM) $(TEST_PROGRAMS)
$(RM) $(FUZZ_PROGRAMS)
+ $(RM) $(HCC)
$(RM) -r bin-wrappers $(dep_dirs)
$(RM) -r po/build/
$(RM) *.pyc *.pyo */*.pyc */*.pyo command-list.h $(ETAGS_TARGET) tags cscope*
diff --git a/RelNotes b/RelNotes
index 248d137..fc657e7 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.23.0.txt \ No newline at end of file
+Documentation/RelNotes/2.24.0.txt \ No newline at end of file
diff --git a/apply.c b/apply.c
index cde9536..b9291f5 100644
--- a/apply.c
+++ b/apply.c
@@ -32,7 +32,7 @@ static void git_apply_config(void)
{
git_config_get_string_const("apply.whitespace", &apply_default_whitespace);
git_config_get_string_const("apply.ignorewhitespace", &apply_default_ignorewhitespace);
- git_config(git_default_config, NULL);
+ git_config(git_xmerge_config, NULL);
}
static int parse_whitespace_option(struct apply_state *state, const char *option)
@@ -1361,11 +1361,32 @@ int parse_git_diff_header(struct strbuf *root,
if (check_header_line(*linenr, patch))
return -1;
if (res > 0)
- return offset;
+ goto done;
break;
}
}
+done:
+ if (!patch->old_name && !patch->new_name) {
+ if (!patch->def_name) {
+ error(Q_("git diff header lacks filename information when removing "
+ "%d leading pathname component (line %d)",
+ "git diff header lacks filename information when removing "
+ "%d leading pathname components (line %d)",
+ parse_hdr_state.p_value),
+ parse_hdr_state.p_value, *linenr);
+ return -128;
+ }
+ patch->old_name = xstrdup(patch->def_name);
+ patch->new_name = xstrdup(patch->def_name);
+ }
+ if ((!patch->new_name && !patch->is_delete) ||
+ (!patch->old_name && !patch->is_new)) {
+ error(_("git diff header lacks filename information "
+ "(line %d)"), *linenr);
+ return -128;
+ }
+ patch->is_toplevel_relative = 1;
return offset;
}
@@ -1546,26 +1567,6 @@ static int find_header(struct apply_state *state,
return -128;
if (git_hdr_len <= len)
continue;
- if (!patch->old_name && !patch->new_name) {
- if (!patch->def_name) {
- error(Q_("git diff header lacks filename information when removing "
- "%d leading pathname component (line %d)",
- "git diff header lacks filename information when removing "
- "%d leading pathname components (line %d)",
- state->p_value),
- state->p_value, state->linenr);
- return -128;
- }
- patch->old_name = xstrdup(patch->def_name);
- patch->new_name = xstrdup(patch->def_name);
- }
- if ((!patch->new_name && !patch->is_delete) ||
- (!patch->old_name && !patch->is_new)) {
- error(_("git diff header lacks filename information "
- "(line %d)"), state->linenr);
- return -128;
- }
- patch->is_toplevel_relative = 1;
*hdrsize = git_hdr_len;
return offset;
}
@@ -4643,6 +4644,7 @@ static int apply_patch(struct apply_state *state,
struct patch *list = NULL, **listp = &list;
int skipped_patch = 0;
int res = 0;
+ int flush_attributes = 0;
state->patch_input_file = filename;
if (read_patch_file(&buf, fd) < 0)
@@ -4670,6 +4672,14 @@ static int apply_patch(struct apply_state *state,
patch_stats(state, patch);
*listp = patch;
listp = &patch->next;
+
+ if ((patch->new_name &&
+ ends_with_path_components(patch->new_name,
+ GITATTRIBUTES_FILE)) ||
+ (patch->old_name &&
+ ends_with_path_components(patch->old_name,
+ GITATTRIBUTES_FILE)))
+ flush_attributes = 1;
}
else {
if (state->apply_verbosity > verbosity_normal)
@@ -4746,6 +4756,8 @@ static int apply_patch(struct apply_state *state,
if (state->summary && state->apply_verbosity > verbosity_silent)
summary_patch_list(list);
+ if (flush_attributes)
+ reset_parsed_attributes();
end:
free_patch_list(list);
strbuf_release(&buf);
diff --git a/apply.h b/apply.h
index a795193..da3d95f 100644
--- a/apply.h
+++ b/apply.h
@@ -1,6 +1,7 @@
#ifndef APPLY_H
#define APPLY_H
+#include "hash.h"
#include "lockfile.h"
#include "string-list.h"
diff --git a/archive-tar.c b/archive-tar.c
index 3e53aac..e16d3f7 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -142,19 +142,25 @@ static int stream_blocked(const struct object_id *oid)
* string and appends it to a struct strbuf.
*/
static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,
- const char *value, unsigned int valuelen)
+ const char *value, size_t valuelen)
{
- int len, tmp;
+ size_t orig_len = sb->len;
+ size_t len, tmp;
/* "%u %s=%s\n" */
len = 1 + 1 + strlen(keyword) + 1 + valuelen + 1;
- for (tmp = len; tmp > 9; tmp /= 10)
+ for (tmp = 1; len / 10 >= tmp; tmp *= 10)
len++;
strbuf_grow(sb, len);
- strbuf_addf(sb, "%u %s=", len, keyword);
+ strbuf_addf(sb, "%"PRIuMAX" %s=", (uintmax_t)len, keyword);
strbuf_add(sb, value, valuelen);
strbuf_addch(sb, '\n');
+
+ if (len != sb->len - orig_len)
+ BUG("pax extended header length miscalculated as %"PRIuMAX
+ ", should be %"PRIuMAX,
+ (uintmax_t)len, (uintmax_t)(sb->len - orig_len));
}
/*
diff --git a/attr.c b/attr.c
index 93dc16b..11f19b5 100644
--- a/attr.c
+++ b/attr.c
@@ -62,7 +62,7 @@ static struct attr_hashmap g_attr_hashmap;
/* The container for objects stored in "struct attr_hashmap" */
struct attr_hash_entry {
- struct hashmap_entry ent; /* must be the first member! */
+ struct hashmap_entry ent;
const char *key; /* the key; memory should be owned by value */
size_t keylen; /* length of the key */
void *value; /* the stored value */
@@ -70,12 +70,14 @@ struct attr_hash_entry {
/* attr_hashmap comparison function */
static int attr_hash_entry_cmp(const void *unused_cmp_data,
- const void *entry,
- const void *entry_or_key,
+ const struct hashmap_entry *eptr,
+ const struct hashmap_entry *entry_or_key,
const void *unused_keydata)
{
- const struct attr_hash_entry *a = entry;
- const struct attr_hash_entry *b = entry_or_key;
+ const struct attr_hash_entry *a, *b;
+
+ a = container_of(eptr, const struct attr_hash_entry, ent);
+ b = container_of(entry_or_key, const struct attr_hash_entry, ent);
return (a->keylen != b->keylen) || strncmp(a->key, b->key, a->keylen);
}
@@ -98,10 +100,10 @@ static void *attr_hashmap_get(struct attr_hashmap *map,
if (!map->map.tablesize)
attr_hashmap_init(map);
- hashmap_entry_init(&k, memhash(key, keylen));
+ hashmap_entry_init(&k.ent, memhash(key, keylen));
k.key = key;
k.keylen = keylen;
- e = hashmap_get(&map->map, &k, NULL);
+ e = hashmap_get_entry(&map->map, &k, ent, NULL);
return e ? e->value : NULL;
}
@@ -117,12 +119,12 @@ static void attr_hashmap_add(struct attr_hashmap *map,
attr_hashmap_init(map);
e = xmalloc(sizeof(struct attr_hash_entry));
- hashmap_entry_init(e, memhash(key, keylen));
+ hashmap_entry_init(&e->ent, memhash(key, keylen));
e->key = key;
e->keylen = keylen;
e->value = value;
- hashmap_add(&map->map, e);
+ hashmap_add(&map->map, &e->ent);
}
struct all_attrs_item {
@@ -161,12 +163,12 @@ static void all_attrs_init(struct attr_hashmap *map, struct attr_check *check)
if (size != check->all_attrs_nr) {
struct attr_hash_entry *e;
struct hashmap_iter iter;
- hashmap_iter_init(&map->map, &iter);
REALLOC_ARRAY(check->all_attrs, size);
check->all_attrs_nr = size;
- while ((e = hashmap_iter_next(&iter))) {
+ hashmap_for_each_entry(&map->map, &iter, e,
+ ent /* member name */) {
const struct git_attr *a = e->value;
check->all_attrs[a->attr_nr].attr = a;
}
@@ -259,7 +261,7 @@ struct pattern {
const char *pattern;
int patternlen;
int nowildcardlen;
- unsigned flags; /* EXC_FLAG_* */
+ unsigned flags; /* PATTERN_FLAG_* */
};
/*
@@ -400,11 +402,11 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
char *p = (char *)&(res->state[num_attr]);
memcpy(p, name, namelen);
res->u.pat.pattern = p;
- parse_exclude_pattern(&res->u.pat.pattern,
+ parse_path_pattern(&res->u.pat.pattern,
&res->u.pat.patternlen,
&res->u.pat.flags,
&res->u.pat.nowildcardlen);
- if (res->u.pat.flags & EXC_FLAG_NEGATIVE) {
+ if (res->u.pat.flags & PATTERN_FLAG_NEGATIVE) {
warning(_("Negative patterns are ignored in git attributes\n"
"Use '\\!' for literal leading exclamation."));
goto fail_return;
@@ -991,10 +993,10 @@ static int path_matches(const char *pathname, int pathlen,
int prefix = pat->nowildcardlen;
int isdir = (pathlen && pathname[pathlen - 1] == '/');
- if ((pat->flags & EXC_FLAG_MUSTBEDIR) && !isdir)
+ if ((pat->flags & PATTERN_FLAG_MUSTBEDIR) && !isdir)
return 0;
- if (pat->flags & EXC_FLAG_NODIR) {
+ if (pat->flags & PATTERN_FLAG_NODIR) {
return match_basename(pathname + basename_offset,
pathlen - basename_offset - isdir,
pattern, prefix,
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index c329b72..af2a5ea 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -1,6 +1,5 @@
-resources:
-- repo: self
- fetchDepth: 1
+variables:
+ Agent.Source.Git.ShallowFetchDepth: 1
jobs:
- job: windows_build
@@ -131,6 +130,165 @@ jobs:
PathtoPublish: t/failed-test-artifacts
ArtifactName: failed-test-artifacts
+- job: vs_build
+ displayName: Visual Studio Build
+ condition: succeeded()
+ pool: Hosted VS2017
+ timeoutInMinutes: 240
+ steps:
+ - powershell: |
+ if ("$GITFILESHAREPWD" -ne "" -and "$GITFILESHAREPWD" -ne "`$`(gitfileshare.pwd)") {
+ net use s: \\gitfileshare.file.core.windows.net\test-cache "$GITFILESHAREPWD" /user:AZURE\gitfileshare /persistent:no
+ cmd /c mklink /d "$(Build.SourcesDirectory)\test-cache" S:\
+ }
+ displayName: 'Mount test-cache'
+ env:
+ GITFILESHAREPWD: $(gitfileshare.pwd)
+ - powershell: |
+ $urlbase = "https://dev.azure.com/git-for-windows/git/_apis/build/builds"
+ $id = ((Invoke-WebRequest -UseBasicParsing "${urlbase}?definitions=22&statusFilter=completed&resultFilter=succeeded&`$top=1").content | ConvertFrom-JSON).value[0].id
+ $downloadUrl = ((Invoke-WebRequest -UseBasicParsing "${urlbase}/$id/artifacts").content | ConvertFrom-JSON).value[1].resource.downloadUrl
+ (New-Object Net.WebClient).DownloadFile($downloadUrl,"git-sdk-64-minimal.zip")
+ Expand-Archive git-sdk-64-minimal.zip -DestinationPath . -Force
+ Remove-Item git-sdk-64-minimal.zip
+
+ # Let Git ignore the SDK and the test-cache
+ "/git-sdk-64-minimal/`n/test-cache/`n" | Out-File -NoNewLine -Encoding ascii -Append "$(Build.SourcesDirectory)\.git\info\exclude"
+ displayName: 'Download git-sdk-64-minimal'
+ - powershell: |
+ & git-sdk-64-minimal\usr\bin\bash.exe -lc @"
+ make NDEBUG=1 DEVELOPER=1 vcxproj
+ "@
+ if (!$?) { exit(1) }
+ displayName: Generate Visual Studio Solution
+ env:
+ HOME: $(Build.SourcesDirectory)
+ MSYSTEM: MINGW64
+ DEVELOPER: 1
+ NO_PERL: 1
+ GIT_CONFIG_PARAMETERS: "'user.name=CI' 'user.email=ci@git'"
+ - powershell: |
+ $urlbase = "https://dev.azure.com/git/git/_apis/build/builds"
+ $id = ((Invoke-WebRequest -UseBasicParsing "${urlbase}?definitions=9&statusFilter=completed&resultFilter=succeeded&`$top=1").content | ConvertFrom-JSON).value[0].id
+ $downloadUrl = ((Invoke-WebRequest -UseBasicParsing "${urlbase}/$id/artifacts").content | ConvertFrom-JSON).value[0].resource.downloadUrl
+ (New-Object Net.WebClient).DownloadFile($downloadUrl, "compat.zip")
+ Expand-Archive compat.zip -DestinationPath . -Force
+ Remove-Item compat.zip
+ displayName: 'Download vcpkg artifacts'
+ - task: MSBuild@1
+ inputs:
+ solution: git.sln
+ platform: x64
+ configuration: Release
+ maximumCpuCount: 4
+ - powershell: |
+ & compat\vcbuild\vcpkg_copy_dlls.bat release
+ if (!$?) { exit(1) }
+ & git-sdk-64-minimal\usr\bin\bash.exe -lc @"
+ mkdir -p artifacts &&
+ eval \"`$(make -n artifacts-tar INCLUDE_DLLS_IN_ARTIFACTS=YesPlease ARTIFACTS_DIRECTORY=artifacts | grep ^tar)\"
+ "@
+ if (!$?) { exit(1) }
+ displayName: Bundle artifact tar
+ env:
+ HOME: $(Build.SourcesDirectory)
+ MSYSTEM: MINGW64
+ DEVELOPER: 1
+ NO_PERL: 1
+ MSVC: 1
+ VCPKG_ROOT: $(Build.SourcesDirectory)\compat\vcbuild\vcpkg
+ - powershell: |
+ $tag = (Invoke-WebRequest -UseBasicParsing "https://gitforwindows.org/latest-tag.txt").content
+ $version = (Invoke-WebRequest -UseBasicParsing "https://gitforwindows.org/latest-version.txt").content
+ $url = "https://github.com/git-for-windows/git/releases/download/${tag}/PortableGit-${version}-64-bit.7z.exe"
+ (New-Object Net.WebClient).DownloadFile($url,"PortableGit.exe")
+ & .\PortableGit.exe -y -oartifacts\PortableGit
+ # Wait until it is unpacked
+ while (-not @(Remove-Item -ErrorAction SilentlyContinue PortableGit.exe; $?)) { sleep 1 }
+ displayName: Download & extract portable Git
+ - task: PublishPipelineArtifact@0
+ displayName: 'Publish Pipeline Artifact: MSVC test artifacts'
+ inputs:
+ artifactName: 'vs-artifacts'
+ targetPath: '$(Build.SourcesDirectory)\artifacts'
+ - powershell: |
+ if ("$GITFILESHAREPWD" -ne "" -and "$GITFILESHAREPWD" -ne "`$`(gitfileshare.pwd)") {
+ cmd /c rmdir "$(Build.SourcesDirectory)\test-cache"
+ }
+ displayName: 'Unmount test-cache'
+ condition: true
+ env:
+ GITFILESHAREPWD: $(gitfileshare.pwd)
+
+- job: vs_test
+ displayName: Visual Studio Test
+ dependsOn: vs_build
+ condition: succeeded()
+ pool: Hosted
+ timeoutInMinutes: 240
+ strategy:
+ parallel: 10
+ steps:
+ - powershell: |
+ if ("$GITFILESHAREPWD" -ne "" -and "$GITFILESHAREPWD" -ne "`$`(gitfileshare.pwd)") {
+ net use s: \\gitfileshare.file.core.windows.net\test-cache "$GITFILESHAREPWD" /user:AZURE\gitfileshare /persistent:no
+ cmd /c mklink /d "$(Build.SourcesDirectory)\test-cache" S:\
+ }
+ displayName: 'Mount test-cache'
+ env:
+ GITFILESHAREPWD: $(gitfileshare.pwd)
+ - task: DownloadPipelineArtifact@0
+ displayName: 'Download Pipeline Artifact: VS test artifacts'
+ inputs:
+ artifactName: 'vs-artifacts'
+ targetPath: '$(Build.SourcesDirectory)'
+ - powershell: |
+ & PortableGit\git-cmd.exe --command=usr\bin\bash.exe -lc @"
+ test -f artifacts.tar.gz || {
+ echo No test artifacts found\; skipping >&2
+ exit 0
+ }
+ tar xf artifacts.tar.gz || exit 1
+
+ # Let Git ignore the SDK and the test-cache
+ printf '%s\n' /PortableGit/ /test-cache/ >>.git/info/exclude
+
+ cd t &&
+ PATH=\"`$PWD/helper:`$PATH\" &&
+ test-tool.exe run-command testsuite --jobs=10 -V -x --write-junit-xml \
+ `$(test-tool.exe path-utils slice-tests \
+ `$SYSTEM_JOBPOSITIONINPHASE `$SYSTEM_TOTALJOBSINPHASE t[0-9]*.sh)
+ "@
+ if (!$?) { exit(1) }
+ displayName: 'Test (parallel)'
+ env:
+ HOME: $(Build.SourcesDirectory)
+ MSYSTEM: MINGW64
+ NO_SVN_TESTS: 1
+ GIT_TEST_SKIP_REBASE_P: 1
+ - powershell: |
+ if ("$GITFILESHAREPWD" -ne "" -and "$GITFILESHAREPWD" -ne "`$`(gitfileshare.pwd)") {
+ cmd /c rmdir "$(Build.SourcesDirectory)\test-cache"
+ }
+ displayName: 'Unmount test-cache'
+ condition: true
+ env:
+ GITFILESHAREPWD: $(gitfileshare.pwd)
+ - task: PublishTestResults@2
+ displayName: 'Publish Test Results **/TEST-*.xml'
+ inputs:
+ mergeTestResults: true
+ testRunTitle: 'vs'
+ platform: Windows
+ publishRunAttachments: false
+ condition: succeededOrFailed()
+ - task: PublishBuildArtifacts@1
+ displayName: 'Publish trash directories of failed tests'
+ condition: failed()
+ inputs:
+ PathtoPublish: t/failed-test-artifacts
+ ArtifactName: failed-vs-test-artifacts
+
- job: linux_clang
displayName: linux-clang
condition: succeeded()
@@ -354,7 +512,7 @@ jobs:
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
sudo apt-get update &&
- sudo apt-get install -y coccinelle &&
+ sudo apt-get install -y coccinelle libcurl4-openssl-dev libssl-dev libexpat-dev gettext &&
export jobname=StaticAnalysis &&
@@ -374,7 +532,7 @@ jobs:
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
sudo apt-get update &&
- sudo apt-get install -y asciidoc xmlto asciidoctor &&
+ sudo apt-get install -y asciidoc xmlto asciidoctor docbook-xsl-ns &&
export ALREADY_HAVE_ASCIIDOCTOR=yes. &&
export jobname=Documentation &&
diff --git a/banned.h b/banned.h
index 447af24..60a18d4 100644
--- a/banned.h
+++ b/banned.h
@@ -26,7 +26,7 @@
#define vsprintf(...) BANNED(vsprintf)
#else
#define sprintf(buf,fmt,arg) BANNED(sprintf)
-#define vsprintf(buf,fmt,arg) BANNED(sprintf)
+#define vsprintf(buf,fmt,arg) BANNED(vsprintf)
#endif
#endif /* BANNED_H */
diff --git a/bisect.c b/bisect.c
index e87ac29..e81c91d 100644
--- a/bisect.c
+++ b/bisect.c
@@ -707,7 +707,7 @@ static int bisect_checkout(const struct object_id *bisect_rev, int no_checkout)
{
char bisect_rev_hex[GIT_MAX_HEXSZ + 1];
- memcpy(bisect_rev_hex, oid_to_hex(bisect_rev), GIT_SHA1_HEXSZ + 1);
+ memcpy(bisect_rev_hex, oid_to_hex(bisect_rev), the_hash_algo->hexsz + 1);
update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
argv_checkout[2] = bisect_rev_hex;
diff --git a/blame.c b/blame.c
index 36a2e7e..29770e5 100644
--- a/blame.c
+++ b/blame.c
@@ -144,7 +144,7 @@ static void append_merge_parents(struct repository *r,
while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) {
struct object_id oid;
- if (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid))
+ if (get_oid_hex(line.buf, &oid))
die("unknown line in '%s': %s",
git_path_merge_head(r), line.buf);
tail = append_parent(r, tail, &oid);
@@ -417,14 +417,15 @@ static void get_fingerprint(struct fingerprint *result,
/* Ignore whitespace pairs */
if (hash == 0)
continue;
- hashmap_entry_init(entry, hash);
+ hashmap_entry_init(&entry->entry, hash);
- found_entry = hashmap_get(&result->map, entry, NULL);
+ found_entry = hashmap_get_entry(&result->map, entry,
+ /* member name */ entry, NULL);
if (found_entry) {
found_entry->count += 1;
} else {
entry->count = 1;
- hashmap_add(&result->map, entry);
+ hashmap_add(&result->map, &entry->entry);
++entry;
}
}
@@ -432,7 +433,7 @@ static void get_fingerprint(struct fingerprint *result,
static void free_fingerprint(struct fingerprint *f)
{
- hashmap_free(&f->map, 0);
+ hashmap_free(&f->map);
free(f->entries);
}
@@ -449,10 +450,10 @@ static int fingerprint_similarity(struct fingerprint *a, struct fingerprint *b)
struct hashmap_iter iter;
const struct fingerprint_entry *entry_a, *entry_b;
- hashmap_iter_init(&b->map, &iter);
-
- while ((entry_b = hashmap_iter_next(&iter))) {
- if ((entry_a = hashmap_get(&a->map, entry_b, NULL))) {
+ hashmap_for_each_entry(&b->map, &iter, entry_b,
+ entry /* member name */) {
+ entry_a = hashmap_get_entry(&a->map, entry_b, entry, NULL);
+ if (entry_a) {
intersection += entry_a->count < entry_b->count ?
entry_a->count : entry_b->count;
}
@@ -470,10 +471,12 @@ static void fingerprint_subtract(struct fingerprint *a, struct fingerprint *b)
hashmap_iter_init(&b->map, &iter);
- while ((entry_b = hashmap_iter_next(&iter))) {
- if ((entry_a = hashmap_get(&a->map, entry_b, NULL))) {
+ hashmap_for_each_entry(&b->map, &iter, entry_b,
+ entry /* member name */) {
+ entry_a = hashmap_get_entry(&a->map, entry_b, entry, NULL);
+ if (entry_a) {
if (entry_a->count <= entry_b->count)
- hashmap_remove(&a->map, entry_b, NULL);
+ hashmap_remove(&a->map, &entry_b->entry, NULL);
else
entry_a->count -= entry_b->count;
}
diff --git a/builtin/am.c b/builtin/am.c
index 1aea657..8181c2a 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -24,7 +24,6 @@
#include "sequencer.h"
#include "revision.h"
#include "merge-recursive.h"
-#include "revision.h"
#include "log-tree.h"
#include "notes-utils.h"
#include "rerere.h"
@@ -1072,19 +1071,6 @@ static const char *msgnum(const struct am_state *state)
}
/**
- * Refresh and write index.
- */
-static void refresh_and_write_cache(void)
-{
- struct lock_file lock_file = LOCK_INIT;
-
- hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
- refresh_cache(REFRESH_QUIET);
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
- die(_("unable to write index file"));
-}
-
-/**
* Dies with a user-friendly message on how to proceed after resolving the
* problem. This message can be overridden with state->resolvemsg.
*/
@@ -1272,7 +1258,9 @@ static void get_commit_info(struct am_state *state, struct commit *commit)
buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding());
ident_line = find_commit_header(buffer, "author", &ident_len);
-
+ if (!ident_line)
+ die(_("missing author line in commit %s"),
+ oid_to_hex(&commit->object.oid));
if (split_ident_line(&id, ident_line, ident_len) < 0)
die(_("invalid ident line: %.*s"), (int)ident_len, ident_line);
@@ -1538,7 +1526,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
o.branch1 = "HEAD";
their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
o.branch2 = their_tree_name;
- o.detect_directory_renames = 0;
+ o.detect_directory_renames = MERGE_DIRECTORY_RENAMES_NONE;
if (state->quiet)
o.verbosity = 0;
@@ -1703,7 +1691,8 @@ static void am_run(struct am_state *state, int resume)
unlink(am_path(state, "dirtyindex"));
- refresh_and_write_cache();
+ if (refresh_and_write_cache(REFRESH_QUIET, 0, 0) < 0)
+ die(_("unable to write index file"));
if (repo_index_has_changes(the_repository, NULL, &sb)) {
write_state_bool(state, "dirtyindex", 1);
diff --git a/builtin/blame.c b/builtin/blame.c
index b6534d4..10185cc 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -26,7 +26,6 @@
#include "progress.h"
#include "object-store.h"
#include "blame.h"
-#include "string-list.h"
#include "refs.h"
static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
@@ -320,18 +319,18 @@ static const char *format_time(timestamp_t time, const char *tz_str,
return time_buf.buf;
}
-#define OUTPUT_ANNOTATE_COMPAT 001
-#define OUTPUT_LONG_OBJECT_NAME 002
-#define OUTPUT_RAW_TIMESTAMP 004
-#define OUTPUT_PORCELAIN 010
-#define OUTPUT_SHOW_NAME 020
-#define OUTPUT_SHOW_NUMBER 040
-#define OUTPUT_SHOW_SCORE 0100
-#define OUTPUT_NO_AUTHOR 0200
-#define OUTPUT_SHOW_EMAIL 0400
-#define OUTPUT_LINE_PORCELAIN 01000
-#define OUTPUT_COLOR_LINE 02000
-#define OUTPUT_SHOW_AGE_WITH_COLOR 04000
+#define OUTPUT_ANNOTATE_COMPAT (1U<<0)
+#define OUTPUT_LONG_OBJECT_NAME (1U<<1)
+#define OUTPUT_RAW_TIMESTAMP (1U<<2)
+#define OUTPUT_PORCELAIN (1U<<3)
+#define OUTPUT_SHOW_NAME (1U<<4)
+#define OUTPUT_SHOW_NUMBER (1U<<5)
+#define OUTPUT_SHOW_SCORE (1U<<6)
+#define OUTPUT_NO_AUTHOR (1U<<7)
+#define OUTPUT_SHOW_EMAIL (1U<<8)
+#define OUTPUT_LINE_PORCELAIN (1U<<9)
+#define OUTPUT_COLOR_LINE (1U<<10)
+#define OUTPUT_SHOW_AGE_WITH_COLOR (1U<<11)
static void emit_porcelain_details(struct blame_origin *suspect, int repeat)
{
@@ -460,7 +459,7 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int
for (cnt = 0; cnt < ent->num_lines; cnt++) {
char ch;
- int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev;
+ int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? the_hash_algo->hexsz : abbrev;
if (opt & OUTPUT_COLOR_LINE) {
if (cnt > 0) {
@@ -885,6 +884,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
struct range_set ranges;
unsigned int range_i;
long anchor;
+ const int hexsz = the_hash_algo->hexsz;
setup_default_color_by_age();
git_config(git_blame_config, &output_option);
@@ -931,11 +931,11 @@ parse_done:
} else if (show_progress < 0)
show_progress = isatty(2);
- if (0 < abbrev && abbrev < GIT_SHA1_HEXSZ)
+ if (0 < abbrev && abbrev < hexsz)
/* one more abbrev length is needed for the boundary commit */
abbrev++;
else if (!abbrev)
- abbrev = GIT_SHA1_HEXSZ;
+ abbrev = hexsz;
if (revs_file && read_ancestry(revs_file))
die_errno("reading graft file '%s' failed", revs_file);
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 995d47c..d6a1aa7 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -15,6 +15,7 @@
#include "sha1-array.h"
#include "packfile.h"
#include "object-store.h"
+#include "promisor-remote.h"
struct batch_options {
int enabled;
@@ -524,8 +525,8 @@ static int batch_objects(struct batch_options *opt)
if (opt->all_objects) {
struct object_cb_data cb;
- if (repository_format_partial_clone)
- warning("This repository has extensions.partialClone set. Some objects may not be loaded.");
+ if (has_promisor_remote())
+ warning("This repository uses promisor remotes. Some objects may not be loaded.");
cb.opt = opt;
cb.expand = &data;
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 5990973..5a4f923 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -32,19 +32,19 @@ static const struct option check_ignore_options[] = {
OPT_END()
};
-static void output_exclude(const char *path, struct exclude *exclude)
+static void output_pattern(const char *path, struct path_pattern *pattern)
{
- char *bang = (exclude && exclude->flags & EXC_FLAG_NEGATIVE) ? "!" : "";
- char *slash = (exclude && exclude->flags & EXC_FLAG_MUSTBEDIR) ? "/" : "";
+ char *bang = (pattern && pattern->flags & PATTERN_FLAG_NEGATIVE) ? "!" : "";
+ char *slash = (pattern && pattern->flags & PATTERN_FLAG_MUSTBEDIR) ? "/" : "";
if (!nul_term_line) {
if (!verbose) {
write_name_quoted(path, stdout, '\n');
} else {
- if (exclude) {
- quote_c_style(exclude->el->src, NULL, stdout, 0);
+ if (pattern) {
+ quote_c_style(pattern->pl->src, NULL, stdout, 0);
printf(":%d:%s%s%s\t",
- exclude->srcpos,
- bang, exclude->pattern, slash);
+ pattern->srcpos,
+ bang, pattern->pattern, slash);
}
else {
printf("::\t");
@@ -56,11 +56,11 @@ static void output_exclude(const char *path, struct exclude *exclude)
if (!verbose) {
printf("%s%c", path, '\0');
} else {
- if (exclude)
+ if (pattern)
printf("%s%c%d%c%s%s%s%c%s%c",
- exclude->el->src, '\0',
- exclude->srcpos, '\0',
- bang, exclude->pattern, slash, '\0',
+ pattern->pl->src, '\0',
+ pattern->srcpos, '\0',
+ bang, pattern->pattern, slash, '\0',
path, '\0');
else
printf("%c%c%c%s%c", '\0', '\0', '\0', path, '\0');
@@ -74,7 +74,7 @@ static int check_ignore(struct dir_struct *dir,
const char *full_path;
char *seen;
int num_ignored = 0, i;
- struct exclude *exclude;
+ struct path_pattern *pattern;
struct pathspec pathspec;
if (!argc) {
@@ -103,15 +103,15 @@ static int check_ignore(struct dir_struct *dir,
seen = find_pathspecs_matching_against_index(&pathspec, &the_index);
for (i = 0; i < pathspec.nr; i++) {
full_path = pathspec.items[i].match;
- exclude = NULL;
+ pattern = NULL;
if (!seen[i]) {
int dtype = DT_UNKNOWN;
- exclude = last_exclude_matching(dir, &the_index,
+ pattern = last_matching_pattern(dir, &the_index,
full_path, &dtype);
}
- if (!quiet && (exclude || show_non_matching))
- output_exclude(pathspec.items[i].original, exclude);
- if (exclude)
+ if (!quiet && (pattern || show_non_matching))
+ output_pattern(pathspec.items[i].original, pattern);
+ if (pattern)
num_ignored++;
}
free(seen);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 6123f73..3634a3d 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -126,6 +126,7 @@ static int update_some(const struct object_id *oid, struct strbuf *base,
if (pos >= 0) {
struct cache_entry *old = active_cache[pos];
if (ce->ce_mode == old->ce_mode &&
+ !ce_intent_to_add(old) &&
oideq(&ce->oid, &old->oid)) {
old->ce_flags |= CE_UPDATE;
discard_cache_entry(ce);
@@ -708,11 +709,11 @@ static int merge_working_tree(const struct checkout_opts *opts,
* give up or do a real merge, depending on
* whether the merge flag was used.
*/
- struct tree *result;
struct tree *work;
struct tree *old_tree;
struct merge_options o;
struct strbuf sb = STRBUF_INIT;
+ struct strbuf old_commit_shortname = STRBUF_INIT;
if (!opts->merge)
return 1;
@@ -730,13 +731,6 @@ static int merge_working_tree(const struct checkout_opts *opts,
"the following files:\n%s"), sb.buf);
strbuf_release(&sb);
- if (repo_index_has_changes(the_repository,
- get_commit_tree(old_branch_info->commit),
- &sb))
- warning(_("staged changes in the following files may be lost: %s"),
- sb.buf);
- strbuf_release(&sb);
-
/* Do more real merge */
/*
@@ -760,7 +754,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
*/
init_merge_options(&o, the_repository);
o.verbosity = 0;
- work = write_tree_from_memory(&o);
+ work = write_in_core_index_as_tree(the_repository);
ret = reset_tree(new_tree,
opts, 1,
@@ -768,19 +762,25 @@ static int merge_working_tree(const struct checkout_opts *opts,
if (ret)
return ret;
o.ancestor = old_branch_info->name;
+ if (old_branch_info->name == NULL) {
+ strbuf_add_unique_abbrev(&old_commit_shortname,
+ &old_branch_info->commit->object.oid,
+ DEFAULT_ABBREV);
+ o.ancestor = old_commit_shortname.buf;
+ }
o.branch1 = new_branch_info->name;
o.branch2 = "local";
ret = merge_trees(&o,
new_tree,
work,
- old_tree,
- &result);
+ old_tree);
if (ret < 0)
exit(128);
ret = reset_tree(new_tree,
opts, 0,
writeout_error);
strbuf_release(&o.obuf);
+ strbuf_release(&old_commit_shortname);
if (ret)
return ret;
}
@@ -1714,6 +1714,15 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
opts.checkout_index = -2; /* default on */
opts.checkout_worktree = -2; /* default on */
+ if (argc == 3 && !strcmp(argv[1], "-b")) {
+ /*
+ * User ran 'git checkout -b <branch>' and expects
+ * the same behavior as 'git switch -c <branch>'.
+ */
+ opts.switch_branch_doing_nothing_is_ok = 0;
+ opts.only_merge_on_switching_branches = 1;
+ }
+
options = parse_options_dup(checkout_options);
options = add_common_options(&opts, options);
options = add_common_switch_branch_options(&opts, options);
diff --git a/builtin/clean.c b/builtin/clean.c
index d5579da..5abf087 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -158,7 +158,8 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
*dir_gone = 1;
- if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) && is_nonbare_repository_dir(path)) {
+ if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
+ is_nonbare_repository_dir(path)) {
if (!quiet) {
quote_path_relative(path->buf, prefix, &quoted);
printf(dry_run ? _(msg_would_skip_git_dir) : _(msg_skip_git_dir),
@@ -648,7 +649,7 @@ static int filter_by_patterns_cmd(void)
struct strbuf confirm = STRBUF_INIT;
struct strbuf **ignore_list;
struct string_list_item *item;
- struct exclude_list *el;
+ struct pattern_list *pl;
int changed = -1, i;
for (;;) {
@@ -671,7 +672,7 @@ static int filter_by_patterns_cmd(void)
break;
memset(&dir, 0, sizeof(dir));
- el = add_exclude_list(&dir, EXC_CMDL, "manual exclude");
+ pl = add_pattern_list(&dir, EXC_CMDL, "manual exclude");
ignore_list = strbuf_split_max(&confirm, ' ', 0);
for (i = 0; ignore_list[i]; i++) {
@@ -679,7 +680,7 @@ static int filter_by_patterns_cmd(void)
if (!ignore_list[i]->len)
continue;
- add_exclude(ignore_list[i]->buf, "", 0, el, -(i+1));
+ add_pattern(ignore_list[i]->buf, "", 0, pl, -(i+1));
}
changed = 0;
@@ -901,7 +902,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
struct pathspec pathspec;
struct strbuf buf = STRBUF_INIT;
struct string_list exclude_list = STRING_LIST_INIT_NODUP;
- struct exclude_list *el;
+ struct pattern_list *pl;
struct string_list_item *item;
const char *qname;
struct option options[] = {
@@ -946,9 +947,19 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
if (force > 1)
rm_flags = 0;
+ else
+ dir.flags |= DIR_SKIP_NESTED_GIT;
dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
+ if (argc) {
+ /*
+ * Remaining args implies pathspecs specified, and we should
+ * recurse within those.
+ */
+ remove_directories = 1;
+ }
+
if (remove_directories)
dir.flags |= DIR_SHOW_IGNORED_TOO | DIR_KEEP_UNTRACKED_CONTENTS;
@@ -958,9 +969,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
if (!ignored)
setup_standard_excludes(&dir);
- el = add_exclude_list(&dir, EXC_CMDL, "--exclude option");
+ pl = add_pattern_list(&dir, EXC_CMDL, "--exclude option");
for (i = 0; i < exclude_list.nr; i++)
- add_exclude(exclude_list.items[i].string, "", 0, el, -(i+1));
+ add_pattern(exclude_list.items[i].string, "", 0, pl, -(i+1));
parse_pathspec(&pathspec, 0,
PATHSPEC_PREFER_CWD,
@@ -1007,6 +1018,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
for_each_string_list_item(item, &del_list) {
struct stat st;
+ strbuf_reset(&abs_path);
if (prefix)
strbuf_addstr(&abs_path, prefix);
@@ -1040,7 +1052,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
}
}
- strbuf_reset(&abs_path);
}
strbuf_release(&abs_path);
diff --git a/builtin/clone.c b/builtin/clone.c
index f665b28..c46ee29 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -32,7 +32,6 @@
#include "connected.h"
#include "packfile.h"
#include "list-objects-filter-options.h"
-#include "object-store.h"
/*
* Overall FIXMEs:
@@ -785,7 +784,7 @@ static int checkout(int submodule_progress)
if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
die(_("unable to write new index file"));
- err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
+ err |= run_hook_le(NULL, "post-checkout", oid_to_hex(&null_oid),
oid_to_hex(&oid), "1", NULL);
if (!err && (option_recurse_submodules.nr > 0)) {
@@ -1160,13 +1159,11 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
transport->server_options = &server_options;
if (filter_options.choice) {
- struct strbuf expanded_filter_spec = STRBUF_INIT;
- expand_list_objects_filter_spec(&filter_options,
- &expanded_filter_spec);
+ const char *spec =
+ expand_list_objects_filter_spec(&filter_options);
transport_set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
- expanded_filter_spec.buf);
+ spec);
transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
- strbuf_release(&expanded_filter_spec);
}
if (transport->smart_options && !deepen && !filter_options.choice)
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index 38027b8..ec0fc93 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -8,15 +8,14 @@
#include "object-store.h"
static char const * const builtin_commit_graph_usage[] = {
- N_("git commit-graph [--object-dir <objdir>]"),
N_("git commit-graph read [--object-dir <objdir>]"),
- N_("git commit-graph verify [--object-dir <objdir>] [--shallow]"),
- N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits] <split options>"),
+ N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"),
+ N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits] [--[no-]progress] <split options>"),
NULL
};
static const char * const builtin_commit_graph_verify_usage[] = {
- N_("git commit-graph verify [--object-dir <objdir>] [--shallow]"),
+ N_("git commit-graph verify [--object-dir <objdir>] [--shallow] [--[no-]progress]"),
NULL
};
@@ -26,7 +25,7 @@ static const char * const builtin_commit_graph_read_usage[] = {
};
static const char * const builtin_commit_graph_write_usage[] = {
- N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits] <split options>"),
+ N_("git commit-graph write [--object-dir <objdir>] [--append|--split] [--reachable|--stdin-packs|--stdin-commits] [--[no-]progress] <split options>"),
NULL
};
@@ -38,6 +37,7 @@ static struct opts_commit_graph {
int append;
int split;
int shallow;
+ int progress;
} opts;
static int graph_verify(int argc, const char **argv)
@@ -55,9 +55,13 @@ static int graph_verify(int argc, const char **argv)
N_("The object directory to store the graph")),
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(),
};
+ trace2_cmd_mode("verify");
+
+ opts.progress = isatty(2);
argc = parse_options(argc, argv, NULL,
builtin_commit_graph_verify_options,
builtin_commit_graph_verify_usage, 0);
@@ -66,6 +70,8 @@ static int graph_verify(int argc, const char **argv)
opts.obj_dir = get_object_directory();
if (opts.shallow)
flags |= COMMIT_GRAPH_VERIFY_SHALLOW;
+ if (opts.progress)
+ flags |= COMMIT_GRAPH_WRITE_PROGRESS;
graph_name = get_commit_graph_filename(opts.obj_dir);
open_ok = open_commit_graph(graph_name, &fd, &st);
@@ -102,6 +108,8 @@ static int graph_read(int argc, const char **argv)
OPT_END(),
};
+ trace2_cmd_mode("read");
+
argc = parse_options(argc, argv, NULL,
builtin_commit_graph_read_options,
builtin_commit_graph_read_usage, 0);
@@ -154,7 +162,7 @@ static int graph_write(int argc, const char **argv)
struct string_list *commit_hex = NULL;
struct string_list lines;
int result = 0;
- unsigned int flags = COMMIT_GRAPH_PROGRESS;
+ enum commit_graph_write_flags flags = 0;
static struct option builtin_commit_graph_write_options[] = {
OPT_STRING(0, "object-dir", &opts.obj_dir,
@@ -168,6 +176,7 @@ static int graph_write(int argc, const char **argv)
N_("start walk at commits listed by stdin")),
OPT_BOOL(0, "append", &opts.append,
N_("include all commits already in the commit-graph file")),
+ OPT_BOOL(0, "progress", &opts.progress, N_("force progress reporting")),
OPT_BOOL(0, "split", &opts.split,
N_("allow writing an incremental commit-graph file")),
OPT_INTEGER(0, "max-commits", &split_opts.max_commits,
@@ -179,10 +188,13 @@ static int graph_write(int argc, const char **argv)
OPT_END(),
};
+ opts.progress = isatty(2);
split_opts.size_multiple = 2;
split_opts.max_commits = 0;
split_opts.expire_time = 0;
+ trace2_cmd_mode("write");
+
argc = parse_options(argc, argv, NULL,
builtin_commit_graph_write_options,
builtin_commit_graph_write_usage, 0);
@@ -192,9 +204,11 @@ static int graph_write(int argc, const char **argv)
if (!opts.obj_dir)
opts.obj_dir = get_object_directory();
if (opts.append)
- flags |= COMMIT_GRAPH_APPEND;
+ flags |= COMMIT_GRAPH_WRITE_APPEND;
if (opts.split)
- flags |= COMMIT_GRAPH_SPLIT;
+ flags |= COMMIT_GRAPH_WRITE_SPLIT;
+ if (opts.progress)
+ flags |= COMMIT_GRAPH_WRITE_PROGRESS;
read_replace_refs = 0;
@@ -213,8 +227,10 @@ static int graph_write(int argc, const char **argv)
if (opts.stdin_packs)
pack_indexes = &lines;
- if (opts.stdin_commits)
+ if (opts.stdin_commits) {
commit_hex = &lines;
+ flags |= COMMIT_GRAPH_WRITE_CHECK_OIDS;
+ }
UNLEAK(buf);
}
@@ -249,6 +265,8 @@ int cmd_commit_graph(int argc, const char **argv, const char *prefix)
builtin_commit_graph_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
+ save_commit_buffer = 0;
+
if (argc > 0) {
if (!strcmp(argv[0], "read"))
return graph_read(argc, argv);
diff --git a/builtin/commit.c b/builtin/commit.c
index ae7aaf6..294dc57 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -510,7 +510,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
s->nowarn = nowarn;
s->is_initial = get_oid(s->reference, &oid) ? 1 : 0;
if (!s->is_initial)
- hashcpy(s->sha1_commit, oid.hash);
+ oidcpy(&s->oid_commit, &oid);
s->status_format = status_format;
s->ignore_submodule_arg = ignore_submodule_arg;
@@ -1406,7 +1406,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
s.is_initial = get_oid(s.reference, &oid) ? 1 : 0;
if (!s.is_initial)
- hashcpy(s.sha1_commit, oid.hash);
+ oidcpy(&s.oid_commit, &oid);
s.ignore_submodule_arg = ignore_submodule_arg;
s.status_format = status_format;
@@ -1463,28 +1463,6 @@ static int git_commit_config(const char *k, const char *v, void *cb)
return git_status_config(k, v, s);
}
-int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...)
-{
- struct argv_array hook_env = ARGV_ARRAY_INIT;
- va_list args;
- int ret;
-
- argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s", index_file);
-
- /*
- * Let the hook know that no editor will be launched.
- */
- if (!editor_is_used)
- argv_array_push(&hook_env, "GIT_EDITOR=:");
-
- va_start(args, name);
- ret = run_hook_ve(hook_env.argv,name, args);
- va_end(args);
- argv_array_clear(&hook_env);
-
- return ret;
-}
-
int cmd_commit(int argc, const char **argv, const char *prefix)
{
const char *argv_gc_auto[] = {"gc", "--auto", NULL};
diff --git a/builtin/describe.c b/builtin/describe.c
index 2001542..b6df81d 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -15,7 +15,6 @@
#include "argv-array.h"
#include "run-command.h"
#include "object-store.h"
-#include "revision.h"
#include "list-objects.h"
#include "commit-slab.h"
@@ -64,19 +63,22 @@ static const char *prio_names[] = {
};
static int commit_name_neq(const void *unused_cmp_data,
- const void *entry,
- const void *entry_or_key,
+ const struct hashmap_entry *eptr,
+ const struct hashmap_entry *entry_or_key,
const void *peeled)
{
- const struct commit_name *cn1 = entry;
- const struct commit_name *cn2 = entry_or_key;
+ const struct commit_name *cn1, *cn2;
+
+ cn1 = container_of(eptr, const struct commit_name, entry);
+ cn2 = container_of(entry_or_key, const struct commit_name, entry);
return !oideq(&cn1->peeled, peeled ? peeled : &cn2->peeled);
}
static inline struct commit_name *find_commit_name(const struct object_id *peeled)
{
- return hashmap_get_from_hash(&names, oidhash(peeled), peeled);
+ return hashmap_get_entry_from_hash(&names, oidhash(peeled), peeled,
+ struct commit_name, entry);
}
static int replace_name(struct commit_name *e,
@@ -123,8 +125,8 @@ static void add_to_known_names(const char *path,
if (!e) {
e = xmalloc(sizeof(struct commit_name));
oidcpy(&e->peeled, peeled);
- hashmap_entry_init(e, oidhash(peeled));
- hashmap_add(&names, e);
+ hashmap_entry_init(&e->entry, oidhash(peeled));
+ hashmap_add(&names, &e->entry);
e->path = NULL;
}
e->tag = tag;
@@ -313,7 +315,7 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst)
*/
append_name(n, dst);
if (longformat)
- append_suffix(0, n->tag ? &n->tag->tagged->oid : oid, dst);
+ append_suffix(0, n->tag ? get_tagged_oid(n->tag) : oid, dst);
if (suffix)
strbuf_addstr(dst, suffix);
return;
@@ -330,8 +332,8 @@ static void describe_commit(struct object_id *oid, struct strbuf *dst)
struct commit_name *n;
init_commit_names(&commit_names);
- n = hashmap_iter_first(&names, &iter);
- for (; n; n = hashmap_iter_next(&iter)) {
+ hashmap_for_each_entry(&names, &iter, n,
+ entry /* member name */) {
c = lookup_commit_reference_gently(the_repository,
&n->peeled, 1);
if (c)
diff --git a/builtin/difftool.c b/builtin/difftool.c
index 16eb8b7..c280e68 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -125,12 +125,15 @@ struct working_tree_entry {
};
static int working_tree_entry_cmp(const void *unused_cmp_data,
- const void *entry,
- const void *entry_or_key,
+ const struct hashmap_entry *eptr,
+ const struct hashmap_entry *entry_or_key,
const void *unused_keydata)
{
- const struct working_tree_entry *a = entry;
- const struct working_tree_entry *b = entry_or_key;
+ const struct working_tree_entry *a, *b;
+
+ a = container_of(eptr, const struct working_tree_entry, entry);
+ b = container_of(entry_or_key, const struct working_tree_entry, entry);
+
return strcmp(a->path, b->path);
}
@@ -145,12 +148,14 @@ struct pair_entry {
};
static int pair_cmp(const void *unused_cmp_data,
- const void *entry,
- const void *entry_or_key,
+ const struct hashmap_entry *eptr,
+ const struct hashmap_entry *entry_or_key,
const void *unused_keydata)
{
- const struct pair_entry *a = entry;
- const struct pair_entry *b = entry_or_key;
+ const struct pair_entry *a, *b;
+
+ a = container_of(eptr, const struct pair_entry, entry);
+ b = container_of(entry_or_key, const struct pair_entry, entry);
return strcmp(a->path, b->path);
}
@@ -161,14 +166,14 @@ static void add_left_or_right(struct hashmap *map, const char *path,
struct pair_entry *e, *existing;
FLEX_ALLOC_STR(e, path, path);
- hashmap_entry_init(e, strhash(path));
- existing = hashmap_get(map, e, NULL);
+ hashmap_entry_init(&e->entry, strhash(path));
+ existing = hashmap_get_entry(map, e, entry, NULL);
if (existing) {
free(e);
e = existing;
} else {
e->left[0] = e->right[0] = '\0';
- hashmap_add(map, e);
+ hashmap_add(map, &e->entry);
}
strlcpy(is_right ? e->right : e->left, content, PATH_MAX);
}
@@ -179,12 +184,14 @@ struct path_entry {
};
static int path_entry_cmp(const void *unused_cmp_data,
- const void *entry,
- const void *entry_or_key,
+ const struct hashmap_entry *eptr,
+ const struct hashmap_entry *entry_or_key,
const void *key)
{
- const struct path_entry *a = entry;
- const struct path_entry *b = entry_or_key;
+ const struct path_entry *a, *b;
+
+ a = container_of(eptr, const struct path_entry, entry);
+ b = container_of(entry_or_key, const struct path_entry, entry);
return strcmp(a->path, key ? key : b->path);
}
@@ -234,8 +241,8 @@ static void changed_files(struct hashmap *result, const char *index_path,
while (!strbuf_getline_nul(&buf, fp)) {
struct path_entry *entry;
FLEX_ALLOC_STR(entry, path, buf.buf);
- hashmap_entry_init(entry, strhash(buf.buf));
- hashmap_add(result, entry);
+ hashmap_entry_init(&entry->entry, strhash(buf.buf));
+ hashmap_add(result, &entry->entry);
}
fclose(fp);
if (finish_command(&diff_files))
@@ -461,12 +468,13 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
/* Avoid duplicate working_tree entries */
FLEX_ALLOC_STR(entry, path, dst_path);
- hashmap_entry_init(entry, strhash(dst_path));
- if (hashmap_get(&working_tree_dups, entry, NULL)) {
+ hashmap_entry_init(&entry->entry, strhash(dst_path));
+ if (hashmap_get(&working_tree_dups, &entry->entry,
+ NULL)) {
free(entry);
continue;
}
- hashmap_add(&working_tree_dups, entry);
+ hashmap_add(&working_tree_dups, &entry->entry);
if (!use_wt_file(workdir, dst_path, &roid)) {
if (checkout_path(rmode, &roid, dst_path,
@@ -530,8 +538,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
* temporary file to both the left and right directories to show the
* change in the recorded SHA1 for the submodule.
*/
- hashmap_iter_init(&submodules, &iter);
- while ((entry = hashmap_iter_next(&iter))) {
+ 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);
@@ -549,8 +557,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
* shows only the link itself, not the contents of the link target.
* This loop replicates that behavior.
*/
- hashmap_iter_init(&symlinks2, &iter);
- while ((entry = hashmap_iter_next(&iter))) {
+ 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);
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index f541f55..dbec4df 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -40,6 +40,7 @@ static int no_data;
static int full_tree;
static int reference_excluded_commits;
static int show_original_ids;
+static int mark_tags;
static struct string_list extra_refs = STRING_LIST_INIT_NODUP;
static struct string_list tag_refs = STRING_LIST_INIT_NODUP;
static struct refspec refspecs = REFSPEC_INIT_FETCH;
@@ -126,10 +127,15 @@ struct anonymized_entry {
};
static int anonymized_entry_cmp(const void *unused_cmp_data,
- const void *va, const void *vb,
+ const struct hashmap_entry *eptr,
+ const struct hashmap_entry *entry_or_key,
const void *unused_keydata)
{
- const struct anonymized_entry *a = va, *b = vb;
+ const struct anonymized_entry *a, *b;
+
+ a = container_of(eptr, const struct anonymized_entry, hash);
+ b = container_of(entry_or_key, const struct anonymized_entry, hash);
+
return a->orig_len != b->orig_len ||
memcmp(a->orig, b->orig, a->orig_len);
}
@@ -148,10 +154,10 @@ static const void *anonymize_mem(struct hashmap *map,
if (!map->cmpfn)
hashmap_init(map, anonymized_entry_cmp, NULL, 0);
- hashmap_entry_init(&key, memhash(orig, *len));
+ hashmap_entry_init(&key.hash, memhash(orig, *len));
key.orig = orig;
key.orig_len = *len;
- ret = hashmap_get(map, &key, NULL);
+ ret = hashmap_get_entry(map, &key, hash, NULL);
if (!ret) {
ret = xmalloc(sizeof(*ret));
@@ -160,7 +166,7 @@ static const void *anonymize_mem(struct hashmap *map,
ret->orig_len = *len;
ret->anon = generate(orig, len);
ret->anon_len = *len;
- hashmap_put(map, ret);
+ hashmap_put(map, &ret->hash);
}
*len = ret->anon_len;
@@ -842,25 +848,40 @@ static void handle_tag(const char *name, struct tag *tag)
free(buf);
return;
case REWRITE:
- if (tagged->type != OBJ_COMMIT) {
- die("tag %s tags unexported %s!",
- oid_to_hex(&tag->object.oid),
- type_name(tagged->type));
- }
- p = rewrite_commit((struct commit *)tagged);
- if (!p) {
- printf("reset %s\nfrom %s\n\n",
- name, oid_to_hex(&null_oid));
- free(buf);
- return;
+ if (tagged->type == OBJ_TAG && !mark_tags) {
+ die(_("Error: Cannot export nested tags unless --mark-tags is specified."));
+ } else if (tagged->type == OBJ_COMMIT) {
+ p = rewrite_commit((struct commit *)tagged);
+ if (!p) {
+ printf("reset %s\nfrom %s\n\n",
+ name, oid_to_hex(&null_oid));
+ free(buf);
+ return;
+ }
+ tagged_mark = get_object_mark(&p->object);
+ } else {
+ /* tagged->type is either OBJ_BLOB or OBJ_TAG */
+ tagged_mark = get_object_mark(tagged);
}
- tagged_mark = get_object_mark(&p->object);
}
}
+ if (tagged->type == OBJ_TAG) {
+ printf("reset %s\nfrom %s\n\n",
+ name, oid_to_hex(&null_oid));
+ }
if (starts_with(name, "refs/tags/"))
name += 10;
- printf("tag %s\nfrom :%d\n", name, tagged_mark);
+ printf("tag %s\n", name);
+ if (mark_tags) {
+ mark_next_object(&tag->object);
+ printf("mark :%"PRIu32"\n", last_idnum);
+ }
+ if (tagged_mark)
+ printf("from :%d\n", tagged_mark);
+ else
+ printf("from %s\n", oid_to_hex(&tagged->oid));
+
if (show_original_ids)
printf("original-oid %s\n", oid_to_hex(&tag->object.oid));
printf("%.*s%sdata %d\n%.*s\n",
@@ -1047,11 +1068,16 @@ static void export_marks(char *file)
error("Unable to write marks file %s.", file);
}
-static void import_marks(char *input_file)
+static void import_marks(char *input_file, int check_exists)
{
char line[512];
- FILE *f = xfopen(input_file, "r");
+ FILE *f;
+ struct stat sb;
+
+ if (check_exists && stat(input_file, &sb))
+ return;
+ f = xfopen(input_file, "r");
while (fgets(line, sizeof(line), f)) {
uint32_t mark;
char *line_end, *mark_end;
@@ -1115,7 +1141,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
struct rev_info revs;
struct object_array commits = OBJECT_ARRAY_INIT;
struct commit *commit;
- char *export_filename = NULL, *import_filename = NULL;
+ char *export_filename = NULL,
+ *import_filename = NULL,
+ *import_filename_if_exists = NULL;
uint32_t lastimportid;
struct string_list refspecs_list = STRING_LIST_INIT_NODUP;
struct string_list paths_of_changed_objects = STRING_LIST_INIT_DUP;
@@ -1135,6 +1163,10 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
N_("Dump marks to this file")),
OPT_STRING(0, "import-marks", &import_filename, N_("file"),
N_("Import marks from this file")),
+ OPT_STRING(0, "import-marks-if-exists",
+ &import_filename_if_exists,
+ N_("file"),
+ N_("Import marks from this file if it exists")),
OPT_BOOL(0, "fake-missing-tagger", &fake_missing_tagger,
N_("Fake a tagger when tags lack one")),
OPT_BOOL(0, "full-tree", &full_tree,
@@ -1149,6 +1181,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
&reference_excluded_commits, N_("Reference parents which are not in fast-export stream by object id")),
OPT_BOOL(0, "show-original-ids", &show_original_ids,
N_("Show original object ids of blobs/commits")),
+ OPT_BOOL(0, "mark-tags", &mark_tags,
+ N_("Label tags with mark ids")),
OPT_END()
};
@@ -1182,8 +1216,12 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
if (use_done_feature)
printf("feature done\n");
+ if (import_filename && import_filename_if_exists)
+ die(_("Cannot pass both --import-marks and --import-marks-if-exists"));
if (import_filename)
- import_marks(import_filename);
+ import_marks(import_filename, 0);
+ else if (import_filename_if_exists)
+ import_marks(import_filename_if_exists, 1);
lastimportid = last_idnum;
if (import_filename && revs.prune_data.nr)
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 717dd14..863c858 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -7,6 +7,7 @@
#include "refs.h"
#include "refspec.h"
#include "object-store.h"
+#include "oidset.h"
#include "commit.h"
#include "builtin.h"
#include "string-list.h"
@@ -23,6 +24,9 @@
#include "packfile.h"
#include "list-objects-filter-options.h"
#include "commit-reach.h"
+#include "branch.h"
+#include "promisor-remote.h"
+#include "commit-graph.h"
#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
@@ -50,11 +54,13 @@ static int fetch_prune_tags_config = -1; /* unspecified */
static int prune_tags = -1; /* unspecified */
#define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */
-static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
+static int all, append, dry_run, force, keep, multiple, update_head_ok;
+static int verbosity, deepen_relative, set_upstream;
static int progress = -1;
static int enable_auto_gc = 1;
static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
-static int max_children = 1;
+static int max_jobs = -1, submodule_fetch_jobs_config = -1;
+static int fetch_parallel_config = 1;
static enum transport_family family;
static const char *depth;
static const char *deepen_since;
@@ -96,13 +102,20 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
}
if (!strcmp(k, "submodule.fetchjobs")) {
- max_children = parse_submodule_fetchjobs(k, v);
+ submodule_fetch_jobs_config = parse_submodule_fetchjobs(k, v);
return 0;
} else if (!strcmp(k, "fetch.recursesubmodules")) {
recurse_submodules = parse_fetch_recurse_submodules_arg(k, v);
return 0;
}
+ if (!strcmp(k, "fetch.parallel")) {
+ fetch_parallel_config = git_config_int(k, v);
+ if (fetch_parallel_config < 0)
+ die(_("fetch.parallel cannot be negative"));
+ return 0;
+ }
+
return git_default_config(k, v, cb);
}
@@ -123,6 +136,8 @@ static struct option builtin_fetch_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "all", &all,
N_("fetch from all remotes")),
+ OPT_BOOL(0, "set-upstream", &set_upstream,
+ N_("set upstream for git pull/fetch")),
OPT_BOOL('a', "append", &append,
N_("append to .git/FETCH_HEAD instead of overwriting")),
OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
@@ -134,7 +149,7 @@ static struct option builtin_fetch_options[] = {
N_("fetch all tags and associated objects"), TAGS_SET),
OPT_SET_INT('n', NULL, &tags,
N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
- OPT_INTEGER('j', "jobs", &max_children,
+ OPT_INTEGER('j', "jobs", &max_jobs,
N_("number of submodules fetched in parallel")),
OPT_BOOL('p', "prune", &prune,
N_("prune remote-tracking branches no longer on remote")),
@@ -239,32 +254,31 @@ static void add_merge_config(struct ref **head,
}
}
-static int will_fetch(struct ref **head, const unsigned char *sha1)
+static void create_fetch_oidset(struct ref **head, struct oidset *out)
{
struct ref *rm = *head;
while (rm) {
- if (hasheq(rm->old_oid.hash, sha1))
- return 1;
+ oidset_insert(out, &rm->old_oid);
rm = rm->next;
}
- return 0;
}
struct refname_hash_entry {
- struct hashmap_entry ent; /* must be the first member */
+ struct hashmap_entry ent;
struct object_id oid;
int ignore;
char refname[FLEX_ARRAY];
};
static int refname_hash_entry_cmp(const void *hashmap_cmp_fn_data,
- const void *e1_,
- const void *e2_,
+ const struct hashmap_entry *eptr,
+ const struct hashmap_entry *entry_or_key,
const void *keydata)
{
- const struct refname_hash_entry *e1 = e1_;
- const struct refname_hash_entry *e2 = e2_;
+ const struct refname_hash_entry *e1, *e2;
+ e1 = container_of(eptr, const struct refname_hash_entry, ent);
+ e2 = container_of(entry_or_key, const struct refname_hash_entry, ent);
return strcmp(e1->refname, keydata ? keydata : e2->refname);
}
@@ -276,9 +290,9 @@ static struct refname_hash_entry *refname_hash_add(struct hashmap *map,
size_t len = strlen(refname);
FLEX_ALLOC_MEM(ent, refname, refname, len);
- hashmap_entry_init(ent, strhash(refname));
+ hashmap_entry_init(&ent->ent, strhash(refname));
oidcpy(&ent->oid, oid);
- hashmap_add(map, ent);
+ hashmap_add(map, &ent->ent);
return ent;
}
@@ -313,6 +327,7 @@ static void find_non_local_tags(const struct ref *refs,
{
struct hashmap existing_refs;
struct hashmap remote_refs;
+ struct oidset fetch_oids = OIDSET_INIT;
struct string_list remote_refs_list = STRING_LIST_INIT_NODUP;
struct string_list_item *remote_ref_item;
const struct ref *ref;
@@ -320,6 +335,7 @@ static void find_non_local_tags(const struct ref *refs,
refname_hash_init(&existing_refs);
refname_hash_init(&remote_refs);
+ create_fetch_oidset(head, &fetch_oids);
for_each_ref(add_one_refname, &existing_refs);
for (ref = refs; ref; ref = ref->next) {
@@ -336,9 +352,9 @@ static void find_non_local_tags(const struct ref *refs,
if (item &&
!has_object_file_with_flags(&ref->old_oid,
OBJECT_INFO_QUICK) &&
- !will_fetch(head, ref->old_oid.hash) &&
+ !oidset_contains(&fetch_oids, &ref->old_oid) &&
!has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
- !will_fetch(head, item->oid.hash))
+ !oidset_contains(&fetch_oids, &item->oid))
clear_item(item);
item = NULL;
continue;
@@ -352,7 +368,7 @@ static void find_non_local_tags(const struct ref *refs,
*/
if (item &&
!has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
- !will_fetch(head, item->oid.hash))
+ !oidset_contains(&fetch_oids, &item->oid))
clear_item(item);
item = NULL;
@@ -365,7 +381,7 @@ static void find_non_local_tags(const struct ref *refs,
item = refname_hash_add(&remote_refs, ref->name, &ref->old_oid);
string_list_insert(&remote_refs_list, ref->name);
}
- hashmap_free(&existing_refs, 1);
+ hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent);
/*
* We may have a final lightweight tag that needs to be
@@ -373,7 +389,7 @@ static void find_non_local_tags(const struct ref *refs,
*/
if (item &&
!has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
- !will_fetch(head, item->oid.hash))
+ !oidset_contains(&fetch_oids, &item->oid))
clear_item(item);
/*
@@ -383,8 +399,10 @@ static void find_non_local_tags(const struct ref *refs,
for_each_string_list_item(remote_ref_item, &remote_refs_list) {
const char *refname = remote_ref_item->string;
struct ref *rm;
+ unsigned int hash = strhash(refname);
- item = hashmap_get_from_hash(&remote_refs, strhash(refname), refname);
+ item = hashmap_get_entry_from_hash(&remote_refs, hash, refname,
+ struct refname_hash_entry, ent);
if (!item)
BUG("unseen remote ref?");
@@ -398,8 +416,9 @@ static void find_non_local_tags(const struct ref *refs,
**tail = rm;
*tail = &rm->next;
}
- hashmap_free(&remote_refs, 1);
+ hashmap_free_entries(&remote_refs, struct refname_hash_entry, ent);
string_list_clear(&remote_refs_list, 0);
+ oidset_clear(&fetch_oids);
}
static struct ref *get_ref_map(struct remote *remote,
@@ -516,17 +535,18 @@ static struct ref *get_ref_map(struct remote *remote,
if (rm->peer_ref) {
const char *refname = rm->peer_ref->name;
struct refname_hash_entry *peer_item;
+ unsigned int hash = strhash(refname);
- peer_item = hashmap_get_from_hash(&existing_refs,
- strhash(refname),
- refname);
+ peer_item = hashmap_get_entry_from_hash(&existing_refs,
+ hash, refname,
+ struct refname_hash_entry, ent);
if (peer_item) {
struct object_id *old_oid = &peer_item->oid;
oidcpy(&rm->peer_ref->old_oid, old_oid);
}
}
}
- hashmap_free(&existing_refs, 1);
+ hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent);
return ref_map;
}
@@ -1065,8 +1085,11 @@ static int check_exist_and_connected(struct ref *ref_map)
static int fetch_refs(struct transport *transport, struct ref *ref_map)
{
int ret = check_exist_and_connected(ref_map);
- if (ret)
+ 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)
/*
* Keep the new pack's ".keep" file around to allow the caller
@@ -1082,11 +1105,14 @@ 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 = store_updated_refs(transport->url,
- transport->remote->name,
- connectivity_checked,
- ref_map);
+ 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);
return ret;
}
@@ -1238,13 +1264,10 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
if (update_shallow)
set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
if (filter_options.choice) {
- struct strbuf expanded_filter_spec = STRBUF_INIT;
- expand_list_objects_filter_spec(&filter_options,
- &expanded_filter_spec);
- set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
- expanded_filter_spec.buf);
+ const char *spec =
+ expand_list_objects_filter_spec(&filter_options);
+ set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER, spec);
set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
- strbuf_release(&expanded_filter_spec);
}
if (negotiation_tip.nr) {
if (transport->smart_options)
@@ -1334,9 +1357,11 @@ static int do_fetch(struct transport *transport,
argv_array_push(&ref_prefixes, "refs/tags/");
}
- if (must_list_refs)
+ if (must_list_refs) {
+ trace2_region_enter("fetch", "remote_refs", the_repository);
remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
- else
+ trace2_region_leave("fetch", "remote_refs", the_repository);
+ } else
remote_refs = NULL;
argv_array_clear(&ref_prefixes);
@@ -1367,6 +1392,51 @@ static int do_fetch(struct transport *transport,
retcode = 1;
goto cleanup;
}
+
+ if (set_upstream) {
+ struct branch *branch = branch_get("HEAD");
+ struct ref *rm;
+ struct ref *source_ref = NULL;
+
+ /*
+ * We're setting the upstream configuration for the
+ * current branch. The relevent upstream is the
+ * fetched branch that is meant to be merged with the
+ * current one, i.e. the one fetched to FETCH_HEAD.
+ *
+ * When there are several such branches, consider the
+ * request ambiguous and err on the safe side by doing
+ * nothing and just emit a warning.
+ */
+ for (rm = ref_map; rm; rm = rm->next) {
+ if (!rm->peer_ref) {
+ if (source_ref) {
+ warning(_("multiple branch detected, incompatible with --set-upstream"));
+ goto skip;
+ } else {
+ source_ref = rm;
+ }
+ }
+ }
+ if (source_ref) {
+ if (!strcmp(source_ref->name, "HEAD") ||
+ starts_with(source_ref->name, "refs/heads/"))
+ install_branch_config(0,
+ branch->name,
+ transport->remote->name,
+ source_ref->name);
+ else if (starts_with(source_ref->name, "refs/remotes/"))
+ warning(_("not setting upstream for a remote remote-tracking branch"));
+ else if (starts_with(source_ref->name, "refs/tags/"))
+ warning(_("not setting upstream for a remote tag"));
+ else
+ warning(_("unknown branch type"));
+ } else {
+ warning(_("no source branch found.\n"
+ "you need to specify exactly one branch with the --set-upstream option."));
+ }
+ }
+ skip:
free_refs(ref_map);
/* if neither --no-tags nor --tags was specified, do automated tag
@@ -1463,7 +1533,62 @@ static void add_options_to_argv(struct argv_array *argv)
}
-static int fetch_multiple(struct string_list *list)
+/* Fetch multiple remotes in parallel */
+
+struct parallel_fetch_state {
+ const char **argv;
+ struct string_list *remotes;
+ int next, result;
+};
+
+static int fetch_next_remote(struct child_process *cp, struct strbuf *out,
+ void *cb, void **task_cb)
+{
+ struct parallel_fetch_state *state = cb;
+ char *remote;
+
+ if (state->next < 0 || state->next >= state->remotes->nr)
+ return 0;
+
+ remote = state->remotes->items[state->next++].string;
+ *task_cb = remote;
+
+ argv_array_pushv(&cp->args, state->argv);
+ argv_array_push(&cp->args, remote);
+ cp->git_cmd = 1;
+
+ if (verbosity >= 0)
+ printf(_("Fetching %s\n"), remote);
+
+ return 1;
+}
+
+static int fetch_failed_to_start(struct strbuf *out, void *cb, void *task_cb)
+{
+ struct parallel_fetch_state *state = cb;
+ const char *remote = task_cb;
+
+ state->result = error(_("Could not fetch %s"), remote);
+
+ return 0;
+}
+
+static int fetch_finished(int result, struct strbuf *out,
+ void *cb, void *task_cb)
+{
+ struct parallel_fetch_state *state = cb;
+ const char *remote = task_cb;
+
+ if (result) {
+ strbuf_addf(out, _("could not fetch '%s' (exit code: %d)\n"),
+ remote, result);
+ state->result = -1;
+ }
+
+ return 0;
+}
+
+static int fetch_multiple(struct string_list *list, int max_children)
{
int i, result = 0;
struct argv_array argv = ARGV_ARRAY_INIT;
@@ -1477,20 +1602,34 @@ static int fetch_multiple(struct string_list *list)
argv_array_pushl(&argv, "fetch", "--append", "--no-auto-gc", NULL);
add_options_to_argv(&argv);
- for (i = 0; i < list->nr; i++) {
- const char *name = list->items[i].string;
- argv_array_push(&argv, name);
- if (verbosity >= 0)
- printf(_("Fetching %s\n"), name);
- if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
- error(_("Could not fetch %s"), name);
- result = 1;
+ if (max_children != 1 && list->nr != 1) {
+ struct parallel_fetch_state state = { argv.argv, list, 0, 0 };
+
+ argv_array_push(&argv, "--end-of-options");
+ result = run_processes_parallel_tr2(max_children,
+ &fetch_next_remote,
+ &fetch_failed_to_start,
+ &fetch_finished,
+ &state,
+ "fetch", "parallel/fetch");
+
+ if (!result)
+ result = state.result;
+ } else
+ for (i = 0; i < list->nr; i++) {
+ const char *name = list->items[i].string;
+ argv_array_push(&argv, name);
+ if (verbosity >= 0)
+ printf(_("Fetching %s\n"), name);
+ if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
+ error(_("Could not fetch %s"), name);
+ result = 1;
+ }
+ argv_array_pop(&argv);
}
- argv_array_pop(&argv);
- }
argv_array_clear(&argv);
- return result;
+ return !!result;
}
/*
@@ -1510,37 +1649,27 @@ static inline void fetch_one_setup_partial(struct remote *remote)
* If no prior partial clone/fetch and the current fetch DID NOT
* request a partial-fetch, do a normal fetch.
*/
- if (!repository_format_partial_clone && !filter_options.choice)
+ if (!has_promisor_remote() && !filter_options.choice)
return;
/*
- * If this is the FIRST partial-fetch request, we enable partial
- * on this repo and remember the given filter-spec as the default
- * for subsequent fetches to this remote.
+ * If this is a partial-fetch request, we enable partial on
+ * this repo if not already enabled and remember the given
+ * filter-spec as the default for subsequent fetches to this
+ * remote.
*/
- if (!repository_format_partial_clone && filter_options.choice) {
+ if (filter_options.choice) {
partial_clone_register(remote->name, &filter_options);
return;
}
/*
- * We are currently limited to only ONE promisor remote and only
- * allow partial-fetches from the promisor remote.
- */
- if (strcmp(remote->name, repository_format_partial_clone)) {
- if (filter_options.choice)
- die(_("--filter can only be used with the remote "
- "configured in extensions.partialClone"));
- return;
- }
-
- /*
* Do a partial-fetch from the promisor remote using either the
* explicitly given filter-spec or inherit the filter-spec from
* the config.
*/
if (!filter_options.choice)
- partial_clone_get_default_filter_spec(&filter_options);
+ partial_clone_get_default_filter_spec(&filter_options, remote->name);
return;
}
@@ -1626,14 +1755,13 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
packet_trace_identity("fetch");
- fetch_if_missing = 0;
-
/* Record the command line for the reflog */
strbuf_addstr(&default_rla, "fetch");
for (i = 1; i < argc; i++)
strbuf_addf(&default_rla, " %s", argv[i]);
- fetch_config_from_gitmodules(&max_children, &recurse_submodules);
+ fetch_config_from_gitmodules(&submodule_fetch_jobs_config,
+ &recurse_submodules);
git_config(git_fetch_config, NULL);
argc = parse_options(argc, argv, prefix,
@@ -1661,7 +1789,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
if (depth || deepen_since || deepen_not.nr)
deepen = 1;
- if (filter_options.choice && !repository_format_partial_clone)
+ if (filter_options.choice && !has_promisor_remote())
die("--filter can only be used when extensions.partialClone is set");
if (all) {
@@ -1694,20 +1822,34 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
}
}
+ fetch_if_missing = 0;
+
if (remote) {
- if (filter_options.choice || repository_format_partial_clone)
+ if (filter_options.choice || has_promisor_remote())
fetch_one_setup_partial(remote);
result = fetch_one(remote, argc, argv, prune_tags_ok);
} else {
+ int max_children = max_jobs;
+
if (filter_options.choice)
die(_("--filter can only be used with the remote "
"configured in extensions.partialclone"));
+
+ if (max_children < 0)
+ max_children = fetch_parallel_config;
+
/* TODO should this also die if we have a previous partial-clone? */
- result = fetch_multiple(&list);
+ result = fetch_multiple(&list, max_children);
}
if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
struct argv_array options = ARGV_ARRAY_INIT;
+ int max_children = max_jobs;
+
+ if (max_children < 0)
+ max_children = submodule_fetch_jobs_config;
+ if (max_children < 0)
+ max_children = fetch_parallel_config;
add_options_to_argv(&options);
result = fetch_populated_submodules(the_repository,
@@ -1722,6 +1864,20 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
string_list_clear(&list, 0);
+ prepare_repo_settings(the_repository);
+ if (the_repository->settings.fetch_write_commit_graph) {
+ int commit_graph_flags = COMMIT_GRAPH_WRITE_SPLIT;
+ struct split_commit_graph_opts split_opts;
+ memset(&split_opts, 0, sizeof(struct split_commit_graph_opts));
+
+ if (progress)
+ commit_graph_flags |= COMMIT_GRAPH_WRITE_PROGRESS;
+
+ write_commit_graph_reachable(get_object_directory(),
+ commit_graph_flags,
+ &split_opts);
+ }
+
close_object_store(the_repository->objects);
if (enable_auto_gc) {
diff --git a/builtin/gc.c b/builtin/gc.c
index c18efad..fadb454 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -27,6 +27,7 @@
#include "pack-objects.h"
#include "blob.h"
#include "tree.h"
+#include "promisor-remote.h"
#define FAILED_RUN "failed to run %s"
@@ -41,7 +42,6 @@ static int aggressive_depth = 50;
static int aggressive_window = 250;
static int gc_auto_threshold = 6700;
static int gc_auto_pack_limit = 50;
-static int gc_write_commit_graph;
static int detach_auto = 1;
static timestamp_t gc_log_expire_time;
static const char *gc_log_expire = "1.day.ago";
@@ -148,7 +148,6 @@ static void gc_config(void)
git_config_get_int("gc.aggressivedepth", &aggressive_depth);
git_config_get_int("gc.auto", &gc_auto_threshold);
git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
- git_config_get_bool("gc.writecommitgraph", &gc_write_commit_graph);
git_config_get_bool("gc.autodetach", &detach_auto);
git_config_get_expiry("gc.pruneexpire", &prune_expire);
git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire);
@@ -661,7 +660,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
argv_array_push(&prune, prune_expire);
if (quiet)
argv_array_push(&prune, "--no-progress");
- if (repository_format_partial_clone)
+ if (has_promisor_remote())
argv_array_push(&prune,
"--exclude-promisor-objects");
if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
@@ -685,11 +684,11 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
clean_pack_garbage();
}
- if (gc_write_commit_graph &&
- write_commit_graph_reachable(get_object_directory(),
- !quiet && !daemonized ? COMMIT_GRAPH_PROGRESS : 0,
- NULL))
- return 1;
+ prepare_repo_settings(the_repository);
+ if (the_repository->settings.gc_write_commit_graph == 1)
+ write_commit_graph_reachable(get_object_directory(),
+ !quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0,
+ NULL);
if (auto_gc && too_many_loose_objects())
warning(_("There are too many unreachable loose objects; "
diff --git a/builtin/grep.c b/builtin/grep.c
index 5600517..50ce8d9 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -403,7 +403,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
static int grep_submodule(struct grep_opt *opt,
const struct pathspec *pathspec,
const struct object_id *oid,
- const char *filename, const char *path)
+ const char *filename, const char *path, int cached)
{
struct repository subrepo;
struct repository *superproject = opt->repo;
@@ -475,7 +475,7 @@ static int grep_submodule(struct grep_opt *opt,
strbuf_release(&base);
free(data);
} else {
- hit = grep_cache(&subopt, pathspec, 1);
+ hit = grep_cache(&subopt, pathspec, cached);
}
repo_clear(&subrepo);
@@ -523,7 +523,8 @@ static int grep_cache(struct grep_opt *opt,
}
} else if (recurse_submodules && S_ISGITLINK(ce->ce_mode) &&
submodule_path_match(repo->index, pathspec, name.buf, NULL)) {
- hit |= grep_submodule(opt, pathspec, NULL, ce->name, ce->name);
+ hit |= grep_submodule(opt, pathspec, NULL, ce->name,
+ ce->name, cached);
} else {
continue;
}
@@ -598,7 +599,8 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
free(data);
} else if (recurse_submodules && S_ISGITLINK(entry.mode)) {
hit |= grep_submodule(opt, pathspec, &entry.oid,
- base->buf, base->buf + tn_len);
+ base->buf, base->buf + tn_len,
+ 1); /* ignored */
}
strbuf_setlen(base, old_baselen);
@@ -1108,8 +1110,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, buf.buf);
- strbuf_detach(&buf, NULL);
+ string_list_append(&path_list,
+ strbuf_detach(&buf, NULL));
}
}
@@ -1145,5 +1147,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
run_pager(&opt, prefix);
clear_pathspec(&pathspec);
free_grep_patterns(&opt);
+ grep_destroy();
return !hit;
}
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 0d55f73..60a5591 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -14,7 +14,7 @@
#include "thread-utils.h"
#include "packfile.h"
#include "object-store.h"
-#include "fetch-object.h"
+#include "promisor-remote.h"
static const char index_pack_usage[] =
"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
@@ -1352,7 +1352,7 @@ static void fix_unresolved_deltas(struct hashfile *f)
sorted_by_pos[i] = &ref_deltas[i];
QSORT(sorted_by_pos, nr_ref_deltas, delta_pos_compare);
- if (repository_format_partial_clone) {
+ if (has_promisor_remote()) {
/*
* Prefetch the delta bases.
*/
@@ -1366,8 +1366,8 @@ static void fix_unresolved_deltas(struct hashfile *f)
oid_array_append(&to_fetch, &d->oid);
}
if (to_fetch.nr)
- fetch_objects(repository_format_partial_clone,
- to_fetch.oid, to_fetch.nr);
+ promisor_remote_get_direct(the_repository,
+ to_fetch.oid, to_fetch.nr);
oid_array_clear(&to_fetch);
}
@@ -1490,11 +1490,11 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
}
if (!from_stdin) {
- printf("%s\n", sha1_to_hex(hash));
+ printf("%s\n", hash_to_hex(hash));
} else {
struct strbuf buf = STRBUF_INIT;
- strbuf_addf(&buf, "%s\t%s\n", report, sha1_to_hex(hash));
+ strbuf_addf(&buf, "%s\t%s\n", report, hash_to_hex(hash));
write_or_die(1, buf.buf, buf.len);
strbuf_release(&buf);
diff --git a/builtin/log.c b/builtin/log.c
index 44b10b3..a26f223 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -37,6 +37,7 @@
#include "range-diff.h"
#define MAIL_DEFAULT_WRAP 72
+#define COVER_FROM_AUTO_MAX_SUBJECT_LEN 100
/* Set a default date-time format for git log ("log.date" config variable) */
static const char *default_date_mode = NULL;
@@ -627,6 +628,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
break;
case OBJ_TAG: {
struct tag *t = (struct tag *)o;
+ struct object_id *oid = get_tagged_oid(t);
if (rev.shown_one)
putchar('\n');
@@ -638,10 +640,10 @@ int cmd_show(int argc, const char **argv, const char *prefix)
rev.shown_one = 1;
if (ret)
break;
- o = parse_object(the_repository, &t->tagged->oid);
+ o = parse_object(the_repository, oid);
if (!o)
ret = error(_("could not read object %s"),
- oid_to_hex(&t->tagged->oid));
+ oid_to_hex(oid));
objects[i].item = o;
i--;
break;
@@ -764,23 +766,51 @@ static void add_header(const char *value)
item->string[len] = '\0';
}
-#define THREAD_SHALLOW 1
-#define THREAD_DEEP 2
-static int thread;
+enum cover_setting {
+ COVER_UNSET,
+ COVER_OFF,
+ COVER_ON,
+ COVER_AUTO
+};
+
+enum thread_level {
+ THREAD_UNSET,
+ THREAD_SHALLOW,
+ THREAD_DEEP
+};
+
+enum cover_from_description {
+ COVER_FROM_NONE,
+ COVER_FROM_MESSAGE,
+ COVER_FROM_SUBJECT,
+ COVER_FROM_AUTO
+};
+
+static enum thread_level thread;
static int do_signoff;
static int base_auto;
static char *from;
static const char *signature = git_version_string;
static const char *signature_file;
-static int config_cover_letter;
+static enum cover_setting config_cover_letter;
static const char *config_output_directory;
+static enum cover_from_description cover_from_description_mode = COVER_FROM_MESSAGE;
-enum {
- COVER_UNSET,
- COVER_OFF,
- COVER_ON,
- COVER_AUTO
-};
+static enum cover_from_description parse_cover_from_description(const char *arg)
+{
+ if (!arg || !strcmp(arg, "default"))
+ return COVER_FROM_MESSAGE;
+ else if (!strcmp(arg, "none"))
+ return COVER_FROM_NONE;
+ else if (!strcmp(arg, "message"))
+ return COVER_FROM_MESSAGE;
+ else if (!strcmp(arg, "subject"))
+ return COVER_FROM_SUBJECT;
+ else if (!strcmp(arg, "auto"))
+ return COVER_FROM_AUTO;
+ else
+ die(_("%s: invalid cover from description mode"), arg);
+}
static int git_format_config(const char *var, const char *value, void *cb)
{
@@ -835,7 +865,7 @@ static int git_format_config(const char *var, const char *value, void *cb)
thread = THREAD_SHALLOW;
return 0;
}
- thread = git_config_bool(var, value) && THREAD_SHALLOW;
+ thread = git_config_bool(var, value) ? THREAD_SHALLOW : THREAD_UNSET;
return 0;
}
if (!strcmp(var, "format.signoff")) {
@@ -887,6 +917,10 @@ static int git_format_config(const char *var, const char *value, void *cb)
}
return 0;
}
+ if (!strcmp(var, "format.coverfromdescription")) {
+ cover_from_description_mode = parse_cover_from_description(value);
+ return 0;
+ }
return git_log_config(var, value, cb);
}
@@ -993,20 +1027,6 @@ static void print_signature(FILE *file)
putc('\n', file);
}
-static void add_branch_description(struct strbuf *buf, const char *branch_name)
-{
- struct strbuf desc = STRBUF_INIT;
- if (!branch_name || !*branch_name)
- return;
- read_branch_desc(&desc, branch_name);
- if (desc.len) {
- strbuf_addch(buf, '\n');
- strbuf_addbuf(buf, &desc);
- strbuf_addch(buf, '\n');
- }
- strbuf_release(&desc);
-}
-
static char *find_branch_name(struct rev_info *rev)
{
int i, positive = -1;
@@ -1053,6 +1073,44 @@ static void show_diffstat(struct rev_info *rev,
fprintf(rev->diffopt.file, "\n");
}
+static void prepare_cover_text(struct pretty_print_context *pp,
+ const char *branch_name,
+ struct strbuf *sb,
+ const char *encoding,
+ int need_8bit_cte)
+{
+ const char *subject = "*** SUBJECT HERE ***";
+ const char *body = "*** BLURB HERE ***";
+ struct strbuf description_sb = STRBUF_INIT;
+ struct strbuf subject_sb = STRBUF_INIT;
+
+ if (cover_from_description_mode == COVER_FROM_NONE)
+ goto do_pp;
+
+ if (branch_name && *branch_name)
+ read_branch_desc(&description_sb, branch_name);
+ if (!description_sb.len)
+ goto do_pp;
+
+ if (cover_from_description_mode == COVER_FROM_SUBJECT ||
+ cover_from_description_mode == COVER_FROM_AUTO)
+ body = format_subject(&subject_sb, description_sb.buf, " ");
+
+ if (cover_from_description_mode == COVER_FROM_MESSAGE ||
+ (cover_from_description_mode == COVER_FROM_AUTO &&
+ subject_sb.len > COVER_FROM_AUTO_MAX_SUBJECT_LEN))
+ body = description_sb.buf;
+ else
+ subject = subject_sb.buf;
+
+do_pp:
+ pp_title_line(pp, &subject, sb, encoding, need_8bit_cte);
+ pp_remainder(pp, &body, sb, 0);
+
+ strbuf_release(&description_sb);
+ strbuf_release(&subject_sb);
+}
+
static void make_cover_letter(struct rev_info *rev, int use_stdout,
struct commit *origin,
int nr, struct commit **list,
@@ -1060,8 +1118,6 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
int quiet)
{
const char *committer;
- const char *body = "*** SUBJECT HERE ***\n\n*** BLURB HERE ***\n";
- const char *msg;
struct shortlog log;
struct strbuf sb = STRBUF_INIT;
int i;
@@ -1091,15 +1147,12 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
if (!branch_name)
branch_name = find_branch_name(rev);
- msg = body;
pp.fmt = CMIT_FMT_EMAIL;
pp.date_mode.type = DATE_RFC2822;
pp.rev = rev;
pp.print_email_subject = 1;
pp_user_info(&pp, NULL, &sb, committer, encoding);
- pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
- pp_remainder(&pp, &msg, &sb, 0);
- add_branch_description(&sb, branch_name);
+ prepare_cover_text(&pp, branch_name, &sb, encoding, need_8bit_cte);
fprintf(rev->diffopt.file, "%s\n", sb.buf);
strbuf_release(&sb);
@@ -1248,9 +1301,9 @@ static int output_directory_callback(const struct option *opt, const char *arg,
static int thread_callback(const struct option *opt, const char *arg, int unset)
{
- int *thread = (int *)opt->value;
+ enum thread_level *thread = (enum thread_level *)opt->value;
if (unset)
- *thread = 0;
+ *thread = THREAD_UNSET;
else if (!arg || !strcmp(arg, "shallow"))
*thread = THREAD_SHALLOW;
else if (!strcmp(arg, "deep"))
@@ -1541,6 +1594,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
int use_patch_format = 0;
int quiet = 0;
int reroll_count = -1;
+ char *cover_from_description_arg = NULL;
char *branch_name = NULL;
char *base_commit = NULL;
struct base_tree_info bases;
@@ -1577,6 +1631,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
{ OPTION_CALLBACK, 0, "rfc", &rev, NULL,
N_("Use [RFC PATCH] instead of [PATCH]"),
PARSE_OPT_NOARG | PARSE_OPT_NONEG, rfc_callback },
+ OPT_STRING(0, "cover-from-description", &cover_from_description_arg,
+ N_("cover-from-description-mode"),
+ N_("generate parts of a cover letter based on a branch's description")),
{ OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"),
N_("Use [<prefix>] instead of [PATCH]"),
PARSE_OPT_NONEG, subject_prefix_callback },
@@ -1668,6 +1725,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
PARSE_OPT_KEEP_DASHDASH);
+ if (cover_from_description_arg)
+ cover_from_description_mode = parse_cover_from_description(cover_from_description_arg);
+
if (0 < reroll_count) {
struct strbuf sprefix = STRBUF_INIT;
strbuf_addf(&sprefix, "%s v%d",
@@ -1765,10 +1825,26 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
setup_pager();
if (output_directory) {
+ int saved;
if (rev.diffopt.use_color != GIT_COLOR_ALWAYS)
rev.diffopt.use_color = GIT_COLOR_NEVER;
if (use_stdout)
die(_("standard output, or directory, which one?"));
+ /*
+ * We consider <outdir> as 'outside of gitdir', therefore avoid
+ * applying adjust_shared_perm in s-c-l-d.
+ */
+ saved = get_shared_repository();
+ set_shared_repository(0);
+ switch (safe_create_leading_directories_const(output_directory)) {
+ case SCLD_OK:
+ case SCLD_EXISTS:
+ break;
+ default:
+ die(_("could not create leading directories "
+ "of '%s'"), output_directory);
+ }
+ set_shared_repository(saved);
if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
die_errno(_("could not create directory '%s'"),
output_directory);
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 670e8fb..f069a02 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -492,7 +492,7 @@ static int option_parse_exclude_from(const struct option *opt,
BUG_ON_OPT_NEG(unset);
exc_given = 1;
- add_excludes_from_file(dir, arg);
+ add_patterns_from_file(dir, arg);
return 0;
}
@@ -516,7 +516,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
int require_work_tree = 0, show_tag = 0, i;
const char *max_prefix;
struct dir_struct dir;
- struct exclude_list *el;
+ struct pattern_list *pl;
struct string_list exclude_list = STRING_LIST_INIT_NODUP;
struct option builtin_ls_files_options[] = {
/* Think twice before adding "--nul" synonym to this */
@@ -594,9 +594,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
argc = parse_options(argc, argv, prefix, builtin_ls_files_options,
ls_files_usage, 0);
- el = add_exclude_list(&dir, EXC_CMDL, "--exclude option");
+ pl = add_pattern_list(&dir, EXC_CMDL, "--exclude option");
for (i = 0; i < exclude_list.nr; i++) {
- add_exclude(exclude_list.items[i].string, "", 0, el, --exclude_args);
+ add_pattern(exclude_list.items[i].string, "", 0, pl, --exclude_args);
}
if (show_tag || show_valid_bit || show_fsmonitor_bit) {
tag_cached = "H ";
diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
index 5b910e3..a4bfd8f 100644
--- a/builtin/merge-recursive.c
+++ b/builtin/merge-recursive.c
@@ -1,3 +1,4 @@
+#include "cache.h"
#include "builtin.h"
#include "commit.h"
#include "tag.h"
@@ -63,6 +64,9 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
if (argc - i != 3) /* "--" "<head>" "<remote>" */
die(_("not handling anything other than two heads merge."));
+ if (repo_read_index_unmerged(the_repository))
+ die_resolve_conflict("merge");
+
o.branch1 = argv[++i];
o.branch2 = argv[++i];
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 97b54ca..e72714a 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -180,8 +180,9 @@ static struct merge_list *create_entry(unsigned stage, unsigned mode, const stru
static char *traverse_path(const struct traverse_info *info, const struct name_entry *n)
{
- char *path = xmallocz(traverse_path_len(info, n) + the_hash_algo->rawsz);
- return make_traverse_path(path, info, n);
+ struct strbuf buf = STRBUF_INIT;
+ strbuf_make_traverse_path(&buf, info, n->path, n->pathlen);
+ return strbuf_detach(&buf, NULL);
}
static void resolve(const struct traverse_info *info, struct name_entry *ours, struct name_entry *result)
diff --git a/builtin/merge.c b/builtin/merge.c
index e2ccbc4..062e911 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -81,7 +81,7 @@ static int show_progress = -1;
static int default_to_upstream = 1;
static int signoff;
static const char *sign_commit;
-static int verify_msg = 1;
+static int no_verify;
static struct strategy all_strategy[] = {
{ "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL },
@@ -287,7 +287,7 @@ static struct option builtin_merge_options[] = {
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
- OPT_BOOL(0, "verify", &verify_msg, N_("verify commit-msg hook")),
+ OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
OPT_END()
};
@@ -688,16 +688,13 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
struct commit_list *remoteheads,
struct commit *head)
{
- struct lock_file lock = LOCK_INIT;
const char *head_arg = "HEAD";
- hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
- refresh_cache(REFRESH_QUIET);
- if (write_locked_index(&the_index, &lock,
- COMMIT_LOCK | SKIP_IF_UNCHANGED))
+ if (refresh_and_write_cache(REFRESH_QUIET, SKIP_IF_UNCHANGED, 0) < 0)
return error(_("Unable to write index."));
if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
+ struct lock_file lock = LOCK_INIT;
int clean, x;
struct commit *result;
struct commit_list *reversed = NULL;
@@ -816,6 +813,18 @@ static void write_merge_heads(struct commit_list *);
static void prepare_to_commit(struct commit_list *remoteheads)
{
struct strbuf msg = STRBUF_INIT;
+ const char *index_file = get_index_file();
+
+ if (!no_verify && run_commit_hook(0 < option_edit, index_file, "pre-merge-commit", NULL))
+ abort_commit(remoteheads, NULL);
+ /*
+ * Re-read the index as pre-merge-commit hook could have updated it,
+ * 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"))
+ discard_cache();
+ read_cache_from(index_file);
strbuf_addbuf(&msg, &merge_msg);
if (squash)
BUG("the control must not reach here under --squash");
@@ -842,7 +851,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)
abort_commit(remoteheads, NULL);
}
- if (verify_msg && run_commit_hook(0 < option_edit, get_index_file(),
+ if (!no_verify && run_commit_hook(0 < option_edit, get_index_file(),
"commit-msg",
git_path_merge_msg(the_repository), NULL))
abort_commit(remoteheads, NULL);
@@ -860,12 +869,8 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
{
struct object_id result_tree, result_commit;
struct commit_list *parents, **pptr = &parents;
- struct lock_file lock = LOCK_INIT;
- hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
- refresh_cache(REFRESH_QUIET);
- if (write_locked_index(&the_index, &lock,
- COMMIT_LOCK | SKIP_IF_UNCHANGED))
+ if (refresh_and_write_cache(REFRESH_QUIET, SKIP_IF_UNCHANGED, 0) < 0)
return error(_("Unable to write index."));
write_tree_trivial(&result_tree);
diff --git a/builtin/multi-pack-index.c b/builtin/multi-pack-index.c
index b1ea1a6..5bf88cd 100644
--- a/builtin/multi-pack-index.c
+++ b/builtin/multi-pack-index.c
@@ -6,21 +6,25 @@
#include "trace2.h"
static char const * const builtin_multi_pack_index_usage[] = {
- N_("git multi-pack-index [--object-dir=<dir>] (write|verify|expire|repack --batch-size=<size>)"),
+ N_("git multi-pack-index [<options>] (write|verify|expire|repack --batch-size=<size>)"),
NULL
};
static struct opts_multi_pack_index {
const char *object_dir;
unsigned long batch_size;
+ int progress;
} opts;
int cmd_multi_pack_index(int argc, const char **argv,
const char *prefix)
{
+ unsigned flags = 0;
+
static struct option builtin_multi_pack_index_options[] = {
OPT_FILENAME(0, "object-dir", &opts.object_dir,
N_("object directory containing set of packfile and pack-index pairs")),
+ OPT_BOOL(0, "progress", &opts.progress, N_("force progress reporting")),
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_END(),
@@ -28,12 +32,15 @@ int cmd_multi_pack_index(int argc, const char **argv,
git_config(git_default_config, NULL);
+ opts.progress = isatty(2);
argc = parse_options(argc, argv, prefix,
builtin_multi_pack_index_options,
builtin_multi_pack_index_usage, 0);
if (!opts.object_dir)
opts.object_dir = get_object_directory();
+ if (opts.progress)
+ flags |= MIDX_PROGRESS;
if (argc == 0)
usage_with_options(builtin_multi_pack_index_usage,
@@ -47,16 +54,17 @@ int cmd_multi_pack_index(int argc, const char **argv,
trace2_cmd_mode(argv[0]);
if (!strcmp(argv[0], "repack"))
- return midx_repack(the_repository, opts.object_dir, (size_t)opts.batch_size);
+ return midx_repack(the_repository, opts.object_dir,
+ (size_t)opts.batch_size, flags);
if (opts.batch_size)
die(_("--batch-size option is only for 'repack' subcommand"));
if (!strcmp(argv[0], "write"))
- return write_midx_file(opts.object_dir);
+ return write_midx_file(opts.object_dir, flags);
if (!strcmp(argv[0], "verify"))
- return verify_midx_file(the_repository, opts.object_dir);
+ return verify_midx_file(the_repository, opts.object_dir, flags);
if (!strcmp(argv[0], "expire"))
- return expire_midx_packs(the_repository, opts.object_dir);
+ return expire_midx_packs(the_repository, opts.object_dir, flags);
die(_("unrecognized subcommand: %s"), argv[0]);
}
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index c785fe1..b0f0776 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -9,7 +9,11 @@
#include "sha1-lookup.h"
#include "commit-slab.h"
-#define CUTOFF_DATE_SLOP 86400 /* one day */
+/*
+ * One day. See the 'name a rev shortly after epoch' test in t6120 when
+ * changing this value
+ */
+#define CUTOFF_DATE_SLOP 86400
typedef struct rev_name {
const char *tip_name;
@@ -481,8 +485,13 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
add_object_array(object, *argv, &revs);
}
- if (cutoff)
- cutoff = cutoff - CUTOFF_DATE_SLOP;
+ if (cutoff) {
+ /* check for undeflow */
+ if (cutoff > TIME_MIN + CUTOFF_DATE_SLOP)
+ cutoff = cutoff - CUTOFF_DATE_SLOP;
+ else
+ cutoff = TIME_MIN;
+ }
for_each_ref(name_ref, &data);
if (transform_stdin) {
diff --git a/builtin/notes.c b/builtin/notes.c
index 02e97f5..95456f3 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -513,7 +513,7 @@ static int copy(int argc, const char **argv, const char *prefix)
}
}
- if (argc < 2) {
+ if (argc < 1) {
error(_("too few parameters"));
usage_with_options(git_notes_copy_usage, options);
}
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 76ce906..5876583 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -610,12 +610,12 @@ static int mark_tagged(const char *path, const struct object_id *oid, int flag,
void *cb_data)
{
struct object_id peeled;
- struct object_entry *entry = packlist_find(&to_pack, oid, NULL);
+ struct object_entry *entry = packlist_find(&to_pack, oid);
if (entry)
entry->tagged = 1;
if (!peel_ref(path, &peeled)) {
- entry = packlist_find(&to_pack, &peeled, NULL);
+ entry = packlist_find(&to_pack, &peeled);
if (entry)
entry->tagged = 1;
}
@@ -996,12 +996,11 @@ static int no_try_delta(const char *path)
* few lines later when we want to add the new entry.
*/
static int have_duplicate_entry(const struct object_id *oid,
- int exclude,
- uint32_t *index_pos)
+ int exclude)
{
struct object_entry *entry;
- entry = packlist_find(&to_pack, oid, index_pos);
+ entry = packlist_find(&to_pack, oid);
if (!entry)
return 0;
@@ -1141,13 +1140,12 @@ static void create_object_entry(const struct object_id *oid,
uint32_t hash,
int exclude,
int no_try_delta,
- uint32_t index_pos,
struct packed_git *found_pack,
off_t found_offset)
{
struct object_entry *entry;
- entry = packlist_alloc(&to_pack, oid->hash, index_pos);
+ entry = packlist_alloc(&to_pack, oid);
entry->hash = hash;
oe_set_type(entry, type);
if (exclude)
@@ -1171,11 +1169,10 @@ static int add_object_entry(const struct object_id *oid, enum object_type type,
{
struct packed_git *found_pack = NULL;
off_t found_offset = 0;
- uint32_t index_pos;
display_progress(progress_state, ++nr_seen);
- if (have_duplicate_entry(oid, exclude, &index_pos))
+ if (have_duplicate_entry(oid, exclude))
return 0;
if (!want_object_in_pack(oid, exclude, &found_pack, &found_offset)) {
@@ -1190,7 +1187,7 @@ static int add_object_entry(const struct object_id *oid, enum object_type type,
create_object_entry(oid, type, pack_name_hash(name),
exclude, name && no_try_delta(name),
- index_pos, found_pack, found_offset);
+ found_pack, found_offset);
return 1;
}
@@ -1199,17 +1196,15 @@ static int add_object_entry_from_bitmap(const struct object_id *oid,
int flags, uint32_t name_hash,
struct packed_git *pack, off_t offset)
{
- uint32_t index_pos;
-
display_progress(progress_state, ++nr_seen);
- if (have_duplicate_entry(oid, 0, &index_pos))
+ if (have_duplicate_entry(oid, 0))
return 0;
if (!want_object_in_pack(oid, 0, &pack, &offset))
return 0;
- create_object_entry(oid, type, name_hash, 0, 0, index_pos, pack, offset);
+ create_object_entry(oid, type, name_hash, 0, 0, pack, offset);
return 1;
}
@@ -1507,7 +1502,7 @@ static int can_reuse_delta(const unsigned char *base_sha1,
* First see if we're already sending the base (or it's explicitly in
* our "excluded" list).
*/
- base = packlist_find(&to_pack, &base_oid, NULL);
+ base = packlist_find(&to_pack, &base_oid);
if (base) {
if (!in_same_island(&delta->idx.oid, &base->idx.oid))
return 0;
@@ -2342,15 +2337,6 @@ static void find_deltas(struct object_entry **list, unsigned *list_size,
free(array);
}
-static void try_to_free_from_threads(size_t size)
-{
- packing_data_lock(&to_pack);
- release_pack_memory(size);
- packing_data_unlock(&to_pack);
-}
-
-static try_to_free_t old_try_to_free_routine;
-
/*
* The main object list is split into smaller lists, each is handed to
* one worker.
@@ -2391,12 +2377,10 @@ static void init_threaded_search(void)
pthread_mutex_init(&cache_mutex, NULL);
pthread_mutex_init(&progress_mutex, NULL);
pthread_cond_init(&progress_cond, NULL);
- old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads);
}
static void cleanup_threaded_search(void)
{
- set_try_to_free_routine(old_try_to_free_routine);
pthread_cond_destroy(&progress_cond);
pthread_mutex_destroy(&cache_mutex);
pthread_mutex_destroy(&progress_mutex);
@@ -2579,7 +2563,7 @@ static void add_tag_chain(const struct object_id *oid)
* it was included via bitmaps, we would not have parsed it
* previously).
*/
- if (packlist_find(&to_pack, oid, NULL))
+ if (packlist_find(&to_pack, oid))
return;
tag = lookup_tag(the_repository, oid);
@@ -2603,7 +2587,7 @@ static int add_ref_tag(const char *path, const struct object_id *oid, int flag,
if (starts_with(path, "refs/tags/") && /* is a tag? */
!peel_ref(path, &peeled) && /* peelable? */
- packlist_find(&to_pack, &peeled, NULL)) /* object packed? */
+ packlist_find(&to_pack, &peeled)) /* object packed? */
add_tag_chain(oid);
return 0;
}
@@ -2715,10 +2699,6 @@ static int git_pack_config(const char *k, const char *v, void *cb)
use_bitmap_index_default = git_config_bool(k, v);
return 0;
}
- if (!strcmp(k, "pack.usesparse")) {
- sparse = git_config_bool(k, v);
- return 0;
- }
if (!strcmp(k, "pack.threads")) {
delta_search_threads = git_config_int(k, v);
if (delta_search_threads < 0)
@@ -2803,7 +2783,7 @@ static void show_object(struct object *obj, const char *name, void *data)
for (p = strchr(name, '/'); p; p = strchr(p + 1, '/'))
depth++;
- ent = packlist_find(&to_pack, &obj->oid, NULL);
+ ent = packlist_find(&to_pack, &obj->oid);
if (ent && depth > oe_tree_depth(&to_pack, ent))
oe_set_tree_depth(&to_pack, ent, depth);
}
@@ -3034,7 +3014,7 @@ static void loosen_unused_packed_objects(void)
for (i = 0; i < p->num_objects; i++) {
nth_packed_object_oid(&oid, p, i);
- if (!packlist_find(&to_pack, &oid, NULL) &&
+ if (!packlist_find(&to_pack, &oid) &&
!has_sha1_pack_kept_or_nonlocal(&oid) &&
!loosened_object_can_be_discarded(&oid, p->mtime))
if (force_object_loose(&oid, p->mtime))
@@ -3343,6 +3323,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
read_replace_refs = 0;
sparse = git_env_bool("GIT_TEST_PACK_SPARSE", 0);
+ prepare_repo_settings(the_repository);
+ if (!sparse && the_repository->settings.pack_use_sparse != -1)
+ sparse = the_repository->settings.pack_use_sparse;
+
reset_pack_idx_option(&pack_idx_opts);
git_config(git_pack_config, NULL);
diff --git a/builtin/patch-id.c b/builtin/patch-id.c
index bd28b80..3059e52 100644
--- a/builtin/patch-id.c
+++ b/builtin/patch-id.c
@@ -1,15 +1,16 @@
+#include "cache.h"
#include "builtin.h"
#include "config.h"
#include "diff.h"
static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
{
- char name[50];
+ char name[GIT_MAX_HEXSZ + 1];
if (!patchlen)
return;
- memcpy(name, oid_to_hex(id), GIT_SHA1_HEXSZ + 1);
+ memcpy(name, oid_to_hex(id), the_hash_algo->hexsz + 1);
printf("%s %s\n", oid_to_hex(result), name);
}
@@ -60,9 +61,9 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
{
int patchlen = 0, found_next = 0;
int before = -1, after = -1;
- git_SHA_CTX ctx;
+ git_hash_ctx ctx;
- git_SHA1_Init(&ctx);
+ the_hash_algo->init_fn(&ctx);
oidclr(result);
while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) {
@@ -122,7 +123,7 @@ static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
/* Compute the sha without whitespace */
len = remove_space(line);
patchlen += len;
- git_SHA1_Update(&ctx, line, len);
+ the_hash_algo->update_fn(&ctx, line, len);
}
if (!found_next)
diff --git a/builtin/pull.c b/builtin/pull.c
index f1eaf6e..d25ff13 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -129,6 +129,7 @@ static char *opt_refmap;
static char *opt_ipv4;
static char *opt_ipv6;
static int opt_show_forced_updates = -1;
+static char *set_upstream;
static struct option pull_options[] = {
/* Shared options */
@@ -243,6 +244,9 @@ static struct option pull_options[] = {
PARSE_OPT_NOARG),
OPT_BOOL(0, "show-forced-updates", &opt_show_forced_updates,
N_("check for forced-updates on all updated branches")),
+ OPT_PASSTHRU(0, "set-upstream", &set_upstream, NULL,
+ N_("set upstream for git pull/fetch"),
+ PARSE_OPT_NOARG),
OPT_END()
};
@@ -556,6 +560,8 @@ static int run_fetch(const char *repo, const char **refspecs)
argv_array_push(&args, "--show-forced-updates");
else if (opt_show_forced_updates == 0)
argv_array_push(&args, "--no-show-forced-updates");
+ if (set_upstream)
+ argv_array_push(&args, set_upstream);
if (repo) {
argv_array_push(&args, repo);
diff --git a/builtin/push.c b/builtin/push.c
index 021dd3b..843f5b2 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -143,8 +143,8 @@ static int push_url_of_remote(struct remote *remote, const char ***url_p)
return remote->url_nr;
}
-static NORETURN int die_push_simple(struct branch *branch,
- struct remote *remote)
+static NORETURN void die_push_simple(struct branch *branch,
+ struct remote *remote)
{
/*
* There's no point in using shorten_unambiguous_ref here,
@@ -357,8 +357,10 @@ static int push_with_options(struct transport *transport, struct refspec *rs,
if (verbosity > 0)
fprintf(stderr, _("Pushing to %s\n"), transport->url);
+ trace2_region_enter("push", "transport_push", the_repository);
err = transport_push(the_repository, transport,
rs, flags, &reject_reasons);
+ trace2_region_leave("push", "transport_push", the_repository);
if (err != 0) {
fprintf(stderr, "%s", push_get_color(PUSH_COLOR_ERROR));
error(_("failed to push some refs to '%s'"), transport->url);
@@ -385,30 +387,14 @@ static int push_with_options(struct transport *transport, struct refspec *rs,
}
static int do_push(const char *repo, int flags,
- const struct string_list *push_options)
+ const struct string_list *push_options,
+ struct remote *remote)
{
int i, errs;
- struct remote *remote = pushremote_get(repo);
const char **url;
int url_nr;
struct refspec *push_refspec = &rs;
- if (!remote) {
- if (repo)
- die(_("bad repository '%s'"), repo);
- die(_("No configured push destination.\n"
- "Either specify the URL from the command-line or configure a remote repository using\n"
- "\n"
- " git remote add <name> <url>\n"
- "\n"
- "and then push using the remote name\n"
- "\n"
- " git push <name>\n"));
- }
-
- if (remote->mirror)
- flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
-
if (push_options->nr)
flags |= TRANSPORT_PUSH_OPTIONS;
@@ -548,6 +534,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
struct string_list push_options_cmdline = STRING_LIST_INIT_DUP;
struct string_list *push_options;
const struct string_list_item *item;
+ struct remote *remote;
struct option options[] = {
OPT__VERBOSITY(&verbosity),
@@ -602,20 +589,6 @@ int cmd_push(int argc, const char **argv, const char *prefix)
die(_("--delete is incompatible with --all, --mirror and --tags"));
if (deleterefs && argc < 2)
die(_("--delete doesn't make sense without any refs"));
- if (flags & TRANSPORT_PUSH_ALL) {
- if (tags)
- die(_("--all and --tags are incompatible"));
- if (argc >= 2)
- die(_("--all can't be combined with refspecs"));
- }
- if (flags & TRANSPORT_PUSH_MIRROR) {
- if (tags)
- die(_("--mirror and --tags are incompatible"));
- if (argc >= 2)
- die(_("--mirror can't be combined with refspecs"));
- }
- if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR))
- die(_("--all and --mirror are incompatible"));
if (recurse_submodules == RECURSE_SUBMODULES_CHECK)
flags |= TRANSPORT_RECURSE_SUBMODULES_CHECK;
@@ -632,11 +605,43 @@ int cmd_push(int argc, const char **argv, const char *prefix)
set_refspecs(argv + 1, argc - 1, repo);
}
+ remote = pushremote_get(repo);
+ if (!remote) {
+ if (repo)
+ die(_("bad repository '%s'"), repo);
+ die(_("No configured push destination.\n"
+ "Either specify the URL from the command-line or configure a remote repository using\n"
+ "\n"
+ " git remote add <name> <url>\n"
+ "\n"
+ "and then push using the remote name\n"
+ "\n"
+ " git push <name>\n"));
+ }
+
+ if (remote->mirror)
+ flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
+
+ if (flags & TRANSPORT_PUSH_ALL) {
+ if (tags)
+ die(_("--all and --tags are incompatible"));
+ if (argc >= 2)
+ die(_("--all can't be combined with refspecs"));
+ }
+ if (flags & TRANSPORT_PUSH_MIRROR) {
+ if (tags)
+ die(_("--mirror and --tags are incompatible"));
+ if (argc >= 2)
+ die(_("--mirror can't be combined with refspecs"));
+ }
+ if ((flags & TRANSPORT_PUSH_ALL) && (flags & TRANSPORT_PUSH_MIRROR))
+ die(_("--all and --mirror are incompatible"));
+
for_each_string_list_item(item, push_options)
if (strchr(item->string, '\n'))
die(_("push options must not have new line characters"));
- rc = do_push(repo, flags, push_options);
+ rc = do_push(repo, flags, push_options, remote);
string_list_clear(&push_options_cmdline, 0);
string_list_clear(&push_options_config, 0);
if (rc == -1)
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 670096c..e755087 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -29,8 +29,8 @@
#include "rebase-interactive.h"
static char const * const builtin_rebase_usage[] = {
- N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
- "[<upstream>] [<branch>]"),
+ N_("git rebase [-i] [options] [--exec <cmd>] "
+ "[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"),
N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
"--root [<branch>]"),
N_("git rebase --continue | --abort | --skip | --edit-todo"),
@@ -62,7 +62,7 @@ struct rebase_options {
const char *onto_name;
const char *revisions;
const char *switch_to;
- int root;
+ int root, root_with_onto;
struct object_id *squash_onto;
struct commit *restrict_revision;
int dont_finish_rebase;
@@ -374,6 +374,7 @@ static int run_rebase_interactive(struct rebase_options *opts,
flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
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;
switch (command) {
@@ -1260,24 +1261,44 @@ static int is_linear_history(struct commit *from, struct commit *to)
return 1;
}
-static int can_fast_forward(struct commit *onto, struct object_id *head_oid,
- struct object_id *merge_base)
+static int can_fast_forward(struct commit *onto, struct commit *upstream,
+ struct commit *restrict_revision,
+ struct object_id *head_oid, struct object_id *merge_base)
{
struct commit *head = lookup_commit(the_repository, head_oid);
- struct commit_list *merge_bases;
- int res;
+ struct commit_list *merge_bases = NULL;
+ int res = 0;
if (!head)
- return 0;
+ goto done;
merge_bases = get_merge_bases(onto, head);
- if (merge_bases && !merge_bases->next) {
- oidcpy(merge_base, &merge_bases->item->object.oid);
- res = oideq(merge_base, &onto->object.oid);
- } else {
+ if (!merge_bases || merge_bases->next) {
oidcpy(merge_base, &null_oid);
- res = 0;
+ goto done;
}
+
+ oidcpy(merge_base, &merge_bases->item->object.oid);
+ if (!oideq(merge_base, &onto->object.oid))
+ goto done;
+
+ if (restrict_revision && !oideq(&restrict_revision->object.oid, merge_base))
+ goto done;
+
+ if (!upstream)
+ goto done;
+
+ free_commit_list(merge_bases);
+ merge_bases = get_merge_bases(upstream, head);
+ if (!merge_bases || merge_bases->next)
+ goto done;
+
+ if (!oideq(&onto->object.oid, &merge_bases->item->object.oid))
+ goto done;
+
+ res = 1;
+
+done:
free_commit_list(merge_bases);
return res && is_linear_history(onto, head);
}
@@ -1376,6 +1397,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
struct rebase_options options = REBASE_OPTIONS_INIT;
const char *branch_name;
int ret, flags, total_argc, in_progress = 0;
+ int keep_base = 0;
int ok_to_skip_pre_rebase = 0;
struct strbuf msg = STRBUF_INIT;
struct strbuf revisions = STRBUF_INIT;
@@ -1394,6 +1416,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
OPT_STRING(0, "onto", &options.onto_name,
N_("revision"),
N_("rebase onto given branch instead of upstream")),
+ OPT_BOOL(0, "keep-base", &keep_base,
+ N_("use the merge-base of upstream and branch as the current base")),
OPT_BOOL(0, "no-verify", &ok_to_skip_pre_rebase,
N_("allow pre-rebase hook to run")),
OPT_NEGBIT('q', "quiet", &options.flags,
@@ -1447,9 +1471,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('p', "preserve-merges", &options.type,
- N_("(DEPRECATED) try to recreate merges instead of "
- "ignoring them"), REBASE_PRESERVE_MERGES),
+ OPT_SET_INT_F('p', "preserve-merges", &options.type,
+ N_("(DEPRECATED) try to recreate merges instead of "
+ "ignoring them"),
+ REBASE_PRESERVE_MERGES, PARSE_OPT_HIDDEN),
OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
OPT_BOOL('k', "keep-empty", &options.keep_empty,
N_("preserve empty commits during rebase")),
@@ -1547,6 +1572,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
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'"));
+ if (options.root)
+ die(_("cannot combine '--keep-base' with '--root'"));
+ }
+
if (action != ACTION_NONE && !in_progress)
die(_("No rebase in progress?"));
setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
@@ -1833,15 +1865,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
"'--reschedule-failed-exec'"));
}
- if (options.rebase_merges) {
- if (strategy_options.nr)
- die(_("cannot combine '--rebase-merges' with "
- "'--strategy-option'"));
- if (options.strategy)
- die(_("cannot combine '--rebase-merges' with "
- "'--strategy'"));
- }
-
if (!options.root) {
if (argc < 1) {
struct branch *branch;
@@ -1872,7 +1895,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.squash_onto = &squash_onto;
options.onto_name = squash_onto_name =
xstrdup(oid_to_hex(&squash_onto));
- }
+ } else
+ options.root_with_onto = 1;
+
options.upstream_name = NULL;
options.upstream = NULL;
if (argc > 1)
@@ -1882,12 +1907,22 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
}
/* Make sure the branch to rebase onto is valid. */
- if (!options.onto_name)
+ if (keep_base) {
+ strbuf_reset(&buf);
+ strbuf_addstr(&buf, options.upstream_name);
+ strbuf_addstr(&buf, "...");
+ options.onto_name = xstrdup(buf.buf);
+ } else if (!options.onto_name)
options.onto_name = options.upstream_name;
if (strstr(options.onto_name, "...")) {
- if (get_oid_mb(options.onto_name, &merge_base) < 0)
- die(_("'%s': need exactly one merge base"),
- options.onto_name);
+ if (get_oid_mb(options.onto_name, &merge_base) < 0) {
+ if (keep_base)
+ die(_("'%s': need exactly one merge base with branch"),
+ options.upstream_name);
+ else
+ die(_("'%s': need exactly one merge base"),
+ options.onto_name);
+ }
options.onto = lookup_commit_or_die(&merge_base,
options.onto_name);
} else {
@@ -1968,9 +2003,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
state_dir_path("autostash", &options);
struct child_process stash = CHILD_PROCESS_INIT;
struct object_id oid;
- struct commit *head =
- lookup_commit_reference(the_repository,
- &options.orig_head);
argv_array_pushl(&stash.args,
"stash", "create", "autostash", NULL);
@@ -1991,17 +2023,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
options.state_dir);
write_file(autostash, "%s", oid_to_hex(&oid));
printf(_("Created autostash: %s\n"), buf.buf);
- if (reset_head(&head->object.oid, "reset --hard",
+ if (reset_head(NULL, "reset --hard",
NULL, RESET_HEAD_HARD, NULL, NULL) < 0)
die(_("could not reset --hard"));
- printf(_("HEAD is now at %s"),
- find_unique_abbrev(&head->object.oid,
- DEFAULT_ABBREV));
- strbuf_reset(&buf);
- pp_commit_easy(CMIT_FMT_ONELINE, head, &buf);
- if (buf.len > 0)
- printf(" %s", buf.buf);
- putchar('\n');
if (discard_index(the_repository->index) < 0 ||
repo_read_index(the_repository) < 0)
@@ -2022,13 +2046,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
/*
* Check if we are already based on onto with linear history,
- * but this should be done only when upstream and onto are the same
- * and if this is not an interactive rebase.
+ * in which case we could fast-forward without replacing the commits
+ * with new commits recreated by replaying their changes. This
+ * optimization must not be done if this is an interactive rebase.
*/
- if (can_fast_forward(options.onto, &options.orig_head, &merge_base) &&
- !is_interactive(&options) && !options.restrict_revision &&
- options.upstream &&
- !oidcmp(&options.upstream->object.oid, &options.onto->object.oid)) {
+ if (can_fast_forward(options.onto, options.upstream, options.restrict_revision,
+ &options.orig_head, &merge_base) &&
+ !is_interactive(&options)) {
int flag;
if (!(options.flags & REBASE_FORCE)) {
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index dcf3855..411e0b4 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -417,24 +417,22 @@ static int copy_to_sideband(int in, int out, void *arg)
return 0;
}
-#define HMAC_BLOCK_SIZE 64
-
-static void hmac_sha1(unsigned char *out,
+static void hmac(unsigned char *out,
const char *key_in, size_t key_len,
const char *text, size_t text_len)
{
- unsigned char key[HMAC_BLOCK_SIZE];
- unsigned char k_ipad[HMAC_BLOCK_SIZE];
- unsigned char k_opad[HMAC_BLOCK_SIZE];
+ unsigned char key[GIT_MAX_BLKSZ];
+ unsigned char k_ipad[GIT_MAX_BLKSZ];
+ unsigned char k_opad[GIT_MAX_BLKSZ];
int i;
- git_SHA_CTX ctx;
+ git_hash_ctx ctx;
/* RFC 2104 2. (1) */
- memset(key, '\0', HMAC_BLOCK_SIZE);
- if (HMAC_BLOCK_SIZE < key_len) {
- git_SHA1_Init(&ctx);
- git_SHA1_Update(&ctx, key_in, key_len);
- git_SHA1_Final(key, &ctx);
+ memset(key, '\0', GIT_MAX_BLKSZ);
+ if (the_hash_algo->blksz < key_len) {
+ the_hash_algo->init_fn(&ctx);
+ the_hash_algo->update_fn(&ctx, key_in, key_len);
+ the_hash_algo->final_fn(key, &ctx);
} else {
memcpy(key, key_in, key_len);
}
@@ -446,29 +444,29 @@ static void hmac_sha1(unsigned char *out,
}
/* RFC 2104 2. (3) & (4) */
- git_SHA1_Init(&ctx);
- git_SHA1_Update(&ctx, k_ipad, sizeof(k_ipad));
- git_SHA1_Update(&ctx, text, text_len);
- git_SHA1_Final(out, &ctx);
+ the_hash_algo->init_fn(&ctx);
+ the_hash_algo->update_fn(&ctx, k_ipad, sizeof(k_ipad));
+ the_hash_algo->update_fn(&ctx, text, text_len);
+ the_hash_algo->final_fn(out, &ctx);
/* RFC 2104 2. (6) & (7) */
- git_SHA1_Init(&ctx);
- git_SHA1_Update(&ctx, k_opad, sizeof(k_opad));
- git_SHA1_Update(&ctx, out, GIT_SHA1_RAWSZ);
- git_SHA1_Final(out, &ctx);
+ the_hash_algo->init_fn(&ctx);
+ the_hash_algo->update_fn(&ctx, k_opad, sizeof(k_opad));
+ the_hash_algo->update_fn(&ctx, out, the_hash_algo->rawsz);
+ the_hash_algo->final_fn(out, &ctx);
}
static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
{
struct strbuf buf = STRBUF_INIT;
- unsigned char sha1[GIT_SHA1_RAWSZ];
+ unsigned char hash[GIT_MAX_RAWSZ];
strbuf_addf(&buf, "%s:%"PRItime, path, stamp);
- hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));
+ hmac(hash, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));
strbuf_release(&buf);
/* RFC 2104 5. HMAC-SHA1-80 */
- strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, GIT_SHA1_HEXSZ, sha1_to_hex(sha1));
+ strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, (int)the_hash_algo->hexsz, hash_to_hex(hash));
return strbuf_detach(&buf, NULL);
}
@@ -970,7 +968,7 @@ static const char *push_to_deploy(unsigned char *sha1,
if (run_command(&child))
return "Working directory has staged changes";
- read_tree[3] = sha1_to_hex(sha1);
+ read_tree[3] = hash_to_hex(sha1);
child_process_init(&child);
child.argv = read_tree;
child.env = env->argv;
@@ -987,13 +985,13 @@ static const char *push_to_deploy(unsigned char *sha1,
static const char *push_to_checkout_hook = "push-to-checkout";
-static const char *push_to_checkout(unsigned char *sha1,
+static const char *push_to_checkout(unsigned char *hash,
struct argv_array *env,
const char *work_tree)
{
argv_array_pushf(env, "GIT_WORK_TREE=%s", absolute_path(work_tree));
if (run_hook_le(env->argv, push_to_checkout_hook,
- sha1_to_hex(sha1), NULL))
+ hash_to_hex(hash), NULL))
return "push-to-checkout hook declined";
else
return NULL;
diff --git a/builtin/repack.c b/builtin/repack.c
index 632c0c0..0781763 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -11,6 +11,7 @@
#include "midx.h"
#include "packfile.h"
#include "object-store.h"
+#include "promisor-remote.h"
static int delta_base_offset = 1;
static int pack_kept_objects = -1;
@@ -190,7 +191,7 @@ static int write_oid(const struct object_id *oid, struct packed_git *pack,
die(_("could not start pack-objects to repack promisor objects"));
}
- xwrite(cmd->in, oid_to_hex(oid), GIT_SHA1_HEXSZ);
+ xwrite(cmd->in, oid_to_hex(oid), the_hash_algo->hexsz);
xwrite(cmd->in, "\n", 1);
return 0;
}
@@ -232,6 +233,13 @@ static void repack_promisor_objects(const struct pack_objects_args *args,
/*
* pack-objects creates the .pack and .idx files, but not the
* .promisor file. Create the .promisor file, which is empty.
+ *
+ * NEEDSWORK: fetch-pack sometimes generates non-empty
+ * .promisor files containing the ref names and associated
+ * hashes at the point of generation of the corresponding
+ * packfile, but this would not preserve their contents. Maybe
+ * concatenate the contents of all .promisor files instead of
+ * just creating a new empty file.
*/
promisor_name = mkpathdup("%s-%s.promisor", packtmp,
line.buf);
@@ -361,7 +369,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
argv_array_push(&cmd.args, "--all");
argv_array_push(&cmd.args, "--reflog");
argv_array_push(&cmd.args, "--indexed-objects");
- if (repository_format_partial_clone)
+ if (has_promisor_remote())
argv_array_push(&cmd.args, "--exclude-promisor-objects");
if (write_bitmaps > 0)
argv_array_push(&cmd.args, "--write-bitmap-index");
@@ -561,7 +569,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
remove_temporary_files();
if (git_env_bool(GIT_TEST_MULTI_PACK_INDEX, 0))
- write_midx_file(get_object_directory());
+ write_midx_file(get_object_directory(), 0);
string_list_clear(&names, 0);
string_list_clear(&rollback, 0);
diff --git a/builtin/replace.c b/builtin/replace.c
index 644b21c..bd92dc6 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -272,7 +272,7 @@ static int import_object(struct object_id *oid, enum object_type type,
return error(_("unable to spawn mktree"));
}
- if (strbuf_read(&result, cmd.out, 41) < 0) {
+ if (strbuf_read(&result, cmd.out, the_hash_algo->hexsz + 1) < 0) {
error_errno(_("unable to read from mktree"));
close(fd);
close(cmd.out);
@@ -358,14 +358,15 @@ static int replace_parents(struct strbuf *buf, int argc, const char **argv)
struct strbuf new_parents = STRBUF_INIT;
const char *parent_start, *parent_end;
int i;
+ const unsigned hexsz = the_hash_algo->hexsz;
/* find existing parents */
parent_start = buf->buf;
- parent_start += GIT_SHA1_HEXSZ + 6; /* "tree " + "hex sha1" + "\n" */
+ parent_start += hexsz + 6; /* "tree " + "hex sha1" + "\n" */
parent_end = parent_start;
while (starts_with(parent_end, "parent "))
- parent_end += 48; /* "parent " + "hex sha1" + "\n" */
+ parent_end += hexsz + 8; /* "parent " + "hex sha1" + "\n" */
/* prepare new parents */
for (i = 0; i < argc; i++) {
@@ -421,7 +422,7 @@ static int check_one_mergetag(struct commit *commit,
if (get_oid(mergetag_data->argv[i], &oid) < 0)
return error(_("not a valid object name: '%s'"),
mergetag_data->argv[i]);
- if (oideq(&tag->tagged->oid, &oid))
+ if (oideq(get_tagged_oid(tag), &oid))
return 0; /* found */
}
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 301ccb9..e28d62e 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -18,7 +18,6 @@
#include "reflog-walk.h"
#include "oidset.h"
#include "packfile.h"
-#include "object-store.h"
static const char rev_list_usage[] =
"git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
@@ -471,10 +470,6 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
parse_list_objects_filter(&filter_options, arg);
if (filter_options.choice && !revs.blob_objects)
die(_("object filtering requires --objects"));
- if (filter_options.choice == LOFC_SPARSE_OID &&
- !filter_options.sparse_oid_value)
- die(_("invalid sparse value '%s'"),
- filter_options.filter_spec);
continue;
}
if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) {
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index f8bbe6d..308c67e 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -593,6 +593,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
const char *name = NULL;
struct object_context unused;
struct strbuf buf = STRBUF_INIT;
+ const int hexsz = the_hash_algo->hexsz;
if (argc > 1 && !strcmp("--parseopt", argv[1]))
return cmd_parseopt(argc - 1, argv + 1, prefix);
@@ -730,8 +731,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
abbrev = strtoul(arg, NULL, 10);
if (abbrev < MINIMUM_ABBREV)
abbrev = MINIMUM_ABBREV;
- else if (40 <= abbrev)
- abbrev = 40;
+ else if (hexsz <= abbrev)
+ abbrev = hexsz;
continue;
}
if (!strcmp(arg, "--sq")) {
diff --git a/builtin/show-index.c b/builtin/show-index.c
index a6e6788..0826f6a 100644
--- a/builtin/show-index.c
+++ b/builtin/show-index.c
@@ -11,6 +11,7 @@ int cmd_show_index(int argc, const char **argv, const char *prefix)
unsigned nr;
unsigned int version;
static unsigned int top_index[256];
+ const unsigned hashsz = the_hash_algo->rawsz;
if (argc != 1)
usage(show_index_usage);
@@ -36,23 +37,23 @@ int cmd_show_index(int argc, const char **argv, const char *prefix)
}
if (version == 1) {
for (i = 0; i < nr; i++) {
- unsigned int offset, entry[6];
+ unsigned int offset, entry[(GIT_MAX_RAWSZ + 4) / sizeof(unsigned int)];
- if (fread(entry, 4 + 20, 1, stdin) != 1)
+ if (fread(entry, 4 + hashsz, 1, stdin) != 1)
die("unable to read entry %u/%u", i, nr);
offset = ntohl(entry[0]);
- printf("%u %s\n", offset, sha1_to_hex((void *)(entry+1)));
+ printf("%u %s\n", offset, hash_to_hex((void *)(entry+1)));
}
} else {
unsigned off64_nr = 0;
struct {
- unsigned char sha1[20];
+ struct object_id oid;
uint32_t crc;
uint32_t off;
} *entries;
ALLOC_ARRAY(entries, nr);
for (i = 0; i < nr; i++)
- if (fread(entries[i].sha1, 20, 1, stdin) != 1)
+ if (fread(entries[i].oid.hash, hashsz, 1, stdin) != 1)
die("unable to read sha1 %u/%u", i, nr);
for (i = 0; i < nr; i++)
if (fread(&entries[i].crc, 4, 1, stdin) != 1)
@@ -77,7 +78,7 @@ int cmd_show_index(int argc, const char **argv, const char *prefix)
}
printf("%" PRIuMAX " %s (%08"PRIx32")\n",
(uintmax_t) offset,
- sha1_to_hex(entries[i].sha1),
+ oid_to_hex(&entries[i].oid),
ntohl(entries[i].crc));
}
free(entries);
diff --git a/builtin/stash.c b/builtin/stash.c
index 56f3b55..d913487 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -396,7 +396,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
const struct object_id *bases[1];
read_cache_preload(NULL);
- if (refresh_cache(REFRESH_QUIET))
+ if (refresh_and_write_cache(REFRESH_QUIET, 0, 0))
return -1;
if (write_cache_as_tree(&c_tree, 0, NULL))
@@ -427,6 +427,8 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
return error(_("could not save index tree"));
reset_head();
+ discard_cache();
+ read_cache();
}
}
@@ -485,7 +487,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
}
if (quiet) {
- if (refresh_cache(REFRESH_QUIET))
+ if (refresh_and_write_cache(REFRESH_QUIET, 0, 0))
warning("could not refresh index");
} else {
struct child_process cp = CHILD_PROCESS_INIT;
@@ -497,6 +499,10 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
*/
cp.git_cmd = 1;
cp.dir = prefix;
+ argv_array_pushf(&cp.env_array, GIT_WORK_TREE_ENVIRONMENT"=%s",
+ absolute_path(get_git_work_tree()));
+ argv_array_pushf(&cp.env_array, GIT_DIR_ENVIRONMENT"=%s",
+ absolute_path(get_git_dir()));
argv_array_push(&cp.args, "status");
run_command(&cp);
}
@@ -1130,7 +1136,10 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
prepare_fallback_ident("git stash", "git@stash");
read_cache_preload(NULL);
- refresh_cache(REFRESH_QUIET);
+ if (refresh_and_write_cache(REFRESH_QUIET, 0, 0) < 0) {
+ ret = -1;
+ goto done;
+ }
if (get_oid("HEAD", &info->b_commit)) {
if (!quiet)
@@ -1291,7 +1300,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
free(ps_matched);
}
- if (refresh_cache(REFRESH_QUIET)) {
+ if (refresh_and_write_cache(REFRESH_QUIET, 0, 0)) {
ret = -1;
goto done;
}
@@ -1384,7 +1393,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
struct child_process cp = CHILD_PROCESS_INIT;
cp.git_cmd = 1;
argv_array_pushl(&cp.args, "reset", "--hard", "-q",
- NULL);
+ "--no-recurse-submodules", NULL);
if (run_command(&cp)) {
ret = -1;
goto done;
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 909e77e..2c2395a 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -424,7 +424,7 @@ static int module_list(int argc, const char **argv, const char *prefix)
const struct cache_entry *ce = list.entries[i];
if (ce_stage(ce))
- printf("%06o %s U\t", ce->ce_mode, sha1_to_hex(null_sha1));
+ printf("%06o %s U\t", ce->ce_mode, oid_to_hex(&null_oid));
else
printf("%06o %s %d\t", ce->ce_mode,
oid_to_hex(&ce->oid), ce_stage(ce));
@@ -1874,7 +1874,7 @@ static int update_clone(int argc, const char **argv, const char *prefix)
};
const char *const git_submodule_helper_usage[] = {
- N_("git submodule--helper update_clone [--prefix=<path>] [<path>...]"),
+ N_("git submodule--helper update-clone [--prefix=<path>] [<path>...]"),
NULL
};
suc.prefix = prefix;
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 074d563..d527b8f 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -968,6 +968,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
struct parse_opt_ctx_t ctx;
strbuf_getline_fn getline_fn;
int parseopt_state = PARSE_OPT_UNKNOWN;
+ struct repository *r = the_repository;
struct option options[] = {
OPT_BIT('q', NULL, &refresh_args.flags,
N_("continue refresh even when index needs update"),
@@ -1184,11 +1185,12 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
remove_split_index(&the_index);
}
+ prepare_repo_settings(r);
switch (untracked_cache) {
case UC_UNSPECIFIED:
break;
case UC_DISABLE:
- if (git_config_get_untracked_cache() == 1)
+ if (r->settings.core_untracked_cache == UNTRACKED_CACHE_WRITE)
warning(_("core.untrackedCache is set to true; "
"remove or change it, if you really want to "
"disable the untracked cache"));
@@ -1200,7 +1202,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
return !test_if_untracked_cache_is_supported();
case UC_ENABLE:
case UC_FORCE:
- if (git_config_get_untracked_cache() == 0)
+ if (r->settings.core_untracked_cache == UNTRACKED_CACHE_REMOVE)
warning(_("core.untrackedCache is set to false; "
"remove or change it, if you really want to "
"enable the untracked cache"));
diff --git a/builtin/worktree.c b/builtin/worktree.c
index a5bb02b..4de44f5 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -10,7 +10,6 @@
#include "run-command.h"
#include "sigchain.h"
#include "submodule.h"
-#include "refs.h"
#include "utf8.h"
#include "worktree.h"
@@ -350,7 +349,7 @@ static int add_worktree(const char *path, const char *refname,
*/
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
- write_file(sb.buf, "%s", sha1_to_hex(null_sha1));
+ write_file(sb.buf, "%s", oid_to_hex(&null_oid));
strbuf_reset(&sb);
strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
write_file(sb.buf, "../..");
@@ -880,7 +879,7 @@ static void check_clean_worktree(struct worktree *wt,
original_path);
ret = xread(cp.out, buf, sizeof(buf));
if (ret)
- die(_("'%s' is dirty, use --force to delete it"),
+ die(_("'%s' contains modified or untracked files, use --force to delete it"),
original_path);
close(cp.out);
ret = finish_command(&cp);
diff --git a/bulk-checkin.c b/bulk-checkin.c
index 39ee7d6..583aacb 100644
--- a/bulk-checkin.c
+++ b/bulk-checkin.c
@@ -197,7 +197,7 @@ static int deflate_to_pack(struct bulk_checkin_state *state,
git_hash_ctx ctx;
unsigned char obuf[16384];
unsigned header_len;
- struct hashfile_checkpoint checkpoint;
+ struct hashfile_checkpoint checkpoint = {0};
struct pack_idx_entry *idx = NULL;
seekback = lseek(fd, 0, SEEK_CUR);
diff --git a/bundle.c b/bundle.c
index b5d21cd..a85ed3f 100644
--- a/bundle.c
+++ b/bundle.c
@@ -282,7 +282,7 @@ static int write_pack_data(int bundle_fd, struct rev_info *revs)
struct object *object = revs->pending.objects[i].item;
if (object->flags & UNINTERESTING)
write_or_die(pack_objects.in, "^", 1);
- write_or_die(pack_objects.in, oid_to_hex(&object->oid), GIT_SHA1_HEXSZ);
+ write_or_die(pack_objects.in, oid_to_hex(&object->oid), the_hash_algo->hexsz);
write_or_die(pack_objects.in, "\n", 1);
}
close(pack_objects.in);
@@ -414,7 +414,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
}
ref_count++;
- write_or_die(bundle_fd, oid_to_hex(&e->item->oid), 40);
+ write_or_die(bundle_fd, oid_to_hex(&e->item->oid), the_hash_algo->hexsz);
write_or_die(bundle_fd, " ", 1);
write_or_die(bundle_fd, display_ref, strlen(display_ref));
write_or_die(bundle_fd, "\n", 1);
diff --git a/cache-tree.c b/cache-tree.c
index 706ffcf..1bd1b23 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -5,6 +5,7 @@
#include "cache-tree.h"
#include "object-store.h"
#include "replace-object.h"
+#include "promisor-remote.h"
#ifndef DEBUG_CACHE_TREE
#define DEBUG_CACHE_TREE 0
@@ -357,7 +358,7 @@ static int update_one(struct cache_tree *it,
}
ce_missing_ok = mode == S_IFGITLINK || missing_ok ||
- (repository_format_partial_clone &&
+ (has_promisor_remote() &&
ce_skip_worktree(ce));
if (is_null_oid(oid) ||
(!ce_missing_ok && !has_object_file(oid))) {
@@ -407,7 +408,7 @@ static int update_one(struct cache_tree *it,
if (repair) {
struct object_id oid;
hash_object_file(buffer.buf, buffer.len, tree_type, &oid);
- if (has_object_file(&oid))
+ if (has_object_file_with_flags(&oid, OBJECT_INFO_SKIP_FETCH_OBJECT))
oidcpy(&it->oid, &oid);
else
to_invalidate = 1;
@@ -608,11 +609,66 @@ static struct cache_tree *cache_tree_find(struct cache_tree *it, const char *pat
return it;
}
+static int write_index_as_tree_internal(struct object_id *oid,
+ struct index_state *index_state,
+ int cache_tree_valid,
+ int flags,
+ const char *prefix)
+{
+ if (flags & WRITE_TREE_IGNORE_CACHE_TREE) {
+ cache_tree_free(&index_state->cache_tree);
+ cache_tree_valid = 0;
+ }
+
+ if (!index_state->cache_tree)
+ index_state->cache_tree = cache_tree();
+
+ if (!cache_tree_valid && cache_tree_update(index_state, flags) < 0)
+ return WRITE_TREE_UNMERGED_INDEX;
+
+ if (prefix) {
+ struct cache_tree *subtree;
+ subtree = cache_tree_find(index_state->cache_tree, prefix);
+ if (!subtree)
+ return WRITE_TREE_PREFIX_ERROR;
+ oidcpy(oid, &subtree->oid);
+ }
+ else
+ oidcpy(oid, &index_state->cache_tree->oid);
+
+ return 0;
+}
+
+struct tree* write_in_core_index_as_tree(struct repository *repo) {
+ struct object_id o;
+ int was_valid, ret;
+
+ struct index_state *index_state = repo->index;
+ was_valid = index_state->cache_tree &&
+ cache_tree_fully_valid(index_state->cache_tree);
+
+ ret = write_index_as_tree_internal(&o, index_state, was_valid, 0, NULL);
+ if (ret == WRITE_TREE_UNMERGED_INDEX) {
+ int i;
+ fprintf(stderr, "BUG: There are unmerged index entries:\n");
+ for (i = 0; i < index_state->cache_nr; i++) {
+ const struct cache_entry *ce = index_state->cache[i];
+ if (ce_stage(ce))
+ fprintf(stderr, "BUG: %d %.*s\n", ce_stage(ce),
+ (int)ce_namelen(ce), ce->name);
+ }
+ BUG("unmerged index entries when writing inmemory index");
+ }
+
+ return lookup_tree(repo, &index_state->cache_tree->oid);
+}
+
+
int write_index_as_tree(struct object_id *oid, struct index_state *index_state, const char *index_path, int flags, const char *prefix)
{
int entries, was_valid;
struct lock_file lock_file = LOCK_INIT;
- int ret = 0;
+ int ret;
hold_lock_file_for_update(&lock_file, index_path, LOCK_DIE_ON_ERROR);
@@ -621,18 +677,14 @@ int write_index_as_tree(struct object_id *oid, struct index_state *index_state,
ret = WRITE_TREE_UNREADABLE_INDEX;
goto out;
}
- if (flags & WRITE_TREE_IGNORE_CACHE_TREE)
- cache_tree_free(&index_state->cache_tree);
- if (!index_state->cache_tree)
- index_state->cache_tree = cache_tree();
+ was_valid = !(flags & WRITE_TREE_IGNORE_CACHE_TREE) &&
+ index_state->cache_tree &&
+ cache_tree_fully_valid(index_state->cache_tree);
- was_valid = cache_tree_fully_valid(index_state->cache_tree);
- if (!was_valid) {
- if (cache_tree_update(index_state, flags) < 0) {
- ret = WRITE_TREE_UNMERGED_INDEX;
- goto out;
- }
+ ret = write_index_as_tree_internal(oid, index_state, was_valid, flags,
+ prefix);
+ if (!ret && !was_valid) {
write_locked_index(index_state, &lock_file, COMMIT_LOCK);
/* Not being able to write is fine -- we are only interested
* in updating the cache-tree part, and if the next caller
@@ -642,18 +694,6 @@ int write_index_as_tree(struct object_id *oid, struct index_state *index_state,
*/
}
- if (prefix) {
- struct cache_tree *subtree;
- subtree = cache_tree_find(index_state->cache_tree, prefix);
- if (!subtree) {
- ret = WRITE_TREE_PREFIX_ERROR;
- goto out;
- }
- oidcpy(oid, &subtree->oid);
- }
- else
- oidcpy(oid, &index_state->cache_tree->oid);
-
out:
rollback_lock_file(&lock_file);
return ret;
@@ -713,7 +753,7 @@ static struct cache_tree *find_cache_tree_from_traversal(struct cache_tree *root
if (!info->prev)
return root;
our_parent = find_cache_tree_from_traversal(root, info->prev);
- return cache_tree_find(our_parent, info->name.path);
+ return cache_tree_find(our_parent, info->name);
}
int cache_tree_matches_traversal(struct cache_tree *root,
diff --git a/cache-tree.h b/cache-tree.h
index 757bbc4..639bfa5 100644
--- a/cache-tree.h
+++ b/cache-tree.h
@@ -34,7 +34,7 @@ int cache_tree_fully_valid(struct cache_tree *);
int cache_tree_update(struct index_state *, int);
void cache_tree_verify(struct repository *, struct index_state *);
-/* bitmasks to write_cache_as_tree flags */
+/* bitmasks to write_index_as_tree flags */
#define WRITE_TREE_MISSING_OK 1
#define WRITE_TREE_IGNORE_CACHE_TREE 2
#define WRITE_TREE_DRY_RUN 4
@@ -46,6 +46,7 @@ void cache_tree_verify(struct repository *, struct index_state *);
#define WRITE_TREE_UNMERGED_INDEX (-2)
#define WRITE_TREE_PREFIX_ERROR (-3)
+struct tree* write_in_core_index_as_tree(struct repository *repo);
int write_index_as_tree(struct object_id *oid, struct index_state *index_state, const char *index_path, int flags, const char *prefix);
void prime_cache_tree(struct repository *, struct index_state *, struct tree *);
diff --git a/cache.h b/cache.h
index b1da1ab..04cabaa 100644
--- a/cache.h
+++ b/cache.h
@@ -414,6 +414,7 @@ extern struct index_state the_index;
#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags))
#define chmod_cache_entry(ce, flip) chmod_index_entry(&the_index, (ce), (flip))
#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
+#define refresh_and_write_cache(refresh_flags, write_flags, gentle) repo_refresh_and_write_index(the_repository, (refresh_flags), (write_flags), (gentle), NULL, NULL, NULL)
#define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
#define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
#define cache_dir_exists(name, namelen) index_dir_exists(&the_index, (name), (namelen))
@@ -636,6 +637,9 @@ int daemonize(void);
* at least 'nr' entries; the number of entries currently allocated
* is 'alloc', using the standard growing factor alloc_nr() macro.
*
+ * Consider using ALLOC_GROW_BY instead of ALLOC_GROW as it has some
+ * added niceties.
+ *
* DO NOT USE any expression with side-effect for 'x', 'nr', or 'alloc'.
*/
#define ALLOC_GROW(x, nr, alloc) \
@@ -649,6 +653,25 @@ int daemonize(void);
} \
} while (0)
+/*
+ * Similar to ALLOC_GROW but handles updating of the nr value and
+ * zeroing the bytes of the newly-grown array elements.
+ *
+ * DO NOT USE any expression with side-effect for any of the
+ * arguments.
+ */
+#define ALLOC_GROW_BY(x, nr, increase, alloc) \
+ do { \
+ if (increase) { \
+ size_t new_nr = nr + (increase); \
+ if (new_nr < nr) \
+ BUG("negative growth in ALLOC_GROW_BY"); \
+ ALLOC_GROW(x, new_nr, alloc); \
+ memset((x) + nr, 0, sizeof(*(x)) * (increase)); \
+ nr = new_nr; \
+ } \
+ } while (0)
+
/* Initialize and use the cache information */
struct lock_file;
void preload_index(struct index_state *index,
@@ -725,6 +748,19 @@ struct cache_entry *index_file_exists(struct index_state *istate, const char *na
*/
int index_name_pos(const struct index_state *, const char *name, int namelen);
+/*
+ * Some functions return the negative complement of an insert position when a
+ * precise match was not found but a position was found where the entry would
+ * need to be inserted. This helper protects that logic from any integer
+ * underflow.
+ */
+static inline int index_pos_to_insert_pos(uintmax_t pos)
+{
+ if (pos > INT_MAX)
+ die("overflow: -1 - %"PRIuMAX, pos);
+ return -1 - (int)pos;
+}
+
#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */
#define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */
#define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */
@@ -812,6 +848,23 @@ void fill_stat_cache_info(struct index_state *istate, struct cache_entry *ce, st
#define REFRESH_IN_PORCELAIN 0x0020 /* user friendly output, not "needs update" */
#define REFRESH_PROGRESS 0x0040 /* show progress bar if stderr is tty */
int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
+/*
+ * Refresh the index and write it to disk.
+ *
+ * 'refresh_flags' is passed directly to 'refresh_index()', while
+ * 'COMMIT_LOCK | write_flags' is passed to 'write_locked_index()', so
+ * the lockfile is always either committed or rolled back.
+ *
+ * If 'gentle' is passed, errors locking the index are ignored.
+ *
+ * Return 1 if refreshing the index returns an error, -1 if writing
+ * the index to disk fails, 0 on success.
+ *
+ * Note that if refreshing the index returns an error, we still write
+ * out the index (unless locking fails).
+ */
+int repo_refresh_and_write_index(struct repository*, unsigned int refresh_flags, unsigned int write_flags, int gentle, const struct pathspec *, char *seen, const char *header_msg);
+
struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
void set_alternate_index_output(const char *);
@@ -937,8 +990,6 @@ extern int grafts_replace_parents;
#define GIT_REPO_VERSION 0
#define GIT_REPO_VERSION_READ 1
extern int repository_format_precious_objects;
-extern char *repository_format_partial_clone;
-extern const char *core_partial_clone_filter_default;
extern int repository_format_worktree_config;
/*
@@ -1029,7 +1080,6 @@ const char *repo_find_unique_abbrev(struct repository *r, const struct object_id
int repo_find_unique_abbrev_r(struct repository *r, char *hex, const struct object_id *oid, int len);
#define find_unique_abbrev_r(hex, oid, len) repo_find_unique_abbrev_r(the_repository, hex, oid, len)
-extern const unsigned char null_sha1[GIT_MAX_RAWSZ];
extern const struct object_id null_oid;
static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
@@ -1064,14 +1114,9 @@ static inline int oideq(const struct object_id *oid1, const struct object_id *oi
return hasheq(oid1->hash, oid2->hash);
}
-static inline int is_null_sha1(const unsigned char *sha1)
-{
- return hasheq(sha1, null_sha1);
-}
-
static inline int is_null_oid(const struct object_id *oid)
{
- return hasheq(oid->hash, null_sha1);
+ return oideq(oid, &null_oid);
}
static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
@@ -1516,8 +1561,7 @@ struct date_mode {
struct date_mode *date_mode_from_type(enum date_mode_type type);
const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode);
-void show_date_relative(timestamp_t time, const struct timeval *now,
- struct strbuf *timebuf);
+void show_date_relative(timestamp_t time, struct strbuf *timebuf);
void show_date_human(timestamp_t time, int tz, const struct timeval *now,
struct strbuf *timebuf);
int parse_date(const char *date, struct strbuf *out);
@@ -1526,7 +1570,7 @@ int parse_expiry_date(const char *date, timestamp_t *timestamp);
void datestamp(struct strbuf *out);
#define approxidate(s) approxidate_careful((s), NULL)
timestamp_t approxidate_careful(const char *, int *);
-timestamp_t approxidate_relative(const char *date, const struct timeval *now);
+timestamp_t approxidate_relative(const char *date);
void parse_date_format(const char *format, struct date_mode *mode);
int date_overflows(timestamp_t date);
diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh
index 8cc7250..4e64a19 100755
--- a/ci/install-dependencies.sh
+++ b/ci/install-dependencies.sh
@@ -40,20 +40,27 @@ osx-clang|osx-gcc)
test -z "$BREW_INSTALL_PACKAGES" ||
brew install $BREW_INSTALL_PACKAGES
brew link --force gettext
+ brew cask install perforce || {
+ # Update the definitions and try again
+ git -C "$(brew --repository)"/Library/Taps/homebrew/homebrew-cask pull &&
+ brew cask install perforce
+ } ||
brew install caskroom/cask/perforce
case "$jobname" in
osx-gcc)
+ brew link gcc ||
brew link gcc@8
;;
esac
;;
StaticAnalysis)
sudo apt-get -q update
- sudo apt-get -q -y install coccinelle
+ sudo apt-get -q -y install coccinelle libcurl4-openssl-dev libssl-dev \
+ libexpat-dev gettext
;;
Documentation)
sudo apt-get -q update
- sudo apt-get -q -y install asciidoc xmlto
+ sudo apt-get -q -y install asciidoc xmlto docbook-xsl-ns
test -n "$ALREADY_HAVE_ASCIIDOCTOR" ||
gem install --version 1.5.8 asciidoctor
diff --git a/ci/lib.sh b/ci/lib.sh
index 44db2d5..c8c2c38 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -34,6 +34,11 @@ save_good_tree () {
# successfully before (e.g. because the branch got rebased, changing only
# the commit messages).
skip_good_tree () {
+ if test "$TRAVIS_DEBUG_MODE" = true
+ then
+ return
+ fi
+
if ! good_tree_info="$(grep "^$(git rev-parse $CI_COMMIT^{tree}) " "$good_trees_file")"
then
# Haven't seen this tree yet, or no cached good trees file yet.
@@ -160,7 +165,7 @@ linux-clang|linux-gcc)
export CC=gcc-8
fi
- export GIT_TEST_HTTPD=YesPlease
+ export GIT_TEST_HTTPD=true
# The Linux build installs the defined dependency versions below.
# The OS X build installs much more recent versions, whichever
diff --git a/ci/run-static-analysis.sh b/ci/run-static-analysis.sh
index a19aa7e..65bcebd 100755
--- a/ci/run-static-analysis.sh
+++ b/ci/run-static-analysis.sh
@@ -26,4 +26,7 @@ then
exit 1
fi
+make hdr-check ||
+exit 1
+
save_good_tree
diff --git a/ci/test-documentation.sh b/ci/test-documentation.sh
index d490898..b3e76ef 100755
--- a/ci/test-documentation.sh
+++ b/ci/test-documentation.sh
@@ -8,6 +8,8 @@
filter_log () {
sed -e '/^GIT_VERSION = /d' \
-e '/^ \* new asciidoc flags$/d' \
+ -e '/stripped namespace before processing/d' \
+ -e '/Attributed.*IDs for element/d' \
"$1"
}
diff --git a/column.c b/column.c
index 7a17c14..4a38eed 100644
--- a/column.c
+++ b/column.c
@@ -23,18 +23,7 @@ struct column_data {
/* return length of 's' in letters, ANSI escapes stripped */
static int item_length(const char *s)
{
- int len, i = 0;
- struct strbuf str = STRBUF_INIT;
-
- strbuf_addstr(&str, s);
- while ((s = strstr(str.buf + i, "\033[")) != NULL) {
- int len = strspn(s + 2, "0123456789;");
- i = s - str.buf;
- strbuf_remove(&str, i, len + 3); /* \033[<len><func char> */
- }
- len = utf8_strwidth(str.buf);
- strbuf_release(&str);
- return len;
+ return utf8_strnwidth(s, -1, 1);
}
/*
diff --git a/combine-diff.c b/combine-diff.c
index 3e49f3b..d5c4d83 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -930,7 +930,7 @@ static void show_combined_header(struct combine_diff_path *elem,
int show_file_header)
{
struct diff_options *opt = &rev->diffopt;
- int abbrev = opt->flags.full_index ? GIT_SHA1_HEXSZ : DEFAULT_ABBREV;
+ int abbrev = opt->flags.full_index ? the_hash_algo->hexsz : DEFAULT_ABBREV;
const char *a_prefix = opt->a_prefix ? opt->a_prefix : "a/";
const char *b_prefix = opt->b_prefix ? opt->b_prefix : "b/";
const char *c_meta = diff_get_color_opt(opt, DIFF_METAINFO);
diff --git a/commit-graph.c b/commit-graph.c
index fe954ab..0aea7b2 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -41,6 +41,9 @@
#define GRAPH_MIN_SIZE (GRAPH_HEADER_SIZE + 4 * GRAPH_CHUNKLOOKUP_WIDTH \
+ GRAPH_FANOUT_SIZE + the_hash_algo->rawsz)
+/* Remember to update object flag allocation in object.h */
+#define REACHABLE (1u<<15)
+
char *get_commit_graph_filename(const char *obj_dir)
{
char *filename = xstrfmt("%s/info/commit-graph", obj_dir);
@@ -467,19 +470,26 @@ static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)
static int prepare_commit_graph(struct repository *r)
{
struct object_directory *odb;
- int config_value;
- if (git_env_bool(GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD, 0))
- die("dying as requested by the '%s' variable on commit-graph load!",
- GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD);
+ /*
+ * This must come before the "already attempted?" check below, because
+ * we want to disable even an already-loaded graph file.
+ */
+ if (r->commit_graph_disabled)
+ return 0;
if (r->objects->commit_graph_attempted)
return !!r->objects->commit_graph;
r->objects->commit_graph_attempted = 1;
+ if (git_env_bool(GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD, 0))
+ die("dying as requested by the '%s' variable on commit-graph load!",
+ GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD);
+
+ prepare_repo_settings(r);
+
if (!git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
- (repo_config_get_bool(r, "core.commitgraph", &config_value) ||
- !config_value))
+ r->settings.core_commit_graph != 1)
/*
* This repository is not configured to use commit graphs, so
* do not load one. (But report commit_graph_attempted anyway
@@ -783,7 +793,8 @@ struct write_commit_graph_context {
unsigned append:1,
report_progress:1,
- split:1;
+ split:1,
+ check_oids:1;
const struct split_commit_graph_opts *split_opts;
};
@@ -838,12 +849,19 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
while (list < last) {
struct commit_list *parent;
+ struct object_id *tree;
int edge_value;
uint32_t packedDate[2];
display_progress(ctx->progress, ++ctx->progress_cnt);
- parse_commit_no_graph(*list);
- hashwrite(f, get_commit_tree_oid(*list)->hash, hash_len);
+ if (parse_commit_no_graph(*list))
+ die(_("unable to parse commit %s"),
+ oid_to_hex(&(*list)->object.oid));
+ tree = get_commit_tree_oid(*list);
+ if (!tree)
+ die(_("unable to get tree for %s"),
+ oid_to_hex(&(*list)->object.oid));
+ hashwrite(f, tree->hash, hash_len);
parent = (*list)->parents;
@@ -1015,11 +1033,11 @@ static void add_missing_parents(struct write_commit_graph_context *ctx, struct c
{
struct commit_list *parent;
for (parent = commit->parents; parent; parent = parent->next) {
- if (!(parent->item->object.flags & UNINTERESTING)) {
+ if (!(parent->item->object.flags & REACHABLE)) {
ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
oidcpy(&ctx->oids.list[ctx->oids.nr], &(parent->item->object.oid));
ctx->oids.nr++;
- parent->item->object.flags |= UNINTERESTING;
+ parent->item->object.flags |= REACHABLE;
}
}
}
@@ -1037,7 +1055,7 @@ static void close_reachable(struct write_commit_graph_context *ctx)
display_progress(ctx->progress, i + 1);
commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
if (commit)
- commit->object.flags |= UNINTERESTING;
+ commit->object.flags |= REACHABLE;
}
stop_progress(&ctx->progress);
@@ -1049,7 +1067,7 @@ static void close_reachable(struct write_commit_graph_context *ctx)
if (ctx->report_progress)
ctx->progress = start_delayed_progress(
_("Expanding reachable commits in commit graph"),
- ctx->oids.nr);
+ 0);
for (i = 0; i < ctx->oids.nr; i++) {
display_progress(ctx->progress, i + 1);
commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
@@ -1074,7 +1092,7 @@ static void close_reachable(struct write_commit_graph_context *ctx)
commit = lookup_commit(ctx->r, &ctx->oids.list[i]);
if (commit)
- commit->object.flags &= ~UNINTERESTING;
+ commit->object.flags &= ~REACHABLE;
}
stop_progress(&ctx->progress);
}
@@ -1134,7 +1152,8 @@ static int add_ref_to_list(const char *refname,
return 0;
}
-int write_commit_graph_reachable(const char *obj_dir, unsigned int flags,
+int write_commit_graph_reachable(const char *obj_dir,
+ enum commit_graph_write_flags flags,
const struct split_commit_graph_opts *split_opts)
{
struct string_list list = STRING_LIST_INIT_DUP;
@@ -1193,8 +1212,8 @@ static int fill_oids_from_packs(struct write_commit_graph_context *ctx,
return 0;
}
-static void fill_oids_from_commit_hex(struct write_commit_graph_context *ctx,
- struct string_list *commit_hex)
+static int fill_oids_from_commit_hex(struct write_commit_graph_context *ctx,
+ struct string_list *commit_hex)
{
uint32_t i;
struct strbuf progress_title = STRBUF_INIT;
@@ -1215,20 +1234,21 @@ static void fill_oids_from_commit_hex(struct write_commit_graph_context *ctx,
struct commit *result;
display_progress(ctx->progress, i + 1);
- if (commit_hex->items[i].string &&
- parse_oid_hex(commit_hex->items[i].string, &oid, &end))
- continue;
-
- result = lookup_commit_reference_gently(ctx->r, &oid, 1);
-
- if (result) {
+ if (!parse_oid_hex(commit_hex->items[i].string, &oid, &end) &&
+ (result = lookup_commit_reference_gently(ctx->r, &oid, 1))) {
ALLOC_GROW(ctx->oids.list, ctx->oids.nr + 1, ctx->oids.alloc);
oidcpy(&ctx->oids.list[ctx->oids.nr], &(result->object.oid));
ctx->oids.nr++;
+ } else if (ctx->check_oids) {
+ error(_("invalid commit object id: %s"),
+ commit_hex->items[i].string);
+ return -1;
}
}
stop_progress(&ctx->progress);
strbuf_release(&progress_title);
+
+ return 0;
}
static void fill_oids_from_all_packs(struct write_commit_graph_context *ctx)
@@ -1276,7 +1296,6 @@ static uint32_t count_distinct_commits(struct write_commit_graph_context *ctx)
static void copy_oids_to_commits(struct write_commit_graph_context *ctx)
{
uint32_t i;
- struct commit_list *parent;
ctx->num_extra_edges = 0;
if (ctx->report_progress)
@@ -1284,7 +1303,8 @@ static void copy_oids_to_commits(struct write_commit_graph_context *ctx)
_("Finding extra edges in commit graph"),
ctx->oids.nr);
for (i = 0; i < ctx->oids.nr; i++) {
- int num_parents = 0;
+ unsigned int num_parents;
+
display_progress(ctx->progress, i + 1);
if (i > 0 && oideq(&ctx->oids.list[i - 1], &ctx->oids.list[i]))
continue;
@@ -1298,10 +1318,7 @@ static void copy_oids_to_commits(struct write_commit_graph_context *ctx)
parse_commit_no_graph(ctx->commits.list[ctx->commits.nr]);
- for (parent = ctx->commits.list[ctx->commits.nr]->parents;
- parent; parent = parent->next)
- num_parents++;
-
+ num_parents = commit_list_count(ctx->commits.list[ctx->commits.nr]->parents);
if (num_parents > 2)
ctx->num_extra_edges += num_parents - 1;
@@ -1519,8 +1536,8 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
static void split_graph_merge_strategy(struct write_commit_graph_context *ctx)
{
- struct commit_graph *g = ctx->r->objects->commit_graph;
- uint32_t num_commits = ctx->commits.nr;
+ struct commit_graph *g;
+ uint32_t num_commits;
uint32_t i;
int max_commits = 0;
@@ -1532,6 +1549,7 @@ static void split_graph_merge_strategy(struct write_commit_graph_context *ctx)
}
g = ctx->r->objects->commit_graph;
+ num_commits = ctx->commits.nr;
ctx->num_commit_graphs_after = ctx->num_commit_graphs_before + 1;
while (g && (g->num_commits <= size_mult * num_commits ||
@@ -1613,8 +1631,7 @@ static int commit_compare(const void *_a, const void *_b)
static void sort_and_scan_merged_commits(struct write_commit_graph_context *ctx)
{
- uint32_t i, num_parents;
- struct commit_list *parent;
+ uint32_t i;
if (ctx->report_progress)
ctx->progress = start_delayed_progress(
@@ -1632,10 +1649,9 @@ static void sort_and_scan_merged_commits(struct write_commit_graph_context *ctx)
die(_("unexpected duplicate commit id %s"),
oid_to_hex(&ctx->commits.list[i]->object.oid));
} else {
- num_parents = 0;
- for (parent = ctx->commits.list[i]->parents; parent; parent = parent->next)
- num_parents++;
+ unsigned int num_parents;
+ num_parents = commit_list_count(ctx->commits.list[i]->parents);
if (num_parents > 2)
ctx->num_extra_edges += num_parents - 1;
}
@@ -1752,7 +1768,7 @@ out:
int write_commit_graph(const char *obj_dir,
struct string_list *pack_indexes,
struct string_list *commit_hex,
- unsigned int flags,
+ enum commit_graph_write_flags flags,
const struct split_commit_graph_opts *split_opts)
{
struct write_commit_graph_context *ctx;
@@ -1773,9 +1789,10 @@ int write_commit_graph(const char *obj_dir,
if (len && ctx->obj_dir[len - 1] == '/')
ctx->obj_dir[len - 1] = 0;
- ctx->append = flags & COMMIT_GRAPH_APPEND ? 1 : 0;
- ctx->report_progress = flags & COMMIT_GRAPH_PROGRESS ? 1 : 0;
- ctx->split = flags & COMMIT_GRAPH_SPLIT ? 1 : 0;
+ ctx->append = flags & COMMIT_GRAPH_WRITE_APPEND ? 1 : 0;
+ ctx->report_progress = flags & COMMIT_GRAPH_WRITE_PROGRESS ? 1 : 0;
+ ctx->split = flags & COMMIT_GRAPH_WRITE_SPLIT ? 1 : 0;
+ ctx->check_oids = flags & COMMIT_GRAPH_WRITE_CHECK_OIDS ? 1 : 0;
ctx->split_opts = split_opts;
if (ctx->split) {
@@ -1830,8 +1847,10 @@ int write_commit_graph(const char *obj_dir,
goto cleanup;
}
- if (commit_hex)
- fill_oids_from_commit_hex(ctx, commit_hex);
+ if (commit_hex) {
+ if ((res = fill_oids_from_commit_hex(ctx, commit_hex)))
+ goto cleanup;
+ }
if (!pack_indexes && !commit_hex)
fill_oids_from_all_packs(ctx);
@@ -1986,8 +2005,10 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
if (verify_commit_graph_error & ~VERIFY_COMMIT_GRAPH_ERROR_HASH)
return verify_commit_graph_error;
- progress = start_progress(_("Verifying commits in commit graph"),
- g->num_commits);
+ if (flags & COMMIT_GRAPH_WRITE_PROGRESS)
+ progress = start_progress(_("Verifying commits in commit graph"),
+ g->num_commits);
+
for (i = 0; i < g->num_commits; i++) {
struct commit *graph_commit, *odb_commit;
struct commit_list *graph_parents, *odb_parents;
@@ -2095,3 +2116,8 @@ void free_commit_graph(struct commit_graph *g)
free(g->filename);
free(g);
}
+
+void disable_commit_graph(struct repository *r)
+{
+ r->commit_graph_disabled = 1;
+}
diff --git a/commit-graph.h b/commit-graph.h
index df9a3b2..7f5c933 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -71,9 +71,13 @@ struct commit_graph *parse_commit_graph(void *graph_map, int fd,
*/
int generation_numbers_enabled(struct repository *r);
-#define COMMIT_GRAPH_APPEND (1 << 0)
-#define COMMIT_GRAPH_PROGRESS (1 << 1)
-#define COMMIT_GRAPH_SPLIT (1 << 2)
+enum commit_graph_write_flags {
+ COMMIT_GRAPH_WRITE_APPEND = (1 << 0),
+ COMMIT_GRAPH_WRITE_PROGRESS = (1 << 1),
+ COMMIT_GRAPH_WRITE_SPLIT = (1 << 2),
+ /* Make sure that each OID in the input is a valid commit OID. */
+ COMMIT_GRAPH_WRITE_CHECK_OIDS = (1 << 3)
+};
struct split_commit_graph_opts {
int size_multiple;
@@ -87,12 +91,13 @@ struct split_commit_graph_opts {
* is not compatible with the commit-graph feature, then the
* methods will return 0 without writing a commit-graph.
*/
-int write_commit_graph_reachable(const char *obj_dir, unsigned int flags,
+int write_commit_graph_reachable(const char *obj_dir,
+ enum commit_graph_write_flags flags,
const struct split_commit_graph_opts *split_opts);
int write_commit_graph(const char *obj_dir,
struct string_list *pack_indexes,
struct string_list *commit_hex,
- unsigned int flags,
+ enum commit_graph_write_flags flags,
const struct split_commit_graph_opts *split_opts);
#define COMMIT_GRAPH_VERIFY_SHALLOW (1 << 0)
@@ -102,4 +107,10 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
void close_commit_graph(struct raw_object_store *);
void free_commit_graph(struct commit_graph *);
+/*
+ * Disable further use of the commit graph in this process when parsing a
+ * "struct commit".
+ */
+void disable_commit_graph(struct repository *r);
+
#endif
diff --git a/commit-reach.c b/commit-reach.c
index 3ea1747..4ca7e70 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -10,7 +10,6 @@
#include "commit-reach.h"
/* Remember to update object flag allocation in object.h */
-#define REACHABLE (1u<<15)
#define PARENT1 (1u<<16)
#define PARENT2 (1u<<17)
#define STALE (1u<<18)
diff --git a/commit.c b/commit.c
index a98de16..52036bc 100644
--- a/commit.c
+++ b/commit.c
@@ -19,6 +19,7 @@
#include "advice.h"
#include "refs.h"
#include "commit-reach.h"
+#include "run-command.h"
static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
@@ -358,14 +359,15 @@ struct tree *repo_get_commit_tree(struct repository *r,
struct object_id *get_commit_tree_oid(const struct commit *commit)
{
- return &get_commit_tree(commit)->object.oid;
+ struct tree *tree = get_commit_tree(commit);
+ return tree ? &tree->object.oid : NULL;
}
void release_commit_memory(struct parsed_object_pool *pool, struct commit *c)
{
set_commit_tree(c, NULL);
- c->index = 0;
free_commit_buffer(pool, c);
+ c->index = 0;
free_commit_list(c->parents);
c->object.parsed = 0;
@@ -1580,3 +1582,26 @@ size_t ignore_non_trailer(const char *buf, size_t len)
}
return boc ? len - boc : len - cutoff;
}
+
+int run_commit_hook(int editor_is_used, const char *index_file,
+ const char *name, ...)
+{
+ struct argv_array hook_env = ARGV_ARRAY_INIT;
+ va_list args;
+ int ret;
+
+ argv_array_pushf(&hook_env, "GIT_INDEX_FILE=%s", index_file);
+
+ /*
+ * Let the hook know that no editor will be launched.
+ */
+ if (!editor_is_used)
+ argv_array_push(&hook_env, "GIT_EDITOR=:");
+
+ va_start(args, name);
+ ret = run_hook_ve(hook_env.argv,name, args);
+ va_end(args);
+ argv_array_clear(&hook_env);
+
+ return ret;
+}
diff --git a/common-main.c b/common-main.c
index 582a7b1..71e21dd 100644
--- a/common-main.c
+++ b/common-main.c
@@ -39,16 +39,16 @@ int main(int argc, const char **argv)
git_resolve_executable_dir(argv[0]);
- trace2_initialize();
- trace2_cmd_start(argv);
- trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);
-
git_setup_gettext();
initialize_the_repository();
attr_start();
+ trace2_initialize();
+ trace2_cmd_start(argv);
+ trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);
+
result = cmd_main(argc, argv);
trace2_cmd_exit(result);
diff --git a/compat/mingw.c b/compat/mingw.c
index 738f0a8..fe60923 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -363,6 +363,8 @@ static inline int needs_hiding(const char *path)
/* ignore trailing slashes */
if (*path)
basename = path;
+ else
+ break;
}
if (hide_dotfiles == HIDE_DOTFILES_TRUE)
@@ -1161,14 +1163,21 @@ static char *lookup_prog(const char *dir, int dirlen, const char *cmd,
int isexe, int exe_only)
{
char path[MAX_PATH];
+ wchar_t wpath[MAX_PATH];
snprintf(path, sizeof(path), "%.*s\\%s.exe", dirlen, dir, cmd);
- if (!isexe && access(path, F_OK) == 0)
+ if (xutftowcs_path(wpath, path) < 0)
+ return NULL;
+
+ if (!isexe && _waccess(wpath, F_OK) == 0)
return xstrdup(path);
- path[strlen(path)-4] = '\0';
- if ((!exe_only || isexe) && access(path, F_OK) == 0)
- if (!(GetFileAttributes(path) & FILE_ATTRIBUTE_DIRECTORY))
+ wpath[wcslen(wpath)-4] = '\0';
+ if ((!exe_only || isexe) && _waccess(wpath, F_OK) == 0) {
+ if (!(GetFileAttributesW(wpath) & FILE_ATTRIBUTE_DIRECTORY)) {
+ path[strlen(path)-4] = '\0';
return xstrdup(path);
+ }
+ }
return NULL;
}
@@ -1229,11 +1238,6 @@ static int wenvcmp(const void *a, const void *b)
return _wcsnicmp(p, q, p_len);
}
-/* We need a stable sort to convert the environment between UTF-16 <-> UTF-8 */
-#ifndef INTERNAL_QSORT
-#include "qsort.c"
-#endif
-
/*
* Build an environment block combining the inherited environment
* merged with the given list of settings.
@@ -1265,15 +1269,15 @@ static wchar_t *make_environment_block(char **deltaenv)
}
ALLOC_ARRAY(result, size);
- memcpy(result, wenv, size * sizeof(*wenv));
+ COPY_ARRAY(result, wenv, size);
FreeEnvironmentStringsW(wenv);
return result;
}
/*
* If there is a deltaenv, let's accumulate all keys into `array`,
- * sort them using the stable git_qsort() and then copy, skipping
- * duplicate keys
+ * sort them using the stable git_stable_qsort() and then copy,
+ * skipping duplicate keys
*/
for (p = wenv; p && *p; ) {
ALLOC_GROW(array, nr + 1, alloc);
@@ -1296,7 +1300,7 @@ static wchar_t *make_environment_block(char **deltaenv)
p += wlen + 1;
}
- git_qsort(array, nr, sizeof(*array), wenvcmp);
+ git_stable_qsort(array, nr, sizeof(*array), wenvcmp);
ALLOC_ARRAY(result, size + delta_size);
for (p = result, i = 0; i < nr; i++) {
@@ -1309,7 +1313,7 @@ static wchar_t *make_environment_block(char **deltaenv)
continue;
size = wcslen(array[i]) + 1;
- memcpy(p, array[i], size * sizeof(*p));
+ COPY_ARRAY(p, array[i], size);
p += size;
}
*p = L'\0';
@@ -1663,6 +1667,8 @@ char *mingw_getenv(const char *name)
if (!w_key)
die("Out of memory, (tried to allocate %u wchar_t's)", len_key);
xutftowcs(w_key, name, len_key);
+ /* GetEnvironmentVariableW() only sets the last error upon failure */
+ SetLastError(ERROR_SUCCESS);
len_value = GetEnvironmentVariableW(w_key, w_value, ARRAY_SIZE(w_value));
if (!len_value && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
free(w_key);
diff --git a/compat/mingw.h b/compat/mingw.h
index a03e40e..9ad204c 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -11,7 +11,7 @@ typedef _sigset_t sigset_t;
#undef _POSIX_THREAD_SAFE_FUNCTIONS
#endif
-extern int mingw_core_config(const char *var, const char *value, void *cb);
+int mingw_core_config(const char *var, const char *value, void *cb);
#define platform_core_config mingw_core_config
/*
@@ -443,7 +443,7 @@ static inline void convert_slashes(char *path)
*path = '/';
}
#define PATH_SEP ';'
-extern char *mingw_query_user_email(void);
+char *mingw_query_user_email(void);
#define query_user_email mingw_query_user_email
#if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800)
#define PRIuMAX "I64u"
@@ -580,4 +580,4 @@ int main(int argc, const char **argv);
/*
* Used by Pthread API implementation for Windows
*/
-extern int err_win_to_posix(DWORD winerr);
+int err_win_to_posix(DWORD winerr);
diff --git a/compat/nedmalloc/malloc.c.h b/compat/nedmalloc/malloc.c.h
index b833ff9..9134349 100644
--- a/compat/nedmalloc/malloc.c.h
+++ b/compat/nedmalloc/malloc.c.h
@@ -1755,10 +1755,10 @@ static FORCEINLINE void pthread_release_lock (MLOCK_T *sl) {
assert(sl->l != 0);
assert(sl->threadid == CURRENT_THREAD);
if (--sl->c == 0) {
- sl->threadid = 0;
volatile unsigned int* lp = &sl->l;
int prev = 0;
int ret;
+ sl->threadid = 0;
__asm__ __volatile__ ("lock; xchgl %0, %1"
: "=r" (ret)
: "m" (*(lp)), "0"(prev)
@@ -3066,7 +3066,7 @@ static int init_mparams(void) {
#if !ONLY_MSPACES
/* Set up lock for main malloc area */
gm->mflags = mparams.default_mflags;
- INITIAL_LOCK(&gm->mutex);
+ (void)INITIAL_LOCK(&gm->mutex);
#endif
#if (FOOTERS && !INSECURE)
@@ -5017,7 +5017,7 @@ static mstate init_user_mstate(char* tbase, size_t tsize) {
mchunkptr msp = align_as_chunk(tbase);
mstate m = (mstate)(chunk2mem(msp));
memset(m, 0, msize);
- INITIAL_LOCK(&m->mutex);
+ (void)INITIAL_LOCK(&m->mutex);
msp->head = (msize|PINUSE_BIT|CINUSE_BIT);
m->seg.base = m->least_addr = tbase;
m->seg.size = m->footprint = m->max_footprint = tsize;
diff --git a/compat/vcbuild/scripts/clink.pl b/compat/vcbuild/scripts/clink.pl
index c7b021b..ec95a3b 100755
--- a/compat/vcbuild/scripts/clink.pl
+++ b/compat/vcbuild/scripts/clink.pl
@@ -68,8 +68,54 @@ while (@ARGV) {
} elsif ("$arg" =~ /^-L/ && "$arg" ne "-LTCG") {
$arg =~ s/^-L/-LIBPATH:/;
push(@lflags, $arg);
- } elsif ("$arg" =~ /^-R/) {
+ } elsif ("$arg" =~ /^-[Rl]/) {
# eat
+ } elsif ("$arg" eq "-Werror") {
+ push(@cflags, "-WX");
+ } elsif ("$arg" eq "-Wall") {
+ # cl.exe understands -Wall, but it is really overzealous
+ push(@cflags, "-W4");
+ # disable the "signed/unsigned mismatch" warnings; our source code violates that
+ push(@cflags, "-wd4018");
+ push(@cflags, "-wd4245");
+ push(@cflags, "-wd4389");
+ # disable the "unreferenced formal parameter" warning; our source code violates that
+ push(@cflags, "-wd4100");
+ # disable the "conditional expression is constant" warning; our source code violates that
+ push(@cflags, "-wd4127");
+ # disable the "const object should be initialized" warning; these warnings affect only objects that are `static`
+ push(@cflags, "-wd4132");
+ # disable the "function/data pointer conversion in expression" warning; our source code violates that
+ push(@cflags, "-wd4152");
+ # disable the "non-constant aggregate initializer" warning; our source code violates that
+ push(@cflags, "-wd4204");
+ # disable the "cannot be initialized using address of automatic variable" warning; our source code violates that
+ push(@cflags, "-wd4221");
+ # disable the "possible loss of data" warnings; our source code violates that
+ push(@cflags, "-wd4244");
+ push(@cflags, "-wd4267");
+ # disable the "array is too small to include a terminating null character" warning; we ab-use strings to initialize OIDs
+ push(@cflags, "-wd4295");
+ # disable the "'<<': result of 32-bit shift implicitly converted to 64 bits" warning; our source code violates that
+ push(@cflags, "-wd4334");
+ # disable the "declaration hides previous local declaration" warning; our source code violates that
+ push(@cflags, "-wd4456");
+ # disable the "declaration hides function parameter" warning; our source code violates that
+ push(@cflags, "-wd4457");
+ # disable the "declaration hides global declaration" warning; our source code violates that
+ push(@cflags, "-wd4459");
+ # disable the "potentially uninitialized local variable '<name>' used" warning; our source code violates that
+ push(@cflags, "-wd4701");
+ # disable the "unreachable code" warning; our source code violates that
+ push(@cflags, "-wd4702");
+ # disable the "potentially uninitialized local pointer variable used" warning; our source code violates that
+ push(@cflags, "-wd4703");
+ # disable the "assignment within conditional expression" warning; our source code violates that
+ push(@cflags, "-wd4706");
+ # disable the "'inet_ntoa': Use inet_ntop() or InetNtop() instead" warning; our source code violates that
+ push(@cflags, "-wd4996");
+ } elsif ("$arg" =~ /^-W[a-z]/) {
+ # let's ignore those
} else {
push(@args, $arg);
}
diff --git a/compat/win32/path-utils.h b/compat/win32/path-utils.h
index 0f70d43..8ed062a 100644
--- a/compat/win32/path-utils.h
+++ b/compat/win32/path-utils.h
@@ -1,3 +1,6 @@
+#ifndef WIN32_PATH_UTILS_H
+#define WIN32_PATH_UTILS_H
+
#define has_dos_drive_prefix(path) \
(isalpha(*(path)) && (path)[1] == ':' ? 2 : 0)
int win32_skip_dos_drive_prefix(char **path);
@@ -18,3 +21,5 @@ static inline char *win32_find_last_dir_sep(const char *path)
#define find_last_dir_sep win32_find_last_dir_sep
int win32_offset_1st_component(const char *path);
#define offset_1st_component win32_offset_1st_component
+
+#endif
diff --git a/compat/win32/pthread.h b/compat/win32/pthread.h
index c6cb8dd..737983d 100644
--- a/compat/win32/pthread.h
+++ b/compat/win32/pthread.h
@@ -50,8 +50,8 @@ typedef struct {
DWORD tid;
} pthread_t;
-extern int pthread_create(pthread_t *thread, const void *unused,
- void *(*start_routine)(void*), void *arg);
+int pthread_create(pthread_t *thread, const void *unused,
+ void *(*start_routine)(void*), void *arg);
/*
* To avoid the need of copying a struct, we use small macro wrapper to pass
@@ -59,10 +59,10 @@ extern int pthread_create(pthread_t *thread, const void *unused,
*/
#define pthread_join(a, b) win32_pthread_join(&(a), (b))
-extern int win32_pthread_join(pthread_t *thread, void **value_ptr);
+int win32_pthread_join(pthread_t *thread, void **value_ptr);
#define pthread_equal(t1, t2) ((t1).tid == (t2).tid)
-extern pthread_t pthread_self(void);
+pthread_t pthread_self(void);
static inline void NORETURN pthread_exit(void *ret)
{
diff --git a/compat/winansi.c b/compat/winansi.c
index cacd82c..54fd701 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -546,7 +546,7 @@ static HANDLE swap_osfhnd(int fd, HANDLE new_handle)
typedef struct _OBJECT_NAME_INFORMATION
{
UNICODE_STRING Name;
- WCHAR NameBuffer[0];
+ WCHAR NameBuffer[FLEX_ARRAY];
} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
#define ObjectNameInformation 1
diff --git a/config.c b/config.c
index 3900e49..e7052b3 100644
--- a/config.c
+++ b/config.c
@@ -275,7 +275,7 @@ static int include_by_branch(const char *cond, size_t cond_len)
int flags;
int ret;
struct strbuf pattern = STRBUF_INIT;
- const char *refname = !the_repository || !the_repository->gitdir ?
+ const char *refname = !the_repository->gitdir ?
NULL : resolve_ref_unsafe("HEAD", 0, NULL, &flags);
const char *shortname;
@@ -1204,7 +1204,7 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
default_abbrev = -1;
else {
int abbrev = git_config_int(var, value);
- if (abbrev < minimum_abbrev || abbrev > 40)
+ if (abbrev < minimum_abbrev || abbrev > the_hash_algo->hexsz)
return error(_("abbrev length out of range: %d"), abbrev);
default_abbrev = abbrev;
}
@@ -1379,11 +1379,6 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
return 0;
}
- if (!strcmp(var, "core.partialclonefilter")) {
- return git_config_string(&core_partial_clone_filter_default,
- var, value);
- }
-
if (!strcmp(var, "core.usereplacerefs")) {
read_replace_refs = git_config_bool(var, value);
return 0;
@@ -1861,9 +1856,9 @@ static struct config_set_element *configset_find_element(struct config_set *cs,
if (git_config_parse_key(key, &normalized_key, NULL))
return NULL;
- hashmap_entry_init(&k, strhash(normalized_key));
+ hashmap_entry_init(&k.ent, strhash(normalized_key));
k.key = normalized_key;
- found_entry = hashmap_get(&cs->config_hash, &k, NULL);
+ found_entry = hashmap_get_entry(&cs->config_hash, &k, ent, NULL);
free(normalized_key);
return found_entry;
}
@@ -1882,10 +1877,10 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
*/
if (!e) {
e = xmalloc(sizeof(*e));
- hashmap_entry_init(e, strhash(key));
+ hashmap_entry_init(&e->ent, strhash(key));
e->key = xstrdup(key);
string_list_init(&e->value_list, 1);
- hashmap_add(&cs->config_hash, e);
+ hashmap_add(&cs->config_hash, &e->ent);
}
si = string_list_append_nodup(&e->value_list, xstrdup_or_null(value));
@@ -1913,12 +1908,14 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha
}
static int config_set_element_cmp(const void *unused_cmp_data,
- const void *entry,
- const void *entry_or_key,
+ const struct hashmap_entry *eptr,
+ const struct hashmap_entry *entry_or_key,
const void *unused_keydata)
{
- const struct config_set_element *e1 = entry;
- const struct config_set_element *e2 = entry_or_key;
+ const struct config_set_element *e1, *e2;
+
+ e1 = container_of(eptr, const struct config_set_element, ent);
+ e2 = container_of(entry_or_key, const struct config_set_element, ent);
return strcmp(e1->key, e2->key);
}
@@ -1939,12 +1936,12 @@ void git_configset_clear(struct config_set *cs)
if (!cs->hash_initialized)
return;
- hashmap_iter_init(&cs->config_hash, &iter);
- while ((entry = hashmap_iter_next(&iter))) {
+ hashmap_for_each_entry(&cs->config_hash, &iter, entry,
+ ent /* member name */) {
free(entry->key);
string_list_clear(&entry->value_list, 1);
}
- hashmap_free(&cs->config_hash, 1);
+ hashmap_free_entries(&cs->config_hash, struct config_set_element, ent);
cs->hash_initialized = 0;
free(cs->list.items);
cs->list.nr = 0;
@@ -2288,30 +2285,6 @@ int git_config_get_expiry_in_days(const char *key, timestamp_t *expiry, timestam
return -1; /* thing exists but cannot be parsed */
}
-int git_config_get_untracked_cache(void)
-{
- int val = -1;
- const char *v;
-
- /* Hack for test programs like test-dump-untracked-cache */
- if (ignore_untracked_cache_config)
- return -1;
-
- if (!git_config_get_maybe_bool("core.untrackedcache", &val))
- return val;
-
- if (!git_config_get_value("core.untrackedcache", &v)) {
- if (!strcasecmp(v, "keep"))
- return -1;
-
- error(_("unknown core.untrackedCache value '%s'; "
- "using 'keep' default value"), v);
- return -1;
- }
-
- return -1; /* default value */
-}
-
int git_config_get_split_index(void)
{
int val;
diff --git a/config.mak.uname b/config.mak.uname
index db7f06b..cc8efd9 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -703,20 +703,24 @@ vcxproj:
perl contrib/buildsystems/generate -g Vcxproj
git add -f git.sln {*,*/lib,t/helper/*}/*.vcxproj
- # Generate the LinkOrCopyBuiltins.targets file
+ # Generate the LinkOrCopyBuiltins.targets and LinkOrCopyRemoteHttp.targets file
(echo '<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">' && \
echo ' <Target Name="CopyBuiltins_AfterBuild" AfterTargets="AfterBuild">' && \
for name in $(BUILT_INS);\
do \
echo ' <Copy SourceFiles="$$(OutDir)\git.exe" DestinationFiles="$$(OutDir)\'"$$name"'" SkipUnchangedFiles="true" UseHardlinksIfPossible="true" />'; \
done && \
+ echo ' </Target>' && \
+ echo '</Project>') >git/LinkOrCopyBuiltins.targets
+ (echo '<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">' && \
+ echo ' <Target Name="CopyBuiltins_AfterBuild" AfterTargets="AfterBuild">' && \
for name in $(REMOTE_CURL_ALIASES); \
do \
echo ' <Copy SourceFiles="$$(OutDir)\'"$(REMOTE_CURL_PRIMARY)"'" DestinationFiles="$$(OutDir)\'"$$name"'" SkipUnchangedFiles="true" UseHardlinksIfPossible="true" />'; \
done && \
echo ' </Target>' && \
- echo '</Project>') >git/LinkOrCopyBuiltins.targets
- git add -f git/LinkOrCopyBuiltins.targets
+ echo '</Project>') >git-remote-http/LinkOrCopyRemoteHttp.targets
+ git add -f git/LinkOrCopyBuiltins.targets git-remote-http/LinkOrCopyRemoteHttp.targets
# Add command-list.h
$(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 command-list.h
@@ -724,11 +728,10 @@ vcxproj:
# Add scripts
rm -f perl/perl.mak
- $(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 \
- $(SCRIPT_LIB) $(SCRIPT_SH_GEN) $(SCRIPT_PERL_GEN)
+ $(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 $(SCRIPT_LIB) $(SCRIPTS)
# Strip out the sane tool path, needed only for building
sed -i '/^git_broken_path_fix ".*/d' git-sh-setup
- git add -f $(SCRIPT_LIB) $(SCRIPT_SH_GEN) $(SCRIPT_PERL_GEN)
+ git add -f $(SCRIPT_LIB) $(SCRIPTS)
# Add Perl module
$(MAKE) $(LIB_PERL_GEN)
@@ -758,6 +761,10 @@ vcxproj:
$(MAKE) -C templates
git add -f templates/boilerplates.made templates/blt/
+ # Add the translated messages
+ make MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 $(MOFILES)
+ git add -f $(MOFILES)
+
# Add build options
$(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 GIT-BUILD-OPTIONS
git add -f GIT-BUILD-OPTIONS
diff --git a/connect.c b/connect.c
index 2778481..da7daa2 100644
--- a/connect.c
+++ b/connect.c
@@ -915,6 +915,10 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host,
if (protocol == PROTO_LOCAL)
path = end;
+ else if (protocol == PROTO_FILE && *host != '/' &&
+ !has_dos_drive_prefix(host) &&
+ offset_1st_component(host - 2) > 1)
+ path = host - 2; /* include the leading "//" */
else if (protocol == PROTO_FILE && has_dos_drive_prefix(end))
path = end; /* "file://$(pwd)" may be "file://C:/projects/repo" */
else
diff --git a/connected.c b/connected.c
index cd9b324..36c4e5d 100644
--- a/connected.c
+++ b/connected.c
@@ -5,6 +5,7 @@
#include "connected.h"
#include "transport.h"
#include "packfile.h"
+#include "promisor-remote.h"
/*
* If we feed all the commits we want to verify to this command
@@ -28,6 +29,7 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
struct packed_git *new_pack = NULL;
struct transport *transport;
size_t base_len;
+ const unsigned hexsz = the_hash_algo->hexsz;
if (!opt)
opt = &defaults;
@@ -73,7 +75,7 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
argv_array_push(&rev_list.args,"rev-list");
argv_array_push(&rev_list.args, "--objects");
argv_array_push(&rev_list.args, "--stdin");
- if (repository_format_partial_clone)
+ if (has_promisor_remote())
argv_array_push(&rev_list.args, "--exclude-promisor-objects");
if (!opt->is_deepening_fetch) {
argv_array_push(&rev_list.args, "--not");
@@ -99,7 +101,7 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
sigchain_push(SIGPIPE, SIG_IGN);
- commit[GIT_SHA1_HEXSZ] = '\n';
+ commit[hexsz] = '\n';
do {
/*
* If index-pack already checked that:
@@ -112,8 +114,8 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
if (new_pack && find_pack_entry_one(oid.hash, new_pack))
continue;
- memcpy(commit, oid_to_hex(&oid), GIT_SHA1_HEXSZ);
- if (write_in_full(rev_list.in, commit, GIT_SHA1_HEXSZ + 1) < 0) {
+ memcpy(commit, oid_to_hex(&oid), hexsz);
+ if (write_in_full(rev_list.in, commit, hexsz + 1) < 0) {
if (errno != EPIPE && errno != EINVAL)
error_errno(_("failed write to rev-list"));
err = -1;
diff --git a/contrib/buildsystems/Generators/Vcxproj.pm b/contrib/buildsystems/Generators/Vcxproj.pm
index 576ccab..5c666f9 100644
--- a/contrib/buildsystems/Generators/Vcxproj.pm
+++ b/contrib/buildsystems/Generators/Vcxproj.pm
@@ -79,7 +79,8 @@ sub createProject {
if (!$static_library) {
$libs_release = join(";", sort(grep /^(?!libgit\.lib|xdiff\/lib\.lib|vcs-svn\/lib\.lib)/, @{$$build_structure{"$prefix${name}_LIBS"}}));
$libs_debug = $libs_release;
- $libs_debug =~ s/zlib\.lib/zlibd\.lib/;
+ $libs_debug =~ s/zlib\.lib/zlibd\.lib/g;
+ $libs_debug =~ s/libcurl\.lib/libcurl-d\.lib/g;
}
$defines =~ s/-D//g;
@@ -119,13 +120,13 @@ sub createProject {
<VCPKGArch Condition="'\$(Platform)'=='Win32'">x86-windows</VCPKGArch>
<VCPKGArch Condition="'\$(Platform)'!='Win32'">x64-windows</VCPKGArch>
<VCPKGArchDirectory>$cdup\\compat\\vcbuild\\vcpkg\\installed\\\$(VCPKGArch)</VCPKGArchDirectory>
- <VCPKGBinDirectory Condition="'\(Configuration)'=='Debug'">\$(VCPKGArchDirectory)\\debug\\bin</VCPKGBinDirectory>
- <VCPKGLibDirectory Condition="'\(Configuration)'=='Debug'">\$(VCPKGArchDirectory)\\debug\\lib</VCPKGLibDirectory>
- <VCPKGBinDirectory Condition="'\(Configuration)'!='Debug'">\$(VCPKGArchDirectory)\\bin</VCPKGBinDirectory>
- <VCPKGLibDirectory Condition="'\(Configuration)'!='Debug'">\$(VCPKGArchDirectory)\\lib</VCPKGLibDirectory>
+ <VCPKGBinDirectory Condition="'\$(Configuration)'=='Debug'">\$(VCPKGArchDirectory)\\debug\\bin</VCPKGBinDirectory>
+ <VCPKGLibDirectory Condition="'\$(Configuration)'=='Debug'">\$(VCPKGArchDirectory)\\debug\\lib</VCPKGLibDirectory>
+ <VCPKGBinDirectory Condition="'\$(Configuration)'!='Debug'">\$(VCPKGArchDirectory)\\bin</VCPKGBinDirectory>
+ <VCPKGLibDirectory Condition="'\$(Configuration)'!='Debug'">\$(VCPKGArchDirectory)\\lib</VCPKGLibDirectory>
<VCPKGIncludeDirectory>\$(VCPKGArchDirectory)\\include</VCPKGIncludeDirectory>
- <VCPKGLibs Condition="'\(Configuration)'=='Debug'">$libs_debug</VCPKGLibs>
- <VCPKGLibs Condition="'\(Configuration)'!='Debug'">$libs_release</VCPKGLibs>
+ <VCPKGLibs Condition="'\$(Configuration)'=='Debug'">$libs_debug</VCPKGLibs>
+ <VCPKGLibs Condition="'\$(Configuration)'!='Debug'">$libs_release</VCPKGLibs>
</PropertyGroup>
<Import Project="\$(VCTargetsPath)\\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'\$(Configuration)'=='Debug'" Label="Configuration">
@@ -277,6 +278,9 @@ EOM
if ($target eq 'git') {
print F " <Import Project=\"LinkOrCopyBuiltins.targets\" />\n";
}
+ if ($target eq 'git-remote-http') {
+ print F " <Import Project=\"LinkOrCopyRemoteHttp.targets\" />\n";
+ }
print F << "EOM";
</Project>
EOM
diff --git a/contrib/coccinelle/hashmap.cocci b/contrib/coccinelle/hashmap.cocci
new file mode 100644
index 0000000..d69e120
--- /dev/null
+++ b/contrib/coccinelle/hashmap.cocci
@@ -0,0 +1,16 @@
+@ hashmap_entry_init_usage @
+expression E;
+struct hashmap_entry HME;
+@@
+- HME.hash = E;
++ hashmap_entry_init(&HME, E);
+
+@@
+identifier f !~ "^hashmap_entry_init$";
+expression E;
+struct hashmap_entry *HMEP;
+@@
+ f(...) {<...
+- HMEP->hash = E;
++ hashmap_entry_init(HMEP, E);
+ ...>}
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index e087c4b..00fbe6c 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -340,7 +340,7 @@ __gitcomp ()
c="$c${4-}"
if [[ $c == "$cur_"* ]]; then
case $c in
- --*=*|*.) ;;
+ --*=|*.) ;;
*) c="$c " ;;
esac
COMPREPLY[i++]="${2-}$c"
@@ -360,7 +360,7 @@ __gitcomp ()
c="$c${4-}"
if [[ $c == "$cur_"* ]]; then
case $c in
- --*=*|*.) ;;
+ *=|*.) ;;
*) c="$c " ;;
esac
COMPREPLY[i++]="${2-}$c"
@@ -524,7 +524,7 @@ __git_index_files ()
# Even when a directory name itself does not contain
# any special characters, it will still be quoted if
# any of its (stripped) trailing path components do.
- # Because of this we may have seen the same direcory
+ # Because of this we may have seen the same directory
# both quoted and unquoted.
if (p in paths)
# We have seen the same directory unquoted,
@@ -1250,10 +1250,7 @@ _git_archive ()
return
;;
--*)
- __gitcomp "
- --format= --list --verbose
- --prefix= --remote= --exec= --output
- "
+ __gitcomp_builtin archive "--format= --list --verbose --prefix= --worktree-attributes"
return
;;
esac
@@ -1361,7 +1358,9 @@ _git_checkout ()
esac
}
-__git_cherry_pick_inprogress_options="--continue --quit --abort"
+__git_sequencer_inprogress_options="--continue --quit --abort --skip"
+
+__git_cherry_pick_inprogress_options=$__git_sequencer_inprogress_options
_git_cherry_pick ()
{
@@ -1399,7 +1398,18 @@ _git_clean ()
_git_clone ()
{
+ case "$prev" in
+ -c|--config)
+ __git_complete_config_variable_name_and_value
+ return
+ ;;
+ esac
case "$cur" in
+ --config=*)
+ __git_complete_config_variable_name_and_value \
+ --cur="${cur##--config=}"
+ return
+ ;;
--*)
__gitcomp_builtin clone
return
@@ -1476,6 +1486,8 @@ __git_diff_common_options="--stat --numstat --shortstat --summary
--dirstat-by-file= --cumulative
--diff-algorithm=
--submodule --submodule= --ignore-submodules
+ --indent-heuristic --no-indent-heuristic
+ --textconv --no-textconv
"
_git_diff ()
@@ -1784,6 +1796,10 @@ _git_log ()
__gitcomp "$__git_diff_submodule_formats" "" "${cur##--submodule=}"
return
;;
+ --no-walk=*)
+ __gitcomp "sorted unsorted" "" "${cur##--no-walk=}"
+ return
+ ;;
--*)
__gitcomp "
$__git_log_common_options
@@ -1791,16 +1807,19 @@ _git_log ()
$__git_log_gitk_options
--root --topo-order --date-order --reverse
--follow --full-diff
- --abbrev-commit --abbrev=
+ --abbrev-commit --no-abbrev-commit --abbrev=
--relative-date --date=
--pretty= --format= --oneline
--show-signature
--cherry-mark
--cherry-pick
--graph
- --decorate --decorate=
+ --decorate --decorate= --no-decorate
--walk-reflogs
+ --no-walk --no-walk= --do-walk
--parents --children
+ --expand-tabs --expand-tabs= --no-expand-tabs
+ --patch
$merge
$__git_diff_common_options
--pickaxe-all --pickaxe-regex
@@ -2004,15 +2023,18 @@ _git_range_diff ()
__git_complete_revlist
}
+__git_rebase_inprogress_options="--continue --skip --abort --quit --show-current-patch"
+__git_rebase_interactive_inprogress_options="$__git_rebase_inprogress_options --edit-todo"
+
_git_rebase ()
{
__git_find_repo_path
if [ -f "$__git_repo_path"/rebase-merge/interactive ]; then
- __gitcomp "--continue --skip --abort --quit --edit-todo --show-current-patch"
+ __gitcomp "$__git_rebase_interactive_inprogress_options"
return
elif [ -d "$__git_repo_path"/rebase-apply ] || \
[ -d "$__git_repo_path"/rebase-merge ]; then
- __gitcomp "--continue --skip --abort --quit --show-current-patch"
+ __gitcomp "$__git_rebase_inprogress_options"
return
fi
__git_complete_strategy && return
@@ -2022,19 +2044,8 @@ _git_rebase ()
return
;;
--*)
- __gitcomp "
- --onto --merge --strategy --interactive
- --rebase-merges --preserve-merges --stat --no-stat
- --committer-date-is-author-date --ignore-date
- --ignore-whitespace --whitespace=
- --autosquash --no-autosquash
- --fork-point --no-fork-point
- --autostash --no-autostash
- --verify --no-verify
- --keep-empty --root --force-rebase --no-ff
- --rerere-autoupdate
- --exec
- "
+ __gitcomp_builtin rebase "" \
+ "$__git_rebase_interactive_inprogress_options"
return
esac
@@ -2225,181 +2236,282 @@ __git_config_vars=
__git_compute_config_vars ()
{
test -n "$__git_config_vars" ||
- __git_config_vars="$(git help --config-for-completion | sort | uniq)"
+ __git_config_vars="$(git help --config-for-completion | sort -u)"
}
-_git_config ()
+# Completes possible values of various configuration variables.
+#
+# Usage: __git_complete_config_variable_value [<option>]...
+# --varname=<word>: The name of the configuration variable whose value is
+# to be completed. Defaults to the previous word on the
+# command line.
+# --cur=<word>: The current value to be completed. Defaults to the current
+# word to be completed.
+__git_complete_config_variable_value ()
{
- local varname
+ local varname="$prev" cur_="$cur"
+
+ while test $# != 0; do
+ case "$1" in
+ --varname=*) varname="${1##--varname=}" ;;
+ --cur=*) cur_="${1##--cur=}" ;;
+ *) return 1 ;;
+ esac
+ shift
+ done
if [ "${BASH_VERSINFO[0]:-0}" -ge 4 ]; then
- varname="${prev,,}"
+ varname="${varname,,}"
else
- varname="$(echo "$prev" |tr A-Z a-z)"
+ varname="$(echo "$varname" |tr A-Z a-z)"
fi
case "$varname" in
branch.*.remote|branch.*.pushremote)
- __gitcomp_nl "$(__git_remotes)"
+ __gitcomp_nl "$(__git_remotes)" "" "$cur_"
return
;;
branch.*.merge)
- __git_complete_refs
+ __git_complete_refs --cur="$cur_"
return
;;
branch.*.rebase)
- __gitcomp "false true merges preserve interactive"
+ __gitcomp "false true merges preserve interactive" "" "$cur_"
return
;;
remote.pushdefault)
- __gitcomp_nl "$(__git_remotes)"
+ __gitcomp_nl "$(__git_remotes)" "" "$cur_"
return
;;
remote.*.fetch)
- local remote="${prev#remote.}"
+ local remote="${varname#remote.}"
remote="${remote%.fetch}"
- if [ -z "$cur" ]; then
+ if [ -z "$cur_" ]; then
__gitcomp_nl "refs/heads/" "" "" ""
return
fi
- __gitcomp_nl "$(__git_refs_remotes "$remote")"
+ __gitcomp_nl "$(__git_refs_remotes "$remote")" "" "$cur_"
return
;;
remote.*.push)
- local remote="${prev#remote.}"
+ local remote="${varname#remote.}"
remote="${remote%.push}"
__gitcomp_nl "$(__git for-each-ref \
- --format='%(refname):%(refname)' refs/heads)"
+ --format='%(refname):%(refname)' refs/heads)" "" "$cur_"
return
;;
pull.twohead|pull.octopus)
__git_compute_merge_strategies
- __gitcomp "$__git_merge_strategies"
- return
- ;;
- color.branch|color.diff|color.interactive|\
- color.showbranch|color.status|color.ui)
- __gitcomp "always never auto"
+ __gitcomp "$__git_merge_strategies" "" "$cur_"
return
;;
color.pager)
- __gitcomp "false true"
+ __gitcomp "false true" "" "$cur_"
return
;;
color.*.*)
__gitcomp "
normal black red green yellow blue magenta cyan white
bold dim ul blink reverse
- "
+ " "" "$cur_"
+ return
+ ;;
+ color.*)
+ __gitcomp "false true always never auto" "" "$cur_"
return
;;
diff.submodule)
- __gitcomp "$__git_diff_submodule_formats"
+ __gitcomp "$__git_diff_submodule_formats" "" "$cur_"
return
;;
help.format)
- __gitcomp "man info web html"
+ __gitcomp "man info web html" "" "$cur_"
return
;;
log.date)
- __gitcomp "$__git_log_date_formats"
+ __gitcomp "$__git_log_date_formats" "" "$cur_"
return
;;
sendemail.aliasfiletype)
- __gitcomp "mutt mailrc pine elm gnus"
+ __gitcomp "mutt mailrc pine elm gnus" "" "$cur_"
return
;;
sendemail.confirm)
- __gitcomp "$__git_send_email_confirm_options"
+ __gitcomp "$__git_send_email_confirm_options" "" "$cur_"
return
;;
sendemail.suppresscc)
- __gitcomp "$__git_send_email_suppresscc_options"
+ __gitcomp "$__git_send_email_suppresscc_options" "" "$cur_"
return
;;
sendemail.transferencoding)
- __gitcomp "7bit 8bit quoted-printable base64"
- return
- ;;
- --get|--get-all|--unset|--unset-all)
- __gitcomp_nl "$(__git_config_get_set_variables)"
+ __gitcomp "7bit 8bit quoted-printable base64" "" "$cur_"
return
;;
*.*)
return
;;
esac
- case "$cur" in
- --*)
- __gitcomp_builtin config
- return
- ;;
+}
+
+# Completes configuration sections, subsections, variable names.
+#
+# Usage: __git_complete_config_variable_name [<option>]...
+# --cur=<word>: The current configuration section/variable name to be
+# completed. Defaults to the current word to be completed.
+# --sfx=<suffix>: A suffix to be appended to each fully completed
+# configuration variable name (but not to sections or
+# subsections) instead of the default space.
+__git_complete_config_variable_name ()
+{
+ local cur_="$cur" sfx
+
+ while test $# != 0; do
+ case "$1" in
+ --cur=*) cur_="${1##--cur=}" ;;
+ --sfx=*) sfx="${1##--sfx=}" ;;
+ *) return 1 ;;
+ esac
+ shift
+ done
+
+ case "$cur_" in
branch.*.*)
- local pfx="${cur%.*}." cur_="${cur##*.}"
- __gitcomp "remote pushRemote merge mergeOptions rebase" "$pfx" "$cur_"
+ local pfx="${cur_%.*}."
+ cur_="${cur_##*.}"
+ __gitcomp "remote pushRemote merge mergeOptions rebase" "$pfx" "$cur_" "$sfx"
return
;;
branch.*)
- local pfx="${cur%.*}." cur_="${cur#*.}"
+ local pfx="${cur%.*}."
+ cur_="${cur#*.}"
__gitcomp_direct "$(__git_heads "$pfx" "$cur_" ".")"
- __gitcomp_nl_append $'autoSetupMerge\nautoSetupRebase\n' "$pfx" "$cur_"
+ __gitcomp_nl_append $'autoSetupMerge\nautoSetupRebase\n' "$pfx" "$cur_" "$sfx"
return
;;
guitool.*.*)
- local pfx="${cur%.*}." cur_="${cur##*.}"
+ local pfx="${cur_%.*}."
+ cur_="${cur_##*.}"
__gitcomp "
argPrompt cmd confirm needsFile noConsole noRescan
prompt revPrompt revUnmerged title
- " "$pfx" "$cur_"
+ " "$pfx" "$cur_" "$sfx"
return
;;
difftool.*.*)
- local pfx="${cur%.*}." cur_="${cur##*.}"
- __gitcomp "cmd path" "$pfx" "$cur_"
+ local pfx="${cur_%.*}."
+ cur_="${cur_##*.}"
+ __gitcomp "cmd path" "$pfx" "$cur_" "$sfx"
return
;;
man.*.*)
- local pfx="${cur%.*}." cur_="${cur##*.}"
- __gitcomp "cmd path" "$pfx" "$cur_"
+ local pfx="${cur_%.*}."
+ cur_="${cur_##*.}"
+ __gitcomp "cmd path" "$pfx" "$cur_" "$sfx"
return
;;
mergetool.*.*)
- local pfx="${cur%.*}." cur_="${cur##*.}"
- __gitcomp "cmd path trustExitCode" "$pfx" "$cur_"
+ local pfx="${cur_%.*}."
+ cur_="${cur_##*.}"
+ __gitcomp "cmd path trustExitCode" "$pfx" "$cur_" "$sfx"
return
;;
pager.*)
- local pfx="${cur%.*}." cur_="${cur#*.}"
+ local pfx="${cur_%.*}."
+ cur_="${cur_#*.}"
__git_compute_all_commands
- __gitcomp_nl "$__git_all_commands" "$pfx" "$cur_"
+ __gitcomp_nl "$__git_all_commands" "$pfx" "$cur_" "$sfx"
return
;;
remote.*.*)
- local pfx="${cur%.*}." cur_="${cur##*.}"
+ local pfx="${cur_%.*}."
+ cur_="${cur_##*.}"
__gitcomp "
url proxy fetch push mirror skipDefaultUpdate
receivepack uploadpack tagOpt pushurl
- " "$pfx" "$cur_"
+ " "$pfx" "$cur_" "$sfx"
return
;;
remote.*)
- local pfx="${cur%.*}." cur_="${cur#*.}"
+ local pfx="${cur_%.*}."
+ cur_="${cur_#*.}"
__gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "."
- __gitcomp_nl_append "pushDefault" "$pfx" "$cur_"
+ __gitcomp_nl_append "pushDefault" "$pfx" "$cur_" "$sfx"
return
;;
url.*.*)
- local pfx="${cur%.*}." cur_="${cur##*.}"
- __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_"
+ local pfx="${cur_%.*}."
+ cur_="${cur_##*.}"
+ __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_" "$sfx"
return
;;
*.*)
__git_compute_config_vars
- __gitcomp "$__git_config_vars"
+ __gitcomp "$__git_config_vars" "" "$cur_" "$sfx"
;;
*)
__git_compute_config_vars
- __gitcomp "$(echo "$__git_config_vars" | sed 's/\.[^ ]*/./g')"
+ __gitcomp "$(echo "$__git_config_vars" |
+ awk -F . '{
+ sections[$1] = 1
+ }
+ END {
+ for (s in sections)
+ print s "."
+ }
+ ')" "" "$cur_"
+ ;;
+ esac
+}
+
+# Completes '='-separated configuration sections/variable names and values
+# for 'git -c section.name=value'.
+#
+# Usage: __git_complete_config_variable_name_and_value [<option>]...
+# --cur=<word>: The current configuration section/variable name/value to be
+# completed. Defaults to the current word to be completed.
+__git_complete_config_variable_name_and_value ()
+{
+ local cur_="$cur"
+
+ while test $# != 0; do
+ case "$1" in
+ --cur=*) cur_="${1##--cur=}" ;;
+ *) return 1 ;;
+ esac
+ shift
+ done
+
+ case "$cur_" in
+ *=*)
+ __git_complete_config_variable_value \
+ --varname="${cur_%%=*}" --cur="${cur_#*=}"
+ ;;
+ *)
+ __git_complete_config_variable_name --cur="$cur_" --sfx='='
+ ;;
+ esac
+}
+
+_git_config ()
+{
+ case "$prev" in
+ --get|--get-all|--unset|--unset-all)
+ __gitcomp_nl "$(__git_config_get_set_variables)"
+ return
+ ;;
+ *.*)
+ __git_complete_config_variable_value
+ return
+ ;;
+ esac
+ case "$cur" in
+ --*)
+ __gitcomp_builtin config
+ ;;
+ *)
+ __git_complete_config_variable_name
+ ;;
esac
}
@@ -2512,7 +2624,7 @@ _git_restore ()
esac
}
-__git_revert_inprogress_options="--continue --quit --abort"
+__git_revert_inprogress_options=$__git_sequencer_inprogress_options
_git_revert ()
{
@@ -2580,8 +2692,9 @@ _git_show ()
return
;;
--*)
- __gitcomp "--pretty= --format= --abbrev-commit --oneline
- --show-signature
+ __gitcomp "--pretty= --format= --abbrev-commit --no-abbrev-commit
+ --oneline --show-signature --patch
+ --expand-tabs --expand-tabs= --no-expand-tabs
$__git_diff_common_options
"
return
@@ -2956,7 +3069,11 @@ __git_main ()
# Bash filename completion
return
;;
- -c|--namespace)
+ -c)
+ __git_complete_config_variable_name_and_value
+ return
+ ;;
+ --namespace)
# we don't support completing these options' arguments
return
;;
diff --git a/contrib/completion/git-completion.zsh b/contrib/completion/git-completion.zsh
index 886bf95..eef4eff 100644
--- a/contrib/completion/git-completion.zsh
+++ b/contrib/completion/git-completion.zsh
@@ -11,8 +11,9 @@
#
# zstyle ':completion:*:*:git:*' script ~/.git-completion.zsh
#
-# The recommended way to install this script is to copy to '~/.zsh/_git', and
-# then add the following to your ~/.zshrc file:
+# The recommended way to install this script is to make a copy of it in
+# ~/.zsh/ directory as ~/.zsh/git-completion.zsh and then add the following
+# to your ~/.zshrc file:
#
# fpath=(~/.zsh $fpath)
diff --git a/contrib/diff-highlight/DiffHighlight.pm b/contrib/diff-highlight/DiffHighlight.pm
index 7440aa1..e258992 100644
--- a/contrib/diff-highlight/DiffHighlight.pm
+++ b/contrib/diff-highlight/DiffHighlight.pm
@@ -72,7 +72,7 @@ sub handle_line {
(?:$COLOR?\|$COLOR?[ ])* # zero or more trailing "|"
[ ]* # trailing whitespace for merges
/x) {
- my $graph_prefix = $&;
+ my $graph_prefix = $&;
# We must flush before setting graph indent, since the
# new commit may be indented differently from what we
diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py
index de3f816..7eb1b24 100755
--- a/contrib/hg-to-git/hg-to-git.py
+++ b/contrib/hg-to-git/hg-to-git.py
@@ -42,7 +42,7 @@ hgnewcsets = 0
def usage():
- print """\
+ print("""\
%s: [OPTIONS] <hgprj>
options:
@@ -54,7 +54,7 @@ options:
required:
hgprj: name of the HG project to import (directory)
-""" % sys.argv[0]
+""" % sys.argv[0])
#------------------------------------------------------------------------------
@@ -104,22 +104,22 @@ os.chdir(hgprj)
if state:
if os.path.exists(state):
if verbose:
- print 'State does exist, reading'
+ print('State does exist, reading')
f = open(state, 'r')
hgvers = pickle.load(f)
else:
- print 'State does not exist, first run'
+ print('State does not exist, first run')
sock = os.popen('hg tip --template "{rev}"')
tip = sock.read()
if sock.close():
sys.exit(1)
if verbose:
- print 'tip is', tip
+ print('tip is', tip)
# Calculate the branches
if verbose:
- print 'analysing the branches...'
+ print('analysing the branches...')
hgchildren["0"] = ()
hgparents["0"] = (None, None)
hgbranch["0"] = "master"
@@ -154,15 +154,15 @@ for cset in range(1, int(tip) + 1):
else:
hgbranch[str(cset)] = "branch-" + str(cset)
-if not hgvers.has_key("0"):
- print 'creating repository'
+if "0" not in hgvers:
+ print('creating repository')
os.system('git init')
# loop through every hg changeset
for cset in range(int(tip) + 1):
# incremental, already seen
- if hgvers.has_key(str(cset)):
+ if str(cset) in hgvers:
continue
hgnewcsets += 1
@@ -180,27 +180,27 @@ for cset in range(int(tip) + 1):
os.write(fdcomment, csetcomment)
os.close(fdcomment)
- print '-----------------------------------------'
- print 'cset:', cset
- print 'branch:', hgbranch[str(cset)]
- print 'user:', user
- print 'date:', date
- print 'comment:', csetcomment
+ print('-----------------------------------------')
+ print('cset:', cset)
+ print('branch:', hgbranch[str(cset)])
+ print('user:', user)
+ print('date:', date)
+ print('comment:', csetcomment)
if parent:
- print 'parent:', parent
+ print('parent:', parent)
if mparent:
- print 'mparent:', mparent
+ print('mparent:', mparent)
if tag:
- print 'tag:', tag
- print '-----------------------------------------'
+ print('tag:', tag)
+ print('-----------------------------------------')
# checkout the parent if necessary
if cset != 0:
if hgbranch[str(cset)] == "branch-" + str(cset):
- print 'creating new branch', hgbranch[str(cset)]
+ print('creating new branch', hgbranch[str(cset)])
os.system('git checkout -b %s %s' % (hgbranch[str(cset)], hgvers[parent]))
else:
- print 'checking out branch', hgbranch[str(cset)]
+ print('checking out branch', hgbranch[str(cset)])
os.system('git checkout %s' % hgbranch[str(cset)])
# merge
@@ -209,7 +209,7 @@ for cset in range(int(tip) + 1):
otherbranch = hgbranch[mparent]
else:
otherbranch = hgbranch[parent]
- print 'merging', otherbranch, 'into', hgbranch[str(cset)]
+ print('merging', otherbranch, 'into', hgbranch[str(cset)])
os.system(getgitenv(user, date) + 'git merge --no-commit -s ours "" %s %s' % (hgbranch[str(cset)], otherbranch))
# remove everything except .git and .hg directories
@@ -233,12 +233,12 @@ for cset in range(int(tip) + 1):
# delete branch if not used anymore...
if mparent and len(hgchildren[str(cset)]):
- print "Deleting unused branch:", otherbranch
+ print("Deleting unused branch:", otherbranch)
os.system('git branch -d %s' % otherbranch)
# retrieve and record the version
vvv = os.popen('git show --quiet --pretty=format:%H').read()
- print 'record', cset, '->', vvv
+ print('record', cset, '->', vvv)
hgvers[str(cset)] = vvv
if hgnewcsets >= opt_nrepack and opt_nrepack != -1:
@@ -247,7 +247,7 @@ if hgnewcsets >= opt_nrepack and opt_nrepack != -1:
# write the state for incrementals
if state:
if verbose:
- print 'Writing state'
+ print('Writing state')
f = open(state, 'w')
pickle.dump(hgvers, f)
diff --git a/contrib/svn-fe/svn-fe.txt b/contrib/svn-fe/svn-fe.txt
index a3425f4..19333fc 100644
--- a/contrib/svn-fe/svn-fe.txt
+++ b/contrib/svn-fe/svn-fe.txt
@@ -56,7 +56,7 @@ line. This line has the form `git-svn-id: URL@REVNO UUID`.
The resulting repository will generally require further processing
to put each project in its own repository and to separate the history
-of each branch. The 'git filter-branch --subdirectory-filter' command
+of each branch. The 'git filter-repo --subdirectory-filter' command
may be useful for this purpose.
BUGS
@@ -67,5 +67,5 @@ The exit status does not reflect whether an error was detected.
SEE ALSO
--------
-git-svn(1), svn2git(1), svk(1), git-filter-branch(1), git-fast-import(1),
+git-svn(1), svn2git(1), svk(1), git-filter-repo(1), git-fast-import(1),
https://svn.apache.org/repos/asf/subversion/trunk/notes/dump-load-format.txt
diff --git a/contrib/svn-fe/svnrdump_sim.py b/contrib/svn-fe/svnrdump_sim.py
index 11ac6f6..50c6a4f 100755
--- a/contrib/svn-fe/svnrdump_sim.py
+++ b/contrib/svn-fe/svnrdump_sim.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
"""
Simulates svnrdump by replaying an existing dump from a file, taking care
of the specified revision range.
diff --git a/convert.c b/convert.c
index 94ff837..25ac525 100644
--- a/convert.c
+++ b/convert.c
@@ -8,6 +8,7 @@
#include "pkt-line.h"
#include "sub-process.h"
#include "utf8.h"
+#include "ll-merge.h"
/*
* convert.c - convert a file when checking it out and checking it in.
@@ -289,8 +290,8 @@ static int validate_encoding(const char *path, const char *enc,
const char *stripped = NULL;
char *upper = xstrdup_toupper(enc);
upper[strlen(upper)-2] = '\0';
- if (!skip_prefix(upper, "UTF-", &stripped))
- skip_prefix(stripped, "UTF", &stripped);
+ if (skip_prefix(upper, "UTF", &stripped))
+ skip_prefix(stripped, "-", &stripped);
advise(advise_msg, path, stripped);
free(upper);
if (die_on_error)
@@ -309,8 +310,8 @@ static int validate_encoding(const char *path, const char *enc,
"working-tree-encoding.");
const char *stripped = NULL;
char *upper = xstrdup_toupper(enc);
- if (!skip_prefix(upper, "UTF-", &stripped))
- skip_prefix(stripped, "UTF", &stripped);
+ if (skip_prefix(upper, "UTF", &stripped))
+ skip_prefix(stripped, "-", &stripped);
advise(advise_msg, path, stripped, stripped);
free(upper);
if (die_on_error)
@@ -1293,10 +1294,11 @@ struct conv_attrs {
const char *working_tree_encoding; /* Supported encoding or default encoding if NULL */
};
+static struct attr_check *check;
+
static void convert_attrs(const struct index_state *istate,
struct conv_attrs *ca, const char *path)
{
- static struct attr_check *check;
struct attr_check_item *ccheck = NULL;
if (!check) {
@@ -1339,6 +1341,23 @@ static void convert_attrs(const struct index_state *istate,
ca->crlf_action = CRLF_AUTO_INPUT;
}
+void reset_parsed_attributes(void)
+{
+ struct convert_driver *drv, *next;
+
+ attr_check_free(check);
+ check = NULL;
+ reset_merge_attributes();
+
+ for (drv = user_convert; drv; drv = next) {
+ next = drv->next;
+ free((void *)drv->name);
+ free(drv);
+ }
+ user_convert = NULL;
+ user_convert_tail = NULL;
+}
+
int would_convert_to_git_filter_fd(const struct index_state *istate, const char *path)
{
struct conv_attrs ca;
diff --git a/convert.h b/convert.h
index 831559f..3710969 100644
--- a/convert.h
+++ b/convert.h
@@ -94,6 +94,12 @@ void convert_to_git_filter_fd(const struct index_state *istate,
int would_convert_to_git_filter_fd(const struct index_state *istate,
const char *path);
+/*
+ * Reset the internal list of attributes used by convert_to_git and
+ * convert_to_working_tree.
+ */
+void reset_parsed_attributes(void);
+
/*****************************************************************
*
* Streaming conversion support
diff --git a/credential-store.c b/credential-store.c
index ac29542..c010497 100644
--- a/credential-store.c
+++ b/credential-store.c
@@ -72,15 +72,16 @@ static void store_credential_file(const char *fn, struct credential *c)
struct strbuf buf = STRBUF_INIT;
strbuf_addf(&buf, "%s://", c->protocol);
- strbuf_addstr_urlencode(&buf, c->username, 1);
+ strbuf_addstr_urlencode(&buf, c->username, is_rfc3986_unreserved);
strbuf_addch(&buf, ':');
- strbuf_addstr_urlencode(&buf, c->password, 1);
+ strbuf_addstr_urlencode(&buf, c->password, is_rfc3986_unreserved);
strbuf_addch(&buf, '@');
if (c->host)
- strbuf_addstr_urlencode(&buf, c->host, 1);
+ strbuf_addstr_urlencode(&buf, c->host, is_rfc3986_unreserved);
if (c->path) {
strbuf_addch(&buf, '/');
- strbuf_addstr_urlencode(&buf, c->path, 0);
+ strbuf_addstr_urlencode(&buf, c->path,
+ is_rfc3986_reserved_or_unreserved);
}
rewrite_credential_file(fn, c, &buf);
diff --git a/date.c b/date.c
index 8126146..041db7d 100644
--- a/date.c
+++ b/date.c
@@ -128,16 +128,17 @@ static void get_time(struct timeval *now)
gettimeofday(now, NULL);
}
-void show_date_relative(timestamp_t time,
- const struct timeval *now,
- struct strbuf *timebuf)
+void show_date_relative(timestamp_t time, struct strbuf *timebuf)
{
+ struct timeval now;
timestamp_t diff;
- if (now->tv_sec < time) {
+
+ get_time(&now);
+ if (now.tv_sec < time) {
strbuf_addstr(timebuf, _("in the future"));
return;
}
- diff = now->tv_sec - time;
+ diff = now.tv_sec - time;
if (diff < 90) {
strbuf_addf(timebuf,
Q_("%"PRItime" second ago", "%"PRItime" seconds ago", diff), diff);
@@ -240,9 +241,7 @@ static void show_date_normal(struct strbuf *buf, timestamp_t time, struct tm *tm
/* Show "today" times as just relative times */
if (hide.wday) {
- struct timeval now;
- get_time(&now);
- show_date_relative(time, &now, buf);
+ show_date_relative(time, buf);
return;
}
@@ -313,11 +312,8 @@ const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
}
if (mode->type == DATE_RELATIVE) {
- struct timeval now;
-
strbuf_reset(&timebuf);
- get_time(&now);
- show_date_relative(time, &now, &timebuf);
+ show_date_relative(time, &timebuf);
return timebuf.buf;
}
@@ -1288,15 +1284,18 @@ static timestamp_t approxidate_str(const char *date,
return (timestamp_t)update_tm(&tm, &now, 0);
}
-timestamp_t approxidate_relative(const char *date, const struct timeval *tv)
+timestamp_t approxidate_relative(const char *date)
{
+ struct timeval tv;
timestamp_t timestamp;
int offset;
int errors = 0;
if (!parse_date_basic(date, &timestamp, &offset))
return timestamp;
- return approxidate_str(date, tv, &errors);
+
+ get_time(&tv);
+ return approxidate_str(date, (const struct timeval *) &tv, &errors);
}
timestamp_t approxidate_careful(const char *date, int *error_ret)
diff --git a/diff-delta.c b/diff-delta.c
index e496433..77fea08 100644
--- a/diff-delta.c
+++ b/diff-delta.c
@@ -326,6 +326,8 @@ create_delta(const struct delta_index *index,
const unsigned char *ref_data, *ref_top, *data, *top;
unsigned char *out;
+ *delta_size = 0;
+
if (!trg_buf || !trg_size)
return NULL;
diff --git a/diff.c b/diff.c
index efe42b3..afe4400 100644
--- a/diff.c
+++ b/diff.c
@@ -25,7 +25,7 @@
#include "packfile.h"
#include "parse-options.h"
#include "help.h"
-#include "fetch-object.h"
+#include "promisor-remote.h"
#ifdef NO_FAST_WORKING_DIRECTORY
#define FAST_WORKING_DIRECTORY 0
@@ -933,16 +933,18 @@ static int cmp_in_block_with_wsd(const struct diff_options *o,
}
static int moved_entry_cmp(const void *hashmap_cmp_fn_data,
- const void *entry,
- const void *entry_or_key,
+ const struct hashmap_entry *eptr,
+ const struct hashmap_entry *entry_or_key,
const void *keydata)
{
const struct diff_options *diffopt = hashmap_cmp_fn_data;
- const struct moved_entry *a = entry;
- const struct moved_entry *b = entry_or_key;
+ const struct moved_entry *a, *b;
unsigned flags = diffopt->color_moved_ws_handling
& XDF_WHITESPACE_FLAGS;
+ a = container_of(eptr, const struct moved_entry, ent);
+ b = container_of(entry_or_key, const struct moved_entry, ent);
+
if (diffopt->color_moved_ws_handling &
COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
/*
@@ -964,8 +966,9 @@ static struct moved_entry *prepare_entry(struct diff_options *o,
struct moved_entry *ret = xmalloc(sizeof(*ret));
struct emitted_diff_symbol *l = &o->emitted_symbols->buf[line_no];
unsigned flags = o->color_moved_ws_handling & XDF_WHITESPACE_FLAGS;
+ unsigned int hash = xdiff_hash_string(l->line, l->len, flags);
- ret->ent.hash = xdiff_hash_string(l->line, l->len, flags);
+ hashmap_entry_init(&ret->ent, hash);
ret->es = l;
ret->next_line = NULL;
@@ -1002,7 +1005,7 @@ static void add_lines_to_move_detection(struct diff_options *o,
if (prev_line && prev_line->es->s == o->emitted_symbols->buf[n].s)
prev_line->next_line = key;
- hashmap_add(hm, key);
+ hashmap_add(hm, &key->ent);
prev_line = key;
}
}
@@ -1018,7 +1021,7 @@ static void pmb_advance_or_null(struct diff_options *o,
struct moved_entry *prev = pmb[i].match;
struct moved_entry *cur = (prev && prev->next_line) ?
prev->next_line : NULL;
- if (cur && !hm->cmpfn(o, cur, match, NULL)) {
+ if (cur && !hm->cmpfn(o, &cur->ent, &match->ent, NULL)) {
pmb[i].match = cur;
} else {
pmb[i].match = NULL;
@@ -1035,7 +1038,7 @@ static void pmb_advance_or_null_multi_match(struct diff_options *o,
int i;
char *got_match = xcalloc(1, pmb_nr);
- for (; match; match = hashmap_get_next(hm, match)) {
+ hashmap_for_each_entry_from(hm, match, ent) {
for (i = 0; i < pmb_nr; i++) {
struct moved_entry *prev = pmb[i].match;
struct moved_entry *cur = (prev && prev->next_line) ?
@@ -1143,13 +1146,13 @@ static void mark_color_as_moved(struct diff_options *o,
case DIFF_SYMBOL_PLUS:
hm = del_lines;
key = prepare_entry(o, n);
- match = hashmap_get(hm, key, NULL);
+ match = hashmap_get_entry(hm, key, ent, NULL);
free(key);
break;
case DIFF_SYMBOL_MINUS:
hm = add_lines;
key = prepare_entry(o, n);
- match = hashmap_get(hm, key, NULL);
+ match = hashmap_get_entry(hm, key, ent, NULL);
free(key);
break;
default:
@@ -1188,7 +1191,7 @@ static void mark_color_as_moved(struct diff_options *o,
* The current line is the start of a new block.
* Setup the set of potential blocks.
*/
- for (; match; match = hashmap_get_next(hm, match)) {
+ hashmap_for_each_entry_from(hm, match, ent) {
ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
if (o->color_moved_ws_handling &
COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {
@@ -5978,7 +5981,7 @@ static void diff_summary(struct diff_options *opt, struct diff_filepair *p)
}
struct patch_id_t {
- git_SHA_CTX *ctx;
+ git_hash_ctx *ctx;
int patchlen;
};
@@ -5995,16 +5998,16 @@ static int remove_space(char *line, int len)
return dst - line;
}
-void flush_one_hunk(struct object_id *result, git_SHA_CTX *ctx)
+void flush_one_hunk(struct object_id *result, git_hash_ctx *ctx)
{
unsigned char hash[GIT_MAX_RAWSZ];
unsigned short carry = 0;
int i;
- git_SHA1_Final(hash, ctx);
- git_SHA1_Init(ctx);
+ the_hash_algo->final_fn(hash, ctx);
+ the_hash_algo->init_fn(ctx);
/* 20-byte sum, with carry */
- for (i = 0; i < GIT_SHA1_RAWSZ; ++i) {
+ for (i = 0; i < the_hash_algo->rawsz; ++i) {
carry += result->hash[i] + hash[i];
result->hash[i] = carry;
carry >>= 8;
@@ -6018,21 +6021,21 @@ static void patch_id_consume(void *priv, char *line, unsigned long len)
new_len = remove_space(line, len);
- git_SHA1_Update(data->ctx, line, new_len);
+ the_hash_algo->update_fn(data->ctx, line, new_len);
data->patchlen += new_len;
}
-static void patch_id_add_string(git_SHA_CTX *ctx, const char *str)
+static void patch_id_add_string(git_hash_ctx *ctx, const char *str)
{
- git_SHA1_Update(ctx, str, strlen(str));
+ the_hash_algo->update_fn(ctx, str, strlen(str));
}
-static void patch_id_add_mode(git_SHA_CTX *ctx, unsigned mode)
+static void patch_id_add_mode(git_hash_ctx *ctx, unsigned mode)
{
/* large enough for 2^32 in octal */
char buf[12];
int len = xsnprintf(buf, sizeof(buf), "%06o", mode);
- git_SHA1_Update(ctx, buf, len);
+ the_hash_algo->update_fn(ctx, buf, len);
}
/* returns 0 upon success, and writes result into oid */
@@ -6040,10 +6043,10 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
{
struct diff_queue_struct *q = &diff_queued_diff;
int i;
- git_SHA_CTX ctx;
+ git_hash_ctx ctx;
struct patch_id_t data;
- git_SHA1_Init(&ctx);
+ the_hash_algo->init_fn(&ctx);
memset(&data, 0, sizeof(struct patch_id_t));
data.ctx = &ctx;
oidclr(oid);
@@ -6076,27 +6079,27 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
len2 = remove_space(p->two->path, strlen(p->two->path));
patch_id_add_string(&ctx, "diff--git");
patch_id_add_string(&ctx, "a/");
- git_SHA1_Update(&ctx, p->one->path, len1);
+ the_hash_algo->update_fn(&ctx, p->one->path, len1);
patch_id_add_string(&ctx, "b/");
- git_SHA1_Update(&ctx, p->two->path, len2);
+ the_hash_algo->update_fn(&ctx, p->two->path, len2);
if (p->one->mode == 0) {
patch_id_add_string(&ctx, "newfilemode");
patch_id_add_mode(&ctx, p->two->mode);
patch_id_add_string(&ctx, "---/dev/null");
patch_id_add_string(&ctx, "+++b/");
- git_SHA1_Update(&ctx, p->two->path, len2);
+ the_hash_algo->update_fn(&ctx, p->two->path, len2);
} else if (p->two->mode == 0) {
patch_id_add_string(&ctx, "deletedfilemode");
patch_id_add_mode(&ctx, p->one->mode);
patch_id_add_string(&ctx, "---a/");
- git_SHA1_Update(&ctx, p->one->path, len1);
+ the_hash_algo->update_fn(&ctx, p->one->path, len1);
patch_id_add_string(&ctx, "+++/dev/null");
} else {
patch_id_add_string(&ctx, "---a/");
- git_SHA1_Update(&ctx, p->one->path, len1);
+ the_hash_algo->update_fn(&ctx, p->one->path, len1);
patch_id_add_string(&ctx, "+++b/");
- git_SHA1_Update(&ctx, p->two->path, len2);
+ the_hash_algo->update_fn(&ctx, p->two->path, len2);
}
if (diff_header_only)
@@ -6108,10 +6111,10 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
if (diff_filespec_is_binary(options->repo, p->one) ||
diff_filespec_is_binary(options->repo, p->two)) {
- git_SHA1_Update(&ctx, oid_to_hex(&p->one->oid),
- GIT_SHA1_HEXSZ);
- git_SHA1_Update(&ctx, oid_to_hex(&p->two->oid),
- GIT_SHA1_HEXSZ);
+ the_hash_algo->update_fn(&ctx, oid_to_hex(&p->one->oid),
+ the_hash_algo->hexsz);
+ the_hash_algo->update_fn(&ctx, oid_to_hex(&p->two->oid),
+ the_hash_algo->hexsz);
continue;
}
@@ -6128,7 +6131,7 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
}
if (!stable)
- git_SHA1_Final(oid->hash, &ctx);
+ the_hash_algo->final_fn(oid->hash, &ctx);
return 0;
}
@@ -6230,8 +6233,10 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
if (o->color_moved == COLOR_MOVED_ZEBRA_DIM)
dim_moved_lines(o);
- hashmap_free(&add_lines, 1);
- hashmap_free(&del_lines, 1);
+ hashmap_free_entries(&add_lines, struct moved_entry,
+ ent);
+ hashmap_free_entries(&del_lines, struct moved_entry,
+ ent);
}
for (i = 0; i < esm.nr; i++)
@@ -6512,6 +6517,7 @@ static void add_if_missing(struct repository *r,
const struct diff_filespec *filespec)
{
if (filespec && filespec->oid_valid &&
+ !S_ISGITLINK(filespec->mode) &&
oid_object_info_extended(r, &filespec->oid, NULL,
OBJECT_INFO_FOR_PREFETCH))
oid_array_append(to_fetch, &filespec->oid);
@@ -6519,8 +6525,7 @@ static void add_if_missing(struct repository *r,
void diffcore_std(struct diff_options *options)
{
- if (options->repo == the_repository &&
- repository_format_partial_clone) {
+ if (options->repo == the_repository && has_promisor_remote()) {
/*
* Prefetch the diff pairs that are about to be flushed.
*/
@@ -6537,8 +6542,8 @@ void diffcore_std(struct diff_options *options)
/*
* NEEDSWORK: Consider deduplicating the OIDs sent.
*/
- fetch_objects(repository_format_partial_clone,
- to_fetch.oid, to_fetch.nr);
+ promisor_remote_get_direct(options->repo,
+ to_fetch.oid, to_fetch.nr);
oid_array_clear(&to_fetch);
}
diff --git a/diff.h b/diff.h
index c2c3056..7f8f024 100644
--- a/diff.h
+++ b/diff.h
@@ -438,7 +438,7 @@ int run_diff_index(struct rev_info *revs, int cached);
int do_diff_cache(const struct object_id *, struct diff_options *);
int diff_flush_patch_id(struct diff_options *, struct object_id *, int, int);
-void flush_one_hunk(struct object_id *, git_SHA_CTX *);
+void flush_one_hunk(struct object_id *result, git_hash_ctx *ctx);
int diff_result_code(struct diff_options *, int);
diff --git a/diffcore-break.c b/diffcore-break.c
index 875aefd..9d20a6a 100644
--- a/diffcore-break.c
+++ b/diffcore-break.c
@@ -286,17 +286,17 @@ void diffcore_merge_broken(void)
/* Peer survived. Merge them */
merge_broken(p, pp, &outq);
q->queue[j] = NULL;
- break;
+ goto next;
}
}
- if (q->nr <= j)
- /* The peer did not survive, so we keep
- * it in the output.
- */
- diff_q(&outq, p);
+ /* The peer did not survive, so we keep
+ * it in the output.
+ */
+ diff_q(&outq, p);
}
else
diff_q(&outq, p);
+next:;
}
free(q->queue);
*q = outq;
diff --git a/diffcore-rename.c b/diffcore-rename.c
index 9624864..531d7ad 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -274,18 +274,17 @@ static int find_identical_files(struct hashmap *srcs,
struct diff_options *options)
{
int renames = 0;
-
struct diff_filespec *target = rename_dst[dst_index].two;
struct file_similarity *p, *best = NULL;
int i = 100, best_score = -1;
+ unsigned int hash = hash_filespec(options->repo, target);
/*
* Find the best source match for specified destination.
*/
- p = hashmap_get_from_hash(srcs,
- hash_filespec(options->repo, target),
- NULL);
- for (; p; p = hashmap_get_next(srcs, p)) {
+ p = hashmap_get_entry_from_hash(srcs, hash, NULL,
+ struct file_similarity, entry);
+ hashmap_for_each_entry_from(srcs, p, entry) {
int score;
struct diff_filespec *source = p->filespec;
@@ -329,8 +328,8 @@ static void insert_file_table(struct repository *r,
entry->index = index;
entry->filespec = filespec;
- hashmap_entry_init(entry, hash_filespec(r, filespec));
- hashmap_add(table, entry);
+ hashmap_entry_init(&entry->entry, hash_filespec(r, filespec));
+ hashmap_add(table, &entry->entry);
}
/*
@@ -359,7 +358,7 @@ static int find_exact_renames(struct diff_options *options)
renames += find_identical_files(&file_table, i, options);
/* Free the hash data structure and entries */
- hashmap_free(&file_table, 1);
+ hashmap_free_entries(&file_table, struct file_similarity, entry);
return renames;
}
@@ -585,7 +584,7 @@ void diffcore_rename(struct diff_options *options)
stop_progress(&progress);
/* cost matrix sorted by most to least similar pair */
- QSORT(mx, dst_cnt * NUM_CANDIDATE_PER_DST, score_compare);
+ STABLE_QSORT(mx, dst_cnt * NUM_CANDIDATE_PER_DST, score_compare);
rename_count += find_renames(mx, dst_cnt, minimum_score, 0);
if (detect_rename == DIFF_DETECT_COPY)
diff --git a/dir.c b/dir.c
index d021c90..61f559f9 100644
--- a/dir.c
+++ b/dir.c
@@ -139,7 +139,7 @@ static size_t common_prefix_len(const struct pathspec *pathspec)
* ":(icase)path" is treated as a pathspec full of
* wildcard. In other words, only prefix is considered common
* prefix. If the pathspec is abc/foo abc/bar, running in
- * subdir xyz, the common prefix is still xyz, not xuz/abc as
+ * subdir xyz, the common prefix is still xyz, not xyz/abc as
* in non-:(icase).
*/
GUARD_PATHSPEC(pathspec,
@@ -273,19 +273,30 @@ static int do_read_blob(const struct object_id *oid, struct oid_stat *oid_stat,
#define DO_MATCH_EXCLUDE (1<<0)
#define DO_MATCH_DIRECTORY (1<<1)
-#define DO_MATCH_SUBMODULE (1<<2)
+#define DO_MATCH_LEADING_PATHSPEC (1<<2)
/*
- * Does 'match' match the given name?
- * A match is found if
+ * Does the given pathspec match the given name? A match is found if
*
- * (1) the 'match' string is leading directory of 'name', or
- * (2) the 'match' string is a wildcard and matches 'name', or
- * (3) the 'match' string is exactly the same as 'name'.
+ * (1) the pathspec string is leading directory of 'name' ("RECURSIVELY"), or
+ * (2) the pathspec string has a leading part matching 'name' ("LEADING"), or
+ * (3) the pathspec string is a wildcard and matches 'name' ("WILDCARD"), or
+ * (4) the pathspec string is exactly the same as 'name' ("EXACT").
*
- * and the return value tells which case it was.
+ * Return value tells which case it was (1-4), or 0 when there is no match.
*
- * It returns 0 when there is no match.
+ * It may be instructive to look at a small table of concrete examples
+ * to understand the differences between 1, 2, and 4:
+ *
+ * Pathspecs
+ * | a/b | a/b/ | a/b/c
+ * ------+-----------+-----------+------------
+ * a/b | EXACT | EXACT[1] | LEADING[2]
+ * Names a/b/ | RECURSIVE | EXACT | LEADING[2]
+ * a/b/c | RECURSIVE | RECURSIVE | EXACT
+ *
+ * [1] Only if DO_MATCH_DIRECTORY is passed; otherwise, this is NOT a match.
+ * [2] Only if DO_MATCH_LEADING_PATHSPEC is passed; otherwise, not a match.
*/
static int match_pathspec_item(const struct index_state *istate,
const struct pathspec_item *item, int prefix,
@@ -353,13 +364,14 @@ static int match_pathspec_item(const struct index_state *istate,
item->nowildcard_len - prefix))
return MATCHED_FNMATCH;
- /* Perform checks to see if "name" is a super set of the pathspec */
- if (flags & DO_MATCH_SUBMODULE) {
+ /* Perform checks to see if "name" is a leading string of the pathspec */
+ if (flags & DO_MATCH_LEADING_PATHSPEC) {
/* name is a literal prefix of the pathspec */
+ int offset = name[namelen-1] == '/' ? 1 : 0;
if ((namelen < matchlen) &&
- (match[namelen] == '/') &&
+ (match[namelen-offset] == '/') &&
!ps_strncmp(item, match, name, namelen))
- return MATCHED_RECURSIVELY;
+ return MATCHED_RECURSIVELY_LEADING_PATHSPEC;
/* name" doesn't match up to the first wild character */
if (item->nowildcard_len < item->len &&
@@ -376,7 +388,7 @@ static int match_pathspec_item(const struct index_state *istate,
* The submodules themselves will be able to perform more
* accurate matching to determine if the pathspec matches.
*/
- return MATCHED_RECURSIVELY;
+ return MATCHED_RECURSIVELY_LEADING_PATHSPEC;
}
return 0;
@@ -497,7 +509,7 @@ int submodule_path_match(const struct index_state *istate,
strlen(submodule_name),
0, seen,
DO_MATCH_DIRECTORY |
- DO_MATCH_SUBMODULE);
+ DO_MATCH_LEADING_PATHSPEC);
return matched;
}
@@ -561,7 +573,7 @@ int no_wildcard(const char *string)
return string[simple_length(string)] == '\0';
}
-void parse_exclude_pattern(const char **pattern,
+void parse_path_pattern(const char **pattern,
int *patternlen,
unsigned *flags,
int *nowildcardlen)
@@ -571,20 +583,20 @@ void parse_exclude_pattern(const char **pattern,
*flags = 0;
if (*p == '!') {
- *flags |= EXC_FLAG_NEGATIVE;
+ *flags |= PATTERN_FLAG_NEGATIVE;
p++;
}
len = strlen(p);
if (len && p[len - 1] == '/') {
len--;
- *flags |= EXC_FLAG_MUSTBEDIR;
+ *flags |= PATTERN_FLAG_MUSTBEDIR;
}
for (i = 0; i < len; i++) {
if (p[i] == '/')
break;
}
if (i == len)
- *flags |= EXC_FLAG_NODIR;
+ *flags |= PATTERN_FLAG_NODIR;
*nowildcardlen = simple_length(p);
/*
* we should have excluded the trailing slash from 'p' too,
@@ -594,35 +606,35 @@ void parse_exclude_pattern(const char **pattern,
if (*nowildcardlen > len)
*nowildcardlen = len;
if (*p == '*' && no_wildcard(p + 1))
- *flags |= EXC_FLAG_ENDSWITH;
+ *flags |= PATTERN_FLAG_ENDSWITH;
*pattern = p;
*patternlen = len;
}
-void add_exclude(const char *string, const char *base,
- int baselen, struct exclude_list *el, int srcpos)
+void add_pattern(const char *string, const char *base,
+ int baselen, struct pattern_list *pl, int srcpos)
{
- struct exclude *x;
+ struct path_pattern *pattern;
int patternlen;
unsigned flags;
int nowildcardlen;
- parse_exclude_pattern(&string, &patternlen, &flags, &nowildcardlen);
- if (flags & EXC_FLAG_MUSTBEDIR) {
- FLEXPTR_ALLOC_MEM(x, pattern, string, patternlen);
+ parse_path_pattern(&string, &patternlen, &flags, &nowildcardlen);
+ if (flags & PATTERN_FLAG_MUSTBEDIR) {
+ FLEXPTR_ALLOC_MEM(pattern, pattern, string, patternlen);
} else {
- x = xmalloc(sizeof(*x));
- x->pattern = string;
+ pattern = xmalloc(sizeof(*pattern));
+ pattern->pattern = string;
}
- x->patternlen = patternlen;
- x->nowildcardlen = nowildcardlen;
- x->base = base;
- x->baselen = baselen;
- x->flags = flags;
- x->srcpos = srcpos;
- ALLOC_GROW(el->excludes, el->nr + 1, el->alloc);
- el->excludes[el->nr++] = x;
- x->el = el;
+ pattern->patternlen = patternlen;
+ pattern->nowildcardlen = nowildcardlen;
+ pattern->base = base;
+ pattern->baselen = baselen;
+ pattern->flags = flags;
+ pattern->srcpos = srcpos;
+ ALLOC_GROW(pl->patterns, pl->nr + 1, pl->alloc);
+ pl->patterns[pl->nr++] = pattern;
+ pattern->pl = pl;
}
static int read_skip_worktree_file_from_index(const struct index_state *istate,
@@ -643,19 +655,19 @@ static int read_skip_worktree_file_from_index(const struct index_state *istate,
}
/*
- * Frees memory within el which was allocated for exclude patterns and
- * the file buffer. Does not free el itself.
+ * Frees memory within pl which was allocated for exclude patterns and
+ * the file buffer. Does not free pl itself.
*/
-void clear_exclude_list(struct exclude_list *el)
+void clear_pattern_list(struct pattern_list *pl)
{
int i;
- for (i = 0; i < el->nr; i++)
- free(el->excludes[i]);
- free(el->excludes);
- free(el->filebuf);
+ for (i = 0; i < pl->nr; i++)
+ free(pl->patterns[i]);
+ free(pl->patterns);
+ free(pl->filebuf);
- memset(el, 0, sizeof(*el));
+ memset(pl, 0, sizeof(*pl));
}
static void trim_trailing_spaces(char *buf)
@@ -762,21 +774,21 @@ static void invalidate_directory(struct untracked_cache *uc,
dir->dirs[i]->recurse = 0;
}
-static int add_excludes_from_buffer(char *buf, size_t size,
+static int add_patterns_from_buffer(char *buf, size_t size,
const char *base, int baselen,
- struct exclude_list *el);
+ struct pattern_list *pl);
/*
* Given a file with name "fname", read it (either from disk, or from
* an index if 'istate' is non-null), parse it and store the
- * exclude rules in "el".
+ * exclude rules in "pl".
*
* If "ss" is not NULL, compute SHA-1 of the exclude file and fill
- * stat data from disk (only valid if add_excludes returns zero). If
+ * stat data from disk (only valid if add_patterns returns zero). If
* ss_valid is non-zero, "ss" must contain good value as input.
*/
-static int add_excludes(const char *fname, const char *base, int baselen,
- struct exclude_list *el, struct index_state *istate,
+static int add_patterns(const char *fname, const char *base, int baselen,
+ struct pattern_list *pl, struct index_state *istate,
struct oid_stat *oid_stat)
{
struct stat st;
@@ -837,21 +849,21 @@ static int add_excludes(const char *fname, const char *base, int baselen,
}
}
- add_excludes_from_buffer(buf, size, base, baselen, el);
+ add_patterns_from_buffer(buf, size, base, baselen, pl);
return 0;
}
-static int add_excludes_from_buffer(char *buf, size_t size,
+static int add_patterns_from_buffer(char *buf, size_t size,
const char *base, int baselen,
- struct exclude_list *el)
+ struct pattern_list *pl)
{
int i, lineno = 1;
char *entry;
- el->filebuf = buf;
+ pl->filebuf = buf;
if (skip_utf8_bom(&buf, size))
- size -= buf - el->filebuf;
+ size -= buf - pl->filebuf;
entry = buf;
@@ -860,7 +872,7 @@ static int add_excludes_from_buffer(char *buf, size_t size,
if (entry != buf + i && entry[0] != '#') {
buf[i - (i && buf[i-1] == '\r')] = 0;
trim_trailing_spaces(entry);
- add_exclude(entry, base, baselen, el, lineno);
+ add_pattern(entry, base, baselen, pl, lineno);
}
lineno++;
entry = buf + i + 1;
@@ -869,17 +881,17 @@ static int add_excludes_from_buffer(char *buf, size_t size,
return 0;
}
-int add_excludes_from_file_to_list(const char *fname, const char *base,
- int baselen, struct exclude_list *el,
+int add_patterns_from_file_to_list(const char *fname, const char *base,
+ int baselen, struct pattern_list *pl,
struct index_state *istate)
{
- return add_excludes(fname, base, baselen, el, istate, NULL);
+ return add_patterns(fname, base, baselen, pl, istate, NULL);
}
-int add_excludes_from_blob_to_list(
+int add_patterns_from_blob_to_list(
struct object_id *oid,
const char *base, int baselen,
- struct exclude_list *el)
+ struct pattern_list *pl)
{
char *buf;
size_t size;
@@ -889,31 +901,31 @@ int add_excludes_from_blob_to_list(
if (r != 1)
return r;
- add_excludes_from_buffer(buf, size, base, baselen, el);
+ add_patterns_from_buffer(buf, size, base, baselen, pl);
return 0;
}
-struct exclude_list *add_exclude_list(struct dir_struct *dir,
+struct pattern_list *add_pattern_list(struct dir_struct *dir,
int group_type, const char *src)
{
- struct exclude_list *el;
+ struct pattern_list *pl;
struct exclude_list_group *group;
group = &dir->exclude_list_group[group_type];
- ALLOC_GROW(group->el, group->nr + 1, group->alloc);
- el = &group->el[group->nr++];
- memset(el, 0, sizeof(*el));
- el->src = src;
- return el;
+ ALLOC_GROW(group->pl, group->nr + 1, group->alloc);
+ pl = &group->pl[group->nr++];
+ memset(pl, 0, sizeof(*pl));
+ pl->src = src;
+ return pl;
}
/*
* Used to set up core.excludesfile and .git/info/exclude lists.
*/
-static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname,
+static void add_patterns_from_file_1(struct dir_struct *dir, const char *fname,
struct oid_stat *oid_stat)
{
- struct exclude_list *el;
+ struct pattern_list *pl;
/*
* catch setup_standard_excludes() that's called before
* dir->untracked is assigned. That function behaves
@@ -921,15 +933,15 @@ static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname,
*/
if (!dir->untracked)
dir->unmanaged_exclude_files++;
- el = add_exclude_list(dir, EXC_FILE, fname);
- if (add_excludes(fname, "", 0, el, NULL, oid_stat) < 0)
+ pl = add_pattern_list(dir, EXC_FILE, fname);
+ if (add_patterns(fname, "", 0, pl, NULL, oid_stat) < 0)
die(_("cannot use %s as an exclude file"), fname);
}
-void add_excludes_from_file(struct dir_struct *dir, const char *fname)
+void add_patterns_from_file(struct dir_struct *dir, const char *fname)
{
dir->unmanaged_exclude_files++; /* see validate_untracked_cache() */
- add_excludes_from_file_1(dir, fname, NULL);
+ add_patterns_from_file_1(dir, fname, NULL);
}
int match_basename(const char *basename, int basenamelen,
@@ -940,7 +952,7 @@ int match_basename(const char *basename, int basenamelen,
if (patternlen == basenamelen &&
!fspathncmp(pattern, basename, basenamelen))
return 1;
- } else if (flags & EXC_FLAG_ENDSWITH) {
+ } else if (flags & PATTERN_FLAG_ENDSWITH) {
/* "*literal" matching against "fooliteral" */
if (patternlen - 1 <= basenamelen &&
!fspathncmp(pattern + 1,
@@ -1021,85 +1033,97 @@ int match_pathname(const char *pathname, int pathlen,
* any, determines the fate. Returns the exclude_list element which
* matched, or NULL for undecided.
*/
-static struct exclude *last_exclude_matching_from_list(const char *pathname,
+static struct path_pattern *last_matching_pattern_from_list(const char *pathname,
int pathlen,
const char *basename,
int *dtype,
- struct exclude_list *el,
+ struct pattern_list *pl,
struct index_state *istate)
{
- struct exclude *exc = NULL; /* undecided */
+ struct path_pattern *res = NULL; /* undecided */
int i;
- if (!el->nr)
+ if (!pl->nr)
return NULL; /* undefined */
- for (i = el->nr - 1; 0 <= i; i--) {
- struct exclude *x = el->excludes[i];
- const char *exclude = x->pattern;
- int prefix = x->nowildcardlen;
+ for (i = pl->nr - 1; 0 <= i; i--) {
+ struct path_pattern *pattern = pl->patterns[i];
+ const char *exclude = pattern->pattern;
+ int prefix = pattern->nowildcardlen;
- if (x->flags & EXC_FLAG_MUSTBEDIR) {
+ if (pattern->flags & PATTERN_FLAG_MUSTBEDIR) {
if (*dtype == DT_UNKNOWN)
*dtype = get_dtype(NULL, istate, pathname, pathlen);
if (*dtype != DT_DIR)
continue;
}
- if (x->flags & EXC_FLAG_NODIR) {
+ if (pattern->flags & PATTERN_FLAG_NODIR) {
if (match_basename(basename,
pathlen - (basename - pathname),
- exclude, prefix, x->patternlen,
- x->flags)) {
- exc = x;
+ exclude, prefix, pattern->patternlen,
+ pattern->flags)) {
+ res = pattern;
break;
}
continue;
}
- assert(x->baselen == 0 || x->base[x->baselen - 1] == '/');
+ assert(pattern->baselen == 0 ||
+ pattern->base[pattern->baselen - 1] == '/');
if (match_pathname(pathname, pathlen,
- x->base, x->baselen ? x->baselen - 1 : 0,
- exclude, prefix, x->patternlen, x->flags)) {
- exc = x;
+ pattern->base,
+ pattern->baselen ? pattern->baselen - 1 : 0,
+ exclude, prefix, pattern->patternlen,
+ pattern->flags)) {
+ res = pattern;
break;
}
}
- return exc;
+ return res;
}
/*
- * Scan the list and let the last match determine the fate.
- * Return 1 for exclude, 0 for include and -1 for undecided.
+ * Scan the list of patterns to determine if the ordered list
+ * of patterns matches on 'pathname'.
+ *
+ * Return 1 for a match, 0 for not matched and -1 for undecided.
*/
-int is_excluded_from_list(const char *pathname,
- int pathlen, const char *basename, int *dtype,
- struct exclude_list *el, struct index_state *istate)
-{
- struct exclude *exclude;
- exclude = last_exclude_matching_from_list(pathname, pathlen, basename,
- dtype, el, istate);
- if (exclude)
- return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1;
- return -1; /* undecided */
+enum pattern_match_result path_matches_pattern_list(
+ const char *pathname, int pathlen,
+ const char *basename, int *dtype,
+ struct pattern_list *pl,
+ struct index_state *istate)
+{
+ struct path_pattern *pattern;
+ pattern = last_matching_pattern_from_list(pathname, pathlen, basename,
+ dtype, pl, istate);
+ if (pattern) {
+ if (pattern->flags & PATTERN_FLAG_NEGATIVE)
+ return NOT_MATCHED;
+ else
+ return MATCHED;
+ }
+
+ return UNDECIDED;
}
-static struct exclude *last_exclude_matching_from_lists(struct dir_struct *dir,
- struct index_state *istate,
- const char *pathname, int pathlen, const char *basename,
- int *dtype_p)
+static struct path_pattern *last_matching_pattern_from_lists(
+ struct dir_struct *dir, struct index_state *istate,
+ const char *pathname, int pathlen,
+ const char *basename, int *dtype_p)
{
int i, j;
struct exclude_list_group *group;
- struct exclude *exclude;
+ struct path_pattern *pattern;
for (i = EXC_CMDL; i <= EXC_FILE; i++) {
group = &dir->exclude_list_group[i];
for (j = group->nr - 1; j >= 0; j--) {
- exclude = last_exclude_matching_from_list(
+ pattern = last_matching_pattern_from_list(
pathname, pathlen, basename, dtype_p,
- &group->el[j], istate);
- if (exclude)
- return exclude;
+ &group->pl[j], istate);
+ if (pattern)
+ return pattern;
}
}
return NULL;
@@ -1114,7 +1138,7 @@ static void prep_exclude(struct dir_struct *dir,
const char *base, int baselen)
{
struct exclude_list_group *group;
- struct exclude_list *el;
+ struct pattern_list *pl;
struct exclude_stack *stk = NULL;
struct untracked_cache_dir *untracked;
int current;
@@ -1130,17 +1154,17 @@ static void prep_exclude(struct dir_struct *dir,
if (stk->baselen <= baselen &&
!strncmp(dir->basebuf.buf, base, stk->baselen))
break;
- el = &group->el[dir->exclude_stack->exclude_ix];
+ pl = &group->pl[dir->exclude_stack->exclude_ix];
dir->exclude_stack = stk->prev;
- dir->exclude = NULL;
- free((char *)el->src); /* see strbuf_detach() below */
- clear_exclude_list(el);
+ dir->pattern = NULL;
+ free((char *)pl->src); /* see strbuf_detach() below */
+ clear_pattern_list(pl);
free(stk);
group->nr--;
}
/* Skip traversing into sub directories if the parent is excluded */
- if (dir->exclude)
+ if (dir->pattern)
return;
/*
@@ -1181,7 +1205,7 @@ static void prep_exclude(struct dir_struct *dir,
stk->baselen = cp - base;
stk->exclude_ix = group->nr;
stk->ucd = untracked;
- el = add_exclude_list(dir, EXC_DIRS, NULL);
+ pl = add_pattern_list(dir, EXC_DIRS, NULL);
strbuf_add(&dir->basebuf, base + current, stk->baselen - current);
assert(stk->baselen == dir->basebuf.len);
@@ -1189,15 +1213,15 @@ static void prep_exclude(struct dir_struct *dir,
if (stk->baselen) {
int dt = DT_DIR;
dir->basebuf.buf[stk->baselen - 1] = 0;
- dir->exclude = last_exclude_matching_from_lists(dir,
+ dir->pattern = last_matching_pattern_from_lists(dir,
istate,
dir->basebuf.buf, stk->baselen - 1,
dir->basebuf.buf + current, &dt);
dir->basebuf.buf[stk->baselen - 1] = '/';
- if (dir->exclude &&
- dir->exclude->flags & EXC_FLAG_NEGATIVE)
- dir->exclude = NULL;
- if (dir->exclude) {
+ if (dir->pattern &&
+ dir->pattern->flags & PATTERN_FLAG_NEGATIVE)
+ dir->pattern = NULL;
+ if (dir->pattern) {
dir->exclude_stack = stk;
return;
}
@@ -1223,30 +1247,30 @@ static void prep_exclude(struct dir_struct *dir,
/*
* dir->basebuf gets reused by the traversal, but we
* need fname to remain unchanged to ensure the src
- * member of each struct exclude correctly
+ * member of each struct path_pattern correctly
* back-references its source file. Other invocations
- * of add_exclude_list provide stable strings, so we
+ * of add_pattern_list provide stable strings, so we
* strbuf_detach() and free() here in the caller.
*/
struct strbuf sb = STRBUF_INIT;
strbuf_addbuf(&sb, &dir->basebuf);
strbuf_addstr(&sb, dir->exclude_per_dir);
- el->src = strbuf_detach(&sb, NULL);
- add_excludes(el->src, el->src, stk->baselen, el, istate,
+ pl->src = strbuf_detach(&sb, NULL);
+ add_patterns(pl->src, pl->src, stk->baselen, pl, istate,
untracked ? &oid_stat : NULL);
}
/*
* NEEDSWORK: when untracked cache is enabled, prep_exclude()
* will first be called in valid_cached_dir() then maybe many
- * times more in last_exclude_matching(). When the cache is
- * used, last_exclude_matching() will not be called and
+ * times more in last_matching_pattern(). When the cache is
+ * used, last_matching_pattern() will not be called and
* reading .gitignore content will be a waste.
*
* So when it's called by valid_cached_dir() and we can get
* .gitignore SHA-1 from the index (i.e. .gitignore is not
* modified on work tree), we could delay reading the
* .gitignore content until we absolutely need it in
- * last_exclude_matching(). Be careful about ignore rule
+ * last_matching_pattern(). Be careful about ignore rule
* order, though, if you do that.
*/
if (untracked &&
@@ -1266,7 +1290,7 @@ static void prep_exclude(struct dir_struct *dir,
* Returns the exclude_list element which matched, or NULL for
* undecided.
*/
-struct exclude *last_exclude_matching(struct dir_struct *dir,
+struct path_pattern *last_matching_pattern(struct dir_struct *dir,
struct index_state *istate,
const char *pathname,
int *dtype_p)
@@ -1277,10 +1301,10 @@ struct exclude *last_exclude_matching(struct dir_struct *dir,
prep_exclude(dir, istate, pathname, basename-pathname);
- if (dir->exclude)
- return dir->exclude;
+ if (dir->pattern)
+ return dir->pattern;
- return last_exclude_matching_from_lists(dir, istate, pathname, pathlen,
+ return last_matching_pattern_from_lists(dir, istate, pathname, pathlen,
basename, dtype_p);
}
@@ -1292,10 +1316,10 @@ struct exclude *last_exclude_matching(struct dir_struct *dir,
int is_excluded(struct dir_struct *dir, struct index_state *istate,
const char *pathname, int *dtype_p)
{
- struct exclude *exclude =
- last_exclude_matching(dir, istate, pathname, dtype_p);
- if (exclude)
- return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1;
+ struct path_pattern *pattern =
+ last_matching_pattern(dir, istate, pathname, dtype_p);
+ if (pattern)
+ return pattern->flags & PATTERN_FLAG_NEGATIVE ? 0 : 1;
return 0;
}
@@ -1439,6 +1463,16 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
return path_none;
case index_nonexistent:
+ if (dir->flags & DIR_SKIP_NESTED_GIT) {
+ int nested_repo;
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_addstr(&sb, dirname);
+ nested_repo = is_nonbare_repository_dir(&sb);
+ strbuf_release(&sb);
+ if (nested_repo)
+ return path_none;
+ }
+
if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
break;
if (exclude &&
@@ -1808,7 +1842,7 @@ static int valid_cached_dir(struct dir_struct *dir,
/*
* prep_exclude will be called eventually on this directory,
- * but it's called much later in last_exclude_matching(). We
+ * but it's called much later in last_matching_pattern(). We
* need it now to determine the validity of the cache for this
* path. The next calls will be nearly no-op, the way
* prep_exclude() is designed.
@@ -1938,8 +1972,11 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
/* recurse into subdir if instructed by treat_path */
if ((state == path_recurse) ||
((state == path_untracked) &&
- (dir->flags & DIR_SHOW_IGNORED_TOO) &&
- (get_dtype(cdir.de, istate, path.buf, path.len) == DT_DIR))) {
+ (get_dtype(cdir.de, istate, path.buf, path.len) == DT_DIR) &&
+ ((dir->flags & DIR_SHOW_IGNORED_TOO) ||
+ (pathspec &&
+ do_match_pathspec(istate, pathspec, path.buf, path.len,
+ baselen, NULL, DO_MATCH_LEADING_PATHSPEC) == MATCHED_RECURSIVELY_LEADING_PATHSPEC)))) {
struct untracked_cache_dir *ud;
ud = lookup_untracked(dir->untracked, untracked,
path.buf + baselen,
@@ -1950,6 +1987,12 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
check_only, stop_at_first_file, pathspec);
if (subdir_state > dir_state)
dir_state = subdir_state;
+
+ if (pathspec &&
+ !match_pathspec(istate, pathspec, path.buf, path.len,
+ 0 /* prefix */, NULL,
+ 0 /* do NOT special case dirs */))
+ state = path_none;
}
if (check_only) {
@@ -2488,14 +2531,14 @@ void setup_standard_excludes(struct dir_struct *dir)
if (!excludes_file)
excludes_file = xdg_config_home("ignore");
if (excludes_file && !access_or_warn(excludes_file, R_OK, 0))
- add_excludes_from_file_1(dir, excludes_file,
+ add_patterns_from_file_1(dir, excludes_file,
dir->untracked ? &dir->ss_excludes_file : NULL);
/* per repository user preference */
if (startup_info->have_repository) {
const char *path = git_path_info_exclude();
if (!access_or_warn(path, R_OK, 0))
- add_excludes_from_file_1(dir, path,
+ add_patterns_from_file_1(dir, path,
dir->untracked ? &dir->ss_info_exclude : NULL);
}
}
@@ -2527,18 +2570,18 @@ void clear_directory(struct dir_struct *dir)
{
int i, j;
struct exclude_list_group *group;
- struct exclude_list *el;
+ struct pattern_list *pl;
struct exclude_stack *stk;
for (i = EXC_CMDL; i <= EXC_FILE; i++) {
group = &dir->exclude_list_group[i];
for (j = 0; j < group->nr; j++) {
- el = &group->el[j];
+ pl = &group->pl[j];
if (i == EXC_DIRS)
- free((char *)el->src);
- clear_exclude_list(el);
+ free((char *)pl->src);
+ clear_pattern_list(pl);
}
- free(group->el);
+ free(group->pl);
}
stk = dir->exclude_stack;
diff --git a/dir.h b/dir.h
index 680079b..2fbdef0 100644
--- a/dir.h
+++ b/dir.h
@@ -11,24 +11,24 @@ struct dir_entry {
char name[FLEX_ARRAY]; /* more */
};
-#define EXC_FLAG_NODIR 1
-#define EXC_FLAG_ENDSWITH 4
-#define EXC_FLAG_MUSTBEDIR 8
-#define EXC_FLAG_NEGATIVE 16
+#define PATTERN_FLAG_NODIR 1
+#define PATTERN_FLAG_ENDSWITH 4
+#define PATTERN_FLAG_MUSTBEDIR 8
+#define PATTERN_FLAG_NEGATIVE 16
-struct exclude {
+struct path_pattern {
/*
- * This allows callers of last_exclude_matching() etc.
+ * This allows callers of last_matching_pattern() etc.
* to determine the origin of the matching pattern.
*/
- struct exclude_list *el;
+ struct pattern_list *pl;
const char *pattern;
int patternlen;
int nowildcardlen;
const char *base;
int baselen;
- unsigned flags; /* EXC_FLAG_* */
+ unsigned flags; /* PATTERN_FLAG_* */
/*
* Counting starts from 1 for line numbers in ignore files,
@@ -44,7 +44,7 @@ struct exclude {
* can also be used to represent the list of --exclude values passed
* via CLI args.
*/
-struct exclude_list {
+struct pattern_list {
int nr;
int alloc;
@@ -54,7 +54,7 @@ struct exclude_list {
/* origin of list, e.g. path to filename, or descriptive string */
const char *src;
- struct exclude **excludes;
+ struct path_pattern **patterns;
};
/*
@@ -72,7 +72,7 @@ struct exclude_stack {
struct exclude_list_group {
int nr, alloc;
- struct exclude_list *el;
+ struct pattern_list *pl;
};
struct oid_stat {
@@ -156,7 +156,8 @@ struct dir_struct {
DIR_SHOW_IGNORED_TOO = 1<<5,
DIR_COLLECT_KILLED_ONLY = 1<<6,
DIR_KEEP_UNTRACKED_CONTENTS = 1<<7,
- DIR_SHOW_IGNORED_TOO_MODE_MATCHING = 1<<8
+ DIR_SHOW_IGNORED_TOO_MODE_MATCHING = 1<<8,
+ DIR_SKIP_NESTED_GIT = 1<<9
} flags;
struct dir_entry **entries;
struct dir_entry **ignored;
@@ -191,7 +192,7 @@ struct dir_struct {
* matching exclude struct if the directory is excluded.
*/
struct exclude_stack *exclude_stack;
- struct exclude *exclude;
+ struct path_pattern *pattern;
struct strbuf basebuf;
/* Enable untracked file cache if set */
@@ -211,8 +212,9 @@ int count_slashes(const char *s);
* when populating the seen[] array.
*/
#define MATCHED_RECURSIVELY 1
-#define MATCHED_FNMATCH 2
-#define MATCHED_EXACTLY 3
+#define MATCHED_RECURSIVELY_LEADING_PATHSPEC 2
+#define MATCHED_FNMATCH 3
+#define MATCHED_EXACTLY 4
int simple_length(const char *match);
int no_wildcard(const char *string);
char *common_prefix(const struct pathspec *pathspec);
@@ -230,10 +232,23 @@ int read_directory(struct dir_struct *, struct index_state *istate,
const char *path, int len,
const struct pathspec *pathspec);
-int is_excluded_from_list(const char *pathname, int pathlen,
- const char *basename, int *dtype,
- struct exclude_list *el,
- struct index_state *istate);
+enum pattern_match_result {
+ UNDECIDED = -1,
+ NOT_MATCHED = 0,
+ MATCHED = 1,
+};
+
+/*
+ * Scan the list of patterns to determine if the ordered list
+ * of patterns matches on 'pathname'.
+ *
+ * Return 1 for a match, 0 for not matched and -1 for undecided.
+ */
+enum pattern_match_result path_matches_pattern_list(const char *pathname,
+ int pathlen,
+ const char *basename, int *dtype,
+ struct pattern_list *pl,
+ struct index_state *istate);
struct dir_entry *dir_add_ignored(struct dir_struct *dir,
struct index_state *istate,
const char *pathname, int len);
@@ -248,26 +263,26 @@ int match_pathname(const char *, int,
const char *, int,
const char *, int, int, unsigned);
-struct exclude *last_exclude_matching(struct dir_struct *dir,
- struct index_state *istate,
- const char *name, int *dtype);
+struct path_pattern *last_matching_pattern(struct dir_struct *dir,
+ struct index_state *istate,
+ const char *name, int *dtype);
int is_excluded(struct dir_struct *dir,
struct index_state *istate,
const char *name, int *dtype);
-struct exclude_list *add_exclude_list(struct dir_struct *dir,
+struct pattern_list *add_pattern_list(struct dir_struct *dir,
int group_type, const char *src);
-int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,
- struct exclude_list *el, struct index_state *istate);
-void add_excludes_from_file(struct dir_struct *, const char *fname);
-int add_excludes_from_blob_to_list(struct object_id *oid,
+int add_patterns_from_file_to_list(const char *fname, const char *base, int baselen,
+ struct pattern_list *pl, struct index_state *istate);
+void add_patterns_from_file(struct dir_struct *, const char *fname);
+int add_patterns_from_blob_to_list(struct object_id *oid,
const char *base, int baselen,
- struct exclude_list *el);
-void parse_exclude_pattern(const char **string, int *patternlen, unsigned *flags, int *nowildcardlen);
-void add_exclude(const char *string, const char *base,
- int baselen, struct exclude_list *el, int srcpos);
-void clear_exclude_list(struct exclude_list *el);
+ struct pattern_list *pl);
+void parse_path_pattern(const char **string, int *patternlen, unsigned *flags, int *nowildcardlen);
+void add_pattern(const char *string, const char *base,
+ int baselen, struct pattern_list *pl, int srcpos);
+void clear_pattern_list(struct pattern_list *pl);
void clear_directory(struct dir_struct *dir);
int repo_file_exists(struct repository *repo, const char *path);
diff --git a/environment.c b/environment.c
index 89af47c..efa0726 100644
--- a/environment.c
+++ b/environment.c
@@ -31,8 +31,6 @@ int warn_ambiguous_refs = 1;
int warn_on_object_refname_ambiguity = 1;
int ref_paranoia = -1;
int repository_format_precious_objects;
-char *repository_format_partial_clone;
-const char *core_partial_clone_filter_default;
int repository_format_worktree_config;
const char *git_commit_encoding;
const char *git_log_output_encoding;
diff --git a/fast-import.c b/fast-import.c
index b44d6a4..9503d08 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -1763,7 +1763,6 @@ static int read_next_command(void)
} else {
struct recent_command *rc;
- strbuf_detach(&command_buf, NULL);
stdin_eof = strbuf_getline_lf(&command_buf, stdin);
if (stdin_eof)
return EOF;
@@ -1784,7 +1783,7 @@ static int read_next_command(void)
free(rc->buf);
}
- rc->buf = command_buf.buf;
+ rc->buf = xstrdup(command_buf.buf);
rc->prev = cmd_tail;
rc->next = cmd_hist.prev;
rc->prev->next = rc;
@@ -1833,7 +1832,6 @@ static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
char *term = xstrdup(data);
size_t term_len = command_buf.len - (data - command_buf.buf);
- strbuf_detach(&command_buf, NULL);
for (;;) {
if (strbuf_getline_lf(&command_buf, stdin) == EOF)
die("EOF in data (terminator '%s' not found)", term);
@@ -2491,18 +2489,14 @@ static void parse_from_existing(struct branch *b)
}
}
-static int parse_from(struct branch *b)
+static int parse_objectish(struct branch *b, const char *objectish)
{
- const char *from;
struct branch *s;
struct object_id oid;
- if (!skip_prefix(command_buf.buf, "from ", &from))
- return 0;
-
oidcpy(&oid, &b->branch_tree.versions[1].oid);
- s = lookup_branch(from);
+ s = lookup_branch(objectish);
if (b == s)
die("Can't create a branch from itself: %s", b->name);
else if (s) {
@@ -2510,8 +2504,8 @@ static int parse_from(struct branch *b)
oidcpy(&b->oid, &s->oid);
oidcpy(&b->branch_tree.versions[0].oid, t);
oidcpy(&b->branch_tree.versions[1].oid, t);
- } else if (*from == ':') {
- uintmax_t idnum = parse_mark_ref_eol(from);
+ } else if (*objectish == ':') {
+ uintmax_t idnum = parse_mark_ref_eol(objectish);
struct object_entry *oe = find_mark(idnum);
if (oe->type != OBJ_COMMIT)
die("Mark :%" PRIuMAX " not a commit", idnum);
@@ -2525,13 +2519,13 @@ static int parse_from(struct branch *b)
} else
parse_from_existing(b);
}
- } else if (!get_oid(from, &b->oid)) {
+ } else if (!get_oid(objectish, &b->oid)) {
parse_from_existing(b);
if (is_null_oid(&b->oid))
b->delete = 1;
}
else
- die("Invalid ref name or SHA1 expression: %s", from);
+ die("Invalid ref name or SHA1 expression: %s", objectish);
if (b->branch_tree.tree && !oideq(&oid, &b->branch_tree.versions[1].oid)) {
release_tree_content_recursive(b->branch_tree.tree);
@@ -2542,6 +2536,26 @@ static int parse_from(struct branch *b)
return 1;
}
+static int parse_from(struct branch *b)
+{
+ const char *from;
+
+ if (!skip_prefix(command_buf.buf, "from ", &from))
+ return 0;
+
+ return parse_objectish(b, from);
+}
+
+static int parse_objectish_with_prefix(struct branch *b, const char *prefix)
+{
+ const char *base;
+
+ if (!skip_prefix(command_buf.buf, prefix, &base))
+ return 0;
+
+ return parse_objectish(b, base);
+}
+
static struct hash_list *parse_merge(unsigned int *count)
{
struct hash_list *list = NULL, **tail = &list, *n;
@@ -2588,7 +2602,7 @@ static void parse_new_commit(const char *arg)
struct branch *b;
char *author = NULL;
char *committer = NULL;
- const char *encoding = NULL;
+ char *encoding = NULL;
struct hash_list *merge_list = NULL;
unsigned int merge_count;
unsigned char prev_fanout, new_fanout;
@@ -2611,8 +2625,10 @@ static void parse_new_commit(const char *arg)
}
if (!committer)
die("Expected committer but didn't get one");
- if (skip_prefix(command_buf.buf, "encoding ", &encoding))
+ if (skip_prefix(command_buf.buf, "encoding ", &v)) {
+ encoding = xstrdup(v);
read_next_command();
+ }
parse_data(&msg, 0, NULL);
read_next_command();
parse_from(b);
@@ -2686,6 +2702,7 @@ static void parse_new_commit(const char *arg)
strbuf_addbuf(&new_data, &msg);
free(author);
free(committer);
+ free(encoding);
if (!store_object(OBJ_COMMIT, &new_data, NULL, &b->oid, next_mark))
b->pack_id = pack_id;
@@ -2713,6 +2730,7 @@ static void parse_new_tag(const char *arg)
first_tag = t;
last_tag = t;
read_next_command();
+ parse_mark();
/* from ... */
if (!skip_prefix(command_buf.buf, "from ", &from))
@@ -2769,7 +2787,7 @@ static void parse_new_tag(const char *arg)
strbuf_addbuf(&new_data, &msg);
free(tagger);
- if (store_object(OBJ_TAG, &new_data, NULL, &t->oid, 0))
+ if (store_object(OBJ_TAG, &new_data, NULL, &t->oid, next_mark))
t->pack_id = MAX_PACK_ID;
else
t->pack_id = pack_id;
@@ -2778,6 +2796,7 @@ static void parse_new_tag(const char *arg)
static void parse_reset_branch(const char *arg)
{
struct branch *b;
+ const char *tag_name;
b = lookup_branch(arg);
if (b) {
@@ -2793,6 +2812,32 @@ static void parse_reset_branch(const char *arg)
b = new_branch(arg);
read_next_command();
parse_from(b);
+ if (b->delete && skip_prefix(b->name, "refs/tags/", &tag_name)) {
+ /*
+ * Elsewhere, we call dump_branches() before dump_tags(),
+ * and dump_branches() will handle ref deletions first, so
+ * in order to make sure the deletion actually takes effect,
+ * we need to remove the tag from our list of tags to update.
+ *
+ * NEEDSWORK: replace list of tags with hashmap for faster
+ * deletion?
+ */
+ struct tag *t, *prev = NULL;
+ for (t = first_tag; t; t = t->next_tag) {
+ if (!strcmp(t->name, tag_name))
+ break;
+ prev = t;
+ }
+ if (t) {
+ if (prev)
+ prev->next_tag = t->next_tag;
+ else
+ first_tag = t->next_tag;
+ if (!t->next_tag)
+ last_tag = prev;
+ /* There is no mem_pool_free(t) function to call. */
+ }
+ }
if (command_buf.len > 0)
unread_command_buf = 1;
}
@@ -3059,6 +3104,28 @@ static void parse_progress(void)
skip_optional_lf();
}
+static void parse_alias(void)
+{
+ struct object_entry *e;
+ struct branch b;
+
+ skip_optional_lf();
+ read_next_command();
+
+ /* mark ... */
+ parse_mark();
+ if (!next_mark)
+ die(_("Expected 'mark' command, got %s"), command_buf.buf);
+
+ /* to ... */
+ memset(&b, 0, sizeof(b));
+ if (!parse_objectish_with_prefix(&b, "to "))
+ die(_("Expected 'to' command, got %s"), command_buf.buf);
+ e = find_object(&b.oid);
+ assert(e);
+ insert_mark(next_mark, e);
+}
+
static char* make_fast_import_path(const char *path)
{
if (!relative_marks_paths || is_absolute_path(path))
@@ -3186,6 +3253,8 @@ static int parse_one_feature(const char *feature, int from_stream)
option_import_marks(arg, from_stream, 1);
} else if (skip_prefix(feature, "export-marks=", &arg)) {
option_export_marks(arg);
+ } else if (!strcmp(feature, "alias")) {
+ ; /* Don't die - this feature is supported */
} else if (!strcmp(feature, "get-mark")) {
; /* Don't die - this feature is supported */
} else if (!strcmp(feature, "cat-blob")) {
@@ -3342,6 +3411,8 @@ int cmd_main(int argc, const char **argv)
parse_checkpoint();
else if (!strcmp("done", command_buf.buf))
break;
+ else if (!strcmp("alias", command_buf.buf))
+ parse_alias();
else if (starts_with(command_buf.buf, "progress "))
parse_progress();
else if (skip_prefix(command_buf.buf, "feature ", &v))
diff --git a/fetch-negotiator.c b/fetch-negotiator.c
index d6d685c..0a1357d 100644
--- a/fetch-negotiator.c
+++ b/fetch-negotiator.c
@@ -2,19 +2,20 @@
#include "fetch-negotiator.h"
#include "negotiator/default.h"
#include "negotiator/skipping.h"
+#include "repository.h"
-void fetch_negotiator_init(struct fetch_negotiator *negotiator,
- const char *algorithm)
+void fetch_negotiator_init(struct repository *r,
+ struct fetch_negotiator *negotiator)
{
- if (algorithm) {
- if (!strcmp(algorithm, "skipping")) {
- skipping_negotiator_init(negotiator);
- return;
- } else if (!strcmp(algorithm, "default")) {
- /* Fall through to default initialization */
- } else {
- die("unknown fetch negotiation algorithm '%s'", algorithm);
- }
+ prepare_repo_settings(r);
+ switch(r->settings.fetch_negotiation_algorithm) {
+ case FETCH_NEGOTIATION_SKIPPING:
+ skipping_negotiator_init(negotiator);
+ return;
+
+ case FETCH_NEGOTIATION_DEFAULT:
+ default:
+ default_negotiator_init(negotiator);
+ return;
}
- default_negotiator_init(negotiator);
}
diff --git a/fetch-negotiator.h b/fetch-negotiator.h
index 9e3967c..ea78868 100644
--- a/fetch-negotiator.h
+++ b/fetch-negotiator.h
@@ -2,6 +2,7 @@
#define FETCH_NEGOTIATOR_H
struct commit;
+struct repository;
/*
* An object that supplies the information needed to negotiate the contents of
@@ -52,7 +53,7 @@ struct fetch_negotiator {
void *data;
};
-void fetch_negotiator_init(struct fetch_negotiator *negotiator,
- const char *algorithm);
+void fetch_negotiator_init(struct repository *r,
+ struct fetch_negotiator *negotiator);
#endif
diff --git a/fetch-object.c b/fetch-object.c
deleted file mode 100644
index 4266548..0000000
--- a/fetch-object.c
+++ /dev/null
@@ -1,40 +0,0 @@
-#include "cache.h"
-#include "packfile.h"
-#include "pkt-line.h"
-#include "strbuf.h"
-#include "transport.h"
-#include "fetch-object.h"
-
-static void fetch_refs(const char *remote_name, struct ref *ref)
-{
- struct remote *remote;
- struct transport *transport;
- int original_fetch_if_missing = fetch_if_missing;
-
- fetch_if_missing = 0;
- remote = remote_get(remote_name);
- if (!remote->url[0])
- die(_("Remote with no URL"));
- transport = transport_get(remote, remote->url[0]);
-
- transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
- transport_set_option(transport, TRANS_OPT_NO_DEPENDENTS, "1");
- transport_fetch_refs(transport, ref);
- fetch_if_missing = original_fetch_if_missing;
-}
-
-void fetch_objects(const char *remote_name, const struct object_id *oids,
- int oid_nr)
-{
- struct ref *ref = NULL;
- int i;
-
- for (i = 0; i < oid_nr; i++) {
- struct ref *new_ref = alloc_ref(oid_to_hex(&oids[i]));
- oidcpy(&new_ref->old_oid, &oids[i]);
- new_ref->exact_oid = 1;
- new_ref->next = ref;
- ref = new_ref;
- }
- fetch_refs(remote_name, ref);
-}
diff --git a/fetch-object.h b/fetch-object.h
deleted file mode 100644
index d6444ca..0000000
--- a/fetch-object.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef FETCH_OBJECT_H
-#define FETCH_OBJECT_H
-
-struct object_id;
-
-void fetch_objects(const char *remote_name, const struct object_id *oids,
- int oid_nr);
-
-#endif
diff --git a/fetch-pack.c b/fetch-pack.c
index 65be043..f80e2d1 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -36,7 +36,6 @@ static int agent_supported;
static int server_supports_filtering;
static struct lock_file shallow_lock;
static const char *alternate_shallow_file;
-static char *negotiation_algorithm;
static struct strbuf fsck_msg_types = STRBUF_INIT;
/* Remember to update object flag allocation in object.h */
@@ -168,16 +167,16 @@ static enum ack_type get_ack(struct packet_reader *reader,
if (!strcmp(reader->line, "NAK"))
return NAK;
if (skip_prefix(reader->line, "ACK ", &arg)) {
- if (!get_oid_hex(arg, result_oid)) {
- arg += 40;
- len -= arg - reader->line;
+ const char *p;
+ if (!parse_oid_hex(arg, result_oid, &p)) {
+ len -= p - reader->line;
if (len < 1)
return ACK;
- if (strstr(arg, "continue"))
+ if (strstr(p, "continue"))
return ACK_continue;
- if (strstr(arg, "common"))
+ if (strstr(p, "common"))
return ACK_common;
- if (strstr(arg, "ready"))
+ if (strstr(p, "ready"))
return ACK_ready;
return ACK;
}
@@ -339,12 +338,9 @@ static int find_common(struct fetch_negotiator *negotiator,
}
}
if (server_supports_filtering && args->filter_options.choice) {
- struct strbuf expanded_filter_spec = STRBUF_INIT;
- expand_list_objects_filter_spec(&args->filter_options,
- &expanded_filter_spec);
- packet_buf_write(&req_buf, "filter %s",
- expanded_filter_spec.buf);
- strbuf_release(&expanded_filter_spec);
+ const char *spec =
+ expand_list_objects_filter_spec(&args->filter_options);
+ packet_buf_write(&req_buf, "filter %s", spec);
}
packet_buf_flush(&req_buf);
state_len = req_buf.len;
@@ -386,6 +382,7 @@ static int find_common(struct fetch_negotiator *negotiator,
state_len = 0;
}
+ trace2_region_enter("fetch-pack", "negotiation_v0_v1", the_repository);
flushes = 0;
retval = -1;
if (args->no_dependents)
@@ -470,6 +467,7 @@ static int find_common(struct fetch_negotiator *negotiator,
}
}
done:
+ trace2_region_leave("fetch-pack", "negotiation_v0_v1", the_repository);
if (!got_ready || !no_done) {
packet_buf_write(&req_buf, "done\n");
send_request(args, fd[1], &req_buf);
@@ -758,8 +756,33 @@ static int sideband_demux(int in, int out, void *data)
return ret;
}
+static void write_promisor_file(const char *keep_name,
+ struct ref **sought, int nr_sought)
+{
+ struct strbuf promisor_name = STRBUF_INIT;
+ int suffix_stripped;
+ FILE *output;
+ int i;
+
+ strbuf_addstr(&promisor_name, keep_name);
+ suffix_stripped = strbuf_strip_suffix(&promisor_name, ".keep");
+ if (!suffix_stripped)
+ BUG("name of pack lockfile should end with .keep (was '%s')",
+ keep_name);
+ strbuf_addstr(&promisor_name, ".promisor");
+
+ output = xfopen(promisor_name.buf, "w");
+ for (i = 0; i < nr_sought; i++)
+ fprintf(output, "%s %s\n", oid_to_hex(&sought[i]->old_oid),
+ sought[i]->name);
+ fclose(output);
+
+ strbuf_release(&promisor_name);
+}
+
static int get_pack(struct fetch_pack_args *args,
- int xd[2], char **pack_lockfile)
+ int xd[2], char **pack_lockfile,
+ struct ref **sought, int nr_sought)
{
struct async demux;
int do_keep = args->keep_pack;
@@ -821,7 +844,13 @@ static int get_pack(struct fetch_pack_args *args,
}
if (args->check_self_contained_and_connected)
argv_array_push(&cmd.args, "--check-self-contained-and-connected");
- if (args->from_promisor)
+ /*
+ * If we're obtaining the filename of a lockfile, we'll use
+ * that filename to write a .promisor file with more
+ * information below. If not, we need index-pack to do it for
+ * us.
+ */
+ if (!(do_keep && pack_lockfile) && args->from_promisor)
argv_array_push(&cmd.args, "--promisor");
}
else {
@@ -875,6 +904,14 @@ static int get_pack(struct fetch_pack_args *args,
die(_("%s failed"), cmd_name);
if (use_sideband && finish_async(&demux))
die(_("error in sideband demultiplexer"));
+
+ /*
+ * Now that index-pack has succeeded, write the promisor file using the
+ * obtained .keep filename if necessary
+ */
+ if (do_keep && pack_lockfile && args->from_promisor)
+ write_promisor_file(*pack_lockfile, sought, nr_sought);
+
return 0;
}
@@ -892,12 +929,13 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
struct shallow_info *si,
char **pack_lockfile)
{
+ struct repository *r = the_repository;
struct ref *ref = copy_ref_list(orig_ref);
struct object_id oid;
const char *agent_feature;
int agent_len;
struct fetch_negotiator negotiator;
- fetch_negotiator_init(&negotiator, negotiation_algorithm);
+ fetch_negotiator_init(r, &negotiator);
sort_ref_list(&ref, ref_compare_name);
QSORT(sought, nr_sought, cmp_ref_by_name);
@@ -911,7 +949,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
if (server_supports("shallow"))
print_verbose(args, _("Server supports %s"), "shallow");
- else if (args->depth > 0 || is_repository_shallow(the_repository))
+ else if (args->depth > 0 || is_repository_shallow(r))
die(_("Server does not support shallow clients"));
if (args->depth > 0 || args->deepen_since || args->deepen_not)
args->deepen = 1;
@@ -1009,7 +1047,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
alternate_shallow_file = setup_temporary_shallow(si->shallow);
else
alternate_shallow_file = NULL;
- if (get_pack(args, fd, pack_lockfile))
+ if (get_pack(args, fd, pack_lockfile, sought, nr_sought))
die(_("git fetch-pack: fetch failed."));
all_done:
@@ -1112,7 +1150,7 @@ static int add_haves(struct fetch_negotiator *negotiator,
}
static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
- const struct fetch_pack_args *args,
+ struct fetch_pack_args *args,
const struct ref *wants, struct oidset *common,
int *haves_to_send, int *in_vain,
int sideband_all)
@@ -1153,13 +1191,10 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
/* Add filter */
if (server_supports_feature("fetch", "filter", 0) &&
args->filter_options.choice) {
- struct strbuf expanded_filter_spec = STRBUF_INIT;
+ const char *spec =
+ expand_list_objects_filter_spec(&args->filter_options);
print_verbose(args, _("Server supports filter"));
- expand_list_objects_filter_spec(&args->filter_options,
- &expanded_filter_spec);
- packet_buf_write(&req_buf, "filter %s",
- expanded_filter_spec.buf);
- strbuf_release(&expanded_filter_spec);
+ packet_buf_write(&req_buf, "filter %s", spec);
} else if (args->filter_options.choice) {
warning("filtering not recognized by server, ignoring");
}
@@ -1379,14 +1414,15 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
struct shallow_info *si,
char **pack_lockfile)
{
+ struct repository *r = the_repository;
struct ref *ref = copy_ref_list(orig_ref);
enum fetch_state state = FETCH_CHECK_LOCAL;
struct oidset common = OIDSET_INIT;
struct packet_reader reader;
- int in_vain = 0;
+ int in_vain = 0, negotiation_started = 0;
int haves_to_send = INITIAL_FLUSH;
struct fetch_ne